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

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 (368) hide show
  1. package/fesm2022/tinya-code-gas-admin-ui-badge-EeCA_l-o.mjs +84 -0
  2. package/fesm2022/tinya-code-gas-admin-ui-badge-EeCA_l-o.mjs.map +1 -0
  3. package/fesm2022/tinya-code-gas-admin-ui-bar-chart-G7sEMSBY.mjs +140 -0
  4. package/fesm2022/tinya-code-gas-admin-ui-bar-chart-G7sEMSBY.mjs.map +1 -0
  5. package/fesm2022/tinya-code-gas-admin-ui-boolean-B2ASKL0E.mjs +113 -0
  6. package/fesm2022/tinya-code-gas-admin-ui-boolean-B2ASKL0E.mjs.map +1 -0
  7. package/fesm2022/tinya-code-gas-admin-ui-button-BQjNW8KB.mjs +163 -0
  8. package/fesm2022/tinya-code-gas-admin-ui-button-BQjNW8KB.mjs.map +1 -0
  9. package/fesm2022/tinya-code-gas-admin-ui-calendar-view-DCY8bj2V.mjs +1027 -0
  10. package/fesm2022/tinya-code-gas-admin-ui-calendar-view-DCY8bj2V.mjs.map +1 -0
  11. package/fesm2022/tinya-code-gas-admin-ui-chart-line-BjdlKHoH.mjs +278 -0
  12. package/fesm2022/tinya-code-gas-admin-ui-chart-line-BjdlKHoH.mjs.map +1 -0
  13. package/fesm2022/tinya-code-gas-admin-ui-color-Dljh_By3.mjs +107 -0
  14. package/fesm2022/tinya-code-gas-admin-ui-color-Dljh_By3.mjs.map +1 -0
  15. package/fesm2022/tinya-code-gas-admin-ui-condition-evaluator-DY2k1M3C.mjs +101 -0
  16. package/fesm2022/tinya-code-gas-admin-ui-condition-evaluator-DY2k1M3C.mjs.map +1 -0
  17. package/fesm2022/tinya-code-gas-admin-ui-currency-BvuF3Z63.mjs +141 -0
  18. package/fesm2022/tinya-code-gas-admin-ui-currency-BvuF3Z63.mjs.map +1 -0
  19. package/fesm2022/tinya-code-gas-admin-ui-data-source.service-96FSbN72.mjs +88 -0
  20. package/fesm2022/tinya-code-gas-admin-ui-data-source.service-96FSbN72.mjs.map +1 -0
  21. package/fesm2022/tinya-code-gas-admin-ui-date-1SJGXZrd.mjs +107 -0
  22. package/fesm2022/tinya-code-gas-admin-ui-date-1SJGXZrd.mjs.map +1 -0
  23. package/fesm2022/tinya-code-gas-admin-ui-display-badge-fwk6sDU7.mjs +140 -0
  24. package/fesm2022/tinya-code-gas-admin-ui-display-badge-fwk6sDU7.mjs.map +1 -0
  25. package/fesm2022/tinya-code-gas-admin-ui-display-date-D7zLiDiG.mjs +119 -0
  26. package/fesm2022/tinya-code-gas-admin-ui-display-date-D7zLiDiG.mjs.map +1 -0
  27. package/fesm2022/tinya-code-gas-admin-ui-display-file-BiIhqvQC.mjs +125 -0
  28. package/fesm2022/tinya-code-gas-admin-ui-display-file-BiIhqvQC.mjs.map +1 -0
  29. package/fesm2022/tinya-code-gas-admin-ui-display-image-C1dhnZi1.mjs +152 -0
  30. package/fesm2022/tinya-code-gas-admin-ui-display-image-C1dhnZi1.mjs.map +1 -0
  31. package/fesm2022/tinya-code-gas-admin-ui-display-list-DOEuUbGe.mjs +211 -0
  32. package/fesm2022/tinya-code-gas-admin-ui-display-list-DOEuUbGe.mjs.map +1 -0
  33. package/fesm2022/tinya-code-gas-admin-ui-display-number-DDMzr4Hn.mjs +116 -0
  34. package/fesm2022/tinya-code-gas-admin-ui-display-number-DDMzr4Hn.mjs.map +1 -0
  35. package/fesm2022/tinya-code-gas-admin-ui-display-text-B8CdVNpv.mjs +111 -0
  36. package/fesm2022/tinya-code-gas-admin-ui-display-text-B8CdVNpv.mjs.map +1 -0
  37. package/fesm2022/tinya-code-gas-admin-ui-dropdown-C3blvmE1.mjs +122 -0
  38. package/fesm2022/tinya-code-gas-admin-ui-dropdown-C3blvmE1.mjs.map +1 -0
  39. package/fesm2022/tinya-code-gas-admin-ui-email-CT_O5ihA.mjs +107 -0
  40. package/fesm2022/tinya-code-gas-admin-ui-email-CT_O5ihA.mjs.map +1 -0
  41. package/fesm2022/tinya-code-gas-admin-ui-empty-state-BMc0QtoT.mjs +109 -0
  42. package/fesm2022/tinya-code-gas-admin-ui-empty-state-BMc0QtoT.mjs.map +1 -0
  43. package/fesm2022/tinya-code-gas-admin-ui-error-mapper.service-DY5y1FjE.mjs +45 -0
  44. package/fesm2022/tinya-code-gas-admin-ui-error-mapper.service-DY5y1FjE.mjs.map +1 -0
  45. package/fesm2022/tinya-code-gas-admin-ui-field-aria-BD1Wiggc.mjs +50 -0
  46. package/fesm2022/tinya-code-gas-admin-ui-field-aria-BD1Wiggc.mjs.map +1 -0
  47. package/fesm2022/tinya-code-gas-admin-ui-field-node-CIK6KNN_.mjs +24 -0
  48. package/fesm2022/tinya-code-gas-admin-ui-field-node-CIK6KNN_.mjs.map +1 -0
  49. package/fesm2022/tinya-code-gas-admin-ui-field-path-B4-FTBGZ.mjs +27 -0
  50. package/fesm2022/tinya-code-gas-admin-ui-field-path-B4-FTBGZ.mjs.map +1 -0
  51. package/fesm2022/tinya-code-gas-admin-ui-file-Dmxioog7.mjs +408 -0
  52. package/fesm2022/tinya-code-gas-admin-ui-file-Dmxioog7.mjs.map +1 -0
  53. package/fesm2022/tinya-code-gas-admin-ui-form-view-CEe7aZ4m.mjs +4516 -0
  54. package/fesm2022/tinya-code-gas-admin-ui-form-view-CEe7aZ4m.mjs.map +1 -0
  55. package/fesm2022/tinya-code-gas-admin-ui-format-badge-CK8ct5Wk.mjs +82 -0
  56. package/fesm2022/tinya-code-gas-admin-ui-format-badge-CK8ct5Wk.mjs.map +1 -0
  57. package/fesm2022/tinya-code-gas-admin-ui-format-boolean-ClzezZz-.mjs +105 -0
  58. package/fesm2022/tinya-code-gas-admin-ui-format-boolean-ClzezZz-.mjs.map +1 -0
  59. package/fesm2022/tinya-code-gas-admin-ui-format-currency-CVmniKml.mjs +65 -0
  60. package/fesm2022/tinya-code-gas-admin-ui-format-currency-CVmniKml.mjs.map +1 -0
  61. package/fesm2022/tinya-code-gas-admin-ui-format-date-Dr1YsDd_.mjs +65 -0
  62. package/fesm2022/tinya-code-gas-admin-ui-format-date-Dr1YsDd_.mjs.map +1 -0
  63. package/fesm2022/tinya-code-gas-admin-ui-format-number-CWCjpI4n.mjs +65 -0
  64. package/fesm2022/tinya-code-gas-admin-ui-format-number-CWCjpI4n.mjs.map +1 -0
  65. package/fesm2022/tinya-code-gas-admin-ui-format-text-DBOgJkH0.mjs +59 -0
  66. package/fesm2022/tinya-code-gas-admin-ui-format-text-DBOgJkH0.mjs.map +1 -0
  67. package/fesm2022/tinya-code-gas-admin-ui-format-thumbnail-PCRvxWKt.mjs +94 -0
  68. package/fesm2022/tinya-code-gas-admin-ui-format-thumbnail-PCRvxWKt.mjs.map +1 -0
  69. package/fesm2022/tinya-code-gas-admin-ui-gap-sorting-iTdy_DYG.mjs +72 -0
  70. package/fesm2022/tinya-code-gas-admin-ui-gap-sorting-iTdy_DYG.mjs.map +1 -0
  71. package/fesm2022/tinya-code-gas-admin-ui-group-h7-O8tt4.mjs +238 -0
  72. package/fesm2022/tinya-code-gas-admin-ui-group-h7-O8tt4.mjs.map +1 -0
  73. package/fesm2022/tinya-code-gas-admin-ui-image-7UhJ7nqI.mjs +416 -0
  74. package/fesm2022/tinya-code-gas-admin-ui-image-7UhJ7nqI.mjs.map +1 -0
  75. package/fesm2022/tinya-code-gas-admin-ui-kanban-view-Dl0Ol5Kb.mjs +439 -0
  76. package/fesm2022/tinya-code-gas-admin-ui-kanban-view-Dl0Ol5Kb.mjs.map +1 -0
  77. package/fesm2022/tinya-code-gas-admin-ui-key-value-nVdgg5at.mjs +513 -0
  78. package/fesm2022/tinya-code-gas-admin-ui-key-value-nVdgg5at.mjs.map +1 -0
  79. package/fesm2022/tinya-code-gas-admin-ui-list-DTtCYS4d.mjs +637 -0
  80. package/fesm2022/tinya-code-gas-admin-ui-list-DTtCYS4d.mjs.map +1 -0
  81. package/fesm2022/tinya-code-gas-admin-ui-metric-card-DnGMQL8l.mjs +127 -0
  82. package/fesm2022/tinya-code-gas-admin-ui-metric-card-DnGMQL8l.mjs.map +1 -0
  83. package/fesm2022/tinya-code-gas-admin-ui-multiselect-PB28e-jl.mjs +251 -0
  84. package/fesm2022/tinya-code-gas-admin-ui-multiselect-PB28e-jl.mjs.map +1 -0
  85. package/fesm2022/tinya-code-gas-admin-ui-notification.service-LjSULER6.mjs +66 -0
  86. package/fesm2022/tinya-code-gas-admin-ui-notification.service-LjSULER6.mjs.map +1 -0
  87. package/fesm2022/tinya-code-gas-admin-ui-number-JNr7glc8.mjs +114 -0
  88. package/fesm2022/tinya-code-gas-admin-ui-number-JNr7glc8.mjs.map +1 -0
  89. package/fesm2022/tinya-code-gas-admin-ui-phone-jR417hPf.mjs +116 -0
  90. package/fesm2022/tinya-code-gas-admin-ui-phone-jR417hPf.mjs.map +1 -0
  91. package/fesm2022/tinya-code-gas-admin-ui-quick-actions-DCQ6emmF.mjs +235 -0
  92. package/fesm2022/tinya-code-gas-admin-ui-quick-actions-DCQ6emmF.mjs.map +1 -0
  93. package/fesm2022/tinya-code-gas-admin-ui-radio-mode-DxvN07DY.mjs +8 -0
  94. package/fesm2022/tinya-code-gas-admin-ui-radio-mode-DxvN07DY.mjs.map +1 -0
  95. package/fesm2022/tinya-code-gas-admin-ui-readonly-text-BL7-4qie.mjs +124 -0
  96. package/fesm2022/tinya-code-gas-admin-ui-readonly-text-BL7-4qie.mjs.map +1 -0
  97. package/fesm2022/tinya-code-gas-admin-ui-record-list-BiZfnd_7.mjs +249 -0
  98. package/fesm2022/tinya-code-gas-admin-ui-record-list-BiZfnd_7.mjs.map +1 -0
  99. package/fesm2022/tinya-code-gas-admin-ui-relation-drawer.service-B0kT8EiW.mjs +39 -0
  100. package/fesm2022/tinya-code-gas-admin-ui-relation-drawer.service-B0kT8EiW.mjs.map +1 -0
  101. package/fesm2022/tinya-code-gas-admin-ui-relation-ykpJ3PYl.mjs +309 -0
  102. package/fesm2022/tinya-code-gas-admin-ui-relation-ykpJ3PYl.mjs.map +1 -0
  103. package/fesm2022/tinya-code-gas-admin-ui-render-registry-B88Pme-y.mjs +576 -0
  104. package/fesm2022/tinya-code-gas-admin-ui-render-registry-B88Pme-y.mjs.map +1 -0
  105. package/fesm2022/tinya-code-gas-admin-ui-rich-select-CIHaOrbU.mjs +161 -0
  106. package/fesm2022/tinya-code-gas-admin-ui-rich-select-CIHaOrbU.mjs.map +1 -0
  107. package/fesm2022/tinya-code-gas-admin-ui-rolldown-runtime-DpJrqANO.mjs +11 -0
  108. package/fesm2022/tinya-code-gas-admin-ui-schema-catalog-D0Pk0wAN.mjs +685 -0
  109. package/fesm2022/tinya-code-gas-admin-ui-schema-catalog-D0Pk0wAN.mjs.map +1 -0
  110. package/fesm2022/tinya-code-gas-admin-ui-seed-DkVZbxPP.mjs +80 -0
  111. package/fesm2022/tinya-code-gas-admin-ui-seed-DkVZbxPP.mjs.map +1 -0
  112. package/fesm2022/tinya-code-gas-admin-ui-select-CDwX9yNO.mjs +296 -0
  113. package/fesm2022/tinya-code-gas-admin-ui-select-CDwX9yNO.mjs.map +1 -0
  114. package/fesm2022/tinya-code-gas-admin-ui-skeleton-DnWK0tcZ.mjs +119 -0
  115. package/fesm2022/tinya-code-gas-admin-ui-skeleton-DnWK0tcZ.mjs.map +1 -0
  116. package/fesm2022/tinya-code-gas-admin-ui-slug-DU58cbiY.mjs +158 -0
  117. package/fesm2022/tinya-code-gas-admin-ui-slug-DU58cbiY.mjs.map +1 -0
  118. package/fesm2022/tinya-code-gas-admin-ui-source-options-B9R4mw8-.mjs +26 -0
  119. package/fesm2022/tinya-code-gas-admin-ui-source-options-B9R4mw8-.mjs.map +1 -0
  120. package/fesm2022/tinya-code-gas-admin-ui-status-progress-DmeG8htE.mjs +205 -0
  121. package/fesm2022/tinya-code-gas-admin-ui-status-progress-DmeG8htE.mjs.map +1 -0
  122. package/fesm2022/tinya-code-gas-admin-ui-string-list-gleasqzD.mjs +432 -0
  123. package/fesm2022/tinya-code-gas-admin-ui-string-list-gleasqzD.mjs.map +1 -0
  124. package/fesm2022/tinya-code-gas-admin-ui-template-interpolator-ewv7CkNX.mjs +36 -0
  125. package/fesm2022/tinya-code-gas-admin-ui-template-interpolator-ewv7CkNX.mjs.map +1 -0
  126. package/fesm2022/tinya-code-gas-admin-ui-text-CUPmL7W_.mjs +107 -0
  127. package/fesm2022/tinya-code-gas-admin-ui-text-CUPmL7W_.mjs.map +1 -0
  128. package/fesm2022/tinya-code-gas-admin-ui-textarea-CGLSIW13.mjs +223 -0
  129. package/fesm2022/tinya-code-gas-admin-ui-textarea-CGLSIW13.mjs.map +1 -0
  130. package/fesm2022/tinya-code-gas-admin-ui-time-BcHzsTWf.mjs +107 -0
  131. package/fesm2022/tinya-code-gas-admin-ui-time-BcHzsTWf.mjs.map +1 -0
  132. package/fesm2022/tinya-code-gas-admin-ui-upload.service-CZPEPCeN.mjs +60 -0
  133. package/fesm2022/tinya-code-gas-admin-ui-upload.service-CZPEPCeN.mjs.map +1 -0
  134. package/fesm2022/tinya-code-gas-admin-ui-url-GO4LZi__.mjs +141 -0
  135. package/fesm2022/tinya-code-gas-admin-ui-url-GO4LZi__.mjs.map +1 -0
  136. package/fesm2022/tinya-code-gas-admin-ui.mjs +4738 -0
  137. package/fesm2022/tinya-code-gas-admin-ui.mjs.map +1 -0
  138. package/package.json +15 -3
  139. package/types/tinya-code-gas-admin-ui.d.ts +3634 -0
  140. package/ng-package.json +0 -7
  141. package/src/lib/actions/action-registry.spec.ts +0 -52
  142. package/src/lib/actions/action-registry.ts +0 -126
  143. package/src/lib/actions/action-service.spec.ts +0 -215
  144. package/src/lib/actions/action-service.ts +0 -87
  145. package/src/lib/actions/descriptors.spec.ts +0 -155
  146. package/src/lib/actions/descriptors.ts +0 -154
  147. package/src/lib/columns/column-cell.ts +0 -33
  148. package/src/lib/columns/column-defaults.spec.ts +0 -99
  149. package/src/lib/columns/column-defaults.ts +0 -84
  150. package/src/lib/columns/column-format.spec.ts +0 -274
  151. package/src/lib/columns/descriptors.ts +0 -113
  152. package/src/lib/columns/format-badge.ts +0 -51
  153. package/src/lib/columns/format-boolean.ts +0 -58
  154. package/src/lib/columns/format-currency.ts +0 -30
  155. package/src/lib/columns/format-date.ts +0 -35
  156. package/src/lib/columns/format-number.ts +0 -32
  157. package/src/lib/columns/format-text.ts +0 -23
  158. package/src/lib/columns/format-thumbnail.ts +0 -63
  159. package/src/lib/core/adapters/firebase-auth.adapter.ts +0 -6
  160. package/src/lib/core/guards/auth.guard.spec.ts +0 -88
  161. package/src/lib/core/guards/auth.guard.ts +0 -48
  162. package/src/lib/core/guards/unsaved-changes.guard.ts +0 -16
  163. package/src/lib/core/interceptors/api-error.interceptor.spec.ts +0 -182
  164. package/src/lib/core/interceptors/api-error.interceptor.ts +0 -87
  165. package/src/lib/core/models/api.model.ts +0 -104
  166. package/src/lib/core/models/schema-descriptions.ts +0 -118
  167. package/src/lib/core/models/schema.model.ts +0 -1097
  168. package/src/lib/core/registry/composition-validator.spec.ts +0 -358
  169. package/src/lib/core/registry/composition-validator.ts +0 -270
  170. package/src/lib/core/registry/field-validators.spec.ts +0 -199
  171. package/src/lib/core/registry/field-validators.ts +0 -440
  172. package/src/lib/core/registry/render-host.spec.ts +0 -208
  173. package/src/lib/core/registry/render-host.ts +0 -646
  174. package/src/lib/core/registry/render-registry.spec.ts +0 -96
  175. package/src/lib/core/registry/render-registry.ts +0 -312
  176. package/src/lib/core/registry/schema-catalog.ts +0 -156
  177. package/src/lib/core/services/api.service.spec.ts +0 -1142
  178. package/src/lib/core/services/api.service.ts +0 -371
  179. package/src/lib/core/services/auth.service.spec.ts +0 -653
  180. package/src/lib/core/services/auth.service.ts +0 -503
  181. package/src/lib/core/services/config.service.spec.ts +0 -121
  182. package/src/lib/core/services/config.service.ts +0 -119
  183. package/src/lib/core/services/data-source.service.spec.ts +0 -179
  184. package/src/lib/core/services/data-source.service.ts +0 -142
  185. package/src/lib/core/services/error-mapper.service.spec.ts +0 -50
  186. package/src/lib/core/services/error-mapper.service.ts +0 -49
  187. package/src/lib/core/services/notification.service.ts +0 -73
  188. package/src/lib/core/services/relation-drawer.service.ts +0 -41
  189. package/src/lib/core/services/theme.service.spec.ts +0 -106
  190. package/src/lib/core/services/theme.service.ts +0 -86
  191. package/src/lib/core/services/transport.spec.ts +0 -198
  192. package/src/lib/core/services/transport.ts +0 -164
  193. package/src/lib/core/services/upload.service.ts +0 -81
  194. package/src/lib/core/testing/categories.schema.ts +0 -125
  195. package/src/lib/core/testing/orders.schema.ts +0 -372
  196. package/src/lib/core/testing/products.schema.ts +0 -258
  197. package/src/lib/core/testing/site.schema.ts +0 -195
  198. package/src/lib/core/testing/test-catalog.ts +0 -21
  199. package/src/lib/core/tokens.ts +0 -112
  200. package/src/lib/core/utils/clipboard.spec.ts +0 -55
  201. package/src/lib/core/utils/clipboard.ts +0 -90
  202. package/src/lib/core/utils/context-resolver.spec.ts +0 -95
  203. package/src/lib/core/utils/context-resolver.ts +0 -101
  204. package/src/lib/core/utils/response-unwrapper.spec.ts +0 -70
  205. package/src/lib/core/utils/response-unwrapper.ts +0 -103
  206. package/src/lib/core/utils/source-options.spec.ts +0 -96
  207. package/src/lib/core/utils/source-options.ts +0 -54
  208. package/src/lib/core/utils/template-interpolator.spec.ts +0 -59
  209. package/src/lib/core/utils/template-interpolator.ts +0 -56
  210. package/src/lib/core/view-navigation/view-navigation.service.spec.ts +0 -207
  211. package/src/lib/core/view-navigation/view-navigation.service.ts +0 -132
  212. package/src/lib/fields/boolean/boolean.descriptor.ts +0 -11
  213. package/src/lib/fields/boolean/boolean.ts +0 -40
  214. package/src/lib/fields/color/color.descriptor.ts +0 -12
  215. package/src/lib/fields/color/color.ts +0 -34
  216. package/src/lib/fields/currency/currency.descriptor.ts +0 -12
  217. package/src/lib/fields/currency/currency.ts +0 -67
  218. package/src/lib/fields/date/date.descriptor.ts +0 -12
  219. package/src/lib/fields/date/date.ts +0 -36
  220. package/src/lib/fields/descriptors.ts +0 -122
  221. package/src/lib/fields/display/display-badge.ts +0 -65
  222. package/src/lib/fields/display/display-date.ts +0 -39
  223. package/src/lib/fields/display/display-defaults.ts +0 -84
  224. package/src/lib/fields/display/display-file.ts +0 -41
  225. package/src/lib/fields/display/display-image.ts +0 -54
  226. package/src/lib/fields/display/display-list.ts +0 -107
  227. package/src/lib/fields/display/display-number.ts +0 -41
  228. package/src/lib/fields/display/display-text.ts +0 -42
  229. package/src/lib/fields/display/display.spec.ts +0 -242
  230. package/src/lib/fields/dropdown/dropdown.descriptor.ts +0 -12
  231. package/src/lib/fields/dropdown/dropdown.ts +0 -45
  232. package/src/lib/fields/email/email.descriptor.ts +0 -12
  233. package/src/lib/fields/email/email.ts +0 -28
  234. package/src/lib/fields/field-aria.ts +0 -60
  235. package/src/lib/fields/field-host/field-host.spec.ts +0 -118
  236. package/src/lib/fields/field-host/field-host.ts +0 -47
  237. package/src/lib/fields/field-node.ts +0 -88
  238. package/src/lib/fields/file/file.descriptor.ts +0 -14
  239. package/src/lib/fields/file/file.spec.ts +0 -134
  240. package/src/lib/fields/file/file.ts +0 -281
  241. package/src/lib/fields/group/group.descriptor.ts +0 -21
  242. package/src/lib/fields/group/group.ts +0 -110
  243. package/src/lib/fields/image/image.descriptor.ts +0 -12
  244. package/src/lib/fields/image/image.spec.ts +0 -117
  245. package/src/lib/fields/image/image.ts +0 -281
  246. package/src/lib/fields/key-value/key-value.descriptor.ts +0 -11
  247. package/src/lib/fields/key-value/key-value.ts +0 -283
  248. package/src/lib/fields/list/list.descriptor.ts +0 -21
  249. package/src/lib/fields/list/list.ts +0 -376
  250. package/src/lib/fields/multiselect/multiselect.descriptor.ts +0 -11
  251. package/src/lib/fields/multiselect/multiselect.spec.ts +0 -142
  252. package/src/lib/fields/multiselect/multiselect.ts +0 -152
  253. package/src/lib/fields/number/number.descriptor.ts +0 -12
  254. package/src/lib/fields/number/number.ts +0 -40
  255. package/src/lib/fields/phone/phone.descriptor.ts +0 -12
  256. package/src/lib/fields/phone/phone.ts +0 -46
  257. package/src/lib/fields/readonly-text/readonly-text.descriptor.ts +0 -12
  258. package/src/lib/fields/readonly-text/readonly-text.ts +0 -58
  259. package/src/lib/fields/relation/relation.descriptor.ts +0 -12
  260. package/src/lib/fields/relation/relation.spec.ts +0 -193
  261. package/src/lib/fields/relation/relation.ts +0 -224
  262. package/src/lib/fields/rich-select/rich-select.descriptor.ts +0 -12
  263. package/src/lib/fields/rich-select/rich-select.ts +0 -175
  264. package/src/lib/fields/select/radio-mode.ts +0 -25
  265. package/src/lib/fields/select/select.descriptor.ts +0 -12
  266. package/src/lib/fields/select/select.spec.ts +0 -162
  267. package/src/lib/fields/select/select.ts +0 -210
  268. package/src/lib/fields/slug/slug.descriptor.ts +0 -19
  269. package/src/lib/fields/slug/slug.ts +0 -67
  270. package/src/lib/fields/string-list/string-list.descriptor.ts +0 -11
  271. package/src/lib/fields/string-list/string-list.ts +0 -222
  272. package/src/lib/fields/text/text.descriptor.ts +0 -12
  273. package/src/lib/fields/text/text.ts +0 -35
  274. package/src/lib/fields/textarea/textarea.descriptor.ts +0 -12
  275. package/src/lib/fields/textarea/textarea.spec.ts +0 -117
  276. package/src/lib/fields/textarea/textarea.ts +0 -126
  277. package/src/lib/fields/time/time.descriptor.ts +0 -12
  278. package/src/lib/fields/time/time.ts +0 -28
  279. package/src/lib/fields/unknown-render/unknown-render.ts +0 -49
  280. package/src/lib/fields/url/url.descriptor.ts +0 -12
  281. package/src/lib/fields/url/url.ts +0 -53
  282. package/src/lib/gas-admin-ui.spec.ts +0 -21
  283. package/src/lib/gas-admin-ui.ts +0 -9
  284. package/src/lib/layouts/descriptors.ts +0 -9
  285. package/src/lib/pages/dashboard/dashboard.ts +0 -167
  286. package/src/lib/pages/detail-view/detail-view.spec.ts +0 -333
  287. package/src/lib/pages/detail-view/detail-view.ts +0 -570
  288. package/src/lib/pages/drawer-form/drawer-form.ts +0 -228
  289. package/src/lib/pages/form-view/form-model.ts +0 -88
  290. package/src/lib/pages/form-view/form-schema.ts +0 -255
  291. package/src/lib/pages/form-view/form-view.ts +0 -1622
  292. package/src/lib/pages/list-view/list-view.formats.spec.ts +0 -214
  293. package/src/lib/pages/list-view/list-view.ts +0 -882
  294. package/src/lib/pages/login/login.spec.ts +0 -109
  295. package/src/lib/pages/login/login.ts +0 -227
  296. package/src/lib/pages/resource-page/resource-page.ts +0 -234
  297. package/src/lib/shared/components/badge/badge.ts +0 -35
  298. package/src/lib/shared/components/button/button.ts +0 -65
  299. package/src/lib/shared/components/confirm-dialog/confirm-dialog.ts +0 -48
  300. package/src/lib/shared/components/copy-button/copy-button.ts +0 -56
  301. package/src/lib/shared/components/copy-share/copy-share.spec.ts +0 -122
  302. package/src/lib/shared/components/copy-share/copy-share.ts +0 -107
  303. package/src/lib/shared/components/drawer/drawer.spec.ts +0 -127
  304. package/src/lib/shared/components/drawer/drawer.ts +0 -172
  305. package/src/lib/shared/components/empty-state/empty-state.ts +0 -30
  306. package/src/lib/shared/components/modal/modal.spec.ts +0 -149
  307. package/src/lib/shared/components/modal/modal.ts +0 -145
  308. package/src/lib/shared/components/post-create-modal/post-create-modal.spec.ts +0 -165
  309. package/src/lib/shared/components/post-create-modal/post-create-modal.ts +0 -159
  310. package/src/lib/shared/components/skeleton/skeleton.ts +0 -31
  311. package/src/lib/shared/components/toast/toast.ts +0 -58
  312. package/src/lib/shared/utils/col-span.ts +0 -25
  313. package/src/lib/shared/utils/condition-evaluator.ts +0 -53
  314. package/src/lib/shared/utils/depends-on.ts +0 -48
  315. package/src/lib/shared/utils/dialog-focus.ts +0 -28
  316. package/src/lib/shared/utils/field-path.ts +0 -49
  317. package/src/lib/shared/utils/gap-sorting.ts +0 -144
  318. package/src/lib/shared/utils/seed.spec.ts +0 -64
  319. package/src/lib/shared/utils/seed.ts +0 -149
  320. package/src/lib/shared/utils/slugify.ts +0 -13
  321. package/src/lib/shared/utils/validators.ts +0 -41
  322. package/src/lib/shell/layout/layout.ts +0 -63
  323. package/src/lib/shell/shell.routes.spec.ts +0 -59
  324. package/src/lib/shell/shell.routes.ts +0 -122
  325. package/src/lib/shell/sidebar/sidebar.ts +0 -139
  326. package/src/lib/shell/theme-toggle/theme-toggle.ts +0 -74
  327. package/src/lib/views/calendar-view/calendar-model.ts +0 -426
  328. package/src/lib/views/calendar-view/calendar-view.spec.ts +0 -557
  329. package/src/lib/views/calendar-view/calendar-view.ts +0 -568
  330. package/src/lib/views/composer/composer-host.spec.ts +0 -301
  331. package/src/lib/views/composer/composer-host.ts +0 -248
  332. package/src/lib/views/composer/composer-imports.spec.ts +0 -54
  333. package/src/lib/views/composer/view-context.spec.ts +0 -117
  334. package/src/lib/views/composer/view-context.ts +0 -109
  335. package/src/lib/views/composer/view-slot.spec.ts +0 -130
  336. package/src/lib/views/composer/view-slot.ts +0 -178
  337. package/src/lib/views/descriptors.ts +0 -46
  338. package/src/lib/views/kanban-view/kanban-model.ts +0 -173
  339. package/src/lib/views/kanban-view/kanban-view.spec.ts +0 -540
  340. package/src/lib/views/kanban-view/kanban-view.ts +0 -327
  341. package/src/lib/widgets/bar-chart/bar-chart.descriptor.ts +0 -17
  342. package/src/lib/widgets/bar-chart/bar-chart.spec.ts +0 -66
  343. package/src/lib/widgets/bar-chart/bar-chart.ts +0 -76
  344. package/src/lib/widgets/chart-line/chart-line.descriptor.ts +0 -17
  345. package/src/lib/widgets/chart-line/chart-line.spec.ts +0 -113
  346. package/src/lib/widgets/chart-line/chart-line.ts +0 -219
  347. package/src/lib/widgets/descriptors.ts +0 -29
  348. package/src/lib/widgets/metric-card/metric-card.descriptor.ts +0 -41
  349. package/src/lib/widgets/metric-card/metric-card.spec.ts +0 -52
  350. package/src/lib/widgets/metric-card/metric-card.ts +0 -48
  351. package/src/lib/widgets/quick-actions/quick-actions.descriptor.ts +0 -13
  352. package/src/lib/widgets/quick-actions/quick-actions.spec.ts +0 -61
  353. package/src/lib/widgets/quick-actions/quick-actions.ts +0 -100
  354. package/src/lib/widgets/record-list/record-list.descriptor.ts +0 -16
  355. package/src/lib/widgets/record-list/record-list.spec.ts +0 -79
  356. package/src/lib/widgets/record-list/record-list.ts +0 -115
  357. package/src/lib/widgets/series.spec.ts +0 -42
  358. package/src/lib/widgets/series.ts +0 -56
  359. package/src/lib/widgets/status-progress/status-progress.descriptor.ts +0 -19
  360. package/src/lib/widgets/status-progress/status-progress.spec.ts +0 -63
  361. package/src/lib/widgets/status-progress/status-progress.ts +0 -104
  362. package/src/lib/widgets/widget-host/widget-host.spec.ts +0 -179
  363. package/src/lib/widgets/widget-host/widget-host.ts +0 -99
  364. package/src/public-api.ts +0 -104
  365. package/tinya-code-gas-admin-ui-0.2.0-alpha.15.tgz +0 -0
  366. package/tsconfig.lib.json +0 -13
  367. package/tsconfig.lib.prod.json +0 -11
  368. package/tsconfig.spec.json +0 -10
