@protobase/ui 0.25.2 → 0.25.4
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/dist/account/account-pages.js +3 -2
- package/dist/account/account-section.js +1 -1
- package/dist/account/account-security-page.js +9 -4
- package/dist/account/backup-codes.js +7 -6
- package/dist/account/messages.js +214 -0
- package/dist/account/passkeys-section.js +23 -22
- package/dist/account/password-dialog.js +4 -3
- package/dist/account/sign-in-policy-form.js +14 -13
- package/dist/account/sign-in-policy-labels.js +30 -29
- package/dist/account/sign-in-policy-page.js +11 -6
- package/dist/account/sign-in-providers-section.js +6 -5
- package/dist/account/staff-sign-in-log.js +11 -10
- package/dist/account/totp-qr.js +2 -1
- package/dist/account/two-factor-section.js +24 -23
- package/dist/account/two-factor-setup.js +19 -18
- package/dist/action-card/action-card.js +2 -1
- package/dist/action-card/field-list.js +1 -1
- package/dist/action-card/messages.js +8 -0
- package/dist/activity-chart/activity-chart.js +3 -2
- package/dist/activity-chart/messages.js +14 -0
- package/dist/app/app.d.ts +1 -1
- package/dist/app/app.js +20 -21
- package/dist/app/assistant-shell.js +2 -1
- package/dist/app/batch-failure.js +2 -1
- package/dist/app/breadcrumb-for.js +4 -3
- package/dist/app/charts/chart-groups.js +3 -2
- package/dist/app/charts/chart-view.js +3 -2
- package/dist/app/charts/group-bars.js +2 -1
- package/dist/app/charts/messages.js +26 -0
- package/dist/app/charts/period-chart.js +3 -2
- package/dist/app/charts/split-points.js +2 -1
- package/dist/app/comments/comment-composer.js +19 -18
- package/dist/app/comments/comments-section.js +4 -3
- package/dist/app/comments/messages.js +26 -0
- package/dist/app/create/create-page.js +11 -18
- package/dist/app/custom-section.js +4 -6
- package/dist/app/displays/board-column.js +7 -6
- package/dist/app/displays/board-columns.js +3 -2
- package/dist/app/displays/board-display.js +13 -15
- package/dist/app/displays/calendar-controls.js +3 -2
- package/dist/app/displays/calendar-display.js +12 -17
- package/dist/app/displays/card-carousel.js +4 -3
- package/dist/app/displays/cards-display.js +2 -1
- package/dist/app/displays/custom-display.js +4 -6
- package/dist/app/displays/display-frame.js +3 -2
- package/dist/app/displays/list-cards.js +5 -8
- package/dist/app/displays/list-displays.js +3 -3
- package/dist/app/displays/map-display.js +15 -14
- package/dist/app/displays/messages.js +128 -2
- package/dist/app/displays/period-controls.js +4 -3
- package/dist/app/displays/record-card.js +7 -6
- package/dist/app/displays/timeline-display.js +17 -26
- package/dist/app/displays/timeline-range.js +2 -1
- package/dist/app/displays/timeline-row.js +1 -1
- package/dist/app/displays/use-board-move.js +2 -2
- package/dist/app/displays/use-calendar-move.js +2 -1
- package/dist/app/document-title.js +2 -1
- package/dist/app/error-banner.js +1 -1
- package/dist/app/export/builder-port.js +2 -1
- package/dist/app/export/export-dialog.js +39 -43
- package/dist/app/export/export-worker.js +2 -0
- package/dist/app/export/messages.js +78 -0
- package/dist/app/export/run-export.js +5 -2
- package/dist/app/export/sink.js +4 -3
- package/dist/app/export/use-export-flow.js +4 -3
- package/dist/app/field-editor.js +4 -3
- package/dist/app/field-types/messages.js +8 -0
- package/dist/app/field-types/widget-part.js +2 -1
- package/dist/app/file-field.js +11 -14
- package/dist/app/gallery/gallery-section.js +4 -3
- package/dist/app/gallery/lightbox.js +5 -4
- package/dist/app/gallery/messages.js +30 -0
- package/dist/app/gallery/photo-details.js +3 -2
- package/dist/app/hand-order/hand-sorted-list.js +3 -6
- package/dist/app/hand-order/messages.js +4 -2
- package/dist/app/history/describe-change.js +3 -8
- package/dist/app/history/messages.js +32 -0
- package/dist/app/import/convert.js +20 -19
- package/dist/app/import/failed-rows.js +3 -2
- package/dist/app/import/import-button.js +2 -1
- package/dist/app/import/import-check-step.js +21 -37
- package/dist/app/import/import-dialog.js +13 -17
- package/dist/app/import/import-file-step.js +5 -9
- package/dist/app/import/import-mapping-step.js +32 -35
- package/dist/app/import/import-result-step.js +13 -12
- package/dist/app/import/messages.js +200 -0
- package/dist/app/import/parse-worker.js +2 -0
- package/dist/app/import/prepare-rows.js +5 -4
- package/dist/app/import/sheet-cell.js +2 -1
- package/dist/app/import/sheet-file.js +3 -2
- package/dist/app/import/sheet-source.js +7 -3
- package/dist/app/import/xlsx-reader.js +4 -3
- package/dist/app/import/xlsx-xml.js +2 -1
- package/dist/app/import/zip-reader.js +4 -3
- package/dist/app/list-edit/use-list-edits.js +1 -1
- package/dist/app/list-group-chart.js +2 -1
- package/dist/app/list-page.js +1 -1
- package/dist/app/map/map-layer-switch.js +3 -2
- package/dist/app/map/messages.js +14 -0
- package/dist/app/messages.js +116 -2
- package/dist/app/meta-gate.js +1 -1
- package/dist/app/nav-from-meta.js +11 -10
- package/dist/app/pages/action-inputs/action-inputs-dialog.js +3 -2
- package/dist/app/pages/action-inputs/input-answers.js +6 -5
- package/dist/app/pages/blocks/card-row.js +3 -2
- package/dist/app/pages/blocks/field.js +1 -1
- package/dist/app/pages/blocks/modal-form.js +5 -4
- package/dist/app/pages/blocks/progress.js +1 -1
- package/dist/app/pages/blocks/record-card.js +3 -2
- package/dist/app/pages/blocks/stat.js +2 -1
- package/dist/app/pages/composed-page.js +6 -8
- package/dist/app/pages/confirm-action.js +2 -1
- package/dist/app/pages/custom-boundary.js +4 -2
- package/dist/app/pages/messages.js +64 -0
- package/dist/app/pages/render-node.js +5 -7
- package/dist/app/pages/use-run-action.js +11 -10
- package/dist/app/record-page.js +1 -1
- package/dist/app/record-row/messages.js +26 -0
- package/dist/app/record-row/record-row-body.js +12 -7
- package/dist/app/record-row/record-row.js +4 -3
- package/dist/app/record-row/saved-record-row.js +3 -2
- package/dist/app/related-dots/dots-summary.js +2 -1
- package/dist/app/related-dots/messages.js +8 -0
- package/dist/app/relation-picker.js +9 -8
- package/dist/app/router.js +5 -4
- package/dist/app/runtime/messages.js +58 -0
- package/dist/app/runtime/runtime-status.js +10 -23
- package/dist/app/runtime/runtime-update-dialog.js +7 -6
- package/dist/app/runtime/runtime-update.js +4 -3
- package/dist/app/saved/favorite-button.js +2 -1
- package/dist/app/saved/messages.js +22 -0
- package/dist/app/saved/saved-list.js +4 -7
- package/dist/app/saved/use-favorite.js +3 -2
- package/dist/app/search/messages.js +6 -2
- package/dist/app/search/navigation-hits.js +9 -7
- package/dist/app/searches/filter-chip-list.js +2 -1
- package/dist/app/searches/messages.js +96 -0
- package/dist/app/searches/save-search-button.js +5 -4
- package/dist/app/searches/saved-search-rows.js +3 -2
- package/dist/app/searches/saved-search-section.js +6 -5
- package/dist/app/searches/saved-search.js +4 -3
- package/dist/app/searches/saved-searches.js +15 -25
- package/dist/app/searches/search-controls.js +5 -4
- package/dist/app/searches/search-diff.js +4 -2
- package/dist/app/searches/search-name-dialog.js +4 -3
- package/dist/app/searches/update-search-dialog.js +15 -30
- package/dist/app/searches/use-save-search.js +8 -7
- package/dist/app/searches/use-search-row.js +4 -3
- package/dist/app/sensitive-field.js +5 -4
- package/dist/app/source-notice.js +13 -8
- package/dist/app/staff-banner.js +13 -20
- package/dist/app-shell/global-search.js +2 -2
- package/dist/app-shell/messages.js +4 -2
- package/dist/app-shell/sidebar-group.js +1 -1
- package/dist/app-shell/sidebar-item.js +1 -1
- package/dist/app-shell/sidebar.js +1 -1
- package/dist/assistant-dock/assistant-dock.js +6 -6
- package/dist/assistant-dock/messages.js +26 -0
- package/dist/assistant-dock/use-assistant.js +2 -1
- package/dist/assistant-dock/use-backend-data.js +2 -1
- package/dist/assistant-dock/widget-view.js +3 -2
- package/dist/auth/auth-gate.js +10 -5
- package/dist/auth/auth-locale.js +14 -0
- package/dist/auth/auth-messages.js +122 -43
- package/dist/auth/auth-provider.js +6 -1
- package/dist/auth/code-form.js +4 -3
- package/dist/auth/email-code-form.js +11 -13
- package/dist/auth/email-code-page.js +2 -1
- package/dist/auth/first-run-page.js +6 -5
- package/dist/auth/forgot-password-form.js +15 -17
- package/dist/auth/forgot-password-page.js +2 -1
- package/dist/auth/messages.js +200 -0
- package/dist/auth/provider-sign-in-page.js +5 -5
- package/dist/auth/resend-code-button.js +2 -1
- package/dist/auth/reset-password-form.js +12 -11
- package/dist/auth/reset-password-page.js +4 -3
- package/dist/auth/setup-required-form.js +8 -10
- package/dist/auth/setup-required-page.js +7 -2
- package/dist/auth/sign-in-form.js +18 -21
- package/dist/auth/sign-in-prompt.js +4 -3
- package/dist/auth/sign-up-code-form.js +11 -13
- package/dist/auth/sign-up-details-form.js +15 -22
- package/dist/auth/sign-up-email-form.js +9 -8
- package/dist/auth/sign-up-page.js +2 -1
- package/dist/auth/staff-sign-in-form.js +16 -19
- package/dist/auth/two-factor-form.js +19 -18
- package/dist/auth/user-to-shell.js +2 -1
- package/dist/chat/composer.js +8 -7
- package/dist/chat/messages.js +14 -0
- package/dist/compact-table/compact-table.js +4 -8
- package/dist/compact-table/messages.js +18 -0
- package/dist/data/meta-data.d.ts +1 -1
- package/dist/data/use-update-record.js +1 -1
- package/dist/data-grid/grid-header.js +1 -1
- package/dist/filter-panel/date-presets.js +1 -1
- package/dist/forms/public-form-page.js +1 -1
- package/dist/inline-child-table/inline-child-table.js +12 -12
- package/dist/inline-child-table/line-row.js +10 -9
- package/dist/inline-child-table/messages.js +52 -0
- package/dist/inline-child-table/totals-footer.js +5 -8
- package/dist/invitations/invite-form.js +3 -2
- package/dist/invitations/link-notice.js +6 -4
- package/dist/invitations/messages.js +24 -0
- package/dist/invitations/pending-invitation-row.js +4 -3
- package/dist/lists/messages.js +8 -0
- package/dist/lists/paged-search-list.js +2 -1
- package/dist/locale/slotted.js +14 -0
- package/dist/markdown/markdown-editor.js +6 -5
- package/dist/markdown/messages.js +16 -0
- package/dist/organizations/all-organizations.js +6 -5
- package/dist/organizations/app-roles-picker.js +2 -1
- package/dist/organizations/invitation-page.js +41 -51
- package/dist/organizations/invitations-page.js +11 -10
- package/dist/organizations/members-page.js +16 -11
- package/dist/organizations/messages.js +180 -0
- package/dist/organizations/organization-page.js +27 -26
- package/dist/organizations/organization-paths.js +16 -13
- package/dist/organizations/organization-row.js +3 -2
- package/dist/organizations/organizations-page.js +17 -16
- package/dist/organizations/use-organizations.js +2 -1
- package/dist/package.js +1 -1
- package/dist/plugins/before-sign-in.js +6 -1
- package/dist/plugins/load-plugin.d.ts +1 -1
- package/dist/plugins/load-plugin.js +3 -2
- package/dist/plugins/messages.js +36 -0
- package/dist/plugins/plugin-gate.js +6 -1
- package/dist/plugins/plugin-manifest.js +8 -7
- package/dist/plugins/plugin-notice.js +8 -12
- package/dist/primitives/button.js +1 -1
- package/dist/primitives/messages.js +16 -0
- package/dist/primitives/segmented.js +1 -1
- package/dist/primitives/sheet.js +2 -1
- package/dist/primitives/spinner.js +2 -1
- package/dist/primitives/token.js +2 -1
- package/dist/record-view/field.js +2 -2
- package/dist/record-view/messages.js +4 -2
- package/dist/record-view/record-tabs.js +2 -1
- package/dist/roles/messages.js +14 -0
- package/dist/roles/use-active-role.js +3 -2
- package/dist/shell/assets/app-yCMYvqMX.js +69 -0
- package/dist/shell/assets/cn-Bi0ZEjD9.js +1 -0
- package/dist/shell/assets/export-worker-ClACk00b.js +2 -0
- package/dist/shell/assets/{index-BRkDA5KD.css → index-Bkc7w8I4.css} +1 -1
- package/dist/shell/assets/{index-xY0Mvv6V.js → index-HkjqX_O-.js} +2 -2
- package/dist/shell/assets/maplibre-DUTS_eJM.js +814 -0
- package/dist/shell/assets/{markdown-content-ukDTCu6u.js → markdown-content-CtGUP4W6.js} +1 -1
- package/dist/shell/assets/parse-worker-UhbwEjS0.js +5 -0
- package/dist/shell/assets/preload-helper-BZ3hrQDD.js +1 -0
- package/dist/shell/assets/{record-map-BJmYZ7kQ.js → record-map-DFy7sJEU.js} +1 -1
- package/dist/shell/assets/records-map-MfQEuiFB.js +1 -0
- package/dist/shell/assets/shared-protobase-ui-Bfa59vx6.js +1 -0
- package/dist/shell/index.html +6 -4
- package/dist/shell/protobase.shell.json +1 -1
- package/dist/shell/protobase.sw.js +2 -2
- package/dist/side-panel/messages.js +8 -0
- package/dist/side-panel/side-panel.js +2 -1
- package/dist/toasts/messages.js +8 -0
- package/dist/toasts/toast.js +2 -1
- package/dist/users/app-invitations.js +5 -4
- package/dist/users/create-user-form.js +9 -8
- package/dist/users/messages.js +52 -0
- package/dist/users/use-user-roles.js +2 -1
- package/dist/users/user-list.js +6 -5
- package/dist/users/user-row.js +5 -4
- package/package.json +4 -4
- package/dist/shell/assets/app-C-OLrsRR.js +0 -69
- package/dist/shell/assets/export-worker-Bz57Pf5g.js +0 -2
- package/dist/shell/assets/maplibre-Dov6nhPX.js +0 -814
- package/dist/shell/assets/parse-worker-BtcY0dQw.js +0 -5
- package/dist/shell/assets/records-map-C1_gDKHR.js +0 -1
- package/dist/shell/assets/shared-protobase-ui-CFYJAbMR.js +0 -1
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
import { defineMessages } from "../../locale/messages.js";
|
|
2
|
+
//#region src/app/pages/messages.ts
|
|
3
|
+
const text = defineMessages({
|
|
4
|
+
en: {
|
|
5
|
+
notFound: "Not found",
|
|
6
|
+
noPage: "There is no page called {}.",
|
|
7
|
+
cancel: "Cancel",
|
|
8
|
+
couldNotDraw: "{} could not be drawn.",
|
|
9
|
+
noComponent: "No component is registered as {}. Export it from protobase.ui.tsx, or load the plugin that provides it.",
|
|
10
|
+
unknownBlock: "{} is not a block this version of the app knows.",
|
|
11
|
+
somethingWrong: "Something went wrong",
|
|
12
|
+
needsRecord: "Needs a record",
|
|
13
|
+
cannotChange: "You cannot change this record",
|
|
14
|
+
cannotDelete: "You cannot delete this record",
|
|
15
|
+
noHandler: (name) => `The app has no handler for "${name}"`,
|
|
16
|
+
noInputComponent: (component, input) => `The app has no component "${component}" for input "${input}"`,
|
|
17
|
+
done: (label) => `${label}: done`,
|
|
18
|
+
failed: (label) => `${label} failed`,
|
|
19
|
+
noAction: (name) => `No action "${name}"`,
|
|
20
|
+
couldNotLoad: "Could not load",
|
|
21
|
+
nothingYet: "Nothing here yet.",
|
|
22
|
+
saved: (label) => `${label}: saved`,
|
|
23
|
+
create: "Create",
|
|
24
|
+
save: "Save",
|
|
25
|
+
notSaved: "Not saved",
|
|
26
|
+
couldNotCount: "Could not count",
|
|
27
|
+
required: "Required",
|
|
28
|
+
wholeNumber: "A whole number",
|
|
29
|
+
number: "A number",
|
|
30
|
+
dateFormat: "A date as YYYY-MM-DD",
|
|
31
|
+
atMost: (max) => `At most ${max} characters`
|
|
32
|
+
},
|
|
33
|
+
nl: {
|
|
34
|
+
notFound: "Niet gevonden",
|
|
35
|
+
noPage: "Er is geen pagina met de naam {}.",
|
|
36
|
+
cancel: "Annuleren",
|
|
37
|
+
couldNotDraw: "{} kon niet worden getekend.",
|
|
38
|
+
noComponent: "Er is geen component geregistreerd als {}. Exporteer die uit protobase.ui.tsx, of laad de plug-in die haar levert.",
|
|
39
|
+
unknownBlock: "{} is geen blok dat deze versie van de app kent.",
|
|
40
|
+
somethingWrong: "Er ging iets mis",
|
|
41
|
+
needsRecord: "Vraagt een record",
|
|
42
|
+
cannotChange: "Je kunt dit record niet wijzigen",
|
|
43
|
+
cannotDelete: "Je kunt dit record niet verwijderen",
|
|
44
|
+
noHandler: (name) => `De app heeft geen handler voor "${name}"`,
|
|
45
|
+
noInputComponent: (component, input) => `De app heeft geen component "${component}" voor invoer "${input}"`,
|
|
46
|
+
done: (label) => `${label}: klaar`,
|
|
47
|
+
failed: (label) => `${label} mislukt`,
|
|
48
|
+
noAction: (name) => `Geen actie "${name}"`,
|
|
49
|
+
couldNotLoad: "Kon niet laden",
|
|
50
|
+
nothingYet: "Hier staat nog niets.",
|
|
51
|
+
saved: (label) => `${label}: bewaard`,
|
|
52
|
+
create: "Maken",
|
|
53
|
+
save: "Bewaren",
|
|
54
|
+
notSaved: "Niet bewaard",
|
|
55
|
+
couldNotCount: "Kon niet tellen",
|
|
56
|
+
required: "Verplicht",
|
|
57
|
+
wholeNumber: "Een geheel getal",
|
|
58
|
+
number: "Een getal",
|
|
59
|
+
dateFormat: "Een datum als JJJJ-MM-DD",
|
|
60
|
+
atMost: (max) => `Hoogstens ${max} tekens`
|
|
61
|
+
}
|
|
62
|
+
});
|
|
63
|
+
//#endregion
|
|
64
|
+
export { text };
|
|
@@ -1,4 +1,6 @@
|
|
|
1
|
+
import { slotted } from "../../locale/slotted.js";
|
|
1
2
|
import { Problem } from "./problem.js";
|
|
3
|
+
import { text } from "./messages.js";
|
|
2
4
|
import { CustomBoundary } from "./custom-boundary.js";
|
|
3
5
|
import { useProjectUi } from "./project-ui.js";
|
|
4
6
|
import { ActionBlock } from "./blocks/action.js";
|
|
@@ -19,7 +21,7 @@ import { TableBlock } from "./blocks/table.js";
|
|
|
19
21
|
import { UsersBlock } from "../../users/users-block.js";
|
|
20
22
|
import { Fragment } from "react";
|
|
21
23
|
import { unmarkFieldRefs } from "@protobase/schema/internal";
|
|
22
|
-
import { Fragment as Fragment$1, jsx
|
|
24
|
+
import { Fragment as Fragment$1, jsx } from "react/jsx-runtime";
|
|
23
25
|
import { isLayoutNode } from "@protobase/layout";
|
|
24
26
|
//#region src/app/pages/render-node.tsx
|
|
25
27
|
const blocks = {
|
|
@@ -66,11 +68,7 @@ const RenderNode = ({ node }) => {
|
|
|
66
68
|
const { components } = useProjectUi();
|
|
67
69
|
if (node.custom) {
|
|
68
70
|
const Custom = components?.[node.type];
|
|
69
|
-
if (!Custom) return /* @__PURE__ */
|
|
70
|
-
"No component is registered as ",
|
|
71
|
-
node.type,
|
|
72
|
-
". Export it from protobase.ui.tsx, or load the plugin that provides it."
|
|
73
|
-
] });
|
|
71
|
+
if (!Custom) return /* @__PURE__ */ jsx(Problem, { children: slotted(text("noComponent"), node.type) });
|
|
74
72
|
return /* @__PURE__ */ jsx(CustomBoundary, {
|
|
75
73
|
name: node.type,
|
|
76
74
|
children: /* @__PURE__ */ jsx(Custom, {
|
|
@@ -80,7 +78,7 @@ const RenderNode = ({ node }) => {
|
|
|
80
78
|
});
|
|
81
79
|
}
|
|
82
80
|
const Block = blocks[node.type];
|
|
83
|
-
if (!Block) return /* @__PURE__ */
|
|
81
|
+
if (!Block) return /* @__PURE__ */ jsx(Problem, { children: slotted(text("unknownBlock"), node.type) });
|
|
84
82
|
return /* @__PURE__ */ jsx(Block, {
|
|
85
83
|
node,
|
|
86
84
|
slots: renderSlots(node),
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { useClient, useResourceClient } from "../../data/api-provider.js";
|
|
2
|
+
import { text } from "./messages.js";
|
|
2
3
|
import { useToast } from "../../toasts/toast-provider.js";
|
|
3
4
|
import { useAdminMeta, usePermissions } from "../meta-gate.js";
|
|
4
5
|
import { useAppAddress } from "../use-app-address.js";
|
|
@@ -9,7 +10,7 @@ import { useRefreshResource } from "./use-layout-data.js";
|
|
|
9
10
|
import { useState } from "react";
|
|
10
11
|
import { fillAddress, updateValues } from "@protobase/schema/internal";
|
|
11
12
|
//#region src/app/pages/use-run-action.ts
|
|
12
|
-
const message = (error) => error instanceof Error ? error.message : "
|
|
13
|
+
const message = (error) => error instanceof Error ? error.message : text("somethingWrong");
|
|
13
14
|
/**
|
|
14
15
|
* The named actions of `resource`'s view, ready to run on `scope` (the record they act on): built-in behaviour first
|
|
15
16
|
* (`update`, `delete`, `link`, and `ai`, which the server runs), else the handler the project registered under the
|
|
@@ -30,13 +31,13 @@ const useActionRunner = (resource, scope, afterRun) => {
|
|
|
30
31
|
const actions = (views[resource]?.actions ?? []).flatMap((action) => actionFor(action, scope?.record) ?? []);
|
|
31
32
|
const blocked = (action) => {
|
|
32
33
|
const kind = action.run?.kind;
|
|
33
|
-
if ((kind === "update" || kind === "delete" || kind === "ai") && !scope) return "
|
|
34
|
-
if ((kind === "update" || kind === "ai") && (!can(permissions, "update") || scope?.permissions?.update === false)) return "
|
|
35
|
-
if (action.run?.kind === "ai" && scope?.permissions?.fields?.[action.run.writes] === "read") return "
|
|
36
|
-
if (kind === "delete" && (!can(permissions, "delete") || scope?.permissions?.delete === false)) return "
|
|
37
|
-
if (!action.run && !handlers?.[action.name]) return
|
|
34
|
+
if ((kind === "update" || kind === "delete" || kind === "ai") && !scope) return text("needsRecord");
|
|
35
|
+
if ((kind === "update" || kind === "ai") && (!can(permissions, "update") || scope?.permissions?.update === false)) return text("cannotChange");
|
|
36
|
+
if (action.run?.kind === "ai" && scope?.permissions?.fields?.[action.run.writes] === "read") return text("cannotChange");
|
|
37
|
+
if (kind === "delete" && (!can(permissions, "delete") || scope?.permissions?.delete === false)) return text("cannotDelete");
|
|
38
|
+
if (!action.run && !handlers?.[action.name]) return text("noHandler", action.name);
|
|
38
39
|
const custom = action.inputs?.find((input) => input.kind === "custom" && !components?.[input.component ?? ""]);
|
|
39
|
-
if (custom) return
|
|
40
|
+
if (custom) return text("noInputComponent", String(custom.component), custom.name);
|
|
40
41
|
};
|
|
41
42
|
const perform = async (action, values) => {
|
|
42
43
|
const { run } = action;
|
|
@@ -60,7 +61,7 @@ const useActionRunner = (resource, scope, afterRun) => {
|
|
|
60
61
|
await refresh(resource);
|
|
61
62
|
toast.show({
|
|
62
63
|
state: "success",
|
|
63
|
-
title:
|
|
64
|
+
title: text("done", action.label)
|
|
64
65
|
});
|
|
65
66
|
afterRun?.(action);
|
|
66
67
|
};
|
|
@@ -69,7 +70,7 @@ const useActionRunner = (resource, scope, afterRun) => {
|
|
|
69
70
|
setRunning(action.name);
|
|
70
71
|
await perform(action).catch((error) => toast.show({
|
|
71
72
|
state: "error",
|
|
72
|
-
title:
|
|
73
|
+
title: text("failed", action.label),
|
|
73
74
|
description: message(error)
|
|
74
75
|
}));
|
|
75
76
|
setRunning(void 0);
|
|
@@ -93,7 +94,7 @@ const useRunAction = (resource, name, scope) => {
|
|
|
93
94
|
const action = runner.actions.find((entry) => entry.name === name);
|
|
94
95
|
return {
|
|
95
96
|
action,
|
|
96
|
-
blocked: action ? runner.blocked(action) :
|
|
97
|
+
blocked: action ? runner.blocked(action) : text("noAction", name),
|
|
97
98
|
running: runner.running === name,
|
|
98
99
|
run: () => action ? runner.run(action) : Promise.resolve(),
|
|
99
100
|
submit: (values) => action ? runner.submit(action, values) : Promise.resolve()
|
package/dist/app/record-page.js
CHANGED
|
@@ -6,6 +6,7 @@ import { Button, buttonClass } from "../primitives/button.js";
|
|
|
6
6
|
import { usePauseFocusRefresh } from "../data/focus-refresh.js";
|
|
7
7
|
import { keys } from "../data/query-keys.js";
|
|
8
8
|
import { useClient } from "../data/api-provider.js";
|
|
9
|
+
import { text } from "./messages.js";
|
|
9
10
|
import { useNavigationGuard } from "./router.js";
|
|
10
11
|
import { useOpenedRecord } from "../data/use-record.js";
|
|
11
12
|
import { humanize } from "../live/naming.js";
|
|
@@ -29,7 +30,6 @@ import { KeyFacts } from "../record-view/key-facts.js";
|
|
|
29
30
|
import { fromDraft, isRequiredOnEdit } from "../live/field-values.js";
|
|
30
31
|
import { fieldHelp, helpShown, recordShown, whenShown } from "./record-mode/record-mode.js";
|
|
31
32
|
import { isChosen, keyValue } from "./create/create-values.js";
|
|
32
|
-
import { text } from "./messages.js";
|
|
33
33
|
import { ErrorBanner } from "./error-banner.js";
|
|
34
34
|
import { useProjectUi } from "./pages/project-ui.js";
|
|
35
35
|
import { shownWidget } from "./field-types/shown-widget.js";
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
import { defineMessages } from "../../locale/messages.js";
|
|
2
|
+
//#region src/app/record-row/messages.ts
|
|
3
|
+
const text = defineMessages({
|
|
4
|
+
en: {
|
|
5
|
+
couldNotLoad: "Could not load",
|
|
6
|
+
noneMatch: (plural) => `No ${plural} match.`,
|
|
7
|
+
viewAll: "View all",
|
|
8
|
+
new: "New",
|
|
9
|
+
previous: (label) => `Previous ${label}`,
|
|
10
|
+
next: (label) => `Next ${label}`,
|
|
11
|
+
couldNotLoadYour: (plural) => `Could not load your ${plural}`,
|
|
12
|
+
noneYet: (plural) => `No ${plural} yet.`
|
|
13
|
+
},
|
|
14
|
+
nl: {
|
|
15
|
+
couldNotLoad: "Kon niet laden",
|
|
16
|
+
noneMatch: (plural) => `Geen ${plural} gevonden.`,
|
|
17
|
+
viewAll: "Alles bekijken",
|
|
18
|
+
new: "Nieuw",
|
|
19
|
+
previous: (label) => `Terug in ${label}`,
|
|
20
|
+
next: (label) => `Verder in ${label}`,
|
|
21
|
+
couldNotLoadYour: (plural) => `Kon je ${plural} niet laden`,
|
|
22
|
+
noneYet: (plural) => `Nog geen ${plural}.`
|
|
23
|
+
}
|
|
24
|
+
});
|
|
25
|
+
//#endregion
|
|
26
|
+
export { text };
|
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import { useIsXs } from "../../lib/use-media-query.js";
|
|
2
1
|
import { formatTag } from "../../locale/active-locale.js";
|
|
2
|
+
import { useIsXs } from "../../lib/use-media-query.js";
|
|
3
3
|
import { Skeleton } from "../../primitives/skeleton.js";
|
|
4
4
|
import { humanize } from "../../live/naming.js";
|
|
5
5
|
import { displaysOf, loadedFields } from "../displays/list-displays.js";
|
|
@@ -11,6 +11,7 @@ import { useLiveRows } from "../../live/use-live-rows.js";
|
|
|
11
11
|
import { viewAllAddress } from "../embedded-list/embedded-display.js";
|
|
12
12
|
import { FilterChipList } from "../searches/filter-chip-list.js";
|
|
13
13
|
import { useFilterChips } from "../searches/use-filter-chips.js";
|
|
14
|
+
import { text } from "./messages.js";
|
|
14
15
|
import { RecordRow } from "./record-row.js";
|
|
15
16
|
import { useMemo } from "react";
|
|
16
17
|
import { ArrowRight } from "lucide-react";
|
|
@@ -77,11 +78,11 @@ const RecordRowBody = ({ model, view, filter, sort = "", display: named, limit,
|
|
|
77
78
|
})]
|
|
78
79
|
}) : null,
|
|
79
80
|
rows.isLoading ? /* @__PURE__ */ jsx(Loading, {}) : rows.error ? /* @__PURE__ */ jsx(ErrorBanner, {
|
|
80
|
-
title: "
|
|
81
|
+
title: text("couldNotLoad"),
|
|
81
82
|
error: rows.error
|
|
82
83
|
}) : !source || source.total === 0 ? /* @__PURE__ */ jsx("p", {
|
|
83
84
|
className: "text-muted-foreground",
|
|
84
|
-
children: empty ??
|
|
85
|
+
children: empty ?? text("noneMatch", plural.toLowerCase())
|
|
85
86
|
}) : /* @__PURE__ */ jsx(RecordRow, {
|
|
86
87
|
model,
|
|
87
88
|
view,
|
|
@@ -100,10 +101,14 @@ const RecordRowBody = ({ model, view, filter, sort = "", display: named, limit,
|
|
|
100
101
|
}),
|
|
101
102
|
onClick: viewAll?.onOpen,
|
|
102
103
|
className: "flex items-center gap-1 self-end font-medium text-primary-text hover:underline",
|
|
103
|
-
children: [viewAll?.label ?? /* @__PURE__ */ jsxs(Fragment$1, { children: [
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
104
|
+
children: [viewAll?.label ?? /* @__PURE__ */ jsxs(Fragment$1, { children: [
|
|
105
|
+
text("viewAll"),
|
|
106
|
+
" ",
|
|
107
|
+
/* @__PURE__ */ jsx("span", {
|
|
108
|
+
className: "sr-only",
|
|
109
|
+
children: plural.toLowerCase()
|
|
110
|
+
})
|
|
111
|
+
] }), /* @__PURE__ */ jsx(ArrowRight, {
|
|
107
112
|
className: "size-3.5",
|
|
108
113
|
"aria-hidden": true
|
|
109
114
|
})]
|
|
@@ -4,6 +4,7 @@ import { usePagedRows } from "../../data-grid/use-paged-rows.js";
|
|
|
4
4
|
import { RecordCard } from "../displays/record-card.js";
|
|
5
5
|
import { rowOffset } from "./row-window.js";
|
|
6
6
|
import { useRowScroll } from "./use-row-scroll.js";
|
|
7
|
+
import { text } from "./messages.js";
|
|
7
8
|
import { ChevronLeft, ChevronRight } from "lucide-react";
|
|
8
9
|
import { jsx, jsxs } from "react/jsx-runtime";
|
|
9
10
|
//#region src/app/record-row/record-row.tsx
|
|
@@ -60,7 +61,7 @@ const RecordRow = ({ model, view, display, source, label, isNew }) => {
|
|
|
60
61
|
display,
|
|
61
62
|
row,
|
|
62
63
|
swipe: false,
|
|
63
|
-
mark: isNew?.(row) ? "
|
|
64
|
+
mark: isNew?.(row) ? text("new") : void 0
|
|
64
65
|
})
|
|
65
66
|
}, row.key);
|
|
66
67
|
}),
|
|
@@ -69,7 +70,7 @@ const RecordRow = ({ model, view, display, source, label, isNew }) => {
|
|
|
69
70
|
}),
|
|
70
71
|
scroll.back && /* @__PURE__ */ jsx("button", {
|
|
71
72
|
type: "button",
|
|
72
|
-
"aria-label":
|
|
73
|
+
"aria-label": text("previous", label.toLowerCase()),
|
|
73
74
|
onClick: () => scroll.page(-1),
|
|
74
75
|
className: `${arrowClass} -left-3`,
|
|
75
76
|
children: /* @__PURE__ */ jsx(ChevronLeft, {
|
|
@@ -79,7 +80,7 @@ const RecordRow = ({ model, view, display, source, label, isNew }) => {
|
|
|
79
80
|
}),
|
|
80
81
|
scroll.on && /* @__PURE__ */ jsx("button", {
|
|
81
82
|
type: "button",
|
|
82
|
-
"aria-label":
|
|
83
|
+
"aria-label": text("next", label.toLowerCase()),
|
|
83
84
|
onClick: () => scroll.page(1),
|
|
84
85
|
className: `${arrowClass} -right-3`,
|
|
85
86
|
children: /* @__PURE__ */ jsx(ChevronRight, {
|
|
@@ -5,6 +5,7 @@ import { useAdminMeta } from "../meta-gate.js";
|
|
|
5
5
|
import { ErrorBanner } from "../error-banner.js";
|
|
6
6
|
import { savedScope } from "../saved/saved-records.js";
|
|
7
7
|
import { useSavedRecords } from "../saved/use-saved-records.js";
|
|
8
|
+
import { text } from "./messages.js";
|
|
8
9
|
import { RecordRowBody } from "./record-row-body.js";
|
|
9
10
|
import { jsx } from "react/jsx-runtime";
|
|
10
11
|
//#region src/app/record-row/saved-record-row.tsx
|
|
@@ -19,14 +20,14 @@ const SavedRecordRow = ({ model, view, empty, ...props }) => {
|
|
|
19
20
|
const plural = (view.names?.plural ?? humanize(model.name)).toLowerCase();
|
|
20
21
|
if (!listed.listOf) return null;
|
|
21
22
|
if (saved.error) return /* @__PURE__ */ jsx(ErrorBanner, {
|
|
22
|
-
title:
|
|
23
|
+
title: text("couldNotLoadYour", plural),
|
|
23
24
|
error: saved.error
|
|
24
25
|
});
|
|
25
26
|
if (!saved.data) return /* @__PURE__ */ jsx(Skeleton, { className: "h-24" });
|
|
26
27
|
const scope = savedScope(listed.model, saved.data);
|
|
27
28
|
if (!scope) return /* @__PURE__ */ jsx("p", {
|
|
28
29
|
className: "text-muted-foreground",
|
|
29
|
-
children: empty ??
|
|
30
|
+
children: empty ?? text("noneYet", plural)
|
|
30
31
|
});
|
|
31
32
|
return /* @__PURE__ */ jsx(RecordRowBody, {
|
|
32
33
|
...props,
|
|
@@ -1,10 +1,11 @@
|
|
|
1
|
+
import { text } from "./messages.js";
|
|
1
2
|
//#region src/app/related-dots/dots-summary.ts
|
|
2
3
|
/**
|
|
3
4
|
* What a related column's dots say, as text: how many records there are, then how many have each status, in the order
|
|
4
5
|
* of the status's values, such as "4 tasks: 1 To do, 3 Done". "No tasks" without any.
|
|
5
6
|
*/
|
|
6
7
|
const dotsSummary = (statuses, { values, label, singular, plural }) => {
|
|
7
|
-
if (statuses.length === 0) return
|
|
8
|
+
if (statuses.length === 0) return text("none", plural);
|
|
8
9
|
const counts = /* @__PURE__ */ new Map();
|
|
9
10
|
for (const status of statuses) if (status !== null && status !== void 0) counts.set(String(status), (counts.get(String(status)) ?? 0) + 1);
|
|
10
11
|
const parts = [...values, ...[...counts.keys()].filter((value) => !values.includes(value))].filter((value) => counts.has(value)).map((value) => `${counts.get(value)} ${label(value)}`);
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { cn } from "../lib/cn.js";
|
|
2
2
|
import { Input } from "../primitives/input.js";
|
|
3
|
+
import { text } from "./messages.js";
|
|
3
4
|
import { humanize } from "../live/naming.js";
|
|
4
5
|
import { labelFieldOf } from "../live/model-helpers.js";
|
|
5
6
|
import { useList } from "../data/use-list.js";
|
|
@@ -27,7 +28,7 @@ const useLoadMoreInView = (list, sentinel, loaded, loadMore) => {
|
|
|
27
28
|
* choose with the mouse or with the arrow keys and Enter; Escape closes the list.
|
|
28
29
|
*/
|
|
29
30
|
const RelationPicker = ({ target, view, value, label, chosen = [value], several = false, invalid, onChange }) => {
|
|
30
|
-
const [text, setText] = useState("");
|
|
31
|
+
const [text$1, setText] = useState("");
|
|
31
32
|
const [open, setOpen] = useState(false);
|
|
32
33
|
const [active, setActive] = useState(-1);
|
|
33
34
|
const [list, setList] = useState(null);
|
|
@@ -36,7 +37,7 @@ const RelationPicker = ({ target, view, value, label, chosen = [value], several
|
|
|
36
37
|
const keyField = target.primaryKey[0];
|
|
37
38
|
const labelField = labelFieldOf(target, view);
|
|
38
39
|
const [refused, setRefused] = useState(false);
|
|
39
|
-
const query = relationOptionsQuery(target, labelField, text, refused);
|
|
40
|
+
const query = relationOptionsQuery(target, labelField, text$1, refused);
|
|
40
41
|
const rows = useList(target.name, {
|
|
41
42
|
filter: query.filter,
|
|
42
43
|
fields: [keyField, labelField],
|
|
@@ -46,7 +47,7 @@ const RelationPicker = ({ target, view, value, label, chosen = [value], several
|
|
|
46
47
|
useEffect(() => {
|
|
47
48
|
if (expensive) setRefused(true);
|
|
48
49
|
}, [expensive]);
|
|
49
|
-
const items = query.local ? rows.items.filter((item) => titleMatches(item[labelField], text)) : rows.items;
|
|
50
|
+
const items = query.local ? rows.items.filter((item) => titleMatches(item[labelField], text$1)) : rows.items;
|
|
50
51
|
const more = Boolean(rows.hasNextPage);
|
|
51
52
|
const loading = rows.isPending || rows.isFetchingNextPage;
|
|
52
53
|
const { fetchNextPage } = rows;
|
|
@@ -99,8 +100,8 @@ const RelationPicker = ({ target, view, value, label, chosen = [value], several
|
|
|
99
100
|
"aria-controls": listId,
|
|
100
101
|
"aria-activedescendant": open && items[active] ? optionId(active) : void 0,
|
|
101
102
|
invalid,
|
|
102
|
-
placeholder:
|
|
103
|
-
value: open ? text : label ?? value,
|
|
103
|
+
placeholder: text("searchPlural", (view?.names?.plural ?? humanize(target.name)).toLowerCase()),
|
|
104
|
+
value: open ? text$1 : label ?? value,
|
|
104
105
|
onFocus: () => setOpen(true),
|
|
105
106
|
onBlur: close,
|
|
106
107
|
onKeyDown,
|
|
@@ -133,15 +134,15 @@ const RelationPicker = ({ target, view, value, label, chosen = [value], several
|
|
|
133
134
|
}),
|
|
134
135
|
rows.error && !expensive && /* @__PURE__ */ jsx("li", {
|
|
135
136
|
className: "px-3 py-1.5 text-muted-foreground",
|
|
136
|
-
children: "
|
|
137
|
+
children: text("couldNotLoadOptions")
|
|
137
138
|
}),
|
|
138
139
|
!more && !loading && !rows.error && items.length === 0 && /* @__PURE__ */ jsx("li", {
|
|
139
140
|
className: "px-3 py-1.5 text-muted-foreground",
|
|
140
|
-
children: "
|
|
141
|
+
children: text("noMatches")
|
|
141
142
|
}),
|
|
142
143
|
loading && /* @__PURE__ */ jsx("li", {
|
|
143
144
|
className: "px-3 py-1.5 text-muted-foreground",
|
|
144
|
-
children: "
|
|
145
|
+
children: text("loading")
|
|
145
146
|
})
|
|
146
147
|
]
|
|
147
148
|
})]
|
package/dist/app/router.js
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { RouteLink } from "../primitives/route-link.js";
|
|
2
2
|
import { Button } from "../primitives/button.js";
|
|
3
3
|
import { Dialog } from "../primitives/dialog.js";
|
|
4
|
+
import { text } from "./messages.js";
|
|
4
5
|
import { createContext, useCallback, useContext, useEffect, useMemo, useRef, useState } from "react";
|
|
5
6
|
import { Fragment as Fragment$1, jsx, jsxs } from "react/jsx-runtime";
|
|
6
7
|
//#region src/app/router.tsx
|
|
@@ -103,16 +104,16 @@ const Router = ({ basePath = "", initialUrl, children }) => {
|
|
|
103
104
|
return /* @__PURE__ */ jsxs(RouterContext.Provider, {
|
|
104
105
|
value,
|
|
105
106
|
children: [children, pending && /* @__PURE__ */ jsx(Dialog, {
|
|
106
|
-
title: "
|
|
107
|
-
description: "
|
|
107
|
+
title: text("leaveTitle"),
|
|
108
|
+
description: text("leaveDescription"),
|
|
108
109
|
onClose: () => setPending(void 0),
|
|
109
110
|
actions: /* @__PURE__ */ jsxs(Fragment$1, { children: [/* @__PURE__ */ jsx(Button, {
|
|
110
111
|
onClick: () => setPending(void 0),
|
|
111
|
-
children: "
|
|
112
|
+
children: text("stay")
|
|
112
113
|
}), /* @__PURE__ */ jsx(Button, {
|
|
113
114
|
variant: "primary",
|
|
114
115
|
onClick: leave,
|
|
115
|
-
children: "
|
|
116
|
+
children: text("leave")
|
|
116
117
|
})] })
|
|
117
118
|
})]
|
|
118
119
|
});
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
import { defineMessages } from "../../locale/messages.js";
|
|
2
|
+
//#region src/app/runtime/messages.ts
|
|
3
|
+
const text = defineMessages({
|
|
4
|
+
en: {
|
|
5
|
+
immediate: "Updates as soon as a release is out",
|
|
6
|
+
weekly: "Updates at a set time every week",
|
|
7
|
+
scheduled: "Updates at the time set for this release",
|
|
8
|
+
manual: "Updates only when asked to",
|
|
9
|
+
job: "Preparing the update",
|
|
10
|
+
build: "Building",
|
|
11
|
+
updatingNow: "Updating now",
|
|
12
|
+
started: (stage, ago) => `${stage}, started ${ago}`,
|
|
13
|
+
nextOn: (policy, when) => `${policy}: next on ${when}`,
|
|
14
|
+
succeeded: (version, when) => `Updated to ${version} on ${when}`,
|
|
15
|
+
unchanged: (version, when) => `The update to ${version} on ${when} changed nothing`,
|
|
16
|
+
failed: (version, when) => `The update to ${version} failed on ${when}`,
|
|
17
|
+
alreadyLatest: "Already on the latest version, or an update is running.",
|
|
18
|
+
onlyAdmin: "Only an admin can update the runtime.",
|
|
19
|
+
couldNotStart: "The update could not be started. Try again later.",
|
|
20
|
+
runtimeUpdate: "Runtime update",
|
|
21
|
+
close: "Close",
|
|
22
|
+
updating: "Updating…",
|
|
23
|
+
updateNow: "Update now",
|
|
24
|
+
currentVersion: "Current version",
|
|
25
|
+
latestVersion: "Latest version",
|
|
26
|
+
checkFailed: "The status could not be checked again; this is the last one read.",
|
|
27
|
+
updateAvailable: "Update available",
|
|
28
|
+
update: "Update"
|
|
29
|
+
},
|
|
30
|
+
nl: {
|
|
31
|
+
immediate: "Werkt bij zodra er een release is",
|
|
32
|
+
weekly: "Werkt elke week op een vast moment bij",
|
|
33
|
+
scheduled: "Werkt bij op het moment dat voor deze release is ingesteld",
|
|
34
|
+
manual: "Werkt alleen bij als erom wordt gevraagd",
|
|
35
|
+
job: "De update voorbereiden",
|
|
36
|
+
build: "Bouwen",
|
|
37
|
+
updatingNow: "Nu aan het bijwerken",
|
|
38
|
+
started: (stage, ago) => `${stage}, begonnen ${ago}`,
|
|
39
|
+
nextOn: (policy, when) => `${policy}: volgende op ${when}`,
|
|
40
|
+
succeeded: (version, when) => `Bijgewerkt naar ${version} op ${when}`,
|
|
41
|
+
unchanged: (version, when) => `De update naar ${version} op ${when} veranderde niets`,
|
|
42
|
+
failed: (version, when) => `De update naar ${version} mislukte op ${when}`,
|
|
43
|
+
alreadyLatest: "Al op de nieuwste versie, of er loopt een update.",
|
|
44
|
+
onlyAdmin: "Alleen een beheerder kan de runtime bijwerken.",
|
|
45
|
+
couldNotStart: "De update kon niet worden gestart. Probeer het later opnieuw.",
|
|
46
|
+
runtimeUpdate: "Runtime-update",
|
|
47
|
+
close: "Sluiten",
|
|
48
|
+
updating: "Bijwerken…",
|
|
49
|
+
updateNow: "Nu bijwerken",
|
|
50
|
+
currentVersion: "Huidige versie",
|
|
51
|
+
latestVersion: "Nieuwste versie",
|
|
52
|
+
checkFailed: "De status kon niet opnieuw worden gecontroleerd; dit is de laatst gelezen.",
|
|
53
|
+
updateAvailable: "Update beschikbaar",
|
|
54
|
+
update: "Bijwerken"
|
|
55
|
+
}
|
|
56
|
+
});
|
|
57
|
+
//#endregion
|
|
58
|
+
export { text };
|
|
@@ -1,50 +1,37 @@
|
|
|
1
1
|
import { viewerZone } from "../../format/zone.js";
|
|
2
2
|
import { formatAgo, formatDateTime } from "../../format/date.js";
|
|
3
|
+
import { text } from "./messages.js";
|
|
3
4
|
import { isApiError } from "@protobase/client";
|
|
4
5
|
//#region src/app/runtime/runtime-status.ts
|
|
5
6
|
/** The top bar shows the update button only when there is an update and the endpoint wants it shown. */
|
|
6
7
|
const showsUpdateButton = (status) => Boolean(status?.updateAvailable && status.indicator);
|
|
7
8
|
/** How often the status is read again: rarely, and every few seconds while an update runs so its progress shows. */
|
|
8
9
|
const refreshMs = (updating) => updating ? 5e3 : 18e5;
|
|
9
|
-
const policyLabels = {
|
|
10
|
-
immediate: "Updates as soon as a release is out",
|
|
11
|
-
weekly: "Updates at a set time every week",
|
|
12
|
-
scheduled: "Updates at the time set for this release",
|
|
13
|
-
manual: "Updates only when asked to"
|
|
14
|
-
};
|
|
15
|
-
const stageLabels = {
|
|
16
|
-
job: "Preparing the update",
|
|
17
|
-
build: "Building"
|
|
18
|
-
};
|
|
19
10
|
const runningText = (status, now) => {
|
|
20
|
-
if (!status.updateStage) return "
|
|
21
|
-
const label =
|
|
11
|
+
if (!status.updateStage) return text("updatingNow");
|
|
12
|
+
const label = text(status.updateStage);
|
|
22
13
|
if (!status.updateStartedAt) return label;
|
|
23
14
|
const seconds = Math.max(0, Math.floor((now - Date.parse(status.updateStartedAt)) / 1e3));
|
|
24
|
-
return
|
|
15
|
+
return text("started", label, formatAgo(seconds));
|
|
25
16
|
};
|
|
26
17
|
/** How far the running update is, or when the latest version will be applied on its own, in words; `now` in ms. */
|
|
27
18
|
const nextUpdateText = (status, now = Date.now(), zone = viewerZone()) => {
|
|
28
19
|
if (status.updating) return runningText(status, now);
|
|
29
|
-
if (status.nextUpdateAt) return
|
|
30
|
-
return
|
|
20
|
+
if (status.nextUpdateAt) return text("nextOn", text(status.policy), formatDateTime(Date.parse(status.nextUpdateAt), zone));
|
|
21
|
+
return text(status.policy);
|
|
31
22
|
};
|
|
32
23
|
/** How the last update ended, in words, with the endpoint's message for the owner when it has one. */
|
|
33
24
|
const lastUpdateText = (last, zone = viewerZone()) => {
|
|
34
25
|
const when = formatDateTime(Date.parse(last.finishedAt), zone);
|
|
35
|
-
const headline =
|
|
36
|
-
succeeded: `Updated to ${last.toVersion} on ${when}`,
|
|
37
|
-
unchanged: `The update to ${last.toVersion} on ${when} changed nothing`,
|
|
38
|
-
failed: `The update to ${last.toVersion} failed on ${when}`
|
|
39
|
-
}[last.outcome];
|
|
26
|
+
const headline = text(last.outcome, last.toVersion, when);
|
|
40
27
|
return last.message ? `${headline}: ${last.message}` : `${headline}.`;
|
|
41
28
|
};
|
|
42
29
|
/** Why "Update now" failed, in words: the endpoint's own message for a refusal, when it sends one. */
|
|
43
30
|
const updateErrorText = (error) => {
|
|
44
31
|
if (isApiError(error) && error.status < 500 && error.problem.detail) return error.problem.detail;
|
|
45
|
-
if (isApiError(error) && error.status === 409) return "
|
|
46
|
-
if (isApiError(error) && error.status === 403) return "
|
|
47
|
-
return "
|
|
32
|
+
if (isApiError(error) && error.status === 409) return text("alreadyLatest");
|
|
33
|
+
if (isApiError(error) && error.status === 403) return text("onlyAdmin");
|
|
34
|
+
return text("couldNotStart");
|
|
48
35
|
};
|
|
49
36
|
//#endregion
|
|
50
37
|
export { lastUpdateText, nextUpdateText, refreshMs, showsUpdateButton, updateErrorText };
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { Spinner } from "../../primitives/spinner.js";
|
|
2
2
|
import { Button } from "../../primitives/button.js";
|
|
3
3
|
import { Dialog } from "../../primitives/dialog.js";
|
|
4
|
+
import { text } from "./messages.js";
|
|
4
5
|
import { lastUpdateText, nextUpdateText, updateErrorText } from "./runtime-status.js";
|
|
5
6
|
import { Fragment, jsx, jsxs } from "react/jsx-runtime";
|
|
6
7
|
//#region src/app/runtime/runtime-update-dialog.tsx
|
|
@@ -21,7 +22,7 @@ const Row = ({ label, value }) => /* @__PURE__ */ jsxs("div", {
|
|
|
21
22
|
const RuntimeUpdateDialog = ({ status, requesting, error, checkError, onUpdate, onClose }) => {
|
|
22
23
|
const last = status.updating ? void 0 : status.lastUpdate;
|
|
23
24
|
return /* @__PURE__ */ jsxs(Dialog, {
|
|
24
|
-
title: "
|
|
25
|
+
title: text("runtimeUpdate"),
|
|
25
26
|
description: status.updating ? /* @__PURE__ */ jsxs("span", {
|
|
26
27
|
className: "inline-flex items-center gap-1.5",
|
|
27
28
|
children: [/* @__PURE__ */ jsx(Spinner, { className: "size-3.5" }), nextUpdateText(status)]
|
|
@@ -29,21 +30,21 @@ const RuntimeUpdateDialog = ({ status, requesting, error, checkError, onUpdate,
|
|
|
29
30
|
onClose,
|
|
30
31
|
actions: /* @__PURE__ */ jsxs(Fragment, { children: [/* @__PURE__ */ jsx(Button, {
|
|
31
32
|
onClick: onClose,
|
|
32
|
-
children: "
|
|
33
|
+
children: text("close")
|
|
33
34
|
}), /* @__PURE__ */ jsx(Button, {
|
|
34
35
|
variant: "primary",
|
|
35
36
|
loading: requesting,
|
|
36
37
|
disabled: status.updating || !status.updateAvailable,
|
|
37
38
|
onClick: onUpdate,
|
|
38
|
-
children: status.updating ? "
|
|
39
|
+
children: status.updating ? text("updating") : text("updateNow")
|
|
39
40
|
})] }),
|
|
40
41
|
children: [
|
|
41
42
|
/* @__PURE__ */ jsx(Row, {
|
|
42
|
-
label: "
|
|
43
|
+
label: text("currentVersion"),
|
|
43
44
|
value: status.version
|
|
44
45
|
}),
|
|
45
46
|
/* @__PURE__ */ jsx(Row, {
|
|
46
|
-
label: "
|
|
47
|
+
label: text("latestVersion"),
|
|
47
48
|
value: status.latest
|
|
48
49
|
}),
|
|
49
50
|
last && /* @__PURE__ */ jsx("p", {
|
|
@@ -57,7 +58,7 @@ const RuntimeUpdateDialog = ({ status, requesting, error, checkError, onUpdate,
|
|
|
57
58
|
}),
|
|
58
59
|
checkError !== void 0 && /* @__PURE__ */ jsx("p", {
|
|
59
60
|
className: "mt-3 text-muted-foreground",
|
|
60
|
-
children: "
|
|
61
|
+
children: text("checkFailed")
|
|
61
62
|
})
|
|
62
63
|
]
|
|
63
64
|
});
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { Button } from "../../primitives/button.js";
|
|
2
|
+
import { text } from "./messages.js";
|
|
2
3
|
import { showsUpdateButton } from "./runtime-status.js";
|
|
3
4
|
import { RuntimeUpdateDialog } from "./runtime-update-dialog.js";
|
|
4
5
|
import { useRuntimeClient, useRuntimeStatus } from "./use-runtime-status.js";
|
|
@@ -17,13 +18,13 @@ const RuntimeUpdate = ({ client }) => {
|
|
|
17
18
|
return /* @__PURE__ */ jsxs(Fragment$1, { children: [/* @__PURE__ */ jsxs(Button, {
|
|
18
19
|
size: "sm",
|
|
19
20
|
variant: "ghost",
|
|
20
|
-
"aria-label": "
|
|
21
|
-
title: "
|
|
21
|
+
"aria-label": text("updateAvailable"),
|
|
22
|
+
title: text("updateAvailable"),
|
|
22
23
|
onClick: () => setOpen(true),
|
|
23
24
|
className: "max-md:size-9 max-md:p-0",
|
|
24
25
|
children: [/* @__PURE__ */ jsx(ArrowUpCircle, { className: "size-3.5 text-primary" }), /* @__PURE__ */ jsx("span", {
|
|
25
26
|
className: "max-md:sr-only",
|
|
26
|
-
children: "
|
|
27
|
+
children: text("update")
|
|
27
28
|
})]
|
|
28
29
|
}), open && /* @__PURE__ */ jsx(RuntimeUpdateDialog, {
|
|
29
30
|
status,
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { cn } from "../../lib/cn.js";
|
|
2
2
|
import { SignInPrompt } from "../../auth/sign-in-prompt.js";
|
|
3
|
+
import { text } from "./messages.js";
|
|
3
4
|
import { useFavorite } from "./use-favorite.js";
|
|
4
5
|
import { useState } from "react";
|
|
5
6
|
import { Heart } from "lucide-react";
|
|
@@ -14,7 +15,7 @@ const FavoriteButton = ({ favorite, recordKey, target, className }) => {
|
|
|
14
15
|
const state = useFavorite(favorite, recordKey, target);
|
|
15
16
|
const [prompting, setPrompting] = useState(false);
|
|
16
17
|
if (!state.mode) return null;
|
|
17
|
-
const label = favorite.label ?? "
|
|
18
|
+
const label = favorite.label ?? text("favorite");
|
|
18
19
|
const base = cn("absolute right-2 top-2 flex size-8 items-center justify-center rounded-full bg-background/85 text-foreground shadow-sm transition-colors hover:bg-background focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-primary/30", className);
|
|
19
20
|
if (state.mode === "sign-in") return /* @__PURE__ */ jsxs(Fragment$1, { children: [/* @__PURE__ */ jsx("button", {
|
|
20
21
|
type: "button",
|