contentoh-components-library 21.6.57 → 21.6.59

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 (49) hide show
  1. package/.claude/settings.local.json +7 -0
  2. package/dist/components/pages/CreatorsProductEdition/CreatorsProductEditionView.js +90 -135
  3. package/dist/components/pages/CreatorsProductEdition/hooks/useCreatorsProductEdition.js +148 -162
  4. package/dist/components/pages/CreatorsProductEdition/mocks/api.fixtures.js +16 -0
  5. package/dist/components/pages/CreatorsProductEdition/mocks/mockApi.js +1 -0
  6. package/dist/components/pages/CreatorsProductEdition/sections/ImageGallery.js +433 -0
  7. package/dist/components/pages/CreatorsProductEdition/sections/NoticeDialog.js +155 -0
  8. package/dist/components/pages/CreatorsProductEdition/sections/RejectDialog.js +201 -0
  9. package/dist/components/pages/CreatorsProductEdition/sections/VersionDrawer.js +615 -0
  10. package/dist/components/pages/CreatorsProductEdition/stories/CreatorsProductEdition.stories.js +3 -31
  11. package/dist/components/pages/CreatorsProductEdition/stories/CreatorsProductEditionAPI.stories.js +55 -30
  12. package/dist/components/pages/CreatorsProductEdition/stories/products.js +923 -0
  13. package/dist/components/pages/CreatorsProductEdition/styles.js +27 -7
  14. package/dist/components/pages/ProviderProductEdition/ProviderProductEdition.stories.js +2 -1
  15. package/dist/components/pages/ProviderProductEdition/context/provider-product-edition.context.js +325 -10
  16. package/dist/components/pages/ProviderProductEdition/context/provider-product-edition.reducer.js +12 -2
  17. package/dist/components/pages/ProviderProductEdition/index.js +486 -231
  18. package/dist/index.js +13 -0
  19. package/package.json +1 -1
  20. package/src/components/pages/CreatorsProductEdition/CreatorsProductEditionView.jsx +777 -0
  21. package/src/components/pages/CreatorsProductEdition/constants.js +21 -0
  22. package/src/components/pages/CreatorsProductEdition/hooks/useCreatorsProductEdition.js +2238 -0
  23. package/src/components/pages/CreatorsProductEdition/index.js +19 -0
  24. package/src/components/pages/CreatorsProductEdition/mocks/api.fixtures.js +357 -0
  25. package/src/components/pages/CreatorsProductEdition/mocks/mockApi.js +144 -0
  26. package/src/components/pages/CreatorsProductEdition/sections/CommentBox.jsx +183 -0
  27. package/src/components/pages/CreatorsProductEdition/sections/ImageGallery.jsx +269 -0
  28. package/src/components/pages/CreatorsProductEdition/sections/ImagePanel.jsx +332 -0
  29. package/src/components/pages/CreatorsProductEdition/sections/ImagesSection.jsx +385 -0
  30. package/src/components/pages/CreatorsProductEdition/sections/NoticeDialog.jsx +111 -0
  31. package/src/components/pages/CreatorsProductEdition/sections/ProductHeader.jsx +305 -0
  32. package/src/components/pages/CreatorsProductEdition/sections/ProductMedia.jsx +101 -0
  33. package/src/components/pages/CreatorsProductEdition/sections/RejectDialog.jsx +125 -0
  34. package/src/components/pages/CreatorsProductEdition/sections/RetailerMenu.jsx +129 -0
  35. package/src/components/pages/CreatorsProductEdition/sections/ServiceActions.jsx +382 -0
  36. package/src/components/pages/CreatorsProductEdition/sections/StatusChip.jsx +37 -0
  37. package/src/components/pages/CreatorsProductEdition/sections/TabsBar.jsx +187 -0
  38. package/src/components/pages/CreatorsProductEdition/sections/VariantsPanel.jsx +311 -0
  39. package/src/components/pages/CreatorsProductEdition/sections/VersionDrawer.jsx +357 -0
  40. package/src/components/pages/CreatorsProductEdition/sections/tokens.js +63 -0
  41. package/src/components/pages/CreatorsProductEdition/stories/CreatorsProductEdition.stories.js +111 -0
  42. package/src/components/pages/CreatorsProductEdition/stories/CreatorsProductEditionAPI.stories.js +85 -0
  43. package/src/components/pages/CreatorsProductEdition/stories/products.js +966 -0
  44. package/src/components/pages/CreatorsProductEdition/styles.js +178 -0
  45. package/src/components/pages/ProviderProductEdition/ProviderProductEdition.stories.js +1 -0
  46. package/src/components/pages/ProviderProductEdition/context/provider-product-edition.context.jsx +201 -0
  47. package/src/components/pages/ProviderProductEdition/context/provider-product-edition.reducer.js +12 -0
  48. package/src/components/pages/ProviderProductEdition/index.js +228 -7
  49. package/src/index.js +1 -0
