feat(unit-4): Dashboard — availability widget with TanStack Query
- Add @tanstack/react-query 5.101.0; wrap app with QueryClientProvider - Add AvailabilityStatus type and AvailabilityResponse to api/types.ts - Implement useAvailabilityStatus (staleTime 30s, stale-on-error preserved) - Add AvailabilityStatusBadge with green/amber/red states and stale indicator - Replace DashboardPage placeholder card with live availability widget - Add MSW availability handler; update test/utils with QueryClientProvider - 55/55 tests pass (FR-05) Co-Authored-By: Claude Haiku 4.5 <noreply@anthropic.com>
This commit is contained in:
+114
@@ -0,0 +1,114 @@
|
||||
# Business Logic Model — Unit 4: Dashboard
|
||||
|
||||
## Process Overview
|
||||
|
||||
Unit 4 introduces TanStack Query as a shared data-fetching layer and implements the Dashboard page with a live availability widget. The logic splits into three concerns: query infrastructure setup, the availability hook, and the dashboard UI composition.
|
||||
|
||||
---
|
||||
|
||||
## TanStack Query Setup
|
||||
|
||||
A `QueryClient` is created once at app startup and provided via `QueryClientProvider` wrapping the `RouterProvider` in `main.tsx`. This is a one-time global change that enables all future units to use `useQuery` / `useMutation`.
|
||||
|
||||
```ts
|
||||
// Recommended default options for the QueryClient
|
||||
const queryClient = new QueryClient({
|
||||
defaultOptions: {
|
||||
queries: {
|
||||
retry: 1,
|
||||
refetchOnWindowFocus: true,
|
||||
},
|
||||
},
|
||||
});
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## useAvailabilityStatus Hook
|
||||
|
||||
**Location**: `frontend/src/api/useAvailability.ts`
|
||||
|
||||
**Query key**: `['availability', 'status']`
|
||||
|
||||
**Fetch function**: Calls `GET /api/v1/Availability/status` via the shared `api` client.
|
||||
|
||||
**staleTime**: `30_000` ms — TanStack Query refetches automatically in the background once data is 30 seconds old.
|
||||
|
||||
**Return shape**:
|
||||
|
||||
```ts
|
||||
{
|
||||
data: AvailabilityResponse | undefined; // undefined until first successful fetch
|
||||
error: Error | null;
|
||||
isLoading: boolean; // true only during initial fetch with no data
|
||||
isFetching: boolean; // true during any in-flight refetch
|
||||
isError: boolean;
|
||||
refetch: () => void; // exposed for Retry button
|
||||
}
|
||||
```
|
||||
|
||||
**Stale-with-error state**: When a background refetch fails, TanStack Query preserves the previous `data` value while setting `isError = true`. This enables the dashboard to display the last known status alongside the error state (BR-U4-11).
|
||||
|
||||
---
|
||||
|
||||
## DashboardPage Composition
|
||||
|
||||
The DashboardPage composes existing layout primitives with the new availability widget. No routing changes are needed — `DashboardPage` is already registered as the `/dashboard` route component in `router.tsx`.
|
||||
|
||||
**Sections**:
|
||||
1. **Header** — welcome message with user name (already implemented; retained as-is)
|
||||
2. **Availability Widget** — replaces the generic placeholder card
|
||||
|
||||
---
|
||||
|
||||
## Availability Widget Logic Flow
|
||||
|
||||
```mermaid
|
||||
sequenceDiagram
|
||||
box rgba(99,179,237,0.4) Component Layer
|
||||
participant DP as DashboardPage
|
||||
participant Badge as AvailabilityStatusBadge
|
||||
end
|
||||
box rgba(154,230,180,0.4) Query Layer
|
||||
participant Q as useAvailabilityStatus
|
||||
end
|
||||
box rgba(233,213,255,0.4) External
|
||||
participant API as GET /availability/status
|
||||
end
|
||||
|
||||
DP->>Q: mount — useAvailabilityStatus()
|
||||
Q->>API: fetch on mount
|
||||
API-->>Q: 200 AvailabilityResponse
|
||||
Q-->>DP: data + isLoading=false
|
||||
DP->>Badge: render with status and message
|
||||
Note over Q: staleTime 30s elapses
|
||||
Q->>API: background refetch
|
||||
alt Refetch succeeds
|
||||
API-->>Q: 200 AvailabilityResponse
|
||||
Q-->>DP: updated data
|
||||
DP->>Badge: re-render with fresh data
|
||||
else Refetch fails
|
||||
API-->>Q: network error or 5xx
|
||||
Q-->>DP: isError=true data=stale
|
||||
DP->>Badge: render stale badge with stale indicator
|
||||
DP->>DP: show error banner with Retry button
|
||||
end
|
||||
```
|
||||
|
||||
Sequence showing initial fetch, badge render, background refetch on staleTime expiry, and error handling with stale data preservation.
|
||||
|
||||
---
|
||||
|
||||
## i18n Keys (additions)
|
||||
|
||||
New translation keys needed for the availability widget:
|
||||
|
||||
| Key | English | Dutch |
|
||||
|---|---|---|
|
||||
| `availability.title` | System Status | Systeemstatus |
|
||||
| `availability.available` | Available | Beschikbaar |
|
||||
| `availability.maintenance` | Maintenance | Onderhoud |
|
||||
| `availability.unavailable` | Unavailable | Niet beschikbaar |
|
||||
| `availability.errorTitle` | Could not load status | Status kon niet worden geladen |
|
||||
| `availability.staleLabel` | Last known status | Laatste bekende status |
|
||||
| `availability.retry` | Retry | Opnieuw proberen |
|
||||
Reference in New Issue
Block a user