@protobase/ui 0.46.9 → 0.46.11

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 (30) hide show
  1. package/dist/app/create/create-page.js +2 -2
  2. package/dist/app/create/create-values.js +17 -2
  3. package/dist/app/nav-from-meta.js +7 -3
  4. package/dist/app-shell/sidebar.js +1 -1
  5. package/dist/auth/auth-messages.js +4 -1
  6. package/dist/auth/user-to-shell.js +4 -1
  7. package/dist/format/messages.js +2 -2
  8. package/dist/format/number.js +1 -1
  9. package/dist/forms/public-form-page.js +2 -2
  10. package/dist/organizations/messages.js +2 -0
  11. package/dist/organizations/organization-paths.js +3 -2
  12. package/dist/package.js +1 -1
  13. package/dist/shell/assets/app-KkSCyh3l.js +9 -0
  14. package/dist/shell/assets/export-worker-IJWS5-Wc.js +3 -0
  15. package/dist/shell/assets/{index-1Ue_dZiA.js → index-qw49QDJt.js} +2 -2
  16. package/dist/shell/assets/{records-map-DPA8Y3C8.js → records-map-1-IIJOiJ.js} +2 -2
  17. package/dist/shell/assets/{shared-protobase-ui-dWIf89ff.js → shared-protobase-ui-Da6Lujxh.js} +2 -2
  18. package/dist/shell/index.html +3 -3
  19. package/dist/shell/protobase.shell.json +1 -1
  20. package/dist/shell/protobase.sw.js +2 -2
  21. package/dist/shell-source-maps/assets/app-KkSCyh3l.js.map +1 -0
  22. package/dist/shell-source-maps/assets/export-worker-IJWS5-Wc.js.map +1 -0
  23. package/dist/shell-source-maps/assets/{index-1Ue_dZiA.js.map → index-qw49QDJt.js.map} +1 -1
  24. package/dist/shell-source-maps/assets/{records-map-DPA8Y3C8.js.map → records-map-1-IIJOiJ.js.map} +1 -1
  25. package/dist/shell-source-maps/assets/{shared-protobase-ui-dWIf89ff.js.map → shared-protobase-ui-Da6Lujxh.js.map} +1 -1
  26. package/package.json +4 -4
  27. package/dist/shell/assets/app-axeIZ-10.js +0 -9
  28. package/dist/shell/assets/export-worker-ClACk00b.js +0 -3
  29. package/dist/shell-source-maps/assets/app-axeIZ-10.js.map +0 -1
  30. package/dist/shell-source-maps/assets/export-worker-ClACk00b.js.map +0 -1
@@ -6,7 +6,7 @@ import { text } from "../messages.js";
6
6
  import { useNavigationGuard } from "../router.js";
7
7
  import { useAppAddress } from "../use-app-address.js";
8
8
  import { text as text$1 } from "./messages.js";
9
- import { createBody, createSections, hasDatabaseDefault, initialDraft, isChosen, isRequired } from "./create-values.js";
9
+ import { createBody, createSections, hasDatabaseDefault, initialDraft, isChosen, isRequired, numberDefault } from "./create-values.js";
10
10
  import { FieldEditor } from "../field-editor.js";
11
11
  import { can } from "../permissions.js";
12
12
  import { fetchLabels } from "../../live/relation-labels.js";
