@tinya-code/gas-admin-ui 0.2.0-alpha.12 → 0.2.0-alpha.15

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.
Files changed (369) hide show
  1. package/README.md +222 -0
  2. package/ng-package.json +7 -0
  3. package/package.json +3 -15
  4. package/src/lib/actions/action-registry.spec.ts +52 -0
  5. package/src/lib/actions/action-registry.ts +126 -0
  6. package/src/lib/actions/action-service.spec.ts +215 -0
  7. package/src/lib/actions/action-service.ts +87 -0
  8. package/src/lib/actions/descriptors.spec.ts +155 -0
  9. package/src/lib/actions/descriptors.ts +154 -0
  10. package/src/lib/columns/column-cell.ts +33 -0
  11. package/src/lib/columns/column-defaults.spec.ts +99 -0
  12. package/src/lib/columns/column-defaults.ts +84 -0
  13. package/src/lib/columns/column-format.spec.ts +274 -0
  14. package/src/lib/columns/descriptors.ts +113 -0
  15. package/src/lib/columns/format-badge.ts +51 -0
  16. package/src/lib/columns/format-boolean.ts +58 -0
  17. package/src/lib/columns/format-currency.ts +30 -0
  18. package/src/lib/columns/format-date.ts +35 -0
  19. package/src/lib/columns/format-number.ts +32 -0
  20. package/src/lib/columns/format-text.ts +23 -0
  21. package/src/lib/columns/format-thumbnail.ts +63 -0
  22. package/src/lib/core/adapters/firebase-auth.adapter.ts +6 -0
  23. package/src/lib/core/guards/auth.guard.spec.ts +88 -0
  24. package/src/lib/core/guards/auth.guard.ts +48 -0
  25. package/src/lib/core/guards/unsaved-changes.guard.ts +16 -0
  26. package/src/lib/core/interceptors/api-error.interceptor.spec.ts +182 -0
  27. package/src/lib/core/interceptors/api-error.interceptor.ts +87 -0
  28. package/src/lib/core/models/api.model.ts +104 -0
  29. package/src/lib/core/models/schema-descriptions.ts +118 -0
  30. package/src/lib/core/models/schema.model.ts +1097 -0
  31. package/src/lib/core/registry/composition-validator.spec.ts +358 -0
  32. package/src/lib/core/registry/composition-validator.ts +270 -0
  33. package/src/lib/core/registry/field-validators.spec.ts +199 -0
  34. package/src/lib/core/registry/field-validators.ts +440 -0
  35. package/src/lib/core/registry/render-host.spec.ts +208 -0
  36. package/src/lib/core/registry/render-host.ts +646 -0
  37. package/src/lib/core/registry/render-registry.spec.ts +96 -0
  38. package/src/lib/core/registry/render-registry.ts +312 -0
  39. package/src/lib/core/registry/schema-catalog.ts +156 -0
  40. package/src/lib/core/services/api.service.spec.ts +1142 -0
  41. package/src/lib/core/services/api.service.ts +371 -0
  42. package/src/lib/core/services/auth.service.spec.ts +653 -0
  43. package/src/lib/core/services/auth.service.ts +503 -0
  44. package/src/lib/core/services/config.service.spec.ts +121 -0
  45. package/src/lib/core/services/config.service.ts +119 -0
  46. package/src/lib/core/services/data-source.service.spec.ts +179 -0
  47. package/src/lib/core/services/data-source.service.ts +142 -0
  48. package/src/lib/core/services/error-mapper.service.spec.ts +50 -0
  49. package/src/lib/core/services/error-mapper.service.ts +49 -0
  50. package/src/lib/core/services/notification.service.ts +73 -0
  51. package/src/lib/core/services/relation-drawer.service.ts +41 -0
  52. package/src/lib/core/services/theme.service.spec.ts +106 -0
  53. package/src/lib/core/services/theme.service.ts +86 -0
  54. package/src/lib/core/services/transport.spec.ts +198 -0
  55. package/src/lib/core/services/transport.ts +164 -0
  56. package/src/lib/core/services/upload.service.ts +81 -0
  57. package/src/lib/core/testing/categories.schema.ts +125 -0
  58. package/src/lib/core/testing/orders.schema.ts +372 -0
  59. package/src/lib/core/testing/products.schema.ts +258 -0
  60. package/src/lib/core/testing/site.schema.ts +195 -0
  61. package/src/lib/core/testing/test-catalog.ts +21 -0
  62. package/src/lib/core/tokens.ts +112 -0
  63. package/src/lib/core/utils/clipboard.spec.ts +55 -0
  64. package/src/lib/core/utils/clipboard.ts +90 -0
  65. package/src/lib/core/utils/context-resolver.spec.ts +95 -0
  66. package/src/lib/core/utils/context-resolver.ts +101 -0
  67. package/src/lib/core/utils/response-unwrapper.spec.ts +70 -0
  68. package/src/lib/core/utils/response-unwrapper.ts +103 -0
  69. package/src/lib/core/utils/source-options.spec.ts +96 -0
  70. package/src/lib/core/utils/source-options.ts +54 -0
  71. package/src/lib/core/utils/template-interpolator.spec.ts +59 -0
  72. package/src/lib/core/utils/template-interpolator.ts +56 -0
  73. package/src/lib/core/view-navigation/view-navigation.service.spec.ts +207 -0
  74. package/src/lib/core/view-navigation/view-navigation.service.ts +132 -0
  75. package/src/lib/fields/boolean/boolean.descriptor.ts +11 -0
  76. package/src/lib/fields/boolean/boolean.ts +40 -0
  77. package/src/lib/fields/color/color.descriptor.ts +12 -0
  78. package/src/lib/fields/color/color.ts +34 -0
  79. package/src/lib/fields/currency/currency.descriptor.ts +12 -0
  80. package/src/lib/fields/currency/currency.ts +67 -0
  81. package/src/lib/fields/date/date.descriptor.ts +12 -0
  82. package/src/lib/fields/date/date.ts +36 -0
  83. package/src/lib/fields/descriptors.ts +122 -0
  84. package/src/lib/fields/display/display-badge.ts +65 -0
  85. package/src/lib/fields/display/display-date.ts +39 -0
  86. package/src/lib/fields/display/display-defaults.ts +84 -0
  87. package/src/lib/fields/display/display-file.ts +41 -0
  88. package/src/lib/fields/display/display-image.ts +54 -0
  89. package/src/lib/fields/display/display-list.ts +107 -0
  90. package/src/lib/fields/display/display-number.ts +41 -0
  91. package/src/lib/fields/display/display-text.ts +42 -0
  92. package/src/lib/fields/display/display.spec.ts +242 -0
  93. package/src/lib/fields/dropdown/dropdown.descriptor.ts +12 -0
  94. package/src/lib/fields/dropdown/dropdown.ts +45 -0
  95. package/src/lib/fields/email/email.descriptor.ts +12 -0
  96. package/src/lib/fields/email/email.ts +28 -0
  97. package/src/lib/fields/field-aria.ts +60 -0
  98. package/src/lib/fields/field-host/field-host.spec.ts +118 -0
  99. package/src/lib/fields/field-host/field-host.ts +47 -0
  100. package/src/lib/fields/field-node.ts +88 -0
  101. package/src/lib/fields/file/file.descriptor.ts +14 -0
  102. package/src/lib/fields/file/file.spec.ts +134 -0
  103. package/src/lib/fields/file/file.ts +281 -0
  104. package/src/lib/fields/group/group.descriptor.ts +21 -0
  105. package/src/lib/fields/group/group.ts +110 -0
  106. package/src/lib/fields/image/image.descriptor.ts +12 -0
  107. package/src/lib/fields/image/image.spec.ts +117 -0
  108. package/src/lib/fields/image/image.ts +281 -0
  109. package/src/lib/fields/key-value/key-value.descriptor.ts +11 -0
  110. package/src/lib/fields/key-value/key-value.ts +283 -0
  111. package/src/lib/fields/list/list.descriptor.ts +21 -0
  112. package/src/lib/fields/list/list.ts +376 -0
  113. package/src/lib/fields/multiselect/multiselect.descriptor.ts +11 -0
  114. package/src/lib/fields/multiselect/multiselect.spec.ts +142 -0
  115. package/src/lib/fields/multiselect/multiselect.ts +152 -0
  116. package/src/lib/fields/number/number.descriptor.ts +12 -0
  117. package/src/lib/fields/number/number.ts +40 -0
  118. package/src/lib/fields/phone/phone.descriptor.ts +12 -0
  119. package/src/lib/fields/phone/phone.ts +46 -0
  120. package/src/lib/fields/readonly-text/readonly-text.descriptor.ts +12 -0
  121. package/src/lib/fields/readonly-text/readonly-text.ts +58 -0
  122. package/src/lib/fields/relation/relation.descriptor.ts +12 -0
  123. package/src/lib/fields/relation/relation.spec.ts +193 -0
  124. package/src/lib/fields/relation/relation.ts +224 -0
  125. package/src/lib/fields/rich-select/rich-select.descriptor.ts +12 -0
  126. package/src/lib/fields/rich-select/rich-select.ts +175 -0
  127. package/src/lib/fields/select/radio-mode.ts +25 -0
  128. package/src/lib/fields/select/select.descriptor.ts +12 -0
  129. package/src/lib/fields/select/select.spec.ts +162 -0
  130. package/src/lib/fields/select/select.ts +210 -0
  131. package/src/lib/fields/slug/slug.descriptor.ts +19 -0
  132. package/src/lib/fields/slug/slug.ts +67 -0
  133. package/src/lib/fields/string-list/string-list.descriptor.ts +11 -0
  134. package/src/lib/fields/string-list/string-list.ts +222 -0
  135. package/src/lib/fields/text/text.descriptor.ts +12 -0
  136. package/src/lib/fields/text/text.ts +35 -0
  137. package/src/lib/fields/textarea/textarea.descriptor.ts +12 -0
  138. package/src/lib/fields/textarea/textarea.spec.ts +117 -0
  139. package/src/lib/fields/textarea/textarea.ts +126 -0
  140. package/src/lib/fields/time/time.descriptor.ts +12 -0
  141. package/src/lib/fields/time/time.ts +28 -0
  142. package/src/lib/fields/unknown-render/unknown-render.ts +49 -0
  143. package/src/lib/fields/url/url.descriptor.ts +12 -0
  144. package/src/lib/fields/url/url.ts +53 -0
  145. package/src/lib/gas-admin-ui.spec.ts +21 -0
  146. package/src/lib/gas-admin-ui.ts +9 -0
  147. package/src/lib/layouts/descriptors.ts +9 -0
  148. package/src/lib/pages/dashboard/dashboard.ts +167 -0
  149. package/src/lib/pages/detail-view/detail-view.spec.ts +333 -0
  150. package/src/lib/pages/detail-view/detail-view.ts +570 -0
  151. package/src/lib/pages/drawer-form/drawer-form.ts +228 -0
  152. package/src/lib/pages/form-view/form-model.ts +88 -0
  153. package/src/lib/pages/form-view/form-schema.ts +255 -0
  154. package/src/lib/pages/form-view/form-view.ts +1622 -0
  155. package/src/lib/pages/list-view/list-view.formats.spec.ts +214 -0
  156. package/src/lib/pages/list-view/list-view.ts +882 -0
  157. package/src/lib/pages/login/login.spec.ts +109 -0
  158. package/src/lib/pages/login/login.ts +227 -0
  159. package/src/lib/pages/resource-page/resource-page.ts +234 -0
  160. package/src/lib/shared/components/badge/badge.ts +35 -0
  161. package/src/lib/shared/components/button/button.ts +65 -0
  162. package/src/lib/shared/components/confirm-dialog/confirm-dialog.ts +48 -0
  163. package/src/lib/shared/components/copy-button/copy-button.ts +56 -0
  164. package/src/lib/shared/components/copy-share/copy-share.spec.ts +122 -0
  165. package/src/lib/shared/components/copy-share/copy-share.ts +107 -0
  166. package/src/lib/shared/components/drawer/drawer.spec.ts +127 -0
  167. package/src/lib/shared/components/drawer/drawer.ts +172 -0
  168. package/src/lib/shared/components/empty-state/empty-state.ts +30 -0
  169. package/src/lib/shared/components/modal/modal.spec.ts +149 -0
  170. package/src/lib/shared/components/modal/modal.ts +145 -0
  171. package/src/lib/shared/components/post-create-modal/post-create-modal.spec.ts +165 -0
  172. package/src/lib/shared/components/post-create-modal/post-create-modal.ts +159 -0
  173. package/src/lib/shared/components/skeleton/skeleton.ts +31 -0
  174. package/src/lib/shared/components/toast/toast.ts +58 -0
  175. package/src/lib/shared/utils/col-span.ts +25 -0
  176. package/src/lib/shared/utils/condition-evaluator.ts +53 -0
  177. package/src/lib/shared/utils/depends-on.ts +48 -0
  178. package/src/lib/shared/utils/dialog-focus.ts +28 -0
  179. package/src/lib/shared/utils/field-path.ts +49 -0
  180. package/src/lib/shared/utils/gap-sorting.ts +144 -0
  181. package/src/lib/shared/utils/seed.spec.ts +64 -0
  182. package/src/lib/shared/utils/seed.ts +149 -0
  183. package/src/lib/shared/utils/slugify.ts +13 -0
  184. package/src/lib/shared/utils/validators.ts +41 -0
  185. package/src/lib/shell/layout/layout.ts +63 -0
  186. package/src/lib/shell/shell.routes.spec.ts +59 -0
  187. package/src/lib/shell/shell.routes.ts +122 -0
  188. package/src/lib/shell/sidebar/sidebar.ts +139 -0
  189. package/src/lib/shell/theme-toggle/theme-toggle.ts +74 -0
  190. package/src/lib/views/calendar-view/calendar-model.ts +426 -0
  191. package/src/lib/views/calendar-view/calendar-view.spec.ts +557 -0
  192. package/src/lib/views/calendar-view/calendar-view.ts +568 -0
  193. package/src/lib/views/composer/composer-host.spec.ts +301 -0
  194. package/src/lib/views/composer/composer-host.ts +248 -0
  195. package/src/lib/views/composer/composer-imports.spec.ts +54 -0
  196. package/src/lib/views/composer/view-context.spec.ts +117 -0
  197. package/src/lib/views/composer/view-context.ts +109 -0
  198. package/src/lib/views/composer/view-slot.spec.ts +130 -0
  199. package/src/lib/views/composer/view-slot.ts +178 -0
  200. package/src/lib/views/descriptors.ts +46 -0
  201. package/src/lib/views/kanban-view/kanban-model.ts +173 -0
  202. package/src/lib/views/kanban-view/kanban-view.spec.ts +540 -0
  203. package/src/lib/views/kanban-view/kanban-view.ts +327 -0
  204. package/src/lib/widgets/bar-chart/bar-chart.descriptor.ts +17 -0
  205. package/src/lib/widgets/bar-chart/bar-chart.spec.ts +66 -0
  206. package/src/lib/widgets/bar-chart/bar-chart.ts +76 -0
  207. package/src/lib/widgets/chart-line/chart-line.descriptor.ts +17 -0
  208. package/src/lib/widgets/chart-line/chart-line.spec.ts +113 -0
  209. package/src/lib/widgets/chart-line/chart-line.ts +219 -0
  210. package/src/lib/widgets/descriptors.ts +29 -0
  211. package/src/lib/widgets/metric-card/metric-card.descriptor.ts +41 -0
  212. package/src/lib/widgets/metric-card/metric-card.spec.ts +52 -0
  213. package/src/lib/widgets/metric-card/metric-card.ts +48 -0
  214. package/src/lib/widgets/quick-actions/quick-actions.descriptor.ts +13 -0
  215. package/src/lib/widgets/quick-actions/quick-actions.spec.ts +61 -0
  216. package/src/lib/widgets/quick-actions/quick-actions.ts +100 -0
  217. package/src/lib/widgets/record-list/record-list.descriptor.ts +16 -0
  218. package/src/lib/widgets/record-list/record-list.spec.ts +79 -0
  219. package/src/lib/widgets/record-list/record-list.ts +115 -0
  220. package/src/lib/widgets/series.spec.ts +42 -0
  221. package/src/lib/widgets/series.ts +56 -0
  222. package/src/lib/widgets/status-progress/status-progress.descriptor.ts +19 -0
  223. package/src/lib/widgets/status-progress/status-progress.spec.ts +63 -0
  224. package/src/lib/widgets/status-progress/status-progress.ts +104 -0
  225. package/src/lib/widgets/widget-host/widget-host.spec.ts +179 -0
  226. package/src/lib/widgets/widget-host/widget-host.ts +99 -0
  227. package/src/public-api.ts +104 -0
  228. package/tinya-code-gas-admin-ui-0.2.0-alpha.15.tgz +0 -0
  229. package/tsconfig.lib.json +13 -0
  230. package/tsconfig.lib.prod.json +11 -0
  231. package/tsconfig.spec.json +10 -0
  232. package/fesm2022/tinya-code-gas-admin-ui-badge-EeCA_l-o.mjs +0 -84
  233. package/fesm2022/tinya-code-gas-admin-ui-badge-EeCA_l-o.mjs.map +0 -1
  234. package/fesm2022/tinya-code-gas-admin-ui-bar-chart-G7sEMSBY.mjs +0 -140
  235. package/fesm2022/tinya-code-gas-admin-ui-bar-chart-G7sEMSBY.mjs.map +0 -1
  236. package/fesm2022/tinya-code-gas-admin-ui-boolean-B2ASKL0E.mjs +0 -113
  237. package/fesm2022/tinya-code-gas-admin-ui-boolean-B2ASKL0E.mjs.map +0 -1
  238. package/fesm2022/tinya-code-gas-admin-ui-button-BQjNW8KB.mjs +0 -163
  239. package/fesm2022/tinya-code-gas-admin-ui-button-BQjNW8KB.mjs.map +0 -1
  240. package/fesm2022/tinya-code-gas-admin-ui-calendar-view-DCY8bj2V.mjs +0 -1027
  241. package/fesm2022/tinya-code-gas-admin-ui-calendar-view-DCY8bj2V.mjs.map +0 -1
  242. package/fesm2022/tinya-code-gas-admin-ui-chart-line-BjdlKHoH.mjs +0 -278
  243. package/fesm2022/tinya-code-gas-admin-ui-chart-line-BjdlKHoH.mjs.map +0 -1
  244. package/fesm2022/tinya-code-gas-admin-ui-color-Dljh_By3.mjs +0 -107
  245. package/fesm2022/tinya-code-gas-admin-ui-color-Dljh_By3.mjs.map +0 -1
  246. package/fesm2022/tinya-code-gas-admin-ui-condition-evaluator-DY2k1M3C.mjs +0 -101
  247. package/fesm2022/tinya-code-gas-admin-ui-condition-evaluator-DY2k1M3C.mjs.map +0 -1
  248. package/fesm2022/tinya-code-gas-admin-ui-currency-BvuF3Z63.mjs +0 -141
  249. package/fesm2022/tinya-code-gas-admin-ui-currency-BvuF3Z63.mjs.map +0 -1
  250. package/fesm2022/tinya-code-gas-admin-ui-data-source.service-96FSbN72.mjs +0 -88
  251. package/fesm2022/tinya-code-gas-admin-ui-data-source.service-96FSbN72.mjs.map +0 -1
  252. package/fesm2022/tinya-code-gas-admin-ui-date-1SJGXZrd.mjs +0 -107
  253. package/fesm2022/tinya-code-gas-admin-ui-date-1SJGXZrd.mjs.map +0 -1
  254. package/fesm2022/tinya-code-gas-admin-ui-display-badge-fwk6sDU7.mjs +0 -140
  255. package/fesm2022/tinya-code-gas-admin-ui-display-badge-fwk6sDU7.mjs.map +0 -1
  256. package/fesm2022/tinya-code-gas-admin-ui-display-date-D7zLiDiG.mjs +0 -119
  257. package/fesm2022/tinya-code-gas-admin-ui-display-date-D7zLiDiG.mjs.map +0 -1
  258. package/fesm2022/tinya-code-gas-admin-ui-display-file-BiIhqvQC.mjs +0 -125
  259. package/fesm2022/tinya-code-gas-admin-ui-display-file-BiIhqvQC.mjs.map +0 -1
  260. package/fesm2022/tinya-code-gas-admin-ui-display-image-C1dhnZi1.mjs +0 -152
  261. package/fesm2022/tinya-code-gas-admin-ui-display-image-C1dhnZi1.mjs.map +0 -1
  262. package/fesm2022/tinya-code-gas-admin-ui-display-list-DOEuUbGe.mjs +0 -211
  263. package/fesm2022/tinya-code-gas-admin-ui-display-list-DOEuUbGe.mjs.map +0 -1
  264. package/fesm2022/tinya-code-gas-admin-ui-display-number-DDMzr4Hn.mjs +0 -116
  265. package/fesm2022/tinya-code-gas-admin-ui-display-number-DDMzr4Hn.mjs.map +0 -1
  266. package/fesm2022/tinya-code-gas-admin-ui-display-text-B8CdVNpv.mjs +0 -111
  267. package/fesm2022/tinya-code-gas-admin-ui-display-text-B8CdVNpv.mjs.map +0 -1
  268. package/fesm2022/tinya-code-gas-admin-ui-dropdown-C3blvmE1.mjs +0 -122
  269. package/fesm2022/tinya-code-gas-admin-ui-dropdown-C3blvmE1.mjs.map +0 -1
  270. package/fesm2022/tinya-code-gas-admin-ui-email-CT_O5ihA.mjs +0 -107
  271. package/fesm2022/tinya-code-gas-admin-ui-email-CT_O5ihA.mjs.map +0 -1
  272. package/fesm2022/tinya-code-gas-admin-ui-empty-state-BMc0QtoT.mjs +0 -109
  273. package/fesm2022/tinya-code-gas-admin-ui-empty-state-BMc0QtoT.mjs.map +0 -1
  274. package/fesm2022/tinya-code-gas-admin-ui-error-mapper.service-DY5y1FjE.mjs +0 -45
  275. package/fesm2022/tinya-code-gas-admin-ui-error-mapper.service-DY5y1FjE.mjs.map +0 -1
  276. package/fesm2022/tinya-code-gas-admin-ui-field-aria-BD1Wiggc.mjs +0 -50
  277. package/fesm2022/tinya-code-gas-admin-ui-field-aria-BD1Wiggc.mjs.map +0 -1
  278. package/fesm2022/tinya-code-gas-admin-ui-field-node-CIK6KNN_.mjs +0 -24
  279. package/fesm2022/tinya-code-gas-admin-ui-field-node-CIK6KNN_.mjs.map +0 -1
  280. package/fesm2022/tinya-code-gas-admin-ui-field-path-B4-FTBGZ.mjs +0 -27
  281. package/fesm2022/tinya-code-gas-admin-ui-field-path-B4-FTBGZ.mjs.map +0 -1
  282. package/fesm2022/tinya-code-gas-admin-ui-file-Dmxioog7.mjs +0 -408
  283. package/fesm2022/tinya-code-gas-admin-ui-file-Dmxioog7.mjs.map +0 -1
  284. package/fesm2022/tinya-code-gas-admin-ui-form-view-C709rc57.mjs +0 -4516
  285. package/fesm2022/tinya-code-gas-admin-ui-form-view-C709rc57.mjs.map +0 -1
  286. package/fesm2022/tinya-code-gas-admin-ui-format-badge-CK8ct5Wk.mjs +0 -82
  287. package/fesm2022/tinya-code-gas-admin-ui-format-badge-CK8ct5Wk.mjs.map +0 -1
  288. package/fesm2022/tinya-code-gas-admin-ui-format-boolean-ClzezZz-.mjs +0 -105
  289. package/fesm2022/tinya-code-gas-admin-ui-format-boolean-ClzezZz-.mjs.map +0 -1
  290. package/fesm2022/tinya-code-gas-admin-ui-format-currency-CVmniKml.mjs +0 -65
  291. package/fesm2022/tinya-code-gas-admin-ui-format-currency-CVmniKml.mjs.map +0 -1
  292. package/fesm2022/tinya-code-gas-admin-ui-format-date-Dr1YsDd_.mjs +0 -65
  293. package/fesm2022/tinya-code-gas-admin-ui-format-date-Dr1YsDd_.mjs.map +0 -1
  294. package/fesm2022/tinya-code-gas-admin-ui-format-number-CWCjpI4n.mjs +0 -65
  295. package/fesm2022/tinya-code-gas-admin-ui-format-number-CWCjpI4n.mjs.map +0 -1
  296. package/fesm2022/tinya-code-gas-admin-ui-format-text-DBOgJkH0.mjs +0 -59
  297. package/fesm2022/tinya-code-gas-admin-ui-format-text-DBOgJkH0.mjs.map +0 -1
  298. package/fesm2022/tinya-code-gas-admin-ui-format-thumbnail-PCRvxWKt.mjs +0 -94
  299. package/fesm2022/tinya-code-gas-admin-ui-format-thumbnail-PCRvxWKt.mjs.map +0 -1
  300. package/fesm2022/tinya-code-gas-admin-ui-gap-sorting-iTdy_DYG.mjs +0 -72
  301. package/fesm2022/tinya-code-gas-admin-ui-gap-sorting-iTdy_DYG.mjs.map +0 -1
  302. package/fesm2022/tinya-code-gas-admin-ui-group-h7-O8tt4.mjs +0 -238
  303. package/fesm2022/tinya-code-gas-admin-ui-group-h7-O8tt4.mjs.map +0 -1
  304. package/fesm2022/tinya-code-gas-admin-ui-image-7UhJ7nqI.mjs +0 -416
  305. package/fesm2022/tinya-code-gas-admin-ui-image-7UhJ7nqI.mjs.map +0 -1
  306. package/fesm2022/tinya-code-gas-admin-ui-kanban-view-Dl0Ol5Kb.mjs +0 -439
  307. package/fesm2022/tinya-code-gas-admin-ui-kanban-view-Dl0Ol5Kb.mjs.map +0 -1
  308. package/fesm2022/tinya-code-gas-admin-ui-key-value-nVdgg5at.mjs +0 -513
  309. package/fesm2022/tinya-code-gas-admin-ui-key-value-nVdgg5at.mjs.map +0 -1
  310. package/fesm2022/tinya-code-gas-admin-ui-list-C8ijJHAx.mjs +0 -637
  311. package/fesm2022/tinya-code-gas-admin-ui-list-C8ijJHAx.mjs.map +0 -1
  312. package/fesm2022/tinya-code-gas-admin-ui-metric-card-DnGMQL8l.mjs +0 -127
  313. package/fesm2022/tinya-code-gas-admin-ui-metric-card-DnGMQL8l.mjs.map +0 -1
  314. package/fesm2022/tinya-code-gas-admin-ui-multiselect-PB28e-jl.mjs +0 -251
  315. package/fesm2022/tinya-code-gas-admin-ui-multiselect-PB28e-jl.mjs.map +0 -1
  316. package/fesm2022/tinya-code-gas-admin-ui-notification.service-LjSULER6.mjs +0 -66
  317. package/fesm2022/tinya-code-gas-admin-ui-notification.service-LjSULER6.mjs.map +0 -1
  318. package/fesm2022/tinya-code-gas-admin-ui-number-JNr7glc8.mjs +0 -114
  319. package/fesm2022/tinya-code-gas-admin-ui-number-JNr7glc8.mjs.map +0 -1
  320. package/fesm2022/tinya-code-gas-admin-ui-phone-jR417hPf.mjs +0 -116
  321. package/fesm2022/tinya-code-gas-admin-ui-phone-jR417hPf.mjs.map +0 -1
  322. package/fesm2022/tinya-code-gas-admin-ui-quick-actions-DCQ6emmF.mjs +0 -235
  323. package/fesm2022/tinya-code-gas-admin-ui-quick-actions-DCQ6emmF.mjs.map +0 -1
  324. package/fesm2022/tinya-code-gas-admin-ui-radio-mode-DxvN07DY.mjs +0 -8
  325. package/fesm2022/tinya-code-gas-admin-ui-radio-mode-DxvN07DY.mjs.map +0 -1
  326. package/fesm2022/tinya-code-gas-admin-ui-readonly-text-BL7-4qie.mjs +0 -124
  327. package/fesm2022/tinya-code-gas-admin-ui-readonly-text-BL7-4qie.mjs.map +0 -1
  328. package/fesm2022/tinya-code-gas-admin-ui-record-list-BiZfnd_7.mjs +0 -249
  329. package/fesm2022/tinya-code-gas-admin-ui-record-list-BiZfnd_7.mjs.map +0 -1
  330. package/fesm2022/tinya-code-gas-admin-ui-relation-drawer.service-B0kT8EiW.mjs +0 -39
  331. package/fesm2022/tinya-code-gas-admin-ui-relation-drawer.service-B0kT8EiW.mjs.map +0 -1
  332. package/fesm2022/tinya-code-gas-admin-ui-relation-ykpJ3PYl.mjs +0 -309
  333. package/fesm2022/tinya-code-gas-admin-ui-relation-ykpJ3PYl.mjs.map +0 -1
  334. package/fesm2022/tinya-code-gas-admin-ui-render-registry-CULkxrSt.mjs +0 -576
  335. package/fesm2022/tinya-code-gas-admin-ui-render-registry-CULkxrSt.mjs.map +0 -1
  336. package/fesm2022/tinya-code-gas-admin-ui-rich-select-CIHaOrbU.mjs +0 -161
  337. package/fesm2022/tinya-code-gas-admin-ui-rich-select-CIHaOrbU.mjs.map +0 -1
  338. package/fesm2022/tinya-code-gas-admin-ui-rolldown-runtime-DpJrqANO.mjs +0 -11
  339. package/fesm2022/tinya-code-gas-admin-ui-schema-catalog-D0Pk0wAN.mjs +0 -685
  340. package/fesm2022/tinya-code-gas-admin-ui-schema-catalog-D0Pk0wAN.mjs.map +0 -1
  341. package/fesm2022/tinya-code-gas-admin-ui-seed-CcCA3FdQ.mjs +0 -80
  342. package/fesm2022/tinya-code-gas-admin-ui-seed-CcCA3FdQ.mjs.map +0 -1
  343. package/fesm2022/tinya-code-gas-admin-ui-select-CDwX9yNO.mjs +0 -296
  344. package/fesm2022/tinya-code-gas-admin-ui-select-CDwX9yNO.mjs.map +0 -1
  345. package/fesm2022/tinya-code-gas-admin-ui-skeleton-DnWK0tcZ.mjs +0 -119
  346. package/fesm2022/tinya-code-gas-admin-ui-skeleton-DnWK0tcZ.mjs.map +0 -1
  347. package/fesm2022/tinya-code-gas-admin-ui-slug-DU58cbiY.mjs +0 -158
  348. package/fesm2022/tinya-code-gas-admin-ui-slug-DU58cbiY.mjs.map +0 -1
  349. package/fesm2022/tinya-code-gas-admin-ui-source-options-B9R4mw8-.mjs +0 -26
  350. package/fesm2022/tinya-code-gas-admin-ui-source-options-B9R4mw8-.mjs.map +0 -1
  351. package/fesm2022/tinya-code-gas-admin-ui-status-progress-DmeG8htE.mjs +0 -205
  352. package/fesm2022/tinya-code-gas-admin-ui-status-progress-DmeG8htE.mjs.map +0 -1
  353. package/fesm2022/tinya-code-gas-admin-ui-string-list-gleasqzD.mjs +0 -432
  354. package/fesm2022/tinya-code-gas-admin-ui-string-list-gleasqzD.mjs.map +0 -1
  355. package/fesm2022/tinya-code-gas-admin-ui-template-interpolator-ewv7CkNX.mjs +0 -36
  356. package/fesm2022/tinya-code-gas-admin-ui-template-interpolator-ewv7CkNX.mjs.map +0 -1
  357. package/fesm2022/tinya-code-gas-admin-ui-text-CUPmL7W_.mjs +0 -107
  358. package/fesm2022/tinya-code-gas-admin-ui-text-CUPmL7W_.mjs.map +0 -1
  359. package/fesm2022/tinya-code-gas-admin-ui-textarea-CGLSIW13.mjs +0 -223
  360. package/fesm2022/tinya-code-gas-admin-ui-textarea-CGLSIW13.mjs.map +0 -1
  361. package/fesm2022/tinya-code-gas-admin-ui-time-BcHzsTWf.mjs +0 -107
  362. package/fesm2022/tinya-code-gas-admin-ui-time-BcHzsTWf.mjs.map +0 -1
  363. package/fesm2022/tinya-code-gas-admin-ui-upload.service-CZPEPCeN.mjs +0 -60
  364. package/fesm2022/tinya-code-gas-admin-ui-upload.service-CZPEPCeN.mjs.map +0 -1
  365. package/fesm2022/tinya-code-gas-admin-ui-url-GO4LZi__.mjs +0 -141
  366. package/fesm2022/tinya-code-gas-admin-ui-url-GO4LZi__.mjs.map +0 -1
  367. package/fesm2022/tinya-code-gas-admin-ui.mjs +0 -4599
  368. package/fesm2022/tinya-code-gas-admin-ui.mjs.map +0 -1
  369. package/types/tinya-code-gas-admin-ui.d.ts +0 -3632
