Files
fotospiel-app/resources/js/admin/mobile/lib/tabHistory.ts
Codex Agent cf73f408b2 Navigation now feels more “app‑like” with
stateful tabs and reliable back behavior, and a full onboarding flow is wired in with conditional package selection
  (skips when an active package exists).

  What changed

  - Added per‑tab history + back navigation fallback to make tab switching/Back feel native (resources/js/admin/mobile/
    lib/tabHistory.ts, resources/js/admin/mobile/hooks/useBackNavigation.ts, resources/js/admin/mobile/hooks/
    useMobileNav.ts, resources/js/admin/mobile/components/MobileShell.tsx + updates across mobile pages).
  - Implemented onboarding flow pages + shared shell, and wired new routes/prefetch (resources/js/admin/mobile/welcome/
    WelcomeLandingPage.tsx, resources/js/admin/mobile/welcome/WelcomePackagesPage.tsx, resources/js/admin/mobile/
    welcome/WelcomeSummaryPage.tsx, resources/js/admin/mobile/welcome/WelcomeEventPage.tsx, resources/js/admin/mobile/
    components/OnboardingShell.tsx, resources/js/admin/router.tsx, resources/js/admin/mobile/prefetch.ts).
  - Conditional package step: packages page redirects to event setup if activePackage exists; selection stored locally
    for summary (resources/js/admin/mobile/lib/onboardingSelection.ts, resources/js/admin/mobile/welcome/
    WelcomePackagesPage.tsx).
  - Added a “Start welcome journey” CTA in the empty dashboard state (resources/js/admin/mobile/DashboardPage.tsx).
  - Added translations for onboarding shell + selected package + dashboard CTA (resources/js/admin/i18n/locales/en/
    onboarding.json, resources/js/admin/i18n/locales/de/onboarding.json, resources/js/admin/i18n/locales/en/
    management.json, resources/js/admin/i18n/locales/de/management.json).
  - Tests for new helpers/hooks (resources/js/admin/mobile/lib/tabHistory.test.ts, resources/js/admin/mobile/lib/
    onboardingSelection.test.ts, resources/js/admin/mobile/hooks/useBackNavigation.test.tsx).
2025-12-28 19:51:57 +01:00

93 lines
2.1 KiB
TypeScript

import { adminPath } from '../../constants';
import type { NavKey } from '../components/BottomNav';
const STORAGE_KEY = 'admin-mobile-tab-history-v1';
type TabHistory = Partial<Record<NavKey, string>>;
function readHistory(): TabHistory {
if (typeof window === 'undefined') {
return {};
}
try {
const raw = window.localStorage.getItem(STORAGE_KEY);
if (!raw) {
return {};
}
const parsed = JSON.parse(raw) as TabHistory;
return parsed ?? {};
} catch {
return {};
}
}
function writeHistory(history: TabHistory): void {
if (typeof window === 'undefined') {
return;
}
try {
window.localStorage.setItem(STORAGE_KEY, JSON.stringify(history));
} catch {
// Ignore storage errors.
}
}
export function setTabHistory(key: NavKey, path: string): void {
const history = readHistory();
history[key] = path;
writeHistory(history);
}
export function getTabHistory(): TabHistory {
return readHistory();
}
function resolveDefaultTarget(key: NavKey, slug?: string | null): string {
if (key === 'tasks') {
return slug ? adminPath(`/mobile/events/${slug}/tasks`) : adminPath('/mobile/tasks');
}
if (key === 'uploads') {
return slug ? adminPath(`/mobile/events/${slug}/photos`) : adminPath('/mobile/uploads');
}
if (key === 'profile') {
return adminPath('/mobile/profile');
}
return adminPath('/mobile/dashboard');
}
function resolveEventScopedTarget(path: string, slug: string | null | undefined, key: NavKey): string {
if (!slug) {
return path;
}
if (key !== 'tasks' && key !== 'uploads') {
return path;
}
const match = path.match(/\/event-admin\/mobile\/events\/([^/]+)\/(tasks|photos)(?:\/.*)?$/);
if (!match) {
return resolveDefaultTarget(key, slug);
}
const storedSlug = match[1];
if (storedSlug === slug) {
return path;
}
return resolveDefaultTarget(key, slug);
}
export function resolveTabTarget(key: NavKey, slug?: string | null): string {
const history = readHistory();
const stored = history[key];
const fallback = resolveDefaultTarget(key, slug);
if (!stored) {
return fallback;
}
return resolveEventScopedTarget(stored, slug, key);
}