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.
- package/.claude/settings.local.json +7 -0
- package/dist/components/pages/CreatorsProductEdition/CreatorsProductEditionView.js +90 -135
- package/dist/components/pages/CreatorsProductEdition/hooks/useCreatorsProductEdition.js +148 -162
- package/dist/components/pages/CreatorsProductEdition/mocks/api.fixtures.js +16 -0
- package/dist/components/pages/CreatorsProductEdition/mocks/mockApi.js +1 -0
- package/dist/components/pages/CreatorsProductEdition/sections/ImageGallery.js +433 -0
- package/dist/components/pages/CreatorsProductEdition/sections/NoticeDialog.js +155 -0
- package/dist/components/pages/CreatorsProductEdition/sections/RejectDialog.js +201 -0
- package/dist/components/pages/CreatorsProductEdition/sections/VersionDrawer.js +615 -0
- package/dist/components/pages/CreatorsProductEdition/stories/CreatorsProductEdition.stories.js +3 -31
- package/dist/components/pages/CreatorsProductEdition/stories/CreatorsProductEditionAPI.stories.js +55 -30
- package/dist/components/pages/CreatorsProductEdition/stories/products.js +923 -0
- package/dist/components/pages/CreatorsProductEdition/styles.js +27 -7
- package/dist/components/pages/ProviderProductEdition/ProviderProductEdition.stories.js +2 -1
- package/dist/components/pages/ProviderProductEdition/context/provider-product-edition.context.js +325 -10
- package/dist/components/pages/ProviderProductEdition/context/provider-product-edition.reducer.js +12 -2
- package/dist/components/pages/ProviderProductEdition/index.js +486 -231
- package/dist/index.js +13 -0
- package/package.json +1 -1
- package/src/components/pages/CreatorsProductEdition/CreatorsProductEditionView.jsx +777 -0
- package/src/components/pages/CreatorsProductEdition/constants.js +21 -0
- package/src/components/pages/CreatorsProductEdition/hooks/useCreatorsProductEdition.js +2238 -0
- package/src/components/pages/CreatorsProductEdition/index.js +19 -0
- package/src/components/pages/CreatorsProductEdition/mocks/api.fixtures.js +357 -0
- package/src/components/pages/CreatorsProductEdition/mocks/mockApi.js +144 -0
- package/src/components/pages/CreatorsProductEdition/sections/CommentBox.jsx +183 -0
- package/src/components/pages/CreatorsProductEdition/sections/ImageGallery.jsx +269 -0
- package/src/components/pages/CreatorsProductEdition/sections/ImagePanel.jsx +332 -0
- package/src/components/pages/CreatorsProductEdition/sections/ImagesSection.jsx +385 -0
- package/src/components/pages/CreatorsProductEdition/sections/NoticeDialog.jsx +111 -0
- package/src/components/pages/CreatorsProductEdition/sections/ProductHeader.jsx +305 -0
- package/src/components/pages/CreatorsProductEdition/sections/ProductMedia.jsx +101 -0
- package/src/components/pages/CreatorsProductEdition/sections/RejectDialog.jsx +125 -0
- package/src/components/pages/CreatorsProductEdition/sections/RetailerMenu.jsx +129 -0
- package/src/components/pages/CreatorsProductEdition/sections/ServiceActions.jsx +382 -0
- package/src/components/pages/CreatorsProductEdition/sections/StatusChip.jsx +37 -0
- package/src/components/pages/CreatorsProductEdition/sections/TabsBar.jsx +187 -0
- package/src/components/pages/CreatorsProductEdition/sections/VariantsPanel.jsx +311 -0
- package/src/components/pages/CreatorsProductEdition/sections/VersionDrawer.jsx +357 -0
- package/src/components/pages/CreatorsProductEdition/sections/tokens.js +63 -0
- package/src/components/pages/CreatorsProductEdition/stories/CreatorsProductEdition.stories.js +111 -0
- package/src/components/pages/CreatorsProductEdition/stories/CreatorsProductEditionAPI.stories.js +85 -0
- package/src/components/pages/CreatorsProductEdition/stories/products.js +966 -0
- package/src/components/pages/CreatorsProductEdition/styles.js +178 -0
- package/src/components/pages/ProviderProductEdition/ProviderProductEdition.stories.js +1 -0
- package/src/components/pages/ProviderProductEdition/context/provider-product-edition.context.jsx +201 -0
- package/src/components/pages/ProviderProductEdition/context/provider-product-edition.reducer.js +12 -0
- package/src/components/pages/ProviderProductEdition/index.js +228 -7
- 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
|
+
};
|