feat(ce): Sprint K — public badge + invites CE_MEMBER + tests #85

Merged
tarzzan merged 1 commit from feat/ce-sprint-k into main 2026-06-03 00:03:39 +00:00
Owner

Sprint K — Visibilité publique + invitations CE_MEMBER + tests

Finalisation du module CE management (Sprints G→J déjà mergés). Marketplace CE end-to-end : public badge sur la fiche carbet, invitations CE_MEMBER auto-affiliantes, tests vitest, CTA marketing.

Public badge sur fiche carbet

  • carbet-public.ts charge les OrganizationCarbetMembership (filtre org approved=true uniquement) + expose organizations: {id,name,slug}[]
  • /carbets/[slug] affiche « Géré par le CE » sous le header si au moins 1 org liée

Invites CE_MEMBER

  • Migration 20260603300000_org_invite_token (appliquée prod) : OrgInviteToken { tokenHash, organizationId, email?, createdByUserId, expiresAt, usedAt }. Cascade sur Organization.
  • src/lib/ce-invites.ts : createOrgInviteToken (TTL 14j), listOrgInviteTokens, getOrgInviteByToken (validité + expiry), markOrgInviteConsumed, revokeOrgInviteToken. Token = 24 bytes base64url, hash sha256.
  • /espace-ce/membres : liste membres (CE_MANAGER + CE_MEMBER actifs) + form de génération de lien (email optionnel = lock email côté signup) + liste des invitations avec statut actif/consommé/expiré + bouton révoquer
  • API /api/signup étendue : zod accepte inviteToken, branche dédiée qui crée User CE_MEMBER + organizationId du token + marquage usedAt. Vérif email match si email fourni dans le token
  • /inscription?invite=TOKEN : pré-affiche org name, lock email si fourni, masque les fieldsets type de compte (forcé CE_MEMBER)

CTA marketing

/pour-comites-entreprise : section CTA « Créer mon espace CE » sous le rendu content-pages, conditionnée par plugin ce-management.

Tests vitest

tests/lib/ce-access.test.ts (9 cas) :

  • ADMIN toujours OK
  • OWNER direct OK / autre owner refusé
  • CE_MANAGER via membership org match OK / autre org refusée / sans membership refusée / sans organizationId refusée
  • TOURIST refusé
  • CE_MANAGER en tant qu'owner direct OK

Mocks next-auth / @/auth / @/lib/authorization pour éviter next/server (incompatible vitest sans setup).

Total : 62/62 tests ✓ (53 précédents + 9 nouveaux). Lint + typecheck + build ✓.

Test plan

  • Vitest 62/62 ✓
  • CE_MANAGER valide org → /espace-ce/membres → génère invite avec email → lien copié
  • Anonyme ouvre lien /inscription?invite=TOKEN → form pré-rempli, email locké, fieldsets masqués
  • Signup → User CE_MEMBER créé + lié à org + OrgInviteToken.usedAt updated
  • Lien expiré (TTL 14j) ou déjà consommé → 400 « Lien invalide ou expiré »
  • Lien avec email locké → 400 si email signup différent
  • Carbet publié par un CE → fiche publique montre badge « Géré par le CE X »

🤖 Generated with Claude Code

