slice-machine-ui 2.18.1-alpha.jp-unauthorized-error.3 → 2.18.1-alpha.jp-unauthorized-error-improvement.1
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/out/404.html +1 -1
- package/out/_next/static/{6PcBvU_g2QBnuMafU4iYu → bWd9YPY0k1NsrjJCNbscH}/_buildManifest.js +1 -1
- package/out/_next/static/chunks/{248-bdbfde18c5a04eae.js → 248-bcf03aa3c62e7dfb.js} +1 -1
- package/out/_next/static/chunks/34-50c64778da33cff6.js +1 -0
- package/out/_next/static/chunks/422-c9192a1dbdd2ae0e.js +1 -0
- package/out/_next/static/chunks/429-1137c819c2bf6b66.js +3 -0
- package/out/_next/static/chunks/{489-6edb99e269996dd1.js → 489-77d1fe67b6d0f0f8.js} +1 -1
- package/out/_next/static/chunks/630-c34de0401b8a0375.js +1 -0
- package/out/_next/static/chunks/pages/{_app-2fd7ba7c1a132cb4.js → _app-d298cdfd4509fb06.js} +40 -40
- package/out/_next/static/chunks/pages/changelog-3901f2fc937d9648.js +1 -0
- package/out/_next/static/chunks/pages/changes-62d4e18795217cba.js +1 -0
- package/out/_next/static/chunks/pages/custom-types/{[customTypeId]-7102c23f96cd1768.js → [customTypeId]-816acb31b652239b.js} +1 -1
- package/out/_next/static/chunks/pages/{labs-d79597003a1ff74e.js → labs-5f5c63fb1f7d4b92.js} +1 -1
- package/out/_next/static/chunks/pages/page-types/{[pageTypeId]-d4bc920a5efffa0a.js → [pageTypeId]-669d5479e81b638b.js} +1 -1
- package/out/_next/static/chunks/pages/slices/[lib]/[sliceName]/[variation]/simulator-eca10940c9083d4c.js +1 -0
- package/out/_next/static/chunks/pages/slices/[lib]/[sliceName]/[variation]-b4700a6332ea828c.js +1 -0
- package/out/_next/static/chunks/pages/slices-bf63f937b184b470.js +1 -0
- package/out/changelog.html +1 -1
- package/out/changes.html +1 -1
- package/out/custom-types/[customTypeId].html +1 -1
- package/out/custom-types.html +1 -1
- package/out/index.html +1 -1
- package/out/labs.html +1 -1
- package/out/page-types/[pageTypeId].html +1 -1
- package/out/slices/[lib]/[sliceName]/[variation]/simulator.html +1 -1
- package/out/slices/[lib]/[sliceName]/[variation].html +1 -1
- package/out/slices.html +1 -1
- package/package.json +3 -6
- package/src/errorBoundaries.tsx +150 -0
- package/src/features/auth/LogoutButton.tsx +42 -36
- package/src/features/builder/fields/contentRelationship/ContentRelationshipFieldPicker.tsx +3 -3
- package/src/features/changes/StatusBadge.tsx +1 -9
- package/src/features/customTypes/customTypesBuilder/PageSnippetDialog/PageSnippetDialog.tsx +3 -3
- package/src/features/customTypes/customTypesTable/CustomTypesTablePage.tsx +3 -3
- package/src/features/environments/actions/useSetEnvironment.ts +22 -0
- package/src/features/environments/useActiveEnvironment.ts +17 -9
- package/src/features/environments/useEnvironments.ts +11 -8
- package/src/features/labs/labsList/LabsPage.tsx +3 -3
- package/src/features/navigation/Navigation.tsx +31 -8
- package/src/features/navigation/NavigationItem.tsx +9 -3
- package/src/features/navigation/SliceMachineVersion.tsx +1 -6
- package/src/features/slices/sliceBuilder/FloatingBackButton.tsx +3 -3
- package/src/features/slices/sliceBuilder/McpPromoLink.tsx +29 -0
- package/src/features/sync/actions/pushChanges.ts +1 -0
- package/src/features/sync/getUnSyncChanges.ts +23 -7
- package/src/legacy/components/AppLayout/index.tsx +10 -85
- package/src/legacy/components/ChangesEmptyState/UnauthenticatedView.tsx +31 -0
- package/src/legacy/components/ChangesEmptyState/index.ts +1 -1
- package/src/legacy/components/ChangesItems/ChangesItems.tsx +3 -3
- package/src/legacy/components/LoginModal/index.tsx +13 -5
- package/src/legacy/components/Navigation/ChangesItem.tsx +2 -6
- package/src/legacy/components/Navigation/Environment.tsx +2 -7
- package/src/legacy/components/Navigation/SideNavEnvironmentSelector/SideNavEnvironmentSelector.tsx +3 -8
- package/src/legacy/components/Simulator/index.tsx +3 -3
- package/src/legacy/lib/builders/CustomTypeBuilder/TabZone/index.tsx +3 -3
- package/src/legacy/lib/builders/SliceBuilder/index.tsx +2 -0
- package/src/legacy/lib/models/common/ModelStatus/index.ts +6 -1
- package/src/modules/userContext/index.ts +6 -3
- package/src/modules/userContext/types.ts +1 -1
- package/src/pages/_app.tsx +88 -95
- package/src/pages/changes.tsx +4 -5
- package/src/queryClient.tsx +24 -0
- package/test/__testutils__/index.tsx +13 -10
- package/out/_next/static/chunks/157-54b8336d20b41933.js +0 -3
- package/out/_next/static/chunks/34-8d9d9b2944824750.js +0 -1
- package/out/_next/static/chunks/385-b949173dfa03dd3e.js +0 -1
- package/out/_next/static/chunks/630-bb6e3db525588f16.js +0 -1
- package/out/_next/static/chunks/pages/changelog-21b960abba5abf71.js +0 -1
- package/out/_next/static/chunks/pages/changes-7919746009a45ad8.js +0 -1
- package/out/_next/static/chunks/pages/slices/[lib]/[sliceName]/[variation]/simulator-b127d948a17968d3.js +0 -1
- package/out/_next/static/chunks/pages/slices/[lib]/[sliceName]/[variation]-98f85d5fb8d5c704.js +0 -1
- package/out/_next/static/chunks/pages/slices-046e5e978ffc3a42.js +0 -1
- package/src/ErrorBoundary.tsx +0 -47
- package/src/features/environments/actions/setEnvironment.ts +0 -18
- package/src/legacy/components/ChangesEmptyState/AuthErrorPage.tsx +0 -44
- /package/out/_next/static/{6PcBvU_g2QBnuMafU4iYu → bWd9YPY0k1NsrjJCNbscH}/_ssgManifest.js +0 -0
|
@@ -0,0 +1,150 @@
|
|
|
1
|
+
import {
|
|
2
|
+
BlankSlate,
|
|
3
|
+
BlankSlateDescription,
|
|
4
|
+
BlankSlateIcon,
|
|
5
|
+
BlankSlateTitle,
|
|
6
|
+
Box,
|
|
7
|
+
ErrorBoundary as EditorUiErrorBoundary,
|
|
8
|
+
Text,
|
|
9
|
+
} from "@prismicio/editor-ui";
|
|
10
|
+
import {
|
|
11
|
+
isInvalidActiveEnvironmentError,
|
|
12
|
+
isUnauthenticatedError,
|
|
13
|
+
isUnauthorizedError,
|
|
14
|
+
} from "@slicemachine/manager/client";
|
|
15
|
+
import Link from "next/link";
|
|
16
|
+
import {
|
|
17
|
+
type ComponentPropsWithoutRef,
|
|
18
|
+
type FC,
|
|
19
|
+
PropsWithChildren,
|
|
20
|
+
useCallback,
|
|
21
|
+
useRef,
|
|
22
|
+
} from "react";
|
|
23
|
+
|
|
24
|
+
import { ReloadLogoutButton } from "@/features/auth/LogoutButton";
|
|
25
|
+
import { useAuthStatus } from "@/hooks/useAuthStatus";
|
|
26
|
+
|
|
27
|
+
type DefaultErrorBoundaryProps = Pick<
|
|
28
|
+
// TODO(DT-1979): Export the `ErrorBoundaryProps` type from `@prismicio/editor-ui`.
|
|
29
|
+
ComponentPropsWithoutRef<typeof EditorUiErrorBoundary>,
|
|
30
|
+
"children" | "renderError"
|
|
31
|
+
>;
|
|
32
|
+
|
|
33
|
+
export const DefaultErrorBoundary: FC<DefaultErrorBoundaryProps> = (props) => {
|
|
34
|
+
const errorRef = useRef<unknown>();
|
|
35
|
+
const authStatus = useAuthStatus();
|
|
36
|
+
return (
|
|
37
|
+
<EditorUiErrorBoundary
|
|
38
|
+
{...props}
|
|
39
|
+
onError={(error) => {
|
|
40
|
+
errorRef.current = error;
|
|
41
|
+
}}
|
|
42
|
+
ref={useCallback(
|
|
43
|
+
(errorBoundary: EditorUiErrorBoundary | null) => {
|
|
44
|
+
if (errorBoundary === null) return;
|
|
45
|
+
const error = errorRef.current;
|
|
46
|
+
if (isUnauthenticatedError(error) || isUnauthorizedError(error)) {
|
|
47
|
+
errorRef.current = undefined;
|
|
48
|
+
errorBoundary.reset();
|
|
49
|
+
}
|
|
50
|
+
},
|
|
51
|
+
/* eslint-disable-next-line react-hooks/exhaustive-deps --
|
|
52
|
+
* Whenever `authStatus` changes, we want to `reset` the `errorBoundary`
|
|
53
|
+
* if an authentication or authorization error has been caught.
|
|
54
|
+
**/
|
|
55
|
+
[authStatus],
|
|
56
|
+
)}
|
|
57
|
+
/>
|
|
58
|
+
);
|
|
59
|
+
};
|
|
60
|
+
|
|
61
|
+
export function AppStateErrorBoundary(props: PropsWithChildren) {
|
|
62
|
+
return (
|
|
63
|
+
<EditorUiErrorBoundary
|
|
64
|
+
renderError={(error) => {
|
|
65
|
+
return (
|
|
66
|
+
<Box
|
|
67
|
+
position="absolute"
|
|
68
|
+
top={64}
|
|
69
|
+
width="100%"
|
|
70
|
+
justifyContent="center"
|
|
71
|
+
flexDirection="column"
|
|
72
|
+
>
|
|
73
|
+
<BlankSlate>
|
|
74
|
+
<BlankSlateIcon
|
|
75
|
+
lineColor="tomato11"
|
|
76
|
+
backgroundColor="tomato3"
|
|
77
|
+
name="alert"
|
|
78
|
+
/>
|
|
79
|
+
<BlankSlateTitle>Failed to load Slice Machine</BlankSlateTitle>
|
|
80
|
+
<RenderErrorDescription error={error} />
|
|
81
|
+
</BlankSlate>
|
|
82
|
+
</Box>
|
|
83
|
+
);
|
|
84
|
+
}}
|
|
85
|
+
>
|
|
86
|
+
{props.children}
|
|
87
|
+
</EditorUiErrorBoundary>
|
|
88
|
+
);
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
function RenderErrorDescription(args: { error: unknown }) {
|
|
92
|
+
const { error } = args;
|
|
93
|
+
|
|
94
|
+
if (isUnauthorizedError(error)) {
|
|
95
|
+
return (
|
|
96
|
+
<CommonErrorBox>
|
|
97
|
+
<Box flexDirection="column" gap={8} alignItems="center">
|
|
98
|
+
<Text variant="h3" align="center">
|
|
99
|
+
You don't have access to this repository.
|
|
100
|
+
</Text>
|
|
101
|
+
<Text align="center">
|
|
102
|
+
Check that the repository name is correct, then contact your
|
|
103
|
+
repository administrator.
|
|
104
|
+
</Text>
|
|
105
|
+
</Box>
|
|
106
|
+
<ReloadLogoutButton sx={{ alignSelf: "center" }} />
|
|
107
|
+
</CommonErrorBox>
|
|
108
|
+
);
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
if (isInvalidActiveEnvironmentError(error)) {
|
|
112
|
+
return (
|
|
113
|
+
<CommonErrorBox>
|
|
114
|
+
<Box flexDirection="column" gap={8} alignItems="center">
|
|
115
|
+
<Text variant="h3" align="center">
|
|
116
|
+
Your current environment is invalid.
|
|
117
|
+
</Text>
|
|
118
|
+
<Text align="center">
|
|
119
|
+
Check with your repository administrator that you have permissions
|
|
120
|
+
and correctly configured your environment for the current
|
|
121
|
+
repository. For more details, consult the{" "}
|
|
122
|
+
<Link href="https://prismic.io/docs/environments" target="_blank">
|
|
123
|
+
documentation
|
|
124
|
+
</Link>
|
|
125
|
+
.
|
|
126
|
+
</Text>
|
|
127
|
+
</Box>
|
|
128
|
+
<ReloadLogoutButton sx={{ alignSelf: "center" }} />
|
|
129
|
+
</CommonErrorBox>
|
|
130
|
+
);
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
return <BlankSlateDescription>{JSON.stringify(error)}</BlankSlateDescription>;
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
function CommonErrorBox(args: { children: React.ReactNode }) {
|
|
137
|
+
const { children } = args;
|
|
138
|
+
|
|
139
|
+
return (
|
|
140
|
+
<Box
|
|
141
|
+
flexDirection="column"
|
|
142
|
+
alignItems="center"
|
|
143
|
+
gap={16}
|
|
144
|
+
margin={{ top: 8 }}
|
|
145
|
+
maxWidth={768}
|
|
146
|
+
>
|
|
147
|
+
{children}
|
|
148
|
+
</Box>
|
|
149
|
+
);
|
|
150
|
+
}
|
|
@@ -1,52 +1,34 @@
|
|
|
1
1
|
import { Button, Icon, IconButton, Tooltip } from "@prismicio/editor-ui";
|
|
2
2
|
import { SX } from "@prismicio/editor-ui/dist/theme";
|
|
3
3
|
import * as Sentry from "@sentry/nextjs";
|
|
4
|
-
import {
|
|
4
|
+
import { useQueryClient } from "@tanstack/react-query";
|
|
5
|
+
import { useRouter } from "next/router";
|
|
6
|
+
import { toast } from "react-toastify";
|
|
5
7
|
|
|
6
|
-
import { clearAuth
|
|
7
|
-
import {
|
|
8
|
-
import {
|
|
8
|
+
import { clearAuth, getState } from "@/apiClient";
|
|
9
|
+
import { GetActiveEnvironmentQueryKey } from "@/features/environments/useActiveEnvironment";
|
|
10
|
+
import { GetEnvironmentsQueryKey } from "@/features/environments/useEnvironments";
|
|
9
11
|
import useSliceMachineActions from "@/modules/useSliceMachineActions";
|
|
10
12
|
|
|
11
|
-
|
|
12
|
-
children?: ReactNode;
|
|
13
|
-
onLogoutSuccess?: () => void;
|
|
14
|
-
isLoading?: boolean;
|
|
15
|
-
sx?: SX;
|
|
16
|
-
}
|
|
17
|
-
|
|
18
|
-
export function LogoutButton(props: LogoutButtonProps) {
|
|
19
|
-
const { children, onLogoutSuccess, isLoading, sx } = props;
|
|
20
|
-
|
|
13
|
+
export function EnvironmentLogoutButton() {
|
|
21
14
|
const { refreshState } = useSliceMachineActions();
|
|
15
|
+
const queryClient = useQueryClient();
|
|
22
16
|
|
|
23
17
|
async function onClick() {
|
|
24
|
-
await
|
|
18
|
+
await clearAuth();
|
|
25
19
|
|
|
26
20
|
const serverState = await getState();
|
|
27
21
|
refreshState(serverState);
|
|
28
|
-
|
|
29
22
|
Sentry.setUser({ id: serverState.env.shortId });
|
|
30
23
|
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
24
|
+
await Promise.all([
|
|
25
|
+
queryClient.invalidateQueries({ queryKey: GetEnvironmentsQueryKey }),
|
|
26
|
+
queryClient.invalidateQueries({
|
|
27
|
+
queryKey: GetActiveEnvironmentQueryKey,
|
|
28
|
+
}),
|
|
29
|
+
]);
|
|
34
30
|
|
|
35
|
-
|
|
36
|
-
}
|
|
37
|
-
|
|
38
|
-
if (children !== undefined) {
|
|
39
|
-
return (
|
|
40
|
-
<Button
|
|
41
|
-
onClick={() => void onClick()}
|
|
42
|
-
renderEndIcon={() => <Icon name="logout" size="extraSmall" />}
|
|
43
|
-
color="grey"
|
|
44
|
-
loading={isLoading}
|
|
45
|
-
sx={sx}
|
|
46
|
-
>
|
|
47
|
-
{children}
|
|
48
|
-
</Button>
|
|
49
|
-
);
|
|
31
|
+
toast.success("Logged out");
|
|
50
32
|
}
|
|
51
33
|
|
|
52
34
|
return (
|
|
@@ -55,9 +37,33 @@ export function LogoutButton(props: LogoutButtonProps) {
|
|
|
55
37
|
icon="logout"
|
|
56
38
|
onClick={() => void onClick()}
|
|
57
39
|
hiddenLabel="Log out"
|
|
58
|
-
loading={isLoading}
|
|
59
|
-
sx={sx}
|
|
60
40
|
/>
|
|
61
41
|
</Tooltip>
|
|
62
42
|
);
|
|
63
43
|
}
|
|
44
|
+
|
|
45
|
+
interface ReloadLogoutButtonProps {
|
|
46
|
+
sx?: SX;
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
export function ReloadLogoutButton(props: ReloadLogoutButtonProps) {
|
|
50
|
+
const { sx } = props;
|
|
51
|
+
const router = useRouter();
|
|
52
|
+
|
|
53
|
+
async function onClick() {
|
|
54
|
+
await clearAuth();
|
|
55
|
+
Sentry.setUser({ id: undefined });
|
|
56
|
+
router.reload();
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
return (
|
|
60
|
+
<Button
|
|
61
|
+
onClick={() => void onClick()}
|
|
62
|
+
renderEndIcon={() => <Icon name="logout" size="extraSmall" />}
|
|
63
|
+
color="grey"
|
|
64
|
+
sx={sx}
|
|
65
|
+
>
|
|
66
|
+
Log out
|
|
67
|
+
</Button>
|
|
68
|
+
);
|
|
69
|
+
}
|
|
@@ -30,7 +30,7 @@ import {
|
|
|
30
30
|
} from "@prismicio/types-internal/lib/customtypes";
|
|
31
31
|
import { useEffect } from "react";
|
|
32
32
|
|
|
33
|
-
import {
|
|
33
|
+
import { DefaultErrorBoundary } from "@/errorBoundaries";
|
|
34
34
|
import {
|
|
35
35
|
revalidateGetCustomTypes,
|
|
36
36
|
useCustomTypes as useCustomTypesRequest,
|
|
@@ -226,7 +226,7 @@ export function ContentRelationshipFieldPicker(
|
|
|
226
226
|
props: ContentRelationshipFieldPickerProps,
|
|
227
227
|
) {
|
|
228
228
|
return (
|
|
229
|
-
<
|
|
229
|
+
<DefaultErrorBoundary
|
|
230
230
|
renderError={() => (
|
|
231
231
|
<Box alignItems="center" gap={8}>
|
|
232
232
|
<Icon name="alert" size="small" color="tomato10" />
|
|
@@ -254,7 +254,7 @@ export function ContentRelationshipFieldPicker(
|
|
|
254
254
|
>
|
|
255
255
|
<ContentRelationshipFieldPickerContent {...props} />
|
|
256
256
|
</AnimatedSuspense>
|
|
257
|
-
</
|
|
257
|
+
</DefaultErrorBoundary>
|
|
258
258
|
);
|
|
259
259
|
}
|
|
260
260
|
|
|
@@ -73,7 +73,7 @@ function getStatusBadgeContent(
|
|
|
73
73
|
"Data from the remote repository could not be fetched (no internet connection).",
|
|
74
74
|
};
|
|
75
75
|
}
|
|
76
|
-
if (args.authStatus === AuthStatus.
|
|
76
|
+
if (args.authStatus === AuthStatus.UNAUTHENTICATED) {
|
|
77
77
|
return {
|
|
78
78
|
badgeColor: "grey",
|
|
79
79
|
badgeTitle: "Unknown",
|
|
@@ -81,14 +81,6 @@ function getStatusBadgeContent(
|
|
|
81
81
|
"Data from the remote repository could not be fetched (not connected to Prismic).",
|
|
82
82
|
};
|
|
83
83
|
}
|
|
84
|
-
if (args.authStatus === AuthStatus.FORBIDDEN) {
|
|
85
|
-
return {
|
|
86
|
-
badgeColor: "grey",
|
|
87
|
-
badgeTitle: "Unknown",
|
|
88
|
-
tooltipContent:
|
|
89
|
-
"Data from the remote repository could not be fetched (you don't have access to the repository).",
|
|
90
|
-
};
|
|
91
|
-
}
|
|
92
84
|
}
|
|
93
85
|
default: {
|
|
94
86
|
return {
|
|
@@ -4,7 +4,7 @@ import { FC, Suspense } from "react";
|
|
|
4
4
|
|
|
5
5
|
import { telemetry } from "@/apiClient";
|
|
6
6
|
import { ContentTabs } from "@/components/ContentTabs";
|
|
7
|
-
import {
|
|
7
|
+
import { DefaultErrorBoundary } from "@/errorBoundaries";
|
|
8
8
|
import { MarkdownRenderer } from "@/features/documentation/MarkdownRenderer";
|
|
9
9
|
import { useDocumentation } from "@/features/documentation/useDocumentation";
|
|
10
10
|
import { useOnboarding } from "@/features/onboarding/useOnboarding";
|
|
@@ -68,7 +68,7 @@ type PageSnippetDialogProps = { model: CustomType };
|
|
|
68
68
|
export const PageSnippetDialog: FC<PageSnippetDialogProps> = ({ model }) => {
|
|
69
69
|
return (
|
|
70
70
|
<div>
|
|
71
|
-
<
|
|
71
|
+
<DefaultErrorBoundary>
|
|
72
72
|
<Suspense
|
|
73
73
|
fallback={
|
|
74
74
|
<Button color="grey" startIcon="code">
|
|
@@ -78,7 +78,7 @@ export const PageSnippetDialog: FC<PageSnippetDialogProps> = ({ model }) => {
|
|
|
78
78
|
>
|
|
79
79
|
<PageSnippetContent model={model} />
|
|
80
80
|
</Suspense>
|
|
81
|
-
</
|
|
81
|
+
</DefaultErrorBoundary>
|
|
82
82
|
</div>
|
|
83
83
|
);
|
|
84
84
|
};
|
|
@@ -9,7 +9,7 @@ import Head from "next/head";
|
|
|
9
9
|
import { type FC, Suspense, useState } from "react";
|
|
10
10
|
|
|
11
11
|
import { BreadcrumbItem } from "@/components/Breadcrumb";
|
|
12
|
-
import {
|
|
12
|
+
import { DefaultErrorBoundary } from "@/errorBoundaries";
|
|
13
13
|
import { CUSTOM_TYPES_MESSAGES } from "@/features/customTypes/customTypesMessages";
|
|
14
14
|
import {
|
|
15
15
|
AppLayout,
|
|
@@ -42,7 +42,7 @@ export const CustomTypesTablePage: FC<CustomTypesTablePageProps> = ({
|
|
|
42
42
|
Machine
|
|
43
43
|
</title>
|
|
44
44
|
</Head>
|
|
45
|
-
<
|
|
45
|
+
<DefaultErrorBoundary
|
|
46
46
|
renderError={() => (
|
|
47
47
|
<AppLayout>
|
|
48
48
|
<AppLayoutContent>
|
|
@@ -119,7 +119,7 @@ export const CustomTypesTablePage: FC<CustomTypesTablePageProps> = ({
|
|
|
119
119
|
</AppLayoutContent>
|
|
120
120
|
</AppLayout>
|
|
121
121
|
</Suspense>
|
|
122
|
-
</
|
|
122
|
+
</DefaultErrorBoundary>
|
|
123
123
|
</>
|
|
124
124
|
);
|
|
125
125
|
};
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
import { revalidateData } from "@prismicio/editor-support/Suspense";
|
|
2
|
+
import { Environment } from "@slicemachine/manager/client";
|
|
3
|
+
import { useQueryClient } from "@tanstack/react-query";
|
|
4
|
+
|
|
5
|
+
import { getState } from "@/apiClient";
|
|
6
|
+
import { GetActiveEnvironmentQueryKey } from "@/features/environments/useActiveEnvironment";
|
|
7
|
+
import { managerClient } from "@/managerClient";
|
|
8
|
+
|
|
9
|
+
export function useSetEnvironment() {
|
|
10
|
+
const queryClient = useQueryClient();
|
|
11
|
+
|
|
12
|
+
return async (environment: Pick<Environment, "domain">) => {
|
|
13
|
+
await managerClient.project.updateEnvironment({
|
|
14
|
+
environment: environment.domain,
|
|
15
|
+
});
|
|
16
|
+
|
|
17
|
+
void Promise.all([
|
|
18
|
+
queryClient.invalidateQueries({ queryKey: GetActiveEnvironmentQueryKey }),
|
|
19
|
+
revalidateData(getState, []),
|
|
20
|
+
]);
|
|
21
|
+
};
|
|
22
|
+
}
|
|
@@ -1,14 +1,22 @@
|
|
|
1
|
-
import {
|
|
2
|
-
invalidateFetcherData,
|
|
3
|
-
useRequest,
|
|
4
|
-
} from "@prismicio/editor-support/Suspense";
|
|
1
|
+
import { useQuery, useSuspenseQuery } from "@tanstack/react-query";
|
|
5
2
|
|
|
6
3
|
import { getActiveEnvironment } from "./actions/getActiveEnvironment";
|
|
7
4
|
|
|
8
|
-
export
|
|
9
|
-
|
|
10
|
-
}
|
|
5
|
+
export const GetActiveEnvironmentQueryKey = ["getActiveEnvironment"];
|
|
6
|
+
|
|
7
|
+
export function useActiveEnvironment(options?: { suspense?: boolean }) {
|
|
8
|
+
const { suspense } = options ?? {};
|
|
9
|
+
|
|
10
|
+
const hook = suspense === true ? useSuspenseQuery : useQuery;
|
|
11
|
+
|
|
12
|
+
const { data, error, ...rest } = hook({
|
|
13
|
+
queryKey: GetActiveEnvironmentQueryKey,
|
|
14
|
+
queryFn: () => getActiveEnvironment(),
|
|
15
|
+
});
|
|
11
16
|
|
|
12
|
-
|
|
13
|
-
|
|
17
|
+
return {
|
|
18
|
+
activeEnvironment: data?.activeEnvironment,
|
|
19
|
+
error: data?.error ?? error ?? undefined,
|
|
20
|
+
...rest,
|
|
21
|
+
};
|
|
14
22
|
}
|
|
@@ -1,14 +1,17 @@
|
|
|
1
|
-
import {
|
|
2
|
-
invalidateFetcherData,
|
|
3
|
-
useRequest,
|
|
4
|
-
} from "@prismicio/editor-support/Suspense";
|
|
1
|
+
import { useQuery } from "@tanstack/react-query";
|
|
5
2
|
|
|
6
3
|
import { getEnvironments } from "./actions/getEnvironments";
|
|
7
4
|
|
|
8
|
-
export
|
|
9
|
-
invalidateFetcherData(getEnvironments);
|
|
10
|
-
}
|
|
5
|
+
export const GetEnvironmentsQueryKey = ["getEnvironments"];
|
|
11
6
|
|
|
12
7
|
export function useEnvironments() {
|
|
13
|
-
|
|
8
|
+
const { data, error, ...rest } = useQuery({
|
|
9
|
+
queryKey: GetEnvironmentsQueryKey,
|
|
10
|
+
queryFn: () => getEnvironments(),
|
|
11
|
+
});
|
|
12
|
+
return {
|
|
13
|
+
environments: data?.environments,
|
|
14
|
+
error: data?.error ?? error ?? undefined,
|
|
15
|
+
...rest,
|
|
16
|
+
};
|
|
14
17
|
}
|
|
@@ -3,7 +3,7 @@ import Head from "next/head";
|
|
|
3
3
|
import { type FC, ReactNode, Suspense } from "react";
|
|
4
4
|
|
|
5
5
|
import { BreadcrumbItem } from "@/components/Breadcrumb";
|
|
6
|
-
import {
|
|
6
|
+
import { DefaultErrorBoundary } from "@/errorBoundaries";
|
|
7
7
|
import {
|
|
8
8
|
AppLayout,
|
|
9
9
|
AppLayoutBreadcrumb,
|
|
@@ -19,7 +19,7 @@ export const LabsPage: FC = () => {
|
|
|
19
19
|
<Head>
|
|
20
20
|
<title>Labs - Slice Machine</title>
|
|
21
21
|
</Head>
|
|
22
|
-
<
|
|
22
|
+
<DefaultErrorBoundary
|
|
23
23
|
renderError={() => (
|
|
24
24
|
<LabsPageLayout>
|
|
25
25
|
<Box alignItems="center" justifyContent="center">
|
|
@@ -42,7 +42,7 @@ export const LabsPage: FC = () => {
|
|
|
42
42
|
<LabsList />
|
|
43
43
|
</LabsPageLayout>
|
|
44
44
|
</Suspense>
|
|
45
|
-
</
|
|
45
|
+
</DefaultErrorBoundary>
|
|
46
46
|
</>
|
|
47
47
|
);
|
|
48
48
|
};
|
|
@@ -1,15 +1,22 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import {
|
|
2
|
+
ActionList,
|
|
3
|
+
Badge,
|
|
4
|
+
Box,
|
|
5
|
+
Separator,
|
|
6
|
+
Skeleton,
|
|
7
|
+
} from "@prismicio/editor-ui";
|
|
2
8
|
import { useRouter } from "next/router";
|
|
3
9
|
import { Suspense } from "react";
|
|
4
10
|
|
|
5
11
|
import { telemetry } from "@/apiClient";
|
|
6
|
-
import {
|
|
12
|
+
import { DefaultErrorBoundary } from "@/errorBoundaries";
|
|
7
13
|
import { CUSTOM_TYPES_CONFIG } from "@/features/customTypes/customTypesConfig";
|
|
8
14
|
import { CUSTOM_TYPES_MESSAGES } from "@/features/customTypes/customTypesMessages";
|
|
9
15
|
import { RepositoryInfo } from "@/features/navigation/RepositoryInfo";
|
|
10
16
|
import { OnboardingGuide } from "@/features/onboarding";
|
|
11
17
|
import { useAdapterName } from "@/hooks/useAdapterName";
|
|
12
18
|
import { useMarketingContent } from "@/hooks/useMarketingContent";
|
|
19
|
+
import { CodeIcon } from "@/icons/CodeIcon";
|
|
13
20
|
import { FolderIcon } from "@/icons/FolderIcon";
|
|
14
21
|
import { LightningIcon } from "@/icons/Lightning";
|
|
15
22
|
import { MenuBookIcon } from "@/icons/MenuBookIcon";
|
|
@@ -79,20 +86,20 @@ export function Navigation() {
|
|
|
79
86
|
/>
|
|
80
87
|
</ActionList>
|
|
81
88
|
|
|
82
|
-
<
|
|
89
|
+
<DefaultErrorBoundary>
|
|
83
90
|
<Suspense>
|
|
84
91
|
<UpdateInfo />
|
|
85
92
|
</Suspense>
|
|
86
|
-
</
|
|
93
|
+
</DefaultErrorBoundary>
|
|
87
94
|
</Box>
|
|
88
95
|
|
|
89
96
|
<Box flexDirection="column">
|
|
90
97
|
<ActionList variant="compact">
|
|
91
|
-
<
|
|
98
|
+
<DefaultErrorBoundary>
|
|
92
99
|
<Suspense>
|
|
93
100
|
<OnboardingGuide />
|
|
94
101
|
</Suspense>
|
|
95
|
-
</
|
|
102
|
+
</DefaultErrorBoundary>
|
|
96
103
|
<NavigationItem
|
|
97
104
|
title="Documentation"
|
|
98
105
|
href={documentationLink}
|
|
@@ -106,17 +113,33 @@ export function Navigation() {
|
|
|
106
113
|
}}
|
|
107
114
|
/>
|
|
108
115
|
|
|
116
|
+
<NavigationItem
|
|
117
|
+
title="Prismic MCP"
|
|
118
|
+
href="https://prismic.io/docs/ai#code-with-prismics-mcp-server"
|
|
119
|
+
target="_blank"
|
|
120
|
+
Icon={CodeIcon}
|
|
121
|
+
RightElement={<Badge title="New" color="indigo" />}
|
|
122
|
+
tooltip="Connect Prismic MCP to your editor (like Cursor) for AI-powered coding."
|
|
123
|
+
onClick={() => {
|
|
124
|
+
void telemetry.track({
|
|
125
|
+
event: "sidebar:link-clicked",
|
|
126
|
+
link_name: "prismic_mcp",
|
|
127
|
+
source: "slice_machine_sidebar",
|
|
128
|
+
});
|
|
129
|
+
}}
|
|
130
|
+
/>
|
|
131
|
+
|
|
109
132
|
<NavigationItem
|
|
110
133
|
title="Changelog"
|
|
111
134
|
href="/changelog"
|
|
112
135
|
Icon={LightningIcon}
|
|
113
136
|
active={router.asPath.startsWith("/changelog")}
|
|
114
137
|
RightElement={
|
|
115
|
-
<
|
|
138
|
+
<DefaultErrorBoundary>
|
|
116
139
|
<Suspense fallback={<Skeleton height={16} />}>
|
|
117
140
|
<SliceMachineVersion />
|
|
118
141
|
</Suspense>
|
|
119
|
-
</
|
|
142
|
+
</DefaultErrorBoundary>
|
|
120
143
|
}
|
|
121
144
|
/>
|
|
122
145
|
</ActionList>
|
|
@@ -13,6 +13,7 @@ type NavigationItemPropsBase = {
|
|
|
13
13
|
active?: boolean;
|
|
14
14
|
Icon: FC<SVGProps<SVGSVGElement>>;
|
|
15
15
|
RightElement?: ReactNode;
|
|
16
|
+
tooltip?: string;
|
|
16
17
|
};
|
|
17
18
|
|
|
18
19
|
type NavigationLinkItemProps = NavigationItemPropsBase & {
|
|
@@ -32,7 +33,8 @@ type NavigationButtonItemProps = NavigationItemPropsBase & {
|
|
|
32
33
|
type NavigationItemProps = NavigationLinkItemProps | NavigationButtonItemProps;
|
|
33
34
|
|
|
34
35
|
export function NavigationItem(props: NavigationItemProps) {
|
|
35
|
-
const { title, href, target, active, Icon, RightElement, onClick } =
|
|
36
|
+
const { title, href, target, active, Icon, RightElement, onClick, tooltip } =
|
|
37
|
+
props;
|
|
36
38
|
|
|
37
39
|
const isCollapsed = useMediaQuery({ max: "medium" });
|
|
38
40
|
|
|
@@ -45,7 +47,7 @@ export function NavigationItem(props: NavigationItemProps) {
|
|
|
45
47
|
textVariant="normal"
|
|
46
48
|
backgroundColor="transparent"
|
|
47
49
|
renderStartIcon={ItemIcon}
|
|
48
|
-
endAdornment={RightElement}
|
|
50
|
+
endAdornment={isCollapsed ? undefined : RightElement}
|
|
49
51
|
selected={active}
|
|
50
52
|
>
|
|
51
53
|
{isCollapsed ? null : title}
|
|
@@ -53,7 +55,11 @@ export function NavigationItem(props: NavigationItemProps) {
|
|
|
53
55
|
);
|
|
54
56
|
|
|
55
57
|
return (
|
|
56
|
-
<Tooltip
|
|
58
|
+
<Tooltip
|
|
59
|
+
content={Boolean(tooltip) ? tooltip ?? "" : title}
|
|
60
|
+
side="right"
|
|
61
|
+
visible={Boolean(tooltip) ? undefined : isCollapsed}
|
|
62
|
+
>
|
|
57
63
|
{href !== undefined ? (
|
|
58
64
|
<Link
|
|
59
65
|
href={href}
|
|
@@ -1,14 +1,9 @@
|
|
|
1
|
-
import { Text
|
|
1
|
+
import { Text } from "@prismicio/editor-ui";
|
|
2
2
|
|
|
3
3
|
import { useSliceMachineRunningVersion } from "@/hooks/useSliceMachineRunningVersion";
|
|
4
4
|
|
|
5
5
|
export function SliceMachineVersion() {
|
|
6
6
|
const sliceMachineRunningVersion = useSliceMachineRunningVersion();
|
|
7
|
-
const isCollapsed = useMediaQuery({ max: "medium" });
|
|
8
|
-
|
|
9
|
-
if (isCollapsed) {
|
|
10
|
-
return null;
|
|
11
|
-
}
|
|
12
7
|
|
|
13
8
|
return (
|
|
14
9
|
<Text
|
|
@@ -2,7 +2,7 @@ import { Box, Button, ButtonGroup } from "@prismicio/editor-ui";
|
|
|
2
2
|
import { useRouter } from "next/router";
|
|
3
3
|
import { type FC, Suspense, useState } from "react";
|
|
4
4
|
|
|
5
|
-
import {
|
|
5
|
+
import { DefaultErrorBoundary } from "@/errorBoundaries";
|
|
6
6
|
import { useCustomType } from "@/features/customTypes/customTypesBuilder/useCustomType";
|
|
7
7
|
import {
|
|
8
8
|
CUSTOM_TYPES_CONFIG,
|
|
@@ -17,7 +17,7 @@ export const FloatingBackButton: FC = () => {
|
|
|
17
17
|
const { source } = useRouteChange();
|
|
18
18
|
const sourceCustomTypeId = getSourceCustomTypeId(source);
|
|
19
19
|
return sourceCustomTypeId !== undefined ? (
|
|
20
|
-
<
|
|
20
|
+
<DefaultErrorBoundary>
|
|
21
21
|
<Suspense>
|
|
22
22
|
<Box
|
|
23
23
|
bottom={32}
|
|
@@ -29,7 +29,7 @@ export const FloatingBackButton: FC = () => {
|
|
|
29
29
|
<BackButton sourceCustomTypeId={sourceCustomTypeId} />
|
|
30
30
|
</Box>
|
|
31
31
|
</Suspense>
|
|
32
|
-
</
|
|
32
|
+
</DefaultErrorBoundary>
|
|
33
33
|
) : null;
|
|
34
34
|
};
|
|
35
35
|
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
import { Button } from "@prismicio/editor-ui";
|
|
2
|
+
|
|
3
|
+
import { telemetry } from "@/apiClient";
|
|
4
|
+
|
|
5
|
+
export const McpPromoLink = () => {
|
|
6
|
+
return (
|
|
7
|
+
<Button
|
|
8
|
+
asChild
|
|
9
|
+
invisible
|
|
10
|
+
color="grey"
|
|
11
|
+
endIcon="openInNew"
|
|
12
|
+
sx={{ alignSelf: "center" }}
|
|
13
|
+
onClick={() => {
|
|
14
|
+
void telemetry.track({
|
|
15
|
+
event: "mcp:promo-link-clicked",
|
|
16
|
+
source: "slice_editor",
|
|
17
|
+
target: "docs",
|
|
18
|
+
});
|
|
19
|
+
}}
|
|
20
|
+
>
|
|
21
|
+
<a
|
|
22
|
+
href="https://prismic.io/docs/ai#code-with-prismics-mcp-server"
|
|
23
|
+
target="_blank"
|
|
24
|
+
>
|
|
25
|
+
Boost your workflow in Cursor with Prismic MCP
|
|
26
|
+
</a>
|
|
27
|
+
</Button>
|
|
28
|
+
);
|
|
29
|
+
};
|
|
@@ -29,6 +29,7 @@ export async function pushChanges(
|
|
|
29
29
|
type: "Slice" as const,
|
|
30
30
|
libraryID: sliceChange.slice.from,
|
|
31
31
|
status: sliceChange.status,
|
|
32
|
+
variationImageUrlMap: sliceChange.variationImageUrlMap,
|
|
32
33
|
}));
|
|
33
34
|
const customTypeChanges = changedCustomTypes.map((customTypeChange) => ({
|
|
34
35
|
id: customTypeChange.customType.id,
|