@forgeroute/ui 0.2.0 → 0.3.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/fesm2022/forgeroute-ui.mjs +25257 -12
- package/fesm2022/forgeroute-ui.mjs.map +1 -1
- package/package.json +4 -2
- package/src/_animations.scss +94 -0
- package/types/forgeroute-ui.d.ts +5410 -6
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"forgeroute-ui.mjs","names":[],"sources":["../../../projects/forgeroute-ui/src/lib/search-input/search-input.component.ts"],"sourcesContent":["import {\n Component, input, output, model,\n ChangeDetectionStrategy, inject, DestroyRef,\n} from '@angular/core';\nimport { Subject } from 'rxjs';\nimport { debounceTime, distinctUntilChanged } from 'rxjs/operators';\nimport { takeUntilDestroyed } from '@angular/core/rxjs-interop';\n\n@Component({\n selector: 'ui-search-input',\n standalone: true,\n changeDetection: ChangeDetectionStrategy.OnPush,\n template: `\n <div class=\"search-bar\" role=\"search\">\n <svg\n class=\"search-icon\"\n aria-hidden=\"true\"\n width=\"16\"\n height=\"16\"\n viewBox=\"0 0 24 24\"\n fill=\"none\"\n stroke=\"currentColor\"\n stroke-width=\"2\"\n stroke-linecap=\"round\"\n stroke-linejoin=\"round\">\n <circle cx=\"11\" cy=\"11\" r=\"8\"/>\n <line x1=\"21\" y1=\"21\" x2=\"16.65\" y2=\"16.65\"/>\n </svg>\n <input\n class=\"search-input\"\n type=\"search\"\n [placeholder]=\"placeholder()\"\n [attr.aria-label]=\"ariaLabel() || placeholder()\"\n [value]=\"value()\"\n (input)=\"onInput($any($event.target).value)\"\n autocomplete=\"off\"\n inputmode=\"search\"\n [attr.maxlength]=\"maxlength()\"\n data-testid=\"search-input\" />\n </div>\n `,\n styles: [`\n :host { display: block; }\n\n .search-bar {\n position: relative;\n display: flex;\n align-items: center;\n }\n\n .search-icon {\n position: absolute;\n left: 10px;\n color: #94a3b8;\n pointer-events: none;\n }\n\n .search-input {\n width: 100%;\n padding: 8px 10px 8px 34px;\n border: 1px solid #e2e8f0;\n border-radius: 6px;\n font-size: 0.875rem;\n background: #fff;\n color: #1e293b;\n outline: none;\n min-width: 380px;\n font-family: inherit;\n transition: border-color 0.15s;\n }\n\n .search-input:focus {\n border-color: #1e293b;\n box-shadow: 0 0 0 3px rgba(30, 41, 59, 0.08);\n }\n\n .search-input::placeholder { color: #94a3b8; }\n\n @media (max-width: 768px) {\n .search-input { min-width: 180px; }\n }\n\n @media (max-width: 480px) {\n .search-input { min-width: 0; width: 100%; font-size: 16px; }\n }\n `],\n})\nexport class SearchInputComponent {\n readonly placeholder = input('Search...');\n readonly ariaLabel = input('');\n readonly value = model('');\n readonly maxlength = input(200);\n\n /** Emits the debounced search value after 300ms of inactivity. Use this to trigger API calls. */\n readonly searchChange = output<string>();\n\n private readonly input$ = new Subject<string>();\n\n constructor() {\n this.input$.pipe(\n debounceTime(300),\n distinctUntilChanged(),\n takeUntilDestroyed(inject(DestroyRef)),\n ).subscribe(val => this.searchChange.emit(val));\n }\n\n onInput(val: string): void {\n this.value.set(val);\n this.input$.next(val);\n }\n}\n"],"mappings":";;;;;AAuFA,IAAa,uBAAb,MAAa,qBAAoB;CAW/B,cAAA;EAVS,KAAA,cAAc,MAAM,aAAY,GAAA,YAAA,CAAA,EAAA,WAAA,cAAA,CAAA,+BAAA,CAAA,CAAA;EAChC,KAAA,YAAY,MAAM,IAAG,GAAA,YAAA,CAAA,EAAA,WAAA,YAAA,CAAA,+BAAA,CAAA,CAAA;EACrB,KAAA,QAAQ,MAAM,IAAG,GAAA,YAAA,CAAA,EAAA,WAAA,QAAA,CAAA,+BAAA,CAAA,CAAA;EACjB,KAAA,YAAY,MAAM,KAAI,GAAA,YAAA,CAAA,EAAA,WAAA,YAAA,CAAA,+BAAA,CAAA,CAAA;EAGtB,KAAA,eAAe,OAAM;EAEb,KAAA,SAAS,IAAI,QAAO;EAGnC,KAAK,OAAO,KACV,aAAa,GAAG,GAChB,qBAAoB,GACpB,mBAAmB,OAAO,UAAU,CAAC,CAAC,CACvC,CAAC,WAAU,QAAO,KAAK,aAAa,KAAK,GAAG,CAAC;CAChD;CAEA,QAAQ,KAAW;EACjB,KAAK,MAAM,IAAI,GAAG;EAClB,KAAK,OAAO,KAAK,GAAG;CACtB;CAtBW;EAAA,KAAA,OAAA,GAAA,mBAAA;GAAA,YAAA;GAAA,SAAA;GAAA,UAAA;GAAA,MAAA;GAAoB,MAAA,CAAA;GAAA,QAAA,GAAA,gBAAA;EAAA,CAAA;CAAA;CAApB;EAAA,KAAA,OAAA,GAAA,qBAAA;GAAA,YAAA;GAAA,SAAA;GAAA,MAAA;GAAoB,cAAA;GAAA,UAAA;GAAA,QAAA;IAAA,aAAA;KAAA,mBAAA;KAAA,YAAA;KAAA,UAAA;KAAA,YAAA;KAAA,mBAAA;IAAA;IAAA,WAAA;KAAA,mBAAA;KAAA,YAAA;KAAA,UAAA;KAAA,YAAA;KAAA,mBAAA;IAAA;IAAA,OAAA;KAAA,mBAAA;KAAA,YAAA;KAAA,UAAA;KAAA,YAAA;KAAA,mBAAA;IAAA;IAAA,WAAA;KAAA,mBAAA;KAAA,YAAA;KAAA,UAAA;KAAA,YAAA;KAAA,mBAAA;IAAA;GAAA;GAAA,SAAA;IAAA,OAAA;IAAA,cAAA;GAAA;GAAA,UAAA;GAAA,UA3ErB;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA4BT,UAAA;GAAA,QAAA,CAAA,knBAAA;GAAA,iBAAA,GAAA,wBAAA;EAAA,CAAA;CAAA;;AA+CU,GAAA,yBAAA;CAAA,YAAA;CAAA,SAAA;CAAA,UAAA;CAAA,MAAA;CAAoB,YAAA,CAAA;EA/EhC,MAAA;EACW,MAAA,CAAA;GAAA,UAAA;GAAiB,YACf;GAAI,iBACC,wBAAwB;GAAM,UACrC;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA4BT,QAAA,CAAA,knBAAA;EAAA,CAAA"}
|
|
1
|
+
{"version":3,"file":"forgeroute-ui.mjs","names":[],"sources":["../../../projects/forgeroute-ui/src/lib/config/forgeroute.config.ts","../../../projects/forgeroute-ui/src/lib/data-table/data-table.types.ts","../../../projects/forgeroute-ui/src/lib/search-input/search-input.component.ts","../../../projects/forgeroute-ui/src/lib/pagination/pagination.component.ts","../../../projects/forgeroute-ui/src/lib/quick-filter-tabs/quick-filter-tabs.component.ts","../../../projects/forgeroute-ui/src/lib/column-filter/column-filter.component.ts","../../../projects/forgeroute-ui/src/lib/badge/badge.component.ts","../../../projects/forgeroute-ui/src/lib/avatar/avatar.component.ts","../../../projects/forgeroute-ui/src/lib/more-menu/more-menu.component.ts","../../../projects/forgeroute-ui/src/lib/utils/overlay-stack.ts","../../../projects/forgeroute-ui/src/lib/utils/overlay-focus.ts","../../../projects/forgeroute-ui/src/lib/utils/visual-viewport.ts","../../../projects/forgeroute-ui/src/lib/modal/modal.component.ts","../../../projects/forgeroute-ui/src/lib/checkbox/checkbox.component.ts","../../../projects/forgeroute-ui/src/lib/button/button.component.ts","../../../projects/forgeroute-ui/src/lib/utils/field-validation.ts","../../../projects/forgeroute-ui/src/lib/text-input/text-input.component.ts","../../../projects/forgeroute-ui/src/lib/table-toolbar/table-toolbar.component.ts","../../../projects/forgeroute-ui/src/lib/bulk-eligible.ts","../../../projects/forgeroute-ui/src/lib/skeleton/skeleton.component.ts","../../../projects/forgeroute-ui/src/lib/progress-bar/progress-bar.component.ts","../../../projects/forgeroute-ui/src/lib/select/select.component.ts","../../../projects/forgeroute-ui/src/lib/toast/toast.service.ts","../../../projects/forgeroute-ui/src/lib/toast/toast-host-registry.ts","../../../projects/forgeroute-ui/src/lib/toast/toast.component.ts","../../../projects/forgeroute-ui/src/lib/utils/human-readable-message.ts","../../../projects/forgeroute-ui/src/lib/import-export/import-export.component.ts","../../../projects/forgeroute-ui/src/lib/confirm/confirm.service.ts","../../../projects/forgeroute-ui/src/lib/confirm/confirm-outlet.component.ts","../../../projects/forgeroute-ui/src/lib/empty-state/empty-state.component.ts","../../../projects/forgeroute-ui/src/lib/error-state/error-state.component.ts","../../../projects/forgeroute-ui/src/lib/data-table/data-table.component.ts","../../../projects/forgeroute-ui/src/lib/side-panel/side-panel.component.ts","../../../projects/forgeroute-ui/src/lib/toggle-badge/toggle-badge.component.ts","../../../projects/forgeroute-ui/src/lib/pill-tabs/pill-tabs.component.ts","../../../projects/forgeroute-ui/src/lib/contact-picker/contact-picker.component.ts","../../../projects/forgeroute-ui/src/lib/error/error.component.ts","../../../projects/forgeroute-ui/src/lib/video-picker/video-picker.component.ts","../../../projects/forgeroute-ui/src/lib/template-picker/template-picker.component.ts","../../../projects/forgeroute-ui/src/lib/combobox/combobox.component.ts","../../../projects/forgeroute-ui/src/lib/error-handler.ts","../../../projects/forgeroute-ui/src/lib/textarea-input/textarea-input.component.ts","../../../projects/forgeroute-ui/src/lib/datetime-input/datetime-input.component.ts","../../../projects/forgeroute-ui/src/lib/form-section/form-section.component.ts","../../../projects/forgeroute-ui/src/lib/chip/chip.component.ts","../../../projects/forgeroute-ui/src/lib/chip/chip-group.component.ts","../../../projects/forgeroute-ui/src/lib/document-send-options/document-send-options.component.ts","../../../projects/forgeroute-ui/src/lib/send-message-panel/send-documents.ts","../../../projects/forgeroute-ui/src/lib/send-message-panel/merge-tags.ts","../../../projects/forgeroute-ui/src/lib/send-message-panel/send-email.component.ts","../../../projects/forgeroute-ui/src/lib/send-message-panel/send-text.component.ts","../../../projects/forgeroute-ui/src/lib/send-message-panel/send-message-panel.component.ts","../../../projects/forgeroute-ui/src/lib/bulk-message-panel/bulk-message-panel.component.ts","../../../projects/forgeroute-ui/src/lib/file-input/file-input.component.ts","../../../projects/forgeroute-ui/src/lib/date-input/date-input.component.ts","../../../projects/forgeroute-ui/src/lib/time-input/time-input.component.ts","../../../projects/forgeroute-ui/src/lib/multi-select/multi-select.component.ts","../../../projects/forgeroute-ui/src/lib/discount-selector/discount-selector.component.ts","../../../projects/forgeroute-ui/src/lib/dropdown/dropdown.component.ts","../../../projects/forgeroute-ui/src/lib/form-row/form-row.component.ts","../../../projects/forgeroute-ui/src/lib/list-item/list-item.component.ts","../../../projects/forgeroute-ui/src/lib/qr-code/qr-encoder.ts","../../../projects/forgeroute-ui/src/lib/qr-code/qr-code.component.ts","../../../projects/forgeroute-ui/src/lib/panel-info-list/panel-info-list.component.ts","../../../projects/forgeroute-ui/src/lib/utils/us-states.ts","../../../projects/forgeroute-ui/src/lib/app-shell/app-shell.component.ts","../../../projects/forgeroute-ui/src/lib/alert/alert.component.ts","../../../projects/forgeroute-ui/src/lib/hint/hint.component.ts","../../../projects/forgeroute-ui/src/lib/image-preview/image-preview.component.ts","../../../projects/forgeroute-ui/src/lib/kpi-card/kpi-card.component.ts","../../../projects/forgeroute-ui/src/lib/date-range-picker/date-range-picker.component.ts","../../../projects/forgeroute-ui/src/lib/rich-text-editor/rich-text-sanitizer.ts","../../../projects/forgeroute-ui/src/lib/rich-text-editor/rich-text-editor.component.ts"],"sourcesContent":["import { InjectionToken } from '@angular/core';\n\nexport interface ForgerouteConfig {\n baseUrl: string;\n}\n\nexport const FORGEROUTE_CONFIG = new InjectionToken<ForgerouteConfig>('FORGEROUTE_CONFIG');\n","import { InjectionToken, WritableSignal } from '@angular/core';\n\nexport type SortDir = 'asc' | 'desc';\n\n/**\n * Shared signal provided at the DataTableComponent level.\n * Holds the ID of the currently-open dropdown. When any dropdown\n * opens it sets this to its own ID — all others react and close.\n */\nexport const DROPDOWN_GROUP = new InjectionToken<WritableSignal<string>>('DROPDOWN_GROUP');\n\n// ── Badge ──────────────────────────────────────────────────────\nexport type BadgeVariant =\n | 'green' | 'blue' | 'red' | 'amber' | 'purple' | 'gray' | 'teal' | 'pink'\n | 'indigo' | 'orange' | 'cyan' | 'lime' | 'rose' | 'slate'\n | 'emerald' | 'sky' | 'fuchsia' | 'yellow' | 'violet' | 'stone'\n | 'mint' | 'steel' | 'bronze' | 'plum' | 'sage' | 'coral' | 'sand' | 'ocean' | 'pewter'\n | 'gold' | 'jade' | 'sienna' | 'mauve' | 'walnut' | 'navy';\n\nexport const BADGE_VARIANT_STYLES: Record<BadgeVariant, { bg: string; color: string }> = {\n green: { bg: '#dcfce7', color: '#166534' },\n blue: { bg: '#dbeafe', color: '#1e40af' },\n red: { bg: '#fee2e2', color: '#991b1b' },\n amber: { bg: '#fef3c7', color: '#92400e' },\n purple: { bg: '#fff1f2', color: '#881337' },\n gray: { bg: '#f1f5f9', color: '#475569' },\n teal: { bg: '#ccfbf1', color: '#115e59' },\n pink: { bg: '#fce7f3', color: '#be185d' },\n indigo: { bg: '#bfdbfe', color: '#1e3a8a' },\n orange: { bg: '#ffedd5', color: '#9a3412' },\n cyan: { bg: '#cffafe', color: '#155e75' },\n lime: { bg: '#ecfccb', color: '#3f6212' },\n rose: { bg: '#ffe4e6', color: '#9f1239' },\n slate: { bg: '#e2e8f0', color: '#334155' },\n emerald: { bg: '#d1fae5', color: '#065f46' },\n sky: { bg: '#e0f2fe', color: '#0c4a6e' },\n fuchsia: { bg: '#fdf4ff', color: '#a21caf' },\n yellow: { bg: '#fef9c3', color: '#854d0e' },\n violet: { bg: '#fefce8', color: '#713f12' },\n stone: { bg: '#f5f5f4', color: '#57534e' },\n mint: { bg: '#d1fae5', color: '#047857' },\n steel: { bg: '#cbd5e1', color: '#1e293b' },\n bronze: { bg: '#fde68a', color: '#78350f' },\n plum: { bg: '#f5f5f4', color: '#44403c' },\n sage: { bg: '#d9f99d', color: '#4d7c0f' },\n coral: { bg: '#fecdd3', color: '#be123c' },\n sand: { bg: '#e7e5e4', color: '#44403c' },\n ocean: { bg: '#bae6fd', color: '#075985' },\n pewter: { bg: '#d4d4d8', color: '#3f3f46' },\n gold: { bg: '#fbbf24', color: '#78350f' },\n jade: { bg: '#6ee7b7', color: '#064e3b' },\n sienna: { bg: '#f0cfb8', color: '#7c2d12' },\n mauve: { bg: '#f0d9e0', color: '#6b2d3e' },\n walnut: { bg: '#d4a27a', color: '#1c0f09' },\n navy: { bg: '#93c5fd', color: '#172554' },\n};\n\n/**\n * Global semantic map — same label always gets the same color across every\n * table in the app, regardless of which page or column it appears in.\n */\nexport const SEMANTIC_BADGE_MAP: Readonly<Record<string, BadgeVariant>> = {\n // Active/Inactive states\n 'Active': 'green',\n 'Inactive': 'gray',\n 'Off': 'gray',\n 'Disabled': 'red',\n 'Paused': 'amber',\n // Workflow\n 'Pending': 'blue',\n 'In Progress': 'cyan',\n 'Completed': 'emerald',\n 'Resolved': 'green',\n 'Draft': 'slate',\n 'Voided': 'slate',\n // Audit\n 'Created': 'green',\n 'Updated': 'blue',\n 'Deleted': 'red',\n 'Login': 'amber',\n 'Logout': 'gray',\n 'Exported': 'pink',\n 'Imported': 'orange',\n // Comms delivery\n 'Sent': 'blue',\n 'Delivered': 'green',\n 'Opened': 'teal',\n 'Clicked': 'emerald',\n 'Bounced': 'amber',\n 'Scheduled': 'sky',\n // Terminal/negative\n 'Failed': 'red',\n 'Cancelled': 'gray',\n 'Expired': 'orange',\n 'Declined': 'red',\n 'Signed': 'emerald',\n 'Paid': 'green',\n // Priority\n 'High': 'red',\n 'Medium': 'amber',\n 'Low': 'slate',\n // Direction\n 'Inbound': 'cyan',\n 'Outbound': 'teal',\n // Boolean/consent\n 'Yes': 'green',\n 'No': 'gray',\n 'Opted In': 'green',\n 'Opted Out': 'red',\n // Channel\n 'Text': 'indigo',\n 'Email': 'sky',\n};\n\n/** Visually distinct variants used for auto-color assignment. */\nconst AUTO_COLOR_PALETTE: BadgeVariant[] = [\n 'green', 'blue', 'teal', 'pink', 'sky', 'ocean', 'cyan', 'rose',\n 'amber', 'purple', 'orange', 'sage', 'indigo', 'emerald', 'violet',\n 'lime', 'steel', 'coral', 'plum', 'slate', 'fuchsia', 'jade', 'navy',\n 'sienna', 'mauve', 'gold', 'bronze', 'walnut',\n];\n\n/**\n * Deterministic color from a string value — same value always gets the\n * same color, and adjacent values in the palette are visually distinct.\n */\nexport function autoVariant(value: string): BadgeVariant {\n let hash = 0;\n for (let i = 0; i < value.length; i++) {\n hash = (hash * 31 + value.charCodeAt(i)) & 0xffff;\n }\n return AUTO_COLOR_PALETTE[Math.abs(hash) % AUTO_COLOR_PALETTE.length];\n}\n\nexport interface BadgeConfig {\n label: string;\n variant: BadgeVariant;\n}\n\n// ── Toolbar actions ────────────────────────────────────────────\nexport interface ToolbarAction {\n type: string;\n label: string;\n /** SVG path data for an icon (optional) */\n icon?: string;\n /** 'primary' = blue standalone button, 'secondary' = gray outlined standalone button, omit = inside More Actions dropdown */\n variant?: 'primary' | 'secondary';\n /** Optional count badge shown on the button */\n badge?: number;\n /** Hide this action entirely */\n hidden?: boolean;\n /** Only shown when the table's `permission` is 'full' (e.g. \"Add …\", bulk mutations). */\n requiresFull?: boolean;\n /**\n * Keep this action when the table is empty and unfiltered. By default only `variant: 'primary'` buttons survive that\n * state; set this on secondary buttons and More Actions items that must stay reachable on an empty table (e.g. an\n * \"Import\" or \"Invite\" that is how the first record gets created). A kept More Actions item brings the More Actions\n * menu back (with its usual items).\n */\n keepWhenEmpty?: boolean;\n /**\n * Standalone toggle buttons (e.g. \"Assigned to Me\"): true / false renders `aria-pressed` so screen readers announce\n * the on / off state. Leave unset for ordinary buttons (no attribute).\n */\n pressed?: boolean;\n}\n\n// ── Confirm config (used by RowAction.modal and ConfirmService) ─\nexport interface ConfirmConfig {\n title: string;\n /** Message text. In actionsFn context item is already in scope so a plain string is fine.\n * Pass a function when defining actions as a static array outside actionsFn. */\n message: string | ((item: any) => string);\n confirmLabel?: string; // default 'Confirm'\n cancelLabel?: string; // default 'Cancel'\n variant?: 'danger' | 'primary' | 'warning'; // default 'danger'; 'warning' makes the confirm button amber\n /** Bullet list shown under the message (consequences, what is NOT affected, undoable or not). Plain text only (never HTML).\n * Rendered by the confirm dialog (`data-testid=\"confirm-details\"`, aria-label \"What will happen\") and by the built-in row delete. */\n details?: string[];\n}\n\n// ── Row actions (for actions cell type) ───────────────────────\nexport interface RowAction {\n type: string;\n label: string;\n /** SVG path data string (the <path> or <line> etc. content only, no outer <svg>) */\n icon?: string;\n variant?: 'default' | 'danger';\n hidden?: (item: any) => boolean;\n disabled?: (item: any) => boolean;\n /** How the table handles this action when clicked.\n * 'modal' — table shows a built-in confirm modal; onAction is called only if confirmed.\n * 'panel' — table emits (panelActionClick); host component opens the right panel or form modal.\n * Omit — onAction is called directly (navigate, toggle, call, etc.) */\n opens?: 'modal' | 'panel';\n /** Required when opens === 'modal'. Drives the confirm dialog shown by the table. */\n modal?: ConfirmConfig;\n /** Only shown when the table's `permission` is 'full' (edit, delete, any mutation).\n * The built-in `type: 'delete'` action is always treated as requiring full permission. */\n requiresFull?: boolean;\n}\n\n/** Page-level permission passed to `<ui-data-table [permission]>`. Anything other than 'full' is read-only\n * (apps use values like 'read-only' / 'hidden'). */\nexport type TablePermission = 'full' | 'read' | 'read-only' | 'none' | 'hidden';\n\n// ── Cell types ────────────────────────────────────────────────\nexport type CellType =\n | 'text' // default — renders item[col.key] as string\n | 'badge' // renders a colored badge pill\n | 'date' // formats a date value\n | 'phone' // formats a phone number\n | 'number' // right-aligns header + cell; renders via render() or item[col.key]\n | 'avatar-name' // avatar circle + full name\n | 'days-ago' // \"Xd ago\" / \"Never\" with overdue styling\n | 'progress' // progress bar + label, driven by progressFn\n | 'tags' // first tag badge + \"+N more\"\n | 'image' // thumbnail driven by imageFn (placeholder icon when there is no URL / it fails to load)\n | 'actions'; // more-menu dropdown\n\n// ── Column definition ─────────────────────────────────────────\nexport interface ColumnDef {\n key: string;\n label: string;\n sortable?: boolean;\n defaultVisible?: boolean;\n /** If true, this column's label appears in the search input placeholder when the column is visible. */\n searchable?: boolean;\n /** Values a bulk \"Change <label>\" may set. When omitted the options come from /filter-counts.\n * Use it when only some values are user-settable or when a valid value may currently have no rows. */\n bulkOptions?: string[];\n align?: 'left' | 'right' | 'center';\n headerAlign?: 'left' | 'right' | 'center';\n\n /** How to render cell content. Defaults to 'text'. */\n cellType?: CellType;\n\n // ── badge ──\n /**\n * Maps a cell value (or full item) to a { label, variant } config.\n * When omitted, auto-color is applied using the value string.\n * Return null to render a plain '—' instead of a badge (e.g. for an empty value).\n */\n badgeFn?: (value: any, item?: any) => BadgeConfig | null;\n\n // ── date ──\n /** Date format string. Default: 'MMM d, y' */\n dateFormat?: string;\n\n // ── avatar-name ──\n /** Returns the display name for the row. Default: item[col.key] */\n nameFn?: (item: any) => string;\n /** Returns initials for the avatar. Default: first char of nameFn result */\n initialsFn?: (item: any) => string;\n\n // ── days-ago ──\n /** Key in item holding the days-since count. Defaults to col.key. */\n daysKey?: string;\n /** Days after which the cell is styled as overdue. Default: 30 */\n overdueThreshold?: number;\n\n // ── progress ──\n /**\n * Maps a row to a progress bar. Return null to render a plain '—' (e.g. nothing to track).\n * `label` is the text beside the bar (e.g. \"3/5\"); `tooltip` is the hover text.\n */\n progressFn?: (item: any) => { value: number; max: number; label?: string; variant?: 'blue' | 'green' | 'amber' | 'red' | 'slate'; tooltip?: string } | null;\n\n // ── tags ──\n /** Key in item holding the tags array. Defaults to col.key.\n * Expected shape: { name: string; variant?: BadgeVariant }[] */\n tagsKey?: string;\n\n // ── image ──\n /** Returns the image URL for the row. A falsy value (null / undefined / '') shows the placeholder icon. */\n imageFn?: (item: any) => string | null | undefined;\n /** Alt text for the row's image. When omitted the image is decorative (`alt=\"\"`). */\n imageAlt?: (item: any) => string;\n /** 'circle' (default) or 'rounded' (3:2 rounded rectangle). */\n imageShape?: 'circle' | 'rounded';\n /** 'md' (default) or 'sm'. circle: 40px / 28px; rounded: 48x32 / 36x24. */\n imageSize?: 'sm' | 'md';\n /** Neutral icon shown when there is no URL or the image fails to load. Default: 'image'. */\n imagePlaceholder?: 'person' | 'image';\n\n // ── actions ──\n /** Returns the list of actions for a given row item. */\n actionsFn?: (item: any) => RowAction[];\n /** Called when the user clicks an action. */\n onAction?: (type: string, item: any) => void;\n\n // ── fallback ──\n /** Custom plain-text render function. Only used for 'text' type. */\n render?: (item: any) => string;\n\n // ── mobile ──\n /** Hide this column in the card-view detail rows on mobile (≤640px).\n * The title (avatar-name) and badge columns are always shown. */\n mobileHidden?: boolean;\n}\n\n// ── Quick filter tab ──────────────────────────────────────────\nexport interface QuickFilterTab {\n value: string;\n label: string;\n /** When true, the tab is shown as informational only and cannot be clicked. */\n disabled?: boolean;\n}\n\n// ── Column filter ─────────────────────────────────────────────\nexport interface FilterOption {\n value: string;\n label: string;\n color?: string;\n count?: number;\n}\n\n// ── Saved view ────────────────────────────────────────────────\nexport interface SavedView {\n id: string;\n name: string;\n isSystem?: boolean;\n /** Active quick-filter tab value at time of save */\n quickFilter?: string | null;\n /** Active sort column key at time of save */\n sortField?: string | null;\n /** Sort direction at time of save */\n sortDir?: string | null;\n /** Visible column keys at time of save (empty = use defaultVisible) */\n visibleColumns?: string[];\n /**\n * Active column filters at time of save: column key → comma-joined selected values (the same wire format the\n * table sends as query params). `undefined`/`null` (views saved before this existed, or a backend that doesn't\n * store it) leaves the current column filters untouched on load; `{}` clears them.\n */\n columnFilters?: Record<string, string> | null;\n}\n\n// ── Bulk actions ──────────────────────────────────────────────\n\n/**\n * Declares a bulk action available in the table's selection bar.\n * Options for select-based actions are derived automatically from the table's\n * column configuration (columnFilters → quickFilterTabs → filterCounts → distinct values).\n */\n/**\n * One `bulkUpdateOptions` entry:\n * - `'delete'` — the built-in bulk delete;\n * - a column key (e.g. `'status'`) — \"Change <column label>\" with options from /filter-counts;\n * - `{ field, exclude }` — the same, minus values users can't set by hand (e.g. a status only the\n * system sets). Excluded values are left out of the bulk select only — the column filter, quick-filter\n * tabs and counts still show them. Matching ignores case, spaces, dashes and underscores.\n *\n * readonly bulkUpdateOptions: BulkUpdateOption[] = [{ field: 'status', exclude: ['EXPIRED'] }, 'delete'];\n * - `{ field, allowedFrom }` — the status workflow: a map of TARGET value -> the values a row may currently\n * have to move to it, e.g. `{ SENT: ['DRAFT'], VOIDED: ['DRAFT', 'SENT'] }`. The bulk select then offers only\n * targets that at least one selected row can legally move to (a target with no entry is not selectable), and rows\n * whose current value is not in the target's list are skipped (counted, never sent). Without `allowedFrom` every\n * row can move to any value it does not already have. Combine with `exclude` freely (exact, case-sensitive values).\n *\n * { field: 'status', allowedFrom: { SENT: ['DRAFT'], VOIDED: ['DRAFT', 'SENT'] } }\n */\nexport type BulkUpdateOption<T = any> = string | {\n field: string;\n exclude?: string[];\n allowedFrom?: Record<string, string[]>;\n /**\n * Host-written confirm copy (see `BulkConfirmFn`): one function for every target value, or a map target value -> function\n * (a target without an entry keeps the library's default wording). For the reserved delete key use\n * `{ field: 'delete', confirm }`. Omit it and the confirm keeps today's default wording.\n */\n confirm?: BulkConfirmFn<T> | Record<string, BulkConfirmFn<T>>;\n /**\n * Bulk Delete only (`{ field: 'delete', deletable }`): true when the server will accept deleting this row. Keep it identical\n * to the backend rule. Only deletable rows are counted in the confirm, sent to `/bulk/delete` and shown as deleted; the others\n * are skipped (\"N selected rows can't be deleted and will be skipped.\"). The Delete button is disabled (with a reason) when\n * no selected row is deletable. Omit it and every selected row is sent, as before.\n *\n * { field: 'delete', deletable: (row: Invoice) => row.status === 'DRAFT' }\n */\n deletable?: (row: T) => boolean;\n};\n\n/** What a bulk confirm function receives. For bulk Delete: `targetValue`/`targetLabel` are '', `columnLabel` is 'Delete', skipped counts are 0. */\nexport interface BulkConfirmContext<T = any> {\n /** Rows that WILL be sent (eligible). For Delete: the selected count. */\n count: number;\n /** Selected rows skipped because they already hold the target value. */\n skippedAlready: number;\n /** Selected rows skipped because `allowedFrom` does not allow the move. */\n skippedBlocked: number;\n /** Everything the user selected (`count + skippedAlready + skippedBlocked`). */\n selectedCount: number;\n /** Singular / plural item label (the table's `itemLabel` / `itemLabelPlural`). */\n itemLabel: string;\n itemLabelPlural: string;\n /** Label of the column being changed (\"Status\"). */\n columnLabel: string;\n /** Raw target value (\"APPROVED\") and its display label (\"Approved\"). */\n targetValue: string;\n targetLabel: string;\n /** The eligible row objects (name records, show before -> after). Only rows on the loaded page are available. */\n rows: T[];\n /** The selected rows that will be skipped. */\n skippedRows: T[];\n}\n\n/** Host copy for one bulk confirm. Plain text only (never HTML); `\\n` in `message` shows as a line break. */\nexport interface BulkConfirmCopy {\n /** Default: the action label (\"Change Status\") / \"Delete Selected\". */\n title?: string;\n message: string;\n /** Rendered as a bullet list under the message (consequences, what is NOT affected, undoable or not). */\n details?: string[];\n /** Default: the action label / \"Delete\". */\n confirmLabel?: string;\n /** Default: 'primary' for a field change, 'danger' for Delete. 'danger' makes the confirm button red. */\n variant?: 'primary' | 'danger' | 'warning';\n /** By default the library appends its standard \"N selected rows ... will be skipped.\" sentence (change actions); set true to write it yourself. */\n omitSkippedNote?: boolean;\n}\n\nexport type BulkConfirmFn<T = any> = (ctx: BulkConfirmContext<T>) => BulkConfirmCopy | string;\n\nexport interface BulkActionDef {\n /** Action key — passed through to the (bulkAction) event. */\n type: string;\n /** Button label shown in the bulk bar. */\n label: string;\n /** Button style. Default: 'secondary'. Use 'danger' for destructive actions. */\n variant?: 'secondary' | 'danger';\n /** Values left out of this field's bulk select (from a `{ field, exclude }` bulkUpdateOptions entry). */\n exclude?: string[];\n /** Workflow rules (from `{ field, allowedFrom }`): target value -> source values a row may move from. */\n allowedFrom?: Record<string, string[]>;\n /** Host confirm copy (from `{ field, confirm }`). */\n confirm?: BulkConfirmFn | Record<string, BulkConfirmFn>;\n /** Bulk Delete only: which rows the server will delete (from `{ field: 'delete', deletable }`). */\n deletable?: (row: any) => boolean;\n /**\n * The item field this action targets (e.g. 'status', 'type').\n * When set the table shows an inline select populated from the column's option sources.\n * Omit for direct actions like delete that need no value selection.\n */\n field?: string;\n}\n\n/** Emitted by (bulkAction) after the user selects a value and clicks Apply. */\nexport interface BulkActionEvent {\n type: string;\n ids: string[];\n value?: string;\n skippedCount: number;\n}\n\n// ── Page response from backend ────────────────────────────────\nexport interface PageResponse<T> {\n content: T[];\n totalElements: number;\n totalPages: number;\n number: number;\n size: number;\n}\n","import {\n Component, input, output, model, signal, effect, untracked,\n ChangeDetectionStrategy, inject, DestroyRef,\n} from '@angular/core';\nimport { HttpClient, HttpParams } from '@angular/common/http';\nimport { toObservable } from '@angular/core/rxjs-interop';\nimport { debounceTime, distinctUntilChanged, skip, switchMap } from 'rxjs/operators';\nimport { of } from 'rxjs';\nimport { takeUntilDestroyed } from '@angular/core/rxjs-interop';\nimport { FORGEROUTE_CONFIG } from '../config/forgeroute.config';\n\n@Component({\n selector: 'ui-search-input',\n standalone: true,\n changeDetection: ChangeDetectionStrategy.OnPush,\n template: `\n <div class=\"search-bar\" role=\"search\">\n <svg\n class=\"search-icon\"\n aria-hidden=\"true\"\n width=\"16\"\n height=\"16\"\n viewBox=\"0 0 24 24\"\n fill=\"none\"\n stroke=\"currentColor\"\n stroke-width=\"2\"\n stroke-linecap=\"round\"\n stroke-linejoin=\"round\">\n <circle cx=\"11\" cy=\"11\" r=\"8\"/>\n <line x1=\"21\" y1=\"21\" x2=\"16.65\" y2=\"16.65\"/>\n </svg>\n <input\n class=\"search-input\"\n type=\"search\"\n [placeholder]=\"placeholder()\"\n [attr.aria-label]=\"ariaLabel() || placeholder()\"\n [value]=\"value()\"\n (input)=\"onInput($any($event.target).value)\"\n autocomplete=\"off\"\n inputmode=\"search\"\n [attr.maxlength]=\"maxlength()\"\n [attr.aria-busy]=\"loading() ? 'true' : null\"\n [class.search-input-loading]=\"loading()\"\n data-testid=\"search-input\" />\n <span class=\"search-sr-only\" role=\"status\" aria-live=\"polite\" data-testid=\"search-status\">{{ loading() ? 'Searching' : '' }}</span>\n @if (loading()) {\n <span class=\"search-spinner\" aria-hidden=\"true\" data-testid=\"search-spinner\">\n <span class=\"search-spinner-ring\"></span>\n </span>\n }\n </div>\n `,\n styles: [`\n :host { display: block; flex: 0 1 325px; min-width: 0; }\n\n .search-bar {\n position: relative;\n display: flex;\n align-items: center;\n width: 100%;\n }\n\n .search-icon {\n position: absolute;\n left: 10px;\n color: #94a3b8;\n pointer-events: none;\n }\n\n .search-input {\n width: 100%;\n padding: 9px 10px 9px 34px;\n border: 1px solid #e2e8f0;\n border-radius: 8px;\n font-size: 0.875rem;\n background: #fff;\n color: #1e293b;\n outline: none;\n min-width: 0;\n font-family: inherit;\n transition: border-color 0.15s, box-shadow 0.15s;\n }\n\n .search-input:focus {\n border-color: #3b82f6;\n box-shadow: 0 0 0 3px rgba(59, 130, 246, 0.12);\n }\n\n .search-input::placeholder { color: #94a3b8; }\n\n /* Loading: the spinner takes the right edge, so hide the native clear (x) button meanwhile. */\n .search-input-loading { padding-right: 34px; }\n .search-input-loading::-webkit-search-cancel-button { display: none; }\n .search-spinner {\n position: absolute; right: 10px; top: 50%; margin-top: -8px;\n width: 16px; height: 16px; pointer-events: none;\n }\n .search-spinner-ring {\n display: block; width: 16px; height: 16px; box-sizing: border-box;\n border: 2px solid #e2e8f0; border-top-color: #64748b; border-radius: 50%;\n animation: search-spin 0.6s linear infinite;\n }\n .search-sr-only {\n position: absolute; width: 1px; height: 1px; margin: -1px; padding: 0;\n overflow: hidden; clip: rect(0 0 0 0); white-space: nowrap; border: 0;\n }\n @keyframes search-spin { to { transform: rotate(360deg); } }\n\n /* Touch targets: at least 44px tall on touch devices and phones. */\n @media (pointer: coarse), (max-width: 480px) {\n .search-input { min-height: 44px; }\n }\n\n /* Tablet: allow narrower search so toolbar buttons have room */\n @media (max-width: 1024px) {\n :host { flex: 0 1 280px; }\n }\n\n /* Phone landscape / small tablet: compress further */\n @media (max-width: 768px) {\n :host { flex: 0 1 240px; }\n }\n\n /* Phone: grow to fill the full row so tabs wrap cleanly below */\n @media (max-width: 640px) {\n :host { flex: 1 1 auto; }\n /* 16px prevents iOS auto-zoom on input focus */\n .search-input { font-size: 16px; }\n }\n `],\n})\nexport class SearchInputComponent<T> {\n readonly placeholder = input('Search...');\n readonly ariaLabel = input('');\n readonly value = model('');\n readonly maxlength = input(200);\n /** When set, the component fetches from this endpoint and emits `filteredData`. */\n readonly endpoint = input('');\n /** Shows a small spinner at the right edge (and `aria-busy`) while a search is being fetched. */\n readonly loading = input(false);\n\n /** Always emits the debounced search value regardless of endpoint. */\n readonly searchChange = output<string>();\n /** Emits backend results when `endpoint` is provided. */\n readonly filteredData = output<T[]>();\n\n private readonly config = inject(FORGEROUTE_CONFIG, { optional: true } as any);\n private readonly http = inject(HttpClient);\n private readonly destroyRef = inject(DestroyRef);\n private readonly query = signal('');\n\n constructor() {\n // Keep the debounced query in step when the parent changes `value` (e.g. a table's\n // \"Clear filters\" resets it to ''), so the box shows the reset value and typing the\n // previous term again still emits instead of being swallowed by distinctUntilChanged.\n effect(() => {\n const v = this.value();\n untracked(() => { if (v !== this.query()) this.query.set(v); });\n });\n\n toObservable(this.query).pipe(\n skip(1), // skip initial emission to prevent spurious load on mount\n debounceTime(300),\n distinctUntilChanged(),\n switchMap(query => {\n this.searchChange.emit(query);\n if (!this.endpoint() || !this.config) return of(null);\n const params = new HttpParams().set('search', query);\n return this.http.get<any>(`${this.config.baseUrl}${this.endpoint()}`, { params });\n }),\n takeUntilDestroyed(this.destroyRef),\n ).subscribe(response => {\n if (response === null) return;\n const results: T[] = Array.isArray(response) ? response : (response?.content ?? []);\n this.filteredData.emit(results);\n });\n }\n\n onInput(val: string): void {\n this.value.set(val);\n this.query.set(val);\n }\n}\n","import { Component, input, output, computed, ChangeDetectionStrategy } from '@angular/core';\n\n@Component({\n selector: 'ui-pagination',\n standalone: true,\n changeDetection: ChangeDetectionStrategy.OnPush,\n host: { '[class.pagination-detached]': 'detached()' },\n template: `\n @if (totalItems() > pageSize()) {\n <nav class=\"pagination\" role=\"navigation\" aria-label=\"Pagination\">\n <span class=\"pagination-info\">{{ startItem() }}–{{ endItem() }} of {{ totalItems() }}</span>\n <div class=\"pagination-controls\">\n <button class=\"pagination-nav\" [disabled]=\"page() <= 1\" aria-label=\"Previous page\" (click)=\"goTo(page() - 1)\">\n <svg width=\"16\" height=\"16\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\" aria-hidden=\"true\"><polyline points=\"15 18 9 12 15 6\"/></svg>\n </button>\n @for (p of visiblePages(); track p) {\n @if (p === -1) {\n <span class=\"pagination-ellipsis\">...</span>\n } @else {\n <button class=\"pagination-page\" [class.active]=\"p === page()\" (click)=\"goTo(p)\" [attr.aria-current]=\"p === page() ? 'page' : null\" [attr.aria-label]=\"'Page ' + p\">{{ p }}</button>\n }\n }\n <button class=\"pagination-nav\" [disabled]=\"page() >= totalPages()\" aria-label=\"Next page\" (click)=\"goTo(page() + 1)\">\n <svg width=\"16\" height=\"16\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\" aria-hidden=\"true\"><polyline points=\"9 18 15 12 9 6\"/></svg>\n </button>\n </div>\n </nav>\n }\n `,\n styles: [`\n .pagination {\n display: flex; align-items: center; justify-content: space-between;\n padding: 12px 16px; gap: 12px; flex-wrap: wrap;\n background: #fff; border: 1px solid #e2e8f0; border-top: none;\n border-radius: 0 0 10px 10px;\n }\n /* Under the phone card list (data-table sets [detached]): the cards are separate bordered boxes,\n so the bar is its own bordered, fully rounded box instead of sitting flush under a table. */\n :host(.pagination-detached) .pagination {\n border: 1px solid #e2e8f0; border-radius: 10px; margin-top: 4px;\n }\n .pagination-info { font-size: 0.8125rem; color: #64748b; }\n .pagination-controls { display: flex; align-items: center; gap: 4px; }\n .pagination-nav {\n width: 32px; height: 32px; border-radius: 8px; border: 1px solid #e2e8f0;\n background: #fff; cursor: pointer; display: flex; align-items: center;\n justify-content: center; color: #334155; font-family: inherit;\n transition: background 0.1s;\n &:hover:not(:disabled) { background: #f8fafc; border-color: #cbd5e1; }\n &:disabled { opacity: 0.4; cursor: default; }\n &:focus-visible { outline: 2px solid #3b82f6; outline-offset: 2px; }\n }\n .pagination-page {\n min-width: 32px; height: 32px; border-radius: 8px; border: 1px solid #e2e8f0;\n background: #fff; font-size: 0.8125rem; font-weight: 500; color: #334155;\n cursor: pointer; display: flex; align-items: center; justify-content: center;\n font-family: inherit; transition: background 0.1s, border-color 0.1s;\n &:hover { background: #f8fafc; border-color: #cbd5e1; }\n &.active { background: #3b82f6; color: #fff; border-color: #3b82f6; }\n &:focus-visible { outline: 2px solid #3b82f6; outline-offset: 2px; }\n }\n .pagination-ellipsis { padding: 0 4px; color: #94a3b8; font-size: 0.8125rem; }\n /* Touch: 44px targets; controls wrap so up to 7 pages + prev/next still fit a phone. */\n @media (max-width: 640px), (pointer: coarse) {\n .pagination-controls { flex-wrap: wrap; justify-content: center; }\n .pagination-nav { width: 44px; height: 44px; }\n .pagination-page { min-width: 44px; height: 44px; }\n }\n @media (max-width: 480px) {\n .pagination { justify-content: center; padding: 10px 12px; }\n .pagination-info { width: 100%; text-align: center; }\n }\n `],\n})\nexport class PaginationComponent {\n readonly page = input(1);\n readonly pageSize = input(25);\n readonly totalItems = input(0);\n /** Standalone look (full border, full radius, small top margin) for use under separate cards rather than flush under a table. */\n readonly detached = input(false);\n\n readonly pageChange = output<number>();\n\n readonly totalPages = computed(() => Math.ceil(this.totalItems() / this.pageSize()) || 1);\n readonly startItem = computed(() => (this.page() - 1) * this.pageSize() + 1);\n readonly endItem = computed(() => Math.min(this.page() * this.pageSize(), this.totalItems()));\n\n readonly visiblePages = computed(() => {\n const total = this.totalPages();\n if (total <= 7) return Array.from({ length: total }, (_, i) => i + 1);\n const pages: number[] = [1];\n const start = Math.max(2, this.page() - 1);\n const end = Math.min(total - 1, this.page() + 1);\n if (start > 2) pages.push(-1);\n for (let i = start; i <= end; i++) pages.push(i);\n if (end < total - 1) pages.push(-1);\n pages.push(total);\n return pages;\n });\n\n goTo(p: number) {\n if (p >= 1 && p <= this.totalPages() && p !== this.page()) {\n this.pageChange.emit(p);\n }\n }\n}\n","import { Component, input, output, computed, signal, effect, ChangeDetectionStrategy, inject, ElementRef } from '@angular/core';\nimport { DecimalPipe } from '@angular/common';\nimport { QuickFilterTab, DROPDOWN_GROUP } from '../data-table/data-table.types';\n\nconst MAX_VISIBLE = 3; // max non-All tabs shown before overflow\n\n@Component({\n selector: 'ui-quick-filter-tabs',\n standalone: true,\n imports: [DecimalPipe],\n changeDetection: ChangeDetectionStrategy.OnPush,\n host: { '(document:click)': 'onDocumentClick($event)' },\n template: `\n <div class=\"tabs-outer\">\n <div class=\"tabs\" role=\"tablist\" [attr.aria-label]=\"ariaLabel()\">\n\n <!-- All tab (always first) -->\n @if (allTab(); as all) {\n <button class=\"tab\" role=\"tab\"\n [class.active]=\"activeValue() === all.value\"\n [attr.aria-selected]=\"activeValue() === all.value\"\n (click)=\"tabChange.emit(all.value)\">\n {{ all.label }}\n @if (counts()[all.value] != null) {\n <span class=\"tab-count\">{{ counts()[all.value] | number }}</span>\n }\n </button>\n }\n\n <!-- Top N visible tabs -->\n @for (tab of visibleTabs(); track tab.value) {\n <button class=\"tab\" role=\"tab\"\n [class.active]=\"activeValue() === tab.value\"\n [class.tab-disabled]=\"tab.disabled\"\n [attr.aria-selected]=\"activeValue() === tab.value\"\n [attr.aria-disabled]=\"tab.disabled || null\"\n (click)=\"!tab.disabled && tabChange.emit(tab.value)\">\n {{ tab.label }}\n @if (counts()[tab.value] != null) {\n <span class=\"tab-count\">{{ counts()[tab.value] | number }}</span>\n }\n </button>\n }\n\n <!-- More button (inside scrollable .tabs for layout) -->\n @if (overflowTabs().length) {\n <button class=\"tab more-btn\" [class.active]=\"overflowActive()\"\n (click)=\"toggleDropdown($event)\">\n @if (overflowActive()) {\n {{ activeOverflowLabel() }}\n <span class=\"tab-count active-count\">{{ counts()[activeValue()] | number }}</span>\n } @else {\n More\n }\n <svg width=\"11\" height=\"11\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2.5\" aria-hidden=\"true\">\n <path d=\"M6 9l6 6 6-6\"/>\n </svg>\n </button>\n }\n\n </div>\n\n <!-- Dropdown outside .tabs so overflow-x:auto doesn't clip it -->\n @if (overflowTabs().length && dropdownOpen()) {\n <div class=\"more-dropdown\" role=\"listbox\">\n @for (tab of overflowTabs(); track tab.value) {\n <button class=\"more-option\" role=\"option\"\n [class.more-option-active]=\"activeValue() === tab.value\"\n (click)=\"selectOverflow(tab.value)\">\n <span class=\"more-option-label\">{{ tab.label }}</span>\n @if (counts()[tab.value] != null) {\n <span class=\"more-option-count\">{{ counts()[tab.value] | number }}</span>\n }\n </button>\n }\n </div>\n }\n </div>\n `,\n styles: [`\n :host { display: block; min-width: 0; }\n .tabs-outer { position: relative; display: inline-flex; align-items: center; }\n .tabs {\n display: flex; align-items: center; gap: 2px;\n background: #f1f5f9; border-radius: 8px; padding: 3px;\n overflow-x: auto; scrollbar-width: none;\n &::-webkit-scrollbar { display: none; }\n }\n .tab {\n display: inline-flex; align-items: center; gap: 6px;\n padding: 6px 14px; border-radius: 6px; border: none;\n background: transparent; font-size: 0.8125rem; font-weight: 500;\n color: #64748b; cursor: pointer; font-family: inherit;\n transition: background 0.1s, color 0.1s; white-space: nowrap;\n flex-shrink: 0;\n &:hover { color: #1e293b; }\n &.active { background: #fff; color: #1e293b; box-shadow: 0 1px 3px rgba(0,0,0,0.08); }\n &:focus-visible { outline: 2px solid #60a5fa; outline-offset: 2px; }\n &.tab-disabled { opacity: 0.45; cursor: default; pointer-events: none; }\n }\n .tab-count {\n display: inline-flex; align-items: center; justify-content: center;\n min-width: 18px; height: 18px; padding: 0 5px; border-radius: 999px;\n font-size: 0.6875rem; font-weight: 700; background: #e2e8f0; color: #475569; line-height: 1;\n .active & { background: #1e293b; color: #fff; }\n }\n .active-count { background: #1e293b; color: #fff; }\n .more-btn { gap: 4px; }\n .more-dropdown {\n position: absolute; top: calc(100% + 4px); right: 0; z-index: 100;\n background: #fff; border: 1px solid #e2e8f0; border-radius: 8px;\n box-shadow: 0 4px 16px rgba(0,0,0,0.1); min-width: 180px;\n padding: 4px; max-height: 260px; overflow-y: auto;\n }\n .more-option {\n display: flex; align-items: center; justify-content: space-between;\n width: 100%; padding: 8px 12px; background: none; border: none;\n cursor: pointer; font-size: 0.875rem; color: #334155; font-family: inherit;\n border-radius: 5px; transition: background 0.1s; text-align: left; gap: 12px;\n &:hover { background: #f8fafc; }\n &.more-option-active { background: #f1f5f9; color: #1e293b; font-weight: 500; }\n &:focus-visible { outline: 2px solid #60a5fa; outline-offset: -2px; }\n }\n .more-option-label { flex: 1; }\n .more-option-count {\n display: inline-flex; align-items: center; justify-content: center;\n min-width: 20px; height: 20px; padding: 0 6px; border-radius: 999px;\n font-size: 0.6875rem; font-weight: 700; line-height: 1;\n background: #e2e8f0; color: #475569;\n .more-option-active & { background: #1e293b; color: #fff; }\n }\n\n /* ── iPad / tablet ─── */\n @media (max-width: 1024px) {\n .tab { padding: 7px 12px; }\n }\n\n /* ── Touch (incl. iPad): 44px tap targets, desktop mouse sizes unchanged ─── */\n @media (pointer: coarse) {\n .tab { min-height: 44px; }\n }\n\n /* ── Phone ─── */\n @media (max-width: 640px) {\n /* Natural width — gray pill only wraps visible tabs, no awkward empty space */\n :host { align-self: flex-start; }\n /* Larger tap targets — 36px+ height (10+10 padding + ~13px line-height) */\n .tab { padding: 10px 12px; font-size: 0.8125rem; min-height: 44px; }\n .more-option { padding: 12px 14px; }\n }\n `],\n})\nexport class QuickFilterTabsComponent {\n readonly tabs = input.required<QuickFilterTab[]>();\n readonly counts = input<Record<string, number>>({});\n readonly activeValue = input('');\n readonly ariaLabel = input('Filter tabs');\n\n readonly tabChange = output<string>();\n\n protected readonly dropdownOpen = signal(false);\n private readonly _el = inject(ElementRef);\n private readonly _group = inject(DROPDOWN_GROUP, { optional: true });\n private readonly _id = 'qft-' + Math.random().toString(36).slice(2);\n\n constructor() {\n const group = this._group;\n if (group) {\n effect(() => { if (group() !== this._id && this.dropdownOpen()) this.dropdownOpen.set(false); });\n }\n }\n\n protected readonly allTab = computed(() =>\n this.tabs().find(t => t.value === '') ?? null\n );\n\n /** Non-All tabs that have at least 1 record, sorted by count desc */\n private readonly sortedFilterTabs = computed(() => {\n const counts = this.counts();\n const hasCounts = Object.keys(counts).length > 0;\n return this.tabs()\n .filter(t => t.value !== '')\n // the selected tab always stays, even at 0 (a search inside it can leave it empty)\n .filter(t => !hasCounts || (counts[t.value] ?? 0) > 0 || t.value === this.activeValue())\n .sort((a, b) => hasCounts ? (counts[b.value] ?? 0) - (counts[a.value] ?? 0) : 0);\n });\n\n protected readonly visibleTabs = computed(() =>\n this.sortedFilterTabs().slice(0, MAX_VISIBLE)\n );\n\n protected readonly overflowTabs = computed(() =>\n this.sortedFilterTabs().slice(MAX_VISIBLE)\n );\n\n protected readonly overflowActive = computed(() =>\n this.overflowTabs().some(t => t.value === this.activeValue())\n );\n\n protected readonly activeOverflowLabel = computed(() =>\n this.overflowTabs().find(t => t.value === this.activeValue())?.label ?? 'More'\n );\n\n toggleDropdown(_e: Event) {\n if (!this.dropdownOpen()) this._group?.set(this._id);\n this.dropdownOpen.update(v => !v);\n }\n\n selectOverflow(value: string) {\n this.tabChange.emit(value);\n this.dropdownOpen.set(false);\n }\n\n onDocumentClick(event: Event) {\n if (this.dropdownOpen() && !this._el.nativeElement.contains(event.target)) {\n this.dropdownOpen.set(false);\n }\n }\n}\n","import { Component, input, output, signal, computed, effect, afterNextRender, Injector, ChangeDetectionStrategy, inject, ElementRef } from '@angular/core';\nimport { FilterOption, DROPDOWN_GROUP } from '../data-table/data-table.types';\n\n@Component({\n selector: 'ui-column-filter',\n standalone: true,\n changeDetection: ChangeDetectionStrategy.OnPush,\n host: {\n '(document:click)': 'onDocumentClick($event)',\n '(document:keydown.escape)': 'onEscape()',\n '(keydown.arrowdown)': 'onArrow($event, 1)',\n '(keydown.arrowup)': 'onArrow($event, -1)',\n },\n template: `\n <div class=\"cf-wrapper\">\n <button\n class=\"cf-trigger\"\n [class.cf-active]=\"activeValues().length > 0\"\n [class.cf-trigger--chip]=\"chip()\"\n [attr.aria-label]=\"'Filter by ' + label()\"\n [attr.aria-expanded]=\"open()\"\n (click)=\"toggle($event)\">\n <svg width=\"12\" height=\"12\" viewBox=\"0 0 24 24\" fill=\"none\"\n [attr.stroke]=\"activeValues().length > 0 ? '#3b82f6' : 'currentColor'\"\n stroke-width=\"2\" aria-hidden=\"true\">\n <path d=\"M22 3H2l8 9.46V19l4 2v-8.54L22 3z\"/>\n </svg>\n @if (chip()) {\n <span class=\"cf-chip-label\">{{ label() }}</span>\n }\n @if (activeValues().length > 0) {\n <span class=\"cf-active-label\">{{ activeLabelText() }}</span>\n }\n </button>\n\n @if (open()) {\n <div class=\"cf-dropdown\" role=\"listbox\" [attr.aria-label]=\"label() + ' filter options'\"\n [style.top.px]=\"_pos().top\" [style.left.px]=\"_pos().left\"\n (click)=\"$event.stopPropagation()\">\n <div class=\"cf-header\">\n <span class=\"cf-header-label\">{{ label() }}</span>\n <button class=\"cf-close\" type=\"button\" aria-label=\"Close filter\" (click)=\"open.set(false)\">\n <svg width=\"13\" height=\"13\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2.5\" stroke-linecap=\"round\" aria-hidden=\"true\">\n <line x1=\"18\" y1=\"6\" x2=\"6\" y2=\"18\"/><line x1=\"6\" y1=\"6\" x2=\"18\" y2=\"18\"/>\n </svg>\n </button>\n </div>\n <div class=\"cf-options\">\n @for (opt of _visibleOptions(); track opt.value) {\n <button\n class=\"cf-option\"\n role=\"option\"\n [class.cf-selected]=\"pendingValues().includes(opt.value)\"\n [attr.aria-selected]=\"pendingValues().includes(opt.value)\"\n (click)=\"select(opt.value)\">\n <span class=\"cf-checkbox\" [class.cf-checkbox--checked]=\"pendingValues().includes(opt.value)\" aria-hidden=\"true\">\n @if (pendingValues().includes(opt.value)) {\n <svg width=\"10\" height=\"10\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"3\" stroke-linecap=\"round\" stroke-linejoin=\"round\" aria-hidden=\"true\">\n <polyline points=\"20 6 9 17 4 12\"/>\n </svg>\n }\n </span>\n {{ opt.label }}\n @if (opt.count != null) {\n <span class=\"cf-count\">{{ opt.count }}</span>\n }\n </button>\n }\n </div>\n <div class=\"cf-footer\">\n @if (pendingHasChanges()) {\n <button class=\"cf-clear\" (click)=\"clear()\">Clear</button>\n } @else {\n <button class=\"cf-clear\" (click)=\"cancel()\">Cancel</button>\n }\n <button class=\"cf-apply\" (click)=\"apply()\">Apply</button>\n </div>\n </div>\n }\n </div>\n `,\n styles: [`\n :host { display: inline-block; position: relative; }\n .cf-wrapper { position: relative; }\n .cf-trigger {\n display: inline-flex; align-items: center; gap: 4px;\n background: none; border: none; cursor: pointer;\n color: #94a3b8; padding: 2px 4px; border-radius: 4px;\n font-family: inherit; font-size: 0.75rem;\n transition: color 0.15s;\n &:hover { color: #1e293b; }\n &.cf-active { color: #3b82f6; }\n &:focus-visible { outline: 2px solid #3b82f6; outline-offset: 2px; }\n }\n /* Touch: invisible 44px hit area around the funnel; header height is unchanged. */\n @media (max-width: 640px), (pointer: coarse) {\n .cf-trigger { position: relative; }\n .cf-trigger::after { content: ''; position: absolute; top: 50%; left: 50%; width: 44px; height: 44px; transform: translate(-50%, -50%); }\n }\n .cf-active-label { font-size: 0.75rem; font-weight: 500; color: #3b82f6; }\n /* Phone filter chip (data-table control row): labelled pill, 44px tall. */\n .cf-trigger--chip {\n min-height: 44px; padding: 0 14px; gap: 6px;\n border: 1px solid #e2e8f0; border-radius: 999px; background: #fff;\n color: #334155; font-size: 0.8125rem; font-weight: 500;\n &.cf-active { border-color: #bfdbfe; background: #eff6ff; }\n }\n .cf-trigger--chip::after { display: none; }\n .cf-chip-label { white-space: nowrap; }\n /* position: fixed (top/left set from the trigger rect in TS) so the table's scroll container can't clip it. */\n .cf-dropdown {\n position: fixed;\n display: flex; flex-direction: column; max-height: calc(100vh - 16px);\n z-index: 9999;\n background: #fff;\n border: 1px solid #e2e8f0;\n border-radius: 12px;\n box-shadow: 0 4px 6px rgba(0,0,0,0.04), 0 12px 28px rgba(0,0,0,0.10);\n min-width: 220px;\n animation: cf-drop 160ms cubic-bezier(0.16, 1, 0.3, 1);\n }\n .cf-header {\n display: flex; align-items: center; justify-content: space-between;\n padding: 12px 12px 10px 16px;\n border-bottom: 1px solid #f1f5f9;\n }\n .cf-header-label {\n font-size: 0.7rem; font-weight: 700;\n text-transform: uppercase; letter-spacing: 0.07em; color: #64748b;\n }\n .cf-close {\n display: flex; align-items: center; justify-content: center;\n width: 24px; height: 24px; border-radius: 6px;\n background: none; border: none; cursor: pointer; color: #94a3b8;\n flex-shrink: 0; transition: background 130ms, color 130ms;\n &:hover { background: #f1f5f9; color: #1e293b; }\n }\n .cf-options { padding: 6px 8px; overflow-y: auto; min-height: 0; }\n .cf-option {\n display: flex; align-items: center; gap: 10px; width: 100%;\n padding: 10px 10px; background: none; border: none; border-bottom: 1px solid #e2e8f0;\n cursor: pointer; font-size: 0.875rem; color: #1e293b; font-family: inherit; text-align: left;\n border-radius: 0; transition: background 0.12s;\n &:last-child { border-bottom: none; }\n &:hover { background: #f8fafc; }\n &.cf-selected { background: #eff6ff; color: #1d4ed8; font-weight: 500; }\n &:focus-visible { outline: 2px solid #3b82f6; outline-offset: -2px; }\n }\n .cf-checkbox {\n flex-shrink: 0;\n width: 16px; height: 16px; border-radius: 4px;\n border: 1.5px solid #d1d5db;\n display: flex; align-items: center; justify-content: center;\n background: #fff; transition: background 0.12s, border-color 0.12s;\n &.cf-checkbox--checked { background: #3b82f6; border-color: #3b82f6; color: #fff; }\n }\n .cf-count {\n margin-left: auto; font-size: 0.7rem; font-weight: 600; line-height: 1;\n background: #f1f5f9; color: #64748b; border-radius: 999px;\n padding: 2px 7px; min-width: 22px; text-align: center;\n }\n .cf-option.cf-selected .cf-count { background: #dbeafe; color: #2563eb; }\n .cf-footer {\n display: flex; gap: 8px; padding: 10px 12px 12px;\n border-top: 1px solid #f1f5f9;\n }\n .cf-clear {\n flex: 1; padding: 8px; border-radius: 8px; border: 1px solid #e2e8f0;\n background: #fff; font-size: 0.8125rem; color: #64748b; cursor: pointer;\n font-family: inherit; font-weight: 500; transition: background 0.12s, color 0.12s;\n &:hover { background: #f8fafc; color: #1e293b; }\n }\n .cf-apply {\n flex: 1; padding: 8px; border-radius: 8px; border: none;\n background: #0f172a; color: #fff; font-size: 0.8125rem;\n cursor: pointer; font-family: inherit; font-weight: 600;\n transition: background 0.12s;\n &:hover { background: #1e293b; }\n }\n @keyframes cf-drop {\n from { opacity: 0; transform: translateY(-4px) scale(0.98); }\n to { opacity: 1; transform: translateY(0) scale(1); }\n }\n /* ── iPad (≤1024px) — bigger funnel tap target + comfortable option height ─── */\n @media (max-width: 1024px) {\n .cf-trigger { padding: 6px 8px; }\n .cf-option { padding: 11px 10px; min-height: 40px; }\n .cf-clear, .cf-apply { padding: 10px; }\n }\n\n /* ── Phone (≤640px) — right-anchored dropdown, full touch targets ─── */\n @media (max-width: 640px) {\n .cf-dropdown { min-width: 200px; max-width: calc(100vw - 16px); }\n .cf-option { padding: 12px 10px; min-height: 44px; }\n .cf-clear, .cf-apply { padding: 11px; }\n }\n `],\n})\nexport class ColumnFilterComponent {\n readonly label = input.required<string>();\n readonly options = input.required<FilterOption[]>();\n /** Currently applied values — empty array means no filter active. */\n readonly activeValues = input<string[]>([]);\n /** Render the trigger as a labelled 44px chip (phone control row) instead of the bare funnel icon. */\n readonly chip = input(false);\n\n /** Emits the new selection on Apply. Empty array = filter cleared. */\n readonly filterChange = output<string[]>();\n\n protected readonly open = signal(false);\n protected readonly pendingValues = signal<string[]>([]);\n\n /** Hide options with count = 0, unless they are currently selected (so user can deselect). */\n protected readonly _visibleOptions = computed(() =>\n this.options().filter(o => o.count == null || o.count > 0 || this.activeValues().includes(o.value))\n );\n\n protected readonly pendingHasChanges = computed(() => {\n const p = this.pendingValues();\n const a = this.activeValues();\n return p.length !== a.length || p.some(v => !a.includes(v));\n });\n\n private readonly _el = inject(ElementRef);\n private readonly _group = inject(DROPDOWN_GROUP, { optional: true });\n private readonly _injector = inject(Injector);\n private readonly _id = Math.random().toString(36).slice(2);\n\n constructor() {\n const group = this._group;\n if (group) {\n effect(() => { if (group() !== this._id && this.open()) this.open.set(false); });\n }\n // While open, keep the fixed dropdown glued to its trigger on window resize and on any scroll\n // (capture phase, so the table's own scroll container and the page are both caught).\n effect((onCleanup) => {\n if (!this.open()) return;\n const reposition = () => this._reposition();\n window.addEventListener('resize', reposition);\n document.addEventListener('scroll', reposition, true);\n onCleanup(() => {\n window.removeEventListener('resize', reposition);\n document.removeEventListener('scroll', reposition, true);\n });\n });\n }\n\n /** Viewport coordinates (px) of the fixed dropdown. */\n protected readonly _pos = signal<{ top: number; left: number }>({ top: 0, left: 0 });\n\n protected readonly activeLabelText = computed(() => {\n const vals = this.activeValues();\n if (vals.length === 0) return '';\n if (vals.length === 1) return this.options().find(o => o.value === vals[0])?.label ?? vals[0];\n return `${vals.length} selected`;\n });\n\n toggle(event: Event) {\n event.stopPropagation();\n if (!this.open()) {\n this._group?.set(this._id);\n this.pendingValues.set([...this.activeValues()]);\n this._reposition(); // estimate (dropdown not rendered yet)\n afterNextRender(() => this._reposition(), { injector: this._injector }); // exact, once it is measurable\n setTimeout(() => this._optionEls()[0]?.focus(), 30);\n }\n this.open.update(v => !v);\n }\n\n select(value: string) {\n const cur = this.pendingValues();\n this.pendingValues.set(cur.includes(value) ? cur.filter(v => v !== value) : [...cur, value]);\n }\n\n apply() {\n this.filterChange.emit(this.pendingValues());\n this.open.set(false);\n this._trigger()?.focus();\n }\n\n cancel() {\n this.open.set(false);\n this._trigger()?.focus();\n }\n\n clear() {\n this.pendingValues.set([]);\n this.filterChange.emit([]);\n this.open.set(false);\n this._trigger()?.focus();\n }\n\n onDocumentClick(_event: Event) { if (this.open()) this.open.set(false); }\n\n onEscape() { if (this.open()) { this.open.set(false); this._trigger()?.focus(); } }\n\n onArrow(event: Event, dir: 1 | -1) {\n if (!this.open()) return;\n event.preventDefault();\n const items = this._optionEls();\n if (!items.length) return;\n const idx = items.indexOf(document.activeElement as HTMLButtonElement);\n items[(idx === -1 ? 0 : (idx + dir + items.length) % items.length)].focus();\n }\n\n /**\n * Places the fixed dropdown from the trigger's viewport rect: centred under the trigger, clamped\n * 8px inside the viewport horizontally, flipped above the trigger when it doesn't fit below\n * (or pinned to the viewport's bottom margin when it fits in neither place).\n */\n private _reposition(): void {\n const trigger = this._trigger();\n if (!trigger) return;\n const MARGIN = 8, GAP = 8;\n const r = trigger.getBoundingClientRect();\n const dd = this._el.nativeElement.querySelector('.cf-dropdown') as HTMLElement | null;\n const w = dd?.offsetWidth || 220;\n const h = dd?.offsetHeight || 0;\n const vw = window.innerWidth, vh = window.innerHeight;\n const left = Math.min(Math.max(r.left + r.width / 2 - w / 2, MARGIN), Math.max(MARGIN, vw - w - MARGIN));\n let top = r.bottom + GAP;\n if (top + h > vh - MARGIN) {\n const above = r.top - GAP - h;\n top = above >= MARGIN ? above : Math.max(MARGIN, vh - MARGIN - h);\n }\n this._pos.set({ top, left });\n }\n\n private _optionEls(): HTMLButtonElement[] {\n return Array.from(this._el.nativeElement.querySelectorAll('.cf-option')) as HTMLButtonElement[];\n }\n\n private _trigger(): HTMLButtonElement | null {\n return this._el.nativeElement.querySelector('.cf-trigger') as HTMLButtonElement | null;\n }\n}\n","import { Component, input, computed, ChangeDetectionStrategy } from '@angular/core';\nimport { BadgeVariant, BADGE_VARIANT_STYLES, SEMANTIC_BADGE_MAP, autoVariant } from '../data-table/data-table.types';\n\n@Component({\n selector: 'ui-badge',\n standalone: true,\n changeDetection: ChangeDetectionStrategy.OnPush,\n template: `\n <span\n class=\"badge\"\n [class.badge-gray]=\"resolvedVariant() === 'gray'\"\n [style.background]=\"styles().bg\"\n [style.color]=\"styles().color\">\n {{ label() }}\n </span>\n `,\n styles: [`\n :host { display: inline-flex; }\n .badge {\n display: inline-flex; align-items: center; padding: 2px 10px;\n border-radius: 999px; font-size: 0.75rem; font-weight: 600;\n white-space: nowrap; line-height: 1.5;\n border: 1px solid transparent;\n }\n .badge-gray { border-color: #cbd5e1; }\n `],\n})\nexport class BadgeComponent {\n readonly label = input.required<string>();\n /**\n * When omitted:\n * 1. Check SEMANTIC_BADGE_MAP (e.g. 'Active' → green always)\n * 2. Fall back to auto-color based on the label string\n */\n readonly variant = input<BadgeVariant | null>(null);\n\n protected readonly resolvedVariant = computed<BadgeVariant>(() => {\n if (this.variant()) return this.variant()!;\n return SEMANTIC_BADGE_MAP[this.label()] ?? autoVariant(this.label());\n });\n\n protected readonly styles = computed(() =>\n BADGE_VARIANT_STYLES[this.resolvedVariant()] ?? BADGE_VARIANT_STYLES['gray'],\n );\n}\n","import { Component, input, computed, ChangeDetectionStrategy } from '@angular/core';\n\nconst BG_PALETTE = [\n '#dbeafe', '#dcfce7', '#fce7f3', '#ccfbf1', '#ffedd5',\n '#bfdbfe', '#d1fae5', '#fef3c7', '#fdf4ff', '#cffafe',\n];\nconst COLOR_PALETTE = [\n '#1e40af', '#166534', '#be185d', '#115e59', '#9a3412',\n '#1e3a8a', '#065f46', '#92400e', '#a21caf', '#155e75',\n];\n\nfunction colorIndex(str: string): number {\n let h = 0;\n for (let i = 0; i < str.length; i++) h = (h * 31 + str.charCodeAt(i)) & 0xffff;\n return Math.abs(h) % BG_PALETTE.length;\n}\n\n@Component({\n selector: 'ui-avatar',\n standalone: true,\n changeDetection: ChangeDetectionStrategy.OnPush,\n template: `\n <span\n class=\"avatar\"\n role=\"img\"\n [style.width.px]=\"size()\"\n [style.height.px]=\"size()\"\n [style.font-size.px]=\"size() * 0.38\"\n [style.background]=\"bg()\"\n [style.color]=\"color()\"\n [attr.aria-label]=\"name() || initials()\">\n {{ initials() }}\n </span>\n `,\n styles: [`\n :host { display: inline-flex; flex-shrink: 0; }\n .avatar {\n border-radius: 50%; display: flex; align-items: center;\n justify-content: center; font-weight: 600;\n text-transform: uppercase; letter-spacing: 0.02em;\n user-select: none; flex-shrink: 0;\n }\n `],\n})\nexport class AvatarComponent {\n readonly initials = input.required<string>();\n readonly name = input('');\n readonly size = input(32);\n\n protected readonly bg = computed(() => BG_PALETTE[colorIndex(this.initials())]);\n protected readonly color = computed(() => COLOR_PALETTE[colorIndex(this.initials())]);\n}\n","import { Component, input, output, signal, ChangeDetectionStrategy, inject, ElementRef } from '@angular/core';\nimport { RowAction } from '../data-table/data-table.types';\n\nexport interface MenuItem {\n type: string;\n label: string;\n icon?: string;\n primary?: boolean;\n danger?: boolean;\n disabled?: boolean;\n}\n\nconst BUILT_IN_ICONS: Record<string, string> = {\n 'view': 'M1 12s4-8 11-8 11 8 11 8-4 8-11 8-11-8-11-8z M12 9a3 3 0 1 0 0 6 3 3 0 0 0 0-6z',\n 'edit': 'M11 4H4a2 2 0 0 0-2 2v14a2 2 0 0 0 2 2h14a2 2 0 0 0 2-2v-7 M18.5 2.5a2.121 2.121 0 0 1 3 3L12 15l-4 1 1-4 9.5-9.5z',\n 'delete': 'M3 6h18 M19 6l-1 14a2 2 0 0 1-2 2H8a2 2 0 0 1-2-2L5 6 M8 6V4a1 1 0 0 1 1-1h6a1 1 0 0 1 1 1v2',\n 'call': 'M22 16.92v3a2 2 0 0 1-2.18 2 19.79 19.79 0 0 1-8.63-3.07 19.5 19.5 0 0 1-6-6 19.79 19.79 0 0 1-3.07-8.67A2 2 0 0 1 4.11 2h3a2 2 0 0 1 2 1.72c.127.96.361 1.903.7 2.81a2 2 0 0 1-.45 2.11L8.09 9.91a16 16 0 0 0 6 6l1.27-1.27a2 2 0 0 1 2.11-.45c.907.339 1.85.573 2.81.7A2 2 0 0 1 22 16.92z',\n 'message': 'M21 15a2 2 0 0 1-2 2H7l-4 4V5a2 2 0 0 1 2-2h14a2 2 0 0 1 2 2z',\n 'email': 'M4 4h16c1.1 0 2 .9 2 2v12c0 1.1-.9 2-2 2H4c-1.1 0-2-.9-2-2V6c0-1.1.9-2 2-2z M22 6l-10 7L2 6',\n 'task': 'M9 11l3 3L22 4 M21 12v7a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2V5a2 2 0 0 1 2-2h11',\n 'opportunity': 'M22 12h-4l-3 9L9 3l-3 9H2',\n 'assign-owner': 'M20 21v-2a4 4 0 0 0-4-4H8a4 4 0 0 0-4 4v2 M12 3a4 4 0 1 0 0 8 4 4 0 0 0 0-8z M19 8v6 M22 11h-6',\n 'assign-agent': 'M20 21v-2a4 4 0 0 0-4-4H8a4 4 0 0 0-4 4v2 M12 3a4 4 0 1 0 0 8 4 4 0 0 0 0-8z M19 8v6 M22 11h-6',\n 'view-contact': 'M20 21v-2a4 4 0 0 0-4-4H8a4 4 0 0 0-4 4v2 M12 3a4 4 0 1 0 0 8 4 4 0 0 0 0-8z',\n 'confirm': 'M22 11.08V12a10 10 0 1 1-5.93-9.14 M22 4L12 14.01l-3-3',\n 'decline': 'M12 2a10 10 0 1 0 0 20 10 10 0 0 0 0-20z M15 9l-6 6 M9 9l6 6',\n 'sphere': 'M17 21v-2a4 4 0 0 0-4-4H5a4 4 0 0 0-4 4v2 M9 3a4 4 0 1 0 0 8 4 4 0 0 0 0-8z M23 21v-2a4 4 0 0 0-3-3.87 M16 3.13a4 4 0 0 1 0 7.75',\n 'remove-sphere': 'M17 21v-2a4 4 0 0 0-4-4H5a4 4 0 0 0-4 4v2 M9 3a4 4 0 1 0 0 8 4 4 0 0 0 0-8z M23 18h-6',\n 'portal-invite': 'M4 4h16c1.1 0 2 .9 2 2v12c0 1.1-.9 2-2 2H4c-1.1 0-2-.9-2-2V6c0-1.1.9-2 2-2z M15 9l3 3-3 3 M18 12H8',\n 'archive': 'M21 8v13H3V8 M1 3h22v5H1z M10 12h4',\n 'duplicate': 'M20 2H8a2 2 0 0 0-2 2v12a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V4a2 2 0 0 0-2-2z M4 6H2v14a2 2 0 0 0 2 2h14v-2H4V6z',\n 'download': 'M21 15v4a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2v-4 M7 10l5 5 5-5 M12 15V3',\n 'export': 'M21 15v4a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2v-4 M7 10l5 5 5-5 M12 15V3',\n 'share': 'M4 12v8a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2v-8 M16 6l-4-4-4 4 M12 2v13',\n // Status / workflow actions\n 'approve': 'M22 11.08V12a10 10 0 1 1-5.93-9.14 M22 4L12 14.01l-3-3',\n 'reject': 'M12 2a10 10 0 1 0 0 20 10 10 0 0 0 0-20z M15 9l-6 6 M9 9l6 6',\n 'activate': 'M12 2a10 10 0 1 0 0 20 10 10 0 0 0 0-20z M10 8l6 4-6 4V8z',\n 'deactivate': 'M12 2a10 10 0 1 0 0 20 10 10 0 0 0 0-20z M8 12h8',\n 'pause': 'M12 2a10 10 0 1 0 0 20 10 10 0 0 0 0-20z M10 15V9 M14 15V9',\n 'cancel': 'M12 2a10 10 0 1 0 0 20 10 10 0 0 0 0-20z M4.93 4.93l14.14 14.14',\n 'restore': 'M1 4v6h6 M3.51 15a9 9 0 1 0 2.13-9.36L1 10',\n // Create / navigate actions\n 'send-message': 'M21 15a2 2 0 0 1-2 2H7l-4 4V5a2 2 0 0 1 2-2h14a2 2 0 0 1 2 2z',\n 'create-job': 'M20 7H4a2 2 0 0 0-2 2v10a2 2 0 0 0 2 2h16a2 2 0 0 0 2-2V9a2 2 0 0 0-2-2z M16 21V5a2 2 0 0 0-2-2h-4a2 2 0 0 0-2 2v16',\n 'view-job': 'M20 7H4a2 2 0 0 0-2 2v10a2 2 0 0 0 2 2h16a2 2 0 0 0 2-2V9a2 2 0 0 0-2-2z M16 21V5a2 2 0 0 0-2-2h-4a2 2 0 0 0-2 2v16',\n 'create-invoice': 'M14 2H6a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V8z M14 2v6h6 M16 13H8 M16 17H8 M10 9H8',\n 'create-estimate':'M14 2H6a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V8z M14 2v6h6 M12 18v-6 M9 15h6',\n 'add-note': 'M12 20h9 M16.5 3.5a2.121 2.121 0 0 1 3 3L7 19l-4 1 1-4L16.5 3.5z',\n 'invite-portal': 'M4 4h16c1.1 0 2 .9 2 2v12c0 1.1-.9 2-2 2H4c-1.1 0-2-.9-2-2V6c0-1.1.9-2 2-2z M15 9l3 3-3 3 M18 12H8',\n};\n\n/** Built-in SVG path for a row-action type (exported so apps can reuse it for their own action types). */\nexport function builtInActionIcon(type: string): string | undefined { return BUILT_IN_ICONS[type]; }\n\n@Component({\n selector: 'ui-more-menu',\n standalone: true,\n changeDetection: ChangeDetectionStrategy.OnPush,\n host: {\n '(document:click)': 'onDocumentClick()',\n '(document:keydown.escape)': 'onEscape()',\n '(keydown.arrowdown)': 'onArrow($event, 1)',\n '(keydown.arrowup)': 'onArrow($event, -1)',\n },\n template: `\n <div class=\"mm-wrapper\">\n <button\n class=\"mm-trigger\"\n [attr.aria-expanded]=\"open()\"\n aria-haspopup=\"menu\"\n [attr.aria-label]=\"ariaLabel()\"\n (click)=\"toggle($event)\">\n <svg width=\"16\" height=\"16\" viewBox=\"0 0 24 24\" fill=\"currentColor\" aria-hidden=\"true\">\n <circle cx=\"12\" cy=\"5\" r=\"1.5\"/><circle cx=\"12\" cy=\"12\" r=\"1.5\"/><circle cx=\"12\" cy=\"19\" r=\"1.5\"/>\n </svg>\n </button>\n\n @if (open()) {\n <div class=\"mm-dropdown\" [class.mm-up]=\"openUp()\" [style.max-height.px]=\"maxHeight()\" role=\"menu\">\n @for (action of visibleActions(); track action.type) {\n <button\n class=\"mm-item\"\n [class.mm-danger]=\"action.variant === 'danger'\"\n [disabled]=\"isDisabled(action)\"\n role=\"menuitem\"\n (click)=\"onActionClick(action, $event)\">\n <svg class=\"mm-icon\" width=\"14\" height=\"14\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\" stroke-linecap=\"round\" stroke-linejoin=\"round\" aria-hidden=\"true\">\n <path [attr.d]=\"resolveIcon(action)\" />\n </svg>\n {{ action.label }}\n </button>\n }\n </div>\n }\n </div>\n `,\n styles: [`\n :host { display: inline-block; position: relative; }\n .mm-wrapper { position: relative; }\n .mm-trigger {\n display: flex; align-items: center; justify-content: center;\n width: 32px; height: 32px; border-radius: 8px; border: none;\n background: transparent; color: #94a3b8; cursor: pointer;\n transition: background 0.1s, color 0.1s;\n &:hover { background: #f1f5f9; color: #334155; }\n &:focus-visible { outline: 2px solid #3b82f6; outline-offset: 2px; }\n }\n .mm-dropdown {\n position: absolute; right: 0; top: calc(100% + 4px); z-index: 200;\n border: 1px solid #e2e8f0; border-radius: 8px;\n box-shadow: 0 4px 16px rgba(0,0,0,0.1); min-width: 160px;\n overflow-x: hidden; overflow-y: auto; padding: 4px;\n /* Scroll affordance: edge shadows appear only when content is clipped on that side\n (\"scroll shadows\"): white covers (attachment: local) hide the shadows (attachment: scroll)\n once scrolled to that end, and everything is invisible when the menu does not overflow. */\n background:\n linear-gradient(#fff 30%, rgba(255,255,255,0)) center top / 100% 16px no-repeat local,\n linear-gradient(rgba(255,255,255,0), #fff 70%) center bottom / 100% 16px no-repeat local,\n radial-gradient(farthest-side at 50% 0, rgba(15,23,42,0.22), rgba(15,23,42,0)) center top / 100% 8px no-repeat scroll,\n radial-gradient(farthest-side at 50% 100%, rgba(15,23,42,0.22), rgba(15,23,42,0)) center bottom / 100% 8px no-repeat scroll,\n #fff;\n &.mm-up { top: auto; bottom: calc(100% + 4px); }\n }\n .mm-item {\n display: flex; align-items: center; gap: 10px; width: 100%;\n padding: 10px 12px; background: none; border: none; cursor: pointer;\n font-size: 0.875rem; color: #334155; font-family: inherit;\n text-align: left; border-radius: 5px; transition: background 0.1s;\n min-height: 40px;\n &:hover:not(:disabled) { background: #f8fafc; }\n &:disabled { opacity: 0.4; cursor: default; }\n &.mm-danger { color: #dc2626; }\n &.mm-danger:hover:not(:disabled) { background: #fef2f2; }\n &:focus-visible { outline: 2px solid #60a5fa; outline-offset: -2px; }\n }\n .mm-icon { flex-shrink: 0; }\n /* ── iPad (≤1024px) — 36px trigger, comfortable item height ─── */\n @media (max-width: 1024px) {\n .mm-trigger { width: 36px; height: 36px; }\n .mm-item { padding: 11px 14px; min-height: 40px; }\n }\n\n /* ── Touch (incl. iPad): 44px trigger, desktop mouse sizes unchanged ─── */\n @media (pointer: coarse) {\n .mm-trigger { width: 44px; height: 44px; }\n }\n\n /* ── Phone (≤640px) — full touch targets, safe dropdown bounds ─── */\n @media (max-width: 640px) {\n .mm-trigger { width: 44px; height: 44px; }\n .mm-item { padding: 12px 14px; font-size: 0.9375rem; min-height: 44px; }\n .mm-dropdown { max-height: 60vh; overflow-y: auto; max-width: calc(100vw - 32px); }\n }\n `],\n})\nexport class MoreMenuComponent {\n /** Accessible name of the ⋮ trigger. Lists pass a row-specific one (\"Row actions for Acme Electric\"). */\n readonly ariaLabel = input('Row actions');\n readonly actions = input<RowAction[]>([]);\n readonly actionClick = output<string>();\n\n protected readonly open = signal(false);\n protected readonly openUp = signal(false);\n /** Caps the dropdown to the space on the side it opens, so it never runs off-screen; it scrolls instead. */\n protected readonly maxHeight = signal<number | null>(null);\n\n private readonly _el = inject(ElementRef);\n\n protected readonly visibleActions = () =>\n this.actions().filter(a => !a.hidden?.(null));\n\n protected resolveIcon(action: RowAction): string {\n return action.icon ?? BUILT_IN_ICONS[action.type] ?? 'M5 12h14 M12 5l7 7-7 7';\n }\n\n protected isDisabled(action: RowAction): boolean {\n return typeof action.disabled === 'function' ? action.disabled(null) : !!action.disabled;\n }\n\n toggle(event: Event) {\n event.stopPropagation();\n if (!this.open()) {\n const rect = (event.currentTarget as HTMLElement).getBoundingClientRect();\n const w = window.innerWidth;\n const itemHeight = w <= 640 ? 44 : w <= 1024 ? 40 : 36;\n let estimatedHeight = this.visibleActions().length * itemHeight + 16;\n // On phones the dropdown is capped at 60vh and scrolls (see styles), so it never needs more.\n if (w <= 640) estimatedHeight = Math.min(estimatedHeight, window.innerHeight * 0.6);\n // Measure within the nearest scrolling ancestor (it clips the dropdown — e.g. the mobile card\n // list sits below the app header and toolbar), trimmed to the visible window.\n const bounds = this._visibleBounds();\n const spaceBelow = bounds.bottom - rect.bottom;\n const spaceAbove = rect.top - bounds.top;\n // Only flip upward when it doesn't fit below AND there is more room above — otherwise an upward\n // menu can run off the top of the screen (e.g. under a fixed header) where items can't be tapped.\n const up = spaceBelow < estimatedHeight && spaceAbove > spaceBelow;\n this.openUp.set(up);\n // 4px gap to the trigger + 8px margin from the screen edge.\n this.maxHeight.set(Math.max(120, Math.floor((up ? spaceAbove : spaceBelow) - 12)));\n setTimeout(() => this._menuItems()[0]?.focus(), 30);\n }\n this.open.update(v => !v);\n }\n\n onActionClick(action: RowAction, event: Event) {\n event.stopPropagation();\n this.actionClick.emit(action.type);\n this.open.set(false);\n this._trigger()?.focus();\n }\n\n onDocumentClick() {\n if (this.open()) this.open.set(false);\n }\n\n onEscape() {\n if (this.open()) {\n this.open.set(false);\n this._trigger()?.focus();\n }\n }\n\n onArrow(event: Event, dir: 1 | -1) {\n if (!this.open()) return;\n event.preventDefault();\n const items = this._menuItems();\n if (!items.length) return;\n const idx = items.indexOf(document.activeElement as HTMLButtonElement);\n const next = idx === -1 ? 0 : (idx + dir + items.length) % items.length;\n items[next].focus();\n }\n\n /** Visible top/bottom of the nearest scrolling ancestor, clipped to the viewport. */\n private _visibleBounds(): { top: number; bottom: number } {\n let top = 0;\n let bottom = window.innerHeight;\n let el: HTMLElement | null = (this._el.nativeElement as HTMLElement).parentElement;\n while (el && el !== document.body) {\n const oy = getComputedStyle(el).overflowY;\n if (oy === 'auto' || oy === 'scroll' || oy === 'hidden') {\n const r = el.getBoundingClientRect();\n top = Math.max(top, r.top);\n bottom = Math.min(bottom, r.bottom);\n break;\n }\n el = el.parentElement;\n }\n return { top, bottom };\n }\n\n private _menuItems(): HTMLButtonElement[] {\n return Array.from(\n this._el.nativeElement.querySelectorAll('.mm-item:not([disabled])')\n ) as HTMLButtonElement[];\n }\n\n private _trigger(): HTMLButtonElement | null {\n return this._el.nativeElement.querySelector('.mm-trigger') as HTMLButtonElement | null;\n }\n}\n","import { Injectable } from '@angular/core';\n\n/**\n * Open overlays (ui-modal, ui-side-panel) in the order they opened. Both listen for keydown on the whole\n * document, so only the TOPMOST one may react to Escape / trap Tab — otherwise Escape on a confirm modal\n * stacked over a side panel closes both. Body scroll is unlocked only when the last overlay closes.\n */\n@Injectable({ providedIn: 'root' })\nexport class OverlayStack {\n private readonly stack: object[] = [];\n\n push(overlay: object): void {\n this.remove(overlay);\n this.stack.push(overlay);\n }\n\n remove(overlay: object): void {\n const i = this.stack.indexOf(overlay);\n if (i >= 0) this.stack.splice(i, 1);\n }\n\n isTop(overlay: object): boolean {\n return this.stack.length > 0 && this.stack[this.stack.length - 1] === overlay;\n }\n\n get isEmpty(): boolean {\n return this.stack.length === 0;\n }\n}\n","/**\n * Initial-focus helpers shared by ui-side-panel and ui-modal (internal — not exported from the public API).\n *\n * Order: an `[autofocus]` / `[data-autofocus]` target in the body → the first usable form control in the\n * body → the dialog container itself. The header's close button is never chosen. On phones / coarse\n * pointers the container is focused instead of a field so no on-screen keyboard or iOS zoom is forced.\n */\n\nconst NON_TEXT_INPUT_TYPES = new Set(['checkbox', 'radio', 'file', 'button', 'submit', 'reset', 'image', 'hidden']);\n\n/** `(max-width: 480px)` or `(pointer: coarse)` — SSR / old-browser safe. */\nexport function isTouchViewport(doc: Document): boolean {\n const w = doc.defaultView;\n return !!w?.matchMedia && (w.matchMedia('(max-width: 480px)').matches || w.matchMedia('(pointer: coarse)').matches);\n}\n\n/** The native control behind an autofocus marker: the element itself or its first descendant control. */\nexport function nativeControl(el: HTMLElement): HTMLElement | null {\n if (/^(INPUT|SELECT|TEXTAREA|BUTTON)$/.test(el.tagName)) return el;\n return el.querySelector('input, select, textarea, button') as HTMLElement | null;\n}\n\n/** Enabled, visible, editable, not hidden from assistive tech, and a real text-like control. */\nexport function isUsableField(el: HTMLElement): boolean {\n const c = el as HTMLInputElement;\n if (c.disabled || c.readOnly) return false;\n if (el.tagName === 'INPUT' && NON_TEXT_INPUT_TYPES.has((c.type || '').toLowerCase())) return false;\n if (el.closest('[hidden], [aria-hidden=\"true\"], [inert]')) return false;\n if (!el.getClientRects().length) return false;\n const view = el.ownerDocument.defaultView;\n return !view || view.getComputedStyle(el).visibility !== 'hidden';\n}\n\n/** Elements that take part in the Tab trap: focusable and not `tabindex=\"-1\"`. */\nexport function tabbables(root: HTMLElement): HTMLElement[] {\n return (Array.from(root.querySelectorAll(\n 'button:not([disabled]), [href], input:not([disabled]), select:not([disabled]), textarea:not([disabled]), [tabindex]:not([tabindex=\"-1\"])'\n )) as HTMLElement[]).filter(el => el.getAttribute('tabindex') !== '-1');\n}\n\n/** Moves focus per the order above; returns the element that ended up focused. */\nexport function focusInitial(dialog: HTMLElement | null, body: HTMLElement | null, doc: Document): void {\n if (!dialog) return;\n if (isTouchViewport(doc) || !body) { dialog.focus(); return; }\n\n const tryFocus = (el: HTMLElement | null): boolean => {\n if (!el) return false;\n el.focus();\n return doc.activeElement === el;\n };\n\n for (const marker of Array.from(body.querySelectorAll('[autofocus], [data-autofocus]')) as HTMLElement[]) {\n const control = nativeControl(marker);\n if (!control) continue;\n const usable = control.tagName === 'BUTTON'\n ? !(control as HTMLButtonElement).disabled && control.getClientRects().length > 0\n : isUsableField(control);\n if (usable && tryFocus(control)) return;\n }\n\n const fields = Array.from(body.querySelectorAll('input, select, textarea')) as HTMLElement[];\n for (const f of fields) {\n if (isUsableField(f) && tryFocus(f)) return;\n }\n dialog.focus();\n}\n","/**\n * Tracks the visual viewport (the part of the page not covered by the on-screen keyboard) and publishes it as CSS\n * custom properties on `host`:\n * --ui-vvh : visible height in px\n * --ui-kb : px covered at the bottom of the layout viewport (keyboard height), 0 when no keyboard\n * Overlays use them so their footer stays above the keyboard. SSR-safe (no-op without a window / visualViewport).\n * Returns a cleanup function that removes the listeners and the properties.\n */\nexport function trackVisualViewport(win: Window | null | undefined, host: HTMLElement): () => void {\n const vv = win?.visualViewport;\n if (!win || !vv) return () => {};\n const update = () => {\n const covered = Math.max(0, Math.round(win.innerHeight - (vv.offsetTop + vv.height)));\n host.style.setProperty('--ui-vvh', `${Math.round(vv.height)}px`);\n host.style.setProperty('--ui-kb', `${covered}px`);\n };\n update();\n vv.addEventListener('resize', update);\n vv.addEventListener('scroll', update);\n return () => {\n vv.removeEventListener('resize', update);\n vv.removeEventListener('scroll', update);\n host.style.removeProperty('--ui-vvh');\n host.style.removeProperty('--ui-kb');\n };\n}\n","import { Component, ChangeDetectionStrategy, input, output, inject, ElementRef, Renderer2, OnInit, OnDestroy, effect } from '@angular/core';\nimport { DOCUMENT } from '@angular/common';\nimport { OverlayStack } from '../utils/overlay-stack';\nimport { focusInitial, tabbables } from '../utils/overlay-focus';\nimport { trackVisualViewport } from '../utils/visual-viewport';\n\n@Component({\n selector: 'ui-modal',\n standalone: true,\n changeDetection: ChangeDetectionStrategy.OnPush,\n host: { '(document:keydown)': 'onKeydown($event)' },\n template: `\n @if (open()) {\n <div class=\"m-backdrop\" (click)=\"close.emit()\"></div>\n <div class=\"m-modal\" role=\"dialog\" aria-modal=\"true\" tabindex=\"-1\" [attr.aria-labelledby]=\"titleId\" [style.max-width]=\"maxWidth()\">\n <div class=\"m-header\">\n <div class=\"m-header-text\">\n <h3 class=\"m-title\" [id]=\"titleId\">{{ title() }}</h3>\n @if (description()) {\n <p class=\"m-description\">{{ description() }}</p>\n }\n </div>\n <button class=\"m-close\" type=\"button\" aria-label=\"Close\" (click)=\"close.emit()\">\n <svg width=\"18\" height=\"18\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\" aria-hidden=\"true\">\n <line x1=\"18\" y1=\"6\" x2=\"6\" y2=\"18\"/><line x1=\"6\" y1=\"6\" x2=\"18\" y2=\"18\"/>\n </svg>\n </button>\n </div>\n <div class=\"m-body\" [style.overflow-y]=\"bodyOverflow()\">\n <ng-content />\n </div>\n <div class=\"m-footer\">\n <div class=\"m-footer-left\"><ng-content select=\"[modal-footer-left]\" /></div>\n <div class=\"m-footer-right\"><ng-content select=\"[modal-footer-right]\" /></div>\n </div>\n </div>\n }\n `,\n styles: [`\n .m-backdrop {\n position: fixed; inset: 0;\n background: rgba(2, 8, 23, 0.45);\n backdrop-filter: blur(4px);\n -webkit-backdrop-filter: blur(4px);\n z-index: 200;\n animation: m-fade 180ms ease;\n }\n .m-modal:focus { outline: none; }\n .m-modal {\n position: fixed; top: 50%; left: 50%; transform: translate(-50%, -50%);\n background: #fff;\n border-radius: 16px;\n border: 1px solid rgba(0,0,0,0.07);\n z-index: 201;\n width: calc(100% - 32px);\n box-shadow: 0 4px 6px rgba(0,0,0,0.04), 0 16px 32px rgba(0,0,0,0.12), 0 48px 96px rgba(0,0,0,0.10);\n max-height: calc(100vh - 48px);\n display: flex; flex-direction: column;\n animation: m-scale 220ms cubic-bezier(0.16, 1, 0.3, 1);\n }\n .m-header {\n display: flex; align-items: flex-start; justify-content: space-between;\n padding: 20px 24px 16px;\n background: #f8fafc;\n border-bottom: 1px solid #e2e8f0;\n border-radius: 16px 16px 0 0;\n flex-shrink: 0;\n }\n .m-header-text { display: flex; flex-direction: column; gap: 3px; flex: 1; min-width: 0; }\n .m-title {\n font-size: 1.125rem; font-weight: 700; color: #0f172a; margin: 0;\n letter-spacing: -0.02em; line-height: 1.3;\n }\n .m-description {\n font-size: 0.8125rem; color: #64748b; margin: 0; line-height: 1.4;\n }\n .m-close {\n display: flex; align-items: center; justify-content: center;\n width: 30px; height: 30px; border-radius: 8px; border: none;\n background: none; cursor: pointer; color: #94a3b8; flex-shrink: 0; margin-left: 12px;\n transition: background 130ms, color 130ms;\n &:hover { background: #f1f5f9; color: #1e293b; }\n &:focus-visible { outline: 2px solid #3b82f6; outline-offset: 2px; }\n }\n .m-body {\n padding: 20px 24px; overflow-y: auto; flex: 1; min-height: 0;\n font-size: 0.9rem; color: #475569; line-height: 1.65;\n }\n .m-footer {\n padding: 14px 24px 18px;\n background: #f8fafc;\n border-top: 1px solid #e2e8f0;\n border-radius: 0 0 16px 16px;\n flex-shrink: 0;\n display: flex; align-items: center; justify-content: space-between; gap: 8px;\n &:not(:has(*)) { display: none; }\n }\n .m-footer-left { display: flex; align-items: center; gap: 8px; }\n .m-footer-right { display: flex; align-items: center; gap: 8px; margin-left: auto; }\n\n @media (max-width: 480px) {\n .m-modal {\n top: auto; bottom: var(--ui-kb, 0px); left: 0; right: 0; transform: none;\n width: 100vw; max-width: none !important; max-height: min(85dvh, var(--ui-vvh, 100dvh));\n border-radius: 20px 20px 0 0; animation: m-up 240ms cubic-bezier(0.16, 1, 0.3, 1);\n }\n .m-footer {\n border-radius: 0; flex-direction: column-reverse; align-items: stretch;\n padding-bottom: max(18px, env(safe-area-inset-bottom));\n }\n }\n\n @keyframes m-fade { from { opacity: 0; } to { opacity: 1; } }\n @keyframes m-scale {\n from { opacity: 0; transform: translate(-50%, -48%) scale(0.96); }\n to { opacity: 1; transform: translate(-50%, -50%) scale(1); }\n }\n @keyframes m-up { from { transform: translateY(100%); } to { transform: translateY(0); } }\n `],\n})\nexport class ModalComponent implements OnInit, OnDestroy {\n readonly open = input(false);\n readonly title = input('');\n readonly description = input('');\n readonly maxWidth = input('460px');\n readonly bodyOverflow = input<'auto' | 'visible' | 'hidden'>('auto');\n readonly close = output<void>();\n\n protected readonly titleId = `m-title-${Math.random().toString(36).slice(2, 8)}`;\n\n private readonly el = inject(ElementRef);\n private readonly ren = inject(Renderer2);\n private readonly doc = inject(DOCUMENT);\n\n private readonly _overlays = inject(OverlayStack);\n private _prevFocus: HTMLElement | null = null;\n private _scrollLocked = false;\n private _stopViewport: (() => void) | null = null;\n\n constructor() {\n effect(() => {\n if (this.open()) {\n this._overlays.push(this);\n this._prevFocus = this.doc.activeElement as HTMLElement;\n this.doc.body.style.overflow = 'hidden';\n this._scrollLocked = true;\n this._stopViewport?.();\n this._stopViewport = trackVisualViewport(this.doc.defaultView, this.el.nativeElement);\n setTimeout(() => this._focusFirst(), 50);\n } else if (this._scrollLocked) {\n this._overlays.remove(this);\n // Another overlay (e.g. the side panel under a confirm modal) is still open — keep the page locked.\n if (this._overlays.isEmpty) this.doc.body.style.overflow = '';\n this._scrollLocked = false;\n this._stopViewport?.();\n this._stopViewport = null;\n this._prevFocus?.focus();\n this._prevFocus = null;\n }\n });\n }\n\n onKeydown(event: KeyboardEvent) {\n // Only the topmost open overlay handles Escape / Tab (see OverlayStack).\n if (!this.open() || !this._overlays.isTop(this)) return;\n if (event.key === 'Escape') {\n event.preventDefault();\n this.close.emit();\n return;\n }\n if (event.key === 'Tab') {\n const focusable = this._focusable();\n if (!focusable.length) { event.preventDefault(); return; }\n const first = focusable[0];\n const last = focusable[focusable.length - 1];\n if (event.shiftKey) {\n const active = this.doc.activeElement;\n // Focus on the dialog container itself: Shift+Tab must wrap, not leave the modal.\n if (active === first || active === this.el.nativeElement.querySelector('.m-modal')) { event.preventDefault(); last.focus(); }\n } else {\n if (this.doc.activeElement === last) { event.preventDefault(); first.focus(); }\n }\n }\n }\n\n /** Initial focus — see `focusInitial` (autofocus target → first usable field → dialog container). */\n private _focusFirst() {\n focusInitial(\n this.el.nativeElement.querySelector('.m-modal'),\n this.el.nativeElement.querySelector('.m-body'),\n this.doc,\n );\n }\n\n private _focusable(): HTMLElement[] {\n return tabbables(this.el.nativeElement);\n }\n\n ngOnInit(): void {\n this.ren.appendChild(this.doc.body, this.el.nativeElement);\n }\n\n ngOnDestroy(): void {\n this._stopViewport?.();\n this._stopViewport = null;\n this._overlays.remove(this);\n if (this._scrollLocked && this._overlays.isEmpty) this.doc.body.style.overflow = '';\n const el = this.el.nativeElement as HTMLElement;\n if (el.parentNode === this.doc.body) {\n this.ren.removeChild(this.doc.body, el);\n }\n }\n}\n","import { Component, input, model, signal, computed, forwardRef, ChangeDetectionStrategy } from '@angular/core';\nimport { ControlValueAccessor, NG_VALUE_ACCESSOR } from '@angular/forms';\n\n@Component({\n selector: 'ui-checkbox',\n standalone: true,\n changeDetection: ChangeDetectionStrategy.OnPush,\n providers: [{\n provide: NG_VALUE_ACCESSOR,\n useExisting: forwardRef(() => CheckboxComponent),\n multi: true,\n }],\n template: `\n <label class=\"cb-wrap\" [class.cb-disabled]=\"disabled()\">\n <span class=\"cb-box\" [class.cb-checked]=\"checked()\" [class.cb-indeterminate]=\"indeterminate() && !checked()\" [class.cb-disabled]=\"disabled()\">\n @if (checked()) {\n <svg width=\"13\" height=\"13\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"#fff\" stroke-width=\"3\" stroke-linecap=\"round\" stroke-linejoin=\"round\" aria-hidden=\"true\">\n <path d=\"M20 6 9 17l-5-5\"/>\n </svg>\n } @else if (indeterminate()) {\n <svg width=\"10\" height=\"2\" viewBox=\"0 0 10 2\" fill=\"none\" aria-hidden=\"true\">\n <line x1=\"0\" y1=\"1\" x2=\"10\" y2=\"1\" stroke=\"#fff\" stroke-width=\"2\" stroke-linecap=\"round\"/>\n </svg>\n }\n </span>\n <input\n type=\"checkbox\"\n class=\"cb-hidden\"\n [checked]=\"checked()\"\n [disabled]=\"disabled()\"\n [attr.aria-label]=\"ariaLabel() || null\"\n [attr.aria-checked]=\"indeterminate() && !checked() ? 'mixed' : null\"\n (change)=\"onToggle($event)\"\n />\n @if (label()) {\n <span class=\"cb-label\">{{ label() }}</span>\n }\n <ng-content />\n </label>\n `,\n styles: [`\n .cb-wrap {\n display: inline-flex;\n align-items: center;\n gap: 9px;\n cursor: pointer;\n user-select: none;\n &.cb-disabled { cursor: not-allowed; opacity: 0.5; }\n }\n\n /* Visually hidden but accessible to keyboard/screen readers */\n .cb-hidden {\n position: absolute; width: 1px; height: 1px;\n margin: -1px; padding: 0; overflow: hidden;\n clip: rect(0, 0, 0, 0); white-space: nowrap; border: 0;\n }\n\n /* Show focus ring on the visual box when the hidden input is focused via keyboard */\n .cb-wrap:has(.cb-hidden:focus-visible) .cb-box {\n outline: 2px solid #3b82f6;\n outline-offset: 2px;\n }\n\n .cb-box {\n flex-shrink: 0;\n width: 20px;\n height: 20px;\n border: 2px solid #cbd5e1;\n border-radius: 5px;\n background: #fff;\n display: flex;\n align-items: center;\n justify-content: center;\n transition: background 0.12s, border-color 0.12s;\n\n &.cb-checked, &.cb-indeterminate {\n background: #3b82f6;\n border-color: #3b82f6;\n }\n\n &:not(.cb-checked):not(.cb-disabled) {\n .cb-wrap:hover & { border-color: #3b82f6; }\n }\n }\n\n /* Touch: grow the hit area to 44x44 around the 20px box without moving it (no layout shift). */\n @media (max-width: 640px), (pointer: coarse) {\n .cb-box { position: relative; }\n .cb-box::after { content: ''; position: absolute; inset: -12px; }\n }\n\n .cb-label {\n font-size: 0.875rem;\n color: #334155;\n line-height: 1.4;\n }\n `],\n})\nexport class CheckboxComponent implements ControlValueAccessor {\n readonly label = input('');\n /** Accessible name for the native checkbox when there is no visible `label` (e.g. a table row's selector). */\n readonly ariaLabel = input('');\n readonly indeterminate = input(false);\n readonly checked = model(false);\n\n /** Public so a host template can bind `[disabled]` (a protected input fails strict template type-checking, TS2445). */\n readonly _disabledInput = input(false, { alias: 'disabled' });\n private readonly _cvaDisabled = signal(false);\n protected readonly disabled = computed(() => this._disabledInput() || this._cvaDisabled());\n\n private onChange = (_: boolean) => {};\n private onTouched = () => {};\n\n writeValue(val: boolean) { this.checked.set(!!val); }\n registerOnChange(fn: (v: boolean) => void) { this.onChange = fn; }\n registerOnTouched(fn: () => void) { this.onTouched = fn; }\n setDisabledState(d: boolean) { this._cvaDisabled.set(d); }\n\n onToggle(event: Event) {\n if (this.disabled()) return;\n const val = (event.target as HTMLInputElement).checked;\n this.checked.set(val);\n this.onChange(val);\n this.onTouched();\n }\n}\n","import { Component, ChangeDetectionStrategy, Input, input } from '@angular/core';\n\n@Component({\n selector: 'ui-button',\n standalone: true,\n changeDetection: ChangeDetectionStrategy.OnPush,\n template: `\n <button\n [type]=\"type()\"\n [class]=\"'btn btn-' + variant() + ' btn-' + size()\"\n [class.btn-full]=\"fullWidth()\"\n [class.active]=\"active()\"\n [disabled]=\"disabled() || loading()\"\n [attr.aria-label]=\"ariaLabel()\"\n [attr.aria-expanded]=\"ariaExpanded()\"\n [attr.aria-pressed]=\"ariaPressed()\"\n [style.background]=\"color() || null\"\n [style.border-color]=\"color() || null\"\n >\n @if (loading()) {\n <span class=\"spinner\"></span>\n }\n <ng-content />\n </button>\n `,\n styles: [`\n :host { display: inline-flex; }\n .btn {\n display: inline-flex; align-items: center; justify-content: center; gap: 6px;\n border: none; border-radius: 8px; font-weight: 600; cursor: pointer;\n font-family: inherit; transition: all 150ms ease; white-space: nowrap;\n &:disabled { opacity: 0.5; cursor: not-allowed; }\n &:focus-visible { outline: 2px solid #3b82f6; outline-offset: 2px; }\n }\n .btn-full { width: 100%; }\n\n .btn-primary {\n background: #3B82F6; color: #fff; border: 1px solid #3B82F6;\n &:hover:not(:disabled) { background: #2563EB; border-color: #2563EB; }\n }\n .btn-secondary {\n background: #fff; color: #334155; border: 1px solid #e2e8f0;\n &:hover:not(:disabled) { background: #f8fafc; border-color: #cbd5e1; }\n }\n .btn-ghost {\n background: transparent; color: #64748b; border: 1px solid transparent;\n &:hover:not(:disabled) { background: #f8fafc; color: #334155; }\n }\n .btn-danger {\n background: #dc2626; color: #fff; border: 1px solid #dc2626;\n &:hover:not(:disabled) { background: #b91c1c; border-color: #b91c1c; }\n }\n .btn-link {\n background: none; color: #3b82f6; border: none; padding: 0; font-weight: 500;\n &:hover:not(:disabled) { color: #60a5fa; }\n &.btn-xs, &.btn-sm, &.btn-md, &.btn-lg { padding: 0; }\n }\n .btn-warning {\n background: #f97316; color: #fff; border: 1px solid #f97316;\n &:hover:not(:disabled) { background: #ea6c0a; border-color: #ea6c0a; }\n }\n .btn-tab {\n background: none; color: #64748b; border: none; border-bottom: 2px solid transparent;\n border-radius: 0; font-weight: 600; font-size: 0.875rem; transition: all 0.15s;\n &:hover:not(:disabled) { background: none; color: #1e293b; }\n &.active { color: #3b82f6; border-bottom-color: #3b82f6; }\n &.btn-xs, &.btn-sm, &.btn-md, &.btn-lg { padding: 10px 20px; border-radius: 0; }\n }\n .btn-icon {\n width: 28px; height: 28px; padding: 0; border-radius: 6px;\n background: transparent; border: none; color: #94a3b8;\n &:hover:not(:disabled) { background: #f1f5f9; color: #334155; }\n }\n .btn-icon-danger {\n width: 28px; height: 28px; padding: 0; border-radius: 6px;\n background: transparent; border: none; color: #94a3b8;\n &:hover:not(:disabled) { background: #fee2e2; color: #dc2626; }\n }\n\n .btn-xs { padding: 3px 8px; font-size: 0.75rem; border-radius: 5px; gap: 4px; }\n .btn-sm { padding: 6px 12px; font-size: 0.8125rem; border-radius: 6px; }\n .btn-md { padding: 10px 16px; font-size: 0.875rem; }\n .btn-lg { padding: 14px 20px; font-size: 1rem; }\n\n /* Touch targets: >= 44px hit height on touch devices and phones (desktop unchanged). Link buttons are inline text and\n keep their line height; icon buttons keep their 28px look but get a 44px hit area through ::after. */\n @media (pointer: coarse), (max-width: 480px) {\n .btn-xs, .btn-sm, .btn-md, .btn-lg, .btn-tab { min-height: 44px; }\n .btn-link.btn-xs, .btn-link.btn-sm, .btn-link.btn-md, .btn-link.btn-lg { min-height: 0; }\n .btn-icon, .btn-icon-danger { position: relative; }\n .btn-icon::after, .btn-icon-danger::after { content: ''; position: absolute; inset: -8px; }\n }\n\n .spinner {\n width: 16px; height: 16px; border: 2px solid rgba(255,255,255,0.3);\n border-top-color: currentColor; border-radius: 50%; animation: spin 0.6s linear infinite;\n }\n @keyframes spin { to { transform: rotate(360deg); } }\n `],\n})\nexport class ButtonComponent {\n readonly variant = input<'primary' | 'secondary' | 'ghost' | 'danger' | 'link' | 'warning' | 'tab' | 'icon' | 'icon-danger'>('primary');\n readonly color = input<string | undefined>(undefined);\n readonly active = input(false);\n readonly size = input<'xs' | 'sm' | 'md' | 'lg'>('md');\n readonly type = input<'button' | 'submit' | 'reset'>('button');\n readonly disabled = input(false);\n readonly loading = input(false);\n readonly fullWidth = input(false);\n readonly ariaLabel = input<string | undefined>(undefined);\n readonly ariaExpanded = input<boolean | undefined>(undefined);\n readonly ariaPressed = input<boolean | undefined>(undefined);\n // Content-projection slot markers used by ui-modal — declared here so Angular's\n // template type-checker doesn't emit unknown-attribute warnings.\n @Input('modal-footer-left') _footerLeft?: string;\n @Input('modal-footer-right') _footerRight?: string;\n}\n","import { Signal, WritableSignal, computed, effect, signal } from '@angular/core';\nimport { ValidationErrors } from '@angular/forms';\n\n/**\n * Shared validity contract of the form inputs (`ui-text-input`, `ui-textarea-input`, `ui-select`,\n * `ui-date-input`, `ui-datetime-input`).\n *\n * Every input provides this token, so a host can read the validity of all the fields in its template\n * without reimplementing any rule:\n *\n * ```ts\n * readonly fields = viewChildren(UiFieldValidity);\n * readonly hasInvalid = computed(() => this.fields().some(f => f.invalid()));\n * ```\n */\nexport abstract class UiFieldValidity {\n /** True while the field's current value breaks a built-in rule (or the host passed a non-empty `[error]`). Not gated on touched. */\n abstract readonly invalid: Signal<boolean>;\n /** The message of the first broken built-in rule ('' when valid), whether or not it is currently shown. */\n abstract readonly errorMessage: Signal<string>;\n}\n\n/** One broken rule: `key` is the Angular validation-error key reported on the form control. */\nexport interface FieldIssue {\n key: string;\n message: string;\n value?: unknown;\n}\n\n/** Maps an issue to the `ValidationErrors` a `Validator` returns. */\nexport function issueToErrors(issue: FieldIssue | null): ValidationErrors | null {\n return issue ? { [issue.key]: issue.value ?? true } : null;\n}\n\n/** \"Name is required.\" / \"This field is required.\" */\nexport function requiredIssue(label: string): FieldIssue {\n return { key: 'required', message: `${label.trim() || 'This field'} is required.` };\n}\n\n/** \"Too long (max N characters).\" when the trimmed text is longer than `max`. */\nexport function maxLengthIssue(text: string, max: number | undefined | null): FieldIssue | null {\n if (max == null || max < 0 || text.length <= max) return null;\n return { key: 'maxlength', message: `Too long (max ${max} characters).`, value: { requiredLength: max, actualLength: text.length } };\n}\n\n/**\n * Touched / reveal bookkeeping shared by the inputs. Internal messages are shown only once the field was blurred or the\n * host sets `showErrors`. Switching `showErrors` from true back to false (the host re-initialising its form) clears the\n * touched state again. Must be called from an injection context.\n */\nexport function createFieldReveal(showErrors: Signal<boolean>): { touched: WritableSignal<boolean>; revealed: Signal<boolean> } {\n const touched = signal(false);\n let previous = false;\n effect(() => {\n const show = showErrors();\n if (previous && !show) touched.set(false);\n previous = show;\n });\n return { touched, revealed: computed(() => touched() || showErrors()) };\n}\n\nconst MONTHS = ['Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun', 'Jul', 'Aug', 'Sep', 'Oct', 'Nov', 'Dec'];\n\n/** Splits `YYYY-MM-DD` (optionally followed by `Thh:mm[:ss]`) and rejects impossible dates (2026-02-31) and times (25:00). */\nexport function parseIso(value: string, withTime: boolean): { y: number; mo: number; d: number; h: number; mi: number } | null {\n const m = /^(\\d{4})-(\\d{2})-(\\d{2})(?:[T ](\\d{2}):(\\d{2})(?::\\d{2}(?:\\.\\d+)?)?)?$/.exec(value.trim());\n if (!m) return null;\n if (withTime && m[4] === undefined) return null;\n if (!withTime && m[4] !== undefined) return null;\n const y = Number(m[1]), mo = Number(m[2]), d = Number(m[3]);\n const h = m[4] === undefined ? 0 : Number(m[4]);\n const mi = m[5] === undefined ? 0 : Number(m[5]);\n if (mo < 1 || mo > 12 || d < 1 || d > new Date(Date.UTC(y, mo, 0)).getUTCDate()) return null;\n if (h > 23 || mi > 59) return null;\n return { y, mo, d, h, mi };\n}\n\n/** \"Oct 20, 2026\" from an ISO date (never shifted by the time zone). */\nexport function formatIsoDate(value: string): string {\n const p = parseIso(value, false);\n return p ? `${MONTHS[p.mo - 1]} ${p.d}, ${p.y}` : value;\n}\n\n/** \"Oct 20, 2026, 3:30 PM\" from an ISO local date-time. */\nexport function formatIsoDateTime(value: string): string {\n const p = parseIso(value, true);\n if (!p) return value;\n const hour = p.h % 12 === 0 ? 12 : p.h % 12;\n return `${MONTHS[p.mo - 1]} ${p.d}, ${p.y}, ${hour}:${String(p.mi).padStart(2, '0')} ${p.h < 12 ? 'AM' : 'PM'}`;\n}\n\n/** Sortable key `YYYYMMDDHHmm` of a parsed ISO value. */\nexport function isoKey(p: { y: number; mo: number; d: number; h: number; mi: number }): number {\n return ((((p.y * 100 + p.mo) * 100 + p.d) * 100 + p.h) * 100) + p.mi;\n}\n","import { Component, ChangeDetectionStrategy, input, output, signal, computed, effect, forwardRef } from '@angular/core';\nimport { AbstractControl, ControlValueAccessor, NG_VALIDATORS, NG_VALUE_ACCESSOR, ValidationErrors, Validator } from '@angular/forms';\nimport { FieldIssue, UiFieldValidity, createFieldReveal, issueToErrors, maxLengthIssue, requiredIssue } from '../utils/field-validation';\n\n@Component({\n selector: 'ui-text-input',\n standalone: true,\n changeDetection: ChangeDetectionStrategy.OnPush,\n providers: [{\n provide: NG_VALUE_ACCESSOR,\n useExisting: forwardRef(() => TextInputComponent),\n multi: true,\n }, {\n provide: NG_VALIDATORS,\n useExisting: forwardRef(() => TextInputComponent),\n multi: true,\n }, {\n provide: UiFieldValidity,\n useExisting: forwardRef(() => TextInputComponent),\n }],\n template: `\n <div class=\"ti-field\" [class.ti-error]=\"_activeError()\" [class.ti-disabled]=\"disabled()\">\n @if (label()) {\n <label class=\"ti-label\" [for]=\"_inputId\">\n {{ label() }}\n @if (required()) { <span class=\"ti-required\" aria-hidden=\"true\">*</span> }\n </label>\n }\n <input\n class=\"ti-input\"\n [id]=\"_inputId\"\n [type]=\"type()\"\n [attr.inputmode]=\"type() === 'tel' ? 'tel' : null\"\n [placeholder]=\"placeholder()\"\n [value]=\"_value()\"\n [disabled]=\"disabled()\"\n [readOnly]=\"readonly()\"\n [attr.maxlength]=\"maxlength() ?? null\"\n [attr.autocomplete]=\"autocomplete()\"\n [attr.required]=\"required() || null\"\n [attr.aria-label]=\"ariaLabel() || null\"\n [attr.aria-invalid]=\"_activeError() ? 'true' : null\"\n [attr.aria-describedby]=\"(_activeError() || hint()) ? _inputId + '-desc' : null\"\n (input)=\"onInput($event)\"\n (focus)=\"onFocus()\"\n (blur)=\"onBlur()\"\n />\n @if (_activeError()) {\n <p class=\"ti-desc ti-desc-error\" [id]=\"_inputId + '-desc'\" role=\"alert\">{{ _activeError() }}</p>\n } @else if (hint()) {\n <p class=\"ti-desc\" [id]=\"_inputId + '-desc'\">{{ hint() }}</p>\n }\n </div>\n `,\n styles: [`\n :host { display: block; }\n\n .ti-field { display: flex; flex-direction: column; gap: 6px; }\n\n .ti-label {\n font-size: 0.8125rem; font-weight: 600; color: #374151; line-height: 1;\n }\n .ti-required { color: #ef4444; margin-left: 2px; }\n\n .ti-input {\n width: 100%; box-sizing: border-box;\n padding: 9px 12px; font-size: 0.875rem; font-family: inherit;\n color: #1e293b; background: #fff;\n border: 1px solid #e2e8f0; border-radius: 8px;\n outline: none; transition: border-color 0.15s, box-shadow 0.15s;\n &::placeholder { color: #94a3b8; }\n &:focus {\n border-color: #3b82f6;\n box-shadow: 0 0 0 3px rgba(59, 130, 246, 0.12);\n }\n &:read-only { background: #f8fafc; cursor: default; }\n }\n\n .ti-error .ti-input {\n border-color: #ef4444;\n &:focus { box-shadow: 0 0 0 3px rgba(239, 68, 68, 0.12); }\n }\n\n .ti-disabled .ti-input { opacity: 0.5; cursor: not-allowed; }\n\n .ti-desc { margin: 0; font-size: 0.75rem; line-height: 1.4; color: #64748b; }\n .ti-desc-error { color: #ef4444; }\n\n @media (max-width: 480px) { .ti-input { font-size: 1rem; } }\n @media (pointer: coarse), (max-width: 480px) { .ti-input { min-height: 44px; } }\n `],\n})\n/**\n * Text field. Works with `[(ngModel)]` and reactive forms (ControlValueAccessor).\n *\n * Built-in validation (all derived from the inputs below, no host code):\n * - `required` -> \"<label> is required.\" (or \"This field is required.\") when empty.\n * - `maxlength` -> \"Too long (max N characters).\" when the trimmed value is longer (typing is still capped natively,\n * but a value pasted or set programmatically from a loaded record is flagged).\n * - `type=\"email\"` -> \"Please enter a valid email address.\"\n * - `type=\"tel\"` -> formats as a US phone number (like `phoneFormat`) and requires 10 digits (optional leading 1);\n * a value starting with \"+\" is treated as international (8-15 digits). \"Please enter a valid phone number.\"\n * `phoneFormat` on its own (any other type) only formats, it does not validate.\n * - `type=\"number\"` with `min` / `max` / `maxDecimals` -> \"Must be at least X\" / \"Must be at most X\" /\n * \"Use at most N decimal places\"; text that is not a number -> \"Please enter a valid number.\"\n * Empty values are valid unless `required`. Disabled / readonly fields are never invalid.\n *\n * Validity API (same on every ui input):\n * - The component is an Angular `Validator`, so `ngModel` / `FormControl` report `.invalid` / `.errors`\n * (keys: required, maxlength, email, phone, number, min, max, decimals).\n * - Signals `invalid` and `errorMessage` plus the `invalidChange` output; the token `UiFieldValidity` lets a host collect\n * them with `viewChildren(UiFieldValidity)`.\n * - Messages are only displayed after the field was blurred or while `[showErrors]=\"true\"` (set it on a failed Save click).\n * Setting `showErrors` back to false also clears the touched state. A host-provided `[error]` always wins.\n */\nexport class TextInputComponent implements ControlValueAccessor, Validator, UiFieldValidity {\n readonly label = input('');\n /** Accessible name for a field with no visible label (e.g. an inline cell or title input). */\n readonly ariaLabel = input('');\n readonly placeholder = input('');\n readonly type = input<'text' | 'email' | 'password' | 'tel' | 'url' | 'number'>('text');\n readonly maxlength = input<number | undefined>(undefined);\n readonly required = input(false);\n readonly autocomplete = input('off');\n readonly readonly = input(false);\n readonly hint = input('');\n readonly error = input('');\n /** When true, automatically formats the value as a US phone number as the user types. */\n readonly phoneFormat = input(false);\n /** `type=\"number\"` only: smallest allowed value. */\n readonly min = input<number | undefined>(undefined);\n /** `type=\"number\"` only: largest allowed value. */\n readonly max = input<number | undefined>(undefined);\n /** `type=\"number\"` only: most digits allowed after the decimal point. */\n readonly maxDecimals = input<number | undefined>(undefined);\n /** Reveal the built-in error messages now (e.g. after a failed Save click), without waiting for blur. */\n readonly showErrors = input(false);\n\n readonly focused = output<void>();\n readonly blurred = output<void>();\n /** Emits whenever the field becomes invalid / valid. */\n readonly invalidChange = output<boolean>();\n\n protected readonly _disabledInput = input(false, { alias: 'disabled' });\n private readonly _cvaDisabled = signal(false);\n protected readonly disabled = computed(() => this._disabledInput() || this._cvaDisabled());\n\n protected readonly _value = signal('');\n private readonly _reveal = createFieldReveal(this.showErrors);\n private readonly _formatting = computed(() => this.phoneFormat() || this.type() === 'tel');\n private readonly _issue = computed(() => this._check(this._value()));\n /** True while the value breaks a built-in rule or the host passed an `[error]`. */\n readonly invalid = computed(() => !!this.error() || !!this._issue());\n /** Message of the first broken built-in rule ('' when valid), shown or not. */\n readonly errorMessage = computed(() => this._issue()?.message ?? '');\n protected readonly _activeError = computed(() => this.error() || (this._reveal.revealed() ? this.errorMessage() : ''));\n protected readonly _inputId = `ti-${Math.random().toString(36).slice(2, 8)}`;\n\n private static readonly _EMAIL_RE = /^[^\\s@]+@[^\\s@]+\\.[^\\s@]+$/;\n\n private _onChange = (_: string) => {};\n private _onTouched = () => {};\n private _onValidatorChange = () => {};\n\n constructor() {\n effect(() => { this.invalidChange.emit(this.invalid()); });\n }\n\n /** Re-run the form control's validation when a rule input (required, maxlength, min, options, ...) changes. */\n ngOnChanges() { this._onValidatorChange(); }\n\n writeValue(val: string | number | null) {\n const text = val == null ? '' : String(val);\n this._value.set(this._formatting() ? TextInputComponent._formatPhone(text) : text);\n }\n validate(control: AbstractControl): ValidationErrors | null {\n const v = control.value;\n return issueToErrors(this._check(v == null ? '' : String(v)));\n }\n registerOnValidatorChange(fn: () => void) { this._onValidatorChange = fn; }\n registerOnChange(fn: (v: string) => void) { this._onChange = fn; }\n registerOnTouched(fn: () => void) { this._onTouched = fn; }\n setDisabledState(d: boolean) { this._cvaDisabled.set(d); }\n\n protected onInput(event: Event) {\n const raw = (event.target as HTMLInputElement).value;\n const val = this._formatting() ? TextInputComponent._formatPhone(raw) : raw;\n this._value.set(val);\n this._onChange(val);\n }\n\n protected onFocus() { this.focused.emit(); }\n protected onBlur() {\n this._onTouched();\n this.blurred.emit();\n this._reveal.touched.set(true);\n }\n\n /** First broken built-in rule for `value`, or null. */\n private _check(value: string): FieldIssue | null {\n if (this.disabled() || this.readonly()) return null;\n const text = value.trim();\n if (!text) return this.required() ? requiredIssue(this.label()) : null;\n const tooLong = maxLengthIssue(text, this.maxlength());\n if (tooLong) return tooLong;\n switch (this.type()) {\n case 'email':\n return TextInputComponent._EMAIL_RE.test(text) ? null : { key: 'email', message: 'Please enter a valid email address.' };\n case 'tel':\n return TextInputComponent._validPhone(text) ? null : { key: 'phone', message: 'Please enter a valid phone number.' };\n case 'number':\n return this._checkNumber(text);\n default:\n return null;\n }\n }\n\n private _checkNumber(text: string): FieldIssue | null {\n const n = Number(text);\n if (!Number.isFinite(n)) return { key: 'number', message: 'Please enter a valid number.' };\n const min = this.min(), max = this.max(), decimals = this.maxDecimals();\n if (min != null && n < min) return { key: 'min', message: `Must be at least ${min}`, value: { min, actual: n } };\n if (max != null && n > max) return { key: 'max', message: `Must be at most ${max}`, value: { max, actual: n } };\n if (decimals != null && !/e/i.test(text) && (text.split('.')[1]?.length ?? 0) > decimals) {\n return { key: 'decimals', message: `Use at most ${decimals} decimal place${decimals === 1 ? '' : 's'}`, value: { maxDecimals: decimals } };\n }\n return null;\n }\n\n private static _validPhone(text: string): boolean {\n const digits = text.replace(/\\D/g, '');\n if (text.startsWith('+')) return digits.length >= 8 && digits.length <= 15;\n return digits.length === 10 || (digits.length === 11 && digits.startsWith('1'));\n }\n\n static _formatPhone(raw: string): string {\n if (!raw) return '';\n if (raw.startsWith('+')) return raw; // leave international numbers alone\n const digits = raw.replace(/\\D/g, '');\n const d = digits.startsWith('1') && digits.length === 11 ? digits.slice(1) : digits.slice(0, 10);\n if (d.length <= 3) return d;\n if (d.length <= 6) return `(${d.slice(0, 3)}) ${d.slice(3)}`;\n return `(${d.slice(0, 3)}) ${d.slice(3, 6)}-${d.slice(6)}`;\n }\n}\n","import { Component, input, output, signal, computed, inject, ChangeDetectionStrategy } from '@angular/core';\nimport { FormsModule } from '@angular/forms';\nimport { ColumnDef, SavedView, ToolbarAction } from '../data-table/data-table.types';\nimport { ModalComponent } from '../modal/modal.component';\nimport { CheckboxComponent } from '../checkbox/checkbox.component';\nimport { ButtonComponent } from '../button/button.component';\nimport { TextInputComponent } from '../text-input/text-input.component';\n\n@Component({\n selector: 'ui-table-toolbar',\n standalone: true,\n changeDetection: ChangeDetectionStrategy.OnPush,\n imports: [FormsModule, ModalComponent, CheckboxComponent, ButtonComponent, TextInputComponent],\n host: {\n '(document:click)': 'onDocumentClick()',\n '(document:keydown.escape)': 'onEscape()',\n },\n template: `\n <div class=\"toolbar\">\n\n <!-- ── Standalone buttons (primary = blue, secondary = outlined) -->\n @for (btn of standaloneButtons(); track btn.type) {\n <button\n [class]=\"btn.variant === 'primary' ? 'toolbar-primary-btn' : 'toolbar-btn'\"\n [attr.aria-pressed]=\"btn.pressed ?? null\"\n (click)=\"emitAction(btn.type, $event)\">\n @if (btn.icon) {\n <svg width=\"13\" height=\"13\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\" aria-hidden=\"true\"><path [attr.d]=\"btn.icon\"/></svg>\n }\n {{ btn.label }}\n @if (btn.badge) { <span class=\"toolbar-badge\">{{ btn.badge }}</span> }\n </button>\n }\n\n <!-- ── More Actions dropdown (hidden entirely when it would be empty, e.g. read-only with no saved views) -->\n @if (hasMoreItems() && !hideMoreActions()) {\n <div class=\"toolbar-item\">\n <button\n class=\"toolbar-btn\"\n [class.toolbar-btn-active]=\"moreOpen()\"\n (click)=\"toggleMore($event)\"\n aria-label=\"More actions\"\n [attr.aria-expanded]=\"moreOpen()\">\n More Actions\n <svg width=\"12\" height=\"12\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\" aria-hidden=\"true\"><polyline points=\"6 9 12 15 18 9\"/></svg>\n </button>\n\n @if (moreOpen()) {\n <div class=\"more-dropdown\" (click)=\"$event.stopPropagation()\">\n\n <!-- 1. Export (when in More Actions) -->\n @if (exportDropdownButton(); as exp) {\n <button class=\"more-option\" (click)=\"emitAction(exp.type, $event)\">\n @if (exp.icon) {\n <svg width=\"14\" height=\"14\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\" aria-hidden=\"true\"><path [attr.d]=\"exp.icon\"/></svg>\n }\n {{ exp.label }}\n </button>\n }\n\n <!-- 2. Import -->\n @if (importButton(); as imp) {\n <button class=\"more-option\" (click)=\"emitAction(imp.type, $event)\">\n @if (imp.icon) {\n <svg width=\"14\" height=\"14\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\" aria-hidden=\"true\"><path [attr.d]=\"imp.icon\"/></svg>\n }\n {{ imp.label }}\n @if (imp.badge) { <span class=\"more-badge\">{{ imp.badge }}</span> }\n </button>\n }\n\n <!-- 3. Columns (not for read-only users) -->\n @if (!readOnly()) {\n <button class=\"more-option\" (click)=\"openColModal($event)\">\n <svg width=\"14\" height=\"14\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\" aria-hidden=\"true\"><line x1=\"8\" y1=\"6\" x2=\"21\" y2=\"6\"/><line x1=\"8\" y1=\"12\" x2=\"21\" y2=\"12\"/><line x1=\"8\" y1=\"18\" x2=\"21\" y2=\"18\"/><line x1=\"3\" y1=\"6\" x2=\"3.01\" y2=\"6\"/><line x1=\"3\" y1=\"12\" x2=\"3.01\" y2=\"12\"/><line x1=\"3\" y1=\"18\" x2=\"3.01\" y2=\"18\"/></svg>\n Columns\n </button>\n }\n\n <!-- 3. Save Current View (full permission only) 4. Saved Views (read-only: only when they have some) -->\n @if (showSavedViews() && !readOnly()) {\n <button class=\"more-option\" (click)=\"saveView(); moreOpen.set(false)\">\n <svg width=\"14\" height=\"14\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\" aria-hidden=\"true\"><path d=\"M19 21H5a2 2 0 0 1-2-2V5a2 2 0 0 1 2-2h11l5 5v11a2 2 0 0 1-2 2z\"/><polyline points=\"17 21 17 13 7 13 7 21\"/></svg>\n Save Current View\n </button>\n }\n @if (showSavedViewsItem()) {\n <button class=\"more-option\" (click)=\"openViewModal($event)\">\n <svg width=\"14\" height=\"14\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\" aria-hidden=\"true\"><path d=\"M9 3H5a2 2 0 0 0-2 2v4m6-6h10a2 2 0 0 1 2 2v4M9 3v18m0 0h10a2 2 0 0 0 2-2V9M9 21H5a2 2 0 0 1-2-2V9m0 0h18\"/></svg>\n Saved Views\n @if (activeViewId()) { <span class=\"more-view-name\">{{ activeViewName() }}</span> }\n </button>\n }\n\n <!-- 5. Additional page-specific options (alphabetical) -->\n @for (btn of additionalDropdownButtons(); track btn.type) {\n <button class=\"more-option\" (click)=\"emitAction(btn.type, $event)\">\n @if (btn.icon) {\n <svg width=\"14\" height=\"14\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\" aria-hidden=\"true\"><path [attr.d]=\"btn.icon\"/></svg>\n }\n {{ btn.label }}\n @if (btn.badge) { <span class=\"more-badge\">{{ btn.badge }}</span> }\n </button>\n }\n\n </div>\n }\n </div>\n }\n </div>\n\n <!-- ── Columns Modal -->\n <ui-modal [open]=\"colModalOpen()\" title=\"Columns\" description=\"Toggle columns on or off, then click Apply to update your table.\" maxWidth=\"520px\" (close)=\"closeColModal()\">\n <div class=\"col-list\">\n @for (col of configurableColumns(); track col.key) {\n <ui-checkbox\n class=\"col-option\"\n [checked]=\"isVisible(col.key)\"\n [label]=\"col.label\"\n (checkedChange)=\"toggleColumn(col.key)\" />\n }\n </div>\n <ui-button modal-footer-left variant=\"secondary\" size=\"sm\" (click)=\"closeColModal()\">Cancel</ui-button>\n <ui-button modal-footer-right variant=\"primary\" size=\"sm\" (click)=\"applyColumns()\"><svg width=\"14\" height=\"14\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\"><polyline points=\"20 6 9 17 4 12\"/></svg> Apply</ui-button>\n </ui-modal>\n\n <!-- ── Saved Views Modal -->\n @if (showSavedViews()) {\n <ui-modal [open]=\"viewModalOpen()\" title=\"Saved Views\" maxWidth=\"520px\" (close)=\"viewModalOpen.set(false)\">\n @if (savedViews().length === 0) {\n <p class=\"view-empty\">No saved views yet.</p>\n } @else {\n <div class=\"view-list\">\n @for (view of sortedViews(); track view.id) {\n <div class=\"view-option\" [class.view-active]=\"view.id === activeViewId()\">\n <button class=\"view-select-btn\" (click)=\"loadView(view)\">{{ view.name }}</button>\n @if (!view.isSystem && !readOnly()) {\n <button class=\"view-delete-btn\" [attr.aria-label]=\"'Delete ' + view.name\" (click)=\"deleteView(view.id)\">\n <svg width=\"12\" height=\"12\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\"><line x1=\"18\" y1=\"6\" x2=\"6\" y2=\"18\"/><line x1=\"6\" y1=\"6\" x2=\"18\" y2=\"18\"/></svg>\n </button>\n }\n </div>\n }\n </div>\n }\n <ui-button modal-footer-right variant=\"secondary\" size=\"sm\" (click)=\"viewModalOpen.set(false)\"><svg width=\"14\" height=\"14\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\"><polyline points=\"20 6 9 17 4 12\"/></svg> Done</ui-button>\n </ui-modal>\n }\n\n <!-- ── Save View Name Modal -->\n <ui-modal [open]=\"saveNameModalOpen()\" title=\"Save View\" maxWidth=\"360px\" (close)=\"closeSaveNameModal()\">\n <ui-text-input label=\"View Name\" [(ngModel)]=\"_saveViewName\" placeholder=\"My Custom View\" [maxlength]=\"100\" autocomplete=\"off\" />\n <ui-button modal-footer-left variant=\"secondary\" size=\"sm\" (click)=\"closeSaveNameModal()\">Cancel</ui-button>\n <ui-button modal-footer-right variant=\"primary\" size=\"sm\" [disabled]=\"!_saveViewName.trim()\" (click)=\"confirmSaveName()\"><svg width=\"14\" height=\"14\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\"><polyline points=\"20 6 9 17 4 12\"/></svg> Save</ui-button>\n </ui-modal>\n `,\n styles: [`\n :host { display: inline-flex; max-width: 100%; }\n .toolbar { display: flex; align-items: center; gap: 6px; flex-wrap: wrap; }\n .toolbar-item { position: relative; }\n\n /* ── Shared button ─── */\n .toolbar-btn {\n display: inline-flex; align-items: center; gap: 6px;\n padding: 6px 10px; border-radius: 6px;\n border: 1px solid #e2e8f0; background: #fff;\n font-size: 0.8125rem; font-weight: 500; color: #334155;\n cursor: pointer; font-family: inherit; white-space: nowrap;\n transition: background 0.1s, border-color 0.1s;\n &:hover { background: #f8fafc; }\n &.toolbar-btn-active { border-color: #1e293b; background: #f8fafc; }\n &:focus-visible { outline: 2px solid #60a5fa; outline-offset: 2px; }\n }\n .toolbar-badge {\n display: inline-flex; align-items: center; justify-content: center;\n background: #1e293b; color: #fff; border-radius: 999px;\n font-size: 0.625rem; font-weight: 700; min-width: 16px; height: 16px;\n padding: 0 4px;\n }\n\n /* ── Primary (blue) standalone button ─── */\n .toolbar-primary-btn {\n display: inline-flex; align-items: center; gap: 6px;\n padding: 6px 12px; border-radius: 6px; border: none;\n background: #2563eb; color: #fff;\n font-size: 0.8125rem; font-weight: 600; cursor: pointer;\n font-family: inherit; white-space: nowrap;\n transition: background 0.1s;\n &:hover { background: #1d4ed8; }\n &:focus-visible { outline: 2px solid #60a5fa; outline-offset: 2px; }\n }\n\n /* ── More Actions dropdown ─── */\n .more-dropdown {\n position: absolute; top: calc(100% + 6px); right: 0; z-index: 200;\n background: #fff; border: 1px solid #e2e8f0; border-radius: 10px;\n box-shadow: 0 4px 6px rgba(0,0,0,0.04), 0 12px 28px rgba(0,0,0,0.10);\n min-width: 200px; overflow: hidden;\n }\n .more-option {\n display: flex; align-items: center; gap: 10px; width: 100%;\n padding: 10px 14px; background: none; border: none; border-bottom: 1px solid #f1f5f9;\n font-size: 0.875rem; color: #1e293b; font-family: inherit;\n text-align: left; cursor: pointer; transition: background 0.1s;\n &:last-child { border-bottom: none; }\n &:hover { background: #f8fafc; }\n &:focus-visible { outline: 2px solid #3b82f6; outline-offset: -2px; }\n }\n .more-badge {\n margin-left: auto; display: inline-flex; align-items: center; justify-content: center;\n background: #1e293b; color: #fff; border-radius: 999px;\n font-size: 0.625rem; font-weight: 700; min-width: 16px; height: 16px; padding: 0 4px;\n }\n .more-divider { height: 1px; background: #f1f5f9; margin: 2px 0; }\n .more-view-name {\n font-size: 0.75rem; color: #64748b; max-width: 100px;\n overflow: hidden; text-overflow: ellipsis; white-space: nowrap;\n }\n\n /* ── Columns modal content ─── */\n .col-list {\n display: grid; grid-template-columns: 1fr 1fr;\n max-height: 340px; overflow-y: auto;\n }\n @media (max-width: 480px) {\n .col-list { grid-template-columns: 1fr; }\n }\n\n /* ── iPad / tablet (≤1024px) — 36px+ touch targets ─── */\n @media (max-width: 1024px) {\n .toolbar-btn { padding: 8px 12px; }\n .toolbar-primary-btn { padding: 8px 14px; }\n }\n\n /* ── Touch (incl. iPad): 44px tap targets, desktop mouse sizes unchanged ─── */\n @media (pointer: coarse) {\n .toolbar-btn, .toolbar-primary-btn { min-height: 44px; }\n }\n\n /* ── Phone (≤640px) — full-width host, 40px+ touch targets, safe dropdown height ─── */\n @media (max-width: 640px) {\n :host { display: flex; width: 100%; }\n .toolbar { width: 100%; justify-content: flex-end; }\n .toolbar-btn { padding: 10px 12px; font-size: 0.8125rem; min-height: 44px; }\n .toolbar-primary-btn { padding: 10px 14px; font-size: 0.8125rem; min-height: 44px; }\n .more-dropdown { max-height: calc(100vh - 140px); overflow-y: auto; }\n .more-option { padding: 12px 16px; font-size: 0.9375rem; }\n }\n .col-option {\n display: flex; padding: 9px 4px;\n border-bottom: 1px solid #f1f5f9;\n transition: background 0.1s; border-radius: 6px;\n &:hover { background: #f8fafc; }\n /* hide border on last visible row (last 1 or 2 items) */\n &:nth-last-child(-n+2) { border-bottom: none; }\n }\n\n /* ── Views modal content ─── */\n .view-list { max-height: 360px; overflow-y: auto; margin: 0 -24px; }\n .view-empty { padding: 16px 24px; font-size: 0.9rem; color: #94a3b8; margin: 0; }\n .view-option {\n display: flex; align-items: center; padding-right: 16px;\n border-bottom: 1px solid #f1f5f9;\n transition: background 0.1s;\n &:last-child { border-bottom: none; }\n &:hover { background: #f8fafc; }\n &.view-active { background: #eff6ff; }\n }\n .view-select-btn {\n flex: 1; padding: 12px 8px 12px 24px; background: none; border: none;\n font-size: 0.9rem; color: #334155; cursor: pointer; font-family: inherit; text-align: left;\n }\n .view-delete-btn {\n display: flex; align-items: center; justify-content: center;\n width: 28px; height: 28px; border-radius: 5px;\n background: none; border: none; cursor: pointer; color: #94a3b8;\n &:hover { color: #dc2626; background: #fef2f2; }\n }\n\n `],\n})\nexport class TableToolbarComponent {\n readonly columns = input.required<ColumnDef[]>();\n readonly visibleColumns = input.required<string[]>();\n readonly lockedColumn = input('');\n readonly savedViews = input<SavedView[]>([]);\n readonly activeViewId = input<string | null>(null);\n readonly showSavedViews = input(false);\n /**\n * Read-only (permission isn't 'full'): only Export stays — no page buttons, no Import, no Columns, no\n * Save Current View, no deleting views. More Actions offers only the user's existing Saved Views and is\n * hidden when they have none.\n */\n readonly readOnly = input(false);\n /** Hides the More Actions dropdown (the table sets it while it is empty and unfiltered, leaving only the primary buttons). */\n readonly hideMoreActions = input(false);\n\n readonly tableButtons = input<ToolbarAction[]>([]);\n\n readonly visibleColumnsChange = output<string[]>();\n readonly saveViewClick = output<string>();\n readonly loadViewClick = output<SavedView>();\n readonly deleteViewClick = output<string>();\n readonly tableButtonClick = output<string>();\n\n protected readonly colModalOpen = signal(false);\n protected readonly viewModalOpen = signal(false);\n protected readonly moreOpen = signal(false);\n protected readonly saveNameModalOpen = signal(false);\n protected _saveViewName = '';\n\n /** Draft column visibility — only applied to the table when the user clicks Apply. */\n private readonly _pendingCols = signal<string[]>([]);\n\n /** Buttons this user may see — read-only users only get Export (page actions and Import are hidden). */\n private readonly _allowedButtons = computed(() =>\n this.readOnly() ? this.tableButtons().filter(b => b.type === 'export') : this.tableButtons(),\n );\n\n protected readonly standaloneButtons = computed(() =>\n this._allowedButtons()\n .filter(b => (b.variant === 'primary' || b.variant === 'secondary') && !b.hidden)\n .sort((a, b) => {\n // Primary buttons always render before secondary — regardless of definition order\n if (a.variant === b.variant) return 0;\n return a.variant === 'primary' ? -1 : 1;\n }),\n );\n protected readonly importButton = computed(() =>\n this._allowedButtons().find(b => b.type === 'import' && !b.hidden) ?? null,\n );\n protected readonly exportDropdownButton = computed(() =>\n this._allowedButtons().find(b => b.type === 'export' && !b.variant && !b.hidden) ?? null,\n );\n protected readonly additionalDropdownButtons = computed(() =>\n this._allowedButtons()\n .filter(b => !b.variant && !b.hidden && b.type !== 'import' && b.type !== 'export')\n .sort((a, b) => a.label.localeCompare(b.label, undefined, { sensitivity: 'base' })),\n );\n\n /** Saved Views entry: always for full users; for read-only users only when they already have views. */\n protected readonly showSavedViewsItem = computed(() =>\n this.showSavedViews() && (!this.readOnly() || this.savedViews().length > 0),\n );\n\n /** True when More Actions would contain at least one item. */\n protected readonly hasMoreItems = computed(() =>\n !!this.exportDropdownButton() || !!this.importButton() || !this.readOnly()\n || this.showSavedViewsItem() || this.additionalDropdownButtons().length > 0,\n );\n\n protected readonly configurableColumns = computed(() =>\n this.columns().filter(c => c.key !== this.lockedColumn()),\n );\n\n protected readonly hiddenCount = computed(() =>\n this.configurableColumns().filter(c => !this.visibleColumns().includes(c.key)).length,\n );\n\n protected readonly activeViewName = computed(() =>\n this.savedViews().find(v => v.id === this.activeViewId())?.name ?? '',\n );\n\n protected readonly sortedViews = computed(() => {\n const views = [...this.savedViews()];\n return views.sort((a, b) => {\n if (a.isSystem !== b.isSystem) return a.isSystem ? -1 : 1;\n return a.name.localeCompare(b.name, undefined, { sensitivity: 'base' });\n });\n });\n\n protected isVisible(key: string): boolean {\n return this._pendingCols().includes(key);\n }\n\n protected toggleColumn(key: string) {\n this._pendingCols.update(cols =>\n cols.includes(key) ? cols.filter(k => k !== key) : [...cols, key],\n );\n }\n\n protected applyColumns() {\n this.visibleColumnsChange.emit(this._pendingCols());\n this.colModalOpen.set(false);\n }\n\n protected closeColModal() {\n this.colModalOpen.set(false);\n }\n\n protected loadView(view: SavedView) {\n this.loadViewClick.emit(view);\n this.viewModalOpen.set(false);\n }\n\n protected deleteView(id: string) {\n if (this.readOnly()) return;\n this.deleteViewClick.emit(id);\n }\n\n protected saveView() {\n if (this.readOnly()) return;\n this._saveViewName = '';\n this.saveNameModalOpen.set(true);\n }\n\n protected confirmSaveName() {\n if (this.readOnly() || !this._saveViewName.trim()) return;\n this.saveViewClick.emit(this._saveViewName.trim());\n this.saveNameModalOpen.set(false);\n this._saveViewName = '';\n }\n\n protected closeSaveNameModal() {\n this.saveNameModalOpen.set(false);\n this._saveViewName = '';\n }\n\n protected openColModal(event: Event) {\n event.stopPropagation();\n this.moreOpen.set(false);\n this._pendingCols.set([...this.visibleColumns()]);\n this.colModalOpen.set(true);\n }\n\n protected openViewModal(event: Event) {\n event.stopPropagation();\n this.moreOpen.set(false);\n this.viewModalOpen.set(true);\n }\n\n protected emitAction(type: string, event: Event) {\n event.stopPropagation();\n this.moreOpen.set(false);\n this.tableButtonClick.emit(type);\n }\n\n protected toggleMore(event: Event) {\n event.stopPropagation();\n this.moreOpen.update(v => !v);\n }\n\n protected onDocumentClick() {\n this.moreOpen.set(false);\n }\n\n protected onEscape() {\n this.moreOpen.set(false);\n }\n}\n","/**\n * Filters selected IDs to only include records eligible for a given bulk action.\n * Returns { eligibleIds, skippedCount } so the UI can warn the user if some were skipped.\n *\n * Usage:\n * const { eligibleIds, skippedCount } = getEligibleIds(\n * contacts,\n * selectedIds,\n * 'change-status',\n * { targetValue: 'active' }\n * );\n */\n\nexport interface EligibleResult {\n eligibleIds: string[];\n skippedCount: number;\n}\n\nexport interface EligibleOptions {\n targetValue?: string;\n targetTag?: string;\n}\n\n/**\n * Filters a list of options to only include values that would actually change\n * at least one of the selected items. Removes any option where ALL selected\n * items already have that value (no-op options).\n *\n * Usage:\n * const opts = getEligibleOptions(statusOptions, selectedItems, 'status');\n */\nexport function getEligibleOptions<T>(\n options: { value: string; label: string; [key: string]: any }[],\n selectedItems: T[],\n field: keyof T,\n): { value: string; label: string; [key: string]: any }[] {\n if (selectedItems.length === 0) return options;\n // Compared as text so boolean / numeric fields match their string option values ('true', '3').\n return options.filter(opt => !selectedItems.every(item => String(item[field] ?? '') === opt.value));\n}\n\nexport function getEligibleIds<T extends { id: string; status?: string; type?: string; tags?: { name: string }[] }>(\n items: T[],\n selectedIds: Set<string>,\n action: string,\n options: EligibleOptions = {},\n): EligibleResult {\n const selected = items.filter(item => selectedIds.has(item.id));\n let eligible: T[];\n\n switch (action) {\n case 'change-status':\n // Skip records that already have the target status\n eligible = selected.filter(item => item.status !== options.targetValue);\n break;\n\n case 'change-type':\n // Skip records that already have the target type\n eligible = selected.filter(item => item.type !== options.targetValue);\n break;\n\n case 'change-property-type':\n // Skip records that already have the target propertyType (Property model uses propertyType, not type)\n eligible = selected.filter(item => (item as any).propertyType !== options.targetValue);\n break;\n\n case 'change-side':\n // Skip records that already have the target side\n eligible = selected.filter(item => (item as any).side !== options.targetValue);\n break;\n\n case 'add-tag':\n // Skip records that already have the target tag\n eligible = selected.filter(item =>\n !item.tags?.some(t => t.name.toLowerCase() === options.targetTag?.toLowerCase())\n );\n break;\n\n case 'remove-tag':\n // Only include records that have the target tag\n eligible = selected.filter(item =>\n item.tags?.some(t => t.name.toLowerCase() === options.targetTag?.toLowerCase())\n );\n break;\n\n case 'archive':\n // Skip records already archived\n eligible = selected.filter(item =>\n (item as any).lifecycleState !== 'ARCHIVED' && (item as any).archived !== true && item.status !== 'ARCHIVED'\n );\n break;\n\n case 'deprecate':\n // Skip records already deprecated\n eligible = selected.filter(item => (item as any).lifecycleState !== 'DEPRECATED');\n break;\n\n case 'restore':\n // Skip records that are not archived (nothing to restore)\n eligible = selected.filter(item =>\n (item as any).lifecycleState === 'ARCHIVED' || (item as any).archived === true\n );\n break;\n\n case 'change-tier':\n // Skip records that already have the target tier\n eligible = selected.filter(item => (item as any).tier !== options.targetValue);\n break;\n\n case 'change-stage':\n // Skip records that already have the target stage\n eligible = selected.filter(item => (item as any).stage !== options.targetValue);\n break;\n\n case 'change-priority':\n // Skip records that already have the target priority\n eligible = selected.filter(item => (item as any).priority !== options.targetValue);\n break;\n\n case 'activate':\n // Skip records already active\n eligible = selected.filter(item => !(item as any).active);\n break;\n\n case 'pause':\n // Skip records already paused/inactive\n eligible = selected.filter(item => !!(item as any).active);\n break;\n\n case 'change-category':\n // Skip records that already have the target category\n eligible = selected.filter(item => (item as any).categoryName !== options.targetValue);\n break;\n\n case 'complete':\n // Skip records already completed\n eligible = selected.filter(item => !(item as any).completed);\n break;\n\n case 'incomplete':\n // Skip records already incomplete\n eligible = selected.filter(item => !!(item as any).completed);\n break;\n\n case 'bulk-disable':\n // Only active members can be disabled\n eligible = selected.filter(item => item.status === 'active');\n break;\n\n case 'bulk-enable':\n // Only disabled members can be re-enabled\n eligible = selected.filter(item => item.status === 'disabled');\n break;\n\n case 'resend-invite':\n // Only invited members can have their invite resent\n eligible = selected.filter(item => item.status === 'invited');\n break;\n\n case 'change-agent':\n // Skip records already assigned to the target agent (null targetValue means clearing — skip already-null)\n eligible = selected.filter(item => (item as any).assignedAgentName !== (options.targetValue ?? null));\n break;\n\n case 'change-source':\n // Skip records already having the target lead source (null targetValue means clearing — skip already-null)\n eligible = selected.filter(item => (item as any).leadSource !== (options.targetValue ?? null));\n break;\n\n case 'change-expense-category':\n eligible = selected.filter(item => (item as any).category !== options.targetValue);\n break;\n\n case 'change-expense-payment-method':\n eligible = selected.filter(item => (item as any).paymentMethod !== options.targetValue);\n break;\n\n case 'change-tax-deductible':\n eligible = selected.filter(item => String((item as any).taxDeductible) !== options.targetValue);\n break;\n\n case 'change-recurring':\n eligible = selected.filter(item => String((item as any).recurring) !== options.targetValue);\n break;\n\n case 'share':\n // Only include records that are not yet shared\n eligible = selected.filter(item => !(item as any).isPublic);\n break;\n\n case 'unshare':\n // Only include records that are currently shared\n eligible = selected.filter(item => !!(item as any).isPublic);\n break;\n\n case 'pay-fee':\n // Skip fees already paid or cancelled\n eligible = selected.filter(item => item.status !== 'paid' && item.status !== 'cancelled');\n break;\n\n case 'change-template-type':\n // Skip checklist templates that already have the target transactionType\n eligible = selected.filter(item => (item as any).transactionType !== options.targetValue);\n break;\n\n case 'change-template-category':\n // Skip checklist templates that already have the target category\n eligible = selected.filter(item => (item as any).category !== options.targetValue);\n break;\n\n case 'change-template-required':\n // Skip checklist templates where required already matches the target boolean (passed as string 'true'/'false')\n eligible = selected.filter(item => String((item as any).required) !== options.targetValue);\n break;\n\n case 'change-transaction-status':\n // Same as change-status but reads transactionStatus field (compliance summary model)\n eligible = selected.filter(item => (item as any).transactionStatus !== options.targetValue);\n break;\n\n case 'change-transaction-type':\n // Same as change-type but reads transactionType field (compliance summary model)\n eligible = selected.filter(item => (item as any).transactionType !== options.targetValue);\n break;\n\n case 'change-role':\n // Skip team members already at the target access role\n eligible = selected.filter(item => (item as any).role !== options.targetValue);\n break;\n\n case 'change-team-role':\n // Skip team members already at the target position\n eligible = selected.filter(item => (item as any).teamRole !== options.targetValue);\n break;\n\n case 'compliance-submit':\n // Deals with at least one pending item (totalItems > submitted + approved + waived)\n eligible = selected.filter(item =>\n (item as any).totalItems > (item as any).submittedItems + (item as any).approvedItems + (item as any).waivedItems\n );\n break;\n\n case 'compliance-approve':\n // Deals not already fully compliant\n eligible = selected.filter(item => (item as any).complianceStatus !== 'compliant');\n break;\n\n case 'compliance-waive':\n // Deals with at least one item not yet approved or waived\n eligible = selected.filter(item =>\n (item as any).totalItems > (item as any).approvedItems + (item as any).waivedItems\n );\n break;\n\n case 'compliance-reset':\n // Deals with at least one non-pending item (submitted, approved, or waived)\n eligible = selected.filter(item =>\n (item as any).submittedItems + (item as any).approvedItems + (item as any).waivedItems > 0\n );\n break;\n\n case 'compliance-apply-template':\n // All deals are candidates; backend skips those whose items already match the template by name\n eligible = selected;\n break;\n\n case 'enable':\n // Only inactive items benefit from being enabled\n eligible = selected.filter(item => (item as any).isActive === false);\n break;\n\n case 'disable':\n // Only active items benefit from being disabled\n eligible = selected.filter(item => (item as any).isActive === true);\n break;\n\n case 'delete':\n // All selected are eligible for deletion\n eligible = selected;\n break;\n\n case 'send-email':\n // Skip records with no email\n eligible = selected.filter(item => !!(item as any).email);\n break;\n\n case 'send-text':\n // Skip records with no phone\n eligible = selected.filter(item => !!(item as any).phone);\n break;\n\n case 'complete-follow-up':\n // Any call without a recent completion is eligible — follow-ups can repeat\n eligible = selected.filter(item => !(item as any).followUpCompletedAt);\n break;\n\n default:\n eligible = selected;\n }\n\n return {\n eligibleIds: eligible.map(item => item.id),\n skippedCount: selected.length - eligible.length,\n };\n}\n","import { Component, ChangeDetectionStrategy, input } from '@angular/core';\n\n@Component({\n selector: 'ui-skeleton',\n standalone: true,\n changeDetection: ChangeDetectionStrategy.OnPush,\n template: `<div class=\"sk\" [style.height.px]=\"height()\" [style.width]=\"width()\" [style.border-radius.px]=\"radius()\" aria-hidden=\"true\"></div>`,\n host: { '[style.margin-bottom.px]': 'mb() || null', 'aria-hidden': 'true' },\n styles: [`\n .sk {\n position: relative;\n overflow: hidden;\n background: #f1f5f9;\n }\n .sk::after {\n content: '';\n position: absolute;\n inset: 0;\n background: linear-gradient(90deg, transparent 25%, rgba(255,255,255,0.65) 50%, transparent 75%);\n transform: translateX(-100%);\n animation: sk-shimmer 1.5s ease-in-out infinite;\n }\n @keyframes sk-shimmer { to { transform: translateX(200%); } }\n `],\n})\nexport class SkeletonComponent {\n readonly height = input(20);\n readonly width = input('100%');\n readonly radius = input(8);\n readonly mb = input(0);\n}\n","import { Component, ChangeDetectionStrategy, input, computed } from '@angular/core';\n\nexport type ProgressBarVariant = 'blue' | 'green' | 'amber' | 'red' | 'slate';\n\n/**\n * Horizontal progress bar with an optional trailing label (e.g. \"3/5\").\n * Pass `value` and `max`; the fill width is clamped to 0–100%.\n */\n@Component({\n selector: 'ui-progress-bar',\n standalone: true,\n changeDetection: ChangeDetectionStrategy.OnPush,\n template: `\n <div class=\"pb\"\n role=\"progressbar\"\n [attr.aria-valuemin]=\"0\"\n [attr.aria-valuemax]=\"max()\"\n [attr.aria-valuenow]=\"clampedValue()\"\n [attr.aria-label]=\"ariaLabel() || label() || null\"\n [attr.title]=\"tooltip() || null\">\n <div class=\"pb-track\"><div class=\"pb-fill\" [class]=\"'pb-fill pb-' + variant()\" [style.width.%]=\"percent()\"></div></div>\n @if (label()) { <span class=\"pb-label\">{{ label() }}</span> }\n </div>\n `,\n styles: [`\n :host { display: block; }\n .pb { display: flex; align-items: center; gap: 8px; min-width: 100px; }\n .pb-track { flex: 1; height: 6px; background: #e2e8f0; border-radius: 3px; overflow: hidden; }\n .pb-fill { height: 100%; border-radius: 3px; transition: width 0.3s; }\n .pb-blue { background: #3b82f6; }\n .pb-green { background: #16a34a; }\n .pb-amber { background: #f59e0b; }\n .pb-red { background: #ef4444; }\n .pb-slate { background: #94a3b8; }\n .pb-label { font-size: 0.75rem; font-weight: 500; color: #64748b; white-space: nowrap; }\n `],\n})\nexport class ProgressBarComponent {\n /** Completed amount. */\n readonly value = input(0);\n /** Total amount; 0 renders an empty bar. */\n readonly max = input(100);\n /** Text shown to the right of the bar, e.g. \"3/5\". */\n readonly label = input('');\n readonly variant = input<ProgressBarVariant>('blue');\n /** Native hover tooltip. */\n readonly tooltip = input('');\n readonly ariaLabel = input('');\n\n protected readonly clampedValue = computed(() => Math.min(Math.max(this.value(), 0), this.max()));\n protected readonly percent = computed(() => this.max() > 0 ? Math.round((this.clampedValue() / this.max()) * 100) : 0);\n}\n","import { Component, ChangeDetectionStrategy, input, output, signal, computed, effect, forwardRef } from '@angular/core';\nimport { AbstractControl, ControlValueAccessor, NG_VALIDATORS, NG_VALUE_ACCESSOR, ValidationErrors, Validator } from '@angular/forms';\nimport { FieldIssue, UiFieldValidity, createFieldReveal, issueToErrors, requiredIssue } from '../utils/field-validation';\n\nexport interface SelectOption {\n value: string;\n label: string;\n}\n\n@Component({\n selector: 'ui-select',\n standalone: true,\n changeDetection: ChangeDetectionStrategy.OnPush,\n providers: [{\n provide: NG_VALUE_ACCESSOR,\n useExisting: forwardRef(() => SelectComponent),\n multi: true,\n }, {\n provide: NG_VALIDATORS,\n useExisting: forwardRef(() => SelectComponent),\n multi: true,\n }, {\n provide: UiFieldValidity,\n useExisting: forwardRef(() => SelectComponent),\n }],\n template: `\n <div class=\"sl-field\" [class.sl-error]=\"_activeError()\" [class.sl-disabled]=\"disabled()\">\n @if (label()) {\n <label class=\"sl-label\" [for]=\"_selectId\">\n {{ label() }}\n @if (required()) { <span class=\"sl-required\" aria-hidden=\"true\">*</span> }\n </label>\n }\n <div class=\"sl-wrap\">\n <select\n [class]=\"'sl-select' + (size() === 'sm' ? ' sl-select-sm' : '')\"\n [id]=\"_selectId\"\n [disabled]=\"disabled()\"\n [attr.required]=\"required() || null\"\n [attr.aria-label]=\"ariaLabel() || null\"\n [attr.aria-invalid]=\"_activeError() ? 'true' : null\"\n [attr.aria-describedby]=\"(_activeError() || hint()) ? _selectId + '-desc' : null\"\n (change)=\"onChange($event)\"\n (blur)=\"onBlur()\">\n @if (placeholder()) {\n <option value=\"\" [disabled]=\"true\" [selected]=\"_value() === ''\">{{ placeholder() }}</option>\n }\n @for (opt of options(); track opt.value) {\n <option [value]=\"opt.value\" [selected]=\"_value() === opt.value\">{{ opt.label }}</option>\n }\n </select>\n <svg class=\"sl-chevron\" width=\"14\" height=\"14\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2.5\" aria-hidden=\"true\"><polyline points=\"6 9 12 15 18 9\"/></svg>\n </div>\n @if (_activeError()) {\n <p class=\"sl-desc sl-desc-error\" [id]=\"_selectId + '-desc'\" role=\"alert\">{{ _activeError() }}</p>\n } @else if (hint()) {\n <p class=\"sl-desc\" [id]=\"_selectId + '-desc'\">{{ hint() }}</p>\n }\n </div>\n `,\n styles: [`\n :host { display: block; }\n\n .sl-field { display: flex; flex-direction: column; gap: 6px; }\n\n .sl-label {\n font-size: 0.8125rem; font-weight: 600; color: #374151; line-height: 1;\n }\n .sl-required { color: #ef4444; margin-left: 2px; }\n\n .sl-wrap { position: relative; display: flex; align-items: center; }\n\n .sl-select {\n width: 100%; box-sizing: border-box;\n padding: 9px 32px 9px 12px; font-size: 0.875rem; font-family: inherit;\n color: #1e293b; background: #fff;\n border: 1px solid #e2e8f0; border-radius: 8px;\n outline: none; appearance: none; cursor: pointer;\n transition: border-color 0.15s, box-shadow 0.15s;\n &:focus {\n border-color: #3b82f6;\n box-shadow: 0 0 0 3px rgba(59, 130, 246, 0.12);\n }\n }\n\n .sl-error .sl-select {\n border-color: #ef4444;\n &:focus { box-shadow: 0 0 0 3px rgba(239, 68, 68, 0.12); }\n }\n\n .sl-disabled .sl-select { opacity: 0.5; cursor: not-allowed; }\n\n .sl-select-sm { padding: 6px 32px 6px 12px; font-size: 0.8125rem; border-radius: 6px; }\n\n .sl-chevron {\n position: absolute; right: 10px; pointer-events: none;\n color: #94a3b8; flex-shrink: 0;\n }\n\n .sl-desc { margin: 0; font-size: 0.75rem; line-height: 1.4; color: #64748b; }\n .sl-desc-error { color: #ef4444; }\n\n @media (max-width: 480px) { .sl-select { font-size: 1rem; } }\n /* Touch targets: 44px tall on touch devices and phones (the sm size too); desktop unchanged. */\n @media (pointer: coarse), (max-width: 480px) { .sl-select, .sl-select-sm { min-height: 44px; } }\n `],\n})\n/**\n * Native select (ControlValueAccessor).\n * Built-in validation: `required` -> \"<label> is required.\" when empty; a non-empty bound value that is not one of the\n * `options` values -> \"Select a valid option.\" (not checked while `options` is empty, so asynchronously loaded options\n * never flag a value early). Disabled selects are never invalid.\n *\n * Validity API (same on every ui input): the component is an Angular `Validator` (`ngModel` / `FormControl` report\n * `.invalid` / `.errors`), exposes the signals `invalid` + `errorMessage` and the `invalidChange` output, and provides\n * `UiFieldValidity` so a host can use `viewChildren(UiFieldValidity)`. Messages are displayed only after blur or while\n * `[showErrors]=\"true\"`; setting `showErrors` back to false clears the touched state. A host-provided `[error]` wins.\n */\nexport class SelectComponent implements ControlValueAccessor, Validator, UiFieldValidity {\n readonly label = input('');\n /** Accessible name for a select with no visible label (e.g. an inline row or table-cell select). */\n readonly ariaLabel = input('');\n readonly placeholder = input('');\n readonly options = input<SelectOption[]>([]);\n readonly required = input(false);\n readonly hint = input('');\n readonly error = input('');\n /** Reveal the built-in error messages now (e.g. after a failed Save click), without waiting for blur. */\n readonly showErrors = input(false);\n readonly size = input<'default' | 'sm'>('default');\n\n readonly _disabledInput = input(false, { alias: 'disabled' });\n private readonly _cvaDisabled = signal(false);\n protected readonly disabled = computed(() => this._disabledInput() || this._cvaDisabled());\n\n protected readonly _value = signal('');\n\n private readonly _reveal = createFieldReveal(this.showErrors);\n private readonly _issue = computed(() => this._check(this._value()));\n /** True while the value breaks a built-in rule or the host passed an `[error]`. */\n readonly invalid = computed(() => !!this.error() || !!this._issue());\n /** Message of the first broken built-in rule ('' when valid), shown or not. */\n readonly errorMessage = computed(() => this._issue()?.message ?? '');\n /** Emits whenever the field becomes invalid / valid. */\n readonly invalidChange = output<boolean>();\n protected readonly _activeError = computed(() => this.error() || (this._reveal.revealed() ? this.errorMessage() : ''));\n private _onValidatorChange = () => {};\n\n constructor() {\n effect(() => { this.invalidChange.emit(this.invalid()); });\n }\n\n /** Re-run the form control's validation when a rule input (required, maxlength, min, options, ...) changes. */\n ngOnChanges() { this._onValidatorChange(); }\n\n validate(control: AbstractControl): ValidationErrors | null {\n const v = control.value;\n return issueToErrors(this._check(v == null ? '' : String(v)));\n }\n registerOnValidatorChange(fn: () => void) { this._onValidatorChange = fn; }\n\n protected readonly _selectId = `sl-${Math.random().toString(36).slice(2, 8)}`;\n\n private _onChange = (_: string) => {};\n private _onTouched = () => {};\n\n writeValue(val: string) { this._value.set(val ?? ''); }\n registerOnChange(fn: (v: string) => void) { this._onChange = fn; }\n registerOnTouched(fn: () => void) { this._onTouched = fn; }\n setDisabledState(d: boolean) { this._cvaDisabled.set(d); }\n\n protected onChange(event: Event) {\n const val = (event.target as HTMLSelectElement).value;\n this._value.set(val);\n this._onChange(val);\n }\n\n protected onBlur() { this._onTouched(); this._reveal.touched.set(true); }\n\n private _check(value: string): FieldIssue | null {\n if (this.disabled()) return null;\n if (value === '') return this.required() ? requiredIssue(this.label()) : null;\n const opts = this.options();\n if (opts.length > 0 && !opts.some(o => o.value === value)) return { key: 'option', message: 'Select a valid option.' };\n return null;\n }\n}\n","import { Injectable, signal } from '@angular/core';\n\nexport type ToastType = 'success' | 'error' | 'info';\n\n@Injectable({ providedIn: 'root' })\nexport class ToastService {\n readonly message = signal<string | null>(null);\n readonly type = signal<ToastType>('success');\n\n private _timer: ReturnType<typeof setTimeout> | null = null;\n\n show(msg: string, duration = 3000) {\n this._display('success', msg, duration);\n }\n\n /** Neutral (blue) toast for a result that is neither a success nor a failure, e.g. \"No changes to save.\". */\n info(msg: string, duration = 3000) {\n this._display('info', msg, duration);\n }\n\n error(msg: string, duration = 4000) {\n this._display('error', msg, duration);\n }\n\n // Cancel the previous toast's timer so it can't clear a newer toast early.\n private _display(type: ToastType, msg: string, duration: number) {\n if (this._timer) clearTimeout(this._timer);\n this.type.set(type);\n this.message.set(msg);\n this._timer = setTimeout(() => { this.message.set(null); this._timer = null; }, duration);\n }\n}\n","import { Injectable, signal, computed } from '@angular/core';\n\n/**\n * Tracks every live `<ui-toast>` so only one renders (internal — not exported from the public API).\n *\n * The data-table embeds its own toast and apps/shells render one too; they all read the same\n * ToastService at the same fixed position, which would show (and announce) each message twice.\n * Rule: the EARLIEST-CREATED live instance is the host. When it is destroyed the next one takes over.\n *\n * Not handled: a host with no layout box (e.g. inside a `display: none` ancestor) does not hand over.\n * Detecting that needs a post-render layout measurement on every toast; shells render their toast\n * outside hidden containers in practice.\n */\n@Injectable({ providedIn: 'root' })\nexport class ToastHostRegistry {\n private readonly _ids = signal<symbol[]>([]);\n /** The registered instance that currently renders, or undefined when none is alive. */\n readonly host = computed(() => this._ids()[0]);\n register(id: symbol) { this._ids.update(a => [...a, id]); }\n unregister(id: symbol) { this._ids.update(a => a.filter(x => x !== id)); }\n}\n","import { Component, inject, signal, computed, effect, untracked, DestroyRef, ChangeDetectionStrategy } from '@angular/core';\nimport { ToastService } from './toast.service';\nimport { ToastHostRegistry } from './toast-host-registry';\n\n@Component({\n selector: 'ui-toast',\n standalone: true,\n changeDetection: ChangeDetectionStrategy.OnPush,\n template: `\n @if (isHost() && toast.message()) {\n <div\n class=\"t-toast\"\n [class.t-error]=\"toast.type() === 'error'\"\n [class.t-info]=\"toast.type() === 'info'\"\n [class.t-adopted]=\"adopted()\"\n [attr.role]=\"adopted() ? null : (toast.type() === 'error' ? 'alert' : 'status')\"\n [attr.aria-live]=\"adopted() ? null : (toast.type() === 'error' ? 'assertive' : 'polite')\">\n @if (toast.type() === 'error') {\n <svg width=\"16\" height=\"16\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\" aria-hidden=\"true\">\n <circle cx=\"12\" cy=\"12\" r=\"10\"/><line x1=\"15\" y1=\"9\" x2=\"9\" y2=\"15\"/><line x1=\"9\" y1=\"9\" x2=\"15\" y2=\"15\"/>\n </svg>\n } @else if (toast.type() === 'info') {\n <svg width=\"16\" height=\"16\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\" aria-hidden=\"true\">\n <circle cx=\"12\" cy=\"12\" r=\"10\"/><line x1=\"12\" y1=\"16\" x2=\"12\" y2=\"12\"/><line x1=\"12\" y1=\"8\" x2=\"12.01\" y2=\"8\"/>\n </svg>\n } @else {\n <svg width=\"16\" height=\"16\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\" aria-hidden=\"true\">\n <path d=\"M22 11.08V12a10 10 0 1 1-5.93-9.14\"/><polyline points=\"22 4 12 14.01 9 11.01\"/>\n </svg>\n }\n <span>{{ toast.message() }}</span>\n </div>\n }\n `,\n styles: [`\n .t-toast {\n position: fixed; bottom: 24px; left: 50%; transform: translateX(-50%);\n background: #059669; color: #fff; padding: 11px 20px; border-radius: 12px;\n font-size: 0.875rem; font-weight: 500; z-index: 9999;\n box-shadow: 0 4px 16px rgba(0,0,0,0.18); animation: t-in 280ms cubic-bezier(0.16, 1, 0.3, 1);\n display: flex; align-items: center; gap: 8px;\n max-width: calc(100vw - 32px); word-break: break-word;\n }\n .t-error { background: #dc2626; }\n .t-info { background: #2563eb; }\n /* Taken over from a destroyed host mid-toast: no slide-in replay. */\n .t-adopted { animation: none; }\n @keyframes t-in {\n from { opacity: 0; transform: translateX(-50%) translateY(10px); }\n to { opacity: 1; transform: translateX(-50%) translateY(0); }\n }\n @media (max-width: 480px) {\n .t-toast {\n bottom: max(16px, env(safe-area-inset-bottom, 16px));\n font-size: 0.8125rem; padding: 10px 16px;\n }\n }\n `],\n})\nexport class ToastComponent {\n protected readonly toast = inject(ToastService);\n private readonly _registry = inject(ToastHostRegistry);\n private readonly _id = Symbol('ui-toast');\n /** True only for the earliest live instance, so exactly one toast (and one live region) renders. */\n protected readonly isHost = computed(() => this._registry.host() === this._id);\n\n /**\n * Message that was already on screen when this instance took over from a destroyed host. While it is\n * unchanged the toast renders without the entry animation and without live-region roles, so the\n * hand-over neither replays nor re-announces it. A new message clears this.\n */\n private readonly _adoptedMessage = signal<string | null>(null);\n protected readonly adopted = computed(() => {\n const m = this._adoptedMessage();\n return m !== null && m === this.toast.message();\n });\n private _wasWaiting = false;\n\n constructor() {\n this._registry.register(this._id);\n inject(DestroyRef).onDestroy(() => this._registry.unregister(this._id));\n\n effect(() => {\n const host = this.isHost();\n const msg = this.toast.message();\n untracked(() => {\n if (!host) { this._wasWaiting = true; return; }\n if (this._wasWaiting) { this._wasWaiting = false; this._adoptedMessage.set(msg); }\n else if (this._adoptedMessage() !== null && msg !== this._adoptedMessage()) this._adoptedMessage.set(null);\n });\n });\n }\n}\n","const MAX_LENGTH = 300;\n\n/** Fragments that mark a string as technical (stack traces, class names, status text, markup) rather than a sentence for a person. */\nconst TECHNICAL = [\n /org\\.springframework/i, /\\bjava\\./i, /\\bjavax\\./i, /\\bkotlin\\./i, /exception/i, /\\bat\\s+\\S+\\(.*\\)/,\n /\\binternal server error\\b/i, /^\\s*(?:5\\d\\d|4\\d\\d)\\b/, /\\bstack\\s?trace\\b/i, /<[a-z!/][^>]*>/i,\n /^\\s*[{[]/, /^\\s*(?:null|undefined|nan)\\s*$/i, /\\bnull\\b/i, /\\bundefined\\b/i, /\\bsql\\b/i, /\\bconstraint\\b/i,\n /\\bnullpointer/i, /\\bhttp\\s?\\d{3}\\b/i, /\\b(?:bad request|not found|conflict|forbidden|unauthorized)\\s*$/i,\n];\n\n/**\n * Returns the trimmed text when `value` is a plain, user-facing sentence from the server, otherwise `null`\n * (not a string, empty, longer than 300 chars, or technical: stack trace, exception/class name, JSON, HTML,\n * raw status text, \"null\"). Callers fall back to their own generated wording on `null`.\n */\nexport function humanReadableMessage(value: unknown): string | null {\n if (typeof value !== 'string') return null;\n const text = value.trim();\n if (!text || text.length > MAX_LENGTH) return null;\n if (/[\\r\\n]/.test(text)) return null;\n if (TECHNICAL.some(re => re.test(text))) return null;\n return text;\n}\n","import {\n Component, input, output, effect, ChangeDetectionStrategy,\n ChangeDetectorRef, inject,\n} from '@angular/core';\nimport { HttpClient, HttpParams } from '@angular/common/http';\nimport { FORGEROUTE_CONFIG } from '../config/forgeroute.config';\nimport { FormsModule } from '@angular/forms';\nimport { ModalComponent } from '../modal/modal.component';\nimport { ButtonComponent } from '../button/button.component';\nimport { SelectComponent } from '../select/select.component';\nimport type { SelectOption } from '../select/select.component';\nimport { ToastService } from '../toast/toast.service';\n\n// ── Public types ─────────────────────────────────────────────────────────────\n\nexport interface ImportFieldDef {\n key: string;\n label: string;\n required?: boolean;\n}\n\nexport interface ImportSource {\n value: string;\n label: string;\n desc?: string;\n letter?: string;\n color?: string;\n aliases?: Record<string, string[]>;\n}\n\nexport interface ImportExportConfig {\n entityName: string;\n entityNamePlural: string;\n exportEndpoint: string;\n /**\n * POST endpoint for the import wizard. Omit to make the table export-only —\n * ui-data-table only injects the Import button when this is set.\n */\n importEndpoint?: string;\n exportHeaders: string[];\n exportRow: (item: any) => (string | number)[];\n importFields?: ImportFieldDef[];\n columnAliases?: Record<string, string[]>;\n templateSampleRows?: string[][];\n /** When true, the Export button appears in the More Actions dropdown instead of as a standalone button. */\n exportInMoreActions?: boolean;\n}\n\ninterface ImportError { row: number; message: string; }\ntype ImportStep = 'source' | 'upload' | 'mapping' | 'preview' | 'result';\n\n// ── CSV helpers ───────────────────────────────────────────────────────────────\n\nfunction parseCsvLine(line: string): string[] {\n const cells: string[] = [];\n let i = 0;\n while (i <= line.length) {\n if (i === line.length) { cells.push(''); break; }\n if (line[i] === '\"') {\n let cell = '';\n i++;\n while (i < line.length) {\n if (line[i] === '\"') {\n if (line[i + 1] === '\"') { cell += '\"'; i += 2; }\n else { i++; break; }\n } else { cell += line[i++]; }\n }\n if (line[i] === ',') i++;\n cells.push(cell);\n if (i >= line.length) break;\n } else {\n const end = line.indexOf(',', i);\n if (end === -1) { cells.push(line.slice(i)); break; }\n cells.push(line.slice(i, end));\n i = end + 1;\n }\n }\n return cells;\n}\n\nfunction parseCsv(text: string): string[][] {\n return text.split(/\\r?\\n/).filter(l => l.trim()).map(parseCsvLine);\n}\n\n/**\n * CSV / formula-injection guard. Excel, Sheets and Numbers run a cell that starts with = + - @ (or a tab / carriage return) as a\n * formula - also after leading spaces, which some spreadsheets skip - and exports carry free text (names, notes, subjects). Such a TEXT cell gets a leading apostrophe so it opens as plain text.\n * Numbers, and strings that are only a signed number with an optional currency symbol or percent sign (-12.50, -$12.50, +15125550177, -3%),\n * cannot form a formula and are left exactly as they are, so negative amounts and phone numbers keep their value.\n */\nfunction csvCell(cell: string | number): string {\n if (typeof cell === 'number') return String(cell);\n const v = String(cell);\n return /^(?:[\\t\\r]|\\s*[=+\\-@])/.test(v) && !/^\\s*[+-]?[$€£]?\\d[\\d.,]*%?$/.test(v) ? `'${v}` : v;\n}\n\nfunction exportToCsv(filenamePrefix: string, headers: string[], rows: (string | number)[][]): void {\n const csvContent = [headers, ...rows]\n .map(row => row.map(cell => `\"${csvCell(cell).replace(/\"/g, '\"\"')}\"`).join(','))\n .join('\\n');\n const blob = new Blob([csvContent], { type: 'text/csv;charset=utf-8;' });\n const url = URL.createObjectURL(blob);\n const link = document.createElement('a');\n link.href = url;\n link.download = `${filenamePrefix}-${new Date().toISOString().split('T')[0]}.csv`;\n link.click();\n // Revoking synchronously can cancel the download in some browsers — release the URL once the click has been handled.\n setTimeout(() => URL.revokeObjectURL(url), 1000);\n}\n\n// ── Component ─────────────────────────────────────────────────────────────────\n\n@Component({\n selector: 'ui-import-export',\n standalone: true,\n changeDetection: ChangeDetectionStrategy.OnPush,\n imports: [FormsModule, ModalComponent, ButtonComponent, SelectComponent],\n template: `\n <!-- Trigger buttons -->\n <div class=\"ie-actions\">\n <ui-button variant=\"secondary\" size=\"sm\" [loading]=\"exporting\" (click)=\"exportAll()\" ariaLabel=\"Export all records to CSV\">\n <svg width=\"14\" height=\"14\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\" aria-hidden=\"true\"><path d=\"M21 15v4a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2v-4\"/><polyline points=\"7 10 12 15 17 10\"/><line x1=\"12\" y1=\"15\" x2=\"12\" y2=\"3\"/></svg>\n Export\n </ui-button>\n @if (!exportOnly()) {\n <ui-button variant=\"secondary\" size=\"sm\" (click)=\"openImport()\" ariaLabel=\"Import records from CSV\">\n <svg width=\"14\" height=\"14\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\" aria-hidden=\"true\"><path d=\"M21 15v4a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2v-4\"/><polyline points=\"7 10 12 5 17 10\"/><line x1=\"12\" y1=\"3\" x2=\"12\" y2=\"15\"/></svg>\n Import\n </ui-button>\n }\n </div>\n\n <!-- Import modal -->\n <ui-modal [open]=\"importOpen\" [title]=\"modalTitle\" maxWidth=\"640px\" (close)=\"closeImport()\">\n\n <!-- Step indicator -->\n <ol class=\"ie-steps\" aria-label=\"Import progress\">\n @if (importSources()) {\n <li class=\"ie-step\" [class.active]=\"step === 'source'\" [class.done]=\"stepIndex > 0\">\n <span class=\"ie-step-num\" aria-hidden=\"true\">1</span><span class=\"ie-step-label\">Source</span>\n </li>\n <div class=\"ie-step-divider\" aria-hidden=\"true\"></div>\n }\n <li class=\"ie-step\" [class.active]=\"step === 'upload'\" [class.done]=\"stepIndex > (importSources() ? 1 : 0)\">\n <span class=\"ie-step-num\" aria-hidden=\"true\">{{ importSources() ? 2 : 1 }}</span><span class=\"ie-step-label\">Upload</span>\n </li>\n <div class=\"ie-step-divider\" aria-hidden=\"true\"></div>\n <li class=\"ie-step\" [class.active]=\"step === 'mapping'\" [class.done]=\"stepIndex > (importSources() ? 2 : 1)\">\n <span class=\"ie-step-num\" aria-hidden=\"true\">{{ importSources() ? 3 : 2 }}</span><span class=\"ie-step-label\">Map Columns</span>\n </li>\n <div class=\"ie-step-divider\" aria-hidden=\"true\"></div>\n <li class=\"ie-step\" [class.active]=\"step === 'preview'\" [class.done]=\"stepIndex > (importSources() ? 3 : 2)\">\n <span class=\"ie-step-num\" aria-hidden=\"true\">{{ importSources() ? 4 : 3 }}</span><span class=\"ie-step-label\">Preview</span>\n </li>\n <div class=\"ie-step-divider\" aria-hidden=\"true\"></div>\n <li class=\"ie-step\" [class.active]=\"step === 'result'\">\n <span class=\"ie-step-num\" aria-hidden=\"true\">{{ importSources() ? 5 : 4 }}</span><span class=\"ie-step-label\">Done</span>\n </li>\n </ol>\n\n <!-- Step 1: Source selection -->\n @if (step === 'source' && importSources()) {\n <div class=\"ie-source-step\">\n <p class=\"ie-mapping-hint\">Select where you're importing from. We'll automatically match the columns from your export file.</p>\n <div class=\"ie-source-grid\" role=\"list\" [attr.aria-label]=\"'Select your ' + config.entityName + ' source'\">\n @for (src of importSources()!; track src.value) {\n <button\n class=\"ie-source-card\"\n role=\"listitem\"\n type=\"button\"\n [class.ie-source-card--active]=\"selectedSource === src.value\"\n [attr.aria-pressed]=\"selectedSource === src.value\"\n (click)=\"onSourceSelected(src.value)\">\n @if (src.letter) {\n <span class=\"ie-source-letter\" [style.background]=\"src.color || '#64748b'\" aria-hidden=\"true\">{{ src.letter }}</span>\n }\n <span class=\"ie-source-label\">{{ src.label }}</span>\n @if (src.desc) { <span class=\"ie-source-desc\">{{ src.desc }}</span> }\n </button>\n }\n </div>\n </div>\n }\n\n <!-- Step 2: Upload -->\n @if (step === 'upload') {\n <div class=\"ie-upload-area\">\n <div class=\"ie-template-hint\">\n <svg width=\"16\" height=\"16\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\" aria-hidden=\"true\"><circle cx=\"12\" cy=\"12\" r=\"10\"/><line x1=\"12\" y1=\"8\" x2=\"12\" y2=\"12\"/><line x1=\"12\" y1=\"16\" x2=\"12.01\" y2=\"16\"/></svg>\n Start with our CSV template to ensure correct column names.\n <ui-button variant=\"ghost\" size=\"sm\" (click)=\"downloadTemplate()\">Download template</ui-button>\n </div>\n <label class=\"ie-dropzone\" [class.has-error]=\"!!fileError\">\n <input type=\"file\" class=\"ie-file-hidden\" accept=\".csv,text/csv\" aria-label=\"Choose CSV file to import\" (change)=\"onFileChange($event)\" />\n <svg width=\"32\" height=\"32\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"#94a3b8\" stroke-width=\"1.5\" aria-hidden=\"true\"><path d=\"M21 15v4a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2v-4\"/><polyline points=\"17 8 12 3 7 8\"/><line x1=\"12\" y1=\"3\" x2=\"12\" y2=\"15\"/></svg>\n <span class=\"ie-dropzone-text\">Click to choose a CSV file</span>\n <span class=\"ie-dropzone-sub\">Supported: .csv files</span>\n </label>\n @if (fileError) { <p class=\"ie-error\" role=\"alert\">{{ fileError }}</p> }\n </div>\n }\n\n <!-- Step 3: Column mapping -->\n @if (step === 'mapping') {\n <div class=\"ie-mapping\">\n @if (allRequiredMapped) {\n <div class=\"ie-mapping-success\">\n <svg width=\"15\" height=\"15\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2.5\" aria-hidden=\"true\"><circle cx=\"12\" cy=\"12\" r=\"10\"/><path d=\"m9 12 2 2 4-4\"/></svg>\n All required columns matched automatically. Review below or click <strong>Preview Import</strong> to continue.\n </div>\n } @else {\n <p class=\"ie-mapping-hint\">\n We couldn't match {{ unmappedRequiredFields.length }} required field(s): <strong>{{ unmappedRequiredFields.join(', ') }}</strong>.\n Use the dropdowns below to map them manually.\n </p>\n }\n <div class=\"ie-mapping-table\" role=\"table\" [attr.aria-label]=\"config.entityName + ' column mapping'\">\n <div class=\"ie-mapping-header\" role=\"row\">\n <span role=\"columnheader\">{{ config.entityName }} Field</span>\n <span role=\"columnheader\">Your CSV Column</span>\n <span role=\"columnheader\">Sample Value</span>\n </div>\n @for (field of config.importFields; track field.key) {\n <div class=\"ie-mapping-row\" [class.ie-mapping-row--matched]=\"!!columnMapping[field.key]\" [class.ie-mapping-row--required-missing]=\"field.required && !columnMapping[field.key]\" role=\"row\">\n <span class=\"ie-field-label\" role=\"rowheader\">\n @if (columnMapping[field.key]) {\n <svg class=\"ie-match-icon\" width=\"13\" height=\"13\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2.5\" aria-hidden=\"true\"><path d=\"m20 6-11 11-5-5\"/></svg>\n }\n {{ field.label }}\n @if (field.required) { <span class=\"ie-required\" aria-label=\"required\">*</span> }\n </span>\n <ui-select\n [options]=\"csvHeaderOptions\"\n [(ngModel)]=\"columnMapping[field.key]\"\n [error]=\"field.required && !columnMapping[field.key] ? 'Required' : ''\" />\n <span class=\"ie-sample\" aria-label=\"Sample value\">{{ getSample(field.key) }}</span>\n </div>\n }\n </div>\n @if (unmatchedCsvHeaders.length > 0) {\n <p class=\"ie-mapping-unmatched\">\n <svg width=\"13\" height=\"13\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\" aria-hidden=\"true\"><circle cx=\"12\" cy=\"12\" r=\"10\"/><line x1=\"12\" y1=\"8\" x2=\"12\" y2=\"12\"/><line x1=\"12\" y1=\"16\" x2=\"12.01\" y2=\"16\"/></svg>\n {{ unmatchedCsvHeaders.length }} column(s) in your CSV weren't matched and will be ignored: <em>{{ unmatchedCsvHeaders.join(', ') }}</em>\n </p>\n }\n @if (mappingError) { <p class=\"ie-error\" role=\"alert\">{{ mappingError }}</p> }\n </div>\n }\n\n <!-- Step 4: Preview -->\n @if (step === 'preview') {\n <div class=\"ie-preview\">\n <p class=\"ie-mapping-hint\">Preview of the first {{ previewRows.length }} row(s) of {{ csvRows.length }} total. Review before importing.</p>\n <div class=\"ie-preview-scroll\">\n <table class=\"ie-preview-table\" aria-label=\"Import data preview\">\n <thead>\n <tr>@for (field of mappedFields; track field.key) { <th scope=\"col\">{{ field.label }}</th> }</tr>\n </thead>\n <tbody>\n @for (row of previewRows; track $index) {\n <tr>@for (field of mappedFields; track field.key) { <td>{{ row[field.key] || '—' }}</td> }</tr>\n }\n </tbody>\n </table>\n </div>\n </div>\n }\n\n <!-- Step 5: Result -->\n @if (step === 'result') {\n <div class=\"ie-result\" aria-live=\"polite\">\n @if (importResult) {\n <div class=\"ie-result-summary\">\n <div class=\"ie-result-stat success\">\n <svg width=\"20\" height=\"20\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\" aria-hidden=\"true\"><circle cx=\"12\" cy=\"12\" r=\"10\"/><path d=\"m9 12 2 2 4-4\"/></svg>\n <strong>{{ importResult.imported }}</strong> record(s) imported\n </div>\n @if (importResult.errors.length > 0) {\n <div class=\"ie-result-stat error\">\n <svg width=\"20\" height=\"20\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\" aria-hidden=\"true\"><circle cx=\"12\" cy=\"12\" r=\"10\"/><line x1=\"15\" y1=\"9\" x2=\"9\" y2=\"15\"/><line x1=\"9\" y1=\"9\" x2=\"15\" y2=\"15\"/></svg>\n <strong>{{ importResult.errors.length }}</strong> row(s) had errors\n </div>\n }\n @if (importResult.warnings.length > 0) {\n <div class=\"ie-result-stat warning\">\n <svg width=\"20\" height=\"20\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\" aria-hidden=\"true\"><path d=\"M10.29 3.86 1.82 18a2 2 0 0 0 1.71 3h16.94a2 2 0 0 0 1.71-3L13.71 3.86a2 2 0 0 0-3.42 0z\"/><line x1=\"12\" y1=\"9\" x2=\"12\" y2=\"13\"/><line x1=\"12\" y1=\"17\" x2=\"12.01\" y2=\"17\"/></svg>\n <strong>{{ importResult.warnings.length }}</strong> value(s) changed during import\n </div>\n }\n </div>\n @if (importResult.errors.length > 0) {\n <div class=\"ie-errors-list\">\n <p class=\"ie-errors-title\">Errors:</p>\n @for (err of importResult.errors; track err.row) {\n <div class=\"ie-error-row\">\n <span class=\"ie-error-row-num\">Row {{ err.row }}</span>\n <span>{{ err.message }}</span>\n </div>\n }\n </div>\n }\n @if (importResult.warnings.length > 0) {\n <div class=\"ie-warnings-list\">\n <p class=\"ie-warnings-title\">Imported with changes:</p>\n @for (w of importResult.warnings; track $index) {\n <div class=\"ie-warning-row\">\n <span class=\"ie-warning-row-num\">Row {{ w.row }}</span>\n <span>{{ w.message }}</span>\n </div>\n }\n </div>\n }\n }\n </div>\n }\n\n <!-- Footer -->\n @if (step === 'source') {\n <ui-button modal-footer-left variant=\"secondary\" size=\"sm\" (click)=\"closeImport()\">Cancel</ui-button>\n }\n @if (step === 'source') {\n <ui-button modal-footer-right variant=\"primary\" size=\"sm\" [disabled]=\"!selectedSource\" (click)=\"confirmSource()\">Next</ui-button>\n }\n @if (step === 'upload') {\n <span modal-footer-left></span>\n }\n @if (step === 'upload' && importSources()?.length) {\n <div modal-footer-right class=\"ie-footer-right\">\n <ui-button variant=\"ghost\" size=\"sm\" (click)=\"step = 'source'\">Back</ui-button>\n <ui-button variant=\"secondary\" size=\"sm\" (click)=\"closeImport()\">Cancel</ui-button>\n </div>\n }\n @if (step === 'upload' && !importSources()?.length) {\n <ui-button modal-footer-right variant=\"secondary\" size=\"sm\" (click)=\"closeImport()\">Cancel</ui-button>\n }\n @if (step === 'mapping') {\n <ui-button modal-footer-left variant=\"ghost\" size=\"sm\" (click)=\"step = 'upload'\">Back</ui-button>\n }\n @if (step === 'mapping') {\n <div modal-footer-right class=\"ie-footer-right\">\n <ui-button variant=\"secondary\" size=\"sm\" (click)=\"closeImport()\">Cancel</ui-button>\n <ui-button variant=\"primary\" size=\"sm\" [disabled]=\"!canSubmit\" (click)=\"goToPreview()\">Preview Import</ui-button>\n </div>\n }\n @if (step === 'preview') {\n <ui-button modal-footer-left variant=\"ghost\" size=\"sm\" (click)=\"step = 'mapping'\">Back</ui-button>\n }\n @if (step === 'preview') {\n <div modal-footer-right class=\"ie-footer-right\">\n <ui-button variant=\"secondary\" size=\"sm\" (click)=\"closeImport()\">Cancel</ui-button>\n <ui-button variant=\"primary\" size=\"sm\" [loading]=\"importing\" (click)=\"submitImport()\">Import {{ csvRows.length }} Record(s)</ui-button>\n </div>\n }\n @if (step === 'result') {\n <span modal-footer-left></span>\n }\n @if (step === 'result') {\n <ui-button modal-footer-right variant=\"primary\" size=\"sm\" (click)=\"finishImport()\">Done</ui-button>\n }\n\n </ui-modal>\n `,\n styles: [`\n .ie-actions { display: flex; gap: 8px; }\n\n /* Step indicator */\n .ie-steps { display: flex; align-items: center; margin-bottom: 24px; gap: 0; list-style: none; padding: 0; }\n .ie-step { display: flex; align-items: center; gap: 6px; flex-shrink: 0; }\n .ie-step-num { width: 24px; height: 24px; border-radius: 50%; background: #e2e8f0; color: #64748b; font-size: 0.75rem; font-weight: 700; display: flex; align-items: center; justify-content: center; flex-shrink: 0; }\n .ie-step-label { font-size: 0.8125rem; color: #94a3b8; font-weight: 500; }\n .ie-step.active .ie-step-num { background: #3b82f6; color: #fff; }\n .ie-step.active .ie-step-label { color: #1e293b; font-weight: 600; }\n .ie-step.done .ie-step-num { background: #22c55e; color: #fff; }\n .ie-step.done .ie-step-label { color: #64748b; }\n .ie-step-divider { flex: 1; height: 1px; background: #e2e8f0; margin: 0 8px; min-width: 16px; }\n\n /* Source step */\n .ie-source-step { display: flex; flex-direction: column; gap: 16px; }\n .ie-source-grid { display: grid; grid-template-columns: repeat(3, 1fr); gap: 10px; }\n @media (max-width: 520px) { .ie-source-grid { grid-template-columns: repeat(2, 1fr); } }\n .ie-source-card {\n display: flex; flex-direction: column; align-items: flex-start; gap: 4px;\n padding: 12px; border: 1px solid #e2e8f0; border-radius: 10px;\n background: #fff; cursor: pointer; font-family: inherit; text-align: left;\n transition: all 0.15s;\n &:hover { border-color: #94a3b8; background: #f8fafc; }\n &.ie-source-card--active { border-color: #3b82f6; background: #eff6ff; }\n &:focus-visible { outline: 2px solid #60a5fa; outline-offset: 2px; }\n }\n .ie-source-letter { width: 36px; height: 36px; border-radius: 8px; color: #fff; font-size: 1rem; font-weight: 700; display: flex; align-items: center; justify-content: center; flex-shrink: 0; }\n .ie-source-label { font-size: 0.8125rem; font-weight: 600; color: #1e293b; line-height: 1.2; }\n .ie-source-desc { font-size: 0.75rem; color: #64748b; }\n\n /* Upload step */\n .ie-upload-area { display: flex; flex-direction: column; gap: 12px; }\n .ie-template-hint { display: flex; align-items: center; gap: 8px; flex-wrap: wrap; font-size: 0.875rem; color: #64748b; background: #f8fafc; border: 1px solid #e2e8f0; border-radius: 8px; padding: 10px 14px; }\n .ie-file-hidden { display: none; }\n .ie-dropzone {\n display: flex; flex-direction: column; align-items: center; justify-content: center;\n gap: 8px; padding: 40px 24px; border: 2px dashed #cbd5e1; border-radius: 10px;\n cursor: pointer; transition: border-color 180ms ease, background 180ms ease;\n &:hover, &:focus-within { border-color: #3b82f6; background: #f0f7ff; }\n &.has-error { border-color: #ef4444; }\n }\n .ie-dropzone-text { font-size: 0.9375rem; font-weight: 600; color: #1e293b; }\n .ie-dropzone-sub { font-size: 0.8125rem; color: #94a3b8; }\n\n /* Mapping step */\n .ie-mapping { display: flex; flex-direction: column; gap: 12px; }\n .ie-mapping-hint { font-size: 0.875rem; color: #64748b; margin: 0; }\n .ie-mapping-success { display: flex; align-items: flex-start; gap: 8px; padding: 10px 14px; background: #f0fdf4; border: 1px solid #bbf7d0; border-radius: 8px; font-size: 0.875rem; color: #15803d; svg { flex-shrink: 0; margin-top: 1px; color: #16a34a; } }\n .ie-mapping-unmatched { display: flex; align-items: flex-start; gap: 6px; font-size: 0.8125rem; color: #64748b; margin: 0; padding-top: 4px; svg { flex-shrink: 0; margin-top: 1px; } em { font-style: normal; color: #475569; } }\n .ie-mapping-table { border: 1px solid #e2e8f0; border-radius: 8px; overflow: hidden; }\n .ie-mapping-header { display: grid; grid-template-columns: 1fr 1fr 1fr; padding: 8px 12px; background: #f8fafc; border-bottom: 1px solid #e2e8f0; font-size: 0.75rem; font-weight: 600; color: #64748b; text-transform: uppercase; }\n .ie-mapping-row { display: grid; grid-template-columns: 1fr 1fr 1fr; align-items: start; padding: 8px 12px; border-bottom: 1px solid #f1f5f9; gap: 8px; &:last-child { border-bottom: none; } &--required-missing { background: #fff7ed; .ie-field-label { color: #c2410c; } } }\n .ie-match-icon { color: #16a34a; margin-right: 4px; vertical-align: -1px; }\n .ie-field-label { display: flex; align-items: center; font-size: 0.875rem; color: #1e293b; font-weight: 500; }\n .ie-required { color: #ef4444; margin-left: 2px; }\n .ie-sample { font-size: 0.8125rem; color: #64748b; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }\n\n /* Preview step */\n .ie-preview { display: flex; flex-direction: column; gap: 12px; }\n .ie-preview-scroll { overflow-x: auto; border: 1px solid #e2e8f0; border-radius: 8px; }\n .ie-preview-table { width: 100%; border-collapse: collapse; font-size: 0.8125rem; th { background: #f8fafc; padding: 8px 12px; text-align: left; font-size: 0.75rem; font-weight: 600; color: #64748b; text-transform: uppercase; border-bottom: 1px solid #e2e8f0; white-space: nowrap; } td { padding: 7px 12px; color: #1e293b; border-bottom: 1px solid #f1f5f9; max-width: 160px; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; } tr:last-child td { border-bottom: none; } }\n\n /* Result step */\n .ie-result { display: flex; flex-direction: column; gap: 16px; }\n .ie-result-summary { display: flex; flex-direction: column; gap: 8px; }\n .ie-result-stat { display: flex; align-items: center; gap: 10px; padding: 12px 16px; border-radius: 8px; font-size: 0.9375rem; &.success { background: #f0fdf4; color: #15803d; svg { stroke: #22c55e; } } &.error { background: #fef2f2; color: #b91c1c; svg { stroke: #ef4444; } } &.warning { background: #fffbeb; color: #b45309; svg { stroke: #f59e0b; } } }\n .ie-errors-list { border: 1px solid #fecaca; border-radius: 8px; overflow: hidden; }\n .ie-errors-title { font-size: 0.8125rem; font-weight: 600; color: #b91c1c; padding: 8px 12px; background: #fef2f2; margin: 0; border-bottom: 1px solid #fecaca; }\n .ie-error-row { display: flex; gap: 12px; padding: 7px 12px; border-bottom: 1px solid #fff1f2; font-size: 0.8125rem; color: #374151; &:last-child { border-bottom: none; } }\n .ie-error-row-num { font-weight: 600; color: #b91c1c; flex-shrink: 0; }\n .ie-warnings-list { border: 1px solid #fde68a; border-radius: 8px; overflow: hidden; }\n .ie-warnings-title { font-size: 0.8125rem; font-weight: 600; color: #b45309; padding: 8px 12px; background: #fffbeb; margin: 0; border-bottom: 1px solid #fde68a; }\n .ie-warning-row { display: flex; gap: 12px; padding: 7px 12px; border-bottom: 1px solid #fef3c7; font-size: 0.8125rem; color: #374151; &:last-child { border-bottom: none; } }\n .ie-warning-row-num { font-weight: 600; color: #b45309; flex-shrink: 0; }\n\n /* Shared */\n .ie-error { font-size: 0.875rem; color: #ef4444; margin: 0; }\n .ie-footer-right { display: flex; gap: 8px; align-items: center; }\n\n @media (max-width: 600px) {\n .ie-step-label { display: none; }\n .ie-dropzone { padding: 24px 16px; }\n .ie-mapping-header { grid-template-columns: 1fr 1fr; span:last-child { display: none; } }\n .ie-mapping-row { grid-template-columns: 1fr 1fr; .ie-sample { display: none; } }\n }\n `],\n})\nexport class ImportExportComponent {\n readonly _configInput = input<ImportExportConfig | null>(null, { alias: 'config' });\n readonly importSources = input<ImportSource[] | undefined>(undefined);\n readonly exportParams = input<Record<string, string>>({});\n readonly exportOnly = input(false);\n readonly imported = output<void>();\n\n config!: ImportExportConfig;\n\n private readonly http = inject(HttpClient);\n /** API root from FORGEROUTE_CONFIG (e.g. a cross-origin prod API); falls back to same-origin `/api`. */\n private readonly apiBase = inject(FORGEROUTE_CONFIG, { optional: true })?.baseUrl ?? '/api';\n private readonly toast = inject(ToastService);\n private readonly cdr = inject(ChangeDetectorRef);\n\n constructor() {\n effect(() => { const v = this._configInput(); if (v !== null) this.config = v!; });\n }\n\n // ── Export ────────────────────────────────────────────────────────────────\n\n exporting = false;\n\n exportAll(): void {\n if (this.exporting) return;\n this.exporting = true;\n this.cdr.detectChanges();\n let params = new HttpParams().set('size', '10000');\n Object.entries(this.exportParams()).forEach(([k, v]) => { if (v) params = params.set(k, v); });\n this.http.get<any>(`${this.apiBase}${this.config.exportEndpoint}`, { params }).subscribe({\n next: (response) => {\n const items: any[] = response.content ?? response;\n const totalInDb: number = response.totalElements ?? items.length;\n exportToCsv(\n this.config.entityNamePlural.toLowerCase().replace(/\\s+/g, '-'),\n this.config.exportHeaders,\n items.map(item => this.config.exportRow(item)),\n );\n this.exporting = false;\n if (totalInDb > items.length) {\n this.toast.show(`${items.length} of ${totalInDb} ${this.config.entityNamePlural.toLowerCase()} exported — export limit reached.`);\n } else {\n const noun = items.length === 1 ? this.config.entityName : this.config.entityNamePlural;\n this.toast.show(`${items.length} ${noun.toLowerCase()} exported`);\n }\n this.cdr.detectChanges();\n },\n error: () => {\n this.exporting = false;\n this.toast.error(`Failed to export ${this.config.entityNamePlural.toLowerCase()}. Please try again.`);\n this.cdr.detectChanges();\n },\n });\n }\n\n downloadTemplate(): void {\n const headers = (this.config.importFields ?? []).map(f => f.label);\n exportToCsv(\n `${this.config.entityName.toLowerCase().replace(/\\s+/g, '-')}-template`,\n headers,\n this.config.templateSampleRows ?? [],\n );\n }\n\n // ── Import modal state ────────────────────────────────────────────────────\n\n importOpen = false;\n step: ImportStep = 'upload';\n selectedSource = '';\n csvHeaders: string[] = [];\n csvRows: string[][] = [];\n fileError: string | null = null;\n columnMapping: Record<string, string> = {};\n mappingError: string | null = null;\n importing = false;\n /** `warnings` = rows imported with a value changed (e.g. an unrecognised status mapped to the default). */\n importResult: { imported: number; errors: ImportError[]; warnings: ImportError[] } | null = null;\n\n get stepIndex(): number {\n if (this.importSources()) {\n return ({ source: 0, upload: 1, mapping: 2, preview: 3, result: 4 } as any)[this.step] ?? 0;\n }\n return ({ upload: 0, mapping: 1, preview: 2, result: 3 } as any)[this.step] ?? 0;\n }\n\n get modalTitle(): string {\n if (this.selectedSource && this.selectedSource !== 'custom') {\n const src = this.importSources()?.find(s => s.value === this.selectedSource);\n if (src) return `Import from ${src.label}`;\n }\n return `Import ${this.config?.entityNamePlural ?? ''}`;\n }\n\n openImport(): void {\n this.importOpen = true;\n this.step = this.importSources()?.length ? 'source' : 'upload';\n this.selectedSource = '';\n this.csvHeaders = []; this.csvRows = [];\n this.fileError = null; this.columnMapping = {};\n this.mappingError = null; this.importResult = null;\n // Called from the host data-table (outside this OnPush view) — must trigger CD or the wizard never renders.\n this.cdr.detectChanges();\n }\n\n onSourceSelected(value: string): void { this.selectedSource = value; this.cdr.detectChanges(); }\n\n confirmSource(): void {\n if (!this.selectedSource) return;\n const src = this.importSources()?.find(s => s.value === this.selectedSource);\n this.config = { ...this.config, columnAliases: src?.aliases ?? {} };\n this.step = 'upload';\n this.cdr.detectChanges();\n }\n\n closeImport(): void {\n if (this.step === 'result') this.finishImport();\n else this.importOpen = false;\n }\n\n finishImport(): void { this.importOpen = false; this.imported.emit(); }\n\n onFileChange(event: Event): void {\n const file = (event.target as HTMLInputElement).files?.[0];\n if (!file) return;\n const reader = new FileReader();\n reader.onload = (e) => {\n try {\n const text = (e.target!.result as string).trim();\n const parsed = parseCsv(text);\n if (parsed.length < 1) { this.fileError = 'The CSV file appears to be empty.'; this.cdr.detectChanges(); return; }\n if (parsed.length < 2) { this.fileError = 'CSV must have a header row and at least one data row.'; this.cdr.detectChanges(); return; }\n this.csvHeaders = parsed[0]; this.csvRows = parsed.slice(1);\n this.fileError = null; this.autoMap(); this.step = 'mapping';\n this.cdr.detectChanges();\n } catch {\n this.fileError = 'Failed to parse the CSV file. Please check the format and try again.';\n this.cdr.detectChanges();\n }\n };\n reader.readAsText(file);\n }\n\n private autoMap(): void {\n this.columnMapping = {};\n const normalize = (s: string) => s.trim().toLowerCase().replace(/[\\s_\\-./]+/g, '');\n for (const field of (this.config.importFields ?? [])) {\n const normalizedLabel = normalize(field.label);\n const normalizedKey = normalize(field.key);\n let match = this.csvHeaders.find(h => h.trim().toLowerCase() === field.label.toLowerCase() || h.trim().toLowerCase() === field.key.toLowerCase());\n if (!match) match = this.csvHeaders.find(h => { const n = normalize(h); return n === normalizedLabel || n === normalizedKey; });\n if (!match && this.config.columnAliases?.[field.key]) {\n const aliases = this.config.columnAliases[field.key].map(normalize);\n match = this.csvHeaders.find(h => aliases.includes(normalize(h)));\n }\n this.columnMapping[field.key] = match ?? '';\n }\n }\n\n get canSubmit(): boolean { return (this.config.importFields ?? []).filter(f => f.required).every(f => !!this.columnMapping[f.key]); }\n get unmappedRequiredFields(): string[] { return (this.config.importFields ?? []).filter(f => f.required && !this.columnMapping[f.key]).map(f => f.label); }\n get unmatchedCsvHeaders(): string[] { const used = new Set(Object.values(this.columnMapping).filter(Boolean)); return this.csvHeaders.filter(h => !used.has(h)); }\n\n get csvHeaderOptions(): SelectOption[] {\n return [\n { value: '', label: '— Leave blank —' },\n ...this.csvHeaders.map(h => ({ value: h, label: h })),\n ];\n }\n get allRequiredMapped(): boolean { return this.unmappedRequiredFields.length === 0; }\n\n getSample(fieldKey: string): string {\n const col = this.columnMapping[fieldKey];\n if (!col) return '';\n const idx = this.csvHeaders.indexOf(col);\n return idx < 0 ? '' : (this.csvRows[0]?.[idx] ?? '');\n }\n\n goToPreview(): void {\n this.mappingError = null;\n const missing = (this.config.importFields ?? []).filter(f => f.required && !this.columnMapping[f.key]).map(f => f.label);\n if (missing.length) { this.mappingError = `Required field(s) not mapped: ${missing.join(', ')}`; return; }\n this.step = 'preview';\n }\n\n get mappedFields(): ImportFieldDef[] { return (this.config.importFields ?? []).filter(f => !!this.columnMapping[f.key]); }\n\n get previewRows(): Record<string, string>[] {\n return this.csvRows.slice(0, 5).map(row => {\n const record: Record<string, string> = {};\n for (const field of (this.config.importFields ?? [])) {\n const col = this.columnMapping[field.key];\n if (!col) continue;\n const idx = this.csvHeaders.indexOf(col);\n record[field.key] = idx >= 0 ? (row[idx] ?? '') : '';\n }\n return record;\n });\n }\n\n submitImport(): void {\n this.importing = true;\n this.cdr.detectChanges();\n const records = this.csvRows.map(row => {\n const record: Record<string, string> = {};\n for (const field of (this.config.importFields ?? [])) {\n const col = this.columnMapping[field.key];\n if (!col) continue;\n const idx = this.csvHeaders.indexOf(col);\n record[field.key] = idx >= 0 ? (row[idx]?.trim() ?? '') : '';\n }\n return record;\n });\n this.http.post<{ imported: number; errors?: ImportError[]; warnings?: ImportError[] }>(`${this.apiBase}${this.config.importEndpoint ?? ''}`, { records }).subscribe({\n next: (result) => {\n this.importing = false;\n this.importResult = { imported: result.imported, errors: result.errors ?? [], warnings: result.warnings ?? [] };\n this.step = 'result';\n if (result.imported > 0) this.toast.show(`${result.imported} ${this.config.entityNamePlural.toLowerCase()} imported successfully`);\n this.cdr.detectChanges();\n },\n error: () => {\n this.importing = false;\n this.toast.error('Import failed. Please check your data and try again.');\n this.cdr.detectChanges();\n },\n });\n }\n}\n","import { Injectable, computed, signal } from '@angular/core';\n\nexport interface ActiveConfirm {\n title: string;\n message: string;\n confirmLabel: string;\n cancelLabel: string;\n variant: 'danger' | 'primary' | 'warning';\n /** Optional bullet list rendered under the message (plain text). */\n details: string[];\n}\n\n@Injectable({ providedIn: 'root' })\nexport class ConfirmService {\n private _open = signal(false);\n private _active = signal<ActiveConfirm | null>(null);\n private _resolve: ((v: boolean) => void) | null = null;\n\n readonly open = this._open.asReadonly();\n readonly active = this._active.asReadonly();\n\n /**\n * Live <ui-confirm-outlet> instances, oldest first. Every ui-data-table renders an outlet, so\n * several can exist at once (e.g. during a route change the next page's table is created before\n * the previous page's is destroyed). Only the oldest live outlet renders the dialog; when it is\n * destroyed the next one takes over, so a confirm always has somewhere to render.\n */\n private readonly _outlets = signal<readonly object[]>([]);\n readonly primaryOutlet = computed(() => this._outlets()[0] ?? null);\n\n registerOutlet(outlet: object): void {\n this._outlets.update(list => [...list, outlet]);\n }\n\n unregisterOutlet(outlet: object): void {\n this._outlets.update(list => list.filter(o => o !== outlet));\n }\n\n confirm(config: {\n title: string;\n message: string;\n confirmLabel?: string;\n cancelLabel?: string;\n variant?: 'danger' | 'primary' | 'warning';\n details?: string[];\n }): Promise<boolean> {\n return new Promise(resolve => {\n this._resolve = resolve;\n this._active.set({\n title: config.title,\n message: config.message,\n confirmLabel: config.confirmLabel ?? 'Confirm',\n cancelLabel: config.cancelLabel ?? 'Cancel',\n variant: config.variant ?? 'danger',\n details: config.details ?? [],\n });\n this._open.set(true);\n });\n }\n\n dismiss(confirmed: boolean): void {\n this._open.set(false);\n const res = this._resolve;\n this._resolve = null;\n this._active.set(null);\n res?.(confirmed);\n }\n}\n","import { Component, ChangeDetectionStrategy, inject, computed, OnInit, OnDestroy } from '@angular/core';\nimport { ConfirmService } from './confirm.service';\nimport { ModalComponent } from '../modal/modal.component';\nimport { ButtonComponent } from '../button/button.component';\n\n// Only one outlet renders the modal (preventing double-backdrop stacking when e.g. app-shell and\n// data-table both provide one). The primary outlet is re-evaluated whenever outlets come and go —\n// see ConfirmService.primaryOutlet — rather than fixed at creation time.\n\n@Component({\n selector: 'ui-confirm-outlet',\n standalone: true,\n changeDetection: ChangeDetectionStrategy.OnPush,\n imports: [ModalComponent, ButtonComponent],\n template: `\n @if (_isPrimary() && svc.active()) {\n <ui-modal\n data-testid=\"confirm-modal\"\n [open]=\"svc.open()\"\n [title]=\"svc.active()!.title\"\n maxWidth=\"420px\"\n (close)=\"svc.dismiss(false)\">\n <p class=\"confirm-msg\" data-testid=\"confirm-message\">{{ svc.active()!.message }}</p>\n @if (svc.active()!.details.length) {\n <ul class=\"confirm-details\" data-testid=\"confirm-details\" aria-label=\"What will happen\">\n @for (d of svc.active()!.details; track $index) { <li>{{ d }}</li> }\n </ul>\n }\n <ui-button modal-footer-left data-testid=\"confirm-cancel\" variant=\"secondary\" size=\"sm\" (click)=\"svc.dismiss(false)\">\n {{ svc.active()!.cancelLabel }}\n </ui-button>\n <ui-button\n modal-footer-right\n data-testid=\"confirm-accept\"\n [variant]=\"svc.active()!.variant\"\n size=\"sm\"\n (click)=\"svc.dismiss(true)\">\n {{ svc.active()!.confirmLabel }}\n </ui-button>\n </ui-modal>\n }\n `,\n styles: [`\n .confirm-msg { margin: 0; font-size: 0.875rem; color: #64748b; line-height: 1.5; overflow-wrap: anywhere; word-break: break-word; white-space: pre-line; }\n .confirm-details { margin: 10px 0 0; padding-left: 20px; font-size: 0.875rem; color: #64748b; line-height: 1.5; overflow-wrap: anywhere; word-break: break-word; }\n .confirm-details li + li { margin-top: 4px; }\n `],\n})\nexport class ConfirmOutletComponent implements OnInit, OnDestroy {\n protected readonly svc = inject(ConfirmService);\n protected readonly _isPrimary = computed(() => this.svc.primaryOutlet() === this);\n\n ngOnInit(): void {\n this.svc.registerOutlet(this);\n }\n\n ngOnDestroy(): void {\n this.svc.unregisterOutlet(this);\n }\n}\n","import { Component, ChangeDetectionStrategy, input } from '@angular/core';\n\n@Component({\n selector: 'ui-empty-state',\n standalone: true,\n changeDetection: ChangeDetectionStrategy.OnPush,\n template: `\n <div class=\"es-wrap\">\n <div class=\"es-icon\">\n @switch (iconType()) {\n @case ('search') {\n <svg width=\"28\" height=\"28\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"1.5\" aria-hidden=\"true\"><circle cx=\"11\" cy=\"11\" r=\"8\"/><line x1=\"21\" y1=\"21\" x2=\"16.65\" y2=\"16.65\"/></svg>\n }\n @case ('table') {\n <svg width=\"28\" height=\"28\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"1.5\" aria-hidden=\"true\"><rect x=\"3\" y=\"3\" width=\"18\" height=\"18\" rx=\"2\"/><path d=\"M3 9h18M9 21V9\"/></svg>\n }\n @case ('chart') {\n <svg width=\"28\" height=\"28\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"1.5\" aria-hidden=\"true\"><line x1=\"18\" y1=\"20\" x2=\"18\" y2=\"10\"/><line x1=\"12\" y1=\"20\" x2=\"12\" y2=\"4\"/><line x1=\"6\" y1=\"20\" x2=\"6\" y2=\"14\"/></svg>\n }\n @case ('users') {\n <svg width=\"28\" height=\"28\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"1.5\" aria-hidden=\"true\"><path d=\"M17 21v-2a4 4 0 0 0-4-4H5a4 4 0 0 0-4 4v2\"/><circle cx=\"9\" cy=\"7\" r=\"4\"/><path d=\"M23 21v-2a4 4 0 0 0-3-3.87\"/><path d=\"M16 3.13a4 4 0 0 1 0 7.75\"/></svg>\n }\n @case ('person') {\n <svg width=\"28\" height=\"28\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"1.5\" aria-hidden=\"true\"><path d=\"M20 21v-2a4 4 0 0 0-4-4H8a4 4 0 0 0-4 4v2\"/><circle cx=\"12\" cy=\"7\" r=\"4\"/></svg>\n }\n @case ('message') {\n <svg width=\"28\" height=\"28\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"1.5\" aria-hidden=\"true\"><path d=\"M21 15a2 2 0 0 1-2 2H7l-4 4V5a2 2 0 0 1 2-2h14a2 2 0 0 1 2 2z\"/></svg>\n }\n @case ('email') {\n <svg width=\"28\" height=\"28\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"1.5\" aria-hidden=\"true\"><rect x=\"2\" y=\"4\" width=\"20\" height=\"16\" rx=\"2\"/><path d=\"m22 7-8.97 5.7a1.94 1.94 0 0 1-2.06 0L2 7\"/></svg>\n }\n @case ('task') {\n <svg width=\"28\" height=\"28\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"1.5\" aria-hidden=\"true\"><path d=\"M9 11l3 3L22 4\"/><path d=\"M21 12v7a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2V5a2 2 0 0 1 2-2h11\"/></svg>\n }\n @case ('house') {\n <svg width=\"28\" height=\"28\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"1.5\" aria-hidden=\"true\"><path d=\"M3 9l9-7 9 7v11a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2z\"/><polyline points=\"9 22 9 12 15 12 15 22\"/></svg>\n }\n @case ('money') {\n <svg width=\"28\" height=\"28\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"1.5\" aria-hidden=\"true\"><line x1=\"12\" y1=\"1\" x2=\"12\" y2=\"23\"/><path d=\"M17 5H9.5a3.5 3.5 0 0 0 0 7h5a3.5 3.5 0 0 1 0 7H6\"/></svg>\n }\n @case ('star') {\n <svg width=\"28\" height=\"28\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"1.5\" aria-hidden=\"true\"><polygon points=\"12 2 15.09 8.26 22 9.27 17 14.14 18.18 21.02 12 17.77 5.82 21.02 7 14.14 2 9.27 8.91 8.26 12 2\"/></svg>\n }\n @case ('video') {\n <svg width=\"28\" height=\"28\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"1.5\" aria-hidden=\"true\"><rect x=\"2\" y=\"5\" width=\"14\" height=\"14\" rx=\"2\"/><path d=\"m16 10 6-3.5v11L16 14\"/></svg>\n }\n @case ('shield') {\n <svg width=\"28\" height=\"28\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"1.5\" aria-hidden=\"true\"><path d=\"M12 22s8-4 8-10V5l-8-3-8 3v7c0 6 8 10 8 10z\"/></svg>\n }\n @default {\n <svg width=\"28\" height=\"28\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"1.5\" aria-hidden=\"true\"><path d=\"M14 2H6a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V8z\"/><polyline points=\"14 2 14 8 20 8\"/><line x1=\"16\" y1=\"13\" x2=\"8\" y2=\"13\"/><line x1=\"16\" y1=\"17\" x2=\"8\" y2=\"17\"/></svg>\n }\n }\n </div>\n @if (title()) {\n <strong class=\"es-title\">{{ title() }}</strong>\n }\n <p class=\"es-message\">{{ message() }}</p>\n <div class=\"es-action\">\n <ng-content />\n </div>\n </div>\n `,\n styles: [`\n :host { display: flex; flex: 1; }\n .es-wrap {\n display: flex; flex-direction: column; align-items: center; justify-content: center;\n gap: 10px; padding: 48px 24px; text-align: center; flex: 1; min-height: 160px;\n }\n .es-icon {\n width: 56px; height: 56px; border-radius: 50%;\n background: #eff6ff; color: #3b82f6;\n display: flex; align-items: center; justify-content: center; flex-shrink: 0;\n }\n .es-title { font-size: 0.9375rem; font-weight: 700; color: #1e293b; margin: 0; }\n .es-message { color: #94a3b8; font-size: 0.8125rem; margin: 0; line-height: 1.5; }\n .es-action { margin-top: 4px; &:empty { display: none; } }\n `],\n})\nexport class EmptyStateComponent {\n readonly message = input('');\n readonly title = input('');\n readonly iconType = input('document');\n}\n","import { Component, ChangeDetectionStrategy, input, output } from '@angular/core';\nimport { ButtonComponent } from '../button/button.component';\n\n@Component({\n selector: 'ui-error-state',\n standalone: true,\n changeDetection: ChangeDetectionStrategy.OnPush,\n imports: [ButtonComponent],\n template: `\n <div class=\"es-wrap\" role=\"alert\">\n <div class=\"es-icon\">\n <svg width=\"28\" height=\"28\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"1.5\" aria-hidden=\"true\">\n <circle cx=\"12\" cy=\"12\" r=\"10\"/>\n <line x1=\"12\" y1=\"8\" x2=\"12\" y2=\"12\"/>\n <line x1=\"12\" y1=\"16\" x2=\"12.01\" y2=\"16\" stroke-width=\"2.5\"/>\n </svg>\n </div>\n <p class=\"es-message\">{{ message() || 'Something went wrong. Please try again.' }}</p>\n @if (retryLabel()) {\n <ui-button variant=\"secondary\" size=\"sm\" [loading]=\"retrying()\" (click)=\"retry.emit()\">\n {{ retryLabel() }}\n </ui-button>\n }\n </div>\n `,\n styles: [`\n :host { display: flex; flex: 1; }\n .es-wrap {\n display: flex; flex-direction: column; align-items: center; justify-content: center;\n gap: 12px; padding: 48px 24px; text-align: center; flex: 1; min-height: 160px;\n }\n .es-icon {\n width: 56px; height: 56px; border-radius: 50%;\n background: #fee2e2; color: #ef4444;\n display: flex; align-items: center; justify-content: center; flex-shrink: 0;\n }\n .es-message { margin: 0; font-size: 0.875rem; color: #64748b; max-width: 360px; line-height: 1.5; }\n `],\n})\nexport class ErrorStateComponent {\n readonly message = input('');\n readonly retryLabel = input('Try again');\n readonly retrying = input(false);\n readonly retry = output<void>();\n}\n","import {\n Component, input, output, signal, computed, viewChild, effect, untracked,\n ChangeDetectionStrategy, inject, DestroyRef, OnInit, Signal,\n} from '@angular/core';\n\nimport { DatePipe, NgTemplateOutlet } from '@angular/common';\nimport { FormsModule } from '@angular/forms';\nimport { HttpClient, HttpParams } from '@angular/common/http';\nimport { Subject, Subscription } from 'rxjs';\nimport { switchMap, catchError } from 'rxjs/operators';\nimport { of } from 'rxjs';\nimport { takeUntilDestroyed } from '@angular/core/rxjs-interop';\nimport { FORGEROUTE_CONFIG } from '../config/forgeroute.config';\nimport {\n ColumnDef, SortDir, QuickFilterTab, FilterOption, SavedView, ToolbarAction,\n BadgeConfig, autoVariant, SEMANTIC_BADGE_MAP, BADGE_VARIANT_STYLES,\n BulkActionDef, BulkActionEvent, BulkConfirmContext, BulkConfirmCopy, DROPDOWN_GROUP, RowAction, TablePermission, BulkUpdateOption } from './data-table.types';\nimport { getEligibleOptions } from '../bulk-eligible';\nimport { SearchInputComponent } from '../search-input/search-input.component';\nimport { PaginationComponent } from '../pagination/pagination.component';\nimport { QuickFilterTabsComponent } from '../quick-filter-tabs/quick-filter-tabs.component';\nimport { ColumnFilterComponent } from '../column-filter/column-filter.component';\nimport { TableToolbarComponent } from '../table-toolbar/table-toolbar.component';\nimport { BadgeComponent } from '../badge/badge.component';\nimport { AvatarComponent } from '../avatar/avatar.component';\nimport { MoreMenuComponent } from '../more-menu/more-menu.component';\nimport { SkeletonComponent } from '../skeleton/skeleton.component';\nimport { ProgressBarComponent } from '../progress-bar/progress-bar.component';\nimport { ButtonComponent } from '../button/button.component';\nimport { CheckboxComponent } from '../checkbox/checkbox.component';\nimport { SelectComponent } from '../select/select.component';\nimport { ToastComponent } from '../toast/toast.component';\nimport { ToastService } from '../toast/toast.service';\nimport { humanReadableMessage } from '../utils/human-readable-message';\nimport { ImportExportComponent, ImportExportConfig, ImportSource } from '../import-export/import-export.component';\nimport { ConfirmService } from '../confirm/confirm.service';\nimport { ConfirmOutletComponent } from '../confirm/confirm-outlet.component';\nimport { ModalComponent } from '../modal/modal.component';\nimport { EmptyStateComponent } from '../empty-state/empty-state.component';\nimport { ErrorStateComponent } from '../error-state/error-state.component';\n\nfunction formatPhone(raw: string | null | undefined): string {\n if (!raw) return '—';\n const digits = raw.replace(/\\D/g, '');\n if (digits.length === 10) {\n return `(${digits.slice(0, 3)}) ${digits.slice(3, 6)}-${digits.slice(6)}`;\n }\n if (digits.length === 11 && digits[0] === '1') {\n return `(${digits.slice(1, 4)}) ${digits.slice(4, 7)}-${digits.slice(7)}`;\n }\n return raw;\n}\n\n/** Simple English plural: \"property\" → \"properties\", \"box\" → \"boxes\", \"contact\" → \"contacts\". */\nexport function pluralize(word: string): string {\n if (!word) return word;\n if (/[^aeiou]y$/i.test(word)) return word.slice(0, -1) + 'ies';\n if (/(s|x|z|ch|sh)$/i.test(word)) return word + 'es';\n return word + 's';\n}\n\n@Component({\n selector: 'ui-data-table',\n standalone: true,\n changeDetection: ChangeDetectionStrategy.OnPush,\n providers: [DatePipe, { provide: DROPDOWN_GROUP, useFactory: () => signal('') }],\n imports: [\n DatePipe, NgTemplateOutlet,\n SearchInputComponent, PaginationComponent, QuickFilterTabsComponent,\n ColumnFilterComponent, TableToolbarComponent,\n FormsModule,\n BadgeComponent, AvatarComponent, MoreMenuComponent, SkeletonComponent, ProgressBarComponent, ButtonComponent, CheckboxComponent, SelectComponent,\n ToastComponent, ImportExportComponent, ConfirmOutletComponent, ModalComponent,\n EmptyStateComponent, ErrorStateComponent,\n ],\n template: `\n <div class=\"dt-host\">\n\n <!-- ── HEADER ──────────────────────────────────────────────── -->\n @if (!_hasLoaded()) {\n <!-- Skeleton header: shaped to match real controls -->\n <div class=\"dt-sk-toolbar\">\n <div class=\"dt-sk-toolbar-left\">\n <div class=\"dt-sk-search\">\n <div class=\"dt-sk-shimmer dt-sk-search-icon\"></div>\n <div class=\"dt-sk-shimmer dt-sk-search-text\"></div>\n </div>\n @if (_effectiveTabs().length) {\n <div class=\"dt-sk-tab-strip\">\n @for (_ of _effectiveTabs(); track $index) {\n <div class=\"dt-sk-shimmer dt-sk-tab\"></div>\n }\n </div>\n }\n </div>\n @if (showToolbar()) {\n <div class=\"dt-sk-toolbar-right\">\n <div class=\"dt-sk-shimmer dt-sk-icon-btn\"></div>\n <div class=\"dt-sk-shimmer dt-sk-icon-btn\"></div>\n </div>\n }\n </div>\n } @else {\n <div class=\"dt-header alive-header\">\n <div class=\"dt-header-left\">\n @if (!_emptyUnfiltered()) {\n <ui-search-input\n [value]=\"_search()\"\n [placeholder]=\"_effectiveSearchPlaceholder()\"\n [loading]=\"_searchLoading()\"\n (searchChange)=\"onSearchChange($event)\" />\n }\n @if (_effectiveTabs().length && !_emptyUnfiltered()) {\n <ui-quick-filter-tabs\n [tabs]=\"_effectiveTabs()\"\n [counts]=\"_tabCounts()\"\n [activeValue]=\"_activeTab()\"\n (tabChange)=\"onTabChange($event)\" />\n }\n </div>\n <div class=\"dt-header-right\">\n @if (showToolbar()) {\n <ui-table-toolbar\n [columns]=\"columns()\"\n [visibleColumns]=\"visibleCols().map(c => c.key)\"\n [lockedColumn]=\"lockedColumn()\"\n [savedViews]=\"_effectiveSavedViews()\"\n [activeViewId]=\"_activeViewId()\"\n [showSavedViews]=\"_effectiveSavedViews().length > 0 || !!page() || showToolbar()\"\n [readOnly]=\"!_canEdit()\"\n [tableButtons]=\"_toolbarButtons()\"\n [hideMoreActions]=\"_emptyUnfiltered() && !_keepMoreActions()\"\n (visibleColumnsChange)=\"_visibleColumns.set($event)\"\n (saveViewClick)=\"onSaveViewClick($event)\"\n (loadViewClick)=\"onLoadViewClick($event)\"\n (deleteViewClick)=\"onDeleteViewClick($event)\"\n (tableButtonClick)=\"onInternalButtonClick($event)\" />\n }\n </div>\n </div>\n }\n\n <!-- ── BULK SELECTION BAR ───────────────────────────────── -->\n @if (_isSelectable() && _selectedIds().size > 0) {\n <div class=\"dt-bulk-bar\">\n <span class=\"dt-bulk-count\" role=\"status\" aria-live=\"polite\">{{ _selectedIds().size }} {{ _countLabel(_selectedIds().size) }} selected</span>\n <div class=\"dt-bulk-actions\">\n @if (_activeBulkDef(); as def) {\n <!-- Inline select for field-based bulk actions -->\n <ui-select name=\"dt-bulk\" size=\"sm\" [ngModel]=\"_bulkSelectStr\" (ngModelChange)=\"_bulkSelectStr = $event\" [options]=\"_eligibleBulkOptions()\" />\n <ui-button variant=\"primary\" size=\"sm\" [disabled]=\"!_bulkSelectStr\" [loading]=\"_applyingBulk()\" (click)=\"applyBulkAction()\">Apply</ui-button>\n <ui-button variant=\"ghost\" size=\"sm\" [disabled]=\"_applyingBulk()\" (click)=\"_activeBulkType.set(null)\">Cancel</ui-button>\n } @else {\n @for (def of _bulkDefs(); track def.type) {\n <ui-button [variant]=\"def.variant ?? 'secondary'\" size=\"sm\"\n [loading]=\"def.type === 'delete' && _deletingBulk()\"\n [disabled]=\"_deletingBulk() || !!_bulkBlockedReason(def)\"\n [ariaLabel]=\"_bulkBlockedReason(def) ? def.label + ' — ' + _bulkBlockedReason(def) : undefined\"\n [attr.title]=\"_bulkBlockedReason(def) || null\"\n (click)=\"onBulkActionClick(def)\">{{ def.label }}</ui-button>\n }\n <!-- Visible reason for a blocked action (the title tooltip is invisible on touch) -->\n @for (reason of _bulkBlockedHints(); track reason) {\n <span class=\"dt-bulk-hint\" role=\"status\" data-testid=\"dt-bulk-hint\">{{ reason }}</span>\n }\n }\n </div>\n <ui-button variant=\"ghost\" size=\"sm\" (click)=\"clearSelection()\">Clear</ui-button>\n </div>\n }\n\n <!-- Shared image-cell markup (table cell + mobile card header) -->\n <ng-template #imageCell let-col=\"col\" let-item=\"item\" let-small=\"small\">\n @let imgUrl = imageUrl(col, item);\n @let imgClass = imageClass(col, !!small);\n @if (imgUrl && !_failedImages().has(imgUrl)) {\n <img [class]=\"imgClass + ' dt-img'\" [src]=\"imgUrl\" [attr.alt]=\"imageAlt(col, item)\"\n loading=\"lazy\" decoding=\"async\" (error)=\"onImageError(imgUrl)\" />\n } @else {\n <span [class]=\"imgClass + ' dt-img-ph'\" aria-hidden=\"true\">\n @if (col.imagePlaceholder === 'person') {\n <svg viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"1.8\" stroke-linecap=\"round\" stroke-linejoin=\"round\"><circle cx=\"12\" cy=\"8\" r=\"4\"/><path d=\"M4 21c0-4 3.6-7 8-7s8 3 8 7\"/></svg>\n } @else {\n <svg viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"1.8\" stroke-linecap=\"round\" stroke-linejoin=\"round\"><rect x=\"3\" y=\"4\" width=\"18\" height=\"16\" rx=\"2\"/><circle cx=\"9\" cy=\"10\" r=\"1.8\"/><path d=\"M21 16l-5-5-8 9\"/></svg>\n }\n </span>\n }\n </ng-template>\n\n <!-- ── TABLE ─────────────────────────────────────────────── -->\n @if (!_isPhone()) {\n <div class=\"dt-table-wrap\">\n <table [attr.aria-label]=\"tableLabel()\" [attr.aria-busy]=\"_loading()\">\n <thead>\n <tr>\n @if (_isSelectable()) {\n <th class=\"dt-th-cb\" scope=\"col\">\n <ui-checkbox\n ariaLabel=\"Select all rows\"\n [checked]=\"_allSelected()\"\n [indeterminate]=\"_someSelected()\"\n (checkedChange)=\"toggleSelectAll()\" />\n </th>\n }\n @for (col of visibleCols(); track col.key) {\n <th\n scope=\"col\"\n [style.text-align]=\"col.headerAlign ?? (col.cellType === 'number' ? 'right' : 'center')\"\n [attr.aria-sort]=\"_isSortable(col) ? sortAriaLabel(col.key) : null\"\n [class.dt-th-sorted]=\"_sortField() === col.key\"\n [class.dt-th-actions]=\"col.cellType === 'actions'\">\n <div class=\"dt-th-inner\">\n @if (_isSortable(col)) {\n <button\n class=\"dt-sort-btn\"\n (click)=\"onSort(col.key)\"\n [attr.aria-label]=\"'Sort by ' + col.label\"\n [attr.aria-sort]=\"sortAriaLabel(col.key)\">\n {{ col.label }}\n @if (_sortField() === col.key) {\n <svg class=\"dt-sort-icon\" [class.dt-sort-desc]=\"_sortDir() === 'desc'\" width=\"13\" height=\"13\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2.5\" aria-hidden=\"true\"><path d=\"M12 5v14M5 12l7-7 7 7\"/></svg>\n } @else if (!_sortField()) {\n <svg class=\"dt-sort-icon dt-sort-inactive\" width=\"13\" height=\"13\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2.5\" aria-hidden=\"true\"><path d=\"M7 15l5 5 5-5M7 9l5-5 5 5\"/></svg>\n }\n </button>\n } @else {\n {{ col.label }}\n }\n @if (getColFilter(col); as cf) {\n <ui-column-filter\n [label]=\"col.label\"\n [options]=\"cf.options\"\n [activeValues]=\"cf.activeValues\"\n (filterChange)=\"onColFilterChange(col.key, $event)\" />\n }\n </div>\n </th>\n }\n </tr>\n </thead>\n <tbody>\n\n <!-- Reload skeleton (search / filter / sort) -->\n @if (_loading()) {\n @for (_ of skeletonRows(); track $index) {\n <tr class=\"dt-sk-reload-row\">\n @if (_isSelectable()) {\n <td class=\"dt-td-cb\"></td>\n }\n @for (col of visibleCols(); track col.key) {\n <td><ui-skeleton [height]=\"14\" [radius]=\"4\" /></td>\n }\n </tr>\n }\n }\n\n <!-- Error -->\n @else if (_error()) {\n <tr>\n <td [attr.colspan]=\"visibleCols().length + (_isSelectable() ? 1 : 0)\" class=\"dt-state-cell\">\n <ui-error-state [message]=\"_error()!\" (retry)=\"load()\" />\n </td>\n </tr>\n }\n\n <!-- Empty (no filters active) -->\n @else if (_items().length === 0 && !_hasActiveFilters()) {\n <tr>\n <td [attr.colspan]=\"visibleCols().length + (_isSelectable() ? 1 : 0)\" class=\"dt-state-cell\">\n <ui-empty-state iconType=\"table\" [message]=\"emptyMessage()\" />\n </td>\n </tr>\n }\n\n <!-- No match -->\n @else if (_items().length === 0 && _hasActiveFilters()) {\n <tr>\n <td [attr.colspan]=\"visibleCols().length + (_isSelectable() ? 1 : 0)\" class=\"dt-state-cell\">\n <ui-empty-state iconType=\"search\" message=\"No results match your search or filters.\">\n <ui-button variant=\"secondary\" size=\"sm\" (click)=\"clearFilters()\">Clear filters</ui-button>\n </ui-empty-state>\n </td>\n </tr>\n }\n\n <!-- Data rows — --i is capped at 8 so the entrance stagger never exceeds ~240ms -->\n @else {\n @for (item of _items(); track trackItem(item, $index)) {\n <tr\n class=\"dt-row alive-row\"\n [style.--i]=\"$index < 8 ? $index : 8\"\n [class.dt-row-clickable]=\"isRowClickable(item)\"\n [class.dt-row-selected]=\"_isSelectable() && _selectedIds().has(item['id'])\"\n [attr.tabindex]=\"isRowClickable(item) ? 0 : null\"\n [attr.aria-label]=\"rowLabel(item)\"\n (keydown)=\"onRowKeydown($event, item)\"\n (click)=\"onRowClick(item)\">\n @if (_isSelectable()) {\n <td class=\"dt-td-cb\" (click)=\"$event.stopPropagation()\">\n <ui-checkbox\n [ariaLabel]=\"selectRowLabel(item)\"\n [checked]=\"_selectedIds().has(item['id'])\"\n (checkedChange)=\"toggleRow(item)\" />\n </td>\n }\n @for (col of visibleCols(); track col.key) {\n <td\n [style.text-align]=\"resolveCellAlign(col, item)\"\n [class.dt-td-actions]=\"col.cellType === 'actions'\">\n\n @switch (col.cellType) {\n\n <!-- Badge cell -->\n @case ('badge') {\n @let bCfg = resolveBadge(col, item);\n @if (bCfg) { <ui-badge [label]=\"bCfg.label\" [variant]=\"bCfg.variant\" /> } @else { — }\n }\n\n <!-- Date cell -->\n @case ('date') {\n {{ item[col.key] ? (item[col.key] | date:(col.dateFormat ?? 'MMM d, y')) : '—' }}\n }\n\n <!-- Phone cell -->\n @case ('phone') {\n {{ phoneCell(item[col.key]) }}\n }\n\n <!-- Avatar + name cell -->\n @case ('avatar-name') {\n @let displayName = col.nameFn ? col.nameFn(item) : (item[col.key] ?? '');\n @let initials = col.initialsFn ? col.initialsFn(item) : getInitials(displayName);\n <span class=\"dt-avatar-name\">\n <ui-avatar [initials]=\"initials\" [name]=\"displayName\" [size]=\"28\" />\n <span class=\"dt-name-text\">{{ displayName }}</span>\n </span>\n }\n\n <!-- Days ago cell -->\n @case ('days-ago') {\n @let days = item[col.daysKey ?? col.key];\n <span [class.dt-overdue]=\"days !== null && days > (col.overdueThreshold ?? 30)\">\n @if (days !== null && days !== undefined) {\n {{ days }}d ago\n } @else {\n Never\n }\n </span>\n }\n\n <!-- Progress cell -->\n @case ('progress') {\n @let pCfg = col.progressFn ? col.progressFn(item) : null;\n @if (pCfg) {\n <ui-progress-bar [value]=\"pCfg.value\" [max]=\"pCfg.max\" [label]=\"pCfg.label ?? ''\"\n [variant]=\"pCfg.variant ?? 'blue'\" [tooltip]=\"pCfg.tooltip ?? ''\" />\n } @else { <span class=\"dt-none\">—</span> }\n }\n\n <!-- Tags cell -->\n @case ('tags') {\n @let tags = item[col.tagsKey ?? col.key];\n @if (tags?.length > 0) {\n <span class=\"dt-tags\">\n <ui-badge\n [label]=\"tags[0].name\"\n [variant]=\"tags[0].variant ?? null\" />\n @if (tags.length > 1) {\n <span\n class=\"dt-tags-more\"\n (mouseenter)=\"showTagTooltip($event, tags.slice(1))\"\n (mouseleave)=\"hideTagTooltip()\"\n (focus)=\"showTagTooltip($event, tags.slice(1))\"\n (blur)=\"hideTagTooltip()\"\n tabindex=\"0\"\n [attr.aria-label]=\"tags.slice(1).map(t => t.name).join(', ')\">\n +{{ tags.length - 1 }}\n </span>\n }\n </span>\n } @else {\n <span class=\"dt-none\">—</span>\n }\n }\n\n <!-- Image cell -->\n @case ('image') {\n <ng-container [ngTemplateOutlet]=\"imageCell\" [ngTemplateOutletContext]=\"{ col: col, item: item }\" />\n }\n\n <!-- Actions cell -->\n @case ('actions') {\n <span class=\"dt-actions-cell\" (click)=\"$event.stopPropagation()\">\n @let rowActions = _rowActions(col, item);\n @if (rowActions.length) {\n <ui-more-menu\n [actions]=\"rowActions\"\n [ariaLabel]=\"rowActionsLabel(item)\"\n (actionClick)=\"onTableRowAction($event, col, item)\" />\n }\n </span>\n }\n\n <!-- Default: plain text -->\n @default {\n {{ col.render ? col.render(item) : (item[col.key] ?? '—') }}\n }\n\n }\n </td>\n }\n </tr>\n }\n }\n </tbody>\n </table>\n </div>\n }\n\n <!-- ── CARD LIST (phone, ≤640px) — only one tree is rendered per viewport ─── -->\n @if (_isPhone()) {\n <!-- Phone sort + column filters: the table header (which owns them on larger screens) isn't rendered here -->\n @if (_hasPhoneControls()) {\n <div class=\"dt-phone-controls\" role=\"group\" aria-label=\"Sort and filter\">\n @if (_phoneSortOptions().length > 1) {\n <label class=\"dt-phone-sort\">\n <span class=\"dt-phone-sort-label\">Sort</span>\n <select class=\"dt-phone-sort-select\" aria-label=\"Sort by\" (change)=\"onPhoneSort($any($event.target).value)\">\n @for (opt of _phoneSortOptions(); track opt.value) {\n <option [value]=\"opt.value\" [selected]=\"opt.value === _phoneSortValue()\">{{ opt.label }}</option>\n }\n </select>\n </label>\n }\n @if (_phoneFilterCols().length) {\n <div class=\"dt-phone-filters\">\n @for (f of _phoneFilterCols(); track f.col.key) {\n <ui-column-filter\n [chip]=\"true\"\n [label]=\"f.col.label\"\n [options]=\"f.cf.options\"\n [activeValues]=\"f.cf.activeValues\"\n (filterChange)=\"onColFilterChange(f.col.key, $event)\" />\n }\n </div>\n }\n </div>\n }\n <div class=\"dt-card-list\" role=\"list\" [attr.aria-label]=\"tableLabel()\">\n\n @if (_loading()) {\n @for (_ of skeletonRows(); track $index) {\n <!-- Card skeleton matches exact layout: avatar circle + name line + menu dot | badge pills | key/value rows -->\n <div class=\"dt-card dt-card-sk\" role=\"listitem\">\n <div class=\"dt-card-sk-header\">\n <!-- Avatar circle -->\n <ui-skeleton [height]=\"32\" width=\"32px\" [radius]=\"50\" />\n <!-- Name line -->\n <ui-skeleton [height]=\"14\" width=\"160px\" [radius]=\"4\" />\n <!-- Action menu dot -->\n <ui-skeleton [height]=\"24\" width=\"24px\" [radius]=\"5\" style=\"margin-left:auto\" />\n </div>\n <!-- Badge pills row -->\n <div class=\"dt-card-sk-badges\">\n <ui-skeleton [height]=\"22\" width=\"72px\" [radius]=\"999\" />\n <ui-skeleton [height]=\"22\" width=\"52px\" [radius]=\"999\" />\n </div>\n <!-- Detail key/value rows -->\n <div class=\"dt-card-sk-rows\">\n <ui-skeleton [height]=\"12\" [radius]=\"4\" />\n <ui-skeleton [height]=\"12\" width=\"70%\" [radius]=\"4\" />\n </div>\n </div>\n }\n\n } @else if (_error()) {\n <ui-error-state [message]=\"_error()!\" (retry)=\"load()\" />\n\n } @else if (_items().length === 0 && !_hasActiveFilters()) {\n <ui-empty-state iconType=\"table\" [message]=\"emptyMessage()\" />\n\n } @else if (_items().length === 0 && _hasActiveFilters()) {\n <ui-empty-state iconType=\"search\" message=\"No results match your search or filters.\">\n <ui-button variant=\"secondary\" size=\"sm\" (click)=\"clearFilters()\">Clear filters</ui-button>\n </ui-empty-state>\n\n } @else {\n @for (item of _items(); track trackItem(item, $index)) {\n <div\n class=\"dt-card alive-row\"\n [style.--i]=\"$index < 8 ? $index : 8\"\n role=\"listitem\"\n [class.dt-card-clickable]=\"isRowClickable(item)\"\n [class.dt-card-selected]=\"_isSelectable() && _selectedIds().has(item['id'])\"\n [attr.tabindex]=\"isRowClickable(item) ? 0 : null\"\n [attr.aria-label]=\"rowLabel(item)\"\n (keydown)=\"onRowKeydown($event, item)\"\n (click)=\"onRowClick(item)\">\n\n <!-- Selection checkbox (shown when bulk actions are enabled) -->\n @if (_isSelectable()) {\n <div class=\"dt-card-cb\" (click)=\"$event.stopPropagation()\">\n <ui-checkbox\n [ariaLabel]=\"selectRowLabel(item)\"\n [checked]=\"_selectedIds().has(item['id'])\"\n (checkedChange)=\"toggleRow(item)\" />\n </div>\n }\n\n <!-- Title row: avatar/name + action menu -->\n <div class=\"dt-card-header\">\n @if (_cardCols().imageCol; as ic) {\n <ng-container [ngTemplateOutlet]=\"imageCell\" [ngTemplateOutletContext]=\"{ col: ic, item: item, small: true }\" />\n }\n @if (_cardCols().titleCol; as tc) {\n @if (tc.cellType === 'avatar-name') {\n @let displayName = tc.nameFn ? tc.nameFn(item) : (item[tc.key] ?? '');\n @let initials = tc.initialsFn ? tc.initialsFn(item) : getInitials(displayName);\n <span class=\"dt-card-title\">\n <ui-avatar [initials]=\"initials\" [name]=\"displayName\" [size]=\"32\" />\n <span class=\"dt-card-name\">{{ displayName }}</span>\n </span>\n } @else {\n <span class=\"dt-card-name dt-card-name-plain\">\n {{ tc.render ? tc.render(item) : (item[tc.key] ?? '—') }}\n </span>\n }\n }\n @if (_cardCols().actionsCol; as ac) {\n @let cardActions = _rowActions(ac, item);\n @if (cardActions.length) {\n <span class=\"dt-card-actions\" (click)=\"$event.stopPropagation()\">\n <ui-more-menu\n [actions]=\"cardActions\"\n [ariaLabel]=\"rowActionsLabel(item)\"\n (actionClick)=\"onTableRowAction($event, ac, item)\" />\n </span>\n }\n }\n </div>\n\n <!-- Badge pills (status / type / source etc.) -->\n @if (_cardCols().badgeCols.length) {\n <div class=\"dt-card-badges\">\n @for (bc of _cardCols().badgeCols; track bc.key) {\n @let bCfg = resolveBadge(bc, item);\n @if (bCfg) { <ui-badge [label]=\"bCfg.label\" [variant]=\"bCfg.variant\" /> }\n }\n </div>\n }\n\n <!-- Tag pills (rendered as badge pills, one row per tags column) -->\n @for (tc of _cardCols().tagCols; track tc.key) {\n @let tagItems = item[tc.tagsKey ?? tc.key];\n @if (tagItems?.length) {\n <div class=\"dt-card-tags\">\n @for (tag of tagItems; track tag.name) {\n <ui-badge [label]=\"tag.name\" [variant]=\"tag.variant ?? 'gray'\" size=\"sm\" />\n }\n </div>\n }\n }\n\n <!-- Detail key/value rows -->\n @if (_cardCols().detailCols.length) {\n <div class=\"dt-card-details\">\n @for (dc of _cardCols().detailCols; track dc.key) {\n @let val = cardCellText(dc, item);\n @if (val !== '—') {\n <div class=\"dt-card-detail-row\">\n <span class=\"dt-card-detail-label\">{{ dc.label }}</span>\n <span class=\"dt-card-detail-value\"\n [class.dt-overdue]=\"dc.cellType === 'days-ago' && isCardOverdue(dc, item)\">\n {{ val }}</span>\n </div>\n }\n }\n </div>\n }\n\n </div>\n }\n }\n\n </div>\n }\n\n <!-- ── PAGINATION ─────────────────────────────────────────── -->\n @if (_total() > pageSize()) {\n <ui-pagination\n [page]=\"_page()\"\n [pageSize]=\"pageSize()\"\n [totalItems]=\"_total()\"\n [detached]=\"_isPhone()\"\n (pageChange)=\"onPageChange($event)\" />\n }\n\n <!-- ── BUILT-IN SERVICES (toast, confirm, import/export) ──── -->\n <ui-toast />\n <ui-confirm-outlet />\n\n @if (importExportConfig()) {\n <ui-import-export\n #ieRef\n [config]=\"importExportConfig()!\"\n [importSources]=\"importSources()\"\n [exportParams]=\"_exportParams()\"\n (imported)=\"onImported()\"\n style=\"display:none\" />\n }\n\n <!-- Built-in row-delete confirmation modal -->\n @if (_rowDeleteTarget()) {\n <ui-modal\n data-testid=\"confirm-modal\"\n [open]=\"true\"\n [title]=\"_rowDeleteConfig().title\"\n maxWidth=\"420px\"\n (close)=\"_rowDeleteTarget.set(null)\">\n <p class=\"dt-confirm-msg\" data-testid=\"confirm-message\">{{ _rowDeleteConfig().message }}</p>\n @if (_rowDeleteConfig().details?.length) {\n <ul class=\"dt-confirm-details\" data-testid=\"confirm-details\" aria-label=\"What will happen\">\n @for (d of _rowDeleteConfig().details; track $index) { <li>{{ d }}</li> }\n </ul>\n }\n <ui-button modal-footer-left data-testid=\"confirm-cancel\" variant=\"secondary\" size=\"sm\" [disabled]=\"_rowDeleting()\" (click)=\"_rowDeleteTarget.set(null)\">Cancel</ui-button>\n <ui-button modal-footer-right data-testid=\"confirm-accept\" [variant]=\"_rowDeleteConfig().variant === 'primary' ? 'primary' : _rowDeleteConfig().variant === 'warning' ? 'warning' : 'danger'\" size=\"sm\" [loading]=\"_rowDeleting()\" (click)=\"_confirmRowDelete()\">{{ _rowDeleteConfig().confirmLabel }}</ui-button>\n </ui-modal>\n }\n\n <!-- Tags overflow tooltip — position: fixed so it escapes dt-table-wrap overflow -->\n @if (_tagTooltip()) {\n <div\n class=\"dt-tag-tooltip\"\n role=\"tooltip\"\n [style.left.px]=\"_tagTooltip()!.x\"\n [style.top.px]=\"_tagTooltip()!.y\">\n @for (tag of _tagTooltip()!.tags; track tag.name) {\n <ui-badge [label]=\"tag.name\" [variant]=\"tag.variant ?? 'gray'\" />\n }\n </div>\n }\n </div>\n `,\n styles: [`\n :host { display: flex; flex-direction: column; flex: 1; min-height: 0; }\n\n .dt-host { display: flex; flex-direction: column; height: 100%; min-height: 0; }\n\n /* ── Header ─── */\n .dt-header {\n display: flex; align-items: center; gap: 12px;\n margin-bottom: 16px; flex-wrap: wrap; justify-content: space-between;\n position: relative; z-index: 10;\n }\n .dt-header-left { display: flex; align-items: center; gap: 10px; flex-wrap: wrap; flex: 1; min-width: 0; }\n .dt-header-right { display: flex; align-items: center; gap: 8px; flex-shrink: 0; flex-wrap: wrap; justify-content: flex-end; }\n\n /* ── Shimmer base — GPU-composited via transform so no paint per frame ─── */\n .dt-sk-shimmer {\n position: relative; overflow: hidden; background: #f1f5f9;\n &::after {\n content: ''; position: absolute; inset: 0;\n background: linear-gradient(90deg, transparent 25%, rgba(255,255,255,0.65) 50%, transparent 75%);\n transform: translateX(-100%);\n animation: dt-shimmer 1.4s ease-in-out infinite;\n }\n }\n @keyframes dt-shimmer { to { transform: translateX(200%); } }\n\n /* ── Header skeleton toolbar ─── */\n .dt-sk-toolbar {\n display: flex; align-items: center; justify-content: space-between;\n gap: 12px; margin-bottom: 16px;\n }\n .dt-sk-toolbar-left { display: flex; align-items: center; gap: 10px; flex-wrap: wrap; }\n .dt-sk-toolbar-right { display: flex; align-items: center; gap: 8px; flex-shrink: 0; }\n\n /* Search input: matches padding:8px 10px 8px 34px, border-radius:6px, min-width:380px */\n .dt-sk-search {\n display: flex; align-items: center; gap: 10px;\n width: 380px; height: 36px; padding: 0 12px;\n border: 1px solid #e2e8f0; border-radius: 6px; background: #f8fafc;\n box-sizing: border-box;\n }\n .dt-sk-search-icon { width: 16px; height: 16px; border-radius: 50%; flex-shrink: 0; }\n .dt-sk-search-text { flex: 1; height: 11px; border-radius: 4px; }\n\n /* Quick filter tab strip: matches wrapper border-radius:8px padding:3px, tabs padding:6px 14px border-radius:6px */\n .dt-sk-tab-strip {\n display: flex; gap: 2px; padding: 3px;\n background: #f1f5f9; border-radius: 8px;\n }\n .dt-sk-tab { height: 28px; width: 70px; border-radius: 6px; }\n\n /* Toolbar icon buttons: matches padding:6px 10px border-radius:6px */\n .dt-sk-icon-btn { width: 32px; height: 32px; border-radius: 6px; }\n\n /* ── Table skeleton ─── */\n .dt-skeleton { border: 1px solid #e2e8f0; border-radius: 10px; overflow: hidden; }\n .dt-sk-header {\n display: flex; gap: 24px; padding: 12px 16px;\n background: #f8fafc; border-bottom: 1px solid #e2e8f0;\n }\n .dt-sk-row {\n display: flex; gap: 24px; padding: 13px 16px;\n border-bottom: 1px solid #f1f5f9;\n &:last-child { border-bottom: none; }\n }\n .dt-sk-cell {\n flex: 1; height: 13px; border-radius: 4px;\n position: relative; overflow: hidden; background: #f1f5f9;\n &:first-child { max-width: 100px; }\n &::after {\n content: ''; position: absolute; inset: 0;\n background: linear-gradient(90deg, transparent 25%, rgba(255,255,255,0.65) 50%, transparent 75%);\n transform: translateX(-100%);\n animation: dt-shimmer 1.4s ease-in-out infinite;\n }\n }\n .dt-sk-head { height: 10px; }\n\n /* ── Table wrap — flex:1 so it fills remaining height; both axes scroll ─── */\n .dt-table-wrap {\n flex: 1; min-height: 0; overflow: auto;\n border: 1px solid #e2e8f0; border-radius: 10px 10px 0 0;\n }\n .dt-sk-reload-row td { padding: 10px 14px; border-bottom: 1px solid #f1f5f9; }\n\n table { width: 100%; border-collapse: collapse; font-size: 0.875rem; }\n\n /* ── Head — sticky so column headers stay visible while rows scroll ─── */\n thead tr { background: #f8fafc; border-bottom: 1px solid #e2e8f0; }\n thead { position: sticky; top: 0; z-index: 2; }\n th {\n padding: 10px 14px; font-size: 0.75rem; font-weight: 600;\n color: #64748b; text-transform: uppercase; letter-spacing: 0.04em;\n white-space: nowrap; border-right: 1px solid #e2e8f0; text-align: center;\n background: #f8fafc;\n &:last-child { border-right: none; }\n &.dt-th-sorted { background: #f1f5f9; }\n &.dt-th-actions { width: 48px; }\n }\n .dt-th-inner { display: flex; align-items: center; justify-content: center; gap: 4px; }\n\n /* ── Sort ─── */\n .dt-sort-btn {\n display: inline-flex; align-items: center; gap: 4px;\n background: none; border: none; cursor: pointer; font-size: inherit;\n font-weight: inherit; color: inherit; font-family: inherit; padding: 0;\n text-transform: inherit; letter-spacing: inherit;\n &:hover { color: #1e293b; }\n &:focus-visible { outline: 2px solid #60a5fa; outline-offset: 2px; border-radius: 2px; }\n }\n .dt-sort-icon { transition: transform 0.15s; }\n .dt-sort-desc { transform: rotate(180deg); }\n .dt-sort-inactive { opacity: 0.4; }\n\n /* ── Body ─── */\n tbody tr {\n border-bottom: 1px solid #f1f5f9; background: #fff;\n transition: background 0.1s;\n &:last-child { border-bottom: none; }\n &:hover { background: #f8fafc; }\n }\n .dt-row-clickable { cursor: pointer; }\n .dt-row-clickable:focus-visible { outline: 2px solid #3b82f6; outline-offset: -2px; }\n td {\n padding: 10px 14px; color: #334155; vertical-align: middle;\n white-space: nowrap; border-right: 1px solid #f1f5f9;\n &:last-child { border-right: none; }\n }\n .dt-td-actions { text-align: center; padding: 6px 8px; }\n\n /* ── Cell types ─── */\n .dt-img { display: inline-block; vertical-align: middle; object-fit: cover; border: 1px solid #e2e8f0; background: #f1f5f9; flex-shrink: 0; }\n .dt-img-ph { display: inline-flex; align-items: center; justify-content: center; vertical-align: middle; color: #94a3b8; background: #f1f5f9; border: 1px solid #e2e8f0; flex-shrink: 0; }\n .dt-img-ph svg { width: 55%; height: 55%; }\n .dt-img-circle-md { width: 40px; height: 40px; border-radius: 50%; }\n .dt-img-circle-sm { width: 28px; height: 28px; border-radius: 50%; }\n .dt-img-rounded-md { width: 48px; height: 32px; border-radius: 6px; }\n .dt-img-rounded-sm { width: 36px; height: 24px; border-radius: 5px; }\n .dt-avatar-name { display: flex; align-items: center; gap: 8px; }\n .dt-name-text { font-weight: 500; color: #1e293b; }\n .dt-overdue { color: #dc2626; font-weight: 500; }\n .dt-none { color: #94a3b8; }\n .dt-tags { display: flex; align-items: center; gap: 4px; }\n .dt-tags-more {\n font-size: 0.75rem; font-weight: 600; color: #475569;\n background: #f1f5f9; border-radius: 999px; padding: 2px 7px;\n cursor: default; user-select: none;\n border: 1px solid #e2e8f0;\n &:focus-visible { outline: 2px solid #60a5fa; outline-offset: 2px; border-radius: 999px; }\n }\n\n /* Fixed tooltip — renders outside dt-table-wrap so overflow:auto can't clip it */\n .dt-tag-tooltip {\n position: fixed; z-index: 9999; pointer-events: none;\n background: #1e293b; border-radius: 10px;\n padding: 8px 10px; display: flex; flex-direction: column; gap: 5px;\n box-shadow: 0 4px 6px rgba(0,0,0,0.06), 0 10px 24px rgba(0,0,0,0.14);\n transform: translateX(-50%) translateY(-100%);\n animation: dt-tip-in 120ms ease;\n max-width: 220px;\n }\n @keyframes dt-tip-in { from { opacity: 0; transform: translateX(-50%) translateY(calc(-100% + 4px)); } to { opacity: 1; transform: translateX(-50%) translateY(-100%); } }\n .dt-actions-cell { display: flex; align-items: center; justify-content: center; }\n\n /* ── Bulk bar ─── */\n .dt-bulk-bar {\n display: flex; align-items: center; gap: 10px; flex-wrap: wrap;\n align-self: flex-start;\n padding: 10px 14px; margin-bottom: 8px;\n background: #eff6ff; border: 1px solid #bfdbfe; border-radius: 8px;\n }\n .dt-bulk-count { font-size: 0.875rem; font-weight: 600; color: #1e40af; white-space: nowrap; flex-shrink: 0; }\n .dt-bulk-actions { display: flex; align-items: center; gap: 8px; flex-wrap: wrap; flex: 1; min-width: 0; }\n .dt-bulk-actions ui-select { max-width: 180px; min-width: 120px; }\n .dt-bulk-hint { font-size: 0.8125rem; line-height: 1.35; color: #475569; }\n\n /* ── Card checkbox ─── */\n .dt-card-cb {\n display: flex; align-items: center; padding: 0 0 6px 0;\n }\n\n /* ── Card selected state ─── */\n .dt-card-selected { background: #eff6ff !important; border-color: #bfdbfe !important; }\n\n /* ── Checkbox column ─── */\n .dt-th-cb {\n width: 44px; padding: 0 10px !important; text-align: center !important;\n border-right: 1px solid #e2e8f0; background: #f8fafc;\n }\n .dt-td-cb {\n width: 44px; padding: 0 10px !important; text-align: center !important;\n border-right: 1px solid #f1f5f9;\n }\n\n /* ── Selected row ─── */\n .dt-row-selected { background: #eff6ff !important; }\n .dt-row-selected:hover { background: #dbeafe !important; }\n\n /* ── States ─── */\n .dt-state-cell { padding: 0 !important; border: none !important; background: #fff !important; }\n\n /* ── iPad / tablet optimizations (≤1024px) ─── */\n @media (max-width: 1024px) {\n /* Row height: 38px+ for comfortable touch */\n th { padding: 10px 12px; }\n td { padding: 11px 12px; }\n /* Sort button — larger vertical tap area */\n .dt-sort-btn { padding: 4px 0; }\n /* Actions cell — taller so ⋮ button is easier to tap */\n .dt-td-actions { padding: 6px 6px; }\n /* Bulk bar stretches full width on tablet so wrapped buttons have room */\n .dt-bulk-bar { align-self: stretch; }\n }\n\n /* ── Small tablet / large phone landscape (≤768px) ─── */\n @media (max-width: 768px) {\n th { padding: 9px 10px; font-size: 0.8125rem; }\n td { padding: 10px 10px; font-size: 0.8125rem; }\n .dt-sk-search { width: 100%; }\n }\n\n /* ── Phone (≤640px): card view replaces table ─── */\n @media (max-width: 640px) {\n .dt-header { flex-direction: column; align-items: stretch; }\n .dt-header-left, .dt-header-right { width: 100%; }\n /* Hide table, show cards */\n .dt-table-wrap { display: none; }\n /* Skeleton toolbar adapts */\n .dt-sk-search { width: 100%; }\n .dt-sk-toolbar { flex-direction: column; align-items: stretch; gap: 8px; }\n .dt-sk-toolbar-right { align-self: flex-end; }\n /* Bulk bar: full width, count + clear on one row, buttons fill below */\n .dt-bulk-bar {\n align-self: stretch;\n padding: 10px 12px;\n row-gap: 8px;\n }\n .dt-bulk-count { flex: 1; }\n .dt-bulk-actions { width: 100%; }\n .dt-bulk-hint { flex-basis: 100%; }\n .dt-bulk-actions ui-select { max-width: 100%; flex: 1; }\n }\n\n /* Built-in delete confirm modal message */\n .dt-confirm-msg { margin: 0; font-size: 0.9375rem; color: #334155; line-height: 1.5; overflow-wrap: anywhere; word-break: break-word; white-space: pre-line; }\n .dt-confirm-details { margin: 10px 0 0; padding-left: 20px; font-size: 0.875rem; color: #64748b; line-height: 1.5; overflow-wrap: anywhere; word-break: break-word; }\n .dt-confirm-details li + li { margin-top: 4px; }\n\n /* Pagination stays flush to the table bottom border */\n ui-pagination { flex-shrink: 0; }\n\n /* ── Card list — fills remaining height and scrolls vertically on mobile ─── */\n /* Phone-only sort + filter row above the card list (rendered only when _isPhone()). */\n .dt-phone-controls { display: flex; flex-direction: column; gap: 8px; margin-bottom: 8px; flex-shrink: 0; }\n .dt-phone-sort { display: flex; align-items: center; gap: 8px; }\n .dt-phone-sort-label { font-size: 0.8125rem; font-weight: 600; color: #64748b; flex-shrink: 0; }\n .dt-phone-sort-select {\n flex: 1; min-width: 0; min-height: 44px; box-sizing: border-box; padding: 0 12px;\n font-family: inherit; font-size: 1rem; /* 16px: no iOS focus-zoom */\n color: #1e293b; background: #fff; border: 1px solid #e2e8f0; border-radius: 8px;\n }\n .dt-phone-filters { display: flex; flex-wrap: wrap; gap: 8px; }\n\n .dt-card-list {\n display: none; /* shown only at ≤640px via the media query below */\n flex-direction: column; gap: 8px;\n flex: 1; min-height: 0; overflow-y: auto;\n }\n /* Must come after the base rule above: same specificity, so the later rule wins. */\n @media (max-width: 640px) {\n .dt-card-list { display: flex; padding-bottom: 8px; }\n }\n\n .dt-card {\n background: #fff; border: 1px solid #e2e8f0; border-radius: 10px;\n contain: layout style;\n padding: 14px 16px; display: flex; flex-direction: column; gap: 10px;\n transition: background 0.1s;\n }\n /* contain: layout makes each card its own stacking context, which would trap an open row menu\n beneath the following cards. The menu's dropdown only exists in the DOM while open, so raise\n just that card above its siblings. */\n .dt-card:has(.mm-dropdown) { position: relative; z-index: 1; }\n .dt-card-clickable { cursor: pointer; }\n .dt-card-clickable:active { background: #f8fafc; }\n .dt-card-clickable:focus-visible { outline: 2px solid #3b82f6; outline-offset: 2px; }\n\n /* Card header: avatar/name + action menu */\n .dt-card-header {\n display: flex; align-items: center; gap: 10px;\n }\n .dt-card-title {\n display: flex; align-items: center; gap: 10px;\n flex: 1; min-width: 0;\n }\n .dt-card-name {\n min-width: 0; font-weight: 600; color: #1e293b; font-size: 0.9375rem;\n overflow: hidden; text-overflow: ellipsis; white-space: nowrap;\n }\n .dt-card-name-plain { flex: 1; }\n .dt-card-actions { margin-left: auto; flex-shrink: 0; }\n\n /* Badge pills row */\n .dt-card-badges { display: flex; flex-wrap: wrap; gap: 6px; }\n\n /* Tag pills row (smaller than status badges) */\n .dt-card-tags { display: flex; flex-wrap: wrap; gap: 4px; }\n\n /* Detail key/value rows */\n .dt-card-details { display: flex; flex-direction: column; gap: 5px; }\n .dt-card-detail-row {\n display: flex; justify-content: space-between; align-items: baseline;\n gap: 8px; font-size: 0.8125rem;\n }\n .dt-card-detail-label { color: #64748b; flex-shrink: 0; }\n .dt-card-detail-value { color: #334155; font-weight: 500; text-align: right; min-width: 0; overflow-wrap: anywhere; word-break: break-word; }\n\n /* Card skeleton layout wrappers (ui-skeleton handles shimmer) */\n .dt-card-sk { gap: 12px; }\n .dt-card-sk-header { display: flex; align-items: center; gap: 10px; }\n .dt-card-sk-badges { display: flex; gap: 6px; }\n .dt-card-sk-rows { display: flex; flex-direction: column; gap: 8px; }\n `],\n})\nexport class DataTableComponent<T extends Record<string, any>> implements OnInit {\n /* ── Inputs ─────────────────────────────────────────────────── */\n readonly endpoint = input.required<string>();\n readonly columns = input.required<ColumnDef[]>();\n readonly searchPlaceholder = input('Search...');\n readonly pageSize = input(50);\n readonly tableLabel = input('Data table');\n readonly emptyMessage = input('No records yet.');\n readonly lockedColumn = input('');\n readonly savedViews = input<SavedView[]>([]);\n /**\n * When provided, the table manages saved views automatically — loads from backend on init,\n * saves/deletes via API, and restores state on load. Value is the `page` key used in\n * `GET /api/saved-views?page=<savedViewsPage>`. No host wiring needed.\n */\n readonly page = input('');\n /** Show column config + saved views toolbar. Defaults true. Set false to hide both. */\n readonly showToolbar = input(true);\n /** Optional endpoint that returns Record<string,number> counts per quick-filter value. */\n readonly countsEndpoint = input('');\n /**\n * Optional extra query params owned by the host page (e.g. a period picker or a parent-record id),\n * sent with every row, /counts, /filter-counts and export request. When the value changes the table\n * goes back to page 1 and refreshes rows + counts; search, sort, tab and column filters are kept.\n * Omit it (default null) and nothing changes. Empty / null values are dropped.\n * [params]=\"{ period: period() }\"\n */\n readonly params = input<Record<string, string | null | undefined> | null>(null);\n /** Action buttons rendered in the table header. Primary variant = standalone button; secondary = More Actions dropdown. */\n readonly tableButtons = input<ToolbarAction[]>([]);\n /** Optional endpoint that returns Record<string, Record<string,number>> counts per badge column value. */\n readonly filterCountsEndpoint = input('');\n /** Import/export config. When provided, Import + Export buttons are automatically added to the toolbar. */\n readonly importExportConfig = input<ImportExportConfig | null>(null);\n /** Competitor/source profiles for the import wizard. App-defined — not baked into the library. */\n readonly importSources = input<ImportSource[] | undefined>(undefined);\n /**\n * Page permission. Anything other than 'full' makes the table read-only: no Import,\n * no bulk update/delete and no checkbox column (even when `selectable` is set), no page-defined\n * toolbar buttons at all (only Export; More Actions offers only their existing Saved Views), and the\n * row menu offers only View (`type: 'view'`).\n * Rows with nothing to show get no \"⋮\" trigger; the Actions column is dropped when no row has View.\n */\n readonly permission = input<TablePermission>('full');\n protected readonly _canEdit = computed(() => this.permission() === 'full');\n /** Enable row checkboxes and selection count bar (without bulk actions). */\n readonly selectable = input(false);\n /** Singular item label used in the selection count (e.g. \"contact\" → \"3 contacts selected\"). */\n readonly itemLabel = input('item');\n /** Plural label; defaults to English pluralisation of `itemLabel` (\"property\" → \"properties\"). */\n readonly itemLabelPlural = input<string>('');\n protected _countLabel(count: number): string {\n return count === 1 ? this.itemLabel() : (this.itemLabelPlural() || pluralize(this.itemLabel()));\n }\n /**\n * Column keys to enable bulk updates for, plus the special keyword `'delete'`.\n * e.g. `['status', 'type', 'delete']`\n * The table matches each key to its column definition to get the label and options —\n * no extra config or API calls needed. Setting this automatically enables row checkboxes.\n * Use `{ field, exclude }` to leave values users can't set out of that field's bulk select:\n * `[{ field: 'status', exclude: ['EXPIRED'] }, 'delete']` (see `BulkUpdateOption`).\n * Use `{ field, allowedFrom: { TARGET: [sourceValues] } }` for a status workflow: only targets some selected row can\n * legally move to are offered, and rows that can't move are skipped (never sent).\n */\n readonly bulkUpdateOptions = input<BulkUpdateOption<T>[]>([]);\n\n /* ── Outputs ────────────────────────────────────────────────── */\n readonly rowClick = output<T>();\n /**\n * Optional per-row opt-out for (rowClick): rows where it returns false get no pointer cursor,\n * no tab stop and no click/keyboard activation (e.g. log rows with no linked record).\n */\n readonly rowClickableFn = input<((item: T) => boolean) | null>(null);\n /**\n * Accessible name of a row / card (e.g. `View case for Jane Doe`) so a screen reader announces what activating it does.\n * Return '' (or omit the input) for no `aria-label`.\n */\n readonly rowLabelFn = input<((item: T) => string) | null>(null);\n readonly tableButtonClick = output<string>();\n /** Emitted when a row action with opens === 'panel' is clicked.\n * The host component listens to this and opens the appropriate side panel or form modal. */\n readonly panelActionClick = output<{ type: string; item: T }>();\n readonly saveViewClick = output<void>();\n readonly loadViewClick = output<SavedView>();\n readonly deleteViewClick = output<string>();\n readonly imported = output<void>();\n readonly selectionChange = output<Set<string>>();\n /**\n * Emitted with the active quick-filter tab's key whenever it changes — a tab click, \"Clear filters\" (`''`),\n * or a saved view being applied. `''` means the \"All\" tab. Lets a host page know which tab the user is on\n * (e.g. to preselect it in an Add panel).\n */\n readonly tabChange = output<string>();\n /** Emitted when the user applies a bulk action. The table has already filtered to eligible IDs. */\n readonly bulkAction = output<BulkActionEvent>();\n /** Emitted after a built-in single-row delete completes successfully. */\n readonly rowDeleted = output<T>();\n\n /* ── DI ─────────────────────────────────────────────────────── */\n private readonly config = inject(FORGEROUTE_CONFIG);\n private readonly http = inject(HttpClient);\n private readonly destroyRef = inject(DestroyRef);\n private readonly _confirmSvc = inject(ConfirmService);\n private readonly _datePipe = inject(DatePipe);\n private readonly _toast = inject(ToastService);\n\n /* ── Internal state ─────────────────────────────────────────── */\n protected readonly _items = signal<T[]>([]);\n /** Public read-only signal of current page items — used by host components for bulk selection. */\n readonly items: Signal<T[]> = this._items.asReadonly();\n protected readonly _selectedIds = signal<Set<string>>(new Set());\n protected readonly _total = signal(0);\n protected readonly _loading = signal(true);\n protected readonly _hasLoaded = signal(false);\n /** Image URLs that failed to load — their cells show the placeholder instead. */\n /** True only while a SEARCH-triggered load is in flight (drives the search box spinner). */\n protected readonly _searchLoading = signal(false);\n private _searchPending = false;\n protected readonly _failedImages = signal<Set<string>>(new Set());\n private readonly _numericKeys = signal<Set<string>>(new Set());\n protected readonly _tabCounts = signal<Record<string, number>>({});\n protected readonly _filterCounts = signal<Record<string, Record<string, number>>>({});\n private _tabCountsSub?: Subscription;\n private _filterCountsSub?: Subscription;\n\n /**\n * Values that actually occur in the data (count > 0). Shared by quick-filter tabs\n * and column filters so both follow the same rule: with only one value there is\n * nothing to filter on — tabs are disabled and the column filter icon is hidden.\n */\n private _presentKeys(counts: Record<string, number>): string[] {\n return Object.keys(counts).filter(k => k !== '' && k !== 'all' && (counts[k] ?? 0) > 0);\n }\n\n /** Auto-generates tabs from countsEndpoint keys. */\n protected readonly _effectiveTabs = computed<QuickFilterTab[]>(() => {\n const counts = this._tabCounts();\n const present = this._presentKeys(counts);\n // The selected tab always stays (even at 0 under a search): the user is searching within it.\n const active = this._activeTab();\n const hasCounts = Object.keys(counts).length > 0;\n const keys = hasCounts && active && !present.includes(active) ? [...present, active] : present;\n if (!keys.length) return [];\n const toLabel = (s: string) => this._columnBadgeLabel(s) ?? s\n .replace(/([a-z])([A-Z])/g, '$1 $2')\n .replace(/-/g, ' ')\n .replace(/\\b\\w/g, c => c.toUpperCase());\n const singleValue = keys.length === 1;\n return [\n { value: '', label: 'All' },\n ...keys.map(k => ({ value: k, label: toLabel(k), disabled: singleValue })),\n ];\n });\n\n /**\n * Badge label for a quick-filter tab key, so a tab reads exactly like the column's badges\n * (`signsAndPrint` → \"Signs & Print\", `mlsFees` → \"MLS Fees\"). The key is matched to a value from\n * /filter-counts of a column with a badgeFn, ignoring case, dashes, underscores and spaces.\n * null when nothing matches — the caller falls back to splitting the key.\n */\n private _columnBadgeLabel(key: string): string | null {\n const norm = (v: string) => v.toLowerCase().replace(/[^a-z0-9]/g, '');\n const target = norm(key);\n if (!target) return null;\n const filterCounts = this._filterCounts();\n for (const col of this.columns()) {\n if (!col.badgeFn) continue;\n const value = Object.keys(filterCounts[col.key] ?? {}).find(v => norm(v) === target);\n if (value === undefined) continue;\n const label = col.badgeFn(value, null)?.label;\n if (label) return label;\n }\n return null;\n }\n protected readonly _error = signal<string | null>(null);\n protected readonly _search = signal('');\n protected readonly _page = signal(1);\n protected readonly _sortField = signal<string | null>(null);\n protected readonly _sortDir = signal<SortDir>('asc');\n protected readonly _activeTab = signal('');\n protected readonly _colFilterValues = signal<Record<string, string[]>>({});\n protected readonly _activeViewId = signal<string | null>(null);\n\n /** Internal saved views managed by the table when savedViewsPage is set. */\n private readonly _internalSavedViews = signal<SavedView[]>([]);\n /** Resolved saved views — internal when savedViewsPage is set, host-supplied otherwise. */\n protected readonly _effectiveSavedViews = computed(() =>\n this.page() ? this._internalSavedViews() : this.savedViews(),\n );\n\n /** Empty = show all defaultVisible; non-empty = show exactly these keys */\n protected readonly _visibleColumns = signal<string[]>([]);\n\n protected readonly _ieRef = viewChild<ImportExportComponent>('ieRef');\n\n /** Tag overflow tooltip — fixed position to escape table's overflow:auto */\n protected readonly _tagTooltip = signal<{ tags: any[]; x: number; y: number } | null>(null);\n\n protected readonly visibleCols = computed(() => {\n const vis = this._visibleColumns();\n const cols = this.columns();\n const numericKeys = this._numericKeys();\n const base = vis.length\n ? cols.filter(c => vis.includes(c.key))\n : cols.filter(c => c.defaultVisible !== false);\n\n const dates: ColumnDef[] = [];\n const status: ColumnDef[] = [];\n const regular: ColumnDef[] = [];\n const numeric: ColumnDef[] = [];\n const actions: ColumnDef[] = [];\n for (const col of base) {\n if (col.cellType === 'actions') { actions.push(col); }\n else if (col.cellType === 'date') { dates.push(col); }\n else if (col.key === 'status') { status.push(col); }\n else if (numericKeys.has(col.key)) { numeric.push(col); }\n else { regular.push(col); }\n }\n // Read-only users only get View in the row menu — drop the Actions column when no loaded row offers it.\n const shownActions = this._canEdit()\n ? actions\n : actions.filter(col => this._items().some(item => this._rowActions(col, item).length > 0));\n return [...dates, ...status, ...regular, ...numeric, ...shownActions];\n });\n\n protected readonly _effectiveSearchPlaceholder = computed(() => {\n const searchableCols = this.visibleCols().filter(c => c.searchable);\n if (!searchableCols.length) return this.searchPlaceholder();\n const labels = searchableCols.map(c => c.label.toLowerCase());\n return `Search by ${labels.join(', ')}...`;\n });\n\n /**\n * True when the host binds (rowClick). Signal outputs have no public \"observed\" flag, so read the\n * OutputEmitterRef's subscriber list (a method, not a computed: subscribers aren't a signal).\n * Falls back to true if Angular ever changes that internal shape.\n */\n protected _rowClickable(): boolean {\n const listeners = (this.rowClick as any).listeners;\n return listeners === undefined ? true : (listeners?.length ?? 0) > 0;\n }\n\n protected readonly skeletonCols = computed(() =>\n Array.from({ length: Math.min(this.columns().length || 5, 8) }),\n );\n protected readonly skeletonRows = computed(() =>\n Array.from({ length: Math.min(this.pageSize(), 8) }),\n );\n\n /** Splits visible columns into roles for the mobile card view. */\n protected readonly _cardCols = computed(() => {\n const cols = this.visibleCols();\n // Title: avatar-name column, else the page's locked (primary identifier) column, else the first\n // data column. The locked column matters because column order puts the record date first.\n const locked = this.lockedColumn();\n const titleCol = cols.find(c => c.cellType === 'avatar-name')\n ?? (locked ? cols.find(c => c.key === locked && c.cellType !== 'actions') : undefined)\n ?? cols.find(c => c.cellType !== 'actions' && c.cellType !== 'image') ?? null;\n return {\n titleCol,\n imageCol: cols.find(c => c.cellType === 'image') ?? null,\n badgeCols: cols.filter(c => c.cellType === 'badge'),\n tagCols: cols.filter(c => c.cellType === 'tags'),\n detailCols: cols.filter(c =>\n c.key !== titleCol?.key &&\n c.cellType !== 'avatar-name' &&\n c.cellType !== 'image' &&\n c.cellType !== 'badge' &&\n c.cellType !== 'tags' &&\n c.cellType !== 'actions' &&\n !c.mobileHidden,\n ),\n actionsCol: cols.find(c => c.cellType === 'actions') ?? null,\n };\n });\n\n /**\n * A successful load returned no rows and no search, tab or column filter is active: there is nothing to search,\n * export or configure, so only the primary buttons (Add) stay in the toolbar. Host `params` (a date range, a selected\n * parent) don't count — the host's own controls for them stay on screen — but an active search, tab or column filter\n * keeps the full toolbar so the user can clear it.\n */\n protected readonly _emptyUnfiltered = computed(() =>\n this._hasLoaded() && !this._loading() && !this._error() && this._items().length === 0 &&\n !this._hasActiveFilters(),\n );\n\n /** Buttons handed to the toolbar: all of them, or only the primary ones while the table is empty and unfiltered. */\n protected readonly _toolbarButtons = computed((): ToolbarAction[] =>\n this._emptyUnfiltered() ? this._effectiveButtons().filter(b => b.variant === 'primary' || b.keepWhenEmpty) : this._effectiveButtons(),\n );\n\n /** True while the table is empty and unfiltered but the host kept a More Actions item (`keepWhenEmpty`) — the menu stays. */\n protected readonly _keepMoreActions = computed(() =>\n this._emptyUnfiltered() && this._toolbarButtons().some(b => !b.variant && !b.hidden),\n );\n\n protected readonly _hasActiveFilters = computed(() => {\n const cf = this._colFilterValues();\n return !!this._search() || !!this._activeTab() || Object.values(cf).some(v => v.length > 0);\n });\n\n /** Host `params` with empty values dropped; equal by content so a new object with the same values doesn't reload. */\n protected readonly _extraParams = computed<Record<string, string>>(() => {\n const out: Record<string, string> = {};\n for (const [key, val] of Object.entries(this.params() ?? {})) {\n if (val != null && val !== '') out[key] = String(val);\n }\n return out;\n }, { equal: (a, b) => JSON.stringify(a) === JSON.stringify(b) });\n\n /** Current active filter/search/sort params — passed to the export so it exports the filtered view. */\n /** Visible searchable column keys (comma-joined) — the table and Export only search what's on screen. */\n protected readonly _searchFields = computed(() =>\n this.visibleCols().filter(c => c.searchable).map(c => c.key).join(','));\n\n protected readonly _exportParams = computed<Record<string, string>>(() => {\n const params: Record<string, string> = { ...this._extraParams() };\n if (this._search()) {\n params['search'] = this._search();\n // Same scope as the table's own search, so the CSV never includes rows the table hides.\n if (this._searchFields()) params['searchFields'] = this._searchFields();\n }\n if (this._activeTab()) params['filter'] = this._activeTab();\n if (this._sortField()) { params['sort'] = this._sortField()!; params['direction'] = this._sortDir(); }\n const cf = this._colFilterValues();\n for (const [key, vals] of Object.entries(cf)) { if (vals.length > 0) params[key] = vals.join(','); }\n return params;\n });\n\n /**\n * Toolbar buttons with auto-injected Export (always) and Import (only when\n * importExportConfig.importEndpoint is set) when importExportConfig is provided.\n */\n protected readonly _effectiveButtons = computed((): ToolbarAction[] => {\n const canEdit = this._canEdit();\n // Read-only users get no page-defined toolbar actions at all — only Export (+ their saved views).\n const buttons = canEdit ? this.tableButtons() : [];\n const cfg = this.importExportConfig();\n if (!cfg) return buttons;\n // Remove any manually-defined import/export buttons to avoid duplication\n const filtered = buttons.filter(b => b.type !== 'import' && b.type !== 'export');\n return [\n ...filtered,\n { type: 'export', label: 'Export', ...(cfg.exportInMoreActions ? {} : { variant: 'secondary' as const }),\n icon: 'M4 16v1a3 3 0 0 0 3 3h10a3 3 0 0 0 3-3v-1m-4-8l-4-4m0 0L8 8m4-4v12' },\n // No importEndpoint → export-only table; read-only permission → no Import either\n ...(cfg.importEndpoint && canEdit ? [{ type: 'import', label: 'Import',\n icon: 'M4 16v1a3 3 0 0 0 3 3h10a3 3 0 0 0 3-3v-1m-4-4l-4 4m0 0l-4-4m4 4V4' }] : []),\n ];\n });\n\n /** True when checkboxes should be shown — either explicit selectable or bulk options are defined. */\n /** Row checkboxes — never for read-only users (no bulk column at all), even when `selectable` is set. */\n protected readonly _isSelectable = computed(() => this._canEdit() && (this.selectable() || this._bulkDefs().length > 0));\n\n /**\n * Derives internal action definitions from the `bulkUpdateOptions` string array.\n * 'delete' → built-in danger action with table-managed confirm dialog.\n * Everything else → matched to a column: label = \"Change {col.label}\", options from column config.\n */\n protected readonly _bulkDefs = computed((): BulkActionDef[] =>\n (this._canEdit() ? this.bulkUpdateOptions() : [])\n .map((entry): BulkActionDef => {\n const key = typeof entry === 'string' ? entry : entry.field;\n const confirm = typeof entry === 'string' ? undefined : entry.confirm;\n const deletable = typeof entry === 'string' ? undefined : entry.deletable;\n if (key === 'delete') return { type: 'delete', label: 'Delete', variant: 'danger', ...(confirm ? { confirm } : {}),\n ...(deletable ? { deletable } : {}) };\n const exclude = typeof entry === 'string' ? [] : (entry.exclude ?? []);\n const col = this.columns().find(c => c.key === key);\n const allowedFrom = typeof entry === 'string' ? undefined : entry.allowedFrom;\n return { type: `change-${key}`, label: `Change ${col?.label ?? key}`, variant: 'secondary', field: key,\n ...(exclude.length ? { exclude } : {}), ...(allowedFrom ? { allowedFrom } : {}),\n ...(confirm ? { confirm } : {}) };\n })\n // A field whose exclusions leave nothing to pick gets no \"Change <column>\" button.\n .filter(def => !def.field || !def.exclude?.length || this._bulkOptionsFor(def).length > 0),\n );\n\n /** Options for a field-based bulk action: the field's options minus the entry's `exclude` values. */\n private _bulkOptionsFor(def: BulkActionDef): { value: string; label: string }[] {\n const opts = this._getOptionsForField(def.field!);\n return def.exclude?.length ? opts.filter(o => !this._isBulkExcluded(def, o.value)) : opts;\n }\n\n /** True when a row whose field currently holds `current` may be moved to `target` by this action. */\n private _canBulkMove(def: BulkActionDef, current: unknown, target: string): boolean {\n const cur = String(current ?? '');\n if (cur === String(target)) return false; // already there\n if (!def.allowedFrom) return true; // no workflow rules\n return !!def.allowedFrom[target]?.includes(cur); // no entry = not selectable\n }\n\n /** True when `value` is in the action's `exclude` list (ignoring case, spaces, dashes and underscores). */\n private _isBulkExcluded(def: BulkActionDef, value: string): boolean {\n if (!def.exclude?.length) return false;\n const norm = (v: string) => String(v).toLowerCase().replace(/[^a-z0-9]/g, '');\n const target = norm(value);\n return def.exclude.some(x => norm(x) === target);\n }\n\n /** Which bulk action is currently showing its inline select (null = show action buttons). */\n protected readonly _activeBulkType = signal<string | null>(null);\n /** True while the Apply (field change) HTTP call is in flight. */\n protected readonly _applyingBulk = signal(false);\n /** True while the bulk Delete HTTP call is in flight. */\n protected readonly _deletingBulk = signal(false);\n protected readonly _activeBulkDef = computed(() => {\n const t = this._activeBulkType();\n return t ? (this._bulkDefs().find(d => d.type === t) ?? null) : null;\n });\n\n /** Bound to the inline ui-select via ngModel. Plain property — signal change triggers re-render. */\n protected _bulkSelectStr = '';\n\n /** Options for the active bulk action's inline select, filtered to exclude no-ops. */\n protected readonly _eligibleBulkOptions = computed(() => {\n const def = this._activeBulkDef();\n if (!def?.field) return [];\n const allOpts = this._bulkOptionsFor(def);\n const selected = this._items().filter(i => this._selectedIds().has(i['id']));\n const eligible = getEligibleOptions(allOpts, selected, def.field as keyof T);\n if (!def.allowedFrom) return eligible;\n // Workflow: offer only targets at least one selected row can legally move to.\n return eligible.filter(o => selected.some(i => this._canBulkMove(def, i[def.field!], o.value)));\n });\n\n protected readonly _allSelected = computed(() => {\n const items = this._items();\n const sel = this._selectedIds();\n return items.length > 0 && items.every(item => sel.has(item['id']));\n });\n\n protected readonly _someSelected = computed(() =>\n this._selectedIds().size > 0 && !this._allSelected(),\n );\n\n /**\n * The row's human name for assistive tech: the locked column's value (or the first avatar-name / text column),\n * through the column's own `nameFn` / `render` so it reads exactly like the cell. '' when there is none.\n */\n protected rowName(item: T): string {\n const cols = this.columns();\n const locked = this.lockedColumn();\n const col = (locked ? cols.find(c => c.key === locked) : undefined)\n ?? cols.find(c => c.cellType === 'avatar-name')\n ?? cols.find(c => !c.cellType || c.cellType === 'text');\n if (!col) return '';\n const raw = col.nameFn ? col.nameFn(item) : col.render ? col.render(item) : (item as Record<string, unknown>)[col.key];\n return raw == null ? '' : String(raw).trim();\n }\n\n /** \"Select Acme Electric\" — a row's selection checkbox name. */\n protected selectRowLabel(item: T): string {\n const name = this.rowName(item);\n return name ? `Select ${name}` : 'Select row';\n }\n\n /**\n * \"Row actions for Acme Electric\" — a row's actions-menu trigger name. It still contains the generic \"Row actions\"\n * text, so lookups by that accessible name (substring) keep finding the button, while a screen-reader user now\n * hears which row it belongs to.\n */\n protected rowActionsLabel(item: T): string {\n const name = this.rowName(item);\n return name ? `Row actions for ${name}` : 'Row actions';\n }\n\n protected toggleSelectAll(): void {\n if (!this._isSelectable()) return; // read-only users can never select rows\n const next = this._allSelected()\n ? new Set<string>()\n : new Set(this._items().map(item => item['id'] as string));\n this._selectedIds.set(next);\n this.selectionChange.emit(new Set(next));\n }\n\n protected toggleRow(item: T): void {\n if (!this._isSelectable()) return; // read-only users can never select rows\n const current = new Set(this._selectedIds());\n const id = item['id'] as string;\n if (current.has(id)) { current.delete(id); } else { current.add(id); }\n this._selectedIds.set(current);\n this.selectionChange.emit(new Set(current));\n }\n\n /** Clears the selection — call from the host component after a bulk action completes. */\n clearSelection(): void {\n this._selectedIds.set(new Set());\n this._activeBulkType.set(null);\n this.selectionChange.emit(new Set());\n }\n\n resetColumns(): void {\n this._visibleColumns.set([]);\n }\n\n /**\n * Host-written confirm copy for a bulk action (null = use the default wording). `skippedNote` is the library's standard\n * skipped-rows sentence, appended on a new paragraph unless the copy sets `omitSkippedNote`.\n */\n private _hostConfirm(def: BulkActionDef, ctx: BulkConfirmContext<T>, skippedNote: string,\n fallback: { title: string; confirmLabel: string; variant: 'primary' | 'danger' | 'warning' }) {\n const fn = typeof def.confirm === 'function' ? def.confirm : def.confirm?.[ctx.targetValue];\n if (!fn) return null;\n const out = fn(ctx);\n const copy: BulkConfirmCopy = typeof out === 'string' ? { message: out } : out;\n const note = copy.omitSkippedNote ? '' : skippedNote.trim();\n return {\n title: copy.title ?? fallback.title,\n message: note ? `${copy.message}\\n\\n${note}` : copy.message,\n confirmLabel: copy.confirmLabel ?? fallback.confirmLabel,\n variant: copy.variant ?? fallback.variant,\n ...(copy.details?.length ? { details: copy.details } : {}),\n };\n }\n\n /** Why a bulk action can't be used right now ('' = it can). Only Delete with `deletable` can be blocked. */\n protected _bulkBlockedReason(def: BulkActionDef): string {\n if (def.type !== 'delete' || !def.deletable) return '';\n return this._deleteSplit(def).deletable.length === 0 ? 'None of the selected rows can be deleted.' : '';\n }\n\n /** The distinct reasons of the currently blocked bulk actions, shown as visible text next to the disabled buttons. */\n protected _bulkBlockedHints(): string[] {\n return [...new Set(this._bulkDefs().map(d => this._bulkBlockedReason(d)).filter(r => !!r))];\n }\n\n /** Selected rows (loaded page) split by the delete option's `deletable` predicate; without it every selected row is deletable. */\n private _deleteSplit(def: BulkActionDef): { deletable: T[]; blocked: T[] } {\n const selected = (this._items() as T[]).filter(i => this._selectedIds().has((i as any)['id']));\n if (!def.deletable) return { deletable: selected, blocked: [] };\n return { deletable: selected.filter(r => def.deletable!(r)), blocked: selected.filter(r => !def.deletable!(r)) };\n }\n\n /** Called when the user clicks a bulk action button in the selection bar. */\n protected onBulkActionClick(def: BulkActionDef): void {\n if (def.type === 'delete') {\n const split = this._deleteSplit(def);\n // Nothing the server would accept: no confirm, no request.\n if (def.deletable && split.deletable.length === 0) return;\n const selectedCount = this._selectedIds().size;\n const count = def.deletable ? split.deletable.length : selectedCount;\n const blockedCount = split.blocked.length;\n const skippedNote = blockedCount === 0 ? ''\n : blockedCount === 1 ? \" 1 selected row can't be deleted and will be skipped.\"\n : ` ${blockedCount} selected rows can't be deleted and will be skipped.`;\n const deleteDefault = {\n title: 'Delete Selected',\n message: `Delete ${count} ${this._countLabel(count)}? This cannot be undone.${skippedNote}`,\n confirmLabel: 'Delete',\n variant: 'danger' as const,\n };\n const hostCopy = def.confirm\n ? this._hostConfirm(def, {\n count, skippedAlready: 0, skippedBlocked: blockedCount, selectedCount,\n itemLabel: this.itemLabel(), itemLabelPlural: this.itemLabelPlural() || pluralize(this.itemLabel()),\n columnLabel: 'Delete', targetValue: '', targetLabel: '', rows: split.deletable, skippedRows: split.blocked,\n }, skippedNote, { title: deleteDefault.title, confirmLabel: deleteDefault.confirmLabel, variant: 'danger' })\n : null;\n this._confirmSvc.confirm(hostCopy ?? deleteDefault).then(confirmed => {\n if (!confirmed) return;\n const { eligibleIds, skippedCount } = this._getEligible('delete', undefined, def);\n this._deletingBulk.set(true);\n const url = `${this.config.baseUrl}${this.endpoint()}/bulk/delete`;\n this.http.post<any>(url, { ids: eligibleIds })\n .pipe(takeUntilDestroyed(this.destroyRef))\n .subscribe({\n next: (r: any) => {\n this._deletingBulk.set(false);\n // Rows the backend refused to delete (e.g. not in a deletable status) come back as `skipped`.\n // An empty body (204 / null) means the backend deleted everything it was sent.\n const deleted = r == null ? eligibleIds.length : (r.deleted ?? 0);\n const skipped = skippedCount + (r?.skipped ?? 0);\n // Rows the user isn't permitted to change come back as `notAllowed` — report them, don't hide them.\n const notAllowed = r?.notAllowed ?? 0;\n const generated = skipped > 0 || notAllowed > 0\n ? [`${deleted} deleted`, skipped > 0 ? `${skipped} skipped` : '', notAllowed > 0 ? `${notAllowed} not allowed` : '']\n .filter(Boolean).join(', ') + '.'\n : `${deleted} ${this.itemLabel()}(s) deleted.`;\n // A readable server `message` explains why rows were left out; nothing deleted → shown as an error.\n const serverMsg = humanReadableMessage(r?.message);\n if (serverMsg && deleted === 0) this._toast.error(serverMsg);\n else this._toast.show(serverMsg ? `${generated} ${serverMsg}` : generated);\n this.clearSelection();\n this.refresh();\n this.bulkAction.emit({ type: 'delete', ids: eligibleIds, skippedCount });\n },\n error: (err: any) => {\n this._deletingBulk.set(false);\n this._toast.error(err?.error?.message ?? 'Something went wrong. Please try again.');\n },\n });\n });\n return;\n }\n if (def.field) {\n // Field-based action: show inline select with eligible options pre-selected\n this._activeBulkType.set(def.type);\n const opts = this._eligibleBulkOptions();\n this._bulkSelectStr = opts[0]?.value ?? '';\n }\n }\n\n /** True while the field-change confirmation dialog is open (guards against a second Apply click). */\n private _confirmingBulk = false;\n\n /**\n * Applies the inline-selected value for the active field-based bulk action.\n * Asks the user to confirm first (library confirm dialog, same as bulk Delete); the request is only sent on confirm.\n * Cancel / Escape / backdrop leave the selection and the inline select untouched.\n */\n protected async applyBulkAction(): Promise<void> {\n const def = this._activeBulkDef();\n if (!def || !this._bulkSelectStr || !def.field) return;\n if (this._confirmingBulk || this._applyingBulk()) return;\n // An excluded value (e.g. set programmatically) is never sent.\n if (this._isBulkExcluded(def, this._bulkSelectStr)) return;\n const { eligibleIds, skippedCount } = this._getEligible(def.type, this._bulkSelectStr, def);\n const selectedValue = this._bulkSelectStr;\n const optionLabel = this._bulkOptionsFor(def).find(o => o.value === selectedValue)?.label ?? selectedValue;\n // How many skipped rows already hold the value (the rest can't legally move to it).\n const selectedRows = (this._items() as any[]).filter(i => this._selectedIds().has(i.id));\n const alreadyCount = selectedRows.filter(i => String(i[def.field!] ?? '') === selectedValue).length;\n const blockedCount = skippedCount - alreadyCount;\n // Nothing can change — nothing to send, nothing to confirm.\n if (eligibleIds.length === 0) {\n this._toast.show(blockedCount > 0\n ? `No changes — the selected rows can't be changed to ${optionLabel}.`\n : `No changes — all selected ${this._countLabel(skippedCount)} already have ${optionLabel}.`);\n this._activeBulkType.set(null);\n return;\n }\n const columnLabel = this.columns().find(c => c.key === def.field)?.label ?? def.field;\n // \"already has this value\" only when that is true of every skipped row; otherwise the generic wording.\n const skippedNote = skippedCount === 0 ? ''\n : blockedCount > 0\n ? (skippedCount === 1\n ? ` 1 selected row can't be changed to ${optionLabel} and will be skipped.`\n : ` ${skippedCount} selected rows can't be changed to ${optionLabel} and will be skipped.`)\n : skippedCount === 1\n ? ' 1 selected row already has this value and will be skipped.'\n : ` ${skippedCount} selected rows already have this value and will be skipped.`;\n this._confirmingBulk = true;\n let confirmed = false;\n try {\n const defaultCopy = {\n title: def.label,\n message: `Change ${columnLabel} to ${optionLabel} for ${eligibleIds.length} ${this._countLabel(eligibleIds.length)}?${skippedNote}`,\n confirmLabel: def.label,\n variant: 'primary' as const,\n };\n const eligibleSet = new Set(eligibleIds);\n const hostCopy = def.confirm\n ? this._hostConfirm(def, {\n count: eligibleIds.length, skippedAlready: alreadyCount, skippedBlocked: blockedCount,\n selectedCount: this._selectedIds().size,\n itemLabel: this.itemLabel(), itemLabelPlural: this.itemLabelPlural() || pluralize(this.itemLabel()),\n columnLabel: columnLabel as string, targetValue: selectedValue, targetLabel: optionLabel,\n rows: selectedRows.filter(i => eligibleSet.has(i.id)) as T[],\n skippedRows: selectedRows.filter(i => !eligibleSet.has(i.id)) as T[],\n }, skippedNote, { title: defaultCopy.title, confirmLabel: defaultCopy.confirmLabel, variant: 'primary' })\n : null;\n confirmed = await this._confirmSvc.confirm(hostCopy ?? defaultCopy);\n } finally {\n this._confirmingBulk = false;\n }\n if (!confirmed) return;\n this._applyingBulk.set(true);\n const url = `${this.config.baseUrl}${this.endpoint()}/bulk/${def.field}`;\n this.http.post<any>(url, { ids: eligibleIds, [def.field]: selectedValue })\n .pipe(takeUntilDestroyed(this.destroyRef))\n .subscribe({\n next: (r: any) => {\n this._applyingBulk.set(false);\n this._activeBulkType.set(null);\n // An empty body (204 / null) means the backend updated everything it was sent.\n const count = r == null ? eligibleIds.length : (r.updated ?? r.deleted ?? 0);\n // Rows the table left out plus rows the backend skipped (e.g. changed elsewhere since the page loaded).\n const totalSkipped = skippedCount + (r?.skipped ?? 0);\n // Rows the user isn't permitted to change come back as `notAllowed` — report them, don't hide them.\n const notAllowed = r?.notAllowed ?? 0;\n const msg = totalSkipped > 0 || notAllowed > 0\n ? [`${count} updated`, totalSkipped > 0 ? `${totalSkipped} skipped` : '', notAllowed > 0 ? `${notAllowed} not allowed` : '']\n .filter(Boolean).join(', ') + '.'\n : `${count} ${this.itemLabel()}(s) updated.`;\n const serverMsg = humanReadableMessage(r?.message);\n if (serverMsg && count === 0) this._toast.error(serverMsg);\n else this._toast.show(serverMsg ? `${msg} ${serverMsg}` : msg);\n this.clearSelection();\n this.refresh();\n this.bulkAction.emit({ type: def.type, ids: eligibleIds, value: selectedValue, skippedCount });\n },\n error: (err: any) => {\n this._applyingBulk.set(false);\n this._toast.error(err?.error?.message ?? 'Something went wrong. Please try again.');\n },\n });\n }\n\n /**\n * Returns the eligible IDs and skipped count for a bulk action.\n * - 'delete' — all selected are eligible\n * - 'change-{field}' — skip rows already at targetValue\n * - anything else — all selected are eligible\n */\n private _getEligible(action: string, targetValue?: string, def?: BulkActionDef): { eligibleIds: string[]; skippedCount: number } {\n const selected = (this._items() as any[]).filter(i => this._selectedIds().has(i.id));\n if (action === 'delete') {\n if (def?.deletable) {\n const ok = selected.filter(i => def.deletable!(i));\n return { eligibleIds: ok.map(i => i.id), skippedCount: selected.length - ok.length };\n }\n return { eligibleIds: selected.map(i => i.id), skippedCount: 0 };\n }\n if (action.startsWith('change-') && targetValue !== undefined) {\n const field = action.slice(7);\n const eligible = selected.filter(item => def\n ? this._canBulkMove(def, (item as any)[field], targetValue)\n : String((item as any)[field] ?? '') !== String(targetValue));\n return { eligibleIds: eligible.map(i => i.id), skippedCount: selected.length - eligible.length };\n }\n return { eligibleIds: selected.map(i => i.id), skippedCount: 0 };\n }\n\n /**\n * Derives the available options for a given field from the table's existing config —\n * no extra inputs or API calls needed.\n * Priority: filterCounts → distinct item values.\n */\n private _getOptionsForField(field: string): { value: string; label: string }[] {\n // 0. Explicit bulk options declared on the column (only user-settable values)\n const declared = this.columns().find(c => c.key === field);\n if (declared?.bulkOptions?.length) {\n return declared.bulkOptions.map(value => ({\n value,\n label: declared.badgeFn ? (declared.badgeFn(value, null)?.label || value) : value,\n }));\n }\n\n // 1. Already-fetched filter counts (from /filter-counts endpoint)\n const colCounts = this._filterCounts()[field];\n if (colCounts) {\n const col = this.columns().find(c => c.key === field);\n return Object.keys(colCounts).map(value => ({\n value,\n label: col?.badgeFn ? (col.badgeFn(value, null)?.label || value) : value,\n }));\n }\n\n // 4. Distinct values from current page items\n const col = this.columns().find(c => c.key === field);\n const distinct = [...new Set(this._items().map(i => String(i[field] ?? '')).filter(Boolean))];\n return distinct.map(value => ({\n value,\n label: col?.badgeFn ? (col.badgeFn(value, null)?.label || value) : value,\n }));\n }\n\n /** State for the built-in single-row delete confirmation modal. */\n protected readonly _rowDeleteTarget = signal<T | null>(null);\n protected readonly _rowDeleting = signal(false);\n protected readonly _rowDeleteConfig = signal<{ title: string; message: string; confirmLabel: string; details?: string[]; variant?: 'danger' | 'primary' | 'warning' }>({\n title: 'Delete', message: 'Delete this record? This cannot be undone.', confirmLabel: 'Delete',\n });\n\n protected _confirmRowDelete(): void {\n const item = this._rowDeleteTarget();\n if (!item || this._rowDeleting()) return;\n this._rowDeleting.set(true);\n this.http.post<{ deleted?: number; skipped?: number; message?: string } | null>(\n `${this.config.baseUrl}${this.endpoint()}/bulk/delete`,\n { ids: [item['id']] },\n ).pipe(takeUntilDestroyed(this.destroyRef)).subscribe({\n next: (r) => {\n this._rowDeleting.set(false);\n this._rowDeleteTarget.set(null);\n // The backend refused (not deletable / changed elsewhere) or the row is already gone: `deleted: 0`.\n // A response without a count (e.g. 204) is treated as success.\n if (r?.deleted === 0) {\n this._toast.error(humanReadableMessage(r.message) || `This ${this.itemLabel()} wasn't deleted — it may have changed or already been removed.`);\n this.refresh();\n return;\n }\n this._toast.show(`${this.itemLabel()} deleted`);\n this.refresh();\n this.rowDeleted.emit(item);\n },\n error: (err: any) => {\n this._rowDeleting.set(false);\n this._toast.error(err?.error?.message ?? 'Failed to delete. Please try again.');\n },\n });\n }\n\n private readonly triggerLoad$ = new Subject<void>();\n\n /** Phone viewport (≤640px, same breakpoint as the card-view CSS) — renders the card list instead of the table. */\n protected readonly _isPhone = signal(false);\n\n constructor() {\n // Render ONE tree (table or cards) per viewport so row menus/checkboxes/renderers don't run twice.\n if (typeof window !== 'undefined' && typeof window.matchMedia === 'function') {\n const mq = window.matchMedia('(max-width: 640px)');\n this._isPhone.set(mq.matches);\n const onChange = (e: MediaQueryListEvent) => this._isPhone.set(e.matches);\n mq.addEventListener('change', onChange);\n this.destroyRef.onDestroy(() => mq.removeEventListener('change', onChange));\n }\n\n // Losing edit permission (e.g. switching to read-only) drops any selection — read-only users never\n // have selected rows, and the host is told via selectionChange.\n effect(() => {\n if (this._canEdit()) return;\n untracked(() => { if (this._selectedIds().size > 0 || this._activeBulkType()) this.clearSelection(); });\n });\n\n // Host `params` changed (e.g. a new period) → page 1 + full refresh. The first run only records the\n // initial value — ngOnInit does the initial load.\n let paramsSeen = false;\n effect(() => {\n this._extraParams();\n if (!paramsSeen) { paramsSeen = true; return; }\n untracked(() => this.resetToFirstPage());\n });\n\n // HTTP pipeline\n this.triggerLoad$.pipe(\n switchMap(() => {\n // A silent refresh (background poll) keeps the rows on screen instead of swapping in skeleton rows.\n if (!this._silentNext) this._loading.set(true);\n this._silentNext = false;\n this._searchLoading.set(this._searchPending);\n this._searchPending = false;\n this._error.set(null);\n\n let params = new HttpParams({ fromObject: this._extraParams() })\n .set('page', this._page() - 1)\n .set('size', this.pageSize());\n\n if (this._search()) {\n params = params.set('search', this._search());\n const searchFields = this._searchFields();\n if (searchFields) params = params.set('searchFields', searchFields);\n }\n if (this._sortField()) {\n params = params.set('sort', this._sortField()!).set('direction', this._sortDir());\n }\n if (this._activeTab()) params = params.set('filter', this._activeTab());\n const cf = this._colFilterValues();\n for (const [key, vals] of Object.entries(cf)) {\n if (vals.length > 0) params = params.set(key, vals.join(','));\n }\n\n return this.http.get<any>(`${this.config.baseUrl}${this.endpoint()}`, { params }).pipe(\n catchError(() => {\n this._error.set('Something went wrong. Please try again.');\n return of(null);\n }),\n );\n }),\n takeUntilDestroyed(this.destroyRef),\n ).subscribe(response => {\n if (response !== null) {\n const items: T[] = Array.isArray(response) ? response : (response?.content ?? []);\n this._items.set(items);\n // A completed load gives every image URL a fresh attempt (a recovered image shows again); one that fails\n // again after this render becomes a placeholder again. A failed request keeps the rows, so it never clears.\n if (this._failedImages().size > 0) this._failedImages.set(new Set());\n this._selectedIds.set(new Set());\n this.selectionChange.emit(new Set());\n this._total.set(Array.isArray(response) ? response.length : (response?.totalElements ?? 0));\n if (!this._hasLoaded()) {\n const numericKeys = new Set(\n this.columns().filter(c => this.isNumericCol(c, items)).map(c => c.key)\n );\n this._numericKeys.set(numericKeys);\n this._hasLoaded.set(true);\n }\n }\n this._loading.set(false);\n this._searchLoading.set(false);\n });\n\n }\n\n /** Query params for the counts requests: host params plus the active search (and its scope). Tabs and column filters are never sent. */\n private _countParams(): Record<string, string> {\n const params: Record<string, string> = { ...this._extraParams() };\n if (this._search()) {\n params['search'] = this._search();\n if (this._searchFields()) params['searchFields'] = this._searchFields();\n }\n return params;\n }\n\n /**\n * Fetches `/counts` (tab badges) and `/filter-counts` (column options). The numbers follow the SEARCH only: the\n * requests carry the active `search` + `searchFields`, and are refetched when the search changes. Picking a tab or a\n * column-filter option never changes them. A backend may ignore the params (counts stay tenant-wide). Stale\n * responses are cancelled. As always, only values with a count above 0 become tabs / filter options.\n */\n private _fetchCounts(): void {\n const ep = this.countsEndpoint() || (this.page() ? `/${this.page()}/counts` : '');\n if (ep) {\n const params = this._countParams();\n this._tabCountsSub?.unsubscribe();\n this._tabCountsSub = this.http.get<Record<string, number>>(`${this.config.baseUrl}${ep}`, { params })\n .pipe(takeUntilDestroyed(this.destroyRef))\n .subscribe({\n next: counts => {\n const { all: allCount, ...tabCounts } = counts as Record<string, number> & { all?: number };\n const total = allCount ?? Object.values(tabCounts).reduce((a, b) => a + b, 0);\n this._tabCounts.set({ '': total, ...tabCounts });\n },\n error: () => {},\n });\n }\n const fep = this.filterCountsEndpoint() || (this.page() ? `/${this.page()}/filter-counts` : '');\n if (fep) {\n const params = this._countParams();\n this._filterCountsSub?.unsubscribe();\n this._filterCountsSub = this.http.get<Record<string, Record<string, number>>>(`${this.config.baseUrl}${fep}`, { params })\n .pipe(takeUntilDestroyed(this.destroyRef))\n .subscribe({\n next: counts => this._filterCounts.set(counts),\n error: () => {},\n });\n }\n }\n\n ngOnInit(): void {\n this.triggerLoad$.next();\n if (this.page()) this._loadSavedViews();\n this._fetchCounts();\n }\n\n /* ── Event handlers ─────────────────────────────────────────── */\n protected onSearchChange(val: string) {\n // Already applied (e.g. the search box echoing a reset from clearFilters) — avoid a duplicate load.\n if (val === this._search()) return;\n this._search.set(val);\n this._page.set(1);\n this._searchPending = true;\n this.load();\n this._fetchCounts();\n }\n\n /** Phone sort control: \"Default\" (value '') + one entry per sortable visible column and direction (\"<key>:asc|desc\"). */\n protected readonly _phoneSortOptions = computed(() => {\n const opts = [{ value: '', label: 'Default' }];\n for (const col of this.visibleCols()) {\n if (!this._isSortable(col)) continue;\n opts.push({ value: `${col.key}:asc`, label: `${col.label} ↑` }, { value: `${col.key}:desc`, label: `${col.label} ↓` });\n }\n return opts;\n });\n\n protected readonly _phoneSortValue = computed(() => this._sortField() ? `${this._sortField()}:${this._sortDir()}` : '');\n\n /** Filterable visible columns (same rule as the header funnels). */\n protected readonly _phoneFilterCols = computed(() =>\n this.visibleCols()\n .map(col => ({ col, cf: this.getColFilter(col) }))\n .filter((f): f is { col: ColumnDef; cf: { options: FilterOption[]; activeValues: string[] } } => f.cf !== null));\n\n protected readonly _hasPhoneControls = computed(() => this._phoneSortOptions().length > 1 || this._phoneFilterCols().length > 0);\n\n /** Phone sort select → sets the sort directly (same state, page reset and load as the header's onSort). */\n protected onPhoneSort(value: string) {\n if (!value) {\n this._sortField.set(null); this._sortDir.set('asc');\n } else {\n const i = value.lastIndexOf(':');\n this._sortField.set(value.slice(0, i)); this._sortDir.set(value.slice(i + 1) === 'desc' ? 'desc' : 'asc');\n }\n this._page.set(1);\n this.load();\n }\n\n protected onSort(field: string) {\n if (this._sortField() === field) {\n if (this._sortDir() === 'desc') {\n // Third click: reset\n this._sortField.set(null); this._sortDir.set('asc');\n } else {\n // Second click: asc → desc\n this._sortDir.set('desc');\n }\n } else {\n // First click: start asc\n this._sortField.set(field); this._sortDir.set('asc');\n }\n this._page.set(1);\n this.load();\n }\n\n protected onPageChange(p: number) { this._page.set(p); this.load(); }\n\n protected onTabChange(value: string) {\n this._activeTab.set(value);\n this.tabChange.emit(value);\n this._page.set(1);\n this.load();\n }\n\n protected onColFilterChange(column: string, values: string[]) {\n this._colFilterValues.update(prev => ({ ...prev, [column]: values }));\n this._page.set(1);\n this.load();\n }\n\n protected clearFilters() {\n const hadSearch = !!this._search();\n this._search.set('');\n this._activeTab.set('');\n this.tabChange.emit('');\n this._colFilterValues.set({});\n this._page.set(1);\n this.load();\n if (hadSearch) this._fetchCounts(); // the numbers only follow the search\n }\n\n protected getColFilter(col: ColumnDef): { options: FilterOption[]; activeValues: string[] } | null {\n const colCounts = this._filterCounts()[col.key] ?? {};\n const activeValues = this._colFilterValues()[col.key] ?? [];\n const present = this._presentKeys(colCounts);\n // Same rule as quick-filter tabs: a single value means nothing to filter on.\n // Keep the icon while a filter is active so the user can still clear it.\n if (present.length <= 1 && activeValues.length === 0) return null;\n // A selected value stays listed even when a search leaves it at 0, so it can still be unchecked.\n const keys = [...present, ...activeValues.filter(v => !present.includes(v))];\n\n const countOptions: FilterOption[] = keys\n .map(value => [value, colCounts[value] ?? 0] as const)\n .sort(([, a], [, b]) => b - a)\n .map(([value, count]) => {\n const badgeCfg = (col.badgeFn ? col.badgeFn(value, null) : null)\n ?? { label: value, variant: SEMANTIC_BADGE_MAP[value] ?? autoVariant(value) } as BadgeConfig;\n const color = BADGE_VARIANT_STYLES[badgeCfg.variant]?.color ?? '#64748b';\n return { value, label: badgeCfg.label || value, color, count: count as number };\n });\n\n return countOptions.length > 0 ? { options: countOptions, activeValues } : null;\n }\n\n private _loadSavedViews(): void {\n this.http.get<any[]>(`${this.config.baseUrl}/saved-views`, { params: { page: this.page() } })\n .pipe(takeUntilDestroyed(this.destroyRef))\n .subscribe({\n next: (dtos) => this._internalSavedViews.set(dtos.map(d => ({\n id: d.id, name: d.name,\n quickFilter: d.quickFilter ?? null,\n sortField: d.sortField ?? null,\n sortDir: d.sortDir ?? null,\n visibleColumns: d.visibleColumns ?? [],\n columnFilters: d.columnFilters ?? null,\n }))),\n error: () => {},\n });\n }\n\n protected onSaveViewClick(name: string): void {\n if (!this._canEdit()) return; // read-only users can load their saved views but not create them\n if (this.page()) {\n const state = this.getState();\n this.http.post<any>(`${this.config.baseUrl}/saved-views`, { page: this.page(), name, ...state })\n .pipe(takeUntilDestroyed(this.destroyRef))\n .subscribe({\n next: (dto) => {\n this._internalSavedViews.update(v => [...v, {\n id: dto.id, name: dto.name,\n quickFilter: dto.quickFilter ?? null,\n sortField: dto.sortField ?? null,\n sortDir: dto.sortDir ?? null,\n visibleColumns: dto.visibleColumns ?? [],\n columnFilters: dto.columnFilters ?? null,\n }]);\n this._toast.show('View saved');\n },\n error: () => this._toast.error('Failed to save view'),\n });\n } else {\n this.saveViewClick.emit();\n }\n }\n\n protected onLoadViewClick(view: SavedView): void {\n if (this.page()) {\n this._activeViewId.set(view.id);\n this.applyView(view);\n } else {\n this.loadViewClick.emit(view);\n }\n }\n\n protected onDeleteViewClick(id: string): void {\n if (!this._canEdit()) return; // read-only users can't delete saved views\n if (this.page()) {\n this.http.delete(`${this.config.baseUrl}/saved-views/${id}`)\n .pipe(takeUntilDestroyed(this.destroyRef))\n .subscribe({\n next: () => {\n this._internalSavedViews.update(v => v.filter(v => v.id !== id));\n if (this._activeViewId() === id) this._activeViewId.set(null);\n this._toast.show('View deleted');\n },\n error: () => this._toast.error('Failed to delete view'),\n });\n } else {\n this.deleteViewClick.emit(id);\n }\n }\n\n /** Returns a snapshot of current table state for saving as a view. */\n getState(): Pick<SavedView, 'quickFilter' | 'sortField' | 'sortDir' | 'visibleColumns'> & { columnFilters: Record<string, string> } {\n const columnFilters: Record<string, string> = {};\n for (const [key, vals] of Object.entries(this._colFilterValues())) {\n if (vals.length > 0) columnFilters[key] = vals.join(',');\n }\n return {\n columnFilters,\n quickFilter: this._activeTab() || null,\n sortField: this._sortField() || null,\n sortDir: this._sortField() ? this._sortDir() : null,\n visibleColumns: this._visibleColumns().length ? [...this._visibleColumns()] : [],\n };\n }\n\n /** Restores table state from a saved view and reloads. */\n applyView(view: SavedView): void {\n this._activeTab.set(view.quickFilter ?? '');\n this.tabChange.emit(view.quickFilter ?? '');\n if (view.sortField) {\n this._sortField.set(view.sortField);\n this._sortDir.set((view.sortDir as SortDir) ?? 'asc');\n } else {\n this._sortField.set(null);\n this._sortDir.set('asc');\n }\n this._visibleColumns.set(view.visibleColumns?.length ? view.visibleColumns : []);\n // Views saved before column filters were stored (field absent/null) leave the current filters alone.\n if (view.columnFilters && typeof view.columnFilters === 'object') {\n this._colFilterValues.set(this._restorableColumnFilters(view.columnFilters));\n }\n this.resetToFirstPage();\n }\n\n /** Keeps only filters that still make sense: known column keys, and (when the counts list the column) known values. */\n private _restorableColumnFilters(saved: Record<string, string>): Record<string, string[]> {\n const keys = new Set(this.columns().map(c => c.key));\n const counts = this._filterCounts();\n const out: Record<string, string[]> = {};\n for (const [key, raw] of Object.entries(saved)) {\n if (!keys.has(key) || typeof raw !== 'string') continue;\n const known = Object.keys(counts[key] ?? {});\n let vals = raw.split(',').map(v => v.trim()).filter(Boolean);\n if (known.length > 0) vals = vals.filter(v => known.includes(v));\n if (vals.length > 0) out[key] = vals;\n }\n return out;\n }\n\n /** Plain/number/date columns sort unless `sortable: false`; badge and image columns only when `sortable: true`; never actions. */\n protected _isSortable(col: ColumnDef): boolean {\n if (col.cellType === 'actions') return false;\n return col.cellType === 'badge' || col.cellType === 'image' ? col.sortable === true : col.sortable !== false;\n }\n\n protected sortAriaLabel(field: string): string {\n if (this._sortField() !== field) return 'none';\n return this._sortDir() === 'asc' ? 'ascending' : 'descending';\n }\n\n protected trackItem(item: T, index: number): string | number {\n return item['id'] ?? index;\n }\n\n protected isRowClickable(item: T): boolean {\n if (!this._rowClickable()) return false;\n const fn = this.rowClickableFn();\n return fn ? fn(item) : true;\n }\n\n protected rowLabel(item: T): string | null {\n const fn = this.rowLabelFn();\n return fn ? (fn(item) || null) : null;\n }\n\n protected onRowClick(item: T) {\n if (this.isRowClickable(item)) this.rowClick.emit(item);\n }\n\n /** Enter / Space on a focused clickable row or card activates it — ignored when focus is on a control inside it. */\n protected onRowKeydown(e: KeyboardEvent, item: T) {\n if (e.target !== e.currentTarget || (e.key !== 'Enter' && e.key !== ' ')) return;\n if (!this.isRowClickable(item)) return;\n e.preventDefault();\n this.rowClick.emit(item);\n }\n\n protected onInternalButtonClick(type: string): void {\n if (type === 'import') { this._ieRef()?.openImport(); return; }\n if (type === 'export') { this._ieRef()?.exportAll(); return; }\n this.tableButtonClick.emit(type);\n }\n\n protected onImported(): void {\n this.refresh();\n this.imported.emit();\n }\n\n /** Row actions visible for the current permission: everything for 'full', only View (`type: 'view'`) otherwise. */\n protected _rowActions(col: ColumnDef, item: T): RowAction[] {\n const actions = col.actionsFn?.(item) ?? [];\n if (this._canEdit()) return actions;\n const visible = actions.filter(a => a.type === 'view');\n return visible.length === actions.length ? actions : visible;\n }\n\n protected async onTableRowAction(type: string, col: ColumnDef, item: T): Promise<void> {\n const actions = this._rowActions(col, item);\n const action = actions.find(a => a.type === type);\n // An action hidden by permission (or unknown) must never run — including the built-in delete.\n if (!action) return;\n\n if (action?.opens === 'panel') {\n this.panelActionClick.emit({ type, item });\n return;\n }\n\n // Built-in delete: fires when type === 'delete' and no opens override is set.\n // Opens the table's own confirmation modal (bypasses ConfirmService entirely).\n // Host pages need only define { type: 'delete', label: '...', variant: 'danger' } —\n // no opens, no modal config, no onAction handler required.\n if (type === 'delete' && !action?.opens) {\n const title = action?.modal?.title ?? 'Delete';\n const rawMsg = action?.modal?.message;\n const msg = rawMsg\n ? (typeof rawMsg === 'function' ? rawMsg(item) : rawMsg)\n : `Delete this ${this.itemLabel()}? This cannot be undone.`;\n const confirmLabel = action?.modal?.confirmLabel ?? 'Delete';\n const details = action?.modal?.details;\n const variant = action?.modal?.variant;\n this._rowDeleteConfig.set({ title, message: msg, confirmLabel,\n ...(details?.length ? { details } : {}), ...(variant ? { variant } : {}) });\n this._rowDeleteTarget.set(item);\n return;\n }\n\n if (action?.opens === 'modal' && action.modal) {\n const msg = typeof action.modal.message === 'function'\n ? action.modal.message(item)\n : action.modal.message;\n const confirmed = await this._confirmSvc.confirm({\n title: action.modal.title,\n message: msg,\n confirmLabel: action.modal.confirmLabel,\n cancelLabel: action.modal.cancelLabel,\n variant: action.modal.variant,\n ...(action.modal.details?.length ? { details: action.modal.details } : {}),\n });\n if (!confirmed) return;\n }\n\n col.onAction?.(type, item);\n }\n\n private isNumericCol(col: ColumnDef, items: T[]): boolean {\n // Any explicit cellType (including 'text') opts out of numeric auto-detection — e.g. ZIP codes are text.\n if (col.cellType) return false;\n for (const item of items) {\n const raw = col.render ? col.render(item) : item[col.key];\n if (raw == null || raw === '' || raw === '—') continue;\n if (typeof raw === 'number') return true;\n if (typeof raw === 'string') {\n const t = raw.trim();\n if (t.startsWith('$') || t.endsWith('%')) return true;\n if (isFinite(Number(t))) return true;\n }\n return false;\n }\n return false;\n }\n\n protected resolveCellAlign(col: ColumnDef, item: T): 'left' | 'right' | 'center' {\n if (col.align) return col.align;\n if (col.cellType === 'number') return 'right';\n if (col.cellType === 'actions' || col.cellType === 'image') return 'center';\n if (this.isNumericCol(col, [item])) return 'right';\n return 'left';\n }\n\n /** null → the cell renders a plain '—' (badgeFn returned null, or an empty value without a badgeFn). */\n protected resolveBadge(col: ColumnDef, item: T): BadgeConfig | null {\n if (col.badgeFn) return col.badgeFn(item[col.key], item);\n const value = String(item[col.key] ?? '');\n if (!value) return null;\n return {\n label: value,\n variant: SEMANTIC_BADGE_MAP[value] ?? autoVariant(value),\n };\n }\n\n protected showTagTooltip(event: MouseEvent | FocusEvent, tags: any[]): void {\n const rect = (event.target as HTMLElement).getBoundingClientRect();\n this._tagTooltip.set({ tags, x: rect.left + rect.width / 2, y: rect.top - 8 });\n }\n\n protected hideTagTooltip(): void {\n this._tagTooltip.set(null);\n }\n\n protected imageUrl(col: ColumnDef, item: T): string | null {\n try {\n const url = col.imageFn?.(item);\n if (typeof url !== 'string') return null;\n return url.trim() || null;\n } catch {\n return null;\n }\n }\n\n /** Always a string so the <img> keeps an `alt` attribute ('' = decorative). */\n protected imageAlt(col: ColumnDef, item: T): string {\n try {\n return col.imageAlt?.(item) ?? '';\n } catch {\n return '';\n }\n }\n\n protected imageClass(col: ColumnDef, small: boolean): string {\n const size = small ? 'sm' : (col.imageSize ?? 'md');\n return `dt-img-${col.imageShape ?? 'circle'}-${size}`;\n }\n\n protected onImageError(url: string): void {\n this._failedImages.update(s => new Set(s).add(url));\n }\n\n protected phoneCell(value: any): string { return formatPhone(value); }\n protected getInitials(name: string): string {\n return name.split(' ').filter(Boolean).map(p => p[0]).slice(0, 2).join('').toUpperCase();\n }\n\n protected isCardOverdue(col: ColumnDef, item: T): boolean {\n const days = item[col.daysKey ?? col.key];\n return days != null && days > (col.overdueThreshold ?? 30);\n }\n\n /** Renders any non-badge column value as a plain string for the mobile card view. */\n protected cardCellText(col: ColumnDef, item: T): string {\n switch (col.cellType) {\n case 'date': {\n const v = item[col.key];\n return v ? (this._datePipe.transform(v, col.dateFormat ?? 'MMM d, y') ?? '—') : '—';\n }\n case 'phone': return formatPhone(item[col.key]);\n case 'days-ago': {\n const days = item[col.daysKey ?? col.key];\n return days != null ? `${days}d ago` : 'Never';\n }\n case 'tags': {\n const tags = item[col.tagsKey ?? col.key];\n return tags?.length > 0 ? tags.map((t: any) => t.name).join(', ') : '—';\n }\n case 'image': return '';\n case 'progress': {\n const p = col.progressFn?.(item);\n return p ? (p.label ?? `${p.value}/${p.max}`) : '—';\n }\n default:\n return col.render ? col.render(item) : String(item[col.key] ?? '—');\n }\n }\n\n /** Refresh data rows only — used internally for sort, filter, search, pagination. */\n load() { this.triggerLoad$.next(); }\n\n /**\n * Drops the active quick-filter tab (back to \"All\"), emits `tabChange('')` and reloads from page 1. For hosts\n * whose tab was renamed or removed by the user's own change, so the table doesn't stay on a tab key that no\n * longer exists. No-op when no tab is active.\n */\n resetTab(): void {\n if (!this._activeTab()) return;\n this._activeTab.set('');\n this.tabChange.emit('');\n this.resetToFirstPage();\n }\n\n /** Go to page 1 and do a full refresh — use after adding a new record. */\n resetToFirstPage(): void { this._page.set(1); this.refresh(); }\n\n /**\n * Full refresh after a mutation — fires data rows, tab counts, and filter counts\n * in parallel so every part of the UI (pagination totals, quick-filter badges,\n * column filter options) reflects the change.\n */\n refresh(options?: { silent?: boolean }): void {\n this._silentNext = !!options?.silent && this._hasLoaded();\n this.triggerLoad$.next();\n this._fetchCounts();\n }\n\n /** Set by `refresh({ silent: true })` for the next load only: rows stay visible (no skeleton) while it runs. */\n private _silentNext = false;\n}\n","import { Component, input, output, ChangeDetectionStrategy, inject, ElementRef, effect, OnDestroy } from '@angular/core';\nimport { DOCUMENT } from '@angular/common';\nimport { OverlayStack } from '../utils/overlay-stack';\nimport { focusInitial, tabbables } from '../utils/overlay-focus';\nimport { trackVisualViewport } from '../utils/visual-viewport';\n\n@Component({\n selector: 'ui-side-panel',\n standalone: true,\n changeDetection: ChangeDetectionStrategy.OnPush,\n host: { '(document:keydown)': 'onKeydown($event)' },\n template: `\n @if (open()) {\n <div class=\"sp-backdrop\" (click)=\"closed.emit()\"></div>\n <aside\n class=\"sp-panel\"\n [class.sp-wide]=\"wide()\"\n [class.sp-extra-wide]=\"extraWide()\"\n role=\"dialog\"\n aria-modal=\"true\"\n tabindex=\"-1\"\n [attr.aria-labelledby]=\"titleId\">\n\n <!-- Header -->\n <div class=\"sp-header\">\n <div class=\"sp-header-text\">\n <h2 class=\"sp-title\" [id]=\"titleId\">{{ title() }}</h2>\n @if (subtitle()) { <p class=\"sp-subtitle\">{{ subtitle() }}</p> }\n </div>\n <button class=\"sp-close\" type=\"button\" (click)=\"closed.emit()\" aria-label=\"Dismiss panel\">\n <svg width=\"20\" height=\"20\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\" aria-hidden=\"true\">\n <line x1=\"18\" y1=\"6\" x2=\"6\" y2=\"18\"/><line x1=\"6\" y1=\"6\" x2=\"18\" y2=\"18\"/>\n </svg>\n </button>\n </div>\n\n <!-- Body -->\n <div class=\"sp-body\">\n <ng-content />\n </div>\n\n <!-- Footer: cancel always left, primary always right -->\n @if (cancelLabel() || primaryLabel()) {\n <div class=\"sp-footer\">\n @if (cancelLabel()) {\n <button class=\"sp-btn sp-btn-cancel\" type=\"button\" (click)=\"closed.emit()\">\n <svg width=\"14\" height=\"14\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\" aria-hidden=\"true\"><line x1=\"18\" y1=\"6\" x2=\"6\" y2=\"18\"/><line x1=\"6\" y1=\"6\" x2=\"18\" y2=\"18\"/></svg>\n {{ cancelLabel() }}\n </button>\n }\n @if (primaryLabel()) {\n <button\n class=\"sp-btn\"\n [class.sp-btn-primary]=\"primaryVariant() === 'primary'\"\n [class.sp-btn-danger]=\"primaryVariant() === 'danger'\"\n [class.sp-btn-warning]=\"primaryVariant() === 'warning'\"\n type=\"button\"\n [disabled]=\"primaryDisabled() || primaryLoading()\"\n (click)=\"primaryClick.emit()\">\n @if (primaryLoading()) {\n <span class=\"sp-spinner\" aria-hidden=\"true\"></span>\n } @else if (primaryVariant() === 'danger') {\n <svg width=\"14\" height=\"14\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\" aria-hidden=\"true\"><polyline points=\"3 6 5 6 21 6\"/><path d=\"M19 6l-1 14a2 2 0 0 1-2 2H8a2 2 0 0 1-2-2L5 6\"/><path d=\"M10 11v6M14 11v6\"/><path d=\"M9 6V4a1 1 0 0 1 1-1h4a1 1 0 0 1 1 1v2\"/></svg>\n } @else if (primaryVariant() === 'warning') {\n <svg width=\"14\" height=\"14\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\" aria-hidden=\"true\"><line x1=\"22\" y1=\"2\" x2=\"11\" y2=\"13\"/><polygon points=\"22 2 15 22 11 13 2 9 22 2\"/></svg>\n } @else {\n <svg width=\"14\" height=\"14\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\" aria-hidden=\"true\"><polyline points=\"20 6 9 17 4 12\"/></svg>\n }\n {{ primaryLabel() }}\n </button>\n }\n </div>\n }\n </aside>\n }\n `,\n styles: [`\n .sp-backdrop {\n position: fixed; inset: 0; background: rgba(0,0,0,0.3); z-index: 90;\n animation: sp-fade 200ms ease;\n }\n .sp-panel {\n position: fixed; top: 0; right: 0; bottom: var(--ui-kb, 0px); width: 480px; max-width: 100vw;\n max-height: var(--ui-vvh, 100dvh);\n background: #fff; z-index: 100; box-shadow: -4px 0 20px rgba(0,0,0,0.1);\n display: flex; flex-direction: column; animation: sp-slide 250ms ease;\n }\n .sp-panel:focus { outline: none; }\n .sp-wide { width: 620px; }\n .sp-extra-wide { width: 760px; }\n\n /* ── Header ─── */\n .sp-header {\n display: flex; justify-content: space-between; align-items: center;\n padding: 24px 24px 20px; gap: 12px; flex-shrink: 0;\n background: linear-gradient(135deg, #1e293b 0%, #334155 100%);\n }\n .sp-header-text { flex: 1; min-width: 0; }\n .sp-title { font-size: 1.125rem; font-weight: 700; color: #fff; margin: 0; }\n .sp-subtitle { font-size: 0.8125rem; color: #94a3b8; margin: 4px 0 0; }\n .sp-close {\n display: flex; align-items: center; justify-content: center; flex-shrink: 0;\n width: 36px; height: 36px; border-radius: 8px;\n border: 1px solid rgba(255,255,255,0.2); background: rgba(255,255,255,0.1);\n cursor: pointer; color: #cbd5e1; transition: background 0.15s, border-color 0.15s;\n &:hover { background: rgba(255,255,255,0.2); border-color: rgba(255,255,255,0.3); color: #fff; }\n &:focus-visible { outline: 2px solid #3b82f6; outline-offset: 2px; }\n }\n\n /* ── Body ─── */\n .sp-body { padding: 20px 24px 24px; flex: 1; overflow-y: auto; overflow-x: hidden; }\n\n /* ── Footer ─── */\n .sp-footer {\n display: flex; align-items: center; justify-content: space-between;\n flex-shrink: 0; padding: 14px 24px calc(14px + env(safe-area-inset-bottom, 0px));\n border-top: 1px solid #e2e8f0; background: #f8fafc;\n }\n .sp-btn {\n display: inline-flex; align-items: center; gap: 7px;\n padding: 8px 18px; border-radius: 7px; font-size: 0.875rem;\n font-weight: 500; font-family: inherit; cursor: pointer; white-space: nowrap;\n transition: background 0.1s, border-color 0.1s, opacity 0.1s;\n &:disabled { opacity: 0.5; cursor: default; }\n &:focus-visible { outline: 2px solid #3b82f6; outline-offset: 2px; }\n }\n .sp-btn-cancel {\n background: #fff; border: 1px solid #e2e8f0; color: #475569;\n &:hover:not(:disabled) { background: #f8fafc; border-color: #cbd5e1; }\n }\n .sp-btn-primary {\n background: #3b82f6; border: 1px solid #3b82f6; color: #fff;\n &:hover:not(:disabled) { background: #2563eb; border-color: #2563eb; }\n }\n .sp-btn-danger {\n background: #dc2626; border: 1px solid #dc2626; color: #fff;\n &:hover:not(:disabled) { background: #b91c1c; border-color: #b91c1c; }\n }\n .sp-btn-warning {\n background: #d97706; border: 1px solid #d97706; color: #fff;\n &:hover:not(:disabled) { background: #b45309; border-color: #b45309; }\n }\n .sp-spinner {\n display: inline-block; width: 13px; height: 13px; border-radius: 50%;\n border: 2px solid rgba(255,255,255,0.35); border-top-color: #fff;\n animation: sp-spin 0.55s linear infinite; flex-shrink: 0;\n }\n\n /* ── Responsive ─── */\n @media (max-width: 768px) {\n .sp-panel, .sp-wide, .sp-extra-wide { width: 100vw; }\n }\n @media (max-width: 480px) {\n .sp-panel, .sp-wide, .sp-extra-wide {\n top: auto; bottom: 0; left: 0; right: 0;\n width: 100vw; max-height: min(90dvh, var(--ui-vvh, 100dvh));\n border-radius: 16px 16px 0 0;\n box-shadow: 0 -4px 24px rgba(0,0,0,0.15);\n animation: sp-up 250ms ease;\n }\n .sp-header { padding: 16px 16px 14px; }\n .sp-body { padding: 16px; }\n .sp-footer { padding: 12px 16px calc(12px + env(safe-area-inset-bottom, 0px)); }\n }\n\n @keyframes sp-slide { from { transform: translateX(100%); } to { transform: translateX(0); } }\n @keyframes sp-up { from { transform: translateY(100%); } to { transform: translateY(0); } }\n @keyframes sp-fade { from { opacity: 0; } to { opacity: 1; } }\n @keyframes sp-spin { to { transform: rotate(360deg); } }\n `],\n})\nexport class SidePanelComponent implements OnDestroy {\n readonly open = input(false);\n readonly title = input.required<string>();\n readonly subtitle = input('');\n readonly wide = input(false);\n readonly extraWide = input(false);\n /** Left-aligned cancel button. Default 'Cancel'. Set to '' to hide. */\n readonly cancelLabel = input('Cancel');\n /** Right-aligned primary action button label. Empty string = hidden. */\n readonly primaryLabel = input('');\n readonly primaryVariant = input<'primary' | 'danger' | 'warning'>('primary');\n readonly primaryLoading = input(false);\n readonly primaryDisabled = input(false);\n\n readonly closed = output<void>();\n readonly primaryClick = output<void>();\n\n protected readonly titleId = `sp-title-${Math.random().toString(36).slice(2, 8)}`;\n\n private readonly _el = inject(ElementRef);\n private readonly _doc = inject(DOCUMENT);\n private readonly _overlays = inject(OverlayStack);\n private _prevFocus: HTMLElement | null = null;\n private _scrollLocked = false;\n private _stopViewport: (() => void) | null = null;\n\n constructor() {\n effect(() => {\n if (this.open()) {\n this._overlays.push(this);\n this._prevFocus = this._doc.activeElement as HTMLElement;\n this._doc.body.style.overflow = 'hidden';\n this._scrollLocked = true;\n this._stopViewport?.();\n this._stopViewport = trackVisualViewport(this._doc.defaultView, this._el.nativeElement);\n setTimeout(() => this._focusFirst(), 50);\n } else if (this._scrollLocked) {\n this._overlays.remove(this);\n // Another overlay (e.g. the side panel under a confirm modal) is still open — keep the page locked.\n if (this._overlays.isEmpty) this._doc.body.style.overflow = '';\n this._scrollLocked = false;\n this._stopViewport?.();\n this._stopViewport = null;\n this._prevFocus?.focus();\n this._prevFocus = null;\n }\n });\n }\n\n onKeydown(event: KeyboardEvent) {\n // Only the topmost open overlay handles Escape / Tab (see OverlayStack).\n if (!this.open() || !this._overlays.isTop(this)) return;\n if (event.key === 'Escape') {\n event.preventDefault();\n this.closed.emit();\n return;\n }\n if (event.key === 'Tab') {\n const focusable = this._focusable();\n if (!focusable.length) { event.preventDefault(); return; }\n const first = focusable[0];\n const last = focusable[focusable.length - 1];\n if (event.shiftKey) {\n const active = this._doc.activeElement;\n // Focus on the dialog container itself: Shift+Tab must wrap, not leave the panel.\n if (active === first || active === this._el.nativeElement.querySelector('.sp-panel')) { event.preventDefault(); last.focus(); }\n } else {\n if (this._doc.activeElement === last) { event.preventDefault(); first.focus(); }\n }\n }\n }\n\n /** Initial focus — see `focusInitial` (autofocus target → first usable field → dialog container). */\n private _focusFirst() {\n focusInitial(\n this._el.nativeElement.querySelector('.sp-panel'),\n this._el.nativeElement.querySelector('.sp-body'),\n this._doc,\n );\n }\n\n private _focusable(): HTMLElement[] {\n return tabbables(this._el.nativeElement);\n }\n\n ngOnDestroy() {\n this._stopViewport?.();\n this._stopViewport = null;\n this._overlays.remove(this);\n if (this._scrollLocked && this._overlays.isEmpty) this._doc.body.style.overflow = '';\n }\n}\n","import { Component, ChangeDetectionStrategy, input, output } from '@angular/core';\n\n@Component({\n selector: 'ui-toggle-badge',\n standalone: true,\n changeDetection: ChangeDetectionStrategy.OnPush,\n template: `\n <button class=\"toggle-badge\" [class.active]=\"active()\" (click)=\"toggle.emit()\">\n <ng-content />\n {{ label() }}\n </button>\n `,\n styles: [`\n .toggle-badge {\n display: inline-flex;\n align-items: center;\n gap: 5px;\n padding: 3px 10px;\n border-radius: 999px;\n border: 1px solid #e2e8f0;\n background: #fff;\n font-size: 0.75rem;\n font-weight: 500;\n color: #64748b;\n cursor: pointer;\n font-family: inherit;\n transition: all 150ms ease;\n line-height: 1;\n\n &:hover { background: #f8fafc; border-color: #94a3b8; }\n &.active { background: #1e293b; color: #fff; border-color: #1e293b; }\n &:focus-visible { outline: 2px solid #60a5fa; outline-offset: 2px; }\n }\n `],\n})\nexport class ToggleBadgeComponent {\n readonly label = input('');\n readonly active = input(false);\n readonly toggle = output<void>();\n}\n","import {\n Component, ChangeDetectionStrategy, ElementRef, DestroyRef, inject, input, output, signal, effect, afterNextRender,\n} from '@angular/core';\n\nexport interface PillTab {\n key: string;\n label: string;\n count?: number;\n disabled?: boolean;\n /**\n * Optional help text. When set, an info icon is shown next to the label and the text appears in a tooltip when the\n * tab is hovered or keyboard-focused. It is also the tab's accessible description (`aria-describedby`), not part of\n * its name. The tooltip is not shown in the horizontally scrolling mobile layout (it would be clipped).\n */\n info?: string;\n}\n\n/**\n * Pill-style tabs following the WAI-ARIA tabs pattern: each tab is a real <button role=\"tab\">,\n * only the selected tab is in the Tab order (roving tabindex), and Arrow Left/Right, Home and End\n * move focus + selection between enabled tabs.\n */\n@Component({\n selector: 'ui-pill-tabs',\n standalone: true,\n changeDetection: ChangeDetectionStrategy.OnPush,\n template: `\n <div class=\"pill-wrap\">\n <div class=\"pill-tabs\" role=\"tablist\" [attr.aria-label]=\"ariaLabel()\" (keydown)=\"onKeydown($event)\" (scroll)=\"updateOverflow()\">\n @for (tab of tabs(); track tab.key) {\n <button type=\"button\" class=\"pill-tab\" role=\"tab\"\n [id]=\"name() + '-' + tab.key\"\n [class.active]=\"activeKey() === tab.key\"\n [class.disabled]=\"tab.disabled\"\n [disabled]=\"tab.disabled ?? false\"\n [attr.aria-selected]=\"activeKey() === tab.key\"\n [attr.tabindex]=\"tabIndexFor(tab)\"\n [attr.aria-describedby]=\"tab.info ? name() + '-' + tab.key + '-info' : null\"\n (click)=\"select(tab.key)\">\n <span>{{ tab.label }}</span>\n @if (tab.count !== undefined) {\n <span class=\"pill-count\">{{ tab.count }}</span>\n }\n @if (tab.info) {\n <svg class=\"pill-info-icon\" width=\"14\" height=\"14\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\" stroke-linecap=\"round\" stroke-linejoin=\"round\" aria-hidden=\"true\" data-testid=\"pill-info-icon\">\n <circle cx=\"12\" cy=\"12\" r=\"10\"/><line x1=\"12\" y1=\"16\" x2=\"12\" y2=\"12\"/><line x1=\"12\" y1=\"8\" x2=\"12.01\" y2=\"8\"/>\n </svg>\n <span class=\"pill-info-tip\" role=\"tooltip\" aria-hidden=\"true\" [id]=\"name() + '-' + tab.key + '-info'\" data-testid=\"pill-info-tip\">{{ tab.info }}</span>\n }\n </button>\n }\n </div>\n <span class=\"pill-fade pill-fade--start\" [class.visible]=\"canScrollStart()\" aria-hidden=\"true\" data-testid=\"pill-fade-start\"></span>\n <span class=\"pill-fade pill-fade--end\" [class.visible]=\"canScrollEnd()\" aria-hidden=\"true\" data-testid=\"pill-fade-end\"></span>\n </div>\n `,\n styles: [`\n .pill-wrap { position: relative; }\n /* Scroll hint: edge fades shown only while more pills are scrollable in that direction. Absolutely positioned\n and pointer-events none, so no layout shift and no blocked clicks. Override the colour with\n --ui-pill-tabs-fade when the tabs sit on a non-white background. */\n .pill-fade {\n position: absolute; top: 0; bottom: 0; width: 28px; pointer-events: none;\n opacity: 0; transition: opacity 0.15s;\n }\n .pill-fade.visible { opacity: 1; }\n .pill-fade--start { left: 0; background: linear-gradient(to right, var(--ui-pill-tabs-fade, #fff), transparent); }\n .pill-fade--end { right: 0; background: linear-gradient(to left, var(--ui-pill-tabs-fade, #fff), transparent); }\n .pill-tabs {\n display: flex;\n gap: 8px;\n flex-wrap: wrap;\n }\n @media (max-width: 768px) {\n .pill-tabs {\n flex-wrap: nowrap;\n overflow-x: auto;\n -webkit-overflow-scrolling: touch;\n padding-bottom: 2px;\n scrollbar-width: none;\n &::-webkit-scrollbar { display: none; }\n }\n }\n .pill-tab {\n position: relative;\n display: inline-flex;\n align-items: center;\n gap: 6px;\n padding: 7px 16px;\n border-radius: 999px;\n border: 1px solid #e2e8f0;\n background: #fff;\n font-size: 0.8125rem;\n font-weight: 500;\n color: #64748b;\n cursor: pointer;\n transition: all 0.15s;\n font-family: inherit;\n\n &:hover:not(.disabled):not(.active) { background: #f8fafc; border-color: #94a3b8; }\n &:focus-visible { outline: 2px solid #3b82f6; outline-offset: 2px; }\n\n .pill-count {\n background: #e2e8f0; color: #475569;\n font-size: 0.7rem; font-weight: 600;\n padding: 1px 6px; border-radius: 999px; line-height: 1.4;\n }\n\n &.active {\n background: #1e293b; color: #fff; border-color: #1e293b;\n .pill-count { background: rgba(255,255,255,0.2); color: #fff; }\n &:hover { background: #1e293b; color: #fff; }\n }\n\n &.disabled { opacity: 0.4; cursor: not-allowed; pointer-events: none; }\n\n .pill-info-icon { flex-shrink: 0; opacity: 0.7; }\n &:hover .pill-info-icon, &:focus-visible .pill-info-icon { opacity: 1; }\n\n /* Tooltip with the tab's info text. Hidden until hover / keyboard focus; never intercepts the pointer. */\n .pill-info-tip {\n position: absolute; left: 0; top: calc(100% + 8px); z-index: 20;\n width: max-content; max-width: 280px;\n padding: 8px 12px; border-radius: 8px;\n background: #1e293b; color: #fff;\n font-size: 0.75rem; font-weight: 400; line-height: 1.45; text-align: left; white-space: normal;\n box-shadow: 0 4px 12px rgba(15, 23, 42, 0.25);\n opacity: 0; visibility: hidden; pointer-events: none;\n transition: opacity 0.12s, visibility 0.12s;\n }\n &:hover .pill-info-tip, &:focus-visible .pill-info-tip { opacity: 1; visibility: visible; }\n }\n `],\n})\nexport class PillTabsComponent {\n private readonly host = inject<ElementRef<HTMLElement>>(ElementRef);\n\n readonly tabs = input<PillTab[]>([]);\n readonly activeKey = input('');\n /** Accessible name for the tab list (e.g. 'Reporting period'). Omit to leave it unnamed. */\n readonly ariaLabel = input<string | null>(null);\n readonly name = input('pill-tabs-' + Math.random().toString(36).slice(2));\n readonly tabChange = output<string>();\n\n /** True while pills are hidden past the start / end edge of the scroll area (drives the fade hints). */\n protected readonly canScrollStart = signal(false);\n protected readonly canScrollEnd = signal(false);\n\n constructor() {\n const destroyRef = inject(DestroyRef);\n // Re-measure whenever the tab list changes.\n effect(() => { this.tabs(); queueMicrotask(() => this.updateOverflow()); });\n afterNextRender(() => {\n this.updateOverflow();\n const list = this.host.nativeElement.querySelector('.pill-tabs');\n if (list && typeof ResizeObserver !== 'undefined') {\n const ro = new ResizeObserver(() => this.updateOverflow());\n ro.observe(list);\n destroyRef.onDestroy(() => ro.disconnect());\n }\n });\n }\n\n protected updateOverflow() {\n const el = this.host.nativeElement.querySelector<HTMLElement>('.pill-tabs');\n if (!el) return;\n const max = el.scrollWidth - el.clientWidth;\n const pos = Math.abs(el.scrollLeft); // abs: RTL scrollLeft is negative\n this.canScrollStart.set(max > 1 && pos > 1);\n this.canScrollEnd.set(max > 1 && pos < max - 1);\n }\n\n select(key: string) {\n if (key !== this.activeKey()) this.tabChange.emit(key);\n }\n\n /** Roving tabindex — the selected tab (or the first enabled one when none is selected) is tabbable. */\n protected tabIndexFor(tab: PillTab): number {\n const tabs = this.tabs();\n const focusKey = tabs.some(t => t.key === this.activeKey() && !t.disabled)\n ? this.activeKey()\n : tabs.find(t => !t.disabled)?.key;\n return tab.key === focusKey ? 0 : -1;\n }\n\n protected onKeydown(e: KeyboardEvent) {\n const enabled = this.tabs().filter(t => !t.disabled);\n if (!enabled.length || !['ArrowRight', 'ArrowLeft', 'Home', 'End'].includes(e.key)) return;\n const current = Math.max(0, enabled.findIndex(t => t.key === this.activeKey()));\n const next = e.key === 'Home' ? 0\n : e.key === 'End' ? enabled.length - 1\n : (current + (e.key === 'ArrowRight' ? 1 : -1) + enabled.length) % enabled.length;\n e.preventDefault();\n const key = enabled[next].key;\n this.select(key);\n this.host.nativeElement.querySelector<HTMLElement>(`#${CSS.escape(this.name() + '-' + key)}`)?.focus();\n }\n}\n","import { Component, inject, signal, computed, ChangeDetectionStrategy, input, output, effect, DestroyRef, ElementRef, Injector, afterNextRender } from '@angular/core';\nimport { HttpClient } from '@angular/common/http';\nimport { FORGEROUTE_CONFIG } from '../config/forgeroute.config';\nimport { Subject, debounceTime, switchMap, of, catchError } from 'rxjs';\nimport { takeUntilDestroyed } from '@angular/core/rxjs-interop';\nimport { FormsModule } from '@angular/forms';\nimport { CheckboxComponent } from '../checkbox/checkbox.component';\nimport { UiFieldValidity, createFieldReveal, requiredIssue } from '../utils/field-validation';\n\nexport interface ContactLookup {\n id: string;\n name: string;\n phone: string | null;\n email: string | null;\n smsOptedIn: boolean;\n}\n\nlet nextPickerId = 0;\n\nfunction formatPhone(raw: string | null | undefined): string {\n if (!raw) return '';\n const normalized = raw.startsWith('+1') && raw.replace(/\\D/g, '').length === 11 ? raw.slice(2) : raw;\n if (normalized.startsWith('+')) return normalized;\n const digits = normalized.replace(/\\D/g, '');\n const d = digits.startsWith('1') && digits.length === 11 ? digits.slice(1) : digits.slice(0, 10);\n if (d.length <= 3) return d;\n if (d.length <= 6) return `(${d.slice(0, 3)}) ${d.slice(3)}`;\n return `(${d.slice(0, 3)}) ${d.slice(3, 6)}-${d.slice(6)}`;\n}\n\n@Component({\n selector: 'ui-contact-picker',\n standalone: true,\n changeDetection: ChangeDetectionStrategy.OnPush,\n imports: [FormsModule, CheckboxComponent],\n providers: [{\n // `invalid` is already a public input of this picker (host flag), so the token is backed by a small adapter\n // exposing the combined state (`fieldInvalid` = host `[invalid]` OR required-and-empty).\n provide: UiFieldValidity,\n useFactory: () => {\n const c = inject(ContactPickerComponent);\n return { invalid: c.fieldInvalid, errorMessage: c.errorMessage } satisfies UiFieldValidity;\n },\n }],\n template: `\n <div class=\"picker-wrapper\">\n @if (label()) {\n <label class=\"picker-label\" [id]=\"labelId\" [attr.for]=\"inputFocusable() ? inputId : null\">{{ label() }}@if (required()) {<span class=\"picker-req\"> *</span>}</label>\n }\n\n <!-- ── Multi-select mode ── -->\n @if (isMultiSelect()) {\n @if (showChips() && selectedMultiple().length > 0) {\n <div class=\"multi-chips\">\n @for (c of selectedMultiple(); track c.id) {\n <span class=\"multi-chip\">\n {{ c.name }}\n <button type=\"button\" class=\"chip-remove\" (mousedown)=\"onChipMouseDown(c)\" (click)=\"onChipClick(c)\" [attr.aria-label]=\"'Remove ' + c.name\">×</button>\n </span>\n }\n </div>\n }\n <input\n class=\"picker-input\"\n type=\"text\"\n [id]=\"inputId\"\n [placeholder]=\"placeholder()\"\n [attr.aria-describedby]=\"describedBy()\"\n [attr.aria-invalid]=\"ariaInvalid() ? 'true' : null\"\n [attr.aria-required]=\"required() || null\"\n [attr.aria-label]=\"label() ? null : fallbackName()\"\n role=\"combobox\"\n aria-autocomplete=\"list\"\n aria-haspopup=\"listbox\"\n [attr.aria-expanded]=\"showResults()\"\n [attr.aria-controls]=\"showResults() ? listboxId : null\"\n [attr.aria-activedescendant]=\"activeDescendant()\"\n [(ngModel)]=\"query\"\n autocomplete=\"off\"\n (input)=\"onInput()\"\n (focus)=\"onFocus()\"\n (blur)=\"onBlur()\"\n (keydown)=\"onKeydown($event)\"\n />\n @if (showResults()) {\n <div class=\"picker-dropdown\" role=\"listbox\" [id]=\"listboxId\" aria-multiselectable=\"true\"\n [attr.aria-labelledby]=\"label() ? labelId : null\" [attr.aria-label]=\"label() ? null : fallbackName()\"\n (mousedown)=\"$event.preventDefault()\">\n @if (loading() || (pending() && results().length === 0)) {\n <div class=\"picker-loading\">Searching...</div>\n } @else if (results().length === 0 && query.length >= 2) {\n <div class=\"picker-empty\">No contacts found</div>\n } @else {\n @for (c of options(); track c.id; let i = $index) {\n <div class=\"picker-option picker-option--check\" role=\"option\"\n [id]=\"optionId(i)\" [class.picker-option--active]=\"i === activeIndex()\"\n [attr.aria-selected]=\"i === activeIndex()\"\n (mousedown)=\"onOptionMouseDown(); toggleContact(c)\"\n (click)=\"onOptionClick($event, c)\">\n <!-- Presentational: the listbox is the only interaction surface (no nested tab stop). -->\n <span class=\"option-check\" aria-hidden=\"true\" inert>\n <ui-checkbox [checked]=\"isSelected(c)\" />\n </span>\n <span class=\"option-body\">\n <span class=\"option-name\">{{ c.name }}</span>\n @if (c.email || c.phone) {\n <span class=\"option-detail\">{{ c.email || c.phone }}</span>\n }\n </span>\n </div>\n }\n }\n </div>\n }\n\n <!-- ── Single-select mode ── -->\n } @else {\n @if (selected()) {\n <div class=\"selected-input\" role=\"group\" tabindex=\"-1\" [attr.aria-labelledby]=\"label() ? labelId : null\"\n [attr.aria-describedby]=\"describedBy()\" [attr.aria-invalid]=\"ariaInvalid() ? 'true' : null\">\n <span>{{ selected()!.name }}</span>\n <button type=\"button\" class=\"clear-btn\" (click)=\"clear()\" aria-label=\"Clear selection\">×</button>\n </div>\n @if (showDetails()) {\n <div class=\"recipient-details\">\n <span class=\"details-label\">RECIPIENT DETAILS</span>\n <div class=\"details-card\">\n <div class=\"detail-row\">\n <span class=\"detail-key\">Name</span>\n <span class=\"detail-val\">{{ selected()!.name }}</span>\n </div>\n <div class=\"detail-row\">\n <span class=\"detail-key\">Email</span>\n <span class=\"detail-val\">{{ selected()!.email || '—' }}</span>\n </div>\n <div class=\"detail-row last\">\n <span class=\"detail-key\">Phone</span>\n <span class=\"detail-val\">{{ formatPhone(selected()!.phone) || '—' }}</span>\n </div>\n </div>\n </div>\n }\n } @else {\n <input\n class=\"picker-input\"\n type=\"text\"\n [id]=\"inputId\"\n [placeholder]=\"placeholder()\"\n [attr.aria-describedby]=\"describedBy()\"\n [attr.aria-invalid]=\"ariaInvalid() ? 'true' : null\"\n [attr.aria-required]=\"required() || null\"\n [attr.aria-label]=\"label() ? null : fallbackName()\"\n role=\"combobox\"\n aria-autocomplete=\"list\"\n aria-haspopup=\"listbox\"\n [attr.aria-expanded]=\"showResults()\"\n [attr.aria-controls]=\"showResults() ? listboxId : null\"\n [attr.aria-activedescendant]=\"activeDescendant()\"\n [(ngModel)]=\"query\"\n autocomplete=\"off\"\n (input)=\"onInput()\"\n (focus)=\"onFocus()\"\n (blur)=\"onBlur()\"\n (keydown)=\"onKeydown($event)\"\n />\n @if (showResults()) {\n <div class=\"picker-dropdown\" role=\"listbox\" [id]=\"listboxId\"\n [attr.aria-labelledby]=\"label() ? labelId : null\" [attr.aria-label]=\"label() ? null : fallbackName()\"\n (mousedown)=\"$event.preventDefault()\">\n @if (loading() || (pending() && results().length === 0)) {\n <div class=\"picker-loading\">Searching...</div>\n } @else if (results().length === 0 && query.length >= 2) {\n <div class=\"picker-empty\">No contacts found</div>\n } @else {\n @for (c of results(); track c.id; let i = $index) {\n <button type=\"button\" class=\"picker-option\" role=\"option\" tabindex=\"-1\"\n [id]=\"optionId(i)\" [class.picker-option--active]=\"i === activeIndex()\"\n [attr.aria-selected]=\"i === activeIndex()\"\n (mousedown)=\"onOptionMouseDown(); selectContact(c)\"\n (click)=\"onOptionClick($event, c)\">\n <span class=\"option-name\">{{ c.name }}</span>\n <span class=\"option-detail\">{{ c.email || c.phone || '' }}</span>\n </button>\n }\n }\n </div>\n }\n }\n }\n\n <!-- Announces the result count to screen readers while the list is open. -->\n <span class=\"picker-sr\" role=\"status\" aria-live=\"polite\" aria-atomic=\"true\">{{ liveText() }}</span>\n\n @if (hint() && showHint()) {\n <p class=\"picker-hint\" [id]=\"hintId\">{{ hint() }}</p>\n }\n </div>\n `,\n styles: [`\n .picker-wrapper { position: relative; overflow: visible; }\n .picker-label { display: block; font-size: 0.8125rem; font-weight: 600; color: #374151; margin-bottom: 6px; }\n .picker-req { color: #ef4444; }\n .picker-hint { margin: 6px 0 0; font-size: 0.75rem; line-height: 1.4; color: #64748b; }\n .picker-input {\n width: 100%; padding: 9px 12px; border: 1px solid #e2e8f0; border-radius: 8px;\n font-size: 0.875rem; color: #1e293b; font-family: inherit; outline: none; box-sizing: border-box;\n &::placeholder { color: #94a3b8; }\n &:focus { border-color: #3b82f6; box-shadow: 0 0 0 3px rgba(59,130,246,0.12); }\n }\n .picker-input:focus-visible { outline: 2px solid transparent; }\n /* 16px on phones stops iOS Safari zooming the page when the field is focused (matches ui-text-input). */\n @media (max-width: 480px) { .picker-input { font-size: 1rem; } }\n .picker-sr {\n position: absolute; width: 1px; height: 1px; margin: -1px; padding: 0;\n overflow: hidden; clip: rect(0, 0, 0, 0); white-space: nowrap; border: 0;\n }\n .picker-dropdown {\n position: absolute; top: calc(100% + 4px); left: 0; right: 0; z-index: 9999;\n background: #fff; border: 1px solid #e2e8f0; border-radius: 8px;\n box-shadow: 0 6px 16px rgba(0,0,0,0.1); max-height: 240px; overflow-y: auto;\n }\n /* Touch: 44px rows / hit areas and a list that never outgrows a phone screen. Desktop (fine pointer, wide) is unchanged. */\n @media (pointer: coarse), (max-width: 480px) {\n .picker-input { min-height: 44px; }\n .picker-dropdown { max-height: min(240px, 40vh); overscroll-behavior: contain; }\n .picker-option { min-height: 44px; justify-content: center; box-sizing: border-box; }\n .picker-option--check { min-height: 44px; }\n .chip-remove { position: relative; }\n .chip-remove::after { content: ''; position: absolute; inset: -13px; }\n }\n .picker-option {\n display: flex; flex-direction: column; gap: 2px; width: 100%;\n padding: 10px 12px; border: none; background: none; text-align: left;\n cursor: pointer; font-family: inherit;\n &:hover, &.picker-option--active { background: #f8fafc; }\n &:first-child { border-radius: 8px 8px 0 0; }\n &:last-child { border-radius: 0 0 8px 8px; }\n }\n /* Multi-select rows (checkbox + name). Written flat: \"&--check\" is not valid CSS nesting and was silently dropped. */\n .picker-option--check {\n flex-direction: row; align-items: center; gap: 10px;\n .option-body { display: flex; flex-direction: column; gap: 1px; min-width: 0; flex: 1; }\n .option-name { white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }\n .option-detail { white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }\n }\n .option-check { display: flex; pointer-events: none; }\n .option-name { font-size: 0.875rem; font-weight: 600; color: #1e293b; overflow-wrap: anywhere; }\n .option-detail { font-size: 0.75rem; color: #94a3b8; overflow-wrap: anywhere; }\n .picker-loading, .picker-empty { padding: 12px; font-size: 0.8125rem; color: #94a3b8; text-align: center; }\n\n .selected-input {\n display: flex; align-items: center; justify-content: space-between;\n padding: 9px 12px; border: 1px solid #e2e8f0; border-radius: 8px; background: #f8fafc;\n font-size: 0.875rem; font-weight: 600; color: #1e293b; gap: 8px; overflow-wrap: anywhere; min-width: 0;\n }\n .selected-input:focus { outline: none; }\n .selected-input:focus-visible { outline: 2px solid #3b82f6; outline-offset: 2px; }\n .clear-btn {\n width: 18px; height: 18px; border: none; background: #e2e8f0; border-radius: 50%;\n cursor: pointer; font-size: 0.75rem; color: #64748b; display: flex; align-items: center; justify-content: center;\n position: relative; flex-shrink: 0;\n &:focus-visible { outline: 2px solid #3b82f6; outline-offset: 2px; }\n &:hover { background: #cbd5e1; color: #1e293b; }\n /* Invisible 44x44 touch target around the 18px glyph (no visual change). */\n &::after { content: ''; position: absolute; inset: -13px; }\n }\n\n .recipient-details { margin-top: 16px; }\n .details-label {\n display: block; font-size: 0.6875rem; font-weight: 600; color: #94a3b8;\n text-transform: uppercase; letter-spacing: 0.05em; margin-bottom: 8px;\n }\n .details-card { border: 1px solid #e2e8f0; border-radius: 8px; overflow: hidden; }\n .detail-row {\n display: flex; justify-content: space-between; align-items: center;\n padding: 10px 14px; border-bottom: 1px solid #f1f5f9;\n &.last { border-bottom: none; }\n &:nth-child(even) { background: #f8fafc; }\n }\n .detail-key { font-size: 0.8125rem; color: #94a3b8; font-weight: 500; }\n .detail-val { font-size: 0.875rem; color: #1e293b; font-weight: 600; }\n\n .multi-chips { display: flex; flex-wrap: wrap; gap: 6px; margin-bottom: 8px; }\n .multi-chip {\n display: inline-flex; align-items: center; gap: 4px;\n padding: 4px 8px 4px 10px; background: #dbeafe; border-radius: 20px;\n font-size: 0.8125rem; font-weight: 500; color: #1e40af;\n }\n .chip-remove {\n width: 18px; height: 18px; border: none; background: rgba(30,64,175,0.15);\n border-radius: 50%; cursor: pointer; font-size: 0.875rem; line-height: 1;\n color: #1e40af; display: flex; align-items: center; justify-content: center; flex-shrink: 0;\n &:hover { background: rgba(30,64,175,0.3); }\n &:focus-visible { outline: 2px solid #1e40af; outline-offset: 2px; }\n }\n /* Forced colors (Windows high contrast) drops box-shadow: the focus ring and the active row become real outlines. */\n @media (forced-colors: active) {\n .picker-input:focus { outline: 2px solid Highlight; outline-offset: 1px; }\n .picker-option--active { outline: 2px solid Highlight; outline-offset: -2px; }\n }\n `],\n})\nexport class ContactPickerComponent {\n readonly formatPhone = formatPhone;\n\n readonly label = input('');\n readonly placeholder = input('Search by name...');\n /** Helper text under the picker (linked to the search input via aria-describedby). */\n readonly hint = input('');\n readonly required = input(false);\n /** Id of an external error element (e.g. `<ui-error>` under the picker); added to aria-describedby. */\n readonly errorId = input('');\n /** Marks the combobox / selected group `aria-invalid=\"true\"`. The picker renders no error text itself. */\n readonly invalid = input(false);\n /** Reveal the built-in \"<label> is required.\" message (`visibleErrorMessage`) now, e.g. after a failed Save click. */\n readonly showErrors = input(false);\n readonly showDetails = input(true);\n readonly isMultiSelect = input(false);\n readonly showChips = input(true);\n readonly initialContact = input<ContactLookup | null>(null);\n\n readonly contactSelected = output<ContactLookup | null>();\n readonly contactsSelected = output<ContactLookup[]>();\n /** Emits whenever `fieldInvalid` flips. */\n readonly invalidChange = output<boolean>();\n\n private readonly http = inject(HttpClient);\n /** API root from FORGEROUTE_CONFIG (e.g. a cross-origin prod API); falls back to same-origin `/api`. */\n private readonly apiBase = inject(FORGEROUTE_CONFIG, { optional: true })?.baseUrl ?? '/api';\n private readonly destroyRef = inject(DestroyRef);\n private readonly search$ = new Subject<string>();\n\n /** Unique ids tie the visible label to the search input (and to the selected-contact group). */\n private readonly uid = `ui-contact-picker-${nextPickerId++}`;\n readonly inputId = `${this.uid}-input`;\n readonly labelId = `${this.uid}-label`;\n readonly hintId = `${this.uid}-hint`;\n readonly listboxId = `${this.uid}-listbox`;\n\n query = '';\n readonly results = signal<ContactLookup[]>([]);\n readonly loading = signal(false);\n readonly selected = signal<ContactLookup | null>(null);\n readonly selectedMultiple = signal<ContactLookup[]>([]);\n readonly showResults = signal(false);\n /** True from the first keystroke of a >=2-char query until its results arrive (covers the debounce). */\n readonly pending = signal(false);\n /** Index of the keyboard-highlighted option in `options()`; -1 = none (aria-activedescendant unset). */\n readonly activeIndex = signal(-1);\n\n private readonly reveal = createFieldReveal(this.showErrors);\n /** True when `required` and nothing is picked (single: no contact; multi: no contacts). */\n private readonly requiredEmpty = computed(() =>\n this.required() && (this.isMultiSelect() ? this.selectedMultiple().length === 0 : !this.selected()));\n /** Validity for `UiFieldValidity`: the host `[invalid]` flag OR required-and-empty. */\n readonly fieldInvalid = computed(() => this.invalid() || this.requiredEmpty());\n /** \"<label> is required.\" while required-and-empty ('' otherwise), shown or not. */\n readonly errorMessage = computed(() => this.requiredEmpty() ? requiredIssue(this.label()).message : '');\n /** `errorMessage` only after the input was blurred or `showErrors` is set — render it in a `<ui-error>` under the picker. */\n readonly visibleErrorMessage = computed(() => this.reveal.revealed() ? this.errorMessage() : '');\n /** aria-invalid: the host flag, or required-and-empty while the host opted in with `[showErrors]` (so apps that only pass `required` render exactly as before). */\n readonly ariaInvalid = computed(() => this.invalid() || (this.showErrors() && this.requiredEmpty()));\n\n /** The hint is search guidance: hidden while a contact is selected (single-select), back after clear. */\n readonly showHint = computed(() => this.isMultiSelect() || !this.selected());\n\n /** Space-joined ids of the hint (when rendered) and the external error element; null when neither. */\n readonly describedBy = computed(() => {\n const ids = [this.hint() && this.showHint() ? this.hintId : '', this.errorId()].filter(Boolean);\n return ids.length ? ids.join(' ') : null;\n });\n\n /** The options currently offered: single-select shows every result, multi-select hides already-picked ones. */\n readonly options = computed(() =>\n this.isMultiSelect() ? this.results().filter(c => !this.isSelected(c)) : this.results());\n\n /** The search input only exists while nothing is selected (single mode) — the label's `for` must not dangle. */\n readonly inputFocusable = computed(() => this.isMultiSelect() || !this.selected());\n\n readonly activeDescendant = computed(() => {\n const i = this.activeIndex();\n return this.showResults() && i >= 0 && i < this.options().length ? this.optionId(i) : null;\n });\n\n readonly liveText = computed(() => {\n // While a lookup is pending/in flight only \"Searching\" is announced — never \"No results\" (or the previous\n // query's count) during the 250ms debounce.\n if (!this.showResults()) return '';\n if (this.loading() || this.pending()) return 'Searching';\n const n = this.options().length;\n if (n === 0) return this.query.length >= 2 ? 'No results' : '';\n return n === 1 ? '1 result' : `${n} results`;\n });\n\n private blurTimer: ReturnType<typeof setTimeout> | null = null;\n /** Timestamp of the last mousedown activation — a click right after it is the same gesture, not a second pick. */\n private lastMouseActivation = 0;\n private readonly host = inject<ElementRef<HTMLElement>>(ElementRef);\n private readonly injector = inject(Injector);\n\n /** Accessible name of the search input and listbox when the picker has no visible label. */\n readonly fallbackName = computed(() => this.placeholder() || 'Search contacts');\n\n optionId(i: number): string { return `${this.uid}-opt-${i}`; }\n\n constructor() {\n this.destroyRef.onDestroy(() => this.clearBlurTimer());\n effect(() => { this.invalidChange.emit(this.fieldInvalid()); });\n effect(() => { this.selected.set(this.initialContact()); });\n\n this.search$.pipe(\n debounceTime(250),\n switchMap(q => {\n if (q.length < 2) { this.results.set([]); this.loading.set(false); this.pending.set(false); return of([]); }\n this.loading.set(true);\n return this.http.get<ContactLookup[]>(`${this.apiBase}/contacts/lookup`, { params: { search: q } }).pipe(\n catchError(() => of([])),\n );\n }),\n takeUntilDestroyed(this.destroyRef),\n ).subscribe(results => { this.results.set(results); this.activeIndex.set(-1); this.loading.set(false); this.pending.set(false); });\n }\n\n onInput() {\n this.clearBlurTimer();\n this.activeIndex.set(-1);\n this.pending.set(this.query.length >= 2);\n this.search$.next(this.query);\n this.showResults.set(this.query.length >= 2);\n }\n\n onFocus() {\n this.clearBlurTimer();\n if (this.query.length >= 2) this.showResults.set(true);\n }\n\n /** Delayed so a click on an option still lands; cancelled if focus returns (or the user types) first. */\n onBlur() {\n this.reveal.touched.set(true);\n this.clearBlurTimer();\n this.blurTimer = setTimeout(() => {\n this.blurTimer = null;\n this.showResults.set(false);\n this.activeIndex.set(-1);\n }, 200);\n }\n\n private clearBlurTimer() {\n if (this.blurTimer !== null) { clearTimeout(this.blurTimer); this.blurTimer = null; }\n }\n\n /** ARIA combobox keyboard support: Up/Down (wrapping), Home/End, Enter to pick, Escape to close. */\n onKeydown(event: KeyboardEvent) {\n this.clearBlurTimer();\n const n = this.options().length;\n const open = this.showResults();\n switch (event.key) {\n case 'ArrowDown':\n case 'ArrowUp': {\n if (!open) {\n if (this.query.length < 2) return;\n this.showResults.set(true);\n event.preventDefault();\n return;\n }\n event.preventDefault();\n if (n === 0) return;\n const cur = this.activeIndex();\n const next = event.key === 'ArrowDown'\n ? (cur + 1) % n\n : (cur <= 0 ? n - 1 : cur - 1);\n this.activeIndex.set(next);\n this.scrollActiveIntoView(next);\n return;\n }\n case 'Home':\n case 'End':\n if (!open || n === 0) return;\n event.preventDefault();\n this.activeIndex.set(event.key === 'Home' ? 0 : n - 1);\n this.scrollActiveIntoView(this.activeIndex());\n return;\n case 'Enter': {\n const i = this.activeIndex();\n if (!open || i < 0 || i >= n) return;\n event.preventDefault();\n const c = this.options()[i];\n if (this.isMultiSelect()) this.toggleContact(c); else this.selectContact(c);\n return;\n }\n case 'Escape':\n if (!open) return;\n // Close the list only — don't let the same Escape also close a hosting panel/modal.\n event.preventDefault();\n event.stopPropagation();\n this.showResults.set(false);\n this.activeIndex.set(-1);\n return;\n }\n }\n\n private scrollActiveIntoView(i: number) {\n (this.host.nativeElement as HTMLElement).querySelector<HTMLElement>(`#${this.optionId(i)}`)?.scrollIntoView?.({ block: 'nearest' });\n }\n\n onOptionMouseDown() { this.lastMouseActivation = Date.now(); }\n\n /**\n * Activation by assistive tech / virtual cursor arrives as a click without a preceding mousedown.\n * A real mouse click follows its mousedown (already handled) — ignore that one.\n */\n onOptionClick(event: MouseEvent, c: ContactLookup) {\n if (Date.now() - this.lastMouseActivation < 500) return;\n if (this.isMultiSelect()) this.toggleContact(c); else this.selectContact(c);\n }\n\n selectContact(c: ContactLookup) {\n // The search input is replaced by the selected-contact group: keep a keyboard / screen-reader user's place by\n // moving focus there (only when focus was in this picker — never when a host sets `initialContact`).\n const hadFocus = this.host.nativeElement.contains(document.activeElement);\n this.selected.set(c);\n this.query = '';\n this.pending.set(false);\n this.showResults.set(false);\n this.activeIndex.set(-1);\n this.contactSelected.emit(c);\n if (hadFocus) this.focusAfterRender('.selected-input');\n }\n\n clear() {\n this.selected.set(null);\n this.query = '';\n this.results.set([]);\n this.pending.set(false);\n this.showResults.set(false);\n this.contactSelected.emit(null);\n // The clear button disappears with the group: return focus to the search input.\n this.focusAfterRender('.picker-input');\n }\n\n private focusAfterRender(selector: string) {\n afterNextRender(() => this.host.nativeElement.querySelector<HTMLElement>(selector)?.focus(), { injector: this.injector });\n }\n\n isSelected(c: ContactLookup): boolean {\n return this.selectedMultiple().some(s => s.id === c.id);\n }\n\n toggleContact(c: ContactLookup) {\n const current = this.selectedMultiple();\n const next = this.isSelected(c)\n ? current.filter(s => s.id !== c.id)\n : [...current, c];\n this.selectedMultiple.set(next);\n // The offered options re-index (picked contacts are filtered out): drop the highlight so a repeated\n // Enter can never pick a contact the user did not just focus.\n this.activeIndex.set(-1);\n this.contactsSelected.emit(next);\n }\n\n removeContact(c: ContactLookup) {\n this.selectedMultiple.set(this.selectedMultiple().filter(s => s.id !== c.id));\n this.activeIndex.set(-1);\n this.contactsSelected.emit(this.selectedMultiple());\n }\n\n /** Chip remove: a mouse press removes at once (as before); the click that follows it is the same gesture. */\n onChipMouseDown(c: ContactLookup) { this.lastMouseActivation = Date.now(); this.removeContact(c); }\n\n /** Keyboard (Enter / Space) and assistive-tech activation arrive as a click with no mousedown. */\n onChipClick(c: ContactLookup) {\n if (Date.now() - this.lastMouseActivation < 500) return;\n this.removeContact(c);\n // The focused chip button is gone: keep focus in the picker.\n this.focusAfterRender('.picker-input');\n }\n}\n","import { Component, ChangeDetectionStrategy, input } from '@angular/core';\n\n/**\n * Inline field error for controls that have no `error` input of their own — e.g. an app-specific\n * picker. Renders exactly like the library inputs' own field error (small red text under the field,\n * announced via role=\"alert\") and renders nothing while `message` is empty, so it can stay in the\n * template unconditionally.\n *\n * <app-job-picker ... [attr.aria-describedby]=\"jobError() ? 'job-error' : null\" />\n * <ui-error errorId=\"job-error\" [message]=\"jobError()\" />\n */\n@Component({\n selector: 'ui-error',\n standalone: true,\n changeDetection: ChangeDetectionStrategy.OnPush,\n template: `\n @if (message()) {\n <p class=\"ui-error\" role=\"alert\" [attr.id]=\"errorId() || null\">{{ message() }}</p>\n }\n `,\n styles: [`\n :host { display: block; }\n .ui-error { margin: 6px 0 0; font-size: 0.75rem; line-height: 1.4; color: #ef4444; }\n `],\n})\nexport class ErrorComponent {\n /** The error text. Empty → nothing is rendered. */\n readonly message = input('');\n /** Optional id for the error element, so the field can point `aria-describedby` at it. */\n readonly errorId = input('');\n}\n","import {\n Component, inject, ChangeDetectionStrategy, signal, computed, effect, forwardRef, input, output, DestroyRef, ElementRef,\n Injector, afterNextRender,\n} from '@angular/core';\nimport { FormsModule } from '@angular/forms';\nimport { HttpClient } from '@angular/common/http';\nimport { takeUntilDestroyed } from '@angular/core/rxjs-interop';\nimport { FORGEROUTE_CONFIG } from '../config/forgeroute.config';\nimport { ErrorComponent } from '../error/error.component';\nimport { UiFieldValidity, createFieldReveal, requiredIssue } from '../utils/field-validation';\nimport { Subject, debounceTime, switchMap, of, catchError } from 'rxjs';\n\nlet nextVideoPickerId = 0;\n\nexport interface VideoMessage {\n id: string;\n title: string;\n category: string;\n shareLink: string | null;\n}\n\n@Component({\n selector: 'ui-video-picker',\n standalone: true,\n changeDetection: ChangeDetectionStrategy.OnPush,\n imports: [FormsModule, ErrorComponent],\n providers: [{ provide: UiFieldValidity, useExisting: forwardRef(() => VideoPickerComponent) }],\n template: `\n <div class=\"picker-wrapper\">\n @if (label()) {\n <label class=\"picker-label\" [id]=\"labelId\" [attr.for]=\"selected() ? null : inputId\">{{ label() }}@if (required()) {<span class=\"picker-required\" aria-hidden=\"true\">*</span>}</label>\n }\n @if (selected()) {\n <div class=\"selected-input\" role=\"group\" tabindex=\"-1\"\n [attr.aria-labelledby]=\"label() ? labelId : null\" [attr.aria-label]=\"label() ? null : 'Selected video'\">\n <div class=\"selected-video\">\n <svg width=\"12\" height=\"12\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\" aria-hidden=\"true\"><polygon points=\"23 7 16 12 23 17 23 7\"/><rect x=\"1\" y=\"5\" width=\"15\" height=\"14\" rx=\"2\" ry=\"2\"/></svg>\n <span>{{ selected()!.title }}</span>\n </div>\n <button type=\"button\" class=\"clear-btn\" (click)=\"clear()\" aria-label=\"Remove video\">×</button>\n </div>\n } @else {\n <input\n class=\"picker-input\"\n type=\"text\"\n role=\"combobox\"\n aria-autocomplete=\"list\"\n aria-haspopup=\"listbox\"\n placeholder=\"Search videos...\"\n [id]=\"inputId\"\n [attr.aria-label]=\"label() ? null : 'Search videos'\"\n [attr.aria-required]=\"required() || null\"\n [attr.aria-invalid]=\"visibleError() ? 'true' : null\"\n [attr.aria-describedby]=\"visibleError() && !showResults() ? errorId : null\"\n [attr.aria-expanded]=\"showResults()\"\n [attr.aria-controls]=\"showResults() ? listboxId : null\"\n [attr.aria-activedescendant]=\"activeDescendant()\"\n [(ngModel)]=\"query\"\n autocomplete=\"off\"\n (input)=\"onInput()\"\n (focus)=\"onFocus()\"\n (blur)=\"onBlur()\"\n (keydown)=\"onKeydown($event)\"\n />\n @if (showResults()) {\n <div class=\"picker-dropdown\" role=\"listbox\" [id]=\"listboxId\"\n [attr.aria-labelledby]=\"label() ? labelId : null\" [attr.aria-label]=\"label() ? null : 'Videos'\"\n (mousedown)=\"$event.preventDefault()\">\n @if (loading() || (pending() && results().length === 0)) {\n <div class=\"picker-loading\">Searching...</div>\n } @else if (results().length === 0 && query.length >= 2) {\n <div class=\"picker-empty\">No videos found</div>\n } @else {\n @for (v of results(); track v.id; let i = $index) {\n <button type=\"button\" class=\"picker-option\" role=\"option\" tabindex=\"-1\"\n [id]=\"optionId(i)\" [class.picker-option--active]=\"i === activeIndex()\"\n [attr.aria-selected]=\"i === activeIndex()\"\n (mousedown)=\"onOptionMouseDown(); select(v)\"\n (click)=\"onOptionClick(v)\">\n <span class=\"option-name\">{{ v.title }}</span>\n <span class=\"option-detail\">{{ v.category }}</span>\n </button>\n }\n }\n </div>\n }\n }\n\n <!-- Announces searching / the result count / no results to screen readers while the list is open. -->\n <span class=\"picker-sr\" role=\"status\" aria-live=\"polite\" aria-atomic=\"true\">{{ liveText() }}</span>\n\n <!-- Hidden while the list is open so the floating list always sits right under the field. -->\n <ui-error [errorId]=\"errorId\" [message]=\"showResults() ? '' : visibleError()\" />\n </div>\n `,\n styles: [`\n .picker-wrapper { position: relative; overflow: visible; }\n .picker-label { display: block; font-size: 0.8125rem; font-weight: 600; color: #374151; margin-bottom: 6px; }\n .picker-required { color: #ef4444; margin-left: 2px; }\n .picker-input {\n width: 100%; padding: 9px 12px; border: 1px solid #e2e8f0; border-radius: 8px;\n font-size: 0.875rem; color: #1e293b; font-family: inherit; outline: none; box-sizing: border-box;\n &::placeholder { color: #94a3b8; }\n &:focus { border-color: #60a5fa; box-shadow: 0 0 0 3px rgba(96,165,250,0.1); }\n &:focus-visible { outline: 2px solid transparent; }\n }\n .picker-sr {\n position: absolute; width: 1px; height: 1px; margin: -1px; padding: 0;\n overflow: hidden; clip: rect(0, 0, 0, 0); white-space: nowrap; border: 0;\n }\n .picker-dropdown {\n position: absolute; top: calc(100% + 4px); left: 0; right: 0; z-index: 9999;\n background: #fff; border: 1px solid #e2e8f0; border-radius: 8px;\n box-shadow: 0 6px 16px rgba(0,0,0,0.1); max-height: 240px; overflow-y: auto;\n }\n .picker-option {\n display: flex; flex-direction: column; gap: 2px; width: 100%;\n padding: 10px 12px; border: none; background: none; text-align: left;\n cursor: pointer; font-family: inherit; box-sizing: border-box;\n &:hover, &.picker-option--active { background: #f8fafc; }\n &:first-child { border-radius: 8px 8px 0 0; }\n &:last-child { border-radius: 0 0 8px 8px; }\n }\n .option-name { font-size: 0.875rem; font-weight: 600; color: #1e293b; overflow-wrap: anywhere; }\n .option-detail { font-size: 0.75rem; color: #94a3b8; text-transform: capitalize; overflow-wrap: anywhere; }\n .picker-loading, .picker-empty { padding: 12px; font-size: 0.8125rem; color: #94a3b8; text-align: center; }\n .selected-input {\n display: flex; align-items: center; justify-content: space-between; gap: 8px;\n padding: 9px 12px; border: 1px solid #e2e8f0; border-radius: 8px; background: #f8fafc;\n &:focus { outline: none; }\n &:focus-visible { outline: 2px solid #3b82f6; outline-offset: 2px; }\n }\n .selected-video {\n display: flex; align-items: center; gap: 7px;\n font-size: 0.875rem; font-weight: 600; color: #1e293b; min-width: 0;\n svg { flex-shrink: 0; color: #64748b; }\n span { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }\n }\n .clear-btn {\n width: 18px; height: 18px; border: none; background: #e2e8f0; border-radius: 50%;\n cursor: pointer; font-size: 0.75rem; color: #64748b; display: flex; align-items: center;\n justify-content: center; flex-shrink: 0; position: relative;\n &:hover { background: #cbd5e1; color: #1e293b; }\n &:focus-visible { outline: 2px solid #3b82f6; outline-offset: 2px; }\n /* Invisible 44x44 touch target around the 18px glyph (no visual change). */\n &::after { content: ''; position: absolute; inset: -13px; }\n }\n\n /* Forced colors (Windows high contrast) drops box-shadow: the focus ring and the active row become real outlines. */\n @media (forced-colors: active) {\n .picker-input:focus { outline: 2px solid Highlight; outline-offset: 1px; }\n .picker-option--active { outline: 2px solid Highlight; outline-offset: -2px; }\n }\n\n /* Touch: 44px field and rows, and a list that never outgrows a phone screen; 16px text on phones so iOS does not zoom. */\n @media (pointer: coarse), (max-width: 480px) {\n .picker-input { min-height: 44px; }\n .picker-option { min-height: 44px; justify-content: center; }\n .picker-dropdown { max-height: min(240px, 40vh); overscroll-behavior: contain; }\n }\n @media (max-width: 480px) { .picker-input { font-size: 1rem; } }\n `],\n})\nexport class VideoPickerComponent implements UiFieldValidity {\n readonly videoSelected = output<VideoMessage>();\n\n /** Optional visible label (names the search field and the selected-video group). Without it the field is named \"Search videos\". */\n readonly label = input('');\n /** Shows the red required marker; the picker is then invalid until a video is picked. */\n readonly required = input(false);\n /** Reveal the built-in \"<label> is required.\" message now (e.g. after a failed Save click); otherwise it shows after the field was blurred. */\n readonly showErrors = input(false);\n /** Emits whenever `invalid` flips. */\n readonly invalidChange = output<boolean>();\n\n private readonly http = inject(HttpClient);\n private readonly destroyRef = inject(DestroyRef);\n private readonly host = inject<ElementRef<HTMLElement>>(ElementRef);\n private readonly injector = inject(Injector);\n\n /** API root from FORGEROUTE_CONFIG (e.g. a cross-origin prod API); falls back to same-origin `/api`. */\n private readonly apiBase = inject(FORGEROUTE_CONFIG, { optional: true })?.baseUrl ?? '/api';\n\n private readonly uid = `ui-video-picker-${nextVideoPickerId++}`;\n readonly inputId = `${this.uid}-input`;\n readonly labelId = `${this.uid}-label`;\n readonly listboxId = `${this.uid}-listbox`;\n readonly errorId = `${this.uid}-error`;\n optionId(i: number): string { return `${this.uid}-opt-${i}`; }\n\n query = '';\n readonly selected = signal<VideoMessage | null>(null);\n readonly results = signal<VideoMessage[]>([]);\n readonly loading = signal(false);\n readonly showResults = signal(false);\n /** True from the first keystroke of a >=2-char query until its results arrive (covers the debounce). */\n readonly pending = signal(false);\n /** Index of the keyboard-highlighted option; -1 = none (aria-activedescendant unset). */\n readonly activeIndex = signal(-1);\n\n private readonly reveal = createFieldReveal(this.showErrors);\n /** `UiFieldValidity`: true while `required` and no video is picked. */\n readonly invalid = computed(() => this.required() && !this.selected());\n /** \"<label> is required.\" while invalid ('' otherwise), shown or not. */\n readonly errorMessage = computed(() => this.invalid() ? requiredIssue(this.label() || 'Video').message : '');\n /** `errorMessage` once the field was blurred or `showErrors` is set. */\n readonly visibleError = computed(() => this.reveal.revealed() ? this.errorMessage() : '');\n\n readonly activeDescendant = computed(() => {\n const i = this.activeIndex();\n return this.showResults() && i >= 0 && i < this.results().length ? this.optionId(i) : null;\n });\n\n /** Polite announcement for screen readers: searching, the result count, or no results (only while the list is open). */\n readonly liveText = computed(() => {\n if (!this.showResults()) return '';\n if (this.loading() || this.pending()) return 'Searching';\n const n = this.results().length;\n return n === 0 ? 'No results' : n === 1 ? '1 result' : `${n} results`;\n });\n\n private readonly search$ = new Subject<string>();\n private blurTimer: ReturnType<typeof setTimeout> | null = null;\n private lastMouseActivation = 0;\n\n constructor() {\n effect(() => { this.invalidChange.emit(this.invalid()); });\n this.destroyRef.onDestroy(() => this.clearBlurTimer());\n this.search$.pipe(\n debounceTime(250),\n switchMap(q => {\n if (q.length < 2) { this.results.set([]); this.loading.set(false); this.pending.set(false); return of(null); }\n this.loading.set(true);\n return this.http.get<any>(`${this.apiBase}/video-messages`, { params: { search: q, page: '0', size: '20' } }).pipe(\n catchError(() => of(null)),\n );\n }),\n takeUntilDestroyed(this.destroyRef),\n ).subscribe(res => { this.results.set(res?.content ?? []); this.activeIndex.set(-1); this.loading.set(false); this.pending.set(false); });\n }\n\n onInput() {\n this.clearBlurTimer();\n this.activeIndex.set(-1);\n this.pending.set(this.query.length >= 2);\n this.search$.next(this.query);\n this.showResults.set(this.query.length >= 2);\n }\n\n onFocus() { this.clearBlurTimer(); if (this.query.length >= 2) this.showResults.set(true); }\n\n /** Delayed so a click on an option still lands; cancelled if focus returns (or the user types) first. */\n onBlur() {\n this.reveal.touched.set(true);\n this.clearBlurTimer();\n this.blurTimer = setTimeout(() => { this.blurTimer = null; this.showResults.set(false); this.activeIndex.set(-1); }, 200);\n }\n\n private clearBlurTimer() {\n if (this.blurTimer !== null) { clearTimeout(this.blurTimer); this.blurTimer = null; }\n }\n\n /**\n * ARIA combobox keys: ArrowDown/ArrowUp open the list and move the highlight (wrapping), Home/End jump to the first /\n * last option, Enter picks the highlighted option, Escape closes the list only (so a surrounding side panel / modal\n * stays open; with the list closed it passes through). Tab selects nothing.\n */\n onKeydown(e: KeyboardEvent) {\n this.clearBlurTimer();\n const n = this.results().length;\n const open = this.showResults();\n switch (e.key) {\n case 'ArrowDown':\n case 'ArrowUp': {\n if (!open) {\n if (this.query.length < 2) return;\n this.showResults.set(true);\n e.preventDefault();\n return;\n }\n e.preventDefault();\n if (n === 0) return;\n const cur = this.activeIndex();\n this.setActive(e.key === 'ArrowDown' ? (cur + 1) % n : (cur <= 0 ? n - 1 : cur - 1));\n return;\n }\n case 'Home':\n case 'End':\n if (!open || n === 0) return;\n e.preventDefault();\n this.setActive(e.key === 'Home' ? 0 : n - 1);\n return;\n case 'Enter': {\n const i = this.activeIndex();\n if (!open || i < 0 || i >= n) return;\n e.preventDefault();\n this.select(this.results()[i]);\n return;\n }\n case 'Escape':\n if (!open) return;\n e.preventDefault();\n e.stopPropagation();\n this.showResults.set(false);\n this.activeIndex.set(-1);\n return;\n }\n }\n\n private setActive(i: number) {\n this.activeIndex.set(i);\n this.host.nativeElement.querySelector<HTMLElement>(`#${this.optionId(i)}`)?.scrollIntoView?.({ block: 'nearest' });\n }\n\n onOptionMouseDown() { this.lastMouseActivation = Date.now(); }\n\n /** Activation by assistive tech / a virtual cursor arrives as a click without a preceding mousedown. */\n onOptionClick(v: VideoMessage) {\n if (Date.now() - this.lastMouseActivation < 500) return;\n this.select(v);\n }\n\n select(v: VideoMessage) {\n // The search input is replaced by the selected-video group: keep the user's place by moving focus there.\n const hadFocus = this.host.nativeElement.contains(document.activeElement);\n this.selected.set(v);\n this.query = '';\n this.results.set([]);\n this.pending.set(false);\n this.showResults.set(false);\n this.activeIndex.set(-1);\n this.videoSelected.emit(v);\n if (hadFocus) this.focusAfterRender('.selected-input');\n }\n\n clear() {\n this.selected.set(null);\n this.query = '';\n this.results.set([]);\n this.pending.set(false);\n this.showResults.set(false);\n // The clear button disappears with the group: return focus to the search input.\n this.focusAfterRender('.picker-input');\n }\n\n private focusAfterRender(selector: string) {\n afterNextRender(() => this.host.nativeElement.querySelector<HTMLElement>(selector)?.focus(), { injector: this.injector });\n }\n}\n","import {\n Component, ChangeDetectionStrategy, input, output, inject, signal, computed, effect, linkedSignal, DestroyRef, ElementRef, forwardRef,\n} from '@angular/core';\nimport { HttpClient } from '@angular/common/http';\nimport { FORGEROUTE_CONFIG } from '../config/forgeroute.config';\nimport { Subject, debounceTime, switchMap, of, catchError } from 'rxjs';\nimport { takeUntilDestroyed } from '@angular/core/rxjs-interop';\nimport { FormsModule } from '@angular/forms';\nimport { ToastService } from '../toast/toast.service';\nimport { ErrorComponent } from '../error/error.component';\nimport { UiFieldValidity, createFieldReveal, requiredIssue } from '../utils/field-validation';\n\nexport interface TemplateItem {\n id: string;\n name: string;\n subject: string | null;\n body: string;\n isActive?: boolean;\n activeFrom?: string | null;\n activeTo?: string | null;\n useAllYear?: boolean;\n}\n\n@Component({\n selector: 'ui-template-picker',\n standalone: true,\n changeDetection: ChangeDetectionStrategy.OnPush,\n imports: [FormsModule, ErrorComponent],\n providers: [{ provide: UiFieldValidity, useExisting: forwardRef(() => TemplatePickerComponent) }],\n template: `\n <div class=\"picker-wrapper\">\n <label class=\"picker-label\" [attr.for]=\"inputId\">\n {{ label() }}@if (required()) {<span class=\"picker-required\" aria-hidden=\"true\">*</span>}\n </label>\n\n <input\n class=\"picker-input\"\n type=\"text\"\n role=\"combobox\"\n [id]=\"inputId\"\n placeholder=\"Search templates...\"\n autocomplete=\"off\"\n aria-autocomplete=\"list\"\n aria-haspopup=\"listbox\"\n [attr.aria-label]=\"label() ? null : 'Search templates'\"\n [attr.aria-invalid]=\"visibleError() ? 'true' : null\"\n [attr.aria-describedby]=\"visibleError() && !showResults() ? errorId : null\"\n [attr.aria-required]=\"required() || null\"\n [attr.aria-expanded]=\"showResults()\"\n [attr.aria-controls]=\"showResults() ? listId : null\"\n [attr.aria-activedescendant]=\"showResults() && activeIndex() >= 0 ? optionId(activeIndex()) : null\"\n [ngModel]=\"search()\"\n (ngModelChange)=\"onInput($event)\"\n (focus)=\"onFocus()\"\n (blur)=\"onBlur()\"\n (keydown)=\"onKeydown($event)\" />\n\n @if (showResults()) {\n <div class=\"picker-dropdown\" [id]=\"listId\" role=\"listbox\" [attr.aria-label]=\"label() || 'Search templates'\" (mousedown)=\"$event.preventDefault()\">\n @if (loading()) {\n <div class=\"picker-loading\">Searching...</div>\n } @else if (results().length === 0) {\n <div class=\"picker-empty\">No templates found</div>\n } @else {\n @for (tpl of results(); track tpl.id; let i = $index) {\n <div\n class=\"picker-option\"\n role=\"option\"\n [id]=\"optionId(i)\"\n [class.active]=\"activeIndex() === i\"\n [class.selected]=\"selectedId() === tpl.id\"\n [attr.aria-selected]=\"selectedId() === tpl.id\"\n (mousedown)=\"onOptionMouseDown(); select(tpl)\"\n (click)=\"onOptionClick(tpl)\"\n (mouseenter)=\"activeIndex.set(i)\">\n <span class=\"option-body\">\n <span class=\"option-name\">{{ tpl.name }}</span>\n <span class=\"option-detail\">\n @if (!isCurrentlyActive(tpl)) { <span class=\"option-inactive\">Inactive · </span> }{{ previewText(tpl.subject || tpl.body) }}\n </span>\n </span>\n <button\n type=\"button\"\n class=\"option-preview-btn\"\n [attr.aria-label]=\"'Preview ' + tpl.name\"\n (mousedown)=\"$event.stopPropagation(); $event.preventDefault(); onPreviewMouseDown(tpl)\"\n (click)=\"$event.stopPropagation(); onPreviewClick(tpl)\">\n <svg width=\"14\" height=\"14\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\" aria-hidden=\"true\">\n <path d=\"M1 12s4-8 11-8 11 8 11 8-4 8-11 8-11-8-11-8z\"/>\n <circle cx=\"12\" cy=\"12\" r=\"3\"/>\n </svg>\n </button>\n </div>\n }\n }\n </div>\n }\n\n <!-- Announces searching / the result count / no results to screen readers while the list is open. -->\n <span class=\"picker-sr\" role=\"status\" aria-live=\"polite\" aria-atomic=\"true\">{{ liveText() }}</span>\n\n <!-- Hidden while the list is open so the floating list always sits right under the field. -->\n <ui-error [errorId]=\"errorId\" [message]=\"showResults() ? '' : visibleError()\" />\n </div>\n `,\n styles: [`\n /* Same look as ui-contact-picker: label, input and a floating results dropdown. */\n .picker-wrapper { position: relative; overflow: visible; }\n .picker-label { display: block; font-size: 0.8125rem; font-weight: 600; color: #374151; margin-bottom: 6px; }\n .picker-required { color: #ef4444; margin-left: 2px; }\n .picker-input {\n width: 100%; padding: 9px 12px; border: 1px solid #e2e8f0; border-radius: 8px;\n font-size: 0.875rem; color: #1e293b; font-family: inherit; outline: none; box-sizing: border-box;\n &::placeholder { color: #94a3b8; }\n &:focus { border-color: #3b82f6; box-shadow: 0 0 0 3px rgba(59,130,246,0.12); }\n }\n .picker-dropdown {\n position: absolute; top: calc(100% + 4px); left: 0; right: 0; z-index: 9999;\n background: #fff; border: 1px solid #e2e8f0; border-radius: 8px;\n box-shadow: 0 6px 16px rgba(0,0,0,0.1); max-height: 240px; overflow-y: auto;\n }\n .picker-option {\n display: flex; align-items: center; gap: 8px; width: 100%; box-sizing: border-box;\n padding: 10px 12px; text-align: left; cursor: pointer; font-family: inherit;\n &:hover, &.active { background: #f8fafc; }\n &.selected .option-name { color: #2563eb; }\n &:first-child { border-radius: 8px 8px 0 0; }\n &:last-child { border-radius: 0 0 8px 8px; }\n }\n .option-body { display: flex; flex-direction: column; gap: 2px; min-width: 0; flex: 1; }\n .option-name { font-size: 0.875rem; font-weight: 600; color: #1e293b; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }\n .option-detail { font-size: 0.75rem; color: #94a3b8; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }\n .option-inactive { font-weight: 600; }\n .option-preview-btn {\n display: inline-flex; align-items: center; justify-content: center; flex-shrink: 0;\n width: 28px; height: 28px; border: none; border-radius: 6px; background: none;\n color: #94a3b8; cursor: pointer;\n &:hover { background: #e2e8f0; color: #475569; }\n &:focus-visible { outline: 2px solid #60a5fa; outline-offset: 2px; }\n }\n .picker-input:focus-visible { outline: 2px solid transparent; }\n .picker-sr {\n position: absolute; width: 1px; height: 1px; margin: -1px; padding: 0;\n overflow: hidden; clip: rect(0, 0, 0, 0); white-space: nowrap; border: 0;\n }\n /* Forced colors (Windows high contrast) drops box-shadow: the focus ring and the active row become real outlines. */\n @media (forced-colors: active) {\n .picker-input:focus { outline: 2px solid Highlight; outline-offset: 1px; }\n .picker-option.active { outline: 2px solid Highlight; outline-offset: -2px; }\n }\n .picker-loading, .picker-empty { padding: 12px; font-size: 0.8125rem; color: #94a3b8; text-align: center; }\n\n /* Phones / touch: 44px field, rows and preview button, and a results list that fits above the on-screen keyboard\n (40vh) instead of a fixed 240px; 16px input text on phones so iOS does not zoom on focus. */\n @media (pointer: coarse), (max-width: 480px) {\n .picker-input { min-height: 44px; }\n .picker-option { min-height: 44px; }\n .option-preview-btn { width: 44px; height: 44px; }\n .picker-dropdown { max-height: min(240px, 40vh); overscroll-behavior: contain; }\n }\n @media (max-width: 480px) {\n .picker-input { font-size: 1rem; }\n }\n `],\n})\nexport class TemplatePickerComponent {\n private readonly http = inject(HttpClient);\n /** API root from FORGEROUTE_CONFIG (e.g. a cross-origin prod API); falls back to same-origin `/api`. */\n private readonly apiBase = inject(FORGEROUTE_CONFIG, { optional: true })?.baseUrl ?? '/api';\n private readonly destroyRef = inject(DestroyRef);\n private readonly toast = inject(ToastService);\n private readonly host = inject<ElementRef<HTMLElement>>(ElementRef);\n private readonly search$ = new Subject<string>();\n\n /** Filter results to a specific channel: 'email', 'text', or omit for all. */\n readonly channel = input<'email' | 'text' | ''>('');\n readonly selectedId = input<string | null>(null);\n readonly label = input('Template');\n /** Shows the red required asterisk (same as the library inputs) — e.g. when a template must be picked to send. */\n readonly required = input(false);\n /** Reveal the built-in \"<label> is required.\" message now (e.g. after a failed Send click); otherwise it shows after the field was blurred. */\n readonly showErrors = input(false);\n\n readonly templateSelected = output<TemplateItem>();\n readonly previewClick = output<{ subject: string; body: string }>();\n /** Emits whenever `invalid` flips (required and no template picked). */\n readonly invalidChange = output<boolean>();\n\n readonly search = signal('');\n readonly results = signal<TemplateItem[]>([]);\n readonly loading = signal(false);\n /** Dropdown open state — like ui-contact-picker: open while focused with 2+ characters typed. */\n readonly showResults = signal(false);\n /** Keyboard-highlighted option index (-1 = none). */\n readonly activeIndex = signal(-1);\n\n /** The picked template id: follows `[selectedId]`; a pick made in the picker overrides it until the host changes `[selectedId]`. */\n private readonly picked = linkedSignal<string | null>(() => this.selectedId());\n private readonly reveal = createFieldReveal(this.showErrors);\n /** `UiFieldValidity`: true while `required` and no template is picked. */\n readonly invalid = computed(() => this.required() && !this.picked());\n /** \"<label> is required.\" while invalid ('' otherwise), shown or not. */\n readonly errorMessage = computed(() => this.invalid() ? requiredIssue(this.label()).message : '');\n /** `errorMessage` once the field was blurred or `showErrors` is set. */\n readonly visibleError = computed(() => this.reveal.revealed() ? this.errorMessage() : '');\n\n /** Polite announcement for screen readers: searching, the result count, or no results (only while the list is open). */\n readonly liveText = computed(() => {\n if (!this.showResults()) return '';\n if (this.loading()) return 'Searching';\n const n = this.results().length;\n return n === 0 ? 'No results' : n === 1 ? '1 result' : `${n} results`;\n });\n\n private static nextId = 0;\n private readonly uid = `ui-tpl-${TemplatePickerComponent.nextId++}`;\n readonly inputId = `${this.uid}-input`;\n readonly listId = `${this.uid}-list`;\n readonly errorId = `${this.uid}-error`;\n optionId(i: number): string { return `${this.uid}-opt-${i}`; }\n\n private blurTimer: ReturnType<typeof setTimeout> | null = null;\n\n constructor() {\n effect(() => { this.invalidChange.emit(this.invalid()); });\n this.destroyRef.onDestroy(() => this.clearBlurTimer());\n this.search$.pipe(\n debounceTime(250),\n switchMap(q => {\n if (q.length < 2) { this.results.set([]); this.loading.set(false); return of({ content: [] }); }\n this.loading.set(true);\n const params: Record<string, string> = { search: q, size: '20' };\n if (this.channel()) params['channel'] = this.channel()!;\n return this.http.get<{ content: any[] }>(`${this.apiBase}/templates`, { params }).pipe(\n catchError(() => of({ content: [] })),\n );\n }),\n takeUntilDestroyed(this.destroyRef),\n ).subscribe(res => {\n this.results.set((res.content ?? []).map((t: any): TemplateItem => ({\n id: t.id, name: t.name, subject: t.subject ?? null, body: t.body,\n isActive: t.isActive, activeFrom: t.activeFrom ?? null,\n activeTo: t.activeTo ?? null, useAllYear: t.useAllYear ?? true,\n })));\n this.loading.set(false);\n this.activeIndex.set(-1);\n });\n }\n\n onInput(q: string) {\n this.search.set(q);\n this.showResults.set(q.length >= 2);\n // Show \"Searching...\" right away — never the previous query's (or an empty) result list during the debounce.\n this.loading.set(q.length >= 2);\n this.activeIndex.set(-1);\n this.search$.next(q);\n }\n\n onFocus() { this.clearBlurTimer(); if (this.search().length >= 2) this.showResults.set(true); }\n\n onBlur() {\n this.reveal.touched.set(true);\n this.clearBlurTimer();\n this.blurTimer = setTimeout(() => { this.blurTimer = null; this.showResults.set(false); }, 200);\n }\n\n private clearBlurTimer() {\n if (this.blurTimer !== null) { clearTimeout(this.blurTimer); this.blurTimer = null; }\n }\n\n /**\n * ARIA combobox keys: ArrowDown/ArrowUp open the list and move the highlight (wrapping), Home/End jump to the first /\n * last option, Enter picks the highlighted option, Escape closes the list only (so a surrounding side panel / modal\n * stays open; with the list closed it passes through). Tab selects nothing.\n */\n onKeydown(e: KeyboardEvent) {\n const n = this.results().length;\n if (e.key === 'Escape') {\n if (this.showResults()) { e.preventDefault(); e.stopPropagation(); this.showResults.set(false); this.activeIndex.set(-1); }\n return;\n }\n if ((e.key === 'ArrowDown' || e.key === 'ArrowUp') && !this.showResults()) {\n if (this.search().length < 2) return;\n e.preventDefault();\n this.clearBlurTimer();\n this.showResults.set(true);\n return;\n }\n if (!this.showResults() || this.loading() || n === 0) return;\n if (e.key === 'Home' || e.key === 'End') { e.preventDefault(); this.setActive(e.key === 'Home' ? 0 : n - 1); return; }\n if (e.key === 'ArrowDown') { e.preventDefault(); this.setActive((this.activeIndex() + 1) % n); }\n else if (e.key === 'ArrowUp') { e.preventDefault(); this.setActive(this.activeIndex() <= 0 ? n - 1 : this.activeIndex() - 1); }\n else if (e.key === 'Enter' && this.activeIndex() >= 0) { e.preventDefault(); this.select(this.results()[this.activeIndex()]); }\n }\n\n /** Highlights option `i` and scrolls it into view inside the dropdown. */\n private setActive(i: number) {\n this.activeIndex.set(i);\n this.host.nativeElement.querySelector<HTMLElement>(`#${this.optionId(i)}`)?.scrollIntoView({ block: 'nearest' });\n }\n\n /** Timestamps of the last mousedown activation: the click that follows it is the same gesture, not a second action. */\n private lastOptionMouse = 0;\n private lastPreviewMouse = 0;\n onOptionMouseDown() { this.lastOptionMouse = Date.now(); }\n /** Activation by assistive tech / a virtual cursor arrives as a click without a preceding mousedown. */\n onOptionClick(tpl: TemplateItem) { if (Date.now() - this.lastOptionMouse >= 500) this.select(tpl); }\n onPreviewMouseDown(tpl: TemplateItem) { this.lastPreviewMouse = Date.now(); this.preview(tpl); }\n onPreviewClick(tpl: TemplateItem) { if (Date.now() - this.lastPreviewMouse >= 500) this.preview(tpl); }\n\n select(tpl: TemplateItem) {\n this.picked.set(tpl.id);\n this.showResults.set(false);\n this.activeIndex.set(-1);\n this.templateSelected.emit(tpl);\n }\n\n preview(tpl: TemplateItem) { this.previewClick.emit({ subject: tpl.subject || '', body: tpl.body }); }\n\n private readonly sampleVars: Record<string, string> = {\n first_name: 'Sarah', last_name: 'Johnson', full_name: 'Sarah Johnson',\n email: 'sarah@example.com', phone: '(555) 867-5309',\n property_address: '1842 Maple Grove Lane', address: '1842 Maple Grove Lane',\n price: '$485,000', list_price: '$485,000', agent_name: 'Alex Rivera',\n company_name: 'Real Estate', closing_date: 'Sept 15, 2026',\n };\n\n previewText(raw: string): string {\n const resolved = raw.replace(/\\{\\{(\\w+)\\}\\}/g, (_, k) => this.sampleVars[k] ?? '…');\n return resolved.length > 80 ? resolved.slice(0, 80) + '…' : resolved;\n }\n\n isCurrentlyActive(tpl: TemplateItem): boolean {\n if (tpl.isActive === false) return false;\n if (tpl.useAllYear !== false) return true;\n const now = new Date();\n const from = tpl.activeFrom ? new Date(tpl.activeFrom) : null;\n const to = tpl.activeTo ? new Date(tpl.activeTo) : null;\n if (from && now < from) return false;\n if (to && now > to) return false;\n return true;\n }\n\n activeDateLabel(tpl: TemplateItem): string {\n if (tpl.useAllYear !== false) return 'Active all year';\n const fmt = (d: string) => new Date(d).toLocaleDateString('en-US', { month: 'short', day: 'numeric' });\n if (tpl.activeFrom && tpl.activeTo) return `${fmt(tpl.activeFrom)} – ${fmt(tpl.activeTo)}`;\n if (tpl.activeFrom) return `From ${fmt(tpl.activeFrom)}`;\n if (tpl.activeTo) return `Until ${fmt(tpl.activeTo)}`;\n return 'Active all year';\n }\n}\n","import {\n Component, ChangeDetectionStrategy, DestroyRef, ElementRef, Injector, afterNextRender, computed, effect, forwardRef,\n inject, input, linkedSignal, output, signal, viewChild,\n} from '@angular/core';\nimport { AbstractControl, ControlValueAccessor, NG_VALIDATORS, NG_VALUE_ACCESSOR, ValidationErrors, Validator } from '@angular/forms';\nimport { FieldIssue, UiFieldValidity, createFieldReveal, issueToErrors, requiredIssue } from '../utils/field-validation';\n\n/** One selectable row. `data` carries any host payload (a typed record, an id, ...) back through `(valueChange)`. */\nexport interface ComboboxOption<T = unknown> {\n value: string;\n label: string;\n /** Secondary line under the label (trade, customer name, ...). */\n description?: string;\n disabled?: boolean;\n data?: T;\n}\n\n/** The selected value: what `[value]` and `ngModel` / `FormControl` hold. */\nexport interface ComboboxSelection { value: string; label: string }\n\nlet nextComboboxId = 0;\n\n/**\n * Search-as-you-type single select (WAI-ARIA 1.2 combobox with listbox popup). The HOST fetches: the combobox emits\n * `(searchChange)` with the typed term (debounced) and renders whatever `[options]` currently is, so a host using\n * `switchMap` is race-safe and a stale array never \"sticks\" (the active option is re-validated on every change).\n *\n * ## API\n * - `[options]` `ComboboxOption[]` current results (`{ value, label, description?, disabled?, data? }`).\n * - `[value]` `{ value, label } | null` the selection. Also what `ngModel` / `formControl` hold (same shape).\n * - `[displayText]` text shown in the field instead of the selected label — use it for legacy / orphaned links:\n * `displayText=\"Not linked: Legacy Co\"` (with `[value]=\"null\"`) or `displayText=\"Acme (no longer exists)\"`.\n * Pair it with `[displayWarning]=\"true\"` for the amber \"needs attention\" style. A `displayText` with no `value` is\n * shown and has a clear button, but counts as EMPTY for `required`.\n * - `[loading]` shows \"Searching…\" and announces it. `[noResultsText]` (default \"No results found\").\n * - `[minChars]` (default 0): below it nothing is searched or listed (\"Type at least N characters\"). 0 = the list opens\n * on focus and `(searchChange)` fires with '' so the host can show its initial results.\n * - `[debounce]` ms between the last keystroke and `(searchChange)` (default 250). Focus / ArrowDown-open emit immediately.\n * - `[label] [ariaLabel] [placeholder] [required] [hint] [error] [disabled] [showErrors]` like `ui-text-input`\n * (host `[error]` wins over the built-in \"<label> is required.\"; messages show after blur or `showErrors`).\n * - `(searchChange)` string, `(valueChange)` `ComboboxOption | null` (the full chosen option, null when cleared),\n * `(invalidChange)` boolean, `(focused)`, `(blurred)`. Validity: `UiFieldValidity` token + Angular `Validator`.\n *\n * ## Behaviour\n * - The field shows the selected label. Focusing it selects that text; typing starts a NEW search and the old value is\n * kept until another option is picked or the clear (x) button is pressed. Leaving the field (blur / Tab / Escape)\n * discards the typed text and shows the selection again.\n * - Escape: if the list is open it closes the list ONLY (stopPropagation, so a surrounding side panel / modal stays\n * open). If the list is already closed and text was typed, it discards the text (also stopped). Otherwise the key\n * passes through to the panel.\n * - Tab never selects: it closes the list and moves on.\n *\n * ## Keyboard\n * ArrowDown / ArrowUp open the list and move the active option (wraps, skips disabled). Home / End jump to the first /\n * last option while the list is open. Enter picks the active option (and does not submit a surrounding form while open).\n * Escape / Tab as above. Typing opens the list. Mouse: options are picked on mousedown so blur does not swallow it.\n *\n * ## ARIA\n * The inner `<input>` is `role=\"combobox\"` with `aria-expanded`, `aria-controls` (the listbox, while open),\n * `aria-autocomplete=\"list\"`, `aria-activedescendant`, `aria-invalid`, `aria-required`, `aria-describedby` (hint / error);\n * the `<label for>` names it. The popup is `role=\"listbox\"` of `role=\"option\"` rows (`aria-selected` = active row,\n * stable ids). A polite `role=\"status\"` region announces \"Searching\", \"N results available\" or the no-results text.\n *\n * ## Example\n * ```html\n * <ui-combobox label=\"Subcontractor\" [required]=\"true\" [options]=\"opts()\" [loading]=\"loading()\"\n * [value]=\"sub()\" [displayText]=\"legacyName() ? 'Not linked: ' + legacyName() : ''\" [error]=\"err()\"\n * (searchChange)=\"search$.next($event)\" (valueChange)=\"onPick($event)\" />\n * ```\n */\n@Component({\n selector: 'ui-combobox',\n standalone: true,\n changeDetection: ChangeDetectionStrategy.OnPush,\n providers: [{\n provide: NG_VALUE_ACCESSOR,\n useExisting: forwardRef(() => ComboboxComponent),\n multi: true,\n }, {\n provide: NG_VALIDATORS,\n useExisting: forwardRef(() => ComboboxComponent),\n multi: true,\n }, {\n provide: UiFieldValidity,\n useExisting: forwardRef(() => ComboboxComponent),\n }],\n template: `\n <div class=\"cb-field\" [class.cb-error]=\"_activeError()\" [class.cb-disabled]=\"disabled()\" [class.cb-warning]=\"displayWarning()\">\n @if (label()) {\n <label class=\"cb-label\" [for]=\"_inputId\">\n {{ label() }}\n @if (required()) { <span class=\"cb-required\" aria-hidden=\"true\">*</span> }\n </label>\n }\n <div class=\"cb-control\">\n <input\n #field\n class=\"cb-input\"\n type=\"text\"\n role=\"combobox\"\n aria-autocomplete=\"list\"\n autocomplete=\"off\"\n [id]=\"_inputId\"\n [class.cb-has-clear]=\"_showClear()\"\n [placeholder]=\"placeholder()\"\n [value]=\"_inputText()\"\n [disabled]=\"disabled()\"\n [attr.aria-label]=\"ariaLabel() || null\"\n [attr.aria-expanded]=\"_open() ? 'true' : 'false'\"\n [attr.aria-controls]=\"_open() ? _listId : null\"\n [attr.aria-activedescendant]=\"_activeDescendant()\"\n [attr.aria-invalid]=\"_activeError() ? 'true' : null\"\n [attr.aria-required]=\"required() || null\"\n [attr.aria-describedby]=\"(_activeError() || hint()) ? _descId : null\"\n (input)=\"onInput($event)\"\n (focus)=\"onFocus($event)\"\n (click)=\"onClick()\"\n (blur)=\"onBlur()\"\n (keydown)=\"onKeydown($event)\"\n />\n @if (_showClear()) {\n <button type=\"button\" class=\"cb-clear\" data-testid=\"combobox-clear\" [attr.aria-label]=\"'Clear ' + (label() || ariaLabel() || 'selection')\"\n (mousedown)=\"$event.preventDefault()\" (click)=\"clear()\">×</button>\n }\n @if (_open()) {\n <div class=\"cb-popup\" (mousedown)=\"$event.preventDefault()\">\n <div class=\"cb-list\" role=\"listbox\" [id]=\"_listId\" [attr.aria-label]=\"label() || ariaLabel() || null\">\n @if (_belowMin() === false) {\n @for (o of options(); track o.value; let i = $index) {\n <div class=\"cb-option\" role=\"option\" [id]=\"_optionId(i)\"\n [class.cb-option-active]=\"i === _activeIndex()\"\n [class.cb-option-disabled]=\"o.disabled\"\n [class.cb-option-selected]=\"o.value === _selected()?.value\"\n [attr.aria-selected]=\"i === _activeIndex() ? 'true' : 'false'\"\n [attr.aria-disabled]=\"o.disabled ? 'true' : null\"\n (mousedown)=\"onOptionMouseDown($event, o)\"\n (mousemove)=\"onOptionHover(i, o)\">\n <span class=\"cb-option-label\">{{ o.label }}</span>\n @if (o.description) { <span class=\"cb-option-desc\">{{ o.description }}</span> }\n </div>\n }\n }\n </div>\n @if (_statusText(); as status) {\n <div class=\"cb-status\" data-testid=\"combobox-status\">{{ status }}</div>\n }\n </div>\n }\n </div>\n <span class=\"cb-live\" role=\"status\" aria-live=\"polite\">{{ _liveText() }}</span>\n @if (_activeError()) {\n <p class=\"cb-desc cb-desc-error\" [id]=\"_descId\" role=\"alert\">{{ _activeError() }}</p>\n } @else if (hint()) {\n <p class=\"cb-desc\" [id]=\"_descId\">{{ hint() }}</p>\n }\n </div>\n `,\n styles: [`\n :host { display: block; }\n\n .cb-field { display: flex; flex-direction: column; gap: 6px; }\n .cb-label { font-size: 0.8125rem; font-weight: 600; color: #374151; line-height: 1; }\n .cb-required { color: #ef4444; margin-left: 2px; }\n\n .cb-control { position: relative; }\n .cb-input {\n width: 100%; box-sizing: border-box;\n padding: 9px 12px; font-size: 0.875rem; font-family: inherit;\n color: #1e293b; background: #fff;\n border: 1px solid #e2e8f0; border-radius: 8px;\n outline: none; transition: border-color 0.15s, box-shadow 0.15s;\n &::placeholder { color: #94a3b8; }\n &:focus { border-color: #3b82f6; box-shadow: 0 0 0 3px rgba(59, 130, 246, 0.12); }\n }\n .cb-has-clear { padding-right: 40px; }\n\n .cb-warning .cb-input { border-color: #f59e0b; background: #fffbeb; }\n .cb-error .cb-input {\n border-color: #ef4444;\n &:focus { box-shadow: 0 0 0 3px rgba(239, 68, 68, 0.12); }\n }\n .cb-disabled .cb-input { opacity: 0.5; cursor: not-allowed; }\n\n .cb-clear {\n position: absolute; top: 50%; right: 4px; transform: translateY(-50%);\n border: 0; background: transparent; cursor: pointer; color: #64748b;\n font-size: 1.25rem; line-height: 1; min-width: 32px; min-height: 32px; padding: 0 8px; border-radius: 6px;\n &:hover { color: #0f172a; }\n &:focus-visible { outline: 2px solid #3b82f6; }\n }\n\n .cb-popup {\n position: absolute; z-index: 30; left: 0; right: 0; top: 100%; margin-top: 4px; box-sizing: border-box;\n max-width: 100%; max-height: min(260px, 40vh); overflow-y: auto; overscroll-behavior: contain;\n background: #fff; border: 1px solid #e2e8f0; border-radius: 8px; box-shadow: 0 8px 24px rgba(15, 23, 42, 0.12);\n }\n .cb-list:empty { display: none; }\n .cb-status { padding: 12px; font-size: 0.875rem; color: #64748b; }\n .cb-option {\n display: flex; flex-direction: column; gap: 2px; padding: 8px 12px; min-height: 40px; justify-content: center;\n box-sizing: border-box; cursor: pointer;\n }\n .cb-option-active { background: #eff6ff; }\n .cb-option-selected .cb-option-label { font-weight: 600; }\n .cb-option-disabled { opacity: 0.5; cursor: not-allowed; }\n .cb-option-label { font-size: 0.875rem; color: #0f172a; overflow-wrap: anywhere; }\n .cb-option-desc { font-size: 0.75rem; color: #64748b; overflow-wrap: anywhere; }\n\n .cb-desc { margin: 0; font-size: 0.75rem; line-height: 1.4; color: #64748b; }\n .cb-desc-error { color: #ef4444; }\n\n .cb-live { position: absolute; width: 1px; height: 1px; overflow: hidden; clip: rect(0 0 0 0); white-space: nowrap; }\n\n /* Reduced motion: no transition on the field. Forced colors (Windows high contrast) drop box-shadow, so the focus\n ring and the active option fall back to real outlines. */\n @media (prefers-reduced-motion: reduce) { .cb-input { transition: none; } }\n @media (forced-colors: active) {\n .cb-input:focus { outline: 2px solid Highlight; outline-offset: 1px; }\n .cb-option-active { outline: 2px solid Highlight; outline-offset: -2px; }\n }\n\n /* Touch targets: the field and its clear (x) button are at least 44px on touch devices and phones. */\n @media (pointer: coarse), (max-width: 480px) {\n .cb-input { min-height: 44px; }\n .cb-clear { min-width: 44px; min-height: 44px; }\n }\n @media (pointer: coarse) { .cb-option { min-height: 44px; } }\n @media (max-width: 480px) {\n .cb-input { font-size: 1rem; }\n .cb-option { min-height: 44px; }\n }\n `],\n})\nexport class ComboboxComponent<T = unknown> implements ControlValueAccessor, Validator, UiFieldValidity {\n readonly label = input('');\n /** Accessible name for a field with no visible label. */\n readonly ariaLabel = input('');\n readonly placeholder = input('');\n readonly required = input(false);\n readonly hint = input('');\n /** Host error message; always wins over the built-in message. */\n readonly error = input('');\n readonly showErrors = input(false);\n /** Current results (host-owned). */\n readonly options = input<ComboboxOption<T>[]>([]);\n readonly loading = input(false);\n readonly noResultsText = input('No results found');\n /** Characters needed before searching / listing. 0 = open on focus. */\n readonly minChars = input(0);\n /** Milliseconds between the last keystroke and `(searchChange)`. */\n readonly debounce = input(250);\n /** The selection. */\n readonly value = input<ComboboxSelection | null>(null);\n /** Text shown instead of the selected label (legacy / orphaned links). */\n readonly displayText = input('');\n /** Amber style for a legacy / orphaned `displayText`. */\n readonly displayWarning = input(false);\n\n /** Typed term, debounced (immediate on focus / open). */\n readonly searchChange = output<string>();\n /** The chosen option, or null when cleared. */\n readonly valueChange = output<ComboboxOption<T> | null>();\n readonly invalidChange = output<boolean>();\n readonly focused = output<void>();\n readonly blurred = output<void>();\n\n protected readonly _disabledInput = input(false, { alias: 'disabled' });\n private readonly _cvaDisabled = signal(false);\n protected readonly disabled = computed(() => this._disabledInput() || this._cvaDisabled());\n\n private readonly _field = viewChild<ElementRef<HTMLInputElement>>('field');\n private readonly _host = inject(ElementRef) as ElementRef<HTMLElement>;\n private readonly _injector = inject(Injector);\n private readonly _destroyRef = inject(DestroyRef);\n\n /** Follows `[value]`; ngModel / formControl writes and user picks override it until `[value]` changes. */\n protected readonly _selected = linkedSignal<ComboboxSelection | null>(() => this.value());\n private readonly _text = signal('');\n private readonly _editing = signal(false);\n protected readonly _open = signal(false);\n /** Reset to the first enabled option whenever `[options]` changes, so a stale index never survives new results. */\n protected readonly _activeIndex = linkedSignal<ComboboxOption<T>[], number>({\n source: this.options,\n computation: opts => this._firstEnabled(opts),\n });\n\n protected readonly _uid = ++nextComboboxId;\n protected readonly _inputId = `cb-${this._uid}`;\n protected readonly _listId = `cb-list-${this._uid}`;\n protected readonly _descId = `cb-desc-${this._uid}`;\n\n private readonly _reveal = createFieldReveal(this.showErrors);\n private readonly _issue = computed(() => this._check(this._selected()));\n readonly invalid = computed(() => !!this.error() || !!this._issue());\n readonly errorMessage = computed(() => this._issue()?.message ?? '');\n protected readonly _activeError = computed(() => this.error() || (this._reveal.revealed() ? this.errorMessage() : ''));\n\n protected readonly _inputText = computed(() =>\n this._editing() ? this._text() : (this.displayText() || this._selected()?.label || ''));\n protected readonly _showClear = computed(() => !this.disabled() && !this._editing() && !!(this._selected() || this.displayText()));\n protected readonly _belowMin = computed(() => this._term().length < this.minChars());\n protected readonly _statusText = computed(() => {\n if (this._belowMin()) return `Type at least ${this.minChars()} character${this.minChars() === 1 ? '' : 's'}`;\n if (this.loading()) return 'Searching…';\n return this.options().length === 0 ? this.noResultsText() : '';\n });\n protected readonly _liveText = computed(() => {\n if (!this._open() || this._belowMin()) return '';\n if (this.loading()) return 'Searching';\n const n = this.options().length;\n if (n === 0) return this.noResultsText();\n return `${n} result${n === 1 ? '' : 's'} available`;\n });\n protected readonly _activeDescendant = computed(() => {\n const i = this._activeIndex();\n return this._open() && !this._belowMin() && i >= 0 && i < this.options().length ? this._optionId(i) : null;\n });\n\n private _debounceTimer: ReturnType<typeof setTimeout> | null = null;\n private _onChange = (_: ComboboxSelection | null) => {};\n private _onTouched = () => {};\n private _onValidatorChange = () => {};\n\n constructor() {\n effect(() => { this.invalidChange.emit(this.invalid()); });\n this._destroyRef.onDestroy(() => this._cancelTimer());\n }\n\n ngOnChanges() { this._onValidatorChange(); }\n\n // ── CVA / Validator ───────────────────────────────────────────────\n writeValue(val: ComboboxSelection | null) { this._selected.set(val ?? null); }\n validate(control: AbstractControl): ValidationErrors | null {\n return issueToErrors(this._check((control.value as ComboboxSelection | null) ?? null));\n }\n registerOnValidatorChange(fn: () => void) { this._onValidatorChange = fn; }\n registerOnChange(fn: (v: ComboboxSelection | null) => void) { this._onChange = fn; }\n registerOnTouched(fn: () => void) { this._onTouched = fn; }\n setDisabledState(d: boolean) { this._cvaDisabled.set(d); }\n\n private _check(sel: ComboboxSelection | null): FieldIssue | null {\n if (this.disabled()) return null;\n return !sel && this.required() ? requiredIssue(this.label()) : null;\n }\n\n // ── Helpers ───────────────────────────────────────────────────────\n /** What the user typed (trimmed); '' while not editing. */\n private _term(): string { return this._editing() ? this._text().trim() : ''; }\n protected _optionId(i: number) { return `${this._listId}-opt-${i}`; }\n private _firstEnabled(opts: ComboboxOption<T>[]) { return opts.findIndex(o => !o.disabled); }\n\n private _emitSearch() { this._cancelTimer(); if (!this._belowMin()) this.searchChange.emit(this._term()); }\n private _cancelTimer() { if (this._debounceTimer != null) { clearTimeout(this._debounceTimer); this._debounceTimer = null; } }\n\n private _revert() { this._cancelTimer(); this._editing.set(false); this._text.set(''); }\n\n private _openList() {\n if (this.disabled()) return;\n if (!this._open()) { this._open.set(true); this._emitSearch(); }\n }\n\n private _move(dir: 1 | -1) {\n const opts = this.options();\n if (!opts.length || opts.every(o => o.disabled)) return;\n let i = this._activeIndex();\n for (let step = 0; step < opts.length; step++) {\n i = ((i < 0 ? (dir === 1 ? -1 : 0) : i) + dir + opts.length) % opts.length;\n if (!opts[i].disabled) break;\n }\n this._setActive(i);\n }\n\n private _setActive(i: number) {\n this._activeIndex.set(i);\n afterNextRender(() => {\n const el = this._host.nativeElement.querySelector(`#${this._optionId(i)}`) as HTMLElement | null;\n if (el && typeof el.scrollIntoView === 'function') el.scrollIntoView({ block: 'nearest' });\n }, { injector: this._injector });\n }\n\n // ── Events ────────────────────────────────────────────────────────\n protected onInput(event: Event) {\n this._text.set((event.target as HTMLInputElement).value);\n this._editing.set(true);\n this._open.set(true);\n this._cancelTimer();\n if (this._belowMin()) return;\n this._debounceTimer = setTimeout(() => { this._debounceTimer = null; this._emitSearch(); }, this.debounce());\n }\n\n protected onFocus(event: FocusEvent) {\n this.focused.emit();\n const el = event.target as HTMLInputElement;\n if (this._selected() || this.displayText()) el.select?.();\n this._openList();\n }\n\n protected onClick() { this._openList(); }\n\n protected onBlur() {\n this._open.set(false);\n this._revert();\n this._onTouched();\n this.blurred.emit();\n this._reveal.touched.set(true);\n }\n\n protected onKeydown(e: KeyboardEvent) {\n const opts = this.options();\n const listing = this._open() && !this._belowMin();\n switch (e.key) {\n case 'ArrowDown':\n case 'ArrowUp':\n e.preventDefault();\n if (!this._open()) { this._openList(); return; }\n this._move(e.key === 'ArrowDown' ? 1 : -1);\n return;\n case 'Home':\n case 'End':\n if (listing && opts.length) {\n e.preventDefault();\n const idx = e.key === 'Home' ? opts.findIndex(o => !o.disabled) : opts.map(o => !o.disabled).lastIndexOf(true);\n if (idx >= 0) this._setActive(idx);\n }\n return;\n case 'Enter':\n if (this._open()) {\n e.preventDefault();\n const i = this._activeIndex();\n if (listing && i >= 0 && i < opts.length && !opts[i].disabled) this.choose(opts[i]);\n }\n return;\n case 'Escape':\n if (this._open()) { e.preventDefault(); e.stopPropagation(); this._cancelTimer(); this._open.set(false); }\n else if (this._editing()) { e.preventDefault(); e.stopPropagation(); this._revert(); }\n return;\n case 'Tab':\n this._open.set(false);\n return;\n }\n }\n\n protected onOptionMouseDown(e: MouseEvent, o: ComboboxOption<T>) {\n e.preventDefault();\n if (!o.disabled) this.choose(o);\n }\n\n protected onOptionHover(i: number, o: ComboboxOption<T>) {\n if (!o.disabled && i !== this._activeIndex()) this._activeIndex.set(i);\n }\n\n /** Select `o` (also callable by a host through a template ref). */\n choose(o: ComboboxOption<T>) {\n const sel = { value: o.value, label: o.label };\n this._selected.set(sel);\n this._revert();\n this._open.set(false);\n this._onChange(sel);\n this.valueChange.emit(o);\n }\n\n /** Clear the selection (the x button). */\n clear() {\n this._selected.set(null);\n this._revert();\n this._open.set(false);\n this._onChange(null);\n this.valueChange.emit(null);\n afterNextRender(() => this._field()?.nativeElement.focus(), { injector: this._injector });\n }\n}\n","import { HttpErrorResponse, HttpInterceptorFn } from '@angular/common/http';\nimport { catchError, throwError } from 'rxjs';\n\n/** What the user was doing when the request failed — drives the fallback wording. */\nexport type ErrorAction =\n | 'load' | 'save' | 'create' | 'update' | 'delete'\n | 'send' | 'export' | 'import' | 'upload' | 'download';\n\nconst ACTION_VERB: Record<ErrorAction, string> = {\n load: 'loading', save: 'saving', create: 'creating', update: 'updating', delete: 'deleting',\n send: 'sending', export: 'exporting', import: 'importing', upload: 'uploading', download: 'downloading',\n};\n\n/** Present-tense verb for the 403 message (\"You don't have permission to <verb> X.\"). */\nconst ACTION_PERMISSION_VERB: Record<ErrorAction, string> = {\n load: 'access', save: 'save', create: 'create', update: 'update', delete: 'delete',\n send: 'send', export: 'export', import: 'import', upload: 'upload', download: 'download',\n};\n\n/** Non-enumerable property `errorContextInterceptor` attaches to failed responses. */\nconst REQUEST_METHOD = '__forgerouteRequestMethod';\n\n/**\n * Records the HTTP method on every failed response so `getErrorMessage` can word its fallback\n * for what the user was doing (GET → loading, POST/PUT/PATCH → saving, DELETE → deleting).\n * Register it FIRST in `withInterceptors([...])` so it sees the final error. The error object is\n * re-thrown unchanged (same instance), so `instanceof HttpErrorResponse` checks keep working.\n */\nexport const errorContextInterceptor: HttpInterceptorFn = (req, next) =>\n next(req).pipe(\n catchError((err: unknown) => {\n if (err instanceof HttpErrorResponse && !(REQUEST_METHOD in err)) {\n Object.defineProperty(err, REQUEST_METHOD, { value: req.method.toUpperCase(), enumerable: false });\n }\n return throwError(() => err);\n }),\n );\n\n/** The action implied by a failed request's method, or 'load' when unknown. */\nfunction actionFromMethod(error: HttpErrorResponse): ErrorAction {\n const method = (error as unknown as Record<string, unknown>)[REQUEST_METHOD];\n switch (method) {\n case 'POST': case 'PUT': case 'PATCH': return 'save';\n case 'DELETE': return 'delete';\n default: return 'load';\n }\n}\n\n/**\n * Extracts a user-friendly error message from any error response.\n * Prioritizes: backend message > status text > typed fallback.\n *\n * The fallback names what the user was doing: pass `action` explicitly ('send', 'export', …), or\n * let it come from the request method recorded by `errorContextInterceptor`. With neither it says \"loading\".\n */\nexport function getErrorMessage(error: unknown, fallbackContext?: string, action?: ErrorAction): string {\n const ctx = fallbackContext || 'data';\n const act: ErrorAction = action ?? (error instanceof HttpErrorResponse ? actionFromMethod(error) : 'load');\n const failed = `There was an error ${ACTION_VERB[act]} ${ctx}.`;\n if (error instanceof HttpErrorResponse) {\n if (error.error?.message) {\n return error.error.message;\n }\n if (typeof error.error === 'string' && error.error.length < 200) {\n return error.error;\n }\n switch (error.status) {\n case 0:\n return `${failed} Please check your connection and try again.`;\n case 401:\n return 'Your session has expired. Please log in again.';\n case 403:\n return `You don't have permission to ${ACTION_PERMISSION_VERB[act]} ${ctx}.`;\n case 404:\n return `The ${ctx} you're looking for could not be found.`;\n case 408:\n return `${failed} The request timed out — please try again.`;\n case 422:\n return 'The data you submitted is invalid. Please check your input and try again.';\n case 429:\n return `${failed} Too many requests — please wait a moment and try again.`;\n case 502:\n case 503:\n case 504:\n return `${failed} Please try again in a few minutes.`;\n default:\n return `${failed} Please try again.`;\n }\n }\n if (error instanceof Error) return error.message;\n if (typeof error === 'string') return error;\n return `${failed} Please try again.`;\n}\n","import { Component, ChangeDetectionStrategy, input, output, signal, computed, effect, forwardRef } from '@angular/core';\nimport { AbstractControl, ControlValueAccessor, NG_VALIDATORS, NG_VALUE_ACCESSOR, ValidationErrors, Validator } from '@angular/forms';\nimport { FieldIssue, UiFieldValidity, createFieldReveal, issueToErrors, maxLengthIssue, requiredIssue } from '../utils/field-validation';\n\n@Component({\n selector: 'ui-textarea-input',\n standalone: true,\n changeDetection: ChangeDetectionStrategy.OnPush,\n providers: [{\n provide: NG_VALUE_ACCESSOR,\n useExisting: forwardRef(() => TextareaInputComponent),\n multi: true,\n }, {\n provide: NG_VALIDATORS,\n useExisting: forwardRef(() => TextareaInputComponent),\n multi: true,\n }, {\n provide: UiFieldValidity,\n useExisting: forwardRef(() => TextareaInputComponent),\n }],\n template: `\n <div class=\"ta-field\" [class.ta-error]=\"_activeError()\" [class.ta-disabled]=\"disabled()\">\n @if (label()) {\n <label class=\"ta-label\" [for]=\"_inputId\">\n {{ label() }}\n @if (required()) { <span class=\"ta-required\" aria-hidden=\"true\">*</span> }\n @if (hint()) { <span class=\"ta-hint-inline\">({{ hint() }})</span> }\n </label>\n }\n <textarea\n class=\"ta-input\"\n [id]=\"_inputId\"\n [placeholder]=\"placeholder()\"\n [value]=\"_value()\"\n [disabled]=\"disabled()\"\n [readOnly]=\"readonly()\"\n [rows]=\"rows()\"\n [attr.maxlength]=\"maxlength() ?? null\"\n [attr.required]=\"required() || null\"\n [attr.aria-label]=\"ariaLabel() || null\"\n [attr.aria-invalid]=\"_activeError() ? 'true' : null\"\n [attr.aria-describedby]=\"_activeError() ? _inputId + '-desc' : null\"\n (input)=\"onInput($event)\"\n (blur)=\"onBlur()\"\n ></textarea>\n @if (_activeError()) {\n <p class=\"ta-desc ta-desc-error\" [id]=\"_inputId + '-desc'\" role=\"alert\">{{ _activeError() }}</p>\n }\n </div>\n `,\n styles: [`\n :host { display: block; }\n\n .ta-field { display: flex; flex-direction: column; gap: 6px; }\n\n .ta-label {\n font-size: 0.8125rem; font-weight: 600; color: #374151; line-height: 1;\n }\n .ta-required { color: #ef4444; margin-left: 2px; }\n .ta-hint-inline { font-size: 0.75rem; color: #94a3b8; font-weight: 400; margin-left: 4px; }\n\n .ta-input {\n width: 100%; box-sizing: border-box;\n padding: 9px 12px; font-size: 0.875rem; font-family: inherit;\n color: #1e293b; background: #fff;\n border: 1px solid #e2e8f0; border-radius: 8px;\n outline: none; resize: vertical;\n transition: border-color 0.15s, box-shadow 0.15s;\n &::placeholder { color: #94a3b8; }\n &:focus {\n border-color: #3b82f6;\n box-shadow: 0 0 0 3px rgba(59, 130, 246, 0.12);\n }\n &:read-only { background: #f8fafc; cursor: default; }\n }\n\n .ta-error .ta-input {\n border-color: #ef4444;\n &:focus { box-shadow: 0 0 0 3px rgba(239, 68, 68, 0.12); }\n }\n\n .ta-disabled .ta-input { opacity: 0.5; cursor: not-allowed; }\n\n .ta-desc { margin: 0; font-size: 0.75rem; line-height: 1.4; color: #64748b; }\n .ta-desc-error { color: #ef4444; }\n\n @media (max-width: 480px) { .ta-input { font-size: 1rem; } }\n `],\n})\n/**\n * Multi-line text field (ControlValueAccessor).\n * Built-in validation: `required` -> \"<label> is required.\"; `maxlength` -> \"Too long (max N characters).\" (also for a\n * loaded value longer than the limit; trimmed length). Empty is valid unless `required`; disabled / readonly never invalid.\n *\n * Validity API (same on every ui input): the component is an Angular `Validator` (`ngModel` / `FormControl` report\n * `.invalid` / `.errors`), exposes the signals `invalid` + `errorMessage` and the `invalidChange` output, and provides\n * `UiFieldValidity` so a host can use `viewChildren(UiFieldValidity)`. Messages are displayed only after blur or while\n * `[showErrors]=\"true\"`; setting `showErrors` back to false clears the touched state. A host-provided `[error]` wins.\n */\nexport class TextareaInputComponent implements ControlValueAccessor, Validator, UiFieldValidity {\n readonly label = input('');\n /** Accessible name for a field with no visible label. */\n readonly ariaLabel = input('');\n readonly placeholder = input('');\n readonly rows = input(3);\n readonly maxlength = input<number | undefined>(undefined);\n readonly required = input(false);\n readonly readonly = input(false);\n readonly hint = input('');\n readonly error = input('');\n /** Reveal the built-in error messages now (e.g. after a failed Save click), without waiting for blur. */\n readonly showErrors = input(false);\n\n protected readonly _disabledInput = input(false, { alias: 'disabled' });\n private readonly _cvaDisabled = signal(false);\n protected readonly disabled = computed(() => this._disabledInput() || this._cvaDisabled());\n\n protected readonly _value = signal('');\n\n private readonly _reveal = createFieldReveal(this.showErrors);\n private readonly _issue = computed(() => this._check(this._value()));\n /** True while the value breaks a built-in rule or the host passed an `[error]`. */\n readonly invalid = computed(() => !!this.error() || !!this._issue());\n /** Message of the first broken built-in rule ('' when valid), shown or not. */\n readonly errorMessage = computed(() => this._issue()?.message ?? '');\n /** Emits whenever the field becomes invalid / valid. */\n readonly invalidChange = output<boolean>();\n protected readonly _activeError = computed(() => this.error() || (this._reveal.revealed() ? this.errorMessage() : ''));\n private _onValidatorChange = () => {};\n\n constructor() {\n effect(() => { this.invalidChange.emit(this.invalid()); });\n }\n\n /** Re-run the form control's validation when a rule input (required, maxlength, min, options, ...) changes. */\n ngOnChanges() { this._onValidatorChange(); }\n\n validate(control: AbstractControl): ValidationErrors | null {\n const v = control.value;\n return issueToErrors(this._check(v == null ? '' : String(v)));\n }\n registerOnValidatorChange(fn: () => void) { this._onValidatorChange = fn; }\n\n protected readonly _inputId = `ta-${Math.random().toString(36).slice(2, 8)}`;\n\n private _onChange = (_: string) => {};\n private _onTouched = () => {};\n\n writeValue(val: string | null) { this._value.set(val ?? ''); }\n registerOnChange(fn: (v: string) => void) { this._onChange = fn; }\n registerOnTouched(fn: () => void) { this._onTouched = fn; }\n setDisabledState(d: boolean) { this._cvaDisabled.set(d); }\n\n protected onInput(event: Event) {\n const val = (event.target as HTMLTextAreaElement).value;\n this._value.set(val);\n this._onChange(val);\n }\n\n protected onBlur() { this._onTouched(); this._reveal.touched.set(true); }\n\n private _check(value: string): FieldIssue | null {\n if (this.disabled() || this.readonly()) return null;\n const text = value.trim();\n if (!text) return this.required() ? requiredIssue(this.label()) : null;\n return maxLengthIssue(text, this.maxlength());\n }\n}\n","import { Component, ChangeDetectionStrategy, input, output, signal, computed, effect, forwardRef } from '@angular/core';\nimport { AbstractControl, ControlValueAccessor, NG_VALIDATORS, NG_VALUE_ACCESSOR, ValidationErrors, Validator } from '@angular/forms';\nimport { FieldIssue, UiFieldValidity, createFieldReveal, issueToErrors, requiredIssue, parseIso, isoKey, formatIsoDateTime } from '../utils/field-validation';\n\n@Component({\n selector: 'ui-datetime-input',\n standalone: true,\n changeDetection: ChangeDetectionStrategy.OnPush,\n providers: [{\n provide: NG_VALUE_ACCESSOR,\n useExisting: forwardRef(() => DatetimeInputComponent),\n multi: true,\n }, {\n provide: NG_VALIDATORS,\n useExisting: forwardRef(() => DatetimeInputComponent),\n multi: true,\n }, {\n provide: UiFieldValidity,\n useExisting: forwardRef(() => DatetimeInputComponent),\n }],\n template: `\n <div class=\"dti-field\" [class.dti-error]=\"_activeError()\" [class.dti-disabled]=\"disabled()\">\n @if (label()) {\n <label class=\"dti-label\" [for]=\"_inputId\">\n {{ label() }}\n @if (required()) { <span class=\"dti-required\" aria-hidden=\"true\">*</span> }\n </label>\n }\n <input\n class=\"dti-input\"\n type=\"datetime-local\"\n [id]=\"_inputId\"\n [value]=\"_value()\"\n [disabled]=\"disabled()\"\n [attr.min]=\"min() || null\"\n [attr.max]=\"max() || null\"\n [attr.required]=\"required() || null\"\n [attr.aria-invalid]=\"_activeError() ? 'true' : null\"\n [attr.aria-describedby]=\"(_activeError() || hint()) ? _inputId + '-desc' : null\"\n (change)=\"onChange($event)\"\n (blur)=\"onBlur()\"\n />\n @if (_activeError()) {\n <p class=\"dti-desc dti-desc-error\" [id]=\"_inputId + '-desc'\" role=\"alert\">{{ _activeError() }}</p>\n } @else if (hint()) {\n <p class=\"dti-desc\" [id]=\"_inputId + '-desc'\">{{ hint() }}</p>\n }\n </div>\n `,\n styles: [`\n :host { display: block; }\n\n .dti-field { display: flex; flex-direction: column; gap: 6px; }\n\n .dti-label {\n font-size: 0.8125rem; font-weight: 600; color: #374151; line-height: 1;\n }\n .dti-required { color: #ef4444; margin-left: 2px; }\n\n .dti-input {\n width: 100%; box-sizing: border-box;\n padding: 8px 12px; font-size: 0.875rem; font-family: inherit;\n color: #1e293b; background: #fff;\n border: 1px solid #e2e8f0; border-radius: 8px;\n outline: none; transition: border-color 0.15s, box-shadow 0.15s;\n appearance: none;\n &:focus {\n border-color: #3b82f6;\n box-shadow: 0 0 0 3px rgba(59, 130, 246, 0.12);\n }\n }\n\n .dti-error .dti-input {\n border-color: #ef4444;\n &:focus { box-shadow: 0 0 0 3px rgba(239, 68, 68, 0.12); }\n }\n\n .dti-disabled .dti-input { opacity: 0.5; cursor: not-allowed; }\n\n .dti-desc { margin: 0; font-size: 0.75rem; line-height: 1.4; color: #64748b; }\n .dti-desc-error { color: #ef4444; }\n\n @media (max-width: 480px) { .dti-input { font-size: 1rem; } }\n @media (pointer: coarse), (max-width: 480px) { .dti-input { min-height: 44px; } }\n `],\n})\n/**\n * Native date-time picker (ControlValueAccessor, ISO string value).\n * Built-in validation: `required` -> \"<label> is required.\"; a value that is not a real date-time -> \"Please enter a valid date-time.\";\n * below `min` -> \"Must be on or after <date>\"; above `max` -> \"Must be on or before <date>\" (a `min`/`max` that cannot be\n * parsed is ignored). Disabled fields are never invalid.\n *\n * Validity API (same on every ui input): the component is an Angular `Validator` (`ngModel` / `FormControl` report\n * `.invalid` / `.errors`), exposes the signals `invalid` + `errorMessage` and the `invalidChange` output, and provides\n * `UiFieldValidity` so a host can use `viewChildren(UiFieldValidity)`. Messages are displayed only after blur or while\n * `[showErrors]=\"true\"`; setting `showErrors` back to false clears the touched state. A host-provided `[error]` wins.\n */\nexport class DatetimeInputComponent implements ControlValueAccessor, Validator, UiFieldValidity {\n readonly label = input('');\n readonly required = input(false);\n readonly min = input('');\n readonly max = input('');\n readonly hint = input('');\n readonly error = input('');\n /** Reveal the built-in error messages now (e.g. after a failed Save click), without waiting for blur. */\n readonly showErrors = input(false);\n\n protected readonly _disabledInput = input(false, { alias: 'disabled' });\n private readonly _cvaDisabled = signal(false);\n protected readonly disabled = computed(() => this._disabledInput() || this._cvaDisabled());\n\n protected readonly _value = signal('');\n\n private readonly _reveal = createFieldReveal(this.showErrors);\n private readonly _issue = computed(() => this._check(this._value()));\n /** True while the value breaks a built-in rule or the host passed an `[error]`. */\n readonly invalid = computed(() => !!this.error() || !!this._issue());\n /** Message of the first broken built-in rule ('' when valid), shown or not. */\n readonly errorMessage = computed(() => this._issue()?.message ?? '');\n /** Emits whenever the field becomes invalid / valid. */\n readonly invalidChange = output<boolean>();\n protected readonly _activeError = computed(() => this.error() || (this._reveal.revealed() ? this.errorMessage() : ''));\n private _onValidatorChange = () => {};\n\n constructor() {\n effect(() => { this.invalidChange.emit(this.invalid()); });\n }\n\n /** Re-run the form control's validation when a rule input (required, maxlength, min, options, ...) changes. */\n ngOnChanges() { this._onValidatorChange(); }\n\n validate(control: AbstractControl): ValidationErrors | null {\n const v = control.value;\n return issueToErrors(this._check(v == null ? '' : String(v)));\n }\n registerOnValidatorChange(fn: () => void) { this._onValidatorChange = fn; }\n\n protected readonly _inputId = `dti-${Math.random().toString(36).slice(2, 8)}`;\n\n private _onChange = (_: string) => {};\n private _onTouched = () => {};\n\n writeValue(val: string) { this._value.set(val ?? ''); }\n registerOnChange(fn: (v: string) => void) { this._onChange = fn; }\n registerOnTouched(fn: () => void) { this._onTouched = fn; }\n setDisabledState(d: boolean) { this._cvaDisabled.set(d); }\n\n protected onChange(event: Event) {\n const val = (event.target as HTMLInputElement).value;\n this._value.set(val);\n this._onChange(val);\n }\n\n protected onBlur() { this._onTouched(); this._reveal.touched.set(true); }\n\n private _check(value: string): FieldIssue | null {\n if (this.disabled()) return null;\n const text = value.trim();\n if (!text) return this.required() ? requiredIssue(this.label()) : null;\n const p = parseIso(text, true);\n if (!p) return { key: 'date', message: 'Please enter a valid date-time.' };\n const min = this.min() ? parseIso(this.min(), true) : null;\n if (min && isoKey(p) < isoKey(min)) return { key: 'min', message: `Must be on or after ${formatIsoDateTime(this.min())}`, value: { min: this.min(), actual: text } };\n const max = this.max() ? parseIso(this.max(), true) : null;\n if (max && isoKey(p) > isoKey(max)) return { key: 'max', message: `Must be on or before ${formatIsoDateTime(this.max())}`, value: { max: this.max(), actual: text } };\n return null;\n }\n}\n","import { Component, ChangeDetectionStrategy, input } from '@angular/core';\n\n@Component({\n selector: 'ui-form-section',\n standalone: true,\n changeDetection: ChangeDetectionStrategy.OnPush,\n template: `\n @if (divider()) { <div class=\"fs-divider\"></div> }\n @if (label()) { <p class=\"fs-label\">{{ label() }}</p> }\n <div class=\"fs-fields\"><ng-content /></div>\n `,\n styles: [`\n :host { display: block; }\n\n .fs-divider {\n height: 1px;\n background: #f1f5f9;\n margin: 8px -24px 16px;\n }\n\n .fs-label {\n font-size: 0.6875rem;\n font-weight: 700;\n letter-spacing: 0.06em;\n text-transform: uppercase;\n color: #94a3b8;\n margin: 0 0 12px;\n }\n\n .fs-fields {\n display: flex;\n flex-direction: column;\n gap: 14px;\n }\n `],\n})\nexport class FormSectionComponent {\n readonly label = input('');\n readonly divider = input(false);\n}\n","import {\n Component, input, output, signal, computed,\n ChangeDetectionStrategy, forwardRef,\n} from '@angular/core';\nimport { NG_VALUE_ACCESSOR, ControlValueAccessor } from '@angular/forms';\n\n/**\n * ui-chip — a toggleable pill button, optionally dismissible.\n *\n * Standalone (not in a form):\n * <ui-chip label=\"Condo\" [active]=\"selected\" size=\"sm\" (select)=\"toggle()\" />\n *\n * A non-dismissible chip is a toggle button: it exposes `aria-pressed=\"true|false\"` from its active state so screen\n * readers announce it as pressed / not pressed. A dismissible chip is a removable tag, not a toggle, so it carries no\n * `aria-pressed` (the ✕ button is named \"Remove <label>\").\n *\n * Dismissible (shows an ✕ button — parent removes the chip on dismiss):\n * <ui-chip label=\"Austin\" size=\"sm\" [dismissible]=\"true\" (dismiss)=\"removeCity('Austin')\" />\n *\n * Inside a template-driven form (boolean — true = active):\n * <ui-chip label=\"Condo\" size=\"sm\" [(ngModel)]=\"form.condo\" />\n *\n * Inside a reactive form:\n * <ui-chip label=\"Condo\" size=\"sm\" [formControl]=\"condoCtrl\" />\n */\n@Component({\n selector: 'ui-chip',\n standalone: true,\n changeDetection: ChangeDetectionStrategy.OnPush,\n providers: [{\n provide: NG_VALUE_ACCESSOR,\n useExisting: forwardRef(() => ChipComponent),\n multi: true,\n }],\n template: `\n <button\n type=\"button\"\n class=\"chip\"\n [class.chip--active]=\"_resolvedActive()\"\n [class.chip--sm]=\"size() === 'sm'\"\n [class.chip--lg]=\"size() === 'lg'\"\n [class.chip--dismissible]=\"dismissible()\"\n [attr.aria-pressed]=\"dismissible() ? null : (_resolvedActive() ? 'true' : 'false')\"\n [disabled]=\"_disabled()\"\n (click)=\"onClick()\"\n (blur)=\"_onTouched()\">\n {{ label() }}\n @if (dismissible()) {\n <span\n class=\"chip-dismiss\"\n role=\"button\"\n [attr.aria-label]=\"'Remove ' + label()\"\n (click)=\"onDismiss($event)\">\n <svg width=\"10\" height=\"10\" viewBox=\"0 0 24 24\" fill=\"none\"\n stroke=\"currentColor\" stroke-width=\"2.5\" aria-hidden=\"true\">\n <line x1=\"18\" y1=\"6\" x2=\"6\" y2=\"18\"/>\n <line x1=\"6\" y1=\"6\" x2=\"18\" y2=\"18\"/>\n </svg>\n </span>\n }\n </button>\n `,\n styles: [`\n :host { display: contents; }\n\n .chip {\n display: inline-flex; align-items: center; gap: 5px;\n border-radius: 999px; border: 1.5px solid #d1d5db;\n background: #f9fafb; color: #374151;\n font-weight: 500; font-family: inherit;\n cursor: pointer; user-select: none; white-space: nowrap;\n transition: background 0.15s, border-color 0.15s, color 0.15s;\n\n &:hover:not(:disabled) { border-color: #3b82f6; color: #3b82f6; background: #eff6ff; }\n &:focus-visible { outline: 2px solid #3b82f6; outline-offset: 2px; }\n &:disabled { opacity: 0.5; cursor: default; }\n }\n\n /* ── Sizes ─── */\n .chip--sm { padding: 2px 10px; font-size: 0.75rem; }\n .chip--lg { padding: 6px 16px; font-size: 0.875rem; }\n\n /* When dismissible the right padding tightens — the × button provides visual end space */\n .chip--sm.chip--dismissible { padding-right: 6px; }\n .chip--lg.chip--dismissible { padding-right: 10px; }\n\n /* ── Touch devices: >= 44px (sm: 40px) interactive height; desktop/mouse unchanged ─── */\n @media (pointer: coarse) {\n .chip { min-height: 44px; }\n .chip--sm { min-height: 40px; padding-top: 6px; padding-bottom: 6px; padding-left: 14px; padding-right: 14px; }\n .chip--lg { padding-left: 18px; padding-right: 18px; }\n .chip--sm.chip--dismissible { padding-right: 8px; }\n .chip--lg.chip--dismissible { padding-right: 12px; }\n .chip-dismiss { width: 24px; height: 24px; }\n }\n\n /* ── Active state ─── */\n .chip--active {\n background: #3b82f6; border-color: #3b82f6; color: #fff;\n &:hover:not(:disabled) { background: #2563eb; border-color: #2563eb; color: #fff; }\n }\n\n /* ── Dismiss button ─── */\n .chip-dismiss {\n display: inline-flex; align-items: center; justify-content: center;\n width: 14px; height: 14px; border-radius: 50%;\n background: rgba(0,0,0,0.12); flex-shrink: 0;\n transition: background 0.15s;\n &:hover { background: rgba(0,0,0,0.25); }\n }\n .chip--active .chip-dismiss {\n background: rgba(255,255,255,0.3);\n &:hover { background: rgba(255,255,255,0.5); }\n }\n `],\n})\nexport class ChipComponent implements ControlValueAccessor {\n readonly label = input.required<string>();\n readonly size = input<'sm' | 'lg'>('lg');\n /**\n * When true the chip shows an ✕ button. Clicking it emits (dismiss) and\n * does NOT toggle the chip — the parent is responsible for removing it.\n */\n readonly dismissible = input(false);\n\n /**\n * Standalone active state — used when not bound to a form.\n * When bound via ngModel/formControl the CVA value takes precedence.\n */\n readonly active = input(false);\n\n /** Emitted on chip body click (toggle behaviour). */\n readonly select = output<void>();\n /** Emitted when the ✕ dismiss button is clicked. */\n readonly dismiss = output<void>();\n\n // ── CVA internal state ─────────────────────────────────────────────────────\n // null = not yet bound to a form; non-null = CVA owns the state\n private readonly _cvActive = signal<boolean | null>(null);\n protected readonly _disabled = signal(false);\n\n /**\n * Single source of truth for the active visual state:\n * - CVA value wins when the chip is form-bound (writeValue has been called)\n * - Falls back to the [active] input for standalone usage\n */\n protected readonly _resolvedActive = computed(() => {\n const cv = this._cvActive();\n return cv !== null ? cv : this.active();\n });\n\n private _onChange: (v: boolean) => void = () => {};\n protected _onTouched: () => void = () => {};\n\n protected onClick() {\n if (this._disabled()) return;\n const cv = this._cvActive();\n if (cv !== null) {\n const next = !cv;\n this._cvActive.set(next);\n this._onChange(next);\n }\n this.select.emit();\n this._onTouched();\n }\n\n protected onDismiss(event: Event) {\n event.stopPropagation(); // don't also trigger onClick / toggle\n if (this._disabled()) return;\n this.dismiss.emit();\n }\n\n // ── ControlValueAccessor ───────────────────────────────────────────────────\n writeValue(v: boolean) { this._cvActive.set(!!v); }\n registerOnChange(fn: (v: boolean) => void) { this._onChange = fn; }\n registerOnTouched(fn: () => void) { this._onTouched = fn; }\n setDisabledState(disabled: boolean) { this._disabled.set(disabled); }\n}\n","import { Component, input, computed, ChangeDetectionStrategy } from '@angular/core';\n\nconst GAP = { sm: '6px', md: '8px', lg: '12px' } as const;\nconst SPACE = { none: '0', sm: '6px', md: '12px' } as const;\n\n/**\n * ui-chip-group — wraps ui-chip elements with configurable gap and spacing.\n *\n * <ui-chip-group gap=\"md\" spaceAbove=\"sm\" spaceBelow=\"none\">\n * <ui-chip label=\"Condo\" size=\"sm\" [active]=\"...\" (select)=\"...\" />\n * <ui-chip label=\"Single Family\" size=\"sm\" [active]=\"...\" (select)=\"...\" />\n * </ui-chip-group>\n */\n@Component({\n selector: 'ui-chip-group',\n standalone: true,\n changeDetection: ChangeDetectionStrategy.OnPush,\n template: `\n <div\n class=\"chip-group\"\n [class.chip-group--tight]=\"gap() === 'sm'\"\n [style.gap]=\"_gap()\"\n [style.margin-top]=\"_spaceAbove()\"\n [style.margin-bottom]=\"_spaceBelow()\">\n <ng-content />\n </div>\n `,\n styles: [`\n :host { display: block; }\n .chip-group { display: flex; flex-wrap: wrap; }\n /* Touch: chips are taller, so never let them sit closer than 8px (inline gap would otherwise win). */\n @media (pointer: coarse) { .chip-group--tight { gap: 8px !important; } }\n `],\n})\nexport class ChipGroupComponent {\n /** Space between each chip. */\n readonly gap = input<'sm' | 'md' | 'lg'>('md');\n /** Space above the chip group. */\n readonly spaceAbove = input<'none' | 'sm' | 'md'>('sm');\n /** Space below the chip group. */\n readonly spaceBelow = input<'none' | 'sm' | 'md'>('md');\n\n protected readonly _gap = computed(() => GAP[this.gap()]);\n protected readonly _spaceAbove = computed(() => SPACE[this.spaceAbove()]);\n protected readonly _spaceBelow = computed(() => SPACE[this.spaceBelow()]);\n}\n","import {\n Component, ChangeDetectionStrategy, DestroyRef, computed, effect, forwardRef, inject, input, output, signal, untracked, viewChild,\n} from '@angular/core';\nimport { FormsModule } from '@angular/forms';\nimport { HttpClient } from '@angular/common/http';\nimport { takeUntilDestroyed } from '@angular/core/rxjs-interop';\nimport { Subject, catchError, of, switchMap } from 'rxjs';\nimport { FORGEROUTE_CONFIG } from '../config/forgeroute.config';\nimport { ComboboxComponent } from '../combobox/combobox.component';\nimport type { ComboboxOption } from '../combobox/combobox.component';\nimport { ChipComponent } from '../chip/chip.component';\nimport { ChipGroupComponent } from '../chip/chip-group.component';\nimport { SelectComponent } from '../select/select.component';\nimport type { SelectOption } from '../select/select.component';\nimport { ErrorComponent } from '../error/error.component';\nimport { UiFieldValidity } from '../utils/field-validation';\n\n/** What the sender wants done with the chosen documents. Same values as the backend (`ESIGN` | `ATTACH` | `BOTH`). */\nexport type DocumentSendPurpose = 'ESIGN' | 'ATTACH' | 'BOTH';\n/** How the request reaches the recipient — derived from the channel: the Email tab sends `EMAIL`, the Text tab `TEXT`. */\nexport type DocumentSendDelivery = 'EMAIL' | 'TEXT';\n\n/** One document from `GET <baseUrl>/documents`, reduced to what the picker needs. */\nexport interface DocumentOption {\n id: string;\n title: string;\n fileName: string | null;\n /** True when the document has a stored file (so it can be attached to an email). */\n hasFile: boolean;\n}\n\n/** The value `ui-document-send-options` emits and the message forms turn into `SendEmailPayload.documents` / `SendTextPayload.documents`. */\nexport interface DocumentSendValue {\n documentIds: string[];\n documents: DocumentOption[];\n purpose: DocumentSendPurpose;\n delivery: DocumentSendDelivery;\n}\n\n/** Request page size of the document search. */\nconst PAGE_SIZE = 20;\n\nexport const DOCUMENT_PURPOSE_OPTIONS: SelectOption[] = [\n { value: 'ESIGN', label: 'Require e-signature' },\n { value: 'ATTACH', label: 'Send as attachment' },\n { value: 'BOTH', label: 'E-signature and attachment' },\n];\n\n/**\n * ui-document-send-options — choose the documents that go out with an email or a text.\n *\n * Search-as-you-type over `GET <baseUrl>/documents?search=&size=20` (nothing is requested before 2 characters, the combobox\n * debounces 250 ms, a newer search cancels an older one), the chosen documents shown as removable chips, and — on the email channel — a\n * \"What should the recipient do?\" select (e-signature / attachment / both). On the text channel documents can only go as a\n * link, so the purpose is fixed to e-signature and the select is not shown.\n *\n * It is embedded by `ui-send-message-panel` (`[documents]=\"true\"`); it can also be used on its own.\n *\n * ```html\n * <ui-document-send-options channel=\"email\" [showErrors]=\"touched()\" (valueChange)=\"docs.set($event)\" (invalidChange)=\"docsInvalid.set($event)\" />\n * ```\n *\n * Validity: provides `UiFieldValidity` (`invalid` / `errorMessage`) and the `invalidChange` output. Invalid when `required`\n * and nothing is chosen, or when the purpose is \"Send as attachment\" and a chosen document has no stored file. Messages\n * show only while `showErrors` is true.\n */\n@Component({\n selector: 'ui-document-send-options',\n standalone: true,\n changeDetection: ChangeDetectionStrategy.OnPush,\n imports: [FormsModule, ComboboxComponent, ChipComponent, ChipGroupComponent, SelectComponent, ErrorComponent],\n providers: [{ provide: UiFieldValidity, useExisting: forwardRef(() => DocumentSendOptionsComponent) }],\n template: `\n <div class=\"dso\" role=\"group\" [attr.aria-label]=\"label()\">\n @if (available()) {\n <ui-combobox\n [label]=\"label()\"\n [required]=\"required()\"\n placeholder=\"Search your documents by name…\"\n [options]=\"options()\"\n [loading]=\"loading()\"\n [minChars]=\"2\"\n [noResultsText]=\"searchFailed() ? 'The search failed' : 'No matching documents'\"\n [hint]=\"channel() === 'text' ? 'Documents are texted as a link to the signing page.' : ''\"\n [error]=\"searchFailed() ? searchFailedMessage : ''\"\n [showErrors]=\"searchFailed()\"\n (searchChange)=\"search$.next($event)\"\n (valueChange)=\"onPick($event)\" />\n <ui-error errorId=\"dso-search-error\" [message]=\"searchFailed() ? 'Couldn\\\\'t search your documents. Check your connection and try again.' : ''\" />\n\n @if (selected().length > 0) {\n <ui-chip-group class=\"dso-chips\" gap=\"sm\" spaceAbove=\"sm\" spaceBelow=\"none\">\n @for (d of selected(); track d.id) {\n <ui-chip [label]=\"d.title\" size=\"sm\" [dismissible]=\"true\" (dismiss)=\"remove(d.id)\" />\n }\n </ui-chip-group>\n }\n\n @if (channel() === 'email' && selected().length > 0 && purposeOptions().length > 1) {\n <ui-select\n label=\"What should the recipient do?\"\n [options]=\"purposeOptions()\"\n [ngModel]=\"purpose()\"\n (ngModelChange)=\"setPurpose($event)\" />\n }\n\n <ui-error errorId=\"dso-error\" [message]=\"showErrors() ? errorMessage() : ''\" />\n } @else {\n <p class=\"dso-unavailable\" role=\"note\">Documents can only be texted as an e-signature link, which isn't available here.</p>\n }\n </div>\n `,\n styles: [`\n :host { display: block; }\n .dso { display: flex; flex-direction: column; gap: 10px; min-width: 0; }\n .dso-unavailable { margin: 0; font-size: 0.8125rem; color: #64748b; }\n /* Long document titles wrap inside the chip instead of widening the form on a phone; the whole chip is the dismiss\n target, so keep it at least 24px tall on desktop too (the chip itself is 40-44px on touch devices). */\n .dso-chips { max-width: 100%; min-width: 0; }\n :host ::ng-deep .dso-chips .chip { max-width: 100%; min-height: 24px; white-space: normal; text-align: left; overflow-wrap: anywhere; }\n `],\n})\nexport class DocumentSendOptionsComponent implements UiFieldValidity {\n private readonly http = inject(HttpClient);\n private readonly destroyRef = inject(DestroyRef);\n /** API root from FORGEROUTE_CONFIG (e.g. a cross-origin prod API); falls back to same-origin `/api`. */\n private readonly apiBase = inject(FORGEROUTE_CONFIG, { optional: true })?.baseUrl ?? '/api';\n\n /** `email` → purpose select + attachment allowed; `text` → link only (purpose fixed to e-signature). */\n readonly channel = input<'email' | 'text'>('email');\n readonly label = input('Documents');\n /** When true, at least one document must be chosen. The message forms leave it false: documents are optional there. */\n readonly required = input(false);\n /** Shows the validation message (bind it to the host's \"tried to submit\" flag). */\n readonly showErrors = input(false);\n\n readonly valueChange = output<DocumentSendValue>();\n readonly invalidChange = output<boolean>();\n\n /**\n * Which purposes the app's backend supports (default: all three). With a single purpose the select is hidden and that\n * purpose is always sent — e.g. `['ESIGN']` for an app that only does e-signature.\n */\n readonly purposes = input<DocumentSendPurpose[]>(['ESIGN', 'ATTACH', 'BOTH']);\n /**\n * Documents already chosen when the form opens (e.g. the row the user clicked \"Send Document\" on): shown as chips and part\n * of the value straight away; the user can remove them or add more. Read once — later changes to the input are ignored.\n * `hasFile` defaults to true (an attachment check then relies on the server's answer).\n */\n readonly initialDocuments = input<{ id: string; title: string; fileName?: string | null; hasFile?: boolean }[]>([]);\n protected readonly purposeOptions = computed(() => {\n const allowed = this.purposes();\n const opts = DOCUMENT_PURPOSE_OPTIONS.filter(o => allowed.includes(o.value as DocumentSendPurpose));\n return opts.length ? opts : DOCUMENT_PURPOSE_OPTIONS;\n });\n readonly search$ = new Subject<string>();\n\n readonly options = signal<ComboboxOption<DocumentOption>[]>([]);\n readonly loading = signal(false);\n readonly selected = signal<DocumentOption[]>([]);\n readonly purposeChoice = signal<DocumentSendPurpose>('ESIGN');\n /** The search box; cleared (and kept focused) after each pick so the next search starts empty. */\n private readonly combo = viewChild(ComboboxComponent);\n /** True when the last search request failed (shown inline instead of \"No matching documents\"). */\n readonly searchFailed = signal(false);\n /** Shown through the combobox's own `error` input, so it is announced and tied to the search field natively (aria-describedby on the inner input). */\n protected readonly searchFailedMessage = \"Couldn't search your documents. Check your connection and try again.\";\n /**\n * False when the section can't be used: the text channel only sends an e-signature link, so an app whose `purposes` excludes\n * `ESIGN` has nothing to offer there. The section then shows a short note instead of a picker (it never silently sends another purpose).\n */\n readonly available = computed(() => this.channel() !== 'text' || this.purposes().includes('ESIGN'));\n\n /** The purpose that is actually sent: the text channel can only send a link, so it is always e-signature. */\n readonly purpose = computed<DocumentSendPurpose>(() => {\n if (this.channel() === 'text') return 'ESIGN';\n const opts = this.purposeOptions().map(o => o.value as DocumentSendPurpose);\n return opts.includes(this.purposeChoice()) ? this.purposeChoice() : opts[0];\n });\n readonly delivery = computed<DocumentSendDelivery>(() => this.channel() === 'text' ? 'TEXT' : 'EMAIL');\n\n readonly value = computed<DocumentSendValue>(() => ({\n documentIds: this.selected().map(d => d.id),\n documents: this.selected(),\n purpose: this.purpose(),\n delivery: this.delivery(),\n }));\n\n /** The first broken rule ('' when valid). */\n readonly errorMessage = computed(() => {\n const docs = this.selected();\n if (this.required() && docs.length === 0) return 'Select at least one document.';\n if (this.purpose() === 'ATTACH') {\n const noFile = docs.find(d => !d.hasFile);\n if (noFile) return `\"${noFile.title}\" has no file to attach — choose e-signature or remove it.`;\n }\n return '';\n });\n readonly invalid = computed(() => this.errorMessage() !== '');\n\n constructor() {\n this.search$.pipe(\n switchMap(q => {\n const term = q.trim();\n if (term.length < 2) { this.loading.set(false); this.searchFailed.set(false); return of(null); }\n this.loading.set(true);\n this.searchFailed.set(false);\n return this.http.get<{ content?: any[] }>(`${this.apiBase}/documents`, { params: { search: term, size: String(PAGE_SIZE) } }).pipe(\n catchError(() => { this.searchFailed.set(true); return of({ content: [] }); }),\n );\n }),\n takeUntilDestroyed(this.destroyRef),\n ).subscribe(res => {\n this.loading.set(false);\n if (res === null) { this.options.set([]); return; }\n const chosen = new Set(this.selected().map(d => d.id));\n this.options.set((res.content ?? [])\n // An archived document cannot be sent, and one that is already chosen is not offered twice.\n .filter(d => d && d.status !== 'ARCHIVED' && !chosen.has(String(d.id)))\n .map(d => {\n const doc = DocumentSendOptionsComponent.toOption(d);\n return { value: doc.id, label: doc.title, description: doc.fileName ?? 'No file uploaded', data: doc };\n }));\n });\n\n let seeded = false;\n effect(() => {\n const initial = this.initialDocuments();\n untracked(() => {\n if (seeded || initial.length === 0) return;\n seeded = true;\n this.selected.set(initial.map(d => ({ id: String(d.id), title: d.title, fileName: d.fileName ?? null, hasFile: d.hasFile ?? true })));\n });\n });\n\n // Nothing is emitted for the untouched initial state (empty, valid) — hosts start from that state themselves.\n let firstValue = true;\n effect(() => {\n const v = this.value();\n untracked(() => {\n if (firstValue) { firstValue = false; if (v.documentIds.length === 0) return; }\n this.valueChange.emit(v);\n });\n });\n let firstValidity = true;\n effect(() => {\n const bad = this.invalid();\n untracked(() => {\n if (firstValidity) { firstValidity = false; if (!bad) return; }\n this.invalidChange.emit(bad);\n });\n });\n }\n\n private static toOption(d: any): DocumentOption {\n return {\n id: String(d.id),\n title: String(d.title ?? d.fileName ?? 'Untitled document'),\n fileName: d.fileName ?? null,\n hasFile: Number(d.fileSize ?? 0) > 0 || !!d.fileName,\n };\n }\n\n onPick(opt: ComboboxOption<DocumentOption> | null) {\n const doc = opt?.data;\n if (!doc || !this.available()) return;\n if (!this.selected().some(d => d.id === doc.id)) this.selected.update(list => [...list, doc]);\n this.options.set([]);\n // Empty the search box for the next search and leave focus in it (the combobox's own clear refocuses the field).\n this.combo()?.clear();\n }\n\n remove(id: string) { this.selected.update(list => list.filter(d => d.id !== id)); }\n\n setPurpose(p: string) {\n if ((p === 'ESIGN' || p === 'ATTACH' || p === 'BOTH') && this.purposeOptions().some(o => o.value === p)) this.purposeChoice.set(p);\n }\n\n /** Empties the selection (the host calls it after a successful send). */\n reset() { this.selected.set([]); this.purposeChoice.set('ESIGN'); this.options.set([]); this.searchFailed.set(false); }\n}\n","import { switchMap } from 'rxjs';\nimport type { Observable } from 'rxjs';\nimport type { HttpClient } from '@angular/common/http';\nimport type { DocumentSendDelivery, DocumentSendPurpose } from '../document-send-options/document-send-options.component';\n\n/** The `documents` key of `SendEmailPayload` / `SendTextPayload`: present only when at least one document was chosen. */\nexport interface SendDocumentsPayload {\n documentIds: string[];\n /** `ESIGN` | `ATTACH` | `BOTH` (always `ESIGN` for texts). */\n purpose: DocumentSendPurpose;\n /** `EMAIL` for the email form, `TEXT` for the text form. */\n delivery: DocumentSendDelivery;\n}\n\n/**\n * Subject of the signature request when the sender has none (texts have no subject). The server treats exactly this\n * text as a placeholder and builds the real subject from the document names.\n */\nexport const DEFAULT_DOCUMENT_SUBJECT = 'Document Signature Required';\n\n/**\n * The subject the library suggests once documents are chosen on the email form — the same text the servers build when the\n * sender leaves the subject out: signing (ESIGN / BOTH) → \"Signature required: <title>\" (several: \"<N> documents to sign: <first>…\");\n * attachment only → \"Document attached: <title>\" (several: \"<N> documents attached: <first>…\"). '' for no documents.\n */\nexport function suggestDocumentSubject(titles: string[], purpose: DocumentSendPurpose): string {\n if (titles.length === 0) return '';\n const attachOnly = purpose === 'ATTACH';\n if (titles.length === 1) return `${attachOnly ? 'Document attached' : 'Signature required'}: ${titles[0]}`;\n return `${titles.length} documents ${attachOnly ? 'attached' : 'to sign'}: ${titles[0]}…`;\n}\n\n/** Most people that can be copied on one email (same limit as the server). */\nexport const MAX_CC = 10;\n\n/** CC addresses typed as text: comma / semicolon / whitespace separated, trimmed, de-duplicated (case-insensitive). */\nexport function parseCcList(raw: string): string[] {\n const seen = new Set<string>();\n const out: string[] = [];\n for (const part of (raw ?? '').split(/[,;\\s]+/)) {\n const email = part.trim();\n if (!email || seen.has(email.toLowerCase())) continue;\n seen.add(email.toLowerCase());\n out.push(email);\n }\n return out;\n}\n\n/** One address: no spaces, no second `@`, and none of `, ; < > \"` anywhere (they would split a list or break out of a header). Same rule as the servers. */\nconst CC_EMAIL_RE = /^[^\\s@,;<>\"]+@[^\\s@,;<>\"]+\\.[^\\s@,;<>\"]+$/;\n\n/** The first problem with a CC list, '' when fine — the same rules the server enforces (valid addresses, at most 10, fits the column). */\nexport function ccError(list: string[]): string {\n const bad = list.find(a => a.length > 254 || !CC_EMAIL_RE.test(a));\n if (bad) return `\"${bad}\" isn't a valid CC email address.`;\n if (list.length > MAX_CC) return `You can copy at most ${MAX_CC} people.`;\n if (list.join(',').length > 1000) return 'The CC list is too long.';\n return '';\n}\n\n/** What the library sends to create a document request (`POST <baseUrl>/documents/envelopes`). See docs/send-documents-contract.md. */\nexport interface CreateDocumentRequestBody {\n subject: string;\n message: string;\n customerId?: string;\n customerName: string;\n customerEmail?: string;\n customerPhone?: string;\n documentIds: string[];\n purpose: DocumentSendPurpose;\n delivery: DocumentSendDelivery;\n ccEmails?: string[];\n}\n\n/**\n * The default way a message form sends documents: create the request, then send it.\n * 1. POST <baseUrl>/documents/envelopes → 201 { id, ... }\n * 2. POST <baseUrl>/documents/envelopes/{id}/send → 200 (body { templateId } only when the message came from a template)\n * A failure in step 2 leaves the created request as a draft on the server; the error is surfaced like any send error.\n */\nexport function sendDocumentRequest(\n http: HttpClient, apiBase: string, body: CreateDocumentRequestBody, templateId?: string,\n): Observable<unknown> {\n return http.post<{ id: string }>(`${apiBase}/documents/envelopes`, body).pipe(\n switchMap(envelope => http.post(`${apiBase}/documents/envelopes/${envelope.id}/send`, templateId ? { templateId } : {})),\n );\n}\n","/**\n * Merge-tag helpers shared by the email and text forms, so the Preview pane, the \"View Send Preview\" modal and the\n * sent payload all come from the same values.\n *\n * Rules:\n * - A tag is `{{key}}` (inner spaces allowed: `{{ key }}`); `key` is letters, digits, `_` and `.`.\n * - A value counts only when it is non-blank: a blank contact field, market value or `context` entry never\n * resolves a tag — the form asks for it instead.\n * - Anything still shaped like `{{…}}` after merging blocks sending (the server answers such a body with a 400).\n */\n\nconst TAG_RE = /\\{\\{\\s*([\\w.]+)\\s*\\}\\}/g;\nconst ANY_TAG_RE = /\\{\\{[^{}]*\\}\\}/;\n\n/** Every distinct tag key in `text`, in order of first appearance. */\nexport function mergeTagKeys(text: string): string[] {\n return [...new Set([...(text ?? '').matchAll(TAG_RE)].map(m => m[1]))];\n}\n\n/** Replaces each tag that has a non-blank value in `values`; other tags are left as written. */\nexport function applyMergeTags(text: string, values: Record<string, string>): string {\n return (text ?? '').replace(TAG_RE, (whole, k: string) => {\n const v = values[k];\n return v != null && String(v).trim() !== '' ? String(v) : whole;\n });\n}\n\n/** True while `text` still contains anything shaped like `{{…}}`. */\nexport function hasLeftoverTags(text: string): boolean { return ANY_TAG_RE.test(text ?? ''); }\n\n/** Copies only the non-blank entries of each source, later sources winning. */\nexport function nonBlankValues(...sources: Record<string, unknown>[]): Record<string, string> {\n const out: Record<string, string> = {};\n for (const src of sources) {\n for (const [k, v] of Object.entries(src ?? {})) {\n if (v != null && String(v).trim() !== '') out[k] = String(v);\n }\n }\n return out;\n}\n\n/** The current date variables (`month`, `year`, `date`, `today`). */\nexport function dateVars(now = new Date()): Record<string, string> {\n const long = now.toLocaleDateString('en-US', { month: 'long', day: 'numeric', year: 'numeric' });\n return {\n month: now.toLocaleString('en-US', { month: 'long' }),\n year: now.getFullYear().toString(),\n date: long, today: long,\n };\n}\n\n/**\n * Market variables from the `/service-areas` page: the primary area (or the first one). Only values the server\n * actually returned are kept — with no areas (or a blank value) the tag stays a prompted field.\n */\nexport function marketVarsFrom(resp: { content?: any[] } | null | undefined): Record<string, string> {\n const areas: any[] = resp?.content ?? [];\n const primary = areas.find((a: any) => a?.isPrimary) ?? areas[0];\n if (!primary) return {};\n return nonBlankValues({\n area: primary.area, median_price: primary.medianPrice, dom: primary.dom, new_listings: primary.newListings,\n });\n}\n\n/** Turns a key like `job_name` into the missing-field label \"Job Name\". */\nexport function formatVarLabel(key: string): string {\n return key.replace(/[_.]/g, ' ').replace(/\\b\\w/g, c => c.toUpperCase());\n}\n","import {\n Component, input, output, effect, untracked, OnInit,\n inject, ChangeDetectionStrategy, ChangeDetectorRef, DestroyRef, signal,\n} from '@angular/core';\nimport { FormsModule } from '@angular/forms';\nimport { HttpClient } from '@angular/common/http';\nimport type { Observable } from 'rxjs';\nimport { FORGEROUTE_CONFIG } from '../config/forgeroute.config';\nimport { getErrorMessage } from '../error-handler';\nimport { takeUntilDestroyed } from '@angular/core/rxjs-interop';\nimport { ButtonComponent } from '../button/button.component';\nimport { CheckboxComponent } from '../checkbox/checkbox.component';\nimport { PillTabsComponent } from '../pill-tabs/pill-tabs.component';\nimport type { PillTab } from '../pill-tabs/pill-tabs.component';\nimport { TemplatePickerComponent } from '../template-picker/template-picker.component';\nimport type { TemplateItem } from '../template-picker/template-picker.component';\nimport { VideoPickerComponent } from '../video-picker/video-picker.component';\nimport type { VideoMessage } from '../video-picker/video-picker.component';\nimport { ToastService } from '../toast/toast.service';\nimport { TextInputComponent } from '../text-input/text-input.component';\nimport { TextareaInputComponent } from '../textarea-input/textarea-input.component';\nimport { DatetimeInputComponent } from '../datetime-input/datetime-input.component';\nimport { FormSectionComponent } from '../form-section/form-section.component';\nimport { DocumentSendOptionsComponent } from '../document-send-options/document-send-options.component';\nimport type { DocumentSendValue, DocumentSendPurpose } from '../document-send-options/document-send-options.component';\nimport { DEFAULT_DOCUMENT_SUBJECT, ccError as ccListError, parseCcList, sendDocumentRequest, suggestDocumentSubject } from './send-documents';\nimport type { SendDocumentsPayload } from './send-documents';\nimport {\n applyMergeTags, dateVars, formatVarLabel, hasLeftoverTags, marketVarsFrom, mergeTagKeys, nonBlankValues,\n} from './merge-tags';\n\nconst EMAIL_RE = /^[^\\s@]+@[^\\s@]+\\.[^\\s@]+$/;\n\n/** What the email form sends — the default `POST <baseUrl>/email-messages` body, or the host's `sendFn` argument. */\nexport interface SendEmailPayload {\n contactName: string;\n contactEmail: string;\n subject: string;\n body: string;\n contactId?: string;\n scheduledAt?: string;\n attachments?: { filename: string; content: string; contentType: string }[];\n /** Only present when the host enabled `showReceiptOption`: whether the sender ticked the receipt checkbox. */\n includeReceipt?: boolean;\n /** Only present when the email was sent from a saved template (still set after editing its text); lets the server count the template use. */\n templateId?: string;\n /** Only present when the host enabled `showCc` and the sender typed addresses: the people copied on the email (valid, deduplicated, at most 10). */\n cc?: string[];\n /** Only present when the host enabled `documents` and the sender chose at least one document (then `attachments` and `scheduledAt` are never set). */\n documents?: SendDocumentsPayload;\n}\n\n/**\n * HTML for the Preview pane, the \"View Send Preview\" modal AND the sent email body — the server converts the sent\n * body with the same rules (contractor `messagePreviewHtml` in EmailTemplateService.kt), so what is previewed is\n * exactly what is sent:\n * - empty → ''\n * - a body containing an HTML tag (`/<[a-z][\\s\\S]*>/i`) is HTML and is used untouched\n * - otherwise it is plain text: `&`, `<`, `>`, `\"` and `'` are escaped (`&` `<` `>` `"` `'`)\n * and every line break (`\\r\\n`, `\\r`, `\\n`) becomes `<br>`.\n */\nexport function messagePreviewHtml(body: string): string {\n if (!body) return '';\n if (/<[a-z][\\s\\S]*>/i.test(body)) return body;\n return body\n .replace(/&/g, '&').replace(/</g, '<').replace(/>/g, '>')\n .replace(/\"/g, '"').replace(/'/g, ''')\n .replace(/\\r\\n|\\r|\\n/g, '<br>');\n}\n\n@Component({\n selector: 'ui-send-email',\n standalone: true,\n changeDetection: ChangeDetectionStrategy.OnPush,\n imports: [FormsModule, ButtonComponent, CheckboxComponent, PillTabsComponent, TemplatePickerComponent, VideoPickerComponent, TextInputComponent, TextareaInputComponent, DatetimeInputComponent, FormSectionComponent, DocumentSendOptionsComponent],\n template: `\n <!-- Missing email guard -->\n @if (!contactEmail()) {\n <div class=\"sm-missing-banner\">\n <svg width=\"16\" height=\"16\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\" aria-hidden=\"true\"><circle cx=\"12\" cy=\"12\" r=\"10\"/><line x1=\"12\" y1=\"8\" x2=\"12\" y2=\"12\"/><line x1=\"12\" y1=\"16\" x2=\"12.01\" y2=\"16\"/></svg>\n No email address on file for this contact.\n </div>\n <ui-text-input\n label=\"Add Email Address\"\n type=\"email\"\n [required]=\"true\"\n placeholder=\"email@example.com\"\n [(ngModel)]=\"pendingEmail\"\n (ngModelChange)=\"notifyValid()\"\n [error]=\"pendingEmail && !pendingEmailValid ? 'Please enter a valid email address.' : ''\" />\n <ui-button variant=\"primary\" [disabled]=\"!pendingEmailValid || savingEmail()\" [loading]=\"savingEmail()\" (click)=\"saveEmail()\">\n Save & Continue\n </ui-button>\n } @else {\n <!-- To -->\n <div class=\"sm-group\">\n <span class=\"sm-group-label\">To</span>\n <div class=\"sm-to-card\">\n <span class=\"sm-to-name\">{{ contactName() }}</span>\n <span class=\"sm-to-detail\">{{ contactEmail() }}</span>\n </div>\n </div>\n\n @if (showCc()) {\n <ui-text-input\n label=\"CC\"\n placeholder=\"name@example.com, other@example.com\"\n hint=\"Separate addresses with commas. Everyone listed receives a copy.\"\n [(ngModel)]=\"ccText\"\n [error]=\"ccError\"\n (ngModelChange)=\"notifyValid()\" />\n }\n\n <!-- Mode -->\n <div class=\"sm-mode-row\">\n <ui-pill-tabs [tabs]=\"modeTabs\" [activeKey]=\"mode\" (tabChange)=\"setMode($event)\" />\n </div>\n\n @if (mode === 'compose') {\n <ui-text-input\n label=\"Subject\"\n [required]=\"true\"\n placeholder=\"Email subject...\"\n [(ngModel)]=\"subject\"\n (ngModelChange)=\"notifyValid()\" />\n <ui-textarea-input\n label=\"Message\"\n [required]=\"true\"\n placeholder=\"Write your message...\"\n [rows]=\"8\"\n [(ngModel)]=\"body\"\n [error]=\"composeTagError\"\n (ngModelChange)=\"notifyValid()\" />\n\n @if (!hasDocuments && attachmentsAndScheduling()) {\n <div class=\"sm-field\">\n <label class=\"sm-field-label\">Attachments</label>\n @if (attachments.length > 0) {\n <div class=\"attach-chips\">\n @for (a of attachments; track a.filename; let i = $index) {\n <div class=\"attach-chip\">\n <svg width=\"12\" height=\"12\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\" aria-hidden=\"true\"><path d=\"M21.44 11.05l-9.19 9.19a6 6 0 0 1-8.49-8.49l9.19-9.19a4 4 0 0 1 5.66 5.66l-9.2 9.19a2 2 0 0 1-2.83-2.83l8.49-8.48\"/></svg>\n <span class=\"attach-name\">{{ a.filename }}</span>\n <span class=\"attach-size\">{{ a.sizeLabel }}</span>\n <button class=\"attach-remove\" type=\"button\" (click)=\"removeAttachment(i)\" aria-label=\"Remove attachment\">×</button>\n </div>\n }\n </div>\n }\n <input #emailFileInput type=\"file\" multiple hidden (change)=\"onFilesSelected($event)\" />\n <button type=\"button\" class=\"attach-add-btn\" (click)=\"emailFileInput.click()\">\n <svg width=\"13\" height=\"13\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\" aria-hidden=\"true\"><path d=\"M21.44 11.05l-9.19 9.19a6 6 0 0 1-8.49-8.49l9.19-9.19a4 4 0 0 1 5.66 5.66l-9.2 9.19a2 2 0 0 1-2.83-2.83l8.49-8.48\"/></svg>\n Attach Files\n </button>\n </div>\n\n <ui-checkbox [checked]=\"showSchedule\" label=\"Schedule for later\" (checkedChange)=\"showSchedule = $event; notifyValid()\" />\n @if (showSchedule) {\n <ui-datetime-input\n label=\"Send at\"\n [min]=\"minScheduleTime\"\n [(ngModel)]=\"scheduledAt\"\n (ngModelChange)=\"notifyValid()\" />\n }\n }\n\n <ui-form-section label=\"Add A Video\" [divider]=\"true\">\n <ui-video-picker (videoSelected)=\"insertVideo($event)\" />\n </ui-form-section>\n } @else if (templateId) {\n @if (missingVars.length > 0) {\n <div class=\"mv-section\">\n <p class=\"mv-hint\">\n <svg width=\"13\" height=\"13\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\" aria-hidden=\"true\"><circle cx=\"12\" cy=\"12\" r=\"10\"/><line x1=\"12\" y1=\"8\" x2=\"12\" y2=\"12\"/><line x1=\"12\" y1=\"16\" x2=\"12.01\" y2=\"16\"/></svg>\n This template has fields that need values before sending.\n </p>\n @for (key of missingVars; track key) {\n <div class=\"mv-row\">\n <div class=\"mv-input-wrap\">\n <ui-text-input class=\"mv-text-input\" [label]=\"formatVarLabel(key)\" [required]=\"true\" [placeholder]=\"'Enter ' + formatVarLabel(key).toLowerCase() + '...'\" [(ngModel)]=\"pendingInputs[key]\" (ngModelChange)=\"notifyValid()\" (keydown.enter)=\"applyVar(key)\" />\n <ui-button variant=\"primary\" size=\"sm\" [disabled]=\"!(pendingInputs[key] || '').trim()\" (click)=\"applyVar(key)\" ariaLabel=\"Apply value\">\n <svg width=\"14\" height=\"14\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2.5\" aria-hidden=\"true\"><line x1=\"12\" y1=\"5\" x2=\"12\" y2=\"19\"/><line x1=\"5\" y1=\"12\" x2=\"19\" y2=\"12\"/></svg>\n </ui-button>\n </div>\n </div>\n }\n </div>\n }\n\n <div class=\"tpl-preview-header\">\n <ui-button variant=\"ghost\" size=\"sm\" (click)=\"clearTemplate()\">\n <svg width=\"14\" height=\"14\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2.5\" aria-hidden=\"true\"><polyline points=\"15 18 9 12 15 6\"/></svg>\n Back to templates\n </ui-button>\n <div class=\"tpl-view-toggle\">\n <ui-button variant=\"tab\" size=\"sm\" [active]=\"previewFormat === 'preview'\" [ariaPressed]=\"previewFormat === 'preview'\" (click)=\"setPreviewFormat('preview')\">Preview</ui-button>\n <ui-button variant=\"tab\" size=\"sm\" [active]=\"previewFormat === 'raw'\" [ariaPressed]=\"previewFormat === 'raw'\" (click)=\"setPreviewFormat('raw')\">Raw</ui-button>\n </div>\n </div>\n\n <div class=\"tpl-subject-card\">\n <div class=\"tpl-subject-card-top\">\n <span class=\"tpl-subject-label\">SUBJECT</span>\n <ui-button variant=\"secondary\" size=\"sm\" (click)=\"openSendPreview()\">\n <svg width=\"14\" height=\"14\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\" aria-hidden=\"true\"><path d=\"M1 12s4-8 11-8 11 8 11 8-4 8-11 8-11-8-11-8z\"/><circle cx=\"12\" cy=\"12\" r=\"3\"/></svg>\n View Send Preview\n </ui-button>\n </div>\n <span class=\"tpl-subject-text\">{{ resolvedSubject }}</span>\n </div>\n @if (previewFormat === 'preview') {\n <div class=\"tpl-body-preview\" [innerHTML]=\"previewBodyHtml\"></div>\n } @else {\n <!-- Raw: the template source exactly as written, {{tags}} included — subject first, then the body. -->\n <div class=\"tpl-raw-label\">Subject</div>\n <pre class=\"tpl-subject-raw\">{{ subject }}</pre>\n <div class=\"tpl-raw-label\">Body</div>\n <pre class=\"tpl-body-raw\">{{ body }}</pre>\n }\n } @else {\n <ui-template-picker\n channel=\"email\"\n [required]=\"true\"\n [selectedId]=\"templateId\"\n (templateSelected)=\"onTemplateSelected($event)\"\n (previewClick)=\"openQuickPreview($event)\" />\n }\n\n @if (documents()) {\n <ui-document-send-options\n channel=\"email\"\n [purposes]=\"documentPurposes()\"\n [initialDocuments]=\"initialDocuments()\"\n [showErrors]=\"true\"\n (valueChange)=\"onDocumentsChange($event)\"\n (invalidChange)=\"onDocumentsInvalid($event)\" />\n <div class=\"sm-docs-live\" role=\"status\" aria-live=\"polite\">\n @if (hasDocuments) {\n <p class=\"sm-docs-note\">Your message goes out with the documents. Attachments and scheduling aren't available when sending documents.</p>\n @if (docsRemovedNote) { <p class=\"sm-docs-note sm-docs-removed\" data-testid=\"documents-removed-note\">{{ docsRemovedNote }}</p> }\n }\n </div>\n }\n\n @if (showReceiptOption() === true) {\n <ui-checkbox\n class=\"sm-receipt-option\"\n data-testid=\"include-receipt\"\n [checked]=\"includeReceipt\"\n [label]=\"receiptLabel()\"\n (checkedChange)=\"includeReceipt = $event\" />\n }\n }\n\n <!-- Send Preview Modal -->\n @if (showSendPreview) {\n <div class=\"sp-backdrop\" (click)=\"closeSendPreview()\">\n <div class=\"sp-modal\" (click)=\"$event.stopPropagation()\">\n <div class=\"sp-modal-header\">\n <div class=\"sp-modal-title\">\n <svg width=\"16\" height=\"16\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\" aria-hidden=\"true\"><rect x=\"2\" y=\"4\" width=\"20\" height=\"16\" rx=\"2\"/><path d=\"m22 7-8.97 5.7a1.94 1.94 0 0 1-2.06 0L2 7\"/></svg>\n Email Preview\n </div>\n <ui-button variant=\"ghost\" size=\"sm\" (click)=\"closeSendPreview()\" ariaLabel=\"Close preview\">\n <svg width=\"16\" height=\"16\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\" aria-hidden=\"true\"><line x1=\"18\" y1=\"6\" x2=\"6\" y2=\"18\"/><line x1=\"6\" y1=\"6\" x2=\"18\" y2=\"18\"/></svg>\n </ui-button>\n </div>\n <div class=\"sp-email-meta\">\n <div class=\"sp-meta-row\">\n <span class=\"sp-meta-label\">To</span>\n <span class=\"sp-meta-value\">{{ contactName() }} <{{ contactEmail() }}></span>\n </div>\n <div class=\"sp-meta-row\">\n <span class=\"sp-meta-label\">Subject</span>\n <span class=\"sp-meta-value sp-meta-subject\">{{ sendPreviewSubject }}</span>\n </div>\n </div>\n <div class=\"sp-email-body\" [innerHTML]=\"sendPreviewHtml\"></div>\n </div>\n </div>\n }\n `,\n styles: [`\n :host { display: flex; flex-direction: column; gap: 18px; }\n\n .sm-missing-banner {\n display: flex; align-items: center; gap: 8px; padding: 12px 14px;\n background: #fef3c7; border: 1px solid #fcd34d; border-radius: 8px;\n font-size: 0.8125rem; font-weight: 500; color: #92400e;\n svg { flex-shrink: 0; color: #d97706; }\n }\n\n .sm-group-label { display: block; font-size: 0.8125rem; font-weight: 600; color: #334155; margin-bottom: 6px; }\n .sm-to-card { border: 1px solid #e2e8f0; border-radius: 8px; padding: 12px 14px; display: flex; flex-direction: column; gap: 3px; }\n .sm-to-name { font-size: 0.9375rem; font-weight: 600; color: #1e293b; }\n .sm-to-detail { font-size: 0.8125rem; color: #64748b; }\n .sm-mode-row { display: flex; }\n .sm-field { display: flex; flex-direction: column; gap: 6px; }\n .sm-field-label { font-size: 0.8125rem; font-weight: 600; color: #374151; }\n .sm-docs-note { margin: 0; font-size: 0.75rem; color: #64748b; }\n .sm-docs-removed { color: #92400e; margin-top: 4px; }\n\n .attach-chips { display: flex; flex-direction: column; gap: 6px; margin-bottom: 8px; }\n .attach-chip { display: flex; align-items: center; gap: 8px; padding: 7px 10px; background: #f8fafc; border: 1px solid #e2e8f0; border-radius: 8px; svg { flex-shrink: 0; color: #64748b; } }\n .attach-name { font-size: 0.8125rem; font-weight: 500; color: #1e293b; flex: 1; min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }\n .attach-size { font-size: 0.75rem; color: #94a3b8; flex-shrink: 0; }\n .attach-remove {\n width: 18px; height: 18px; border: none; background: #e2e8f0; border-radius: 50%;\n cursor: pointer; font-size: 0.75rem; color: #64748b; display: flex; align-items: center; justify-content: center; flex-shrink: 0;\n &:hover { background: #cbd5e1; color: #1e293b; }\n }\n .attach-add-btn {\n display: inline-flex; align-items: center; gap: 6px; align-self: flex-start;\n padding: 6px 12px; border: 1px solid #e2e8f0; border-radius: 7px;\n background: #f8fafc; color: #475569; font-size: 0.8125rem; font-weight: 500;\n cursor: pointer; font-family: inherit; transition: all 0.12s;\n &:hover { border-color: #93c5fd; color: #2563eb; background: #eff6ff; }\n }\n\n .mv-section { display: flex; flex-direction: column; gap: 10px; padding: 14px 16px; background: #fffbeb; border: 1px solid #fde68a; border-radius: 10px; }\n .mv-hint { display: flex; align-items: center; gap: 6px; font-size: 0.8125rem; font-weight: 500; color: #92400e; margin: 0; svg { flex-shrink: 0; color: #d97706; } }\n .mv-row { display: flex; flex-direction: column; gap: 5px; }\n .mv-input-wrap { display: flex; gap: 6px; align-items: flex-end; }\n .mv-text-input { flex: 1; min-width: 0; }\n\n .tpl-preview-header { display: flex; align-items: center; justify-content: space-between; }\n .tpl-view-toggle { display: flex; border: 1px solid #e2e8f0; border-radius: 8px; overflow: hidden; }\n .tpl-subject-card { border: 1px solid #e2e8f0; border-radius: 10px; padding: 14px 16px; background: #fff; display: flex; flex-direction: column; gap: 6px; }\n .tpl-subject-card-top { display: flex; align-items: center; justify-content: space-between; }\n .tpl-subject-label { font-size: 0.6875rem; font-weight: 700; letter-spacing: 0.08em; color: #94a3b8; }\n .tpl-subject-text { font-size: 1rem; font-weight: 700; color: #1e293b; line-height: 1.3; }\n .tpl-body-preview { border: 1px solid #e2e8f0; border-radius: 10px; padding: 18px; background: #fff; font-size: 0.9rem; color: #1e293b; line-height: 1.7; min-height: 200px; overflow-y: auto; }\n .tpl-raw-label { font-size: 0.6875rem; font-weight: 700; letter-spacing: 0.08em; color: #94a3b8; text-transform: uppercase; margin-bottom: -10px; }\n .tpl-subject-raw { border: 1px solid #e2e8f0; border-radius: 10px; padding: 12px 18px; background: #fff; font-size: 0.8125rem; color: #475569; line-height: 1.6; white-space: pre-wrap; font-family: monospace; margin: 0; }\n .tpl-body-raw { border: 1px solid #e2e8f0; border-radius: 10px; padding: 18px; background: #fff; font-size: 0.8125rem; color: #475569; line-height: 1.6; min-height: 200px; overflow-y: auto; white-space: pre-wrap; font-family: monospace; margin: 0; }\n\n .sp-backdrop { position: fixed; inset: 0; background: rgba(15,23,42,0.45); backdrop-filter: blur(3px); z-index: 9999; display: flex; align-items: center; justify-content: center; padding: 24px; }\n .sp-modal { background: #fff; border-radius: 14px; width: 100%; max-width: 600px; max-height: 80vh; display: flex; flex-direction: column; box-shadow: 0 20px 60px rgba(0,0,0,0.18); overflow: hidden; }\n .sp-modal-header { display: flex; align-items: center; justify-content: space-between; padding: 16px 20px; border-bottom: 1px solid #f1f5f9; flex-shrink: 0; }\n .sp-modal-title { display: flex; align-items: center; gap: 8px; font-size: 0.9375rem; font-weight: 700; color: #1e293b; }\n .sp-email-meta { padding: 14px 20px; border-bottom: 1px solid #f1f5f9; display: flex; flex-direction: column; gap: 6px; flex-shrink: 0; background: #f8fafc; }\n .sp-meta-row { display: flex; align-items: baseline; gap: 10px; }\n .sp-meta-label { font-size: 0.75rem; font-weight: 700; color: #94a3b8; letter-spacing: 0.04em; min-width: 50px; flex-shrink: 0; }\n .sp-meta-value { font-size: 0.875rem; color: #334155; }\n .sp-meta-subject { font-weight: 600; color: #1e293b; }\n .sp-email-body { padding: 24px; overflow-y: auto; font-size: 0.9375rem; color: #1e293b; line-height: 1.75; flex: 1; }\n\n @media (max-width: 480px) {\n .sm-input, .sm-textarea { font-size: 1rem; }\n .tpl-preview-header { flex-wrap: wrap; gap: 8px; }\n .sp-backdrop { padding: 0; align-items: flex-end; }\n .sp-modal { max-height: 90dvh; border-radius: 14px 14px 0 0; }\n .sp-email-body { padding: 14px; }\n }\n\n /* Phones: long unbroken values (emails, subjects, file names) wrap inside their card instead of widening the panel. */\n .sm-to-card, .sm-to-name, .sm-to-detail, .tpl-subject-text, .sp-meta-value { min-width: 0; overflow-wrap: anywhere; }\n .sp-email-body { overflow-wrap: anywhere; }\n .tpl-subject-card-top { gap: 8px; flex-wrap: wrap; }\n /* Touch: the attachment remove (x) and Attach Files controls reach a 44px target (the 18px circle keeps its look). */\n @media (pointer: coarse) {\n .attach-remove { position: relative; }\n .attach-remove::after { content: ''; position: absolute; inset: -13px; }\n .attach-add-btn { min-height: 44px; }\n }\n /* Bottom-sheet preview: keep the last lines clear of the home indicator. */\n @media (max-width: 480px) {\n .sp-email-body { padding-bottom: calc(14px + env(safe-area-inset-bottom, 0px)); }\n }\n `],\n})\nexport class SendEmailComponent implements OnInit {\n private readonly destroyRef = inject(DestroyRef);\n private readonly http = inject(HttpClient);\n /** API root from FORGEROUTE_CONFIG (e.g. a cross-origin prod API); falls back to same-origin `/api`. */\n private readonly apiBase = inject(FORGEROUTE_CONFIG, { optional: true })?.baseUrl ?? '/api';\n private readonly toast = inject(ToastService);\n readonly cdr = inject(ChangeDetectorRef);\n\n readonly contactName = input('');\n readonly contactEmail = input('');\n /** The contact's phone — fills `{{phone}}` (a blank phone makes `{{phone}}` a prompted field). */\n readonly contactPhone = input('');\n readonly contactId = input('');\n /** Prefills Subject in \"Create Message\" mode (e.g. \"Estimate #1042 — Kitchen Remodel\"). */\n readonly defaultSubject = input('');\n /** Prefills Message in \"Create Message\" mode. */\n readonly defaultBody = input('');\n /** Extra merge variables (e.g. `estimate_number`, `estimate_total`) used in templates and the preview. */\n readonly context = input<Record<string, string>>({});\n /**\n * Sends through the host instead of `POST <baseUrl>/email-messages` — e.g. a page-specific endpoint that\n * also marks a record Sent. Loading, toasts, `sent` and the double-send guard behave the same.\n */\n readonly sendFn = input<((payload: SendEmailPayload) => Observable<unknown>) | null>(null);\n /** Shows an \"include receipt\" checkbox; the choice is sent as `includeReceipt`. Off = payload has no such key. */\n readonly showReceiptOption = input(false);\n readonly receiptLabel = input('Include payment receipt');\n /** Initial state of the receipt checkbox (and the state it returns to after a send). */\n readonly receiptDefault = input(false);\n /** Shows the \"Documents\" section (search your documents, choose what the recipient does with them). Off = the form is unchanged. */\n readonly documents = input(false);\n /** Purposes the app's backend supports for documents (default all); a single entry hides the purpose select. */\n readonly documentPurposes = input<DocumentSendPurpose[]>(['ESIGN', 'ATTACH', 'BOTH']);\n /** Documents already chosen in the Documents section when the form opens. */\n readonly initialDocuments = input<{ id: string; title: string; fileName?: string | null; hasFile?: boolean }[]>([]);\n /** Shows a CC field; the typed addresses are sent as `cc`. Off = payload has no such key. */\n readonly showCc = input(false);\n /** false hides Attach Files and \"Schedule for later\" (for a host whose send function can't deliver them). */\n readonly attachmentsAndScheduling = input(true);\n\n readonly sent = output<void>();\n readonly emailAdded = output<string>();\n readonly validChange = output<boolean>();\n\n mode: 'compose' | 'template' = 'compose';\n subject = '';\n body = '';\n templateId: string | null = null;\n previewFormat: 'preview' | 'raw' = 'preview';\n showSendPreview = false;\n previewingTemplate: { subject: string; body: string } | null = null;\n customVars: Record<string, string> = {};\n pendingInputs: Record<string, string> = {};\n loading = false;\n includeReceipt = false;\n showSchedule = false;\n scheduledAt = '';\n attachments: { filename: string; content: string; contentType: string; sizeLabel: string }[] = [];\n pendingEmail = '';\n ccText = '';\n /** What the document section holds (null / empty = no documents) and whether it currently breaks a rule. */\n documentsValue: DocumentSendValue | null = null;\n documentsInvalid = false;\n\n readonly savingEmail = signal(false);\n\n readonly modeTabs: PillTab[] = [\n { key: 'compose', label: 'Create Message' },\n { key: 'template', label: 'Use Existing Template' },\n ];\n\n /** Market values (`area`, `median_price`, `dom`, `new_listings`) — only the ones `/service-areas` returned. */\n private marketVars: Record<string, string> = {};\n\n ngOnInit() {\n this.includeReceipt = this.receiptDefault();\n this.http.get<any>(`${this.apiBase}/service-areas?size=100`).pipe(takeUntilDestroyed(this.destroyRef)).subscribe({\n next: (resp) => {\n this.marketVars = marketVarsFrom(resp);\n // Fields the market values now fill stop being prompted; preview, modal and payload all re-read them.\n this.notifyValid();\n },\n });\n }\n\n /** Defaults last applied to compose mode — a default only replaces text the user hasn't edited. */\n private _appliedDefaults = { subject: '', body: '' };\n\n constructor() {\n effect(() => {\n this.contactName(); this.contactEmail(); this.contactId(); this.context();\n untracked(() => this.notifyValid());\n });\n effect(() => {\n const subject = this.defaultSubject(); const body = this.defaultBody();\n untracked(() => this.applyDefaults(subject, body));\n });\n }\n\n /** The Subject last filled from the chosen documents — it is replaced only while the Subject still equals it (the user hasn't edited it). */\n private _suggestedSubject = '';\n\n /** Fills / updates / clears the document-based Subject suggestion (compose mode only; never touches text the user or the host wrote). */\n private syncSuggestedSubject() {\n if (this.mode !== 'compose') return;\n const docs = this.hasDocuments ? this.documentsValue : null;\n const next = docs ? suggestDocumentSubject(docs.documents.map(d => d.title), docs.purpose) : '';\n if (this.subject !== '' && this.subject !== this._suggestedSubject) return;\n this.subject = next;\n this._suggestedSubject = next;\n }\n\n /** Puts the host's defaults into compose mode unless the user already changed that field. */\n private applyDefaults(subject: string, body: string) {\n if (this.mode === 'compose') {\n if (this.subject === this._appliedDefaults.subject) this.subject = subject;\n else if (subject && this._suggestedSubject && this.subject === this._suggestedSubject) { this.subject = subject; this._suggestedSubject = ''; }\n if (this.body === this._appliedDefaults.body) this.body = body;\n }\n this._appliedDefaults = { subject, body };\n this.notifyValid();\n }\n\n protected readonly previewHtml = messagePreviewHtml;\n\n setPreviewFormat(f: 'preview' | 'raw') { this.previewFormat = f; this.cdr.markForCheck(); }\n\n get pendingEmailValid(): boolean { return EMAIL_RE.test(this.pendingEmail.trim()); }\n\n /** True when the sender chose at least one document (only possible when the `documents` input is on). */\n get hasDocuments(): boolean { return this.documents() && (this.documentsValue?.documentIds.length ?? 0) > 0; }\n\n /** The people copied on the email, as typed (empty unless `showCc` is on). */\n get ccList(): string[] { return this.showCc() ? parseCcList(this.ccText) : []; }\n\n /** The first problem with the CC field ('' when fine). */\n get ccError(): string { return this.showCc() ? ccListError(this.ccList) : ''; }\n\n /** Set when choosing the first document removed something the sender had already added - said out loud (aria-live), never silently. */\n docsRemovedNote = '';\n onDocumentsChange(v: DocumentSendValue) {\n this.documentsValue = v;\n this.syncSuggestedSubject();\n if (this.hasDocuments) {\n const removed = [this.attachments.length > 0 ? 'your attachments' : '', this.showSchedule ? 'the schedule' : ''].filter(Boolean);\n if (removed.length > 0) this.docsRemovedNote = `We removed ${removed.join(' and ')} because documents are sent as a request.`;\n this.attachments = []; this.showSchedule = false; this.scheduledAt = '';\n } else {\n this.docsRemovedNote = '';\n }\n this.notifyValid();\n }\n onDocumentsInvalid(bad: boolean) { this.documentsInvalid = bad; this.notifyValid(); }\n\n get canSend(): boolean {\n if (this.ccError || (this.documents() && this.documentsInvalid)) return false;\n const hasContent = !!this.subject.trim() && !!this.body.trim() && this.unresolvedVars.length === 0\n && !hasLeftoverTags(this.resolvedSubject) && !hasLeftoverTags(this.resolvedBody);\n if (this.showSchedule) return hasContent && !!this.scheduledAt && new Date(this.scheduledAt).getTime() > Date.now();\n return hasContent;\n }\n\n get minScheduleTime(): string { return new Date(Date.now() + 60000).toISOString().slice(0, 16); }\n\n /**\n * Values every tag can resolve from before the user types anything: the contact (name, first / last name, email,\n * phone), the date, the market values `/service-areas` returned and the host's `context` — non-blank values only,\n * later sources winning.\n */\n private get knownVars(): Record<string, string> {\n const name = this.contactName().trim();\n const parts = name.split(/\\s+/);\n return nonBlankValues(\n {\n first_name: parts[0] ?? '', last_name: parts.slice(1).join(' '), full_name: name, name,\n email: this.contactEmail(), phone: this.contactPhone(),\n },\n dateVars(), this.marketVars, this.context(), this.customVars,\n );\n }\n\n /** Merges `text` with every known value plus what the user typed — the ONE merge the pane, modal and payload use. */\n resolveMergeTags(text: string): string {\n return applyMergeTags(text, { ...this.knownVars, ...this.liveVars });\n }\n\n /** Applied values plus anything typed into a missing-field input — so the preview updates as the user types. */\n private get liveVars(): Record<string, string> {\n const live: Record<string, string> = { ...this.customVars };\n for (const [k, v] of Object.entries(this.pendingInputs)) { if ((v ?? '').trim()) live[k] = v.trim(); }\n return live;\n }\n\n /** The subject / body exactly as they will be sent (live: recomputed on every read, never cached). */\n get resolvedSubject(): string { return this.resolveMergeTags(this.subject); }\n get resolvedBody(): string { return this.resolveMergeTags(this.body); }\n /** The Preview pane's HTML — the same HTML the server sends (see messagePreviewHtml). */\n get previewBodyHtml(): string { return messagePreviewHtml(this.resolvedBody); }\n\n /** The send-preview modal: the eye-button template when one is being previewed, otherwise the selected one. */\n get sendPreviewSubject(): string {\n return this.previewingTemplate ? this.resolveMergeTags(this.previewingTemplate.subject) : this.resolvedSubject;\n }\n get sendPreviewHtml(): string {\n return this.previewingTemplate\n ? messagePreviewHtml(this.resolveMergeTags(this.stripBlockWrappers(this.previewingTemplate.body)))\n : this.previewBodyHtml;\n }\n\n /** Template fields the user still has to fill — tags with no non-blank known value and nothing applied. */\n get missingVars(): string[] {\n const known = this.knownVars;\n return mergeTagKeys(this.subject + ' ' + this.body).filter(k => !known[k]);\n }\n\n /** Compose mode: a `{{…}}` the form can't fill blocks sending (the server would refuse it). */\n get composeTagError(): string {\n if (this.mode !== 'compose') return '';\n return hasLeftoverTags(this.resolvedSubject) || hasLeftoverTags(this.resolvedBody)\n ? 'Replace every {{…}} field with text before sending.' : '';\n }\n\n /** Missing fields with nothing typed yet — Send stays disabled until this is empty. */\n get unresolvedVars(): string[] {\n return this.missingVars.filter(k => !(this.pendingInputs[k] ?? '').trim());\n }\n\n /**\n * Applies every typed missing-field value (what the preview already shows) before sending. Never saves\n * to the contact — only the explicit \"+\" / Enter apply does that.\n */\n private applyPendingVars() {\n for (const k of this.missingVars) { if ((this.pendingInputs[k] ?? '').trim()) this.applyVar(k, false); }\n }\n\n formatVarLabel(key: string): string { return formatVarLabel(key); }\n\n /** Applies a typed missing-field value. The explicit \"+\" / Enter apply (`persist`) also saves phone / address to the contact. */\n applyVar(key: string, persist = true) {\n const val = (this.pendingInputs[key] ?? '').trim();\n if (!val) return;\n this.customVars = { ...this.customVars, [key]: val };\n this.pendingInputs[key] = '';\n if (persist && this.contactId() && (key === 'phone' || key === 'address')) {\n this.http.patch(`${this.apiBase}/contacts/${this.contactId()}`, { [key]: val }).subscribe();\n }\n this.notifyValid();\n }\n\n stripBlockWrappers(html: string): string {\n if (!html || !html.includes('data-block')) return html;\n return html\n .replace(/<\\/div>\\s*(?=<div[^>]*data-block)/g, '')\n .replace(/<div[^>]*data-block[^>]*>/g, '')\n .replace(/<\\/div>\\s*$/, '');\n }\n\n /** \"View Send Preview\": always the SELECTED template (never a template last opened with the eye button). */\n openSendPreview() { this.previewingTemplate = null; this.showSendPreview = true; this.cdr.markForCheck(); }\n\n /** The picker's eye button: previews that template without selecting it. */\n openQuickPreview(tpl: { subject: string; body: string }) {\n this.previewingTemplate = tpl; this.showSendPreview = true; this.cdr.markForCheck();\n }\n\n closeSendPreview() { this.showSendPreview = false; this.previewingTemplate = null; this.cdr.markForCheck(); }\n\n notifyValid() {\n this.validChange.emit(!!this.contactEmail() && this.canSend);\n this.cdr.markForCheck();\n }\n\n setMode(key: string) {\n const next = key as 'compose' | 'template';\n if (next === 'compose') {\n this.templateId = null;\n this.subject = this._appliedDefaults.subject; this.body = this._appliedDefaults.body;\n this.customVars = {}; this.pendingInputs = {}; this.previewFormat = 'preview';\n } else {\n this.subject = ''; this.body = '';\n }\n this._suggestedSubject = '';\n this.showSchedule = false; this.scheduledAt = '';\n this.mode = next;\n this.syncSuggestedSubject();\n this.notifyValid();\n }\n\n saveEmail() {\n if (!this.pendingEmailValid || this.savingEmail()) return;\n this.savingEmail.set(true);\n this.cdr.markForCheck();\n this.emailAdded.emit(this.pendingEmail.trim());\n this.savingEmail.set(false);\n this.cdr.markForCheck();\n }\n\n\n clearTemplate() {\n this.templateId = null; this.subject = ''; this.body = '';\n this.previewFormat = 'preview'; this.customVars = {}; this.pendingInputs = {};\n this.notifyValid();\n }\n\n onTemplateSelected(tpl: TemplateItem) {\n this.templateId = tpl.id;\n this.subject = tpl.subject ?? '';\n this.body = this.stripBlockWrappers(tpl.body);\n this.previewFormat = 'preview';\n // Values typed for a previous template must not leak into this one.\n this.customVars = {};\n this.pendingInputs = {};\n for (const k of this.missingVars) { this.pendingInputs[k] = ''; }\n this.notifyValid();\n }\n\n onFilesSelected(event: Event) {\n const files = (event.target as HTMLInputElement).files;\n if (!files) return;\n Array.from(files).forEach(file => {\n const reader = new FileReader();\n reader.onload = () => {\n const base64 = (reader.result as string).split(',')[1];\n const sizeLabel = file.size < 1024 * 1024\n ? `${Math.round(file.size / 1024)} KB`\n : `${(file.size / (1024 * 1024)).toFixed(1)} MB`;\n this.attachments = [...this.attachments, {\n filename: file.name, content: base64,\n contentType: file.type || 'application/octet-stream', sizeLabel,\n }];\n this.cdr.markForCheck();\n };\n reader.readAsDataURL(file);\n });\n (event.target as HTMLInputElement).value = '';\n }\n\n removeAttachment(index: number) { this.attachments = this.attachments.filter((_, i) => i !== index); }\n\n insertVideo(v: VideoMessage) {\n if (!v.shareLink) return;\n const sep = this.body.trim() ? '\\n\\n' : '';\n this.body = this.body + sep + `Watch \"${v.title}\": ${v.shareLink}`;\n this.notifyValid();\n }\n\n submit() {\n if (!this.canSend || this.loading || !this.contactEmail()) return;\n if (this.mode === 'template') this.applyPendingVars();\n this.loading = true;\n this.cdr.markForCheck();\n const attachments = this.attachments.map(({ filename, content, contentType }) => ({ filename, content, contentType }));\n const payload: SendEmailPayload = {\n contactName: this.contactName(),\n contactEmail: this.contactEmail(),\n // Exactly what the Preview pane and the send-preview modal show (compose text is merged the same way).\n subject: this.resolvedSubject,\n body: this.resolvedBody,\n contactId: this.contactId() || undefined,\n scheduledAt: this.showSchedule && this.scheduledAt ? new Date(this.scheduledAt).toISOString() : undefined,\n attachments: attachments.length ? attachments : undefined,\n };\n if (this.showReceiptOption() === true) payload.includeReceipt = this.includeReceipt;\n if (this.mode === 'template' && this.templateId) payload.templateId = this.templateId;\n const cc = this.ccList;\n if (cc.length) payload.cc = cc;\n const docs = this.hasDocuments ? this.documentsValue : null;\n if (docs) {\n // A document request has its own delivery: no manual attachments, no scheduling.\n payload.documents = { documentIds: docs.documentIds, purpose: docs.purpose, delivery: 'EMAIL' };\n payload.attachments = undefined;\n payload.scheduledAt = undefined;\n }\n const custom = this.sendFn();\n const req$ = custom ? custom(payload)\n : docs ? sendDocumentRequest(this.http, this.apiBase, {\n subject: payload.subject || DEFAULT_DOCUMENT_SUBJECT, message: payload.body,\n customerId: payload.contactId, customerName: payload.contactName, customerEmail: payload.contactEmail,\n documentIds: docs.documentIds, purpose: docs.purpose, delivery: 'EMAIL',\n ccEmails: cc.length ? cc : undefined,\n }, payload.templateId)\n : this.http.post(`${this.apiBase}/email-messages`, payload);\n req$.pipe(takeUntilDestroyed(this.destroyRef)).subscribe({\n next: () => {\n this.toast.show(this.showSchedule ? 'Email scheduled' : 'Email sent');\n this.loading = false; this.attachments = [];\n this.includeReceipt = this.receiptDefault();\n this.sent.emit(); this.cdr.markForCheck();\n },\n error: (err: unknown) => {\n // Default endpoint keeps its fixed message; a host endpoint may return a specific one.\n this.toast.error(custom || docs ? getErrorMessage(err, 'email', 'send') : 'Failed to send email');\n this.loading = false; this.cdr.markForCheck();\n },\n });\n }\n}\n","import {\n Component, input, output, effect, untracked, OnInit,\n inject, ChangeDetectionStrategy, ChangeDetectorRef, DestroyRef, signal,\n} from '@angular/core';\nimport { FormsModule } from '@angular/forms';\nimport { HttpClient } from '@angular/common/http';\nimport type { Observable } from 'rxjs';\nimport { FORGEROUTE_CONFIG } from '../config/forgeroute.config';\nimport { getErrorMessage } from '../error-handler';\nimport { takeUntilDestroyed } from '@angular/core/rxjs-interop';\nimport { CheckboxComponent } from '../checkbox/checkbox.component';\nimport { PillTabsComponent } from '../pill-tabs/pill-tabs.component';\nimport type { PillTab } from '../pill-tabs/pill-tabs.component';\nimport { TemplatePickerComponent } from '../template-picker/template-picker.component';\nimport type { TemplateItem } from '../template-picker/template-picker.component';\nimport { VideoPickerComponent } from '../video-picker/video-picker.component';\nimport type { VideoMessage } from '../video-picker/video-picker.component';\nimport { ToastService } from '../toast/toast.service';\nimport { ButtonComponent } from '../button/button.component';\nimport { TextInputComponent } from '../text-input/text-input.component';\nimport { TextareaInputComponent } from '../textarea-input/textarea-input.component';\nimport { DatetimeInputComponent } from '../datetime-input/datetime-input.component';\nimport { FormSectionComponent } from '../form-section/form-section.component';\nimport { DocumentSendOptionsComponent } from '../document-send-options/document-send-options.component';\nimport type { DocumentSendValue, DocumentSendPurpose } from '../document-send-options/document-send-options.component';\nimport { DEFAULT_DOCUMENT_SUBJECT, sendDocumentRequest } from './send-documents';\nimport type { SendDocumentsPayload } from './send-documents';\nimport {\n applyMergeTags, dateVars, formatVarLabel, hasLeftoverTags, marketVarsFrom, mergeTagKeys, nonBlankValues,\n} from './merge-tags';\n\nconst MAX_SMS_LENGTH = 320;\n\n/** What the text form sends — the default `POST <baseUrl>/text-messages` body, or the host's `sendFn` argument. */\nexport interface SendTextPayload {\n contactName: string;\n contactPhone: string;\n body: string;\n contactId?: string;\n scheduledAt?: string;\n mediaUrl?: string;\n mediaType?: string;\n mediaFileName?: string;\n /** The template the message was made from (template mode only) — the backend counts it toward the template's Sent total. */\n templateId?: string;\n /** Only present when the host enabled `documents` and the sender chose at least one document (then `mediaUrl` and `scheduledAt` are never set). Texts send a link: `delivery` is `TEXT`, `purpose` is `ESIGN`. */\n documents?: SendDocumentsPayload;\n}\nconst PHONE_RE = /^\\+?[\\d\\s\\-().]{7,}$/;\n\nfunction formatPhone(raw: string | null | undefined): string {\n if (!raw) return '';\n const normalized = raw.startsWith('+1') && raw.replace(/\\D/g, '').length === 11 ? raw.slice(2) : raw;\n if (normalized.startsWith('+')) return normalized;\n const digits = normalized.replace(/\\D/g, '');\n const d = digits.startsWith('1') && digits.length === 11 ? digits.slice(1) : digits.slice(0, 10);\n if (d.length <= 3) return d;\n if (d.length <= 6) return `(${d.slice(0, 3)}) ${d.slice(3)}`;\n return `(${d.slice(0, 3)}) ${d.slice(3, 6)}-${d.slice(6)}`;\n}\n\n@Component({\n selector: 'ui-send-text',\n standalone: true,\n changeDetection: ChangeDetectionStrategy.OnPush,\n imports: [FormsModule, ButtonComponent, CheckboxComponent, PillTabsComponent, TemplatePickerComponent, VideoPickerComponent, TextInputComponent, TextareaInputComponent, DatetimeInputComponent, FormSectionComponent, DocumentSendOptionsComponent],\n template: `\n <!-- Missing phone guard -->\n @if (!contactPhone()) {\n <div class=\"sm-missing-banner\">\n <svg width=\"16\" height=\"16\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\" aria-hidden=\"true\"><circle cx=\"12\" cy=\"12\" r=\"10\"/><line x1=\"12\" y1=\"8\" x2=\"12\" y2=\"12\"/><line x1=\"12\" y1=\"16\" x2=\"12.01\" y2=\"16\"/></svg>\n No phone number on file for this contact.\n </div>\n <ui-text-input\n label=\"Add Phone Number\"\n type=\"tel\"\n [required]=\"true\"\n placeholder=\"(555) 000-0000\"\n [(ngModel)]=\"pendingPhone\"\n (ngModelChange)=\"notifyValid()\"\n [error]=\"pendingPhone && !pendingPhoneValid ? 'Please enter a valid phone number.' : ''\" />\n <ui-button variant=\"primary\" [disabled]=\"!pendingPhoneValid || savingPhone()\" [loading]=\"savingPhone()\" (click)=\"savePhone()\">\n Save & Continue\n </ui-button>\n } @else {\n <!-- To -->\n <div class=\"sm-group\">\n <span class=\"sm-group-label\">To</span>\n <div class=\"sm-to-card\">\n <span class=\"sm-to-name\">{{ contactName() }}</span>\n <span class=\"sm-to-detail\">{{ formatPhone(contactPhone()) }}</span>\n </div>\n </div>\n\n <!-- Mode -->\n <div class=\"sm-mode-row\">\n <ui-pill-tabs [tabs]=\"modeTabs\" [activeKey]=\"mode\" (tabChange)=\"setMode($event)\" />\n </div>\n\n @if (mode === 'compose') {\n <div class=\"sm-field\">\n <ui-textarea-input\n label=\"Message\"\n [required]=\"true\"\n placeholder=\"Write your message...\"\n [rows]=\"8\"\n [maxlength]=\"MAX_LENGTH\"\n [(ngModel)]=\"body\"\n [error]=\"composeTagError\"\n (ngModelChange)=\"notifyValid()\" />\n <div class=\"sm-char-row\">\n <span class=\"sm-hint\">Sent as SMS. Keep messages concise for best delivery.</span>\n <span class=\"sm-char-count\" [class.sm-char-warn]=\"body.length > MAX_LENGTH * 0.9\">\n {{ body.length }} / {{ MAX_LENGTH }}\n </span>\n </div>\n </div>\n\n @if (!hasDocuments && attachmentsAndScheduling()) {\n <div class=\"sm-field\">\n <label class=\"sm-field-label\">Add Image / Media (MMS)</label>\n @if (mediaAttachment()) {\n <div class=\"attach-chips\">\n <div class=\"attach-chip\">\n <svg width=\"12\" height=\"12\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\" aria-hidden=\"true\"><rect x=\"3\" y=\"3\" width=\"18\" height=\"18\" rx=\"2\" ry=\"2\"/><circle cx=\"8.5\" cy=\"8.5\" r=\"1.5\"/><polyline points=\"21 15 16 10 5 21\"/></svg>\n <span class=\"attach-name\">{{ mediaAttachment()!.mediaFileName }}</span>\n <span class=\"attach-size\">{{ mediaAttachment()!.sizeLabel }}</span>\n <button class=\"attach-remove\" type=\"button\" (click)=\"removeMedia()\" aria-label=\"Remove attachment\">×</button>\n </div>\n </div>\n }\n @if (!mediaAttachment()) {\n <input #smsFileInput type=\"file\" accept=\"image/*,video/*,application/pdf\" hidden (change)=\"onFileSelected($event)\" />\n <button type=\"button\" class=\"attach-add-btn\" [disabled]=\"mediaUploading()\" (click)=\"smsFileInput.click()\">\n @if (mediaUploading()) {\n <span class=\"attach-spinner\"></span> Uploading...\n } @else {\n <svg width=\"13\" height=\"13\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\" aria-hidden=\"true\"><rect x=\"3\" y=\"3\" width=\"18\" height=\"18\" rx=\"2\" ry=\"2\"/><circle cx=\"8.5\" cy=\"8.5\" r=\"1.5\"/><polyline points=\"21 15 16 10 5 21\"/></svg>\n Attach Image\n }\n </button>\n }\n </div>\n\n <ui-checkbox [checked]=\"showSchedule\" label=\"Schedule for later\" (checkedChange)=\"showSchedule = $event; notifyValid()\" />\n @if (showSchedule) {\n <ui-datetime-input\n label=\"Send at\"\n [min]=\"minScheduleTime\"\n [(ngModel)]=\"scheduledAt\"\n (ngModelChange)=\"notifyValid()\" />\n }\n }\n\n <ui-form-section label=\"Video\" [divider]=\"true\">\n <ui-video-picker (videoSelected)=\"insertVideo($event)\" />\n </ui-form-section>\n } @else if (templateId) {\n @if (missingVars.length > 0) {\n <div class=\"mv-section\">\n <p class=\"mv-hint\">\n <svg width=\"13\" height=\"13\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\" aria-hidden=\"true\"><circle cx=\"12\" cy=\"12\" r=\"10\"/><line x1=\"12\" y1=\"8\" x2=\"12\" y2=\"12\"/><line x1=\"12\" y1=\"16\" x2=\"12.01\" y2=\"16\"/></svg>\n This template has fields that need values before sending.\n </p>\n @for (key of missingVars; track key) {\n <div class=\"mv-row\">\n <div class=\"mv-input-wrap\">\n <ui-text-input class=\"mv-text-input\" [label]=\"formatVarLabel(key)\" [required]=\"true\" [placeholder]=\"'Enter ' + formatVarLabel(key).toLowerCase() + '...'\" [(ngModel)]=\"pendingInputs[key]\" (ngModelChange)=\"notifyValid()\" (keydown.enter)=\"applyVar(key)\" />\n <button class=\"mv-apply-btn\" type=\"button\" [disabled]=\"!(pendingInputs[key] || '').trim()\" (click)=\"applyVar(key)\" aria-label=\"Apply value\">\n <svg width=\"14\" height=\"14\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2.5\" aria-hidden=\"true\"><line x1=\"12\" y1=\"5\" x2=\"12\" y2=\"19\"/><line x1=\"5\" y1=\"12\" x2=\"19\" y2=\"12\"/></svg>\n </button>\n </div>\n </div>\n }\n </div>\n }\n\n <div class=\"tpl-preview-header\">\n <button class=\"tpl-back-btn\" type=\"button\" (click)=\"clearTemplate()\">\n <svg width=\"14\" height=\"14\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2.5\" aria-hidden=\"true\"><polyline points=\"15 18 9 12 15 6\"/></svg>\n Back to templates\n </button>\n <div class=\"tpl-view-toggle\">\n <button class=\"tpl-toggle-btn\" [class.active]=\"previewFormat === 'preview'\" [attr.aria-pressed]=\"previewFormat === 'preview'\" type=\"button\" (click)=\"setPreviewFormat('preview')\">Preview</button>\n <button class=\"tpl-toggle-btn\" [class.active]=\"previewFormat === 'raw'\" [attr.aria-pressed]=\"previewFormat === 'raw'\" type=\"button\" (click)=\"setPreviewFormat('raw')\">Raw</button>\n </div>\n </div>\n\n <div class=\"tpl-send-preview-row\">\n <ui-button variant=\"secondary\" size=\"sm\" (click)=\"openSendPreview()\">\n <svg width=\"14\" height=\"14\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\" aria-hidden=\"true\"><path d=\"M1 12s4-8 11-8 11 8 11 8-4 8-11 8-11-8-11-8z\"/><circle cx=\"12\" cy=\"12\" r=\"3\"/></svg>\n View Send Preview\n </ui-button>\n </div>\n\n @if (previewFormat === 'preview') {\n <div class=\"tpl-body-preview\">{{ resolvedBody }}</div>\n } @else {\n <pre class=\"tpl-body-raw\">{{ body }}</pre>\n }\n } @else {\n <ui-template-picker\n channel=\"text\"\n [required]=\"true\"\n [selectedId]=\"templateId\"\n (templateSelected)=\"onTemplateSelected($event)\"\n (previewClick)=\"openQuickPreview($event)\" />\n }\n\n @if (documents()) {\n <ui-document-send-options\n channel=\"text\"\n [purposes]=\"documentPurposes()\"\n [initialDocuments]=\"initialDocuments()\"\n [showErrors]=\"true\"\n (valueChange)=\"onDocumentsChange($event)\"\n (invalidChange)=\"onDocumentsInvalid($event)\" />\n <div class=\"sm-docs-live\" role=\"status\" aria-live=\"polite\">\n @if (hasDocuments) {\n <p class=\"sm-docs-note\">The documents are texted as a link after your message. Images and scheduling aren't available when sending documents.</p>\n @if (docsRemovedNote) { <p class=\"sm-docs-note sm-docs-removed\" data-testid=\"documents-removed-note\">{{ docsRemovedNote }}</p> }\n }\n </div>\n }\n }\n\n <!-- Send preview modal: the SELECTED template exactly as it will be sent -->\n @if (showSendPreview) {\n <div class=\"sp-backdrop\" data-testid=\"text-send-preview\" (click)=\"closeSendPreview()\">\n <div class=\"sp-modal\" (click)=\"$event.stopPropagation()\">\n <div class=\"sp-modal-header\">\n <div class=\"sp-modal-title\">\n <svg width=\"16\" height=\"16\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\" aria-hidden=\"true\"><path d=\"M21 15a2 2 0 0 1-2 2H7l-4 4V5a2 2 0 0 1 2-2h14a2 2 0 0 1 2 2z\"/></svg>\n Text Preview\n </div>\n <button class=\"sp-close-btn\" type=\"button\" (click)=\"closeSendPreview()\" aria-label=\"Close preview\">\n <svg width=\"16\" height=\"16\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\" aria-hidden=\"true\"><line x1=\"18\" y1=\"6\" x2=\"6\" y2=\"18\"/><line x1=\"6\" y1=\"6\" x2=\"18\" y2=\"18\"/></svg>\n </button>\n </div>\n <div class=\"sp-text-meta\">\n <div class=\"sp-meta-row\">\n <span class=\"sp-meta-label\">To</span>\n <span class=\"sp-meta-value\">{{ contactName() }} {{ formatPhone(contactPhone()) }}</span>\n </div>\n </div>\n <div class=\"sp-text-body\">{{ resolvedBody }}</div>\n </div>\n </div>\n }\n\n <!-- Quick preview modal (the picker's eye button) -->\n @if (showQuickPreview && previewingTemplate) {\n <div class=\"sp-backdrop\" data-testid=\"text-quick-preview\" (click)=\"showQuickPreview = false; previewingTemplate = null\">\n <div class=\"sp-modal\" (click)=\"$event.stopPropagation()\">\n <div class=\"sp-modal-header\">\n <div class=\"sp-modal-title\">\n <svg width=\"16\" height=\"16\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\" aria-hidden=\"true\"><path d=\"M21 15a2 2 0 0 1-2 2H7l-4 4V5a2 2 0 0 1 2-2h14a2 2 0 0 1 2 2z\"/></svg>\n Text Preview\n </div>\n <button class=\"sp-close-btn\" type=\"button\" (click)=\"showQuickPreview = false; previewingTemplate = null\" aria-label=\"Close preview\">\n <svg width=\"16\" height=\"16\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\" aria-hidden=\"true\"><line x1=\"18\" y1=\"6\" x2=\"6\" y2=\"18\"/><line x1=\"6\" y1=\"6\" x2=\"18\" y2=\"18\"/></svg>\n </button>\n </div>\n <div class=\"sp-text-body\">{{ resolveMergeTags(previewingTemplate.body) }}</div>\n </div>\n </div>\n }\n `,\n styles: [`\n :host { display: flex; flex-direction: column; gap: 18px; }\n\n .sm-missing-banner {\n display: flex; align-items: center; gap: 8px; padding: 12px 14px;\n background: #fef3c7; border: 1px solid #fcd34d; border-radius: 8px;\n font-size: 0.8125rem; font-weight: 500; color: #92400e;\n svg { flex-shrink: 0; color: #d97706; }\n }\n @keyframes sm-spin { to { transform: rotate(360deg); } }\n\n .sm-group-label { display: block; font-size: 0.8125rem; font-weight: 600; color: #334155; margin-bottom: 6px; }\n .sm-to-card { border: 1px solid #e2e8f0; border-radius: 8px; padding: 12px 14px; display: flex; flex-direction: column; gap: 3px; }\n .sm-to-name { font-size: 0.9375rem; font-weight: 600; color: #1e293b; }\n .sm-to-detail { font-size: 0.8125rem; color: #64748b; }\n .sm-mode-row { display: flex; }\n .sm-field { display: flex; flex-direction: column; gap: 6px; }\n .sm-field-label { font-size: 0.8125rem; font-weight: 600; color: #374151; }\n .sm-docs-removed { color: #92400e; margin-top: 4px; }\n .sm-docs-note { margin: 0; font-size: 0.75rem; color: #64748b; }\n .sm-char-row { display: flex; align-items: center; justify-content: space-between; gap: 8px; }\n .sm-hint { font-size: 0.75rem; color: #94a3b8; }\n .sm-char-count { font-size: 0.75rem; color: #94a3b8; flex-shrink: 0; }\n .sm-char-warn { color: #ef4444; }\n\n .mv-section { display: flex; flex-direction: column; gap: 10px; padding: 14px 16px; background: #fffbeb; border: 1px solid #fde68a; border-radius: 10px; }\n .mv-hint { display: flex; align-items: center; gap: 6px; font-size: 0.8125rem; font-weight: 500; color: #92400e; margin: 0; svg { flex-shrink: 0; color: #d97706; } }\n .mv-row { display: flex; flex-direction: column; gap: 5px; }\n .mv-input-wrap { display: flex; gap: 6px; align-items: flex-end; }\n .mv-text-input { flex: 1; min-width: 0; }\n .mv-apply-btn {\n display: flex; align-items: center; justify-content: center;\n width: 34px; height: 34px; flex-shrink: 0; border: none; border-radius: 7px;\n background: #f59e0b; color: #fff; cursor: pointer; transition: background 0.12s;\n &:hover:not(:disabled) { background: #d97706; }\n &:disabled { opacity: 0.4; cursor: not-allowed; }\n }\n\n .attach-chips { display: flex; flex-direction: column; gap: 6px; margin-bottom: 8px; }\n .attach-chip { display: flex; align-items: center; gap: 8px; padding: 7px 10px; background: #f8fafc; border: 1px solid #e2e8f0; border-radius: 8px; svg { flex-shrink: 0; color: #64748b; } }\n .attach-name { font-size: 0.8125rem; font-weight: 500; color: #1e293b; flex: 1; min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }\n .attach-size { font-size: 0.75rem; color: #94a3b8; flex-shrink: 0; }\n .attach-remove { width: 18px; height: 18px; border: none; background: #e2e8f0; border-radius: 50%; cursor: pointer; font-size: 0.75rem; color: #64748b; display: flex; align-items: center; justify-content: center; flex-shrink: 0; &:hover { background: #cbd5e1; color: #1e293b; } }\n .attach-add-btn {\n display: inline-flex; align-items: center; gap: 6px; align-self: flex-start;\n padding: 6px 12px; border: 1px solid #e2e8f0; border-radius: 7px;\n background: #f8fafc; color: #475569; font-size: 0.8125rem; font-weight: 500;\n cursor: pointer; font-family: inherit; transition: all 0.12s;\n &:hover:not(:disabled) { border-color: #93c5fd; color: #2563eb; background: #eff6ff; }\n &:disabled { opacity: 0.5; cursor: not-allowed; }\n }\n .attach-spinner { width: 12px; height: 12px; border: 2px solid #cbd5e1; border-top-color: #3b82f6; border-radius: 50%; animation: sm-spin 0.6s linear infinite; flex-shrink: 0; }\n\n .tpl-preview-header { display: flex; align-items: center; justify-content: space-between; }\n .tpl-back-btn { display: inline-flex; align-items: center; gap: 5px; font-size: 0.8125rem; font-weight: 500; color: #475569; background: none; border: none; cursor: pointer; padding: 0; font-family: inherit; &:hover { color: #1e293b; } }\n .tpl-view-toggle { display: flex; border: 1px solid #e2e8f0; border-radius: 8px; overflow: hidden; }\n .tpl-toggle-btn { padding: 5px 14px; font-size: 0.8125rem; font-weight: 500; color: #64748b; background: #fff; border: none; cursor: pointer; font-family: inherit; transition: background 0.12s, color 0.12s; &.active { background: #1e293b; color: #fff; } &:not(.active):hover { background: #f8fafc; } }\n .tpl-body-preview { border: 1px solid #e2e8f0; border-radius: 10px; padding: 18px; background: #fff; font-size: 0.9rem; color: #1e293b; line-height: 1.7; min-height: 120px; overflow-y: auto; white-space: pre-wrap; }\n .tpl-body-raw { border: 1px solid #e2e8f0; border-radius: 10px; padding: 18px; background: #fff; font-size: 0.8125rem; color: #475569; line-height: 1.6; min-height: 120px; overflow-y: auto; white-space: pre-wrap; font-family: monospace; margin: 0; }\n\n .sp-backdrop { position: fixed; inset: 0; background: rgba(15,23,42,0.45); backdrop-filter: blur(3px); z-index: 9999; display: flex; align-items: center; justify-content: center; padding: 24px; }\n .sp-modal { background: #fff; border-radius: 14px; width: 100%; max-width: 480px; max-height: 70vh; display: flex; flex-direction: column; box-shadow: 0 20px 60px rgba(0,0,0,0.18); overflow: hidden; }\n .sp-modal-header { display: flex; align-items: center; justify-content: space-between; padding: 14px 18px; border-bottom: 1px solid #f1f5f9; flex-shrink: 0; }\n .sp-modal-title { display: flex; align-items: center; gap: 8px; font-size: 0.9375rem; font-weight: 700; color: #1e293b; }\n .sp-close-btn { display: flex; align-items: center; justify-content: center; width: 30px; height: 30px; border: 1px solid #e2e8f0; border-radius: 7px; background: none; cursor: pointer; color: #64748b; transition: background 0.12s, color 0.12s; &:hover { background: #f1f5f9; color: #1e293b; } }\n .tpl-send-preview-row { display: flex; justify-content: flex-end; }\n .sp-text-meta { padding: 12px 18px; border-bottom: 1px solid #f1f5f9; background: #f8fafc; flex-shrink: 0; }\n .sp-meta-row { display: flex; align-items: baseline; gap: 10px; }\n .sp-meta-label { font-size: 0.75rem; font-weight: 700; color: #94a3b8; letter-spacing: 0.04em; min-width: 50px; flex-shrink: 0; }\n .sp-meta-value { font-size: 0.875rem; color: #334155; }\n .sp-text-body { padding: 20px; overflow-y: auto; font-size: 0.9375rem; color: #1e293b; line-height: 1.75; white-space: pre-wrap; flex: 1; }\n\n @media (max-width: 480px) {\n .sp-backdrop { padding: 0; align-items: flex-end; }\n .sp-modal { max-height: 85dvh; border-radius: 14px 14px 0 0; }\n .sp-text-body { padding-bottom: calc(20px + env(safe-area-inset-bottom, 0px)); }\n }\n\n /* Phones: long unbroken values wrap inside their card instead of widening the panel. */\n .sm-to-card, .sm-to-name, .sm-to-detail, .sp-meta-value, .sp-text-body { min-width: 0; overflow-wrap: anywhere; }\n /* Touch: the attachment remove (x) and Attach Files controls reach a 44px target. */\n @media (pointer: coarse) {\n .attach-remove { position: relative; }\n .attach-remove::after { content: ''; position: absolute; inset: -13px; }\n .attach-add-btn { min-height: 44px; }\n .sp-close-btn { width: 44px; height: 44px; }\n }\n `],\n})\nexport class SendTextComponent implements OnInit {\n protected readonly formatPhone = formatPhone;\n\n private readonly destroyRef = inject(DestroyRef);\n private readonly http = inject(HttpClient);\n /** API root from FORGEROUTE_CONFIG (e.g. a cross-origin prod API); falls back to same-origin `/api`. */\n private readonly apiBase = inject(FORGEROUTE_CONFIG, { optional: true })?.baseUrl ?? '/api';\n private readonly toast = inject(ToastService);\n readonly cdr = inject(ChangeDetectorRef);\n\n readonly contactName = input('');\n readonly contactPhone = input('');\n /** The contact's email — fills `{{email}}` (a blank email makes `{{email}}` a prompted field). */\n readonly contactEmail = input('');\n readonly contactId = input('');\n /** Prefills Message in \"Create Message\" mode. */\n readonly defaultBody = input('');\n /** Extra merge variables (e.g. `estimate_number`) used in templates and the preview. */\n readonly context = input<Record<string, string>>({});\n /**\n * Sends through the host instead of `POST <baseUrl>/text-messages` — e.g. a page-specific endpoint that\n * also marks a record Sent. Loading, toasts, `sent` and the double-send guard behave the same.\n */\n readonly sendFn = input<((payload: SendTextPayload) => Observable<unknown>) | null>(null);\n /** Shows the \"Documents\" section (search your documents; they are texted as a link). Off = the form is unchanged. */\n readonly documents = input(false);\n /** false hides Attach Image and \"Schedule for later\" (for a host whose send function can't deliver them). */\n readonly attachmentsAndScheduling = input(true);\n /** Purposes the app's backend supports for documents (default all). The text form only sends `ESIGN`; without it the section is unavailable. */\n readonly documentPurposes = input<DocumentSendPurpose[]>(['ESIGN', 'ATTACH', 'BOTH']);\n /** Documents already chosen in the Documents section when the form opens. */\n readonly initialDocuments = input<{ id: string; title: string; fileName?: string | null; hasFile?: boolean }[]>([]);\n\n readonly sent = output<void>();\n readonly phoneAdded = output<string>();\n readonly validChange = output<boolean>();\n\n readonly MAX_LENGTH = MAX_SMS_LENGTH;\n\n mode: 'compose' | 'template' = 'compose';\n body = '';\n templateId: string | null = null;\n previewFormat: 'preview' | 'raw' = 'preview';\n previewingTemplate: { subject: string | null; body: string } | null = null;\n showQuickPreview = false;\n /** \"View Send Preview\" for the selected template. */\n showSendPreview = false;\n customVars: Record<string, string> = {};\n pendingInputs: Record<string, string> = {};\n loading = false;\n showSchedule = false;\n scheduledAt = '';\n pendingPhone = '';\n /** What the document section holds (null / empty = no documents) and whether it currently breaks a rule. */\n documentsValue: DocumentSendValue | null = null;\n documentsInvalid = false;\n\n readonly mediaAttachment = signal<{ url: string; mediaType: string; mediaFileName: string; sizeLabel: string } | null>(null);\n readonly mediaUploading = signal(false);\n readonly savingPhone = signal(false);\n\n readonly modeTabs: PillTab[] = [\n { key: 'compose', label: 'Create Message' },\n { key: 'template', label: 'Use Existing Template' },\n ];\n\n /** Market values (`area`, `median_price`, `dom`, `new_listings`) — only the ones `/service-areas` returned. */\n private marketVars: Record<string, string> = {};\n\n ngOnInit() {\n this.http.get<any>(`${this.apiBase}/service-areas?size=100`).pipe(takeUntilDestroyed(this.destroyRef)).subscribe({\n next: (resp) => {\n this.marketVars = marketVarsFrom(resp);\n // Fields the market values now fill stop being prompted; preview, modal and payload all re-read them.\n this.notifyValid();\n },\n });\n }\n\n /** Default last applied to compose mode — it only replaces text the user hasn't edited. */\n private _appliedDefaultBody = '';\n\n constructor() {\n effect(() => {\n this.contactName(); this.contactPhone(); this.contactId(); this.context();\n untracked(() => this.notifyValid());\n });\n effect(() => {\n const body = this.defaultBody();\n untracked(() => {\n if (this.mode === 'compose' && this.body === this._appliedDefaultBody) this.body = body.slice(0, this.MAX_LENGTH);\n this._appliedDefaultBody = body.slice(0, this.MAX_LENGTH);\n this.notifyValid();\n });\n });\n }\n\n setPreviewFormat(f: 'preview' | 'raw') { this.previewFormat = f; this.cdr.markForCheck(); }\n\n get pendingPhoneValid(): boolean { return PHONE_RE.test(this.pendingPhone.trim()); }\n\n /** True when the sender chose at least one document (only possible when the `documents` input is on). */\n get hasDocuments(): boolean { return this.documents() && (this.documentsValue?.documentIds.length ?? 0) > 0; }\n\n /** Set when choosing the first document removed something the sender had already added - said out loud (aria-live), never silently. */\n docsRemovedNote = '';\n onDocumentsChange(v: DocumentSendValue) {\n this.documentsValue = v;\n if (this.hasDocuments) {\n const removed = [this.mediaAttachment() ? 'your image' : '', this.showSchedule ? 'the schedule' : ''].filter(Boolean);\n if (removed.length > 0) this.docsRemovedNote = `We removed ${removed.join(' and ')} because documents are sent as a request.`;\n this.showSchedule = false; this.scheduledAt = ''; this.mediaAttachment.set(null);\n } else {\n this.docsRemovedNote = '';\n }\n this.notifyValid();\n }\n onDocumentsInvalid(bad: boolean) { this.documentsInvalid = bad; this.notifyValid(); }\n\n get canSend(): boolean {\n if (this.documents() && this.documentsInvalid) return false;\n const hasBody = this.body.trim().length > 0 && this.unresolvedVars.length === 0 && !hasLeftoverTags(this.resolvedBody);\n if (this.showSchedule) return hasBody && !!this.scheduledAt && new Date(this.scheduledAt).getTime() > Date.now();\n return hasBody;\n }\n\n get minScheduleTime(): string { return new Date(Date.now() + 60000).toISOString().slice(0, 16); }\n\n /**\n * Values every tag can resolve from before the user types anything: the contact (name, first / last name, phone,\n * email), the date, the market values `/service-areas` returned and the host's `context` — non-blank values only,\n * later sources winning.\n */\n private get knownVars(): Record<string, string> {\n const name = this.contactName().trim();\n const parts = name.split(/\\s+/);\n return nonBlankValues(\n {\n first_name: parts[0] ?? '', last_name: parts.slice(1).join(' '), full_name: name, name,\n phone: this.contactPhone(), email: this.contactEmail(),\n },\n dateVars(), this.marketVars, this.context(), this.customVars,\n );\n }\n\n /** Merges `text` with every known value plus what the user typed — the ONE merge the pane, modal and payload use. */\n resolveMergeTags(text: string): string {\n return applyMergeTags(text, { ...this.knownVars, ...this.liveVars });\n }\n\n /** Applied values plus anything typed into a missing-field input — so the preview updates as the user types. */\n private get liveVars(): Record<string, string> {\n const live: Record<string, string> = { ...this.customVars };\n for (const [k, v] of Object.entries(this.pendingInputs)) { if ((v ?? '').trim()) live[k] = v.trim(); }\n return live;\n }\n\n /** The body exactly as it will be sent (live: recomputed on every read, never cached). */\n get resolvedBody(): string { return this.resolveMergeTags(this.body); }\n\n /** Template fields the user still has to fill — tags with no non-blank known value and nothing applied. */\n get missingVars(): string[] {\n const known = this.knownVars;\n return mergeTagKeys(this.body).filter(k => !known[k]);\n }\n\n /** Compose mode: a `{{…}}` the form can't fill blocks sending (the server would refuse it). */\n get composeTagError(): string {\n return this.mode === 'compose' && hasLeftoverTags(this.resolvedBody)\n ? 'Replace every {{…}} field with text before sending.' : '';\n }\n\n /** Missing fields with nothing typed yet — Send stays disabled until this is empty. */\n get unresolvedVars(): string[] {\n return this.missingVars.filter(k => !(this.pendingInputs[k] ?? '').trim());\n }\n\n /**\n * Applies every typed missing-field value (what the preview already shows) before sending. Never saves\n * to the contact — only the explicit \"+\" / Enter apply does that.\n */\n private applyPendingVars() {\n for (const k of this.missingVars) { if ((this.pendingInputs[k] ?? '').trim()) this.applyVar(k, false); }\n }\n\n formatVarLabel(key: string): string { return formatVarLabel(key); }\n\n /** \"View Send Preview\": the selected template, merged exactly as it will be sent. */\n openSendPreview() { this.showSendPreview = true; this.cdr.markForCheck(); }\n closeSendPreview() { this.showSendPreview = false; this.cdr.markForCheck(); }\n\n /** The picker's eye button: previews that template without selecting it. */\n openQuickPreview(tpl: { subject: string | null; body: string }) {\n this.previewingTemplate = tpl; this.showQuickPreview = true; this.cdr.markForCheck();\n }\n\n /** Applies a typed missing-field value. The explicit \"+\" / Enter apply (`persist`) also saves phone / address to the contact. */\n applyVar(key: string, persist = true) {\n const val = (this.pendingInputs[key] ?? '').trim();\n if (!val) return;\n this.customVars = { ...this.customVars, [key]: val };\n this.pendingInputs[key] = '';\n if (persist && this.contactId() && (key === 'phone' || key === 'address')) {\n this.http.patch(`${this.apiBase}/contacts/${this.contactId()}`, { [key]: val }).subscribe();\n }\n this.notifyValid();\n }\n\n clearTemplate() {\n this.templateId = null; this.body = '';\n this.previewFormat = 'preview'; this.customVars = {}; this.pendingInputs = {};\n this.notifyValid();\n }\n\n notifyValid() {\n this.validChange.emit(!!this.contactPhone() && this.canSend);\n this.cdr.detectChanges();\n }\n\n setMode(key: string) {\n const next = key as 'compose' | 'template';\n if (next === 'compose') {\n this.templateId = null; this.body = this._appliedDefaultBody;\n this.customVars = {}; this.pendingInputs = {}; this.previewFormat = 'preview';\n } else {\n this.body = '';\n }\n this.showSchedule = false; this.scheduledAt = '';\n this.mode = next;\n this.notifyValid();\n }\n\n savePhone() {\n if (!this.pendingPhoneValid || this.savingPhone()) return;\n this.savingPhone.set(true);\n this.cdr.detectChanges();\n this.phoneAdded.emit(this.pendingPhone.trim());\n this.savingPhone.set(false);\n this.cdr.detectChanges();\n }\n\n onTemplateSelected(tpl: TemplateItem) {\n this.templateId = tpl.id;\n this.body = tpl.body;\n this.previewFormat = 'preview'; this.customVars = {}; this.pendingInputs = {};\n for (const k of this.missingVars) { this.pendingInputs[k] = ''; }\n this.notifyValid();\n }\n\n onFileSelected(event: Event) {\n const file = (event.target as HTMLInputElement).files?.[0];\n if (!file) return;\n const sizeLabel = file.size < 1024 * 1024\n ? `${Math.round(file.size / 1024)} KB`\n : `${(file.size / (1024 * 1024)).toFixed(1)} MB`;\n this.mediaUploading.set(true);\n this.cdr.detectChanges();\n const form = new FormData();\n form.append('file', file);\n this.http.post<{ url: string; mediaType: string; mediaFileName: string }>(`${this.apiBase}/text-messages/media`, form)\n .pipe(takeUntilDestroyed(this.destroyRef))\n .subscribe({\n next: (res) => {\n this.mediaAttachment.set({ url: res.url, mediaType: res.mediaType, mediaFileName: res.mediaFileName, sizeLabel });\n this.mediaUploading.set(false);\n this.cdr.detectChanges();\n },\n error: () => {\n this.toast.error('Failed to upload attachment');\n this.mediaUploading.set(false);\n this.cdr.detectChanges();\n },\n });\n (event.target as HTMLInputElement).value = '';\n }\n\n removeMedia() { this.mediaAttachment.set(null); }\n\n insertVideo(v: VideoMessage) {\n if (!v.shareLink) return;\n const sep = this.body.trim() ? '\\n\\n' : '';\n this.body = (this.body + sep + `Watch \"${v.title}\": ${v.shareLink}`).slice(0, this.MAX_LENGTH);\n this.notifyValid();\n }\n\n submit() {\n if (!this.canSend || this.loading || !this.contactPhone()) return;\n if (this.mode === 'template') this.applyPendingVars();\n this.loading = true;\n this.cdr.detectChanges();\n const media = this.mediaAttachment();\n const payload: SendTextPayload = {\n contactName: this.contactName(),\n contactPhone: this.contactPhone(),\n // Exactly what the Preview pane and the send-preview modal show (compose text is merged the same way).\n body: this.resolvedBody,\n contactId: this.contactId() || undefined,\n scheduledAt: this.showSchedule && this.scheduledAt ? new Date(this.scheduledAt).toISOString() : undefined,\n mediaUrl: media?.url ?? undefined,\n mediaType: media?.mediaType ?? undefined,\n mediaFileName: media?.mediaFileName ?? undefined,\n };\n if (this.mode === 'template' && this.templateId) payload.templateId = this.templateId;\n const docs = this.hasDocuments ? this.documentsValue : null;\n if (docs) {\n // A document request is texted as a link: no image, no scheduling, and always e-signature.\n payload.documents = { documentIds: docs.documentIds, purpose: 'ESIGN', delivery: 'TEXT' };\n payload.mediaUrl = undefined; payload.mediaType = undefined; payload.mediaFileName = undefined;\n payload.scheduledAt = undefined;\n }\n const custom = this.sendFn();\n const req$ = custom ? custom(payload)\n : docs ? sendDocumentRequest(this.http, this.apiBase, {\n subject: DEFAULT_DOCUMENT_SUBJECT, message: payload.body,\n customerId: payload.contactId, customerName: payload.contactName, customerEmail: this.contactEmail() || undefined,\n customerPhone: payload.contactPhone,\n documentIds: docs.documentIds, purpose: 'ESIGN', delivery: 'TEXT',\n }, payload.templateId)\n : this.http.post(`${this.apiBase}/text-messages`, payload);\n req$.pipe(takeUntilDestroyed(this.destroyRef)).subscribe({\n next: () => {\n this.toast.show(this.showSchedule ? 'Message scheduled' : 'Text message sent');\n this.loading = false; this.mediaAttachment.set(null);\n this.sent.emit(); this.cdr.detectChanges();\n },\n error: (err: unknown) => {\n // Default endpoint keeps its fixed message; a host endpoint may return a specific one.\n this.toast.error(custom || docs ? getErrorMessage(err, 'text message', 'send') : 'Failed to send text message');\n this.loading = false; this.cdr.detectChanges();\n },\n });\n }\n}\n","import {\n Component, input, output, effect, untracked, inject, DestroyRef,\n viewChild, ChangeDetectionStrategy, signal, computed,\n} from '@angular/core';\nimport { takeUntilDestroyed } from '@angular/core/rxjs-interop';\nimport { Subject, switchMap, of, catchError } from 'rxjs';\nimport { ComboboxComponent } from '../combobox/combobox.component';\nimport type { ComboboxOption, ComboboxSelection } from '../combobox/combobox.component';\nimport { SidePanelComponent } from '../side-panel/side-panel.component';\nimport { ToggleBadgeComponent } from '../toggle-badge/toggle-badge.component';\nimport { ContactPickerComponent } from '../contact-picker/contact-picker.component';\nimport type { ContactLookup } from '../contact-picker/contact-picker.component';\nimport type { Observable } from 'rxjs';\nimport type { DocumentSendPurpose } from '../document-send-options/document-send-options.component';\nimport { SendEmailComponent } from './send-email.component';\nimport type { SendEmailPayload } from './send-email.component';\nimport { SendTextComponent } from './send-text.component';\nimport type { SendTextPayload } from './send-text.component';\n\n/** One match of the host's `jobSearch` function; `description` is the small second line (e.g. the customer). */\nexport interface JobSearchResult { id: string; name: string; description?: string | null }\n\nexport interface MessageContact {\n id: string;\n name: string;\n email?: string | null;\n phone?: string | null;\n smsOptedIn?: boolean | null;\n}\n\n@Component({\n selector: 'ui-send-message-panel',\n standalone: true,\n changeDetection: ChangeDetectionStrategy.OnPush,\n imports: [SidePanelComponent, ToggleBadgeComponent, SendEmailComponent, SendTextComponent, ContactPickerComponent, ComboboxComponent],\n template: `\n <ui-side-panel\n [open]=\"open()\"\n [title]=\"title()\"\n cancelLabel=\"Cancel\"\n [primaryLabel]=\"!loading() && activeContact() ? (channel() === 'email' ? 'Send Email' : 'Send Text') : ''\"\n primaryVariant=\"warning\"\n [primaryDisabled]=\"!valid()\"\n (closed)=\"onClose()\"\n (primaryClick)=\"submit()\">\n\n @if (loading()) {\n <div class=\"msg-skeleton\">\n <div class=\"skeleton-line skeleton-line--wide\"></div>\n <div class=\"skeleton-line skeleton-line--medium\"></div>\n <div class=\"skeleton-line skeleton-line--narrow\"></div>\n </div>\n }\n\n @if (!loading() && !activeContact()) {\n <div class=\"msg-step\">\n <span class=\"msg-step-badge\">1</span>\n <span class=\"msg-step-text\">Who would you like to message?</span>\n </div>\n <ui-contact-picker\n placeholder=\"Search by contact name...\"\n (contactSelected)=\"onContactSelected($event)\" />\n }\n\n @if (!loading() && activeContact()) {\n @if (textEnabled() && activeContact()?.smsOptedIn) {\n <div class=\"msg-channel-row\">\n <label class=\"form-label msg-via-label\">Send via</label>\n <div class=\"msg-via-tabs\">\n <ui-toggle-badge [active]=\"channel() === 'email'\" (toggle)=\"onChannelChange('email')\">\n <svg width=\"14\" height=\"14\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\" aria-hidden=\"true\"><rect x=\"2\" y=\"4\" width=\"20\" height=\"16\" rx=\"2\"/><path d=\"m22 7-8.97 5.7a1.94 1.94 0 0 1-2.06 0L2 7\"/></svg>\n Email\n </ui-toggle-badge>\n <ui-toggle-badge [active]=\"channel() === 'text'\" (toggle)=\"onChannelChange('text')\">\n <svg width=\"14\" height=\"14\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\" aria-hidden=\"true\"><path d=\"M21 15a2 2 0 0 1-2 2H7l-4 4V5a2 2 0 0 1 2-2h14a2 2 0 0 1 2 2z\"/></svg>\n Text\n </ui-toggle-badge>\n </div>\n </div>\n }\n\n @for (n of contactNotices(); track n) {\n <div class=\"msg-sms-notice\">\n <svg width=\"14\" height=\"14\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\" aria-hidden=\"true\"><circle cx=\"12\" cy=\"12\" r=\"10\"/><path d=\"M12 8v4m0 4h.01\"/></svg>\n {{ n }}\n </div>\n }\n\n <!-- Optional job link (hidden unless the host turns it on). -->\n @if (showJobPicker()) {\n @for (epoch of [formEpoch()]; track epoch) {\n <div class=\"msg-job\">\n <ui-combobox\n [label]=\"jobLabel()\" placeholder=\"Search by job name…\" [hint]=\"jobHint()\"\n [options]=\"jobOptions()\" [loading]=\"jobLoading()\" noResultsText=\"No jobs found\" [minChars]=\"2\" [debounce]=\"300\"\n [value]=\"jobValue()\"\n (searchChange)=\"jobSearch$.next($event)\" (valueChange)=\"onJobPick($event)\" />\n </div>\n }\n }\n\n <!-- Keyed on formEpoch: a fresh open or another contact gets brand-new forms (no stale draft / template). -->\n @for (epoch of [formEpoch()]; track epoch) {\n @if (channel() === 'email') {\n <ui-send-email\n #sendEmailRef\n [contactName]=\"activeContact()!.name\"\n [contactEmail]=\"activeContact()!.email ?? ''\"\n [contactPhone]=\"activeContact()!.phone ?? ''\"\n [contactId]=\"activeContact()!.id\"\n [defaultSubject]=\"defaultSubject()\"\n [defaultBody]=\"defaultBody()\"\n [context]=\"context()\"\n [sendFn]=\"sendEmail()\"\n [showReceiptOption]=\"showReceiptOption()\"\n [receiptLabel]=\"receiptLabel()\"\n [receiptDefault]=\"receiptDefault()\"\n [documents]=\"documentsOn()\"\n [documentPurposes]=\"documentPurposes()\"\n [initialDocuments]=\"initialDocuments()\"\n [showCc]=\"showCc()\"\n [attachmentsAndScheduling]=\"attachmentsAndScheduling()\"\n (sent)=\"onSent()\"\n (emailAdded)=\"onEmailAdded($event)\"\n (validChange)=\"valid.set($event)\" />\n } @else {\n <ui-send-text\n #sendTextRef\n [contactName]=\"activeContact()!.name\"\n [contactPhone]=\"activeContact()!.phone ?? ''\"\n [contactEmail]=\"activeContact()!.email ?? ''\"\n [contactId]=\"activeContact()!.id\"\n [defaultBody]=\"defaultMessage()\"\n [context]=\"context()\"\n [sendFn]=\"sendText()\"\n [attachmentsAndScheduling]=\"attachmentsAndScheduling()\"\n [documents]=\"documentsOn()\"\n [documentPurposes]=\"documentPurposes()\"\n [initialDocuments]=\"initialDocuments()\"\n (sent)=\"onSent()\"\n (phoneAdded)=\"onPhoneAdded($event)\"\n (validChange)=\"valid.set($event)\" />\n }\n }\n }\n\n </ui-side-panel>\n `,\n styles: [`\n .msg-step { display: flex; align-items: center; gap: 10px; margin-bottom: 14px; }\n .msg-step-badge {\n display: flex; align-items: center; justify-content: center;\n width: 24px; height: 24px; flex-shrink: 0;\n border-radius: 50%; background: #1e293b; color: #fff;\n font-size: 0.75rem; font-weight: 700;\n }\n .msg-step-text { font-size: 0.9375rem; font-weight: 600; color: #1e293b; }\n .msg-channel-row { display: flex; align-items: center; gap: 10px; margin-bottom: 16px; flex-wrap: wrap; }\n .msg-sms-notice { display: flex; align-items: center; gap: 8px; margin-bottom: 16px; padding: 10px 12px; border-radius: 8px; background: #f8fafc; border: 1px solid #e2e8f0; font-size: 0.8125rem; color: #64748b; }\n .msg-job { margin-bottom: 16px; }\n .msg-via-tabs { display: flex; gap: 6px; flex-wrap: wrap; }\n .msg-skeleton { display: flex; flex-direction: column; gap: 12px; padding: 4px 0; }\n .skeleton-line { height: 14px; border-radius: 6px; background: linear-gradient(90deg, #e2e8f0 25%, #f1f5f9 50%, #e2e8f0 75%); background-size: 200% 100%; animation: skeleton-shimmer 1.4s infinite; }\n .skeleton-line--wide { width: 100%; }\n .skeleton-line--medium { width: 70%; }\n .skeleton-line--narrow { width: 45%; }\n @keyframes skeleton-shimmer { 0% { background-position: 200% 0; } 100% { background-position: -200% 0; } }\n `],\n})\nexport class SendMessagePanelComponent {\n readonly open = input(false);\n readonly contact = input<MessageContact | null>(null);\n readonly loading = input(false);\n /** Pass true when Twilio / SMS is enabled in the consuming app's settings. */\n readonly textEnabled = input(false);\n /** Panel header. */\n readonly title = input('Send Message');\n /** Prefill for the email Subject / Message and the text Message in \"Create Message\" mode. */\n readonly defaultSubject = input('');\n readonly defaultBody = input('');\n readonly defaultMessage = input('');\n /** Extra merge variables (e.g. `estimate_number`, `estimate_total`) for templates and the preview. */\n readonly context = input<Record<string, string>>({});\n /**\n * Page-specific sending: when set, the email / text form calls this instead of POSTing to the default\n * `/email-messages` / `/text-messages` endpoint (e.g. an endpoint that also marks an estimate Sent).\n */\n readonly sendEmail = input<((payload: SendEmailPayload) => Observable<unknown>) | null>(null);\n readonly sendText = input<((payload: SendTextPayload) => Observable<unknown>) | null>(null);\n /** Email tab only: shows an \"include receipt\" checkbox; the choice is sent as `SendEmailPayload.includeReceipt`. */\n readonly showReceiptOption = input(false);\n readonly receiptLabel = input('Include payment receipt');\n readonly receiptDefault = input(false);\n /**\n * Adds a \"Documents\" section to the email and text forms: the sender searches the company's documents and sends them\n * with the message (e-signature link, attachment or both on email; a link on text). Needs the standard documents routes —\n * see docs/send-documents-contract.md. Off (default) = both forms are exactly as before.\n */\n readonly documents = input(false);\n /** Pass false for a user who may not send documents (e.g. read-only): the Documents section is hidden even when `documents` is on. */\n readonly documentsEnabled = input(true);\n /** Email tab only: adds a CC field; the addresses are sent as `SendEmailPayload.cc`. */\n readonly showCc = input(false);\n /** false hides Attach Files / Attach Image and \"Schedule for later\" in both forms (a host `sendEmail` / `sendText` that can't deliver them). */\n readonly attachmentsAndScheduling = input(true);\n /** Purposes the app's backend supports for documents (default all three); `['ESIGN']` hides the purpose select. */\n readonly documentPurposes = input<DocumentSendPurpose[]>(['ESIGN', 'ATTACH', 'BOTH']);\n /** Documents already chosen when the panel opens (e.g. the row \"Send Document\" was clicked on); the user can change them. */\n readonly initialDocuments = input<{ id: string; title: string; fileName?: string | null; hasFile?: boolean }[]>([]);\n /** The Documents section is shown only when the host turned it on AND the user may send documents. */\n protected readonly documentsOn = computed(() => this.documents() && this.documentsEnabled());\n\n /**\n * Shows an optional \"Job\" search under the contact (off by default): the sender may link the message to one of the\n * company's jobs (found with the host's `jobSearch` function). The pick is reported through `jobSelected`; it is cleared whenever the\n * panel opens or another contact is chosen. The panel itself sends nothing about it — the host's `sendEmail` / `sendText`\n * function adds it to its request.\n */\n readonly showJobPicker = input(false);\n readonly jobLabel = input('Job');\n readonly jobHint = input('');\n /**\n * Looks jobs up for the Job field (the panel knows nothing about the app's API): called with the typed text (2+ characters,\n * debounced); returns the matches. Required for `showJobPicker` to find anything.\n */\n readonly jobSearch = input<((query: string) => Observable<JobSearchResult[]>) | null>(null);\n readonly jobSelected = output<{ id: string; name: string } | null>();\n\n readonly closed = output<void>();\n readonly sent = output<void>();\n readonly emailAdded = output<string>();\n readonly phoneAdded = output<string>();\n\n readonly sendEmailRef = viewChild<SendEmailComponent>('sendEmailRef');\n readonly sendTextRef = viewChild<SendTextComponent>('sendTextRef');\n\n private readonly destroyRef = inject(DestroyRef);\n readonly jobSearch$ = new Subject<string>();\n readonly jobOptions = signal<ComboboxOption[]>([]);\n readonly jobLoading = signal(false);\n private readonly pickedJob = signal<{ id: string; name: string } | null>(null);\n readonly jobValue = computed((): ComboboxSelection | null => {\n const j = this.pickedJob();\n return j ? { value: j.id, label: j.name } : null;\n });\n\n onJobPick(o: ComboboxOption | null) {\n const job = o ? { id: o.value, name: o.label } : null;\n this.jobOptions.set([]);\n this.pickedJob.set(job);\n this.jobSelected.emit(job);\n }\n\n /** A new form (fresh open / another contact) starts without a linked job. */\n private resetJob() {\n if (this.pickedJob()) { this.pickedJob.set(null); this.jobSelected.emit(null); }\n this.jobOptions.set([]);\n }\n\n readonly activeContact = signal<MessageContact | null>(null);\n\n /** What the sender should know about the picked contact before sending (why a channel is missing, what to add). */\n readonly contactNotices = computed((): string[] => {\n const c = this.activeContact();\n if (!c) return [];\n const notices: string[] = [];\n const hasEmail = !!c.email, hasPhone = !!c.phone;\n if (!this.textEnabled()) {\n notices.push('Text messaging is not enabled. Connect Twilio in Comm Settings to send texts.');\n } else if (!c.smsOptedIn) {\n notices.push(`${c.name} has not opted in to receive text messages, so this can only be sent by email.${!hasEmail && hasPhone ? ' Add an email address below to send.' : ''}`);\n } else if (!hasPhone) {\n notices.push(`${c.name} has no phone number on file, so a text needs one added first.`);\n }\n if (!hasEmail && !hasPhone) {\n notices.push(`${c.name} has no email address or phone number on file. Add an email address below to send.`);\n }\n return notices;\n });\n readonly channel = signal<'email' | 'text'>('email');\n readonly valid = signal(false);\n /** Bumped on every fresh open / new contact — re-creates the email and text forms so no draft carries over. */\n readonly formEpoch = signal(0);\n private _prevOpen = false;\n\n constructor() {\n this.jobSearch$.pipe(\n switchMap(q => {\n const search = this.jobSearch();\n if (q.length < 2 || !search) { this.jobLoading.set(false); return of([] as JobSearchResult[]); }\n this.jobLoading.set(true);\n return search(q).pipe(catchError(() => of([] as JobSearchResult[])));\n }),\n takeUntilDestroyed(this.destroyRef),\n ).subscribe(rows => {\n this.jobOptions.set(rows.map(j => ({ value: j.id, label: j.name, description: j.description || undefined })));\n this.jobLoading.set(false);\n });\n\n effect(() => {\n const openVal = this.open();\n const contactVal = this.contact();\n const loadingVal = this.loading();\n const openedNow = openVal && !this._prevOpen;\n this._prevOpen = openVal;\n const contactArrivedWhileOpen = openVal && !loadingVal && !!contactVal;\n if (!openedNow && !contactArrivedWhileOpen) return;\n const c = contactVal ?? null;\n const active = untracked(() => this.activeContact());\n // Same contact updated while open (email / phone added): take the new details, keep channel + form state.\n if (!openedNow && active && c && active.id === c.id) {\n this.activeContact.set(c);\n return;\n }\n this.activeContact.set(c);\n const preferText = untracked(() => this.textEnabled()) && !!c?.smsOptedIn && !c?.email && !!c?.phone;\n this.channel.set(preferText ? 'text' : 'email');\n this.valid.set(false);\n this.resetJob();\n this.formEpoch.update(n => n + 1);\n });\n }\n\n onContactSelected(c: ContactLookup | null) {\n this.activeContact.set(c);\n this.channel.set(c?.email || !this.textEnabled() || !c?.smsOptedIn ? 'email' : 'text');\n this.valid.set(false);\n this.resetJob();\n this.formEpoch.update(n => n + 1);\n }\n\n onChannelChange(ch: 'email' | 'text') {\n this.channel.set(ch);\n this.valid.set(false);\n }\n\n onEmailAdded(email: string) {\n const c = this.activeContact();\n if (c) this.activeContact.set({ ...c, email });\n this.emailAdded.emit(email);\n }\n\n onPhoneAdded(phone: string) {\n const c = this.activeContact();\n if (c) this.activeContact.set({ ...c, phone });\n this.phoneAdded.emit(phone);\n }\n\n submit() {\n if (this.channel() === 'email') {\n this.sendEmailRef()?.submit();\n } else {\n this.sendTextRef()?.submit();\n }\n }\n\n onSent() { this.sent.emit(); this.closed.emit(); }\n onClose() { this.closed.emit(); }\n}\n","import {\n Component, input, output, effect, untracked,\n ChangeDetectionStrategy, signal, computed,\n} from '@angular/core';\nimport { FormsModule } from '@angular/forms';\nimport { SidePanelComponent } from '../side-panel/side-panel.component';\nimport { ToggleBadgeComponent } from '../toggle-badge/toggle-badge.component';\nimport { TextInputComponent } from '../text-input/text-input.component';\nimport { TextareaInputComponent } from '../textarea-input/textarea-input.component';\n\nexport interface BulkMessageContact {\n id: string;\n name: string;\n email: string | null;\n phone: string | null;\n}\n\nexport interface BulkMessageSendEvent {\n channel: 'email' | 'text';\n subject: string;\n body: string;\n contacts: BulkMessageContact[];\n}\n\n@Component({\n selector: 'ui-bulk-message-panel',\n standalone: true,\n changeDetection: ChangeDetectionStrategy.OnPush,\n imports: [FormsModule, SidePanelComponent, ToggleBadgeComponent, TextInputComponent, TextareaInputComponent],\n template: `\n <ui-side-panel\n [open]=\"open()\"\n title=\"Send Bulk Message\"\n [primaryLabel]=\"primaryLabel()\"\n primaryVariant=\"warning\"\n [primaryLoading]=\"sending()\"\n [primaryDisabled]=\"!canSend()\"\n (closed)=\"closed.emit()\"\n (primaryClick)=\"onSend()\">\n\n <div class=\"msg-channel-row\">\n <label class=\"form-label\">Send via</label>\n <div class=\"msg-via-tabs\">\n <ui-toggle-badge [active]=\"channel() === 'email'\" (toggle)=\"onChannelChange('email')\">\n <svg width=\"14\" height=\"14\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\" aria-hidden=\"true\"><rect x=\"2\" y=\"4\" width=\"20\" height=\"16\" rx=\"2\"/><path d=\"m22 7-8.97 5.7a1.94 1.94 0 0 1-2.06 0L2 7\"/></svg>\n Email\n </ui-toggle-badge>\n @if (textEnabled()) {\n <ui-toggle-badge [active]=\"channel() === 'text'\" (toggle)=\"onChannelChange('text')\">\n <svg width=\"14\" height=\"14\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\" aria-hidden=\"true\"><path d=\"M21 15a2 2 0 0 1-2 2H7l-4 4V5a2 2 0 0 1 2-2h14a2 2 0 0 1 2 2z\"/></svg>\n Text\n </ui-toggle-badge>\n }\n </div>\n </div>\n\n <p class=\"bulk-msg-recipients\">\n Sending to <strong>{{ activeContacts().length }}</strong> contact{{ activeContacts().length === 1 ? '' : 's' }}\n @if (skippedCount() > 0) {\n <span class=\"bulk-msg-skipped\">— {{ skippedCount() }} skipped (no {{ channel() === 'email' ? 'email' : 'phone' }})</span>\n }\n </p>\n\n @if (channel() === 'email') {\n <ui-text-input label=\"Subject\" [(ngModel)]=\"subject\" placeholder=\"Subject line\" />\n }\n <ui-textarea-input label=\"Message\" [rows]=\"6\" [(ngModel)]=\"body\" placeholder=\"Write your message...\" />\n\n </ui-side-panel>\n `,\n styles: [`\n .msg-channel-row { display: flex; align-items: center; gap: 10px; margin-bottom: 16px; flex-wrap: wrap; }\n .msg-via-tabs { display: flex; gap: 6px; flex-wrap: wrap; }\n .form-label { font-size: 0.75rem; font-weight: 600; color: #64748b; text-transform: uppercase; letter-spacing: 0.05em; white-space: nowrap; }\n .bulk-msg-recipients { font-size: 0.9375rem; color: #334155; margin: 0 0 16px; }\n .bulk-msg-skipped { color: #94a3b8; font-size: 0.875rem; }\n `],\n})\nexport class BulkMessagePanelComponent {\n readonly open = input(false);\n readonly contacts = input<BulkMessageContact[]>([]);\n readonly textEnabled = input(false);\n readonly sending = input(false);\n\n readonly closed = output<void>();\n readonly send = output<BulkMessageSendEvent>();\n\n readonly channel = signal<'email' | 'text'>('email');\n\n private readonly _subject = signal('');\n private readonly _body = signal('');\n\n get subject() { return this._subject(); }\n set subject(v: string) { this._subject.set(v); }\n\n get body() { return this._body(); }\n set body(v: string) { this._body.set(v); }\n\n readonly emailContacts = computed(() => this.contacts().filter(c => !!c.email));\n readonly textContacts = computed(() => this.contacts().filter(c => !!c.phone));\n readonly activeContacts = computed(() => this.channel() === 'email' ? this.emailContacts() : this.textContacts());\n readonly skippedCount = computed(() => this.contacts().length - this.activeContacts().length);\n\n readonly canSend = computed(() => {\n if (this.activeContacts().length === 0) return false;\n if (this.channel() === 'email') return !!this._subject().trim() && !!this._body().trim();\n return !!this._body().trim();\n });\n\n readonly primaryLabel = computed(() => {\n const n = this.activeContacts().length;\n return `Send to ${n} ${n === 1 ? 'Contact' : 'Contacts'}`;\n });\n\n private _prevOpen = false;\n\n constructor() {\n effect(() => {\n const isOpen = this.open();\n const openedNow = isOpen && !this._prevOpen;\n this._prevOpen = isOpen;\n if (openedNow) {\n untracked(() => {\n const hasEmail = this.contacts().some(c => !!c.email);\n this.channel.set(hasEmail ? 'email' : 'text');\n this._subject.set('');\n this._body.set('');\n });\n }\n });\n }\n\n onChannelChange(ch: 'email' | 'text') { this.channel.set(ch); }\n\n onSend() {\n if (!this.canSend() || this.sending()) return;\n this.send.emit({\n channel: this.channel(),\n subject: this._subject(),\n body: this._body(),\n contacts: this.activeContacts(),\n });\n }\n}\n","import { Component, ChangeDetectionStrategy, input, output, signal, computed, forwardRef, viewChild, ElementRef } from '@angular/core';\nimport { ControlValueAccessor, NG_VALUE_ACCESSOR } from '@angular/forms';\n\n/**\n * File picker styled like the other `ui-*` inputs (label, hint, required marker, error, disabled).\n * Value (ngModel / forms) is `File | null`, or `File[]` when `multiple` is set.\n *\n * Set `[showField]=\"false\"` for a picker with no visible field and call `open()` from the host\n * (e.g. an \"Attach document\" menu action). `(filesSelected)` fires in both modes.\n */\n@Component({\n selector: 'ui-file-input',\n standalone: true,\n changeDetection: ChangeDetectionStrategy.OnPush,\n providers: [{\n provide: NG_VALUE_ACCESSOR,\n useExisting: forwardRef(() => FileInputComponent),\n multi: true,\n }],\n template: `\n <div class=\"fi-field\" [class.fi-error]=\"error()\" [class.fi-disabled]=\"disabled()\" [class.fi-hidden]=\"!showField()\">\n @if (showField() && label()) {\n <label class=\"fi-label\" [for]=\"_inputId\">\n {{ label() }}\n @if (required()) { <span class=\"fi-required\" aria-hidden=\"true\">*</span> }\n @if (hint()) { <span class=\"fi-hint-inline\">({{ hint() }})</span> }\n </label>\n }\n <input\n #fileEl\n type=\"file\"\n class=\"fi-native\"\n [id]=\"_inputId\"\n [attr.accept]=\"accept() || null\"\n [multiple]=\"multiple()\"\n [disabled]=\"disabled()\"\n [attr.required]=\"required() || null\"\n [attr.aria-label]=\"ariaLabel() || null\"\n [attr.aria-invalid]=\"error() ? 'true' : null\"\n [attr.aria-describedby]=\"error() ? _inputId + '-desc' : null\"\n [attr.tabindex]=\"showField() ? -1 : null\"\n (change)=\"onChange($event)\"\n (blur)=\"_onTouched()\" />\n @if (showField()) {\n <button type=\"button\" class=\"fi-control\" [disabled]=\"disabled()\" (click)=\"open()\" [attr.aria-describedby]=\"error() ? _inputId + '-desc' : null\">\n <span class=\"fi-name\" [class.fi-placeholder]=\"!summary()\">{{ summary() || placeholder() }}</span>\n <span class=\"fi-browse\">{{ buttonLabel() }}</span>\n </button>\n }\n @if (showField() && error()) {\n <p class=\"fi-desc fi-desc-error\" [id]=\"_inputId + '-desc'\" role=\"alert\">{{ error() }}</p>\n }\n </div>\n `,\n styles: [`\n :host { display: block; }\n :host(:has(.fi-hidden)) { display: contents; }\n\n .fi-field { display: flex; flex-direction: column; gap: 6px; }\n .fi-hidden { display: contents; }\n\n .fi-label { font-size: 0.8125rem; font-weight: 600; color: #374151; line-height: 1; }\n .fi-required { color: #ef4444; margin-left: 2px; }\n .fi-hint-inline { font-size: 0.75rem; color: #94a3b8; font-weight: 400; margin-left: 4px; }\n\n /* Native control stays in the DOM (forms, a11y, programmatic click) but is visually hidden. */\n .fi-native {\n position: absolute; width: 1px; height: 1px; margin: -1px; padding: 0;\n overflow: hidden; clip: rect(0 0 0 0); white-space: nowrap; border: 0;\n }\n\n .fi-control {\n display: flex; align-items: center; justify-content: space-between; gap: 12px;\n width: 100%; box-sizing: border-box; text-align: left;\n padding: 9px 12px; font-size: 0.875rem; font-family: inherit;\n color: #1e293b; background: #fff;\n border: 1px solid #e2e8f0; border-radius: 8px;\n outline: none; cursor: pointer;\n transition: border-color 0.15s, box-shadow 0.15s;\n &:focus-visible {\n border-color: #3b82f6;\n box-shadow: 0 0 0 3px rgba(59, 130, 246, 0.12);\n }\n }\n .fi-name { flex: 1; min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }\n .fi-placeholder { color: #94a3b8; }\n .fi-browse {\n flex-shrink: 0; font-size: 0.75rem; font-weight: 600; color: #374151;\n padding: 3px 10px; border: 1px solid #e2e8f0; border-radius: 6px; background: #f8fafc;\n }\n\n .fi-error .fi-control {\n border-color: #ef4444;\n &:focus-visible { box-shadow: 0 0 0 3px rgba(239, 68, 68, 0.12); }\n }\n .fi-disabled .fi-control { opacity: 0.5; cursor: not-allowed; }\n\n .fi-desc { margin: 0; font-size: 0.75rem; line-height: 1.4; color: #64748b; }\n .fi-desc-error { color: #ef4444; }\n\n @media (max-width: 480px) { .fi-control { font-size: 1rem; } }\n `],\n})\nexport class FileInputComponent implements ControlValueAccessor {\n readonly label = input('');\n /** Accessible name for a field with no visible label. */\n readonly ariaLabel = input('');\n readonly placeholder = input('No file chosen');\n readonly buttonLabel = input('Browse');\n /** Native `accept` string, e.g. \".pdf,.png\". */\n readonly accept = input('');\n readonly multiple = input(false);\n readonly required = input(false);\n readonly hint = input('');\n readonly error = input('');\n /** false = no visible field; open the picker from the host with `open()`. */\n readonly showField = input(true);\n\n /** Emits the chosen files (one or many) each time the user picks. */\n readonly filesSelected = output<File[]>();\n\n protected readonly _disabledInput = input(false, { alias: 'disabled' });\n private readonly _cvaDisabled = signal(false);\n protected readonly disabled = computed(() => this._disabledInput() || this._cvaDisabled());\n\n protected readonly _files = signal<File[]>([]);\n protected readonly _inputId = `fi-${Math.random().toString(36).slice(2, 8)}`;\n private readonly fileEl = viewChild<ElementRef<HTMLInputElement>>('fileEl');\n\n protected readonly summary = computed(() => {\n const f = this._files();\n if (f.length === 0) return '';\n return f.length === 1 ? f[0].name : `${f.length} files selected`;\n });\n\n protected _onChange = (_: File | File[] | null) => {};\n protected _onTouched = () => {};\n\n /** Opens the native file dialog. */\n open(): void {\n if (!this.disabled()) this.fileEl()?.nativeElement.click();\n }\n\n /** Clears the selection (and the native input, so the same file can be picked again). */\n clear(): void {\n const el = this.fileEl()?.nativeElement;\n if (el) el.value = '';\n this._files.set([]);\n this._onChange(this.multiple() ? [] : null);\n }\n\n writeValue(val: File | File[] | null) {\n const files = Array.isArray(val) ? val : val ? [val] : [];\n this._files.set(files);\n const el = this.fileEl()?.nativeElement;\n if (files.length === 0 && el) el.value = '';\n }\n registerOnChange(fn: (v: File | File[] | null) => void) { this._onChange = fn; }\n registerOnTouched(fn: () => void) { this._onTouched = fn; }\n setDisabledState(d: boolean) { this._cvaDisabled.set(d); }\n\n protected onChange(event: Event) {\n const el = event.target as HTMLInputElement;\n const files = Array.from(el.files ?? []);\n this._files.set(files);\n this._onChange(this.multiple() ? files : (files[0] ?? null));\n this._onTouched();\n if (files.length) this.filesSelected.emit(files);\n // Visible field keeps its selection; picker-only mode resets so the same file can be chosen again.\n if (!this.showField()) el.value = '';\n }\n}\n","import { Component, ChangeDetectionStrategy, input, output, signal, computed, effect, forwardRef } from '@angular/core';\nimport { AbstractControl, ControlValueAccessor, NG_VALIDATORS, NG_VALUE_ACCESSOR, ValidationErrors, Validator } from '@angular/forms';\nimport { FieldIssue, UiFieldValidity, createFieldReveal, issueToErrors, requiredIssue, parseIso, isoKey, formatIsoDate } from '../utils/field-validation';\n\n@Component({\n selector: 'ui-date-input',\n standalone: true,\n changeDetection: ChangeDetectionStrategy.OnPush,\n providers: [{\n provide: NG_VALUE_ACCESSOR,\n useExisting: forwardRef(() => DateInputComponent),\n multi: true,\n }, {\n provide: NG_VALIDATORS,\n useExisting: forwardRef(() => DateInputComponent),\n multi: true,\n }, {\n provide: UiFieldValidity,\n useExisting: forwardRef(() => DateInputComponent),\n }],\n template: `\n <div class=\"di-field\" [class.di-error]=\"_activeError()\" [class.di-disabled]=\"disabled()\">\n @if (label()) {\n <label class=\"di-label\" [for]=\"_inputId\">\n {{ label() }}\n @if (required()) { <span class=\"di-required\" aria-hidden=\"true\">*</span> }\n </label>\n }\n <input\n class=\"di-input\"\n type=\"date\"\n [id]=\"_inputId\"\n [value]=\"_value()\"\n [disabled]=\"disabled()\"\n [attr.min]=\"min() || null\"\n [attr.max]=\"max() || null\"\n [attr.required]=\"required() || null\"\n [attr.aria-invalid]=\"_activeError() ? 'true' : null\"\n [attr.aria-describedby]=\"(_activeError() || hint()) ? _inputId + '-desc' : null\"\n (change)=\"onChange($event)\"\n (blur)=\"onBlur()\"\n />\n @if (_activeError()) {\n <p class=\"di-desc di-desc-error\" [id]=\"_inputId + '-desc'\" role=\"alert\">{{ _activeError() }}</p>\n } @else if (hint()) {\n <p class=\"di-desc\" [id]=\"_inputId + '-desc'\">{{ hint() }}</p>\n }\n </div>\n `,\n styles: [`\n :host { display: block; }\n\n .di-field { display: flex; flex-direction: column; gap: 6px; }\n\n .di-label {\n font-size: 0.8125rem; font-weight: 600; color: #374151; line-height: 1;\n }\n .di-required { color: #ef4444; margin-left: 2px; }\n\n .di-input {\n width: 100%; box-sizing: border-box;\n padding: 8px 12px; font-size: 0.875rem; font-family: inherit;\n color: #1e293b; background: #fff;\n border: 1px solid #e2e8f0; border-radius: 8px;\n outline: none; transition: border-color 0.15s, box-shadow 0.15s;\n appearance: none;\n &:focus {\n border-color: #3b82f6;\n box-shadow: 0 0 0 3px rgba(59, 130, 246, 0.12);\n }\n }\n\n .di-error .di-input {\n border-color: #ef4444;\n &:focus { box-shadow: 0 0 0 3px rgba(239, 68, 68, 0.12); }\n }\n\n .di-disabled .di-input { opacity: 0.5; cursor: not-allowed; }\n\n .di-desc { margin: 0; font-size: 0.75rem; line-height: 1.4; color: #64748b; }\n .di-desc-error { color: #ef4444; }\n\n @media (max-width: 480px) { .di-input { font-size: 1rem; } }\n @media (pointer: coarse), (max-width: 480px) { .di-input { min-height: 44px; } }\n `],\n})\n/**\n * Native date picker (ControlValueAccessor, ISO string value).\n * Built-in validation: `required` -> \"<label> is required.\"; a value that is not a real date -> \"Please enter a valid date.\";\n * below `min` -> \"Must be on or after <date>\"; above `max` -> \"Must be on or before <date>\" (a `min`/`max` that cannot be\n * parsed is ignored). Disabled fields are never invalid.\n *\n * Validity API (same on every ui input): the component is an Angular `Validator` (`ngModel` / `FormControl` report\n * `.invalid` / `.errors`), exposes the signals `invalid` + `errorMessage` and the `invalidChange` output, and provides\n * `UiFieldValidity` so a host can use `viewChildren(UiFieldValidity)`. Messages are displayed only after blur or while\n * `[showErrors]=\"true\"`; setting `showErrors` back to false clears the touched state. A host-provided `[error]` wins.\n */\nexport class DateInputComponent implements ControlValueAccessor, Validator, UiFieldValidity {\n readonly label = input('');\n readonly required = input(false);\n readonly min = input('');\n readonly max = input('');\n readonly hint = input('');\n readonly error = input('');\n /** Reveal the built-in error messages now (e.g. after a failed Save click), without waiting for blur. */\n readonly showErrors = input(false);\n\n protected readonly _disabledInput = input(false, { alias: 'disabled' });\n private readonly _cvaDisabled = signal(false);\n protected readonly disabled = computed(() => this._disabledInput() || this._cvaDisabled());\n\n protected readonly _value = signal('');\n\n private readonly _reveal = createFieldReveal(this.showErrors);\n private readonly _issue = computed(() => this._check(this._value()));\n /** True while the value breaks a built-in rule or the host passed an `[error]`. */\n readonly invalid = computed(() => !!this.error() || !!this._issue());\n /** Message of the first broken built-in rule ('' when valid), shown or not. */\n readonly errorMessage = computed(() => this._issue()?.message ?? '');\n /** Emits whenever the field becomes invalid / valid. */\n readonly invalidChange = output<boolean>();\n protected readonly _activeError = computed(() => this.error() || (this._reveal.revealed() ? this.errorMessage() : ''));\n private _onValidatorChange = () => {};\n\n constructor() {\n effect(() => { this.invalidChange.emit(this.invalid()); });\n }\n\n /** Re-run the form control's validation when a rule input (required, maxlength, min, options, ...) changes. */\n ngOnChanges() { this._onValidatorChange(); }\n\n validate(control: AbstractControl): ValidationErrors | null {\n const v = control.value;\n return issueToErrors(this._check(v == null ? '' : String(v)));\n }\n registerOnValidatorChange(fn: () => void) { this._onValidatorChange = fn; }\n\n protected readonly _inputId = `di-${Math.random().toString(36).slice(2, 8)}`;\n\n private _onChange = (_: string) => {};\n private _onTouched = () => {};\n\n writeValue(val: string) { this._value.set(val ?? ''); }\n registerOnChange(fn: (v: string) => void) { this._onChange = fn; }\n registerOnTouched(fn: () => void) { this._onTouched = fn; }\n setDisabledState(d: boolean) { this._cvaDisabled.set(d); }\n\n protected onChange(event: Event) {\n const val = (event.target as HTMLInputElement).value;\n this._value.set(val);\n this._onChange(val);\n }\n\n protected onBlur() { this._onTouched(); this._reveal.touched.set(true); }\n\n private _check(value: string): FieldIssue | null {\n if (this.disabled()) return null;\n const text = value.trim();\n if (!text) return this.required() ? requiredIssue(this.label()) : null;\n const p = parseIso(text, false);\n if (!p) return { key: 'date', message: 'Please enter a valid date.' };\n const min = this.min() ? parseIso(this.min(), false) : null;\n if (min && isoKey(p) < isoKey(min)) return { key: 'min', message: `Must be on or after ${formatIsoDate(this.min())}`, value: { min: this.min(), actual: text } };\n const max = this.max() ? parseIso(this.max(), false) : null;\n if (max && isoKey(p) > isoKey(max)) return { key: 'max', message: `Must be on or before ${formatIsoDate(this.max())}`, value: { max: this.max(), actual: text } };\n return null;\n }\n}\n","import { Component, ChangeDetectionStrategy, input, signal, computed, forwardRef } from '@angular/core';\nimport { ControlValueAccessor, NG_VALUE_ACCESSOR } from '@angular/forms';\n\n@Component({\n selector: 'ui-time-input',\n standalone: true,\n changeDetection: ChangeDetectionStrategy.OnPush,\n providers: [{\n provide: NG_VALUE_ACCESSOR,\n useExisting: forwardRef(() => TimeInputComponent),\n multi: true,\n }],\n template: `\n <div class=\"tmi-field\" [class.tmi-error]=\"error()\" [class.tmi-disabled]=\"disabled()\">\n @if (label()) {\n <label class=\"tmi-label\" [for]=\"_inputId\">\n {{ label() }}\n @if (required()) { <span class=\"tmi-required\" aria-hidden=\"true\">*</span> }\n </label>\n }\n <input\n class=\"tmi-input\"\n type=\"time\"\n [id]=\"_inputId\"\n [value]=\"_value()\"\n [disabled]=\"disabled()\"\n [attr.min]=\"min() || null\"\n [attr.max]=\"max() || null\"\n [attr.required]=\"required() || null\"\n [attr.aria-invalid]=\"error() ? 'true' : null\"\n [attr.aria-describedby]=\"(error() || hint()) ? _inputId + '-desc' : null\"\n (change)=\"onChange($event)\"\n (blur)=\"onBlur()\"\n />\n @if (error()) {\n <p class=\"tmi-desc tmi-desc-error\" [id]=\"_inputId + '-desc'\" role=\"alert\">{{ error() }}</p>\n } @else if (hint()) {\n <p class=\"tmi-desc\" [id]=\"_inputId + '-desc'\">{{ hint() }}</p>\n }\n </div>\n `,\n styles: [`\n :host { display: block; }\n\n .tmi-field { display: flex; flex-direction: column; gap: 6px; }\n\n .tmi-label {\n font-size: 0.8125rem; font-weight: 600; color: #374151; line-height: 1;\n }\n .tmi-required { color: #ef4444; margin-left: 2px; }\n\n .tmi-input {\n width: 100%; box-sizing: border-box;\n padding: 8px 12px; font-size: 0.875rem; font-family: inherit;\n color: #1e293b; background: #fff;\n border: 1px solid #e2e8f0; border-radius: 8px;\n outline: none; transition: border-color 0.15s, box-shadow 0.15s;\n appearance: none;\n &:focus {\n border-color: #3b82f6;\n box-shadow: 0 0 0 3px rgba(59, 130, 246, 0.12);\n }\n }\n\n .tmi-error .tmi-input {\n border-color: #ef4444;\n &:focus { box-shadow: 0 0 0 3px rgba(239, 68, 68, 0.12); }\n }\n\n .tmi-disabled .tmi-input { opacity: 0.5; cursor: not-allowed; }\n\n .tmi-desc { margin: 0; font-size: 0.75rem; line-height: 1.4; color: #64748b; }\n .tmi-desc-error { color: #ef4444; }\n\n @media (max-width: 480px) { .tmi-input { font-size: 1rem; } }\n @media (pointer: coarse), (max-width: 480px) { .tmi-input { min-height: 44px; } }\n `],\n})\nexport class TimeInputComponent implements ControlValueAccessor {\n readonly label = input('');\n readonly required = input(false);\n readonly min = input('');\n readonly max = input('');\n readonly hint = input('');\n readonly error = input('');\n\n protected readonly _disabledInput = input(false, { alias: 'disabled' });\n private readonly _cvaDisabled = signal(false);\n protected readonly disabled = computed(() => this._disabledInput() || this._cvaDisabled());\n\n protected readonly _value = signal('');\n protected readonly _inputId = `tmi-${Math.random().toString(36).slice(2, 8)}`;\n\n private _onChange = (_: string) => {};\n private _onTouched = () => {};\n\n writeValue(val: string) { this._value.set(val ?? ''); }\n registerOnChange(fn: (v: string) => void) { this._onChange = fn; }\n registerOnTouched(fn: () => void) { this._onTouched = fn; }\n setDisabledState(d: boolean) { this._cvaDisabled.set(d); }\n\n protected onChange(event: Event) {\n const val = (event.target as HTMLInputElement).value;\n this._value.set(val);\n this._onChange(val);\n }\n\n protected onBlur() { this._onTouched(); }\n}\n","import {\n Component, ChangeDetectionStrategy, ElementRef, HostListener, input, model, signal, computed, forwardRef, inject,\n} from '@angular/core';\nimport { ControlValueAccessor, NG_VALUE_ACCESSOR } from '@angular/forms';\nimport { ChipComponent } from '../chip/chip.component';\nimport { CheckboxComponent } from '../checkbox/checkbox.component';\nimport type { SelectOption } from '../select/select.component';\n\n/**\n * Searchable multi-value dropdown. Selected values render as dismissible chips above the trigger.\n *\n * Bind with `[(selected)]` or `[(ngModel)]` — the value is an array of option `value`s.\n *\n * <ui-multi-select label=\"Job Types\" [options]=\"jobTypeOptions\" [(selected)]=\"jobTypeIds\" />\n */\n@Component({\n selector: 'ui-multi-select',\n standalone: true,\n changeDetection: ChangeDetectionStrategy.OnPush,\n imports: [ChipComponent, CheckboxComponent],\n providers: [{\n provide: NG_VALUE_ACCESSOR,\n useExisting: forwardRef(() => MultiSelectComponent),\n multi: true,\n }],\n template: `\n <div class=\"ms-field\" [class.ms-has-error]=\"error()\" [class.ms-disabled]=\"disabled()\">\n @if (label()) {\n <span class=\"ms-label\" [id]=\"_id + '-label'\">\n {{ label() }}\n @if (required()) { <span class=\"ms-required\" aria-hidden=\"true\">*</span> }\n </span>\n }\n\n @if (_selectedOptions().length) {\n <div class=\"ms-tags\">\n @for (opt of _selectedOptions(); track opt.value) {\n <ui-chip size=\"sm\" [label]=\"opt.label\" [active]=\"true\" [dismissible]=\"!disabled()\" (dismiss)=\"remove(opt.value)\" />\n }\n </div>\n }\n\n <div class=\"ms-wrap\">\n <button\n type=\"button\"\n class=\"ms-trigger\"\n [class.ms-open]=\"_open()\"\n [disabled]=\"disabled()\"\n [attr.aria-expanded]=\"_open()\"\n [attr.aria-labelledby]=\"label() ? _id + '-label' : null\"\n (click)=\"toggleOpen()\">\n <span class=\"ms-placeholder\">{{ _selectedSet().size ? _selectedSet().size + ' selected' : placeholder() }}</span>\n <svg class=\"ms-chevron\" [class.ms-chevron-open]=\"_open()\" width=\"14\" height=\"14\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2.5\" aria-hidden=\"true\"><polyline points=\"6 9 12 15 18 9\"/></svg>\n </button>\n\n @if (_open()) {\n <div class=\"ms-dropdown\" role=\"listbox\" aria-multiselectable=\"true\">\n @if (searchable()) {\n <div class=\"ms-search-wrap\">\n <input\n class=\"ms-search\"\n type=\"text\"\n [placeholder]=\"searchPlaceholder()\"\n [value]=\"_query()\"\n (input)=\"_query.set($any($event.target).value)\" />\n </div>\n }\n <div class=\"ms-options\">\n @for (opt of _filteredOptions(); track opt.value) {\n <div class=\"ms-option\" [class.ms-option-selected]=\"_selectedSet().has(opt.value)\">\n <ui-checkbox\n [label]=\"opt.label\"\n [checked]=\"_selectedSet().has(opt.value)\"\n (checkedChange)=\"toggleValue(opt.value)\" />\n </div>\n } @empty {\n <div class=\"ms-empty\">No options found</div>\n }\n </div>\n </div>\n }\n </div>\n\n @if (error()) {\n <p class=\"ms-desc ms-desc-error\" role=\"alert\">{{ error() }}</p>\n } @else if (hint()) {\n <p class=\"ms-desc\">{{ hint() }}</p>\n }\n </div>\n `,\n styles: [`\n :host { display: block; }\n\n .ms-field { display: flex; flex-direction: column; gap: 6px; }\n\n .ms-label { font-size: 0.8125rem; font-weight: 600; color: #374151; line-height: 1; }\n .ms-required { color: #ef4444; margin-left: 2px; }\n\n .ms-tags { display: flex; flex-wrap: wrap; gap: 6px; }\n\n .ms-wrap { position: relative; }\n\n .ms-trigger {\n width: 100%; box-sizing: border-box; display: flex; align-items: center; justify-content: space-between;\n padding: 9px 10px 9px 12px; font-size: 0.875rem; font-family: inherit; text-align: left;\n color: #1e293b; background: #fff; border: 1px solid #e2e8f0; border-radius: 8px;\n cursor: pointer; outline: none; transition: border-color 0.15s, box-shadow 0.15s;\n &:focus-visible, &.ms-open { border-color: #3b82f6; box-shadow: 0 0 0 3px rgba(59, 130, 246, 0.12); }\n }\n .ms-has-error .ms-trigger { border-color: #ef4444; }\n .ms-disabled .ms-trigger { opacity: 0.5; cursor: not-allowed; }\n\n .ms-placeholder { color: #64748b; }\n\n .ms-chevron { color: #94a3b8; flex-shrink: 0; margin-left: 8px; transition: transform 0.2s ease; }\n .ms-chevron-open { transform: rotate(180deg); }\n\n .ms-dropdown {\n position: absolute; top: calc(100% + 4px); left: 0; right: 0; z-index: 50;\n display: flex; flex-direction: column; max-height: 260px;\n background: #fff; border: 1px solid #e2e8f0; border-radius: 8px;\n box-shadow: 0 4px 16px rgba(15, 23, 42, 0.1);\n }\n\n .ms-search-wrap { padding: 8px; border-bottom: 1px solid #e2e8f0; }\n .ms-search {\n width: 100%; box-sizing: border-box; padding: 7px 10px;\n font-size: 0.8125rem; font-family: inherit; color: #1e293b;\n border: 1px solid #e2e8f0; border-radius: 6px; outline: none;\n &::placeholder { color: #94a3b8; }\n &:focus { border-color: #3b82f6; }\n }\n\n .ms-options { overflow-y: auto; padding: 4px 0; }\n .ms-option {\n padding: 7px 12px; font-size: 0.8125rem;\n &:hover { background: #f8fafc; }\n }\n .ms-option-selected { background: #eff6ff; }\n\n .ms-empty { padding: 16px; text-align: center; color: #94a3b8; font-size: 0.8125rem; }\n\n .ms-desc { margin: 0; font-size: 0.75rem; line-height: 1.4; color: #64748b; }\n .ms-desc-error { color: #ef4444; }\n\n @media (max-width: 480px) { .ms-trigger, .ms-search { font-size: 1rem; } }\n `],\n})\nexport class MultiSelectComponent implements ControlValueAccessor {\n readonly label = input('');\n readonly placeholder = input('Select...');\n readonly options = input<SelectOption[]>([]);\n readonly searchable = input(true);\n readonly searchPlaceholder = input('Search...');\n readonly required = input(false);\n readonly hint = input('');\n readonly error = input('');\n /** Selected option values — two-way bindable as `[(selected)]`. */\n readonly selected = model<string[]>([]);\n\n readonly _disabledInput = input(false, { alias: 'disabled' });\n private readonly _cvaDisabled = signal(false);\n protected readonly disabled = computed(() => this._disabledInput() || this._cvaDisabled());\n\n private readonly el = inject(ElementRef);\n protected readonly _id = `ms-${Math.random().toString(36).slice(2, 8)}`;\n protected readonly _open = signal(false);\n protected readonly _query = signal('');\n\n protected readonly _selectedSet = computed(() => new Set(this.selected()));\n protected readonly _selectedOptions = computed(() => this.options().filter(o => this._selectedSet().has(o.value)));\n protected readonly _filteredOptions = computed(() => {\n const q = this._query().trim().toLowerCase();\n return q ? this.options().filter(o => o.label.toLowerCase().includes(q)) : this.options();\n });\n\n private _onChange = (_: string[]) => {};\n private _onTouched = () => {};\n\n writeValue(val: string[] | null) { this.selected.set(val ?? []); }\n registerOnChange(fn: (v: string[]) => void) { this._onChange = fn; }\n registerOnTouched(fn: () => void) { this._onTouched = fn; }\n setDisabledState(d: boolean) { this._cvaDisabled.set(d); }\n\n @HostListener('document:click', ['$event'])\n protected onDocumentClick(event: Event) {\n if (this._open() && !this.el.nativeElement.contains(event.target)) this.close();\n }\n\n protected toggleOpen() {\n if (this.disabled()) return;\n if (this._open()) this.close(); else this._open.set(true);\n }\n\n protected toggleValue(value: string) {\n const set = new Set(this.selected());\n if (set.has(value)) set.delete(value); else set.add(value);\n this._set(Array.from(set));\n }\n\n protected remove(value: string) {\n this._set(this.selected().filter(v => v !== value));\n }\n\n private close() {\n this._open.set(false);\n this._query.set('');\n this._onTouched();\n }\n\n private _set(values: string[]) {\n this.selected.set(values);\n this._onChange(values);\n }\n}\n","import { Component, ChangeDetectionStrategy, DestroyRef, inject, input, output, signal, computed } from '@angular/core';\nimport { HttpClient } from '@angular/common/http';\nimport { takeUntilDestroyed } from '@angular/core/rxjs-interop';\nimport { FORGEROUTE_CONFIG } from '../config/forgeroute.config';\nimport { CheckboxComponent } from '../checkbox/checkbox.component';\n\nexport interface DiscountOption {\n id: string;\n name: string;\n /** 'FIXED' (dollar amount) or a percentage type. */\n type: string;\n value: number;\n /** 'LABOR' | 'MATERIALS' | anything else = whole total. */\n appliesTo: string;\n description: string | null;\n}\n\nexport interface AppliedDiscount extends DiscountOption {\n /** Dollar amount off: `value` for FIXED, otherwise `subtotal * value / 100`. */\n computedAmount: number;\n}\n\n/**\n * Collapsible list of the account's active discounts (`GET <baseUrl>/discounts/active`).\n * Emits the chosen discounts with their computed amounts against `subtotal`.\n *\n * <ui-discount-selector [subtotal]=\"subtotal()\" (discountsChanged)=\"onDiscounts($event)\" />\n */\n@Component({\n selector: 'ui-discount-selector',\n standalone: true,\n changeDetection: ChangeDetectionStrategy.OnPush,\n imports: [CheckboxComponent],\n template: `\n <div class=\"ds\">\n <button type=\"button\" class=\"ds-header\" [attr.aria-expanded]=\"_expanded()\" (click)=\"_expanded.set(!_expanded())\">\n <span class=\"ds-label\">\n <svg width=\"16\" height=\"16\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\" stroke-linecap=\"round\" stroke-linejoin=\"round\" aria-hidden=\"true\"><path d=\"M20.59 13.41l-7.17 7.17a2 2 0 01-2.83 0L2 12V2h10l8.59 8.59a2 2 0 010 2.82z\"/><line x1=\"7\" y1=\"7\" x2=\"7.01\" y2=\"7\"/></svg>\n Discounts\n @if (_selectedIds().size > 0) { <span class=\"ds-badge\">{{ _selectedIds().size }}</span> }\n </span>\n <svg class=\"ds-chevron\" [class.ds-chevron-open]=\"_expanded()\" width=\"16\" height=\"16\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\" stroke-linecap=\"round\" stroke-linejoin=\"round\" aria-hidden=\"true\"><polyline points=\"6 9 12 15 18 9\"/></svg>\n </button>\n\n @if (_expanded()) {\n <div class=\"ds-body\">\n @for (d of _discounts(); track d.id) {\n <div class=\"ds-option\" [class.ds-option-selected]=\"_selectedIds().has(d.id)\">\n <ui-checkbox [checked]=\"_selectedIds().has(d.id)\" (checkedChange)=\"toggle(d.id)\">\n <span class=\"ds-option-info\">\n <span class=\"ds-option-name\">{{ d.name }}</span>\n <span class=\"ds-option-detail\">{{ d.description || formatValue(d) + ' off ' + formatAppliesTo(d) }}</span>\n </span>\n </ui-checkbox>\n <span class=\"ds-option-value\">{{ formatValue(d) }}</span>\n </div>\n } @empty {\n <p class=\"ds-empty\">{{ emptyMessage() }}</p>\n }\n </div>\n }\n\n @if (_selected().length > 0) {\n <div class=\"ds-applied\">\n @for (d of _selected(); track d.id) {\n <div class=\"ds-applied-row\">\n <span class=\"ds-applied-name\">{{ d.name }}</span>\n <span class=\"ds-applied-value\">-{{ formatValue(d) }}</span>\n <button type=\"button\" class=\"ds-remove\" [attr.aria-label]=\"'Remove ' + d.name\" (click)=\"toggle(d.id)\">\n <svg width=\"12\" height=\"12\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\" stroke-linecap=\"round\" stroke-linejoin=\"round\" aria-hidden=\"true\"><line x1=\"18\" y1=\"6\" x2=\"6\" y2=\"18\"/><line x1=\"6\" y1=\"6\" x2=\"18\" y2=\"18\"/></svg>\n </button>\n </div>\n }\n </div>\n }\n </div>\n `,\n styles: [`\n :host { display: block; }\n\n .ds { border: 1px solid #e2e8f0; border-radius: 10px; overflow: hidden; background: #fff; }\n\n .ds-header {\n width: 100%; display: flex; justify-content: space-between; align-items: center;\n padding: 12px 16px; border: none; background: transparent; font-family: inherit; cursor: pointer;\n &:hover { background: #f8fafc; }\n }\n .ds-label {\n display: flex; align-items: center; gap: 8px;\n font-size: 0.875rem; font-weight: 600; color: #334155;\n svg { color: #d97706; }\n }\n .ds-badge {\n background: #d97706; color: #fff; font-size: 0.6875rem; font-weight: 700;\n padding: 1px 7px; border-radius: 10px; line-height: 1.4;\n }\n .ds-chevron { color: #94a3b8; transition: transform 0.2s ease; }\n .ds-chevron-open { transform: rotate(180deg); }\n\n .ds-body { border-top: 1px solid #e2e8f0; padding: 4px 0; }\n .ds-empty { padding: 16px; margin: 0; text-align: center; color: #94a3b8; font-size: 0.8125rem; }\n\n .ds-option {\n display: flex; align-items: center; gap: 12px; padding: 10px 16px;\n &:hover { background: #fffbeb; }\n ui-checkbox { flex: 1; min-width: 0; }\n }\n .ds-option-selected { background: #fffbeb; }\n .ds-option-info { display: flex; flex-direction: column; min-width: 0; }\n .ds-option-name { font-size: 0.8125rem; font-weight: 600; color: #0f172a; }\n .ds-option-detail { font-size: 0.75rem; color: #94a3b8; margin-top: 1px; }\n .ds-option-value { font-size: 0.8125rem; font-weight: 700; color: #d97706; white-space: nowrap; }\n\n .ds-applied { border-top: 1px solid #e2e8f0; padding: 8px 16px; background: #fffbeb; }\n .ds-applied-row { display: flex; align-items: center; gap: 8px; padding: 4px 0; font-size: 0.8125rem; color: #334155; }\n .ds-applied-name { flex: 1; }\n .ds-applied-value { font-weight: 600; color: #16a34a; }\n .ds-remove {\n display: flex; align-items: center; justify-content: center;\n width: 22px; height: 22px; padding: 0; border: none; border-radius: 4px;\n background: transparent; color: #94a3b8; cursor: pointer;\n &:hover { background: #fef2f2; color: #dc2626; }\n }\n `],\n})\nexport class DiscountSelectorComponent {\n /** Amount percentage discounts are computed against. */\n readonly subtotal = input(0);\n readonly emptyMessage = input('No discounts available. Create discounts in Settings.');\n\n readonly discountsChanged = output<AppliedDiscount[]>();\n\n private readonly http = inject(HttpClient);\n /** API root from FORGEROUTE_CONFIG (e.g. a cross-origin prod API); falls back to same-origin `/api`. */\n private readonly apiBase = inject(FORGEROUTE_CONFIG, { optional: true })?.baseUrl ?? '/api';\n private readonly destroyRef = inject(DestroyRef);\n\n protected readonly _discounts = signal<DiscountOption[]>([]);\n protected readonly _selectedIds = signal(new Set<string>());\n protected readonly _expanded = signal(false);\n protected readonly _selected = computed(() => this._discounts().filter(d => this._selectedIds().has(d.id)));\n\n constructor() {\n this.http.get<DiscountOption[]>(`${this.apiBase}/discounts/active`)\n .pipe(takeUntilDestroyed(this.destroyRef))\n .subscribe({ next: d => this._discounts.set(d ?? []), error: () => this._discounts.set([]) });\n }\n\n protected toggle(id: string) {\n const ids = new Set(this._selectedIds());\n if (ids.has(id)) ids.delete(id); else ids.add(id);\n this._selectedIds.set(ids);\n const subtotal = this.subtotal();\n this.discountsChanged.emit(this._selected().map(d => ({\n ...d,\n computedAmount: d.type === 'FIXED' ? d.value : (subtotal * d.value) / 100,\n })));\n }\n\n protected formatValue(d: DiscountOption): string {\n return d.type === 'FIXED' ? `$${d.value}` : `${d.value}%`;\n }\n\n protected formatAppliesTo(d: DiscountOption): string {\n switch (d.appliesTo) {\n case 'LABOR': return 'labor';\n case 'MATERIALS': return 'materials';\n default: return 'total';\n }\n }\n}\n","import { Component, input, output, signal, computed, effect, untracked, ChangeDetectionStrategy } from '@angular/core';\n\n// Shared across all instances — tracks which dropdown is currently open so\n// opening one automatically closes any other that is open.\nconst _activeDropdownId = signal('');\n\nexport interface DropdownItem {\n label: string;\n value: string;\n count?: number;\n}\n\n@Component({\n selector: 'ui-dropdown',\n standalone: true,\n changeDetection: ChangeDetectionStrategy.OnPush,\n host: { '(document:click)': 'close()' },\n template: `\n <div class=\"dd-wrapper\">\n @if (variant() === 'badge') {\n <button class=\"dd-trigger dd-trigger-badge\" [class.dd-active]=\"active()\" [attr.aria-expanded]=\"open()\" aria-haspopup=\"menu\" (click)=\"toggle($event)\">\n {{ label() }}\n <svg width=\"11\" height=\"11\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2.5\" aria-hidden=\"true\"><polyline points=\"6 9 12 15 18 9\"/></svg>\n </button>\n } @else {\n <button class=\"dd-trigger dd-trigger-btn\" [class.dd-active]=\"active()\" [attr.aria-expanded]=\"open()\" aria-haspopup=\"menu\" (click)=\"toggle($event)\">\n {{ label() }}\n <svg width=\"11\" height=\"11\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2.5\" aria-hidden=\"true\"><polyline points=\"6 9 12 15 18 9\"/></svg>\n </button>\n }\n @if (open()) {\n <div class=\"dd-menu\" role=\"menu\" [style.min-width]=\"menuWidth()\" (click)=\"$event.stopPropagation()\">\n @if (loading()) {\n <div class=\"dd-loading\">\n <span class=\"dd-spinner\"></span>\n <span>Loading...</span>\n </div>\n } @else {\n @for (item of displayItems(); track item.value) {\n <button class=\"dd-item\" role=\"menuitem\" [class.dd-item-active]=\"activeValue() === item.value\" (click)=\"select(item)\">\n <span>{{ item.label }}</span>\n @if (item.count !== undefined) {\n <span class=\"dd-count\">{{ item.count }}</span>\n }\n </button>\n }\n }\n </div>\n }\n </div>\n `,\n styles: [`\n :host { display: inline-flex; }\n\n .dd-wrapper { position: relative; display: inline-flex; }\n\n .dd-trigger {\n display: inline-flex; align-items: center; gap: 6px;\n cursor: pointer; font-family: inherit; transition: all 150ms ease;\n &:focus-visible { outline: 2px solid #60a5fa; outline-offset: 2px; }\n }\n\n .dd-trigger-badge {\n padding: 6px 14px; border-radius: 999px; border: 1px solid #e2e8f0;\n background: #fff; font-size: 0.8125rem; font-weight: 500; color: #64748b;\n &:hover { background: #f8fafc; border-color: #94a3b8; }\n &.dd-active { background: #1e293b; color: #fff; border-color: #1e293b; }\n }\n\n .dd-trigger-btn {\n padding: 6px 12px; border-radius: 8px; border: 1px solid #e2e8f0;\n background: #fff; font-size: 0.8125rem; font-weight: 500; color: #334155;\n &:hover { background: #f8fafc; border-color: #94a3b8; }\n &.dd-active { background: #1e293b; color: #fff; border-color: #1e293b; }\n }\n\n .dd-menu {\n position: absolute; top: calc(100% + 6px); left: 0; z-index: 9999;\n background: #fff; border: 1px solid #e2e8f0; border-radius: 8px;\n box-shadow: 0 4px 16px rgba(0,0,0,0.12);\n max-height: 300px; overflow-y: auto; padding: 4px;\n max-width: calc(100vw - 32px);\n }\n\n .dd-item {\n display: flex; align-items: center; justify-content: space-between;\n width: 100%; padding: 10px 12px; border: none; background: none;\n font-size: 0.8125rem; font-weight: 500; color: #374151; cursor: pointer;\n font-family: inherit; border-radius: 6px; text-align: left; transition: background 100ms;\n border-bottom: 1px solid #f1f5f9; min-height: 44px;\n &:last-child { border-bottom: none; }\n &:hover { background: #f8fafc; }\n &:focus-visible { outline: 2px solid #60a5fa; outline-offset: -2px; }\n &.dd-item-active { background: #eff6ff; color: #2563eb; }\n }\n\n .dd-count {\n font-size: 0.6875rem; font-weight: 700;\n background: #f1f5f9; color: #64748b;\n padding: 1px 7px; border-radius: 999px; flex-shrink: 0;\n }\n\n .dd-loading {\n display: flex; align-items: center; gap: 8px;\n padding: 12px; color: #64748b; font-size: 0.8125rem;\n }\n .dd-spinner {\n width: 14px; height: 14px; border-radius: 50%; flex-shrink: 0;\n border: 2px solid #e2e8f0; border-top-color: #64748b;\n animation: dd-spin 0.6s linear infinite;\n }\n @keyframes dd-spin { to { transform: rotate(360deg); } }\n\n @media (max-width: 768px) {\n .dd-trigger-badge, .dd-trigger-btn { min-height: 44px; }\n .dd-menu { max-height: 50vh; left: auto; right: 0; }\n }\n `],\n})\nexport class DropdownComponent {\n readonly label = input('');\n readonly items = input<DropdownItem[]>([]);\n /** Optional async loader — called once on open, result cached until closed. */\n readonly loader = input<(() => Promise<DropdownItem[]>) | null>(null);\n readonly active = input(false);\n readonly activeValue = input('');\n readonly variant = input<'button' | 'badge'>('button');\n readonly menuWidth = input('200px');\n\n readonly itemClick = output<DropdownItem>();\n\n protected readonly open = signal(false);\n protected readonly loading = signal(false);\n private readonly _loaded = signal<DropdownItem[] | null>(null);\n private readonly _id = Math.random().toString(36).slice(2);\n\n protected readonly displayItems = computed<DropdownItem[]>(() =>\n this.open() ? (this._loaded() ?? this.items()) : []\n );\n\n constructor() {\n effect(() => {\n if (_activeDropdownId() !== this._id) {\n untracked(() => { if (this.open()) this.open.set(false); });\n }\n });\n\n effect(() => {\n const isOpen = this.open();\n const loader = this.loader();\n if (!loader) return;\n if (isOpen && untracked(() => this._loaded()) === null) {\n this.loading.set(true);\n loader().then(items => { this._loaded.set(items); this.loading.set(false); });\n } else if (!isOpen) {\n this._loaded.set(null);\n }\n });\n }\n\n toggle(event: Event) {\n event.stopPropagation();\n const next = !this.open();\n this.open.set(next);\n if (next) _activeDropdownId.set(this._id);\n else if (_activeDropdownId() === this._id) _activeDropdownId.set('');\n }\n\n select(item: DropdownItem) { this.open.set(false); this.itemClick.emit(item); }\n\n close() {\n if (this.open()) {\n this.open.set(false);\n if (_activeDropdownId() === this._id) _activeDropdownId.set('');\n }\n }\n}\n","import { Component, ChangeDetectionStrategy, input } from '@angular/core';\n\n@Component({\n selector: 'ui-form-row',\n standalone: true,\n changeDetection: ChangeDetectionStrategy.OnPush,\n template: `<div class=\"fr-row\" [style.--fr-cols]=\"cols()\"><ng-content /></div>`,\n styles: [`\n :host { display: block; }\n\n .fr-row {\n display: grid;\n grid-template-columns: repeat(var(--fr-cols, 2), 1fr);\n gap: 12px;\n }\n\n @media (max-width: 480px) {\n .fr-row { grid-template-columns: 1fr; }\n }\n `],\n})\nexport class FormRowComponent {\n readonly cols = input<1 | 2 | 3>(2);\n}\n","import { Component, ChangeDetectionStrategy, input, output, HostBinding } from '@angular/core';\nimport { BadgeComponent } from '../badge/badge.component';\nimport { BadgeVariant } from '../data-table/data-table.types';\n\n/**\n * Fixed-layout list item with an optional leading radio/checkbox, a title + badge,\n * and a secondary detail row rendered from a string array. All layout is handled\n * internally — pass inputs only, no ng-content.\n *\n * Usage:\n * <ui-list-item\n * leading=\"radio\"\n * inputName=\"survivor-group1\"\n * [inputId]=\"'row-' + c.id\"\n * [checked]=\"selected\"\n * [highlighted]=\"selected\"\n * [title]=\"c.firstName + ' ' + c.lastName\"\n * badge=\"KEEP\"\n * badgeVariant=\"green\"\n * [details]=\"[c.email, c.phone, 'Added ' + (c.createdAt | date:'MMM d, y')]\"\n * (select)=\"onSelect(c.id)\" />\n */\n@Component({\n selector: 'ui-list-item',\n standalone: true,\n changeDetection: ChangeDetectionStrategy.OnPush,\n imports: [BadgeComponent],\n template: `\n <div class=\"li-row\" [class.li-row--highlighted]=\"highlighted()\">\n\n @if (leading() === 'radio') {\n <input\n type=\"radio\"\n class=\"li-input\"\n [id]=\"inputId()\"\n [name]=\"inputName()\"\n [checked]=\"checked()\"\n (change)=\"select.emit()\"\n />\n } @else if (leading() === 'checkbox') {\n <input\n type=\"checkbox\"\n class=\"li-input\"\n [id]=\"inputId()\"\n [checked]=\"checked()\"\n (change)=\"select.emit()\"\n />\n }\n\n <label class=\"li-body\" [attr.for]=\"inputId() || null\">\n <div class=\"li-title-row\">\n <span class=\"li-title\">{{ title() }}</span>\n @if (badge() && checked()) {\n <ui-badge [label]=\"badge()!\" [variant]=\"badgeVariant()\" />\n }\n </div>\n @if (visibleDetails().length) {\n <div class=\"li-details\">\n @for (detail of visibleDetails(); track $index) {\n <span>{{ detail }}</span>\n }\n </div>\n }\n </label>\n\n </div>\n `,\n styles: [`\n :host { display: block; }\n :host(.li-bordered) {\n border: 1px solid var(--border, #e2e8f0);\n border-radius: 8px;\n overflow: hidden;\n margin-bottom: 6px;\n &:last-child { margin-bottom: 0; }\n }\n\n .li-row {\n display: flex;\n align-items: flex-start;\n gap: 10px;\n padding: 10px 14px;\n border-bottom: 1px solid var(--border, #e2e8f0);\n transition: background 0.12s;\n\n &:last-child { border-bottom: none; }\n :host(.li-bordered) & { border-bottom: none; }\n &:hover { background: var(--surface-hover, #f8fafc); }\n &--highlighted { background: #f0fdf4; }\n &--highlighted:hover { background: #dcfce7; }\n }\n\n .li-input {\n margin-top: 3px;\n flex-shrink: 0;\n accent-color: #16a34a;\n width: 1rem;\n height: 1rem;\n cursor: pointer;\n }\n\n .li-body {\n flex: 1;\n min-width: 0;\n cursor: pointer;\n display: flex;\n flex-direction: column;\n gap: 3px;\n }\n\n .li-title-row {\n display: flex;\n align-items: center;\n gap: 6px;\n flex-wrap: wrap;\n }\n\n .li-title {\n font-size: 0.875rem;\n font-weight: 500;\n color: var(--text-primary, #1e293b);\n }\n\n .li-details {\n display: flex;\n flex-wrap: wrap;\n gap: 6px;\n font-size: 0.775rem;\n color: var(--text-secondary, #64748b);\n }\n `],\n})\nexport class ListItemComponent {\n /** Show a radio button, checkbox, or nothing on the left. */\n readonly leading = input<'radio' | 'checkbox' | 'none'>('none');\n /** `name` attribute for the radio/checkbox (groups radios together). */\n readonly inputName = input('');\n /** `id` attribute for the radio/checkbox (links the label). */\n readonly inputId = input('');\n /** Whether this item is currently selected/checked. */\n readonly checked = input(false);\n /** Highlights the row (e.g. green tint for the selected \"keep\" row). */\n readonly highlighted = input(false);\n /** Wraps the item in a bordered card (border + border-radius). */\n readonly bordered = input(false);\n\n @HostBinding('class.li-bordered') get _borderedClass() { return this.bordered(); }\n\n /** Primary label. */\n readonly title = input('');\n /** Badge shown next to the title — only visible when the item is checked. */\n readonly badge = input<string | null>(null);\n readonly badgeVariant = input<BadgeVariant>('green');\n\n /**\n * Secondary detail strings rendered inline below the title.\n * Pass whatever is relevant — email, phone, date, role, address, etc.\n * Falsy entries (null, undefined, '') are filtered out automatically.\n */\n readonly details = input<(string | null | undefined)[]>([]);\n\n /** Emitted when the radio/checkbox changes. */\n readonly select = output<void>();\n\n protected readonly visibleDetails = () =>\n this.details().filter((d): d is string => !!d);\n}\n","/**\n * Dependency-free QR Code encoder (ISO/IEC 18004), byte mode, versions 1–40.\n *\n * Algorithm follows the public reference implementation by Project Nayuki\n * (https://www.nayuki.io/page/qr-code-generator-library, MIT License).\n */\n\nexport type QrErrorCorrection = 'L' | 'M' | 'Q' | 'H';\n\nexport interface QrMatrix {\n /** Modules per side (version * 4 + 17), excluding any quiet zone. */\n readonly size: number;\n readonly version: number;\n readonly mask: number;\n /** modules[y][x] — true = dark. */\n readonly modules: boolean[][];\n}\n\nconst ECL_ORDINAL: Record<QrErrorCorrection, number> = { L: 0, M: 1, Q: 2, H: 3 };\n/** 2-bit error-correction indicator used in the format information. */\nconst ECL_FORMAT_BITS: Record<QrErrorCorrection, number> = { L: 1, M: 0, Q: 3, H: 2 };\n\n// Indexed [ecl ordinal][version]; index 0 is unused padding.\nconst ECC_CODEWORDS_PER_BLOCK: readonly (readonly number[])[] = [\n [-1, 7, 10, 15, 20, 26, 18, 20, 24, 30, 18, 20, 24, 26, 30, 22, 24, 28, 30, 28, 28, 28, 28, 30, 30, 26, 28, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30],\n [-1, 10, 16, 26, 18, 24, 16, 18, 22, 22, 26, 30, 22, 22, 24, 24, 28, 28, 26, 26, 26, 26, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28],\n [-1, 13, 22, 18, 26, 18, 24, 18, 22, 20, 24, 28, 26, 24, 20, 30, 24, 28, 28, 26, 30, 28, 30, 30, 30, 30, 28, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30],\n [-1, 17, 28, 22, 16, 22, 28, 26, 26, 24, 28, 24, 28, 22, 24, 24, 30, 28, 28, 26, 28, 30, 24, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30],\n];\nconst NUM_ERROR_CORRECTION_BLOCKS: readonly (readonly number[])[] = [\n [-1, 1, 1, 1, 1, 1, 2, 2, 2, 2, 4, 4, 4, 4, 4, 6, 6, 6, 6, 7, 8, 8, 9, 9, 10, 12, 12, 12, 13, 14, 15, 16, 17, 18, 19, 19, 20, 21, 22, 24, 25],\n [-1, 1, 1, 1, 2, 2, 4, 4, 4, 5, 5, 5, 8, 9, 9, 10, 10, 11, 13, 14, 16, 17, 17, 18, 20, 21, 23, 25, 26, 28, 29, 31, 33, 35, 37, 38, 40, 43, 45, 47, 49],\n [-1, 1, 1, 2, 2, 4, 4, 6, 6, 8, 8, 8, 10, 12, 16, 12, 17, 16, 18, 21, 20, 23, 23, 25, 27, 29, 34, 34, 35, 38, 40, 43, 45, 48, 51, 53, 56, 59, 62, 65, 68],\n [-1, 1, 1, 2, 4, 4, 4, 5, 6, 8, 8, 11, 11, 16, 16, 18, 16, 19, 21, 25, 25, 25, 34, 30, 32, 35, 37, 40, 42, 45, 48, 51, 54, 57, 60, 63, 66, 70, 74, 77, 81],\n];\n\nconst PENALTY_N1 = 3;\nconst PENALTY_N2 = 3;\nconst PENALTY_N3 = 40;\nconst PENALTY_N4 = 10;\n\n/**\n * Encodes `text` (UTF-8, byte mode) into the smallest QR version that fits.\n * `mask` forces a mask pattern (0–7); by default the lowest-penalty mask is chosen.\n * Throws if the text exceeds version 40 capacity for the error-correction level.\n */\nexport function encodeQr(text: string, options: { ecl?: QrErrorCorrection; mask?: number } = {}): QrMatrix {\n const ecl = options.ecl ?? 'M';\n const forcedMask = options.mask;\n if (forcedMask !== undefined && (!Number.isInteger(forcedMask) || forcedMask < 0 || forcedMask > 7)) {\n throw new RangeError('Mask must be an integer from 0 to 7.');\n }\n const bytes = new TextEncoder().encode(text);\n\n // Smallest version whose data capacity fits mode (4) + count + 8 bits per byte.\n let version = 1;\n let dataBits = 0;\n for (; ; version++) {\n if (version > 40) throw new RangeError('Data too long for a QR code.');\n dataBits = 4 + (version <= 9 ? 8 : 16) + bytes.length * 8;\n if (dataBits <= numDataCodewords(version, ecl) * 8) break;\n }\n\n // Bit stream: byte-mode indicator, character count, data, terminator, byte padding.\n const bits: number[] = [];\n appendBits(bits, 0b0100, 4);\n appendBits(bits, bytes.length, version <= 9 ? 8 : 16);\n for (const b of bytes) appendBits(bits, b, 8);\n const capacityBits = numDataCodewords(version, ecl) * 8;\n appendBits(bits, 0, Math.min(4, capacityBits - bits.length));\n appendBits(bits, 0, (8 - (bits.length % 8)) % 8);\n for (let pad = 0xec; bits.length < capacityBits; pad ^= 0xec ^ 0x11) appendBits(bits, pad, 8);\n\n const dataCodewords: number[] = [];\n for (let i = 0; i < bits.length; i += 8) {\n let byte = 0;\n for (let j = 0; j < 8; j++) byte = (byte << 1) | bits[i + j];\n dataCodewords.push(byte);\n }\n\n const qr = new QrBuilder(version, ecl);\n qr.drawFunctionPatterns();\n qr.drawCodewords(addEccAndInterleave(dataCodewords, version, ecl));\n\n let mask = forcedMask ?? -1;\n if (mask === -1) {\n let minPenalty = Infinity;\n for (let m = 0; m < 8; m++) {\n qr.applyMask(m);\n qr.drawFormatBits(m);\n const penalty = qr.penaltyScore();\n if (penalty < minPenalty) { mask = m; minPenalty = penalty; }\n qr.applyMask(m); // XOR again to undo\n }\n }\n qr.applyMask(mask);\n qr.drawFormatBits(mask);\n return { size: qr.size, version, mask, modules: qr.modules };\n}\n\nfunction appendBits(out: number[], value: number, length: number) {\n for (let i = length - 1; i >= 0; i--) out.push((value >>> i) & 1);\n}\n\nfunction getBit(x: number, i: number): boolean {\n return ((x >>> i) & 1) !== 0;\n}\n\n/** Data + ECC modules available after function patterns, version and format info. */\nfunction numRawDataModules(version: number): number {\n let result = (16 * version + 128) * version + 64;\n if (version >= 2) {\n const numAlign = Math.floor(version / 7) + 2;\n result -= (25 * numAlign - 10) * numAlign - 55;\n if (version >= 7) result -= 36;\n }\n return result;\n}\n\nfunction numDataCodewords(version: number, ecl: QrErrorCorrection): number {\n const e = ECL_ORDINAL[ecl];\n return Math.floor(numRawDataModules(version) / 8) - ECC_CODEWORDS_PER_BLOCK[e][version] * NUM_ERROR_CORRECTION_BLOCKS[e][version];\n}\n\nfunction addEccAndInterleave(data: number[], version: number, ecl: QrErrorCorrection): number[] {\n const e = ECL_ORDINAL[ecl];\n const numBlocks = NUM_ERROR_CORRECTION_BLOCKS[e][version];\n const blockEccLen = ECC_CODEWORDS_PER_BLOCK[e][version];\n const rawCodewords = Math.floor(numRawDataModules(version) / 8);\n const numShortBlocks = numBlocks - (rawCodewords % numBlocks);\n const shortBlockLen = Math.floor(rawCodewords / numBlocks);\n\n const divisor = reedSolomonDivisor(blockEccLen);\n const blocks: number[][] = [];\n for (let i = 0, k = 0; i < numBlocks; i++) {\n const dat = data.slice(k, k + shortBlockLen - blockEccLen + (i < numShortBlocks ? 0 : 1));\n k += dat.length;\n const ecc = reedSolomonRemainder(dat, divisor);\n if (i < numShortBlocks) dat.push(0); // placeholder so all blocks share a length; skipped below\n blocks.push(dat.concat(ecc));\n }\n\n const result: number[] = [];\n for (let i = 0; i < blocks[0].length; i++) {\n blocks.forEach((block, j) => {\n if (i !== shortBlockLen - blockEccLen || j >= numShortBlocks) result.push(block[i]);\n });\n }\n return result;\n}\n\nfunction reedSolomonDivisor(degree: number): number[] {\n const result = new Array<number>(degree).fill(0);\n result[degree - 1] = 1;\n let root = 1;\n for (let i = 0; i < degree; i++) {\n for (let j = 0; j < result.length; j++) {\n result[j] = reedSolomonMultiply(result[j], root);\n if (j + 1 < result.length) result[j] ^= result[j + 1];\n }\n root = reedSolomonMultiply(root, 0x02);\n }\n return result;\n}\n\nfunction reedSolomonRemainder(data: readonly number[], divisor: readonly number[]): number[] {\n const result = divisor.map(() => 0);\n for (const b of data) {\n const factor = b ^ (result.shift() as number);\n result.push(0);\n divisor.forEach((coef, i) => (result[i] ^= reedSolomonMultiply(coef, factor)));\n }\n return result;\n}\n\n/** Multiplication in GF(2^8) modulo x^8 + x^4 + x^3 + x^2 + 1. */\nfunction reedSolomonMultiply(x: number, y: number): number {\n let z = 0;\n for (let i = 7; i >= 0; i--) {\n z = (z << 1) ^ ((z >>> 7) * 0x11d);\n z ^= ((y >>> i) & 1) * x;\n }\n return z;\n}\n\nclass QrBuilder {\n readonly size: number;\n readonly modules: boolean[][];\n private readonly isFunction: boolean[][];\n\n constructor(private readonly version: number, private readonly ecl: QrErrorCorrection) {\n this.size = version * 4 + 17;\n this.modules = Array.from({ length: this.size }, () => new Array<boolean>(this.size).fill(false));\n this.isFunction = Array.from({ length: this.size }, () => new Array<boolean>(this.size).fill(false));\n }\n\n drawFunctionPatterns() {\n for (let i = 0; i < this.size; i++) {\n this.setFunction(6, i, i % 2 === 0);\n this.setFunction(i, 6, i % 2 === 0);\n }\n this.drawFinderPattern(3, 3);\n this.drawFinderPattern(this.size - 4, 3);\n this.drawFinderPattern(3, this.size - 4);\n\n const align = this.alignmentPatternPositions();\n const n = align.length;\n for (let i = 0; i < n; i++) {\n for (let j = 0; j < n; j++) {\n if (!((i === 0 && j === 0) || (i === 0 && j === n - 1) || (i === n - 1 && j === 0))) {\n this.drawAlignmentPattern(align[i], align[j]);\n }\n }\n }\n this.drawFormatBits(0); // reserve the area; real bits are drawn after masking\n this.drawVersion();\n }\n\n drawFormatBits(mask: number) {\n const data = (ECL_FORMAT_BITS[this.ecl] << 3) | mask;\n let rem = data;\n for (let i = 0; i < 10; i++) rem = (rem << 1) ^ ((rem >>> 9) * 0x537);\n const bits = ((data << 10) | rem) ^ 0x5412;\n\n for (let i = 0; i <= 5; i++) this.setFunction(8, i, getBit(bits, i));\n this.setFunction(8, 7, getBit(bits, 6));\n this.setFunction(8, 8, getBit(bits, 7));\n this.setFunction(7, 8, getBit(bits, 8));\n for (let i = 9; i < 15; i++) this.setFunction(14 - i, 8, getBit(bits, i));\n\n for (let i = 0; i < 8; i++) this.setFunction(this.size - 1 - i, 8, getBit(bits, i));\n for (let i = 8; i < 15; i++) this.setFunction(8, this.size - 15 + i, getBit(bits, i));\n this.setFunction(8, this.size - 8, true); // always-dark module\n }\n\n drawCodewords(data: readonly number[]) {\n let i = 0;\n for (let right = this.size - 1; right >= 1; right -= 2) {\n if (right === 6) right = 5; // skip the vertical timing column\n for (let vert = 0; vert < this.size; vert++) {\n for (let j = 0; j < 2; j++) {\n const x = right - j;\n const upward = ((right + 1) & 2) === 0;\n const y = upward ? this.size - 1 - vert : vert;\n if (!this.isFunction[y][x] && i < data.length * 8) {\n this.modules[y][x] = getBit(data[i >>> 3], 7 - (i & 7));\n i++;\n }\n }\n }\n }\n }\n\n /** XORs the data modules with mask pattern `mask`; applying it twice restores the original. */\n applyMask(mask: number) {\n for (let y = 0; y < this.size; y++) {\n for (let x = 0; x < this.size; x++) {\n let invert: boolean;\n switch (mask) {\n case 0: invert = (x + y) % 2 === 0; break;\n case 1: invert = y % 2 === 0; break;\n case 2: invert = x % 3 === 0; break;\n case 3: invert = (x + y) % 3 === 0; break;\n case 4: invert = (Math.floor(x / 3) + Math.floor(y / 2)) % 2 === 0; break;\n case 5: invert = ((x * y) % 2) + ((x * y) % 3) === 0; break;\n case 6: invert = (((x * y) % 2) + ((x * y) % 3)) % 2 === 0; break;\n default: invert = (((x + y) % 2) + ((x * y) % 3)) % 2 === 0; break;\n }\n if (!this.isFunction[y][x] && invert) this.modules[y][x] = !this.modules[y][x];\n }\n }\n }\n\n penaltyScore(): number {\n let result = 0;\n const size = this.size;\n const m = this.modules;\n\n // Runs of 5+ same-colored modules and finder-like patterns, in rows then columns.\n for (const horizontal of [true, false]) {\n for (let a = 0; a < size; a++) {\n let runColor = false;\n let runLen = 0;\n const history = [0, 0, 0, 0, 0, 0, 0];\n for (let b = 0; b < size; b++) {\n const color = horizontal ? m[a][b] : m[b][a];\n if (color === runColor) {\n runLen++;\n if (runLen === 5) result += PENALTY_N1;\n else if (runLen > 5) result++;\n } else {\n this.finderPenaltyAddHistory(runLen, history);\n if (!runColor) result += this.finderPenaltyCountPatterns(history) * PENALTY_N3;\n runColor = color;\n runLen = 1;\n }\n }\n result += this.finderPenaltyTerminateAndCount(runColor, runLen, history) * PENALTY_N3;\n }\n }\n\n // 2x2 blocks of one color.\n for (let y = 0; y < size - 1; y++) {\n for (let x = 0; x < size - 1; x++) {\n const c = m[y][x];\n if (c === m[y][x + 1] && c === m[y + 1][x] && c === m[y + 1][x + 1]) result += PENALTY_N2;\n }\n }\n\n // Balance of dark and light modules.\n let dark = 0;\n for (const row of m) for (const c of row) if (c) dark++;\n const total = size * size;\n const k = Math.ceil(Math.abs(dark * 20 - total * 10) / total) - 1;\n result += k * PENALTY_N4;\n return result;\n }\n\n private finderPenaltyCountPatterns(h: readonly number[]): number {\n const n = h[1];\n const core = n > 0 && h[2] === n && h[3] === n * 3 && h[4] === n && h[5] === n;\n return (core && h[0] >= n * 4 && h[6] >= n ? 1 : 0) + (core && h[6] >= n * 4 && h[0] >= n ? 1 : 0);\n }\n\n private finderPenaltyTerminateAndCount(runColor: boolean, runLen: number, history: number[]): number {\n if (runColor) {\n this.finderPenaltyAddHistory(runLen, history);\n runLen = 0;\n }\n runLen += this.size; // light border after the last run\n this.finderPenaltyAddHistory(runLen, history);\n return this.finderPenaltyCountPatterns(history);\n }\n\n private finderPenaltyAddHistory(runLen: number, history: number[]) {\n if (history[0] === 0) runLen += this.size; // light border before the first run\n history.pop();\n history.unshift(runLen);\n }\n\n private alignmentPatternPositions(): number[] {\n if (this.version === 1) return [];\n const numAlign = Math.floor(this.version / 7) + 2;\n const step = Math.floor((this.version * 8 + numAlign * 3 + 5) / (numAlign * 4 - 4)) * 2;\n const result = [6];\n for (let pos = this.size - 7; result.length < numAlign; pos -= step) result.splice(1, 0, pos);\n return result;\n }\n\n private drawFinderPattern(x: number, y: number) {\n for (let dy = -4; dy <= 4; dy++) {\n for (let dx = -4; dx <= 4; dx++) {\n const dist = Math.max(Math.abs(dx), Math.abs(dy));\n const xx = x + dx;\n const yy = y + dy;\n if (xx >= 0 && xx < this.size && yy >= 0 && yy < this.size) this.setFunction(xx, yy, dist !== 2 && dist !== 4);\n }\n }\n }\n\n private drawAlignmentPattern(x: number, y: number) {\n for (let dy = -2; dy <= 2; dy++) {\n for (let dx = -2; dx <= 2; dx++) this.setFunction(x + dx, y + dy, Math.max(Math.abs(dx), Math.abs(dy)) !== 1);\n }\n }\n\n private drawVersion() {\n if (this.version < 7) return;\n let rem = this.version;\n for (let i = 0; i < 12; i++) rem = (rem << 1) ^ ((rem >>> 11) * 0x1f25);\n const bits = (this.version << 12) | rem;\n for (let i = 0; i < 18; i++) {\n const bit = getBit(bits, i);\n const a = this.size - 11 + (i % 3);\n const b = Math.floor(i / 3);\n this.setFunction(a, b, bit);\n this.setFunction(b, a, bit);\n }\n }\n\n private setFunction(x: number, y: number, dark: boolean) {\n this.modules[y][x] = dark;\n this.isFunction[y][x] = true;\n }\n}\n","import { Component, ChangeDetectionStrategy, input, computed } from '@angular/core';\nimport { ButtonComponent } from '../button/button.component';\nimport { encodeQr, QrMatrix } from './qr-encoder';\n\n/** Light modules around the code, as required by the QR spec. */\nconst QUIET_ZONE = 4;\nconst DARK = '#1e293b';\nconst LIGHT = '#ffffff';\n\n/**\n * QR code for a URL (or any text), with a \"Download QR Code\" button (PNG). Projected content\n * renders next to the download button (e.g. an \"Open Kiosk Mode\" action).\n *\n * Self-contained: uses the built-in encoder in ./qr-encoder.ts — no third-party QR package.\n *\n * ```html\n * <ui-qr-code [url]=\"publicLink()\" filename=\"open-house-qr-code\">\n * <ui-button variant=\"secondary\" size=\"sm\" (click)=\"openKiosk()\">Open Kiosk Mode</ui-button>\n * </ui-qr-code>\n * ```\n */\n@Component({\n selector: 'ui-qr-code',\n standalone: true,\n changeDetection: ChangeDetectionStrategy.OnPush,\n imports: [ButtonComponent],\n template: `\n <div class=\"qr-box\">\n @if (qr().error) {\n <div class=\"qr-error\" role=\"alert\">\n <svg width=\"20\" height=\"20\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"#ef4444\" stroke-width=\"2\" aria-hidden=\"true\"><circle cx=\"12\" cy=\"12\" r=\"10\"/><line x1=\"12\" y1=\"8\" x2=\"12\" y2=\"12\"/><line x1=\"12\" y1=\"16\" x2=\"12.01\" y2=\"16\"/></svg>\n <span>QR code unavailable. Copy the link below instead.</span>\n </div>\n } @else if (qr().dataUrl) {\n <img\n [src]=\"qr().dataUrl\"\n [width]=\"size()\"\n [height]=\"size()\"\n [alt]=\"alt()\"\n class=\"qr-img\"\n />\n @if (isIOS) {\n <p class=\"qr-ios-hint\">Long-press the image above to save it.</p>\n }\n }\n <div class=\"qr-actions\">\n @if (!isIOS) {\n <ui-button variant=\"secondary\" size=\"sm\" [disabled]=\"!qr().dataUrl\" (click)=\"download()\">\n <svg width=\"14\" height=\"14\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\" aria-hidden=\"true\">\n <path d=\"M21 15v4a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2v-4\"/>\n <polyline points=\"7 10 12 15 17 10\"/>\n <line x1=\"12\" y1=\"15\" x2=\"12\" y2=\"3\"/>\n </svg>\n Download QR Code\n </ui-button>\n }\n <ng-content />\n </div>\n </div>\n `,\n styles: [`\n .qr-box {\n display: flex; flex-direction: column; align-items: center; gap: 12px;\n padding: 20px; background: #f8fafc; border: 1px solid #e2e8f0; border-radius: 12px;\n }\n .qr-img { display: block; border-radius: 8px; max-width: 100%; height: auto; image-rendering: pixelated; }\n .qr-error {\n display: flex; align-items: flex-start; gap: 8px; padding: 12px 14px;\n background: #fef2f2; border: 1px solid #fecaca; border-radius: 8px;\n width: 100%; box-sizing: border-box;\n }\n .qr-error span { font-size: 0.8125rem; color: #dc2626; line-height: 1.5; }\n .qr-error svg { flex-shrink: 0; margin-top: 1px; }\n .qr-ios-hint { font-size: 0.75rem; color: #94a3b8; margin: 0; text-align: center; }\n .qr-actions { display: flex; gap: 8px; flex-wrap: wrap; justify-content: center; }\n `],\n})\nexport class QrCodeComponent {\n /** URL (or text) encoded into the QR code. Nothing is rendered while empty. */\n readonly url = input('');\n /** Displayed width/height in px. The PNG is rendered at 2x for sharp printing. */\n readonly size = input(240);\n /** Download file name, without the .png extension. */\n readonly filename = input('qr-code');\n readonly alt = input('QR code — scan to open the link');\n\n /** iOS Safari doesn't support programmatic download of data: or blob: URLs. */\n protected readonly isIOS = typeof navigator !== 'undefined' &&\n (/iPad|iPhone|iPod/.test(navigator.userAgent) || (navigator.platform === 'MacIntel' && navigator.maxTouchPoints > 1));\n\n protected readonly qr = computed((): { dataUrl: string; error: boolean } => {\n const url = this.url();\n if (!url) return { dataUrl: '', error: false };\n try {\n return { dataUrl: renderPng(encodeQr(url), this.size() * 2), error: false };\n } catch {\n return { dataUrl: '', error: true };\n }\n });\n\n download() {\n const dataUrl = this.qr().dataUrl;\n if (!dataUrl) return;\n try {\n // Convert the data URL to a Blob for broader browser support (Safari desktop and data: hrefs).\n const [header, b64] = dataUrl.split(',');\n const mime = header.match(/:(.*?);/)?.[1] ?? 'image/png';\n const bytes = atob(b64);\n const arr = new Uint8Array(bytes.length);\n for (let i = 0; i < bytes.length; i++) arr[i] = bytes.charCodeAt(i);\n const blobUrl = URL.createObjectURL(new Blob([arr], { type: mime }));\n\n const a = document.createElement('a');\n a.href = blobUrl;\n a.download = `${this.filename()}.png`;\n document.body.appendChild(a);\n a.click();\n document.body.removeChild(a);\n // Small delay before revoking so the browser has time to start the download.\n setTimeout(() => URL.revokeObjectURL(blobUrl), 1000);\n } catch {\n window.open(dataUrl, '_blank', 'noopener');\n }\n }\n}\n\n/** Draws the matrix (plus quiet zone) onto a canvas with whole-pixel modules and returns a PNG data URL. */\nfunction renderPng(qr: QrMatrix, targetPx: number): string {\n const count = qr.size + QUIET_ZONE * 2;\n const scale = Math.max(1, Math.floor(targetPx / count));\n const canvas = document.createElement('canvas');\n canvas.width = canvas.height = count * scale;\n const ctx = canvas.getContext('2d');\n if (!ctx) throw new Error('Canvas 2D context unavailable.');\n ctx.fillStyle = LIGHT;\n ctx.fillRect(0, 0, canvas.width, canvas.height);\n ctx.fillStyle = DARK;\n for (let y = 0; y < qr.size; y++) {\n for (let x = 0; x < qr.size; x++) {\n if (qr.modules[y][x]) ctx.fillRect((x + QUIET_ZONE) * scale, (y + QUIET_ZONE) * scale, scale, scale);\n }\n }\n return canvas.toDataURL('image/png');\n}\n","import { Component, ChangeDetectionStrategy, input, computed } from '@angular/core';\nimport { RouterLink } from '@angular/router';\nimport { BadgeComponent } from '../badge/badge.component';\nimport { EmptyStateComponent } from '../empty-state/empty-state.component';\nimport { ButtonComponent } from '../button/button.component';\nimport { BadgeVariant } from '../data-table/data-table.types';\n\nexport interface PanelInfoItem {\n label: string;\n value?: string | null;\n badge?: { label: string; variant: BadgeVariant };\n badges?: Array<{ label: string; variant: BadgeVariant }>;\n /** Router link for the value. */\n link?: string;\n /** Show the value with an \"open\" icon instead of blue link styling. */\n linkIcon?: boolean;\n /** Renders a small edit (pencil) icon button after the value. */\n editAction?: () => void;\n editTooltip?: string;\n muted?: boolean;\n danger?: boolean;\n hidden?: boolean;\n}\n\nexport interface PanelInfoSection {\n title?: string;\n headerLink?: { label: string; url: string };\n items: PanelInfoItem[];\n columns?: 2;\n}\n\n/**\n * Read-only label/value list for side panels and detail views, grouped into bordered\n * sections. Items with `hidden: true` are skipped; sections with no visible items are\n * dropped unless `showEmptyState` is set (then they show an empty-state message).\n *\n * Usage:\n * <ui-panel-info-list [sections]=\"[{ title: 'DETAILS', items: [\n * { label: 'Status', badge: { label: 'Pending', variant: 'yellow' } },\n * { label: 'Notes', value: notes, hidden: !notes },\n * ] }]\" />\n */\n@Component({\n selector: 'ui-panel-info-list',\n standalone: true,\n changeDetection: ChangeDetectionStrategy.OnPush,\n imports: [BadgeComponent, RouterLink, EmptyStateComponent, ButtonComponent],\n template: `\n @for (section of displaySections(); track section.title ?? $index) {\n <div class=\"pil-section\">\n @if (section.title) {\n <div class=\"pil-section-label\">\n <span>{{ section.title }}</span>\n @if (section.headerLink) {\n <a class=\"pil-header-link\" [routerLink]=\"section.headerLink.url\">{{ section.headerLink.label }}</a>\n }\n </div>\n }\n @if (visibleItems(section).length === 0) {\n <ui-empty-state message=\"No information available.\" />\n } @else if (section.columns === 2) {\n <div class=\"pil-grid-2\">\n @for (item of visibleItems(section); track item.label) {\n <div class=\"pil-grid-cell\">\n <span class=\"pil-label\">{{ item.label }}</span>\n <span class=\"pil-value pil-value--block\">\n @if (item.badge) {\n <ui-badge [label]=\"item.badge.label\" [variant]=\"item.badge.variant\" />\n } @else {\n <span [class.pil-muted]=\"item.muted\" [class.pil-danger]=\"item.danger\">{{ item.value || '—' }}</span>\n }\n </span>\n </div>\n }\n </div>\n } @else {\n @for (item of visibleItems(section); track item.label) {\n <div class=\"pil-row\">\n <span class=\"pil-label\">{{ item.label }}</span>\n <span class=\"pil-value\">\n @if (item.badges) {\n <span class=\"pil-badges\">\n @for (b of item.badges; track b.label) {\n <ui-badge [label]=\"b.label\" [variant]=\"b.variant\" />\n }\n </span>\n } @else if (item.badge) {\n <ui-badge [label]=\"item.badge.label\" [variant]=\"item.badge.variant\" />\n } @else if (item.link && item.value && item.linkIcon) {\n <a class=\"pil-link-icon\" [routerLink]=\"item.link\">\n {{ item.value }}\n <svg width=\"12\" height=\"12\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2.5\" aria-hidden=\"true\"><path d=\"M18 13v6a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2V8a2 2 0 0 1 2-2h6\"/><polyline points=\"15 3 21 3 21 9\"/><line x1=\"10\" y1=\"14\" x2=\"21\" y2=\"3\"/></svg>\n </a>\n } @else if (item.link && item.value) {\n <a class=\"pil-link\" [routerLink]=\"item.link\">{{ item.value }}</a>\n } @else {\n <span [class.pil-muted]=\"item.muted\" [class.pil-danger]=\"item.danger\">{{ item.value || '—' }}</span>\n }\n @if (item.editAction) {\n <ui-button class=\"pil-edit-btn\" variant=\"icon\" size=\"xs\"\n [ariaLabel]=\"item.editTooltip ?? 'Edit'\" [attr.title]=\"item.editTooltip ?? 'Edit'\"\n (click)=\"item.editAction!()\">\n <svg width=\"11\" height=\"11\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2.5\" aria-hidden=\"true\"><path d=\"M11 4H4a2 2 0 0 0-2 2v14a2 2 0 0 0 2 2h14a2 2 0 0 0 2-2v-7\"/><path d=\"M18.5 2.5a2.121 2.121 0 0 1 3 3L12 15l-4 1 1-4 9.5-9.5z\"/></svg>\n </ui-button>\n }\n </span>\n </div>\n }\n }\n </div>\n }\n `,\n styles: [`\n :host { display: flex; flex-direction: column; gap: 12px; }\n\n .pil-section {\n border: 1px solid #e2e8f0;\n border-radius: 8px;\n overflow: hidden;\n }\n\n .pil-section-label {\n display: flex;\n justify-content: space-between;\n align-items: center;\n font-size: 0.6875rem;\n font-weight: 700;\n letter-spacing: 0.07em;\n color: #94a3b8;\n text-transform: uppercase;\n padding: 8px 14px;\n background: #f8fafc;\n border-bottom: 1px solid #e2e8f0;\n }\n\n .pil-header-link {\n font-size: 0.75rem;\n font-weight: 500;\n color: #2563eb;\n text-decoration: none;\n text-transform: none;\n letter-spacing: 0;\n &:hover { text-decoration: underline; }\n }\n\n .pil-row {\n display: flex;\n justify-content: space-between;\n align-items: center;\n gap: 12px;\n padding: 9px 14px;\n border-bottom: 1px solid #f1f5f9;\n &:last-child { border-bottom: none; }\n }\n\n .pil-label {\n font-size: 0.8125rem;\n color: #64748b;\n flex-shrink: 0;\n }\n\n .pil-value {\n font-size: 0.875rem;\n font-weight: 500;\n color: #1e293b;\n text-align: right;\n display: flex;\n align-items: center;\n justify-content: flex-end;\n }\n\n .pil-badges { display: flex; align-items: center; gap: 4px; flex-wrap: wrap; justify-content: flex-end; }\n\n .pil-edit-btn { margin-left: 4px; flex-shrink: 0; }\n\n ui-empty-state { padding: 12px 0; }\n .pil-muted { color: #1e293b; font-weight: 400; }\n .pil-danger { color: #ef4444; font-weight: 600; }\n\n .pil-link {\n font-size: 0.875rem;\n font-weight: 500;\n color: #2563eb;\n text-decoration: none;\n &:hover { text-decoration: underline; }\n }\n\n .pil-link-icon {\n display: inline-flex;\n align-items: center;\n gap: 5px;\n font-size: 0.875rem;\n font-weight: 500;\n color: #1e293b;\n text-decoration: none;\n svg { color: #94a3b8; flex-shrink: 0; }\n }\n\n .pil-grid-2 {\n display: grid;\n grid-template-columns: 1fr 1fr;\n }\n\n .pil-grid-cell {\n display: flex;\n flex-direction: column;\n gap: 3px;\n padding: 10px 14px;\n border-right: 1px solid #f1f5f9;\n &:last-child { border-right: none; }\n }\n\n .pil-value--block {\n text-align: left;\n justify-content: flex-start;\n }\n `],\n})\nexport class PanelInfoListComponent {\n readonly sections = input<PanelInfoSection[]>([]);\n readonly showEmptyState = input(false);\n\n readonly displaySections = computed(() =>\n this.showEmptyState()\n ? this.sections()\n : this.sections().filter(s => s.items.some(i => !i.hidden))\n );\n\n visibleItems(section: PanelInfoSection): PanelInfoItem[] {\n return section.items.filter(i => !i.hidden);\n }\n}\n","export interface UsStateOption {\n value: string;\n label: string;\n}\n\nexport const US_STATES: UsStateOption[] = [\n { value: 'AL', label: 'Alabama' },\n { value: 'AK', label: 'Alaska' },\n { value: 'AZ', label: 'Arizona' },\n { value: 'AR', label: 'Arkansas' },\n { value: 'CA', label: 'California' },\n { value: 'CO', label: 'Colorado' },\n { value: 'CT', label: 'Connecticut' },\n { value: 'DE', label: 'Delaware' },\n { value: 'FL', label: 'Florida' },\n { value: 'GA', label: 'Georgia' },\n { value: 'HI', label: 'Hawaii' },\n { value: 'ID', label: 'Idaho' },\n { value: 'IL', label: 'Illinois' },\n { value: 'IN', label: 'Indiana' },\n { value: 'IA', label: 'Iowa' },\n { value: 'KS', label: 'Kansas' },\n { value: 'KY', label: 'Kentucky' },\n { value: 'LA', label: 'Louisiana' },\n { value: 'ME', label: 'Maine' },\n { value: 'MD', label: 'Maryland' },\n { value: 'MA', label: 'Massachusetts' },\n { value: 'MI', label: 'Michigan' },\n { value: 'MN', label: 'Minnesota' },\n { value: 'MS', label: 'Mississippi' },\n { value: 'MO', label: 'Missouri' },\n { value: 'MT', label: 'Montana' },\n { value: 'NE', label: 'Nebraska' },\n { value: 'NV', label: 'Nevada' },\n { value: 'NH', label: 'New Hampshire' },\n { value: 'NJ', label: 'New Jersey' },\n { value: 'NM', label: 'New Mexico' },\n { value: 'NY', label: 'New York' },\n { value: 'NC', label: 'North Carolina' },\n { value: 'ND', label: 'North Dakota' },\n { value: 'OH', label: 'Ohio' },\n { value: 'OK', label: 'Oklahoma' },\n { value: 'OR', label: 'Oregon' },\n { value: 'PA', label: 'Pennsylvania' },\n { value: 'RI', label: 'Rhode Island' },\n { value: 'SC', label: 'South Carolina' },\n { value: 'SD', label: 'South Dakota' },\n { value: 'TN', label: 'Tennessee' },\n { value: 'TX', label: 'Texas' },\n { value: 'UT', label: 'Utah' },\n { value: 'VT', label: 'Vermont' },\n { value: 'VA', label: 'Virginia' },\n { value: 'WA', label: 'Washington' },\n { value: 'WV', label: 'West Virginia' },\n { value: 'WI', label: 'Wisconsin' },\n { value: 'WY', label: 'Wyoming' },\n { value: 'DC', label: 'District of Columbia' },\n];\n","import {\n Component, input, output, signal, computed,\n inject, OnInit, OnDestroy, ChangeDetectionStrategy,\n} from '@angular/core';\nimport { RouterLink, RouterLinkActive, Router, NavigationEnd } from '@angular/router';\nimport { DomSanitizer, SafeHtml } from '@angular/platform-browser';\nimport { filter, Subscription } from 'rxjs';\nimport { ConfirmOutletComponent } from '../confirm/confirm-outlet.component';\n\n// ── Public interfaces ────────────────────────────────────────────────────────\n\nexport interface NavLink {\n label: string;\n route: string;\n /** Full <svg>…</svg> HTML string used as the icon. */\n icon?: string;\n /** Require an exact URL match for routerLinkActive. Default false. */\n exact?: boolean;\n /** Red badge count shown on the link (e.g. unread messages). */\n badge?: number;\n /** Hide this link. Default true (visible). */\n visible?: boolean;\n}\n\nexport interface NavGroup {\n key: string;\n label: string;\n /** Hide the entire group. Default true (visible). */\n visible?: boolean;\n /** Start collapsed. Default false. */\n defaultCollapsed?: boolean;\n links: NavLink[];\n}\n\nexport interface ShellUser {\n firstName?: string;\n lastName?: string;\n role?: string;\n}\n\n/** localStorage key the shell uses for a given `persistKey` (exported so apps can migrate an older key). */\nexport function appShellGroupsStorageKey(persistKey: string): string {\n return `ui-app-shell-groups:${persistKey}`;\n}\n\n// ─────────────────────────────────────────────────────────────────────────────\n\n@Component({\n selector: 'ui-app-shell',\n standalone: true,\n changeDetection: ChangeDetectionStrategy.OnPush,\n imports: [RouterLink, RouterLinkActive, ConfirmOutletComponent],\n template: `\n <div\n class=\"shell\"\n [class.sidebar-open]=\"_sidebarOpen()\"\n [class.collapsed]=\"_isRail()\"\n [style.--shell-bg]=\"backgroundColor() || null\"\n [style.--shell-text]=\"textColor() || null\"\n >\n\n <!-- Mobile backdrop -->\n <div\n class=\"sidebar-backdrop\"\n (click)=\"closeSidebar()\"\n aria-hidden=\"true\"\n ></div>\n\n <!-- ── Sidebar ────────────────────────────────────────────── -->\n <aside class=\"sidebar\" id=\"main-sidebar\" aria-label=\"Main navigation\">\n\n <!-- Brand slot (project custom brand or fallback to appName) -->\n <div class=\"sidebar-header\">\n <div class=\"brand-full\">\n <ng-content select=\"[shell-brand]\">\n <div class=\"brand-default\">\n <span class=\"brand-mark\" aria-hidden=\"true\">{{ appName().charAt(0) }}</span>\n <span class=\"brand-text\">{{ appName() }}</span>\n </div>\n </ng-content>\n </div>\n @if (collapsible()) {\n <button\n class=\"rail-toggle\"\n type=\"button\"\n [attr.aria-label]=\"_collapsed() ? 'Expand sidebar' : 'Collapse sidebar'\"\n [attr.aria-pressed]=\"_collapsed()\"\n [attr.title]=\"_collapsed() ? 'Expand sidebar' : 'Collapse sidebar'\"\n (click)=\"toggleCollapsed()\"\n >\n <svg width=\"18\" height=\"18\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\"\n stroke-width=\"2\" stroke-linecap=\"round\" stroke-linejoin=\"round\" aria-hidden=\"true\">\n @if (_collapsed()) {\n <path d=\"M13 5l7 7-7 7M5 5l7 7-7 7\"/>\n } @else {\n <path d=\"M11 19l-7-7 7-7m8 14l-7-7 7-7\"/>\n }\n </svg>\n </button>\n }\n </div>\n\n <!-- Nav groups -->\n <nav class=\"sidebar-nav\" aria-label=\"Site navigation\">\n @for (group of _visibleGroups(); track group.key) {\n <div class=\"nav-group\">\n @if (_isRail()) { <div class=\"rail-divider\" aria-hidden=\"true\"></div> }\n <button\n class=\"nav-group-label\"\n type=\"button\"\n [attr.aria-expanded]=\"!_isCollapsed(group.key)\"\n [attr.aria-controls]=\"'navg-' + group.key\"\n (click)=\"toggleGroup(group.key)\"\n >\n <svg\n class=\"chevron\"\n [class.collapsed]=\"_isCollapsed(group.key)\"\n width=\"12\" height=\"12\" viewBox=\"0 0 24 24\"\n fill=\"none\" stroke=\"currentColor\" stroke-width=\"2.5\"\n aria-hidden=\"true\"\n >\n <polyline points=\"6 9 12 15 18 9\"/>\n </svg>\n {{ group.label }}\n </button>\n\n @if (_isRail() || !_isCollapsed(group.key)) {\n <div [id]=\"'navg-' + group.key\" role=\"list\">\n @for (link of _visibleLinks(group); track link.route) {\n <a\n role=\"listitem\"\n [routerLink]=\"link.route\"\n routerLinkActive=\"active\"\n [routerLinkActiveOptions]=\"{ exact: !!link.exact }\"\n #rla=\"routerLinkActive\"\n [attr.aria-current]=\"rla.isActive ? 'page' : null\"\n [attr.title]=\"_isRail() ? link.label : null\"\n [attr.aria-label]=\"_isRail() ? link.label : null\"\n >\n @if (link.icon) {\n <span\n class=\"nav-icon\"\n [innerHTML]=\"_safeIcon(link.icon)\"\n aria-hidden=\"true\"\n ></span>\n }\n <span class=\"nav-label\">{{ link.label }}</span>\n @if (link.badge && link.badge > 0) {\n <span\n class=\"nav-badge\"\n [attr.aria-label]=\"link.badge + ' unread'\"\n >{{ link.badge }}</span>\n }\n </a>\n }\n </div>\n }\n </div>\n }\n </nav>\n\n <!-- User footer -->\n <div class=\"sidebar-footer\">\n <div class=\"user-info\">\n <div class=\"user-avatar\" aria-hidden=\"true\">{{ _userInitials() }}</div>\n <div class=\"user-details\">\n <span class=\"user-name\">{{ _userName() }}</span>\n @if (user()?.role) {\n <span class=\"user-role\">{{ user()!.role }}</span>\n }\n </div>\n <button\n class=\"btn-logout\"\n type=\"button\"\n aria-label=\"Log out\"\n (click)=\"logoutClick.emit()\"\n >\n <svg width=\"18\" height=\"18\" viewBox=\"0 0 24 24\" fill=\"none\"\n stroke=\"currentColor\" stroke-width=\"1.8\"\n stroke-linecap=\"round\" stroke-linejoin=\"round\"\n aria-hidden=\"true\">\n <path d=\"M9 21H5a2 2 0 01-2-2V5a2 2 0 012-2h4\"/>\n <polyline points=\"16 17 21 12 16 7\"/>\n <line x1=\"21\" y1=\"12\" x2=\"9\" y2=\"12\"/>\n </svg>\n </button>\n </div>\n </div>\n </aside>\n\n <!-- ── Main area ──────────────────────────────────────────── -->\n <div class=\"main-area\">\n\n <!-- Top header -->\n <header class=\"top-header\" role=\"banner\">\n <button\n class=\"hamburger\"\n type=\"button\"\n [attr.aria-expanded]=\"_sidebarOpen()\"\n aria-controls=\"main-sidebar\"\n aria-label=\"Toggle navigation\"\n (click)=\"toggleSidebar()\"\n >\n <svg width=\"20\" height=\"20\" viewBox=\"0 0 24 24\" fill=\"none\"\n stroke=\"currentColor\" stroke-width=\"2\"\n stroke-linecap=\"round\" stroke-linejoin=\"round\"\n aria-hidden=\"true\">\n <line x1=\"3\" y1=\"6\" x2=\"21\" y2=\"6\"/>\n <line x1=\"3\" y1=\"12\" x2=\"21\" y2=\"12\"/>\n <line x1=\"3\" y1=\"18\" x2=\"21\" y2=\"18\"/>\n </svg>\n </button>\n\n @if (pageTitle()) {\n <div class=\"page-title\">{{ pageTitle() }}</div>\n }\n\n @if (readOnly()) {\n <span\n class=\"read-only-badge\"\n role=\"status\"\n aria-label=\"This page is read-only\"\n >Read only</span>\n }\n\n <!-- Right-side header content (search, notifications, etc.) -->\n <div class=\"header-end\">\n <ng-content select=\"[shell-header-end]\" />\n </div>\n </header>\n\n <!-- Banners / toasts above content -->\n <ng-content select=\"[shell-banners]\" />\n\n <!-- Page content -->\n <main class=\"content\" id=\"main-content\" role=\"main\" tabindex=\"-1\">\n <ng-content />\n </main>\n </div>\n </div>\n\n <!-- Global confirm overlay — position:fixed so it overlays without affecting layout -->\n <ui-confirm-outlet />\n `,\n styles: [`\n :host { display: contents; }\n\n /* ── Shell frame ────────────────────────────────────────────── */\n .shell {\n display: flex;\n height: 100vh; /* fallback for browsers without dvh */\n height: 100dvh; /* excludes the mobile browser's collapsing URL bar */\n overflow: hidden;\n font-family: 'Inter', -apple-system, BlinkMacSystemFont, 'Segoe UI', sans-serif;\n }\n\n /* ── Backdrop ────────────────────────────────────────────────── */\n .sidebar-backdrop {\n display: none;\n position: fixed;\n inset: 0;\n background: rgba(0, 0, 0, 0.5);\n backdrop-filter: blur(2px);\n -webkit-backdrop-filter: blur(2px);\n z-index: 199;\n }\n\n /* ── Sidebar ─────────────────────────────────────────────────── */\n .sidebar {\n width: 256px;\n background: var(--shell-bg, linear-gradient(175deg, #0f172a 0%, #111827 100%));\n display: flex;\n flex-direction: column;\n flex-shrink: 0;\n overflow: hidden;\n }\n\n .sidebar-header {\n display: flex;\n align-items: center;\n padding: 0 20px;\n min-height: 64px;\n border-bottom: 1px solid color-mix(in srgb, var(--shell-text, #fff) 6%, transparent);\n box-sizing: border-box;\n }\n\n .brand-default {\n display: flex;\n align-items: center;\n gap: 10px;\n }\n .brand-text {\n font-size: 1.125rem;\n font-weight: 700;\n color: var(--shell-text, #fff);\n letter-spacing: -0.01em;\n }\n\n /* ── Nav ──────────────────────────────────────────────────────── */\n .sidebar-nav {\n padding: 6px 0 8px;\n flex: 1;\n overflow-y: auto;\n overflow-x: hidden;\n scrollbar-width: thin;\n scrollbar-color: color-mix(in srgb, var(--shell-text, #fff) 8%, transparent) transparent;\n }\n .sidebar-nav::-webkit-scrollbar { width: 3px; }\n .sidebar-nav::-webkit-scrollbar-track { background: transparent; }\n .sidebar-nav::-webkit-scrollbar-thumb {\n background: color-mix(in srgb, var(--shell-text, #fff) 8%, transparent);\n border-radius: 2px;\n }\n\n .nav-group { margin-bottom: 6px; }\n\n .nav-group-label {\n display: flex;\n align-items: center;\n gap: 6px;\n width: 100%;\n padding: 8px 16px;\n font-size: 0.6875rem;\n font-weight: 700;\n color: color-mix(in srgb, var(--shell-text, #fff) 80%, transparent);\n text-transform: uppercase;\n letter-spacing: 0.12em;\n background: none;\n border: none;\n cursor: pointer;\n transition: color 150ms;\n text-align: left;\n border-radius: 6px;\n background: color-mix(in srgb, var(--shell-text, #fff) 10%, transparent);\n margin: 6px 4px 4px;\n width: calc(100% - 8px);\n box-sizing: border-box;\n }\n .nav-group-label:hover { color: color-mix(in srgb, var(--shell-text, #fff) 100%, transparent); }\n .nav-group-label:focus-visible {\n outline: 2px solid rgba(96, 165, 250, 0.5);\n outline-offset: -1px;\n }\n\n .chevron {\n flex-shrink: 0;\n transition: transform 200ms cubic-bezier(0.4, 0, 0.2, 1);\n opacity: 0.85;\n }\n .chevron.collapsed { transform: rotate(-90deg); }\n\n /* Nav links */\n .sidebar-nav a {\n display: flex;\n align-items: center;\n gap: 10px;\n padding: 8px 12px 8px 32px;\n color: color-mix(in srgb, var(--shell-text, #fff) 95%, transparent);\n font-size: 0.9375rem;\n font-weight: 500;\n text-decoration: none;\n border-left: 2px solid transparent;\n border-radius: 0 8px 8px 0;\n margin-right: 10px;\n transition: color 150ms, background 150ms, border-color 150ms;\n }\n .sidebar-nav a:hover {\n color: color-mix(in srgb, var(--shell-text, #fff) 100%, transparent);\n background: color-mix(in srgb, var(--shell-text, #fff) 6%, transparent);\n }\n .sidebar-nav a.active {\n color: var(--shell-text, #fff);\n background: rgba(96, 165, 250, 0.13);\n border-left-color: #60a5fa;\n font-weight: 600;\n }\n .sidebar-nav a:focus-visible {\n outline: 2px solid rgba(96, 165, 250, 0.5);\n outline-offset: -2px;\n }\n\n /* Icons */\n .nav-icon {\n display: flex;\n align-items: center;\n flex-shrink: 0;\n }\n .nav-icon ::ng-deep svg {\n width: 17px;\n height: 17px;\n stroke: color-mix(in srgb, var(--shell-text, #fff) 90%, transparent);\n transition: stroke 150ms;\n flex-shrink: 0;\n }\n .sidebar-nav a:hover .nav-icon ::ng-deep svg { stroke: color-mix(in srgb, var(--shell-text, #fff) 100%, transparent); }\n .sidebar-nav a.active .nav-icon ::ng-deep svg { stroke: #93c5fd; }\n\n .nav-label { flex: 1; }\n\n .nav-badge {\n display: inline-flex;\n align-items: center;\n justify-content: center;\n min-width: 18px;\n height: 18px;\n padding: 0 5px;\n border-radius: 9px;\n background: #ef4444;\n color: #fff;\n font-size: 0.625rem;\n font-weight: 700;\n margin-left: auto;\n }\n\n /* ── User footer ──────────────────────────────────────────────── */\n .sidebar-footer {\n padding: 10px 14px;\n border-top: 1px solid color-mix(in srgb, var(--shell-text, #fff) 6%, transparent);\n }\n .user-info {\n display: flex;\n align-items: center;\n gap: 10px;\n }\n .user-avatar {\n width: 32px;\n height: 32px;\n border-radius: 50%;\n background: linear-gradient(135deg, rgba(96, 165, 250, 0.22), rgba(96, 165, 250, 0.08));\n border: 1px solid rgba(96, 165, 250, 0.18);\n color: color-mix(in srgb, var(--shell-text, #fff) 88%, transparent);\n font-size: 0.75rem;\n font-weight: 600;\n display: flex;\n align-items: center;\n justify-content: center;\n flex-shrink: 0;\n }\n .user-details {\n display: flex;\n flex-direction: column;\n min-width: 0;\n flex: 1;\n }\n .user-name {\n font-size: 0.8125rem;\n font-weight: 600;\n color: var(--shell-text, #fff);\n white-space: nowrap;\n overflow: hidden;\n text-overflow: ellipsis;\n }\n .user-role {\n font-size: 0.6875rem;\n color: color-mix(in srgb, var(--shell-text, #fff) 80%, transparent);\n text-transform: uppercase;\n letter-spacing: 0.05em;\n font-weight: 500;\n }\n .btn-logout {\n background: none;\n border: none;\n cursor: pointer;\n color: color-mix(in srgb, var(--shell-text, #fff) 85%, transparent);\n padding: 6px;\n border-radius: 8px;\n display: flex;\n align-items: center;\n justify-content: center;\n flex-shrink: 0;\n transition: color 150ms, background 150ms;\n }\n .btn-logout:hover { color: #f87171; background: rgba(248, 113, 113, 0.1); }\n .btn-logout:focus-visible {\n outline: 2px solid rgba(96, 165, 250, 0.5);\n outline-offset: 2px;\n }\n\n\n /* ── Collapsed rail (desktop only) ────────────────────────────── */\n .brand-full { display: flex; align-items: center; min-width: 0; flex: 1; }\n .brand-mark { display: none; }\n .shell.collapsed .brand-mark {\n width: 36px; height: 36px; border-radius: 10px;\n display: flex; align-items: center; justify-content: center;\n font-weight: 700; font-size: 1.125rem;\n color: var(--shell-text, #fff);\n background: color-mix(in srgb, var(--shell-text, #fff) 15%, transparent);\n }\n .rail-toggle {\n display: none;\n align-items: center; justify-content: center;\n width: 32px; height: 32px; margin-left: auto; flex-shrink: 0;\n border: none; background: none; cursor: pointer; border-radius: 8px;\n color: color-mix(in srgb, var(--shell-text, #fff) 85%, transparent);\n transition: background 150ms, color 150ms;\n }\n .rail-toggle:hover {\n background: color-mix(in srgb, var(--shell-text, #fff) 12%, transparent);\n color: var(--shell-text, #fff);\n }\n .rail-toggle:focus-visible { outline: 2px solid rgba(96, 165, 250, 0.5); outline-offset: 2px; }\n .rail-divider { display: none; }\n\n @media (min-width: 1025px) {\n .rail-toggle { display: flex; }\n .sidebar { transition: width 200ms cubic-bezier(0.4, 0, 0.2, 1); }\n .shell.collapsed .sidebar { width: 68px; }\n .shell.collapsed .sidebar-header { padding: 0; flex-direction: column; justify-content: center; gap: 4px; padding: 8px 0; }\n /* Rail: the projected brand is cropped to a centered square mark; default text brand shows its initial */\n .shell.collapsed .brand-full {\n flex: none; width: 44px; height: 44px; overflow: hidden; border-radius: 10px;\n justify-content: center;\n }\n .shell.collapsed .brand-full ::ng-deep img,\n .shell.collapsed .brand-full ::ng-deep svg {\n width: 44px; height: 44px; max-width: none; object-fit: cover; object-position: 50% 42%;\n }\n .shell.collapsed .brand-text { display: none; }\n .shell.collapsed .rail-toggle { margin-left: 0; }\n .shell.collapsed .nav-group-label { display: none; }\n .shell.collapsed .rail-divider {\n display: block; height: 1px; margin: 8px 14px;\n background: color-mix(in srgb, var(--shell-text, #fff) 20%, transparent);\n }\n .shell.collapsed .nav-group:first-child .rail-divider { display: none; }\n .shell.collapsed .sidebar-nav a {\n position: relative; justify-content: center;\n padding: 10px 0; margin: 0 8px 2px; border-left: none; border-radius: 8px;\n }\n .shell.collapsed .sidebar-nav a.active { box-shadow: inset 3px 0 0 #60a5fa; }\n .shell.collapsed .nav-label { display: none; }\n .shell.collapsed .nav-icon ::ng-deep svg { width: 20px; height: 20px; }\n .shell.collapsed .nav-badge {\n position: absolute; top: 2px; right: 4px; min-width: 16px; height: 16px;\n padding: 0 4px; font-size: 0.5625rem; margin: 0;\n }\n .shell.collapsed .sidebar-footer { padding: 10px 8px; }\n .shell.collapsed .user-info { flex-direction: column; gap: 8px; }\n .shell.collapsed .user-details { display: none; }\n }\n\n /* ── Main area ────────────────────────────────────────────────── */\n .main-area {\n flex: 1;\n display: flex;\n flex-direction: column;\n overflow: hidden;\n min-width: 0;\n }\n\n /* ── Header ───────────────────────────────────────────────────── */\n .top-header {\n display: flex;\n align-items: center;\n gap: 8px;\n padding: 0 24px;\n background: #fff;\n box-shadow: 0 1px 3px rgba(0, 0, 0, 0.07), 0 1px 0 rgba(0, 0, 0, 0.04);\n min-height: 64px;\n box-sizing: border-box;\n position: relative;\n z-index: 50;\n }\n\n .hamburger {\n display: none;\n align-items: center;\n justify-content: center;\n width: 36px;\n height: 36px;\n border: none;\n background: none;\n cursor: pointer;\n color: #374151;\n border-radius: 8px;\n flex-shrink: 0;\n transition: background 150ms, color 150ms;\n }\n .hamburger:hover { background: #f3f4f6; }\n .hamburger:focus-visible {\n outline: 2px solid rgba(59, 130, 246, 0.5);\n outline-offset: 2px;\n }\n\n .page-title {\n font-size: 1.25rem;\n font-weight: 700;\n color: #111827;\n margin: 0;\n letter-spacing: -0.02em;\n }\n\n .read-only-badge {\n display: inline-flex;\n align-items: center;\n flex-shrink: 0;\n padding: 2px 8px;\n border-radius: 9999px;\n font-size: 0.6875rem;\n font-weight: 600;\n letter-spacing: 0.02em;\n text-transform: uppercase;\n background: #fef9c3;\n color: #854d0e;\n border: 1px solid #fde68a;\n white-space: nowrap;\n }\n\n .header-end {\n display: flex;\n align-items: center;\n margin-left: auto;\n }\n\n /* ── Content ──────────────────────────────────────────────────── */\n .content {\n flex: 1;\n overflow-y: auto;\n padding: 24px;\n background: #F7F8F6;\n display: flex;\n flex-direction: column;\n box-sizing: border-box;\n min-height: 0;\n }\n .content > * {\n flex: 1;\n min-height: 0;\n display: flex;\n flex-direction: column;\n }\n\n /* ── Tablet / Mobile ≤ 1024px ─────────────────────────────────── */\n @media (max-width: 1024px) {\n .hamburger { display: flex; }\n\n .sidebar {\n position: fixed;\n top: 0; left: 0; bottom: 0;\n z-index: 200;\n transform: translateX(-100%);\n transition: transform 260ms cubic-bezier(0.4, 0, 0.2, 1);\n box-shadow: 4px 0 32px rgba(0, 0, 0, 0.3);\n }\n .shell.sidebar-open .sidebar { transform: translateX(0); }\n .shell.sidebar-open .sidebar-backdrop { display: block; }\n\n .main-area { width: 100%; }\n .top-header { padding: 0 16px; gap: 4px; }\n .content { padding: 16px; }\n }\n\n /* ── Small mobile ≤ 480px ─────────────────────────────────────── */\n @media (max-width: 480px) {\n .page-title { font-size: 1.0625rem; }\n .content { padding: 12px; }\n }\n\n /* ── Reduced motion ──────────────────────────────────────────── */\n @media (prefers-reduced-motion: reduce) {\n .chevron, .sidebar-nav a, .sidebar, .nav-icon ::ng-deep svg { transition: none !important; }\n }\n `],\n})\nexport class AppShellComponent implements OnInit, OnDestroy {\n private readonly sanitizer = inject(DomSanitizer);\n private readonly router = inject(Router);\n private _routerSub?: Subscription;\n\n // ── Inputs ────────────────────────────────────────────────────\n readonly navGroups = input<NavGroup[]>([]);\n readonly pageTitle = input('');\n readonly appName = input('App');\n readonly user = input<ShellUser | null>(null);\n readonly readOnly = input(false);\n /** Sidebar background — any CSS background value (color or gradient). Falls back to the default dark gradient. */\n readonly backgroundColor = input<string>('');\n /** Sidebar text/icon color — any CSS color. Hover/muted shades are derived from it. Falls back to white. */\n readonly textColor = input<string>('');\n /** Show a desktop toggle that shrinks the sidebar to icons only. The choice is remembered per browser. */\n readonly collapsible = input(false);\n /**\n * When set, which nav groups the user opened/closed is saved to localStorage under this key\n * and restored on load (plain per-browser storage). Include a user id in the key for per-user state.\n * Unset = nothing is stored.\n */\n readonly persistKey = input<string>('');\n\n // ── Outputs ───────────────────────────────────────────────────\n readonly logoutClick = output<void>();\n\n // ── Internal state ────────────────────────────────────────────\n protected readonly _sidebarOpen = signal(false);\n protected readonly _collapsed = signal(AppShellComponent._loadCollapsed());\n protected readonly _isRail = computed(() => this.collapsible() && this._collapsed());\n private readonly _collapsedGroups = signal<Record<string, boolean>>({});\n\n // ── Computed ──────────────────────────────────────────────────\n protected readonly _visibleGroups = computed(() =>\n this.navGroups().filter(g => g.visible !== false)\n );\n\n protected readonly _userInitials = computed(() => {\n const u = this.user();\n return `${u?.firstName?.charAt(0) ?? ''}${u?.lastName?.charAt(0) ?? ''}`;\n });\n\n protected readonly _userName = computed(() => {\n const u = this.user();\n return [u?.firstName, u?.lastName].filter(Boolean).join(' ');\n });\n\n // ── Icon cache (avoids repeated DomSanitizer calls) ───────────\n private readonly _iconCache = new Map<string, SafeHtml>();\n\n _safeIcon(svg: string): SafeHtml {\n let safe = this._iconCache.get(svg);\n if (!safe) {\n safe = this.sanitizer.bypassSecurityTrustHtml(svg);\n this._iconCache.set(svg, safe);\n }\n return safe;\n }\n\n _visibleLinks(group: NavGroup): NavLink[] {\n return group.links.filter(l => l.visible !== false);\n }\n\n // ── Group collapse ────────────────────────────────────────────\n _isCollapsed(key: string): boolean {\n return !!this._collapsedGroups()[key];\n }\n\n toggleGroup(key: string): void {\n const collapsed = !this._collapsedGroups()[key];\n this._collapsedGroups.update(g => ({ ...g, [key]: collapsed }));\n this._saveGroup(key, collapsed);\n }\n\n private _readSavedGroups(): Record<string, boolean> {\n const pk = this.persistKey();\n if (!pk) return {};\n try {\n const raw = JSON.parse(localStorage.getItem(appShellGroupsStorageKey(pk)) ?? 'null');\n if (!raw || typeof raw !== 'object' || Array.isArray(raw)) return {};\n return Object.fromEntries(Object.entries(raw).filter(([, v]) => typeof v === 'boolean')) as Record<string, boolean>;\n } catch { return {}; }\n }\n\n /** Only groups the user toggled are stored, so groups added later keep their defaults. */\n private _saveGroup(key: string, collapsed: boolean): void {\n const pk = this.persistKey();\n if (!pk) return;\n try {\n const saved = { ...this._readSavedGroups(), [key]: collapsed };\n localStorage.setItem(appShellGroupsStorageKey(pk), JSON.stringify(saved));\n } catch { /* storage unavailable */ }\n }\n\n toggleCollapsed(): void {\n const next = !this._collapsed();\n this._collapsed.set(next);\n try { localStorage.setItem(AppShellComponent._COLLAPSE_KEY, String(next)); } catch { /* storage unavailable */ }\n }\n\n private static readonly _COLLAPSE_KEY = 'ui-app-shell-collapsed';\n private static _loadCollapsed(): boolean {\n try { return localStorage.getItem(AppShellComponent._COLLAPSE_KEY) === 'true'; } catch { return false; }\n }\n\n toggleSidebar(): void { this._sidebarOpen.update(v => !v); }\n closeSidebar(): void { this._sidebarOpen.set(false); }\n\n // ── Lifecycle ─────────────────────────────────────────────────\n ngOnInit(): void {\n // Seed collapse state from group defaults\n const initial: Record<string, boolean> = {};\n this.navGroups().forEach(g => { if (g.defaultCollapsed) initial[g.key] = true; });\n this._collapsedGroups.set({ ...initial, ...this._readSavedGroups() });\n\n // Expand the group containing the active route; also close mobile sidebar\n this._routerSub = this.router.events.pipe(\n filter((e): e is NavigationEnd => e instanceof NavigationEnd),\n ).subscribe(e => {\n this._sidebarOpen.set(false);\n this._expandActiveGroup(e.urlAfterRedirects);\n });\n\n this._expandActiveGroup(this.router.url);\n }\n\n ngOnDestroy(): void {\n this._routerSub?.unsubscribe();\n this._iconCache.clear();\n }\n\n private _expandActiveGroup(url: string): void {\n const path = url.split('?')[0].replace(/^\\//, '');\n for (const group of this.navGroups()) {\n const hasActive = group.links.some(l => {\n if (l.visible === false) return false;\n const route = l.route.replace(/^\\//, '');\n return l.exact ? route === path : path === route || path.startsWith(route + '/');\n });\n if (hasActive) {\n this._collapsedGroups.update(g => ({ ...g, [group.key]: false }));\n return;\n }\n }\n }\n}\n","import { Component, ChangeDetectionStrategy, input, output } from '@angular/core';\n\nexport type AlertVariant = 'info' | 'success' | 'warning' | 'danger';\n\n/**\n * Inline status message (info / success / warning / danger) with an optional title,\n * optional dismiss button, and projected body content (text, links, a retry ui-button…).\n *\n * <ui-alert variant=\"warning\">No recipients — this campaign will not be sent.</ui-alert>\n * <ui-alert variant=\"danger\" title=\"Couldn't load recipients\">\n * {{ error }} <ui-button variant=\"ghost\" size=\"sm\" (click)=\"retry()\">Retry</ui-button>\n * </ui-alert>\n */\n@Component({\n selector: 'ui-alert',\n standalone: true,\n changeDetection: ChangeDetectionStrategy.OnPush,\n template: `\n <div class=\"alert\" [class]=\"'alert alert--' + variant()\" [attr.role]=\"variant() === 'danger' ? 'alert' : 'status'\">\n <div class=\"alert-content\">\n @if (title()) { <div class=\"alert-title\">{{ title() }}</div> }\n <div class=\"alert-body\"><ng-content /></div>\n </div>\n @if (dismissible()) {\n <button class=\"alert-dismiss\" type=\"button\" aria-label=\"Dismiss\" (click)=\"dismissed.emit()\">\n <svg width=\"14\" height=\"14\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2.5\" aria-hidden=\"true\">\n <line x1=\"18\" y1=\"6\" x2=\"6\" y2=\"18\"/><line x1=\"6\" y1=\"6\" x2=\"18\" y2=\"18\"/>\n </svg>\n </button>\n }\n </div>\n `,\n styles: [`\n :host { display: block; }\n .alert {\n display: flex; align-items: flex-start; gap: 12px;\n padding: 12px 14px; border-radius: 8px; border-left: 4px solid transparent;\n font-size: 13px; line-height: 1.5;\n }\n .alert--info { background: #dbeafe; border-color: #3b82f6; color: #1e3a8a; }\n .alert--success { background: #dcfce7; border-color: #22c55e; color: #14532d; }\n .alert--warning { background: #ffedd5; border-color: #f97316; color: #7c2d12; }\n .alert--danger { background: #fee2e2; border-color: #ef4444; color: #7f1d1d; }\n .alert-content { flex: 1; display: flex; flex-direction: column; gap: 2px; }\n .alert-title { font-weight: 700; }\n .alert-body { opacity: 0.9; display: flex; align-items: center; flex-wrap: wrap; gap: 8px; }\n .alert-dismiss {\n flex-shrink: 0; background: none; border: none; cursor: pointer; padding: 2px;\n border-radius: 4px; color: inherit; opacity: 0.6; display: flex; align-items: center;\n transition: opacity 150ms;\n }\n .alert-dismiss:hover { opacity: 1; }\n `],\n})\nexport class AlertComponent {\n readonly variant = input<AlertVariant>('info');\n readonly title = input('');\n readonly dismissible = input(false);\n readonly dismissed = output<void>();\n}\n","import { Component, ChangeDetectionStrategy } from '@angular/core';\n\n/**\n * Small muted helper text shown under a form or panel section.\n *\n * <ui-hint>This does not affect the scheduled send date.</ui-hint>\n */\n@Component({\n selector: 'ui-hint',\n standalone: true,\n changeDetection: ChangeDetectionStrategy.OnPush,\n template: `<p class=\"hint\"><ng-content /></p>`,\n styles: [`\n :host { display: block; }\n .hint { font-size: 0.75rem; line-height: 1.4; color: #64748b; margin: 0; } /* 4.76:1 on white (WCAG AA) */\n `],\n})\nexport class HintComponent {}\n","import { Component, ChangeDetectionStrategy, computed, effect, input, signal, untracked } from '@angular/core';\n\nexport type ImagePreviewShape = 'circle' | 'rounded';\nexport type ImagePreviewSize = 'sm' | 'md' | 'lg';\nexport type ImagePreviewPlaceholder = 'person' | 'image';\n\n/**\n * Small bordered image preview (current photo / cover / background) with a neutral placeholder icon shown when there\n * is no `src` or the image fails to load. Same look as the data table's image cell.\n *\n * <ui-image-preview [src]=\"photoUrl()\" alt=\"Obituary photo\" size=\"lg\" placeholder=\"person\" />\n *\n * `alt` defaults to '' (decorative). The placeholder is always `aria-hidden`.\n * Sizes (w x h): circle sm 28, md 40, lg 80 (square); rounded sm 36x24, md 48x32, lg 112x64.\n */\n@Component({\n selector: 'ui-image-preview',\n standalone: true,\n changeDetection: ChangeDetectionStrategy.OnPush,\n template: `\n @if (_url() && !_failed()) {\n <img class=\"ip-img\" [class]=\"_cls()\" [src]=\"_url()\" [attr.alt]=\"alt()\" [attr.loading]=\"loading()\" decoding=\"async\" (error)=\"_onError()\" />\n } @else {\n <span class=\"ip-img ip-ph\" [class]=\"_cls()\" aria-hidden=\"true\">\n @if (placeholder() === 'person') {\n <svg viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"1.8\" stroke-linecap=\"round\" stroke-linejoin=\"round\"><circle cx=\"12\" cy=\"8\" r=\"4\"/><path d=\"M4 21c0-4 3.6-7 8-7s8 3 8 7\"/></svg>\n } @else {\n <svg viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"1.8\" stroke-linecap=\"round\" stroke-linejoin=\"round\"><rect x=\"3\" y=\"4\" width=\"18\" height=\"16\" rx=\"2\"/><circle cx=\"9\" cy=\"10\" r=\"1.8\"/><path d=\"M21 16l-5-5-8 9\"/></svg>\n }\n </span>\n }\n `,\n styles: [`\n :host { display: inline-block; line-height: 0; flex-shrink: 0; }\n .ip-img { display: block; object-fit: cover; border: 1px solid #e2e8f0; background: #f1f5f9; box-sizing: border-box; flex-shrink: 0; }\n .ip-ph { display: inline-flex; align-items: center; justify-content: center; color: #94a3b8; }\n .ip-ph svg { width: 55%; height: 55%; }\n .ip-circle-sm { width: 28px; height: 28px; border-radius: 50%; }\n .ip-circle-md { width: 40px; height: 40px; border-radius: 50%; }\n .ip-circle-lg { width: 80px; height: 80px; border-radius: 50%; }\n .ip-rounded-sm { width: 36px; height: 24px; border-radius: 5px; }\n .ip-rounded-md { width: 48px; height: 32px; border-radius: 6px; }\n .ip-rounded-lg { width: 112px; height: 64px; border-radius: 8px; }\n `],\n})\nexport class ImagePreviewComponent {\n /** Image URL (or data URL). Falsy shows the placeholder. */\n readonly src = input<string | null | undefined>(null);\n /** Alt text; '' (default) marks the image decorative. */\n readonly alt = input('');\n readonly shape = input<ImagePreviewShape>('rounded');\n readonly size = input<ImagePreviewSize>('md');\n readonly placeholder = input<ImagePreviewPlaceholder>('image');\n readonly loading = input<'lazy' | 'eager'>('lazy');\n\n /**\n * Optional retry trigger: whenever its value changes, a failed image is attempted again (signal inputs cannot see\n * the same `src` being set twice, so a host bumps this, e.g. a counter, to retry the same URL).\n */\n readonly retryKey = input<unknown>(null);\n\n protected readonly _failed = signal(false);\n /** Trimmed src; blank / whitespace-only is treated as no image. */\n protected readonly _url = computed(() => this.src()?.trim() || null);\n protected readonly _cls = computed(() => `ip-${this.shape()}-${this.size()}`);\n\n constructor() {\n // A new src (or a changed retryKey) gets a fresh chance to load.\n effect(() => { this.src(); this.retryKey(); untracked(() => this._failed.set(false)); });\n }\n\n /** Clears the failed state so the current `src` is attempted again (call via viewChild). */\n retry(): void { this._failed.set(false); }\n\n protected _onError(): void { this._failed.set(true); }\n}\n","import { Component, ChangeDetectionStrategy, ElementRef, computed, inject, input } from '@angular/core';\nimport { DomSanitizer, SafeHtml } from '@angular/platform-browser';\n\nexport type KpiCardColor = 'green' | 'blue' | 'amber' | 'orange' | 'red' | 'slate';\nexport type KpiCardValueColor = 'default' | 'green' | 'blue';\n\n/**\n * Compact KPI summary card: optional icon, uppercase label, large value, optional subtitle.\n * Set `clickable` to show a hover state + chevron (bind `(click)` on the host). A clickable card is a keyboard button:\n * role=\"button\", focusable (tabindex 0, visible focus ring), and Enter / Space fire the host's (click). Give it an\n * accessible name with [attr.aria-label] when the label alone isn't enough (e.g. \"Net Commission: $12.5K — show details\").\n *\n * <ui-kpi-card label=\"Deals Closed\" [value]=\"'' + deals\" subtitle=\"Year to Date\" icon=\"check-circle\" color=\"blue\" />\n * <ui-kpi-card label=\"Net Commission\" [value]=\"net\" icon=\"dollar\" color=\"green\" [highlight]=\"true\"\n * [clickable]=\"true\" (click)=\"openDetails()\" />\n */\n@Component({\n selector: 'ui-kpi-card',\n standalone: true,\n changeDetection: ChangeDetectionStrategy.OnPush,\n host: {\n '[attr.role]': 'clickable() ? \"button\" : null',\n '[attr.tabindex]': 'clickable() ? 0 : null',\n '(keydown.enter)': 'onActivateKey($event)',\n '(keydown.space)': 'onActivateKey($event)',\n },\n template: `\n <div class=\"kpi-card\" [class.highlight]=\"highlight()\" [class.clickable]=\"clickable()\">\n @if (iconSvg()) {\n <div [class]=\"'kpi-icon ' + color() + '-icon'\" aria-hidden=\"true\" [innerHTML]=\"iconSvg()\"></div>\n }\n <span class=\"kpi-label\">{{ label() }}</span>\n <span class=\"kpi-value\"\n [class.green-value]=\"highlight() || valueColor() === 'green'\"\n [class.blue-value]=\"valueColor() === 'blue'\">{{ value() }}</span>\n @if (subtitle()) {\n <span class=\"kpi-subtitle\">{{ subtitle() }}</span>\n }\n @if (clickable()) {\n <svg class=\"kpi-arrow\" width=\"14\" height=\"14\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2.5\" stroke-linecap=\"round\" stroke-linejoin=\"round\" aria-hidden=\"true\"><polyline points=\"9 18 15 12 9 6\"/></svg>\n }\n </div>\n `,\n styles: [`\n :host { display: block; height: 100%; }\n .kpi-card {\n position: relative;\n background: #fff; border: 1px solid #e2e8f0; border-radius: 10px;\n padding: 16px; display: flex; flex-direction: column; gap: 4px; min-width: 0;\n height: 100%; box-sizing: border-box;\n transition: border-color 150ms ease, box-shadow 150ms ease;\n }\n :host(:focus) { outline: none; }\n :host(:focus-visible) .kpi-card { outline: 2px solid #3b82f6; outline-offset: 2px; }\n .kpi-card.clickable { cursor: pointer; }\n .kpi-card.clickable:hover { border-color: #93c5fd; box-shadow: 0 2px 8px rgba(59,130,246,0.08); }\n .kpi-card.clickable:hover .kpi-arrow { color: #3b82f6; transform: translateX(2px); }\n .kpi-arrow {\n position: absolute; bottom: 14px; right: 14px;\n color: #94a3b8; transition: color 150ms ease, transform 150ms ease;\n }\n .kpi-icon {\n flex-shrink: 0; width: 36px; height: 36px; border-radius: 8px;\n display: flex; align-items: center; justify-content: center;\n margin-bottom: 8px;\n }\n .green-icon { background: #dcfce7; color: #16a34a; }\n .blue-icon { background: #dbeafe; color: #2563eb; }\n .amber-icon { background: #fef3c7; color: #d97706; }\n .orange-icon { background: #ffedd5; color: #c2410c; }\n .red-icon { background: #fee2e2; color: #dc2626; }\n .slate-icon { background: #f1f5f9; color: #475569; }\n .kpi-label {\n font-size: 0.75rem; font-weight: 600; color: #64748b;\n text-transform: uppercase; letter-spacing: 0.04em;\n }\n .kpi-value { font-size: 1.5rem; font-weight: 700; color: #111827; line-height: 1.1; }\n .kpi-value.green-value { color: #16a34a; }\n .kpi-value.blue-value { color: #3b82f6; }\n .kpi-subtitle { font-size: 0.75rem; color: #64748b; margin-top: 2px; } /* slate-500: AA contrast on white */\n `],\n})\nexport class KpiCardComponent {\n private readonly sanitizer = inject(DomSanitizer);\n private readonly host = inject<ElementRef<HTMLElement>>(ElementRef);\n\n readonly label = input('');\n readonly value = input('');\n readonly subtitle = input('');\n /** Built-in icon name (see KPI_ICONS keys), e.g. 'dollar', 'users', 'check-circle'. */\n readonly icon = input('');\n readonly color = input<KpiCardColor>('slate');\n readonly highlight = input(false);\n readonly clickable = input(false);\n readonly valueColor = input<KpiCardValueColor>('default');\n\n /** Enter / Space on a clickable card act like a click (Space would otherwise scroll the page). */\n protected onActivateKey(event: Event) {\n if (!this.clickable()) return;\n event.preventDefault();\n this.host.nativeElement.click();\n }\n\n protected readonly iconSvg = computed((): SafeHtml | null => {\n const svg = KPI_ICONS[this.icon()];\n return svg ? this.sanitizer.bypassSecurityTrustHtml(svg) : null;\n });\n}\n\n// Static, library-owned SVG markup only — never user input.\nconst KPI_ICONS: Record<string, string> = {\n 'shield-check': '<svg width=\"18\" height=\"18\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"1.8\" stroke-linecap=\"round\" stroke-linejoin=\"round\"><path d=\"M12 22s8-4 8-10V5l-8-3-8 3v7c0 6 8 10 8 10z\"/><path d=\"m9 12 2 2 4-4\"/></svg>',\n 'clock': '<svg width=\"18\" height=\"18\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"1.8\" stroke-linecap=\"round\" stroke-linejoin=\"round\"><circle cx=\"12\" cy=\"12\" r=\"10\"/><polyline points=\"12 6 12 12 16 14\"/></svg>',\n 'alert-circle': '<svg width=\"18\" height=\"18\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"1.8\" stroke-linecap=\"round\" stroke-linejoin=\"round\"><circle cx=\"12\" cy=\"12\" r=\"10\"/><line x1=\"12\" y1=\"8\" x2=\"12\" y2=\"12\"/><line x1=\"12\" y1=\"16\" x2=\"12.01\" y2=\"16\"/></svg>',\n 'alert-triangle': '<svg width=\"18\" height=\"18\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"1.8\" stroke-linecap=\"round\" stroke-linejoin=\"round\"><path d=\"M10.29 3.86 1.82 18a2 2 0 0 0 1.71 3h16.94a2 2 0 0 0 1.71-3L13.71 3.86a2 2 0 0 0-3.42 0z\"/><line x1=\"12\" y1=\"9\" x2=\"12\" y2=\"13\"/><line x1=\"12\" y1=\"17\" x2=\"12.01\" y2=\"17\"/></svg>',\n 'check-circle': '<svg width=\"18\" height=\"18\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"1.8\" stroke-linecap=\"round\" stroke-linejoin=\"round\"><path d=\"M22 11.08V12a10 10 0 1 1-5.93-9.14\"/><polyline points=\"22 4 12 14.01 9 11.01\"/></svg>',\n 'users': '<svg width=\"18\" height=\"18\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"1.8\" stroke-linecap=\"round\" stroke-linejoin=\"round\"><path d=\"M17 21v-2a4 4 0 0 0-4-4H5a4 4 0 0 0-4 4v2\"/><circle cx=\"9\" cy=\"7\" r=\"4\"/><path d=\"M23 21v-2a4 4 0 0 0-3-3.87\"/><path d=\"M16 3.13a4 4 0 0 1 0 7.75\"/></svg>',\n 'user': '<svg width=\"18\" height=\"18\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"1.8\" stroke-linecap=\"round\" stroke-linejoin=\"round\"><path d=\"M20 21v-2a4 4 0 0 0-4-4H8a4 4 0 0 0-4 4v2\"/><circle cx=\"12\" cy=\"7\" r=\"4\"/></svg>',\n 'send': '<svg width=\"18\" height=\"18\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"1.8\" stroke-linecap=\"round\" stroke-linejoin=\"round\"><line x1=\"22\" y1=\"2\" x2=\"11\" y2=\"13\"/><polygon points=\"22 2 15 22 11 13 2 9 22 2\"/></svg>',\n 'inbox': '<svg width=\"18\" height=\"18\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"1.8\" stroke-linecap=\"round\" stroke-linejoin=\"round\"><polyline points=\"22 12 16 12 14 15 10 15 8 12 2 12\"/><path d=\"M5.45 5.11 2 12v6a2 2 0 0 0 2 2h16a2 2 0 0 0 2-2v-6l-3.45-6.89A2 2 0 0 0 16.76 4H7.24a2 2 0 0 0-1.79 1.11z\"/></svg>',\n 'mail': '<svg width=\"18\" height=\"18\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"1.8\" stroke-linecap=\"round\" stroke-linejoin=\"round\"><rect x=\"2\" y=\"4\" width=\"20\" height=\"16\" rx=\"2\"/><path d=\"m22 7-8.97 5.7a1.94 1.94 0 0 1-2.06 0L2 7\"/></svg>',\n 'message': '<svg width=\"18\" height=\"18\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"1.8\" stroke-linecap=\"round\" stroke-linejoin=\"round\"><path d=\"M21 15a2 2 0 0 1-2 2H7l-4 4V5a2 2 0 0 1 2-2h14a2 2 0 0 1 2 2z\"/></svg>',\n 'play': '<svg width=\"18\" height=\"18\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"1.8\" stroke-linecap=\"round\" stroke-linejoin=\"round\"><polygon points=\"5 3 19 12 5 21 5 3\"/></svg>',\n 'eye': '<svg width=\"18\" height=\"18\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"1.8\" stroke-linecap=\"round\" stroke-linejoin=\"round\"><path d=\"M1 12s4-8 11-8 11 8 11 8-4 8-11 8-11-8-11-8z\"/><circle cx=\"12\" cy=\"12\" r=\"3\"/></svg>',\n 'percent': '<svg width=\"18\" height=\"18\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"1.8\" stroke-linecap=\"round\" stroke-linejoin=\"round\"><line x1=\"19\" y1=\"5\" x2=\"5\" y2=\"19\"/><circle cx=\"6.5\" cy=\"6.5\" r=\"2.5\"/><circle cx=\"17.5\" cy=\"17.5\" r=\"2.5\"/></svg>',\n 'dollar': '<svg width=\"18\" height=\"18\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"1.8\" stroke-linecap=\"round\" stroke-linejoin=\"round\"><line x1=\"12\" y1=\"1\" x2=\"12\" y2=\"23\"/><path d=\"M17 5H9.5a3.5 3.5 0 0 0 0 7h5a3.5 3.5 0 0 1 0 7H6\"/></svg>',\n 'phone': '<svg width=\"18\" height=\"18\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"1.8\" stroke-linecap=\"round\" stroke-linejoin=\"round\"><path d=\"M22 16.92v3a2 2 0 0 1-2.18 2 19.79 19.79 0 0 1-8.63-3.07 19.5 19.5 0 0 1-6-6 19.79 19.79 0 0 1-3.07-8.67A2 2 0 0 1 4.11 2h3a2 2 0 0 1 2 1.72 12.84 12.84 0 0 0 .7 2.81 2 2 0 0 1-.45 2.11L8.09 9.91a16 16 0 0 0 6 6l1.27-1.27a2 2 0 0 1 2.11-.45 12.84 12.84 0 0 0 2.81.7A2 2 0 0 1 22 16.92z\"/></svg>',\n 'file': '<svg width=\"18\" height=\"18\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"1.8\" stroke-linecap=\"round\" stroke-linejoin=\"round\"><path d=\"M14 2H6a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V8z\"/><polyline points=\"14 2 14 8 20 8\"/><line x1=\"16\" y1=\"13\" x2=\"8\" y2=\"13\"/><line x1=\"16\" y1=\"17\" x2=\"8\" y2=\"17\"/></svg>',\n 'target': '<svg width=\"18\" height=\"18\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"1.8\" stroke-linecap=\"round\" stroke-linejoin=\"round\"><circle cx=\"12\" cy=\"12\" r=\"10\"/><circle cx=\"12\" cy=\"12\" r=\"6\"/><circle cx=\"12\" cy=\"12\" r=\"2\"/></svg>',\n 'trending-up': '<svg width=\"18\" height=\"18\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"1.8\" stroke-linecap=\"round\" stroke-linejoin=\"round\"><polyline points=\"23 6 13.5 15.5 8.5 10.5 1 18\"/><polyline points=\"17 6 23 6 23 12\"/></svg>',\n 'x-circle': '<svg width=\"18\" height=\"18\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"1.8\" stroke-linecap=\"round\" stroke-linejoin=\"round\"><circle cx=\"12\" cy=\"12\" r=\"10\"/><line x1=\"15\" y1=\"9\" x2=\"9\" y2=\"15\"/><line x1=\"9\" y1=\"9\" x2=\"15\" y2=\"15\"/></svg>',\n 'video': '<svg width=\"18\" height=\"18\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"1.8\" stroke-linecap=\"round\" stroke-linejoin=\"round\"><rect x=\"2\" y=\"5\" width=\"14\" height=\"14\" rx=\"2\"/><path d=\"m16 10 6-3.5v11L16 14\"/></svg>',\n 'calendar': '<svg width=\"18\" height=\"18\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"1.8\" stroke-linecap=\"round\" stroke-linejoin=\"round\"><rect x=\"3\" y=\"4\" width=\"18\" height=\"18\" rx=\"2\"/><line x1=\"16\" y1=\"2\" x2=\"16\" y2=\"6\"/><line x1=\"8\" y1=\"2\" x2=\"8\" y2=\"6\"/><line x1=\"3\" y1=\"10\" x2=\"21\" y2=\"10\"/></svg>',\n 'activity': '<svg width=\"18\" height=\"18\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"1.8\" stroke-linecap=\"round\" stroke-linejoin=\"round\"><polyline points=\"22 12 18 12 15 21 9 3 6 12 2 12\"/></svg>',\n};\n","import { Component, ChangeDetectionStrategy, computed, input, linkedSignal, output, signal } from '@angular/core';\nimport { FormsModule } from '@angular/forms';\nimport { PillTabsComponent, PillTab } from '../pill-tabs/pill-tabs.component';\nimport { DateInputComponent } from '../date-input/date-input.component';\nimport { ButtonComponent } from '../button/button.component';\n\nexport type DatePreset = 'today' | '7days' | '30days' | 'month' | 'quarter' | 'ytd' | 'all' | 'custom';\n\nexport interface DateRange {\n preset: DatePreset;\n /** ISO date, 'YYYY-MM-DD'. */\n startDate: string;\n /** ISO date, 'YYYY-MM-DD'. */\n endDate: string;\n label: string;\n}\n\nconst PRESET_LABELS: Record<DatePreset, string> = {\n today: 'Today', '7days': 'Last 7 Days', '30days': 'Last 30 Days', month: 'This Month',\n quarter: 'This Quarter', ytd: 'Year to Date', all: 'All Time', custom: 'Custom Range',\n};\n\n/**\n * Date range picker: preset pills (Today … Year to Date, optional All Time, Custom Range) plus\n * From / To date inputs with an Apply button for the custom range. Emits `rangeChange` with the\n * resolved ISO dates whenever a preset is picked or a custom range is applied.\n *\n * <ui-date-range-picker [activePreset]=\"preset()\" [includeAllTime]=\"true\" (rangeChange)=\"onRange($event)\" />\n * <!-- custom range only (host renders its own presets) -->\n * <ui-date-range-picker activePreset=\"custom\" [showPresets]=\"false\" (rangeChange)=\"onRange($event)\" />\n */\n@Component({\n selector: 'ui-date-range-picker',\n standalone: true,\n changeDetection: ChangeDetectionStrategy.OnPush,\n imports: [FormsModule, PillTabsComponent, DateInputComponent, ButtonComponent],\n template: `\n <div class=\"date-range-picker\">\n @if (showPresets()) {\n <ui-pill-tabs [tabs]=\"presetTabs()\" [activeKey]=\"selectedPreset()\" (tabChange)=\"selectPreset($any($event))\" />\n }\n @if (selectedPreset() === 'custom' || !showPresets()) {\n <div class=\"custom-range\">\n <ui-date-input label=\"From\" [max]=\"customEnd()\" [ngModel]=\"customStart()\" (ngModelChange)=\"customStart.set($event)\" />\n <span class=\"date-sep\" aria-hidden=\"true\">–</span>\n <ui-date-input label=\"To\" [min]=\"customStart()\" [ngModel]=\"customEnd()\" (ngModelChange)=\"customEnd.set($event)\" />\n <ui-button variant=\"primary\" size=\"sm\" [disabled]=\"!customStart() || !customEnd()\" (click)=\"applyCustom()\">Apply</ui-button>\n </div>\n }\n </div>\n `,\n styles: [`\n :host { display: block; }\n .date-range-picker { display: flex; flex-direction: column; gap: 10px; }\n .custom-range { display: flex; align-items: flex-end; gap: 8px; flex-wrap: wrap; }\n .date-sep { color: #94a3b8; font-weight: 500; padding-bottom: 10px; }\n `],\n})\nexport class DateRangePickerComponent {\n readonly activePreset = input<DatePreset>('ytd');\n readonly showPresets = input(true);\n readonly includeAllTime = input(false);\n readonly rangeChange = output<DateRange>();\n\n /** Follows `activePreset` from the host, and the user's own picks in between. */\n protected readonly selectedPreset = linkedSignal(() => this.activePreset());\n protected readonly customStart = signal('');\n protected readonly customEnd = signal('');\n\n protected readonly presetTabs = computed((): PillTab[] => {\n const keys: DatePreset[] = ['today', '7days', '30days', 'month', 'quarter', 'ytd'];\n if (this.includeAllTime()) keys.push('all');\n keys.push('custom');\n return keys.map(key => ({ key, label: PRESET_LABELS[key] }));\n });\n\n selectPreset(key: DatePreset) {\n this.selectedPreset.set(key);\n if (key !== 'custom') this.rangeChange.emit(resolvePreset(key, new Date()));\n }\n\n applyCustom() {\n if (!this.customStart() || !this.customEnd()) return;\n this.rangeChange.emit({\n preset: 'custom', startDate: this.customStart(), endDate: this.customEnd(), label: PRESET_LABELS.custom,\n });\n }\n}\n\n/** Resolves a preset to concrete ISO start/end dates relative to `today`. */\nexport function resolvePreset(key: DatePreset, today: Date): DateRange {\n // Local calendar date — toISOString() is UTC and shifts the day for non-UTC timezones.\n const fmt = (d: Date) =>\n `${d.getFullYear()}-${String(d.getMonth() + 1).padStart(2, '0')}-${String(d.getDate()).padStart(2, '0')}`;\n const end = fmt(today);\n const range = (start: Date | string, preset: DatePreset = key): DateRange => ({\n preset, startDate: typeof start === 'string' ? start : fmt(start), endDate: end, label: PRESET_LABELS[preset],\n });\n switch (key) {\n case 'today': return range(end);\n case '7days': { const d = new Date(today); d.setDate(d.getDate() - 6); return range(d); }\n case '30days': { const d = new Date(today); d.setDate(d.getDate() - 29); return range(d); }\n case 'month': return range(new Date(today.getFullYear(), today.getMonth(), 1));\n case 'quarter': return range(new Date(today.getFullYear(), Math.floor(today.getMonth() / 3) * 3, 1));\n case 'ytd': return range(new Date(today.getFullYear(), 0, 1));\n case 'all': return { preset: 'all', startDate: '2000-01-01', endDate: '2099-12-31', label: PRESET_LABELS.all };\n default: return range(new Date(today.getFullYear(), 0, 1), 'ytd');\n }\n}\n","/**\n * Allow-list HTML sanitiser used by `ui-rich-text-editor`.\n *\n * The allow-list matches the markup the editor itself produces (and what a server-side allow-list\n * sanitiser for editor output typically keeps): bold/italic/underline/strike, paragraphs and line\n * breaks, headings, lists, block quotes (indent), links, and the inline styles used for alignment,\n * size, font, colour and highlight. Everything else is removed: scripts and styles with their\n * contents, event-handler attributes, `javascript:` and other unsafe URL schemes, and any style\n * property or value outside the lists below.\n *\n * Runs on an inert document (DOMParser), so nothing in the input is executed or fetched.\n */\n\nexport type RichTextSanitizeMode = 'content' | 'paste';\n\nexport interface RichTextSanitizeOptions {\n /** `content` keeps allow-listed inline styles; `paste` flattens them to plain semantic tags. */\n mode?: RichTextSanitizeMode;\n /** Tag names (upper-case) that must not appear in the output — they are replaced per `replace`/unwrapped. */\n blockedTags?: ReadonlySet<string>;\n /** Replacement tag (upper-case) for blocked tags; a blocked tag without an entry is unwrapped. */\n replace?: Readonly<Record<string, string>>;\n}\n\nconst ALLOWED_TAGS = new Set([\n 'P', 'DIV', 'SPAN', 'BR',\n 'B', 'STRONG', 'I', 'EM', 'U', 'S', 'STRIKE', 'DEL', 'SUB', 'SUP',\n 'UL', 'OL', 'LI', 'BLOCKQUOTE',\n 'H1', 'H2', 'H3', 'H4',\n 'A', 'FONT',\n]);\n\n/** Dropped together with their contents. */\nconst DROP_WITH_CONTENT = new Set([\n 'SCRIPT', 'STYLE', 'NOSCRIPT', 'TEXTAREA', 'OPTION', 'SELECT', 'IFRAME', 'OBJECT', 'EMBED',\n 'TEMPLATE', 'HEAD', 'TITLE', 'META', 'LINK', 'SVG', 'MATH', 'CANVAS', 'AUDIO', 'VIDEO',\n 'INPUT', 'BUTTON', 'FORM', 'IMG', 'APPLET', 'FRAME', 'FRAMESET',\n]);\n\n/** Unknown block-level tags become a `<div>` so their line breaks survive. */\nconst BLOCK_TO_DIV = new Set([\n 'SECTION', 'ARTICLE', 'HEADER', 'FOOTER', 'MAIN', 'ASIDE', 'NAV', 'PRE', 'H5', 'H6',\n 'CAPTION', 'FIGURE', 'DL', 'DT', 'DD', 'ADDRESS', 'FIELDSET', 'CENTER',\n]);\n\nconst COLOR = /^(#[0-9a-f]{3,8}|rgba?\\(\\s*\\d{1,3}\\s*,\\s*\\d{1,3}\\s*,\\s*\\d{1,3}\\s*(,\\s*(0|1|0?\\.\\d+)\\s*)?\\)|[a-z]{3,20})$/i;\nconst LENGTH = /^-?\\d+(\\.\\d+)?(px|pt|em|rem|%)?$/;\nconst MARGIN = /^(-?\\d+(\\.\\d+)?(px|pt|em|rem|%)?\\s*){1,4}$/;\n\nconst STYLE_RULES: Record<string, RegExp[]> = {\n 'text-align': [/^(left|right|center|justify)$/],\n 'font-size': [LENGTH, /^(x-small|small|medium|large|x-large|xx-large|xxx-large)$/],\n 'font-family': [/^[\\w\\s,'\"-]+$/],\n 'font-weight': [/^(normal|bold|[1-9]00)$/],\n 'font-style': [/^(normal|italic)$/],\n 'text-decoration': [/^(none|underline|line-through)( (none|underline|line-through))*$/],\n color: [COLOR],\n 'background-color': [COLOR],\n margin: [MARGIN],\n 'margin-left': [LENGTH],\n 'padding-left': [LENGTH],\n};\n\nconst ALLOWED_SCHEMES = /^(https?|mailto|tel):/i;\n\nfunction safeHref(raw: string): string | null {\n const cleaned = raw.replace(/[\\u0000- \\u007f-\\u009f]/g, '');\n if (!cleaned) return null;\n if (ALLOWED_SCHEMES.test(cleaned)) return raw.trim();\n if (/^[a-z][a-z0-9+.-]*:/i.test(cleaned) || /^[\\/\\\\]{2}/.test(cleaned)) return null;\n return raw.trim();\n}\n\n/** Parses an inline style attribute into `[property, value]` pairs (lower-cased property). */\nfunction parseStyle(style: string): [string, string][] {\n return style\n .split(';')\n .map(decl => {\n const i = decl.indexOf(':');\n return i < 0 ? null : ([decl.slice(0, i).trim().toLowerCase(), decl.slice(i + 1).trim()] as [string, string]);\n })\n .filter((d): d is [string, string] => !!d && !!d[0] && !!d[1]);\n}\n\nfunction filterStyle(style: string): string {\n return parseStyle(style)\n .filter(([prop, value]) => (STYLE_RULES[prop] ?? []).some(rule => rule.test(value)))\n .map(([prop, value]) => `${prop}: ${value}`)\n .join('; ');\n}\n\ninterface Ctx {\n doc: Document;\n mode: RichTextSanitizeMode;\n blocked: ReadonlySet<string>;\n replace: Readonly<Record<string, string>>;\n}\n\n/** Output tag for an input element, or `null` to unwrap it (keep only its children). */\nfunction resolveTag(el: Element, ctx: Ctx): string | null {\n let tag = el.tagName.toUpperCase();\n if (BLOCK_TO_DIV.has(tag)) tag = 'DIV';\n if (!ALLOWED_TAGS.has(tag)) return null;\n if (ctx.mode === 'paste' && (tag === 'SPAN' || tag === 'FONT')) return null;\n if (ctx.blocked.has(tag)) return ctx.replace[tag] ?? null;\n return tag;\n}\n\n/** Emphasis tags implied by inline styles — only used when pasting, where styles are flattened. */\nfunction impliedTags(el: Element, ctx: Ctx): string[] {\n const out: string[] = [];\n const decls = new Map(parseStyle(el.getAttribute('style') ?? ''));\n const weight = decls.get('font-weight');\n if (weight && (weight === 'bold' || (/^\\d+$/.test(weight) && Number(weight) >= 600))) out.push('B');\n if (decls.get('font-style') === 'italic') out.push('I');\n const deco = decls.get('text-decoration') ?? '';\n if (/\\bunderline\\b/.test(deco)) out.push('U');\n if (/\\bline-through\\b/.test(deco)) out.push('S');\n return out.filter(t => !ctx.blocked.has(t));\n}\n\nfunction copyAttributes(src: Element, dest: Element, tag: string, ctx: Ctx): void {\n if (ctx.mode === 'paste') {\n if (tag === 'A') setLinkAttributes(src, dest);\n return;\n }\n const style = filterStyle(src.getAttribute('style') ?? '');\n if (style) dest.setAttribute('style', style);\n const align = src.getAttribute('align')?.toLowerCase();\n if (align && /^(left|right|center|justify)$/.test(align)) dest.setAttribute('align', align);\n if (tag === 'A') setLinkAttributes(src, dest);\n if (tag === 'FONT') {\n const face = src.getAttribute('face');\n if (face && /^[\\w\\s,'\"-]+$/.test(face)) dest.setAttribute('face', face);\n const size = src.getAttribute('size');\n if (size && /^[+-]?\\d$/.test(size)) dest.setAttribute('size', size);\n const color = src.getAttribute('color');\n if (color && COLOR.test(color)) dest.setAttribute('color', color);\n }\n}\n\nfunction setLinkAttributes(src: Element, dest: Element): void {\n const href = safeHref(src.getAttribute('href') ?? '');\n if (href) {\n dest.setAttribute('href', href);\n dest.setAttribute('target', '_blank');\n dest.setAttribute('rel', 'noopener noreferrer');\n }\n}\n\nfunction walkChildren(src: Node, dest: Node, ctx: Ctx): void {\n for (const child of Array.from(src.childNodes)) {\n if (child.nodeType === Node.TEXT_NODE) {\n dest.appendChild(ctx.doc.createTextNode(child.nodeValue ?? ''));\n } else if (child.nodeType === Node.ELEMENT_NODE) {\n walkElement(child as Element, dest, ctx);\n }\n }\n}\n\n/**\n * Table handling: every `<tr>` becomes its own `<div>` and the cells of a row are separated by one normal space\n * (tabs would collapse in contenteditable), so a pasted sheet reads as lines. Cells are never emitted as elements and\n * `<table>`/`<thead>`/`<tbody>`/`<tfoot>` are unwrapped; `<caption>` becomes a block of its own. Empty or\n * whitespace-only cells add no separator (a separator only sits between cells that have content), so the output has no\n * leading, trailing or doubled spaces and sanitising it again changes nothing. A cell's own edge whitespace is trimmed.\n * Inline emphasis on a cell (paste mode: bold/italic styles) is kept; `<th>` is not bolded on its own.\n */\nfunction walkRow(el: Element, dest: Node, ctx: Ctx): void {\n const rowTag = ctx.blocked.has('DIV') ? (ctx.replace['DIV'] ?? null) : 'DIV';\n const row: Node = rowTag ? ctx.doc.createElement(rowTag) : ctx.doc.createDocumentFragment();\n let first = true;\n for (const cell of Array.from(el.children)) {\n const t = cell.tagName.toUpperCase();\n if (t !== 'TD' && t !== 'TH') { walkElement(cell, row, ctx); continue; }\n const holder = ctx.doc.createElement('div');\n let target: Node = holder;\n if (ctx.mode === 'paste') {\n for (const wrap of impliedTags(cell, ctx)) {\n const w = ctx.doc.createElement(wrap);\n target.appendChild(w);\n target = w;\n }\n }\n walkChildren(cell, target, ctx);\n if (holder.firstChild?.nodeType === Node.TEXT_NODE) holder.firstChild.nodeValue = (holder.firstChild.nodeValue ?? '').trimStart();\n if (holder.lastChild?.nodeType === Node.TEXT_NODE) holder.lastChild.nodeValue = (holder.lastChild.nodeValue ?? '').trimEnd();\n if (!(holder.textContent ?? '').trim()) continue;\n if (!first) row.appendChild(ctx.doc.createTextNode(' '));\n first = false;\n while (holder.firstChild) row.appendChild(holder.firstChild);\n }\n if (!row.firstChild) return;\n // With no block element available for rows, a <br> still keeps the rows on separate lines.\n if (!rowTag) row.appendChild(ctx.doc.createElement('br'));\n dest.appendChild(row);\n}\n\nfunction walkElement(el: Element, dest: Node, ctx: Ctx): void {\n const original = el.tagName.toUpperCase();\n if (DROP_WITH_CONTENT.has(original)) return;\n if (original === 'TR') { walkRow(el, dest, ctx); return; }\n\n let tag = resolveTag(el, ctx);\n // A list item whose own list type is blocked has no list left to live in.\n if (tag === 'LI' && el.parentElement) {\n const parent = el.parentElement.tagName.toUpperCase();\n if ((parent === 'UL' || parent === 'OL') && ctx.blocked.has(parent)) tag = 'DIV';\n }\n\n let target: Node = dest;\n if (ctx.mode === 'paste') {\n for (const wrap of impliedTags(el, ctx)) {\n const w = ctx.doc.createElement(wrap);\n target.appendChild(w);\n target = w;\n }\n }\n if (tag) {\n const out = ctx.doc.createElement(tag);\n copyAttributes(el, out, tag, ctx);\n target.appendChild(out);\n target = out;\n }\n if (tag === 'BR') return;\n walkChildren(el, target, ctx);\n}\n\n/** Returns `html` reduced to the allow-list. Empty input returns an empty string. */\nexport function sanitizeRichTextHtml(html: string | null | undefined, options: RichTextSanitizeOptions = {}): string {\n if (!html) return '';\n const parsed = new DOMParser().parseFromString(`<body>${html}</body>`, 'text/html');\n const ctx: Ctx = {\n doc: parsed,\n mode: options.mode ?? 'content',\n blocked: options.blockedTags ?? new Set<string>(),\n replace: options.replace ?? {},\n };\n const out = parsed.createElement('body');\n walkChildren(parsed.body, out, ctx);\n return out.innerHTML;\n}\n\n/** Escapes plain text for HTML, turning line breaks into `<br>`. */\nexport function plainTextToHtml(text: string): string {\n return text\n .replace(/\\r\\n?/g, '\\n')\n .replace(/&/g, '&')\n .replace(/</g, '<')\n .replace(/>/g, '>')\n .replace(/\\n/g, '<br>');\n}\n","import {\n Component, ChangeDetectionStrategy, ElementRef, computed, forwardRef, input, signal, viewChild, afterNextRender,\n} from '@angular/core';\nimport { ControlValueAccessor, NG_VALUE_ACCESSOR } from '@angular/forms';\nimport { plainTextToHtml, sanitizeRichTextHtml } from './rich-text-sanitizer';\n\n/**\n * Every toolbar control of `ui-rich-text-editor`. All are shown by default; pass any of them in\n * `hide` to remove just that control (a whole group, and its divider, disappears once all of its\n * controls are hidden).\n *\n * Groups, left to right: `bold italic underline strikethrough` | `heading fontSize fontFamily` |\n * `textColor highlight` | `align` | `bulletList numberedList indent outdent` | `clearFormat`.\n * `align` is the three alignment buttons (left, center, right) as one option.\n */\nexport type RichTextOption =\n | 'bold' | 'italic' | 'underline' | 'strikethrough'\n | 'heading' | 'fontSize' | 'fontFamily'\n | 'textColor' | 'highlight'\n | 'align'\n | 'bulletList' | 'numberedList' | 'indent' | 'outdent'\n | 'clearFormat';\n\n/** All option keys, in toolbar order. */\nexport const RICH_TEXT_OPTIONS: readonly RichTextOption[] = [\n 'bold', 'italic', 'underline', 'strikethrough',\n 'heading', 'fontSize', 'fontFamily',\n 'textColor', 'highlight',\n 'align',\n 'bulletList', 'numberedList', 'indent', 'outdent',\n 'clearFormat',\n];\n\ntype GroupId = 'inline' | 'text' | 'color' | 'align' | 'blocks' | 'clear';\ntype MenuId = 'heading' | 'size' | 'font';\n\nconst GROUPS: { id: GroupId; options: RichTextOption[] }[] = [\n { id: 'inline', options: ['bold', 'italic', 'underline', 'strikethrough'] },\n { id: 'text', options: ['heading', 'fontSize', 'fontFamily'] },\n { id: 'color', options: ['textColor', 'highlight'] },\n { id: 'align', options: ['align'] },\n { id: 'blocks', options: ['bulletList', 'numberedList', 'indent', 'outdent'] },\n { id: 'clear', options: ['clearFormat'] },\n];\n\n/** Tags (upper-case) removed from pasted content when the matching option is hidden. */\nconst OPTION_TAGS: Partial<Record<RichTextOption, string[]>> = {\n bold: ['B', 'STRONG'],\n italic: ['I', 'EM'],\n underline: ['U'],\n strikethrough: ['S', 'STRIKE', 'DEL'],\n heading: ['H1', 'H2', 'H3', 'H4'],\n bulletList: ['UL'],\n numberedList: ['OL'],\n indent: ['BLOCKQUOTE'],\n};\n/** Hidden block tags become a plain line instead of vanishing (keeps the text on its own line). */\nconst BLOCK_REPLACEMENT: Record<string, string> = { H1: 'DIV', H2: 'DIV', H3: 'DIV', H4: 'DIV', BLOCKQUOTE: 'DIV' };\n\nconst HEADINGS = [\n { tag: 'p', label: 'Normal', text: 'Normal', cls: '' },\n { tag: 'h1', label: 'H1', text: 'H1 — Heading 1', cls: 'di-h1' },\n { tag: 'h2', label: 'H2', text: 'H2 — Heading 2', cls: 'di-h2' },\n { tag: 'h3', label: 'H3', text: 'H3 — Heading 3', cls: 'di-h3' },\n];\n\nconst SIZES = [\n { value: '1', label: 'Small', px: 11 },\n { value: '3', label: 'Normal', px: 13 },\n { value: '4', label: 'Medium', px: 15 },\n { value: '5', label: 'Large', px: 18 },\n { value: '6', label: 'X-Large', px: 22 },\n { value: '7', label: 'Huge', px: 26 },\n];\n\nconst FONT_GROUPS = [\n { label: 'Default', fonts: [{ value: 'inherit', label: 'Default', preview: 'inherit' }] },\n { label: 'Serif', fonts: [\n { value: 'Lora, Georgia, serif', label: 'Lora', preview: 'Georgia, serif' },\n { value: 'Cormorant Garamond, serif', label: 'Cormorant Garamond', preview: 'Georgia, serif' },\n { value: 'Georgia, serif', label: 'Georgia', preview: 'Georgia, serif' },\n { value: 'Palatino Linotype, Palatino, serif', label: 'Palatino', preview: 'Palatino, serif' },\n { value: 'Times New Roman, Times, serif', label: 'Times New Roman', preview: \"'Times New Roman', serif\" },\n { value: 'Garamond, serif', label: 'Garamond', preview: 'Garamond, serif' },\n { value: 'Book Antiqua, Palatino, serif', label: 'Book Antiqua', preview: 'Palatino, serif' },\n ] },\n { label: 'Sans-Serif', fonts: [\n { value: 'Lato, sans-serif', label: 'Lato', preview: 'Arial, sans-serif' },\n { value: 'Inter, sans-serif', label: 'Inter', preview: 'Arial, sans-serif' },\n { value: 'Arial, Helvetica, sans-serif', label: 'Arial', preview: 'Arial, sans-serif' },\n { value: 'Helvetica Neue, Helvetica, sans-serif', label: 'Helvetica', preview: 'Helvetica, Arial, sans-serif' },\n { value: 'Open Sans, sans-serif', label: 'Open Sans', preview: 'Arial, sans-serif' },\n { value: 'Montserrat, sans-serif', label: 'Montserrat', preview: 'Arial, sans-serif' },\n { value: 'Raleway, sans-serif', label: 'Raleway', preview: 'Arial, sans-serif' },\n { value: 'Nunito, sans-serif', label: 'Nunito', preview: 'Arial, sans-serif' },\n { value: 'Poppins, sans-serif', label: 'Poppins', preview: 'Arial, sans-serif' },\n { value: 'Verdana, Geneva, sans-serif', label: 'Verdana', preview: 'Verdana, sans-serif' },\n { value: 'Tahoma, Geneva, sans-serif', label: 'Tahoma', preview: 'Tahoma, sans-serif' },\n { value: 'Trebuchet MS, Helvetica, sans-serif', label: 'Trebuchet MS', preview: \"'Trebuchet MS', sans-serif\" },\n ] },\n { label: 'Monospace', fonts: [\n { value: 'Courier New, Courier, monospace', label: 'Courier New', preview: \"'Courier New', monospace\" },\n { value: 'Lucida Console, Monaco, monospace', label: 'Lucida Console', preview: 'Monaco, monospace' },\n ] },\n { label: 'Display', fonts: [\n { value: 'Impact, Charcoal, sans-serif', label: 'Impact', preview: 'Impact, sans-serif' },\n { value: 'Comic Sans MS, cursive', label: 'Comic Sans', preview: \"'Comic Sans MS', cursive\" },\n ] },\n];\n\n/** Commands whose on/off state is mirrored with `aria-pressed`. */\nconst STATE_COMMANDS = [\n 'bold', 'italic', 'underline', 'strikeThrough',\n 'insertUnorderedList', 'insertOrderedList',\n 'justifyLeft', 'justifyCenter', 'justifyRight',\n] as const;\n\nlet nextId = 0;\n\n@Component({\n selector: 'ui-rich-text-editor',\n standalone: true,\n changeDetection: ChangeDetectionStrategy.OnPush,\n providers: [{\n provide: NG_VALUE_ACCESSOR,\n useExisting: forwardRef(() => RichTextEditorComponent),\n multi: true,\n }],\n host: {\n '(document:click)': 'onDocumentClick()',\n '(document:selectionchange)': 'onSelectionChange()',\n },\n template: `\n <div class=\"rte-field\" [class.rte-error]=\"error()\" [class.rte-disabled]=\"disabled()\">\n @if (label()) {\n <label class=\"rte-label\" [id]=\"_labelId\" (click)=\"focus()\">\n {{ label() }}\n @if (required()) { <span class=\"rte-required\" aria-hidden=\"true\">*</span> }\n @if (hint()) { <span class=\"rte-hint-inline\">({{ hint() }})</span> }\n </label>\n }\n\n <div class=\"rte-box\">\n @if (toolbarVisible()) {\n <div class=\"rte-toolbar\" role=\"toolbar\" aria-label=\"Text formatting\" [attr.aria-controls]=\"_editorId\"\n (mousedown)=\"onToolbarMouseDown($event)\">\n @if (show('bold') || show('italic') || show('underline') || show('strikethrough')) {\n @if (show('bold')) {\n <button type=\"button\" class=\"rte-btn\" data-option=\"bold\" [class.active]=\"active()['bold']\"\n [attr.aria-pressed]=\"active()['bold']\" [disabled]=\"inert()\"\n aria-label=\"Bold\" title=\"Bold (Ctrl+B)\" (click)=\"exec('bold')\">\n <svg aria-hidden=\"true\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"3\"><path d=\"M6 4h8a4 4 0 014 4 4 4 0 01-4 4H6z\"/><path d=\"M6 12h9a4 4 0 014 4 4 4 0 01-4 4H6z\"/></svg>\n </button>\n }\n @if (show('italic')) {\n <button type=\"button\" class=\"rte-btn\" data-option=\"italic\" [class.active]=\"active()['italic']\"\n [attr.aria-pressed]=\"active()['italic']\" [disabled]=\"inert()\"\n aria-label=\"Italic\" title=\"Italic (Ctrl+I)\" (click)=\"exec('italic')\">\n <svg aria-hidden=\"true\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\"><line x1=\"19\" y1=\"4\" x2=\"10\" y2=\"4\"/><line x1=\"14\" y1=\"20\" x2=\"5\" y2=\"20\"/><line x1=\"15\" y1=\"4\" x2=\"9\" y2=\"20\"/></svg>\n </button>\n }\n @if (show('underline')) {\n <button type=\"button\" class=\"rte-btn\" data-option=\"underline\" [class.active]=\"active()['underline']\"\n [attr.aria-pressed]=\"active()['underline']\" [disabled]=\"inert()\"\n aria-label=\"Underline\" title=\"Underline (Ctrl+U)\" (click)=\"exec('underline')\">\n <svg aria-hidden=\"true\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\"><path d=\"M6 3v7a6 6 0 006 6 6 6 0 006-6V3\"/><line x1=\"4\" y1=\"21\" x2=\"20\" y2=\"21\"/></svg>\n </button>\n }\n @if (show('strikethrough')) {\n <button type=\"button\" class=\"rte-btn\" data-option=\"strikethrough\" [class.active]=\"active()['strikeThrough']\"\n [attr.aria-pressed]=\"active()['strikeThrough']\" [disabled]=\"inert()\"\n aria-label=\"Strikethrough\" title=\"Strikethrough\" (click)=\"exec('strikeThrough')\">\n <svg aria-hidden=\"true\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\"><line x1=\"4\" y1=\"12\" x2=\"20\" y2=\"12\"/><path d=\"M6 4h8a4 4 0 010 8H6\"/><path d=\"M10 12v8\"/></svg>\n </button>\n }\n }\n\n @if (divider()['text']) { <div class=\"rte-divider\" role=\"separator\" aria-orientation=\"vertical\"></div> }\n @if (show('heading')) {\n <span class=\"rte-menu-wrap\" (click)=\"$event.stopPropagation()\">\n <button type=\"button\" class=\"rte-btn rte-menu-btn\" data-option=\"heading\" [class.active]=\"openMenu() === 'heading'\"\n [disabled]=\"inert()\" aria-label=\"Heading style\" title=\"Heading Style\"\n aria-haspopup=\"menu\" [attr.aria-expanded]=\"openMenu() === 'heading'\" (click)=\"toggleMenu('heading')\">\n <svg aria-hidden=\"true\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\"><path d=\"M4 6h16M4 12h16M4 18h10\"/></svg>\n <svg class=\"rte-chevron\" aria-hidden=\"true\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2.5\"><polyline points=\"6 9 12 15 18 9\"/></svg>\n </button>\n @if (openMenu() === 'heading') {\n <div class=\"rte-dropdown\" role=\"menu\" aria-label=\"Heading style\" style=\"min-width:130px\">\n @for (h of headings; track h.tag) {\n <button type=\"button\" role=\"menuitemradio\" class=\"rte-item\" [class]=\"h.cls\"\n [class.selected]=\"activeHeading() === h.label\" [attr.aria-checked]=\"activeHeading() === h.label\"\n (click)=\"selectHeading(h.tag, h.label)\">{{ h.text }}</button>\n }\n </div>\n }\n </span>\n }\n @if (show('fontSize')) {\n <span class=\"rte-menu-wrap\" (click)=\"$event.stopPropagation()\">\n <button type=\"button\" class=\"rte-btn rte-menu-btn\" data-option=\"fontSize\" [class.active]=\"openMenu() === 'size'\"\n [disabled]=\"inert()\" aria-label=\"Font size\" title=\"Font Size\"\n aria-haspopup=\"menu\" [attr.aria-expanded]=\"openMenu() === 'size'\" (click)=\"toggleMenu('size')\">\n <svg aria-hidden=\"true\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\"><text x=\"2\" y=\"17\" font-size=\"14\" fill=\"currentColor\" stroke=\"none\" font-weight=\"bold\">T</text><text x=\"13\" y=\"20\" font-size=\"9\" fill=\"currentColor\" stroke=\"none\">T</text></svg>\n <svg class=\"rte-chevron\" aria-hidden=\"true\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2.5\"><polyline points=\"6 9 12 15 18 9\"/></svg>\n </button>\n @if (openMenu() === 'size') {\n <div class=\"rte-dropdown\" role=\"menu\" aria-label=\"Font size\" style=\"min-width:110px\">\n @for (s of sizes; track s.value) {\n <button type=\"button\" role=\"menuitemradio\" class=\"rte-item\"\n [class.selected]=\"activeFontSize() === s.label\" [attr.aria-checked]=\"activeFontSize() === s.label\"\n (click)=\"selectSize(s.value, s.label)\"><span [style.font-size.px]=\"s.px\">{{ s.label }}</span></button>\n }\n </div>\n }\n </span>\n }\n @if (show('fontFamily')) {\n <span class=\"rte-menu-wrap\" (click)=\"$event.stopPropagation()\">\n <button type=\"button\" class=\"rte-btn rte-menu-btn\" data-option=\"fontFamily\" [class.active]=\"openMenu() === 'font'\"\n [disabled]=\"inert()\" aria-label=\"Font family\" title=\"Font Family\"\n aria-haspopup=\"menu\" [attr.aria-expanded]=\"openMenu() === 'font'\" (click)=\"toggleMenu('font')\">\n <svg aria-hidden=\"true\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\"><text x=\"1\" y=\"18\" font-size=\"18\" fill=\"currentColor\" stroke=\"none\" font-weight=\"600\" font-style=\"italic\">Aa</text></svg>\n <svg class=\"rte-chevron\" aria-hidden=\"true\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2.5\"><polyline points=\"6 9 12 15 18 9\"/></svg>\n </button>\n @if (openMenu() === 'font') {\n <div class=\"rte-dropdown rte-font-panel\" role=\"menu\" aria-label=\"Font family\">\n @for (g of fontGroups; track g.label) {\n <div class=\"rte-group-label\" role=\"presentation\">{{ g.label }}</div>\n @for (f of g.fonts; track f.label) {\n <button type=\"button\" role=\"menuitemradio\" class=\"rte-item\"\n [class.selected]=\"activeFont() === f.label\" [attr.aria-checked]=\"activeFont() === f.label\"\n [style.font-family]=\"f.preview\" (click)=\"selectFont(f.value, f.label)\">{{ f.label }}</button>\n }\n }\n </div>\n }\n </span>\n }\n\n @if (divider()['color']) { <div class=\"rte-divider\" role=\"separator\" aria-orientation=\"vertical\"></div> }\n @if (show('textColor')) {\n <label class=\"rte-btn\" data-option=\"textColor\" title=\"Text Color\" [class.rte-btn-disabled]=\"inert()\">\n <svg aria-hidden=\"true\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\"><path d=\"M4 20h16M12 4L6 18M12 4l6 14M8.5 13h7\"/></svg>\n <input type=\"color\" class=\"rte-color\" aria-label=\"Text color\" value=\"#000000\" [disabled]=\"inert()\"\n (input)=\"execWithArg('foreColor', $any($event.target).value)\" />\n </label>\n }\n @if (show('highlight')) {\n <label class=\"rte-btn\" data-option=\"highlight\" title=\"Highlight Color\" [class.rte-btn-disabled]=\"inert()\">\n <svg aria-hidden=\"true\" viewBox=\"0 0 24 24\" fill=\"currentColor\"><path d=\"M15.24 2.58a1 1 0 00-1.41 0L5.59 10.82a2 2 0 000 2.83l4.24 4.24L8.41 19.3a1 1 0 001.42 1.41l1.41-1.41 1.06 1.06a1 1 0 001.42 0l8-8a1 1 0 000-1.42l-6.48-6.36zM7 13.65l3.54-3.54 3.54 3.54-3.54 3.53L7 13.65z\" opacity=\".4\"/><path d=\"M10.54 10.11l3.54-3.54 3.53 3.54-3.53 3.53-3.54-3.53z\"/></svg>\n <input type=\"color\" class=\"rte-color\" aria-label=\"Highlight color\" value=\"#ffff00\" [disabled]=\"inert()\"\n (input)=\"execWithArg('hiliteColor', $any($event.target).value)\" />\n </label>\n }\n\n @if (divider()['align']) { <div class=\"rte-divider\" role=\"separator\" aria-orientation=\"vertical\"></div> }\n @if (show('align')) {\n <button type=\"button\" class=\"rte-btn\" data-option=\"align-left\" [class.active]=\"active()['justifyLeft']\"\n [attr.aria-pressed]=\"active()['justifyLeft']\" [disabled]=\"inert()\"\n aria-label=\"Align left\" title=\"Align Left\" (click)=\"exec('justifyLeft')\">\n <svg aria-hidden=\"true\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\"><line x1=\"3\" y1=\"6\" x2=\"21\" y2=\"6\"/><line x1=\"3\" y1=\"12\" x2=\"15\" y2=\"12\"/><line x1=\"3\" y1=\"18\" x2=\"18\" y2=\"18\"/></svg>\n </button>\n <button type=\"button\" class=\"rte-btn\" data-option=\"align-center\" [class.active]=\"active()['justifyCenter']\"\n [attr.aria-pressed]=\"active()['justifyCenter']\" [disabled]=\"inert()\"\n aria-label=\"Align center\" title=\"Align Center\" (click)=\"exec('justifyCenter')\">\n <svg aria-hidden=\"true\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\"><line x1=\"3\" y1=\"6\" x2=\"21\" y2=\"6\"/><line x1=\"6\" y1=\"12\" x2=\"18\" y2=\"12\"/><line x1=\"4\" y1=\"18\" x2=\"20\" y2=\"18\"/></svg>\n </button>\n <button type=\"button\" class=\"rte-btn\" data-option=\"align-right\" [class.active]=\"active()['justifyRight']\"\n [attr.aria-pressed]=\"active()['justifyRight']\" [disabled]=\"inert()\"\n aria-label=\"Align right\" title=\"Align Right\" (click)=\"exec('justifyRight')\">\n <svg aria-hidden=\"true\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\"><line x1=\"3\" y1=\"6\" x2=\"21\" y2=\"6\"/><line x1=\"9\" y1=\"12\" x2=\"21\" y2=\"12\"/><line x1=\"6\" y1=\"18\" x2=\"21\" y2=\"18\"/></svg>\n </button>\n }\n\n @if (divider()['blocks']) { <div class=\"rte-divider\" role=\"separator\" aria-orientation=\"vertical\"></div> }\n @if (show('bulletList')) {\n <button type=\"button\" class=\"rte-btn\" data-option=\"bulletList\" [class.active]=\"active()['insertUnorderedList']\"\n [attr.aria-pressed]=\"active()['insertUnorderedList']\" [disabled]=\"inert()\"\n aria-label=\"Bullet list\" title=\"Bullet List\" (click)=\"exec('insertUnorderedList')\">\n <svg aria-hidden=\"true\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\"><line x1=\"9\" y1=\"6\" x2=\"20\" y2=\"6\"/><line x1=\"9\" y1=\"12\" x2=\"20\" y2=\"12\"/><line x1=\"9\" y1=\"18\" x2=\"20\" y2=\"18\"/><circle cx=\"5\" cy=\"6\" r=\"1.5\" fill=\"currentColor\"/><circle cx=\"5\" cy=\"12\" r=\"1.5\" fill=\"currentColor\"/><circle cx=\"5\" cy=\"18\" r=\"1.5\" fill=\"currentColor\"/></svg>\n </button>\n }\n @if (show('numberedList')) {\n <button type=\"button\" class=\"rte-btn\" data-option=\"numberedList\" [class.active]=\"active()['insertOrderedList']\"\n [attr.aria-pressed]=\"active()['insertOrderedList']\" [disabled]=\"inert()\"\n aria-label=\"Numbered list\" title=\"Numbered List\" (click)=\"exec('insertOrderedList')\">\n <svg aria-hidden=\"true\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\"><line x1=\"10\" y1=\"6\" x2=\"20\" y2=\"6\"/><line x1=\"10\" y1=\"12\" x2=\"20\" y2=\"12\"/><line x1=\"10\" y1=\"18\" x2=\"20\" y2=\"18\"/><text x=\"3\" y=\"8\" font-size=\"8\" fill=\"currentColor\" stroke=\"none\" font-weight=\"bold\">1</text><text x=\"3\" y=\"14\" font-size=\"8\" fill=\"currentColor\" stroke=\"none\" font-weight=\"bold\">2</text><text x=\"3\" y=\"20\" font-size=\"8\" fill=\"currentColor\" stroke=\"none\" font-weight=\"bold\">3</text></svg>\n </button>\n }\n @if (show('indent')) {\n <button type=\"button\" class=\"rte-btn\" data-option=\"indent\" [disabled]=\"inert()\"\n aria-label=\"Indent\" title=\"Indent\" (click)=\"exec('indent')\">\n <svg aria-hidden=\"true\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\"><line x1=\"3\" y1=\"6\" x2=\"21\" y2=\"6\"/><line x1=\"3\" y1=\"18\" x2=\"21\" y2=\"18\"/><polyline points=\"9 8 13 12 9 16\"/><line x1=\"3\" y1=\"12\" x2=\"13\" y2=\"12\"/></svg>\n </button>\n }\n @if (show('outdent')) {\n <button type=\"button\" class=\"rte-btn\" data-option=\"outdent\" [disabled]=\"inert()\"\n aria-label=\"Outdent\" title=\"Outdent\" (click)=\"exec('outdent')\">\n <svg aria-hidden=\"true\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\"><line x1=\"3\" y1=\"6\" x2=\"21\" y2=\"6\"/><line x1=\"3\" y1=\"18\" x2=\"21\" y2=\"18\"/><polyline points=\"7 8 3 12 7 16\"/><line x1=\"3\" y1=\"12\" x2=\"21\" y2=\"12\"/></svg>\n </button>\n }\n\n @if (divider()['clear']) { <div class=\"rte-divider\" role=\"separator\" aria-orientation=\"vertical\"></div> }\n @if (show('clearFormat')) {\n <button type=\"button\" class=\"rte-btn\" data-option=\"clearFormat\" [disabled]=\"inert()\"\n aria-label=\"Clear formatting\" title=\"Clear Formatting\" (click)=\"exec('removeFormat')\">\n <svg aria-hidden=\"true\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\"><path d=\"M4 7h16M6 4h8a2 2 0 012 2v0a2 2 0 01-2 2H6\"/><line x1=\"4\" y1=\"20\" x2=\"20\" y2=\"4\" stroke-width=\"1.5\"/></svg>\n </button>\n }\n </div>\n }\n\n <div\n #editor\n class=\"rte-editor\"\n [id]=\"_editorId\"\n [attr.contenteditable]=\"inert() ? 'false' : 'true'\"\n role=\"textbox\"\n aria-multiline=\"true\"\n [attr.aria-label]=\"label() ? null : (ariaLabel() || placeholder() || 'Rich text editor')\"\n [attr.aria-labelledby]=\"label() ? _labelId : null\"\n [attr.aria-required]=\"required() || null\"\n [attr.aria-readonly]=\"readonly() || null\"\n [attr.aria-disabled]=\"disabled() || null\"\n [attr.aria-invalid]=\"error() ? 'true' : null\"\n [attr.aria-describedby]=\"error() ? _errorId : null\"\n [attr.data-placeholder]=\"placeholder()\"\n [attr.data-empty]=\"empty() ? 'true' : null\"\n [style.min-height.px]=\"minHeight()\"\n [style.max-height.px]=\"maxHeight()\"\n (input)=\"onInput()\"\n (keydown)=\"onKeydown($event)\"\n (paste)=\"onPaste($event)\"\n (keyup)=\"refreshActive()\"\n (mouseup)=\"refreshActive()\"\n (blur)=\"onBlur()\"\n ></div>\n </div>\n\n @if (error()) {\n <p class=\"rte-desc rte-desc-error\" [id]=\"_errorId\" role=\"alert\">{{ error() }}</p>\n }\n </div>\n `,\n styles: [`\n :host { display: block; }\n\n .rte-field { display: flex; flex-direction: column; gap: 6px; }\n\n .rte-label { font-size: 0.8125rem; font-weight: 600; color: #374151; line-height: 1; }\n .rte-required { color: #ef4444; margin-left: 2px; }\n .rte-hint-inline { font-size: 0.75rem; color: #94a3b8; font-weight: 400; margin-left: 4px; }\n\n .rte-box {\n border: 1px solid #e2e8f0; border-radius: 8px; background: #fff;\n transition: border-color 0.15s, box-shadow 0.15s;\n }\n .rte-box:focus-within { border-color: #3b82f6; box-shadow: 0 0 0 3px rgba(59, 130, 246, 0.12); }\n .rte-error .rte-box { border-color: #ef4444; }\n .rte-error .rte-box:focus-within { box-shadow: 0 0 0 3px rgba(239, 68, 68, 0.12); }\n .rte-disabled .rte-box { opacity: 0.5; cursor: not-allowed; }\n\n .rte-toolbar {\n display: flex; flex-wrap: wrap; align-items: center; gap: 2px;\n padding: 6px 8px; background: #f8fafc;\n border-bottom: 1px solid #e2e8f0; border-radius: 8px 8px 0 0;\n }\n .rte-divider { width: 1px; height: 20px; background: #cbd5e1; margin: 0 4px; }\n\n .rte-btn {\n position: relative;\n display: inline-flex; align-items: center; justify-content: center;\n width: 28px; height: 28px; padding: 0; border: none; border-radius: 4px;\n background: none; color: #64748b; cursor: pointer; transition: background 0.15s, color 0.15s;\n }\n .rte-btn svg { width: 16px; height: 16px; }\n .rte-btn:hover:not(:disabled) { background: #e2e8f0; color: #1e293b; }\n .rte-btn:focus-visible, .rte-btn:focus-within { outline: 2px solid #3b82f6; outline-offset: 1px; }\n .rte-btn.active { background: #dbeafe; color: #2563eb; }\n .rte-btn:disabled, .rte-btn-disabled { cursor: not-allowed; opacity: 0.5; }\n\n .rte-menu-wrap { position: relative; display: inline-flex; }\n .rte-menu-btn { width: auto; padding: 0 6px; gap: 3px; }\n .rte-menu-btn .rte-chevron { width: 10px; height: 10px; color: #94a3b8; flex-shrink: 0; }\n\n .rte-dropdown {\n position: absolute; top: calc(100% + 4px); left: 0; z-index: 100;\n background: #fff; border: 1px solid #e2e8f0; border-radius: 6px;\n box-shadow: 0 4px 12px rgba(0, 0, 0, 0.12); padding: 4px;\n overflow-y: auto; max-height: 260px;\n }\n .rte-font-panel { min-width: 180px; }\n .rte-item {\n display: block; width: 100%; text-align: left; padding: 5px 10px;\n font-size: 12px; color: #374151; border: none; background: none;\n border-radius: 4px; cursor: pointer; white-space: nowrap;\n }\n .rte-item:hover { background: #f1f5f9; }\n .rte-item.selected { background: #dbeafe; color: #2563eb; font-weight: 600; }\n .rte-group-label {\n font-size: 10px; font-weight: 600; text-transform: uppercase; letter-spacing: 0.05em;\n color: #94a3b8; padding: 6px 10px 2px;\n }\n .di-h1 { font-size: 18px; font-weight: 700; }\n .di-h2 { font-size: 15px; font-weight: 600; }\n .di-h3 { font-size: 13px; font-weight: 600; }\n\n .rte-color { position: absolute; width: 1px; height: 1px; opacity: 0; pointer-events: none; }\n\n .rte-editor {\n padding: 12px; outline: none; overflow-y: auto; box-sizing: border-box;\n font-size: 0.875rem; line-height: 1.6; color: #1e293b; background: #fff;\n border-radius: 0 0 8px 8px; word-break: break-word;\n }\n .rte-editor[data-empty=\"true\"]::before {\n content: attr(data-placeholder); color: #94a3b8; pointer-events: none; float: left; height: 0;\n }\n .rte-editor ul, .rte-editor ol { padding-left: 1.5em; margin: 0.25em 0; }\n .rte-editor li { margin: 0.15em 0; }\n .rte-editor h1 { font-size: 1.75em; font-weight: 700; margin: 0.5em 0 0.25em; }\n .rte-editor h2 { font-size: 1.4em; font-weight: 600; margin: 0.5em 0 0.25em; }\n .rte-editor h3 { font-size: 1.15em; font-weight: 600; margin: 0.5em 0 0.25em; }\n .rte-editor a { color: #2563eb; text-decoration: underline; }\n\n .rte-desc { margin: 0; font-size: 0.75rem; line-height: 1.4; color: #64748b; }\n .rte-desc-error { color: #ef4444; }\n\n @media (max-width: 480px) { .rte-editor { font-size: 1rem; } }\n `],\n})\nexport class RichTextEditorComponent implements ControlValueAccessor {\n readonly label = input('');\n /** Accessible name for an editor with no visible label. */\n readonly ariaLabel = input('');\n readonly placeholder = input('');\n readonly required = input(false);\n readonly readonly = input(false);\n readonly hint = input('');\n readonly error = input('');\n /** Minimum editor height in px. */\n readonly minHeight = input(200);\n /** Maximum editor height in px; taller content scrolls. */\n readonly maxHeight = input(600);\n /** Toolbar controls to remove. Everything is shown by default. */\n readonly hide = input<RichTextOption[]>([]);\n\n protected readonly _disabledInput = input(false, { alias: 'disabled' });\n private readonly _cvaDisabled = signal(false);\n protected readonly disabled = computed(() => this._disabledInput() || this._cvaDisabled());\n /** Disabled or read-only: the editor is not editable and every toolbar control is disabled. */\n protected readonly inert = computed(() => this.disabled() || this.readonly());\n\n private readonly editor = viewChild<ElementRef<HTMLDivElement>>('editor');\n\n protected readonly _id = ++nextId;\n protected readonly _editorId = `rte-${this._id}`;\n protected readonly _labelId = `rte-${this._id}-label`;\n protected readonly _errorId = `rte-${this._id}-error`;\n\n protected readonly headings = HEADINGS;\n protected readonly sizes = SIZES;\n protected readonly fontGroups = FONT_GROUPS;\n\n protected readonly openMenu = signal<MenuId | null>(null);\n protected readonly activeHeading = signal('Normal');\n protected readonly activeFontSize = signal('Normal');\n protected readonly activeFont = signal('Default');\n protected readonly active = signal<Record<string, boolean>>(Object.fromEntries(STATE_COMMANDS.map(c => [c, false])));\n protected readonly empty = signal(true);\n\n private readonly hidden = computed(() => new Set<RichTextOption>(this.hide()));\n\n /** Group id → at least one of its controls is shown. */\n private readonly groupVisible = computed(() => {\n const hidden = this.hidden();\n const out: Record<string, boolean> = {};\n for (const g of GROUPS) out[g.id] = g.options.some(o => !hidden.has(o));\n return out;\n });\n protected readonly toolbarVisible = computed(() => Object.values(this.groupVisible()).some(Boolean));\n /** Group id → a divider belongs in front of it (it is visible and an earlier group is too). */\n protected readonly divider = computed(() => {\n const visible = this.groupVisible();\n const out: Record<string, boolean> = {};\n let seen = false;\n for (const g of GROUPS) {\n out[g.id] = visible[g.id] && seen;\n seen = seen || visible[g.id];\n }\n return out;\n });\n\n private _lastEmitted = '';\n private _pending: string | null = null;\n private _savedRange: Range | null = null;\n\n private _onChange = (_: string) => {};\n private _onTouched = () => {};\n\n constructor() {\n afterNextRender(() => {\n if (this._pending !== null) {\n this._setHtml(this._pending);\n this._pending = null;\n }\n });\n }\n\n protected show(option: RichTextOption): boolean {\n return !this.hidden().has(option);\n }\n\n // ── ControlValueAccessor ────────────────────────────────────────────────\n\n writeValue(val: string | null): void {\n const html = val ?? '';\n if (html === this._lastEmitted) return; // our own change echoed back by ngModel — don't reset the caret\n this._lastEmitted = html;\n this._setHtml(html);\n }\n registerOnChange(fn: (v: string) => void): void { this._onChange = fn; }\n registerOnTouched(fn: () => void): void { this._onTouched = fn; }\n setDisabledState(d: boolean): void { this._cvaDisabled.set(d); }\n\n private _setHtml(html: string): void {\n const el = this.editor()?.nativeElement;\n if (!el) { this._pending = html; return; }\n const clean = sanitizeRichTextHtml(html);\n if (el.innerHTML !== clean) el.innerHTML = clean;\n this.empty.set(this._isEmpty(el));\n }\n\n private _isEmpty(el: HTMLElement): boolean {\n return !(el.textContent ?? '').trim() && !el.querySelector('li, hr, img');\n }\n\n // ── Public helpers ──────────────────────────────────────────────────────\n\n focus(): void {\n this.editor()?.nativeElement.focus();\n }\n\n // ── Events ──────────────────────────────────────────────────────────────\n\n protected onInput(): void {\n const el = this.editor()?.nativeElement;\n if (!el) return;\n const isEmpty = this._isEmpty(el);\n this.empty.set(isEmpty);\n // an emptied editor leaves a stray <br> behind — report it as an empty value\n const html = isEmpty ? '' : el.innerHTML;\n this._lastEmitted = html;\n this._onChange(html);\n }\n\n protected onBlur(): void {\n this._saveSelection();\n this._onTouched();\n }\n\n protected onToolbarMouseDown(event: MouseEvent): void {\n // keep the editor's selection when a toolbar button is pressed (colour pickers need the click)\n const target = event.target as HTMLElement;\n if (target.closest('button')) event.preventDefault();\n }\n\n onDocumentClick(): void {\n if (this.openMenu()) this.openMenu.set(null);\n }\n\n onSelectionChange(): void {\n const el = this.editor()?.nativeElement;\n const sel = window.getSelection();\n if (!el || !sel || sel.rangeCount === 0 || !el.contains(sel.anchorNode)) return;\n this.refreshActive();\n }\n\n protected refreshActive(): void {\n if (this.inert()) return;\n const next: Record<string, boolean> = {};\n for (const cmd of STATE_COMMANDS) {\n try { next[cmd] = document.queryCommandState(cmd); } catch { next[cmd] = false; }\n }\n const prev = this.active();\n if (STATE_COMMANDS.some(c => prev[c] !== next[c])) this.active.set(next);\n }\n\n protected onKeydown(event: KeyboardEvent): void {\n if (!(event.metaKey || event.ctrlKey) || event.altKey || event.shiftKey) return;\n const shortcut: Record<string, { option: RichTextOption; command: string }> = {\n b: { option: 'bold', command: 'bold' },\n i: { option: 'italic', command: 'italic' },\n u: { option: 'underline', command: 'underline' },\n };\n const hit = shortcut[event.key.toLowerCase()];\n if (!hit) return;\n // always swallow the browser's own shortcut so a hidden option can't be applied by keyboard\n event.preventDefault();\n if (this.show(hit.option) && !this.inert()) this.exec(hit.command);\n }\n\n protected onPaste(event: ClipboardEvent): void {\n event.preventDefault();\n if (this.inert()) return;\n const data = event.clipboardData;\n if (!data) return;\n const html = data.getData('text/html');\n const text = data.getData('text/plain');\n if (html) {\n const clean = sanitizeRichTextHtml(html, {\n mode: 'paste',\n blockedTags: this._blockedTags(),\n replace: BLOCK_REPLACEMENT,\n });\n document.execCommand('insertHTML', false, clean);\n } else if (text) {\n if (text.includes('\\n') || text.includes('\\r')) document.execCommand('insertHTML', false, plainTextToHtml(text));\n else document.execCommand('insertText', false, text);\n }\n this.onInput();\n }\n\n private _blockedTags(): Set<string> {\n const blocked = new Set<string>();\n for (const option of this.hidden()) for (const tag of OPTION_TAGS[option] ?? []) blocked.add(tag);\n return blocked;\n }\n\n // ── Selection ───────────────────────────────────────────────────────────\n\n private _saveSelection(): void {\n const el = this.editor()?.nativeElement;\n const sel = window.getSelection();\n if (el && sel && sel.rangeCount > 0 && el.contains(sel.anchorNode)) {\n this._savedRange = sel.getRangeAt(0).cloneRange();\n }\n }\n\n private _restoreSelection(): void {\n const el = this.editor()?.nativeElement;\n if (!el) return;\n const sel = window.getSelection();\n const inside = !!sel && sel.rangeCount > 0 && el.contains(sel.anchorNode);\n el.focus();\n if (!inside && this._savedRange && sel) {\n sel.removeAllRanges();\n sel.addRange(this._savedRange);\n }\n }\n\n // ── Commands ────────────────────────────────────────────────────────────\n\n protected toggleMenu(menu: MenuId): void {\n this.openMenu.set(this.openMenu() === menu ? null : menu);\n }\n\n protected selectHeading(tag: string, label: string): void {\n this.activeHeading.set(label);\n this.openMenu.set(null);\n this._restoreSelection();\n document.execCommand('formatBlock', false, tag);\n this.onInput();\n }\n\n protected selectSize(value: string, label: string): void {\n this.activeFontSize.set(label);\n this.openMenu.set(null);\n this.execWithArg('fontSize', value);\n }\n\n protected selectFont(value: string, label: string): void {\n this.activeFont.set(label);\n this.openMenu.set(null);\n this.execWithArg('fontName', value);\n }\n\n protected exec(command: string): void {\n if (this.inert()) return;\n this._restoreSelection();\n document.execCommand(command, false);\n this.onInput();\n this.refreshActive();\n }\n\n protected execWithArg(command: string, value: string): void {\n if (!value || this.inert()) return;\n this._restoreSelection();\n document.execCommand(command, false, value);\n this.onInput();\n }\n}\n"],"mappings":"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"}
|