@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-image-7UhJ7nqI.mjs","names":[],"sources":["../../../projects/gas-admin-ui/src/lib/fields/image/image.ts"],"sourcesContent":["import { HttpEventType } from '@angular/common/http';\nimport {\n Component,\n computed,\n DestroyRef,\n ElementRef,\n inject,\n input,\n model,\n output,\n signal,\n viewChild,\n} from '@angular/core';\nimport type { FormValueControl } from '@angular/forms/signals';\n\nimport type { ImageField } from '../../core/models/schema.model';\nimport { ApiError } from '../../core/models/api.model';\nimport { NotificationService } from '../../core/services/notification.service';\nimport { Button } from '../../shared/components/button/button';\nimport { UploadService } from '../../core/services/upload.service';\n\n/**\n * Carga de imagen (base.md §7): el valor es SOLO la URL pública.\n *\n * Control propio `FormValueControl<string>`: el nodo llega con `[formField]`\n * desde el despachador y el `id` lo pone el host sobre este elemento (mismo\n * patrón que `multiselect`), para que el `<label for>` del campo exista.\n *\n * Estados: vacío (zona punteada + arrastrar-soltar), subiendo (progreso; el\n * valor no cambia hasta la respuesta), con imagen (preview + Reemplazar /\n * Quitar / Ver en grande, y drop sobre el preview para reemplazar) y error\n * (conserva la imagen anterior).\n */\n@Component({\n selector: 'app-field-image',\n imports: [Button],\n template: ` @if (error(); as message) {\n <p\n class=\"mb-2 rounded-lg border border-danger/30 bg-danger/10 px-3 py-2 text-xs text-danger\"\n role=\"alert\"\n >\n {{ message }}\n </p>\n }\n @if (uploading()) {\n <div class=\"rounded-xl border border-neutral/20 bg-surface p-3\" data-testid=\"image-uploading\">\n <div class=\"h-1.5 w-full overflow-hidden rounded-full bg-neutral/20\">\n <div\n class=\"h-full rounded-full bg-primary transition-all\"\n [style.width.%]=\"progress()\"\n ></div>\n </div>\n <p class=\"mt-1 text-xs text-neutral\">Subiendo… {{ progress() }}%</p>\n </div>\n } @else if (value()) {\n <div\n class=\"rounded-xl border border-neutral/20 bg-surface p-3\"\n data-testid=\"image-preview\"\n (dragover)=\"$event.preventDefault()\"\n (drop)=\"onDrop($event)\"\n >\n <img\n [src]=\"value()\"\n [alt]=\"''\"\n [class]=\"previewClass()\"\n [style.aspect-ratio]=\"aspectRatio()\"\n />\n <div class=\"mt-2 flex flex-wrap items-center gap-2\">\n <button\n type=\"button\"\n app-button\n variant=\"outline\"\n [disabled]=\"disabled()\"\n (click)=\"pickFile()\"\n >\n Reemplazar\n </button>\n @if (!field().required) {\n <button\n type=\"button\"\n app-button\n variant=\"outline\"\n [disabled]=\"disabled()\"\n (click)=\"clear()\"\n >\n Quitar\n </button>\n }\n <a\n class=\"text-sm font-medium text-primary hover:underline\"\n [href]=\"value()\"\n target=\"_blank\"\n rel=\"noopener noreferrer\"\n >\n Ver en grande\n </a>\n </div>\n </div>\n } @else {\n <div\n class=\"rounded-xl border-2 border-dashed border-neutral/30 p-6 text-center\"\n data-testid=\"image-dropzone\"\n (dragover)=\"$event.preventDefault()\"\n (drop)=\"onDrop($event)\"\n >\n <p class=\"text-sm text-neutral\">Arrastrá una imagen acá</p>\n <button\n type=\"button\"\n app-button\n variant=\"outline\"\n class=\"mt-2\"\n [disabled]=\"disabled()\"\n (click)=\"pickFile()\"\n >\n Subir imagen\n </button>\n </div>\n }\n\n <input\n #fileInput\n type=\"file\"\n class=\"sr-only\"\n [accept]=\"acceptAttr()\"\n [disabled]=\"disabled()\"\n (change)=\"onFileSelected($event)\"\n />`,\n})\nexport class FieldImage implements FormValueControl<string> {\n readonly field = input.required<ImageField>();\n /** Recurso dueño de la imagen (firma de subida: carpeta de Cloudinary, §11.2). */\n readonly resource = input('');\n\n /** Sincronizado por el directive `FormField` del host. */\n readonly value = model.required<string>();\n readonly disabled = input(false);\n /** Marca el campo como tocado (upload/clear/error), como el `blur` nativo. */\n readonly touch = output<void>();\n\n private readonly upload = inject(UploadService);\n private readonly notifications = inject(NotificationService);\n private readonly destroyRef = inject(DestroyRef);\n\n protected readonly uploading = signal(false);\n protected readonly progress = signal(0);\n protected readonly error = signal<string | null>(null);\n\n /** Literal en el código: Tailwind v4 genera las clases al escanearlo. */\n protected readonly previewClass = computed(\n () => `${this.field().previewSize ?? 'w-48'} rounded-lg object-cover`,\n );\n protected readonly aspectRatio = computed(() => this.field().aspectRatio ?? null);\n protected readonly acceptAttr = computed(() => this.field().accept ?? 'image/*');\n\n /**\n * Template ref `#fileInput`: Angular NO auto-asigna las template refs a\n * propiedades de clase (el campo `fileInput` quedaba `undefined` y\n * `pickFile()` no hacía nada — el selector no se abría). Igual que en\n * `modal.ts`: el tipo se expresa como genérico (`ElementRef<T>`).\n */\n private readonly fileInput = viewChild.required<ElementRef<HTMLInputElement>>('fileInput');\n private uploadSub?: { unsubscribe(): void };\n\n protected pickFile(): void {\n this.fileInput().nativeElement.click();\n }\n\n protected clear(): void {\n this.value.set('');\n this.error.set(null);\n this.touch.emit();\n }\n\n protected onFileSelected(event: Event): void {\n const input = event.target as HTMLInputElement;\n const file = input.files?.[0];\n // Permite volver a elegir el mismo archivo tras un error.\n input.value = '';\n if (file) {\n this.startUpload(file);\n }\n }\n\n protected onDrop(event: Event): void {\n event.preventDefault();\n const file = (event as DragEvent).dataTransfer?.files?.[0];\n if (file) {\n this.startUpload(file);\n }\n }\n\n /** Valida en cliente (accept + maxSizeMB) y sube con progreso. */\n private startUpload(file: File): void {\n if (this.disabled()) {\n return;\n }\n const problem = this.validate(file);\n if (problem !== null) {\n this.error.set(problem);\n this.touch.emit();\n return;\n }\n this.error.set(null);\n this.uploading.set(true);\n this.progress.set(0);\n this.touch.emit();\n\n this.uploadSub?.unsubscribe();\n // El nombre del campo viaja siempre: si declara `formats`, el backend\n // firma ESA lista; sin formatos firma la lista global (R9.5.3).\n this.uploadSub = this.upload\n .uploadWithProgress(file, this.resource(), this.field().key)\n .subscribe({\n next: (event) => {\n if (event.type === HttpEventType.UploadProgress) {\n if (event.total) {\n this.progress.set(Math.round((event.loaded / event.total) * 100));\n }\n return;\n }\n if (event.type === HttpEventType.Response) {\n const url = event.body ?? '';\n this.uploading.set(false);\n if (url === '') {\n this.error.set('No se pudo subir la imagen.');\n return;\n }\n this.value.set(url);\n this.notifications.success('Imagen subida.');\n }\n },\n error: (err: unknown) => {\n this.uploading.set(false);\n // El backend explica el motivo (firma inválida, 429 rate-limit…).\n this.error.set(\n err instanceof ApiError\n ? err.message\n : 'No se pudo subir la imagen. Revisa la conexión e inténtalo de nuevo.',\n );\n },\n });\n this.destroyRef.onDestroy(() => this.uploadSub?.unsubscribe());\n }\n\n private validate(file: File): string | null {\n if (!file.type.startsWith('image/')) {\n return 'El archivo debe ser una imagen.';\n }\n const accept = this.field().accept;\n if (accept !== undefined && !acceptMatches(accept, file)) {\n return 'Formato no permitido para este campo.';\n }\n const maxSizeMB = this.field().maxSizeMB;\n if (maxSizeMB !== undefined && file.size > maxSizeMB * 1024 * 1024) {\n return `La imagen supera ${maxSizeMB} MB.`;\n }\n return null;\n }\n}\n\n/** `accept` de `<input type=file>`: lista, `image/*` o extensión `.png`. */\nfunction acceptMatches(accept: string, file: File): boolean {\n for (const raw of accept.split(',')) {\n const entry = raw.trim().toLowerCase();\n if (entry === '') {\n continue;\n }\n if (entry.startsWith('.')) {\n if (file.name.toLowerCase().endsWith(entry)) {\n return true;\n }\n } else if (entry.endsWith('/*')) {\n if (file.type.startsWith(entry.slice(0, -1))) {\n return true;\n }\n } else if (file.type === entry) {\n return true;\n }\n }\n return false;\n}\n"],"mappings":"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"}
@@ -1,439 +0,0 @@
1
- import { i as getSchema } from "./tinya-code-gas-admin-ui-schema-catalog-D0Pk0wAN.mjs";
2
- import { t as Button } from "./tinya-code-gas-admin-ui-button-BQjNW8KB.mjs";
3
- import { t as DataSourceService } from "./tinya-code-gas-admin-ui-data-source.service-96FSbN72.mjs";
4
- import { t as ErrorMapperService } from "./tinya-code-gas-admin-ui-error-mapper.service-DY5y1FjE.mjs";
5
- import { t as EmptyState } from "./tinya-code-gas-admin-ui-empty-state-BMc0QtoT.mjs";
6
- import { t as Skeleton } from "./tinya-code-gas-admin-ui-skeleton-DnWK0tcZ.mjs";
7
- import * as i0 from "@angular/core";
8
- import { ChangeDetectionStrategy, Component, computed, effect, inject, signal } from "@angular/core";
9
- import { ActivatedRoute } from "@angular/router";
10
- import { toSignal } from "@angular/core/rxjs-interop";
11
- const CARD_NO_TITLE = "Sin título";
12
- const OBJECT_VALUE_KEYS = [
13
- "label",
14
- "value",
15
- "id"
16
- ];
17
- function scalarText(value) {
18
- if (typeof value === "string") {
19
- const text = value.trim();
20
- return text === "" ? null : text;
21
- }
22
- if (typeof value === "number" || typeof value === "boolean" || typeof value === "bigint") return String(value);
23
- return null;
24
- }
25
- function cellText(value) {
26
- const scalar = scalarText(value);
27
- if (scalar !== null) return scalar;
28
- if (value !== null && typeof value === "object") {
29
- const cell = value;
30
- for (const key of OBJECT_VALUE_KEYS) {
31
- const text = scalarText(cell[key]);
32
- if (text !== null) return text;
33
- }
34
- }
35
- return null;
36
- }
37
- function mapKanbanRows(rows, config, opts = {}) {
38
- const columns = [];
39
- const byValue = /* @__PURE__ */ new Map();
40
- const declared = Array.isArray(config.columns) ? config.columns : [];
41
- for (const column of declared) {
42
- const value = typeof column?.value === "string" ? column.value : "";
43
- if (value === "" || byValue.has(value)) continue;
44
- const bucket = {
45
- value,
46
- label: typeof column.label === "string" && column.label !== "" ? column.label : value,
47
- rows: []
48
- };
49
- byValue.set(value, bucket);
50
- columns.push(bucket);
51
- }
52
- const unknown = [];
53
- for (const row of rows) {
54
- const value = cellText(row[config.groupField]);
55
- if (value === null) {
56
- unknown.push(row);
57
- continue;
58
- }
59
- let bucket = byValue.get(value);
60
- if (bucket === void 0) {
61
- bucket = {
62
- value,
63
- label: opts.labelFor !== void 0 ? opts.labelFor(value) : value,
64
- rows: []
65
- };
66
- byValue.set(value, bucket);
67
- columns.push(bucket);
68
- }
69
- bucket.rows.push(row);
70
- }
71
- return {
72
- columns,
73
- unknown
74
- };
75
- }
76
- function cardTitle(row, config, schema) {
77
- const candidates = [
78
- config.titleField,
79
- schema.titleField,
80
- schema.keyField
81
- ];
82
- for (const field of candidates) {
83
- if (typeof field !== "string" || field === "") continue;
84
- const text = cellText(row[field]);
85
- if (text !== null) return text;
86
- }
87
- return CARD_NO_TITLE;
88
- }
89
- function cardSubtitle(row, config, _schema) {
90
- const field = config.subtitleField;
91
- if (typeof field !== "string" || field === "") return "";
92
- return cellText(row[field]) ?? "";
93
- }
94
- function kanbanConfigOf(view) {
95
- if (!view || typeof view !== "object") return void 0;
96
- const candidate = view;
97
- return candidate.type === "kanban" && candidate.config !== void 0 ? candidate.config : void 0;
98
- }
99
- const UNKNOWN_LABEL = "Sin valor";
100
- const UNKNOWN_VALUE = "__sin_valor__";
101
- var KanbanView = class KanbanView {
102
- route = inject(ActivatedRoute);
103
- routeData = toSignal(this.route.data, { requireSync: true });
104
- dataSource = inject(DataSourceService);
105
- errorMapper = inject(ErrorMapperService);
106
- schema = computed(() => {
107
- const data = this.routeData();
108
- const explicit = data["schema"];
109
- if (explicit !== void 0) return explicit;
110
- const resourceId = data["resourceId"];
111
- return typeof resourceId === "string" ? getSchema(resourceId) : void 0;
112
- }, ...ngDevMode ? [{ debugName: "schema" }] : /* istanbul ignore next */ []);
113
- config = computed(() => {
114
- const schema = this.schema();
115
- const viewName = this.routeData()["uiView"];
116
- if (schema === void 0 || typeof viewName !== "string") return void 0;
117
- const cfg = kanbanConfigOf(schema.uiViews?.[viewName]);
118
- if (cfg === void 0) return void 0;
119
- return typeof cfg.groupField === "string" && cfg.groupField !== "" && Array.isArray(cfg.columns) && cfg.columns.length > 0 && cfg.columns.every((column) => typeof column?.value === "string" && column.value !== "") ? cfg : void 0;
120
- }, ...ngDevMode ? [{ debugName: "config" }] : /* istanbul ignore next */ []);
121
- status = signal("loading", ...ngDevMode ? [{ debugName: "status" }] : /* istanbul ignore next */ []);
122
- rows = signal([], ...ngDevMode ? [{ debugName: "rows" }] : /* istanbul ignore next */ []);
123
- errorMessage = signal("", ...ngDevMode ? [{ debugName: "errorMessage" }] : /* istanbul ignore next */ []);
124
- seleccionada = signal(null, ...ngDevMode ? [{ debugName: "seleccionada" }] : /* istanbul ignore next */ []);
125
- loadEpoch = 0;
126
- constructor() {
127
- effect(() => {
128
- const cfg = this.config();
129
- if (cfg === void 0) return;
130
- this.load(cfg);
131
- });
132
- }
133
- mapping = computed(() => {
134
- const cfg = this.config();
135
- const schema = this.schema();
136
- if (cfg === void 0 || schema === void 0) return {
137
- columns: [],
138
- unknown: []
139
- };
140
- return mapKanbanRows(this.rows(), cfg);
141
- }, ...ngDevMode ? [{ debugName: "mapping" }] : /* istanbul ignore next */ []);
142
- columns = computed(() => {
143
- const { columns, unknown } = this.mapping();
144
- if (unknown.length === 0) return columns;
145
- return [...columns, {
146
- value: UNKNOWN_VALUE,
147
- label: UNKNOWN_LABEL,
148
- rows: unknown
149
- }];
150
- }, ...ngDevMode ? [{ debugName: "columns" }] : /* istanbul ignore next */ []);
151
- retry() {
152
- const cfg = this.config();
153
- if (cfg !== void 0) this.load(cfg);
154
- }
155
- keyOf(row) {
156
- const schema = this.schema();
157
- const key = schema ? row[schema.keyField] : void 0;
158
- return key === void 0 || key === null ? "" : String(key);
159
- }
160
- binding() {
161
- return this.routeData()["select"];
162
- }
163
- activar(event, row) {
164
- if (!this.seleccionable()) return;
165
- event.preventDefault();
166
- this.seleccionar(row);
167
- }
168
- seleccionar(row) {
169
- const binding = this.binding();
170
- if (binding === void 0) return;
171
- if (binding.mode === "inline") {
172
- this.seleccionada.set(this.keyOf(row));
173
- return;
174
- }
175
- if (binding.mode === "none") return;
176
- const schema = this.schema();
177
- const emit = this.routeData()["emit"];
178
- emit?.({
179
- resource: schema?.id ?? String(this.routeData()["resourceId"] ?? ""),
180
- key: this.keyOf(row),
181
- action: binding.on
182
- });
183
- }
184
- seleccionable() {
185
- const binding = this.binding();
186
- return binding !== void 0 && binding.mode !== "none";
187
- }
188
- esSeleccionada(row) {
189
- return this.seleccionable() && this.seleccionada() === this.keyOf(row);
190
- }
191
- columnAria(column) {
192
- const count = column.rows.length;
193
- return `${column.label}: ${count} ${count === 1 ? "tarjeta" : "tarjetas"}`;
194
- }
195
- cardTitleOf(row) {
196
- const cfg = this.config();
197
- const schema = this.schema();
198
- if (cfg === void 0 || schema === void 0) return "";
199
- return cardTitle(row, cfg, schema);
200
- }
201
- cardSubtitleOf(row) {
202
- const cfg = this.config();
203
- const schema = this.schema();
204
- if (cfg === void 0 || schema === void 0) return "";
205
- return cardSubtitle(row, cfg, schema);
206
- }
207
- async load(cfg) {
208
- const schema = this.schema();
209
- if (schema === void 0) return;
210
- const epoch = ++this.loadEpoch;
211
- this.status.set("loading");
212
- const source = cfg.source ?? { resource: schema.id };
213
- try {
214
- const rows = await this.dataSource.rows(source, void 0);
215
- if (epoch !== this.loadEpoch) return;
216
- this.rows.set(rows);
217
- this.errorMessage.set("");
218
- this.status.set("ready");
219
- } catch (error) {
220
- if (epoch !== this.loadEpoch) return;
221
- this.errorMessage.set(this.errorMapper.map(error).general ?? "No se pudo cargar el tablero.");
222
- this.status.set("error");
223
- }
224
- }
225
- static ɵfac = i0.ɵɵngDeclareFactory({
226
- minVersion: "12.0.0",
227
- version: "22.2.3",
228
- ngImport: i0,
229
- type: KanbanView,
230
- deps: [],
231
- target: i0.ɵɵFactoryTarget.Component
232
- });
233
- static ɵcmp = i0.ɵɵngDeclareComponent({
234
- minVersion: "17.0.0",
235
- version: "22.2.3",
236
- type: KanbanView,
237
- isStandalone: true,
238
- selector: "app-kanban-view",
239
- ngImport: i0,
240
- template: `
241
- @if (config(); as cfg) {
242
- <section class="space-y-4" aria-label="Tablero" [attr.aria-busy]="status() === 'loading'">
243
- <header class="flex flex-wrap items-center justify-between gap-3">
244
- <h2 class="font-display text-xl font-semibold">Tablero</h2>
245
- <p class="text-sm text-neutral">
246
- Agrupado por <code>{{ cfg.groupField }}</code>
247
- </p>
248
- </header>
249
-
250
- @switch (status()) {
251
- @case ('loading') {
252
- <app-skeleton [lines]="6" [height]="32" />
253
- }
254
- @case ('error') {
255
- <app-empty-state title="No se pudo cargar el tablero" [message]="errorMessage()">
256
- <button app-button variant="outline" type="button" (click)="retry()">
257
- Reintentar
258
- </button>
259
- </app-empty-state>
260
- }
261
- @case ('ready') {
262
- <div class="flex gap-3 overflow-x-auto pb-2">
263
- @for (column of columns(); track column.value) {
264
- <section
265
- class="flex w-64 shrink-0 flex-col gap-3 rounded-xl border border-neutral/20 bg-surface p-3"
266
- [attr.data-column]="column.value"
267
- [attr.aria-label]="columnAria(column)"
268
- >
269
- <header class="flex items-center justify-between gap-2">
270
- <h3 class="text-sm font-semibold">{{ column.label }}</h3>
271
- <span class="rounded-full bg-neutral/10 px-2 py-0.5 text-xs text-neutral">
272
- {{ column.rows.length }}<span class="sr-only"> tarjetas</span>
273
- </span>
274
- </header>
275
- @if (column.rows.length === 0) {
276
- <p class="text-sm text-neutral">Sin tarjetas</p>
277
- } @else {
278
- <ul class="flex flex-col gap-2">
279
- @for (row of column.rows; track $index) {
280
- <li
281
- class="rounded-lg border border-neutral/15 p-2"
282
- [attr.tabindex]="seleccionable() ? '0' : null"
283
- [attr.role]="seleccionable() ? 'button' : null"
284
- [class.cursor-pointer]="seleccionable()"
285
- [class.ring-2]="esSeleccionada(row)"
286
- [attr.data-selectable]="seleccionable() ? 'true' : null"
287
- (click)="seleccionar(row)"
288
- (keydown.enter)="activar($event, row)"
289
- (keydown.space)="activar($event, row)"
290
- >
291
- <p class="text-sm font-medium">{{ cardTitleOf(row) }}</p>
292
- @if (cardSubtitleOf(row); as subtitle) {
293
- <p class="mt-0.5 text-xs text-neutral">{{ subtitle }}</p>
294
- }
295
- </li>
296
- }
297
- </ul>
298
- }
299
- </section>
300
- }
301
- </div>
302
- }
303
- }
304
- </section>
305
- } @else {
306
- <app-empty-state
307
- title="Configuración inválida"
308
- message="Esta vista no declara una configuración de kanban válida."
309
- />
310
- }
311
- `,
312
- isInline: true,
313
- dependencies: [
314
- {
315
- kind: "component",
316
- type: Button,
317
- selector: "button[app-button]",
318
- inputs: [
319
- "variant",
320
- "size",
321
- "type",
322
- "disabled",
323
- "loading"
324
- ]
325
- },
326
- {
327
- kind: "component",
328
- type: EmptyState,
329
- selector: "app-empty-state",
330
- inputs: ["title", "message"]
331
- },
332
- {
333
- kind: "component",
334
- type: Skeleton,
335
- selector: "app-skeleton",
336
- inputs: [
337
- "lines",
338
- "height",
339
- "width"
340
- ]
341
- }
342
- ],
343
- changeDetection: i0.ChangeDetectionStrategy.OnPush
344
- });
345
- };
346
- i0.ɵɵngDeclareClassMetadata({
347
- minVersion: "12.0.0",
348
- version: "22.2.3",
349
- ngImport: i0,
350
- type: KanbanView,
351
- decorators: [{
352
- type: Component,
353
- args: [{
354
- selector: "app-kanban-view",
355
- changeDetection: ChangeDetectionStrategy.OnPush,
356
- imports: [
357
- Button,
358
- EmptyState,
359
- Skeleton
360
- ],
361
- template: `
362
- @if (config(); as cfg) {
363
- <section class="space-y-4" aria-label="Tablero" [attr.aria-busy]="status() === 'loading'">
364
- <header class="flex flex-wrap items-center justify-between gap-3">
365
- <h2 class="font-display text-xl font-semibold">Tablero</h2>
366
- <p class="text-sm text-neutral">
367
- Agrupado por <code>{{ cfg.groupField }}</code>
368
- </p>
369
- </header>
370
-
371
- @switch (status()) {
372
- @case ('loading') {
373
- <app-skeleton [lines]="6" [height]="32" />
374
- }
375
- @case ('error') {
376
- <app-empty-state title="No se pudo cargar el tablero" [message]="errorMessage()">
377
- <button app-button variant="outline" type="button" (click)="retry()">
378
- Reintentar
379
- </button>
380
- </app-empty-state>
381
- }
382
- @case ('ready') {
383
- <div class="flex gap-3 overflow-x-auto pb-2">
384
- @for (column of columns(); track column.value) {
385
- <section
386
- class="flex w-64 shrink-0 flex-col gap-3 rounded-xl border border-neutral/20 bg-surface p-3"
387
- [attr.data-column]="column.value"
388
- [attr.aria-label]="columnAria(column)"
389
- >
390
- <header class="flex items-center justify-between gap-2">
391
- <h3 class="text-sm font-semibold">{{ column.label }}</h3>
392
- <span class="rounded-full bg-neutral/10 px-2 py-0.5 text-xs text-neutral">
393
- {{ column.rows.length }}<span class="sr-only"> tarjetas</span>
394
- </span>
395
- </header>
396
- @if (column.rows.length === 0) {
397
- <p class="text-sm text-neutral">Sin tarjetas</p>
398
- } @else {
399
- <ul class="flex flex-col gap-2">
400
- @for (row of column.rows; track $index) {
401
- <li
402
- class="rounded-lg border border-neutral/15 p-2"
403
- [attr.tabindex]="seleccionable() ? '0' : null"
404
- [attr.role]="seleccionable() ? 'button' : null"
405
- [class.cursor-pointer]="seleccionable()"
406
- [class.ring-2]="esSeleccionada(row)"
407
- [attr.data-selectable]="seleccionable() ? 'true' : null"
408
- (click)="seleccionar(row)"
409
- (keydown.enter)="activar($event, row)"
410
- (keydown.space)="activar($event, row)"
411
- >
412
- <p class="text-sm font-medium">{{ cardTitleOf(row) }}</p>
413
- @if (cardSubtitleOf(row); as subtitle) {
414
- <p class="mt-0.5 text-xs text-neutral">{{ subtitle }}</p>
415
- }
416
- </li>
417
- }
418
- </ul>
419
- }
420
- </section>
421
- }
422
- </div>
423
- }
424
- }
425
- </section>
426
- } @else {
427
- <app-empty-state
428
- title="Configuración inválida"
429
- message="Esta vista no declara una configuración de kanban válida."
430
- />
431
- }
432
- `
433
- }]
434
- }],
435
- ctorParameters: () => []
436
- });
437
- export { KanbanView };
438
-
439
- //# sourceMappingURL=tinya-code-gas-admin-ui-kanban-view-Dl0Ol5Kb.mjs.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"tinya-code-gas-admin-ui-kanban-view-Dl0Ol5Kb.mjs","names":[],"sources":["../../../projects/gas-admin-ui/src/lib/views/kanban-view/kanban-model.ts","../../../projects/gas-admin-ui/src/lib/views/kanban-view/kanban-view.ts"],"sourcesContent":["import type { KanbanViewConfig, ResourceSchema } from '../../core/models/schema.model';\nimport type { SourceRow } from '../../core/services/data-source.service';\n\n/**\n * Pure grouping + card-text model for the generic kanban view (R9.8.4).\n *\n * No Angular imports: everything here is a plain function over rows, so the\n * rules below are testable without a TestBed.\n *\n * Rules (decided · R9.8.1 read-only, R9.8.4 visibility):\n * - Column order is `config.columns` order; a declared column ALWAYS exists,\n * even with zero rows (an empty column is a visible «Sin tarjetas», never\n * a missing column).\n * - A row whose `groupField` value is empty/undefined goes to `unknown` —\n * surfaced, never silently dropped.\n * - A value that is not declared gets its own column appended AFTER the\n * declared ones: undeclared values are VISIBLE, not hidden.\n * - Order inside a column is the row insertion order (stable, deterministic).\n */\n\n/** One column of the board: declared in the config or discovered in the rows. */\nexport interface KanbanColumnBucket {\n /** Raw `groupField` value that owns the bucket (unique across the board). */\n value: string;\n /** Visible label: the declared column's label, or the raw value. */\n label: string;\n /** Rows of the bucket, in insertion order. */\n rows: SourceRow[];\n}\n\n/** Result of grouping raw rows onto board columns (R9.8.4). */\nexport interface KanbanMapping {\n /** Declared columns (in declared order) + one bucket per undeclared value. */\n columns: KanbanColumnBucket[];\n /** Rows whose `groupField` value is empty — visible, never dropped. */\n unknown: SourceRow[];\n}\n\n/** Options of the grouping (all optional; the defaults are the decided rules). */\nexport interface KanbanMapOptions {\n /**\n * Label of buckets whose value is NOT declared in `config.columns`.\n * Default: the raw value itself, so undeclared values stay verbatim and\n * visible. The view uses the default; the hook exists for callers that\n * want to decorate the label without touching the grouping rules.\n */\n labelFor?: (value: string) => string;\n}\n\n/** Title of a card whose every candidate is empty/unreadable (never blank). */\nexport const CARD_NO_TITLE = 'Sin título';\n\n/** Object cells: the readable keys, in preference order. */\nconst OBJECT_VALUE_KEYS = ['label', 'value', 'id'] as const;\n\n/** Printable text of a scalar cell; `null` when it is empty or not scalar. */\nfunction scalarText(value: unknown): string | null {\n if (typeof value === 'string') {\n const text = value.trim();\n return text === '' ? null : text;\n }\n if (typeof value === 'number' || typeof value === 'boolean' || typeof value === 'bigint') {\n return String(value);\n }\n return null;\n}\n\n/**\n * Printable text of ANY cell: scalars as-is (trimmed), objects reduced to\n * their first readable `label`/`value`/`id`. `null` when there is nothing\n * readable — the caller decides (bucket → `unknown`, card → next fallback).\n *\n * Never returns `undefined` nor `[object Object]`: that is the whole point.\n */\nfunction cellText(value: unknown): string | null {\n const scalar = scalarText(value);\n if (scalar !== null) return scalar;\n if (value !== null && typeof value === 'object') {\n const cell = value as Record<string, unknown>;\n for (const key of OBJECT_VALUE_KEYS) {\n const text = scalarText(cell[key]);\n if (text !== null) return text;\n }\n }\n return null;\n}\n\n/**\n * Pure row → board grouping (R9.8.4): the single place that decides which\n * column owns each row, which rows are «unknown» and in what order the\n * columns are painted.\n *\n * Deterministic: declared columns keep their declared order (even when\n * empty), undeclared values are appended in first-appearance order, and the\n * rows inside each column keep their insertion order.\n */\nexport function mapKanbanRows(\n rows: readonly SourceRow[],\n config: KanbanViewConfig,\n opts: KanbanMapOptions = {},\n): KanbanMapping {\n const columns: KanbanColumnBucket[] = [];\n const byValue = new Map<string, KanbanColumnBucket>();\n\n // Declared columns first — always visible, even with zero rows.\n const declared = Array.isArray(config.columns) ? config.columns : [];\n for (const column of declared) {\n const value = typeof column?.value === 'string' ? column.value : '';\n // Contract (R9.8.2): `value` obligatorio y único — a duplicated or empty\n // declaration cannot own a bucket, the first valid one wins.\n if (value === '' || byValue.has(value)) continue;\n const label = typeof column.label === 'string' && column.label !== '' ? column.label : value;\n const bucket: KanbanColumnBucket = { value, label, rows: [] };\n byValue.set(value, bucket);\n columns.push(bucket);\n }\n\n const unknown: SourceRow[] = [];\n for (const row of rows) {\n const value = cellText(row[config.groupField]);\n if (value === null) {\n // Empty/undefined (or unreadable object): visible bucket, never dropped.\n unknown.push(row);\n continue;\n }\n let bucket = byValue.get(value);\n if (bucket === undefined) {\n // Undeclared value: its own column, appended AFTER the declared ones.\n const label = opts.labelFor !== undefined ? opts.labelFor(value) : value;\n bucket = { value, label, rows: [] };\n byValue.set(value, bucket);\n columns.push(bucket);\n }\n bucket.rows.push(row);\n }\n\n return { columns, unknown };\n}\n\n/**\n * Title of a card: `config.titleField` → `schema.titleField` →\n * `schema.keyField`, first candidate with readable text wins. An unreadable\n * value (empty, `undefined`, an object with no label) never reaches the DOM:\n * the chain falls through to the key, and to a visible «Sin título» when not\n * even the key has text.\n */\nexport function cardTitle(\n row: SourceRow,\n config: KanbanViewConfig,\n schema: ResourceSchema,\n): string {\n const candidates = [config.titleField, schema.titleField, schema.keyField];\n for (const field of candidates) {\n if (typeof field !== 'string' || field === '') continue;\n const text = cellText(row[field]);\n if (text !== null) return text;\n }\n return CARD_NO_TITLE;\n}\n\n/**\n * Second line of a card: the `config.subtitleField` value when the config\n * declares one, `''` otherwise (the schema has no notion of subtitle).\n */\nexport function cardSubtitle(\n row: SourceRow,\n config: KanbanViewConfig,\n _schema: ResourceSchema,\n): string {\n const field = config.subtitleField;\n if (typeof field !== 'string' || field === '') return '';\n return cellText(row[field]) ?? '';\n}\n","import {\n ChangeDetectionStrategy,\n Component,\n computed,\n effect,\n inject,\n signal,\n} from '@angular/core';\nimport { toSignal } from '@angular/core/rxjs-interop';\nimport { ActivatedRoute } from '@angular/router';\n\nimport type {\n KanbanViewConfig,\n ResourceSchema,\n SelectionBinding,\n} from '../../core/models/schema.model';\nimport { ErrorMapperService } from '../../core/services/error-mapper.service';\nimport { DataSourceService, type SourceRow } from '../../core/services/data-source.service';\nimport type { SelectionEmitter } from '../composer/view-context';\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 {\n cardSubtitle,\n cardTitle,\n mapKanbanRows,\n type KanbanColumnBucket,\n type KanbanMapping,\n} from './kanban-model';\n\n/**\n * Narrowing helper: `UiViewBase` keeps `type: string` open, so comparing\n * `view.type === 'kanban'` alone does not remove the base member from the\n * union. Requiring `config` in the value is what actually proves the view is\n * a `UiViewKanban` at runtime.\n */\nfunction kanbanConfigOf(view: unknown): KanbanViewConfig | undefined {\n if (!view || typeof view !== 'object') return undefined;\n const candidate = view as { type?: unknown; config?: unknown };\n return candidate.type === 'kanban' && candidate.config !== undefined\n ? (candidate.config as KanbanViewConfig)\n : undefined;\n}\n\ntype Status = 'loading' | 'ready' | 'error';\n\n/** Label of the bucket for rows without a group value (Spanish UI copy). */\nconst UNKNOWN_LABEL = 'Sin valor';\n\n/**\n * Synthetic `value` of that bucket: a real group value can never collide\n * with it (a declared `value` is non-empty and the grouping never produces\n * this literal), so `@for` tracking stays unique.\n */\nconst UNKNOWN_VALUE = '__sin_valor__';\n\n/**\n * Generic kanban view (R9.8.4): rows of the declared\n * `KanbanViewConfig.source` (or the resource's own listing) grouped by\n * `groupField` into the declared columns, client-side.\n *\n * Data flow: ONE fetcher (`DataSourceService.rows`) — there is no dynamic\n * context here, so `ctx` is `undefined`; the 30 s cache of the service is\n * what keeps repeated visits free.\n *\n * Read-only by design (R9.8.1): no drag-and-drop, no move buttons, no\n * mutation path at all. Colour never carries meaning alone — every count is\n * text — and an empty column is a visible «Sin tarjetas», never a gap.\n */\n@Component({\n selector: 'app-kanban-view',\n changeDetection: ChangeDetectionStrategy.OnPush,\n imports: [Button, EmptyState, Skeleton],\n template: `\n @if (config(); as cfg) {\n <section class=\"space-y-4\" aria-label=\"Tablero\" [attr.aria-busy]=\"status() === 'loading'\">\n <header class=\"flex flex-wrap items-center justify-between gap-3\">\n <h2 class=\"font-display text-xl font-semibold\">Tablero</h2>\n <p class=\"text-sm text-neutral\">\n Agrupado por <code>{{ cfg.groupField }}</code>\n </p>\n </header>\n\n @switch (status()) {\n @case ('loading') {\n <app-skeleton [lines]=\"6\" [height]=\"32\" />\n }\n @case ('error') {\n <app-empty-state title=\"No se pudo cargar el tablero\" [message]=\"errorMessage()\">\n <button app-button variant=\"outline\" type=\"button\" (click)=\"retry()\">\n Reintentar\n </button>\n </app-empty-state>\n }\n @case ('ready') {\n <div class=\"flex gap-3 overflow-x-auto pb-2\">\n @for (column of columns(); track column.value) {\n <section\n class=\"flex w-64 shrink-0 flex-col gap-3 rounded-xl border border-neutral/20 bg-surface p-3\"\n [attr.data-column]=\"column.value\"\n [attr.aria-label]=\"columnAria(column)\"\n >\n <header class=\"flex items-center justify-between gap-2\">\n <h3 class=\"text-sm font-semibold\">{{ column.label }}</h3>\n <span class=\"rounded-full bg-neutral/10 px-2 py-0.5 text-xs text-neutral\">\n {{ column.rows.length }}<span class=\"sr-only\"> tarjetas</span>\n </span>\n </header>\n @if (column.rows.length === 0) {\n <p class=\"text-sm text-neutral\">Sin tarjetas</p>\n } @else {\n <ul class=\"flex flex-col gap-2\">\n @for (row of column.rows; track $index) {\n <li\n class=\"rounded-lg border border-neutral/15 p-2\"\n [attr.tabindex]=\"seleccionable() ? '0' : null\"\n [attr.role]=\"seleccionable() ? 'button' : null\"\n [class.cursor-pointer]=\"seleccionable()\"\n [class.ring-2]=\"esSeleccionada(row)\"\n [attr.data-selectable]=\"seleccionable() ? 'true' : null\"\n (click)=\"seleccionar(row)\"\n (keydown.enter)=\"activar($event, row)\"\n (keydown.space)=\"activar($event, row)\"\n >\n <p class=\"text-sm font-medium\">{{ cardTitleOf(row) }}</p>\n @if (cardSubtitleOf(row); as subtitle) {\n <p class=\"mt-0.5 text-xs text-neutral\">{{ subtitle }}</p>\n }\n </li>\n }\n </ul>\n }\n </section>\n }\n </div>\n }\n }\n </section>\n } @else {\n <app-empty-state\n title=\"Configuración inválida\"\n message=\"Esta vista no declara una configuración de kanban válida.\"\n />\n }\n `,\n})\nexport class KanbanView {\n private readonly route = inject(ActivatedRoute);\n // The shell mounts views via `loadComponent` (no input bindings), so the\n // schema is resolved from route data exactly like CalendarView does.\n private readonly routeData = toSignal(this.route.data, { requireSync: true });\n private readonly dataSource = inject(DataSourceService);\n private readonly errorMapper = inject(ErrorMapperService);\n\n /**\n * Resource schema for this view. The shell mounts views via\n * `loadComponent` (no input bindings), so the schema is resolved from\n * route data: either an explicit `schema` object (fixtures), or a\n * `resourceId` looked up in the registry (the production route shape).\n */\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 /**\n * Config resolved from `schema.uiViews[<route uiView>]`; `undefined` when\n * the route does not name a kanban view with a usable config — the\n * template then renders the invalid-config empty state and NOTHING is\n * fetched (the effect below bails out on `undefined`).\n */\n readonly config = computed<KanbanViewConfig | undefined>(() => {\n const schema = this.schema();\n const viewName = this.routeData()['uiView'];\n if (schema === undefined || typeof viewName !== 'string') return undefined;\n const cfg = kanbanConfigOf(schema.uiViews?.[viewName]);\n if (cfg === undefined) return undefined;\n // Usable = the two references the board cannot render without: a\n // non-empty `groupField` and at least one column with a non-empty value.\n const usable =\n typeof cfg.groupField === 'string' &&\n cfg.groupField !== '' &&\n Array.isArray(cfg.columns) &&\n cfg.columns.length > 0 &&\n cfg.columns.every((column) => typeof column?.value === 'string' && column.value !== '');\n return usable ? cfg : undefined;\n });\n\n readonly status = signal<Status>('loading');\n readonly rows = signal<SourceRow[]>([]);\n readonly errorMessage = signal('');\n /** Clave seleccionada localmente cuando el binding declara `mode:'inline'`. */\n readonly seleccionada = signal<string | null>(null);\n\n /** Guards against a slow response overwriting a newer load's rows. */\n private loadEpoch = 0;\n\n constructor() {\n effect(() => {\n const cfg = this.config();\n if (cfg === undefined) return;\n void this.load(cfg);\n });\n }\n\n /** Rows grouped onto columns + the visible `unknown` bucket (pure model). */\n readonly mapping = computed<KanbanMapping>(() => {\n const cfg = this.config();\n const schema = this.schema();\n if (cfg === undefined || schema === undefined) return { columns: [], unknown: [] };\n return mapKanbanRows(this.rows(), cfg);\n });\n\n /**\n * Columns to paint: the model's buckets plus the `unknown` bucket at the\n * END — but only when it actually has rows, otherwise the board would show\n * an empty column nobody declared.\n */\n readonly columns = computed<readonly KanbanColumnBucket[]>(() => {\n const { columns, unknown } = this.mapping();\n if (unknown.length === 0) return columns;\n return [...columns, { value: UNKNOWN_VALUE, label: UNKNOWN_LABEL, rows: unknown }];\n });\n\n retry(): void {\n const cfg = this.config();\n if (cfg !== undefined) void this.load(cfg);\n }\n\n /** Clave estable de la fila (mismo campo que el resto del motor). */\n private keyOf(row: SourceRow): string {\n const schema = this.schema();\n const key = schema ? row[schema.keyField] : undefined;\n return key === undefined || key === null ? '' : String(key);\n }\n\n /** Binding `select` declarado por la vista (R9.9.4 · decision table). */\n private binding(): SelectionBinding | undefined {\n return this.routeData()['select'] as SelectionBinding | undefined;\n }\n\n /**\n * Clic de tarjeta. Sin declaración ⇒ inerte (el tablero no navegaba);\n * con declaración ⇒ sólo el contrato declarado corre: `route` emite al\n * dispatcher, `inline` selecciona en local, `none` es no-op explícito.\n */\n /**\n * R9.8.7 — la misma selección que el clic, operable con teclado (Enter y\n * Space sobre la tarjeta focusable). Sólo tiene sentido con `select`\n * declarado: sin binding la tarjeta no es focusable y esto nunca corre.\n */\n activar(event: Event, row: SourceRow): void {\n if (!this.seleccionable()) return;\n event.preventDefault(); // Space no debe desplazar la página\n this.seleccionar(row);\n }\n\n seleccionar(row: SourceRow): void {\n const binding = this.binding();\n if (binding === undefined) return;\n if (binding.mode === 'inline') {\n this.seleccionada.set(this.keyOf(row));\n return;\n }\n if (binding.mode === 'none') return;\n const schema = this.schema();\n const emit = this.routeData()['emit'] as SelectionEmitter | undefined;\n emit?.({\n resource: schema?.id ?? String(this.routeData()['resourceId'] ?? ''),\n key: this.keyOf(row),\n action: binding.on,\n });\n }\n\n /** El DOM declara clicable sólo si hay contrato que responder. */\n seleccionable(): boolean {\n const binding = this.binding();\n return binding !== undefined && binding.mode !== 'none';\n }\n\n esSeleccionada(row: SourceRow): boolean {\n return this.seleccionable() && this.seleccionada() === this.keyOf(row);\n }\n\n /** «Por hacer: 2 tarjetas» — the count is text, never colour alone. */\n columnAria(column: KanbanColumnBucket): string {\n const count = column.rows.length;\n return `${column.label}: ${count} ${count === 1 ? 'tarjeta' : 'tarjetas'}`;\n }\n\n cardTitleOf(row: SourceRow): string {\n const cfg = this.config();\n const schema = this.schema();\n if (cfg === undefined || schema === undefined) return '';\n return cardTitle(row, cfg, schema);\n }\n\n cardSubtitleOf(row: SourceRow): string {\n const cfg = this.config();\n const schema = this.schema();\n if (cfg === undefined || schema === undefined) return '';\n return cardSubtitle(row, cfg, schema);\n }\n\n private async load(cfg: KanbanViewConfig): Promise<void> {\n const schema = this.schema();\n if (schema === undefined) return;\n const epoch = ++this.loadEpoch;\n this.status.set('loading');\n const source = cfg.source ?? { resource: schema.id };\n try {\n const rows = await this.dataSource.rows(source, undefined);\n if (epoch !== this.loadEpoch) return;\n this.rows.set(rows);\n this.errorMessage.set('');\n this.status.set('ready');\n } catch (error) {\n if (epoch !== this.loadEpoch) return;\n this.errorMessage.set(this.errorMapper.map(error).general ?? 'No se pudo cargar el tablero.');\n this.status.set('error');\n }\n }\n}\n"],"mappings":"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"}