Skip to content

Unify navigation tabs with displayMode and use built in pages for autofilled links#1002

Open
rchlfryn wants to merge 42 commits intofeat/provisioningfrom
nav-updates
Open

Unify navigation tabs with displayMode and use built in pages for autofilled links#1002
rchlfryn wants to merge 42 commits intofeat/provisioningfrom
nav-updates

Conversation

@rchlfryn
Copy link
Copy Markdown
Collaborator

@rchlfryn rchlfryn commented Mar 23, 2026

Description

Restructures the Navigation admin UI and seeds built-in page references for forecasts, observations, blog, and events so their links appear as proper CMS relationships rather than being hardcoded. Also unifies all nav tabs behind a single navTab helper with a displayMode field, letting admins pick how each tab renders (dropdown, single link, or button) without a code change.

Related Issues

Fixes #988
Fixes #825

Follow-ups:

Key Changes

  • Unified navTab helper — replaces the old topLevelNavTab / singleLinkNavTab split. Every tab has the same shape: a displayMode radio (Dropdown / Link / Button) alongside an enabled toggle, with link / items fields conditional on the selected mode. This gives admins flexibility while keeping all tabs rendered through the same code path.
  • BannerDescription component — reusable Payload admin component that wraps @payloadcms/ui's Banner with an icon, used for contextual info banners on nav tabs.
  • Tab descriptions — every tab now has an inline banner explaining what it does, where its data comes from, and whether admin edits may be overwritten by autofill.
  • Stale-data clearing on mode switch — switching a tab's displayMode clears the fields not used by the new mode. A custom DisplayModeField client component wipes the in-form values immediately (no save needed), and a collection beforeChange hook (clearUnusedTabData) provides a server-side safety net so the DB stays clean even if the admin bypasses the UI.
  • Universal button rendering — any tab with displayMode: 'button' renders as a styled callout button in both desktop and mobile nav. Donate is no longer special-cased in Header/utils.ts or the nav components; it flows through the same unified array as every other tab.
  • Forecasts renderingHeader/utils.ts now handles nested nav items (items with sub-items render as accordion sections in the dropdown). Multi-zone centers show "All Forecasts" + a "Zones" section with per-zone links.
  • Seed — fetches active forecast zones per tenant from NAC API; creates zone-specific built-in pages; populates forecasts, observations, blog, and events navigation entries with proper builtInPage references.
  • Data migration — backfills existing tenants' built-in pages and navigation references so no standalone post-deploy script is needed.

Per-tab defaults (admin can change via displayMode):

  • Forecasts — dropdown, autofilled from NAC zone data. Single-zone centers (e.g. SAC) show "Avalanche Forecast"; multi-zone centers (e.g. NWAC) show "All Forecasts" + a "Zones" accordion with per-zone items sorted by rank.
  • Observations — dropdown, autofilled with "Recent Observations" and "Submit Observations".
  • Blog / Events — single link to landing page.
  • Donate — button, to donation page.
  • Weather / Education / Accidents / About / Support — dropdown, admin-configured.
  • isRecord type guard — extracted to src/utilities/isRecord.ts; findDocumentsWithBlockReferences and the new nav hook both consume it instead of redefining locally.

How to test

  • Run pnpm seed
  • In admin, open Navigation. For each tab, verify the displayMode radio defaults as listed above and the admin UI swaps between link/items appropriately when toggled.
    • Forecasts
      • single-zone center (SAC): single "Avalanche Forecast" item, no zones section
      • multi-zone center (NWAC): "All Forecasts" item + "Zones" accordion with per-zone items
    • Observations: "Recent Observations" and "Submit Observations" as items
    • Blog / Events: link to their landing page
    • Donate: link renders as a button on the site
  • Flip a tab's displayMode and confirm the visible fields change (items hidden, link shown, etc.) without data loss.

Screenshots / Demo video

https://www.loom.com/share/2f9df8650db64dd6af417a94570e1bf3

Migration Explanation

Two migrations (run in order):