## Sprint K — Visibilité publique + invitations CE_MEMBER + tests Finalisation du module CE management (Sprints G→J déjà mergés). Marketplace CE end-to-end : public badge sur la fiche carbet, invitations CE_MEMBER auto-affiliantes, tests vitest, CTA marketing. ### Public badge sur fiche carbet - `carbet-public.ts` charge les `OrganizationCarbetMembership` (filtre org `approved=true` uniquement) + expose `organizations: {id,name,slug}[]` - `/carbets/[slug]` affiche « Géré par le CE <name> » sous le header si au moins 1 org liée ### Invites CE_MEMBER - **Migration `20260603300000_org_invite_token`** (appliquée prod) : `OrgInviteToken { tokenHash, organizationId, email?, createdByUserId, expiresAt, usedAt }`. Cascade sur Organization. - `src/lib/ce-invites.ts` : `createOrgInviteToken` (TTL 14j), `listOrgInviteTokens`, `getOrgInviteByToken` (validité + expiry), `markOrgInviteConsumed`, `revokeOrgInviteToken`. Token = 24 bytes base64url, hash sha256. - `/espace-ce/membres` : liste membres (CE_MANAGER + CE_MEMBER actifs) + form de génération de lien (email optionnel = lock email côté signup) + liste des invitations avec statut actif/consommé/expiré + bouton révoquer - API `/api/signup` étendue : zod accepte `inviteToken`, branche dédiée qui crée User CE_MEMBER + organizationId du token + marquage `usedAt`. Vérif email match si email fourni dans le token - `/inscription?invite=TOKEN` : pré-affiche org name, lock email si fourni, masque les fieldsets type de compte (forcé CE_MEMBER) ### CTA marketing `/pour-comites-entreprise` : section CTA « Créer mon espace CE » sous le rendu content-pages, conditionnée par plugin `ce-management`. ### Tests vitest `tests/lib/ce-access.test.ts` (9 cas) : - ADMIN toujours OK - OWNER direct OK / autre owner refusé - CE_MANAGER via membership org match OK / autre org refusée / sans membership refusée / sans organizationId refusée - TOURIST refusé - CE_MANAGER en tant qu'owner direct OK Mocks `next-auth` / `@/auth` / `@/lib/authorization` pour éviter next/server (incompatible vitest sans setup). **Total : 62/62 tests ✓** (53 précédents + 9 nouveaux). Lint + typecheck + build ✓. ### Test plan - [x] Vitest 62/62 ✓ - [ ] CE_MANAGER valide org → /espace-ce/membres → génère invite avec email → lien copié - [ ] Anonyme ouvre lien `/inscription?invite=TOKEN` → form pré-rempli, email locké, fieldsets masqués - [ ] Signup → User CE_MEMBER créé + lié à org + `OrgInviteToken.usedAt` updated - [ ] Lien expiré (TTL 14j) ou déjà consommé → 400 « Lien invalide ou expiré » - [ ] Lien avec email locké → 400 si email signup différent - [ ] Carbet publié par un CE → fiche publique montre badge « Géré par le CE X » 🤖 Generated with [Claude Code](https://claude.com/claude-code)
tarzzan added 1 commit 2026-06-03 00:03:38 +00:00
feat(ce): Sprint K — public badge + invites CE_MEMBER + tests
All checks were successful
CI / test (pull_request) Successful in 2m45s
ea0e606735
Public badge sur fiche carbet :
- carbet-public.ts charge les OrganizationCarbetMembership (org
  approuvée uniquement) + expose `organizations: {id,name,slug}[]`.
- /carbets/[slug] affiche « Géré par le CE <name> » sous le header
  si au moins 1 org liée.

Invites CE_MEMBER :
- Migration 20260603300000_org_invite_token : OrgInviteToken
  (tokenHash, organizationId, email?, createdByUserId, expiresAt,
  usedAt). Cascade sur Organization. Index expiresAt + organizationId.
- src/lib/ce-invites.ts : createOrgInviteToken (TTL 14j),
  listOrgInviteTokens, getOrgInviteByToken (validité + expiry),
  markOrgInviteConsumed, revokeOrgInviteToken. Token = 24 bytes
  base64url, hash sha256.
- /espace-ce/membres : liste membres (CE_MANAGER + CE_MEMBER actifs)
  + form de génération de lien (email optionnel = lock email côté
  signup) + liste des invitations avec statut actif/consommé/expiré +
  bouton révoquer.
- /espace-ce/membres/actions.ts : createInviteAction +
  revokeInviteAction. Audit log scope=ce.invite.
- API /api/signup étendue : zod accepte inviteToken, branche dédiée
  qui crée User CE_MEMBER + organizationId du token + marquage
  usedAt. Vérif email match si email fourni dans le token.
- /inscription?invite=TOKEN : récupère l'invite, pré-affiche org name,
  lock email si fourni, masque les fieldsets type de compte (forcé
  CE_MEMBER).

CTA marketing :
- /pour-comites-entreprise : section CTA « Créer mon espace CE » sous
  le rendu content-pages, conditionnée par plugin ce-management.

Tests vitest (tests/lib/ce-access.test.ts) :
- canManageCarbet : admin always, owner direct, CE_MANAGER via org
  match, refus si autre org / pas d'org / TOURIST / pas de membership.
- 9 tests, mocks next-auth + @/auth + @/lib/authorization pour éviter
  next/server (incompatible vitest sans setup).
- Total tests projet : 62/62 ✓.

Dashboard /espace-ce : lien vers /espace-ce/membres en bas.

Migration prod appliquée.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
tarzzan merged commit 2b8d786cf9 into main 2026-06-03 00:03:39 +00:00
tarzzan deleted branch feat/ce-sprint-k 2026-06-03 00:03:39 +00:00
Sign in to join this conversation.
No reviewers
No labels
No milestone
No project
No assignees
1 participant
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set.

Reference: tarzzan/karbe#85
No description provided.