Notification Settings Toggles
Business view
Every Dentolize user already has a personal list of notification preferences — one on/off switch per topic (invoices, inventory, reminders, calendar, treasury, and so on). This PR adds two more entries to that same list:
- Daily Summary — a financial/operational recap of the day, for people
who can already see that data through analytics.
- Daily Appointments Summary — a personal recap for doctors of what
they finished today and what's first tomorrow.
Both default to on for every user, existing and new — nobody has to opt in. Turning either off is a personal choice with no effect on anyone else's notifications, and no effect on whether the underlying data is calculated (the cron still processes every company each night; it just skips sending to that one person).
Because the setting lives on the user record, it travels with the person, not the role — a doctor who's also a manager can, for instance, keep the appointments recap and turn off the financial summary, or vice versa.
Technical view
Data model
Two new Boolean columns on UserAlertSettings, both @default(true):
model UserAlertSettings {
...
treasury Boolean @default(true)
summaryCron Boolean @default(true)
appointmentsCron Boolean @default(true)
...
}
packages/prisma/schema.prisma:3319-3323
The migration is a plain ALTER TABLE ... ADD COLUMN ... DEFAULT true (packages/prisma/migrations/20260902120000_add_daily_notification_alert_settings/migration.sql:1-3), so every existing row is backfilled to true at migration time — there's no separate backfill script and no window where old users are silently opted out.
GraphQL surface
AlertSettingsInputgainssummaryCron: Booleanand
appointmentsCron: Boolean (packages/server/src/inputs.graphql:590-591).
UserAlertSettings(the output type) gains the matching non-null fields
(packages/server/src/types.graphql:2470-2471).
- The
updateUserDetailsmutation'salertSettings.createdefaults and the
me / user-details queries' alertSettings.select both list the two new fields explicitly (packages/server/src/resolvers/mutations/userMutations.js:234-235, packages/server/src/resolvers/queries/userQueries.js:116-117) — Prisma doesn't infer select/create shapes from the schema automatically, so every one of these call sites has to be updated by hand whenever a field is added to UserAlertSettings. That's the same pattern the treasury field already followed.
Mobile UI
NotificationsScreen.js builds its form fields dynamically from Object.keys(user.alertSettings) (filtering out __typename/id), so adding the GraphQL fields above is not enough on its own — the two new SwitchField rows had to be added explicitly:
<SwitchField title={t('app.summaryCron')} control={control} name="summaryCron" />
...
<SwitchField title={t('app.appointmentsCron')} control={control} name="appointmentsCron" />
packages/clinic-mobile/src/components/dashboard/More/settings/NotificationsScreen.js:126-134
Saving calls UPDATE_USER_MUTATION with the whole alertSettings object from the form (NotificationsScreen.js:47-52), and that mutation's selection set was extended to request/send summaryCron and appointmentsCron (packages/clinic-mobile/src/shared/store/mutations/authMutations.js:202-203, packages/clinic-mobile/src/shared/store/queries/userQueries.js:81-82).
Web UI — and a side fix
The web MainSettings form works differently: it drives a single Checkbox.Group off a hardcoded alertSettingsTypes array, and on submit reduces that array (not the checked values) into the payload:
alertSettings: alertSettingsTypes.reduce((obj, s) => {
obj[s] = values.alertSettings.includes(s)
return obj
}, {})
packages/clinic-web/src/components/dashboard/settings/Account/MainSettings.js:115-118
Before this PR, alertSettingsTypes ended at 'calendar' — treasury was not in the list, even though a "Treasury" <Checkbox value="treasury"> already existed in the JSX (MainSettings.js:359-366). The practical effect: checking/unchecking Treasury notifications in the web app rendered fine and looked interactive, but the reduce above never iterated over 'treasury', so the value silently never made it into the mutation payload — toggling it on web did nothing.
This PR's alertSettingsTypes change adds all three missing keys at once:
const alertSettingsTypes = [
'invoice', 'inventory', 'masterInventory', 'reminder', 'onlineAppointment',
'appointmentQrCode', 'pendingPatients', 'orders', 'operations', 'calendar',
'treasury', 'summaryCron', 'appointmentsCron'
]
packages/clinic-web/src/components/dashboard/settings/Account/MainSettings.js:42-54
So as a side effect of wiring up the two new toggles, this PR also makes the pre-existing Treasury checkbox actually persist on web for the first time. Two new <Col> blocks with <Checkbox value="summaryCron"> and <Checkbox value="appointmentsCron">, each with the same tooltip pattern as every other row, complete the web side (MainSettings.js:367-382).
Translation keys
Both toggle labels and their tooltip bodies are added to all 9 shipped language files (en, ar, fr, it, el, nl, pl, ku, ckb) under the existing app.* namespace: app.summaryCron, app.summaryCronNotifications, app.appointmentsCron, app.appointmentsCronNotifications. These are the UI-facing strings (checkbox label + tooltip) — they are a separate set of strings from the ones baked into the cron's own push-notification bodies, covered in The Cron.