@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.
Files changed (271) hide show
  1. package/dist/account/account-pages.js +3 -2
  2. package/dist/account/account-section.js +1 -1
  3. package/dist/account/account-security-page.js +9 -4
  4. package/dist/account/backup-codes.js +7 -6
  5. package/dist/account/messages.js +214 -0
  6. package/dist/account/passkeys-section.js +23 -22
  7. package/dist/account/password-dialog.js +4 -3
  8. package/dist/account/sign-in-policy-form.js +14 -13
  9. package/dist/account/sign-in-policy-labels.js +30 -29
  10. package/dist/account/sign-in-policy-page.js +11 -6
  11. package/dist/account/sign-in-providers-section.js +6 -5
  12. package/dist/account/staff-sign-in-log.js +11 -10
  13. package/dist/account/totp-qr.js +2 -1
  14. package/dist/account/two-factor-section.js +24 -23
  15. package/dist/account/two-factor-setup.js +19 -18
  16. package/dist/action-card/action-card.js +2 -1
  17. package/dist/action-card/field-list.js +1 -1
  18. package/dist/action-card/messages.js +8 -0
  19. package/dist/activity-chart/activity-chart.js +3 -2
  20. package/dist/activity-chart/messages.js +14 -0
  21. package/dist/app/app.d.ts +1 -1
  22. package/dist/app/app.js +20 -21
  23. package/dist/app/assistant-shell.js +2 -1
  24. package/dist/app/batch-failure.js +2 -1
  25. package/dist/app/breadcrumb-for.js +4 -3
  26. package/dist/app/charts/chart-groups.js +3 -2
  27. package/dist/app/charts/chart-view.js +3 -2
  28. package/dist/app/charts/group-bars.js +2 -1
  29. package/dist/app/charts/messages.js +26 -0
  30. package/dist/app/charts/period-chart.js +3 -2
  31. package/dist/app/charts/split-points.js +2 -1
  32. package/dist/app/comments/comment-composer.js +19 -18
  33. package/dist/app/comments/comments-section.js +4 -3
  34. package/dist/app/comments/messages.js +26 -0
  35. package/dist/app/create/create-page.js +11 -18
  36. package/dist/app/custom-section.js +4 -6
  37. package/dist/app/displays/board-column.js +7 -6
  38. package/dist/app/displays/board-columns.js +3 -2
  39. package/dist/app/displays/board-display.js +13 -15
  40. package/dist/app/displays/calendar-controls.js +3 -2
  41. package/dist/app/displays/calendar-display.js +12 -17
  42. package/dist/app/displays/card-carousel.js +4 -3
  43. package/dist/app/displays/cards-display.js +2 -1
  44. package/dist/app/displays/custom-display.js +4 -6
  45. package/dist/app/displays/display-frame.js +3 -2
  46. package/dist/app/displays/list-cards.js +5 -8
  47. package/dist/app/displays/list-displays.js +3 -3
  48. package/dist/app/displays/map-display.js +15 -14
  49. package/dist/app/displays/messages.js +128 -2
  50. package/dist/app/displays/period-controls.js +4 -3
  51. package/dist/app/displays/record-card.js +7 -6
  52. package/dist/app/displays/timeline-display.js +17 -26
  53. package/dist/app/displays/timeline-range.js +2 -1
  54. package/dist/app/displays/timeline-row.js +1 -1
  55. package/dist/app/displays/use-board-move.js +2 -2
  56. package/dist/app/displays/use-calendar-move.js +2 -1
  57. package/dist/app/document-title.js +2 -1
  58. package/dist/app/error-banner.js +1 -1
  59. package/dist/app/export/builder-port.js +2 -1
  60. package/dist/app/export/export-dialog.js +39 -43
  61. package/dist/app/export/export-worker.js +2 -0
  62. package/dist/app/export/messages.js +78 -0
  63. package/dist/app/export/run-export.js +5 -2
  64. package/dist/app/export/sink.js +4 -3
  65. package/dist/app/export/use-export-flow.js +4 -3
  66. package/dist/app/field-editor.js +4 -3
  67. package/dist/app/field-types/messages.js +8 -0
  68. package/dist/app/field-types/widget-part.js +2 -1
  69. package/dist/app/file-field.js +11 -14
  70. package/dist/app/gallery/gallery-section.js +4 -3
  71. package/dist/app/gallery/lightbox.js +5 -4
  72. package/dist/app/gallery/messages.js +30 -0
  73. package/dist/app/gallery/photo-details.js +3 -2
  74. package/dist/app/hand-order/hand-sorted-list.js +3 -6
  75. package/dist/app/hand-order/messages.js +4 -2
  76. package/dist/app/history/describe-change.js +3 -8
  77. package/dist/app/history/messages.js +32 -0
  78. package/dist/app/import/convert.js +20 -19
  79. package/dist/app/import/failed-rows.js +3 -2
  80. package/dist/app/import/import-button.js +2 -1
  81. package/dist/app/import/import-check-step.js +21 -37
  82. package/dist/app/import/import-dialog.js +13 -17
  83. package/dist/app/import/import-file-step.js +5 -9
  84. package/dist/app/import/import-mapping-step.js +32 -35
  85. package/dist/app/import/import-result-step.js +13 -12
  86. package/dist/app/import/messages.js +200 -0
  87. package/dist/app/import/parse-worker.js +2 -0
  88. package/dist/app/import/prepare-rows.js +5 -4
  89. package/dist/app/import/sheet-cell.js +2 -1
  90. package/dist/app/import/sheet-file.js +3 -2
  91. package/dist/app/import/sheet-source.js +7 -3
  92. package/dist/app/import/xlsx-reader.js +4 -3
  93. package/dist/app/import/xlsx-xml.js +2 -1
  94. package/dist/app/import/zip-reader.js +4 -3
  95. package/dist/app/list-edit/use-list-edits.js +1 -1
  96. package/dist/app/list-group-chart.js +2 -1
  97. package/dist/app/list-page.js +1 -1
  98. package/dist/app/map/map-layer-switch.js +3 -2
  99. package/dist/app/map/messages.js +14 -0
  100. package/dist/app/messages.js +116 -2
  101. package/dist/app/meta-gate.js +1 -1
  102. package/dist/app/nav-from-meta.js +11 -10
  103. package/dist/app/pages/action-inputs/action-inputs-dialog.js +3 -2
  104. package/dist/app/pages/action-inputs/input-answers.js +6 -5
  105. package/dist/app/pages/blocks/card-row.js +3 -2
  106. package/dist/app/pages/blocks/field.js +1 -1
  107. package/dist/app/pages/blocks/modal-form.js +5 -4
  108. package/dist/app/pages/blocks/progress.js +1 -1
  109. package/dist/app/pages/blocks/record-card.js +3 -2
  110. package/dist/app/pages/blocks/stat.js +2 -1
  111. package/dist/app/pages/composed-page.js +6 -8
  112. package/dist/app/pages/confirm-action.js +2 -1
  113. package/dist/app/pages/custom-boundary.js +4 -2
  114. package/dist/app/pages/messages.js +64 -0
  115. package/dist/app/pages/render-node.js +5 -7
  116. package/dist/app/pages/use-run-action.js +11 -10
  117. package/dist/app/record-page.js +1 -1
  118. package/dist/app/record-row/messages.js +26 -0
  119. package/dist/app/record-row/record-row-body.js +12 -7
  120. package/dist/app/record-row/record-row.js +4 -3
  121. package/dist/app/record-row/saved-record-row.js +3 -2
  122. package/dist/app/related-dots/dots-summary.js +2 -1
  123. package/dist/app/related-dots/messages.js +8 -0
  124. package/dist/app/relation-picker.js +9 -8
  125. package/dist/app/router.js +5 -4
  126. package/dist/app/runtime/messages.js +58 -0
  127. package/dist/app/runtime/runtime-status.js +10 -23
  128. package/dist/app/runtime/runtime-update-dialog.js +7 -6
  129. package/dist/app/runtime/runtime-update.js +4 -3
  130. package/dist/app/saved/favorite-button.js +2 -1
  131. package/dist/app/saved/messages.js +22 -0
  132. package/dist/app/saved/saved-list.js +4 -7
  133. package/dist/app/saved/use-favorite.js +3 -2
  134. package/dist/app/search/messages.js +6 -2
  135. package/dist/app/search/navigation-hits.js +9 -7
  136. package/dist/app/searches/filter-chip-list.js +2 -1
  137. package/dist/app/searches/messages.js +96 -0
  138. package/dist/app/searches/save-search-button.js +5 -4
  139. package/dist/app/searches/saved-search-rows.js +3 -2
  140. package/dist/app/searches/saved-search-section.js +6 -5
  141. package/dist/app/searches/saved-search.js +4 -3
  142. package/dist/app/searches/saved-searches.js +15 -25
  143. package/dist/app/searches/search-controls.js +5 -4
  144. package/dist/app/searches/search-diff.js +4 -2
  145. package/dist/app/searches/search-name-dialog.js +4 -3
  146. package/dist/app/searches/update-search-dialog.js +15 -30
  147. package/dist/app/searches/use-save-search.js +8 -7
  148. package/dist/app/searches/use-search-row.js +4 -3
  149. package/dist/app/sensitive-field.js +5 -4
  150. package/dist/app/source-notice.js +13 -8
  151. package/dist/app/staff-banner.js +13 -20
  152. package/dist/app-shell/global-search.js +2 -2
  153. package/dist/app-shell/messages.js +4 -2
  154. package/dist/app-shell/sidebar-group.js +1 -1
  155. package/dist/app-shell/sidebar-item.js +1 -1
  156. package/dist/app-shell/sidebar.js +1 -1
  157. package/dist/assistant-dock/assistant-dock.js +6 -6
  158. package/dist/assistant-dock/messages.js +26 -0
  159. package/dist/assistant-dock/use-assistant.js +2 -1
  160. package/dist/assistant-dock/use-backend-data.js +2 -1
  161. package/dist/assistant-dock/widget-view.js +3 -2
  162. package/dist/auth/auth-gate.js +10 -5
  163. package/dist/auth/auth-locale.js +14 -0
  164. package/dist/auth/auth-messages.js +122 -43
  165. package/dist/auth/auth-provider.js +6 -1
  166. package/dist/auth/code-form.js +4 -3
  167. package/dist/auth/email-code-form.js +11 -13
  168. package/dist/auth/email-code-page.js +2 -1
  169. package/dist/auth/first-run-page.js +6 -5
  170. package/dist/auth/forgot-password-form.js +15 -17
  171. package/dist/auth/forgot-password-page.js +2 -1
  172. package/dist/auth/messages.js +200 -0
  173. package/dist/auth/provider-sign-in-page.js +5 -5
  174. package/dist/auth/resend-code-button.js +2 -1
  175. package/dist/auth/reset-password-form.js +12 -11
  176. package/dist/auth/reset-password-page.js +4 -3
  177. package/dist/auth/setup-required-form.js +8 -10
  178. package/dist/auth/setup-required-page.js +7 -2
  179. package/dist/auth/sign-in-form.js +18 -21
  180. package/dist/auth/sign-in-prompt.js +4 -3
  181. package/dist/auth/sign-up-code-form.js +11 -13
  182. package/dist/auth/sign-up-details-form.js +15 -22
  183. package/dist/auth/sign-up-email-form.js +9 -8
  184. package/dist/auth/sign-up-page.js +2 -1
  185. package/dist/auth/staff-sign-in-form.js +16 -19
  186. package/dist/auth/two-factor-form.js +19 -18
  187. package/dist/auth/user-to-shell.js +2 -1
  188. package/dist/chat/composer.js +8 -7
  189. package/dist/chat/messages.js +14 -0
  190. package/dist/compact-table/compact-table.js +4 -8
  191. package/dist/compact-table/messages.js +18 -0
  192. package/dist/data/meta-data.d.ts +1 -1
  193. package/dist/data/use-update-record.js +1 -1
  194. package/dist/data-grid/grid-header.js +1 -1
  195. package/dist/filter-panel/date-presets.js +1 -1
  196. package/dist/forms/public-form-page.js +1 -1
  197. package/dist/inline-child-table/inline-child-table.js +12 -12
  198. package/dist/inline-child-table/line-row.js +10 -9
  199. package/dist/inline-child-table/messages.js +52 -0
  200. package/dist/inline-child-table/totals-footer.js +5 -8
  201. package/dist/invitations/invite-form.js +3 -2
  202. package/dist/invitations/link-notice.js +6 -4
  203. package/dist/invitations/messages.js +24 -0
  204. package/dist/invitations/pending-invitation-row.js +4 -3
  205. package/dist/lists/messages.js +8 -0
  206. package/dist/lists/paged-search-list.js +2 -1
  207. package/dist/locale/slotted.js +14 -0
  208. package/dist/markdown/markdown-editor.js +6 -5
  209. package/dist/markdown/messages.js +16 -0
  210. package/dist/organizations/all-organizations.js +6 -5
  211. package/dist/organizations/app-roles-picker.js +2 -1
  212. package/dist/organizations/invitation-page.js +41 -51
  213. package/dist/organizations/invitations-page.js +11 -10
  214. package/dist/organizations/members-page.js +16 -11
  215. package/dist/organizations/messages.js +180 -0
  216. package/dist/organizations/organization-page.js +27 -26
  217. package/dist/organizations/organization-paths.js +16 -13
  218. package/dist/organizations/organization-row.js +3 -2
  219. package/dist/organizations/organizations-page.js +17 -16
  220. package/dist/organizations/use-organizations.js +2 -1
  221. package/dist/package.js +1 -1
  222. package/dist/plugins/before-sign-in.js +6 -1
  223. package/dist/plugins/load-plugin.d.ts +1 -1
  224. package/dist/plugins/load-plugin.js +3 -2
  225. package/dist/plugins/messages.js +36 -0
  226. package/dist/plugins/plugin-gate.js +6 -1
  227. package/dist/plugins/plugin-manifest.js +8 -7
  228. package/dist/plugins/plugin-notice.js +8 -12
  229. package/dist/primitives/button.js +1 -1
  230. package/dist/primitives/messages.js +16 -0
  231. package/dist/primitives/segmented.js +1 -1
  232. package/dist/primitives/sheet.js +2 -1
  233. package/dist/primitives/spinner.js +2 -1
  234. package/dist/primitives/token.js +2 -1
  235. package/dist/record-view/field.js +2 -2
  236. package/dist/record-view/messages.js +4 -2
  237. package/dist/record-view/record-tabs.js +2 -1
  238. package/dist/roles/messages.js +14 -0
  239. package/dist/roles/use-active-role.js +3 -2
  240. package/dist/shell/assets/app-yCMYvqMX.js +69 -0
  241. package/dist/shell/assets/cn-Bi0ZEjD9.js +1 -0
  242. package/dist/shell/assets/export-worker-ClACk00b.js +2 -0
  243. package/dist/shell/assets/{index-BRkDA5KD.css → index-Bkc7w8I4.css} +1 -1
  244. package/dist/shell/assets/{index-xY0Mvv6V.js → index-HkjqX_O-.js} +2 -2
  245. package/dist/shell/assets/maplibre-DUTS_eJM.js +814 -0
  246. package/dist/shell/assets/{markdown-content-ukDTCu6u.js → markdown-content-CtGUP4W6.js} +1 -1
  247. package/dist/shell/assets/parse-worker-UhbwEjS0.js +5 -0
  248. package/dist/shell/assets/preload-helper-BZ3hrQDD.js +1 -0
  249. package/dist/shell/assets/{record-map-BJmYZ7kQ.js → record-map-DFy7sJEU.js} +1 -1
  250. package/dist/shell/assets/records-map-MfQEuiFB.js +1 -0
  251. package/dist/shell/assets/shared-protobase-ui-Bfa59vx6.js +1 -0
  252. package/dist/shell/index.html +6 -4
  253. package/dist/shell/protobase.shell.json +1 -1
  254. package/dist/shell/protobase.sw.js +2 -2
  255. package/dist/side-panel/messages.js +8 -0
  256. package/dist/side-panel/side-panel.js +2 -1
  257. package/dist/toasts/messages.js +8 -0
  258. package/dist/toasts/toast.js +2 -1
  259. package/dist/users/app-invitations.js +5 -4
  260. package/dist/users/create-user-form.js +9 -8
  261. package/dist/users/messages.js +52 -0
  262. package/dist/users/use-user-roles.js +2 -1
  263. package/dist/users/user-list.js +6 -5
  264. package/dist/users/user-row.js +5 -4
  265. package/package.json +4 -4
  266. package/dist/shell/assets/app-C-OLrsRR.js +0 -69
  267. package/dist/shell/assets/export-worker-Bz57Pf5g.js +0 -2
  268. package/dist/shell/assets/maplibre-Dov6nhPX.js +0 -814
  269. package/dist/shell/assets/parse-worker-BtcY0dQw.js +0 -5
  270. package/dist/shell/assets/records-map-C1_gDKHR.js +0 -1
  271. package/dist/shell/assets/shared-protobase-ui-CFYJAbMR.js +0 -1