@@ -1 +0,0 @@
1
- {"version":3,"file":"tinya-code-gas-admin-ui.mjs","names":[],"sources":["../../../projects/gas-admin-ui/src/lib/core/guards/auth.guard.ts","../../../projects/gas-admin-ui/src/lib/core/registry/composition-validator.ts","../../../projects/gas-admin-ui/src/lib/core/guards/unsaved-changes.guard.ts","../../../projects/gas-admin-ui/src/lib/views/composer/view-context.ts","../../../projects/gas-admin-ui/src/lib/core/services/theme.service.ts","../../../projects/gas-admin-ui/src/lib/shell/theme-toggle/theme-toggle.ts","../../../projects/gas-admin-ui/src/lib/shell/sidebar/sidebar.ts","../../../projects/gas-admin-ui/src/lib/shell/layout/layout.ts","../../../projects/gas-admin-ui/src/lib/shell/shell.routes.ts","../../../projects/gas-admin-ui/src/lib/pages/login/login.ts","../../../projects/gas-admin-ui/src/lib/widgets/widget-host/widget-host.ts","../../../projects/gas-admin-ui/src/lib/pages/dashboard/dashboard.ts","../../../projects/gas-admin-ui/src/lib/actions/action-service.ts","../../../projects/gas-admin-ui/src/lib/columns/column-defaults.ts","../../../projects/gas-admin-ui/src/lib/pages/list-view/list-view.ts","../../../projects/gas-admin-ui/src/lib/pages/detail-view/detail-view.ts","../../../projects/gas-admin-ui/src/lib/pages/resource-page/resource-page.ts","../../../projects/gas-admin-ui/src/lib/views/composer/view-slot.ts","../../../projects/gas-admin-ui/src/lib/views/composer/composer-host.ts","../../../projects/gas-admin-ui/src/lib/shared/components/confirm-dialog/confirm-dialog.ts","../../../projects/gas-admin-ui/src/lib/shared/components/toast/toast.ts","../../../projects/gas-admin-ui/src/lib/core/models/schema-descriptions.ts"],"sourcesContent":["import { inject } from '@angular/core';\nimport type { CanActivateFn } from '@angular/router';\nimport { Router } from '@angular/router';\n\nimport { getSchema } from '../registry/schema-catalog';\nimport { AuthService, LOGIN_PATH } from '../services/auth.service';\n\n/**\n * Guards de sesión (auth.md §5.3).\n *\n * Dos puertas y ninguna es una barrera de seguridad: el back re-verifica\n * `accessLevel` en **cada** petición desde R2.6. Éstas sólo evitan pintar\n * una pantalla que no le toca al usuario — enseñarle un formulario que va\n * a recibir 403 es mala UX, no un agujero.\n *\n * `authGuard` va en la ruta madre (todo el shell); `roleGuard` en cada\n * `:id`, que es donde aparece el recurso que puede tener `access`.\n */\n\n/** Sin sesión → login. Corre ANTES que cualquier ruta del shell. */\nexport const authGuard: CanActivateFn = () => {\n const auth = inject(AuthService);\n if (auth.isAuthenticated()) return true;\n return inject(Router).createUrlTree([LOGIN_PATH]);\n};\n\n/**\n * El recurso declara `access` y esta sesión no lo cumple → se cancela la\n * navegación. No hay pantalla de 403 (tampoco la pide §5.3) y el menú ya\n * oculta la entrada, así que queda sólo el caso de alguien tecleando la URL.\n */\nexport const roleGuard: CanActivateFn = (route) => {\n const auth = inject(AuthService);\n const router = inject(Router);\n\n // Redundante con `authGuard` (la madre ya filtra) pero barato: un hijo\n // nunca debe dejar una sesión muerta navegando sola.\n if (!auth.isAuthenticated()) return router.createUrlTree([LOGIN_PATH]);\n\n const id = route.data?.['resourceId'] ?? route.paramMap.get('id');\n // Ruta sin recurso declarado, o id que no existe en el registry → que\n // responda el back (404). No inventar una política sobre datos ajenos.\n if (id === null) return true;\n const schema = getSchema(String(id));\n if (schema === undefined) return true;\n\n return auth.canAccess(schema.access);\n};\n","import { getSchema } from './schema-catalog';\nimport type {\n DataSource,\n NavigationTarget,\n ResourceSchema,\n SelectionBinding,\n UiViewDescriptor,\n ViewChildNode,\n} from '../models/schema.model';\nimport { get } from './render-registry';\n\n/**\n * Validador de composición de vistas (R9.9.2 · D2/D3).\n *\n * Una composición (un `uiView` contenedor con `config.children`, p. ej.\n * `tabs`) se valida ANTES del render con esta única función pura y síncrona:\n *\n * - identidad de hijos (`id` presente y único entre hermanos);\n * - tipos resueltos con `get('view', type)` — **nunca** `resolve()`, cuyo\n * `fallback` silencioso dejaría un hijo inválido pintándose de todos modos;\n * - `ref` rotos y ciclos (directos o transitivos) que los `ref` permiten;\n * - profundidad y cantidad de nodos con topes configurables;\n * - destinos de navegación (`resource` + `view` + params) y fuentes de\n * filas — una URL cruda nunca es un destino válido;\n *\n * El llamante es D3: `resourceRoutes()` en `shell.routes.ts` (fail-loud al\n * arrancar) y el guardián de montaje del composer. Cualquier violación lanza\n * `SchemaCompositionError` con un mensaje legible que nombra recurso,\n * contenedor e hijo: nada se oculta ni se sustituye en silencio.\n *\n * El tipo de una `uiView` RAÍZ no se chequea acá: eso ya lo hace\n * `resourceRoutes()` con su error genérico de descriptor inexistente. Este\n * validador cubre lo que ese recorrido no ve: la estructura de los hijos.\n */\n\n/** Profundidad máxima de anidamiento de contenedores (hijos raíz = 1). */\nexport const MAX_DEPTH = 3;\n\n/** Cantidad máxima de nodos-hijo declarados en la composición de un schema. */\nexport const MAX_NODES = 50;\n\n/** Violación del contrato de composición, detectada antes del render. */\nexport class SchemaCompositionError extends Error {\n constructor(message: string) {\n super(message);\n this.name = 'SchemaCompositionError';\n }\n}\n\n/** Estado del recorrido: cuenta de nodos y camino actual (detección de ciclos). */\ninterface Estado {\n readonly esquema: ResourceSchema;\n nodos: number;\n readonly contados: Set<ViewChildNode>;\n readonly camino: Set<string>;\n}\n\n/** `config` de la vista cuando la hay (`tabs` es el único contenedor hoy). */\nfunction configDe(vista: UiViewDescriptor | ViewChildNode): unknown {\n return 'config' in vista ? vista.config : undefined;\n}\n\n/** Hijos declarados si la vista es un contenedor; `undefined` si no lo es. */\nfunction hijosDe(vista: UiViewDescriptor | ViewChildNode): ViewChildNode[] | undefined {\n const hijos = (configDe(vista) as { children?: unknown } | undefined)?.children;\n return Array.isArray(hijos) ? (hijos as ViewChildNode[]) : undefined;\n}\n\n/**\n * Valida TODA la composición de un schema. Lanza `SchemaCompositionError`\n * en la primera violación; no lanzar significa «apto para renderizar».\n */\nexport function validateComposition(schema: ResourceSchema): void {\n const estado: Estado = { esquema: schema, nodos: 0, contados: new Set(), camino: new Set() };\n for (const [nombre, vista] of Object.entries(schema.uiViews ?? {})) {\n estado.camino.add(nombre);\n validarContenido(nombre, vista, 1, estado);\n estado.camino.delete(nombre);\n }\n}\n\n/**\n * Valida una `uiView` ya resuelta (raíz o referenciada por `ref`): sus\n * bindings, su fuente de filas y —si es contenedor— sus hijos.\n * `profundidadHijos` es la profundidad que tendrán sus hijos directos.\n */\nfunction validarContenido(\n nombre: string,\n vista: UiViewDescriptor,\n profundidadHijos: number,\n estado: Estado,\n): void {\n const donde = `${estado.esquema.id}.${nombre}`;\n validarSelect(donde, (vista as { select?: SelectionBinding }).select);\n validarSource(donde, (configDe(vista) as { source?: DataSource } | undefined)?.source);\n const hijos = hijosDe(vista);\n if (!hijos) return;\n validarHijos(nombre, hijos, profundidadHijos, estado);\n}\n\n/** Valida la lista de hijos de un contenedor: identidad única entre hermanos. */\nfunction validarHijos(\n contenedor: string,\n hijos: readonly ViewChildNode[],\n profundidad: number,\n estado: Estado,\n): void {\n const ids = new Set<string>();\n for (const hijo of hijos) {\n const id = (hijo as { id?: unknown }).id;\n if (typeof id !== 'string' || id === '') {\n throw new SchemaCompositionError(`${estado.esquema.id}.${contenedor}: hijo sin id`);\n }\n if (ids.has(id)) {\n throw new SchemaCompositionError(`${estado.esquema.id}.${contenedor}: id duplicado '${id}'`);\n }\n ids.add(id);\n validarHijo(hijo, contenedor, id, profundidad, estado);\n }\n}\n\n/** Valida un hijo: topes de nodos/profundidad, ciclo, ref y tipo inline. */\nfunction validarHijo(\n hijo: ViewChildNode,\n contenedor: string,\n id: string,\n profundidad: number,\n estado: Estado,\n): void {\n const donde = `${estado.esquema.id}.${contenedor} → child '${id}'`;\n\n if (!estado.contados.has(hijo)) {\n estado.contados.add(hijo);\n estado.nodos += 1;\n if (estado.nodos > MAX_NODES) {\n throw new SchemaCompositionError(\n `${estado.esquema.id}: la composición supera el límite de ${MAX_NODES} nodos (observados ${estado.nodos})`,\n );\n }\n }\n if (profundidad > MAX_DEPTH) {\n throw new SchemaCompositionError(\n `${donde}: profundidad ${profundidad} supera el máximo ${MAX_DEPTH}`,\n );\n }\n\n if ('ref' in hijo) {\n validarRef(donde, (hijo as { ref?: unknown }).ref as string, profundidad, estado);\n return;\n }\n\n const tipo = (hijo as { type?: unknown }).type as string;\n if (!get('view', tipo)) {\n throw new SchemaCompositionError(`${donde}: tipo desconocido '${tipo}'`);\n }\n validarSelect(donde, (hijo as { select?: SelectionBinding }).select);\n validarSource(donde, (configDe(hijo) as { source?: DataSource } | undefined)?.source);\n /**\n * Contenedor inline: sus propios hijos también se validan. Sin esto, una\n * composición anidada dentro de otro `tabs` inline se renderiza sin haber\n * pasado por topes de profundidad/nodos ni por la detección de ciclos de\n * sus `ref` (D2: invalid branch NUNCA llega al render). La profundidad\n * CONTINUA desde este hijo — no se reinicia — y el camino de ciclo se\n * mantiene intacto, porque un `ref` sólo puede volver a nombres que ya\n * están en él.\n */\n const internos = hijosDe(hijo);\n if (internos !== undefined) {\n validarHijos(id, internos, profundidad + 1, estado);\n }\n}\n\n/**\n * Resuelve un hijo `ref` contra `schema.uiViews`: el destino debe existir y\n * no formar parte del camino actual (ciclo directo o transitivo). El\n * contenido del destino se valida en profundidad CONTINUANDO la del ref —\n * un `ref` anida de verdad (R9.9.2).\n */\nfunction validarRef(donde: string, target: string, profundidad: number, estado: Estado): void {\n if (estado.camino.has(target)) {\n throw new SchemaCompositionError(\n `${donde}: ciclo detectado — '${target}' ya está en el camino`,\n );\n }\n const vista = estado.esquema.uiViews?.[target];\n if (!vista) {\n throw new SchemaCompositionError(\n `${donde}: ref '${target}' no existe en ${estado.esquema.id}.uiViews`,\n );\n }\n estado.camino.add(target);\n validarContenido(target, vista, profundidad + 1, estado);\n estado.camino.delete(target);\n}\n\n/**\n * Valida el binding de selección de una `uiView` o de un hijo (R9.9.4).\n * Sólo `mode: 'route'` exige destino declarado; `none`/`inline` son locales.\n */\nfunction validarSelect(donde: string, select: SelectionBinding | undefined): void {\n if (select === undefined || select.mode !== 'route') return;\n const target = (select as { target?: unknown }).target;\n if (target === undefined || target === null) {\n throw new SchemaCompositionError(`${donde}: select mode 'route' sin target`);\n }\n if (typeof target === 'string') {\n throw new SchemaCompositionError(\n `${donde}: destino con URL cruda '${target}' — declará resource + view`,\n );\n }\n validarTarget(donde, target as NavigationTarget);\n}\n\n/**\n * El destino sólo puede ser un recurso + `uiView` declarados, con los params\n * declarados EXACTAMENTE igual que los params de la ruta del destino: ni\n * params ajenos a la ruta ni params de la ruta sin declarar.\n */\nfunction validarTarget(donde: string, target: NavigationTarget): void {\n const { resource, view, params } = target;\n if (typeof resource !== 'string' || resource === '' || typeof view !== 'string' || view === '') {\n throw new SchemaCompositionError(`${donde}: destino sin resource + view`);\n }\n const destino = getSchema(resource);\n if (!destino) {\n throw new SchemaCompositionError(`${donde}: destino '${resource}' no existe`);\n }\n const vista = destino.uiViews?.[view];\n if (!vista) {\n throw new SchemaCompositionError(\n `${donde}: destino ${resource}.${view} no tiene uiView declarada`,\n );\n }\n const paramsRuta = (vista.path ?? '')\n .split('/')\n .filter((segmento) => segmento.startsWith(':'))\n .map((segmento) => segmento.slice(1));\n const declarados = params ?? [];\n for (const param of declarados) {\n if (!paramsRuta.includes(param)) {\n throw new SchemaCompositionError(\n `${donde}: param '${param}' no existe en la ruta ${resource}.${view}`,\n );\n }\n }\n for (const param of paramsRuta) {\n if (!declarados.includes(param)) {\n throw new SchemaCompositionError(\n `${donde}: falta declarar el param '${param}' de la ruta ${resource}.${view}`,\n );\n }\n }\n}\n\n/** Valida la fuente de filas (`config.source`) declarada por una vista (R9.8). */\nfunction validarSource(donde: string, source: DataSource | undefined): void {\n if (source === undefined) return;\n if (typeof source.resource !== 'string' || source.resource === '') {\n throw new SchemaCompositionError(`${donde}: source sin resource`);\n }\n const origen = getSchema(source.resource);\n if (!origen) {\n throw new SchemaCompositionError(`${donde}: source '${source.resource}' no existe`);\n }\n if (source.view !== undefined && origen.views?.[source.view] === undefined) {\n throw new SchemaCompositionError(\n `${donde}: la vista '${source.view}' no existe en ${source.resource}.views`,\n );\n }\n}\n","import { CanDeactivateFn } from '@angular/router';\n\n/** Componente que expone si se puede salir (base.md §13.9: cambios sin guardar). */\nexport interface CanLeaveWhenDirty {\n canDeactivate(): boolean | Promise<boolean>;\n}\n\n/**\n * Avisa antes de salir con cambios sin guardar. El componente decide:\n * `canDeactivate()` devuelve `true` para salir limpio o una `Promise` que\n * resuelve según la respuesta del usuario (vía `NotificationService.confirm`).\n */\nexport const unsavedChangesGuard: CanDeactivateFn<unknown> = (component) => {\n const candidate = component as Partial<CanLeaveWhenDirty> | null | undefined;\n return candidate?.canDeactivate?.() ?? true;\n};\n","import { convertToParamMap, type ActivatedRoute } from '@angular/router';\nimport { of } from 'rxjs';\n\nimport type {\n ResourceSchema,\n SelectionBinding,\n UiViewDescriptor,\n} from '../../core/models/schema.model';\n\n/**\n * R9.9 · D1 — Canal ViewContext.\n *\n * El shell monta hojas con `loadComponent` SIN bindings de input, así que el\n * único canal que una hoja recibe es `route.data`. Este módulo es la fábrica\n * ÚNICA de ese payload para ambos productores:\n *\n * - Lado embebido (`ViewSlot`, slice 3): `syntheticRoute(payload)` — una\n * `ActivatedRoute` sintética cuyo `data` emite síncrono, de modo que\n * `toSignal(route.data, {requireSync:true})` sigue funcionando igual.\n * - Lado ruta (`shell.routes`): `resolve:{viewContext}` devuelve el mismo\n * payload con el `emit` ligado en contexto de inyección.\n *\n * Cero reescritura en las hojas: leen `schema`/`resourceId`/`uiView` como\n * siempre. Un `emit` ausente significa «sin comportamiento declarado ⇒ el\n * comportamiento local sigue» — nunca es un error.\n */\n\n/** Evento de selección que una hoja emite cuando hay `select` declarado. */\nexport interface SelectionEvent {\n /** Recurso del registro seleccionado. */\n resource: string;\n /** Clave estable del registro — la ÚNICA identidad que viaja al router. */\n key: string;\n /** Disparador que activó la transición (`on` del binding). */\n action: string;\n}\n\n/** Callback de selección entregado a la hoja a través del canal. */\nexport type SelectionEmitter = (event: SelectionEvent) => void;\n\n/**\n * Payload del canal: las mismas claves planas que una hoja montada por ruta\n * lee en `route.data`. `schema` explícito sólo existe en el lado embebido\n * (schema virtual); montado por ruta la hoja lo resuelve vía `resourceId`.\n */\nexport interface ViewContextData {\n schema?: ResourceSchema;\n resourceId: string;\n uiView: string;\n viewType: string;\n /**\n * Profundidad de ESTA instancia (raíz ⇒ campo ausente ⇒ 0). El productor\n * embebido (`ViewSlot`) la fija para que un contenedor anidado pase\n * `depth + 1` a sus propios slots y el tope `MAX_DEPTH` se cumpla en\n * runtime aunque el validador no llegue a correr (defensa en profundidad).\n */\n depth?: number;\n /**\n * Binding `select` declarado por la vista (cualquier mode). El `emit`\n * sólo existe para `mode:'route'`; la hoja necesita VER el binding para\n * aplicar la decision table (R9.9.4): ausente ⇒ comportamiento local,\n * `inline` ⇒ selección local, `none` ⇒ no-op explícito.\n */\n select?: SelectionBinding;\n emit?: SelectionEmitter;\n}\n\n/** Identidad estable de una instancia embebida: `parentId + '/' + childId`. */\nexport function instanceIdFor(parentId: string, childId: string): string {\n return `${parentId}/${childId}`;\n}\n\n/**\n * Schema virtual por instancia: clona el schema y expone la config del hijo\n * bajo `uiViews[instanceId]`, de modo que la lookup existente\n * `schema.uiViews[route.uiView]` de las hojas resuelve la config del nodo\n * SIN tocar ninguna hoja. No muta el schema original.\n */\nexport function virtualSchemaFor(\n schema: ResourceSchema,\n instanceId: string,\n view: UiViewDescriptor,\n): ResourceSchema {\n return { ...schema, uiViews: { ...schema.uiViews, [instanceId]: view } };\n}\n\n/**\n * `ActivatedRoute` sintética para hijos embebidos. `of()` emite síncrono ⇒\n * `toSignal(..., {requireSync:true})` no revienta. El cast es único y\n * documentado (D1): sólo se consume `data`, igual que una ruta real.\n */\nexport function syntheticRoute(data: ViewContextData): ActivatedRoute {\n // `paramMap` además de `data`: hojas tipo `ResourcePage` (list/detail/\n // dashboard) hacen `toSignal(route.paramMap, {requireSync:true})` y\n // `paramMap.subscribe(...)`. Vacío = embebido sin clave de registro\n // (nunca un detail-key implícito); la clave real viaja en el payload.\n return {\n data: of(data),\n paramMap: of(convertToParamMap({})),\n } as unknown as ActivatedRoute;\n}\n\n/** Construye el payload; `emit` sólo se agrega si existe (clave ausente si no). */\nexport function createViewContext(\n base: Omit<ViewContextData, 'emit'>,\n emit?: SelectionEmitter,\n): ViewContextData {\n return emit === undefined ? { ...base } : { ...base, emit };\n}\n","import { Service, signal } from '@angular/core';\n\n/** Los tres estados del selector (§8.6). */\nexport type ThemeMode = 'system' | 'light' | 'dark';\n\n/** Lo que acaba pintado en `<html data-theme>`. */\nexport type ResolvedTheme = 'light' | 'dark';\n\nconst STORAGE_KEY = 'admin-theme';\nconst QUERY = '(prefers-color-scheme: dark)';\nconst MODES: string[] = ['system', 'light', 'dark'];\n\n/** Lee la elección guardada; cualquier basura vuelve a `system`. */\nfunction storedMode(): ThemeMode {\n try {\n const raw = localStorage.getItem(STORAGE_KEY);\n return raw !== null && MODES.includes(raw) ? (raw as ThemeMode) : 'system';\n } catch {\n // localStorage puede tirar en modo privado o con cuota agotada.\n return 'system';\n }\n}\n\n/**\n * `system` → lo que diga el sistema operativo.\n * jsdom no implementa `matchMedia`: sin él se asume claro, que es además\n * el estado por defecto antes de que corra el script de `index.html`.\n */\nfunction prefersDark(): boolean {\n return typeof window.matchMedia === 'function' && window.matchMedia(QUERY).matches;\n}\n\nfunction resolve(mode: ThemeMode): ResolvedTheme {\n return mode === 'system' ? (prefersDark() ? 'dark' : 'light') : mode;\n}\n\n/**\n * Selector de tema con tres estados: `system`, `light`, `dark` (Fase 8.6).\n *\n * `system` NO se traduce en CSS con una media query sino que se resuelve a\n * un valor concreto, porque con tres estados un único `prefers-color-scheme`\n * no alcanza para distinguir \"oscuro elegido a mano\" de \"oscuro heredado\".\n *\n * El arranque sin flash vive en `index.html`: un script síncrono en el\n * `<head>` escribe `data-theme` y `data-theme-mode` antes del primer\n * pintado. Este servicio toma el relevo después y además escucha cambios\n * en vivo del sistema, para que cambiar el tema del SO con la app abierta\n * la recoloree — pero sólo mientras la elección siga siendo `system`: si el\n * usuario eligió un modo concreto, el SO deja de mandar.\n */\n@Service()\nexport class ThemeService {\n /** Elección persistida. */\n readonly mode = signal<ThemeMode>(storedMode());\n /** Lo que está pintado ahora mismo. */\n readonly resolved = signal<ResolvedTheme>(resolve(this.mode()));\n\n constructor() {\n this.apply();\n\n const media = typeof window.matchMedia === 'function' ? window.matchMedia(QUERY) : null;\n media?.addEventListener('change', () => {\n if (this.mode() === 'system') {\n this.apply();\n }\n });\n }\n\n set(mode: ThemeMode): void {\n this.mode.set(mode);\n try {\n localStorage.setItem(STORAGE_KEY, mode);\n } catch {\n // Sin persistencia el tema sigue funcionando durante la sesión.\n }\n this.apply();\n }\n\n /** Escribe el resultado en `<html>`; es lo que lee `theme.css`. */\n private apply(): void {\n const resolved = resolve(this.mode());\n this.resolved.set(resolved);\n document.documentElement.dataset['theme'] = resolved;\n document.documentElement.dataset['themeMode'] = this.mode();\n }\n}\n","import { Component, inject } from '@angular/core';\nimport { LucideMonitor, LucideMoon, LucideSun } from '@lucide/angular';\n\nimport { ThemeService, type ThemeMode } from '../../core/services/theme.service';\n\nconst BASE =\n 'rounded-md p-1.5 transition-colors ' +\n // 44×44 en mobile (guía ux-ui-mobile §8.2); en lg vuelve a 28 px (ratón).\n 'min-h-11 min-w-11 lg:min-h-0 lg:min-w-0 ' +\n 'focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-primary';\nconst ACTIVE = 'bg-primary/10 text-primary';\nconst IDLE = 'text-neutral hover:bg-neutral/10';\n\n/**\n * Selector de tema de tres posiciones: Sistema / Claro / Oscuro (§8.6).\n *\n * Van tres `<button>` con `aria-pressed` y no un `role=\"radiogroup\"`: un\n * radiogroup exige navegación con flechas para cumplir su contrato, mientras\n * que tres botones ya son operables con Tab + Enter/Space y el lector de\n * pantalla anuncia \"presionado / no presionado\" en cada uno. Es la opción\n * con menos JS y sin degradar el teclado.\n *\n * Sólo icono, con `aria-label` y `title` para no ocupar ancho en la cabecera\n * de la sidebar (240 px).\n */\n@Component({\n selector: 'app-theme-toggle',\n imports: [LucideMonitor, LucideSun, LucideMoon],\n template: `\n <div\n role=\"group\"\n aria-label=\"Tema del panel\"\n class=\"inline-flex items-center gap-0.5 rounded-lg border border-neutral/20 bg-surface p-0.5\"\n >\n <button\n type=\"button\"\n [class]=\"classes('system')\"\n [attr.aria-pressed]=\"theme.mode() === 'system'\"\n aria-label=\"Tema: seguir al sistema\"\n title=\"Seguir al sistema\"\n (click)=\"theme.set('system')\"\n >\n <svg lucideMonitor size=\"16\" />\n </button>\n <button\n type=\"button\"\n [class]=\"classes('light')\"\n [attr.aria-pressed]=\"theme.mode() === 'light'\"\n aria-label=\"Tema: claro\"\n title=\"Tema claro\"\n (click)=\"theme.set('light')\"\n >\n <svg lucideSun size=\"16\" />\n </button>\n <button\n type=\"button\"\n [class]=\"classes('dark')\"\n [attr.aria-pressed]=\"theme.mode() === 'dark'\"\n aria-label=\"Tema: oscuro\"\n title=\"Tema oscuro\"\n (click)=\"theme.set('dark')\"\n >\n <svg lucideMoon size=\"16\" />\n </button>\n </div>\n `,\n})\nexport class ThemeToggle {\n readonly theme = inject(ThemeService);\n\n classes(mode: ThemeMode): string {\n return `${BASE} ${this.theme.mode() === mode ? ACTIVE : IDLE}`;\n }\n}\n","import { Component, computed, inject, model, signal } from '@angular/core';\nimport { RouterLink, RouterLinkActive } from '@angular/router';\nimport { LucideLogOut } from '@lucide/angular';\n\nimport { allSchemas } from '../../core/registry/schema-catalog';\nimport { AuthService } from '../../core/services/auth.service';\nimport { NotificationService } from '../../core/services/notification.service';\nimport { Button } from '../../shared/components/button/button';\nimport { ThemeToggle } from '../theme-toggle/theme-toggle';\nimport type { ResourceSchema } from '../../core/models/schema.model';\n\ninterface MenuEntry {\n id: string;\n label: string;\n}\n\n/**\n * Menú lateral generado desde el catálogo EFECTIVO (F7-4): locales\n * fusionados + recursos sintetizados del schema remoto — un recurso nuevo\n * del backend aparece acá con 0 cambios en `src/`. Reactivo a la señal del\n * schema remoto (el initializer lo resuelve antes del primer render, pero\n * el `computed` cubre el caso de respuesta tardía).\n * `labelPlural` + `permissions`: un recurso se oculta solo si declara\n * create, update y remove explícitamente en falso (no hay permiso de\n * lectura).\n */\n@Component({\n selector: 'app-sidebar',\n imports: [RouterLink, RouterLinkActive, ThemeToggle, Button, LucideLogOut],\n template: `\n <!-- Offcanvas en mobile (transform); las clases lg: la dejan estática\n y siempre visible en pantallas grandes. -->\n <aside\n class=\"fixed inset-y-0 left-0 z-40 w-60 shrink-0 h-[100svh] min-h-[80svh] lg:sticky border-r border-neutral/20 bg-surface-raised transition-transform duration-200 lg:static lg:z-auto lg:translate-x-0\"\n [class.-translate-x-full]=\"!open()\"\n >\n <div class=\"flex items-center justify-between gap-2 border-b border-neutral/20 px-4 py-4\">\n <!-- min-w-0+truncate: si el toggle exige su ancho, el rótulo se\n trunca en vez de desbordar la sidebar de 240 px. -->\n <span class=\"min-w-0 truncate font-display text-base font-semibold\">Admin</span>\n <app-theme-toggle />\n </div>\n <nav aria-label=\"Principal\" class=\"p-3\">\n <ul class=\"space-y-2 lg:space-y-1\">\n @for (item of menuItems(); track item.id) {\n <li>\n <a\n [routerLink]=\"['/', item.id]\"\n routerLinkActive=\"bg-primary/10 text-primary\"\n class=\"flex min-h-11 items-center rounded-lg px-3 py-2 text-sm font-medium text-neutral hover:bg-neutral/10\"\n (click)=\"open.set(false)\"\n >\n {{ item.label }}\n </a>\n </li>\n }\n </ul>\n </nav>\n @if (isAuthenticated()) {\n <div class=\"border-t border-neutral/20 p-3\">\n <button\n app-button\n type=\"button\"\n variant=\"danger\"\n [loading]=\"loggingOut()\"\n [disabled]=\"loggingOut()\"\n (click)=\"closeSession()\"\n class=\"w-full\"\n >\n <svg lucideLogOut size=\"16\" />\n Cerrar sesión\n </button>\n </div>\n }\n </aside>\n `,\n})\nexport class Sidebar {\n /** Estado del offcanvas (sólo relevante en mobile; ver `layout.ts`).\n * `model()`: el padre lo setea (abrir) y el hijo también (cerrar al\n * navegar) — binding de dos vías `[(open)]`. */\n readonly open = model(false);\n\n private readonly auth = inject(AuthService);\n private readonly notifications = inject(NotificationService);\n readonly isAuthenticated = this.auth.isAuthenticated;\n readonly loggingOut = signal(false);\n\n /**\n * R2.10 — el menú se corta dos veces: `isVisible` (permisos declarados\n * en el schema) y `canAccess` (auth.md §5.3, `access` de la sesión).\n * Sólo UX: filtrar acá no protege nada, el back re-verifica en R2.6.\n */\n readonly menuItems = computed<MenuEntry[]>(() =>\n allSchemas()\n .filter((schema) => isVisible(schema) && this.auth.canAccess(schema.access))\n .map((schema) => ({\n id: schema.id,\n label: schema.labelPlural,\n })),\n );\n\n async closeSession(): Promise<void> {\n const accepted = await this.notifications.confirm({\n title: 'Cerrar sesión',\n message: '¿Seguro que querés cerrar tu sesión?',\n confirmLabel: 'Cerrar sesión',\n });\n if (!accepted) return;\n\n this.loggingOut.set(true);\n this.auth.logout().subscribe({\n complete: () => this.loggingOut.set(false),\n });\n }\n}\n\nfunction isVisible(schema: ResourceSchema): boolean {\n // R7.5: usar policies cuando existan, fallback a permissions\n if (schema.policies?.access) {\n // Con policies, asumimos que si el usuario tiene sesión puede ver el recurso\n // (el filtrado real lo hace `canAccess` en el computed, que evalúa el rol).\n // Esta función sólo puede aplicar criterios estáticos, no dinámicos.\n return true;\n }\n\n // Fallback a permissions (legacy)\n const permissions = schema.permissions;\n if (!permissions) {\n return true;\n }\n // Un recurso de sólo lectura (todas las escrituras en false) sigue siendo\n // visible si tiene endpoint de lectura (list/get): no se oculta por ser\n // read-only. Sólo se oculta si no puede leer NI escribir.\n const hasReadEndpoint = !!schema.endpoint?.list || !!schema.endpoint?.get;\n const canWrite =\n permissions.create !== false || permissions.update !== false || permissions.remove !== false;\n return hasReadEndpoint || canWrite;\n}\n","import { Component, signal } from '@angular/core';\nimport { RouterOutlet } from '@angular/router';\n\nimport { Button } from '../../shared/components/button/button';\nimport { Sidebar } from '../sidebar/sidebar';\n\n/**\n * Esqueleto de la app autenticada: sidebar + área principal con outlet.\n *\n * Responsive: en pantallas chicas la sidebar es **offcanvas** (detrás de un\n * botón hamburguesa + backdrop); desde `lg` vuelve a ser estática y siempre\n * visible (clases `lg:*` del propio aside ganan sobre el estado `open`).\n */\n@Component({\n selector: 'app-layout',\n imports: [RouterOutlet, Sidebar, Button],\n template: `\n <div class=\"flex min-h-screen bg-neutral/5\">\n <button\n app-button\n type=\"button\"\n variant=\"outline\"\n size=\"icon\"\n class=\"fixed top-[calc(env(safe-area-inset-top)+0.75rem)] left-3 z-40 shadow-md lg:hidden\"\n [attr.aria-expanded]=\"sidebarOpen()\"\n aria-controls=\"main-sidebar\"\n aria-label=\"Abrir menú\"\n (click)=\"sidebarOpen.set(true)\"\n >\n <svg\n aria-hidden=\"true\"\n class=\"size-5\"\n fill=\"none\"\n stroke=\"currentColor\"\n stroke-width=\"2\"\n viewBox=\"0 0 24 24\"\n >\n <path stroke-linecap=\"round\" d=\"M4 6h16M4 12h16M4 18h16\" />\n </svg>\n </button>\n @if (sidebarOpen()) {\n <div\n class=\"fixed inset-0 z-30 bg-black/30 lg:hidden\"\n aria-hidden=\"true\"\n (click)=\"sidebarOpen.set(false)\"\n ></div>\n }\n <app-sidebar id=\"main-sidebar\" [(open)]=\"sidebarOpen\" />\n <!-- pt reafirmado en sm: sin esto la variante sm:p-6 (va después en la\n cascada) pisa el pt-16 base y el contenido choca con la hamburguesa.\n base/sm dejan pasar el safe-area del notch; en lg no hay hamburguesa. -->\n <main\n class=\"min-w-0 flex-1 p-4 pt-[calc(4rem+env(safe-area-inset-top))] sm:p-6 sm:pt-[calc(4rem+env(safe-area-inset-top))] lg:p-8\"\n >\n <router-outlet />\n </main>\n </div>\n `,\n})\nexport class Layout {\n /** Estado del offcanvas en mobile; en `lg` la sidebar es estática (siempre visible). */\n readonly sidebarOpen = signal(false);\n}\n","import type { Route, Routes } from '@angular/router';\nimport { inject } from '@angular/core';\n\nimport { validateComposition } from '../core/registry/composition-validator';\nimport type { ResourceSchema } from '../core/models/schema.model';\nimport { get } from '../core/registry/render-registry';\nimport { roleGuard } from '../core/guards/auth.guard';\nimport { unsavedChangesGuard } from '../core/guards/unsaved-changes.guard';\nimport { ViewNavigationService } from '../core/view-navigation/view-navigation.service';\nimport { allSchemas } from '../core/registry/schema-catalog';\nimport { createViewContext } from '../views/composer/view-context';\nimport { Layout } from './layout/layout';\n\nfunction pathRank(path: string): number[] {\n const segments = path.split('/');\n return [segments.length, segments.filter((segment) => !segment.startsWith(':')).length];\n}\n\nfunction compareRoutes(a: Route, b: Route): number {\n const aRank = pathRank(a.path ?? '');\n const bRank = pathRank(b.path ?? '');\n return bRank[0] - aRank[0] || bRank[1] - aRank[1];\n}\n\n/**\n * Rutas de los schemas recibidos (por defecto, la registry completa).\n * Cada schema se valida con `validateComposition` ANTES de generar rutas\n * (D3): una composición inválida revienta al arrancar, no en el render.\n */\nexport function resourceRoutes(catalog: readonly ResourceSchema[] = allSchemas()): Route[] {\n return catalog\n .flatMap((schema) => {\n if (!schema.uiViews || Object.keys(schema.uiViews).length === 0) {\n throw new Error(`El recurso '${schema.id}' no declara uiViews`);\n }\n validateComposition(schema);\n return Object.entries(schema.uiViews).map(([name, view]) => {\n const descriptor = get('view', view.type);\n if (!descriptor) {\n throw new Error(\n `No existe descriptor kind:view type '${view.type}' para ${schema.id}.${name}`,\n );\n }\n const relativePath = view.path ?? '';\n if (\n relativePath.startsWith('/') ||\n relativePath.endsWith('/') ||\n (relativePath !== '' &&\n relativePath\n .split('/')\n .some((segment) => segment === '' || segment === '.' || segment === '..'))\n ) {\n throw new Error(`Ruta uiViews inválida en ${schema.id}.${name}: '${relativePath}'`);\n }\n const path = [schema.id, relativePath].filter(Boolean).join('/');\n const route: Route = {\n path,\n loadComponent: descriptor.load,\n canActivate: [roleGuard],\n data: {\n resourceId: schema.id,\n uiView: name,\n viewType: view.type,\n },\n /**\n * R9.9 · D1/D4 — el payload lleva SIEMPRE el `select` declarado\n * (cualquier mode) para que la hoja aplique la decision table; el\n * `emit` se liga aquí (contexto de inyección) a\n * `ViewNavigationService.dispatch`, que resuelve el target,\n * construye los segmentos y respeta guards + historial. Sólo un\n * binding `mode:'route'` lleva `emit`; `none`/`inline`/ausente ⇒\n * payload sin emisor.\n */\n resolve: {\n viewContext: () => {\n const base = {\n resourceId: schema.id,\n uiView: name,\n viewType: view.type,\n ...(view.select !== undefined ? { select: view.select } : {}),\n };\n const select = view.select;\n if (select?.mode !== 'route') return createViewContext(base);\n const nav = inject(ViewNavigationService);\n return createViewContext(base, (event) => {\n void nav.dispatch(select, event);\n });\n },\n },\n };\n if (view.type === 'form') route.canDeactivate = [unsavedChangesGuard];\n return route;\n });\n })\n .sort(compareRoutes);\n}\n\n/**\n * Las rutas del shell salen de `uiViews`, no de `ResourceSchema.views` (que\n * conserva los metadatos de consultas del backend). Añadir o cambiar una\n * página requiere una declaración y un descriptor `kind: 'view'`.\n *\n * Los hijos se construyen DENTRO del `loadChildren` (en la primera\n * navegación), nunca en la evaluación del módulo: el consumidor importa\n * este barrel desde `app.routes.ts` ANTES de que `provideGasAdmin` siembre\n * el catálogo (corre en el cuerpo de `app.config`), y el paquete compilado\n * es eager — construir en el import congelaría `children: []` para siempre\n * y toda URL protegida renderizaría vacío (regresión: shell.routes.spec en\n * orden de producción).\n */\nexport const shellRoutes: Routes = [\n {\n path: '',\n loadChildren: () => [\n {\n path: '',\n component: Layout,\n children: resourceRoutes(),\n },\n ],\n },\n];\n","import { Component, inject, signal } from '@angular/core';\nimport { Router } from '@angular/router';\n\nimport { ApiError } from '../../core/models/api.model';\nimport { AuthService, LOGIN_PATH } from '../../core/services/auth.service';\nimport { Button } from '../../shared/components/button/button';\nimport { getAuth } from '../../core/registry/schema-catalog';\n\n/** Copy de las etiquetas — vive acá, no en el schema: una sola pantalla. */\nconst LABELS: Record<string, string> = {\n email: 'Correo electrónico',\n password: 'Contraseña',\n};\n\nconst FIELD_BASE =\n 'mt-1 w-full rounded-lg border border-neutral/40 bg-surface px-3 py-2 text-sm text-neutral placeholder:text-neutral/50 focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-primary';\n\n/**\n * Login (auth.md §5.1).\n *\n * **Pintada a partir de `authSchema.loginFields`**: si mañana cambian los\n * campos, éste componente no se toca — sólo se mueven claves y etiquetas.\n * No usa `FieldHost` porque el tipo `password` todavía no existe en\n * `FieldSchema` (es pieza nueva de Fase 3, R3.3); el `@switch` de abajo es\n * exactamente el reemplazo que R3.x hará por el registro de render.\n *\n * **No hay casilla «recordarme»**: D10-3 negó `localStorage` (§9.3) y el\n * token muere con la pestaña. Poner un checkbox sería prometer que la\n * sesión sobrevive a cerrar el navegador.\n */\n@Component({\n selector: 'app-login',\n imports: [Button],\n template: `\n <main class=\"flex min-h-[100svh] items-center justify-center bg-surface px-4\">\n <div\n class=\"w-full max-w-sm rounded-xl border border-neutral/20 bg-surface-raised p-8 shadow-sm\"\n >\n <h1 class=\"font-display text-lg font-semibold\">Admin</h1>\n <p class=\"mt-1 text-sm text-neutral\">Inicia sesión para continuar</p>\n\n @if (providers.includes('google')) {\n <button\n type=\"button\"\n id=\"btn-login-google\"\n app-button\n variant=\"outline\"\n [loading]=\"providerLoading() === 'google'\"\n (click)=\"loginWithGoogle()\"\n class=\"mt-6 w-full\"\n >\n Continuar con Google\n </button>\n <div class=\"my-4 flex items-center gap-3\">\n <hr class=\"flex-1 border-neutral/20\" />\n <span class=\"text-xs text-neutral/50\">o</span>\n <hr class=\"flex-1 border-neutral/20\" />\n </div>\n }\n\n <form\n [class]=\"providers.includes('google') ? 'space-y-4' : 'mt-6 space-y-4'\"\n (submit)=\"submit($event)\"\n novalidate\n >\n @for (field of schema.loginFields; track field.key) {\n <div>\n <label [attr.for]=\"field.key\" class=\"block text-sm font-medium text-neutral\">\n {{ label(field.key) }}\n </label>\n\n @switch (field.type) {\n @case ('email') {\n <input\n [attr.id]=\"field.key\"\n [attr.name]=\"field.key\"\n type=\"email\"\n autocomplete=\"email\"\n inputmode=\"email\"\n [attr.required]=\"field.required ? '' : null\"\n [value]=\"value(field.key)\"\n (input)=\"setValue(field.key, $any($event.target).value)\"\n class=\"${FIELD_BASE}\"\n />\n }\n @default {\n <input\n [attr.id]=\"field.key\"\n [attr.name]=\"field.key\"\n type=\"password\"\n autocomplete=\"current-password\"\n [attr.required]=\"field.required ? '' : null\"\n [value]=\"value(field.key)\"\n (input)=\"setValue(field.key, $any($event.target).value)\"\n class=\"${FIELD_BASE}\"\n />\n }\n }\n </div>\n }\n\n @if (error(); as message) {\n <p role=\"alert\" class=\"rounded-lg bg-danger/10 px-3 py-2 text-sm text-danger\">\n {{ message }}\n </p>\n }\n\n <button app-button type=\"submit\" [loading]=\"loading()\" class=\"w-full\">\n Iniciar sesión\n </button>\n </form>\n </div>\n </main>\n `,\n})\nexport class Login {\n private readonly auth = inject(AuthService);\n private readonly router = inject(Router);\n\n // Se resuelve en runtime: el catálogo lo fija `provideGasAdmin` en el bootstrap.\n readonly schema = getAuth()!;\n readonly providers = this.schema.providers;\n readonly error = signal<string | null>(null);\n readonly loading = signal(false);\n readonly providerLoading = signal<string | null>(null);\n\n /** Valores por clave: el set lo pinta el `@for` del schema. */\n private readonly values = signal<Record<string, string>>({});\n\n label(key: string): string {\n return LABELS[key] ?? key;\n }\n\n value(key: string): string {\n return this.values()[key] ?? '';\n }\n\n setValue(key: string, next: string): void {\n this.values.update((current) => ({ ...current, [key]: next }));\n }\n\n loginWithGoogle(): void {\n if (this.providerLoading()) return;\n this.providerLoading.set('google');\n this.error.set(null);\n this.auth.loginWithGoogle().subscribe({\n next: () => {\n this.providerLoading.set(null);\n this.goAfterLogin();\n },\n error: (err: unknown) => {\n this.providerLoading.set(null);\n this.error.set(\n err instanceof ApiError && err.message !== ''\n ? err.message\n : err instanceof Error && err.message !== ''\n ? err.message\n : 'No se pudo iniciar sesión con Google.',\n );\n },\n });\n }\n\n submit(event: Event): void {\n event.preventDefault();\n if (this.loading()) return;\n\n const email = this.value('email').trim();\n const password = this.value('password');\n if (email === '' || password === '') {\n this.error.set('Completa el correo y la contraseña.');\n return;\n }\n\n if (this.schema.firebase && this.schema.providers.includes('password')) {\n this.loading.set(true);\n this.error.set(null);\n this.auth.loginWithFirebasePassword(email, password).subscribe({\n next: () => {\n this.loading.set(false);\n this.goAfterLogin();\n },\n error: (err: unknown) => {\n this.loading.set(false);\n this.error.set(\n err instanceof ApiError && err.message !== ''\n ? err.message\n : err instanceof Error && err.message !== ''\n ? err.message\n : 'No se pudo iniciar sesión.',\n );\n },\n });\n return;\n }\n\n this.loading.set(true);\n this.error.set(null);\n this.auth.login(email, password).subscribe({\n next: () => {\n this.loading.set(false);\n this.goAfterLogin();\n },\n error: (err: unknown) => {\n this.loading.set(false);\n // El mensaje del backend ya viene en español y ya describe el\n // caso («credenciales inválidas»): reformatearlo acá sólo\n // duplicaría la verdad y se perdería si cambia el texto del back.\n this.error.set(\n err instanceof ApiError && err.message !== ''\n ? err.message\n : 'No se pudo iniciar sesión.',\n );\n },\n });\n }\n\n private goAfterLogin(): void {\n const previous = this.auth.consumeReturnUrl();\n const target =\n previous !== null && previous !== '' && !previous.startsWith(LOGIN_PATH)\n ? previous\n : this.schema.redirectAfterLogin;\n void this.router.navigateByUrl(target).catch(() => undefined);\n }\n}\n\n","import { Component, computed, input } from '@angular/core';\n\nimport type { WidgetSchema } from '../../core/models/schema.model';\nimport { RenderHost } from '../../core/registry/render-host';\nimport { Skeleton } from '../../shared/components/skeleton/skeleton';\n\n/** Ancho mobile (base, sin prefijo) — literales para JIT de Tailwind v4. */\nconst MOBILE_SPANS: Record<number, string> = {\n 1: 'col-span-1',\n 2: 'col-span-2',\n 3: 'col-span-3',\n 4: 'col-span-4',\n 5: 'col-span-5',\n 6: 'col-span-6',\n 7: 'col-span-7',\n 8: 'col-span-8',\n 9: 'col-span-9',\n 10: 'col-span-10',\n 11: 'col-span-11',\n 12: 'col-span-12',\n};\n\n/** Ancho responsive (sm/lg) — literales para JIT de Tailwind v4. */\nconst RESPONSIVE_SPANS: Record<number, string> = {\n 12: '',\n 11: 'lg:col-span-11',\n 10: 'lg:col-span-10',\n 9: 'lg:col-span-9',\n 8: 'lg:col-span-8',\n 7: 'sm:col-span-6 lg:col-span-7',\n 6: 'sm:col-span-6',\n 5: 'sm:col-span-6 lg:col-span-5',\n 4: 'sm:col-span-6 lg:col-span-4',\n 3: 'sm:col-span-6 lg:col-span-3',\n 2: 'sm:col-span-6 lg:col-span-2',\n 1: 'sm:col-span-6 lg:col-span-1',\n};\n\n/**\n * Adaptador de widgets (R3.6 · `doc/plan-refactor-motor.md` §3.6).\n *\n * Ya no despacha: `WidgetHost` era un `@switch` de seis ramas con los seis\n * componentes importados a mano, exactamente lo que el registro viene a\n * eliminar. Hoy sólo conserva lo que es **suyo** — traducir el contrato\n * histórico `widget`/`data` al contrato genérico del motor\n * (`type`/`config`/`data`) y pintar la clase de ancho en la grilla, que es\n * una decisión del panel y no del motor de render.\n *\n * El `@switch`, los seis imports y `SUPPORTED_WIDGET_TYPES` desaparecieron:\n * el tipo sale por `[type]`, el registro devuelve el componente y el\n * `<app-render-host>` lo monta. Añadir un widget ⇒ carpeta + descriptor +\n * una línea en `widgets/descriptors.ts` (R3.8).\n *\n * Nota sobre la accesibilidad: los widgets no llevan `aria-live`. Cada uno\n * ya publica su propio estado (`role=\"img\"` con resumen, `data-testid`,\n * texto visible) y envolver el outlet entero en un `aria-live` anunciaría\n * el panel completo con cada actualización de datos.\n */\n@Component({\n selector: 'app-widget-host',\n imports: [RenderHost, Skeleton],\n host: {\n '[class]': 'gridSpanClass()',\n },\n template: `\n @if (loading()) {\n <!-- Reserva el espacio del widget mientras su dato viaja (la red\n Apps Script tarda ~1s): sin esto el panel se veía \"completo\"\n con valores vacíos que después aparecían solos (cargar ≠ vacío,\n doc/ux-ui-mobile.md §estados de UI). La grilla mantiene su\n span porque la clase la pone el host. -->\n <div class=\"rounded-lg border border-neutral/20 bg-surface p-4\">\n <app-skeleton [lines]=\"1\" [height]=\"14\" width=\"40%\" />\n <div class=\"mt-3\">\n <app-skeleton [lines]=\"2\" [height]=\"32\" />\n </div>\n </div>\n } @else {\n <app-render-host kind=\"widget\" [type]=\"widget().type\" [config]=\"widget()\" [data]=\"data()\" />\n }\n `,\n})\nexport class WidgetHost {\n readonly widget = input.required<WidgetSchema>();\n readonly data = input<unknown>(null);\n /** El dato del widget todavía viaja: la tarjeta muestra skeleton. */\n readonly loading = input(false);\n\n /** Clases de ancho responsive (literales: Tailwind v4 es JIT, no interpolar). */\n protected readonly gridSpanClass = computed(() => {\n const w = this.widget().width ?? 4;\n const mw = this.widget().mobileWidth ?? 12;\n\n const mobile = MOBILE_SPANS[mw] ?? 'col-span-12';\n const responsive = RESPONSIVE_SPANS[w] ?? 'sm:col-span-6 lg:col-span-4';\n\n return [mobile, responsive, 'block'].filter(Boolean).join(' ');\n });\n}\n","import { Component, computed, inject, input, resource } from '@angular/core';\nimport { toObservable, toSignal } from '@angular/core/rxjs-interop';\nimport { catchError, of, switchMap } from 'rxjs';\n\nimport type { DataSource, ResourceSchema, WidgetSchema } from '../../core/models/schema.model';\nimport { ApiService } from '../../core/services/api.service';\nimport { DataSourceService, type SourceRow } from '../../core/services/data-source.service';\nimport { EmptyState } from '../../shared/components/empty-state/empty-state';\nimport { WidgetHost } from '../../widgets/widget-host/widget-host';\n\n/**\n * Panel de métricas — tercer `kind` del registry (refactormotor.md F4).\n *\n * Sólo lectura: no hay CRUD y los datos llegan de un endpoint declarativo\n * (Etapa 2, S4). Los widgets se declaran en `widgets` del schema — no en\n * `fields`, porque un KPI no es un campo (ver `ResourceSchema.widgets`).\n *\n * Dos formas de dato, la que declare cada widget (F8 · R6.5):\n *\n * - **payload**: el `key` del widget es su clave en la respuesta del\n * endpoint; `source: { kind: 'payload' }` lee la respuesta entera.\n * - **fuente declarada** (`source: { resource, view?, params? }`): el widget\n * pide sus filas al `DataSourceService` y deja de depender de que un\n * handler haya armado `data[key]`. Acá se piden TODAS juntas, así que un\n * panel con seis fuentes iguales hace una vuelta, no seis (la caché del\n * servicio es compartida con relations y selects).\n */\n@Component({\n selector: 'app-dashboard',\n imports: [EmptyState, WidgetHost],\n template: `\n <header class=\"mb-6\">\n <h1 class=\"font-display text-2xl font-semibold\">{{ schema().labelPlural }}</h1>\n <p class=\"text-sm text-neutral\">Dashboard · {{ widgets().length }} widgets</p>\n </header>\n\n @if (falloFuentes()) {\n <!-- Degradación visible (F9): sin este aviso el fallo de una fuente\n se confundiría con «el panel no tiene datos». -->\n <p class=\"mb-4 text-sm text-danger\" role=\"alert\">\n No se pudieron cargar los datos de algunos widgets.\n </p>\n }\n\n @if (widgets().length === 0) {\n <app-empty-state\n title=\"Sin widgets declarados\"\n message=\"Los widgets se declaran en la propiedad widgets del schema del dashboard.\"\n />\n } @else {\n <div class=\"grid grid-cols-12 gap-4\">\n @for (widget of widgets(); track widget.key) {\n <app-widget-host [widget]=\"widget\" [data]=\"datum(widget)\" [loading]=\"cargando()\" />\n }\n </div>\n }\n `,\n})\nexport class Dashboard {\n private readonly api = inject(ApiService, { optional: true });\n private readonly ds = inject(DataSourceService);\n\n /** Descriptor del dashboard (lo resuelve `ResourcePage` contra el registry). */\n readonly schema = input.required<ResourceSchema>();\n\n /**\n * Respuesta del endpoint de métricas indexada por `key` de widget.\n * Si se pasa explícitamente (como en tests con fixtures), se usa directamente;\n * de lo contrario se obtienen los datos de forma reactiva desde el endpoint `get`.\n */\n readonly data = input<Record<string, unknown> | null>(null);\n\n private readonly fetchedData = toSignal(\n toObservable(this.schema).pipe(\n switchMap((schema) => {\n const getEndpoint = schema.endpoint?.get;\n // `of({})` = SIN get contratado: ya hay respuesta (vacía). El\n // `null` inicial del signal en cambio marca «todavía viaja».\n if (!getEndpoint || !this.api) return of<Record<string, unknown>>({});\n return this.api\n .request<Record<string, unknown>>('GET', getEndpoint)\n .pipe(catchError(() => of<Record<string, unknown>>({})));\n }),\n ),\n { initialValue: null },\n );\n\n protected readonly effectiveData = computed<Record<string, unknown>>(() => {\n const override = this.data();\n if (override !== null && override !== undefined) return override;\n return this.fetchedData() ?? {};\n });\n\n protected readonly widgets = computed(() => this.schema().widgets ?? []);\n\n // ── Fuentes declaradas (F8 · R6.5) ──────────────────────────────────────\n\n /**\n * Claves de los widgets con fuente declarada, en una cadena: es el\n * `params` del `resource` y sólo cambia cuando cambia el panel. `''`\n * (ninguna fuente) ⇒ `undefined` ⇒ el resource queda idle y no pide nada.\n */\n private readonly clavesFuente = computed(() =>\n this.widgets()\n .map((widget) => (esFuente(widget.source) ? widget.key : ''))\n .filter((key) => key !== '')\n .join('|'),\n );\n\n /**\n * Filas de todos los widgets con `source`, pedidas de una sola vez.\n *\n * Cada fila queda bajo `widget.key`, que es el mismo contrato de lectura\n * que tiene el payload: `datum()` devuelve filas cuando hay fuente y la\n * clave de siempre cuando no, y el descriptor del widget traduce la forma\n * que necesita.\n */\n private readonly fuentes = resource({\n params: () => (this.clavesFuente() === '' ? undefined : this.clavesFuente()),\n loader: async () => {\n const filas: Record<string, SourceRow[]> = {};\n for (const widget of this.widgets()) {\n if (!esFuente(widget.source)) continue;\n filas[widget.key] = await this.ds.rows(widget.source);\n }\n return filas;\n },\n });\n\n /** ¿Alguna fuente del panel falló? (`status()` no tira; `value()` sí.) */\n protected readonly falloFuentes = computed(() => this.fuentes.status() === 'error');\n\n /**\n * Estado de carga del panel — mientras dure, cada tarjeta reserva su\n * espacio con skeleton (`cargar ≠ vacío`, doc/ux-ui-mobile.md §estados).\n * Cubre LOS dos caminos de dato: el payload (`null` inicial = sin\n * respuesta todavía) y las fuentes declaradas (`resource` en loading;\n * con params vacíos queda idle, que NO es carga).\n */\n protected readonly cargando = computed(() => {\n const payloadViaja = this.data() === null && this.fetchedData() === null;\n const fuentesViajan = this.fuentes.status() === 'loading';\n return payloadViaja || fuentesViajan;\n });\n\n /** `null` explícito cuando la clave no vino: el widget muestra su vacío. */\n protected datum(widget: WidgetSchema): unknown {\n // Fuente declarada: el dato son SUS filas, no una clave del payload.\n if (esFuente(widget.source)) {\n // `value()` del resource TIRA en estado error (contract de Angular):\n // se mira el estado antes de leerlo. En carga devuelve `null` igual\n // que el camino de payload, que también arranca vacío.\n return this.fuentes.status() === 'error'\n ? null\n : (this.fuentes.value()?.[widget.key] ?? null);\n }\n const allData = this.effectiveData();\n // `source` lo declara el schema: el orquestador no sabe qué tipo es\n // ni qué claves compone cada widget.\n return widget.source?.kind === 'payload' ? allData : (allData[widget.key] ?? null);\n }\n}\n\n/** ¿Este widget declara una fuente (`{ resource, … }`) y no el payload? */\nfunction esFuente(source: WidgetSchema['source']): source is DataSource {\n return source !== undefined && source !== null && 'resource' in source;\n}\n","import { Service, inject } from '@angular/core';\n\nimport {\n actionType,\n getAction,\n type ActionContext,\n type ActionRequest,\n type ActionResult,\n} from './action-registry';\nimport type { ResourceAction } from '../core/models/schema.model';\nimport { ConfigService } from '../core/services/config.service';\nimport { ErrorMapperService } from '../core/services/error-mapper.service';\n\n/**\n * Ejecuta una acción declarada en el schema (R3.9 · `refactormotor.md`).\n *\n * Sustituye el dispatch por tipo que hoy vive en `list-view` y en las\n * copias de `detail-view` y `post-create-modal` (R3.11). El camino siempre\n * es el mismo:\n *\n * 1. resolver el descriptor (si no existe, **rompe** — nada de «disponible\n * próximamente», que hoy oculta esquemas mal declarados);\n * 2. pedir confirmación si el descriptor la exige;\n * 3. montar el `ActionContext` (petición + `vars` de interpolación);\n * 4. ejecutar y, si el descriptor lanza, notificar el error y devolver\n * `{ ok: false }` para que la vista no trate el fallo como éxito.\n */\n@Service()\nexport class ActionService {\n private readonly config = inject(ConfigService);\n private readonly errores = inject(ErrorMapperService);\n\n async run(action: ResourceAction, request: ActionRequest): Promise<ActionResult> {\n const type = actionType(action);\n const descriptor = getAction(type);\n if (!descriptor) {\n throw new Error(\n `[actions] «${action.label}» no tiene descriptor registrado (type \"${type}\").`,\n );\n }\n\n if (descriptor.confirm) {\n const aceptado = await request.toast.confirm({\n title: action.label,\n message: confirmMessage(action),\n confirmLabel: 'Confirmar',\n cancelLabel: 'Cancelar',\n });\n if (!aceptado) {\n return { ok: false };\n }\n }\n\n // `copy-share` (R8.7) resuelve su mensaje contra la config global\n // (`share_message_template`). Si el load del bootstrap no llegó (arranque\n // sin sesión o red lenta), lo pedimos ahora —con la sesión ya iniciada—\n // ANTES de congelar `vars`: si no, el mensaje copiado degradaría a\n // «sólo la URL» hasta el próximo F5.\n if (descriptor.type === 'copy-share') {\n await this.config.ensureLoaded();\n }\n\n const ctx: ActionContext = { ...request, vars: this.vars(request.record) };\n try {\n return await descriptor.run(ctx, action);\n } catch (err) {\n request.toast.error(this.errores.messageOf(err));\n return { ok: false };\n }\n }\n\n /**\n * Config global + registro. Sin registro (`header`, `bulk`) el contexto de\n * interpolación queda vacío: el descriptor no tiene nada que interpolar.\n *\n * Público porque `detail-view` sólo **presenta** una acción (pinta\n * `<app-copy-share>`) en vez de ejecutarla, y aun así necesita el mismo\n * contexto: R3.11 acabó con la tercera copia de esta lógica.\n */\n vars(record: Record<string, unknown> | null): Record<string, unknown> {\n return record ? this.config.buildInterpolationContext(record) : {};\n }\n}\n\nfunction confirmMessage(action: ResourceAction): string {\n return 'confirmMessage' in action && action.confirmMessage ? action.confirmMessage : action.label;\n}\n","import type {\n FieldSchema,\n FieldType,\n ListColumn,\n ListColumnFormat,\n} from '../core/models/schema.model';\n\n/**\n * **Tabla de formato de columna por defecto según `field.type`** (R4.4 del\n * plan): «declarada, no implícita» — `column.format` sólo sirve para\n * sobreescribirla.\n *\n * Es la hermana de `fields/display/display-defaults.ts` (R4.1): un solo\n * sitio decide qué pinta una columna cuando el schema no dice nada, en vez\n * de un `?? 'text'` repartido por la vista. El vocabulario se alinea con el\n * display del detalle — `select` es chip, `date` es fecha, `image` es\n * miniatura — con las dos concesiones que sólo el listado puede hacer:\n * `boolean` baja al interruptor (R4.3) en vez de al chip, y `list`, que en\n * la ficha es timeline, acá sigue siendo texto porque el registro de\n * formatos no tiene timeline (D41).\n *\n * `Record<FieldType, …>` es el **tripwire de exhaustividad en compilación**:\n * un tipo nuevo obliga a elegir su formato de lista antes de que el build\n * pase, y un tipo retirado obliga a limpiar la entrada.\n */\nexport const FORMATO_POR_DEFECTO: Record<FieldType, ListColumnFormat> = {\n text: 'text',\n textarea: 'text',\n slug: 'text',\n url: 'text',\n email: 'text',\n phone: 'text',\n 'readonly-text': 'text',\n relation: 'text',\n group: 'text',\n 'key-value': 'text',\n 'string-list': 'text',\n multiselect: 'text',\n time: 'text',\n list: 'text',\n date: 'date',\n number: 'number',\n currency: 'currency',\n boolean: 'boolean',\n select: 'badge',\n image: 'thumbnail',\n // Un archivo no tiene miniatura: en la lista va como URL en crudo (R9.5.3).\n file: 'text',\n // Un color se pinta como texto (el hex #RRGGBB) en el listado.\n color: 'text',\n // Un rich-select se pinta como badge con el label de la opción.\n 'rich-select': 'badge',\n // Un dropdown se pinta como badge con el label de la opción.\n dropdown: 'badge',\n};\n\n/**\n * Formato por defecto para un `type`, o `undefined` si el schema lo escribió\n * mal (tipo fuera de la unión). La degradación — igual que `displayLoader()` —\n * es `text`, que era el `@default` del `@switch` que R4.3 reemplazó.\n */\nexport function formatoPorTipo(type: string): ListColumnFormat | undefined {\n return Object.prototype.hasOwnProperty.call(FORMATO_POR_DEFECTO, type)\n ? FORMATO_POR_DEFECTO[type as FieldType]\n : undefined;\n}\n\n/**\n * Formato final de una columna: la declaración del schema si existe, si no,\n * la tabla de arriba según el `type` del campo con esa `key`, y si la columna\n * ni siquiera aparece en `fields` (columna derivada o key inventada), `text`.\n *\n * Es la única puerta por la que `list-view` decide qué pinta una celda.\n */\nexport function formatoDeColumna(\n column: ListColumn,\n fields: readonly FieldSchema[],\n): ListColumnFormat {\n if (column.format) {\n return column.format;\n }\n const field = fields.find((candidate) => candidate.key === column.key);\n return (field ? formatoPorTipo(field.type) : undefined) ?? 'text';\n}\n","import { CdkDrag, CdkDragDrop, CdkDragHandle, CdkDropList } from '@angular/cdk/drag-drop';\nimport { Component, computed, effect, inject, input, output, signal } from '@angular/core';\nimport { Router } from '@angular/router';\nimport { firstValueFrom } from 'rxjs';\n\nimport {\n LucideChevronDown,\n LucideChevronRight,\n LucideChevronUp,\n LucideDynamicIcon,\n LucideEye,\n LucideGripVertical,\n LucidePencil,\n LucidePlus,\n LucideRotateCw,\n LucideTrash2,\n LucideZap,\n} from '@lucide/angular';\nimport type { LucideIcon } from '@lucide/angular';\nimport { actionType, getAction } from '../../actions/action-registry';\nimport { ActionService } from '../../actions/action-service';\nimport { ApiError } from '../../core/models/api.model';\nimport type {\n FieldSchema,\n ListColumn,\n ListColumnFormat,\n ListFilter,\n ResourceAction,\n ResourceSchema,\n SelectOption,\n} from '../../core/models/schema.model';\nimport { ApiService } from '../../core/services/api.service';\nimport { AuthService } from '../../core/services/auth.service';\nimport { NotificationService } from '../../core/services/notification.service';\nimport { DrawerForm } from '../drawer-form/drawer-form';\nimport { RenderHost } from '../../core/registry/render-host';\nimport { getSchema } from '../../core/registry/schema-catalog';\nimport { Button } from '../../shared/components/button/button';\nimport { EmptyState } from '../../shared/components/empty-state/empty-state';\nimport { Skeleton } from '../../shared/components/skeleton/skeleton';\nimport { getValue } from '../../shared/utils/field-path';\nimport { formatoDeColumna } from '../../columns/column-defaults';\nimport type { ColumnCell } from '../../columns/column-cell';\n\ntype Row = Record<string, unknown>;\ntype Status = 'loading' | 'ready' | 'error';\n\nconst BOOLEAN_FILTER_OPTIONS: SelectOption[] = [\n { value: '', label: 'Todos' },\n { value: 'yes', label: 'Sí' },\n { value: 'no', label: 'No' },\n];\n\nfunction errorMessage(err: unknown, fallback: string): string {\n return err instanceof ApiError ? err.message : fallback;\n}\n\n/**\n * Listado genérico de una colección (base.md §10 «Listado»): tabla desde\n * `listColumns` con los 7 formatos **resueltos por el registro** (R4.3),\n * búsqueda/filtros **en el cliente** (el\n * contrato de api.md no define parámetros de consulta: el endpoint admin\n * devuelve todos los registros), interruptor `active`, acciones de fila y\n * estados universales (skeleton, vacío, error con reintento).\n */\n@Component({\n selector: 'app-list-view',\n imports: [\n Button,\n CdkDrag,\n CdkDragHandle,\n CdkDropList,\n DrawerForm,\n EmptyState,\n LucideChevronDown,\n LucideChevronRight,\n LucideChevronUp,\n LucideEye,\n LucideGripVertical,\n LucidePencil,\n LucidePlus,\n LucideRotateCw,\n LucideDynamicIcon,\n LucideTrash2,\n LucideZap,\n RenderHost,\n Skeleton,\n ],\n template: `\n <section class=\"space-y-4\" aria-label=\"Listado\" [attr.aria-busy]=\"status() === 'loading'\">\n <div class=\"flex flex-wrap items-end justify-between gap-3\">\n <div class=\"flex flex-wrap items-end gap-3\">\n @if (schema().search?.length) {\n <label class=\"flex flex-col gap-1 text-xs font-medium text-neutral\">\n Buscar\n <input\n type=\"search\"\n class=\"w-56 rounded-lg border border-neutral/30 bg-surface px-3 py-2 text-sm\"\n placeholder=\"Buscar…\"\n [value]=\"query()\"\n (input)=\"setQuery(inputValue($event))\"\n />\n </label>\n }\n @for (filter of schema().filters ?? []; track filter.key) {\n <label class=\"flex flex-col gap-1 text-xs font-medium text-neutral\">\n {{ filter.label }}\n @if (filter.type === 'text') {\n <input\n type=\"search\"\n class=\"w-44 rounded-lg border border-neutral/30 bg-surface px-3 py-2 text-sm\"\n [value]=\"filterValues()[filter.key] ?? ''\"\n (input)=\"setFilter(filter.key, inputValue($event))\"\n />\n } @else {\n <select\n class=\"w-44 rounded-lg border border-neutral/30 bg-surface px-3 py-2 text-sm\"\n [value]=\"filterValues()[filter.key] ?? ''\"\n (change)=\"setFilter(filter.key, selectValue($event))\"\n >\n @for (option of filterOptions(filter); track option.value) {\n <option [value]=\"option.value\">{{ option.label }}</option>\n }\n </select>\n }\n </label>\n }\n </div>\n @if (canCreate()) {\n <button app-button type=\"button\" (click)=\"goCreate()\">\n <svg lucidePlus size=\"16\" />\n Crear {{ schema().label }}\n </button>\n }\n </div>\n\n @switch (status()) {\n @case ('loading') {\n <app-skeleton [lines]=\"6\" [height]=\"44\" />\n }\n @case ('error') {\n <app-empty-state\n title=\"No se pudo cargar el listado\"\n message=\"Revisa tu conexión e intenta de nuevo.\"\n >\n <button app-button variant=\"outline\" type=\"button\" (click)=\"retry()\">\n <svg lucideRotateCw size=\"16\" />\n Reintentar\n </button>\n </app-empty-state>\n }\n @case ('ready') {\n @if (visibleRows().length === 0) {\n <app-empty-state\n [title]=\"rows().length === 0 ? 'Sin registros' : 'Sin resultados'\"\n [message]=\"\n rows().length === 0\n ? 'Todavía no hay registros en este recurso.'\n : 'Ningún registro coincide con la búsqueda o los filtros.'\n \"\n >\n @if (rows().length > 0) {\n <button app-button variant=\"outline\" type=\"button\" (click)=\"clearFilters()\">\n Limpiar filtros\n </button>\n } @else if (canCreate()) {\n <button app-button type=\"button\" (click)=\"goCreate()\">\n <svg lucidePlus size=\"16\" />\n Crear el primero\n </button>\n }\n </app-empty-state>\n } @else {\n <!-- La tabla es la vista de escritorio: se oculta bajo lg para\n que la lista de tarjetas de abajo sea la ÚNICA vista en\n móvil (con las dos visibles, el listado se duplicaba). -->\n <div\n class=\"hidden overflow-x-auto rounded-xl border border-neutral/20 bg-surface lg:block\"\n >\n <table class=\"w-full text-sm\">\n <thead>\n <tr class=\"border-b border-neutral/20 text-left text-xs uppercase text-neutral\">\n @if (canReorder()) {\n <th scope=\"col\" class=\"w-16 px-2 py-3 font-medium\">\n <span class=\"sr-only\">Orden</span>\n </th>\n }\n @for (column of schema().listColumns; track column.key) {\n <th scope=\"col\" class=\"px-4 py-3 font-medium\">\n {{ column.label }}\n </th>\n }\n @if (hasRowActions()) {\n <th scope=\"col\" class=\"px-4 py-3 text-right font-medium\">Acciones</th>\n }\n </tr>\n </thead>\n <tbody\n cdkDropList\n [cdkDropListData]=\"visibleRows()\"\n [cdkDropListDisabled]=\"dropDisabled()\"\n (cdkDropListDropped)=\"onDrop($event)\"\n >\n @for (row of visibleRows(); track keyOf(row); let i = $index) {\n <tr\n class=\"border-b border-neutral/10 last:border-0 hover:bg-neutral/5 transition-colors\"\n [class.cursor-pointer]=\"isRowClickable()\"\n cdkDrag\n [cdkDragDisabled]=\"dropDisabled()\"\n (click)=\"onRowClick(row)\"\n >\n @if (canReorder()) {\n <td class=\"px-2 py-2.5 align-middle\" (click)=\"$event.stopPropagation()\">\n <div class=\"flex items-center gap-1\">\n <button\n type=\"button\"\n class=\"cursor-grab text-neutral hover:text-primary\"\n cdkDragHandle\n [attr.aria-label]=\"'Reordenar ' + titleOf(row)\"\n title=\"Arrastrar para reordenar\"\n >\n <svg lucideGripVertical size=\"16\" />\n </button>\n <span class=\"flex flex-col leading-none\">\n <button\n type=\"button\"\n class=\"px-1 text-xs text-neutral hover:text-primary disabled:opacity-30\"\n [disabled]=\"i === 0 || reordering()\"\n [attr.aria-label]=\"'Subir ' + titleOf(row)\"\n (click)=\"moveBy(keyOf(row), -1)\"\n >\n <svg lucideChevronUp size=\"16\" />\n </button>\n <button\n type=\"button\"\n class=\"px-1 text-xs text-neutral hover:text-primary disabled:opacity-30\"\n [disabled]=\"i === visibleRows().length - 1 || reordering()\"\n [attr.aria-label]=\"'Bajar ' + titleOf(row)\"\n (click)=\"moveBy(keyOf(row), 1)\"\n >\n <svg lucideChevronDown size=\"16\" />\n </button>\n </span>\n </div>\n </td>\n }\n @for (column of schema().listColumns; track column.key) {\n <td class=\"px-4 py-2.5 align-middle\">\n <!-- Los 7 formatos salen del registro (R4.3):\n la vista sólo elige cuál y entrega la celda. -->\n <app-render-host\n kind=\"column-format\"\n [type]=\"formatOf(column)\"\n [config]=\"column\"\n [data]=\"cellOf(row, column)\"\n />\n </td>\n }\n @if (hasRowActions()) {\n <td class=\"px-4 py-2.5\" (click)=\"$event.stopPropagation()\">\n <div class=\"flex items-center justify-end gap-1\">\n @if (schema().detail?.enabled) {\n <button\n app-button\n variant=\"ghost\"\n type=\"button\"\n (click)=\"goView(row)\"\n title=\"Ver ficha de detalle\"\n class=\"h-8 px-2.5 text-xs text-neutral-light hover:text-primary hover:bg-primary/10\"\n >\n <svg lucideEye size=\"15\" />\n <span>Ver</span>\n </button>\n }\n @for (action of schema().actions ?? []; track action.id) {\n <button\n app-button\n variant=\"ghost\"\n type=\"button\"\n [title]=\"action.label\"\n [attr.aria-label]=\"action.label\"\n (click)=\"runAction(action, row)\"\n class=\"h-8 px-2 text-xs text-neutral hover:text-primary hover:bg-primary/10\"\n >\n @let actionIcon = iconOf(action);\n @if (actionIcon) {\n <svg [lucideIcon]=\"actionIcon\" size=\"15\" />\n } @else {\n {{ action.label }}\n }\n </button>\n }\n @if (canQuickEdit()) {\n <button\n app-button\n variant=\"ghost\"\n type=\"button\"\n (click)=\"quickEditRow.set(row)\"\n title=\"Edición rápida\"\n class=\"h-8 px-2 text-xs text-neutral hover:text-primary hover:bg-neutral/10\"\n >\n <svg lucideZap size=\"15\" />\n <span>Edición rápida</span>\n </button>\n }\n @if (canUpdate()) {\n <button\n app-button\n variant=\"ghost\"\n type=\"button\"\n (click)=\"goEdit(row)\"\n title=\"Editar registro\"\n class=\"h-8 px-2.5 text-xs text-primary hover:bg-primary/10\"\n >\n <svg lucidePencil size=\"15\" />\n <span>Editar</span>\n </button>\n }\n @if (canRemove()) {\n <button\n app-button\n variant=\"ghost\"\n type=\"button\"\n (click)=\"removeRow(row)\"\n title=\"Eliminar registro\"\n class=\"h-8 px-2 text-xs text-danger hover:bg-danger/10\"\n >\n <svg lucideTrash2 size=\"15\" />\n <span>Eliminar</span>\n </button>\n }\n </div>\n </td>\n }\n </tr>\n }\n </tbody>\n </table>\n </div>\n\n <!-- Vista de tarjetas — mobile-first (guía mobile §6).\n La tabla de arriba necesita scroll horizontal con 5–7 columnas\n (el anti-patrón #2): en pantallas chicas se convierte en una\n lista de tarjetas de dos niveles (principal + secundario),\n donde cada fila completa es un objetivo táctil. lg:hidden la\n retira cuando la tabla vuelve a ser usable. -->\n <ul class=\"space-y-2 lg:hidden\">\n @for (row of visibleRows(); track keyOf(row)) {\n <li\n class=\"rounded-xl border border-neutral/20 bg-surface p-3\"\n [class.cursor-pointer]=\"isRowClickable()\"\n (click)=\"onRowClick(row)\"\n >\n <!-- Nivel principal: el título del registro, siempre visible\n (guía §6.2: el identificador nunca se esconde). -->\n <div class=\"flex items-start justify-between gap-2\">\n <span class=\"text-sm font-medium\">\n {{ titleOf(row) }}\n </span>\n @if (schema().detail?.enabled) {\n <svg\n lucideChevronRight\n size=\"16\"\n class=\"mt-0.5 shrink-0 text-neutral\"\n aria-hidden=\"true\"\n />\n }\n </div>\n <!-- Nivel secundario: el resto de las columnas como pares\n etiqueta/valor. Sin hover: todo lo accionable va en el\n pie de la tarjeta, al alcance del pulgar. -->\n <dl class=\"mt-2 grid grid-cols-[auto_1fr] gap-x-3 gap-y-1 text-xs\">\n @for (column of schema().listColumns; track column.key) {\n <dt class=\"text-neutral\">{{ column.label }}</dt>\n <dd>\n <app-render-host\n kind=\"column-format\"\n [type]=\"formatOf(column)\"\n [config]=\"column\"\n [data]=\"cellOf(row, column)\"\n />\n </dd>\n }\n </dl>\n @if (hasRowActions()) {\n <!-- Pie táctil: objetivos ≥44 px (guía §8.2), sin hover. -->\n <div class=\"mt-3 flex flex-wrap gap-2 border-t border-neutral/10 pt-2\">\n @if (schema().detail?.enabled) {\n <button\n app-button\n variant=\"outline\"\n type=\"button\"\n size=\"sm\"\n class=\"flex-1\"\n (click)=\"$event.stopPropagation(); goView(row)\"\n >\n <svg lucideEye size=\"15\" aria-hidden=\"true\" />\n <span>Ver</span>\n </button>\n }\n @for (action of schema().actions ?? []; track action.id) {\n <button\n app-button\n variant=\"outline\"\n type=\"button\"\n size=\"sm\"\n class=\"flex-1\"\n (click)=\"$event.stopPropagation(); runAction(action, row)\"\n >\n @let actionIcon = iconOf(action);\n @if (actionIcon) {\n <svg [lucideIcon]=\"actionIcon\" size=\"15\" aria-hidden=\"true\" />\n }\n <span>{{ action.label }}</span>\n </button>\n }\n <!-- Editar es O una vía u otra, como en la tabla: si el\n schema declara quickEdit va el atajo; si no, la\n pantalla completa. Apilados los dos sería ruido. -->\n @if (canQuickEdit()) {\n <button\n app-button\n variant=\"outline\"\n type=\"button\"\n size=\"sm\"\n class=\"flex-1\"\n (click)=\"$event.stopPropagation(); quickEditRow.set(row)\"\n >\n <svg lucideZap size=\"15\" aria-hidden=\"true\" />\n <span>Editar</span>\n </button>\n } @else if (canUpdate()) {\n <button\n app-button\n variant=\"outline\"\n type=\"button\"\n size=\"sm\"\n class=\"flex-1\"\n (click)=\"$event.stopPropagation(); goEdit(row)\"\n >\n <svg lucidePencil size=\"15\" aria-hidden=\"true\" />\n <span>Editar</span>\n </button>\n }\n @if (canRemove()) {\n <button\n app-button\n variant=\"danger\"\n type=\"button\"\n size=\"sm\"\n class=\"flex-1\"\n (click)=\"$event.stopPropagation(); removeRow(row)\"\n >\n <svg lucideTrash2 size=\"15\" aria-hidden=\"true\" />\n <span>Eliminar</span>\n </button>\n }\n </div>\n }\n </li>\n }\n </ul>\n }\n }\n }\n\n @if (quickEditRow(); as row) {\n <app-drawer-form\n [open]=\"true\"\n [schema]=\"schema()\"\n mode=\"edit\"\n [recordKey]=\"keyOf(row)\"\n [fields]=\"quickEditFields()\"\n title=\"Edición rápida\"\n size=\"sm\"\n (close)=\"quickEditRow.set(null)\"\n (saved)=\"quickEditSaved()\"\n />\n }\n </section>\n `,\n})\nexport class ListView {\n readonly schema = input.required<ResourceSchema>();\n\n /** Emitido al pulsar «Editar» cuando `schema.detail.enabled === true`. */\n readonly viewRecord = output<Row>();\n\n private readonly api = inject(ApiService);\n private readonly auth = inject(AuthService);\n private readonly router = inject(Router);\n readonly notifications = inject(NotificationService);\n private readonly actionService = inject(ActionService);\n\n readonly status = signal<Status>('loading');\n readonly rows = signal<Row[]>([]);\n readonly query = signal('');\n readonly filterValues = signal<Record<string, string>>({});\n /** Clave del registro cuyo interruptor `active` está guardando. */\n readonly savingKey = signal<string | null>(null);\n /** Fila que se está editando en el drawer (plan 6.5); `null` = cerrado. */\n readonly quickEditRow = signal<Row | null>(null);\n\n /** Evita que una respuesta lenta pise el recurso ya cambiado. */\n private loadEpoch = 0;\n\n constructor() {\n effect(() => {\n const schema = this.schema();\n this.query.set('');\n this.filterValues.set({});\n void this.load(schema);\n });\n }\n\n /** Búsqueda + filtros + orden `positionField` ascendente (cliente). */\n readonly visibleRows = computed(() => {\n const schema = this.schema();\n let rows = this.rows();\n\n const query = this.query().trim().toLowerCase();\n if (query !== '' && schema.search?.length) {\n rows = rows.filter((row) =>\n schema.search!.some((key) =>\n String(getValue(row, key) ?? '')\n .toLowerCase()\n .includes(query),\n ),\n );\n }\n\n for (const filter of schema.filters ?? []) {\n const value = this.filterValues()[filter.key] ?? '';\n if (value === '') {\n continue;\n }\n rows = rows.filter((row) => {\n const cell = getValue(row, filter.key);\n if (filter.type === 'boolean') {\n return (cell === true || cell === 'true') === (value === 'yes');\n }\n if (filter.type === 'select') {\n return String(cell ?? '') === value;\n }\n return String(cell ?? '')\n .toLowerCase()\n .includes(value.toLowerCase());\n });\n }\n\n const positionField = schema.positionField;\n if (positionField) {\n const positionOf = (row: Row): number => {\n const position = Number(getValue(row, positionField));\n return Number.isFinite(position) ? position : Number.POSITIVE_INFINITY;\n };\n rows = [...rows].sort((a, b) => positionOf(a) - positionOf(b));\n }\n\n return rows;\n });\n\n readonly currencyCode = computed(() => {\n // Moneda del singleton `site` (campo `currency`); Fase 12 podrá usar el\n // valor real cargado del backend.\n const field = getSchema('site')?.fields.find((f) => f.key === 'currency');\n return field?.type === 'text' && field.default ? field.default : 'USD';\n });\n\n private readonly permissions = computed(() => this.schema().permissions);\n readonly canCreate = computed(() => this.auth.canDoOperation(this.schema(), 'create'));\n readonly canUpdate = computed(() => this.auth.canDoOperation(this.schema(), 'update'));\n readonly canRemove = computed(() => this.auth.canDoOperation(this.schema(), 'remove'));\n\n /** El atajo existe sólo si el schema lo declara y hay permiso de editar. */\n readonly canQuickEdit = computed(\n () => this.canUpdate() && (this.schema().quickEdit?.length ?? 0) > 0,\n );\n\n /** Subconjunto de campos del drawer, en el orden declarado en el schema. */\n readonly quickEditFields = computed<readonly FieldSchema[]>(() => {\n const keys = this.schema().quickEdit ?? [];\n const byKey = new Map(this.schema().fields.map((field) => [field.key, field]));\n return keys\n .map((key) => byKey.get(key))\n .filter((field): field is FieldSchema => field !== undefined);\n });\n\n readonly hasRowActions = computed(\n () => this.canUpdate() || this.canRemove() || (this.schema().actions?.length ?? 0) > 0,\n );\n\n // ── Reordenamiento (base.md §8) ──\n\n /** Mientras persiste un movimiento: bloquea drag y botones. */\n readonly reordering = signal(false);\n\n /** Sin búsqueda ni filtros aplicados (base.md §10). */\n private readonly hasActiveFilters = computed(() => {\n if (this.query().trim() !== '') {\n return true;\n }\n return Object.values(this.filterValues()).some((value) => value !== '');\n });\n\n /**\n * Reordenable sólo si el schema lo declara, el permiso lo permite y no hay\n * filtros activos: con filtros filtrados, el índice visual no corresponde a\n * la posición real.\n */\n readonly canReorder = computed(() => {\n const schema = this.schema();\n return (\n schema.sortable === true &&\n schema.positionField !== undefined &&\n schema.permissions?.reorder !== false && // reorder está en permissions, no en policies aún\n !this.hasActiveFilters()\n );\n });\n\n readonly dropDisabled = computed(() => !this.canReorder() || this.reordering());\n\n // ── Acciones de datos ──\n\n setQuery(value: string): void {\n this.query.set(value);\n }\n\n setFilter(key: string, value: string): void {\n this.filterValues.update((values) => ({ ...values, [key]: value }));\n }\n\n clearFilters(): void {\n this.query.set('');\n this.filterValues.set({});\n }\n\n retry(): void {\n void this.load(this.schema());\n }\n\n isRowClickable(): boolean {\n return Boolean(this.schema().detail?.enabled || this.canUpdate());\n }\n\n goView(row: Row): void {\n this.viewRecord.emit(row);\n }\n\n onRowClick(row: Row): void {\n if (this.schema().detail?.enabled) {\n this.goView(row);\n } else if (this.canUpdate()) {\n this.goEdit(row);\n }\n }\n\n goCreate(): void {\n void this.router.navigate(['/', this.schema().id, 'new']);\n }\n\n goEdit(row: Row): void {\n if (this.schema().detail?.enabled === true) {\n this.viewRecord.emit(row);\n return;\n }\n void this.router.navigate(['/', this.schema().id, this.keyOf(row), 'edit']);\n }\n\n /** Cerró con guardado: se recarga la fila (la respuesta puede venir achatada). */\n quickEditSaved(): void {\n this.quickEditRow.set(null);\n void this.load(this.schema());\n }\n\n async toggleActive(row: Row, checked: boolean): Promise<void> {\n const schema = this.schema();\n const key = this.keyOf(row);\n this.savingKey.set(key);\n try {\n await firstValueFrom(this.api.update(schema.endpoint, key, { ...row, active: checked }));\n this.rows.update((rows) =>\n rows.map((candidate) =>\n this.keyOf(candidate) === key ? { ...candidate, active: checked } : candidate,\n ),\n );\n } catch (err) {\n // El cambio no se guardó: la re-renderización revierte el interruptor.\n this.notifications.error(errorMessage(err, 'No se pudo actualizar el registro.'));\n } finally {\n this.savingKey.set(null);\n }\n }\n\n /** Drop del CDK: `currentIndex` ya es el índice en la lista sin el elemento. */\n onDrop(event: CdkDragDrop<Row[]>): void {\n if (event.previousContainer !== event.container || event.previousIndex === event.currentIndex) {\n return;\n }\n const row = this.visibleRows()[event.previousIndex];\n if (row !== undefined) {\n void this.moveRow(this.keyOf(row), event.currentIndex);\n }\n }\n\n /** Botones subir/bajar (accesibilidad y móvil, base.md §13.10). */\n moveBy(key: string, delta: -1 | 1): void {\n const index = this.visibleRows().findIndex((row) => this.keyOf(row) === key);\n if (index < 0) {\n return;\n }\n void this.moveRow(key, index + delta);\n }\n\n /**\n * Un movimiento → UNA intención al backend (baseapi §2.3 y §9):\n * `{ reorder: { key, after | toStart | toEnd } }`, nunca posiciones\n * calculadas. El índice objetivo es la posición FINAL del registro (el\n * `currentIndex` del CDK ya es el índice de inserción en la lista sin el\n * elemento, idéntico al índice final; subir/bajar suma/resta 1). El\n * backend re-balancea, recalcula `position` y devuelve el listado fresco:\n * sin escrituras optimistas — si falla, no cambió nada. `gap-sorting`\n * quedó sólo para los campos con listas locales.\n */\n private async moveRow(key: string, targetIndex: number): Promise<void> {\n const schema = this.schema();\n const path = schema.endpoint.list;\n if (!path || !schema.positionField || this.reordering()) {\n return;\n }\n const rows = this.visibleRows();\n const from = rows.findIndex((row) => this.keyOf(row) === key);\n if (from < 0) {\n return;\n }\n\n // Índice final deseado, acotado a la lista SIN el elemento movido.\n const without = rows.filter((row) => this.keyOf(row) !== key);\n const target = Math.min(Math.max(targetIndex, 0), without.length);\n if (target === from) {\n return;\n }\n\n const reorder: Record<string, unknown> =\n target === 0\n ? { key, toStart: true }\n : target >= without.length\n ? { key, toEnd: true }\n : { key, after: this.keyOf(without[target - 1]) };\n\n this.reordering.set(true);\n try {\n const fresh = await firstValueFrom(this.api.request<Row[]>('PUT', path, { reorder }));\n if (Array.isArray(fresh)) {\n this.rows.set(fresh);\n }\n this.notifications.success('Orden actualizado.');\n } catch (err) {\n this.notifications.error(errorMessage(err, 'No se pudo guardar el orden.'));\n } finally {\n this.reordering.set(false);\n }\n }\n\n async removeRow(row: Row): Promise<void> {\n const schema = this.schema();\n const key = this.keyOf(row);\n const accepted = await this.notifications.confirm({\n title: `Eliminar «${this.titleOf(row)}»`,\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.rows.update((rows) => rows.filter((candidate) => this.keyOf(candidate) !== key));\n } catch (err) {\n this.notifications.error(errorMessage(err, 'No se pudo eliminar el registro.'));\n }\n }\n\n /**\n * Ejecuta una acción del schema. El comportamiento vive en el registro de\n * acciones (R3.9–R3.11): la vista sólo aporta el registro y los puertos.\n */\n async runAction(action: ResourceAction, row: Row): Promise<void> {\n await this.actionService.run(action, {\n record: row,\n resource: this.schema().id,\n api: this.api,\n router: this.router,\n toast: this.notifications,\n user: this.auth.user(),\n keyField: this.schema().keyField,\n });\n }\n\n /** Icono que el descriptor declara para la acción; sin él, el rótulo. */\n protected iconOf(action: ResourceAction): LucideIcon | undefined {\n return getAction(actionType(action))?.icon;\n }\n\n // ── Helpers de celda (uso en template) ──\n\n keyOf(row: Row): string {\n return String(row[this.schema().keyField] ?? '');\n }\n\n titleOf(row: Row): string {\n return String(row[this.schema().titleField] ?? '');\n }\n\n /**\n * Formato efectivo de la columna (R4.4 · «declarada, no implícita»).\n *\n * `column.format` sólo sobreescribe; si no, manda la tabla de\n * `column-defaults` según el `type` del campo — nunca el `?? 'text'`\n * implícito que había.\n */\n formatOf(column: ListColumn): ListColumnFormat {\n return formatoDeColumna(column, this.schema().fields);\n }\n\n /**\n * Contexto de la celda que recibe el formato (R4.3 · `extras.data`).\n *\n * Concentra aquí lo que sólo la vista sabe — permisos, moneda, guardado y\n * la escritura de la fila — para que «añadir un formato» no la toque.\n */\n cellOf(row: Row, column: ListColumn): ColumnCell {\n return {\n row,\n column,\n fields: this.schema().fields,\n title: this.titleOf(row),\n canUpdate: this.canUpdate(),\n saving: this.savingKey() === this.keyOf(row),\n currency: this.currencyCode(),\n toggle: (checked: boolean) => void this.toggleActive(row, checked),\n };\n }\n\n filterOptions(filter: ListFilter): SelectOption[] {\n if (filter.type === 'boolean') {\n return BOOLEAN_FILTER_OPTIONS;\n }\n if (filter.type === 'select') {\n return [{ value: '', label: 'Todos' }, ...(filter.options ?? [])];\n }\n return [];\n }\n\n inputValue(event: Event): string {\n return (event.target as HTMLInputElement).value;\n }\n\n selectValue(event: Event): string {\n return (event.target as HTMLSelectElement).value;\n }\n\n private async load(schema: ResourceSchema): Promise<void> {\n const epoch = ++this.loadEpoch;\n this.status.set('loading');\n try {\n const rows = await firstValueFrom(this.api.list<Row>(schema.endpoint));\n if (epoch !== this.loadEpoch) {\n return;\n }\n this.rows.set(Array.isArray(rows) ? rows : []);\n this.status.set('ready');\n } catch {\n if (epoch !== this.loadEpoch) {\n return;\n }\n this.status.set('error');\n }\n }\n}\n","import { Component, computed, inject, input, output, signal } from '@angular/core';\nimport { toObservable, toSignal } from '@angular/core/rxjs-interop';\nimport { Router } from '@angular/router';\n\nimport { ViewNavigationService } from '../../core/view-navigation/view-navigation.service';\nimport { catchError, combineLatest, map, of, switchMap } from 'rxjs';\n\nimport {\n LucideArrowLeft,\n LucideExternalLink,\n LucidePencil,\n LucideRotateCw,\n LucideTrash2,\n} from '@lucide/angular';\nimport type {\n CopyShareAction,\n DetailViewSchema,\n FieldSchema,\n ResourceAction,\n ResourceSchema,\n TriggerAction,\n} from '../../core/models/schema.model';\nimport {\n isShareAction,\n isTriggerAction,\n resolveShare,\n type ResolvedShare,\n} from '../../actions/action-registry';\nimport { ActionService } from '../../actions/action-service';\nimport { RenderHost } from '../../core/registry/render-host';\nimport { ApiService } from '../../core/services/api.service';\nimport { AuthService } from '../../core/services/auth.service';\nimport { ConfigService } from '../../core/services/config.service';\nimport { NotificationService } from '../../core/services/notification.service';\nimport { Button } from '../../shared/components/button/button';\nimport { CopyShare } from '../../shared/components/copy-share/copy-share';\nimport { EmptyState } from '../../shared/components/empty-state/empty-state';\nimport { Skeleton } from '../../shared/components/skeleton/skeleton';\nimport { getValue } from '../../shared/utils/field-path';\n\ntype Row = Record<string, unknown>;\n\n/**\n * Estado etiquetado del `get` del registro.\n *\n * `pending` distingue «todavía viaja» de «terminó sin datos» (sin\n * `endpoint.get` contratado o sin key): antes ambos eran `null` y el\n * detalle pintaba la fila parcial de la lista como si fuera todo, con los\n * campos pesados en `—` que después mutaban solos (cargar ≠ vacío,\n * doc/ux-ui-mobile.md §estados de UI).\n */\ntype FetchState =\n | { phase: 'pending' }\n | { phase: 'done'; data: Row | null }\n /** D4: el error ORIGINAL se retiene para diagnóstico (identity intacta). */\n | { phase: 'error'; error: unknown };\n\n/**\n * Campo ya resuelto contra el registro, **sin formatear**.\n *\n * Antes de R4.2 este objeto traía `value` + `kind` + `badgeKind`, que era\n * como `detail-view` derivaba a mano qué pintar (`image | badge | text`).\n * Hoy sólo aporta agrupación —clave, etiqueta, campo y valor crudo— y el\n * pintado lo decide el registro vía `<app-render-host mode=\"display\">`.\n */\ninterface FieldValue {\n key: string;\n label: string;\n /** Campo del schema (o un `text` sintético si la clave no existe). */\n field: FieldSchema;\n /** Valor crudo del registro. */\n raw: unknown;\n}\n\ninterface DetailSection {\n id: string;\n label: string;\n fields: FieldValue[];\n}\n\n/**\n * Vista de detalle genérica: muestra los campos de un registro en modo lectura.\n * Incluye cabecera estilizada, timeline interactivo para listas anidadas\n * (como history) y barra de acciones.\n *\n * **R4.2 · F3 (`refactormotor.md`):** dejó de derivar `kind` a mano y de\n * tener plantilla propia de campo. Agrupa (`sections()`, `resolveFieldValue()`,\n * el `<dt>`/`<dd>`, el bloque de historial y `hasLargeContent()`) y el valor\n * lo pinta el registro con `mode=\"display\"` — la misma tabla por tipo que\n * cualquier otra superficie. Lo único que queda por fuera son las secciones\n * que no son campos: cabecera, `copy-share` y acciones.\n */\n@Component({\n selector: 'app-detail-view',\n imports: [\n Button,\n CopyShare,\n EmptyState,\n LucideArrowLeft,\n LucideExternalLink,\n LucidePencil,\n LucideRotateCw,\n LucideTrash2,\n RenderHost,\n Skeleton,\n ],\n template: `\n <div class=\"space-y-6\">\n <!-- ── Cabecera Principal ── -->\n <header\n class=\"flex flex-wrap items-start justify-between gap-4 rounded-2xl border border-neutral/15 bg-surface p-6 shadow-xs\"\n >\n <div class=\"space-y-2\">\n <div class=\"flex flex-wrap items-center gap-2.5\">\n <h1 class=\"font-display text-2xl font-bold tracking-tight text-neutral-light\">\n {{ title() }}\n </h1>\n @for (campo of headerFields(); track campo.key) {\n @if (!vacio(campo.raw)) {\n <!-- La cabecera siempre mostró los headerFields como chips:\n el matiz viaja al host, no un @switch por tipo acá. -->\n <app-render-host\n mode=\"display\"\n [chip]=\"true\"\n [type]=\"campo.field.type\"\n [config]=\"campo.field\"\n [value]=\"campo.raw\"\n />\n }\n }\n </div>\n <p class=\"text-sm text-neutral\">\n {{ schema().label }} · Identificador:\n <span class=\"font-mono font-medium text-neutral-light\">{{ recordKey() }}</span>\n </p>\n </div>\n\n <div class=\"flex flex-wrap items-center gap-2.5\">\n <button app-button variant=\"outline\" type=\"button\" (click)=\"back.emit()\">\n <svg lucideArrowLeft size=\"16\" />\n Volver\n </button>\n @if (detail().allowEdit !== false && status() !== 'error') {\n <button app-button variant=\"primary\" type=\"button\" (click)=\"edit.emit(record())\">\n <svg lucidePencil size=\"16\" />\n Editar\n </button>\n }\n @if (detail().allowDelete !== false && status() !== 'error') {\n <button app-button variant=\"danger\" type=\"button\" (click)=\"delete.emit(record())\">\n <svg lucideTrash2 size=\"16\" />\n Eliminar\n </button>\n }\n </div>\n </header>\n\n <!-- ── Acciones Destacadas del Registro (si existen) ── -->\n @if (copyShareActions().length > 0 || triggerActions().length > 0) {\n <section\n class=\"rounded-2xl border border-primary/25 bg-gradient-to-r from-primary/10 via-primary/5 to-transparent p-5 shadow-xs\"\n >\n <div class=\"mb-3 flex items-center gap-2\">\n <svg lucideExternalLink size=\"16\" class=\"text-primary\" />\n <h2 class=\"text-sm font-semibold text-primary\">Acciones del Registro</h2>\n </div>\n <div class=\"flex flex-col gap-3 sm:flex-row sm:flex-wrap\">\n @for (action of copyShareActions(); track action.id) {\n <div class=\"flex-1 min-w-[280px]\">\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 @for (action of triggerActions(); track action.id) {\n <button app-button variant=\"outline\" type=\"button\" (click)=\"runTrigger(action)\">\n {{ action.label }}\n </button>\n }\n </div>\n </section>\n }\n\n <!-- ── Secciones de Datos ── -->\n @switch (status()) {\n @case ('loading') {\n <!-- Skeleton con la forma de las tarjetas de sección: reserva el\n espacio (sin layout jump) y distingue «cargando» de «vacío».\n La cabecera y las acciones de arriba sí se pintan ya: son la\n fila de la lista, datos reales que no están en disputa. -->\n <div aria-busy=\"true\" class=\"grid grid-cols-1 gap-6 lg:grid-cols-2\">\n @for (placeholder of [0, 1]; track placeholder) {\n <section\n class=\"rounded-2xl border border-neutral/15 bg-surface shadow-xs overflow-hidden\"\n >\n <header class=\"border-b border-neutral/10 bg-neutral/5 px-5 py-3.5\">\n <app-skeleton [lines]=\"1\" [height]=\"14\" width=\"30%\" />\n </header>\n <div class=\"p-5\">\n <app-skeleton [lines]=\"6\" [height]=\"44\" />\n </div>\n </section>\n }\n </div>\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()\">\n <svg lucideRotateCw size=\"16\" />\n Reintentar\n </button>\n <button app-button variant=\"outline\" type=\"button\" (click)=\"volverDeError()\">\n <svg lucideArrowLeft size=\"16\" />\n Volver\n </button>\n </app-empty-state>\n }\n @case ('ready') {\n <div class=\"grid grid-cols-1 gap-6 lg:grid-cols-2\">\n @for (section of sections(); track section.id) {\n <section\n class=\"rounded-2xl border border-neutral/15 bg-surface shadow-xs overflow-hidden flex flex-col\"\n [class.lg:col-span-2]=\"hasLargeContent(section)\"\n >\n <header class=\"border-b border-neutral/10 bg-neutral/5 px-5 py-3.5\">\n <h2 class=\"text-sm font-semibold tracking-wide text-neutral-light uppercase\">\n {{ section.label }}\n </h2>\n </header>\n\n <div class=\"p-5 flex-1 space-y-4\">\n <!-- Grid para campos regulares -->\n @if (regularFields(section).length > 0) {\n <dl class=\"grid grid-cols-1 gap-x-6 gap-y-4 sm:grid-cols-2\">\n @for (campo of regularFields(section); track campo.key) {\n <div class=\"space-y-1\">\n <dt class=\"text-xs font-medium text-neutral\">{{ campo.label }}</dt>\n <dd class=\"text-sm font-medium text-neutral-light break-words\">\n <app-render-host\n mode=\"display\"\n [type]=\"campo.field.type\"\n [config]=\"campo.field\"\n [value]=\"campo.raw\"\n />\n </dd>\n </div>\n }\n </dl>\n }\n\n <!-- Renderizado de listas anidadas (Timeline de eventos / items) -->\n @for (campo of listFields(section); track campo.key) {\n <div class=\"pt-2\">\n <h3 class=\"mb-3 text-xs font-semibold text-neutral uppercase\">\n {{ campo.label }}\n </h3>\n <app-render-host\n mode=\"display\"\n [type]=\"campo.field.type\"\n [config]=\"campo.field\"\n [value]=\"campo.raw\"\n />\n </div>\n }\n </div>\n </section>\n }\n </div>\n }\n }\n </div>\n `,\n})\nexport class DetailView {\n private readonly api = inject(ApiService, { optional: true });\n private readonly actionService = inject(ActionService);\n private readonly auth = inject(AuthService);\n // R8.8: el trigger (Regenerar PIN) confirma vía toast y dispara la\n // recarga del detalle — el mismo puerto que ya usa list-view.\n private readonly router = inject(Router);\n private readonly nav = inject(ViewNavigationService);\n private readonly notifications = inject(NotificationService);\n private readonly configService = inject(ConfigService);\n\n constructor() {\n // Este detalle presenta `<app-copy-share>` sin pasar por `ActionService`:\n // si el load del bootstrap falló, el mensaje resolvería a «sólo la\n // URL». Reintentamos acá —con la sesión ya iniciada—; `globalConfig` es\n // signal, así que `shareOf` se recalcula cuando llega.\n void this.configService.ensureLoaded();\n }\n\n readonly schema = input.required<ResourceSchema>();\n readonly record = input.required<Row>();\n\n readonly edit = output<Row>();\n readonly back = output<void>();\n readonly delete = output<Row>();\n\n /**\n * R8.8: contador que fuerza la relectura del registro. Una acción de\n * dominio muta la fila en el back (el PIN regenerado vive en la hoja);\n * sin este tick, `toObservable(this.record)` no emite (la referencia no\n * cambió) y el detalle mostraría el PIN viejo.\n */\n private readonly refreshTick = signal(0);\n\n /**\n * Obtiene el registro completo desde el endpoint get si existe.\n * Esto asegura que campos pesados o no proyectados en la lista (como fotos, notas o historial)\n * se carguen y muestren completos. El resultado va etiquetado (`FetchState`)\n * para que la vista sepa distinguir «cargando» de «sin datos» de «error».\n */\n private readonly fetched = toSignal(\n combineLatest([toObservable(this.record), toObservable(this.refreshTick)]).pipe(\n switchMap(([rec]) => {\n const schema = this.schema();\n const key = rec ? String(rec[schema.keyField] ?? '') : '';\n const getEndpoint = schema.endpoint?.get;\n if (!key || !getEndpoint || typeof this.api?.get !== 'function') {\n // Sin get contratado la fila de la lista ES todo lo que hay.\n return of<FetchState>({ phase: 'done', data: null });\n }\n return this.api.get<Row>(schema.endpoint, key).pipe(\n map((data): FetchState => ({ phase: 'done', data })),\n catchError((error) => of<FetchState>({ phase: 'error', error })),\n );\n }),\n ),\n { initialValue: { phase: 'pending' } as FetchState },\n );\n\n /**\n * Estados universales del detalle — el mismo trío que `list-view` y\n * `form-view`. El skeleton reserva el espacio de las secciones mientras\n * viaja el `get` (Apps Script tarda ~1s): sin él, el operador veía una\n * pantalla «completa» con campos vacíos que después aparecían solos.\n */\n protected readonly status = computed<'loading' | 'ready' | 'error'>(() => {\n const fetched = this.fetched();\n if (fetched.phase === 'pending') return 'loading';\n if (fetched.phase === 'error') return 'error';\n return 'ready';\n });\n\n /** Reintenta el `get` del registro desde el estado de error. */\n protected retry(): void {\n this.refreshTick.update((tick) => tick + 1);\n }\n\n /**\n * D4 — el error original con IDENTIDAD intacta (no un string mapeado):\n * el mensaje mostrado es display-only, el objeto crudo queda aquí para\n * diagnóstico. `undefined` cuando no hay fallo de carga.\n */\n errorDeCarga(): unknown {\n const fetched = this.fetched();\n return fetched.phase === 'error' ? fetched.error : undefined;\n }\n\n /**\n * D4 — «way back» desde el estado de error: emite el `back` (el host\n * embebido sigue gestionando su propia vuelta) Y navega por el servicio\n * declarado — en la ruta directa (`/:id/:key`) nadie escucha el output,\n * y un estado de error sin salida es una pantalla muerta.\n */\n protected volverDeError(): void {\n this.back.emit();\n void this.nav.back();\n }\n\n protected readonly effectiveRecord = computed<Row>(() => {\n const initial = this.record();\n const fetched = this.fetched();\n if (\n fetched.phase === 'done' &&\n fetched.data &&\n typeof fetched.data === 'object' &&\n Object.keys(fetched.data).length > 0\n ) {\n return { ...initial, ...fetched.data };\n }\n return initial;\n });\n\n protected readonly detail = computed<DetailViewSchema>(\n () => this.schema().detail ?? { enabled: true },\n );\n\n protected readonly title = computed(() => {\n const rec = this.effectiveRecord();\n const schema = this.schema();\n const raw = rec[schema.titleField];\n return raw !== undefined && raw !== null && raw !== '' ? String(raw) : schema.label;\n });\n\n protected readonly recordKey = computed(() => {\n const rec = this.effectiveRecord();\n const schema = this.schema();\n return String(rec[schema.keyField] ?? '—');\n });\n\n protected readonly headerFields = computed<FieldValue[]>(() => {\n const headerKeys = this.detail().headerFields ?? [];\n return headerKeys\n .map((key) => this.resolveFieldValue(key))\n .filter((f): f is FieldValue => f !== null);\n });\n\n protected readonly sections = computed<DetailSection[]>(() => {\n const schema = this.schema();\n const detailConfig = this.detail();\n\n if (detailConfig.sections && detailConfig.sections.length > 0) {\n return detailConfig.sections\n .map((sec) => ({\n id: sec.id,\n label: sec.label,\n fields: sec.fields\n .map((key) => this.resolveFieldValue(key))\n .filter((f): f is FieldValue => f !== null),\n }))\n .filter((sec) => sec.fields.length > 0);\n }\n\n const layoutSections = schema.layout?.sections ?? [];\n const sectionMap = new Map<string, DetailSection>();\n\n for (const layoutSection of layoutSections) {\n sectionMap.set(layoutSection.id, {\n id: layoutSection.id,\n label: layoutSection.label,\n fields: [],\n });\n }\n\n const unsectioned: DetailSection = { id: '_unsectioned', label: schema.label, fields: [] };\n\n for (const field of schema.fields) {\n const value = this.resolveFieldValue(field.key);\n if (value === null) continue; // R7.5: campo oculto por políticas\n if (field.section && sectionMap.has(field.section)) {\n sectionMap.get(field.section)!.fields.push(value);\n } else {\n unsectioned.fields.push(value);\n }\n }\n\n const result: DetailSection[] = [];\n if (unsectioned.fields.length > 0) result.push(unsectioned);\n for (const sec of sectionMap.values()) {\n if (sec.fields.length > 0) result.push(sec);\n }\n\n return result;\n });\n\n protected readonly copyShareActions = computed<CopyShareAction[]>(() => {\n const actions: ResourceAction[] = this.schema().actions ?? this.detail().actions ?? [];\n return actions.filter(isShareAction);\n });\n\n /** R8.8: acciones de dominio del back (trigger) visibles en el detalle. */\n protected readonly triggerActions = computed<TriggerAction[]>(() => {\n const actions: ResourceAction[] = this.schema().actions ?? this.detail().actions ?? [];\n return actions.filter(isTriggerAction);\n });\n\n /**\n * R8.8: dispara un `trigger` y, si tocó datos (`reload`), fuerza la\n * relectura del registro. La confirmación (si el descriptor la pide) la\n * resuelve `actionService.run` — este método no decide por tipo.\n */\n protected async runTrigger(action: TriggerAction): Promise<void> {\n if (!this.api) return; // sin API no hay ruta que disparar\n const result = await this.actionService.run(action, {\n record: this.effectiveRecord(),\n resource: this.schema().id,\n api: this.api,\n router: this.router,\n toast: this.notifications,\n user: this.auth.user(),\n keyField: this.schema().keyField,\n });\n if (result.ok && result.reload) {\n this.refreshTick.update((tick) => tick + 1);\n }\n }\n\n /**\n * Agrupación, no formato: devuelve el campo y el valor crudo. El pintado\n * es cosa del registro (R4.2).\n *\n * R7.5: oculta campos según `field.access.read` (sólo UX; el back\n * vuelve a filtrar la respuesta).\n */\n private resolveFieldValue(key: string): FieldValue | null {\n const schema = this.schema();\n const rec = this.effectiveRecord();\n const field = schema.fields.find((f) => f.key === key);\n const raw = getValue(rec, key);\n\n // Una clave que ningún schema declara no tiene descriptor: se resuelve\n // como texto crudo, que era exactamente lo que hacía el `default` del\n // `formatValue` viejo.\n const declarado: FieldSchema = field ?? { key, label: key.replace(/_/g, ' '), type: 'text' };\n\n // R7.5: ocultar si el usuario no tiene permiso de lectura\n if (!this.auth.canReadField(declarado)) {\n return null;\n }\n\n return {\n key,\n label: field?.label ?? declarado.label,\n field: declarado,\n raw,\n };\n }\n\n /**\n * Valor vacío con el criterio de siempre (`null`, `undefined`, `''`): ni\n * el chip de la cabecera ni el `—` del cuerpo cambian de criterio.\n */\n protected vacio(valor: unknown): boolean {\n return valor === null || valor === undefined || valor === '';\n }\n\n /** Un historial sólo es historial si hay valor que recorrer. */\n private esHistorial(campo: FieldValue): boolean {\n return campo.field.type === 'list' && !this.vacio(campo.raw);\n }\n\n protected regularFields(section: DetailSection): FieldValue[] {\n return section.fields.filter((f) => !this.esHistorial(f));\n }\n\n protected listFields(section: DetailSection): FieldValue[] {\n return section.fields.filter((f) => this.esHistorial(f));\n }\n\n protected hasLargeContent(section: DetailSection): boolean {\n return section.fields.some((f) => this.esHistorial(f) || f.key === 'description');\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.actionService.vars(this.effectiveRecord()));\n }\n}\n","import { Component, computed, inject, signal } from '@angular/core';\nimport { takeUntilDestroyed, toSignal } from '@angular/core/rxjs-interop';\nimport { ActivatedRoute, Router } from '@angular/router';\nimport { distinctUntilChanged, map, of } from 'rxjs';\n\nimport type { ResourceSchema, SelectionBinding } from '../../core/models/schema.model';\nimport { AuthService } from '../../core/services/auth.service';\nimport type { SelectionEmitter } from '../../views/composer/view-context';\nimport { getSchema } from '../../core/registry/schema-catalog';\nimport { EmptyState } from '../../shared/components/empty-state/empty-state';\nimport { Dashboard } from '../dashboard/dashboard';\nimport { DetailView } from '../detail-view/detail-view';\nimport { FormView } from '../form-view/form-view';\nimport { ListView } from '../list-view/list-view';\n\ntype View = 'list' | 'detail';\n\n/**\n * Página del recurso: resuelve el `id` de la ruta contra el registry y\n * despacha según `kind` — colección → list view, singleton → form view,\n * dashboard → panel de sólo lectura — aplicando `permissions` (estado\n * «sin permisos»).\n *\n * Cuando `schema.detail.enabled === true`, intercepta el click de fila del\n * `ListView` y muestra el `DetailView` en lugar de navegar al formulario.\n * «Editar» desde el `DetailView` sí navega al formulario de edición.\n */\n@Component({\n selector: 'app-resource-page',\n imports: [Dashboard, DetailView, EmptyState, FormView, ListView],\n template: `\n @if (schema(); as schema) {\n @if (hasNoAccess(schema)) {\n <app-empty-state\n title=\"Sin permisos\"\n message=\"No tiene permisos para gestionar este recurso.\"\n />\n } @else {\n @switch (schema.kind) {\n @case ('collection') {\n @switch (currentView()) {\n @case ('detail') {\n @if (selectedRecord(); as rec) {\n <app-detail-view\n [schema]=\"schema\"\n [record]=\"rec\"\n (back)=\"switchToList()\"\n (edit)=\"goEdit($event)\"\n (delete)=\"switchToList()\"\n />\n }\n }\n @default {\n <header class=\"mb-6\">\n <h1 class=\"font-display text-2xl font-semibold\">\n {{ schema.labelPlural }}\n </h1>\n <p class=\"text-sm text-neutral\">Colección · {{ schema.fields.length }} campos</p>\n </header>\n <app-list-view [schema]=\"schema\" (viewRecord)=\"switchToDetail($event)\" />\n }\n }\n }\n @case ('dashboard') {\n <app-dashboard [schema]=\"schema\" />\n }\n @default {\n <!-- singleton: formulario directo -->\n <app-form-view />\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})\nexport class ResourcePage {\n private readonly route = inject(ActivatedRoute);\n private readonly router = inject(Router);\n private readonly auth = inject(AuthService);\n // paramMap emite síncrono (ConvertToParamMap) → requireSync evita `| undefined`.\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\n /** Vista activa: 'list' (por defecto) o 'detail' (al hacer clic en una fila con detail.enabled). */\n readonly currentView = signal<View>('list');\n /** Registro seleccionado para la vista de detalle. */\n readonly selectedRecord = signal<Record<string, unknown> | null>(null);\n\n constructor() {\n // La ficha es ENLAZABLE: `/:id/:key` (p. ej. el «Ver Ficha» del\n // dashboard) entra directo al detalle. Al navegar — desde una fila o con\n // el botón atrás — la URL es la única fuente de verdad de la vista.\n this.route.paramMap\n .pipe(\n map((params) => params.get('key')),\n distinctUntilChanged(),\n takeUntilDestroyed(),\n )\n .subscribe((key) => this.openFromRoute(key));\n this.route.data\n ?.pipe(takeUntilDestroyed())\n .subscribe(() => this.openFromRoute(this.params().get('key')));\n }\n\n private openFromRoute(key: string | null): void {\n const schema = this.schema();\n if (!schema || schema.kind !== 'collection') return;\n\n const requestedView = this.routeData()['viewType'];\n if ((requestedView === 'detail' || key) && key && schema.detail?.enabled) {\n // Si el registro ya está cargado (clic en una fila) se conserva tal\n // cual; si llegamos por URL sólo hay clave, y DetailView trae el resto.\n const current = this.selectedRecord();\n if (!current || String(current[schema.keyField]) !== key) {\n this.selectedRecord.set({ [schema.keyField]: key });\n }\n this.currentView.set('detail');\n } else if (this.currentView() === 'detail') {\n this.selectedRecord.set(null);\n this.currentView.set('list');\n }\n }\n\n /**\n * Clic de fila. R9.9.4 · decision table: si el schema declara `select`,\n * el contrato declarado REEMPLAZA el comportamiento local (`route` ⇒\n * emisión al dispatcher, `inline` ⇒ selección local sin URL, `none` ⇒\n * no-op); sin declaración, la navegación local actual sigue intacta\n * (backward compat — R9.9 «existing routes untouched»).\n */\n switchToDetail(record: Record<string, unknown>): void {\n const schema = this.schema();\n const data = this.routeData();\n const binding = data['select'] as SelectionBinding | undefined;\n\n if (binding !== undefined) {\n const key =\n schema && record[schema.keyField] !== undefined && record[schema.keyField] !== null\n ? String(record[schema.keyField])\n : '';\n if (binding.mode === 'route') {\n const emit = data['emit'] as SelectionEmitter | undefined;\n emit?.({\n resource: schema?.id ?? String(data['resourceId'] ?? ''),\n key,\n action: binding.on,\n });\n return;\n }\n if (binding.mode === 'none') return;\n // inline ⇒ selección local (ficha embebida), sin tocar la URL\n this.selectedRecord.set(record);\n this.currentView.set('detail');\n return;\n }\n\n this.selectedRecord.set(record);\n this.currentView.set('detail');\n const k = schema ? record[schema.keyField] : undefined;\n if (schema && k !== undefined && k !== null && String(k) !== '') {\n void this.router.navigate(['/', schema.id, String(k)]);\n }\n }\n\n switchToList(): void {\n this.selectedRecord.set(null);\n this.currentView.set('list');\n const schema = this.schema();\n if (schema && this.params().get('key')) {\n void this.router.navigate(['/', schema.id]);\n }\n }\n\n goEdit(record: Record<string, unknown>): void {\n const schema = this.schema();\n if (!schema) return;\n const key = record[schema.keyField] as string | undefined;\n if (key) {\n void this.router.navigate(['/', schema.id, key, 'edit']);\n }\n }\n\n /**\n * Colección sin ninguna operación posible u singleton sin `update`: no hay\n * nada que mostrar con sentido (base.md §10 «Sin permisos»).\n *\n * R7.5: usa policies cuando existan, fallback a permissions.\n *\n * Un dashboard nunca entra aquí: es un panel de sólo lectura, `ResourcePermissions`\n * no tiene `read` y el acceso real a los datos lo decide el endpoint\n * (A4: el cliente es interfaz, no autoridad).\n */\n hasNoAccess(schema: ResourceSchema): boolean {\n // R7.5: intentar policies primero\n if (schema.policies?.access) {\n // Con policies, ocultar si el usuario no puede hacer NINGUNA operación\n // relevante para el tipo de recurso\n if (schema.kind === 'singleton') {\n return !this.auth.canDoOperation(schema, 'update');\n }\n // collection: ocultar si no puede hacer list, create, update ni remove\n return (\n !this.auth.canDoOperation(schema, 'list') &&\n !this.auth.canDoOperation(schema, 'create') &&\n !this.auth.canDoOperation(schema, 'update') &&\n !this.auth.canDoOperation(schema, 'remove')\n );\n }\n\n // Fallback a permissions (legacy)\n const permissions = schema.permissions;\n if (!permissions || schema.kind === 'dashboard') {\n return false;\n }\n if (schema.kind === 'singleton') {\n const hasReadEndpoint = !!schema.endpoint?.get;\n return !hasReadEndpoint && permissions.update === false;\n }\n const hasReadEndpoint = !!schema.endpoint?.list || !!schema.endpoint?.get;\n const canWrite =\n permissions.create !== false || permissions.update !== false || permissions.remove !== false;\n return !hasReadEndpoint && !canWrite;\n }\n}\n","import {\n ChangeDetectionStrategy,\n Component,\n EnvironmentInjector,\n Injector,\n ViewContainerRef,\n effect,\n inject,\n input,\n signal,\n viewChild,\n} from '@angular/core';\nimport { ActivatedRoute } from '@angular/router';\n\nimport type { ResourceSchema, ViewChildNode } from '../../core/models/schema.model';\nimport { MAX_DEPTH } from '../../core/registry/composition-validator';\nimport { get } from '../../core/registry/render-registry';\nimport { ViewNavigationService } from '../../core/view-navigation/view-navigation.service';\nimport { EmptyState } from '../../shared/components/empty-state/empty-state';\nimport { createViewContext, instanceIdFor, syntheticRoute, virtualSchemaFor } from './view-context';\nimport type { ViewContextData } from './view-context';\n\n/**\n * R9.9 · slice 3 — Slot de instancia embebida.\n *\n * Monta UN hijo de composición resolviéndolo SOLO por `(kind:'view', type)`\n * en el registro (`get`, nunca `resolve` ⇒ el fallback silencioso del\n * registro es inalcanzable, D2) y entregándole un canal ViewContext\n * sintético (D1). Cada slot es una isla: carga/error/estado se aíslan por\n * `instanceId`, y el rechazo de `load()` queda retenido en `error()` para\n * diagnóstico sin tumbar a los hermanos.\n */\n@Component({\n selector: 'app-view-slot',\n standalone: true,\n imports: [EmptyState],\n changeDetection: ChangeDetectionStrategy.OnPush,\n template: `\n @if (status() === 'loading') {\n <div role=\"status\" class=\"p-3 text-sm text-neutral\">Cargando…</div>\n } @else if (status() === 'error') {\n <app-empty-state title=\"No se pudo mostrar esta vista\" [message]=\"mensaje()\" />\n } @else {\n <div #host></div>\n }\n `,\n})\nexport class ViewSlot {\n /** Schema del recurso padre (el host le pasa el schema real). */\n readonly schema = input.required<ResourceSchema>();\n /** Nodo declarado (inline con `type` o por `ref`). */\n readonly node = input.required<ViewChildNode>();\n /** instanceId del contenedor padre (raíz ⇒ nombre de la uiView de ruta). */\n readonly parentInstanceId = input.required<string>();\n /** Profundidad del propio slot (hijo de raíz ⇒ 1). */\n readonly depth = input(0);\n\n /** Estado de ESTE slot — aislado por instancia, jamás compartido con hermanos. */\n readonly status = signal<'loading' | 'ready' | 'error'>('loading');\n /** Rechazo original retenido para diagnóstico (el display usa `mensaje()`). */\n readonly error = signal<unknown>(null);\n /** Payload entregado al hijo (público para tests/diagnóstico). */\n readonly payload = signal<ViewContextData | null>(null);\n\n private readonly nav = inject(ViewNavigationService);\n private readonly envInjector = inject(EnvironmentInjector);\n private readonly ancla = viewChild('host', { read: ViewContainerRef });\n\n private epoch = 0;\n private pendiente: { tipo: unknown; injector: Injector } | null = null;\n\n constructor() {\n // Fase 1: montaje/reactivo — cualquier cambio de inputs re-monta el slot.\n effect(() => this.monar());\n // Fase 2: cuando el ancla #host existe (status ready), crear el hijo.\n effect(() => {\n if (this.status() !== 'ready') return;\n const pend = this.pendiente;\n let ancla: ViewContainerRef | undefined;\n try {\n ancla = this.ancla();\n } catch {\n ancla = undefined; // ancla aún no materializada; el effect re-corre al aparecer\n }\n if (!pend || !ancla) return;\n this.pendiente = null;\n ancla.clear();\n ancla.createComponent(pend.tipo as never, { injector: pend.injector });\n });\n }\n\n private async monar(): Promise<void> {\n const esquema = this.schema();\n const nodo = this.node();\n if (!esquema || !nodo) return;\n const turno = ++this.epoch;\n this.pendiente = null;\n\n if (this.depth() > MAX_DEPTH) {\n this.error.set(\n new Error(\n `Límite de profundidad de composición excedido (${MAX_DEPTH}) en ${esquema.id}.${nodo.id}`,\n ),\n );\n this.status.set('error');\n return;\n }\n\n // Referencia por uiView del mismo schema o nodo inline.\n const resuelto = 'ref' in nodo ? esquema.uiViews?.[nodo.ref] : nodo;\n if (resuelto === undefined) {\n const ref = 'ref' in nodo ? nodo.ref : nodo.id;\n this.error.set(new Error(`Referencia '${ref}' inexistente en ${esquema.id}`));\n this.status.set('error');\n return;\n }\n\n const descriptor = get('view', resuelto.type);\n if (!descriptor) {\n // Sin fallback: error legible, nunca pantalla de reserva (D2).\n this.error.set(\n new Error(`No existe descriptor kind:view type '${resuelto.type}' en ${esquema.id}`),\n );\n this.status.set('error');\n return;\n }\n\n const instanceId = instanceIdFor(this.parentInstanceId(), nodo.id);\n const select = 'ref' in nodo ? resuelto.select : nodo.select;\n const virtual = virtualSchemaFor(esquema, instanceId, resuelto);\n // D4: el emisor va a `dispatch` (target resuelto, segmentos, guards);\n // sólo un binding `mode:'route'` lleva `emit`, pero el payload declara\n // `select` SIEMPRE para que la hoja aplique la decision table.\n const emit =\n select?.mode === 'route'\n ? (event: Parameters<NonNullable<ViewContextData['emit']>>[0]): void => {\n void this.nav.dispatch(select, event);\n }\n : undefined;\n this.payload.set(\n createViewContext(\n {\n schema: virtual,\n resourceId: esquema.id,\n uiView: instanceId,\n viewType: resuelto.type,\n depth: this.depth(),\n ...(select !== undefined ? { select } : {}),\n },\n emit,\n ),\n );\n\n this.error.set(null);\n this.status.set('loading');\n const injector = Injector.create({\n providers: [{ provide: ActivatedRoute, useValue: syntheticRoute(this.payload()!) }],\n parent: this.envInjector,\n });\n\n try {\n const tipo = await descriptor.load();\n if (turno !== this.epoch) return; // inputs cambiaron: descartar\n this.pendiente = { tipo, injector };\n this.status.set('ready');\n } catch (e) {\n if (turno !== this.epoch) return;\n this.error.set(e);\n this.status.set('error');\n }\n }\n\n /** Mensaje mostrado (el error original queda en `error()`). */\n mensaje(): string {\n const e = this.error();\n return e instanceof Error ? e.message : String(e);\n }\n}\n","import { ChangeDetectionStrategy, Component, computed, inject, signal } from '@angular/core';\nimport { ActivatedRoute } from '@angular/router';\nimport { toSignal } from '@angular/core/rxjs-interop';\n\nimport type { ResourceSchema, UiViewTabs, ViewChildNode } from '../../core/models/schema.model';\nimport { validateComposition } from '../../core/registry/composition-validator';\nimport { getSchema } from '../../core/registry/schema-catalog';\nimport { EmptyState } from '../../shared/components/empty-state/empty-state';\nimport { evaluateCondition } from '../../shared/utils/condition-evaluator';\nimport { ViewSlot } from './view-slot';\n\n/**\n * R9.9 · slice 3 — Host genérico de composición (`uiView` tipo `tabs`).\n *\n * Un SOLO componente recursivo renderiza cualquier composición declarada:\n * resuelve cada hijo por `(kind:'view', type)` dentro del `ViewSlot` (D2,\n * sin `switch` de dominio y sin importar hojas), monta la validación del\n * schema ANTES del primer render (fail-loud) y maneja el tablist accesible\n * con roving tabindex (R9.9.5).\n *\n * Decisiones R9.9.5:\n * - Retención: un hijo `retain:true` visitado queda MONTADO pero oculto\n * (`hidden`); sin `retain` se destruye al cambiar de pestaña.\n * - Pestaña activa LOCAL por componente: no hay estado compartido entre\n * instancias de tabs anidadas (documentado en los docs de slice 5).\n * - `visibleWhen` se evalúa sin record (el contenedor no es un formulario):\n * contra registro vacío, TODAS las condiciones deben pasar (AND).\n * Si la pestaña activa queda oculta, la selección cae visiblemente a la\n * primera visible — nunca se pierde en silencio.\n */\n@Component({\n selector: 'app-composer-host',\n standalone: true,\n imports: [ViewSlot, EmptyState],\n changeDetection: ChangeDetectionStrategy.OnPush,\n template: `\n @if (error() !== null) {\n <app-empty-state title=\"Composición no disponible\" [message]=\"error()!\" />\n } @else if (visibles().length === 0) {\n <app-empty-state\n title=\"Sin secciones disponibles\"\n message=\"La vista no declara secciones visibles.\"\n />\n } @else {\n <div\n role=\"tablist\"\n class=\"flex flex-wrap gap-1 border-b border-neutral/20 px-1\"\n [attr.aria-label]=\"etiqueta()\"\n (keydown)=\"onKeydown($event)\"\n >\n @for (h of visibles(); track h.id) {\n <button\n type=\"button\"\n role=\"tab\"\n [id]=\"'tab-' + h.id\"\n [attr.aria-selected]=\"h.id === activo()\"\n [attr.aria-controls]=\"montado(h.id) ? 'panel-' + h.id : null\"\n [attr.tabindex]=\"h.id === activo() ? 0 : -1\"\n [class]=\"claseTab(h.id)\"\n (click)=\"seleccionar(h.id, true)\"\n >\n {{ h.label }}\n </button>\n }\n </div>\n\n @for (h of montados(); track h.id) {\n <div\n role=\"tabpanel\"\n [id]=\"'panel-' + h.id\"\n [attr.aria-labelledby]=\"'tab-' + h.id\"\n [class.hidden]=\"h.id !== activo()\"\n class=\"py-3 focus:outline-none\"\n tabindex=\"0\"\n >\n <app-view-slot\n [schema]=\"schema()!\"\n [node]=\"h\"\n [parentInstanceId]=\"uiViewName()\"\n [depth]=\"profundidad() + 1\"\n />\n </div>\n }\n }\n `,\n})\nexport class ComposerHost {\n private readonly route = inject(ActivatedRoute);\n private readonly routeData = toSignal(this.route.data, { requireSync: true });\n\n /** Schema: explícito en `data` (fixtures/ruta embebida) o vía `resourceId`. */\n readonly schema = computed<ResourceSchema | undefined>(() => {\n const data = this.routeData();\n const explicit = data['schema'];\n if (explicit !== undefined) return explicit as ResourceSchema;\n const resourceId = data['resourceId'];\n return typeof resourceId === 'string' ? getSchema(resourceId) : undefined;\n });\n\n /** instanceId de ESTE contenedor (raíz ⇒ nombre de la uiView de ruta). */\n readonly uiViewName = computed(() => {\n const v = this.routeData()['uiView'];\n return typeof v === 'string' ? v : '';\n });\n\n /** Profundidad de ESTE contenedor (raíz ⇒ 0; embebido ⇒ payload.depth). */\n readonly profundidad = computed(() => {\n const d = this.routeData()['depth'];\n return typeof d === 'number' ? d : 0;\n });\n\n /** Config `tabs` declarada para esta instancia. */\n readonly vista = computed<UiViewTabs | undefined>(() => {\n const schema = this.schema();\n const nombre = this.uiViewName();\n if (schema === undefined || nombre === '') return undefined;\n const vista = schema.uiViews?.[nombre];\n // `'config' in` descarta UiViewBase; el literal descarta calendar/kanban.\n return vista !== undefined && 'config' in vista && vista.type === 'tabs' ? vista : undefined;\n });\n\n /** Error determinista de configuración (schema/vista ausente o no-tabs). */\n private readonly errorConfig = computed<string | null>(() => {\n const data = this.routeData();\n const schema = this.schema();\n const nombre = this.uiViewName();\n if (schema === undefined) {\n return `Schema no encontrado para '${String(data['resourceId'] ?? '')}'`;\n }\n if (nombre === '') return 'Ruta de composición sin uiView declarada';\n const vista = schema.uiViews?.[nombre];\n if (vista === undefined) return `Vista '${nombre}' no declarada en ${schema.id}`;\n if (vista.type !== 'tabs') {\n return `Configuración inválida: ${schema.id}.${nombre} declara '${vista.type}', no es una vista tabs`;\n }\n return null;\n });\n\n /** Error de validación D2 (se recalcula una sola vez, en el constructor). */\n private readonly errorValidacion = signal<string | null>(null);\n\n /** Error mostrado: validación del schema primero, luego configuración. */\n readonly error = computed<string | null>(() => this.errorValidacion() ?? this.errorConfig());\n\n /** Todos los hijos declarados (orden de declaración). */\n private readonly hijos = computed<readonly ViewChildNode[]>(\n () => this.vista()?.config.children ?? [],\n );\n\n /** Hijos visibles: TODAS sus condiciones `visibleWhen` pasan sin record. */\n readonly visibles = computed<readonly ViewChildNode[]>(() =>\n this.hijos().filter(\n (h) =>\n h.visibleWhen === undefined ||\n h.visibleWhen.every((cond) => evaluateCondition(cond, {}, {})),\n ),\n );\n\n /** Selección del usuario (intención; vacío ⇒ usar `initial`/primera). */\n private readonly seleccion = signal('');\n\n /**\n * Pestaña activa EFECTIVA: derivada pura de la selección y de las\n * visibles — si la selección dejó de existir (u oculta), cae a `initial`\n * o a la primera visible de forma SINCRÓNICA y visible (R9.9.5: nunca\n * perder la vista elegida en silencio). Estado LOCAL por instancia.\n */\n readonly activo = computed<string>(() => {\n const vis = this.visibles();\n if (vis.length === 0) return '';\n const sel = this.seleccion();\n if (vis.some((h) => h.id === sel)) return sel;\n const inicial = this.vista()?.config.initial;\n return inicial !== undefined && vis.some((h) => h.id === inicial) ? inicial : vis[0].id;\n });\n\n /** Ids ya seleccionados alguna vez (sustento de la retención). */\n private readonly visitados = signal<ReadonlySet<string>>(new Set());\n\n /** Paneles en DOM: activo + retenidos visitados que siguen visibles. */\n readonly montados = computed<readonly ViewChildNode[]>(() => {\n const visibles = new Set(this.visibles().map((h) => h.id));\n const activo = this.activo();\n const visitados = this.visitados();\n return this.hijos().filter(\n (h) => visibles.has(h.id) && (h.id === activo || (h.retain === true && visitados.has(h.id))),\n );\n });\n\n constructor() {\n // D2 — validar TODO el schema antes del primer render (fail-loud).\n const schema = this.schema();\n if (schema !== undefined) {\n try {\n validateComposition(schema);\n } catch (e) {\n this.errorValidacion.set(e instanceof Error ? e.message : String(e));\n }\n }\n // La pestaña activa al montar cuenta como visitada: al cambiar de\n // pestaña, un hijo `retain:true` que estaba activo conserva su panel.\n const inicial = this.activo();\n if (inicial !== '') this.visitados.set(new Set([inicial]));\n }\n\n /** Selecciona una pestaña y, opcionalmente, mueve el foco a su botón. */\n seleccionar(id: string, conFoco: boolean): void {\n this.seleccion.set(id);\n this.visitados.update((v) => new Set(v).add(id));\n if (conFoco) {\n setTimeout(() => document.getElementById(`tab-${id}`)?.focus(), 0);\n }\n }\n\n /** Navegación por teclado del tablist: flechas con wrap, Home/End. */\n onKeydown(event: KeyboardEvent): void {\n const vis = this.visibles();\n if (vis.length === 0) return;\n const i = vis.findIndex((h) => h.id === this.activo());\n let next = -1;\n if (event.key === 'ArrowRight') next = (i + 1) % vis.length;\n else if (event.key === 'ArrowLeft') next = (i - 1 + vis.length) % vis.length;\n else if (event.key === 'Home') next = 0;\n else if (event.key === 'End') next = vis.length - 1;\n if (next < 0) return;\n event.preventDefault();\n this.seleccionar(vis[next].id, true);\n }\n\n /** El panel de este hijo está montado (⇒ existe su `aria-controls`). */\n montado(id: string): boolean {\n return this.montados().some((h) => h.id === id);\n }\n\n /** Clases del botón de pestaña: activo resaltado con token `primary`. */\n claseTab(id: string): string {\n const base =\n 'rounded-t-lg px-3 py-2 text-sm transition-colors focus-visible:outline-2 focus-visible:outline-offset-1 focus-visible:outline-primary';\n return id === this.activo()\n ? `${base} bg-primary/10 font-medium text-primary`\n : `${base} text-neutral hover:text-primary`;\n }\n\n /** Etiqueta a11y del tablist (la del recurso). */\n etiqueta(): string {\n return this.schema()?.labelPlural ?? 'Secciones';\n }\n}\n","import { Component, input, output } from '@angular/core';\n\nimport { Button } from '../button/button';\nimport { Modal } from '../modal/modal';\n\n/**\n * Opciones del diálogo. Es estructuralmente compatible con\n * `core.NotificationService['confirmation']` (mismos campos) — `shared` no\n * importa `core`, quien une ambos lados es el shell (Fase 7).\n */\nexport interface ConfirmDialogOptions {\n title: string;\n message: string;\n confirmLabel?: string;\n cancelLabel?: string;\n}\n\n@Component({\n selector: 'app-confirm-dialog',\n imports: [Modal, Button],\n template: `\n @if (options(); as opts) {\n <app-modal [open]=\"true\" [title]=\"opts.title\" (close)=\"cancelled.emit()\">\n <p class=\"text-sm text-neutral\">{{ opts.message }}</p>\n <div class=\"mt-6 flex justify-end gap-3\">\n <button app-button variant=\"outline\" type=\"button\" (click)=\"cancelled.emit()\">\n {{ opts.cancelLabel ?? 'Cancel' }}\n </button>\n <button\n app-button\n type=\"button\"\n [variant]=\"danger() ? 'danger' : 'primary'\"\n (click)=\"confirmed.emit()\"\n >\n {{ opts.confirmLabel ?? 'Confirm' }}\n </button>\n </div>\n </app-modal>\n }\n `,\n})\nexport class ConfirmDialog {\n /** `null` = oculto (viene de `NotificationService.confirmation`). */\n readonly options = input<ConfirmDialogOptions | null>(null);\n readonly danger = input(false);\n readonly confirmed = output<void>();\n readonly cancelled = output<void>();\n}\n","import { Component, input, output } from '@angular/core';\nimport { LucideCircleCheck, LucideCircleX, LucideInfo, LucideX } from '@lucide/angular';\n\nexport type ToastKind = 'success' | 'error' | 'info';\n\n/** Estructuralmente idéntico a `core`'s `Toast` — no importa `core`. */\nexport interface ToastItem {\n id: number;\n kind: ToastKind;\n message: string;\n}\n\n/**\n * Contenedor visual de toasts: fijo arriba a la derecha, región `status`\n * viva (polite) para que se anuncien los mensajes. Recibe los toasts por\n * `input()` desde quien sí tiene `NotificationService` (Fase 7).\n */\n@Component({\n selector: 'app-toast',\n imports: [LucideCircleCheck, LucideCircleX, LucideInfo, LucideX],\n host: {\n class: 'pointer-events-none fixed right-4 top-4 z-50 flex w-full max-w-sm flex-col gap-2',\n role: 'status',\n 'aria-live': 'polite',\n },\n template: `\n @for (toast of toasts(); track toast.id) {\n <div\n class=\"pointer-events-auto flex items-start gap-3 border-l-4 bg-surface-raised p-4 shadow-lg\"\n [class.border-l-success]=\"toast.kind === 'success'\"\n [class.border-l-danger]=\"toast.kind === 'error'\"\n [class.border-l-primary]=\"toast.kind === 'info'\"\n >\n @if (toast.kind === 'success') {\n <svg lucideCircleCheck size=\"18\" class=\"mt-0.5 shrink-0 text-success\" />\n } @else if (toast.kind === 'error') {\n <svg lucideCircleX size=\"18\" class=\"mt-0.5 shrink-0 text-danger\" />\n } @else {\n <svg lucideInfo size=\"18\" class=\"mt-0.5 shrink-0 text-primary\" />\n }\n <p class=\"flex-1 text-sm font-medium\">{{ toast.message }}</p>\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)=\"dismissed.emit(toast.id)\"\n >\n <svg lucideX size=\"18\" />\n </button>\n </div>\n }\n `,\n})\nexport class Toast {\n readonly toasts = input<ToastItem[]>([]);\n readonly closeLabel = input('Cerrar');\n readonly dismissed = output<number>();\n}\n","/**\n * Descripciones centralizadas del catálogo de campos del motor `@tinya-code/gas-admin-ui`.\n *\n * Este archivo es el único lugar donde se editan los textos descriptivos de\n * cada propiedad del schema. Los JSDoc de `schema.model.ts` usan estos mismos\n * textos para que el editor (VSCode) muestre tooltips al pasar el cursor\n * sobre una propiedad del schema del consumidor.\n *\n * Estructura: clave de propiedad → descripción en español.\n *\n * Uso en el consumidor (loyalty-admin, etc.):\n * import { schemaDescriptions } from '@tinya-code/gas-admin-ui';\n * schemaDescriptions['docLink']; // 'URL de documentación externa para este campo.'\n *\n * Nota: este objeto no se renderiza en la UI; solo existe para documentación\n * y tooltips del editor. Para renderizar tooltips en el formulario usa\n * `description` en cada campo del schema del consumidor.\n */\n\nexport const schemaDescriptions = {\n // ── Propiedades comunes a todo campo (FieldBase) ───────────────────────\n key: 'Nombre de la propiedad en el JSON que envía/recibe el back.',\n label: 'Rótulo visible para el usuario en el formulario y la ficha.',\n help: 'Texto corto de ayuda que se muestra debajo del campo.',\n placeholder: 'Texto sugerente que se muestra dentro del input cuando está vacío.',\n required: 'Si es obligatorio. El back también lo valida (R7.4).',\n default: 'Valor por defecto si el registro no trae este campo (ej. `default: \"none\"` en un `select`/`dropdown`).',\n readonly: 'Si el campo es solo lectura (no editable en el formulario).',\n readonlyOn: 'Bloquea el campo solo en creación, solo en edición o siempre.',\n readonlyWhen: 'Condición bajo la cual el campo es solo lectura (expresión sobre otros campos).',\n visibleWhen: 'Condición bajo la cual el campo se muestra u oculta.',\n hiddenOn: 'Oculta el campo en creación, en edición o siempre.',\n dependsOn: 'Selección dependiente (guía §1: país → ciudad). Clave del campo padre del mismo nivel.',\n validators: 'Reglas de validación (mínimo, máximo, patrón, unicidad, etc.).',\n width: 'Ancho en la grilla del formulario (1–12), igual que un widget del dashboard.',\n section: 'Id de la sección/pestaña del layout a la que pertenece (base.md §4).',\n read: 'Código de nivel de acceso requerido para leer el campo.',\n write: 'Código de nivel de acceso requerido para escribir el campo.',\n description: 'Descripción corta de para qué sirve este campo. Se muestra como tooltip al pasar el cursor sobre el rótulo.',\n docLink: 'URL de documentación externa para este campo (se abre al hacer clic en el tooltip).',\n\n // ── Opciones de select/dropdown ───────────────────────────────────────\n value: 'Valor que se envía al back cuando se elige esta opción.',\n labelOption: 'Texto visible de la opción.',\n disabled: 'Si la opción está deshabilitada (no se puede elegir).',\n\n // ── Campos específicos ─────────────────────────────────────────────────\n type: 'Tipo de campo: `text`, `color`, `number`, `currency`, `boolean`, `select`, `dropdown`, `rich-select`, `multiselect`, `url`, `email`, `phone`, `date`, `time`, `readonly-text`, `slug`, `textarea`, `relation`, `image`, `file`, `group`, `list`, `string-list`, `key-value`.',\n maxLength: 'Longitud máxima de caracteres permitida.',\n pattern: 'Expresión regular que debe cumplir el valor.',\n rows: 'Número de filas visibles del textarea.',\n minWords: 'Cantidad mínima de palabras.',\n maxWords: 'Cantidad máxima de palabras.',\n tokens: 'Marcadores insertables como chips bajo el área (plantillas de mensaje).',\n from: 'Campo del registro del que se autogenera el slug.',\n lockAfterCreate: 'Bloquea el campo una vez creado el registro (por defecto: sí).',\n min: 'Valor mínimo permitido.',\n max: 'Valor máximo permitido.',\n step: 'Incremento entre valores permitidos.',\n decimals: 'Número de decimales mostrados.',\n currencyFrom: 'Campo del registro del que se toma la moneda (ej. `currency`).',\n trueLabel: 'Texto que se muestra cuando el valor es verdadero.',\n falseLabel: 'Texto que se muestra cuando el valor es falso.',\n source: 'De dónde salen las opciones: `{ resource, view?, params?, … }` (F8 · R6.5).',\n resource: 'Recurso declarado (`resource.id`): su listado es el de las opciones.',\n view: 'Vista declarada en `resource.views` (opcional): filtra en el back.',\n params: 'Valores interpolados para la vista (`{campo}` ← contexto).',\n labelField: 'Etiqueta por defecto de las filas (si no, `titleField` del recurso).',\n valueField: 'Valor por defecto de las filas (si no, `keyField` del recurso).',\n secondaryField: 'Segundo dato a mostrar junto a la etiqueta (ej. \"Juan Pérez · DNI 123\").',\n onlyActive: 'Cargar solo registros activos (lo exige además la FK en el back).',\n accept: 'Tipos aceptados (ej. `image/*`, `image/png,image/jpeg` o `pdf,docx`).',\n maxSizeMB: 'Tamaño máximo del archivo en megabytes.',\n aspectRatio: 'Relación de aspecto de la vista previa (ej. `16/9`).',\n previewSize: 'Tamaño de la vista previa.',\n formatCurrency: 'Campo del registro del que se toma la moneda (ej. `currency`).',\n display: 'Presentación del grupo: `card`, `collapsible` o `inline`.',\n fields: 'Campos de cada item (cualquier tipo, incluso `group` o `list`).',\n minItems: 'Cantidad mínima de items.',\n maxItems: 'Cantidad máxima de items.',\n sortable: 'Si se puede reordenar (por defecto: sí).',\n positionField: 'Campo donde se guarda el orden dentro de cada item (`position`).',\n addLabel: 'Etiqueta del botón para agregar un item nuevo.',\n emptyText: 'Texto que se muestra cuando no hay items.',\n itemDisplay: 'Cómo se muestra cada item: `card`, `row` o `accordion`.',\n itemFields: 'Campos de cada item (cualquier tipo, incluso `group` o `list`).',\n itemTitle: 'Campo del item usado como título cuando está colapsado.',\n primaryFirst: 'El primer item tiene significado especial (ej. imagen principal).',\n itemType: 'Tipo permitido de cada item (base.md §9: solo `text`, `url` o `email`).',\n sort: 'Si la lista se ordena alfabéticamente o por posición.',\n filter: 'Si el listado permite filtrado por texto.',\n search: 'Campos buscables en el listado.',\n filters: 'Filtros declarados para el listado.',\n listColumns: 'Columnas que se muestran en el listado.',\n searchable: 'Si el campo es buscable.',\n filterable: 'Si el campo es filtrable.',\n sortableItems: 'Si los items de la lista se pueden reordenar.',\n groupBy: 'Campo por el que agrupar los items.',\n cardWidth: 'Ancho de cada tarjeta de opción en la grilla (1–12). Por defecto `4` (3 tarjetas por fila en escritorio).',\n cardCols: 'Cuántas tarjetas mostrar por fila en el punto de ruptura `lg`. Sobrescribe el cálculo automático basado en `cardWidth`.',\n hintMetric: 'Texto secundario bajo la cifra (estático, va en el schema).',\n unitChart: 'Sufijo de las cifras del eje («unidades», «S/»).',\n segments: 'Segmentos de la barra de distribución porcentual.',\n actions: 'Acciones de navegación por fila.',\n labelAction: 'Etiqueta de la acción.',\n icon: 'Icono de la acción.',\n navigateTo: 'Ruta de destino de la acción.',\n variant: 'Variante visual: `primary`, `secondary` u `outline`.',\n\n // ── Widgets de dashboard ──────────────────────────────────────────────\n widgets: 'Widgets del panel. Se declara en `ResourceSchema.widgets` — no en `fields`, porque un KPI no es un campo.',\n mobileWidth: 'Ancho en la grilla en mobile (1–12). Por defecto `12` (ancho completo, apilado).',\n sourceWidget: 'De dónde saca sus datos: `DataSource` o `{ kind: \"payload\" }`.',\n hintWidget: 'Texto secundario bajo la cifra (estático, va en el schema).',\n unitWidget: 'Sufijo de las cifras del eje.',\n} as const;\n\nexport type SchemaDescriptionKey = keyof typeof schemaDescriptions;\n"],"mappings":"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"}