@mintplayer/ng-spark 22.26.0 → 22.28.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +6 -4
- package/fesm2022/mintplayer-ng-spark-grid.mjs +73 -50
- package/fesm2022/mintplayer-ng-spark-grid.mjs.map +1 -1
- package/fesm2022/mintplayer-ng-spark-history.mjs +1 -1
- package/fesm2022/mintplayer-ng-spark-history.mjs.map +1 -1
- package/fesm2022/mintplayer-ng-spark-models.mjs +19 -10
- package/fesm2022/mintplayer-ng-spark-models.mjs.map +1 -1
- package/fesm2022/mintplayer-ng-spark-moderation.mjs +1 -1
- package/fesm2022/mintplayer-ng-spark-moderation.mjs.map +1 -1
- package/fesm2022/mintplayer-ng-spark-pipes.mjs +5 -2
- package/fesm2022/mintplayer-ng-spark-pipes.mjs.map +1 -1
- package/fesm2022/mintplayer-ng-spark-po-create.mjs +16 -7
- package/fesm2022/mintplayer-ng-spark-po-create.mjs.map +1 -1
- package/fesm2022/mintplayer-ng-spark-po-detail.mjs +30 -14
- package/fesm2022/mintplayer-ng-spark-po-detail.mjs.map +1 -1
- package/fesm2022/mintplayer-ng-spark-po-edit.mjs +45 -15
- package/fesm2022/mintplayer-ng-spark-po-edit.mjs.map +1 -1
- package/fesm2022/mintplayer-ng-spark-po-form.mjs +4 -4
- package/fesm2022/mintplayer-ng-spark-po-form.mjs.map +1 -1
- package/fesm2022/mintplayer-ng-spark-query-list.mjs +18 -2
- package/fesm2022/mintplayer-ng-spark-query-list.mjs.map +1 -1
- package/fesm2022/mintplayer-ng-spark-renderers.mjs +2 -1
- package/fesm2022/mintplayer-ng-spark-renderers.mjs.map +1 -1
- package/fesm2022/mintplayer-ng-spark-retry-action-modal.mjs +0 -1
- package/fesm2022/mintplayer-ng-spark-retry-action-modal.mjs.map +1 -1
- package/fesm2022/mintplayer-ng-spark-services.mjs +12 -9
- package/fesm2022/mintplayer-ng-spark-services.mjs.map +1 -1
- package/fesm2022/mintplayer-ng-spark-soft-delete.mjs +4 -3
- package/fesm2022/mintplayer-ng-spark-soft-delete.mjs.map +1 -1
- package/package.json +3 -3
- package/types/mintplayer-ng-spark-grid.d.ts +50 -46
- package/types/mintplayer-ng-spark-models.d.ts +75 -40
- package/types/mintplayer-ng-spark-moderation.d.ts +8 -4
- package/types/mintplayer-ng-spark-pipes.d.ts +3 -0
- package/types/mintplayer-ng-spark-po-create.d.ts +4 -0
- package/types/mintplayer-ng-spark-po-detail.d.ts +12 -2
- package/types/mintplayer-ng-spark-po-edit.d.ts +16 -0
- package/types/mintplayer-ng-spark-po-form.d.ts +1 -0
- package/types/mintplayer-ng-spark-query-list.d.ts +11 -1
- package/types/mintplayer-ng-spark-services.d.ts +38 -9
- package/types/mintplayer-ng-spark-soft-delete.d.ts +1 -1
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"mintplayer-ng-spark-po-form.mjs","names":[],"sources":["../../po-form/src/spark-reference-picker.component.ts","../../po-form/src/spark-reference-picker.component.html","../../po-form/src/spark-lookup-picker.component.ts","../../po-form/src/spark-lookup-picker.component.html","../../po-form/src/refresh-coordinator.ts","../../po-form/src/spark-po-form.component.ts","../../po-form/src/spark-po-form.component.html"],"sourcesContent":["import { ChangeDetectionStrategy, Component, computed, inject, input, output, signal } from '@angular/core';\nimport { FormsModule } from '@angular/forms';\nimport { Color } from '@mintplayer/ng-bootstrap';\nimport { BsFormComponent, BsFormControlDirective } from '@mintplayer/ng-bootstrap/form';\nimport { BsGridComponent, BsGridRowDirective, BsGridColumnDirective } from '@mintplayer/ng-bootstrap/grid';\nimport { BsInputGroupComponent } from '@mintplayer/ng-bootstrap/input-group';\nimport { BsButtonTypeDirective } from '@mintplayer/ng-bootstrap/button-type';\nimport { BsModalHostComponent, BsModalDirective, BsModalHeaderDirective, BsModalBodyDirective, BsModalFooterDirective } from '@mintplayer/ng-bootstrap/modal';\nimport { BsDatatableComponent, BsDatatableColumnDirective, BsRowTemplateDirective, DatatableSettings } from '@mintplayer/ng-bootstrap/datatable';\nimport { BsSpinnerComponent } from '@mintplayer/ng-bootstrap/spinner';\nimport { PaginationResponse } from '@mintplayer/pagination';\nimport { SparkService, SparkLanguageService } from '@mintplayer/ng-spark/services';\nimport { EntityType, QueryResultItem } from '@mintplayer/ng-spark/models';\nimport { TranslateKeyPipe, ResolveTranslationPipe, ReferenceAttrValuePipe } from '@mintplayer/ng-spark/pipes';\nimport { SparkAttributeDescriptionComponent } from '@mintplayer/ng-spark/attribute-description';\nimport { SparkIconComponent } from '@mintplayer/ng-spark/icon';\n\n/**\n * Reusable Reference value picker: a readonly textbox showing the current selection's\n * breadcrumb/name plus a \"…\" button that opens a searchable modal grid over the candidate\n * `options`. Per-instance state (no single top-level slot), so it works for top-level\n * reference attributes and for individual inline AsDetail reference cells alike.\n *\n * The parent pre-loads `options` (the query result) and passes `referenceType` (the target\n * CLR type); the component lazily loads that EntityType's metadata on first open to render\n * the grid's column headers. Emits the picked id via `valueChange`.\n */\n@Component({\n selector: 'spark-reference-picker',\n imports: [FormsModule, BsFormComponent, BsFormControlDirective, BsGridComponent, BsGridRowDirective, BsGridColumnDirective, BsInputGroupComponent, BsButtonTypeDirective, BsModalHostComponent, BsModalDirective, BsModalHeaderDirective, BsModalBodyDirective, BsModalFooterDirective, BsDatatableComponent, BsDatatableColumnDirective, BsRowTemplateDirective, BsSpinnerComponent, SparkIconComponent, TranslateKeyPipe, ResolveTranslationPipe, ReferenceAttrValuePipe, SparkAttributeDescriptionComponent],\n templateUrl: './spark-reference-picker.component.html',\n changeDetection: ChangeDetectionStrategy.OnPush\n})\nexport class SparkReferencePickerComponent {\n private readonly sparkService = inject(SparkService);\n private readonly lang = inject(SparkLanguageService);\n\n /** Currently selected referenced id (or null when unset). */\n value = input<string | null>(null);\n /** Candidate items to pick from (the full query result, pre-loaded by the parent). */\n options = input<QueryResultItem[]>([]);\n /** Target entity type's CLR type name; used to load the grid's column headers. */\n referenceType = input<string | undefined>(undefined);\n /** Renders the field with the Bootstrap is-invalid state. */\n isInvalid = input(false);\n /** Optional id forwarded to the readonly input (for label association). */\n inputId = input<string | undefined>(undefined);\n /** Optional resolved label appended to the modal header (\"Select {title}\"). */\n title = input<string>('');\n\n valueChange = output<string | null>();\n\n colors = Color;\n\n showModal = signal(false);\n entityType = signal<EntityType | null>(null);\n pagination = signal<PaginationResponse<QueryResultItem> | undefined>(undefined);\n settings = signal(new DatatableSettings({\n perPage: { values: [10, 25, 50], selected: 10 },\n page: { values: [1], selected: 1 },\n sortColumns: []\n }));\n searchTerm = '';\n\n visibleAttributes = computed(() => {\n return this.entityType()?.attributes\n .filter(a => a.isVisible)\n .sort((a, b) => a.order - b.order) || [];\n });\n\n // Typed rows so the datatable generic infers the row type.\n rows = computed<QueryResultItem[]>(() => this.pagination()?.data ?? []);\n\n displayValue = computed(() => {\n const id = this.value();\n if (!id) return this.lang.t('common.notSelected');\n const selected = this.options().find(o => o.id === id);\n return selected?.breadcrumb || id;\n });\n\n async open(): Promise<void> {\n this.searchTerm = '';\n this.settings.set(new DatatableSettings({\n perPage: { values: [10, 25, 50], selected: 10 },\n page: { values: [1], selected: 1 },\n sortColumns: []\n }));\n\n // Lazily resolve the target entity type for the grid's column headers.\n const refType = this.referenceType();\n if (!this.entityType() && refType) {\n const types = await this.sparkService.getEntityTypes();\n this.entityType.set(types.find(t => t.clrType === refType) || null);\n }\n\n this.applyFilter();\n this.showModal.set(true);\n }\n\n onSearchChange(): void {\n this.settings().page.selected = 1;\n this.applyFilter();\n }\n\n applyFilter(): void {\n let filteredItems = this.options();\n\n if (this.searchTerm.trim()) {\n const term = this.searchTerm.toLowerCase().trim();\n filteredItems = this.options().filter(item => {\n if (item.breadcrumb?.toLowerCase().includes(term)) return true;\n return item.values.some(v => {\n const value = v.breadcrumb ?? v.value;\n if (value == null) return false;\n return String(value).toLowerCase().includes(term);\n });\n });\n }\n\n const totalPages = Math.ceil(filteredItems.length / this.settings().perPage.selected) || 1;\n this.pagination.set({\n data: filteredItems,\n totalRecords: filteredItems.length,\n totalPages: totalPages,\n perPage: this.settings().perPage.selected,\n page: this.settings().page.selected\n });\n\n this.settings().page.values = Array.from({ length: totalPages }, (_, i) => i + 1);\n\n if (this.settings().page.selected > totalPages) {\n this.settings().page.selected = 1;\n }\n }\n\n clearSearch(): void {\n this.searchTerm = '';\n this.onSearchChange();\n }\n\n select(item: QueryResultItem): void {\n this.valueChange.emit(item.id ?? null);\n this.close();\n }\n\n close(): void {\n this.showModal.set(false);\n this.searchTerm = '';\n }\n}\n","<bs-input-group>\n <input\n type=\"text\"\n [id]=\"inputId()\"\n [value]=\"displayValue()\"\n readonly\n [class.is-invalid]=\"isInvalid()\">\n <button\n type=\"button\"\n bsInputGroupBtn\n [color]=\"colors.secondary\"\n (click)=\"open()\">\n ...\n </button>\n</bs-input-group>\n\n<bs-modal [isOpen]=\"showModal()\" (isOpenChange)=\"!$event && close()\">\n <div *bsModal class=\"reference-modal\">\n <div bsModalHeader>\n <h5 class=\"modal-title\">{{ 'common.select' | t }} {{ title() }}</h5>\n </div>\n\n <div bsModalBody>\n <!-- bs-modal portals its content into a CDK overlay outside the host form, so the\n modal needs its own <bs-form> context for inputs to pick up .form-control styles. -->\n <bs-form>\n @if (entityType()) {\n <!-- Search box -->\n <bs-grid>\n <div bsRow class=\"mb-3\">\n <div [md]=\"12\">\n <bs-input-group>\n <span class=\"addon\">\n <spark-icon name=\"search\" />\n </span>\n <input\n type=\"text\"\n [placeholder]=\"'common.search' | t\"\n [(ngModel)]=\"searchTerm\"\n (ngModelChange)=\"onSearchChange()\">\n @if (searchTerm) {\n <button\n type=\"button\"\n class=\"btn btn-outline-secondary\"\n (click)=\"clearSearch()\">\n <spark-icon name=\"x-lg\" />\n </button>\n }\n </bs-input-group>\n </div>\n @if (searchTerm && pagination()) {\n <div [md]=\"12\" class=\"text-end mt-1\">\n <span class=\"text-muted\">\n {{ pagination()!.totalRecords }} {{ pagination()!.totalRecords === 1 ? ('common.resultFound' | t) : ('common.resultsFound' | t) }}\n </span>\n </div>\n }\n </div>\n </bs-grid>\n\n <bs-datatable\n [(settings)]=\"settings\"\n [data]=\"rows()\"\n (rowClick)=\"select($event.row)\">\n @for (attr of visibleAttributes(); track attr.id) {\n <div *bsDatatableColumn=\"attr.name; sortable: true\">\n {{ attr.label | resolveTranslation:attr.name }}\n <spark-attribute-description [description]=\"attr.description\" />\n </div>\n }\n\n <ng-container *bsRowTemplate=\"let item\">\n @for (attr of visibleAttributes(); track attr.id) {\n <td>{{ $any(item) | referenceAttrValue:attr.name }}</td>\n }\n </ng-container>\n </bs-datatable>\n } @else {\n <div class=\"text-center p-3\">\n <bs-spinner />\n </div>\n }\n </bs-form>\n </div>\n\n <div bsModalFooter>\n <button type=\"button\" [color]=\"colors.secondary\" (click)=\"close()\">{{ 'common.cancel' | t }}</button>\n </div>\n </div>\n</bs-modal>\n","import { ChangeDetectionStrategy, Component, computed, input, output, signal } from '@angular/core';\nimport { FormsModule } from '@angular/forms';\nimport { Color } from '@mintplayer/ng-bootstrap';\nimport { BsFormComponent, BsFormControlDirective } from '@mintplayer/ng-bootstrap/form';\nimport { BsGridComponent, BsGridRowDirective, BsGridColDirective } from '@mintplayer/ng-bootstrap/grid';\nimport { BsInputGroupComponent } from '@mintplayer/ng-bootstrap/input-group';\nimport { BsButtonTypeDirective } from '@mintplayer/ng-bootstrap/button-type';\nimport { BsModalHostComponent, BsModalDirective, BsModalHeaderDirective, BsModalBodyDirective, BsModalFooterDirective } from '@mintplayer/ng-bootstrap/modal';\nimport { BsTableComponent } from '@mintplayer/ng-bootstrap/table';\nimport { LookupReferenceValue, resolveTranslation } from '@mintplayer/ng-spark/models';\nimport { TranslateKeyPipe, ResolveTranslationPipe } from '@mintplayer/ng-spark/pipes';\nimport { SparkIconComponent } from '@mintplayer/ng-spark/icon';\n\n/**\n * Reusable LookupReference value picker: a readonly textbox showing the current selection's\n * translated label plus a \"…\" button that opens a searchable modal list over the active\n * `options`. Per-instance state, so it serves both top-level lookup attributes (display type\n * Modal) and individual inline AsDetail lookup cells. Emits the picked key via `valueChange`.\n */\n@Component({\n selector: 'spark-lookup-picker',\n imports: [FormsModule, BsFormComponent, BsFormControlDirective, BsGridComponent, BsGridRowDirective, BsGridColDirective, BsInputGroupComponent, BsButtonTypeDirective, BsModalHostComponent, BsModalDirective, BsModalHeaderDirective, BsModalBodyDirective, BsModalFooterDirective, BsTableComponent, SparkIconComponent, TranslateKeyPipe, ResolveTranslationPipe],\n templateUrl: './spark-lookup-picker.component.html',\n changeDetection: ChangeDetectionStrategy.OnPush\n})\nexport class SparkLookupPickerComponent {\n /** Currently selected lookup key (or null when unset). */\n value = input<string | null>(null);\n /** Active lookup values to pick from. */\n options = input<LookupReferenceValue[]>([]);\n /** Renders the field with the Bootstrap is-invalid state. */\n isInvalid = input(false);\n /** Optional id forwarded to the readonly input (for label association). */\n inputId = input<string | undefined>(undefined);\n /** Optional resolved label appended to the modal header (\"Select {title}\"). */\n title = input<string>('');\n\n valueChange = output<string | null>();\n\n colors = Color;\n\n showModal = signal(false);\n searchTerm = signal('');\n\n displayValue = computed(() => {\n const key = this.value();\n if (key == null || key === '') return '';\n const selected = this.options().find(o => o.key === String(key));\n if (!selected) return String(key);\n return resolveTranslation(selected.values) || selected.key;\n });\n\n filteredItems = computed(() => {\n if (!this.searchTerm().trim()) {\n return this.options();\n }\n const term = this.searchTerm().toLowerCase().trim();\n return this.options().filter(item => {\n const translation = resolveTranslation(item.values);\n return translation.toLowerCase().includes(term) || item.key.toLowerCase().includes(term);\n });\n });\n\n open(): void {\n this.searchTerm.set('');\n this.showModal.set(true);\n }\n\n select(item: LookupReferenceValue): void {\n this.valueChange.emit(item.key);\n this.close();\n }\n\n close(): void {\n this.showModal.set(false);\n this.searchTerm.set('');\n }\n}\n","<bs-input-group>\n <input\n type=\"text\"\n [id]=\"inputId()\"\n [value]=\"displayValue()\"\n readonly\n [class.is-invalid]=\"isInvalid()\">\n <button\n type=\"button\"\n bsInputGroupBtn\n [color]=\"colors.secondary\"\n (click)=\"open()\">\n ...\n </button>\n</bs-input-group>\n\n<bs-modal [isOpen]=\"showModal()\" (isOpenChange)=\"!$event && close()\">\n <div *bsModal>\n <div bsModalHeader>\n <h5 class=\"modal-title\">{{ 'common.select' | t }} {{ title() }}</h5>\n </div>\n\n <div bsModalBody>\n <!-- bs-modal portals its content into a CDK overlay outside the host form, so the\n modal needs its own <bs-form> context for inputs to pick up .form-control styles. -->\n <bs-form>\n <!-- Search box -->\n <bs-grid>\n <div bsRow class=\"mb-3\">\n <div [col]>\n <bs-input-group>\n <span class=\"addon\">\n <spark-icon name=\"search\" />\n </span>\n <input\n type=\"text\"\n [placeholder]=\"'common.search' | t\"\n [ngModel]=\"searchTerm()\"\n (ngModelChange)=\"searchTerm.set($event)\">\n @if (searchTerm()) {\n <button\n type=\"button\"\n class=\"btn btn-outline-secondary\"\n (click)=\"searchTerm.set('')\">\n <spark-icon name=\"x-lg\" />\n </button>\n }\n </bs-input-group>\n </div>\n </div>\n </bs-grid>\n\n <!-- List of items -->\n <bs-table [striped]=\"true\" [hover]=\"true\">\n <tbody class=\"align-middle\">\n @for (item of filteredItems(); track item.key) {\n <tr\n [class.table-primary]=\"value() === item.key\"\n (click)=\"select(item)\"\n style=\"cursor: pointer;\">\n <td>{{ item.values | resolveTranslation:item.key }}</td>\n </tr>\n } @empty {\n <tr>\n <td class=\"text-center text-muted\">{{ 'common.noItemsFound' | t }}</td>\n </tr>\n }\n </tbody>\n </bs-table>\n </bs-form>\n </div>\n\n <div bsModalFooter>\n <button type=\"button\" [color]=\"colors.secondary\" (click)=\"close()\">{{ 'common.cancel' | t }}</button>\n </div>\n </div>\n</bs-modal>\n","import { PersistentObject, RefreshOverlay, RefreshTrigger } from '@mintplayer/ng-spark/models';\n\n/** What the coordinator needs from its host, so it can be tested without mounting a form. */\nexport interface RefreshCoordinatorHost {\n /** POSTs the object and resolves with the reshaped one. */\n send(triggeredBy: string): Promise<PersistentObject>;\n /** Values as they are right now — read at dispatch time to snapshot what is being sent. */\n currentValues(): Record<string, any>;\n /** Applies a settled response. Not called for a superseded one. */\n apply(response: PersistentObject, sent: Record<string, any>): void;\n /** Surfaced so the host can show a busy affordance. Never used to disable fields. */\n setBusy(busy: boolean): void;\n}\n\n/**\n * Serializes refreshes for **one** form instance and drops superseded ones.\n *\n * Per-instance rather than a service, deliberately. The retry-action modal renders its own\n * `spark-po-form`, and a refresh can carry a retry operation — so a refresh can open a modal\n * containing a form whose own attributes may trigger refreshes. A shared coordinator would let the\n * nested form resolve or supersede the outer form's pending request. The same applies to the\n * recursive `spark-po-form` used for modal AsDetail editing.\n *\n * Cancellation is not available: the service layer is promise-based (`firstValueFrom`), so a stale\n * response *will* arrive. It is discarded by sequence number rather than prevented.\n */\nexport class RefreshCoordinator {\n private queue: Promise<void> = Promise.resolve();\n private sequence = 0;\n private settled = 0;\n private pending = new Set<string>();\n /** Debounce timers for `ValueChanged` free text, keyed by path. A path here is also in `pending`. */\n private timers = new Map<string, ReturnType<typeof setTimeout>>();\n\n constructor(private readonly host: RefreshCoordinatorHost) {}\n\n /** Whether a refresh is in flight. */\n get isRefreshing(): boolean {\n return this.sequence !== this.settled;\n }\n\n /**\n * Marks `attributeName` as needing a refresh without sending one — for free-text editors, which\n * would otherwise issue a request per keystroke. Flushed by {@link blur} or {@link flush}.\n */\n markPending(attributeName: string): void {\n this.pending.add(attributeName);\n }\n\n /**\n * Routes a change by its {@link RefreshDispatch}: send now, send once typing pauses, or wait for\n * blur. The one entry point a form needs, so the three editors' policies live here.\n */\n dispatch(attributeName: string, mode: RefreshDispatch): Promise<void> {\n switch (mode) {\n case 'immediate':\n return this.trigger(attributeName);\n case 'debounced':\n return this.trigger(attributeName, { debounceMs: SPARK_REFRESH_DEBOUNCE_MS });\n case 'blur':\n this.markPending(attributeName);\n return Promise.resolve();\n }\n }\n\n /** Sends a pending refresh for `attributeName`, if one was marked (or is waiting on its debounce). */\n blur(attributeName: string): Promise<void> {\n this.clearTimer(attributeName);\n if (!this.pending.delete(attributeName)) return Promise.resolve();\n return this.trigger(attributeName);\n }\n\n /**\n * Sends every refresh still marked pending, including any waiting on a debounce. Called before\n * save, so a value typed and never blurred — the user tabbing straight to the save button — is\n * still reflected before the object goes.\n */\n async flush(): Promise<void> {\n for (const name of [...this.timers.keys()]) this.clearTimer(name);\n const names = [...this.pending];\n this.pending.clear();\n for (const name of names) await this.trigger(name);\n await this.queue;\n }\n\n /** Drops every pending mark and debounce timer without sending — the form is going away. */\n dispose(): void {\n for (const name of [...this.timers.keys()]) this.clearTimer(name);\n this.pending.clear();\n }\n\n /**\n * Sends a refresh — immediately for discrete editors, where every change is a committed one.\n *\n * With `debounceMs`, the send waits until `attributeName` has been quiet for that long; each call\n * restarts the wait, so a burst of keystrokes costs one request. Until it fires, the path counts as\n * pending, so {@link blur} and {@link flush} send it straight away instead of waiting out the timer.\n */\n trigger(attributeName: string, options?: { debounceMs?: number }): Promise<void> {\n const debounceMs = options?.debounceMs ?? 0;\n if (debounceMs > 0) {\n this.clearTimer(attributeName);\n this.pending.add(attributeName);\n this.timers.set(attributeName, setTimeout(() => {\n this.timers.delete(attributeName);\n if (this.pending.delete(attributeName)) void this.trigger(attributeName);\n }, debounceMs));\n return Promise.resolve();\n }\n\n // An immediate send covers anything this path still had waiting.\n this.clearTimer(attributeName);\n this.pending.delete(attributeName);\n\n const ticket = ++this.sequence;\n this.host.setBusy(true);\n\n this.queue = this.queue.then(async () => {\n const sent = { ...this.host.currentValues() };\n try {\n const response = await this.host.send(attributeName);\n\n // A newer refresh was dispatched while this one was in flight, so this response describes a\n // form state that no longer exists. Applying it would resurrect superseded metadata.\n if (ticket !== this.sequence) return;\n\n this.host.apply(response, sent);\n } finally {\n this.settled = Math.max(this.settled, ticket);\n if (!this.isRefreshing) this.host.setBusy(false);\n }\n });\n\n return this.queue;\n }\n\n private clearTimer(attributeName: string): void {\n const timer = this.timers.get(attributeName);\n if (timer === undefined) return;\n clearTimeout(timer);\n this.timers.delete(attributeName);\n }\n}\n\n/** How long `ValueChanged` free text waits for typing to pause before refreshing — like the search box. */\nexport const SPARK_REFRESH_DEBOUNCE_MS = 300;\n\n/** The subset of an attribute definition that decides how its editor behaves. */\nexport interface RefreshTriggerShape {\n dataType?: string;\n lookupReferenceType?: string;\n isArray?: boolean;\n triggersRefresh?: RefreshTrigger;\n}\n\n/** When a change sends its refresh: on the change itself, on blur, or never. */\nexport type EffectiveRefreshTrigger = 'change' | 'blur' | 'none';\n\n/** How {@link RefreshCoordinator.dispatch} handles a change. */\nexport type RefreshDispatch = 'immediate' | 'debounced' | 'blur';\n\n/**\n * Resolves the declared `triggersRefresh` against the editor the attribute renders as.\n *\n * - `None` / absent → `'none'`.\n * - `Auto` → `'change'` for a discrete editor, `'blur'` for free text (the original behaviour).\n * - `ValueChanged` → `'change'` (debounced for free text — see {@link refreshDispatch}).\n * - `Blur` → `'blur'`, except on a discrete editor, which has no meaningful blur and so acts as\n * `ValueChanged`. The server's `--spark-verify-model` warns about that declaration.\n */\nexport function effectiveTrigger(attr: RefreshTriggerShape | undefined): EffectiveRefreshTrigger {\n switch (attr?.triggersRefresh) {\n case 'Auto':\n return isDiscreteEditor(attr) ? 'change' : 'blur';\n case 'ValueChanged':\n return 'change';\n case 'Blur':\n return isDiscreteEditor(attr) ? 'change' : 'blur';\n default:\n return 'none';\n }\n}\n\n/**\n * How a change to `attr` is sent, or `null` when it sends nothing. A `'change'` trigger on free\n * text is debounced; on a discrete editor it is immediate.\n */\nexport function refreshDispatch(attr: RefreshTriggerShape | undefined): RefreshDispatch | null {\n switch (effectiveTrigger(attr)) {\n case 'change':\n return isDiscreteEditor(attr) ? 'immediate' : 'debounced';\n case 'blur':\n return 'blur';\n default:\n return null;\n }\n}\n\n/** Whether the change to `attr` is sent immediately (a discrete editor with a change trigger). */\nexport function triggersImmediately(attr: RefreshTriggerShape | undefined): boolean {\n return refreshDispatch(attr) === 'immediate';\n}\n\n/**\n * Editors where every change is a deliberate, committed one. Everything else is free text, where\n * firing per keystroke would be unacceptable.\n *\n * ⚠️ This asks how the attribute is **rendered**, not what its `dataType` says, and the difference\n * is not academic. A lookup attribute carries the data type of its *key* — Fleet's `Car.Status` is\n * `dataType: \"string\"` with `lookupReferenceType: \"CarStatus\"` — so keying on `dataType` alone\n * classifies a `<bs-select>` as free text. It then waits for a blur that a select never emits, and\n * the refresh simply never fires: no request, no error, nothing to see.\n */\nexport function isDiscreteEditor(attr: RefreshTriggerShape | undefined): boolean {\n if (!attr) return false;\n\n // A lookup renders as a select (or a modal picker) whatever its key's type is.\n if (attr.lookupReferenceType) return true;\n\n switch ((attr.dataType ?? '').toLowerCase()) {\n case 'reference':\n case 'lookupreference':\n case 'boolean':\n case 'bool':\n case 'date':\n case 'datetime':\n case 'dateonly':\n case 'enum':\n case 'color':\n return true;\n default:\n return false;\n }\n}\n\n/** Empty overlay, so callers do not have to spell the type. */\nexport const NO_OVERLAY: RefreshOverlay = {};\n","import { ChangeDetectionStrategy, Component, computed, DestroyRef, inject, input, model, output, signal, effect, Type } from '@angular/core';\nimport { CommonModule, NgComponentOutlet, NgTemplateOutlet } from '@angular/common';\nimport { FormsModule } from '@angular/forms';\nimport { HttpErrorResponse } from '@angular/common/http';\nimport { CdkDropList, CdkDrag, CdkDragHandle, CdkDragPreview, CdkDragDrop, moveItemInArray } from '@angular/cdk/drag-drop';\nimport { Color } from '@mintplayer/ng-bootstrap';\nimport { BsCardComponent, BsCardHeaderComponent } from '@mintplayer/ng-bootstrap/card';\nimport { BsFormComponent, BsFormControlDirective } from '@mintplayer/ng-bootstrap/form';\nimport { BsGridComponent, BsGridRowDirective, BsGridColumnDirective, BsGridColDirective, BsColFormLabelDirective } from '@mintplayer/ng-bootstrap/grid';\nimport { BsInputGroupComponent } from '@mintplayer/ng-bootstrap/input-group';\nimport { BsButtonTypeDirective } from '@mintplayer/ng-bootstrap/button-type';\nimport { BsSelectComponent, BsSelectOption } from '@mintplayer/ng-bootstrap/select';\nimport { BsTreeSelectComponent, InMemoryTreeSelectProvider, TreeNode } from '@mintplayer/ng-bootstrap/tree-select';\nimport { BsModalHostComponent, BsModalDirective, BsModalHeaderDirective, BsModalBodyDirective, BsModalFooterDirective } from '@mintplayer/ng-bootstrap/modal';\nimport { BsCheckboxComponent } from '@mintplayer/ng-bootstrap/checkbox';\nimport { BsSpinnerComponent } from '@mintplayer/ng-bootstrap/spinner';\nimport { BsTabControlComponent, BsTabPageComponent, BsTabPageHeaderDirective } from '@mintplayer/ng-bootstrap/tab-control';\nimport { BsTableComponent } from '@mintplayer/ng-bootstrap/table';\nimport { SparkService, SparkLanguageService } from '@mintplayer/ng-spark/services';\nimport {\n TranslateKeyPipe,\n ResolveTranslationPipe,\n InputTypePipe,\n LookupDisplayTypePipe,\n LookupOptionsPipe,\n AsDetailDisplayValuePipe,\n AsDetailTypePipe,\n AsDetailColumnsPipe,\n AsDetailRowRendererPipe,\n AsDetailCellValuePipe,\n CanCreateDetailRowPipe,\n CanEditDetailRowPipe,\n CanDeleteDetailRowPipe,\n InlineRefOptionsPipe,\n ErrorForAttributePipe,\n} from '@mintplayer/ng-spark/pipes';\nimport {\n AttributeGroup,\n AttributeTab,\n ELookupDisplayType,\n EReferenceDisplayType,\n EntityAttributeDefinition,\n EntityPermissions,\n EntityType,\n LookupReference,\n LookupReferenceValue,\n PersistentObject,\n QueryResultItem,\n RefreshOverlay,\n RefreshedOption,\n RuleFailure,\n ShowedOn,\n ValidationError,\n AS_DETAIL_ROW_KEY,\n applyOverlay,\n nestedPoToDict,\n evaluateRules,\n hasShowedOnFlag,\n mergeRefreshValues,\n overlayFromResponse,\n resolveTranslation,\n} from '@mintplayer/ng-spark/models';\nimport { SparkIconComponent } from '@mintplayer/ng-spark/icon';\nimport { SparkAttributeDescriptionComponent } from '@mintplayer/ng-spark/attribute-description';\nimport { SPARK_ATTRIBUTE_RENDERERS, SparkResolvedRowRenderer, rowRendererInputs, withDeclaredInputs } from '@mintplayer/ng-spark/renderers';\nimport { SparkReferencePickerComponent } from './spark-reference-picker.component';\nimport { SparkLookupPickerComponent } from './spark-lookup-picker.component';\nimport { RefreshCoordinator, RefreshDispatch, effectiveTrigger, refreshDispatch } from './refresh-coordinator';\n\n/**\n * A refresh a form embedded in another form is asking its host to issue on its behalf.\n *\n * `dispatch` carries the decision the child's own editor made from its declared `triggersRefresh`\n * — send now, send once typing pauses, or wait for blur — so the host applies exactly the semantics\n * it would have applied to an editor of its own.\n */\nexport interface NestedTriggerRequest {\n /** The full path the server addresses, e.g. `Gate.ProjectMode`. */\n path: string;\n dispatch: RefreshDispatch;\n}\n\n@Component({\n selector: 'spark-po-form',\n imports: [CommonModule, NgTemplateOutlet, NgComponentOutlet, FormsModule, CdkDropList, CdkDrag, CdkDragHandle, CdkDragPreview, BsCardComponent, BsCardHeaderComponent, BsFormComponent, BsFormControlDirective, BsGridComponent, BsGridRowDirective, BsGridColumnDirective, BsGridColDirective, BsColFormLabelDirective, BsButtonTypeDirective, BsInputGroupComponent, BsSelectComponent, BsSelectOption, BsTreeSelectComponent, BsModalHostComponent, BsModalDirective, BsModalHeaderDirective, BsModalBodyDirective, BsModalFooterDirective, BsTableComponent, BsCheckboxComponent, BsSpinnerComponent, BsTabControlComponent, BsTabPageComponent, BsTabPageHeaderDirective, SparkIconComponent, SparkPoFormComponent, SparkReferencePickerComponent, SparkLookupPickerComponent, TranslateKeyPipe, ResolveTranslationPipe, InputTypePipe, LookupDisplayTypePipe, LookupOptionsPipe, AsDetailDisplayValuePipe, AsDetailTypePipe, AsDetailColumnsPipe, AsDetailRowRendererPipe, AsDetailCellValuePipe, CanCreateDetailRowPipe, CanDeleteDetailRowPipe, CanEditDetailRowPipe, InlineRefOptionsPipe, ErrorForAttributePipe, SparkAttributeDescriptionComponent],\n templateUrl: './spark-po-form.component.html',\n // The CDK drag placeholder is a clone of the dragged row (so it keeps the exact row\n // height). Hide its contents but keep it occupying space, so the drop gap is blank and\n // the surrounding rows don't shift. ::ng-deep because the cloned node is styled by CDK.\n styles: [`:host ::ng-deep .cdk-drag-placeholder { visibility: hidden; }`],\n changeDetection: ChangeDetectionStrategy.OnPush\n})\nexport class SparkPoFormComponent {\n private readonly sparkService = inject(SparkService);\n private readonly translations = inject(SparkLanguageService);\n protected readonly rendererRegistry = inject(SPARK_ATTRIBUTE_RENDERERS);\n\n entityType = input<EntityType | null>(null);\n formData = model<Record<string, any>>({});\n validationErrors = input<ValidationError[]>([]);\n showButtons = input(false);\n isSaving = input(false);\n parentId = input<string | undefined>(undefined);\n parentType = input<string | undefined>(undefined);\n /**\n * The parent a Reference attribute's option query runs under, when it differs from\n * `parentId`/`parentType`. Those two also name the object that owns an AsDetail row for the row\n * round-trips (`parentTypeForWire`), so they cannot carry a create page's sub-query parent: a New\n * opened from an Account's card must offer that account's repositories, while its detail rows still\n * belong to the type being created. Unset, the option queries use `parentId`/`parentType` as before.\n */\n referenceParentId = input<string | undefined>(undefined);\n referenceParentType = input<string | undefined>(undefined);\n\n private readonly optionQueryParent = computed(() => this.referenceParentId() && this.referenceParentType()\n ? { parentId: this.referenceParentId(), parentType: this.referenceParentType() }\n : { parentId: this.parentId(), parentType: this.parentType() });\n\n save = output<void>();\n cancel = output<void>();\n\n /**\n * The type id to refresh against. Absent means refresh is unavailable — the form still renders and\n * edits normally, so a host that has not opted in loses nothing.\n */\n objectTypeId = input<string | undefined>(undefined);\n /** The id of the object being edited; absent for a create. */\n objectId = input<string | undefined>(undefined);\n\n /**\n * Set when this form renders a single embedded AsDetail object inside a host form: the name of\n * the attribute holding it, e.g. `\"Gate\"`. Presence of a prefix switches the form from issuing\n * its own refreshes to reporting them upward, because an embedded object is not something the\n * server can be asked about on its own — the request must describe, and be authorized against,\n * the object that owns it.\n */\n triggerPathPrefix = input<string | null>(null);\n\n /** Raised instead of self-issuing when {@link triggerPathPrefix} is set. */\n nestedTriggerRequested = output<NestedTriggerRequest>();\n /** Blur counterpart, flushing a trigger the host only marked pending. */\n nestedTriggerBlurred = output<string>();\n\n /**\n * What the last refresh changed about each attribute's presentation, keyed by attribute name.\n *\n * Deliberately NOT folded back into `entityType`. All option loading hangs off one effect keyed on\n * `entityType` identity and `SparkService` caches nothing, so re-setting it would re-issue every\n * reference query and lookup fetch on every refresh; mutating it in place would not re-render at\n * all.\n *\n * ⚠️ A `model`, not a plain signal, because the page that owns the save has to see it. This form\n * renders from the overlaid attribute list; `spark-po-edit` and `spark-po-create` build the\n * payload from their own copy of the same filter. While the overlay stopped here, those two\n * filtered on the attribute's state *as loaded* — so an attribute a refresh hook revealed was\n * rendered, filled in by the user, and then silently left out of the save. When the same hook also\n * made it required (the ordinary case — a field appears *because* it is now needed) the server\n * refused the save for the very field the user had just filled, and the form became a dead end\n * with no way forward from inside it.\n */\n refreshOverlay = model<RefreshOverlay>({});\n isRefreshing = signal(false);\n\n colors = Color;\n referenceOptions = signal<Record<string, QueryResultItem[]>>({});\n\n // Multi-reference (Reference && isArray) editor state. Built from the same query\n // results as referenceOptions: a flat TreeNode list per attribute drives an\n // InMemoryTreeSelectProvider (the <bs-tree-select> data port), and a node lookup\n // resolves selected ids back to chip labels.\n referenceProviders = signal<Record<string, InMemoryTreeSelectProvider>>({});\n referenceNodes = signal<Record<string, Record<string, TreeNode>>>({});\n asDetailTypes = signal<Record<string, EntityType>>({});\n lookupReferenceOptions = signal<Record<string, LookupReference>>({});\n\n // Modal state for AsDetail object editing\n editingAsDetailAttr = signal<EntityAttributeDefinition | null>(null);\n asDetailFormData = signal<Record<string, any>>({});\n showAsDetailModal = signal(false);\n editingArrayIndex = signal<number | null>(null);\n\n // Permissions for array AsDetail entity types\n asDetailPermissions = signal<Record<string, EntityPermissions>>({});\n\n /**\n * What the server said when it refused to construct or release a row.\n *\n * A local signal rather than the `validationErrors` input, because this is the one class of error\n * the form raises on its own behalf: `validationErrors` is owned by the host (po-edit / po-create)\n * and arrives from a save. Cleared at the start of each lifecycle call, so a stale refusal never\n * outlives the click that caused it.\n *\n * Keyed by attribute name so a message renders against the grid it came from. A single global\n * alert would have been less code and worse: a form with two detail grids would show one grid's\n * refusal above the other, and the user's next click is on the grid, not the top of the page.\n */\n rowLifecycleErrors = signal<Record<string, ValidationError[]>>({});\n\n /** The refusal to render under `attrName`'s grid, if any. */\n rowLifecycleErrorsFor(attrName: string): ValidationError[] {\n return this.rowLifecycleErrors()[attrName] ?? [];\n }\n\n // Reference options for columns within array AsDetail types (keyed by parent attr name, then column name)\n asDetailReferenceOptions = signal<Record<string, Record<string, QueryResultItem[]>>>({});\n\n // Reference/Lookup picking is owned by the standalone spark-reference-picker /\n // spark-lookup-picker components (per-instance modal state); the form just feeds them\n // options and writes the emitted value back into formData / the row.\n ELookupDisplayType = ELookupDisplayType;\n EReferenceDisplayType = EReferenceDisplayType;\n\n /**\n * Every attribute this form could ever need option data for — including ones the model hides,\n * because a refresh may reveal them.\n *\n * Read by the option-loading effect, and deliberately independent of `refreshOverlay`: the loaders\n * read this synchronously, so an overlay dependency here would make every refresh re-issue every\n * reference query and lookup fetch. That is the whole reason the overlay is a separate signal.\n */\n optionSourceAttributes = computed(() => {\n return this.entityType()?.attributes\n .filter(a => hasShowedOnFlag(a.showedOn, ShowedOn.PersistentObject))\n .sort((a, b) => a.order - b.order) || [];\n });\n\n editableAttributes = computed(() => {\n const overlay = this.refreshOverlay();\n return this.entityType()?.attributes\n .map(a => applyOverlay(a, overlay[a.name]))\n .filter(a => a.isVisible && !a.isReadOnly && hasShowedOnFlag(a.showedOn, ShowedOn.PersistentObject))\n .sort((a, b) => a.order - b.order) || [];\n });\n\n private static readonly DEFAULT_TAB: AttributeTab = { id: '__default__', name: 'Algemeen', label: { nl: 'Algemeen', en: 'General' }, order: 0 };\n\n ungroupedAttributes = computed(() => {\n const attrs = this.editableAttributes();\n const groupIds = new Set((this.entityType()?.groups || []).map(g => g.id));\n return attrs.filter(a => !a.group || !groupIds.has(a.group));\n });\n\n resolvedTabs = computed((): AttributeTab[] => {\n const et = this.entityType();\n const definedTabs = et?.tabs?.length ? [...et.tabs].sort((a, b) => a.order - b.order) : [];\n const hasUngroupedAttrs = this.ungroupedAttributes().length > 0;\n const hasUntabbedGroups = (et?.groups || []).some(g => !g.tab);\n\n if (hasUngroupedAttrs || hasUntabbedGroups || definedTabs.length === 0) {\n return [SparkPoFormComponent.DEFAULT_TAB, ...definedTabs];\n }\n return definedTabs;\n });\n\n groupsForTab(tab: AttributeTab): AttributeGroup[] {\n const groups = this.entityType()?.groups || [];\n if (tab.id === '__default__') {\n return groups.filter(g => !g.tab).sort((a, b) => a.order - b.order);\n }\n return groups.filter(g => g.tab === tab.id).sort((a, b) => a.order - b.order);\n }\n\n attrsForGroup(group: AttributeGroup): EntityAttributeDefinition[] {\n return this.editableAttributes().filter(a => a.group === group.id);\n }\n\n constructor() {\n // A `ValueChanged` debounce still waiting when the form goes must not fire against a dead form.\n inject(DestroyRef).onDestroy(() => this.refreshCoordinator.dispose());\n\n effect(() => {\n const et = this.entityType();\n const _parent = this.optionQueryParent();\n if (et) {\n this.loadReferenceOptions();\n this.loadAsDetailTypes();\n this.loadLookupReferenceOptions();\n }\n });\n }\n\n private toRecord<T>(entries: [string, T][]): Record<string, T> {\n const result: Record<string, T> = {};\n for (const [key, value] of entries) {\n result[key] = value;\n }\n return result;\n }\n\n async loadReferenceOptions(): Promise<void> {\n const refAttrs = this.optionSourceAttributes().filter(a => a.dataType === 'Reference' && a.query);\n if (refAttrs.length === 0) return;\n\n const entries = await Promise.all(\n refAttrs.filter(a => a.query).map(async attr => {\n const result = await this.sparkService.executeQueryByName(attr.query!, this.optionQueryParent());\n return [attr.name, result.items] as [string, QueryResultItem[]];\n })\n );\n const optionsByAttr = this.toRecord(entries);\n this.referenceOptions.set(optionsByAttr);\n\n // For multi-reference attributes (Reference && isArray), turn each query result\n // into a flat TreeNode list + an in-memory provider for <bs-tree-select>, and a\n // by-id node map used to render chip labels for the current selection.\n const providers: Record<string, InMemoryTreeSelectProvider> = {};\n const nodesByAttr: Record<string, Record<string, TreeNode>> = {};\n for (const attr of refAttrs) {\n if (!attr.isArray) continue;\n const pos = optionsByAttr[attr.name] || [];\n const nodes: TreeNode[] = pos\n .filter(po => !!po.id)\n .map(po => ({ id: po.id!, label: po.breadcrumb || po.id! }));\n providers[attr.name] = new InMemoryTreeSelectProvider(nodes);\n nodesByAttr[attr.name] = this.toRecord(nodes.map(n => [n.id, n] as [string, TreeNode]));\n }\n this.referenceProviders.set(providers);\n this.referenceNodes.set(nodesByAttr);\n }\n\n /**\n * Stable TreeNode[] value per multi-reference attribute, derived from the id array\n * in formData and the resolved node map. Recomputes only when formData or the node\n * map changes, so binding it into <bs-tree-select> doesn't churn on every CD pass.\n * Ids without a resolved node fall back to a label of the id itself.\n */\n referenceTreeValues = computed<Record<string, TreeNode[]>>(() => {\n const fd = this.formData();\n const nodesByAttr = this.referenceNodes();\n const result: Record<string, TreeNode[]> = {};\n for (const attr of this.editableAttributes()) {\n if (attr.dataType !== 'Reference' || !attr.isArray) continue;\n const ids: string[] = Array.isArray(fd[attr.name]) ? fd[attr.name] : [];\n const nodeMap = nodesByAttr[attr.name] || {};\n result[attr.name] = ids.map(id => nodeMap[id] ?? { id, label: id });\n }\n return result;\n });\n\n getReferenceProvider(attr: EntityAttributeDefinition): InMemoryTreeSelectProvider | undefined {\n return this.referenceProviders()[attr.name];\n }\n\n onReferenceTreeChange(attr: EntityAttributeDefinition, value: TreeNode | TreeNode[] | null): void {\n const nodes = Array.isArray(value) ? value : value ? [value] : [];\n const data = { ...this.formData() };\n data[attr.name] = nodes.map(n => n.id);\n this.formData.set(data);\n // A picked value is a discrete change: triggersRefresh fires now, as for a checkbox or a select.\n this.noteChange(attr);\n }\n\n async loadAsDetailTypes(): Promise<void> {\n const asDetailAttrs = this.optionSourceAttributes().filter(a => a.dataType === 'AsDetail' && a.asDetailType);\n if (asDetailAttrs.length === 0) return;\n\n const types = await this.sparkService.getEntityTypes();\n const newAsDetailTypes: Record<string, EntityType> = {};\n\n // The catalogue is Query-gated, and a row type edited through its parent usually has no rights\n // of its own — so it is normally absent from `types`, and resolving from there alone left\n // `asDetailTypes` empty. `asDetailColumns` then returned [] and the table rendered with no\n // columns at all. The server now ships the row definitions alongside the parent, gated on the\n // parent's right; the catalogue stays the first source so nothing changes for a type that is\n // in it (#385).\n const embedded = this.entityType()?.detailTypes ?? [];\n\n for (const attr of asDetailAttrs) {\n const asDetailType =\n types.find(t => t.clrType === attr.asDetailType)\n ?? embedded.find(t => t.clrType === attr.asDetailType);\n if (asDetailType) {\n newAsDetailTypes[attr.name] = asDetailType;\n\n if (attr.isArray) {\n // Ask by CLR type when the row type carries no alias — the embedded copy has its alias\n // pruned, and `getPermissions` accepts either.\n const perms = await this.sparkService.getPermissions(asDetailType.id);\n this.asDetailPermissions.update(prev => ({ ...prev, [attr.name]: perms }));\n\n const refCols = asDetailType.attributes.filter(a => a.dataType === 'Reference' && a.query);\n if (refCols.length > 0) {\n const refEntries = await Promise.all(\n refCols.filter(c => c.query).map(async col => {\n const result = await this.sparkService.executeQueryByName(col.query!, this.optionQueryParent());\n return [col.name, result.items] as [string, QueryResultItem[]];\n })\n );\n this.asDetailReferenceOptions.update(prev => ({ ...prev, [attr.name]: this.toRecord(refEntries) }));\n }\n\n // Inline editing of LookupReference child columns needs their options loaded\n // (keyed by lookup name in the shared lookupReferenceOptions, deduped).\n const lookupCols = asDetailType.attributes.filter(a => a.lookupReferenceType);\n for (const col of lookupCols) {\n const lookupName = col.lookupReferenceType!;\n if (!this.lookupReferenceOptions()[lookupName]) {\n const ref = await this.sparkService.getLookupReference(lookupName);\n this.lookupReferenceOptions.update(prev => ({ ...prev, [lookupName]: ref }));\n }\n }\n }\n }\n }\n this.asDetailTypes.set(newAsDetailTypes);\n }\n\n async loadLookupReferenceOptions(): Promise<void> {\n const lookupAttrs = this.optionSourceAttributes().filter(a => a.lookupReferenceType);\n if (lookupAttrs.length === 0) return;\n\n const lookupNames = [...new Set(lookupAttrs.map(a => a.lookupReferenceType!))];\n const entries = await Promise.all(\n lookupNames.map(async name => {\n const ref = await this.sparkService.getLookupReference(name);\n return [name, ref] as [string, LookupReference];\n })\n );\n this.lookupReferenceOptions.set(this.toRecord(entries));\n }\n\n getLookupOptions(attr: EntityAttributeDefinition): LookupReferenceValue[] {\n const lookupRef = attr.lookupReferenceType ? this.lookupReferenceOptions()[attr.lookupReferenceType] : null;\n return lookupRef?.values.filter(v => v.isActive) || [];\n }\n\n // Write the value emitted by a top-level spark-reference-picker / spark-lookup-picker\n // back into formData (the same write the old in-form modal selectors performed).\n onReferenceValueChange(attr: EntityAttributeDefinition, id: string | null): void {\n const data = { ...this.formData() };\n data[attr.name] = id;\n this.formData.set(data);\n this.noteChange(attr);\n }\n\n onLookupValueChange(attr: EntityAttributeDefinition, key: string | null): void {\n const data = { ...this.formData() };\n data[attr.name] = key;\n this.formData.set(data);\n this.noteChange(attr);\n }\n\n getEditRendererComponent(attr: EntityAttributeDefinition): Type<any> | null {\n if (!attr.renderer) return null;\n const reg = this.rendererRegistry.find(r => r.name === attr.renderer);\n return reg?.editComponent ?? null;\n }\n\n getEditRendererInputs(component: Type<any>, attr: EntityAttributeDefinition): Record<string, any> {\n return withDeclaredInputs(component, {\n value: this.formData()[attr.name],\n attribute: attr,\n options: attr.rendererOptions,\n valueChange: (newValue: any) => {\n const data = { ...this.formData() };\n data[attr.name] = newValue;\n this.formData.set(data);\n },\n });\n }\n\n /** Column renderer for a cell of an AsDetail sub-table (so embedded rows honor `col.renderer` too). */\n getAsDetailCellRendererComponent(col: EntityAttributeDefinition): Type<any> | null {\n if (!col.renderer) return null;\n return this.rendererRegistry.find(r => r.name === col.renderer)?.columnComponent ?? null;\n }\n\n /** Inputs of an AsDetail row renderer (`rowComponent`): the row and the object being edited. */\n getRowRendererInputs(renderer: SparkResolvedRowRenderer, row: Record<string, any>): Record<string, any> {\n return rowRendererInputs(renderer, row, this.objectId());\n }\n\n getAsDetailCellRendererInputs(component: Type<any>, row: Record<string, any>, col: EntityAttributeDefinition): Record<string, any> {\n return withDeclaredInputs(component, {\n value: row[col.name],\n attribute: col,\n options: col.rendererOptions,\n item: row,\n });\n }\n\n /** Edit-renderer for an inline AsDetail cell (so inline editing honors `col.renderer`, not just display). */\n getAsDetailCellEditRenderer(col: EntityAttributeDefinition): Type<any> | null {\n if (!col.renderer) return null;\n return this.rendererRegistry.find(r => r.name === col.renderer)?.editComponent ?? null;\n }\n\n getAsDetailCellEditRendererInputs(component: Type<any>, row: Record<string, any>, col: EntityAttributeDefinition): Record<string, any> {\n return withDeclaredInputs(component, {\n value: row[col.name],\n attribute: col,\n options: col.rendererOptions,\n item: row,\n valueChange: (newValue: any) => {\n row[col.name] = newValue;\n this.onFieldChange();\n },\n });\n }\n\n /**\n * Rules evaluated in the browser, against the *effective* metadata — so a rule a refresh hook\n * imposed is visible before the round-trip rather than only after the server rejects the save.\n */\n clientRuleFailures = computed<RuleFailure[]>(() => {\n const values = this.formData();\n return this.editableAttributes().flatMap(attr => evaluateRules(attr, values[attr.name]));\n });\n\n hasError(attrName: string): boolean {\n return this.clientRuleFailures().some(f => f.attributeName === attrName)\n || this.validationErrors().some(e => e.attributeName === attrName);\n }\n\n // Per-cell validation for inline AsDetail rows. Server-emitted errors are keyed by the\n // path \"{attr}[{rowIndex}].{col}\"; the client surfaces them like top-level field errors.\n private inlineErrorPath(attr: EntityAttributeDefinition, rowIndex: number, col: EntityAttributeDefinition): string {\n return `${attr.name}[${rowIndex}].${col.name}`;\n }\n\n hasInlineError(attr: EntityAttributeDefinition, rowIndex: number, col: EntityAttributeDefinition): boolean {\n const path = this.inlineErrorPath(attr, rowIndex, col);\n return this.validationErrors().some(e => e.attributeName === path);\n }\n\n inlineErrorMessage(attr: EntityAttributeDefinition, rowIndex: number, col: EntityAttributeDefinition): string | null {\n const path = this.inlineErrorPath(attr, rowIndex, col);\n const error = this.validationErrors().find(e => e.attributeName === path);\n return error ? resolveTranslation(error.errorMessage) : null;\n }\n\n /**\n * The single funnel every scalar / boolean / inline-cell edit passes through.\n *\n * `attr` is optional because some editors have nothing useful to say about which attribute moved.\n * A caller that knows should always say so — without it no refresh can fire.\n */\n onFieldChange(attr?: EntityAttributeDefinition): void {\n this.formData.set({ ...this.formData() });\n if (attr) this.noteChange(attr);\n }\n\n /**\n * A trigger inside an AsDetail row. Addressed by the same `{attr}[{index}].{col}` path the inline\n * validation errors already use, so the server can tell which row asked without a second\n * addressing scheme being invented for it.\n */\n onInlineCellChange(attr: EntityAttributeDefinition, rowIndex: number, col: EntityAttributeDefinition): void {\n this.onFieldChange();\n const dispatch = refreshDispatch(col);\n if (!dispatch || !this.objectTypeId()) return;\n\n const path = this.inlineErrorPath(attr, rowIndex, col);\n this.pendingNestedTrigger = { kind: 'row', attribute: attr.name, rowIndex };\n\n void this.refreshCoordinator.dispatch(path, dispatch);\n }\n\n /**\n * Which embedded target the in-flight refresh belongs to, if any.\n *\n * ⚠️ Tagged, not a nullable `rowIndex`. The two arms write to different places — a row inside\n * `formData`, versus the modal's working copy in `asDetailFormData` — and an index that happens\n * to be absent is not the same statement as \"this attribute holds one object\". With a nullable\n * index, a row refresh carrying a bad index would fall silently into the object arm.\n */\n private pendingNestedTrigger:\n | { kind: 'row'; attribute: string; rowIndex: number }\n | { kind: 'object'; attribute: string }\n | null = null;\n\n /**\n * A trigger inside a single embedded AsDetail object, raised by the modal's recursive form.\n *\n * The child form does not issue the request itself, for two reasons that both bite. Its\n * `entityType` is the embedded type, so a payload built there describes `GateSettings` while the\n * request must describe the Repository that owns it. And nested AsDetail types carry no rights of\n * their own — the server authorizes a nested refresh on the OWNING type — so a child-issued\n * request would be asking the wrong question of security.json.\n */\n onEmbeddedTrigger(attributeName: string, event: NestedTriggerRequest): void {\n if (!this.objectTypeId()) return;\n\n // Set on both arms, exactly as onInlineCellChange does: the blur that later flushes a pending\n // trigger does not know which target it belongs to, so the mark is what remembers.\n this.pendingNestedTrigger = { kind: 'object', attribute: attributeName };\n\n void this.refreshCoordinator.dispatch(event.path, event.dispatch);\n }\n\n /** Blur counterpart of {@link onEmbeddedTrigger}, for the child's free-text editors. */\n onEmbeddedTriggerBlur(path: string): void {\n if (!this.objectTypeId()) return;\n void this.refreshCoordinator.blur(path);\n }\n\n /**\n * Applies a refresh that ran against a detail row: the row's own values, and the column metadata\n * for the grid it lives in.\n *\n * The column metadata comes from `asDetailTypes` — a different signal from `entityType` — which is\n * why a nested response cannot go through the top-level overlay.\n *\n * ⚠️ The row array is mutated in place rather than replaced. Rows are tracked by index, so handing\n * the template a new array destroys and rebuilds every row's DOM and takes focus with it, mid-edit.\n */\n private applyNestedResponse(\n nested: { kind: 'row'; attribute: string; rowIndex: number } | { kind: 'object'; attribute: string },\n response: PersistentObject,\n ): void {\n this.pendingNestedTrigger = null;\n\n if (nested.kind === 'row') {\n const rows = this.formData()[nested.attribute];\n const row = Array.isArray(rows) ? rows[nested.rowIndex] : undefined;\n if (row) {\n for (const attribute of response.attributes ?? []) {\n row[attribute.name] = attribute.value ?? null;\n }\n // The array identity is unchanged; this only tells the signal graph the contents moved.\n this.formData.set({ ...this.formData() });\n }\n } else {\n // ⚠️ The modal's working copy, never `formData`. `openAsDetailEditor` copies the embedded dict\n // precisely so dismissing the modal discards the edit; writing a refreshed value through to\n // the parent would make a cancelled edit stick. `saveAsDetailObject` remains the only writer.\n //\n // Replacing the object identity is also correct here, where mutating in place is correct for\n // a row: rows are tracked by index and rebuilding them costs focus, while the recursive form\n // re-renders from this signal by design.\n const current = this.asDetailFormData();\n const next = { ...current };\n for (const attribute of response.attributes ?? []) {\n next[attribute.name] = attribute.value ?? null;\n }\n this.asDetailFormData.set(next);\n }\n\n const overlay = overlayFromResponse(response);\n this.asDetailTypes.update(prev => {\n const type = prev[nested.attribute];\n if (!type) return prev;\n\n return {\n ...prev,\n [nested.attribute]: {\n ...type,\n attributes: type.attributes.map(a => applyOverlay(a, overlay[a.name])),\n },\n };\n });\n }\n\n onInlineCellBlur(attr: EntityAttributeDefinition, rowIndex: number, col: EntityAttributeDefinition): void {\n if (effectiveTrigger(col) === 'none' || !this.objectTypeId()) return;\n void this.refreshCoordinator.blur(this.inlineErrorPath(attr, rowIndex, col));\n }\n\n /**\n * Blur handler for free-text editors — sends the refresh their keystrokes only marked pending, or\n * cuts short a `ValueChanged` debounce that is still waiting.\n */\n onFieldBlur(attr: EntityAttributeDefinition): void {\n if (this.triggerPathPrefix()) {\n if (effectiveTrigger(attr) === 'none') return;\n this.nestedTriggerBlurred.emit(this.nestedTriggerPath(attr));\n return;\n }\n\n if (!this.canRefresh(attr)) return;\n void this.refreshCoordinator.blur(attr.name);\n }\n\n private noteChange(attr: EntityAttributeDefinition): void {\n // Embedded: report upward and stop. The parent owns the request because only it knows the\n // object being edited and holds the right the server authorizes against.\n const dispatch = refreshDispatch(attr);\n if (this.triggerPathPrefix()) {\n if (!dispatch) return;\n this.nestedTriggerRequested.emit({ path: this.nestedTriggerPath(attr), dispatch });\n return;\n }\n\n if (!dispatch || !this.canRefresh(attr)) return;\n\n // Discrete editors send now. Free text either only marks itself pending (`Auto` / `Blur`: the\n // request goes on blur, or on save) or waits for typing to pause (`ValueChanged`).\n void this.refreshCoordinator.dispatch(attr.name, dispatch);\n }\n\n /** `Gate.ProjectMode` — the index-free counterpart of the inline grid's `Jobs[1].Kind`. */\n private nestedTriggerPath(attr: EntityAttributeDefinition): string {\n return `${this.triggerPathPrefix()}.${attr.name}`;\n }\n\n private canRefresh(attr: EntityAttributeDefinition): boolean {\n return effectiveTrigger(attr) !== 'none' && !!this.objectTypeId();\n }\n\n /**\n * Per-instance, never a service: the retry-action modal renders its own `spark-po-form`, and a\n * refresh may carry a retry operation — so a refresh can open a modal containing a form that\n * refreshes. A shared coordinator would let the nested form supersede this one's request.\n */\n protected readonly refreshCoordinator = new RefreshCoordinator({\n send: (triggeredBy) => this.sparkService.refresh(\n this.objectTypeId()!, this.buildRefreshPayload(), triggeredBy),\n currentValues: () => this.formData(),\n apply: (response, sent) => {\n // A nested response describes a detail ROW, not this form. Applying it to the top-level\n // overlay would hide every attribute the row does not happen to have.\n const nested = this.pendingNestedTrigger;\n if (nested) {\n this.applyNestedResponse(nested, response);\n return;\n }\n\n this.refreshOverlay.set(overlayFromResponse(response));\n this.formData.set(mergeRefreshValues(sent, this.formData(), response));\n this.applyRefreshedOptions(response);\n },\n setBusy: (busy) => this.isRefreshing.set(busy),\n });\n\n private buildRefreshPayload() {\n const et = this.entityType();\n const values = this.embeddedValues();\n return {\n id: this.objectId(),\n name: et?.name ?? '',\n // The EntityType's id, never `objectTypeId()` — that is the ROUTE segment, which is an alias\n // (\"car\") as often as a guid. The server types this field as a Guid, so an alias fails\n // deserialization and the request 500s before the handler is reached: no hook, no error the\n // client can act on. The route segment still carries the alias, which the server resolves.\n objectTypeId: et?.id ?? this.objectTypeId()!,\n attributes: (et?.attributes ?? []).map(a => ({\n name: a.name,\n value: values[a.name] ?? null,\n isValueChanged: true,\n })),\n } as any;\n }\n\n /**\n * The values to post, with the open modal's working copy substituted for the attribute it edits.\n *\n * ⚠️ Without this the server receives the embedded object as it was when the modal opened — `{}`\n * for a gate that has never been set — and the hook decides against values the user cannot see it\n * ignoring. `formData` deliberately does not hold the in-progress edit (cancelling the modal has\n * to discard it), so the refresh has to reach into `asDetailFormData` instead. Only the attribute\n * under edit is substituted; everything else is the form's own state.\n */\n private embeddedValues(): Record<string, any> {\n const nested = this.pendingNestedTrigger;\n if (nested?.kind !== 'object') return this.formData();\n\n return { ...this.formData(), [nested.attribute]: this.asDetailFormData() };\n }\n\n /**\n * Folds replaced option lists into the signals the editors already read, so a refreshed dropdown\n * renders through the same path as a loaded one.\n *\n * `undefined` means the hook did not touch this attribute's options and the loaded set stands; an\n * empty array means it deliberately left none. Collapsing the two would blank every dropdown the\n * hook never mentioned.\n */\n private applyRefreshedOptions(response: PersistentObject): void {\n const replaced = (response.attributes ?? [])\n .map(a => [a.name, (a as { options?: RefreshedOption[] | null }).options] as const)\n .filter(([, options]) => options !== undefined && options !== null);\n\n if (replaced.length === 0) return;\n\n const byName = new Map(this.optionSourceAttributes().map(a => [a.name, a]));\n\n this.lookupReferenceOptions.update(prev => {\n const next = { ...prev };\n for (const [name, options] of replaced) {\n const attr = byName.get(name);\n if (!attr?.lookupReferenceType) continue;\n next[attr.lookupReferenceType] = {\n ...(next[attr.lookupReferenceType] ?? { name: attr.lookupReferenceType, isTransient: true, displayType: ELookupDisplayType.Dropdown }),\n values: (options ?? []).map(o => ({ key: o.key, values: o.label ?? { en: o.key }, isActive: true })),\n } as LookupReference;\n }\n return next;\n });\n\n this.referenceOptions.update(prev => {\n const next = { ...prev };\n for (const [name, options] of replaced) {\n const attr = byName.get(name);\n if (attr?.dataType !== 'Reference') continue;\n // A refresh hook replaced this attribute's options, so synthesise the same row shape the\n // query path produces: an id and a display string, which is all an option list ever needed.\n next[name] = (options ?? []).map(o => ({\n id: o.key,\n breadcrumb: o.label ? resolveTranslation(o.label) : o.key,\n values: [],\n })) as QueryResultItem[];\n }\n return next;\n });\n\n // A multi-reference renders through <bs-tree-select>, which reads the provider and the by-id\n // node map rather than `referenceOptions` — so a refreshed list has to be rebuilt there too, or\n // the hook's options never reach the picker. The node map keeps the labels it already had: a\n // selected id the new list no longer offers must still render as its chip, not as a raw id.\n const multi = replaced.filter(([name]) => byName.get(name)?.dataType === 'Reference' && byName.get(name)?.isArray);\n if (multi.length === 0) return;\n\n const providers = { ...this.referenceProviders() };\n const nodesByAttr = { ...this.referenceNodes() };\n for (const [name, options] of multi) {\n const nodes: TreeNode[] = (options ?? []).map(o => ({ id: o.key, label: o.label ? resolveTranslation(o.label) : o.key }));\n providers[name] = new InMemoryTreeSelectProvider(nodes);\n nodesByAttr[name] = { ...(nodesByAttr[name] ?? {}), ...this.toRecord(nodes.map(n => [n.id, n] as [string, TreeNode])) };\n }\n this.referenceProviders.set(providers);\n this.referenceNodes.set(nodesByAttr);\n }\n\n /** Sends anything still pending, so a typed-but-never-blurred trigger is reflected before save. */\n async flushPendingRefresh(): Promise<void> {\n await this.refreshCoordinator.flush();\n }\n\n async onSave(): Promise<void> {\n // A value typed and never blurred — the user tabbing straight to Save — must still reshape the\n // object before it goes, or the server enforces rules the user was never shown.\n await this.flushPendingRefresh();\n\n // Evaluated AFTER the flush: the refresh may be what imposes the rule being checked.\n if (this.clientRuleFailures().length > 0) return;\n\n this.save.emit();\n }\n\n onCancel(): void {\n this.cancel.emit();\n }\n\n // AsDetail object modal methods\n openAsDetailEditor(attr: EntityAttributeDefinition): void {\n this.editingAsDetailAttr.set(attr);\n this.editingArrayIndex.set(null);\n this.asDetailFormData.set({ ...(this.formData()[attr.name] || {}) });\n this.showAsDetailModal.set(true);\n }\n\n saveAsDetailObject(): void {\n const attr = this.editingAsDetailAttr();\n if (attr) {\n const data = { ...this.formData() };\n if (attr.isArray) {\n const arr = [...(data[attr.name] || [])];\n const idx = this.editingArrayIndex();\n if (idx !== null) {\n arr[idx] = { ...this.asDetailFormData() };\n } else {\n arr.push({ ...this.asDetailFormData() });\n }\n data[attr.name] = arr;\n } else {\n data[attr.name] = { ...this.asDetailFormData() };\n }\n this.formData.set(data);\n }\n this.closeAsDetailModal();\n }\n\n closeAsDetailModal(): void {\n this.showAsDetailModal.set(false);\n this.editingAsDetailAttr.set(null);\n this.editingArrayIndex.set(null);\n this.asDetailFormData.set({});\n }\n\n // ---- Server-side row lifecycle ------------------------------------------------------------\n //\n // A row type may opt in (EntityType.serverSideRowLifecycle) to having the server construct its\n // new rows and be consulted before one is removed. Everything below is a no-op for a type that\n // has not — which is the point: not opting in costs no request, and the four mutators behave\n // exactly as they did before.\n\n /**\n * Whether `attr`'s row type asks the server before a row is added or removed.\n *\n * Read off the ROW type, never the parent. `asDetailTypes` already resolves it from the Query-gated\n * catalogue first and the parent's `detailTypes` second, and the flag survives the server-side\n * pruning of the embedded copy, so both sources give the same answer.\n */\n private roundTripsRowLifecycle(attr: EntityAttributeDefinition): boolean {\n return this.asDetailTypes()[attr.name]?.serverSideRowLifecycle === true;\n }\n\n /** The type id to address the row type by on the wire. */\n private rowTypeId(attr: EntityAttributeDefinition): string | undefined {\n return this.asDetailTypes()[attr.name]?.id;\n }\n\n /**\n * How to name the PARENT's type on the wire.\n *\n * ⚠️ The fallback must be the type **id**, never `clrType`. The server resolves this through\n * `ModelLoader.ResolveEntityType`, which accepts a GUID or a declared alias and nothing else — so\n * a CLR name resolves to null and the request is refused, indistinguishably from an unknown type.\n * The user would see \"the server refused this change\" on a perfectly ordinary Add.\n *\n * It is a live path, not a theoretical one: `spark-po-create` binds no `parentType` (there is no\n * parent route segment on a create page), so every host that creates an object with an opted-in\n * detail grid lands here.\n */\n private parentTypeForWire(): string | undefined {\n return this.parentType() ?? this.entityType()?.id;\n }\n\n /**\n * Builds the dict a new row starts from: `{}` as before, or the object the server constructed.\n *\n * Returns null when the server refused — the caller must then add nothing, which is the whole\n * difference between a veto and a message.\n */\n private async buildNewRow(attr: EntityAttributeDefinition): Promise<Record<string, any> | null> {\n const typeId = this.rowTypeId(attr);\n if (!this.roundTripsRowLifecycle(attr) || !typeId) return {};\n\n this.clearRowLifecycleError(attr.name);\n try {\n const po = await this.sparkService.newObject(typeId, {\n asDetailAttribute: attr.name,\n parentType: this.parentTypeForWire(),\n // Absent while the parent is unsaved, deliberately: the server hands the hook a null parent\n // rather than trusting the client's copy of one.\n parentId: this.objectId(),\n });\n // nestedPoToDict carries the server-minted row key across as __sparkRowKey, so the row is\n // matchable on save from the moment it appears — the reason the round trip is worth a request.\n return nestedPoToDict(po);\n } catch (e) {\n this.captureRowLifecycleError(attr.name, e);\n return null;\n }\n }\n\n /**\n * Asks whether a stored row may be removed. True when it may — including every case where there\n * is nothing to ask about: a type that has not opted in, a parent that has never been saved, or a\n * row with no key yet because it was added in this same editing session and never persisted.\n */\n private async mayRemoveRow(attr: EntityAttributeDefinition, row: Record<string, any> | undefined): Promise<boolean> {\n const typeId = this.rowTypeId(attr);\n const parentId = this.objectId();\n const parentType = this.parentTypeForWire();\n const rowKey = row?.[AS_DETAIL_ROW_KEY];\n\n if (!this.roundTripsRowLifecycle(attr) || !typeId || !parentId || !parentType || !rowKey) {\n return true;\n }\n\n this.clearRowLifecycleError(attr.name);\n try {\n await this.sparkService.deleteRow(typeId, {\n asDetailAttribute: attr.name,\n parentType,\n parentId,\n rowKey,\n });\n return true;\n } catch (e) {\n this.captureRowLifecycleError(attr.name, e);\n return false;\n }\n }\n\n /**\n * Turns a rejected lifecycle call into something the user can read.\n *\n * The envelope puts validation errors under `result`, the same place the save path reads them\n * from — reading `error.error.errors` matched nothing and made every veto silent.\n */\n private clearRowLifecycleError(attrName: string): void {\n this.rowLifecycleErrors.update(prev => {\n const next = { ...prev };\n delete next[attrName];\n return next;\n });\n }\n\n private captureRowLifecycleError(attrName: string, e: unknown): void {\n const error = e as HttpErrorResponse;\n const errors: ValidationError[] | undefined = error?.error?.result?.errors ?? error?.error?.errors;\n const resolved: ValidationError[] = error?.status === 400 && errors?.length\n ? errors\n : [{\n attributeName: '',\n errorMessage: { en: error?.error?.result?.error || error?.error?.error || error?.message || 'The server refused this change.' },\n ruleType: 'error',\n }];\n this.rowLifecycleErrors.update(prev => ({ ...prev, [attrName]: resolved }));\n }\n\n // Inline AsDetail methods\n async addInlineRow(attr: EntityAttributeDefinition): Promise<void> {\n const row = await this.buildNewRow(attr);\n if (row === null) return;\n\n const data = { ...this.formData() };\n const arr = [...(data[attr.name] || [])];\n arr.push(row);\n data[attr.name] = arr;\n this.formData.set(data);\n }\n\n // Array AsDetail methods\n async addArrayItem(attr: EntityAttributeDefinition): Promise<void> {\n const row = await this.buildNewRow(attr);\n if (row === null) return;\n\n this.editingAsDetailAttr.set(attr);\n this.editingArrayIndex.set(null);\n this.asDetailFormData.set(row);\n this.showAsDetailModal.set(true);\n }\n\n editArrayItem(attr: EntityAttributeDefinition, index: number): void {\n this.editingAsDetailAttr.set(attr);\n this.editingArrayIndex.set(index);\n const arr = this.formData()[attr.name] || [];\n this.asDetailFormData.set({ ...(arr[index] || {}) });\n this.showAsDetailModal.set(true);\n }\n\n async removeArrayItem(attr: EntityAttributeDefinition, index: number): Promise<void> {\n const current = this.formData()[attr.name] || [];\n if (!(await this.mayRemoveRow(attr, current[index]))) return;\n\n const data = { ...this.formData() };\n const arr = [...current];\n arr.splice(index, 1);\n data[attr.name] = arr;\n this.formData.set(data);\n }\n\n // Drag-reorder for [Sortable] AsDetail arrays. Order = array position, so moving the\n // row within formData()[attr.name] and re-emitting the signal IS the persisted order\n // (po-edit flags AsDetail attributes isValueChanged on save).\n onAsDetailReorder(attr: EntityAttributeDefinition, event: CdkDragDrop<Record<string, any>[]>): void {\n if (event.previousIndex === event.currentIndex) return;\n const data = { ...this.formData() };\n const arr = [...(data[attr.name] ?? [])];\n moveItemInArray(arr, event.previousIndex, event.currentIndex);\n data[attr.name] = arr;\n this.formData.set(data);\n }\n}\n","<bs-form>\n @if (entityType()) {\n <bs-grid>\n <bs-tab-control>\n @for (tab of resolvedTabs(); track tab.id) {\n <bs-tab-page>\n <ng-template bsTabPageHeader>{{ tab.label | resolveTranslation:tab.name }}</ng-template>\n <ng-container *ngTemplateOutlet=\"tabContent; context: { $implicit: tab }\"></ng-container>\n </bs-tab-page>\n }\n </bs-tab-control>\n\n <ng-template #tabContent let-tab>\n @if (tab.id === '__default__') {\n @if (ungroupedAttributes().length > 0) {\n <bs-card class=\"d-block m-3\">\n <div class=\"p-3\">\n @for (attr of ungroupedAttributes(); track attr.id) {\n <ng-container *ngTemplateOutlet=\"attrField; context: { $implicit: attr }\"></ng-container>\n }\n </div>\n </bs-card>\n }\n }\n @for (group of groupsForTab(tab); track group.id) {\n @if (attrsForGroup(group); as groupAttrs) {\n @if (groupAttrs.length > 0) {\n <bs-card class=\"d-block m-3\">\n @if (group.label) {\n <bs-card-header>{{ group.label | resolveTranslation:group.name }}</bs-card-header>\n }\n <div class=\"p-3\">\n @for (attr of groupAttrs; track attr.id) {\n <ng-container *ngTemplateOutlet=\"attrField; context: { $implicit: attr }\"></ng-container>\n }\n </div>\n </bs-card>\n }\n }\n }\n </ng-template>\n\n <ng-template #attrField let-attr>\n <div bsRow class=\"mb-3\">\n <label [md]=\"4\" bsColFormLabel [for]=\"attr.name\">\n {{ attr.label | resolveTranslation:attr.name }}\n @if (attr.isRequired) {\n <span class=\"text-danger\">*</span>\n }\n <spark-attribute-description [description]=\"attr.description\" />\n </label>\n <div [md]=\"8\">\n <!-- A lookup wins over the raw data type. `[LookupReference]` does not change dataType —\n only `[Reference]` does — so a lookup-backed bool still reports 'boolean' here. Tested\n first, this branch swallowed it into a two-state checkbox and the select below was\n unreachable, which made a declared lookup silently do nothing. -->\n @if (attr.dataType === 'boolean' && !attr.lookupReferenceType) {\n <bs-checkbox\n [ngModel]=\"formData()[attr.name]\"\n (ngModelChange)=\"formData()[attr.name] = $event; onFieldChange(attr)\">\n </bs-checkbox>\n } @else if (attr.lookupReferenceType) {\n @if ((attr | lookupDisplayType:lookupReferenceOptions()) === ELookupDisplayType.Modal) {\n <spark-lookup-picker\n [value]=\"formData()[attr.name]\"\n [options]=\"getLookupOptions(attr)\"\n [inputId]=\"attr.name\"\n [title]=\"attr.label | resolveTranslation:attr.name\"\n [isInvalid]=\"hasError(attr.name)\"\n (valueChange)=\"onLookupValueChange(attr, $event)\">\n </spark-lookup-picker>\n } @else {\n <bs-select\n class=\"w-100\"\n [ngModel]=\"formData()[attr.name]\"\n (ngModelChange)=\"formData()[attr.name] = $event; onFieldChange(attr)\"\n [id]=\"attr.name\"\n [class.is-invalid]=\"hasError(attr.name)\">\n <option [ngValue]=\"null\">{{ 'common.selectPlaceholder' | t }}</option>\n @for (option of (attr | lookupOptions:lookupReferenceOptions()); track option.key) {\n <option [ngValue]=\"option.key\">\n {{ option.values | resolveTranslation:option.key }}\n </option>\n }\n </bs-select>\n }\n } @else if (attr.dataType === 'Reference' && attr.isArray) {\n <bs-tree-select\n mode=\"multiple\"\n variant=\"textbox\"\n [showClear]=\"true\"\n [placeholder]=\"'common.search' | t\"\n [provider]=\"getReferenceProvider(attr)\"\n [ngModel]=\"referenceTreeValues()[attr.name] || []\"\n (ngModelChange)=\"onReferenceTreeChange(attr, $event)\"\n [ngModelOptions]=\"{ standalone: true }\">\n </bs-tree-select>\n } @else if (attr.dataType === 'Reference') {\n <spark-reference-picker\n [value]=\"formData()[attr.name]\"\n [options]=\"referenceOptions()[attr.name] || []\"\n [referenceType]=\"attr.referenceType\"\n [inputId]=\"attr.name\"\n [title]=\"attr.label | resolveTranslation:attr.name\"\n [isInvalid]=\"hasError(attr.name)\"\n (valueChange)=\"onReferenceValueChange(attr, $event)\">\n </spark-reference-picker>\n } @else if (attr.dataType === 'AsDetail' && attr.isArray && attr.editMode === 'inline') {\n <!-- One source of truth for an inline cell, reused by the row and by the drag\n preview. The preview is a live Angular view (not a cloneNode), so its\n <bs-select> binds to row[col.name] and shows the value while dragging. -->\n <ng-template #inlineDetailCell let-row=\"row\" let-col=\"col\" let-attr=\"attr\" let-rowIndex=\"rowIndex\" let-rowRenderer=\"rowRenderer\">\n <td>\n <!-- A row the caller may not edit renders through the same read-only span as a\n read-only column: still fully readable, just not editable. Hiding it would\n hide the data, and the save path would silently restore any edit anyway. -->\n @if (col.isReadOnly || !(attr | canEditDetailRow:asDetailPermissions())) {\n <span class=\"text-muted\">{{ row | asDetailCellValue:attr:col:asDetailReferenceOptions() }}</span>\n } @else if (getAsDetailCellEditRenderer(col); as editRenderer) {\n <ng-container *ngComponentOutlet=\"editRenderer; inputs: getAsDetailCellEditRendererInputs(editRenderer, row, col)\"></ng-container>\n } @else if (col.dataType === 'AsDetail') {\n <!-- Nested AsDetail can't flatten into a row: escalate to the row modal,\n where the recursive spark-po-form renders the nested structure. -->\n <div class=\"d-flex align-items-center gap-1\">\n <span class=\"text-truncate flex-grow-1\">{{ row | asDetailCellValue:attr:col:asDetailReferenceOptions() }}</span>\n <button type=\"button\" class=\"btn btn-sm btn-outline-secondary\" (click)=\"editArrayItem(attr, rowIndex)\">\n <spark-icon name=\"pencil\" />\n </button>\n </div>\n } @else if (col.dataType === 'boolean' && !col.lookupReferenceType) {\n <!-- Same precedence as the top-level chain above: a lookup-backed column must\n reach its select rather than being swallowed by the checkbox. -->\n <bs-checkbox\n [(ngModel)]=\"row[col.name]\"\n (ngModelChange)=\"onInlineCellChange(attr, rowIndex, col)\">\n </bs-checkbox>\n } @else if (col.dataType === 'Reference' && col.query) {\n @if (col.referenceDisplayType === EReferenceDisplayType.Modal) {\n <spark-reference-picker\n [value]=\"row[col.name]\"\n [options]=\"(attr | inlineRefOptions:col:asDetailReferenceOptions())\"\n [referenceType]=\"col.referenceType\"\n [title]=\"col.label | resolveTranslation:col.name\"\n [isInvalid]=\"hasInlineError(attr, rowIndex, col)\"\n (valueChange)=\"row[col.name] = $event; onInlineCellChange(attr, rowIndex, col)\">\n </spark-reference-picker>\n } @else {\n <bs-select\n class=\"w-100\"\n [(ngModel)]=\"row[col.name]\"\n (ngModelChange)=\"onInlineCellChange(attr, rowIndex, col)\"\n [class.is-invalid]=\"hasInlineError(attr, rowIndex, col)\">\n <option [ngValue]=\"null\">{{ 'common.selectPlaceholder' | t }}</option>\n @for (option of (attr | inlineRefOptions:col:asDetailReferenceOptions()); track option.id) {\n <option [ngValue]=\"option.id\">\n {{ option.breadcrumb || option.id }}\n </option>\n }\n </bs-select>\n }\n } @else if (col.lookupReferenceType) {\n @if ((col | lookupDisplayType:lookupReferenceOptions()) === ELookupDisplayType.Modal) {\n <spark-lookup-picker\n [value]=\"row[col.name]\"\n [options]=\"getLookupOptions(col)\"\n [title]=\"col.label | resolveTranslation:col.name\"\n [isInvalid]=\"hasInlineError(attr, rowIndex, col)\"\n (valueChange)=\"row[col.name] = $event; onInlineCellChange(attr, rowIndex, col)\">\n </spark-lookup-picker>\n } @else {\n <bs-select\n class=\"w-100\"\n [(ngModel)]=\"row[col.name]\"\n (ngModelChange)=\"onInlineCellChange(attr, rowIndex, col)\"\n [class.is-invalid]=\"hasInlineError(attr, rowIndex, col)\">\n <option [ngValue]=\"null\">{{ 'common.selectPlaceholder' | t }}</option>\n @for (option of getLookupOptions(col); track option.key) {\n <option [ngValue]=\"option.key\">\n {{ option.values | resolveTranslation:option.key }}\n </option>\n }\n </bs-select>\n }\n } @else if (col.dataType === 'MultiLineString') {\n <textarea\n [(ngModel)]=\"row[col.name]\"\n [required]=\"col.isRequired\"\n rows=\"3\"\n [class.is-invalid]=\"hasInlineError(attr, rowIndex, col)\"\n (ngModelChange)=\"onInlineCellChange(attr, rowIndex, col)\"\n (blur)=\"onInlineCellBlur(attr, rowIndex, col)\"></textarea>\n } @else {\n <input\n [type]=\"col.dataType | inputType\"\n [(ngModel)]=\"row[col.name]\"\n [required]=\"col.isRequired\"\n [step]=\"col.dataType === 'decimal' ? '0.01' : '1'\"\n [class.is-invalid]=\"hasInlineError(attr, rowIndex, col)\"\n (ngModelChange)=\"onInlineCellChange(attr, rowIndex, col)\"\n (blur)=\"onInlineCellBlur(attr, rowIndex, col)\">\n }\n @if (inlineErrorMessage(attr, rowIndex, col); as errorMsg) {\n <div class=\"invalid-feedback d-block\">{{ errorMsg }}</div>\n }\n <!-- Passed for the first cell only: a row renderer (rowComponent) draws its\n attributes once per row, under that cell, instead of as columns. -->\n @if (rowRenderer) {\n <div class=\"spark-row-renderer\">\n <ng-container *ngComponentOutlet=\"rowRenderer.component; inputs: getRowRendererInputs(rowRenderer, row)\"></ng-container>\n </div>\n }\n </td>\n </ng-template>\n <bs-table [isResponsive]=\"true\" class=\"mb-1\">\n <thead>\n <tr>\n @if (attr.isSortable) {\n <th style=\"width: 32px\"></th>\n }\n @for (col of (attr | asDetailColumns:asDetailTypes():rendererRegistry); track col.name) {\n <th>{{ col.label | resolveTranslation:col.name }}<spark-attribute-description [description]=\"col.description\" /></th>\n }\n <th style=\"width: 50px\"></th>\n </tr>\n </thead>\n @let inlineRowRenderer = (attr | asDetailRowRenderer:asDetailTypes():rendererRegistry);\n <tbody class=\"align-middle\" cdkDropList [cdkDropListDisabled]=\"!attr.isSortable\" (cdkDropListDropped)=\"onAsDetailReorder(attr, $event)\">\n @for (row of formData()[attr.name] || []; track $index; let rowIndex = $index) {\n <tr cdkDrag [cdkDragDisabled]=\"!attr.isSortable\">\n @if (attr.isSortable) {\n <td class=\"text-center text-muted\" style=\"cursor: move; touch-action: none\" cdkDragHandle>\n <spark-icon name=\"grip-vertical\" />\n </td>\n }\n @for (col of (attr | asDetailColumns:asDetailTypes():rendererRegistry); track col.name; let firstCol = $first) {\n <ng-container *ngTemplateOutlet=\"inlineDetailCell; context: { row: row, col: col, attr: attr, rowIndex: rowIndex, rowRenderer: firstCol ? inlineRowRenderer : null }\"></ng-container>\n }\n <td class=\"text-nowrap\">\n @if (attr | canDeleteDetailRow:asDetailPermissions()) {\n <button type=\"button\" [color]=\"colors.secondary\" (click)=\"removeArrayItem(attr, rowIndex)\">\n <spark-icon name=\"trash\" />\n </button>\n }\n </td>\n\n <!-- Floating preview = a LIVE view of the row's cells (not CDK's cloneNode),\n so <bs-select> shows its value instead of blanking. The in-grid\n placeholder still uses CDK's default clone (exact row height) and is\n hidden via .cdk-drag-placeholder { visibility:hidden } in the styles,\n so the drop gap is blank without collapsing/shifting the other rows. -->\n <ng-template cdkDragPreview>\n <table class=\"table mb-0 bg-body shadow-sm spark-drag-preview\">\n <tbody class=\"align-middle\">\n <tr>\n @if (attr.isSortable) {\n <td class=\"text-center text-muted\" style=\"width: 32px\"><spark-icon name=\"grip-vertical\" /></td>\n }\n @for (col of (attr | asDetailColumns:asDetailTypes():rendererRegistry); track col.name) {\n <ng-container *ngTemplateOutlet=\"inlineDetailCell; context: { row: row, col: col, attr: attr, rowIndex: rowIndex }\"></ng-container>\n }\n </tr>\n </tbody>\n </table>\n </ng-template>\n </tr>\n } @empty {\n <tr>\n <td [attr.colspan]=\"(attr | asDetailColumns:asDetailTypes():rendererRegistry).length + (attr.isSortable ? 2 : 1)\" class=\"text-center text-muted\">\n {{ 'common.noItemsFound' | t }}\n </td>\n </tr>\n }\n </tbody>\n </bs-table>\n @for (err of rowLifecycleErrorsFor(attr.name); track $index) {\n <div class=\"text-danger small mt-1\">{{ err.errorMessage | resolveTranslation }}</div>\n }\n @if (attr | canCreateDetailRow:asDetailPermissions()) {\n <button type=\"button\" [color]=\"colors.primary\" class=\"w-100 rounded-0\" (click)=\"addInlineRow(attr)\">\n <spark-icon name=\"plus\" /> {{ 'common.add' | t }}\n </button>\n }\n } @else if (attr.dataType === 'AsDetail' && attr.isArray) {\n <bs-table [isResponsive]=\"true\" class=\"mb-1\">\n <thead>\n <tr>\n @if (attr.isSortable) {\n <th style=\"width: 32px\"></th>\n }\n @for (col of (attr | asDetailColumns:asDetailTypes():rendererRegistry); track col.name) {\n <th>{{ col.label | resolveTranslation:col.name }}<spark-attribute-description [description]=\"col.description\" /></th>\n }\n <th style=\"width: 80px\"></th>\n </tr>\n </thead>\n <tbody class=\"align-middle\" cdkDropList [cdkDropListDisabled]=\"!attr.isSortable\" (cdkDropListDropped)=\"onAsDetailReorder(attr, $event)\">\n @let modalRowRenderer = (attr | asDetailRowRenderer:asDetailTypes():rendererRegistry);\n @for (row of formData()[attr.name] || []; track $index) {\n <tr cdkDrag [cdkDragDisabled]=\"!attr.isSortable\">\n @if (attr.isSortable) {\n <td class=\"text-center text-muted\" style=\"cursor: move; touch-action: none\" cdkDragHandle>\n <spark-icon name=\"grip-vertical\" />\n </td>\n }\n @for (col of (attr | asDetailColumns:asDetailTypes():rendererRegistry); track col.name; let firstCol = $first) {\n <td>\n @if (getAsDetailCellRendererComponent(col); as cellRenderer) {\n <ng-container *ngComponentOutlet=\"cellRenderer; inputs: getAsDetailCellRendererInputs(cellRenderer, row, col)\"></ng-container>\n } @else {\n {{ row | asDetailCellValue:attr:col:asDetailReferenceOptions() }}\n }\n @if (firstCol && modalRowRenderer) {\n <div class=\"spark-row-renderer\">\n <ng-container *ngComponentOutlet=\"modalRowRenderer.component; inputs: getRowRendererInputs(modalRowRenderer, row)\"></ng-container>\n </div>\n }\n </td>\n }\n <td class=\"text-nowrap\">\n @if (attr | canEditDetailRow:asDetailPermissions()) {\n <button type=\"button\" class=\"btn btn-sm btn-outline-secondary me-1\" (click)=\"editArrayItem(attr, $index)\">\n <spark-icon name=\"pencil\" />\n </button>\n }\n @if (attr | canDeleteDetailRow:asDetailPermissions()) {\n <button type=\"button\" class=\"btn btn-sm btn-outline-danger\" (click)=\"removeArrayItem(attr, $index)\">\n <spark-icon name=\"trash\" />\n </button>\n }\n </td>\n </tr>\n } @empty {\n <tr>\n <td [attr.colspan]=\"(attr | asDetailColumns:asDetailTypes():rendererRegistry).length + (attr.isSortable ? 2 : 1)\" class=\"text-center text-muted\">\n {{ 'common.noItemsFound' | t }}\n </td>\n </tr>\n }\n </tbody>\n </bs-table>\n @for (err of rowLifecycleErrorsFor(attr.name); track $index) {\n <div class=\"text-danger small mt-1\">{{ err.errorMessage | resolveTranslation }}</div>\n }\n @if (attr | canCreateDetailRow:asDetailPermissions()) {\n <button type=\"button\" [color]=\"colors.primary\" class=\"w-100 rounded-0\" (click)=\"addArrayItem(attr)\">\n <spark-icon name=\"plus\" /> {{ 'common.add' | t }}\n </button>\n }\n } @else if (attr.dataType === 'AsDetail') {\n <bs-input-group>\n <input\n type=\"text\"\n [id]=\"attr.name\"\n [value]=\"attr | asDetailDisplayValue:formData():asDetailTypes()\"\n readonly\n [class.is-invalid]=\"hasError(attr.name)\">\n <button\n type=\"button\"\n bsInputGroupBtn\n [color]=\"colors.secondary\"\n (click)=\"openAsDetailEditor(attr)\">\n <spark-icon name=\"pencil\" />\n </button>\n </bs-input-group>\n } @else if (getEditRendererComponent(attr); as editComp) {\n <ng-container *ngComponentOutlet=\"editComp; inputs: getEditRendererInputs(editComp, attr)\"></ng-container>\n } @else if (attr.dataType === 'MultiLineString') {\n <textarea\n [id]=\"attr.name\"\n [ngModel]=\"formData()[attr.name]\"\n (ngModelChange)=\"formData()[attr.name] = $event; onFieldChange(attr)\"\n (blur)=\"onFieldBlur(attr)\"\n [name]=\"attr.name\"\n [required]=\"attr.isRequired\"\n rows=\"6\"\n [class.is-invalid]=\"hasError(attr.name)\"></textarea>\n } @else {\n <input\n [type]=\"attr.dataType | inputType\"\n [id]=\"attr.name\"\n [ngModel]=\"formData()[attr.name]\"\n (ngModelChange)=\"formData()[attr.name] = $event; onFieldChange(attr)\"\n (blur)=\"onFieldBlur(attr)\"\n [name]=\"attr.name\"\n [required]=\"attr.isRequired\"\n [step]=\"attr.dataType === 'decimal' ? '0.01' : '1'\"\n [class.is-invalid]=\"hasError(attr.name)\">\n }\n @if (attr.name | errorForAttribute:validationErrors(); as errorMsg) {\n <div class=\"invalid-feedback d-block\">\n {{ errorMsg }}\n </div>\n }\n </div>\n </div>\n </ng-template>\n\n @if (showButtons()) {\n <div bsRow class=\"mt-4\">\n <div [md]=\"4\"></div>\n <div [md]=\"8\" class=\"d-flex justify-content-end gap-2\">\n <button type=\"button\" [color]=\"colors.secondary\" (click)=\"onCancel()\" [disabled]=\"isSaving()\">{{ 'common.cancel' | t }}</button>\n <button type=\"submit\" [color]=\"colors.primary\" [disabled]=\"isSaving()\" (click)=\"onSave()\">\n @if (isSaving()) {\n <bs-spinner class=\"me-1\" />\n }\n {{ 'common.save' | t }}\n </button>\n </div>\n </div>\n }\n </bs-grid>\n}\n\n<!-- Modal for editing AsDetail objects -->\n<bs-modal [isOpen]=\"showAsDetailModal()\" (isOpenChange)=\"!$event && closeAsDetailModal()\">\n <div *bsModal>\n <div bsModalHeader>\n <h5 class=\"modal-title\">{{ 'common.edit' | t }} {{ editingAsDetailAttr()?.label | resolveTranslation:editingAsDetailAttr()?.name }}</h5>\n </div>\n\n @if (editingAsDetailAttr(); as attr) {\n <div bsModalBody>\n <spark-po-form\n [entityType]=\"attr | asDetailType:asDetailTypes()\"\n [(formData)]=\"asDetailFormData\"\n [parentId]=\"parentId()\"\n [parentType]=\"parentType()\"\n [referenceParentId]=\"referenceParentId()\"\n [referenceParentType]=\"referenceParentType()\"\n [triggerPathPrefix]=\"attr.isArray ? null : attr.name\"\n (nestedTriggerRequested)=\"onEmbeddedTrigger(attr.name, $event)\"\n (nestedTriggerBlurred)=\"onEmbeddedTriggerBlur($event)\">\n </spark-po-form>\n </div>\n }\n\n <div bsModalFooter>\n <button type=\"button\" [color]=\"colors.secondary\" (click)=\"closeAsDetailModal()\">{{ 'common.cancel' | t }}</button>\n <button type=\"button\" [color]=\"colors.primary\" (click)=\"saveAsDetailObject()\">{{ 'common.save' | t }}</button>\n </div>\n </div>\n</bs-modal>\n</bs-form>\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"mintplayer-ng-spark-po-form.mjs","names":[],"sources":["../../po-form/src/spark-reference-picker.component.ts","../../po-form/src/spark-reference-picker.component.html","../../po-form/src/spark-lookup-picker.component.ts","../../po-form/src/spark-lookup-picker.component.html","../../po-form/src/refresh-coordinator.ts","../../po-form/src/spark-po-form.component.ts","../../po-form/src/spark-po-form.component.html"],"sourcesContent":["import { ChangeDetectionStrategy, Component, computed, inject, input, output, signal } from '@angular/core';\nimport { FormsModule } from '@angular/forms';\nimport { Color } from '@mintplayer/ng-bootstrap';\nimport { BsFormComponent, BsFormControlDirective } from '@mintplayer/ng-bootstrap/form';\nimport { BsGridComponent, BsGridRowDirective, BsGridColumnDirective } from '@mintplayer/ng-bootstrap/grid';\nimport { BsInputGroupComponent } from '@mintplayer/ng-bootstrap/input-group';\nimport { BsButtonTypeDirective } from '@mintplayer/ng-bootstrap/button-type';\nimport { BsModalHostComponent, BsModalDirective, BsModalHeaderDirective, BsModalBodyDirective, BsModalFooterDirective } from '@mintplayer/ng-bootstrap/modal';\nimport { BsDatatableComponent, BsDatatableColumnDirective, BsRowTemplateDirective, DatatableSettings } from '@mintplayer/ng-bootstrap/datatable';\nimport { BsSpinnerComponent } from '@mintplayer/ng-bootstrap/spinner';\nimport { PaginationResponse } from '@mintplayer/pagination';\nimport { SparkService, SparkLanguageService } from '@mintplayer/ng-spark/services';\nimport { EntityType, QueryResultItem, ShowedOn, hasShowedOnFlag } from '@mintplayer/ng-spark/models';\nimport { TranslateKeyPipe, ResolveTranslationPipe, ReferenceAttrValuePipe } from '@mintplayer/ng-spark/pipes';\nimport { SparkAttributeDescriptionComponent } from '@mintplayer/ng-spark/attribute-description';\nimport { SparkIconComponent } from '@mintplayer/ng-spark/icon';\n\n/**\n * Reusable Reference value picker: a readonly textbox showing the current selection's\n * breadcrumb/name plus a \"…\" button that opens a searchable modal grid over the candidate\n * `options`. Per-instance state (no single top-level slot), so it works for top-level\n * reference attributes and for individual inline AsDetail reference cells alike.\n *\n * The parent pre-loads `options` (the query result) and passes `referenceType` (the target\n * CLR type); the component lazily loads that EntityType's metadata on first open to render\n * the grid's column headers. Emits the picked id via `valueChange`.\n */\n@Component({\n selector: 'spark-reference-picker',\n imports: [FormsModule, BsFormComponent, BsFormControlDirective, BsGridComponent, BsGridRowDirective, BsGridColumnDirective, BsInputGroupComponent, BsButtonTypeDirective, BsModalHostComponent, BsModalDirective, BsModalHeaderDirective, BsModalBodyDirective, BsModalFooterDirective, BsDatatableComponent, BsDatatableColumnDirective, BsRowTemplateDirective, BsSpinnerComponent, SparkIconComponent, TranslateKeyPipe, ResolveTranslationPipe, ReferenceAttrValuePipe, SparkAttributeDescriptionComponent],\n templateUrl: './spark-reference-picker.component.html',\n changeDetection: ChangeDetectionStrategy.OnPush\n})\nexport class SparkReferencePickerComponent {\n private readonly sparkService = inject(SparkService);\n private readonly lang = inject(SparkLanguageService);\n\n /** Currently selected referenced id (or null when unset). */\n value = input<string | null>(null);\n /** Candidate items to pick from (the full query result, pre-loaded by the parent). */\n options = input<QueryResultItem[]>([]);\n /** Target entity type's CLR type name; used to load the grid's column headers. */\n referenceType = input<string | undefined>(undefined);\n /** Renders the field with the Bootstrap is-invalid state. */\n isInvalid = input(false);\n /** Optional id forwarded to the readonly input (for label association). */\n inputId = input<string | undefined>(undefined);\n /** Optional resolved label appended to the modal header (\"Select {title}\"). */\n title = input<string>('');\n\n valueChange = output<string | null>();\n\n colors = Color;\n\n showModal = signal(false);\n entityType = signal<EntityType | null>(null);\n pagination = signal<PaginationResponse<QueryResultItem> | undefined>(undefined);\n settings = signal(new DatatableSettings({\n perPage: { values: [10, 25, 50], selected: 10 },\n page: { values: [1], selected: 1 },\n sortColumns: []\n }));\n searchTerm = '';\n\n /** The picker lists query rows, so its columns are the query's: `showedOn` includes Query (#264; not None). */\n visibleAttributes = computed(() => {\n return this.entityType()?.attributes\n .filter(a => hasShowedOnFlag(a.showedOn, ShowedOn.Query))\n .sort((a, b) => a.order - b.order) || [];\n });\n\n // Typed rows so the datatable generic infers the row type.\n rows = computed<QueryResultItem[]>(() => this.pagination()?.data ?? []);\n\n displayValue = computed(() => {\n const id = this.value();\n if (!id) return this.lang.t('common.notSelected');\n const selected = this.options().find(o => o.id === id);\n return selected?.breadcrumb || id;\n });\n\n async open(): Promise<void> {\n this.searchTerm = '';\n this.settings.set(new DatatableSettings({\n perPage: { values: [10, 25, 50], selected: 10 },\n page: { values: [1], selected: 1 },\n sortColumns: []\n }));\n\n // Lazily resolve the target entity type for the grid's column headers.\n const refType = this.referenceType();\n if (!this.entityType() && refType) {\n const types = await this.sparkService.getEntityTypes();\n this.entityType.set(types.find(t => t.clrType === refType) || null);\n }\n\n this.applyFilter();\n this.showModal.set(true);\n }\n\n onSearchChange(): void {\n this.settings().page.selected = 1;\n this.applyFilter();\n }\n\n applyFilter(): void {\n let filteredItems = this.options();\n\n if (this.searchTerm.trim()) {\n const term = this.searchTerm.toLowerCase().trim();\n filteredItems = this.options().filter(item => {\n if (item.breadcrumb?.toLowerCase().includes(term)) return true;\n return item.values.some(v => {\n const value = v.breadcrumb ?? v.value;\n if (value == null) return false;\n return String(value).toLowerCase().includes(term);\n });\n });\n }\n\n const totalPages = Math.ceil(filteredItems.length / this.settings().perPage.selected) || 1;\n this.pagination.set({\n data: filteredItems,\n totalRecords: filteredItems.length,\n totalPages: totalPages,\n perPage: this.settings().perPage.selected,\n page: this.settings().page.selected\n });\n\n this.settings().page.values = Array.from({ length: totalPages }, (_, i) => i + 1);\n\n if (this.settings().page.selected > totalPages) {\n this.settings().page.selected = 1;\n }\n }\n\n clearSearch(): void {\n this.searchTerm = '';\n this.onSearchChange();\n }\n\n select(item: QueryResultItem): void {\n this.valueChange.emit(item.id ?? null);\n this.close();\n }\n\n close(): void {\n this.showModal.set(false);\n this.searchTerm = '';\n }\n}\n","<bs-input-group>\n <input\n type=\"text\"\n [id]=\"inputId()\"\n [value]=\"displayValue()\"\n readonly\n [class.is-invalid]=\"isInvalid()\">\n <button\n type=\"button\"\n bsInputGroupBtn\n [color]=\"colors.secondary\"\n (click)=\"open()\">\n ...\n </button>\n</bs-input-group>\n\n<bs-modal [isOpen]=\"showModal()\" (isOpenChange)=\"!$event && close()\">\n <div *bsModal class=\"reference-modal\">\n <div bsModalHeader>\n <h5 class=\"modal-title\">{{ 'common.select' | t }} {{ title() }}</h5>\n </div>\n\n <div bsModalBody>\n <!-- bs-modal portals its content into a CDK overlay outside the host form, so the\n modal needs its own <bs-form> context for inputs to pick up .form-control styles. -->\n <bs-form>\n @if (entityType()) {\n <!-- Search box -->\n <bs-grid>\n <div bsRow class=\"mb-3\">\n <div [md]=\"12\">\n <bs-input-group>\n <span class=\"addon\">\n <spark-icon name=\"search\" />\n </span>\n <input\n type=\"text\"\n [placeholder]=\"'common.search' | t\"\n [(ngModel)]=\"searchTerm\"\n (ngModelChange)=\"onSearchChange()\">\n @if (searchTerm) {\n <button\n type=\"button\"\n class=\"btn btn-outline-secondary\"\n (click)=\"clearSearch()\">\n <spark-icon name=\"x-lg\" />\n </button>\n }\n </bs-input-group>\n </div>\n @if (searchTerm && pagination()) {\n <div [md]=\"12\" class=\"text-end mt-1\">\n <span class=\"text-muted\">\n {{ pagination()!.totalRecords }} {{ pagination()!.totalRecords === 1 ? ('common.resultFound' | t) : ('common.resultsFound' | t) }}\n </span>\n </div>\n }\n </div>\n </bs-grid>\n\n <bs-datatable\n [(settings)]=\"settings\"\n [data]=\"rows()\"\n (rowClick)=\"select($event.row)\">\n @for (attr of visibleAttributes(); track attr.id) {\n <div *bsDatatableColumn=\"attr.name; sortable: true\">\n {{ attr.label | resolveTranslation:attr.name }}\n <spark-attribute-description [description]=\"attr.description\" />\n </div>\n }\n\n <ng-container *bsRowTemplate=\"let item\">\n @for (attr of visibleAttributes(); track attr.id) {\n <td>{{ $any(item) | referenceAttrValue:attr.name }}</td>\n }\n </ng-container>\n </bs-datatable>\n } @else {\n <div class=\"text-center p-3\">\n <bs-spinner />\n </div>\n }\n </bs-form>\n </div>\n\n <div bsModalFooter>\n <button type=\"button\" [color]=\"colors.secondary\" (click)=\"close()\">{{ 'common.cancel' | t }}</button>\n </div>\n </div>\n</bs-modal>\n","import { ChangeDetectionStrategy, Component, computed, input, output, signal } from '@angular/core';\nimport { FormsModule } from '@angular/forms';\nimport { Color } from '@mintplayer/ng-bootstrap';\nimport { BsFormComponent, BsFormControlDirective } from '@mintplayer/ng-bootstrap/form';\nimport { BsGridComponent, BsGridRowDirective, BsGridColDirective } from '@mintplayer/ng-bootstrap/grid';\nimport { BsInputGroupComponent } from '@mintplayer/ng-bootstrap/input-group';\nimport { BsButtonTypeDirective } from '@mintplayer/ng-bootstrap/button-type';\nimport { BsModalHostComponent, BsModalDirective, BsModalHeaderDirective, BsModalBodyDirective, BsModalFooterDirective } from '@mintplayer/ng-bootstrap/modal';\nimport { BsTableComponent } from '@mintplayer/ng-bootstrap/table';\nimport { LookupReferenceValue, resolveTranslation } from '@mintplayer/ng-spark/models';\nimport { TranslateKeyPipe, ResolveTranslationPipe } from '@mintplayer/ng-spark/pipes';\nimport { SparkIconComponent } from '@mintplayer/ng-spark/icon';\n\n/**\n * Reusable LookupReference value picker: a readonly textbox showing the current selection's\n * translated label plus a \"…\" button that opens a searchable modal list over the active\n * `options`. Per-instance state, so it serves both top-level lookup attributes (display type\n * Modal) and individual inline AsDetail lookup cells. Emits the picked key via `valueChange`.\n */\n@Component({\n selector: 'spark-lookup-picker',\n imports: [FormsModule, BsFormComponent, BsFormControlDirective, BsGridComponent, BsGridRowDirective, BsGridColDirective, BsInputGroupComponent, BsButtonTypeDirective, BsModalHostComponent, BsModalDirective, BsModalHeaderDirective, BsModalBodyDirective, BsModalFooterDirective, BsTableComponent, SparkIconComponent, TranslateKeyPipe, ResolveTranslationPipe],\n templateUrl: './spark-lookup-picker.component.html',\n changeDetection: ChangeDetectionStrategy.OnPush\n})\nexport class SparkLookupPickerComponent {\n /** Currently selected lookup key (or null when unset). */\n value = input<string | null>(null);\n /** Active lookup values to pick from. */\n options = input<LookupReferenceValue[]>([]);\n /** Renders the field with the Bootstrap is-invalid state. */\n isInvalid = input(false);\n /** Optional id forwarded to the readonly input (for label association). */\n inputId = input<string | undefined>(undefined);\n /** Optional resolved label appended to the modal header (\"Select {title}\"). */\n title = input<string>('');\n\n valueChange = output<string | null>();\n\n colors = Color;\n\n showModal = signal(false);\n searchTerm = signal('');\n\n displayValue = computed(() => {\n const key = this.value();\n if (key == null || key === '') return '';\n const selected = this.options().find(o => o.key === String(key));\n if (!selected) return String(key);\n return resolveTranslation(selected.values) || selected.key;\n });\n\n filteredItems = computed(() => {\n if (!this.searchTerm().trim()) {\n return this.options();\n }\n const term = this.searchTerm().toLowerCase().trim();\n return this.options().filter(item => {\n const translation = resolveTranslation(item.values);\n return translation.toLowerCase().includes(term) || item.key.toLowerCase().includes(term);\n });\n });\n\n open(): void {\n this.searchTerm.set('');\n this.showModal.set(true);\n }\n\n select(item: LookupReferenceValue): void {\n this.valueChange.emit(item.key);\n this.close();\n }\n\n close(): void {\n this.showModal.set(false);\n this.searchTerm.set('');\n }\n}\n","<bs-input-group>\n <input\n type=\"text\"\n [id]=\"inputId()\"\n [value]=\"displayValue()\"\n readonly\n [class.is-invalid]=\"isInvalid()\">\n <button\n type=\"button\"\n bsInputGroupBtn\n [color]=\"colors.secondary\"\n (click)=\"open()\">\n ...\n </button>\n</bs-input-group>\n\n<bs-modal [isOpen]=\"showModal()\" (isOpenChange)=\"!$event && close()\">\n <div *bsModal>\n <div bsModalHeader>\n <h5 class=\"modal-title\">{{ 'common.select' | t }} {{ title() }}</h5>\n </div>\n\n <div bsModalBody>\n <!-- bs-modal portals its content into a CDK overlay outside the host form, so the\n modal needs its own <bs-form> context for inputs to pick up .form-control styles. -->\n <bs-form>\n <!-- Search box -->\n <bs-grid>\n <div bsRow class=\"mb-3\">\n <div [col]>\n <bs-input-group>\n <span class=\"addon\">\n <spark-icon name=\"search\" />\n </span>\n <input\n type=\"text\"\n [placeholder]=\"'common.search' | t\"\n [ngModel]=\"searchTerm()\"\n (ngModelChange)=\"searchTerm.set($event)\">\n @if (searchTerm()) {\n <button\n type=\"button\"\n class=\"btn btn-outline-secondary\"\n (click)=\"searchTerm.set('')\">\n <spark-icon name=\"x-lg\" />\n </button>\n }\n </bs-input-group>\n </div>\n </div>\n </bs-grid>\n\n <!-- List of items -->\n <bs-table [striped]=\"true\" [hover]=\"true\">\n <tbody class=\"align-middle\">\n @for (item of filteredItems(); track item.key) {\n <tr\n [class.table-primary]=\"value() === item.key\"\n (click)=\"select(item)\"\n style=\"cursor: pointer;\">\n <td>{{ item.values | resolveTranslation:item.key }}</td>\n </tr>\n } @empty {\n <tr>\n <td class=\"text-center text-muted\">{{ 'common.noItemsFound' | t }}</td>\n </tr>\n }\n </tbody>\n </bs-table>\n </bs-form>\n </div>\n\n <div bsModalFooter>\n <button type=\"button\" [color]=\"colors.secondary\" (click)=\"close()\">{{ 'common.cancel' | t }}</button>\n </div>\n </div>\n</bs-modal>\n","import { PersistentObject, RefreshOverlay, RefreshTrigger } from '@mintplayer/ng-spark/models';\n\n/** What the coordinator needs from its host, so it can be tested without mounting a form. */\nexport interface RefreshCoordinatorHost {\n /** POSTs the object and resolves with the reshaped one. */\n send(triggeredBy: string): Promise<PersistentObject>;\n /** Values as they are right now — read at dispatch time to snapshot what is being sent. */\n currentValues(): Record<string, any>;\n /** Applies a settled response. Not called for a superseded one. */\n apply(response: PersistentObject, sent: Record<string, any>): void;\n /** Surfaced so the host can show a busy affordance. Never used to disable fields. */\n setBusy(busy: boolean): void;\n}\n\n/**\n * Serializes refreshes for **one** form instance and drops superseded ones.\n *\n * Per-instance rather than a service, deliberately. The retry-action modal renders its own\n * `spark-po-form`, and a refresh can carry a retry operation — so a refresh can open a modal\n * containing a form whose own attributes may trigger refreshes. A shared coordinator would let the\n * nested form resolve or supersede the outer form's pending request. The same applies to the\n * recursive `spark-po-form` used for modal AsDetail editing.\n *\n * Cancellation is not available: the service layer is promise-based (`firstValueFrom`), so a stale\n * response *will* arrive. It is discarded by sequence number rather than prevented.\n */\nexport class RefreshCoordinator {\n private queue: Promise<void> = Promise.resolve();\n private sequence = 0;\n private settled = 0;\n private pending = new Set<string>();\n /** Debounce timers for `ValueChanged` free text, keyed by path. A path here is also in `pending`. */\n private timers = new Map<string, ReturnType<typeof setTimeout>>();\n\n constructor(private readonly host: RefreshCoordinatorHost) {}\n\n /** Whether a refresh is in flight. */\n get isRefreshing(): boolean {\n return this.sequence !== this.settled;\n }\n\n /**\n * Marks `attributeName` as needing a refresh without sending one — for free-text editors, which\n * would otherwise issue a request per keystroke. Flushed by {@link blur} or {@link flush}.\n */\n markPending(attributeName: string): void {\n this.pending.add(attributeName);\n }\n\n /**\n * Routes a change by its {@link RefreshDispatch}: send now, send once typing pauses, or wait for\n * blur. The one entry point a form needs, so the three editors' policies live here.\n */\n dispatch(attributeName: string, mode: RefreshDispatch): Promise<void> {\n switch (mode) {\n case 'immediate':\n return this.trigger(attributeName);\n case 'debounced':\n return this.trigger(attributeName, { debounceMs: SPARK_REFRESH_DEBOUNCE_MS });\n case 'blur':\n this.markPending(attributeName);\n return Promise.resolve();\n }\n }\n\n /** Sends a pending refresh for `attributeName`, if one was marked (or is waiting on its debounce). */\n blur(attributeName: string): Promise<void> {\n this.clearTimer(attributeName);\n if (!this.pending.delete(attributeName)) return Promise.resolve();\n return this.trigger(attributeName);\n }\n\n /**\n * Sends every refresh still marked pending, including any waiting on a debounce. Called before\n * save, so a value typed and never blurred — the user tabbing straight to the save button — is\n * still reflected before the object goes.\n */\n async flush(): Promise<void> {\n for (const name of [...this.timers.keys()]) this.clearTimer(name);\n const names = [...this.pending];\n this.pending.clear();\n for (const name of names) await this.trigger(name);\n await this.queue;\n }\n\n /** Drops every pending mark and debounce timer without sending — the form is going away. */\n dispose(): void {\n for (const name of [...this.timers.keys()]) this.clearTimer(name);\n this.pending.clear();\n }\n\n /**\n * Sends a refresh — immediately for discrete editors, where every change is a committed one.\n *\n * With `debounceMs`, the send waits until `attributeName` has been quiet for that long; each call\n * restarts the wait, so a burst of keystrokes costs one request. Until it fires, the path counts as\n * pending, so {@link blur} and {@link flush} send it straight away instead of waiting out the timer.\n */\n trigger(attributeName: string, options?: { debounceMs?: number }): Promise<void> {\n const debounceMs = options?.debounceMs ?? 0;\n if (debounceMs > 0) {\n this.clearTimer(attributeName);\n this.pending.add(attributeName);\n this.timers.set(attributeName, setTimeout(() => {\n this.timers.delete(attributeName);\n if (this.pending.delete(attributeName)) void this.trigger(attributeName);\n }, debounceMs));\n return Promise.resolve();\n }\n\n // An immediate send covers anything this path still had waiting.\n this.clearTimer(attributeName);\n this.pending.delete(attributeName);\n\n const ticket = ++this.sequence;\n this.host.setBusy(true);\n\n this.queue = this.queue.then(async () => {\n const sent = { ...this.host.currentValues() };\n try {\n const response = await this.host.send(attributeName);\n\n // A newer refresh was dispatched while this one was in flight, so this response describes a\n // form state that no longer exists. Applying it would resurrect superseded metadata.\n if (ticket !== this.sequence) return;\n\n this.host.apply(response, sent);\n } finally {\n this.settled = Math.max(this.settled, ticket);\n if (!this.isRefreshing) this.host.setBusy(false);\n }\n });\n\n return this.queue;\n }\n\n private clearTimer(attributeName: string): void {\n const timer = this.timers.get(attributeName);\n if (timer === undefined) return;\n clearTimeout(timer);\n this.timers.delete(attributeName);\n }\n}\n\n/** How long `ValueChanged` free text waits for typing to pause before refreshing — like the search box. */\nexport const SPARK_REFRESH_DEBOUNCE_MS = 300;\n\n/** The subset of an attribute definition that decides how its editor behaves. */\nexport interface RefreshTriggerShape {\n dataType?: string;\n lookupReferenceType?: string;\n isArray?: boolean;\n triggersRefresh?: RefreshTrigger;\n}\n\n/** When a change sends its refresh: on the change itself, on blur, or never. */\nexport type EffectiveRefreshTrigger = 'change' | 'blur' | 'none';\n\n/** How {@link RefreshCoordinator.dispatch} handles a change. */\nexport type RefreshDispatch = 'immediate' | 'debounced' | 'blur';\n\n/**\n * Resolves the declared `triggersRefresh` against the editor the attribute renders as.\n *\n * - `None` / absent → `'none'`.\n * - `Auto` → `'change'` for a discrete editor, `'blur'` for free text (the original behaviour).\n * - `ValueChanged` → `'change'` (debounced for free text — see {@link refreshDispatch}).\n * - `Blur` → `'blur'`, except on a discrete editor, which has no meaningful blur and so acts as\n * `ValueChanged`. The server's `--spark-verify-model` warns about that declaration.\n */\nexport function effectiveTrigger(attr: RefreshTriggerShape | undefined): EffectiveRefreshTrigger {\n switch (attr?.triggersRefresh) {\n case 'Auto':\n return isDiscreteEditor(attr) ? 'change' : 'blur';\n case 'ValueChanged':\n return 'change';\n case 'Blur':\n return isDiscreteEditor(attr) ? 'change' : 'blur';\n default:\n return 'none';\n }\n}\n\n/**\n * How a change to `attr` is sent, or `null` when it sends nothing. A `'change'` trigger on free\n * text is debounced; on a discrete editor it is immediate.\n */\nexport function refreshDispatch(attr: RefreshTriggerShape | undefined): RefreshDispatch | null {\n switch (effectiveTrigger(attr)) {\n case 'change':\n return isDiscreteEditor(attr) ? 'immediate' : 'debounced';\n case 'blur':\n return 'blur';\n default:\n return null;\n }\n}\n\n/** Whether the change to `attr` is sent immediately (a discrete editor with a change trigger). */\nexport function triggersImmediately(attr: RefreshTriggerShape | undefined): boolean {\n return refreshDispatch(attr) === 'immediate';\n}\n\n/**\n * Editors where every change is a deliberate, committed one. Everything else is free text, where\n * firing per keystroke would be unacceptable.\n *\n * ⚠️ This asks how the attribute is **rendered**, not what its `dataType` says, and the difference\n * is not academic. A lookup attribute carries the data type of its *key* — Fleet's `Car.Status` is\n * `dataType: \"string\"` with `lookupReferenceType: \"CarStatus\"` — so keying on `dataType` alone\n * classifies a `<bs-select>` as free text. It then waits for a blur that a select never emits, and\n * the refresh simply never fires: no request, no error, nothing to see.\n */\nexport function isDiscreteEditor(attr: RefreshTriggerShape | undefined): boolean {\n if (!attr) return false;\n\n // A lookup renders as a select (or a modal picker) whatever its key's type is.\n if (attr.lookupReferenceType) return true;\n\n switch ((attr.dataType ?? '').toLowerCase()) {\n case 'reference':\n case 'lookupreference':\n case 'boolean':\n case 'bool':\n case 'date':\n case 'datetime':\n case 'dateonly':\n case 'enum':\n case 'color':\n return true;\n default:\n return false;\n }\n}\n\n/** Empty overlay, so callers do not have to spell the type. */\nexport const NO_OVERLAY: RefreshOverlay = {};\n","import { ChangeDetectionStrategy, Component, computed, DestroyRef, inject, input, model, output, signal, effect, Type } from '@angular/core';\nimport { CommonModule, NgComponentOutlet, NgTemplateOutlet } from '@angular/common';\nimport { FormsModule } from '@angular/forms';\nimport { HttpErrorResponse } from '@angular/common/http';\nimport { CdkDropList, CdkDrag, CdkDragHandle, CdkDragPreview, CdkDragDrop, moveItemInArray } from '@angular/cdk/drag-drop';\nimport { Color } from '@mintplayer/ng-bootstrap';\nimport { BsCardComponent, BsCardHeaderComponent } from '@mintplayer/ng-bootstrap/card';\nimport { BsFormComponent, BsFormControlDirective } from '@mintplayer/ng-bootstrap/form';\nimport { BsGridComponent, BsGridRowDirective, BsGridColumnDirective, BsGridColDirective, BsColFormLabelDirective } from '@mintplayer/ng-bootstrap/grid';\nimport { BsInputGroupComponent } from '@mintplayer/ng-bootstrap/input-group';\nimport { BsButtonTypeDirective } from '@mintplayer/ng-bootstrap/button-type';\nimport { BsSelectComponent, BsSelectOption } from '@mintplayer/ng-bootstrap/select';\nimport { BsTreeSelectComponent, InMemoryTreeSelectProvider, TreeNode } from '@mintplayer/ng-bootstrap/tree-select';\nimport { BsModalHostComponent, BsModalDirective, BsModalHeaderDirective, BsModalBodyDirective, BsModalFooterDirective } from '@mintplayer/ng-bootstrap/modal';\nimport { BsCheckboxComponent } from '@mintplayer/ng-bootstrap/checkbox';\nimport { BsSpinnerComponent } from '@mintplayer/ng-bootstrap/spinner';\nimport { BsTabControlComponent, BsTabPageComponent, BsTabPageHeaderDirective } from '@mintplayer/ng-bootstrap/tab-control';\nimport { BsTableComponent } from '@mintplayer/ng-bootstrap/table';\nimport { SparkService, SparkLanguageService } from '@mintplayer/ng-spark/services';\nimport {\n TranslateKeyPipe,\n ResolveTranslationPipe,\n InputTypePipe,\n LookupDisplayTypePipe,\n LookupOptionsPipe,\n AsDetailDisplayValuePipe,\n AsDetailTypePipe,\n AsDetailColumnsPipe,\n AsDetailRowRendererPipe,\n AsDetailCellValuePipe,\n CanCreateDetailRowPipe,\n CanEditDetailRowPipe,\n CanDeleteDetailRowPipe,\n InlineRefOptionsPipe,\n ErrorForAttributePipe,\n} from '@mintplayer/ng-spark/pipes';\nimport {\n AttributeGroup,\n AttributeTab,\n ELookupDisplayType,\n EReferenceDisplayType,\n EntityAttributeDefinition,\n EntityPermissions,\n EntityType,\n LookupReference,\n LookupReferenceValue,\n PersistentObject,\n QueryResultItem,\n RefreshOverlay,\n RefreshedOption,\n RuleFailure,\n ShowedOn,\n ValidationError,\n AS_DETAIL_ROW_KEY,\n applyOverlay,\n nestedPoToDict,\n evaluateRules,\n hasShowedOnFlag,\n mergeRefreshValues,\n overlayFromResponse,\n resolveTranslation,\n} from '@mintplayer/ng-spark/models';\nimport { SparkIconComponent } from '@mintplayer/ng-spark/icon';\nimport { SparkAttributeDescriptionComponent } from '@mintplayer/ng-spark/attribute-description';\nimport { SPARK_ATTRIBUTE_RENDERERS, SparkResolvedRowRenderer, rowRendererInputs, withDeclaredInputs } from '@mintplayer/ng-spark/renderers';\nimport { SparkReferencePickerComponent } from './spark-reference-picker.component';\nimport { SparkLookupPickerComponent } from './spark-lookup-picker.component';\nimport { RefreshCoordinator, RefreshDispatch, effectiveTrigger, refreshDispatch } from './refresh-coordinator';\n\n/**\n * A refresh a form embedded in another form is asking its host to issue on its behalf.\n *\n * `dispatch` carries the decision the child's own editor made from its declared `triggersRefresh`\n * — send now, send once typing pauses, or wait for blur — so the host applies exactly the semantics\n * it would have applied to an editor of its own.\n */\nexport interface NestedTriggerRequest {\n /** The full path the server addresses, e.g. `Gate.ProjectMode`. */\n path: string;\n dispatch: RefreshDispatch;\n}\n\n@Component({\n selector: 'spark-po-form',\n imports: [CommonModule, NgTemplateOutlet, NgComponentOutlet, FormsModule, CdkDropList, CdkDrag, CdkDragHandle, CdkDragPreview, BsCardComponent, BsCardHeaderComponent, BsFormComponent, BsFormControlDirective, BsGridComponent, BsGridRowDirective, BsGridColumnDirective, BsGridColDirective, BsColFormLabelDirective, BsButtonTypeDirective, BsInputGroupComponent, BsSelectComponent, BsSelectOption, BsTreeSelectComponent, BsModalHostComponent, BsModalDirective, BsModalHeaderDirective, BsModalBodyDirective, BsModalFooterDirective, BsTableComponent, BsCheckboxComponent, BsSpinnerComponent, BsTabControlComponent, BsTabPageComponent, BsTabPageHeaderDirective, SparkIconComponent, SparkPoFormComponent, SparkReferencePickerComponent, SparkLookupPickerComponent, TranslateKeyPipe, ResolveTranslationPipe, InputTypePipe, LookupDisplayTypePipe, LookupOptionsPipe, AsDetailDisplayValuePipe, AsDetailTypePipe, AsDetailColumnsPipe, AsDetailRowRendererPipe, AsDetailCellValuePipe, CanCreateDetailRowPipe, CanDeleteDetailRowPipe, CanEditDetailRowPipe, InlineRefOptionsPipe, ErrorForAttributePipe, SparkAttributeDescriptionComponent],\n templateUrl: './spark-po-form.component.html',\n // The CDK drag placeholder is a clone of the dragged row (so it keeps the exact row\n // height). Hide its contents but keep it occupying space, so the drop gap is blank and\n // the surrounding rows don't shift. ::ng-deep because the cloned node is styled by CDK.\n styles: [`:host ::ng-deep .cdk-drag-placeholder { visibility: hidden; }`],\n changeDetection: ChangeDetectionStrategy.OnPush\n})\nexport class SparkPoFormComponent {\n private readonly sparkService = inject(SparkService);\n private readonly translations = inject(SparkLanguageService);\n protected readonly rendererRegistry = inject(SPARK_ATTRIBUTE_RENDERERS);\n\n entityType = input<EntityType | null>(null);\n formData = model<Record<string, any>>({});\n validationErrors = input<ValidationError[]>([]);\n showButtons = input(false);\n isSaving = input(false);\n parentId = input<string | undefined>(undefined);\n parentType = input<string | undefined>(undefined);\n /**\n * The parent a Reference attribute's option query runs under, when it differs from\n * `parentId`/`parentType`. Those two also name the object that owns an AsDetail row for the row\n * round-trips (`parentTypeForWire`), so they cannot carry a create page's sub-query parent: a New\n * opened from an Account's card must offer that account's repositories, while its detail rows still\n * belong to the type being created. Unset, the option queries use `parentId`/`parentType` as before.\n */\n referenceParentId = input<string | undefined>(undefined);\n referenceParentType = input<string | undefined>(undefined);\n\n private readonly optionQueryParent = computed(() => this.referenceParentId() && this.referenceParentType()\n ? { parentId: this.referenceParentId(), parentType: this.referenceParentType() }\n : { parentId: this.parentId(), parentType: this.parentType() });\n\n save = output<void>();\n cancel = output<void>();\n\n /**\n * The type id to refresh against. Absent means refresh is unavailable — the form still renders and\n * edits normally, so a host that has not opted in loses nothing.\n */\n objectTypeId = input<string | undefined>(undefined);\n /** The id of the object being edited; absent for a create. */\n objectId = input<string | undefined>(undefined);\n\n /**\n * Set when this form renders a single embedded AsDetail object inside a host form: the name of\n * the attribute holding it, e.g. `\"Gate\"`. Presence of a prefix switches the form from issuing\n * its own refreshes to reporting them upward, because an embedded object is not something the\n * server can be asked about on its own — the request must describe, and be authorized against,\n * the object that owns it.\n */\n triggerPathPrefix = input<string | null>(null);\n\n /** Raised instead of self-issuing when {@link triggerPathPrefix} is set. */\n nestedTriggerRequested = output<NestedTriggerRequest>();\n /** Blur counterpart, flushing a trigger the host only marked pending. */\n nestedTriggerBlurred = output<string>();\n\n /**\n * What the last refresh changed about each attribute's presentation, keyed by attribute name.\n *\n * Deliberately NOT folded back into `entityType`. All option loading hangs off one effect keyed on\n * `entityType` identity and `SparkService` caches nothing, so re-setting it would re-issue every\n * reference query and lookup fetch on every refresh; mutating it in place would not re-render at\n * all.\n *\n * ⚠️ A `model`, not a plain signal, because the page that owns the save has to see it. This form\n * renders from the overlaid attribute list; `spark-po-edit` and `spark-po-create` build the\n * payload from their own copy of the same filter. While the overlay stopped here, those two\n * filtered on the attribute's state *as loaded* — so an attribute a refresh hook revealed was\n * rendered, filled in by the user, and then silently left out of the save. When the same hook also\n * made it required (the ordinary case — a field appears *because* it is now needed) the server\n * refused the save for the very field the user had just filled, and the form became a dead end\n * with no way forward from inside it.\n */\n refreshOverlay = model<RefreshOverlay>({});\n isRefreshing = signal(false);\n\n colors = Color;\n referenceOptions = signal<Record<string, QueryResultItem[]>>({});\n\n // Multi-reference (Reference && isArray) editor state. Built from the same query\n // results as referenceOptions: a flat TreeNode list per attribute drives an\n // InMemoryTreeSelectProvider (the <bs-tree-select> data port), and a node lookup\n // resolves selected ids back to chip labels.\n referenceProviders = signal<Record<string, InMemoryTreeSelectProvider>>({});\n referenceNodes = signal<Record<string, Record<string, TreeNode>>>({});\n asDetailTypes = signal<Record<string, EntityType>>({});\n lookupReferenceOptions = signal<Record<string, LookupReference>>({});\n\n // Modal state for AsDetail object editing\n editingAsDetailAttr = signal<EntityAttributeDefinition | null>(null);\n asDetailFormData = signal<Record<string, any>>({});\n showAsDetailModal = signal(false);\n editingArrayIndex = signal<number | null>(null);\n\n // Permissions for array AsDetail entity types\n asDetailPermissions = signal<Record<string, EntityPermissions>>({});\n\n /**\n * What the server said when it refused to construct or release a row.\n *\n * A local signal rather than the `validationErrors` input, because this is the one class of error\n * the form raises on its own behalf: `validationErrors` is owned by the host (po-edit / po-create)\n * and arrives from a save. Cleared at the start of each lifecycle call, so a stale refusal never\n * outlives the click that caused it.\n *\n * Keyed by attribute name so a message renders against the grid it came from. A single global\n * alert would have been less code and worse: a form with two detail grids would show one grid's\n * refusal above the other, and the user's next click is on the grid, not the top of the page.\n */\n rowLifecycleErrors = signal<Record<string, ValidationError[]>>({});\n\n /** The refusal to render under `attrName`'s grid, if any. */\n rowLifecycleErrorsFor(attrName: string): ValidationError[] {\n return this.rowLifecycleErrors()[attrName] ?? [];\n }\n\n // Reference options for columns within array AsDetail types (keyed by parent attr name, then column name)\n asDetailReferenceOptions = signal<Record<string, Record<string, QueryResultItem[]>>>({});\n\n // Reference/Lookup picking is owned by the standalone spark-reference-picker /\n // spark-lookup-picker components (per-instance modal state); the form just feeds them\n // options and writes the emitted value back into formData / the row.\n ELookupDisplayType = ELookupDisplayType;\n EReferenceDisplayType = EReferenceDisplayType;\n\n /**\n * Every attribute this form could ever need option data for — including ones the model hides,\n * because a refresh may reveal them.\n *\n * Read by the option-loading effect, and deliberately independent of `refreshOverlay`: the loaders\n * read this synchronously, so an overlay dependency here would make every refresh re-issue every\n * reference query and lookup fetch. That is the whole reason the overlay is a separate signal.\n */\n optionSourceAttributes = computed(() => {\n // `showedOn: None` too (#264): the model draws it nowhere, and an action shows it per object — on load,\n // new or refresh. Only a query-only attribute can never appear on a form.\n return this.entityType()?.attributes\n .filter(a => hasShowedOnFlag(a.showedOn, ShowedOn.PersistentObject) || !hasShowedOnFlag(a.showedOn, ShowedOn.Query))\n .sort((a, b) => a.order - b.order) || [];\n });\n\n editableAttributes = computed(() => {\n const overlay = this.refreshOverlay();\n return this.entityType()?.attributes\n .map(a => applyOverlay(a, overlay[a.name]))\n .filter(a => !a.isReadOnly && hasShowedOnFlag(a.showedOn, ShowedOn.PersistentObject))\n .sort((a, b) => a.order - b.order) || [];\n });\n\n private static readonly DEFAULT_TAB: AttributeTab = { id: '__default__', name: 'Algemeen', label: { nl: 'Algemeen', en: 'General' }, order: 0 };\n\n ungroupedAttributes = computed(() => {\n const attrs = this.editableAttributes();\n const groupIds = new Set((this.entityType()?.groups || []).map(g => g.id));\n return attrs.filter(a => !a.group || !groupIds.has(a.group));\n });\n\n resolvedTabs = computed((): AttributeTab[] => {\n const et = this.entityType();\n const definedTabs = et?.tabs?.length ? [...et.tabs].sort((a, b) => a.order - b.order) : [];\n const hasUngroupedAttrs = this.ungroupedAttributes().length > 0;\n const hasUntabbedGroups = (et?.groups || []).some(g => !g.tab);\n\n if (hasUngroupedAttrs || hasUntabbedGroups || definedTabs.length === 0) {\n return [SparkPoFormComponent.DEFAULT_TAB, ...definedTabs];\n }\n return definedTabs;\n });\n\n groupsForTab(tab: AttributeTab): AttributeGroup[] {\n const groups = this.entityType()?.groups || [];\n if (tab.id === '__default__') {\n return groups.filter(g => !g.tab).sort((a, b) => a.order - b.order);\n }\n return groups.filter(g => g.tab === tab.id).sort((a, b) => a.order - b.order);\n }\n\n attrsForGroup(group: AttributeGroup): EntityAttributeDefinition[] {\n return this.editableAttributes().filter(a => a.group === group.id);\n }\n\n constructor() {\n // A `ValueChanged` debounce still waiting when the form goes must not fire against a dead form.\n inject(DestroyRef).onDestroy(() => this.refreshCoordinator.dispose());\n\n effect(() => {\n const et = this.entityType();\n const _parent = this.optionQueryParent();\n if (et) {\n this.loadReferenceOptions();\n this.loadAsDetailTypes();\n this.loadLookupReferenceOptions();\n }\n });\n }\n\n private toRecord<T>(entries: [string, T][]): Record<string, T> {\n const result: Record<string, T> = {};\n for (const [key, value] of entries) {\n result[key] = value;\n }\n return result;\n }\n\n async loadReferenceOptions(): Promise<void> {\n const refAttrs = this.optionSourceAttributes().filter(a => a.dataType === 'Reference' && a.query);\n if (refAttrs.length === 0) return;\n\n const entries = await Promise.all(\n refAttrs.filter(a => a.query).map(async attr => {\n const result = await this.sparkService.executeQueryByName(attr.query!, this.optionQueryParent());\n return [attr.name, result.items] as [string, QueryResultItem[]];\n })\n );\n const optionsByAttr = this.toRecord(entries);\n this.referenceOptions.set(optionsByAttr);\n\n // For multi-reference attributes (Reference && isArray), turn each query result\n // into a flat TreeNode list + an in-memory provider for <bs-tree-select>, and a\n // by-id node map used to render chip labels for the current selection.\n const providers: Record<string, InMemoryTreeSelectProvider> = {};\n const nodesByAttr: Record<string, Record<string, TreeNode>> = {};\n for (const attr of refAttrs) {\n if (!attr.isArray) continue;\n const pos = optionsByAttr[attr.name] || [];\n const nodes: TreeNode[] = pos\n .filter(po => !!po.id)\n .map(po => ({ id: po.id!, label: po.breadcrumb || po.id! }));\n providers[attr.name] = new InMemoryTreeSelectProvider(nodes);\n nodesByAttr[attr.name] = this.toRecord(nodes.map(n => [n.id, n] as [string, TreeNode]));\n }\n this.referenceProviders.set(providers);\n this.referenceNodes.set(nodesByAttr);\n }\n\n /**\n * Stable TreeNode[] value per multi-reference attribute, derived from the id array\n * in formData and the resolved node map. Recomputes only when formData or the node\n * map changes, so binding it into <bs-tree-select> doesn't churn on every CD pass.\n * Ids without a resolved node fall back to a label of the id itself.\n */\n referenceTreeValues = computed<Record<string, TreeNode[]>>(() => {\n const fd = this.formData();\n const nodesByAttr = this.referenceNodes();\n const result: Record<string, TreeNode[]> = {};\n for (const attr of this.editableAttributes()) {\n if (attr.dataType !== 'Reference' || !attr.isArray) continue;\n const ids: string[] = Array.isArray(fd[attr.name]) ? fd[attr.name] : [];\n const nodeMap = nodesByAttr[attr.name] || {};\n result[attr.name] = ids.map(id => nodeMap[id] ?? { id, label: id });\n }\n return result;\n });\n\n getReferenceProvider(attr: EntityAttributeDefinition): InMemoryTreeSelectProvider | undefined {\n return this.referenceProviders()[attr.name];\n }\n\n onReferenceTreeChange(attr: EntityAttributeDefinition, value: TreeNode | TreeNode[] | null): void {\n const nodes = Array.isArray(value) ? value : value ? [value] : [];\n const data = { ...this.formData() };\n data[attr.name] = nodes.map(n => n.id);\n this.formData.set(data);\n // A picked value is a discrete change: triggersRefresh fires now, as for a checkbox or a select.\n this.noteChange(attr);\n }\n\n async loadAsDetailTypes(): Promise<void> {\n const asDetailAttrs = this.optionSourceAttributes().filter(a => a.dataType === 'AsDetail' && a.asDetailType);\n if (asDetailAttrs.length === 0) return;\n\n const types = await this.sparkService.getEntityTypes();\n const newAsDetailTypes: Record<string, EntityType> = {};\n\n // The catalogue is Query-gated, and a row type edited through its parent usually has no rights\n // of its own — so it is normally absent from `types`, and resolving from there alone left\n // `asDetailTypes` empty. `asDetailColumns` then returned [] and the table rendered with no\n // columns at all. The server now ships the row definitions alongside the parent, gated on the\n // parent's right; the catalogue stays the first source so nothing changes for a type that is\n // in it (#385).\n const embedded = this.entityType()?.detailTypes ?? [];\n\n for (const attr of asDetailAttrs) {\n const asDetailType =\n types.find(t => t.clrType === attr.asDetailType)\n ?? embedded.find(t => t.clrType === attr.asDetailType);\n if (asDetailType) {\n newAsDetailTypes[attr.name] = asDetailType;\n\n if (attr.isArray) {\n // Ask by CLR type when the row type carries no alias — the embedded copy has its alias\n // pruned, and `getPermissions` accepts either.\n const perms = await this.sparkService.getPermissions(asDetailType.id);\n this.asDetailPermissions.update(prev => ({ ...prev, [attr.name]: perms }));\n\n const refCols = asDetailType.attributes.filter(a => a.dataType === 'Reference' && a.query);\n if (refCols.length > 0) {\n const refEntries = await Promise.all(\n refCols.filter(c => c.query).map(async col => {\n const result = await this.sparkService.executeQueryByName(col.query!, this.optionQueryParent());\n return [col.name, result.items] as [string, QueryResultItem[]];\n })\n );\n this.asDetailReferenceOptions.update(prev => ({ ...prev, [attr.name]: this.toRecord(refEntries) }));\n }\n\n // Inline editing of LookupReference child columns needs their options loaded\n // (keyed by lookup name in the shared lookupReferenceOptions, deduped).\n const lookupCols = asDetailType.attributes.filter(a => a.lookupReferenceType);\n for (const col of lookupCols) {\n const lookupName = col.lookupReferenceType!;\n if (!this.lookupReferenceOptions()[lookupName]) {\n const ref = await this.sparkService.getLookupReference(lookupName);\n this.lookupReferenceOptions.update(prev => ({ ...prev, [lookupName]: ref }));\n }\n }\n }\n }\n }\n this.asDetailTypes.set(newAsDetailTypes);\n }\n\n async loadLookupReferenceOptions(): Promise<void> {\n const lookupAttrs = this.optionSourceAttributes().filter(a => a.lookupReferenceType);\n if (lookupAttrs.length === 0) return;\n\n const lookupNames = [...new Set(lookupAttrs.map(a => a.lookupReferenceType!))];\n const entries = await Promise.all(\n lookupNames.map(async name => {\n const ref = await this.sparkService.getLookupReference(name);\n return [name, ref] as [string, LookupReference];\n })\n );\n this.lookupReferenceOptions.set(this.toRecord(entries));\n }\n\n getLookupOptions(attr: EntityAttributeDefinition): LookupReferenceValue[] {\n const lookupRef = attr.lookupReferenceType ? this.lookupReferenceOptions()[attr.lookupReferenceType] : null;\n return lookupRef?.values.filter(v => v.isActive) || [];\n }\n\n // Write the value emitted by a top-level spark-reference-picker / spark-lookup-picker\n // back into formData (the same write the old in-form modal selectors performed).\n onReferenceValueChange(attr: EntityAttributeDefinition, id: string | null): void {\n const data = { ...this.formData() };\n data[attr.name] = id;\n this.formData.set(data);\n this.noteChange(attr);\n }\n\n onLookupValueChange(attr: EntityAttributeDefinition, key: string | null): void {\n const data = { ...this.formData() };\n data[attr.name] = key;\n this.formData.set(data);\n this.noteChange(attr);\n }\n\n getEditRendererComponent(attr: EntityAttributeDefinition): Type<any> | null {\n if (!attr.renderer) return null;\n const reg = this.rendererRegistry.find(r => r.name === attr.renderer);\n return reg?.editComponent ?? null;\n }\n\n getEditRendererInputs(component: Type<any>, attr: EntityAttributeDefinition): Record<string, any> {\n return withDeclaredInputs(component, {\n value: this.formData()[attr.name],\n attribute: attr,\n options: attr.rendererOptions,\n valueChange: (newValue: any) => {\n const data = { ...this.formData() };\n data[attr.name] = newValue;\n this.formData.set(data);\n },\n });\n }\n\n /** Column renderer for a cell of an AsDetail sub-table (so embedded rows honor `col.renderer` too). */\n getAsDetailCellRendererComponent(col: EntityAttributeDefinition): Type<any> | null {\n if (!col.renderer) return null;\n return this.rendererRegistry.find(r => r.name === col.renderer)?.columnComponent ?? null;\n }\n\n /** Inputs of an AsDetail row renderer (`rowComponent`): the row and the object being edited. */\n getRowRendererInputs(renderer: SparkResolvedRowRenderer, row: Record<string, any>): Record<string, any> {\n return rowRendererInputs(renderer, row, this.objectId());\n }\n\n getAsDetailCellRendererInputs(component: Type<any>, row: Record<string, any>, col: EntityAttributeDefinition): Record<string, any> {\n return withDeclaredInputs(component, {\n value: row[col.name],\n attribute: col,\n options: col.rendererOptions,\n item: row,\n });\n }\n\n /** Edit-renderer for an inline AsDetail cell (so inline editing honors `col.renderer`, not just display). */\n getAsDetailCellEditRenderer(col: EntityAttributeDefinition): Type<any> | null {\n if (!col.renderer) return null;\n return this.rendererRegistry.find(r => r.name === col.renderer)?.editComponent ?? null;\n }\n\n getAsDetailCellEditRendererInputs(component: Type<any>, row: Record<string, any>, col: EntityAttributeDefinition): Record<string, any> {\n return withDeclaredInputs(component, {\n value: row[col.name],\n attribute: col,\n options: col.rendererOptions,\n item: row,\n valueChange: (newValue: any) => {\n row[col.name] = newValue;\n this.onFieldChange();\n },\n });\n }\n\n /**\n * Rules evaluated in the browser, against the *effective* metadata — so a rule a refresh hook\n * imposed is visible before the round-trip rather than only after the server rejects the save.\n */\n clientRuleFailures = computed<RuleFailure[]>(() => {\n const values = this.formData();\n return this.editableAttributes().flatMap(attr => evaluateRules(attr, values[attr.name]));\n });\n\n hasError(attrName: string): boolean {\n return this.clientRuleFailures().some(f => f.attributeName === attrName)\n || this.validationErrors().some(e => e.attributeName === attrName);\n }\n\n // Per-cell validation for inline AsDetail rows. Server-emitted errors are keyed by the\n // path \"{attr}[{rowIndex}].{col}\"; the client surfaces them like top-level field errors.\n private inlineErrorPath(attr: EntityAttributeDefinition, rowIndex: number, col: EntityAttributeDefinition): string {\n return `${attr.name}[${rowIndex}].${col.name}`;\n }\n\n hasInlineError(attr: EntityAttributeDefinition, rowIndex: number, col: EntityAttributeDefinition): boolean {\n const path = this.inlineErrorPath(attr, rowIndex, col);\n return this.validationErrors().some(e => e.attributeName === path);\n }\n\n inlineErrorMessage(attr: EntityAttributeDefinition, rowIndex: number, col: EntityAttributeDefinition): string | null {\n const path = this.inlineErrorPath(attr, rowIndex, col);\n const error = this.validationErrors().find(e => e.attributeName === path);\n return error ? resolveTranslation(error.errorMessage) : null;\n }\n\n /**\n * The single funnel every scalar / boolean / inline-cell edit passes through.\n *\n * `attr` is optional because some editors have nothing useful to say about which attribute moved.\n * A caller that knows should always say so — without it no refresh can fire.\n */\n onFieldChange(attr?: EntityAttributeDefinition): void {\n this.formData.set({ ...this.formData() });\n if (attr) this.noteChange(attr);\n }\n\n /**\n * A trigger inside an AsDetail row. Addressed by the same `{attr}[{index}].{col}` path the inline\n * validation errors already use, so the server can tell which row asked without a second\n * addressing scheme being invented for it.\n */\n onInlineCellChange(attr: EntityAttributeDefinition, rowIndex: number, col: EntityAttributeDefinition): void {\n this.onFieldChange();\n const dispatch = refreshDispatch(col);\n if (!dispatch || !this.objectTypeId()) return;\n\n const path = this.inlineErrorPath(attr, rowIndex, col);\n this.pendingNestedTrigger = { kind: 'row', attribute: attr.name, rowIndex };\n\n void this.refreshCoordinator.dispatch(path, dispatch);\n }\n\n /**\n * Which embedded target the in-flight refresh belongs to, if any.\n *\n * ⚠️ Tagged, not a nullable `rowIndex`. The two arms write to different places — a row inside\n * `formData`, versus the modal's working copy in `asDetailFormData` — and an index that happens\n * to be absent is not the same statement as \"this attribute holds one object\". With a nullable\n * index, a row refresh carrying a bad index would fall silently into the object arm.\n */\n private pendingNestedTrigger:\n | { kind: 'row'; attribute: string; rowIndex: number }\n | { kind: 'object'; attribute: string }\n | null = null;\n\n /**\n * A trigger inside a single embedded AsDetail object, raised by the modal's recursive form.\n *\n * The child form does not issue the request itself, for two reasons that both bite. Its\n * `entityType` is the embedded type, so a payload built there describes `GateSettings` while the\n * request must describe the Repository that owns it. And nested AsDetail types carry no rights of\n * their own — the server authorizes a nested refresh on the OWNING type — so a child-issued\n * request would be asking the wrong question of security.json.\n */\n onEmbeddedTrigger(attributeName: string, event: NestedTriggerRequest): void {\n if (!this.objectTypeId()) return;\n\n // Set on both arms, exactly as onInlineCellChange does: the blur that later flushes a pending\n // trigger does not know which target it belongs to, so the mark is what remembers.\n this.pendingNestedTrigger = { kind: 'object', attribute: attributeName };\n\n void this.refreshCoordinator.dispatch(event.path, event.dispatch);\n }\n\n /** Blur counterpart of {@link onEmbeddedTrigger}, for the child's free-text editors. */\n onEmbeddedTriggerBlur(path: string): void {\n if (!this.objectTypeId()) return;\n void this.refreshCoordinator.blur(path);\n }\n\n /**\n * Applies a refresh that ran against a detail row: the row's own values, and the column metadata\n * for the grid it lives in.\n *\n * The column metadata comes from `asDetailTypes` — a different signal from `entityType` — which is\n * why a nested response cannot go through the top-level overlay.\n *\n * ⚠️ The row array is mutated in place rather than replaced. Rows are tracked by index, so handing\n * the template a new array destroys and rebuilds every row's DOM and takes focus with it, mid-edit.\n */\n private applyNestedResponse(\n nested: { kind: 'row'; attribute: string; rowIndex: number } | { kind: 'object'; attribute: string },\n response: PersistentObject,\n ): void {\n this.pendingNestedTrigger = null;\n\n if (nested.kind === 'row') {\n const rows = this.formData()[nested.attribute];\n const row = Array.isArray(rows) ? rows[nested.rowIndex] : undefined;\n if (row) {\n for (const attribute of response.attributes ?? []) {\n row[attribute.name] = attribute.value ?? null;\n }\n // The array identity is unchanged; this only tells the signal graph the contents moved.\n this.formData.set({ ...this.formData() });\n }\n } else {\n // ⚠️ The modal's working copy, never `formData`. `openAsDetailEditor` copies the embedded dict\n // precisely so dismissing the modal discards the edit; writing a refreshed value through to\n // the parent would make a cancelled edit stick. `saveAsDetailObject` remains the only writer.\n //\n // Replacing the object identity is also correct here, where mutating in place is correct for\n // a row: rows are tracked by index and rebuilding them costs focus, while the recursive form\n // re-renders from this signal by design.\n const current = this.asDetailFormData();\n const next = { ...current };\n for (const attribute of response.attributes ?? []) {\n next[attribute.name] = attribute.value ?? null;\n }\n this.asDetailFormData.set(next);\n }\n\n const overlay = overlayFromResponse(response);\n this.asDetailTypes.update(prev => {\n const type = prev[nested.attribute];\n if (!type) return prev;\n\n return {\n ...prev,\n [nested.attribute]: {\n ...type,\n attributes: type.attributes.map(a => applyOverlay(a, overlay[a.name])),\n },\n };\n });\n }\n\n onInlineCellBlur(attr: EntityAttributeDefinition, rowIndex: number, col: EntityAttributeDefinition): void {\n if (effectiveTrigger(col) === 'none' || !this.objectTypeId()) return;\n void this.refreshCoordinator.blur(this.inlineErrorPath(attr, rowIndex, col));\n }\n\n /**\n * Blur handler for free-text editors — sends the refresh their keystrokes only marked pending, or\n * cuts short a `ValueChanged` debounce that is still waiting.\n */\n onFieldBlur(attr: EntityAttributeDefinition): void {\n if (this.triggerPathPrefix()) {\n if (effectiveTrigger(attr) === 'none') return;\n this.nestedTriggerBlurred.emit(this.nestedTriggerPath(attr));\n return;\n }\n\n if (!this.canRefresh(attr)) return;\n void this.refreshCoordinator.blur(attr.name);\n }\n\n private noteChange(attr: EntityAttributeDefinition): void {\n // Embedded: report upward and stop. The parent owns the request because only it knows the\n // object being edited and holds the right the server authorizes against.\n const dispatch = refreshDispatch(attr);\n if (this.triggerPathPrefix()) {\n if (!dispatch) return;\n this.nestedTriggerRequested.emit({ path: this.nestedTriggerPath(attr), dispatch });\n return;\n }\n\n if (!dispatch || !this.canRefresh(attr)) return;\n\n // Discrete editors send now. Free text either only marks itself pending (`Auto` / `Blur`: the\n // request goes on blur, or on save) or waits for typing to pause (`ValueChanged`).\n void this.refreshCoordinator.dispatch(attr.name, dispatch);\n }\n\n /** `Gate.ProjectMode` — the index-free counterpart of the inline grid's `Jobs[1].Kind`. */\n private nestedTriggerPath(attr: EntityAttributeDefinition): string {\n return `${this.triggerPathPrefix()}.${attr.name}`;\n }\n\n private canRefresh(attr: EntityAttributeDefinition): boolean {\n return effectiveTrigger(attr) !== 'none' && !!this.objectTypeId();\n }\n\n /**\n * Per-instance, never a service: the retry-action modal renders its own `spark-po-form`, and a\n * refresh may carry a retry operation — so a refresh can open a modal containing a form that\n * refreshes. A shared coordinator would let the nested form supersede this one's request.\n */\n protected readonly refreshCoordinator = new RefreshCoordinator({\n send: (triggeredBy) => this.sparkService.refresh(\n this.objectTypeId()!, this.buildRefreshPayload(), triggeredBy),\n currentValues: () => this.formData(),\n apply: (response, sent) => {\n // A nested response describes a detail ROW, not this form. Applying it to the top-level\n // overlay would hide every attribute the row does not happen to have.\n const nested = this.pendingNestedTrigger;\n if (nested) {\n this.applyNestedResponse(nested, response);\n return;\n }\n\n this.refreshOverlay.set(overlayFromResponse(response));\n this.formData.set(mergeRefreshValues(sent, this.formData(), response));\n this.applyRefreshedOptions(response);\n },\n setBusy: (busy) => this.isRefreshing.set(busy),\n });\n\n private buildRefreshPayload() {\n const et = this.entityType();\n const values = this.embeddedValues();\n return {\n id: this.objectId(),\n name: et?.name ?? '',\n // The EntityType's id, never `objectTypeId()` — that is the ROUTE segment, which is an alias\n // (\"car\") as often as a guid. The server types this field as a Guid, so an alias fails\n // deserialization and the request 500s before the handler is reached: no hook, no error the\n // client can act on. The route segment still carries the alias, which the server resolves.\n objectTypeId: et?.id ?? this.objectTypeId()!,\n attributes: (et?.attributes ?? []).map(a => ({\n name: a.name,\n value: values[a.name] ?? null,\n isValueChanged: true,\n })),\n } as any;\n }\n\n /**\n * The values to post, with the open modal's working copy substituted for the attribute it edits.\n *\n * ⚠️ Without this the server receives the embedded object as it was when the modal opened — `{}`\n * for a gate that has never been set — and the hook decides against values the user cannot see it\n * ignoring. `formData` deliberately does not hold the in-progress edit (cancelling the modal has\n * to discard it), so the refresh has to reach into `asDetailFormData` instead. Only the attribute\n * under edit is substituted; everything else is the form's own state.\n */\n private embeddedValues(): Record<string, any> {\n const nested = this.pendingNestedTrigger;\n if (nested?.kind !== 'object') return this.formData();\n\n return { ...this.formData(), [nested.attribute]: this.asDetailFormData() };\n }\n\n /**\n * Folds replaced option lists into the signals the editors already read, so a refreshed dropdown\n * renders through the same path as a loaded one.\n *\n * `undefined` means the hook did not touch this attribute's options and the loaded set stands; an\n * empty array means it deliberately left none. Collapsing the two would blank every dropdown the\n * hook never mentioned.\n */\n private applyRefreshedOptions(response: PersistentObject): void {\n const replaced = (response.attributes ?? [])\n .map(a => [a.name, (a as { options?: RefreshedOption[] | null }).options] as const)\n .filter(([, options]) => options !== undefined && options !== null);\n\n if (replaced.length === 0) return;\n\n const byName = new Map(this.optionSourceAttributes().map(a => [a.name, a]));\n\n this.lookupReferenceOptions.update(prev => {\n const next = { ...prev };\n for (const [name, options] of replaced) {\n const attr = byName.get(name);\n if (!attr?.lookupReferenceType) continue;\n next[attr.lookupReferenceType] = {\n ...(next[attr.lookupReferenceType] ?? { name: attr.lookupReferenceType, isTransient: true, displayType: ELookupDisplayType.Dropdown }),\n values: (options ?? []).map(o => ({ key: o.key, values: o.label ?? { en: o.key }, isActive: true })),\n } as LookupReference;\n }\n return next;\n });\n\n this.referenceOptions.update(prev => {\n const next = { ...prev };\n for (const [name, options] of replaced) {\n const attr = byName.get(name);\n if (attr?.dataType !== 'Reference') continue;\n // A refresh hook replaced this attribute's options, so synthesise the same row shape the\n // query path produces: an id and a display string, which is all an option list ever needed.\n next[name] = (options ?? []).map(o => ({\n id: o.key,\n breadcrumb: o.label ? resolveTranslation(o.label) : o.key,\n values: [],\n })) as QueryResultItem[];\n }\n return next;\n });\n\n // A multi-reference renders through <bs-tree-select>, which reads the provider and the by-id\n // node map rather than `referenceOptions` — so a refreshed list has to be rebuilt there too, or\n // the hook's options never reach the picker. The node map keeps the labels it already had: a\n // selected id the new list no longer offers must still render as its chip, not as a raw id.\n const multi = replaced.filter(([name]) => byName.get(name)?.dataType === 'Reference' && byName.get(name)?.isArray);\n if (multi.length === 0) return;\n\n const providers = { ...this.referenceProviders() };\n const nodesByAttr = { ...this.referenceNodes() };\n for (const [name, options] of multi) {\n const nodes: TreeNode[] = (options ?? []).map(o => ({ id: o.key, label: o.label ? resolveTranslation(o.label) : o.key }));\n providers[name] = new InMemoryTreeSelectProvider(nodes);\n nodesByAttr[name] = { ...(nodesByAttr[name] ?? {}), ...this.toRecord(nodes.map(n => [n.id, n] as [string, TreeNode])) };\n }\n this.referenceProviders.set(providers);\n this.referenceNodes.set(nodesByAttr);\n }\n\n /** Sends anything still pending, so a typed-but-never-blurred trigger is reflected before save. */\n async flushPendingRefresh(): Promise<void> {\n await this.refreshCoordinator.flush();\n }\n\n async onSave(): Promise<void> {\n // A value typed and never blurred — the user tabbing straight to Save — must still reshape the\n // object before it goes, or the server enforces rules the user was never shown.\n await this.flushPendingRefresh();\n\n // Evaluated AFTER the flush: the refresh may be what imposes the rule being checked.\n if (this.clientRuleFailures().length > 0) return;\n\n this.save.emit();\n }\n\n onCancel(): void {\n this.cancel.emit();\n }\n\n // AsDetail object modal methods\n openAsDetailEditor(attr: EntityAttributeDefinition): void {\n this.editingAsDetailAttr.set(attr);\n this.editingArrayIndex.set(null);\n this.asDetailFormData.set({ ...(this.formData()[attr.name] || {}) });\n this.showAsDetailModal.set(true);\n }\n\n saveAsDetailObject(): void {\n const attr = this.editingAsDetailAttr();\n if (attr) {\n const data = { ...this.formData() };\n if (attr.isArray) {\n const arr = [...(data[attr.name] || [])];\n const idx = this.editingArrayIndex();\n if (idx !== null) {\n arr[idx] = { ...this.asDetailFormData() };\n } else {\n arr.push({ ...this.asDetailFormData() });\n }\n data[attr.name] = arr;\n } else {\n data[attr.name] = { ...this.asDetailFormData() };\n }\n this.formData.set(data);\n }\n this.closeAsDetailModal();\n }\n\n closeAsDetailModal(): void {\n this.showAsDetailModal.set(false);\n this.editingAsDetailAttr.set(null);\n this.editingArrayIndex.set(null);\n this.asDetailFormData.set({});\n }\n\n // ---- Server-side row lifecycle ------------------------------------------------------------\n //\n // A row type may opt in (EntityType.serverSideRowLifecycle) to having the server construct its\n // new rows and be consulted before one is removed. Everything below is a no-op for a type that\n // has not — which is the point: not opting in costs no request, and the four mutators behave\n // exactly as they did before.\n\n /**\n * Whether `attr`'s row type asks the server before a row is added or removed.\n *\n * Read off the ROW type, never the parent. `asDetailTypes` already resolves it from the Query-gated\n * catalogue first and the parent's `detailTypes` second, and the flag survives the server-side\n * pruning of the embedded copy, so both sources give the same answer.\n */\n private roundTripsRowLifecycle(attr: EntityAttributeDefinition): boolean {\n return this.asDetailTypes()[attr.name]?.serverSideRowLifecycle === true;\n }\n\n /** The type id to address the row type by on the wire. */\n private rowTypeId(attr: EntityAttributeDefinition): string | undefined {\n return this.asDetailTypes()[attr.name]?.id;\n }\n\n /**\n * How to name the PARENT's type on the wire.\n *\n * ⚠️ The fallback must be the type **id**, never `clrType`. The server resolves this through\n * `ModelLoader.ResolveEntityType`, which accepts a GUID or a declared alias and nothing else — so\n * a CLR name resolves to null and the request is refused, indistinguishably from an unknown type.\n * The user would see \"the server refused this change\" on a perfectly ordinary Add.\n *\n * It is a live path, not a theoretical one: `spark-po-create` binds no `parentType` (there is no\n * parent route segment on a create page), so every host that creates an object with an opted-in\n * detail grid lands here.\n */\n private parentTypeForWire(): string | undefined {\n return this.parentType() ?? this.entityType()?.id;\n }\n\n /**\n * Builds the dict a new row starts from: `{}` as before, or the object the server constructed.\n *\n * Returns null when the server refused — the caller must then add nothing, which is the whole\n * difference between a veto and a message.\n */\n private async buildNewRow(attr: EntityAttributeDefinition): Promise<Record<string, any> | null> {\n const typeId = this.rowTypeId(attr);\n if (!this.roundTripsRowLifecycle(attr) || !typeId) return {};\n\n this.clearRowLifecycleError(attr.name);\n try {\n const po = await this.sparkService.newObject(typeId, {\n asDetailAttribute: attr.name,\n parentType: this.parentTypeForWire(),\n // Absent while the parent is unsaved, deliberately: the server hands the hook a null parent\n // rather than trusting the client's copy of one.\n parentId: this.objectId(),\n });\n // nestedPoToDict carries the server-minted row key across as __sparkRowKey, so the row is\n // matchable on save from the moment it appears — the reason the round trip is worth a request.\n return nestedPoToDict(po);\n } catch (e) {\n this.captureRowLifecycleError(attr.name, e);\n return null;\n }\n }\n\n /**\n * Asks whether a stored row may be removed. True when it may — including every case where there\n * is nothing to ask about: a type that has not opted in, a parent that has never been saved, or a\n * row with no key yet because it was added in this same editing session and never persisted.\n */\n private async mayRemoveRow(attr: EntityAttributeDefinition, row: Record<string, any> | undefined): Promise<boolean> {\n const typeId = this.rowTypeId(attr);\n const parentId = this.objectId();\n const parentType = this.parentTypeForWire();\n const rowKey = row?.[AS_DETAIL_ROW_KEY];\n\n if (!this.roundTripsRowLifecycle(attr) || !typeId || !parentId || !parentType || !rowKey) {\n return true;\n }\n\n this.clearRowLifecycleError(attr.name);\n try {\n await this.sparkService.deleteRow(typeId, {\n asDetailAttribute: attr.name,\n parentType,\n parentId,\n rowKey,\n });\n return true;\n } catch (e) {\n this.captureRowLifecycleError(attr.name, e);\n return false;\n }\n }\n\n /**\n * Turns a rejected lifecycle call into something the user can read.\n *\n * The envelope puts validation errors under `result`, the same place the save path reads them\n * from — reading `error.error.errors` matched nothing and made every veto silent.\n */\n private clearRowLifecycleError(attrName: string): void {\n this.rowLifecycleErrors.update(prev => {\n const next = { ...prev };\n delete next[attrName];\n return next;\n });\n }\n\n private captureRowLifecycleError(attrName: string, e: unknown): void {\n const error = e as HttpErrorResponse;\n const errors: ValidationError[] | undefined = error?.error?.result?.errors ?? error?.error?.errors;\n const resolved: ValidationError[] = error?.status === 400 && errors?.length\n ? errors\n : [{\n attributeName: '',\n errorMessage: { en: error?.error?.result?.error || error?.error?.error || error?.message || 'The server refused this change.' },\n ruleType: 'error',\n }];\n this.rowLifecycleErrors.update(prev => ({ ...prev, [attrName]: resolved }));\n }\n\n // Inline AsDetail methods\n async addInlineRow(attr: EntityAttributeDefinition): Promise<void> {\n const row = await this.buildNewRow(attr);\n if (row === null) return;\n\n const data = { ...this.formData() };\n const arr = [...(data[attr.name] || [])];\n arr.push(row);\n data[attr.name] = arr;\n this.formData.set(data);\n }\n\n // Array AsDetail methods\n async addArrayItem(attr: EntityAttributeDefinition): Promise<void> {\n const row = await this.buildNewRow(attr);\n if (row === null) return;\n\n this.editingAsDetailAttr.set(attr);\n this.editingArrayIndex.set(null);\n this.asDetailFormData.set(row);\n this.showAsDetailModal.set(true);\n }\n\n editArrayItem(attr: EntityAttributeDefinition, index: number): void {\n this.editingAsDetailAttr.set(attr);\n this.editingArrayIndex.set(index);\n const arr = this.formData()[attr.name] || [];\n this.asDetailFormData.set({ ...(arr[index] || {}) });\n this.showAsDetailModal.set(true);\n }\n\n async removeArrayItem(attr: EntityAttributeDefinition, index: number): Promise<void> {\n const current = this.formData()[attr.name] || [];\n if (!(await this.mayRemoveRow(attr, current[index]))) return;\n\n const data = { ...this.formData() };\n const arr = [...current];\n arr.splice(index, 1);\n data[attr.name] = arr;\n this.formData.set(data);\n }\n\n // Drag-reorder for [Sortable] AsDetail arrays. Order = array position, so moving the\n // row within formData()[attr.name] and re-emitting the signal IS the persisted order\n // (po-edit flags AsDetail attributes isValueChanged on save).\n onAsDetailReorder(attr: EntityAttributeDefinition, event: CdkDragDrop<Record<string, any>[]>): void {\n if (event.previousIndex === event.currentIndex) return;\n const data = { ...this.formData() };\n const arr = [...(data[attr.name] ?? [])];\n moveItemInArray(arr, event.previousIndex, event.currentIndex);\n data[attr.name] = arr;\n this.formData.set(data);\n }\n}\n","<bs-form>\n @if (entityType()) {\n <bs-grid>\n <bs-tab-control>\n @for (tab of resolvedTabs(); track tab.id) {\n <bs-tab-page>\n <ng-template bsTabPageHeader>{{ tab.label | resolveTranslation:tab.name }}</ng-template>\n <ng-container *ngTemplateOutlet=\"tabContent; context: { $implicit: tab }\"></ng-container>\n </bs-tab-page>\n }\n </bs-tab-control>\n\n <ng-template #tabContent let-tab>\n @if (tab.id === '__default__') {\n @if (ungroupedAttributes().length > 0) {\n <bs-card class=\"d-block m-3\">\n <div class=\"p-3\">\n @for (attr of ungroupedAttributes(); track attr.id) {\n <ng-container *ngTemplateOutlet=\"attrField; context: { $implicit: attr }\"></ng-container>\n }\n </div>\n </bs-card>\n }\n }\n @for (group of groupsForTab(tab); track group.id) {\n @if (attrsForGroup(group); as groupAttrs) {\n @if (groupAttrs.length > 0) {\n <bs-card class=\"d-block m-3\">\n @if (group.label) {\n <bs-card-header>{{ group.label | resolveTranslation:group.name }}</bs-card-header>\n }\n <div class=\"p-3\">\n @for (attr of groupAttrs; track attr.id) {\n <ng-container *ngTemplateOutlet=\"attrField; context: { $implicit: attr }\"></ng-container>\n }\n </div>\n </bs-card>\n }\n }\n }\n </ng-template>\n\n <ng-template #attrField let-attr>\n <div bsRow class=\"mb-3\">\n <label [md]=\"4\" bsColFormLabel [for]=\"attr.name\">\n {{ attr.label | resolveTranslation:attr.name }}\n @if (attr.isRequired) {\n <span class=\"text-danger\">*</span>\n }\n <spark-attribute-description [description]=\"attr.description\" />\n </label>\n <div [md]=\"8\">\n <!-- A lookup wins over the raw data type. `[LookupReference]` does not change dataType —\n only `[Reference]` does — so a lookup-backed bool still reports 'boolean' here. Tested\n first, this branch swallowed it into a two-state checkbox and the select below was\n unreachable, which made a declared lookup silently do nothing. -->\n @if (attr.dataType === 'boolean' && !attr.lookupReferenceType) {\n <bs-checkbox\n [ngModel]=\"formData()[attr.name]\"\n (ngModelChange)=\"formData()[attr.name] = $event; onFieldChange(attr)\">\n </bs-checkbox>\n } @else if (attr.lookupReferenceType) {\n @if ((attr | lookupDisplayType:lookupReferenceOptions()) === ELookupDisplayType.Modal) {\n <spark-lookup-picker\n [value]=\"formData()[attr.name]\"\n [options]=\"getLookupOptions(attr)\"\n [inputId]=\"attr.name\"\n [title]=\"attr.label | resolveTranslation:attr.name\"\n [isInvalid]=\"hasError(attr.name)\"\n (valueChange)=\"onLookupValueChange(attr, $event)\">\n </spark-lookup-picker>\n } @else {\n <bs-select\n class=\"w-100\"\n [ngModel]=\"formData()[attr.name]\"\n (ngModelChange)=\"formData()[attr.name] = $event; onFieldChange(attr)\"\n [id]=\"attr.name\"\n [class.is-invalid]=\"hasError(attr.name)\">\n <option [ngValue]=\"null\">{{ 'common.selectPlaceholder' | t }}</option>\n @for (option of (attr | lookupOptions:lookupReferenceOptions()); track option.key) {\n <option [ngValue]=\"option.key\">\n {{ option.values | resolveTranslation:option.key }}\n </option>\n }\n </bs-select>\n }\n } @else if (attr.dataType === 'Reference' && attr.isArray) {\n <bs-tree-select\n mode=\"multiple\"\n variant=\"textbox\"\n [showClear]=\"true\"\n [placeholder]=\"'common.search' | t\"\n [provider]=\"getReferenceProvider(attr)\"\n [ngModel]=\"referenceTreeValues()[attr.name] || []\"\n (ngModelChange)=\"onReferenceTreeChange(attr, $event)\"\n [ngModelOptions]=\"{ standalone: true }\">\n </bs-tree-select>\n } @else if (attr.dataType === 'Reference') {\n <spark-reference-picker\n [value]=\"formData()[attr.name]\"\n [options]=\"referenceOptions()[attr.name] || []\"\n [referenceType]=\"attr.referenceType\"\n [inputId]=\"attr.name\"\n [title]=\"attr.label | resolveTranslation:attr.name\"\n [isInvalid]=\"hasError(attr.name)\"\n (valueChange)=\"onReferenceValueChange(attr, $event)\">\n </spark-reference-picker>\n } @else if (attr.dataType === 'AsDetail' && attr.isArray && attr.editMode === 'inline') {\n <!-- One source of truth for an inline cell, reused by the row and by the drag\n preview. The preview is a live Angular view (not a cloneNode), so its\n <bs-select> binds to row[col.name] and shows the value while dragging. -->\n <ng-template #inlineDetailCell let-row=\"row\" let-col=\"col\" let-attr=\"attr\" let-rowIndex=\"rowIndex\" let-rowRenderer=\"rowRenderer\">\n <td>\n <!-- A row the caller may not edit renders through the same read-only span as a\n read-only column: still fully readable, just not editable. Hiding it would\n hide the data, and the save path would silently restore any edit anyway. -->\n @if (col.isReadOnly || !(attr | canEditDetailRow:asDetailPermissions())) {\n <span class=\"text-muted\">{{ row | asDetailCellValue:attr:col:asDetailReferenceOptions() }}</span>\n } @else if (getAsDetailCellEditRenderer(col); as editRenderer) {\n <ng-container *ngComponentOutlet=\"editRenderer; inputs: getAsDetailCellEditRendererInputs(editRenderer, row, col)\"></ng-container>\n } @else if (col.dataType === 'AsDetail') {\n <!-- Nested AsDetail can't flatten into a row: escalate to the row modal,\n where the recursive spark-po-form renders the nested structure. -->\n <div class=\"d-flex align-items-center gap-1\">\n <span class=\"text-truncate flex-grow-1\">{{ row | asDetailCellValue:attr:col:asDetailReferenceOptions() }}</span>\n <button type=\"button\" class=\"btn btn-sm btn-outline-secondary\" (click)=\"editArrayItem(attr, rowIndex)\">\n <spark-icon name=\"pencil\" />\n </button>\n </div>\n } @else if (col.dataType === 'boolean' && !col.lookupReferenceType) {\n <!-- Same precedence as the top-level chain above: a lookup-backed column must\n reach its select rather than being swallowed by the checkbox. -->\n <bs-checkbox\n [(ngModel)]=\"row[col.name]\"\n (ngModelChange)=\"onInlineCellChange(attr, rowIndex, col)\">\n </bs-checkbox>\n } @else if (col.dataType === 'Reference' && col.query) {\n @if (col.referenceDisplayType === EReferenceDisplayType.Modal) {\n <spark-reference-picker\n [value]=\"row[col.name]\"\n [options]=\"(attr | inlineRefOptions:col:asDetailReferenceOptions())\"\n [referenceType]=\"col.referenceType\"\n [title]=\"col.label | resolveTranslation:col.name\"\n [isInvalid]=\"hasInlineError(attr, rowIndex, col)\"\n (valueChange)=\"row[col.name] = $event; onInlineCellChange(attr, rowIndex, col)\">\n </spark-reference-picker>\n } @else {\n <bs-select\n class=\"w-100\"\n [(ngModel)]=\"row[col.name]\"\n (ngModelChange)=\"onInlineCellChange(attr, rowIndex, col)\"\n [class.is-invalid]=\"hasInlineError(attr, rowIndex, col)\">\n <option [ngValue]=\"null\">{{ 'common.selectPlaceholder' | t }}</option>\n @for (option of (attr | inlineRefOptions:col:asDetailReferenceOptions()); track option.id) {\n <option [ngValue]=\"option.id\">\n {{ option.breadcrumb || option.id }}\n </option>\n }\n </bs-select>\n }\n } @else if (col.lookupReferenceType) {\n @if ((col | lookupDisplayType:lookupReferenceOptions()) === ELookupDisplayType.Modal) {\n <spark-lookup-picker\n [value]=\"row[col.name]\"\n [options]=\"getLookupOptions(col)\"\n [title]=\"col.label | resolveTranslation:col.name\"\n [isInvalid]=\"hasInlineError(attr, rowIndex, col)\"\n (valueChange)=\"row[col.name] = $event; onInlineCellChange(attr, rowIndex, col)\">\n </spark-lookup-picker>\n } @else {\n <bs-select\n class=\"w-100\"\n [(ngModel)]=\"row[col.name]\"\n (ngModelChange)=\"onInlineCellChange(attr, rowIndex, col)\"\n [class.is-invalid]=\"hasInlineError(attr, rowIndex, col)\">\n <option [ngValue]=\"null\">{{ 'common.selectPlaceholder' | t }}</option>\n @for (option of getLookupOptions(col); track option.key) {\n <option [ngValue]=\"option.key\">\n {{ option.values | resolveTranslation:option.key }}\n </option>\n }\n </bs-select>\n }\n } @else if (col.dataType === 'MultiLineString') {\n <textarea\n [(ngModel)]=\"row[col.name]\"\n [required]=\"col.isRequired\"\n rows=\"3\"\n [class.is-invalid]=\"hasInlineError(attr, rowIndex, col)\"\n (ngModelChange)=\"onInlineCellChange(attr, rowIndex, col)\"\n (blur)=\"onInlineCellBlur(attr, rowIndex, col)\"></textarea>\n } @else {\n <input\n [type]=\"col.dataType | inputType\"\n [(ngModel)]=\"row[col.name]\"\n [required]=\"col.isRequired\"\n [step]=\"col.dataType === 'decimal' ? '0.01' : '1'\"\n [class.is-invalid]=\"hasInlineError(attr, rowIndex, col)\"\n (ngModelChange)=\"onInlineCellChange(attr, rowIndex, col)\"\n (blur)=\"onInlineCellBlur(attr, rowIndex, col)\">\n }\n @if (inlineErrorMessage(attr, rowIndex, col); as errorMsg) {\n <div class=\"invalid-feedback d-block\">{{ errorMsg }}</div>\n }\n <!-- Passed for the first cell only: a row renderer (rowComponent) draws its\n attributes once per row, under that cell, instead of as columns. -->\n @if (rowRenderer) {\n <div class=\"spark-row-renderer\">\n <ng-container *ngComponentOutlet=\"rowRenderer.component; inputs: getRowRendererInputs(rowRenderer, row)\"></ng-container>\n </div>\n }\n </td>\n </ng-template>\n <bs-table [isResponsive]=\"true\" class=\"mb-1\">\n <thead>\n <tr>\n @if (attr.isSortable) {\n <th style=\"width: 32px\"></th>\n }\n @for (col of (attr | asDetailColumns:asDetailTypes():rendererRegistry); track col.name) {\n <th>{{ col.label | resolveTranslation:col.name }}<spark-attribute-description [description]=\"col.description\" /></th>\n }\n <th style=\"width: 50px\"></th>\n </tr>\n </thead>\n @let inlineRowRenderer = (attr | asDetailRowRenderer:asDetailTypes():rendererRegistry);\n <tbody class=\"align-middle\" cdkDropList [cdkDropListDisabled]=\"!attr.isSortable\" (cdkDropListDropped)=\"onAsDetailReorder(attr, $event)\">\n @for (row of formData()[attr.name] || []; track $index; let rowIndex = $index) {\n <tr cdkDrag [cdkDragDisabled]=\"!attr.isSortable\">\n @if (attr.isSortable) {\n <td class=\"text-center text-muted\" style=\"cursor: move; touch-action: none\" cdkDragHandle>\n <spark-icon name=\"grip-vertical\" />\n </td>\n }\n @for (col of (attr | asDetailColumns:asDetailTypes():rendererRegistry); track col.name; let firstCol = $first) {\n <ng-container *ngTemplateOutlet=\"inlineDetailCell; context: { row: row, col: col, attr: attr, rowIndex: rowIndex, rowRenderer: firstCol ? inlineRowRenderer : null }\"></ng-container>\n }\n <td class=\"text-nowrap\">\n @if (attr | canDeleteDetailRow:asDetailPermissions()) {\n <button type=\"button\" [color]=\"colors.secondary\" (click)=\"removeArrayItem(attr, rowIndex)\">\n <spark-icon name=\"trash\" />\n </button>\n }\n </td>\n\n <!-- Floating preview = a LIVE view of the row's cells (not CDK's cloneNode),\n so <bs-select> shows its value instead of blanking. The in-grid\n placeholder still uses CDK's default clone (exact row height) and is\n hidden via .cdk-drag-placeholder { visibility:hidden } in the styles,\n so the drop gap is blank without collapsing/shifting the other rows. -->\n <ng-template cdkDragPreview>\n <table class=\"table mb-0 bg-body shadow-sm spark-drag-preview\">\n <tbody class=\"align-middle\">\n <tr>\n @if (attr.isSortable) {\n <td class=\"text-center text-muted\" style=\"width: 32px\"><spark-icon name=\"grip-vertical\" /></td>\n }\n @for (col of (attr | asDetailColumns:asDetailTypes():rendererRegistry); track col.name) {\n <ng-container *ngTemplateOutlet=\"inlineDetailCell; context: { row: row, col: col, attr: attr, rowIndex: rowIndex }\"></ng-container>\n }\n </tr>\n </tbody>\n </table>\n </ng-template>\n </tr>\n } @empty {\n <tr>\n <td [attr.colspan]=\"(attr | asDetailColumns:asDetailTypes():rendererRegistry).length + (attr.isSortable ? 2 : 1)\" class=\"text-center text-muted\">\n {{ 'common.noItemsFound' | t }}\n </td>\n </tr>\n }\n </tbody>\n </bs-table>\n @for (err of rowLifecycleErrorsFor(attr.name); track $index) {\n <div class=\"text-danger small mt-1\">{{ err.errorMessage | resolveTranslation }}</div>\n }\n @if (attr | canCreateDetailRow:asDetailPermissions()) {\n <button type=\"button\" [color]=\"colors.primary\" class=\"w-100 rounded-0\" (click)=\"addInlineRow(attr)\">\n <spark-icon name=\"plus\" /> {{ 'common.add' | t }}\n </button>\n }\n } @else if (attr.dataType === 'AsDetail' && attr.isArray) {\n <bs-table [isResponsive]=\"true\" class=\"mb-1\">\n <thead>\n <tr>\n @if (attr.isSortable) {\n <th style=\"width: 32px\"></th>\n }\n @for (col of (attr | asDetailColumns:asDetailTypes():rendererRegistry); track col.name) {\n <th>{{ col.label | resolveTranslation:col.name }}<spark-attribute-description [description]=\"col.description\" /></th>\n }\n <th style=\"width: 80px\"></th>\n </tr>\n </thead>\n <tbody class=\"align-middle\" cdkDropList [cdkDropListDisabled]=\"!attr.isSortable\" (cdkDropListDropped)=\"onAsDetailReorder(attr, $event)\">\n @let modalRowRenderer = (attr | asDetailRowRenderer:asDetailTypes():rendererRegistry);\n @for (row of formData()[attr.name] || []; track $index) {\n <tr cdkDrag [cdkDragDisabled]=\"!attr.isSortable\">\n @if (attr.isSortable) {\n <td class=\"text-center text-muted\" style=\"cursor: move; touch-action: none\" cdkDragHandle>\n <spark-icon name=\"grip-vertical\" />\n </td>\n }\n @for (col of (attr | asDetailColumns:asDetailTypes():rendererRegistry); track col.name; let firstCol = $first) {\n <td>\n @if (getAsDetailCellRendererComponent(col); as cellRenderer) {\n <ng-container *ngComponentOutlet=\"cellRenderer; inputs: getAsDetailCellRendererInputs(cellRenderer, row, col)\"></ng-container>\n } @else {\n {{ row | asDetailCellValue:attr:col:asDetailReferenceOptions() }}\n }\n @if (firstCol && modalRowRenderer) {\n <div class=\"spark-row-renderer\">\n <ng-container *ngComponentOutlet=\"modalRowRenderer.component; inputs: getRowRendererInputs(modalRowRenderer, row)\"></ng-container>\n </div>\n }\n </td>\n }\n <td class=\"text-nowrap\">\n @if (attr | canEditDetailRow:asDetailPermissions()) {\n <button type=\"button\" class=\"btn btn-sm btn-outline-secondary me-1\" (click)=\"editArrayItem(attr, $index)\">\n <spark-icon name=\"pencil\" />\n </button>\n }\n @if (attr | canDeleteDetailRow:asDetailPermissions()) {\n <button type=\"button\" class=\"btn btn-sm btn-outline-danger\" (click)=\"removeArrayItem(attr, $index)\">\n <spark-icon name=\"trash\" />\n </button>\n }\n </td>\n </tr>\n } @empty {\n <tr>\n <td [attr.colspan]=\"(attr | asDetailColumns:asDetailTypes():rendererRegistry).length + (attr.isSortable ? 2 : 1)\" class=\"text-center text-muted\">\n {{ 'common.noItemsFound' | t }}\n </td>\n </tr>\n }\n </tbody>\n </bs-table>\n @for (err of rowLifecycleErrorsFor(attr.name); track $index) {\n <div class=\"text-danger small mt-1\">{{ err.errorMessage | resolveTranslation }}</div>\n }\n @if (attr | canCreateDetailRow:asDetailPermissions()) {\n <button type=\"button\" [color]=\"colors.primary\" class=\"w-100 rounded-0\" (click)=\"addArrayItem(attr)\">\n <spark-icon name=\"plus\" /> {{ 'common.add' | t }}\n </button>\n }\n } @else if (attr.dataType === 'AsDetail') {\n <bs-input-group>\n <input\n type=\"text\"\n [id]=\"attr.name\"\n [value]=\"attr | asDetailDisplayValue:formData():asDetailTypes()\"\n readonly\n [class.is-invalid]=\"hasError(attr.name)\">\n <button\n type=\"button\"\n bsInputGroupBtn\n [color]=\"colors.secondary\"\n (click)=\"openAsDetailEditor(attr)\">\n <spark-icon name=\"pencil\" />\n </button>\n </bs-input-group>\n } @else if (getEditRendererComponent(attr); as editComp) {\n <ng-container *ngComponentOutlet=\"editComp; inputs: getEditRendererInputs(editComp, attr)\"></ng-container>\n } @else if (attr.dataType === 'MultiLineString') {\n <textarea\n [id]=\"attr.name\"\n [ngModel]=\"formData()[attr.name]\"\n (ngModelChange)=\"formData()[attr.name] = $event; onFieldChange(attr)\"\n (blur)=\"onFieldBlur(attr)\"\n [name]=\"attr.name\"\n [required]=\"attr.isRequired\"\n rows=\"6\"\n [class.is-invalid]=\"hasError(attr.name)\"></textarea>\n } @else {\n <input\n [type]=\"attr.dataType | inputType\"\n [id]=\"attr.name\"\n [ngModel]=\"formData()[attr.name]\"\n (ngModelChange)=\"formData()[attr.name] = $event; onFieldChange(attr)\"\n (blur)=\"onFieldBlur(attr)\"\n [name]=\"attr.name\"\n [required]=\"attr.isRequired\"\n [step]=\"attr.dataType === 'decimal' ? '0.01' : '1'\"\n [class.is-invalid]=\"hasError(attr.name)\">\n }\n @if (attr.name | errorForAttribute:validationErrors(); as errorMsg) {\n <div class=\"invalid-feedback d-block\">\n {{ errorMsg }}\n </div>\n }\n </div>\n </div>\n </ng-template>\n\n @if (showButtons()) {\n <div bsRow class=\"mt-4\">\n <div [md]=\"4\"></div>\n <div [md]=\"8\" class=\"d-flex justify-content-end gap-2\">\n <button type=\"button\" [color]=\"colors.secondary\" (click)=\"onCancel()\" [disabled]=\"isSaving()\">{{ 'common.cancel' | t }}</button>\n <button type=\"submit\" [color]=\"colors.primary\" [disabled]=\"isSaving()\" (click)=\"onSave()\">\n @if (isSaving()) {\n <bs-spinner class=\"me-1\" />\n }\n {{ 'common.save' | t }}\n </button>\n </div>\n </div>\n }\n </bs-grid>\n}\n\n<!-- Modal for editing AsDetail objects -->\n<bs-modal [isOpen]=\"showAsDetailModal()\" (isOpenChange)=\"!$event && closeAsDetailModal()\">\n <div *bsModal>\n <div bsModalHeader>\n <h5 class=\"modal-title\">{{ 'common.edit' | t }} {{ editingAsDetailAttr()?.label | resolveTranslation:editingAsDetailAttr()?.name }}</h5>\n </div>\n\n @if (editingAsDetailAttr(); as attr) {\n <div bsModalBody>\n <spark-po-form\n [entityType]=\"attr | asDetailType:asDetailTypes()\"\n [(formData)]=\"asDetailFormData\"\n [parentId]=\"parentId()\"\n [parentType]=\"parentType()\"\n [referenceParentId]=\"referenceParentId()\"\n [referenceParentType]=\"referenceParentType()\"\n [triggerPathPrefix]=\"attr.isArray ? null : attr.name\"\n (nestedTriggerRequested)=\"onEmbeddedTrigger(attr.name, $event)\"\n (nestedTriggerBlurred)=\"onEmbeddedTriggerBlur($event)\">\n </spark-po-form>\n </div>\n }\n\n <div bsModalFooter>\n <button type=\"button\" [color]=\"colors.secondary\" (click)=\"closeAsDetailModal()\">{{ 'common.cancel' | t }}</button>\n <button type=\"button\" [color]=\"colors.primary\" (click)=\"saveAsDetailObject()\">{{ 'common.save' | t }}</button>\n </div>\n </div>\n</bs-modal>\n</bs-form>\n"],"mappings":"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"}
|