@protobase/ui 0.46.6 → 0.46.8
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/app/record-elsewhere.js +25 -0
- package/dist/app/record-messages.js +2 -0
- package/dist/app/record-page.js +23 -3
- package/dist/app/use-record-elsewhere.js +43 -0
- package/dist/auth/auth-messages.js +6 -3
- package/dist/auth/reset-password-page.js +2 -1
- package/dist/package.js +1 -1
- package/dist/shell/assets/app-BxGBuUKh.js +9 -0
- package/dist/shell/assets/{index-DyprR7Dr.js → index-oppnv6lh.js} +2 -2
- package/dist/shell/assets/{records-map-BYkiC5Eo.js → records-map-Ck8_EDiN.js} +2 -2
- package/dist/shell/assets/{shared-protobase-ui-BtcDyJbx.js → shared-protobase-ui-DWI6ok-_.js} +2 -2
- package/dist/shell/index.html +3 -3
- package/dist/shell/protobase.shell.json +1 -1
- package/dist/shell/protobase.sw.js +2 -2
- package/dist/shell-source-maps/assets/app-BxGBuUKh.js.map +1 -0
- package/dist/shell-source-maps/assets/{index-DyprR7Dr.js.map → index-oppnv6lh.js.map} +1 -1
- package/dist/shell-source-maps/assets/{records-map-BYkiC5Eo.js.map → records-map-Ck8_EDiN.js.map} +1 -1
- package/dist/shell-source-maps/assets/{shared-protobase-ui-BtcDyJbx.js.map → shared-protobase-ui-DWI6ok-_.js.map} +1 -1
- package/package.json +4 -4
- package/dist/shell/assets/app-q4G2ZH9i.js +0 -9
- package/dist/shell-source-maps/assets/app-q4G2ZH9i.js.map +0 -1
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
import { createWhere, printFilter } from "@protobase/schema";
|
|
2
|
+
//#region src/app/record-elsewhere.ts
|
|
3
|
+
const w = createWhere();
|
|
4
|
+
/**
|
|
5
|
+
* The list request that finds a record the session's organization does not have, and the organization it is in: for a
|
|
6
|
+
* resource whose lists read across the person's organizations (`tenantAcross`), with a key of one field they can
|
|
7
|
+
* filter on. `undefined` for any other resource, whose record is simply not found.
|
|
8
|
+
*/
|
|
9
|
+
const elsewhereRequest = (model, recordKey) => {
|
|
10
|
+
const [key, ...more] = model.primaryKey;
|
|
11
|
+
if (!model.tenantAcross || model.tenant === void 0 || key === void 0 || more.length > 0 || !model.fields[key]?.filterable) return void 0;
|
|
12
|
+
return {
|
|
13
|
+
filter: printFilter(w.eq(key, recordKey)),
|
|
14
|
+
pageSize: 1,
|
|
15
|
+
fields: [key, model.tenant]
|
|
16
|
+
};
|
|
17
|
+
};
|
|
18
|
+
/** The organization a found row is in, when it is another than `current`; `undefined` when it is not found or is there. */
|
|
19
|
+
const otherOrganization = (model, row, current) => {
|
|
20
|
+
const tenant = row?.[model.tenant];
|
|
21
|
+
if (tenant === void 0 || tenant === null || String(tenant) === current) return void 0;
|
|
22
|
+
return String(tenant);
|
|
23
|
+
};
|
|
24
|
+
//#endregion
|
|
25
|
+
export { elsewhereRequest, otherOrganization };
|
|
@@ -9,6 +9,7 @@ const text = defineMessages({
|
|
|
9
9
|
loading: "Loading",
|
|
10
10
|
fetching: "Fetching the record.",
|
|
11
11
|
noRecord: "This record does not exist, or you cannot see it.",
|
|
12
|
+
openingElsewhere: "Opening the record in its organization.",
|
|
12
13
|
couldNotLoad: "Could not load the record",
|
|
13
14
|
someoneElse: "Someone else",
|
|
14
15
|
justNow: "just now",
|
|
@@ -45,6 +46,7 @@ const text = defineMessages({
|
|
|
45
46
|
loading: "Laden",
|
|
46
47
|
fetching: "Het record wordt opgehaald.",
|
|
47
48
|
noRecord: "Dit record bestaat niet, of je mag het niet zien.",
|
|
49
|
+
openingElsewhere: "Het record wordt in zijn organisatie geopend.",
|
|
48
50
|
couldNotLoad: "Kon het record niet laden",
|
|
49
51
|
someoneElse: "Iemand anders",
|
|
50
52
|
justNow: "zojuist",
|
package/dist/app/record-page.js
CHANGED
|
@@ -61,6 +61,7 @@ import { HistorySection } from "./history/history-section.js";
|
|
|
61
61
|
import { CommentsSection } from "./comments/comments-section.js";
|
|
62
62
|
import { useShownLayout } from "./record-mode/use-shown-layout.js";
|
|
63
63
|
import { useRecordEditor } from "./use-record-editor.js";
|
|
64
|
+
import { useRecordElsewhere } from "./use-record-elsewhere.js";
|
|
64
65
|
import { useRelatedRecords } from "./use-related-records.js";
|
|
65
66
|
import { IncomingCard } from "./incoming-card.js";
|
|
66
67
|
import { RelatedSection } from "./related-section.js";
|
|
@@ -113,9 +114,9 @@ const RecordPage = ({ resource, recordKey }) => {
|
|
|
113
114
|
});
|
|
114
115
|
if (record.error) return /* @__PURE__ */ jsx("div", {
|
|
115
116
|
className: "p-8",
|
|
116
|
-
children: isApiError(record.error) && record.error.status === 404 ? /* @__PURE__ */ jsx(
|
|
117
|
-
|
|
118
|
-
|
|
117
|
+
children: isApiError(record.error) && record.error.status === 404 ? /* @__PURE__ */ jsx(RecordNotFound, {
|
|
118
|
+
model,
|
|
119
|
+
recordKey
|
|
119
120
|
}) : /* @__PURE__ */ jsx(ErrorBanner, {
|
|
120
121
|
title: text$1("couldNotLoad"),
|
|
121
122
|
error: record.error
|
|
@@ -128,6 +129,25 @@ const RecordPage = ({ resource, recordKey }) => {
|
|
|
128
129
|
preview: record.isPlaceholderData
|
|
129
130
|
}, record.isPlaceholderData ? "preview" : "loaded");
|
|
130
131
|
};
|
|
132
|
+
const RecordNotFound = ({ model, recordKey }) => {
|
|
133
|
+
const elsewhere = useRecordElsewhere(model, recordKey);
|
|
134
|
+
if (elsewhere.state === "looking") return /* @__PURE__ */ jsx(Notice, {
|
|
135
|
+
title: text$1("loading"),
|
|
136
|
+
children: text$1("fetching")
|
|
137
|
+
});
|
|
138
|
+
if (elsewhere.state === "switching") return /* @__PURE__ */ jsx(Notice, {
|
|
139
|
+
title: text$1("loading"),
|
|
140
|
+
children: text$1("openingElsewhere")
|
|
141
|
+
});
|
|
142
|
+
if (elsewhere.state === "failed") return /* @__PURE__ */ jsx(ErrorBanner, {
|
|
143
|
+
title: text$1("couldNotLoad"),
|
|
144
|
+
error: elsewhere.error
|
|
145
|
+
});
|
|
146
|
+
return /* @__PURE__ */ jsx(Notice, {
|
|
147
|
+
title: text("notFound"),
|
|
148
|
+
children: text$1("noRecord")
|
|
149
|
+
});
|
|
150
|
+
};
|
|
131
151
|
const LoadedRecord = ({ model, recordKey, stored, preview }) => {
|
|
132
152
|
const meta = useAdminMeta();
|
|
133
153
|
const { address, go } = useAppAddress();
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
import { useAuth } from "../auth/auth-provider.js";
|
|
2
|
+
import { useClient } from "../data/api-provider.js";
|
|
3
|
+
import { elsewhereRequest, otherOrganization } from "./record-elsewhere.js";
|
|
4
|
+
import { useEffect, useMemo, useState } from "react";
|
|
5
|
+
//#region src/app/use-record-elsewhere.ts
|
|
6
|
+
/**
|
|
7
|
+
* For a record not found in the session's organization: looks for it across the person's organizations, and when it is
|
|
8
|
+
* in another one, switches the session there and opens the page again, so the record and everything on its page are
|
|
9
|
+
* read in its own organization. `missing` when it is nowhere else, or the resource does not list across organizations.
|
|
10
|
+
*/
|
|
11
|
+
const useRecordElsewhere = (model, recordKey) => {
|
|
12
|
+
const client = useClient();
|
|
13
|
+
const { session, organizations } = useAuth();
|
|
14
|
+
const request = useMemo(() => organizations ? elsewhereRequest(model, recordKey) : void 0, [
|
|
15
|
+
organizations,
|
|
16
|
+
model,
|
|
17
|
+
recordKey
|
|
18
|
+
]);
|
|
19
|
+
const [found, setFound] = useState(request ? { state: "looking" } : { state: "missing" });
|
|
20
|
+
useEffect(() => {
|
|
21
|
+
if (!request) return;
|
|
22
|
+
const look = async () => {
|
|
23
|
+
const [page, current] = await Promise.all([client.list(model.name, request), session.organizations.current()]);
|
|
24
|
+
const other = otherOrganization(model, page.items[0], current.organization?.id);
|
|
25
|
+
if (other === void 0) return setFound({ state: "missing" });
|
|
26
|
+
setFound({ state: "switching" });
|
|
27
|
+
await session.organizations.switchTo(other);
|
|
28
|
+
window.location.reload();
|
|
29
|
+
};
|
|
30
|
+
look().catch((error) => setFound({
|
|
31
|
+
state: "failed",
|
|
32
|
+
error
|
|
33
|
+
}));
|
|
34
|
+
}, [
|
|
35
|
+
client,
|
|
36
|
+
session,
|
|
37
|
+
model,
|
|
38
|
+
request
|
|
39
|
+
]);
|
|
40
|
+
return found;
|
|
41
|
+
};
|
|
42
|
+
//#endregion
|
|
43
|
+
export { useRecordElsewhere };
|
|
@@ -26,7 +26,8 @@ const text = defineMessages({
|
|
|
26
26
|
staffReasonRequired: "Say why you sign in as this person, in at least 10 characters.",
|
|
27
27
|
staffForbidden: "Staff sign-in is turned off for this app.",
|
|
28
28
|
untrustedCallback: "This page is not on an address the server trusts.",
|
|
29
|
-
staffFailed: "The staff sign-in did not go through. Try again."
|
|
29
|
+
staffFailed: "The staff sign-in did not go through. Try again.",
|
|
30
|
+
passwordLeaked: "This password appears in a known data breach. Choose a different one."
|
|
30
31
|
},
|
|
31
32
|
nl: {
|
|
32
33
|
tooManyRequests: "Te veel pogingen. Wacht een minuut en probeer het opnieuw.",
|
|
@@ -53,7 +54,8 @@ const text = defineMessages({
|
|
|
53
54
|
staffReasonRequired: "Zeg in minstens 10 tekens waarom je als deze persoon inlogt.",
|
|
54
55
|
staffForbidden: "Inloggen als medewerker staat uit voor deze app.",
|
|
55
56
|
untrustedCallback: "Deze pagina staat niet op een adres dat de server vertrouwt.",
|
|
56
|
-
staffFailed: "De inlog als medewerker lukte niet. Probeer het opnieuw."
|
|
57
|
+
staffFailed: "De inlog als medewerker lukte niet. Probeer het opnieuw.",
|
|
58
|
+
passwordLeaked: "Dit wachtwoord komt voor in een bekend datalek. Kies een ander wachtwoord."
|
|
57
59
|
}
|
|
58
60
|
});
|
|
59
61
|
const providerText = defineMessages({
|
|
@@ -94,7 +96,8 @@ const messages = {
|
|
|
94
96
|
AUTHENTICATION_FAILED: "passkeyFailed",
|
|
95
97
|
PREVIOUSLY_REGISTERED: "passkeyAdded",
|
|
96
98
|
ERROR_AUTHENTICATOR_PREVIOUSLY_REGISTERED: "passkeyAdded",
|
|
97
|
-
SESSION_NOT_FRESH: "notFresh"
|
|
99
|
+
SESSION_NOT_FRESH: "notFresh",
|
|
100
|
+
PASSWORD_COMPROMISED: "passwordLeaked"
|
|
98
101
|
};
|
|
99
102
|
const lookup = (ids, code) => {
|
|
100
103
|
const id = code ? ids[code] : void 0;
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { text } from "./messages.js";
|
|
2
2
|
import { useAuth } from "./auth-provider.js";
|
|
3
|
+
import { authMessage } from "./auth-messages.js";
|
|
3
4
|
import { ResetPasswordForm } from "./reset-password-form.js";
|
|
4
5
|
import { useState } from "react";
|
|
5
6
|
import { AuthError } from "@protobase/client";
|
|
@@ -10,7 +11,7 @@ const resetPasswordMessage = (error) => {
|
|
|
10
11
|
if (error.status === 429) return text("tooManyAttempts");
|
|
11
12
|
if (error.code === "PASSWORD_TOO_SHORT") return text("useAtLeast", 12);
|
|
12
13
|
if (error.code === "PASSWORD_TOO_LONG") return text("passwordTooLong");
|
|
13
|
-
return error
|
|
14
|
+
return authMessage(error);
|
|
14
15
|
};
|
|
15
16
|
/** Sets a new password with the token of an emailed reset link. */
|
|
16
17
|
const ResetPasswordPage = ({ token, onDone, onRequestNewLink, onBack }) => {
|
package/dist/package.js
CHANGED