@@ -133,7 +133,7 @@ const LoadedCreate = ({ resource, values }) => {
133
133
  value: isChosen(value) ? value.key : value ?? (field.type === "boolean" ? false : ""),
134
134
  stored: void 0,
135
135
  relationLabel: isChosen(value) ? value.label : void 0,
136
- placeholder: hasDatabaseDefault(field) ? text$1("setByDatabase") : void 0,
136
+ placeholder: hasDatabaseDefault(field) ? text$1("setByDatabase") : numberDefault(field),
137
137
  relationPicker: target ? {
138
138
  target,
139
139
  view: meta.views[target.name]
@@ -59,17 +59,32 @@ const givenValue = (field, value) => {
59
59
  if (field.type === "boolean") return value === "true";
60
60
  return value;
61
61
  };
62
+ const numeric = /* @__PURE__ */ new Set([
63
+ "integer",
64
+ "bigint",
65
+ "decimal"
66
+ ]);
67
+ /**
68
+ * A number field's literal default, which its empty input shows as a placeholder rather than as its value: typing into a
69
+ * prefilled `0` would add to it, and the create applies the default to an empty field.
70
+ */
71
+ const numberDefault = (field) => {
72
+ if (!numeric.has(field.type) || !field.default || !("value" in field.default)) return void 0;
73
+ const value = field.default.value;
74
+ return value === null || value === void 0 ? void 0 : String(value);
75
+ };
62
76
  /**
63
77
  * The form's starting values: the values the address gives (`/notes/new?listing_id=42`), for the fields the form has,
64
- * else literal defaults from `/meta`. Database defaults stay empty so the database fills them.
78
+ * else literal defaults from `/meta`, but for numbers. Database defaults stay empty so the database fills them.
65
79
  */
66
80
  const initialDraft = (model, sections, given = {}) => Object.fromEntries(sections.flatMap((section) => section.fields.flatMap((name) => {
67
81
  const field = model.fields[name];
68
82
  if (Object.hasOwn(given, name)) return [[name, givenValue(field, given[name])]];
69
83
  const fallback = field.default;
84
+ if (numberDefault(field) !== void 0) return [];
70
85
  if (!fallback || !("value" in fallback) || fallback.value === null || fallback.value === void 0) return [];
71
86
  return [[name, typeof fallback.value === "boolean" ? fallback.value : String(fallback.value)]];
72
87
  })));
73
88
  const hasDatabaseDefault = (field) => Boolean(field.default && "db" in field.default);
74
89
  //#endregion
75
- export { createBody, createSections, hasDatabaseDefault, initialDraft, isChosen, isCreatable, isRequired, keyValue };
90
+ export { createBody, createSections, hasDatabaseDefault, initialDraft, isChosen, isCreatable, isRequired, keyValue, numberDefault };
@@ -16,8 +16,13 @@ const groupLabels = {
16
16
  core: "admin",
17
17
  hr: "hr"
18
18
  };
19
+ /** The group of a schema's resources: none for the default `public` schema, whose name would read as "visible to anyone". */
20
+ const schemaGroup = (schema = "public") => {
21
+ if (schema === "public") return "";
22
+ return groupLabels[schema] ? text(groupLabels[schema]) : humanize(schema);
23
+ };
19
24
  /**
20
- * One sidebar group per `nav.group` (default: the database schema, and "Pages" for pages); `nav.hidden` and a place in
25
+ * One sidebar group per `nav.group` (default: the database schema, none for `public`, and "Pages" for pages); `nav.hidden` and a place in
21
26
  * the user menu leave an entry out, `nav.order` sorts within a group, and `recent` (from `useNavRecent`) makes an entry
22
27
  * collapsible. Groups with pages come first, so a dashboard leads the sidebar. The resources of a source that cannot be
23
28
  * read are listed under its label, each with the problem. A visitor also gets the resources whose views ask them to sign
@@ -50,8 +55,7 @@ const navFromMeta = (meta, basePath = "", recent = {}) => {
50
55
  for (const model of Object.values(meta.resources)) {
51
56
  const view = meta.views[model.name];
52
57
  if (!view || inMenu.has(model.name)) continue;
53
- const schema = model.table.schema ?? "public";
54
- const defaultGroup = view.nav?.group ?? (groupLabels[schema] ? text(groupLabels[schema]) : humanize(schema));
58
+ const defaultGroup = view.nav?.group ?? schemaGroup(model.table.schema);
55
59
  const add = (groupName, item, order) => {
56
60
  const group = groups.get(groupName) ?? {
57
61
  label: groupName,
@@ -34,7 +34,7 @@ const Sidebar = ({ mode, activeItem, user, workspace, appName, appIcon, forceExp
34
34
  className: cn("flex-1 overflow-y-auto overflow-x-hidden pb-2", mode === "icon-label" ? "px-2" : "px-2"),
35
35
  children: groups.map((group, index) => /* @__PURE__ */ jsxs("div", {
36
36
  className: cn("flex flex-col gap-0.5", index > 0 && "mt-3"),
37
- children: [inline ? /* @__PURE__ */ jsx("div", {
37
+ children: [inline ? group.label && /* @__PURE__ */ jsx("div", {
38
38
  className: cn("px-3 pb-1 pt-1 text-xs font-medium uppercase tracking-wide text-faint-foreground", expanding && "h-4 whitespace-nowrap py-0 opacity-0 transition-opacity group-hover/side:opacity-100 group-focus-within/side:opacity-100", expanded && "opacity-100"),
39
39
  children: group.label
40
40
  }) : index > 0 && /* @__PURE__ */ jsx("div", { className: "mx-auto mb-1 h-px w-6 bg-border" }), group.items.map((item) => item.recent && isTextMode(mode) ? /* @__PURE__ */ jsx(SidebarGroup, {
@@ -7,6 +7,7 @@ const text = defineMessages({
7
7
  wrongEmailOrPassword: "The email or password is not right.",
8
8
  wrongPassword: "The password is not right.",
9
9
  wrongCode: "The code is not right.",
10
+ wrongOrExpiredCode: "The code is not right, or has expired. Ask for a new one.",
10
11
  codeExpired: "The code has expired. Ask for a new one.",
11
12
  tooManyCodes: "Too many wrong codes. Ask for a new one.",
12
13
  wrongBackupCode: "The backup code is not right, or was used before.",
@@ -35,6 +36,7 @@ const text = defineMessages({
35
36
  wrongEmailOrPassword: "Het e-mailadres of wachtwoord klopt niet.",
36
37
  wrongPassword: "Het wachtwoord klopt niet.",
37
38
  wrongCode: "De code klopt niet.",
39
+ wrongOrExpiredCode: "De code klopt niet, of is verlopen. Vraag een nieuwe aan.",
38
40
  codeExpired: "De code is verlopen. Vraag een nieuwe aan.",
39
41
  tooManyCodes: "Te veel verkeerde codes. Vraag een nieuwe aan.",
40
42
  wrongBackupCode: "De back-upcode klopt niet, of is al eerder gebruikt.",
@@ -81,8 +83,9 @@ const providerText = defineMessages({
81
83
  const messages = {
82
84
  INVALID_EMAIL_OR_PASSWORD: "wrongEmailOrPassword",
83
85
  INVALID_PASSWORD: "wrongPassword",
84
- INVALID_OTP: "wrongCode",
86
+ INVALID_OTP: "wrongOrExpiredCode",
85
87
  OTP_EXPIRED: "codeExpired",
88
+ OTP_HAS_EXPIRED: "codeExpired",
86
89
  TOO_MANY_ATTEMPTS: "tooManyCodes",
87
90
  INVALID_CODE: "wrongCode",
88
91
  INVALID_BACKUP_CODE: "wrongBackupCode",
@@ -1,4 +1,5 @@
1
1
  import { text } from "./messages.js";
2
+ import { capitalise } from "../live/naming.js";
2
3
  //#region src/auth/user-to-shell.ts
3
4
  /** The hue of someone's avatar, the same for the same id. */
4
5
  const hueOf = (text) => [...text].reduce((sum, char) => (sum * 31 + char.charCodeAt(0)) % 360, 7);
@@ -7,6 +8,8 @@ const initialsOf = (name) => name.split(/[\s@.]+/).filter(Boolean).slice(0, 2).m
7
8
  const rolesOf = (user) => (user.role ?? "user").split(",").map((role) => role.trim());
8
9
  /** The user holds the `admin` role, among others perhaps. */
9
10
  const isAdmin = (user) => rolesOf(user).includes("admin");
11
+ /** A role's name as words: `full_access` becomes "Full access". */
12
+ const roleLabel = (role) => capitalise(role.replace(/[_-]+/g, " ").trim());
10
13
  /** What the sidebar's profile menu shows for a signed-in user. */
11
14
  const userToShell = (user) => {
12
15
  const roles = rolesOf(user);
@@ -16,7 +19,7 @@ const userToShell = (user) => {
16
19
  email: user.email,
17
20
  initials: initialsOf(user.name || user.email) || "?",
18
21
  hue: hueOf(user.id || user.email),
19
- role: admin ? text("admin") : (roles[0] ?? "user").replace(/^./, (char) => char.toUpperCase()),
22
+ role: admin ? text("admin") : roleLabel(roles[0] ?? "user"),
20
23
  roleTone: admin ? "violet" : "blue"
21
24
  };
22
25
  };
@@ -2,7 +2,7 @@ import { defineMessages } from "../locale/messages.js";
2
2
  //#region src/format/messages.ts
3
3
  const text = defineMessages({
4
4
  en: {
5
- rows: (count) => `${count} rows`,
5
+ rows: (count, one) => `${count} ${one ? "row" : "rows"}`,
6
6
  aboutRows: (count) => `about ${count} rows`,
7
7
  secondsAgo: (count) => `${count} s ago`,
8
8
  minutesAgo: (count) => `${count} min ago`,
@@ -11,7 +11,7 @@ const text = defineMessages({
11
11
  justNow: "just now"
12
12
  },
13
13
  nl: {
14
- rows: (count) => `${count} rijen`,
14
+ rows: (count, one) => `${count} ${one ? "rij" : "rijen"}`,
15
15
  aboutRows: (count) => `ongeveer ${count} rijen`,
16
16
  secondsAgo: (count) => `${count} s geleden`,
17
17
  minutesAgo: (count) => `${count} min geleden`,
@@ -6,7 +6,7 @@ const formatInt = (value) => grouped().format(value);
6
6
  const formatSigned = (value) => value > 0 ? `+${grouped().format(value)}` : grouped().format(value).replace("-", "−");
7
7
  const formatPercent = (value) => `${value}%`;
8
8
  const formatApproxRows = (count) => {
9
- if (count < 1e4) return text("rows", formatInt(count));
9
+ if (count < 1e4) return text("rows", formatInt(count), count === 1);
10
10
  const magnitude = 10 ** (Math.floor(Math.log10(count)) - 3);
11
11
  return text("aboutRows", formatInt(Math.round(count / magnitude) * magnitude));
12
12
  };
@@ -6,7 +6,7 @@ import { Notice } from "../app/meta-gate.js";
6
6
  import { Button } from "../primitives/button.js";
7
7
  import { Field } from "../record-view/field.js";
8
8
  import { humanize } from "../live/naming.js";
9
- import { createBody, hasDatabaseDefault, initialDraft, isRequired } from "../app/create/create-values.js";
9
+ import { createBody, hasDatabaseDefault, initialDraft, isRequired, numberDefault } from "../app/create/create-values.js";
10
10
  import { ErrorBanner } from "../app/error-banner.js";
11
11
  import { FieldEditor } from "../app/field-editor.js";
12
12
  import { fieldErrors, fieldMessage } from "../app/field-errors.js";
@@ -123,7 +123,7 @@ const LoadedForm = ({ form }) => {
123
123
  hints,
124
124
  value: draft[name] ?? (field.type === "boolean" ? false : ""),
125
125
  stored: void 0,
126
- placeholder: hasDatabaseDefault(field) ? text$1("setByDatabase") : void 0,
126
+ placeholder: hasDatabaseDefault(field) ? text$1("setByDatabase") : numberDefault(field),
127
127
  invalid: Boolean(problem),
128
128
  onChange: (next) => change(name, next)
129
129
  })
@@ -3,6 +3,7 @@ import { defineMessages } from "../locale/messages.js";
3
3
  const text = defineMessages({
4
4
  en: {
5
5
  organizations: "Organizations",
6
+ manageOrganizations: "Manage organizations",
6
7
  organization: "Organization",
7
8
  members: "Members",
8
9
  invitations: "Invitations",
@@ -91,6 +92,7 @@ const text = defineMessages({
91
92
  },
92
93
  nl: {
93
94
  organizations: "Organisaties",
95
+ manageOrganizations: "Organisaties beheren",
94
96
  organization: "Organisatie",
95
97
  members: "Leden",
96
98
  invitations: "Uitnodigingen",
@@ -24,7 +24,8 @@ const roleLabel = (roles, name) => roles.find((role) => role.name === name)?.lab
24
24
  const managesOrganization = (current) => current?.role === "owner" || current?.role === "admin";
25
25
  /**
26
26
  * The organization pages in the profile menu: every organization of the person, and for the one they work in its
27
- * details, and for its owners and admins its members and invitations.
27
+ * details, and for its owners and admins its members and invitations. The first is "Manage organizations", as the
28
+ * switcher above it already lists the organizations.
28
29
  */
29
30
  const organizationMenuItems = (basePath, path, current) => {
30
31
  const item = (id, label, icon, target) => ({
@@ -35,7 +36,7 @@ const organizationMenuItems = (basePath, path, current) => {
35
36
  active: path === target
36
37
  });
37
38
  return [
38
- item("organizations", text("organizations"), Network, organizationsPath),
39
+ item("organizations", text("manageOrganizations"), Network, organizationsPath),
39
40
  ...current?.organization ? [item("organization", text("organization"), Building2, organizationPath)] : [],
40
41
  ...managesOrganization(current) ? [item("members", text("members"), Users, membersPath), item("invitations", text("invitations"), Mail, invitationsPath)] : []
41
42
  ];
package/dist/package.js CHANGED
@@ -1,5 +1,5 @@
1
1
  //#region package.json
2
- var version = "0.46.9";
2
+ var version = "0.46.11";
3
3
  var protobase = { "pluginApi": 1 };
4
4
  //#endregion
5
5
  export { protobase, version };