feat(contact): modèle + POST /contact + formulaire utilisateur (#80) #123
Loading…
Reference in a new issue
No description provided.
Delete branch "feat/contact-message-model-and-form"
Deleting a branch is permanent. Although the deleted branch may continue to exist for a short time before it actually gets removed, it CANNOT be undone in most cases. Continue?
PR 1/2 de l'issue #80 — le formulaire in-app de remontée de bug / suggestion pour les utilisateurs connectés. La consultation admin (
GET /admin/api/contact+ page/admin/messages) suivra en PR 2, stackée sur celle-ci.Contenu
Modèle & contrat
ContactMessage(Prisma) + relation inversecontactMessagessurUserProfile; migration20260908120000_contact_message(table neuve, aucun backfill).authorIdnullable +onDelete: SetNull— un signalement survit à la suppression de son compte (même choix queIngredient.createdById).packages/shared/src/schemas/contact.ts:CONTACT_MESSAGE_TYPES(bug|suggestion|other),submitContactMessageSchema(typeenum,message10–5000trim,pageUrl/appVersionoptionnels). Le user-agent n'est pas dans le schéma — le serveur le lit de l'en-tête."contact_message"ajouté à la liste TRUNCATE dereset-db.ts.API — module
contactPOST /contact(requireAuth, monté/contact) →204sans corps.204et non201-avec-corps : le formulaire n'a rien à faire de la ligne stockée, etApiClient._requestne traite que204comme sans corps (même forme quePOST /auth/logout).submitContactMessage(authorId, input, userAgent)insère la ligne puis émetanalytics.recordEvent("contact.submitted", { actorType: "user", actorId, context: { type } })(fire-and-forget) —admin-metricsagrège lesAnalyticsEventpartypegénériquement, l'event apparaît dans le dashboard sans code en plus.Front — page
/parametres/contactContactPage(+contact.tspourbuildContactMessageInput, +.scss) :<select>de type,<textarea>message, validation cliente viasubmitContactMessageSchema(messages du schéma affichés tels quels), états succès / erreur viaErrorMessageService(jamaiserr.message).SETTINGS_ITEMS+ clé i18nlayout.settings.nav.contact+ bloccontact.*+ icôneContactIcon(MessageSquare).apiClient.submitContactMessage(input)— ajoutepageUrl: window.location.pathnameetappVersion: __APP_VERSION__.RequireAuth/AppLayoutdansApp.tsx.Tests
apps/api/test/contact.test.ts(Mocha) : 401 sans session ; insertion avec les bons champs +authorId= appelant + user-agent pris de l'en-tête ; user-agent du corps ignoré ;pageUrl/appVersionnullsi omis ; message trop court / trop long /typeinconnu →400 VALIDATION_ERROR.apps/web/cypress/e2e/contact.feature+contact.ts: envoi d'un bug → confirmation, le corps de la requête portepageUrl/appVersion; message trop court rejeté côté client sans requête.apps/web/cypress/e2e/contact-page.cy.ts: gardeRequireAuth(→/login), rendu du formulaire, label d'erreur traduit (jamais le message serveur brut) sur échec.Docs :
batch-cooking-modele.md,backend-architecture.md,frontend-architecture.md.Vérifié localement
pnpm lint✅ ·pnpm build(tsc api + web, vite build) ✅ · Cypresscontact.feature+contact-page.cy.ts+ specs voisines (sidebar,layout,app-settings-page,user-preferences) : 24/24 ✅. Mocha non exécutable ici (pas de Postgres local) → CI.Closes #80 partiellement (PR 1/2).
🤖 Generated with Claude Code