feat(entities/user): access.ts (copie backend) + lib.ts (hasAccess/canSimulate) + 37 tests

This commit is contained in:
Hermann_Kitio 2026-04-17 17:45:40 +03:00
parent 94731edafc
commit ef9a84433e
4 changed files with 421 additions and 0 deletions

73
src/entities/user/lib.ts Normal file
View file

@ -0,0 +1,73 @@
/**
* Alias frontend-idiomatiques pour les fonctions d'accès.
*
* Ce fichier est la PORTE D'ENTRÉE pour tout code frontend qui vérifie
* une permission ou un quota. Ne JAMAIS importer directement depuis access.ts
* dans les composants ou les hooks.
*
* Cf. ADR 005 pour la justification des alias et ADR 004 pour la règle
* de synchronisation de access.ts avec le backend.
*/
import {
canUserSimulate,
checkFeatureAccess,
getPlanPermissions,
type Feature,
type Plan,
} from './access'
/**
* Vérifie si un plan a accès à une feature booléenne donnée.
*
* Alias idiomatique React de `checkFeatureAccess` (backend).
* La feature 'basic_report' est toujours autorisée (tous plans).
*
* @example
* if (hasAccess(plan, 'exam_mode')) { ... }
* if (hasAccess(plan, 'oral_t2_live')) { ... }
*
* @param plan - Plan de l'utilisateur ('free' | 'standard' | 'premium')
* @param feature - Nom de la feature à vérifier
* @returns true si le plan donne accès à la feature
*/
export const hasAccess = checkFeatureAccess
/**
* Résultat d'un check de simulation.
*/
export interface SimulationCheck {
allowed: boolean
reason?: 'quota_reached' | 'invalid_plan' | string
}
/**
* Vérifie si un utilisateur peut lancer une nouvelle simulation.
*
* Alias idiomatique de `canUserSimulate` (backend) avec une signature
* plus ergonomique côté frontend : prend `plan` et `simulationsUsed`
* séparément au lieu d'un objet `user`.
*
* @example
* const { allowed, reason } = canSimulate(plan, simulationsUsed)
* if (!allowed) {
* if (reason === 'quota_reached') openUpgradeModal()
* }
*
* @param plan - Plan de l'utilisateur
* @param simulationsUsed - Nombre de simulations déjà consommées
* @returns Objet `{ allowed, reason? }`
*/
export function canSimulate(plan: string, simulationsUsed: number): SimulationCheck {
return canUserSimulate({ plan, simulations_used: simulationsUsed })
}
/**
* -export direct pas d'alias car le nom est déjà idiomatique.
*/
export { getPlanPermissions }
/**
* -export des types pour les consommateurs.
*/
export type { Feature, Plan }