@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,882 +0,0 @@
|
|
|
1
|
-
import { CdkDrag, CdkDragDrop, CdkDragHandle, CdkDropList } from '@angular/cdk/drag-drop';
|
|
2
|
-
import { Component, computed, effect, inject, input, output, signal } from '@angular/core';
|
|
3
|
-
import { Router } from '@angular/router';
|
|
4
|
-
import { firstValueFrom } from 'rxjs';
|
|
5
|
-
|
|
6
|
-
import {
|
|
7
|
-
LucideChevronDown,
|
|
8
|
-
LucideChevronRight,
|
|
9
|
-
LucideChevronUp,
|
|
10
|
-
LucideDynamicIcon,
|
|
11
|
-
LucideEye,
|
|
12
|
-
LucideGripVertical,
|
|
13
|
-
LucidePencil,
|
|
14
|
-
LucidePlus,
|
|
15
|
-
LucideRotateCw,
|
|
16
|
-
LucideTrash2,
|
|
17
|
-
LucideZap,
|
|
18
|
-
} from '@lucide/angular';
|
|
19
|
-
import type { LucideIcon } from '@lucide/angular';
|
|
20
|
-
import { actionType, getAction } from '../../actions/action-registry';
|
|
21
|
-
import { ActionService } from '../../actions/action-service';
|
|
22
|
-
import { ApiError } from '../../core/models/api.model';
|
|
23
|
-
import type {
|
|
24
|
-
FieldSchema,
|
|
25
|
-
ListColumn,
|
|
26
|
-
ListColumnFormat,
|
|
27
|
-
ListFilter,
|
|
28
|
-
ResourceAction,
|
|
29
|
-
ResourceSchema,
|
|
30
|
-
SelectOption,
|
|
31
|
-
} from '../../core/models/schema.model';
|
|
32
|
-
import { ApiService } from '../../core/services/api.service';
|
|
33
|
-
import { AuthService } from '../../core/services/auth.service';
|
|
34
|
-
import { NotificationService } from '../../core/services/notification.service';
|
|
35
|
-
import { DrawerForm } from '../drawer-form/drawer-form';
|
|
36
|
-
import { RenderHost } from '../../core/registry/render-host';
|
|
37
|
-
import { getSchema } from '../../core/registry/schema-catalog';
|
|
38
|
-
import { Button } from '../../shared/components/button/button';
|
|
39
|
-
import { EmptyState } from '../../shared/components/empty-state/empty-state';
|
|
40
|
-
import { Skeleton } from '../../shared/components/skeleton/skeleton';
|
|
41
|
-
import { getValue } from '../../shared/utils/field-path';
|
|
42
|
-
import { formatoDeColumna } from '../../columns/column-defaults';
|
|
43
|
-
import type { ColumnCell } from '../../columns/column-cell';
|
|
44
|
-
|
|
45
|
-
type Row = Record<string, unknown>;
|
|
46
|
-
type Status = 'loading' | 'ready' | 'error';
|
|
47
|
-
|
|
48
|
-
const BOOLEAN_FILTER_OPTIONS: SelectOption[] = [
|
|
49
|
-
{ value: '', label: 'Todos' },
|
|
50
|
-
{ value: 'yes', label: 'Sí' },
|
|
51
|
-
{ value: 'no', label: 'No' },
|
|
52
|
-
];
|
|
53
|
-
|
|
54
|
-
function errorMessage(err: unknown, fallback: string): string {
|
|
55
|
-
return err instanceof ApiError ? err.message : fallback;
|
|
56
|
-
}
|
|
57
|
-
|
|
58
|
-
/**
|
|
59
|
-
* Listado genérico de una colección (base.md §10 «Listado»): tabla desde
|
|
60
|
-
* `listColumns` con los 7 formatos **resueltos por el registro** (R4.3),
|
|
61
|
-
* búsqueda/filtros **en el cliente** (el
|
|
62
|
-
* contrato de api.md no define parámetros de consulta: el endpoint admin
|
|
63
|
-
* devuelve todos los registros), interruptor `active`, acciones de fila y
|
|
64
|
-
* estados universales (skeleton, vacío, error con reintento).
|
|
65
|
-
*/
|
|
66
|
-
@Component({
|
|
67
|
-
selector: 'app-list-view',
|
|
68
|
-
imports: [
|
|
69
|
-
Button,
|
|
70
|
-
CdkDrag,
|
|
71
|
-
CdkDragHandle,
|
|
72
|
-
CdkDropList,
|
|
73
|
-
DrawerForm,
|
|
74
|
-
EmptyState,
|
|
75
|
-
LucideChevronDown,
|
|
76
|
-
LucideChevronRight,
|
|
77
|
-
LucideChevronUp,
|
|
78
|
-
LucideEye,
|
|
79
|
-
LucideGripVertical,
|
|
80
|
-
LucidePencil,
|
|
81
|
-
LucidePlus,
|
|
82
|
-
LucideRotateCw,
|
|
83
|
-
LucideDynamicIcon,
|
|
84
|
-
LucideTrash2,
|
|
85
|
-
LucideZap,
|
|
86
|
-
RenderHost,
|
|
87
|
-
Skeleton,
|
|
88
|
-
],
|
|
89
|
-
template: `
|
|
90
|
-
<section class="space-y-4" aria-label="Listado" [attr.aria-busy]="status() === 'loading'">
|
|
91
|
-
<div class="flex flex-wrap items-end justify-between gap-3">
|
|
92
|
-
<div class="flex flex-wrap items-end gap-3">
|
|
93
|
-
@if (schema().search?.length) {
|
|
94
|
-
<label class="flex flex-col gap-1 text-xs font-medium text-neutral">
|
|
95
|
-
Buscar
|
|
96
|
-
<input
|
|
97
|
-
type="search"
|
|
98
|
-
class="w-56 rounded-lg border border-neutral/30 bg-surface px-3 py-2 text-sm"
|
|
99
|
-
placeholder="Buscar…"
|
|
100
|
-
[value]="query()"
|
|
101
|
-
(input)="setQuery(inputValue($event))"
|
|
102
|
-
/>
|
|
103
|
-
</label>
|
|
104
|
-
}
|
|
105
|
-
@for (filter of schema().filters ?? []; track filter.key) {
|
|
106
|
-
<label class="flex flex-col gap-1 text-xs font-medium text-neutral">
|
|
107
|
-
{{ filter.label }}
|
|
108
|
-
@if (filter.type === 'text') {
|
|
109
|
-
<input
|
|
110
|
-
type="search"
|
|
111
|
-
class="w-44 rounded-lg border border-neutral/30 bg-surface px-3 py-2 text-sm"
|
|
112
|
-
[value]="filterValues()[filter.key] ?? ''"
|
|
113
|
-
(input)="setFilter(filter.key, inputValue($event))"
|
|
114
|
-
/>
|
|
115
|
-
} @else {
|
|
116
|
-
<select
|
|
117
|
-
class="w-44 rounded-lg border border-neutral/30 bg-surface px-3 py-2 text-sm"
|
|
118
|
-
[value]="filterValues()[filter.key] ?? ''"
|
|
119
|
-
(change)="setFilter(filter.key, selectValue($event))"
|
|
120
|
-
>
|
|
121
|
-
@for (option of filterOptions(filter); track option.value) {
|
|
122
|
-
<option [value]="option.value">{{ option.label }}</option>
|
|
123
|
-
}
|
|
124
|
-
</select>
|
|
125
|
-
}
|
|
126
|
-
</label>
|
|
127
|
-
}
|
|
128
|
-
</div>
|
|
129
|
-
@if (canCreate()) {
|
|
130
|
-
<button app-button type="button" (click)="goCreate()">
|
|
131
|
-
<svg lucidePlus size="16" />
|
|
132
|
-
Crear {{ schema().label }}
|
|
133
|
-
</button>
|
|
134
|
-
}
|
|
135
|
-
</div>
|
|
136
|
-
|
|
137
|
-
@switch (status()) {
|
|
138
|
-
@case ('loading') {
|
|
139
|
-
<app-skeleton [lines]="6" [height]="44" />
|
|
140
|
-
}
|
|
141
|
-
@case ('error') {
|
|
142
|
-
<app-empty-state
|
|
143
|
-
title="No se pudo cargar el listado"
|
|
144
|
-
message="Revisa tu conexión e intenta de nuevo."
|
|
145
|
-
>
|
|
146
|
-
<button app-button variant="outline" type="button" (click)="retry()">
|
|
147
|
-
<svg lucideRotateCw size="16" />
|
|
148
|
-
Reintentar
|
|
149
|
-
</button>
|
|
150
|
-
</app-empty-state>
|
|
151
|
-
}
|
|
152
|
-
@case ('ready') {
|
|
153
|
-
@if (visibleRows().length === 0) {
|
|
154
|
-
<app-empty-state
|
|
155
|
-
[title]="rows().length === 0 ? 'Sin registros' : 'Sin resultados'"
|
|
156
|
-
[message]="
|
|
157
|
-
rows().length === 0
|
|
158
|
-
? 'Todavía no hay registros en este recurso.'
|
|
159
|
-
: 'Ningún registro coincide con la búsqueda o los filtros.'
|
|
160
|
-
"
|
|
161
|
-
>
|
|
162
|
-
@if (rows().length > 0) {
|
|
163
|
-
<button app-button variant="outline" type="button" (click)="clearFilters()">
|
|
164
|
-
Limpiar filtros
|
|
165
|
-
</button>
|
|
166
|
-
} @else if (canCreate()) {
|
|
167
|
-
<button app-button type="button" (click)="goCreate()">
|
|
168
|
-
<svg lucidePlus size="16" />
|
|
169
|
-
Crear el primero
|
|
170
|
-
</button>
|
|
171
|
-
}
|
|
172
|
-
</app-empty-state>
|
|
173
|
-
} @else {
|
|
174
|
-
<!-- La tabla es la vista de escritorio: se oculta bajo lg para
|
|
175
|
-
que la lista de tarjetas de abajo sea la ÚNICA vista en
|
|
176
|
-
móvil (con las dos visibles, el listado se duplicaba). -->
|
|
177
|
-
<div
|
|
178
|
-
class="hidden overflow-x-auto rounded-xl border border-neutral/20 bg-surface lg:block"
|
|
179
|
-
>
|
|
180
|
-
<table class="w-full text-sm">
|
|
181
|
-
<thead>
|
|
182
|
-
<tr class="border-b border-neutral/20 text-left text-xs uppercase text-neutral">
|
|
183
|
-
@if (canReorder()) {
|
|
184
|
-
<th scope="col" class="w-16 px-2 py-3 font-medium">
|
|
185
|
-
<span class="sr-only">Orden</span>
|
|
186
|
-
</th>
|
|
187
|
-
}
|
|
188
|
-
@for (column of schema().listColumns; track column.key) {
|
|
189
|
-
<th scope="col" class="px-4 py-3 font-medium">
|
|
190
|
-
{{ column.label }}
|
|
191
|
-
</th>
|
|
192
|
-
}
|
|
193
|
-
@if (hasRowActions()) {
|
|
194
|
-
<th scope="col" class="px-4 py-3 text-right font-medium">Acciones</th>
|
|
195
|
-
}
|
|
196
|
-
</tr>
|
|
197
|
-
</thead>
|
|
198
|
-
<tbody
|
|
199
|
-
cdkDropList
|
|
200
|
-
[cdkDropListData]="visibleRows()"
|
|
201
|
-
[cdkDropListDisabled]="dropDisabled()"
|
|
202
|
-
(cdkDropListDropped)="onDrop($event)"
|
|
203
|
-
>
|
|
204
|
-
@for (row of visibleRows(); track keyOf(row); let i = $index) {
|
|
205
|
-
<tr
|
|
206
|
-
class="border-b border-neutral/10 last:border-0 hover:bg-neutral/5 transition-colors"
|
|
207
|
-
[class.cursor-pointer]="isRowClickable()"
|
|
208
|
-
cdkDrag
|
|
209
|
-
[cdkDragDisabled]="dropDisabled()"
|
|
210
|
-
(click)="onRowClick(row)"
|
|
211
|
-
>
|
|
212
|
-
@if (canReorder()) {
|
|
213
|
-
<td class="px-2 py-2.5 align-middle" (click)="$event.stopPropagation()">
|
|
214
|
-
<div class="flex items-center gap-1">
|
|
215
|
-
<button
|
|
216
|
-
type="button"
|
|
217
|
-
class="cursor-grab text-neutral hover:text-primary"
|
|
218
|
-
cdkDragHandle
|
|
219
|
-
[attr.aria-label]="'Reordenar ' + titleOf(row)"
|
|
220
|
-
title="Arrastrar para reordenar"
|
|
221
|
-
>
|
|
222
|
-
<svg lucideGripVertical size="16" />
|
|
223
|
-
</button>
|
|
224
|
-
<span class="flex flex-col leading-none">
|
|
225
|
-
<button
|
|
226
|
-
type="button"
|
|
227
|
-
class="px-1 text-xs text-neutral hover:text-primary disabled:opacity-30"
|
|
228
|
-
[disabled]="i === 0 || reordering()"
|
|
229
|
-
[attr.aria-label]="'Subir ' + titleOf(row)"
|
|
230
|
-
(click)="moveBy(keyOf(row), -1)"
|
|
231
|
-
>
|
|
232
|
-
<svg lucideChevronUp size="16" />
|
|
233
|
-
</button>
|
|
234
|
-
<button
|
|
235
|
-
type="button"
|
|
236
|
-
class="px-1 text-xs text-neutral hover:text-primary disabled:opacity-30"
|
|
237
|
-
[disabled]="i === visibleRows().length - 1 || reordering()"
|
|
238
|
-
[attr.aria-label]="'Bajar ' + titleOf(row)"
|
|
239
|
-
(click)="moveBy(keyOf(row), 1)"
|
|
240
|
-
>
|
|
241
|
-
<svg lucideChevronDown size="16" />
|
|
242
|
-
</button>
|
|
243
|
-
</span>
|
|
244
|
-
</div>
|
|
245
|
-
</td>
|
|
246
|
-
}
|
|
247
|
-
@for (column of schema().listColumns; track column.key) {
|
|
248
|
-
<td class="px-4 py-2.5 align-middle">
|
|
249
|
-
<!-- Los 7 formatos salen del registro (R4.3):
|
|
250
|
-
la vista sólo elige cuál y entrega la celda. -->
|
|
251
|
-
<app-render-host
|
|
252
|
-
kind="column-format"
|
|
253
|
-
[type]="formatOf(column)"
|
|
254
|
-
[config]="column"
|
|
255
|
-
[data]="cellOf(row, column)"
|
|
256
|
-
/>
|
|
257
|
-
</td>
|
|
258
|
-
}
|
|
259
|
-
@if (hasRowActions()) {
|
|
260
|
-
<td class="px-4 py-2.5" (click)="$event.stopPropagation()">
|
|
261
|
-
<div class="flex items-center justify-end gap-1">
|
|
262
|
-
@if (schema().detail?.enabled) {
|
|
263
|
-
<button
|
|
264
|
-
app-button
|
|
265
|
-
variant="ghost"
|
|
266
|
-
type="button"
|
|
267
|
-
(click)="goView(row)"
|
|
268
|
-
title="Ver ficha de detalle"
|
|
269
|
-
class="h-8 px-2.5 text-xs text-neutral-light hover:text-primary hover:bg-primary/10"
|
|
270
|
-
>
|
|
271
|
-
<svg lucideEye size="15" />
|
|
272
|
-
<span>Ver</span>
|
|
273
|
-
</button>
|
|
274
|
-
}
|
|
275
|
-
@for (action of schema().actions ?? []; track action.id) {
|
|
276
|
-
<button
|
|
277
|
-
app-button
|
|
278
|
-
variant="ghost"
|
|
279
|
-
type="button"
|
|
280
|
-
[title]="action.label"
|
|
281
|
-
[attr.aria-label]="action.label"
|
|
282
|
-
(click)="runAction(action, row)"
|
|
283
|
-
class="h-8 px-2 text-xs text-neutral hover:text-primary hover:bg-primary/10"
|
|
284
|
-
>
|
|
285
|
-
@let actionIcon = iconOf(action);
|
|
286
|
-
@if (actionIcon) {
|
|
287
|
-
<svg [lucideIcon]="actionIcon" size="15" />
|
|
288
|
-
} @else {
|
|
289
|
-
{{ action.label }}
|
|
290
|
-
}
|
|
291
|
-
</button>
|
|
292
|
-
}
|
|
293
|
-
@if (canQuickEdit()) {
|
|
294
|
-
<button
|
|
295
|
-
app-button
|
|
296
|
-
variant="ghost"
|
|
297
|
-
type="button"
|
|
298
|
-
(click)="quickEditRow.set(row)"
|
|
299
|
-
title="Edición rápida"
|
|
300
|
-
class="h-8 px-2 text-xs text-neutral hover:text-primary hover:bg-neutral/10"
|
|
301
|
-
>
|
|
302
|
-
<svg lucideZap size="15" />
|
|
303
|
-
<span>Edición rápida</span>
|
|
304
|
-
</button>
|
|
305
|
-
}
|
|
306
|
-
@if (canUpdate()) {
|
|
307
|
-
<button
|
|
308
|
-
app-button
|
|
309
|
-
variant="ghost"
|
|
310
|
-
type="button"
|
|
311
|
-
(click)="goEdit(row)"
|
|
312
|
-
title="Editar registro"
|
|
313
|
-
class="h-8 px-2.5 text-xs text-primary hover:bg-primary/10"
|
|
314
|
-
>
|
|
315
|
-
<svg lucidePencil size="15" />
|
|
316
|
-
<span>Editar</span>
|
|
317
|
-
</button>
|
|
318
|
-
}
|
|
319
|
-
@if (canRemove()) {
|
|
320
|
-
<button
|
|
321
|
-
app-button
|
|
322
|
-
variant="ghost"
|
|
323
|
-
type="button"
|
|
324
|
-
(click)="removeRow(row)"
|
|
325
|
-
title="Eliminar registro"
|
|
326
|
-
class="h-8 px-2 text-xs text-danger hover:bg-danger/10"
|
|
327
|
-
>
|
|
328
|
-
<svg lucideTrash2 size="15" />
|
|
329
|
-
<span>Eliminar</span>
|
|
330
|
-
</button>
|
|
331
|
-
}
|
|
332
|
-
</div>
|
|
333
|
-
</td>
|
|
334
|
-
}
|
|
335
|
-
</tr>
|
|
336
|
-
}
|
|
337
|
-
</tbody>
|
|
338
|
-
</table>
|
|
339
|
-
</div>
|
|
340
|
-
|
|
341
|
-
<!-- Vista de tarjetas — mobile-first (guía mobile §6).
|
|
342
|
-
La tabla de arriba necesita scroll horizontal con 5–7 columnas
|
|
343
|
-
(el anti-patrón #2): en pantallas chicas se convierte en una
|
|
344
|
-
lista de tarjetas de dos niveles (principal + secundario),
|
|
345
|
-
donde cada fila completa es un objetivo táctil. lg:hidden la
|
|
346
|
-
retira cuando la tabla vuelve a ser usable. -->
|
|
347
|
-
<ul class="space-y-2 lg:hidden">
|
|
348
|
-
@for (row of visibleRows(); track keyOf(row)) {
|
|
349
|
-
<li
|
|
350
|
-
class="rounded-xl border border-neutral/20 bg-surface p-3"
|
|
351
|
-
[class.cursor-pointer]="isRowClickable()"
|
|
352
|
-
(click)="onRowClick(row)"
|
|
353
|
-
>
|
|
354
|
-
<!-- Nivel principal: el título del registro, siempre visible
|
|
355
|
-
(guía §6.2: el identificador nunca se esconde). -->
|
|
356
|
-
<div class="flex items-start justify-between gap-2">
|
|
357
|
-
<span class="text-sm font-medium">
|
|
358
|
-
{{ titleOf(row) }}
|
|
359
|
-
</span>
|
|
360
|
-
@if (schema().detail?.enabled) {
|
|
361
|
-
<svg
|
|
362
|
-
lucideChevronRight
|
|
363
|
-
size="16"
|
|
364
|
-
class="mt-0.5 shrink-0 text-neutral"
|
|
365
|
-
aria-hidden="true"
|
|
366
|
-
/>
|
|
367
|
-
}
|
|
368
|
-
</div>
|
|
369
|
-
<!-- Nivel secundario: el resto de las columnas como pares
|
|
370
|
-
etiqueta/valor. Sin hover: todo lo accionable va en el
|
|
371
|
-
pie de la tarjeta, al alcance del pulgar. -->
|
|
372
|
-
<dl class="mt-2 grid grid-cols-[auto_1fr] gap-x-3 gap-y-1 text-xs">
|
|
373
|
-
@for (column of schema().listColumns; track column.key) {
|
|
374
|
-
<dt class="text-neutral">{{ column.label }}</dt>
|
|
375
|
-
<dd>
|
|
376
|
-
<app-render-host
|
|
377
|
-
kind="column-format"
|
|
378
|
-
[type]="formatOf(column)"
|
|
379
|
-
[config]="column"
|
|
380
|
-
[data]="cellOf(row, column)"
|
|
381
|
-
/>
|
|
382
|
-
</dd>
|
|
383
|
-
}
|
|
384
|
-
</dl>
|
|
385
|
-
@if (hasRowActions()) {
|
|
386
|
-
<!-- Pie táctil: objetivos ≥44 px (guía §8.2), sin hover. -->
|
|
387
|
-
<div class="mt-3 flex flex-wrap gap-2 border-t border-neutral/10 pt-2">
|
|
388
|
-
@if (schema().detail?.enabled) {
|
|
389
|
-
<button
|
|
390
|
-
app-button
|
|
391
|
-
variant="outline"
|
|
392
|
-
type="button"
|
|
393
|
-
size="sm"
|
|
394
|
-
class="flex-1"
|
|
395
|
-
(click)="$event.stopPropagation(); goView(row)"
|
|
396
|
-
>
|
|
397
|
-
<svg lucideEye size="15" aria-hidden="true" />
|
|
398
|
-
<span>Ver</span>
|
|
399
|
-
</button>
|
|
400
|
-
}
|
|
401
|
-
@for (action of schema().actions ?? []; track action.id) {
|
|
402
|
-
<button
|
|
403
|
-
app-button
|
|
404
|
-
variant="outline"
|
|
405
|
-
type="button"
|
|
406
|
-
size="sm"
|
|
407
|
-
class="flex-1"
|
|
408
|
-
(click)="$event.stopPropagation(); runAction(action, row)"
|
|
409
|
-
>
|
|
410
|
-
@let actionIcon = iconOf(action);
|
|
411
|
-
@if (actionIcon) {
|
|
412
|
-
<svg [lucideIcon]="actionIcon" size="15" aria-hidden="true" />
|
|
413
|
-
}
|
|
414
|
-
<span>{{ action.label }}</span>
|
|
415
|
-
</button>
|
|
416
|
-
}
|
|
417
|
-
<!-- Editar es O una vía u otra, como en la tabla: si el
|
|
418
|
-
schema declara quickEdit va el atajo; si no, la
|
|
419
|
-
pantalla completa. Apilados los dos sería ruido. -->
|
|
420
|
-
@if (canQuickEdit()) {
|
|
421
|
-
<button
|
|
422
|
-
app-button
|
|
423
|
-
variant="outline"
|
|
424
|
-
type="button"
|
|
425
|
-
size="sm"
|
|
426
|
-
class="flex-1"
|
|
427
|
-
(click)="$event.stopPropagation(); quickEditRow.set(row)"
|
|
428
|
-
>
|
|
429
|
-
<svg lucideZap size="15" aria-hidden="true" />
|
|
430
|
-
<span>Editar</span>
|
|
431
|
-
</button>
|
|
432
|
-
} @else if (canUpdate()) {
|
|
433
|
-
<button
|
|
434
|
-
app-button
|
|
435
|
-
variant="outline"
|
|
436
|
-
type="button"
|
|
437
|
-
size="sm"
|
|
438
|
-
class="flex-1"
|
|
439
|
-
(click)="$event.stopPropagation(); goEdit(row)"
|
|
440
|
-
>
|
|
441
|
-
<svg lucidePencil size="15" aria-hidden="true" />
|
|
442
|
-
<span>Editar</span>
|
|
443
|
-
</button>
|
|
444
|
-
}
|
|
445
|
-
@if (canRemove()) {
|
|
446
|
-
<button
|
|
447
|
-
app-button
|
|
448
|
-
variant="danger"
|
|
449
|
-
type="button"
|
|
450
|
-
size="sm"
|
|
451
|
-
class="flex-1"
|
|
452
|
-
(click)="$event.stopPropagation(); removeRow(row)"
|
|
453
|
-
>
|
|
454
|
-
<svg lucideTrash2 size="15" aria-hidden="true" />
|
|
455
|
-
<span>Eliminar</span>
|
|
456
|
-
</button>
|
|
457
|
-
}
|
|
458
|
-
</div>
|
|
459
|
-
}
|
|
460
|
-
</li>
|
|
461
|
-
}
|
|
462
|
-
</ul>
|
|
463
|
-
}
|
|
464
|
-
}
|
|
465
|
-
}
|
|
466
|
-
|
|
467
|
-
@if (quickEditRow(); as row) {
|
|
468
|
-
<app-drawer-form
|
|
469
|
-
[open]="true"
|
|
470
|
-
[schema]="schema()"
|
|
471
|
-
mode="edit"
|
|
472
|
-
[recordKey]="keyOf(row)"
|
|
473
|
-
[fields]="quickEditFields()"
|
|
474
|
-
title="Edición rápida"
|
|
475
|
-
size="sm"
|
|
476
|
-
(close)="quickEditRow.set(null)"
|
|
477
|
-
(saved)="quickEditSaved()"
|
|
478
|
-
/>
|
|
479
|
-
}
|
|
480
|
-
</section>
|
|
481
|
-
`,
|
|
482
|
-
})
|
|
483
|
-
export class ListView {
|
|
484
|
-
readonly schema = input.required<ResourceSchema>();
|
|
485
|
-
|
|
486
|
-
/** Emitido al pulsar «Editar» cuando `schema.detail.enabled === true`. */
|
|
487
|
-
readonly viewRecord = output<Row>();
|
|
488
|
-
|
|
489
|
-
private readonly api = inject(ApiService);
|
|
490
|
-
private readonly auth = inject(AuthService);
|
|
491
|
-
private readonly router = inject(Router);
|
|
492
|
-
readonly notifications = inject(NotificationService);
|
|
493
|
-
private readonly actionService = inject(ActionService);
|
|
494
|
-
|
|
495
|
-
readonly status = signal<Status>('loading');
|
|
496
|
-
readonly rows = signal<Row[]>([]);
|
|
497
|
-
readonly query = signal('');
|
|
498
|
-
readonly filterValues = signal<Record<string, string>>({});
|
|
499
|
-
/** Clave del registro cuyo interruptor `active` está guardando. */
|
|
500
|
-
readonly savingKey = signal<string | null>(null);
|
|
501
|
-
/** Fila que se está editando en el drawer (plan 6.5); `null` = cerrado. */
|
|
502
|
-
readonly quickEditRow = signal<Row | null>(null);
|
|
503
|
-
|
|
504
|
-
/** Evita que una respuesta lenta pise el recurso ya cambiado. */
|
|
505
|
-
private loadEpoch = 0;
|
|
506
|
-
|
|
507
|
-
constructor() {
|
|
508
|
-
effect(() => {
|
|
509
|
-
const schema = this.schema();
|
|
510
|
-
this.query.set('');
|
|
511
|
-
this.filterValues.set({});
|
|
512
|
-
void this.load(schema);
|
|
513
|
-
});
|
|
514
|
-
}
|
|
515
|
-
|
|
516
|
-
/** Búsqueda + filtros + orden `positionField` ascendente (cliente). */
|
|
517
|
-
readonly visibleRows = computed(() => {
|
|
518
|
-
const schema = this.schema();
|
|
519
|
-
let rows = this.rows();
|
|
520
|
-
|
|
521
|
-
const query = this.query().trim().toLowerCase();
|
|
522
|
-
if (query !== '' && schema.search?.length) {
|
|
523
|
-
rows = rows.filter((row) =>
|
|
524
|
-
schema.search!.some((key) =>
|
|
525
|
-
String(getValue(row, key) ?? '')
|
|
526
|
-
.toLowerCase()
|
|
527
|
-
.includes(query),
|
|
528
|
-
),
|
|
529
|
-
);
|
|
530
|
-
}
|
|
531
|
-
|
|
532
|
-
for (const filter of schema.filters ?? []) {
|
|
533
|
-
const value = this.filterValues()[filter.key] ?? '';
|
|
534
|
-
if (value === '') {
|
|
535
|
-
continue;
|
|
536
|
-
}
|
|
537
|
-
rows = rows.filter((row) => {
|
|
538
|
-
const cell = getValue(row, filter.key);
|
|
539
|
-
if (filter.type === 'boolean') {
|
|
540
|
-
return (cell === true || cell === 'true') === (value === 'yes');
|
|
541
|
-
}
|
|
542
|
-
if (filter.type === 'select') {
|
|
543
|
-
return String(cell ?? '') === value;
|
|
544
|
-
}
|
|
545
|
-
return String(cell ?? '')
|
|
546
|
-
.toLowerCase()
|
|
547
|
-
.includes(value.toLowerCase());
|
|
548
|
-
});
|
|
549
|
-
}
|
|
550
|
-
|
|
551
|
-
const positionField = schema.positionField;
|
|
552
|
-
if (positionField) {
|
|
553
|
-
const positionOf = (row: Row): number => {
|
|
554
|
-
const position = Number(getValue(row, positionField));
|
|
555
|
-
return Number.isFinite(position) ? position : Number.POSITIVE_INFINITY;
|
|
556
|
-
};
|
|
557
|
-
rows = [...rows].sort((a, b) => positionOf(a) - positionOf(b));
|
|
558
|
-
}
|
|
559
|
-
|
|
560
|
-
return rows;
|
|
561
|
-
});
|
|
562
|
-
|
|
563
|
-
readonly currencyCode = computed(() => {
|
|
564
|
-
// Moneda del singleton `site` (campo `currency`); Fase 12 podrá usar el
|
|
565
|
-
// valor real cargado del backend.
|
|
566
|
-
const field = getSchema('site')?.fields.find((f) => f.key === 'currency');
|
|
567
|
-
return field?.type === 'text' && field.default ? field.default : 'USD';
|
|
568
|
-
});
|
|
569
|
-
|
|
570
|
-
private readonly permissions = computed(() => this.schema().permissions);
|
|
571
|
-
readonly canCreate = computed(() => this.auth.canDoOperation(this.schema(), 'create'));
|
|
572
|
-
readonly canUpdate = computed(() => this.auth.canDoOperation(this.schema(), 'update'));
|
|
573
|
-
readonly canRemove = computed(() => this.auth.canDoOperation(this.schema(), 'remove'));
|
|
574
|
-
|
|
575
|
-
/** El atajo existe sólo si el schema lo declara y hay permiso de editar. */
|
|
576
|
-
readonly canQuickEdit = computed(
|
|
577
|
-
() => this.canUpdate() && (this.schema().quickEdit?.length ?? 0) > 0,
|
|
578
|
-
);
|
|
579
|
-
|
|
580
|
-
/** Subconjunto de campos del drawer, en el orden declarado en el schema. */
|
|
581
|
-
readonly quickEditFields = computed<readonly FieldSchema[]>(() => {
|
|
582
|
-
const keys = this.schema().quickEdit ?? [];
|
|
583
|
-
const byKey = new Map(this.schema().fields.map((field) => [field.key, field]));
|
|
584
|
-
return keys
|
|
585
|
-
.map((key) => byKey.get(key))
|
|
586
|
-
.filter((field): field is FieldSchema => field !== undefined);
|
|
587
|
-
});
|
|
588
|
-
|
|
589
|
-
readonly hasRowActions = computed(
|
|
590
|
-
() => this.canUpdate() || this.canRemove() || (this.schema().actions?.length ?? 0) > 0,
|
|
591
|
-
);
|
|
592
|
-
|
|
593
|
-
// ── Reordenamiento (base.md §8) ──
|
|
594
|
-
|
|
595
|
-
/** Mientras persiste un movimiento: bloquea drag y botones. */
|
|
596
|
-
readonly reordering = signal(false);
|
|
597
|
-
|
|
598
|
-
/** Sin búsqueda ni filtros aplicados (base.md §10). */
|
|
599
|
-
private readonly hasActiveFilters = computed(() => {
|
|
600
|
-
if (this.query().trim() !== '') {
|
|
601
|
-
return true;
|
|
602
|
-
}
|
|
603
|
-
return Object.values(this.filterValues()).some((value) => value !== '');
|
|
604
|
-
});
|
|
605
|
-
|
|
606
|
-
/**
|
|
607
|
-
* Reordenable sólo si el schema lo declara, el permiso lo permite y no hay
|
|
608
|
-
* filtros activos: con filtros filtrados, el índice visual no corresponde a
|
|
609
|
-
* la posición real.
|
|
610
|
-
*/
|
|
611
|
-
readonly canReorder = computed(() => {
|
|
612
|
-
const schema = this.schema();
|
|
613
|
-
return (
|
|
614
|
-
schema.sortable === true &&
|
|
615
|
-
schema.positionField !== undefined &&
|
|
616
|
-
schema.permissions?.reorder !== false && // reorder está en permissions, no en policies aún
|
|
617
|
-
!this.hasActiveFilters()
|
|
618
|
-
);
|
|
619
|
-
});
|
|
620
|
-
|
|
621
|
-
readonly dropDisabled = computed(() => !this.canReorder() || this.reordering());
|
|
622
|
-
|
|
623
|
-
// ── Acciones de datos ──
|
|
624
|
-
|
|
625
|
-
setQuery(value: string): void {
|
|
626
|
-
this.query.set(value);
|
|
627
|
-
}
|
|
628
|
-
|
|
629
|
-
setFilter(key: string, value: string): void {
|
|
630
|
-
this.filterValues.update((values) => ({ ...values, [key]: value }));
|
|
631
|
-
}
|
|
632
|
-
|
|
633
|
-
clearFilters(): void {
|
|
634
|
-
this.query.set('');
|
|
635
|
-
this.filterValues.set({});
|
|
636
|
-
}
|
|
637
|
-
|
|
638
|
-
retry(): void {
|
|
639
|
-
void this.load(this.schema());
|
|
640
|
-
}
|
|
641
|
-
|
|
642
|
-
isRowClickable(): boolean {
|
|
643
|
-
return Boolean(this.schema().detail?.enabled || this.canUpdate());
|
|
644
|
-
}
|
|
645
|
-
|
|
646
|
-
goView(row: Row): void {
|
|
647
|
-
this.viewRecord.emit(row);
|
|
648
|
-
}
|
|
649
|
-
|
|
650
|
-
onRowClick(row: Row): void {
|
|
651
|
-
if (this.schema().detail?.enabled) {
|
|
652
|
-
this.goView(row);
|
|
653
|
-
} else if (this.canUpdate()) {
|
|
654
|
-
this.goEdit(row);
|
|
655
|
-
}
|
|
656
|
-
}
|
|
657
|
-
|
|
658
|
-
goCreate(): void {
|
|
659
|
-
void this.router.navigate(['/', this.schema().id, 'new']);
|
|
660
|
-
}
|
|
661
|
-
|
|
662
|
-
goEdit(row: Row): void {
|
|
663
|
-
if (this.schema().detail?.enabled === true) {
|
|
664
|
-
this.viewRecord.emit(row);
|
|
665
|
-
return;
|
|
666
|
-
}
|
|
667
|
-
void this.router.navigate(['/', this.schema().id, this.keyOf(row), 'edit']);
|
|
668
|
-
}
|
|
669
|
-
|
|
670
|
-
/** Cerró con guardado: se recarga la fila (la respuesta puede venir achatada). */
|
|
671
|
-
quickEditSaved(): void {
|
|
672
|
-
this.quickEditRow.set(null);
|
|
673
|
-
void this.load(this.schema());
|
|
674
|
-
}
|
|
675
|
-
|
|
676
|
-
async toggleActive(row: Row, checked: boolean): Promise<void> {
|
|
677
|
-
const schema = this.schema();
|
|
678
|
-
const key = this.keyOf(row);
|
|
679
|
-
this.savingKey.set(key);
|
|
680
|
-
try {
|
|
681
|
-
await firstValueFrom(this.api.update(schema.endpoint, key, { ...row, active: checked }));
|
|
682
|
-
this.rows.update((rows) =>
|
|
683
|
-
rows.map((candidate) =>
|
|
684
|
-
this.keyOf(candidate) === key ? { ...candidate, active: checked } : candidate,
|
|
685
|
-
),
|
|
686
|
-
);
|
|
687
|
-
} catch (err) {
|
|
688
|
-
// El cambio no se guardó: la re-renderización revierte el interruptor.
|
|
689
|
-
this.notifications.error(errorMessage(err, 'No se pudo actualizar el registro.'));
|
|
690
|
-
} finally {
|
|
691
|
-
this.savingKey.set(null);
|
|
692
|
-
}
|
|
693
|
-
}
|
|
694
|
-
|
|
695
|
-
/** Drop del CDK: `currentIndex` ya es el índice en la lista sin el elemento. */
|
|
696
|
-
onDrop(event: CdkDragDrop<Row[]>): void {
|
|
697
|
-
if (event.previousContainer !== event.container || event.previousIndex === event.currentIndex) {
|
|
698
|
-
return;
|
|
699
|
-
}
|
|
700
|
-
const row = this.visibleRows()[event.previousIndex];
|
|
701
|
-
if (row !== undefined) {
|
|
702
|
-
void this.moveRow(this.keyOf(row), event.currentIndex);
|
|
703
|
-
}
|
|
704
|
-
}
|
|
705
|
-
|
|
706
|
-
/** Botones subir/bajar (accesibilidad y móvil, base.md §13.10). */
|
|
707
|
-
moveBy(key: string, delta: -1 | 1): void {
|
|
708
|
-
const index = this.visibleRows().findIndex((row) => this.keyOf(row) === key);
|
|
709
|
-
if (index < 0) {
|
|
710
|
-
return;
|
|
711
|
-
}
|
|
712
|
-
void this.moveRow(key, index + delta);
|
|
713
|
-
}
|
|
714
|
-
|
|
715
|
-
/**
|
|
716
|
-
* Un movimiento → UNA intención al backend (baseapi §2.3 y §9):
|
|
717
|
-
* `{ reorder: { key, after | toStart | toEnd } }`, nunca posiciones
|
|
718
|
-
* calculadas. El índice objetivo es la posición FINAL del registro (el
|
|
719
|
-
* `currentIndex` del CDK ya es el índice de inserción en la lista sin el
|
|
720
|
-
* elemento, idéntico al índice final; subir/bajar suma/resta 1). El
|
|
721
|
-
* backend re-balancea, recalcula `position` y devuelve el listado fresco:
|
|
722
|
-
* sin escrituras optimistas — si falla, no cambió nada. `gap-sorting`
|
|
723
|
-
* quedó sólo para los campos con listas locales.
|
|
724
|
-
*/
|
|
725
|
-
private async moveRow(key: string, targetIndex: number): Promise<void> {
|
|
726
|
-
const schema = this.schema();
|
|
727
|
-
const path = schema.endpoint.list;
|
|
728
|
-
if (!path || !schema.positionField || this.reordering()) {
|
|
729
|
-
return;
|
|
730
|
-
}
|
|
731
|
-
const rows = this.visibleRows();
|
|
732
|
-
const from = rows.findIndex((row) => this.keyOf(row) === key);
|
|
733
|
-
if (from < 0) {
|
|
734
|
-
return;
|
|
735
|
-
}
|
|
736
|
-
|
|
737
|
-
// Índice final deseado, acotado a la lista SIN el elemento movido.
|
|
738
|
-
const without = rows.filter((row) => this.keyOf(row) !== key);
|
|
739
|
-
const target = Math.min(Math.max(targetIndex, 0), without.length);
|
|
740
|
-
if (target === from) {
|
|
741
|
-
return;
|
|
742
|
-
}
|
|
743
|
-
|
|
744
|
-
const reorder: Record<string, unknown> =
|
|
745
|
-
target === 0
|
|
746
|
-
? { key, toStart: true }
|
|
747
|
-
: target >= without.length
|
|
748
|
-
? { key, toEnd: true }
|
|
749
|
-
: { key, after: this.keyOf(without[target - 1]) };
|
|
750
|
-
|
|
751
|
-
this.reordering.set(true);
|
|
752
|
-
try {
|
|
753
|
-
const fresh = await firstValueFrom(this.api.request<Row[]>('PUT', path, { reorder }));
|
|
754
|
-
if (Array.isArray(fresh)) {
|
|
755
|
-
this.rows.set(fresh);
|
|
756
|
-
}
|
|
757
|
-
this.notifications.success('Orden actualizado.');
|
|
758
|
-
} catch (err) {
|
|
759
|
-
this.notifications.error(errorMessage(err, 'No se pudo guardar el orden.'));
|
|
760
|
-
} finally {
|
|
761
|
-
this.reordering.set(false);
|
|
762
|
-
}
|
|
763
|
-
}
|
|
764
|
-
|
|
765
|
-
async removeRow(row: Row): Promise<void> {
|
|
766
|
-
const schema = this.schema();
|
|
767
|
-
const key = this.keyOf(row);
|
|
768
|
-
const accepted = await this.notifications.confirm({
|
|
769
|
-
title: `Eliminar «${this.titleOf(row)}»`,
|
|
770
|
-
message: 'Esta acción no se puede deshacer.',
|
|
771
|
-
confirmLabel: 'Eliminar',
|
|
772
|
-
cancelLabel: 'Cancelar',
|
|
773
|
-
});
|
|
774
|
-
if (!accepted) {
|
|
775
|
-
return;
|
|
776
|
-
}
|
|
777
|
-
try {
|
|
778
|
-
await firstValueFrom(this.api.remove(schema.endpoint, key));
|
|
779
|
-
this.notifications.success('Registro eliminado.');
|
|
780
|
-
this.rows.update((rows) => rows.filter((candidate) => this.keyOf(candidate) !== key));
|
|
781
|
-
} catch (err) {
|
|
782
|
-
this.notifications.error(errorMessage(err, 'No se pudo eliminar el registro.'));
|
|
783
|
-
}
|
|
784
|
-
}
|
|
785
|
-
|
|
786
|
-
/**
|
|
787
|
-
* Ejecuta una acción del schema. El comportamiento vive en el registro de
|
|
788
|
-
* acciones (R3.9–R3.11): la vista sólo aporta el registro y los puertos.
|
|
789
|
-
*/
|
|
790
|
-
async runAction(action: ResourceAction, row: Row): Promise<void> {
|
|
791
|
-
await this.actionService.run(action, {
|
|
792
|
-
record: row,
|
|
793
|
-
resource: this.schema().id,
|
|
794
|
-
api: this.api,
|
|
795
|
-
router: this.router,
|
|
796
|
-
toast: this.notifications,
|
|
797
|
-
user: this.auth.user(),
|
|
798
|
-
keyField: this.schema().keyField,
|
|
799
|
-
});
|
|
800
|
-
}
|
|
801
|
-
|
|
802
|
-
/** Icono que el descriptor declara para la acción; sin él, el rótulo. */
|
|
803
|
-
protected iconOf(action: ResourceAction): LucideIcon | undefined {
|
|
804
|
-
return getAction(actionType(action))?.icon;
|
|
805
|
-
}
|
|
806
|
-
|
|
807
|
-
// ── Helpers de celda (uso en template) ──
|
|
808
|
-
|
|
809
|
-
keyOf(row: Row): string {
|
|
810
|
-
return String(row[this.schema().keyField] ?? '');
|
|
811
|
-
}
|
|
812
|
-
|
|
813
|
-
titleOf(row: Row): string {
|
|
814
|
-
return String(row[this.schema().titleField] ?? '');
|
|
815
|
-
}
|
|
816
|
-
|
|
817
|
-
/**
|
|
818
|
-
* Formato efectivo de la columna (R4.4 · «declarada, no implícita»).
|
|
819
|
-
*
|
|
820
|
-
* `column.format` sólo sobreescribe; si no, manda la tabla de
|
|
821
|
-
* `column-defaults` según el `type` del campo — nunca el `?? 'text'`
|
|
822
|
-
* implícito que había.
|
|
823
|
-
*/
|
|
824
|
-
formatOf(column: ListColumn): ListColumnFormat {
|
|
825
|
-
return formatoDeColumna(column, this.schema().fields);
|
|
826
|
-
}
|
|
827
|
-
|
|
828
|
-
/**
|
|
829
|
-
* Contexto de la celda que recibe el formato (R4.3 · `extras.data`).
|
|
830
|
-
*
|
|
831
|
-
* Concentra aquí lo que sólo la vista sabe — permisos, moneda, guardado y
|
|
832
|
-
* la escritura de la fila — para que «añadir un formato» no la toque.
|
|
833
|
-
*/
|
|
834
|
-
cellOf(row: Row, column: ListColumn): ColumnCell {
|
|
835
|
-
return {
|
|
836
|
-
row,
|
|
837
|
-
column,
|
|
838
|
-
fields: this.schema().fields,
|
|
839
|
-
title: this.titleOf(row),
|
|
840
|
-
canUpdate: this.canUpdate(),
|
|
841
|
-
saving: this.savingKey() === this.keyOf(row),
|
|
842
|
-
currency: this.currencyCode(),
|
|
843
|
-
toggle: (checked: boolean) => void this.toggleActive(row, checked),
|
|
844
|
-
};
|
|
845
|
-
}
|
|
846
|
-
|
|
847
|
-
filterOptions(filter: ListFilter): SelectOption[] {
|
|
848
|
-
if (filter.type === 'boolean') {
|
|
849
|
-
return BOOLEAN_FILTER_OPTIONS;
|
|
850
|
-
}
|
|
851
|
-
if (filter.type === 'select') {
|
|
852
|
-
return [{ value: '', label: 'Todos' }, ...(filter.options ?? [])];
|
|
853
|
-
}
|
|
854
|
-
return [];
|
|
855
|
-
}
|
|
856
|
-
|
|
857
|
-
inputValue(event: Event): string {
|
|
858
|
-
return (event.target as HTMLInputElement).value;
|
|
859
|
-
}
|
|
860
|
-
|
|
861
|
-
selectValue(event: Event): string {
|
|
862
|
-
return (event.target as HTMLSelectElement).value;
|
|
863
|
-
}
|
|
864
|
-
|
|
865
|
-
private async load(schema: ResourceSchema): Promise<void> {
|
|
866
|
-
const epoch = ++this.loadEpoch;
|
|
867
|
-
this.status.set('loading');
|
|
868
|
-
try {
|
|
869
|
-
const rows = await firstValueFrom(this.api.list<Row>(schema.endpoint));
|
|
870
|
-
if (epoch !== this.loadEpoch) {
|
|
871
|
-
return;
|
|
872
|
-
}
|
|
873
|
-
this.rows.set(Array.isArray(rows) ? rows : []);
|
|
874
|
-
this.status.set('ready');
|
|
875
|
-
} catch {
|
|
876
|
-
if (epoch !== this.loadEpoch) {
|
|
877
|
-
return;
|
|
878
|
-
}
|
|
879
|
-
this.status.set('error');
|
|
880
|
-
}
|
|
881
|
-
}
|
|
882
|
-
}
|