Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions src/content/docs/changelog/index.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ Notable changes to the kit, newest first.

## 2026-11-03

- **Dependencies: the React apps are on the latest stable versions (chore).** Both apps move to **Vite 8** (Rolldown) with `@vitejs/plugin-react` 6, **TypeScript 6.0.3** (held below 6.1 until `typescript-eslint` supports 7), **ESLint 10**, `eslint-plugin-react-hooks` 7, **lucide-react 1.x**, React 19.3 and the current minors of TanStack Query, React Router, Radix, Tailwind 4.3, SignalR and i18next; the admin app moves to **zod 4** with `@hookform/resolvers` 7. The container build and Frontend CI run on **Node 26** (LTS), the runtime image is pinned to `nginx:1.31.8-alpine`, and `engines.node` is now `^20.19.0 || >=22.13.0`. If you forked the apps: `tsconfig` drops the deprecated `baseUrl`; `eslint-plugin-jsx-a11y` (no ESLint 10 release yet) is replaced by the `eslint-plugin-jsx-a11y-x` fork under the same `jsx-a11y` rule names; and the React Compiler-aware hooks rules now fail the lint on `setState` called directly in an effect, refs read or written during render, and components created during render, so your own pages may need the same fixes. zod 4 deprecates `.email()`/`.url()` and `{ message }` in favour of `z.email()`/`z.url()` and `{ error }`. The admin dev proxy now forwards SignalR's WebSocket upgrade. See [Development](/docs/frontend/development/#linting), [Prerequisites](/docs/getting-started/prerequisites/) and PR [#1461](https://github.com/fullstackhero/dotnet-starter-kit/pull/1461); part of [#1453](https://github.com/fullstackhero/dotnet-starter-kit/issues/1453).
- **Dependencies: the backend stack is on the latest stable versions (chore).** ASP.NET Core, EF Core and `Microsoft.Extensions.*` move to the **10.0.14** patch line (with the `System.Security.Cryptography.Xml` / `.Pkcs` pins), .NET **Aspire 13.6.4** (hosting packages and AppHost SDK), Finbuckle.MultiTenant 10.1.6, FluentValidation 12.2.0, OpenTelemetry 1.20.1 (EF Core and StackExchange.Redis instrumentation on 1.20.1-beta.1), StackExchange.Redis 3.4.31, Scalar.AspNetCore 2.18.7, MailKit/MimeKit 4.18.2, AWSSDK.S3 4.0.104.8, QuestPDF 2026.9.1, Testcontainers 4.16.0 and Spectre.Console.Cli 0.57.2. `global.json` now asks for SDK `10.0.400` or later (still `latestFeature`). Containers: Postgres is **18** everywhere (Docker Compose already was; the Testcontainers harness and the CI migrator smoke move from 17), Valkey 9.1.3, RustFS 1.0.4, Mailpit v1.33.0, `amazon/aws-cli` 2.39.3, and RedisInsight is pinned to 3.8.0 instead of `latest`. Aspire 13.6 still defaults to `postgres:18.3`, so an Aspire data volume from 13.4 or later keeps working; only a volume created before 13.4 needs the [wipe](/docs/getting-started/troubleshooting/). Terraform's AWS provider constraint moves to `~> 6.71` (Terraform 1.15.4 is still the floor). If you forked the CLI, Spectre.Console.Cli 0.57 made `ExecuteAsync` overrides `public`. Part of [#1453](https://github.com/fullstackhero/dotnet-starter-kit/issues/1453); see PR [#1460](https://github.com/fullstackhero/dotnet-starter-kit/pull/1460).
- **Frontends: i18n follow-ups (fix).** The admin language switcher now saves with `If-Match`, so a profile edit made in between is no longer silently overwritten (it retries once on `412`). The dashboard container now honours `FSH_DEFAULT_LANGUAGE` like the admin one; before, a containerized dashboard always started in `en-US`. `deploy/docker` and Terraform pass the setting to both apps. The dashboard no longer matches the English text of the deactivated-tenant error, because the API always sends its `code` now. See [Internationalization](/docs/cross-cutting-concerns/internationalization/#per-deployment-default) and [#1459](https://github.com/fullstackhero/dotnet-starter-kit/pull/1459).
- **Identity: a role can no longer hold the same permission twice (fix, migration).** `identity.RoleClaims` had no uniqueness, so two API instances starting at once after a release that adds a permission, or the tenant seeder overlapping the startup sync, could both insert the same grant and leave a duplicate row behind with nothing in the logs. Authorization was not affected, but the table drifted. The new `AddRoleClaimsUniqueIndex` Identity migration first deletes existing duplicates (keeping the lowest `Id` per `(RoleId, ClaimType, ClaimValue)`) and then adds a unique index on those columns, which replaces the `RoleId`-only index. `RolePermissionSyncer` and `IdentityDbInitializer` treat a unique violation on that index as another writer having inserted the grant first and log it at Information instead of failing, and `PUT /api/v1/identity/{id}/permissions` collapses a permission repeated in the request. Run the DbMigrator `apply` as usual; claims with a `NULL` value are not affected. See [Authorization](/docs/security/authorization/#the-role-permission-syncer), [#1435](https://github.com/fullstackhero/dotnet-starter-kit/issues/1435) and [#1458](https://github.com/fullstackhero/dotnet-starter-kit/pull/1458).
Expand Down
2 changes: 1 addition & 1 deletion src/content/docs/deployment/ci-cd.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -77,7 +77,7 @@ need a `NUGET_API_KEY` repository secret to push packages to NuGet.org.

## frontend.yml - lint, build, E2E

Triggers on pushes and PRs to `main` (and manual dispatch). A `Detect changes` job gates the work on `clients/**`. Both apps run as a matrix (`admin`, `dashboard`) on **Node 22** with npm caching.
Triggers on pushes and PRs to `main` (and manual dispatch). A `Detect changes` job gates the work on `clients/**`. Both apps run as a matrix (`admin`, `dashboard`) on **Node 26** with npm caching.

| Job | Runs when | What it does |
|---|---|---|
Expand Down
10 changes: 5 additions & 5 deletions src/content/docs/frontend/admin.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -23,15 +23,15 @@ seo:
| | Version | Used for |
|---|---|---|
| React | 19.x | UI framework |
| Vite | 7.x | Dev server + build |
| TypeScript | 5.x | Type safety |
| Vite | 8.x | Dev server + build (Rolldown) |
| TypeScript | 6.0 | Type safety |
| TanStack Query | 5.x | Server-state cache + mutations |
| React Router | 7.x | Routing + protected routes |
| react-hook-form + zod | 7.x / 3.x | Forms + schema validation (admin only - the dashboard doesn't use them) |
| react-hook-form + zod | 7.x / 4.x | Forms + schema validation (admin only - the dashboard doesn't use them) |
| @microsoft/signalr | 10.x | Realtime connection to the kit's hub |
| Tailwind CSS | 4.x | Styling (shadcn-style, with CVA) |
| Radix UI | 1.x–2.x | Headless primitives (Dialog, DropdownMenu, etc.) |
| Lucide | 0.4xx | Icon set |
| Lucide | 1.x | Icon set |
| Playwright | 1.x | Route-mocked E2E tests (`npm run test:e2e`) |

All pinned in `clients/admin/package.json`.
Expand Down Expand Up @@ -226,7 +226,7 @@ npm install
npm run dev # http://localhost:5173 with HMR
```

The Vite dev server proxies `/api`, `/health`, `/openapi`, and `/scalar` to the backend - `http://localhost:5030` by default, overridable with a `VITE_API_BASE_URL` env var (this var only steers the **dev proxy target**; it is never baked into a build). Runtime configuration - API base URL, default tenant, inactivity timings - comes from `public/config.json`, fetched once at boot before React mounts.
The Vite dev server proxies `/api` (with WebSocket forwarding for SignalR), `/health`, `/openapi`, and `/scalar` to the backend - `http://localhost:5030` by default, overridable with a `VITE_API_BASE_URL` env var (this var only steers the **dev proxy target**; it is never baked into a build). Runtime configuration - API base URL, default tenant, inactivity timings - comes from `public/config.json`, fetched once at boot before React mounts.

The kit's Aspire AppHost wires all of this automatically when you run `dotnet run --project src/Host/FSH.Starter.AppHost` - the API, admin, and dashboard all come up together.

Expand Down
4 changes: 2 additions & 2 deletions src/content/docs/frontend/architecture.mdx
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
---
title: Frontend architecture
lastUpdated: 2026-10-14
lastUpdated: 2026-11-03
description: Shared patterns across the admin + dashboard apps - folder layout, API client, route guards, realtime context, error handling, ProblemDetails parsing.
sidebar:
label: Architecture
Expand All @@ -22,7 +22,7 @@ If you're forking only the dashboard (deleting admin), copy admin's audit-log vi

```
clients/{app}/
├── package.json React 19, Vite 7, TS 5.x, TanStack Query, Radix, etc.
├── package.json React 19, Vite 8, TS 6.0, TanStack Query, Radix, etc.
├── vite.config.ts Vite + React + Tailwind plugins + dev proxy config
├── tsconfig.json
├── index.html Single-page-app shell
Expand Down
4 changes: 2 additions & 2 deletions src/content/docs/frontend/dashboard.mdx
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
---
title: Tenant dashboard
lastUpdated: 2026-10-14
lastUpdated: 2026-11-03
description: The end-user-facing React + Vite app at clients/dashboard - catalog, chat, files, tickets, invoices, identity admin, plus real-time SignalR + SSE feeds.
sidebar:
label: Tenant dashboard
Expand All @@ -20,7 +20,7 @@ seo:

## Tech stack

Same shape as the [admin console](/docs/frontend/admin/) - React 19 + Vite 7 + TypeScript + TanStack Query v5 + React Router 7 + Tailwind 4 + Radix + Lucide. Differences worth knowing:
Same shape as the [admin console](/docs/frontend/admin/) - React 19 + Vite 8 + TypeScript + TanStack Query v5 + React Router 7 + Tailwind 4 + Radix + Lucide. Differences worth knowing:

| | Used for |
|---|---|
Expand Down
6 changes: 3 additions & 3 deletions src/content/docs/frontend/development.mdx
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
---
title: Local development
lastUpdated: 2026-10-14
lastUpdated: 2026-11-03
description: Running the React frontends with Vite HMR, building for production, deploying to static hosts or behind Nginx, runtime config via /config.json.
sidebar:
label: Local development
Expand Down Expand Up @@ -107,7 +107,7 @@ The dashboard's variant swaps `dashboardUrl` for a `demoMode` flag (login-page d

### The container pattern

`clients/{app}/Dockerfile` is a two-stage build - `node:22-alpine` runs `npm ci && npm run build`, then `nginx:alpine` serves `dist/`. The entrypoint renders the runtime config before starting Nginx:
`clients/{app}/Dockerfile` is a two-stage build - `node:26-alpine` runs `npm ci && npm run build`, then `nginx:1.31.8-alpine` (pinned) serves `dist/`. The entrypoint renders the runtime config before starting Nginx:

```sh
# clients/{app}/docker/docker-entrypoint.sh
Expand Down Expand Up @@ -241,7 +241,7 @@ Tests live under `clients/{app}/tests/`. There is no unit-test runner (Vitest) w

## Linting

ESLint is wired (flat config at `clients/{app}/eslint.config.js`, extending the React + TypeScript + a11y rule sets):
ESLint 10 is wired (flat config at `clients/{app}/eslint.config.js`, extending the React + TypeScript + a11y rule sets). The React hooks rules are the React Compiler-aware set from `eslint-plugin-react-hooks` 7, so a `setState` called synchronously in an effect body, a ref read or written during render, or a component defined inside another component is a lint error. The a11y rules come from `eslint-plugin-jsx-a11y-x`, a fork of `eslint-plugin-jsx-a11y` with the same rules, because the original has no ESLint 10 release yet; it is registered under the `jsx-a11y` name, so rule ids are unchanged:

```bash
npm run lint # report
Expand Down
4 changes: 2 additions & 2 deletions src/content/docs/frontend/index.mdx
Original file line number Diff line number Diff line change
@@ -1,13 +1,13 @@
---
title: Overview
lastUpdated: 2026-06-11
lastUpdated: 2026-11-03
description: Two React + Vite + TypeScript apps ship with the kit - an admin console for operators and a tenant dashboard for end users.
sidebar:
order: 1
pageType: guide
seo:
title: 'Frontend in .NET 10 - React + Vite admin + dashboard'
description: 'fullstackhero ships two production React 19 + Vite 7 + TypeScript frontends - clients/admin (operator console) and clients/dashboard (tenant-facing).'
description: 'fullstackhero ships two production React 19 + Vite 8 + TypeScript frontends - clients/admin (operator console) and clients/dashboard (tenant-facing).'
keywords: 'fullstackhero react frontend, vite admin console, react dashboard .net 10, jwt auth react vite, tenant theme react'
---

Expand Down
4 changes: 2 additions & 2 deletions src/content/docs/getting-started/prerequisites.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ sidebar:
pageType: guide
seo:
title: 'Prerequisites - .NET 10 SDK, Docker, Node setup'
description: 'Tools you need before running fullstackhero locally: .NET 10 SDK, Docker (for Postgres, Valkey, and RustFS containers), and optional Node 20+ for the React frontends.'
description: 'Tools you need before running fullstackhero locally: .NET 10 SDK, Docker (for Postgres, Valkey, and RustFS containers), and optional Node 20.19+ or 22.13+ for the React frontends.'
keywords: '.NET 10 SDK install, fullstackhero prerequisites, .NET development environment'
---

Expand All @@ -23,7 +23,7 @@ No separate Aspire install is needed: the AppHost project pulls the Aspire SDK (

## Optional

- **Node 20+** - only if you'll run the React + Vite frontends in `clients/admin` and `clients/dashboard` (both declare `"node": ">=20"`).
- **Node 20.19+ or 22.13+** - only if you'll run the React + Vite frontends in `clients/admin` and `clients/dashboard` (both declare `"node": "^20.19.0 || >=22.13.0"`, the floor Vite 8 and ESLint 10 need). CI and the container builds use Node 26, the current LTS.
- **PostgreSQL** - install locally only if you'd rather not run Postgres in a container. Otherwise Aspire provisions one for you (the kit's containers run Postgres 18).
- **Valkey** - same. The kit runs Valkey 9 in its containers (a Redis-compatible, BSD-licensed Redis fork); any Redis-protocol server also works.

Expand Down