Unify navigation tabs with displayMode and use built in pages for autofilled links#1002
Unify navigation tabs with displayMode and use built in pages for autofilled links#1002rchlfryn wants to merge 42 commits intofeat/provisioningfrom
displayMode and use built in pages for autofilled links#1002Conversation
Migration Safety CheckFound 5 potential issues: 20260418_120100_backfill_nav_builtin_pages.ts Warning (line 162): DELETE keyword detected - review for data loss DELETE FROM navigations_relsWarning (line 179): DELETE keyword detected - review for data loss DELETE FROM navigations_relsWarning (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 |
|
Preview deployment: https://nav-updates.preview.avy-fx.org |
…for prod migration
…-in pages that are nav links
…Item(), push button rendering into RenderNavLink, and render button mode tabs inline on mobile
|
Github is struggling today, but ready for re-review |
busbyk
left a comment
There was a problem hiding this comment.
Looks good, just one comment.
| // 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' }, | ||
| }) | ||
| } | ||
| } | ||
|
|
There was a problem hiding this comment.
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?
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
navTabhelper with adisplayModefield, 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
navTabhelper — replaces the oldtopLevelNavTab/singleLinkNavTabsplit. Every tab has the same shape: adisplayModeradio (Dropdown / Link / Button) alongside an enabled toggle, withlink/itemsfields conditional on the selected mode. This gives admins flexibility while keeping all tabs rendered through the same code path.BannerDescriptioncomponent — reusable Payload admin component that wraps@payloadcms/ui'sBannerwith an icon, used for contextual info banners on nav tabs.displayModeclears the fields not used by the new mode. A customDisplayModeFieldclient component wipes the in-form values immediately (no save needed), and a collectionbeforeChangehook (clearUnusedTabData) provides a server-side safety net so the DB stays clean even if the admin bypasses the UI.displayMode: 'button'renders as a styled callout button in both desktop and mobile nav. Donate is no longer special-cased inHeader/utils.tsor the nav components; it flows through the same unified array as every other tab.Header/utils.tsnow 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.builtInPagereferences.Per-tab defaults (admin can change via
displayMode):isRecordtype guard — extracted tosrc/utilities/isRecord.ts;findDocumentsWithBlockReferencesand the new nav hook both consume it instead of redefining locally.How to test
pnpm seeddisplayModeradio defaults as listed above and the admin UI swaps between link/items appropriately when toggled.displayModeand 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 theoptions.displayModecolumn pluslinkanditemsstructures to every tab innavigations(and_navigations_v). Defaults per tab:dropdownfor forecasts/observations/weather/education/accidents/about/support;linkfor blog/events;buttonfor donate. Existing rows inherit defaults viaALTER 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
fetchrather 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