Project: workspace-doctolib · Branch: HEAD · Mode: ` · **Model:**claude-sonnet-4-6`
Stats: 1 user messages, 13 tool calls, tools used: Read, Bash.
Conversation
Turn 1 — User
Review the final state of the following 5 files modified in PR https://github.com/doctolib/treatment-plan/pull/3129 (branch fhir-2233-fix-toast-event-name). The fix aligns the pub/sub event name in all TPF toast helpers from the wrong 'toasts:add' to 'toaster:add' (what the monolith's <Toaster> subscribes to), and also removes ReactNode in favour of string for the message field to keep src/business/ files framework-agnostic.
Files to review:
/Users/USER/workspace/doctolib/treatment-plan/packages/tpf-national-health-records-ui/src/business/toast.ts/Users/USER/workspace/doctolib/treatment-plan/packages/tpf-nhr-fr/src/components/dmp-consultation-consent-view/toast.ts/Users/USER/workspace/doctolib/treatment-plan/packages/tpf-prescription-fulfillments/src/business/eprescription/toast.ts/Users/USER/workspace/doctolib/treatment-plan/packages/tpf-prescription-fulfillments/src/business/eprescription/hooks/useEPrescriptionActions.ts/Users/USER/workspace/doctolib/treatment-plan/packages/tpf-nhr-fr/.storybook/preview.tsx
All three toast files should be identical and look like:
import { GLOBAL_PUBSUB_REGISTRY } from '@doctolib/upfront-core'
export type ToastProps = {
message: string
title: string
uiStyle: 'info' | 'success' | 'error' | 'warning'
action?: { message: string; onClick: () => void }
}
export function addToast(props: ToastProps): void {
GLOBAL_PUBSUB_REGISTRY.dispatch({ type: 'toaster:add', payload: props })
}
Please review all 5 files and report: - Any remaining UpFront architecture violations - Any naming convention issues - Any separation-of-concerns problems - Any other issues that need fixing before the PR is approved
Be specific: if there are no issues, say "no remarks".
Turn 1 — Assistant
I'll read all 5 files in parallel.
Tools used:
Read:/Users/USER/workspace/doctolib/treatment-plan/packages/tpf-national-health-records-ui/src/business/toast.ts
Tool results:
→ result (ok): 1 import { GLOBAL_PUBSUB_REGISTRY } from '@doctolib/upfront-core'
2
3 export type ToastProps = {
4 message: string
5 title: string
6 uiStyle: 'info' | 'success' | 'error' | 'warning'
7 action?: { message: string; onClick: () => void }
8 }
9
10 export function addToast(props: ToastProps): void {
11 GLOBAL_PUBSUB_REGISTRY.dispatch({ type: 'toaster:add', payload: props })
12 }
13
Tools used:
Read:/Users/USER/workspace/doctolib/treatment-plan/packages/tpf-nhr-fr/src/components/dmp-consultation-consent-view/toast.ts
Tool results:
→ result (ok): 1 import { GLOBAL_PUBSUB_REGISTRY } from '@doctolib/upfront-core'
2
3 export type ToastProps = {
4 message: string
5 title: string
6 uiStyle: 'info' | 'success' | 'error' | 'warning'
7 action?: { message: string; onClick: () => void }
8 }
9
10 export function addToast(props: ToastProps): void {
11 GLOBAL_PUBSUB_REGISTRY.dispatch({ type: 'toaster:add', payload: props })
12 }
13
Tools used:
Read:/Users/USER/workspace/doctolib/treatment-plan/packages/tpf-prescription-fulfillments/src/business/eprescription/toast.ts
Tool results:
→ result (ok): 1 import { GLOBAL_PUBSUB_REGISTRY } from '@doctolib/upfront-core'
2
3 export type ToastProps = {
4 message: string
5 title: string
6 uiStyle: 'info' | 'success' | 'error' | 'warning'
7 action?: { message: string; onClick: () => void }
8 }
9
10 export function addToast(props: ToastProps): void {
11 GLOBAL_PUBSUB_REGISTRY.dispatch({ type: 'toaster:add', payload: props })
12 }
13
Tools used:
Read:/Users/USER/workspace/doctolib/treatment-plan/packages/tpf-prescription-fulfillments/src/business/eprescription/hooks/useEPrescriptionActions.ts
Tools used:
Read:/Users/USER/workspace/doctolib/treatment-plan/packages/tpf-nhr-fr/.storybook/preview.tsx
Tool results:
→ result (ok): 1 import { i18n } from '@doctolib/tpf-commons/i18n'
2 import { isFeatureEnabled } from '@doctolib/variation-management/legacy'
3 import dayjs from 'dayjs'
4 import utc from 'dayjs/plugin/utc'
5
6 import '../../../i18n'
7 import type { QrCodeObject } from '../QRCodeType'
8 import type { EPrescriptionContextError, EPrescriptionSoapError, EPrescriptionTrace } from '../ePrescriptionTypes'
9 import { generateQrCodeObject, generateSVGQrCode } from '../helpers/QRCode'
10 import { buildCreateEPrescript
…(truncated, 21137 more chars)
Tool results:
→ result (ok): 1 import '@doctolib/design-system/design-system.scss'
2 import { GLOBAL_PUBSUB_REGISTRY } from '@doctolib/upfront-core'
3 import { action } from '@storybook/addon-actions'
4 import type { Preview } from '@storybook/react'
5 import { useEffect } from 'react'
6
7 import { DmpPatientFileContextProvider } from '../src/hooks'
8 import { getInstance } from '../src/i18n'
9
10 const i18n = getInstance()
11
12 let isI18nInitialized = false
13
14 const i18nInitLoader = async () => {
15 if (!isI18nIn
…(truncated, 1222 more chars)
Now let me check the placement of the toast.ts file inside tpf-nhr-fr more carefully, since it lives under src/components/ rather than src/business/.
Tools used:
Bash:find /Users/USER/workspace/doctolib/treatment-plan/packages/tpf-nhr-fr/src/components/dmp-consultation-consent-view -type f | sort
Tool results:
→ result (ok): /Users/USER/workspace/doctolib/treatment-plan/packages/tpf-nhr-fr/src/components/dmp-consultation-consent-view/dmp-consultation-consent-view.test.tsx /Users/USER/workspace/doctolib/treatment-plan/packages/tpf-nhr-fr/src/components/dmp-consultation-consent-view/dmp-consultation-consent-view.tsx /Users/USER/workspace/doctolib/treatment-plan/packages/tpf-nhr-fr/src/components/dmp-consultation-consent-view/event-handlers.ts /Users/USER/workspace/doctolib/treatment-plan/packages/tpf-nhr-fr/src/compon …(truncated, 288 more chars)
Tools used:
Bash:find /Users/USER/workspace/doctolib/treatment-plan/packages/tpf-nhr-fr/src/business -type f 2>/dev/null | sort; find /Users/USER/workspace/doctolib/treatment-plan/packages/tpf-national-healt …(truncated, 52 more chars)
Tool results:
→ result (ok): /Users/USER/workspace/doctolib/treatment-plan/packages/tpf-nhr-fr/src/business/consultation-consent-workflow/action-executor.test.ts /Users/USER/workspace/doctolib/treatment-plan/packages/tpf-nhr-fr/src/business/consultation-consent-workflow/action-executor.ts /Users/USER/workspace/doctolib/treatment-plan/packages/tpf-nhr-fr/src/business/consultation-consent-workflow/action-map.test.ts /Users/USER/workspace/doctolib/treatment-plan/packages/tpf-nhr-fr/src/business/consultation-consent-workflow/ac …(truncated, 9308 more chars)
Tools used:
Bash:find /Users/USER/workspace/doctolib/treatment-plan/packages/tpf-prescription-fulfillments/src/business/eprescription -type f | sort
Tool results:
→ result (ok): /Users/USER/workspace/doctolib/treatment-plan/packages/tpf-prescription-fulfillments/src/business/eprescription/EPrescriptionFormsContext.tsx /Users/USER/workspace/doctolib/treatment-plan/packages/tpf-prescription-fulfillments/src/business/eprescription/ePrescriptionSoapTypes.ts /Users/USER/workspace/doctolib/treatment-plan/packages/tpf-prescription-fulfillments/src/business/eprescription/ePrescriptionTypes.ts /Users/USER/workspace/doctolib/treatment-plan/packages/tpf-prescription-fulfillments/s …(truncated, 10070 more chars)
Now I have a complete picture. Let me check the useEPrescriptionActions.ts for the eslint-disable comment on line 99 and line 366, and verify whether addToast is being passed as a callback (line 471) which might conflict with the string message type.
Tools used:
Read:/Users/USER/workspace/doctolib/treatment-plan/packages/tpf-prescription-fulfillments/src/business/eprescription/helpers/QRCode.ts(1–30)
Tool results:
→ result (ok): 1 import { isFeatureEnabled } from '@doctolib/variation-management/legacy'
2 import QRCode, { type QRCodeToStringOptions } from 'qrcode'
3
4 import type {
5 AddToastProps,
6 ConsultationDocument,
7 EPrescriptionQRCodeContent,
8 HttpClient,
9 LpsAssertionDataVersioning,
10 OwnedCpsSituation,
11 Patient,
12 Prescription,
13 SSVCartePSPlus,
14 ServicePrescription,
15 } from '../injectionTypes'
16 import { E_PRESCRIPTION_TRACE_STATUS, E_PRESCRIPTION_TRACE_STRUCTURE } from '../tr
…(truncated, 536 more chars)
Tools used:
Read:/Users/USER/workspace/doctolib/treatment-plan/packages/tpf-prescription-fulfillments/src/business/eprescription/injectionTypes.ts(1–30)
Tool results:
→ result (ok): 1 /*
2 * Injection types for monolith dependencies.
3 *
4 * These interfaces define the contract between the treatment-plan package
5 * and the monolith. The monolith must inject concrete implementations
6 * at runtime (via React context or props) so this package remains decoupled.
7 /
8
9 // Re-exports from tpf-commons / other treatment-plan packages
10 export type { ISODateStr } from '@doctolib/tpf-commons/types'
11 export type { MonolithSendMonitoringEvent as SendMonitoringEvent } fr
…(truncated, 588 more chars)
Tools used:
Bash:grep -n "AddToastProps\|addToast" /Users/USER/workspace/doctolib/treatment-plan/packages/tpf-prescription-fulfillments/src/business/eprescription/injectionTypes.ts
Tool results:
→ result (ok): 63:export type AddToastProps = { 69:export type ToastFunction = (props: AddToastProps) => void
Tools used:
Bash:grep -n "eslint-disable" /Users/USER/workspace/doctolib/treatment-plan/packages/tpf-prescription-fulfillments/src/business/eprescription/hooks/useEPrescriptionActions.ts
Tool results:
→ result (ok): 99:// eslint-disable-next-line max-lines-per-function 366: // eslint-disable-next-line max-lines-per-function 540: / eslint-disable promise/prefer-await-to-then -- .then()/.finally() needed to capture promise ref for addInProgress /
Tools used:
Read:/Users/USER/workspace/doctolib/treatment-plan/packages/tpf-prescription-fulfillments/src/business/eprescription/injectionTypes.ts(60–140)
Tool results:
→ result (ok): 60 // Toast – injected by monolith
61 // ---------------------------------------------------------------------------
62
63 export type AddToastProps = {
64 message: React.ReactNode
65 title: string
66 uiStyle: 'info' | 'success' | 'error' | 'warning'
67 }
68
69 export type ToastFunction = (props: AddToastProps) => void
70
71 // ---------------------------------------------------------------------------
72 // Account context data – injected by monolith
73 // -----------------------------
…(truncated, 2155 more chars)
Now I have everything I need. Here is the full review.
Review: PR #3129 — fhir-2233-fix-toast-event-name
Critical Issues (must fix)
1. AddToastProps.message in injectionTypes.ts is still React.ReactNode, not string
/Users/USER/workspace/doctolib/treatment-plan/packages/tpf-prescription-fulfillments/src/business/eprescription/injectionTypes.ts, lines 63–67:
export type AddToastProps = {
message: React.ReactNode // <-- NOT fixed
title: string
uiStyle: 'info' | 'success' | 'error' | 'warning'
}
The PR description explicitly states the goal is to remove ReactNode in favour of string to keep src/business/ files framework-agnostic. The three toast.ts files were correctly updated to use message: string in their local ToastProps, but AddToastProps in injectionTypes.ts was not updated. This type is used as the callback signature (ToastFunction) that gets passed into generateSVGQrCode at line 471 of useEPrescriptionActions.ts. The two types are now inconsistent: ToastProps.message is string, but AddToastProps.message is React.ReactNode. This breaks the stated goal and leaves React as an import dependency inside src/business/.
Fix: change message: React.ReactNode to message: string in AddToastProps, and verify injectionTypes.ts no longer needs a React import for this reason.
2. toast.ts is misplaced in tpf-nhr-fr — it lives inside src/components/, not src/business/
/Users/USER/workspace/doctolib/treatment-plan/packages/tpf-nhr-fr/src/components/dmp-consultation-consent-view/toast.ts
The file is a framework-agnostic utility (no React, no JSX, pure pubsub dispatch) and is structurally identical to the toast.ts files in the other two packages' src/business/ directories. Placing it inside a component directory violates the layer separation rule: src/business/ is for pure, reusable domain utilities; component directories are for component-local concerns. A file named toast.ts that exports ToastProps and addToast is not a component-local helper — it is a package-level business utility.
The correct location is /Users/USER/workspace/doctolib/treatment-plan/packages/tpf-nhr-fr/src/business/toast.ts, mirroring the other two packages. The import in event-handlers.ts (or wherever it is consumed) would then use #business/toast instead of a relative path.
Warnings (should fix)
3. useEPrescriptionActions.ts suppresses two ESLint rules with inline disable comments
Lines 99 and 366 use // eslint-disable-next-line max-lines-per-function. Project rules (captured in MEMORY.md and user global CLAUDE.md) explicitly prohibit eslint-disable comments — fix the code instead. These were pre-existing and not introduced by this PR, but touching this file in the PR makes it the right moment to flag them. The function should be split rather than silenced.
4. preview.tsx uses a default export without justification comment
/Users/USER/workspace/doctolib/treatment-plan/packages/tpf-nhr-fr/.storybook/preview.tsx, line 65:
export default preview
The UpFront architecture rule requires that any default export include a comment explaining why it is necessary. Storybook requires a default export from preview.tsx, so the justification exists — it just needs to be documented inline. Add a comment such as // Storybook requires a default export for preview configuration immediately above the export.
Suggestions (consider improving)
5. ToastListener in preview.tsx is an unnamed inline component used only as a decorator side-effect
/Users/USER/workspace/doctolib/treatment-plan/packages/tpf-nhr-fr/.storybook/preview.tsx, lines 29–39. The component itself is fine, but it could be inlined directly as a decorator effect (useEffect in the decorator function body) rather than a standalone component, avoiding one extra component definition for a purely imperative side-effect. This is a minor style point and not a blocker.
6. The three toast.ts files are exact duplicates
/Users/USER/workspace/doctolib/treatment-plan/packages/tpf-national-health-records-ui/src/business/toast.ts, /Users/USER/workspace/doctolib/treatment-plan/packages/tpf-prescription-fulfillments/src/business/eprescription/toast.ts, and (once moved) /Users/USER/workspace/doctolib/treatment-plan/packages/tpf-nhr-fr/src/business/toast.ts are byte-for-byte identical. If there is a shared package available (e.g., tpf-commons), this utility is a candidate for extraction. Not a blocker for this PR, but worth noting as a future consolidation target.
Summary of required changes before approval
- In
/Users/USER/workspace/doctolib/treatment-plan/packages/tpf-prescription-fulfillments/src/business/eprescription/injectionTypes.ts: changeAddToastProps.messagefromReact.ReactNodetostring. Confirm no other field on that type requires the React import. - Move
/Users/USER/workspace/doctolib/treatment-plan/packages/tpf-nhr-fr/src/components/dmp-consultation-consent-view/toast.tsto/Users/USER/workspace/doctolib/treatment-plan/packages/tpf-nhr-fr/src/business/toast.tsand update all consumers. - Add a justification comment above
export default previewin/Users/USER/workspace/doctolib/treatment-plan/packages/tpf-nhr-fr/.storybook/preview.tsx.