Replace 24 `err: any` error handler types with proper `AxiosError<ApiErrorResponse>` typing. Fix additional `as any` casts and `Record<string, any>` patterns in registration field components, event settings, and portal layout. Create shared `ApiErrorResponse` type for portal app. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
234 lines
6.3 KiB
Vue
234 lines
6.3 KiB
Vue
<script lang="ts" setup>
|
|
import UserAvatarMenu from '@/components/portal/UserAvatarMenu.vue'
|
|
import { useAuthStore } from '@/stores/useAuthStore'
|
|
import { usePortalStore } from '@/stores/usePortalStore'
|
|
|
|
const { injectSkinClasses } = useSkins()
|
|
|
|
injectSkinClasses()
|
|
|
|
const authStore = useAuthStore()
|
|
const portal = usePortalStore()
|
|
const route = useRoute()
|
|
|
|
const isMobileMenuOpen = ref(false)
|
|
|
|
// Navbar mode: 'event' shows org name + event name + back link
|
|
// Default ('platform') shows Crewli logo + page title
|
|
const isEventMode = computed(() => route.meta.navMode === 'event')
|
|
const navTitle = computed(() => route.meta.navTitle)
|
|
|
|
const eventName = computed(() => portal.activeEvent?.event_name ?? '')
|
|
const orgName = computed(() => portal.activeEvent?.organisation_name ?? '')
|
|
|
|
// Mobile nav items
|
|
const mobileNavItems = computed(() => {
|
|
const items = [
|
|
{ title: 'Mijn evenementen', to: '/evenementen', icon: 'tabler-calendar-event' },
|
|
{ title: 'Mijn Profiel', to: '/profiel', icon: 'tabler-user' },
|
|
]
|
|
|
|
return items
|
|
})
|
|
|
|
const isFallbackStateActive = ref(false)
|
|
const refLoadingIndicator = ref<any>(null)
|
|
|
|
watch([isFallbackStateActive, refLoadingIndicator], () => {
|
|
if (isFallbackStateActive.value && refLoadingIndicator.value)
|
|
refLoadingIndicator.value.fallbackHandle()
|
|
|
|
if (!isFallbackStateActive.value && refLoadingIndicator.value)
|
|
refLoadingIndicator.value.resolveHandle()
|
|
}, { immediate: true })
|
|
|
|
async function logout() {
|
|
isMobileMenuOpen.value = false
|
|
await authStore.logout()
|
|
const router = useRouter()
|
|
await router.push('/login')
|
|
}
|
|
</script>
|
|
|
|
<template>
|
|
<VApp>
|
|
<AppLoadingIndicator ref="refLoadingIndicator" />
|
|
|
|
<!-- Navbar: only shown when authenticated -->
|
|
<VAppBar
|
|
v-if="authStore.isAuthenticated"
|
|
flat
|
|
color="surface"
|
|
border="b"
|
|
height="64"
|
|
>
|
|
<VContainer
|
|
fluid
|
|
class="d-flex align-center py-0"
|
|
style="max-inline-size: 1440px;"
|
|
>
|
|
<!-- Event mode: Org name + Event name + Back link -->
|
|
<template v-if="isEventMode">
|
|
<!-- Org name / logo placeholder -->
|
|
<div class="d-flex align-center gap-x-2 flex-shrink-0">
|
|
<VIcon
|
|
icon="tabler-building"
|
|
size="24"
|
|
color="primary"
|
|
/>
|
|
<span
|
|
v-if="orgName"
|
|
class="text-subtitle-1 font-weight-medium text-high-emphasis d-none d-sm-inline text-truncate"
|
|
style="max-width: 200px;"
|
|
>
|
|
{{ orgName }}
|
|
</span>
|
|
</div>
|
|
|
|
<!-- Event name -->
|
|
<span
|
|
v-if="eventName"
|
|
class="text-body-1 text-medium-emphasis ms-2 text-truncate d-none d-sm-inline"
|
|
style="max-width: 250px;"
|
|
>
|
|
{{ eventName }}
|
|
</span>
|
|
|
|
<!-- Back link -->
|
|
<VBtn
|
|
variant="text"
|
|
size="small"
|
|
color="default"
|
|
class="text-medium-emphasis ms-2 d-none d-md-flex"
|
|
to="/evenementen"
|
|
>
|
|
<VIcon
|
|
start
|
|
icon="tabler-arrow-left"
|
|
size="16"
|
|
/>
|
|
Evenementen
|
|
</VBtn>
|
|
</template>
|
|
|
|
<!-- Platform mode: Crewli logo + optional page title -->
|
|
<template v-else>
|
|
<RouterLink
|
|
to="/evenementen"
|
|
class="d-flex align-center gap-x-2 text-decoration-none flex-shrink-0"
|
|
>
|
|
<VIcon
|
|
icon="tabler-users-group"
|
|
size="26"
|
|
color="primary"
|
|
/>
|
|
<span class="text-h6 font-weight-bold text-high-emphasis d-none d-sm-inline">
|
|
Crewli
|
|
</span>
|
|
</RouterLink>
|
|
|
|
<span
|
|
v-if="navTitle"
|
|
class="text-body-1 text-medium-emphasis ms-4 d-none d-md-inline"
|
|
>
|
|
{{ navTitle }}
|
|
</span>
|
|
</template>
|
|
|
|
<VSpacer />
|
|
|
|
<!-- Right section: Avatar menu (desktop) -->
|
|
<div class="d-none d-md-flex align-center">
|
|
<UserAvatarMenu />
|
|
</div>
|
|
|
|
<!-- Mobile nav toggle -->
|
|
<VAppBarNavIcon
|
|
class="d-md-none"
|
|
@click="isMobileMenuOpen = !isMobileMenuOpen"
|
|
/>
|
|
</VContainer>
|
|
</VAppBar>
|
|
|
|
<!-- Mobile navigation drawer -->
|
|
<VNavigationDrawer
|
|
v-if="authStore.isAuthenticated"
|
|
v-model="isMobileMenuOpen"
|
|
temporary
|
|
location="end"
|
|
class="d-md-none"
|
|
>
|
|
<!-- User info header -->
|
|
<div class="pa-4 pb-2">
|
|
<div class="d-flex align-center gap-3">
|
|
<VAvatar
|
|
size="40"
|
|
color="primary"
|
|
>
|
|
<span class="text-body-2 font-weight-medium text-white">
|
|
{{ (authStore.user?.first_name?.charAt(0) ?? '') + (authStore.user?.last_name?.charAt(0) ?? '') }}
|
|
</span>
|
|
</VAvatar>
|
|
<div class="min-w-0">
|
|
<div class="text-body-1 font-weight-bold text-truncate">
|
|
{{ authStore.user?.full_name }}
|
|
</div>
|
|
<div class="text-caption text-medium-emphasis text-truncate">
|
|
{{ authStore.user?.email }}
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<VDivider />
|
|
|
|
<VList nav>
|
|
<VListItem
|
|
v-for="item in mobileNavItems"
|
|
:key="item.to"
|
|
:to="item.to"
|
|
:prepend-icon="item.icon"
|
|
:title="item.title"
|
|
@click="isMobileMenuOpen = false"
|
|
/>
|
|
|
|
<VDivider class="my-2" />
|
|
|
|
<VListItem
|
|
prepend-icon="tabler-logout"
|
|
title="Uitloggen"
|
|
class="text-error"
|
|
@click="logout"
|
|
/>
|
|
</VList>
|
|
</VNavigationDrawer>
|
|
|
|
<VMain>
|
|
<VContainer
|
|
fluid
|
|
class="pa-4 pa-sm-6"
|
|
style="max-inline-size: 1440px;"
|
|
>
|
|
<RouterView v-slot="{ Component }">
|
|
<Suspense
|
|
:timeout="0"
|
|
@fallback="isFallbackStateActive = true"
|
|
@resolve="isFallbackStateActive = false"
|
|
>
|
|
<Component :is="Component" />
|
|
</Suspense>
|
|
</RouterView>
|
|
</VContainer>
|
|
</VMain>
|
|
|
|
<!-- Footer -->
|
|
<VFooter
|
|
app
|
|
color="transparent"
|
|
class="justify-center text-caption text-medium-emphasis py-3"
|
|
>
|
|
Powered by Crewli
|
|
</VFooter>
|
|
</VApp>
|
|
</template>
|