@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
@@ -0,0 +1,22 @@
1
+ import { defineMessages } from "../../locale/messages.js";
2
+ //#region src/app/saved/messages.ts
3
+ const text = defineMessages({
4
+ en: {
5
+ favorite: "Favorite",
6
+ couldNotLoadYour: (plural) => `Could not load your ${plural}`,
7
+ noneYet: (plural) => `No ${plural} yet.`,
8
+ somethingWrong: "Something went wrong",
9
+ couldNotTakeOff: "Could not take it off",
10
+ couldNotSave: "Could not save it"
11
+ },
12
+ nl: {
13
+ favorite: "Favoriet",
14
+ couldNotLoadYour: (plural) => `Kon je ${plural} niet laden`,
15
+ noneYet: (plural) => `Nog geen ${plural}.`,
16
+ somethingWrong: "Er ging iets mis",
17
+ couldNotTakeOff: "Kon het niet weghalen",
18
+ couldNotSave: "Kon het niet bewaren"
19
+ }
20
+ });
21
+ //#endregion
22
+ export { text };
@@ -4,6 +4,7 @@ import { humanize } from "../../live/naming.js";
4
4
  import { ErrorBanner } from "../error-banner.js";
5
5
  import { savedScope } from "./saved-records.js";
6
6
  import { useSavedRecords } from "./use-saved-records.js";
7
+ import { text } from "./messages.js";
7
8
  import { jsx, jsxs } from "react/jsx-runtime";
8
9
  //#region src/app/saved/saved-list.tsx
9
10
  /**
@@ -21,7 +22,7 @@ const SavedList = ({ model, view, target, children }) => {
21
22
  }), body]
22
23
  });
23
24
  if (saved.error) return frame(/* @__PURE__ */ jsx(ErrorBanner, {
24
- title: `Could not load your ${plural.toLowerCase()}`,
25
+ title: text("couldNotLoadYour", plural.toLowerCase()),
25
26
  error: saved.error
26
27
  }));
