@protobase/ui 0.25.2 → 0.25.3

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.
Files changed (255) hide show
  1. package/dist/account/account-pages.js +3 -2
  2. package/dist/account/account-security-page.js +9 -4
  3. package/dist/account/backup-codes.js +7 -6
  4. package/dist/account/messages.js +214 -0
  5. package/dist/account/passkeys-section.js +23 -22
  6. package/dist/account/password-dialog.js +3 -2
  7. package/dist/account/sign-in-policy-form.js +11 -10
  8. package/dist/account/sign-in-policy-labels.js +30 -29
  9. package/dist/account/sign-in-policy-page.js +11 -6
  10. package/dist/account/sign-in-providers-section.js +6 -5
  11. package/dist/account/staff-sign-in-log.js +11 -10
  12. package/dist/account/totp-qr.js +2 -1
  13. package/dist/account/two-factor-section.js +24 -23
  14. package/dist/account/two-factor-setup.js +18 -17
  15. package/dist/action-card/action-card.js +2 -1
  16. package/dist/action-card/messages.js +8 -0
  17. package/dist/activity-chart/activity-chart.js +3 -2
  18. package/dist/activity-chart/messages.js +14 -0
  19. package/dist/app/app.d.ts +1 -1
  20. package/dist/app/app.js +20 -21
  21. package/dist/app/assistant-shell.js +2 -1
  22. package/dist/app/batch-failure.js +2 -1
  23. package/dist/app/breadcrumb-for.js +4 -3
  24. package/dist/app/charts/chart-groups.js +3 -2
  25. package/dist/app/charts/chart-view.js +3 -2
  26. package/dist/app/charts/group-bars.js +2 -1
  27. package/dist/app/charts/messages.js +26 -0
  28. package/dist/app/charts/period-chart.js +3 -2
  29. package/dist/app/charts/split-points.js +2 -1
  30. package/dist/app/comments/comment-composer.js +19 -18
  31. package/dist/app/comments/comments-section.js +4 -3
  32. package/dist/app/comments/messages.js +26 -0
  33. package/dist/app/create/create-page.js +11 -18
  34. package/dist/app/custom-section.js +4 -6
  35. package/dist/app/displays/board-column.js +6 -5
  36. package/dist/app/displays/board-columns.js +3 -2
  37. package/dist/app/displays/board-display.js +13 -15
  38. package/dist/app/displays/calendar-controls.js +3 -2
  39. package/dist/app/displays/calendar-display.js +12 -17
  40. package/dist/app/displays/card-carousel.js +4 -3
  41. package/dist/app/displays/cards-display.js +2 -1
  42. package/dist/app/displays/custom-display.js +4 -6
  43. package/dist/app/displays/display-frame.js +3 -2
  44. package/dist/app/displays/list-cards.js +5 -8
  45. package/dist/app/displays/list-displays.js +3 -3
  46. package/dist/app/displays/map-display.js +15 -14
  47. package/dist/app/displays/messages.js +128 -2
  48. package/dist/app/displays/period-controls.js +4 -3
  49. package/dist/app/displays/record-card.js +7 -6
  50. package/dist/app/displays/timeline-display.js +17 -26
  51. package/dist/app/displays/timeline-range.js +2 -1
  52. package/dist/app/displays/use-board-move.js +2 -2
  53. package/dist/app/displays/use-calendar-move.js +2 -1
  54. package/dist/app/document-title.js +2 -1
  55. package/dist/app/export/builder-port.js +2 -1
  56. package/dist/app/export/export-dialog.js +36 -40
  57. package/dist/app/export/export-worker.js +2 -0
  58. package/dist/app/export/messages.js +78 -0
  59. package/dist/app/export/run-export.js +5 -2
  60. package/dist/app/export/sink.js +4 -3
  61. package/dist/app/export/use-export-flow.js +4 -3
  62. package/dist/app/field-editor.js +4 -3
  63. package/dist/app/field-types/messages.js +8 -0
  64. package/dist/app/field-types/widget-part.js +2 -1
  65. package/dist/app/file-field.js +11 -14
  66. package/dist/app/gallery/gallery-section.js +4 -3
  67. package/dist/app/gallery/lightbox.js +5 -4
  68. package/dist/app/gallery/messages.js +30 -0
  69. package/dist/app/gallery/photo-details.js +3 -2
  70. package/dist/app/hand-order/hand-sorted-list.js +3 -6
  71. package/dist/app/hand-order/messages.js +4 -2
  72. package/dist/app/history/describe-change.js +3 -8
  73. package/dist/app/history/messages.js +32 -0
  74. package/dist/app/import/convert.js +20 -19
  75. package/dist/app/import/failed-rows.js +3 -2
  76. package/dist/app/import/import-button.js +2 -1
  77. package/dist/app/import/import-check-step.js +19 -35
  78. package/dist/app/import/import-dialog.js +13 -17
  79. package/dist/app/import/import-file-step.js +5 -9
  80. package/dist/app/import/import-mapping-step.js +32 -35
  81. package/dist/app/import/import-result-step.js +13 -12
  82. package/dist/app/import/messages.js +200 -0
  83. package/dist/app/import/parse-worker.js +2 -0
  84. package/dist/app/import/prepare-rows.js +5 -4
  85. package/dist/app/import/sheet-cell.js +2 -1
  86. package/dist/app/import/sheet-file.js +3 -2
  87. package/dist/app/import/sheet-source.js +7 -3
  88. package/dist/app/import/xlsx-reader.js +4 -3
  89. package/dist/app/import/xlsx-xml.js +2 -1
  90. package/dist/app/import/zip-reader.js +4 -3
  91. package/dist/app/list-edit/use-list-edits.js +1 -1
  92. package/dist/app/list-group-chart.js +2 -1
  93. package/dist/app/list-page.js +1 -1
  94. package/dist/app/map/map-layer-switch.js +3 -2
  95. package/dist/app/map/messages.js +14 -0
  96. package/dist/app/messages.js +116 -2
  97. package/dist/app/meta-gate.js +1 -1
  98. package/dist/app/nav-from-meta.js +11 -10
  99. package/dist/app/pages/action-inputs/action-inputs-dialog.js +3 -2
  100. package/dist/app/pages/action-inputs/input-answers.js +6 -5
  101. package/dist/app/pages/blocks/card-row.js +3 -2
  102. package/dist/app/pages/blocks/modal-form.js +5 -4
  103. package/dist/app/pages/blocks/record-card.js +3 -2
  104. package/dist/app/pages/blocks/stat.js +2 -1
  105. package/dist/app/pages/composed-page.js +6 -8
  106. package/dist/app/pages/confirm-action.js +2 -1
  107. package/dist/app/pages/custom-boundary.js +4 -2
  108. package/dist/app/pages/messages.js +64 -0
  109. package/dist/app/pages/render-node.js +5 -7
  110. package/dist/app/pages/use-run-action.js +11 -10
  111. package/dist/app/record-page.js +1 -1
  112. package/dist/app/record-row/messages.js +26 -0
  113. package/dist/app/record-row/record-row-body.js +12 -7
  114. package/dist/app/record-row/record-row.js +4 -3
  115. package/dist/app/record-row/saved-record-row.js +3 -2
  116. package/dist/app/related-dots/dots-summary.js +2 -1
  117. package/dist/app/related-dots/messages.js +8 -0
  118. package/dist/app/relation-picker.js +9 -8
  119. package/dist/app/router.js +5 -4
  120. package/dist/app/runtime/messages.js +58 -0
  121. package/dist/app/runtime/runtime-status.js +10 -23
  122. package/dist/app/runtime/runtime-update-dialog.js +7 -6
  123. package/dist/app/runtime/runtime-update.js +4 -3
  124. package/dist/app/saved/favorite-button.js +2 -1
  125. package/dist/app/saved/messages.js +22 -0
  126. package/dist/app/saved/saved-list.js +4 -7
  127. package/dist/app/saved/use-favorite.js +3 -2
  128. package/dist/app/search/messages.js +6 -2
  129. package/dist/app/search/navigation-hits.js +9 -7
  130. package/dist/app/searches/filter-chip-list.js +2 -1
  131. package/dist/app/searches/messages.js +96 -0
  132. package/dist/app/searches/save-search-button.js +5 -4
  133. package/dist/app/searches/saved-search-rows.js +3 -2
  134. package/dist/app/searches/saved-search-section.js +6 -5
  135. package/dist/app/searches/saved-search.js +4 -3
  136. package/dist/app/searches/saved-searches.js +15 -25
  137. package/dist/app/searches/search-controls.js +5 -4
  138. package/dist/app/searches/search-diff.js +4 -2
  139. package/dist/app/searches/search-name-dialog.js +4 -3
  140. package/dist/app/searches/update-search-dialog.js +15 -30
  141. package/dist/app/searches/use-save-search.js +8 -7
  142. package/dist/app/searches/use-search-row.js +4 -3
  143. package/dist/app/sensitive-field.js +5 -4
  144. package/dist/app/source-notice.js +13 -8
  145. package/dist/app/staff-banner.js +12 -19
  146. package/dist/app-shell/global-search.js +2 -2
  147. package/dist/app-shell/messages.js +4 -2
  148. package/dist/assistant-dock/assistant-dock.js +5 -5
  149. package/dist/assistant-dock/messages.js +26 -0
  150. package/dist/assistant-dock/use-assistant.js +2 -1
  151. package/dist/assistant-dock/use-backend-data.js +2 -1
  152. package/dist/assistant-dock/widget-view.js +3 -2
  153. package/dist/auth/auth-gate.js +10 -5
  154. package/dist/auth/auth-locale.js +14 -0
  155. package/dist/auth/auth-messages.js +122 -43
  156. package/dist/auth/auth-provider.js +6 -1
  157. package/dist/auth/code-form.js +2 -1
  158. package/dist/auth/email-code-form.js +11 -13
  159. package/dist/auth/email-code-page.js +2 -1
  160. package/dist/auth/first-run-page.js +6 -5
  161. package/dist/auth/forgot-password-form.js +14 -16
  162. package/dist/auth/forgot-password-page.js +2 -1
  163. package/dist/auth/messages.js +200 -0
  164. package/dist/auth/provider-sign-in-page.js +5 -5
  165. package/dist/auth/resend-code-button.js +2 -1
  166. package/dist/auth/reset-password-form.js +11 -10
  167. package/dist/auth/reset-password-page.js +4 -3
  168. package/dist/auth/setup-required-form.js +7 -9
  169. package/dist/auth/setup-required-page.js +7 -2
  170. package/dist/auth/sign-in-form.js +15 -18
  171. package/dist/auth/sign-in-prompt.js +4 -3
  172. package/dist/auth/sign-up-code-form.js +11 -13
  173. package/dist/auth/sign-up-details-form.js +14 -21
  174. package/dist/auth/sign-up-email-form.js +8 -7
  175. package/dist/auth/sign-up-page.js +2 -1
  176. package/dist/auth/staff-sign-in-form.js +14 -17
  177. package/dist/auth/two-factor-form.js +17 -16
  178. package/dist/auth/user-to-shell.js +2 -1
  179. package/dist/chat/composer.js +8 -7
  180. package/dist/chat/messages.js +14 -0
  181. package/dist/compact-table/compact-table.js +4 -8
  182. package/dist/compact-table/messages.js +18 -0
  183. package/dist/data/meta-data.d.ts +1 -1
  184. package/dist/data/use-update-record.js +1 -1
  185. package/dist/inline-child-table/inline-child-table.js +12 -12
  186. package/dist/inline-child-table/line-row.js +10 -9
  187. package/dist/inline-child-table/messages.js +52 -0
  188. package/dist/inline-child-table/totals-footer.js +5 -8
  189. package/dist/invitations/invite-form.js +3 -2
  190. package/dist/invitations/link-notice.js +6 -4
  191. package/dist/invitations/messages.js +24 -0
  192. package/dist/invitations/pending-invitation-row.js +4 -3
  193. package/dist/lists/messages.js +8 -0
  194. package/dist/lists/paged-search-list.js +2 -1
  195. package/dist/locale/slotted.js +14 -0
  196. package/dist/markdown/markdown-editor.js +6 -5
  197. package/dist/markdown/messages.js +16 -0
  198. package/dist/organizations/all-organizations.js +6 -5
  199. package/dist/organizations/app-roles-picker.js +2 -1
  200. package/dist/organizations/invitation-page.js +39 -49
  201. package/dist/organizations/invitations-page.js +11 -10
  202. package/dist/organizations/members-page.js +15 -10
  203. package/dist/organizations/messages.js +180 -0
  204. package/dist/organizations/organization-page.js +27 -26
  205. package/dist/organizations/organization-paths.js +16 -13
  206. package/dist/organizations/organization-row.js +3 -2
  207. package/dist/organizations/organizations-page.js +17 -16
  208. package/dist/organizations/use-organizations.js +2 -1
  209. package/dist/package.js +1 -1
  210. package/dist/plugins/before-sign-in.js +6 -1
  211. package/dist/plugins/load-plugin.d.ts +1 -1
  212. package/dist/plugins/load-plugin.js +3 -2
  213. package/dist/plugins/messages.js +36 -0
  214. package/dist/plugins/plugin-gate.js +6 -1
  215. package/dist/plugins/plugin-manifest.js +8 -7
  216. package/dist/plugins/plugin-notice.js +8 -12
  217. package/dist/primitives/messages.js +16 -0
  218. package/dist/primitives/sheet.js +2 -1
  219. package/dist/primitives/spinner.js +2 -1
  220. package/dist/primitives/token.js +2 -1
  221. package/dist/record-view/messages.js +4 -2
  222. package/dist/record-view/record-tabs.js +2 -1
  223. package/dist/roles/messages.js +14 -0
  224. package/dist/roles/use-active-role.js +3 -2
  225. package/dist/shell/assets/app-CW3U56yM.js +69 -0
  226. package/dist/shell/assets/cn-Bi0ZEjD9.js +1 -0
  227. package/dist/shell/assets/export-worker-ClACk00b.js +2 -0
  228. package/dist/shell/assets/{index-xY0Mvv6V.js → index-B7cIjCuz.js} +2 -2
  229. package/dist/shell/assets/maplibre-CVG5L9FD.js +814 -0
  230. package/dist/shell/assets/{markdown-content-ukDTCu6u.js → markdown-content-CtGUP4W6.js} +1 -1
  231. package/dist/shell/assets/parse-worker-UhbwEjS0.js +5 -0
  232. package/dist/shell/assets/preload-helper-BX9CcZkY.js +1 -0
  233. package/dist/shell/assets/{record-map-BJmYZ7kQ.js → record-map-Cw3_NcUl.js} +1 -1
  234. package/dist/shell/assets/records-map-D4f3ZeDL.js +1 -0
  235. package/dist/shell/assets/shared-protobase-ui-CB_i50NP.js +1 -0
  236. package/dist/shell/index.html +5 -3
  237. package/dist/shell/protobase.shell.json +1 -1
  238. package/dist/shell/protobase.sw.js +2 -2
  239. package/dist/side-panel/messages.js +8 -0
  240. package/dist/side-panel/side-panel.js +2 -1
  241. package/dist/toasts/messages.js +8 -0
  242. package/dist/toasts/toast.js +2 -1
  243. package/dist/users/app-invitations.js +5 -4
  244. package/dist/users/create-user-form.js +9 -8
  245. package/dist/users/messages.js +52 -0
  246. package/dist/users/use-user-roles.js +2 -1
  247. package/dist/users/user-list.js +6 -5
  248. package/dist/users/user-row.js +5 -4
  249. package/package.json +4 -4
  250. package/dist/shell/assets/app-C-OLrsRR.js +0 -69
  251. package/dist/shell/assets/export-worker-Bz57Pf5g.js +0 -2
  252. package/dist/shell/assets/maplibre-Dov6nhPX.js +0 -814
  253. package/dist/shell/assets/parse-worker-BtcY0dQw.js +0 -5
  254. package/dist/shell/assets/records-map-C1_gDKHR.js +0 -1
  255. package/dist/shell/assets/shared-protobase-ui-CFYJAbMR.js +0 -1
