import { type HTMLAttributes, type ReactNode } from 'react'; import { type VariantProps } from 'class-variance-authority'; import { type TaskItem } from '../task-card'; /** Which set of tasks the panel shows — the host owns the actual feed. */ export type TasksPanelScope = 'patient' | 'mine'; /** Status segment value. Mirrors the platform `done` filter (default `todo`). */ export type TasksPanelStatusFilter = 'todo' | 'done' | 'all'; /** Who filters the list: the panel ('client') or the host ('server'). */ export type TasksPanelFiltering = 'client' | 'server'; /** The whole filter state, as `onFiltersChange` reports it. */ export interface TasksPanelFilters { status: TasksPanelStatusFilter; type: string | null; assignee: string | null; overdueOnly: boolean; } /** * Panel-local extension of the card's `TaskItem`. The extra fields here drive * bucketing + filtering; the row's display fields (the typed link via * `linkType` + `linkedLabel`, the `assigneeLabel`, `dueLabel`) live on * `TaskItem` and are rendered by `TaskCard` itself. The host maps the platform * `DoctorReminder` → this shape — composing `title` (a summary, NOT the bare * type), `note`, the linked entity, the assignee, and `dueLabel`. */ export interface TasksPanelItem extends TaskItem { /** ISO-8601 due/scheduled instant (platform `DoctorReminder.date`). Buckets + orders the list. */ dueIso: string; /** System or custom task-type key (`recall` | `payment` | `collection` | `other` | `custom-*`). Drives the type filter + badge. */ type?: string; /** * Localised type label, required for CUSTOM types (the panel resolves system * keys itself). Falls back to the system label for the four known keys, * then to "Other". The raw key is never shown. */ typeLabel?: string; /** * Assignee id (the platform `practiceUser`). Omit ⇒ unassigned; give pool * tasks a shared sentinel value (e.g. `'unassigned'`) if you want the pool * itself filterable. Drives the assignee FILTER (exact match); the displayed * name is `TaskItem.assigneeLabel`. */ assignee?: string; /** Provenance — manual vs workflow-created. Filter only. */ createdBy?: 'user' | 'workflow'; } /** Curated imperative handle — identifiers + structural state only, no PHI. */ export interface TasksPanelHandle { getItems: () => Array<{ id: string; status: string; }>; selectItem: (id: string) => void; /** * Same path as a click on the done box. Resolves once the host has * answered (in client mode, after the row's fade) and rejects if the * host refuses, or if nothing was started: an unknown id, a read-only * panel, no `onToggleDone`, or a change to that task already pending. */ toggleDone: (id: string) => Promise; /** Same path as the row's dismiss action, and settles in the same way. */ dismiss: (id: string) => Promise; jumpToOverdue: () => void; setFilter: (next: { status?: TasksPanelStatusFilter; type?: string | null; assignee?: string | null; overdueOnly?: boolean; }) => void; /** Switch the task scope segment (patient ↔ mine). The host re-feeds items. */ changeScope: (next: TasksPanelScope) => void; /** Request the host to append the next page of tasks (mirrors onLoadMore). */ loadMore: () => void; } interface FilterOption { /** Stable value passed back through the matching `on…Change`. */ value: string; /** Localised label. */ label: string; } export interface TasksPanelProps extends Omit, 'onClick' | 'id' | 'title'> { /** Stable instance id, surfaced as `data-component-id`. */ id?: string; /** Tasks to render — any order; bucketed + sorted internally (due ASC, Done last). */ items: TasksPanelItem[]; /** ISO-8601 "now" anchor — buckets + overdue compute against it. Defaults to the current instant; stories pass a fixed value. */ nowIso?: string; /** Panel heading. Defaults to the localised "Tasks". */ title?: string; /** PanelHeader glyph. Default . */ icon?: ReactNode; /** * A total the host counted, shown as given after the title ("Tasks (42)") * and read as "42 tasks". Omitted: nothing shown. The panel never * computes it. */ totalCount?: number; /** Visual density forwarded to each TaskCard. Defaults to `sm`, matching the note and appointment rows. */ size?: 'xs' | 'sm' | 'md'; /** * 'client' (default): the panel filters `items`. 'server': the host has * already filtered and paged `items`; the panel shows them as given. In * server mode a filter control shows only when `onFiltersChange` or its * own `on…Change` is set, since only the host can re-query. */ filtering?: TasksPanelFiltering; /** Fires when a row is activated. */ onOpenTask?: (item: TasksPanelItem) => void; /** * Fires when the host should set a task's done state to `nextDone`. In * server mode it fires on the click. In client mode it fires on the click * for a row that stays in view, and once the row has faded for one that * leaves. Either way, the box ignores clicks until a returned promise * settles, and a promise that rejects puts the row back, unticked. The * panel does not announce a refusal: report it yourself (for example * `toastError` with a translated message) and rethrow, so the row still * rolls back. */ onToggleDone?: (item: TasksPanelItem, nextDone: boolean) => void | Promise; /** Fires when the host should open the task editor. Omit to hide the per-row edit action. */ onEditTask?: (item: TasksPanelItem) => void; /** * Fires when the host should dismiss / remove a task. Omit to hide the * per-row dismiss action. In server mode it fires on the click, in client * mode once the row has faded. A returned promise that rejects restores * the row. The panel does not announce a refusal: report it yourself and * rethrow, as for `onToggleDone`. */ onDismissTask?: (item: TasksPanelItem) => void | Promise; /** Fires when the header "Add" affordance is activated. Omit to hide it. */ onAddTask?: () => void; /** * Read-only mode — hides every write affordance (toggle / edit / dismiss / * add), and the handle's `toggleDone` and `dismiss` reject without calling * the host. Mirrors the platform gating writes behind `practice_task_edit`. */ readOnly?: boolean; /** * Per-row trailing action toolbar, forwarded to each TaskCard's `actions` * slot. Overrides the default toggle / edit / dismiss buttons (the type Badge * is always prepended by the panel). Ignored when `readOnly`. */ renderItemActions?: (item: TasksPanelItem) => ReactNode; /** * Extra per-row actions, rendered before the toggle / edit / dismiss * actions (or before `renderItemActions`, which still replaces those). * Not a task write, so `readOnly` does not hide them: withhold them from * the host side when they do not apply. */ renderItemExtraActions?: (item: TasksPanelItem) => ReactNode; /** Controlled scope segment. */ scope?: TasksPanelScope; /** Uncontrolled initial scope. Default `'patient'` when both scopes are available, else the only one. */ defaultScope?: TasksPanelScope; /** Fires when the user switches scope — the host re-feeds `items`. */ onScopeChange?: (next: TasksPanelScope) => void; /** Which scopes the segment offers. Omit ⇒ no scope segment (single-scope panel). */ availableScopes?: TasksPanelScope[]; /** Load the next page — fired once per page when the bottom edge is reached, and via the footer button. */ onLoadMore?: () => void; /** Whether more tasks exist. Defaults to `true` when `onLoadMore` is set (client mode only; server mode needs `true`). */ hasMore?: boolean; /** Show the bottom loading row while the next page is fetched. */ loadingMore?: boolean; /** Loading state. Skeleton rows while there are none to show; in server mode, rows already shown stay. */ loading?: boolean; /** Error surface — `true` for the default error EmptyState, or a custom node. */ error?: boolean | ReactNode; /** Adds a Retry action to the default error state (`error === true`). */ onRetry?: () => void; /** * Show the filter header (status segment + type / assignee selects + * overdue toggle). Default `true`. In server mode each control also needs * `onFiltersChange` or its own `on…Change`. */ showFilters?: boolean; /** * The host's default query: the uncontrolled start, what Clear filters * returns to, and what server mode judges "filtered empty" against. * Absent: To do, any type, anyone, not overdue-only. */ defaultFilters?: TasksPanelFilters; /** * Once per user action, with the whole next filter state. Never fires * when nothing changed. Fires in both modes. */ onFiltersChange?: (next: TasksPanelFilters) => void; /** Controlled status segment. */ statusFilter?: TasksPanelStatusFilter; /** Uncontrolled initial status segment. Default `'todo'` (the platform default). */ defaultStatusFilter?: TasksPanelStatusFilter; onStatusFilterChange?: (next: TasksPanelStatusFilter) => void; /** Task types for the type filter. Omit ⇒ no type filter. */ typeOptions?: FilterOption[]; /** Controlled type filter (`null`/empty ⇒ all). */ typeFilter?: string | null; onTypeFilterChange?: (next: string | null) => void; /** Assignees for the assignee filter. Pass an explicit Unassigned option if wanted. Omit ⇒ no assignee filter. */ assigneeOptions?: FilterOption[]; /** Controlled assignee filter (`null`/empty ⇒ anyone). */ assigneeFilter?: string | null; onAssigneeFilterChange?: (next: string | null) => void; /** Controlled overdue-only toggle. */ overdueOnly?: boolean; /** Uncontrolled initial overdue-only state. Default `false`. */ defaultOverdueOnly?: boolean; onOverdueOnlyChange?: (next: boolean) => void; emptyTitle?: string; emptyDescription?: string; /** CTA wired into the first-use empty state (e.g. Create task). */ emptyAction?: { label: string; onClick: () => void; }; /** Accessible name for the panel region (defaults to `title`). */ 'aria-label'?: string; } declare const rootVariants: (props?: ({ size?: "sm" | "md" | "xs" | null | undefined; } & import("class-variance-authority/types").ClassProp) | undefined) => string; export declare const TasksPanel: import("react").ForwardRefExoticComponent>; /** Re-export of the size variant union for downstream typing. */ export type TasksPanelSize = NonNullable['size']>; export {}; //# sourceMappingURL=tasks-panel.d.ts.map