@tinya-code/gas-admin-ui 0.2.0-alpha.13 → 0.2.0-alpha.15
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/ng-package.json +7 -0
- package/package.json +3 -15
- package/src/lib/actions/action-registry.spec.ts +52 -0
- package/src/lib/actions/action-registry.ts +126 -0
- package/src/lib/actions/action-service.spec.ts +215 -0
- package/src/lib/actions/action-service.ts +87 -0
- package/src/lib/actions/descriptors.spec.ts +155 -0
- package/src/lib/actions/descriptors.ts +154 -0
- package/src/lib/columns/column-cell.ts +33 -0
- package/src/lib/columns/column-defaults.spec.ts +99 -0
- package/src/lib/columns/column-defaults.ts +84 -0
- package/src/lib/columns/column-format.spec.ts +274 -0
- package/src/lib/columns/descriptors.ts +113 -0
- package/src/lib/columns/format-badge.ts +51 -0
- package/src/lib/columns/format-boolean.ts +58 -0
- package/src/lib/columns/format-currency.ts +30 -0
- package/src/lib/columns/format-date.ts +35 -0
- package/src/lib/columns/format-number.ts +32 -0
- package/src/lib/columns/format-text.ts +23 -0
- package/src/lib/columns/format-thumbnail.ts +63 -0
- package/src/lib/core/adapters/firebase-auth.adapter.ts +6 -0
- package/src/lib/core/guards/auth.guard.spec.ts +88 -0
- package/src/lib/core/guards/auth.guard.ts +48 -0
- package/src/lib/core/guards/unsaved-changes.guard.ts +16 -0
- package/src/lib/core/interceptors/api-error.interceptor.spec.ts +182 -0
- package/src/lib/core/interceptors/api-error.interceptor.ts +87 -0
- package/src/lib/core/models/api.model.ts +104 -0
- package/src/lib/core/models/schema-descriptions.ts +118 -0
- package/src/lib/core/models/schema.model.ts +1097 -0
- package/src/lib/core/registry/composition-validator.spec.ts +358 -0
- package/src/lib/core/registry/composition-validator.ts +270 -0
- package/src/lib/core/registry/field-validators.spec.ts +199 -0
- package/src/lib/core/registry/field-validators.ts +440 -0
- package/src/lib/core/registry/render-host.spec.ts +208 -0
- package/src/lib/core/registry/render-host.ts +646 -0
- package/src/lib/core/registry/render-registry.spec.ts +96 -0
- package/src/lib/core/registry/render-registry.ts +312 -0
- package/src/lib/core/registry/schema-catalog.ts +156 -0
- package/src/lib/core/services/api.service.spec.ts +1142 -0
- package/src/lib/core/services/api.service.ts +371 -0
- package/src/lib/core/services/auth.service.spec.ts +653 -0
- package/src/lib/core/services/auth.service.ts +503 -0
- package/src/lib/core/services/config.service.spec.ts +121 -0
- package/src/lib/core/services/config.service.ts +119 -0
- package/src/lib/core/services/data-source.service.spec.ts +179 -0
- package/src/lib/core/services/data-source.service.ts +142 -0
- package/src/lib/core/services/error-mapper.service.spec.ts +50 -0
- package/src/lib/core/services/error-mapper.service.ts +49 -0
- package/src/lib/core/services/notification.service.ts +73 -0
- package/src/lib/core/services/relation-drawer.service.ts +41 -0
- package/src/lib/core/services/theme.service.spec.ts +106 -0
- package/src/lib/core/services/theme.service.ts +86 -0
- package/src/lib/core/services/transport.spec.ts +198 -0
- package/src/lib/core/services/transport.ts +164 -0
- package/src/lib/core/services/upload.service.ts +81 -0
- package/src/lib/core/testing/categories.schema.ts +125 -0
- package/src/lib/core/testing/orders.schema.ts +372 -0
- package/src/lib/core/testing/products.schema.ts +258 -0
- package/src/lib/core/testing/site.schema.ts +195 -0
- package/src/lib/core/testing/test-catalog.ts +21 -0
- package/src/lib/core/tokens.ts +112 -0
- package/src/lib/core/utils/clipboard.spec.ts +55 -0
- package/src/lib/core/utils/clipboard.ts +90 -0
- package/src/lib/core/utils/context-resolver.spec.ts +95 -0
- package/src/lib/core/utils/context-resolver.ts +101 -0
- package/src/lib/core/utils/response-unwrapper.spec.ts +70 -0
- package/src/lib/core/utils/response-unwrapper.ts +103 -0
- package/src/lib/core/utils/source-options.spec.ts +96 -0
- package/src/lib/core/utils/source-options.ts +54 -0
- package/src/lib/core/utils/template-interpolator.spec.ts +59 -0
- package/src/lib/core/utils/template-interpolator.ts +56 -0
- package/src/lib/core/view-navigation/view-navigation.service.spec.ts +207 -0
- package/src/lib/core/view-navigation/view-navigation.service.ts +132 -0
- package/src/lib/fields/boolean/boolean.descriptor.ts +11 -0
- package/src/lib/fields/boolean/boolean.ts +40 -0
- package/src/lib/fields/color/color.descriptor.ts +12 -0
- package/src/lib/fields/color/color.ts +34 -0
- package/src/lib/fields/currency/currency.descriptor.ts +12 -0
- package/src/lib/fields/currency/currency.ts +67 -0
- package/src/lib/fields/date/date.descriptor.ts +12 -0
- package/src/lib/fields/date/date.ts +36 -0
- package/src/lib/fields/descriptors.ts +122 -0
- package/src/lib/fields/display/display-badge.ts +65 -0
- package/src/lib/fields/display/display-date.ts +39 -0
- package/src/lib/fields/display/display-defaults.ts +84 -0
- package/src/lib/fields/display/display-file.ts +41 -0
- package/src/lib/fields/display/display-image.ts +54 -0
- package/src/lib/fields/display/display-list.ts +107 -0
- package/src/lib/fields/display/display-number.ts +41 -0
- package/src/lib/fields/display/display-text.ts +42 -0
- package/src/lib/fields/display/display.spec.ts +242 -0
- package/src/lib/fields/dropdown/dropdown.descriptor.ts +12 -0
- package/src/lib/fields/dropdown/dropdown.ts +45 -0
- package/src/lib/fields/email/email.descriptor.ts +12 -0
- package/src/lib/fields/email/email.ts +28 -0
- package/src/lib/fields/field-aria.ts +60 -0
- package/src/lib/fields/field-host/field-host.spec.ts +118 -0
- package/src/lib/fields/field-host/field-host.ts +47 -0
- package/src/lib/fields/field-node.ts +88 -0
- package/src/lib/fields/file/file.descriptor.ts +14 -0
- package/src/lib/fields/file/file.spec.ts +134 -0
- package/src/lib/fields/file/file.ts +281 -0
- package/src/lib/fields/group/group.descriptor.ts +21 -0
- package/src/lib/fields/group/group.ts +110 -0
- package/src/lib/fields/image/image.descriptor.ts +12 -0
- package/src/lib/fields/image/image.spec.ts +117 -0
- package/src/lib/fields/image/image.ts +281 -0
- package/src/lib/fields/key-value/key-value.descriptor.ts +11 -0
- package/src/lib/fields/key-value/key-value.ts +283 -0
- package/src/lib/fields/list/list.descriptor.ts +21 -0
- package/src/lib/fields/list/list.ts +376 -0
- package/src/lib/fields/multiselect/multiselect.descriptor.ts +11 -0
- package/src/lib/fields/multiselect/multiselect.spec.ts +142 -0
- package/src/lib/fields/multiselect/multiselect.ts +152 -0
- package/src/lib/fields/number/number.descriptor.ts +12 -0
- package/src/lib/fields/number/number.ts +40 -0
- package/src/lib/fields/phone/phone.descriptor.ts +12 -0
- package/src/lib/fields/phone/phone.ts +46 -0
- package/src/lib/fields/readonly-text/readonly-text.descriptor.ts +12 -0
- package/src/lib/fields/readonly-text/readonly-text.ts +58 -0
- package/src/lib/fields/relation/relation.descriptor.ts +12 -0
- package/src/lib/fields/relation/relation.spec.ts +193 -0
- package/src/lib/fields/relation/relation.ts +224 -0
- package/src/lib/fields/rich-select/rich-select.descriptor.ts +12 -0
- package/src/lib/fields/rich-select/rich-select.ts +175 -0
- package/src/lib/fields/select/radio-mode.ts +25 -0
- package/src/lib/fields/select/select.descriptor.ts +12 -0
- package/src/lib/fields/select/select.spec.ts +162 -0
- package/src/lib/fields/select/select.ts +210 -0
- package/src/lib/fields/slug/slug.descriptor.ts +19 -0
- package/src/lib/fields/slug/slug.ts +67 -0
- package/src/lib/fields/string-list/string-list.descriptor.ts +11 -0
- package/src/lib/fields/string-list/string-list.ts +222 -0
- package/src/lib/fields/text/text.descriptor.ts +12 -0
- package/src/lib/fields/text/text.ts +35 -0
- package/src/lib/fields/textarea/textarea.descriptor.ts +12 -0
- package/src/lib/fields/textarea/textarea.spec.ts +117 -0
- package/src/lib/fields/textarea/textarea.ts +126 -0
- package/src/lib/fields/time/time.descriptor.ts +12 -0
- package/src/lib/fields/time/time.ts +28 -0
- package/src/lib/fields/unknown-render/unknown-render.ts +49 -0
- package/src/lib/fields/url/url.descriptor.ts +12 -0
- package/src/lib/fields/url/url.ts +53 -0
- package/src/lib/gas-admin-ui.spec.ts +21 -0
- package/src/lib/gas-admin-ui.ts +9 -0
- package/src/lib/layouts/descriptors.ts +9 -0
- package/src/lib/pages/dashboard/dashboard.ts +167 -0
- package/src/lib/pages/detail-view/detail-view.spec.ts +333 -0
- package/src/lib/pages/detail-view/detail-view.ts +570 -0
- package/src/lib/pages/drawer-form/drawer-form.ts +228 -0
- package/src/lib/pages/form-view/form-model.ts +88 -0
- package/src/lib/pages/form-view/form-schema.ts +255 -0
- package/src/lib/pages/form-view/form-view.ts +1622 -0
- package/src/lib/pages/list-view/list-view.formats.spec.ts +214 -0
- package/src/lib/pages/list-view/list-view.ts +882 -0
- package/src/lib/pages/login/login.spec.ts +109 -0
- package/src/lib/pages/login/login.ts +227 -0
- package/src/lib/pages/resource-page/resource-page.ts +234 -0
- package/src/lib/shared/components/badge/badge.ts +35 -0
- package/src/lib/shared/components/button/button.ts +65 -0
- package/src/lib/shared/components/confirm-dialog/confirm-dialog.ts +48 -0
- package/src/lib/shared/components/copy-button/copy-button.ts +56 -0
- package/src/lib/shared/components/copy-share/copy-share.spec.ts +122 -0
- package/src/lib/shared/components/copy-share/copy-share.ts +107 -0
- package/src/lib/shared/components/drawer/drawer.spec.ts +127 -0
- package/src/lib/shared/components/drawer/drawer.ts +172 -0
- package/src/lib/shared/components/empty-state/empty-state.ts +30 -0
- package/src/lib/shared/components/modal/modal.spec.ts +149 -0
- package/src/lib/shared/components/modal/modal.ts +145 -0
- package/src/lib/shared/components/post-create-modal/post-create-modal.spec.ts +165 -0
- package/src/lib/shared/components/post-create-modal/post-create-modal.ts +159 -0
- package/src/lib/shared/components/skeleton/skeleton.ts +31 -0
- package/src/lib/shared/components/toast/toast.ts +58 -0
- package/src/lib/shared/utils/col-span.ts +25 -0
- package/src/lib/shared/utils/condition-evaluator.ts +53 -0
- package/src/lib/shared/utils/depends-on.ts +48 -0
- package/src/lib/shared/utils/dialog-focus.ts +28 -0
- package/src/lib/shared/utils/field-path.ts +49 -0
- package/src/lib/shared/utils/gap-sorting.ts +144 -0
- package/src/lib/shared/utils/seed.spec.ts +64 -0
- package/src/lib/shared/utils/seed.ts +149 -0
- package/src/lib/shared/utils/slugify.ts +13 -0
- package/src/lib/shared/utils/validators.ts +41 -0
- package/src/lib/shell/layout/layout.ts +63 -0
- package/src/lib/shell/shell.routes.spec.ts +59 -0
- package/src/lib/shell/shell.routes.ts +122 -0
- package/src/lib/shell/sidebar/sidebar.ts +139 -0
- package/src/lib/shell/theme-toggle/theme-toggle.ts +74 -0
- package/src/lib/views/calendar-view/calendar-model.ts +426 -0
- package/src/lib/views/calendar-view/calendar-view.spec.ts +557 -0
- package/src/lib/views/calendar-view/calendar-view.ts +568 -0
- package/src/lib/views/composer/composer-host.spec.ts +301 -0
- package/src/lib/views/composer/composer-host.ts +248 -0
- package/src/lib/views/composer/composer-imports.spec.ts +54 -0
- package/src/lib/views/composer/view-context.spec.ts +117 -0
- package/src/lib/views/composer/view-context.ts +109 -0
- package/src/lib/views/composer/view-slot.spec.ts +130 -0
- package/src/lib/views/composer/view-slot.ts +178 -0
- package/src/lib/views/descriptors.ts +46 -0
- package/src/lib/views/kanban-view/kanban-model.ts +173 -0
- package/src/lib/views/kanban-view/kanban-view.spec.ts +540 -0
- package/src/lib/views/kanban-view/kanban-view.ts +327 -0
- package/src/lib/widgets/bar-chart/bar-chart.descriptor.ts +17 -0
- package/src/lib/widgets/bar-chart/bar-chart.spec.ts +66 -0
- package/src/lib/widgets/bar-chart/bar-chart.ts +76 -0
- package/src/lib/widgets/chart-line/chart-line.descriptor.ts +17 -0
- package/src/lib/widgets/chart-line/chart-line.spec.ts +113 -0
- package/src/lib/widgets/chart-line/chart-line.ts +219 -0
- package/src/lib/widgets/descriptors.ts +29 -0
- package/src/lib/widgets/metric-card/metric-card.descriptor.ts +41 -0
- package/src/lib/widgets/metric-card/metric-card.spec.ts +52 -0
- package/src/lib/widgets/metric-card/metric-card.ts +48 -0
- package/src/lib/widgets/quick-actions/quick-actions.descriptor.ts +13 -0
- package/src/lib/widgets/quick-actions/quick-actions.spec.ts +61 -0
- package/src/lib/widgets/quick-actions/quick-actions.ts +100 -0
- package/src/lib/widgets/record-list/record-list.descriptor.ts +16 -0
- package/src/lib/widgets/record-list/record-list.spec.ts +79 -0
- package/src/lib/widgets/record-list/record-list.ts +115 -0
- package/src/lib/widgets/series.spec.ts +42 -0
- package/src/lib/widgets/series.ts +56 -0
- package/src/lib/widgets/status-progress/status-progress.descriptor.ts +19 -0
- package/src/lib/widgets/status-progress/status-progress.spec.ts +63 -0
- package/src/lib/widgets/status-progress/status-progress.ts +104 -0
- package/src/lib/widgets/widget-host/widget-host.spec.ts +179 -0
- package/src/lib/widgets/widget-host/widget-host.ts +99 -0
- package/src/public-api.ts +104 -0
- package/tinya-code-gas-admin-ui-0.2.0-alpha.15.tgz +0 -0
- package/tsconfig.lib.json +13 -0
- package/tsconfig.lib.prod.json +11 -0
- package/tsconfig.spec.json +10 -0
- package/fesm2022/tinya-code-gas-admin-ui-badge-EeCA_l-o.mjs +0 -84
- package/fesm2022/tinya-code-gas-admin-ui-badge-EeCA_l-o.mjs.map +0 -1
- package/fesm2022/tinya-code-gas-admin-ui-bar-chart-G7sEMSBY.mjs +0 -140
- package/fesm2022/tinya-code-gas-admin-ui-bar-chart-G7sEMSBY.mjs.map +0 -1
- package/fesm2022/tinya-code-gas-admin-ui-boolean-B2ASKL0E.mjs +0 -113
- package/fesm2022/tinya-code-gas-admin-ui-boolean-B2ASKL0E.mjs.map +0 -1
- package/fesm2022/tinya-code-gas-admin-ui-button-BQjNW8KB.mjs +0 -163
- package/fesm2022/tinya-code-gas-admin-ui-button-BQjNW8KB.mjs.map +0 -1
- package/fesm2022/tinya-code-gas-admin-ui-calendar-view-DCY8bj2V.mjs +0 -1027
- package/fesm2022/tinya-code-gas-admin-ui-calendar-view-DCY8bj2V.mjs.map +0 -1
- package/fesm2022/tinya-code-gas-admin-ui-chart-line-BjdlKHoH.mjs +0 -278
- package/fesm2022/tinya-code-gas-admin-ui-chart-line-BjdlKHoH.mjs.map +0 -1
- package/fesm2022/tinya-code-gas-admin-ui-color-Dljh_By3.mjs +0 -107
- package/fesm2022/tinya-code-gas-admin-ui-color-Dljh_By3.mjs.map +0 -1
- package/fesm2022/tinya-code-gas-admin-ui-condition-evaluator-DY2k1M3C.mjs +0 -101
- package/fesm2022/tinya-code-gas-admin-ui-condition-evaluator-DY2k1M3C.mjs.map +0 -1
- package/fesm2022/tinya-code-gas-admin-ui-currency-BvuF3Z63.mjs +0 -141
- package/fesm2022/tinya-code-gas-admin-ui-currency-BvuF3Z63.mjs.map +0 -1
- package/fesm2022/tinya-code-gas-admin-ui-data-source.service-96FSbN72.mjs +0 -88
- package/fesm2022/tinya-code-gas-admin-ui-data-source.service-96FSbN72.mjs.map +0 -1
- package/fesm2022/tinya-code-gas-admin-ui-date-1SJGXZrd.mjs +0 -107
- package/fesm2022/tinya-code-gas-admin-ui-date-1SJGXZrd.mjs.map +0 -1
- package/fesm2022/tinya-code-gas-admin-ui-display-badge-fwk6sDU7.mjs +0 -140
- package/fesm2022/tinya-code-gas-admin-ui-display-badge-fwk6sDU7.mjs.map +0 -1
- package/fesm2022/tinya-code-gas-admin-ui-display-date-D7zLiDiG.mjs +0 -119
- package/fesm2022/tinya-code-gas-admin-ui-display-date-D7zLiDiG.mjs.map +0 -1
- package/fesm2022/tinya-code-gas-admin-ui-display-file-BiIhqvQC.mjs +0 -125
- package/fesm2022/tinya-code-gas-admin-ui-display-file-BiIhqvQC.mjs.map +0 -1
- package/fesm2022/tinya-code-gas-admin-ui-display-image-C1dhnZi1.mjs +0 -152
- package/fesm2022/tinya-code-gas-admin-ui-display-image-C1dhnZi1.mjs.map +0 -1
- package/fesm2022/tinya-code-gas-admin-ui-display-list-DOEuUbGe.mjs +0 -211
- package/fesm2022/tinya-code-gas-admin-ui-display-list-DOEuUbGe.mjs.map +0 -1
- package/fesm2022/tinya-code-gas-admin-ui-display-number-DDMzr4Hn.mjs +0 -116
- package/fesm2022/tinya-code-gas-admin-ui-display-number-DDMzr4Hn.mjs.map +0 -1
- package/fesm2022/tinya-code-gas-admin-ui-display-text-B8CdVNpv.mjs +0 -111
- package/fesm2022/tinya-code-gas-admin-ui-display-text-B8CdVNpv.mjs.map +0 -1
- package/fesm2022/tinya-code-gas-admin-ui-dropdown-C3blvmE1.mjs +0 -122
- package/fesm2022/tinya-code-gas-admin-ui-dropdown-C3blvmE1.mjs.map +0 -1
- package/fesm2022/tinya-code-gas-admin-ui-email-CT_O5ihA.mjs +0 -107
- package/fesm2022/tinya-code-gas-admin-ui-email-CT_O5ihA.mjs.map +0 -1
- package/fesm2022/tinya-code-gas-admin-ui-empty-state-BMc0QtoT.mjs +0 -109
- package/fesm2022/tinya-code-gas-admin-ui-empty-state-BMc0QtoT.mjs.map +0 -1
- package/fesm2022/tinya-code-gas-admin-ui-error-mapper.service-DY5y1FjE.mjs +0 -45
- package/fesm2022/tinya-code-gas-admin-ui-error-mapper.service-DY5y1FjE.mjs.map +0 -1
- package/fesm2022/tinya-code-gas-admin-ui-field-aria-BD1Wiggc.mjs +0 -50
- package/fesm2022/tinya-code-gas-admin-ui-field-aria-BD1Wiggc.mjs.map +0 -1
- package/fesm2022/tinya-code-gas-admin-ui-field-node-CIK6KNN_.mjs +0 -24
- package/fesm2022/tinya-code-gas-admin-ui-field-node-CIK6KNN_.mjs.map +0 -1
- package/fesm2022/tinya-code-gas-admin-ui-field-path-B4-FTBGZ.mjs +0 -27
- package/fesm2022/tinya-code-gas-admin-ui-field-path-B4-FTBGZ.mjs.map +0 -1
- package/fesm2022/tinya-code-gas-admin-ui-file-Dmxioog7.mjs +0 -408
- package/fesm2022/tinya-code-gas-admin-ui-file-Dmxioog7.mjs.map +0 -1
- package/fesm2022/tinya-code-gas-admin-ui-form-view-C709rc57.mjs +0 -4516
- package/fesm2022/tinya-code-gas-admin-ui-form-view-C709rc57.mjs.map +0 -1
- package/fesm2022/tinya-code-gas-admin-ui-format-badge-CK8ct5Wk.mjs +0 -82
- package/fesm2022/tinya-code-gas-admin-ui-format-badge-CK8ct5Wk.mjs.map +0 -1
- package/fesm2022/tinya-code-gas-admin-ui-format-boolean-ClzezZz-.mjs +0 -105
- package/fesm2022/tinya-code-gas-admin-ui-format-boolean-ClzezZz-.mjs.map +0 -1
- package/fesm2022/tinya-code-gas-admin-ui-format-currency-CVmniKml.mjs +0 -65
- package/fesm2022/tinya-code-gas-admin-ui-format-currency-CVmniKml.mjs.map +0 -1
- package/fesm2022/tinya-code-gas-admin-ui-format-date-Dr1YsDd_.mjs +0 -65
- package/fesm2022/tinya-code-gas-admin-ui-format-date-Dr1YsDd_.mjs.map +0 -1
- package/fesm2022/tinya-code-gas-admin-ui-format-number-CWCjpI4n.mjs +0 -65
- package/fesm2022/tinya-code-gas-admin-ui-format-number-CWCjpI4n.mjs.map +0 -1
- package/fesm2022/tinya-code-gas-admin-ui-format-text-DBOgJkH0.mjs +0 -59
- package/fesm2022/tinya-code-gas-admin-ui-format-text-DBOgJkH0.mjs.map +0 -1
- package/fesm2022/tinya-code-gas-admin-ui-format-thumbnail-PCRvxWKt.mjs +0 -94
- package/fesm2022/tinya-code-gas-admin-ui-format-thumbnail-PCRvxWKt.mjs.map +0 -1
- package/fesm2022/tinya-code-gas-admin-ui-gap-sorting-iTdy_DYG.mjs +0 -72
- package/fesm2022/tinya-code-gas-admin-ui-gap-sorting-iTdy_DYG.mjs.map +0 -1
- package/fesm2022/tinya-code-gas-admin-ui-group-h7-O8tt4.mjs +0 -238
- package/fesm2022/tinya-code-gas-admin-ui-group-h7-O8tt4.mjs.map +0 -1
- package/fesm2022/tinya-code-gas-admin-ui-image-7UhJ7nqI.mjs +0 -416
- package/fesm2022/tinya-code-gas-admin-ui-image-7UhJ7nqI.mjs.map +0 -1
- package/fesm2022/tinya-code-gas-admin-ui-kanban-view-Dl0Ol5Kb.mjs +0 -439
- package/fesm2022/tinya-code-gas-admin-ui-kanban-view-Dl0Ol5Kb.mjs.map +0 -1
- package/fesm2022/tinya-code-gas-admin-ui-key-value-nVdgg5at.mjs +0 -513
- package/fesm2022/tinya-code-gas-admin-ui-key-value-nVdgg5at.mjs.map +0 -1
- package/fesm2022/tinya-code-gas-admin-ui-list-C8ijJHAx.mjs +0 -637
- package/fesm2022/tinya-code-gas-admin-ui-list-C8ijJHAx.mjs.map +0 -1
- package/fesm2022/tinya-code-gas-admin-ui-metric-card-DnGMQL8l.mjs +0 -127
- package/fesm2022/tinya-code-gas-admin-ui-metric-card-DnGMQL8l.mjs.map +0 -1
- package/fesm2022/tinya-code-gas-admin-ui-multiselect-PB28e-jl.mjs +0 -251
- package/fesm2022/tinya-code-gas-admin-ui-multiselect-PB28e-jl.mjs.map +0 -1
- package/fesm2022/tinya-code-gas-admin-ui-notification.service-LjSULER6.mjs +0 -66
- package/fesm2022/tinya-code-gas-admin-ui-notification.service-LjSULER6.mjs.map +0 -1
- package/fesm2022/tinya-code-gas-admin-ui-number-JNr7glc8.mjs +0 -114
- package/fesm2022/tinya-code-gas-admin-ui-number-JNr7glc8.mjs.map +0 -1
- package/fesm2022/tinya-code-gas-admin-ui-phone-jR417hPf.mjs +0 -116
- package/fesm2022/tinya-code-gas-admin-ui-phone-jR417hPf.mjs.map +0 -1
- package/fesm2022/tinya-code-gas-admin-ui-quick-actions-DCQ6emmF.mjs +0 -235
- package/fesm2022/tinya-code-gas-admin-ui-quick-actions-DCQ6emmF.mjs.map +0 -1
- package/fesm2022/tinya-code-gas-admin-ui-radio-mode-DxvN07DY.mjs +0 -8
- package/fesm2022/tinya-code-gas-admin-ui-radio-mode-DxvN07DY.mjs.map +0 -1
- package/fesm2022/tinya-code-gas-admin-ui-readonly-text-BL7-4qie.mjs +0 -124
- package/fesm2022/tinya-code-gas-admin-ui-readonly-text-BL7-4qie.mjs.map +0 -1
- package/fesm2022/tinya-code-gas-admin-ui-record-list-BiZfnd_7.mjs +0 -249
- package/fesm2022/tinya-code-gas-admin-ui-record-list-BiZfnd_7.mjs.map +0 -1
- package/fesm2022/tinya-code-gas-admin-ui-relation-drawer.service-B0kT8EiW.mjs +0 -39
- package/fesm2022/tinya-code-gas-admin-ui-relation-drawer.service-B0kT8EiW.mjs.map +0 -1
- package/fesm2022/tinya-code-gas-admin-ui-relation-ykpJ3PYl.mjs +0 -309
- package/fesm2022/tinya-code-gas-admin-ui-relation-ykpJ3PYl.mjs.map +0 -1
- package/fesm2022/tinya-code-gas-admin-ui-render-registry-CULkxrSt.mjs +0 -576
- package/fesm2022/tinya-code-gas-admin-ui-render-registry-CULkxrSt.mjs.map +0 -1
- package/fesm2022/tinya-code-gas-admin-ui-rich-select-CIHaOrbU.mjs +0 -161
- package/fesm2022/tinya-code-gas-admin-ui-rich-select-CIHaOrbU.mjs.map +0 -1
- package/fesm2022/tinya-code-gas-admin-ui-rolldown-runtime-DpJrqANO.mjs +0 -11
- package/fesm2022/tinya-code-gas-admin-ui-schema-catalog-D0Pk0wAN.mjs +0 -685
- package/fesm2022/tinya-code-gas-admin-ui-schema-catalog-D0Pk0wAN.mjs.map +0 -1
- package/fesm2022/tinya-code-gas-admin-ui-seed-CcCA3FdQ.mjs +0 -80
- package/fesm2022/tinya-code-gas-admin-ui-seed-CcCA3FdQ.mjs.map +0 -1
- package/fesm2022/tinya-code-gas-admin-ui-select-CDwX9yNO.mjs +0 -296
- package/fesm2022/tinya-code-gas-admin-ui-select-CDwX9yNO.mjs.map +0 -1
- package/fesm2022/tinya-code-gas-admin-ui-skeleton-DnWK0tcZ.mjs +0 -119
- package/fesm2022/tinya-code-gas-admin-ui-skeleton-DnWK0tcZ.mjs.map +0 -1
- package/fesm2022/tinya-code-gas-admin-ui-slug-DU58cbiY.mjs +0 -158
- package/fesm2022/tinya-code-gas-admin-ui-slug-DU58cbiY.mjs.map +0 -1
- package/fesm2022/tinya-code-gas-admin-ui-source-options-B9R4mw8-.mjs +0 -26
- package/fesm2022/tinya-code-gas-admin-ui-source-options-B9R4mw8-.mjs.map +0 -1
- package/fesm2022/tinya-code-gas-admin-ui-status-progress-DmeG8htE.mjs +0 -205
- package/fesm2022/tinya-code-gas-admin-ui-status-progress-DmeG8htE.mjs.map +0 -1
- package/fesm2022/tinya-code-gas-admin-ui-string-list-gleasqzD.mjs +0 -432
- package/fesm2022/tinya-code-gas-admin-ui-string-list-gleasqzD.mjs.map +0 -1
- package/fesm2022/tinya-code-gas-admin-ui-template-interpolator-ewv7CkNX.mjs +0 -36
- package/fesm2022/tinya-code-gas-admin-ui-template-interpolator-ewv7CkNX.mjs.map +0 -1
- package/fesm2022/tinya-code-gas-admin-ui-text-CUPmL7W_.mjs +0 -107
- package/fesm2022/tinya-code-gas-admin-ui-text-CUPmL7W_.mjs.map +0 -1
- package/fesm2022/tinya-code-gas-admin-ui-textarea-CGLSIW13.mjs +0 -223
- package/fesm2022/tinya-code-gas-admin-ui-textarea-CGLSIW13.mjs.map +0 -1
- package/fesm2022/tinya-code-gas-admin-ui-time-BcHzsTWf.mjs +0 -107
- package/fesm2022/tinya-code-gas-admin-ui-time-BcHzsTWf.mjs.map +0 -1
- package/fesm2022/tinya-code-gas-admin-ui-upload.service-CZPEPCeN.mjs +0 -60
- package/fesm2022/tinya-code-gas-admin-ui-upload.service-CZPEPCeN.mjs.map +0 -1
- package/fesm2022/tinya-code-gas-admin-ui-url-GO4LZi__.mjs +0 -141
- package/fesm2022/tinya-code-gas-admin-ui-url-GO4LZi__.mjs.map +0 -1
- package/fesm2022/tinya-code-gas-admin-ui.mjs +0 -4599
- package/fesm2022/tinya-code-gas-admin-ui.mjs.map +0 -1
- package/types/tinya-code-gas-admin-ui.d.ts +0 -3632
|
@@ -0,0 +1,568 @@
|
|
|
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
|
+
import { LucideChevronLeft, LucideChevronRight } from '@lucide/angular';
|
|
12
|
+
|
|
13
|
+
import type {
|
|
14
|
+
CalendarViewConfig,
|
|
15
|
+
ResourceSchema,
|
|
16
|
+
UiViewCalendar,
|
|
17
|
+
SelectionBinding,
|
|
18
|
+
} from '../../core/models/schema.model';
|
|
19
|
+
import { ErrorMapperService } from '../../core/services/error-mapper.service';
|
|
20
|
+
import { DataSourceService, type SourceRow } from '../../core/services/data-source.service';
|
|
21
|
+
import type { SelectionEmitter } from '../composer/view-context';
|
|
22
|
+
import { getSchema } from '../../core/registry/schema-catalog';
|
|
23
|
+
import { Button } from '../../shared/components/button/button';
|
|
24
|
+
import { EmptyState } from '../../shared/components/empty-state/empty-state';
|
|
25
|
+
import { Skeleton } from '../../shared/components/skeleton/skeleton';
|
|
26
|
+
import {
|
|
27
|
+
agendaDayLabel,
|
|
28
|
+
type CalendarMapping,
|
|
29
|
+
type CalendarMode,
|
|
30
|
+
type CalEvent,
|
|
31
|
+
type CivilRange,
|
|
32
|
+
daysInRange,
|
|
33
|
+
eventsByDay,
|
|
34
|
+
findFieldByKey,
|
|
35
|
+
mapCalendarRows,
|
|
36
|
+
monthGridDays,
|
|
37
|
+
rangeLabelFor,
|
|
38
|
+
REASON_END_BEFORE_START,
|
|
39
|
+
REASON_UNDATED,
|
|
40
|
+
sameMonth,
|
|
41
|
+
shiftCursor,
|
|
42
|
+
CALENDAR_TODAY,
|
|
43
|
+
visibleRangeFor,
|
|
44
|
+
weekDays,
|
|
45
|
+
WEEKDAY_HEADINGS,
|
|
46
|
+
weekdayShort,
|
|
47
|
+
} from './calendar-model';
|
|
48
|
+
|
|
49
|
+
/**
|
|
50
|
+
* Narrowing helper: `UiViewBase` keeps `type: string` open, so comparing
|
|
51
|
+
* `view.type === 'calendar'` alone does not remove the base member from the
|
|
52
|
+
* union. Requiring `config` in the value is what actually proves the view is
|
|
53
|
+
* a `UiViewCalendar` at runtime.
|
|
54
|
+
*/
|
|
55
|
+
function calendarConfigOf(view: unknown): UiViewCalendar['config'] | undefined {
|
|
56
|
+
if (!view || typeof view !== 'object') return undefined;
|
|
57
|
+
const candidate = view as { type?: unknown; config?: unknown };
|
|
58
|
+
return candidate.type === 'calendar' && candidate.config !== undefined
|
|
59
|
+
? (candidate.config as UiViewCalendar['config'])
|
|
60
|
+
: undefined;
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
type Status = 'loading' | 'ready' | 'error';
|
|
64
|
+
|
|
65
|
+
/** Mode switcher, in display order (Spanish UI copy). */
|
|
66
|
+
const MODE_OPTIONS: readonly { mode: CalendarMode; label: string }[] = [
|
|
67
|
+
{ mode: 'month', label: 'Mes' },
|
|
68
|
+
{ mode: 'week', label: 'Semana' },
|
|
69
|
+
{ mode: 'day', label: 'Día' },
|
|
70
|
+
{ mode: 'agenda', label: 'Agenda' },
|
|
71
|
+
];
|
|
72
|
+
|
|
73
|
+
/** Stable empty bucket — avoids allocating a new array per day per cycle. */
|
|
74
|
+
const NO_EVENTS: readonly CalEvent[] = [];
|
|
75
|
+
|
|
76
|
+
/**
|
|
77
|
+
* Generic calendar view (R9.8.3): month / week / day / agenda over the rows
|
|
78
|
+
* of the declared `CalendarViewConfig.source` (or the resource's own
|
|
79
|
+
* listing), positioned client-side onto civil days.
|
|
80
|
+
*
|
|
81
|
+
* Data flow: ONE fetcher (`DataSourceService.rows`) per visible range. When
|
|
82
|
+
* the schema declares `source.view` the visible `desde`/`hasta` travel as
|
|
83
|
+
* `ctx` so the backend view can filter the range; without a view nothing
|
|
84
|
+
* filters, the full listing is cached (30 s TTL) and reused while the user
|
|
85
|
+
* navigates — both paths share this component, only the `ctx` differs.
|
|
86
|
+
*
|
|
87
|
+
* Read-only by design: no drag, no click-to-edit. Color never carries
|
|
88
|
+
* meaning alone — every event pairs its color dot with the title text — and
|
|
89
|
+
* the agenda mode is the no-grid fallback for small screens.
|
|
90
|
+
*/
|
|
91
|
+
@Component({
|
|
92
|
+
selector: 'app-calendar-view',
|
|
93
|
+
changeDetection: ChangeDetectionStrategy.OnPush,
|
|
94
|
+
imports: [Button, EmptyState, LucideChevronLeft, LucideChevronRight, Skeleton],
|
|
95
|
+
template: `
|
|
96
|
+
@if (config(); as cfg) {
|
|
97
|
+
<section class="space-y-4" aria-label="Calendario" [attr.aria-busy]="status() === 'loading'">
|
|
98
|
+
<header class="flex flex-wrap items-center justify-between gap-3">
|
|
99
|
+
<h2 class="font-display text-xl font-semibold">{{ rangeLabel() }}</h2>
|
|
100
|
+
<div class="flex items-center gap-1">
|
|
101
|
+
<button
|
|
102
|
+
app-button
|
|
103
|
+
variant="outline"
|
|
104
|
+
size="icon"
|
|
105
|
+
type="button"
|
|
106
|
+
aria-label="Período anterior"
|
|
107
|
+
title="Período anterior"
|
|
108
|
+
(click)="previous()"
|
|
109
|
+
>
|
|
110
|
+
<svg lucideChevronLeft size="16" aria-hidden="true" />
|
|
111
|
+
</button>
|
|
112
|
+
<button app-button variant="outline" size="sm" type="button" (click)="today()">
|
|
113
|
+
Hoy
|
|
114
|
+
</button>
|
|
115
|
+
<button
|
|
116
|
+
app-button
|
|
117
|
+
variant="outline"
|
|
118
|
+
size="icon"
|
|
119
|
+
type="button"
|
|
120
|
+
aria-label="Período siguiente"
|
|
121
|
+
title="Período siguiente"
|
|
122
|
+
(click)="next()"
|
|
123
|
+
>
|
|
124
|
+
<svg lucideChevronRight size="16" aria-hidden="true" />
|
|
125
|
+
</button>
|
|
126
|
+
</div>
|
|
127
|
+
<div class="flex items-center gap-1" role="group" aria-label="Vista del calendario">
|
|
128
|
+
@for (option of modeOptions; track option.mode) {
|
|
129
|
+
<button
|
|
130
|
+
app-button
|
|
131
|
+
variant="ghost"
|
|
132
|
+
size="sm"
|
|
133
|
+
type="button"
|
|
134
|
+
[attr.aria-pressed]="mode() === option.mode"
|
|
135
|
+
(click)="setMode(option.mode)"
|
|
136
|
+
>
|
|
137
|
+
{{ option.label }}
|
|
138
|
+
</button>
|
|
139
|
+
}
|
|
140
|
+
</div>
|
|
141
|
+
</header>
|
|
142
|
+
|
|
143
|
+
@switch (status()) {
|
|
144
|
+
@case ('loading') {
|
|
145
|
+
<app-skeleton [lines]="8" [height]="32" />
|
|
146
|
+
}
|
|
147
|
+
@case ('error') {
|
|
148
|
+
<app-empty-state title="No se pudo cargar el calendario" [message]="errorMessage()">
|
|
149
|
+
<button app-button variant="outline" type="button" (click)="retry()">
|
|
150
|
+
Reintentar
|
|
151
|
+
</button>
|
|
152
|
+
</app-empty-state>
|
|
153
|
+
}
|
|
154
|
+
@case ('ready') {
|
|
155
|
+
@if (mapping().events.length === 0) {
|
|
156
|
+
<app-empty-state
|
|
157
|
+
title="Sin eventos"
|
|
158
|
+
message="No hay eventos con fecha válida en este período."
|
|
159
|
+
/>
|
|
160
|
+
} @else {
|
|
161
|
+
@switch (mode()) {
|
|
162
|
+
@case ('month') {
|
|
163
|
+
<div class="overflow-x-auto rounded-xl border border-neutral/20 bg-surface">
|
|
164
|
+
<div class="grid min-w-[640px] grid-cols-7">
|
|
165
|
+
@for (heading of weekdayHeadings; track heading) {
|
|
166
|
+
<div
|
|
167
|
+
class="border-b border-neutral/15 px-2 py-1.5 text-xs font-medium uppercase text-neutral"
|
|
168
|
+
>
|
|
169
|
+
{{ heading }}
|
|
170
|
+
</div>
|
|
171
|
+
}
|
|
172
|
+
@for (day of monthGrid(); track day) {
|
|
173
|
+
<div
|
|
174
|
+
class="min-h-24 border-b border-r border-neutral/10 p-1.5"
|
|
175
|
+
[attr.data-day]="day"
|
|
176
|
+
[class.opacity-40]="!isInCursorMonth(day)"
|
|
177
|
+
>
|
|
178
|
+
<span class="text-xs font-medium">{{ dayNumber(day) }}</span>
|
|
179
|
+
<ul class="mt-1 space-y-0.5">
|
|
180
|
+
@for (event of eventsFor(day); track event.key) {
|
|
181
|
+
<li
|
|
182
|
+
class="flex items-center gap-1 text-xs"
|
|
183
|
+
[class.cursor-pointer]="seleccionable()"
|
|
184
|
+
[class.ring-2]="esSeleccionada(event)"
|
|
185
|
+
[attr.data-event-key]="event.key"
|
|
186
|
+
[attr.data-selectable]="seleccionable() ? 'true' : null"
|
|
187
|
+
(click)="seleccionar(event)"
|
|
188
|
+
>
|
|
189
|
+
<span
|
|
190
|
+
class="size-2 shrink-0 rounded-full bg-neutral/30"
|
|
191
|
+
[style.background-color]="event.color ?? null"
|
|
192
|
+
aria-hidden="true"
|
|
193
|
+
></span>
|
|
194
|
+
<span class="truncate">{{ event.title }}</span>
|
|
195
|
+
</li>
|
|
196
|
+
}
|
|
197
|
+
</ul>
|
|
198
|
+
</div>
|
|
199
|
+
}
|
|
200
|
+
</div>
|
|
201
|
+
</div>
|
|
202
|
+
}
|
|
203
|
+
@case ('week') {
|
|
204
|
+
<div class="overflow-x-auto rounded-xl border border-neutral/20 bg-surface">
|
|
205
|
+
<div class="grid min-w-[560px] grid-cols-7">
|
|
206
|
+
@for (day of weekColumnDays(); track day) {
|
|
207
|
+
<div class="min-h-32 border-r border-neutral/10 p-2" [attr.data-day]="day">
|
|
208
|
+
<span class="text-xs font-medium uppercase text-neutral">
|
|
209
|
+
{{ weekdayShort(day) }} {{ dayNumber(day) }}
|
|
210
|
+
</span>
|
|
211
|
+
<ul class="mt-2 space-y-1">
|
|
212
|
+
@for (event of eventsFor(day); track event.key) {
|
|
213
|
+
<li
|
|
214
|
+
class="flex items-center gap-1.5 text-sm"
|
|
215
|
+
[class.cursor-pointer]="seleccionable()"
|
|
216
|
+
[class.ring-2]="esSeleccionada(event)"
|
|
217
|
+
[attr.data-event-key]="event.key"
|
|
218
|
+
[attr.data-selectable]="seleccionable() ? 'true' : null"
|
|
219
|
+
(click)="seleccionar(event)"
|
|
220
|
+
>
|
|
221
|
+
<span
|
|
222
|
+
class="size-2 shrink-0 rounded-full bg-neutral/30"
|
|
223
|
+
[style.background-color]="event.color ?? null"
|
|
224
|
+
aria-hidden="true"
|
|
225
|
+
></span>
|
|
226
|
+
<span class="truncate">{{ event.title }}</span>
|
|
227
|
+
</li>
|
|
228
|
+
}
|
|
229
|
+
</ul>
|
|
230
|
+
</div>
|
|
231
|
+
}
|
|
232
|
+
</div>
|
|
233
|
+
</div>
|
|
234
|
+
}
|
|
235
|
+
@case ('day') {
|
|
236
|
+
<div
|
|
237
|
+
class="rounded-xl border border-neutral/20 bg-surface p-4"
|
|
238
|
+
[attr.data-day]="cursor()"
|
|
239
|
+
>
|
|
240
|
+
<h3 class="font-display text-base font-semibold">
|
|
241
|
+
{{ dayHeading(cursor()) }}
|
|
242
|
+
</h3>
|
|
243
|
+
@if (eventsFor(cursor()).length === 0) {
|
|
244
|
+
<p class="mt-2 text-sm text-neutral">Sin eventos este día.</p>
|
|
245
|
+
} @else {
|
|
246
|
+
<ul class="mt-3 space-y-2">
|
|
247
|
+
@for (event of eventsFor(cursor()); track event.key) {
|
|
248
|
+
<li
|
|
249
|
+
class="flex items-center gap-1 text-xs"
|
|
250
|
+
[class.cursor-pointer]="seleccionable()"
|
|
251
|
+
[class.ring-2]="esSeleccionada(event)"
|
|
252
|
+
[attr.data-event-key]="event.key"
|
|
253
|
+
[attr.data-selectable]="seleccionable() ? 'true' : null"
|
|
254
|
+
(click)="seleccionar(event)"
|
|
255
|
+
>
|
|
256
|
+
<span
|
|
257
|
+
class="size-2.5 shrink-0 rounded-full bg-neutral/30"
|
|
258
|
+
[style.background-color]="event.color ?? null"
|
|
259
|
+
aria-hidden="true"
|
|
260
|
+
></span>
|
|
261
|
+
<span>{{ event.title }}</span>
|
|
262
|
+
</li>
|
|
263
|
+
}
|
|
264
|
+
</ul>
|
|
265
|
+
}
|
|
266
|
+
</div>
|
|
267
|
+
}
|
|
268
|
+
@case ('agenda') {
|
|
269
|
+
<ol class="space-y-4">
|
|
270
|
+
@for (day of agendaDays(); track day) {
|
|
271
|
+
<li class="rounded-xl border border-neutral/20 bg-surface p-4">
|
|
272
|
+
<h3 class="text-sm font-semibold">{{ dayHeading(day) }}</h3>
|
|
273
|
+
<ul class="mt-2 space-y-1">
|
|
274
|
+
@for (event of eventsFor(day); track event.key) {
|
|
275
|
+
<li
|
|
276
|
+
class="flex items-center gap-2 text-sm"
|
|
277
|
+
[class.cursor-pointer]="seleccionable()"
|
|
278
|
+
[class.ring-2]="esSeleccionada(event)"
|
|
279
|
+
[attr.data-event-key]="event.key"
|
|
280
|
+
[attr.data-selectable]="seleccionable() ? 'true' : null"
|
|
281
|
+
(click)="seleccionar(event)"
|
|
282
|
+
>
|
|
283
|
+
<span
|
|
284
|
+
class="size-2 shrink-0 rounded-full bg-neutral/30"
|
|
285
|
+
[style.background-color]="event.color ?? null"
|
|
286
|
+
aria-hidden="true"
|
|
287
|
+
></span>
|
|
288
|
+
<span>{{ event.title }}</span>
|
|
289
|
+
</li>
|
|
290
|
+
}
|
|
291
|
+
</ul>
|
|
292
|
+
</li>
|
|
293
|
+
}
|
|
294
|
+
</ol>
|
|
295
|
+
}
|
|
296
|
+
}
|
|
297
|
+
}
|
|
298
|
+
|
|
299
|
+
<!-- Problem records are VISIBLE, never silently dropped: a row
|
|
300
|
+
without a valid start or with an impossible interval cannot
|
|
301
|
+
be painted, but the operator must see why. -->
|
|
302
|
+
@if (hasProblemRecords()) {
|
|
303
|
+
<section
|
|
304
|
+
class="rounded-xl border border-neutral/30 p-4"
|
|
305
|
+
aria-label="Registros sin fecha válida"
|
|
306
|
+
>
|
|
307
|
+
<h3 class="font-display text-sm font-semibold">Registros sin fecha válida</h3>
|
|
308
|
+
<p class="mt-1 text-help text-neutral">
|
|
309
|
+
Estos registros no se pueden pintar en el calendario:
|
|
310
|
+
</p>
|
|
311
|
+
<ul class="mt-2 space-y-1 text-sm">
|
|
312
|
+
@for (row of mapping().undated; track $index) {
|
|
313
|
+
<li>{{ recordLabel(row) }} — {{ reasonUndated }}</li>
|
|
314
|
+
}
|
|
315
|
+
@for (item of mapping().invalid; track $index) {
|
|
316
|
+
<li>{{ recordLabel(item.row) }} — {{ item.reason }}</li>
|
|
317
|
+
}
|
|
318
|
+
</ul>
|
|
319
|
+
</section>
|
|
320
|
+
}
|
|
321
|
+
}
|
|
322
|
+
}
|
|
323
|
+
</section>
|
|
324
|
+
} @else {
|
|
325
|
+
<app-empty-state
|
|
326
|
+
title="Configuración inválida"
|
|
327
|
+
message="Esta vista no declara una configuración de calendario válida."
|
|
328
|
+
/>
|
|
329
|
+
}
|
|
330
|
+
`,
|
|
331
|
+
})
|
|
332
|
+
export class CalendarView {
|
|
333
|
+
private readonly route = inject(ActivatedRoute);
|
|
334
|
+
// The shell mounts views via `loadComponent` (no input bindings), so the
|
|
335
|
+
// schema is resolved from route data exactly like FormView does.
|
|
336
|
+
private readonly routeData = toSignal(this.route.data, { requireSync: true });
|
|
337
|
+
private readonly dataSource = inject(DataSourceService);
|
|
338
|
+
private readonly errorMapper = inject(ErrorMapperService);
|
|
339
|
+
|
|
340
|
+
/**
|
|
341
|
+
* Resource schema for this view. The shell mounts views via
|
|
342
|
+
* `loadComponent` (no input bindings), so the schema is resolved from
|
|
343
|
+
* route data: either an explicit `schema` object, or a `resourceId` looked
|
|
344
|
+
* up in the registry (same route shape FormView uses).
|
|
345
|
+
*/
|
|
346
|
+
readonly schema = computed<ResourceSchema | undefined>(() => {
|
|
347
|
+
const data = this.routeData();
|
|
348
|
+
const explicit = data['schema'];
|
|
349
|
+
if (explicit !== undefined) return explicit as ResourceSchema;
|
|
350
|
+
const resourceId = data['resourceId'];
|
|
351
|
+
return typeof resourceId === 'string' ? getSchema(resourceId) : undefined;
|
|
352
|
+
});
|
|
353
|
+
|
|
354
|
+
/**
|
|
355
|
+
* Config resolved from `schema.uiViews[<route uiView>]`; `undefined` when
|
|
356
|
+
* the route does not name a calendar view with a usable config — the
|
|
357
|
+
* template then renders the invalid-config empty state instead of a grid.
|
|
358
|
+
*/
|
|
359
|
+
readonly config = computed<CalendarViewConfig | undefined>(() => {
|
|
360
|
+
const schema = this.schema();
|
|
361
|
+
const viewName = this.routeData()['uiView'];
|
|
362
|
+
if (schema === undefined || typeof viewName !== 'string') return undefined;
|
|
363
|
+
const cfg = calendarConfigOf(schema.uiViews?.[viewName]);
|
|
364
|
+
if (cfg === undefined) return undefined;
|
|
365
|
+
const usable =
|
|
366
|
+
typeof cfg.startField === 'string' &&
|
|
367
|
+
cfg.startField !== '' &&
|
|
368
|
+
typeof cfg.titleField === 'string' &&
|
|
369
|
+
cfg.titleField !== '';
|
|
370
|
+
return usable ? cfg : undefined;
|
|
371
|
+
});
|
|
372
|
+
|
|
373
|
+
/** Explicit user choice; `undefined` ⇒ the config's `defaultView`. */
|
|
374
|
+
private readonly explicitMode = signal<CalendarMode | undefined>(undefined);
|
|
375
|
+
|
|
376
|
+
readonly mode = computed<CalendarMode>(
|
|
377
|
+
() => this.explicitMode() ?? this.config()?.defaultView ?? 'month',
|
|
378
|
+
);
|
|
379
|
+
|
|
380
|
+
/** Anchor civil day of the visible range; init = today (civil). */
|
|
381
|
+
readonly cursor = signal<string>(inject(CALENDAR_TODAY)());
|
|
382
|
+
|
|
383
|
+
/** "Hoy" inyectable (default: fecha real del sistema). */
|
|
384
|
+
private readonly todayFn = inject(CALENDAR_TODAY);
|
|
385
|
+
|
|
386
|
+
/** Visible `{desde, hasta}` derived from cursor + mode. */
|
|
387
|
+
readonly visibleRange = computed<CivilRange | null>(() =>
|
|
388
|
+
this.config() === undefined ? null : visibleRangeFor(this.mode(), this.cursor()),
|
|
389
|
+
);
|
|
390
|
+
|
|
391
|
+
readonly rangeLabel = computed(() =>
|
|
392
|
+
this.config() === undefined ? '' : rangeLabelFor(this.mode(), this.cursor()),
|
|
393
|
+
);
|
|
394
|
+
|
|
395
|
+
readonly status = signal<Status>('loading');
|
|
396
|
+
readonly rows = signal<SourceRow[]>([]);
|
|
397
|
+
/** Clave seleccionada localmente cuando el binding declara `mode:'inline'`. */
|
|
398
|
+
readonly seleccionada = signal<string | null>(null);
|
|
399
|
+
readonly errorMessage = signal('');
|
|
400
|
+
|
|
401
|
+
readonly modeOptions = MODE_OPTIONS;
|
|
402
|
+
readonly weekdayHeadings = WEEKDAY_HEADINGS;
|
|
403
|
+
readonly reasonUndated = REASON_UNDATED;
|
|
404
|
+
|
|
405
|
+
/** Guards against a slow response overwriting a newer range's rows. */
|
|
406
|
+
private loadEpoch = 0;
|
|
407
|
+
|
|
408
|
+
constructor() {
|
|
409
|
+
effect(() => {
|
|
410
|
+
const cfg = this.config();
|
|
411
|
+
const range = this.visibleRange();
|
|
412
|
+
if (cfg === undefined || range === null) return;
|
|
413
|
+
void this.load(cfg, range);
|
|
414
|
+
});
|
|
415
|
+
}
|
|
416
|
+
|
|
417
|
+
/** Rows mapped to events + visible problem records (pure model). */
|
|
418
|
+
readonly mapping = computed<CalendarMapping>(() => {
|
|
419
|
+
const cfg = this.config();
|
|
420
|
+
const schema = this.schema();
|
|
421
|
+
if (cfg === undefined || schema === undefined) return { events: [], undated: [], invalid: [] };
|
|
422
|
+
const endFieldType =
|
|
423
|
+
cfg.endField !== undefined ? findFieldByKey(schema.fields, cfg.endField)?.type : undefined;
|
|
424
|
+
return mapCalendarRows(this.rows(), cfg, {
|
|
425
|
+
keyField: schema.keyField,
|
|
426
|
+
endFieldType,
|
|
427
|
+
});
|
|
428
|
+
});
|
|
429
|
+
|
|
430
|
+
/** Events bucketed per civil day, clamped to the visible range. */
|
|
431
|
+
readonly eventsByDay = computed(() => {
|
|
432
|
+
const range = this.visibleRange();
|
|
433
|
+
if (range === null) return new Map<string, CalEvent[]>();
|
|
434
|
+
return eventsByDay(this.mapping().events, range);
|
|
435
|
+
});
|
|
436
|
+
|
|
437
|
+
readonly monthGrid = computed(() => monthGridDays(this.cursor()));
|
|
438
|
+
readonly weekColumnDays = computed(() => weekDays(this.cursor()));
|
|
439
|
+
|
|
440
|
+
/** Agenda headings: the days of the range that actually have events. */
|
|
441
|
+
readonly agendaDays = computed(() => {
|
|
442
|
+
const range = this.visibleRange();
|
|
443
|
+
if (range === null) return [] as string[];
|
|
444
|
+
return daysInRange(range).filter((day) => this.eventsByDay().has(day));
|
|
445
|
+
});
|
|
446
|
+
|
|
447
|
+
readonly hasProblemRecords = computed(
|
|
448
|
+
() => this.mapping().undated.length > 0 || this.mapping().invalid.length > 0,
|
|
449
|
+
);
|
|
450
|
+
|
|
451
|
+
previous(): void {
|
|
452
|
+
this.cursor.set(shiftCursor(this.mode(), this.cursor(), -1));
|
|
453
|
+
}
|
|
454
|
+
|
|
455
|
+
next(): void {
|
|
456
|
+
this.cursor.set(shiftCursor(this.mode(), this.cursor(), 1));
|
|
457
|
+
}
|
|
458
|
+
|
|
459
|
+
/** Reset the cursor to today (civil). */
|
|
460
|
+
today(): void {
|
|
461
|
+
this.cursor.set(this.todayFn());
|
|
462
|
+
}
|
|
463
|
+
|
|
464
|
+
setMode(mode: CalendarMode): void {
|
|
465
|
+
this.explicitMode.set(mode);
|
|
466
|
+
}
|
|
467
|
+
|
|
468
|
+
retry(): void {
|
|
469
|
+
const cfg = this.config();
|
|
470
|
+
const range = this.visibleRange();
|
|
471
|
+
if (cfg !== undefined && range !== null) void this.load(cfg, range);
|
|
472
|
+
}
|
|
473
|
+
|
|
474
|
+
/** Binding `select` declarado por la vista (R9.9.4 · decision table). */
|
|
475
|
+
private binding(): SelectionBinding | undefined {
|
|
476
|
+
return this.routeData()['select'] as SelectionBinding | undefined;
|
|
477
|
+
}
|
|
478
|
+
|
|
479
|
+
/**
|
|
480
|
+
* Clic de evento. Sin declaración ⇒ inerte (el calendario no navegaba);
|
|
481
|
+
* con declaración ⇒ sólo el contrato declarado corre: `route` emite al
|
|
482
|
+
* dispatcher, `inline` selecciona en local, `none` es no-op explícito.
|
|
483
|
+
*/
|
|
484
|
+
seleccionar(event: CalEvent): void {
|
|
485
|
+
const binding = this.binding();
|
|
486
|
+
if (binding === undefined) return;
|
|
487
|
+
if (binding.mode === 'inline') {
|
|
488
|
+
this.seleccionada.set(event.key);
|
|
489
|
+
return;
|
|
490
|
+
}
|
|
491
|
+
if (binding.mode === 'none') return;
|
|
492
|
+
const schema = this.schema();
|
|
493
|
+
const emit = this.routeData()['emit'] as SelectionEmitter | undefined;
|
|
494
|
+
emit?.({
|
|
495
|
+
resource: schema?.id ?? String(this.routeData()['resourceId'] ?? ''),
|
|
496
|
+
key: event.key,
|
|
497
|
+
action: binding.on,
|
|
498
|
+
});
|
|
499
|
+
}
|
|
500
|
+
|
|
501
|
+
/** El DOM declara clicable sólo si hay contrato que responder. */
|
|
502
|
+
seleccionable(): boolean {
|
|
503
|
+
const binding = this.binding();
|
|
504
|
+
return binding !== undefined && binding.mode !== 'none';
|
|
505
|
+
}
|
|
506
|
+
|
|
507
|
+
esSeleccionada(event: CalEvent): boolean {
|
|
508
|
+
return this.seleccionable() && this.seleccionada() === event.key;
|
|
509
|
+
}
|
|
510
|
+
|
|
511
|
+
eventsFor(day: string): readonly CalEvent[] {
|
|
512
|
+
return this.eventsByDay().get(day) ?? NO_EVENTS;
|
|
513
|
+
}
|
|
514
|
+
|
|
515
|
+
dayNumber(day: string): number {
|
|
516
|
+
return Number(day.slice(8, 10));
|
|
517
|
+
}
|
|
518
|
+
|
|
519
|
+
isInCursorMonth(day: string): boolean {
|
|
520
|
+
return sameMonth(day, this.cursor());
|
|
521
|
+
}
|
|
522
|
+
|
|
523
|
+
/** Weekday + number ("lun 12") for week columns. */
|
|
524
|
+
weekdayShort(day: string): string {
|
|
525
|
+
return weekdayShort(day);
|
|
526
|
+
}
|
|
527
|
+
|
|
528
|
+
/** Full heading ("lunes, 12 de octubre de 2026") for day/agenda. */
|
|
529
|
+
dayHeading(day: string): string {
|
|
530
|
+
return agendaDayLabel(day);
|
|
531
|
+
}
|
|
532
|
+
|
|
533
|
+
recordLabel(row: SourceRow): string {
|
|
534
|
+
const schema = this.schema();
|
|
535
|
+
if (schema === undefined) return '';
|
|
536
|
+
const title = String(row[schema.titleField] ?? '').trim();
|
|
537
|
+
return title !== '' ? title : String(row[schema.keyField] ?? '');
|
|
538
|
+
}
|
|
539
|
+
|
|
540
|
+
private async load(cfg: CalendarViewConfig, range: CivilRange): Promise<void> {
|
|
541
|
+
const schema = this.schema();
|
|
542
|
+
if (schema === undefined) return;
|
|
543
|
+
const epoch = ++this.loadEpoch;
|
|
544
|
+
this.status.set('loading');
|
|
545
|
+
// Optimization branch (decided): range ctx ONLY when the schema declares
|
|
546
|
+
// a backend view — that's the signal that a `where` may filter on
|
|
547
|
+
// `param: 'desde'`/`'hasta'`. Without a view the params would be ignored
|
|
548
|
+
// (and the full listing is cached while the user navigates).
|
|
549
|
+
const ctx =
|
|
550
|
+
cfg.source?.view !== undefined && cfg.source.view !== ''
|
|
551
|
+
? { desde: range.desde, hasta: range.hasta }
|
|
552
|
+
: undefined;
|
|
553
|
+
const source = cfg.source ?? { resource: schema.id };
|
|
554
|
+
try {
|
|
555
|
+
const rows = await this.dataSource.rows(source, ctx);
|
|
556
|
+
if (epoch !== this.loadEpoch) return;
|
|
557
|
+
this.rows.set(rows);
|
|
558
|
+
this.errorMessage.set('');
|
|
559
|
+
this.status.set('ready');
|
|
560
|
+
} catch (error) {
|
|
561
|
+
if (epoch !== this.loadEpoch) return;
|
|
562
|
+
this.errorMessage.set(
|
|
563
|
+
this.errorMapper.map(error).general ?? 'No se pudo cargar el calendario.',
|
|
564
|
+
);
|
|
565
|
+
this.status.set('error');
|
|
566
|
+
}
|
|
567
|
+
}
|
|
568
|
+
}
|