165 lines
4.9 KiB
JavaScript
165 lines
4.9 KiB
JavaScript
import { SETTINGS_SECTIONS } from '../constants/settingsSections'
|
|
|
|
const stripHtml = value => {
|
|
if (!value) return ''
|
|
if (typeof globalThis.document === 'undefined')
|
|
return String(value).replace(/<[^>]*>/g, ' ')
|
|
const element = globalThis.document.createElement('div')
|
|
element.innerHTML = String(value)
|
|
return element.textContent || element.innerText || ''
|
|
}
|
|
|
|
const HISTORY_STATUS = {
|
|
0: 'in progress',
|
|
1: 'completed',
|
|
2: 'skipped',
|
|
3: 'pending approval',
|
|
4: 'rejected',
|
|
5: 'missed',
|
|
6: 'rescheduled',
|
|
}
|
|
|
|
const providers = []
|
|
|
|
export const registerSearchProvider = provider => {
|
|
if (!provider?.id || typeof provider.getDocuments !== 'function') {
|
|
throw new Error('A search provider needs an id and getDocuments function')
|
|
}
|
|
const existing = providers.findIndex(item => item.id === provider.id)
|
|
if (existing >= 0) providers.splice(existing, 1, provider)
|
|
else providers.push(provider)
|
|
return () => {
|
|
const index = providers.indexOf(provider)
|
|
if (index >= 0) providers.splice(index, 1)
|
|
}
|
|
}
|
|
|
|
export const getSearchProviders = () => [...providers]
|
|
|
|
const document = (provider, item) => ({ provider, ...item })
|
|
|
|
registerSearchProvider({
|
|
id: 'tasks',
|
|
getDocuments: ({ chores, membersById, projectsById }) =>
|
|
chores.map(chore => {
|
|
const labels =
|
|
chore.labelsV2?.map(label => label.name).filter(Boolean) || []
|
|
const project = projectsById.get(String(chore.projectId))
|
|
const assignees = (chore.assignees || [])
|
|
.map(assignee => membersById.get(String(assignee.userId))?.displayName)
|
|
.filter(Boolean)
|
|
const description = stripHtml(chore.description)
|
|
return document('tasks', {
|
|
id: `task:${chore.id}`,
|
|
entityId: chore.id,
|
|
title: chore.name || 'Untitled task',
|
|
subtitle:
|
|
[project?.name, ...labels].filter(Boolean).join(' · ') || 'Task',
|
|
body: description,
|
|
keywords: [...labels, project?.name, ...assignees]
|
|
.filter(Boolean)
|
|
.join(' '),
|
|
route: `/chores/${chore.id}`,
|
|
updatedAt: chore.updatedAt || chore.createdAt,
|
|
})
|
|
}),
|
|
})
|
|
|
|
registerSearchProvider({
|
|
id: 'history',
|
|
getDocuments: ({ choresById, history, membersById }) =>
|
|
history.flatMap(entry => {
|
|
const note = stripHtml(entry.notes).trim()
|
|
if (!note) return []
|
|
|
|
const chore = choresById.get(String(entry.choreId))
|
|
const member = membersById.get(String(entry.completedBy))
|
|
return [
|
|
document('history', {
|
|
id: `history:${entry.id}`,
|
|
entityId: entry.id,
|
|
title: chore?.name || entry.choreName || 'Task note',
|
|
subtitle: [
|
|
member?.displayName,
|
|
entry.performedAt
|
|
? new Date(entry.performedAt).toLocaleDateString()
|
|
: null,
|
|
]
|
|
.filter(Boolean)
|
|
.join(' · '),
|
|
body: note,
|
|
keywords: `${HISTORY_STATUS[entry.status] || 'activity'} ${member?.displayName || ''}`,
|
|
route: entry.choreId
|
|
? `/chores/${entry.choreId}/history`
|
|
: '/activities',
|
|
updatedAt: entry.performedAt || entry.updatedAt,
|
|
}),
|
|
]
|
|
}),
|
|
})
|
|
|
|
registerSearchProvider({
|
|
id: 'projects',
|
|
getDocuments: ({ projects }) =>
|
|
projects.map(project =>
|
|
document('projects', {
|
|
id: `project:${project.id}`,
|
|
entityId: project.id,
|
|
title: project.name || 'Untitled project',
|
|
subtitle: 'Project',
|
|
body: stripHtml(project.description),
|
|
keywords: 'folder project',
|
|
route: `/chores?project=${encodeURIComponent(project.id)}`,
|
|
updatedAt: project.updatedAt,
|
|
}),
|
|
),
|
|
})
|
|
|
|
registerSearchProvider({
|
|
id: 'labels',
|
|
getDocuments: ({ labels }) =>
|
|
labels.map(label =>
|
|
document('labels', {
|
|
id: `label:${label.id}`,
|
|
entityId: label.id,
|
|
title: label.name || 'Untitled label',
|
|
subtitle: 'Label',
|
|
keywords: 'tag label',
|
|
route: '/labels',
|
|
color: label.color,
|
|
}),
|
|
),
|
|
})
|
|
|
|
registerSearchProvider({
|
|
id: 'people',
|
|
getDocuments: ({ members }) =>
|
|
members.map(member =>
|
|
document('people', {
|
|
id: `person:${member.userId}`,
|
|
entityId: member.userId,
|
|
title: member.displayName || member.username || 'Circle member',
|
|
subtitle: 'Circle member',
|
|
keywords: `${member.username || ''} person member assignee`,
|
|
route: '/chores',
|
|
}),
|
|
),
|
|
})
|
|
|
|
registerSearchProvider({
|
|
id: 'settings',
|
|
getDocuments: ({ isParent, t }) =>
|
|
SETTINGS_SECTIONS.filter(({ parentOnly }) => !parentOnly || isParent).map(
|
|
({ id }) =>
|
|
document('settings', {
|
|
id: `setting:${id}`,
|
|
entityId: id,
|
|
title: t(`overview.sections.${id}.title`),
|
|
subtitle: 'Settings',
|
|
body: t(`overview.sections.${id}.description`),
|
|
keywords: `preferences configuration ${id}`,
|
|
route: `/settings/${id}`,
|
|
}),
|
|
),
|
|
})
|