fix: maintain Services dropdown header from one canonical source

The site header / Services mega-dropdown was duplicated across two render
systems (Astro pages via Base.astro->nav.html, and ~80 pre-rendered static
public/**/index.html pages each embedding their own copy). They had drifted
into 5 different variants (missing 'New Carrier Setup', misplaced Healthcare
column, NEW vs FREE badges, em-dash encoding differences), so
dev.performancewest.net, the order pages, and the rest of the site disagreed.

- Make site/src/partials/nav.html the single source of truth (adopts the most
  complete variant).
- Add scripts/sync_nav.py to rewrite every static page's <nav> block from
  nav.html (idempotent; --check guards against drift in CI/deploy).
- Run the sync automatically in deploy.sh and scripts/deploy-dev.sh.
- Deprecate scripts/inject_healthcare_nav.py (now delegates to sync_nav.py).
- Neutralize the broken no-op SiteNav.astro component.

All 80 headers + the Astro-built order pages now render the identical dropdown.
This commit is contained in:
justin 2026-06-05 14:27:24 -05:00
parent 695ace207c
commit bd9a70607f
86 changed files with 250 additions and 645 deletions

View file

@ -1,82 +1,21 @@
#!/usr/bin/env python3
"""Inject the Healthcare nav column into the pre-rendered static pages.
"""DEPRECATED -- superseded by scripts/sync_nav.py.
The site is mostly static HTML under site/public/**/index.html, each carrying
its own copy of the Services mega-dropdown (desktop + mobile). The Astro
Base.astro layout reads src/partials/nav.html, but the static pages do NOT,
so adding a sector to nav.html alone does not show up on those pages.
Historically this script injected just the Healthcare column into the static
pages, which caused the Services dropdown to drift (different sectors / badges /
ordering on different pages). The header is now maintained in ONE place
(site/src/partials/nav.html) and synced wholesale into every static page by
scripts/sync_nav.py.
This injects the (canonical) Healthcare block from nav.html into every static
page that has the dropdown but is missing Healthcare. Idempotent.
This shim simply delegates to sync_nav.py so any existing automation that still
calls inject_healthcare_nav.py keeps working.
"""
import re
import runpy
import sys
from pathlib import Path
ROOT = Path(__file__).resolve().parents[1]
PUBLIC = ROOT / "site" / "public"
NAV = ROOT / "site" / "src" / "partials" / "nav.html"
nav = NAV.read_text()
m_desk = re.search(
r'(<p class="text-\[11px\][^>]*>Healthcare</p>.*?npi-compliance-check.*?</a>)',
nav, re.S,
)
m_mob = re.search(
r'(<p class="text-xs font-semibold[^>]*>Healthcare</p>.*?npi-compliance-check.*?</a>)',
nav, re.S,
)
if not (m_desk and m_mob):
sys.exit("Could not extract Healthcare blocks from nav.html")
DESKTOP_BLOCK = m_desk.group(1)
MOBILE_BLOCK = m_mob.group(1)
# Desktop insertion: place the Healthcare block at the end of Column 3, right
# before the "Form a Business" CTA that closes that column.
DESKTOP_ANCHOR = '<a href="/order/formation" class="mt-3 block py-2 px-3 text-sm font-medium text-white bg-pw-700 hover:bg-pw-800 rounded-lg text-center transition-colors">Form a Business</a>'
# Mobile insertion: place Healthcare right before the mobile Corporate heading.
MOBILE_ANCHOR = '<p class="text-xs font-semibold text-slate-500 uppercase tracking-wider px-2 pt-3">Corporate</p>'
def inject(html: str) -> tuple[str, bool]:
if "services-menu" not in html:
return html, False # no dropdown on this page
if "npi-compliance-check" in html:
return html, False # already has Healthcare
changed = False
if DESKTOP_ANCHOR in html and DESKTOP_BLOCK not in html:
html = html.replace(DESKTOP_ANCHOR, DESKTOP_BLOCK + " " + DESKTOP_ANCHOR, 1)
changed = True
if MOBILE_ANCHOR in html and MOBILE_BLOCK not in html:
html = html.replace(MOBILE_ANCHOR, MOBILE_BLOCK + " " + MOBILE_ANCHOR, 1)
changed = True
return html, changed
def main():
files = sorted(PUBLIC.rglob("index.html")) + [PUBLIC / "404.html"]
touched, skipped, nodrop = 0, 0, 0
for f in files:
if not f.exists():
continue
html = f.read_text()
new, changed = inject(html)
if changed:
f.write_text(new)
touched += 1
elif "services-menu" not in html:
nodrop += 1
else:
skipped += 1
print(f"injected: {touched} already-had/partial: {skipped} no-dropdown: {nodrop}")
if __name__ == "__main__":
main()
print("inject_healthcare_nav.py is deprecated; delegating to sync_nav.py", file=sys.stderr)
target = Path(__file__).with_name("sync_nav.py")
sys.argv = [str(target)]
runpy.run_path(str(target), run_name="__main__")