slice-machine-ui 2.18.1-alpha.jp-unauthorized-error.4 → 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.
Files changed (76) hide show
  1. package/out/404.html +1 -1
  2. package/out/_next/static/{eWxsTVqoYTTRkUB_nHhoh → bWd9YPY0k1NsrjJCNbscH}/_buildManifest.js +1 -1
  3. package/out/_next/static/chunks/{248-bdbfde18c5a04eae.js → 248-bcf03aa3c62e7dfb.js} +1 -1
  4. package/out/_next/static/chunks/34-50c64778da33cff6.js +1 -0
  5. package/out/_next/static/chunks/422-c9192a1dbdd2ae0e.js +1 -0
  6. package/out/_next/static/chunks/429-1137c819c2bf6b66.js +3 -0
  7. package/out/_next/static/chunks/{489-6edb99e269996dd1.js → 489-77d1fe67b6d0f0f8.js} +1 -1
  8. package/out/_next/static/chunks/630-c34de0401b8a0375.js +1 -0
  9. package/out/_next/static/chunks/pages/{_app-d1098ab653091b72.js → _app-d298cdfd4509fb06.js} +40 -40
  10. package/out/_next/static/chunks/pages/changelog-3901f2fc937d9648.js +1 -0
  11. package/out/_next/static/chunks/pages/changes-62d4e18795217cba.js +1 -0
  12. package/out/_next/static/chunks/pages/custom-types/{[customTypeId]-7102c23f96cd1768.js → [customTypeId]-816acb31b652239b.js} +1 -1
  13. package/out/_next/static/chunks/pages/{labs-d79597003a1ff74e.js → labs-5f5c63fb1f7d4b92.js} +1 -1
  14. package/out/_next/static/chunks/pages/page-types/{[pageTypeId]-d4bc920a5efffa0a.js → [pageTypeId]-669d5479e81b638b.js} +1 -1
  15. package/out/_next/static/chunks/pages/slices/[lib]/[sliceName]/[variation]/simulator-eca10940c9083d4c.js +1 -0
  16. package/out/_next/static/chunks/pages/slices/[lib]/[sliceName]/[variation]-b4700a6332ea828c.js +1 -0
  17. package/out/_next/static/chunks/pages/slices-bf63f937b184b470.js +1 -0
  18. package/out/changelog.html +1 -1
  19. package/out/changes.html +1 -1
  20. package/out/custom-types/[customTypeId].html +1 -1
  21. package/out/custom-types.html +1 -1
  22. package/out/index.html +1 -1
  23. package/out/labs.html +1 -1
  24. package/out/page-types/[pageTypeId].html +1 -1
  25. package/out/slices/[lib]/[sliceName]/[variation]/simulator.html +1 -1
  26. package/out/slices/[lib]/[sliceName]/[variation].html +1 -1
  27. package/out/slices.html +1 -1
  28. package/package.json +3 -6
  29. package/src/errorBoundaries.tsx +150 -0
  30. package/src/features/auth/LogoutButton.tsx +42 -36
  31. package/src/features/builder/fields/contentRelationship/ContentRelationshipFieldPicker.tsx +3 -3
  32. package/src/features/changes/StatusBadge.tsx +1 -9
  33. package/src/features/customTypes/customTypesBuilder/PageSnippetDialog/PageSnippetDialog.tsx +3 -3
  34. package/src/features/customTypes/customTypesTable/CustomTypesTablePage.tsx +3 -3
  35. package/src/features/environments/actions/useSetEnvironment.ts +22 -0
  36. package/src/features/environments/useActiveEnvironment.ts +17 -9
  37. package/src/features/environments/useEnvironments.ts +11 -8
  38. package/src/features/labs/labsList/LabsPage.tsx +3 -3
  39. package/src/features/navigation/Navigation.tsx +31 -8
  40. package/src/features/navigation/NavigationItem.tsx +9 -3
  41. package/src/features/navigation/SliceMachineVersion.tsx +1 -6
  42. package/src/features/slices/sliceBuilder/FloatingBackButton.tsx +3 -3
  43. package/src/features/slices/sliceBuilder/McpPromoLink.tsx +29 -0
  44. package/src/features/sync/actions/pushChanges.ts +1 -0
  45. package/src/features/sync/getUnSyncChanges.ts +23 -7
  46. package/src/legacy/components/AppLayout/index.tsx +10 -85
  47. package/src/legacy/components/ChangesEmptyState/UnauthenticatedView.tsx +31 -0
  48. package/src/legacy/components/ChangesEmptyState/index.ts +1 -1
  49. package/src/legacy/components/ChangesItems/ChangesItems.tsx +3 -3
  50. package/src/legacy/components/LoginModal/index.tsx +13 -5
  51. package/src/legacy/components/Navigation/ChangesItem.tsx +2 -6
  52. package/src/legacy/components/Navigation/Environment.tsx +2 -7
  53. package/src/legacy/components/Navigation/SideNavEnvironmentSelector/SideNavEnvironmentSelector.tsx +3 -8
  54. package/src/legacy/components/Simulator/index.tsx +3 -3
  55. package/src/legacy/lib/builders/CustomTypeBuilder/TabZone/index.tsx +3 -3
  56. package/src/legacy/lib/builders/SliceBuilder/index.tsx +2 -0
  57. package/src/legacy/lib/models/common/ModelStatus/index.ts +6 -1
  58. package/src/modules/userContext/index.ts +6 -3
  59. package/src/modules/userContext/types.ts +1 -1
  60. package/src/pages/_app.tsx +88 -95
  61. package/src/pages/changes.tsx +4 -5
  62. package/src/queryClient.tsx +24 -0
  63. package/test/__testutils__/index.tsx +13 -10
  64. package/out/_next/static/chunks/157-54b8336d20b41933.js +0 -3
  65. package/out/_next/static/chunks/34-8d9d9b2944824750.js +0 -1
  66. package/out/_next/static/chunks/385-b949173dfa03dd3e.js +0 -1
  67. package/out/_next/static/chunks/630-bb6e3db525588f16.js +0 -1
  68. package/out/_next/static/chunks/pages/changelog-21b960abba5abf71.js +0 -1
  69. package/out/_next/static/chunks/pages/changes-7919746009a45ad8.js +0 -1
  70. package/out/_next/static/chunks/pages/slices/[lib]/[sliceName]/[variation]/simulator-b127d948a17968d3.js +0 -1
  71. package/out/_next/static/chunks/pages/slices/[lib]/[sliceName]/[variation]-98f85d5fb8d5c704.js +0 -1
  72. package/out/_next/static/chunks/pages/slices-046e5e978ffc3a42.js +0 -1
  73. package/src/ErrorBoundary.tsx +0 -47
  74. package/src/features/environments/actions/setEnvironment.ts +0 -18
  75. package/src/legacy/components/ChangesEmptyState/AuthErrorPage.tsx +0 -44
  76. /package/out/_next/static/{eWxsTVqoYTTRkUB_nHhoh → bWd9YPY0k1NsrjJCNbscH}/_ssgManifest.js +0 -0
