diff --git a/.gitattributes b/.gitattributes new file mode 100644 index 0000000..807095d --- /dev/null +++ b/.gitattributes @@ -0,0 +1,19 @@ +# Enforce LF line endings on every platform. +# Without this, Windows + git autocrlf (default) checks files out with +# CRLF, which conflicts with Prettier's `endOfLine: "lf"` and breaks +# format:check on Windows working trees. +* text=auto eol=lf + +# Generated route tree (gitignored anyway, but if it ever sneaks in) +src/routeTree.gen.ts text eol=lf + +# Lock files: keep LF +pnpm-lock.yaml text eol=lf + +# Binary blobs +*.png binary +*.jpg binary +*.jpeg binary +*.gif binary +*.ico binary +*.svg text eol=lf diff --git a/.planning/ROADMAP.md b/.planning/ROADMAP.md index 047a7cb..b62b247 100644 --- a/.planning/ROADMAP.md +++ b/.planning/ROADMAP.md @@ -47,17 +47,17 @@ These rules govern every task. Any deviation must be discussed and documented as [**See `phase-1-foundation/README.md`**](./phase-1-foundation/README.md) -| # | Task | Status | Landed in | -| ---- | ------------------------------------------------------------------------------------------------------------ | ------ | --------- | -| 1.1 | Project scaffold (Vite + React + TS) | 🟩 | (manual) | -| 1.2 | [Stack rounding-out (Tailwind + shadcn/ui + deps + Prettier)](./phase-1-foundation/02-stack-rounding-out.md) | 🟩 | `9918418` | -| 1.3 | [Vite dev proxy + path aliases + tsconfig hardening](./phase-1-foundation/03-vite-dev-proxy.md) | 🟩 | `39b60c9` | -| 1.4 | [Runtime config endpoint](./phase-1-foundation/04-runtime-config.md) | 🟩 | `8e2151a` | -| 1.5 | [Directus auth client (cookie mode + refresh)](./phase-1-foundation/05-directus-auth-client.md) | 🟩 | `38fe2e3` | -| 1.6 | [Login page](./phase-1-foundation/06-login-page.md) | 🟩 | `7215cb5` | -| 1.7 | [Routing skeleton (TanStack Router + role-aware guards)](./phase-1-foundation/07-routing-skeleton.md) | 🟩 | `f4a5e5b` | -| 1.8 | [Logout flow](./phase-1-foundation/08-logout-flow.md) | 🟩 | `1ee339c` | -| 1.9 | [Gitea CI + Dockerfile + nginx static serve](./phase-1-foundation/09-gitea-ci-and-dockerfile.md) | 🟩 | `9bd3b84` | +| # | Task | Status | Landed in | +| ---- | ------------------------------------------------------------------------------------------------------------ | ------ | -------------------- | +| 1.1 | Project scaffold (Vite + React + TS) | 🟩 | (manual) | +| 1.2 | [Stack rounding-out (Tailwind + shadcn/ui + deps + Prettier)](./phase-1-foundation/02-stack-rounding-out.md) | 🟩 | `9918418` | +| 1.3 | [Vite dev proxy + path aliases + tsconfig hardening](./phase-1-foundation/03-vite-dev-proxy.md) | 🟩 | `39b60c9` | +| 1.4 | [Runtime config endpoint](./phase-1-foundation/04-runtime-config.md) | 🟩 | `8e2151a` | +| 1.5 | [Directus auth client (cookie mode + refresh)](./phase-1-foundation/05-directus-auth-client.md) | 🟩 | `38fe2e3` | +| 1.6 | [Login page](./phase-1-foundation/06-login-page.md) | 🟩 | `7215cb5` | +| 1.7 | [Routing skeleton (TanStack Router + role-aware guards)](./phase-1-foundation/07-routing-skeleton.md) | 🟩 | `f4a5e5b` | +| 1.8 | [Logout flow](./phase-1-foundation/08-logout-flow.md) | 🟩 | `1ee339c` | +| 1.9 | [Gitea CI + Dockerfile + nginx static serve](./phase-1-foundation/09-gitea-ci-and-dockerfile.md) | 🟩 | `9bd3b84` | | 1.10 | [Compose service block in trm/deploy](./phase-1-foundation/10-deploy-compose-block.md) | 🟩 | trm/deploy `68ab08f` | ### Phase 2 — Live monitoring map diff --git a/.planning/phase-1-foundation/09-gitea-ci-and-dockerfile.md b/.planning/phase-1-foundation/09-gitea-ci-and-dockerfile.md index 1e89f68..7df864e 100644 --- a/.planning/phase-1-foundation/09-gitea-ci-and-dockerfile.md +++ b/.planning/phase-1-foundation/09-gitea-ci-and-dockerfile.md @@ -196,7 +196,7 @@ Four files landed, matching the conventions established by `trm/processor`: - `build` — `pnpm install --frozen-lockfile --offline` then `pnpm build` produces `dist/`. - `runtime` (nginx:1.27-alpine) — copies `nginx.conf` to `/etc/nginx/conf.d/default.conf` and `dist/` to `/usr/share/nginx/html`. `EXPOSE 80`. `HEALTHCHECK` via `wget -qO- http://localhost/`. nginx default CMD. - **`nginx.conf`** — single server block on `:80`. Gzip for text assets. Three location rules: `/assets/` long-cache (`max-age=31536000, immutable` for hashed filenames), `/config.json` no-cache (overridable via volume mount in stage/prod), `/index.html` no-cache. SPA fallback `try_files $uri $uri/ /index.html;` for client-side routes. -- **`.dockerignore`** — node_modules, dist, .env*, *.log, .git, .gitea, .planning, *.md (except README), .claude, .vscode. Keeps the build context small. +- **`.dockerignore`** — node_modules, dist, .env*, *.log, .git, .gitea, .planning, \*.md (except README), .claude, .vscode. Keeps the build context small. - **`.gitea/workflows/build.yml`** — matches `trm/processor`'s shape with one additional gate (`pnpm format:check` between lint and test). Path filter covers source, config, and Docker-related files; `.planning/**` and most markdown are excluded so docs-only commits skip CI. Steps: checkout → setup Node 22 → enable pnpm@latest-9 → install --frozen-lockfile → typecheck → lint → format:check → test → setup buildx → registry login → build & push `git.dev.microservices.al/trm/spa:main` → trigger Portainer webhook. **Required secrets in Gitea repo settings** (same names as the other repos so they can be reused): diff --git a/package.json b/package.json index 592425b..200ef3a 100644 --- a/package.json +++ b/package.json @@ -5,12 +5,13 @@ "type": "module", "scripts": { "dev": "vite", - "build": "tsc -b && vite build", + "build": "pnpm route-tree && tsc -b && vite build", "preview": "vite preview", "lint": "eslint .", - "typecheck": "tsc -b --noEmit", + "typecheck": "pnpm route-tree && tsc -b --noEmit", "format": "prettier --write .", "format:check": "prettier --check .", + "route-tree": "tsr generate", "test": "echo \"no tests yet\" && exit 0" }, "dependencies": { @@ -33,6 +34,7 @@ "@eslint/js": "^10.0.1", "@tailwindcss/vite": "^4.2.4", "@tanstack/react-query-devtools": "^5.100.8", + "@tanstack/router-cli": "^1.166.40", "@tanstack/router-devtools": "^1.166.13", "@tanstack/router-plugin": "^1.167.32", "@types/node": "^24.12.2", diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 595a72c..91cc348 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -60,6 +60,9 @@ importers: '@tanstack/react-query-devtools': specifier: ^5.100.8 version: 5.100.8(@tanstack/react-query@5.100.8(react@19.2.5))(react@19.2.5) + '@tanstack/router-cli': + specifier: ^1.166.40 + version: 1.166.40 '@tanstack/router-devtools': specifier: ^1.166.13 version: 1.166.13(@tanstack/react-router@1.169.1(react-dom@19.2.5(react@19.2.5))(react@19.2.5))(@tanstack/router-core@1.169.1)(csstype@3.2.3)(react-dom@19.2.5(react@19.2.5))(react@19.2.5) @@ -1252,6 +1255,11 @@ packages: react: ^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0 react-dom: ^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0 + '@tanstack/router-cli@1.166.40': + resolution: {integrity: sha512-qvb1+9UI8S0UzYF/apzjZj6WC4VfhxFPvytcmKcbB1fooARSm0UEw67RVN21beWWiFIHx5tcjyffAx2Atr/cfA==} + engines: {node: '>=20.19'} + hasBin: true + '@tanstack/router-core@1.169.1': resolution: {integrity: sha512-x+2gIGKTTE1qAn7tLieGfrB5ciOviDmmi2ox9fAWUubRV+yTU5ruGFXocoCIWF+lB+SOtnHjo2E9BLSWyYoEmA==} engines: {node: '>=20.19'} @@ -1425,6 +1433,14 @@ packages: ajv@6.15.0: resolution: {integrity: sha512-fgFx7Hfoq60ytK2c7DhnF8jIvzYgOMxfugjLOSMHjLIPgenqa7S7oaagATUq99mV6IYvN2tRmC0wnTYX6iPbMw==} + ansi-regex@5.0.1: + resolution: {integrity: sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==} + engines: {node: '>=8'} + + ansi-styles@4.3.0: + resolution: {integrity: sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==} + engines: {node: '>=8'} + ansis@4.2.0: resolution: {integrity: sha512-HqZ5rWlFjGiV0tDm3UxxgNRqsOTniqoKZu0pIAfh7TZQMGuZK+hH0drySty0si0QXj1ieop4+SkSfPZBPPkHig==} engines: {node: '>=14'} @@ -1476,10 +1492,21 @@ packages: class-variance-authority@0.7.1: resolution: {integrity: sha512-Ka+9Trutv7G8M6WT6SeiRWz792K5qEqIGEGzXKhAE6xOWAY6pPH8U+9IY3oCMv6kqTmLsv7Xh/2w2RigkePMsg==} + cliui@8.0.1: + resolution: {integrity: sha512-BSeNnyus75C4//NQ9gQt1/csTXyo/8Sb+afLAkzAptFuMsod9HFokGNudZpi/oQV73hnVK+sR+5PVRMd+Dr7YQ==} + engines: {node: '>=12'} + clsx@2.1.1: resolution: {integrity: sha512-eYm0QWBtUrBWZWG0d386OGAw16Z995PiOVo2B7bjWSbHedGl5e0ZWaq65kOGgUSNesEIDkB9ISbTg/JK9dhCZA==} engines: {node: '>=6'} + color-convert@2.0.1: + resolution: {integrity: sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==} + engines: {node: '>=7.0.0'} + + color-name@1.1.4: + resolution: {integrity: sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==} + convert-source-map@2.0.0: resolution: {integrity: sha512-Kvp459HrV2FEJ1CAsi1Ku+MY3kasH19TFykTz2xWmMeq6bk2NU3XXvfJ+Q61m0xktWwt+1HSYf3JZsTms3aRJg==} @@ -1519,6 +1546,9 @@ packages: electron-to-chromium@1.5.349: resolution: {integrity: sha512-QsWVGyRuY07Aqb234QytTfwd5d9AJlfNIQ5wIOl1L+PZDzI9d9+Fn0FRale/QYlFxt/bUnB0/nLd1jFPGxGK1A==} + emoji-regex@8.0.0: + resolution: {integrity: sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==} + enhanced-resolve@5.21.0: resolution: {integrity: sha512-otxSQPw4lkOZWkHpB3zaEQs6gWYEsmX4xQF68ElXC/TWvGxGMSGOvoNbaLXm6/cS/fSfHtsEdw90y20PCd+sCA==} engines: {node: '>=10.13.0'} @@ -1653,6 +1683,10 @@ packages: resolution: {integrity: sha512-3hN7NaskYvMDLQY55gnW3NQ+mesEAepTqlg+VEbj7zzqEMBVNhzcGYYeqFo/TlYz6eQiFcp1HcsCZO+nGgS8zg==} engines: {node: '>=6.9.0'} + get-caller-file@2.0.5: + resolution: {integrity: sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg==} + engines: {node: 6.* || 8.* || >= 10.*} + get-nonce@1.0.1: resolution: {integrity: sha512-FJhYRoDaiatfEkUK8HKlicmu/3SGFD51q3itKDGoSTysQJBnfOcxU5GxnhE1E6soB76MbT0MBtnKJuXyAx+96Q==} engines: {node: '>=6'} @@ -1703,6 +1737,10 @@ packages: resolution: {integrity: sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==} engines: {node: '>=0.10.0'} + is-fullwidth-code-point@3.0.0: + resolution: {integrity: sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==} + engines: {node: '>=8'} + is-glob@4.0.3: resolution: {integrity: sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==} engines: {node: '>=0.10.0'} @@ -1979,6 +2017,10 @@ packages: resolution: {integrity: sha512-hOS089on8RduqdbhvQ5Z37A0ESjsqz6qnRcffsMU3495FuTdqSm+7bhJ29JvIOsBDEEnan5DPu9t3To9VRlMzA==} engines: {node: '>=8.10.0'} + require-directory@2.1.1: + resolution: {integrity: sha512-fGxEI7+wsG9xrvdjsrlmL22OMTTiHRwAMroiEeMgq8gzoLC/PQr7RsRDSTLUg/bZAZtF+TVIkHc6/4RIKrui+Q==} + engines: {node: '>=0.10.0'} + rolldown@1.0.0-rc.17: resolution: {integrity: sha512-ZrT53oAKrtA4+YtBWPQbtPOxIbVDbxT0orcYERKd63VJTF13zPcgXTvD4843L8pcsI7M6MErt8QtON6lrB9tyA==} engines: {node: ^20.19.0 || >=22.12.0} @@ -2018,6 +2060,14 @@ packages: resolution: {integrity: sha512-UXWMKhLOwVKb728IUtQPXxfYU+usdybtUrK/8uGE8CQMvrhOpwvzDBwj0QhSL7MQc7vIsISBG8VQ8+IDQxpfQA==} engines: {node: '>=0.10.0'} + string-width@4.2.3: + resolution: {integrity: sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==} + engines: {node: '>=8'} + + strip-ansi@6.0.1: + resolution: {integrity: sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==} + engines: {node: '>=8'} + synckit@0.11.12: resolution: {integrity: sha512-Bh7QjT8/SuKUIfObSXNHNSK6WHo6J1tHCqJsuaFDP7gP0fkzSfTxI8y85JrppZ0h8l0maIgc2tfuZQ6/t3GtnQ==} engines: {node: ^14.18.0 || >=16.0.0} @@ -2161,9 +2211,25 @@ packages: resolution: {integrity: sha512-BN22B5eaMMI9UMtjrGd5g5eCYPpCPDUy0FJXbYsaT5zYxjFOckS53SQDE3pWkVoWpHXVb3BrYcEN4Twa55B5cA==} engines: {node: '>=0.10.0'} + wrap-ansi@7.0.0: + resolution: {integrity: sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==} + engines: {node: '>=10'} + + y18n@5.0.8: + resolution: {integrity: sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA==} + engines: {node: '>=10'} + yallist@3.1.1: resolution: {integrity: sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g==} + yargs-parser@21.1.1: + resolution: {integrity: sha512-tVpsJW7DdjecAiFpbIB1e3qxIQsE6NoPc5/eTdrbbIC4h0LVsWhnoa3g+m2HclBIujHzsxZ4VJVA+GUuc2/LBw==} + engines: {node: '>=12'} + + yargs@17.7.2: + resolution: {integrity: sha512-7dSzzRQ++CKnNI/krKnYRV7JKKPUXMEh61soaHKg9mrWEhzFWhFnxPxGl+69cD1Ou63C13NUPCnmIcrvqCuM6w==} + engines: {node: '>=12'} + yocto-queue@0.1.0: resolution: {integrity: sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q==} engines: {node: '>=10'} @@ -3346,6 +3412,14 @@ snapshots: react-dom: 19.2.5(react@19.2.5) use-sync-external-store: 1.6.0(react@19.2.5) + '@tanstack/router-cli@1.166.40': + dependencies: + '@tanstack/router-generator': 1.166.39 + chokidar: 3.6.0 + yargs: 17.7.2 + transitivePeerDependencies: + - supports-color + '@tanstack/router-core@1.169.1': dependencies: '@tanstack/history': 1.161.6 @@ -3558,6 +3632,12 @@ snapshots: json-schema-traverse: 0.4.1 uri-js: 4.4.1 + ansi-regex@5.0.1: {} + + ansi-styles@4.3.0: + dependencies: + color-convert: 2.0.1 + ansis@4.2.0: {} anymatch@3.1.3: @@ -3618,8 +3698,20 @@ snapshots: dependencies: clsx: 2.1.1 + cliui@8.0.1: + dependencies: + string-width: 4.2.3 + strip-ansi: 6.0.1 + wrap-ansi: 7.0.0 + clsx@2.1.1: {} + color-convert@2.0.1: + dependencies: + color-name: 1.1.4 + + color-name@1.1.4: {} + convert-source-map@2.0.0: {} cookie-es@3.1.1: {} @@ -3646,6 +3738,8 @@ snapshots: electron-to-chromium@1.5.349: {} + emoji-regex@8.0.0: {} + enhanced-resolve@5.21.0: dependencies: graceful-fs: 4.2.11 @@ -3786,6 +3880,8 @@ snapshots: gensync@1.0.0-beta.2: {} + get-caller-file@2.0.5: {} + get-nonce@1.0.1: {} glob-parent@5.1.2: @@ -3822,6 +3918,8 @@ snapshots: is-extglob@2.1.1: {} + is-fullwidth-code-point@3.0.0: {} + is-glob@4.0.3: dependencies: is-extglob: 2.1.1 @@ -4084,6 +4182,8 @@ snapshots: dependencies: picomatch: 2.3.2 + require-directory@2.1.1: {} + rolldown@1.0.0-rc.17: dependencies: '@oxc-project/types': 0.127.0 @@ -4125,6 +4225,16 @@ snapshots: source-map-js@1.2.1: {} + string-width@4.2.3: + dependencies: + emoji-regex: 8.0.0 + is-fullwidth-code-point: 3.0.0 + strip-ansi: 6.0.1 + + strip-ansi@6.0.1: + dependencies: + ansi-regex: 5.0.1 + synckit@0.11.12: dependencies: '@pkgr/core': 0.2.9 @@ -4224,8 +4334,28 @@ snapshots: word-wrap@1.2.5: {} + wrap-ansi@7.0.0: + dependencies: + ansi-styles: 4.3.0 + string-width: 4.2.3 + strip-ansi: 6.0.1 + + y18n@5.0.8: {} + yallist@3.1.1: {} + yargs-parser@21.1.1: {} + + yargs@17.7.2: + dependencies: + cliui: 8.0.1 + escalade: 3.2.0 + get-caller-file: 2.0.5 + require-directory: 2.1.1 + string-width: 4.2.3 + y18n: 5.0.8 + yargs-parser: 21.1.1 + yocto-queue@0.1.0: {} zod-validation-error@4.0.2(zod@4.4.2):