slice-machine-ui 1.16.1-dev-next-release.0 → 1.16.1-dev-next-release.2
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/lib/builders/CustomTypeBuilder/SliceZone/List.tsx +12 -7
- package/lib/builders/CustomTypeBuilder/SliceZone/index.tsx +14 -12
- package/out/404.html +1 -1
- package/out/[lib]/[sliceName]/[variation]/screenshot.html +1 -1
- package/out/[lib]/[sliceName]/[variation]/simulator.html +1 -1
- package/out/[lib]/[sliceName]/[variation].html +1 -1
- package/out/_next/static/chunks/933-b69f0da068255b0d.js +1 -0
- package/out/_next/static/chunks/pages/{_app-aff70c74b7bb0ef6.js → _app-93364eb6be33939e.js} +5 -5
- package/out/_next/static/chunks/pages/custom-types/{[customTypeId]-1baa6af0ddf0ab6b.js → [customTypeId]-375f234af61fdf94.js} +1 -1
- package/out/_next/static/chunks/pages/page-types/{[pageTypeId]-708b782db680b804.js → [pageTypeId]-7bf582e0368b1713.js} +1 -1
- package/out/_next/static/{h37WLcHVDHmbz47jhppT5 → i9dDW3KALPc1gFSmc9aLo}/_buildManifest.js +1 -1
- 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/page-types/[pageTypeId].html +1 -1
- package/out/slices.html +1 -1
- package/package.json +4 -4
- package/src/domain/slice.ts +51 -3
- package/src/features/slices/convertLegacySlice/ConvertLegacySliceAsNewSliceDialog.tsx +151 -0
- package/src/features/slices/convertLegacySlice/ConvertLegacySliceAsNewVariationDialog.tsx +233 -0
- package/src/features/slices/convertLegacySlice/ConvertLegacySliceButton.css.ts +15 -0
- package/src/features/slices/convertLegacySlice/ConvertLegacySliceButton.tsx +268 -0
- package/src/features/slices/convertLegacySlice/ConvertLegacySliceMergeWithIdenticalDialog.tsx +106 -0
- package/src/features/slices/convertLegacySlice/types.ts +37 -0
- package/src/features/slices/sliceCards/NonSharedSliceViewCard.tsx +22 -4
- package/out/_next/static/chunks/14-9205b47277b0e0f4.js +0 -1
- /package/out/_next/static/{h37WLcHVDHmbz47jhppT5 → i9dDW3KALPc1gFSmc9aLo}/_ssgManifest.js +0 -0
|
@@ -0,0 +1,151 @@
|
|
|
1
|
+
import type { FC } from "react";
|
|
2
|
+
import { useSelector } from "react-redux";
|
|
3
|
+
import { Formik } from "formik";
|
|
4
|
+
import {
|
|
5
|
+
Box,
|
|
6
|
+
Dialog,
|
|
7
|
+
DialogActions,
|
|
8
|
+
DialogContent,
|
|
9
|
+
DialogHeader,
|
|
10
|
+
ScrollArea,
|
|
11
|
+
FormInput,
|
|
12
|
+
Text,
|
|
13
|
+
Select,
|
|
14
|
+
SelectItem,
|
|
15
|
+
} from "@prismicio/editor-ui";
|
|
16
|
+
|
|
17
|
+
import { SliceMachineStoreType } from "@src/redux/type";
|
|
18
|
+
import { getRemoteSlices } from "@src/modules/slices";
|
|
19
|
+
import { pascalize } from "@lib/utils/str";
|
|
20
|
+
import { validateSliceModalValues as validateAsNewSliceValues } from "@components/Forms/formsValidator";
|
|
21
|
+
|
|
22
|
+
import * as styles from "./ConvertLegacySliceButton.css";
|
|
23
|
+
import { DialogProps } from "./types";
|
|
24
|
+
|
|
25
|
+
export const ConvertLegacySliceAsNewSliceDialog: FC<DialogProps> = ({
|
|
26
|
+
isOpen,
|
|
27
|
+
close,
|
|
28
|
+
onSubmit,
|
|
29
|
+
isLoading,
|
|
30
|
+
slice,
|
|
31
|
+
libraries,
|
|
32
|
+
}) => {
|
|
33
|
+
const { remoteSlices } = useSelector((store: SliceMachineStoreType) => ({
|
|
34
|
+
remoteSlices: getRemoteSlices(store),
|
|
35
|
+
}));
|
|
36
|
+
|
|
37
|
+
return (
|
|
38
|
+
<Dialog
|
|
39
|
+
open={isOpen}
|
|
40
|
+
onOpenChange={(open) => !open && close()}
|
|
41
|
+
size={{ width: 448, height: "auto" }}
|
|
42
|
+
>
|
|
43
|
+
<DialogHeader title="Upgrade slice" />
|
|
44
|
+
<DialogContent>
|
|
45
|
+
<Formik
|
|
46
|
+
validateOnChange
|
|
47
|
+
validateOnMount
|
|
48
|
+
initialValues={{
|
|
49
|
+
_init: false,
|
|
50
|
+
from: libraries[0]?.name,
|
|
51
|
+
sliceName: pascalize(slice.key),
|
|
52
|
+
}}
|
|
53
|
+
validate={(values) => {
|
|
54
|
+
return validateAsNewSliceValues(values, libraries, remoteSlices);
|
|
55
|
+
}}
|
|
56
|
+
onSubmit={(values) => {
|
|
57
|
+
onSubmit({ libraryID: values.from, sliceID: values.sliceName });
|
|
58
|
+
}}
|
|
59
|
+
>
|
|
60
|
+
{(formik) => {
|
|
61
|
+
return (
|
|
62
|
+
<form id="convert-legacy-slice-as-new-slice-dialog">
|
|
63
|
+
<Box display="flex" flexDirection="column">
|
|
64
|
+
<ScrollArea className={styles.scrollArea}>
|
|
65
|
+
<Text variant="normal" color="grey11">
|
|
66
|
+
This will create a new slice with the same fields. The new
|
|
67
|
+
slice will replace the legacy slice in all of your types,
|
|
68
|
+
and the existing slice content will be re-mapped to the
|
|
69
|
+
new slice.
|
|
70
|
+
</Text>
|
|
71
|
+
<Text variant="normal" color="grey11">
|
|
72
|
+
This will not migrate your component. You will need to do
|
|
73
|
+
that manually.
|
|
74
|
+
</Text>
|
|
75
|
+
<Box display="flex" flexDirection="column" gap={4}>
|
|
76
|
+
<label className={styles.label}>
|
|
77
|
+
<Text variant="bold">Display name*</Text>
|
|
78
|
+
{typeof formik.errors.sliceName === "string" ? (
|
|
79
|
+
<Text variant="small" color="tomato10">
|
|
80
|
+
{formik.errors.sliceName}
|
|
81
|
+
</Text>
|
|
82
|
+
) : null}
|
|
83
|
+
</label>
|
|
84
|
+
<FormInput
|
|
85
|
+
placeholder={`Pascalised Slice API ID (e.g. ${pascalize(
|
|
86
|
+
slice.key
|
|
87
|
+
)})`}
|
|
88
|
+
error={typeof formik.errors.sliceName === "string"}
|
|
89
|
+
value={formik.values.sliceName}
|
|
90
|
+
onValueChange={(value) =>
|
|
91
|
+
void formik.setFieldValue(
|
|
92
|
+
"sliceName",
|
|
93
|
+
value.slice(0, 30)
|
|
94
|
+
)
|
|
95
|
+
}
|
|
96
|
+
data-cy="slice-name-input"
|
|
97
|
+
/>
|
|
98
|
+
<Text variant="normal" color="grey11">
|
|
99
|
+
A display name for the slice
|
|
100
|
+
</Text>
|
|
101
|
+
</Box>
|
|
102
|
+
<Box display="flex" flexDirection="column" gap={4}>
|
|
103
|
+
<label className={styles.label}>
|
|
104
|
+
<Text variant="bold">Slice library*</Text>
|
|
105
|
+
{typeof formik.errors.from === "string" ? (
|
|
106
|
+
<Text variant="small" color="tomato10">
|
|
107
|
+
{formik.errors.from}
|
|
108
|
+
</Text>
|
|
109
|
+
) : null}
|
|
110
|
+
</label>
|
|
111
|
+
<Select
|
|
112
|
+
size="medium"
|
|
113
|
+
variant="secondary"
|
|
114
|
+
startIcon="folder"
|
|
115
|
+
flexContent={true}
|
|
116
|
+
value={formik.values.from}
|
|
117
|
+
onValueChange={(value) =>
|
|
118
|
+
value
|
|
119
|
+
? void formik.setFieldValue("from", value)
|
|
120
|
+
: null
|
|
121
|
+
}
|
|
122
|
+
>
|
|
123
|
+
{libraries.map((library) => (
|
|
124
|
+
<SelectItem key={library.name} value={library.name}>
|
|
125
|
+
{library.name}
|
|
126
|
+
</SelectItem>
|
|
127
|
+
))}
|
|
128
|
+
</Select>
|
|
129
|
+
<Text variant="normal" color="grey11">
|
|
130
|
+
The library where we'll store your slice
|
|
131
|
+
</Text>
|
|
132
|
+
</Box>
|
|
133
|
+
</ScrollArea>
|
|
134
|
+
<DialogActions
|
|
135
|
+
ok={{
|
|
136
|
+
text: "Upgrade",
|
|
137
|
+
onClick: () => void formik.submitForm(),
|
|
138
|
+
loading: isLoading,
|
|
139
|
+
disabled: !formik.isValid,
|
|
140
|
+
}}
|
|
141
|
+
cancel={{ text: "Cancel" }}
|
|
142
|
+
/>
|
|
143
|
+
</Box>
|
|
144
|
+
</form>
|
|
145
|
+
);
|
|
146
|
+
}}
|
|
147
|
+
</Formik>
|
|
148
|
+
</DialogContent>
|
|
149
|
+
</Dialog>
|
|
150
|
+
);
|
|
151
|
+
};
|
|
@@ -0,0 +1,233 @@
|
|
|
1
|
+
import { useState, type FC } from "react";
|
|
2
|
+
import { Formik } from "formik";
|
|
3
|
+
import {
|
|
4
|
+
Box,
|
|
5
|
+
Dialog,
|
|
6
|
+
DialogActions,
|
|
7
|
+
DialogContent,
|
|
8
|
+
DialogHeader,
|
|
9
|
+
ScrollArea,
|
|
10
|
+
FormInput,
|
|
11
|
+
Text,
|
|
12
|
+
Select,
|
|
13
|
+
SelectItem,
|
|
14
|
+
} from "@prismicio/editor-ui";
|
|
15
|
+
|
|
16
|
+
import { Variation } from "@models/common/Variation";
|
|
17
|
+
import { LibraryUI } from "@models/common/LibraryUI";
|
|
18
|
+
|
|
19
|
+
import * as styles from "./ConvertLegacySliceButton.css";
|
|
20
|
+
import { DialogProps } from "./types";
|
|
21
|
+
|
|
22
|
+
type FormValues = {
|
|
23
|
+
libraryID: string;
|
|
24
|
+
sliceID: string;
|
|
25
|
+
variationID: string;
|
|
26
|
+
variationName: string;
|
|
27
|
+
};
|
|
28
|
+
|
|
29
|
+
export const ConvertLegacySliceAsNewVariationDialog: FC<DialogProps> = ({
|
|
30
|
+
isOpen,
|
|
31
|
+
close,
|
|
32
|
+
onSubmit,
|
|
33
|
+
isLoading,
|
|
34
|
+
slice,
|
|
35
|
+
sliceName,
|
|
36
|
+
libraries,
|
|
37
|
+
localSharedSlices,
|
|
38
|
+
}) => {
|
|
39
|
+
const [inferIDFromName, setInferIDFromName] = useState(true);
|
|
40
|
+
|
|
41
|
+
return (
|
|
42
|
+
<Dialog
|
|
43
|
+
open={isOpen}
|
|
44
|
+
onOpenChange={(open) => !open && close()}
|
|
45
|
+
size={{ width: 448, height: "auto" }}
|
|
46
|
+
>
|
|
47
|
+
<DialogHeader title="Convert to slice variation" />
|
|
48
|
+
<DialogContent>
|
|
49
|
+
<Formik
|
|
50
|
+
validateOnChange
|
|
51
|
+
initialValues={{
|
|
52
|
+
libraryID: localSharedSlices[0]?.from,
|
|
53
|
+
sliceID: localSharedSlices[0]?.model.id,
|
|
54
|
+
variationID: Variation.generateId(slice.key),
|
|
55
|
+
variationName: sliceName,
|
|
56
|
+
}}
|
|
57
|
+
validate={(values) => {
|
|
58
|
+
return validateAsNewVariationValues(values, libraries);
|
|
59
|
+
}}
|
|
60
|
+
onSubmit={(values) => {
|
|
61
|
+
onSubmit(values);
|
|
62
|
+
}}
|
|
63
|
+
>
|
|
64
|
+
{(formik) => {
|
|
65
|
+
return (
|
|
66
|
+
<form id="convert-legacy-slice-as-new-variation-dialog">
|
|
67
|
+
<Box display="flex" flexDirection="column">
|
|
68
|
+
<ScrollArea className={styles.scrollArea}>
|
|
69
|
+
<Text variant="normal" color="grey11">
|
|
70
|
+
If you have multiple slices that are similar, you can
|
|
71
|
+
combine them as variations of the same slice.
|
|
72
|
+
</Text>
|
|
73
|
+
<Box display="flex" flexDirection="column" gap={4}>
|
|
74
|
+
<label className={styles.label}>
|
|
75
|
+
<Text variant="bold">Target slice*</Text>
|
|
76
|
+
{typeof formik.errors.libraryID === "string" ? (
|
|
77
|
+
<Text variant="small" color="tomato10">
|
|
78
|
+
{formik.errors.libraryID}
|
|
79
|
+
</Text>
|
|
80
|
+
) : null}
|
|
81
|
+
{typeof formik.errors.sliceID === "string" ? (
|
|
82
|
+
<Text variant="small" color="tomato10">
|
|
83
|
+
{formik.errors.sliceID}
|
|
84
|
+
</Text>
|
|
85
|
+
) : null}
|
|
86
|
+
</label>
|
|
87
|
+
<Select
|
|
88
|
+
size="medium"
|
|
89
|
+
variant="secondary"
|
|
90
|
+
startIcon="viewDay"
|
|
91
|
+
flexContent={true}
|
|
92
|
+
value={`${formik.values.libraryID}::${formik.values.sliceID}`}
|
|
93
|
+
onValueChange={(value) => {
|
|
94
|
+
if (value) {
|
|
95
|
+
const [libraryID, sliceID] = value.split("::");
|
|
96
|
+
void formik.setFieldValue("libraryID", libraryID);
|
|
97
|
+
void formik.setFieldValue("sliceID", sliceID);
|
|
98
|
+
}
|
|
99
|
+
}}
|
|
100
|
+
>
|
|
101
|
+
{localSharedSlices.map((slice) => (
|
|
102
|
+
<SelectItem
|
|
103
|
+
key={`${slice.from}::${slice.model.id}`}
|
|
104
|
+
value={`${slice.from}::${slice.model.id}`}
|
|
105
|
+
>
|
|
106
|
+
{slice.from} {">"} {slice.model.name} (
|
|
107
|
+
{slice.model.id})
|
|
108
|
+
</SelectItem>
|
|
109
|
+
))}
|
|
110
|
+
</Select>
|
|
111
|
+
<Text variant="normal" color="grey11">
|
|
112
|
+
Choose the slice to which you would like to add this
|
|
113
|
+
variation.
|
|
114
|
+
</Text>
|
|
115
|
+
</Box>
|
|
116
|
+
<Box display="flex" flexDirection="column" gap={4}>
|
|
117
|
+
<label className={styles.label}>
|
|
118
|
+
<Text variant="bold">Variation name*</Text>
|
|
119
|
+
{typeof formik.errors.variationName === "string" ? (
|
|
120
|
+
<Text variant="small" color="tomato10">
|
|
121
|
+
{formik.errors.variationName}
|
|
122
|
+
</Text>
|
|
123
|
+
) : null}
|
|
124
|
+
</label>
|
|
125
|
+
<FormInput
|
|
126
|
+
placeholder={sliceName}
|
|
127
|
+
error={typeof formik.errors.variationName === "string"}
|
|
128
|
+
value={formik.values.variationName}
|
|
129
|
+
onValueChange={(value) => {
|
|
130
|
+
const values = {
|
|
131
|
+
...formik.values,
|
|
132
|
+
variationName: value.slice(0, 30),
|
|
133
|
+
};
|
|
134
|
+
|
|
135
|
+
if (inferIDFromName) {
|
|
136
|
+
values.variationID = Variation.generateId(
|
|
137
|
+
values.variationName
|
|
138
|
+
);
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
formik.setValues(values, true);
|
|
142
|
+
}}
|
|
143
|
+
data-cy="variation-name-input"
|
|
144
|
+
/>
|
|
145
|
+
</Box>
|
|
146
|
+
<Box display="flex" flexDirection="column" gap={4}>
|
|
147
|
+
<label className={styles.label}>
|
|
148
|
+
<Text variant="bold">ID*</Text>
|
|
149
|
+
{typeof formik.errors.variationID === "string" ? (
|
|
150
|
+
<Text variant="small" color="tomato10">
|
|
151
|
+
{formik.errors.variationID}
|
|
152
|
+
</Text>
|
|
153
|
+
) : null}
|
|
154
|
+
</label>
|
|
155
|
+
<FormInput
|
|
156
|
+
placeholder={Variation.generateId(slice.key)}
|
|
157
|
+
error={typeof formik.errors.variationID === "string"}
|
|
158
|
+
value={formik.values.variationID}
|
|
159
|
+
onValueChange={(value) => {
|
|
160
|
+
setInferIDFromName(false);
|
|
161
|
+
void formik.setFieldValue(
|
|
162
|
+
"variationID",
|
|
163
|
+
Variation.generateId(value.slice(0, 30))
|
|
164
|
+
);
|
|
165
|
+
}}
|
|
166
|
+
data-cy="variation-id-input"
|
|
167
|
+
/>
|
|
168
|
+
</Box>
|
|
169
|
+
</ScrollArea>
|
|
170
|
+
<DialogActions
|
|
171
|
+
ok={{
|
|
172
|
+
text: "Convert",
|
|
173
|
+
onClick: () => void formik.submitForm(),
|
|
174
|
+
loading: isLoading,
|
|
175
|
+
disabled: !formik.isValid,
|
|
176
|
+
}}
|
|
177
|
+
cancel={{ text: "Cancel" }}
|
|
178
|
+
/>
|
|
179
|
+
</Box>
|
|
180
|
+
</form>
|
|
181
|
+
);
|
|
182
|
+
}}
|
|
183
|
+
</Formik>
|
|
184
|
+
</DialogContent>
|
|
185
|
+
</Dialog>
|
|
186
|
+
);
|
|
187
|
+
};
|
|
188
|
+
|
|
189
|
+
const validateAsNewVariationValues = (
|
|
190
|
+
values: FormValues,
|
|
191
|
+
libraries: readonly LibraryUI[]
|
|
192
|
+
): Partial<Record<keyof FormValues, string>> => {
|
|
193
|
+
const errors: Partial<Record<keyof FormValues, string>> = {};
|
|
194
|
+
|
|
195
|
+
if (!values.libraryID) {
|
|
196
|
+
errors.libraryID = "Cannot be empty.";
|
|
197
|
+
}
|
|
198
|
+
const library = libraries.find(
|
|
199
|
+
(library) => library.path === values.libraryID
|
|
200
|
+
);
|
|
201
|
+
// eslint-disable-next-line @typescript-eslint/strict-boolean-expressions
|
|
202
|
+
if (!errors.libraryID && !library) {
|
|
203
|
+
errors.libraryID = "Does not exist.";
|
|
204
|
+
}
|
|
205
|
+
|
|
206
|
+
if (!values.sliceID) {
|
|
207
|
+
errors.sliceID = "Cannot be empty.";
|
|
208
|
+
}
|
|
209
|
+
const slice = library?.components.find(
|
|
210
|
+
(component) => component.model.id === values.sliceID
|
|
211
|
+
);
|
|
212
|
+
// eslint-disable-next-line @typescript-eslint/strict-boolean-expressions
|
|
213
|
+
if (!errors.sliceID && !slice) {
|
|
214
|
+
errors.sliceID = "Does not exist.";
|
|
215
|
+
}
|
|
216
|
+
|
|
217
|
+
if (!values.variationName) {
|
|
218
|
+
errors.variationName = "Cannot be empty.";
|
|
219
|
+
}
|
|
220
|
+
|
|
221
|
+
if (!values.variationID) {
|
|
222
|
+
errors.variationID = "Cannot be empty.";
|
|
223
|
+
} else {
|
|
224
|
+
const variationIDs =
|
|
225
|
+
slice?.model.variations.map((variation) => variation.id) ?? [];
|
|
226
|
+
|
|
227
|
+
if (variationIDs.includes(values.variationID)) {
|
|
228
|
+
errors.variationID = "Slice variation ID is already taken.";
|
|
229
|
+
}
|
|
230
|
+
}
|
|
231
|
+
|
|
232
|
+
return errors;
|
|
233
|
+
};
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import { sprinkles } from "@prismicio/editor-ui";
|
|
2
|
+
|
|
3
|
+
export const scrollArea = sprinkles({
|
|
4
|
+
height: "100%",
|
|
5
|
+
display: "flex",
|
|
6
|
+
gap: 8,
|
|
7
|
+
paddingInline: 16,
|
|
8
|
+
paddingBlock: 16,
|
|
9
|
+
});
|
|
10
|
+
|
|
11
|
+
export const label = sprinkles({
|
|
12
|
+
display: "inline-flex",
|
|
13
|
+
alignItems: "center",
|
|
14
|
+
gap: 4,
|
|
15
|
+
});
|
|
@@ -0,0 +1,268 @@
|
|
|
1
|
+
import { useMemo, useState, type FC } from "react";
|
|
2
|
+
import { useSelector } from "react-redux";
|
|
3
|
+
import {
|
|
4
|
+
DropdownMenu,
|
|
5
|
+
DropdownMenuContent,
|
|
6
|
+
DropdownMenuItem,
|
|
7
|
+
DropdownMenuTrigger,
|
|
8
|
+
Icon,
|
|
9
|
+
Button,
|
|
10
|
+
} from "@prismicio/editor-ui";
|
|
11
|
+
|
|
12
|
+
import { NonSharedSliceInSliceZone } from "@models/common/CustomType/sliceZone";
|
|
13
|
+
import { ComponentUI } from "@models/common/ComponentUI";
|
|
14
|
+
import { CustomTypes } from "@models/common/CustomType";
|
|
15
|
+
import { getLibraries } from "@src/modules/slices";
|
|
16
|
+
import { SliceMachineStoreType } from "@src/redux/type";
|
|
17
|
+
import { managerClient } from "@src/managerClient";
|
|
18
|
+
import { getState, telemetry } from "@src/apiClient";
|
|
19
|
+
import useSliceMachineActions from "@src/modules/useSliceMachineActions";
|
|
20
|
+
import { ToasterType } from "@src/modules/toaster";
|
|
21
|
+
import { getFieldMappingFingerprint } from "@src/domain/slice";
|
|
22
|
+
|
|
23
|
+
import { NonSharedSliceViewCardProps } from "../sliceCards/NonSharedSliceViewCard";
|
|
24
|
+
import { ConvertLegacySliceAsNewSliceDialog } from "./ConvertLegacySliceAsNewSliceDialog";
|
|
25
|
+
import { ConvertLegacySliceAsNewVariationDialog } from "./ConvertLegacySliceAsNewVariationDialog";
|
|
26
|
+
import { ConvertLegacySliceMergeWithIdenticalDialog } from "./ConvertLegacySliceMergeWithIdenticalDialog";
|
|
27
|
+
import {
|
|
28
|
+
ConvertLegacySliceAndTrackArgs,
|
|
29
|
+
IdenticalSlice,
|
|
30
|
+
LegacySliceConversionType,
|
|
31
|
+
} from "./types";
|
|
32
|
+
|
|
33
|
+
type ConvertLegacySliceButtonProps = NonSharedSliceViewCardProps;
|
|
34
|
+
|
|
35
|
+
export const ConvertLegacySliceButton: FC<ConvertLegacySliceButtonProps> = ({
|
|
36
|
+
slice,
|
|
37
|
+
path,
|
|
38
|
+
}) => {
|
|
39
|
+
const {
|
|
40
|
+
refreshState,
|
|
41
|
+
openToaster,
|
|
42
|
+
initCustomTypeStore,
|
|
43
|
+
saveCustomTypeSuccess,
|
|
44
|
+
} = useSliceMachineActions();
|
|
45
|
+
|
|
46
|
+
const [isLoading, setIsLoading] = useState(false);
|
|
47
|
+
const [dialog, setDialog] = useState<LegacySliceConversionType | undefined>();
|
|
48
|
+
|
|
49
|
+
const { libraries: allLibraries } = useSelector(
|
|
50
|
+
(store: SliceMachineStoreType) => ({
|
|
51
|
+
libraries: getLibraries(store),
|
|
52
|
+
})
|
|
53
|
+
);
|
|
54
|
+
|
|
55
|
+
const sliceName =
|
|
56
|
+
slice.value.type === "Slice"
|
|
57
|
+
? slice.value.fieldset ?? slice.key
|
|
58
|
+
: slice.key;
|
|
59
|
+
const libraries = allLibraries.filter((library) => library.isLocal);
|
|
60
|
+
const localSharedSlices = libraries
|
|
61
|
+
.map((library) => library.components)
|
|
62
|
+
.flat();
|
|
63
|
+
const identicalSlices = useIdenticalSlices(
|
|
64
|
+
slice,
|
|
65
|
+
sliceName,
|
|
66
|
+
localSharedSlices
|
|
67
|
+
);
|
|
68
|
+
|
|
69
|
+
const convertLegacySliceAndTrack = async (
|
|
70
|
+
args: ConvertLegacySliceAndTrackArgs
|
|
71
|
+
) => {
|
|
72
|
+
if (!dialog) {
|
|
73
|
+
return;
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
setIsLoading(true);
|
|
77
|
+
|
|
78
|
+
void telemetry.track({
|
|
79
|
+
event: "legacy-slice:converted",
|
|
80
|
+
id: args.sliceID,
|
|
81
|
+
variation: args.variationID ?? "default",
|
|
82
|
+
library: args.libraryID,
|
|
83
|
+
conversionType: dialog,
|
|
84
|
+
});
|
|
85
|
+
|
|
86
|
+
const { errors } =
|
|
87
|
+
await managerClient.slices.convertLegacySliceToSharedSlice({
|
|
88
|
+
model: slice.value,
|
|
89
|
+
src: {
|
|
90
|
+
...path,
|
|
91
|
+
sliceID: slice.key,
|
|
92
|
+
},
|
|
93
|
+
dest: {
|
|
94
|
+
libraryID: args.libraryID,
|
|
95
|
+
sliceID: args.sliceID,
|
|
96
|
+
variationName: args.variationName ?? "Default",
|
|
97
|
+
variationID: args.variationID ?? "default",
|
|
98
|
+
},
|
|
99
|
+
});
|
|
100
|
+
|
|
101
|
+
if (errors.length) {
|
|
102
|
+
console.error(`Could not convert slice \`${sliceName}\``, errors);
|
|
103
|
+
|
|
104
|
+
openToaster(
|
|
105
|
+
`Could not convert slice \`${sliceName}\``,
|
|
106
|
+
ToasterType.ERROR
|
|
107
|
+
);
|
|
108
|
+
|
|
109
|
+
throw errors;
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
const { model: customType, errors: customTypeReadErrors } =
|
|
113
|
+
await managerClient.customTypes.readCustomType({
|
|
114
|
+
id: path.customTypeID,
|
|
115
|
+
});
|
|
116
|
+
|
|
117
|
+
if (customTypeReadErrors.length || !customType) {
|
|
118
|
+
console.error(
|
|
119
|
+
`Could not refresh custom type view \`${path.customTypeID}\``,
|
|
120
|
+
customTypeReadErrors
|
|
121
|
+
);
|
|
122
|
+
|
|
123
|
+
openToaster(
|
|
124
|
+
`Could not refresh custom type view \`${path.customTypeID}\``,
|
|
125
|
+
ToasterType.ERROR
|
|
126
|
+
);
|
|
127
|
+
|
|
128
|
+
return;
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
// TODO(DT-1453): Remove the need of the global getState
|
|
132
|
+
const serverState = await getState();
|
|
133
|
+
// Update Redux store
|
|
134
|
+
refreshState(serverState);
|
|
135
|
+
|
|
136
|
+
setIsLoading(false);
|
|
137
|
+
setDialog(undefined);
|
|
138
|
+
switch (dialog) {
|
|
139
|
+
case "as_new_slice":
|
|
140
|
+
openToaster(
|
|
141
|
+
`${sliceName} has been upgraded to a new slice ${args.libraryID} > ${args.sliceID}`,
|
|
142
|
+
ToasterType.SUCCESS
|
|
143
|
+
);
|
|
144
|
+
break;
|
|
145
|
+
|
|
146
|
+
case "as_new_variation":
|
|
147
|
+
openToaster(
|
|
148
|
+
`${sliceName} has been converted as a variation of ${args.libraryID} > ${args.sliceID}`,
|
|
149
|
+
ToasterType.SUCCESS
|
|
150
|
+
);
|
|
151
|
+
break;
|
|
152
|
+
|
|
153
|
+
case "merge_with_identical":
|
|
154
|
+
default:
|
|
155
|
+
openToaster(
|
|
156
|
+
`${sliceName} has been merged with ${args.libraryID} > ${args.sliceID}`,
|
|
157
|
+
ToasterType.SUCCESS
|
|
158
|
+
);
|
|
159
|
+
break;
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
const customTypeSM = CustomTypes.toSM(customType);
|
|
163
|
+
initCustomTypeStore(customTypeSM, customTypeSM);
|
|
164
|
+
saveCustomTypeSuccess(customType);
|
|
165
|
+
};
|
|
166
|
+
|
|
167
|
+
const formProps = {
|
|
168
|
+
path,
|
|
169
|
+
slice,
|
|
170
|
+
sliceName,
|
|
171
|
+
libraries,
|
|
172
|
+
localSharedSlices,
|
|
173
|
+
identicalSlices,
|
|
174
|
+
close: () => setDialog(undefined),
|
|
175
|
+
onSubmit: convertLegacySliceAndTrack,
|
|
176
|
+
isLoading,
|
|
177
|
+
};
|
|
178
|
+
|
|
179
|
+
return (
|
|
180
|
+
<>
|
|
181
|
+
<DropdownMenu>
|
|
182
|
+
<DropdownMenuTrigger>
|
|
183
|
+
<Button
|
|
184
|
+
data-cy="convert-legacy-slice"
|
|
185
|
+
startIcon="refresh"
|
|
186
|
+
endIcon="arrowDropDown"
|
|
187
|
+
size="medium"
|
|
188
|
+
variant="secondary"
|
|
189
|
+
>
|
|
190
|
+
Migrate legacy slice
|
|
191
|
+
</Button>
|
|
192
|
+
</DropdownMenuTrigger>
|
|
193
|
+
<DropdownMenuContent align="start">
|
|
194
|
+
<DropdownMenuItem
|
|
195
|
+
startIcon={<Icon name="folder" size="large" />}
|
|
196
|
+
description="Use it with new types"
|
|
197
|
+
onSelect={() => setDialog("as_new_slice")}
|
|
198
|
+
>
|
|
199
|
+
Upgrade slice
|
|
200
|
+
</DropdownMenuItem>
|
|
201
|
+
<DropdownMenuItem
|
|
202
|
+
startIcon={<Icon name="viewDay" size="large" />}
|
|
203
|
+
description="Add it to another slice"
|
|
204
|
+
onSelect={() => setDialog("as_new_variation")}
|
|
205
|
+
disabled={!localSharedSlices.length}
|
|
206
|
+
>
|
|
207
|
+
Convert to slice variation
|
|
208
|
+
</DropdownMenuItem>
|
|
209
|
+
<DropdownMenuItem
|
|
210
|
+
startIcon={<Icon name="driveFileMove" size="large" />}
|
|
211
|
+
description="Combine identical slices"
|
|
212
|
+
onSelect={() => setDialog("merge_with_identical")}
|
|
213
|
+
disabled={!identicalSlices.length}
|
|
214
|
+
>
|
|
215
|
+
Merge with another slice
|
|
216
|
+
</DropdownMenuItem>
|
|
217
|
+
</DropdownMenuContent>
|
|
218
|
+
</DropdownMenu>
|
|
219
|
+
<ConvertLegacySliceAsNewSliceDialog
|
|
220
|
+
{...formProps}
|
|
221
|
+
isOpen={dialog === "as_new_slice"}
|
|
222
|
+
/>
|
|
223
|
+
<ConvertLegacySliceAsNewVariationDialog
|
|
224
|
+
{...formProps}
|
|
225
|
+
isOpen={dialog === "as_new_variation"}
|
|
226
|
+
/>
|
|
227
|
+
<ConvertLegacySliceMergeWithIdenticalDialog
|
|
228
|
+
{...formProps}
|
|
229
|
+
isOpen={dialog === "merge_with_identical"}
|
|
230
|
+
/>
|
|
231
|
+
</>
|
|
232
|
+
);
|
|
233
|
+
};
|
|
234
|
+
|
|
235
|
+
const useIdenticalSlices = (
|
|
236
|
+
slice: NonSharedSliceInSliceZone,
|
|
237
|
+
sliceName: string,
|
|
238
|
+
localSharedSlices: ComponentUI[]
|
|
239
|
+
) => {
|
|
240
|
+
return useMemo<IdenticalSlice[]>(() => {
|
|
241
|
+
const results: IdenticalSlice[] = [];
|
|
242
|
+
|
|
243
|
+
const sliceFields = getFieldMappingFingerprint(slice.value, sliceName);
|
|
244
|
+
|
|
245
|
+
for (const sharedSlice of localSharedSlices) {
|
|
246
|
+
for (const variation of sharedSlice.model.variations) {
|
|
247
|
+
const variationFields = getFieldMappingFingerprint(
|
|
248
|
+
variation,
|
|
249
|
+
sharedSlice.model.name
|
|
250
|
+
);
|
|
251
|
+
|
|
252
|
+
if (
|
|
253
|
+
sliceFields.primary === variationFields.primary &&
|
|
254
|
+
sliceFields.items === variationFields.items
|
|
255
|
+
) {
|
|
256
|
+
results.push({
|
|
257
|
+
libraryID: sharedSlice.from,
|
|
258
|
+
sliceID: sharedSlice.model.id,
|
|
259
|
+
variationID: variation.id,
|
|
260
|
+
path: `${sharedSlice.from}::${sharedSlice.model.id}::${variation.id}`,
|
|
261
|
+
});
|
|
262
|
+
}
|
|
263
|
+
}
|
|
264
|
+
}
|
|
265
|
+
|
|
266
|
+
return results;
|
|
267
|
+
}, [slice, sliceName, localSharedSlices]);
|
|
268
|
+
};
|