feat(portal): auth persistence, shift visibility, profile page, and UI polish
- Fix session persistence: add loading state to App.vue, hydrate portal store in router guards so page refresh preserves auth + event context - Fix shift visibility for festivals: query child event time slots so shifts on sub-events appear in the portal - Add profile page with editable personal info and password change - Add backend endpoints: PUT /portal/profile and PUT /portal/password - Fix registration form: make first_name/last_name editable for logged-in users - Restyle login page: remove Vuexy illustration, center form with Crewli branding - Improve dashboard StatusCard with action cards, icons, and upcoming shift count - Enhance shift cards with status border colors and availability progress bars Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -4,6 +4,8 @@ const props = defineProps<{
|
||||
eventName: string
|
||||
registeredAt?: string | null
|
||||
nextShiftSummary?: string | null
|
||||
upcomingCount?: number
|
||||
availableCount?: number | null
|
||||
}>()
|
||||
|
||||
const registeredLabel = computed(() => {
|
||||
@@ -88,6 +90,7 @@ const registeredLabel = computed(() => {
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Quick action cards -->
|
||||
<VRow class="mb-6">
|
||||
<VCol
|
||||
cols="12"
|
||||
@@ -95,15 +98,23 @@ const registeredLabel = computed(() => {
|
||||
>
|
||||
<VCard
|
||||
:to="{ name: 'portal-my-shifts' }"
|
||||
variant="outlined"
|
||||
class="pa-4 h-100 text-decoration-none"
|
||||
class="h-100 text-decoration-none portal-action-card"
|
||||
elevation="1"
|
||||
>
|
||||
<div class="text-subtitle-2 text-medium-emphasis mb-1">
|
||||
Mijn Diensten
|
||||
</div>
|
||||
<div class="text-body-2">
|
||||
Rooster bekijken
|
||||
</div>
|
||||
<VCardText class="d-flex flex-column align-center text-center pa-4">
|
||||
<VIcon
|
||||
icon="tabler-calendar-check"
|
||||
size="28"
|
||||
color="primary"
|
||||
class="mb-2"
|
||||
/>
|
||||
<div class="text-subtitle-2 font-weight-bold mb-1">
|
||||
Mijn Diensten
|
||||
</div>
|
||||
<div class="text-caption text-medium-emphasis">
|
||||
Rooster bekijken
|
||||
</div>
|
||||
</VCardText>
|
||||
</VCard>
|
||||
</VCol>
|
||||
<VCol
|
||||
@@ -112,15 +123,23 @@ const registeredLabel = computed(() => {
|
||||
>
|
||||
<VCard
|
||||
:to="{ name: 'portal-claim-shifts' }"
|
||||
variant="outlined"
|
||||
class="pa-4 h-100 text-decoration-none"
|
||||
class="h-100 text-decoration-none portal-action-card"
|
||||
elevation="1"
|
||||
>
|
||||
<div class="text-subtitle-2 text-medium-emphasis mb-1">
|
||||
Diensten claimen
|
||||
</div>
|
||||
<div class="text-body-2">
|
||||
Schrijf je in voor diensten
|
||||
</div>
|
||||
<VCardText class="d-flex flex-column align-center text-center pa-4">
|
||||
<VIcon
|
||||
icon="tabler-calendar-plus"
|
||||
size="28"
|
||||
color="primary"
|
||||
class="mb-2"
|
||||
/>
|
||||
<div class="text-subtitle-2 font-weight-bold mb-1">
|
||||
Diensten Claimen
|
||||
</div>
|
||||
<div class="text-caption text-medium-emphasis">
|
||||
Schrijf je in
|
||||
</div>
|
||||
</VCardText>
|
||||
</VCard>
|
||||
</VCol>
|
||||
<VCol
|
||||
@@ -129,21 +148,30 @@ const registeredLabel = computed(() => {
|
||||
>
|
||||
<VCard
|
||||
:to="{ name: 'portal-profile' }"
|
||||
variant="outlined"
|
||||
class="pa-4 h-100 text-decoration-none"
|
||||
class="h-100 text-decoration-none portal-action-card"
|
||||
elevation="1"
|
||||
>
|
||||
<div class="text-subtitle-2 text-medium-emphasis mb-1">
|
||||
Profiel
|
||||
</div>
|
||||
<div class="text-body-2">
|
||||
Gegevens bekijken
|
||||
</div>
|
||||
<VCardText class="d-flex flex-column align-center text-center pa-4">
|
||||
<VIcon
|
||||
icon="tabler-user"
|
||||
size="28"
|
||||
color="primary"
|
||||
class="mb-2"
|
||||
/>
|
||||
<div class="text-subtitle-2 font-weight-bold mb-1">
|
||||
Mijn Profiel
|
||||
</div>
|
||||
<div class="text-caption text-medium-emphasis">
|
||||
Gegevens bekijken
|
||||
</div>
|
||||
</VCardText>
|
||||
</VCard>
|
||||
</VCol>
|
||||
</VRow>
|
||||
|
||||
<!-- Upcoming shift -->
|
||||
<div class="text-subtitle-1 font-weight-bold mb-2">
|
||||
Komende shift
|
||||
Komende dienst
|
||||
</div>
|
||||
<p
|
||||
v-if="nextShiftSummary"
|
||||
@@ -155,8 +183,57 @@ const registeredLabel = computed(() => {
|
||||
v-else
|
||||
class="text-body-2 text-medium-emphasis mb-0"
|
||||
>
|
||||
Er is nog geen shift ingepland. Je coördinator houdt je op de hoogte.
|
||||
Nog geen diensten ingepland.
|
||||
<RouterLink
|
||||
:to="{ name: 'portal-claim-shifts' }"
|
||||
class="text-primary font-weight-medium"
|
||||
>
|
||||
Diensten claimen
|
||||
</RouterLink>
|
||||
</p>
|
||||
|
||||
<!-- Quick stats -->
|
||||
<div
|
||||
v-if="upcomingCount !== undefined || availableCount !== null"
|
||||
class="d-flex flex-wrap gap-4 mt-4 pt-4"
|
||||
style="border-top: 1px solid rgba(var(--v-border-color), var(--v-border-opacity));"
|
||||
>
|
||||
<div
|
||||
v-if="upcomingCount !== undefined"
|
||||
class="text-body-2"
|
||||
>
|
||||
<VIcon
|
||||
icon="tabler-calendar-check"
|
||||
size="16"
|
||||
class="me-1"
|
||||
/>
|
||||
Diensten ingepland: <strong>{{ upcomingCount }}</strong>
|
||||
</div>
|
||||
<RouterLink
|
||||
v-if="availableCount !== null && availableCount !== undefined"
|
||||
:to="{ name: 'portal-claim-shifts' }"
|
||||
class="text-body-2 text-primary text-decoration-none"
|
||||
>
|
||||
<VIcon
|
||||
icon="tabler-calendar-plus"
|
||||
size="16"
|
||||
class="me-1"
|
||||
/>
|
||||
Beschikbare diensten bekijken
|
||||
</RouterLink>
|
||||
</div>
|
||||
</template>
|
||||
</VCard>
|
||||
</template>
|
||||
|
||||
<style scoped>
|
||||
.portal-action-card {
|
||||
transition: transform 0.15s ease, box-shadow 0.15s ease;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.portal-action-card:hover {
|
||||
transform: translateY(-2px);
|
||||
box-shadow: 0 4px 12px rgba(0, 0, 0, 0.1) !important;
|
||||
}
|
||||
</style>
|
||||
|
||||
Reference in New Issue
Block a user