fix(support): [HOTFIX] closing a ticket violated resolved-consistency constraint#377
fix(support): [HOTFIX] closing a ticket violated resolved-consistency constraint#377thierryvm wants to merge 2 commits into
Conversation
…traint [HOTFIX] Changing a support ticket to "Fermé" (closed) from the super_admin triage UI failed with "Mise à jour du statut impossible" — no ticket could ever be closed. Root cause (verified against prod schema): the DB CHECK constraint `support_tickets_resolved_consistency` treats BOTH 'resolved' AND 'closed' as terminal states that require resolved_at + resolved_by to be non-null (and null for 'open'/'in_progress'). But updateStatus only set resolved_* for 'resolved' (`isResolved`), clearing them on every other transition — so a move to 'closed' sent resolved_at=null, resolved_by=null and the UPDATE was rejected by the constraint (a generic, non-42501 error → the generic FR message). Fix: treat 'resolved' and 'closed' as terminal. Preserve the existing resolution stamp when closing an already-resolved ticket (don't clobber when/who resolved it); stamp now/self when entering a terminal state directly from open/in_progress. Added `tickets` to the callback deps to read the current row. - Reported by @Thierry on prod (real ticket 21f3df1e, resolved → could not close). - 2 regression tests added (close preserves stamp; close-from-open stamps now/self). - type-check + lint clean, 17/17 support hook tests pass. No DB/RLS change. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
|
The latest updates on your projects. Learn more about Vercel for GitHub.
|
Guide du réviseurAjuste les mises à jour de statut des tickets de support afin que « resolved » et « closed » soient tous deux traités comme des états terminaux nécessitant des métadonnées de résolution non nulles, en préservant les tampons de résolution existants lors de la fermeture de tickets déjà résolus, en les appliquant lors de la fermeture depuis des états non terminaux, et en ajoutant des tests de régression ainsi que des mises à jour des dépendances des hooks pour couvrir ce comportement. Diagramme de flux pour la gestion des états terminaux des tickets de support dans updateStatusflowchart TD
A[updateStatus called with id and next status] --> B[Compute isTerminal = next is resolved or closed]
A --> C[Find current ticket by id in tickets]
B --> D{isTerminal?}
D -- No --> E[Set resolved_at = null
resolved_by = null]
D -- Yes --> F{current has resolved_at and resolved_by?}
F -- Yes --> G[Preserve current.resolved_at
and current.resolved_by]
F -- No --> H[Set resolved_at = now
resolved_by = user.id]
E --> I[Send PATCH with status and resolution fields]
G --> I
H --> I
Modifications au niveau des fichiers
Conseils et commandesInteragir avec Sourcery
Personnaliser votre expérienceAccédez à votre tableau de bord pour :
Obtenir de l’aide
Original review guide in EnglishReviewer's GuideAdjusts support ticket status updates so that both 'resolved' and 'closed' are treated as terminal states requiring non-null resolution metadata, preserving existing resolution stamps when closing already-resolved tickets, stamping them when closing from non-terminal states, and adds regression tests plus hook dependency updates to cover this behavior. Flow diagram for support ticket terminal state handling in updateStatusflowchart TD
A[updateStatus called with id and next status] --> B[Compute isTerminal = next is resolved or closed]
A --> C[Find current ticket by id in tickets]
B --> D{isTerminal?}
D -- No --> E[Set resolved_at = null
resolved_by = null]
D -- Yes --> F{current has resolved_at and resolved_by?}
F -- Yes --> G[Preserve current.resolved_at
and current.resolved_by]
F -- No --> H[Set resolved_at = now
resolved_by = user.id]
E --> I[Send PATCH with status and resolution fields]
G --> I
H --> I
File-Level Changes
Tips and commandsInteracting with Sourcery
Customizing Your ExperienceAccess your dashboard to:
Getting Help
|
There was a problem hiding this comment.
Salut - j'ai laissé quelques retours de haut niveau :
- Utiliser
ticketsdans le tableau de dépendances du callbackupdateStatusfera changer l'identité de la fonction à chaque modification detickets. Si ce callback est largement propagé dans l'arborescence, envisage d'obtenircurrentà partir de la fonction de mise à jour (par exemple viasetTicketsou un sélecteur dédié) afin de garder les dépendances minimales et d’éviter des re-rendus inutiles.
Invite pour les agents IA
Veuillez traiter les commentaires de cette revue de code :
## Commentaires généraux
- Utiliser `tickets` dans le tableau de dépendances du callback `updateStatus` fera changer l'identité de la fonction à chaque modification de `tickets`. Si ce callback est largement propagé dans l'arborescence, envisage d'obtenir `current` à partir de la fonction de mise à jour (par exemple via `setTickets` ou un sélecteur dédié) afin de garder les dépendances minimales et d’éviter des re-rendus inutiles.Sourcery est gratuit pour l’open source - si nos revues vous plaisent, pensez à les partager ✨
Original comment in English
Hey - I've left some high level feedback:
- Using
ticketsin theupdateStatuscallback dependency array will cause the function identity to change on every tickets change; if this callback is passed down widely, consider derivingcurrentfrom the updater (e.g. viasetTicketsor a dedicated selector) to keep dependencies minimal and avoid unnecessary re-renders.
Prompt for AI Agents
Please address the comments from this code review:
## Overall Comments
- Using `tickets` in the `updateStatus` callback dependency array will cause the function identity to change on every tickets change; if this callback is passed down widely, consider deriving `current` from the updater (e.g. via `setTickets` or a dedicated selector) to keep dependencies minimal and avoid unnecessary re-renders.Help me be more useful! Please click 👍 or 👎 on each comment and I'll use the feedback to improve your reviews.
… (Sourcery #377) Sourcery flagged that adding `tickets` to updateStatus's dep array rebuilds the callback on every ticket change, re-rendering every TicketCard it is passed to. Mirror `tickets` into a `ticketsRef` (synced by an effect, same pattern as the existing isMountedRef/updatingIdRef) and read the current row from the ref, so deps stay [user]. Behaviour unchanged: closing an already-resolved ticket still preserves its resolution stamp. 17/17 tests pass, type-check + lint clean. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
[HOTFIX] Impossible de fermer un ticket support
Bug prod signalé par @Thierry : passer un ticket à « Fermé » depuis le triage super_admin (
/app/admin) échoue avec « Mise à jour du statut impossible ». Aucun ticket ne pouvait être fermé.Cause racine (vérifiée contre le schéma prod)
La contrainte
support_tickets_resolved_consistencytraiteresolvedETclosedcomme états terminaux exigeantresolved_at+resolved_bynon-null (et null pouropen/in_progress). OrupdateStatusne posaitresolved_*que pourresolved(isResolved) → un passage àclosedenvoyaitresolved_at=null→ violation de contrainte → UPDATE rejeté (erreur générique, pas 42501).Fix
updateStatustraiteresolvedetclosedcomme terminaux : préserve le stamp de résolution existant en fermant un ticket déjà résolu (ne pas écraser qui/quand), estampille now/self en entrant dans un état terminal depuisopen/in_progress.ticketsajouté aux deps du callback pour lire la ligne courante.Validation
21f3df1e(resolved + resolved_* set → le fix le fermera).🤖 Generated with Claude Code
Résumé par Sourcery
Garantir que les tickets de support considèrent à la fois les statuts « résolu » et « fermé » comme des états terminaux, avec des métadonnées de résolution cohérentes lors de la mise à jour du statut.
Corrections de bugs :
resolved_atetresolved_bypour les états terminaux afin de satisfaire la contrainte de cohérence de la base de données.Tests :
Original summary in English
Summary by Sourcery
Ensure support tickets treat both resolved and closed as terminal states with consistent resolution metadata when updating status.
Bug Fixes:
Tests: