@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,327 +0,0 @@
|
|
|
1
|
-
import {
|
|
2
|
-
ChangeDetectionStrategy,
|
|
3
|
-
Component,
|
|
4
|
-
computed,
|
|
5
|
-
effect,
|
|
6
|
-
inject,
|
|
7
|
-
signal,
|
|
8
|
-
} from '@angular/core';
|
|
9
|
-
import { toSignal } from '@angular/core/rxjs-interop';
|
|
10
|
-
import { ActivatedRoute } from '@angular/router';
|
|
11
|
-
|
|
12
|
-
import type {
|
|
13
|
-
KanbanViewConfig,
|
|
14
|
-
ResourceSchema,
|
|
15
|
-
SelectionBinding,
|
|
16
|
-
} from '../../core/models/schema.model';
|
|
17
|
-
import { ErrorMapperService } from '../../core/services/error-mapper.service';
|
|
18
|
-
import { DataSourceService, type SourceRow } from '../../core/services/data-source.service';
|
|
19
|
-
import type { SelectionEmitter } from '../composer/view-context';
|
|
20
|
-
import { getSchema } from '../../core/registry/schema-catalog';
|
|
21
|
-
import { Button } from '../../shared/components/button/button';
|
|
22
|
-
import { EmptyState } from '../../shared/components/empty-state/empty-state';
|
|
23
|
-
import { Skeleton } from '../../shared/components/skeleton/skeleton';
|
|
24
|
-
import {
|
|
25
|
-
cardSubtitle,
|
|
26
|
-
cardTitle,
|
|
27
|
-
mapKanbanRows,
|
|
28
|
-
type KanbanColumnBucket,
|
|
29
|
-
type KanbanMapping,
|
|
30
|
-
} from './kanban-model';
|
|
31
|
-
|
|
32
|
-
/**
|
|
33
|
-
* Narrowing helper: `UiViewBase` keeps `type: string` open, so comparing
|
|
34
|
-
* `view.type === 'kanban'` alone does not remove the base member from the
|
|
35
|
-
* union. Requiring `config` in the value is what actually proves the view is
|
|
36
|
-
* a `UiViewKanban` at runtime.
|
|
37
|
-
*/
|
|
38
|
-
function kanbanConfigOf(view: unknown): KanbanViewConfig | undefined {
|
|
39
|
-
if (!view || typeof view !== 'object') return undefined;
|
|
40
|
-
const candidate = view as { type?: unknown; config?: unknown };
|
|
41
|
-
return candidate.type === 'kanban' && candidate.config !== undefined
|
|
42
|
-
? (candidate.config as KanbanViewConfig)
|
|
43
|
-
: undefined;
|
|
44
|
-
}
|
|
45
|
-
|
|
46
|
-
type Status = 'loading' | 'ready' | 'error';
|
|
47
|
-
|
|
48
|
-
/** Label of the bucket for rows without a group value (Spanish UI copy). */
|
|
49
|
-
const UNKNOWN_LABEL = 'Sin valor';
|
|
50
|
-
|
|
51
|
-
/**
|
|
52
|
-
* Synthetic `value` of that bucket: a real group value can never collide
|
|
53
|
-
* with it (a declared `value` is non-empty and the grouping never produces
|
|
54
|
-
* this literal), so `@for` tracking stays unique.
|
|
55
|
-
*/
|
|
56
|
-
const UNKNOWN_VALUE = '__sin_valor__';
|
|
57
|
-
|
|
58
|
-
/**
|
|
59
|
-
* Generic kanban view (R9.8.4): rows of the declared
|
|
60
|
-
* `KanbanViewConfig.source` (or the resource's own listing) grouped by
|
|
61
|
-
* `groupField` into the declared columns, client-side.
|
|
62
|
-
*
|
|
63
|
-
* Data flow: ONE fetcher (`DataSourceService.rows`) — there is no dynamic
|
|
64
|
-
* context here, so `ctx` is `undefined`; the 30 s cache of the service is
|
|
65
|
-
* what keeps repeated visits free.
|
|
66
|
-
*
|
|
67
|
-
* Read-only by design (R9.8.1): no drag-and-drop, no move buttons, no
|
|
68
|
-
* mutation path at all. Colour never carries meaning alone — every count is
|
|
69
|
-
* text — and an empty column is a visible «Sin tarjetas», never a gap.
|
|
70
|
-
*/
|
|
71
|
-
@Component({
|
|
72
|
-
selector: 'app-kanban-view',
|
|
73
|
-
changeDetection: ChangeDetectionStrategy.OnPush,
|
|
74
|
-
imports: [Button, EmptyState, Skeleton],
|
|
75
|
-
template: `
|
|
76
|
-
@if (config(); as cfg) {
|
|
77
|
-
<section class="space-y-4" aria-label="Tablero" [attr.aria-busy]="status() === 'loading'">
|
|
78
|
-
<header class="flex flex-wrap items-center justify-between gap-3">
|
|
79
|
-
<h2 class="font-display text-xl font-semibold">Tablero</h2>
|
|
80
|
-
<p class="text-sm text-neutral">
|
|
81
|
-
Agrupado por <code>{{ cfg.groupField }}</code>
|
|
82
|
-
</p>
|
|
83
|
-
</header>
|
|
84
|
-
|
|
85
|
-
@switch (status()) {
|
|
86
|
-
@case ('loading') {
|
|
87
|
-
<app-skeleton [lines]="6" [height]="32" />
|
|
88
|
-
}
|
|
89
|
-
@case ('error') {
|
|
90
|
-
<app-empty-state title="No se pudo cargar el tablero" [message]="errorMessage()">
|
|
91
|
-
<button app-button variant="outline" type="button" (click)="retry()">
|
|
92
|
-
Reintentar
|
|
93
|
-
</button>
|
|
94
|
-
</app-empty-state>
|
|
95
|
-
}
|
|
96
|
-
@case ('ready') {
|
|
97
|
-
<div class="flex gap-3 overflow-x-auto pb-2">
|
|
98
|
-
@for (column of columns(); track column.value) {
|
|
99
|
-
<section
|
|
100
|
-
class="flex w-64 shrink-0 flex-col gap-3 rounded-xl border border-neutral/20 bg-surface p-3"
|
|
101
|
-
[attr.data-column]="column.value"
|
|
102
|
-
[attr.aria-label]="columnAria(column)"
|
|
103
|
-
>
|
|
104
|
-
<header class="flex items-center justify-between gap-2">
|
|
105
|
-
<h3 class="text-sm font-semibold">{{ column.label }}</h3>
|
|
106
|
-
<span class="rounded-full bg-neutral/10 px-2 py-0.5 text-xs text-neutral">
|
|
107
|
-
{{ column.rows.length }}<span class="sr-only"> tarjetas</span>
|
|
108
|
-
</span>
|
|
109
|
-
</header>
|
|
110
|
-
@if (column.rows.length === 0) {
|
|
111
|
-
<p class="text-sm text-neutral">Sin tarjetas</p>
|
|
112
|
-
} @else {
|
|
113
|
-
<ul class="flex flex-col gap-2">
|
|
114
|
-
@for (row of column.rows; track $index) {
|
|
115
|
-
<li
|
|
116
|
-
class="rounded-lg border border-neutral/15 p-2"
|
|
117
|
-
[attr.tabindex]="seleccionable() ? '0' : null"
|
|
118
|
-
[attr.role]="seleccionable() ? 'button' : null"
|
|
119
|
-
[class.cursor-pointer]="seleccionable()"
|
|
120
|
-
[class.ring-2]="esSeleccionada(row)"
|
|
121
|
-
[attr.data-selectable]="seleccionable() ? 'true' : null"
|
|
122
|
-
(click)="seleccionar(row)"
|
|
123
|
-
(keydown.enter)="activar($event, row)"
|
|
124
|
-
(keydown.space)="activar($event, row)"
|
|
125
|
-
>
|
|
126
|
-
<p class="text-sm font-medium">{{ cardTitleOf(row) }}</p>
|
|
127
|
-
@if (cardSubtitleOf(row); as subtitle) {
|
|
128
|
-
<p class="mt-0.5 text-xs text-neutral">{{ subtitle }}</p>
|
|
129
|
-
}
|
|
130
|
-
</li>
|
|
131
|
-
}
|
|
132
|
-
</ul>
|
|
133
|
-
}
|
|
134
|
-
</section>
|
|
135
|
-
}
|
|
136
|
-
</div>
|
|
137
|
-
}
|
|
138
|
-
}
|
|
139
|
-
</section>
|
|
140
|
-
} @else {
|
|
141
|
-
<app-empty-state
|
|
142
|
-
title="Configuración inválida"
|
|
143
|
-
message="Esta vista no declara una configuración de kanban válida."
|
|
144
|
-
/>
|
|
145
|
-
}
|
|
146
|
-
`,
|
|
147
|
-
})
|
|
148
|
-
export class KanbanView {
|
|
149
|
-
private readonly route = inject(ActivatedRoute);
|
|
150
|
-
// The shell mounts views via `loadComponent` (no input bindings), so the
|
|
151
|
-
// schema is resolved from route data exactly like CalendarView does.
|
|
152
|
-
private readonly routeData = toSignal(this.route.data, { requireSync: true });
|
|
153
|
-
private readonly dataSource = inject(DataSourceService);
|
|
154
|
-
private readonly errorMapper = inject(ErrorMapperService);
|
|
155
|
-
|
|
156
|
-
/**
|
|
157
|
-
* Resource schema for this view. The shell mounts views via
|
|
158
|
-
* `loadComponent` (no input bindings), so the schema is resolved from
|
|
159
|
-
* route data: either an explicit `schema` object (fixtures), or a
|
|
160
|
-
* `resourceId` looked up in the registry (the production route shape).
|
|
161
|
-
*/
|
|
162
|
-
readonly schema = computed<ResourceSchema | undefined>(() => {
|
|
163
|
-
const data = this.routeData();
|
|
164
|
-
const explicit = data['schema'];
|
|
165
|
-
if (explicit !== undefined) return explicit as ResourceSchema;
|
|
166
|
-
const resourceId = data['resourceId'];
|
|
167
|
-
return typeof resourceId === 'string' ? getSchema(resourceId) : undefined;
|
|
168
|
-
});
|
|
169
|
-
|
|
170
|
-
/**
|
|
171
|
-
* Config resolved from `schema.uiViews[<route uiView>]`; `undefined` when
|
|
172
|
-
* the route does not name a kanban view with a usable config — the
|
|
173
|
-
* template then renders the invalid-config empty state and NOTHING is
|
|
174
|
-
* fetched (the effect below bails out on `undefined`).
|
|
175
|
-
*/
|
|
176
|
-
readonly config = computed<KanbanViewConfig | undefined>(() => {
|
|
177
|
-
const schema = this.schema();
|
|
178
|
-
const viewName = this.routeData()['uiView'];
|
|
179
|
-
if (schema === undefined || typeof viewName !== 'string') return undefined;
|
|
180
|
-
const cfg = kanbanConfigOf(schema.uiViews?.[viewName]);
|
|
181
|
-
if (cfg === undefined) return undefined;
|
|
182
|
-
// Usable = the two references the board cannot render without: a
|
|
183
|
-
// non-empty `groupField` and at least one column with a non-empty value.
|
|
184
|
-
const usable =
|
|
185
|
-
typeof cfg.groupField === 'string' &&
|
|
186
|
-
cfg.groupField !== '' &&
|
|
187
|
-
Array.isArray(cfg.columns) &&
|
|
188
|
-
cfg.columns.length > 0 &&
|
|
189
|
-
cfg.columns.every((column) => typeof column?.value === 'string' && column.value !== '');
|
|
190
|
-
return usable ? cfg : undefined;
|
|
191
|
-
});
|
|
192
|
-
|
|
193
|
-
readonly status = signal<Status>('loading');
|
|
194
|
-
readonly rows = signal<SourceRow[]>([]);
|
|
195
|
-
readonly errorMessage = signal('');
|
|
196
|
-
/** Clave seleccionada localmente cuando el binding declara `mode:'inline'`. */
|
|
197
|
-
readonly seleccionada = signal<string | null>(null);
|
|
198
|
-
|
|
199
|
-
/** Guards against a slow response overwriting a newer load's rows. */
|
|
200
|
-
private loadEpoch = 0;
|
|
201
|
-
|
|
202
|
-
constructor() {
|
|
203
|
-
effect(() => {
|
|
204
|
-
const cfg = this.config();
|
|
205
|
-
if (cfg === undefined) return;
|
|
206
|
-
void this.load(cfg);
|
|
207
|
-
});
|
|
208
|
-
}
|
|
209
|
-
|
|
210
|
-
/** Rows grouped onto columns + the visible `unknown` bucket (pure model). */
|
|
211
|
-
readonly mapping = computed<KanbanMapping>(() => {
|
|
212
|
-
const cfg = this.config();
|
|
213
|
-
const schema = this.schema();
|
|
214
|
-
if (cfg === undefined || schema === undefined) return { columns: [], unknown: [] };
|
|
215
|
-
return mapKanbanRows(this.rows(), cfg);
|
|
216
|
-
});
|
|
217
|
-
|
|
218
|
-
/**
|
|
219
|
-
* Columns to paint: the model's buckets plus the `unknown` bucket at the
|
|
220
|
-
* END — but only when it actually has rows, otherwise the board would show
|
|
221
|
-
* an empty column nobody declared.
|
|
222
|
-
*/
|
|
223
|
-
readonly columns = computed<readonly KanbanColumnBucket[]>(() => {
|
|
224
|
-
const { columns, unknown } = this.mapping();
|
|
225
|
-
if (unknown.length === 0) return columns;
|
|
226
|
-
return [...columns, { value: UNKNOWN_VALUE, label: UNKNOWN_LABEL, rows: unknown }];
|
|
227
|
-
});
|
|
228
|
-
|
|
229
|
-
retry(): void {
|
|
230
|
-
const cfg = this.config();
|
|
231
|
-
if (cfg !== undefined) void this.load(cfg);
|
|
232
|
-
}
|
|
233
|
-
|
|
234
|
-
/** Clave estable de la fila (mismo campo que el resto del motor). */
|
|
235
|
-
private keyOf(row: SourceRow): string {
|
|
236
|
-
const schema = this.schema();
|
|
237
|
-
const key = schema ? row[schema.keyField] : undefined;
|
|
238
|
-
return key === undefined || key === null ? '' : String(key);
|
|
239
|
-
}
|
|
240
|
-
|
|
241
|
-
/** Binding `select` declarado por la vista (R9.9.4 · decision table). */
|
|
242
|
-
private binding(): SelectionBinding | undefined {
|
|
243
|
-
return this.routeData()['select'] as SelectionBinding | undefined;
|
|
244
|
-
}
|
|
245
|
-
|
|
246
|
-
/**
|
|
247
|
-
* Clic de tarjeta. Sin declaración ⇒ inerte (el tablero no navegaba);
|
|
248
|
-
* con declaración ⇒ sólo el contrato declarado corre: `route` emite al
|
|
249
|
-
* dispatcher, `inline` selecciona en local, `none` es no-op explícito.
|
|
250
|
-
*/
|
|
251
|
-
/**
|
|
252
|
-
* R9.8.7 — la misma selección que el clic, operable con teclado (Enter y
|
|
253
|
-
* Space sobre la tarjeta focusable). Sólo tiene sentido con `select`
|
|
254
|
-
* declarado: sin binding la tarjeta no es focusable y esto nunca corre.
|
|
255
|
-
*/
|
|
256
|
-
activar(event: Event, row: SourceRow): void {
|
|
257
|
-
if (!this.seleccionable()) return;
|
|
258
|
-
event.preventDefault(); // Space no debe desplazar la página
|
|
259
|
-
this.seleccionar(row);
|
|
260
|
-
}
|
|
261
|
-
|
|
262
|
-
seleccionar(row: SourceRow): void {
|
|
263
|
-
const binding = this.binding();
|
|
264
|
-
if (binding === undefined) return;
|
|
265
|
-
if (binding.mode === 'inline') {
|
|
266
|
-
this.seleccionada.set(this.keyOf(row));
|
|
267
|
-
return;
|
|
268
|
-
}
|
|
269
|
-
if (binding.mode === 'none') return;
|
|
270
|
-
const schema = this.schema();
|
|
271
|
-
const emit = this.routeData()['emit'] as SelectionEmitter | undefined;
|
|
272
|
-
emit?.({
|
|
273
|
-
resource: schema?.id ?? String(this.routeData()['resourceId'] ?? ''),
|
|
274
|
-
key: this.keyOf(row),
|
|
275
|
-
action: binding.on,
|
|
276
|
-
});
|
|
277
|
-
}
|
|
278
|
-
|
|
279
|
-
/** El DOM declara clicable sólo si hay contrato que responder. */
|
|
280
|
-
seleccionable(): boolean {
|
|
281
|
-
const binding = this.binding();
|
|
282
|
-
return binding !== undefined && binding.mode !== 'none';
|
|
283
|
-
}
|
|
284
|
-
|
|
285
|
-
esSeleccionada(row: SourceRow): boolean {
|
|
286
|
-
return this.seleccionable() && this.seleccionada() === this.keyOf(row);
|
|
287
|
-
}
|
|
288
|
-
|
|
289
|
-
/** «Por hacer: 2 tarjetas» — the count is text, never colour alone. */
|
|
290
|
-
columnAria(column: KanbanColumnBucket): string {
|
|
291
|
-
const count = column.rows.length;
|
|
292
|
-
return `${column.label}: ${count} ${count === 1 ? 'tarjeta' : 'tarjetas'}`;
|
|
293
|
-
}
|
|
294
|
-
|
|
295
|
-
cardTitleOf(row: SourceRow): string {
|
|
296
|
-
const cfg = this.config();
|
|
297
|
-
const schema = this.schema();
|
|
298
|
-
if (cfg === undefined || schema === undefined) return '';
|
|
299
|
-
return cardTitle(row, cfg, schema);
|
|
300
|
-
}
|
|
301
|
-
|
|
302
|
-
cardSubtitleOf(row: SourceRow): string {
|
|
303
|
-
const cfg = this.config();
|
|
304
|
-
const schema = this.schema();
|
|
305
|
-
if (cfg === undefined || schema === undefined) return '';
|
|
306
|
-
return cardSubtitle(row, cfg, schema);
|
|
307
|
-
}
|
|
308
|
-
|
|
309
|
-
private async load(cfg: KanbanViewConfig): Promise<void> {
|
|
310
|
-
const schema = this.schema();
|
|
311
|
-
if (schema === undefined) return;
|
|
312
|
-
const epoch = ++this.loadEpoch;
|
|
313
|
-
this.status.set('loading');
|
|
314
|
-
const source = cfg.source ?? { resource: schema.id };
|
|
315
|
-
try {
|
|
316
|
-
const rows = await this.dataSource.rows(source, undefined);
|
|
317
|
-
if (epoch !== this.loadEpoch) return;
|
|
318
|
-
this.rows.set(rows);
|
|
319
|
-
this.errorMessage.set('');
|
|
320
|
-
this.status.set('ready');
|
|
321
|
-
} catch (error) {
|
|
322
|
-
if (epoch !== this.loadEpoch) return;
|
|
323
|
-
this.errorMessage.set(this.errorMapper.map(error).general ?? 'No se pudo cargar el tablero.');
|
|
324
|
-
this.status.set('error');
|
|
325
|
-
}
|
|
326
|
-
}
|
|
327
|
-
}
|
|
@@ -1,17 +0,0 @@
|
|
|
1
|
-
import type { BarChartWidget } from '../../core/models/schema.model';
|
|
2
|
-
import type { RenderDescriptor } from '../../core/registry/render-registry';
|
|
3
|
-
|
|
4
|
-
import { toSeries } from '../series';
|
|
5
|
-
|
|
6
|
-
/** Descriptor del widget `bar-chart` (R3.6). */
|
|
7
|
-
export const descriptor: RenderDescriptor = {
|
|
8
|
-
kind: 'widget',
|
|
9
|
-
type: 'bar-chart',
|
|
10
|
-
since: '1.0',
|
|
11
|
-
load: () => import('./bar-chart').then((module) => module.BarChart),
|
|
12
|
-
surfaces: ['display'],
|
|
13
|
-
inputs: (config, extras) => {
|
|
14
|
-
const cfg = config as BarChartWidget;
|
|
15
|
-
return { label: cfg.label, data: toSeries(extras.data), unit: cfg.unit ?? '' };
|
|
16
|
-
},
|
|
17
|
-
};
|
|
@@ -1,66 +0,0 @@
|
|
|
1
|
-
import { ComponentFixture, TestBed } from '@angular/core/testing';
|
|
2
|
-
|
|
3
|
-
import { BarChart, type BarDatum } from './bar-chart';
|
|
4
|
-
|
|
5
|
-
const DATA: BarDatum[] = [
|
|
6
|
-
{ label: 'Lun', value: 3 },
|
|
7
|
-
{ label: 'Mar', value: 5 },
|
|
8
|
-
{ label: 'Mié', value: 1 },
|
|
9
|
-
];
|
|
10
|
-
|
|
11
|
-
describe('BarChart', () => {
|
|
12
|
-
let fixture: ComponentFixture<BarChart>;
|
|
13
|
-
|
|
14
|
-
const root = (): HTMLElement => fixture.nativeElement as HTMLElement;
|
|
15
|
-
const bars = (): HTMLElement[] => [...root().querySelectorAll<HTMLElement>('[role="img"] > div')];
|
|
16
|
-
|
|
17
|
-
beforeEach(async () => {
|
|
18
|
-
await TestBed.configureTestingModule({ imports: [BarChart] }).compileComponents();
|
|
19
|
-
fixture = TestBed.createComponent(BarChart);
|
|
20
|
-
fixture.componentRef.setInput('label', 'Entregas');
|
|
21
|
-
});
|
|
22
|
-
|
|
23
|
-
it('renderiza N barras con el resumen en aria-label', () => {
|
|
24
|
-
fixture.componentRef.setInput('data', DATA);
|
|
25
|
-
fixture.detectChanges();
|
|
26
|
-
|
|
27
|
-
expect(bars().length).toBe(3);
|
|
28
|
-
expect(root().textContent).toContain('Entregas');
|
|
29
|
-
expect(root().textContent).toContain('Lun');
|
|
30
|
-
|
|
31
|
-
const chart = root().querySelector('[role="img"]');
|
|
32
|
-
expect(chart?.getAttribute('aria-label')).toContain('3 categorías');
|
|
33
|
-
expect(chart?.getAttribute('aria-label')).toContain('Mayor: Mar con 5');
|
|
34
|
-
});
|
|
35
|
-
|
|
36
|
-
it('data vacía ⇒ «Sin datos» y sin grilla accesible', () => {
|
|
37
|
-
fixture.componentRef.setInput('data', []);
|
|
38
|
-
fixture.detectChanges();
|
|
39
|
-
|
|
40
|
-
expect(root().querySelector('[data-testid="empty"]')?.textContent).toContain('Sin datos');
|
|
41
|
-
expect(root().querySelector('[role="img"]')).toBeNull();
|
|
42
|
-
expect(bars().length).toBe(0);
|
|
43
|
-
});
|
|
44
|
-
|
|
45
|
-
it('el unit se añade a las cifras del resumen', () => {
|
|
46
|
-
fixture.componentRef.setInput('data', DATA);
|
|
47
|
-
fixture.componentRef.setInput('unit', ' u.');
|
|
48
|
-
fixture.detectChanges();
|
|
49
|
-
|
|
50
|
-
expect(root().querySelector('[role="img"]')?.getAttribute('aria-label')).toContain('con 5 u.');
|
|
51
|
-
});
|
|
52
|
-
|
|
53
|
-
it('con todos los valores en 0 no divide entre cero', () => {
|
|
54
|
-
fixture.componentRef.setInput('data', [
|
|
55
|
-
{ label: 'A', value: 0 },
|
|
56
|
-
{ label: 'B', value: 0 },
|
|
57
|
-
]);
|
|
58
|
-
fixture.detectChanges();
|
|
59
|
-
|
|
60
|
-
expect(bars().length).toBe(2);
|
|
61
|
-
const heights = bars().map(
|
|
62
|
-
(bar) => bar.querySelector<HTMLElement>('div[style*="height"]')?.style.height,
|
|
63
|
-
);
|
|
64
|
-
expect(heights).toEqual(['0%', '0%']);
|
|
65
|
-
});
|
|
66
|
-
});
|
|
@@ -1,76 +0,0 @@
|
|
|
1
|
-
import { Component, computed, input } from '@angular/core';
|
|
2
|
-
|
|
3
|
-
export interface BarDatum {
|
|
4
|
-
label: string;
|
|
5
|
-
value: number;
|
|
6
|
-
}
|
|
7
|
-
|
|
8
|
-
/**
|
|
9
|
-
* Barras por categoría, sin librería: se dibujan con CSS y un `@for` de
|
|
10
|
-
* Angular (~1 K gz frente a los 61,4 K de Chart.js — viabilidad B2.1).
|
|
11
|
-
*
|
|
12
|
-
* Accesibilidad (plan 3.2): la grilla es `role="img"` con un `aria-label`
|
|
13
|
-
* que resume los datos, de modo que un lector de pantalla no recorra cada
|
|
14
|
-
* barra suelta sino que escuche «8 categorías. Mayor: …».
|
|
15
|
-
*/
|
|
16
|
-
@Component({
|
|
17
|
-
selector: 'app-bar-chart',
|
|
18
|
-
template: `
|
|
19
|
-
<div class="rounded-lg border border-neutral/20 bg-surface p-4">
|
|
20
|
-
<p class="mb-3 text-sm text-neutral">{{ label() }}</p>
|
|
21
|
-
|
|
22
|
-
@if (data().length === 0) {
|
|
23
|
-
<p class="text-sm text-neutral/60" data-testid="empty">Sin datos.</p>
|
|
24
|
-
} @else {
|
|
25
|
-
<div role="img" [attr.aria-label]="summary()" class="flex h-32 items-end gap-2">
|
|
26
|
-
@for (row of data(); track row.label) {
|
|
27
|
-
<div class="flex min-w-0 flex-1 flex-col items-center justify-end gap-1">
|
|
28
|
-
<span class="text-xs tabular-nums text-neutral"> {{ row.value }}{{ unit() }} </span>
|
|
29
|
-
<div class="w-full rounded-t bg-primary" [style.height.%]="height(row.value)"></div>
|
|
30
|
-
<span class="w-full truncate text-center text-xs text-neutral">{{ row.label }}</span>
|
|
31
|
-
</div>
|
|
32
|
-
}
|
|
33
|
-
</div>
|
|
34
|
-
}
|
|
35
|
-
</div>
|
|
36
|
-
`,
|
|
37
|
-
})
|
|
38
|
-
export class BarChart {
|
|
39
|
-
/** Título del gráfico. */
|
|
40
|
-
readonly label = input.required<string>();
|
|
41
|
-
/** Series a dibujar; vacío ⇒ estado «Sin datos». */
|
|
42
|
-
readonly data = input<BarDatum[]>([]);
|
|
43
|
-
/** Sufijo de las cifras del eje («unidades», «S/»). */
|
|
44
|
-
readonly unit = input('');
|
|
45
|
-
|
|
46
|
-
private readonly max = computed(() => Math.max(0, ...this.data().map((row) => row.value)));
|
|
47
|
-
|
|
48
|
-
/**
|
|
49
|
-
* Altura relativa de una barra. `max = 0` ⇒ 0 % (nada de división por
|
|
50
|
-
* cero); un valor de 0 se queda en 0, y los demás bajan a un 2 % mínimo
|
|
51
|
-
* para que la barra siga siendo visible.
|
|
52
|
-
*/
|
|
53
|
-
protected height(value: number): number {
|
|
54
|
-
if (value === 0) {
|
|
55
|
-
return 0;
|
|
56
|
-
}
|
|
57
|
-
const max = this.max();
|
|
58
|
-
if (max === 0) {
|
|
59
|
-
return 0;
|
|
60
|
-
}
|
|
61
|
-
return Math.max(2, (value / max) * 100);
|
|
62
|
-
}
|
|
63
|
-
|
|
64
|
-
protected readonly summary = computed(() => {
|
|
65
|
-
const rows = this.data();
|
|
66
|
-
if (rows.length === 0) {
|
|
67
|
-
return 'Sin datos.';
|
|
68
|
-
}
|
|
69
|
-
const top = rows.reduce((best, row) => (row.value > best.value ? row : best), rows[0]);
|
|
70
|
-
const unit = this.unit();
|
|
71
|
-
return (
|
|
72
|
-
`${rows.length} categorías. Mayor: ${top.label} con ${top.value}${unit}. ` +
|
|
73
|
-
`Menor: ${rows.reduce((low, row) => (row.value < low.value ? row : low), rows[0]).label}.`
|
|
74
|
-
);
|
|
75
|
-
});
|
|
76
|
-
}
|
|
@@ -1,17 +0,0 @@
|
|
|
1
|
-
import type { ChartLineWidget } from '../../core/models/schema.model';
|
|
2
|
-
import type { RenderDescriptor } from '../../core/registry/render-registry';
|
|
3
|
-
|
|
4
|
-
import { toSeries } from '../series';
|
|
5
|
-
|
|
6
|
-
/** Descriptor del widget `chart-line` (R3.6). */
|
|
7
|
-
export const descriptor: RenderDescriptor = {
|
|
8
|
-
kind: 'widget',
|
|
9
|
-
type: 'chart-line',
|
|
10
|
-
since: '1.0',
|
|
11
|
-
load: () => import('./chart-line').then((module) => module.ChartLine),
|
|
12
|
-
surfaces: ['display'],
|
|
13
|
-
inputs: (config, extras) => {
|
|
14
|
-
const cfg = config as ChartLineWidget;
|
|
15
|
-
return { label: cfg.label, data: toSeries(extras.data), unit: cfg.unit ?? '' };
|
|
16
|
-
},
|
|
17
|
-
};
|
|
@@ -1,113 +0,0 @@
|
|
|
1
|
-
import {
|
|
2
|
-
DeferBlockBehavior,
|
|
3
|
-
DeferBlockState,
|
|
4
|
-
ComponentFixture,
|
|
5
|
-
TestBed,
|
|
6
|
-
} from '@angular/core/testing';
|
|
7
|
-
|
|
8
|
-
import { ChartLine } from './chart-line';
|
|
9
|
-
|
|
10
|
-
/**
|
|
11
|
-
* jsdom no implementa el lienzo 2D: `getContext('2d')` devuelve `null` y
|
|
12
|
-
* Chart.js revienta al primer trazo. Se stubbea por `beforeAll` para poder
|
|
13
|
-
* montar el componente de verdad y se restaura en `afterAll`.
|
|
14
|
-
*/
|
|
15
|
-
const canvasStub = { width: 300, height: 150 } as HTMLCanvasElement;
|
|
16
|
-
|
|
17
|
-
const ctxStub = new Proxy({} as unknown as CanvasRenderingContext2D, {
|
|
18
|
-
get(_target, prop) {
|
|
19
|
-
if (prop === 'canvas') {
|
|
20
|
-
return canvasStub;
|
|
21
|
-
}
|
|
22
|
-
if (prop === 'measureText') {
|
|
23
|
-
return () => ({ width: 10 });
|
|
24
|
-
}
|
|
25
|
-
if (prop === 'createLinearGradient') {
|
|
26
|
-
return () => ({ addColorStop: () => undefined });
|
|
27
|
-
}
|
|
28
|
-
return () => undefined;
|
|
29
|
-
},
|
|
30
|
-
set() {
|
|
31
|
-
return true;
|
|
32
|
-
},
|
|
33
|
-
});
|
|
34
|
-
|
|
35
|
-
describe('ChartLine', () => {
|
|
36
|
-
let fixture: ComponentFixture<ChartLine>;
|
|
37
|
-
|
|
38
|
-
const root = (): HTMLElement => fixture.nativeElement as HTMLElement;
|
|
39
|
-
const originalGetContext = HTMLCanvasElement.prototype.getContext;
|
|
40
|
-
|
|
41
|
-
/**
|
|
42
|
-
* `on viewport` necesita `IntersectionObserver`, que jsdom no implementa:
|
|
43
|
-
* con `Manual` el bloque no evalúa su condición y lo pintamos a mano.
|
|
44
|
-
*/
|
|
45
|
-
const pintarDefer = async (estado: DeferBlockState): Promise<void> => {
|
|
46
|
-
const blocks = await fixture.getDeferBlocks();
|
|
47
|
-
await blocks[0].render(estado);
|
|
48
|
-
fixture.detectChanges();
|
|
49
|
-
};
|
|
50
|
-
|
|
51
|
-
beforeAll(() => {
|
|
52
|
-
HTMLCanvasElement.prototype.getContext = (() =>
|
|
53
|
-
ctxStub) as unknown as typeof originalGetContext;
|
|
54
|
-
});
|
|
55
|
-
|
|
56
|
-
afterAll(() => {
|
|
57
|
-
HTMLCanvasElement.prototype.getContext = originalGetContext;
|
|
58
|
-
});
|
|
59
|
-
|
|
60
|
-
beforeEach(async () => {
|
|
61
|
-
await TestBed.configureTestingModule({
|
|
62
|
-
imports: [ChartLine],
|
|
63
|
-
deferBlockBehavior: DeferBlockBehavior.Manual,
|
|
64
|
-
}).compileComponents();
|
|
65
|
-
fixture = TestBed.createComponent(ChartLine);
|
|
66
|
-
});
|
|
67
|
-
|
|
68
|
-
it('sin datos muestra el estado vacío y no toca el lienzo', async () => {
|
|
69
|
-
fixture.componentRef.setInput('label', 'Entregas por día');
|
|
70
|
-
fixture.componentRef.setInput('data', []);
|
|
71
|
-
fixture.detectChanges();
|
|
72
|
-
|
|
73
|
-
expect(root().querySelector('[data-testid="empty"]')?.textContent?.trim()).toBe('Sin datos.');
|
|
74
|
-
expect(root().querySelector('canvas')).toBeNull();
|
|
75
|
-
// Sin datos no hay bloque que diferir: sólo existe el estado vacío.
|
|
76
|
-
expect(await fixture.getDeferBlocks()).toHaveLength(0);
|
|
77
|
-
});
|
|
78
|
-
|
|
79
|
-
it('en placeholder dibuja la miniatura SVG y no monta el lienzo', async () => {
|
|
80
|
-
fixture.componentRef.setInput('label', 'Entregas por día');
|
|
81
|
-
fixture.componentRef.setInput('data', [
|
|
82
|
-
{ label: 'Lun', value: 2 },
|
|
83
|
-
{ label: 'Mar', value: 5 },
|
|
84
|
-
]);
|
|
85
|
-
fixture.detectChanges();
|
|
86
|
-
|
|
87
|
-
await pintarDefer(DeferBlockState.Placeholder);
|
|
88
|
-
|
|
89
|
-
expect(root().querySelector('canvas')).toBeNull();
|
|
90
|
-
expect(root().querySelector('[role="img"]')).toBeNull();
|
|
91
|
-
expect(root().querySelector('polyline')?.getAttribute('points')?.split(' ')).toHaveLength(2);
|
|
92
|
-
expect(root().textContent).toContain('Entregas por día');
|
|
93
|
-
});
|
|
94
|
-
|
|
95
|
-
it('completado el defer dibuja el lienzo y resume la serie en el aria-label', async () => {
|
|
96
|
-
fixture.componentRef.setInput('label', 'Entregas por día');
|
|
97
|
-
fixture.componentRef.setInput('unit', ' u.');
|
|
98
|
-
fixture.componentRef.setInput('data', [
|
|
99
|
-
{ label: 'Lun', value: 2 },
|
|
100
|
-
{ label: 'Mar', value: 5 },
|
|
101
|
-
]);
|
|
102
|
-
fixture.detectChanges();
|
|
103
|
-
|
|
104
|
-
await pintarDefer(DeferBlockState.Complete);
|
|
105
|
-
|
|
106
|
-
const chart = root().querySelector('[role="img"]');
|
|
107
|
-
expect(root().querySelector('canvas')).not.toBeNull();
|
|
108
|
-
expect(chart?.getAttribute('aria-label')).toBe(
|
|
109
|
-
'Serie de 2 puntos, de Lun (2 u.) a Mar (5 u.): sube.',
|
|
110
|
-
);
|
|
111
|
-
expect(root().textContent).toContain('Entregas por día');
|
|
112
|
-
});
|
|
113
|
-
});
|