@@ -0,0 +1,311 @@
1
+ import {
2
+ Box,
3
+ Button,
4
+ ButtonBase,
5
+ Collapse,
6
+ Link,
7
+ Paper,
8
+ Stack,
9
+ Typography,
10
+ } from "@mui/material";
11
+ import ArrowDropDownIcon from "@mui/icons-material/ArrowDropDown";
12
+ import ArrowDropUpIcon from "@mui/icons-material/ArrowDropUp";
13
+
14
+ import { COLORS, FONTS } from "./tokens";
15
+
16
+ /*
17
+ Variantes del producto: padre/hijo (ligar, ver la familia, desvincular) y
18
+ agrupación de variantes (agrupar, ver el grupo, salir del grupo).
19
+
20
+ Reemplaza el marcado de la vista original (clases variants-actions,
21
+ variants-family, family-row...) con MUI, con el mismo comportamiento. Los dos
22
+ mecanismos siguen separados a propósito: padre/hijo es herencia; el grupo es
23
+ solo un código compartido, y juntarlos haría creer que agrupar hereda algo.
24
+ */
25
+
26
+ // Rosa ContentOh, el mismo del modal de ligar productos.
27
+ const PINK = "#E13AA8";
28
+
29
+ const OutlineButton = ({ children, onClick }) => (
30
+ <Button
31
+ variant="outlined"
32
+ size="small"
33
+ onClick={onClick}
34
+ sx={{
35
+ borderColor: PINK,
36
+ color: PINK,
37
+ borderRadius: "6px",
38
+ px: "12px",
39
+ py: "5px",
40
+ minHeight: 0,
41
+ lineHeight: 1.3,
42
+ textTransform: "none",
43
+ fontFamily: FONTS.Raleway_600,
44
+ fontSize: 11,
45
+ "&:hover": { borderColor: PINK, bgcolor: `rgba(${COLORS.rgb_color_pink}, 0.06)` },
46
+ }}
47
+ >
48
+ {children}
49
+ </Button>
50
+ );
51
+
52
+ const ToggleLink = ({ open, onClick, children }) => (
53
+ <Link
54
+ component="button"
55
+ type="button"
56
+ aria-expanded={open}
57
+ onClick={onClick}
58
+ sx={{
59
+ display: "inline-flex",
60
+ alignItems: "center",
61
+ fontFamily: FONTS.Raleway_500,
62
+ fontSize: 11,
63
+ color: COLORS.gray,
64
+ textDecorationColor: "currentColor",
65
+ }}
66
+ >
67
+ {children}
68
+ {open ? <ArrowDropUpIcon fontSize="small" /> : <ArrowDropDownIcon fontSize="small" />}
69
+ </Link>
70
+ );
71
+
72
+ const SmallLink = ({ onClick, title, children }) => (
73
+ <Link
74
+ component="button"
75
+ type="button"
76
+ title={title}
77
+ onClick={onClick}
78
+ sx={{
79
+ flexShrink: 0,
80
+ px: "6px",
81
+ py: "4px",
82
+ fontFamily: FONTS.Raleway_500,
83
+ fontSize: 10,
84
+ color: COLORS.gray,
85
+ whiteSpace: "nowrap",
86
+ textDecorationColor: "currentColor",
87
+ "&:hover": { color: PINK },
88
+ }}
89
+ >
90
+ {children}
91
+ </Link>
92
+ );
93
+
94
+ /** Fila de la familia: el botón navega; "Desvincular" queda FUERA de él. */
95
+ const FamilyRow = ({ role, name, upc, disabled, current, onOpen, action }) => (
96
+ <Stack direction="row" alignItems="center" gap="4px">
97
+ <ButtonBase
98
+ disabled={disabled}
99
+ onClick={onOpen}
100
+ sx={{
101
+ flex: 1,
102
+ minWidth: 0,
103
+ display: "flex",
104
+ flexWrap: { xs: "wrap", sm: "nowrap" },
105
+ alignItems: "baseline",
106
+ justifyContent: "flex-start",
107
+ columnGap: 1,
108
+ rowGap: "2px",
109
+ px: 1,
110
+ py: "6px",
111
+ borderRadius: "4px",
112
+ textAlign: "left",
113
+ fontFamily: FONTS.Raleway_500,
114
+ fontSize: 12,
115
+ color: COLORS.deep_gray,
116
+ "&:hover:not(:disabled)": { bgcolor: COLORS.s2 },
117
+ "&.Mui-disabled": { opacity: 0.7 },
118
+ }}
119
+ >
120
+ <Box component="span" sx={{ minWidth: 96, fontSize: 10, color: current ? COLORS.gray : PINK }}>
121
+ {role}
122
+ </Box>
123
+ <Box
124
+ component="span"
125
+ sx={{ flex: 1, minWidth: 0, overflow: "hidden", textOverflow: "ellipsis", whiteSpace: "nowrap" }}
126
+ >
127
+ {name}
128
+ </Box>
129
+ <Box component="span" sx={{ fontSize: 11, color: COLORS.gray }}>
130
+ {upc}
131
+ </Box>
132
+ </ButtonBase>
133
+ {action}
134
+ </Stack>
135
+ );
136
+
137
+ const Note = ({ children }) => (
138
+ <Typography sx={{ m: 0, fontFamily: FONTS.Raleway_500, fontSize: 11, color: COLORS.gray }}>
139
+ {children}
140
+ </Typography>
141
+ );
142
+
143
+ const FamilyBox = ({ open, children }) => (
144
+ <Collapse in={open} unmountOnExit>
145
+ <Paper
146
+ variant="outlined"
147
+ sx={{ display: "flex", flexDirection: "column", gap: "4px", mb: "10px", p: 1, borderColor: COLORS.s2, borderRadius: "6px" }}
148
+ >
149
+ {children}
150
+ </Paper>
151
+ </Collapse>
152
+ );
153
+
154
+ export const VariantsPanel = ({
155
+ product,
156
+ // padre/hijo
157
+ esHijoDeOtro,
158
+ hijosDelProducto,
159
+ family,
160
+ showFamily,
161
+ setShowFamily,
162
+ loadFamily,
163
+ loadingVariant,
164
+ avisoFamilia,
165
+ openFamilyMember,
166
+ setPorDesvincular,
167
+ setShowLinkVariants,
168
+ // agrupación
169
+ puedeAgruparse,
170
+ variantGroup,
171
+ showGroup,
172
+ setShowGroup,
173
+ setShowGroupModal,
174
+ handleOnLeaveGroup,
175
+ }) => {
176
+ const toggleFamily = () => {
177
+ setShowFamily((abierto) => !abierto);
178
+ loadFamily();
179
+ };
180
+ const opening = (articleId, label) => (loadingVariant === articleId ? "Abriendo…" : label);
181
+ const otherMembers = (variantGroup?.members ?? []).filter((miembro) => !miembro.isCurrent);
182
+
183
+ return (
184
+ <Box sx={{ minWidth: 0 }}>
185
+ {/* Solo si el producto NO es ya hijo de alguien: ligarlo otra vez
186
+ devolvería VARIANT_ALREADY_LINKED sin excepción. */}
187
+ <Stack direction="row" flexWrap="wrap" alignItems="center" gap="10px" sx={{ mb: 1 }}>
188
+ {!esHijoDeOtro && (
189
+ <OutlineButton onClick={() => setShowLinkVariants(true)}>Ligar productos</OutlineButton>
190
+ )}
191
+ {!esHijoDeOtro && hijosDelProducto > 0 && (
192
+ <ToggleLink open={showFamily} onClick={toggleFamily}>
193
+ {hijosDelProducto} hijo{hijosDelProducto === 1 ? "" : "s"}
194
+ </ToggleLink>
195
+ )}
196
+ {esHijoDeOtro && (
197
+ <ToggleLink open={showFamily} onClick={toggleFamily}>
198
+ Hijo de otro producto
199
+ </ToggleLink>
200
+ )}
201
+ </Stack>
202
+
203
+ <FamilyBox open={showFamily}>
204
+ {!family && <Note>Cargando familia…</Note>}
205
+ {family?.error && <Note>No se pudo cargar la familia de este producto.</Note>}
206
+
207
+ {/* Desvincular se ofrece donde se ve el vínculo, y solo en la dirección
208
+ que corresponde: desde un hijo, sobre su padre; desde un padre, sobre
209
+ cada hijo. A un hermano no: para eso se abre su ficha. */}
210
+ {family?.parent && (
211
+ <FamilyRow
212
+ role={opening(family.parent.articleId, "Producto padre")}
213
+ name={family.parent.name}
214
+ upc={family.parent.upc}
215
+ disabled={loadingVariant !== null}
216
+ onOpen={() => openFamilyMember(family.parent.articleId)}
217
+ action={
218
+ <SmallLink
219
+ title="Dejar de ser hijo de este producto"
220
+ onClick={() =>
221
+ setPorDesvincular({
222
+ mode: "self",
223
+ childArticleId: product?.id_article,
224
+ childName: product?.name,
225
+ childUpc: product?.upc,
226
+ parentName: family.parent.name,
227
+ parentUpc: family.parent.upc,
228
+ })
229
+ }
230
+ >
231
+ Desvincular
232
+ </SmallLink>
233
+ }
234
+ />
235
+ )}
236
+
237
+ {family?.variants?.map((variant) => (
238
+ <FamilyRow
239
+ key={variant.articleId}
240
+ role={opening(variant.articleId, variant.isCurrent ? "Estás aquí" : "Hijo")}
241
+ name={variant.name}
242
+ upc={variant.upc}
243
+ current={variant.isCurrent}
244
+ disabled={variant.isCurrent || loadingVariant !== null}
245
+ onOpen={() => openFamilyMember(variant.articleId)}
246
+ action={
247
+ // Solo cuando esta ficha ES el padre: si hay `parent`, lo que se
248
+ // lista son hermanos.
249
+ !family.parent && !variant.isCurrent ? (
250
+ <SmallLink
251
+ title="Desvincular este hijo"
252
+ onClick={() =>
253
+ setPorDesvincular({
254
+ mode: "child",
255
+ childArticleId: variant.articleId,
256
+ childName: variant.name,
257
+ childUpc: variant.upc,
258
+ parentName: product?.name,
259
+ parentUpc: product?.upc,
260
+ })
261
+ }
262
+ >
263
+ Desvincular
264
+ </SmallLink>
265
+ ) : null
266
+ }
267
+ />
268
+ ))}
269
+
270
+ {family && !family.error && !family.variants?.length && <Note>Este producto no tiene hijos.</Note>}
271
+ {avisoFamilia && <Note>{avisoFamilia}</Note>}
272
+ </FamilyBox>
273
+
274
+ {puedeAgruparse && (
275
+ <>
276
+ <Stack direction="row" flexWrap="wrap" alignItems="center" gap="10px" sx={{ mb: 1 }}>
277
+ <OutlineButton onClick={() => setShowGroupModal(true)}>
278
+ {variantGroup?.group ? "Cambiar de grupo" : "Agrupar variantes"}
279
+ </OutlineButton>
280
+ {variantGroup?.group && (
281
+ <ToggleLink open={showGroup} onClick={() => setShowGroup((abierto) => !abierto)}>
282
+ Variante {variantGroup.group}
283
+ </ToggleLink>
284
+ )}
285
+ </Stack>
286
+
287
+ <FamilyBox open={showGroup && Boolean(variantGroup?.group)}>
288
+ {otherMembers.map((miembro) => (
289
+ <FamilyRow
290
+ key={miembro.articleId}
291
+ role={opening(miembro.articleId, "Variante")}
292
+ name={miembro.name}
293
+ upc={miembro.upc}
294
+ disabled={loadingVariant !== null}
295
+ onOpen={() => openFamilyMember(miembro.articleId)}
296
+ />
297
+ ))}
298
+ {otherMembers.length === 0 && (
299
+ <Note>Este producto es el único en el grupo {variantGroup?.group}.</Note>
300
+ )}
301
+ <Box>
302
+ <SmallLink title="Quitar este producto del grupo" onClick={handleOnLeaveGroup}>
303
+ Quitar del grupo
304
+ </SmallLink>
305
+ </Box>
306
+ </FamilyBox>
307
+ </>
308
+ )}
309
+ </Box>
310
+ );
311
+ };
@@ -0,0 +1,357 @@
1
+ import { useEffect, useState } from "react";
2
+ import axios from "axios";
3
+ import {
4
+ Box,
5
+ Button,
6
+ ButtonBase,
7
+ Dialog,
8
+ Drawer,
9
+ FormControl,
10
+ IconButton,
11
+ InputLabel,
12
+ MenuItem,
13
+ Select,
14
+ Stack,
15
+ Tooltip,
16
+ Typography,
17
+ useMediaQuery,
18
+ } from "@mui/material";
19
+ import AddIcon from "@mui/icons-material/Add";
20
+ import CloseIcon from "@mui/icons-material/Close";
21
+
22
+ import nullIcon from "../../../../assets/images/versionSelector/nullIcon.svg";
23
+ import acceptIcon from "../../../../assets/images/versionSelector/acceptIcon.svg";
24
+ import versionSelected from "../../../../assets/images/versionSelector/versionSelected.svg";
25
+ import createVersionImg from "../../../../assets/images/versionSelector/createVersion.svg";
26
+ import warningIcon from "../../../../assets/images/genericModal/genericModalWarning.svg";
27
+
28
+ import { COLORS, FONTS } from "./tokens";
29
+
30
+ /*
31
+ Versiones del producto: lista, cambio de versión y creación de una nueva.
32
+
33
+ Reemplaza a `organisms/VersionSelector` (+ VersionItem y CreateVersion) con
34
+ los mismos props, las mismas llamadas y el mismo diseño oscuro, hecho con el
35
+ Drawer y el Dialog de MUI. El original era un panel `absolute` de 300px que
36
+ en celular tapaba media pantalla sin poder cerrarse tocando fuera; aquí en
37
+ celular ocupa todo el ancho.
38
+
39
+ El diálogo de nueva versión va con `disablePortal` para quedar dentro del
40
+ panel en el DOM.
41
+ */
42
+
43
+ const WHITE_BUTTON = {
44
+ textTransform: "none",
45
+ borderRadius: "20px",
46
+ minHeight: 40,
47
+ flex: "1 1 0",
48
+ fontFamily: FONTS.Raleway_500,
49
+ color: COLORS.white,
50
+ border: `1px solid ${COLORS.white}`,
51
+ "&:hover": { bgcolor: "rgba(255,255,255,0.08)" },
52
+ };
53
+
54
+ const PINK_BUTTON = {
55
+ ...WHITE_BUTTON,
56
+ border: 0,
57
+ bgcolor: COLORS.magenta_s2,
58
+ "&:hover": { bgcolor: COLORS.secondary_magenta },
59
+ };
60
+
61
+ const exportedText = (timestamp) =>
62
+ timestamp ? `Última vez exportado: ${new Date(timestamp).toLocaleDateString()}` : "";
63
+
64
+ // --- Una versión de la lista ----------------------------------------------
65
+
66
+ const VersionRow = ({ item, isCurrent, companyName, onSelect }) => {
67
+ const validated = Boolean(item.article_status);
68
+ const text = { fontFamily: FONTS.AvenirNext, fontSize: 12, color: COLORS.s2 };
69
+
70
+ return (
71
+ <Box sx={{ pb: "5px", borderBottom: `1px solid ${COLORS.s4}` }}>
72
+ <Stack direction="row" alignItems="center" justifyContent="space-between">
73
+ <Tooltip title={isCurrent ? "Versión actual" : "Usar esta versión"} arrow>
74
+ <span>
75
+ <ButtonBase
76
+ aria-label={`Usar la versión ${item.version}`}
77
+ disabled={isCurrent}
78
+ onClick={() => onSelect(item.version)}
79
+ sx={{ borderRadius: "50%", opacity: isCurrent ? 0.4 : 1 }}
80
+ >
81
+ <Box component="img" src={versionSelected} alt="" sx={{ width: 30, height: 30 }} />
82
+ </ButtonBase>
83
+ </span>
84
+ </Tooltip>
85
+ <Box
86
+ component="img"
87
+ src={validated ? acceptIcon : nullIcon}
88
+ alt={validated ? "Versión validada" : "Versión sin validar"}
89
+ />
90
+ </Stack>
91
+ <Stack alignItems="flex-end" gap="5px" sx={{ textAlign: "right" }}>
92
+ <Typography sx={{ fontFamily: FONTS.Raleway_600, fontSize: 16, color: COLORS.magenta_s2 }}>
93
+ Versión {item.version}
94
+ </Typography>
95
+ {item.timestamp && <Typography sx={text}>{exportedText(item.timestamp)}</Typography>}
96
+ <Typography sx={text}>
97
+ {isCurrent ? "Versión actual" : validated ? "Edición Content-Oh!" : `Versión de ${companyName}`}
98
+ </Typography>
99
+ <Typography sx={text}>{validated ? "Content-Oh! digital" : companyName}</Typography>
100
+ </Stack>
101
+ </Box>
102
+ );
103
+ };
104
+
105
+ // --- Nueva versión -------------------------------------------------------
106
+
107
+ const CONTENT_SELECTS = [
108
+ { id: "datasheet", label: "Ficha técnica" },
109
+ { id: "description", label: "Descripción" },
110
+ { id: "image", label: "Imágenes" },
111
+ ];
112
+
113
+ const CreateVersionDialog = ({ open, onClose, articleId, currentVersion, versions, jwt, onCreated }) => {
114
+ const [step, setStep] = useState("options");
115
+ const [selected, setSelected] = useState({});
116
+ const [saving, setSaving] = useState(false);
117
+ const isPhone = useMediaQuery("(max-width:599.95px)");
118
+
119
+ useEffect(() => {
120
+ if (open) {
121
+ setStep("options");
122
+ setSelected({ datasheet: currentVersion, description: currentVersion, image: currentVersion });
123
+ }
124
+ }, [open, currentVersion]);
125
+
126
+ // Mismo POST que `CreateVersion`.
127
+ const createVersion = async (isEmpty) => {
128
+ setSaving(true);
129
+ try {
130
+ const data = { articleId, isEmpty };
131
+ if (!isEmpty) data.version = selected;
132
+ await axios.post(process.env.REACT_APP_VERSIONS_ENDPOINT, data, {
133
+ headers: { Authorization: jwt },
134
+ });
135
+ onClose();
136
+ onCreated();
137
+ } catch (error) {
138
+ console.error(error);
139
+ } finally {
140
+ setSaving(false);
141
+ }
142
+ };
143
+
144
+ const title = (text) => (
145
+ <Typography sx={{ fontFamily: FONTS.Raleway_500, fontSize: 22, lineHeight: 1.3 }}>{text}</Typography>
146
+ );
147
+ const subtitle = (text) => (
148
+ <Typography sx={{ fontFamily: FONTS.Raleway, fontSize: 16, lineHeight: 1.4 }}>{text}</Typography>
149
+ );
150
+
151
+ const steps = {
152
+ options: (
153
+ <>
154
+ <Box component="img" src={createVersionImg} alt="" sx={{ mx: "auto" }} />
155
+ {title("Crea una nueva versión")}
156
+ {subtitle("Puedes elegir el contenido de versiones anteriores o crear una versión desde cero")}
157
+ <Stack direction={{ xs: "column", sm: "row" }} gap={2}>
158
+ <Button sx={WHITE_BUTTON} onClick={() => setStep("empty")}>
159
+ Versión desde cero
160
+ </Button>
161
+ <Button sx={PINK_BUTTON} onClick={() => setStep("custom")}>
162
+ Versión personalizada
163
+ </Button>
164
+ </Stack>
165
+ </>
166
+ ),
167
+ empty: (
168
+ <>
169
+ <Box component="img" src={warningIcon} alt="" sx={{ mx: "auto" }} />
170
+ {title("Si creas una versión desde cero perderás la información actual")}
171
+ {subtitle("¿Deseas continuar?")}
172
+ <Stack direction={{ xs: "column", sm: "row" }} gap={2}>
173
+ <Button sx={WHITE_BUTTON} onClick={() => setStep("options")}>
174
+ Atrás
175
+ </Button>
176
+ <Button sx={PINK_BUTTON} disabled={saving} onClick={() => createVersion(true)}>
177
+ Continuar
178
+ </Button>
179
+ </Stack>
180
+ </>
181
+ ),
182
+ custom: (
183
+ <>
184
+ {subtitle("Elige el contenido que deseas utilizar de versiones anteriores")}
185
+ <Stack gap={2}>
186
+ {CONTENT_SELECTS.map(({ id, label }) => (
187
+ <FormControl key={id} size="small" fullWidth>
188
+ <InputLabel id={`${id}-label`} sx={{ color: COLORS.s3, "&.Mui-focused": { color: COLORS.white } }}>
189
+ {label}
190
+ </InputLabel>
191
+ <Select
192
+ labelId={`${id}-label`}
193
+ label={label}
194
+ value={selected[id] ?? ""}
195
+ onChange={(e) => setSelected((prev) => ({ ...prev, [id]: parseInt(e.target.value) }))}
196
+ MenuProps={{ disablePortal: true }}
197
+ sx={{
198
+ color: COLORS.white,
199
+ textAlign: "left",
200
+ "& .MuiOutlinedInput-notchedOutline": { borderColor: COLORS.s4 },
201
+ "&:hover .MuiOutlinedInput-notchedOutline": { borderColor: COLORS.s3 },
202
+ "&.Mui-focused .MuiOutlinedInput-notchedOutline": { borderColor: COLORS.magenta_s2 },
203
+ "& .MuiSvgIcon-root": { color: COLORS.white },
204
+ }}
205
+ >
206
+ {versions.map((item) => (
207
+ <MenuItem key={`${id}-${item.version}`} value={item.version}>
208
+ Versión {item.version}
209
+ </MenuItem>
210
+ ))}
211
+ </Select>
212
+ </FormControl>
213
+ ))}
214
+ </Stack>
215
+ <Stack direction={{ xs: "column", sm: "row" }} gap={2}>
216
+ <Button sx={WHITE_BUTTON} onClick={() => setStep("options")}>
217
+ Atrás
218
+ </Button>
219
+ <Button sx={PINK_BUTTON} disabled={saving} onClick={() => createVersion(false)}>
220
+ Continuar
221
+ </Button>
222
+ </Stack>
223
+ </>
224
+ ),
225
+ };
226
+
227
+ return (
228
+ <Dialog
229
+ open={open}
230
+ onClose={onClose}
231
+ disablePortal
232
+ fullScreen={isPhone}
233
+ maxWidth={false}
234
+ PaperProps={{
235
+ id: "create-version",
236
+ sx: {
237
+ width: { sm: "min(600px, calc(100% - 64px))" },
238
+ bgcolor: COLORS.deep_gray,
239
+ color: COLORS.white,
240
+ borderRadius: { xs: 0, sm: "39px" },
241
+ p: { xs: "56px 20px 24px", sm: "40px" },
242
+ justifyContent: { xs: "center", sm: "flex-start" },
243
+ },
244
+ }}
245
+ >
246
+ <IconButton
247
+ aria-label="Cerrar"
248
+ onClick={onClose}
249
+ sx={{ position: "absolute", top: 16, right: 16, color: COLORS.white }}
250
+ >
251
+ <CloseIcon />
252
+ </IconButton>
253
+ <Stack gap={2.5} sx={{ textAlign: "center" }}>
254
+ {steps[step]}
255
+ </Stack>
256
+ </Dialog>
257
+ );
258
+ };
259
+
260
+ // --- Panel ---------------------------------------------------------------
261
+
262
+ export const VersionDrawer = ({
263
+ open,
264
+ modalId = "version-selector",
265
+ articleId,
266
+ setVersion,
267
+ companyName,
268
+ currentVersion,
269
+ setShowVersionSelector,
270
+ jwt,
271
+ }) => {
272
+ const [versions, setVersions] = useState([]);
273
+ const [showCreate, setShowCreate] = useState(false);
274
+ const [reload, setReload] = useState(0);
275
+
276
+ // Misma llamada que `VersionSelector`.
277
+ useEffect(() => {
278
+ if (!open) return undefined;
279
+ let active = true;
280
+ axios
281
+ .get(`${process.env.REACT_APP_VERSIONS_ENDPOINT}?articleId=${articleId}&provider=true`, {
282
+ headers: { Authorization: jwt },
283
+ })
284
+ .then((response) => active && setVersions(JSON.parse(response.data.body).data ?? []))
285
+ .catch((error) => console.error(error));
286
+ return () => {
287
+ active = false;
288
+ };
289
+ }, [open, articleId, jwt, reload]);
290
+
291
+ useEffect(() => {
292
+ if (!open) setShowCreate(false);
293
+ }, [open]);
294
+
295
+ return (
296
+ <Drawer
297
+ anchor="right"
298
+ open={open}
299
+ onClose={() => setShowVersionSelector(false)}
300
+ PaperProps={{
301
+ id: modalId,
302
+ sx: {
303
+ width: { xs: "100%", sm: 300 },
304
+ bgcolor: COLORS.deep_gray,
305
+ color: COLORS.white,
306
+ p: "15px",
307
+ },
308
+ }}
309
+ >
310
+ <Stack direction="row" alignItems="center" justifyContent="space-between" sx={{ mb: 2 }}>
311
+ <Typography sx={{ fontFamily: FONTS.Lato, fontSize: 18 }}>Versión del producto</Typography>
312
+ <Stack direction="row" gap={1}>
313
+ <Tooltip title="Nueva versión" arrow>
314
+ <IconButton
315
+ id="add-version"
316
+ aria-label="Nueva versión"
317
+ onClick={() => setShowCreate(true)}
318
+ sx={{ color: COLORS.white, border: `1px solid ${COLORS.s4}`, width: 32, height: 32 }}
319
+ >
320
+ <AddIcon fontSize="small" />
321
+ </IconButton>
322
+ </Tooltip>
323
+ <IconButton
324
+ id="close-button"
325
+ aria-label="Cerrar"
326
+ onClick={() => setShowVersionSelector(false)}
327
+ sx={{ color: COLORS.white, border: `1px solid ${COLORS.s4}`, width: 32, height: 32 }}
328
+ >
329
+ <CloseIcon fontSize="small" />
330
+ </IconButton>
331
+ </Stack>
332
+ </Stack>
333
+
334
+ <Stack gap="18px">
335
+ {versions.map((item) => (
336
+ <VersionRow
337
+ key={item.version}
338
+ item={item}
339
+ isCurrent={item.version === currentVersion}
340
+ companyName={companyName}
341
+ onSelect={setVersion}
342
+ />
343
+ ))}
344
+ </Stack>
345
+
346
+ <CreateVersionDialog
347
+ open={showCreate}
348
+ onClose={() => setShowCreate(false)}
349
+ articleId={articleId}
350
+ currentVersion={currentVersion}
351
+ versions={versions}
352
+ jwt={jwt}
353
+ onCreated={() => setReload((n) => n + 1)}
354
+ />
355
+ </Drawer>
356
+ );
357
+ };