package/dist/app/app.js CHANGED
@@ -1,18 +1,21 @@
1
- import { accountMenuItems } from "../account/account-pages.js";
2
1
  import { setFallbackLocale } from "../locale/active-locale.js";
2
+ import { accountMenuItems } from "../account/account-pages.js";
3
3
  import { AppShell } from "../app-shell/app-shell.js";
4
4
  import { AuthProvider, useAuth } from "../auth/auth-provider.js";
5
5
  import { AccountSecurityPage } from "../account/account-security-page.js";
6
6
  import { SignInPolicyPage } from "../account/sign-in-policy-page.js";
7
7
  import { ApiProvider } from "../data/api-provider.js";
8
+ import { text } from "./messages.js";
8
9
  import { Router, useRouter } from "./router.js";
10
+ import { slotted } from "../locale/slotted.js";
9
11
  import { useSignInAddress } from "../auth/use-sign-in-address.js";
10
- import { managesOrganization, organizationMenuItems, organizationPageTitles } from "../organizations/organization-paths.js";
12
+ import { managesOrganization, organizationMenuItems, organizationPageTitle } from "../organizations/organization-paths.js";
11
13
  import { startErrorReporting } from "./error-reporting.js";
12
14
  import { CustomBoundary } from "./pages/custom-boundary.js";