@@ -1,109 +0,0 @@
1
- import { ComponentFixture, TestBed } from '@angular/core/testing';
2
- import { provideHttpClient } from '@angular/common/http';
3
- import { provideHttpClientTesting } from '@angular/common/http/testing';
4
- import { provideRouter, Router } from '@angular/router';
5
- import { of } from 'rxjs';
6
- import { vi } from 'vitest';
7
-
8
- import type { AuthProvider } from '../../core/models/schema.model';
9
- import { setSchemaCatalog } from '../../core/registry/schema-catalog';
10
- import { AuthService } from '../../core/services/auth.service';
11
- import { API_URL } from '../../core/tokens';
12
- import { Login } from './login';
13
-
14
- describe('Login component', () => {
15
- let fixture: ComponentFixture<Login>;
16
- let component: Login;
17
- let auth: AuthService;
18
- let router: Router;
19
-
20
- function setup(providers: AuthProvider[], hasFirebase = false) {
21
- TestBed.resetTestingModule();
22
- setSchemaCatalog([], {
23
- providers,
24
- loginFields: [
25
- { key: 'email', type: 'email', required: true },
26
- { key: 'password', type: 'password', required: true },
27
- ],
28
- redirectAfterLogin: '/dashboard',
29
- rememberMe: false,
30
- ...(hasFirebase ? { firebase: { exchangeEndpoint: '/auth/firebase' } } : {}),
31
- });
32
-
33
- TestBed.configureTestingModule({
34
- imports: [Login],
35
- providers: [
36
- provideHttpClient(),
37
- provideHttpClientTesting(),
38
- provideRouter([]),
39
- { provide: API_URL, useValue: '/api' },
40
- ],
41
- });
42
-
43
- auth = TestBed.inject(AuthService);
44
- router = TestBed.inject(Router);
45
- vi.spyOn(router, 'navigateByUrl').mockResolvedValue(true);
46
- fixture = TestBed.createComponent(Login);
47
- component = fixture.componentInstance;
48
- fixture.detectChanges();
49
- }
50
-
51
- afterEach(() => {
52
- TestBed.resetTestingModule();
53
- });
54
-
55
- it('providers = [password], sin firebase: no muestra botón de Google y submit() llama a auth.login()', () => {
56
- setup(['password'], false);
57
- const compiled = fixture.nativeElement as HTMLElement;
58
- expect(compiled.querySelector('#btn-login-google')).toBeNull();
59
-
60
- const loginSpy = vi.spyOn(auth, 'login').mockReturnValue(
61
- of({ id: 'u_1', email: 'test@example.com', role: 'admin' }),
62
- );
63
-
64
- component.setValue('email', 'test@example.com');
65
- component.setValue('password', 'secret');
66
-
67
- const form = compiled.querySelector('form')!;
68
- form.dispatchEvent(new Event('submit'));
69
-
70
- expect(loginSpy).toHaveBeenCalledWith('test@example.com', 'secret');
71
- expect(router.navigateByUrl).toHaveBeenCalledWith('/dashboard');
72
- });
73
-
74
- it('providers = [google, password]: muestra botón de Google en el DOM', () => {
75
- setup(['google', 'password'], false);
76
- const compiled = fixture.nativeElement as HTMLElement;
77
- expect(compiled.querySelector('#btn-login-google')).not.toBeNull();
78
- expect(compiled.querySelector('#btn-login-google')?.textContent).toContain('Continuar con Google');
79
- });
80
-
81
- it('loginWithGoogle() llama a auth.loginWithGoogle() y navega al tener éxito', () => {
82
- setup(['google', 'password'], false);
83
- const googleSpy = vi.spyOn(auth, 'loginWithGoogle').mockReturnValue(
84
- of({ id: 'u_google', email: 'google@test.com', role: 'admin' }),
85
- );
86
-
87
- component.loginWithGoogle();
88
-
89
- expect(googleSpy).toHaveBeenCalled();
90
- expect(router.navigateByUrl).toHaveBeenCalledWith('/dashboard');
91
- });
92
-
93
- it('providers = [password] con firebase: submit() delega a loginWithFirebasePassword()', () => {
94
- setup(['password'], true);
95
- const fbPwSpy = vi.spyOn(auth, 'loginWithFirebasePassword').mockReturnValue(
96
- of({ id: 'u_pw', email: 'pw@test.com', role: 'admin' }),
97
- );
98
-
99
- component.setValue('email', 'pw@test.com');
100
- component.setValue('password', 'secret');
101
-
102
- const compiled = fixture.nativeElement as HTMLElement;
103
- const form = compiled.querySelector('form')!;
104
- form.dispatchEvent(new Event('submit'));
105
-
106
- expect(fbPwSpy).toHaveBeenCalledWith('pw@test.com', 'secret');
107
- expect(router.navigateByUrl).toHaveBeenCalledWith('/dashboard');
108
- });
109
- });
@@ -1,227 +0,0 @@
1
- import { Component, inject, signal } from '@angular/core';
2
- import { Router } from '@angular/router';
3
-
4
- import { ApiError } from '../../core/models/api.model';
5
- import { AuthService, LOGIN_PATH } from '../../core/services/auth.service';
6
- import { Button } from '../../shared/components/button/button';
7
- import { getAuth } from '../../core/registry/schema-catalog';
8
-
9
- /** Copy de las etiquetas — vive acá, no en el schema: una sola pantalla. */
10
- const LABELS: Record<string, string> = {
11
- email: 'Correo electrónico',
12
- password: 'Contraseña',
13
- };
14
-
15
- const FIELD_BASE =
16
- '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';
17
-
18
- /**
19
- * Login (auth.md §5.1).
20
- *
21
- * **Pintada a partir de `authSchema.loginFields`**: si mañana cambian los
22
- * campos, éste componente no se toca — sólo se mueven claves y etiquetas.
23
- * No usa `FieldHost` porque el tipo `password` todavía no existe en
24
- * `FieldSchema` (es pieza nueva de Fase 3, R3.3); el `@switch` de abajo es
25
- * exactamente el reemplazo que R3.x hará por el registro de render.
26
- *
27
- * **No hay casilla «recordarme»**: D10-3 negó `localStorage` (§9.3) y el
28
- * token muere con la pestaña. Poner un checkbox sería prometer que la
29
- * sesión sobrevive a cerrar el navegador.
30
- */
31
- @Component({
32
- selector: 'app-login',
33
- imports: [Button],
34
- template: `
35
- <main class="flex min-h-[100svh] items-center justify-center bg-surface px-4">
36
- <div
37
- class="w-full max-w-sm rounded-xl border border-neutral/20 bg-surface-raised p-8 shadow-sm"
38
- >
39
- <h1 class="font-display text-lg font-semibold">Admin</h1>
40
- <p class="mt-1 text-sm text-neutral">Inicia sesión para continuar</p>
41
-
42
- @if (providers.includes('google')) {
43
- <button
44
- type="button"
45
- id="btn-login-google"
46
- app-button
47
- variant="outline"
48
- [loading]="providerLoading() === 'google'"
49
- (click)="loginWithGoogle()"
50
- class="mt-6 w-full"
51
- >
52
- Continuar con Google
53
- </button>
54
- <div class="my-4 flex items-center gap-3">
55
- <hr class="flex-1 border-neutral/20" />
56
- <span class="text-xs text-neutral/50">o</span>
57
- <hr class="flex-1 border-neutral/20" />
58
- </div>
59
- }
60
-
61
- <form
62
- [class]="providers.includes('google') ? 'space-y-4' : 'mt-6 space-y-4'"
63
- (submit)="submit($event)"
64
- novalidate
65
- >
66
- @for (field of schema.loginFields; track field.key) {
67
- <div>
68
- <label [attr.for]="field.key" class="block text-sm font-medium text-neutral">
69
- {{ label(field.key) }}
70
- </label>
71
-
72
- @switch (field.type) {
73
- @case ('email') {
74
- <input
75
- [attr.id]="field.key"
76
- [attr.name]="field.key"
77
- type="email"
78
- autocomplete="email"
79
- inputmode="email"
80
- [attr.required]="field.required ? '' : null"
81
- [value]="value(field.key)"
82
- (input)="setValue(field.key, $any($event.target).value)"
83
- class="${FIELD_BASE}"
84
- />
85
- }
86
- @default {
87
- <input
88
- [attr.id]="field.key"
89
- [attr.name]="field.key"
90
- type="password"
91
- autocomplete="current-password"
92
- [attr.required]="field.required ? '' : null"
93
- [value]="value(field.key)"
94
- (input)="setValue(field.key, $any($event.target).value)"
95
- class="${FIELD_BASE}"
96
- />
97
- }
98
- }
99
- </div>
100
- }
101
-
102
- @if (error(); as message) {
103
- <p role="alert" class="rounded-lg bg-danger/10 px-3 py-2 text-sm text-danger">
104
- {{ message }}
105
- </p>
106
- }
107
-
108
- <button app-button type="submit" [loading]="loading()" class="w-full">
109
- Iniciar sesión
110
- </button>
111
- </form>
112
- </div>
113
- </main>
114
- `,
115
- })
116
- export class Login {
117
- private readonly auth = inject(AuthService);
118
- private readonly router = inject(Router);
119
-
120
- // Se resuelve en runtime: el catálogo lo fija `provideGasAdmin` en el bootstrap.
121
- readonly schema = getAuth()!;
122
- readonly providers = this.schema.providers;
123
- readonly error = signal<string | null>(null);
124
- readonly loading = signal(false);
125
- readonly providerLoading = signal<string | null>(null);
126
-
127
- /** Valores por clave: el set lo pinta el `@for` del schema. */
128
- private readonly values = signal<Record<string, string>>({});
129
-
130
- label(key: string): string {
131
- return LABELS[key] ?? key;
132
- }
133
-
134
- value(key: string): string {
135
- return this.values()[key] ?? '';
136
- }
137
-
138
- setValue(key: string, next: string): void {
139
- this.values.update((current) => ({ ...current, [key]: next }));
140
- }
141
-
142
- loginWithGoogle(): void {
143
- if (this.providerLoading()) return;
144
- this.providerLoading.set('google');
145
- this.error.set(null);
146
- this.auth.loginWithGoogle().subscribe({
147
- next: () => {
148
- this.providerLoading.set(null);
149
- this.goAfterLogin();
150
- },
151
- error: (err: unknown) => {
152
- this.providerLoading.set(null);
153
- this.error.set(
154
- err instanceof ApiError && err.message !== ''
155
- ? err.message
156
- : err instanceof Error && err.message !== ''
157
- ? err.message
158
- : 'No se pudo iniciar sesión con Google.',
159
- );
160
- },
161
- });
162
- }
163
-
164
- submit(event: Event): void {
165
- event.preventDefault();
166
- if (this.loading()) return;
167
-
168
- const email = this.value('email').trim();
169
- const password = this.value('password');
170
- if (email === '' || password === '') {
171
- this.error.set('Completa el correo y la contraseña.');
172
- return;
173
- }
174
-
175
- if (this.schema.firebase && this.schema.providers.includes('password')) {
176
- this.loading.set(true);
177
- this.error.set(null);
178
- this.auth.loginWithFirebasePassword(email, password).subscribe({
179
- next: () => {
180
- this.loading.set(false);
181
- this.goAfterLogin();
182
- },
183
- error: (err: unknown) => {
184
- this.loading.set(false);
185
- this.error.set(
186
- err instanceof ApiError && err.message !== ''
187
- ? err.message
188
- : err instanceof Error && err.message !== ''
189
- ? err.message
190
- : 'No se pudo iniciar sesión.',
191
- );
192
- },
193
- });
194
- return;
195
- }
196
-
197
- this.loading.set(true);
198
- this.error.set(null);
199
- this.auth.login(email, password).subscribe({
200
- next: () => {
201
- this.loading.set(false);
202
- this.goAfterLogin();
203
- },
204
- error: (err: unknown) => {
205
- this.loading.set(false);
206
- // El mensaje del backend ya viene en español y ya describe el
207
- // caso («credenciales inválidas»): reformatearlo acá sólo
208
- // duplicaría la verdad y se perdería si cambia el texto del back.
209
- this.error.set(
210
- err instanceof ApiError && err.message !== ''
211
- ? err.message
212
- : 'No se pudo iniciar sesión.',
213
- );
214
- },
215
- });
216
- }
217
-
218
- private goAfterLogin(): void {
219
- const previous = this.auth.consumeReturnUrl();
220
- const target =
221
- previous !== null && previous !== '' && !previous.startsWith(LOGIN_PATH)
222
- ? previous
223
- : this.schema.redirectAfterLogin;
224
- void this.router.navigateByUrl(target).catch(() => undefined);
225
- }
226
- }
227
-
@@ -1,234 +0,0 @@
1
- import { Component, computed, inject, signal } from '@angular/core';
2
- import { takeUntilDestroyed, toSignal } from '@angular/core/rxjs-interop';
3
- import { ActivatedRoute, Router } from '@angular/router';
4
- import { distinctUntilChanged, map, of } from 'rxjs';
5
-
6
- import type { ResourceSchema, SelectionBinding } from '../../core/models/schema.model';
7
- import { AuthService } from '../../core/services/auth.service';
8
- import type { SelectionEmitter } from '../../views/composer/view-context';
9
- import { getSchema } from '../../core/registry/schema-catalog';
10
- import { EmptyState } from '../../shared/components/empty-state/empty-state';
11
- import { Dashboard } from '../dashboard/dashboard';
12
- import { DetailView } from '../detail-view/detail-view';
13
- import { FormView } from '../form-view/form-view';
14
- import { ListView } from '../list-view/list-view';
15
-
16
- type View = 'list' | 'detail';
17
-
18
- /**
19
- * Página del recurso: resuelve el `id` de la ruta contra el registry y
20
- * despacha según `kind` — colección → list view, singleton → form view,
21
- * dashboard → panel de sólo lectura — aplicando `permissions` (estado
22
- * «sin permisos»).
23
- *
24
- * Cuando `schema.detail.enabled === true`, intercepta el click de fila del
25
- * `ListView` y muestra el `DetailView` en lugar de navegar al formulario.
26
- * «Editar» desde el `DetailView` sí navega al formulario de edición.
27
- */
28
- @Component({
29
- selector: 'app-resource-page',
30
- imports: [Dashboard, DetailView, EmptyState, FormView, ListView],
31
- template: `
32
- @if (schema(); as schema) {
33
- @if (hasNoAccess(schema)) {
34
- <app-empty-state
35
- title="Sin permisos"
36
- message="No tiene permisos para gestionar este recurso."
37
- />
38
- } @else {
39
- @switch (schema.kind) {
40
- @case ('collection') {
41
- @switch (currentView()) {
42
- @case ('detail') {
43
- @if (selectedRecord(); as rec) {
44
- <app-detail-view
45
- [schema]="schema"
46
- [record]="rec"
47
- (back)="switchToList()"
48
- (edit)="goEdit($event)"
49
- (delete)="switchToList()"
50
- />
51
- }
52
- }
53
- @default {
54
- <header class="mb-6">
55
- <h1 class="font-display text-2xl font-semibold">
56
- {{ schema.labelPlural }}
57
- </h1>
58
- <p class="text-sm text-neutral">Colección · {{ schema.fields.length }} campos</p>
59
- </header>
60
- <app-list-view [schema]="schema" (viewRecord)="switchToDetail($event)" />
61
- }
62
- }
63
- }
64
- @case ('dashboard') {
65
- <app-dashboard [schema]="schema" />
66
- }
67
- @default {
68
- <!-- singleton: formulario directo -->
69
- <app-form-view />
70
- }
71
- }
72
- }
73
- } @else {
74
- <app-empty-state
75
- title="Recurso no encontrado"
76
- message="No existe un schema con ese id en el registry."
77
- />
78
- }
79
- `,
80
- })
81
- export class ResourcePage {
82
- private readonly route = inject(ActivatedRoute);
83
- private readonly router = inject(Router);
84
- private readonly auth = inject(AuthService);
85
- // paramMap emite síncrono (ConvertToParamMap) → requireSync evita `| undefined`.
86
- private readonly params = toSignal(this.route.paramMap, { requireSync: true });
87
- private readonly routeData = toSignal(this.route.data ?? of({}), { requireSync: true });
88
-
89
- readonly schema = computed(() => {
90
- const resourceId = this.routeData()['resourceId'] ?? this.params().get('id');
91
- return getSchema(typeof resourceId === 'string' ? resourceId : '');
92
- });
93
-
94
- /** Vista activa: 'list' (por defecto) o 'detail' (al hacer clic en una fila con detail.enabled). */
95
- readonly currentView = signal<View>('list');
96
- /** Registro seleccionado para la vista de detalle. */
97
- readonly selectedRecord = signal<Record<string, unknown> | null>(null);
98
-
99
- constructor() {
100
- // La ficha es ENLAZABLE: `/:id/:key` (p. ej. el «Ver Ficha» del
101
- // dashboard) entra directo al detalle. Al navegar — desde una fila o con
102
- // el botón atrás — la URL es la única fuente de verdad de la vista.
103
- this.route.paramMap
104
- .pipe(
105
- map((params) => params.get('key')),
106
- distinctUntilChanged(),
107
- takeUntilDestroyed(),
108
- )
109
- .subscribe((key) => this.openFromRoute(key));
110
- this.route.data
111
- ?.pipe(takeUntilDestroyed())
112
- .subscribe(() => this.openFromRoute(this.params().get('key')));
113
- }
114
-
115
- private openFromRoute(key: string | null): void {
116
- const schema = this.schema();
117
- if (!schema || schema.kind !== 'collection') return;
118
-
119
- const requestedView = this.routeData()['viewType'];
120
- if ((requestedView === 'detail' || key) && key && schema.detail?.enabled) {
121
- // Si el registro ya está cargado (clic en una fila) se conserva tal
122
- // cual; si llegamos por URL sólo hay clave, y DetailView trae el resto.
123
- const current = this.selectedRecord();
124
- if (!current || String(current[schema.keyField]) !== key) {
125
- this.selectedRecord.set({ [schema.keyField]: key });
126
- }
127
- this.currentView.set('detail');
128
- } else if (this.currentView() === 'detail') {
129
- this.selectedRecord.set(null);
130
- this.currentView.set('list');
131
- }
132
- }
133
-
134
- /**
135
- * Clic de fila. R9.9.4 · decision table: si el schema declara `select`,
136
- * el contrato declarado REEMPLAZA el comportamiento local (`route` ⇒
137
- * emisión al dispatcher, `inline` ⇒ selección local sin URL, `none` ⇒
138
- * no-op); sin declaración, la navegación local actual sigue intacta
139
- * (backward compat — R9.9 «existing routes untouched»).
140
- */
141
- switchToDetail(record: Record<string, unknown>): void {
142
- const schema = this.schema();
143
- const data = this.routeData();
144
- const binding = data['select'] as SelectionBinding | undefined;
145
-
146
- if (binding !== undefined) {
147
- const key =
148
- schema && record[schema.keyField] !== undefined && record[schema.keyField] !== null
149
- ? String(record[schema.keyField])
150
- : '';
151
- if (binding.mode === 'route') {
152
- const emit = data['emit'] as SelectionEmitter | undefined;
153
- emit?.({
154
- resource: schema?.id ?? String(data['resourceId'] ?? ''),
155
- key,
156
- action: binding.on,
157
- });
158
- return;
159
- }
160
- if (binding.mode === 'none') return;
161
- // inline ⇒ selección local (ficha embebida), sin tocar la URL
162
- this.selectedRecord.set(record);
163
- this.currentView.set('detail');
164
- return;
165
- }
166
-
167
- this.selectedRecord.set(record);
168
- this.currentView.set('detail');
169
- const k = schema ? record[schema.keyField] : undefined;
170
- if (schema && k !== undefined && k !== null && String(k) !== '') {
171
- void this.router.navigate(['/', schema.id, String(k)]);
172
- }
173
- }
174
-
175
- switchToList(): void {
176
- this.selectedRecord.set(null);
177
- this.currentView.set('list');
178
- const schema = this.schema();
179
- if (schema && this.params().get('key')) {
180
- void this.router.navigate(['/', schema.id]);
181
- }
182
- }
183
-
184
- goEdit(record: Record<string, unknown>): void {
185
- const schema = this.schema();
186
- if (!schema) return;
187
- const key = record[schema.keyField] as string | undefined;
188
- if (key) {
189
- void this.router.navigate(['/', schema.id, key, 'edit']);
190
- }
191
- }
192
-
193
- /**
194
- * Colección sin ninguna operación posible u singleton sin `update`: no hay
195
- * nada que mostrar con sentido (base.md §10 «Sin permisos»).
196
- *
197
- * R7.5: usa policies cuando existan, fallback a permissions.
198
- *
199
- * Un dashboard nunca entra aquí: es un panel de sólo lectura, `ResourcePermissions`
200
- * no tiene `read` y el acceso real a los datos lo decide el endpoint
201
- * (A4: el cliente es interfaz, no autoridad).
202
- */
203
- hasNoAccess(schema: ResourceSchema): boolean {
204
- // R7.5: intentar policies primero
205
- if (schema.policies?.access) {
206
- // Con policies, ocultar si el usuario no puede hacer NINGUNA operación
207
- // relevante para el tipo de recurso
208
- if (schema.kind === 'singleton') {
209
- return !this.auth.canDoOperation(schema, 'update');
210
- }
211
- // collection: ocultar si no puede hacer list, create, update ni remove
212
- return (
213
- !this.auth.canDoOperation(schema, 'list') &&
214
- !this.auth.canDoOperation(schema, 'create') &&
215
- !this.auth.canDoOperation(schema, 'update') &&
216
- !this.auth.canDoOperation(schema, 'remove')
217
- );
218
- }
219
-
220
- // Fallback a permissions (legacy)
221
- const permissions = schema.permissions;
222
- if (!permissions || schema.kind === 'dashboard') {
223
- return false;
224
- }
225
- if (schema.kind === 'singleton') {
226
- const hasReadEndpoint = !!schema.endpoint?.get;
227
- return !hasReadEndpoint && permissions.update === false;
228
- }
229
- const hasReadEndpoint = !!schema.endpoint?.list || !!schema.endpoint?.get;
230
- const canWrite =
231
- permissions.create !== false || permissions.update !== false || permissions.remove !== false;
232
- return !hasReadEndpoint && !canWrite;
233
- }
234
- }
@@ -1,35 +0,0 @@
1
- import { Component, computed, input } from '@angular/core';
2
-
3
- export type BadgeKind = 'success' | 'danger' | 'warning' | 'info' | 'neutral';
4
-
5
- const KINDS: Record<BadgeKind, string> = {
6
- success: 'bg-success text-surface',
7
- danger: 'bg-danger text-surface',
8
- warning: 'bg-accent/20 text-accent-text font-semibold',
9
- info: 'bg-primary text-surface',
10
- neutral: 'bg-neutral text-surface',
11
- };
12
-
13
- /**
14
- * Etiqueta compacta (booleanos, estados).
15
- *
16
- * El texto va con `text-surface` y no `text-white` porque el fondo lo da
17
- * el token y en oscuro se aclara: blanco sobre `--color-danger` oscuro
18
- * sería 2,8:1. `--color-surface` vale #fff en claro, así que el render
19
- * actual no cambia, y en oscuro el texto se vuelve oscuro sobre el tono
20
- * aclarado (6,4–9,2:1).
21
- */
22
- @Component({
23
- selector: 'app-badge',
24
- host: { '[class]': 'classes()' },
25
- template: `{{ label() }}`,
26
- })
27
- export class Badge {
28
- readonly label = input.required<string>();
29
- readonly kind = input<BadgeKind>('neutral');
30
-
31
- readonly classes = computed(
32
- () =>
33
- `inline-flex items-center rounded-full px-2 py-0.5 text-xs font-medium ${KINDS[this.kind()]}`,
34
- );
35
- }
@@ -1,65 +0,0 @@
1
- import { Component, computed, input } from '@angular/core';
2
-
3
- export type ButtonVariant = 'primary' | 'outline' | 'danger' | 'ghost';
4
- export type ButtonSize = 'md' | 'sm' | 'icon';
5
-
6
- const BASE =
7
- 'inline-flex cursor-pointer items-center justify-center gap-2 rounded-lg font-medium transition-colors focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-primary disabled:cursor-not-allowed disabled:opacity-50';
8
-
9
- const VARIANTS: Record<ButtonVariant, string> = {
10
- primary: 'bg-primary text-surface hover:bg-primary/90',
11
- outline: 'border border-neutral/40 bg-surface text-neutral hover:bg-neutral/10',
12
- danger: 'bg-danger text-surface hover:bg-danger/90',
13
- ghost: 'text-neutral hover:bg-neutral/10',
14
- };
15
-
16
- /**
17
- * Tamaño: el padding y el tip-size viven AQUÍ, no en `BASE`. Dos botones
18
- * con la misma propiedad (px-4 vs px-2) los resolvería el orden del CSS
19
- * generado, no el código — el motivo por el que los chips del textarea
20
- * acabaron con `!important`.
21
- *
22
- * Mobile-first: todas arrancan en `min-h-11` (44 px, objetivo táctil de la
23
- * guía mobile §8.2) y en `lg` relajan al tamaño de escritorio, así el mismo
24
- * componente cumple la guía táctil sin engordar la UI de escritorio.
25
- * - `md` → botón de texto (Guardar, Cancelar…)
26
- * - `sm` → chips y acciones compactas (texto `xs`)
27
- * - `icon` → sólo glifo, cuadrado (toolbars de filas, arrastre)
28
- */
29
- const SIZES: Record<ButtonSize, string> = {
30
- md: 'px-4 py-2 text-sm min-h-11 lg:min-h-0',
31
- sm: 'px-2 py-1 text-xs min-h-11 lg:min-h-0',
32
- icon: 'p-1 min-h-11 min-w-11 lg:min-h-0 lg:min-w-0',
33
- };
34
-
35
- /**
36
- * Botón nativo (selector por atributo, patrón Material `button[mat-button]`):
37
- * conserva semántica y estilos de `<button>` y las clases del consumidor.
38
- */
39
- @Component({
40
- selector: 'button[app-button]',
41
- host: {
42
- '[class]': 'classes()',
43
- '[type]': 'type()',
44
- '[disabled]': 'disabled() || loading()',
45
- '[attr.aria-busy]': 'loading() || null',
46
- },
47
- template: `
48
- @if (loading()) {
49
- <span
50
- class="size-4 animate-spin rounded-full border-2 border-current border-t-transparent"
51
- aria-hidden="true"
52
- ></span>
53
- }
54
- <ng-content />
55
- `,
56
- })
57
- export class Button {
58
- readonly variant = input<ButtonVariant>('primary');
59
- readonly size = input<ButtonSize>('md');
60
- readonly type = input<'button' | 'submit'>('button');
61
- readonly disabled = input(false);
62
- readonly loading = input(false);
63
-
64
- readonly classes = computed(() => `${BASE} ${VARIANTS[this.variant()]} ${SIZES[this.size()]}`);
65
- }