@antelopejs/interface-dms 0.7.3 → 0.8.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 +129 -0
- package/dist/auth/index.d.ts +3 -3
- package/dist/auth/index.js +11 -9
- package/dist/auth/index.js.map +1 -1
- package/dist/auth/internal/jwt-auth-decorator.d.ts +20 -0
- package/dist/auth/internal/jwt-auth-decorator.js +51 -0
- package/dist/auth/internal/jwt-auth-decorator.js.map +1 -0
- package/dist/auth/internal/request-authenticators.d.ts +12 -0
- package/dist/auth/internal/request-authenticators.js +22 -0
- package/dist/auth/internal/request-authenticators.js.map +1 -1
- package/dist/base/activity-feed.d.ts +6 -6
- package/dist/base/block-registry/types.d.ts +16 -0
- package/dist/base/block-types.d.ts +1 -0
- package/dist/base/block-types.js +1 -0
- package/dist/base/block-types.js.map +1 -1
- package/dist/base/chart-card.d.ts +15 -0
- package/dist/base/chart-card.js +4 -0
- package/dist/base/chart-card.js.map +1 -1
- package/dist/base/chart-schemas.d.ts +26 -26
- package/dist/base/code-block.d.ts +3 -3
- package/dist/base/data-types/compare-types.js +19 -13
- package/dist/base/data-types/compare-types.js.map +1 -1
- package/dist/base/data-types/core.d.ts +11 -0
- package/dist/base/data-types/core.js +14 -1
- package/dist/base/data-types/core.js.map +1 -1
- package/dist/base/data-types/default-types.d.ts +112 -2
- package/dist/base/data-types/default-types.js +90 -19
- package/dist/base/data-types/default-types.js.map +1 -1
- package/dist/base/data-types/field-types.d.ts +10 -4
- package/dist/base/data-types/field-types.js +12 -2
- package/dist/base/data-types/field-types.js.map +1 -1
- package/dist/base/data-types/internal/compare-types.d.ts +3 -0
- package/dist/base/data-types/internal/compare-types.js +7 -0
- package/dist/base/data-types/internal/compare-types.js.map +1 -1
- package/dist/base/empty-state.d.ts +4 -4
- package/dist/base/form-block-schema.d.ts +59 -3
- package/dist/base/form-block-schema.js +12 -2
- package/dist/base/form-block-schema.js.map +1 -1
- package/dist/base/form-schema.d.ts +46 -1
- package/dist/base/form-schema.js.map +1 -1
- package/dist/base/form-types.d.ts +56 -13
- package/dist/base/form-types.js +12 -4
- package/dist/base/form-types.js.map +1 -1
- package/dist/base/guard.d.ts +6 -6
- package/dist/base/helpers/archive-mode-helpers.js +6 -2
- package/dist/base/helpers/archive-mode-helpers.js.map +1 -1
- package/dist/base/index.d.ts +1 -0
- package/dist/base/index.js +1 -0
- package/dist/base/index.js.map +1 -1
- package/dist/base/internal/form-schema.js +2 -0
- package/dist/base/internal/form-schema.js.map +1 -1
- package/dist/base/internal/layouts.d.ts +4 -0
- package/dist/base/key-value-list.d.ts +12 -9
- package/dist/base/key-value-list.js +1 -1
- package/dist/base/key-value-list.js.map +1 -1
- package/dist/base/kpi-card.d.ts +39 -5
- package/dist/base/kpi-card.js +18 -6
- package/dist/base/kpi-card.js.map +1 -1
- package/dist/base/layouts.d.ts +24 -0
- package/dist/base/layouts.js.map +1 -1
- package/dist/base/live-toggle.d.ts +11 -0
- package/dist/base/live-toggle.js +8 -1
- package/dist/base/live-toggle.js.map +1 -1
- package/dist/base/page-toolbar.d.ts +158 -0
- package/dist/base/page-toolbar.js +198 -0
- package/dist/base/page-toolbar.js.map +1 -0
- package/dist/base/period-selector.d.ts +9 -2
- package/dist/base/period-selector.js.map +1 -1
- package/dist/base/placeholder.d.ts +2 -2
- package/dist/base/section.d.ts +2 -2
- package/dist/base/stack.d.ts +2 -2
- package/dist/base/stat-group.d.ts +18 -6
- package/dist/base/stat-group.js +9 -0
- package/dist/base/stat-group.js.map +1 -1
- package/dist/base/table-view/column-display.d.ts +9 -5
- package/dist/base/table-view/column-display.js +1 -1
- package/dist/base/table-view/column-display.js.map +1 -1
- package/dist/base/table-view/data-functions.d.ts +26 -7
- package/dist/base/table-view/data-functions.js +10 -6
- package/dist/base/table-view/data-functions.js.map +1 -1
- package/dist/base/table-view/factory.js +24 -13
- package/dist/base/table-view/factory.js.map +1 -1
- package/dist/base/table-view/internal/auth.d.ts +2 -0
- package/dist/base/table-view/internal/auth.js +3 -1
- package/dist/base/table-view/internal/auth.js.map +1 -1
- package/dist/base/table-view/internal/factory-helpers.d.ts +5 -0
- package/dist/base/table-view/internal/factory-helpers.js +55 -11
- package/dist/base/table-view/internal/factory-helpers.js.map +1 -1
- package/dist/base/table-view/internal/guards.js +19 -2
- package/dist/base/table-view/internal/guards.js.map +1 -1
- package/dist/base/table-view/internal/realtime.js +17 -0
- package/dist/base/table-view/internal/realtime.js.map +1 -1
- package/dist/base/table-view/internal/resource-form.js +1 -0
- package/dist/base/table-view/internal/resource-form.js.map +1 -1
- package/dist/base/table-view/internal/row-actions-schema.d.ts +296 -0
- package/dist/base/table-view/internal/row-actions-schema.js +62 -0
- package/dist/base/table-view/internal/row-actions-schema.js.map +1 -0
- package/dist/base/table-view/internal/validation.d.ts +15 -3
- package/dist/base/table-view/internal/validation.js +70 -2
- package/dist/base/table-view/internal/validation.js.map +1 -1
- package/dist/base/table-view/meta.d.ts +8 -0
- package/dist/base/table-view/meta.js +25 -3
- package/dist/base/table-view/meta.js.map +1 -1
- package/dist/base/table-view/options.d.ts +90 -16
- package/dist/base/table-view/options.js.map +1 -1
- package/dist/base/table-view/realtime.d.ts +8 -0
- package/dist/base/table-view/realtime.js.map +1 -1
- package/dist/base/table-view/routes.js +5 -2
- package/dist/base/table-view/routes.js.map +1 -1
- package/dist/base/table-view/schema.d.ts +36 -30
- package/dist/base/table-view/schema.js +11 -57
- package/dist/base/table-view/schema.js.map +1 -1
- package/dist/base/table-view/source.d.ts +1 -1
- 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 +2 -2
- package/dist/base/table-view.js.map +1 -1
- package/dist/base/top-list-card.d.ts +5 -5
- package/dist/base/types/action-target.d.ts +77 -20
- package/dist/base/types/confirm-dialog.d.ts +5 -1
- package/dist/base/types/custom-button.d.ts +5 -3
- package/dist/base/types/field-config.d.ts +4 -4
- package/dist/base/types/guards.d.ts +10 -0
- package/dist/base/types/row-action.d.ts +6 -1
- package/dist/base/types/size.d.ts +3 -1
- package/dist/client-ip.d.ts +6 -2
- package/dist/client-ip.js +4 -2
- package/dist/client-ip.js.map +1 -1
- package/dist/component.d.ts +8 -0
- package/dist/component.js.map +1 -1
- package/dist/data-controllers/members.d.ts +2 -5
- package/dist/data-controllers/members.js +38 -9
- package/dist/data-controllers/members.js.map +1 -1
- package/dist/db/internal/tenantLifecycle.model.d.ts +4 -0
- package/dist/db/internal/tenantLifecycle.model.js +26 -0
- package/dist/db/internal/tenantLifecycle.model.js.map +1 -1
- package/dist/db/internal/tenantLifecycle.table.d.ts +1 -0
- package/dist/db/internal/tenantLifecycle.table.js +4 -0
- package/dist/db/internal/tenantLifecycle.table.js.map +1 -1
- package/dist/guards.js +2 -3
- package/dist/guards.js.map +1 -1
- package/dist/hooks.d.ts +47 -0
- package/dist/hooks.js +6 -0
- package/dist/hooks.js.map +1 -1
- package/dist/index.d.ts +3 -3
- package/dist/index.js +3 -2
- package/dist/index.js.map +1 -1
- package/dist/internal/invite-membership.d.ts +9 -0
- package/dist/internal/invite-membership.js +25 -0
- package/dist/internal/invite-membership.js.map +1 -1
- package/dist/internal/member-changes.d.ts +24 -0
- package/dist/internal/member-changes.js +66 -0
- package/dist/internal/member-changes.js.map +1 -0
- package/dist/internal/tenant-ownership.d.ts +4 -1
- package/dist/internal/tenant-ownership.js +4 -1
- package/dist/internal/tenant-ownership.js.map +1 -1
- package/dist/invite-extensions.d.ts +0 -9
- package/dist/invite-extensions.js +1 -36
- package/dist/invite-extensions.js.map +1 -1
- package/dist/invite-resolution.js +6 -2
- package/dist/invite-resolution.js.map +1 -1
- package/dist/invites.js +36 -22
- package/dist/invites.js.map +1 -1
- package/dist/layout-banners.d.ts +40 -4
- package/dist/layout-banners.js +25 -3
- package/dist/layout-banners.js.map +1 -1
- package/dist/notifications/internal.d.ts +2 -2
- package/dist/notifications/internal.js.map +1 -1
- package/dist/notifications/sendable.d.ts +3 -2
- package/dist/notifications/sendable.js +2 -1
- package/dist/notifications/sendable.js.map +1 -1
- package/dist/notifications/types.d.ts +9 -0
- package/dist/page/controllers.d.ts +44 -0
- package/dist/page/controllers.js +14 -1
- package/dist/page/controllers.js.map +1 -1
- package/dist/page/internal/layout-filter.d.ts +2 -1
- package/dist/page/internal/layout-filter.js +3 -1
- package/dist/page/internal/layout-filter.js.map +1 -1
- package/dist/page/internal/metadata-helpers.d.ts +7 -0
- package/dist/page/internal/metadata-helpers.js +13 -2
- package/dist/page/internal/metadata-helpers.js.map +1 -1
- package/dist/page/internal/page-registration.d.ts +2 -0
- package/dist/page/internal/page-registration.js.map +1 -1
- package/dist/page/layout-filter.js +31 -10
- package/dist/page/layout-filter.js.map +1 -1
- package/dist/page/metadata.js +29 -24
- package/dist/page/metadata.js.map +1 -1
- package/dist/page/types.d.ts +39 -4
- package/dist/page/types.js.map +1 -1
- package/dist/page.d.ts +1 -1
- package/dist/page.js +2 -1
- package/dist/page.js.map +1 -1
- package/dist/permissions-resolver.d.ts +26 -3
- package/dist/permissions-resolver.js +2 -1
- package/dist/permissions-resolver.js.map +1 -1
- package/dist/permissions.d.ts +7 -3
- package/dist/permissions.js +8 -6
- package/dist/permissions.js.map +1 -1
- package/dist/realtime.d.ts +24 -1
- package/dist/realtime.js +7 -1
- package/dist/realtime.js.map +1 -1
- package/dist/request-tenant.d.ts +17 -13
- package/dist/request-tenant.js +54 -42
- package/dist/request-tenant.js.map +1 -1
- package/dist/tenant-access.d.ts +15 -0
- package/dist/tenant-access.js +11 -1
- package/dist/tenant-access.js.map +1 -1
- package/dist/tenant-lifecycle.d.ts +27 -0
- package/dist/tenant-lifecycle.js +35 -0
- package/dist/tenant-lifecycle.js.map +1 -1
- package/dist/tenant-members.d.ts +26 -0
- package/dist/tenant-members.js +29 -0
- package/dist/tenant-members.js.map +1 -0
- package/dist/tenant-ownership.d.ts +4 -1
- package/dist/tenant-ownership.js +15 -3
- package/dist/tenant-ownership.js.map +1 -1
- package/dist/tenant-scoped-model.d.ts +2 -1
- package/dist/tenant-scoped-model.js +4 -3
- package/dist/tenant-scoped-model.js.map +1 -1
- package/docs/1.core/10.layout-banners.md +57 -2
- package/docs/1.core/2.pages-and-modules.md +20 -11
- package/docs/1.core/4.permissions.md +34 -1
- package/docs/1.core/5.tenancy.md +51 -5
- package/docs/1.core/7.hooks.md +13 -0
- package/docs/1.core/8.realtime.md +24 -3
- package/docs/2.auth/2.authentication.md +3 -3
- package/docs/3.base/1.introduction.md +1 -0
- package/docs/3.base/10.display-blocks.md +20 -2
- package/docs/3.base/2.types.md +7 -5
- package/docs/3.base/3.forms.md +21 -6
- package/docs/3.base/4.tables.md +14 -9
- package/docs/3.base/5.charts-and-widgets.md +8 -4
- package/docs/3.base/6.layout.md +38 -1
- package/docs/3.base/8.data-types.md +14 -7
- package/docs/4.notifications/3.sending.md +4 -3
- package/docs/5.html-render/1.introduction.md +5 -7
- package/package.json +3 -7
|
@@ -313,10 +313,11 @@ const card = ChartCard({
|
|
|
313
313
|
| `currencyCode` | `string` | Currency for `valueFormat: "currency"`. |
|
|
314
314
|
| `valuePrecision` | `ValuePrecision` | Fixed fraction digits or `"native"` Intl defaults; omitted keeps legacy formatting. Also applies to the previous value and nested chart's value axis and tooltip. |
|
|
315
315
|
| `showDelta` | `boolean` | Show the change against the comparison period. |
|
|
316
|
+
| `invert` | `boolean` | Treat an increase as bad — for metrics where lower is better. |
|
|
316
317
|
| `showLegend` | `boolean` | Show the primary / comparison legend. Defaults to `true`. |
|
|
317
318
|
| `showSeriesLegend` | `boolean` | Show the nested chart's legend, naming each series. Defaults to `false`. |
|
|
318
319
|
| `primaryLabel` | `string` | Label of the primary series. |
|
|
319
|
-
| `comparisonLabel` | `string` | Label of the comparison series. |
|
|
320
|
+
| `comparisonLabel` | `string` | Label of the comparison series. Without it, a card with a `periodScope` names the selected comparison ("Previous year"). |
|
|
320
321
|
|
|
321
322
|
## KpiCard
|
|
322
323
|
|
|
@@ -345,17 +346,20 @@ const denied = KpiCard({
|
|
|
345
346
|
| `icon` | `string` | Card icon. Defaults to `"i-ph-trend-up"` for the component metadata. |
|
|
346
347
|
| `fetchUrl` | `string` | URL the card loads its value from. |
|
|
347
348
|
| `fetchUrlMethod` | `HttpMethod` | Method for `fetchUrl`. |
|
|
348
|
-
| `periodScope` | `string` | Id of the `PeriodSelector` to follow. |
|
|
349
|
+
| `periodScope` | `string` | Id of the `PeriodSelector` to follow. Its comparison names the change; "No comparison" hides the change, the comparison value and the label. |
|
|
350
|
+
| `realtimeTopic` | `string \| string[]` | Topics whose events make the card read `fetchUrl` again, through the page's `live` gate. The builder registers them on the page. |
|
|
349
351
|
| `valueFormat` | `ValueFormat` | How the value is formatted. |
|
|
350
352
|
| `currencyCode` | `string` | Currency for `valueFormat: "currency"`. |
|
|
351
353
|
| `valuePrecision` | `ValuePrecision` | Fixed fraction digits or `"native"` Intl defaults; omitted keeps legacy formatting. |
|
|
352
354
|
| `showDelta` | `boolean` | Show the change against the comparison period. |
|
|
355
|
+
| `showPreviousValue` | `boolean` | Show the route's `previousValue` next to the change ("vs €42,900"), formatted as the value. Defaults to `true`. |
|
|
353
356
|
| `showSparkline` | `boolean` | Show the sparkline. |
|
|
354
357
|
| `sparklineAccent` | `ChartColorValue \| "auto"` | Sparkline colour. |
|
|
355
358
|
| `invert` | `boolean` | Treat an increase as bad — for metrics where lower is better. |
|
|
356
|
-
| `compareLabel` | `string` | Label next to the delta. |
|
|
359
|
+
| `compareLabel` | `string` | Label next to the delta. Without it (and without `description`), a card with a `periodScope` names the selected comparison ("vs previous year"). Shown with the delta only: with no comparison, the `description` shows instead. |
|
|
357
360
|
| `staticValue` | `number` | Inline value, as an alternative to `fetchUrl`. |
|
|
358
361
|
| `staticDelta` | `number` | Inline delta, in percent (`12.5` reads +12.5 %). |
|
|
362
|
+
| `staticPreviousValue` | `number` | Inline value compared against, as a fetched `previousValue`. |
|
|
359
363
|
| `staticSparkline` | `number[]` | Inline sparkline points. |
|
|
360
364
|
|
|
361
365
|
## TopListCard
|
|
@@ -450,7 +454,7 @@ const selector = PeriodSelector({
|
|
|
450
454
|
| `size` | `"xs" \| "sm" \| "md" \| "lg" \| "xl"` | Control size. |
|
|
451
455
|
| `showRangeLabel` | `boolean` | Show the resolved date range as text. |
|
|
452
456
|
| `rangeLabel` | `PeriodRangeLabel` | `"absolute"` (default): both bounds. `"relative"`: the start of a window reaching now, "since yesterday 11:52". |
|
|
453
|
-
| `comparisonControl` | `PeriodComparisonControl` | `"menu"`, `"toggle"` (one inline button switching a comparison on and off) or `"hidden"` (nothing drawn, the comparison kept on). Defaults to `"toggle"` when `comparisons` offers a single comparison besides `"none"`, `"menu"` otherwise. |
|
|
457
|
+
| `comparisonControl` | `PeriodComparisonControl` | `"menu"`, `"toggle"` (one inline button switching a comparison on and off) or `"hidden"` (nothing drawn, the comparison kept on). Defaults to `"toggle"` when `comparisons` offers a single comparison besides `"none"`, `"menu"` otherwise. The menu's trigger names the compared window ("vs 16 – 17 Oct", the option's name in its tooltip and its list), so the range label beside it keeps the current window only. |
|
|
454
458
|
| `rememberKey` | `string` | Selectors carrying the same key share the remembered last choice, on any page. |
|
|
455
459
|
| `variant` | `PeriodSelectorVariant` | `"default"` or `"segmented"`. |
|
|
456
460
|
| `maxDays` | `number` | The longest custom range, in days (both ends included), the user can pick in the calendar or the date fields. Presets and a range given by the URL are not limited. |
|
package/docs/3.base/6.layout.md
CHANGED
|
@@ -195,6 +195,7 @@ export class OverviewPage extends PageController(
|
|
|
195
195
|
| `hideHeader` | `boolean` | Omit the page header. |
|
|
196
196
|
| `fillHeight` | `boolean` | Let the page fill the height of the panel instead of growing with its content, for a tool that scrolls inside itself — see [Pages that fill the panel](#pages-that-fill-the-panel). Defaults to `false`. |
|
|
197
197
|
| `headerActions` | `CustomButton[]` | Buttons right of the page title, in order — see [Header actions](#header-actions). |
|
|
198
|
+
| `record` | `PageRecordSource` | The record the page shows (`{ fetchUrl }`), which header buttons act on — see [Buttons acting on the page's record](#buttons-acting-on-the-pages-record). |
|
|
198
199
|
|
|
199
200
|
Dashboard pages are full-width unless they opt out, which is what a table, a grid of cards or a chart wants. Set `fullWidth: false` for a page that only holds a form, where a constrained column stays readable — or use `FormPageLayout`, which is exactly that:
|
|
200
201
|
|
|
@@ -275,7 +276,7 @@ A page scoped by its URL — `?workspace=web`, a record id in the path — decla
|
|
|
275
276
|
|
|
276
277
|
A label's translation takes the page's route and query parameters as its own (a query parameter wins over a route parameter of the same name), so `"Download {locale}.json"` names the language of `?locale=fr` and follows it as the URL changes.
|
|
277
278
|
|
|
278
|
-
An `availability` receives the page URL's query as `context.query` (one value per name; caller-supplied, so check it like any input). The header asks again whenever the query
|
|
279
|
+
An `availability` receives the page URL's query as `context.query` (one value per name; caller-supplied, so check it like any input) and its route parameters as `context.params` (`/projects/project/p1` on a page registered at `/projects/project/:id` is `{ id: "p1" }`). The header asks again whenever the query or the route parameters change — only when one of its buttons declares an `availability` — and keeps the buttons it has until the answer lands. The keys the DMS writes for the view itself never cause a request: `record` (the row open in a side panel), `live` (the paused live mode) and a period selector's `<id>.period`, `<id>.compare`, `<id>.from`, `<id>.to`, `<id>.compareFrom` and `<id>.compareTo`. A query key may carry dots, so `{{query.api.period}}` reads a period selector's choice.
|
|
279
280
|
|
|
280
281
|
```typescript
|
|
281
282
|
DefaultLayout({
|
|
@@ -301,6 +302,26 @@ A table view of the page places its own buttons in the header with `placement: "
|
|
|
301
302
|
|
|
302
303
|
Who sees a button is settled on the server when the layout is served: a button whose `permission` or `permissionId` the user lacks is left out of the layout, and a quick-action button only shows to the users the quick action is served to — that is, the users its page admits. A page drawn by a Vue page component can add its own buttons next to these with `usePageHeaderActions`; call it before the first `await` of the component's setup, so the server renders the buttons with the page and they never pop in. A page whose changes save as soon as they are made shows the shared **Saved instantly** pill there with `useInstantSaveHeader` ([Instant save or a save bar](/docs/components/lists-and-settings/section#instant-save-or-a-save-bar)).
|
|
303
304
|
|
|
305
|
+
#### Buttons acting on the page's record
|
|
306
|
+
|
|
307
|
+
A page about one record (a site, an invoice) declares it with `record: { fetchUrl }` — typically its controller's `get` route, `{{params.X}}` and `{{query.X}}` filled in from the page URL. The browser reads it for the header, and a button's target names its fields as `{{record.X}}`, percent-encoded: a maps link to the site's address, an API call on its own id. A button acting on the record — one whose target names `{{record.X}}`, or declaring `requiresRecord: true` (an "Edit" link that only needs `{{params.id}}`) — is disabled while the record loads, and left out of a page whose record cannot be read (not found, refused). The other buttons never wait for it:
|
|
308
|
+
|
|
309
|
+
```typescript
|
|
310
|
+
DefaultLayout({
|
|
311
|
+
record: { fetchUrl: "/api/sites/get?id={{params.id}}" },
|
|
312
|
+
headerActions: [
|
|
313
|
+
{
|
|
314
|
+
label: "$sites.directions",
|
|
315
|
+
icon: "i-ph-map-pin",
|
|
316
|
+
target: { type: "external", url: "https://www.google.com/maps/search/?api=1&query={{record.address}}", newTab: true },
|
|
317
|
+
},
|
|
318
|
+
{ label: "$sites.edit", requiresRecord: true, target: { type: "page", url: "/sites/{{params.id}}/edit" } },
|
|
319
|
+
],
|
|
320
|
+
});
|
|
321
|
+
```
|
|
322
|
+
|
|
323
|
+
The record is read like a block's `fetchUrl` (`useApiData`, keyed on the URL): a block of the page reading the same URL — the `ResourceForm` or `KeyValueList` showing the record — shares the one request, and a server render hands it to the browser with the page. Its fields only shape the buttons; the route behind each one still checks the record on its own.
|
|
324
|
+
|
|
304
325
|
#### Hidden or disabled
|
|
305
326
|
|
|
306
327
|
An `availability` has three answers. `undefined` leaves the button as declared, `{ reason }` disables it with the reason in a tooltip — for something the user can change ("No seat left: upgrade the plan") — and `{ hidden: true }` leaves it out of the header, as a missing permission does — for a button that makes no sense in the scope the page shows. Like a disabled one, a hidden button follows the page URL: the header asks again when the query changes, so a button can disappear on one `?workspace=` and come back on another, and the server-rendered header already carries the answer for the URL it was rendered for.
|
|
@@ -320,6 +341,22 @@ DefaultLayout({
|
|
|
320
341
|
});
|
|
321
342
|
```
|
|
322
343
|
|
|
344
|
+
A detail page reads its record from `context.params`, and hides the buttons that act on it when the record does not exist — the page then shows its "not found" state with no "Edit" or "Delete" beside the title:
|
|
345
|
+
|
|
346
|
+
```typescript
|
|
347
|
+
DefaultLayout({
|
|
348
|
+
headerActions: [
|
|
349
|
+
{
|
|
350
|
+
id: "edit",
|
|
351
|
+
label: "$projects.edit",
|
|
352
|
+
target: { type: "page", url: "/projects/project/{{params.id}}/edit" },
|
|
353
|
+
availability: async ({ params }) =>
|
|
354
|
+
(await projectExists(params?.id)) ? undefined : { hidden: true },
|
|
355
|
+
},
|
|
356
|
+
],
|
|
357
|
+
});
|
|
358
|
+
```
|
|
359
|
+
|
|
323
360
|
Hiding only shapes the UI: the route behind the button must still refuse the operation on its own.
|
|
324
361
|
|
|
325
362
|
#### Ordering the header
|
|
@@ -73,6 +73,7 @@ abstract class DataType {
|
|
|
73
73
|
filter(context, proxy, key, value, mode, row): ValueProxyOrValue<boolean>;
|
|
74
74
|
filterComponents(): Record<string, ComponentInfoSerialized> & { default: ComponentInfoSerialized };
|
|
75
75
|
inputComponent(): ComponentInfoSerialized;
|
|
76
|
+
describedInputComponent(): ComponentInfoSerialized;
|
|
76
77
|
decorateField?(target: unknown, key: PropertyKey, descriptor?: PropertyDescriptor): void;
|
|
77
78
|
}
|
|
78
79
|
```
|
|
@@ -85,6 +86,7 @@ abstract class DataType {
|
|
|
85
86
|
| `defaultInputComponent()` | Abstract. Returns the input component the type renders. Implemented by every concrete type. |
|
|
86
87
|
| `getValidation()` | Abstract. Returns the Zod schema for one value of this type, before the field-level adaptation described in [Forms](/docs/interfaces/base/forms). |
|
|
87
88
|
| `inputComponent()` | The form input. Defaults to `defaultInputComponent()`. |
|
|
89
|
+
| `describedInputComponent()` | The input as it is described where nothing is entered through it: a table column's type (its cells, cards and quick filters read the options) and a read-only form field. Defaults to `inputComponent()`; a type leaves out what only an editable control uses, as a relation leaves out its add form. |
|
|
88
90
|
| `filterComponents()` | The filter inputs, keyed by compare-mode id with a mandatory `default`. Defaults to `{ default: defaultInputComponent() }`. |
|
|
89
91
|
| `filter()` | Applies a filter for one compare mode. The base implementation looks the mode up in the compare-mode registry and delegates to it; a type overrides it when a mode needs different semantics. |
|
|
90
92
|
| `decorateField()` | Optional. Called by the `@Column` decorator so the type can add further decorators to the field — this is how the relation types install their join. |
|
|
@@ -207,7 +209,7 @@ function serializeType(instance?: DataType): DataTypeSerialized | undefined;
|
|
|
207
209
|
| `compareModes` | `string[]` | Ids of the type's compare modes; unregistered ones are dropped. |
|
|
208
210
|
| `defaultCompareMode` | `string` | Id of the default mode. |
|
|
209
211
|
| `filterComponents` | `Record<string, FilterComponent>` | Filter inputs by mode id, from `filterComponents()`, with every `noInput` mode's entry replaced by the literal `"noInput"`. |
|
|
210
|
-
| `inputComponent` | `ComponentInfoSerialized` | The
|
|
212
|
+
| `inputComponent` | `ComponentInfoSerialized` | The input the column's cells and quick filters read the options of, from `describedInputComponent()`. |
|
|
211
213
|
|
|
212
214
|
`FilterComponent` is `ComponentInfoSerialized | "noInput"`. The value-level counterpart, `ComponentOrNoInput`, is described in [Shared Types](/docs/interfaces/base/types).
|
|
213
215
|
|
|
@@ -232,7 +234,7 @@ function serializeType(instance?: DataType): DataTypeSerialized | undefined;
|
|
|
232
234
|
| `NotContains` | `not_contains` | value |
|
|
233
235
|
| `ArrayContainsString` | `array_contains_string` | value |
|
|
234
236
|
|
|
235
|
-
`IsEmpty` and `IsNotEmpty` are the two registered with `noInput`. The namespace also exports `escapeRegex(str: string): string`, the helper the substring modes use to make a viewer-supplied value safe inside a pattern.
|
|
237
|
+
`IsEmpty` and `IsNotEmpty` are the two registered with `noInput`. `Include`, `Exclude` and `ArrayContainsString` read a `null` or missing list as an empty one, so a row saved without a value never fails the query: it is left out by `include` and `array_contains_string`, and kept by `exclude`. The namespace also exports `escapeRegex(str: string): string`, the helper the substring modes use to make a viewer-supplied value safe inside a pattern.
|
|
236
238
|
|
|
237
239
|
## The Default Types
|
|
238
240
|
|
|
@@ -264,12 +266,13 @@ function serializeType(instance?: DataType): DataTypeSerialized | undefined;
|
|
|
264
266
|
|
|
265
267
|
| Options interface | Fields |
|
|
266
268
|
| --- | --- |
|
|
267
|
-
| `StringTypeOptions` | `placeholder?`, `maxLength?`, `minLength?`, `showCount?`, `textarea?`, `rows?`, `fallback?` |
|
|
268
|
-
| `NumberTypeOptions` | `min?`, `max?`, `step?`, `placeholder?`, `suffix?`, `fallback?` |
|
|
269
|
+
| `StringTypeOptions` | `placeholder?`, `maxLength?`, `minLength?`, `showCount?`, `textarea?`, `rows?`, `width?`, `fallback?` |
|
|
270
|
+
| `NumberTypeOptions` | `min?`, `max?`, `step?`, `placeholder?`, `suffix?`, `width?`, `fallback?` |
|
|
269
271
|
| `DateTypeOptions` | `range?`, `multiple?`, `minDate?`, `maxDate?`, `fallback?` |
|
|
270
272
|
| `BooleanTypeOptions` | `fallback?` |
|
|
271
273
|
| `SelectTypeOptions` | `items: FormComponents.SelectOption[]`, `placeholder?`, `multiple?`, `deselectable?`, `display?`, `columns?` (cards per row of the `cards` display, 2 by default), `fallback?` |
|
|
272
|
-
| `EmailTypeOptions`, `ColorTypeOptions`, `
|
|
274
|
+
| `EmailTypeOptions`, `ColorTypeOptions`, `RichTextTypeOptions` | `placeholder?`, `fallback?` |
|
|
275
|
+
| `UrlTypeOptions` | `placeholder?`, `preview?` (`"avatar"` \| `"image"`: shown read-only, the URL is drawn as the image it points to; the form still edits it as text), `fallback?` |
|
|
273
276
|
| `PasswordTypeOptions` | `placeholder?`, `minLength?`, `confirmPassword?`, `confirmPlaceholder?`, `fallback?` |
|
|
274
277
|
| `PhoneTypeOptions` | `placeholder?`, `requiredPrefix?`, `fallback?` |
|
|
275
278
|
| `StringTimeTypeOptions` | `placeholder?`, `min?`, `max?`, `fallback?` |
|
|
@@ -279,6 +282,7 @@ Notes on the ones whose behaviour is not obvious from the options:
|
|
|
279
282
|
- `StringType` renders a textarea instead of a single-line input when `textarea` is set, and passes `rows` to it.
|
|
280
283
|
- `StringType` with `showCount` shows the characters typed, out of `maxLength` when set ("4 / 60"), in its input or under its textarea.
|
|
281
284
|
- `NumberType` hands `suffix` (an i18n key with `$`, or a literal) to its input, which writes it after the field; the value stays a number. `PriceType` inherits it; `PercentageType` keeps its own `%`.
|
|
285
|
+
- `NumberType` (and the types extending it) and a single-line `StringType` hand `width` (`FormComponents.InputWidth`) to their input: `"compact"` sizes it for a short value from `sm` up, `"full"` fills the row, and left out the input is compact when `min` / `max` fit in 6 digits or `maxLength` is 16 or less.
|
|
282
286
|
- `DateType` overrides `filter` so `is`, `is_not` and `is_between` compare whole UTC days rather than instants, and adds a range picker as the filter component for `is_between`. Its schema accepts a `Date` or an ISO-shaped string and transforms it to a `Date`; with `multiple` the value becomes an array, and with `range` it becomes `{ start, end }`, `end` on or after `start` (a pair `[start, end]` is still read and validates to the same object).
|
|
283
287
|
- `BooleanType` uses a two-option select as its filter component, so a viewer picks "checked"/"unchecked" rather than typing a value.
|
|
284
288
|
- `SelectType` validates against the `value`s of its `items`, as an enum or an array of that enum when `multiple` is set.
|
|
@@ -317,9 +321,12 @@ declare customerId: string;
|
|
|
317
321
|
| `deselectable` | `boolean` | Allow clearing the selection. |
|
|
318
322
|
| `filterOnly` | `boolean` | Skip the join entirely: the raw scalar id stays in the list output and the funnel filter runs equality on it. |
|
|
319
323
|
| `addForm` | `{ displayName?: string; description?: string }` | Title and description of the drawer the picker's "add" entry opens ("New customer"). `$`-prefixed: i18n keys. Default to "New entry". |
|
|
324
|
+
| `filterBy` | `{ field, from }` | List only the target rows whose `field` equals the form's current value of the field `from`, e.g. `{ field: "customerId", from: "customerId" }` for a site among its customer's sites. `field` must be filterable on the target (`filterable: true` or `@Filter`); `decorateField` throws otherwise. |
|
|
320
325
|
| `fallback` | `string` | Text used in place of an empty value. |
|
|
321
326
|
|
|
322
|
-
|
|
327
|
+
With `filterBy`, the picker sends the table filter syntax to the `select` endpoint (`filter_<field>=is:<value>`), which honors only the filters the target controller declares. It searches again when `from` changes and clears a pick made for its previous value; the pair a record loaded with is kept, and while `from` is empty every row is offered. It narrows what is offered only: a rule that must hold on save belongs to the module's routes.
|
|
328
|
+
|
|
329
|
+
When the target controller exposes a `new` endpoint and has form fields, the picker of an editable form field also carries an inline "add" form built from the target's `new` fields, plus the permission id of the target's `add` action. That form is the bulk of the component — the target's fields, its validation schema, its own relations' add forms — so a table column's type, its filter and a read-only field leave it out: they read the related rows through the `select` route, and a filter only picks among them.
|
|
323
330
|
|
|
324
331
|
`CascaderRelationType<T>` is the same idea for a self-referencing table: the target's `select` endpoint returns a flat list, and the widget rebuilds the tree client-side. Its `keyMapping` is required and adds `parent` — the field pointing at the parent row, which must itself carry `@Select()` — alongside the required `label` and `value`. It adds `maxDepth` and `leafOnly` to the relation options, and supports the same `filterOnly` behaviour. In a table cell it shows the path of the picked node ("Electronics / Audio"); a `multiple` one shows a badge per picked node, named by its own label with its path on hover, and folds those past three into `+N`.
|
|
325
332
|
|
|
@@ -338,7 +345,7 @@ When the target controller exposes a `new` endpoint and has form fields, the pic
|
|
|
338
345
|
| `addressLine2` | no | Supplementary delivery information. |
|
|
339
346
|
| `countrySubdivision` | no | Region, state or province. |
|
|
340
347
|
|
|
341
|
-
The type's options are `placeholder` (one string per address component), `autocomplete`, an optional `dataApiController` with a `keyMapping` of `label` / `value` for picking an existing address, and `fallback`. `AddressAutocomplete` configures the geocoder:
|
|
348
|
+
The type's options are `placeholder` (one string per address component), `autocomplete`, `defaultCountry` (the ISO 3166-1 alpha-2 country shown while the address has none and whose places the street suggestions list first, written with the first part typed: an address left empty stays empty), an optional `dataApiController` with a `keyMapping` of `label` / `value` for picking an existing address, and `fallback`. `AddressAutocomplete` configures the geocoder:
|
|
342
349
|
|
|
343
350
|
| Option | Type | Description |
|
|
344
351
|
| --- | --- | --- |
|
|
@@ -33,7 +33,7 @@ A `SendableNotification` is immutable and holds no recipient, so you can build o
|
|
|
33
33
|
| `toUser` | `(userId: string, options?: SendOptions) => Promise<void>` | Send to one user. |
|
|
34
34
|
| `toUsers` | `(userIds: string[], options?: SendOptions) => Promise<void>` | Send to a list of users. |
|
|
35
35
|
| `toUsersIdempotently` | `(userIds: string[], idempotencyKey: string, options?: SendOptions) => Promise<void>` | Send a durable event once per recipient, including after dismissal. |
|
|
36
|
-
| `toRoles` | `(roleIds: string[], options?:
|
|
36
|
+
| `toRoles` | `(roleIds: string[], options?: RoleSendOptions) => Promise<void>` | Send to every user holding at least one of the roles, in `options.tenantId` when set. |
|
|
37
37
|
| `broadcast` | `(options?: SendOptions) => Promise<void>` | Send to every user. |
|
|
38
38
|
|
|
39
39
|
## Building a Notification
|
|
@@ -146,10 +146,10 @@ await shipped.toUsers([ownerId, managerId], { readScope: "shared" });
|
|
|
146
146
|
`toRoles(roleIds, options?)` resolves the users that hold any of the given roles and sends to them. An empty `roleIds` array sends nothing, and so does a set of roles nobody holds.
|
|
147
147
|
|
|
148
148
|
```typescript
|
|
149
|
-
await shipped.toRoles([adminRoleId], { readScope: "shared" });
|
|
149
|
+
await shipped.toRoles([adminRoleId], { readScope: "shared", tenantId });
|
|
150
150
|
```
|
|
151
151
|
|
|
152
|
-
Role ids are the DMS role identifiers described in the DMS tenancy and permissions guide; membership is resolved across tenants, so a user holding the role in any tenant is included.
|
|
152
|
+
Role ids are the DMS role identifiers described in the DMS tenancy and permissions guide, and they belong to a tenant. `tenantId` (`RoleSendOptions`) restricts the recipients to that tenant's members; without it, membership is resolved across tenants, so a user holding the role in any tenant is included.
|
|
153
153
|
|
|
154
154
|
## Broadcasting
|
|
155
155
|
|
|
@@ -168,6 +168,7 @@ Every dispatch method accepts an optional `SendOptions` object.
|
|
|
168
168
|
| `readScope` | `ReadScope` (optional) | Read-state grouping for a multi-recipient send. Defaults to `"individual"` — see [Read Scope](/docs/interfaces/notifications/categories-and-subjects#read-scope). |
|
|
169
169
|
| `idempotencyKey` | `string` (optional) | Stable event key, unique across producers: prefix it with your module ID. Reusing it for a recipient preserves the original notification and dismissal state. |
|
|
170
170
|
| `dedupe` | `boolean` (optional) | `false` stores every unkeyed send, even one identical to a notification the recipient got less than 10 seconds earlier. Defaults to `true`; see below. |
|
|
171
|
+
| `tenantId` | `string` (optional, `toRoles` only: `RoleSendOptions`) | Only the members of this tenant holding the roles; every tenant when unset. |
|
|
171
172
|
|
|
172
173
|
Pick `"shared"` for alerts that only need one person to act on them, and `"individual"` for anything each recipient has to see for themselves.
|
|
173
174
|
|
|
@@ -255,7 +255,7 @@ Types the `htmlRender` block of the DMS module configuration in `antelope.config
|
|
|
255
255
|
| Option | Type | Description |
|
|
256
256
|
| ------ | ---- | ----------- |
|
|
257
257
|
| `renderEndpoint` | `string` | Absolute URL `GenerateHtml` posts to. Default `http://localhost:3001/api/html/render`. |
|
|
258
|
-
| `serviceSecret` | `string` | Secret used to sign the service token that authenticates the render call. Default `"dev"
|
|
258
|
+
| `serviceSecret` | `string` | Secret used to sign the service token that authenticates the render call. Default `"dev"`, which the DMS refuses in production. |
|
|
259
259
|
| `serviceTokenLifetime` | `number` | Lifetime of that service token, in milliseconds. The frontend render server rejects a token valid for more than five minutes. Default `300000` (5 minutes). |
|
|
260
260
|
|
|
261
261
|
The DMS merges the block you supply over those defaults, so a configuration only needs the keys it changes:
|
|
@@ -263,13 +263,12 @@ The DMS merges the block you supply over those defaults, so a configuration only
|
|
|
263
263
|
```typescript
|
|
264
264
|
import { defineConfig } from "@antelopejs/interface-core/config";
|
|
265
265
|
|
|
266
|
-
const renderSecret = process.env.DMS_HTML_RENDER_SECRET;
|
|
267
|
-
if (!renderSecret) {
|
|
268
|
-
throw new Error("DMS_HTML_RENDER_SECRET is not set");
|
|
269
|
-
}
|
|
270
|
-
|
|
271
266
|
export default defineConfig({
|
|
272
267
|
name: "acme-admin",
|
|
268
|
+
// Read at start; a production start without it is refused by the DMS.
|
|
269
|
+
envOverrides: {
|
|
270
|
+
DMS_HTML_RENDER_SECRET: "modules.dms.config.htmlRender.serviceSecret",
|
|
271
|
+
},
|
|
273
272
|
modules: {
|
|
274
273
|
dms: {
|
|
275
274
|
source: {
|
|
@@ -280,7 +279,6 @@ export default defineConfig({
|
|
|
280
279
|
config: {
|
|
281
280
|
htmlRender: {
|
|
282
281
|
renderEndpoint: "https://admin.example.com/api/html/render",
|
|
283
|
-
serviceSecret: renderSecret,
|
|
284
282
|
},
|
|
285
283
|
},
|
|
286
284
|
},
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@antelopejs/interface-dms",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.8.0",
|
|
4
4
|
"description": "Public AntelopeJS interfaces of the DMS: pages, components, permissions, tenancy, auth, notifications and HTML rendering.",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"antelopejs",
|
|
@@ -45,7 +45,6 @@
|
|
|
45
45
|
"knip:all": "knip"
|
|
46
46
|
},
|
|
47
47
|
"dependencies": {
|
|
48
|
-
"jsonwebtoken": "^9.0.3",
|
|
49
48
|
"randomstring": "^1.3.1",
|
|
50
49
|
"zod": "~3.24.2",
|
|
51
50
|
"zod-to-json-schema": "~3.24.6"
|
|
@@ -53,9 +52,8 @@
|
|
|
53
52
|
"peerDependencies": {
|
|
54
53
|
"@antelopejs/interface-api": ">=0.0.15 <1.0.0",
|
|
55
54
|
"@antelopejs/interface-api-util": ">=0.1.3 <1.0.0",
|
|
56
|
-
"@antelopejs/interface-auth": ">=0.0.7 <1.0.0",
|
|
57
55
|
"@antelopejs/interface-core": ">=0.2.4 <1.0.0",
|
|
58
|
-
"@antelopejs/interface-data-api": ">=0.
|
|
56
|
+
"@antelopejs/interface-data-api": ">=0.3.0 <1.0.0",
|
|
59
57
|
"@antelopejs/interface-database": ">=0.1.10 <1.0.0",
|
|
60
58
|
"@antelopejs/interface-database-decorators": ">=0.1.9 <1.0.0",
|
|
61
59
|
"@antelopejs/interface-file-storage": ">=0.1.4 <1.0.0"
|
|
@@ -63,13 +61,11 @@
|
|
|
63
61
|
"devDependencies": {
|
|
64
62
|
"@antelopejs/interface-api": ">=0.0.15 <1.0.0",
|
|
65
63
|
"@antelopejs/interface-api-util": ">=0.1.3 <1.0.0",
|
|
66
|
-
"@antelopejs/interface-auth": ">=0.0.7 <1.0.0",
|
|
67
64
|
"@antelopejs/interface-core": ">=0.2.4 <1.0.0",
|
|
68
|
-
"@antelopejs/interface-data-api": ">=0.
|
|
65
|
+
"@antelopejs/interface-data-api": ">=0.3.0 <1.0.0",
|
|
69
66
|
"@antelopejs/interface-database": ">=0.1.10 <1.0.0",
|
|
70
67
|
"@antelopejs/interface-database-decorators": ">=0.1.9 <1.0.0",
|
|
71
68
|
"@antelopejs/interface-file-storage": ">=0.1.4 <1.0.0",
|
|
72
|
-
"@types/jsonwebtoken": "^9.0.10",
|
|
73
69
|
"@types/node": "^24.0.0",
|
|
74
70
|
"@types/randomstring": "^1.3.0",
|
|
75
71
|
"knip": "^6.34.0",
|