Split into Budget.Core / Budget.Infrastructure / Budget.Api projects
Budget.Core: entities, DTOs, enums, FrequencyCalculator (no EF/ASP.NET deps) Budget.Infrastructure: AppDbContext, migrations, BudgetAuthorizationService Budget.Api: controllers, middleware, Program.cs — references both projects EF and Npgsql packages moved to Infrastructure; Api retains only JwtBearer, HealthChecks, and EF.Design (needed for dotnet ef CLI). Dockerfile updated to copy all three project directories before publishing. Migration namespaces updated from Budget.Api.Data.* to Budget.Infrastructure.Data.* and model type strings updated to Budget.Core.Models.* in the snapshot. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -0,0 +1,123 @@
|
||||
# Plan: Add TanStack React Query
|
||||
|
||||
## Goal
|
||||
|
||||
Replace the ad-hoc `useEffect` + `useState` fetch pattern with `@tanstack/react-query`.
|
||||
All data fetching moves into typed `useQuery` / `useMutation` hooks. A global `MutationCache`
|
||||
fires toasts on success and error so pages don't have to wire that up individually.
|
||||
|
||||
**Prerequisite:** Plan #11 (react-oidc-context) must be complete, as `TokenSync` replaces
|
||||
the `setTokenProvider` wiring that pages currently rely on.
|
||||
|
||||
## Current state
|
||||
|
||||
Each page manages its own loading/error state with `useState` and fetches in `useEffect`:
|
||||
```ts
|
||||
const [budgets, setBudgets] = useState<BudgetDto[]>([]);
|
||||
useEffect(() => {
|
||||
api.get<BudgetDto[]>('/api/budgets').then(setBudgets);
|
||||
}, []);
|
||||
```
|
||||
|
||||
Mutations are plain `async` calls with no error handling or cache invalidation.
|
||||
|
||||
## Target state
|
||||
|
||||
```
|
||||
src/api/
|
||||
client.ts — unchanged fetch wrapper
|
||||
queryClient.ts — QueryClient with MutationCache for global toasts
|
||||
budgets.ts — useQuery/useMutation hooks for budgets
|
||||
incomes.ts — hooks for incomes
|
||||
outgos.ts — hooks for outgos
|
||||
shares.ts — hooks for shares
|
||||
summary.ts — hooks for summary
|
||||
```
|
||||
|
||||
Pages become thin: call a hook, render data — no fetch logic inline.
|
||||
|
||||
## Steps
|
||||
|
||||
### Phase 1 — Install and configure QueryClient
|
||||
|
||||
1. `npm install @tanstack/react-query` in `src/Budget.Client/`.
|
||||
2. Create `src/Budget.Client/src/api/queryClient.ts`:
|
||||
- Create a `QueryClient` with a `MutationCache` that reads `meta.successMessage` and
|
||||
`meta.errorMessage` from each mutation and fires the existing `toast` utility on
|
||||
`onSuccess` / `onError`.
|
||||
- Export the `queryClient` singleton.
|
||||
3. Wrap the app in `<QueryClientProvider client={queryClient}>` in `main.tsx`
|
||||
(inside `<AuthProvider>`).
|
||||
|
||||
### Phase 2 — Create domain hook files
|
||||
|
||||
For each domain, create a hooks file following this pattern:
|
||||
|
||||
**`src/api/budgets.ts`**
|
||||
```ts
|
||||
export function useBudgets() {
|
||||
return useQuery({ queryKey: ['budgets'], queryFn: () => api.get<BudgetDto[]>('/api/budgets') });
|
||||
}
|
||||
|
||||
export function useCreateBudget() {
|
||||
const qc = useQueryClient();
|
||||
return useMutation({
|
||||
mutationFn: (req: CreateBudgetRequest) => api.post<BudgetDto>('/api/budgets', req),
|
||||
onSuccess: () => qc.invalidateQueries({ queryKey: ['budgets'] }),
|
||||
meta: { successMessage: 'Budget created', errorMessage: 'Failed to create budget' },
|
||||
});
|
||||
}
|
||||
|
||||
export function useUpdateBudget(id: string) { ... }
|
||||
export function useDeleteBudget() { ... }
|
||||
```
|
||||
|
||||
4. Create `src/api/budgets.ts` — hooks for List, Create, Update, Delete.
|
||||
5. Create `src/api/incomes.ts` — hooks for List, Create, Update, Delete, Reorder.
|
||||
Query key: `['budgets', budgetId, 'incomes']`.
|
||||
6. Create `src/api/outgos.ts` — hooks for List, Create, Update, Delete, Reorder.
|
||||
Query key: `['budgets', budgetId, 'outgos']`.
|
||||
Also hooks for `categories` and `payment-sources` lookups.
|
||||
7. Create `src/api/shares.ts` — hooks for List, Add, Update, Revoke.
|
||||
Query key: `['budgets', budgetId, 'shares']`.
|
||||
8. Create `src/api/summary.ts` — hooks for Get and UpdateTaxRate.
|
||||
Query key: `['budgets', budgetId, 'summary']`.
|
||||
|
||||
### Phase 3 — Update pages to use hooks
|
||||
|
||||
9. **`BudgetsPage.tsx`** — replace `useEffect` + `useState` with `useBudgets()` and `useCreateBudget()`.
|
||||
10. **`IncomePage.tsx`** — replace with `useIncomes()`, `useCreateIncome()`, `useUpdateIncome()`, `useDeleteIncome()`, `useReorderIncomes()`.
|
||||
11. **`OutgoPage.tsx`** — replace with outgo hooks + category/payment-source lookup hooks.
|
||||
12. **`SummaryPage.tsx`** — replace with `useSummary()` and `useUpdateTaxRate()`.
|
||||
13. **`SettingsPage.tsx`** — replace with `useShares()`, `useAddShare()`, `useUpdateShare()`, `useRevokeShare()`.
|
||||
|
||||
### Phase 4 — Clean up
|
||||
|
||||
14. Remove `setTokenProvider` from `main.tsx` if it is no longer wired there (it should now live only in `TokenSync.tsx` from plan #11).
|
||||
15. Remove unused `useEffect` / `useState` imports from pages.
|
||||
16. `npm run build` — zero TypeScript errors.
|
||||
|
||||
## Key decisions
|
||||
|
||||
- `api/client.ts` is not changed — hooks wrap it, they don't replace it.
|
||||
- Cache invalidation strategy: mutations invalidate the narrowest relevant query key
|
||||
(e.g., a deleted income invalidates `['budgets', id, 'incomes']`, not all budgets).
|
||||
- No optimistic updates in this plan — add separately if needed.
|
||||
- `meta.successMessage` and `meta.errorMessage` are typed by augmenting the
|
||||
`@tanstack/react-query` module's `Register` interface so TypeScript validates them.
|
||||
|
||||
## Files affected
|
||||
|
||||
- `package.json`
|
||||
- `src/Budget.Client/src/main.tsx`
|
||||
- New: `src/Budget.Client/src/api/queryClient.ts`
|
||||
- New: `src/Budget.Client/src/api/budgets.ts`
|
||||
- New: `src/Budget.Client/src/api/incomes.ts`
|
||||
- New: `src/Budget.Client/src/api/outgos.ts`
|
||||
- New: `src/Budget.Client/src/api/shares.ts`
|
||||
- New: `src/Budget.Client/src/api/summary.ts`
|
||||
- `src/Budget.Client/src/pages/BudgetsPage.tsx`
|
||||
- `src/Budget.Client/src/pages/IncomePage.tsx`
|
||||
- `src/Budget.Client/src/pages/OutgoPage.tsx`
|
||||
- `src/Budget.Client/src/pages/SummaryPage.tsx`
|
||||
- `src/Budget.Client/src/pages/SettingsPage.tsx`
|
||||
Reference in New Issue
Block a user