SvelteKit public site — layout shell + all 8 pages from playground #98
Labels
No labels
domain:backend
domain:devops
domain:frontend
No milestone
No project
No assignees
1 participant
Notifications
Due date
No due date set.
Dependencies
No dependencies set.
Reference
forgejo_admin/westside-landing#98
Loading…
Add table
Add a link
Reference in a new issue
No description provided.
Delete branch "%!s()"
Deleting a branch is permanent. Although the deleted branch may continue to exist for a short time before it actually gets removed, it CANNOT be undone in most cases. Continue?
Type
Feature
Lineage
Ports westside-playground HTML to production SvelteKit. Depends on public API endpoints in basketball-api.
Repo
forgejo_admin/westside-appUser Story
As a visitor, I want to browse the Westside Kings and Queens website with real team data and coach bios so that the site feels professional and up-to-date.
story:WS-S26
Context
The westside-playground has 8 annotated public pages with
@svelte-notestranslation guides. westside-app already hasadapter-staticconfigured with 13+ existing authenticated routes using Keycloak. Theconvention-sveltekit-spadefines the full architecture.No auth needed for public pages. Public and authenticated routes coexist via SvelteKit route groups.
Architecture: Route Groups
SvelteKit route groups isolate layouts. Public pages get their own layout (no Keycloak). Authenticated pages keep their existing layout (Keycloak init + auth guards). The group name does not appear in the URL.
Scope
1. Route group restructuring:
src/routes/(public)/directorysrc/routes/(app)/directory(app)/+layout.svelte(with Keycloak) to(app)/+layout.svelte+layout.svelte(just CSS import)src/routes/+page.svelte(auth redirect). Migrate redirect logic into(app)/+layout.svelteauth guardtryouts/from auth routes (replaced by public). Keepteams/[id]andcoaches/[id]under (app) — these are auth detail pages, no collision with public list pages2. Public layout shell (
(public)/+layout.svelte):$staterune with localStorage$effectqueens-activebody class via<svelte:body>3. CSS (
src/app.css):shared/style.css+layout.svelte4. Public fetch helper (
src/lib/public-api.js):Separate from existing
apiFetch(which requires Keycloak tokens). Public pages MUST usepublicFetch, NOTapiFetch.5. Static pages (copy-paste from playground + layout):
(public)/+page.svelte— home (hero, value props, Marcus quote, explore cards)(public)/about/+page.svelte— program info, divisions, recruitment, philosophy(public)/sponsors/+page.svelte— Snoopy, NanoReleaf, Coach West, FAQ(public)/schedule/+page.svelte— tournaments, practice grid (hardcoded)(public)/tryouts/+page.svelte— past history, upcoming (hardcoded)(public)/gear/+page.svelte— jersey photos from MinIO, KQ toggle6. Dynamic pages (fetch from basketball-api public endpoints):
(public)/teams/+page.svelte—publicFetch('/public/teams'), roster cards, coach anchor links(public)/staff/+page.svelte— static coach cards (bios hardcoded from playground, no API)7. Inline nav cleanup:
(app)/tryouts/+page.svelte(lines 9-15)(app)/register/+page.svelte(line 182)Translation Reference
Read
@svelte-notesin each playground HTML file before coding. Key patterns:data-program-content→{#if program === 'kings'}blocksinitKQToggle()→ gone, Svelte reactivity replaces it(public)/+layout.svelte(render once).queens-active→<svelte:body class:queens-active={program === 'queens'}>onMount+publicFetchFile Targets
src/routes/+layout.svelte— ROOT: minimal, justimport '../app.css'src/routes/+layout.js—export const ssr = falsesrc/routes/(public)/+layout.svelte— public nav/footer/togglesrc/routes/(public)/+page.svelte— homesrc/routes/(public)/about/+page.sveltesrc/routes/(public)/staff/+page.sveltesrc/routes/(public)/teams/+page.sveltesrc/routes/(public)/schedule/+page.sveltesrc/routes/(public)/tryouts/+page.sveltesrc/routes/(public)/gear/+page.sveltesrc/routes/(public)/sponsors/+page.sveltesrc/routes/(app)/+layout.svelte— existing Keycloak layout, movedsrc/routes/(app)/— all existing auth routes moved heresrc/lib/public-api.js— unauthenticated fetch helpersrc/app.css— from playgroundshared/style.csssrc/routes/+page.svelte(old auth redirect — replaced by (public)/+page.svelte)(app)/tryouts/— replaced by (public)/tryouts(app)/register/+page.svelte— strip inline navAcceptance Criteria
Test Expectations
npm run buildsucceeds with adapter-staticConstraints
convention-sveltekit-spaexactly+page.server.ts— all client-sidepublicFetch, NOTapiFetch— no Keycloak dependencyChecklist
Depends On
Related
convention-sveltekit-spa— architecture referenceScope Review: NEEDS_REFINEMENT
Review note:
review-431-2026-03-26Template complete, traceability triangle intact, playground sources verified. Three issues found:
+layout.svelteinitializes Keycloak and guards all protected routes. Issue says "no keycloak, no auth" but doesn't describe how authenticated routes (/admin,/coach,/my-players, etc.) survive the layout replacement. Must add a Layout Strategy section (route groups, conditional rendering, or layout reset)./tryouts/+page.svelte(lines 9-15) has its own nav that will double-render with the new layout shell. Add to file targets.$lib/api.jsrequires Keycloak tokens. Dynamic pages need rawfetch()or a public API utility. One line in Constraints prevents the most likely agent mistake.Review Response
1. Auth coexistence strategy
The existing
+layout.sveltewith Keycloak stays intact for authenticated routes. The public pages use a route group layout:SvelteKit route groups (
(public)vs(app)) get different layouts. No conflict. The root+layout.sveltebecomes minimal (just shared CSS import), each group has its own layout.2. Duplicate nav cleanup
Added to scope: strip inline nav from existing route files (
/tryouts/+page.sveltelines 9-15, and any other routes that duplicate nav). File targets updated to include cleanup of existing routes that will live under the(public)group.3. Public fetch pattern
Public pages use plain
fetch()— no auth wrapper needed:This is separate from the existing
apiFetch(which requires Keycloak tokens). Constraint added: "Dynamic pages usepublicFetch, NOTapiFetch. No Keycloak dependency."Scope Review (Re-review): NEEDS_REFINEMENT
Review note:
review-431-2026-03-26(updated)Previous 3 issues all have correct solutions in comment #8102, but the issue body has not been updated — agents execute against the body, not comments. Five issues found:
(app)/is not in Scope, File Targets, or Checklist. This is a major structural change the agent won't know to do.src/routes/...but should besrc/routes/(public)/.... Also missingsrc/routes/(app)/+layout.svelteandsrc/lib/public-api.js./register/+page.svelteline 182) has same double-render problem as tryouts. Placement decision needed:(public)or(app)?Scope Review (3rd review): NEEDS_REFINEMENT
Review note:
review-431-2026-03-26-r3Issue body rewrite is excellent — route groups, publicFetch, nav cleanup, file targets all verified against codebase. Two remaining gaps:
src/routes/+page.sveltehas auth redirect logic (lines 6-11). The new(public)/+page.sveltetakes over/, but the issue doesn't say to delete the existing root page or where the redirect logic migrates (likely(app)/+layout.svelte).(app)/tryouts/+page.svelte(moved) and(public)/tryouts/+page.svelte(new) resolve to/tryouts. SvelteKit will error. Must clarify: existing tryouts page is replaced (not moved to (app)), or relocated to a different URL.Review Response v3
Finding 1: Root +page.svelte disposition
The existing
src/routes/+page.svelte(auth redirect logic) gets deleted. The(public)/+page.sveltetakes over/as the home page. The auth redirect logic (if authenticated → role dashboard) moves to(app)/+layout.svelteas part of its auth guard.Adding to Scope section 1: "Delete existing
src/routes/+page.svelte(auth redirect). Migrate its redirect logic into(app)/+layout.svelteauth guard."Finding 2: Route collision at /tryouts
Routes that exist in BOTH
(app)/and(public)/are not moved to (app)/. They are replaced by the public version. Affected routes:/tryouts— public version replaces authenticated version/teams— public version replaces authenticated version/coaches→/staff— different URL, no collisionThe existing
(app)/tryouts/and(app)/teams/authenticated pages are deleted. If authenticated team/tryout views are needed later, they get different URLs (e.g./admin/tryouts,/my-team).Adding to Scope section 1: "Routes with both public and auth versions: public wins at the base URL. Delete
tryouts/,teams/,coaches/from(app)/— these are replaced by(public)/versions."Scope Review v4: NEEDS_REFINEMENT
Review note:
review-431-2026-03-26All 5 v3 issues are resolved in the updated issue body. One new critical issue found:
teams/[id]/+page.svelteandcoaches/[id]/+page.svelteare authenticated detail views (fetch by ID viaapiFetch). They are NOT duplicates of the public list pages ((public)/teamsand(public)/staff). No route collision exists --/coaches/123and/staffare different URLs. Deleting these directories breaks links from admin dashboard, player profiles, and coach profiles (5+ cross-page links). These routes should be KEPT under(app)/, not deleted.Fix needed: Remove DELETE lines for
(app)/teams/and(app)/coaches/. Update architecture tree to show(app)/teams/[id]/...and(app)/coaches/[id]/...as retained routes. Clarify in Scope section 1 thatteams/[id]andcoaches/[id]are auth detail routes that coexist alongside the new public list pages.Once corrected, this ticket is READY.
Scope Review v5: READY
Review note:
review-431-2026-03-26v4 fix verified — DELETE lines for
(app)/teams/and(app)/coaches/removed, architecture tree shows them as retained auth detail routes, Scope section 1 explicitly keepsteams/[id]andcoaches/[id]under(app). All 17 file targets verified against codebase. Traceability triangle complete. Template fully populated.This ticket is approved to move from
todotonext_up.