27
28
  if (!saved.data) return frame(/* @__PURE__ */ jsx("div", {
@@ -29,13 +30,9 @@ const SavedList = ({ model, view, target, children }) => {
29
30
  children: Array.from({ length: 3 }, (_, index) => /* @__PURE__ */ jsx(Skeleton, { className: "h-24" }, index))
30
31
  }));
31
32
  const scope = savedScope(target, saved.data);
32
- if (!scope) return frame(/* @__PURE__ */ jsxs("p", {
33
+ if (!scope) return frame(/* @__PURE__ */ jsx("p", {
33
34
  className: "py-8 text-center text-muted-foreground",
34
- children: [
35
- "No ",
36
- plural.toLowerCase(),
37
- " yet."
38
- ]
35
+ children: text("noneYet", plural.toLowerCase())
39
36
  }));
40
37
  return children(scope);
41
38
  };
@@ -5,9 +5,10 @@ import { can } from "../permissions.js";
5
5
  import { useRefreshResource } from "../pages/use-layout-data.js";
6
6
  import { savedFor, withSaved, withoutSaved } from "./saved-records.js";
7
7
  import { savedRecordsKey, useSavedRecords } from "./use-saved-records.js";
8
+ import { text } from "./messages.js";
8
9
  import { useMutation, useQueryClient } from "@tanstack/react-query";
9
10
  //#region src/app/saved/use-favorite.ts
10
- const message = (error) => error instanceof Error ? error.message : "Something went wrong";
11
+ const message = (error) => error instanceof Error ? error.message : text("somethingWrong");
11
12
  /**
12
13
  * A card's favorite button: `save` for someone who can list, create and delete the saved records, `sign-in` for a
13
14
  * visitor the saved records' resource asks to sign in (`prompt`), nothing otherwise. A press saves the card's record,
@@ -44,7 +45,7 @@ const useFavorite = (favorite, key, target) => {
44
45
  queryClient.setQueryData(cacheKey, context?.before);
45
46
  toast.show({
46
47
  state: "error",
47
- title: existing ? "Could not take it off" : "Could not save it",
48
+ title: existing ? text("couldNotTakeOff") : text("couldNotSave"),
48
49
  description: message(error)
49
50
  });
50
51
  },
@@ -5,13 +5,17 @@ const text = defineMessages({
5
5
  search: "Search",
6
6
  searchOne: (name) => `Search ${name}`,
7
7
  searchTwo: (first, second) => `Search ${first} and ${second}`,
8
- searchMore: (first, second) => `Search ${first}, ${second} and more`
8
+ searchMore: (first, second) => `Search ${first}, ${second} and more`,
9
+ goTo: "Go to",
10
+ create: "Create"
9
11
  },
10
12
  nl: {
11
13
  search: "Zoeken",
12
14
  searchOne: (name) => `Zoek in ${name}`,
13
15
  searchTwo: (first, second) => `Zoek in ${first} en ${second}`,
14
- searchMore: (first, second) => `Zoek in ${first}, ${second} en meer`
16
+ searchMore: (first, second) => `Zoek in ${first}, ${second} en meer`,
17
+ goTo: "Ga naar",
18
+ create: "Maken"
15
19
  }
16
20
  });
17
21
  //#endregion
@@ -1,7 +1,9 @@
1
1
  import { humanize } from "../../live/naming.js";
2
2
  import { allowEverything } from "../../data/meta-data.js";
3
+ import { text } from "../create/messages.js";
3
4
  import { createOffered } from "../create/create-offered.js";
4
5
  import { navFromMeta } from "../nav-from-meta.js";
6
+ import { text as text$1 } from "./messages.js";
5
7
  import { appPath } from "@protobase/schema";
6
8
  const matches = (label, text) => label.toLowerCase().includes(text.trim().toLowerCase());
7
9
  /**
@@ -9,15 +11,15 @@ const matches = (label, text) => label.toLowerCase().includes(text.trim().toLowe
9
11
  * Stock levels), and "New ..." for each resource the user may create whose name does ("new order" or "order" finds New
10
12
  * order). Each is a link, as the sidebar's entries are.
11
13
  */
12
- const navigationHits = (meta, text, basePath = "") => {
13
- const pages = navFromMeta(meta, basePath).flatMap((group) => group.items.filter((item) => item.href !== void 0 && !item.problem)).filter((item) => matches(item.label, text)).slice(0, 5);
14
- const asked = text.trim().replace(/^new\s+/i, "");
14
+ const navigationHits = (meta, text$2, basePath = "") => {
15
+ const pages = navFromMeta(meta, basePath).flatMap((group) => group.items.filter((item) => item.href !== void 0 && !item.problem)).filter((item) => matches(item.label, text$2)).slice(0, 5);
16
+ const asked = text$2.trim().replace(/^new\s+/i, "");
15
17
  const creates = Object.values(meta.resources).flatMap((model) => {
16
18
  const view = meta.views[model.name];
17
19
  if (!view || view.nav?.hidden || !createOffered(view, meta.pages, meta.permissions[model.name] ?? allowEverything)) return [];
18
20
  const singular = view.names?.singular ?? humanize(model.name);
19
- const label = `New ${singular.toLowerCase()}`;
20
- if (!matches(label, text) && !matches(singular, asked) && !matches(view.names?.plural ?? humanize(model.name), asked)) return [];
21
+ const label = text("title", singular);
22
+ if (!matches(label, text$2) && !matches(singular, asked) && !matches(view.names?.plural ?? humanize(model.name), asked)) return [];
21
23
  return [{
22
24
  id: `new:${model.name}`,
23
25
  title: label,
@@ -29,7 +31,7 @@ const navigationHits = (meta, text, basePath = "") => {
29
31
  });
30
32
  return [...pages.length > 0 ? [{
31
33
  id: "go-to",
32
- label: "Go to",
34
+ label: text$1("goTo"),
33
35
  hits: pages.map((item) => ({
34
36
  id: `go-to:${item.id}`,
35
37
  title: item.label,
@@ -37,7 +39,7 @@ const navigationHits = (meta, text, basePath = "") => {
37
39
  }))
38
40
  }] : [], ...creates.length > 0 ? [{
39
41
  id: "create",
40
- label: "Create",
42
+ label: text$1("create"),
41
43
  hits: creates.slice(0, 5)
42
44
  }] : []];
43
45
  };
@@ -1,11 +1,12 @@
1
1
  import { cn } from "../../lib/cn.js";
2
+ import { text } from "./messages.js";
2
3
  import { jsx, jsxs } from "react/jsx-runtime";
3
4
  //#region src/app/searches/filter-chip-list.tsx
4
5
  const chipClass = "inline-flex h-6 items-center rounded-full border border-primary-border bg-primary-soft px-2.5 text-xs font-medium text-primary-text";
5
6
  /** What a filter filters on, concisely: its chips as a list labelled "Filters". */
6
7
  const FilterChipList = ({ chips, className }) => /* @__PURE__ */ jsx("ul", {
7
8
  className: cn("flex flex-wrap gap-1.5", className),
8
- "aria-label": "Filters",
9
+ "aria-label": text("filters"),
9
10
  children: chips.map((chip) => /* @__PURE__ */ jsx("li", {
10
11
  className: chipClass,
11
12
  children: chip.label
@@ -0,0 +1,96 @@
1
+ import { defineMessages } from "../../locale/messages.js";
2
+ //#region src/app/searches/messages.ts
3
+ const text = defineMessages({
4
+ en: {
5
+ saveSearch: "Save search",
6
+ saveAsNew: (noun) => `Save as new ${noun}`,
7
+ updateName: (name) => `Update ${name}`,
8
+ updateNameQuestion: (name) => `Update ${name}?`,
9
+ updateNounQuestion: (noun) => `Update this ${noun}?`,
10
+ couldNotLoadYour: (plural) => `Could not load your ${plural}`,
11
+ noneYet: (plural) => `No ${plural} yet.`,
12
+ somethingWrong: "Something went wrong",
13
+ couldNotRename: "Could not rename the search",
14
+ renameTitle: (title) => `Rename ${title}`,
15
+ deleteTitle: (title) => `Delete ${title}`,
16
+ unreadableFilter: "A filter that cannot be read",
17
+ notFound: "Not found",
18
+ noResource: "There is no resource called {}.",
19
+ noSavedYet: (plural) => `No ${plural} yet. Filter the {} and save the search.`,
20
+ on: "On",
21
+ off: "Off",
22
+ saved: (title) => `${title} saved`,
23
+ couldNotChange: "Could not change the search",
24
+ asMany: (plural) => `As many ${plural}`,
25
+ more: (count, noun) => `${count} more ${noun}`,
26
+ fewer: (count, noun) => `${count} fewer ${noun}`,
27
+ cancel: "Cancel",
28
+ save: "Save",
29
+ optional: "Optional",
30
+ added: "Added",
31
+ removed: "Removed",
32
+ changed: "Changed",
33
+ filterChanges: "Filter changes",
34
+ firstOf: (limit, total) => `The first ${limit} of ${total}.`,
35
+ noPlural: (plural) => `No ${plural}.`,
36
+ update: "Update",
37
+ addedRemoved: (added, removed) => `${added} added, ${removed} removed`,
38
+ searchSaved: "Search saved",
39
+ openPlural: (plural) => `Open ${plural.toLowerCase()}`,
40
+ couldNotSave: "Could not save the search",
41
+ couldNotTakeOff: "Could not take the search off",
42
+ searchUpdated: "Search updated",
43
+ couldNotUpdate: "Could not update the search",
44
+ noneMatch: (plural) => `No ${plural} match.`,
45
+ noNew: (plural) => `No new ${plural} since you last looked.`,
46
+ viewAll: "View all results",
47
+ filters: "Filters"
48
+ },
49
+ nl: {
50
+ saveSearch: "Zoekopdracht bewaren",
51
+ saveAsNew: (noun) => `Als extra ${noun} bewaren`,
52
+ updateName: (name) => `${name} bijwerken`,
53
+ updateNameQuestion: (name) => `${name} bijwerken?`,
54
+ updateNounQuestion: (noun) => `${noun} bijwerken?`,
55
+ couldNotLoadYour: (plural) => `Kon je ${plural} niet laden`,
56
+ noneYet: (plural) => `Nog geen ${plural}.`,
57
+ somethingWrong: "Er ging iets mis",
58
+ couldNotRename: "Kon de zoekopdracht niet hernoemen",
59
+ renameTitle: (title) => `${title} hernoemen`,
60
+ deleteTitle: (title) => `${title} verwijderen`,
61
+ unreadableFilter: "Een filter dat niet kan worden gelezen",
62
+ notFound: "Niet gevonden",
63
+ noResource: "Er is geen onderdeel met de naam {}.",
64
+ noSavedYet: (plural) => `Nog geen ${plural}. Filter de {} en bewaar de zoekopdracht.`,
65
+ on: "Aan",
66
+ off: "Uit",
67
+ saved: (title) => `${title} bewaard`,
68
+ couldNotChange: "Kon de zoekopdracht niet wijzigen",
69
+ asMany: (plural) => `Evenveel ${plural}`,
70
+ more: (count, noun) => `${count} ${noun} meer`,
71
+ fewer: (count, noun) => `${count} ${noun} minder`,
72
+ cancel: "Annuleren",
73
+ save: "Bewaren",
74
+ optional: "Optioneel",
75
+ added: "Erbij",
76
+ removed: "Eraf",
77
+ changed: "Gewijzigd",
78
+ filterChanges: "Wijzigingen in het filter",
79
+ firstOf: (limit, total) => `De eerste ${limit} van ${total}.`,
80
+ noPlural: (plural) => `Geen ${plural}.`,
81
+ update: "Bijwerken",
82
+ addedRemoved: (added, removed) => `${added} erbij, ${removed} eraf`,
83
+ searchSaved: "Zoekopdracht bewaard",
84
+ openPlural: (plural) => `${plural} openen`,
85
+ couldNotSave: "Kon de zoekopdracht niet bewaren",
86
+ couldNotTakeOff: "Kon de zoekopdracht niet weghalen",
87
+ searchUpdated: "Zoekopdracht bijgewerkt",
88
+ couldNotUpdate: "Kon de zoekopdracht niet bijwerken",
89
+ noneMatch: (plural) => `Geen ${plural} gevonden.`,
90
+ noNew: (plural) => `Geen nieuwe ${plural} sinds je laatst keek.`,
91
+ viewAll: "Alle resultaten bekijken",
92
+ filters: "Filters"
93
+ }
94
+ });
95
+ //#endregion
96
+ export { text };
@@ -1,4 +1,5 @@
1
1
  import { Button } from "../../primitives/button.js";
2
+ import { text } from "./messages.js";
2
3
  import { SignInPrompt } from "../../auth/sign-in-prompt.js";
3
4
  import { SearchNameDialog } from "./search-name-dialog.js";
4
5
  import { UpdateSearchDialog } from "./update-search-dialog.js";
@@ -19,7 +20,7 @@ const SaveSearchButton = ({ model, view, search, filter, chips, className }) =>
19
20
  const [confirming, setConfirming] = useState(false);
20
21
  const [naming, setNaming] = useState();
21
22
  if (!state.mode) return null;
22
- const label = search.label ?? "Save search";
23
+ const label = search.label ?? text("saveSearch");
23
24
  if (state.mode === "sign-in") return /* @__PURE__ */ jsxs(Fragment$1, { children: [/* @__PURE__ */ jsxs(Button, {
24
25
  className,
25
26
  "aria-haspopup": "dialog",
@@ -50,7 +51,7 @@ const SaveSearchButton = ({ model, view, search, filter, chips, className }) =>
50
51
  });
51
52
  const opened = state.opened;
52
53
  if (opened) {
53
- const saveNewLabel = `Save as new ${state.noun}`;
54
+ const saveNewLabel = text("saveAsNew", state.noun);
54
55
  return /* @__PURE__ */ jsxs(Fragment$1, { children: [
55
56
  /* @__PURE__ */ jsxs(Button, {
56
57
  className,
@@ -62,7 +63,7 @@ const SaveSearchButton = ({ model, view, search, filter, chips, className }) =>
62
63
  "aria-hidden": true
63
64
  }), /* @__PURE__ */ jsx("span", {
64
65
  className: "max-w-60 truncate",
65
- children: opened.name ? `Update ${opened.name}` : `Update ${state.noun}`
66
+ children: text("updateName", opened.name ?? state.noun)
66
67
  })]
67
68
  }),
68
69
  /* @__PURE__ */ jsxs(Button, {
@@ -78,7 +79,7 @@ const SaveSearchButton = ({ model, view, search, filter, chips, className }) =>
78
79
  confirming && /* @__PURE__ */ jsx(UpdateSearchDialog, {
79
80
  model,
80
81
  view,
81
- title: opened.name ? `Update ${opened.name}?` : `Update this ${state.noun}?`,
82
+ title: opened.name ? text("updateNameQuestion", opened.name) : text("updateNounQuestion", state.noun),
82
83
  before: opened.filter,
83
84
  after: filter,
84
85
  saveNewLabel,
@@ -4,6 +4,7 @@ import { recordKey } from "../../live/model-helpers.js";
4
4
  import { useAdminMeta } from "../meta-gate.js";
5
5
  import { AppLink } from "../use-app-address.js";
6
6
  import { ErrorBanner } from "../error-banner.js";
7
+ import { text } from "./messages.js";
7
8
  import { InlineFilterChips } from "./filter-chip-list.js";
8
9
  import { RecordRowBody } from "../record-row/record-row-body.js";
9
10
  import { useSavedSearches } from "./use-saved-searches.js";
@@ -50,13 +51,13 @@ const SavedSearchRows = ({ model, view, empty }) => {
50
51
  const plural = (view.names?.plural ?? humanize(model.name)).toLowerCase();
51
52
  if (!target) return null;
52
53
  if (saved.error) return /* @__PURE__ */ jsx(ErrorBanner, {
53
- title: `Could not load your ${plural}`,
54
+ title: text("couldNotLoadYour", plural),
54
55
  error: saved.error
55
56
  });
56
57
  if (!saved.data) return /* @__PURE__ */ jsx(Skeleton, { className: "h-40" });
57
58
  if (saved.data.length === 0) return /* @__PURE__ */ jsx("p", {
58
59
  className: "text-muted-foreground",
59
- children: empty ?? `No ${plural} yet.`
60
+ children: empty ?? text("noneYet", plural)
60
61
  });
61
62
  return /* @__PURE__ */ jsx("div", {
62
63
  className: "flex flex-col divide-y",
@@ -4,6 +4,7 @@ import { humanize } from "../../live/naming.js";
4
4
  import { useToast } from "../../toasts/toast-provider.js";
5
5
  import { AppLink } from "../use-app-address.js";
6
6
  import { useRefreshResource } from "../pages/use-layout-data.js";
7
+ import { text } from "./messages.js";
7
8
  import { InlineFilterChips } from "./filter-chip-list.js";
8
9
  import { RecordRowBody } from "../record-row/record-row-body.js";
9
10
  import { useSearchRow } from "./use-search-row.js";
@@ -14,7 +15,7 @@ import { Pencil, Trash2 } from "lucide-react";
14
15
  import { jsx, jsxs } from "react/jsx-runtime";
15
16
  import { useMutation } from "@tanstack/react-query";
16
17
  //#region src/app/searches/saved-search-section.tsx
17
- const message = (error) => error instanceof Error ? error.message : "Something went wrong";
18
+ const message = (error) => error instanceof Error ? error.message : text("somethingWrong");
18
19
  /**
19
20
  * One saved search on its list page: its name when it has one, and what it filters on as chips, which are its title
20
21
  * when it has none; its controls, rename and delete; and the records it finds in a row of their cards that scrolls
@@ -38,7 +39,7 @@ const SavedSearchSection = ({ model, view, of, target, targetView, row, recordKe
38
39
  onSuccess: () => setRenaming(false),
39
40
  onError: (error) => toast.show({
40
41
  state: "error",
41
- title: "Could not rename the search",
42
+ title: text("couldNotRename"),
42
43
  description: message(error)
43
44
  }),
44
45
  onSettled: () => refresh(model.name)
@@ -77,7 +78,7 @@ const SavedSearchSection = ({ model, view, of, target, targetView, row, recordKe
77
78
  }),
78
79
  canRename && nameField && /* @__PURE__ */ jsx(Button, {
79
80
  variant: "ghost",
80
- "aria-label": `Rename ${title}`,
81
+ "aria-label": text("renameTitle", title),
81
82
  "aria-haspopup": "dialog",
82
83
  onClick: () => setRenaming(true),
83
84
  children: /* @__PURE__ */ jsx(Pencil, {
@@ -87,7 +88,7 @@ const SavedSearchSection = ({ model, view, of, target, targetView, row, recordKe
87
88
  }),
88
89
  onDelete && /* @__PURE__ */ jsx(Button, {
89
90
  variant: "ghost",
90
- "aria-label": `Delete ${title}`,
91
+ "aria-label": text("deleteTitle", title),
91
92
  onClick: () => onDelete(title),
92
93
  children: /* @__PURE__ */ jsx(Trash2, {
93
94
  className: "size-3.5",
@@ -98,7 +99,7 @@ const SavedSearchSection = ({ model, view, of, target, targetView, row, recordKe
98
99
  })]
99
100
  }),
100
101
  renaming && nameField && /* @__PURE__ */ jsx(SearchNameDialog, {
101
- title: `Rename ${title}`,
102
+ title: text("renameTitle", title),
102
103
  chips,
103
104
  label: view.fields[nameField.name]?.label ?? humanize(nameField.name),
104
105
  required: !nameField.nullable,
@@ -1,4 +1,5 @@
1
1
  import { humanize } from "../../live/naming.js";
2
+ import { text } from "./messages.js";
2
3
  import { activeChips } from "../../filter-panel/filter-state.js";
3
4
  import { textToState } from "../../live/filter-string.js";
4
5
  import { rangeFormat, widgetFields } from "../../live/view-fields.js";
@@ -55,12 +56,12 @@ const namedConfig = (model, view, names) => {
55
56
  * A saved filter as the chips the list page shows for it, then a chip per condition no widget holds: the search text
56
57
  * in quotes, or the condition as written. One chip saying so when the filter cannot be read.
57
58
  */
58
- const filterChips = (model, view, text, names = {}) => {
59
+ const filterChips = (model, view, text$1, names = {}) => {
59
60
  const config = namedConfig(model, view, names);
60
- const parsed = textToState(config, text);
61
+ const parsed = textToState(config, text$1);
61
62
  if (!parsed.ok) return [{
62
63
  key: "unreadable",
63
- label: "A filter that cannot be read"
64
+ label: text("unreadableFilter")
64
65
  }];
65
66
  const extras = parsed.extras.map((clause, index) => ({
66
67
  key: `extra:${index}`,
@@ -1,11 +1,13 @@
1
1
  import { Skeleton } from "../../primitives/skeleton.js";
2
2
  import { PageHeader } from "../../app-shell/page-header.js";
3
+ import { slotted } from "../../locale/slotted.js";
3
4
  import { humanize } from "../../live/naming.js";
4
5
  import { recordKey } from "../../live/model-helpers.js";
5
6
  import { Notice, useAdminMeta, usePermissions } from "../meta-gate.js";
6
7
  import { AppLink } from "../use-app-address.js";
7
8
  import { ErrorBanner } from "../error-banner.js";
8
9
  import { can } from "../permissions.js";
10
+ import { text } from "./messages.js";
9
11
  import { useSavedSearches } from "./use-saved-searches.js";
10
12
  import { useDeleteFlow } from "../delete/use-delete-flow.js";
11
13
  import { SavedSearchSection } from "./saved-search-section.js";
@@ -39,16 +41,12 @@ const SavedSearches = ({ model, view }) => {
39
41
  body
40
42
  ]
41
43
  });
42
- if (!target) return frame(/* @__PURE__ */ jsxs(Notice, {
43
- title: "Not found",
44
- children: [
45
- "There is no resource called ",
46
- of.resource,
47
- "."
48
- ]
44
+ if (!target) return frame(/* @__PURE__ */ jsx(Notice, {
45
+ title: text("notFound"),
46
+ children: slotted(text("noResource"), of.resource)
49
47
  }));
50
48
  if (saved.error) return frame(/* @__PURE__ */ jsx(ErrorBanner, {
51
- title: `Could not load your ${plural.toLowerCase()}`,
49
+ title: text("couldNotLoadYour", plural.toLowerCase()),
52
50
  error: saved.error
53
51
  }));
54
52
  if (!saved.data) return frame(/* @__PURE__ */ jsx("div", {
@@ -57,24 +55,16 @@ const SavedSearches = ({ model, view }) => {
57
55
  }));
58
56
  if (saved.data.length === 0) {
59
57
  const searched = (targetView?.names?.plural ?? humanize(target.name)).toLowerCase();
60
- return frame(/* @__PURE__ */ jsxs("p", {
58
+ return frame(/* @__PURE__ */ jsx("p", {
61
59
  className: "py-8 text-center text-muted-foreground",
62
- children: [
63
- "No ",
64
- plural.toLowerCase(),
65
- " yet. Filter the",
66
- " ",
67
- /* @__PURE__ */ jsx(AppLink, {
68
- to: {
69
- kind: "list",
70
- resource: target.name
71
- },
72
- className: "font-medium text-primary-text hover:underline",
73
- children: searched
74
- }),
75
- " ",
76
- "and save the search."
77
- ]
60
+ children: slotted(text("noSavedYet", plural.toLowerCase()), /* @__PURE__ */ jsx(AppLink, {
61
+ to: {
62
+ kind: "list",
63
+ resource: target.name
64
+ },
65
+ className: "font-medium text-primary-text hover:underline",
66
+ children: searched
67
+ }))
78
68
  }));
79
69
  }
80
70
  return frame(/* @__PURE__ */ jsx("div", {
@@ -3,12 +3,13 @@ import { humanize } from "../../live/naming.js";
3
3
  import { useToast } from "../../toasts/toast-provider.js";
4
4
  import { Switch } from "../../primitives/switch.js";
5
5
  import { useRefreshResource } from "../pages/use-layout-data.js";
6
+ import { text } from "./messages.js";
6
7
  import { useId } from "react";
7
8
  import { jsx, jsxs } from "react/jsx-runtime";
8
9
  import { useMutation } from "@tanstack/react-query";
9
10
  import { enumLabel } from "@protobase/schema";
10
11
  //#region src/app/searches/search-controls.tsx
11
- const message = (error) => error instanceof Error ? error.message : "Something went wrong";
12
+ const message = (error) => error instanceof Error ? error.message : text("somethingWrong");
12
13
  const Control = ({ model, view, name, value, onChange }) => {
13
14
  const id = useId();
14
15
  const field = model.fields[name];
@@ -36,7 +37,7 @@ const Control = ({ model, view, name, value, onChange }) => {
36
37
  });
37
38
  };
38
39
  const changed = (model, view, patch) => Object.entries(patch).map(([name, value]) => {
39
- return `${view.fields[name]?.label ?? humanize(name)}: ${model.fields[name]?.type === "boolean" ? value === true ? "On" : "Off" : enumLabel(String(value), view.fields[name]?.valueLabels)}`;
40
+ return `${view.fields[name]?.label ?? humanize(name)}: ${model.fields[name]?.type === "boolean" ? value === true ? text("on") : text("off") : enumLabel(String(value), view.fields[name]?.valueLabels)}`;
40
41
  }).join(", ");
41
42
  /**
42
43
  * A saved search's controls, each saved as soon as it changes, which a toast confirms; a refused change says why and shows
@@ -50,12 +51,12 @@ const SearchControls = ({ model, view, recordKey, title, row, fields }) => {
50
51
  mutationFn: (patch) => client.update(model.name, recordKey, patch, row.etag ?? "*"),
51
52
  onSuccess: (_, patch) => toast.show({
52
53
  state: "success",
53
- title: `${title} saved`,
54
+ title: text("saved", title),
54
55
  description: changed(model, view, patch)
55
56
  }),
56
57
  onError: (error) => toast.show({
57
58
  state: "error",
58
- title: "Could not change the search",
59
+ title: text("couldNotChange"),
59
60
  description: message(error)
60
61
  }),
61
62
  onSettled: () => refresh(model.name)
@@ -1,3 +1,4 @@
1
+ import { text } from "./messages.js";
1
2
  import { createWhere, parseFilter, printFilter } from "@protobase/schema";
2
3
  //#region src/app/searches/search-diff.ts
3
4
  const where$1 = createWhere();
@@ -90,8 +91,9 @@ const filterChanges = (before, after) => {
90
91
  /** The net change in the records a search finds: "5 more homes", "1 fewer home" or "As many homes". */
91
92
  const netChange = (added, removed, singular, plural) => {
92
93
  const net = added - removed;
93
- if (net === 0) return `As many ${plural}`;
94
- return `${Math.abs(net)} ${net > 0 ? "more" : "fewer"} ${Math.abs(net) === 1 ? singular : plural}`;
94
+ if (net === 0) return text("asMany", plural);
95
+ const noun = Math.abs(net) === 1 ? singular : plural;
96
+ return net > 0 ? text("more", net, noun) : text("fewer", -net, noun);
95
97
  };
96
98
  //#endregion
97
99
  export { addedFilter, filterChanges, netChange, removedFilter };
@@ -1,6 +1,7 @@
1
1
  import { Input } from "../../primitives/input.js";
2
2
  import { Button } from "../../primitives/button.js";
3
3
  import { ResponsiveDialog } from "../../primitives/responsive-dialog.js";
4
+ import { text } from "./messages.js";
4
5
  import { FilterChipList } from "./filter-chip-list.js";
5
6
  import { useId, useState } from "react";
6
7
  import { Fragment as Fragment$1, jsx, jsxs } from "react/jsx-runtime";
@@ -16,13 +17,13 @@ const SearchNameDialog = ({ title, chips, label, required, initial = "", busy, o
16
17
  onClose,
17
18
  actions: /* @__PURE__ */ jsxs(Fragment$1, { children: [/* @__PURE__ */ jsx(Button, {
18
19
  onClick: onClose,
19
- children: "Cancel"
20
+ children: text("cancel")
20
21
  }), /* @__PURE__ */ jsx(Button, {
21
22
  type: "submit",
22
23
  form: formId,
23
24
  variant: "primary",
24
25
  loading: busy,
25
- children: "Save"
26
+ children: text("save")
26
27
  })] }),
27
28
  children: /* @__PURE__ */ jsxs("form", {
28
29
  id: formId,
@@ -42,7 +43,7 @@ const SearchNameDialog = ({ title, chips, label, required, initial = "", busy, o
42
43
  }), !required && /* @__PURE__ */ jsx("span", {
43
44
  id: `${id}-hint`,
44
45
  className: "text-xs text-muted-foreground",
45
- children: "Optional"
46
+ children: text("optional")
46
47
  })]
47
48
  }), /* @__PURE__ */ jsx(Input, {
48
49
  id,