slice-machine-ui 1.23.2-beta.1 → 1.23.2-beta.10
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/.depcheckrc +3 -0
- package/components/DeleteSliceModal/index.tsx +34 -24
- package/components/DeleteVariationModal/index.tsx +1 -0
- package/components/Forms/CreateCustomTypeModal/CreateCustomTypeModal.tsx +61 -38
- package/components/Forms/RenameCustomTypeModal/RenameCustomTypeModal.tsx +2 -2
- package/components/Forms/RenameSliceModal/RenameSliceModal.tsx +33 -21
- package/components/Forms/RenameVariationModal/RenameVariationModal.tsx +0 -1
- package/components/ModalFormCard/index.tsx +0 -1
- package/components/Navigation/ChangesListItem.tsx +14 -32
- package/components/Navigation/index.tsx +29 -12
- package/components/ReviewModal/ReviewForm.tsx +0 -1
- package/lib/builders/common/EditModal/Form.jsx +0 -1
- package/lib/builders/common/Zone/Card/components/NewField.tsx +0 -1
- package/lib/models/common/widgets/StructuredText/index.ts +1 -1
- package/out/404.html +1 -1
- package/out/_next/static/Dz7wwTNVdXvoAjySXz7mE/_buildManifest.js +1 -0
- package/out/_next/static/chunks/18539003-5f78714a35fde4a1.js +97 -0
- package/out/_next/static/chunks/25-01f422cea8066349.js +1 -0
- package/out/_next/static/chunks/318-99b5914b60bf1d17.js +1 -0
- package/out/_next/static/chunks/727-f8fefdd3e50cab2a.js +1 -0
- package/out/_next/static/chunks/777-8c5507e3b119b5dc.js +8 -0
- package/out/_next/static/chunks/799-b616401fe0dafdeb.js +1 -0
- package/out/_next/static/chunks/904-4653b7a849f4db94.js +1 -0
- package/out/_next/static/chunks/pages/_app-5ec41fc53c1f6405.js +434 -0
- package/out/_next/static/chunks/pages/{changelog-afd18276fe018b24.js → changelog-02cd80dad6f40878.js} +1 -1
- package/out/_next/static/chunks/pages/changes-8e9da8fbb809aad3.js +1 -0
- package/out/_next/static/chunks/pages/custom-types/{[customTypeId]-3f862ad6111a1fc0.js → [customTypeId]-97ab3b90b85d645d.js} +1 -1
- package/out/_next/static/chunks/pages/{custom-types-94b8f8dbf7727412.js → custom-types-3c8e721ec6802127.js} +1 -1
- package/out/_next/static/chunks/pages/index-cf93d16e9c63b6db.js +1 -0
- package/out/_next/static/chunks/pages/{labs-ee206005948f8e87.js → labs-39b50541a4c370bf.js} +1 -1
- package/out/_next/static/chunks/pages/page-types/{[pageTypeId]-d1ffca11c42f2603.js → [pageTypeId]-84a22c5e480d3fa9.js} +1 -1
- package/out/_next/static/chunks/pages/settings-879686e3a8759dc3.js +1 -0
- package/out/_next/static/chunks/pages/slices/[lib]/[sliceName]/[variation]/{simulator-897ca93c114a1435.js → simulator-c498e9c80cae8cce.js} +1 -1
- package/out/_next/static/chunks/pages/slices/[lib]/[sliceName]/[variation]-9b893c62b2f2fe4a.js +1 -0
- package/out/_next/static/chunks/pages/slices-4522d604117d8c66.js +1 -0
- package/out/_next/static/css/3c26dbbbd8f4be12.css +1 -0
- package/out/_next/static/css/{37153180c3b0dd66.css → 838fd8bb5a7c32b0.css} +1 -1
- package/out/_next/static/css/ca32882a5d2d8d88.css +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/settings.html +1 -0
- 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 +9 -6
- package/pages/settings.tsx +1 -0
- package/pages/slices.tsx +20 -8
- package/postcss.config.js +10 -0
- package/src/apiClient.ts +1 -22
- package/src/components/FieldSet/FieldSet.stories.tsx +20 -11
- package/src/components/RelativeTime/RelativeTime.stories.tsx +25 -0
- package/src/components/RelativeTime/RelativeTime.tsx +40 -0
- package/src/components/RelativeTime/index.ts +1 -0
- package/src/domain/__tests__/customType.test.ts +205 -19
- package/src/domain/customType.ts +132 -7
- package/src/domain/slice.ts +38 -0
- package/src/features/customTypes/PageTypeOnboarding.tsx +21 -26
- package/src/features/customTypes/actions/convertCustomToPageType.ts +14 -10
- package/src/features/customTypes/actions/createCustomType.ts +69 -0
- package/src/features/customTypes/actions/deleteCustomType.ts +13 -3
- package/src/features/customTypes/actions/renameCustomType.ts +10 -4
- package/src/features/customTypes/customTypesBuilder/CustomTypesBuilderPage.tsx +3 -1
- package/src/features/customTypes/customTypesTable/CustomTypesTable.tsx +8 -8
- package/src/features/customTypes/customTypesTable/CustomTypesTablePage.tsx +18 -17
- package/src/features/environments/actions/setEnvironment.ts +2 -2
- package/src/features/settings/SettingsPage.tsx +47 -0
- package/src/features/settings/git/ConnectGitRepository.tsx +126 -0
- package/src/features/settings/git/ConnectGitRepositoryBlankSlate.tsx +33 -0
- package/src/features/settings/git/GitOwnerSelect.tsx +71 -0
- package/src/features/settings/git/GitProvider.ts +40 -0
- package/src/features/settings/git/GitProviderConnectButtons.tsx +63 -0
- package/src/features/settings/git/GitRepositoriesList.tsx +76 -0
- package/src/features/settings/git/GitRepositoriesSearch.tsx +69 -0
- package/src/features/settings/git/GitRepositoryConnectDialog.tsx +85 -0
- package/src/features/settings/git/GitRepositoryDisconnectDialog.tsx +60 -0
- package/src/features/settings/git/useGitIntegrationExperiment.ts +8 -0
- package/src/features/settings/git/useGitOwners.ts +12 -0
- package/src/features/settings/git/useGitRepos.ts +24 -0
- package/src/features/settings/git/useLinkedGitRepos.ts +41 -0
- package/src/features/settings/git/useWriteAPIToken.ts +23 -0
- package/src/features/slices/actions/createSlice.ts +5 -29
- package/src/features/slices/actions/deleteSlice.ts +33 -0
- package/src/features/slices/actions/renameSlice.ts +37 -0
- package/src/features/slices/convertLegacySlice/ConvertLegacySliceAsNewSliceDialog.tsx +0 -1
- package/src/features/slices/convertLegacySlice/ConvertLegacySliceAsNewVariationDialog.tsx +0 -1
- package/src/features/slices/convertLegacySlice/ConvertLegacySliceMergeWithIdenticalDialog.tsx +0 -1
- package/src/hooks/useUser.ts +14 -0
- package/src/icons/SettingsIcon.tsx +19 -0
- package/src/modules/availableCustomTypes/index.ts +29 -135
- package/src/modules/loading/types.ts +0 -5
- package/src/modules/modal/types.ts +0 -3
- package/src/modules/slices/index.ts +27 -193
- package/src/modules/slices/selector.ts +0 -14
- package/src/modules/useSliceMachineActions.ts +70 -89
- package/src/modules/userContext/index.ts +3 -3
- package/src/redux/saga.ts +0 -4
- package/test/src/modules/__fixtures__/serverState.ts +1 -2
- package/test/src/modules/availableCustomTypes/index.test.ts +2 -243
- package/test/src/modules/slices.test.ts +1 -267
- package/out/_next/static/chunks/18539003-5cc559566b738915.js +0 -97
- package/out/_next/static/chunks/25-37d6fa7f064be04b.js +0 -1
- package/out/_next/static/chunks/318-ce7e74f18f2d5b22.js +0 -1
- package/out/_next/static/chunks/376-7e784d6069e01654.js +0 -8
- package/out/_next/static/chunks/727-3a3bc32518778317.js +0 -1
- package/out/_next/static/chunks/731-501990ebe6a71b32.js +0 -1
- package/out/_next/static/chunks/904-9a4c1264727ae89a.js +0 -1
- package/out/_next/static/chunks/pages/_app-cd9d4c76a4abc11a.js +0 -432
- package/out/_next/static/chunks/pages/changes-2a976f9efeb719aa.js +0 -1
- package/out/_next/static/chunks/pages/index-64cfb8b0948ba8ef.js +0 -1
- package/out/_next/static/chunks/pages/slices/[lib]/[sliceName]/[variation]-b2a9d773ce5d7739.js +0 -1
- package/out/_next/static/chunks/pages/slices-e9c70c082ca3e3f9.js +0 -1
- package/out/_next/static/css/200244e3e0463c39.css +0 -1
- package/out/_next/static/m6psnC5u1xT7Y6osWHKjp/_buildManifest.js +0 -1
- package/src/features/customTypes/__tests__/createCustomType.test.ts +0 -159
- package/src/features/customTypes/customTypesTable/createCustomType.ts +0 -107
- package/test/components/CreateCustomTypeModal.test.tsx +0 -73
- /package/out/_next/static/{m6psnC5u1xT7Y6osWHKjp → Dz7wwTNVdXvoAjySXz7mE}/_ssgManifest.js +0 -0
|
@@ -1,8 +1,10 @@
|
|
|
1
|
+
import { toast } from "react-toastify";
|
|
1
2
|
import { CustomType } from "@prismicio/types-internal/lib/customtypes";
|
|
3
|
+
|
|
2
4
|
import { managerClient } from "@src/managerClient";
|
|
3
|
-
import { CUSTOM_TYPES_MESSAGES } from "../customTypesMessages";
|
|
4
5
|
import { CustomTypeFormat } from "@slicemachine/manager";
|
|
5
|
-
|
|
6
|
+
|
|
7
|
+
import { CUSTOM_TYPES_MESSAGES } from "../customTypesMessages";
|
|
6
8
|
|
|
7
9
|
type RenameCustomTypeArgs = {
|
|
8
10
|
model: CustomType;
|
|
@@ -23,10 +25,14 @@ export async function renameCustomType({
|
|
|
23
25
|
label: newLabel,
|
|
24
26
|
};
|
|
25
27
|
|
|
26
|
-
await managerClient.customTypes.renameCustomType({
|
|
28
|
+
const { errors } = await managerClient.customTypes.renameCustomType({
|
|
27
29
|
model: renamedCustomType,
|
|
28
30
|
});
|
|
29
31
|
|
|
32
|
+
if (errors.length > 0) {
|
|
33
|
+
throw errors;
|
|
34
|
+
}
|
|
35
|
+
|
|
30
36
|
onSuccess(renamedCustomType);
|
|
31
37
|
|
|
32
38
|
toast.success(
|
|
@@ -40,8 +46,8 @@ export async function renameCustomType({
|
|
|
40
46
|
start: true,
|
|
41
47
|
plural: false,
|
|
42
48
|
})} could not be renamed`;
|
|
43
|
-
console.error(errorMessage, e);
|
|
44
49
|
|
|
50
|
+
console.error(errorMessage, e);
|
|
45
51
|
toast.error(errorMessage);
|
|
46
52
|
}
|
|
47
53
|
}
|
|
@@ -39,7 +39,9 @@ export const CustomTypesBuilderPage: FC = () => {
|
|
|
39
39
|
);
|
|
40
40
|
|
|
41
41
|
useEffect(() => {
|
|
42
|
-
//
|
|
42
|
+
// This is a fallback in case the user try to directly access a custom type
|
|
43
|
+
// that don't exist
|
|
44
|
+
// TODO(DT-1801): implement a 404 page.
|
|
43
45
|
if (!selectedCustomType || !hasLocal(selectedCustomType)) {
|
|
44
46
|
void router.replace("/");
|
|
45
47
|
}
|
|
@@ -11,16 +11,9 @@ import {
|
|
|
11
11
|
} from "@src/components/Table";
|
|
12
12
|
import { ReusableIcon } from "@src/icons/ReusableIcon";
|
|
13
13
|
import { UniqueIcon } from "@src/icons/UniqueIcon";
|
|
14
|
-
import useSliceMachineActions from "@src/modules/useSliceMachineActions";
|
|
15
14
|
import { type CustomType } from "@prismicio/types-internal/lib/customtypes";
|
|
16
15
|
import { type CustomTypeFormat } from "@slicemachine/manager";
|
|
17
16
|
import { CUSTOM_TYPES_MESSAGES } from "@src/features/customTypes/customTypesMessages";
|
|
18
|
-
import { CUSTOM_TYPES_CONFIG } from "../customTypesConfig";
|
|
19
|
-
import {
|
|
20
|
-
useCustomTypes,
|
|
21
|
-
useCustomTypesAutoRevalidation,
|
|
22
|
-
} from "./useCustomTypes";
|
|
23
|
-
|
|
24
17
|
import {
|
|
25
18
|
BlankSlate,
|
|
26
19
|
BlankSlateImage,
|
|
@@ -29,18 +22,25 @@ import {
|
|
|
29
22
|
BlankSlateActions,
|
|
30
23
|
BlankSlateContent,
|
|
31
24
|
} from "@src/components/BlankSlate";
|
|
25
|
+
|
|
26
|
+
import { CUSTOM_TYPES_CONFIG } from "../customTypesConfig";
|
|
32
27
|
import { EditDropdown } from "../EditDropdown";
|
|
28
|
+
import {
|
|
29
|
+
useCustomTypes,
|
|
30
|
+
useCustomTypesAutoRevalidation,
|
|
31
|
+
} from "./useCustomTypes";
|
|
33
32
|
|
|
34
33
|
type CustomTypesTableProps = {
|
|
35
34
|
format: CustomTypeFormat;
|
|
36
35
|
isCreatingCustomType: boolean;
|
|
36
|
+
openCreateCustomTypeModal: () => void;
|
|
37
37
|
};
|
|
38
38
|
|
|
39
39
|
export const CustomTypesTable: FC<CustomTypesTableProps> = ({
|
|
40
40
|
format,
|
|
41
41
|
isCreatingCustomType,
|
|
42
|
+
openCreateCustomTypeModal,
|
|
42
43
|
}) => {
|
|
43
|
-
const { openCreateCustomTypeModal } = useSliceMachineActions();
|
|
44
44
|
const router = useRouter();
|
|
45
45
|
const { customTypes, updateCustomTypes } = useCustomTypes(format);
|
|
46
46
|
const sortedCustomTypes = customTypes.sort(
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { type FC, Suspense } from "react";
|
|
1
|
+
import { type FC, Suspense, useState } from "react";
|
|
2
2
|
import {
|
|
3
3
|
Button,
|
|
4
4
|
ErrorBoundary,
|
|
@@ -7,7 +7,6 @@ import {
|
|
|
7
7
|
DefaultErrorMessage,
|
|
8
8
|
} from "@prismicio/editor-ui";
|
|
9
9
|
import Head from "next/head";
|
|
10
|
-
import { useSelector } from "react-redux";
|
|
11
10
|
|
|
12
11
|
import {
|
|
13
12
|
AppLayout,
|
|
@@ -16,13 +15,10 @@ import {
|
|
|
16
15
|
AppLayoutContent,
|
|
17
16
|
AppLayoutHeader,
|
|
18
17
|
} from "@components/AppLayout";
|
|
19
|
-
import useSliceMachineActions from "@src/modules/useSliceMachineActions";
|
|
20
|
-
import { isLoading } from "@src/modules/loading";
|
|
21
|
-
import { type SliceMachineStoreType } from "@src/redux/type";
|
|
22
|
-
import { LoadingKeysEnum } from "@src/modules/loading/types";
|
|
23
18
|
import { CreateCustomTypeModal } from "@components/Forms/CreateCustomTypeModal";
|
|
24
19
|
import { CUSTOM_TYPES_MESSAGES } from "@src/features/customTypes/customTypesMessages";
|
|
25
20
|
import type { CustomTypeFormat } from "@slicemachine/manager";
|
|
21
|
+
|
|
26
22
|
import { CustomTypesTable } from "./CustomTypesTable";
|
|
27
23
|
|
|
28
24
|
type CustomTypesTablePageProps = {
|
|
@@ -33,15 +29,9 @@ export const CustomTypesTablePage: FC<CustomTypesTablePageProps> = ({
|
|
|
33
29
|
format,
|
|
34
30
|
}) => {
|
|
35
31
|
const customTypesMessages = CUSTOM_TYPES_MESSAGES[format];
|
|
36
|
-
const
|
|
37
|
-
const
|
|
38
|
-
(
|
|
39
|
-
isCreatingCustomType: isLoading(
|
|
40
|
-
store,
|
|
41
|
-
LoadingKeysEnum.CREATE_CUSTOM_TYPE,
|
|
42
|
-
),
|
|
43
|
-
}),
|
|
44
|
-
);
|
|
32
|
+
const [isCreatingCustomType, setIsCreatingCustomType] = useState(false);
|
|
33
|
+
const [isCreateCustomTypeModalOpen, setIsCreateCustomTypeModalOpen] =
|
|
34
|
+
useState(false);
|
|
45
35
|
|
|
46
36
|
return (
|
|
47
37
|
<>
|
|
@@ -101,7 +91,9 @@ export const CustomTypesTablePage: FC<CustomTypesTablePageProps> = ({
|
|
|
101
91
|
<Button
|
|
102
92
|
data-cy="create-ct"
|
|
103
93
|
loading={isCreatingCustomType}
|
|
104
|
-
onClick={
|
|
94
|
+
onClick={() => {
|
|
95
|
+
setIsCreateCustomTypeModalOpen(true);
|
|
96
|
+
}}
|
|
105
97
|
startIcon="add"
|
|
106
98
|
>
|
|
107
99
|
Create
|
|
@@ -113,8 +105,17 @@ export const CustomTypesTablePage: FC<CustomTypesTablePageProps> = ({
|
|
|
113
105
|
<CustomTypesTable
|
|
114
106
|
format={format}
|
|
115
107
|
isCreatingCustomType={isCreatingCustomType}
|
|
108
|
+
openCreateCustomTypeModal={() => {
|
|
109
|
+
setIsCreateCustomTypeModalOpen(true);
|
|
110
|
+
}}
|
|
111
|
+
/>
|
|
112
|
+
<CreateCustomTypeModal
|
|
113
|
+
format={format}
|
|
114
|
+
isCreating={isCreatingCustomType}
|
|
115
|
+
isOpen={isCreateCustomTypeModalOpen}
|
|
116
|
+
onCreateChange={setIsCreatingCustomType}
|
|
117
|
+
onOpenChange={setIsCreateCustomTypeModalOpen}
|
|
116
118
|
/>
|
|
117
|
-
<CreateCustomTypeModal format={format} />
|
|
118
119
|
</Box>
|
|
119
120
|
</AppLayoutContent>
|
|
120
121
|
</AppLayout>
|
|
@@ -13,6 +13,6 @@ export async function setEnvironment(
|
|
|
13
13
|
environment: environment.domain,
|
|
14
14
|
});
|
|
15
15
|
|
|
16
|
-
revalidateData(getActiveEnvironment, []);
|
|
17
|
-
revalidateData(getLegacySliceMachineState, []);
|
|
16
|
+
void revalidateData(getActiveEnvironment, []);
|
|
17
|
+
void revalidateData(getLegacySliceMachineState, []);
|
|
18
18
|
}
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
import { Box } from "@prismicio/editor-ui";
|
|
2
|
+
import Head from "next/head";
|
|
3
|
+
import { useRouter } from "next/router";
|
|
4
|
+
import { type FC, useEffect } from "react";
|
|
5
|
+
|
|
6
|
+
import {
|
|
7
|
+
AppLayout,
|
|
8
|
+
AppLayoutBreadcrumb,
|
|
9
|
+
AppLayoutContent,
|
|
10
|
+
AppLayoutHeader,
|
|
11
|
+
} from "@components/AppLayout";
|
|
12
|
+
import { ConnectGitRepository } from "@src/features/settings/git/ConnectGitRepository";
|
|
13
|
+
import { useGitIntegrationExperiment } from "@src/features/settings/git/useGitIntegrationExperiment";
|
|
14
|
+
|
|
15
|
+
export const SettingsPage: FC = () => {
|
|
16
|
+
const gitIntegrationExperiment = useGitIntegrationExperiment();
|
|
17
|
+
const router = useRouter();
|
|
18
|
+
|
|
19
|
+
// TODO(DT-1801): implement a 404 page.
|
|
20
|
+
useEffect(() => {
|
|
21
|
+
if (!gitIntegrationExperiment.eligible) {
|
|
22
|
+
void router.replace("/");
|
|
23
|
+
}
|
|
24
|
+
}, [gitIntegrationExperiment.eligible, router]);
|
|
25
|
+
|
|
26
|
+
if (!gitIntegrationExperiment.eligible) {
|
|
27
|
+
return null;
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
return (
|
|
31
|
+
<>
|
|
32
|
+
<Head>
|
|
33
|
+
<title>Settings - Slice Machine</title>
|
|
34
|
+
</Head>
|
|
35
|
+
<AppLayout>
|
|
36
|
+
<AppLayoutHeader>
|
|
37
|
+
<AppLayoutBreadcrumb folder="Settings" />
|
|
38
|
+
</AppLayoutHeader>
|
|
39
|
+
<AppLayoutContent>
|
|
40
|
+
<Box flexDirection="column" maxWidth={600}>
|
|
41
|
+
<ConnectGitRepository />
|
|
42
|
+
</Box>
|
|
43
|
+
</AppLayoutContent>
|
|
44
|
+
</AppLayout>
|
|
45
|
+
</>
|
|
46
|
+
);
|
|
47
|
+
};
|
|
@@ -0,0 +1,126 @@
|
|
|
1
|
+
import {
|
|
2
|
+
Button,
|
|
3
|
+
ErrorBoundary,
|
|
4
|
+
IconButton,
|
|
5
|
+
Tooltip,
|
|
6
|
+
} from "@prismicio/editor-ui";
|
|
7
|
+
import {
|
|
8
|
+
// isUnauthenticatedError,
|
|
9
|
+
isUnauthorizedError,
|
|
10
|
+
} from "@slicemachine/manager/client";
|
|
11
|
+
import { type FC, type ReactNode, Suspense } from "react";
|
|
12
|
+
|
|
13
|
+
import {
|
|
14
|
+
FieldSet,
|
|
15
|
+
FieldSetContent,
|
|
16
|
+
FieldSetFooter,
|
|
17
|
+
FieldSetLegend,
|
|
18
|
+
} from "@src/components/FieldSet";
|
|
19
|
+
import { ConnectGitRepositoryBlankSlate } from "@src/features/settings/git/ConnectGitRepositoryBlankSlate";
|
|
20
|
+
import { GitProviderConnectButtons } from "@src/features/settings/git/GitProviderConnectButtons";
|
|
21
|
+
import { GitRepositoriesList } from "@src/features/settings/git/GitRepositoriesList";
|
|
22
|
+
import {
|
|
23
|
+
GitRepositoriesSearch,
|
|
24
|
+
GitRepositoriesSearchSkeleton,
|
|
25
|
+
} from "@src/features/settings/git/GitRepositoriesSearch";
|
|
26
|
+
import { useGitOwners } from "@src/features/settings/git/useGitOwners";
|
|
27
|
+
import { useLinkedGitRepos } from "@src/features/settings/git/useLinkedGitRepos";
|
|
28
|
+
import { useUser } from "@src/hooks/useUser";
|
|
29
|
+
import useSliceMachineActions from "@src/modules/useSliceMachineActions";
|
|
30
|
+
|
|
31
|
+
export const ConnectGitRepository: FC = () => (
|
|
32
|
+
<FieldSet>
|
|
33
|
+
<FieldSetLegend>Connected Git Repository</FieldSetLegend>
|
|
34
|
+
<ErrorBoundary renderError={renderError}>
|
|
35
|
+
<Suspense fallback={<GitRepositoriesSearchSkeleton gitOwnerSelect />}>
|
|
36
|
+
<Content />
|
|
37
|
+
</Suspense>
|
|
38
|
+
</ErrorBoundary>
|
|
39
|
+
<FieldSetFooter
|
|
40
|
+
action={
|
|
41
|
+
<Tooltip content="Documentation is coming soon." side="bottom">
|
|
42
|
+
<IconButton disabled icon="openInNew" />
|
|
43
|
+
</Tooltip>
|
|
44
|
+
}
|
|
45
|
+
>
|
|
46
|
+
Learn more about Prismic for Git
|
|
47
|
+
</FieldSetFooter>
|
|
48
|
+
</FieldSet>
|
|
49
|
+
);
|
|
50
|
+
|
|
51
|
+
const Content: FC = () => {
|
|
52
|
+
// TODO(TD-1952): the `useUser` hook should be replaced with a mechanism that
|
|
53
|
+
// automatically revalidates the `Suspense` cache whenever the Redux
|
|
54
|
+
// authentication status changes.
|
|
55
|
+
const { isLoggedIn } = useUser();
|
|
56
|
+
return isLoggedIn ? <LoggedInContent /> : <UnauthenticatedErrorContent />;
|
|
57
|
+
};
|
|
58
|
+
|
|
59
|
+
const LoggedInContent: FC = () => {
|
|
60
|
+
const { linkedGitRepos } = useLinkedGitRepos();
|
|
61
|
+
return linkedGitRepos.length > 0 ? (
|
|
62
|
+
<GitRepositoriesList mode="unlink" repos={linkedGitRepos} />
|
|
63
|
+
) : (
|
|
64
|
+
<UnlinkedRepositoryContent />
|
|
65
|
+
);
|
|
66
|
+
};
|
|
67
|
+
|
|
68
|
+
const UnlinkedRepositoryContent: FC = () => {
|
|
69
|
+
const owners = useGitOwners();
|
|
70
|
+
return owners.length > 0 ? (
|
|
71
|
+
<GitRepositoriesSearch owners={owners} />
|
|
72
|
+
) : (
|
|
73
|
+
<FieldSetContent>
|
|
74
|
+
<GitProviderConnectButtons />
|
|
75
|
+
</FieldSetContent>
|
|
76
|
+
);
|
|
77
|
+
};
|
|
78
|
+
|
|
79
|
+
function renderError(error: unknown, reset: () => void): ReactNode {
|
|
80
|
+
// if (isUnauthenticatedError(error)) {
|
|
81
|
+
// return <UnauthenticatedErrorContent />;
|
|
82
|
+
// } else
|
|
83
|
+
if (isUnauthorizedError(error)) {
|
|
84
|
+
return <UnauthorizedErrorContent />;
|
|
85
|
+
} else {
|
|
86
|
+
return <UnknownErrorContent reset={reset} />;
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
const UnauthenticatedErrorContent: FC = () => {
|
|
91
|
+
const { openLoginModal } = useSliceMachineActions();
|
|
92
|
+
return (
|
|
93
|
+
<FieldSetContent>
|
|
94
|
+
<ConnectGitRepositoryBlankSlate
|
|
95
|
+
title="It seems like you are logged out"
|
|
96
|
+
description="Log in to connect a Git repository."
|
|
97
|
+
action={<Button onClick={openLoginModal}>Log in to Prismic</Button>}
|
|
98
|
+
/>
|
|
99
|
+
</FieldSetContent>
|
|
100
|
+
);
|
|
101
|
+
};
|
|
102
|
+
|
|
103
|
+
const UnauthorizedErrorContent: FC = () => (
|
|
104
|
+
<FieldSetContent>
|
|
105
|
+
<ConnectGitRepositoryBlankSlate
|
|
106
|
+
title="It seems like you do not have permission"
|
|
107
|
+
description="An owner or admin is required to connect a Git repository."
|
|
108
|
+
/>
|
|
109
|
+
</FieldSetContent>
|
|
110
|
+
);
|
|
111
|
+
|
|
112
|
+
type UnknownErrorContentProps = { reset: () => void };
|
|
113
|
+
|
|
114
|
+
const UnknownErrorContent: FC<UnknownErrorContentProps> = ({ reset }) => (
|
|
115
|
+
<FieldSetContent>
|
|
116
|
+
<ConnectGitRepositoryBlankSlate
|
|
117
|
+
title="Unable to fetch data"
|
|
118
|
+
description="An error occurred while fetching the list of connected Git repositories."
|
|
119
|
+
action={
|
|
120
|
+
<Button color="grey" onClick={reset}>
|
|
121
|
+
Retry
|
|
122
|
+
</Button>
|
|
123
|
+
}
|
|
124
|
+
/>
|
|
125
|
+
</FieldSetContent>
|
|
126
|
+
);
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
import { Box, ButtonGroup, Text } from "@prismicio/editor-ui";
|
|
2
|
+
import type { FC, ReactNode } from "react";
|
|
3
|
+
|
|
4
|
+
type ConnectGitRepositoryBlankSlateProps = {
|
|
5
|
+
title: string;
|
|
6
|
+
description: string;
|
|
7
|
+
action?: ReactNode;
|
|
8
|
+
};
|
|
9
|
+
|
|
10
|
+
export const ConnectGitRepositoryBlankSlate: FC<
|
|
11
|
+
ConnectGitRepositoryBlankSlateProps
|
|
12
|
+
> = ({ title, description, action }) => (
|
|
13
|
+
<Box
|
|
14
|
+
flexDirection="column"
|
|
15
|
+
/*
|
|
16
|
+
* TODO: these `padding` values actually don't match Figma, but they are the
|
|
17
|
+
* closest allowed by the `Box` component.
|
|
18
|
+
*/
|
|
19
|
+
padding={{ block: 72, inline: 100 }}
|
|
20
|
+
>
|
|
21
|
+
<Text align="center" variant="emphasized">
|
|
22
|
+
{title}
|
|
23
|
+
</Text>
|
|
24
|
+
<Text align="center" color="grey11">
|
|
25
|
+
{description}
|
|
26
|
+
</Text>
|
|
27
|
+
{Boolean(action) ? (
|
|
28
|
+
<ButtonGroup sx={{ alignSelf: "center", marginTop: 8 }}>
|
|
29
|
+
{action}
|
|
30
|
+
</ButtonGroup>
|
|
31
|
+
) : undefined}
|
|
32
|
+
</Box>
|
|
33
|
+
);
|
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
import { Select, SelectItem, tokens } from "@prismicio/editor-ui";
|
|
2
|
+
import type { GitOwner } from "@slicemachine/manager";
|
|
3
|
+
import type { ComponentProps, FC } from "react";
|
|
4
|
+
|
|
5
|
+
import { gitProviderToConfig } from "@src/features/settings/git/GitProvider";
|
|
6
|
+
|
|
7
|
+
type GitOwnerSelectProps = {
|
|
8
|
+
disabled?: boolean;
|
|
9
|
+
owners?: GitOwner[];
|
|
10
|
+
selectedOwner?: GitOwner;
|
|
11
|
+
onSelectedOwnerChange?: (selectedOwner: GitOwner) => void;
|
|
12
|
+
sx?: SX;
|
|
13
|
+
};
|
|
14
|
+
|
|
15
|
+
export const GitOwnerSelect: FC<GitOwnerSelectProps> = ({
|
|
16
|
+
disabled,
|
|
17
|
+
owners = [],
|
|
18
|
+
selectedOwner,
|
|
19
|
+
onSelectedOwnerChange,
|
|
20
|
+
sx,
|
|
21
|
+
}) => (
|
|
22
|
+
<Select
|
|
23
|
+
color="grey"
|
|
24
|
+
constrainContentWidth
|
|
25
|
+
disabled={disabled}
|
|
26
|
+
flexContent
|
|
27
|
+
onValueChange={(value) => {
|
|
28
|
+
const [provider, id] = parseGitOwnerKey(value);
|
|
29
|
+
const selectedOwner = owners.find(
|
|
30
|
+
(owner) => owner.provider === provider && owner.id === id,
|
|
31
|
+
);
|
|
32
|
+
if (selectedOwner) {
|
|
33
|
+
onSelectedOwnerChange?.(selectedOwner);
|
|
34
|
+
}
|
|
35
|
+
}}
|
|
36
|
+
placeholder="Owner"
|
|
37
|
+
renderStartIcon={() => <GitOwnerIcon owner={selectedOwner} />}
|
|
38
|
+
size="large"
|
|
39
|
+
sx={sx}
|
|
40
|
+
value={selectedOwner ? formatGitOwnerKey(selectedOwner) : undefined}
|
|
41
|
+
>
|
|
42
|
+
{owners.map((owner) => (
|
|
43
|
+
<SelectItem
|
|
44
|
+
key={formatGitOwnerKey(owner)}
|
|
45
|
+
renderStartIcon={() => <GitOwnerIcon owner={owner} />}
|
|
46
|
+
size="large"
|
|
47
|
+
value={formatGitOwnerKey(owner)}
|
|
48
|
+
>
|
|
49
|
+
{owner.name}
|
|
50
|
+
</SelectItem>
|
|
51
|
+
))}
|
|
52
|
+
</Select>
|
|
53
|
+
);
|
|
54
|
+
|
|
55
|
+
type GitOwnerIconProps = { owner: GitOwner | undefined };
|
|
56
|
+
|
|
57
|
+
const GitOwnerIcon: FC<GitOwnerIconProps> = ({ owner }) => {
|
|
58
|
+
const { Icon } = gitProviderToConfig[owner?.provider ?? "gitHub"];
|
|
59
|
+
return <Icon color={tokens.color.greyLight11} />;
|
|
60
|
+
};
|
|
61
|
+
|
|
62
|
+
function formatGitOwnerKey(owner: GitOwner): string {
|
|
63
|
+
return `${owner.provider}@${owner.id}`;
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
function parseGitOwnerKey(key: string): string[] {
|
|
67
|
+
return key.split("@");
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
// TODO(DT-1928): export the `SX` type from `@prismicio/editor-ui`.
|
|
71
|
+
type SX = ComponentProps<typeof Select>["sx"];
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
import { BitbucketIcon } from "@src/icons/BitbucketIcon";
|
|
2
|
+
import { GitHubIcon } from "@src/icons/GitHubIcon";
|
|
3
|
+
import { GitLabIcon } from "@src/icons/GitLabIcon";
|
|
4
|
+
import { managerClient } from "@src/managerClient";
|
|
5
|
+
|
|
6
|
+
export type GitProvider = keyof typeof gitProviderToConfig;
|
|
7
|
+
|
|
8
|
+
const GITHUB_APP_SLUG = "prismic-io";
|
|
9
|
+
|
|
10
|
+
export const gitProviderToConfig = {
|
|
11
|
+
gitHub: {
|
|
12
|
+
connect: async () => {
|
|
13
|
+
const state = await managerClient.git.createGitHubAuthState();
|
|
14
|
+
const url = new URL(
|
|
15
|
+
`https://github.com/apps/${GITHUB_APP_SLUG}/installations/new`,
|
|
16
|
+
);
|
|
17
|
+
url.searchParams.set("state", state.key);
|
|
18
|
+
window.open(url, "git-hub-app-installation");
|
|
19
|
+
},
|
|
20
|
+
Icon: GitHubIcon,
|
|
21
|
+
name: "GitHub",
|
|
22
|
+
supported: true,
|
|
23
|
+
},
|
|
24
|
+
bitbucket: {
|
|
25
|
+
connect: () => {
|
|
26
|
+
throw new Error("Not implemented.");
|
|
27
|
+
},
|
|
28
|
+
Icon: BitbucketIcon,
|
|
29
|
+
name: "Bitbucket",
|
|
30
|
+
supported: false,
|
|
31
|
+
},
|
|
32
|
+
gitLab: {
|
|
33
|
+
connect: () => {
|
|
34
|
+
throw new Error("Not implemented.");
|
|
35
|
+
},
|
|
36
|
+
Icon: GitLabIcon,
|
|
37
|
+
name: "GitLab",
|
|
38
|
+
supported: false,
|
|
39
|
+
},
|
|
40
|
+
};
|
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
import { keys } from "@prismicio/editor-support/Object";
|
|
2
|
+
import { Button, ButtonGroup, Text, tokens } from "@prismicio/editor-ui";
|
|
3
|
+
import { type ComponentProps, type FC, useState } from "react";
|
|
4
|
+
import { toast } from "react-toastify";
|
|
5
|
+
|
|
6
|
+
import {
|
|
7
|
+
type GitProvider,
|
|
8
|
+
gitProviderToConfig,
|
|
9
|
+
} from "@src/features/settings/git/GitProvider";
|
|
10
|
+
|
|
11
|
+
export const GitProviderConnectButtons: FC = () => (
|
|
12
|
+
<ButtonGroup>
|
|
13
|
+
{keys(gitProviderToConfig).map((provider) => (
|
|
14
|
+
<GitProviderConnectButton
|
|
15
|
+
key={provider}
|
|
16
|
+
provider={provider}
|
|
17
|
+
sx={{ flexBasis: 0, flexGrow: 1 }}
|
|
18
|
+
/>
|
|
19
|
+
))}
|
|
20
|
+
</ButtonGroup>
|
|
21
|
+
);
|
|
22
|
+
|
|
23
|
+
type GitProviderConnectButtonProps = { provider: GitProvider; sx?: SX };
|
|
24
|
+
|
|
25
|
+
const GitProviderConnectButton: FC<GitProviderConnectButtonProps> = ({
|
|
26
|
+
provider,
|
|
27
|
+
sx,
|
|
28
|
+
}) => {
|
|
29
|
+
const { connect, Icon, name, supported } = gitProviderToConfig[provider];
|
|
30
|
+
const [loading, setLoading] = useState(false);
|
|
31
|
+
return (
|
|
32
|
+
<Button
|
|
33
|
+
color="grey"
|
|
34
|
+
disabled={!supported}
|
|
35
|
+
loading={loading}
|
|
36
|
+
onClick={() => {
|
|
37
|
+
void (async () => {
|
|
38
|
+
setLoading(true);
|
|
39
|
+
try {
|
|
40
|
+
await connect();
|
|
41
|
+
} catch (error) {
|
|
42
|
+
const message = `Could not connect to ${name}`;
|
|
43
|
+
console.error(message, error);
|
|
44
|
+
toast.error(message);
|
|
45
|
+
setLoading(false);
|
|
46
|
+
}
|
|
47
|
+
})();
|
|
48
|
+
}}
|
|
49
|
+
renderStartIcon={() => <Icon color={tokens.color.greyLight11} />}
|
|
50
|
+
sx={sx}
|
|
51
|
+
>
|
|
52
|
+
{name}
|
|
53
|
+
{supported ? undefined : (
|
|
54
|
+
<Text color="inherit" variant="small">
|
|
55
|
+
{" (soon)"}
|
|
56
|
+
</Text>
|
|
57
|
+
)}
|
|
58
|
+
</Button>
|
|
59
|
+
);
|
|
60
|
+
};
|
|
61
|
+
|
|
62
|
+
// TODO(DT-1928): export the `SX` type from `@prismicio/editor-ui`.
|
|
63
|
+
type SX = ComponentProps<typeof Button>["sx"];
|
|
@@ -0,0 +1,76 @@
|
|
|
1
|
+
import { Button, Skeleton, Text } from "@prismicio/editor-ui";
|
|
2
|
+
import type { GitRepo, GitRepoSpecifier } from "@slicemachine/manager";
|
|
3
|
+
import type { FC } from "react";
|
|
4
|
+
|
|
5
|
+
import { FieldSetList, FieldSetListItem } from "@src/components/FieldSet";
|
|
6
|
+
import { RelativeTime } from "@src/components/RelativeTime";
|
|
7
|
+
import { GitRepositoryConnectDialog } from "@src/features/settings/git/GitRepositoryConnectDialog";
|
|
8
|
+
import { GitRepositoryDisconnectDialog } from "@src/features/settings/git/GitRepositoryDisconnectDialog";
|
|
9
|
+
import { useLinkedGitRepos } from "@src/features/settings/git/useLinkedGitRepos";
|
|
10
|
+
|
|
11
|
+
type GitRepositoriesListProps =
|
|
12
|
+
| { mode: "link"; repos: GitRepo[] }
|
|
13
|
+
| { mode: "unlink"; repos: GitRepoSpecifier[] };
|
|
14
|
+
|
|
15
|
+
export const GitRepositoriesList: FC<GitRepositoriesListProps> = ({
|
|
16
|
+
mode,
|
|
17
|
+
repos,
|
|
18
|
+
}) => {
|
|
19
|
+
const { linkRepo, unlinkRepo } = useLinkedGitRepos();
|
|
20
|
+
return (
|
|
21
|
+
<FieldSetList>
|
|
22
|
+
{mode === "link"
|
|
23
|
+
? repos.map((repo) => (
|
|
24
|
+
<FieldSetListItem
|
|
25
|
+
action={
|
|
26
|
+
<GitRepositoryConnectDialog
|
|
27
|
+
linkRepo={linkRepo}
|
|
28
|
+
repo={repo}
|
|
29
|
+
trigger={<Button color="grey">Connect</Button>}
|
|
30
|
+
/>
|
|
31
|
+
}
|
|
32
|
+
key={`${repo.provider}@${repo.id}`}
|
|
33
|
+
>
|
|
34
|
+
{repo.name}
|
|
35
|
+
<Text color="grey11">
|
|
36
|
+
{" • "}
|
|
37
|
+
<RelativeTime date={repo.pushedAt} />
|
|
38
|
+
</Text>
|
|
39
|
+
</FieldSetListItem>
|
|
40
|
+
))
|
|
41
|
+
: repos.map((repo) => (
|
|
42
|
+
<FieldSetListItem
|
|
43
|
+
action={
|
|
44
|
+
<GitRepositoryDisconnectDialog
|
|
45
|
+
repo={repo}
|
|
46
|
+
trigger={<Button color="grey">Disconnect</Button>}
|
|
47
|
+
unlinkRepo={unlinkRepo}
|
|
48
|
+
/>
|
|
49
|
+
}
|
|
50
|
+
key={`${repo.provider}@${repo.owner}/${repo.name}`}
|
|
51
|
+
>
|
|
52
|
+
{repo.name}
|
|
53
|
+
</FieldSetListItem>
|
|
54
|
+
))}
|
|
55
|
+
</FieldSetList>
|
|
56
|
+
);
|
|
57
|
+
};
|
|
58
|
+
|
|
59
|
+
export const GitRepositoriesListSkeleton: FC = () => (
|
|
60
|
+
<FieldSetList>
|
|
61
|
+
{[...Array(4).keys()].map((index) => (
|
|
62
|
+
<FieldSetListItem
|
|
63
|
+
action={<Skeleton height={32} width={67.59} />}
|
|
64
|
+
key={index}
|
|
65
|
+
>
|
|
66
|
+
<Skeleton
|
|
67
|
+
height={24}
|
|
68
|
+
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
|
|
69
|
+
// @ts-ignore TODO(DT-1918): add `verticalAlign: "middle"` to the `sx` prop.
|
|
70
|
+
sx={{ verticalAlign: "middle" }}
|
|
71
|
+
width={129.92}
|
|
72
|
+
/>
|
|
73
|
+
</FieldSetListItem>
|
|
74
|
+
))}
|
|
75
|
+
</FieldSetList>
|
|
76
|
+
);
|