13
15
  import { PluginSourceProvider } from "../plugins/plugin-source.js";
14
16
  import { AuthGate } from "../auth/auth-gate.js";
15
17
  import { isAdmin, userToShell } from "../auth/user-to-shell.js";
18
+ import { AuthLocale } from "../auth/auth-locale.js";
16
19
  import { sourceStatusOf } from "../data/with-sources.js";
17
20
  import { useLiveUpdates } from "../data/use-live-updates.js";
18
21
  import { useOpenedRecord } from "../data/use-record.js";
@@ -137,8 +140,8 @@ const Routes = ({ workspace, sidebarMode, assistant, runtime }) => {
137
140
  const organizations = useOrganizations(Boolean(organizationSettings && user));
138
141
  const current = organizations.current;
139
142
  const outsideOrganizations = Boolean(organizationSettings) && current !== void 0 && !current.organization;
140
- const organizationPage = organizationSettings && user ? organizationPageTitles[path] : void 0;
141
- const accountPage = !user ? void 0 : path === "/-/account" ? "Sign-in & security" : path === "/-/sign-in-policy" ? "Sign-in policy" : organizationPage;
143
+ const organizationPage = organizationSettings && user ? organizationPageTitle(path) : void 0;
144
+ const accountPage = !user ? void 0 : path === "/-/account" ? text("signInSecurity") : path === "/-/sign-in-policy" ? text("signInPolicy") : organizationPage;
142
145
  const resourceLabel = view?.names?.plural ?? (resource && humanize(resource));
143
146
  const breadcrumb = accountPage ? [accountPage] : breadcrumbFor({
144
147
  basePath,
@@ -160,31 +163,27 @@ const Routes = ({ workspace, sidebarMode, assistant, runtime }) => {
160
163
  recordLoading: titleLoading
161
164
  }), meta.appName));
