feat: public site layout shell + all 8 pages from playground #99
No reviewers
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-app!99
Loading…
Add table
Add a link
Reference in a new issue
No description provided.
Delete branch "98-sveltekit-public-site"
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?
Summary
(public)and(app)route groups perconvention-sveltekit-spa(app)/unchangedChanges
src/routes/+layout.svelte-- New minimal root layout (just CSS import)src/routes/(public)/+layout.svelte-- Public layout shell: site-nav, hamburger toggle, KQ toggle state, footersrc/routes/(public)/+page.svelte-- Home (hero, value props, explore cards, how to join)src/routes/(public)/about/+page.svelte-- About (philosophy, divisions, recruitment, location)src/routes/(public)/staff/+page.svelte-- Staff bios (static, hardcoded from playground with anchor IDs)src/routes/(public)/teams/+page.svelte-- Dynamic: fetches/public/teamsvia publicFetch, KQ togglesrc/routes/(public)/tryouts/+page.svelte-- Tryout history + upcoming (static, KQ toggle)src/routes/(public)/schedule/+page.svelte-- Schedule (tournaments + practice grid, KQ toggle)src/routes/(public)/gear/+page.svelte-- Jersey gallery from MinIO (KQ toggle)src/routes/(public)/sponsors/+page.svelte-- Sponsors + partners (static)src/routes/(app)/+layout.svelte-- Existing Keycloak layout moved here, auth redirect merged insrc/routes/(app)/register/+page.svelte-- Inline nav stripped (layout provides it)src/lib/public-api.js-- Unauthenticated fetch helper (plain fetch, null on error)src/lib/program.svelte.js-- Shared KQ toggle state using Svelte 5$staterunesrc/app.css-- Replaced with playgroundshared/style.css(full design system)src/routes/+page.svelte(auth redirect moved to app layout)src/routes/tryouts/+page.svelte(replaced by public version)(app)/teams/[id]and(app)/coaches/[id](auth detail pages, no collision)Test Plan
npm run buildsucceeds with adapter-static/,/about,/staff,/teams,/tryouts,/schedule,/gear,/sponsors/public/teams(or shows empty state gracefully)/admin,/coach,/my-players,/signin,/registerReview Checklist
Related Notes
convention-sveltekit-spa-- architecture referencebasketball-api#176(public teams endpoint)QA Review
Scope Verification
Reviewed all 35 changed files (4511 additions, 3119 deletions). Verified against issue #98 spec and all 7 review comments.
Architecture
(public)/and(app)/correctly isolate layouts. Group names do not appear in URLs.import '../app.css'and{@render children()}. Correct.PUBLIC_APP_ROUTEScorrectly scoped to (app)-only routes.<svelte:body class:queens-active>. No Keycloak dependency.+page.svelteredirect logic migrated to(app)/+layout.svelteonMount. Signin-only redirect (not/since that's now the public home).Files Checked
Issues Found and Fixed
tierOrderdeclared but never referenced in(public)/teams/+page.svelte. Sort logic inlined values instead. Fixed in follow-up commit.Pre-existing Issues (Not Introduced by This PR)
handleSignIn()function declared but never used in(app)/+layout.svelte(carried over from original layout)(app)/admin/teams/+page.svelte(pre-existing click handlers without keyboard events)VERDICT: PASS
All 8 public pages, route group restructuring, layout shell, KQ toggle, and publicFetch helper implemented correctly per issue #98 spec. Build succeeds. One minor fix applied (unused variable). No regressions to auth routes.
PR #99 Review
PR: feat: public site layout shell + all 8 pages from playground
Branch:
98-sveltekit-public-siteParent issue: #98
Files changed: 35 (+4509, -3119)
Commits: 2 (feat commit + cleanup of unused
tierOrdervariable)DOMAIN REVIEW
Tech stack: SvelteKit 5 (Svelte 5 runes), adapter-static SPA, CSS custom properties, Keycloak auth (existing)
Route group architecture -- CORRECT. The restructuring into
(public)/and(app)/route groups follows SvelteKit conventions precisely. Route groups isolate layouts without affecting URLs. The rootsrc/routes/contains only+layout.svelte,+layout.js,(public)/, and(app)/-- no orphan routes.Critical check 1: Existing auth routes survive restructuring -- PASS. All 12 existing authenticated route directories are present under
(app)/: admin, checkout, coach, coaches/[id], forgot-password, jersey, my-players, players/[id], register, reset-password, signin, teams/[id]. The(app)/+layout.svelteretains Keycloak initialization, thePUBLIC_APP_ROUTESallow-list, and the auth guard$effectthat redirects unauthenticated users to/signin. The old root+page.svelte(auth redirect) was correctly deleted since/is now the public home page.Critical check 2: No Keycloak dependency in public pages -- PASS. The
(public)/+layout.svelteimports onlysvelte,$app/stores, and$lib/program.svelte.js. Zero Keycloak references. All 8 public pages import onlyprogramStoreand/orpublicFetch. ThepublicFetchhelper insrc/lib/public-api.jsuses plainfetch()with no auth headers.Critical check 3: CSS matches playground -- PASS. The
app.cssis a properly organized design system migrated from playgroundshared/style.css. Design tokens (colors, typography scale, spacing scale),.queens-activebody class override (swaps--color-redto pink#e91e8c),.sr-onlyutility,:focus-visibleoutlines, and responsive breakpoints are all present.Critical check 4: Route collisions resolved -- PASS. Public
/teams(list page) lives in(public)/teams/+page.svelte. Auth/teams/[id](detail page) lives in(app)/teams/[id]/+page.svelte. No collision -- different URL patterns. Public/staffvs auth/coaches/[id]-- same principle. The old(app)/tryouts/was correctly deleted (404 confirms), replaced by(public)/tryouts/.Critical check 5: Build succeeds -- PASS. CI runs
npm run check+npm run build+test -f build/index.html. Both commits show all checks successful.Svelte 5 patterns -- Good. Uses
$state,$props,$effect,$derivedrunes correctly. TheprogramStoreinsrc/lib/program.svelte.jsuses the getter/setter pattern around$statefor cross-component reactivity. localStorage read happens inonMount(correct SSR guard), localStorage write happens in$effectwithtypeof windowcheck.publicFetch graceful degradation -- Good. Returns
nullon non-OK responses and network errors. Teams page checksif (data)before assignment. Loading state transitions tofalseregardless.Accessibility -- Good. Nav has
aria-label="Main navigation", hamburger hasaria-expandedandaria-controls,.sr-onlyclass exists,:focus-visibleoutlines are defined.Sign-out redirect -- The
(app)layout sign-out callsgoto('/'), which now lands on the public home page instead of the old auth redirect page. This is correct and expected behavior for a site with a public front page.BLOCKERS
None.
Test coverage assessment: The repo has zero test infrastructure (no vitest, no playwright, no test runner in
package.json). This is a pre-existing condition onmain-- not introduced by this PR. The CI validates viasvelte-check(type checking) andvite build(compilation). Adding a test framework is discovered scope, not a blocker for this PR.No secrets in code: The
publicFetchfallback URL (https://basketball-api.tail5b443a.ts.net) is a Tailscale funnel URL, not a secret. MinIO asset URLs are similarly public CDN-style paths. No API keys, tokens, or credentials found.No unvalidated user input: Public pages only display data; there are no forms or user input fields on public routes (registration/signin are under
(app)with existing validation).No DRY violations in auth paths: Auth logic lives exclusively in
(app)/+layout.svelte. Public layout has zero auth code. Clean separation.NITS
Staff page: issue spec inconsistency -- The acceptance criteria in #98 says "Staff page fetches real coach data from /public/coaches via publicFetch" but Section 6 of the scope says "static coach cards (bios hardcoded from playground, no API)". The implementation follows the scope (hardcoded). The acceptance criteria checkbox should be updated to reflect reality, or this should be tracked as discovered scope for a future dynamic staff page.
Hardcoded MinIO URLs in gear page -- Gear page images use hardcoded
https://minio-api.tail5b443a.ts.net/assets/westside/jerseys/...URLs rather than constructing them from an environment variable. The logo in the public layout nav also uses a hardcoded MinIO URL. Consider extracting aVITE_ASSETS_URLenv var for consistency with theVITE_API_URLpattern inpublicFetch. Low risk since these are public asset URLs, but would simplify environment portability.Nav logo
alttext -- The nav logo usesalt="Westside Kings & Queens"which is fine, but thewidth="44" height="44"is hardcoded. If the logo aspect ratio changes, this could cause distortion. Minor since the logo is controlled.No
<svelte:head>on all pages -- The schedule page includes SEO metadata (<svelte:head>) based on the WebFetch summary. Verify all 8 public pages include<title>and<meta name="description">in<svelte:head>for SEO. This is a public-facing site.Queens program empty states -- Schedule and tryouts pages show Queens-specific "coming soon" messaging, which is good. Verify teams page also degrades gracefully when Queens teams data returns empty from the API (not just when the API is unreachable).
SOP COMPLIANCE
98-sveltekit-public-sitereferences issue #98convention-sveltekit-spa, but no plan slug (this may be standalone board work, not plan-driven)PROCESS OBSERVATIONS
Deployment frequency: This is a large PR (35 files, +4509/-3119) but it's a coherent structural change -- route group restructuring + page porting. Smaller would have been splitting the restructuring from the page additions, but the two are tightly coupled (you can't test public pages without the route group).
Change failure risk: LOW. The route group pattern is well-understood SvelteKit. Auth routes are moved unchanged. CI validates build. The main risk is visual regression (CSS differences from playground), which requires manual visual testing.
Discovered scope: (1) Test framework setup for westside-app -- track as a separate issue. (2) Staff page dynamic data from /public/coaches API -- track when basketball-api endpoint is ready. (3) SEO metadata audit across all 8 public pages. (4) Asset URL environment variable extraction.
VERDICT: APPROVED