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 }) } ) )