@plumile/backoffice-react 0.1.199 → 0.1.200
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/README.md +5 -0
- package/lib/esm/auth/backofficeAuthHeaders.js +18 -0
- package/lib/esm/auth/backofficeAuthHeaders.js.map +1 -0
- package/lib/esm/index.js +76 -75
- package/lib/esm/pages/BackofficeAcceptInvitationPage.js +7 -7
- package/lib/esm/pages/BackofficeLayoutPage.js +41 -41
- package/lib/esm/pages/BackofficeLayoutPage.js.map +1 -1
- package/lib/esm/pages/BackofficeLoginPage.js +11 -11
- package/lib/esm/pages/BackofficePasswordResetCompletePage.js +14 -14
- package/lib/esm/pages/BackofficeVerifyEmailPage.js +14 -14
- package/lib/esm/provider/BackofficeProvider.js +78 -77
- package/lib/esm/provider/BackofficeProvider.js.map +1 -1
- package/lib/esm/provider/useBackofficeAuthRouteGate.js +39 -0
- package/lib/esm/provider/useBackofficeAuthRouteGate.js.map +1 -0
- package/lib/esm/router/backofficeAuthPaths.js +11 -2
- package/lib/esm/router/backofficeAuthPaths.js.map +1 -1
- package/lib/types/auth/backofficeAuthHeaders.d.ts +5 -0
- package/lib/types/auth/backofficeAuthHeaders.d.ts.map +1 -0
- package/lib/types/index.d.ts +1 -0
- package/lib/types/index.d.ts.map +1 -1
- package/lib/types/provider/BackofficeProvider.d.ts.map +1 -1
- package/lib/types/provider/useBackofficeAuthRouteGate.d.ts +2 -0
- package/lib/types/provider/useBackofficeAuthRouteGate.d.ts.map +1 -0
- package/lib/types/router/backofficeAuthPaths.d.ts +1 -0
- package/lib/types/router/backofficeAuthPaths.d.ts.map +1 -1
- package/package.json +13 -13
package/README.md
CHANGED
|
@@ -37,6 +37,11 @@ It does not try to become:
|
|
|
37
37
|
npm install @plumile/backoffice-react
|
|
38
38
|
```
|
|
39
39
|
|
|
40
|
+
Use `createBackofficeAuthHeaders({ basePath })` as the Relay
|
|
41
|
+
`getAuthHeaders` callback. It permits anonymous GraphQL requests only from the
|
|
42
|
+
backoffice authentication routes and rejects protected requests before the
|
|
43
|
+
network when the short-lived session has expired.
|
|
44
|
+
|
|
40
45
|
Peer dependencies:
|
|
41
46
|
|
|
42
47
|
```bash
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
import { isBackofficeAuthPath as e } from "../router/backofficeAuthPaths.js";
|
|
2
|
+
import { getShortAccessToken as t } from "@plumile/auth/shortAccessToken.js";
|
|
3
|
+
//#region src/auth/backofficeAuthHeaders.ts
|
|
4
|
+
var n = (n = {}) => {
|
|
5
|
+
let r = n.basePath ?? "/";
|
|
6
|
+
return async () => {
|
|
7
|
+
try {
|
|
8
|
+
return { Authorization: `Bearer ${await t()}` };
|
|
9
|
+
} catch (t) {
|
|
10
|
+
if (typeof window < "u" && e(r, window.location.pathname)) return {};
|
|
11
|
+
throw t;
|
|
12
|
+
}
|
|
13
|
+
};
|
|
14
|
+
};
|
|
15
|
+
//#endregion
|
|
16
|
+
export { n as createBackofficeAuthHeaders };
|
|
17
|
+
|
|
18
|
+
//# sourceMappingURL=backofficeAuthHeaders.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"backofficeAuthHeaders.js","names":[],"sources":["../../../src/auth/backofficeAuthHeaders.ts"],"sourcesContent":["import { getShortAccessToken } from '@plumile/auth/shortAccessToken.js';\n\nimport { isBackofficeAuthPath } from '../router/backofficeAuthPaths.js';\n\nexport type BackofficeAuthHeadersOptions = {\n basePath?: string;\n};\n\n/**\n * Builds the auth-header provider used by a backoffice Relay environment.\n *\n * Authentication failures are allowed to become anonymous requests only on\n * the explicit authentication routes. Protected operations reject before the\n * network request is sent, so an expired session cannot reach a private\n * GraphQL subgraph as an anonymous request.\n */\nexport const createBackofficeAuthHeaders = (\n options: BackofficeAuthHeadersOptions = {},\n): (() => Promise<Record<string, string>>) => {\n const basePath = options.basePath ?? '/';\n return async () => {\n try {\n const token = await getShortAccessToken();\n return { Authorization: `Bearer ${token}` };\n } catch (error: unknown) {\n if (\n typeof window !== 'undefined' &&\n isBackofficeAuthPath(basePath, window.location.pathname)\n ) {\n const anonymousHeaders: Record<string, string> = {};\n return anonymousHeaders;\n }\n throw error;\n }\n };\n};\n"],"mappings":";;;AAgBA,IAAa,KACX,IAAwC,CAAC,MACG;CAC5C,IAAM,IAAW,EAAQ,YAAY;CACrC,OAAO,YAAY;EACjB,IAAI;GAEF,OAAO,EAAE,eAAe,UAAU,MADd,EAAoB,IACE;EAC5C,SAAS,GAAgB;GACvB,IACE,OAAO,SAAW,OAClB,EAAqB,GAAU,OAAO,SAAS,QAAQ,GAGvD,OAAO,CAAA;GAET,MAAM;EACR;CACF;AACF"}
|
package/lib/esm/index.js
CHANGED
|
@@ -1,77 +1,78 @@
|
|
|
1
1
|
import { AuthRefreshNotice as e, useAuthRefreshStateSnapshot as t } from "./auth/AuthRefreshNotice.js";
|
|
2
2
|
import { TotpQrCode as n, buildTotpOtpAuthUri as r } from "./auth/TotpQrCode.js";
|
|
3
|
-
import {
|
|
4
|
-
import {
|
|
5
|
-
import {
|
|
6
|
-
import {
|
|
7
|
-
import {
|
|
8
|
-
import {
|
|
9
|
-
import {
|
|
10
|
-
import {
|
|
11
|
-
import {
|
|
12
|
-
import {
|
|
13
|
-
import {
|
|
14
|
-
import {
|
|
15
|
-
import {
|
|
16
|
-
import {
|
|
17
|
-
import {
|
|
18
|
-
import {
|
|
19
|
-
import {
|
|
20
|
-
import {
|
|
21
|
-
import {
|
|
22
|
-
import {
|
|
23
|
-
import {
|
|
24
|
-
import {
|
|
25
|
-
import {
|
|
26
|
-
import {
|
|
27
|
-
import {
|
|
28
|
-
import {
|
|
29
|
-
import {
|
|
30
|
-
import {
|
|
31
|
-
import {
|
|
32
|
-
import {
|
|
33
|
-
import {
|
|
34
|
-
import {
|
|
35
|
-
import {
|
|
36
|
-
import {
|
|
37
|
-
import {
|
|
38
|
-
import {
|
|
39
|
-
import {
|
|
40
|
-
import {
|
|
41
|
-
import {
|
|
42
|
-
import {
|
|
43
|
-
import {
|
|
44
|
-
import {
|
|
45
|
-
import {
|
|
46
|
-
import {
|
|
47
|
-
import {
|
|
48
|
-
import {
|
|
49
|
-
import {
|
|
50
|
-
import {
|
|
51
|
-
import {
|
|
52
|
-
import {
|
|
53
|
-
import {
|
|
54
|
-
import {
|
|
55
|
-
import {
|
|
56
|
-
import {
|
|
57
|
-
import be from "./hooks/
|
|
58
|
-
import xe from "./hooks/
|
|
59
|
-
import
|
|
60
|
-
import {
|
|
61
|
-
import {
|
|
62
|
-
import {
|
|
63
|
-
import {
|
|
64
|
-
import {
|
|
65
|
-
import {
|
|
66
|
-
import {
|
|
67
|
-
import {
|
|
68
|
-
import {
|
|
69
|
-
import {
|
|
70
|
-
import {
|
|
71
|
-
import {
|
|
72
|
-
import {
|
|
73
|
-
import {
|
|
74
|
-
import {
|
|
75
|
-
import {
|
|
76
|
-
import {
|
|
77
|
-
|
|
3
|
+
import { createBackofficeAuthHeaders as i } from "./auth/backofficeAuthHeaders.js";
|
|
4
|
+
import { LoginFlow as a } from "./auth/login/LoginFlow.js";
|
|
5
|
+
import { synchronizeAuthStatusQuery as o } from "./auth/login/synchronizeAuthStatusQuery.js";
|
|
6
|
+
import { AcceptInvitationScreen as s } from "./auth/pages/AcceptInvitationScreen.js";
|
|
7
|
+
import { PasswordResetCompleteScreen as c } from "./auth/pages/PasswordResetCompleteScreen.js";
|
|
8
|
+
import { PasswordResetRequestScreen as l } from "./auth/pages/PasswordResetRequestScreen.js";
|
|
9
|
+
import { VerifyEmailScreen as u } from "./auth/pages/VerifyEmailScreen.js";
|
|
10
|
+
import { requireField as d, requireLinkedRecordId as f, resolveAgentStartOutcome as p, resolveMutationOutcome as m } from "./relay/mutationResult.js";
|
|
11
|
+
import { useBackofficeConfig as h } from "./provider/BackofficeConfigContext.js";
|
|
12
|
+
import { EntityIdFilterField as g } from "./components/backoffice/filters/EntityIdFilterField.js";
|
|
13
|
+
import { BackofficeErrorBoundary as _ } from "./components/backoffice/errors/BackofficeErrorBoundary.js";
|
|
14
|
+
import { EntityIdPickerDialog as v } from "./components/backoffice/pickers/EntityIdPickerDialog.js";
|
|
15
|
+
import { BackofficeBillingUsageChart as y } from "./components/backoffice/billing/BackofficeBillingUsageChart.js";
|
|
16
|
+
import { resolveBackofficeTargetIcon as b } from "./components/backoffice/links/resolveBackofficeTargetIcon.js";
|
|
17
|
+
import { resolveBackofficeLink as x } from "./components/backoffice/links/resolveBackofficeLink.js";
|
|
18
|
+
import { useBackofficeLink as S } from "./components/backoffice/links/useBackofficeLink.js";
|
|
19
|
+
import { BackofficeInlineLink as C } from "./components/backoffice/links/BackofficeInlineLink.js";
|
|
20
|
+
import { buildDataTableColumns as w } from "./components/backoffice/columns/buildDataTableColumns.js";
|
|
21
|
+
import { BackofficeDetailBadgeRow as T } from "./components/backoffice/detail/BackofficeDetailBadgeRow.js";
|
|
22
|
+
import { BackofficeStatusMetaBadge as E } from "./components/backoffice/detail/BackofficeStatusMetaBadge.js";
|
|
23
|
+
import { BackofficeEntitySummaryHeader as D } from "./components/backoffice/detail/BackofficeEntitySummaryHeader.js";
|
|
24
|
+
import { BackofficeEnumLabel as O } from "./components/backoffice/detail/BackofficeEnumLabel.js";
|
|
25
|
+
import { BackofficeLifecycleTimelineSection as k } from "./components/backoffice/detail/BackofficeLifecycleTimelineSection.js";
|
|
26
|
+
import { BackofficeReferenceValue as A } from "./components/backoffice/detail/BackofficeReferenceValue.js";
|
|
27
|
+
import { BackofficeRelationsSummaryGrid as j } from "./components/backoffice/detail/BackofficeRelationsSummaryGrid.js";
|
|
28
|
+
import { BackofficeStatusGroup as M } from "./components/backoffice/detail/BackofficeStatusGroup.js";
|
|
29
|
+
import { BackofficeTokenUsageBreakdown as N } from "./components/backoffice/detail/BackofficeTokenUsageBreakdown.js";
|
|
30
|
+
import { BackofficeUsageCostBreakdown as P } from "./components/backoffice/detail/BackofficeUsageCostBreakdown.js";
|
|
31
|
+
import { createBackofficeEntityLinkProps as F } from "./components/backoffice/detail/createBackofficeEntityLinkProps.js";
|
|
32
|
+
import { formatListAsMarkdown as I } from "./components/backoffice/detail/detailPayloadUtils.js";
|
|
33
|
+
import { BackofficeFilterAction as L } from "./components/backoffice/filters/BackofficeFilterAction.js";
|
|
34
|
+
import { EntityFilterValue as R, EntityFilterValueText as z } from "./components/backoffice/filters/EntityFilterValue.js";
|
|
35
|
+
import { BackofficeHubTemplate as B } from "./components/backoffice/hub/BackofficeHubTemplate.js";
|
|
36
|
+
import { BackofficeLink as V } from "./components/backoffice/links/BackofficeLink.js";
|
|
37
|
+
import { BackofficeLinkLabel as H } from "./components/backoffice/links/BackofficeLinkLabel.js";
|
|
38
|
+
import { BackofficeRightPageLayout as U } from "./components/backoffice/layout/breadcrumb/BackofficeRightPageLayout.js";
|
|
39
|
+
import { BackofficeOverviewLayout as W } from "./components/backoffice/overview/BackofficeOverviewLayout.js";
|
|
40
|
+
import { BackofficeFilterableCell as G } from "./components/backoffice/shared/BackofficeFilterableCell.js";
|
|
41
|
+
import { BackofficeEntityLink as K } from "./components/backoffice/refs/BackofficeEntityLink.js";
|
|
42
|
+
import { BackofficeLazyEntityCount as q } from "./components/backoffice/refs/BackofficeLazyEntityCount.js";
|
|
43
|
+
import { BackofficeRelatedCountLink as J } from "./components/backoffice/refs/BackofficeRelatedCountLink.js";
|
|
44
|
+
import { BackofficeTabbedDetailShell as Y } from "./components/backoffice/scaffolds/BackofficeTabbedDetailShell.js";
|
|
45
|
+
import { BackofficeFormattedCurrency as X } from "./components/backoffice/shared/BackofficeFormattedCurrency.js";
|
|
46
|
+
import { BackofficeFormattedNumber as Z } from "./components/backoffice/shared/BackofficeFormattedNumber.js";
|
|
47
|
+
import { BackofficeInlineFilterRow as Q } from "./components/backoffice/shared/BackofficeInlineFilterRow.js";
|
|
48
|
+
import { BackofficeToolsDocPanel as $ } from "./components/backoffice/tools/BackofficeToolsDocPanel.js";
|
|
49
|
+
import { BackofficeToolsErrorFallback as ee } from "./components/backoffice/tools/BackofficeToolsErrorFallback.js";
|
|
50
|
+
import { BackofficeToolsJsonForm as te } from "./components/backoffice/tools/BackofficeToolsJsonForm.js";
|
|
51
|
+
import { BackofficeToolsQueryBoundary as ne } from "./components/backoffice/tools/BackofficeToolsQueryBoundary.js";
|
|
52
|
+
import { parseToolJson as re } from "./components/backoffice/tools/parseToolJson.js";
|
|
53
|
+
import { configureRelayEnvironment as ie, getEnvironment as ae, getNetwork as oe, getRelayOperationActivitySnapshot as se, getRelayTransportSnapshot as ce, reconnectRelayWebSocket as le, refreshRelayWebSocketAuthentication as ue, resetRelayStore as de, subscribeRelayOperationActivity as fe, subscribeRelayTransport as pe } from "./relay/environment.js";
|
|
54
|
+
import { base64UrlToBuffer as me, bufferToBase64Url as he, mapWebAuthnRegistrationError as ge, parseSignCount as _e } from "./modules/webauthn.js";
|
|
55
|
+
import { createUseAuth as ve } from "./hooks/useAuth.js";
|
|
56
|
+
import { useBackofficeListUrlState as ye } from "./hooks/useBackofficeListUrlState.js";
|
|
57
|
+
import { useConditionalSubscription as be } from "./hooks/useConditionalSubscription.js";
|
|
58
|
+
import xe from "./hooks/useCopyToClipboard.js";
|
|
59
|
+
import Se from "./hooks/useInfiniteConnection.js";
|
|
60
|
+
import { useRefetchNeededReload as Ce } from "./hooks/useRefetchNeededReload.js";
|
|
61
|
+
import { createI18nInstance as we } from "./i18n/createI18nInstance.js";
|
|
62
|
+
import { backofficeReactI18nResources as Te, withBackofficeReactI18nResources as Ee } from "./i18n/resources.js";
|
|
63
|
+
import { useReviewStatusLabel as De } from "./i18n/useReviewStatusLabel.js";
|
|
64
|
+
import { BackofficeProvider as Oe } from "./provider/BackofficeProvider.js";
|
|
65
|
+
import { createBackofficeLazyValue as ke } from "./provider/lazyValue.js";
|
|
66
|
+
import { decodeBase64ToUtf8 as Ae, encodeUtf8ToBase64 as je } from "./modules/base64.js";
|
|
67
|
+
import { formatFileSize as Me } from "./modules/formatFileSize.js";
|
|
68
|
+
import { uploadFilesSequentially as Ne } from "./modules/uploads.js";
|
|
69
|
+
import { resolveVisibleDetailPages as Pe } from "./pages/detail/pageResolution.js";
|
|
70
|
+
import { createInlineDataReader as Fe } from "./relay/createInlineReader.js";
|
|
71
|
+
import { appendNodeToConnections as Ie } from "./relay/connectionUtils.js";
|
|
72
|
+
import { RelayProvider as Le } from "./relay/RelayProvider.js";
|
|
73
|
+
import { useRelayOperationActivity as Re } from "./relay/useRelayOperationActivity.js";
|
|
74
|
+
import { configureBackofficeShortAccessTokenRelayEnvironment as ze, configureShortAccessTokenRelayEnvironment as Be } from "./relay/shortAccessTokenEnvironment.js";
|
|
75
|
+
import { useMutationAction as Ve } from "./relay/useMutationAction.js";
|
|
76
|
+
import { identityView as He } from "./relay/identityView.js";
|
|
77
|
+
import { useCursorResumableSubscription as Ue } from "./subscriptions/useCursorResumableSubscription.js";
|
|
78
|
+
export { s as AcceptInvitationScreen, e as AuthRefreshNotice, y as BackofficeBillingUsageChart, T as BackofficeDetailBadgeRow, K as BackofficeEntityLink, D as BackofficeEntitySummaryHeader, O as BackofficeEnumLabel, _ as BackofficeErrorBoundary, L as BackofficeFilterAction, G as BackofficeFilterableCell, X as BackofficeFormattedCurrency, Z as BackofficeFormattedNumber, B as BackofficeHubTemplate, Q as BackofficeInlineFilterRow, C as BackofficeInlineLink, q as BackofficeLazyEntityCount, k as BackofficeLifecycleTimelineSection, V as BackofficeLink, H as BackofficeLinkLabel, W as BackofficeOverviewLayout, Oe as BackofficeProvider, A as BackofficeReferenceValue, J as BackofficeRelatedCountLink, j as BackofficeRelationGrid, j as BackofficeRelationsSummaryGrid, U as BackofficeRightPageLayout, M as BackofficeStatusGroup, E as BackofficeStatusMetaBadge, Y as BackofficeTabbedDetailShell, N as BackofficeTokenUsageBreakdown, $ as BackofficeToolsDocPanel, ee as BackofficeToolsErrorFallback, te as BackofficeToolsJsonForm, ne as BackofficeToolsQueryBoundary, P as BackofficeUsageCostBreakdown, R as EntityFilterValue, z as EntityFilterValueText, g as EntityIdFilterField, v as EntityIdPickerDialog, a as LoginFlow, c as PasswordResetCompleteScreen, l as PasswordResetRequestScreen, Le as RelayProvider, n as TotpQrCode, u as VerifyEmailScreen, Ie as appendNodeToConnections, Te as backofficeReactI18nResources, me as base64UrlToBuffer, he as bufferToBase64Url, w as buildDataTableColumns, r as buildTotpOtpAuthUri, ze as configureBackofficeShortAccessTokenRelayEnvironment, ie as configureRelayEnvironment, Be as configureShortAccessTokenRelayEnvironment, i as createBackofficeAuthHeaders, F as createBackofficeEntityLinkProps, ke as createBackofficeLazyValue, we as createI18nInstance, Fe as createInlineDataReader, ve as createUseAuth, Ae as decodeBase64ToUtf8, je as encodeUtf8ToBase64, Me as formatFileSize, I as formatListAsMarkdown, ae as getEnvironment, oe as getNetwork, se as getRelayOperationActivitySnapshot, ce as getRelayTransportSnapshot, He as identityView, ge as mapWebAuthnRegistrationError, _e as parseSignCount, re as parseToolJson, le as reconnectRelayWebSocket, ue as refreshRelayWebSocketAuthentication, d as requireField, f as requireLinkedRecordId, de as resetRelayStore, p as resolveAgentStartOutcome, x as resolveBackofficeLink, b as resolveBackofficeTargetIcon, m as resolveMutationOutcome, Pe as resolveVisibleDetailPages, fe as subscribeRelayOperationActivity, pe as subscribeRelayTransport, o as synchronizeAuthStatusQuery, Ne as uploadFilesSequentially, t as useAuthRefreshStateSnapshot, h as useBackofficeConfig, S as useBackofficeLink, ye as useBackofficeListUrlState, be as useConditionalSubscription, xe as useCopyToClipboard, Ue as useCursorResumableSubscription, Se as useInfiniteConnection, Ve as useMutationAction, Ce as useRefetchNeededReload, Re as useRelayOperationActivity, De as useReviewStatusLabel, Ee as withBackofficeReactI18nResources };
|
|
@@ -1,18 +1,18 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import {
|
|
3
|
-
import {
|
|
4
|
-
import {
|
|
1
|
+
import { getBackofficeLoginPath as e } from "../router/backofficeAuthPaths.js";
|
|
2
|
+
import { AcceptInvitationScreen as t } from "../auth/pages/AcceptInvitationScreen.js";
|
|
3
|
+
import { useBackofficeConfig as n } from "../provider/BackofficeConfigContext.js";
|
|
4
|
+
import { useBackofficeAuth as r } from "../hooks/useBackofficeAuth.js";
|
|
5
5
|
import { useCallback as i, useContext as a } from "react";
|
|
6
6
|
import { jsx as o } from "react/jsx-runtime";
|
|
7
7
|
import s from "@plumile/router/routing/RoutingContext.js";
|
|
8
8
|
//#region src/pages/BackofficeAcceptInvitationPage.tsx
|
|
9
9
|
var c = () => {
|
|
10
|
-
let c = a(s), { basePath: l } =
|
|
10
|
+
let c = a(s), { basePath: l } = n(), u = r(), d = i(() => {
|
|
11
11
|
c?.history.push({ pathname: l });
|
|
12
12
|
}, [l, c?.history]), f = i(() => {
|
|
13
|
-
c?.history.push({ pathname:
|
|
13
|
+
c?.history.push({ pathname: e(l) });
|
|
14
14
|
}, [l, c?.history]);
|
|
15
|
-
return /* @__PURE__ */ o(
|
|
15
|
+
return /* @__PURE__ */ o(t, {
|
|
16
16
|
auth: u,
|
|
17
17
|
onSuccessRedirect: d,
|
|
18
18
|
onBackToLogin: f
|
|
@@ -1,21 +1,21 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import {
|
|
3
|
-
import {
|
|
4
|
-
import {
|
|
5
|
-
import {
|
|
6
|
-
import {
|
|
1
|
+
import { getBackofficeLoginPath as e } from "../router/backofficeAuthPaths.js";
|
|
2
|
+
import { useBackofficeReactTranslation as t } from "../i18n/useBackofficeReactTranslation.js";
|
|
3
|
+
import { useBackofficeConfig as n } from "../provider/BackofficeConfigContext.js";
|
|
4
|
+
import { BackofficeTopbarPortalContextProvider as r } from "../components/backoffice/layout/breadcrumb/BackofficeTopbarPortalContext.js";
|
|
5
|
+
import { resolveActiveEntityId as i, resolveSidebarGroups as a, resolveVisibleEntityIds as ee } from "../components/backoffice/layout/sidebarUtils.js";
|
|
6
|
+
import { BackofficePermissionsProvider as o } from "../components/backoffice/layout/BackofficePermissionsContext.js";
|
|
7
|
+
import { buildSidebarSections as s } from "../components/backoffice/layout/buildSidebarSections.js";
|
|
7
8
|
import te from "../components/backoffice/layout/mapViewerToSidebarProfileView.js";
|
|
8
9
|
import { BackofficeContentBoundary as ne } from "../components/backoffice/routing/BackofficeContentBoundary.js";
|
|
9
10
|
import { resetRelayStore as re } from "../relay/environment.js";
|
|
10
11
|
import { useRelayEnvironment as ie } from "../relay/useRelayEnvironment.js";
|
|
11
12
|
import ae from "../hooks/useBackofficeSidebarPins.js";
|
|
12
13
|
import oe from "../hooks/useSidebarGroupCollapse.js";
|
|
13
|
-
import {
|
|
14
|
-
import {
|
|
15
|
-
import {
|
|
16
|
-
import {
|
|
17
|
-
import {
|
|
18
|
-
import { ToastProvider as f } from "@plumile/ui/atomic/molecules/toast/ToastProvider.js";
|
|
14
|
+
import { useCallback as se, useContext as ce, useEffect as c, useMemo as l, useState as u } from "react";
|
|
15
|
+
import { jsx as d } from "react/jsx-runtime";
|
|
16
|
+
import { useTranslation as le } from "react-i18next";
|
|
17
|
+
import { commitMutation as ue, usePreloadedQuery as f } from "react-relay";
|
|
18
|
+
import { ToastProvider as de } from "@plumile/ui/atomic/molecules/toast/ToastProvider.js";
|
|
19
19
|
import fe from "@plumile/router/routing/RoutingContext.js";
|
|
20
20
|
import { GlobalSearchInput as pe } from "@plumile/ui/backoffice/molecules/global_search_input/GlobalSearchInput.js";
|
|
21
21
|
import me from "@plumile/router/routing/useLocation.js";
|
|
@@ -40,16 +40,16 @@ var ge = "plumile:backoffice:recent-items", _e = "plumile:backoffice:sidebar", v
|
|
|
40
40
|
if (!(typeof window > "u")) try {
|
|
41
41
|
window.localStorage.setItem(e, JSON.stringify(t));
|
|
42
42
|
} catch {}
|
|
43
|
-
}, m = ({ children:
|
|
44
|
-
let { t: _ } =
|
|
43
|
+
}, m = ({ children: f, permissions: m, authStatus: h, activeGroupId: g }) => {
|
|
44
|
+
let { t: _ } = le(), { t: v } = t(), { pathname: y } = me(), b = ce(fe), x = ie(), { auth: S, basePath: C, dashboards: w, entities: T, sidebar: E } = n(), [D, be] = u(""), O = E?.preferences?.storageKey ?? _e, k = E?.preferences?.persistCollapsed === !0, [A, xe] = u(() => {
|
|
45
45
|
if (!k || typeof window > "u") return !1;
|
|
46
46
|
try {
|
|
47
47
|
return window.localStorage.getItem(`${O}:collapsed`) === "true";
|
|
48
48
|
} catch {
|
|
49
49
|
return !1;
|
|
50
50
|
}
|
|
51
|
-
}), [j, M] =
|
|
52
|
-
|
|
51
|
+
}), [j, M] = u(!1), [N, P] = u(null), F = E?.recentItems, I = F?.enabled === !0, L = F?.storageKey ?? ge, R = F?.maxItems ?? 8, [z, Se] = u(() => I ? ve(L) : []);
|
|
52
|
+
c(() => {
|
|
53
53
|
if (!(!k || typeof window > "u")) try {
|
|
54
54
|
window.localStorage.setItem(`${O}:collapsed`, String(A));
|
|
55
55
|
} catch {}
|
|
@@ -58,13 +58,13 @@ var ge = "plumile:backoffice:recent-items", _e = "plumile:backoffice:sidebar", v
|
|
|
58
58
|
k,
|
|
59
59
|
O
|
|
60
60
|
]);
|
|
61
|
-
let B =
|
|
61
|
+
let B = l(() => a(T, E), [T, E]), Ce = l(() => Object.keys(B), [B]), we = l(() => Object.fromEntries(Object.entries(B).map(([e, t]) => [e, t.behavior?.defaultCollapsed ?? !0])), [B]), Te = l(() => ee(B, T, E, m), [
|
|
62
62
|
T,
|
|
63
63
|
B,
|
|
64
64
|
m,
|
|
65
65
|
E
|
|
66
|
-
]), V =
|
|
67
|
-
|
|
66
|
+
]), V = l(() => i(y, T), [T, y]);
|
|
67
|
+
c(() => {
|
|
68
68
|
if (!I || V == null) return;
|
|
69
69
|
let e = T[V];
|
|
70
70
|
if (e == null || e.kind !== "tool" && !e.hasList) return;
|
|
@@ -103,7 +103,7 @@ var ge = "plumile:backoffice:recent-items", _e = "plumile:backoffice:sidebar", v
|
|
|
103
103
|
defaultCollapsedByGroupId: we,
|
|
104
104
|
persist: E?.preferences?.persistGroups === !0,
|
|
105
105
|
storageKey: K
|
|
106
|
-
}), ke =
|
|
106
|
+
}), ke = l(() => s({
|
|
107
107
|
basePath: C,
|
|
108
108
|
pathname: y,
|
|
109
109
|
entities: T,
|
|
@@ -136,7 +136,7 @@ var ge = "plumile:backoffice:recent-items", _e = "plumile:backoffice:sidebar", v
|
|
|
136
136
|
W,
|
|
137
137
|
v,
|
|
138
138
|
_
|
|
139
|
-
]), Ae =
|
|
139
|
+
]), Ae = l(() => s({
|
|
140
140
|
basePath: C,
|
|
141
141
|
pathname: y,
|
|
142
142
|
entities: T,
|
|
@@ -169,22 +169,22 @@ var ge = "plumile:backoffice:recent-items", _e = "plumile:backoffice:sidebar", v
|
|
|
169
169
|
W,
|
|
170
170
|
v,
|
|
171
171
|
_
|
|
172
|
-
]), Y =
|
|
172
|
+
]), Y = se(() => {
|
|
173
173
|
j || (M(!0), (async () => {
|
|
174
174
|
try {
|
|
175
|
-
let
|
|
176
|
-
await new Promise((
|
|
177
|
-
|
|
178
|
-
mutation:
|
|
175
|
+
let t = await S.logout.load();
|
|
176
|
+
await new Promise((e, n) => {
|
|
177
|
+
ue(x, {
|
|
178
|
+
mutation: t.logoutMutation,
|
|
179
179
|
variables: {},
|
|
180
180
|
onCompleted: () => {
|
|
181
|
-
|
|
181
|
+
e();
|
|
182
182
|
},
|
|
183
183
|
onError: (e) => {
|
|
184
184
|
n(e);
|
|
185
185
|
}
|
|
186
186
|
});
|
|
187
|
-
}), localStorage.removeItem("auth_token"), localStorage.removeItem("remember_me"), re(), b?.history.push({ pathname:
|
|
187
|
+
}), localStorage.removeItem("auth_token"), localStorage.removeItem("remember_me"), re(), b?.history.push({ pathname: e(C) });
|
|
188
188
|
} finally {
|
|
189
189
|
M(!1);
|
|
190
190
|
}
|
|
@@ -195,10 +195,10 @@ var ge = "plumile:backoffice:recent-items", _e = "plumile:backoffice:sidebar", v
|
|
|
195
195
|
j,
|
|
196
196
|
x,
|
|
197
197
|
b
|
|
198
|
-
]), X = h?.me ?? null, Z =
|
|
198
|
+
]), X = h?.me ?? null, Z = l(() => te({
|
|
199
199
|
viewer: X,
|
|
200
200
|
unknownUserLabel: v("sidebar.profile.unknownUser")
|
|
201
|
-
}), [v, X]), je = /* @__PURE__ */
|
|
201
|
+
}), [v, X]), je = /* @__PURE__ */ d(p, {
|
|
202
202
|
collapsed: !1,
|
|
203
203
|
viewer: Z,
|
|
204
204
|
labels: {
|
|
@@ -208,7 +208,7 @@ var ge = "plumile:backoffice:recent-items", _e = "plumile:backoffice:sidebar", v
|
|
|
208
208
|
},
|
|
209
209
|
onSignOut: Y,
|
|
210
210
|
isSigningOut: j
|
|
211
|
-
}), Me = /* @__PURE__ */
|
|
211
|
+
}), Me = /* @__PURE__ */ d(p, {
|
|
212
212
|
collapsed: !1,
|
|
213
213
|
viewer: Z,
|
|
214
214
|
labels: {
|
|
@@ -219,14 +219,14 @@ var ge = "plumile:backoffice:recent-items", _e = "plumile:backoffice:sidebar", v
|
|
|
219
219
|
onSignOut: Y,
|
|
220
220
|
isSigningOut: j
|
|
221
221
|
}), Q = null;
|
|
222
|
-
N != null && (Q = /* @__PURE__ */
|
|
223
|
-
let $ = /* @__PURE__ */
|
|
222
|
+
N != null && (Q = /* @__PURE__ */ d(ne, { children: f }));
|
|
223
|
+
let $ = /* @__PURE__ */ d(pe, {
|
|
224
224
|
value: D,
|
|
225
225
|
onChange: be,
|
|
226
226
|
placeholder: v("sidebar.search.placeholder"),
|
|
227
227
|
ariaLabel: v("sidebar.search.placeholder")
|
|
228
228
|
});
|
|
229
|
-
return /* @__PURE__ */
|
|
229
|
+
return /* @__PURE__ */ d(de, { children: /* @__PURE__ */ d(he, {
|
|
230
230
|
sidebar: {
|
|
231
231
|
sections: ke,
|
|
232
232
|
search: $,
|
|
@@ -245,11 +245,11 @@ var ge = "plumile:backoffice:recent-items", _e = "plumile:backoffice:sidebar", v
|
|
|
245
245
|
hideCollapseToggle: !0,
|
|
246
246
|
navigationAriaLabel: v("sidebar.navigationAriaLabel")
|
|
247
247
|
},
|
|
248
|
-
topbar: { breadcrumb: /* @__PURE__ */
|
|
248
|
+
topbar: { breadcrumb: /* @__PURE__ */ d("div", { ref: P }) },
|
|
249
249
|
contentScrollMode: "contained",
|
|
250
|
-
children: /* @__PURE__ */
|
|
250
|
+
children: /* @__PURE__ */ d(o, {
|
|
251
251
|
permissions: m,
|
|
252
|
-
children: /* @__PURE__ */
|
|
252
|
+
children: /* @__PURE__ */ d(r, {
|
|
253
253
|
value: {
|
|
254
254
|
target: N,
|
|
255
255
|
dashboardHref: C,
|
|
@@ -260,20 +260,20 @@ var ge = "plumile:backoffice:recent-items", _e = "plumile:backoffice:sidebar", v
|
|
|
260
260
|
})
|
|
261
261
|
}) });
|
|
262
262
|
}, h = ({ children: e, permissionsQuery: t, prepared: n, authStatus: r, activeGroupId: i }) => {
|
|
263
|
-
let a =
|
|
264
|
-
return /* @__PURE__ */
|
|
263
|
+
let a = f(t, n);
|
|
264
|
+
return /* @__PURE__ */ d(m, {
|
|
265
265
|
permissions: a,
|
|
266
266
|
authStatus: r,
|
|
267
267
|
activeGroupId: i,
|
|
268
268
|
children: e
|
|
269
269
|
});
|
|
270
|
-
}, g = ({ children: e, permissionsQuery: t, prepared: n, authStatus: r, activeGroupId: i }) => t != null && n != null ? /* @__PURE__ */
|
|
270
|
+
}, g = ({ children: e, permissionsQuery: t, prepared: n, authStatus: r, activeGroupId: i }) => t != null && n != null ? /* @__PURE__ */ d(h, {
|
|
271
271
|
permissionsQuery: t,
|
|
272
272
|
prepared: n,
|
|
273
273
|
authStatus: r,
|
|
274
274
|
activeGroupId: i,
|
|
275
275
|
children: e
|
|
276
|
-
}) : /* @__PURE__ */
|
|
276
|
+
}) : /* @__PURE__ */ d(m, {
|
|
277
277
|
permissions: null,
|
|
278
278
|
authStatus: r,
|
|
279
279
|
activeGroupId: i,
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"BackofficeLayoutPage.js","names":[],"sources":["../../../src/pages/BackofficeLayoutPage.tsx"],"sourcesContent":["import {\n useEffect,\n useMemo,\n type JSX,\n type ReactNode,\n useCallback,\n useContext,\n useState,\n} from 'react';\nimport { useTranslation } from 'react-i18next';\nimport {\n commitMutation,\n usePreloadedQuery,\n type PreloadedQuery,\n} from 'react-relay';\nimport type {\n GraphQLTaggedNode,\n MutationParameters,\n OperationType,\n} from 'relay-runtime';\nimport RoutingContext from '@plumile/router/routing/RoutingContext.js';\nimport useLocation from '@plumile/router/routing/useLocation.js';\n\nimport { AdminShellLayout } from '@plumile/ui/admin/templates/admin_shell_layout/AdminShellLayout.js';\nimport { ToastProvider } from '@plumile/ui/atomic/molecules/toast/ToastProvider.js';\nimport { GlobalSearchInput } from '@plumile/ui/backoffice/molecules/global_search_input/GlobalSearchInput.js';\nimport { SidebarProfileMenu } from '@plumile/ui/components/navigation/sidebar/SidebarProfileMenu.js';\n\nimport { useBackofficeReactTranslation } from '../i18n/useBackofficeReactTranslation.js';\nimport { useBackofficeConfig } from '../provider/BackofficeConfigContext.js';\nimport type { LogoutResponse, LogoutVariables } from '../hooks/useAuth.js';\nimport { useBackofficeSidebarPins } from '../hooks/useBackofficeSidebarPins.js';\nimport { useSidebarGroupCollapse } from '../hooks/useSidebarGroupCollapse.js';\nimport { buildSidebarSections } from '../components/backoffice/layout/buildSidebarSections.js';\nimport { BackofficeContentBoundary } from '../components/backoffice/routing/BackofficeContentBoundary.js';\nimport {\n resolveActiveEntityId,\n resolveSidebarGroups,\n resolveVisibleEntityIds,\n} from '../components/backoffice/layout/sidebarUtils.js';\nimport { BackofficeTopbarPortalContextProvider } from '../components/backoffice/layout/breadcrumb/BackofficeTopbarPortalContext.js';\nimport { BackofficePermissionsProvider } from '../components/backoffice/layout/BackofficePermissionsContext.js';\nimport {\n mapViewerToSidebarProfileView,\n type BackofficeViewerIdentity,\n} from '../components/backoffice/layout/mapViewerToSidebarProfileView.js';\nimport { resetRelayStore } from '../relay/environment.js';\nimport { useRelayEnvironment } from '../relay/useRelayEnvironment.js';\nimport { getBackofficeLoginPath } from '../router/backofficeAuthPaths.js';\nimport type { BackofficeSidebarRecentItem } from '../provider/types.js';\n\nexport type BackofficeLayoutPageProps = {\n children: ReactNode;\n permissionsQuery?: GraphQLTaggedNode;\n prepared?: PreloadedQuery<OperationType> | null;\n authStatus?: {\n isLoggedIn?: boolean | null;\n me?: BackofficeViewerIdentity | null;\n } | null;\n activeGroupId?: string | null;\n};\n\ntype LayoutShellProps = {\n children: ReactNode;\n permissions: unknown;\n authStatus?: {\n isLoggedIn?: boolean | null;\n me?: BackofficeViewerIdentity | null;\n } | null;\n activeGroupId?: string | null;\n};\n\nconst DEFAULT_RECENT_ITEMS_STORAGE_KEY = 'plumile:backoffice:recent-items';\nconst DEFAULT_SIDEBAR_PREFS_STORAGE_KEY = 'plumile:backoffice:sidebar';\n\nconst readRecentItems = (\n storageKey: string,\n): readonly BackofficeSidebarRecentItem[] => {\n if (typeof window === 'undefined') {\n return [];\n }\n try {\n const raw = window.localStorage.getItem(storageKey);\n if (raw == null) {\n return [];\n }\n const parsed = JSON.parse(raw) as unknown;\n if (!Array.isArray(parsed)) {\n return [];\n }\n return parsed.filter((item): item is BackofficeSidebarRecentItem => {\n if (item == null || typeof item !== 'object') {\n return false;\n }\n const candidate = item as Partial<BackofficeSidebarRecentItem>;\n return (\n (candidate.kind === 'entity' || candidate.kind === 'tool') &&\n typeof candidate.id === 'string' &&\n typeof candidate.label === 'string' &&\n typeof candidate.href === 'string' &&\n typeof candidate.visitedAt === 'number'\n );\n });\n } catch {\n return [];\n }\n};\n\nconst writeRecentItems = (\n storageKey: string,\n items: readonly BackofficeSidebarRecentItem[],\n): void => {\n if (typeof window === 'undefined') {\n return;\n }\n try {\n window.localStorage.setItem(storageKey, JSON.stringify(items));\n } catch {\n // Ignore storage quota / privacy mode failures.\n }\n};\n\nconst BackofficeLayoutShell = ({\n children,\n permissions,\n authStatus,\n activeGroupId,\n}: LayoutShellProps): JSX.Element => {\n const { t: tApp } = useTranslation();\n const { t } = useBackofficeReactTranslation();\n const { pathname } = useLocation();\n const routing = useContext(RoutingContext);\n const relayEnvironment = useRelayEnvironment();\n const {\n auth: authConfig,\n basePath,\n dashboards,\n entities,\n sidebar,\n } = useBackofficeConfig();\n const [sidebarQuery, setSidebarQuery] = useState('');\n const sidebarPreferencesStorageKey =\n sidebar?.preferences?.storageKey ?? DEFAULT_SIDEBAR_PREFS_STORAGE_KEY;\n const persistSidebarCollapsed =\n sidebar?.preferences?.persistCollapsed === true;\n const [isSidebarCollapsed, setIsSidebarCollapsed] = useState(() => {\n if (!persistSidebarCollapsed || typeof window === 'undefined') {\n return false;\n }\n try {\n return (\n window.localStorage.getItem(\n `${sidebarPreferencesStorageKey}:collapsed`,\n ) === 'true'\n );\n } catch {\n return false;\n }\n });\n const [isSigningOut, setIsSigningOut] = useState(false);\n const [topbarTarget, setTopbarTarget] = useState<HTMLDivElement | null>(null);\n const recentItemsConfig = sidebar?.recentItems;\n const recentItemsEnabled = recentItemsConfig?.enabled === true;\n const recentItemsStorageKey =\n recentItemsConfig?.storageKey ?? DEFAULT_RECENT_ITEMS_STORAGE_KEY;\n const recentItemsMaxItems = recentItemsConfig?.maxItems ?? 8;\n const [recentItems, setRecentItems] = useState<\n readonly BackofficeSidebarRecentItem[]\n >(() => {\n if (!recentItemsEnabled) {\n return [];\n }\n return readRecentItems(recentItemsStorageKey);\n });\n\n useEffect(() => {\n if (!persistSidebarCollapsed || typeof window === 'undefined') {\n return;\n }\n try {\n window.localStorage.setItem(\n `${sidebarPreferencesStorageKey}:collapsed`,\n String(isSidebarCollapsed),\n );\n } catch {\n // Ignore storage quota / privacy mode failures.\n }\n }, [\n isSidebarCollapsed,\n persistSidebarCollapsed,\n sidebarPreferencesStorageKey,\n ]);\n\n const groups = useMemo(() => {\n return resolveSidebarGroups(entities, sidebar);\n }, [entities, sidebar]);\n\n const groupIds = useMemo(() => {\n return Object.keys(groups);\n }, [groups]);\n\n const defaultCollapsedByGroupId = useMemo(() => {\n return Object.fromEntries(\n Object.entries(groups).map(([groupId, group]) => {\n return [groupId, group.behavior?.defaultCollapsed ?? true];\n }),\n );\n }, [groups]);\n\n const visibleEntityIds = useMemo(() => {\n return resolveVisibleEntityIds(groups, entities, sidebar, permissions);\n }, [entities, groups, permissions, sidebar]);\n\n const activeEntityId = useMemo(() => {\n return resolveActiveEntityId(pathname, entities);\n }, [entities, pathname]);\n\n useEffect(() => {\n if (!recentItemsEnabled || activeEntityId == null) {\n return;\n }\n const config = entities[activeEntityId];\n if (config == null) {\n return;\n }\n if (config.kind !== 'tool' && !config.hasList) {\n return;\n }\n const href = config.routes.list;\n let kind: BackofficeSidebarRecentItem['kind'] = 'entity';\n if (config.kind === 'tool') {\n kind = 'tool';\n }\n const item: BackofficeSidebarRecentItem = {\n kind,\n id: activeEntityId,\n label: config.label(tApp),\n href,\n visitedAt: Date.now(),\n };\n setRecentItems((prev) => {\n const next = [\n item,\n ...prev.filter((entry) => {\n return entry.id !== item.id || entry.kind !== item.kind;\n }),\n ].slice(0, recentItemsMaxItems);\n writeRecentItems(recentItemsStorageKey, next);\n return next;\n });\n }, [\n activeEntityId,\n entities,\n recentItemsEnabled,\n recentItemsMaxItems,\n recentItemsStorageKey,\n tApp,\n ]);\n\n const pinningEnabled = sidebar?.pinnedItems?.enabled === true;\n\n const {\n pins,\n toggle: togglePin,\n reorder: reorderPin,\n } = useBackofficeSidebarPins({\n enabled: pinningEnabled,\n storageKey: sidebar?.pinnedItems?.storageKey,\n visibleEntityIds,\n });\n\n let sidebarPinnedEntityIds: readonly string[] | undefined;\n let sidebarTogglePin: ((entityId: string) => void) | undefined;\n let sidebarReorderPin: ((fromId: string, toId: string) => void) | undefined;\n if (pinningEnabled) {\n sidebarPinnedEntityIds = pins;\n sidebarTogglePin = togglePin;\n sidebarReorderPin = reorderPin;\n }\n\n let groupCollapseStorageKey: string | undefined;\n if (sidebar?.preferences?.storageKey != null) {\n groupCollapseStorageKey = `${sidebar.preferences.storageKey}:groups`;\n }\n\n const { collapsedByGroupId, setCollapsed } = useSidebarGroupCollapse({\n groupIds,\n activeGroupId,\n defaultCollapsedByGroupId,\n persist: sidebar?.preferences?.persistGroups === true,\n storageKey: groupCollapseStorageKey,\n });\n\n const sections = useMemo(() => {\n return buildSidebarSections({\n basePath,\n pathname,\n entities,\n dashboards,\n sidebar,\n permissions,\n searchQuery: sidebarQuery,\n tApp,\n t,\n pinnedEntityIds: sidebarPinnedEntityIds,\n recentItems,\n onTogglePin: sidebarTogglePin,\n onReorderPin: sidebarReorderPin,\n collapsedByGroupId,\n onGroupCollapsedChange: setCollapsed,\n sidebarCollapsed: false,\n });\n }, [\n basePath,\n collapsedByGroupId,\n entities,\n dashboards,\n pathname,\n permissions,\n recentItems,\n setCollapsed,\n sidebar,\n sidebarPinnedEntityIds,\n sidebarQuery,\n sidebarReorderPin,\n sidebarTogglePin,\n t,\n tApp,\n ]);\n\n const mobileSections = useMemo(() => {\n return buildSidebarSections({\n basePath,\n pathname,\n entities,\n dashboards,\n sidebar,\n permissions,\n searchQuery: sidebarQuery,\n tApp,\n t,\n pinnedEntityIds: sidebarPinnedEntityIds,\n recentItems,\n onTogglePin: sidebarTogglePin,\n onReorderPin: sidebarReorderPin,\n collapsedByGroupId,\n onGroupCollapsedChange: setCollapsed,\n sidebarCollapsed: false,\n });\n }, [\n basePath,\n collapsedByGroupId,\n entities,\n dashboards,\n pathname,\n permissions,\n recentItems,\n setCollapsed,\n sidebar,\n sidebarPinnedEntityIds,\n sidebarQuery,\n sidebarReorderPin,\n sidebarTogglePin,\n t,\n tApp,\n ]);\n\n const handleSignOut = useCallback(() => {\n if (isSigningOut) {\n return;\n }\n\n type LogoutMutation = MutationParameters & {\n response: LogoutResponse;\n variables: LogoutVariables;\n };\n\n setIsSigningOut(true);\n\n const runSignOut = async (): Promise<void> => {\n try {\n const config = await authConfig.logout.load();\n await new Promise<void>((resolve, reject) => {\n commitMutation<LogoutMutation>(relayEnvironment, {\n mutation: config.logoutMutation,\n variables: {},\n onCompleted: () => {\n resolve();\n },\n onError: (error) => {\n reject(error);\n },\n });\n });\n localStorage.removeItem('auth_token');\n localStorage.removeItem('remember_me');\n resetRelayStore();\n routing?.history.push({ pathname: getBackofficeLoginPath(basePath) });\n } finally {\n setIsSigningOut(false);\n }\n };\n\n runSignOut().catch(() => {\n /* noop */\n });\n }, [authConfig.logout, basePath, isSigningOut, relayEnvironment, routing]);\n\n const viewer = authStatus?.me ?? null;\n const sidebarProfile = useMemo(() => {\n return mapViewerToSidebarProfileView({\n viewer,\n unknownUserLabel: t('sidebar.profile.unknownUser'),\n });\n }, [t, viewer]);\n\n const sidebarFooter = (\n <SidebarProfileMenu\n collapsed={false}\n viewer={sidebarProfile}\n labels={{\n sectionTitle: t('sidebar.profile.title'),\n menuAriaLabel: t('sidebar.profile.menuAriaLabel'),\n signOut: t('sidebar.profile.actions.signOut'),\n }}\n onSignOut={handleSignOut}\n isSigningOut={isSigningOut}\n />\n );\n\n const mobileSidebarFooter = (\n <SidebarProfileMenu\n collapsed={false}\n viewer={sidebarProfile}\n labels={{\n sectionTitle: t('sidebar.profile.title'),\n menuAriaLabel: t('sidebar.profile.menuAriaLabel'),\n signOut: t('sidebar.profile.actions.signOut'),\n }}\n onSignOut={handleSignOut}\n isSigningOut={isSigningOut}\n />\n );\n\n let contentNode: JSX.Element | null = null;\n if (topbarTarget != null) {\n contentNode = (\n <BackofficeContentBoundary>{children}</BackofficeContentBoundary>\n );\n }\n\n const sidebarSearchNode = (\n <GlobalSearchInput\n value={sidebarQuery}\n onChange={setSidebarQuery}\n placeholder={t('sidebar.search.placeholder')}\n ariaLabel={t('sidebar.search.placeholder')}\n />\n );\n\n return (\n <ToastProvider>\n <AdminShellLayout\n sidebar={{\n sections,\n search: sidebarSearchNode,\n footer: sidebarFooter,\n isCollapsed: isSidebarCollapsed,\n onCollapsedChange: setIsSidebarCollapsed,\n collapseToggleLabel: t('sidebar.actions.collapseSidebar'),\n expandToggleLabel: t('sidebar.actions.expandSidebar'),\n navigationAriaLabel: t('sidebar.navigationAriaLabel'),\n }}\n mobileSidebar={{\n sections: mobileSections,\n search: sidebarSearchNode,\n footer: mobileSidebarFooter,\n isCollapsed: false,\n hideCollapseToggle: true,\n navigationAriaLabel: t('sidebar.navigationAriaLabel'),\n }}\n topbar={{\n breadcrumb: <div ref={setTopbarTarget} />,\n }}\n contentScrollMode=\"contained\"\n >\n <BackofficePermissionsProvider permissions={permissions}>\n <BackofficeTopbarPortalContextProvider\n value={{\n target: topbarTarget,\n dashboardHref: basePath,\n dashboardLabel: t('sidebar.items.dashboard'),\n }}\n >\n {contentNode}\n </BackofficeTopbarPortalContextProvider>\n </BackofficePermissionsProvider>\n </AdminShellLayout>\n </ToastProvider>\n );\n};\n\ntype LayoutWithPermissionsProps = {\n children: ReactNode;\n permissionsQuery: GraphQLTaggedNode;\n prepared: PreloadedQuery<OperationType>;\n authStatus?: {\n isLoggedIn?: boolean | null;\n me?: BackofficeViewerIdentity | null;\n } | null;\n activeGroupId?: string | null;\n};\n\nconst LayoutWithPermissions = ({\n children,\n permissionsQuery,\n prepared,\n authStatus,\n activeGroupId,\n}: LayoutWithPermissionsProps): JSX.Element => {\n const permissions = usePreloadedQuery(permissionsQuery, prepared);\n\n return (\n <BackofficeLayoutShell\n permissions={permissions}\n authStatus={authStatus}\n activeGroupId={activeGroupId}\n >\n {children}\n </BackofficeLayoutShell>\n );\n};\n\nexport const BackofficeLayoutPage = ({\n children,\n permissionsQuery,\n prepared,\n authStatus,\n activeGroupId,\n}: BackofficeLayoutPageProps): JSX.Element => {\n if (permissionsQuery != null && prepared != null) {\n return (\n <LayoutWithPermissions\n permissionsQuery={permissionsQuery}\n prepared={prepared}\n authStatus={authStatus}\n activeGroupId={activeGroupId}\n >\n {children}\n </LayoutWithPermissions>\n );\n }\n\n return (\n <BackofficeLayoutShell\n permissions={null}\n authStatus={authStatus}\n activeGroupId={activeGroupId}\n >\n {children}\n </BackofficeLayoutShell>\n );\n};\n\nexport default BackofficeLayoutPage;\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;AAwEA,IAAM,KAAmC,mCACnC,KAAoC,8BAEpC,MACJ,MAC2C;CAC3C,IAAI,OAAO,SAAW,KACpB,OAAO,CAAC;CAEV,IAAI;EACF,IAAM,IAAM,OAAO,aAAa,QAAQ,CAAU;EAClD,IAAI,KAAO,MACT,OAAO,CAAC;EAEV,IAAM,IAAS,KAAK,MAAM,CAAG;EAI7B,OAHK,MAAM,QAAQ,CAAM,IAGlB,EAAO,QAAQ,MAA8C;GAClE,IAAoB,OAAO,KAAS,aAAhC,GACF,OAAO;GAET,IAAM,IAAY;GAClB,QACG,EAAU,SAAS,YAAY,EAAU,SAAS,WACnD,OAAO,EAAU,MAAO,YACxB,OAAO,EAAU,SAAU,YAC3B,OAAO,EAAU,QAAS,YAC1B,OAAO,EAAU,aAAc;EAEnC,CAAC,IAdQ,CAAC;CAeZ,QAAQ;EACN,OAAO,CAAC;CACV;AACF,GAEM,MACJ,GACA,MACS;CACL,aAAO,SAAW,MAGtB,IAAI;EACF,OAAO,aAAa,QAAQ,GAAY,KAAK,UAAU,CAAK,CAAC;CAC/D,QAAQ,CAER;AACF,GAEM,KAAyB,EAC7B,aACA,gBACA,eACA,uBACmC;CACnC,IAAM,EAAE,GAAG,MAAS,GAAe,GAC7B,EAAE,SAAM,EAA8B,GACtC,EAAE,gBAAa,GAAY,GAC3B,IAAU,GAAW,EAAc,GACnC,IAAmB,GAAoB,GACvC,EACJ,MAAM,GACN,aACA,eACA,aACA,eACE,EAAoB,GAClB,CAAC,GAAc,MAAmB,EAAS,EAAE,GAC7C,IACJ,GAAS,aAAa,cAAc,IAChC,IACJ,GAAS,aAAa,qBAAqB,IACvC,CAAC,GAAoB,MAAyB,QAAe;EACjE,IAAI,CAAC,KAA2B,OAAO,SAAW,KAChD,OAAO;EAET,IAAI;GACF,OACE,OAAO,aAAa,QAClB,GAAG,EAA6B,WAClC,MAAM;EAEV,QAAQ;GACN,OAAO;EACT;CACF,CAAC,GACK,CAAC,GAAc,KAAmB,EAAS,EAAK,GAChD,CAAC,GAAc,KAAmB,EAAgC,IAAI,GACtE,IAAoB,GAAS,aAC7B,IAAqB,GAAmB,YAAY,IACpD,IACJ,GAAmB,cAAc,IAC7B,IAAsB,GAAmB,YAAY,GACrD,CAAC,GAAa,MAAkB,QAG/B,IAGE,GAAgB,CAAqB,IAFnC,CAAC,CAGX;CAED,QAAgB;EACV,OAAC,KAA2B,OAAO,SAAW,MAGlD,IAAI;GACF,OAAO,aAAa,QAClB,GAAG,EAA6B,aAChC,OAAO,CAAkB,CAC3B;EACF,QAAQ,CAER;CACF,GAAG;EACD;EACA;EACA;CACF,CAAC;CAED,IAAM,IAAS,QACN,EAAqB,GAAU,CAAO,GAC5C,CAAC,GAAU,CAAO,CAAC,GAEhB,KAAW,QACR,OAAO,KAAK,CAAM,GACxB,CAAC,CAAM,CAAC,GAEL,KAA4B,QACzB,OAAO,YACZ,OAAO,QAAQ,CAAM,CAAC,CAAC,KAAK,CAAC,GAAS,OAC7B,CAAC,GAAS,EAAM,UAAU,oBAAoB,EAAI,CAC1D,CACH,GACC,CAAC,CAAM,CAAC,GAEL,KAAmB,QAChB,EAAwB,GAAQ,GAAU,GAAS,CAAW,GACpE;EAAC;EAAU;EAAQ;EAAa;CAAO,CAAC,GAErC,IAAiB,QACd,EAAsB,GAAU,CAAQ,GAC9C,CAAC,GAAU,CAAQ,CAAC;CAEvB,QAAgB;EACd,IAAI,CAAC,KAAsB,KAAkB,MAC3C;EAEF,IAAM,IAAS,EAAS;EAIxB,IAHI,KAAU,QAGV,EAAO,SAAS,UAAU,CAAC,EAAO,SACpC;EAEF,IAAM,IAAO,EAAO,OAAO,MACvB,IAA4C;EAChD,AAAI,EAAO,SAAS,WAClB,IAAO;EAET,IAAM,IAAoC;GACxC;GACA,IAAI;GACJ,OAAO,EAAO,MAAM,CAAI;GACxB;GACA,WAAW,KAAK,IAAI;EACtB;EACA,IAAgB,MAAS;GACvB,IAAM,IAAO,CACX,GACA,GAAG,EAAK,QAAQ,MACP,EAAM,OAAO,EAAK,MAAM,EAAM,SAAS,EAAK,IACpD,CACH,CAAC,CAAC,MAAM,GAAG,CAAmB;GAE9B,OADA,GAAiB,GAAuB,CAAI,GACrC;EACT,CAAC;CACH,GAAG;EACD;EACA;EACA;EACA;EACA;EACA;CACF,CAAC;CAED,IAAM,IAAiB,GAAS,aAAa,YAAY,IAEnD,EACJ,UACA,QAAQ,IACR,SAAS,OACP,GAAyB;EAC3B,SAAS;EACT,YAAY,GAAS,aAAa;EAClC;CACF,CAAC,GAEG,GACA,GACA;CACJ,AAAI,MACF,IAAyB,IACzB,IAAmB,IACnB,IAAoB;CAGtB,IAAI;CACJ,AAAI,GAAS,aAAa,cAAc,SACtC,IAA0B,GAAG,EAAQ,YAAY,WAAW;CAG9D,IAAM,EAAE,uBAAoB,oBAAiB,GAAwB;EACnE;EACA;EACA;EACA,SAAS,GAAS,aAAa,kBAAkB;EACjD,YAAY;CACd,CAAC,GAEK,KAAW,QACR,EAAqB;EAC1B;EACA;EACA;EACA;EACA;EACA;EACA,aAAa;EACb;EACA;EACA,iBAAiB;EACjB;EACA,aAAa;EACb,cAAc;EACd;EACA,wBAAwB;EACxB,kBAAkB;CACpB,CAAC,GACA;EACD;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;CACF,CAAC,GAEK,KAAiB,QACd,EAAqB;EAC1B;EACA;EACA;EACA;EACA;EACA;EACA,aAAa;EACb;EACA;EACA,iBAAiB;EACjB;EACA,aAAa;EACb,cAAc;EACd;EACA,wBAAwB;EACxB,kBAAkB;CACpB,CAAC,GACA;EACD;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;CACF,CAAC,GAEK,IAAgB,SAAkB;EAClC,MASJ,EAAgB,EAAI,IA0BpB,YAxB8C;GAC5C,IAAI;IACF,IAAM,IAAS,MAAM,EAAW,OAAO,KAAK;IAgB5C,AAfA,MAAM,IAAI,SAAe,GAAS,MAAW;KAC3C,GAA+B,GAAkB;MAC/C,UAAU,EAAO;MACjB,WAAW,CAAC;MACZ,mBAAmB;OACjB,EAAQ;MACV;MACA,UAAU,MAAU;OAClB,EAAO,CAAK;MACd;KACF,CAAC;IACH,CAAC,GACD,aAAa,WAAW,YAAY,GACpC,aAAa,WAAW,aAAa,GACrC,GAAgB,GAChB,GAAS,QAAQ,KAAK,EAAE,UAAU,GAAuB,CAAQ,EAAE,CAAC;GACtE,UAAU;IACR,EAAgB,EAAK;GACvB;EACF,EAEA,CAAW,CAAC,CAAC,YAAY,CAEzB,CAAC;CACH,GAAG;EAAC,EAAW;EAAQ;EAAU;EAAc;EAAkB;CAAO,CAAC,GAEnE,IAAS,GAAY,MAAM,MAC3B,IAAiB,QACd,GAA8B;EACnC;EACA,kBAAkB,EAAE,6BAA6B;CACnD,CAAC,GACA,CAAC,GAAG,CAAM,CAAC,GAER,KACJ,kBAAC,GAAD;EACE,WAAW;EACX,QAAQ;EACR,QAAQ;GACN,cAAc,EAAE,uBAAuB;GACvC,eAAe,EAAE,+BAA+B;GAChD,SAAS,EAAE,iCAAiC;EAC9C;EACA,WAAW;EACG;CACf,CAAA,GAGG,KACJ,kBAAC,GAAD;EACE,WAAW;EACX,QAAQ;EACR,QAAQ;GACN,cAAc,EAAE,uBAAuB;GACvC,eAAe,EAAE,+BAA+B;GAChD,SAAS,EAAE,iCAAiC;EAC9C;EACA,WAAW;EACG;CACf,CAAA,GAGC,IAAkC;CACtC,AAAI,KAAgB,SAClB,IACE,kBAAC,IAAD,EAA4B,YAAoC,CAAA;CAIpE,IAAM,IACJ,kBAAC,IAAD;EACE,OAAO;EACP,UAAU;EACV,aAAa,EAAE,4BAA4B;EAC3C,WAAW,EAAE,4BAA4B;CAC1C,CAAA;CAGH,OACE,kBAAC,GAAD,EAAA,UACE,kBAAC,IAAD;EACE,SAAS;GACP;GACA,QAAQ;GACR,QAAQ;GACR,aAAa;GACb,mBAAmB;GACnB,qBAAqB,EAAE,iCAAiC;GACxD,mBAAmB,EAAE,+BAA+B;GACpD,qBAAqB,EAAE,6BAA6B;EACtD;EACA,eAAe;GACb,UAAU;GACV,QAAQ;GACR,QAAQ;GACR,aAAa;GACb,oBAAoB;GACpB,qBAAqB,EAAE,6BAA6B;EACtD;EACA,QAAQ,EACN,YAAY,kBAAC,OAAD,EAAK,KAAK,EAAkB,CAAA,EAC1C;EACA,mBAAkB;EAElB,UAAA,kBAAC,IAAD;GAA4C;GAC1C,UAAA,kBAAC,GAAD;IACE,OAAO;KACL,QAAQ;KACR,eAAe;KACf,gBAAgB,EAAE,yBAAyB;IAC7C;IAEC,UAAA;GACoC,CAAA;EACV,CAAA;CACf,CAAA,EACL,CAAA;AAEnB,GAaM,KAAyB,EAC7B,aACA,qBACA,aACA,eACA,uBAC6C;CAC7C,IAAM,IAAc,EAAkB,GAAkB,CAAQ;CAEhE,OACE,kBAAC,GAAD;EACe;EACD;EACG;EAEd;CACoB,CAAA;AAE3B,GAEa,KAAwB,EACnC,aACA,qBACA,aACA,eACA,uBAEI,KAAoB,QAAQ,KAAY,OAExC,kBAAC,GAAD;CACoB;CACR;CACE;CACG;CAEd;AACoB,CAAA,IAKzB,kBAAC,GAAD;CACE,aAAa;CACD;CACG;CAEd;AACoB,CAAA"}
|
|
1
|
+
{"version":3,"file":"BackofficeLayoutPage.js","names":[],"sources":["../../../src/pages/BackofficeLayoutPage.tsx"],"sourcesContent":["import {\n useEffect,\n useMemo,\n type JSX,\n type ReactNode,\n useCallback,\n useContext,\n useState,\n} from 'react';\nimport { useTranslation } from 'react-i18next';\nimport {\n commitMutation,\n usePreloadedQuery,\n type PreloadedQuery,\n} from 'react-relay';\nimport type {\n GraphQLTaggedNode,\n MutationParameters,\n OperationType,\n} from 'relay-runtime';\nimport RoutingContext from '@plumile/router/routing/RoutingContext.js';\nimport useLocation from '@plumile/router/routing/useLocation.js';\n\nimport { AdminShellLayout } from '@plumile/ui/admin/templates/admin_shell_layout/AdminShellLayout.js';\nimport { ToastProvider } from '@plumile/ui/atomic/molecules/toast/ToastProvider.js';\nimport { GlobalSearchInput } from '@plumile/ui/backoffice/molecules/global_search_input/GlobalSearchInput.js';\nimport { SidebarProfileMenu } from '@plumile/ui/components/navigation/sidebar/SidebarProfileMenu.js';\n\nimport { useBackofficeReactTranslation } from '../i18n/useBackofficeReactTranslation.js';\nimport { useBackofficeConfig } from '../provider/BackofficeConfigContext.js';\nimport type { LogoutResponse, LogoutVariables } from '../hooks/useAuth.js';\nimport { useBackofficeSidebarPins } from '../hooks/useBackofficeSidebarPins.js';\nimport { useSidebarGroupCollapse } from '../hooks/useSidebarGroupCollapse.js';\nimport { buildSidebarSections } from '../components/backoffice/layout/buildSidebarSections.js';\nimport { BackofficeContentBoundary } from '../components/backoffice/routing/BackofficeContentBoundary.js';\nimport {\n resolveActiveEntityId,\n resolveSidebarGroups,\n resolveVisibleEntityIds,\n} from '../components/backoffice/layout/sidebarUtils.js';\nimport { BackofficeTopbarPortalContextProvider } from '../components/backoffice/layout/breadcrumb/BackofficeTopbarPortalContext.js';\nimport { BackofficePermissionsProvider } from '../components/backoffice/layout/BackofficePermissionsContext.js';\nimport {\n mapViewerToSidebarProfileView,\n type BackofficeViewerIdentity,\n} from '../components/backoffice/layout/mapViewerToSidebarProfileView.js';\nimport { resetRelayStore } from '../relay/environment.js';\nimport { useRelayEnvironment } from '../relay/useRelayEnvironment.js';\nimport { getBackofficeLoginPath } from '../router/backofficeAuthPaths.js';\nimport type { BackofficeSidebarRecentItem } from '../provider/types.js';\n\nexport type BackofficeLayoutPageProps = {\n children: ReactNode;\n permissionsQuery?: GraphQLTaggedNode;\n prepared?: PreloadedQuery<OperationType> | null;\n authStatus?: {\n isLoggedIn?: boolean | null;\n me?: BackofficeViewerIdentity | null;\n } | null;\n activeGroupId?: string | null;\n};\n\ntype LayoutShellProps = {\n children: ReactNode;\n permissions: unknown;\n authStatus?: {\n isLoggedIn?: boolean | null;\n me?: BackofficeViewerIdentity | null;\n } | null;\n activeGroupId?: string | null;\n};\n\nconst DEFAULT_RECENT_ITEMS_STORAGE_KEY = 'plumile:backoffice:recent-items';\nconst DEFAULT_SIDEBAR_PREFS_STORAGE_KEY = 'plumile:backoffice:sidebar';\n\nconst readRecentItems = (\n storageKey: string,\n): readonly BackofficeSidebarRecentItem[] => {\n if (typeof window === 'undefined') {\n return [];\n }\n try {\n const raw = window.localStorage.getItem(storageKey);\n if (raw == null) {\n return [];\n }\n const parsed = JSON.parse(raw) as unknown;\n if (!Array.isArray(parsed)) {\n return [];\n }\n return parsed.filter((item): item is BackofficeSidebarRecentItem => {\n if (item == null || typeof item !== 'object') {\n return false;\n }\n const candidate = item as Partial<BackofficeSidebarRecentItem>;\n return (\n (candidate.kind === 'entity' || candidate.kind === 'tool') &&\n typeof candidate.id === 'string' &&\n typeof candidate.label === 'string' &&\n typeof candidate.href === 'string' &&\n typeof candidate.visitedAt === 'number'\n );\n });\n } catch {\n return [];\n }\n};\n\nconst writeRecentItems = (\n storageKey: string,\n items: readonly BackofficeSidebarRecentItem[],\n): void => {\n if (typeof window === 'undefined') {\n return;\n }\n try {\n window.localStorage.setItem(storageKey, JSON.stringify(items));\n } catch {\n // Ignore storage quota / privacy mode failures.\n }\n};\n\nconst BackofficeLayoutShell = ({\n children,\n permissions,\n authStatus,\n activeGroupId,\n}: LayoutShellProps): JSX.Element => {\n const { t: tApp } = useTranslation();\n const { t } = useBackofficeReactTranslation();\n const { pathname } = useLocation();\n const routing = useContext(RoutingContext);\n const relayEnvironment = useRelayEnvironment();\n const {\n auth: authConfig,\n basePath,\n dashboards,\n entities,\n sidebar,\n } = useBackofficeConfig();\n const [sidebarQuery, setSidebarQuery] = useState('');\n const sidebarPreferencesStorageKey =\n sidebar?.preferences?.storageKey ?? DEFAULT_SIDEBAR_PREFS_STORAGE_KEY;\n const persistSidebarCollapsed =\n sidebar?.preferences?.persistCollapsed === true;\n const [isSidebarCollapsed, setIsSidebarCollapsed] = useState(() => {\n if (!persistSidebarCollapsed || typeof window === 'undefined') {\n return false;\n }\n try {\n return (\n window.localStorage.getItem(\n `${sidebarPreferencesStorageKey}:collapsed`,\n ) === 'true'\n );\n } catch {\n return false;\n }\n });\n const [isSigningOut, setIsSigningOut] = useState(false);\n const [topbarTarget, setTopbarTarget] = useState<HTMLDivElement | null>(null);\n const recentItemsConfig = sidebar?.recentItems;\n const recentItemsEnabled = recentItemsConfig?.enabled === true;\n const recentItemsStorageKey =\n recentItemsConfig?.storageKey ?? DEFAULT_RECENT_ITEMS_STORAGE_KEY;\n const recentItemsMaxItems = recentItemsConfig?.maxItems ?? 8;\n const [recentItems, setRecentItems] = useState<\n readonly BackofficeSidebarRecentItem[]\n >(() => {\n if (!recentItemsEnabled) {\n return [];\n }\n return readRecentItems(recentItemsStorageKey);\n });\n\n useEffect(() => {\n if (!persistSidebarCollapsed || typeof window === 'undefined') {\n return;\n }\n try {\n window.localStorage.setItem(\n `${sidebarPreferencesStorageKey}:collapsed`,\n String(isSidebarCollapsed),\n );\n } catch {\n // Ignore storage quota / privacy mode failures.\n }\n }, [\n isSidebarCollapsed,\n persistSidebarCollapsed,\n sidebarPreferencesStorageKey,\n ]);\n\n const groups = useMemo(() => {\n return resolveSidebarGroups(entities, sidebar);\n }, [entities, sidebar]);\n\n const groupIds = useMemo(() => {\n return Object.keys(groups);\n }, [groups]);\n\n const defaultCollapsedByGroupId = useMemo(() => {\n return Object.fromEntries(\n Object.entries(groups).map(([groupId, group]) => {\n return [groupId, group.behavior?.defaultCollapsed ?? true];\n }),\n );\n }, [groups]);\n\n const visibleEntityIds = useMemo(() => {\n return resolveVisibleEntityIds(groups, entities, sidebar, permissions);\n }, [entities, groups, permissions, sidebar]);\n\n const activeEntityId = useMemo(() => {\n return resolveActiveEntityId(pathname, entities);\n }, [entities, pathname]);\n\n useEffect(() => {\n if (!recentItemsEnabled || activeEntityId == null) {\n return;\n }\n const config = entities[activeEntityId];\n if (config == null) {\n return;\n }\n if (config.kind !== 'tool' && !config.hasList) {\n return;\n }\n const href = config.routes.list;\n let kind: BackofficeSidebarRecentItem['kind'] = 'entity';\n if (config.kind === 'tool') {\n kind = 'tool';\n }\n const item: BackofficeSidebarRecentItem = {\n kind,\n id: activeEntityId,\n label: config.label(tApp),\n href,\n visitedAt: Date.now(),\n };\n setRecentItems((prev) => {\n const next = [\n item,\n ...prev.filter((entry) => {\n return entry.id !== item.id || entry.kind !== item.kind;\n }),\n ].slice(0, recentItemsMaxItems);\n writeRecentItems(recentItemsStorageKey, next);\n return next;\n });\n }, [\n activeEntityId,\n entities,\n recentItemsEnabled,\n recentItemsMaxItems,\n recentItemsStorageKey,\n tApp,\n ]);\n\n const pinningEnabled = sidebar?.pinnedItems?.enabled === true;\n\n const {\n pins,\n toggle: togglePin,\n reorder: reorderPin,\n } = useBackofficeSidebarPins({\n enabled: pinningEnabled,\n storageKey: sidebar?.pinnedItems?.storageKey,\n visibleEntityIds,\n });\n\n let sidebarPinnedEntityIds: readonly string[] | undefined;\n let sidebarTogglePin: ((entityId: string) => void) | undefined;\n let sidebarReorderPin: ((fromId: string, toId: string) => void) | undefined;\n if (pinningEnabled) {\n sidebarPinnedEntityIds = pins;\n sidebarTogglePin = togglePin;\n sidebarReorderPin = reorderPin;\n }\n\n let groupCollapseStorageKey: string | undefined;\n if (sidebar?.preferences?.storageKey != null) {\n groupCollapseStorageKey = `${sidebar.preferences.storageKey}:groups`;\n }\n\n const { collapsedByGroupId, setCollapsed } = useSidebarGroupCollapse({\n groupIds,\n activeGroupId,\n defaultCollapsedByGroupId,\n persist: sidebar?.preferences?.persistGroups === true,\n storageKey: groupCollapseStorageKey,\n });\n\n const sections = useMemo(() => {\n return buildSidebarSections({\n basePath,\n pathname,\n entities,\n dashboards,\n sidebar,\n permissions,\n searchQuery: sidebarQuery,\n tApp,\n t,\n pinnedEntityIds: sidebarPinnedEntityIds,\n recentItems,\n onTogglePin: sidebarTogglePin,\n onReorderPin: sidebarReorderPin,\n collapsedByGroupId,\n onGroupCollapsedChange: setCollapsed,\n sidebarCollapsed: false,\n });\n }, [\n basePath,\n collapsedByGroupId,\n entities,\n dashboards,\n pathname,\n permissions,\n recentItems,\n setCollapsed,\n sidebar,\n sidebarPinnedEntityIds,\n sidebarQuery,\n sidebarReorderPin,\n sidebarTogglePin,\n t,\n tApp,\n ]);\n\n const mobileSections = useMemo(() => {\n return buildSidebarSections({\n basePath,\n pathname,\n entities,\n dashboards,\n sidebar,\n permissions,\n searchQuery: sidebarQuery,\n tApp,\n t,\n pinnedEntityIds: sidebarPinnedEntityIds,\n recentItems,\n onTogglePin: sidebarTogglePin,\n onReorderPin: sidebarReorderPin,\n collapsedByGroupId,\n onGroupCollapsedChange: setCollapsed,\n sidebarCollapsed: false,\n });\n }, [\n basePath,\n collapsedByGroupId,\n entities,\n dashboards,\n pathname,\n permissions,\n recentItems,\n setCollapsed,\n sidebar,\n sidebarPinnedEntityIds,\n sidebarQuery,\n sidebarReorderPin,\n sidebarTogglePin,\n t,\n tApp,\n ]);\n\n const handleSignOut = useCallback(() => {\n if (isSigningOut) {\n return;\n }\n\n type LogoutMutation = MutationParameters & {\n response: LogoutResponse;\n variables: LogoutVariables;\n };\n\n setIsSigningOut(true);\n\n const runSignOut = async (): Promise<void> => {\n try {\n const config = await authConfig.logout.load();\n await new Promise<void>((resolve, reject) => {\n commitMutation<LogoutMutation>(relayEnvironment, {\n mutation: config.logoutMutation,\n variables: {},\n onCompleted: () => {\n resolve();\n },\n onError: (error) => {\n reject(error);\n },\n });\n });\n localStorage.removeItem('auth_token');\n localStorage.removeItem('remember_me');\n resetRelayStore();\n routing?.history.push({ pathname: getBackofficeLoginPath(basePath) });\n } finally {\n setIsSigningOut(false);\n }\n };\n\n runSignOut().catch(() => {\n /* noop */\n });\n }, [authConfig.logout, basePath, isSigningOut, relayEnvironment, routing]);\n\n const viewer = authStatus?.me ?? null;\n const sidebarProfile = useMemo(() => {\n return mapViewerToSidebarProfileView({\n viewer,\n unknownUserLabel: t('sidebar.profile.unknownUser'),\n });\n }, [t, viewer]);\n\n const sidebarFooter = (\n <SidebarProfileMenu\n collapsed={false}\n viewer={sidebarProfile}\n labels={{\n sectionTitle: t('sidebar.profile.title'),\n menuAriaLabel: t('sidebar.profile.menuAriaLabel'),\n signOut: t('sidebar.profile.actions.signOut'),\n }}\n onSignOut={handleSignOut}\n isSigningOut={isSigningOut}\n />\n );\n\n const mobileSidebarFooter = (\n <SidebarProfileMenu\n collapsed={false}\n viewer={sidebarProfile}\n labels={{\n sectionTitle: t('sidebar.profile.title'),\n menuAriaLabel: t('sidebar.profile.menuAriaLabel'),\n signOut: t('sidebar.profile.actions.signOut'),\n }}\n onSignOut={handleSignOut}\n isSigningOut={isSigningOut}\n />\n );\n\n let contentNode: JSX.Element | null = null;\n if (topbarTarget != null) {\n contentNode = (\n <BackofficeContentBoundary>{children}</BackofficeContentBoundary>\n );\n }\n\n const sidebarSearchNode = (\n <GlobalSearchInput\n value={sidebarQuery}\n onChange={setSidebarQuery}\n placeholder={t('sidebar.search.placeholder')}\n ariaLabel={t('sidebar.search.placeholder')}\n />\n );\n\n return (\n <ToastProvider>\n <AdminShellLayout\n sidebar={{\n sections,\n search: sidebarSearchNode,\n footer: sidebarFooter,\n isCollapsed: isSidebarCollapsed,\n onCollapsedChange: setIsSidebarCollapsed,\n collapseToggleLabel: t('sidebar.actions.collapseSidebar'),\n expandToggleLabel: t('sidebar.actions.expandSidebar'),\n navigationAriaLabel: t('sidebar.navigationAriaLabel'),\n }}\n mobileSidebar={{\n sections: mobileSections,\n search: sidebarSearchNode,\n footer: mobileSidebarFooter,\n isCollapsed: false,\n hideCollapseToggle: true,\n navigationAriaLabel: t('sidebar.navigationAriaLabel'),\n }}\n topbar={{\n breadcrumb: <div ref={setTopbarTarget} />,\n }}\n contentScrollMode=\"contained\"\n >\n <BackofficePermissionsProvider permissions={permissions}>\n <BackofficeTopbarPortalContextProvider\n value={{\n target: topbarTarget,\n dashboardHref: basePath,\n dashboardLabel: t('sidebar.items.dashboard'),\n }}\n >\n {contentNode}\n </BackofficeTopbarPortalContextProvider>\n </BackofficePermissionsProvider>\n </AdminShellLayout>\n </ToastProvider>\n );\n};\n\ntype LayoutWithPermissionsProps = {\n children: ReactNode;\n permissionsQuery: GraphQLTaggedNode;\n prepared: PreloadedQuery<OperationType>;\n authStatus?: {\n isLoggedIn?: boolean | null;\n me?: BackofficeViewerIdentity | null;\n } | null;\n activeGroupId?: string | null;\n};\n\nconst LayoutWithPermissions = ({\n children,\n permissionsQuery,\n prepared,\n authStatus,\n activeGroupId,\n}: LayoutWithPermissionsProps): JSX.Element => {\n const permissions = usePreloadedQuery(permissionsQuery, prepared);\n\n return (\n <BackofficeLayoutShell\n permissions={permissions}\n authStatus={authStatus}\n activeGroupId={activeGroupId}\n >\n {children}\n </BackofficeLayoutShell>\n );\n};\n\nexport const BackofficeLayoutPage = ({\n children,\n permissionsQuery,\n prepared,\n authStatus,\n activeGroupId,\n}: BackofficeLayoutPageProps): JSX.Element => {\n if (permissionsQuery != null && prepared != null) {\n return (\n <LayoutWithPermissions\n permissionsQuery={permissionsQuery}\n prepared={prepared}\n authStatus={authStatus}\n activeGroupId={activeGroupId}\n >\n {children}\n </LayoutWithPermissions>\n );\n }\n\n return (\n <BackofficeLayoutShell\n permissions={null}\n authStatus={authStatus}\n activeGroupId={activeGroupId}\n >\n {children}\n </BackofficeLayoutShell>\n );\n};\n\nexport default BackofficeLayoutPage;\n"],"mappings":"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"}
|
|
@@ -1,17 +1,17 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import {
|
|
3
|
-
import {
|
|
4
|
-
import {
|
|
5
|
-
import {
|
|
6
|
-
import {
|
|
7
|
-
import {
|
|
1
|
+
import { getBackofficePasswordResetPath as e } from "../router/backofficeAuthPaths.js";
|
|
2
|
+
import { LoginFlow as t } from "../auth/login/LoginFlow.js";
|
|
3
|
+
import { synchronizeAuthStatusQuery as n } from "../auth/login/synchronizeAuthStatusQuery.js";
|
|
4
|
+
import { useBackofficeConfig as r } from "../provider/BackofficeConfigContext.js";
|
|
5
|
+
import { useRelayEnvironment as i } from "../relay/useRelayEnvironment.js";
|
|
6
|
+
import { useBackofficeAuth as a } from "../hooks/useBackofficeAuth.js";
|
|
7
|
+
import { useBackofficeAuthLoginConfig as o } from "../provider/useBackofficeLazyValue.js";
|
|
8
8
|
import { useCallback as s, useContext as c, useRef as l } from "react";
|
|
9
9
|
import { jsx as u } from "react/jsx-runtime";
|
|
10
10
|
import * as d from "react-relay";
|
|
11
11
|
import f from "@plumile/router/routing/RoutingContext.js";
|
|
12
12
|
//#region src/pages/BackofficeLoginPage.tsx
|
|
13
13
|
var { usePreloadedQuery: p } = d, m = ({ prepared: d }) => {
|
|
14
|
-
let m = c(f), { auth: h, basePath: g } =
|
|
14
|
+
let m = c(f), { auth: h, basePath: g } = r(), _ = o(), v = a(), y = i(), b = l(!1), x = p(_.loginQuery, d.query).oidcProviders ?? [], S = s(() => {
|
|
15
15
|
b.current || (b.current = !0, (async () => {
|
|
16
16
|
try {
|
|
17
17
|
let e = await h.session.load();
|
|
@@ -19,7 +19,7 @@ var { usePreloadedQuery: p } = d, m = ({ prepared: d }) => {
|
|
|
19
19
|
m?.history.push({ pathname: g });
|
|
20
20
|
return;
|
|
21
21
|
}
|
|
22
|
-
if (await h.lifecycle?.onLoginSuccess?.(), !await
|
|
22
|
+
if (await h.lifecycle?.onLoginSuccess?.(), !await n(y, e.authStatusQuery)) return;
|
|
23
23
|
m?.history.push({ pathname: g });
|
|
24
24
|
} catch {} finally {
|
|
25
25
|
b.current = !1;
|
|
@@ -32,9 +32,9 @@ var { usePreloadedQuery: p } = d, m = ({ prepared: d }) => {
|
|
|
32
32
|
y,
|
|
33
33
|
m?.history
|
|
34
34
|
]), C = s(() => {
|
|
35
|
-
m?.history.push({ pathname:
|
|
35
|
+
m?.history.push({ pathname: e(g) });
|
|
36
36
|
}, [g, m?.history]);
|
|
37
|
-
return /* @__PURE__ */ u(
|
|
37
|
+
return /* @__PURE__ */ u(t, {
|
|
38
38
|
auth: v,
|
|
39
39
|
oidcProviders: x,
|
|
40
40
|
onLoginSuccess: S,
|