Bug (systemic pattern)
Several feature query-key factories produce a constant key while the underlying Supabase fetch varies by an all flag (which toggles a published = true filter). So the published-only and include-unpublished variants share one React Query cache entry — whichever runs last populates the cache for both → admin data (incl. unpublished) can bleed into public lists, or vice versa.
Instance 1 — festivals
fetchFestivals({ all }) filters published = true only when !all, but festivalsKeys.all() is the constant ["festivals"].
- Call sites:
src/pages/FestivalSelection.tsx → useFestivalsQuery() (published only); src/pages/admin/festivals/FestivalManagementTable.tsx → useFestivalsQuery({ all: true }).
Instance 2 — editions (found by Qodo on #57)
fetchFestivalEditions(festivalId, { all }) filters published = true only when !all, but editionsKeys.all(festivalId) is the constant ["festivals", festivalId, "editions"].
- Call sites:
src/pages/EditionSelection.tsx → useFestivalEditionsForFestivalQuery(festivalId) (published only); src/pages/admin/festivals/FestivalEditionManagement.tsx → …({ all: true }).
Pre-existing
Neither instance was introduced by the restructure PRs (#54 festivals, #57 editions) — both exist identically on main. Those PRs are deliberately kept mechanical (no behavior change), so the behavioral fix is split out here.
Suggested fix (same shape for both)
Encode the filter into the key; keep mutation invalidation broad so all variants refresh:
- festivals:
festivalsKeys.list({ all }) => ["festivals", { all: !!all }]
- editions:
editionsKeys.list(festivalId, { all }) => ["festivals", festivalId, "editions", { all: !!all }]
- point each
queryOptions factory at the new key; mutations keep invalidating the broad prefix (["festivals"] / ["festivals", festivalId, "editions"]).
Audit TODO
As each remaining feature moves to src/api, check for the same shape (constant key + all/filter-dependent fetch) and fix it the same way — start with sets (setsByEditionQuery / setsQuery, #58).
Context
Found by Qodo review on #54 (festivals) and #57 (editions). Part of restructure #52 / epic #51.
Bug (systemic pattern)
Several feature query-key factories produce a constant key while the underlying Supabase fetch varies by an
allflag (which toggles apublished = truefilter). So the published-only and include-unpublished variants share one React Query cache entry — whichever runs last populates the cache for both → admin data (incl. unpublished) can bleed into public lists, or vice versa.Instance 1 — festivals
fetchFestivals({ all })filterspublished = trueonly when!all, butfestivalsKeys.all()is the constant["festivals"].src/pages/FestivalSelection.tsx→useFestivalsQuery()(published only);src/pages/admin/festivals/FestivalManagementTable.tsx→useFestivalsQuery({ all: true }).Instance 2 — editions (found by Qodo on #57)
fetchFestivalEditions(festivalId, { all })filterspublished = trueonly when!all, buteditionsKeys.all(festivalId)is the constant["festivals", festivalId, "editions"].src/pages/EditionSelection.tsx→useFestivalEditionsForFestivalQuery(festivalId)(published only);src/pages/admin/festivals/FestivalEditionManagement.tsx→…({ all: true }).Pre-existing
Neither instance was introduced by the restructure PRs (#54 festivals, #57 editions) — both exist identically on
main. Those PRs are deliberately kept mechanical (no behavior change), so the behavioral fix is split out here.Suggested fix (same shape for both)
Encode the filter into the key; keep mutation invalidation broad so all variants refresh:
festivalsKeys.list({ all }) => ["festivals", { all: !!all }]editionsKeys.list(festivalId, { all }) => ["festivals", festivalId, "editions", { all: !!all }]queryOptionsfactory at the new key; mutations keep invalidating the broad prefix (["festivals"]/["festivals", festivalId, "editions"]).Audit TODO
As each remaining feature moves to
src/api, check for the same shape (constant key +all/filter-dependent fetch) and fix it the same way — start with sets (setsByEditionQuery/setsQuery, #58).Context
Found by Qodo review on #54 (festivals) and #57 (editions). Part of restructure #52 / epic #51.