162
165
  const content = user && path === "/-/account" ? /* @__PURE__ */ jsx(AccountSecurityPage, { email: user.email }) : user && path === "/-/sign-in-policy" ? admin ? /* @__PURE__ */ jsx(SignInPolicyPage, {}) : /* @__PURE__ */ jsx(Notice, {
163
- title: "Admins only",
164
- children: "Only an admin can change how people sign in."
166
+ title: text("adminsOnly"),
167
+ children: text("onlyAdminPolicy")
165
168
  }) : organizationPage && path === "/-/organization" ? /* @__PURE__ */ jsx(OrganizationPage, {
166
169
  current,
167
170
  basePath,
168
171
  onChanged: organizations.reload
169
172
  }) : organizationPage && (path === "/-/organization/members" || path === "/-/organization/invitations") ? !managesOrganization(current) ? /* @__PURE__ */ jsx(Notice, {
170
- title: "Owners and admins only",
171
- children: "Only the organization's owners and admins manage its members."
173
+ title: text("ownersOnly"),
174
+ children: text("onlyOwners")
172
175
  }) : path === "/-/organization/members" ? /* @__PURE__ */ jsx(MembersPage, { current }) : /* @__PURE__ */ jsx(InvitationsPage, { current }) : organizationPage && path === "/-/organizations" || outsideOrganizations ? /* @__PURE__ */ jsx(OrganizationsPage, {
173
176
  state: organizations,
174
177
  basePath
175
178
  }) : !address ? path === "/" ? /* @__PURE__ */ jsx(Notice, {
176
- title: "Choose a resource",
177
- children: "Pick one from the sidebar."
179
+ title: text("chooseResource"),
180
+ children: text("pickFromSidebar")
178
181
  }) : /* @__PURE__ */ jsx(Notice, {
179
- title: "Not found",
180
- children: "There is nothing at this address."
181
- }) : address.kind === "page" ? page ? /* @__PURE__ */ jsx(ComposedPage, { name: page.name }, page.name) : /* @__PURE__ */ jsxs(Notice, {
182
- title: "Not found",
183
- children: [
184
- "There is no page called ",
185
- address.page,
186
- "."
187
- ]
182
+ title: text("notFound"),
183
+ children: text("nothingHere")
184
+ }) : address.kind === "page" ? page ? /* @__PURE__ */ jsx(ComposedPage, { name: page.name }, page.name) : /* @__PURE__ */ jsx(Notice, {
185
+ title: text("notFound"),
186
+ children: slotted(text("noPage"), address.page)
188
187
  }) : drawnFrom && drawnFrom.state !== "ready" ? /* @__PURE__ */ jsx(SourceNotice, { status: drawnFrom }) : address.kind === "create" ? /* @__PURE__ */ jsx(CreateRoute, {
189
188
  resource: address.resource,
190
189
  values: address.values
@@ -315,7 +314,7 @@ const App = ({ auth, workspace, basePath, initialUrl, pluginLoader, locale, ...r
315
314
  initialUrl,
316
315
  children: /* @__PURE__ */ jsx(PluginSourceProvider, {
317
316
  ...pluginLoader && { loader: pluginLoader },
318
- children: /* @__PURE__ */ jsx(PublicFormGate, {
317
+ children: /* @__PURE__ */ jsx(AuthLocale, { children: /* @__PURE__ */ jsx(PublicFormGate, {
319
318
  baseUrl: rest.baseUrl,
320
319
  client: rest.client,
321
320
  ui: rest.ui,
@@ -326,7 +325,7 @@ const App = ({ auth, workspace, basePath, initialUrl, pluginLoader, locale, ...r
326
325
  ...rest
327
326
  })
328
327
  })
329
- })
328
+ }) })
330
329
  })
331
330
  })
332
331
  }) });
@@ -1,4 +1,5 @@
1
1
  import { Button } from "../primitives/button.js";
2
+ import { text } from "./messages.js";
2
3
  import { AssistantDock } from "../assistant-dock/assistant-dock.js";
3
4
  import { useAssistant } from "../assistant-dock/use-assistant.js";
4
5
  import { useAssistantClient } from "../assistant-dock/use-assistant-client.js";
@@ -14,7 +15,7 @@ const AssistantButton = ({ open, onToggle }) => /* @__PURE__ */ jsxs(Button, {
14
15
  className: "max-md:size-9 max-md:p-0",
15
16
  children: [/* @__PURE__ */ jsx(Sparkles, { className: "size-3.5" }), /* @__PURE__ */ jsx("span", {
16
17
  className: "max-md:sr-only",
17
- children: "Assistant"
18
+ children: text("assistant")
18
19
  })]
19
20
  });
