feat(portal): restructure into three-screen architecture with event tabs

Replace scattered dashboard pages with a three-screen volunteer portal:

1. Mijn evenementen (/evenementen) - landing page with visual event cards
   in a responsive grid, sorted upcoming-first
2. Event-pagina (/evenementen/:eventId) - single page with hash-based tabs
   (Overzicht, Mijn rooster, Diensten claimen, Informatie) replacing the
   old separate dashboard/my-shifts/claim-shifts pages
3. Mijn profiel (/profiel) - unchanged, platform-level settings

Key changes:
- Extract page content into tab components (RoosterTab, ClaimenTab,
  OverzichtTab, InformatieTab) that receive eventId as prop
- Dual-mode navbar: platform mode (Crewli logo) vs event mode (org name
  + event name + back link)
- StatusCard now emits switchTab events instead of route navigation
- Smart login redirect: 1 event → direct to event, 2+ → overview
- Backward-compat redirects for /dashboard/* → /evenementen
- Delete EventSwitcher (replaced by events overview page)
- Update UserAvatarMenu with "Mijn evenementen" link

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
2026-04-13 13:30:20 +02:00
parent 2d7464e05b
commit f9faeb7ea0
22 changed files with 1482 additions and 1102 deletions

View File

@@ -4,6 +4,13 @@ import { usePortalStore } from '@/stores/usePortalStore'
const guestOnlyPaths = ['/login', '/wachtwoord-vergeten', '/wachtwoord-resetten']
// Old dashboard routes that need backward-compat redirects
const dashboardRedirects: Record<string, string> = {
'/dashboard': '/evenementen',
'/dashboard/my-shifts': '/evenementen',
'/dashboard/claim-shifts': '/evenementen',
}
export function setupGuards(router: Router) {
router.beforeEach(async (to) => {
const authStore = useAuthStore()
@@ -18,12 +25,18 @@ export function setupGuards(router: Router) {
await portalStore.hydrateIfNeeded()
}
// Backward-compat redirects for old dashboard routes
const redirect = dashboardRedirects[to.path]
if (redirect && authStore.isAuthenticated) {
return { path: redirect }
}
const requiresAuth = to.meta.requiresAuth === true
// Public routes — no auth check needed
if (!requiresAuth) {
if (authStore.isAuthenticated && guestOnlyPaths.some(p => to.path === p || to.path.startsWith(`${p}/`))) {
return { path: '/dashboard' }
return { path: '/evenementen' }
}
return