11-Apr-2026 — replace hardcoded component with seeded CMS page #214
build 583 — commit e80fb2c
What changed for users
- New CMS-managed Privacy Policy page seeded at about-us/privacy-policy on both fresh bootstraps and existing environments, populated with the group's longName from System Settings
- The page covers what is stored on device (JWTs, display preferences) with PECR Reg 6(4) strictly-necessary justification, plus analytics options (Google, Cloudflare cookieless), third-party services, and a forward link to the technical article
- Privacy Policy is automatically added to footer.legals so it is discoverable through the conventional footer pattern rather than orphaned in the navbar
- Any legacy root-level privacy-policy page is removed when the migration runs so environments do not end up with two copies
- New Allow Search checkbox in action-buttons row settings (off by default) renders a search input above the cards that filters by title or content text, matching the existing album-index search
Technical
- Remove hardcoded PrivacyPolicyComponent and its app-routing entry; the privacy policy is now editable through the CMS like any other page
- Add createPrivacyPolicyPage() and PRIVACY_POLICY_PATH constant to sample-data templates and wire it into createAllSamplePageContent() for the bootstrap path
- Add 20260404000000 migration using replaceOne+upsert against the page-content collection so re-runs pick up the latest group name from System Settings, and createOrUpdateKey(ConfigKey.SYSTEM, ...) for the footer.legals update via the proper config helpers rather than raw collection access
- Refactor 20260319 committee-page and 20260325 navbar-access-levels migrations to read system config via the systemConfig() helper and write via createOrUpdateKey(ConfigKey.SYSTEM, ...) instead of hand rolled findOne/updateOne against the config collection
- Extract a reusable DynamicContentSearchInputComponent and pure filterColumnsBySearchText() helper from the album-index view so the search input, debounce, URL/localStorage sync, and column filtering live in exactly one place
- Add an opt-in allowSearch flag on PageContentRow exposed via the Mongoose schema, consumed by both DynamicContentViewIndex and ActionButtons through the shared component and helper