20
21
  const ConnectedDock = ({ client, page, onClose }) => {
@@ -1,3 +1,4 @@
1
+ import { text } from "./messages.js";
1
2
  import "@protobase/client";
2
3
  //#region src/app/batch-failure.ts
3
4
  const label = (op) => {
@@ -8,7 +9,7 @@ const label = (op) => {
8
9
  const describeBatchFailure = (error, ops) => {
9
10
  const index = error.operation;
10
11
  const op = index === void 0 ? void 0 : ops[index];
11
- return op ? `Operation ${index + 1} (${label(op)}) failed: ${error.message} Nothing was saved.` : `${error.message} Nothing was saved.`;
12
+ return op ? text("operationFailed", index + 1, label(op), error.message) : text("nothingSaved", error.message);
12
13
  };
13
14
  /** The same error with the failing operation named in its message, keeping its class so a 412 stays a 412. */
14
15
  const nameFailingOperation = (error, ops) => {
@@ -1,3 +1,4 @@
1
+ import { text } from "./messages.js";
1
2
  import { appPath } from "@protobase/schema";
2
3
  //#region src/app/breadcrumb-for.ts
3
4
  /** Group, then the page or resource list, then the record (or "New"): each with a page of its own links to it. */
@@ -10,7 +11,7 @@ const breadcrumbFor = ({ basePath, address, group, page, resourceLabel, recordTi
10
11
  page: page.name
11
12
  }, basePath)
12
13
  }];
13
- if (!address || address.kind === "page") return ["Admin"];
14
+ if (!address || address.kind === "page") return [text("admin")];
14
15
  const resource = resourceLabel ? [{
15
16
  label: resourceLabel,
16
17
  href: appPath({
@@ -22,10 +23,10 @@ const breadcrumbFor = ({ basePath, address, group, page, resourceLabel, recordTi
22
23
  if (address.kind === "list") return [...groupCrumb, ...resource];
23
24
  const href = appPath(address, basePath);
24
25
  const record = address.kind === "create" ? {
25
- label: "New",
26
+ label: text("new"),
26
27
  href
27
28
  } : recordTitle === void 0 && recordLoading ? {
28
- label: "Loading record",
29
+ label: text("loadingRecord"),
29
30
  href,
30
31
  loading: true
31
32
  } : {
@@ -1,4 +1,5 @@
1
1
  import { formatTag } from "../../locale/active-locale.js";
2
+ import { text } from "./messages.js";
2
3
  import { createWhere, enumLabel, printFilter } from "@protobase/schema";
3
4
  //#region src/app/charts/chart-groups.ts
4
5
  const where$1 = createWhere();
@@ -12,9 +13,9 @@ const colorOf = (field, hints, value, index) => {
12
13
  return slot(index);
13
14
  };
14
15
  const labelOf = (field, hints, group) => {
15
- if (group.value === null) return "Not set";
16
+ if (group.value === null) return text("notSet");
16
17
  if (field.type === "enum") return enumLabel(String(group.value), hints?.valueLabels);
17
- if (field.type === "boolean") return group.value ? "Yes" : "No";
18
+ if (field.type === "boolean") return group.value ? text("yes") : text("no");
18
19
  return group.label ?? String(group.value);
19
20
  };
20
21
  /** The records of one group: the chart's own filter and the group's value. */
@@ -3,6 +3,7 @@ import { viewerZone } from "../../format/zone.js";
3
3
  import { labelFieldOf } from "../../live/model-helpers.js";
4
4
  import { useAdminMeta } from "../meta-gate.js";
5
5
  import { useAggregate } from "../../data/use-aggregates.js";
6
+ import { text } from "./messages.js";
6
7
  import { periodPoints, valueSlices } from "./chart-groups.js";
7
8
  import { GroupBars } from "./group-bars.js";
8
9
  import { GroupDonut } from "./group-donut.js";
@@ -28,7 +29,7 @@ const labelOf = (field, resources, views) => {
28
29
  * Loads a chart's groups through `:aggregate` and draws them: a date field's buckets as bars or a line, a field's
29
30
  * values as ranked bars or a donut, each value linking to its records. The server checked the chart at startup.
30
31
  */
31
- const ChartView = ({ model, view, groupBy, splitBy, type = "bar", sum, average, filter = "", granularity = "day", range = "30d", limit = 10, empty = "Nothing to chart yet." }) => {
32
+ const ChartView = ({ model, view, groupBy, splitBy, type = "bar", sum, average, filter = "", granularity = "day", range = "30d", limit = 10, empty = text("nothingYet") }) => {
32
33
  const { resources, views } = useAdminMeta();
33
34
  const field = model.fields[groupBy];
34
35
  const dated = field?.type === "date" || field?.type === "timestamp";
@@ -61,7 +62,7 @@ const ChartView = ({ model, view, groupBy, splitBy, type = "bar", sum, average,
61
62
  if (result.isPending) return /* @__PURE__ */ jsx(Skeleton, { className: "h-36 w-full" });
62
63
  if (result.error) return /* @__PURE__ */ jsx("p", {
63
64
  className: "text-danger-text",
64
- children: "Could not load the chart."
65
+ children: text("couldNotLoad")
65
66
  });
66
67
  const { groups } = result.data;
67
68
  if (groups.every((group) => group.count === 0)) return /* @__PURE__ */ jsx("p", {
@@ -1,4 +1,5 @@
1
1
  import { AppLink } from "../use-app-address.js";
2
+ import { text } from "./messages.js";
2
3
  import { measureText } from "./chart-groups.js";
3
4
  import { filteredListAddress } from "../list-link.js";
4
5
  import { jsx, jsxs } from "react/jsx-runtime";
@@ -16,7 +17,7 @@ const GroupBars = ({ resource, view, slices, measure }) => {
16
17
  return /* @__PURE__ */ jsx("li", { children: /* @__PURE__ */ jsxs(AppLink, {
17
18
  to: filteredListAddress(view, resource, slice.filter),
18
19
  className: "group grid grid-cols-[minmax(6rem,35%)_1fr_auto] items-center gap-3 rounded-md px-1 py-1 hover:bg-muted",
19
- title: measure.kind === "count" ? void 0 : `${measureText(slice.count, { kind: "count" })} records`,
20
+ title: measure.kind === "count" ? void 0 : text("records", measureText(slice.count, { kind: "count" })),
20
21
  children: [
21
22
  /* @__PURE__ */ jsx("span", {
22
23
  className: "truncate group-hover:text-primary-text",
@@ -0,0 +1,26 @@
1
+ import { defineMessages } from "../../locale/messages.js";
2
+ //#region src/app/charts/messages.ts
3
+ const text = defineMessages({
4
+ en: {
5
+ notSet: "Not set",
6
+ yes: "Yes",
7
+ no: "No",
8
+ nothingYet: "Nothing to chart yet.",
9
+ couldNotLoad: "Could not load the chart.",
10
+ weekOf: (day) => `Week of ${day}`,
11
+ other: "Other",
12
+ records: (count) => `${count} records`
13
+ },
14
+ nl: {
15
+ notSet: "Niet ingevuld",
16
+ yes: "Ja",
17
+ no: "Nee",
18
+ nothingYet: "Nog niets om in een grafiek te zetten.",
19
+ couldNotLoad: "Kon de grafiek niet laden.",
20
+ weekOf: (day) => `Week van ${day}`,
21
+ other: "Overig",
22
+ records: (count) => `${count} records`
23
+ }
24
+ });
25
+ //#endregion
26
+ export { text };
@@ -1,5 +1,6 @@
1
- import { useMediaQuery } from "../../lib/use-media-query.js";
2
1
  import { formatTag } from "../../locale/active-locale.js";
2
+ import { useMediaQuery } from "../../lib/use-media-query.js";
3
+ import { text } from "./messages.js";
3
4
  import { measureText } from "./chart-groups.js";
4
5
  import { LegendItem } from "./legend-item.js";
5
6
  import { jsx, jsxs } from "react/jsx-runtime";
@@ -68,7 +69,7 @@ const PointTooltip = ({ active, payload, measure, bucket, series }) => {
68
69
  children: [
69
70
  /* @__PURE__ */ jsx("div", {
70
71
  className: "text-muted-foreground",
71
- children: (bucket === "week" ? "Week of " : "") + formatterOf(tooltipFormats[bucket])(point.at)
72
+ children: bucket === "week" ? text("weekOf", formatterOf(tooltipFormats[bucket])(point.at)) : formatterOf(tooltipFormats[bucket])(point.at)
72
73
  }),
73
74
  series?.map((part) => /* @__PURE__ */ jsxs("div", {
74
75
  className: "flex items-center gap-2",
@@ -1,3 +1,4 @@
1
+ import { text } from "./messages.js";
1
2
  import { colorOf, groupFilter, labelOf, periodPoints } from "./chart-groups.js";
2
3
  //#region src/app/charts/split-points.ts
3
4
  const noise = 1e-6;
@@ -42,7 +43,7 @@ const splitPoints = (groups, splits, field, hints, measure, filter = "") => {
42
43
  return {
43
44
  series: [...series, {
44
45
  key: "other",
45
- label: "Other",
46
+ label: text("other"),
46
47
  color: "var(--border-strong)"
47
48
  }],
48
49
  points
@@ -7,6 +7,7 @@ import { menuKeyTarget } from "../../primitives/menu-keys.js";
7
7
  import { activeMention, insertMention, mentionedIds } from "./mention-text.js";
8
8
  import { useMentionable } from "./use-mentionable.js";
9
9
  import { usePostComment } from "./use-post-comment.js";
10
+ import { text } from "./messages.js";
10
11
  import { useEffect, useId, useLayoutEffect, useRef, useState } from "react";
11
12
  import { jsx, jsxs } from "react/jsx-runtime";
12
13
  //#region src/app/comments/comment-composer.tsx
@@ -17,7 +18,7 @@ const shownPeople = 8;
17
18
  * the arrows move, Enter or Tab picks, Escape closes. ⌘/Ctrl+Enter or the button posts.
18
19
  */
19
20
  const CommentComposer = ({ resource, recordKey }) => {
20
- const [text, setText] = useState("");
21
+ const [draft, setDraft] = useState("");
21
22
  const [caret, setCaret] = useState(0);
22
23
  const [focused, setFocused] = useState(false);
23
24
  const [picked, setPicked] = useState([]);
@@ -28,7 +29,7 @@ const CommentComposer = ({ resource, recordKey }) => {
28
29
  const listId = useId();
29
30
  const toast = useToast();
30
31
  const post = usePostComment(resource, recordKey);
31
- const mention = activeMention(text, caret);
32
+ const mention = activeMention(draft, caret);
32
33
  const open = focused && mention !== void 0 && mention.start !== closed;
33
34
  const people = useMentionable(resource, recordKey, open ? mention.query : void 0);
34
35
  const options = open ? (people.data ?? []).slice(0, shownPeople) : [];
@@ -44,27 +45,27 @@ const CommentComposer = ({ resource, recordKey }) => {
44
45
  box.current.setSelectionRange(nextCaret.current, nextCaret.current);
45
46
  setCaret(nextCaret.current);
46
47
  nextCaret.current = void 0;
47
- }, [text]);
48
+ }, [draft]);
48
49
  const pick = (user) => {
49
50
  if (!mention) return;
50
- const next = insertMention(text, mention, user.name);
51
+ const next = insertMention(draft, mention, user.name);
51
52
  nextCaret.current = next.caret;
52
- setText(next.text);
53
+ setDraft(next.text);
53
54
  setPicked((current) => [...current, user]);
54
55
  };
55
56
  const submit = () => {
56
- if (text.trim() === "" || post.isPending) return;
57
+ if (draft.trim() === "" || post.isPending) return;
57
58
  post.mutate({
58
- body: text,
59
- mentions: mentionedIds(text, picked)
59
+ body: draft,
60
+ mentions: mentionedIds(draft, picked)
60
61
  }, {
61
62
  onSuccess: () => {
62
- setText("");
63
+ setDraft("");
63
64
  setPicked([]);
64
65
  },
65
66
  onError: (error) => toast.show({
66
67
  state: "error",
67
- title: "Could not post the comment",
68
+ title: text("couldNotPost"),
68
69
  description: refusalReason(error)
69
70
  })
70
71
  });
@@ -100,16 +101,16 @@ const CommentComposer = ({ resource, recordKey }) => {
100
101
  children: [/* @__PURE__ */ jsx(Textarea, {
101
102
  ref: box,
102
103
  role: "combobox",
103
- "aria-label": "Comment",
104
+ "aria-label": text("comment"),
104
105
  "aria-multiline": true,
105
106
  "aria-autocomplete": "list",
106
107
  "aria-expanded": listed,
107
108
  "aria-controls": listId,
108
109
  "aria-activedescendant": listed && options[active] ? optionId(active) : void 0,
109
- placeholder: "Write a comment, @ to mention someone",
110
- value: text,
110
+ placeholder: text("placeholder"),
111
+ value: draft,
111
112
  onChange: (event) => {
112
- setText(event.target.value);
113
+ setDraft(event.target.value);
113
114
  setCaret(event.target.selectionStart);
114
115
  },
115
116
  onSelect: (event) => setCaret(event.currentTarget.selectionStart),
@@ -119,7 +120,7 @@ const CommentComposer = ({ resource, recordKey }) => {
119
120
  }), listed && /* @__PURE__ */ jsxs("ul", {
120
121
  id: listId,
121
122
  role: "listbox",
122
- "aria-label": "People to mention",
123
+ "aria-label": text("peopleToMention"),
123
124
  className: "absolute z-30 mt-1 w-full max-w-sm rounded-md border bg-background py-1 shadow-pop",
124
125
  onMouseDown: (event) => event.preventDefault(),
125
126
  children: [options.map((user, index) => /* @__PURE__ */ jsx("li", {
@@ -132,16 +133,16 @@ const CommentComposer = ({ resource, recordKey }) => {
132
133
  children: user.name
133
134
  }, user.id)), options.length === 0 && /* @__PURE__ */ jsx("li", {
134
135
  className: "px-3 py-1.5 text-muted-foreground",
135
- children: "No one found"
136
+ children: text("noOneFound")
136
137
  })]
137
138
  })]
138
139
  }), /* @__PURE__ */ jsx("div", { children: /* @__PURE__ */ jsx(Button, {
139
140
  variant: "primary",
140
141
  size: "sm",
141
- disabled: text.trim() === "",
142
+ disabled: draft.trim() === "",
142
143
  loading: post.isPending,
143
144
  onClick: submit,
144
- children: "Comment"
145
+ children: text("comment")
145
146
  }) })]
146
147
  });
147
148
  };
@@ -6,6 +6,7 @@ import { hueOf, initialsOf } from "../../auth/user-to-shell.js";
6
6
  import { Section } from "../../record-view/section.js";
7
7
  import { ErrorBanner } from "../error-banner.js";
8
8
  import { CommentBody } from "./comment-body.js";
9
+ import { text } from "./messages.js";
9
10
  import { CommentComposer } from "./comment-composer.js";
10
11
  import { useRecordComments } from "./use-record-comments.js";
11
12
  import { Fragment, jsx, jsxs } from "react/jsx-runtime";
@@ -51,17 +52,17 @@ const CommentsSection = ({ item, resource, recordKey, canWrite }) => {
51
52
  return /* @__PURE__ */ jsxs(Section, {
52
53
  title: item.title,
53
54
  children: [comments.isPending ? /* @__PURE__ */ jsx(Loading, {}) : comments.error ? /* @__PURE__ */ jsx(ErrorBanner, {
54
- title: "Could not load the comments",
55
+ title: text("couldNotLoad"),
55
56
  error: comments.error
56
57
  }) : /* @__PURE__ */ jsxs(Fragment, { children: [comments.hasNextPage && /* @__PURE__ */ jsx(Button, {
57
58
  size: "sm",
58
59
  className: "mb-4",
59
60
  disabled: comments.isFetchingNextPage,
60
61
  onClick: () => void comments.fetchNextPage(),
61
- children: "Show earlier comments"
62
+ children: text("showEarlier")
62
63
  }), comments.items.length === 0 ? /* @__PURE__ */ jsx("p", {
63
64
  className: "text-muted-foreground",
64
- children: "No comments yet."
65
+ children: text("noComments")
65
66
  }) : /* @__PURE__ */ jsx("ol", {
66
67
  className: "divide-y divide-border",
67
68
  children: comments.items.map((comment) => /* @__PURE__ */ jsx(Comment, { comment }, comment.id))
@@ -0,0 +1,26 @@
1
+ import { defineMessages } from "../../locale/messages.js";
2
+ //#region src/app/comments/messages.ts
3
+ const text = defineMessages({
4
+ en: {
5
+ couldNotPost: "Could not post the comment",
6
+ comment: "Comment",
7
+ placeholder: "Write a comment, @ to mention someone",
8
+ peopleToMention: "People to mention",
9
+ noOneFound: "No one found",
10
+ couldNotLoad: "Could not load the comments",
11
+ showEarlier: "Show earlier comments",
12
+ noComments: "No comments yet."
13
+ },
14
+ nl: {
15
+ couldNotPost: "Kon de opmerking niet plaatsen",
16
+ comment: "Opmerking",
17
+ placeholder: "Schrijf een opmerking, @ om iemand te noemen",
18
+ peopleToMention: "Mensen om te noemen",
19
+ noOneFound: "Niemand gevonden",
20
+ couldNotLoad: "Kon de opmerkingen niet laden",
21
+ showEarlier: "Eerdere opmerkingen tonen",
22
+ noComments: "Nog geen opmerkingen."
23
+ }
24
+ });
25
+ //#endregion
26
+ export { text };
@@ -1,9 +1,10 @@
1
1
  import { useClient } from "../../data/api-provider.js";
2
+ import { text } from "../messages.js";
2
3
  import { humanize } from "../../live/naming.js";
3
4
  import { recordKey } from "../../live/model-helpers.js";
4
5
  import { Notice, useAdminMeta, usePermissions } from "../meta-gate.js";
5
6
  import { useAppAddress } from "../use-app-address.js";
6
- import { text } from "./messages.js";
7
+ import { text as text$1 } from "./messages.js";
7
8
  import { createBody, createSections, hasDatabaseDefault, initialDraft, isChosen, isRequired } from "./create-values.js";
8
9
  import { FieldEditor } from "../field-editor.js";
9
10
  import { fieldErrors, fieldMessage } from "../field-errors.js";
@@ -13,7 +14,7 @@ import { fetchLabels } from "../../live/relation-labels.js";
13
14
  import { useLiveSave } from "../use-live-save.js";
14
15
  import { CreateRecordForm } from "./create-record-form.js";
15
16
  import { useEffect, useState } from "react";
16
- import { jsx, jsxs } from "react/jsx-runtime";
17
+ import { jsx } from "react/jsx-runtime";
17
18
  import { useQueryClient } from "@tanstack/react-query";
18
19
  //#region src/app/create/create-page.tsx
19
20
  const without = (source, name) => Object.fromEntries(Object.entries(source).filter(([key]) => key !== name));
@@ -22,21 +23,13 @@ const CreatePage = ({ resource, values }) => {
22
23
  const meta = useAdminMeta();
23
24
  const model = meta.resources[resource];
24
25
  const permissions = usePermissions(resource);
25
- if (!model) return /* @__PURE__ */ jsxs(Notice, {
26
- title: "Not found",
27
- children: [
28
- "There is no resource called ",
29
- resource,
30
- "."
31
- ]
26
+ if (!model) return /* @__PURE__ */ jsx(Notice, {
27
+ title: text("notFound"),
28
+ children: text("noResource", resource)
32
29
  });
33
- if (!can(permissions, "create")) return /* @__PURE__ */ jsxs(Notice, {
34
- title: "Not allowed",
35
- children: [
36
- "You cannot create ",
37
- meta.views[resource]?.names?.plural.toLowerCase() ?? resource,
38
- "."
39
- ]
30
+ if (!can(permissions, "create")) return /* @__PURE__ */ jsx(Notice, {
31
+ title: text$1("notAllowed"),
32
+ children: text$1("cannotCreate", meta.views[resource]?.names?.plural.toLowerCase() ?? resource)
40
33
  });
41
34
  return /* @__PURE__ */ jsx(LoadedCreate, {
42
35
  resource,
@@ -51,7 +44,7 @@ const LoadedCreate = ({ resource, values }) => {
51
44
  const queryClient = useQueryClient();
52
45
  const { go } = useAppAddress();
53
46
  const noun = view?.names?.singular ?? humanize(resource);
54
- const feedback = useLiveSave(view?.saveFeedback ?? "toast", text("subject", noun));
47
+ const feedback = useLiveSave(view?.saveFeedback ?? "toast", text$1("subject", noun));
55
48
  const sections = createSections(model, view);
56
49
  const [draft, setDraft] = useState(() => initialDraft(model, sections, values));
57
50
  const [errors, setErrors] = useState({});
@@ -132,7 +125,7 @@ const LoadedCreate = ({ resource, values }) => {
132
125
  value: isChosen(value) ? value.key : value ?? (field.type === "boolean" ? false : ""),
133
126
  stored: void 0,
134
127
  relationLabel: isChosen(value) ? value.label : void 0,
135
- placeholder: hasDatabaseDefault(field) ? text("setByDatabase") : void 0,
128
+ placeholder: hasDatabaseDefault(field) ? text$1("setByDatabase") : void 0,
136
129
  relationPicker: target ? {
137
130
  target,
138
131
  view: meta.views[target.name]
@@ -1,3 +1,5 @@
1
+ import { text } from "./messages.js";
2
+ import { slotted } from "../locale/slotted.js";
1
3
  import { Problem } from "./pages/problem.js";
2
4
  import { CustomBoundary } from "./pages/custom-boundary.js";
3
5
  import { Section } from "../record-view/section.js";
@@ -5,7 +7,7 @@ import { SidebarCard } from "../record-view/record-sidebar.js";
5
7
  import { useProjectUi } from "./pages/project-ui.js";
6
8
  import { RecordScope } from "./pages/record-scope.js";
7
9
  import { unmarkFieldRefs } from "@protobase/schema/internal";
8
- import { jsx, jsxs } from "react/jsx-runtime";
10
+ import { jsx } from "react/jsx-runtime";
9
11
  //#region src/app/custom-section.tsx
10
12
  /**
11
13
  * A component of the record layout (`l.custom`), drawn with the record around it, so `useLayoutRecord()` gives it, in a
@@ -20,11 +22,7 @@ const CustomSection = ({ item, scope }) => {
20
22
  name: item.component,
21
23
  children: /* @__PURE__ */ jsx(Component, { ...unmarkFieldRefs(item.props ?? {}) })
22
24
  })
23
- }) : /* @__PURE__ */ jsxs(Problem, { children: [
24
- "No component is registered as ",
25
- item.component,
26
- ". Export it from protobase.ui.tsx, or load the plugin that provides it."
27
- ] });
25
+ }) : /* @__PURE__ */ jsx(Problem, { children: slotted(text("noComponent"), item.component) });
28
26
  if (item.sidebar) return item.title ? /* @__PURE__ */ jsx(SidebarCard, {
29
27
  title: item.title,
30
28
  children: body
@@ -1,8 +1,9 @@
1
- import { cn } from "../../lib/cn.js";
2
1
  import { formatTag } from "../../locale/active-locale.js";
2
+ import { cn } from "../../lib/cn.js";
3
3
  import { Skeleton } from "../../primitives/skeleton.js";
4
4
  import { Button } from "../../primitives/button.js";
5
5
  import { humanize } from "../../live/naming.js";
6
+ import { text } from "./messages.js";
6
7
  import { boardCard } from "./board-columns.js";
7
8
  import { ErrorBanner } from "../error-banner.js";
8
9
  import { rowAllows } from "../permissions.js";
@@ -60,7 +61,7 @@ const BoardColumnView = ({ model, view, display, column, fields, filter, orderBy
60
61
  /* @__PURE__ */ jsxs("header", {
61
62
  className: "flex items-center justify-between gap-2 px-1 pt-0.5",
62
63
  children: [/* @__PURE__ */ jsx("h2", {
63
- className: "min-w-0 truncate text-xs font-medium",
64
+ className: "min-w-0 truncate text-body font-medium",
64
65
  children: column.value === null ? /* @__PURE__ */ jsx("span", {
65
66
  className: "text-muted-foreground",
66
67
  children: column.label
@@ -75,11 +76,11 @@ const BoardColumnView = ({ model, view, display, column, fields, filter, orderBy
75
76
  })]
76
77
  }),
77
78
  list.loading ? Array.from({ length: 3 }, (_, index) => /* @__PURE__ */ jsx(Skeleton, { className: "h-20" }, index)) : list.error ? /* @__PURE__ */ jsx(ErrorBanner, {
78
- title: "Could not load rows",
79
+ title: text("couldNotLoadRows"),
79
80
  error: list.error
80
- }) : rows.length === 0 ? /* @__PURE__ */ jsxs("p", {
81
+ }) : rows.length === 0 ? /* @__PURE__ */ jsx("p", {
81
82
  className: "px-1 py-4 text-center text-xs text-muted-foreground",
82
- children: ["No ", plural]
83
+ children: text("noPlural", plural)
83
84
  }) : /* @__PURE__ */ jsx(SortableContext, {
84
85
  id: columnId(column.value),
85
86
  items: rows.map((row) => row.key),
@@ -103,7 +104,7 @@ const BoardColumnView = ({ model, view, display, column, fields, filter, orderBy
103
104
  size: "sm",
104
105
  variant: "ghost",
105
106
  onClick: list.loadMore,
106
- children: "Show more"
107
+ children: text("showMore")
107
108
  })
108
109
  ]
109
110
  });
@@ -1,5 +1,6 @@
1
1
  import { humanize } from "../../live/naming.js";
2
2
  import { recordTitleField } from "../../live/model-helpers.js";
3
+ import { text } from "./messages.js";
3
4
  import { isApiError } from "@protobase/client";
4
5
  import { enumLabel, printFilter, where } from "@protobase/schema";
5
6
  //#region src/app/displays/board-columns.ts
@@ -18,7 +19,7 @@ const boardColumns = (model, view, display) => {
18
19
  if (!field.nullable) return values;
19
20
  return [...values, {
20
21
  value: null,
21
- label: `No ${(hints?.label ?? humanize(display.by)).toLowerCase()}`
22
+ label: text("noValue", hints?.label ?? humanize(display.by))
22
23
  }];
23
24
  };
24
25
  /** The field a board orders each column by: the resource's `.position()`, when its records are ordered as one rather than within a parent. */
@@ -39,7 +40,7 @@ const boardCard = (model, view, display) => ({
39
40
  });
40
41
  /** Why the server refused a move: the reasons it gives per field, such as a `validate` message, else its detail. */
41
42
  const refusalReason = (error) => {
42
- if (!isApiError(error)) return error instanceof Error ? error.message : "Something went wrong";
43
+ if (!isApiError(error)) return error instanceof Error ? error.message : text("somethingWrong");
43
44
  const reasons = error.errors.map((entry) => entry.message);
44
45
  return reasons.length > 0 ? reasons.join("; ") : error.message;
45
46
  };