slice-machine-ui 1.11.1-dev-next-release.4 → 1.11.1-dev-slice-tenplate-feature-2.0

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 (66) hide show
  1. package/components/ModalFormCard/index.tsx +102 -93
  2. package/lib/builders/CustomTypeBuilder/SliceZone/SlicesTemplatesModal.tsx +88 -0
  3. package/lib/builders/CustomTypeBuilder/SliceZone/UpdateSliceZoneModal.tsx +2 -3
  4. package/lib/builders/CustomTypeBuilder/SliceZone/UpdateSliceZoneModalList.tsx +36 -6
  5. package/lib/builders/CustomTypeBuilder/SliceZone/index.tsx +92 -12
  6. package/lib/builders/CustomTypeBuilder/SliceZone/sliceTemplatesComingSoon.ts +122 -0
  7. package/lib/builders/CustomTypeBuilder/TabZone/index.tsx +50 -39
  8. package/lib/builders/common/Zone/components/EmptyState/index.tsx +13 -14
  9. package/lib/builders/common/Zone/index.jsx +22 -33
  10. package/out/404.html +1 -1
  11. package/out/[lib]/[sliceName]/[variation]/screenshot.html +1 -1
  12. package/out/[lib]/[sliceName]/[variation]/simulator.html +1 -1
  13. package/out/[lib]/[sliceName]/[variation].html +1 -1
  14. package/out/_next/static/a84hXu0hKKBqhkOgLrr1K/_buildManifest.js +1 -0
  15. package/out/_next/static/chunks/18539003-b49b17dc02a89c7b.js +101 -0
  16. package/out/_next/static/chunks/377-04a57b21fc2cae35.js +1 -0
  17. package/out/_next/static/chunks/{482-ad5ef8eecedd8b52.js → 482-5ae2d1fd2572cb1f.js} +1 -1
  18. package/out/_next/static/chunks/{573-aaae870d20e5f86c.js → 573-c4f805b20b6710f3.js} +1 -1
  19. package/out/_next/static/chunks/777-b833ba5749997505.js +1 -0
  20. package/out/_next/static/chunks/{740-d0642219804fa9f0.js → 791-091202c1718b4ac3.js} +2 -2
  21. package/out/_next/static/chunks/799-48a1855b96a3623f.js +1 -0
  22. package/out/_next/static/chunks/962-6beea758652e94f7.js +1 -0
  23. package/out/_next/static/chunks/pages/[lib]/[sliceName]/[variation]/{simulator-2c6033d99c3672e0.js → simulator-e51d1161076d4797.js} +1 -1
  24. package/out/_next/static/chunks/pages/[lib]/[sliceName]/{[variation]-bd1822e54ac8836c.js → [variation]-4b4fed1e884838ad.js} +1 -1
  25. package/out/_next/static/chunks/pages/{_app-2351f4803a15a482.js → _app-441ee3050f578732.js} +98 -98
  26. package/out/_next/static/chunks/pages/{changelog-dda7774c702c6b47.js → changelog-c08cc37b54d73a60.js} +1 -1
  27. package/out/_next/static/chunks/pages/{changes-26dab4f4657a54e4.js → changes-3b7c6b9b312b5e2f.js} +1 -1
  28. package/out/_next/static/chunks/pages/custom-types/{[customTypeId]-a69df1ad7fd8b927.js → [customTypeId]-207b0c837912e5a9.js} +1 -1
  29. package/out/_next/static/chunks/pages/page-types/{[pageTypeId]-5e4e26de800fccff.js → [pageTypeId]-a6a8ebd9f4b3b608.js} +1 -1
  30. package/out/_next/static/chunks/pages/slices-24d2714462334469.js +1 -0
  31. package/out/_next/static/css/{6639e1548a7d3442.css → a978ac70ea31c6d7.css} +1 -1
  32. package/out/_next/static/css/{6c430d5aaa4ef12a.css → bbe0f7491bdca3fc.css} +1 -1
  33. package/out/_next/static/css/de841cf04740a2ad.css +1 -0
  34. package/out/_next/static/css/{d4b59834999106a5.css → f7381722c36382c6.css} +1 -1
  35. package/out/alternateGrid.png +0 -0
  36. package/out/changelog.html +1 -1
  37. package/out/changes.html +1 -1
  38. package/out/custom-types/[customTypeId].html +1 -1
  39. package/out/custom-types.html +1 -1
  40. package/out/customerLogos.png +0 -0
  41. package/out/hero.png +0 -0
  42. package/out/index.html +1 -1
  43. package/out/page-types/[pageTypeId].html +1 -1
  44. package/out/slices.html +1 -1
  45. package/out/videoHighlight.png +0 -0
  46. package/package.json +7 -7
  47. package/public/alternateGrid.png +0 -0
  48. package/public/customerLogos.png +0 -0
  49. package/public/hero.png +0 -0
  50. package/public/videoHighlight.png +0 -0
  51. package/src/components/List/List.css.ts +0 -2
  52. package/src/components/List/List.tsx +3 -7
  53. package/src/features/customTypes/customTypesBuilder/SliceZoneBlankSlate.tsx +16 -4
  54. package/src/features/customTypes/customTypesMessages.ts +2 -2
  55. package/src/features/slicesTemplates/actions/createSlicesTemplates.ts +41 -0
  56. package/src/features/slicesTemplates/useSlicesTemplates.ts +37 -0
  57. package/test/pages/custom-types.test.tsx +38 -20
  58. package/out/_next/static/N2nU1yPW38KWsgo9OL-hs/_buildManifest.js +0 -1
  59. package/out/_next/static/chunks/119-626b7b48e4535694.js +0 -1
  60. package/out/_next/static/chunks/18539003-b0b0df3d19f7d641.js +0 -101
  61. package/out/_next/static/chunks/377-c283ea91a489e394.js +0 -1
  62. package/out/_next/static/chunks/677-d8555c165c261511.js +0 -1
  63. package/out/_next/static/chunks/799-4787c871f6119407.js +0 -1
  64. package/out/_next/static/chunks/pages/slices-d1c96629e458440c.js +0 -1
  65. package/out/_next/static/css/74653a9f89a68929.css +0 -1
  66. /package/out/_next/static/{N2nU1yPW38KWsgo9OL-hs → a84hXu0hKKBqhkOgLrr1K}/_ssgManifest.js +0 -0
