- Tenant-Admin-PWA: Neues /event-admin/welcome Onboarding mit WelcomeHero, Packages-, Order-Summary- und Event-Setup-Pages, Zustandsspeicher, Routing-Guard und Dashboard-CTA für Erstnutzer; Filament-/admin-Login via Custom-View behoben.
- Brand/Theming: Marketing-Farb- und Typographievariablen in `resources/css/app.css` eingeführt, AdminLayout, Dashboardkarten und Onboarding-Komponenten entsprechend angepasst; Dokumentation (`docs/todo/tenant-admin-onboarding-fusion.md`, `docs/changes/...`) aktualisiert. - Checkout & Payments: Checkout-, PayPal-Controller und Tests für integrierte Stripe/PayPal-Flows sowie Paket-Billing-Abläufe überarbeitet; neue PayPal SDK-Factory und Admin-API-Helper (`resources/js/admin/api.ts`) schaffen Grundlage für Billing/Members/Tasks-Seiten. - DX & Tests: Neue Playwright/E2E-Struktur (docs/testing/e2e.md, `tests/e2e/tenant-onboarding-flow.test.ts`, Utilities), E2E-Tenant-Seeder und zusätzliche Übersetzungen/Factories zur Unterstützung der neuen Flows. - Marketing-Kommunikation: Automatische Kontakt-Bestätigungsmail (`ContactConfirmation` + Blade-Template) implementiert; Guest-PWA unter `/event` erreichbar. - Nebensitzung: Blogsystem gefixt und umfassenden BlogPostSeeder für Beispielinhalte angelegt.
This commit is contained in:
109
resources/js/admin/onboarding/store.tsx
Normal file
109
resources/js/admin/onboarding/store.tsx
Normal file
@@ -0,0 +1,109 @@
|
||||
import React from 'react';
|
||||
|
||||
export type OnboardingProgress = {
|
||||
welcomeSeen: boolean;
|
||||
packageSelected: boolean;
|
||||
eventCreated: boolean;
|
||||
lastStep?: string | null;
|
||||
selectedPackage?: {
|
||||
id: number;
|
||||
name: string;
|
||||
priceText?: string | null;
|
||||
isSubscription?: boolean;
|
||||
} | null;
|
||||
};
|
||||
|
||||
type OnboardingContextValue = {
|
||||
progress: OnboardingProgress;
|
||||
setProgress: (updater: (prev: OnboardingProgress) => OnboardingProgress) => void;
|
||||
markStep: (step: Partial<OnboardingProgress>) => void;
|
||||
reset: () => void;
|
||||
};
|
||||
|
||||
const DEFAULT_PROGRESS: OnboardingProgress = {
|
||||
welcomeSeen: false,
|
||||
packageSelected: false,
|
||||
eventCreated: false,
|
||||
lastStep: null,
|
||||
selectedPackage: null,
|
||||
};
|
||||
|
||||
const STORAGE_KEY = 'tenant-admin:onboarding-progress';
|
||||
|
||||
const OnboardingProgressContext = React.createContext<OnboardingContextValue | undefined>(undefined);
|
||||
|
||||
function readStoredProgress(): OnboardingProgress {
|
||||
if (typeof window === 'undefined') {
|
||||
return DEFAULT_PROGRESS;
|
||||
}
|
||||
try {
|
||||
const raw = window.localStorage.getItem(STORAGE_KEY);
|
||||
if (!raw) {
|
||||
return DEFAULT_PROGRESS;
|
||||
}
|
||||
const parsed = JSON.parse(raw) as Partial<OnboardingProgress>;
|
||||
return {
|
||||
...DEFAULT_PROGRESS,
|
||||
...parsed,
|
||||
};
|
||||
} catch (error) {
|
||||
console.warn('[OnboardingProgress] Failed to parse stored value', error);
|
||||
return DEFAULT_PROGRESS;
|
||||
}
|
||||
}
|
||||
|
||||
function writeStoredProgress(progress: OnboardingProgress) {
|
||||
if (typeof window === 'undefined') {
|
||||
return;
|
||||
}
|
||||
try {
|
||||
window.localStorage.setItem(STORAGE_KEY, JSON.stringify(progress));
|
||||
} catch (error) {
|
||||
console.warn('[OnboardingProgress] Failed to persist value', error);
|
||||
}
|
||||
}
|
||||
|
||||
export function OnboardingProgressProvider({ children }: { children: React.ReactNode }) {
|
||||
const [progress, setProgressState] = React.useState<OnboardingProgress>(() => readStoredProgress());
|
||||
|
||||
const setProgress = React.useCallback((updater: (prev: OnboardingProgress) => OnboardingProgress) => {
|
||||
setProgressState((prev) => {
|
||||
const next = updater(prev);
|
||||
writeStoredProgress(next);
|
||||
return next;
|
||||
});
|
||||
}, []);
|
||||
|
||||
const markStep = React.useCallback((step: Partial<OnboardingProgress>) => {
|
||||
setProgress((prev) => ({
|
||||
...prev,
|
||||
...step,
|
||||
lastStep: typeof step.lastStep === 'undefined' ? prev.lastStep : step.lastStep,
|
||||
}));
|
||||
}, [setProgress]);
|
||||
|
||||
const reset = React.useCallback(() => {
|
||||
setProgress(() => DEFAULT_PROGRESS);
|
||||
}, [setProgress]);
|
||||
|
||||
const value = React.useMemo<OnboardingContextValue>(() => ({
|
||||
progress,
|
||||
setProgress,
|
||||
markStep,
|
||||
reset,
|
||||
}), [progress, setProgress, markStep, reset]);
|
||||
|
||||
return (
|
||||
<OnboardingProgressContext.Provider value={value}>
|
||||
{children}
|
||||
</OnboardingProgressContext.Provider>
|
||||
);
|
||||
}
|
||||
|
||||
export function useOnboardingProgress() {
|
||||
const context = React.useContext(OnboardingProgressContext);
|
||||
if (!context) {
|
||||
throw new Error('useOnboardingProgress must be used within OnboardingProgressProvider');
|
||||
}
|
||||
return context;
|
||||
}
|
||||
Reference in New Issue
Block a user