New react app for seahaven
Find a file
Alexandre Brandizzi 80be702733
feat(vendors): structure the address into Street/City/State with autocomplete (#176)
* feat(vendors): structure the address into Street/City/State with autocomplete

The Vendor form carried one free-text "Address (optional)" line, with City,
State and Zip already present but hidden behind display:none, and a separate
"Google Maps URL" input someone had to paste into by hand.

Street Address, City and State are now three required fields. Typing three
characters in Street Address offers up to four candidates; picking one fills
all three at once, and typing without picking stays plain free text. The
Google Maps URL input is gone — the location is derived from the address, the
Street Address itself is the link in view mode, and a keyless map preview
renders once all three parts are present. Zip stays in the payload but out of
the form; the ticket scopes the visible set to three.

The suggestion algorithm, candidate cities and copy are ported from the
approved prototype rather than invented, so dev and design agree on what a
dispatcher sees. Suggestions are deterministic for a given input on purpose:
a reshuffling list moves a row out from under the pointer mid-click.

Test fixtures that predate the requirement now carry an address, so each test
still fails for the reason it is about. The two drawer tests asserting the
stored-URL "Open in Google Maps" row are rewritten to the behaviour that
replaced it.

Delivers SH-271.

* feat(work-orders): show the vendor location map in the Vendor dialog

The last of SH-271's five acceptance bullets. The Work Order Vendor dialog
gets one composed address line from the vendor dropdown payload, not the
structured parts the form and detail drawer hold, so the preview takes the
line directly — it is saved data either way, and the completeness rule exists
to stop a map of half-typed input, not to reject a stored address.

The dialog's hand-built maps URL now goes through the shared helper, so the
link and the preview cannot drift apart.

* test(vendors): update the browser specs and pixel baselines for the new address

`npm run verify` does not run Playwright, so the first push went out with the
browser suite still asserting the UI this ticket removes. Three assertions
were stale: the combined "Address (optional)" input, the "Google Maps URL
(optional)" input, and the detail drawer's separate "Open in Google Maps"
row — now replaced by the Street Address itself being the link, checked
against the derived href. A fourth test created a vendor without an address,
which the new requirement blocks; it fills one, so the test still fails only
for the reason it is about.

Baselines regenerated in mcr.microsoft.com/playwright:v1.61.1-noble, the image
CI uses — macOS font rendering produces different pixels. Exactly three of the
sixteen were rewritten (vendor add, edit, detail); the rest, including every
Work Orders shot, are byte-identical, which is the evidence that this change
stays inside the surfaces it claims.

* test(vendors): keep the map preview out of the pixel baselines

The visual suite mocks `**/api/**` and nothing else, so the address map
preview's iframe reached maps.google.com for real. Whether that frame paints,
and what it paints, depends on the network and on what Google serves that
minute — which is why `vendor-edit` failed in CI at 18178 differing pixels
while passing in a container that could not reach Google. Regenerating the
baseline would not have fixed it; it would have moved the flake.

Aborting the request pins the frame to a blank box, so the shot measures our
layout and nothing else. The committed baselines are unchanged by this — they
were already correct — and a second container run with no --update passes
16/16, which is the evidence the shot is now stable rather than merely green
once.

* fix(vendors): complete structured address map flows

* test(vendors): align add visual baseline with CI

---------

Co-authored-by: Codex Review Integration <codex-review@local.invalid>
2026-09-15 16:13:15 -03:00
.cursor/rules Feat/vite typescript migration (#16) 2026-06-18 14:41:17 -03:00
.github fix(observability): upload source maps under the client Sentry release (SH-342) (#189) 2026-09-14 15:21:15 -04:00
.husky Feat/vite typescript migration (#16) 2026-06-18 14:41:17 -03:00
config feat(observability): identify and scrub Sentry transactions 2026-09-03 17:12:49 -03:00
docs feat(observability): identify and scrub Sentry transactions 2026-09-03 17:12:49 -03:00
e2e feat(vendors): structure the address into Street/City/State with autocomplete (#176) 2026-09-15 16:13:15 -03:00
eslint-rules fix(lint): enforce error typography composition 2026-07-24 11:36:30 -03:00
public Merge pull request #35 from Sea-Haven-Industries/feature/wo-shared-ui 2026-07-21 14:03:58 -03:00
scripts fix(observability): upload source maps under the client Sentry release (SH-342) (#189) 2026-09-14 15:21:15 -04:00
src feat(vendors): structure the address into Street/City/State with autocomplete (#176) 2026-09-15 16:13:15 -03:00
terraform feat(terraform): ship dev content CD through Terraform (SH-300) (#180) 2026-09-11 13:40:14 -04:00
tmp/pr-descriptions Merge remote-tracking branch 'origin/dev' into feature/wo-uplift-pending-close-gate 2026-08-17 13:50:22 -03:00
.env Feat/vite typescript migration (#16) 2026-06-18 14:41:17 -03:00
.env.development Feat/vite typescript migration (#16) 2026-06-18 14:41:17 -03:00
.env.example feat: activate Sentry deployment environments 2026-09-03 15:10:01 -03:00
.env.production feat: activate Sentry deployment environments 2026-09-03 15:10:01 -03:00
.gitignore feat(terraform): ship dev content CD through Terraform (SH-300) (#180) 2026-09-11 13:40:14 -04:00
.prettierignore fix(vendors): complete shell and visual parity gates 2026-08-10 12:11:38 -03:00
.prettierrc Feat/vite typescript migration (#16) 2026-06-18 14:41:17 -03:00
AGENTS.md chore(governance): enforce frontend quality system (#53) 2026-07-24 16:47:34 -03:00
ARCHITECTURE_AND_CODE_QUALITY.md chore(governance): enforce frontend quality system (#53) 2026-07-24 16:47:34 -03:00
commitlint.config.js Feat/vite typescript migration (#16) 2026-06-18 14:41:17 -03:00
eslint.config.js feat(terraform): ship dev content CD through Terraform (SH-300) (#180) 2026-09-11 13:40:14 -04:00
index.html chore: correct Sea Haven branding and rewrite README (#25) 2026-07-17 13:17:21 -04:00
package-lock.json chore(deps-dev): bump fast-uri (#166) 2026-09-15 15:27:25 -03:00
package.json feat(terraform): ship dev content CD through Terraform (SH-300) (#180) 2026-09-11 13:40:14 -04:00
playwright.config.ts fix(vendors): complete shell and visual parity gates 2026-08-10 12:11:38 -03:00
playwright.visual.config.ts fix(vendors): complete shell and visual parity gates 2026-08-10 12:11:38 -03:00
QUALITY_GATES.md feat(terraform): ship dev content CD through Terraform (SH-300) (#180) 2026-09-11 13:40:14 -04:00
README.md feat(terraform): ship dev content CD through Terraform (SH-300) (#180) 2026-09-11 13:40:14 -04:00
REVIEW_AND_PR_FRAMEWORK.md fix(work-orders): treat vendor save as a live company assignment 2026-08-20 14:17:20 -03:00
tsconfig.json chore: upgrade frontend dependencies (#24) 2026-07-14 21:35:59 -03:00
tsconfig.node.json feat(observability): identify and scrub Sentry transactions 2026-09-03 17:12:49 -03:00
vite.config.ts feat(observability): identify and scrub Sentry transactions 2026-09-03 17:12:49 -03:00
vitest.config.ts Feat/vite typescript migration (#16) 2026-06-18 14:41:17 -03:00

SHOC Frontend (shoc-frontend-new)

CI Deploy TypeScript React Vite Terraform

Vite + React SPA for Sea Haven facility management (SHOC): work orders, vendor portal, uplifts, and related admin features. This is the selective rebuild of the legacy SHOC frontend — new code follows the IrisLoan.Admin conventions documented in docs/ARCHITECTURE_PLAN.md.

  • GitHub: Sea-Haven-Industries/shoc-frontend-new
  • Hosted at: https://dev.seahaven.com (dev environment; the only environment today)
  • Backend API: https://api.dev.seahaven.com/api (called directly, cross-origin) — source: Sea-Haven-Industries/shoc-backend

Architecture

Static SPA hosting on AWS, owned by HCP Terraform (terraform/README.md). CloudFront serves the built dist/ from a private S3 bucket using a current/previous origin group; the SPA calls the backend directly over HTTPS at VITE_API_URL (no /api proxy at the CDN — the backend allows CORS).

graph LR
    U[Browser] -->|HTTPS dev.seahaven.com| CF[CloudFront]
    CF -->|origin group OAC| S3[S3 seahaven-shoc-frontend-dev]
    CF -.->|viewer-request fn| FN[SPA rewrite → /index.html]
    U -->|HTTPS api.dev.seahaven.com/api CORS| API[SHOC backend API]
    GH[GitHub Actions] -->|OIDC upload releases/*| S3
    TF[HCP Terraform shoc-frontend-new-dev] -->|pointer origin_path invalidation| CF

Dev hosting and content CD are owned by HCP Terraform (SH-300). Staging still uses CloudFormation outputs and scripts/deploy-web.sh (SH-287).

Frontend stack: React 19, TypeScript, Vite, Tailwind CSS 4 + MUI, TanStack Query, React Router (via @generouted/react-router), React Hook Form + Zod, Ky HTTP client. Source layout: src/api/, src/domain/, src/app/ (see the architecture plan for the keep/discard migration matrix).

AWS Resources

HCP workspace shoc-frontend-new-dev — account 396287094661, region us-east-1. Defined in terraform/live/dev.

Resource Name Purpose
S3 bucket seahaven-shoc-frontend-dev Private origin (BLOCK_ALL, SSE, versioned; OAC-only reads)
CloudFront distribution (stack output DistributionId) HTTPS static hosting on dev.seahaven.com, ACM *.seahaven.com
CloudFront Function SpaRewrite Viewer-request rewrite of extensionless paths to /index.html (deep links)
IAM role githubdeploy-shoc-frontend-new-dev GitHub Actions OIDC deploy role, trust scoped to repo:Sea-Haven-Industries/shoc-frontend-new:ref:refs/heads/dev
Route 53 records A/AAAA apex alias in zone dev.seahaven.com (Z07671212N75U4YLPWZR8) Points the custom domain at CloudFront

No Lambdas, queues, or databases — this stack is static hosting only.

Configuration

Secrets

No Secrets Manager or SSM parameters. AWS access is OIDC only; the deploy role ARNs are deterministic and pinned in the workflows. The one GitHub Actions repo secret is:

Secret Purpose
SENTRY_AUTH_TOKEN Source-map upload by scripts/upload-sourcemaps.sh after a deploy

Environment variables (build-time, VITE_*)

Variable Description Dev value
VITE_API_URL Ky API base prefix, baked into the build at vite build /api (dev server) / https://api.dev.seahaven.com/api (production build)
VITE_API_TARGET Dev-proxy target for /api (Vite dev server only) http://localhost:5141

VITE_API_URL supplies the full API prefix — route paths in API_PATHS do not include /api. Absolute values must end with /api; vite.config.ts enforces this via config/api-url-contract.ts and fails the build otherwise. See .env.example, .env.development, and .env.production.

Pinned hosting constants (domain, certificate ARN, hosted zone) live in terraform/live/dev/main.tf.

Local Development

Requirements: Node.js ≥ 22.22.1 (CI/CD run Node 24), npm 11.16.0 (pinned via packageManager).

npm ci
cp .env.example .env   # then set VITE_API_URL=/api for local dev
npm run dev            # Vite dev server on port 3000, proxies /api → VITE_API_TARGET

The dev proxy expects the shoc-backend API at http://localhost:5141; override with VITE_API_TARGET (e.g. https://api.dev.seahaven.com to use the deployed dev API).

Command Description
npm run dev Start Vite dev server on port 3000
npm run build Type-check (tsc -b) and production build to dist/
npm run preview Preview the production build locally
npm test / npm run test:watch Vitest unit tests (once / watch)
npm run test:e2e / npm run test:e2e:ui Playwright e2e tests (headless / UI mode)
npm run lint / npm run lint:fix ESLint (check / auto-fix)
npm run format / npm run format:check Prettier (write / check)
npm run governance Governance checks (godfile, maintainability, Terraform, CD guards)
npm run verify All gates: format + lint + build + test + governance

npm run governance needs terraform and python3 on PATH for the Terraform and content-CD gates.

Husky + lint-staged run ESLint and Prettier on staged files at commit; commitlint enforces conventional commit messages. Run npx tsc --noEmit (or npm run build) before pushing to catch type errors early.

Contributing

  • Branch from dev with a kebab-case description and a prefix matching the work: feature/, bug/, hotfix/, chore/, docs/, or refactor/ (e.g. feature/vendor-portal-filters, chore/sea-haven-branding).
  • Commit messages follow Conventional Commits — commitlint rejects anything else at commit time.
  • Open PRs against dev. Both dev and main are protected: every PR needs a green CI run and an approving review from a code owner (@Sea-Haven-Industries/internal-dev); new pushes dismiss stale approvals. Merged branches are deleted automatically.
  • A PR that changes terraform/** may not also change application code (the terraform-isolation CI job); ship Terraform in its own PR.
  • Promotion flow: feature/* → dev (deployed to dev.seahaven.com through Terraform content CD once TERRAFORM_CONTENT_CD_ENABLED=true) → main (production promotion — no prod environment exists yet).

Deployment

No stored AWS keys — OIDC only. Infrastructure and content deploy separately:

  • CI (.github/workflows/ci.yaml) — on push and PRs to main/dev/staging, calls Sea-Haven-Industries/.github → ci-typescript-frontend.yaml (Node 24): format check, lint, build, tests; and runs a repo-owned governance job that calls npm run verify so every gate (including the maintainability ratchets in scripts/governance-check.mjs, the Terraform gates, and the content-CD guards) is guaranteed from this repository. Conventions and gates are documented under AGENTS.md, QUALITY_GATES.md, ARCHITECTURE_AND_CODE_QUALITY.md, and REVIEW_AND_PR_FRAMEWORK.md.
  • Terraform isolation (.github/workflows/terraform-isolation.yaml) — fails a PR that mixes terraform/** with application code, so a Terraform merge never races a content release for the HCP workspace.
  • Dev content (.github/workflows/deploy.yml) — workflow_dispatch on dev, and push-to-dev when vars.TERRAFORM_CONTENT_CD_ENABLED is true (paths-ignore: terraform/**). GitHub uploads releases/<sha>-<run>-<attempt>/ only. Terraform updates .release/current, both origin paths, and the invalidation action. Verify and rollback share scripts/verify-cloudfront-release.sh. Every run prints a live-state summary.
  • Staging content (.github/workflows/deploy-staging.yml) — on push to staging, unchanged.
  • Infrastructure — administrator-run HCP Terraform workspace shoc-frontend-new-dev (terraform/README.md). Staging hosting stays on the existing CloudFormation stack until SH-287.

Do not run scripts/deploy-web.sh against dev. That script remains the staging content publisher only.

Operations

  • Verify: open https://dev.seahaven.com after a green Deploy dev content run in the Actions tab; confirm a deep link (e.g. a work-orders route) loads directly and API calls succeed.
  • Logs: deploy logs live in GitHub Actions (CI + Deploy workflows). There are no CloudWatch application logs — the stack is static hosting; runtime errors surface in the browser and on the backend API's side.
  • Common failure modes:
    • Stale content after deploy — CloudFront is still InProgress or an edge still serves the previous index.html hash. Read the live-state summary before assuming the site is down.
    • OIDC AssumeRole errors — the trust policy is scoped to the dev ref on this repo; dispatching the workflow from another branch is rejected by design.
    • Broken API requests after a build — VITE_API_URL missing the /api suffix or carrying the wrong environment's host (it is baked in at build time).
    • CORS errors — the backend must allow the frontend origin; CloudFront does not proxy /api.
  • Push-to-dev is gated. Merging to dev publishes only when TERRAFORM_CONTENT_CD_ENABLED=true. Merging a terraform/** change queues an HCP Terraform run that a human confirms or discards before the next content release (see the operational rules in terraform/README.md).

Documentation