The delete dialog states the server's full open work-order count, but the server returns at most 200 ids. "View open work orders" navigated with that capped list, so a site with 240 open work orders showed 200, and an empty list opened the unfiltered board. The exact-id link is now used only when the ids cover the whole count. Otherwise the link opens Work Orders filtered to the site and every open status across all weeks, the ticket's "board filtered to that site". The board drilldown now reads a `sites` param for this; `ids` still wins. |
||
|---|---|---|
| .cursor/rules | ||
| .github | ||
| .husky | ||
| config | ||
| docs | ||
| e2e | ||
| eslint-rules | ||
| public | ||
| scripts | ||
| src | ||
| terraform | ||
| .env | ||
| .env.development | ||
| .env.example | ||
| .env.production | ||
| .gitignore | ||
| .prettierignore | ||
| .prettierrc | ||
| AGENTS.md | ||
| ARCHITECTURE_AND_CODE_QUALITY.md | ||
| commitlint.config.js | ||
| eslint.config.js | ||
| index.html | ||
| package-lock.json | ||
| package.json | ||
| playwright.config.ts | ||
| playwright.visual.config.ts | ||
| QUALITY_GATES.md | ||
| README.md | ||
| REVIEW_AND_PR_FRAMEWORK.md | ||
| tsconfig.json | ||
| tsconfig.node.json | ||
| vite.config.ts | ||
| vitest.config.ts | ||
SHOC Frontend (shoc-frontend-new)
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, deployed from
main) and https://staging.seahaven.com (staging, deployed fromvX.Y.Z-stagingtags) - 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 at the bucket root;
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 -->|OAC bucket root| 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 s3 sync dist/| S3
TF[HCP Terraform] -->|bucket CloudFront IAM SSM| CF
Dev and staging hosting live in terraform/live/dev and
terraform/live/staging. GitHub .github/workflows/deploy-web.yaml syncs
content.
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 Environment dev plus deploy-web.yaml |
| 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. Deploy looks up /shoc-frontend-new/<env>/deploy/{bucket,distribution-id}
after assuming DEPLOY_ROLE_ARN. AWS access is OIDC only. 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
mainwith a kebab-case description and a prefix matching the work:feature/,fix/,hotfix/,chore/,docs/, orrefactor/(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
main. The PR body uses the three-section layout the template pre-fills: Summary, Changes and value, Ticket.mainneeds theci-completecheck and an approving review from a code owner (@Sea-Haven-Industries/internal-dev); new pushes dismiss stale approvals. PRs merge through the merge queue, so a branch does not need to be updated withmainbefore it merges. Merged branches are deleted automatically. - A change set cannot mix
terraform/with deployable application files (G13), including each queued PR on the merge-group check. Workflow, docs, and gate-script changes may travel with either side.deploy-web.yamlstill ignoresterraform/**so a Terraform-only merge does not sync the bucket. - Promotion flow: merge to
maindeploysdev.seahaven.com. A person cutsvX.Y.Z-stagingforstaging.seahaven.com. CorevX.Y.Zwaits until a prod distribution exists.
Deployment
No stored AWS keys — OIDC only. Infrastructure and content deploy separately:
- CI (
.github/workflows/ci.yaml) — on push and PRs tomain, runs format, lint, build, sharded unit tests, visual regression, Playwright smoke, andnpm run governanceas parallel jobs, thenci-complete. Conventions and gates are documented underAGENTS.md,QUALITY_GATES.md,ARCHITECTURE_AND_CODE_QUALITY.md, andREVIEW_AND_PR_FRAMEWORK.md. - SPA content (
.github/workflows/deploy-web.yaml) — push tomaindeploysdev; a publishedvX.Y.Z-stagingrelease deploysstaging. Syncsdist/to the bucket root and invalidates/*. - Infrastructure — HCP workspaces
shoc-frontend-new-devandshoc-frontend-new-staging(terraform/README.md).
Operations
- Verify: open https://dev.seahaven.com after a green Deploy Web 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
InProgressor an edge still serves the previousindex.htmlhash. Read the live-state summary before assuming the site is down. - OIDC
AssumeRoleerrors — the trust policy is scoped to Environmentdevorstagingplusdeploy-web.yaml. A job withoutenvironment:cannot assume the role. - Broken API requests after a build —
VITE_API_URLmissing the/apisuffix 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. - Non-empty origin path —
deploy-web.yamlrefuses to sync until Terraform has moved every origin to the bucket root.
- Stale content after deploy — CloudFront is still
Documentation
- Terraform runbook:
terraform/README.md - Rebuild strategy and conventions:
docs/ARCHITECTURE_PLAN.md; design system and UI docs underdocs/