- Add <link rel="preload"> for hero background image (LCP element) in index.html
- Remove blocking APP_INITIALIZER for config.json and station config loading
- Config loads now fire asynchronously in App.ngOnInit() (browser only)
- SSR/prerender renders instantly with default config — no network wait
- Improves LCP, Time to First Byte, and initial paint for SEO
- ThemeService: remove isPlatformBrowser guard so CSS custom properties
are applied during SSR/prerender — server-rendered HTML now carries the
correct color scheme and gradients (fixes missing colors on test site).
- main.ts: add provideClientHydration() so Angular preserves the server-
rendered DOM on the client instead of destroying and rebuilding it
(fixes jarring full-page reload between static and dynamic versions).
- Tests: update theme.service.spec.ts to match new behaviour, expand
coverage with gradient tests and empty-value handling (9 tests).
Fix structured-data.component.spec.ts import paths. All 54 tests pass.
The PlatformLocation static initializer in @angular/common/router
requires @angular/compiler to be loaded first. Move the compiler
import before any Angular router/common imports so the JIT facade
is available when the module loads.
- Install @angular/ssr and @angular/platform-server (v21.2.x)
- Create server entry point (server.ts) and server bootstrap (main.server.ts)
- Add server routes config (app.server.routes.ts) with 5 prerendered routes:
/, /about, /schedule, /events, /contact
- Update angular.json with server/ssr/prerender build configuration
- Add prerender-routes.txt for build-time static route generation
- Fix SSR compatibility:
- App component: guard DOM access with isPlatformBrowser
- StructuredDataComponent: skip head manipulation on server
- ThemeService: skip CSS variable application on server
- ShowService/EventService: return empty arrays on API failure
(graceful fallback during prerender with no backend)
- Prerendered output verified: 5 static HTML routes in dist/
Add SeoService for dynamic metadata management:
- Per-route document titles (e.g., 'KMountain Flower Radio — About')
- Meta descriptions from station config per route
- Canonical URLs, Open Graph tags, Twitter Card tags
- Input sanitization (HTML stripping, URL validation, attribute escaping)
- noindex robots directives for /login and /admin routes
- Centralized router watcher (zero per-component boilerplate)
Add StructuredDataService + StructuredDataComponent for JSON-LD:
- Organization schema (NCBOrganization) site-wide with contact info
- WebSite schema with publisher info
- WebPage schema per-route with breadcrumbs
- Dynamic Event schema on /events page
- RadioSeason schema on /schedule page
Wire up SeoService.watchRouter() in app-root ngOnInit.
Wire up StructuredDataComponent in app-root template.
Wire up EventsComponent and ScheduleComponent to feed data to
StructuredDataService for dynamic schema injection.
Includes full test suite for SeoService (22 tests).
- Import RouterLinkWithHref in footer and events components (required for queryParams binding on <a>)
- Add social media fields to DEFAULT_CONFIG in station-config.service.ts
Backend:
- /api/admin/users: GET list, POST create, PUT update, DELETE
- Admin users can be added with email, display name, and password
- Self-deletion and last-admin deletion are blocked
- Admin role auto-assigned on creation
Frontend:
- AdminUsersTabComponent: list + add/delete admin users
- AdminUserService: HTTP client for admin user CRUD
- AdminUser interface
- 'Admins' tab added to admin dashboard
Tests:
- 10 new tests for admin user management endpoints
- 140 total tests pass (130 original + 10 new)
The Programs feature is superseded by Shows, which provides a more
flexible model with schedule slots. This commit removes all Programs
code across frontend and backend:
Frontend:
- Delete admin-program-form component (ts/html/scss)
- Delete program.service.ts and program.ts interface
- Remove Programs tab, form modal, and all CRUD logic from admin
component (ts + html)
Backend:
- Delete programs API router
- Remove Program ORM model and ProgramCreate/Update/Response schemas
- Remove programs router registration from main.py
- Remove Program from seed data, seed helpers, and truncate/reset
- Document bootstrap admin login via KMTN_ADMIN_USERNAME/PASSWORD
env vars, including setup guide, curl examples, and config table
entries across docker-compose and bare-metal deployment sections
- Add KMTN_JWT_SECRET_KEY to all deployment config examples and
production checklist
- Fix docker-entrypoint.sh: remove unused API_UPSTREAM variable from
config.json envsubst (only API_PUBLIC_URL is needed)
- Fix app-config.service: default apiBaseUrl now reads from Angular
environment instead of empty string, and config.json path drops
leading ./ for cleaner resolution