@antelopejs/interface-dms 0.10.4 → 0.12.0
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/CHANGELOG.md +19 -0
- package/README.md +21 -10
- package/dist/attachments.d.ts +15 -2
- package/dist/attachments.js +14 -1
- package/dist/attachments.js.map +1 -1
- package/dist/auth/db/models/sessions.model.d.ts +7 -5
- package/dist/auth/db/models/sessions.model.js +2 -9
- package/dist/auth/db/models/sessions.model.js.map +1 -1
- package/dist/auth/db/models/userExternalIdentities.model.d.ts +7 -5
- package/dist/auth/db/models/users.model.d.ts +7 -5
- package/dist/auth/db/tables/users.table.d.ts +2 -2
- package/dist/auth/db/tables/users.table.js.map +1 -1
- package/dist/auth/index.d.ts +4 -4
- package/dist/auth/internal/jwt-auth-decorator.d.ts +1 -1
- package/dist/auth/internal/jwt-auth-decorator.js +6 -3
- package/dist/auth/internal/jwt-auth-decorator.js.map +1 -1
- package/dist/auth/internal/request-authenticators.d.ts +12 -0
- package/dist/auth/internal/request-authenticators.js +53 -10
- package/dist/auth/internal/request-authenticators.js.map +1 -1
- package/dist/base/activity-feed.d.ts +34 -32
- package/dist/base/activity-feed.js +2 -0
- package/dist/base/activity-feed.js.map +1 -1
- package/dist/base/banner.d.ts +12 -10
- package/dist/base/banner.js +2 -0
- package/dist/base/banner.js.map +1 -1
- package/dist/base/block-registry/internal/describe.js +3 -9
- package/dist/base/block-registry/internal/describe.js.map +1 -1
- package/dist/base/block-registry/internal/ui.js +1 -1
- package/dist/base/block-registry/internal/ui.js.map +1 -1
- package/dist/base/card.d.ts +11 -9
- package/dist/base/card.js +2 -0
- package/dist/base/card.js.map +1 -1
- package/dist/base/chart-card.d.ts +4 -2
- package/dist/base/chart-card.js +2 -0
- package/dist/base/chart-card.js.map +1 -1
- package/dist/base/chart-schemas.d.ts +148 -148
- package/dist/base/chart.d.ts +65 -0
- package/dist/base/chart.js +65 -0
- package/dist/base/chart.js.map +1 -1
- package/dist/base/code-block.d.ts +5 -3
- package/dist/base/code-block.js +2 -0
- package/dist/base/code-block.js.map +1 -1
- package/dist/base/custom.d.ts +5 -0
- package/dist/base/custom.js +5 -0
- package/dist/base/custom.js.map +1 -1
- package/dist/base/data-types/default/address.d.ts +5 -0
- package/dist/base/data-types/default/address.js +5 -0
- package/dist/base/data-types/default/address.js.map +1 -1
- package/dist/base/data-types/default/choices.d.ts +20 -0
- package/dist/base/data-types/default/choices.js +20 -0
- package/dist/base/data-types/default/choices.js.map +1 -1
- package/dist/base/data-types/default/files.d.ts +22 -5
- package/dist/base/data-types/default/files.js +20 -0
- package/dist/base/data-types/default/files.js.map +1 -1
- package/dist/base/data-types/default/numbers.d.ts +15 -0
- package/dist/base/data-types/default/numbers.js +25 -9
- package/dist/base/data-types/default/numbers.js.map +1 -1
- package/dist/base/data-types/default/relations.d.ts +10 -2
- package/dist/base/data-types/default/relations.js +9 -6
- package/dist/base/data-types/default/relations.js.map +1 -1
- package/dist/base/data-types/default/strings.d.ts +25 -0
- package/dist/base/data-types/default/strings.js +25 -0
- package/dist/base/data-types/default/strings.js.map +1 -1
- package/dist/base/data-types/default-types.d.ts +1 -1
- package/dist/base/data-types/field-types.d.ts +10 -0
- package/dist/base/data-types/field-types.js +10 -0
- package/dist/base/data-types/field-types.js.map +1 -1
- package/dist/base/data-types/internal/relation-endpoints.js +1 -0
- package/dist/base/data-types/internal/relation-endpoints.js.map +1 -1
- package/dist/base/data-types/status-type.d.ts +5 -0
- package/dist/base/data-types/status-type.js +5 -0
- package/dist/base/data-types/status-type.js.map +1 -1
- package/dist/base/display.d.ts +6 -6
- package/dist/base/empty-state.d.ts +15 -13
- package/dist/base/empty-state.js +2 -0
- package/dist/base/empty-state.js.map +1 -1
- package/dist/base/form-block-schema.d.ts +50 -50
- package/dist/base/form-schema.d.ts +2 -0
- package/dist/base/form-schema.js +2 -0
- package/dist/base/form-schema.js.map +1 -1
- package/dist/base/grid.d.ts +4 -0
- package/dist/base/grid.js +4 -0
- package/dist/base/grid.js.map +1 -1
- package/dist/base/guard.d.ts +30 -28
- package/dist/base/guard.js +2 -0
- package/dist/base/guard.js.map +1 -1
- package/dist/base/helpers/archive-mode-helpers.d.ts +1 -1
- package/dist/base/helpers/file-refs.js +15 -15
- package/dist/base/helpers/file-refs.js.map +1 -1
- package/dist/base/internal/form-schema-validation.js +7 -1
- package/dist/base/internal/form-schema-validation.js.map +1 -1
- package/dist/base/internal/realtime-topics.js.map +1 -1
- package/dist/base/internal/searchable.d.ts +2 -1
- package/dist/base/internal/searchable.js +1 -3
- package/dist/base/internal/searchable.js.map +1 -1
- package/dist/base/key-value-list.d.ts +21 -19
- package/dist/base/key-value-list.js +2 -0
- package/dist/base/key-value-list.js.map +1 -1
- package/dist/base/kpi-card.d.ts +4 -2
- package/dist/base/kpi-card.js +2 -0
- package/dist/base/kpi-card.js.map +1 -1
- package/dist/base/live-toggle.d.ts +2 -0
- package/dist/base/live-toggle.js +2 -0
- package/dist/base/live-toggle.js.map +1 -1
- package/dist/base/meter.d.ts +23 -21
- package/dist/base/meter.js +2 -0
- package/dist/base/meter.js.map +1 -1
- package/dist/base/nav-card-grid.d.ts +24 -19
- package/dist/base/nav-card-grid.js +5 -0
- package/dist/base/nav-card-grid.js.map +1 -1
- package/dist/base/page-toolbar.d.ts +2 -0
- package/dist/base/page-toolbar.js +2 -0
- package/dist/base/page-toolbar.js.map +1 -1
- package/dist/base/period-selector.d.ts +3 -1
- package/dist/base/period-selector.js +2 -0
- package/dist/base/period-selector.js.map +1 -1
- package/dist/base/placeholder.d.ts +2 -0
- package/dist/base/placeholder.js +2 -0
- package/dist/base/placeholder.js.map +1 -1
- package/dist/base/resource-form-schema.d.ts +2 -2
- package/dist/base/searchable.d.ts +3 -2
- package/dist/base/searchable.js +2 -2
- package/dist/base/searchable.js.map +1 -1
- package/dist/base/section.d.ts +6 -2
- package/dist/base/section.js +4 -0
- package/dist/base/section.js.map +1 -1
- package/dist/base/stack.d.ts +6 -0
- package/dist/base/stack.js +6 -0
- package/dist/base/stack.js.map +1 -1
- package/dist/base/stat-group.d.ts +16 -14
- package/dist/base/stat-group.js +2 -0
- package/dist/base/stat-group.js.map +1 -1
- package/dist/base/tab.d.ts +10 -8
- package/dist/base/tab.js +2 -0
- package/dist/base/tab.js.map +1 -1
- package/dist/base/table-view/column-display.d.ts +1 -1
- package/dist/base/table-view/column-display.js.map +1 -1
- package/dist/base/table-view/data-functions.d.ts +10 -4
- package/dist/base/table-view/data-functions.js.map +1 -1
- package/dist/base/table-view/factory.d.ts +5 -0
- package/dist/base/table-view/factory.js +8 -4
- package/dist/base/table-view/factory.js.map +1 -1
- package/dist/base/table-view/internal/auth.d.ts +17 -3
- package/dist/base/table-view/internal/auth.js +26 -5
- package/dist/base/table-view/internal/auth.js.map +1 -1
- package/dist/base/table-view/internal/column-conditions.d.ts +8 -1
- package/dist/base/table-view/internal/column-conditions.js +25 -13
- package/dist/base/table-view/internal/column-conditions.js.map +1 -1
- package/dist/base/table-view/internal/column-display.d.ts +1 -2
- package/dist/base/table-view/internal/column-display.js +3 -9
- package/dist/base/table-view/internal/column-display.js.map +1 -1
- package/dist/base/table-view/internal/factory-helpers.d.ts +1 -1
- package/dist/base/table-view/internal/factory-helpers.js +17 -14
- package/dist/base/table-view/internal/factory-helpers.js.map +1 -1
- package/dist/base/table-view/internal/factory-lifecycle-created.d.ts +2 -3
- package/dist/base/table-view/internal/factory-lifecycle-created.js +5 -5
- package/dist/base/table-view/internal/factory-lifecycle-created.js.map +1 -1
- package/dist/base/table-view/internal/factory-lifecycle-filter.d.ts +2 -2
- package/dist/base/table-view/internal/factory-private-actions.d.ts +2 -2
- package/dist/base/table-view/internal/files.d.ts +2 -1
- package/dist/base/table-view/internal/files.js +14 -5
- package/dist/base/table-view/internal/files.js.map +1 -1
- package/dist/base/table-view/internal/footer.d.ts +3 -3
- package/dist/base/table-view/internal/footer.js +16 -15
- package/dist/base/table-view/internal/footer.js.map +1 -1
- package/dist/base/table-view/internal/guards.d.ts +27 -5
- package/dist/base/table-view/internal/guards.js +127 -49
- package/dist/base/table-view/internal/guards.js.map +1 -1
- package/dist/base/table-view/internal/meta-helpers.d.ts +1 -1
- package/dist/base/table-view/internal/meta-helpers.js +3 -4
- package/dist/base/table-view/internal/meta-helpers.js.map +1 -1
- package/dist/base/table-view/internal/meta.js +2 -3
- package/dist/base/table-view/internal/meta.js.map +1 -1
- package/dist/base/table-view/internal/option-contract.d.ts +1 -1
- package/dist/base/table-view/internal/option-footer.d.ts +2 -2
- package/dist/base/table-view/internal/option-row-actions.d.ts +1 -1
- package/dist/base/table-view/internal/realtime.d.ts +52 -11
- package/dist/base/table-view/internal/realtime.js +23 -16
- package/dist/base/table-view/internal/realtime.js.map +1 -1
- package/dist/base/table-view/internal/resource-form.d.ts +1 -1
- package/dist/base/table-view/internal/resource-form.js +1 -1
- package/dist/base/table-view/internal/resource-form.js.map +1 -1
- package/dist/base/table-view/internal/row-actions-schema.d.ts +12 -12
- package/dist/base/table-view/internal/row-rule-shape.d.ts +11 -5
- package/dist/base/table-view/internal/row-rule-shape.js +60 -30
- package/dist/base/table-view/internal/row-rule-shape.js.map +1 -1
- package/dist/base/table-view/internal/row-rules.d.ts +28 -1
- package/dist/base/table-view/internal/row-rules.js +8 -19
- package/dist/base/table-view/internal/row-rules.js.map +1 -1
- package/dist/base/table-view/internal/tabs.d.ts +1 -1
- package/dist/base/table-view/internal/tabs.js +1 -4
- package/dist/base/table-view/internal/tabs.js.map +1 -1
- package/dist/base/table-view/internal/validation.d.ts +2 -2
- package/dist/base/table-view/internal/validation.js +7 -7
- package/dist/base/table-view/internal/validation.js.map +1 -1
- package/dist/base/table-view/meta.d.ts +7 -6
- package/dist/base/table-view/meta.js +12 -7
- package/dist/base/table-view/meta.js.map +1 -1
- package/dist/base/table-view/resource-form.d.ts +3 -1
- package/dist/base/table-view/resource-form.js +2 -0
- package/dist/base/table-view/resource-form.js.map +1 -1
- package/dist/base/table-view/routes.d.ts +73 -21
- package/dist/base/table-view/routes.js +13 -10
- package/dist/base/table-view/routes.js.map +1 -1
- package/dist/base/table-view/schema.d.ts +152 -152
- package/dist/base/table-view/source.js +4 -1
- package/dist/base/table-view/source.js.map +1 -1
- package/dist/base/table-view.d.ts +1 -1
- package/dist/base/table-view.js.map +1 -1
- package/dist/base/top-list-card.d.ts +35 -33
- package/dist/base/top-list-card.js +2 -0
- package/dist/base/top-list-card.js.map +1 -1
- package/dist/base/types/error.js +5 -2
- package/dist/base/types/error.js.map +1 -1
- package/dist/base/types/guards.d.ts +4 -4
- package/dist/base/types/json.d.ts +1 -10
- package/dist/base/types/row-action.d.ts +7 -7
- package/dist/component-options/confirm-dialog.d.ts +2 -0
- package/dist/component-options/confirm-dialog.js.map +1 -1
- package/dist/component-options/data-type.d.ts +2 -2
- package/dist/component-options/data-type.js +7 -7
- package/dist/component-options/data-type.js.map +1 -1
- package/dist/component-options/row-action.d.ts +14 -9
- package/dist/component-slots.d.ts +11 -2
- package/dist/component-slots.js +1 -0
- package/dist/component-slots.js.map +1 -1
- package/dist/component.d.ts +7 -8
- package/dist/component.js +17 -25
- package/dist/component.js.map +1 -1
- package/dist/data-controllers/members.d.ts +11 -7
- package/dist/data-controllers/members.js +1 -1
- package/dist/data-controllers/members.js.map +1 -1
- package/dist/data-controllers/roles.d.ts +32 -10
- package/dist/db/internal/exportJobs.model.d.ts +7 -5
- package/dist/db/internal/inviteResolutions.model.d.ts +7 -5
- package/dist/db/internal/tenantLifecycle.model.d.ts +7 -5
- package/dist/db/models/roles.model.d.ts +7 -5
- package/dist/db/models/tenantMembers.model.d.ts +7 -5
- package/dist/db/models/tenants.model.d.ts +7 -5
- package/dist/db/models/user_invites.model.d.ts +7 -5
- package/dist/guards.d.ts +1 -1
- package/dist/hooks.d.ts +1 -2
- package/dist/hooks.js +33 -26
- package/dist/hooks.js.map +1 -1
- package/dist/internal/attachments.d.ts +2 -2
- package/dist/internal/component-builder.js +2 -1
- package/dist/internal/component-builder.js.map +1 -1
- package/dist/internal/component-serialization.d.ts +2 -4
- package/dist/internal/component-serialization.js +4 -3
- package/dist/internal/component-serialization.js.map +1 -1
- package/dist/internal/component-slots.d.ts +19 -2
- package/dist/internal/component-slots.js +35 -12
- package/dist/internal/component-slots.js.map +1 -1
- package/dist/internal/component-watch-filter.d.ts +11 -0
- package/dist/internal/component-watch-filter.js +22 -0
- package/dist/internal/component-watch-filter.js.map +1 -0
- package/dist/internal/component-watches.js +0 -1
- package/dist/internal/component-watches.js.map +1 -1
- package/dist/internal/own-entry.d.ts +7 -0
- package/dist/internal/own-entry.js +13 -0
- package/dist/internal/own-entry.js.map +1 -0
- package/dist/invite-extension-payloads.d.ts +14 -2
- package/dist/invite-extensions/internal/delivery.js +1 -2
- package/dist/invite-extensions/internal/delivery.js.map +1 -1
- package/dist/invite-extensions/internal/edit.d.ts +2 -1
- package/dist/invite-extensions/internal/edit.js +8 -9
- package/dist/invite-extensions/internal/edit.js.map +1 -1
- package/dist/invite-extensions/internal/form-slot.js +8 -15
- package/dist/invite-extensions/internal/form-slot.js.map +1 -1
- package/dist/invite-extensions/internal/payload-slices.d.ts +33 -3
- package/dist/invite-extensions/internal/payload-slices.js +84 -19
- package/dist/invite-extensions/internal/payload-slices.js.map +1 -1
- package/dist/invite-extensions/internal/types.d.ts +10 -1
- package/dist/invite-resolution.js +6 -4
- package/dist/invite-resolution.js.map +1 -1
- package/dist/json.d.ts +10 -0
- package/dist/json.js +3 -0
- package/dist/json.js.map +1 -0
- package/dist/layout-banners.d.ts +5 -2
- package/dist/layout-banners.js.map +1 -1
- package/dist/page/controllers.d.ts +2 -2
- package/dist/page/internal/extension-validation.js +3 -2
- package/dist/page/internal/extension-validation.js.map +1 -1
- package/dist/page/internal/metadata-layout.js +1 -1
- package/dist/page/internal/metadata-layout.js.map +1 -1
- package/dist/page/internal/metadata-publish.js +3 -2
- package/dist/page/internal/metadata-publish.js.map +1 -1
- package/dist/page/internal/metadata-registration.js +5 -4
- package/dist/page/internal/metadata-registration.js.map +1 -1
- package/dist/page/internal/metadata-upload-registration.d.ts +3 -1
- package/dist/page/internal/metadata-upload-registration.js +9 -0
- package/dist/page/internal/metadata-upload-registration.js.map +1 -1
- package/dist/page/internal/metadata-uploads.d.ts +18 -5
- package/dist/page/internal/metadata-uploads.js +7 -8
- package/dist/page/internal/metadata-uploads.js.map +1 -1
- package/dist/page/internal/page-record.d.ts +2 -3
- package/dist/page/internal/page-record.js +19 -14
- package/dist/page/internal/page-record.js.map +1 -1
- package/dist/page/layout-filter.js +17 -6
- package/dist/page/layout-filter.js.map +1 -1
- package/dist/page/metadata.js +5 -7
- package/dist/page/metadata.js.map +1 -1
- package/dist/request-tenant.d.ts +10 -4
- package/dist/request-tenant.js +16 -34
- package/dist/request-tenant.js.map +1 -1
- package/dist/tenant-export.d.ts +2 -1
- package/dist/tenant-lifecycle.d.ts +13 -4
- package/dist/tenant-lifecycle.js +51 -18
- package/dist/tenant-lifecycle.js.map +1 -1
- package/dist/types.d.ts +20 -0
- package/dist/uploads.js +42 -59
- package/dist/uploads.js.map +1 -1
- package/dist/utils/fire-and-forget.js.map +1 -1
- package/dist/utils/internal/decorated-class.d.ts +14 -0
- package/dist/utils/internal/decorated-class.js +37 -0
- package/dist/utils/internal/decorated-class.js.map +1 -0
- package/dist/utils/internal/type-check.d.ts +9 -0
- package/dist/utils/internal/type-check.js +13 -5
- package/dist/utils/internal/type-check.js.map +1 -1
- package/dist/utils/internal/value-parser.d.ts +1 -1
- package/dist/utils/internal/value-parser.js +22 -9
- package/dist/utils/internal/value-parser.js.map +1 -1
- package/dist/watch-actions.d.ts +1 -1
- package/docs/1.core/1.introduction.md +82 -77
- package/docs/1.core/10.layout-banners.md +57 -45
- package/docs/1.core/11.modules-and-dynamic-menus.md +178 -0
- package/docs/1.core/12.frontend-modules-and-request-helpers.md +155 -0
- package/docs/1.core/13.tenant-data-export.md +120 -0
- package/docs/1.core/2.pages-and-modules.md +140 -367
- package/docs/1.core/3.components.md +21 -22
- package/docs/1.core/4.permissions.md +92 -71
- package/docs/1.core/5.tenancy.md +112 -86
- package/docs/1.core/6.invites.md +90 -65
- package/docs/1.core/7.hooks.md +96 -166
- package/docs/1.core/8.realtime.md +44 -45
- package/docs/1.core/9.quick-actions.md +37 -30
- package/docs/2.auth/1.introduction.md +33 -35
- package/docs/2.auth/2.authentication.md +60 -38
- package/docs/2.auth/3.tokens-and-sessions.md +56 -55
- package/docs/2.auth/4.users.md +79 -51
- package/docs/2.auth/5.external-identities.md +25 -26
- package/docs/3.base/1.introduction.md +64 -60
- package/docs/3.base/10.display-blocks.md +15 -16
- package/docs/3.base/11.page-layouts.md +273 -0
- package/docs/3.base/12.table-row-actions.md +122 -0
- package/docs/3.base/13.table-filters-and-forms.md +104 -0
- package/docs/3.base/14.table-events-and-helpers.md +114 -0
- package/docs/3.base/15.table-sources-and-gates.md +24 -0
- package/docs/3.base/2.types.md +44 -41
- package/docs/3.base/3.forms.md +27 -28
- package/docs/3.base/4.tables.md +34 -367
- package/docs/3.base/5.charts-and-widgets.md +28 -29
- package/docs/3.base/6.layout.md +21 -270
- package/docs/3.base/8.data-types.md +39 -40
- package/docs/3.base/9.helpers.md +36 -38
- package/docs/4.notifications/1.introduction.md +22 -26
- package/docs/4.notifications/2.categories-and-subjects.md +58 -55
- package/docs/4.notifications/3.sending.md +72 -68
- package/docs/5.html-render/1.introduction.md +119 -81
- package/docs/5.html-render/2.built-in-emails.md +71 -51
- package/docs/index.md +18 -29
- package/docs/tsconfig.json +4 -0
- package/package.json +11 -11
- package/docs/images/emails-admin-invite.png +0 -0
- package/docs/images/emails-change-notice.png +0 -0
- package/docs/images/emails-change-verification.png +0 -0
- package/docs/images/emails-digest-logo-dark.png +0 -0
- package/docs/images/emails-digest-logo-light.png +0 -0
- package/docs/images/emails-digest.png +0 -0
- package/docs/images/emails-export-partial.png +0 -0
- package/docs/images/emails-export-ready.png +0 -0
- package/docs/images/emails-reset-password.png +0 -0
- package/docs/images/emails-two-factor.png +0 -0
- package/docs/images/emails-user-validation.png +0 -0
package/docs/3.base/4.tables.md
CHANGED
|
@@ -1,13 +1,12 @@
|
|
|
1
1
|
---
|
|
2
2
|
title: "Tables"
|
|
3
3
|
description: "TableView over a data controller: columns, routes, row actions, displays, filters and the forms it opens."
|
|
4
|
+
mode: reference
|
|
4
5
|
---
|
|
5
6
|
|
|
6
|
-
# Tables
|
|
7
|
-
|
|
8
7
|
## Overview
|
|
9
8
|
|
|
10
|
-
A table view is built from a data controller. You declare the controller's routes with `TableViewRoutes`, annotate its fields with `@Column`, and then call `TableView(controller, options)` to get the component. The builder reads the controller's data-api metadata
|
|
9
|
+
A table view is built from a data controller. You declare the controller's routes with `TableViewRoutes`, annotate its fields with `@Column`, and then call `TableView(controller, options)` to get the component. The builder reads the controller's data-api metadata (access modes, mandatory modes, listable flags, sortability) and combines it with the column options to produce the column list, the `new` / `edit` / `view` forms, and the set of permission-carrying actions.
|
|
11
10
|
|
|
12
11
|
Everything on this page is exported from the `table-view` module, reachable from the package root or from its own subpath:
|
|
13
12
|
|
|
@@ -21,11 +20,11 @@ Or import directly from the table-view subpath:
|
|
|
21
20
|
import { Column, TableView } from "@antelopejs/interface-dms/base/table-view";
|
|
22
21
|
```
|
|
23
22
|
|
|
24
|
-
## Declare the
|
|
23
|
+
## Declare the data controller
|
|
25
24
|
|
|
26
25
|
`TableViewRoutes.All` is the route set a table view expects. Field decorators from `@antelopejs/interface-data-api/metadata` stay exactly as they are on any data controller; `@Column` is added on top of them.
|
|
27
26
|
|
|
28
|
-
```typescript
|
|
27
|
+
```typescript fragment
|
|
29
28
|
import { Controller } from "@antelopejs/interface-api";
|
|
30
29
|
import {
|
|
31
30
|
DataController,
|
|
@@ -96,12 +95,12 @@ export class roleDataAPI extends DataController(
|
|
|
96
95
|
}
|
|
97
96
|
```
|
|
98
97
|
|
|
99
|
-
### TableViewRoutes
|
|
98
|
+
### `TableViewRoutes`
|
|
100
99
|
|
|
101
100
|
| Route | Endpoint role |
|
|
102
101
|
| --- | --- |
|
|
103
|
-
| `Get` | Read one row. |
|
|
104
|
-
| `List` | Paginated list with search, filters and sorting. |
|
|
102
|
+
| `Get` | Read one row: its readable fields (`RowFields`). |
|
|
103
|
+
| `List` | Paginated list with search, filters and sorting: a `TableViewListPage`, `{ results, total, offset, limit }`, each result the row's listable fields (`RowFields`). A wrapper reads a field through a guard. |
|
|
105
104
|
| `Select` | List with the `select` pluck mode, used by relation pickers. `?ids=a&ids=b` (with `idKey` when the value is another field) answers those rows alone, as `List` does: a picker and a filter chip name the ids they hold this way. |
|
|
106
105
|
| `Count` | Row count for the current query. |
|
|
107
106
|
| `CountBatch` | One count per repeated `query` value in `GET /count/batch` (tab, view and group counters). Each value is `encodeURIComponent(id) + "?" + listQueryString`, with repeated filters preserved. Requests accept up to 50 entries and 8,192 UTF-8 bytes in the encoded path plus search; the client packs larger batches into bounded GET requests. |
|
|
@@ -114,13 +113,13 @@ export class roleDataAPI extends DataController(
|
|
|
114
113
|
| `ExportRoutes` | A record with `exportStart` (`/export/start`), `exportStatus` (`/export/status/:exportId`) and `exportDownload` (`/export/download/:exportId`). |
|
|
115
114
|
| `All` | Every route above, spread into one object. |
|
|
116
115
|
|
|
117
|
-
These are not the plain data-api defaults. Every route asserts tenant access and the action's permission; the mutating ones
|
|
116
|
+
These are not the plain data-api defaults. Every route asserts tenant access and the action's permission; the mutating ones also run the matching guard and publish the mutation to the table's realtime topic, and `Edit`, `Delete`, `Archive` and `Restore` also validate the affected rows against the action's rule. `New` and `Edit` promote staged file uploads before writing, and `Edit` and `Delete` remove the files a row no longer references afterwards. Passing a narrower set than `All` removes the corresponding capabilities. The export action, for instance, is offered only when all three `ExportRoutes` endpoints are registered.
|
|
118
117
|
|
|
119
118
|
`@Searchable` on the example's `name` field is what makes the list route's free-text search consider that column; it is documented in [Helpers](/docs/interfaces/base/helpers).
|
|
120
119
|
|
|
121
|
-
## The Column
|
|
120
|
+
## The `Column` decorator
|
|
122
121
|
|
|
123
|
-
`@Column` is a method-and-property decorator. It records the column, and unless `validate` is `false` it registers a data-api `Validator` built from the data type's `getValidation()` schema (wrapped for localization when the underlying table field carries the `LocalizationModifier`). When `filterable` is set it registers the data type's `filter` implementation as the field's data-api `Filter`. Finally, if the data type defines `decorateField`, that hook runs
|
|
122
|
+
`@Column` is a method-and-property decorator. It records the column, and unless `validate` is `false` it registers a data-api `Validator` built from the data type's `getValidation()` schema (wrapped for localization when the underlying table field carries the `LocalizationModifier`). When `filterable` is set it registers the data type's `filter` implementation as the field's data-api `Filter`. Finally, if the data type defines `decorateField`, that hook runs. A relation type installs its `Foreign` join this way.
|
|
124
123
|
|
|
125
124
|
| Option | Type | Description |
|
|
126
125
|
| --- | --- | --- |
|
|
@@ -130,7 +129,7 @@ These are not the plain data-api defaults. Every route asserts tenant access and
|
|
|
130
129
|
| `order` | `number` | Sort key among columns; missing values sort as `0`. |
|
|
131
130
|
| `isVisible` | `boolean` | Whether the column is shown by default. `false` hides it until the user picks it in the column menu. |
|
|
132
131
|
| `defaultValue` | `DefaultValue` | Default value for the column and the generated form field. |
|
|
133
|
-
| `readonlyBehavior` | `ReadonlyBehavior` | Per-mode behaviour for a read-only field; see [Shared
|
|
132
|
+
| `readonlyBehavior` | `ReadonlyBehavior` | Per-mode behaviour for a read-only field; see [Shared types](/docs/interfaces/base/types). |
|
|
134
133
|
| `validate` | `boolean` | Set to `false` to skip registering the validator. |
|
|
135
134
|
| `filterable` | `boolean` | Register the type's filter and allow column filtering. |
|
|
136
135
|
| `inputComponent` | `Component` | Overrides the input component the data type supplies. |
|
|
@@ -141,11 +140,11 @@ These are not the plain data-api defaults. Every route asserts tenant access and
|
|
|
141
140
|
| `disabledWhen` | `FieldCondition` | Same shape: the form field is read-only while the condition holds, and the data routes ignore the value sent for it. |
|
|
142
141
|
| `display` | `ColumnDisplay` | A display class instance (`new DefaultDisplays.IdentityDisplay({ … })`, or a module's own `@RegisterDisplay("<module>:<id>")` class) the grid cells (and expanded-row fields) render with, its options typed by the class. `type` keeps driving forms, filters, validation and exports; the formatter also receives the row. Serialized as `{ type, options? }`. |
|
|
143
142
|
|
|
144
|
-
A field is read-only in a form when its `@Access` for the action that form runs is `AccessMode.ReadOnly`: the per-action override (`@Access(AccessMode.ReadWrite, { new: AccessMode.ReadOnly })`, keyed `new`, `edit` and `get` for the view form) where it declares one, else its default access
|
|
143
|
+
A field is read-only in a form when its `@Access` for the action that form runs is `AccessMode.ReadOnly`: the per-action override (`@Access(AccessMode.ReadWrite, { new: AccessMode.ReadOnly })`, keyed `new`, `edit` and `get` for the view form) where it declares one, else its default access: the access the data API applies to the value it writes. Such a field is disabled in that form, or dropped from it when its `readonlyBehavior` resolves to `hidden` for the mode; a field whose data type has no input component is dropped from every form.
|
|
145
144
|
|
|
146
|
-
`visibleWhen` and `disabledWhen` make a field depend on the value of another column, with no watch: the generated forms apply them from the values they open with, and the `new` and `edit` routes (`TableViewRoutes.New`, `Edit`, `EditWith`) apply them to what they write, read on the row as it will be stored
|
|
145
|
+
`visibleWhen` and `disabledWhen` make a field depend on the value of another column, with no watch: the generated forms apply them from the values they open with, and the `new` and `edit` routes (`TableViewRoutes.New`, `Edit`, `EditWith`) apply them to what they write, read on the row as it will be stored, an edit's stored row under its body. A hidden or disabled field leaves the body before the guard runs, and an edit clears the value a hidden field stored (a disabled one keeps its own); `@Mandatory` binds a field with `visibleWhen` only while it shows, and there an edit must leave it filled (a deal moved to `lost` without its reason is refused with `400 { code: "missing_mandatory_fields", fields: ["lostReason"] }`, as the data API refuses any mandatory field). A condition naming an unknown column, the column itself or no value throws at registration.
|
|
147
146
|
|
|
148
|
-
```ts
|
|
147
|
+
```ts fragment
|
|
149
148
|
@Mandatory("new", "edit")
|
|
150
149
|
@Column({ name: "Loss reason", type: reasonType, visibleWhen: { field: "stage", in: ["lost"] } })
|
|
151
150
|
declare lostReason: string;
|
|
@@ -154,11 +153,11 @@ declare lostReason: string;
|
|
|
154
153
|
declare probability: number;
|
|
155
154
|
```
|
|
156
155
|
|
|
157
|
-
### Column
|
|
156
|
+
### Column groups
|
|
158
157
|
|
|
159
158
|
`ColumnGroup(id, config)` is a class decorator on the data controller. Columns join a group by naming its `id` in `ColumnOptions.group`; the generated forms then render a `FieldGroup` in the position of the group's first column.
|
|
160
159
|
|
|
161
|
-
```typescript
|
|
160
|
+
```typescript fragment
|
|
162
161
|
import { Column, ColumnGroup } from "@antelopejs/interface-dms/base/table-view";
|
|
163
162
|
|
|
164
163
|
@ColumnGroup("contact", { label: "Contact", orientation: "horizontal", order: 2 })
|
|
@@ -173,15 +172,15 @@ export class customerDataAPI extends DataController(/* … */) {
|
|
|
173
172
|
|
|
174
173
|
`ColumnGroupConfig` has `label: string` (required), `description?`, `orientation?: "horizontal" | "vertical"` and `order?: number`.
|
|
175
174
|
|
|
176
|
-
###
|
|
175
|
+
### `select` and `exported`
|
|
177
176
|
|
|
178
177
|
`@Select(requiredFields?)` and `@Exported(requiredFields?)` mark a field as listable in the `select` and `export` pluck modes respectively. Both forward to `Listable` from `@antelopejs/interface-data-api/metadata` with the corresponding mode, and both accept the same `boolean | string[]` argument that `Listable` takes for dependent fields.
|
|
179
178
|
|
|
180
|
-
### The
|
|
179
|
+
### The archive field
|
|
181
180
|
|
|
182
181
|
`@ArchiveField` marks the boolean field that carries the archived state. It records the field on the table view metadata and also applies `Filter()` and `Listable()` to it, because archive filtering needs both.
|
|
183
182
|
|
|
184
|
-
```typescript
|
|
183
|
+
```typescript fragment
|
|
185
184
|
import { ArchiveField } from "@antelopejs/interface-dms/base/table-view";
|
|
186
185
|
|
|
187
186
|
@ArchiveField()
|
|
@@ -189,13 +188,13 @@ import { ArchiveField } from "@antelopejs/interface-dms/base/table-view";
|
|
|
189
188
|
declare archived: boolean;
|
|
190
189
|
```
|
|
191
190
|
|
|
192
|
-
`DEFAULT_ROW_ID_FIELD` is the constant `"_id"
|
|
191
|
+
`DEFAULT_ROW_ID_FIELD` is the constant `"_id"`: the row identifier used when `TableViewOptions.rowIdKey` is not set.
|
|
193
192
|
|
|
194
|
-
## Build the
|
|
193
|
+
## Build the table view
|
|
195
194
|
|
|
196
195
|
`TableView` returns a `ComponentBuilder<TableViewOptionsSerialized>`, assigned to a static field of a page class.
|
|
197
196
|
|
|
198
|
-
```typescript
|
|
197
|
+
```typescript fragment
|
|
199
198
|
import {
|
|
200
199
|
PageController,
|
|
201
200
|
pagesCategory,
|
|
@@ -226,7 +225,7 @@ export class RolesPage extends PageController("roles", {
|
|
|
226
225
|
|
|
227
226
|
### Signature
|
|
228
227
|
|
|
229
|
-
```typescript
|
|
228
|
+
```typescript fragment
|
|
230
229
|
function TableView<T extends ControllerClass>(
|
|
231
230
|
controller: T,
|
|
232
231
|
options?: TableViewOptions<InstanceType<T>>,
|
|
@@ -235,7 +234,7 @@ function TableView<T extends ControllerClass>(
|
|
|
235
234
|
|
|
236
235
|
Typing `TableViewOptions` by `InstanceType<T>` is what makes row-action rules field-name-checked against the controller's own fields.
|
|
237
236
|
|
|
238
|
-
### TableViewOptions
|
|
237
|
+
### `TableViewOptions`
|
|
239
238
|
|
|
240
239
|
| Option | Type | Description |
|
|
241
240
|
| --- | --- | --- |
|
|
@@ -243,17 +242,17 @@ Typing `TableViewOptions` by `InstanceType<T>` is what makes row-action rules fi
|
|
|
243
242
|
| `layout` | `TableViewLayout` | `"full"` (default): the dashboard grid; `"compact"`: the list of a settings page (tabs, open search, quick filters, the footer's page size picker; no caption, refresh, ⋯ menu nor column menus). Either way, search shows only with `@Searchable` fields, filters only with a `filterable` column, the caption only when `caption` is set. |
|
|
244
243
|
| `searchPlaceholder` | `string` | Placeholder of the search field, also its accessible name. |
|
|
245
244
|
| `searchShortcut` | `string` | Key opening and focusing the search, in Nuxt UI `defineShortcuts` notation (`"/"`, `"shift_f"`): `"shift_f"` (⇧F) by default. It does not fire while the user types in a field. |
|
|
246
|
-
| `quickFilters` | `TableViewQuickFilter[]` | `{ field, label?, icon?, allLabel?, mode? }` dropdown filters over a filterable column's values, `mode` a `TableViewQuickFilterMode`. The picked value is the column's own filter: kept with the table state, counted, cleared by "Clear filters"; in the `compact` layout the button reads "Role: Admin" with a clear button. Unknown or non-filterable columns throw. The column needs values to list (a select with `items`, a boolean, a status, a relation, or an input declaring `items` or a `searchUrl`): on any other
|
|
245
|
+
| `quickFilters` | `TableViewQuickFilter[]` | `{ field, label?, icon?, allLabel?, mode? }` dropdown filters over a filterable column's values, `mode` a `TableViewQuickFilterMode`. The picked value is the column's own filter: kept with the table state, counted, cleared by "Clear filters"; in the `compact` layout the button reads "Role: Admin" with a clear button. Unknown or non-filterable columns throw. The column needs values to list (a select with `items`, a boolean, a status, a relation, or an input declaring `items` or a `searchUrl`): on any other, such as a free-text string, the toolbar leaves it out and registration logs a warning. |
|
|
247
246
|
| `pageSize` | `number` | Rows per page while the user picked none (default 10), 1 to 50: registration throws above. A size other than 10, 25 and 50 joins them in the table's page size picker. |
|
|
248
247
|
| `pagination` | `TableViewPaginationMode` | `"pages"` (default): a pager; `"loadMore"`: a "Load more" button; `"infinite"`: the next page loads as the footer scrolls into view. The last two ask for every page loaded so far, from the first row, so a refresh re-reads them. |
|
|
249
248
|
| `footer` | `TableViewFooterOptions` | `{ countLabel?, hint?, summary?, legend? }`: the footer's row count (`$` key pluralized on `{ count }`) and hint; `summary` figures (`TableViewFooterSummary`: `label`, `field?`, `op: "sum" \| "count"`, `where?` row rule, `format?` `TableViewSummaryFormat`) computed by the `Summary` route over every listed row; `legend`, a `SelectType` column. Serialized as `TableViewFooterSerialized`: each summary gets an `id`, its `where` stays server-side (`TableViewMeta.footerSummary(id)`). A sum without `field`, an unknown column or a legend that is no select column throws. |
|
|
250
|
-
| `emptyStates` | `TableViewEmptyStates` | `{ firstRun?, filtered?, error? }`, each a `TableViewEmptyState` (`title`, `description?`, `icon?`, `actions?: BlockLinkAction[]`, `component?`) the empty body of the grid and the `cards` display shows instead of the built-in words, texts
|
|
249
|
+
| `emptyStates` | `TableViewEmptyStates` | `{ firstRun?, filtered?, error? }`, each a `TableViewEmptyState` (`title`, `description?`, `icon?`, `actions?: BlockLinkAction[]`, `component?`) the empty body of the grid and the `cards` display shows instead of the built-in words, its texts (keys and literals alike) receiving `{ search }`. A `component` receives `state`, `search`, `refresh()`, and `clearSearch()` / `clearFilters()` while there is a search or a filter chip to clear. |
|
|
251
250
|
| `expandable` | `TableViewExpandableOptions` | Expandable rows, a union: `{ fields, fieldsLabel? }` (column keys or `{ key, label? }`, each rendered by its column's data type) **or** `{ component }` (receives `TableViewExpandedRowProps`: `row`, `rowId`, `columns`, `labelKey`, `actions`, `open` and `refresh`, and draws the whole band), plus `defaultExpanded` (`"none"` \| `"first"` \| `"all"`), `single` and `lazyLoad` (read the row from `get` when it opens). Fields must name declared columns; both or neither throws. Serialized as `TableViewExpandableSerialized`. |
|
|
252
251
|
| `rowIdKey` | `string` | Field holding the row id. Defaults to `DEFAULT_ROW_ID_FIELD`. |
|
|
253
252
|
| `labelKey` | `string` | Field naming a row: it follows the drawer or modal title ("Edit task · Write the docs") and ends the breadcrumb of the edit and details pages (set on their forms as `labelKey`). |
|
|
254
253
|
| `rowActions` | `TableViewRowActionOptions<T>` | Built-in and custom row actions. |
|
|
255
254
|
| `columns` | `TableViewColumnsOptions` | The controller's columns as this table view shows them: `hidden` lists the ones it opens hidden, as `isVisible: false` does on the `@Column`, for this table view only (they stay in the column menu, filterable and in the forms). A name the controller has no column for throws at registration. |
|
|
256
|
-
| `customButtons` | `CustomButton[]` | Toolbar buttons; see [Shared
|
|
255
|
+
| `customButtons` | `CustomButton[]` | Toolbar buttons; see [Shared types](/docs/interfaces/base/types). |
|
|
257
256
|
| `forms` | `Partial<Record<"new" \| "edit" \| "view", TableViewFormSetup>>` | A callback per generated form, `(form: FormBuilder) => void`, receiving its builder before the table view embeds it, for input comfort only: what it declares (`.watch()`, a value filled in from another, a field one table's forms leave out) holds in a drawer, a modal or a form page alike. No data rule here, the routes never see it: a field shown, required or locked by another's value is a column's `visibleWhen` / `disabledWhen`. |
|
|
258
257
|
| `formContainer` | `FormContainer` | Where the `new` / `edit` / `view` forms open. |
|
|
259
258
|
| `archiveMode` | `boolean` | Replace delete with archive/restore. Requires `@ArchiveField`. |
|
|
@@ -279,348 +278,16 @@ Typing `TableViewOptions` by `InstanceType<T>` is what makes row-action rules fi
|
|
|
279
278
|
| `guards` | `TableViewGuards<T>` | Server-side guards run before mutating actions. |
|
|
280
279
|
| `bypassTenantAccessGate` | `boolean` | Skip the tenant access gate on every data route of this table view. |
|
|
281
280
|
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
```typescript
|
|
285
|
-
TableView.fromSource(options: TableViewSourceOptions): ComponentBuilder<TableViewOptionsSerialized>;
|
|
286
|
-
```
|
|
287
|
-
|
|
288
|
-
A read-only table view with no data controller (`tableViewFromSource`). `TableViewSourceOptions` takes `columns` (a record of `TableViewSourceColumn`, a `@Column`'s options plus `sortable`), `fetchUrl` (its `{{params.X}}` / `{{query.X}}` tokens are filled from the page URL, percent-encoded; the table and its tab counters list again when they change and request nothing while one has no value), `fetchRowUrl?` (a route answering one row by id, `{{row.id}}` its percent-encoded id, with the same tokens: a deep link to a row the table does not list opens through it; serialized on `TableViewSourceSerialized`), `rowIdKey?`, `periodScope?` (the id of a `PeriodSelector`: every request carries its `from`, `to`, `preset` and `comparison`, the table lists again when the period changes, and nothing is requested until the selector is on the page), `capabilities?` (`TableViewSourceCapabilities`: `search`, `sort`, `paginate`, `filter`), `queryParamFilters?` (URL query parameters sent to the route as hidden `filter_<field>` filters), custom row actions and buttons only, and the presentation options of a table view (`caption`, `layout`, `pagination`, `tabs`, `quickFilters`, `displays`, `card`, `emptyStates`, `footer` texts and legend…). The route answers `{ results, total }` and is sent the part of the list query its capabilities name, nothing else; the browser does the rest over the rows it answered, except that a paging route's rows are neither searched nor sorted by the browser. Registration runs the checks of `TableView()` that apply (page size, display ids, tab targets) and throws when tabs, quick filters or `queryParamFilters` lack `capabilities.filter`, or when a key they, `labelKey`, `card.fields` or `expandable` name is not a column, or when `expandable` sets `lazyLoad` (a source has no `get` route), or when `fetchRowUrl` lacks `{{row.id}}`; it warns about a quick filter on a column with no values to pick. There is no export, realtime nor footer summary.
|
|
289
|
-
|
|
290
|
-
### Reaching a Table Behind a Tenant Gate
|
|
291
|
-
|
|
292
|
-
`bypassTenantAccessGate` is the table-view mirror of the guard option of the same name. Reserve it for tables that must stay readable while a gate denies the tenant — the invoices on the billing page of a suspended tenant, so it can regularize its situation. Permission checks still apply.
|
|
293
|
-
|
|
294
|
-
Data routes are registered against the controller, not against a page, so the opt-out is controller-level and **latched**: once any `TableView()` call on a controller declares it, the controller's routes bypass the gate wherever they are rendered. A later registration of the same controller without the flag cannot re-gate them, and gets a warning saying so. Two consequences worth designing around:
|
|
295
|
-
|
|
296
|
-
- Declaring the flag on a controller opens its routes for every table view built on it, including one mounted on an ordinary page.
|
|
297
|
-
- Actions that carry no permission id — a plain list on a component that was never mounted — are then served to any member of a denied tenant. That is the escape hatch working as intended, so keep every action of a flagged controller stamped with a permission.
|
|
298
|
-
|
|
299
|
-
`TABLE_DISPLAY_ID` (`"table"`), `KANBAN_DISPLAY_ID` (`"kanban"`), `CARDS_DISPLAY_ID` (`"cards"`) and `GROUPED_DISPLAY_ID` (`"grouped"`) are exported constants for the reserved built-in display ids. `TableViewOptionsSerialized` is the transported shape: `customButtons`, `rowActions`, `displays` become their serialized counterparts, `kanban` and `grouped` are folded into `displays`, and two fields are added — `enableTableExport: boolean` and `formComponents`, a record of the serialized `new` / `edit` / `view` forms. When a page is served, each table view on it also receives `tableId` (its key in the page) and `isSoleTableView`, which decide the URL keys opening its views and tabs (`?<tableId>.view=`, or `?view=` for a page's only table view).
|
|
281
|
+
`TABLE_DISPLAY_ID` (`"table"`), `KANBAN_DISPLAY_ID` (`"kanban"`), `CARDS_DISPLAY_ID` (`"cards"`) and `GROUPED_DISPLAY_ID` (`"grouped"`) are exported constants for the reserved built-in display ids. `TableViewOptionsSerialized` is the transported shape: `customButtons`, `rowActions`, `displays` become their serialized counterparts, `kanban` and `grouped` are folded into `displays`, and two fields are added: `enableTableExport: boolean` and `formComponents`, a record of the serialized `new` / `edit` / `view` forms. When a page is served, each table view on it also receives `tableId` (its key in the page) and `isSoleTableView`, which decide the URL keys opening its views and tabs (`?<tableId>.view=`, or `?view=` for a page's only table view).
|
|
300
282
|
|
|
301
283
|
`tableViewLink(page, tableId, { view?, tab? })` returns the dashboard path of `page` opening one of its table views on a view or a tab (`/workspaces?content.view=past-due`); it throws for a class that is no page controller. `TABLE_VIEW_VIEW_QUERY_KEY` (`"view"`) and `TABLE_VIEW_TAB_QUERY_KEY` (`"tab"`) are the keys it uses.
|
|
302
284
|
|
|
303
285
|
`TableView` throws at declaration time when the options do not hold together: `archiveMode` without an `@ArchiveField`, a `kanban.groupByField` that is not a column or whose type is not a non-multiple `SelectType`, a `BooleanType` or a `StatusType`, a `card.fields` entry that is not a column, a `grouped.groupByField` that is not a sortable column (or not a date one, by day or week), a `defaultDisplay` that no declared display provides, `displays: []` with neither `kanban` nor `grouped` (no display at all), or views that repeat an id, filter on a column that is not filterable, sort on one that is not sortable, name an unknown column, open a display the table does not offer, or default to an undeclared view.
|
|
304
286
|
|
|
305
|
-
##
|
|
306
|
-
|
|
307
|
-
`TableViewRowActionOptions<T>` configures the row menu. Every built-in action takes `true`, `false`, or a `RowActionConfig<T>` carrying a rule.
|
|
308
|
-
|
|
309
|
-
| Option | Type | Description |
|
|
310
|
-
| --- | --- | --- |
|
|
311
|
-
| `add` | `boolean \| RowActionConfig<T>` | Create a row. |
|
|
312
|
-
| `edit` | `boolean \| RowActionConfig<T>` | Edit a row. |
|
|
313
|
-
| `details` | `boolean \| RowActionConfig<T>` | Open the read-only view. |
|
|
314
|
-
| `duplicate` | `boolean \| RowActionConfig<T>` | Open the `new` form pre-filled from the row. |
|
|
315
|
-
| `delete` | `boolean \| RowActionConfig<T>` | Delete a row. |
|
|
316
|
-
| `archive` | `boolean \| RowActionConfig<T>` | Archive a row. |
|
|
317
|
-
| `restore` | `boolean \| RowActionConfig<T>` | Restore an archived row. |
|
|
318
|
-
| `copyLink` | `boolean \| RowActionConfig<T>` | Copy a link to the row. |
|
|
319
|
-
| `hasSelection` | `boolean` | Enable row selection. |
|
|
320
|
-
| `custom` | `CustomRowAction<T>[]` | Extra actions pointing at an `ActionTarget`. |
|
|
321
|
-
|
|
322
|
-
```typescript
|
|
323
|
-
static table = TableView(memberDataAPI, {
|
|
324
|
-
rowActions: {
|
|
325
|
-
add: false,
|
|
326
|
-
edit: { isEnabled: true, isVisible: true },
|
|
327
|
-
delete: { isEnabled: true, rule: { field: "isTenantOwner", notEquals: true } },
|
|
328
|
-
hasSelection: true,
|
|
329
|
-
custom: [
|
|
330
|
-
{
|
|
331
|
-
label: "Validate email",
|
|
332
|
-
icon: "i-ph-check-circle",
|
|
333
|
-
target: {
|
|
334
|
-
type: "api",
|
|
335
|
-
url: "/settings/workspace/members/{{row._id}}/validate-email",
|
|
336
|
-
method: "POST",
|
|
337
|
-
successMessage: "Email validated",
|
|
338
|
-
},
|
|
339
|
-
rule: { field: "isValidated", notEquals: true },
|
|
340
|
-
},
|
|
341
|
-
],
|
|
342
|
-
},
|
|
343
|
-
});
|
|
344
|
-
```
|
|
345
|
-
|
|
346
|
-
A double-click on a row opens `edit`, or `details` when edit is off — a single click on a table declaring `rowClick: "single"` (`TableViewRowClick`), which leaves a click on a control of the row (a button, a link, a checkbox) or one ending a text selection to them. A custom action flagged `isDefault` takes that slot instead — the first flagged action whose `rule` accepts the row wins, and the click opens its `ActionTarget` like the menu entry does. This is how a table whose detail view is a custom drawer keeps the row affordance: without a click target the row is inert and no longer lights up on hover.
|
|
347
|
-
|
|
348
|
-
```typescript
|
|
349
|
-
custom: [
|
|
350
|
-
{
|
|
351
|
-
label: "Details",
|
|
352
|
-
icon: "i-ph-info",
|
|
353
|
-
isDefault: true,
|
|
354
|
-
target: { type: "drawer", component: CustomComponent("SendDetailDrawer"), title: "Details" },
|
|
355
|
-
},
|
|
356
|
-
],
|
|
357
|
-
```
|
|
358
|
-
|
|
359
|
-
Rules are enforced on both sides. A data controller has one **writing** table view — a `TableView` enabling at least one write action (add, duplicate, edit, delete, archive or restore) that the controller offers — and its `rowActions` (archive-mode defaults included) become the controller's `controllerRowActionRules`, along with its `rowIdKey` and `strictRuleValidation`. The `edit`, `delete`, `archive` and `restore` routes validate the affected ids against those rules before running. Rows that fail the rule are dropped from the operation; with `strictRuleValidation` the whole request is rejected instead.
|
|
360
|
-
|
|
361
|
-
A second writing table view over the same controller fails the registration of its page with a `WritingTableViewConflictError` (a `PageDeclarationConflictError`, rethrown even from a nested component) naming both table views. Read-only table views — every write action set to `false`, no `archiveMode` — may share the controller: they open no `new` / `edit` form and leave its rules and options alone. A page re-registering (a hot reload) re-claims the controller; a page that went away releases it. To give the same data a second writing screen, derive a controller (see [Show the same data in two writing screens](/docs/components/derived-controller)).
|
|
362
|
-
|
|
363
|
-
Permissions are applied on top. The table view registers `list` and `select` as component actions always, `add`, `edit` and `view` when the matching generated form exists, `delete` when the controller declares the delete endpoint, `archive`, `restore` and `viewArchived` under `archiveMode`, and `export` when the three export endpoints are registered. Each action carries a permission id of the form `<component-permission>.<action>`. When the viewer lacks the permission the corresponding row action is forced to `false` and the corresponding generated form is withheld. The rule algebra itself — leaf operators, `and` / `or` / `not`, and `RowActionConfig` — is documented in [Shared Types](/docs/interfaces/base/types).
|
|
364
|
-
|
|
365
|
-
Several pages may each mount their own `TableView` over one data controller, and they all share its data routes. A read action (`list`, `select`, `view`, `export`, `viewArchived`) accepts the permission from any table view a page mounts on that controller; a write action (`add`, `edit`, `delete`, `archive`, `restore`) accepts only the writing table view's, so a user granted `add` on a read-only table view cannot create rows. A read-only table view still declares the write actions the controller offers: while no table view writes through the controller, they keep its write routes behind permission ids nobody is granted by default. The inline add button of a relation field pointing at the controller follows the same rule (`addPermissionIds`).
|
|
366
|
-
|
|
367
|
-
A `new` or `edit` `ResourceForm` block over the controller submits to the same routes, so the native files staged from its fields are accepted on save as well, alongside those of the writing table view. It adds no permission of its own to the union: the routes still require the action's permission from a table view of the controller when one is mounted, exactly as for the block's other fields. The `new` and `edit` forms a page-mode table view mounts on its form sub-pages count the same way: each is a component of its own sub-page, so the files staged from it are accepted on save, under the same table view permissions.
|
|
368
|
-
|
|
369
|
-
`TableViewRowActionOptionsSerialized` is the transported shape: rules and flags untyped, `custom` as `CustomRowActionSerialized[]`, plus a `showArchived?: boolean` field the builder fills from the `viewArchived` permission.
|
|
370
|
-
|
|
371
|
-
### Row action extras
|
|
372
|
-
|
|
373
|
-
`TableViewRowActionOptions` also takes `menuLabel`: the accessible name of a row's ⋯ menu button, its `{field}` braces filled from the row (a `$` key's translation receives the row's fields), "Actions for <label>" by default with `labelKey`, "Row actions" without. A `CustomRowAction`'s `label` fills its `{field}` braces from the row the same way.
|
|
374
|
-
|
|
375
|
-
`RowActionConfig` (built-in actions) also takes `label`, `icon` and `confirm` (an `ActionConfirm`: a `ConfirmDialog`, or `{ from }`, a URL answering one for the row — on delete, archive and restore, for a single row); `BulkRowActionConfig` (delete, archive, restore) adds `successMessage`. `CustomRowAction` also takes `confirm`, `permission` (`ButtonPermission`: an action of the table by name, or an `Action`), `permissionId` (an absolute permission id, which wins over `permission`), `color`, `variant`, `showLabel`, `bulk` (`true` or `{ allMatching: true }`: offered on the selection bar, see below) and `deepLink` (a drawer, modal or side panel it opens is kept in the URL as `?record=<id>` — by default for a side panel, which `false` opts out of —, `?<tableId>.record=<id>` on a page with several table views). An action the caller fails the permission for is stripped per request like a custom button; it is only hidden, so the route its target calls must check the permission itself.
|
|
376
|
-
|
|
377
|
-
A bulk custom action sends the selected ids as a repeated `ids` query parameter, or `allMatching=true` with the table's filters, search and archive view after "Select all N matching". `resolveBulkRowIds(this, ctx, user)`, called from a route of the table's data controller — or `resolveBulkRowIds({ model, tableView }, ctx, user)` (`BulkRowSource`: the model the calling route is given, and the table's `TableViewMeta`) from any other route, a page's included — answers the ids the action covers: the selected ones, or every matching row read like the list reads them, refusing more than `MAX_BULK_MATCHING_ROWS` (413). Either way the caller needs the table's `list` permission (403 otherwise). `BULK_ALL_MATCHING_KEY` is the query flag. The `api` action target also takes `body` (the confirmation's field values merged in) and `copy` (response field copied to the clipboard). `CustomButton` takes `placement` (`"toolbar"`, the default, or `"header"`: drawn in the page header, which the server adds it to); `rowActions.add` takes it too (`AddRowActionConfig`).
|
|
378
|
-
|
|
379
|
-
When several table views share a data controller, each read action of its routes is guarded by the permission ids of every mounted table view, each write action by the writing table view's (`TableViewMeta.actionPermissionIds(action)`; any one suffices). `TableViewMeta.componentBuilders` lists them, `writingComponentBuilders` the ones that write.
|
|
380
|
-
|
|
381
|
-
## Archive Mode
|
|
382
|
-
|
|
383
|
-
With `archiveMode: true`, the archive and restore actions replace deletion. The builder calls `applyArchiveModeDefaultRules` (see [Helpers](/docs/interfaces/base/helpers)) to give every relevant action a default rule against the `@ArchiveField` column, so archived rows offer restore and delete while live rows offer edit, duplicate and archive. Actions you configured explicitly keep your settings; only actions left undefined, set to `true`, or given without a rule receive the default.
|
|
384
|
-
|
|
385
|
-
```typescript
|
|
386
|
-
static table = TableView(articleDataAPI, {
|
|
387
|
-
archiveMode: true,
|
|
388
|
-
rowActions: { edit: true, delete: true, archive: true, restore: true },
|
|
389
|
-
});
|
|
390
|
-
```
|
|
391
|
-
|
|
392
|
-
## Guards
|
|
393
|
-
|
|
394
|
-
A guard runs before the mutating route and aborts it by throwing. Use `assert` from `@antelopejs/interface-api-util` to fail with an HTTP status and a message key. `this` inside a guard is the data controller instance.
|
|
395
|
-
|
|
396
|
-
```typescript
|
|
397
|
-
import { assert } from "@antelopejs/interface-api-util";
|
|
398
|
-
import { getRequestTenantId } from "@antelopejs/interface-dms/request-tenant";
|
|
399
|
-
import { GetModel } from "@antelopejs/interface-database-decorators";
|
|
400
|
-
import { TenantMemberModel } from "@antelopejs/interface-dms/db";
|
|
401
|
-
|
|
402
|
-
static table = TableView(memberDataAPI, {
|
|
403
|
-
guards: {
|
|
404
|
-
edit: async (ctx, { current, body }) => {
|
|
405
|
-
const demoting = current.isTenantOwner && body.isTenantOwner === false;
|
|
406
|
-
if (!demoting) return;
|
|
407
|
-
const model = GetModel(TenantMemberModel, getRequestTenantId(ctx));
|
|
408
|
-
const remaining = await model.countOwnersExcluding([current.userId]);
|
|
409
|
-
assert(remaining > 0, 409, "$page.settings.members.error.last_owner");
|
|
410
|
-
},
|
|
411
|
-
delete: async (ctx, { ids }) => {
|
|
412
|
-
// ids is the full set of rows the request targets
|
|
413
|
-
},
|
|
414
|
-
},
|
|
415
|
-
});
|
|
416
|
-
```
|
|
417
|
-
|
|
418
|
-
The `edit` guard receives `{ id, body, current }`, where `current` is the row as it stands in the database — the route loads it before calling the guard and answers `404` when it does not exist. The `new` guard receives `{ body }`, parsed from the request body whether it arrived as a buffer, a string or an object. The `delete`, `archive` and `restore` guards receive `{ ids, values }`: `values` is the answer of the action's confirmation `fields`, sent as the request body, and is unset when the request carried none (see [Confirmations](/docs/components/tables/confirmations#what-the-rows-take-with-them)). Like the rules, guards are stored on the controller metadata the first time a table view declares them, so they apply to every table view on that controller. The argument interfaces are documented in [Shared Types](/docs/interfaces/base/types).
|
|
419
|
-
|
|
420
|
-
## Filters, Tabs and Displays
|
|
421
|
-
|
|
422
|
-
`defaultFilters` seeds the visible filter bar. `queryParamFilters` and `routeParamFilters` add hidden filters instead: each is a `Record<string, QueryParamFilter>` / `Record<string, RouteParamFilter>` mapping a URL parameter name onto `{ field: string; mode?: string }`, filtering the table without appearing in the UI or in the viewer's stored preferences. A `QueryParamFilter` also takes `visible?: boolean` and `label?: string`: a visible one shows as a removable chip of the filters row while the URL applies it. `queryParamFilters` reads the page's query string, `routeParamFilters` reads the parameters of the page slug (for example `:id` on a detail page). Both also seed `submitDefaults` on the generated forms, so a row created from a filtered view inherits the filter's value. An active `queryParamFilters` filter makes the table read as filtered (its empty body is the `filtered` state, without a clear action), and a change of its value in the URL lists from page 1.
|
|
423
|
-
|
|
424
|
-
```typescript
|
|
425
|
-
static table = TableView(invoiceDataAPI, {
|
|
426
|
-
routeParamFilters: { id: { field: "customerId" } },
|
|
427
|
-
defaultSort: { field: "createdAt", desc: true },
|
|
428
|
-
tabs: [
|
|
429
|
-
{ id: "open", label: "Open", filter: { accessorKey: "status", value: "open", mode: "is" } },
|
|
430
|
-
{ id: "paid", label: "Paid", filter: { accessorKey: "status", value: "paid", mode: "is" } },
|
|
431
|
-
],
|
|
432
|
-
});
|
|
433
|
-
```
|
|
434
|
-
|
|
435
|
-
`TableViewTab` is `{ id, label, filter?: TableViewTabFilter, icon?, textColor?: ColorValue, iconColor?: ColorValue }` plus the link fields (`to`, `countFrom`), the gate (`permission`, `permissionId`) and `navBadge?: boolean | TableViewTabNavBadge` (`{ tone?: Tone }`), and `TableViewTabFilter` is `{ accessorKey: string; value?: string; mode: string }`. A tab gives a `filter` or a `to`, never both (registration throws). `permission` is relative to the table view's permission id (`"list"` → `<table permission id>.list`) or an `Action`; `permissionId` is absolute and wins; a link tab to a page controller follows that page's own permission without either. In the page editor, the tab's permission picker sets `permissionId`, and `permission` is the advanced "Action" text field.
|
|
436
|
-
|
|
437
|
-
`navBadge: true` publishes the tab's counter as the navigation badge of the page it stands for: this table's page for a filter tab, the linked page for a link tab — which then needs a page controller `to` and a data controller `countFrom`; a path or a location logs a warning and publishes nothing. The server counts it on each page visit (the page payload's `navBadges`, never the cached site layout), with the caller's own `list` permission on that table, for the pages the caller can open only; the badge shows in the main and the settings navigation. A count of zero shows no badge, a page's static `MenuOptions.badge` wins over it, and while the table is on screen its own counter keeps it fresh. `TableView.fromSource` tables do not support it. `navBadge: { tone }` draws the badge in that tone instead of neutral; counts landing on one page add up, in the strongest tone. Declaring at least one tab also surfaces an implicit "all" tab that applies no filter.
|
|
438
|
-
|
|
439
|
-
`displays` declares alternative renderings of the same data:
|
|
440
|
-
|
|
441
|
-
| Field | Type | Description |
|
|
442
|
-
| --- | --- | --- |
|
|
443
|
-
| `id` | `string` | Display identifier. Required. |
|
|
444
|
-
| `options` | `Record<string, unknown>` | Options handed to the display. |
|
|
445
|
-
| `component` | `Component` | Frontend component, resolved by name from the global registry. |
|
|
446
|
-
| `selfManagedData` | `boolean` | The display fetches its own data. |
|
|
447
|
-
| `capabilities` | `TableViewDisplayCapabilities` | Which toolbar features the display supports: `columnManagement`, `filters`, `search`, `sorting`, `tabs`, all optional booleans. |
|
|
448
|
-
|
|
449
|
-
`TableViewDisplayOptionSerialized` is the same with `component` serialized.
|
|
450
|
-
|
|
451
|
-
`kanban` is a shorthand for one particular display: the builder appends a `displays` entry with id `"kanban"` carrying the kanban options, unless `displays` already places `{ id: "kanban" }`, which then receives them.
|
|
452
|
-
|
|
453
|
-
| Option | Type | Description |
|
|
454
|
-
| --- | --- | --- |
|
|
455
|
-
| `groupByField` | `string` | Column the cards are grouped by. Must be a non-multiple `SelectType`, a `BooleanType` or a `StatusType` column. Required. |
|
|
456
|
-
| `draggable` | `boolean` | Allow dragging a card to another column to change the group field. Defaults to `true`. |
|
|
457
|
-
| `columnMaxHeight` | `string` | CSS max-height of a column's card list before it scrolls. Defaults to `"60vh"`. |
|
|
458
|
-
|
|
459
|
-
`KanbanOptionsSerialized` is `KanbanOptions` plus the serialized `card` (`TableViewCardOptionsSerialized`).
|
|
460
|
-
|
|
461
|
-
`card` (`TableViewCardOptions`: `{ fields?: string[]; component?: Component; minWidth?: string }`) is the card the `kanban` and `cards` displays draw for each row; it joins the options of both display entries. The component receives the same props in both: `row`, `rowId`, `columns`, `labelKey`, `actions`, `selected`, `select(value?)`, `open()`, on the kanban `groupValue`, and on the `cards` display of a table ordered by hand `reorder` (`{ enabled, dragging, handle }`, the card's move handle).
|
|
462
|
-
|
|
463
|
-
## Generated Forms
|
|
464
|
-
|
|
465
|
-
`TableView` builds up to three forms from the columns, one per `FormMode`, using each column's data type for the input and the controller's mandatory and access metadata for required and disabled state. The `new` form fetches from `<location>/get?id={{query.duplicate}}` and submits to `<location>/new`; the `edit` form fetches from `<location>/get?id={{params.id}}&_presence=1` — the extra parameter makes the `Get` route record the viewer's presence on that row — and submits to `<location>/edit?id={{params.id}}`; the `view` form only fetches, from `<location>/get?id={{params.id}}`. A mode whose column set is empty produces no form and no matching action.
|
|
466
|
-
|
|
467
|
-
`formContainer` decides where they open:
|
|
468
|
-
|
|
469
|
-
```typescript
|
|
470
|
-
type FormContainer =
|
|
471
|
-
| { type: "drawer"; pages?: FormContainerPages<FormContainerPageTexts> }
|
|
472
|
-
| { type: "modal"; size?: ModalSize; pages?: FormContainerPages<FormContainerPageTexts> }
|
|
473
|
-
| { type: "page"; pages?: FormContainerPages<FormContainerPageConfig> };
|
|
474
|
-
```
|
|
475
|
-
|
|
476
|
-
Page mode is the default — it is what you get when `formContainer` is omitted. In page mode the builder registers a hidden sub-page per form under the table's page, each guarded by the matching action's permission.
|
|
477
|
-
|
|
478
|
-
The sub-pages are named after the **table view**, not after the page: their default slugs are `<key>/new`, `<key>/:id/edit` and `<key>/:id/view`, where `<key>` is the key the table view is mounted under — `static invoices = TableView(...)` on `/billing` gives `/billing/invoices/new`. It is the *simple* key even for a table view nested deep in a layout (`.child("invoiceTable", TableView(...))` gives `invoiceTable`, not `layout/body/main/invoiceTable`), so the URL stays readable and survives the layout being reorganised. That is what lets one page carry several table views, nested or not, each with form pages of its own. The key does not depend on the page declaring a `permission.id` of its own. A table view instance belongs to the first page that mounts it: its form routes cannot be keyed on a second page mounting the same instance in page mode, which is an error, so give each page its own `TableView(...)`.
|
|
479
|
-
|
|
480
|
-
`pages` (`{ new?, edit?, details? }`) titles the forms in every container: a `FormContainerPageTexts` entry is `{ displayName?, description? }` (`$`-prefixed: i18n keys), falling back to "New entry", "Edit entry" and "Entry details". In page mode an entry is a `FormContainerPageConfig`, which also overrides the sub-page:
|
|
481
|
-
|
|
482
|
-
| Option | Type | Description |
|
|
483
|
-
| --- | --- | --- |
|
|
484
|
-
| `urlSlug` | `string` | Slug of the sub-page, relative to the page — **no key segment is inserted**, so a declared slug is full control over the URL. The `edit` and `details` slugs must contain `:id`, otherwise `TableView` throws. A slug starting with `/` addresses a page of its own; leading `..` segments each drop one segment of the carrying page. |
|
|
485
|
-
| `displayName` | `string` | Form and sub-page title. |
|
|
486
|
-
| `description` | `string` | Form and sub-page description. |
|
|
487
|
-
| `customPage` | `boolean` | Do not auto-create the sub-page — you registered your own page at that URL. The slug is still used for navigation. |
|
|
488
|
-
|
|
489
|
-
Whichever slugs apply, the builder resolves them against the slug of the page
|
|
490
|
-
carrying the table view and serializes the three URLs as `formPages`
|
|
491
|
-
(`{ new, edit, view }`), `:id` placeholder included. The frontend navigates to
|
|
492
|
-
those URLs as they stand — substituting the row id, and the current route
|
|
493
|
-
params for the placeholders the carrying page contributes — rather than
|
|
494
|
-
rebuilding them from the browser path. A `customPage` entry registers no
|
|
495
|
-
sub-page and still gets its URL serialized: it is navigated to all the same.
|
|
496
|
-
|
|
497
|
-
A page below a `:id` page therefore carries two `:id` placeholders
|
|
498
|
-
(`/workspaces/:id/invoiceTable/:id/view`). The row id takes the **last** one
|
|
499
|
-
and the route params of the carrying page fill the rest, so the form reads the
|
|
500
|
-
row id from `params.id` — the same thing it means on a flat `/customers/:id/edit`,
|
|
501
|
-
which is what the generated `fetchUrl` and `submitUrl` count on.
|
|
502
|
-
|
|
503
|
-
The id of the page above is not lost to the repetition: when a route repeats a
|
|
504
|
-
placeholder name, every occurrence is also exposed under `<name>:<n>`, numbered
|
|
505
|
-
from 1 in the order they appear in the pattern. On
|
|
506
|
-
`/workspaces/:id/invoiceTable/:id/view` a form reads `{{params.id}}` or
|
|
507
|
-
`{{params.id:2}}` for the row and `{{params.id:1}}` for the workspace carrying
|
|
508
|
-
it. A name that occurs once gets no indexed key — `{{params.id}}` is all there
|
|
509
|
-
is to read on every single-placeholder route.
|
|
510
|
-
|
|
511
|
-
Two page-mode table views on one page whose form URLs resolve to the same path
|
|
512
|
-
— the same explicit `urlSlug`, or the same key under two different parents —
|
|
513
|
-
**fail the registration** of the page, naming both table views and the path.
|
|
514
|
-
The check covers the page's own components and the table views a page extension
|
|
515
|
-
contributes later. Give one of them a `urlSlug` of its own to resolve it.
|
|
516
|
-
|
|
517
|
-
## Exports
|
|
518
|
-
|
|
519
|
-
The export action is offered only when the controller registered all three `ExportRoutes`. Pressing it starts a background export job, polls its status, and downloads the result; the job machinery itself is shared with any other long export and is documented in [Helpers](/docs/interfaces/base/helpers). Which columns land in the file is decided by `@Exported`.
|
|
520
|
-
|
|
521
|
-
## Realtime
|
|
522
|
-
|
|
523
|
-
Unless `realtime: false` is set, a table view registers two realtime topics for its page (and for each generated sub-page), and its routes publish every create, update and delete on the first and track who has a row open on the second. Both are named after the controller's location:
|
|
524
|
-
|
|
525
|
-
```typescript
|
|
526
|
-
import {
|
|
527
|
-
tableViewPresenceTopic,
|
|
528
|
-
tableViewRowTopic,
|
|
529
|
-
} from "@antelopejs/interface-dms/base/table-view";
|
|
530
|
-
|
|
531
|
-
tableViewRowTopic("/api/orders"); // "tableview:row:/api/orders"
|
|
532
|
-
tableViewPresenceTopic("/api/orders"); // "tableview:presence:/api/orders"
|
|
533
|
-
```
|
|
534
|
-
|
|
535
|
-
The topics are registered with `RegisterPageTopic` from the table view's `onCreated`, so they belong to the module whose page it is, as described in [Realtime](/docs/interfaces/core/realtime). A module that publishes on a table's row topic itself — a background job updating rows, for instance — builds the name with `tableViewRowTopic` rather than spelling out the prefix, and passes the tenant of the rows as `tenantId`.
|
|
536
|
-
|
|
537
|
-
The topics are the same for every tenant, but what is published on them is not: the routes publish each change, and each presence, for the tenant the request was made in, so the sessions of the other tenants neither refetch nor see who has a row open. The DMS decides who else receives them, with no option on the table: a table whose rows live in the schema every tenant shares (`CORE_SCHEMA_NAME`) publishes to every tenant, and a session on a platform page (a module's page) receives the changes of every tenant, as such a page lists the data of all of them. `RealtimeMutationContext` and `RealtimePresenceContext` carry the tenant as `tenantId`, absent on a table of the shared schema.
|
|
538
|
-
|
|
539
|
-
Two functions let other modules observe those mutations:
|
|
540
|
-
|
|
541
|
-
```typescript
|
|
542
|
-
import {
|
|
543
|
-
registerRealtimeMutationListener,
|
|
544
|
-
unregisterRealtimeMutationListener,
|
|
545
|
-
type RealtimeMutationContext,
|
|
546
|
-
} from "@antelopejs/interface-dms/base/table-view";
|
|
547
|
-
|
|
548
|
-
const listener = (context: RealtimeMutationContext) => {
|
|
549
|
-
// context.controllerLocation, context.eventType, context.ids, …
|
|
550
|
-
};
|
|
551
|
-
|
|
552
|
-
registerRealtimeMutationListener(listener);
|
|
553
|
-
```
|
|
554
|
-
|
|
555
|
-
Listeners are additive side observers: they run after the DMS has published the mutation, and a listener that throws is logged, never propagated to the mutating request. A listener is released with the module that registered it, including when that module reloads in development; `unregisterRealtimeMutationListener` is for a module that stops listening while it stays loaded.
|
|
556
|
-
|
|
557
|
-
| Type | Shape |
|
|
558
|
-
| --- | --- |
|
|
559
|
-
| `RealtimeMutationEventType` | `"created" \| "updated" \| "deleted"` |
|
|
560
|
-
| `RealtimeMutationContext` | `controllerLocation`, `rowIdKey`, `eventType`, `ids: string[]`, `sessionId?`, `actor?: RealtimePresenceActor` |
|
|
561
|
-
| `RealtimePresenceActor` | `id`, `displayName?`, `avatarUrl?` |
|
|
562
|
-
| `RealtimePresenceContext` | `controllerLocation`, `rowIdKey`, `sessionId`, `rowId`, `actor` |
|
|
563
|
-
|
|
564
|
-
The routes reach the DMS through two interface functions it implements, `internal.PublishMutation` and `internal.AcquirePresence`. They are internal: nothing outside the DMS implements them, and the core binds them to each generation of the DMS module, so a mutation made while the DMS reloads waits for the next generation instead of reaching the one being torn down.
|
|
565
|
-
|
|
566
|
-
## Metadata
|
|
567
|
-
|
|
568
|
-
`TableViewMeta` is the metadata object the decorators write to and the builder reads from. Retrieve it with `GetMetadata(controller, TableViewMeta)`.
|
|
569
|
-
|
|
570
|
-
| Member | Description |
|
|
571
|
-
| --- | --- |
|
|
572
|
-
| `columns` | `Record<string, ColumnOptions>` keyed by field name. |
|
|
573
|
-
| `groups` | `Record<string, ColumnGroupConfig>` keyed by group id. |
|
|
574
|
-
| `options` | The `TableViewOptions` of the writing `TableView`, or of the last one built while none writes. |
|
|
575
|
-
| `archiveField` | Field name recorded by `@ArchiveField`. |
|
|
576
|
-
| `controllerRowActionRules` | The rules the write routes enforce: the writing table view's `rowActions`, archive-mode defaults included. |
|
|
577
|
-
| `controllerGuards` | Guards promoted to controller-wide guards. |
|
|
578
|
-
| `componentBuilders` | Every live `TableView` builder built on the controller, in build order. |
|
|
579
|
-
| `writingComponentBuilders` | The live builders of `componentBuilders` that write through the data routes. |
|
|
580
|
-
| `actionPermissionIds(actionId)` | The permission id the action carries on each mounted table view of the controller — on the writing one only for a write action. |
|
|
581
|
-
| `addComponentBuilder(builder, access?)` | Writer the builder calls to record itself, with its `TableViewAccess` (`"read"` or `"write"`, the default). A custom component standing in for a table view (the roles editor) records itself the same way. |
|
|
582
|
-
| `resourceFormBuilders` | Every live `new` / `edit` form built over the controller, in build order: the `ResourceForm` blocks and the forms each table view opens. |
|
|
583
|
-
| `addResourceFormBuilder` | Writer the form builder calls to record a form that submits to the controller. |
|
|
584
|
-
| `writingComponents` | Every live component that submits to the controller's write routes: its writing table view and its `new` / `edit` forms, including the ones a page-mode table view mounts on its form sub-pages. A native file staged by any of them is accepted on save; one staged by any other component is refused. |
|
|
585
|
-
| `inherit(parent)` | Called by `GetMetadata` for a derived controller: copies the parent's `columns` (each with its own copy of its data type), `groups`, `options` and `archiveField`, never its table views or forms. |
|
|
586
|
-
| `config` | Getter returning `{ location, columns }`, the serialized column list plus the controller's route location. |
|
|
587
|
-
| `getFormFields(mode)` | Builds the `FormFieldOrGroup[]` for `"view"`, `"edit"` or `"new"`. |
|
|
588
|
-
| `setColumn`, `setGroup`, `setOptions`, `setArchiveField`, `setControllerRowActionRules`, `setControllerGuards` | Writers used by the decorators and the builder. |
|
|
589
|
-
|
|
590
|
-
Each entry of `config.columns` carries `id`, `header`, `isVisible`, `type` (the serialized data type), `accessorKey`, `listable`, `enableSorting`, `enableColumnFilter`, `defaultValue`, `accessMode` and `readonlyBehavior`.
|
|
591
|
-
|
|
592
|
-
## Route Helpers
|
|
593
|
-
|
|
594
|
-
`table-view` also exports the interface functions the wrapped routes call. They are the seam between the interface and its implementation — a consumer wires them by declaring `TableViewRoutes`, and does not normally call them directly.
|
|
595
|
-
|
|
596
|
-
| Function | Role |
|
|
597
|
-
| --- | --- |
|
|
598
|
-
| `listWithSearch` | Runs the list query with search, filters, sorting and pagination; returns `{ results, total, offset, limit }`. |
|
|
599
|
-
| `countWithSearch` | Same query, count only; returns `{ total }`. |
|
|
600
|
-
| `startExport` | Queues an export job; returns `{ jobId, format, extension }`. |
|
|
601
|
-
| `getExportStatus` | Returns `{ status, progress, error? }` for a job. |
|
|
602
|
-
| `downloadExport` | Streams a completed job's file to the response. |
|
|
603
|
-
| `archiveRows` | Sets the archive field on the given ids; returns `{ success, archivedCount }`. |
|
|
604
|
-
| `restoreRows` | Clears it; returns `{ success, restoredCount }`. |
|
|
605
|
-
| `validateRowsAgainstRule` | Splits ids into `{ eligibleIds, rejectedIds }` against a `RowActionRule`. |
|
|
606
|
-
| `fetchRowForGuard` | Loads one row by id field, for the `edit` guard and the file-cleanup passes. |
|
|
607
|
-
|
|
608
|
-
## Events and Functions
|
|
609
|
-
|
|
610
|
-
| Namespace member | Value |
|
|
611
|
-
| --- | --- |
|
|
612
|
-
| `TableViewEvents.ROW_CLICK` | `"DmsComponent.TableView.RowClick"` |
|
|
613
|
-
| `TableViewEvents.ROW_SELECT` | `"DmsComponent.TableView.RowSelect"` |
|
|
614
|
-
| `TableViewEvents.ROW_DELETE` | `"DmsComponent.TableView.RowDelete"` |
|
|
615
|
-
| `TableViewEvents.ROW_ADD` | `"DmsComponent.TableView.RowAdd"` |
|
|
616
|
-
| `TableViewEvents.ROW_EDIT` | `"DmsComponent.TableView.RowEdit"` |
|
|
617
|
-
| `TableViewEvents.FILTER_CHANGE` | `"DmsComponent.TableView.FilterChange"` |
|
|
618
|
-
| `TableViewEvents.SORT_CHANGE` | `"DmsComponent.TableView.SortChange"` |
|
|
619
|
-
| `TableViewEvents.EXPORT` | `"DmsComponent.TableView.Export"` |
|
|
620
|
-
| `TableViewFunctions.CUSTOM_PAGE_FORM_SUCCESS` | `"DmsComponent.TableView.CustomPageFormSuccess"` |
|
|
621
|
-
|
|
622
|
-
`CUSTOM_PAGE_FORM_SUCCESS` registers `{ url: string; preserveQuery?: string[] }` in `WatchFunctionParamMap`. The builder uses it itself: the generated `new` and `edit` page forms watch `FormEvents.SUBMIT_SUCCESS` and navigate back to the table, preserving the query parameters named in `queryParamFilters`. The handler resolves `{{params.X}}` / `{{query.X}}` tokens in `url` against the route being left before navigating; the generated forms rely on it to send the user back to a page whose slug carries parameters: each placeholder of the page's slug becomes the token reading it on the form route (`/workspaces/:id` becomes `/workspaces/{{params.id:1}}` from `/workspaces/:id/invoiceTable/:id/edit`, `/workspaces/{{params.id}}` from `/workspaces/:id/invoiceTable/new`). A slug without parameters is used as is. The `view` form submits nothing and carries no such watch. All three generated pages are registered with `FormPageLayout()`, the constrained-column layout ([Layout and Containers](/docs/interfaces/base/layout)).
|
|
623
|
-
|
|
624
|
-
## Next Steps
|
|
287
|
+
## Next steps
|
|
625
288
|
|
|
626
|
-
- [
|
|
289
|
+
- [Table sources and tenant gates](/docs/interfaces/base/table-sources-and-gates): Table views over a module's route, and behind a tenant gate.
|
|
290
|
+
- [Table row actions](/docs/interfaces/base/table-row-actions): Row actions, archive mode and guards.
|
|
291
|
+
- [Table filters and forms](/docs/interfaces/base/table-filters-and-forms): Filters, tabs, displays and generated forms.
|
|
292
|
+
- [Table events and helpers](/docs/interfaces/base/table-events-and-helpers): Exports, realtime, metadata, route helpers and events.
|
|
293
|
+
- [Charts and widgets](/docs/interfaces/base/charts-and-widgets): Charts, KPI cards, top lists and the period selector.
|