feat: Add Admin Group Hub page and update navigation structure for improved admin management
This commit is contained in:
parent
bbc59457ac
commit
b7f2e2adbe
|
|
@ -37,6 +37,7 @@ import AdminFocusAreasPage from './pages/AdminFocusAreasPage'
|
||||||
import AdminHomePage from './pages/AdminHomePage'
|
import AdminHomePage from './pages/AdminHomePage'
|
||||||
import AdminUsersPage from './pages/AdminUsersPage'
|
import AdminUsersPage from './pages/AdminUsersPage'
|
||||||
import AdminSystemPage from './pages/AdminSystemPage'
|
import AdminSystemPage from './pages/AdminSystemPage'
|
||||||
|
import AdminGroupHubPage from './pages/AdminGroupHubPage'
|
||||||
import RequireAdmin from './layouts/RequireAdmin'
|
import RequireAdmin from './layouts/RequireAdmin'
|
||||||
import AdminShell from './layouts/AdminShell'
|
import AdminShell from './layouts/AdminShell'
|
||||||
import SubscriptionPage from './pages/SubscriptionPage'
|
import SubscriptionPage from './pages/SubscriptionPage'
|
||||||
|
|
@ -227,6 +228,7 @@ function AppShell() {
|
||||||
<Route element={<RequireAdmin />}>
|
<Route element={<RequireAdmin />}>
|
||||||
<Route path="admin" element={<AdminShell />}>
|
<Route path="admin" element={<AdminShell />}>
|
||||||
<Route index element={<AdminHomePage />} />
|
<Route index element={<AdminHomePage />} />
|
||||||
|
<Route path="g/:groupId" element={<AdminGroupHubPage />} />
|
||||||
<Route path="users" element={<AdminUsersPage />} />
|
<Route path="users" element={<AdminUsersPage />} />
|
||||||
<Route path="system" element={<AdminSystemPage />} />
|
<Route path="system" element={<AdminSystemPage />} />
|
||||||
<Route path="tier-limits" element={<AdminTierLimitsPage/>}/>
|
<Route path="tier-limits" element={<AdminTierLimitsPage/>}/>
|
||||||
|
|
|
||||||
|
|
@ -304,6 +304,11 @@ body { font-family: var(--font); background: var(--bg); color: var(--text1); -we
|
||||||
opacity: 1;
|
opacity: 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
a.analysis-split__nav-item {
|
||||||
|
text-decoration: none;
|
||||||
|
box-sizing: border-box;
|
||||||
|
}
|
||||||
|
|
||||||
.analysis-split__main {
|
.analysis-split__main {
|
||||||
min-width: 0;
|
min-width: 0;
|
||||||
}
|
}
|
||||||
|
|
@ -474,90 +479,11 @@ body { font-family: var(--font); background: var(--bg); color: var(--text1); -we
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Admin: gruppierte Sub-Navigation (Mobil = Chips pro Gruppe, Desktop = linke Spalte) */
|
/* Admin: Split-Layout wie .analysis-split (nur Gruppen in der Nav) */
|
||||||
.admin-shell {
|
.admin-shell {
|
||||||
width: 100%;
|
width: 100%;
|
||||||
}
|
}
|
||||||
|
|
||||||
.admin-shell__layout {
|
|
||||||
display: flex;
|
|
||||||
flex-direction: column;
|
|
||||||
gap: 16px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.admin-shell__nav-groups {
|
|
||||||
display: flex;
|
|
||||||
flex-direction: column;
|
|
||||||
gap: 14px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.admin-shell__nav-group {
|
|
||||||
display: flex;
|
|
||||||
flex-direction: column;
|
|
||||||
gap: 6px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.admin-shell__group-title {
|
|
||||||
font-size: 11px;
|
|
||||||
font-weight: 700;
|
|
||||||
letter-spacing: 0.04em;
|
|
||||||
text-transform: uppercase;
|
|
||||||
color: var(--text3);
|
|
||||||
padding: 0 2px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.admin-shell__group-chips {
|
|
||||||
display: flex;
|
|
||||||
flex-direction: row;
|
|
||||||
flex-wrap: nowrap;
|
|
||||||
gap: 6px;
|
|
||||||
overflow-x: auto;
|
|
||||||
padding-bottom: 4px;
|
|
||||||
-ms-overflow-style: none;
|
|
||||||
scrollbar-width: none;
|
|
||||||
}
|
|
||||||
|
|
||||||
.admin-shell__group-chips::-webkit-scrollbar {
|
|
||||||
display: none;
|
|
||||||
}
|
|
||||||
|
|
||||||
.admin-shell__nav-item {
|
|
||||||
flex-shrink: 0;
|
|
||||||
display: inline-flex;
|
|
||||||
align-items: center;
|
|
||||||
justify-content: center;
|
|
||||||
padding: 7px 12px;
|
|
||||||
border-radius: 20px;
|
|
||||||
border: 1.5px solid var(--border2);
|
|
||||||
background: var(--surface);
|
|
||||||
color: var(--text2);
|
|
||||||
font-family: var(--font);
|
|
||||||
font-size: 13px;
|
|
||||||
font-weight: 500;
|
|
||||||
text-decoration: none;
|
|
||||||
white-space: nowrap;
|
|
||||||
box-sizing: border-box;
|
|
||||||
}
|
|
||||||
|
|
||||||
.admin-shell__nav-item:hover {
|
|
||||||
border-color: var(--accent);
|
|
||||||
color: var(--text1);
|
|
||||||
}
|
|
||||||
|
|
||||||
.admin-shell__nav-item--active {
|
|
||||||
border-color: var(--accent);
|
|
||||||
background: var(--accent);
|
|
||||||
color: white;
|
|
||||||
}
|
|
||||||
|
|
||||||
.admin-shell__nav-item--active:hover {
|
|
||||||
color: white;
|
|
||||||
}
|
|
||||||
|
|
||||||
.admin-shell__main {
|
|
||||||
min-width: 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
.admin-page {
|
.admin-page {
|
||||||
width: 100%;
|
width: 100%;
|
||||||
}
|
}
|
||||||
|
|
@ -568,40 +494,6 @@ body { font-family: var(--font); background: var(--bg); color: var(--text1); -we
|
||||||
margin-left: auto;
|
margin-left: auto;
|
||||||
margin-right: auto;
|
margin-right: auto;
|
||||||
}
|
}
|
||||||
|
|
||||||
.admin-shell__layout {
|
|
||||||
flex-direction: row;
|
|
||||||
align-items: flex-start;
|
|
||||||
gap: 24px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.admin-shell__nav-wrap {
|
|
||||||
flex: 0 0 260px;
|
|
||||||
max-width: 280px;
|
|
||||||
position: sticky;
|
|
||||||
top: 16px;
|
|
||||||
align-self: flex-start;
|
|
||||||
}
|
|
||||||
|
|
||||||
.admin-shell__group-chips {
|
|
||||||
flex-direction: column;
|
|
||||||
overflow-x: visible;
|
|
||||||
overflow-y: visible;
|
|
||||||
padding-bottom: 0;
|
|
||||||
gap: 6px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.admin-shell__nav-item {
|
|
||||||
width: 100%;
|
|
||||||
justify-content: flex-start;
|
|
||||||
border-radius: 10px;
|
|
||||||
white-space: normal;
|
|
||||||
padding: 9px 12px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.admin-shell__main {
|
|
||||||
flex: 1;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.muted { color: var(--text3); font-size: 13px; }
|
.muted { color: var(--text3); font-size: 13px; }
|
||||||
|
|
|
||||||
|
|
@ -1,61 +1,154 @@
|
||||||
/**
|
/**
|
||||||
* Gruppierte Admin-Navigation (Shell: Chips mobil, Seitenleiste Desktop).
|
* Admin: Nur Gruppen in der Shell-Navigation; konkrete Seiten wählt man auf der Hub-Seite (/admin/g/:id).
|
||||||
* @typedef {{ to: string, label: string, end?: boolean }} AdminNavItem
|
* @typedef {{ to: string, label: string, description?: string }} AdminGroupItem
|
||||||
* @typedef {{ id: string, label: string, items: AdminNavItem[] }} AdminNavGroup
|
* @typedef {{ id: string, label: string, description: string, items: AdminGroupItem[] }} AdminGroup
|
||||||
*/
|
*/
|
||||||
|
|
||||||
/** @type {AdminNavGroup[]} */
|
/** @type {AdminGroup[]} */
|
||||||
export const ADMIN_NAV_GROUPS = [
|
export const ADMIN_GROUPS = [
|
||||||
{
|
|
||||||
id: 'overview',
|
|
||||||
label: 'Übersicht',
|
|
||||||
items: [{ to: '/admin', label: 'Start', end: true }],
|
|
||||||
},
|
|
||||||
{
|
{
|
||||||
id: 'users',
|
id: 'users',
|
||||||
label: 'Benutzerverwaltung',
|
label: 'Benutzerverwaltung',
|
||||||
items: [{ to: '/admin/users', label: 'Profile & Rollen' }],
|
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.',
|
||||||
|
},
|
||||||
|
],
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
id: 'features',
|
id: 'features',
|
||||||
label: 'Features',
|
label: 'Features',
|
||||||
|
description: 'Feature-Registry, Kontingente und individuelle Overrides.',
|
||||||
items: [
|
items: [
|
||||||
{ to: '/admin/features', label: 'Feature-Registry' },
|
{
|
||||||
{ to: '/admin/tier-limits', label: 'Tier-Limits' },
|
to: '/admin/features',
|
||||||
{ to: '/admin/user-restrictions', label: 'User-Overrides' },
|
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.',
|
||||||
|
},
|
||||||
],
|
],
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
id: 'subscription',
|
id: 'subscription',
|
||||||
label: 'Subscription',
|
label: 'Subscription',
|
||||||
|
description: 'Tiers und Gutscheine für das Freemium-System.',
|
||||||
items: [
|
items: [
|
||||||
{ to: '/admin/tiers', label: 'Tiers' },
|
{ to: '/admin/tiers', label: 'Tiers', description: 'Abo-Stufen und Zuordnung.' },
|
||||||
{ to: '/admin/coupons', label: 'Coupons' },
|
{ to: '/admin/coupons', label: 'Coupons', description: 'Gutscheincodes verwalten.' },
|
||||||
],
|
],
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
id: 'training',
|
id: 'training',
|
||||||
label: 'Trainingstypen',
|
label: 'Trainingstypen',
|
||||||
|
description: 'Trainingstypen, Mappings und Trainings-Profile.',
|
||||||
items: [
|
items: [
|
||||||
{ to: '/admin/training-types', label: 'Trainingstypen' },
|
{
|
||||||
{ to: '/admin/activity-mappings', label: 'Activity-Mappings' },
|
to: '/admin/training-types',
|
||||||
{ to: '/admin/training-profiles', label: 'Trainings-Profile' },
|
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).',
|
||||||
|
},
|
||||||
],
|
],
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
id: 'goals',
|
id: 'goals',
|
||||||
label: 'Ziele & Fokus',
|
label: 'Ziele & Fokus',
|
||||||
|
description: 'Ziel-Typen und Focus Areas.',
|
||||||
items: [
|
items: [
|
||||||
{ to: '/admin/goal-types', label: 'Ziel-Typen' },
|
{
|
||||||
{ to: '/admin/focus-areas', label: 'Focus Areas' },
|
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.',
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: 'prompts',
|
||||||
|
label: 'KI-Prompts',
|
||||||
|
description: 'Pipeline- und Basis-Prompts für die KI-Analyse.',
|
||||||
|
items: [
|
||||||
|
{
|
||||||
|
to: '/admin/prompts',
|
||||||
|
label: 'KI-Prompts verwalten',
|
||||||
|
description: 'Prompts bearbeiten, Stages, Test & Export.',
|
||||||
|
},
|
||||||
],
|
],
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
id: 'system',
|
id: 'system',
|
||||||
label: 'Basiseinstellungen',
|
label: 'Basiseinstellungen',
|
||||||
|
description: 'System-E-Mail und Platzhalter-Metadaten.',
|
||||||
items: [
|
items: [
|
||||||
{ to: '/admin/prompts', label: 'KI-Prompts' },
|
{
|
||||||
{ to: '/admin/system', label: 'SMTP & Metadaten-Export' },
|
to: '/admin/system',
|
||||||
|
label: 'SMTP & Metadaten-Export',
|
||||||
|
description: 'SMTP-Status, Test-Mail und Placeholder-Katalog (JSON/ZIP).',
|
||||||
|
},
|
||||||
],
|
],
|
||||||
},
|
},
|
||||||
]
|
]
|
||||||
|
|
||||||
|
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
|
||||||
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,35 +1,46 @@
|
||||||
import { Outlet, NavLink } from 'react-router-dom'
|
import { Outlet, NavLink, useLocation } from 'react-router-dom'
|
||||||
import { ADMIN_NAV_GROUPS } from '../config/adminNav'
|
import {
|
||||||
|
getAdminShellNavEntries,
|
||||||
|
adminShellEntryIsActive,
|
||||||
|
adminShellEntryItemCount,
|
||||||
|
} from '../config/adminNav'
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Wie KI-Analyse: nur Gruppen-Chips (mobil) bzw. Seitenleiste (desktop);
|
||||||
|
* konkrete Admin-Seiten über Hub unter /admin/g/:groupId.
|
||||||
|
*/
|
||||||
export default function AdminShell() {
|
export default function AdminShell() {
|
||||||
|
const loc = useLocation()
|
||||||
|
const entries = getAdminShellNavEntries()
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="admin-shell">
|
<div className="admin-shell">
|
||||||
<div className="admin-shell__layout">
|
<div className="analysis-split">
|
||||||
<nav className="admin-shell__nav-wrap" aria-label="Adminbereich">
|
<div className="analysis-split__nav-wrap">
|
||||||
<div className="admin-shell__nav-groups">
|
<nav className="analysis-split__nav" aria-label="Adminbereich">
|
||||||
{ADMIN_NAV_GROUPS.map((group) => (
|
{entries.map((entry) => {
|
||||||
<div key={group.id} className="admin-shell__nav-group">
|
const active = adminShellEntryIsActive(loc.pathname, entry)
|
||||||
<div className="admin-shell__group-title">{group.label}</div>
|
const count = adminShellEntryItemCount(entry)
|
||||||
<div className="admin-shell__group-chips">
|
return (
|
||||||
{group.items.map((item) => (
|
<NavLink
|
||||||
<NavLink
|
key={entry.id}
|
||||||
key={`${group.id}-${item.to}`}
|
to={entry.to}
|
||||||
to={item.to}
|
end={!!entry.end}
|
||||||
end={!!item.end}
|
className={() =>
|
||||||
className={({ isActive }) =>
|
'analysis-split__nav-item' +
|
||||||
'admin-shell__nav-item' +
|
(active ? ' analysis-split__nav-item--active' : '')
|
||||||
(isActive ? ' admin-shell__nav-item--active' : '')
|
}
|
||||||
}
|
>
|
||||||
>
|
{entry.label}
|
||||||
{item.label}
|
{count > 0 && (
|
||||||
</NavLink>
|
<span className="analysis-split__nav-cat-count">({count})</span>
|
||||||
))}
|
)}
|
||||||
</div>
|
</NavLink>
|
||||||
</div>
|
)
|
||||||
))}
|
})}
|
||||||
</div>
|
</nav>
|
||||||
</nav>
|
</div>
|
||||||
<div className="admin-shell__main">
|
<div className="analysis-split__main">
|
||||||
<div className="admin-page">
|
<div className="admin-page">
|
||||||
<Outlet />
|
<Outlet />
|
||||||
</div>
|
</div>
|
||||||
|
|
|
||||||
57
frontend/src/pages/AdminGroupHubPage.jsx
Normal file
57
frontend/src/pages/AdminGroupHubPage.jsx
Normal file
|
|
@ -0,0 +1,57 @@
|
||||||
|
import { useParams, Navigate, Link } from 'react-router-dom'
|
||||||
|
import { ADMIN_GROUPS } from '../config/adminNav'
|
||||||
|
|
||||||
|
export default function AdminGroupHubPage() {
|
||||||
|
const { groupId } = useParams()
|
||||||
|
const group = ADMIN_GROUPS.find((g) => g.id === groupId)
|
||||||
|
|
||||||
|
if (!group) {
|
||||||
|
return <Navigate to="/admin" replace />
|
||||||
|
}
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div>
|
||||||
|
<h2 className="page-title" style={{ margin: '0 0 8px', fontSize: 18 }}>
|
||||||
|
{group.label}
|
||||||
|
</h2>
|
||||||
|
<p style={{ fontSize: 13, color: 'var(--text2)', marginBottom: 18, lineHeight: 1.6 }}>
|
||||||
|
{group.description}
|
||||||
|
</p>
|
||||||
|
<p style={{ fontSize: 12, fontWeight: 600, color: 'var(--text3)', marginBottom: 12 }}>
|
||||||
|
Bereich wählen · {group.items.length}{' '}
|
||||||
|
{group.items.length === 1 ? 'Seite' : 'Seiten'}
|
||||||
|
</p>
|
||||||
|
<div style={{ display: 'flex', flexDirection: 'column', gap: 12 }}>
|
||||||
|
{group.items.map((item) => (
|
||||||
|
<Link
|
||||||
|
key={item.to}
|
||||||
|
to={item.to}
|
||||||
|
className="card section-gap"
|
||||||
|
style={{
|
||||||
|
margin: 0,
|
||||||
|
textDecoration: 'none',
|
||||||
|
color: 'inherit',
|
||||||
|
borderColor: 'var(--accent)',
|
||||||
|
borderWidth: 2,
|
||||||
|
display: 'block',
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<div style={{ fontWeight: 700, fontSize: 15, color: 'var(--accent)', marginBottom: 4 }}>
|
||||||
|
{item.label}
|
||||||
|
</div>
|
||||||
|
{item.description && (
|
||||||
|
<div style={{ fontSize: 12, color: 'var(--text2)', lineHeight: 1.5 }}>
|
||||||
|
{item.description}
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
</Link>
|
||||||
|
))}
|
||||||
|
</div>
|
||||||
|
<div style={{ marginTop: 20 }}>
|
||||||
|
<Link to="/admin" className="btn btn-secondary" style={{ fontSize: 13 }}>
|
||||||
|
← Zur Übersicht
|
||||||
|
</Link>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
@ -1,38 +1,39 @@
|
||||||
import { Link } from 'react-router-dom'
|
import { Link } from 'react-router-dom'
|
||||||
import { Shield } from 'lucide-react'
|
import { Shield } from 'lucide-react'
|
||||||
import { ADMIN_NAV_GROUPS } from '../config/adminNav'
|
import { ADMIN_GROUPS, adminGroupHubPath } from '../config/adminNav'
|
||||||
|
|
||||||
const LINK_GROUPS = ADMIN_NAV_GROUPS.filter((g) => g.id !== 'overview')
|
|
||||||
|
|
||||||
export default function AdminHomePage() {
|
export default function AdminHomePage() {
|
||||||
return (
|
return (
|
||||||
<div>
|
<div>
|
||||||
<div style={{display:'flex',alignItems:'center',gap:8,marginBottom:12}}>
|
<div style={{ display: 'flex', alignItems: 'center', gap: 8, marginBottom: 12 }}>
|
||||||
<Shield size={20} color="var(--accent)"/>
|
<Shield size={20} color="var(--accent)" />
|
||||||
<h1 style={{fontSize:18,fontWeight:700,margin:0}}>Adminbereich</h1>
|
<h1 className="page-title" style={{ margin: 0 }}>Adminbereich</h1>
|
||||||
</div>
|
</div>
|
||||||
<p style={{fontSize:13,color:'var(--text2)',marginBottom:20,lineHeight:1.6}}>
|
<p style={{ fontSize: 13, color: 'var(--text2)', marginBottom: 18, lineHeight: 1.6 }}>
|
||||||
Wähle links (Desktop) oder oben (Mobil) einen Bereich – oder springe direkt zu einer Gruppe:
|
Wähle links oder oben eine <strong>Gruppe</strong>. Die einzelnen Verwaltungsseiten erreichst du auf der
|
||||||
|
folgenden Gruppenseite – wie bei der KI-Analyse (Kategorie → Detail).
|
||||||
</p>
|
</p>
|
||||||
<div style={{display:'flex',flexDirection:'column',gap:16}}>
|
<div style={{ display: 'flex', flexDirection: 'column', gap: 12 }}>
|
||||||
{LINK_GROUPS.map((group) => (
|
{ADMIN_GROUPS.map((group) => (
|
||||||
<div key={group.id} className="card section-gap" style={{margin:0}}>
|
<Link
|
||||||
<div style={{fontWeight:700,fontSize:14,marginBottom:10,color:'var(--text1)'}}>
|
key={group.id}
|
||||||
{group.label}
|
to={adminGroupHubPath(group.id)}
|
||||||
|
className="card section-gap"
|
||||||
|
style={{
|
||||||
|
margin: 0,
|
||||||
|
textDecoration: 'none',
|
||||||
|
color: 'inherit',
|
||||||
|
display: 'block',
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<div style={{ fontWeight: 700, fontSize: 15, marginBottom: 6 }}>{group.label}</div>
|
||||||
|
<div style={{ fontSize: 12, color: 'var(--text2)', lineHeight: 1.5 }}>
|
||||||
|
{group.description}
|
||||||
</div>
|
</div>
|
||||||
<div style={{display:'flex',flexDirection:'column',gap:8}}>
|
<div style={{ fontSize: 11, color: 'var(--text3)', marginTop: 8 }}>
|
||||||
{group.items.map((item) => (
|
{group.items.length} {group.items.length === 1 ? 'Bereich' : 'Bereiche'}
|
||||||
<Link
|
|
||||||
key={item.to}
|
|
||||||
to={item.to}
|
|
||||||
className="btn btn-secondary btn-full"
|
|
||||||
style={{ textAlign: 'center', textDecoration: 'none', boxSizing: 'border-box' }}
|
|
||||||
>
|
|
||||||
{item.label}
|
|
||||||
</Link>
|
|
||||||
))}
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</Link>
|
||||||
))}
|
))}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
|
||||||
|
|
@ -632,7 +632,7 @@ export default function Analysis() {
|
||||||
<div className="empty-state">
|
<div className="empty-state">
|
||||||
<p>Keine aktiven Pipeline-Prompts verfügbar.</p>
|
<p>Keine aktiven Pipeline-Prompts verfügbar.</p>
|
||||||
<p style={{fontSize:12,color:'var(--text3)',marginTop:8}}>
|
<p style={{fontSize:12,color:'var(--text3)',marginTop:8}}>
|
||||||
Erstelle Pipeline-Prompts im Admin-Bereich (Einstellungen → Admin → KI-Prompts).
|
Erstelle Pipeline-Prompts im Admin-Bereich unter Admin → KI-Prompts.
|
||||||
</p>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue
Block a user