feat(entities/user): PlanStatus + getPlanStatus + hook usePlan (Sprint 1 étape 1)
Fondations data plan utilisateur pour le dashboard conditionnel :
- entities/user/types.ts : interface PlanStatus (plan, permissions, simulations_used/remaining, plan_expires_at)
- entities/user/api.ts : getPlanStatus() via apiFetch<PlanStatus>('/plans/status')
- features/dashboard/hooks/usePlan.ts : useQuery + PLAN_QUERY_KEY + staleTime 5 min
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
parent
b246f89903
commit
107a37d197
3 changed files with 78 additions and 0 deletions
22
src/entities/user/api.ts
Normal file
22
src/entities/user/api.ts
Normal file
|
|
@ -0,0 +1,22 @@
|
||||||
|
/**
|
||||||
|
* Appels API du domaine `user`.
|
||||||
|
*
|
||||||
|
* Toutes les requêtes passent par `apiFetch` (Règle J) qui gère auth, retry,
|
||||||
|
* timeout et erreurs typées. Les consommateurs consomment ces fonctions via
|
||||||
|
* TanStack Query (cf. `features/dashboard/hooks/usePlan`).
|
||||||
|
*/
|
||||||
|
|
||||||
|
import { apiFetch } from '@/shared/lib/api-client'
|
||||||
|
import type { PlanStatus } from './types'
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Récupère le statut courant du plan de l'utilisateur connecté.
|
||||||
|
*
|
||||||
|
* Endpoint : `GET /plans/status`
|
||||||
|
* Auth : JWT Bearer requis (ajouté automatiquement par `apiFetch`).
|
||||||
|
*
|
||||||
|
* @throws ApiError — notamment `AUTH_REQUIRED` si le JWT est absent/expiré.
|
||||||
|
*/
|
||||||
|
export function getPlanStatus(): Promise<PlanStatus> {
|
||||||
|
return apiFetch<PlanStatus>('/plans/status')
|
||||||
|
}
|
||||||
32
src/entities/user/types.ts
Normal file
32
src/entities/user/types.ts
Normal file
|
|
@ -0,0 +1,32 @@
|
||||||
|
/**
|
||||||
|
* Types publics du domaine `user`.
|
||||||
|
*
|
||||||
|
* Porte d'entrée unique pour les consommateurs frontend : toute UI ou hook
|
||||||
|
* qui manipule un plan ou une permission importe depuis ce fichier (ou `./lib`
|
||||||
|
* pour les fonctions), jamais directement depuis `./access` (cf. ADR 005).
|
||||||
|
*/
|
||||||
|
|
||||||
|
import type { Feature, Plan } from './access'
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Réponse du backend pour `GET /plans/status`.
|
||||||
|
*
|
||||||
|
* Format confirmé par l'audit backend 2026-04-17 (cf. ARCHITECTURE.md §5).
|
||||||
|
*
|
||||||
|
* - `permissions` : dictionnaire booléen par feature. Les consommateurs
|
||||||
|
* doivent passer par `hasAccess(plan, feature)` plutôt que lire ce champ
|
||||||
|
* directement (Règle D / ADR 005). Il est exposé ici uniquement parce que
|
||||||
|
* le backend le renvoie et qu'il peut servir à du debug côté DevTools.
|
||||||
|
* - `simulations_remaining` : `null` si le plan est illimité (standard/premium),
|
||||||
|
* sinon nombre de simulations restantes sur le quota à vie (Free : 5).
|
||||||
|
* - `plan_expires_at` : ISO 8601 pour un plan payant actif, `null` pour Free.
|
||||||
|
*/
|
||||||
|
export interface PlanStatus {
|
||||||
|
plan: Plan
|
||||||
|
permissions: Record<Feature, boolean>
|
||||||
|
simulations_used: number
|
||||||
|
simulations_remaining: number | null
|
||||||
|
plan_expires_at: string | null
|
||||||
|
}
|
||||||
|
|
||||||
|
export type { Feature, Plan }
|
||||||
24
src/features/dashboard/hooks/usePlan.ts
Normal file
24
src/features/dashboard/hooks/usePlan.ts
Normal file
|
|
@ -0,0 +1,24 @@
|
||||||
|
/**
|
||||||
|
* Hook TanStack Query sur le statut du plan utilisateur.
|
||||||
|
*
|
||||||
|
* Source unique de vérité côté frontend pour `plan`, `permissions`, et
|
||||||
|
* compteurs de simulations. Consommé par `DashboardPage`, les gardes de
|
||||||
|
* permission dans les pages simulations/t2-live, et le router.
|
||||||
|
*
|
||||||
|
* `staleTime: 5 min` — le plan change peu (upgrade Stripe, expiration). Les
|
||||||
|
* flux d'upgrade appellent `queryClient.invalidateQueries(['plan'])` pour
|
||||||
|
* forcer un refetch immédiat après webhook.
|
||||||
|
*/
|
||||||
|
|
||||||
|
import { useQuery } from '@tanstack/react-query'
|
||||||
|
import { getPlanStatus } from '@/entities/user/api'
|
||||||
|
|
||||||
|
export const PLAN_QUERY_KEY = ['plan'] as const
|
||||||
|
|
||||||
|
export function usePlan() {
|
||||||
|
return useQuery({
|
||||||
|
queryKey: PLAN_QUERY_KEY,
|
||||||
|
queryFn: getPlanStatus,
|
||||||
|
staleTime: 5 * 60 * 1000,
|
||||||
|
})
|
||||||
|
}
|
||||||
Loading…
Add table
Add a link
Reference in a new issue