@tinya-code/gas-admin-ui 0.0.0-stage → 0.1.0-alpha.1
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 +63 -2
- package/fesm2022/tinya-code-gas-admin-ui-api.service-CZrUeclM.mjs +281 -0
- package/fesm2022/tinya-code-gas-admin-ui-api.service-CZrUeclM.mjs.map +1 -0
- package/fesm2022/tinya-code-gas-admin-ui-badge-EeCA_l-o.mjs +84 -0
- package/fesm2022/tinya-code-gas-admin-ui-badge-EeCA_l-o.mjs.map +1 -0
- package/fesm2022/tinya-code-gas-admin-ui-bar-chart-G7sEMSBY.mjs +140 -0
- package/fesm2022/tinya-code-gas-admin-ui-bar-chart-G7sEMSBY.mjs.map +1 -0
- package/fesm2022/tinya-code-gas-admin-ui-boolean-B2ASKL0E.mjs +113 -0
- package/fesm2022/tinya-code-gas-admin-ui-boolean-B2ASKL0E.mjs.map +1 -0
- package/fesm2022/tinya-code-gas-admin-ui-button-BQjNW8KB.mjs +163 -0
- package/fesm2022/tinya-code-gas-admin-ui-button-BQjNW8KB.mjs.map +1 -0
- package/fesm2022/tinya-code-gas-admin-ui-calendar-view-r-RXDE37.mjs +1022 -0
- package/fesm2022/tinya-code-gas-admin-ui-calendar-view-r-RXDE37.mjs.map +1 -0
- package/fesm2022/tinya-code-gas-admin-ui-chart-line-BjdlKHoH.mjs +278 -0
- package/fesm2022/tinya-code-gas-admin-ui-chart-line-BjdlKHoH.mjs.map +1 -0
- package/fesm2022/tinya-code-gas-admin-ui-condition-evaluator-BBzWUuHl.mjs +101 -0
- package/fesm2022/tinya-code-gas-admin-ui-condition-evaluator-BBzWUuHl.mjs.map +1 -0
- package/fesm2022/tinya-code-gas-admin-ui-currency-BvuF3Z63.mjs +141 -0
- package/fesm2022/tinya-code-gas-admin-ui-currency-BvuF3Z63.mjs.map +1 -0
- package/fesm2022/tinya-code-gas-admin-ui-data-source.service-LD8AM3Rn.mjs +89 -0
- package/fesm2022/tinya-code-gas-admin-ui-data-source.service-LD8AM3Rn.mjs.map +1 -0
- package/fesm2022/tinya-code-gas-admin-ui-date-1SJGXZrd.mjs +107 -0
- package/fesm2022/tinya-code-gas-admin-ui-date-1SJGXZrd.mjs.map +1 -0
- package/fesm2022/tinya-code-gas-admin-ui-display-badge-fwk6sDU7.mjs +140 -0
- package/fesm2022/tinya-code-gas-admin-ui-display-badge-fwk6sDU7.mjs.map +1 -0
- package/fesm2022/tinya-code-gas-admin-ui-display-date-D7zLiDiG.mjs +119 -0
- package/fesm2022/tinya-code-gas-admin-ui-display-date-D7zLiDiG.mjs.map +1 -0
- package/fesm2022/tinya-code-gas-admin-ui-display-file-BiIhqvQC.mjs +125 -0
- package/fesm2022/tinya-code-gas-admin-ui-display-file-BiIhqvQC.mjs.map +1 -0
- package/fesm2022/tinya-code-gas-admin-ui-display-image-C1dhnZi1.mjs +152 -0
- package/fesm2022/tinya-code-gas-admin-ui-display-image-C1dhnZi1.mjs.map +1 -0
- package/fesm2022/tinya-code-gas-admin-ui-display-list-DOEuUbGe.mjs +211 -0
- package/fesm2022/tinya-code-gas-admin-ui-display-list-DOEuUbGe.mjs.map +1 -0
- package/fesm2022/tinya-code-gas-admin-ui-display-number-DDMzr4Hn.mjs +116 -0
- package/fesm2022/tinya-code-gas-admin-ui-display-number-DDMzr4Hn.mjs.map +1 -0
- package/fesm2022/tinya-code-gas-admin-ui-display-text-B8CdVNpv.mjs +111 -0
- package/fesm2022/tinya-code-gas-admin-ui-display-text-B8CdVNpv.mjs.map +1 -0
- package/fesm2022/tinya-code-gas-admin-ui-email-CT_O5ihA.mjs +107 -0
- package/fesm2022/tinya-code-gas-admin-ui-email-CT_O5ihA.mjs.map +1 -0
- package/fesm2022/tinya-code-gas-admin-ui-empty-state-BMc0QtoT.mjs +109 -0
- package/fesm2022/tinya-code-gas-admin-ui-empty-state-BMc0QtoT.mjs.map +1 -0
- package/fesm2022/tinya-code-gas-admin-ui-error-mapper.service-Ci0UpXtV.mjs +45 -0
- package/fesm2022/tinya-code-gas-admin-ui-error-mapper.service-Ci0UpXtV.mjs.map +1 -0
- package/fesm2022/tinya-code-gas-admin-ui-field-aria-BD1Wiggc.mjs +50 -0
- package/fesm2022/tinya-code-gas-admin-ui-field-aria-BD1Wiggc.mjs.map +1 -0
- package/fesm2022/tinya-code-gas-admin-ui-field-node-CIK6KNN_.mjs +24 -0
- package/fesm2022/tinya-code-gas-admin-ui-field-node-CIK6KNN_.mjs.map +1 -0
- package/fesm2022/tinya-code-gas-admin-ui-field-path-B4-FTBGZ.mjs +27 -0
- package/fesm2022/tinya-code-gas-admin-ui-field-path-B4-FTBGZ.mjs.map +1 -0
- package/fesm2022/tinya-code-gas-admin-ui-file-Dg2N3XQ2.mjs +408 -0
- package/fesm2022/tinya-code-gas-admin-ui-file-Dg2N3XQ2.mjs.map +1 -0
- package/fesm2022/tinya-code-gas-admin-ui-form-view-BwGupSbi.mjs +4514 -0
- package/fesm2022/tinya-code-gas-admin-ui-form-view-BwGupSbi.mjs.map +1 -0
- package/fesm2022/tinya-code-gas-admin-ui-format-badge-CK8ct5Wk.mjs +82 -0
- package/fesm2022/tinya-code-gas-admin-ui-format-badge-CK8ct5Wk.mjs.map +1 -0
- package/fesm2022/tinya-code-gas-admin-ui-format-boolean-ClzezZz-.mjs +105 -0
- package/fesm2022/tinya-code-gas-admin-ui-format-boolean-ClzezZz-.mjs.map +1 -0
- package/fesm2022/tinya-code-gas-admin-ui-format-currency-CVmniKml.mjs +65 -0
- package/fesm2022/tinya-code-gas-admin-ui-format-currency-CVmniKml.mjs.map +1 -0
- package/fesm2022/tinya-code-gas-admin-ui-format-date-Dr1YsDd_.mjs +65 -0
- package/fesm2022/tinya-code-gas-admin-ui-format-date-Dr1YsDd_.mjs.map +1 -0
- package/fesm2022/tinya-code-gas-admin-ui-format-number-CWCjpI4n.mjs +65 -0
- package/fesm2022/tinya-code-gas-admin-ui-format-number-CWCjpI4n.mjs.map +1 -0
- package/fesm2022/tinya-code-gas-admin-ui-format-text-DBOgJkH0.mjs +59 -0
- package/fesm2022/tinya-code-gas-admin-ui-format-text-DBOgJkH0.mjs.map +1 -0
- package/fesm2022/tinya-code-gas-admin-ui-format-thumbnail-PCRvxWKt.mjs +94 -0
- package/fesm2022/tinya-code-gas-admin-ui-format-thumbnail-PCRvxWKt.mjs.map +1 -0
- package/fesm2022/tinya-code-gas-admin-ui-gap-sorting-iTdy_DYG.mjs +72 -0
- package/fesm2022/tinya-code-gas-admin-ui-gap-sorting-iTdy_DYG.mjs.map +1 -0
- package/fesm2022/tinya-code-gas-admin-ui-group-h7-O8tt4.mjs +238 -0
- package/fesm2022/tinya-code-gas-admin-ui-group-h7-O8tt4.mjs.map +1 -0
- package/fesm2022/tinya-code-gas-admin-ui-image-Bjw9ImlS.mjs +416 -0
- package/fesm2022/tinya-code-gas-admin-ui-image-Bjw9ImlS.mjs.map +1 -0
- package/fesm2022/tinya-code-gas-admin-ui-kanban-view-CeGueI57.mjs +439 -0
- package/fesm2022/tinya-code-gas-admin-ui-kanban-view-CeGueI57.mjs.map +1 -0
- package/fesm2022/tinya-code-gas-admin-ui-key-value-nVdgg5at.mjs +513 -0
- package/fesm2022/tinya-code-gas-admin-ui-key-value-nVdgg5at.mjs.map +1 -0
- package/fesm2022/tinya-code-gas-admin-ui-list-CMzl9MkX.mjs +637 -0
- package/fesm2022/tinya-code-gas-admin-ui-list-CMzl9MkX.mjs.map +1 -0
- package/fesm2022/tinya-code-gas-admin-ui-metric-card-DnGMQL8l.mjs +127 -0
- package/fesm2022/tinya-code-gas-admin-ui-metric-card-DnGMQL8l.mjs.map +1 -0
- package/fesm2022/tinya-code-gas-admin-ui-multiselect-CuWwAfdY.mjs +251 -0
- package/fesm2022/tinya-code-gas-admin-ui-multiselect-CuWwAfdY.mjs.map +1 -0
- package/fesm2022/tinya-code-gas-admin-ui-notification.service-LjSULER6.mjs +66 -0
- package/fesm2022/tinya-code-gas-admin-ui-notification.service-LjSULER6.mjs.map +1 -0
- package/fesm2022/tinya-code-gas-admin-ui-number-JNr7glc8.mjs +114 -0
- package/fesm2022/tinya-code-gas-admin-ui-number-JNr7glc8.mjs.map +1 -0
- package/fesm2022/tinya-code-gas-admin-ui-phone-jR417hPf.mjs +116 -0
- package/fesm2022/tinya-code-gas-admin-ui-phone-jR417hPf.mjs.map +1 -0
- package/fesm2022/tinya-code-gas-admin-ui-quick-actions-DCQ6emmF.mjs +235 -0
- package/fesm2022/tinya-code-gas-admin-ui-quick-actions-DCQ6emmF.mjs.map +1 -0
- package/fesm2022/tinya-code-gas-admin-ui-radio-mode-DxvN07DY.mjs +8 -0
- package/fesm2022/tinya-code-gas-admin-ui-radio-mode-DxvN07DY.mjs.map +1 -0
- package/fesm2022/tinya-code-gas-admin-ui-readonly-text-BL7-4qie.mjs +124 -0
- package/fesm2022/tinya-code-gas-admin-ui-readonly-text-BL7-4qie.mjs.map +1 -0
- package/fesm2022/tinya-code-gas-admin-ui-record-list-BiZfnd_7.mjs +249 -0
- package/fesm2022/tinya-code-gas-admin-ui-record-list-BiZfnd_7.mjs.map +1 -0
- package/fesm2022/tinya-code-gas-admin-ui-relation-B3W3Ynsp.mjs +309 -0
- package/fesm2022/tinya-code-gas-admin-ui-relation-B3W3Ynsp.mjs.map +1 -0
- package/fesm2022/tinya-code-gas-admin-ui-relation-drawer.service-B0kT8EiW.mjs +39 -0
- package/fesm2022/tinya-code-gas-admin-ui-relation-drawer.service-B0kT8EiW.mjs.map +1 -0
- package/fesm2022/tinya-code-gas-admin-ui-render-registry-B_D5Jhp9.mjs +546 -0
- package/fesm2022/tinya-code-gas-admin-ui-render-registry-B_D5Jhp9.mjs.map +1 -0
- package/fesm2022/tinya-code-gas-admin-ui-rolldown-runtime-DpJrqANO.mjs +11 -0
- package/fesm2022/tinya-code-gas-admin-ui-schema-catalog-CDrz9pVN.mjs +131 -0
- package/fesm2022/tinya-code-gas-admin-ui-schema-catalog-CDrz9pVN.mjs.map +1 -0
- package/fesm2022/tinya-code-gas-admin-ui-seed-CNbQtREA.mjs +80 -0
- package/fesm2022/tinya-code-gas-admin-ui-seed-CNbQtREA.mjs.map +1 -0
- package/fesm2022/tinya-code-gas-admin-ui-select-DzaRfHXO.mjs +292 -0
- package/fesm2022/tinya-code-gas-admin-ui-select-DzaRfHXO.mjs.map +1 -0
- package/fesm2022/tinya-code-gas-admin-ui-skeleton-DnWK0tcZ.mjs +119 -0
- package/fesm2022/tinya-code-gas-admin-ui-skeleton-DnWK0tcZ.mjs.map +1 -0
- package/fesm2022/tinya-code-gas-admin-ui-slug-DU58cbiY.mjs +158 -0
- package/fesm2022/tinya-code-gas-admin-ui-slug-DU58cbiY.mjs.map +1 -0
- package/fesm2022/tinya-code-gas-admin-ui-source-options-B9R4mw8-.mjs +26 -0
- package/fesm2022/tinya-code-gas-admin-ui-source-options-B9R4mw8-.mjs.map +1 -0
- package/fesm2022/tinya-code-gas-admin-ui-status-progress-DmeG8htE.mjs +205 -0
- package/fesm2022/tinya-code-gas-admin-ui-status-progress-DmeG8htE.mjs.map +1 -0
- package/fesm2022/tinya-code-gas-admin-ui-string-list-gleasqzD.mjs +432 -0
- package/fesm2022/tinya-code-gas-admin-ui-string-list-gleasqzD.mjs.map +1 -0
- package/fesm2022/tinya-code-gas-admin-ui-template-interpolator-ewv7CkNX.mjs +36 -0
- package/fesm2022/tinya-code-gas-admin-ui-template-interpolator-ewv7CkNX.mjs.map +1 -0
- package/fesm2022/tinya-code-gas-admin-ui-text-CUPmL7W_.mjs +107 -0
- package/fesm2022/tinya-code-gas-admin-ui-text-CUPmL7W_.mjs.map +1 -0
- package/fesm2022/tinya-code-gas-admin-ui-textarea-CGLSIW13.mjs +223 -0
- package/fesm2022/tinya-code-gas-admin-ui-textarea-CGLSIW13.mjs.map +1 -0
- package/fesm2022/tinya-code-gas-admin-ui-time-BcHzsTWf.mjs +107 -0
- package/fesm2022/tinya-code-gas-admin-ui-time-BcHzsTWf.mjs.map +1 -0
- package/fesm2022/tinya-code-gas-admin-ui-upload.service-C1g545w5.mjs +60 -0
- package/fesm2022/tinya-code-gas-admin-ui-upload.service-C1g545w5.mjs.map +1 -0
- package/fesm2022/tinya-code-gas-admin-ui-url-GO4LZi__.mjs +141 -0
- package/fesm2022/tinya-code-gas-admin-ui-url-GO4LZi__.mjs.map +1 -0
- package/fesm2022/tinya-code-gas-admin-ui.mjs +4400 -0
- package/fesm2022/tinya-code-gas-admin-ui.mjs.map +1 -0
- package/package.json +31 -3
- package/types/tinya-code-gas-admin-ui.d.ts +3091 -0
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"tinya-code-gas-admin-ui-form-view-BwGupSbi.mjs","names":[],"sources":["../../../projects/gas-admin-ui/src/lib/fields/display/display-defaults.ts","../../../projects/gas-admin-ui/src/lib/fields/unknown-render/unknown-render.ts","../../../projects/gas-admin-ui/src/lib/core/registry/render-host.ts","../../../projects/gas-admin-ui/src/lib/core/utils/clipboard.ts","../../../projects/gas-admin-ui/src/lib/actions/descriptors.ts","../../../projects/gas-admin-ui/src/lib/actions/action-registry.ts","../../../projects/gas-admin-ui/src/lib/fields/field-host/field-host.ts","../../../projects/gas-admin-ui/src/lib/shared/utils/dialog-focus.ts","../../../projects/gas-admin-ui/src/lib/shared/components/drawer/drawer.ts","../../../projects/gas-admin-ui/src/lib/shared/utils/validators.ts","../../../projects/gas-admin-ui/src/lib/core/registry/field-validators.ts","../../../projects/gas-admin-ui/src/lib/pages/form-view/form-schema.ts","../../../projects/gas-admin-ui/src/lib/pages/form-view/form-model.ts","../../../projects/gas-admin-ui/src/lib/pages/drawer-form/drawer-form.ts","../../../projects/gas-admin-ui/src/lib/shared/utils/depends-on.ts","../../../projects/gas-admin-ui/src/lib/shared/components/copy-share/copy-share.ts","../../../projects/gas-admin-ui/src/lib/shared/components/modal/modal.ts","../../../projects/gas-admin-ui/src/lib/shared/components/post-create-modal/post-create-modal.ts","../../../projects/gas-admin-ui/src/lib/pages/form-view/form-view.ts"],"sourcesContent":["import type { Type } from '@angular/core';\n\nimport type { FieldType } from '../../core/models/schema.model';\n\n/** Carga perezosa del componente que pinta un valor en modo `display`. */\nexport type DisplayLoader = () => Promise<Type<any>>;\n\nconst TEXTO: DisplayLoader = () =>\n import('./display-text').then((module) => module.FieldDisplayText);\nconst FECHA: DisplayLoader = () =>\n import('./display-date').then((module) => module.FieldDisplayDate);\nconst NUMERO: DisplayLoader = () =>\n import('./display-number').then((module) => module.FieldDisplayNumber);\nconst BADGE: DisplayLoader = () =>\n import('./display-badge').then((module) => module.FieldDisplayBadge);\nconst IMAGEN: DisplayLoader = () =>\n import('./display-image').then((module) => module.FieldDisplayImage);\nconst ARCHIVO: DisplayLoader = () =>\n import('./display-file').then((module) => module.FieldDisplayFile);\nconst LISTA: DisplayLoader = () =>\n import('./display-list').then((module) => module.FieldDisplayList);\n\n/**\n * **Tabla de display por defecto según `field.type`** (F3 de\n * `doc/refactormotor.md`, R4.1 del plan): «cada descriptor declara si sabe\n * pintarse en `display`; si no, usa un `display` por defecto según tipo».\n *\n * La declaración vive en el descriptor (`surfaces: ['form', 'display']`) y\n * el componente sale de acá: un solo sitio decide qué pinta cada tipo en la\n * ficha de detalle. Es la misma familia de cambio que la tabla de formato\n * de columna de R4.4 — «declarada, no implícita».\n *\n * `Record<FieldType, …>` es el **tripwire de exhaustividad en compilación**:\n * un tipo nuevo obliga a elegir su display antes de que el build pase, y un\n * tipo retirado obliga a limpiar la entrada. En runtime el acceso pasa por\n * `displayLoader()`, que no lanza si el `type` viene de un schema mal\n * escrito.\n *\n * Paridad con el `formatValue` que vivía en `detail-view` (R4.2):\n * `text/textarea/slug/url/email/phone/readonly-text/relation/group/\n * key-value/string-list/multiselect/time` → texto crudo · `date` → fecha ·\n * `number/currency` → número · `boolean/select` → badge · `image` →\n * miniatura · `list` → timeline.\n */\nconst DISPLAY_POR_DEFECTO: Record<FieldType, DisplayLoader> = {\n text: TEXTO,\n textarea: TEXTO,\n slug: TEXTO,\n url: TEXTO,\n email: TEXTO,\n phone: TEXTO,\n 'readonly-text': TEXTO,\n relation: TEXTO,\n group: TEXTO,\n 'key-value': TEXTO,\n 'string-list': TEXTO,\n multiselect: TEXTO,\n time: TEXTO,\n date: FECHA,\n number: NUMERO,\n currency: NUMERO,\n boolean: BADGE,\n select: BADGE,\n image: IMAGEN,\n file: ARCHIVO,\n list: LISTA,\n};\n\n/**\n * Componente de display para un `type`, o `undefined` si no está en la\n * tabla (tipo fuera de la unión — p. ej. un schema con un `type` mal\n * escrito). Ese `undefined` es lo que el host traduce en la degradación\n * controlada de R1.10: en dev se avisa, en producción se omite.\n */\nexport function displayLoader(type: string): DisplayLoader | undefined {\n return Object.prototype.hasOwnProperty.call(DISPLAY_POR_DEFECTO, type)\n ? DISPLAY_POR_DEFECTO[type as FieldType]\n : undefined;\n}\n","import { Component, input } from '@angular/core';\n\nimport { LucideInfo } from '@lucide/angular';\n\n/**\n * Degradación controlada de un tipo de campo sin renderizador (R1.10).\n *\n * El `@switch` de `field-host` NO tiene `@default`: un `type` fuera de\n * `SUPPORTED_FIELD_TYPE_LIST` no revienta el template, simplemente no pinta\n * nada. Eso cumple «nunca pantalla rota», pero deja el formulario con un\n * hueco mudo donde el operador no sabe si le falta un campo o si lo escribió\n * mal.\n *\n * Este componente es el aviso de **desarrollo**: se renderiza sólo con\n * `isDevMode()`, nombra el tipo y el campo, y deja claro que el VALOR existe\n * en el schema y se conserva — no se pierde, sólo no se pinta. En producción\n * `field-host` lo omite y deja un `console.warn`.\n *\n * No dibuja control ni etiqueta asociada: no hay `id` al que apuntar un\n * `<label for>` (mismo criterio que el bloque de `composite()` en el host).\n */\n@Component({\n selector: 'app-unknown-render',\n imports: [LucideInfo],\n template: `\n <div\n class=\"mb-4 rounded-lg border border-amber-400/50 bg-amber-500/10 px-3 py-2\"\n data-field-unknown\n >\n <p class=\"flex items-center gap-1.5 text-xs font-medium text-amber-600\">\n <svg lucideInfo size=\"14\" class=\"shrink-0\" aria-hidden=\"true\" />\n Campo sin renderizador en esta versión\n </p>\n <p class=\"mt-1 text-help text-neutral\">\n Tipo <code class=\"rounded bg-amber-500/20 px-1 font-mono\">{{ type() }}</code>\n @if (label()) {\n del campo «{{ label() }}»\n }\n — el valor existe en el schema y se conserva; sólo no se dibuja.\n </p>\n </div>\n `,\n})\nexport class UnknownRender {\n /** `type` declarado en el schema y fuera de `SUPPORTED_FIELD_TYPE_LIST`. */\n readonly type = input.required<string>();\n /** Rótulo del campo, para que el aviso sea ubicable en el formulario. */\n readonly label = input('');\n}\n","import { NgComponentOutlet } from '@angular/common';\nimport {\n Component,\n computed,\n effect,\n forwardRef,\n inject,\n InjectionToken,\n input,\n isDevMode,\n PendingTasks,\n signal,\n viewChild,\n type TemplateRef,\n type Type,\n} from '@angular/core';\nimport { FormField } from '@angular/forms/signals';\nimport { LucideCircleAlert } from '@lucide/angular';\n\nimport { FIELD_ARIA, type FieldAriaContext } from '../../fields/field-aria';\nimport {\n childTree,\n groupTree,\n type ListChildContext,\n type RootTree,\n} from '../../fields/field-node';\nimport { FieldFile } from '../../fields/file/file';\nimport { FieldImage } from '../../fields/image/image';\nimport { FieldMultiselect } from '../../fields/multiselect/multiselect';\nimport { esGrupoRadio } from '../../fields/select/radio-mode';\nimport { displayLoader } from '../../fields/display/display-defaults';\nimport { UnknownRender } from '../../fields/unknown-render/unknown-render';\nimport type {\n FieldSchema,\n FileField,\n GroupField,\n ImageField,\n ListField,\n MultiselectField,\n SelectField,\n WidgetSchema,\n} from '../models/schema.model';\nimport { Skeleton } from '../../shared/components/skeleton/skeleton';\nimport { colSpanClass } from '../../shared/utils/col-span';\nimport { getSchema } from './schema-catalog';\nimport {\n get,\n isSupported,\n RENDER_CONTEXT_VACIO,\n resolve,\n type RenderConfig,\n type RenderContext,\n type RenderExtras,\n type ComponentRenderKind,\n} from './render-registry';\n\n/**\n * El contexto vive en el registro (R3.6): el contrato de `RenderDescriptor.\n * inputs` necesita nombrarlo. Se re-exporta acá para que `field-host` y sus\n * tests no cambien de domicilio.\n */\nexport type { RenderContext } from './render-registry';\n\n/** Token con el que los controles inyectan el `RenderContext` del host. */\nexport const RENDER_CONTEXT = new InjectionToken<RenderContext>('RENDER_CONTEXT');\n\n/**\n * Host genérico de render (R3.4).\n *\n * Sustituye el `@switch` de 21 ramas de `FieldHost` por una resolución contra\n * `core/registry/render-registry.ts`: el tipo llega por `[type]`, el registro\n * devuelve el componente y `NgComponentOutlet` lo monta. Añadir un tipo deja\n * de tocar este archivo.\n *\n * Conserva el envoltorio que `FieldHost` dibujaba — `<label>`, marca\n * required/opcional, `data-field`, `colSpanClass`, mensaje de error y ayuda —\n * porque eso es lo que el operador ve y no depende del tipo de control.\n *\n * **Sin imports de los 20 controles**: sólo llegan por `load()` perezoso del\n * descriptor. Mientras el `import()` resuelve se pinta `Skeleton`, y un tipo\n * fuera del catálogo degrada a `UnknownRender` en dev (nunca pantalla rota).\n *\n * **Modo `display` (R4.1 · R4.2).** El mismo host pinta la ficha de detalle:\n * con `mode=\"display\"` no hay árbol, ni etiqueta, ni error — sólo el valor\n * crudo que llega por `[value]` y el componente que elige la tabla\n * `fields/display/display-defaults.ts`. El descriptor declara la superficie\n * (`surfaces: ['form', 'display']`) y, si no la declara o el tipo no está en\n * la tabla, corre la misma degradación controlada de R1.10.\n */\n@Component({\n selector: 'app-render-host',\n imports: [\n FieldFile,\n FieldImage,\n FieldMultiselect,\n FormField,\n LucideCircleAlert,\n NgComponentOutlet,\n RenderHost,\n Skeleton,\n UnknownRender,\n ],\n // El envoltorio de render es quien conoce la ayuda y el error —ambos viven\n // en SU plantilla—, así que de aquí sale el contexto de accesibilidad y el\n // de render, igual que salían de `FieldHost`.\n providers: [\n { provide: FIELD_ARIA, useExisting: forwardRef(() => RenderHost) },\n { provide: RENDER_CONTEXT, useExisting: forwardRef(() => RenderHost) },\n ],\n template: `\n <ng-template #groupContent>\n @for (child of group().fields; track child.key) {\n <div [class]=\"colSpan(child.width)\">\n <app-render-host\n [kind]=\"kind()\"\n [type]=\"child.type\"\n [config]=\"child\"\n [ctx]=\"groupChildCtx()\"\n />\n </div>\n }\n </ng-template>\n\n <ng-template #childTpl let-child>\n @for (sub of child.fields; track sub.key) {\n <div [class]=\"child.row ? 'min-w-0 grow' : colSpan(sub.width)\">\n <app-render-host\n [kind]=\"kind()\"\n [type]=\"sub.type\"\n [config]=\"sub\"\n [ctx]=\"listChildCtx(child)\"\n />\n </div>\n }\n </ng-template>\n\n @if (esDisplay()) {\n <!-- Rama de display (R4.1 · R4.2): sólo el valor crudo. El host no\n lee el árbol, no dibuja etiqueta ni error y no conoce la\n sección: detail-view agrupa y este bloque pinta. -->\n @if (vacio()) {\n <span aria-hidden=\"true\">—</span>\n } @else if (soportaDisplay()) {\n @if (control()) {\n <ng-container\n [ngComponentOutlet]=\"control()\"\n [ngComponentOutletInputs]=\"displayInputs()\"\n />\n } @else {\n <app-skeleton [lines]=\"1\" [height]=\"20\" />\n }\n } @else if (showUnknown()) {\n <app-unknown-render [type]=\"type()\" [label]=\"config().label\" />\n }\n } @else if (supported()) {\n @if (esCampo()) {\n <div class=\"mb-4\" [class.field-enter]=\"cfgCampo().visibleWhen\" [attr.data-field]=\"name()\">\n @if (type() !== 'group') {\n @if (composite()) {\n <!-- Sin control único no existe ancla para el atributo for del\n label: apuntaría a un id inexistente. Se dibuja como texto\n (en listas/key-value los hijos reciben su etiqueta por\n campo anidado; en un select en modo radio cada opción\n trae su label implícito y el grupo va con aria-label). -->\n <div class=\"mb-1 text-label font-medium\">\n {{ config().label }}\n @if (showRequiredMark()) {\n <span aria-hidden=\"true\" class=\"text-danger\">*</span>\n }\n @if (showOptionalMark()) {\n <span class=\"font-normal text-neutral\">(opcional)</span>\n }\n </div>\n } @else {\n <label [for]=\"name()\" class=\"mb-1 block text-label font-medium\">\n {{ config().label }}\n @if (showRequiredMark()) {\n <span aria-hidden=\"true\" class=\"text-danger\">*</span>\n }\n @if (showOptionalMark()) {\n <span class=\"font-normal text-neutral\">(opcional)</span>\n }\n </label>\n }\n }\n\n @if (type() === 'multiselect') {\n <!-- Tres de los veintiuno no pueden pasar por \"NgComponentOutlet\": su\n sincronización con Signal Forms vive en ɵɵcontrol, una\n instrucción que el compilador emite únicamente junto al binding\n formField de la propia plantilla. Sin ese binding escrito en el\n HTML el model() de value nunca se inicializa y el control lanza\n NG0952. Se declaran a mano (D27) y el resto de tipos siguen\n saliendo del registro. -->\n <app-field-multiselect\n [id]=\"name()\"\n [field]=\"multiselectCfg()\"\n [formField]=\"multiselectNode()\"\n [tree]=\"ctx().tree\"\n />\n } @else if (type() === 'image') {\n <app-field-image\n [id]=\"name()\"\n [field]=\"imageCfg()\"\n [formField]=\"imageNode()\"\n [resource]=\"ctx().resource\"\n />\n } @else if (type() === 'file') {\n <app-field-file\n [id]=\"name()\"\n [field]=\"fileCfg()\"\n [formField]=\"fileNode()\"\n [resource]=\"ctx().resource\"\n />\n } @else {\n @if (control()) {\n <ng-container\n [ngComponentOutlet]=\"control()\"\n [ngComponentOutletInputs]=\"controlInputs()\"\n />\n } @else {\n <app-skeleton [lines]=\"2\" [height]=\"32\" />\n }\n }\n\n @if (errorMessage(); as message) {\n <p\n [id]=\"errorId()\"\n class=\"mt-1 flex items-center gap-1 text-help text-danger\"\n role=\"alert\"\n >\n <svg lucideCircleAlert size=\"14\" class=\"shrink-0\" />\n {{ message }}\n </p>\n } @else if (cfgCampo().help; as help) {\n <p [id]=\"helpId()\" class=\"mt-1 text-help text-neutral\">{{ help }}</p>\n }\n </div>\n } @else {\n <!-- Rama de widget (R3.6): el mismo outlet y el mismo cableado que\n un campo, pero sin el envoltorio — un widget no tiene etiqueta\n asociada, ni marca required, ni error, ni ayuda. El ancho en la\n grilla lo pone su host (app-widget-host), no el motor. -->\n @if (control()) {\n <ng-container\n [ngComponentOutlet]=\"control()\"\n [ngComponentOutletInputs]=\"controlInputs()\"\n />\n } @else {\n <app-skeleton [lines]=\"2\" [height]=\"32\" />\n }\n }\n } @else if (showUnknown()) {\n <!-- R1.10: degradación controlada. Un tipo fuera del catálogo no\n revienta; en dev se avisa y en producción sólo se omite. -->\n <app-unknown-render [type]=\"type()\" [label]=\"config().label\" />\n }\n `,\n})\nexport class RenderHost implements FieldAriaContext, RenderContext {\n /** Categoría de render. Resuelve campos y widgets (R3.6). */\n readonly kind = input<ComponentRenderKind>('field');\n /** Identificador del tipo dentro de su `kind`. */\n readonly type = input.required<string>();\n /**\n * Configuración del descriptor: el `FieldSchema` del campo, el\n * `WidgetSchema` del widget o la `ListColumn` de la columna (R4.3).\n */\n readonly config = input.required<RenderConfig>();\n /**\n * Contexto de render del nivel de este campo.\n *\n * Con valor por defecto y no requerido: los widgets no pertenecen a un\n * formulario y llegan sin árbol (R3.6). El default es un contexto vacío\n * que nunca se lee en esa rama — un campo montado sin contexto real ya\n * era un error antes y `childTree` lo sigue siendo.\n */\n readonly ctx = input<RenderContext>(RENDER_CONTEXT_VACIO);\n /** Payload del widget (`app-widget-host [data]`). `null` en campos. */\n readonly data = input<unknown>(null);\n /**\n * Superficie de render (R4.1).\n *\n * `'form'` (por defecto) es el formulario de siempre: árbol, etiqueta,\n * marca required, error y ayuda. `'display'` es la ficha de detalle:\n * valor crudo, sin envoltorio — es el `mode=\"display\"` con el que\n * `detail-view` deja de dibujar sus propias celdas (R4.2).\n */\n readonly mode = input<'form' | 'display'>('form');\n /**\n * Valor crudo del registro, **sólo para el modo `display`**.\n *\n * En el formulario el valor vive en el árbol de Signal Forms y este\n * input se ignora: un campo de detalle no tiene árbol que consultar.\n */\n readonly value = input<unknown>(null);\n /**\n * Presentación en chip, **sólo para el modo `display`** (R4.2).\n *\n * La cabecera de la ficha siempre mostró `headerFields` como etiquetas\n * compactas, aunque el valor fuera texto plano. Sin este matiz el host\n * pintaría «ORD-42» como texto suelto y la cabecera cambiaría de cara en\n * los 7 detalles. El aviso va en el contrato de entrada de los display —\n * uniforme para los 6 — y sólo lo aprovechan los que dibujan texto: los\n * que ya son chip (`badge`) o no son un valor escalar (`image`, `list`)\n * lo ignoran a propósito.\n */\n readonly chip = input(false);\n\n private readonly dev = isDevMode();\n\n /** Mantiene `whenStable()` bloqueado mientras el descriptor carga su chunk. */\n private readonly pendientes = inject(PendingTasks);\n\n /** `true` cuando se dibuja un campo, `false` para cualquier otro `kind`. */\n protected readonly esCampo = computed(() => this.kind() === 'field');\n\n /** `true` en la rama de detalle (R4.1): sin árbol, etiqueta ni error. */\n protected readonly esDisplay = computed(() => this.mode() === 'display');\n\n /**\n * ¿El descriptor declara la superficie `display`? (R4.1)\n *\n * Es la respuesta a «cada descriptor de campo declara si sabe pintarse en\n * `display`»: sin esa declaración el host no pinta el valor en la ficha,\n * y `contract.spec.ts` vigila que los 20 la traigan.\n */\n private readonly declaraDisplay = computed(\n () => get(this.kind(), this.type())?.surfaces?.includes('display') ?? false,\n );\n\n /**\n * Componente que pinta este tipo en `display`.\n *\n * El descriptor sólo dice **si** sabe pintarse; **qué** componente usa lo\n * decide la tabla por tipo (`fields/display/display-defaults.ts`), que es\n * la que garantiza que el mismo `field.type` se pinte igual en todas las\n * fichas.\n */\n private readonly loaderDisplay = computed(() =>\n this.declaraDisplay() ? displayLoader(this.type()) : undefined,\n );\n\n /** El modo display tiene componente para este tipo. */\n protected readonly soportaDisplay = computed(() => this.loaderDisplay() !== undefined);\n\n /**\n * Valor vacío, con el mismo criterio que `formatValue` de `detail-view`\n * antes de R4.2 (`null`, `undefined` o `''`): se pinta un `—` y el\n * componente ni se monta. `false`, `0` y `[]` **no** están vacíos.\n */\n protected readonly vacio = computed(() => {\n const valor = this.value();\n return valor === null || valor === undefined || valor === '';\n });\n\n /** Configuración leída como campo. Sólo válida en la rama de campo. */\n private readonly cfgCampo = computed(() => this.config() as FieldSchema);\n\n private readonly node = computed(() => childTree<unknown>(this.ctx().tree, this.cfgCampo().key));\n\n protected readonly state = computed(() => this.node()());\n\n /** Nombre del control: ancla del `<label for>` y base de `*-help`/`*-error`. */\n protected readonly name = computed(() => this.state().name());\n\n /**\n * Fuera del catálogo o oculto por `visibleWhen`: no se dibuja.\n *\n * El `isSupported` va primero y corta antes de leer `state()`: un tipo\n * desconocido se siembra como `undefined`, Signal Forms no le crea nodo y\n * `childTree` lanzaría «El formulario no contiene el campo».\n *\n * El `esCampo()` corta lo mismo para los widgets: no hay árbol que ocultar\n * ni nodo que consultar, así que el widget se dibuja si está en el\n * catálogo y ya.\n */\n protected readonly supported = computed(() => {\n if (!isSupported(this.kind(), this.type())) {\n return false;\n }\n return this.esCampo() ? !this.state().hidden() : true;\n });\n\n /**\n * Sin renderizador para este tipo **en la superficie actual**.\n *\n * En `display`: el descriptor no declara la superficie o el tipo no está\n * en la tabla de display. En `form`: sigue siendo «fuera del catálogo».\n * Ninguna de las dos ramas toca `state()`, que en display no existe.\n */\n private readonly unsupported = computed(() =>\n this.esDisplay() ? !this.soportaDisplay() : !isSupported(this.kind(), this.type()),\n );\n\n protected readonly showUnknown = computed(() => this.unsupported() && this.dev);\n\n private readonly _trazaTipoDesconocido = effect(() => {\n if (!this.unsupported() || this.dev) return;\n const superficie = this.esDisplay() ? 'display' : 'form';\n console.warn(\n `[render-host] tipo sin renderizador (${superficie}): \"${this.type()}\" — se omite el control.`,\n );\n });\n\n // ── Resolución del componente ────────────────────────────────────────────\n\n /** Componente montado, o `null` mientras el `load()` perezoso resuelve. */\n private readonly control = signal<Type<unknown> | null>(null);\n\n /** Descarta cargas obsoletas cuando el tipo cambia antes de que resuelva. */\n private carga = 0;\n\n private readonly _cargar = effect(() => {\n const type = this.type();\n const carga = ++this.carga;\n this.control.set(null);\n\n // Modo display (R4.1): el componente sale de la tabla por tipo, no del\n // `load()` del descriptor — la tabla es la que garantiza que un mismo\n // `field.type` se pinte igual en todas las fichas.\n if (this.esDisplay()) {\n const loader = this.loaderDisplay();\n if (!loader) return;\n this.pendientes.run(async () => {\n const componente = await loader();\n if (this.carga === carga) this.control.set(componente);\n });\n return;\n }\n\n if (!isSupported(this.kind(), type)) return;\n // El `load()` del descriptor es un `import()` de chunk, que `whenStable()`\n // NO espera por sí solo (queda fuera del ciclo de estabilidad y el control\n // llegaría tarde a un `detectChanges()` posterior). Registrándolo como\n // tarea pendiente la estabilidad cubre la carga: el skeleton se pinta\n // mientras baja y el control aparece en el siguiente ciclo ya montado.\n this.pendientes.run(async () => {\n const componente = await resolve(this.kind(), type, UnknownRender);\n if (this.carga === carga) this.control.set(componente);\n });\n });\n\n /**\n * Inputs del componente de display.\n *\n * Forma fija y uniforme para los 6 componentes de\n * `fields/display/`: `field` (el `FieldSchema` completo, para los que\n * necesitan `options`), `value` (el valor crudo) y `chip` (la\n * presentación de la cabecera). El descriptor no interviene acá — en\n * display no hay ni control ni árbol que cablear.\n */\n private readonly displayInputs = computed<Record<string, unknown>>(() => ({\n field: this.config() as FieldSchema,\n value: this.value(),\n chip: this.chip(),\n }));\n\n /**\n * Inputs del componente montado.\n *\n * El motor no sabe qué recibe cada tipo: pregunta al descriptor (`inputs`)\n * y sólo aplica la forma estándar del `kind` cuando el descriptor no la\n * sobreescribe — `{field, tree}` en campos, `{widget, data}` en widgets.\n * Ésa es la pieza que hace que «añadir un tipo» no toque este archivo\n * (R3.8). `multiselect` e `image` no pasan por aquí: se declaran a mano\n * por D27.\n */\n private readonly controlInputs = computed<Record<string, unknown>>(() => {\n const cfg = this.config();\n const extras: RenderExtras = {\n ctx: this.ctx(),\n data: this.data(),\n groupContent: this.groupTpl(),\n childTemplate: this.childTpl(),\n };\n\n const descriptor = get(this.kind(), this.type());\n if (descriptor?.inputs) {\n return descriptor.inputs(cfg, extras);\n }\n\n return this.esCampo()\n ? { field: cfg as FieldSchema, tree: this.ctx().tree }\n : { widget: cfg, data: this.data() };\n });\n\n // ── Recursión de los tipos compuestos ────────────────────────────────────\n\n protected readonly group = computed(() => this.config() as GroupField);\n protected readonly list = computed(() => this.config() as ListField);\n\n private readonly groupTpl = viewChild<TemplateRef<unknown>>('groupContent');\n private readonly childTpl = viewChild<TemplateRef<unknown>>('childTpl');\n\n /** Subárbol y prefijo que heredan los hijos del grupo. */\n protected readonly groupChildCtx = computed<RenderContext>(() => ({\n ...this.ctx(),\n tree: groupTree(this.ctx().tree, this.config().key),\n pathPrefix: `${this.ctx().pathPrefix}${this.config().key}.`,\n }));\n\n protected listChildCtx(child: ListChildContext): RenderContext {\n return {\n tree: child.tree,\n serverErrors: child.errors,\n pathPrefix: child.prefix,\n exists: child.exists,\n resource: this.ctx().resource,\n };\n }\n\n /** `sm:col-span-*` compartido con el grupo (literales: Tailwind v4 es JIT). */\n colSpan(width?: number): string {\n return colSpanClass(width);\n }\n\n // ── Envoltorio: label, marcas y estado ───────────────────────────────────\n\n /**\n * Los tres `FormValueControl` se declaran en la plantilla (D27), así que\n * su campo necesita el tipo estrecho que exige cada input — el mismo\n * estrechado que hacía `FieldHost`.\n */\n protected readonly multiselectCfg = computed(() => this.config() as MultiselectField);\n\n protected readonly imageCfg = computed(() => this.config() as ImageField);\n\n protected readonly fileCfg = computed(() => this.config() as FileField);\n\n private readonly multiselectNode = computed(() =>\n childTree<string[]>(this.ctx().tree, this.config().key),\n );\n\n private readonly imageNode = computed(() =>\n childTree<string>(this.ctx().tree, this.config().key),\n );\n\n private readonly fileNode = computed(() => childTree<string>(this.ctx().tree, this.config().key));\n\n /**\n * Error visible: primero el del backend (si existe para esta ruta) y\n * después el propio, solo cuando el campo ya se tocó (validación al\n * perder el foco).\n */\n protected readonly errorMessage = computed<string | null>(() => {\n const serverError = this.ctx().serverErrors[`${this.ctx().pathPrefix}${this.config().key}`];\n if (serverError) {\n return serverError;\n }\n const state = this.state();\n if (!state.invalid() || !state.touched()) {\n return null;\n }\n return state.errors()[0]?.message ?? 'Revisa este campo.';\n });\n\n /**\n * Compuestos: varios controles internos, ninguno único. Para ellos\n * `<label for>` no tendría ancla, porque ningún elemento lleva el id del\n * campo padre (los hijos tienen los suyos propios).\n */\n protected readonly composite = computed(() => {\n const cfg = this.cfgCampo();\n if (new Set(['list', 'key-value', 'string-list', 'multiselect']).has(cfg.type)) {\n return true;\n }\n // Un select en modo radio (2–4 opciones estáticas) tampoco tiene ancla\n // única: el grupo se nombra con `aria-label`, así que su rótulo va como\n // texto. El cálculo vive en `radio-mode.ts` y lo comparte con el campo\n // para que las dos ramas nunca se desincronicen.\n return cfg.type === 'select' && esGrupoRadio(cfg as SelectField);\n });\n\n // Contexto de accesibilidad consumido por `FieldAria` en los controles.\n\n /**\n * Ancla de la ayuda, sólo mientras no hay error: §2 dice que el error\n * **reemplaza** la ayuda, de modo que el id no puede apuntar a un nodo\n * que ya no está en el DOM.\n */\n readonly helpId = computed(() =>\n this.cfgCampo().help && !this.errorMessage() ? `${this.name()}-help` : null,\n );\n\n readonly errorId = computed(() => (this.errorMessage() ? `${this.name()}-error` : null));\n\n readonly describedBy = computed(\n () => [this.helpId(), this.errorId()].filter(Boolean).join(' ') || null,\n );\n\n /** Mismo criterio que `errorMessage()` — el estado del control no es público. */\n readonly invalid = computed(() => this.errorMessage() !== null);\n\n readonly required = computed(() => this.cfgCampo().required ?? false);\n\n // §2: \"si casi todos son obligatorios, marcar los opcionales con\n // «(opcional)»\" en vez de sembrar `*` por todo el formulario.\n\n private readonly schema = computed(() => getSchema(this.ctx().resource));\n\n /**\n * Se decide una vez por recurso y se aplica a todos los niveles: es un\n * juicio de diseño sobre el formulario, no sobre un campo suelto. Contado\n * por nivel (sólo top-level) — un conteo plano mezcla los subcampos de\n * `group`/`list` y da ratios imposibles.\n */\n protected readonly markOptionals = computed(() => {\n const fields = this.schema()?.fields ?? [];\n if (fields.length === 0) {\n return false;\n }\n const required = fields.filter((field) => field.required).length;\n return required / fields.length >= 0.75;\n });\n\n protected readonly showRequiredMark = computed(\n () => !this.markOptionals() && this.cfgCampo().required,\n );\n\n protected readonly showOptionalMark = computed(\n () => this.markOptionals() && !this.cfgCampo().required,\n );\n\n // ── RenderContext (por DI) ───────────────────────────────────────────────\n\n get tree(): RootTree {\n return this.ctx().tree;\n }\n\n get serverErrors(): Record<string, string> {\n return this.ctx().serverErrors;\n }\n\n get pathPrefix(): string {\n return this.ctx().pathPrefix;\n }\n\n get exists(): boolean {\n return this.ctx().exists;\n }\n\n get resource(): string {\n return this.ctx().resource;\n }\n}\n","/**\n * Mensaje completo a copiar o compartir.\n *\n * `shareTextTemplate` es el MENSAJE FORMATEADO por el schema (el que trae\n * saludo, ref, PIN o el enlace ya escrito dentro), mientras que `urlTemplate`\n * es sólo el enlace. Los tres caminos —copiar, compartir nativo y WhatsApp—\n * deben ofrecer el mismo texto: antes `copy()` y la acción del listado sólo\n * escribían `url`, así que el operador se quedaba sin el mensaje.\n *\n * Tres casos, sin perder información y sin duplicar el enlace:\n * - sin `shareText` → sólo la URL\n * - `shareText` con el sitio ya mencionado → el mensaje tal cual\n * - `shareText` sin el sitio → mensaje + espacio + URL\n *\n * \"Sitio\" se decide por ORIGIN, no por la URL exacta: la plantilla puede\n * referenciar sólo la base (`{public_base_url}` → `https://ejemplo.com`)\n * o la URL completa con el path (`…/p/{ref}`); en ambos casos el mensaje\n * YA apunta al sitio y no se le pega nada al final. Antes se comparaba\n * contra la URL completa, así que un mensaje con la base recibía la URL\n * con código duplicada al final.\n *\n * @param url Enlace resuelto (`urlTemplate`).\n * @param shareText Mensaje formateado opcional (`shareTextTemplate`).\n */\nexport function buildShareMessage(url: string, shareText?: string): string {\n const text = (shareText ?? '').trim();\n if (!text) return url;\n if (url && mentionsSite(text, url)) return text;\n return url ? `${text} ${url}` : text;\n}\n\n/** ¿El mensaje ya apunta al sitio de `url` (origin o URL exacta)? */\nfunction mentionsSite(text: string, url: string): boolean {\n if (text.includes(url)) return true;\n try {\n const { origin } = new URL(url);\n return origin !== 'null' && text.includes(origin);\n } catch {\n // `url` relativa (`/p/123`): sin origin que comparar, sólo la URL exacta.\n return false;\n }\n}\n\n/**\n * Escribe en el portapapeles sin reventar: primero la API moderna, después\n * el `textarea` + `execCommand` (obsoleto, pero sigue siendo el fallback\n * que existe en contextos no seguros).\n *\n * Único punto del admin que sabe cómo copiar: `copy-share`, las acciones de\n * fila del listado y cualquier widget futuro comparten este camino, en vez\n * de reimplementar cada uno sus propios fallbacks.\n *\n * @returns `true` si el texto quedó copiado por alguna de las vías.\n */\nexport async function writeClipboard(text: string): Promise<boolean> {\n const clipboard = typeof navigator !== 'undefined' ? navigator.clipboard : undefined;\n if (clipboard && typeof clipboard.writeText === 'function') {\n try {\n await clipboard.writeText(text);\n return true;\n } catch {\n // Permiso denegado o contexto no seguro: sigue al fallback legado.\n }\n }\n return legacyCopy(text);\n}\n\nfunction legacyCopy(text: string): boolean {\n if (typeof document === 'undefined' || typeof document.execCommand !== 'function') {\n return false;\n }\n const area = document.createElement('textarea');\n area.value = text;\n area.setAttribute('readonly', '');\n area.style.position = 'fixed';\n area.style.top = '0';\n area.style.opacity = '0';\n document.body.appendChild(area);\n area.select();\n\n let copied = false;\n try {\n copied = document.execCommand('copy');\n } catch {\n copied = false;\n }\n\n document.body.removeChild(area);\n return copied;\n}\n","import { firstValueFrom } from 'rxjs';\nimport { LucideShare2 } from '@lucide/angular';\n\nimport type { ActionDescriptor } from './action-registry';\nimport type {\n CopyShareAction,\n NavigateAction,\n ResourceAction,\n TriggerAction,\n} from '../core/models/schema.model';\nimport { buildShareMessage, writeClipboard } from '../core/utils/clipboard';\nimport { interpolateTemplate } from '../core/utils/template-interpolator';\n\n/**\n * Descriptores de las tres acciones que hoy declara el modelo\n * (R3.10 · `doc/plan-refactor-motor.md` §3.2).\n *\n * Éste es el fichero que **cierra la triplicación de `copy-share`**: la\n * resolución de enlace y de mensaje vivía en `list-view.runAction`,\n * `detail-view.resolveUrl/resolveShareText` y\n * `post-create-modal.resolvedShareUrl/resolvedShareText`. Aquí se resuelve\n * una sola vez (`resolveShare`) y las vistas consumen ese resultado en R3.11.\n *\n * Se registra al importar, desde `action-registry.ts`, con el mismo criterio\n * que el registro de render: sin esperar a DI, porque el registro es la única\n * fuente de verdad en runtime.\n */\n\n/** Enlace y mensaje ya interpolados, listos para copiar o compartir. */\nexport interface ResolvedShare {\n url: string;\n shareText: string;\n}\n\n/**\n * `true` cuando la acción es de las que las vistas presentan como botón de\n * compartir (`detail-view`, `post-create-modal`).\n *\n * Vive acá y no en la vista porque es **el único sitio donde se puede nombrar\n * `'copy-share'`**: fuera de `descriptors.ts` ninguna vista decide por tipo\n * (criterio de R3.11).\n */\nexport function isShareAction(action: ResourceAction): action is CopyShareAction {\n return action.type === 'copy-share';\n}\n\n/**\n * `true` cuando la acción es un `trigger` (acción de dominio del back).\n *\n * Mismo criterio que `isShareAction`: el único sitio que nombra el tipo,\n * para que las vistas no decidan por string literal (R3.11). `detail-view`\n * lo usa en R8.8 para renderizar «Regenerar PIN» junto a los botones de\n * compartir.\n */\nexport function isTriggerAction(action: ResourceAction): action is TriggerAction {\n return action.type === 'trigger';\n}\n\n/**\n * Resuelve `urlTemplate` y el mensaje de compartir contra `vars`\n * (config global + registro).\n *\n * **Única implementación** de esta lógica en el admin. El fallback a\n * `share_message_template` (la plantilla que declara el singleton de\n * configuración) lo aplican los tres puntos por igual: hasta ahora la fila\n * del listado no lo honraba y se quedaba sin saludo.\n */\nexport function resolveShare(\n action: CopyShareAction,\n vars: Record<string, unknown>,\n): ResolvedShare {\n const template =\n action.shareTextTemplate || (vars['share_message_template'] as string | undefined);\n return {\n url: interpolateTemplate(action.urlTemplate, vars),\n shareText: template ? interpolateTemplate(template, vars) : '',\n };\n}\n\n/** `copy-share` — copia (o comparte) el mensaje completo del enlace público. */\nconst copyShare: ActionDescriptor = {\n type: 'copy-share',\n surfaces: ['row', 'detail', 'post-create'],\n // Icono de la fila del listado: la vista sólo decide entre «icono» y\n // «rótulo», nunca por tipo. Se pasa la CLASE, no el nombre: sin\n // `provideLucideIcons` no hay riesgo de `Unable to resolve icon`.\n icon: LucideShare2,\n run: async (ctx, action) => {\n const { url, shareText } = resolveShare(action as CopyShareAction, ctx.vars);\n const message = buildShareMessage(url, shareText);\n const copied = await writeClipboard(message);\n if (copied) {\n ctx.toast.success('Mensaje copiado al portapapeles.');\n } else {\n ctx.toast.info(`Copiar manualmente: ${message}`);\n }\n return { ok: true };\n },\n};\n\n/**\n * `navigate` — abre la plantilla resuelta: URL absoluta en otra pestaña,\n * ruta interna en el router. `NavigateAction.type` es opcional, así que el\n * servicio llega acá también con una acción que no declara `type`.\n */\nconst navigate: ActionDescriptor = {\n type: 'navigate',\n surfaces: ['row', 'detail'],\n run: async (ctx, action) => {\n const url = interpolateTemplate((action as NavigateAction).urlTemplate, ctx.vars);\n if (/^https?:\\/\\//.test(url)) {\n window.open(url, '_blank', 'noopener');\n } else {\n await ctx.router.navigateByUrl(url);\n }\n return { ok: true };\n },\n};\n\n/**\n * `trigger` — dispara una **acción de dominio declarada en el back** (B3).\n *\n * **R3.12:** el destino ya no lo declara el schema (`endpoint` y `method`\n * murieron en este cambio). Se deriva de `resource` + clave del registro +\n * `name` (o `id`), que es exactamente la ruta que el back publica solo en\n * R5.4: `POST /admin/<recurso>/:id/actions/<name>`.\n *\n * ⚠️ **O2:** la ruta lógica viaja en el `path` del envelope, no en la URL —\n * el HTTP sigue siendo el `POST API_URL` de siempre.\n *\n * Siempre pide confirmación: es la única acción de las tres que puede dejar\n * una cola de trabajo hecha.\n */\nconst trigger: ActionDescriptor = {\n type: 'trigger',\n surfaces: ['row', 'detail'],\n confirm: true,\n run: async (ctx, action) => {\n const declared = action as TriggerAction;\n const rawKey = ctx.record?.[ctx.keyField];\n if (rawKey === null || rawKey === undefined || rawKey === '') {\n throw new Error(\n `La acción «${declared.label}» necesita la clave ${ctx.keyField} del registro.`,\n );\n }\n const name = declared.name ?? declared.id;\n const route = `/admin/${ctx.resource}/${String(rawKey)}/actions/${name}`;\n await firstValueFrom(ctx.api.request('POST', route));\n return { ok: true, reload: true };\n },\n};\n\n/** Catálogo — una línea por acción, en el orden en que se registran. */\nexport const actionDescriptors: readonly ActionDescriptor[] = [copyShare, navigate, trigger];\n","import type { Router } from '@angular/router';\n\nimport type { LucideIcon } from '@lucide/angular';\n\nimport {\n actionDescriptors,\n isShareAction,\n isTriggerAction,\n resolveShare,\n type ResolvedShare,\n} from './descriptors';\nimport type { ResourceAction } from '../core/models/schema.model';\nimport type { SessionUser } from '../core/services/auth.service';\nimport type { ApiService } from '../core/services/api.service';\nimport type { NotificationService } from '../core/services/notification.service';\n\n/**\n * Registro único de acciones (R3.9 · `refactormotor.md`).\n *\n * Es el gemelo del registro de render (`core/registry/render-registry.ts`):\n * en lugar de «qué componente pinta un tipo de campo», responde «qué\n * comportamiento ejecuta un tipo de acción» y permite **migrar el dispatch\n * if/copiado-pegado de las vistas (R3.11) a datos + un solo `run`**.\n *\n * Como los descriptores son objetos planos de módulo (no los inyecta Angular),\n * todo lo que necesiten llegar por el `ActionContext`: los puertos (`api`,\n * `router`, `toast`) y los datos (`record`, `resource`, `vars`).\n */\n\n/** Superficies donde una acción puede aparecer. */\nexport type ActionSurface = 'row' | 'detail' | 'header' | 'bulk' | 'post-create';\n\n/**\n * Lo que aporta el llamador al ejecutar una acción: el registro sobre el que\n * corre y los puertos que el descriptor usará para actuar.\n *\n * `record` es `null` en las superficies sin fila (`header`, `bulk`), para que\n * un descriptor pueda distinguir «sin registro» de «registro vacío».\n */\nexport interface ActionRequest {\n record: Record<string, unknown> | null;\n /** Id del recurso (schema) dueño de la acción. */\n resource: string;\n api: ApiService;\n router: Router;\n toast: NotificationService;\n user: SessionUser | null;\n /**\n * `keyField` del schema. **R3.12 (desviación nº 3 del contrato):** con\n * `record` forma la clave que necesita la ruta lógica de `trigger`\n * (`/admin/<recurso>/<key>/actions/<name>`). Lo aporta la vista, que es\n * quien tiene el schema.\n */\n keyField: string;\n}\n\n/**\n * Contexto que recibe el descriptor: la petición del llamador más el contexto\n * de interpolación ya resuelto. `ActionService.run` lo arma, así que ninguna\n * vista repite `buildInterpolationContext(record)`.\n */\nexport interface ActionContext extends ActionRequest {\n /** `{...configGlobal, ...record, public_base_url}` — listo para las plantillas. */\n vars: Record<string, unknown>;\n}\n\n/** Resultado que la vista puede leer para saber si debe recargar. */\nexport interface ActionResult {\n /** `false` cuando la acción no llegó a ejecutarse; el error ya se notificó. */\n ok: boolean;\n /** `true` cuando tocó datos y la vista debe recargar su listado/detalle. */\n reload?: boolean;\n}\n\nexport interface ActionDescriptor {\n /** Clave del dispatch: el `type` del schema (`'navigate'` no lo declara). */\n type: string;\n /** Superficies donde esta acción puede aparecer. */\n surfaces: ActionSurface[];\n run: (ctx: ActionContext, action: ResourceAction) => Promise<ActionResult>;\n /** `true` si el descriptor exige confirmación antes de ejecutar. */\n confirm?: boolean;\n /**\n * Icono lucide que representa la acción donde el espacio es reducido\n * (la fila del listado). Sin él, la vista muestra `action.label`.\n */\n icon?: LucideIcon;\n}\n\nexport { isShareAction, isTriggerAction, resolveShare, type ResolvedShare };\n\nconst ACCIONES = new Map<string, ActionDescriptor>();\n\n/** Registra (o reemplaza) un descriptor de acción. */\nexport function registerAction(descriptor: ActionDescriptor): void {\n ACCIONES.set(descriptor.type, descriptor);\n}\n\nexport function getAction(type: string): ActionDescriptor | undefined {\n return ACCIONES.get(type);\n}\n\n/** Descriptores disponibles en una superficie. */\nexport function actionsFor(surface: ActionSurface): ActionDescriptor[] {\n return [...ACCIONES.values()].filter((descriptor) => descriptor.surfaces.includes(surface));\n}\n\n/**\n * Clave de dispatch de una acción. `NavigateAction.type` es **opcional**\n * (schema.model.ts), así que una acción sin `type` es una navegación.\n */\nexport function actionType(action: ResourceAction): string {\n return action.type ?? 'navigate';\n}\n\n/** Tipos registrados. Sólo para comprobaciones y tests. */\nexport function actionTypes(): string[] {\n return [...ACCIONES.keys()];\n}\n\n// Catálogo a scope de módulo: se registra al importar, sin esperar a DI,\n// igual que el registro de render (mismo motivo: `buildFormSchema()` es una\n// función pura que consulta sin inyectar nada).\nfor (const descriptor of actionDescriptors) {\n registerAction(descriptor);\n}\n","import { Component, computed, input } from '@angular/core';\n\nimport type { FieldSchema } from '../../core/models/schema.model';\nimport { RenderHost, type RenderContext } from '../../core/registry/render-host';\nimport type { RootTree } from '../field-node';\n\n/**\n * Entrada estable de los formularios (base.md §5).\n *\n * **R3.5 · ya no despacha.** Este host redujo sus 493 líneas —el `@switch`\n * de 20 ramas, los 21 imports a mano y el envoltorio de label/errores— a un\n * adaptador que traduce su API histórica (`field` + `tree` + `pathPrefix` …)\n * al `RenderContext` que espera `<app-render-host>`, el motor único de\n * render (F1 · `refactormotor.md`).\n *\n * El rótulo, las marcas required/opcional, el error, la ayuda,\n * `FIELD_ARIA` y la **recursión de `group`/`list`** (los `ng-template` con\n * `ngTemplateOutlet` y `ListChildContext`) viven ahora en `RenderHost`, que\n * se auto-importa para instanciar sus propios hijos.\n *\n * Rutas del backend: `pathPrefix` encadena el prefijo del contenedor\n * (`address.`, `faq[2].`) y `serverErrors` lleva el mapa completo; el error\n * propio de este campo se resuelve como `serverErrors[prefix + key]`.\n */\n@Component({\n selector: 'app-field-host',\n imports: [RenderHost],\n template: `\n <app-render-host kind=\"field\" [type]=\"field().type\" [config]=\"field()\" [ctx]=\"ctx()\" />\n `,\n})\nexport class FieldHost {\n readonly field = input.required<FieldSchema>();\n readonly tree = input.required<RootTree>();\n readonly exists = input(false);\n readonly serverErrors = input<Record<string, string>>({});\n readonly pathPrefix = input('');\n readonly resource = input('');\n\n protected readonly ctx = computed<RenderContext>(() => ({\n tree: this.tree(),\n serverErrors: this.serverErrors(),\n pathPrefix: this.pathPrefix(),\n exists: this.exists(),\n resource: this.resource(),\n }));\n}\n","/**\n * Utilidades de foco para los diálogos (`modal` y `drawer`).\n *\n * Los dos componentes comparten el mismo contrato: atrapan el Tab dentro del\n * panel para que el foco no se pierda detrás del fondo oscuro. Tener el\n * selector y el test de visibilidad en un solo sitio evita que los dos\n * diálogos diverjan.\n */\n\n/** Candidatos a foco dentro del diálogo (el orden del foco del navegador). */\nexport const FOCUSABLE =\n 'a[href], button:not([disabled]), input:not([disabled]), select:not([disabled]), textarea:not([disabled]), [tabindex]:not([tabindex=\"-1\"])';\n\n/**\n * ¿Se puede enfocar este elemento ahora mismo?\n *\n * No se usa `offsetParent`: en jsdom es siempre `null` y el atrapado del Tab\n * quedaría sin opérculo en los tests. `checkVisibility` da la respuesta\n * exacta en navegador; si no existe (jsdom), se confía en lo que hay en el\n * DOM y sólo se descarta lo marcado `hidden`.\n */\nexport function isVisibleNow(element: HTMLElement): boolean {\n if (element.hasAttribute('hidden') || element.closest('[hidden]') !== null) {\n return false;\n }\n const check = (element as { checkVisibility?: (options?: unknown) => boolean }).checkVisibility;\n return typeof check !== 'function' || check.call(element);\n}\n","import {\n Component,\n ElementRef,\n OnDestroy,\n afterRenderEffect,\n computed,\n input,\n output,\n viewChild,\n} from '@angular/core';\nimport { LucideX } from '@lucide/angular';\n\nimport { FOCUSABLE, isVisibleNow } from '../../utils/dialog-focus';\n\n/** Ancho del panel: `sm` para 1–3 campos, `md`/`lg` para formularios. */\nexport type DrawerSize = 'sm' | 'md' | 'lg';\n\nconst WIDTHS: Record<DrawerSize, string> = {\n sm: 'max-w-sm',\n md: 'max-w-lg',\n lg: 'max-w-2xl',\n};\n\nlet titleCounter = 0;\n\n/**\n * Panel lateral (drawer) — guía §5: «edición rápida de pocos campos: panel\n * lateral en lugar de pantalla nueva, para no perder contexto».\n *\n * Sigue el mismo contrato que `modal`: `role=\"dialog\"` con nombre por\n * `aria-labelledby`, foco al abrirse y restaurado al cerrarse, Escape y el\n * fondo cierran. Además ATRAPA el Tab dentro del panel: sin eso un modal se\n * puede tabular hacia la página de atrás y el foco se pierde detrás del\n * fondo oscuro.\n *\n * Componente tonto: `open`/`title` entran por `input()` y el estado vive\n * fuera. El cuerpo se proyecta en el área con scroll y el pie con el\n * atributo `drawer-actions`.\n */\n@Component({\n selector: 'app-drawer',\n imports: [LucideX],\n host: {\n '(document:keydown.escape)': 'onEscape()',\n // Angular arma la tecla CON sus modificadores y exige igualdad exacta:\n // sin esta segunda entrada, Shift+Tab llega como `shift.tab`, no casa con\n // `tab` y el foco escapa del panel hacia atrás.\n '(document:keydown.tab)': 'onTab($event)',\n '(document:keydown.shift.tab)': 'onTab($event)',\n },\n template: `\n @if (open()) {\n <div class=\"fixed inset-0 z-50\">\n <div class=\"absolute inset-0 bg-black/50\" aria-hidden=\"true\" (click)=\"close.emit()\"></div>\n <section\n #dialog\n role=\"dialog\"\n aria-modal=\"true\"\n [attr.aria-labelledby]=\"titleId\"\n tabindex=\"-1\"\n class=\"absolute inset-y-0 right-0 flex w-full flex-col bg-surface-raised shadow-2xl focus:outline-none\"\n [class]=\"widthClass()\"\n >\n <header\n class=\"flex items-start justify-between gap-4 border-b border-neutral/20 px-5 py-4\"\n >\n <div>\n <h2 [id]=\"titleId\" class=\"font-display text-section font-semibold\">{{ title() }}</h2>\n @if (subtitle(); as subtitleText) {\n <p class=\"mt-1 text-sm text-neutral/70\">{{ subtitleText }}</p>\n }\n </div>\n <button\n type=\"button\"\n class=\"flex min-h-11 min-w-11 items-center justify-center rounded text-neutral hover:bg-neutral/10 focus-visible:outline-2 focus-visible:outline-primary lg:min-h-0 lg:min-w-0\"\n [attr.aria-label]=\"closeLabel()\"\n (click)=\"close.emit()\"\n >\n <svg lucideX size=\"18\" />\n </button>\n </header>\n\n <div class=\"flex-1 overflow-y-auto px-5 py-4\">\n <ng-content />\n </div>\n\n <div class=\"border-t border-neutral/20 px-5 py-4\">\n <ng-content select=\"[drawer-actions]\" />\n </div>\n </section>\n </div>\n }\n `,\n})\nexport class Drawer implements OnDestroy {\n readonly open = input(false);\n readonly title = input('Editar');\n readonly subtitle = input<string | null>(null);\n readonly size = input<DrawerSize>('md');\n readonly closeLabel = input('Cerrar');\n readonly close = output<void>();\n\n readonly titleId = `drawer-title-${++titleCounter}`;\n readonly widthClass = computed(() => WIDTHS[this.size()]);\n\n private readonly dialogRef = viewChild<ElementRef<HTMLElement>>('dialog');\n private lastFocused: HTMLElement | null = null;\n\n private readonly focusEffect = afterRenderEffect(() => {\n const open = this.open();\n const dialog = this.dialogRef();\n if (open && dialog) {\n const active = document.activeElement;\n if (active !== dialog.nativeElement && this.lastFocused === null) {\n this.lastFocused = active instanceof HTMLElement ? active : null;\n }\n dialog.nativeElement.focus();\n } else if (!open && this.lastFocused !== null) {\n this.lastFocused.focus();\n this.lastFocused = null;\n }\n });\n\n /**\n * El consumidor puede destruir el componente con el panel todavía abierto\n * (se monta bajo un `@if`): en ese caso el effect nunca ve la transición a\n * `false` y el foco se quedaría perdido en el body.\n */\n ngOnDestroy(): void {\n if (this.lastFocused?.isConnected) {\n this.lastFocused.focus();\n }\n this.lastFocused = null;\n }\n\n onEscape(): void {\n if (this.open()) {\n this.close.emit();\n }\n }\n\n /**\n * Mantiene el Tab dentro del panel mientras el diálogo está abierto.\n * Recibe `Event` porque los host listeners de Angular tipean `$event` así;\n * el teclado se relee acá dentro.\n */\n onTab(rawEvent: Event): void {\n const event = rawEvent as KeyboardEvent;\n const dialog = this.dialogRef()?.nativeElement;\n if (!this.open() || !dialog) {\n return;\n }\n const focusable = Array.from(dialog.querySelectorAll<HTMLElement>(FOCUSABLE)).filter(\n (element) => isVisibleNow(element),\n );\n if (focusable.length === 0) {\n event.preventDefault();\n dialog.focus();\n return;\n }\n const first = focusable[0];\n const last = focusable[focusable.length - 1];\n const active = document.activeElement;\n if (event.shiftKey && (active === first || active === dialog)) {\n event.preventDefault();\n last.focus();\n } else if (!event.shiftKey && active === last) {\n event.preventDefault();\n first.focus();\n }\n }\n}\n","/**\n * Validadores puros sobre valores (base.md §4): longitud por palabras.\n * Devuelven `null` cuando el valor es válido o cuando no aplica.\n * (Longitud por caracteres, patrón y rango los aplica `@angular/forms/signals`\n * vía `form-schema.ts`: `minLengthRule`/`maxLengthRule`/`patternRule`/`minRule`.)\n *\n * Reglas comunes:\n * - Valor vacío (`undefined` / `null` / `''`) → `null`: la obligatoriedad\n * la maneja `required` aparte. Solo espacios NO cuenta como vacío.\n * - Valores de otro tipo → `null` (no validan aquí; los cubre el campo).\n *\n * Los mensajes se muestran tal cual bajo el campo (Fase 10), en español.\n */\n\nexport function validateMinWords(value: unknown, min: number): string | null {\n const words = wordCount(value);\n if (words === null) {\n return null;\n }\n return words >= min ? null : `Debe tener al menos ${min} palabras.`;\n}\n\nexport function validateMaxWords(value: unknown, max: number): string | null {\n const words = wordCount(value);\n if (words === null) {\n return null;\n }\n return words <= max ? null : `Como máximo ${max} palabras.`;\n}\n\nfunction isEmpty(value: unknown): boolean {\n return value === undefined || value === null || value === '';\n}\n\n/** Conteo de palabras; `null` si el valor no es texto o está vacío. */\nfunction wordCount(value: unknown): number | null {\n if (isEmpty(value) || typeof value !== 'string') {\n return null;\n }\n return value.trim().split(/\\s+/).filter(Boolean).length;\n}\n","/**\n * Registro de validadores del formulario (Plan 2 · F7 + B11 · R4.6).\n *\n * F7: «los validadores y reglas del formulario viven en un registro». Un\n * validador es UNA entrada: `declares` dice si el campo lo pide (tipo + props\n * del schema) y `register` ata la regla de Signal Forms a su ruta.\n * `buildFormSchema` sólo recorre el registro — añadir un validador no toca\n * `form-schema.ts`, que queda con lo que NO es validador: el ciclo de vida\n * (visible/readonly/dependencias) y la estructura (hijos de `group`, ítems\n * de `list`).\n *\n * B11: «mismos nombres que el back». `ValidatorName` es el vocabulario\n * compartido con `api/engine/23-validate.js` (tabla `VALIDATORS_`): el\n * contract check (`scripts/contract-check.mjs`) lo compara con esa tabla y\n * un nombre de un lado sin su entrada del otro es ROJO. Así el front y el\n * back hablan del mismo validador aunque cada uno pinte su mensaje.\n *\n * Los MENSAJES son los del front (más amables que los del back): lo que se\n * comparte es el nombre, el código `VALIDATION_*` y el significado.\n *\n * Para añadir un validador: una entrada acá + su nombre en `ValidatorName` +\n * su tabla en el back. Nada más.\n */\nimport { resource } from '@angular/core';\nimport {\n email as emailRule,\n max as maxRule,\n maxLength as maxLengthRule,\n min as minRule,\n minLength as minLengthRule,\n pattern as patternRule,\n required as requiredRule,\n validate,\n validateAsync,\n type PathKind,\n type SchemaPath,\n type SchemaPathRules,\n} from '@angular/forms/signals';\nimport { firstValueFrom } from 'rxjs';\n\nimport { validateMaxWords, validateMinWords } from '../../shared/utils/validators';\nimport type { FieldSchema, ListOptions, ResourceSchema } from '../models/schema.model';\nimport type { ApiService } from '../services/api.service';\n\n/** Ruta de un campo en el árbol de Signal Forms. Los casts viven acá. */\nexport type FieldPath<V> = SchemaPath<V, SchemaPathRules.Supported, PathKind.Child>;\n\n/**\n * Vocabulario compartido con el back (`VALIDATORS_` en 23-validate.js).\n *\n * El orden es el de registro de `FIELD_VALIDATORS`, que fija el orden de los\n * errores en `field.errors()`: el primero es el que pinta el campo.\n */\nexport type ValidatorName =\n | 'required'\n | 'email'\n | 'url'\n | 'min-length'\n | 'max-length'\n | 'pattern'\n | 'min-words'\n | 'max-words'\n | 'min'\n | 'max'\n | 'min-items'\n | 'max-items'\n | 'date-min'\n | 'date-max'\n | 'unique';\n\n/** Todo lo que un validador necesita para decidir y para registrarse. */\nexport interface ValidatorContext {\n path: FieldPath<unknown>;\n field: FieldSchema;\n schema: ResourceSchema;\n api: ApiService;\n excludeKey: string | undefined;\n}\n\nexport interface ValidatorEntry {\n readonly name: ValidatorName;\n /** ¿Este campo declara este validador? (tipo + props del schema) */\n readonly declares: (ctx: ValidatorContext) => boolean;\n /** Ata la regla de Signal Forms a `ctx.path`. */\n readonly register: (ctx: ValidatorContext) => void;\n}\n\n// ── Tipos ───────────────────────────────────────────────────────────────────\n\n/** Tipos con valor de texto: son los que pueden llevar reglas de string. */\nconst TEXT_TYPES: ReadonlySet<string> = new Set([\n 'text',\n 'textarea',\n 'email',\n 'url',\n 'slug',\n 'phone',\n 'time',\n 'readonly-text',\n 'select',\n 'relation',\n]);\n\n/** Tipos que son un arreglo con conmin: `required` se vuelve `min-items`. */\nconst LIST_TYPES: ReadonlySet<string> = new Set(['list', 'string-list', 'key-value']);\n\n// ── Resolución de límites (los mismos `??` de siempre, en un solo lugar) ────\n\nfunction minLengthOf(field: FieldSchema): number | undefined {\n return TEXT_TYPES.has(field.type) ? field.validators?.minLength : undefined;\n}\n\n/** `validators` tiene prioridad; `text` además declara `maxLength` propio. */\nfunction maxLengthOf(field: FieldSchema): number | undefined {\n if (!TEXT_TYPES.has(field.type)) return undefined;\n if (field.type === 'text') return field.validators?.maxLength ?? field.maxLength;\n return field.validators?.maxLength;\n}\n\nfunction patternOf(field: FieldSchema): string | undefined {\n if (!TEXT_TYPES.has(field.type)) return undefined;\n if (field.type === 'text') return field.validators?.pattern ?? field.pattern;\n return field.validators?.pattern;\n}\n\n/** `textarea` trae `minWords`/`maxWords` propios; el resto, sólo `validators`. */\nfunction minWordsOf(field: FieldSchema): number | undefined {\n if (!TEXT_TYPES.has(field.type)) return undefined;\n if (field.type === 'textarea') return field.validators?.minWords ?? field.minWords;\n return field.validators?.minWords;\n}\n\nfunction maxWordsOf(field: FieldSchema): number | undefined {\n if (!TEXT_TYPES.has(field.type)) return undefined;\n if (field.type === 'textarea') return field.validators?.maxWords ?? field.maxWords;\n return field.validators?.maxWords;\n}\n\n/** `number` declara `min`/`max` propio; `currency` sólo en `validators`. */\nfunction numberBoundOf(field: FieldSchema, bound: 'min' | 'max'): number | undefined {\n if (field.type === 'number') {\n return field.validators?.[bound] ?? (bound === 'min' ? field.min : field.max);\n }\n if (field.type === 'currency') return field.validators?.[bound];\n return undefined;\n}\n\n/** Ítems mínimos: un `required` de lista exige al menos uno. */\nfunction minItemsOf(field: FieldSchema): number | undefined {\n if (!LIST_TYPES.has(field.type)) return undefined;\n const list = field as FieldSchema & ListOptions;\n return list.required ? Math.max(list.min ?? 1, 1) : list.min;\n}\n\nfunction maxItemsOf(field: FieldSchema): number | undefined {\n if (!LIST_TYPES.has(field.type)) return undefined;\n return (field as FieldSchema & ListOptions).max;\n}\n\n// ── Implementaciones compartidas ────────────────────────────────────────────\n\nfunction toError(kind: string, message: string | null): { kind: string; message: string } | null {\n return message === null ? null : { kind, message };\n}\n\n/**\n * Mensaje cuando el `pattern` no coincide (§2: «nada de *Formato inválido*»).\n *\n * El error **reemplaza** a la ayuda bajo el campo (`field-host.ts:284`), así\n * que si el mensaje no repite lo que decía la ayuda el usuario pierde el\n * único texto que explica lo esperado: «11 dígitos.» se convierte en\n * «Formato esperado: 11 dígitos.». La ayuda de las schemas ya está escrita\n * en lenguaje humano, así que se reutiliza en vez de inventar otro.\n */\nfunction patternMessageFor(field: FieldSchema): string {\n const hint = field.help?.trim().replace(/[.。]+$/, '');\n return hint ? `Formato esperado: ${hint}.` : 'Revisa el formato de este campo.';\n}\n\nfunction validateUrl(value: unknown): string | null {\n if (value === undefined || value === null || value === '') {\n return null;\n }\n if (typeof value !== 'string') {\n return URL_MESSAGE;\n }\n return /^https?:\\/\\/\\S+$/i.test(value) ? null : URL_MESSAGE;\n}\n\nconst URL_MESSAGE = 'Debe ser una URL válida (http o https).';\n\n/** Fechas ISO `YYYY-MM-DD`: la comparación lexicográfica es correcta. */\nfunction dateError(\n value: unknown,\n bound: 'min' | 'max',\n iso: string,\n): { kind: string; message: string } | null {\n if (typeof value !== 'string' || value === '') {\n return null;\n }\n const outside = bound === 'min' ? value < iso : value > iso;\n if (!outside) {\n return null;\n }\n return {\n kind: `${bound}-date`,\n message:\n bound === 'min'\n ? `La fecha no puede ser anterior a ${iso}.`\n : `La fecha no puede ser posterior a ${iso}.`,\n };\n}\n\n/** Formato de URL para una ruta cualquiera (`string-list`, ítems de list). */\nexport function registerUrlRule(path: FieldPath<unknown>): void {\n validate(path as FieldPath<string>, (ctx) => toError('url', validateUrl(ctx.value())));\n}\n\n/** Formato de correo para una ruta cualquiera (`string-list`, ítems de list). */\nexport function registerEmailRule(path: FieldPath<unknown>): void {\n emailRule(path as FieldPath<string>, { message: 'Debe ser un correo electrónico válido.' });\n}\n\n/**\n * Regla `unique`: `resource` contra el listado del recurso, descartando el\n * propio registro. Si el listado no se puede leer (error de red) no se\n * bloquea el formulario — `onError` falla abierto.\n */\nfunction registerUniqueRule(ctx: ValidatorContext): void {\n const { path, field, schema, api, excludeKey } = ctx;\n const endpoint = schema.endpoint;\n const keyField = schema.keyField;\n const fieldName = field.key;\n const text = path as FieldPath<string>;\n\n validateAsync(text, {\n debounce: 300,\n params: (c) => {\n const value = c.value().trim();\n return value === '' ? undefined : { value, exclude: excludeKey };\n },\n factory: (params) =>\n resource({\n // El `Signal` es a la vez la fuente de `params` del recurso y la que\n // `validateAsync` controla: `undefined` deja el recurso en `idle`.\n params,\n loader: async ({ params: request }) => {\n const rows = await firstValueFrom(api.list<Record<string, unknown>>(endpoint));\n if (!Array.isArray(rows)) {\n return [];\n }\n return rows.filter(\n (row) =>\n row[fieldName] === request.value &&\n (request.exclude === undefined || row[keyField] !== request.exclude),\n );\n },\n }),\n onError: () => null,\n onSuccess: (rows, c) => {\n const value = c.value().trim();\n if (value === '' || !Array.isArray(rows) || rows.length === 0) {\n return null;\n }\n return { kind: 'unique', message: 'Este valor ya existe en el registro.' };\n },\n });\n}\n\n// ── El registro ─────────────────────────────────────────────────────────────\n\nexport const FIELD_VALIDATORS: readonly ValidatorEntry[] = [\n {\n // `boolean`: `isEmpty` de Signal Forms considera `false` vacío, así que un\n // booleano obligatorio bloquearía el guardado al desmarcarlo → se omite.\n // `group`: su valor es un objeto y un objeto nunca está vacío → sin regla.\n // Listas: lo resuelve `min-items` (un `[]` necesita al menos un ítem).\n name: 'required',\n declares: ({ field }) =>\n field.required === true &&\n field.type !== 'boolean' &&\n field.type !== 'group' &&\n !LIST_TYPES.has(field.type),\n register: ({ path }) => requiredRule(path, { message: 'Campo obligatorio.' }),\n },\n {\n name: 'email',\n declares: ({ field }) => field.type === 'email',\n register: ({ path }) => registerEmailRule(path),\n },\n {\n name: 'url',\n declares: ({ field }) => field.type === 'url',\n register: ({ path }) => registerUrlRule(path),\n },\n {\n name: 'min-length',\n declares: ({ field }) => minLengthOf(field) !== undefined,\n register: ({ path, field }) => {\n const min = minLengthOf(field);\n if (min !== undefined) {\n minLengthRule(path as FieldPath<string>, min, {\n message: `Debe tener al menos ${min} caracteres.`,\n });\n }\n },\n },\n {\n name: 'max-length',\n declares: ({ field }) => maxLengthOf(field) !== undefined,\n register: ({ path, field }) => {\n const max = maxLengthOf(field);\n if (max !== undefined) {\n maxLengthRule(path as FieldPath<string>, max, {\n message: `Como máximo ${max} caracteres.`,\n });\n }\n },\n },\n {\n name: 'pattern',\n declares: ({ field }) => patternOf(field) !== undefined,\n register: ({ path, field }) => {\n const pattern = patternOf(field);\n if (pattern !== undefined) {\n patternRule(path as FieldPath<string>, new RegExp(pattern), {\n message: patternMessageFor(field),\n });\n }\n },\n },\n {\n name: 'min-words',\n declares: ({ field }) => minWordsOf(field) !== undefined,\n register: ({ path, field }) => {\n const min = minWordsOf(field);\n if (min !== undefined) {\n validate(path as FieldPath<string>, (ctx) =>\n toError('min-words', validateMinWords(ctx.value(), min)),\n );\n }\n },\n },\n {\n name: 'max-words',\n declares: ({ field }) => maxWordsOf(field) !== undefined,\n register: ({ path, field }) => {\n const max = maxWordsOf(field);\n if (max !== undefined) {\n validate(path as FieldPath<string>, (ctx) =>\n toError('max-words', validateMaxWords(ctx.value(), max)),\n );\n }\n },\n },\n {\n name: 'min',\n declares: ({ field }) => numberBoundOf(field, 'min') !== undefined,\n register: ({ path, field }) => {\n const min = numberBoundOf(field, 'min');\n if (min !== undefined) {\n minRule(path as FieldPath<number | null>, min, {\n message: `Debe ser como mínimo ${min}.`,\n });\n }\n },\n },\n {\n name: 'max',\n declares: ({ field }) => numberBoundOf(field, 'max') !== undefined,\n register: ({ path, field }) => {\n const max = numberBoundOf(field, 'max');\n if (max !== undefined) {\n maxRule(path as FieldPath<number | null>, max, {\n message: `Debe ser como máximo ${max}.`,\n });\n }\n },\n },\n {\n name: 'min-items',\n declares: ({ field }) => minItemsOf(field) !== undefined,\n register: ({ path, field }) => {\n const min = minItemsOf(field);\n if (min !== undefined) {\n minLengthRule(path as FieldPath<unknown[]>, min, {\n message:\n min === 1 ? 'Debe tener al menos 1 elemento.' : `Debe tener al menos ${min} elementos.`,\n });\n }\n },\n },\n {\n name: 'max-items',\n declares: ({ field }) => maxItemsOf(field) !== undefined,\n register: ({ path, field }) => {\n const max = maxItemsOf(field);\n if (max !== undefined) {\n maxLengthRule(path as FieldPath<unknown[]>, max, {\n message: max === 1 ? 'Como máximo 1 elemento.' : `Como máximo ${max} elementos.`,\n });\n }\n },\n },\n {\n name: 'date-min',\n declares: ({ field }) => field.type === 'date' && field.min !== undefined,\n register: ({ path, field }) => {\n const min = field.type === 'date' ? field.min : undefined;\n if (min !== undefined) {\n validate(path as FieldPath<string>, (ctx) => dateError(ctx.value(), 'min', min));\n }\n },\n },\n {\n name: 'date-max',\n declares: ({ field }) => field.type === 'date' && field.max !== undefined,\n register: ({ path, field }) => {\n const max = field.type === 'date' ? field.max : undefined;\n if (max !== undefined) {\n validate(path as FieldPath<string>, (ctx) => dateError(ctx.value(), 'max', max));\n }\n },\n },\n {\n // Un singleton no tiene listado, así que no hay cómo verificarlo y se omite.\n // Cualquier campo de valor texto puede ser único: `text`, `slug`, `select`…\n // (antes sólo los tipos sin `case` propio, y `products.sku` — `text` —\n // se quedaba sin regla aunque el schema la declaraba y el back la exige).\n name: 'unique',\n declares: ({ field, schema }) =>\n field.validators?.unique === true && TEXT_TYPES.has(field.type) && !!schema.endpoint.list,\n register: (ctx) => registerUniqueRule(ctx),\n },\n];\n\n/** Nombres del registro: lo que `contract-check.mjs` compara con el back. */\nexport const VALIDATOR_NAMES: readonly ValidatorName[] = FIELD_VALIDATORS.map(\n (entry) => entry.name,\n);\n","import {\n applyEach,\n disabled,\n hidden,\n readonly as readonlyRule,\n type SchemaFn,\n type SchemaPathTree,\n} from '@angular/forms/signals';\n\nimport type { FieldSchema, ResourceSchema } from '../../core/models/schema.model';\nimport type { ApiService } from '../../core/services/api.service';\nimport {\n FIELD_VALIDATORS,\n registerEmailRule,\n registerUrlRule,\n type FieldPath,\n type ValidatorContext,\n} from '../../core/registry/field-validators';\nimport { isSupported } from '../../core/registry/render-registry';\nimport { evaluateCondition } from '../../shared/utils/condition-evaluator';\n\n/** Ruta de un hijo bajo un path (raíz, grupo o ítem). */\ntype ChildPath<V> = FieldPath<V>;\n/** Ruta raíz del formulario. */\ntype RootPath = SchemaPathTree<Record<string, unknown>>;\n/** Ruta de un campo lista (para `applyEach`). */\ntype ListPath<V> = FieldPath<V>;\n\n/**\n * Reglas del formulario desde el schema (base.md §4, §6, §9, §10).\n *\n * Hay DOS clases de reglas y viven en sitios distintos:\n *\n * 1. **Ciclo de vida** — `visibleWhen`, `readonlyOn`, `hiddenOn`, `dependsOn`:\n * decides si el campo se toca, se ve o se manda. Vive acá, con la\n * estructura del formulario.\n * 2. **Validadores** — obligatoriedad, longitud, patrón, rangos, únicos…:\n * vive en `core/registry/field-validators.ts` (F7 + B11), con los MISMOS\n * nombres que el back. Este módulo sólo RECORRE ese registro por cada\n * campo: añadir un validador es una entrada allá, no un `case` acá.\n *\n * El schema de Signal Forms recibe el path RAÍZ; las reglas se atan a la ruta\n * dinámica de cada campo. El barrido es **recursivo** — un `group` registra\n * las reglas de sus hijos bajo su propia ruta y un `list` registra las de\n * cada ítem con `applyEach`, de modo que las reglas corran también para los\n * ítems que se agregan después.\n *\n * Solo se generan reglas para los tipos que la UI sabe editar (`isSupported('field', …)`):\n * un campo invisible con `required` bloquearía el guardado para siempre.\n *\n * `api` y `excludeKey` alimentan la validación `unique`: se consulta el\n * listado del recurso y se descarta el propio registro (la clave del registro\n * en edición, tomada de la ruta).\n */\nexport function buildFormSchema(\n schema: ResourceSchema,\n api: ApiService,\n excludeKey?: string,\n): SchemaFn<Record<string, unknown>> {\n return (root) => {\n applyRulesToFields(\n { schema, api, excludeKey, root, container: root, siblings: schema.fields },\n schema.fields,\n );\n };\n}\n\n/**\n * Ámbito de registro de reglas.\n *\n * - `root` es siempre la raíz del formulario (referencia para condiciones que\n * nombran un campo de la raíz, base.md §4: «mismo nivel o registro raíz»).\n * - `container` es el nodo del que cuelgan las rutas hijas: la raíz, un\n * `group` o un ítem de un `list`.\n * - `siblings` son los campos declarados en `container`: si una condición\n * nombra a uno de ellos se evalúa contra el contenedor, si no, contra la\n * raíz.\n */\ninterface RuleScope {\n schema: ResourceSchema;\n api: ApiService;\n excludeKey: string | undefined;\n root: RootPath;\n container: object;\n siblings: readonly FieldSchema[];\n}\n\nfunction applyRulesToFields(scope: RuleScope, fields: readonly FieldSchema[]): void {\n for (const field of fields) {\n if (!isSupported('field', field.type)) {\n continue;\n }\n applyRulesToField(scope, field, pathAt<unknown>(scope.container, field.key));\n }\n}\n\nfunction applyRulesToField(scope: RuleScope, field: FieldSchema, path: ChildPath<unknown>): void {\n applyLifecycleRules(scope, field, path);\n\n // Validadores: el registro manda. Cada entrada decide si el campo la pide\n // y ata su regla a ESTA ruta (F7: sin `if` por regla en este módulo).\n const ctx: ValidatorContext = {\n path,\n field,\n schema: scope.schema,\n api: scope.api,\n excludeKey: scope.excludeKey,\n };\n for (const entry of FIELD_VALIDATORS) {\n if (entry.declares(ctx)) {\n entry.register(ctx);\n }\n }\n\n // Estructura (no es validador): los HIJOS se registran bajo su propia\n // ruta, recursivamente, para que las reglas corran también en los ítems\n // que el usuario agrega después.\n switch (field.type) {\n case 'group': {\n applyRulesToFields({ ...scope, container: path, siblings: field.fields }, field.fields);\n break;\n }\n case 'list': {\n applyEach(path as unknown as ListPath<Record<string, unknown>[]>, (item) =>\n applyRulesToFields(\n { ...scope, container: item as object, siblings: field.itemFields },\n field.itemFields,\n ),\n );\n break;\n }\n case 'string-list': {\n applyEach(path as unknown as ListPath<string[]>, (item) =>\n applyStringItemRules(item as ChildPath<string>, field.itemType),\n );\n break;\n }\n default:\n break;\n }\n}\n\n/**\n * ¿Aplica `mode` en ESTE contexto de formulario?\n *\n * Lo comparten `readonlyOn` y `hiddenOn`: `always` vale en todos los modos,\n * `create` sólo en alta y `update` sólo en edición. El modo se detecta por\n * `scope.excludeKey !== undefined`, que es la señal que ya usaba `readonlyOn`\n * (form-view define `mode` a partir de la misma condición).\n */\nfunction modeAppliesTo(mode: 'create' | 'update' | 'always', isEdit: boolean): boolean {\n return mode === 'always' || (mode === 'update' && isEdit) || (mode === 'create' && !isEdit);\n}\n\n/**\n * Ciclo de vida del campo: visibilidad, edición y dependencias (base.md §4).\n *\n * Aquí NO hay validadores — la obligatoriedad vive en el registro.\n * `hidden` además excluye el campo de `dirty`, `invalid` y del payload\n * (base.md §10 «Campos ocultos»), y `disabled` lo deja fuera de la\n * validación, así un `required` en el hijo no bloquea el guardado mientras\n * no haya nada que elegir.\n */\nfunction applyLifecycleRules(scope: RuleScope, field: FieldSchema, path: ChildPath<unknown>): void {\n if (field.visibleWhen) {\n const condition = field.visibleWhen;\n hidden(path, {\n when: (ctx) =>\n !evaluateCondition(condition, ctx.valueOf(conditionPath(scope, condition.field))),\n });\n }\n // Selección dependiente (guía §1, plan 6.6): el hijo no se puede tocar\n // hasta que el padre tenga valor.\n //\n // Acá NO sirve `conditionPath`: eso devuelve el contenedor entero porque\n // `evaluateCondition` recibe el objeto de hermanos. Acá se necesita el\n // valor concreto del padre.\n if (field.dependsOn) {\n const parentKey = field.dependsOn;\n const isSibling = scope.siblings.some((candidate) => candidate.key === parentKey);\n const parent = isSibling ? scope.container : scope.root;\n const parentPath = pathAt<unknown>(parent, parentKey);\n disabled(path, ({ valueOf }) => isEmptyValue(valueOf(parentPath)));\n }\n if (field.readonly) {\n readonlyRule(path, { when: () => true });\n }\n if (field.readonlyOn && modeAppliesTo(field.readonlyOn, scope.excludeKey !== undefined)) {\n readonlyRule(path, { when: () => true });\n }\n // Ciclo de vida de la visibilidad — simétrico a `readonlyOn` de arriba.\n // `hidden()` deja el campo fuera de la validación y del payload, así que en\n // alta un campo que LLENA EL BACK no ocupa la sección ni exige nada: sin\n // esto, `ref` y `pin` aparecían vacíos con un \"se genera solo al guardar\".\n if (field.hiddenOn && modeAppliesTo(field.hiddenOn, scope.excludeKey !== undefined)) {\n hidden(path, { when: () => true });\n }\n if (field.readonlyWhen) {\n const condition = field.readonlyWhen;\n readonlyRule(path, {\n when: (ctx) =>\n evaluateCondition(condition, ctx.valueOf(conditionPath(scope, condition.field))),\n });\n }\n}\n\n/** Vacío para el propósito de «el padre todavía no eligió nada». */\nfunction isEmptyValue(value: unknown): boolean {\n if (value === undefined || value === null || value === '') {\n return true;\n }\n return Array.isArray(value) && value.length === 0;\n}\n\n/**\n * Path contra el que se evalúa una condición: el contenedor si nombra a un\n * campo hermano, la raíz si no (base.md §4: «otro campo del mismo nivel o\n * del registro raíz»).\n */\nfunction conditionPath(scope: RuleScope, key: string): RootPath {\n const isSibling = scope.siblings.some((field) => field.key === key);\n const target = isSibling ? scope.container : scope.root;\n return target as RootPath;\n}\n\n/** Ítems de `string-list`: validan según el tipo declarado (§9: text/url/email). */\nfunction applyStringItemRules(\n path: ChildPath<string>,\n itemType: 'text' | 'url' | 'email' | undefined,\n): void {\n if (itemType === 'email') {\n registerEmailRule(path);\n }\n if (itemType === 'url') {\n registerUrlRule(path);\n }\n}\n\n/**\n * Navegación dinámica sobre el árbol de paths.\n *\n * Indexar `SchemaPathTree<Record<string, unknown>>` con una clave dinámica\n * devuelve `SchemaPath<unknown, …> | undefined`: el `| undefined` es solo una\n * consecuencia del index signature, y las reglas con valor concreto (`min`,\n * `minLength`, `email`…) exigen afinar el tipo. Los casts de este módulo\n * viven acá; los del registro, en `field-validators.ts` (solución reportada\n * en plan.md, Fase 10).\n */\nfunction pathAt<V>(parent: object, key: string): ChildPath<V> {\n const path = (parent as unknown as Record<string, unknown>)[key];\n if (path === undefined) {\n throw new Error(`El formulario no contiene la ruta \"${key}\".`);\n }\n return path as ChildPath<V>;\n}\n","import type { FieldSchema, ResourceSchema } from '../../core/models/schema.model';\nimport { childTree, itemTree, listTree, type RootTree } from '../../fields/field-node';\nimport { seedFields } from '../../shared/utils/seed';\n\n/**\n * Modelo y payload del formulario genérico (Fase 10, ampliado en Fase 11).\n *\n * - `seedModel` siembra un valor inicial para CADA campo declarado en el\n * schema antes de crear el formulario: el árbol de Signal Forms replica la\n * forma del modelo, así que un campo ausente no tendría estado (ni valor\n * que enviar). La siembra vive en `shared/utils/seed.ts`.\n * - `buildPayload` decide qué viaja al backend (base.md §10): solo las claves\n * declaradas en el schema, sin los ocultos y sin `positionField` raíz.\n */\n\n/** Valores por defecto del schema, en el mismo orden/shape que el JSON. */\nexport function seedModel(schema: ResourceSchema): Record<string, unknown> {\n return seedFields(schema.fields);\n}\n\n/**\n * Payload del guardado: whitelist de `schema.fields`.\n *\n * - Los campos ocultos (`visibleWhen` falso) no se muestran ni se envían\n * (base.md §10): usar `hidden()` garantiza que no aparezcan acá aunque\n * tengan valor sembrado — también dentro de `group` y de los ítems de una\n * `list` (recursivo).\n * - `positionField` del recurso se excluye aunque esté declarado: el orden\n * del listado lo gestiona el backend (api.md R3, nunca se expone). El\n * `positionField` de los ÍTEMS de una lista sí viaja: el orden local lo\n * calcula el front (base.md §8).\n * - Los tipos que la UI todavía no edita SÍ viajan: sus valores llegan del\n * registro leído, así que reenviarlos evita pisarlos.\n */\nexport function buildPayload(schema: ResourceSchema, root: RootTree): Record<string, unknown> {\n const payload: Record<string, unknown> = {};\n collectPayload(root, schema.fields, payload, schema.positionField);\n return payload;\n}\n\nfunction collectPayload(\n tree: RootTree,\n fields: readonly FieldSchema[],\n payload: Record<string, unknown>,\n excludedKey: string | undefined,\n): void {\n for (const field of fields) {\n if (excludedKey !== undefined && field.key === excludedKey) {\n continue;\n }\n const state = childTree<unknown>(tree, field.key)();\n if (state.hidden()) {\n continue;\n }\n if (field.type === 'group') {\n const nested: Record<string, unknown> = {};\n collectPayload(\n childTree<Record<string, unknown>>(tree, field.key),\n field.fields,\n nested,\n undefined,\n );\n payload[field.key] = nested;\n continue;\n }\n if (field.type === 'list') {\n const items = listTree(tree, field.key);\n const raw = (state.value() as Record<string, unknown>[] | null) ?? [];\n const list: unknown[] = [];\n for (let index = 0; index < raw.length; index++) {\n const nested: Record<string, unknown> = {};\n collectPayload(itemTree(items, index), field.itemFields, nested, undefined);\n // El orden de los ítems viaja con el registro padre (base.md §8): el\n // `positionField` no es un campo editable, por eso no está en\n // `itemFields` y hay que leerlo del modelo crudo.\n const positionKey = field.positionField;\n const position = positionKey !== undefined ? raw[index]?.[positionKey] : undefined;\n if (positionKey !== undefined && position !== undefined) {\n nested[positionKey] = position;\n }\n list.push(nested);\n }\n payload[field.key] = list;\n continue;\n }\n payload[field.key] = state.value();\n }\n}\n","import {\n Component,\n DestroyRef,\n Injector,\n OnInit,\n computed,\n inject,\n input,\n output,\n signal,\n} from '@angular/core';\nimport { takeUntilDestroyed } from '@angular/core/rxjs-interop';\nimport { form, submit } from '@angular/forms/signals';\nimport { firstValueFrom } from 'rxjs';\n\nimport type { FieldSchema, ResourceSchema } from '../../core/models/schema.model';\nimport { ApiService } from '../../core/services/api.service';\nimport { ErrorMapperService } from '../../core/services/error-mapper.service';\nimport { NotificationService } from '../../core/services/notification.service';\nimport { FieldHost } from '../../fields/field-host/field-host';\nimport { childTree, type RootTree } from '../../fields/field-node';\nimport { Button } from '../../shared/components/button/button';\nimport { Drawer, type DrawerSize } from '../../shared/components/drawer/drawer';\nimport { Skeleton } from '../../shared/components/skeleton/skeleton';\nimport { buildFormSchema } from '../form-view/form-schema';\nimport { buildPayload, seedModel } from '../form-view/form-model';\n\n/**\n * Formulario dentro de un drawer (plan 6.4/6.5, guía §5).\n *\n * Es deliberadamente más chico que `form-view`: sin pestañas, sin secciones,\n * sin borrador y sin resumen de errores. Sirve para abrir 1–3 campos sin\n * perder el contexto de la pantalla de la que vino el usuario, y para crear\n * un registro desde el dropdown de una relación.\n *\n * Componente controlado: `open` entra por `input()`, y el padre cierra con\n * `close`/`saved`. Nada navega por su cuenta.\n */\n@Component({\n selector: 'app-drawer-form',\n imports: [Button, Drawer, FieldHost, Skeleton],\n template: `\n <app-drawer [open]=\"open()\" [title]=\"drawerTitle()\" [size]=\"size()\" (close)=\"close.emit()\">\n @if (status() === 'loading') {\n <app-skeleton [lines]=\"4\" />\n } @else if (status() === 'error') {\n <p role=\"alert\" class=\"rounded-lg bg-danger/10 px-3 py-2 text-sm text-danger\">\n {{ loadError() }}\n </p>\n } @else if (tree(); as formTree) {\n @if (generalError()) {\n <p\n role=\"alert\"\n class=\"mb-4 rounded-lg bg-danger/10 px-3 py-2 text-sm text-danger\"\n data-drawer-error\n >\n {{ generalError() }}\n </p>\n }\n @for (field of visibleFields(); track field.key) {\n <div class=\"mb-4\">\n <app-field-host\n [field]=\"field\"\n [tree]=\"formTree\"\n [exists]=\"mode() === 'edit'\"\n [serverErrors]=\"fieldErrors()\"\n [resource]=\"schema().id\"\n />\n </div>\n }\n }\n\n <div drawer-actions class=\"flex justify-end gap-2\">\n <button app-button variant=\"outline\" type=\"button\" (click)=\"close.emit()\">Cancelar</button>\n <button app-button type=\"button\" [loading]=\"saving()\" (click)=\"save()\">Guardar</button>\n </div>\n </app-drawer>\n `,\n})\nexport class DrawerForm implements OnInit {\n readonly open = input(false);\n readonly schema = input.required<ResourceSchema>();\n /** Subconjunto de campos a mostrar. Por defecto, todos los del schema. */\n readonly fields = input<readonly FieldSchema[] | undefined>(undefined);\n readonly mode = input<'create' | 'edit'>('create');\n readonly recordKey = input<string | null>(null);\n readonly title = input<string | null>(null);\n readonly size = input<DrawerSize>('md');\n\n /** Registro creado o actualizado, listo para que el padre refresque. */\n readonly saved = output<Record<string, unknown>>();\n readonly close = output<void>();\n\n private readonly api = inject(ApiService);\n private readonly notifications = inject(NotificationService);\n private readonly errorMapper = inject(ErrorMapperService);\n private readonly injector = inject(Injector);\n private readonly destroyRef = inject(DestroyRef);\n\n private readonly tree = signal<RootTree | null>(null);\n private readonly status = signal<'idle' | 'loading' | 'ready' | 'error'>('idle');\n private readonly saving = signal(false);\n private readonly generalError = signal('');\n private readonly fieldErrors = signal<Record<string, string>>({});\n private readonly loadError = signal('');\n\n readonly visibleFields = computed<readonly FieldSchema[]>(\n () => this.fields() ?? this.schema().fields,\n );\n\n readonly drawerTitle = computed(() => {\n if (this.title() !== null) {\n return this.title() as string;\n }\n const label = this.schema().label;\n return this.mode() === 'create' ? `Crear ${label}` : `Editar ${label}`;\n });\n\n ngOnInit(): void {\n // Se construye al montar, NO en un `effect`: `form()` crea un effect\n // interno y no puede correr dentro de otro (NG0602). Los padres montan\n // este componente bajo un `@if` y fijan los inputs, así que no hace\n // falta reaccionar a cambios posteriores.\n this.build();\n }\n\n private build(): void {\n const schema = this.schema();\n const recordKey = this.recordKey();\n const schemaFn = buildFormSchema(schema, this.api, recordKey ?? undefined);\n const tree = form(signal(seedModel(schema)), schemaFn, { injector: this.injector });\n\n if (this.mode() === 'create' || recordKey === null) {\n this.tree.set(tree);\n this.status.set('ready');\n return;\n }\n\n // Edición: el valor se pisa con el registro, dejando los defaults de\n // los campos que el backend no devuelve.\n this.status.set('loading');\n this.api\n .get<Record<string, unknown>>(schema.endpoint, recordKey)\n .pipe(takeUntilDestroyed(this.destroyRef))\n .subscribe({\n next: (record) => {\n tree().value.set({ ...seedModel(schema), ...record });\n this.tree.set(tree);\n this.status.set('ready');\n },\n error: (error: unknown) => {\n this.loadError.set(this.errorMapper.messageOf(error));\n this.status.set('error');\n },\n });\n }\n\n async save(): Promise<void> {\n const tree = this.tree();\n const schema = this.schema();\n if (tree === null || this.saving()) {\n return;\n }\n this.saving.set(true);\n this.generalError.set('');\n this.fieldErrors.set({});\n try {\n const ok = await submit(tree, {\n // `null` = sin errores propios: los del backend se pintan aparte.\n action: async () => {\n await this.persist(tree, schema);\n return null;\n },\n onInvalid: () => this.markTouched(tree),\n });\n if (!ok || Object.keys(this.fieldErrors()).length > 0 || this.generalError() !== '') {\n this.markTouched(tree);\n }\n } finally {\n this.saving.set(false);\n }\n }\n\n /**\n * Marca como tocado lo que se está mostrando. Se recorre por campo y no\n * desde la raíz: `FieldTree<Record<string, unknown>>` se indexa con una\n * clave dinámica y `markAsTouched` no compila ahí (`childTree` centraliza\n * ese cast). Baja a todos los descendientes, así un `group` queda cubierto.\n */\n private markTouched(tree: RootTree): void {\n for (const field of this.visibleFields()) {\n childTree<unknown>(tree, field.key)().markAsTouched();\n }\n }\n\n /** create/update; nunca lanza (los errores quedan en las señales). */\n private async persist(tree: RootTree, schema: ResourceSchema): Promise<void> {\n const payload = buildPayload(schema, tree);\n try {\n if (this.mode() === 'create') {\n const created = await firstValueFrom(\n this.api.create<Record<string, unknown>>(schema.endpoint, payload),\n );\n this.notifications.success('Registro creado.');\n this.saved.emit(created ?? {});\n this.close.emit();\n return;\n }\n const updated = await firstValueFrom(\n this.api.update<Record<string, unknown>>(\n schema.endpoint,\n this.recordKey() ?? undefined,\n payload,\n ),\n );\n this.notifications.success('Registro guardado.');\n this.saved.emit(updated ?? {});\n this.close.emit();\n } catch (error: unknown) {\n const mapped = this.errorMapper.map(error);\n this.fieldErrors.set(mapped.fields);\n const withoutFields = Object.keys(mapped.fields).length === 0;\n this.generalError.set(\n mapped.general ?? (withoutFields ? this.errorMapper.messageOf(error) : ''),\n );\n }\n }\n}\n","import type { FieldSchema } from '../../core/models/schema.model';\nimport { seedFieldValue } from './seed';\n\n/**\n * Enlaces de selección dependiente (guía §1, plan 6.6).\n *\n * `condition-evaluator.ts` sólo RESUELVE condiciones (`visibleWhen` /\n * `readonlyWhen`): no resetea nada. Acá se levanta el mapa de «cuando el\n * padre cambie, limpiar el hijo» que `form-view` observa con un `effect`.\n *\n * Alcance: raíz y `group` (contenedores estáticos). Los ítems de un `list`\n * se crean y borran mientras se edita y sus rutas no son estables — un\n * dependiente dentro de una lista queda sin resetear, así que no se promete.\n */\nexport interface DependsOnLink {\n /** Ruta de contenedores desde la raíz: `[]` en la raíz, `['contacto']` en un grupo. */\n container: string[];\n childKey: string;\n parentKey: string;\n /** Valor con el que se limpia el hijo (el default del schema). */\n resetValue: unknown;\n /** Clave estable del enlace para el snapshot de comparación. */\n id: string;\n}\n\nexport function collectDependsOn(\n fields: readonly FieldSchema[],\n container: string[] = [],\n): DependsOnLink[] {\n const links: DependsOnLink[] = [];\n for (const field of fields) {\n if (field.type === 'group') {\n links.push(...collectDependsOn(field.fields, [...container, field.key]));\n continue;\n }\n if (field.dependsOn === undefined) {\n continue;\n }\n links.push({\n container,\n childKey: field.key,\n parentKey: field.dependsOn,\n resetValue: seedFieldValue(field),\n id: `${container.join('.')}.${field.key}<-${field.dependsOn}`,\n });\n }\n return links;\n}\n","import { Component, computed, inject, input } from '@angular/core';\n\nimport { LucideCopy, LucideShare } from '@lucide/angular';\n\nimport { buildShareMessage, writeClipboard } from '../../../core/utils/clipboard';\nimport { NotificationService } from '../../../core/services/notification.service';\nimport { Button } from '../button/button';\n\n/**\n * Copia el enlace de un registro al portapapeles y ofrece compartirlo.\n *\n * Es lo que hace útil `public_base_url` (Etapa 2, S5): con este botón el\n * operador entrega el enlace público del pedido.\n *\n * Tres caminos, todos degradando en vez de romper:\n *\n * 1. **Copiar** — `navigator.clipboard.writeText`; si el navegador no lo\n * tiene (contexto no seguro) cae a un `textarea` + `execCommand('copy')`.\n * 2. **Compartir** — `navigator.share`, sólo renderizado si existe\n * (`'share' in navigator`; no está en desktop).\n * 3. **WhatsApp** — el reemplazo del 2 cuando no hay `navigator.share`:\n * `wa.me/?text=` con `shareText` + `url`.\n *\n * `navigator.share` se llama con `{ url, text }`: no se envía `title` porque\n * no hay input dedicado y `label` es el rótulo del botón de copiar, no un\n * título de compartir.\n */\n@Component({\n selector: 'app-copy-share',\n imports: [Button, LucideCopy, LucideShare],\n template: `\n <div class=\"flex flex-wrap items-center gap-2\">\n <button app-button type=\"button\" variant=\"outline\" (click)=\"copy()\">\n <svg lucideCopy size=\"16\" />\n {{ label() }}\n </button>\n\n @if (canShare()) {\n <button app-button type=\"button\" variant=\"ghost\" (click)=\"share()\">\n <svg lucideShare size=\"16\" />\n Compartir\n </button>\n } @else {\n <button app-button type=\"button\" variant=\"ghost\" (click)=\"shareWhatsApp()\">\n <svg lucideShare size=\"16\" />\n WhatsApp\n </button>\n }\n </div>\n `,\n})\nexport class CopyShare {\n /** Enlace público a copiar/compartir. */\n readonly url = input.required<string>();\n /** Rótulo del botón de copiar. */\n readonly label = input('Copiar enlace');\n /** Texto que viaja junto al enlace al compartir (nativo o WhatsApp). */\n readonly shareText = input('');\n\n private readonly notify = inject(NotificationService);\n\n /**\n * El share nativo no existe en desktop: sin él se ofrece WhatsApp.\n *\n * Se comprueba `typeof … === 'function'` y no `'share' in navigator` como\n * dice el plan: el `in` da `true` también para una propiedad propia con\n * valor `undefined` (polyfills y stubs hacen exactamente eso), y eso\n * montaría un botón que revienta al pulsarlo.\n */\n protected readonly canShare = computed(\n () => typeof navigator !== 'undefined' && typeof navigator.share === 'function',\n );\n\n protected readonly waUrl = computed(() => {\n // Mismo texto que copia el botón: `buildShareMessage` evita pegar el\n // enlace DOS veces cuando el `shareTextTemplate` ya lo trae escrito.\n const text = buildShareMessage(this.url(), this.shareText());\n return `https://wa.me/?text=${encodeURIComponent(text)}`;\n });\n\n protected async copy(): Promise<void> {\n // El mensaje formateado completo (saludo + ref + enlace + PIN), no sólo\n // la URL: es lo que el operador pega en la conversación con el cliente.\n const copied = await writeClipboard(buildShareMessage(this.url(), this.shareText()));\n if (copied) {\n this.notify.success('Mensaje copiado.');\n } else {\n this.notify.error('No se pudo copiar el mensaje.');\n }\n }\n\n protected async share(): Promise<void> {\n if (!this.canShare()) {\n this.shareWhatsApp();\n return;\n }\n try {\n await navigator.share({ url: this.url(), text: this.shareText() });\n } catch {\n // Cancelar la hoja de compartir dispara AbortError: no es un error.\n }\n }\n\n protected shareWhatsApp(): void {\n window.open(this.waUrl(), '_blank', 'noopener,noreferrer');\n }\n}\n","import {\n Component,\n ElementRef,\n OnDestroy,\n afterRenderEffect,\n input,\n output,\n viewChild,\n} from '@angular/core';\nimport { LucideX } from '@lucide/angular';\n\nimport { FOCUSABLE, isVisibleNow } from '../../utils/dialog-focus';\n\nlet titleCounter = 0;\n\n/**\n * Diálogo modal accesible: `aria-modal` con nombre por `aria-labelledby`,\n * foco al abrirse y restaurado al cerrarse — también si el consumidor lo\n * destruye con el diálogo todavía abierto — Escape y botón ✕ cierran.\n *\n * ATRAPA el Tab dentro del diálogo: sin eso el foco se tabula hacia la página\n * de atrás y se pierde detrás del fondo oscuro. Mismo contrato que `drawer`,\n * con el que comparte el selector de elementos focables.\n *\n * El clic en el fondo es un atajo para puntero; el teclado usa ✕ o Escape.\n * Componente tonto: `open`/`title` entran por `input()`, el estado vive\n * fuera.\n */\n@Component({\n selector: 'app-modal',\n imports: [LucideX],\n host: {\n '(document:keydown.escape)': 'onEscape()',\n // Angular arma la tecla CON sus modificadores y exige igualdad exacta:\n // sin esta segunda entrada, Shift+Tab llega como `shift.tab`, no casa con\n // `tab` y el foco escapa del diálogo hacia atrás.\n '(document:keydown.tab)': 'onTab($event)',\n '(document:keydown.shift.tab)': 'onTab($event)',\n },\n template: `\n @if (open()) {\n <div class=\"fixed inset-0 z-50 flex items-center justify-center p-4\">\n <div class=\"absolute inset-0 bg-black/50\" aria-hidden=\"true\" (click)=\"close.emit()\"></div>\n <div\n #dialog\n role=\"dialog\"\n aria-modal=\"true\"\n [attr.aria-labelledby]=\"titleId\"\n tabindex=\"-1\"\n class=\"relative w-full max-w-lg rounded-xl bg-surface-raised p-6 shadow-xl focus:outline-none\"\n >\n <div class=\"mb-4 flex items-start justify-between gap-4\">\n <h2 [id]=\"titleId\" class=\"font-display text-section font-semibold\">\n {{ title() }}\n </h2>\n <button\n type=\"button\"\n class=\"flex min-h-11 min-w-11 items-center justify-center rounded text-neutral hover:bg-neutral/10 focus-visible:outline-2 focus-visible:outline-primary lg:min-h-0 lg:min-w-0\"\n [attr.aria-label]=\"closeLabel()\"\n (click)=\"close.emit()\"\n >\n <svg lucideX size=\"18\" />\n </button>\n </div>\n <ng-content />\n </div>\n </div>\n }\n `,\n})\nexport class Modal implements OnDestroy {\n readonly open = input(false);\n readonly title = input.required<string>();\n readonly closeLabel = input('Cerrar');\n readonly close = output<void>();\n\n readonly titleId = `modal-title-${++titleCounter}`;\n\n private readonly dialogRef = viewChild<ElementRef<HTMLDivElement>>('dialog');\n private lastFocused: HTMLElement | null = null;\n\n private readonly focusEffect = afterRenderEffect(() => {\n const open = this.open();\n const dialog = this.dialogRef();\n if (open && dialog) {\n const active = document.activeElement;\n if (active !== dialog.nativeElement && this.lastFocused === null) {\n this.lastFocused = active instanceof HTMLElement ? active : null;\n }\n dialog.nativeElement.focus();\n } else if (!open && this.lastFocused !== null) {\n this.lastFocused.focus();\n this.lastFocused = null;\n }\n });\n\n /**\n * El consumidor puede destruir el componente con el diálogo todavía abierto\n * (se monta bajo un `@if`): entonces el effect nunca ve la transición a\n * `false` y el foco se quedaría perdido en el body.\n */\n ngOnDestroy(): void {\n if (this.lastFocused?.isConnected) {\n this.lastFocused.focus();\n }\n this.lastFocused = null;\n }\n\n onEscape(): void {\n if (this.open()) {\n this.close.emit();\n }\n }\n\n /**\n * Mantiene el Tab dentro del diálogo mientras está abierto. Recibe `Event`\n * porque los host listeners de Angular tipean `$event` así; el teclado se\n * relee acá dentro.\n */\n onTab(rawEvent: Event): void {\n const event = rawEvent as KeyboardEvent;\n const dialog = this.dialogRef()?.nativeElement;\n if (!this.open() || !dialog) {\n return;\n }\n const focusable = Array.from(dialog.querySelectorAll<HTMLElement>(FOCUSABLE)).filter(\n (element) => isVisibleNow(element),\n );\n if (focusable.length === 0) {\n event.preventDefault();\n dialog.focus();\n return;\n }\n const first = focusable[0];\n const last = focusable[focusable.length - 1];\n const active = document.activeElement;\n if (event.shiftKey && (active === first || active === dialog)) {\n event.preventDefault();\n last.focus();\n } else if (!event.shiftKey && active === last) {\n event.preventDefault();\n first.focus();\n }\n }\n}\n","import { Component, computed, inject, input, output } from '@angular/core';\nimport { LucideCheckCircle2 } from '@lucide/angular';\n\nimport type {\n CopyShareAction,\n PostCreateSchema,\n ResourceAction,\n ResourceSchema,\n} from '../../../core/models/schema.model';\nimport { isShareAction, resolveShare, type ResolvedShare } from '../../../actions/action-registry';\nimport { ConfigService } from '../../../core/services/config.service';\nimport { interpolateTemplate } from '../../../core/utils/template-interpolator';\nimport { Button } from '../button/button';\nimport { CopyShare } from '../copy-share/copy-share';\nimport { Modal } from '../modal/modal';\n\ninterface SummaryItem {\n key: string;\n label: string;\n value: string;\n}\n\n/**\n * Modal de éxito y resumen tras crear un registro.\n * Permite visualizar datos clave y acciones directas (como CopyShare o compartir por WhatsApp).\n */\n@Component({\n selector: 'app-post-create-modal',\n imports: [Modal, Button, CopyShare, LucideCheckCircle2],\n template: `\n <app-modal [open]=\"true\" [title]=\"title()\" (close)=\"closed.emit()\">\n <div class=\"flex flex-col gap-4\">\n <!-- Encabezado con icono de éxito -->\n <div class=\"flex items-center gap-3\">\n <div\n class=\"flex h-10 w-10 shrink-0 items-center justify-center rounded-full bg-emerald-500/10 text-emerald-500\"\n >\n <svg lucideCheckCircle2 size=\"22\" />\n </div>\n <div>\n <p class=\"text-sm text-neutral-light\">{{ description() }}</p>\n </div>\n </div>\n\n <!-- Tarjeta de campos de resumen -->\n @if (summaryItems().length > 0) {\n <div class=\"rounded-lg border border-neutral/15 bg-surface p-3.5\">\n <dl class=\"grid grid-cols-1 gap-x-4 gap-y-2.5 sm:grid-cols-2\">\n @for (item of summaryItems(); track item.key) {\n <div class=\"flex flex-col\">\n <dt class=\"text-xs font-medium text-neutral\">{{ item.label }}</dt>\n <dd class=\"text-sm font-semibold text-neutral-light truncate\">\n {{ item.value }}\n </dd>\n </div>\n }\n </dl>\n </div>\n }\n\n <!-- Acciones declaradas (ej. CopyShare) -->\n @if (copyShareAction(); as action) {\n <div class=\"rounded-lg border border-primary/20 bg-primary/5 p-3.5\">\n <p class=\"mb-2 text-xs font-medium text-primary\">Enlace de Seguimiento</p>\n @let share = shareOf(action);\n <app-copy-share\n [url]=\"share.url\"\n [shareText]=\"share.shareText\"\n [label]=\"action.label\"\n />\n </div>\n }\n\n <!-- Botón de cierre primario -->\n <div class=\"mt-2 flex justify-end\">\n <button app-button type=\"button\" variant=\"primary\" (click)=\"closed.emit()\">\n Continuar\n </button>\n </div>\n </div>\n </app-modal>\n `,\n})\nexport class PostCreateModal {\n readonly schema = input.required<ResourceSchema>();\n readonly config = input.required<PostCreateSchema>();\n readonly record = input.required<Record<string, unknown>>();\n\n /**\n * Override puntual del contexto de interpolación (tests, o quien quiera\n * forzar un valor). **No** es la vía normal: la config global la aporta\n * `ConfigService`, igual que en `list-view`.\n */\n readonly configValues = input<Record<string, unknown>>({});\n\n readonly closed = output<void>();\n\n /**\n * Inyectado y NO opcional a propósito. El bug que motivó esto (D18): con\n * el input `configValues` como única fuente y nadie bindeándolo, la\n * plantilla salía con `{public_base_url}` vacío y el operador copiaba\n * `…aquí: /p/ORD-0001.` — un enlace inpegable.\n *\n * `buildInterpolationContext` mezcla config global + fila y fija\n * `public_base_url` (`config.service.ts:64-73`); `configValues` se aplica\n * encima, sólo como override.\n */\n private readonly configService = inject(ConfigService);\n\n constructor() {\n // El modal se abre con la sesión ya iniciada: si el load del bootstrap\n // falló (arranque sin sesión), éste es el momento de reintentarlo para\n // que el mensaje de compartir salga del singleton y no «sólo la URL».\n // `globalConfig` es signal: cuando llegue, el contexto y `shareOf` se\n // recalculan solos.\n void this.configService.ensureLoaded();\n }\n\n /** Contexto único con el que se resuelven título, descripción, URL y mensaje. */\n private readonly context = computed(() => ({\n ...this.configService.buildInterpolationContext(this.record()),\n ...this.configValues(),\n }));\n\n protected readonly title = computed(() => {\n const raw = this.config().title || '¡Registro Creado con Éxito!';\n return interpolateTemplate(raw, this.context());\n });\n\n protected readonly description = computed(() => {\n const raw =\n this.config().description ||\n 'El registro fue creado correctamente y ya se encuentra guardado en el sistema.';\n return interpolateTemplate(raw, this.context());\n });\n\n protected readonly summaryItems = computed<SummaryItem[]>(() => {\n const fields = this.config().summaryFields || [];\n const rec = this.record();\n const schemaFields = this.schema().fields || [];\n\n return fields.map((key) => {\n const fieldDef = schemaFields.find((f) => f.key === key);\n const label = fieldDef?.label || key.replace(/_/g, ' ');\n const rawVal = rec[key];\n const value = rawVal === null || rawVal === undefined || rawVal === '' ? '—' : String(rawVal);\n return { key, label, value };\n });\n });\n\n protected readonly copyShareAction = computed<CopyShareAction | null>(\n () => (this.config().actions || []).find(isShareAction) ?? null,\n );\n\n /** Enlace y mensaje, resueltos por `resolveShare` (R3.10: una sola copia). */\n protected shareOf(action: CopyShareAction): ResolvedShare {\n return resolveShare(action, this.context());\n }\n}\n","import {\n ApplicationRef,\n Component,\n computed,\n effect,\n inject,\n Injector,\n signal,\n} from '@angular/core';\nimport { toSignal } from '@angular/core/rxjs-interop';\nimport { form, submit } from '@angular/forms/signals';\nimport { ActivatedRoute, Router } from '@angular/router';\n\nimport { ViewNavigationService } from '../../core/view-navigation/view-navigation.service';\nimport { firstValueFrom, of } from 'rxjs';\n\nimport type { FieldSchema, LayoutMode, ResourceSchema } from '../../core/models/schema.model';\nimport { isSupported } from '../../core/registry/render-registry';\nimport { ApiService } from '../../core/services/api.service';\nimport { AuthService } from '../../core/services/auth.service';\nimport { ErrorMapperService, type MappedError } from '../../core/services/error-mapper.service';\nimport { NotificationService } from '../../core/services/notification.service';\nimport {\n RelationDrawerService,\n type RelationCreateRequest,\n} from '../../core/services/relation-drawer.service';\nimport { FieldHost } from '../../fields/field-host/field-host';\nimport { childTree, groupTree, type RootTree } from '../../fields/field-node';\nimport { DrawerForm } from '../drawer-form/drawer-form';\nimport { getSchema } from '../../core/registry/schema-catalog';\nimport { collectDependsOn } from '../../shared/utils/depends-on';\nimport { Button } from '../../shared/components/button/button';\nimport { EmptyState } from '../../shared/components/empty-state/empty-state';\nimport { PostCreateModal } from '../../shared/components/post-create-modal/post-create-modal';\nimport { Skeleton } from '../../shared/components/skeleton/skeleton';\nimport { colSpanClass } from '../../shared/utils/col-span';\nimport { isRecord, mergeRecord } from '../../shared/utils/seed';\nimport { buildFormSchema } from './form-schema';\nimport { buildPayload, seedModel } from './form-model';\n\ntype FormMode = 'create' | 'edit' | 'single';\ntype FormStatus = 'loading' | 'ready' | 'error';\n\ninterface SectionView {\n id: string;\n label: string;\n fields: FieldSchema[];\n}\n\n/**\n * Un ítem del resumen de errores (§4.11). `id` es el que ya genera\n * `state().name()`: es a la vez el valor de `data-field` del envoltorio y el\n * `id` real del control, así que no se inventa ningún ancla nueva.\n */\ninterface ErrorEntry {\n id: string;\n label: string;\n message: string;\n /** Sección/pestaña del campo raíz; `undefined` si no está en ninguna. */\n section?: string;\n}\n\nconst TAB_ACTIVE =\n 'border-b-2 border-primary px-3 py-2 text-sm font-medium text-primary focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-primary';\nconst TAB_INACTIVE =\n 'border-b-2 border-transparent px-3 py-2 text-sm text-neutral hover:text-primary focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-primary';\n\n/** Espera antes de volcar el borrador a localStorage (§4.6). */\nconst DRAFT_DEBOUNCE_MS = 800;\n\n/**\n * Fecha del registro tal como llega del backend. No está en el schema\n * (`updated_at` es un `readonly-text`, no un campo editable), así que se\n * prueba la forma habitual; si no hay, devuelve `undefined` y el borrador se\n * ofrece igual.\n */\nfunction readTimestamp(record: Record<string, unknown> | null): number | undefined {\n if (!record) {\n return undefined;\n }\n for (const key of ['updated_at', 'updatedAt', 'updated']) {\n const raw = record[key];\n if (typeof raw === 'number') {\n return raw < 1e12 ? raw * 1000 : raw;\n }\n if (typeof raw === 'string') {\n const parsed = Date.parse(raw);\n if (!Number.isNaN(parsed)) {\n return parsed;\n }\n }\n }\n return undefined;\n}\n\n/**\n * Formulario genérico: lo construye el schema del recurso (base.md §4–§6,\n * §10) con `form()` de Signal Forms sobre un modelo `Record<string, unknown>`.\n *\n * - **Acceso dinámico**: `form()[field.key]` no compila con claves\n * arbitrarias; el modelo se tipa como `Record<string, unknown>` y el acceso\n * vive en `fields/field-node.ts` (`childTree`) y en `form-schema.ts`\n * (`pathAt`), que centralizan los dos casts.\n * - **Sin `debounce('blur')`**: difiere `controlValue → value` (el modelo), no\n * la visibilidad de errores, y con controles custom (`multiselect`, que\n * nunca emite `touch`) se perderían datos en el payload. La validación al\n * perder foco la resuelve FieldHost (`invalid() && touched()`).\n * - `<form novalidate>`: Signal Forms expone `required` como atributo nativo,\n * y la validación nativa del navegador bloquearía el submit sin mostrar\n * nada (campos en `<details>` cerrados).\n */\n@Component({\n selector: 'app-form-view',\n imports: [Button, DrawerForm, EmptyState, FieldHost, PostCreateModal, Skeleton],\n template: `\n @if (schema()) {\n <header class=\"mb-6 flex flex-wrap items-start justify-between gap-4\">\n <div>\n <div class=\"flex flex-wrap items-center gap-3\">\n <h1 class=\"font-display text-2xl font-semibold\">{{ heading() }}</h1>\n @if (dirty()) {\n <span\n class=\"rounded-full bg-accent/15 px-2.5 py-0.5 text-xs font-medium text-accent-text\"\n >\n Sin guardar\n </span>\n }\n </div>\n @if (mode() === 'single') {\n <p class=\"text-sm text-neutral\">Configuración única</p>\n }\n </div>\n @if (mode() !== 'single') {\n <button app-button variant=\"outline\" type=\"button\" (click)=\"back()\">\n Volver al listado\n </button>\n }\n </header>\n\n @switch (status()) {\n @case ('loading') {\n <app-skeleton [lines]=\"8\" [height]=\"48\" />\n }\n @case ('error') {\n <app-empty-state\n title=\"No se pudo cargar el registro\"\n message=\"Revisa tu conexión e intenta de nuevo.\"\n >\n <button app-button variant=\"outline\" type=\"button\" (click)=\"retry()\">Reintentar</button>\n <button app-button variant=\"outline\" type=\"button\" (click)=\"volverDeError()\">\n Volver\n </button>\n </app-empty-state>\n }\n @case ('ready') {\n @if (formTree(); as tree) {\n @if (serverErrors().general; as general) {\n <p\n class=\"mb-4 rounded-lg border border-danger/30 bg-danger/10 px-4 py-3 text-sm text-danger\"\n role=\"alert\"\n >\n {{ general }}\n </p>\n }\n\n <form novalidate [attr.aria-busy]=\"saving()\" (submit)=\"onSubmit($event)\">\n @if (draftOffer(); as offer) {\n <!-- §4.6: se OFRECE, nunca se restaura solo. role=status para\n anunciarlo sin arrebatarle el foco a quien está escribiendo. -->\n <div\n class=\"mb-4 flex flex-wrap items-center gap-3 rounded-lg border border-neutral/20 bg-surface px-4 py-3 text-sm\"\n role=\"status\"\n >\n <span class=\"text-neutral\">\n Hay un borrador guardado {{ draftAge(offer.savedAt) }}.\n </span>\n <button app-button variant=\"outline\" type=\"button\" (click)=\"restoreDraft()\">\n Restaurar\n </button>\n <button app-button variant=\"ghost\" type=\"button\" (click)=\"discardDraft()\">\n Descartar\n </button>\n </div>\n }\n\n @if (summaryErrors().length >= 2) {\n <!-- Con uno solo no se pinta: el foco ya está en el campo y\n el resumen sólo restaría. Con 2+ es la única forma de\n ver que hay varios (§4.11). tabindex -1 para poder\n recibir el foco y que role=alert lo anuncie. -->\n <div\n data-error-summary\n tabindex=\"-1\"\n role=\"alert\"\n aria-labelledby=\"error-summary-title\"\n class=\"mb-4 rounded-lg border border-danger/30 bg-danger/10 px-4 py-3 focus:outline-2 focus:outline-danger\"\n >\n <p id=\"error-summary-title\" class=\"text-sm font-semibold text-danger\">\n Hay {{ summaryErrors().length }} errores que corregir\n </p>\n <ul class=\"mt-2 list-disc space-y-1 pl-5 text-sm\">\n @for (entry of summaryErrors(); track entry.id) {\n <li>\n <a\n href=\"#\"\n class=\"text-danger underline underline-offset-2 hover:text-danger/80\"\n (click)=\"goToError(entry, $event)\"\n >\n {{ entry.label }} — {{ entry.message }}\n </a>\n </li>\n }\n </ul>\n </div>\n }\n\n @if (layoutMode() === 'grid') {\n <div class=\"grid grid-cols-1 gap-x-4 sm:grid-cols-12\">\n @for (field of renderedFields(); track field.key) {\n <div [class]=\"colSpan(field)\">\n <app-field-host\n [field]=\"field\"\n [tree]=\"tree\"\n [exists]=\"exists()\"\n [serverErrors]=\"serverErrors().fields\"\n [resource]=\"resourceId()\"\n />\n </div>\n }\n </div>\n } @else if (unsectionedFields().length > 0) {\n <div class=\"grid grid-cols-1 gap-x-4 sm:grid-cols-12\">\n @for (field of unsectionedFields(); track field.key) {\n <div [class]=\"colSpan(field)\">\n <app-field-host\n [field]=\"field\"\n [tree]=\"tree\"\n [exists]=\"exists()\"\n [serverErrors]=\"serverErrors().fields\"\n [resource]=\"resourceId()\"\n />\n </div>\n }\n </div>\n }\n\n @if (layoutMode() === 'tabs') {\n <div\n role=\"tablist\"\n class=\"mb-4 flex flex-wrap gap-1 border-b border-neutral/20\"\n (keydown)=\"onTablistKeydown($event)\"\n >\n @for (section of sectionViews(); track section.id) {\n <button\n type=\"button\"\n role=\"tab\"\n [id]=\"tabId(section.id)\"\n [class]=\"tabClass(section)\"\n [attr.aria-selected]=\"activeSection() === section.id\"\n [attr.tabindex]=\"activeSection() === section.id ? 0 : -1\"\n (click)=\"selectSection(section.id)\"\n >\n {{ section.label }}\n <!-- Progreso (§4.3, decisión A): el ✓ siempre que esté\n completa; el ⚠ recién después del primer Guardar.\n El glifo va oculto para lectores y el estado se\n cuenta aparte en texto. -->\n @switch (sectionProgress(section)) {\n @case ('problem') {\n <span class=\"ml-1 text-danger\" aria-hidden=\"true\">⚠</span>\n <span class=\"sr-only\"> — con errores</span>\n }\n @case ('complete') {\n <span class=\"ml-1 text-success\" aria-hidden=\"true\">✓</span>\n <span class=\"sr-only\"> — completa</span>\n }\n }\n </button>\n }\n </div>\n }\n\n @if (layoutMode() === 'steps') {\n <ol aria-label=\"Progreso del formulario\" class=\"mb-4 flex flex-wrap gap-2\">\n @for (section of sectionViews(); track section.id; let index = $index) {\n <li\n class=\"rounded-full border px-3 py-1 text-sm\"\n [class.border-primary]=\"activeSection() === section.id\"\n [class.font-semibold]=\"activeSection() === section.id\"\n [attr.aria-current]=\"activeSection() === section.id ? 'step' : null\"\n >\n {{ index + 1 }}. {{ section.label }}\n </li>\n }\n </ol>\n }\n\n @if (layoutMode() === 'tabs' || layoutMode() === 'steps') {\n @for (section of visibleSections(); track section.id) {\n <div\n role=\"tabpanel\"\n [attr.id]=\"layoutMode() === 'steps' ? 'step-panel' : null\"\n class=\"grid grid-cols-1 gap-x-4 sm:grid-cols-12\"\n >\n @for (field of section.fields; track field.key) {\n <div [class]=\"colSpan(field)\">\n <app-field-host\n [field]=\"field\"\n [tree]=\"tree\"\n [exists]=\"exists()\"\n [serverErrors]=\"serverErrors().fields\"\n [resource]=\"resourceId()\"\n />\n </div>\n }\n </div>\n }\n @if (layoutMode() === 'steps' && sectionViews().length > 1) {\n <div class=\"mt-4 flex justify-between gap-3\">\n <button\n app-button\n variant=\"outline\"\n type=\"button\"\n [disabled]=\"isFirstStep()\"\n (click)=\"previousStep()\"\n >\n Anterior\n </button>\n @if (!isLastStep()) {\n <button app-button type=\"button\" (click)=\"nextStep()\">Continuar</button>\n }\n </div>\n }\n } @else if (layoutMode() === 'columns') {\n <div\n [class]=\"\n layoutColumns() === 3\n ? 'grid grid-cols-1 gap-4 md:grid-cols-2 lg:grid-cols-3'\n : 'grid grid-cols-1 gap-4 md:grid-cols-2'\n \"\n >\n @for (section of sectionViews(); track section.id) {\n <section class=\"rounded-xl border border-neutral/20 bg-surface p-4\">\n <h2 class=\"mb-3 text-section font-semibold\">{{ section.label }}</h2>\n <div class=\"grid grid-cols-1 gap-x-4 sm:grid-cols-12\">\n @for (field of section.fields; track field.key) {\n <div [class]=\"colSpan(field)\">\n <app-field-host\n [field]=\"field\"\n [tree]=\"tree\"\n [exists]=\"exists()\"\n [serverErrors]=\"serverErrors().fields\"\n [resource]=\"resourceId()\"\n />\n </div>\n }\n </div>\n </section>\n }\n </div>\n } @else if (layoutMode() === 'sections') {\n <!-- Índice interno (§5.3): en escritorio, índice sticky a la\n izquierda; en móvil, chips horizontales. En tabs las\n pestañas ya hacen de índice, así que esto no se dibuja. -->\n <div class=\"flex flex-col gap-4 lg:flex-row lg:gap-6\">\n <nav\n aria-label=\"Secciones\"\n class=\"sticky top-4 hidden shrink-0 self-start lg:block lg:w-44\"\n >\n <p\n class=\"mb-2 text-xs font-semibold uppercase tracking-wide text-neutral\"\n aria-hidden=\"true\"\n >\n Secciones\n </p>\n <ul class=\"space-y-1 border-l border-neutral/20 pl-3\">\n @for (section of visibleSections(); track section.id) {\n <li>\n <button\n type=\"button\"\n class=\"w-full rounded px-2 py-1 text-left text-sm hover:bg-neutral/10 focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-primary\"\n [class.font-medium]=\"sectionProgress(section) === 'problem'\"\n (click)=\"goToSection(section.id)\"\n >\n {{ section.label }}\n @switch (sectionProgress(section)) {\n @case ('problem') {\n <span class=\"ml-1 text-danger\" aria-hidden=\"true\">⚠</span>\n <span class=\"sr-only\"> — con errores</span>\n }\n @case ('complete') {\n <span class=\"ml-1 text-success\" aria-hidden=\"true\">✓</span>\n <span class=\"sr-only\"> — completa</span>\n }\n }\n </button>\n </li>\n }\n </ul>\n </nav>\n\n <div class=\"min-w-0 flex-1\" [class.lg:grid]=\"layoutMode() === 'columns'\">\n <nav\n aria-label=\"Secciones\"\n class=\"-mx-1 mb-4 flex gap-2 overflow-x-auto px-1 pb-1 lg:hidden\"\n >\n @for (section of visibleSections(); track section.id) {\n <button\n type=\"button\"\n class=\"shrink-0 rounded-full border border-neutral/30 px-3 py-1 text-sm focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-primary\"\n [class.border-danger]=\"sectionProgress(section) === 'problem'\"\n [class.text-danger]=\"sectionProgress(section) === 'problem'\"\n [class.font-medium]=\"sectionProgress(section) === 'problem'\"\n (click)=\"goToSection(section.id)\"\n >\n {{ section.label }}\n </button>\n }\n </nav>\n\n @for (section of visibleSections(); track section.id) {\n <details\n id=\"section-{{ section.id }}\"\n class=\"mb-4 rounded-xl border border-neutral/20 bg-surface scroll-mt-4\"\n [open]=\"isSectionOpen(section.id)\"\n (toggle)=\"setSectionOpen(section.id, $event)\"\n >\n <summary\n tabindex=\"-1\"\n class=\"flex cursor-pointer items-center justify-between gap-2 rounded-xl px-4 py-3 text-section font-semibold hover:bg-neutral/5 focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-primary\"\n >\n <span>{{ section.label }}</span>\n @if (sectionProgress(section) === 'problem') {\n <span class=\"text-xs font-medium text-danger\">Revisar</span>\n }\n </summary>\n <div class=\"grid grid-cols-1 gap-x-4 px-4 pb-4 sm:grid-cols-12\">\n @for (field of section.fields; track field.key) {\n <div [class]=\"colSpan(field)\">\n <app-field-host\n [field]=\"field\"\n [tree]=\"tree\"\n [exists]=\"exists()\"\n [serverErrors]=\"serverErrors().fields\"\n [resource]=\"resourceId()\"\n />\n </div>\n }\n </div>\n </details>\n }\n </div>\n </div>\n }\n\n <!-- Sticky (§5.1): en products son 19 campos y Cancelar/Guardar\n desaparecían al final del scroll. bg-surface es opaco: sin\n él el contenido vería pasar por detrás. -->\n <div\n class=\"sticky bottom-0 z-10 mt-6 flex flex-wrap items-center justify-end gap-3 border-t border-neutral/20 bg-surface pt-4 pb-[calc(1rem+env(safe-area-inset-bottom))]\"\n >\n @if (canDelete()) {\n <div class=\"mr-auto\">\n <button app-button variant=\"danger\" type=\"button\" (click)=\"remove()\">\n Eliminar\n </button>\n </div>\n }\n @if (mode() !== 'create') {\n <!-- §5: descartar lo escrito y volver a lo del servidor. -->\n <button app-button variant=\"ghost\" type=\"button\" (click)=\"restablecer()\">\n Restablecer\n </button>\n }\n <button app-button variant=\"outline\" type=\"button\" (click)=\"back()\">\n Cancelar\n </button>\n <button app-button type=\"submit\" [loading]=\"saving()\" [disabled]=\"!canSave()\">\n {{ saving() ? 'Guardando…' : 'Guardar' }}\n </button>\n </div>\n </form>\n }\n }\n }\n } @else {\n <app-empty-state\n title=\"Recurso no encontrado\"\n message=\"No existe un schema con ese id en el registry.\"\n />\n }\n\n <!-- «Crear nuevo» desde el dropdown de una relación (plan 6.4). Se monta\n y desmonta con el pedido: el Drawer devuelve el foco al botón que lo\n abrió al destruirse. -->\n @if (relationDrawer.request(); as createRequest) {\n @if (drawerSchema(createRequest.resource); as target) {\n <app-drawer-form\n [open]=\"true\"\n [schema]=\"target\"\n mode=\"create\"\n [title]=\"'Crear ' + target.label\"\n (close)=\"relationDrawer.clear()\"\n (saved)=\"relationCreated($event, createRequest)\"\n />\n }\n }\n\n @if (createdWithSummary(); as createdRec) {\n @if (schema()?.postCreate; as postCreateConfig) {\n <app-post-create-modal\n [schema]=\"schema()!\"\n [config]=\"postCreateConfig\"\n [record]=\"createdRec\"\n (closed)=\"onPostCreateClosed()\"\n />\n }\n }\n `,\n})\nexport class FormView {\n /** Puente con el control `relation` (ver `relation-drawer.service`). */\n readonly relationDrawer = inject(RelationDrawerService);\n\n private readonly route = inject(ActivatedRoute);\n private readonly router = inject(Router);\n private readonly nav = inject(ViewNavigationService);\n private readonly api = inject(ApiService);\n private readonly auth = inject(AuthService);\n private readonly notifications = inject(NotificationService);\n private readonly errorMapper = inject(ErrorMapperService);\n /** `form()` puede reconstruirse fuera de contexto (cambio de ruta). */\n private readonly injector = inject(Injector);\n /** Estabilidad de la app: cambio de sección + carga de chunks (ver `goToError`). */\n private readonly appRef = inject(ApplicationRef);\n private readonly params = toSignal(this.route.paramMap, { requireSync: true });\n private readonly routeData = toSignal(this.route.data ?? of({}), { requireSync: true });\n\n readonly schema = computed(() => {\n const resourceId = this.routeData()['resourceId'] ?? this.params().get('id');\n return getSchema(typeof resourceId === 'string' ? resourceId : '');\n });\n /** Recurso actual (lo consumen `image` para pedir la firma, §11.2). */\n readonly resourceId = computed(() => this.schema()?.id ?? '');\n\n /** Registro recién creado para alimentar el modal de resumen post-creación. */\n readonly createdWithSummary = signal<Record<string, unknown> | null>(null);\n\n readonly mode = computed<FormMode>(() => {\n const schema = this.schema();\n if (!schema) {\n return 'single';\n }\n if (this.params().get('key') !== null) {\n return 'edit';\n }\n return schema.kind === 'singleton' ? 'single' : 'create';\n });\n\n readonly heading = computed(() => {\n const schema = this.schema();\n if (!schema) {\n return '';\n }\n switch (this.mode()) {\n case 'create':\n return `Nueva ${schema.label}`;\n case 'edit':\n return `Editar ${schema.label}`;\n case 'single':\n return schema.labelPlural;\n }\n });\n\n readonly status = signal<FormStatus>('loading');\n /**\n * D4 — el error ORIGINAL de `load()` con identidad intacta (el mensaje\n * mostrado es display-only). Se limpia cuando un reintento termina bien.\n */\n private errorCarga: unknown = undefined;\n\n errorDeCarga(): unknown {\n return this.status() === 'error' ? this.errorCarga : undefined;\n }\n /** Estado «guardando» del botón (además del bloqueo interno de `submit`). */\n readonly saving = signal(false);\n /** Errores del backend: rutas de campo + mensaje general (base.md §10). */\n readonly serverErrors = signal<MappedError>({ fields: {} });\n /** Pestaña activa (solo `layout.mode === 'tabs'`). */\n readonly activeSection = signal<string | null>(null);\n private readonly attemptedSteps = signal<ReadonlySet<string>>(new Set());\n\n private readonly openSections = signal<Record<string, boolean>>({});\n private readonly formTree = signal<RootTree | null>(null);\n /** Evita que una respuesta lenta pise el formulario ya reconstruido. */\n private loadEpoch = 0;\n /**\n * Borrador (§4.6 / plan 5.4). `draftOffer` queda en `null` cuando no hay\n * nada que ofrecer: NUNCA se restaura solo, sólo con un clic del usuario.\n */\n private readonly draftOffer = signal<{ savedAt: number; value: Record<string, unknown> } | null>(\n null,\n );\n /** Snapshot de lo que vino del servidor; sólo se guarda si el usuario cambió algo. */\n private loadedSnapshot = '';\n /** Registro original, para «Restablecer» (§5). */\n private loadedRecord: Record<string, unknown> | null = null;\n /** Snapshot de los padres de `dependsOn` y el árbol al que pertenece. */\n private readonly dependsOnSnapshot = new Map<string, string>();\n private dependsOnTree: RootTree | null = null;\n /**\n * Hubo un intento de guardado. Sin este guardia el resumen aparecería de\n * entrada en cualquier formulario con `required` vacíos: `invalid()` es\n * cierto desde el primer render, no sólo después de enviar.\n *\n * También gobierna el `⚠` de las pestañas y el bloqueo al cambiar de\n * pestaña (decisión A): antes del primer Guardar un campo `required` vacío\n * YA es inválido, y andar gritando `⚠` —o trancando la navegación— al\n * usuario que todavía no le erró a nada es ruido, no ayuda.\n */\n readonly submitted = signal(false);\n\n constructor() {\n // `paramMap` emite síncrono al suscribirse: el primer `rebuild()` corre\n // dentro del constructor (contexto de inyección) y los siguientes, al\n // cambiar de ruta (`new` → `edit`), por eso `form()` lleva `injector`.\n this.route.paramMap.subscribe(() => this.rebuild());\n\n // Autoguardado del borrador (§4.6): se dispara con cada cambio del árbol\n // y debouncea 800 ms. `tree().value()` es la lectura que registra la\n // dependencia — sin ella el effect no vería las ediciones del usuario.\n effect((onCleanup) => {\n const tree = this.formTree();\n if (!tree) {\n return;\n }\n const value = tree().value();\n if (this.status() !== 'ready' || JSON.stringify(value) === this.loadedSnapshot) {\n return;\n }\n const timer = setTimeout(() => this.saveDraft(value), DRAFT_DEBOUNCE_MS);\n onCleanup(() => clearTimeout(timer));\n });\n\n // Selección dependiente (guía §1, plan 6.6): cuando el padre cambia, el\n // hijo vuelve a su default. `disabled()` en `form-schema.ts` ya lo dejó\n // ineditable mientras el padre esté vacío; esto es la otra mitad.\n // La lectura de `value()` del padre es la que registra la dependencia.\n effect(() => {\n const tree = this.formTree();\n const schema = this.schema();\n if (tree === null || !schema) {\n this.dependsOnSnapshot.clear();\n this.dependsOnTree = null;\n return;\n }\n if (this.dependsOnTree !== tree) {\n // Árbol reconstruido (cambio de ruta): los valores anteriores ya no\n // significan nada, se empieza de cero para no limpiar nada de más.\n this.dependsOnTree = tree;\n this.dependsOnSnapshot.clear();\n }\n for (const link of collectDependsOn(schema.fields)) {\n let container: RootTree = tree;\n for (const key of link.container) {\n container = groupTree(container, key);\n }\n const parentValue = JSON.stringify(\n childTree<unknown>(container, link.parentKey)().value() ?? null,\n );\n const previous = this.dependsOnSnapshot.get(link.id);\n if (previous !== undefined && previous !== parentValue) {\n childTree<unknown>(container, link.childKey)().value.set(link.resetValue);\n }\n this.dependsOnSnapshot.set(link.id, parentValue);\n }\n });\n }\n\n // ── Layout (base.md §4) ──\n\n readonly layoutMode = computed<LayoutMode>(() => {\n const mode = this.schema()?.layout?.mode ?? 'sections';\n if (!isSupported('layout', mode)) {\n throw new Error(`No existe un descriptor para el layout \"${mode}\".`);\n }\n return mode;\n });\n\n readonly layoutColumns = computed(() => this.schema()?.layout?.columns ?? 2);\n\n /** Campos que la UI sabe dibujar (los de Fase 11 se omiten, no se borran del payload). */\n readonly renderedFields = computed(() =>\n (this.schema()?.fields ?? []).filter((field) => isSupported('field', field.type)),\n );\n\n /**\n * Ítems del resumen de errores (§4.11), vacío hasta que se intente guardar.\n *\n * Recorre el ÁRBOL, no `serverErrors.fields`: el backend sólo puede\n * devolver un `path` por respuesta (`api.md §7`), así que ahí nunca hay más\n * de un ítem. Los errores múltiples los produce la validación del cliente,\n * que `submit()` deja marcados como tocados de una.\n */\n readonly summaryErrors = computed<ErrorEntry[]>(() => {\n if (!this.submitted()) {\n return [];\n }\n const errors = this.collectErrors();\n if (this.layoutMode() !== 'steps') {\n return errors;\n }\n const attempted = this.attemptedSteps();\n return errors.filter((entry) => !entry.section || attempted.has(entry.section));\n });\n\n /** Secciones con al menos un campo dibujable. */\n readonly sectionViews = computed<SectionView[]>(() => {\n const layout = this.schema()?.layout;\n if (!layout) {\n return [];\n }\n const rendered = this.renderedFields();\n return (layout.sections ?? [])\n .map((section) => ({\n id: section.id,\n label: section.label,\n fields: rendered.filter((field) => field.section === section.id),\n }))\n .filter((view) => view.fields.length > 0);\n });\n\n /** Campos sin sección (o con una sección inexistente): siempre visibles. */\n readonly unsectionedFields = computed(() => {\n const ids = new Set(this.sectionViews().map((view) => view.id));\n return this.renderedFields().filter(\n (field) => field.section === undefined || !ids.has(field.section),\n );\n });\n\n /** En pestañas solo se dibuja la activa; en secciones, todas (colapsables). */\n readonly visibleSections = computed<SectionView[]>(() => {\n const sections = this.sectionViews();\n if (this.layoutMode() !== 'tabs' && this.layoutMode() !== 'steps') {\n return sections;\n }\n const active = sections.find((section) => section.id === this.activeSection()) ?? sections[0];\n return active ? [active] : [];\n });\n\n // ── Estado del formulario ──\n\n /** Indicador _dirty_ + aviso al salir (base.md §10). Agrega descendientes. */\n readonly dirty = computed(() => this.formTree()?.().dirty() ?? false);\n\n /** `slug` con `lockAfterCreate` se bloquea solo si el registro ya existe. */\n readonly exists = computed(() => this.mode() !== 'create');\n\n readonly canSave = computed(() => {\n const schema = this.schema();\n if (!schema) return false;\n if (this.layoutMode() === 'steps' && !this.isLastStep()) return false;\n const operation = this.mode() === 'create' ? 'create' : 'update';\n return this.auth.canDoOperation(schema, operation);\n });\n\n readonly canDelete = computed(() => {\n const schema = this.schema();\n return this.mode() === 'edit' && schema && this.auth.canDoOperation(schema, 'remove');\n });\n\n // ── Ciclo de vida ──\n\n /** Reconstruye el formulario al cambiar de ruta o de recurso. */\n private rebuild(): void {\n const schema = this.schema();\n this.serverErrors.set({ fields: {} });\n this.openSections.set({});\n this.activeSection.set(null);\n this.attemptedSteps.set(new Set());\n this.submitted.set(false);\n\n if (!schema) {\n this.formTree.set(null);\n this.status.set('error');\n return;\n }\n\n const model = signal(seedModel(schema));\n const schemaFn = buildFormSchema(schema, this.api, this.recordKey() ?? undefined);\n this.formTree.set(form(model, schemaFn, { injector: this.injector }));\n this.activeSection.set(this.sectionViews()[0]?.id ?? null);\n\n // El snapshot de «sin cambios» arranca en los defaults sembrados; si no,\n // el effect guardaría un borrador con el formulario recién abierto.\n const tree = this.formTree();\n this.loadedSnapshot = JSON.stringify(tree ? tree().value() : {});\n this.loadedRecord = null;\n this.draftOffer.set(null);\n\n if (this.mode() === 'create') {\n // Crear no lee nada: el formulario está listo de entrada.\n this.status.set('ready');\n // En crear solo hay clave de recurso sin key: un borrador previo sí\n // puede aplicar (mismo «nuevo»), así que se ofrece.\n this.offerDraft(null);\n return;\n }\n this.status.set('loading');\n void this.load();\n }\n\n private async load(): Promise<void> {\n const schema = this.schema();\n const tree = this.formTree();\n if (!schema || !tree) {\n return;\n }\n const epoch = ++this.loadEpoch;\n try {\n const record = await firstValueFrom(\n this.api.get<Record<string, unknown>>(schema.endpoint, this.recordKey() ?? undefined),\n );\n if (epoch !== this.loadEpoch) {\n return;\n }\n this.applyRecord(tree, schema, record ?? {});\n this.loadedRecord = record ?? {};\n this.errorCarga = undefined;\n this.status.set('ready');\n // Recién termina la carga: es el único momento en que tiene sentido\n // comparar el borrador con el servidor.\n this.offerDraft(record ?? null);\n } catch (error) {\n if (epoch !== this.loadEpoch) {\n return;\n }\n this.errorCarga = error;\n this.status.set('error');\n }\n }\n\n /**\n * Siembra defaults + registro: los campos ausentes en la respuesta conservan\n * su valor por defecto y los grupos/ítems parciales se fusionan en profundidad\n * (si no, `childTree` no encontraría nodos y revienta al renderizar).\n * `value.set` es programático: no marca `dirty`.\n */\n private applyRecord(\n tree: RootTree,\n schema: ResourceSchema,\n record: Record<string, unknown>,\n ): void {\n tree().value.set(mergeRecord(schema.fields, record));\n // Base de comparación del autoguardado: con esto el effect no escribe un\n // borrador idéntico al servidor apenas termina la carga.\n this.loadedSnapshot = JSON.stringify(tree().value());\n }\n\n // ── Borrador (§4.6, plan 5.4) ──\n\n /** `draft:<recurso>:<key|nuevo>` — aislado por recurso Y registro. */\n private draftStorageKey(): string {\n return `draft:${this.resourceId()}:${this.recordKey() ?? 'nuevo'}`;\n }\n\n /** Best-effort: localStorage puede fallar (modo privado, cuota). */\n private saveDraft(value: Record<string, unknown>): void {\n try {\n localStorage.setItem(this.draftStorageKey(), JSON.stringify({ savedAt: Date.now(), value }));\n } catch {\n /* el borrador nunca debe romper el formulario */\n }\n }\n\n /**\n * Ofrece el borrador si es más reciente que el servidor. Si el registro no\n * trae fecha, cualquier borrador sirve — sólo existe si el usuario cambió\n * algo, porque `loadedSnapshot` filtra los idénticos.\n */\n private offerDraft(serverRecord: Record<string, unknown> | null): void {\n let raw: string | null = null;\n try {\n raw = localStorage.getItem(this.draftStorageKey());\n } catch {\n return;\n }\n if (!raw) {\n return;\n }\n let draft: { savedAt?: number; value?: unknown } | null = null;\n try {\n draft = JSON.parse(raw) as { savedAt?: number; value?: unknown };\n } catch {\n return;\n }\n if (!draft || typeof draft.savedAt !== 'number' || !isRecord(draft.value)) {\n this.clearDraft();\n return;\n }\n\n const serverTs = readTimestamp(serverRecord);\n if (serverTs !== undefined && serverTs >= draft.savedAt) {\n // El servidor ya tiene algo más nuevo: el borrador está vencido.\n this.clearDraft();\n return;\n }\n this.draftOffer.set({ savedAt: draft.savedAt, value: draft.value });\n }\n\n /** Restaurar es una acción explícita del usuario (§4.6: nunca en silencio). */\n restoreDraft(): void {\n const offer = this.draftOffer();\n const tree = this.formTree();\n const schema = this.schema();\n if (!offer || !tree || !schema) {\n return;\n }\n this.applyRecord(tree, schema, offer.value);\n this.draftOffer.set(null);\n this.notifications.info('Borrador restaurado.');\n }\n\n /** Descarta el borrador sin tocar lo cargado. */\n discardDraft(): void {\n this.clearDraft();\n this.draftOffer.set(null);\n }\n\n private clearDraft(): void {\n try {\n localStorage.removeItem(this.draftStorageKey());\n } catch {\n /* idem */\n }\n }\n\n /** «hace 1 min» — relativo y discreto (§4.6). */\n draftAge(savedAt: number): string {\n const minutes = Math.max(1, Math.round((Date.now() - savedAt) / 60_000));\n if (minutes < 60) {\n return `hace ${minutes} min`;\n }\n const hours = Math.round(minutes / 60);\n return `hace ${hours} ${hours === 1 ? 'hora' : 'horas'}`;\n }\n\n /**\n * Botón «Restablecer» (§5): vuelve a lo que vino del servidor y limpia el\n * borrador. Es la única vía de descartar cambios en modo edición.\n */\n restablecer(): void {\n const tree = this.formTree();\n const schema = this.schema();\n if (!tree || !schema) {\n return;\n }\n this.applyRecord(tree, schema, this.loadedRecord ?? seedModel(schema));\n this.serverErrors.set({ fields: {} });\n this.submitted.set(false);\n this.attemptedSteps.set(new Set());\n this.draftOffer.set(null);\n this.clearDraft();\n this.notifications.info('Se restauraron los valores originales.');\n }\n\n retry(): void {\n void this.load();\n }\n\n /**\n * D4 — «way back» desde el estado de error: en la ruta directa\n * (`/:id/new`, `/:id/:key/edit`) no hay output que escape, así que el\n * servicio declarado navega al origen registrado o a la raíz del recurso.\n */\n protected volverDeError(): void {\n void this.nav.back();\n }\n\n // ── Guardado ──\n\n async onSubmit(event: Event): Promise<void> {\n event.preventDefault();\n const tree = this.formTree();\n if (!tree || this.saving()) {\n return;\n }\n this.serverErrors.set({ fields: {} });\n this.saving.set(true);\n try {\n const ok = await submit(tree, {\n // `null` = sin errores: los del backend salen por `applyServerErrors`\n // (también si `submit` dio `true`), para no dejar el form invalidado.\n action: async () => {\n await this.persist(tree);\n return null;\n },\n onInvalid: () => this.revealErrors(),\n });\n if (!ok || this.hasServerErrors()) {\n this.revealErrors();\n }\n } finally {\n this.saving.set(false);\n }\n }\n\n /**\n * create/update según el modo; nunca lanza (los errores van a `serverErrors`).\n *\n * **Regla de éxito por flujo** (guía §6: «toast y volver a la lista o\n * quedarse según el flujo») — es la misma para los cuatro recursos:\n *\n * - **Crear** desde la pantalla completa → **vuelve a la lista** del\n * recurso: el registro quedó creado y no hay nada más que editar acá.\n * - **Editar** → **se queda** y recarga lo guardado: el usuario sigue\n * trabajando sobre el mismo registro.\n * - **Eliminar** → **vuelve a la lista** (`back()`).\n * - **Desde un drawer** (crear una relación desde el dropdown, edición\n * rápida desde la lista) → **se queda** donde estaba: salir destruiría\n * el contexto que el drawer existía para conservar. Ver\n * `drawer-form.ts` y `relation-drawer.service.ts`.\n *\n * El toast se emite **antes** de navegar y el contenedor vive en el\n * layout, así que sobrevive al cambio de ruta y el mensaje se lee igual.\n */\n private async persist(tree: RootTree): Promise<void> {\n const schema = this.schema();\n if (!schema) {\n return;\n }\n const payload = buildPayload(schema, tree);\n\n try {\n if (this.mode() === 'create') {\n const created = await firstValueFrom(\n this.api.create<Record<string, unknown>>(schema.endpoint, payload),\n );\n // Limpia `dirty` antes de navegar o abrir modal: si no, el guard pediría confirmar.\n tree().reset();\n // El borrador ya no aporta nada (§5.5).\n this.clearDraft();\n this.draftOffer.set(null);\n\n if (schema.postCreate?.mode === 'modal') {\n this.createdWithSummary.set(created ?? (payload as Record<string, unknown>));\n return;\n }\n\n this.notifications.success('Registro creado.');\n // Vuelve al listado del recurso (el «inicio» de la colección); al\n // reentrar en `:id/new` el form se reconstruye limpio.\n void this.router.navigate(['/', schema.id]);\n return;\n }\n\n await firstValueFrom(\n this.api.update<Record<string, unknown>>(\n schema.endpoint,\n this.mode() === 'edit' ? (this.recordKey() ?? undefined) : undefined,\n payload,\n ),\n );\n this.notifications.success('Registro guardado.');\n tree().reset();\n // Guardado exitoso = fin del borrador (§5.5). El snapshot se actualiza\n // para que el effect no lo reescriba con los valores ya persistidos.\n this.clearDraft();\n this.draftOffer.set(null);\n this.loadedSnapshot = JSON.stringify(tree().value());\n } catch (err) {\n this.applyServerErrors(this.errorMapper.map(err));\n return;\n }\n\n // Recarga del valor guardado (base.md §10); su fallo no invalida el guardado.\n try {\n const record = await firstValueFrom(\n this.api.get<Record<string, unknown>>(schema.endpoint, this.recordKey() ?? undefined),\n );\n this.applyRecord(tree, schema, record ?? {});\n tree().reset();\n } catch {\n this.notifications.error('No se pudo recargar el registro.');\n }\n }\n\n /**\n * Errores del backend → mapa por ruta; el mensaje se queda en `general`\n * sólo si su raíz (`address`, `faq[2]` → `faq`) no es un campo dibujado\n * (base.md §10). Los hijos anidados se resuelven en el host con\n * `serverErrors[prefix + key]`.\n */\n private applyServerErrors(mapped: MappedError): void {\n const rendered = new Set(this.renderedFields().map((field) => field.key));\n const fields: Record<string, string> = {};\n const loose: string[] = [];\n if (mapped.general) {\n loose.push(mapped.general);\n }\n for (const [path, message] of Object.entries(mapped.fields)) {\n const root = path.split(/[.[]/)[0];\n if (rendered.has(root)) {\n fields[path] = message;\n } else {\n // Campo no dibujado u oculto: el usuario tiene que verlo.\n loose.push(message);\n }\n }\n this.serverErrors.set({ fields, general: loose.length > 0 ? loose.join(' ') : undefined });\n }\n\n private hasServerErrors(): boolean {\n const current = this.serverErrors();\n return current.general !== undefined || Object.keys(current.fields).length > 0;\n }\n\n /** Muestra todos los errores: abre secciones, activa la pestaña con problemas. */\n private revealErrors(): void {\n // Marca el intento: habilita `summaryErrors` (vacío hasta acá).\n this.submitted.set(true);\n const sections = this.sectionViews();\n if (this.layoutMode() === 'sections') {\n this.openSections.update((open) => {\n const next = { ...open };\n for (const section of sections) {\n next[section.id] = true;\n }\n return next;\n });\n }\n const withProblem = sections.find((section) => this.sectionHasProblem(section));\n if (withProblem && this.layoutMode() === 'tabs') {\n this.activeSection.set(withProblem.id);\n }\n if (this.layoutMode() === 'steps') {\n const invalidSteps = sections\n .filter((section) => this.sectionHasProblem(section))\n .map((section) => section.id);\n this.attemptedSteps.update((attempted) => new Set([...attempted, ...invalidSteps]));\n if (withProblem) {\n this.activeSection.set(withProblem.id);\n }\n }\n // Una vez abiertas/activadas las secciones con errores, el navegador\n // debe IR al primer campo con problema (no quedarse arriba del form).\n // `setTimeout`: los mensajes se pintan en la pasada de change detection\n // posterior al submit.\n setTimeout(() => {\n if (this.summaryErrors().length >= 2) {\n this.focusSummary();\n } else {\n this.scrollToFirstError();\n }\n });\n }\n\n /** Scroll + focus al primer campo visible con mensaje de error. */\n private scrollToFirstError(): void {\n for (const wrap of document.querySelectorAll<HTMLElement>('[data-field]')) {\n if (!wrap.querySelector('p[role=\"alert\"]')) {\n continue;\n }\n wrap.scrollIntoView({ behavior: 'smooth', block: 'center' });\n const focusable = wrap.querySelector<HTMLElement>(\n 'input:not([type=\"hidden\"]), textarea, select, [contenteditable=\"true\"]',\n );\n focusable?.focus({ preventScroll: true });\n return;\n }\n }\n\n // ── Borrado ──\n\n async remove(): Promise<void> {\n const schema = this.schema();\n const key = this.recordKey();\n if (!schema || this.mode() !== 'edit' || !key) {\n return;\n }\n const title = this.title();\n const accepted = await this.notifications.confirm({\n title: title ? `Eliminar «${title}»` : `Eliminar ${schema.label.toLowerCase()}`,\n message: 'Esta acción no se puede deshacer.',\n confirmLabel: 'Eliminar',\n cancelLabel: 'Cancelar',\n });\n if (!accepted) {\n return;\n }\n try {\n await firstValueFrom(this.api.remove(schema.endpoint, key));\n this.notifications.success('Registro eliminado.');\n this.back();\n } catch (err) {\n // El motivo (p. ej. «categoría con productos») lo decide el backend.\n this.notifications.error(this.errorMapper.messageOf(err));\n }\n }\n\n /** Consultado por `unsavedChangesGuard` al salir de la ruta. */\n async canDeactivate(): Promise<boolean> {\n if (!this.dirty()) {\n return true;\n }\n return this.notifications.confirm({\n title: 'Cambios sin guardar',\n message: 'Si sales ahora, se perderán los cambios realizados.',\n confirmLabel: 'Salir sin guardar',\n cancelLabel: 'Seguir editando',\n });\n }\n\n back(): void {\n void this.router.navigate(['/', this.schema()?.id]);\n }\n\n onPostCreateClosed(): void {\n const schema = this.schema();\n this.createdWithSummary.set(null);\n if (schema) {\n void this.router.navigate(['/', schema.id]);\n }\n }\n\n // ── Helpers de template ──\n\n colSpan(field: FieldSchema): string {\n return colSpanClass(field.width);\n }\n\n /** Id estable de una pestaña: ancla del foco programático. */\n tabId(sectionId: string): string {\n return `section-tab-${sectionId}`;\n }\n\n /**\n * `role=\"tab\"` exige moverse con `ArrowLeft`/`ArrowRight`/`Home`/`End`\n * (WAI-ARIA APG). El foco se reengancha al que quedó ACTIVO, no al que se\n * pidió: si `selectSection()` bloquea el cambio por validación (§4.9,\n * decisión A) la selección no se mueve y el foco se queda donde estaba.\n *\n * Este handler es también lo que hace correcto el `Tab`: con el roving\n * `tabindex` sólo la pestaña activa entra en el orden de tabulación, así\n * que `Tab` va del tablist al primer campo de la sección en lugar de\n * recorrer las pestañas apagadas.\n */\n onTablistKeydown(event: KeyboardEvent): void {\n const tabs = this.sectionViews();\n if (tabs.length < 2) {\n return;\n }\n const current = Math.max(\n tabs.findIndex((section) => section.id === this.activeSection()),\n 0,\n );\n\n let next: number;\n switch (event.key) {\n case 'ArrowRight':\n next = (current + 1) % tabs.length;\n break;\n case 'ArrowLeft':\n next = (current - 1 + tabs.length) % tabs.length;\n break;\n case 'Home':\n next = 0;\n break;\n case 'End':\n next = tabs.length - 1;\n break;\n default:\n return;\n }\n\n event.preventDefault();\n this.selectSection(tabs[next].id);\n const active = this.activeSection() ?? tabs[current].id;\n document.getElementById(this.tabId(active))?.focus();\n }\n\n tabClass(section: SectionView): string {\n return section.id === this.activeSection() ? TAB_ACTIVE : TAB_INACTIVE;\n }\n\n isFirstStep(): boolean {\n const sections = this.sectionViews();\n return sections.findIndex((section) => section.id === this.activeSection()) <= 0;\n }\n\n isLastStep(): boolean {\n const sections = this.sectionViews();\n const index = sections.findIndex((section) => section.id === this.activeSection());\n return sections.length === 0 || index >= sections.length - 1;\n }\n\n previousStep(): void {\n const sections = this.sectionViews();\n const current = sections.findIndex((section) => section.id === this.activeSection());\n if (current > 0) {\n this.activeSection.set(sections[current - 1].id);\n this.scrollToStep();\n }\n }\n\n nextStep(): void {\n const sections = this.sectionViews();\n const current = sections.findIndex((section) => section.id === this.activeSection());\n if (current < 0 || current >= sections.length - 1) {\n return;\n }\n if (current === 0 && this.unsectionedHasLocalErrors()) {\n this.revealUnsectionedErrors();\n return;\n }\n const section = sections[current];\n if (this.sectionHasLocalErrors(section.id)) {\n this.attemptedSteps.update((attempted) => new Set([...attempted, section.id]));\n this.revealSectionErrors(section.id);\n return;\n }\n this.activeSection.set(sections[current + 1].id);\n this.scrollToStep();\n }\n\n private unsectionedHasLocalErrors(): boolean {\n const tree = this.formTree();\n return (\n tree !== null &&\n this.unsectionedFields().some((field) => childTree<unknown>(tree, field.key)().invalid())\n );\n }\n\n private revealUnsectionedErrors(): void {\n const tree = this.formTree();\n if (!tree) {\n return;\n }\n this.submitted.set(true);\n for (const field of this.unsectionedFields()) {\n childTree<unknown>(tree, field.key)().markAsTouched();\n }\n setTimeout(() => this.scrollToFirstError());\n }\n\n private scrollToStep(): void {\n setTimeout(() =>\n document.getElementById('step-panel')?.scrollIntoView({\n behavior: 'smooth',\n block: 'start',\n }),\n );\n }\n\n selectSection(id: string): void {\n const current = this.activeSection();\n // §4.9 / plan 5.7, decisión A: sólo se tranc después del primer envío.\n // Antes, `invalid()` es cierto por los `required` vacíos y no se le puede\n // prohibir al usuario recorrer las pestañas para ver qué hay en cada una.\n if (\n this.submitted() &&\n current !== null &&\n current !== id &&\n this.sectionHasLocalErrors(current)\n ) {\n this.revealSectionErrors(current);\n return;\n }\n this.activeSection.set(id);\n }\n\n /** Sólo validación local: los errores del backend no bloquean navegar. */\n private sectionHasLocalErrors(sectionId: string): boolean {\n const tree = this.formTree();\n const section = this.sectionViews().find((view) => view.id === sectionId);\n if (!tree || !section) {\n return false;\n }\n return section.fields.some((field) => childTree<unknown>(tree, field.key)().invalid());\n }\n\n /**\n * Marca la sección como tocada (los mensajes sólo aparecen una vez tocado)\n * y devuelve el foco al primer campo con problema, sin cambiar de pestaña.\n */\n private revealSectionErrors(sectionId: string): void {\n const tree = this.formTree();\n const section = this.sectionViews().find((view) => view.id === sectionId);\n if (!tree || !section) {\n return;\n }\n this.submitted.set(true);\n this.attemptedSteps.update((attempted) => new Set([...attempted, sectionId]));\n for (const field of section.fields) {\n childTree<unknown>(tree, field.key)().markAsTouched();\n }\n // Los mensajes se pintan en la pasada de change detection posterior.\n setTimeout(() => this.scrollToFirstError());\n }\n\n isSectionOpen(id: string): boolean {\n // Las secciones arrancan abiertas (base.md §4).\n return this.openSections()[id] ?? true;\n }\n\n setSectionOpen(id: string, event: Event): void {\n const open = (event.target as HTMLDetailsElement).open;\n this.openSections.update((sections) => ({ ...sections, [id]: open }));\n }\n\n /**\n * Índice lateral (§5.3) → sección. Abre el `<details>` primero: si estaba\n * plegado, saltar antes daría con un elemento sin altura. El foco va al\n * `<summary>` para que el teclado siga teniendo dónde continuar.\n */\n goToSection(id: string): void {\n this.openSections.update((open) => ({ ...open, [id]: true }));\n setTimeout(() => {\n const section = document.getElementById(`section-${id}`);\n if (!section) {\n return;\n }\n section.scrollIntoView({ behavior: 'smooth', block: 'start' });\n section.querySelector<HTMLElement>('summary')?.focus({ preventScroll: true });\n });\n }\n\n /** Invalido (propio, anidado o del backend): marca la pestaña/sección. */\n fieldHasProblem(field: FieldSchema): boolean {\n const root = field.key;\n const hasServerError = Object.keys(this.serverErrors().fields).some(\n (path) => path === root || path.startsWith(`${root}.`) || path.startsWith(`${root}[`),\n );\n if (hasServerError) {\n return true;\n }\n const tree = this.formTree();\n return tree !== null && childTree<unknown>(tree, root)().invalid();\n }\n\n sectionHasProblem(section: SectionView): boolean {\n return section.fields.some((field) => this.fieldHasProblem(field));\n }\n\n /**\n * Estado de progreso de una sección (§4.3 / decisión A).\n *\n * - `complete`: sin problemas → `✓` siempre, es progreso positivo.\n * - `problem`: con problemas **después** del primer envío → `⚠`.\n * - `pending`: con problemas pero todavía no se envió → sin marca, para\n * no amedrentar a quien recién abrió el formulario.\n */\n sectionProgress(section: SectionView): 'problem' | 'complete' | 'pending' {\n if (!this.sectionHasProblem(section)) {\n return 'complete';\n }\n if (this.layoutMode() === 'steps' && !this.attemptedSteps().has(section.id)) {\n return 'pending';\n }\n return this.submitted() ? 'problem' : 'pending';\n }\n\n // ── Resumen de errores (§4.11) ──\n\n /**\n * Un ítem por campo con problema, recorriendo el árbol. Los `group` se\n * descienden para quedar en la hoja (ahí está el mensaje real); los\n * compuestos (`list`, `key-value`, `string-list`) se listan como uno,\n * porque el envoltorio es el único ancla a la que se puede saltar.\n */\n private collectErrors(): ErrorEntry[] {\n const tree = this.formTree();\n if (!tree) {\n return [];\n }\n const server = this.serverErrors().fields;\n const entries: ErrorEntry[] = [];\n const seen = new Set<string>();\n\n const walk = (\n fields: readonly FieldSchema[],\n subtree: RootTree,\n inheritedSection?: string,\n ): void => {\n for (const field of fields) {\n const section = field.section ?? inheritedSection;\n const state = childTree<unknown>(subtree, field.key)();\n const serverMessage = this.serverMessageFor(field.key, server);\n\n if (field.type === 'group') {\n const before = entries.length;\n walk(field.fields, groupTree(subtree, field.key), section);\n // El grupo sigue marcado pero ningún hijo aportó el motivo.\n if (entries.length === before && (serverMessage || state.invalid())) {\n seen.add(state.name());\n entries.push({\n id: state.name(),\n label: field.label,\n message: serverMessage ?? 'Revisa este campo',\n section,\n });\n }\n continue;\n }\n\n if (!state.invalid() && !serverMessage) {\n continue;\n }\n const composite =\n field.type === 'list' || field.type === 'key-value' || field.type === 'string-list';\n const id = state.name();\n if (seen.has(id)) {\n continue;\n }\n seen.add(id);\n entries.push({\n id,\n label: field.label,\n message:\n serverMessage ??\n state.errors()[0]?.message ??\n (composite ? 'Hay elementos con errores' : 'Revisa este campo'),\n section,\n });\n }\n };\n\n walk(this.renderedFields(), tree);\n return entries;\n }\n\n /** Mensaje del backend para una ruta raíz (`faq` cubre `faq[2].answer`). */\n private serverMessageFor(root: string, server: Record<string, string>): string | undefined {\n for (const [path, message] of Object.entries(server)) {\n if (path === root || path.startsWith(`${root}.`) || path.startsWith(`${root}[`)) {\n return message;\n }\n }\n return undefined;\n }\n\n /**\n * Enlace del resumen → campo. Cambia de pestaña/sección si hace falta y\n * sólo después salta: en modo tabs el campo de otra pestaña todavía no\n * existe en el DOM en este punto.\n *\n * Esperar la estabilidad sustituye al `setTimeout` anterior: cubre de una\n * vez la pasada de change detection del cambio de sección **y** la carga\n * del chunk perezoso del descriptor (`load()`, R3.4). Sin esa espera el\n * foco se pierde sobre el envoltorio, que sí existe pero todavía no tiene\n * control dentro.\n */\n async goToError(entry: ErrorEntry, event: Event): Promise<void> {\n event.preventDefault();\n if (entry.section) {\n if (this.layoutMode() === 'tabs' || this.layoutMode() === 'steps') {\n this.activeSection.set(entry.section);\n } else {\n this.openSections.update((open) => ({ ...open, [entry.section as string]: true }));\n }\n }\n await this.appRef.whenStable();\n this.focusField(entry.id);\n }\n\n /** Scroll + focus al campo con `data-field=\"id\"` (mismo selector que `scrollToFirstError`). */\n private focusField(id: string): void {\n const wrap = document.querySelector<HTMLElement>(`[data-field=\"${id}\"]`);\n if (!wrap) {\n return;\n }\n wrap.scrollIntoView({ behavior: 'smooth', block: 'center' });\n const focusable = wrap.querySelector<HTMLElement>(\n 'input:not([type=\"hidden\"]), textarea, select, [contenteditable=\"true\"]',\n );\n focusable?.focus({ preventScroll: true });\n }\n\n /**\n * Con 2+ errores el foco va al resumen: es la única forma de que el usuario\n * sepa que hay varios. Con uno solo se lo roba el campo, que ya está a la\n * vista — por eso el resumen ni se pinta entonces.\n */\n private focusSummary(): void {\n const summary = document.querySelector<HTMLElement>('[data-error-summary]');\n summary?.scrollIntoView({ behavior: 'smooth', block: 'start' });\n summary?.focus({ preventScroll: true });\n }\n\n private title(): string {\n const tree = this.formTree();\n const schema = this.schema();\n if (!tree || !schema) {\n return '';\n }\n const value = tree().value()[schema.titleField];\n return typeof value === 'string' ? value : '';\n }\n\n private recordKey(): string | null {\n return this.params().get('key');\n }\n\n /** Schema destino del panel «Crear nuevo»; `undefined` si no existe. */\n drawerSchema(resource: string): ResourceSchema | undefined {\n return getSchema(resource);\n }\n\n /**\n * Cerró el panel con un registro nuevo: se cierra, y si vino con clave se\n * le pasa a la relación que lo pidió (recarga sus opciones y lo selecciona).\n */\n relationCreated(record: Record<string, unknown>, request: RelationCreateRequest): void {\n const target = getSchema(request.resource);\n const key = target === undefined ? undefined : record[target.keyField];\n this.relationDrawer.clear();\n if (key === undefined || key === null || key === '') {\n return;\n }\n request.onCreated(String(key));\n }\n}\n"],"mappings":"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"}
|