diff --git a/aos-shots/v20-onboarding-dismissed.png b/aos-shots/v20-onboarding-dismissed.png
new file mode 100644
index 0000000..bad9ac2
Binary files /dev/null and b/aos-shots/v20-onboarding-dismissed.png differ
diff --git a/aos-shots/v20-onboarding-last.png b/aos-shots/v20-onboarding-last.png
new file mode 100644
index 0000000..4cba283
Binary files /dev/null and b/aos-shots/v20-onboarding-last.png differ
diff --git a/aos-shots/v20-onboarding-step1.png b/aos-shots/v20-onboarding-step1.png
new file mode 100644
index 0000000..4cba283
Binary files /dev/null and b/aos-shots/v20-onboarding-step1.png differ
diff --git a/aos-shots/v20-onboarding-step2.png b/aos-shots/v20-onboarding-step2.png
new file mode 100644
index 0000000..bad9ac2
Binary files /dev/null and b/aos-shots/v20-onboarding-step2.png differ
diff --git a/src/components/aos/app-shell.tsx b/src/components/aos/app-shell.tsx
index 544e74e..5cb7e2e 100644
--- a/src/components/aos/app-shell.tsx
+++ b/src/components/aos/app-shell.tsx
@@ -27,6 +27,7 @@ import { SettingsView } from './views/settings'
import { GlobalSettingsView } from './views/global-settings'
import { SetupWizardView } from './views/setup-wizard'
import { PortalShell } from './portal-shell'
+import { OnboardingOverlay } from './onboarding-overlay'
export function AppShell() {
const { view, sidebarOpen, setSidebarOpen, portalMode } = useAos()
@@ -80,6 +81,7 @@ export function AppShell() {
+
)
}
diff --git a/src/components/aos/onboarding-checklist.tsx b/src/components/aos/onboarding-checklist.tsx
new file mode 100644
index 0000000..b550228
--- /dev/null
+++ b/src/components/aos/onboarding-checklist.tsx
@@ -0,0 +1,101 @@
+'use client'
+
+import { useAos } from '@/store/aos'
+import { Card, CardContent } from '@/components/ui/card'
+import { Button } from '@/components/ui/button'
+import { Badge } from '@/components/ui/badge'
+import { Progress } from '@/components/ui/progress'
+import { Check, ChevronRight, Rocket } from 'lucide-react'
+import { cn } from '@/lib/utils'
+import { useI18n } from '@/hooks/use-i18n'
+
+const CHECKLIST_ITEMS = [
+ { key: 'visit_leads', view: 'leads', label_de: 'Leads Finder erkunden', label_en: 'Explore Leads Finder', label_tr: 'Leads Finder keşfet' },
+ { key: 'visit_projects', view: 'projects', label_de: 'Projekte ansehen', label_en: 'View Projects', label_tr: 'Projeleri görüntüle' },
+ { key: 'visit_finance', view: 'finance', label_de: 'Rechnungen prüfen', label_en: 'Check Invoices', label_tr: 'Faturaları kontrol et' },
+ { key: 'visit_accounting', view: 'accounting', label_de: 'Buchhaltung (UStVA) ansehen', label_en: 'View Accounting (VAT)', label_tr: 'Muhasebeyi (KDV) görüntüle' },
+ { key: 'visit_api', view: 'api', label_de: 'API-Token erstellen', label_en: 'Create API Token', label_tr: 'API Token oluştur' },
+ { key: 'visit_portal', view: 'dashboard', label_de: 'Client-Portal öffnen', label_en: 'Open Client Portal', label_tr: 'Müşteri Portalını aç' },
+] as const
+
+export function OnboardingChecklist() {
+ const { checklist, toggleChecklist, setView, setPortalMode } = useAos()
+ const { locale } = useI18n()
+
+ const completed = CHECKLIST_ITEMS.filter((item) => checklist[item.key]).length
+ const total = CHECKLIST_ITEMS.length
+ const pct = Math.round((completed / total) * 100)
+ const allDone = completed === total
+
+ const label = (item: typeof CHECKLIST_ITEMS[number]) => {
+ if (locale === 'en') return item.label_en
+ if (locale === 'tr') return item.label_tr
+ return item.label_de
+ }
+
+ const go = (item: typeof CHECKLIST_ITEMS[number]) => {
+ if (item.view === 'dashboard' && item.key === 'visit_portal') {
+ setPortalMode(true)
+ } else {
+ setView(item.view as 'leads' | 'projects' | 'finance' | 'accounting' | 'api' | 'dashboard')
+ }
+ if (!checklist[item.key]) toggleChecklist(item.key)
+ }
+
+ if (allDone) {
+ return (
+
+
+
+
+
+
+
Onboarding abgeschlossen!
+
Du kennst nun alle Kernfunktionen. Bei Fragen sieh im Handbuch (Wissensdatenbank) nach.
+
+
+
+
+ )
+ }
+
+ return (
+
+
+
+
+
+ Onboarding-Checkliste
+ {completed}/{total}
+
+
{pct}%
+
+
+
+ {CHECKLIST_ITEMS.map((item) => {
+ const done = checklist[item.key]
+ return (
+
+ )
+ })}
+
+
+
+ )
+}
diff --git a/src/components/aos/onboarding-overlay.tsx b/src/components/aos/onboarding-overlay.tsx
new file mode 100644
index 0000000..2c50aba
--- /dev/null
+++ b/src/components/aos/onboarding-overlay.tsx
@@ -0,0 +1,173 @@
+'use client'
+
+import { useAos } from '@/store/aos'
+import { Button } from '@/components/ui/button'
+import { Badge } from '@/components/ui/badge'
+import { cn } from '@/lib/utils'
+import {
+ Rocket,
+ Radar,
+ FolderKanban,
+ Receipt,
+ BookOpen,
+ Webhook,
+ Check,
+ X,
+ ChevronRight,
+ ChevronLeft,
+ Sparkles,
+ ShieldCheck,
+ CalendarDays,
+} from 'lucide-react'
+
+const STEPS = [
+ {
+ icon: Rocket,
+ title: 'Willkommen bei AOS',
+ description: 'Die selbstgehostete Plattform, die deine Agentur end-to-end betreibt: von der Lead-Suche bis zur Rechnung und Lizenzverwaltung.',
+ highlight: '20 Module · 21 API-Endpunkte · MCP-Server · Client-Portal',
+ },
+ {
+ icon: Radar,
+ title: 'Leads Finder',
+ description: 'Evidenzbasierte Website-Audits statt geratener KI-Vorschläge. Jeder Score beruht auf gespeicherten Prüfungen — die AI liefert nur die Erklärung.',
+ highlight: 'Approve → Convert → CRM Company + Deal',
+ },
+ {
+ icon: FolderKanban,
+ title: 'Projekte & Support',
+ description: 'Aufgaben, Zeitverfolgung, Budgets. Support-Tickets mit AI-Zusammenfassung und Antwort-Vorschlägen (z-ai-web-dev-sdk, server-side).',
+ highlight: 'SLA-Timer · AI Ticket Summary · Billable Time → Invoice',
+ },
+ {
+ icon: Receipt,
+ title: 'Finanzen & Buchhaltung',
+ description: 'Rechnungen mit E-Rechnung (ZUGFeRD/XRechnung), Umsatzsteuer-Voranmeldung (UStVA-XML, ELSTER), Bankabgleich (CAMT.053), EÜR, Mahnwesen.',
+ highlight: 'GoBD-konform · DATEV-Export · Hash-Kette Audit',
+ },
+ {
+ icon: BookOpen,
+ title: 'Buchhaltung & Fristen',
+ description: 'USt-Voranmeldung mit Kennzahlen (81/86/66/83/37), EÜR, Bankabgleich. Fristen-Kalender aggregiert alle Deadlines aller Module.',
+ highlight: 'Real CAMT.053 Parser · ELSTER XML · SLA-Tracking',
+ },
+ {
+ icon: Webhook,
+ title: 'API & MCP Server',
+ description: 'Public REST API mit Scoped Tokens (SHA-256 gehasht). MCP Server (JSON-RPC) für externe Agenten wie Claude Desktop oder ZeroClaw.',
+ highlight: '8 MCP Tools · HMAC-signed Webhooks · OpenAPI 3.0',
+ },
+ {
+ icon: ShieldCheck,
+ title: 'Compliance & DSGVO',
+ description: 'GoBD (Unveränderlichkeit, Hash-Kette), DSGVO (Konsens-Protokoll, Art. 15 Auskunft), BFSG (Barrierefreiheit), UWG § 7 (Cold-Outreach-Gate).',
+ highlight: 'Self-hosted · Daten bleiben im Server',
+ },
+ {
+ icon: CalendarDays,
+ title: 'Client-Portal & i18n',
+ description: 'Kunden sehen nur ihre eigenen Daten (projekt-isoliert). Meilenstein-Freigaben mit IP-Evidenz. UI in DE/EN/TR umschaltbar.',
+ highlight: 'AdES Signatur · Real IP Evidence · 3 Sprachen',
+ },
+]
+
+export function OnboardingOverlay() {
+ const { onboardingSeen, onboardingStep, setOnboardingSeen, setOnboardingStep } = useAos()
+
+ if (onboardingSeen) return null
+
+ const step = STEPS[onboardingStep]
+ const Icon = step.icon
+ const isLast = onboardingStep === STEPS.length - 1
+
+ const next = () => {
+ if (isLast) {
+ setOnboardingSeen(true)
+ } else {
+ setOnboardingStep(onboardingStep + 1)
+ }
+ }
+
+ const prev = () => onboardingStep > 0 && setOnboardingStep(onboardingStep - 1)
+ const skip = () => setOnboardingSeen(true)
+
+ return (
+
+
+ {/* Header */}
+
+
+
+ {onboardingStep + 1} / {STEPS.length}
+
+ Onboarding
+
+
+
+
+ {/* Progress bar */}
+
+
+ {/* Content */}
+
+
+
+
+
+
{step.title}
+
{step.description}
+
+
+
+ {step.highlight}
+
+
+
+ {/* Step dots */}
+
+ {STEPS.map((_, i) => (
+
+
+ {/* Footer */}
+
+
+
+
+
+
+
+
+
+ )
+}
diff --git a/src/components/aos/views/dashboard.tsx b/src/components/aos/views/dashboard.tsx
index 9244622..ed5bac2 100644
--- a/src/components/aos/views/dashboard.tsx
+++ b/src/components/aos/views/dashboard.tsx
@@ -18,6 +18,7 @@ import { Badge } from '@/components/ui/badge'
import { Button } from '@/components/ui/button'
import { Skeleton } from '@/components/ui/skeleton'
import { KpiCard, PageHeader, CardSkeletons } from '../primitives'
+import { OnboardingChecklist } from '../onboarding-checklist'
import { formatEuro, formatEuroCompact, formatRelative } from '@/lib/format'
import { useAos } from '@/store/aos'
import { Bar, BarChart, ResponsiveContainer, Tooltip, XAxis, YAxis, Cell } from 'recharts'
@@ -74,6 +75,9 @@ export function DashboardView() {
}
/>
+ {/* Onboarding checklist (PRD §9: onboarding checklists, no dead ends) */}
+
+
{/* KPI grid */}
void
+ // onboarding (PRD §9: onboarding checklists, no dead ends)
+ onboardingSeen: boolean
+ onboardingStep: number
+ setOnboardingSeen: (seen: boolean) => void
+ setOnboardingStep: (step: number) => void
+ checklist: Record
+ toggleChecklist: (key: string) => void
}
export const useAos = create()(
@@ -65,7 +72,13 @@ export const useAos = create()(
setPortalCompany: (portalCompanyId) => set({ portalCompanyId }),
locale: 'de',
setLocale: (locale) => set({ locale }),
+ onboardingSeen: false,
+ onboardingStep: 0,
+ setOnboardingSeen: (onboardingSeen) => set({ onboardingSeen }),
+ setOnboardingStep: (onboardingStep) => set({ onboardingStep }),
+ checklist: {},
+ toggleChecklist: (key) => set((s) => ({ checklist: { ...s.checklist, [key]: !s.checklist[key] } })),
}),
- { name: 'aos-ui', partialize: (s) => ({ view: s.view, portalMode: s.portalMode, portalCompanyId: s.portalCompanyId, locale: s.locale }) }
+ { name: 'aos-ui', partialize: (s) => ({ view: s.view, portalMode: s.portalMode, portalCompanyId: s.portalCompanyId, locale: s.locale, onboardingSeen: s.onboardingSeen, checklist: s.checklist }) }
)
)