20260418_120000_convert_auto_nav_items.ts — schema migration. Adds the options.displayMode column plus link and items structures to every tab in navigations (and _navigations_v). Defaults per tab: dropdown for forecasts/observations/weather/education/accidents/about/support; link for blog/events; button for donate. Existing rows inherit defaults via ALTER TABLE ADD COLUMN DEFAULT.

20260418_120100_backfill_nav_builtin_pages.ts — data migration. Iterates all tenants, creates missing built-in pages (forecast zones, blog, events, observations, weather stations, mountain weather), and populates each tenant's navigation record with the new built-in page references. Uses raw SQL to bypass document-level validation, which would otherwise fail on pre-existing invalid fields in unrelated tabs. Clears stale draft versions so Payload regenerates them from the updated main tables on next load.

The data migration is self-contained — it fetches the NAC API directly via fetch rather than importing from @/services/nac, so it stays stable if service modules change later. If the NAC API is unreachable for a tenant at deploy time, that tenant's forecasts portion is skipped with a warning and the deploy continues; re-saving that tenant's navigation in the admin repopulates it.

Because the schema and data migrations ship together, there is no deploy-gap window between code deploy and data backfill — the moment the deploy completes, navigation is fully populated and the frontend can render real CMS links without hardcoded fallbacks.

Future enhancements / Questions

@rchlfryn rchlfryn changed the title Make automated topLevelNavItems use built-in pages Make automated topLevelNavItems use built-in pages & nav improvements Mar 23, 2026
@github-actions
Copy link
Copy Markdown
Contributor

github-actions Bot commented Mar 23, 2026

Migration Safety Check

Found 5 potential issues:

20260418_120100_backfill_nav_builtin_pages.ts

Warning (line 162): DELETE keyword detected - review for data loss

DELETE FROM navigations_rels

Warning (line 179): DELETE keyword detected - review for data loss

DELETE FROM navigations_rels

Warning (line 185): DELETE keyword detected - review for data loss

await db.run(sql`DELETE FROM navigations_forecasts_items WHERE _parent_id = ${navId}`)

Warning (line 227): DELETE keyword detected - review for data loss

await db.run(sql`DELETE FROM navigations_observations_items WHERE _parent_id = ${navId}`)

Warning (line 248): DELETE keyword detected - review for data loss

await db.run(sql`DELETE FROM _navigations_v WHERE parent_id = ${navId}`)

Review these patterns and add backup/restore logic if needed. See docs/migration-safety.md for guidance.

@github-actions
Copy link
Copy Markdown
Contributor

Preview deployment: https://nav-updates.preview.avy-fx.org

Comment thread src/components/Header/MobileNav.client.tsx Outdated
Comment thread src/components/Header/MobileNavItem.tsx Outdated
Comment thread src/components/Header/DesktopNav.client.tsx Outdated
Comment thread src/components/Header/utils.ts Outdated
Comment thread src/endpoints/seed/index.ts Outdated
Comment thread src/collections/Navigations/index.tsx Outdated
Comment thread src/collections/Navigations/index.tsx Outdated
Comment thread src/collections/Navigations/index.tsx Outdated
Comment thread src/collections/Navigations/index.tsx Outdated
Comment thread src/collections/Navigations/fields/navTab.ts
@rchlfryn rchlfryn changed the base branch from main to feat/provisioning April 27, 2026 17:08
@rchlfryn
Copy link
Copy Markdown
Collaborator Author

Github is struggling today, but ready for re-review

Copy link
Copy Markdown
Collaborator

@busbyk busbyk left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Looks good, just one comment.

Comment on lines +80 to +90
// SAC-specific observations archive link — revert this block when no longer needed
if (center === 'sac') {
const observations = topLevelNavItems.find((item) => item.label === 'Observations')
if (observations?.items) {
observations.items.push({
id: 'archive',
link: { type: 'internal', label: 'Observations Archive', url: '/observations-archive' },
})
}
}

Copy link
Copy Markdown
Collaborator

@busbyk busbyk Apr 28, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

With our ability to control the items in the observations tab now we should be able to remove this since they can add this manually. They'd just add a built-in page pointing to this URL. Right?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Use built-in page references for automated top level nav items Navigation improvements - donate tab & sort link refs

2 participants