@@ -3,6 +3,7 @@ import { LibraryUI } from "@/legacy/lib/models/common/LibraryUI";
3
3
  import {
4
4
  getModelId,
5
5
  hasLocal,
6
+ hasRemote,
6
7
  isRemoteOnly,
7
8
  LocalOrRemoteCustomType,
8
9
  LocalOrRemoteSlice,
@@ -76,11 +77,23 @@ export function getUnSyncedChanges(
76
77
  );
77
78
 
78
79
  const changedSlices = unSyncedSlices
79
- .map((s) => ({
80
- slice: s,
81
- status: modelsStatuses.slices[s.model.id],
82
- }))
80
+ .map((slice) => {
81
+ const status = modelsStatuses.slices[slice.model.id];
82
+
83
+ const imageUrlMap = findRemoteSlice(
84
+ slices,
85
+ slice.model.id,
86
+ )?.variations.reduce<Record<string, string>>((result, variation) => {
87
+ const { imageUrl } = variation;
88
+ if (imageUrl === undefined || imageUrl === "") return result;
89
+ result[variation.id] = imageUrl;
90
+ return result;
91
+ }, {});
92
+
93
+ return { status, slice, variationImageUrlMap: imageUrlMap ?? {} };
94
+ })
83
95
  .filter((s): s is ChangedSlice => unSyncStatuses.includes(s.status));
96
+
84
97
  const changedCustomTypes = unSyncedCustomTypes
85
98
  .map((model) => (hasLocal(model) ? model.local : model.remote))
86
99
  .map((ct) => ({
@@ -98,6 +111,11 @@ export function getUnSyncedChanges(
98
111
  };
99
112
  }
100
113
 
114
+ function findRemoteSlice(slices: LocalOrRemoteSlice[], sliceId: string) {
115
+ const slice = slices.find((s) => hasRemote(s) && s.remote.id === sliceId);
116
+ return slice && hasRemote(slice) ? slice.remote : undefined;
117
+ }
118
+
101
119
  // ComponentUI are manipulated on all the relevant pages
102
120
  // But the data is not available for remote only slices
103
121
  // which have been deleted locally
@@ -131,9 +149,7 @@ export const getModelStatus = (args: GetModelStatusArgs): ModelsStatuses => {
131
149
  const { slices, customTypes, isOnline, authStatus } = args;
132
150
 
133
151
  const userHasAccessToModels =
134
- isOnline &&
135
- authStatus != AuthStatus.FORBIDDEN &&
136
- authStatus != AuthStatus.UNAUTHORIZED;
152
+ isOnline && authStatus === AuthStatus.AUTHORIZED;
137
153
 
138
154
  const modelsStatuses = {
139
155
  slices: computeStatuses(slices, userHasAccessToModels),
@@ -1,16 +1,6 @@
1
- import {
2
- BlankSlate,
3
- BlankSlateDescription,
4
- BlankSlateIcon,
5
- BlankSlateTitle,
6
- Box,
7
- Button,
8
- ButtonGroup,
9
- Text,
10
- } from "@prismicio/editor-ui";
11
- import { isUnauthorizedError } from "@slicemachine/manager/client";
1
+ import { Box, Button, ButtonGroup } from "@prismicio/editor-ui";
12
2
  import { useRouter } from "next/router";
13
- import { FC, PropsWithChildren, Suspense, useState } from "react";
3
+ import { FC, PropsWithChildren, Suspense } from "react";
14
4
 
15
5
  import { Breadcrumb } from "@/components/Breadcrumb";
16
6
  import {
@@ -19,8 +9,6 @@ import {
19
9
  PageLayoutHeader,
20
10
  PageLayoutPane,
21
11
  } from "@/components/PageLayout";
22
- import { ErrorBoundary } from "@/ErrorBoundary";
23
- import { LogoutButton } from "@/features/auth/LogoutButton";
24
12
  import { useActiveEnvironment } from "@/features/environments/useActiveEnvironment";
25
13
  import { Navigation } from "@/features/navigation/Navigation";
26
14
 
@@ -29,80 +17,17 @@ export const AppLayout: FC<PropsWithChildren> = ({
29
17
  ...otherProps
30
18
  }) => {
31
19
  return (
32
- <ErrorBoundary
33
- renderError={(error) => {
34
- return (
35
- <Box
36
- position="absolute"
37
- top={64}
38
- width="100%"
39
- justifyContent="center"
40
- flexDirection="column"
41
- >
42
- <BlankSlate>
43
- <BlankSlateIcon
44
- lineColor="tomato11"
45
- backgroundColor="tomato3"
46
- name="alert"
47
- />
48
- <BlankSlateTitle>Failed to load Slice Machine</BlankSlateTitle>
49
- <BlankSlateDescription>
50
- <RenderError error={error} />
51
- </BlankSlateDescription>
52
- </BlankSlate>
53
- </Box>
54
- );
55
- }}
56
- >
57
- <Suspense>
58
- <PageLayoutWithActiveEnvironment {...otherProps}>
59
- <PageLayoutPane>
60
- <Navigation />
61
- </PageLayoutPane>
62
- {children}
63
- </PageLayoutWithActiveEnvironment>
64
- </Suspense>
65
- </ErrorBoundary>
20
+ <Suspense>
21
+ <PageLayoutWithActiveEnvironment {...otherProps}>
22
+ <PageLayoutPane>
23
+ <Navigation />
24
+ </PageLayoutPane>
25
+ {children}
26
+ </PageLayoutWithActiveEnvironment>
27
+ </Suspense>
66
28
  );
67
29
  };
68
30
 
69
- function RenderError(args: { error: unknown }) {
70
- const { error } = args;
71
-
72
- if (isUnauthorizedError(error)) {
73
- return <UnauthorizedErrorView />;
74
- }
75
- return <>{JSON.stringify(error)}</>;
76
- }
77
-
78
- function UnauthorizedErrorView() {
79
- const [isLoggingOut, setIsLoggingOut] = useState(false);
80
-
81
- return (
82
- <Box flexDirection="column" gap={16} margin={{ top: 8 }}>
83
- <Box flexDirection="column" gap={8} alignItems="center">
84
- <Text variant="h3" align="center">
85
- It seems like you don't have access to this repository
86
- </Text>
87
- <Text align="center">
88
- Check that the repository name is correct, then contact your
89
- repository administrator.
90
- </Text>
91
- </Box>
92
- <LogoutButton
93
- isLoading={isLoggingOut}
94
- onLogoutSuccess={() => {
95
- setIsLoggingOut(true);
96
- window.location.reload();
97
- }}
98
- sx={{ alignSelf: "center" }}
99
- >
100
- Log out
101
- </LogoutButton>
102
- </Box>
103
- );
104
- }
105
-
106
31
  const environmentTopBorderColorMap = {
107
32
  prod: "purple",
108
33
  stage: "indigo",
@@ -0,0 +1,31 @@
1
+ import { Box, Button, Text } from "@prismicio/editor-ui";
2
+
3
+ import useSliceMachineActions from "@/modules/useSliceMachineActions";
4
+
5
+ export const UnauthenticatedView = () => {
6
+ const { openLoginModal } = useSliceMachineActions();
7
+
8
+ return (
9
+ <Box
10
+ flexDirection="column"
11
+ height="100%"
12
+ alignItems="center"
13
+ justifyContent="center"
14
+ gap={8}
15
+ >
16
+ <Text variant="h3" align="center">
17
+ It seems like you are logged out
18
+ </Text>
19
+ <Text align="center">Log in to connect to your repository.</Text>
20
+ <Text align="center">
21
+ If that doesn't work, it's possible that Slice Machine is having trouble
22
+ accessing Prismic's servers.{" "}
23
+ <Text href="https://community.prismic.io/">
24
+ Contact our support team
25
+ </Text>
26
+ .
27
+ </Text>
28
+ <Button onClick={() => openLoginModal()}>Log in to Prismic</Button>
29
+ </Box>
30
+ );
31
+ };
@@ -1,2 +1,2 @@
1
- export { AuthErrorPage } from "./AuthErrorPage";
2
1
  export { OfflinePage } from "./OfflinePage";
2
+ export { UnauthenticatedView } from "./UnauthenticatedView";
@@ -3,7 +3,7 @@ import React, { Suspense } from "react";
3
3
  import { AiOutlineExclamationCircle } from "react-icons/ai";
4
4
 
5
5
  import { countMissingScreenshots } from "@/domain/slice";
6
- import { ErrorBoundary } from "@/ErrorBoundary";
6
+ import { DefaultErrorBoundary } from "@/errorBoundaries";
7
7
  import { SharedSliceCard } from "@/features/slices/sliceCards/SharedSliceCard";
8
8
  import { ModelsStatuses } from "@/features/sync/getUnSyncChanges";
9
9
  import { useScreenshotChangesModal } from "@/hooks/useScreenshotChangesModal";
@@ -146,11 +146,11 @@ export const ChangesItems: React.FC<ChangesItemsProps> = ({
146
146
  )}
147
147
  </Box>
148
148
 
149
- <ErrorBoundary>
149
+ <DefaultErrorBoundary>
150
150
  <Suspense>
151
151
  <DevCollaborationExperiment />
152
152
  </Suspense>
153
- </ErrorBoundary>
153
+ </DefaultErrorBoundary>
154
154
  </Box>
155
155
  </>
156
156
  );
@@ -1,3 +1,4 @@
1
+ import { useQueryClient } from "@tanstack/react-query";
1
2
  import React from "react";
2
3
  import Modal from "react-modal";
3
4
  import { useSelector } from "react-redux";
@@ -15,8 +16,8 @@ import {
15
16
 
16
17
  import { checkAuthStatus, clearAuth, getState } from "@/apiClient";
17
18
  import { getActiveEnvironment } from "@/features/environments/actions/getActiveEnvironment";
18
- import { invalidateActiveEnvironmentData } from "@/features/environments/useActiveEnvironment";
19
- import { invalidateEnvironmentsData } from "@/features/environments/useEnvironments";
19
+ import { GetActiveEnvironmentQueryKey } from "@/features/environments/useActiveEnvironment";
20
+ import { GetEnvironmentsQueryKey } from "@/features/environments/useEnvironments";
20
21
  import { useAutoSync } from "@/features/sync/AutoSyncProvider";
21
22
  import { getUnSyncedChanges } from "@/features/sync/getUnSyncChanges";
22
23
  import SliceMachineModal from "@/legacy/components/SliceMachineModal";
@@ -54,6 +55,7 @@ const LoginModal: React.FunctionComponent = () => {
54
55
  const { syncChanges } = useAutoSync();
55
56
  const { closeModals, startLoadingLogin, stopLoadingLogin, refreshState } =
56
57
  useSliceMachineActions();
58
+ const queryClient = useQueryClient();
57
59
 
58
60
  const prismicBase = preferWroomBase(env.manifest.apiEndpoint);
59
61
  const loginRedirectUrl = `${
@@ -79,8 +81,14 @@ const LoginModal: React.FunctionComponent = () => {
79
81
  );
80
82
 
81
83
  // refresh queries to update the UI
82
- invalidateEnvironmentsData();
83
- invalidateActiveEnvironmentData();
84
+ await Promise.all([
85
+ queryClient.invalidateQueries({
86
+ queryKey: GetEnvironmentsQueryKey,
87
+ }),
88
+ queryClient.invalidateQueries({
89
+ queryKey: GetActiveEnvironmentQueryKey,
90
+ }),
91
+ ]);
84
92
 
85
93
  toast.success("Logged in");
86
94
  stopLoadingLogin();
@@ -127,7 +135,7 @@ const LoginModal: React.FunctionComponent = () => {
127
135
 
128
136
  return (
129
137
  <SliceMachineModal
130
- isOpen={isOpen}
138
+ isOpen={isLoginLoading || isOpen}
131
139
  shouldCloseOnOverlayClick
132
140
  onRequestClose={closeModals}
133
141
  contentLabel={"login_modal"}
@@ -117,16 +117,12 @@ export const ChangesCount: FC<ChangesCountProps> = ({ color }) => {
117
117
 
118
118
  if (
119
119
  !isOnline ||
120
- authStatus === AuthStatus.UNAUTHORIZED ||
121
- authStatus === AuthStatus.FORBIDDEN
120
+ authStatus !== AuthStatus.AUTHORIZED ||
121
+ numberOfChanges === 0
122
122
  ) {
123
123
  return null;
124
124
  }
125
125
 
126
- if (numberOfChanges === 0) {
127
- return null;
128
- }
129
-
130
126
  const formattedNumberOfChanges = numberOfChanges > 9 ? "+9" : numberOfChanges;
131
127
 
132
128
  return (
@@ -5,7 +5,7 @@ import {
5
5
  import { useState } from "react";
6
6
 
7
7
  import { getState, telemetry } from "@/apiClient";
8
- import { setEnvironment } from "@/features/environments/actions/setEnvironment";
8
+ import { useSetEnvironment } from "@/features/environments/actions/useSetEnvironment";
9
9
  import { useActiveEnvironment } from "@/features/environments/useActiveEnvironment";
10
10
  import { useEnvironments } from "@/features/environments/useEnvironments";
11
11
  import { useAutoSync } from "@/features/sync/AutoSyncProvider";
@@ -28,6 +28,7 @@ export function Environment() {
28
28
  const authStatus = useAuthStatus();
29
29
  const [isSwitchingEnv, setIsSwitchingEnv] = useState(false);
30
30
  const { autoSyncStatus } = useAutoSync();
31
+ const setEnvironment = useSetEnvironment();
31
32
 
32
33
  async function onSelect(environment: EnvironmentType) {
33
34
  if (activeEnvironment?.name === environment.name) {
@@ -105,10 +106,4 @@ export function Environment() {
105
106
  />
106
107
  );
107
108
  }
108
-
109
- if (useEnvironmentsError !== undefined) {
110
- throw useEnvironmentsError;
111
- }
112
-
113
- throw activeEnvironmentError;
114
109
  }
@@ -17,9 +17,8 @@ import * as VisuallyHidden from "@radix-ui/react-visually-hidden";
17
17
  import type { Environment } from "@slicemachine/manager/client";
18
18
  import { clsx } from "clsx";
19
19
  import type { FC, ReactNode } from "react";
20
- import { toast } from "react-toastify";
21
20
 
22
- import { LogoutButton } from "@/features/auth/LogoutButton";
21
+ import { EnvironmentLogoutButton } from "@/features/auth/LogoutButton";
23
22
  import { LoginIcon } from "@/icons/LoginIcon";
24
23
  import { LogoIcon } from "@/icons/LogoIcon";
25
24
 
@@ -30,7 +29,7 @@ type SideNavEnvironmentSelectorProps = {
30
29
  disabled?: boolean;
31
30
  environments?: Environment[];
32
31
  loading?: boolean;
33
- variant?: "default" | "offline" | "unauthorized" | "unauthenticated";
32
+ variant?: "default" | "offline" | "unauthenticated";
34
33
  onLogInClick?: () => void;
35
34
  onSelect?: (environment: Environment) => void | Promise<void>;
36
35
  };
@@ -141,11 +140,7 @@ export const SideNavEnvironmentSelector: FC<SideNavEnvironmentSelectorProps> = (
141
140
  />
142
141
  ) : undefined}
143
142
 
144
- {variant === "default" && (
145
- <LogoutButton
146
- onLogoutSuccess={() => toast.success("Logged out")}
147
- />
148
- )}
143
+ {variant === "default" && <EnvironmentLogoutButton />}
149
144
  </Box>
150
145
  </>
151
146
  )}
@@ -17,7 +17,7 @@ import { toast } from "react-toastify";
17
17
  import { BaseStyles, Box, Flex, Spinner } from "theme-ui";
18
18
 
19
19
  import { saveSliceMock, telemetry } from "@/apiClient";
20
- import { ErrorBoundary } from "@/ErrorBoundary";
20
+ import { DefaultErrorBoundary } from "@/errorBoundaries";
21
21
  import useThrottle from "@/hooks/useThrottle";
22
22
  import ScreenshotPreviewModal from "@/legacy/components/ScreenshotPreviewModal";
23
23
  import { ComponentUI } from "@/legacy/lib/models/common/ComponentUI";
@@ -287,7 +287,7 @@ const Simulator: FC<SimulatorProps> = ({ slice, variation }) => {
287
287
  overflowY: "auto",
288
288
  }}
289
289
  >
290
- <ErrorBoundary
290
+ <DefaultErrorBoundary
291
291
  renderError={() => (
292
292
  <DefaultErrorMessage
293
293
  title="Editor error"
@@ -315,7 +315,7 @@ const Simulator: FC<SimulatorProps> = ({ slice, variation }) => {
315
315
  />
316
316
  </QueryClientProvider>
317
317
  </Suspense>
318
- </ErrorBoundary>
318
+ </DefaultErrorBoundary>
319
319
  </Flex>
320
320
  ) : null}
321
321
  </Flex>
@@ -20,7 +20,7 @@ import {
20
20
  reorderField,
21
21
  updateField,
22
22
  } from "@/domain/customType";
23
- import { ErrorBoundary } from "@/ErrorBoundary";
23
+ import { DefaultErrorBoundary } from "@/errorBoundaries";
24
24
  import { useCustomTypeState } from "@/features/customTypes/customTypesBuilder/CustomTypeProvider";
25
25
  import {
26
26
  CustomTypes,
@@ -246,7 +246,7 @@ const TabZone: FC<TabZoneProps> = ({ tabId }) => {
246
246
  };
247
247
 
248
248
  return (
249
- <ErrorBoundary>
249
+ <DefaultErrorBoundary>
250
250
  <Suspense
251
251
  fallback={
252
252
  <Box padding={32}>
@@ -297,7 +297,7 @@ const TabZone: FC<TabZoneProps> = ({ tabId }) => {
297
297
  />
298
298
  </List>
299
299
  </Suspense>
300
- </ErrorBoundary>
300
+ </DefaultErrorBoundary>
301
301
  );
302
302
  };
303
303
 
@@ -4,6 +4,7 @@ import { type FC } from "react";
4
4
  import { BreadcrumbItem } from "@/components/Breadcrumb";
5
5
  import { AutoSaveStatusIndicator } from "@/features/autoSave/AutoSaveStatusIndicator";
6
6
  import { FloatingBackButton } from "@/features/slices/sliceBuilder/FloatingBackButton";
7
+ import { McpPromoLink } from "@/features/slices/sliceBuilder/McpPromoLink";
7
8
  import { useSliceState } from "@/features/slices/sliceBuilder/SliceBuilderProvider";
8
9
  import {
9
10
  AppLayout,
@@ -35,6 +36,7 @@ export const SliceBuilder: FC = () => {
35
36
  <BreadcrumbItem active>{slice.model.name}</BreadcrumbItem>
36
37
  </AppLayoutBreadcrumb>
37
38
  <AppLayoutActions>
39
+ <McpPromoLink />
38
40
  <AutoSaveStatusIndicator status={actionQueueStatus} />
39
41
  <SimulatorButton disabled={actionQueueStatus !== "done"} />
40
42
  </AppLayoutActions>
@@ -30,7 +30,12 @@ export type ChangesStatus =
30
30
  | ModelStatus.New
31
31
  | ModelStatus.Modified;
32
32
 
33
- export type ChangedSlice = { status: ChangesStatus; slice: ComponentUI };
33
+ export type ChangedSlice = {
34
+ status: ChangesStatus;
35
+ slice: ComponentUI;
36
+ /** A map of variation IDs to remote screenshot URLs. */
37
+ variationImageUrlMap: Record<string, string>;
38
+ };
34
39
  export type ChangedCustomType = {
35
40
  status: ChangesStatus;
36
41
  customType: CustomTypeSM;
@@ -1,3 +1,4 @@
1
+ import { UnauthenticatedError } from "@slicemachine/manager/client";
1
2
  import { Reducer } from "redux";
2
3
  import { ActionType, createAction, getType } from "typesafe-actions";
3
4
 
@@ -92,10 +93,12 @@ const getAuthStatus = (
92
93
  case undefined: {
93
94
  return AuthStatus.AUTHORIZED;
94
95
  }
95
- case 403: {
96
- return AuthStatus.UNAUTHORIZED;
97
- }
98
96
  case 401: {
97
+ if (clientError.name === new UnauthenticatedError().name) {
98
+ return AuthStatus.UNAUTHENTICATED;
99
+ }
100
+ }
101
+ case 403: {
99
102
  return AuthStatus.FORBIDDEN;
100
103
  }
101
104
  default: {
@@ -1,6 +1,6 @@
1
1
  export enum AuthStatus {
2
2
  AUTHORIZED = "authorized",
3
- UNAUTHORIZED = "unauthorized",
3
+ UNAUTHENTICATED = "unauthenticated",
4
4
  FORBIDDEN = "forbidden",
5
5
  UNKNOWN = "unknown",
6
6
  }