/** A delivery column, e.g. In-app, Email, Mobile push. */ export interface NotificationPreferenceChannel { /** Key into each row's values. */ id: string; /** Column heading and part of every cell's accessible name. */ label: string; } /** One notification type: a row of channel switches. */ export interface NotificationPreferenceRow { /** Passed to `onChange`. */ id: string; /** e.g. "Leave requests". */ label: string; /** One muted line under the label. */ hint?: string; /** Channel ids that are always on, shown checked, disabled and with a lock; in-app for approvals. */ locked?: string[]; } /** A titled card of rows, e.g. "Requests you approve". */ export interface NotificationPreferenceGroup { /** Stable key. */ id: string; /** Card heading. */ label: string; /** In display order. */ rows: NotificationPreferenceRow[]; } /** Saved state as `values[rowId][channelId]`; a missing cell reads as off. */ export type NotificationPreferenceValues = { [rowId: string]: { [channelId: string]: boolean | undefined; } | undefined; }; /** How email arrives: one per notification, or one summary a day at 9:00. */ export type NotificationEmailSummary = "asItHappens" | "daily"; /** Load state; "readOnly" shows the settings an admin manages but nothing can change. */ export type NotificationPreferencesStatus = "loading" | "ready" | "error" | "readOnly"; /** Strings for the settings page; anything left out stays English. */ export interface NotificationPreferencesLabels { /** Page heading. */ title: string; /** Under the heading; gets the app name. */ subtitle: (appName?: string) => string; /** Email card heading. */ email: string; /** Title of the email timing row. */ emailTiming: string; /** Under the email timing title; gets the app name. */ emailHint: (appName?: string) => string; /** Screen-reader heading of the type column. */ typeColumn: string; /** Tooltip and label explaining why a locked cell cannot be turned off. */ lockedTip: string; /** "As it happens" option. */ asItHappens: string; /** "Daily at 9:00 am" option. */ daily: string; /** Accessible suffix of a locked cell. */ alwaysOn: string; /** Status while a change is in flight. */ saving: string; /** Status after the last change saved. */ saved: string; /** Status before any change. */ autosave: string; /** Toast after a failed save; gets what was put back. */ saveFailed: (what: string) => string; /** Banner while `status` is "readOnly". */ readOnlyBanner: string; /** Banner while offline. */ offlineBanner: string; /** Screen-reader text while `status` is "loading". */ loading: string; /** Error state title. */ errorTitle: string; /** Error state description. */ errorDescription: string; /** Error state retry button. */ retry: string; } /** Notification settings: a type-by-channel grid with locked cells and an email summary choice; every change autosaves and rolls back on failure. */ export interface NotificationPreferencesProps { /** Columns, in order. */ channels: NotificationPreferenceChannel[]; /** Cards of rows, in order. */ groups: NotificationPreferenceGroup[]; /** Saved state; the page shows changes optimistically until `onChange` settles. */ values: NotificationPreferenceValues; /** Saves one cell; reject to roll the cell back with a toast. */ onChange: (rowId: string, channelId: string, enabled: boolean) => Promise; /** Shows the email card. */ emailSummary?: NotificationEmailSummary; /** Saves the email choice; reject to roll it back with a toast. */ onEmailSummaryChange?: (value: NotificationEmailSummary) => Promise; /** Defaults to "ready". */ status?: NotificationPreferencesStatus; /** Retries a failed load from the error state. */ onRetry?: () => void; /** Disables every cell and shows the offline banner. */ isOffline?: boolean; /** Footnote under the cards, e.g. what admins lock. */ note?: string; /** As in "Choose where hrms tells you about things". */ appName?: string; /** "desktop" is a table per card; "phone" stacks a row's switches under its label. Defaults to "desktop". */ variant?: "desktop" | "phone"; /** Overrides for the page strings. */ labels?: Partial; /** Test id root; each cell gets `--`. */ "data-testid"?: string; }