feat: dnd for task fields and analytics

This commit is contained in:
Nikolai Giman
2026-05-24 17:09:45 +02:00
parent c200826d31
commit affe83751b
43 changed files with 1034 additions and 120 deletions
+9
View File
@@ -0,0 +1,9 @@
import { analyticsIndicatorsSection, analyticsChartsSection } from './sections/analytics'
import { tasksSection } from './sections/tasks'
import type { UiCustomizationSectionDef } from './types'
export const uiCustomizationSections: UiCustomizationSectionDef[] = [
analyticsIndicatorsSection,
analyticsChartsSection,
tasksSection,
]
@@ -0,0 +1,49 @@
import { computed } from 'vue'
import { useAnalyticsLocale } from '@/components/features/analytics/composables/useAnalyticsLocale'
import { useAnalyticsStore } from '@/stores/analytics.store'
import type { UiCustomizationSectionDef } from '../types'
type AnalyticsSubsectionArgs = {
id: string
labelKey: string
payloadKind: 'kpi' | 'series'
}
function buildAnalyticsSection(args: AnalyticsSubsectionArgs): UiCustomizationSectionDef {
const { id, labelKey, payloadKind } = args
return {
id,
labelKey,
useSection() {
const store = useAnalyticsStore()
const { pick } = useAnalyticsLocale()
return {
catalogue: computed(() =>
store.sectionsCatalog
.filter(entry => entry.payloadKind === payloadKind)
.map(entry => ({
id: entry.id,
label: pick(entry.title),
})),
),
init: async () => {
if (store.sectionsCatalog.length === 0) {
await store.fetchSectionsCatalog()
}
},
}
},
}
}
export const analyticsIndicatorsSection = buildAnalyticsSection({
id: 'analyticsIndicators',
labelKey: 'uiCustomization.sections.analyticsIndicators',
payloadKind: 'kpi',
})
export const analyticsChartsSection = buildAnalyticsSection({
id: 'analyticsCharts',
labelKey: 'uiCustomization.sections.analyticsCharts',
payloadKind: 'series',
})
+21
View File
@@ -0,0 +1,21 @@
import { computed } from 'vue'
import { useI18n } from 'vue-i18n'
import type { UiCustomizationSectionDef } from '../types'
import { TASK_DETAIL_FIELDS } from './tasks.types'
export const tasksSection: UiCustomizationSectionDef = {
id: 'tasks',
labelKey: 'uiCustomization.sections.tasks',
useSection() {
const { t } = useI18n()
return {
catalogue: computed(() =>
TASK_DETAIL_FIELDS.map(f => ({
id: f.id,
label: t(`uiCustomization.taskFields.${f.id}`),
width: f.width,
})),
),
}
},
}
@@ -0,0 +1,30 @@
export type TaskDetailFieldWidth = 'narrow' | 'wide'
export type TaskDetailField = {
id: string
width: TaskDetailFieldWidth
}
export const TASK_DETAIL_FIELDS = [
{ id: 'subtasks', width: 'wide' },
{ id: 'note', width: 'wide' },
{ id: 'status', width: 'narrow' },
{ id: 'priority', width: 'narrow' },
{ id: 'assignees', width: 'narrow' },
{ id: 'list', width: 'narrow' },
{ id: 'tags', width: 'narrow' },
{ id: 'deadline', width: 'narrow' },
{ id: 'amount', width: 'wide' },
{ id: 'timeTracking', width: 'wide' },
{ id: 'history', width: 'wide' },
] as const satisfies readonly TaskDetailField[]
export type TaskDetailFieldId = typeof TASK_DETAIL_FIELDS[number]['id']
const widthById = new Map<string, TaskDetailFieldWidth>(
TASK_DETAIL_FIELDS.map(f => [f.id, f.width]),
)
export function taskFieldWidth(id: string): TaskDetailFieldWidth {
return widthById.get(id) ?? 'narrow'
}
+13
View File
@@ -0,0 +1,13 @@
import type { ComputedRef } from 'vue'
import type { CatalogueEntry } from '@/composables/useUiPreferences'
export type UiCustomizationSectionUseReturn = {
catalogue: ComputedRef<CatalogueEntry[]>
init?: () => Promise<void> | void
}
export type UiCustomizationSectionDef = {
id: string
labelKey: string
useSection: () => UiCustomizationSectionUseReturn
}