@tinya-code/gas-admin-ui 0.2.0-alpha.15 → 0.2.0-alpha.18
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/fesm2022/tinya-code-gas-admin-ui-badge-EeCA_l-o.mjs +84 -0
- package/fesm2022/tinya-code-gas-admin-ui-badge-EeCA_l-o.mjs.map +1 -0
- package/fesm2022/tinya-code-gas-admin-ui-bar-chart-G7sEMSBY.mjs +140 -0
- package/fesm2022/tinya-code-gas-admin-ui-bar-chart-G7sEMSBY.mjs.map +1 -0
- package/fesm2022/tinya-code-gas-admin-ui-boolean-B2ASKL0E.mjs +113 -0
- package/fesm2022/tinya-code-gas-admin-ui-boolean-B2ASKL0E.mjs.map +1 -0
- package/fesm2022/tinya-code-gas-admin-ui-button-BQjNW8KB.mjs +163 -0
- package/fesm2022/tinya-code-gas-admin-ui-button-BQjNW8KB.mjs.map +1 -0
- package/fesm2022/tinya-code-gas-admin-ui-calendar-view-DCY8bj2V.mjs +1027 -0
- package/fesm2022/tinya-code-gas-admin-ui-calendar-view-DCY8bj2V.mjs.map +1 -0
- package/fesm2022/tinya-code-gas-admin-ui-chart-line-BjdlKHoH.mjs +278 -0
- package/fesm2022/tinya-code-gas-admin-ui-chart-line-BjdlKHoH.mjs.map +1 -0
- package/fesm2022/tinya-code-gas-admin-ui-color-Dljh_By3.mjs +107 -0
- package/fesm2022/tinya-code-gas-admin-ui-color-Dljh_By3.mjs.map +1 -0
- package/fesm2022/tinya-code-gas-admin-ui-condition-evaluator-DY2k1M3C.mjs +101 -0
- package/fesm2022/tinya-code-gas-admin-ui-condition-evaluator-DY2k1M3C.mjs.map +1 -0
- package/fesm2022/tinya-code-gas-admin-ui-currency-BvuF3Z63.mjs +141 -0
- package/fesm2022/tinya-code-gas-admin-ui-currency-BvuF3Z63.mjs.map +1 -0
- package/fesm2022/tinya-code-gas-admin-ui-data-source.service-96FSbN72.mjs +88 -0
- package/fesm2022/tinya-code-gas-admin-ui-data-source.service-96FSbN72.mjs.map +1 -0
- package/fesm2022/tinya-code-gas-admin-ui-date-1SJGXZrd.mjs +107 -0
- package/fesm2022/tinya-code-gas-admin-ui-date-1SJGXZrd.mjs.map +1 -0
- package/fesm2022/tinya-code-gas-admin-ui-display-badge-fwk6sDU7.mjs +140 -0
- package/fesm2022/tinya-code-gas-admin-ui-display-badge-fwk6sDU7.mjs.map +1 -0
- package/fesm2022/tinya-code-gas-admin-ui-display-date-D7zLiDiG.mjs +119 -0
- package/fesm2022/tinya-code-gas-admin-ui-display-date-D7zLiDiG.mjs.map +1 -0
- package/fesm2022/tinya-code-gas-admin-ui-display-file-BiIhqvQC.mjs +125 -0
- package/fesm2022/tinya-code-gas-admin-ui-display-file-BiIhqvQC.mjs.map +1 -0
- package/fesm2022/tinya-code-gas-admin-ui-display-image-C1dhnZi1.mjs +152 -0
- package/fesm2022/tinya-code-gas-admin-ui-display-image-C1dhnZi1.mjs.map +1 -0
- package/fesm2022/tinya-code-gas-admin-ui-display-list-DOEuUbGe.mjs +211 -0
- package/fesm2022/tinya-code-gas-admin-ui-display-list-DOEuUbGe.mjs.map +1 -0
- package/fesm2022/tinya-code-gas-admin-ui-display-number-DDMzr4Hn.mjs +116 -0
- package/fesm2022/tinya-code-gas-admin-ui-display-number-DDMzr4Hn.mjs.map +1 -0
- package/fesm2022/tinya-code-gas-admin-ui-display-text-B8CdVNpv.mjs +111 -0
- package/fesm2022/tinya-code-gas-admin-ui-display-text-B8CdVNpv.mjs.map +1 -0
- package/fesm2022/tinya-code-gas-admin-ui-dropdown-C3blvmE1.mjs +122 -0
- package/fesm2022/tinya-code-gas-admin-ui-dropdown-C3blvmE1.mjs.map +1 -0
- package/fesm2022/tinya-code-gas-admin-ui-email-CT_O5ihA.mjs +107 -0
- package/fesm2022/tinya-code-gas-admin-ui-email-CT_O5ihA.mjs.map +1 -0
- package/fesm2022/tinya-code-gas-admin-ui-empty-state-BMc0QtoT.mjs +109 -0
- package/fesm2022/tinya-code-gas-admin-ui-empty-state-BMc0QtoT.mjs.map +1 -0
- package/fesm2022/tinya-code-gas-admin-ui-error-mapper.service-DY5y1FjE.mjs +45 -0
- package/fesm2022/tinya-code-gas-admin-ui-error-mapper.service-DY5y1FjE.mjs.map +1 -0
- package/fesm2022/tinya-code-gas-admin-ui-field-aria-BD1Wiggc.mjs +50 -0
- package/fesm2022/tinya-code-gas-admin-ui-field-aria-BD1Wiggc.mjs.map +1 -0
- package/fesm2022/tinya-code-gas-admin-ui-field-node-CIK6KNN_.mjs +24 -0
- package/fesm2022/tinya-code-gas-admin-ui-field-node-CIK6KNN_.mjs.map +1 -0
- package/fesm2022/tinya-code-gas-admin-ui-field-path-B4-FTBGZ.mjs +27 -0
- package/fesm2022/tinya-code-gas-admin-ui-field-path-B4-FTBGZ.mjs.map +1 -0
- package/fesm2022/tinya-code-gas-admin-ui-file-Dmxioog7.mjs +408 -0
- package/fesm2022/tinya-code-gas-admin-ui-file-Dmxioog7.mjs.map +1 -0
- package/fesm2022/tinya-code-gas-admin-ui-form-view-CEe7aZ4m.mjs +4516 -0
- package/fesm2022/tinya-code-gas-admin-ui-form-view-CEe7aZ4m.mjs.map +1 -0
- package/fesm2022/tinya-code-gas-admin-ui-format-badge-CK8ct5Wk.mjs +82 -0
- package/fesm2022/tinya-code-gas-admin-ui-format-badge-CK8ct5Wk.mjs.map +1 -0
- package/fesm2022/tinya-code-gas-admin-ui-format-boolean-ClzezZz-.mjs +105 -0
- package/fesm2022/tinya-code-gas-admin-ui-format-boolean-ClzezZz-.mjs.map +1 -0
- package/fesm2022/tinya-code-gas-admin-ui-format-currency-CVmniKml.mjs +65 -0
- package/fesm2022/tinya-code-gas-admin-ui-format-currency-CVmniKml.mjs.map +1 -0
- package/fesm2022/tinya-code-gas-admin-ui-format-date-Dr1YsDd_.mjs +65 -0
- package/fesm2022/tinya-code-gas-admin-ui-format-date-Dr1YsDd_.mjs.map +1 -0
- package/fesm2022/tinya-code-gas-admin-ui-format-number-CWCjpI4n.mjs +65 -0
- package/fesm2022/tinya-code-gas-admin-ui-format-number-CWCjpI4n.mjs.map +1 -0
- package/fesm2022/tinya-code-gas-admin-ui-format-text-DBOgJkH0.mjs +59 -0
- package/fesm2022/tinya-code-gas-admin-ui-format-text-DBOgJkH0.mjs.map +1 -0
- package/fesm2022/tinya-code-gas-admin-ui-format-thumbnail-PCRvxWKt.mjs +94 -0
- package/fesm2022/tinya-code-gas-admin-ui-format-thumbnail-PCRvxWKt.mjs.map +1 -0
- package/fesm2022/tinya-code-gas-admin-ui-gap-sorting-iTdy_DYG.mjs +72 -0
- package/fesm2022/tinya-code-gas-admin-ui-gap-sorting-iTdy_DYG.mjs.map +1 -0
- package/fesm2022/tinya-code-gas-admin-ui-group-h7-O8tt4.mjs +238 -0
- package/fesm2022/tinya-code-gas-admin-ui-group-h7-O8tt4.mjs.map +1 -0
- package/fesm2022/tinya-code-gas-admin-ui-image-7UhJ7nqI.mjs +416 -0
- package/fesm2022/tinya-code-gas-admin-ui-image-7UhJ7nqI.mjs.map +1 -0
- package/fesm2022/tinya-code-gas-admin-ui-kanban-view-Dl0Ol5Kb.mjs +439 -0
- package/fesm2022/tinya-code-gas-admin-ui-kanban-view-Dl0Ol5Kb.mjs.map +1 -0
- package/fesm2022/tinya-code-gas-admin-ui-key-value-nVdgg5at.mjs +513 -0
- package/fesm2022/tinya-code-gas-admin-ui-key-value-nVdgg5at.mjs.map +1 -0
- package/fesm2022/tinya-code-gas-admin-ui-list-DTtCYS4d.mjs +637 -0
- package/fesm2022/tinya-code-gas-admin-ui-list-DTtCYS4d.mjs.map +1 -0
- package/fesm2022/tinya-code-gas-admin-ui-metric-card-DnGMQL8l.mjs +127 -0
- package/fesm2022/tinya-code-gas-admin-ui-metric-card-DnGMQL8l.mjs.map +1 -0
- package/fesm2022/tinya-code-gas-admin-ui-multiselect-PB28e-jl.mjs +251 -0
- package/fesm2022/tinya-code-gas-admin-ui-multiselect-PB28e-jl.mjs.map +1 -0
- package/fesm2022/tinya-code-gas-admin-ui-notification.service-LjSULER6.mjs +66 -0
- package/fesm2022/tinya-code-gas-admin-ui-notification.service-LjSULER6.mjs.map +1 -0
- package/fesm2022/tinya-code-gas-admin-ui-number-JNr7glc8.mjs +114 -0
- package/fesm2022/tinya-code-gas-admin-ui-number-JNr7glc8.mjs.map +1 -0
- package/fesm2022/tinya-code-gas-admin-ui-phone-jR417hPf.mjs +116 -0
- package/fesm2022/tinya-code-gas-admin-ui-phone-jR417hPf.mjs.map +1 -0
- package/fesm2022/tinya-code-gas-admin-ui-quick-actions-DCQ6emmF.mjs +235 -0
- package/fesm2022/tinya-code-gas-admin-ui-quick-actions-DCQ6emmF.mjs.map +1 -0
- package/fesm2022/tinya-code-gas-admin-ui-radio-mode-DxvN07DY.mjs +8 -0
- package/fesm2022/tinya-code-gas-admin-ui-radio-mode-DxvN07DY.mjs.map +1 -0
- package/fesm2022/tinya-code-gas-admin-ui-readonly-text-BL7-4qie.mjs +124 -0
- package/fesm2022/tinya-code-gas-admin-ui-readonly-text-BL7-4qie.mjs.map +1 -0
- package/fesm2022/tinya-code-gas-admin-ui-record-list-BiZfnd_7.mjs +249 -0
- package/fesm2022/tinya-code-gas-admin-ui-record-list-BiZfnd_7.mjs.map +1 -0
- package/fesm2022/tinya-code-gas-admin-ui-relation-drawer.service-B0kT8EiW.mjs +39 -0
- package/fesm2022/tinya-code-gas-admin-ui-relation-drawer.service-B0kT8EiW.mjs.map +1 -0
- package/fesm2022/tinya-code-gas-admin-ui-relation-ykpJ3PYl.mjs +309 -0
- package/fesm2022/tinya-code-gas-admin-ui-relation-ykpJ3PYl.mjs.map +1 -0
- package/fesm2022/tinya-code-gas-admin-ui-render-registry-B88Pme-y.mjs +576 -0
- package/fesm2022/tinya-code-gas-admin-ui-render-registry-B88Pme-y.mjs.map +1 -0
- package/fesm2022/tinya-code-gas-admin-ui-rich-select-CIHaOrbU.mjs +161 -0
- package/fesm2022/tinya-code-gas-admin-ui-rich-select-CIHaOrbU.mjs.map +1 -0
- package/fesm2022/tinya-code-gas-admin-ui-rolldown-runtime-DpJrqANO.mjs +11 -0
- package/fesm2022/tinya-code-gas-admin-ui-schema-catalog-D0Pk0wAN.mjs +685 -0
- package/fesm2022/tinya-code-gas-admin-ui-schema-catalog-D0Pk0wAN.mjs.map +1 -0
- package/fesm2022/tinya-code-gas-admin-ui-seed-DkVZbxPP.mjs +80 -0
- package/fesm2022/tinya-code-gas-admin-ui-seed-DkVZbxPP.mjs.map +1 -0
- package/fesm2022/tinya-code-gas-admin-ui-select-CDwX9yNO.mjs +296 -0
- package/fesm2022/tinya-code-gas-admin-ui-select-CDwX9yNO.mjs.map +1 -0
- package/fesm2022/tinya-code-gas-admin-ui-skeleton-DnWK0tcZ.mjs +119 -0
- package/fesm2022/tinya-code-gas-admin-ui-skeleton-DnWK0tcZ.mjs.map +1 -0
- package/fesm2022/tinya-code-gas-admin-ui-slug-DU58cbiY.mjs +158 -0
- package/fesm2022/tinya-code-gas-admin-ui-slug-DU58cbiY.mjs.map +1 -0
- package/fesm2022/tinya-code-gas-admin-ui-source-options-B9R4mw8-.mjs +26 -0
- package/fesm2022/tinya-code-gas-admin-ui-source-options-B9R4mw8-.mjs.map +1 -0
- package/fesm2022/tinya-code-gas-admin-ui-status-progress-DmeG8htE.mjs +205 -0
- package/fesm2022/tinya-code-gas-admin-ui-status-progress-DmeG8htE.mjs.map +1 -0
- package/fesm2022/tinya-code-gas-admin-ui-string-list-gleasqzD.mjs +432 -0
- package/fesm2022/tinya-code-gas-admin-ui-string-list-gleasqzD.mjs.map +1 -0
- package/fesm2022/tinya-code-gas-admin-ui-template-interpolator-ewv7CkNX.mjs +36 -0
- package/fesm2022/tinya-code-gas-admin-ui-template-interpolator-ewv7CkNX.mjs.map +1 -0
- package/fesm2022/tinya-code-gas-admin-ui-text-CUPmL7W_.mjs +107 -0
- package/fesm2022/tinya-code-gas-admin-ui-text-CUPmL7W_.mjs.map +1 -0
- package/fesm2022/tinya-code-gas-admin-ui-textarea-CGLSIW13.mjs +223 -0
- package/fesm2022/tinya-code-gas-admin-ui-textarea-CGLSIW13.mjs.map +1 -0
- package/fesm2022/tinya-code-gas-admin-ui-time-BcHzsTWf.mjs +107 -0
- package/fesm2022/tinya-code-gas-admin-ui-time-BcHzsTWf.mjs.map +1 -0
- package/fesm2022/tinya-code-gas-admin-ui-upload.service-CZPEPCeN.mjs +60 -0
- package/fesm2022/tinya-code-gas-admin-ui-upload.service-CZPEPCeN.mjs.map +1 -0
- package/fesm2022/tinya-code-gas-admin-ui-url-GO4LZi__.mjs +141 -0
- package/fesm2022/tinya-code-gas-admin-ui-url-GO4LZi__.mjs.map +1 -0
- package/fesm2022/tinya-code-gas-admin-ui.mjs +4738 -0
- package/fesm2022/tinya-code-gas-admin-ui.mjs.map +1 -0
- package/package.json +15 -3
- package/types/tinya-code-gas-admin-ui.d.ts +3634 -0
- package/ng-package.json +0 -7
- package/src/lib/actions/action-registry.spec.ts +0 -52
- package/src/lib/actions/action-registry.ts +0 -126
- package/src/lib/actions/action-service.spec.ts +0 -215
- package/src/lib/actions/action-service.ts +0 -87
- package/src/lib/actions/descriptors.spec.ts +0 -155
- package/src/lib/actions/descriptors.ts +0 -154
- package/src/lib/columns/column-cell.ts +0 -33
- package/src/lib/columns/column-defaults.spec.ts +0 -99
- package/src/lib/columns/column-defaults.ts +0 -84
- package/src/lib/columns/column-format.spec.ts +0 -274
- package/src/lib/columns/descriptors.ts +0 -113
- package/src/lib/columns/format-badge.ts +0 -51
- package/src/lib/columns/format-boolean.ts +0 -58
- package/src/lib/columns/format-currency.ts +0 -30
- package/src/lib/columns/format-date.ts +0 -35
- package/src/lib/columns/format-number.ts +0 -32
- package/src/lib/columns/format-text.ts +0 -23
- package/src/lib/columns/format-thumbnail.ts +0 -63
- package/src/lib/core/adapters/firebase-auth.adapter.ts +0 -6
- package/src/lib/core/guards/auth.guard.spec.ts +0 -88
- package/src/lib/core/guards/auth.guard.ts +0 -48
- package/src/lib/core/guards/unsaved-changes.guard.ts +0 -16
- package/src/lib/core/interceptors/api-error.interceptor.spec.ts +0 -182
- package/src/lib/core/interceptors/api-error.interceptor.ts +0 -87
- package/src/lib/core/models/api.model.ts +0 -104
- package/src/lib/core/models/schema-descriptions.ts +0 -118
- package/src/lib/core/models/schema.model.ts +0 -1097
- package/src/lib/core/registry/composition-validator.spec.ts +0 -358
- package/src/lib/core/registry/composition-validator.ts +0 -270
- package/src/lib/core/registry/field-validators.spec.ts +0 -199
- package/src/lib/core/registry/field-validators.ts +0 -440
- package/src/lib/core/registry/render-host.spec.ts +0 -208
- package/src/lib/core/registry/render-host.ts +0 -646
- package/src/lib/core/registry/render-registry.spec.ts +0 -96
- package/src/lib/core/registry/render-registry.ts +0 -312
- package/src/lib/core/registry/schema-catalog.ts +0 -156
- package/src/lib/core/services/api.service.spec.ts +0 -1142
- package/src/lib/core/services/api.service.ts +0 -371
- package/src/lib/core/services/auth.service.spec.ts +0 -653
- package/src/lib/core/services/auth.service.ts +0 -503
- package/src/lib/core/services/config.service.spec.ts +0 -121
- package/src/lib/core/services/config.service.ts +0 -119
- package/src/lib/core/services/data-source.service.spec.ts +0 -179
- package/src/lib/core/services/data-source.service.ts +0 -142
- package/src/lib/core/services/error-mapper.service.spec.ts +0 -50
- package/src/lib/core/services/error-mapper.service.ts +0 -49
- package/src/lib/core/services/notification.service.ts +0 -73
- package/src/lib/core/services/relation-drawer.service.ts +0 -41
- package/src/lib/core/services/theme.service.spec.ts +0 -106
- package/src/lib/core/services/theme.service.ts +0 -86
- package/src/lib/core/services/transport.spec.ts +0 -198
- package/src/lib/core/services/transport.ts +0 -164
- package/src/lib/core/services/upload.service.ts +0 -81
- package/src/lib/core/testing/categories.schema.ts +0 -125
- package/src/lib/core/testing/orders.schema.ts +0 -372
- package/src/lib/core/testing/products.schema.ts +0 -258
- package/src/lib/core/testing/site.schema.ts +0 -195
- package/src/lib/core/testing/test-catalog.ts +0 -21
- package/src/lib/core/tokens.ts +0 -112
- package/src/lib/core/utils/clipboard.spec.ts +0 -55
- package/src/lib/core/utils/clipboard.ts +0 -90
- package/src/lib/core/utils/context-resolver.spec.ts +0 -95
- package/src/lib/core/utils/context-resolver.ts +0 -101
- package/src/lib/core/utils/response-unwrapper.spec.ts +0 -70
- package/src/lib/core/utils/response-unwrapper.ts +0 -103
- package/src/lib/core/utils/source-options.spec.ts +0 -96
- package/src/lib/core/utils/source-options.ts +0 -54
- package/src/lib/core/utils/template-interpolator.spec.ts +0 -59
- package/src/lib/core/utils/template-interpolator.ts +0 -56
- package/src/lib/core/view-navigation/view-navigation.service.spec.ts +0 -207
- package/src/lib/core/view-navigation/view-navigation.service.ts +0 -132
- package/src/lib/fields/boolean/boolean.descriptor.ts +0 -11
- package/src/lib/fields/boolean/boolean.ts +0 -40
- package/src/lib/fields/color/color.descriptor.ts +0 -12
- package/src/lib/fields/color/color.ts +0 -34
- package/src/lib/fields/currency/currency.descriptor.ts +0 -12
- package/src/lib/fields/currency/currency.ts +0 -67
- package/src/lib/fields/date/date.descriptor.ts +0 -12
- package/src/lib/fields/date/date.ts +0 -36
- package/src/lib/fields/descriptors.ts +0 -122
- package/src/lib/fields/display/display-badge.ts +0 -65
- package/src/lib/fields/display/display-date.ts +0 -39
- package/src/lib/fields/display/display-defaults.ts +0 -84
- package/src/lib/fields/display/display-file.ts +0 -41
- package/src/lib/fields/display/display-image.ts +0 -54
- package/src/lib/fields/display/display-list.ts +0 -107
- package/src/lib/fields/display/display-number.ts +0 -41
- package/src/lib/fields/display/display-text.ts +0 -42
- package/src/lib/fields/display/display.spec.ts +0 -242
- package/src/lib/fields/dropdown/dropdown.descriptor.ts +0 -12
- package/src/lib/fields/dropdown/dropdown.ts +0 -45
- package/src/lib/fields/email/email.descriptor.ts +0 -12
- package/src/lib/fields/email/email.ts +0 -28
- package/src/lib/fields/field-aria.ts +0 -60
- package/src/lib/fields/field-host/field-host.spec.ts +0 -118
- package/src/lib/fields/field-host/field-host.ts +0 -47
- package/src/lib/fields/field-node.ts +0 -88
- package/src/lib/fields/file/file.descriptor.ts +0 -14
- package/src/lib/fields/file/file.spec.ts +0 -134
- package/src/lib/fields/file/file.ts +0 -281
- package/src/lib/fields/group/group.descriptor.ts +0 -21
- package/src/lib/fields/group/group.ts +0 -110
- package/src/lib/fields/image/image.descriptor.ts +0 -12
- package/src/lib/fields/image/image.spec.ts +0 -117
- package/src/lib/fields/image/image.ts +0 -281
- package/src/lib/fields/key-value/key-value.descriptor.ts +0 -11
- package/src/lib/fields/key-value/key-value.ts +0 -283
- package/src/lib/fields/list/list.descriptor.ts +0 -21
- package/src/lib/fields/list/list.ts +0 -376
- package/src/lib/fields/multiselect/multiselect.descriptor.ts +0 -11
- package/src/lib/fields/multiselect/multiselect.spec.ts +0 -142
- package/src/lib/fields/multiselect/multiselect.ts +0 -152
- package/src/lib/fields/number/number.descriptor.ts +0 -12
- package/src/lib/fields/number/number.ts +0 -40
- package/src/lib/fields/phone/phone.descriptor.ts +0 -12
- package/src/lib/fields/phone/phone.ts +0 -46
- package/src/lib/fields/readonly-text/readonly-text.descriptor.ts +0 -12
- package/src/lib/fields/readonly-text/readonly-text.ts +0 -58
- package/src/lib/fields/relation/relation.descriptor.ts +0 -12
- package/src/lib/fields/relation/relation.spec.ts +0 -193
- package/src/lib/fields/relation/relation.ts +0 -224
- package/src/lib/fields/rich-select/rich-select.descriptor.ts +0 -12
- package/src/lib/fields/rich-select/rich-select.ts +0 -175
- package/src/lib/fields/select/radio-mode.ts +0 -25
- package/src/lib/fields/select/select.descriptor.ts +0 -12
- package/src/lib/fields/select/select.spec.ts +0 -162
- package/src/lib/fields/select/select.ts +0 -210
- package/src/lib/fields/slug/slug.descriptor.ts +0 -19
- package/src/lib/fields/slug/slug.ts +0 -67
- package/src/lib/fields/string-list/string-list.descriptor.ts +0 -11
- package/src/lib/fields/string-list/string-list.ts +0 -222
- package/src/lib/fields/text/text.descriptor.ts +0 -12
- package/src/lib/fields/text/text.ts +0 -35
- package/src/lib/fields/textarea/textarea.descriptor.ts +0 -12
- package/src/lib/fields/textarea/textarea.spec.ts +0 -117
- package/src/lib/fields/textarea/textarea.ts +0 -126
- package/src/lib/fields/time/time.descriptor.ts +0 -12
- package/src/lib/fields/time/time.ts +0 -28
- package/src/lib/fields/unknown-render/unknown-render.ts +0 -49
- package/src/lib/fields/url/url.descriptor.ts +0 -12
- package/src/lib/fields/url/url.ts +0 -53
- package/src/lib/gas-admin-ui.spec.ts +0 -21
- package/src/lib/gas-admin-ui.ts +0 -9
- package/src/lib/layouts/descriptors.ts +0 -9
- package/src/lib/pages/dashboard/dashboard.ts +0 -167
- package/src/lib/pages/detail-view/detail-view.spec.ts +0 -333
- package/src/lib/pages/detail-view/detail-view.ts +0 -570
- package/src/lib/pages/drawer-form/drawer-form.ts +0 -228
- package/src/lib/pages/form-view/form-model.ts +0 -88
- package/src/lib/pages/form-view/form-schema.ts +0 -255
- package/src/lib/pages/form-view/form-view.ts +0 -1622
- package/src/lib/pages/list-view/list-view.formats.spec.ts +0 -214
- package/src/lib/pages/list-view/list-view.ts +0 -882
- package/src/lib/pages/login/login.spec.ts +0 -109
- package/src/lib/pages/login/login.ts +0 -227
- package/src/lib/pages/resource-page/resource-page.ts +0 -234
- package/src/lib/shared/components/badge/badge.ts +0 -35
- package/src/lib/shared/components/button/button.ts +0 -65
- package/src/lib/shared/components/confirm-dialog/confirm-dialog.ts +0 -48
- package/src/lib/shared/components/copy-button/copy-button.ts +0 -56
- package/src/lib/shared/components/copy-share/copy-share.spec.ts +0 -122
- package/src/lib/shared/components/copy-share/copy-share.ts +0 -107
- package/src/lib/shared/components/drawer/drawer.spec.ts +0 -127
- package/src/lib/shared/components/drawer/drawer.ts +0 -172
- package/src/lib/shared/components/empty-state/empty-state.ts +0 -30
- package/src/lib/shared/components/modal/modal.spec.ts +0 -149
- package/src/lib/shared/components/modal/modal.ts +0 -145
- package/src/lib/shared/components/post-create-modal/post-create-modal.spec.ts +0 -165
- package/src/lib/shared/components/post-create-modal/post-create-modal.ts +0 -159
- package/src/lib/shared/components/skeleton/skeleton.ts +0 -31
- package/src/lib/shared/components/toast/toast.ts +0 -58
- package/src/lib/shared/utils/col-span.ts +0 -25
- package/src/lib/shared/utils/condition-evaluator.ts +0 -53
- package/src/lib/shared/utils/depends-on.ts +0 -48
- package/src/lib/shared/utils/dialog-focus.ts +0 -28
- package/src/lib/shared/utils/field-path.ts +0 -49
- package/src/lib/shared/utils/gap-sorting.ts +0 -144
- package/src/lib/shared/utils/seed.spec.ts +0 -64
- package/src/lib/shared/utils/seed.ts +0 -149
- package/src/lib/shared/utils/slugify.ts +0 -13
- package/src/lib/shared/utils/validators.ts +0 -41
- package/src/lib/shell/layout/layout.ts +0 -63
- package/src/lib/shell/shell.routes.spec.ts +0 -59
- package/src/lib/shell/shell.routes.ts +0 -122
- package/src/lib/shell/sidebar/sidebar.ts +0 -139
- package/src/lib/shell/theme-toggle/theme-toggle.ts +0 -74
- package/src/lib/views/calendar-view/calendar-model.ts +0 -426
- package/src/lib/views/calendar-view/calendar-view.spec.ts +0 -557
- package/src/lib/views/calendar-view/calendar-view.ts +0 -568
- package/src/lib/views/composer/composer-host.spec.ts +0 -301
- package/src/lib/views/composer/composer-host.ts +0 -248
- package/src/lib/views/composer/composer-imports.spec.ts +0 -54
- package/src/lib/views/composer/view-context.spec.ts +0 -117
- package/src/lib/views/composer/view-context.ts +0 -109
- package/src/lib/views/composer/view-slot.spec.ts +0 -130
- package/src/lib/views/composer/view-slot.ts +0 -178
- package/src/lib/views/descriptors.ts +0 -46
- package/src/lib/views/kanban-view/kanban-model.ts +0 -173
- package/src/lib/views/kanban-view/kanban-view.spec.ts +0 -540
- package/src/lib/views/kanban-view/kanban-view.ts +0 -327
- package/src/lib/widgets/bar-chart/bar-chart.descriptor.ts +0 -17
- package/src/lib/widgets/bar-chart/bar-chart.spec.ts +0 -66
- package/src/lib/widgets/bar-chart/bar-chart.ts +0 -76
- package/src/lib/widgets/chart-line/chart-line.descriptor.ts +0 -17
- package/src/lib/widgets/chart-line/chart-line.spec.ts +0 -113
- package/src/lib/widgets/chart-line/chart-line.ts +0 -219
- package/src/lib/widgets/descriptors.ts +0 -29
- package/src/lib/widgets/metric-card/metric-card.descriptor.ts +0 -41
- package/src/lib/widgets/metric-card/metric-card.spec.ts +0 -52
- package/src/lib/widgets/metric-card/metric-card.ts +0 -48
- package/src/lib/widgets/quick-actions/quick-actions.descriptor.ts +0 -13
- package/src/lib/widgets/quick-actions/quick-actions.spec.ts +0 -61
- package/src/lib/widgets/quick-actions/quick-actions.ts +0 -100
- package/src/lib/widgets/record-list/record-list.descriptor.ts +0 -16
- package/src/lib/widgets/record-list/record-list.spec.ts +0 -79
- package/src/lib/widgets/record-list/record-list.ts +0 -115
- package/src/lib/widgets/series.spec.ts +0 -42
- package/src/lib/widgets/series.ts +0 -56
- package/src/lib/widgets/status-progress/status-progress.descriptor.ts +0 -19
- package/src/lib/widgets/status-progress/status-progress.spec.ts +0 -63
- package/src/lib/widgets/status-progress/status-progress.ts +0 -104
- package/src/lib/widgets/widget-host/widget-host.spec.ts +0 -179
- package/src/lib/widgets/widget-host/widget-host.ts +0 -99
- package/src/public-api.ts +0 -104
- package/tinya-code-gas-admin-ui-0.2.0-alpha.15.tgz +0 -0
- package/tsconfig.lib.json +0 -13
- package/tsconfig.lib.prod.json +0 -11
- package/tsconfig.spec.json +0 -10
|
@@ -1,154 +0,0 @@
|
|
|
1
|
-
import { firstValueFrom } from 'rxjs';
|
|
2
|
-
import { LucideShare2 } from '@lucide/angular';
|
|
3
|
-
|
|
4
|
-
import type { ActionDescriptor } from './action-registry';
|
|
5
|
-
import type {
|
|
6
|
-
CopyShareAction,
|
|
7
|
-
NavigateAction,
|
|
8
|
-
ResourceAction,
|
|
9
|
-
TriggerAction,
|
|
10
|
-
} from '../core/models/schema.model';
|
|
11
|
-
import { buildShareMessage, writeClipboard } from '../core/utils/clipboard';
|
|
12
|
-
import { interpolateTemplate } from '../core/utils/template-interpolator';
|
|
13
|
-
|
|
14
|
-
/**
|
|
15
|
-
* Descriptores de las tres acciones que hoy declara el modelo
|
|
16
|
-
* (R3.10 · `doc/plan-refactor-motor.md` §3.2).
|
|
17
|
-
*
|
|
18
|
-
* Éste es el fichero que **cierra la triplicación de `copy-share`**: la
|
|
19
|
-
* resolución de enlace y de mensaje vivía en `list-view.runAction`,
|
|
20
|
-
* `detail-view.resolveUrl/resolveShareText` y
|
|
21
|
-
* `post-create-modal.resolvedShareUrl/resolvedShareText`. Aquí se resuelve
|
|
22
|
-
* una sola vez (`resolveShare`) y las vistas consumen ese resultado en R3.11.
|
|
23
|
-
*
|
|
24
|
-
* Se registra al importar, desde `action-registry.ts`, con el mismo criterio
|
|
25
|
-
* que el registro de render: sin esperar a DI, porque el registro es la única
|
|
26
|
-
* fuente de verdad en runtime.
|
|
27
|
-
*/
|
|
28
|
-
|
|
29
|
-
/** Enlace y mensaje ya interpolados, listos para copiar o compartir. */
|
|
30
|
-
export interface ResolvedShare {
|
|
31
|
-
url: string;
|
|
32
|
-
shareText: string;
|
|
33
|
-
}
|
|
34
|
-
|
|
35
|
-
/**
|
|
36
|
-
* `true` cuando la acción es de las que las vistas presentan como botón de
|
|
37
|
-
* compartir (`detail-view`, `post-create-modal`).
|
|
38
|
-
*
|
|
39
|
-
* Vive acá y no en la vista porque es **el único sitio donde se puede nombrar
|
|
40
|
-
* `'copy-share'`**: fuera de `descriptors.ts` ninguna vista decide por tipo
|
|
41
|
-
* (criterio de R3.11).
|
|
42
|
-
*/
|
|
43
|
-
export function isShareAction(action: ResourceAction): action is CopyShareAction {
|
|
44
|
-
return action.type === 'copy-share';
|
|
45
|
-
}
|
|
46
|
-
|
|
47
|
-
/**
|
|
48
|
-
* `true` cuando la acción es un `trigger` (acción de dominio del back).
|
|
49
|
-
*
|
|
50
|
-
* Mismo criterio que `isShareAction`: el único sitio que nombra el tipo,
|
|
51
|
-
* para que las vistas no decidan por string literal (R3.11). `detail-view`
|
|
52
|
-
* lo usa en R8.8 para renderizar «Regenerar PIN» junto a los botones de
|
|
53
|
-
* compartir.
|
|
54
|
-
*/
|
|
55
|
-
export function isTriggerAction(action: ResourceAction): action is TriggerAction {
|
|
56
|
-
return action.type === 'trigger';
|
|
57
|
-
}
|
|
58
|
-
|
|
59
|
-
/**
|
|
60
|
-
* Resuelve `urlTemplate` y el mensaje de compartir contra `vars`
|
|
61
|
-
* (config global + registro).
|
|
62
|
-
*
|
|
63
|
-
* **Única implementación** de esta lógica en el admin. El fallback a
|
|
64
|
-
* `share_message_template` (la plantilla que declara el singleton de
|
|
65
|
-
* configuración) lo aplican los tres puntos por igual: hasta ahora la fila
|
|
66
|
-
* del listado no lo honraba y se quedaba sin saludo.
|
|
67
|
-
*/
|
|
68
|
-
export function resolveShare(
|
|
69
|
-
action: CopyShareAction,
|
|
70
|
-
vars: Record<string, unknown>,
|
|
71
|
-
): ResolvedShare {
|
|
72
|
-
const template =
|
|
73
|
-
action.shareTextTemplate || (vars['share_message_template'] as string | undefined);
|
|
74
|
-
return {
|
|
75
|
-
url: interpolateTemplate(action.urlTemplate, vars),
|
|
76
|
-
shareText: template ? interpolateTemplate(template, vars) : '',
|
|
77
|
-
};
|
|
78
|
-
}
|
|
79
|
-
|
|
80
|
-
/** `copy-share` — copia (o comparte) el mensaje completo del enlace público. */
|
|
81
|
-
const copyShare: ActionDescriptor = {
|
|
82
|
-
type: 'copy-share',
|
|
83
|
-
surfaces: ['row', 'detail', 'post-create'],
|
|
84
|
-
// Icono de la fila del listado: la vista sólo decide entre «icono» y
|
|
85
|
-
// «rótulo», nunca por tipo. Se pasa la CLASE, no el nombre: sin
|
|
86
|
-
// `provideLucideIcons` no hay riesgo de `Unable to resolve icon`.
|
|
87
|
-
icon: LucideShare2,
|
|
88
|
-
run: async (ctx, action) => {
|
|
89
|
-
const { url, shareText } = resolveShare(action as CopyShareAction, ctx.vars);
|
|
90
|
-
const message = buildShareMessage(url, shareText);
|
|
91
|
-
const copied = await writeClipboard(message);
|
|
92
|
-
if (copied) {
|
|
93
|
-
ctx.toast.success('Mensaje copiado al portapapeles.');
|
|
94
|
-
} else {
|
|
95
|
-
ctx.toast.info(`Copiar manualmente: ${message}`);
|
|
96
|
-
}
|
|
97
|
-
return { ok: true };
|
|
98
|
-
},
|
|
99
|
-
};
|
|
100
|
-
|
|
101
|
-
/**
|
|
102
|
-
* `navigate` — abre la plantilla resuelta: URL absoluta en otra pestaña,
|
|
103
|
-
* ruta interna en el router. `NavigateAction.type` es opcional, así que el
|
|
104
|
-
* servicio llega acá también con una acción que no declara `type`.
|
|
105
|
-
*/
|
|
106
|
-
const navigate: ActionDescriptor = {
|
|
107
|
-
type: 'navigate',
|
|
108
|
-
surfaces: ['row', 'detail'],
|
|
109
|
-
run: async (ctx, action) => {
|
|
110
|
-
const url = interpolateTemplate((action as NavigateAction).urlTemplate, ctx.vars);
|
|
111
|
-
if (/^https?:\/\//.test(url)) {
|
|
112
|
-
window.open(url, '_blank', 'noopener');
|
|
113
|
-
} else {
|
|
114
|
-
await ctx.router.navigateByUrl(url);
|
|
115
|
-
}
|
|
116
|
-
return { ok: true };
|
|
117
|
-
},
|
|
118
|
-
};
|
|
119
|
-
|
|
120
|
-
/**
|
|
121
|
-
* `trigger` — dispara una **acción de dominio declarada en el back** (B3).
|
|
122
|
-
*
|
|
123
|
-
* **R3.12:** el destino ya no lo declara el schema (`endpoint` y `method`
|
|
124
|
-
* murieron en este cambio). Se deriva de `resource` + clave del registro +
|
|
125
|
-
* `name` (o `id`), que es exactamente la ruta que el back publica solo en
|
|
126
|
-
* R5.4: `POST /admin/<recurso>/:id/actions/<name>`.
|
|
127
|
-
*
|
|
128
|
-
* ⚠️ **O2:** la ruta lógica viaja en el `path` del envelope, no en la URL —
|
|
129
|
-
* el HTTP sigue siendo el `POST API_URL` de siempre.
|
|
130
|
-
*
|
|
131
|
-
* Siempre pide confirmación: es la única acción de las tres que puede dejar
|
|
132
|
-
* una cola de trabajo hecha.
|
|
133
|
-
*/
|
|
134
|
-
const trigger: ActionDescriptor = {
|
|
135
|
-
type: 'trigger',
|
|
136
|
-
surfaces: ['row', 'detail'],
|
|
137
|
-
confirm: true,
|
|
138
|
-
run: async (ctx, action) => {
|
|
139
|
-
const declared = action as TriggerAction;
|
|
140
|
-
const rawKey = ctx.record?.[ctx.keyField];
|
|
141
|
-
if (rawKey === null || rawKey === undefined || rawKey === '') {
|
|
142
|
-
throw new Error(
|
|
143
|
-
`La acción «${declared.label}» necesita la clave ${ctx.keyField} del registro.`,
|
|
144
|
-
);
|
|
145
|
-
}
|
|
146
|
-
const name = declared.name ?? declared.id;
|
|
147
|
-
const route = `/admin/${ctx.resource}/${String(rawKey)}/actions/${name}`;
|
|
148
|
-
await firstValueFrom(ctx.api.request('POST', route));
|
|
149
|
-
return { ok: true, reload: true };
|
|
150
|
-
},
|
|
151
|
-
};
|
|
152
|
-
|
|
153
|
-
/** Catálogo — una línea por acción, en el orden en que se registran. */
|
|
154
|
-
export const actionDescriptors: readonly ActionDescriptor[] = [copyShare, navigate, trigger];
|
|
@@ -1,33 +0,0 @@
|
|
|
1
|
-
import type { FieldSchema, ListColumn } from '../core/models/schema.model';
|
|
2
|
-
|
|
3
|
-
type Row = Record<string, unknown>;
|
|
4
|
-
|
|
5
|
-
/**
|
|
6
|
-
* Contexto de una celda de listado (R4.3 · `kind: 'column-format'`).
|
|
7
|
-
*
|
|
8
|
-
* Es lo que antes cada `@case` de `list-view` pedía a la vista como
|
|
9
|
-
* `row`/`column` y lo que el descriptor cuelga de `extras.data`: el
|
|
10
|
-
* componente de formato no conoce a la vista, sólo a su celda.
|
|
11
|
-
*
|
|
12
|
-
* `fields`, `canUpdate`, `saving`, `currency` y `toggle` viajan acá porque
|
|
13
|
-
* son decisiones de la vista (permisos, moneda del schema, escritura de la
|
|
14
|
-
* fila); un formato nuevo no necesita tocar `list-view` para leerlas.
|
|
15
|
-
*/
|
|
16
|
-
export interface ColumnCell {
|
|
17
|
-
/** Fila completa. */
|
|
18
|
-
row: Row;
|
|
19
|
-
/** Columna que se pinta (`key`, `label`, `format`). */
|
|
20
|
-
column: ListColumn;
|
|
21
|
-
/** Campos del recurso: de acá salen `trueLabel`/`falseLabel`. */
|
|
22
|
-
fields: FieldSchema[];
|
|
23
|
-
/** `titleField` de la fila, para el `aria-label` del interruptor. */
|
|
24
|
-
title: string;
|
|
25
|
-
/** ¿La fila admite escritura? (`ListView.canUpdate`) */
|
|
26
|
-
canUpdate: boolean;
|
|
27
|
-
/** ¿La fila está guardando? (`ListView.savingKey === keyOf(row)`) */
|
|
28
|
-
saving: boolean;
|
|
29
|
-
/** Moneda del schema, para el formato `currency`. */
|
|
30
|
-
currency: string;
|
|
31
|
-
/** Escribe el valor en la fila. Sólo lo usa el interruptor `active`. */
|
|
32
|
-
toggle?: (checked: boolean) => void;
|
|
33
|
-
}
|
|
@@ -1,99 +0,0 @@
|
|
|
1
|
-
import { describe, expect, it } from 'vitest';
|
|
2
|
-
|
|
3
|
-
import { SUPPORTED_FIELD_TYPE_LIST } from '../fields/descriptors';
|
|
4
|
-
import type { FieldSchema, ListColumn } from '../core/models/schema.model';
|
|
5
|
-
import { FORMATO_POR_DEFECTO, formatoDeColumna, formatoPorTipo } from './column-defaults';
|
|
6
|
-
|
|
7
|
-
/**
|
|
8
|
-
* R4.4 — «declarada, no implícita»: la tabla de formato por defecto.
|
|
9
|
-
*
|
|
10
|
-
* Dos promesas: que la tabla cubra **todos** los tipos del catálogo, y que
|
|
11
|
-
* una columna sin `format` pinta lo que la tabla dice — no un `?? 'text'`
|
|
12
|
-
* implícito que dependía de la memoria de quien escribía el schema.
|
|
13
|
-
*/
|
|
14
|
-
describe('R4.4 — formato por defecto según field.type', () => {
|
|
15
|
-
const columna = (parcial: Partial<ListColumn> & Pick<ListColumn, 'key'>): ListColumn => ({
|
|
16
|
-
label: parcial.key,
|
|
17
|
-
...parcial,
|
|
18
|
-
});
|
|
19
|
-
const campo = (key: string, type: FieldSchema['type']): FieldSchema =>
|
|
20
|
-
({ key, label: key, type }) as FieldSchema;
|
|
21
|
-
|
|
22
|
-
it('cubre exactamente el catálogo de tipos de campo', () => {
|
|
23
|
-
// El `Record<FieldType, …>` ya lo exige en compilación; el test además
|
|
24
|
-
// fija que ningún tipo se quede sin fila ni aparezca una fantasma.
|
|
25
|
-
expect(Object.keys(FORMATO_POR_DEFECTO).sort()).toEqual([...SUPPORTED_FIELD_TYPE_LIST].sort());
|
|
26
|
-
expect(SUPPORTED_FIELD_TYPE_LIST.length).toBe(24);
|
|
27
|
-
});
|
|
28
|
-
|
|
29
|
-
it('repasa la paridad tipo a tipo con el display del detalle', () => {
|
|
30
|
-
const esperado: Record<string, string> = {
|
|
31
|
-
text: 'text',
|
|
32
|
-
textarea: 'text',
|
|
33
|
-
slug: 'text',
|
|
34
|
-
url: 'text',
|
|
35
|
-
email: 'text',
|
|
36
|
-
phone: 'text',
|
|
37
|
-
'readonly-text': 'text',
|
|
38
|
-
relation: 'text',
|
|
39
|
-
group: 'text',
|
|
40
|
-
'key-value': 'text',
|
|
41
|
-
'string-list': 'text',
|
|
42
|
-
multiselect: 'text',
|
|
43
|
-
time: 'text',
|
|
44
|
-
list: 'text',
|
|
45
|
-
date: 'date',
|
|
46
|
-
number: 'number',
|
|
47
|
-
currency: 'currency',
|
|
48
|
-
boolean: 'boolean',
|
|
49
|
-
select: 'badge',
|
|
50
|
-
image: 'thumbnail',
|
|
51
|
-
file: 'text',
|
|
52
|
-
color: 'text',
|
|
53
|
-
'rich-select': 'badge',
|
|
54
|
-
dropdown: 'badge',
|
|
55
|
-
};
|
|
56
|
-
expect(Object.keys(FORMATO_POR_DEFECTO).sort()).toEqual(Object.keys(esperado).sort());
|
|
57
|
-
for (const [tipo, formato] of Object.entries(esperado)) {
|
|
58
|
-
expect(formatoPorTipo(tipo), `tipo ${tipo}`).toBe(formato);
|
|
59
|
-
}
|
|
60
|
-
});
|
|
61
|
-
|
|
62
|
-
it('la declaración del schema manda sobre la tabla', () => {
|
|
63
|
-
expect(
|
|
64
|
-
formatoDeColumna(columna({ key: 'status', format: 'text' }), [campo('status', 'select')]),
|
|
65
|
-
).toBe('text');
|
|
66
|
-
expect(
|
|
67
|
-
formatoDeColumna(columna({ key: 'price', format: 'badge' }), [campo('price', 'currency')]),
|
|
68
|
-
).toBe('badge');
|
|
69
|
-
});
|
|
70
|
-
|
|
71
|
-
it('sin formato declarado usa el tipo del campo', () => {
|
|
72
|
-
const campos = [
|
|
73
|
-
campo('status', 'select'),
|
|
74
|
-
campo('pickup_date', 'date'),
|
|
75
|
-
campo('active', 'boolean'),
|
|
76
|
-
campo('photo', 'image'),
|
|
77
|
-
campo('price', 'currency'),
|
|
78
|
-
campo('name', 'slug'),
|
|
79
|
-
];
|
|
80
|
-
expect(formatoDeColumna(columna({ key: 'status' }), campos)).toBe('badge');
|
|
81
|
-
expect(formatoDeColumna(columna({ key: 'pickup_date' }), campos)).toBe('date');
|
|
82
|
-
expect(formatoDeColumna(columna({ key: 'active' }), campos)).toBe('boolean');
|
|
83
|
-
expect(formatoDeColumna(columna({ key: 'photo' }), campos)).toBe('thumbnail');
|
|
84
|
-
expect(formatoDeColumna(columna({ key: 'price' }), campos)).toBe('currency');
|
|
85
|
-
expect(formatoDeColumna(columna({ key: 'name' }), campos)).toBe('text');
|
|
86
|
-
});
|
|
87
|
-
|
|
88
|
-
it('una columna sin campo asociado o con tipo mal escrito degrada a text', () => {
|
|
89
|
-
// El `@default` del `@switch` que R4.3 reemplazó: la degradación es la
|
|
90
|
-
// misma y nunca lanza.
|
|
91
|
-
expect(formatoDeColumna(columna({ key: 'inventada' }), [campo('otra', 'text')])).toBe('text');
|
|
92
|
-
expect(formatoPorTipo('holograma')).toBeUndefined();
|
|
93
|
-
expect(
|
|
94
|
-
formatoDeColumna(columna({ key: 'raro' }), [
|
|
95
|
-
campo('raro', 'holograma' as FieldSchema['type']),
|
|
96
|
-
]),
|
|
97
|
-
).toBe('text');
|
|
98
|
-
});
|
|
99
|
-
});
|
|
@@ -1,84 +0,0 @@
|
|
|
1
|
-
import type {
|
|
2
|
-
FieldSchema,
|
|
3
|
-
FieldType,
|
|
4
|
-
ListColumn,
|
|
5
|
-
ListColumnFormat,
|
|
6
|
-
} from '../core/models/schema.model';
|
|
7
|
-
|
|
8
|
-
/**
|
|
9
|
-
* **Tabla de formato de columna por defecto según `field.type`** (R4.4 del
|
|
10
|
-
* plan): «declarada, no implícita» — `column.format` sólo sirve para
|
|
11
|
-
* sobreescribirla.
|
|
12
|
-
*
|
|
13
|
-
* Es la hermana de `fields/display/display-defaults.ts` (R4.1): un solo
|
|
14
|
-
* sitio decide qué pinta una columna cuando el schema no dice nada, en vez
|
|
15
|
-
* de un `?? 'text'` repartido por la vista. El vocabulario se alinea con el
|
|
16
|
-
* display del detalle — `select` es chip, `date` es fecha, `image` es
|
|
17
|
-
* miniatura — con las dos concesiones que sólo el listado puede hacer:
|
|
18
|
-
* `boolean` baja al interruptor (R4.3) en vez de al chip, y `list`, que en
|
|
19
|
-
* la ficha es timeline, acá sigue siendo texto porque el registro de
|
|
20
|
-
* formatos no tiene timeline (D41).
|
|
21
|
-
*
|
|
22
|
-
* `Record<FieldType, …>` es el **tripwire de exhaustividad en compilación**:
|
|
23
|
-
* un tipo nuevo obliga a elegir su formato de lista antes de que el build
|
|
24
|
-
* pase, y un tipo retirado obliga a limpiar la entrada.
|
|
25
|
-
*/
|
|
26
|
-
export const FORMATO_POR_DEFECTO: Record<FieldType, ListColumnFormat> = {
|
|
27
|
-
text: 'text',
|
|
28
|
-
textarea: 'text',
|
|
29
|
-
slug: 'text',
|
|
30
|
-
url: 'text',
|
|
31
|
-
email: 'text',
|
|
32
|
-
phone: 'text',
|
|
33
|
-
'readonly-text': 'text',
|
|
34
|
-
relation: 'text',
|
|
35
|
-
group: 'text',
|
|
36
|
-
'key-value': 'text',
|
|
37
|
-
'string-list': 'text',
|
|
38
|
-
multiselect: 'text',
|
|
39
|
-
time: 'text',
|
|
40
|
-
list: 'text',
|
|
41
|
-
date: 'date',
|
|
42
|
-
number: 'number',
|
|
43
|
-
currency: 'currency',
|
|
44
|
-
boolean: 'boolean',
|
|
45
|
-
select: 'badge',
|
|
46
|
-
image: 'thumbnail',
|
|
47
|
-
// Un archivo no tiene miniatura: en la lista va como URL en crudo (R9.5.3).
|
|
48
|
-
file: 'text',
|
|
49
|
-
// Un color se pinta como texto (el hex #RRGGBB) en el listado.
|
|
50
|
-
color: 'text',
|
|
51
|
-
// Un rich-select se pinta como badge con el label de la opción.
|
|
52
|
-
'rich-select': 'badge',
|
|
53
|
-
// Un dropdown se pinta como badge con el label de la opción.
|
|
54
|
-
dropdown: 'badge',
|
|
55
|
-
};
|
|
56
|
-
|
|
57
|
-
/**
|
|
58
|
-
* Formato por defecto para un `type`, o `undefined` si el schema lo escribió
|
|
59
|
-
* mal (tipo fuera de la unión). La degradación — igual que `displayLoader()` —
|
|
60
|
-
* es `text`, que era el `@default` del `@switch` que R4.3 reemplazó.
|
|
61
|
-
*/
|
|
62
|
-
export function formatoPorTipo(type: string): ListColumnFormat | undefined {
|
|
63
|
-
return Object.prototype.hasOwnProperty.call(FORMATO_POR_DEFECTO, type)
|
|
64
|
-
? FORMATO_POR_DEFECTO[type as FieldType]
|
|
65
|
-
: undefined;
|
|
66
|
-
}
|
|
67
|
-
|
|
68
|
-
/**
|
|
69
|
-
* Formato final de una columna: la declaración del schema si existe, si no,
|
|
70
|
-
* la tabla de arriba según el `type` del campo con esa `key`, y si la columna
|
|
71
|
-
* ni siquiera aparece en `fields` (columna derivada o key inventada), `text`.
|
|
72
|
-
*
|
|
73
|
-
* Es la única puerta por la que `list-view` decide qué pinta una celda.
|
|
74
|
-
*/
|
|
75
|
-
export function formatoDeColumna(
|
|
76
|
-
column: ListColumn,
|
|
77
|
-
fields: readonly FieldSchema[],
|
|
78
|
-
): ListColumnFormat {
|
|
79
|
-
if (column.format) {
|
|
80
|
-
return column.format;
|
|
81
|
-
}
|
|
82
|
-
const field = fields.find((candidate) => candidate.key === column.key);
|
|
83
|
-
return (field ? formatoPorTipo(field.type) : undefined) ?? 'text';
|
|
84
|
-
}
|
|
@@ -1,274 +0,0 @@
|
|
|
1
|
-
import { Component, input } from '@angular/core';
|
|
2
|
-
import { TestBed } from '@angular/core/testing';
|
|
3
|
-
import { beforeEach, describe, expect, it, vi } from 'vitest';
|
|
4
|
-
|
|
5
|
-
import type { ListColumn } from '../core/models/schema.model';
|
|
6
|
-
import { RenderHost } from '../core/registry/render-host';
|
|
7
|
-
import { COLUMN_FORMAT_TYPES } from './descriptors';
|
|
8
|
-
import type { ColumnCell } from './column-cell';
|
|
9
|
-
|
|
10
|
-
/**
|
|
11
|
-
* Los 7 formatos de columna (R4.3 · `kind: 'column-format'`).
|
|
12
|
-
*
|
|
13
|
-
* Cada caso es un `@case` del `@switch` que vivía en `list-view`: si el
|
|
14
|
-
* formato pinta distinto de lo que pintaba allí, este test se pone rojo. Se
|
|
15
|
-
* monta por `RenderHost` y no por el componente suelto para cubrir también
|
|
16
|
-
* la resolución del registro (descriptor, `inputs`, degradación).
|
|
17
|
-
*/
|
|
18
|
-
@Component({
|
|
19
|
-
selector: 'spec-column-host',
|
|
20
|
-
imports: [RenderHost],
|
|
21
|
-
template: `
|
|
22
|
-
<app-render-host kind="column-format" [type]="formato()" [config]="column()" [data]="cell()" />
|
|
23
|
-
`,
|
|
24
|
-
})
|
|
25
|
-
class Host {
|
|
26
|
-
readonly formato = input.required<string>();
|
|
27
|
-
readonly column = input.required<ListColumn>();
|
|
28
|
-
readonly cell = input.required<ColumnCell>();
|
|
29
|
-
}
|
|
30
|
-
|
|
31
|
-
describe('R4.3 — formatos de columna registrados', () => {
|
|
32
|
-
beforeEach(async () => {
|
|
33
|
-
await TestBed.configureTestingModule({ imports: [Host] }).compileComponents();
|
|
34
|
-
});
|
|
35
|
-
|
|
36
|
-
/**
|
|
37
|
-
* Monta el host y espera el `load()` perezoso del formato.
|
|
38
|
-
*
|
|
39
|
-
* `whenStable()` no cubre el `import()` dinámico por sí solo, así que se
|
|
40
|
-
* espera acotadamente mientras quede `<app-skeleton>`; si el formato nunca
|
|
41
|
-
* monta, la espera se agota y falla la aserción del test.
|
|
42
|
-
*/
|
|
43
|
-
async function pintar(column: ListColumn, cell: Partial<ColumnCell> = {}): Promise<HTMLElement> {
|
|
44
|
-
const fixture = TestBed.createComponent(Host);
|
|
45
|
-
fixture.componentRef.setInput('formato', column.format ?? 'text');
|
|
46
|
-
fixture.componentRef.setInput('column', column);
|
|
47
|
-
fixture.componentRef.setInput('cell', celda({ column, ...cell }));
|
|
48
|
-
fixture.detectChanges();
|
|
49
|
-
await fixture.whenStable();
|
|
50
|
-
for (
|
|
51
|
-
let intento = 0;
|
|
52
|
-
intento < 100 && fixture.nativeElement.querySelector('app-skeleton');
|
|
53
|
-
intento++
|
|
54
|
-
) {
|
|
55
|
-
fixture.detectChanges();
|
|
56
|
-
await new Promise((resolve) => setTimeout(resolve, 1));
|
|
57
|
-
}
|
|
58
|
-
fixture.detectChanges();
|
|
59
|
-
return fixture.nativeElement as HTMLElement;
|
|
60
|
-
}
|
|
61
|
-
|
|
62
|
-
/** Texto de la celda con los espacios duros de `Intl` normalizados. */
|
|
63
|
-
function texto(nodo: HTMLElement): string {
|
|
64
|
-
return (nodo.textContent ?? '').replace(/\u00a0/g, ' ');
|
|
65
|
-
}
|
|
66
|
-
|
|
67
|
-
/** Celda mínima; cada caso sobreescribe lo que necesita. */
|
|
68
|
-
function celda(parcial: { column: ListColumn } & Partial<ColumnCell>): ColumnCell {
|
|
69
|
-
return {
|
|
70
|
-
row: {},
|
|
71
|
-
fields: [],
|
|
72
|
-
title: 'Una fila',
|
|
73
|
-
canUpdate: false,
|
|
74
|
-
saving: false,
|
|
75
|
-
currency: 'PEN',
|
|
76
|
-
...parcial,
|
|
77
|
-
};
|
|
78
|
-
}
|
|
79
|
-
|
|
80
|
-
it('los 7 formatos de la unión están registrados', () => {
|
|
81
|
-
// `ListColumnFormat` vive en tipos y el manifiesto en runtime: sólo un
|
|
82
|
-
// test reconcilia los dos (mismo tripwire que campos y widgets).
|
|
83
|
-
expect(COLUMN_FORMAT_TYPES.length).toBe(7);
|
|
84
|
-
});
|
|
85
|
-
|
|
86
|
-
describe('text', () => {
|
|
87
|
-
const col: ListColumn = { key: 'nombre', label: 'Nombre', format: 'text' };
|
|
88
|
-
|
|
89
|
-
it('pinta el valor crudo', async () => {
|
|
90
|
-
const nodo = await pintar(col, { row: { nombre: 'ORD-42' } });
|
|
91
|
-
expect(nodo.textContent).toContain('ORD-42');
|
|
92
|
-
});
|
|
93
|
-
|
|
94
|
-
it('null y undefined salen vacíos', async () => {
|
|
95
|
-
expect((await pintar(col, { row: { nombre: null } })).textContent?.trim()).toBe('');
|
|
96
|
-
expect((await pintar(col, { row: {} })).textContent?.trim()).toBe('');
|
|
97
|
-
});
|
|
98
|
-
|
|
99
|
-
it('un objeto sale [object Object] (degradación heredada)', async () => {
|
|
100
|
-
const nodo = await pintar(col, { row: { nombre: { a: 1 } } });
|
|
101
|
-
expect(nodo.textContent).toContain('[object Object]');
|
|
102
|
-
});
|
|
103
|
-
});
|
|
104
|
-
|
|
105
|
-
describe('number', () => {
|
|
106
|
-
const col: ListColumn = { key: 'stock', label: 'Stock', format: 'number' };
|
|
107
|
-
|
|
108
|
-
it('agrupa con separador es-PE', async () => {
|
|
109
|
-
const nodo = await pintar(col, { row: { stock: 1234567.5 } });
|
|
110
|
-
expect(nodo.textContent).toContain('1,234,567.5');
|
|
111
|
-
});
|
|
112
|
-
|
|
113
|
-
it('no numérico sale crudo y null sigue saliendo «0»', async () => {
|
|
114
|
-
expect((await pintar(col, { row: { stock: 'varios' } })).textContent).toContain('varios');
|
|
115
|
-
// Quirk heredado de `formatNumber`: `Number(null)` es 0 y por eso la
|
|
116
|
-
// lista ya mostraba «0» en vez de vacío. Se conserva — cambiarlo es
|
|
117
|
-
// otra decisión, no este registro.
|
|
118
|
-
expect((await pintar(col, { row: { stock: null } })).textContent?.trim()).toBe('0');
|
|
119
|
-
});
|
|
120
|
-
});
|
|
121
|
-
|
|
122
|
-
describe('currency', () => {
|
|
123
|
-
const col: ListColumn = { key: 'price', label: 'Precio', format: 'currency' };
|
|
124
|
-
|
|
125
|
-
it('usa la moneda que entrega la vista', async () => {
|
|
126
|
-
const nodo = await pintar(col, { row: { price: 1234.5 } });
|
|
127
|
-
// `Intl` separa el símbolo con espacio duro (U+00A0): se normaliza para
|
|
128
|
-
// que el aserto no dependa de ese byte.
|
|
129
|
-
expect(texto(nodo)).toContain('S/ 1,234.50');
|
|
130
|
-
});
|
|
131
|
-
|
|
132
|
-
it('cambia de moneda con la celda', async () => {
|
|
133
|
-
const nodo = await pintar(col, { row: { price: 1234.5 }, currency: 'USD' });
|
|
134
|
-
expect(texto(nodo)).toContain('USD 1,234.50');
|
|
135
|
-
});
|
|
136
|
-
});
|
|
137
|
-
|
|
138
|
-
describe('date', () => {
|
|
139
|
-
const col: ListColumn = { key: 'fecha', label: 'Fecha', format: 'date' };
|
|
140
|
-
|
|
141
|
-
it('formato corto es-PE en UTC', async () => {
|
|
142
|
-
const nodo = await pintar(col, { row: { fecha: '2026-12-25' } });
|
|
143
|
-
expect(nodo.textContent).toContain('25/12/2026');
|
|
144
|
-
});
|
|
145
|
-
|
|
146
|
-
it('no parseable sale crudo', async () => {
|
|
147
|
-
expect((await pintar(col, { row: { fecha: 'ayer' } })).textContent).toContain('ayer');
|
|
148
|
-
});
|
|
149
|
-
});
|
|
150
|
-
|
|
151
|
-
describe('badge', () => {
|
|
152
|
-
const col: ListColumn = { key: 'status', label: 'Estado', format: 'badge' };
|
|
153
|
-
|
|
154
|
-
it('clasifica el estado como el display del detalle (D41)', async () => {
|
|
155
|
-
// `warning` se pinta como `accent` (bg-accent/20 + text-accent-text).
|
|
156
|
-
const pendiente = await pintar(col, { row: { status: 'PENDIENTE' } });
|
|
157
|
-
expect(pendiente.querySelector('app-badge')?.className).toContain('bg-accent/20');
|
|
158
|
-
|
|
159
|
-
const entregado = await pintar(col, { row: { status: 'ENTREGADO' } });
|
|
160
|
-
expect(entregado.querySelector('app-badge')?.className).toContain('bg-success');
|
|
161
|
-
|
|
162
|
-
const cancelado = await pintar(col, { row: { status: 'Cancelado' } });
|
|
163
|
-
expect(cancelado.querySelector('app-badge')?.className).toContain('bg-danger');
|
|
164
|
-
|
|
165
|
-
const otro = await pintar(col, { row: { status: 'Vigente' } });
|
|
166
|
-
expect(otro.querySelector('app-badge')?.className).toContain('bg-neutral');
|
|
167
|
-
});
|
|
168
|
-
|
|
169
|
-
it('un select pinta la etiqueta de la opción, no el valor crudo', async () => {
|
|
170
|
-
const nodo = await pintar(col, {
|
|
171
|
-
row: { status: 'preparacion' },
|
|
172
|
-
fields: [
|
|
173
|
-
{
|
|
174
|
-
key: 'status',
|
|
175
|
-
label: 'Estado',
|
|
176
|
-
type: 'select',
|
|
177
|
-
options: [
|
|
178
|
-
{ value: 'preparacion', label: 'En preparación' },
|
|
179
|
-
{ value: 'entregado', label: 'Entregado' },
|
|
180
|
-
],
|
|
181
|
-
},
|
|
182
|
-
],
|
|
183
|
-
});
|
|
184
|
-
expect(nodo.textContent).toContain('En preparación');
|
|
185
|
-
});
|
|
186
|
-
|
|
187
|
-
it('el vacío sale «Sin dato» en danger', async () => {
|
|
188
|
-
const nodo = await pintar(col, { row: { status: '' } });
|
|
189
|
-
expect(nodo.textContent).toContain('Sin dato');
|
|
190
|
-
expect(nodo.querySelector('app-badge')?.className).toContain('bg-danger');
|
|
191
|
-
});
|
|
192
|
-
|
|
193
|
-
it('un booleano usa trueLabel/falseLabel del campo', async () => {
|
|
194
|
-
const nodo = await pintar(col, {
|
|
195
|
-
row: { status: true },
|
|
196
|
-
fields: [{ key: 'status', label: 'Estado', type: 'boolean', trueLabel: 'Vigente' }],
|
|
197
|
-
});
|
|
198
|
-
expect(nodo.textContent).toContain('Vigente');
|
|
199
|
-
expect(nodo.querySelector('app-badge')?.className).toContain('bg-success');
|
|
200
|
-
});
|
|
201
|
-
});
|
|
202
|
-
|
|
203
|
-
describe('boolean', () => {
|
|
204
|
-
const col: ListColumn = { key: 'active', label: 'Activo', format: 'boolean' };
|
|
205
|
-
const booleano = { key: 'active', label: 'Activo', type: 'boolean' as const };
|
|
206
|
-
|
|
207
|
-
it('con permiso y campo booleano dibuja el interruptor', async () => {
|
|
208
|
-
const nodo = await pintar(col, {
|
|
209
|
-
row: { active: true },
|
|
210
|
-
fields: [booleano],
|
|
211
|
-
canUpdate: true,
|
|
212
|
-
});
|
|
213
|
-
const box = nodo.querySelector<HTMLInputElement>('input[type="checkbox"]');
|
|
214
|
-
expect(box, 'debe dibujarse el interruptor').not.toBeNull();
|
|
215
|
-
expect(box?.checked).toBe(true);
|
|
216
|
-
expect(box?.getAttribute('aria-label')).toBe('Activo: Una fila');
|
|
217
|
-
});
|
|
218
|
-
|
|
219
|
-
it('sin permiso cae al badge', async () => {
|
|
220
|
-
const nodo = await pintar(col, { row: { active: true }, fields: [booleano] });
|
|
221
|
-
expect(nodo.querySelector('input[type="checkbox"]')).toBeNull();
|
|
222
|
-
expect(nodo.textContent).toContain('Sí');
|
|
223
|
-
});
|
|
224
|
-
|
|
225
|
-
it('mientras la fila guarda, el interruptor queda bloqueado', async () => {
|
|
226
|
-
const nodo = await pintar(col, {
|
|
227
|
-
row: { active: false },
|
|
228
|
-
fields: [booleano],
|
|
229
|
-
canUpdate: true,
|
|
230
|
-
saving: true,
|
|
231
|
-
});
|
|
232
|
-
const box = nodo.querySelector<HTMLInputElement>('input[type="checkbox"]');
|
|
233
|
-
expect(box?.disabled, 'mientras guarda, la fila está bloqueada').toBe(true);
|
|
234
|
-
});
|
|
235
|
-
|
|
236
|
-
it('al alternar escribe por la celda', async () => {
|
|
237
|
-
const toggle = vi.fn();
|
|
238
|
-
const nodo = await pintar(col, {
|
|
239
|
-
row: { active: false },
|
|
240
|
-
fields: [booleano],
|
|
241
|
-
canUpdate: true,
|
|
242
|
-
toggle,
|
|
243
|
-
});
|
|
244
|
-
nodo.querySelector<HTMLInputElement>('input[type="checkbox"]')!.click();
|
|
245
|
-
expect(toggle).toHaveBeenCalledWith(true);
|
|
246
|
-
});
|
|
247
|
-
});
|
|
248
|
-
|
|
249
|
-
describe('thumbnail', () => {
|
|
250
|
-
const col: ListColumn = { key: 'image_url', label: 'Imagen', format: 'thumbnail' };
|
|
251
|
-
|
|
252
|
-
it('pinta la imagen con su alt', async () => {
|
|
253
|
-
const nodo = await pintar(col, { row: { image_url: 'https://x.test/a.png' } });
|
|
254
|
-
const img = nodo.querySelector('img');
|
|
255
|
-
expect(img?.getAttribute('src')).toBe('https://x.test/a.png');
|
|
256
|
-
expect(img?.getAttribute('alt')).toBe('');
|
|
257
|
-
});
|
|
258
|
-
|
|
259
|
-
it('toma el primer item de images[] con su image_alt', async () => {
|
|
260
|
-
const nodo = await pintar(
|
|
261
|
-
{ key: 'images', label: 'Imágenes', format: 'thumbnail' },
|
|
262
|
-
{ row: { images: [{ image_url: 'https://x.test/b.png', image_alt: 'Frente' }] } },
|
|
263
|
-
);
|
|
264
|
-
expect(nodo.querySelector('img')?.getAttribute('src')).toBe('https://x.test/b.png');
|
|
265
|
-
expect(nodo.querySelector('img')?.getAttribute('alt')).toBe('Frente');
|
|
266
|
-
});
|
|
267
|
-
|
|
268
|
-
it('sin URL pinta el guion', async () => {
|
|
269
|
-
const nodo = await pintar(col, { row: { image_url: '' } });
|
|
270
|
-
expect(nodo.textContent).toContain('—');
|
|
271
|
-
expect(nodo.querySelector('img')).toBeNull();
|
|
272
|
-
});
|
|
273
|
-
});
|
|
274
|
-
});
|