@forgeroute/ui 0.4.8 → 0.4.10

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.
@@ -1 +1 @@
1
- {"version":3,"file":"forgeroute-ui-messaging.mjs","names":[],"sources":["../../../projects/forgeroute-ui/messaging/src/video-picker/video-picker.component.ts","../../../projects/forgeroute-ui/messaging/src/template-picker/template-picker.component.ts","../../../projects/forgeroute-ui/messaging/src/send-message-panel/server-message.ts","../../../projects/forgeroute-ui/messaging/src/document-send-options/document-signers.ts","../../../projects/forgeroute-ui/messaging/src/document-send-options/document-send-options.component.ts","../../../projects/forgeroute-ui/messaging/src/document-send-options/document-signers.component.ts","../../../projects/forgeroute-ui/messaging/src/document-send-options/document-merge-check.component.ts","../../../projects/forgeroute-ui/messaging/src/video-link-settings/video-link.ts","../../../projects/forgeroute-ui/messaging/src/video-link-settings/video-link-settings.component.ts","../../../projects/forgeroute-ui/messaging/src/send-message-panel/send-documents.ts","../../../projects/forgeroute-ui/messaging/src/send-message-panel/merge-tags.ts","../../../projects/forgeroute-ui/messaging/src/send-message-panel/send-email.component.ts","../../../projects/forgeroute-ui/messaging/src/send-message-panel/send-text.component.ts","../../../projects/forgeroute-ui/messaging/src/send-message-panel/send-message-panel.component.ts"],"sourcesContent":["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, ErrorComponent, UiFieldValidity, createFieldReveal, requiredIssue } from '@forgeroute/ui';\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\">&times;</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, ToastService, ErrorComponent, UiFieldValidity, createFieldReveal, requiredIssue } from '@forgeroute/ui';\nimport { Subject, debounceTime, switchMap, of, catchError } from 'rxjs';\nimport { takeUntilDestroyed } from '@angular/core/rxjs-interop';\nimport { FormsModule } from '@angular/forms';\n\nexport interface TemplateItem {\n id: string;\n name: string;\n subject: string | null;\n body: string;\n isActive?: boolean;\n /** Set when the API says whether this is a built-in system template (true) or one the company wrote (false); drives the System / Custom badge. Absent = no badge. */\n isSystem?: 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-title\">\n <span class=\"option-name\">{{ tpl.name }}</span>\n @if (tpl.isSystem !== undefined) {\n <span class=\"option-badge\" [class.system]=\"tpl.isSystem\">{{ tpl.isSystem ? 'System' : 'Custom' }}</span>\n }\n </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-title { display: flex; align-items: center; gap: 6px; min-width: 0; }\n .option-badge {\n flex-shrink: 0; font-size: 0.6875rem; font-weight: 600; line-height: 1; padding: 3px 7px; border-radius: 999px;\n background: #f1f5f9; color: #475569; border: 1px solid #e2e8f0;\n }\n .option-badge.system { background: #f3e8ff; color: #6b21a8; border-color: #e9d5ff; }\n .option-name { min-width: 0; 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 ...(typeof t.isSystem === 'boolean' ? { isSystem: t.isSystem } : {}),\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 { HttpErrorResponse } from '@angular/common/http';\n\n/** The `{ message }` a backend put in a failed response body, or '' when there is none (callers then use their own fixed text). */\nexport function serverMessage(err: unknown): string {\n if (!(err instanceof HttpErrorResponse)) return '';\n const m = err.error?.message;\n return typeof m === 'string' ? m.trim() : '';\n}\n","import type { Observable } from 'rxjs';\n\n/**\n * Types and pure helpers for the signer side of sending documents (docs/send-documents-contract.md, Addendum 2026-10-19).\n * Everything here is app-agnostic: the roles are `signer1 .. signer4`, labels / names come from the host.\n */\n\n/** A CUSTOMER signer role. The countersigner (the sending user) is never a role: it is the document's own setting. */\nexport type SignerRole = 'signer1' | 'signer2' | 'signer3' | 'signer4';\n\n/** All roles, in signing order (the order is strictly sequential by role number). */\nexport const SIGNER_ROLES: readonly SignerRole[] = ['signer1', 'signer2', 'signer3', 'signer4'];\n\n/** The part of a document the host's `resolveRoles` callback is called with. */\nexport interface DocumentRolesTarget {\n id: string;\n title: string;\n fileName: string | null;\n}\n\n/**\n * Turns the signer section on (`documentSigners` of `ui-send-message-panel`). `null` / not passed = the panel is the plain single-recipient\n * panel and nothing about signers is shown or sent.\n */\nexport interface DocumentSignersConfig {\n /** Largest number of customer signers the panel accepts (roles above it are ignored). Default 4. */\n maxSigners?: number;\n /** Labels per role. Default `Signer 1` .. `Signer 4`. */\n roleLabels?: Partial<Record<SignerRole, string>>;\n /** May one person fill two roles? Default false. */\n allowSameContactInRoles?: boolean;\n /** Must every further signer have an email address (the chain emails each signer)? Default true; `false` only lets Send through with a warning-free row (the server then decides). */\n requireSignerEmail?: boolean;\n /** Name shown in the read-only countersign line (\"Countersigned by <name> after all signers.\"). Default `you`. */\n countersignerName?: string;\n /**\n * Reads the customer roles a document defines when its `GET /documents` row has no `signerRoles` key (an app that keeps its\n * placements elsewhere). Called once per chosen document, and again on \"Retry\". A failure blocks Send and offers a retry. An empty\n * list = the document has no signature fields (informational).\n */\n resolveRoles?: (doc: DocumentRolesTarget) => Observable<SignerRole[]>;\n}\n\n/** One entry of `documents.signers` (present only for a document with MORE THAN ONE customer signer, always complete). */\nexport interface SendSigner {\n role: SignerRole;\n contactId: string;\n name: string;\n email: string | null;\n /** 1..N, equal to the role number (strictly sequential). */\n order: number;\n}\n\n/** Values the host has for THIS send, keyed by the keys it declares in `documentContextTypes` (e.g. `{ propertyId: '…' }`). */\nexport type SendContext = Record<string, string>;\n\n/** One context key the app's backend accepts. */\nexport interface ContextTypeDef { key: string; label: string }\n\n/** What `documentMergeCheck` sends (default route `POST <baseUrl>/documents/merge-check`). */\nexport interface MergeCheckRequest {\n documentIds: string[];\n customerId?: string;\n signers?: SendSigner[];\n context?: SendContext;\n}\n\n/** One data field that would be left empty. */\nexport interface MergeCheckEmptyField {\n key: string;\n label: string;\n page?: number;\n /** `NO_CONTEXT` | `NO_DATA` | `UNKNOWN_PATH` (other values are shown generically). */\n reason?: string;\n /** Optional plain-language reason from the server (\"no property selected\"); wins over the generic text of `reason`. */\n detail?: string;\n}\n\nexport interface MergeCheckResult {\n documents: { documentId: string; title?: string; empty: MergeCheckEmptyField[] }[];\n}\n\n/** A host function that runs the merge check itself (instead of the default route). */\nexport type MergeCheckFn = (req: MergeCheckRequest) => Observable<MergeCheckResult>;\n\n/** The structural part of a chosen document the helpers below read. */\nexport interface SignerDocLike {\n id: string;\n title: string;\n requiresCountersign?: boolean;\n /** The row says whether the countersign box is placed on the document. `false` (with `requiresCountersign`) offers the \"Place your countersignature box\" link; `undefined` = unknown = no link. */\n hasCountersignPlacement?: boolean;\n signerRoles?: SignerRole[];\n hasMapping?: boolean;\n hasDataFields?: boolean;\n}\n\n/** Where the roles of a document without a `signerRoles` key stand when the host resolves them. */\nexport interface RoleState { status: 'loading' | 'ready' | 'error'; roles: SignerRole[] }\n\n/** What the signer logic concluded for the chosen documents (only on the value when `documentSigners` is on). */\nexport interface DocumentSignerInfo {\n /** `ready` once every document's roles are known; `loading` / `error` block Send. */\n status: 'loading' | 'ready' | 'error';\n /** Every chosen document has no signature fields: the send is informational (view-only). */\n informational: boolean;\n /** Customer roles of the ONE chosen document (always starting at signer1, no gaps). Empty for several documents / informational. */\n roles: SignerRole[];\n /** Exactly one document, with more than one customer signer. */\n multiSigner: boolean;\n /** A document that is sent for signing requires the sender's countersignature. */\n countersign: boolean;\n /**\n * Only present (and non-empty) when a signable chosen document requires the countersignature and its row says the countersign box is NOT\n * placed (`hasCountersignPlacement === false`): the documents the \"Place your countersignature box\" link is offered for.\n */\n countersignUnplaced?: { id: string; title: string }[];\n /** Several documents are chosen and one of them has several signers (it still goes to the one recipient). */\n multiDocumentNote: boolean;\n /** Under the document chip: what the mapping covers ('' when unknown). */\n fieldsLine: string;\n}\n\nexport const SIGNER_ROLE_LABEL_DEFAULT = (role: SignerRole): string => `Signer ${roleNumber(role)}`;\n\nexport function roleNumber(role: SignerRole): number { return Number(role.slice(6)); }\n\n/** The label of a role: the host's `roleLabels` entry, else `Signer N`. */\nexport function signerRoleLabel(role: SignerRole, cfg?: DocumentSignersConfig | null): string {\n const custom = cfg?.roleLabels?.[role]?.trim();\n return custom || SIGNER_ROLE_LABEL_DEFAULT(role);\n}\n\n/** Valid roles only (`signer1..N`, N ≤ maxSigners), unique, sorted. Anything else is dropped. */\nexport function normalizeRoles(raw: unknown, maxSigners = 4): SignerRole[] {\n if (!Array.isArray(raw)) return [];\n const max = Math.min(Math.max(Math.floor(maxSigners) || 4, 1), SIGNER_ROLES.length);\n const set = new Set<SignerRole>();\n for (const r of raw) {\n if (typeof r === 'string' && (SIGNER_ROLES as readonly string[]).includes(r) && roleNumber(r as SignerRole) <= max) set.add(r as SignerRole);\n }\n return [...set].sort((a, b) => roleNumber(a) - roleNumber(b));\n}\n\n/**\n * The customer roles a send needs from a document's saved roles: signer1 is always the recipient, and the roles run without gaps up to\n * the highest saved one (a partial list is never sent). `[]` stays `[]` (no signature fields).\n */\nexport function effectiveRoles(saved: SignerRole[]): SignerRole[] {\n if (saved.length === 0) return [];\n const highest = Math.max(...saved.map(roleNumber));\n return SIGNER_ROLES.slice(0, highest);\n}\n\n/** The roles of one document, or null while they are not known yet. A document nobody could describe (older backend) counts as one signer. */\nfunction rolesOf(doc: SignerDocLike, cfg: DocumentSignersConfig, states: Record<string, RoleState>): RoleState {\n if (doc.signerRoles !== undefined) return { status: 'ready', roles: normalizeRoles(doc.signerRoles, cfg.maxSigners) };\n if (cfg.resolveRoles) {\n const state = states[doc.id];\n return state ? { ...state, roles: normalizeRoles(state.roles, cfg.maxSigners) } : { status: 'loading', roles: [] };\n }\n return { status: 'ready', roles: ['signer1'] };\n}\n\n/** True when the document has mapped data fields (the pre-send \"fields that will be empty\" check applies to it only). */\nexport function hasMappedDataFields(doc: SignerDocLike): boolean {\n return doc.hasDataFields !== undefined ? doc.hasDataFields === true : doc.hasMapping === true;\n}\n\n/** What the signer logic concludes for the chosen documents. `null` = `documentSigners` is off or nothing is chosen. */\nexport function describeDocumentSigners(\n docs: SignerDocLike[], states: Record<string, RoleState>, cfg: DocumentSignersConfig | null | undefined,\n): DocumentSignerInfo | null {\n if (!cfg || docs.length === 0) return null;\n const per = docs.map(d => ({ doc: d, state: rolesOf(d, cfg, states) }));\n const base: DocumentSignerInfo = {\n status: 'ready', informational: false, roles: [], multiSigner: false, countersign: false, multiDocumentNote: false, fieldsLine: '',\n };\n if (per.some(p => p.state.status === 'error')) return { ...base, status: 'error' };\n if (per.some(p => p.state.status === 'loading')) return { ...base, status: 'loading' };\n const signable = per.filter(p => p.state.roles.length > 0);\n const informational = signable.length === 0;\n const info: DocumentSignerInfo = { ...base, informational, countersign: signable.some(p => p.doc.requiresCountersign === true) };\n const unplaced = signable\n .filter(p => p.doc.requiresCountersign === true && p.doc.hasCountersignPlacement === false)\n .map(p => ({ id: p.doc.id, title: p.doc.title }));\n if (unplaced.length > 0) info.countersignUnplaced = unplaced;\n if (docs.length === 1) {\n const roles = effectiveRoles(per[0].state.roles);\n info.roles = roles;\n info.multiSigner = roles.length > 1;\n const d = per[0].doc;\n if (roles.length > 1) info.fieldsLine = `Fields placed for ${roles.length} signers`;\n else if (informational && d.hasMapping !== undefined) info.fieldsLine = 'No signature fields placed. Sent for viewing only.';\n } else {\n info.multiDocumentNote = signable.some(p => effectiveRoles(p.state.roles).length > 1);\n }\n return info;\n}\n\n/** The read-only countersign line ('' = not shown). */\nexport function countersignLine(info: DocumentSignerInfo | null, cfg: DocumentSignersConfig | null | undefined, viaDocuSign: boolean): string {\n if (!info || !cfg || info.informational || !info.countersign) return '';\n if (viaDocuSign) return 'You countersign after the client signs.';\n return `Countersigned by ${cfg.countersignerName?.trim() || 'you'} after all signers.`;\n}\n\nexport interface SignerPick { id: string; name: string; email?: string | null }\n\n/** The complete `signers` list (recipient first), or null while any role has no contact. */\nexport function buildSigners(roles: SignerRole[], recipient: SignerPick, picks: Partial<Record<SignerRole, SignerPick | null>>): SendSigner[] | null {\n if (roles.length < 2) return null;\n const out: SendSigner[] = [];\n for (const role of roles) {\n const p = role === 'signer1' ? recipient : picks[role];\n if (!p) return null;\n out.push({ role, contactId: p.id, name: p.name, email: p.email ? p.email : null, order: roleNumber(role) });\n }\n return out;\n}\n\n/**\n * The first problem per role (`''` = fine) in signing order: empty → `<Role label> is required.`, no email → `<Name> has no email address.`,\n * the same person twice (unless allowed) → `<Name> is already <Earlier role label>.` on the later row.\n */\nexport function signerProblems(\n roles: SignerRole[], recipient: SignerPick, picks: Partial<Record<SignerRole, SignerPick | null>>, cfg: DocumentSignersConfig | null | undefined,\n): Partial<Record<SignerRole, string>> {\n const out: Partial<Record<SignerRole, string>> = {};\n const seen = new Map<string, SignerRole>();\n for (const role of roles) {\n const p = role === 'signer1' ? recipient : picks[role];\n if (!p) { out[role] = `${signerRoleLabel(role, cfg)} is required.`; continue; }\n if (cfg?.requireSignerEmail !== false && !(p.email ?? '').trim()) { out[role] = `${p.name} has no email address.`; }\n const earlier = seen.get(p.id);\n if (earlier && !cfg?.allowSameContactInRoles && !out[role]) out[role] = `${p.name} is already ${signerRoleLabel(earlier, cfg)}.`;\n if (!earlier) seen.set(p.id, role);\n }\n return out;\n}\n\n/** Only the declared context keys, with non-blank values; `undefined` when nothing is left. */\nexport function forwardContext(ctx: SendContext | null | undefined, types: readonly ContextTypeDef[]): { context?: SendContext; dropped: string[] } {\n const dropped: string[] = [];\n if (!ctx) return { dropped };\n const allowed = new Set(types.map(t => t.key));\n const out: SendContext = {};\n for (const [k, v] of Object.entries(ctx)) {\n if (!allowed.has(k)) { dropped.push(k); continue; }\n if (typeof v === 'string' && v.trim() !== '') out[k] = v;\n }\n return Object.keys(out).length ? { context: out, dropped } : { dropped };\n}\n\n/** One merge-check field as text: `Property Address (no property selected)`. */\nexport function mergeFieldText(f: MergeCheckEmptyField): string {\n const reason = f.detail?.trim()\n || (f.reason === 'NO_CONTEXT' ? 'not available for this send'\n : f.reason === 'NO_DATA' ? 'no value on record'\n : f.reason === 'UNKNOWN_PATH' ? 'unknown field' : '');\n return reason ? `${f.label} (${reason})` : f.label;\n}\n\n/** The warning lines of a merge-check answer: one line for one document, `<title>: …` per document for several. Nothing when nothing is empty. */\nexport function mergeWarningLines(res: MergeCheckResult | null | undefined, docTitles: Record<string, string>): string[] {\n const docs = (res?.documents ?? []).filter(d => (d.empty ?? []).length > 0);\n const prefixed = docs.length > 1 || (res?.documents?.length ?? 0) > 1;\n return docs.map(d => {\n const list = d.empty.map(mergeFieldText).join(', ');\n const title = d.title || docTitles[d.documentId] || '';\n return `${prefixed && title ? `${title}: ` : ''}These fields will be left empty: ${list}.`;\n });\n}\n\n/** The accessible name of the \"Place your countersignature box\" link of a document. */\nexport function countersignPlaceLabel(title: string): string { return `Place your countersignature box on ${title}`; }\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, take } from 'rxjs';\nimport type { Observable, Subscription } from 'rxjs';\nimport { FORGEROUTE_CONFIG, ComboboxComponent, type ComboboxOption, ChipComponent, SelectComponent, type SelectOption, ErrorComponent, ButtonComponent, CheckboxComponent, FormSectionComponent, FormRowComponent, HintComponent, RadioGroupComponent, type RadioOption, TextInputComponent, UiFieldValidity } from '@forgeroute/ui';\nimport { describeDocumentSigners, normalizeRoles } from './document-signers';\nimport type { DocumentSignerInfo, DocumentSignersConfig, RoleState, SendContext, SignerRole } from './document-signers';\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/** Who sends a signature request: the app's own e-signature (`NATIVE`) or DocuSign. */\nexport type DocumentSignatureProvider = 'NATIVE' | 'DOCUSIGN';\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 /** Only present (and true) when the row says the document needs the sender's countersignature (`requiresCountersign` on the `GET /documents` row): then a DocuSign send offers \"Require my countersignature after client signs\". */\n requiresCountersign?: boolean;\n /** Only with `documentSigners` and `requiresCountersign`: the row says whether the countersign box is placed. `false` shows the \"Place your countersignature box\" link after the countersign line (`mapFieldsClick`); absent = unknown = no link. */\n hasCountersignPlacement?: boolean;\n /** Only with `documentSigners`: the document's customer roles from the `GET /documents` row (`signerRoles`). `[]` = no signature fields (informational). */\n signerRoles?: SignerRole[];\n /** The row says the document has saved placements of any kind (drives the \"Fields placed for N signers\" line and the merge check). */\n hasMapping?: boolean;\n /** The row says the document has mapped DATA fields (the pre-send merge check applies to it); when absent, `hasMapping` stands in. */\n hasDataFields?: boolean;\n /** Only for an envelope's document (`DocumentSeed.status`). */\n status?: string;\n}\n\n/** A document the form starts with (`initialDocuments`). `hasFile` defaults to true. */\nexport interface DocumentSeed {\n id: string;\n title: string;\n fileName?: string | null;\n hasFile?: boolean;\n requiresCountersign?: boolean;\n hasCountersignPlacement?: boolean;\n signerRoles?: SignerRole[];\n hasMapping?: boolean;\n hasDataFields?: boolean;\n /** An envelope's document that is not `ACTIVE` (e.g. `ARCHIVED`) blocks the send: \"<title> is archived, so this envelope can't be sent. …\". */\n status?: string;\n}\n\n/**\n * An existing envelope the panel sends (`documentEnvelope`): its documents are fixed (no search, no remove) and the request is sent, not\n * created. Give `documents` when the host has read them already, or `loadDocuments` to let the panel read them (it shows the wait, and a\n * failure blocks Send with a Retry button). With neither, the envelope has no documents and cannot be sent.\n */\nexport interface DocumentEnvelopeTarget {\n id: string;\n /** Shown in the subject suggestion (\"Signature required: <name>\") and in the success toast. */\n name: string;\n documents?: DocumentSeed[];\n loadDocuments?: () => Observable<DocumentSeed[]>;\n}\n\n/** The document a host's `documentPreview` handler is called with. */\nexport interface DocumentPreviewTarget {\n id: string;\n title: string;\n fileName: string | null;\n}\n\n/** The recipient the panel currently holds (as the panel's `contactId` / `contactName` / `contactEmail` / `contactPhone` inputs). */\nexport interface DocumentPreviewContact {\n id: string;\n name: string;\n email: string;\n phone: string;\n}\n\n/**\n * Second argument of `documentPreview`: who the document is being sent to, so the host can preview it filled for that recipient.\n * `contact` is `null` when the panel has no recipient (all four contact inputs blank); `sendContext` is the panel's `sendContext` input\n * as given (not filtered by `documentContextTypes`) or `null`.\n */\nexport interface DocumentPreviewContext {\n contact: DocumentPreviewContact | null;\n sendContext: SendContext | null;\n}\n\n/**\n * Opens a document for preview (the host knows its own route / viewer). Called synchronously inside the click. The second argument\n * is new and optional to use: a handler that only declares `doc` keeps working.\n */\nexport type DocumentPreviewHandler = (doc: DocumentPreviewTarget, context: DocumentPreviewContext) => void;\n\n/**\n * Turns on the \"Expiry and Reminders\" settings of the document section and sets their starting values (the keys you leave out start at\n * the defaults below). `null` / not passed = the settings are not shown and nothing about them is sent.\n */\nexport interface DocumentSigningOptions {\n /** Days until an unsigned request expires: whole number 1-90 (default 7). */\n expiryDays?: number;\n /** Days between reminders: whole number 1-30 (default 2). */\n reminderIntervalDays?: number;\n /** How many reminders at most: whole number 0-10 (default 3). */\n maxReminders?: number;\n}\n\n/** Defaults and limits of the signing settings (the same limits the servers enforce). */\nexport const DEFAULT_SIGNING_OPTIONS = { expiryDays: 7, reminderIntervalDays: 2, maxReminders: 3 } as const;\nexport const SIGNING_LIMITS = {\n expiryDays: { min: 1, max: 90, label: 'Expiry (days)' },\n reminderIntervalDays: { min: 1, max: 30, label: 'Reminder Interval (days)' },\n maxReminders: { min: 0, max: 10, label: 'Max Reminders' },\n} as const;\n\nexport const DOCUMENT_PROVIDER_OPTIONS: RadioOption[] = [\n { value: 'NATIVE', label: 'Native E-Sign' },\n { value: 'DOCUSIGN', label: 'Send via DocuSign' },\n];\n\n/** A whole number in the string, or null (blank, decimals, signs and exponents are not whole numbers here). */\nfunction wholeNumber(raw: string): number | null {\n const t = (raw ?? '').trim();\n return /^\\d+$/.test(t) ? Number(t) : null;\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 /** Only when the signing settings are shown (host `signing` input), valid and the request is a native e-signature one. */\n expiryDays?: number;\n reminderIntervalDays?: number;\n maxReminders?: number;\n /** Only when the provider choice is offered (`docuSignConnected` and exactly one document on the email channel). */\n provider?: DocumentSignatureProvider;\n /** Only with `provider: 'DOCUSIGN'`. */\n docuSign?: { countersign: boolean };\n /** Only when `signers` (the host's `documentSigners`) is on and a document is chosen: what the signer logic concluded. Never part of the payload. */\n signerInfo?: DocumentSignerInfo;\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/** Purpose labels when nothing is signed (every chosen document has no signature fields): the recipient gets a view link. */\nconst INFORMATIONAL_PURPOSE_LABELS: Record<string, string> = {\n ESIGN: 'Send a view link',\n BOTH: 'View link 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 * Optional parts, all off by default (hosts reach them through the same-named inputs of `ui-send-message-panel`):\n * - `signing` — \"Expiry and Reminders\" (expiry days, reminder interval, max reminders) for native e-signature requests;\n * - `docuSignConnected` — \"Native E-Sign / Send via DocuSign\" choice (email channel, exactly one document);\n * - `documentPreview` — \"Preview Document\" button(s) that call the host's handler.\n * - `signers` — the signer logic of the panel's `documentSigners` (a document's customer roles, informational documents, the\n * one-signer DocuSign rule, the document's own countersign setting); the value then carries `signerInfo`;\n * - `envelope` — an existing envelope: its documents are the fixed list (given, or read through `loadDocuments`).\n *\n * The signing settings are ONE collapsed summary line (\"Signing options: expires in 7 days, ...\") with a Change / Hide button.\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, when the purpose is \"Send as attachment\" and a chosen document has no stored file, or when a shown signing\n * setting is blank / out of range. 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: [\n FormsModule, ComboboxComponent, ChipComponent, SelectComponent, ErrorComponent, ButtonComponent, CheckboxComponent,\n FormSectionComponent, FormRowComponent, HintComponent, RadioGroupComponent, TextInputComponent,\n ],\n providers: [{ provide: UiFieldValidity, useExisting: forwardRef(() => DocumentSendOptionsComponent) }],\n template: `\n <div class=\"dso\" role=\"group\" [attr.aria-label]=\"label()\">\n @if (available()) {\n @if (locked()) {\n <p class=\"dso-locked-label\" data-testid=\"locked-documents-label\">Included documents</p>\n @if (envelopeStatus() === 'loading') {\n <div role=\"status\"><ui-hint data-testid=\"envelope-loading\">{{ envelopeReadMessage() }}</ui-hint></div>\n } @else if (envelopeStatus() === 'error') {\n <div class=\"dso-retry\">\n <ui-error errorId=\"dso-envelope-error\" [message]=\"envelopeReadMessage()\" />\n <ui-button variant=\"secondary\" size=\"sm\" (click)=\"retryEnvelope()\">Retry</ui-button>\n </div>\n }\n } @else {\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\n @if (selected().length > 0) {\n <!-- One row per document: the chip (or the plain title when the list is fixed) and, beside it, its small Preview button. -->\n <div class=\"dso-docs\" role=\"group\" aria-label=\"Chosen documents\">\n @for (d of selected(); track d.id) {\n <div class=\"dso-doc\">\n @if (locked()) {\n <!-- An envelope's documents are fixed: plain text, not a chip (a chip without a remove button would read as a toggle). -->\n <span class=\"dso-doc-title\">{{ d.title }}</span>\n } @else {\n <ui-chip class=\"dso-doc-chip\" [label]=\"d.title\" size=\"sm\" [dismissible]=\"true\" (dismiss)=\"remove(d.id)\" />\n }\n @if (documentPreview()) {\n <ui-button\n variant=\"secondary\" size=\"sm\"\n [ariaLabel]=\"selected().length === 1 ? undefined : 'Preview ' + d.title\"\n (click)=\"preview(d)\">{{ selected().length === 1 ? 'Preview Document' : 'Preview' }}</ui-button>\n }\n </div>\n }\n </div>\n @if (signerInfo()?.fieldsLine; as line) {\n <p class=\"dso-fields\" data-testid=\"fields-line\">{{ line }}</p>\n }\n }\n\n @if (signerInfo()?.status === 'loading') {\n <div role=\"status\"><ui-hint data-testid=\"signers-loading\">{{ signerReadMessage() }}</ui-hint></div>\n } @else if (signerInfo()?.status === 'error') {\n <div class=\"dso-retry\">\n <ui-error errorId=\"dso-roles-error\" [message]=\"signerReadMessage()\" />\n <ui-button variant=\"secondary\" size=\"sm\" (click)=\"retryRoles()\">Retry</ui-button>\n </div>\n }\n\n @if (providerChoiceShown()) {\n <ui-radio-group\n label=\"Signature provider\"\n orientation=\"horizontal\"\n [options]=\"providerOptions\"\n [ngModel]=\"provider()\"\n (ngModelChange)=\"setProvider($event)\" />\n }\n\n @if (provider() === 'DOCUSIGN') {\n <ui-hint>DocuSign emails the document to the recipient. The Subject and Message above are used for the DocuSign email.</ui-hint>\n @if (showCountersign()) {\n <ui-checkbox\n label=\"Require my countersignature after client signs\"\n [ngModel]=\"countersignChoice()\"\n (ngModelChange)=\"countersignChoice.set(!!$event)\" />\n }\n } @else 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() ? documentError() : ''\" />\n\n @if (signingVisible()) {\n <!-- Collapsed to one summary line: the defaults are valid, so most senders never open it. A broken value opens it by itself. -->\n <div class=\"dso-signing\" data-testid=\"signing-options\">\n <div class=\"dso-signing-line\">\n <span class=\"dso-signing-summary\" data-testid=\"signing-summary\">{{ signingSummary() }}</span>\n <ui-button\n variant=\"ghost\" size=\"xs\"\n [ariaExpanded]=\"signingExpanded()\"\n [ariaLabel]=\"signingExpanded() ? 'Hide signing options' : 'Change signing options'\"\n (click)=\"toggleSigning()\">{{ signingExpanded() ? 'Hide' : 'Change' }}</ui-button>\n </div>\n @if (signingExpanded()) {\n <ui-form-section>\n <!-- One column: three labelled number fields do not fit side by side in a 480px panel or on a phone. -->\n <ui-form-row [cols]=\"1\">\n <ui-text-input\n label=\"Expiry (days)\" type=\"number\" [required]=\"true\" [min]=\"1\" [max]=\"90\" [maxDecimals]=\"0\"\n [showErrors]=\"showErrors()\" [ngModel]=\"expiryDays()\" (ngModelChange)=\"expiryDays.set(asText($event))\" />\n <ui-text-input\n label=\"Reminder Interval (days)\" type=\"number\" [required]=\"true\" [min]=\"1\" [max]=\"30\" [maxDecimals]=\"0\"\n [showErrors]=\"showErrors()\" [ngModel]=\"reminderIntervalDays()\" (ngModelChange)=\"reminderIntervalDays.set(asText($event))\" />\n <ui-text-input\n label=\"Max Reminders\" type=\"number\" [required]=\"true\" [min]=\"0\" [max]=\"10\" [maxDecimals]=\"0\"\n [showErrors]=\"showErrors()\" [ngModel]=\"maxReminders()\" (ngModelChange)=\"maxReminders.set(asText($event))\" />\n </ui-form-row>\n </ui-form-section>\n }\n </div>\n }\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-docs { display: flex; flex-direction: column; gap: 8px; min-width: 0; }\n .dso-doc { display: flex; flex-wrap: wrap; align-items: center; gap: 6px 8px; min-width: 0; }\n .dso-doc-chip { max-width: 100%; min-width: 0; }\n .dso-doc-title { min-width: 0; font-size: 0.875rem; color: #1e293b; overflow-wrap: anywhere; }\n .dso-locked-label { margin: 0; font-size: 0.8125rem; font-weight: 600; color: #334155; }\n .dso-fields { margin: 0; font-size: 0.75rem; color: #64748b; }\n .dso-retry { display: flex; flex-direction: column; align-items: flex-start; gap: 6px; }\n .dso-signing { display: flex; flex-direction: column; gap: 8px; min-width: 0; }\n .dso-signing-line { display: flex; flex-wrap: wrap; align-items: baseline; gap: 4px 10px; min-width: 0; }\n .dso-signing-summary { font-size: 0.8125rem; color: #475569; min-width: 0; overflow-wrap: anywhere; }\n :host ::ng-deep .dso-doc-chip .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<DocumentSeed[]>([]);\n /**\n * Shows the \"Expiry and Reminders\" settings (Expiry 1-90 days, Reminder Interval 1-30 days, Max Reminders 0-10, all required whole\n * numbers; starting at 7 / 2 / 3 unless this object says otherwise) while at least one document is chosen and the request is a native\n * e-signature one (purpose e-signature or both; always on the text channel). `null` (default) = not shown, nothing sent. Read once.\n */\n readonly signing = input<DocumentSigningOptions | null | undefined>(null);\n /**\n * The host knows whether DocuSign is connected (the library never calls `/docusign/status`). `true` offers \"Native E-Sign / Send via\n * DocuSign\" on the email channel while exactly one document is chosen; `false` (default) = no choice, native only.\n */\n readonly docuSignConnected = input(false);\n /**\n * \"Preview Document\": called with the document when its button is clicked (one button for one document, one per document for several).\n * `null` (default) hides the buttons. The host opens its own viewer; to open a new tab do it synchronously inside this call\n * (`window.open` after an await is blocked as a popup in Safari).\n */\n readonly documentPreview = input<DocumentPreviewHandler | null>(null);\n /** The recipient passed to `documentPreview` as `context.contact` (the send panel sets it from its contact inputs). `null` = none. */\n readonly previewContact = input<DocumentPreviewContact | null>(null);\n /** The send context passed to `documentPreview` as `context.sendContext` (the send panel sets it from its `sendContext`). `null` = none. */\n readonly previewSendContext = input<SendContext | null>(null);\n /**\n * Turns on the signer logic (`documentSigners` of the panel): a document's customer roles (from its row's `signerRoles`, or the config's\n * `resolveRoles`), informational documents (no signature fields), the one-signer DocuSign rule and the read-only countersign rule.\n * The value then carries `signerInfo`. `null` (default) = none of it; nothing changes.\n */\n readonly signers = input<DocumentSignersConfig | null | undefined>(null);\n /**\n * An existing envelope (`documentEnvelope` of the panel): its documents are the list, fixed (no search, no remove buttons). Read when it\n * is set; `null` (default) = the sender chooses the documents.\n */\n readonly envelope = input<DocumentEnvelopeTarget | null>(null);\n /** The document list is fixed: an existing envelope's. */\n readonly locked = computed(() => !!this.envelope());\n protected readonly purposeOptions = computed(() => {\n const allowed = this.purposes();\n const base = DOCUMENT_PURPOSE_OPTIONS.filter(o => allowed.includes(o.value as DocumentSendPurpose));\n const opts = base.length ? base : DOCUMENT_PURPOSE_OPTIONS;\n // Nothing is signed in an informational send: the recipient gets a view link.\n return this.signerInfo()?.informational ? opts.map(o => ({ ...o, label: INFORMATIONAL_PURPOSE_LABELS[o.value] ?? o.label })) : opts;\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 /** What the sender picked in the provider choice (only effective while the choice is offered). */\n readonly providerChoice = signal<DocumentSignatureProvider>('NATIVE');\n readonly countersignChoice = signal(false);\n /** Roles of documents whose row had no `signerRoles` and that the host resolves (`signers().resolveRoles`). */\n readonly roleStates = signal<Record<string, RoleState>>({});\n /** The envelope's documents: `loading` / `error` while the panel reads them (`loadDocuments`), `ready` once known. */\n readonly envelopeStatus = signal<'idle' | 'loading' | 'ready' | 'error'>('idle');\n private envelopeSub: Subscription | undefined;\n private readonly roleSubs = new Map<string, Subscription>();\n /** The sender opened the collapsed \"Signing options\" line. */\n readonly signingOpen = signal(false);\n readonly expiryDays = signal(String(DEFAULT_SIGNING_OPTIONS.expiryDays));\n readonly reminderIntervalDays = signal(String(DEFAULT_SIGNING_OPTIONS.reminderIntervalDays));\n readonly maxReminders = signal(String(DEFAULT_SIGNING_OPTIONS.maxReminders));\n protected readonly providerOptions = DOCUMENT_PROVIDER_OPTIONS;\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 /** True when the sender can choose DocuSign: the host says it is connected, email channel, exactly one document (DocuSign sends one), and e-signature is a supported purpose. */\n readonly providerChoiceShown = computed(() => {\n if (!(this.docuSignConnected() && this.channel() === 'email' && this.selected().length === 1 && this.purposes().includes('ESIGN'))) return false;\n // With the signer logic on, the choice appears only where DocuSign can apply: a document that is signed, by one customer signer.\n const info = this.signerInfo();\n return !this.signers() || (info?.status === 'ready' && !info.informational && !info.multiSigner);\n });\n /** The provider that is actually used: DocuSign only while the choice is offered. */\n readonly provider = computed<DocumentSignatureProvider>(() => this.providerChoiceShown() && this.providerChoice() === 'DOCUSIGN' ? 'DOCUSIGN' : 'NATIVE');\n /** The countersignature checkbox: a DocuSign send of a document flagged `requiresCountersign`. */\n readonly showCountersign = computed(() => !this.signers() && this.provider() === 'DOCUSIGN' && this.selected()[0]?.requiresCountersign === true);\n /** The checkbox (no `signers`) or, with `signers` on, the document's own setting: the document says whether it is countersigned. */\n readonly countersign = computed(() => this.signers()\n ? this.provider() === 'DOCUSIGN' && this.selected()[0]?.requiresCountersign === true\n : this.showCountersign() && this.countersignChoice());\n /** The signing settings are shown: turned on by the host, a document is chosen, and it is a native e-signature request (not attachment only, not DocuSign). */\n readonly signingVisible = computed(() =>\n this.signing() != null && this.available() && this.selected().length > 0 && this.provider() === 'NATIVE'\n && (this.signerInfo() === null || (this.signerInfo()!.status === 'ready' && !this.signerInfo()!.informational))\n && (this.channel() === 'text' || this.purpose() !== 'ATTACH'));\n /** What the signer logic concluded for the chosen documents (`null` when `signers` is off or nothing is chosen). */\n readonly signerInfo = computed<DocumentSignerInfo | null>(() => describeDocumentSigners(this.selected(), this.roleStates(), this.signers()));\n /** Why an envelope cannot be sent ('' = it can): nothing to send, or a document that is no longer active. Shown under the section. */\n readonly envelopeProblem = computed(() => {\n if (!this.envelope() || this.envelopeStatus() !== 'ready') return '';\n const docs = this.selected();\n if (docs.length === 0) return 'This envelope has no documents. Add a document first.';\n const inactive = docs.find(d => d.status !== undefined && d.status !== 'ACTIVE');\n return inactive ? `\"${inactive.title}\" is ${inactive.status!.toLowerCase()}, so this envelope can't be sent. Restore it or remove it from the envelope first.` : '';\n });\n /** Why Send waits for the envelope's documents ('' when they are known). */\n readonly envelopeReadMessage = computed(() => {\n const status = this.envelopeStatus();\n return status === 'loading' ? 'Reading the envelope\\'s documents…'\n : status === 'error' ? 'The documents of this envelope couldn\\'t be read, so it can\\'t be sent yet.' : '';\n });\n /** Why Send waits for the document's signers ('' when they are known). */\n readonly signerReadMessage = computed(() => {\n const status = this.signerInfo()?.status;\n return status === 'loading' ? 'Reading the document\\'s signers…'\n : status === 'error' ? 'The signers of this document couldn\\'t be read, so it can\\'t be sent yet.' : '';\n });\n /** The collapsed line: \"Signing options: expires in 7 days, reminders every 2 days, up to 3\". */\n readonly signingSummary = computed(() => {\n const day = (raw: string) => `${raw} ${raw.trim() === '1' ? 'day' : 'days'}`;\n const max = this.maxReminders().trim();\n const reminders = max === '0' ? 'no reminders' : `reminders every ${day(this.reminderIntervalDays().trim())}, up to ${max}`;\n return `Signing options: expires in ${day(this.expiryDays().trim())}, ${reminders}`;\n });\n /** Open when the sender opened it, or while a value is broken (the error must be visible). */\n readonly signingExpanded = computed(() => this.signingOpen() || this.signingError() !== '');\n\n /** The purpose that is actually sent: the text channel and DocuSign can only do e-signature. */\n readonly purpose = computed<DocumentSendPurpose>(() => {\n if (this.channel() === 'text' || this.provider() === 'DOCUSIGN') 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 const v: DocumentSendValue = {\n documentIds: this.selected().map(d => d.id),\n documents: this.selected(),\n purpose: this.purpose(),\n delivery: this.delivery(),\n };\n if (this.providerChoiceShown()) {\n v.provider = this.provider();\n if (this.provider() === 'DOCUSIGN') v.docuSign = { countersign: this.countersign() };\n }\n if (this.signingVisible() && this.signingError() === '') {\n v.expiryDays = Number(this.expiryDays());\n v.reminderIntervalDays = Number(this.reminderIntervalDays());\n v.maxReminders = Number(this.maxReminders());\n }\n const info = this.signerInfo();\n if (info) v.signerInfo = info;\n return v;\n });\n\n /** The first broken signing setting ('' when fine or not shown) — the inputs show their own messages. */\n readonly signingError = computed(() => {\n if (!this.signingVisible()) return '';\n const fields = [\n ['expiryDays', this.expiryDays()], ['reminderIntervalDays', this.reminderIntervalDays()], ['maxReminders', this.maxReminders()],\n ] as const;\n for (const [key, raw] of fields) {\n const { min, max, label } = SIGNING_LIMITS[key];\n const n = wholeNumber(raw);\n if ((raw ?? '').trim() === '') return `${label} is required.`;\n if (n === null || n < min || n > max) return `${label} must be a whole number from ${min} to ${max}.`;\n }\n return '';\n });\n\n /** The first broken document rule ('' when fine) — shown under the section. */\n readonly documentError = 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 const env = this.envelopeProblem();\n if (env) return env;\n const info = this.signerInfo();\n // A signed document with several signers can only be emailed: the texted link goes to the first signer only.\n if (info?.multiSigner && this.channel() === 'text') return `This document has ${info.roles.length} signers. Send it by email.`;\n return '';\n });\n /** The first broken rule ('' when valid): the document rules, the wait for the signers, then the signing settings. */\n readonly errorMessage = computed(() => this.documentError() || this.envelopeReadMessage() || this.signerReadMessage() || this.signingError());\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 // Only an ACTIVE document can be sent (ARCHIVED and DEPRECATED are refused by the host); a result with no status at all is\n // offered as before. One that is already chosen is not offered twice.\n .filter(d => d && (d.status == null || d.status === 'ACTIVE') && !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 || this.envelope()) return;\n seeded = true;\n this.selected.set(initial.map(DocumentSendOptionsComponent.seedToOption));\n });\n });\n\n // An existing envelope: its documents are the list (given, or read by the loader).\n effect(() => {\n const env = this.envelope();\n untracked(() => this.loadEnvelope(env));\n });\n this.destroyRef.onDestroy(() => this.envelopeSub?.unsubscribe());\n\n // Roles of the chosen documents that the host resolves (one request per document; removed documents are forgotten).\n effect(() => {\n const cfg = this.signers();\n const docs = this.selected();\n untracked(() => this.syncRoleRequests(cfg, docs));\n });\n this.destroyRef.onDestroy(() => { this.roleSubs.forEach(sub => sub.unsubscribe()); this.roleSubs.clear(); });\n\n // The host's starting values for the signing settings (the keys it leaves out keep the defaults). Read once.\n let signingSeeded = false;\n effect(() => {\n const s = this.signing();\n untracked(() => {\n if (signingSeeded || !s) return;\n signingSeeded = true;\n this.seedSigning(s);\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 seedToOption(d: DocumentSeed): DocumentOption {\n return {\n id: String(d.id), title: d.title, fileName: d.fileName ?? null, hasFile: d.hasFile ?? true,\n ...(d.requiresCountersign ? { requiresCountersign: true } : {}),\n ...(typeof d.hasCountersignPlacement === 'boolean' ? { hasCountersignPlacement: d.hasCountersignPlacement } : {}),\n ...(Array.isArray(d.signerRoles) ? { signerRoles: normalizeRoles(d.signerRoles) } : {}),\n ...(typeof d.hasMapping === 'boolean' ? { hasMapping: d.hasMapping } : {}),\n ...(typeof d.hasDataFields === 'boolean' ? { hasDataFields: d.hasDataFields } : {}),\n ...(typeof d.status === 'string' ? { status: d.status } : {}),\n };\n }\n\n /** Sets the envelope's documents as the list; reads them first when the host gave a loader. */\n private loadEnvelope(env: DocumentEnvelopeTarget | null) {\n this.envelopeSub?.unsubscribe();\n this.envelopeSub = undefined;\n if (!env) {\n // Leaving an envelope: its fixed documents must not linger as removable chips of a normal send.\n if (this.envelopeStatus() !== 'idle') this.selected.set([]);\n this.envelopeStatus.set('idle');\n return;\n }\n const set = (docs: DocumentSeed[]) => { this.selected.set(docs.map(DocumentSendOptionsComponent.seedToOption)); this.envelopeStatus.set('ready'); };\n if (env.documents) { set(env.documents); return; }\n if (!env.loadDocuments) { set([]); return; }\n this.selected.set([]);\n this.envelopeStatus.set('loading');\n let answered = false;\n try {\n this.envelopeSub = env.loadDocuments().pipe(take(1)).subscribe({\n next: docs => { answered = true; set(docs ?? []); },\n error: () => this.envelopeStatus.set('error'),\n complete: () => { if (!answered) this.envelopeStatus.set('error'); },\n });\n } catch {\n this.envelopeStatus.set('error');\n }\n }\n\n /** \"Retry\" after the envelope's documents could not be read. */\n retryEnvelope() { this.loadEnvelope(this.envelope()); }\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 ...(d.requiresCountersign === true ? { requiresCountersign: true } : {}),\n ...(typeof d.hasCountersignPlacement === 'boolean' ? { hasCountersignPlacement: d.hasCountersignPlacement } : {}),\n ...(Array.isArray(d.signerRoles) ? { signerRoles: normalizeRoles(d.signerRoles) } : {}),\n ...(typeof d.hasMapping === 'boolean' ? { hasMapping: d.hasMapping } : {}),\n ...(typeof d.hasDataFields === 'boolean' ? { hasDataFields: d.hasDataFields } : {}),\n };\n }\n\n /** Asks the host for the roles of every chosen document that needs it; forgets documents that were removed. */\n private syncRoleRequests(cfg: DocumentSignersConfig | null | undefined, docs: DocumentOption[]) {\n const ids = new Set(docs.map(d => d.id));\n for (const [id, sub] of this.roleSubs) {\n if (!ids.has(id)) { sub.unsubscribe(); this.roleSubs.delete(id); }\n }\n const known = this.roleStates();\n if (Object.keys(known).some(id => !ids.has(id))) {\n this.roleStates.set(Object.fromEntries(Object.entries(known).filter(([id]) => ids.has(id))));\n }\n if (!cfg?.resolveRoles) return;\n for (const d of docs) {\n if (d.signerRoles === undefined && !this.roleStates()[d.id]) this.requestRoles(d, cfg);\n }\n }\n\n private requestRoles(d: DocumentOption, cfg: DocumentSignersConfig) {\n const setState = (state: RoleState) => this.roleStates.update(m => ({ ...m, [d.id]: state }));\n this.roleSubs.get(d.id)?.unsubscribe();\n setState({ status: 'loading', roles: [] });\n let answered = false;\n try {\n const sub = cfg.resolveRoles!({ id: d.id, title: d.title, fileName: d.fileName }).pipe(take(1)).subscribe({\n next: roles => { answered = true; setState({ status: 'ready', roles: normalizeRoles(roles, cfg.maxSigners) }); },\n error: () => setState({ status: 'error', roles: [] }),\n complete: () => { if (!answered) setState({ status: 'error', roles: [] }); },\n });\n this.roleSubs.set(d.id, sub);\n } catch {\n setState({ status: 'error', roles: [] });\n }\n }\n\n /** \"Retry\" after the roles of a document could not be read. */\n retryRoles() {\n const cfg = this.signers();\n if (!cfg?.resolveRoles) return;\n for (const d of this.selected()) {\n if (this.roleStates()[d.id]?.status === 'error') this.requestRoles(d, cfg);\n }\n }\n\n /** The Change / Hide button: it flips what the sender sees (a line kept open by an error stays open, but a later fix lets it close). */\n toggleSigning() { this.signingOpen.set(!this.signingExpanded()); }\n\n private seedSigning(s: DocumentSigningOptions | null | undefined) {\n this.expiryDays.set(String(s?.expiryDays ?? DEFAULT_SIGNING_OPTIONS.expiryDays));\n this.reminderIntervalDays.set(String(s?.reminderIntervalDays ?? DEFAULT_SIGNING_OPTIONS.reminderIntervalDays));\n this.maxReminders.set(String(s?.maxReminders ?? DEFAULT_SIGNING_OPTIONS.maxReminders));\n }\n\n /** `ui-text-input` hands the typed text back as a string (or a number from a programmatic write). */\n protected asText(v: unknown): string { return v == null ? '' : String(v); }\n\n setProvider(p: string) { if (p === 'NATIVE' || p === 'DOCUSIGN') this.providerChoice.set(p); }\n\n /** Calls the host's preview handler straight from the click (no await in between: Safari blocks a tab opened later). */\n preview(doc: DocumentOption) { this.documentPreview()?.({ id: doc.id, title: doc.title, fileName: doc.fileName }, { contact: this.previewContact(), sendContext: this.previewSendContext() }); }\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() {\n this.selected.set([]); this.purposeChoice.set('ESIGN'); this.options.set([]); this.searchFailed.set(false);\n this.providerChoice.set('NATIVE'); this.countersignChoice.set(false); this.signingOpen.set(false); this.seedSigning(this.signing());\n }\n}\n","import {\n ChangeDetectionStrategy, Component, computed, effect, forwardRef, input, output, signal, untracked,\n} from '@angular/core';\nimport { ContactPickerComponent, type ContactLookup, ButtonComponent, ErrorComponent, FormSectionComponent, HintComponent, UiFieldValidity } from '@forgeroute/ui';\nimport { buildSigners, countersignLine, countersignPlaceLabel, roleNumber, signerProblems, signerRoleLabel } from './document-signers';\nimport type { DocumentSignerInfo, DocumentSignersConfig, SendSigner, SignerPick, SignerRole } from './document-signers';\n\n/**\n * ui-document-signers — the signer section of a document send: one required contact per customer signer role of the chosen document\n * (Signer 1 is the recipient, shown read-only) in signing order, and the one read-only countersign line.\n *\n * Internal child of the message forms (`ui-send-message-panel` with `documentSigners`); hosts do not place it. Rows exist only for a\n * document with MORE THAN ONE customer signer; a single-signer document shows nothing here except the countersign line. It emits the\n * complete `signers` list (never a partial one: `null` until every role has a contact) and its validity (`UiFieldValidity`):\n * a missing contact is `<Role label> is required.`, a contact without an email `<Name> has no email address.`, the same person in two\n * roles `<Name> is already <Role label>.` (unless the config allows it).\n */\n@Component({\n selector: 'ui-document-signers',\n standalone: true,\n changeDetection: ChangeDetectionStrategy.OnPush,\n imports: [ButtonComponent, ContactPickerComponent, ErrorComponent, FormSectionComponent, HintComponent],\n providers: [{ provide: UiFieldValidity, useExisting: forwardRef(() => DocumentSignersComponent) }],\n template: `\n @if (rowsShown()) {\n <ui-form-section label=\"Signers\" data-testid=\"signers-section\">\n @for (role of info()!.roles; track role) {\n @if (role === 'signer1') {\n <div class=\"ds-row\" [attr.data-testid]=\"'signer-row-' + role\">\n <span class=\"ds-role\">{{ label(role) }}</span>\n <span class=\"ds-name\">{{ recipient().name }}</span>\n </div>\n } @else {\n <div class=\"ds-pick\" [attr.data-testid]=\"'signer-row-' + role\">\n <ui-contact-picker\n #picker\n [label]=\"label(role)\"\n [required]=\"true\"\n [showErrors]=\"showErrors()\"\n [errorId]=\"'ds-error-' + role\"\n placeholder=\"Search for a contact…\"\n (contactSelected)=\"pick(role, $event)\" />\n <!-- The picker's own \"<Role> is required.\" (shown after blur or with showErrors), else the contact problem (no email / twice). -->\n <div class=\"ds-error\" [attr.id]=\"'ds-error-' + role\">\n <ui-error [message]=\"visibleProblem(role) || picker.visibleErrorMessage()\" />\n </div>\n </div>\n }\n }\n <ui-hint data-testid=\"signers-order-hint\">Signers are emailed one after another, in this order; each receives the link after the previous one signs.</ui-hint>\n </ui-form-section>\n }\n @if (line()) {\n <p class=\"ds-countersign\" data-testid=\"countersign-line\">{{ line() }}</p>\n <!-- The countersign box is not placed on the document yet: a link to where it is placed (the host navigates on mapFieldsClick). -->\n @if (placeDocs().length > 0) {\n <div class=\"ds-place\" data-testid=\"countersign-place\">\n @for (d of placeDocs(); track d.id) {\n <ui-button\n class=\"ds-place-btn\"\n variant=\"link\"\n size=\"sm\"\n [ariaLabel]=\"placeLabel(d.title)\"\n [attr.data-testid]=\"'countersign-place-' + d.id\"\n (click)=\"mapFieldsClick.emit(d)\">{{ placeText(d.title) }}</ui-button>\n }\n </div>\n }\n }\n `,\n styles: [`\n :host { display: block; }\n .ds-row { display: flex; flex-wrap: wrap; align-items: baseline; gap: 6px 10px; min-width: 0; }\n .ds-role { font-size: 0.8125rem; font-weight: 600; color: #334155; }\n .ds-name { font-size: 0.875rem; color: #1e293b; overflow-wrap: anywhere; min-width: 0; }\n .ds-countersign { margin: 0; font-size: 0.8125rem; color: #475569; }\n ui-form-section + .ds-countersign { margin-top: 10px; }\n .ds-place { display: flex; flex-direction: column; align-items: flex-start; gap: 2px; min-width: 0; margin-top: 4px; }\n .ds-place-btn { max-width: 100%; font-size: 0.8125rem; }\n /* Wrap long document titles instead of overflowing at 320px (the library button is nowrap). */\n .ds-place-btn ::ng-deep .btn { white-space: normal; text-align: left; overflow-wrap: anywhere; }\n /* ui-button's link variant is exempt from the 44px touch rule (.btn-link.btn-sm { min-height: 0 }); this one is a stand-alone action, so give it a 44px\n hit area on touch. The extra .btn-link class out-ranks that rule whatever the stylesheet order. */\n @media (pointer: coarse), (max-width: 480px) {\n .ds-place-btn ::ng-deep .btn.btn-link { min-height: 44px; }\n }\n `],\n})\nexport class DocumentSignersComponent implements UiFieldValidity {\n /** The signer logic's conclusion for the chosen document(s) (`ui-document-send-options` value `signerInfo`). */\n readonly info = input<DocumentSignerInfo | null>(null);\n readonly config = input<DocumentSignersConfig | null>(null);\n /** The recipient of the message = Signer 1. */\n readonly recipient = input<SignerPick>({ id: '', name: '', email: null });\n /** Shows the \"<Role> is required.\" messages now (otherwise after the first blur). */\n readonly showErrors = input(false);\n /** The sender chose DocuSign: only the countersign line applies. */\n readonly viaDocuSign = input(false);\n\n /** The complete signer list (recipient first) or null while a role has no contact. Emitted on every change. */\n readonly signersChange = output<SendSigner[] | null>();\n readonly invalidChange = output<boolean>();\n /** The \"Place your countersignature box\" link was clicked: the document whose countersign box is not placed (the host opens Map Data Fields). */\n readonly mapFieldsClick = output<{ id: string; title: string }>();\n\n readonly picks = signal<Partial<Record<SignerRole, ContactLookup | null>>>({});\n\n protected readonly rowsShown = computed(() => !!this.info()?.multiSigner && !this.viaDocuSign());\n protected readonly line = computed(() => countersignLine(this.info(), this.config(), this.viaDocuSign()));\n\n /** Documents the \"place your box\" link is offered for: native send only (DocuSign places its own fields) and only while the countersign line shows. */\n protected readonly placeDocs = computed(() => this.line() && !this.viaDocuSign() ? (this.info()?.countersignUnplaced ?? []) : []);\n\n readonly problems = computed(() => this.rowsShown()\n ? signerProblems(this.info()!.roles, this.recipient(), this.picks(), this.config()) : {});\n readonly signers = computed<SendSigner[] | null>(() => {\n if (!this.rowsShown() || Object.keys(this.problems()).length > 0) return null;\n return buildSigners(this.info()!.roles, this.recipient(), this.picks());\n });\n readonly errorMessage = computed(() => {\n const p = this.problems();\n const first = this.info()?.roles.find(r => p[r]);\n return first ? p[first]! : '';\n });\n readonly invalid = computed(() => this.errorMessage() !== '');\n\n constructor() {\n effect(() => {\n const s = this.signers();\n untracked(() => this.signersChange.emit(s));\n });\n effect(() => {\n const bad = this.invalid();\n untracked(() => this.invalidChange.emit(bad));\n });\n }\n\n protected placeLabel(title: string): string { return countersignPlaceLabel(title); }\n /** Visible text: with several documents the title is part of it (label-in-name: it contains the start of the accessible name). */\n protected placeText(title: string): string { return this.placeDocs().length > 1 ? countersignPlaceLabel(title) : 'Place your countersignature box'; }\n\n protected label(role: SignerRole): string { return signerRoleLabel(role, this.config()); }\n\n /** Under a picker: the problems of a chosen contact (no email / same person twice); the empty-row message is the picker's own (`visibleErrorMessage`). */\n protected visibleProblem(role: SignerRole): string {\n return this.picks()[role] ? (this.problems()[role] ?? '') : '';\n }\n\n pick(role: SignerRole, c: ContactLookup | null) {\n if (roleNumber(role) < 2) return;\n this.picks.update(m => ({ ...m, [role]: c }));\n }\n}\n","import { ChangeDetectionStrategy, Component, DestroyRef, computed, inject, input, output } from '@angular/core';\nimport { HttpClient } from '@angular/common/http';\nimport { takeUntilDestroyed, toObservable } from '@angular/core/rxjs-interop';\nimport { catchError, debounceTime, defer, distinctUntilChanged, map, of, switchMap } from 'rxjs';\nimport type { Observable } from 'rxjs';\nimport { FORGEROUTE_CONFIG } from '@forgeroute/ui';\nimport { hasMappedDataFields, mergeWarningLines } from './document-signers';\nimport type { MergeCheckFn, MergeCheckRequest, MergeCheckResult, SendContext, SendSigner, SignerDocLike } from './document-signers';\n\n/** Quiet time after the last change before the check is requested. */\nexport const MERGE_CHECK_DEBOUNCE_MS = 300;\n\n/**\n * ui-document-merge-check — the pre-send \"fields that will be empty\" check (non-blocking), logic only (it renders nothing).\n *\n * Internal child of the message forms. While `enabled` is `true` (default route `POST <baseUrl>/documents/merge-check`) or a host\n * function, and at least one chosen document has mapped data fields, it asks which data fields would be left empty for this recipient,\n * these signers and this context, and emits the warning lines (`warningsChange`). Debounced, a newer request cancels an older one\n * (`switchMap`), never cached. A failed or slow check emits no warning and never blocks Send. Documents with no mapped data fields\n * are not sent to the check.\n */\n@Component({\n selector: 'ui-document-merge-check',\n standalone: true,\n changeDetection: ChangeDetectionStrategy.OnPush,\n template: ``,\n styles: [`:host { display: none; }`],\n})\nexport class DocumentMergeCheckComponent {\n private readonly http = inject(HttpClient);\n private readonly destroyRef = inject(DestroyRef);\n private readonly apiBase = inject(FORGEROUTE_CONFIG, { optional: true })?.baseUrl ?? '/api';\n\n /** `true` = the default route, a function = the host's own call, `false` / `null` = off. */\n readonly enabled = input<boolean | MergeCheckFn | null>(false);\n readonly documents = input<SignerDocLike[]>([]);\n readonly customerId = input('');\n readonly signers = input<SendSigner[] | null>(null);\n readonly context = input<SendContext | undefined>(undefined);\n\n /** The warning lines (empty = nothing to warn about). Emitted whenever they change. */\n readonly warningsChange = output<string[]>();\n\n private readonly request = computed<MergeCheckRequest | null>(() => {\n if (!this.enabled()) return null;\n const ids = this.documents().filter(hasMappedDataFields).map(d => d.id);\n if (ids.length === 0) return null;\n const req: MergeCheckRequest = { documentIds: ids };\n if (this.customerId()) req.customerId = this.customerId();\n const signers = this.signers();\n if (signers?.length) req.signers = signers;\n const ctx = this.context();\n if (ctx && Object.keys(ctx).length) req.context = ctx;\n return req;\n });\n\n constructor() {\n toObservable(this.request).pipe(\n distinctUntilChanged((a, b) => JSON.stringify(a) === JSON.stringify(b)),\n debounceTime(MERGE_CHECK_DEBOUNCE_MS),\n switchMap(req => {\n if (!req) return of<string[]>([]);\n const titles = Object.fromEntries(this.documents().map(d => [d.id, d.title]));\n return defer(() => this.call(req)).pipe(\n map(res => mergeWarningLines(res, titles)),\n catchError(() => of<string[]>([])),\n );\n }),\n takeUntilDestroyed(this.destroyRef),\n ).subscribe(lines => {\n // Only a change is reported (the host starts with no warning); turning the check off or removing the last mapped document\n // clears a warning that is already shown.\n if (lines.join('\\n') === this.lastLines.join('\\n')) return;\n this.lastLines = lines;\n this.warningsChange.emit(lines);\n });\n }\n\n private lastLines: string[] = [];\n\n private call(req: MergeCheckRequest): Observable<MergeCheckResult> {\n const fn = this.enabled();\n return typeof fn === 'function' ? fn(req) : this.http.post<MergeCheckResult>(`${this.apiBase}/documents/merge-check`, req);\n }\n}\n","/**\n * Video share-link helpers shared by every app (they replace the apps' own copies, e.g. real-estate `video-link.utils.ts`).\n *\n * The share link of a video (`VideoMessage.shareLink`) carries the playback settings as query parameters:\n * autoplay=1 · ref=<contact id> · ctaType · ctaUrl · ctaLabel · hideReactions=1 (in exactly that order).\n */\n\nexport interface VideoLinkOptions {\n autoplay?: boolean;\n /** The recipient: lets the video page track who watched (`ref`). */\n contactId?: string;\n /** '' / undefined = none (the video's own default); `schedule` | `contact` | `custom`. */\n ctaType?: string;\n ctaUrl?: string;\n ctaLabel?: string;\n hideReactions?: boolean;\n}\n\n/**\n * Default video settings the host reads from its Communication settings (same keys as `/comm-settings` without the `videoDefault`\n * prefix): `videoDefaultAutoplay` → `autoplay`, `videoDefaultCtaType` → `ctaType`, … Missing keys mean \"off\" / \"none\".\n */\nexport interface VideoDefaults {\n autoplay?: boolean;\n ctaType?: string;\n ctaUrl?: string;\n ctaLabel?: string;\n hideReactions?: boolean;\n}\n\n/** `baseLink` + the settings as query parameters (unchanged when no setting is on). A base that already has a query gets `&`. */\nexport function buildVideoLink(baseLink: string, options: VideoLinkOptions = {}): string {\n const params: string[] = [];\n if (options.autoplay) params.push('autoplay=1');\n if (options.contactId) params.push(`ref=${encodeURIComponent(options.contactId)}`);\n if (options.ctaType) params.push(`ctaType=${encodeURIComponent(options.ctaType)}`);\n if (options.ctaUrl) params.push(`ctaUrl=${encodeURIComponent(options.ctaUrl)}`);\n if (options.ctaLabel) params.push(`ctaLabel=${encodeURIComponent(options.ctaLabel)}`);\n if (options.hideReactions) params.push('hideReactions=1');\n return params.length ? `${baseLink}${baseLink.includes('?') ? '&' : '?'}${params.join('&')}` : baseLink;\n}\n\n/**\n * The share link of a freshly attached video: `baseLink` with the host's defaults and the recipient applied — exactly the link\n * `ui-video-link-settings` shows before the sender changes anything.\n */\nexport function videoLinkWithDefaults(baseLink: string, defaults: VideoDefaults = {}, contactId = ''): string {\n return buildVideoLink(baseLink, {\n autoplay: !!defaults.autoplay,\n contactId: contactId || undefined,\n ctaType: defaults.ctaType || undefined,\n ctaUrl: defaults.ctaUrl || undefined,\n ctaLabel: defaults.ctaLabel || undefined,\n hideReactions: !!defaults.hideReactions,\n });\n}\n\n/** Strips any \"🎬 Watch video: <url>\" line previously inserted into a message body (the line format the apps' own composers use). */\nexport function stripVideoLink(text: string): string {\n return text\n .replace(/\\n\\n🎬 Watch video: [^\\n]*/g, '')\n .replace(/\\n🎬 Watch video: [^\\n]*/g, '')\n .replace(/🎬 Watch video: [^\\n]*/g, '')\n .trimEnd();\n}\n\n/** Appends a \"🎬 Watch video: <link>\" line to a message body (replacing one that is already there). */\nexport function appendVideoLink(text: string, link: string): string {\n const base = stripVideoLink(text);\n return base ? `${base}\\n\\n🎬 Watch video: ${link}` : `🎬 Watch video: ${link}`;\n}\n\n/** The line `ui-send-message-panel` puts in the message for an attached video: `Watch \"<title>\": <link>`. */\nexport function videoMessageLine(title: string, link: string): string {\n return `Watch \"${title}\": ${link}`;\n}\n\n/**\n * Puts `next` into `body` in place of `prev` (the line inserted earlier), so changing a video setting rewrites the link without\n * touching the rest of the message:\n * - no `prev` yet → appended after a blank line (no separator for an empty body);\n * - `prev` is in the body → replaced in place; with an empty `next` it is removed together with its separator;\n * - `prev` was edited away by the sender → the body is left as the sender wrote it.\n */\nexport function applyVideoLine(body: string, prev: string, next: string): string {\n if (!prev) {\n if (!next) return body;\n return body.trim() ? `${body}\\n\\n${next}` : next;\n }\n const at = body.indexOf(prev);\n if (at < 0) return body;\n if (next) return body.slice(0, at) + next + body.slice(at + prev.length);\n const before = body.slice(0, at);\n const after = body.slice(at + prev.length);\n const trimmedBefore = before.replace(/\\n{1,2}$/, '');\n return (trimmedBefore + after).trim() === '' ? '' : trimmedBefore + after;\n}\n","import { ChangeDetectionStrategy, Component, computed, effect, input, output, signal, untracked } from '@angular/core';\nimport { FormsModule } from '@angular/forms';\nimport { ButtonComponent, CheckboxComponent, FormSectionComponent, HintComponent, RadioGroupComponent, type RadioOption, SelectComponent, type SelectOption, TextInputComponent } from '@forgeroute/ui';\nimport type { VideoMessage } from '../video-picker/video-picker.component';\nimport { buildVideoLink } from './video-link';\nimport type { VideoDefaults } from './video-link';\n\nexport const VIDEO_PLAYBACK_OPTIONS: RadioOption[] = [\n { value: 'click', label: 'Click to Play' },\n { value: 'autoplay', label: 'Autoplay' },\n];\n\nexport const VIDEO_CTA_OPTIONS: SelectOption[] = [\n { value: '', label: 'None (use video default)' },\n { value: 'schedule', label: 'Schedule a Call' },\n { value: 'contact', label: 'Contact Me' },\n { value: 'custom', label: 'Custom' },\n];\n\n/**\n * ui-video-link-settings — an attached video: a \"Watch Preview\" link, \"Remove Video\" and ONE \"Video settings\" disclosure (collapsed by default)\n * holding Playback (click to play / autoplay), Call-to-Action (none / schedule / contact / custom, with its URL and button label)\n * and Hide reactions.\n *\n * Internal child of `ui-send-message-panel` (the \"Video\" section of the email and text forms) — hosts turn it on through the panel's\n * `videoDefaults` input and do not place it themselves. It starts from `defaults` whenever a (new) `video` is given (the form writes the\n * initial link itself with `videoLinkWithDefaults`, which gives the same link) and emits the full share link (`buildVideoLink`) after\n * every change the sender makes; the form rewrites the video line of the message with it. It never emits while Angular is rendering.\n */\n@Component({\n selector: 'ui-video-link-settings',\n standalone: true,\n changeDetection: ChangeDetectionStrategy.OnPush,\n imports: [FormsModule, ButtonComponent, CheckboxComponent, FormSectionComponent, HintComponent, RadioGroupComponent, SelectComponent, TextInputComponent],\n template: `\n @if (video(); as v) {\n <ui-form-section class=\"vls\">\n <div class=\"vls-bar\">\n @if (v.shareLink) {\n <ui-hint><a class=\"vls-preview\" [href]=\"v.shareLink\" target=\"_blank\" rel=\"noopener\" aria-label=\"Watch Preview (opens in a new tab)\">Watch Preview →</a></ui-hint>\n }\n <ui-button variant=\"ghost\" size=\"xs\" [ariaExpanded]=\"settingsOpen()\" (click)=\"toggleSettings()\">Video settings</ui-button>\n <ui-button variant=\"secondary\" size=\"sm\" (click)=\"removed.emit()\">Remove Video</ui-button>\n </div>\n <!-- Collapsed by default: the defaults from Communication settings are already applied. -->\n @if (settingsOpen()) {\n <ui-radio-group\n label=\"Playback\"\n orientation=\"horizontal\"\n [options]=\"playbackOptions\"\n [ngModel]=\"autoplay()\"\n (ngModelChange)=\"setAutoplay($event)\" />\n <ui-select\n label=\"Call-to-Action\"\n [options]=\"ctaOptions\"\n [ngModel]=\"ctaType()\"\n (ngModelChange)=\"setCtaType($event)\" />\n @if (ctaType()) {\n <ui-text-input\n label=\"Call-to-action URL\"\n type=\"url\"\n placeholder=\"https://...\"\n [maxlength]=\"500\"\n [ngModel]=\"ctaUrl()\"\n (ngModelChange)=\"setCtaUrl($event)\" />\n <ui-text-input\n label=\"Call-to-action label\"\n placeholder=\"Button label (e.g. Book a Call)\"\n [maxlength]=\"100\"\n [ngModel]=\"ctaLabel()\"\n (ngModelChange)=\"setCtaLabel($event)\" />\n }\n <ui-checkbox label=\"Hide reactions\" [ngModel]=\"hideReactions()\" (ngModelChange)=\"setHideReactions($event)\" />\n }\n </ui-form-section>\n }\n `,\n styles: [`\n :host { display: block; }\n .vls-bar { display: flex; flex-wrap: wrap; align-items: center; gap: 6px 14px; min-width: 0; }\n .vls-preview { color: #2563eb; text-decoration: underline; }\n .vls-preview:focus-visible { outline: 2px solid #3b82f6; outline-offset: 2px; border-radius: 2px; }\n `],\n})\nexport class VideoLinkSettingsComponent {\n /** The attached video; its settings restart from `defaults` whenever this changes. */\n readonly video = input<VideoMessage | null>(null);\n /** The host's Communication-settings defaults. */\n readonly defaults = input<VideoDefaults>({});\n /** The recipient (tracked in the link as `ref`); empty = no `ref`. */\n readonly contactId = input('');\n\n /** The full share link (video link + settings) after each change the sender makes (not on creation). */\n readonly linkChange = output<string>();\n readonly removed = output<void>();\n\n protected readonly playbackOptions = VIDEO_PLAYBACK_OPTIONS;\n protected readonly ctaOptions = VIDEO_CTA_OPTIONS;\n\n readonly autoplay = signal<'click' | 'autoplay'>('click');\n readonly ctaType = signal('');\n readonly ctaUrl = signal('');\n readonly ctaLabel = signal('');\n readonly hideReactions = signal(false);\n /** The \"Video settings\" disclosure; closed again for every newly attached video. */\n readonly settingsOpen = signal(false);\n\n /** The share link with every setting applied ('' while there is no video link). */\n readonly link = computed(() => {\n const base = this.video()?.shareLink;\n if (!base) return '';\n return buildVideoLink(base, {\n autoplay: this.autoplay() === 'autoplay',\n contactId: this.contactId() || undefined,\n ctaType: this.ctaType() || undefined,\n ctaUrl: this.ctaUrl() || undefined,\n ctaLabel: this.ctaLabel() || undefined,\n hideReactions: this.hideReactions(),\n });\n });\n\n constructor() {\n // A new video starts from the defaults (read when the video is attached, so later edits are never overwritten).\n effect(() => {\n const video = this.video();\n if (!video) return;\n untracked(() => {\n const d = this.defaults();\n this.autoplay.set(d.autoplay ? 'autoplay' : 'click');\n this.ctaType.set(d.ctaType || '');\n this.ctaUrl.set(d.ctaUrl || '');\n this.ctaLabel.set(d.ctaLabel || '');\n this.hideReactions.set(d.hideReactions ?? false);\n this.settingsOpen.set(false);\n });\n });\n }\n\n toggleSettings() { this.settingsOpen.update(v => !v); }\n setAutoplay(v: string) { this.autoplay.set(v === 'autoplay' ? 'autoplay' : 'click'); this.emitLink(); }\n setCtaType(v: string | null) { this.ctaType.set(v || ''); this.emitLink(); }\n setCtaUrl(v: string | null) { this.ctaUrl.set(v || ''); this.emitLink(); }\n setCtaLabel(v: string | null) { this.ctaLabel.set(v || ''); this.emitLink(); }\n setHideReactions(v: boolean | null) { this.hideReactions.set(!!v); this.emitLink(); }\n\n private emitLink() {\n const link = this.link();\n if (link) this.linkChange.emit(link);\n }\n}\n","import { switchMap } from 'rxjs';\nimport type { Observable } from 'rxjs';\nimport type { HttpClient } from '@angular/common/http';\nimport type {\n DocumentSendDelivery, DocumentSendPurpose, DocumentSendValue, DocumentSignatureProvider,\n} from '../document-send-options/document-send-options.component';\nimport type { SendContext, SendSigner } from '../document-send-options/document-signers';\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 /** Only when the host turned on `documentSigningOptions` and the request is a native e-signature request (purpose `ESIGN` / `BOTH`): days until the request expires (1-90). */\n expiryDays?: number;\n /** Same condition as `expiryDays`: days between reminders (1-30). */\n reminderIntervalDays?: number;\n /** Same condition as `expiryDays`: how many reminders at most (0-10). */\n maxReminders?: number;\n /** Only when the host passed `docuSignConnected` and the sender can choose (email, exactly one document): which e-signature provider sends the request. */\n provider?: DocumentSignatureProvider;\n /** Only with `provider: 'DOCUSIGN'`: `countersign` is true when the sender ticked \"Require my countersignature after client signs\" (only offered for a document flagged `requiresCountersign`). */\n docuSign?: { countersign: boolean };\n /**\n * Only when the host turned on `documentSigners`, the request is a native e-signature one, ONE document is chosen and it has MORE THAN ONE\n * customer signer: all N signers including signer1 (= the recipient), ordered by role, never partial. A single-signer send has no such key.\n */\n signers?: SendSigner[];\n /** Only when the host passed `sendContext` and at least one key it declared in `documentContextTypes` has a value (other keys are dropped). */\n context?: SendContext;\n /** Only when the send is of an existing envelope (`documentEnvelope`): its id; the request is not created again, it is sent. */\n envelopeId?: string;\n /** Only with `documentSigners` on, when every chosen document has no signature fields: the send is informational (the recipient gets a view link, nothing is signed). */\n informational?: true;\n}\n\n/** What the success toast of a document send can say (`documentSentMessage` of the panel). */\nexport interface DocumentSentInfo {\n titles: string[];\n /** Only when an existing envelope was sent (`documentEnvelope`). */\n envelopeName?: string;\n channel: 'email' | 'text';\n provider?: DocumentSignatureProvider;\n /** Every chosen document has no signature fields (a view link was sent). */\n informational: boolean;\n}\n\n/** Builds the success toast of a document send (default: `Email sent` / `Text message sent` / `\"<title>\" sent via DocuSign` / `Envelope \"<name>\" sent`). */\nexport type DocumentSentMessage = (info: DocumentSentInfo) => string;\n\n/** The optional parts the form adds to the `documents` payload besides what the document section holds. */\nexport interface DocumentsPayloadExtras { signers?: SendSigner[] | null; context?: SendContext; envelopeId?: string }\n\n/**\n * The `documents` part of a payload from what the document section holds. The optional keys (signing settings, provider, DocuSign\n * options) are present only when the section produced them, so a host that turned none of them on gets exactly\n * `{ documentIds, purpose, delivery }`.\n */\nexport function buildDocumentsPayload(\n v: DocumentSendValue, delivery: DocumentSendDelivery, purpose: DocumentSendPurpose = v.purpose, extras: DocumentsPayloadExtras = {},\n): SendDocumentsPayload {\n const out: SendDocumentsPayload = { documentIds: v.documentIds, purpose, delivery };\n if (v.expiryDays !== undefined) out.expiryDays = v.expiryDays;\n if (v.reminderIntervalDays !== undefined) out.reminderIntervalDays = v.reminderIntervalDays;\n if (v.maxReminders !== undefined) out.maxReminders = v.maxReminders;\n if (v.provider !== undefined) out.provider = v.provider;\n if (v.docuSign !== undefined) out.docuSign = v.docuSign;\n if (extras.signers?.length) out.signers = extras.signers;\n if (extras.context && Object.keys(extras.context).length) out.context = extras.context;\n if (extras.envelopeId) out.envelopeId = extras.envelopeId;\n if (v.signerInfo?.informational) out.informational = true;\n return out;\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, informational = false): string {\n if (titles.length === 0) return '';\n // Nothing is signed in an informational send (view link): \"Document shared: <title>\".\n if (informational && purpose !== 'ATTACH') {\n return titles.length === 1 ? `Document shared: ${titles[0]}` : `${titles.length} documents shared: ${titles[0]}…`;\n }\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 /** Native e-signature requests only, and only when the host turned on `documentSigningOptions`. */\n expiryDays?: number;\n reminderIntervalDays?: number;\n maxReminders?: number;\n /** Only with `documentSigners` and a document with several customer signers (complete, ordered). */\n signers?: SendSigner[];\n /** Only with `sendContext` / `documentContextTypes`. */\n context?: SendContext;\n}\n\n/** The signing settings of a `documents` payload as optional keys of the create-request body (nothing when there are none). */\nexport function signingBodyFields(d: SendDocumentsPayload): Pick<CreateDocumentRequestBody, 'expiryDays' | 'reminderIntervalDays' | 'maxReminders'> {\n const out: Pick<CreateDocumentRequestBody, 'expiryDays' | 'reminderIntervalDays' | 'maxReminders'> = {};\n if (d.expiryDays !== undefined) out.expiryDays = d.expiryDays;\n if (d.reminderIntervalDays !== undefined) out.reminderIntervalDays = d.reminderIntervalDays;\n if (d.maxReminders !== undefined) out.maxReminders = d.maxReminders;\n return out;\n}\n\n/** The body of `POST <baseUrl>/documents/envelopes/{id}/send` for an existing envelope: the create body without the document ids (the envelope already has them). */\nexport type ExistingEnvelopeSendBody = Omit<CreateDocumentRequestBody, 'documentIds'> & { templateId?: string };\n\n/**\n * Sends an envelope that already exists (`documentEnvelope`): `POST <baseUrl>/documents/envelopes/{id}/send` with the recipient, message and\n * options in the body (nothing is created first). The host can replace it with `[sendEmail]` / `[sendText]` (payload `documents.envelopeId`).\n */\nexport function sendExistingEnvelope(http: HttpClient, apiBase: string, envelopeId: string, body: ExistingEnvelopeSendBody): Observable<unknown> {\n return http.post(`${apiBase}/documents/envelopes/${encodeURIComponent(envelopeId)}/send`, body);\n}\n\n/** The `signers` / `context` of a `documents` payload as optional keys of the create-request body (nothing when there are none). */\nexport function signersContextBodyFields(d: SendDocumentsPayload): Pick<CreateDocumentRequestBody, 'signers' | 'context'> {\n const out: Pick<CreateDocumentRequestBody, 'signers' | 'context'> = {};\n if (d.signers?.length) out.signers = d.signers;\n if (d.context) out.context = d.context;\n return out;\n}\n\n/** What the library sends to send ONE document through DocuSign (`POST <baseUrl>/docusign/send`). See docs/send-documents-contract.md. */\nexport interface DocuSignSendBody {\n documentId: string;\n contactId: string;\n subject?: string;\n message?: string;\n countersignRequired: boolean;\n}\n\n/** The default DocuSign send: `POST <baseUrl>/docusign/send` → 200. The host can replace it with `[sendEmail]`. */\nexport function sendDocuSignRequest(http: HttpClient, apiBase: string, body: DocuSignSendBody): Observable<unknown> {\n return http.post(`${apiBase}/docusign/send`, body);\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, computed, isDevMode,\n} from '@angular/core';\nimport { FormsModule } from '@angular/forms';\nimport { HttpClient } from '@angular/common/http';\nimport type { Observable } from 'rxjs';\nimport { FORGEROUTE_CONFIG, getErrorMessage, ButtonComponent, CheckboxComponent, PillTabsComponent, type PillTab, ToastService, ErrorComponent, TextInputComponent, TextareaInputComponent, DatetimeInputComponent, FormSectionComponent, AlertComponent } from '@forgeroute/ui';\nimport { serverMessage } from './server-message';\nimport { takeUntilDestroyed } from '@angular/core/rxjs-interop';\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 { DocumentSendOptionsComponent } from '../document-send-options/document-send-options.component';\nimport type {\n DocumentSendValue, DocumentSendPurpose, DocumentSigningOptions, DocumentPreviewHandler, DocumentPreviewContact, DocumentSeed, DocumentEnvelopeTarget, DocumentOption,\n} from '../document-send-options/document-send-options.component';\nimport { DocumentSignersComponent } from '../document-send-options/document-signers.component';\nimport { DocumentMergeCheckComponent } from '../document-send-options/document-merge-check.component';\nimport { countersignLine, forwardContext } from '../document-send-options/document-signers';\nimport type {\n ContextTypeDef, DocumentSignerInfo, DocumentSignersConfig, MergeCheckFn, SendContext, SendSigner,\n} from '../document-send-options/document-signers';\nimport { VideoLinkSettingsComponent } from '../video-link-settings/video-link-settings.component';\nimport { applyVideoLine, videoLinkWithDefaults, videoMessageLine } from '../video-link-settings/video-link';\nimport type { VideoDefaults } from '../video-link-settings/video-link';\nimport {\n DEFAULT_DOCUMENT_SUBJECT, buildDocumentsPayload, ccError as ccListError, parseCcList, sendDocuSignRequest, sendDocumentRequest,\n sendExistingEnvelope, signersContextBodyFields, signingBodyFields, suggestDocumentSubject,\n} from './send-documents';\nimport type { DocuSignSendBody, DocumentSentMessage, 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 (`&amp;` `&lt;` `&gt;` `&quot;` `&#39;`)\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, '&amp;').replace(/</g, '&lt;').replace(/>/g, '&gt;')\n .replace(/\"/g, '&quot;').replace(/'/g, '&#39;')\n .replace(/\\r\\n|\\r|\\n/g, '<br>');\n}\n\n/** A stable empty list (a child input must not receive a new array on every check). */\nconst NO_DOCUMENTS: DocumentOption[] = [];\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, ErrorComponent, VideoLinkSettingsComponent, DocumentSignersComponent, DocumentMergeCheckComponent, AlertComponent],\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 &amp; 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 <ui-error errorId=\"sm-email-disabled\" [message]=\"emailEnabled() ? '' : EMAIL_DISABLED_MESSAGE\" />\n\n <!-- Signers (only for a document with several customer signers) and the read-only countersign line. Keyed on the document: another document starts with empty rows. -->\n @if (signersSectionShown) {\n @for (k of [signersKey]; track k) {\n <ui-document-signers\n [info]=\"signerInfo\"\n [config]=\"documentSigners()\"\n [recipient]=\"recipientPick()\"\n [viaDocuSign]=\"viaDocuSign\"\n (signersChange)=\"onSigners($event)\"\n (invalidChange)=\"onSignersInvalid($event)\"\n (mapFieldsClick)=\"mapFieldsClick.emit($event)\" />\n }\n }\n\n @if (showCc() && !viaDocuSign) {\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]=\"!viaDocuSign\"\n placeholder=\"Email subject...\"\n [maxlength]=\"MAX_SUBJECT\"\n [(ngModel)]=\"subject\"\n (ngModelChange)=\"notifyValid()\" />\n <ui-textarea-input\n label=\"Message\"\n [required]=\"!viaDocuSign\"\n placeholder=\"Write your message...\"\n [rows]=\"8\"\n [(ngModel)]=\"body\"\n [error]=\"composeTagError || bodyLengthError\"\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\">&times;</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 } @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 [signing]=\"documentSigningOptions()\"\n [signers]=\"documentSigners()\"\n [envelope]=\"documentEnvelope()\"\n [docuSignConnected]=\"docuSignConnected() && !documentEnvelope()\"\n [documentPreview]=\"documentPreview()\"\n [previewContact]=\"previewContact()\"\n [previewSendContext]=\"sendContext()\"\n [showErrors]=\"true\"\n (valueChange)=\"onDocumentsChange($event)\"\n (invalidChange)=\"onDocumentsInvalid($event)\" />\n <ui-document-merge-check\n [enabled]=\"documentMergeCheck()\"\n [documents]=\"mergeDocuments\"\n [customerId]=\"contactId()\"\n [signers]=\"signersActive ? signersValue : null\"\n [context]=\"forwardedContext()\"\n (warningsChange)=\"onMergeWarnings($event)\" />\n <!-- One live region, at most ONE warning banner: every current non-blocking warning is a line in it. Blocking problems are inline on their field. -->\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 }\n @if (warningLines.length > 0) {\n <ui-alert variant=\"warning\" title=\"Heads up\" data-testid=\"send-warnings\">\n <ul class=\"sm-warn-list\">\n @for (w of warningLines; track w.key) { <li [attr.data-testid]=\"w.testId\">{{ w.text }}</li> }\n </ul>\n </ui-alert>\n }\n </div>\n }\n\n <!-- Why Send is disabled for length, in every mode (compose shows the message error inline on the editor itself). -->\n <ui-error errorId=\"sm-length-error\" [message]=\"sendLengthError\" />\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 <!-- Video: the LAST section. The picker, and once a video is attached its settings, Watch Preview and Remove Video. -->\n @if (mode === 'compose') {\n <ui-form-section label=\"Video\" [divider]=\"true\">\n @for (k of [videoPickerKey]; track k) {\n <ui-video-picker (videoSelected)=\"onVideoSelected($event)\" />\n }\n @if (videoPicked && videoDefaults()) {\n <ui-video-link-settings\n [video]=\"videoPicked\"\n [defaults]=\"videoDefaults()!\"\n [contactId]=\"contactId()\"\n (linkChange)=\"onVideoLink($event)\"\n (removed)=\"removeVideo()\" />\n }\n </ui-form-section>\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() }} &lt;{{ contactEmail() }}&gt;</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-warn-list { margin: 0; padding-left: 18px; display: flex; flex-direction: column; gap: 2px; }\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); -webkit-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: 90vh; 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<DocumentSeed[]>([]);\n /**\n * Turns on the signer logic: one required contact per customer signer role of the chosen document, the read-only countersign line,\n * informational documents (no signature fields) and the one-signer DocuSign rule. `null` (default) = the single recipient, unchanged.\n */\n readonly documentSigners = input<DocumentSignersConfig | null>(null);\n /** Values the host has for THIS send; only the keys declared in `documentContextTypes` are sent, as `documents.context`. */\n readonly sendContext = input<SendContext | null>(null);\n /** The context keys the app's backend accepts. */\n readonly documentContextTypes = input<ContextTypeDef[]>([]);\n /** `true` = check (default route `POST <baseUrl>/documents/merge-check`), a function = the host's own check; shows a non-blocking \"fields that will be empty\" warning. */\n readonly documentMergeCheck = input<boolean | MergeCheckFn>(false);\n /** An existing envelope to send: its documents are fixed and it is sent (not created). Payload `documents.envelopeId`. `null` (default) = a new request from the chosen documents. */\n readonly documentEnvelope = input<DocumentEnvelopeTarget | null>(null);\n /** false = the app's email sending is switched off (Communication settings): Send stays off and the form says why. */\n readonly emailEnabled = input(true);\n /** Builds the success toast of a document send (default texts otherwise). */\n readonly documentSentMessage = input<DocumentSentMessage | null>(null);\n /** Shows a CC field; the typed addresses are sent as `cc`. Off = payload has no such key. */\n readonly showCc = input(false);\n /** Shows \"Expiry and Reminders\" for e-signature requests (see `DocumentSigningOptions`); sent as `documents.expiryDays` / `reminderIntervalDays` / `maxReminders`. `null` = off. */\n readonly documentSigningOptions = input<DocumentSigningOptions | null | undefined>(null);\n /** The host says DocuSign is connected: offers \"Native E-Sign / Send via DocuSign\" for a single document. Off = native only. */\n readonly docuSignConnected = input(false);\n /** \"Preview Document\" button(s) in the Documents section call this; `null` hides them. */\n readonly documentPreview = input<DocumentPreviewHandler | null>(null);\n /** The recipient handed to `documentPreview` (second argument): the panel's contact inputs, `null` while all four are blank. */\n readonly previewContact = computed<DocumentPreviewContact | null>(() => {\n const c = { id: this.contactId(), name: this.contactName(), email: this.contactEmail(), phone: this.contactPhone() };\n return c.id || c.name || c.email || c.phone ? c : null;\n });\n /**\n * Turns on the video settings (Playback, Call-to-Action, Hide reactions, Watch Preview, Remove Video) once a video is attached, starting\n * from these defaults, and makes the video link carry them. `null` (default) = the attached video's plain share link is inserted, as before.\n */\n readonly videoDefaults = input<VideoDefaults | null>(null);\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 /** A document send was refused because the document / envelope changed meanwhile (404 / 409): the host refreshes its list. */\n readonly rejected = output<void>();\n /** The \"Place your countersignature box\" link under the countersign line was clicked (document row `hasCountersignPlacement: false`). */\n readonly mapFieldsClick = output<{ id: string; title: string }>();\n readonly EMAIL_DISABLED_MESSAGE = 'Email sending is disabled, so this can\\'t be sent. Enable it in Communication Settings.';\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 /** The complete signer list of the signer rows (null while a role has no contact) and whether they break a rule. */\n signersValue: SendSigner[] | null = null;\n signersInvalid = false;\n /** Lines of the merge check (\"These fields will be left empty: …\"). */\n mergeWarnings: string[] = [];\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 // A context key the app did not declare is never sent; say so while developing.\n effect(() => {\n const { dropped } = forwardContext(this.sendContext(), this.documentContextTypes());\n if (dropped.length > 0 && isDevMode()) console.warn(`ui-send-message-panel: sendContext keys not listed in documentContextTypes are not sent: ${dropped.join(', ')}`);\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 env = this.documentEnvelope();\n const next = docs ? suggestDocumentSubject(env ? [env.name] : docs.documents.map(d => d.title), docs.purpose, docs.signerInfo?.informational === true) : '';\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 /** True when the sender chose to send the (single) document through DocuSign: DocuSign emails it itself, so CC, attachments and the required Subject / Message do not apply. */\n get viaDocuSign(): boolean { return this.hasDocuments && this.documentsValue?.provider === 'DOCUSIGN'; }\n\n /** The recipient as Signer 1 (a stable object: it feeds a child input). */\n readonly recipientPick = computed(() => ({ id: this.contactId(), name: this.contactName(), email: this.contactEmail() }));\n\n\n /** The declared context keys with a value (`documents.context`); keys the app did not declare are dropped (with a development warning). */\n readonly forwardedContext = computed<SendContext | undefined>(() => forwardContext(this.sendContext(), this.documentContextTypes()).context);\n\n /** What the signer logic concluded for the chosen documents (null when `documentSigners` is off or nothing is chosen). */\n get signerInfo(): DocumentSignerInfo | null { return this.documents() ? (this.documentsValue?.signerInfo ?? null) : null; }\n\n /** The chosen documents the merge check looks at. */\n get mergeDocuments(): DocumentOption[] { return this.documentsValue?.documents ?? NO_DOCUMENTS; }\n\n /** True when a native e-signature request for ONE document with several customer signers is being built: the signer rows are required. */\n get signersActive(): boolean {\n const info = this.signerInfo;\n return !!this.documentSigners() && this.hasDocuments && info?.status === 'ready' && info.multiSigner\n && !this.viaDocuSign && this.documentsValue?.purpose !== 'ATTACH';\n }\n\n /** The signer section is shown: signer rows, and/or the countersign line (a signed document that is not just attached). */\n get signersSectionShown(): boolean {\n const info = this.signerInfo;\n if (!this.documentSigners() || !this.hasDocuments || info?.status !== 'ready' || this.documentsValue?.purpose === 'ATTACH') return false;\n return this.signersActive || countersignLine(info, this.documentSigners(), this.viaDocuSign) !== '';\n }\n\n /** The signer rows restart whenever the chosen document changes. */\n get signersKey(): string { return this.documentsValue?.documentIds.join(',') ?? ''; }\n\n onSigners(v: SendSigner[] | null) { this.signersValue = v; this.notifyValid(); }\n onSignersInvalid(bad: boolean) { this.signersInvalid = bad; this.notifyValid(); }\n onMergeWarnings(lines: string[]) { this.mergeWarnings = lines; this.cdr.markForCheck(); }\n\n /** The lines of the ONE warning banner: what choosing a document removed, a multi-signer document among several, the merge check. */\n get warningLines(): { key: string; text: string; testId: string | null }[] {\n const out: { key: string; text: string; testId: string | null }[] = [];\n if (this.hasDocuments && this.docsRemovedNote) out.push({ key: 'removed', text: this.docsRemovedNote, testId: 'documents-removed-note' });\n if (this.hasDocuments && this.signerInfo?.multiDocumentNote) {\n out.push({ key: 'multi', text: 'Documents with several signers are sent one at a time.', testId: 'documents-multi-signer-note' });\n }\n if (this.hasDocuments) this.mergeWarnings.forEach((text, i) => out.push({ key: `merge${i}`, text, testId: 'merge-check-warning' }));\n return out;\n }\n\n /** The people copied on the email, as typed (empty unless `showCc` is on, and not used by a DocuSign send). */\n get ccList(): string[] { return this.showCc() && !this.viaDocuSign ? parseCcList(this.ccText) : []; }\n\n /** The first problem with the CC field ('' when fine). */\n get ccError(): string { return this.showCc() && !this.viaDocuSign ? 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 if (!this.signersActive) { this.signersValue = null; this.signersInvalid = false; }\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 /** Server limits (400 \"Subject must be 255 characters or fewer.\" / \"Body must be 10000 characters or fewer.\"). */\n readonly MAX_SUBJECT = 255;\n readonly MAX_BODY = 10000;\n\n /** Inline error when the message, as it will be sent, is over the server limit ('' when fine). */\n get bodyLengthError(): string {\n return this.resolvedBody.length > this.MAX_BODY\n ? `Message is too long (${this.resolvedBody.length.toLocaleString('en-US')} of ${this.MAX_BODY.toLocaleString('en-US')} characters).` : '';\n }\n\n /** Inline error when the subject, as it will be sent, is over the server limit ('' when fine). */\n get subjectLengthError(): string {\n return this.resolvedSubject.length > this.MAX_SUBJECT\n ? `Subject is too long (${this.resolvedSubject.length.toLocaleString('en-US')} of ${this.MAX_SUBJECT} characters).` : '';\n }\n\n /** The length problem shown under the form in every mode; compose mode shows the message one on the editor instead. */\n get sendLengthError(): string {\n return this.subjectLengthError || (this.mode === 'compose' ? '' : this.bodyLengthError);\n }\n\n get canSend(): boolean {\n if (!this.emailEnabled()) return false;\n if (this.bodyLengthError || this.subjectLengthError) return false;\n if (this.ccError || (this.documents() && this.documentsInvalid)) return false;\n // Several customer signers: every role has a contact (never a partial list), each with an email and no one twice.\n if (this.signersActive && (this.signersInvalid || !this.signersValue)) return false;\n // A DocuSign send needs the contact record (the server loads the signer from it) but no Subject / Message: DocuSign has its own defaults.\n if (this.viaDocuSign && !this.contactId()) return false;\n const hasContent = (this.viaDocuSign || (!!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.discardVideo();\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 /** The body of the default DocuSign send: Subject / Message only when the sender has them (DocuSign has its own defaults otherwise). */\n private docuSignBody(docs: DocumentSendValue, payload: SendEmailPayload): DocuSignSendBody {\n const body: DocuSignSendBody = {\n documentId: docs.documentIds[0], contactId: payload.contactId!, countersignRequired: docs.docuSign?.countersign === true,\n };\n if (payload.subject) body.subject = payload.subject;\n if (payload.body) body.message = payload.body;\n return body;\n }\n\n /** The video attached through the settings flow (null = none) and the line it put into the message. */\n videoPicked: VideoMessage | null = null;\n /** Bumped to re-create the video picker (it keeps its own selection and has no clear output). */\n videoPickerKey = 0;\n private _videoLine = '';\n\n /** The picker's output: with `videoDefaults` the video gets its settings section, otherwise the plain link is inserted as before. */\n onVideoSelected(v: VideoMessage) {\n if (!this.videoDefaults()) { this.insertVideo(v); return; }\n if (!v.shareLink) return;\n this.videoPicked = v;\n // The first link is written here (an event, not change detection): the video's own defaults plus the recipient.\n this.onVideoLink(videoLinkWithDefaults(v.shareLink, this.videoDefaults()!, this.contactId()));\n this.cdr.markForCheck();\n }\n\n /** The settings section's output: rewrites the video line of the message with the current link (the rest of the message is untouched). */\n onVideoLink(link: string) {\n const v = this.videoPicked;\n if (!v || !link) return;\n const next = videoMessageLine(v.title, link);\n this.body = applyVideoLine(this.body, this._videoLine, next);\n this._videoLine = next;\n this.notifyValid();\n }\n\n /** \"Remove Video\": takes the video line out of the message and brings the picker back. */\n removeVideo() {\n this.body = applyVideoLine(this.body, this._videoLine, '');\n this.discardVideo();\n this.notifyValid();\n }\n\n private discardVideo() {\n if (!this.videoPicked && !this._videoLine) return;\n this.videoPicked = null; this._videoLine = ''; this.videoPickerKey++;\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 = buildDocumentsPayload(docs, 'EMAIL', docs.purpose, {\n signers: this.signersActive ? this.signersValue : null, context: this.forwardedContext(), envelopeId: this.documentEnvelope()?.id,\n });\n payload.attachments = undefined;\n payload.scheduledAt = undefined;\n }\n const custom = this.sendFn();\n const req$ = custom ? custom(payload)\n : docs?.provider === 'DOCUSIGN' ? sendDocuSignRequest(this.http, this.apiBase, this.docuSignBody(docs, payload))\n : docs && this.documentEnvelope() ? sendExistingEnvelope(this.http, this.apiBase, this.documentEnvelope()!.id, {\n subject: payload.subject || DEFAULT_DOCUMENT_SUBJECT, message: payload.body,\n customerId: payload.contactId, customerName: payload.contactName, customerEmail: payload.contactEmail,\n purpose: docs.purpose, delivery: 'EMAIL',\n ...(cc.length ? { ccEmails: cc } : {}),\n ...signingBodyFields(payload.documents!),\n ...signersContextBodyFields(payload.documents!),\n ...(payload.templateId ? { templateId: payload.templateId } : {}),\n })\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 ...(cc.length ? { ccEmails: cc } : {}),\n ...signingBodyFields(payload.documents!),\n ...signersContextBodyFields(payload.documents!),\n }, payload.templateId)\n : this.http.post(`${this.apiBase}/email-messages`, payload);\n req$.pipe(takeUntilDestroyed(this.destroyRef)).subscribe({\n next: () => {\n const sentText = docs ? this.documentSentMessage()?.({\n titles: docs.documents.map(d => d.title), envelopeName: this.documentEnvelope()?.name, channel: 'email',\n provider: docs.provider, informational: docs.signerInfo?.informational === true,\n }) : undefined;\n this.toast.show(sentText ?? (docs?.provider === 'DOCUSIGN' ? `\"${docs.documents[0].title}\" sent via DocuSign`\n : docs && this.documentEnvelope() ? `Envelope \"${this.documentEnvelope()!.name}\" sent` : 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 // The server's own {message} wins (e.g. a length limit); with none the default endpoint keeps its fixed text.\n this.toast.error(custom || docs ? getErrorMessage(err, 'email', 'send') : (serverMessage(err) || 'Failed to send email'));\n this.loading = false; this.cdr.markForCheck();\n const status = (err as { status?: number } | null)?.status;\n if (docs && (status === 404 || status === 409)) this.rejected.emit();\n },\n });\n }\n}\n","import {\n Component, input, output, effect, untracked, OnInit,\n inject, ChangeDetectionStrategy, ChangeDetectorRef, DestroyRef, signal, computed, isDevMode,\n} from '@angular/core';\nimport { FormsModule } from '@angular/forms';\nimport { HttpClient } from '@angular/common/http';\nimport type { Observable } from 'rxjs';\nimport { FORGEROUTE_CONFIG, getErrorMessage, CheckboxComponent, PillTabsComponent, type PillTab, ToastService, ButtonComponent, TextInputComponent, TextareaInputComponent, DatetimeInputComponent, FormSectionComponent, AlertComponent } from '@forgeroute/ui';\nimport { serverMessage } from './server-message';\nimport { takeUntilDestroyed } from '@angular/core/rxjs-interop';\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 { DocumentSendOptionsComponent } from '../document-send-options/document-send-options.component';\nimport type {\n DocumentSendValue, DocumentSendPurpose, DocumentSigningOptions, DocumentPreviewHandler, DocumentPreviewContact, DocumentSeed, DocumentEnvelopeTarget, DocumentOption,\n} from '../document-send-options/document-send-options.component';\nimport { DocumentMergeCheckComponent } from '../document-send-options/document-merge-check.component';\nimport { forwardContext } from '../document-send-options/document-signers';\nimport type { ContextTypeDef, DocumentSignersConfig, MergeCheckFn, SendContext } from '../document-send-options/document-signers';\nimport { VideoLinkSettingsComponent } from '../video-link-settings/video-link-settings.component';\nimport { applyVideoLine, videoLinkWithDefaults, videoMessageLine } from '../video-link-settings/video-link';\nimport type { VideoDefaults } from '../video-link-settings/video-link';\nimport {\n DEFAULT_DOCUMENT_SUBJECT, buildDocumentsPayload, sendDocumentRequest, sendExistingEnvelope, signersContextBodyFields, signingBodyFields,\n} from './send-documents';\nimport type { DocumentSentMessage, 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/** A stable empty list (a child input must not receive a new array on every check). */\nconst NO_DOCUMENTS: DocumentOption[] = [];\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, VideoLinkSettingsComponent, DocumentMergeCheckComponent, AlertComponent],\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 &amp; 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 || videoLengthError\"\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\">&times;</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 } @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 [signing]=\"documentSigningOptions()\"\n [signers]=\"documentSigners()\"\n [envelope]=\"documentEnvelope()\"\n [documentPreview]=\"documentPreview()\"\n [previewContact]=\"previewContact()\"\n [previewSendContext]=\"sendContext()\"\n [showErrors]=\"true\"\n (valueChange)=\"onDocumentsChange($event)\"\n (invalidChange)=\"onDocumentsInvalid($event)\" />\n <ui-document-merge-check\n [enabled]=\"documentMergeCheck()\"\n [documents]=\"mergeDocuments\"\n [customerId]=\"contactId()\"\n [context]=\"forwardedContext()\"\n (warningsChange)=\"onMergeWarnings($event)\" />\n <!-- One live region, at most ONE warning banner (every current non-blocking warning is a line in it). -->\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 }\n @if (warningLines.length > 0) {\n <ui-alert variant=\"warning\" title=\"Heads up\" data-testid=\"send-warnings\">\n <ul class=\"sm-warn-list\">\n @for (w of warningLines; track w.key) { <li [attr.data-testid]=\"w.testId\">{{ w.text }}</li> }\n </ul>\n </ui-alert>\n }\n </div>\n }\n\n <!-- Video: the LAST section. The picker, and once a video is attached its settings, Watch Preview and Remove Video. -->\n @if (mode === 'compose') {\n <ui-form-section label=\"Video\" [divider]=\"true\">\n @for (k of [videoPickerKey]; track k) {\n <ui-video-picker (videoSelected)=\"onVideoSelected($event)\" />\n }\n @if (videoPicked && videoDefaults()) {\n <ui-video-link-settings\n [video]=\"videoPicked\"\n [defaults]=\"videoDefaults()!\"\n [contactId]=\"contactId()\"\n (linkChange)=\"onVideoLink($event)\"\n (removed)=\"removeVideo()\" />\n }\n </ui-form-section>\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-warn-list { margin: 0; padding-left: 18px; display: flex; flex-direction: column; gap: 2px; }\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); -webkit-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: 85vh; 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 /** Builds the success toast of a document send (default texts otherwise). */\n readonly documentSentMessage = input<DocumentSentMessage | 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<DocumentSeed[]>([]);\n /**\n * Turns on the signer logic for documents (informational documents, the multi-signer rule). A document with several customer signers\n * can't be texted (the link goes to the first signer only): the section says \"Send it by email.\" and Send stays off. `null` = unchanged.\n */\n readonly documentSigners = input<DocumentSignersConfig | null>(null);\n /** Values the host has for THIS send; only the keys declared in `documentContextTypes` are sent, as `documents.context`. */\n readonly sendContext = input<SendContext | null>(null);\n /** The context keys the app's backend accepts. */\n readonly documentContextTypes = input<ContextTypeDef[]>([]);\n /** `true` = check (default route `POST <baseUrl>/documents/merge-check`), a function = the host's own check; shows a non-blocking \"fields that will be empty\" warning. */\n readonly documentMergeCheck = input<boolean | MergeCheckFn>(false);\n /** An existing envelope to text: its documents are fixed and it is sent (not created). Payload `documents.envelopeId`. */\n readonly documentEnvelope = input<DocumentEnvelopeTarget | null>(null);\n /** Shows \"Expiry and Reminders\" for the texted signing link (see `DocumentSigningOptions`); sent as `documents.expiryDays` / `reminderIntervalDays` / `maxReminders`. `null` = off. */\n readonly documentSigningOptions = input<DocumentSigningOptions | null | undefined>(null);\n /** \"Preview Document\" button(s) in the Documents section call this; `null` hides them. */\n readonly documentPreview = input<DocumentPreviewHandler | null>(null);\n /** The recipient handed to `documentPreview` (second argument): the panel's contact inputs, `null` while all four are blank. */\n readonly previewContact = computed<DocumentPreviewContact | null>(() => {\n const c = { id: this.contactId(), name: this.contactName(), email: this.contactEmail(), phone: this.contactPhone() };\n return c.id || c.name || c.email || c.phone ? c : null;\n });\n /**\n * Turns on the video settings (Playback, Call-to-Action, Hide reactions, Watch Preview, Remove Video) once a video is attached, starting\n * from these defaults, and makes the video link carry them. `null` (default) = the plain share link is inserted, as before.\n * A text is limited to 320 characters: a link with settings is longer, and Send stays off while the message is over the limit.\n */\n readonly videoDefaults = input<VideoDefaults | null>(null);\n\n readonly sent = output<void>();\n readonly phoneAdded = output<string>();\n readonly validChange = output<boolean>();\n /** A document send was refused because the document / envelope changed meanwhile (404 / 409): the host refreshes its list. */\n readonly rejected = output<void>();\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 /** Lines of the merge check (\"These fields will be left empty: …\"). */\n mergeWarnings: string[] = [];\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 // A context key the app did not declare is never sent; say so while developing.\n effect(() => {\n const { dropped } = forwardContext(this.sendContext(), this.documentContextTypes());\n if (dropped.length > 0 && isDevMode()) console.warn(`ui-send-message-panel: sendContext keys not listed in documentContextTypes are not sent: ${dropped.join(', ')}`);\n });\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 /** The declared context keys with a value (`documents.context`). */\n readonly forwardedContext = computed<SendContext | undefined>(() => forwardContext(this.sendContext(), this.documentContextTypes()).context);\n /** The chosen documents the merge check looks at. */\n get mergeDocuments(): DocumentOption[] { return this.documentsValue?.documents ?? NO_DOCUMENTS; }\n onMergeWarnings(lines: string[]) { this.mergeWarnings = lines; this.cdr.markForCheck(); }\n /** The lines of the ONE warning banner: what choosing a document removed, the merge check. */\n get warningLines(): { key: string; text: string; testId: string | null }[] {\n const out: { key: string; text: string; testId: string | null }[] = [];\n if (this.hasDocuments && this.docsRemovedNote) out.push({ key: 'removed', text: this.docsRemovedNote, testId: 'documents-removed-note' });\n if (this.hasDocuments) this.mergeWarnings.forEach((text, i) => out.push({ key: `merge${i}`, text, testId: 'merge-check-warning' }));\n return out;\n }\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 // A video link with settings can push the text over the SMS limit (the plain-link path truncates instead).\n if (this._videoLine && this.body.length > this.MAX_LENGTH) 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 /** Why Send is off when the video link pushes the text over the SMS limit ('' otherwise). */\n get videoLengthError(): string {\n return this._videoLine && this.body.length > this.MAX_LENGTH\n ? `The video link makes this text longer than ${this.MAX_LENGTH} characters. Shorten the message or remove the video.` : '';\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.discardVideo();\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 /** The video attached through the settings flow (null = none) and the line it put into the message. */\n videoPicked: VideoMessage | null = null;\n /** Bumped to re-create the video picker (it keeps its own selection and has no clear output). */\n videoPickerKey = 0;\n private _videoLine = '';\n\n /** The picker's output: with `videoDefaults` the video gets its settings section, otherwise the plain link is inserted as before. */\n onVideoSelected(v: VideoMessage) {\n if (!this.videoDefaults()) { this.insertVideo(v); return; }\n if (!v.shareLink) return;\n this.videoPicked = v;\n // The first link is written here (an event, not change detection): the video's own defaults plus the recipient.\n this.onVideoLink(videoLinkWithDefaults(v.shareLink, this.videoDefaults()!, this.contactId()));\n this.cdr.detectChanges();\n }\n\n /** The settings section's output: rewrites the video line of the message with the current link (the rest of the message is untouched). */\n onVideoLink(link: string) {\n const v = this.videoPicked;\n if (!v || !link) return;\n const next = videoMessageLine(v.title, link);\n this.body = applyVideoLine(this.body, this._videoLine, next);\n this._videoLine = next;\n this.notifyValid();\n }\n\n /** \"Remove Video\": takes the video line out of the message and brings the picker back. */\n removeVideo() {\n this.body = applyVideoLine(this.body, this._videoLine, '');\n this.discardVideo();\n this.notifyValid();\n }\n\n private discardVideo() {\n if (!this.videoPicked && !this._videoLine) return;\n this.videoPicked = null; this._videoLine = ''; this.videoPickerKey++;\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 = buildDocumentsPayload(docs, 'TEXT', 'ESIGN', { context: this.forwardedContext(), envelopeId: this.documentEnvelope()?.id });\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 && this.documentEnvelope() ? sendExistingEnvelope(this.http, this.apiBase, this.documentEnvelope()!.id, {\n subject: DEFAULT_DOCUMENT_SUBJECT, message: payload.body,\n customerId: payload.contactId, customerName: payload.contactName, customerEmail: this.contactEmail() || undefined,\n customerPhone: payload.contactPhone,\n purpose: 'ESIGN', delivery: 'TEXT',\n ...signingBodyFields(payload.documents!),\n ...signersContextBodyFields(payload.documents!),\n ...(payload.templateId ? { templateId: payload.templateId } : {}),\n })\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 ...signingBodyFields(payload.documents!),\n ...signersContextBodyFields(payload.documents!),\n }, payload.templateId)\n : this.http.post(`${this.apiBase}/text-messages`, payload);\n req$.pipe(takeUntilDestroyed(this.destroyRef)).subscribe({\n next: () => {\n const sentText = docs ? this.documentSentMessage()?.({\n titles: docs.documents.map(d => d.title), envelopeName: this.documentEnvelope()?.name, channel: 'text',\n informational: docs.signerInfo?.informational === true,\n }) : undefined;\n this.toast.show(sentText ?? (docs && this.documentEnvelope() ? `Envelope \"${this.documentEnvelope()!.name}\" sent` : 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') : (serverMessage(err) || 'Failed to send text message'));\n this.loading = false; this.cdr.detectChanges();\n const status = (err as { status?: number } | null)?.status;\n if (docs && (status === 404 || status === 409)) this.rejected.emit();\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, type ComboboxOption, type ComboboxSelection, SidePanelComponent, ToggleBadgeComponent, ContactPickerComponent, type ContactLookup } from '@forgeroute/ui';\nimport type { Observable } from 'rxjs';\nimport type {\n DocumentSendPurpose, DocumentSigningOptions, DocumentPreviewHandler, DocumentSeed, DocumentEnvelopeTarget,\n} from '../document-send-options/document-send-options.component';\nimport type { ContextTypeDef, DocumentSignersConfig, MergeCheckFn, SendContext } from '../document-send-options/document-signers';\nimport type { VideoDefaults } from '../video-link-settings/video-link';\nimport type { DocumentSentMessage } from './send-documents';\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 [wide]=\"wide()\"\n cancelLabel=\"Cancel\"\n [primaryLabel]=\"!loading() && activeContact() ? (sendLabel() || (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 [documentSigningOptions]=\"documentSigningOptions()\"\n [documentSigners]=\"documentSigners()\"\n [sendContext]=\"sendContext()\"\n [documentContextTypes]=\"documentContextTypes()\"\n [documentMergeCheck]=\"documentMergeCheck()\"\n [documentEnvelope]=\"documentEnvelope()\"\n [emailEnabled]=\"emailEnabled()\"\n [documentSentMessage]=\"documentSentMessage()\"\n (rejected)=\"rejected.emit()\"\n (mapFieldsClick)=\"mapFieldsClick.emit($event)\"\n [docuSignConnected]=\"docuSignConnected()\"\n [documentPreview]=\"documentPreview()\"\n [videoDefaults]=\"videoDefaults()\"\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 [documentSigningOptions]=\"documentSigningOptions()\"\n [documentSigners]=\"documentSigners()\"\n [sendContext]=\"sendContext()\"\n [documentContextTypes]=\"documentContextTypes()\"\n [documentMergeCheck]=\"documentMergeCheck()\"\n [documentEnvelope]=\"documentEnvelope()\"\n [documentSentMessage]=\"documentSentMessage()\"\n (rejected)=\"rejected.emit()\"\n [documentPreview]=\"documentPreview()\"\n [videoDefaults]=\"videoDefaults()\"\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<DocumentSeed[]>([]);\n /**\n * Turns on the signer logic of the Documents section (docs/send-documents-contract.md, Addendum 2026-10-19), all off when `null` (default):\n * - a document with MORE THAN ONE customer signer shows one required contact picker per further role (Signer 1 is the recipient), Send\n * stays off until every role has a contact, and `documents.signers` (complete, ordered by role) is added to the payload;\n * - a document that requires the sender's countersignature shows one read-only line (\"Countersigned by <name> after all signers.\");\n * - a document with no signature fields (`signerRoles: []`) is informational: no signers, countersign line, DocuSign or signing options -\n * the recipient gets a view link;\n * - DocuSign is offered only for a signed document with one customer signer, and the countersign checkbox is replaced by the document's setting.\n * The roles come from the `GET /documents` row (`signerRoles`) or from `resolveRoles`. A multi-signer document can't be texted.\n */\n readonly documentSigners = input<DocumentSignersConfig | null>(null);\n /** Values the host has for THIS send (e.g. `{ propertyId }`); only keys listed in `documentContextTypes` are sent, as `documents.context`. */\n readonly sendContext = input<SendContext | null>(null);\n /** The context keys the app's backend accepts. */\n readonly documentContextTypes = input<ContextTypeDef[]>([]);\n /**\n * The pre-send \"fields that will be empty\" check, for documents with mapped data fields only (non-blocking warning): `true` = default route\n * `POST <baseUrl>/documents/merge-check`, a function = your own call, `false` (default) = off.\n */\n readonly documentMergeCheck = input<boolean | MergeCheckFn>(false);\n /**\n * Sends an EXISTING envelope: its documents are fixed (no search, no remove), the recipient is chosen in the panel, and the request is sent\n * (`POST <baseUrl>/documents/envelopes/{id}/send`, or your `sendEmail` / `sendText` with `documents.envelopeId`) instead of created.\n * Turns the Documents section on by itself. `null` (default) = a new request from the chosen documents.\n */\n readonly documentEnvelope = input<DocumentEnvelopeTarget | null>(null);\n /** Builds the success toast of a document send (`\"<title>\" sent for signature`, `Envelope \"<name>\" sent` …); the default texts apply when unset. */\n readonly documentSentMessage = input<DocumentSentMessage | null>(null);\n /** false = the app's email sending is switched off (Communication settings): the Email tab says so and Send stays off. Default true. */\n readonly emailEnabled = input(true);\n /** The footer button text instead of `Send Email` / `Send Text` (e.g. `Send Document`, `Send Envelope`). */\n readonly sendLabel = input('');\n /** The 620px wide panel (a long form with signers). Default false. */\n readonly wide = input(false);\n /**\n * Shows \"Expiry and Reminders\" in the Documents section (Expiry 1-90 days, Reminder Interval 1-30 days, Max Reminders 0-10; required\n * whole numbers) for native e-signature requests, on the Email tab (purpose e-signature or both) and the Text tab. Pass `{}` for the\n * defaults 7 / 2 / 3 or set any of `expiryDays`, `reminderIntervalDays`, `maxReminders`. The values are sent as\n * `documents.expiryDays` / `reminderIntervalDays` / `maxReminders`. `null` (default) = not shown, nothing sent.\n */\n readonly documentSigningOptions = input<DocumentSigningOptions | null | undefined>(null);\n /**\n * Email tab: the host says DocuSign is connected (the library never calls `/docusign/status`). Offers \"Native E-Sign / Send via DocuSign\"\n * while exactly one document is chosen; DocuSign sends through `POST <baseUrl>/docusign/send` (or your `sendEmail`, which then gets\n * `documents.provider === 'DOCUSIGN'`). A document row/`initialDocuments` entry with `requiresCountersign: true` also offers \"Require my\n * countersignature after client signs\". Default `false` = native only.\n */\n readonly docuSignConnected = input(false);\n /**\n * \"Preview Document\" in the Documents section: called with the chosen document when its button is clicked. The library has no route\n * of its own — the host opens its viewer. Open a tab synchronously inside this function (Safari blocks `window.open` after an await).\n * `null` (default) = no preview button.\n */\n readonly documentPreview = input<DocumentPreviewHandler | null>(null);\n /**\n * The \"Video\" section (always the last section of the Email and Text forms): once a video is attached it shows Playback, Call-to-Action\n * (+ URL / label), Hide reactions, Watch Preview and Remove Video, starting from these defaults (your Communication settings), and the\n * link in the message carries them (`buildVideoLink`). `null` (default) = the plain share link is inserted, with no settings.\n */\n readonly videoDefaults = input<VideoDefaults | null>(null);\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.documentEnvelope()) && 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 /** A document / envelope send was refused because it changed meanwhile (404 / 409): the host refreshes its list. */\n readonly rejected = output<void>();\n /**\n * With `documentSigners` on: the user clicked \"Place your countersignature box\" under the countersign line of a document that requires a\n * countersignature but whose row says `hasCountersignPlacement: false` (docs/send-documents-contract.md, Addendum 2026-10-19 (b)).\n * Payload = that document `{ id, title }`; the host navigates to Map Data Fields. Never emitted when the flag is true / absent.\n */\n readonly mapFieldsClick = output<{ id: string; title: 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"],"mappings":"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"}
1
+ {"version":3,"file":"forgeroute-ui-messaging.mjs","names":[],"sources":["../../../projects/forgeroute-ui/messaging/src/video-picker/video-picker.component.ts","../../../projects/forgeroute-ui/messaging/src/template-picker/template-picker.component.ts","../../../projects/forgeroute-ui/messaging/src/send-message-panel/server-message.ts","../../../projects/forgeroute-ui/messaging/src/document-send-options/document-signers.ts","../../../projects/forgeroute-ui/messaging/src/document-send-options/document-send-options.component.ts","../../../projects/forgeroute-ui/messaging/src/document-send-options/document-signers.component.ts","../../../projects/forgeroute-ui/messaging/src/document-send-options/document-merge-check.component.ts","../../../projects/forgeroute-ui/messaging/src/video-link-settings/video-link.ts","../../../projects/forgeroute-ui/messaging/src/video-link-settings/video-link-settings.component.ts","../../../projects/forgeroute-ui/messaging/src/send-message-panel/send-documents.ts","../../../projects/forgeroute-ui/messaging/src/send-message-panel/merge-tags.ts","../../../projects/forgeroute-ui/messaging/src/send-message-panel/send-email.component.ts","../../../projects/forgeroute-ui/messaging/src/send-message-panel/send-text.component.ts","../../../projects/forgeroute-ui/messaging/src/send-message-panel/send-message-panel.component.ts"],"sourcesContent":["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, ErrorComponent, UiFieldValidity, createFieldReveal, requiredIssue } from '@forgeroute/ui';\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\">&times;</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, ToastService, ErrorComponent, UiFieldValidity, createFieldReveal, requiredIssue } from '@forgeroute/ui';\nimport { Subject, debounceTime, switchMap, of, catchError } from 'rxjs';\nimport { takeUntilDestroyed } from '@angular/core/rxjs-interop';\nimport { FormsModule } from '@angular/forms';\n\nexport interface TemplateItem {\n id: string;\n name: string;\n subject: string | null;\n body: string;\n isActive?: boolean;\n /** Set when the API says whether this is a built-in system template (true) or one the company wrote (false); drives the System / Custom badge. Absent = no badge. */\n isSystem?: 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-title\">\n <span class=\"option-name\">{{ tpl.name }}</span>\n @if (tpl.isSystem !== undefined) {\n <span class=\"option-badge\" [class.system]=\"tpl.isSystem\">{{ tpl.isSystem ? 'System' : 'Custom' }}</span>\n }\n </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-title { display: flex; align-items: center; gap: 6px; min-width: 0; }\n .option-badge {\n flex-shrink: 0; font-size: 0.6875rem; font-weight: 600; line-height: 1; padding: 3px 7px; border-radius: 999px;\n background: #f1f5f9; color: #475569; border: 1px solid #e2e8f0;\n }\n .option-badge.system { background: #f3e8ff; color: #6b21a8; border-color: #e9d5ff; }\n .option-name { min-width: 0; 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 ...(typeof t.isSystem === 'boolean' ? { isSystem: t.isSystem } : {}),\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 { HttpErrorResponse } from '@angular/common/http';\n\n/** The `{ message }` a backend put in a failed response body, or '' when there is none (callers then use their own fixed text). */\nexport function serverMessage(err: unknown): string {\n if (!(err instanceof HttpErrorResponse)) return '';\n const m = err.error?.message;\n return typeof m === 'string' ? m.trim() : '';\n}\n","import type { Observable } from 'rxjs';\n\n/**\n * Types and pure helpers for the signer side of sending documents (docs/send-documents-contract.md, Addendum 2026-10-19).\n * Everything here is app-agnostic: the roles are `signer1 .. signer4`, labels / names come from the host.\n */\n\n/** A CUSTOMER signer role. The countersigner (the sending user) is never a role: it is the document's own setting. */\nexport type SignerRole = 'signer1' | 'signer2' | 'signer3' | 'signer4';\n\n/** All roles, in signing order (the order is strictly sequential by role number). */\nexport const SIGNER_ROLES: readonly SignerRole[] = ['signer1', 'signer2', 'signer3', 'signer4'];\n\n/** The part of a document the host's `resolveRoles` callback is called with. */\nexport interface DocumentRolesTarget {\n id: string;\n title: string;\n fileName: string | null;\n}\n\n/**\n * Turns the signer section on (`documentSigners` of `ui-send-message-panel`). `null` / not passed = the panel is the plain single-recipient\n * panel and nothing about signers is shown or sent.\n */\nexport interface DocumentSignersConfig {\n /** Largest number of customer signers the panel accepts (roles above it are ignored). Default 4. */\n maxSigners?: number;\n /** Labels per role. Default `Signer 1` .. `Signer 4`. */\n roleLabels?: Partial<Record<SignerRole, string>>;\n /** May one person fill two roles? Default false. */\n allowSameContactInRoles?: boolean;\n /** Must every further signer have an email address (the chain emails each signer)? Default true; `false` only lets Send through with a warning-free row (the server then decides). */\n requireSignerEmail?: boolean;\n /** Name shown in the read-only countersign line (\"Countersigned by <name> after all signers.\"). Default `you`. */\n countersignerName?: string;\n /**\n * Reads the customer roles a document defines when its `GET /documents` row has no `signerRoles` key (an app that keeps its\n * placements elsewhere). Called once per chosen document, and again on \"Retry\". A failure blocks Send and offers a retry. An empty\n * list = the document has no signature fields (informational).\n */\n resolveRoles?: (doc: DocumentRolesTarget) => Observable<SignerRole[]>;\n}\n\n/** One entry of `documents.signers` (present only for a document with MORE THAN ONE customer signer, always complete). */\nexport interface SendSigner {\n role: SignerRole;\n contactId: string;\n name: string;\n email: string | null;\n /** 1..N, equal to the role number (strictly sequential). */\n order: number;\n}\n\n/** Values the host has for THIS send, keyed by the keys it declares in `documentContextTypes` (e.g. `{ propertyId: '…' }`). */\nexport type SendContext = Record<string, string>;\n\n/** One context key the app's backend accepts. */\nexport interface ContextTypeDef { key: string; label: string }\n\n/** What `documentMergeCheck` sends (default route `POST <baseUrl>/documents/merge-check`). */\nexport interface MergeCheckRequest {\n documentIds: string[];\n customerId?: string;\n signers?: SendSigner[];\n context?: SendContext;\n}\n\n/** One data field that would be left empty. */\nexport interface MergeCheckEmptyField {\n key: string;\n label: string;\n page?: number;\n /** `NO_CONTEXT` | `NO_DATA` | `UNKNOWN_PATH` (other values are shown generically). */\n reason?: string;\n /** Optional plain-language reason from the server (\"no property selected\"); wins over the generic text of `reason`. */\n detail?: string;\n}\n\nexport interface MergeCheckResult {\n documents: { documentId: string; title?: string; empty: MergeCheckEmptyField[] }[];\n}\n\n/** A host function that runs the merge check itself (instead of the default route). */\nexport type MergeCheckFn = (req: MergeCheckRequest) => Observable<MergeCheckResult>;\n\n/** The structural part of a chosen document the helpers below read. */\nexport interface SignerDocLike {\n id: string;\n title: string;\n requiresCountersign?: boolean;\n /** The row says whether the countersign box is placed on the document. `false` (with `requiresCountersign`) offers the \"Place your countersignature box\" link; `undefined` = unknown = no link. */\n hasCountersignPlacement?: boolean;\n signerRoles?: SignerRole[];\n hasMapping?: boolean;\n hasDataFields?: boolean;\n}\n\n/** Where the roles of a document without a `signerRoles` key stand when the host resolves them. */\nexport interface RoleState { status: 'loading' | 'ready' | 'error'; roles: SignerRole[] }\n\n/** What the signer logic concluded for the chosen documents (only on the value when `documentSigners` is on). */\nexport interface DocumentSignerInfo {\n /** `ready` once every document's roles are known; `loading` / `error` block Send. */\n status: 'loading' | 'ready' | 'error';\n /** Every chosen document has no signature fields: the send is informational (view-only). */\n informational: boolean;\n /** Customer roles of the ONE chosen document (always starting at signer1, no gaps). Empty for several documents / informational. */\n roles: SignerRole[];\n /** Exactly one document, with more than one customer signer. */\n multiSigner: boolean;\n /** A document that is sent for signing requires the sender's countersignature. */\n countersign: boolean;\n /**\n * Only present (and non-empty) when a signable chosen document requires the countersignature and its row says the countersign box is NOT\n * placed (`hasCountersignPlacement === false`): the documents the \"Place your countersignature box\" link is offered for.\n */\n countersignUnplaced?: { id: string; title: string }[];\n /** Several documents are chosen and one of them has several signers (it still goes to the one recipient). */\n multiDocumentNote: boolean;\n /** Under the document chip: what the mapping covers ('' when unknown). */\n fieldsLine: string;\n}\n\nexport const SIGNER_ROLE_LABEL_DEFAULT = (role: SignerRole): string => `Signer ${roleNumber(role)}`;\n\nexport function roleNumber(role: SignerRole): number { return Number(role.slice(6)); }\n\n/** The label of a role: the host's `roleLabels` entry, else `Signer N`. */\nexport function signerRoleLabel(role: SignerRole, cfg?: DocumentSignersConfig | null): string {\n const custom = cfg?.roleLabels?.[role]?.trim();\n return custom || SIGNER_ROLE_LABEL_DEFAULT(role);\n}\n\n/** Valid roles only (`signer1..N`, N ≤ maxSigners), unique, sorted. Anything else is dropped. */\nexport function normalizeRoles(raw: unknown, maxSigners = 4): SignerRole[] {\n if (!Array.isArray(raw)) return [];\n const max = Math.min(Math.max(Math.floor(maxSigners) || 4, 1), SIGNER_ROLES.length);\n const set = new Set<SignerRole>();\n for (const r of raw) {\n if (typeof r === 'string' && (SIGNER_ROLES as readonly string[]).includes(r) && roleNumber(r as SignerRole) <= max) set.add(r as SignerRole);\n }\n return [...set].sort((a, b) => roleNumber(a) - roleNumber(b));\n}\n\n/**\n * The customer roles a send needs from a document's saved roles: signer1 is always the recipient, and the roles run without gaps up to\n * the highest saved one (a partial list is never sent). `[]` stays `[]` (no signature fields).\n */\nexport function effectiveRoles(saved: SignerRole[]): SignerRole[] {\n if (saved.length === 0) return [];\n const highest = Math.max(...saved.map(roleNumber));\n return SIGNER_ROLES.slice(0, highest);\n}\n\n/** The roles of one document, or null while they are not known yet. A document nobody could describe (older backend) counts as one signer. */\nfunction rolesOf(doc: SignerDocLike, cfg: DocumentSignersConfig, states: Record<string, RoleState>): RoleState {\n if (doc.signerRoles !== undefined) return { status: 'ready', roles: normalizeRoles(doc.signerRoles, cfg.maxSigners) };\n if (cfg.resolveRoles) {\n const state = states[doc.id];\n return state ? { ...state, roles: normalizeRoles(state.roles, cfg.maxSigners) } : { status: 'loading', roles: [] };\n }\n return { status: 'ready', roles: ['signer1'] };\n}\n\n/** True when the document has mapped data fields (the pre-send \"fields that will be empty\" check applies to it only). */\nexport function hasMappedDataFields(doc: SignerDocLike): boolean {\n return doc.hasDataFields !== undefined ? doc.hasDataFields === true : doc.hasMapping === true;\n}\n\n/** What the signer logic concludes for the chosen documents. `null` = `documentSigners` is off or nothing is chosen. */\nexport function describeDocumentSigners(\n docs: SignerDocLike[], states: Record<string, RoleState>, cfg: DocumentSignersConfig | null | undefined,\n): DocumentSignerInfo | null {\n if (!cfg || docs.length === 0) return null;\n const per = docs.map(d => ({ doc: d, state: rolesOf(d, cfg, states) }));\n const base: DocumentSignerInfo = {\n status: 'ready', informational: false, roles: [], multiSigner: false, countersign: false, multiDocumentNote: false, fieldsLine: '',\n };\n if (per.some(p => p.state.status === 'error')) return { ...base, status: 'error' };\n if (per.some(p => p.state.status === 'loading')) return { ...base, status: 'loading' };\n const signable = per.filter(p => p.state.roles.length > 0);\n const informational = signable.length === 0;\n const info: DocumentSignerInfo = { ...base, informational, countersign: signable.some(p => p.doc.requiresCountersign === true) };\n const unplaced = signable\n .filter(p => p.doc.requiresCountersign === true && p.doc.hasCountersignPlacement === false)\n .map(p => ({ id: p.doc.id, title: p.doc.title }));\n if (unplaced.length > 0) info.countersignUnplaced = unplaced;\n if (docs.length === 1) {\n const roles = effectiveRoles(per[0].state.roles);\n info.roles = roles;\n info.multiSigner = roles.length > 1;\n const d = per[0].doc;\n if (roles.length > 1) info.fieldsLine = `Fields placed for ${roles.length} signers`;\n else if (informational && d.hasMapping !== undefined) info.fieldsLine = 'No signature fields placed. Sent for viewing only.';\n } else {\n info.multiDocumentNote = signable.some(p => effectiveRoles(p.state.roles).length > 1);\n }\n return info;\n}\n\n/** The read-only countersign line ('' = not shown). */\nexport function countersignLine(info: DocumentSignerInfo | null, cfg: DocumentSignersConfig | null | undefined, viaDocuSign: boolean): string {\n if (!info || !cfg || info.informational || !info.countersign) return '';\n if (viaDocuSign) return 'You countersign after the client signs.';\n return `Countersigned by ${cfg.countersignerName?.trim() || 'you'} after all signers.`;\n}\n\nexport interface SignerPick { id: string; name: string; email?: string | null }\n\n/** The complete `signers` list (recipient first), or null while any role has no contact. */\nexport function buildSigners(roles: SignerRole[], recipient: SignerPick, picks: Partial<Record<SignerRole, SignerPick | null>>): SendSigner[] | null {\n if (roles.length < 2) return null;\n const out: SendSigner[] = [];\n for (const role of roles) {\n const p = role === 'signer1' ? recipient : picks[role];\n if (!p) return null;\n out.push({ role, contactId: p.id, name: p.name, email: p.email ? p.email : null, order: roleNumber(role) });\n }\n return out;\n}\n\n/**\n * The first problem per role (`''` = fine) in signing order: empty → `<Role label> is required.`, no email → `<Name> has no email address.`,\n * the same person twice (unless allowed) → `<Name> is already <Earlier role label>.` on the later row.\n */\nexport function signerProblems(\n roles: SignerRole[], recipient: SignerPick, picks: Partial<Record<SignerRole, SignerPick | null>>, cfg: DocumentSignersConfig | null | undefined,\n): Partial<Record<SignerRole, string>> {\n const out: Partial<Record<SignerRole, string>> = {};\n const seen = new Map<string, SignerRole>();\n for (const role of roles) {\n const p = role === 'signer1' ? recipient : picks[role];\n if (!p) { out[role] = `${signerRoleLabel(role, cfg)} is required.`; continue; }\n if (cfg?.requireSignerEmail !== false && !(p.email ?? '').trim()) { out[role] = `${p.name} has no email address.`; }\n const earlier = seen.get(p.id);\n if (earlier && !cfg?.allowSameContactInRoles && !out[role]) out[role] = `${p.name} is already ${signerRoleLabel(earlier, cfg)}.`;\n if (!earlier) seen.set(p.id, role);\n }\n return out;\n}\n\n/** Only the declared context keys, with non-blank values; `undefined` when nothing is left. */\nexport function forwardContext(ctx: SendContext | null | undefined, types: readonly ContextTypeDef[]): { context?: SendContext; dropped: string[] } {\n const dropped: string[] = [];\n if (!ctx) return { dropped };\n const allowed = new Set(types.map(t => t.key));\n const out: SendContext = {};\n for (const [k, v] of Object.entries(ctx)) {\n if (!allowed.has(k)) { dropped.push(k); continue; }\n if (typeof v === 'string' && v.trim() !== '') out[k] = v;\n }\n return Object.keys(out).length ? { context: out, dropped } : { dropped };\n}\n\n/** One merge-check field as text: `Property Address (no property selected)`. */\nexport function mergeFieldText(f: MergeCheckEmptyField): string {\n const reason = f.detail?.trim()\n || (f.reason === 'NO_CONTEXT' ? 'not available for this send'\n : f.reason === 'NO_DATA' ? 'no value on record'\n : f.reason === 'UNKNOWN_PATH' ? 'unknown field' : '');\n return reason ? `${f.label} (${reason})` : f.label;\n}\n\n/** The warning lines of a merge-check answer: one line for one document, `<title>: …` per document for several. Nothing when nothing is empty. */\nexport function mergeWarningLines(res: MergeCheckResult | null | undefined, docTitles: Record<string, string>): string[] {\n const docs = (res?.documents ?? []).filter(d => (d.empty ?? []).length > 0);\n const prefixed = docs.length > 1 || (res?.documents?.length ?? 0) > 1;\n return docs.map(d => {\n const list = d.empty.map(mergeFieldText).join(', ');\n const title = d.title || docTitles[d.documentId] || '';\n return `${prefixed && title ? `${title}: ` : ''}These fields will be left empty: ${list}.`;\n });\n}\n\n/** The accessible name of the \"Place your countersignature box\" link of a document. */\nexport function countersignPlaceLabel(title: string): string { return `Place your countersignature box on ${title}`; }\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, take } from 'rxjs';\nimport type { Observable, Subscription } from 'rxjs';\nimport { FORGEROUTE_CONFIG, ComboboxComponent, type ComboboxOption, ChipComponent, SelectComponent, type SelectOption, ErrorComponent, ButtonComponent, CheckboxComponent, FormSectionComponent, FormRowComponent, HintComponent, RadioGroupComponent, type RadioOption, TextInputComponent, UiFieldValidity } from '@forgeroute/ui';\nimport { describeDocumentSigners, normalizeRoles } from './document-signers';\nimport type { DocumentSignerInfo, DocumentSignersConfig, RoleState, SendContext, SignerRole } from './document-signers';\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/** Who sends a signature request: the app's own e-signature (`NATIVE`) or DocuSign. */\nexport type DocumentSignatureProvider = 'NATIVE' | 'DOCUSIGN';\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 /** Only present (and true) when the row says the document needs the sender's countersignature (`requiresCountersign` on the `GET /documents` row): then a DocuSign send offers \"Require my countersignature after client signs\". */\n requiresCountersign?: boolean;\n /** Only with `documentSigners` and `requiresCountersign`: the row says whether the countersign box is placed. `false` shows the \"Place your countersignature box\" link after the countersign line (`mapFieldsClick`); absent = unknown = no link. */\n hasCountersignPlacement?: boolean;\n /** Only with `documentSigners`: the document's customer roles from the `GET /documents` row (`signerRoles`). `[]` = no signature fields (informational). */\n signerRoles?: SignerRole[];\n /** The row says the document has saved placements of any kind (drives the \"Fields placed for N signers\" line and the merge check). */\n hasMapping?: boolean;\n /** The row says the document has mapped DATA fields (the pre-send merge check applies to it); when absent, `hasMapping` stands in. */\n hasDataFields?: boolean;\n /** Only for an envelope's document (`DocumentSeed.status`). */\n status?: string;\n}\n\n/** A document the form starts with (`initialDocuments`). `hasFile` defaults to true. */\nexport interface DocumentSeed {\n id: string;\n title: string;\n fileName?: string | null;\n hasFile?: boolean;\n requiresCountersign?: boolean;\n hasCountersignPlacement?: boolean;\n signerRoles?: SignerRole[];\n hasMapping?: boolean;\n hasDataFields?: boolean;\n /** An envelope's document that is not `ACTIVE` (e.g. `ARCHIVED`) blocks the send: \"<title> is archived, so this envelope can't be sent. …\". */\n status?: string;\n}\n\n/**\n * An existing envelope the panel sends (`documentEnvelope`): its documents are fixed (no search, no remove) and the request is sent, not\n * created. Give `documents` when the host has read them already, or `loadDocuments` to let the panel read them (it shows the wait, and a\n * failure blocks Send with a Retry button). With neither, the envelope has no documents and cannot be sent.\n */\nexport interface DocumentEnvelopeTarget {\n id: string;\n /** Shown in the subject suggestion (\"Signature required: <name>\") and in the success toast. */\n name: string;\n documents?: DocumentSeed[];\n loadDocuments?: () => Observable<DocumentSeed[]>;\n}\n\n/** The document a host's `documentPreview` handler is called with. */\nexport interface DocumentPreviewTarget {\n id: string;\n title: string;\n fileName: string | null;\n}\n\n/** The recipient the panel currently holds (as the panel's `contactId` / `contactName` / `contactEmail` / `contactPhone` inputs). */\nexport interface DocumentPreviewContact {\n id: string;\n name: string;\n email: string;\n phone: string;\n}\n\n/**\n * Second argument of `documentPreview`: who the document is being sent to, so the host can preview it filled for that recipient.\n * `contact` is `null` when the panel has no recipient (all four contact inputs blank); `sendContext` is the panel's `sendContext` input\n * as given (not filtered by `documentContextTypes`) or `null`.\n */\nexport interface DocumentPreviewContext {\n contact: DocumentPreviewContact | null;\n sendContext: SendContext | null;\n}\n\n/**\n * Opens a document for preview (the host knows its own route / viewer). Called synchronously inside the click. The second argument\n * is new and optional to use: a handler that only declares `doc` keeps working.\n */\nexport type DocumentPreviewHandler = (doc: DocumentPreviewTarget, context: DocumentPreviewContext) => void;\n\n/**\n * Turns on the \"Expiry and Reminders\" settings of the document section and sets their starting values (the keys you leave out start at\n * the defaults below). `null` / not passed = the settings are not shown and nothing about them is sent.\n */\nexport interface DocumentSigningOptions {\n /** Days until an unsigned request expires: whole number 1-90 (default 7). */\n expiryDays?: number;\n /** Days between reminders: whole number 1-30 (default 2). */\n reminderIntervalDays?: number;\n /** How many reminders at most: whole number 0-10 (default 3). */\n maxReminders?: number;\n}\n\n/** Defaults and limits of the signing settings (the same limits the servers enforce). */\nexport const DEFAULT_SIGNING_OPTIONS = { expiryDays: 7, reminderIntervalDays: 2, maxReminders: 3 } as const;\nexport const SIGNING_LIMITS = {\n expiryDays: { min: 1, max: 90, label: 'Expiry (days)' },\n reminderIntervalDays: { min: 1, max: 30, label: 'Reminder Interval (days)' },\n maxReminders: { min: 0, max: 10, label: 'Max Reminders' },\n} as const;\n\nexport const DOCUMENT_PROVIDER_OPTIONS: RadioOption[] = [\n { value: 'NATIVE', label: 'Native E-Sign' },\n { value: 'DOCUSIGN', label: 'Send via DocuSign' },\n];\n\n/** A whole number in the string, or null (blank, decimals, signs and exponents are not whole numbers here). */\nfunction wholeNumber(raw: string): number | null {\n const t = (raw ?? '').trim();\n return /^\\d+$/.test(t) ? Number(t) : null;\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 /** Only when the signing settings are shown (host `signing` input), valid and the request is a native e-signature one. */\n expiryDays?: number;\n reminderIntervalDays?: number;\n maxReminders?: number;\n /** Only when the provider choice is offered (`docuSignConnected` and exactly one document on the email channel). */\n provider?: DocumentSignatureProvider;\n /** Only with `provider: 'DOCUSIGN'`. */\n docuSign?: { countersign: boolean };\n /** Only when `signers` (the host's `documentSigners`) is on and a document is chosen: what the signer logic concluded. Never part of the payload. */\n signerInfo?: DocumentSignerInfo;\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/** Purpose labels when nothing is signed (every chosen document has no signature fields): the recipient gets a view link. */\nconst INFORMATIONAL_PURPOSE_LABELS: Record<string, string> = {\n ESIGN: 'Send a view link',\n BOTH: 'View link 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 * Optional parts, all off by default (hosts reach them through the same-named inputs of `ui-send-message-panel`):\n * - `signing` — \"Expiry and Reminders\" (expiry days, reminder interval, max reminders) for native e-signature requests;\n * - `docuSignConnected` — \"Native E-Sign / Send via DocuSign\" choice (email channel, exactly one document);\n * - `documentPreview` — \"Preview Document\" button(s) that call the host's handler.\n * - `signers` — the signer logic of the panel's `documentSigners` (a document's customer roles, informational documents, the\n * one-signer DocuSign rule, the document's own countersign setting); the value then carries `signerInfo`;\n * - `envelope` — an existing envelope: its documents are the fixed list (given, or read through `loadDocuments`).\n *\n * The signing settings are ONE collapsed summary line (\"Signing options: expires in 7 days, ...\") with a Change / Hide button.\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, when the purpose is \"Send as attachment\" and a chosen document has no stored file, or when a shown signing\n * setting is blank / out of range. 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: [\n FormsModule, ComboboxComponent, ChipComponent, SelectComponent, ErrorComponent, ButtonComponent, CheckboxComponent,\n FormSectionComponent, FormRowComponent, HintComponent, RadioGroupComponent, TextInputComponent,\n ],\n providers: [{ provide: UiFieldValidity, useExisting: forwardRef(() => DocumentSendOptionsComponent) }],\n template: `\n <div class=\"dso\" role=\"group\" [attr.aria-label]=\"label()\">\n @if (available()) {\n @if (locked()) {\n <p class=\"dso-locked-label\" data-testid=\"locked-documents-label\">Included documents</p>\n @if (envelopeStatus() === 'loading') {\n <div role=\"status\"><ui-hint data-testid=\"envelope-loading\">{{ envelopeReadMessage() }}</ui-hint></div>\n } @else if (envelopeStatus() === 'error') {\n <div class=\"dso-retry\">\n <ui-error errorId=\"dso-envelope-error\" [message]=\"envelopeReadMessage()\" />\n <ui-button variant=\"secondary\" size=\"sm\" (click)=\"retryEnvelope()\">Retry</ui-button>\n </div>\n }\n } @else {\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\n @if (selected().length > 0) {\n <!-- One row per document: the chip (or the plain title when the list is fixed) and, beside it, its small Preview button. -->\n <div class=\"dso-docs\" role=\"group\" aria-label=\"Chosen documents\">\n @for (d of selected(); track d.id) {\n <div class=\"dso-doc\">\n @if (locked()) {\n <!-- An envelope's documents are fixed: plain text, not a chip (a chip without a remove button would read as a toggle). -->\n <span class=\"dso-doc-title\">{{ d.title }}</span>\n } @else {\n <ui-chip class=\"dso-doc-chip\" [label]=\"d.title\" size=\"sm\" [dismissible]=\"true\" (dismiss)=\"remove(d.id)\" />\n }\n @if (documentPreview()) {\n <ui-button\n variant=\"secondary\" size=\"sm\"\n [ariaLabel]=\"selected().length === 1 ? undefined : 'Preview ' + d.title\"\n (click)=\"preview(d)\">{{ selected().length === 1 ? 'Preview Document' : 'Preview' }}</ui-button>\n }\n </div>\n }\n </div>\n @if (signerInfo()?.fieldsLine; as line) {\n <p class=\"dso-fields\" data-testid=\"fields-line\">{{ line }}</p>\n }\n }\n\n @if (signerInfo()?.status === 'loading') {\n <div role=\"status\"><ui-hint data-testid=\"signers-loading\">{{ signerReadMessage() }}</ui-hint></div>\n } @else if (signerInfo()?.status === 'error') {\n <div class=\"dso-retry\">\n <ui-error errorId=\"dso-roles-error\" [message]=\"signerReadMessage()\" />\n <ui-button variant=\"secondary\" size=\"sm\" (click)=\"retryRoles()\">Retry</ui-button>\n </div>\n }\n\n @if (providerChoiceShown()) {\n <ui-radio-group\n label=\"Signature provider\"\n orientation=\"horizontal\"\n [options]=\"providerOptions\"\n [ngModel]=\"provider()\"\n (ngModelChange)=\"setProvider($event)\" />\n }\n\n @if (provider() === 'DOCUSIGN') {\n <ui-hint>DocuSign emails the document to the recipient. The Subject and Message above are used for the DocuSign email.</ui-hint>\n @if (showCountersign()) {\n <ui-checkbox\n label=\"Require my countersignature after client signs\"\n [ngModel]=\"countersignChoice()\"\n (ngModelChange)=\"countersignChoice.set(!!$event)\" />\n }\n } @else 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() ? documentError() : ''\" />\n\n @if (signingVisible()) {\n <!-- Collapsed to one summary line: the defaults are valid, so most senders never open it. A broken value opens it by itself. -->\n <div class=\"dso-signing\" data-testid=\"signing-options\">\n <div class=\"dso-signing-line\">\n <span class=\"dso-signing-summary\" data-testid=\"signing-summary\">{{ signingSummary() }}</span>\n <ui-button\n variant=\"ghost\" size=\"xs\"\n [ariaExpanded]=\"signingExpanded()\"\n [ariaLabel]=\"signingExpanded() ? 'Hide signing options' : 'Change signing options'\"\n (click)=\"toggleSigning()\">{{ signingExpanded() ? 'Hide' : 'Change' }}</ui-button>\n </div>\n @if (signingExpanded()) {\n <ui-form-section>\n <!-- One column: three labelled number fields do not fit side by side in a 480px panel or on a phone. -->\n <ui-form-row [cols]=\"1\">\n <ui-text-input\n label=\"Expiry (days)\" type=\"number\" [required]=\"true\" [min]=\"1\" [max]=\"90\" [maxDecimals]=\"0\"\n [showErrors]=\"showErrors()\" [ngModel]=\"expiryDays()\" (ngModelChange)=\"expiryDays.set(asText($event))\" />\n <ui-text-input\n label=\"Reminder Interval (days)\" type=\"number\" [required]=\"true\" [min]=\"1\" [max]=\"30\" [maxDecimals]=\"0\"\n [showErrors]=\"showErrors()\" [ngModel]=\"reminderIntervalDays()\" (ngModelChange)=\"reminderIntervalDays.set(asText($event))\" />\n <ui-text-input\n label=\"Max Reminders\" type=\"number\" [required]=\"true\" [min]=\"0\" [max]=\"10\" [maxDecimals]=\"0\"\n [showErrors]=\"showErrors()\" [ngModel]=\"maxReminders()\" (ngModelChange)=\"maxReminders.set(asText($event))\" />\n </ui-form-row>\n </ui-form-section>\n }\n </div>\n }\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-docs { display: flex; flex-direction: column; gap: 8px; min-width: 0; }\n .dso-doc { display: flex; flex-wrap: wrap; align-items: center; gap: 6px 8px; min-width: 0; }\n .dso-doc-chip { max-width: 100%; min-width: 0; }\n .dso-doc-title { min-width: 0; font-size: 0.875rem; color: #1e293b; overflow-wrap: anywhere; }\n .dso-locked-label { margin: 0; font-size: 0.8125rem; font-weight: 600; color: #334155; }\n .dso-fields { margin: 0; font-size: 0.75rem; color: #64748b; }\n .dso-retry { display: flex; flex-direction: column; align-items: flex-start; gap: 6px; }\n .dso-signing { display: flex; flex-direction: column; gap: 8px; min-width: 0; }\n .dso-signing-line { display: flex; flex-wrap: wrap; align-items: baseline; gap: 4px 10px; min-width: 0; }\n .dso-signing-summary { font-size: 0.8125rem; color: #475569; min-width: 0; overflow-wrap: anywhere; }\n :host ::ng-deep .dso-doc-chip .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<DocumentSeed[]>([]);\n /**\n * Shows the \"Expiry and Reminders\" settings (Expiry 1-90 days, Reminder Interval 1-30 days, Max Reminders 0-10, all required whole\n * numbers; starting at 7 / 2 / 3 unless this object says otherwise) while at least one document is chosen and the request is a native\n * e-signature one (purpose e-signature or both; always on the text channel). `null` (default) = not shown, nothing sent. Read once.\n */\n readonly signing = input<DocumentSigningOptions | null | undefined>(null);\n /**\n * The host knows whether DocuSign is connected (the library never calls `/docusign/status`). `true` offers \"Native E-Sign / Send via\n * DocuSign\" on the email channel while exactly one document is chosen; `false` (default) = no choice, native only.\n */\n readonly docuSignConnected = input(false);\n /**\n * \"Preview Document\": called with the document when its button is clicked (one button for one document, one per document for several).\n * `null` (default) hides the buttons. The host opens its own viewer; to open a new tab do it synchronously inside this call\n * (`window.open` after an await is blocked as a popup in Safari).\n */\n readonly documentPreview = input<DocumentPreviewHandler | null>(null);\n /** The recipient passed to `documentPreview` as `context.contact` (the send panel sets it from its contact inputs). `null` = none. */\n readonly previewContact = input<DocumentPreviewContact | null>(null);\n /** The send context passed to `documentPreview` as `context.sendContext` (the send panel sets it from its `sendContext`). `null` = none. */\n readonly previewSendContext = input<SendContext | null>(null);\n /**\n * Turns on the signer logic (`documentSigners` of the panel): a document's customer roles (from its row's `signerRoles`, or the config's\n * `resolveRoles`), informational documents (no signature fields), the one-signer DocuSign rule and the read-only countersign rule.\n * The value then carries `signerInfo`. `null` (default) = none of it; nothing changes.\n */\n readonly signers = input<DocumentSignersConfig | null | undefined>(null);\n /**\n * An existing envelope (`documentEnvelope` of the panel): its documents are the list, fixed (no search, no remove buttons). Read when it\n * is set; `null` (default) = the sender chooses the documents.\n */\n readonly envelope = input<DocumentEnvelopeTarget | null>(null);\n /** The document list is fixed: an existing envelope's. */\n readonly locked = computed(() => !!this.envelope());\n protected readonly purposeOptions = computed(() => {\n const allowed = this.purposes();\n const base = DOCUMENT_PURPOSE_OPTIONS.filter(o => allowed.includes(o.value as DocumentSendPurpose));\n const opts = base.length ? base : DOCUMENT_PURPOSE_OPTIONS;\n // Nothing is signed in an informational send: the recipient gets a view link.\n return this.signerInfo()?.informational ? opts.map(o => ({ ...o, label: INFORMATIONAL_PURPOSE_LABELS[o.value] ?? o.label })) : opts;\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 /** What the sender picked in the provider choice (only effective while the choice is offered). */\n readonly providerChoice = signal<DocumentSignatureProvider>('NATIVE');\n readonly countersignChoice = signal(false);\n /** Roles of documents whose row had no `signerRoles` and that the host resolves (`signers().resolveRoles`). */\n readonly roleStates = signal<Record<string, RoleState>>({});\n /** The envelope's documents: `loading` / `error` while the panel reads them (`loadDocuments`), `ready` once known. */\n readonly envelopeStatus = signal<'idle' | 'loading' | 'ready' | 'error'>('idle');\n private envelopeSub: Subscription | undefined;\n private readonly roleSubs = new Map<string, Subscription>();\n /** The sender opened the collapsed \"Signing options\" line. */\n readonly signingOpen = signal(false);\n readonly expiryDays = signal(String(DEFAULT_SIGNING_OPTIONS.expiryDays));\n readonly reminderIntervalDays = signal(String(DEFAULT_SIGNING_OPTIONS.reminderIntervalDays));\n readonly maxReminders = signal(String(DEFAULT_SIGNING_OPTIONS.maxReminders));\n protected readonly providerOptions = DOCUMENT_PROVIDER_OPTIONS;\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 /** True when the sender can choose DocuSign: the host says it is connected, email channel, exactly one document (DocuSign sends one), and e-signature is a supported purpose. */\n readonly providerChoiceShown = computed(() => {\n if (!(this.docuSignConnected() && this.channel() === 'email' && this.selected().length === 1 && this.purposes().includes('ESIGN'))) return false;\n // With the signer logic on, the choice appears only where DocuSign can apply: a document that is signed, by one customer signer.\n const info = this.signerInfo();\n return !this.signers() || (info?.status === 'ready' && !info.informational && !info.multiSigner);\n });\n /** The provider that is actually used: DocuSign only while the choice is offered. */\n readonly provider = computed<DocumentSignatureProvider>(() => this.providerChoiceShown() && this.providerChoice() === 'DOCUSIGN' ? 'DOCUSIGN' : 'NATIVE');\n /** The countersignature checkbox: a DocuSign send of a document flagged `requiresCountersign`. */\n readonly showCountersign = computed(() => !this.signers() && this.provider() === 'DOCUSIGN' && this.selected()[0]?.requiresCountersign === true);\n /** The checkbox (no `signers`) or, with `signers` on, the document's own setting: the document says whether it is countersigned. */\n readonly countersign = computed(() => this.signers()\n ? this.provider() === 'DOCUSIGN' && this.selected()[0]?.requiresCountersign === true\n : this.showCountersign() && this.countersignChoice());\n /** The signing settings are shown: turned on by the host, a document is chosen, and it is a native e-signature request (not attachment only, not DocuSign). */\n readonly signingVisible = computed(() =>\n this.signing() != null && this.available() && this.selected().length > 0 && this.provider() === 'NATIVE'\n && (this.signerInfo() === null || (this.signerInfo()!.status === 'ready' && !this.signerInfo()!.informational))\n && (this.channel() === 'text' || this.purpose() !== 'ATTACH'));\n /** What the signer logic concluded for the chosen documents (`null` when `signers` is off or nothing is chosen). */\n readonly signerInfo = computed<DocumentSignerInfo | null>(() => describeDocumentSigners(this.selected(), this.roleStates(), this.signers()));\n /** Why an envelope cannot be sent ('' = it can): nothing to send, or a document that is no longer active. Shown under the section. */\n readonly envelopeProblem = computed(() => {\n if (!this.envelope() || this.envelopeStatus() !== 'ready') return '';\n const docs = this.selected();\n if (docs.length === 0) return 'This envelope has no documents. Add a document first.';\n const inactive = docs.find(d => d.status !== undefined && d.status !== 'ACTIVE');\n return inactive ? `\"${inactive.title}\" is ${inactive.status!.toLowerCase()}, so this envelope can't be sent. Restore it or remove it from the envelope first.` : '';\n });\n /** Why Send waits for the envelope's documents ('' when they are known). */\n readonly envelopeReadMessage = computed(() => {\n const status = this.envelopeStatus();\n return status === 'loading' ? 'Reading the envelope\\'s documents…'\n : status === 'error' ? 'The documents of this envelope couldn\\'t be read, so it can\\'t be sent yet.' : '';\n });\n /** Why Send waits for the document's signers ('' when they are known). */\n readonly signerReadMessage = computed(() => {\n const status = this.signerInfo()?.status;\n return status === 'loading' ? 'Reading the document\\'s signers…'\n : status === 'error' ? 'The signers of this document couldn\\'t be read, so it can\\'t be sent yet.' : '';\n });\n /** The collapsed line: \"Signing options: expires in 7 days, reminders every 2 days, up to 3\". */\n readonly signingSummary = computed(() => {\n const day = (raw: string) => `${raw} ${raw.trim() === '1' ? 'day' : 'days'}`;\n const max = this.maxReminders().trim();\n const reminders = max === '0' ? 'no reminders' : `reminders every ${day(this.reminderIntervalDays().trim())}, up to ${max}`;\n return `Signing options: expires in ${day(this.expiryDays().trim())}, ${reminders}`;\n });\n /** Open when the sender opened it, or while a value is broken (the error must be visible). */\n readonly signingExpanded = computed(() => this.signingOpen() || this.signingError() !== '');\n\n /** The purpose that is actually sent: the text channel and DocuSign can only do e-signature. */\n readonly purpose = computed<DocumentSendPurpose>(() => {\n if (this.channel() === 'text' || this.provider() === 'DOCUSIGN') 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 const v: DocumentSendValue = {\n documentIds: this.selected().map(d => d.id),\n documents: this.selected(),\n purpose: this.purpose(),\n delivery: this.delivery(),\n };\n if (this.providerChoiceShown()) {\n v.provider = this.provider();\n if (this.provider() === 'DOCUSIGN') v.docuSign = { countersign: this.countersign() };\n }\n if (this.signingVisible() && this.signingError() === '') {\n v.expiryDays = Number(this.expiryDays());\n v.reminderIntervalDays = Number(this.reminderIntervalDays());\n v.maxReminders = Number(this.maxReminders());\n }\n const info = this.signerInfo();\n if (info) v.signerInfo = info;\n return v;\n });\n\n /** The first broken signing setting ('' when fine or not shown) — the inputs show their own messages. */\n readonly signingError = computed(() => {\n if (!this.signingVisible()) return '';\n const fields = [\n ['expiryDays', this.expiryDays()], ['reminderIntervalDays', this.reminderIntervalDays()], ['maxReminders', this.maxReminders()],\n ] as const;\n for (const [key, raw] of fields) {\n const { min, max, label } = SIGNING_LIMITS[key];\n const n = wholeNumber(raw);\n if ((raw ?? '').trim() === '') return `${label} is required.`;\n if (n === null || n < min || n > max) return `${label} must be a whole number from ${min} to ${max}.`;\n }\n return '';\n });\n\n /** The first broken document rule ('' when fine) — shown under the section. */\n readonly documentError = 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 const env = this.envelopeProblem();\n if (env) return env;\n const info = this.signerInfo();\n // A signed document with several signers can only be emailed: the texted link goes to the first signer only.\n if (info?.multiSigner && this.channel() === 'text') return `This document has ${info.roles.length} signers. Send it by email.`;\n return '';\n });\n /** The first broken rule ('' when valid): the document rules, the wait for the signers, then the signing settings. */\n readonly errorMessage = computed(() => this.documentError() || this.envelopeReadMessage() || this.signerReadMessage() || this.signingError());\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 // Only an ACTIVE document can be sent (ARCHIVED and DEPRECATED are refused by the host); a result with no status at all is\n // offered as before. One that is already chosen is not offered twice.\n .filter(d => d && (d.status == null || d.status === 'ACTIVE') && !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 || this.envelope()) return;\n seeded = true;\n this.selected.set(initial.map(DocumentSendOptionsComponent.seedToOption));\n });\n });\n\n // An existing envelope: its documents are the list (given, or read by the loader).\n effect(() => {\n const env = this.envelope();\n untracked(() => this.loadEnvelope(env));\n });\n this.destroyRef.onDestroy(() => this.envelopeSub?.unsubscribe());\n\n // Roles of the chosen documents that the host resolves (one request per document; removed documents are forgotten).\n effect(() => {\n const cfg = this.signers();\n const docs = this.selected();\n untracked(() => this.syncRoleRequests(cfg, docs));\n });\n this.destroyRef.onDestroy(() => { this.roleSubs.forEach(sub => sub.unsubscribe()); this.roleSubs.clear(); });\n\n // The host's starting values for the signing settings (the keys it leaves out keep the defaults). Read once.\n let signingSeeded = false;\n effect(() => {\n const s = this.signing();\n untracked(() => {\n if (signingSeeded || !s) return;\n signingSeeded = true;\n this.seedSigning(s);\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 seedToOption(d: DocumentSeed): DocumentOption {\n return {\n id: String(d.id), title: d.title, fileName: d.fileName ?? null, hasFile: d.hasFile ?? true,\n ...(d.requiresCountersign ? { requiresCountersign: true } : {}),\n ...(typeof d.hasCountersignPlacement === 'boolean' ? { hasCountersignPlacement: d.hasCountersignPlacement } : {}),\n ...(Array.isArray(d.signerRoles) ? { signerRoles: normalizeRoles(d.signerRoles) } : {}),\n ...(typeof d.hasMapping === 'boolean' ? { hasMapping: d.hasMapping } : {}),\n ...(typeof d.hasDataFields === 'boolean' ? { hasDataFields: d.hasDataFields } : {}),\n ...(typeof d.status === 'string' ? { status: d.status } : {}),\n };\n }\n\n /** Sets the envelope's documents as the list; reads them first when the host gave a loader. */\n private loadEnvelope(env: DocumentEnvelopeTarget | null) {\n this.envelopeSub?.unsubscribe();\n this.envelopeSub = undefined;\n if (!env) {\n // Leaving an envelope: its fixed documents must not linger as removable chips of a normal send.\n if (this.envelopeStatus() !== 'idle') this.selected.set([]);\n this.envelopeStatus.set('idle');\n return;\n }\n const set = (docs: DocumentSeed[]) => { this.selected.set(docs.map(DocumentSendOptionsComponent.seedToOption)); this.envelopeStatus.set('ready'); };\n if (env.documents) { set(env.documents); return; }\n if (!env.loadDocuments) { set([]); return; }\n this.selected.set([]);\n this.envelopeStatus.set('loading');\n let answered = false;\n try {\n this.envelopeSub = env.loadDocuments().pipe(take(1)).subscribe({\n next: docs => { answered = true; set(docs ?? []); },\n error: () => this.envelopeStatus.set('error'),\n complete: () => { if (!answered) this.envelopeStatus.set('error'); },\n });\n } catch {\n this.envelopeStatus.set('error');\n }\n }\n\n /** \"Retry\" after the envelope's documents could not be read. */\n retryEnvelope() { this.loadEnvelope(this.envelope()); }\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 ...(d.requiresCountersign === true ? { requiresCountersign: true } : {}),\n ...(typeof d.hasCountersignPlacement === 'boolean' ? { hasCountersignPlacement: d.hasCountersignPlacement } : {}),\n ...(Array.isArray(d.signerRoles) ? { signerRoles: normalizeRoles(d.signerRoles) } : {}),\n ...(typeof d.hasMapping === 'boolean' ? { hasMapping: d.hasMapping } : {}),\n ...(typeof d.hasDataFields === 'boolean' ? { hasDataFields: d.hasDataFields } : {}),\n };\n }\n\n /** Asks the host for the roles of every chosen document that needs it; forgets documents that were removed. */\n private syncRoleRequests(cfg: DocumentSignersConfig | null | undefined, docs: DocumentOption[]) {\n const ids = new Set(docs.map(d => d.id));\n for (const [id, sub] of this.roleSubs) {\n if (!ids.has(id)) { sub.unsubscribe(); this.roleSubs.delete(id); }\n }\n const known = this.roleStates();\n if (Object.keys(known).some(id => !ids.has(id))) {\n this.roleStates.set(Object.fromEntries(Object.entries(known).filter(([id]) => ids.has(id))));\n }\n if (!cfg?.resolveRoles) return;\n for (const d of docs) {\n if (d.signerRoles === undefined && !this.roleStates()[d.id]) this.requestRoles(d, cfg);\n }\n }\n\n private requestRoles(d: DocumentOption, cfg: DocumentSignersConfig) {\n const setState = (state: RoleState) => this.roleStates.update(m => ({ ...m, [d.id]: state }));\n this.roleSubs.get(d.id)?.unsubscribe();\n setState({ status: 'loading', roles: [] });\n let answered = false;\n try {\n const sub = cfg.resolveRoles!({ id: d.id, title: d.title, fileName: d.fileName }).pipe(take(1)).subscribe({\n next: roles => { answered = true; setState({ status: 'ready', roles: normalizeRoles(roles, cfg.maxSigners) }); },\n error: () => setState({ status: 'error', roles: [] }),\n complete: () => { if (!answered) setState({ status: 'error', roles: [] }); },\n });\n this.roleSubs.set(d.id, sub);\n } catch {\n setState({ status: 'error', roles: [] });\n }\n }\n\n /** \"Retry\" after the roles of a document could not be read. */\n retryRoles() {\n const cfg = this.signers();\n if (!cfg?.resolveRoles) return;\n for (const d of this.selected()) {\n if (this.roleStates()[d.id]?.status === 'error') this.requestRoles(d, cfg);\n }\n }\n\n /** The Change / Hide button: it flips what the sender sees (a line kept open by an error stays open, but a later fix lets it close). */\n toggleSigning() { this.signingOpen.set(!this.signingExpanded()); }\n\n private seedSigning(s: DocumentSigningOptions | null | undefined) {\n this.expiryDays.set(String(s?.expiryDays ?? DEFAULT_SIGNING_OPTIONS.expiryDays));\n this.reminderIntervalDays.set(String(s?.reminderIntervalDays ?? DEFAULT_SIGNING_OPTIONS.reminderIntervalDays));\n this.maxReminders.set(String(s?.maxReminders ?? DEFAULT_SIGNING_OPTIONS.maxReminders));\n }\n\n /** `ui-text-input` hands the typed text back as a string (or a number from a programmatic write). */\n protected asText(v: unknown): string { return v == null ? '' : String(v); }\n\n setProvider(p: string) { if (p === 'NATIVE' || p === 'DOCUSIGN') this.providerChoice.set(p); }\n\n /** Calls the host's preview handler straight from the click (no await in between: Safari blocks a tab opened later). */\n preview(doc: DocumentOption) { this.documentPreview()?.({ id: doc.id, title: doc.title, fileName: doc.fileName }, { contact: this.previewContact(), sendContext: this.previewSendContext() }); }\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() {\n this.selected.set([]); this.purposeChoice.set('ESIGN'); this.options.set([]); this.searchFailed.set(false);\n this.providerChoice.set('NATIVE'); this.countersignChoice.set(false); this.signingOpen.set(false); this.seedSigning(this.signing());\n }\n}\n","import {\n ChangeDetectionStrategy, Component, computed, effect, forwardRef, input, output, signal, untracked,\n} from '@angular/core';\nimport { ContactPickerComponent, type ContactLookup, ButtonComponent, ErrorComponent, FormSectionComponent, HintComponent, UiFieldValidity } from '@forgeroute/ui';\nimport { buildSigners, countersignLine, countersignPlaceLabel, roleNumber, signerProblems, signerRoleLabel } from './document-signers';\nimport type { DocumentSignerInfo, DocumentSignersConfig, SendSigner, SignerPick, SignerRole } from './document-signers';\n\n/**\n * ui-document-signers — the signer section of a document send: one required contact per customer signer role of the chosen document\n * (Signer 1 is the recipient, shown read-only) in signing order, and the one read-only countersign line.\n *\n * Internal child of the message forms (`ui-send-message-panel` with `documentSigners`); hosts do not place it. Rows exist only for a\n * document with MORE THAN ONE customer signer; a single-signer document shows nothing here except the countersign line. It emits the\n * complete `signers` list (never a partial one: `null` until every role has a contact) and its validity (`UiFieldValidity`):\n * a missing contact is `<Role label> is required.`, a contact without an email `<Name> has no email address.`, the same person in two\n * roles `<Name> is already <Role label>.` (unless the config allows it).\n */\n@Component({\n selector: 'ui-document-signers',\n standalone: true,\n changeDetection: ChangeDetectionStrategy.OnPush,\n imports: [ButtonComponent, ContactPickerComponent, ErrorComponent, FormSectionComponent, HintComponent],\n providers: [{ provide: UiFieldValidity, useExisting: forwardRef(() => DocumentSignersComponent) }],\n template: `\n @if (rowsShown()) {\n <ui-form-section label=\"Signers\" data-testid=\"signers-section\">\n @for (role of info()!.roles; track role) {\n @if (role === 'signer1') {\n <div class=\"ds-row\" [attr.data-testid]=\"'signer-row-' + role\">\n <span class=\"ds-role\">{{ label(role) }}</span>\n <span class=\"ds-name\">{{ recipient().name }}</span>\n </div>\n } @else {\n <div class=\"ds-pick\" [attr.data-testid]=\"'signer-row-' + role\">\n <ui-contact-picker\n #picker\n [label]=\"label(role)\"\n [required]=\"true\"\n [showErrors]=\"showErrors()\"\n [errorId]=\"'ds-error-' + role\"\n placeholder=\"Search for a contact…\"\n (contactSelected)=\"pick(role, $event)\" />\n <!-- The picker's own \"<Role> is required.\" (shown after blur or with showErrors), else the contact problem (no email / twice). -->\n <div class=\"ds-error\" [attr.id]=\"'ds-error-' + role\">\n <ui-error [message]=\"visibleProblem(role) || picker.visibleErrorMessage()\" />\n </div>\n </div>\n }\n }\n <ui-hint data-testid=\"signers-order-hint\">Signers are emailed one after another, in this order; each receives the link after the previous one signs.</ui-hint>\n </ui-form-section>\n }\n @if (line()) {\n <p class=\"ds-countersign\" data-testid=\"countersign-line\">{{ line() }}</p>\n <!-- The countersign box is not placed on the document yet: a link to where it is placed (the host navigates on mapFieldsClick). -->\n @if (placeDocs().length > 0) {\n <div class=\"ds-place\" data-testid=\"countersign-place\">\n @for (d of placeDocs(); track d.id) {\n <ui-button\n class=\"ds-place-btn\"\n variant=\"link\"\n size=\"sm\"\n [ariaLabel]=\"placeLabel(d.title)\"\n [attr.data-testid]=\"'countersign-place-' + d.id\"\n (click)=\"mapFieldsClick.emit(d)\">{{ placeText(d.title) }}</ui-button>\n }\n </div>\n }\n }\n `,\n styles: [`\n :host { display: block; }\n .ds-row { display: flex; flex-wrap: wrap; align-items: baseline; gap: 6px 10px; min-width: 0; }\n .ds-role { font-size: 0.8125rem; font-weight: 600; color: #334155; }\n .ds-name { font-size: 0.875rem; color: #1e293b; overflow-wrap: anywhere; min-width: 0; }\n .ds-countersign { margin: 0; font-size: 0.8125rem; color: #475569; }\n ui-form-section + .ds-countersign { margin-top: 10px; }\n .ds-place { display: flex; flex-direction: column; align-items: flex-start; gap: 2px; min-width: 0; margin-top: 4px; }\n .ds-place-btn { max-width: 100%; font-size: 0.8125rem; }\n /* Wrap long document titles instead of overflowing at 320px (the library button is nowrap). */\n .ds-place-btn ::ng-deep .btn { white-space: normal; text-align: left; overflow-wrap: anywhere; }\n /* ui-button's link variant is exempt from the 44px touch rule (.btn-link.btn-sm { min-height: 0 }); this one is a stand-alone action, so give it a 44px\n hit area on touch. The extra .btn-link class out-ranks that rule whatever the stylesheet order. */\n @media (pointer: coarse), (max-width: 480px) {\n .ds-place-btn ::ng-deep .btn.btn-link { min-height: 44px; }\n }\n `],\n})\nexport class DocumentSignersComponent implements UiFieldValidity {\n /** The signer logic's conclusion for the chosen document(s) (`ui-document-send-options` value `signerInfo`). */\n readonly info = input<DocumentSignerInfo | null>(null);\n readonly config = input<DocumentSignersConfig | null>(null);\n /** The recipient of the message = Signer 1. */\n readonly recipient = input<SignerPick>({ id: '', name: '', email: null });\n /** Shows the \"<Role> is required.\" messages now (otherwise after the first blur). */\n readonly showErrors = input(false);\n /** The sender chose DocuSign: only the countersign line applies. */\n readonly viaDocuSign = input(false);\n\n /** The complete signer list (recipient first) or null while a role has no contact. Emitted on every change. */\n readonly signersChange = output<SendSigner[] | null>();\n readonly invalidChange = output<boolean>();\n /** The \"Place your countersignature box\" link was clicked: the document whose countersign box is not placed (the host opens Map Data Fields). */\n readonly mapFieldsClick = output<{ id: string; title: string }>();\n\n readonly picks = signal<Partial<Record<SignerRole, ContactLookup | null>>>({});\n\n protected readonly rowsShown = computed(() => !!this.info()?.multiSigner && !this.viaDocuSign());\n protected readonly line = computed(() => countersignLine(this.info(), this.config(), this.viaDocuSign()));\n\n /** Documents the \"place your box\" link is offered for: native send only (DocuSign places its own fields) and only while the countersign line shows. */\n protected readonly placeDocs = computed(() => this.line() && !this.viaDocuSign() ? (this.info()?.countersignUnplaced ?? []) : []);\n\n readonly problems = computed(() => this.rowsShown()\n ? signerProblems(this.info()!.roles, this.recipient(), this.picks(), this.config()) : {});\n readonly signers = computed<SendSigner[] | null>(() => {\n if (!this.rowsShown() || Object.keys(this.problems()).length > 0) return null;\n return buildSigners(this.info()!.roles, this.recipient(), this.picks());\n });\n readonly errorMessage = computed(() => {\n const p = this.problems();\n const first = this.info()?.roles.find(r => p[r]);\n return first ? p[first]! : '';\n });\n readonly invalid = computed(() => this.errorMessage() !== '');\n\n constructor() {\n effect(() => {\n const s = this.signers();\n untracked(() => this.signersChange.emit(s));\n });\n effect(() => {\n const bad = this.invalid();\n untracked(() => this.invalidChange.emit(bad));\n });\n }\n\n protected placeLabel(title: string): string { return countersignPlaceLabel(title); }\n /** Visible text: with several documents the title is part of it (label-in-name: it contains the start of the accessible name). */\n protected placeText(title: string): string { return this.placeDocs().length > 1 ? countersignPlaceLabel(title) : 'Place your countersignature box'; }\n\n protected label(role: SignerRole): string { return signerRoleLabel(role, this.config()); }\n\n /** Under a picker: the problems of a chosen contact (no email / same person twice); the empty-row message is the picker's own (`visibleErrorMessage`). */\n protected visibleProblem(role: SignerRole): string {\n return this.picks()[role] ? (this.problems()[role] ?? '') : '';\n }\n\n pick(role: SignerRole, c: ContactLookup | null) {\n if (roleNumber(role) < 2) return;\n this.picks.update(m => ({ ...m, [role]: c }));\n }\n}\n","import { ChangeDetectionStrategy, Component, DestroyRef, computed, inject, input, output } from '@angular/core';\nimport { HttpClient } from '@angular/common/http';\nimport { takeUntilDestroyed, toObservable } from '@angular/core/rxjs-interop';\nimport { catchError, debounceTime, defer, distinctUntilChanged, map, of, switchMap } from 'rxjs';\nimport type { Observable } from 'rxjs';\nimport { FORGEROUTE_CONFIG } from '@forgeroute/ui';\nimport { hasMappedDataFields, mergeWarningLines } from './document-signers';\nimport type { MergeCheckFn, MergeCheckRequest, MergeCheckResult, SendContext, SendSigner, SignerDocLike } from './document-signers';\n\n/** Quiet time after the last change before the check is requested. */\nexport const MERGE_CHECK_DEBOUNCE_MS = 300;\n\n/**\n * ui-document-merge-check — the pre-send \"fields that will be empty\" check (non-blocking), logic only (it renders nothing).\n *\n * Internal child of the message forms. While `enabled` is `true` (default route `POST <baseUrl>/documents/merge-check`) or a host\n * function, and at least one chosen document has mapped data fields, it asks which data fields would be left empty for this recipient,\n * these signers and this context, and emits the warning lines (`warningsChange`). Debounced, a newer request cancels an older one\n * (`switchMap`), never cached. A failed or slow check emits no warning and never blocks Send. Documents with no mapped data fields\n * are not sent to the check.\n */\n@Component({\n selector: 'ui-document-merge-check',\n standalone: true,\n changeDetection: ChangeDetectionStrategy.OnPush,\n template: ``,\n styles: [`:host { display: none; }`],\n})\nexport class DocumentMergeCheckComponent {\n private readonly http = inject(HttpClient);\n private readonly destroyRef = inject(DestroyRef);\n private readonly apiBase = inject(FORGEROUTE_CONFIG, { optional: true })?.baseUrl ?? '/api';\n\n /** `true` = the default route, a function = the host's own call, `false` / `null` = off. */\n readonly enabled = input<boolean | MergeCheckFn | null>(false);\n readonly documents = input<SignerDocLike[]>([]);\n readonly customerId = input('');\n readonly signers = input<SendSigner[] | null>(null);\n readonly context = input<SendContext | undefined>(undefined);\n\n /** The warning lines (empty = nothing to warn about). Emitted whenever they change. */\n readonly warningsChange = output<string[]>();\n\n private readonly request = computed<MergeCheckRequest | null>(() => {\n if (!this.enabled()) return null;\n const ids = this.documents().filter(hasMappedDataFields).map(d => d.id);\n if (ids.length === 0) return null;\n const req: MergeCheckRequest = { documentIds: ids };\n if (this.customerId()) req.customerId = this.customerId();\n const signers = this.signers();\n if (signers?.length) req.signers = signers;\n const ctx = this.context();\n if (ctx && Object.keys(ctx).length) req.context = ctx;\n return req;\n });\n\n constructor() {\n toObservable(this.request).pipe(\n distinctUntilChanged((a, b) => JSON.stringify(a) === JSON.stringify(b)),\n debounceTime(MERGE_CHECK_DEBOUNCE_MS),\n switchMap(req => {\n if (!req) return of<string[]>([]);\n const titles = Object.fromEntries(this.documents().map(d => [d.id, d.title]));\n return defer(() => this.call(req)).pipe(\n map(res => mergeWarningLines(res, titles)),\n catchError(() => of<string[]>([])),\n );\n }),\n takeUntilDestroyed(this.destroyRef),\n ).subscribe(lines => {\n // Only a change is reported (the host starts with no warning); turning the check off or removing the last mapped document\n // clears a warning that is already shown.\n if (lines.join('\\n') === this.lastLines.join('\\n')) return;\n this.lastLines = lines;\n this.warningsChange.emit(lines);\n });\n }\n\n private lastLines: string[] = [];\n\n private call(req: MergeCheckRequest): Observable<MergeCheckResult> {\n const fn = this.enabled();\n return typeof fn === 'function' ? fn(req) : this.http.post<MergeCheckResult>(`${this.apiBase}/documents/merge-check`, req);\n }\n}\n","/**\n * Video share-link helpers shared by every app (they replace the apps' own copies, e.g. real-estate `video-link.utils.ts`).\n *\n * The share link of a video (`VideoMessage.shareLink`) carries the playback settings as query parameters:\n * autoplay=1 · ref=<contact id> · ctaType · ctaUrl · ctaLabel · hideReactions=1 (in exactly that order).\n */\n\nexport interface VideoLinkOptions {\n autoplay?: boolean;\n /** The recipient: lets the video page track who watched (`ref`). */\n contactId?: string;\n /** '' / undefined = none (the video's own default); `schedule` | `contact` | `custom`. */\n ctaType?: string;\n ctaUrl?: string;\n ctaLabel?: string;\n hideReactions?: boolean;\n}\n\n/**\n * Default video settings the host reads from its Communication settings (same keys as `/comm-settings` without the `videoDefault`\n * prefix): `videoDefaultAutoplay` → `autoplay`, `videoDefaultCtaType` → `ctaType`, … Missing keys mean \"off\" / \"none\".\n */\nexport interface VideoDefaults {\n autoplay?: boolean;\n ctaType?: string;\n ctaUrl?: string;\n ctaLabel?: string;\n hideReactions?: boolean;\n}\n\n/** `baseLink` + the settings as query parameters (unchanged when no setting is on). A base that already has a query gets `&`. */\nexport function buildVideoLink(baseLink: string, options: VideoLinkOptions = {}): string {\n const params: string[] = [];\n if (options.autoplay) params.push('autoplay=1');\n if (options.contactId) params.push(`ref=${encodeURIComponent(options.contactId)}`);\n if (options.ctaType) params.push(`ctaType=${encodeURIComponent(options.ctaType)}`);\n if (options.ctaUrl) params.push(`ctaUrl=${encodeURIComponent(options.ctaUrl)}`);\n if (options.ctaLabel) params.push(`ctaLabel=${encodeURIComponent(options.ctaLabel)}`);\n if (options.hideReactions) params.push('hideReactions=1');\n return params.length ? `${baseLink}${baseLink.includes('?') ? '&' : '?'}${params.join('&')}` : baseLink;\n}\n\n/**\n * The share link of a freshly attached video: `baseLink` with the host's defaults and the recipient applied — exactly the link\n * `ui-video-link-settings` shows before the sender changes anything.\n */\nexport function videoLinkWithDefaults(baseLink: string, defaults: VideoDefaults = {}, contactId = ''): string {\n return buildVideoLink(baseLink, {\n autoplay: !!defaults.autoplay,\n contactId: contactId || undefined,\n ctaType: defaults.ctaType || undefined,\n ctaUrl: defaults.ctaUrl || undefined,\n ctaLabel: defaults.ctaLabel || undefined,\n hideReactions: !!defaults.hideReactions,\n });\n}\n\n/** Strips any \"🎬 Watch video: <url>\" line previously inserted into a message body (the line format the apps' own composers use). */\nexport function stripVideoLink(text: string): string {\n return text\n .replace(/\\n\\n🎬 Watch video: [^\\n]*/g, '')\n .replace(/\\n🎬 Watch video: [^\\n]*/g, '')\n .replace(/🎬 Watch video: [^\\n]*/g, '')\n .trimEnd();\n}\n\n/** Appends a \"🎬 Watch video: <link>\" line to a message body (replacing one that is already there). */\nexport function appendVideoLink(text: string, link: string): string {\n const base = stripVideoLink(text);\n return base ? `${base}\\n\\n🎬 Watch video: ${link}` : `🎬 Watch video: ${link}`;\n}\n\n/** The line `ui-send-message-panel` puts in the message for an attached video: `Watch \"<title>\": <link>`. */\nexport function videoMessageLine(title: string, link: string): string {\n return `Watch \"${title}\": ${link}`;\n}\n\n/**\n * Puts `next` into `body` in place of `prev` (the line inserted earlier), so changing a video setting rewrites the link without\n * touching the rest of the message:\n * - no `prev` yet → appended after a blank line (no separator for an empty body);\n * - `prev` is in the body → replaced in place; with an empty `next` it is removed together with its separator;\n * - `prev` was edited away by the sender → the body is left as the sender wrote it.\n */\nexport function applyVideoLine(body: string, prev: string, next: string): string {\n if (!prev) {\n if (!next) return body;\n return body.trim() ? `${body}\\n\\n${next}` : next;\n }\n const at = body.indexOf(prev);\n if (at < 0) return body;\n if (next) return body.slice(0, at) + next + body.slice(at + prev.length);\n const before = body.slice(0, at);\n const after = body.slice(at + prev.length);\n const trimmedBefore = before.replace(/\\n{1,2}$/, '');\n return (trimmedBefore + after).trim() === '' ? '' : trimmedBefore + after;\n}\n","import { ChangeDetectionStrategy, Component, computed, effect, input, output, signal, untracked } from '@angular/core';\nimport { FormsModule } from '@angular/forms';\nimport { ButtonComponent, CheckboxComponent, FormSectionComponent, HintComponent, RadioGroupComponent, type RadioOption, SelectComponent, type SelectOption, TextInputComponent } from '@forgeroute/ui';\nimport type { VideoMessage } from '../video-picker/video-picker.component';\nimport { buildVideoLink } from './video-link';\nimport type { VideoDefaults } from './video-link';\n\nexport const VIDEO_PLAYBACK_OPTIONS: RadioOption[] = [\n { value: 'click', label: 'Click to Play' },\n { value: 'autoplay', label: 'Autoplay' },\n];\n\nexport const VIDEO_CTA_OPTIONS: SelectOption[] = [\n { value: '', label: 'None (use video default)' },\n { value: 'schedule', label: 'Schedule a Call' },\n { value: 'contact', label: 'Contact Me' },\n { value: 'custom', label: 'Custom' },\n];\n\n/**\n * ui-video-link-settings — an attached video: a \"Watch Preview\" link, \"Remove Video\" and ONE \"Video settings\" disclosure (collapsed by default)\n * holding Playback (click to play / autoplay), Call-to-Action (none / schedule / contact / custom, with its URL and button label)\n * and Hide reactions.\n *\n * Internal child of `ui-send-message-panel` (the \"Video\" section of the email and text forms) — hosts turn it on through the panel's\n * `videoDefaults` input and do not place it themselves. It starts from `defaults` whenever a (new) `video` is given (the form writes the\n * initial link itself with `videoLinkWithDefaults`, which gives the same link) and emits the full share link (`buildVideoLink`) after\n * every change the sender makes; the form rewrites the video line of the message with it. It never emits while Angular is rendering.\n */\n@Component({\n selector: 'ui-video-link-settings',\n standalone: true,\n changeDetection: ChangeDetectionStrategy.OnPush,\n imports: [FormsModule, ButtonComponent, CheckboxComponent, FormSectionComponent, HintComponent, RadioGroupComponent, SelectComponent, TextInputComponent],\n template: `\n @if (video(); as v) {\n <ui-form-section class=\"vls\">\n <div class=\"vls-bar\">\n @if (v.shareLink) {\n <ui-hint><a class=\"vls-preview\" [href]=\"v.shareLink\" target=\"_blank\" rel=\"noopener\" aria-label=\"Watch Preview (opens in a new tab)\">Watch Preview →</a></ui-hint>\n }\n <ui-button variant=\"ghost\" size=\"xs\" [ariaExpanded]=\"settingsOpen()\" (click)=\"toggleSettings()\">Video settings</ui-button>\n <ui-button variant=\"secondary\" size=\"sm\" (click)=\"removed.emit()\">Remove Video</ui-button>\n </div>\n <!-- Collapsed by default: the defaults from Communication settings are already applied. -->\n @if (settingsOpen()) {\n <ui-radio-group\n label=\"Playback\"\n orientation=\"horizontal\"\n [options]=\"playbackOptions\"\n [ngModel]=\"autoplay()\"\n (ngModelChange)=\"setAutoplay($event)\" />\n <ui-select\n label=\"Call-to-Action\"\n [options]=\"ctaOptions\"\n [ngModel]=\"ctaType()\"\n (ngModelChange)=\"setCtaType($event)\" />\n @if (ctaType()) {\n <ui-text-input\n label=\"Call-to-action URL\"\n type=\"url\"\n placeholder=\"https://...\"\n [maxlength]=\"500\"\n [ngModel]=\"ctaUrl()\"\n (ngModelChange)=\"setCtaUrl($event)\" />\n <ui-text-input\n label=\"Call-to-action label\"\n placeholder=\"Button label (e.g. Book a Call)\"\n [maxlength]=\"100\"\n [ngModel]=\"ctaLabel()\"\n (ngModelChange)=\"setCtaLabel($event)\" />\n }\n <ui-checkbox label=\"Hide reactions\" [ngModel]=\"hideReactions()\" (ngModelChange)=\"setHideReactions($event)\" />\n }\n </ui-form-section>\n }\n `,\n styles: [`\n :host { display: block; }\n .vls-bar { display: flex; flex-wrap: wrap; align-items: center; gap: 6px 14px; min-width: 0; }\n .vls-preview { color: #2563eb; text-decoration: underline; }\n .vls-preview:focus-visible { outline: 2px solid #3b82f6; outline-offset: 2px; border-radius: 2px; }\n `],\n})\nexport class VideoLinkSettingsComponent {\n /** The attached video; its settings restart from `defaults` whenever this changes. */\n readonly video = input<VideoMessage | null>(null);\n /** The host's Communication-settings defaults. */\n readonly defaults = input<VideoDefaults>({});\n /** The recipient (tracked in the link as `ref`); empty = no `ref`. */\n readonly contactId = input('');\n\n /** The full share link (video link + settings) after each change the sender makes (not on creation). */\n readonly linkChange = output<string>();\n readonly removed = output<void>();\n\n protected readonly playbackOptions = VIDEO_PLAYBACK_OPTIONS;\n protected readonly ctaOptions = VIDEO_CTA_OPTIONS;\n\n readonly autoplay = signal<'click' | 'autoplay'>('click');\n readonly ctaType = signal('');\n readonly ctaUrl = signal('');\n readonly ctaLabel = signal('');\n readonly hideReactions = signal(false);\n /** The \"Video settings\" disclosure; closed again for every newly attached video. */\n readonly settingsOpen = signal(false);\n\n /** The share link with every setting applied ('' while there is no video link). */\n readonly link = computed(() => {\n const base = this.video()?.shareLink;\n if (!base) return '';\n return buildVideoLink(base, {\n autoplay: this.autoplay() === 'autoplay',\n contactId: this.contactId() || undefined,\n ctaType: this.ctaType() || undefined,\n ctaUrl: this.ctaUrl() || undefined,\n ctaLabel: this.ctaLabel() || undefined,\n hideReactions: this.hideReactions(),\n });\n });\n\n constructor() {\n // A new video starts from the defaults (read when the video is attached, so later edits are never overwritten).\n effect(() => {\n const video = this.video();\n if (!video) return;\n untracked(() => {\n const d = this.defaults();\n this.autoplay.set(d.autoplay ? 'autoplay' : 'click');\n this.ctaType.set(d.ctaType || '');\n this.ctaUrl.set(d.ctaUrl || '');\n this.ctaLabel.set(d.ctaLabel || '');\n this.hideReactions.set(d.hideReactions ?? false);\n this.settingsOpen.set(false);\n });\n });\n }\n\n toggleSettings() { this.settingsOpen.update(v => !v); }\n setAutoplay(v: string) { this.autoplay.set(v === 'autoplay' ? 'autoplay' : 'click'); this.emitLink(); }\n setCtaType(v: string | null) { this.ctaType.set(v || ''); this.emitLink(); }\n setCtaUrl(v: string | null) { this.ctaUrl.set(v || ''); this.emitLink(); }\n setCtaLabel(v: string | null) { this.ctaLabel.set(v || ''); this.emitLink(); }\n setHideReactions(v: boolean | null) { this.hideReactions.set(!!v); this.emitLink(); }\n\n private emitLink() {\n const link = this.link();\n if (link) this.linkChange.emit(link);\n }\n}\n","import { switchMap } from 'rxjs';\nimport type { Observable } from 'rxjs';\nimport type { HttpClient } from '@angular/common/http';\nimport type {\n DocumentSendDelivery, DocumentSendPurpose, DocumentSendValue, DocumentSignatureProvider,\n} from '../document-send-options/document-send-options.component';\nimport type { SendContext, SendSigner } from '../document-send-options/document-signers';\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 /** Only when the host turned on `documentSigningOptions` and the request is a native e-signature request (purpose `ESIGN` / `BOTH`): days until the request expires (1-90). */\n expiryDays?: number;\n /** Same condition as `expiryDays`: days between reminders (1-30). */\n reminderIntervalDays?: number;\n /** Same condition as `expiryDays`: how many reminders at most (0-10). */\n maxReminders?: number;\n /** Only when the host passed `docuSignConnected` and the sender can choose (email, exactly one document): which e-signature provider sends the request. */\n provider?: DocumentSignatureProvider;\n /** Only with `provider: 'DOCUSIGN'`: `countersign` is true when the sender ticked \"Require my countersignature after client signs\" (only offered for a document flagged `requiresCountersign`). */\n docuSign?: { countersign: boolean };\n /**\n * Only when the host turned on `documentSigners`, the request is a native e-signature one, ONE document is chosen and it has MORE THAN ONE\n * customer signer: all N signers including signer1 (= the recipient), ordered by role, never partial. A single-signer send has no such key.\n */\n signers?: SendSigner[];\n /** Only when the host passed `sendContext` and at least one key it declared in `documentContextTypes` has a value (other keys are dropped). */\n context?: SendContext;\n /** Only when the send is of an existing envelope (`documentEnvelope`): its id; the request is not created again, it is sent. */\n envelopeId?: string;\n /** Only with `documentSigners` on, when every chosen document has no signature fields: the send is informational (the recipient gets a view link, nothing is signed). */\n informational?: true;\n}\n\n/** What the success toast of a document send can say (`documentSentMessage` of the panel). */\nexport interface DocumentSentInfo {\n titles: string[];\n /** Only when an existing envelope was sent (`documentEnvelope`). */\n envelopeName?: string;\n channel: 'email' | 'text';\n provider?: DocumentSignatureProvider;\n /** Every chosen document has no signature fields (a view link was sent). */\n informational: boolean;\n}\n\n/** Builds the success toast of a document send (default: `Email sent` / `Text message sent` / `\"<title>\" sent via DocuSign` / `Envelope \"<name>\" sent`). */\nexport type DocumentSentMessage = (info: DocumentSentInfo) => string;\n\n/** The optional parts the form adds to the `documents` payload besides what the document section holds. */\nexport interface DocumentsPayloadExtras { signers?: SendSigner[] | null; context?: SendContext; envelopeId?: string }\n\n/**\n * The `documents` part of a payload from what the document section holds. The optional keys (signing settings, provider, DocuSign\n * options) are present only when the section produced them, so a host that turned none of them on gets exactly\n * `{ documentIds, purpose, delivery }`.\n */\nexport function buildDocumentsPayload(\n v: DocumentSendValue, delivery: DocumentSendDelivery, purpose: DocumentSendPurpose = v.purpose, extras: DocumentsPayloadExtras = {},\n): SendDocumentsPayload {\n const out: SendDocumentsPayload = { documentIds: v.documentIds, purpose, delivery };\n if (v.expiryDays !== undefined) out.expiryDays = v.expiryDays;\n if (v.reminderIntervalDays !== undefined) out.reminderIntervalDays = v.reminderIntervalDays;\n if (v.maxReminders !== undefined) out.maxReminders = v.maxReminders;\n if (v.provider !== undefined) out.provider = v.provider;\n if (v.docuSign !== undefined) out.docuSign = v.docuSign;\n if (extras.signers?.length) out.signers = extras.signers;\n if (extras.context && Object.keys(extras.context).length) out.context = extras.context;\n if (extras.envelopeId) out.envelopeId = extras.envelopeId;\n if (v.signerInfo?.informational) out.informational = true;\n return out;\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, informational = false): string {\n if (titles.length === 0) return '';\n // Nothing is signed in an informational send (view link): \"Document shared: <title>\".\n if (informational && purpose !== 'ATTACH') {\n return titles.length === 1 ? `Document shared: ${titles[0]}` : `${titles.length} documents shared: ${titles[0]}…`;\n }\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 /** Native e-signature requests only, and only when the host turned on `documentSigningOptions`. */\n expiryDays?: number;\n reminderIntervalDays?: number;\n maxReminders?: number;\n /** Only with `documentSigners` and a document with several customer signers (complete, ordered). */\n signers?: SendSigner[];\n /** Only with `sendContext` / `documentContextTypes`. */\n context?: SendContext;\n}\n\n/** The signing settings of a `documents` payload as optional keys of the create-request body (nothing when there are none). */\nexport function signingBodyFields(d: SendDocumentsPayload): Pick<CreateDocumentRequestBody, 'expiryDays' | 'reminderIntervalDays' | 'maxReminders'> {\n const out: Pick<CreateDocumentRequestBody, 'expiryDays' | 'reminderIntervalDays' | 'maxReminders'> = {};\n if (d.expiryDays !== undefined) out.expiryDays = d.expiryDays;\n if (d.reminderIntervalDays !== undefined) out.reminderIntervalDays = d.reminderIntervalDays;\n if (d.maxReminders !== undefined) out.maxReminders = d.maxReminders;\n return out;\n}\n\n/** The body of `POST <baseUrl>/documents/envelopes/{id}/send` for an existing envelope: the create body without the document ids (the envelope already has them). */\nexport type ExistingEnvelopeSendBody = Omit<CreateDocumentRequestBody, 'documentIds'> & { templateId?: string };\n\n/**\n * Sends an envelope that already exists (`documentEnvelope`): `POST <baseUrl>/documents/envelopes/{id}/send` with the recipient, message and\n * options in the body (nothing is created first). The host can replace it with `[sendEmail]` / `[sendText]` (payload `documents.envelopeId`).\n */\nexport function sendExistingEnvelope(http: HttpClient, apiBase: string, envelopeId: string, body: ExistingEnvelopeSendBody): Observable<unknown> {\n return http.post(`${apiBase}/documents/envelopes/${encodeURIComponent(envelopeId)}/send`, body);\n}\n\n/** The `signers` / `context` of a `documents` payload as optional keys of the create-request body (nothing when there are none). */\nexport function signersContextBodyFields(d: SendDocumentsPayload): Pick<CreateDocumentRequestBody, 'signers' | 'context'> {\n const out: Pick<CreateDocumentRequestBody, 'signers' | 'context'> = {};\n if (d.signers?.length) out.signers = d.signers;\n if (d.context) out.context = d.context;\n return out;\n}\n\n/** What the library sends to send ONE document through DocuSign (`POST <baseUrl>/docusign/send`). See docs/send-documents-contract.md. */\nexport interface DocuSignSendBody {\n documentId: string;\n contactId: string;\n subject?: string;\n message?: string;\n countersignRequired: boolean;\n}\n\n/** The default DocuSign send: `POST <baseUrl>/docusign/send` → 200. The host can replace it with `[sendEmail]`. */\nexport function sendDocuSignRequest(http: HttpClient, apiBase: string, body: DocuSignSendBody): Observable<unknown> {\n return http.post(`${apiBase}/docusign/send`, body);\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, computed, isDevMode,\n} from '@angular/core';\nimport { FormsModule } from '@angular/forms';\nimport { HttpClient } from '@angular/common/http';\nimport type { Observable } from 'rxjs';\nimport { FORGEROUTE_CONFIG, getErrorMessage, ButtonComponent, CheckboxComponent, PillTabsComponent, type PillTab, ToastService, ErrorComponent, TextInputComponent, TextareaInputComponent, DatetimeInputComponent, FormSectionComponent, AlertComponent } from '@forgeroute/ui';\nimport { serverMessage } from './server-message';\nimport { takeUntilDestroyed } from '@angular/core/rxjs-interop';\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 { DocumentSendOptionsComponent } from '../document-send-options/document-send-options.component';\nimport type {\n DocumentSendValue, DocumentSendPurpose, DocumentSigningOptions, DocumentPreviewHandler, DocumentPreviewContact, DocumentSeed, DocumentEnvelopeTarget, DocumentOption,\n} from '../document-send-options/document-send-options.component';\nimport { DocumentSignersComponent } from '../document-send-options/document-signers.component';\nimport { DocumentMergeCheckComponent } from '../document-send-options/document-merge-check.component';\nimport { countersignLine, forwardContext } from '../document-send-options/document-signers';\nimport type {\n ContextTypeDef, DocumentSignerInfo, DocumentSignersConfig, MergeCheckFn, SendContext, SendSigner,\n} from '../document-send-options/document-signers';\nimport { VideoLinkSettingsComponent } from '../video-link-settings/video-link-settings.component';\nimport { applyVideoLine, videoLinkWithDefaults, videoMessageLine } from '../video-link-settings/video-link';\nimport type { VideoDefaults } from '../video-link-settings/video-link';\nimport {\n DEFAULT_DOCUMENT_SUBJECT, buildDocumentsPayload, ccError as ccListError, parseCcList, sendDocuSignRequest, sendDocumentRequest,\n sendExistingEnvelope, signersContextBodyFields, signingBodyFields, suggestDocumentSubject,\n} from './send-documents';\nimport type { DocuSignSendBody, DocumentSentMessage, 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 (`&amp;` `&lt;` `&gt;` `&quot;` `&#39;`)\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, '&amp;').replace(/</g, '&lt;').replace(/>/g, '&gt;')\n .replace(/\"/g, '&quot;').replace(/'/g, '&#39;')\n .replace(/\\r\\n|\\r|\\n/g, '<br>');\n}\n\n/** A stable empty list (a child input must not receive a new array on every check). */\nconst NO_DOCUMENTS: DocumentOption[] = [];\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, ErrorComponent, VideoLinkSettingsComponent, DocumentSignersComponent, DocumentMergeCheckComponent, AlertComponent],\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 &amp; 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 <ui-error errorId=\"sm-email-disabled\" [message]=\"emailEnabled() ? '' : EMAIL_DISABLED_MESSAGE\" />\n\n <!-- Signers (only for a document with several customer signers) and the read-only countersign line. Keyed on the document: another document starts with empty rows. -->\n @if (signersSectionShown) {\n @for (k of [signersKey]; track k) {\n <ui-document-signers\n [info]=\"signerInfo\"\n [config]=\"documentSigners()\"\n [recipient]=\"recipientPick()\"\n [viaDocuSign]=\"viaDocuSign\"\n (signersChange)=\"onSigners($event)\"\n (invalidChange)=\"onSignersInvalid($event)\"\n (mapFieldsClick)=\"mapFieldsClick.emit($event)\" />\n }\n }\n\n @if (showCc() && !viaDocuSign) {\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]=\"!viaDocuSign\"\n placeholder=\"Email subject...\"\n [maxlength]=\"MAX_SUBJECT\"\n [(ngModel)]=\"subject\"\n (ngModelChange)=\"notifyValid()\" />\n <ui-textarea-input\n label=\"Message\"\n [required]=\"!viaDocuSign\"\n placeholder=\"Write your message...\"\n [rows]=\"8\"\n [(ngModel)]=\"body\"\n [error]=\"composeTagError || bodyLengthError\"\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\">&times;</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 } @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 [signing]=\"documentSigningOptions()\"\n [signers]=\"documentSigners()\"\n [envelope]=\"documentEnvelope()\"\n [docuSignConnected]=\"docuSignConnected() && !documentEnvelope()\"\n [documentPreview]=\"documentPreview()\"\n [previewContact]=\"previewContact()\"\n [previewSendContext]=\"sendContext()\"\n [showErrors]=\"true\"\n (valueChange)=\"onDocumentsChange($event)\"\n (invalidChange)=\"onDocumentsInvalid($event)\" />\n <ui-document-merge-check\n [enabled]=\"documentMergeCheck()\"\n [documents]=\"mergeDocuments\"\n [customerId]=\"contactId()\"\n [signers]=\"signersActive ? signersValue : null\"\n [context]=\"forwardedContext()\"\n (warningsChange)=\"onMergeWarnings($event)\" />\n <!-- One live region, at most ONE warning banner: every current non-blocking warning is a line in it. Blocking problems are inline on their field. -->\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 }\n @if (warningLines.length > 0) {\n <ui-alert variant=\"warning\" title=\"Heads up\" data-testid=\"send-warnings\">\n <ul class=\"sm-warn-list\">\n @for (w of warningLines; track w.key) { <li [attr.data-testid]=\"w.testId\">{{ w.text }}</li> }\n </ul>\n </ui-alert>\n }\n </div>\n }\n\n <!-- Why Send is disabled for length, in every mode (compose shows the message error inline on the editor itself). -->\n <ui-error errorId=\"sm-length-error\" [message]=\"sendLengthError\" />\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 <!-- Video: the LAST section. The picker, and once a video is attached its settings, Watch Preview and Remove Video. -->\n @if (mode === 'compose') {\n <ui-form-section label=\"Video\" [divider]=\"true\">\n @for (k of [videoPickerKey]; track k) {\n <ui-video-picker (videoSelected)=\"onVideoSelected($event)\" />\n }\n @if (videoPicked && videoDefaults()) {\n <ui-video-link-settings\n [video]=\"videoPicked\"\n [defaults]=\"videoDefaults()!\"\n [contactId]=\"contactId()\"\n (linkChange)=\"onVideoLink($event)\"\n (removed)=\"removeVideo()\" />\n }\n </ui-form-section>\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() }} &lt;{{ contactEmail() }}&gt;</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-warn-list { margin: 0; padding-left: 18px; display: flex; flex-direction: column; gap: 2px; }\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); -webkit-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: 90vh; 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<DocumentSeed[]>([]);\n /**\n * Turns on the signer logic: one required contact per customer signer role of the chosen document, the read-only countersign line,\n * informational documents (no signature fields) and the one-signer DocuSign rule. `null` (default) = the single recipient, unchanged.\n */\n readonly documentSigners = input<DocumentSignersConfig | null>(null);\n /** Values the host has for THIS send; only the keys declared in `documentContextTypes` are sent, as `documents.context`. */\n readonly sendContext = input<SendContext | null>(null);\n /** The context keys the app's backend accepts. */\n readonly documentContextTypes = input<ContextTypeDef[]>([]);\n /** `true` = check (default route `POST <baseUrl>/documents/merge-check`), a function = the host's own check; shows a non-blocking \"fields that will be empty\" warning. */\n readonly documentMergeCheck = input<boolean | MergeCheckFn>(false);\n /** An existing envelope to send: its documents are fixed and it is sent (not created). Payload `documents.envelopeId`. `null` (default) = a new request from the chosen documents. */\n readonly documentEnvelope = input<DocumentEnvelopeTarget | null>(null);\n /** false = the app's email sending is switched off (Communication settings): Send stays off and the form says why. */\n readonly emailEnabled = input(true);\n /** Builds the success toast of a document send (default texts otherwise). */\n readonly documentSentMessage = input<DocumentSentMessage | null>(null);\n /** Shows a CC field; the typed addresses are sent as `cc`. Off = payload has no such key. */\n readonly showCc = input(false);\n /** Shows \"Expiry and Reminders\" for e-signature requests (see `DocumentSigningOptions`); sent as `documents.expiryDays` / `reminderIntervalDays` / `maxReminders`. `null` = off. */\n readonly documentSigningOptions = input<DocumentSigningOptions | null | undefined>(null);\n /** The host says DocuSign is connected: offers \"Native E-Sign / Send via DocuSign\" for a single document. Off = native only. */\n readonly docuSignConnected = input(false);\n /** \"Preview Document\" button(s) in the Documents section call this; `null` hides them. */\n readonly documentPreview = input<DocumentPreviewHandler | null>(null);\n /** The recipient handed to `documentPreview` (second argument): the panel's contact inputs, `null` while all four are blank. */\n readonly previewContact = computed<DocumentPreviewContact | null>(() => {\n const c = { id: this.contactId(), name: this.contactName(), email: this.contactEmail(), phone: this.contactPhone() };\n return c.id || c.name || c.email || c.phone ? c : null;\n });\n /**\n * Turns on the video settings (Playback, Call-to-Action, Hide reactions, Watch Preview, Remove Video) once a video is attached, starting\n * from these defaults, and makes the video link carry them. `null` (default) = the attached video's plain share link is inserted, as before.\n */\n readonly videoDefaults = input<VideoDefaults | null>(null);\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 /** A document send was refused because the document / envelope changed meanwhile (404 / 409): the host refreshes its list. */\n readonly rejected = output<void>();\n /** The \"Place your countersignature box\" link under the countersign line was clicked (document row `hasCountersignPlacement: false`). */\n readonly mapFieldsClick = output<{ id: string; title: string }>();\n readonly EMAIL_DISABLED_MESSAGE = 'Email sending is disabled, so this can\\'t be sent. Enable it in Communication Settings.';\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 /** The complete signer list of the signer rows (null while a role has no contact) and whether they break a rule. */\n signersValue: SendSigner[] | null = null;\n signersInvalid = false;\n /** Lines of the merge check (\"These fields will be left empty: …\"). */\n mergeWarnings: string[] = [];\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 // A context key the app did not declare is never sent; say so while developing.\n effect(() => {\n const { dropped } = forwardContext(this.sendContext(), this.documentContextTypes());\n if (dropped.length > 0 && isDevMode()) console.warn(`ui-send-message-panel: sendContext keys not listed in documentContextTypes are not sent: ${dropped.join(', ')}`);\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 env = this.documentEnvelope();\n const next = docs ? suggestDocumentSubject(env ? [env.name] : docs.documents.map(d => d.title), docs.purpose, docs.signerInfo?.informational === true) : '';\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 /** True when the sender chose to send the (single) document through DocuSign: DocuSign emails it itself, so CC, attachments and the required Subject / Message do not apply. */\n get viaDocuSign(): boolean { return this.hasDocuments && this.documentsValue?.provider === 'DOCUSIGN'; }\n\n /** The recipient as Signer 1 (a stable object: it feeds a child input). */\n readonly recipientPick = computed(() => ({ id: this.contactId(), name: this.contactName(), email: this.contactEmail() }));\n\n\n /** The declared context keys with a value (`documents.context`); keys the app did not declare are dropped (with a development warning). */\n readonly forwardedContext = computed<SendContext | undefined>(() => forwardContext(this.sendContext(), this.documentContextTypes()).context);\n\n /** What the signer logic concluded for the chosen documents (null when `documentSigners` is off or nothing is chosen). */\n get signerInfo(): DocumentSignerInfo | null { return this.documents() ? (this.documentsValue?.signerInfo ?? null) : null; }\n\n /** The chosen documents the merge check looks at. */\n get mergeDocuments(): DocumentOption[] { return this.documentsValue?.documents ?? NO_DOCUMENTS; }\n\n /** True when a native e-signature request for ONE document with several customer signers is being built: the signer rows are required. */\n get signersActive(): boolean {\n const info = this.signerInfo;\n return !!this.documentSigners() && this.hasDocuments && info?.status === 'ready' && info.multiSigner\n && !this.viaDocuSign && this.documentsValue?.purpose !== 'ATTACH';\n }\n\n /** The signer section is shown: signer rows, and/or the countersign line (a signed document that is not just attached). */\n get signersSectionShown(): boolean {\n const info = this.signerInfo;\n if (!this.documentSigners() || !this.hasDocuments || info?.status !== 'ready' || this.documentsValue?.purpose === 'ATTACH') return false;\n return this.signersActive || countersignLine(info, this.documentSigners(), this.viaDocuSign) !== '';\n }\n\n /** The signer rows restart whenever the chosen document changes. */\n get signersKey(): string { return this.documentsValue?.documentIds.join(',') ?? ''; }\n\n onSigners(v: SendSigner[] | null) { this.signersValue = v; this.notifyValid(); }\n onSignersInvalid(bad: boolean) { this.signersInvalid = bad; this.notifyValid(); }\n onMergeWarnings(lines: string[]) { this.mergeWarnings = lines; this.cdr.markForCheck(); }\n\n /** The lines of the ONE warning banner: what choosing a document removed, a multi-signer document among several, the merge check. */\n get warningLines(): { key: string; text: string; testId: string | null }[] {\n const out: { key: string; text: string; testId: string | null }[] = [];\n if (this.hasDocuments && this.docsRemovedNote) out.push({ key: 'removed', text: this.docsRemovedNote, testId: 'documents-removed-note' });\n if (this.hasDocuments && this.signerInfo?.multiDocumentNote) {\n out.push({ key: 'multi', text: 'Documents with several signers are sent one at a time.', testId: 'documents-multi-signer-note' });\n }\n if (this.hasDocuments) this.mergeWarnings.forEach((text, i) => out.push({ key: `merge${i}`, text, testId: 'merge-check-warning' }));\n return out;\n }\n\n /** The people copied on the email, as typed (empty unless `showCc` is on, and not used by a DocuSign send). */\n get ccList(): string[] { return this.showCc() && !this.viaDocuSign ? parseCcList(this.ccText) : []; }\n\n /** The first problem with the CC field ('' when fine). */\n get ccError(): string { return this.showCc() && !this.viaDocuSign ? 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 if (!this.signersActive) { this.signersValue = null; this.signersInvalid = false; }\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 /** Server limits (400 \"Subject must be 255 characters or fewer.\" / \"Body must be 10000 characters or fewer.\"). */\n readonly MAX_SUBJECT = 255;\n readonly MAX_BODY = 10000;\n\n /** Inline error when the message, as it will be sent, is over the server limit ('' when fine). */\n get bodyLengthError(): string {\n return this.resolvedBody.length > this.MAX_BODY\n ? `Message is too long (${this.resolvedBody.length.toLocaleString('en-US')} of ${this.MAX_BODY.toLocaleString('en-US')} characters).` : '';\n }\n\n /** Inline error when the subject, as it will be sent, is over the server limit ('' when fine). */\n get subjectLengthError(): string {\n return this.resolvedSubject.length > this.MAX_SUBJECT\n ? `Subject is too long (${this.resolvedSubject.length.toLocaleString('en-US')} of ${this.MAX_SUBJECT} characters).` : '';\n }\n\n /** The length problem shown under the form in every mode; compose mode shows the message one on the editor instead. */\n get sendLengthError(): string {\n return this.subjectLengthError || (this.mode === 'compose' ? '' : this.bodyLengthError);\n }\n\n get canSend(): boolean {\n if (!this.emailEnabled()) return false;\n if (this.bodyLengthError || this.subjectLengthError) return false;\n if (this.ccError || (this.documents() && this.documentsInvalid)) return false;\n // Several customer signers: every role has a contact (never a partial list), each with an email and no one twice.\n if (this.signersActive && (this.signersInvalid || !this.signersValue)) return false;\n // A DocuSign send needs the contact record (the server loads the signer from it) but no Subject / Message: DocuSign has its own defaults.\n if (this.viaDocuSign && !this.contactId()) return false;\n const hasContent = (this.viaDocuSign || (!!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.discardVideo();\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 /** The body of the default DocuSign send: Subject / Message only when the sender has them (DocuSign has its own defaults otherwise). */\n private docuSignBody(docs: DocumentSendValue, payload: SendEmailPayload): DocuSignSendBody {\n const body: DocuSignSendBody = {\n documentId: docs.documentIds[0], contactId: payload.contactId!, countersignRequired: docs.docuSign?.countersign === true,\n };\n if (payload.subject) body.subject = payload.subject;\n if (payload.body) body.message = payload.body;\n return body;\n }\n\n /** The video attached through the settings flow (null = none) and the line it put into the message. */\n videoPicked: VideoMessage | null = null;\n /** Bumped to re-create the video picker (it keeps its own selection and has no clear output). */\n videoPickerKey = 0;\n private _videoLine = '';\n\n /** The picker's output: with `videoDefaults` the video gets its settings section, otherwise the plain link is inserted as before. */\n onVideoSelected(v: VideoMessage) {\n if (!this.videoDefaults()) { this.insertVideo(v); return; }\n if (!v.shareLink) return;\n this.videoPicked = v;\n // The first link is written here (an event, not change detection): the video's own defaults plus the recipient.\n this.onVideoLink(videoLinkWithDefaults(v.shareLink, this.videoDefaults()!, this.contactId()));\n this.cdr.markForCheck();\n }\n\n /** The settings section's output: rewrites the video line of the message with the current link (the rest of the message is untouched). */\n onVideoLink(link: string) {\n const v = this.videoPicked;\n if (!v || !link) return;\n const next = videoMessageLine(v.title, link);\n this.body = applyVideoLine(this.body, this._videoLine, next);\n this._videoLine = next;\n this.notifyValid();\n }\n\n /** \"Remove Video\": takes the video line out of the message and brings the picker back. */\n removeVideo() {\n this.body = applyVideoLine(this.body, this._videoLine, '');\n this.discardVideo();\n this.notifyValid();\n }\n\n private discardVideo() {\n if (!this.videoPicked && !this._videoLine) return;\n this.videoPicked = null; this._videoLine = ''; this.videoPickerKey++;\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 = buildDocumentsPayload(docs, 'EMAIL', docs.purpose, {\n signers: this.signersActive ? this.signersValue : null, context: this.forwardedContext(), envelopeId: this.documentEnvelope()?.id,\n });\n payload.attachments = undefined;\n payload.scheduledAt = undefined;\n }\n const custom = this.sendFn();\n const req$ = custom ? custom(payload)\n : docs?.provider === 'DOCUSIGN' ? sendDocuSignRequest(this.http, this.apiBase, this.docuSignBody(docs, payload))\n : docs && this.documentEnvelope() ? sendExistingEnvelope(this.http, this.apiBase, this.documentEnvelope()!.id, {\n subject: payload.subject || DEFAULT_DOCUMENT_SUBJECT, message: payload.body,\n customerId: payload.contactId, customerName: payload.contactName, customerEmail: payload.contactEmail,\n purpose: docs.purpose, delivery: 'EMAIL',\n ...(cc.length ? { ccEmails: cc } : {}),\n ...signingBodyFields(payload.documents!),\n ...signersContextBodyFields(payload.documents!),\n ...(payload.templateId ? { templateId: payload.templateId } : {}),\n })\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 ...(cc.length ? { ccEmails: cc } : {}),\n ...signingBodyFields(payload.documents!),\n ...signersContextBodyFields(payload.documents!),\n }, payload.templateId)\n : this.http.post(`${this.apiBase}/email-messages`, payload);\n req$.pipe(takeUntilDestroyed(this.destroyRef)).subscribe({\n next: () => {\n const sentText = docs ? this.documentSentMessage()?.({\n titles: docs.documents.map(d => d.title), envelopeName: this.documentEnvelope()?.name, channel: 'email',\n provider: docs.provider, informational: docs.signerInfo?.informational === true,\n }) : undefined;\n this.toast.show(sentText ?? (docs?.provider === 'DOCUSIGN' ? `\"${docs.documents[0].title}\" sent via DocuSign`\n : docs && this.documentEnvelope() ? `Envelope \"${this.documentEnvelope()!.name}\" sent` : 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 // The server's own {message} wins (e.g. a length limit); with none the default endpoint keeps its fixed text.\n this.toast.error(custom || docs ? getErrorMessage(err, 'email', 'send') : (serverMessage(err) || 'Failed to send email'));\n this.loading = false; this.cdr.markForCheck();\n const status = (err as { status?: number } | null)?.status;\n if (docs && (status === 404 || status === 409)) this.rejected.emit();\n },\n });\n }\n}\n","import {\n Component, input, output, effect, untracked, OnInit,\n inject, ChangeDetectionStrategy, ChangeDetectorRef, DestroyRef, signal, computed, isDevMode,\n} from '@angular/core';\nimport { FormsModule } from '@angular/forms';\nimport { HttpClient } from '@angular/common/http';\nimport type { Observable } from 'rxjs';\nimport { FORGEROUTE_CONFIG, getErrorMessage, CheckboxComponent, PillTabsComponent, type PillTab, ToastService, ButtonComponent, TextInputComponent, TextareaInputComponent, DatetimeInputComponent, FormSectionComponent, AlertComponent } from '@forgeroute/ui';\nimport { serverMessage } from './server-message';\nimport { takeUntilDestroyed } from '@angular/core/rxjs-interop';\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 { DocumentSendOptionsComponent } from '../document-send-options/document-send-options.component';\nimport type {\n DocumentSendValue, DocumentSendPurpose, DocumentSigningOptions, DocumentPreviewHandler, DocumentPreviewContact, DocumentSeed, DocumentEnvelopeTarget, DocumentOption,\n} from '../document-send-options/document-send-options.component';\nimport { DocumentMergeCheckComponent } from '../document-send-options/document-merge-check.component';\nimport { forwardContext } from '../document-send-options/document-signers';\nimport type { ContextTypeDef, DocumentSignersConfig, MergeCheckFn, SendContext } from '../document-send-options/document-signers';\nimport { VideoLinkSettingsComponent } from '../video-link-settings/video-link-settings.component';\nimport { applyVideoLine, videoLinkWithDefaults, videoMessageLine } from '../video-link-settings/video-link';\nimport type { VideoDefaults } from '../video-link-settings/video-link';\nimport {\n DEFAULT_DOCUMENT_SUBJECT, buildDocumentsPayload, sendDocumentRequest, sendExistingEnvelope, signersContextBodyFields, signingBodyFields,\n} from './send-documents';\nimport type { DocumentSentMessage, 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/** A stable empty list (a child input must not receive a new array on every check). */\nconst NO_DOCUMENTS: DocumentOption[] = [];\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, VideoLinkSettingsComponent, DocumentMergeCheckComponent, AlertComponent],\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 &amp; 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 || videoLengthError\"\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\">&times;</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 } @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 [signing]=\"documentSigningOptions()\"\n [signers]=\"documentSigners()\"\n [envelope]=\"documentEnvelope()\"\n [documentPreview]=\"documentPreview()\"\n [previewContact]=\"previewContact()\"\n [previewSendContext]=\"sendContext()\"\n [showErrors]=\"true\"\n (valueChange)=\"onDocumentsChange($event)\"\n (invalidChange)=\"onDocumentsInvalid($event)\" />\n <ui-document-merge-check\n [enabled]=\"documentMergeCheck()\"\n [documents]=\"mergeDocuments\"\n [customerId]=\"contactId()\"\n [context]=\"forwardedContext()\"\n (warningsChange)=\"onMergeWarnings($event)\" />\n <!-- One live region, at most ONE warning banner (every current non-blocking warning is a line in it). -->\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 }\n @if (warningLines.length > 0) {\n <ui-alert variant=\"warning\" title=\"Heads up\" data-testid=\"send-warnings\">\n <ul class=\"sm-warn-list\">\n @for (w of warningLines; track w.key) { <li [attr.data-testid]=\"w.testId\">{{ w.text }}</li> }\n </ul>\n </ui-alert>\n }\n </div>\n }\n\n <!-- Video: the LAST section. The picker, and once a video is attached its settings, Watch Preview and Remove Video. -->\n @if (mode === 'compose') {\n <ui-form-section label=\"Video\" [divider]=\"true\">\n @for (k of [videoPickerKey]; track k) {\n <ui-video-picker (videoSelected)=\"onVideoSelected($event)\" />\n }\n @if (videoPicked && videoDefaults()) {\n <ui-video-link-settings\n [video]=\"videoPicked\"\n [defaults]=\"videoDefaults()!\"\n [contactId]=\"contactId()\"\n (linkChange)=\"onVideoLink($event)\"\n (removed)=\"removeVideo()\" />\n }\n </ui-form-section>\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-warn-list { margin: 0; padding-left: 18px; display: flex; flex-direction: column; gap: 2px; }\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); -webkit-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: 85vh; 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 /** Builds the success toast of a document send (default texts otherwise). */\n readonly documentSentMessage = input<DocumentSentMessage | 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<DocumentSeed[]>([]);\n /**\n * Turns on the signer logic for documents (informational documents, the multi-signer rule). A document with several customer signers\n * can't be texted (the link goes to the first signer only): the section says \"Send it by email.\" and Send stays off. `null` = unchanged.\n */\n readonly documentSigners = input<DocumentSignersConfig | null>(null);\n /** Values the host has for THIS send; only the keys declared in `documentContextTypes` are sent, as `documents.context`. */\n readonly sendContext = input<SendContext | null>(null);\n /** The context keys the app's backend accepts. */\n readonly documentContextTypes = input<ContextTypeDef[]>([]);\n /** `true` = check (default route `POST <baseUrl>/documents/merge-check`), a function = the host's own check; shows a non-blocking \"fields that will be empty\" warning. */\n readonly documentMergeCheck = input<boolean | MergeCheckFn>(false);\n /** An existing envelope to text: its documents are fixed and it is sent (not created). Payload `documents.envelopeId`. */\n readonly documentEnvelope = input<DocumentEnvelopeTarget | null>(null);\n /** Shows \"Expiry and Reminders\" for the texted signing link (see `DocumentSigningOptions`); sent as `documents.expiryDays` / `reminderIntervalDays` / `maxReminders`. `null` = off. */\n readonly documentSigningOptions = input<DocumentSigningOptions | null | undefined>(null);\n /** \"Preview Document\" button(s) in the Documents section call this; `null` hides them. */\n readonly documentPreview = input<DocumentPreviewHandler | null>(null);\n /** The recipient handed to `documentPreview` (second argument): the panel's contact inputs, `null` while all four are blank. */\n readonly previewContact = computed<DocumentPreviewContact | null>(() => {\n const c = { id: this.contactId(), name: this.contactName(), email: this.contactEmail(), phone: this.contactPhone() };\n return c.id || c.name || c.email || c.phone ? c : null;\n });\n /**\n * Turns on the video settings (Playback, Call-to-Action, Hide reactions, Watch Preview, Remove Video) once a video is attached, starting\n * from these defaults, and makes the video link carry them. `null` (default) = the plain share link is inserted, as before.\n * A text is limited to 320 characters: a link with settings is longer, and Send stays off while the message is over the limit.\n */\n readonly videoDefaults = input<VideoDefaults | null>(null);\n\n readonly sent = output<void>();\n readonly phoneAdded = output<string>();\n readonly validChange = output<boolean>();\n /** A document send was refused because the document / envelope changed meanwhile (404 / 409): the host refreshes its list. */\n readonly rejected = output<void>();\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 /** Lines of the merge check (\"These fields will be left empty: …\"). */\n mergeWarnings: string[] = [];\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 // A context key the app did not declare is never sent; say so while developing.\n effect(() => {\n const { dropped } = forwardContext(this.sendContext(), this.documentContextTypes());\n if (dropped.length > 0 && isDevMode()) console.warn(`ui-send-message-panel: sendContext keys not listed in documentContextTypes are not sent: ${dropped.join(', ')}`);\n });\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 /** The declared context keys with a value (`documents.context`). */\n readonly forwardedContext = computed<SendContext | undefined>(() => forwardContext(this.sendContext(), this.documentContextTypes()).context);\n /** The chosen documents the merge check looks at. */\n get mergeDocuments(): DocumentOption[] { return this.documentsValue?.documents ?? NO_DOCUMENTS; }\n onMergeWarnings(lines: string[]) { this.mergeWarnings = lines; this.cdr.markForCheck(); }\n /** The lines of the ONE warning banner: what choosing a document removed, the merge check. */\n get warningLines(): { key: string; text: string; testId: string | null }[] {\n const out: { key: string; text: string; testId: string | null }[] = [];\n if (this.hasDocuments && this.docsRemovedNote) out.push({ key: 'removed', text: this.docsRemovedNote, testId: 'documents-removed-note' });\n if (this.hasDocuments) this.mergeWarnings.forEach((text, i) => out.push({ key: `merge${i}`, text, testId: 'merge-check-warning' }));\n return out;\n }\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 // A video link with settings can push the text over the SMS limit (the plain-link path truncates instead).\n if (this._videoLine && this.body.length > this.MAX_LENGTH) 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 /** Why Send is off when the video link pushes the text over the SMS limit ('' otherwise). */\n get videoLengthError(): string {\n return this._videoLine && this.body.length > this.MAX_LENGTH\n ? `The video link makes this text longer than ${this.MAX_LENGTH} characters. Shorten the message or remove the video.` : '';\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.discardVideo();\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 /** The video attached through the settings flow (null = none) and the line it put into the message. */\n videoPicked: VideoMessage | null = null;\n /** Bumped to re-create the video picker (it keeps its own selection and has no clear output). */\n videoPickerKey = 0;\n private _videoLine = '';\n\n /** The picker's output: with `videoDefaults` the video gets its settings section, otherwise the plain link is inserted as before. */\n onVideoSelected(v: VideoMessage) {\n if (!this.videoDefaults()) { this.insertVideo(v); return; }\n if (!v.shareLink) return;\n this.videoPicked = v;\n // The first link is written here (an event, not change detection): the video's own defaults plus the recipient.\n this.onVideoLink(videoLinkWithDefaults(v.shareLink, this.videoDefaults()!, this.contactId()));\n this.cdr.detectChanges();\n }\n\n /** The settings section's output: rewrites the video line of the message with the current link (the rest of the message is untouched). */\n onVideoLink(link: string) {\n const v = this.videoPicked;\n if (!v || !link) return;\n const next = videoMessageLine(v.title, link);\n this.body = applyVideoLine(this.body, this._videoLine, next);\n this._videoLine = next;\n this.notifyValid();\n }\n\n /** \"Remove Video\": takes the video line out of the message and brings the picker back. */\n removeVideo() {\n this.body = applyVideoLine(this.body, this._videoLine, '');\n this.discardVideo();\n this.notifyValid();\n }\n\n private discardVideo() {\n if (!this.videoPicked && !this._videoLine) return;\n this.videoPicked = null; this._videoLine = ''; this.videoPickerKey++;\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 = buildDocumentsPayload(docs, 'TEXT', 'ESIGN', { context: this.forwardedContext(), envelopeId: this.documentEnvelope()?.id });\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 && this.documentEnvelope() ? sendExistingEnvelope(this.http, this.apiBase, this.documentEnvelope()!.id, {\n subject: DEFAULT_DOCUMENT_SUBJECT, message: payload.body,\n customerId: payload.contactId, customerName: payload.contactName, customerEmail: this.contactEmail() || undefined,\n customerPhone: payload.contactPhone,\n purpose: 'ESIGN', delivery: 'TEXT',\n ...signingBodyFields(payload.documents!),\n ...signersContextBodyFields(payload.documents!),\n ...(payload.templateId ? { templateId: payload.templateId } : {}),\n })\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 ...signingBodyFields(payload.documents!),\n ...signersContextBodyFields(payload.documents!),\n }, payload.templateId)\n : this.http.post(`${this.apiBase}/text-messages`, payload);\n req$.pipe(takeUntilDestroyed(this.destroyRef)).subscribe({\n next: () => {\n const sentText = docs ? this.documentSentMessage()?.({\n titles: docs.documents.map(d => d.title), envelopeName: this.documentEnvelope()?.name, channel: 'text',\n informational: docs.signerInfo?.informational === true,\n }) : undefined;\n this.toast.show(sentText ?? (docs && this.documentEnvelope() ? `Envelope \"${this.documentEnvelope()!.name}\" sent` : 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') : (serverMessage(err) || 'Failed to send text message'));\n this.loading = false; this.cdr.detectChanges();\n const status = (err as { status?: number } | null)?.status;\n if (docs && (status === 404 || status === 409)) this.rejected.emit();\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, type ComboboxOption, type ComboboxSelection, SidePanelComponent, ToggleBadgeComponent, ContactPickerComponent, type ContactLookup } from '@forgeroute/ui';\nimport type { Observable } from 'rxjs';\nimport type {\n DocumentSendPurpose, DocumentSigningOptions, DocumentPreviewHandler, DocumentSeed, DocumentEnvelopeTarget,\n} from '../document-send-options/document-send-options.component';\nimport type { ContextTypeDef, DocumentSignersConfig, MergeCheckFn, SendContext } from '../document-send-options/document-signers';\nimport type { VideoDefaults } from '../video-link-settings/video-link';\nimport type { DocumentSentMessage } from './send-documents';\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 [wide]=\"wide()\"\n cancelLabel=\"Cancel\"\n [primaryLabel]=\"!loading() && activeContact() ? (sendLabel() || (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 [documentSigningOptions]=\"documentSigningOptions()\"\n [documentSigners]=\"documentSigners()\"\n [sendContext]=\"sendContext()\"\n [documentContextTypes]=\"documentContextTypes()\"\n [documentMergeCheck]=\"documentMergeCheck()\"\n [documentEnvelope]=\"documentEnvelope()\"\n [emailEnabled]=\"emailEnabled()\"\n [documentSentMessage]=\"documentSentMessage()\"\n (rejected)=\"rejected.emit()\"\n (mapFieldsClick)=\"mapFieldsClick.emit($event)\"\n [docuSignConnected]=\"docuSignConnected()\"\n [documentPreview]=\"documentPreview()\"\n [videoDefaults]=\"videoDefaults()\"\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 [documentSigningOptions]=\"documentSigningOptions()\"\n [documentSigners]=\"documentSigners()\"\n [sendContext]=\"sendContext()\"\n [documentContextTypes]=\"documentContextTypes()\"\n [documentMergeCheck]=\"documentMergeCheck()\"\n [documentEnvelope]=\"documentEnvelope()\"\n [documentSentMessage]=\"documentSentMessage()\"\n (rejected)=\"rejected.emit()\"\n [documentPreview]=\"documentPreview()\"\n [videoDefaults]=\"videoDefaults()\"\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<DocumentSeed[]>([]);\n /**\n * Turns on the signer logic of the Documents section (docs/send-documents-contract.md, Addendum 2026-10-19), all off when `null` (default):\n * - a document with MORE THAN ONE customer signer shows one required contact picker per further role (Signer 1 is the recipient), Send\n * stays off until every role has a contact, and `documents.signers` (complete, ordered by role) is added to the payload;\n * - a document that requires the sender's countersignature shows one read-only line (\"Countersigned by <name> after all signers.\");\n * - a document with no signature fields (`signerRoles: []`) is informational: no signers, countersign line, DocuSign or signing options -\n * the recipient gets a view link;\n * - DocuSign is offered only for a signed document with one customer signer, and the countersign checkbox is replaced by the document's setting.\n * The roles come from the `GET /documents` row (`signerRoles`) or from `resolveRoles`. A multi-signer document can't be texted.\n */\n readonly documentSigners = input<DocumentSignersConfig | null>(null);\n /** Values the host has for THIS send (e.g. `{ propertyId }`); only keys listed in `documentContextTypes` are sent, as `documents.context`. */\n readonly sendContext = input<SendContext | null>(null);\n /** The context keys the app's backend accepts. */\n readonly documentContextTypes = input<ContextTypeDef[]>([]);\n /**\n * The pre-send \"fields that will be empty\" check, for documents with mapped data fields only (non-blocking warning): `true` = default route\n * `POST <baseUrl>/documents/merge-check`, a function = your own call, `false` (default) = off.\n */\n readonly documentMergeCheck = input<boolean | MergeCheckFn>(false);\n /**\n * Sends an EXISTING envelope: its documents are fixed (no search, no remove), the recipient is chosen in the panel, and the request is sent\n * (`POST <baseUrl>/documents/envelopes/{id}/send`, or your `sendEmail` / `sendText` with `documents.envelopeId`) instead of created.\n * Turns the Documents section on by itself. `null` (default) = a new request from the chosen documents.\n */\n readonly documentEnvelope = input<DocumentEnvelopeTarget | null>(null);\n /** Builds the success toast of a document send (`\"<title>\" sent for signature`, `Envelope \"<name>\" sent` …); the default texts apply when unset. */\n readonly documentSentMessage = input<DocumentSentMessage | null>(null);\n /** false = the app's email sending is switched off (Communication settings): the Email tab says so and Send stays off. Default true. */\n readonly emailEnabled = input(true);\n /** The footer button text instead of `Send Email` / `Send Text` (e.g. `Send Document`, `Send Envelope`). */\n readonly sendLabel = input('');\n /** The 620px wide panel (a long form with signers). Default false. */\n readonly wide = input(false);\n /**\n * Shows \"Expiry and Reminders\" in the Documents section (Expiry 1-90 days, Reminder Interval 1-30 days, Max Reminders 0-10; required\n * whole numbers) for native e-signature requests, on the Email tab (purpose e-signature or both) and the Text tab. Pass `{}` for the\n * defaults 7 / 2 / 3 or set any of `expiryDays`, `reminderIntervalDays`, `maxReminders`. The values are sent as\n * `documents.expiryDays` / `reminderIntervalDays` / `maxReminders`. `null` (default) = not shown, nothing sent.\n */\n readonly documentSigningOptions = input<DocumentSigningOptions | null | undefined>(null);\n /**\n * Email tab: the host says DocuSign is connected (the library never calls `/docusign/status`). Offers \"Native E-Sign / Send via DocuSign\"\n * while exactly one document is chosen; DocuSign sends through `POST <baseUrl>/docusign/send` (or your `sendEmail`, which then gets\n * `documents.provider === 'DOCUSIGN'`). A document row/`initialDocuments` entry with `requiresCountersign: true` also offers \"Require my\n * countersignature after client signs\". Default `false` = native only.\n */\n readonly docuSignConnected = input(false);\n /**\n * \"Preview Document\" in the Documents section: called with the chosen document when its button is clicked. The library has no route\n * of its own — the host opens its viewer. Open a tab synchronously inside this function (Safari blocks `window.open` after an await).\n * `null` (default) = no preview button.\n */\n readonly documentPreview = input<DocumentPreviewHandler | null>(null);\n /**\n * The \"Video\" section (always the last section of the Email and Text forms): once a video is attached it shows Playback, Call-to-Action\n * (+ URL / label), Hide reactions, Watch Preview and Remove Video, starting from these defaults (your Communication settings), and the\n * link in the message carries them (`buildVideoLink`). `null` (default) = the plain share link is inserted, with no settings.\n */\n readonly videoDefaults = input<VideoDefaults | null>(null);\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.documentEnvelope()) && 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 /** A document / envelope send was refused because it changed meanwhile (404 / 409): the host refreshes its list. */\n readonly rejected = output<void>();\n /**\n * With `documentSigners` on: the user clicked \"Place your countersignature box\" under the countersign line of a document that requires a\n * countersignature but whose row says `hasCountersignPlacement: false` (docs/send-documents-contract.md, Addendum 2026-10-19 (b)).\n * Payload = that document `{ id, title }`; the host navigates to Map Data Fields. Never emitted when the flag is true / absent.\n */\n readonly mapFieldsClick = output<{ id: string; title: 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"],"mappings":"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"}