Fix: signature du médecin validateur sur le document et échec visible du contrôle d'interactions - #66
Conversation
|
The latest updates on your projects. Learn more about Vercel for GitHub.
|
|
Warning Review limit reached
Next review available in: 39 minutes Enable usage-based reviews in Billing to review now. Otherwise, wait until the next included review is available. How can I continue?After more reviews become available, a review can be triggered using the To avoid repeated limits, reduce automatic review volume by pausing incremental auto-reviews earlier, using label-based review opt-in, excluding WIP or generated PR titles, or requesting reviews manually when the PR is ready. If your team needs uninterrupted high-volume reviews, an organization admin can enable usage-based reviews. How do review limits work?CodeRabbit enforces per-developer PR review limits for each organization. Most developers receive the normal plan review availability. For paid Pro and Pro+ PR reviews, CodeRabbit uses adaptive limits for sustained high-volume activity. When a developer's recent PR review activity reaches the 95th percentile or higher among CodeRabbit users, additional reviews become available more gradually as earlier reviews age out of the rolling window. Please refer docs for additional details. Review details⚙️ Run configurationConfiguration used: Repository UI Review profile: CHILL Plan: Pro Plus Run ID: 📒 Files selected for processing (6)
📝 WalkthroughWalkthroughLe diagnostic charge la signature du médecin validateur lorsque les conditions de validation sont réunies. Le contrôle des interactions médicamenteuses utilise des états explicites et affiche les résultats, les erreurs, les relances et les médicaments sans code RxNorm. ChangesDiagnostic et prescription
Estimated code review effort: 3 (Modéré) | ~20 minutes Suggested reviewers: Poem
🚥 Pre-merge checks | ✅ 5✅ Passed checks (5 passed)
✨ Finishing Touches📝 Generate docstrings
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
There was a problem hiding this comment.
Actionable comments posted: 4
🧹 Nitpick comments (1)
app/(app)/app/diagnostics/[id]/page.tsx (1)
25-25: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick winCentralisez la valeur de statut
validated.La ligne 25 code une valeur métier en dur. Réutilisez une constante partagée dans cette condition et dans les autres contrôles de statut afin d’éviter une divergence future.
As per coding guidelines — les valeurs métier ne doivent pas être codées en dur; définissez-les dans une constante ou une configuration partagée.
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@app/`(app)/app/diagnostics/[id]/page.tsx at line 25, Replace the hard-coded "validated" status in the diagnostic validation condition with the shared status constant, and update the other status checks in this flow to reuse the same constant. Keep the existing validation behavior unchanged.Source: Coding guidelines
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Inline comments:
In `@app/`(app)/app/diagnostics/[id]/page.tsx:
- Around line 24-27: Restrict the getDoctorSignatureByUserId call in the
diagnostic page to callers authorized by lib/rbac.ts: only owner or super_admin
roles may retrieve the signature. Apply the existing role guard before invoking
it, and preserve the null result for unvalidated diagnostics or unauthorized
callers.
In `@components/diagnostics/prescription-builder-step.tsx`:
- Around line 150-186: Prevent stale checkInteractions responses from updating
current state when treatment changes trigger overlapping requests. Track a
monotonically increasing check identifier or cancel the previous fetch, and
guard every response, error, and completion state update so only the latest
invocation of checkInteractions can update drugInteractions, uncoded count, or
interactionCheckState.
- Around line 447-475: Replace the raw Tailwind color classes in the interaction
status UI—specifically the amber classes on the AlertTriangle and heading plus
the emerald class in the clear-state paragraph—with the semantic alert and
success theme tokens defined in the global theme configuration. Preserve the
existing failed-state destructive styling and all interactionCheckState
behavior.
- Around line 180-183: Validate the parsed response in the drug-interactions
request before updating state: define or reuse a Zod schema for the response and
each DrugInteractionPair, including the allowed interaction severity values, and
parse the JSON result. Only call setDrugInteractions and
setInteractionCheckState with validated data; on schema failure, set the
interaction check state to "failed" instead of treating missing, null, or
malformed interactions as clear.
---
Nitpick comments:
In `@app/`(app)/app/diagnostics/[id]/page.tsx:
- Line 25: Replace the hard-coded "validated" status in the diagnostic
validation condition with the shared status constant, and update the other
status checks in this flow to reuse the same constant. Keep the existing
validation behavior unchanged.
🪄 Autofix (Beta)
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: Repository UI
Review profile: CHILL
Plan: Pro Plus
Run ID: ba6ccd7c-e3f8-4e79-9ee1-718e09b618ad
📒 Files selected for processing (5)
app/(app)/app/diagnostics/[id]/page.tsxcomponents/diagnostics/prescription-builder-step.tsxmessages/en.jsonmessages/fr.jsontypes/index.ts
| const result: { interactions: DrugInteractionPair[] } = await response.json(); | ||
| const foundInteractions = result.interactions ?? []; | ||
| setDrugInteractions(foundInteractions); | ||
| setInteractionCheckState(foundInteractions.length > 0 ? "found" : "clear"); |
There was a problem hiding this comment.
🔒 Security & Privacy | 🟠 Major | ⚡ Quick win
🧩 Analysis chain
🏁 Script executed:
#!/bin/bash
set -euo pipefail
# Localiser les schémas de validation existants avant d'ajouter un schéma de réponse.
fd -a '^validations\.ts$' . | while IFS= read -r file; do
ast-grep outline "$file" --items all
done
# Rechercher les contrats et schémas existants pour les interactions médicamenteuses.
rg -n -C 3 --glob '*.{ts,tsx}' \
'DrugInteractionPair|drugInteraction.*Schema|interactions.*z\.(array|object)'Repository: Zoubeir23/DocFlowAI
Length of output: 1542
🏁 Script executed:
#!/bin/bash
set -euo pipefail
# Inspectez le composant concerné et ses dépendances d'interaction médicamenteuse.
target="components/diagnostics/prescription-builder-step.tsx"
if [ -f "$target" ]; then
echo "== file exists =="
wc -l "$target"
echo "== outline =="
ast-grep outline "$target" --items all || true
echo "== relevant lines =="
sed -n '1,260p' "$target" | nl -ba
fi
echo "== search DrugInteractionPair and related symbols =="
rg -n -C 2 --glob '*.{ts,tsx}' 'DrugInteractionPair|drugInteraction|interactions|Warning|severity|failure|failed' || true
echo "== validations file =="
sed -n '1,180p' lib/validations.ts | nl -baRepository: Zoubeir23/DocFlowAI
Length of output: 2019
🏁 Script executed:
#!/bin/bash
set -euo pipefail
# Listiez les outils disponibles pour lire les segments de fichier.
command -v sed || true
command -v awk || true
file="components/diagnostics/prescription-builder-step.tsx"
types_file="types.ts"
for target in "$file" "$types_file"; do
if [ -f "$target" ]; then
echo "== $target =="
wc -l "$target"
sed -n '1,260p' "$target"
else
echo "== missing $target =="
fi
done
echo "== DrugInteractionWarning =="
fd -a 'drug-interaction-warning\.(tsx|ts)$' . | while IFS= read -r f; do
echo "== $f =="
wc -l "$f"
sed -n '1,220p' "$f"
done
echo "== DrugInteractionPair candidates =="
rg -n -C 3 --glob '*.{ts,tsx}' 'type DrugInteractionPair|interface DrugInteractionPair|DrugInteractionPair' || trueRepository: Zoubeir23/DocFlowAI
Length of output: 13094
🏁 Script executed:
#!/bin/bash
set -euo pipefail
echo "== locate types files =="
fd -a '\.(ts|tsx)$' . | rg '/types\.ts$|types/$|types' || true
echo "== search DrugInteractionPair in tracked files =="
git ls-files | xargs rg -n -C 4 'DrugInteractionPair' || true
echo "== read candidate types files =="
for f in $(git ls-files | rg '/types\.ts$'); do
echo "== $f =="
wc -l "$f"
sed -n '1,260p' "$f"
done
echo "== inspect response body path if any =="
git ls-files | rg 'api/who/drug-interactions|drug.*interaction' | while IFS= read -r f; do
echo "== $f =="
wc -l "$f"
sed -n '1,220p' "$f"
done
echo "== validate empty/missing JSON behavior =="
node - <<'JS'
for const value of [{}, {interactions: undefined}, {interactions: null}, {interactions: true}, {interactions: [{severity: "unknown"}]}] {
const found = value.interactions ?? []
const state = found.length > 0 ? "found" : "clear"
try {
const severityOrder = { high: 0, moderate: 1, low: 2 }
const sorted = [...found].sort((first, second) => severityOrder[first.severity] - second.severity)
console.log(JSON.stringify(value), "=>", state, "sorted:", JSON.stringify(sorted))
} catch (e) {
console.log(JSON.stringify(value), "=> crashed:", e.message)
}
}
JSRepository: Zoubeir23/DocFlowAI
Length of output: 17442
🏁 Script executed:
#!/bin/bash
set -euo pipefail
echo "== node behavior probe =="
node - <<'JS'
for (const value of [
{},
{interactions: undefined},
{interactions: null},
{interactions: true},
{interactions: [{drug1Name: "A", drug2Name: "B", severity: "unknown", description: "x", source: "r"}]},
{interactions: [{drug1Name: "A", drug2Name: "B", severity: "high", description: "x", source: "r"}]},
]) {
const result = { interactions: value.interactions ?? [] }
const foundInteractions = result.interactions
const state = foundInteractions.length > 0 ? "found" : "clear"
try {
const sortedInteractions = [...foundInteractions].sort((first, second) => {
const severityOrder = { high: 0, moderate: 1, low: 2 }
return severityOrder[first.severity] - second.severity
})
const config = {
high: { containerClass: "bg-red-50 border-red-200" },
moderate: { containerClass: "bg-amber-50 border-amber-200" },
low: { containerClass: "bg-blue-50 border-blue-200" },
}
const interaction = sortedInteractions[0]
const actualConfig = config[interaction && interaction.severity] || {}
console.log(JSON.stringify(value), "=> result.interactions =", JSON.stringify(result.interactions), "state =", state, "className =", actualConfig.containerClass || "undefined")
} catch (error) {
console.log(JSON.stringify(value), "=> crashed =", error.message)
}
}
JS
echo "== read full route =="
sed -n '1,160p' app/api/who/drug-interactions/route.tsRepository: Zoubeir23/DocFlowAI
Length of output: 2208
Validez la réponse /api/who/drug-interactions avant de mettre à jour l’état.
La validation TypeScript de result.interactions ne protège pas les réponses JSON corrompues. Une clé interactions manquante ou null est affichée comme interactionsClear; un tableau contenant une sévérité invalide peut faire échouer DrugInteractionWarning. Ajoutez un schéma Zod pour interactions et chaque DrugInteractionPair, puis passez l’état à failed en cas d’échec.
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
In `@components/diagnostics/prescription-builder-step.tsx` around lines 180 - 183,
Validate the parsed response in the drug-interactions request before updating
state: define or reuse a Zod schema for the response and each
DrugInteractionPair, including the allowed interaction severity values, and
parse the JSON result. Only call setDrugInteractions and
setInteractionCheckState with validated data; on schema failure, set the
interaction check state to "failed" instead of treating missing, null, or
malformed interactions as clear.
Source: Coding guidelines
| <AlertTriangle | ||
| className={`w-4 h-4 ${interactionCheckState === "failed" ? "text-destructive" : "text-amber-500"}`} | ||
| /> | ||
| <h3 | ||
| className={`text-sm font-semibold uppercase tracking-wide ${ | ||
| interactionCheckState === "failed" ? "text-destructive" : "text-amber-600" | ||
| }`} | ||
| > | ||
| {t("prescriptionStep.interactionsTitle")} | ||
| {interactionCheckState === "checking" && ( | ||
| <Loader2 className="inline ml-2 w-3 h-3 animate-spin" /> | ||
| )} | ||
| </h3> | ||
| </div> | ||
| <DrugInteractionWarning interactions={drugInteractions} /> | ||
|
|
||
| {interactionCheckState === "checking" && ( | ||
| <p className="text-sm text-muted-foreground"> | ||
| {t("prescriptionStep.interactionsChecking")} | ||
| </p> | ||
| )} | ||
|
|
||
| {interactionCheckState === "found" && ( | ||
| <DrugInteractionWarning interactions={drugInteractions} /> | ||
| )} | ||
|
|
||
| {interactionCheckState === "clear" && ( | ||
| <p className="flex items-center gap-2 text-sm text-emerald-600"> | ||
| <Check className="w-4 h-4" /> | ||
| {t("prescriptionStep.interactionsClear")} |
There was a problem hiding this comment.
📐 Maintainability & Code Quality | 🟡 Minor | ⚡ Quick win
Remplacez les couleurs Tailwind brutes par des tokens de thème.
text-amber-500, text-amber-600 et text-emerald-600 ne suivent pas les tokens définis dans app/globals.css. Ils peuvent aussi produire un contraste incohérent dans le thème sombre.
Utilisez des tokens sémantiques pour les états alerte et succès.
As per coding guidelines, « Utiliser uniquement les tokens de thème définis dans app/globals.css; ne pas coder de couleurs hexadécimales ou de couleurs sombres en dur ».
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
In `@components/diagnostics/prescription-builder-step.tsx` around lines 447 - 475,
Replace the raw Tailwind color classes in the interaction status UI—specifically
the amber classes on the AlertTriangle and heading plus the emerald class in the
clear-state paragraph—with the semantic alert and success theme tokens defined
in the global theme configuration. Preserve the existing failed-state
destructive styling and all interactionCheckState behavior.
Source: Coding guidelines
Contexte
Deux défauts relevés lors de l'audit des fonctionnalités signature / carnet / ordonnance, tous deux
sur des documents médicaux opposables ou la sécurité du patient. Les retours de review ont révélé
que la première version de cette PR ne corrigeait qu'à moitié le second point.
1. Le document portait la signature du lecteur, pas du médecin validateur
app/(app)/app/diagnostics/[id]/page.tsxappelaitgetDoctorSignature(), qui retourne lasignature de l'utilisateur connecté, puis l'apposait sous le nom et le n° RPPS du praticien
enregistré dans le diagnostic. Si le Dr B ouvrait une ordonnance validée par le Dr A, le document
imprimé portait le nom et le RPPS de A avec la signature manuscrite de B.
La page appelle désormais
getSignatureForValidatedDiagnostic(diagnosticId): le serveur résoutlui-même le validateur. Aucune Server Action ne prend plus d'identifiant d'utilisateur libre, ce
qui supprime au passage une primitive d'énumération — n'importe quel membre de la clinique pouvait
appeler
getDoctorSignatureByUserId(userId)depuis son navigateur et récupérer l'image designature d'un confrère sans ouvrir le moindre document.
validated_by_user_idest ajouté àDiagnosticRecord: la colonne existe depuis la migration 010et
select("*")la remontait déjà, mais le type l'ignorait.2. Une panne du service d'interactions s'affichait comme « aucune interaction »
Le défaut avait deux couches, et la première version de cette PR n'en traitait qu'une.
Couche serveur (la plus grave, corrigée depuis).
lib/who-drug-interactions.tsrenvoyait{ hasCritical: false, interactions: [] }quand RxNav répondait en erreur ou levait. La route APIretournait donc HTTP 200 avec une liste vide, et le prescripteur lisait une confirmation rassurante
alors qu'aucun contrôle n'avait eu lieu. Le résultat porte maintenant un
status(
checked|unavailable) et la route répond 502 sur indisponibilité.Couche client. Le panneau n'était rendu que s'il y avait des interactions ou un chargement en
cours : « API tombée » et « aucune interaction » produisaient le même écran vide. Le contrôle
expose désormais son issue dans tous les cas :
checking— contrôle en coursfound— liste des interactionsclear— confirmation explicite qu'aucune interaction n'est connuefailed— message en rouge « vérifiez manuellement avant de prescrire » + bouton de relanceCorrections complémentaires issues de la review :
une plus récente, un
clearpérimé masquant une alerte réelle. Un identifiant monotone écartetoute réponse qui n'appartient pas au dernier contrôle.
interactionsétait lu comme uneliste vide, donc affiché comme
clear. Il bascule maintenant enfailed.affiché sous le panneau.
Six libellés ajoutés en FR et EN sous
diagnostics.prescriptionStep.Tests
__tests__/lib/drug-interactions.test.ts— 6 tests couvrant le correctif de fond :indisponibilité sur réponse non-OK, sur exception réseau, distinction avec une absence réelle
d'interaction, absence d'appel réseau sous deux codes valides, remontée des sévérités hautes,
déduplication inter-sources.
Ces tests ont été vérifiés contre le défaut : en réintroduisant l'ancien comportement
(
!response.okrenvoyantchecked), la suite échoue.npx tsc --noEmit,npm run lint— propres, et la detteanydedoctor-signature.tspasse de 6 à 4 occurrences
npx vitest run— 199 testsnpm run build— succèsLimite restante : la correction n° 1 n'est pas atteignable par le harnais actuel (Vitest en
environnement
node) et sa vérification au navigateur suppose un diagnostic validé en base avecune signature enregistrée, données que je n'ai pas créées dans le projet Supabase.
Non traité — relevé pendant l'audit
doctor_signatures_select_cliniclaisse tout membre de la clinique lire l'image de signature den'importe quel médecin au niveau RLS. Cette PR ferme la voie applicative, pas la policy.
contenu signé.
checkAllergyConflictscompare par sous-chaîne : « pénicilline » ne déclenche rien sur« Amoxicilline ».