fix: portal shows stale events from localStorage after user_id unlinked
The portal store merged events from the API with localStorage events without ever pruning stale entries. When /auth/me returned empty portal_events (e.g. after a person's user_id was cleared), localStorage events persisted, causing "registratie niet ophalen" when /portal/me correctly returned 404. Now when /auth/me succeeds, API data is the source of truth — stored events not confirmed by the API are dropped. localStorage fallback is only used when the API call fails (network error). Also adds an end-to-end test covering the full register → approve → portal/me flow including festival hierarchy. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -46,16 +46,42 @@ function writeStoredActiveEventId(id: string | null): void {
|
||||
else localStorage.removeItem(STORAGE_ACTIVE_EVENT)
|
||||
}
|
||||
|
||||
function mergeEvents(apiEvents: PortalEvent[], stored: PortalEvent[]): PortalEvent[] {
|
||||
/**
|
||||
* 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>()
|
||||
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 || '',
|
||||
})
|
||||
|
||||
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))
|
||||
@@ -84,7 +110,7 @@ export const usePortalStore = defineStore('portal', () => {
|
||||
* 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])
|
||||
const merged = mergeEvents([], [...readStoredEvents(), ...userEvents.value, event], false)
|
||||
userEvents.value = merged
|
||||
persistEvents()
|
||||
if (!activeEventId.value || !merged.some(e => e.event_id === activeEventId.value)) {
|
||||
@@ -99,14 +125,16 @@ export const usePortalStore = defineStore('portal', () => {
|
||||
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)
|
||||
userEvents.value = mergeEvents(apiEvents, stored, apiSucceeded)
|
||||
persistEvents()
|
||||
}
|
||||
catch (e) {
|
||||
@@ -154,7 +182,10 @@ export const usePortalStore = defineStore('portal', () => {
|
||||
)
|
||||
persistEvents()
|
||||
}
|
||||
catch {
|
||||
catch (err) {
|
||||
if (import.meta.env.DEV) {
|
||||
console.warn('[portal] fetchCurrentPerson failed for event_id:', eid, err)
|
||||
}
|
||||
currentPerson.value = null
|
||||
}
|
||||
finally {
|
||||
|
||||
Reference in New Issue
Block a user