2026-04-05 10:32:43 +02:00
|
|
|
|
/**
|
2026-04-05 11:05:45 +02:00
|
|
|
|
* Admin: Nur Gruppen in der Shell-Navigation; konkrete Seiten wählt man auf der Hub-Seite (/admin/g/:id).
|
|
|
|
|
|
* @typedef {{ to: string, label: string, description?: string }} AdminGroupItem
|
|
|
|
|
|
* @typedef {{ id: string, label: string, description: string, items: AdminGroupItem[] }} AdminGroup
|
2026-04-05 10:32:43 +02:00
|
|
|
|
*/
|
|
|
|
|
|
|
2026-04-05 11:05:45 +02:00
|
|
|
|
/** @type {AdminGroup[]} */
|
|
|
|
|
|
export const ADMIN_GROUPS = [
|
2026-04-05 10:32:43 +02:00
|
|
|
|
{
|
|
|
|
|
|
id: 'users',
|
|
|
|
|
|
label: 'Benutzerverwaltung',
|
2026-04-05 11:05:45 +02:00
|
|
|
|
description: 'Profile anlegen, Rollen setzen und Recovery-E-Mails pflegen.',
|
|
|
|
|
|
items: [
|
|
|
|
|
|
{
|
|
|
|
|
|
to: '/admin/users',
|
|
|
|
|
|
label: 'Profile & Rollen',
|
|
|
|
|
|
description: 'Neue Profile, Admin-Rolle, PIN und E-Mail pro Nutzer.',
|
|
|
|
|
|
},
|
|
|
|
|
|
],
|
2026-04-05 10:32:43 +02:00
|
|
|
|
},
|
|
|
|
|
|
{
|
|
|
|
|
|
id: 'features',
|
|
|
|
|
|
label: 'Features',
|
2026-04-05 11:05:45 +02:00
|
|
|
|
description: 'Feature-Registry, Kontingente und individuelle Overrides.',
|
2026-04-05 10:32:43 +02:00
|
|
|
|
items: [
|
2026-04-05 11:05:45 +02:00
|
|
|
|
{
|
|
|
|
|
|
to: '/admin/features',
|
|
|
|
|
|
label: 'Feature-Registry',
|
|
|
|
|
|
description: 'Features definieren und Kategorien zuordnen.',
|
|
|
|
|
|
},
|
|
|
|
|
|
{
|
|
|
|
|
|
to: '/admin/tier-limits',
|
|
|
|
|
|
label: 'Tier-Limits',
|
|
|
|
|
|
description: 'Limit-Matrix pro Tier bearbeiten.',
|
|
|
|
|
|
},
|
|
|
|
|
|
{
|
|
|
|
|
|
to: '/admin/user-restrictions',
|
|
|
|
|
|
label: 'User-Overrides',
|
|
|
|
|
|
description: 'Individuelle Feature-Limits setzen.',
|
|
|
|
|
|
},
|
2026-04-08 12:26:28 +02:00
|
|
|
|
{
|
|
|
|
|
|
to: '/admin/widget-features',
|
|
|
|
|
|
label: 'Widgets × Features',
|
|
|
|
|
|
description: 'Dashboard-Widgets den Registry-Features zuordnen (AND).',
|
|
|
|
|
|
},
|
2026-04-05 10:32:43 +02:00
|
|
|
|
],
|
|
|
|
|
|
},
|
|
|
|
|
|
{
|
|
|
|
|
|
id: 'subscription',
|
|
|
|
|
|
label: 'Subscription',
|
2026-04-05 11:05:45 +02:00
|
|
|
|
description: 'Tiers und Gutscheine für das Freemium-System.',
|
2026-04-05 10:32:43 +02:00
|
|
|
|
items: [
|
2026-04-05 11:05:45 +02:00
|
|
|
|
{ to: '/admin/tiers', label: 'Tiers', description: 'Abo-Stufen und Zuordnung.' },
|
|
|
|
|
|
{ to: '/admin/coupons', label: 'Coupons', description: 'Gutscheincodes verwalten.' },
|
2026-04-05 10:32:43 +02:00
|
|
|
|
],
|
|
|
|
|
|
},
|
|
|
|
|
|
{
|
|
|
|
|
|
id: 'training',
|
|
|
|
|
|
label: 'Trainingstypen',
|
2026-04-05 11:05:45 +02:00
|
|
|
|
description: 'Trainingstypen, Mappings und Trainings-Profile.',
|
2026-04-05 10:32:43 +02:00
|
|
|
|
items: [
|
2026-04-05 11:05:45 +02:00
|
|
|
|
{
|
|
|
|
|
|
to: '/admin/training-types',
|
|
|
|
|
|
label: 'Trainingstypen',
|
|
|
|
|
|
description: 'Kategorien und Typen verwalten.',
|
|
|
|
|
|
},
|
|
|
|
|
|
{
|
|
|
|
|
|
to: '/admin/activity-mappings',
|
|
|
|
|
|
label: 'Activity-Mappings',
|
|
|
|
|
|
description: 'Lernendes Zuordnungssystem (Sprache / Apple Health).',
|
|
|
|
|
|
},
|
|
|
|
|
|
{
|
|
|
|
|
|
to: '/admin/training-profiles',
|
|
|
|
|
|
label: 'Trainings-Profile',
|
|
|
|
|
|
description: 'Training-Type-Profile (#15).',
|
|
|
|
|
|
},
|
2026-04-05 10:32:43 +02:00
|
|
|
|
],
|
|
|
|
|
|
},
|
|
|
|
|
|
{
|
|
|
|
|
|
id: 'goals',
|
|
|
|
|
|
label: 'Ziele & Fokus',
|
2026-04-05 11:05:45 +02:00
|
|
|
|
description: 'Ziel-Typen und Focus Areas.',
|
|
|
|
|
|
items: [
|
|
|
|
|
|
{
|
|
|
|
|
|
to: '/admin/goal-types',
|
|
|
|
|
|
label: 'Ziel-Typen',
|
|
|
|
|
|
description: 'Custom Goal Types mit oder ohne Datenquelle.',
|
|
|
|
|
|
},
|
|
|
|
|
|
{
|
|
|
|
|
|
to: '/admin/focus-areas',
|
|
|
|
|
|
label: 'Focus Areas',
|
|
|
|
|
|
description: 'Dynamische Fokusbereiche und Kategorien.',
|
|
|
|
|
|
},
|
2026-04-06 19:51:23 +02:00
|
|
|
|
{
|
|
|
|
|
|
to: '/admin/reference-value-types',
|
|
|
|
|
|
label: 'Referenz-Kennwerte',
|
|
|
|
|
|
description: 'Typen für persönliche Referenzwerte (Schlüssel, Namen, Metadaten).',
|
|
|
|
|
|
},
|
2026-04-05 11:05:45 +02:00
|
|
|
|
],
|
|
|
|
|
|
},
|
|
|
|
|
|
{
|
|
|
|
|
|
id: 'prompts',
|
|
|
|
|
|
label: 'KI-Prompts',
|
|
|
|
|
|
description: 'Pipeline- und Basis-Prompts für die KI-Analyse.',
|
2026-04-05 10:32:43 +02:00
|
|
|
|
items: [
|
2026-04-05 11:05:45 +02:00
|
|
|
|
{
|
|
|
|
|
|
to: '/admin/prompts',
|
|
|
|
|
|
label: 'KI-Prompts verwalten',
|
|
|
|
|
|
description: 'Prompts bearbeiten, Stages, Test & Export.',
|
|
|
|
|
|
},
|
2026-04-05 10:32:43 +02:00
|
|
|
|
],
|
|
|
|
|
|
},
|
|
|
|
|
|
{
|
|
|
|
|
|
id: 'system',
|
|
|
|
|
|
label: 'Basiseinstellungen',
|
2026-04-05 11:05:45 +02:00
|
|
|
|
description: 'System-E-Mail und Platzhalter-Metadaten.',
|
2026-04-05 10:32:43 +02:00
|
|
|
|
items: [
|
2026-04-05 11:05:45 +02:00
|
|
|
|
{
|
|
|
|
|
|
to: '/admin/system',
|
|
|
|
|
|
label: 'SMTP & Metadaten-Export',
|
|
|
|
|
|
description: 'SMTP-Status, Test-Mail und Placeholder-Katalog (JSON/ZIP).',
|
|
|
|
|
|
},
|
2026-04-08 10:32:18 +02:00
|
|
|
|
{
|
|
|
|
|
|
to: '/admin/dashboard-product-default',
|
|
|
|
|
|
label: 'Produkt-Dashboard (Standard)',
|
|
|
|
|
|
description: 'Globales Standard-Layout der Startseite (DB oder Code-Fallback).',
|
|
|
|
|
|
},
|
2026-04-05 10:32:43 +02:00
|
|
|
|
],
|
|
|
|
|
|
},
|
|
|
|
|
|
]
|
2026-04-05 11:05:45 +02:00
|
|
|
|
|
|
|
|
|
|
export function adminGroupHubPath(groupId) {
|
|
|
|
|
|
return `/admin/g/${groupId}`
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
|
* Shell-Navigation: Übersicht + eine Zeile/Spalte pro Gruppe (ohne Einzelseiten).
|
|
|
|
|
|
* @typedef {{ id: string, label: string, to: string, end?: boolean }} AdminShellNavEntry
|
|
|
|
|
|
* @returns {AdminShellNavEntry[]}
|
|
|
|
|
|
*/
|
|
|
|
|
|
export function getAdminShellNavEntries() {
|
|
|
|
|
|
return [
|
|
|
|
|
|
{ id: 'overview', label: 'Übersicht', to: '/admin', end: true },
|
|
|
|
|
|
...ADMIN_GROUPS.map((g) => ({
|
|
|
|
|
|
id: g.id,
|
|
|
|
|
|
label: g.label,
|
|
|
|
|
|
to: adminGroupHubPath(g.id),
|
|
|
|
|
|
end: false,
|
|
|
|
|
|
})),
|
|
|
|
|
|
]
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/** Aktiver Shell-Eintrag inkl. Leaf-Routen der Gruppe (z. B. /admin/features → Gruppe „Features“). */
|
|
|
|
|
|
export function adminShellEntryIsActive(pathname, entry) {
|
|
|
|
|
|
if (entry.id === 'overview') {
|
|
|
|
|
|
return pathname === '/admin'
|
|
|
|
|
|
}
|
|
|
|
|
|
const group = ADMIN_GROUPS.find((x) => x.id === entry.id)
|
|
|
|
|
|
if (!group) return false
|
|
|
|
|
|
if (pathname === adminGroupHubPath(group.id)) return true
|
|
|
|
|
|
return group.items.some((it) => pathname === it.to)
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/** Anzahl Unterseiten für Chip-Badge (wie KI-Analyse). */
|
|
|
|
|
|
export function adminShellEntryItemCount(entry) {
|
|
|
|
|
|
if (entry.id === 'overview') return 0
|
|
|
|
|
|
const g = ADMIN_GROUPS.find((x) => x.id === entry.id)
|
|
|
|
|
|
return g ? g.items.length : 0
|
|
|
|
|
|
}
|