@@ -4,7 +4,8 @@ import { useAuth } from "../auth/auth-provider.js";
4
4
  import { Button } from "../primitives/button.js";
5
5
  import { Dialog } from "../primitives/dialog.js";
6
6
  import { AccountSection, SectionError } from "../account/account-section.js";
7
- import { managesOrganization, organizationRoleLabels } from "./organization-paths.js";
7
+ import { text } from "./messages.js";
8
+ import { managesOrganization, organizationRoleLabel } from "./organization-paths.js";
8
9
  import { useAction } from "./use-action.js";
9
10
  import { reopenIn } from "./use-organizations.js";
10
11
  import { useEffect, useState } from "react";
@@ -17,8 +18,8 @@ const Details = ({ organization, editable, onSaved }) => {
17
18
  const { busy, error, run } = useAction();
18
19
  const changed = name.trim() !== organization.name || slug !== organization.slug;
19
20
  return /* @__PURE__ */ jsxs(AccountSection, {
20
- title: "Details",
21
- description: editable ? "The name people see, and the slug that names it in links." : "Owners and admins can change these.",
21
+ title: text("details"),
22
+ description: editable ? text("detailsEditable") : text("detailsReadOnly"),
22
23
  children: [/* @__PURE__ */ jsx(SectionError, { message: error }), /* @__PURE__ */ jsxs("form", {
23
24
  className: "flex flex-col gap-2 sm:flex-row",
24
25
  onSubmit: (event) => {
@@ -33,14 +34,14 @@ const Details = ({ organization, editable, onSaved }) => {
33
34
  },
34
35
  children: [
35
36
  /* @__PURE__ */ jsx(Input, {
36
- "aria-label": "Name",
37
+ "aria-label": text("name"),
37
38
  value: name,
38
39
  disabled: !editable,
39
40
  onChange: (event) => setName(event.target.value),
40
41
  wrapperClassName: "flex-1"
41
42
  }),
42
43
  /* @__PURE__ */ jsx(Input, {
43
- "aria-label": "Slug",
44
+ "aria-label": text("slug"),
44
45
  value: slug,
45
46
  disabled: !editable,
46
47
  onChange: (event) => setSlug(event.target.value),
@@ -51,7 +52,7 @@ const Details = ({ organization, editable, onSaved }) => {
51
52
  variant: "primary",
52
53
  loading: busy,
53
54
  disabled: !changed || !name.trim(),
54
- children: "Save"
55
+ children: text("save")
55
56
  })
56
57
  ]
57
58
  })]
@@ -70,18 +71,18 @@ const HandOver = ({ organization, memberId }) => {
70
71
  memberId
71
72
  ]);
72
73
  return /* @__PURE__ */ jsxs(AccountSection, {
73
- title: "Hand over",
74
- description: "Make another member the owner. You stay as an admin.",
74
+ title: text("handOver"),
75
+ description: text("handOverDescription"),
75
76
  children: [/* @__PURE__ */ jsx(SectionError, { message: error }), /* @__PURE__ */ jsxs("div", {
76
77
  className: "flex flex-col gap-2 sm:flex-row",
77
78
  children: [/* @__PURE__ */ jsxs("select", {
78
- "aria-label": "New owner",
79
+ "aria-label": text("newOwner"),
79
80
  value: target,
80
81
  onChange: (event) => setTarget(event.target.value),
81
82
  className: "h-8 flex-1 rounded-md border border-border-strong bg-background px-2",
82
83
  children: [/* @__PURE__ */ jsx("option", {
83
84
  value: "",
84
- children: "Choose a member"
85
+ children: text("chooseMember")
85
86
  }), members.map((member) => /* @__PURE__ */ jsxs("option", {
86
87
  value: member.id,
87
88
  children: [
@@ -98,7 +99,7 @@ const HandOver = ({ organization, memberId }) => {
98
99
  await session.organizations.transferOwnership(target);
99
100
  window.location.reload();
100
101
  }),
101
- children: "Hand over"
102
+ children: text("handOver")
102
103
  })]
103
104
  })]
104
105
  });
@@ -113,31 +114,31 @@ const Delete = ({ organization, basePath }) => {
113
114
  reopenIn(basePath);
114
115
  });
115
116
  return /* @__PURE__ */ jsxs(AccountSection, {
116
- title: "Delete organization",
117
- description: "Takes the organization and its members and invitations away. Its records must be deleted first.",
117
+ title: text("deleteOrganization"),
118
+ description: text("deleteDescription"),
118
119
  children: [
119
120
  /* @__PURE__ */ jsx(SectionError, { message: confirming ? void 0 : error }),
120
121
  /* @__PURE__ */ jsx(Button, {
121
122
  variant: "danger",
122
123
  onClick: () => setConfirming(true),
123
- children: "Delete organization"
124
+ children: text("deleteOrganization")
124
125
  }),
125
126
  confirming && /* @__PURE__ */ jsxs(Dialog, {
126
- title: `Delete ${organization.name}?`,
127
- description: "Type its name to confirm. This cannot be undone.",
127
+ title: text("deleteTitle", organization.name),
128
+ description: text("deleteConfirm"),
128
129
  onClose: () => setConfirming(false),
129
130
  actions: /* @__PURE__ */ jsxs(Fragment$1, { children: [/* @__PURE__ */ jsx(Button, {
130
131
  onClick: () => setConfirming(false),
131
- children: "Cancel"
132
+ children: text("cancel")
132
133
  }), /* @__PURE__ */ jsx(Button, {
133
134
  variant: "danger",
134
135
  loading: busy,
135
136
  disabled: typed !== organization.name,
136
137
  onClick: () => void remove(),
137
- children: "Delete"
138
+ children: text("delete")
138
139
  })] }),
139
140
  children: [/* @__PURE__ */ jsx(SectionError, { message: error }), /* @__PURE__ */ jsx(Input, {
140
- "aria-label": "Organization name",
141
+ "aria-label": text("organizationName"),
141
142
  value: typed,
142
143
  onChange: (event) => setTyped(event.target.value)
143
144
  })]
@@ -149,15 +150,15 @@ const Leave = ({ organization, basePath, owner }) => {
149
150
  const { session } = useAuth();
150
151
  const { busy, error, run } = useAction();
151
152
  return /* @__PURE__ */ jsxs(AccountSection, {
152
- title: "Leave",
153
- description: owner ? "An organization keeps an owner: hand it over before you leave, unless it has another owner." : "You stop being a member, and lose your roles here.",
154
- children: [/* @__PURE__ */ jsx(SectionError, { message: error }), /* @__PURE__ */ jsxs(Button, {
153
+ title: text("leave"),
154
+ description: owner ? text("leaveOwner") : text("leaveMember"),
155
+ children: [/* @__PURE__ */ jsx(SectionError, { message: error }), /* @__PURE__ */ jsx(Button, {
155
156
  loading: busy,
156
157
  onClick: () => void run(async () => {
157
158
  await session.organizations.leave(organization.id);
158
159
  reopenIn(basePath);
159
160
  }),
160
- children: ["Leave ", organization.name]
161
+ children: text("leaveName", organization.name)
161
162
  })]
162
163
  });
163
164
  };
@@ -167,8 +168,8 @@ const OrganizationPage = ({ current, basePath, onChanged }) => {
167
168
  if (!organization) return /* @__PURE__ */ jsx("div", {
168
169
  className: "mx-auto w-full max-w-3xl px-4 py-6 md:px-6",
169
170
  children: /* @__PURE__ */ jsx(PageHeader, {
170
- title: "Organization",
171
- subtitle: "You do not work in an organization now. Choose one under Organizations."
171
+ title: text("organization"),
172
+ subtitle: text("noOrganization")
172
173
  })
173
174
  });
174
175
  const owner = current.role === "owner";
@@ -177,7 +178,7 @@ const OrganizationPage = ({ current, basePath, onChanged }) => {
177
178
  children: [
178
179
  /* @__PURE__ */ jsx(PageHeader, {
179
180
  title: organization.name,
180
- subtitle: current.role ? `You are ${organizationRoleLabels[current.role].toLowerCase()} here.` : "You work here through your global roles, without being a member."
181
+ subtitle: current.role ? text("youAre", organizationRoleLabel(current.role).toLowerCase()) : text("throughGlobalRoles")
181
182
  }),
182
183
  /* @__PURE__ */ jsx(Details, {
183
184
  organization,
@@ -1,20 +1,23 @@
1
+ import { text } from "./messages.js";
1
2
  import { Building2, Mail, Network, Users } from "lucide-react";
2
3
  //#region src/organizations/organization-paths.ts
3
4
  const organizationsPath = "/-/organizations";
4
5
  const organizationPath = "/-/organization";
5
6
  const membersPath = "/-/organization/members";
6
7
  const invitationsPath = "/-/organization/invitations";
7
- const organizationPageTitles = {
8
- [organizationsPath]: "Organizations",
9
- [organizationPath]: "Organization",
10
- [membersPath]: "Members",
11
- [invitationsPath]: "Invitations"
8
+ const pageTitles = {
9
+ [organizationsPath]: "organizations",
10
+ [organizationPath]: "organization",
11
+ [membersPath]: "members",
12
+ [invitationsPath]: "invitations"
12
13
  };
13
- const organizationRoleLabels = {
14
- owner: "Owner",
15
- admin: "Admin",
16
- member: "Member"
14
+ /** The title of the organization page at `path`; undefined for any other path. */
15
+ const organizationPageTitle = (path) => {
16
+ const id = pageTitles[path];
17
+ return id && text(id);
17
18
  };
19
+ /** The word for an organization role. */
20
+ const organizationRoleLabel = (role) => text(role);
18
21
  /** The label of an app role, as the server names it. */
19
22
  const roleLabel = (roles, name) => roles.find((role) => role.name === name)?.label ?? name;
20
23
  /** Whether the person manages the current organization: its owners and admins. */
@@ -32,9 +35,9 @@ const organizationMenuItems = (basePath, path, current) => {
32
35
  active: path === target
33
36
  });
34
37
  return [
35
- item("organizations", "Organizations", Network, organizationsPath),
36
- ...current?.organization ? [item("organization", "Organization", Building2, organizationPath)] : [],
37
- ...managesOrganization(current) ? [item("members", "Members", Users, membersPath), item("invitations", "Invitations", Mail, invitationsPath)] : []
38
+ item("organizations", text("organizations"), Network, organizationsPath),
39
+ ...current?.organization ? [item("organization", text("organization"), Building2, organizationPath)] : [],
40
+ ...managesOrganization(current) ? [item("members", text("members"), Users, membersPath), item("invitations", text("invitations"), Mail, invitationsPath)] : []
38
41
  ];
39
42
  };
40
43
  /** The app roles someone may give or take away: those they hold, those their roles grant, and every one for a superuser. */
@@ -46,4 +49,4 @@ const grantableRoles = (roles, current) => {
46
49
  return new Set([...held, ...granted].filter((name) => membership.includes(name)));
47
50
  };
48
51
  //#endregion
49
- export { grantableRoles, invitationsPath, managesOrganization, membersPath, organizationMenuItems, organizationPageTitles, organizationPath, organizationRoleLabels, organizationsPath, roleLabel };
52
+ export { grantableRoles, invitationsPath, managesOrganization, membersPath, organizationMenuItems, organizationPageTitle, organizationPath, organizationRoleLabel, organizationsPath, roleLabel };
@@ -1,5 +1,6 @@
1
1
  import { Badge } from "../primitives/badge.js";
2
2
  import { Button } from "../primitives/button.js";
3
+ import { text } from "./messages.js";
3
4
  import { jsx, jsxs } from "react/jsx-runtime";
4
5
  //#region src/organizations/organization-row.tsx
5
6
  /** An organization with Open, or a Current badge when the person works in it. */
@@ -16,12 +17,12 @@ const OrganizationRow = ({ organization, current, busy, onOpen }) => /* @__PURE_
16
17
  })]
17
18
  }), current ? /* @__PURE__ */ jsx(Badge, {
18
19
  tone: "green",
19
- children: "Current"
20
+ children: text("current")
20
21
  }) : /* @__PURE__ */ jsx(Button, {
21
22
  size: "sm",
22
23
  loading: busy,
23
24
  onClick: onOpen,
24
- children: "Open"
25
+ children: text("open")
25
26
  })]
26
27
  });
27
28
  //#endregion
@@ -4,6 +4,7 @@ import { PageHeader } from "../app-shell/page-header.js";
4
4
  import { useAuth } from "../auth/auth-provider.js";
5
5
  import { Button } from "../primitives/button.js";
6
6
  import { AccountSection, SectionError } from "../account/account-section.js";
7
+ import { text } from "./messages.js";
7
8
  import { roleLabel } from "./organization-paths.js";
8
9
  import { useAction } from "./use-action.js";
9
10
  import { isAdmin } from "../auth/user-to-shell.js";
@@ -28,8 +29,8 @@ const NewOrganization = ({ basePath }) => {
28
29
  reopenIn(basePath);
29
30
  });
30
31
  return /* @__PURE__ */ jsxs(AccountSection, {
31
- title: "New organization",
32
- description: "You become its owner, with every role, and work in it once it is made.",
32
+ title: text("newOrganization"),
33
+ description: text("newOrganizationDescription"),
33
34
  children: [/* @__PURE__ */ jsx(SectionError, { message: error }), /* @__PURE__ */ jsxs("form", {
34
35
  className: "flex flex-col gap-2 sm:flex-row",
35
36
  onSubmit: (event) => {
@@ -38,14 +39,14 @@ const NewOrganization = ({ basePath }) => {
38
39
  },
39
40
  children: [
40
41
  /* @__PURE__ */ jsx(Input, {
41
- "aria-label": "Name",
42
- placeholder: "Name",
42
+ "aria-label": text("name"),
43
+ placeholder: text("name"),
43
44
  value: name,
44
45
  onChange: (event) => setName(event.target.value),
45
46
  wrapperClassName: "flex-1"
46
47
  }),
47
48
  /* @__PURE__ */ jsx(Input, {
48
- "aria-label": "Slug",
49
+ "aria-label": text("slug"),
49
50
  placeholder: "slug",
50
51
  value: slug ?? slugOf(name),
51
52
  onChange: (event) => setSlug(event.target.value),
@@ -56,7 +57,7 @@ const NewOrganization = ({ basePath }) => {
56
57
  variant: "primary",
57
58
  loading: busy,
58
59
  disabled: !name.trim(),
59
- children: "Create"
60
+ children: text("create")
60
61
  })
61
62
  ]
62
63
  })]
@@ -71,8 +72,8 @@ const YourInvitations = () => {
71
72
  if (invitations.length === 0) return null;
72
73
  const roles = organizations?.roles ?? [];
73
74
  return /* @__PURE__ */ jsx(AccountSection, {
74
- title: "Invitations for you",
75
- description: "Open the link in the invitation email to accept one.",
75
+ title: text("invitationsForYou"),
76
+ description: text("invitationsForYouDescription"),
76
77
  children: /* @__PURE__ */ jsx("ul", {
77
78
  className: "divide-y rounded-md border",
78
79
  children: invitations.map((invitation) => /* @__PURE__ */ jsxs("li", {
@@ -80,13 +81,13 @@ const YourInvitations = () => {
80
81
  children: [
81
82
  /* @__PURE__ */ jsx("span", {
82
83
  className: "font-medium",
83
- children: invitation.organizationName ?? "An organization"
84
+ children: invitation.organizationName ?? text("anOrganization")
84
85
  }),
85
86
  ": ",
86
- invitation.appRoles.map((name) => roleLabel(roles, name)).join(", ") || "no app roles",
87
- /* @__PURE__ */ jsxs("span", {
87
+ invitation.appRoles.map((name) => roleLabel(roles, name)).join(", ") || text("noAppRoles"),
88
+ /* @__PURE__ */ jsx("span", {
88
89
  className: "block text-xs text-muted-foreground",
89
- children: ["Until ", new Date(invitation.expiresAt).toLocaleDateString(formatTag())]
90
+ children: text("until", new Date(invitation.expiresAt).toLocaleDateString(formatTag()))
90
91
  })
91
92
  ]
92
93
  }, invitation.id))
@@ -107,12 +108,12 @@ const OrganizationsPage = ({ state, basePath }) => {
107
108
  className: "mx-auto flex w-full max-w-3xl flex-col gap-4 px-4 py-6 md:px-6",
108
109
  children: [
109
110
  /* @__PURE__ */ jsx(PageHeader, {
110
- title: "Organizations",
111
- subtitle: "You work in one organization at a time; what you see follows from your roles there."
111
+ title: text("organizations"),
112
+ subtitle: text("organizationsSubtitle")
112
113
  }),
113
114
  /* @__PURE__ */ jsxs(AccountSection, {
114
- title: "Your organizations",
115
- description: state.organizations.length > 0 ? "The organizations you are a member of." : "You are not a member of an organization yet.",
115
+ title: text("yourOrganizations"),
116
+ description: state.organizations.length > 0 ? text("memberOf") : text("notMember"),
116
117
  children: [/* @__PURE__ */ jsx(SectionError, { message: error ?? state.error }), state.organizations.length > 0 && /* @__PURE__ */ jsx("ul", {
117
118
  className: "divide-y rounded-md border",
118
119
  children: state.organizations.map((organization) => /* @__PURE__ */ jsx(OrganizationRow, {
@@ -1,5 +1,6 @@
1
1
  import { authMessage } from "../auth/auth-messages.js";
2
2
  import { useAuth } from "../auth/auth-provider.js";
3
+ import { text } from "./messages.js";
3
4
  import { useCallback, useEffect, useState } from "react";
4
5
  import { AuthError } from "@protobase/client";
5
6
  //#region src/organizations/use-organizations.ts
@@ -16,7 +17,7 @@ const useOrganizations = (enabled) => {
16
17
  Promise.all([session.organizations.current(), session.organizations.list()]).then(([currentOrganization, list]) => {
17
18
  setCurrent(currentOrganization);
18
19
  setOrganizations(list);
19
- }, (failure) => setError(failure instanceof AuthError ? authMessage(failure) : "Could not read your organizations"));
20
+ }, (failure) => setError(failure instanceof AuthError ? authMessage(failure) : text("couldNotReadOrganizations")));
20
21
  }, [
21
22
  enabled,
22
23
  session,
package/dist/package.js CHANGED
@@ -1,5 +1,5 @@
1
1
  //#region package.json
2
- var version = "0.25.2";
2
+ var version = "0.25.3";
3
3
  var protobase = { "pluginApi": 1 };
4
4
  //#endregion
5
5
  export { protobase, version };
@@ -3,6 +3,7 @@ import { useAuth } from "../auth/auth-provider.js";
3
3
  import { useRouter } from "../app/router.js";
4
4
  import { CustomBoundary } from "../app/pages/custom-boundary.js";
5
5
  import { mergePlugins } from "./merge-plugins.js";
6
+ import { text } from "./messages.js";
6
7
  import { usePluginSource } from "./plugin-source.js";
7
8
  import { usePluginLoads } from "./use-plugin-loads.js";
8
9
  import { useCallback, useMemo } from "react";
@@ -31,7 +32,11 @@ const BeforeSignIn = ({ children }) => {
31
32
  if (state.kind !== "signed-out" && state.kind !== "visitor") return children;
32
33
  if (!settled) return /* @__PURE__ */ jsxs("div", {
33
34
  className: "flex h-full min-h-48 items-center justify-center gap-2 text-muted-foreground",
34
- children: [/* @__PURE__ */ jsx(Spinner, {}), " Loading"]
35
+ children: [
36
+ /* @__PURE__ */ jsx(Spinner, {}),
37
+ " ",
38
+ text("loading")
39
+ ]
35
40
  });
36
41
  if (!Around) return children;
37
42
  return /* @__PURE__ */ jsx(CustomBoundary, {
@@ -1,6 +1,6 @@
1
1
  import "../app/pages/project-ui.js";
2
- import { PluginRef } from "@protobase/client";
3
2
  import "@protobase/schema/internal";
3
+ import { PluginRef } from "@protobase/client";
4
4
  //#region src/plugins/load-plugin.d.ts
5
5
  /** What loading a plugin needs from the page: reading its plugin.json, adding its stylesheet and importing its module. */
6
6
  export type PluginLoader = {
@@ -1,3 +1,4 @@
1
+ import { text } from "./messages.js";
1
2
  import { parseManifest } from "./plugin-manifest.js";
2
3
  import { pluginUi } from "./plugin-ui.js";
3
4
  import { pluginApi, protobaseVersion } from "./plugin-api.js";
@@ -45,7 +46,7 @@ const loadPlugin = async (ref, loader, base, host = thisApp) => {
45
46
  let name;
46
47
  try {
47
48
  const read = await loader.readJson(url);
48
- if ("status" in read) return off(`its plugin.json answered ${read.status}`);
49
+ if ("status" in read) return off(text("answered", read.status));
49
50
  const parsed = parseManifest(read.json);
50
51
  if ("problem" in parsed) return off(parsed.problem);
51
52
  const { manifest } = parsed;
@@ -66,7 +67,7 @@ const loadPlugin = async (ref, loader, base, host = thisApp) => {
66
67
  ui: pluginUi(plugin, module)
67
68
  };
68
69
  } catch (error) {
69
- return off(`it could not be loaded: ${describe(error)}`, name);
70
+ return off(text("couldNotLoad", describe(error)), name);
70
71
  }
71
72
  };
72
73
  //#endregion
@@ -0,0 +1,36 @@
1
+ import { defineMessages } from "../locale/messages.js";
2
+ //#region src/plugins/messages.ts
3
+ const text = defineMessages({
4
+ en: {
5
+ loading: "Loading",
6
+ pluginsOff: "Plugins off",
7
+ pluginOff: "The plugin {} is off: {}.",
8
+ dismiss: "Dismiss",
9
+ answered: (status) => `its plugin.json answered ${status}`,
10
+ couldNotLoad: (reason) => `it could not be loaded: ${reason}`,
11
+ notObject: "its plugin.json is not an object",
12
+ noName: "its plugin.json names no plugin",
13
+ noApi: "its plugin.json names no plugin API",
14
+ noVersions: "its plugin.json names no Protobase versions",
15
+ noModule: "its plugin.json names no module",
16
+ badStylesheet: "its plugin.json names a stylesheet that is no URL",
17
+ badVersion: "its plugin.json has a version that is no text"
18
+ },
19
+ nl: {
20
+ loading: "Laden",
21
+ pluginsOff: "Plug-ins uit",
22
+ pluginOff: "De plug-in {} staat uit: {}.",
23
+ dismiss: "Sluiten",
24
+ answered: (status) => `de plugin.json antwoordde ${status}`,
25
+ couldNotLoad: (reason) => `hij kon niet worden geladen: ${reason}`,
26
+ notObject: "de plugin.json is geen object",
27
+ noName: "de plugin.json noemt geen plug-in",
28
+ noApi: "de plugin.json noemt geen plug-in-API",
29
+ noVersions: "de plugin.json noemt geen Protobase-versies",
30
+ noModule: "de plugin.json noemt geen module",
31
+ badStylesheet: "de plugin.json noemt een stylesheet die geen URL is",
32
+ badVersion: "de plugin.json heeft een versie die geen tekst is"
33
+ }
34
+ });
35
+ //#endregion
36
+ export { text };
@@ -1,5 +1,6 @@
1
1
  import { Spinner } from "../primitives/spinner.js";
2
2
  import { mergePlugins } from "./merge-plugins.js";
3
+ import { text } from "./messages.js";
3
4
  import { usePluginSource } from "./plugin-source.js";
4
5
  import { usePluginLoads } from "./use-plugin-loads.js";
5
6
  import { useAdminMeta } from "../app/meta-gate.js";
@@ -21,7 +22,11 @@ const PluginGate = ({ ui, children }) => {
21
22
  const merged = useMemo(() => mergePlugins(ui, loads), [ui, loads]);
22
23
  if (!settled) return /* @__PURE__ */ jsxs("div", {
23
24
  className: "flex h-full min-h-48 items-center justify-center gap-2 text-muted-foreground",
24
- children: [/* @__PURE__ */ jsx(Spinner, {}), " Loading"]
25
+ children: [
26
+ /* @__PURE__ */ jsx(Spinner, {}),
27
+ " ",
28
+ text("loading")
29
+ ]
25
30
  });
26
31
  return /* @__PURE__ */ jsx(ProjectUiContext.Provider, {
27
32
  value: merged.ui,
@@ -1,15 +1,16 @@
1
+ import { text } from "./messages.js";
1
2
  //#region src/plugins/plugin-manifest.ts
2
3
  const isText = (value) => typeof value === "string" && value.trim() !== "";
3
4
  /** The manifest in `value`, or what is wrong with it. */
4
5
  const parseManifest = (value) => {
5
- if (typeof value !== "object" || value === null || Array.isArray(value)) return { problem: "its plugin.json is not an object" };
6
+ if (typeof value !== "object" || value === null || Array.isArray(value)) return { problem: text("notObject") };
6
7
  const { name, version, api, protobase, entry, css } = value;
7
- if (!isText(name)) return { problem: "its plugin.json names no plugin" };
8
- if (!Number.isInteger(api)) return { problem: "its plugin.json names no plugin API" };
9
- if (!isText(protobase)) return { problem: "its plugin.json names no Protobase versions" };
10
- if (!isText(entry)) return { problem: "its plugin.json names no module" };
11
- if (css !== void 0 && !isText(css)) return { problem: "its plugin.json names a stylesheet that is no URL" };
12
- if (version !== void 0 && !isText(version)) return { problem: "its plugin.json has a version that is no text" };
8
+ if (!isText(name)) return { problem: text("noName") };
9
+ if (!Number.isInteger(api)) return { problem: text("noApi") };
10
+ if (!isText(protobase)) return { problem: text("noVersions") };
11
+ if (!isText(entry)) return { problem: text("noModule") };
12
+ if (css !== void 0 && !isText(css)) return { problem: text("badStylesheet") };
13
+ if (version !== void 0 && !isText(version)) return { problem: text("badVersion") };
13
14
  return { manifest: {
14
15
  name,
15
16
  api,
@@ -1,3 +1,5 @@
1
+ import { slotted } from "../locale/slotted.js";
2
+ import { text } from "./messages.js";
1
3
  import { useState } from "react";
2
4
  import { Puzzle, X } from "lucide-react";
3
5
  import { jsx, jsxs } from "react/jsx-runtime";
@@ -8,7 +10,7 @@ const PluginNotice = ({ off }) => {
8
10
  if (dismissed || off.length === 0) return null;
9
11
  return /* @__PURE__ */ jsxs("div", {
10
12
  role: "region",
11
- "aria-label": "Plugins off",
13
+ "aria-label": text("pluginsOff"),
12
14
  className: "flex items-start gap-3 border-b border-warning/40 bg-warning-soft px-4 py-2 text-warning-text",
13
15
  children: [
14
16
  /* @__PURE__ */ jsx(Puzzle, {
@@ -17,20 +19,14 @@ const PluginNotice = ({ off }) => {
17
19
  }),
18
20
  /* @__PURE__ */ jsx("ul", {
19
21
  className: "min-w-0 flex-1 space-y-0.5 break-words",
20
- children: off.map((plugin) => /* @__PURE__ */ jsxs("li", { children: [
21
- "The plugin ",
22
- /* @__PURE__ */ jsx("span", {
23
- className: "font-semibold",
24
- children: plugin.name ?? plugin.url
25
- }),
26
- " is off: ",
27
- plugin.reason,
28
- "."
29
- ] }, plugin.url))
22
+ children: off.map((plugin) => /* @__PURE__ */ jsx("li", { children: slotted(text("pluginOff"), /* @__PURE__ */ jsx("span", {
23
+ className: "font-semibold",
24
+ children: plugin.name ?? plugin.url
25
+ }), plugin.reason) }, plugin.url))
30
26
  }),
31
27
  /* @__PURE__ */ jsx("button", {
32
28
  type: "button",
33
- "aria-label": "Dismiss",
29
+ "aria-label": text("dismiss"),
34
30
  className: "rounded p-0.5 hover:bg-warning/10",
35
31
  onClick: () => setDismissed(true),
36
32
  children: /* @__PURE__ */ jsx(X, {
@@ -0,0 +1,16 @@
1
+ import { defineMessages } from "../locale/messages.js";
2
+ //#region src/primitives/messages.ts
3
+ const text = defineMessages({
4
+ en: {
5
+ close: "Close",
6
+ loading: "Loading",
7
+ remove: (label) => `Remove ${label}`
8
+ },
9
+ nl: {
10
+ close: "Sluiten",
11
+ loading: "Laden",
12
+ remove: (label) => `${label} verwijderen`
13
+ }
14
+ });
15
+ //#endregion
16
+ export { text };
@@ -1,4 +1,5 @@
1
1
  import { useFocusTrap } from "../lib/use-focus-trap.js";
2
+ import { text } from "./messages.js";
2
3
  import { closesSheet } from "./sheet-swipe.js";
3
4
  import { useRef } from "react";
4
5
  import { X } from "lucide-react";
@@ -55,7 +56,7 @@ const Sheet = ({ title, open, onClose, children }) => {
55
56
  }),
56
57
  /* @__PURE__ */ jsx("button", {
57
58
  type: "button",
58
- "aria-label": "Close",
59
+ "aria-label": text("close"),
59
60
  onClick: onClose,
60
61
  className: "-mr-2 inline-flex size-11 items-center justify-center rounded-md text-muted-foreground hover:bg-muted",
61
62
  children: /* @__PURE__ */ jsx(X, { className: "size-5" })
@@ -1,9 +1,10 @@
1
1
  import { cn } from "../lib/cn.js";
2
+ import { text } from "./messages.js";
2
3
  import { Loader2 } from "lucide-react";
3
4
  import { jsx } from "react/jsx-runtime";
4
5
  //#region src/primitives/spinner.tsx
5
6
  const Spinner = ({ className }) => /* @__PURE__ */ jsx(Loader2, {
6
- "aria-label": "Loading",
7
+ "aria-label": text("loading"),
7
8
  className: cn("size-4 animate-spin", className)
8
9
  });
9
10
  //#endregion
@@ -1,3 +1,4 @@
1
+ import { text } from "./messages.js";
1
2
  import { X } from "lucide-react";
2
3
  import { jsx, jsxs } from "react/jsx-runtime";
3
4
  //#region src/primitives/token.tsx
@@ -9,7 +10,7 @@ const Token = ({ label, onRemove }) => /* @__PURE__ */ jsxs("span", {
9
10
  children: label
10
11
  }), /* @__PURE__ */ jsx("button", {
11
12
  type: "button",
12
- "aria-label": `Remove ${label}`,
13
+ "aria-label": text("remove", label),
13
14
  className: "rounded p-0.5 hover:bg-primary-border/60",
14
15
  onMouseDown: (event) => event.preventDefault(),
15
16
  onClick: onRemove,
@@ -15,7 +15,8 @@ const text = defineMessages({
15
15
  lastUpdated: (ago) => `Last updated ${ago}`,
16
16
  viewing: (count) => `${count} viewing`,
17
17
  savingSubject: (subject) => `Saving ${subject}...`,
18
- subjectSaved: (subject) => `${subject} saved`
18
+ subjectSaved: (subject) => `${subject} saved`,
19
+ tabs: "Tabs"
19
20
  },
20
21
  nl: {
21
22
  savedDifferent: (name) => `${name} bewaarde een andere waarde`,
@@ -31,7 +32,8 @@ const text = defineMessages({
31
32
  lastUpdated: (ago) => `Laatst bijgewerkt ${ago}`,
32
33
  viewing: (count) => `${count} kijken mee`,
33
34
  savingSubject: (subject) => `${subject} bewaren...`,
34
- subjectSaved: (subject) => `${subject} bewaard`
35
+ subjectSaved: (subject) => `${subject} bewaard`,
36
+ tabs: "Tabbladen"
35
37
  }
36
38
  });
37
39
  //#endregion
@@ -1,11 +1,12 @@
1
1
  import { cn } from "../lib/cn.js";
2
2
  import { segmentedClass } from "../primitives/segmented.js";
3
+ import { text } from "./messages.js";
3
4
  import { AppLink } from "../app/use-app-address.js";
4
5
  import { jsx } from "react/jsx-runtime";
5
6
  //#region src/record-view/record-tabs.tsx
6
7
  /** The record page's tabs, links whose addresses show each; the shown one is the current page. */
7
8
  const RecordTabs = ({ tabs }) => /* @__PURE__ */ jsx("nav", {
8
- "aria-label": "Tabs",
9
+ "aria-label": text("tabs"),
9
10
  className: "overflow-x-auto py-6",
10
11
  children: /* @__PURE__ */ jsx("span", {
11
12
  className: segmentedClass.group("md"),
@@ -0,0 +1,14 @@
1
+ import { defineMessages } from "../locale/messages.js";
2
+ //#region src/roles/messages.ts
3
+ const text = defineMessages({
4
+ en: {
5
+ couldNotReadRoles: "Could not read your roles",
6
+ couldNotSwitch: "Could not switch the role"
7
+ },
8
+ nl: {
9
+ couldNotReadRoles: "Kon je rollen niet lezen",
10
+ couldNotSwitch: "Kon niet van rol wisselen"
11
+ }
12
+ });
13
+ //#endregion
14
+ export { text };