refactor(portal): move stores and rename portal auth store
- apps/portal/src/stores/useAuthStore.ts →
apps/app/src/stores/portal/usePortalAuthStore.ts. The export and
defineStore id are renamed (useAuthStore → usePortalAuthStore,
'auth' → 'portalAuth') so it can coexist with the organizer's
apps/app/src/stores/useAuthStore. Lazy import inside
resetPortalStoresSync() updated to the new path.
- apps/portal/src/stores/usePortalStore.ts →
apps/app/src/stores/portal/usePortalStore.ts (no name change —
apps/app does not have a usePortalStore).
All call sites in moved pages/components now import from
@/stores/portal/{usePortalStore,usePortalAuthStore} and call
usePortalAuthStore() instead of useAuthStore().
PR-B2 will merge this back into a single context-aware auth store.
Also includes the C.1 page meta-block updates (layout: 'PortalLayout'
| 'PublicLayout', context: 'portal') that were left unstaged after
the page-rename commit picked up only the path change.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -1,7 +1,7 @@
|
||||
<script setup lang="ts">
|
||||
import { useAuthStore } from '@/stores/useAuthStore'
|
||||
import { usePortalAuthStore } from '@/stores/portal/usePortalAuthStore'
|
||||
|
||||
const authStore = useAuthStore()
|
||||
const authStore = usePortalAuthStore()
|
||||
const router = useRouter()
|
||||
|
||||
const userInitials = computed(() => {
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
<script setup lang="ts">
|
||||
import { usePortalStore } from '@/stores/usePortalStore'
|
||||
import { usePortalStore } from '@/stores/portal/usePortalStore'
|
||||
|
||||
defineProps<{
|
||||
eventId: string
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
<script setup lang="ts">
|
||||
import StatusCard from '@/components/portal/StatusCard.vue'
|
||||
import { usePortalStore } from '@/stores/usePortalStore'
|
||||
import { usePortalStore } from '@/stores/portal/usePortalStore'
|
||||
import { useMyShifts } from '@/composables/api/usePortalShifts'
|
||||
import type { PortalPersonPayload } from '@/types/portal'
|
||||
|
||||
|
||||
@@ -2,9 +2,10 @@
|
||||
definePage({
|
||||
name: 'artist-advance',
|
||||
meta: {
|
||||
layout: 'portal',
|
||||
layout: 'PortalLayout',
|
||||
requiresAuth: false,
|
||||
requiresToken: true,
|
||||
context: 'portal',
|
||||
},
|
||||
})
|
||||
|
||||
|
||||
@@ -1,15 +1,16 @@
|
||||
<script setup lang="ts">
|
||||
import OverzichtTab from '@/components/event/OverzichtTab.vue'
|
||||
import RoosterTab from '@/components/event/RoosterTab.vue'
|
||||
import ClaimenTab from '@/components/event/ClaimenTab.vue'
|
||||
import InformatieTab from '@/components/event/InformatieTab.vue'
|
||||
import { usePortalStore } from '@/stores/usePortalStore'
|
||||
import OverzichtTab from '@/components/portal/event/OverzichtTab.vue'
|
||||
import RoosterTab from '@/components/portal/event/RoosterTab.vue'
|
||||
import ClaimenTab from '@/components/portal/event/ClaimenTab.vue'
|
||||
import InformatieTab from '@/components/portal/event/InformatieTab.vue'
|
||||
import { usePortalStore } from '@/stores/portal/usePortalStore'
|
||||
|
||||
definePage({
|
||||
name: 'portal-event-detail',
|
||||
meta: {
|
||||
layout: 'portal',
|
||||
layout: 'PortalLayout',
|
||||
requiresAuth: true,
|
||||
context: 'portal',
|
||||
navMode: 'event',
|
||||
},
|
||||
})
|
||||
|
||||
@@ -1,12 +1,13 @@
|
||||
<script setup lang="ts">
|
||||
import EventCard from '@/components/portal/EventCard.vue'
|
||||
import { usePortalStore } from '@/stores/usePortalStore'
|
||||
import { usePortalStore } from '@/stores/portal/usePortalStore'
|
||||
|
||||
definePage({
|
||||
name: 'portal-evenementen',
|
||||
meta: {
|
||||
layout: 'portal',
|
||||
layout: 'PortalLayout',
|
||||
requiresAuth: true,
|
||||
context: 'portal',
|
||||
navMode: 'platform',
|
||||
navTitle: 'Mijn evenementen',
|
||||
},
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
<script setup lang="ts">
|
||||
import { useAuthStore } from '@/stores/useAuthStore'
|
||||
import { usePortalStore } from '@/stores/usePortalStore'
|
||||
import { usePortalAuthStore } from '@/stores/portal/usePortalAuthStore'
|
||||
import { usePortalStore } from '@/stores/portal/usePortalStore'
|
||||
import { useUpdateProfile, useUpdatePassword } from '@/composables/api/usePortalProfile'
|
||||
import {
|
||||
useMfaStatus,
|
||||
@@ -19,14 +19,15 @@ import type { ApiErrorResponse } from '@/types/api'
|
||||
definePage({
|
||||
name: 'portal-profiel',
|
||||
meta: {
|
||||
layout: 'portal',
|
||||
layout: 'PortalLayout',
|
||||
requiresAuth: true,
|
||||
context: 'portal',
|
||||
navMode: 'platform',
|
||||
navTitle: 'Mijn profiel',
|
||||
},
|
||||
})
|
||||
|
||||
const authStore = useAuthStore()
|
||||
const authStore = usePortalAuthStore()
|
||||
const portal = usePortalStore()
|
||||
const router = useRouter()
|
||||
const route = useRoute()
|
||||
|
||||
@@ -2,8 +2,9 @@
|
||||
definePage({
|
||||
name: 'volunteer-register-info',
|
||||
meta: {
|
||||
layout: 'blank',
|
||||
layout: 'PortalLayout',
|
||||
requiresAuth: false,
|
||||
context: 'portal',
|
||||
},
|
||||
})
|
||||
</script>
|
||||
|
||||
@@ -1,17 +1,18 @@
|
||||
<script setup lang="ts">
|
||||
import { useAllMyShifts } from '@/composables/api/usePortalShifts'
|
||||
import { useAuthStore } from '@/stores/useAuthStore'
|
||||
import { usePortalAuthStore } from '@/stores/portal/usePortalAuthStore'
|
||||
import type { AllMyShiftsAssignment } from '@/types/portal-shift'
|
||||
|
||||
definePage({
|
||||
name: 'portal-shifts',
|
||||
meta: {
|
||||
layout: 'portal',
|
||||
layout: 'PortalLayout',
|
||||
requiresAuth: true,
|
||||
context: 'portal',
|
||||
},
|
||||
})
|
||||
|
||||
const auth = useAuthStore()
|
||||
const auth = usePortalAuthStore()
|
||||
const { data: eventGroups, isLoading, isError, refetch } = useAllMyShifts()
|
||||
|
||||
const statusConfig: Record<string, { label: string; color: string }> = {
|
||||
|
||||
@@ -9,8 +9,9 @@ import { apiClient } from '@/lib/axios'
|
||||
definePage({
|
||||
name: 'set-password',
|
||||
meta: {
|
||||
layout: 'blank',
|
||||
layout: 'PortalLayout',
|
||||
requiresAuth: false,
|
||||
context: 'portal',
|
||||
},
|
||||
})
|
||||
|
||||
|
||||
@@ -1,25 +1,24 @@
|
||||
<script setup lang="ts">
|
||||
import { emailValidator } from '@core/utils/validators'
|
||||
import FieldRenderer from '@/components/public-form/FieldRenderer.vue'
|
||||
import FormConfirmation from '@/components/public-form/FormConfirmation.vue'
|
||||
import FormErrorState from '@/components/public-form/FormErrorState.vue'
|
||||
import FormStepper from '@/components/public-form/FormStepper.vue'
|
||||
import SubmitterDetails from '@/components/public-form/SubmitterDetails.vue'
|
||||
import FieldRenderer from '@/components/shared/public-form/FieldRenderer.vue'
|
||||
import FormConfirmation from '@/components/shared/public-form/FormConfirmation.vue'
|
||||
import FormErrorState from '@/components/shared/public-form/FormErrorState.vue'
|
||||
import FormStepper from '@/components/shared/public-form/FormStepper.vue'
|
||||
import SubmitterDetails from '@/components/shared/public-form/SubmitterDetails.vue'
|
||||
import { extractErrorBody, useFetchPublicFormSchema } from '@/composables/api/usePublicForm'
|
||||
import { usePublicFormSections } from '@/composables/api/usePublicFormSections'
|
||||
import { usePublicFormTimeSlots } from '@/composables/api/usePublicFormTimeSlots'
|
||||
import { useFormDraft } from '@/composables/useFormDraft'
|
||||
import { isStepValid, useFormSteps } from '@form-schema/composables/useFormSteps'
|
||||
import { formatFieldValue } from '@form-schema/composables/formatFieldValue'
|
||||
import { isStepValid, useFormSteps } from '@/composables/forms/composables/useFormSteps'
|
||||
import { formatFieldValue } from '@/composables/forms/composables/formatFieldValue'
|
||||
import { providePublicFormLocale, providePublicFormToken } from '@/composables/publicFormInjection'
|
||||
import { FormFieldType } from '@form-schema/types/formBuilder'
|
||||
import type { FormErrorCode, PublicFormField } from '@form-schema/types/formBuilder'
|
||||
import { FormFieldType } from '@/composables/forms/types/formBuilder'
|
||||
import type { FormErrorCode, PublicFormField } from '@/composables/forms/types/formBuilder'
|
||||
|
||||
definePage({
|
||||
name: 'public-form-register',
|
||||
meta: {
|
||||
layout: 'blank',
|
||||
requiresAuth: false,
|
||||
layout: 'PublicLayout',
|
||||
},
|
||||
})
|
||||
|
||||
|
||||
@@ -1,17 +1,15 @@
|
||||
<script setup lang="ts">
|
||||
import { useAuthStore } from '@/stores/useAuthStore'
|
||||
import { usePortalAuthStore } from '@/stores/portal/usePortalAuthStore'
|
||||
|
||||
definePage({
|
||||
name: 'register-success',
|
||||
meta: {
|
||||
layout: 'portal',
|
||||
requiresAuth: false,
|
||||
navMode: 'platform',
|
||||
layout: 'PublicLayout',
|
||||
},
|
||||
})
|
||||
|
||||
const route = useRoute('register-success')
|
||||
const authStore = useAuthStore()
|
||||
const authStore = usePortalAuthStore()
|
||||
|
||||
const eventName = computed(() => (route.query.event as string) || 'het evenement')
|
||||
const bannerUrl = computed(() => (route.query.banner as string) || null)
|
||||
|
||||
110
apps/app/src/stores/portal/usePortalAuthStore.ts
Normal file
110
apps/app/src/stores/portal/usePortalAuthStore.ts
Normal file
@@ -0,0 +1,110 @@
|
||||
import { defineStore } from 'pinia'
|
||||
import { computed, ref } from 'vue'
|
||||
import { apiClient } from '@/lib/axios'
|
||||
import type { AuthMeUser } from '@/types/portal'
|
||||
|
||||
export const usePortalAuthStore = defineStore('portalAuth', () => {
|
||||
const user = ref<AuthMeUser | null>(null)
|
||||
const isInitialized = ref(false)
|
||||
|
||||
const isAuthenticated = computed(() => !!user.value)
|
||||
|
||||
function setUser(data: AuthMeUser | null) {
|
||||
user.value = data
|
||||
}
|
||||
|
||||
async function resetPortalStoresSync(): Promise<void> {
|
||||
const { usePortalStore } = await import('@/stores/portal/usePortalStore')
|
||||
usePortalStore().reset()
|
||||
}
|
||||
|
||||
function clearState() {
|
||||
user.value = null
|
||||
void resetPortalStoresSync()
|
||||
}
|
||||
|
||||
function handleUnauthorized() {
|
||||
clearState()
|
||||
// Do NOT reset isInitialized — the full page reload (below) resets all JS state.
|
||||
// Resetting it here causes a race condition: the async 401 interceptor fires
|
||||
// after doInitialize() sets isInitialized=true, putting the app back into
|
||||
// a loading state that never resolves.
|
||||
|
||||
if (typeof window !== 'undefined') {
|
||||
const path = window.location.pathname
|
||||
const publicPaths = ['/login', '/wachtwoord-vergeten', '/wachtwoord-resetten', '/verify-email-change']
|
||||
if (!publicPaths.some(p => path.startsWith(p)) && !path.startsWith('/register')) {
|
||||
window.location.href = '/login'
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
async function login(email: string, password: string): Promise<void> {
|
||||
const { data } = await apiClient.post<{
|
||||
success: boolean
|
||||
data: { user: AuthMeUser }
|
||||
}>('/auth/login', { email, password })
|
||||
|
||||
// Token is set automatically via httpOnly Set-Cookie header
|
||||
setUser(data.data.user)
|
||||
|
||||
// Validate by fetching full user data
|
||||
const ok = await fetchUser()
|
||||
if (!ok) throw new Error('Sessie kon niet worden gestart.')
|
||||
}
|
||||
|
||||
async function fetchUser(): Promise<boolean> {
|
||||
try {
|
||||
const { data } = await apiClient.get<{ success: boolean; data: AuthMeUser }>('/auth/me')
|
||||
setUser(data.data)
|
||||
|
||||
return true
|
||||
}
|
||||
catch {
|
||||
clearState()
|
||||
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
async function logout(): Promise<void> {
|
||||
try {
|
||||
await apiClient.post('/auth/logout')
|
||||
}
|
||||
catch {
|
||||
// Ignore network errors; still clear local session
|
||||
}
|
||||
clearState()
|
||||
}
|
||||
|
||||
let initializePromise: Promise<void> | null = null
|
||||
|
||||
function initialize(): Promise<void> {
|
||||
if (isInitialized.value) return Promise.resolve()
|
||||
if (!initializePromise)
|
||||
initializePromise = doInitialize()
|
||||
|
||||
return initializePromise
|
||||
}
|
||||
|
||||
async function doInitialize(): Promise<void> {
|
||||
try {
|
||||
await fetchUser()
|
||||
}
|
||||
finally {
|
||||
isInitialized.value = true
|
||||
}
|
||||
}
|
||||
|
||||
return {
|
||||
user,
|
||||
isAuthenticated,
|
||||
isInitialized,
|
||||
setUser,
|
||||
login,
|
||||
logout,
|
||||
fetchUser,
|
||||
initialize,
|
||||
handleUnauthorized,
|
||||
}
|
||||
})
|
||||
254
apps/app/src/stores/portal/usePortalStore.ts
Normal file
254
apps/app/src/stores/portal/usePortalStore.ts
Normal file
@@ -0,0 +1,254 @@
|
||||
import { defineStore } from 'pinia'
|
||||
import { computed, ref } from 'vue'
|
||||
import { apiClient } from '@/lib/axios'
|
||||
import type { AuthMeUser, PortalEvent, PortalPersonPayload } from '@/types/portal'
|
||||
|
||||
const STORAGE_EVENTS = 'crewli_portal_user_events_v1'
|
||||
const STORAGE_ACTIVE_EVENT = 'crewli_portal_active_event_id_v1'
|
||||
|
||||
function readStoredEvents(): PortalEvent[] {
|
||||
if (typeof localStorage === 'undefined') return []
|
||||
try {
|
||||
const raw = localStorage.getItem(STORAGE_EVENTS)
|
||||
if (!raw) return []
|
||||
|
||||
const parsed = JSON.parse(raw) as unknown
|
||||
if (!Array.isArray(parsed)) return []
|
||||
|
||||
return parsed.filter(
|
||||
(e): e is PortalEvent =>
|
||||
typeof e === 'object'
|
||||
&& e !== null
|
||||
&& 'event_id' in e
|
||||
&& 'event_name' in e
|
||||
&& 'person_status' in e,
|
||||
)
|
||||
}
|
||||
catch {
|
||||
return []
|
||||
}
|
||||
}
|
||||
|
||||
function writeStoredEvents(events: PortalEvent[]): void {
|
||||
if (typeof localStorage === 'undefined') return
|
||||
localStorage.setItem(STORAGE_EVENTS, JSON.stringify(events))
|
||||
}
|
||||
|
||||
function readStoredActiveEventId(): string | null {
|
||||
if (typeof localStorage === 'undefined') return null
|
||||
|
||||
return localStorage.getItem(STORAGE_ACTIVE_EVENT)
|
||||
}
|
||||
|
||||
function writeStoredActiveEventId(id: string | null): void {
|
||||
if (typeof localStorage === 'undefined') return
|
||||
if (id) localStorage.setItem(STORAGE_ACTIVE_EVENT, id)
|
||||
else localStorage.removeItem(STORAGE_ACTIVE_EVENT)
|
||||
}
|
||||
|
||||
/**
|
||||
* Merge API events with locally stored events.
|
||||
*
|
||||
* When the API call succeeded (`apiSucceeded = true`), the API is the source
|
||||
* of truth: stored events that are NOT confirmed by the API are dropped.
|
||||
* This prevents stale localStorage entries from showing events the user no
|
||||
* longer has access to (e.g. after user_id was cleared).
|
||||
*
|
||||
* When the API call failed (`apiSucceeded = false`), we fall back to stored
|
||||
* events as a best-effort cache.
|
||||
*/
|
||||
function mergeEvents(apiEvents: PortalEvent[], stored: PortalEvent[], apiSucceeded: boolean): PortalEvent[] {
|
||||
const map = new Map<string, PortalEvent>()
|
||||
|
||||
if (apiSucceeded) {
|
||||
// API is source of truth — start with API events only
|
||||
for (const e of apiEvents) {
|
||||
const prev = stored.find(s => s.event_id === e.event_id)
|
||||
map.set(e.event_id, {
|
||||
...prev,
|
||||
...e,
|
||||
organisation_name: e.organisation_name || prev?.organisation_name || '',
|
||||
})
|
||||
}
|
||||
}
|
||||
else {
|
||||
// API failed — merge stored + whatever API returned (likely empty)
|
||||
for (const e of stored) map.set(e.event_id, { ...e })
|
||||
for (const e of apiEvents) {
|
||||
const prev = map.get(e.event_id)
|
||||
map.set(e.event_id, {
|
||||
...prev,
|
||||
...e,
|
||||
organisation_name: e.organisation_name || prev?.organisation_name || '',
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
return Array.from(map.values()).sort((a, b) => b.start_date.localeCompare(a.start_date))
|
||||
}
|
||||
|
||||
export const usePortalStore = defineStore('portal', () => {
|
||||
const activeEventId = ref<string | null>(readStoredActiveEventId())
|
||||
const userEvents = ref<PortalEvent[]>([])
|
||||
const currentPerson = ref<PortalPersonPayload | null>(null)
|
||||
const isLoadingEvents = ref(false)
|
||||
const isLoadingPerson = ref(false)
|
||||
const loadError = ref<string | null>(null)
|
||||
|
||||
const activeEvent = computed(() => userEvents.value.find(e => e.event_id === activeEventId.value) ?? null)
|
||||
|
||||
function persistActiveEvent(): void {
|
||||
writeStoredActiveEventId(activeEventId.value)
|
||||
}
|
||||
|
||||
function persistEvents(): void {
|
||||
writeStoredEvents(userEvents.value)
|
||||
}
|
||||
|
||||
/**
|
||||
* Call after successful public registration so the volunteer sees the event on the dashboard.
|
||||
* TODO: replace with `portal_events` from GET /auth/me when the API exposes it.
|
||||
*/
|
||||
function savePendingEventFromRegistration(event: PortalEvent): void {
|
||||
const merged = mergeEvents([], [...readStoredEvents(), ...userEvents.value, event], false)
|
||||
userEvents.value = merged
|
||||
persistEvents()
|
||||
if (!activeEventId.value || !merged.some(e => e.event_id === activeEventId.value)) {
|
||||
activeEventId.value = event.event_id
|
||||
persistActiveEvent()
|
||||
}
|
||||
}
|
||||
|
||||
async function loadUserEventsFromApiAndStorage(): Promise<void> {
|
||||
isLoadingEvents.value = true
|
||||
loadError.value = null
|
||||
try {
|
||||
const stored = readStoredEvents()
|
||||
let apiEvents: PortalEvent[] = []
|
||||
let apiSucceeded = false
|
||||
try {
|
||||
const { data } = await apiClient.get<{ success: boolean; data: AuthMeUser }>('/auth/me')
|
||||
apiEvents = data.data.portal_events ?? []
|
||||
apiSucceeded = true
|
||||
}
|
||||
catch {
|
||||
// /auth/me failed — still show locally stored registrations
|
||||
}
|
||||
userEvents.value = mergeEvents(apiEvents, stored, apiSucceeded)
|
||||
persistEvents()
|
||||
}
|
||||
catch (e) {
|
||||
loadError.value = 'Kon je evenementen niet laden.'
|
||||
userEvents.value = readStoredEvents()
|
||||
}
|
||||
finally {
|
||||
isLoadingEvents.value = false
|
||||
}
|
||||
}
|
||||
|
||||
function resolveActiveEventId(): void {
|
||||
if (userEvents.value.length === 0) {
|
||||
activeEventId.value = null
|
||||
persistActiveEvent()
|
||||
|
||||
return
|
||||
}
|
||||
const current = activeEventId.value
|
||||
if (current && userEvents.value.some(e => e.event_id === current)) {
|
||||
persistActiveEvent()
|
||||
|
||||
return
|
||||
}
|
||||
activeEventId.value = userEvents.value[0]!.event_id
|
||||
persistActiveEvent()
|
||||
}
|
||||
|
||||
async function fetchCurrentPerson(): Promise<void> {
|
||||
currentPerson.value = null
|
||||
const eid = activeEventId.value
|
||||
if (!eid) return
|
||||
|
||||
isLoadingPerson.value = true
|
||||
try {
|
||||
const { data } = await apiClient.get<{ success: boolean; data: PortalPersonPayload }>(
|
||||
'/portal/me',
|
||||
{ params: { event_id: eid } },
|
||||
)
|
||||
currentPerson.value = data.data
|
||||
const status = data.data.status
|
||||
const pid = data.data.id
|
||||
userEvents.value = userEvents.value.map(row =>
|
||||
row.event_id === eid ? { ...row, person_id: pid, person_status: status } : row,
|
||||
)
|
||||
persistEvents()
|
||||
}
|
||||
catch (err) {
|
||||
if (import.meta.env.DEV) {
|
||||
console.warn('[portal] fetchCurrentPerson failed for event_id:', eid, err)
|
||||
}
|
||||
currentPerson.value = null
|
||||
}
|
||||
finally {
|
||||
isLoadingPerson.value = false
|
||||
}
|
||||
}
|
||||
|
||||
const isHydrated = ref(false)
|
||||
let hydratePromise: Promise<void> | null = null
|
||||
|
||||
async function hydrateAfterAuth(): Promise<void> {
|
||||
await loadUserEventsFromApiAndStorage()
|
||||
resolveActiveEventId()
|
||||
await fetchCurrentPerson()
|
||||
isHydrated.value = true
|
||||
}
|
||||
|
||||
/**
|
||||
* Hydrate portal data if not already done. Safe to call multiple times —
|
||||
* only the first call triggers the actual hydration.
|
||||
*/
|
||||
function hydrateIfNeeded(): Promise<void> {
|
||||
if (isHydrated.value) return Promise.resolve()
|
||||
if (!hydratePromise)
|
||||
hydratePromise = hydrateAfterAuth().finally(() => { hydratePromise = null })
|
||||
|
||||
return hydratePromise
|
||||
}
|
||||
|
||||
function setActiveEvent(eventId: string): void {
|
||||
if (!userEvents.value.some(e => e.event_id === eventId)) return
|
||||
activeEventId.value = eventId
|
||||
persistActiveEvent()
|
||||
void fetchCurrentPerson()
|
||||
}
|
||||
|
||||
function reset(): void {
|
||||
activeEventId.value = null
|
||||
userEvents.value = []
|
||||
currentPerson.value = null
|
||||
loadError.value = null
|
||||
isHydrated.value = false
|
||||
hydratePromise = null
|
||||
if (typeof localStorage !== 'undefined') {
|
||||
localStorage.removeItem(STORAGE_EVENTS)
|
||||
localStorage.removeItem(STORAGE_ACTIVE_EVENT)
|
||||
}
|
||||
}
|
||||
|
||||
return {
|
||||
activeEventId,
|
||||
userEvents,
|
||||
currentPerson,
|
||||
activeEvent,
|
||||
isLoadingEvents,
|
||||
isLoadingPerson,
|
||||
isHydrated,
|
||||
loadError,
|
||||
savePendingEventFromRegistration,
|
||||
hydrateAfterAuth,
|
||||
hydrateIfNeeded,
|
||||
setActiveEvent,
|
||||
fetchCurrentPerson,
|
||||
reset,
|
||||
}
|
||||
})
|
||||
Reference in New Issue
Block a user