78 lines
2.6 KiB
JavaScript
78 lines
2.6 KiB
JavaScript
// The Tech Tools catalog.
|
|
//
|
|
// This is the ONLY place a tool has to be declared. Adding one is: write the
|
|
// view, add a route in routes.js, add an entry here. The index page groups by
|
|
// category and searches over name + description + keywords.
|
|
//
|
|
// Fields:
|
|
// id - stable slug, also the key the index uses
|
|
// name - card title
|
|
// description - one line, says what it does, not how
|
|
// category - group heading on the index
|
|
// route - path to open
|
|
// standalone - true when the route lives OUTSIDE AppLayout (print pages),
|
|
// so the index opens it as a normal link rather than a
|
|
// router-link and the user keeps this tab
|
|
// keywords - extra search terms someone might type instead of the name
|
|
export const TOOLS = [
|
|
{
|
|
id: 'code-generator',
|
|
name: 'Barcode / QR Generator',
|
|
description: 'Make QR or CODE128 labels from typed text, a URL, or a CSV, sized for your label stock.',
|
|
category: 'labels',
|
|
route: '/tools/codes',
|
|
standalone: true,
|
|
keywords: ['qr', 'barcode', 'code128', 'label', 'zebra', 'sticker', 'csv', 'print'],
|
|
},
|
|
{
|
|
id: 'secure-send',
|
|
name: 'Secure Send',
|
|
description: 'Hand somebody a password by a link that works once and then destroys it.',
|
|
category: 'security',
|
|
route: '/tools/secure-send',
|
|
keywords: ['password', 'secret', 'credential', 'share', 'link', 'expire',
|
|
'one time', 'burn', 'pusher'],
|
|
},
|
|
]
|
|
|
|
export const CATEGORY_LABELS = {
|
|
labels: 'Labels & Printing',
|
|
convert: 'Conversion',
|
|
security: 'Secrets & Access',
|
|
network: 'Network',
|
|
}
|
|
|
|
export function categoryLabel(category) {
|
|
return CATEGORY_LABELS[category] || category
|
|
}
|
|
|
|
// Tools matching a search string, or all of them when the box is empty.
|
|
export function searchTools(query) {
|
|
const needle = (query || '').trim().toLowerCase()
|
|
if (!needle) return TOOLS
|
|
return TOOLS.filter(tool => {
|
|
const haystack = [tool.name, tool.description, ...(tool.keywords || [])]
|
|
.join(' ')
|
|
.toLowerCase()
|
|
return haystack.includes(needle)
|
|
})
|
|
}
|
|
|
|
// Tools grouped for the index, categories in CATEGORY_LABELS order so the
|
|
// page does not reshuffle as tools are added.
|
|
export function groupTools(query) {
|
|
const matched = searchTools(query)
|
|
const order = Object.keys(CATEGORY_LABELS)
|
|
const seen = [...new Set(matched.map(tool => tool.category))]
|
|
seen.sort((a, b) => {
|
|
const ai = order.indexOf(a)
|
|
const bi = order.indexOf(b)
|
|
return (ai === -1 ? 99 : ai) - (bi === -1 ? 99 : bi)
|
|
})
|
|
return seen.map(category => ({
|
|
category,
|
|
label: categoryLabel(category),
|
|
tools: matched.filter(tool => tool.category === category),
|
|
}))
|
|
}
|