import type { ReactNode } from "react"; /** Config + event data for Timeline's grouped feed; onEventClick/onAddNote/onEditNote/onDeleteNote are all optional so the feed can be read-only. */ export interface TimelineProps { /** @deprecated Never read — use `timelineEvents`; passing `events` renders nothing. */ events?: TimelineEvent[]; /** Grouped by `created_at` day in array order — never sorted, so pass it pre-sorted. */ timelineEvents: TimelineEvents[]; /** Makes each card a button with hover lift and focus ring; without it cards are static. */ onEventClick?: (event: TimelineEvents, index: number) => void; /** `index` is the event's position in `timelineEvents`; editor closes at once unless `addNoteLoading` is wired. */ onAddNote?: (note: string, title: string, files: File[], event: TimelineEvents, index: number) => void; /** Unlike onAddNote, the edit editor only auto-closes via editNoteLoading flipping true→false — omit it and Save never closes the form. */ onEditNote?: (logNoteId: number, note: string, title: string, files: File[], event: TimelineEvents, index: number) => void; /** Fires immediately on the trash icon — no confirm step; add your own if needed. */ onDeleteNote?: (logNoteId: number, event: TimelineEvents, index: number) => void; /** Defaults to false; the "Add note" button only appears on card hover or focus. */ showAddNote?: boolean; /** Spinner on the "Add note" editor while the create request is in flight. */ addNoteLoading?: boolean; /** Spinner on the open note editor while the update request is in flight. */ editNoteLoading?: boolean; /** log_note_id currently being deleted — shows a spinner on that note. */ deletingNoteId?: number | string | null; /** Message shown when `timelineEvents` is empty. Defaults to "No timeline events to display". */ emptyText?: ReactNode; } /** @deprecated Shape of the dead `events` prop — Timeline renders `TimelineEvents` (plural). */ export interface TimelineEvent { title: string; description: string; time: string; } /** A file attached to a manual timeline note; size is captured but never read or displayed. */ export interface TimelineNoteAttachment { /** Link target, opened in a new tab; clicks do not trigger the card's `onEventClick`. */ url: string; /** Link text; defaults to "attachment". */ name?: string; size?: number | null; } /** One manual note attached to a log entry; log_note_id doubles as the React key and the edit/delete target. */ export interface TimelineNote { /** Unique note id passed back to `onEditNote` and `onDeleteNote`. */ log_note_id: number; /** @deprecated Never read — the parent event already identifies the log. */ logs_id?: number; /** Bold note heading; omitted when empty. */ title?: string | null; /** Note body, rendered with line breaks preserved; omitted when empty. */ note?: string | null; /** Rendered as link chips under the body. */ attachments?: TimelineNoteAttachment[] | null; /** Epoch ms (number or digit string) or any `Date`-parseable string. */ created_at?: number | string | null; /** Underscores become spaces and each word is capitalised, e.g. `john_doe` → "John Doe". */ created_by?: string | null; } /** One timeline entry (despite the plural name) — the element type of the timelineEvents array Timeline actually renders. */ export interface TimelineEvents { /** Only used in the React key — duplicates are tolerated. */ logs_id: number; /** Shown as the capitalised status pill; "created"/"updated"/"delete(d)" (any case) pick the color and icon, anything else uses primary. */ title: string; /** @deprecated Never read, but required — keep passing it; do not delete. */ primary_id: number; /** @deprecated Never read, but required — `action_from` is the displayed source. */ is_from: string; /** Card heading; "Accounts" and "Notes" also select the fallback node icon. */ action_from: string; /** Shown as "by …" with underscores turned into spaces and words capitalised. */ action_by: string; /** An unparseable value drops the whole event from every render path — no fallback bucket, no error. */ created_at: number; /** Object or JSON string of `{ field: { original, updated } }`; renders a from→to diff, bad JSON renders none. */ changes: unknown; /** Manual notes listed under the card; editable and deletable through `onEditNote` and `onDeleteNote`. */ notes?: TimelineNote[] | null; /** Replaces the node icon derived from `title`/`action_from`. */ icon?: ReactNode; /** Free text or node shown under the card header, above the changes diff. */ description?: ReactNode; }