@antelopejs/interface-dms 0.7.1 → 0.7.2
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 +56 -0
- package/README.md +70 -68
- package/dist/auth/db/models/sessions.model.d.ts +1 -0
- package/dist/auth/db/models/userExternalIdentities.model.d.ts +1 -0
- package/dist/auth/db/models/users.model.d.ts +1 -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/live-toggle.d.ts +2 -2
- package/dist/base/period-selector.d.ts +2 -2
- package/dist/base/table-view/schema.d.ts +4 -4
- package/dist/db/internal/exportJobs.model.d.ts +1 -0
- package/dist/db/internal/inviteResolutions.model.d.ts +1 -0
- package/dist/db/internal/tenantLifecycle.model.d.ts +1 -0
- package/dist/db/models/roles.model.d.ts +1 -0
- package/dist/db/models/tenantMembers.model.d.ts +1 -0
- package/dist/db/models/tenants.model.d.ts +1 -0
- package/dist/db/models/user_invites.model.d.ts +1 -0
- package/docs/.navigation.yml +2 -0
- package/docs/1.core/.navigation.yml +2 -0
- package/docs/1.core/1.introduction.md +169 -0
- package/docs/1.core/10.layout-banners.md +133 -0
- package/docs/1.core/2.pages-and-modules.md +647 -0
- package/docs/1.core/3.components.md +287 -0
- package/docs/1.core/4.permissions.md +279 -0
- package/docs/1.core/5.tenancy.md +356 -0
- package/docs/1.core/6.invites.md +239 -0
- package/docs/1.core/7.hooks.md +354 -0
- package/docs/1.core/8.realtime.md +147 -0
- package/docs/1.core/9.quick-actions.md +115 -0
- package/docs/2.auth/.navigation.yml +2 -0
- package/docs/2.auth/1.introduction.md +75 -0
- package/docs/2.auth/2.authentication.md +222 -0
- package/docs/2.auth/3.tokens-and-sessions.md +352 -0
- package/docs/2.auth/4.users.md +256 -0
- package/docs/2.auth/5.external-identities.md +122 -0
- package/docs/3.base/.navigation.yml +2 -0
- package/docs/3.base/1.introduction.md +202 -0
- package/docs/3.base/10.display-blocks.md +136 -0
- package/docs/3.base/2.types.md +402 -0
- package/docs/3.base/3.forms.md +349 -0
- package/docs/3.base/4.tables.md +608 -0
- package/docs/3.base/5.charts-and-widgets.md +466 -0
- package/docs/3.base/6.layout.md +431 -0
- package/docs/3.base/8.data-types.md +361 -0
- package/docs/3.base/9.helpers.md +414 -0
- package/docs/4.notifications/.navigation.yml +2 -0
- package/docs/4.notifications/1.introduction.md +79 -0
- package/docs/4.notifications/2.categories-and-subjects.md +212 -0
- package/docs/4.notifications/3.sending.md +283 -0
- package/docs/5.html-render/.navigation.yml +2 -0
- package/docs/5.html-render/1.introduction.md +372 -0
- package/docs/5.html-render/2.built-in-emails.md +107 -0
- package/docs/index.md +100 -0
- package/docs/tsconfig.json +11 -0
- package/package.json +24 -18
|
@@ -0,0 +1,133 @@
|
|
|
1
|
+
---
|
|
2
|
+
title: "Layout Banners"
|
|
3
|
+
description: "A layout banner is a global message the backend adds under the dashboard header: a colored strip carrying a line of text and its link buttons, or a component of your frontend layer."
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
# Layout Banners
|
|
7
|
+
|
|
8
|
+
## Overview
|
|
9
|
+
|
|
10
|
+
A layout banner is a global message the backend adds under the dashboard header: a colored strip carrying a line of text and its link buttons, or a component of your frontend layer. It renders on every page of the dashboard layout — tenant pages and the module back-office pages under `/modules` alike — and banners stack by `order`.
|
|
11
|
+
|
|
12
|
+
Whether a banner shows is decided per request, on the server, while the site layout is built. The answer is part of the server-rendered page, so a banner never flashes in or out after hydration.
|
|
13
|
+
|
|
14
|
+
Everything lives in `@antelopejs/interface-dms/layout-banners`.
|
|
15
|
+
|
|
16
|
+
## Register a Banner
|
|
17
|
+
|
|
18
|
+
`RegisterLayoutBanner(info)` registers the banner and returns a function removing it. Register at import time, next to the state the banner reports on; the registration is dropped automatically when your module stops.
|
|
19
|
+
|
|
20
|
+
```typescript
|
|
21
|
+
import {
|
|
22
|
+
LayoutBannerVariant,
|
|
23
|
+
RegisterLayoutBanner,
|
|
24
|
+
} from "@antelopejs/interface-dms/layout-banners";
|
|
25
|
+
import { isMaintenanceScheduled } from "./maintenance";
|
|
26
|
+
|
|
27
|
+
RegisterLayoutBanner({
|
|
28
|
+
key: "my-app:maintenance",
|
|
29
|
+
variant: LayoutBannerVariant.WARNING,
|
|
30
|
+
text: "$my_app.banners.maintenance",
|
|
31
|
+
order: 10,
|
|
32
|
+
dismissible: true,
|
|
33
|
+
visible: async ({ tenantId }) => isMaintenanceScheduled(tenantId),
|
|
34
|
+
});
|
|
35
|
+
```
|
|
36
|
+
|
|
37
|
+
The text can be a [composed text](/docs/components/block-fundamentals#composed-texts) — an i18n key and the raw values it names, written in the reader's language — and `actions` adds link buttons after it, the last one leading:
|
|
38
|
+
|
|
39
|
+
```typescript
|
|
40
|
+
RegisterLayoutBanner({
|
|
41
|
+
key: "my-app:maintenance",
|
|
42
|
+
variant: LayoutBannerVariant.WARNING,
|
|
43
|
+
text: {
|
|
44
|
+
key: "my_app.banners.maintenance_at", // "Maintenance {date}"
|
|
45
|
+
params: { date: { type: "datetime", value: "2026-10-12T22:00:00Z" } },
|
|
46
|
+
},
|
|
47
|
+
actions: [{ label: "$my_app.banners.status", to: "https://status.example.com" }],
|
|
48
|
+
});
|
|
49
|
+
```
|
|
50
|
+
|
|
51
|
+
## Resolve the Content per Request
|
|
52
|
+
|
|
53
|
+
When the message names the state that makes the banner show — the invoice that failed, its amount, the day the workspace is suspended — `resolve(context)` builds the text and the buttons for each request, or answers `undefined` to hide the banner. It replaces `visible`: one read decides both.
|
|
54
|
+
|
|
55
|
+
```typescript
|
|
56
|
+
RegisterLayoutBanner({
|
|
57
|
+
key: "saas:past-due",
|
|
58
|
+
variant: LayoutBannerVariant.ERROR,
|
|
59
|
+
resolve: async ({ tenantId }) => {
|
|
60
|
+
const invoice = await openInvoice(tenantId);
|
|
61
|
+
if (!invoice) return undefined;
|
|
62
|
+
return {
|
|
63
|
+
text: {
|
|
64
|
+
key: "saas.banner.past_due", // "Invoice {invoice} of {amount} failed, retry {date}"
|
|
65
|
+
params: {
|
|
66
|
+
invoice: invoice.number,
|
|
67
|
+
amount: { type: "money", value: invoice.amountDue, currency: invoice.currency },
|
|
68
|
+
date: { type: "date", value: invoice.nextAttemptAt, format: "day" },
|
|
69
|
+
},
|
|
70
|
+
},
|
|
71
|
+
actions: [{ label: "$saas.banner.pay", to: "/settings/workspace/billing?pay=1" }],
|
|
72
|
+
};
|
|
73
|
+
},
|
|
74
|
+
});
|
|
75
|
+
```
|
|
76
|
+
|
|
77
|
+
The server sends the raw values; the browser formats them for the reader's language, time zone and clock, so the resolver never formats text. A banner like this one needs no frontend component.
|
|
78
|
+
|
|
79
|
+
## Mount a Component
|
|
80
|
+
|
|
81
|
+
A banner can mount a global component of your [frontend layer](/docs/building/frontend-layer) instead of a text. The DMS still draws the chrome around it — color, icon, close button and ARIA role — and passes `props` to the component:
|
|
82
|
+
|
|
83
|
+
```typescript
|
|
84
|
+
RegisterLayoutBanner({
|
|
85
|
+
key: "my-app:trial",
|
|
86
|
+
variant: LayoutBannerVariant.INFO,
|
|
87
|
+
component: "MyAppTrialBanner",
|
|
88
|
+
props: { upgradeUrl: "/settings/billing" },
|
|
89
|
+
visible: ({ isOwner }) => isOwner,
|
|
90
|
+
});
|
|
91
|
+
```
|
|
92
|
+
|
|
93
|
+
Re-registering a `key` replaces the banner.
|
|
94
|
+
|
|
95
|
+
## `LayoutBannerInfo`
|
|
96
|
+
|
|
97
|
+
| Field | Type | Description |
|
|
98
|
+
| ----- | ---- | ----------- |
|
|
99
|
+
| `key` | `string` | Unique across the deployment. Required. Also what a dismissal remembers: give a banner a new key when it must show again to users who dismissed it. |
|
|
100
|
+
| `variant` | `LayoutBannerVariant` | `"info"`, `"warning"` or `"error"`. Required. Picks the color, the default icon and the ARIA role. |
|
|
101
|
+
| `text` | `string \| ComposedText` | The message. A `$`-prefixed value resolves as an i18n key; a `ComposedText` is written in the reader's language. Exclusive with `component` and `resolve`; one of the three is required. |
|
|
102
|
+
| `actions` | `BlockLinkAction[]` | Link buttons after `text`, the last one leading. Labels follow the `$` convention. |
|
|
103
|
+
| `component` | `string` | Name of a global component rendered as the banner's content. Exclusive with `text` and `resolve`. |
|
|
104
|
+
| `props` | `Record<string, unknown>` | Props passed to `component`. Must be serializable. |
|
|
105
|
+
| `order` | `number` | Stack order, ascending from the top. Defaults to `0`; ties keep the registration order. |
|
|
106
|
+
| `icon` | `string` | Leading icon. Defaults to one matching the variant. |
|
|
107
|
+
| `dismissible` | `boolean` | Whether the user can close the banner. Defaults to `false`. |
|
|
108
|
+
| `visible` | `LayoutBannerVisibility` | Decides per request whether a `text` or `component` banner shows. Absent means always. |
|
|
109
|
+
| `resolve` | `LayoutBannerResolver` | Builds `{ text, actions? }` per request, or `undefined` to hide the banner. Exclusive with `text`, `component` and `visible`. |
|
|
110
|
+
|
|
111
|
+
## Visibility
|
|
112
|
+
|
|
113
|
+
`visible(context)` returns a boolean, or a promise of one; `resolve(context)` returns a `LayoutBannerContent` (`{ text, actions? }`), `undefined`, or a promise of either. They run server-side for every site layout request, all resolvers together, so the request waits for the slowest one rather than for their sum. A resolver that throws or does not answer within two seconds hides its banner for that request, as does a `visible` returning anything but `true` or a `resolve` answering no text; the other banners are unaffected.
|
|
114
|
+
|
|
115
|
+
### `LayoutBannerContext`
|
|
116
|
+
|
|
117
|
+
| Field | Type | Description |
|
|
118
|
+
| ----- | ---- | ----------- |
|
|
119
|
+
| `user` | `User \| undefined` | The caller; `undefined` for an unauthenticated request. |
|
|
120
|
+
| `tenantId` | `string` | The tenant the site layout is built for. |
|
|
121
|
+
| `permissions` | `Set<string>` | The caller's effective permissions in the tenant. |
|
|
122
|
+
| `isOwner` | `boolean` | Whether the caller holds the wildcard permission. |
|
|
123
|
+
| `isTenantAccessDenied` | `boolean` | Whether a [tenant access gate](/docs/interfaces/core/tenancy) currently refuses the caller. |
|
|
124
|
+
|
|
125
|
+
Unlike product surfaces, a banner reads the permissions a denying access gate would hide: telling a suspended or past-due workspace why is exactly what a banner is for. Check `isTenantAccessDenied` when the two situations call for different banners.
|
|
126
|
+
|
|
127
|
+
The resolvers run whenever a client fetches its site layout — on a full page load and after a menu invalidation. When the state a resolver reads changes, call `NotifyMenuChanged(tenantId)` from `@antelopejs/interface-dms/page` so the connected clients of that tenant re-fetch it and the banner appears or goes without a reload.
|
|
128
|
+
|
|
129
|
+
## Rendering
|
|
130
|
+
|
|
131
|
+
Banners render under the header of the dashboard layout, full width, in `order`, a text banner's `actions` on its right. Each one is its own live region: `error` banners carry `role="alert"`, `info` and `warning` banners `role="status"`.
|
|
132
|
+
|
|
133
|
+
A dismissible banner shows a close button. The dismissal lasts for the browser session and is kept in the `dms-dismissed-banners` cookie, which the server reads too, so a dismissed banner is already absent from the server-rendered page. A banner that is not dismissible ignores any dismissal recorded under its key.
|