@@ -6,8 +6,9 @@ import {
6
6
  FormikErrors,
7
7
  FormikTouched,
8
8
  FormikValues,
9
+ FormikProps,
9
10
  } from "formik";
10
- import { ReactNode, SetStateAction } from "react";
11
+ import { SetStateAction } from "react";
11
12
  import { Flex, Heading, Close, Button as ThemeButton } from "theme-ui";
12
13
 
13
14
  import { Button } from "@components/Button";
@@ -44,7 +45,9 @@ type ModalCardProps<T extends FormikValues> = {
44
45
  omitFooter?: boolean;
45
46
  isLoading?: boolean;
46
47
  dataCy?: string;
47
- actionMessage?: ReactNode;
48
+ actionMessage?:
49
+ | ((props: FormikProps<T>) => React.ReactNode)
50
+ | React.ReactNode;
48
51
  };
49
52
 
50
53
  function ModalCard<Values extends FormikValues>({
@@ -88,102 +91,108 @@ function ModalCard<Values extends FormikValues>({
88
91
  onSubmit(values);
89
92
  }}
90
93
  >
91
- {({
92
- isValid,
93
- isSubmitting,
94
- values,
95
- errors,
96
- touched,
97
- setFieldValue,
98
- setValues,
99
- }) => (
100
- <Form
101
- id={formId}
102
- {...(dataCy != null ? { "data-cy": dataCy } : null)}
103
- >
104
- <Card
105
- borderFooter
106
- footerSx={{
107
- p: 3,
108
- position: "sticky",
109
- bottom: 0,
110
- background: "gray",
111
- }}
112
- bodySx={{ px: 4, py: 4 }}
113
- sx={{ border: "none" }}
114
- {...cardProps}
115
- Header={({ radius }: { radius: string | number }) => (
116
- <Flex
117
- sx={{
118
- position: "sticky",
119
- top: 0,
120
- zIndex: 1,
121
- p: "16px",
122
- pl: 4,
123
- bg: "headSection",
124
- alignItems: "center",
125
- justifyContent: "space-between",
126
- borderTopLeftRadius: radius,
127
- borderTopRightRadius: radius,
128
- // eslint-disable-next-line @typescript-eslint/restrict-template-expressions
129
- borderBottom: (t) => `1px solid ${t.colors?.borders}`,
130
- }}
131
- >
132
- <Heading sx={{ fontSize: "20px" }}>{title}</Heading>
133
- <Close type="button" onClick={close} />
134
- </Flex>
135
- )}
136
- Footer={
137
- !omitFooter ? (
94
+ {(formikProps) => {
95
+ const {
96
+ isValid,
97
+ isSubmitting,
98
+ values,
99
+ errors,
100
+ touched,
101
+ setFieldValue,
102
+ setValues,
103
+ } = formikProps;
104
+
105
+ return (
106
+ <Form
107
+ id={formId}
108
+ {...(dataCy != null ? { "data-cy": dataCy } : null)}
109
+ >
110
+ <Card
111
+ borderFooter
112
+ footerSx={{
113
+ p: 3,
114
+ position: "sticky",
115
+ bottom: 0,
116
+ background: "gray",
117
+ }}
118
+ bodySx={{ px: 4, py: 4 }}
119
+ sx={{ border: "none" }}
120
+ {...cardProps}
121
+ Header={({ radius }: { radius: string | number }) => (
138
122
  <Flex
139
123
  sx={{
140
- alignItems: "space-between",
124
+ position: "sticky",
125
+ top: 0,
126
+ zIndex: 1,
127
+ p: "16px",
128
+ pl: 4,
129
+ bg: "headSection",
130
+ alignItems: "center",
131
+ justifyContent: "space-between",
132
+ borderTopLeftRadius: radius,
133
+ borderTopRightRadius: radius,
134
+ // eslint-disable-next-line @typescript-eslint/restrict-template-expressions
135
+ borderBottom: (t) => `1px solid ${t.colors?.borders}`,
141
136
  }}
142
137
  >
143
- <Flex sx={{ fontSize: "14px", alignItems: "center" }}>
144
- {actionMessage}
145
- </Flex>
146
- <Flex sx={{ ml: "auto" }}>
147
- <ThemeButton
148
- mr={2}
149
- type="button"
150
- onClick={close}
151
- variant="secondary"
152
- disabled={isSubmitting || isLoading}
153
- >
154
- Cancel
155
- </ThemeButton>
156
- <Button
157
- label={buttonLabel}
158
- form={formId}
159
- type="submit"
160
- disabled={!isValid || isSubmitting || isLoading}
161
- isLoading={isSubmitting || isLoading}
162
- sx={{
163
- fontWeight: "400",
164
- paddingBlock: "8px",
165
- paddingInline: "16px",
166
- fontSize: "14px",
167
- borderRadius: "4px",
168
- }}
169
- />
170
- </Flex>
138
+ <Heading sx={{ fontSize: "20px" }}>{title}</Heading>
139
+ <Close type="button" onClick={close} />
171
140
  </Flex>
172
- ) : null
173
- }
174
- >
175
- {children({
176
- isValid,
177
- isSubmitting,
178
- values,
179
- errors,
180
- touched,
181
- setFieldValue,
182
- setValues,
183
- })}
184
- </Card>
185
- </Form>
186
- )}
141
+ )}
142
+ Footer={
143
+ !omitFooter ? (
144
+ <Flex
145
+ sx={{
146
+ alignItems: "space-between",
147
+ }}
148
+ >
149
+ <Flex sx={{ fontSize: "14px", alignItems: "center" }}>
150
+ {typeof actionMessage === "function"
151
+ ? actionMessage(formikProps)
152
+ : actionMessage}
153
+ </Flex>
154
+ <Flex sx={{ ml: "auto" }}>
155
+ <ThemeButton
156
+ mr={2}
157
+ type="button"
158
+ onClick={close}
159
+ variant="secondary"
160
+ disabled={isSubmitting || isLoading}
161
+ >
162
+ Cancel
163
+ </ThemeButton>
164
+ <Button
165
+ label={buttonLabel}
166
+ form={formId}
167
+ type="submit"
168
+ disabled={!isValid || isSubmitting || isLoading}
169
+ isLoading={isSubmitting || isLoading}
170
+ sx={{
171
+ fontWeight: "400",
172
+ paddingBlock: "8px",
173
+ paddingInline: "16px",
174
+ fontSize: "14px",
175
+ borderRadius: "4px",
176
+ }}
177
+ />
178
+ </Flex>
179
+ </Flex>
180
+ ) : null
181
+ }
182
+ >
183
+ {children({
184
+ isValid,
185
+ isSubmitting,
186
+ values,
187
+ errors,
188
+ touched,
189
+ setFieldValue,
190
+ setValues,
191
+ })}
192
+ </Card>
193
+ </Form>
194
+ );
195
+ }}
187
196
  </Formik>
188
197
  </SliceMachineModal>
189
198
  );
@@ -0,0 +1,88 @@
1
+ import { FC } from "react";
2
+ import { Text } from "theme-ui";
3
+
4
+ import { SliceTemplate } from "@src/features/slicesTemplates/useSlicesTemplates";
5
+ import { Slices } from "@lib/models/common/Slice";
6
+ import { ComponentUI } from "@lib/models/common/ComponentUI";
7
+ import UpdateSliceZoneModalList from "./UpdateSliceZoneModalList";
8
+ import ModalFormCard from "../../../../components/ModalFormCard";
9
+ import { sliceTemplatesComingSoon } from "./sliceTemplatesComingSoon";
10
+
11
+ interface UpdateSliceModalProps {
12
+ isOpen: boolean;
13
+ formId: string;
14
+ close: () => void;
15
+ onSubmit: (values: SliceZoneFormValues) => void;
16
+ availableSlicesTemplates: SliceTemplate[];
17
+ }
18
+
19
+ export type SliceZoneFormValues = {
20
+ sliceKeys: string[];
21
+ };
22
+
23
+ export const SlicesTemplatesModal: FC<UpdateSliceModalProps> = ({
24
+ isOpen,
25
+ formId,
26
+ close,
27
+ onSubmit,
28
+ availableSlicesTemplates,
29
+ }) => {
30
+ return (
31
+ <ModalFormCard
32
+ buttonLabel="Create"
33
+ isOpen={isOpen}
34
+ formId={formId}
35
+ close={close}
36
+ onSubmit={(values: SliceZoneFormValues) => {
37
+ onSubmit(values);
38
+ }}
39
+ initialValues={{
40
+ sliceKeys: [],
41
+ }}
42
+ content={{
43
+ title: "Create slices from template",
44
+ }}
45
+ validate={(values) => {
46
+ if (values.sliceKeys.length === 0) {
47
+ return {
48
+ sliceKeys: "Please select at least one slice template",
49
+ };
50
+ }
51
+ }}
52
+ actionMessage={({ errors }) =>
53
+ errors.sliceKeys !== undefined ? (
54
+ <Text sx={{ color: "error" }}>{errors.sliceKeys}</Text>
55
+ ) : undefined
56
+ }
57
+ >
58
+ {({ values }) => (
59
+ <UpdateSliceZoneModalList
60
+ values={values}
61
+ placeholderSlices={sliceTemplatesComingSoon}
62
+ availableSlices={[
63
+ ...availableSlicesTemplates.map(
64
+ (slice): ComponentUI => ({
65
+ extension: "",
66
+ fileName: "",
67
+ from: "",
68
+ href: "",
69
+ pathToSlice: "",
70
+ model: Slices.toSM(slice.model),
71
+ screenshots: Object.entries(slice.screenshots).reduce(
72
+ (acc, curr) => ({
73
+ ...acc,
74
+ [curr[0]]: {
75
+ url: curr[1],
76
+ },
77
+ }),
78
+ {}
79
+ ),
80
+ })
81
+ ),
82
+ ...sliceTemplatesComingSoon,
83
+ ]}
84
+ />
85
+ )}
86
+ </ModalFormCard>
87
+ );
88
+ };
@@ -1,7 +1,6 @@
1
+ import { ComponentUI } from "@lib/models/common/ComponentUI";
1
2
  import ModalFormCard from "../../../../components/ModalFormCard";
2
-
3
3
  import UpdateSliceZoneModalList from "./UpdateSliceZoneModalList";
4
- import { ComponentUI } from "@lib/models/common/ComponentUI";
5
4
 
6
5
  interface UpdateSliceModalProps {
7
6
  isOpen: boolean;
@@ -38,7 +37,7 @@ const UpdateSliceZoneModal: React.FC<UpdateSliceModalProps> = ({
38
37
  sliceKeys: slicesInSliceZone.map((slice) => slice.model.id),
39
38
  }}
40
39
  content={{
41
- title: "Update Slices",
40
+ title: "Update slices",
42
41
  }}
43
42
  dataCy="update-slices-modal"
44
43
  >
@@ -1,5 +1,5 @@
1
1
  import { FieldArray } from "formik";
2
- import { Checkbox, Box } from "theme-ui";
2
+ import { Checkbox, Box, Text } from "theme-ui";
3
3
 
4
4
  import { SharedSlice } from "@lib/models/ui/Slice";
5
5
 
@@ -10,7 +10,8 @@ import { ComponentUI } from "@lib/models/common/ComponentUI";
10
10
  const UpdateSliceZoneModalList: React.FC<{
11
11
  availableSlices: ReadonlyArray<ComponentUI>;
12
12
  values: SliceZoneFormValues;
13
- }> = ({ availableSlices, values }) => (
13
+ placeholderSlices?: ReadonlyArray<ComponentUI>;
14
+ }> = ({ availableSlices, values, placeholderSlices }) => (
14
15
  <FieldArray
15
16
  name="sliceKeys"
16
17
  render={(arrayHelpers) => (
@@ -19,6 +20,10 @@ const UpdateSliceZoneModalList: React.FC<{
19
20
  elems={availableSlices}
20
21
  defineElementKey={(slice: ComponentUI) => slice.model.name}
21
22
  renderElem={(slice: ComponentUI) => {
23
+ const isPlaceholderSlice = placeholderSlices?.find(
24
+ (s) => s.model.id === slice.model.id
25
+ );
26
+
22
27
  return SharedSlice.render({
23
28
  slice: slice,
24
29
  Wrapper: ({ slice, children, sx }) => {
@@ -26,6 +31,10 @@ const UpdateSliceZoneModalList: React.FC<{
26
31
  <Box
27
32
  data-testid="slicezone-modal-item"
28
33
  onClick={() => {
34
+ if (isPlaceholderSlice) {
35
+ return;
36
+ }
37
+
29
38
  const isInSliceZone = values.sliceKeys.includes(
30
39
  slice.model.id
31
40
  );
@@ -37,16 +46,37 @@ const UpdateSliceZoneModalList: React.FC<{
37
46
  arrayHelpers.push(slice.model.id);
38
47
  }}
39
48
  key={`${slice.from}-${slice.model.name}`}
40
- sx={{
41
- cursor: "pointer",
42
- ...sx,
43
- }}
49
+ sx={
50
+ !isPlaceholderSlice
51
+ ? {
52
+ cursor: "pointer",
53
+ ...sx,
54
+ }
55
+ : {
56
+ opacity: 0.5,
57
+ ...sx,
58
+ }
59
+ }
44
60
  >
45
61
  {children}
46
62
  </Box>
47
63
  );
48
64
  },
49
65
  StatusOrCustom: ({ slice }: { slice: ComponentUI }) => {
66
+ if (isPlaceholderSlice) {
67
+ return (
68
+ <Text
69
+ sx={{
70
+ fontSize: 12,
71
+ color: "purpleStrong",
72
+ fontWeight: "bold",
73
+ }}
74
+ >
75
+ Coming soon
76
+ </Text>
77
+ );
78
+ }
79
+
50
80
  const isInSliceZone = values.sliceKeys.includes(slice.model.id);
51
81
 
52
82
  return (
@@ -1,4 +1,13 @@
1
- import { Button, Box, Switch } from "@prismicio/editor-ui";
1
+ import {
2
+ Button,
3
+ Box,
4
+ Switch,
5
+ DropdownMenu,
6
+ DropdownMenuTrigger,
7
+ DropdownMenuContent,
8
+ DropdownMenuItem,
9
+ Icon,
10
+ } from "@prismicio/editor-ui";
2
11
  import { useEffect, useMemo, useState } from "react";
3
12
  import { useSelector } from "react-redux";
4
13
  import { BaseStyles } from "theme-ui";
@@ -21,10 +30,15 @@ import {
21
30
  getRemoteSlices,
22
31
  } from "@src/modules/slices";
23
32
  import type { SliceMachineStoreType } from "@src/redux/type";
33
+ import { useSlicesTemplates } from "@src/features/slicesTemplates/useSlicesTemplates";
34
+ import { createSlicesTemplates } from "@src/features/slicesTemplates/actions/createSlicesTemplates";
24
35
 
25
36
  import { DeleteSliceZoneModal } from "./DeleteSliceZoneModal";
26
37
  import { SlicesList } from "./List";
27
38
  import UpdateSliceZoneModal from "./UpdateSliceZoneModal";
39
+ import { SlicesTemplatesModal } from "./SlicesTemplatesModal";
40
+ import { getState } from "@src/apiClient";
41
+ import useSliceMachineActions from "@src/modules/useSliceMachineActions";
28
42
 
29
43
  const mapAvailableAndSharedSlices = (
30
44
  sliceZone: SlicesSM,
@@ -100,7 +114,11 @@ const SliceZone: React.FC<SliceZoneProps> = ({
100
114
  sliceZone,
101
115
  tabId,
102
116
  }) => {
103
- const [formIsOpen, setFormIsOpen] = useState(false);
117
+ const availableSlicesTemplates = useSlicesTemplates();
118
+ const [isSlicesTemplatesModalOpen, setIsSlicesTemplatesModalOpen] =
119
+ useState(false);
120
+ const [isUpdateSliceZoneModalOpen, setIsUpdateSliceZoneModalOpen] =
121
+ useState(false);
104
122
  const [isCreateSliceModalOpen, setIsCreateSliceModalOpen] = useState(false);
105
123
  const { remoteSlices, libraries, slices } = useSelector(
106
124
  (store: SliceMachineStoreType) => ({
@@ -109,6 +127,7 @@ const SliceZone: React.FC<SliceZoneProps> = ({
109
127
  slices: getFrontendSlices(store),
110
128
  })
111
129
  );
130
+ const { createSliceSuccess } = useSliceMachineActions();
112
131
  const localLibraries: readonly LibraryUI[] = libraries.filter(
113
132
  (library) => library.isLocal
114
133
  );
@@ -142,33 +161,63 @@ const SliceZone: React.FC<SliceZoneProps> = ({
142
161
  .map((e) => (e.payload as NonSharedSliceInSliceZone).key);
143
162
 
144
163
  const onAddNewSlice = () => {
145
- setFormIsOpen(true);
164
+ setIsUpdateSliceZoneModalOpen(true);
146
165
  };
147
166
 
148
167
  const onCreateNewSlice = () => {
149
168
  setIsCreateSliceModalOpen(true);
150
169
  };
151
170
 
171
+ const openSlicesTemplatesModal = () => {
172
+ setIsSlicesTemplatesModalOpen(true);
173
+ };
174
+
152
175
  return (
153
176
  <Box flexDirection="column">
154
177
  <List>
155
178
  <ListHeader
156
179
  actions={
157
- sliceZone ? (
158
- <>
159
- <Button onClick={onCreateNewSlice} startIcon="add">
160
- New slice
161
- </Button>
180
+ sliceZone && slicesInSliceZone.length > 0 ? (
181
+ <Box gap={8}>
182
+ <DropdownMenu>
183
+ <DropdownMenuTrigger>
184
+ <Button variant="secondary" startIcon="add">
185
+ Create
186
+ </Button>
187
+ </DropdownMenuTrigger>
188
+
189
+ <DropdownMenuContent align="end">
190
+ <DropdownMenuItem
191
+ startIcon={<Icon name="add" />}
192
+ onSelect={onCreateNewSlice}
193
+ >
194
+ Blank slice
195
+ </DropdownMenuItem>
196
+
197
+ {availableSlicesTemplates.length > 0 ? (
198
+ <DropdownMenuItem
199
+ onSelect={() => {
200
+ openSlicesTemplatesModal();
201
+ }}
202
+ startIcon={<Icon name="centerFocusWeak" />}
203
+ >
204
+ Use a template
205
+ </DropdownMenuItem>
206
+ ) : undefined}
207
+ </DropdownMenuContent>
208
+ </DropdownMenu>
209
+
162
210
  {availableSlices.length > 0 ? (
163
211
  <Button
164
212
  data-cy="update-slices"
165
213
  onClick={onAddNewSlice}
166
214
  startIcon="edit"
215
+ variant="secondary"
167
216
  >
168
- Update Slices
217
+ Update slices
169
218
  </Button>
170
219
  ) : undefined}
171
- </>
220
+ </Box>
172
221
  ) : undefined
173
222
  }
174
223
  toggle={
@@ -204,12 +253,14 @@ const SliceZone: React.FC<SliceZoneProps> = ({
204
253
  <SliceZoneBlankSlate
205
254
  onAddNewSlice={onAddNewSlice}
206
255
  onCreateNewSlice={onCreateNewSlice}
256
+ openSlicesTemplatesModal={openSlicesTemplatesModal}
207
257
  projectHasAvailableSlices={availableSlices.length > 0}
258
+ isSlicesTemplatesSupported={availableSlicesTemplates.length > 0}
208
259
  />
209
260
  )
210
261
  ) : undefined}
211
262
  <UpdateSliceZoneModal
212
- isOpen={formIsOpen}
263
+ isOpen={isUpdateSliceZoneModalOpen}
213
264
  formId={`tab-slicezone-form-${tabId}`}
214
265
  availableSlices={availableSlices}
215
266
  slicesInSliceZone={sharedSlicesInSliceZone}
@@ -217,7 +268,36 @@ const SliceZone: React.FC<SliceZoneProps> = ({
217
268
  // eslint-disable-next-line @typescript-eslint/no-unsafe-return
218
269
  onSelectSharedSlices(sliceKeys, nonSharedSlicesKeysInSliceZone)
219
270
  }
220
- close={() => setFormIsOpen(false)}
271
+ close={() => setIsUpdateSliceZoneModalOpen(false)}
272
+ />
273
+ <SlicesTemplatesModal
274
+ isOpen={isSlicesTemplatesModalOpen}
275
+ formId={`tab-slicezone-form-${tabId}`}
276
+ availableSlicesTemplates={availableSlicesTemplates}
277
+ onSubmit={({ sliceKeys }) =>
278
+ void createSlicesTemplates({
279
+ templateIDs: sliceKeys,
280
+ localLibrariesNames: localLibraries.map((library) => library.name),
281
+ onSuccess: async (slicesIds: string[]) => {
282
+ // TODO(DT-1453): Remove the need of the global getState
283
+ const serverState = await getState();
284
+
285
+ // Update Redux store
286
+ createSliceSuccess(serverState.libraries);
287
+
288
+ // eslint-disable-next-line @typescript-eslint/no-unsafe-return
289
+ onSelectSharedSlices(
290
+ slicesIds.concat(
291
+ sharedSlicesInSliceZone.map((s) => s.model.id)
292
+ ),
293
+ nonSharedSlicesKeysInSliceZone
294
+ );
295
+
296
+ setIsSlicesTemplatesModalOpen(false);
297
+ },
298
+ })
299
+ }
300
+ close={() => setIsSlicesTemplatesModalOpen(false)}
221
301
  />
222
302
  <DeleteSliceZoneModal
223
303
  isDeleteSliceZoneModalOpen={isDeleteSliceZoneModalOpen}