@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,106 +0,0 @@
1
- import { TestBed } from '@angular/core/testing';
2
-
3
- import { ThemeService } from './theme.service';
4
-
5
- /**
6
- * jsdom no implementa `matchMedia`, así que se instala un doble controlable
7
- * antes de cualquier construcción del servicio: `matches` decide el estado
8
- * del sistema y `listeners` permite disparar el cambio en vivo.
9
- */
10
- describe('ThemeService — 8.6 selector Sistema / Claro / Oscuro', () => {
11
- let matches = false;
12
- const listeners: Array<() => void> = [];
13
-
14
- beforeAll(() => {
15
- Object.defineProperty(window, 'matchMedia', {
16
- writable: true,
17
- configurable: true,
18
- value: (query: string) => ({
19
- matches: query.includes('prefers-color-scheme') && matches,
20
- media: query,
21
- onchange: null,
22
- addEventListener: (_type: string, listener: () => void) => listeners.push(listener),
23
- removeEventListener: () => undefined,
24
- dispatchEvent: () => false,
25
- }),
26
- });
27
- });
28
-
29
- beforeEach(() => {
30
- localStorage.clear();
31
- document.documentElement.removeAttribute('data-theme');
32
- document.documentElement.removeAttribute('data-theme-mode');
33
- matches = false;
34
- listeners.length = 0;
35
- });
36
-
37
- const fireSystemChange = (): void => {
38
- for (const listener of listeners) {
39
- listener();
40
- }
41
- };
42
-
43
- it('arranca en «system» cuando no hay elección guardada', () => {
44
- const theme = new ThemeService();
45
-
46
- expect(theme.mode()).toBe('system');
47
- expect(theme.resolved()).toBe('light');
48
- expect(document.documentElement.dataset['theme']).toBe('light');
49
- expect(document.documentElement.dataset['themeMode']).toBe('system');
50
- });
51
-
52
- it('una elección corrupta en localStorage vuelve a «system»', () => {
53
- localStorage.setItem('admin-theme', 'naranja');
54
-
55
- expect(new ThemeService().mode()).toBe('system');
56
- });
57
-
58
- it('aplica y persiste una elección concreta', () => {
59
- const theme = new ThemeService();
60
-
61
- theme.set('dark');
62
-
63
- expect(theme.mode()).toBe('dark');
64
- expect(theme.resolved()).toBe('dark');
65
- expect(document.documentElement.dataset['theme']).toBe('dark');
66
- expect(document.documentElement.dataset['themeMode']).toBe('dark');
67
- expect(localStorage.getItem('admin-theme')).toBe('dark');
68
- });
69
-
70
- it('recupera la elección guardada al arrancar y pinta ese modo', () => {
71
- matches = true;
72
- localStorage.setItem('admin-theme', 'light');
73
-
74
- const theme = new ThemeService();
75
-
76
- expect(theme.mode()).toBe('light');
77
- expect(theme.resolved()).toBe('light');
78
- expect(document.documentElement.dataset['theme']).toBe('light');
79
- });
80
-
81
- it('«system» reacciona en vivo a un cambio del tema del sistema', () => {
82
- const theme = new ThemeService();
83
- expect(theme.resolved()).toBe('light');
84
-
85
- matches = true;
86
- fireSystemChange();
87
-
88
- expect(theme.resolved()).toBe('dark');
89
- expect(document.documentElement.dataset['theme']).toBe('dark');
90
- });
91
-
92
- it('ignora el SO cuando la elección es concreta', () => {
93
- const theme = new ThemeService();
94
- theme.set('light');
95
-
96
- matches = true;
97
- fireSystemChange();
98
-
99
- expect(theme.resolved()).toBe('light');
100
- expect(document.documentElement.dataset['theme']).toBe('light');
101
- });
102
-
103
- it('queda disponible por DI como servicio raíz', () => {
104
- expect(TestBed.inject(ThemeService)).toBeTruthy();
105
- });
106
- });
@@ -1,86 +0,0 @@
1
- import { Service, signal } from '@angular/core';
2
-
3
- /** Los tres estados del selector (§8.6). */
4
- export type ThemeMode = 'system' | 'light' | 'dark';
5
-
6
- /** Lo que acaba pintado en `<html data-theme>`. */
7
- export type ResolvedTheme = 'light' | 'dark';
8
-
9
- const STORAGE_KEY = 'admin-theme';
10
- const QUERY = '(prefers-color-scheme: dark)';
11
- const MODES: string[] = ['system', 'light', 'dark'];
12
-
13
- /** Lee la elección guardada; cualquier basura vuelve a `system`. */
14
- function storedMode(): ThemeMode {
15
- try {
16
- const raw = localStorage.getItem(STORAGE_KEY);
17
- return raw !== null && MODES.includes(raw) ? (raw as ThemeMode) : 'system';
18
- } catch {
19
- // localStorage puede tirar en modo privado o con cuota agotada.
20
- return 'system';
21
- }
22
- }
23
-
24
- /**
25
- * `system` → lo que diga el sistema operativo.
26
- * jsdom no implementa `matchMedia`: sin él se asume claro, que es además
27
- * el estado por defecto antes de que corra el script de `index.html`.
28
- */
29
- function prefersDark(): boolean {
30
- return typeof window.matchMedia === 'function' && window.matchMedia(QUERY).matches;
31
- }
32
-
33
- function resolve(mode: ThemeMode): ResolvedTheme {
34
- return mode === 'system' ? (prefersDark() ? 'dark' : 'light') : mode;
35
- }
36
-
37
- /**
38
- * Selector de tema con tres estados: `system`, `light`, `dark` (Fase 8.6).
39
- *
40
- * `system` NO se traduce en CSS con una media query sino que se resuelve a
41
- * un valor concreto, porque con tres estados un único `prefers-color-scheme`
42
- * no alcanza para distinguir "oscuro elegido a mano" de "oscuro heredado".
43
- *
44
- * El arranque sin flash vive en `index.html`: un script síncrono en el
45
- * `<head>` escribe `data-theme` y `data-theme-mode` antes del primer
46
- * pintado. Este servicio toma el relevo después y además escucha cambios
47
- * en vivo del sistema, para que cambiar el tema del SO con la app abierta
48
- * la recoloree — pero sólo mientras la elección siga siendo `system`: si el
49
- * usuario eligió un modo concreto, el SO deja de mandar.
50
- */
51
- @Service()
52
- export class ThemeService {
53
- /** Elección persistida. */
54
- readonly mode = signal<ThemeMode>(storedMode());
55
- /** Lo que está pintado ahora mismo. */
56
- readonly resolved = signal<ResolvedTheme>(resolve(this.mode()));
57
-
58
- constructor() {
59
- this.apply();
60
-
61
- const media = typeof window.matchMedia === 'function' ? window.matchMedia(QUERY) : null;
62
- media?.addEventListener('change', () => {
63
- if (this.mode() === 'system') {
64
- this.apply();
65
- }
66
- });
67
- }
68
-
69
- set(mode: ThemeMode): void {
70
- this.mode.set(mode);
71
- try {
72
- localStorage.setItem(STORAGE_KEY, mode);
73
- } catch {
74
- // Sin persistencia el tema sigue funcionando durante la sesión.
75
- }
76
- this.apply();
77
- }
78
-
79
- /** Escribe el resultado en `<html>`; es lo que lee `theme.css`. */
80
- private apply(): void {
81
- const resolved = resolve(this.mode());
82
- this.resolved.set(resolved);
83
- document.documentElement.dataset['theme'] = resolved;
84
- document.documentElement.dataset['themeMode'] = this.mode();
85
- }
86
- }
@@ -1,198 +0,0 @@
1
- import { HttpClient, HttpParams, provideHttpClient } from '@angular/common/http';
2
- import { HttpTestingController, provideHttpClientTesting } from '@angular/common/http/testing';
3
- import { TestBed } from '@angular/core/testing';
4
- import { firstValueFrom } from 'rxjs';
5
-
6
- import {
7
- buildUrl,
8
- doRestRequest,
9
- sendGas,
10
- sendRest,
11
- toHttpParams,
12
- type TransportDeps,
13
- } from './transport';
14
-
15
- // Transporte desacoplado (plan doc/engine-refactor-plan.md · Fase 4).
16
- // Funciones puras con dependencias por parámetro; sin DI, sin ciclo (R6).
17
- describe('transport — funciones puras (Fase 4)', () => {
18
- let http: HttpClient;
19
- let httpMock: HttpTestingController;
20
-
21
- const deps = (overrides: Partial<TransportDeps> = {}): TransportDeps => ({
22
- http,
23
- apiUrl: '/api',
24
- sessionToken: () => 'tok',
25
- ...overrides,
26
- });
27
-
28
- beforeEach(() => {
29
- TestBed.configureTestingModule({
30
- providers: [provideHttpClient(), provideHttpClientTesting()],
31
- });
32
- http = TestBed.inject(HttpClient);
33
- httpMock = TestBed.inject(HttpTestingController);
34
- });
35
-
36
- afterEach(() => {
37
- httpMock.verify();
38
- });
39
-
40
- // ── buildUrl ────────────────────────────────────────────────────────────
41
- describe('buildUrl', () => {
42
- it('concatena apiUrl + path con slash inicial', () => {
43
- expect(buildUrl('/api', '/admin/items')).toBe('/api/admin/items');
44
- });
45
-
46
- it('tolera path sin slash inicial', () => {
47
- expect(buildUrl('/api', 'admin/items')).toBe('/api/admin/items');
48
- });
49
-
50
- it('recorta slashes finales de la base', () => {
51
- expect(buildUrl('https://x.example/', '/admin')).toBe('https://x.example/admin');
52
- });
53
-
54
- it('apiUrl vacía ⇒ path con slash inicial', () => {
55
- expect(buildUrl('', '/admin')).toBe('/admin');
56
- });
57
- });
58
-
59
- // ── toHttpParams ────────────────────────────────────────────────────────
60
- describe('toHttpParams', () => {
61
- it('plana objetos anidados un nivel (contrato histórico)', () => {
62
- const params = toHttpParams({ view: 'calendar', params: { orden: 'es' } });
63
- expect(params.get('view')).toBe('calendar');
64
- expect(params.get('orden')).toBe('es');
65
- });
66
-
67
- it('omite null y undefined', () => {
68
- const params = toHttpParams({ a: null, b: undefined, c: 'x' });
69
- expect(params.has('a')).toBe(false);
70
- expect(params.has('b')).toBe(false);
71
- expect(params.get('c')).toBe('x');
72
- });
73
-
74
- it('arrays viajan como repetición de la clave', () => {
75
- const params = toHttpParams({ tag: ['a', 'b'] });
76
- expect(params.getAll('tag')).toEqual(['a', 'b']);
77
- });
78
-
79
- it('payload vacío ⇒ HttpParams vacío', () => {
80
- expect(toHttpParams(undefined) instanceof HttpParams).toBe(true);
81
- expect(toHttpParams({}).keys().length).toBe(0);
82
- });
83
- });
84
-
85
- // ── sendGas ─────────────────────────────────────────────────────────────
86
- describe('sendGas', () => {
87
- it('POST text/plain a apiUrl con envelope { token, method, path, payload }', async () => {
88
- const pending = firstValueFrom(sendGas(deps(), '/admin/items', 'GET', { view: 'v' }));
89
- const req = httpMock.expectOne('/api');
90
- expect(req.request.method).toBe('POST');
91
- expect(req.request.headers.get('Content-Type')).toBe('text/plain');
92
- expect(req.request.body).toEqual({
93
- token: 'tok',
94
- method: 'GET',
95
- path: '/admin/items',
96
- payload: { view: 'v' },
97
- });
98
- req.flush({ items: [] });
99
- await pending;
100
- });
101
-
102
- it('payload ausente ⇒ {} en el envelope', async () => {
103
- const pending = firstValueFrom(sendGas(deps(), '/admin/x', 'POST'));
104
- const req = httpMock.expectOne('/api');
105
- expect((req.request.body as { payload: unknown }).payload).toEqual({});
106
- req.flush({});
107
- await pending;
108
- });
109
-
110
- it('lanza si se pide multipart en el driver gas', () => {
111
- expect(() => sendGas(deps(), '/admin/x', 'POST', undefined, true)).toThrow(
112
- /only supported with the REST driver/i,
113
- );
114
- });
115
-
116
- it('lee el token de sesión en cada petición (getter), no al construir', async () => {
117
- let token = 't1';
118
- const pending = firstValueFrom(sendGas(deps({ sessionToken: () => token }), '/x', 'GET'));
119
- const req = httpMock.expectOne('/api');
120
- expect((req.request.body as { token: string }).token).toBe('t1');
121
- token = 't2'; // cambio de sesión antes del flush no afecta lo ya enviado
122
- req.flush({});
123
- await pending;
124
- });
125
- });
126
-
127
- // ── sendRest ────────────────────────────────────────────────────────────
128
- describe('sendRest', () => {
129
- it('GET con Authorization bearer del getter de sesión y params en query', async () => {
130
- const pending = firstValueFrom(sendRest(deps(), '/admin/items', 'GET', { page: 2 }));
131
- const req = httpMock.expectOne('/api/admin/items?page=2');
132
- expect(req.request.method).toBe('GET');
133
- expect(req.request.headers.get('Authorization')).toBe('Bearer tok');
134
- req.flush([]);
135
- await pending;
136
- });
137
-
138
- it('POST JSON: Content-Type application/json y body = payload', async () => {
139
- const pending = firstValueFrom(sendRest(deps(), '/admin/items', 'POST', { slug: 'a' }));
140
- const req = httpMock.expectOne('/api/admin/items');
141
- expect(req.request.headers.get('Content-Type')).toBe('application/json');
142
- expect(req.request.body).toEqual({ slug: 'a' });
143
- req.flush({});
144
- await pending;
145
- });
146
-
147
- it('multipart: FormData sin Content-Type application/json (boundary del navegador)', async () => {
148
- const fd = new FormData();
149
- fd.append('file', new Blob(['x']), 'x.txt');
150
- const pending = firstValueFrom(sendRest(deps(), '/admin/upload', 'POST', fd, true));
151
- const req = httpMock.expectOne('/api/admin/upload');
152
- expect(req.request.body instanceof FormData).toBe(true);
153
- expect(req.request.headers.get('Content-Type')).not.toBe('application/json');
154
- req.flush({});
155
- await pending;
156
- });
157
-
158
- it('sin getIdToken usa el token de sesión almacenado', async () => {
159
- const pending = firstValueFrom(sendRest(deps(), '/admin/x', 'GET'));
160
- const req = httpMock.expectOne('/api/admin/x');
161
- expect(req.request.headers.get('Authorization')).toBe('Bearer tok');
162
- req.flush({});
163
- await pending;
164
- });
165
-
166
- it('con getIdToken fresco se usa el token devuelto por el adaptador', async () => {
167
- const d = deps({ getIdToken: () => Promise.resolve('fresh-123') });
168
- const pending = firstValueFrom(sendRest(d, '/admin/x', 'GET'));
169
- // La promesa resuelve en un microtask: esperar antes de buscar la petición.
170
- await new Promise((resolve) => setTimeout(resolve, 0));
171
- const req = httpMock.expectOne('/api/admin/x');
172
- expect(req.request.headers.get('Authorization')).toBe('Bearer fresh-123');
173
- req.flush({});
174
- await pending;
175
- });
176
-
177
- it('un rechazo de getIdToken viaja por el canal de errores (sin petición)', async () => {
178
- const d = deps({ getIdToken: () => Promise.reject(new Error('token expirado')) });
179
- const pending = firstValueFrom(sendRest(d, '/admin/x', 'GET'));
180
- await expect(pending).rejects.toThrow('token expirado');
181
- httpMock.expectNone('/api/admin/x');
182
- });
183
- });
184
-
185
- // ── doRestRequest ───────────────────────────────────────────────────────
186
- describe('doRestRequest', () => {
187
- it('DELETE va con params, no con body', async () => {
188
- const pending = firstValueFrom(
189
- doRestRequest(deps(), '/api/admin/x/1', 'DELETE', { reason: 'r' }, undefined),
190
- );
191
- const req = httpMock.expectOne('/api/admin/x/1?reason=r');
192
- expect(req.request.method).toBe('DELETE');
193
- expect(req.request.body).toBeNull();
194
- req.flush(null);
195
- await pending;
196
- });
197
- });
198
- });
@@ -1,164 +0,0 @@
1
- /**
2
- * Transporte HTTP puro del motor (plan doc/engine-refactor-plan.md · Fase 4).
3
- *
4
- * Funciones sin estado ni DI: todas las dependencias (HttpClient, apiUrl,
5
- * getter de token de sesión, adaptador opcional de Firebase) entran por
6
- * parámetro en `TransportDeps`. `ApiService` queda como fachada que arma los
7
- * deps y delega acá — mismo comportamiento observable, cero ciclos de DI (R6).
8
- *
9
- * Los contratos de transporte (R7) viven en este módulo y no cambian:
10
- *
11
- * - **gas**: `POST text/plain` a `apiUrl` con envelope
12
- * `{ token, method, path, payload }` (contrato Google Apps Script).
13
- * - **rest**: verbos reales con `Authorization: Bearer`, params en query
14
- * (GET/DELETE) o body JSON (POST/PUT/PATCH), multipart sin Content-Type.
15
- */
16
- import { HttpClient, HttpHeaders, HttpParams } from '@angular/common/http';
17
- import { from, Observable, switchMap } from 'rxjs';
18
-
19
- /** Métodos que entiende el envelope del backend (baseapi §2.3). */
20
- export type EnvelopeMethod = 'GET' | 'POST' | 'PUT' | 'PATCH' | 'DELETE';
21
-
22
- /** Dependencias del transporte, aportadas por la fachada (no hay singleton acá). */
23
- export interface TransportDeps {
24
- http: HttpClient;
25
- /** URL base (`API_URL`): el Web App de Apps Script o el origen REST. */
26
- apiUrl: string;
27
- /** Getter del token de SESIÓN, leído en CADA petición (R2.8). */
28
- sessionToken: () => string | null;
29
- /**
30
- * Refresco opcional de ID token (Fase Firebase): si existe, se llama antes
31
- * de armar la cabecera Authorization. Un rechazo viaja por el canal de
32
- * errores — nunca se manda una petición con token inválido.
33
- */
34
- getIdToken?: () => Promise<string>;
35
- }
36
-
37
- /** `POST text/plain` a `apiUrl` con el envelope GAS. */
38
- export function sendGas<T>(
39
- deps: TransportDeps,
40
- path: string,
41
- method: EnvelopeMethod,
42
- payload?: Record<string, unknown> | FormData,
43
- multipart = false,
44
- ): Observable<T> {
45
- if (multipart) {
46
- throw new Error('Multipart payload mode is only supported with the REST driver');
47
- }
48
- // Token de la SESIÓN, leído en cada petición (R2.8): no hay credencial
49
- // fija que inyectar en el arranque.
50
- const body = {
51
- token: deps.sessionToken(),
52
- method,
53
- path,
54
- payload: payload ?? {},
55
- };
56
- return deps.http.post<T>(deps.apiUrl, body, {
57
- headers: { 'Content-Type': 'text/plain' },
58
- });
59
- }
60
-
61
- /**
62
- * Verbo HTTP real con `Authorization: Bearer`. Si el adaptador provee
63
- * `getIdToken()`, se resuelve el token fresco ANTES de la cabecera; si no,
64
- * se usa el de sesión almacenado (fallback). Cualquier error de `getIdToken()`
65
- * se propaga por el canal de errores.
66
- */
67
- export function sendRest<T>(
68
- deps: TransportDeps,
69
- path: string,
70
- method: EnvelopeMethod,
71
- payload?: Record<string, unknown> | FormData,
72
- multipart = false,
73
- ): Observable<T> {
74
- const url = buildUrl(deps.apiUrl, path);
75
- const headers = new HttpHeaders();
76
-
77
- if (deps.getIdToken) {
78
- return from(deps.getIdToken()).pipe(
79
- switchMap((freshToken) =>
80
- doRestRequest<T>(
81
- deps,
82
- url,
83
- method,
84
- payload,
85
- headers.set('Authorization', `Bearer ${freshToken}`),
86
- multipart,
87
- ),
88
- ),
89
- );
90
- }
91
-
92
- const token = deps.sessionToken();
93
- const finalHeaders = token ? headers.set('Authorization', `Bearer ${token}`) : headers;
94
- return doRestRequest<T>(deps, url, method, payload, finalHeaders, multipart);
95
- }
96
-
97
- /** Despacho REST concreto sobre una URL ya construida. */
98
- export function doRestRequest<T>(
99
- deps: TransportDeps,
100
- url: string,
101
- method: EnvelopeMethod,
102
- payload: Record<string, unknown> | FormData | undefined,
103
- headers: HttpHeaders = new HttpHeaders(),
104
- multipart = false,
105
- ): Observable<T> {
106
- if (method === 'GET' || method === 'DELETE') {
107
- const params = toHttpParams(
108
- payload instanceof FormData ? undefined : (payload as Record<string, unknown> | undefined),
109
- );
110
- return deps.http.request<T>(method, url, { headers, params });
111
- }
112
-
113
- if (multipart || payload instanceof FormData) {
114
- // Sin Content-Type: application/json para que el navegador ponga el boundary.
115
- return deps.http.request<T>(method, url, { headers, body: payload ?? {} });
116
- }
117
-
118
- return deps.http.request<T>(method, url, {
119
- headers: headers.set('Content-Type', 'application/json'),
120
- body: payload ?? {},
121
- });
122
- }
123
-
124
- /** `${apiUrl}${path}` normalizada: sin dobles slashes ni path sin slash inicial. */
125
- export function buildUrl(apiUrl: string, path: string): string {
126
- const base = apiUrl ? apiUrl.replace(/\/+$/, '') : '';
127
- const cleanPath = path.startsWith('/') ? path : `/${path}`;
128
- return `${base}${cleanPath}`;
129
- }
130
-
131
- /**
132
- * Convierte un payload en HttpParams para peticiones GET y DELETE en modo REST.
133
- * Si un valor es un objeto plano (ej: `{ view: 'calendar', params: { orden: 'es' } }`),
134
- * lo aplana un nivel para que sus claves viajen como query params reales
135
- * en lugar de serializarse como `params=[object Object]`.
136
- */
137
- export function toHttpParams(payload?: Record<string, unknown>): HttpParams {
138
- let params = new HttpParams();
139
- if (!payload) {
140
- return params;
141
- }
142
-
143
- for (const [key, value] of Object.entries(payload)) {
144
- if (value === undefined || value === null) {
145
- continue;
146
- }
147
- if (typeof value === 'object' && !Array.isArray(value)) {
148
- for (const [nestedKey, nestedValue] of Object.entries(value as Record<string, unknown>)) {
149
- if (nestedValue !== undefined && nestedValue !== null) {
150
- params = params.set(nestedKey, String(nestedValue));
151
- }
152
- }
153
- } else if (Array.isArray(value)) {
154
- for (const item of value) {
155
- if (item !== undefined && item !== null) {
156
- params = params.append(key, String(item));
157
- }
158
- }
159
- } else {
160
- params = params.set(key, String(value));
161
- }
162
- }
163
- return params;
164
- }
@@ -1,81 +0,0 @@
1
- import { HttpClient, HttpEvent, HttpEventType, HttpResponse } from '@angular/common/http';
2
- import { inject, Service } from '@angular/core';
3
- import { map, Observable, switchMap } from 'rxjs';
4
-
5
- import { ApiService } from './api.service';
6
-
7
- /** Firma que emite `/admin/upload-signature` (baseapi §11.3). NUNCA `api_secret`. */
8
- interface UploadSignature {
9
- cloud_name: string;
10
- api_key: string;
11
- timestamp: number;
12
- signature: string;
13
- folder: string;
14
- allowed_formats: string;
15
- upload_url: string;
16
- }
17
-
18
- /** Respuesta de Cloudinary en un upload normal (sólo interesa `secure_url`). */
19
- interface CloudinaryResponse {
20
- secure_url?: unknown;
21
- }
22
-
23
- /**
24
- * Subida de imágenes en DOS PASOS (baseapi §11.2, opción B):
25
- * 1. Firma firmada por el backend → `POST /admin/upload-signature`
26
- * con `{ resource }` (mismo envelope text/plain que todo `/admin/*`).
27
- * 2. El navegador sube directo a Cloudinary (FormData a `upload_url`) con
28
- * los parámetros que fijó y firmó el backend; el API secret jamás sale
29
- * de acá (§11.1–§11.3).
30
- *
31
- * Se guarda SOLO `secure_url` (base.md §7: la URL pública es el valor del
32
- * campo; la imagen anterior no se borra al reemplazar — §11.6).
33
- *
34
- * Los errores (firma caducada, rate-limit 429, red) llegan por el canal de
35
- * error; el componente conserva la imagen anterior (§11.2 paso 6).
36
- */
37
- @Service()
38
- export class UploadService {
39
- private readonly http = inject(HttpClient);
40
- private readonly api = inject(ApiService);
41
-
42
- /**
43
- * Misma subida con eventos de progreso (base.md §7, estado «Subiendo»):
44
- * emite `UploadProgress` durante la subida a Cloudinary y termina con
45
- * `Response` llevando la URL en el body. El campo sólo cambia cuando
46
- * llega la respuesta.
47
- */
48
- uploadWithProgress(file: File, resource: string, field?: string): Observable<HttpEvent<string>> {
49
- // `field` es opcional (R9.5.3): si el campo declara `formats`, esos son
50
- // los formatos que el backend firma. Sin campo firma la lista global.
51
- const payload: { resource: string; field?: string } = field
52
- ? { resource, field }
53
- : { resource };
54
- return this.api.request<UploadSignature>('POST', '/admin/upload-signature', payload).pipe(
55
- switchMap((signature) => {
56
- const form = new FormData();
57
- form.append('file', file, file.name);
58
- form.append('api_key', signature.api_key);
59
- form.append('timestamp', String(signature.timestamp));
60
- form.append('signature', signature.signature);
61
- form.append('folder', signature.folder);
62
- form.append('allowed_formats', signature.allowed_formats);
63
- return this.http.post<CloudinaryResponse>(signature.upload_url, form, {
64
- reportProgress: true,
65
- observe: 'events',
66
- });
67
- }),
68
- map((event) => {
69
- if (event.type !== HttpEventType.Response) {
70
- return event;
71
- }
72
- const url = typeof event.body?.secure_url === 'string' ? event.body.secure_url : '';
73
- return new HttpResponse({
74
- body: url,
75
- status: event.status,
76
- statusText: event.statusText,
77
- });
78
- }),
79
- );
80
- }
81
- }