contentoh-components-library 21.6.24 → 21.6.25
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/dist/components/organisms/LinkVariantsModal/index.js +115 -63
- package/dist/components/organisms/LinkVariantsModal/styles.js +26 -16
- package/dist/components/pages/ProviderProductEdition/styles.js +4 -1
- package/package.json +1 -1
- package/src/components/organisms/LinkVariantsModal/index.js +133 -44
- package/src/components/organisms/LinkVariantsModal/styles.js +69 -49
- package/src/components/pages/ProviderProductEdition/styles.js +6 -3
|
@@ -2,10 +2,7 @@ import { useCallback, useEffect, useMemo, useState } from "react";
|
|
|
2
2
|
import axios from "axios";
|
|
3
3
|
|
|
4
4
|
import { readPayload, ApiError } from "./api";
|
|
5
|
-
import {
|
|
6
|
-
getProductImage,
|
|
7
|
-
getRetailerPicture,
|
|
8
|
-
} from "../../../_utils/helper";
|
|
5
|
+
import { getProductImage, getRetailerPicture } from "../../../_utils/helper";
|
|
9
6
|
|
|
10
7
|
import {
|
|
11
8
|
Overlay,
|
|
@@ -130,11 +127,27 @@ export const LinkVariantsModal = ({
|
|
|
130
127
|
cuatro decisiones por producto. Al desmarcarlo aparecen las pestañas y cada
|
|
131
128
|
producto guarda su propia selección en `perChild`.
|
|
132
129
|
*/
|
|
133
|
-
|
|
130
|
+
// Desmarcado a propósito: aplicar la misma herencia a varios productos es una
|
|
131
|
+
// decisión, no un valor por omisión. Marcado de entrada se ligaban todos igual
|
|
132
|
+
// sin que nadie lo hubiera elegido.
|
|
133
|
+
const [applyToAll, setApplyToAll] = useState(false);
|
|
134
134
|
const [activeChild, setActiveChild] = useState(null);
|
|
135
135
|
const [perChild, setPerChild] = useState({});
|
|
136
136
|
|
|
137
137
|
// Cadenas que el padre NO tiene y el usuario decidió agregar a la variante.
|
|
138
|
+
/*
|
|
139
|
+
Imágenes que la base lista pero cuyo archivo no está en S3.
|
|
140
|
+
|
|
141
|
+
Pasa: hay filas de rel_article_image con un src válido que apunta a un objeto
|
|
142
|
+
que ya no existe —comprobado, CloudFront responde 404—. Sin esto el paso 4
|
|
143
|
+
muestra una tarjeta rota, con el texto alternativo donde debería ir la foto, y
|
|
144
|
+
deja marcar para heredar algo que no se puede heredar.
|
|
145
|
+
|
|
146
|
+
No se puede saber desde el servidor sin ir a S3 por cada imagen, así que se
|
|
147
|
+
detecta al fallar la carga y se descuenta de la lista y del conteo.
|
|
148
|
+
*/
|
|
149
|
+
const [brokenImages, setBrokenImages] = useState([]);
|
|
150
|
+
|
|
138
151
|
const [extraRetailers, setExtraRetailers] = useState([]);
|
|
139
152
|
const [newRetailer, setNewRetailer] = useState("");
|
|
140
153
|
const [newDepartment, setNewDepartment] = useState("");
|
|
@@ -277,8 +290,9 @@ export const LinkVariantsModal = ({
|
|
|
277
290
|
images: null,
|
|
278
291
|
});
|
|
279
292
|
setExtraRetailers([]);
|
|
293
|
+
setBrokenImages([]);
|
|
280
294
|
setPerChild({});
|
|
281
|
-
setApplyToAll(
|
|
295
|
+
setApplyToAll(false);
|
|
282
296
|
setActiveChild(mode === "children" ? selected[0] : null);
|
|
283
297
|
setStep("inherit");
|
|
284
298
|
} catch (err) {
|
|
@@ -315,20 +329,25 @@ export const LinkVariantsModal = ({
|
|
|
315
329
|
setLoadingFilters(true);
|
|
316
330
|
try {
|
|
317
331
|
const res = await axios.get(
|
|
318
|
-
`${filtersEndpoint}?retailerSelected=${
|
|
319
|
-
|
|
320
|
-
}`,
|
|
332
|
+
`${filtersEndpoint}?retailerSelected=${
|
|
333
|
+
retailer ?? ""
|
|
334
|
+
}&departmentSelected=${department ?? ""}`,
|
|
321
335
|
{ headers: { Authorization: token } }
|
|
322
336
|
);
|
|
323
337
|
|
|
324
|
-
|
|
325
|
-
|
|
338
|
+
// readPayload YA devuelve el cuerpo parseado. Volver a buscar `body`
|
|
339
|
+
// dentro dejaba la lista siempre vacía, y por eso el selector de cadenas
|
|
340
|
+
// no mostraba nada.
|
|
341
|
+
const lista = readPayload(res)?.data ?? [];
|
|
326
342
|
|
|
327
343
|
setFilters((current) => {
|
|
328
344
|
if (!retailer) {
|
|
329
345
|
return {
|
|
330
346
|
...current,
|
|
331
|
-
retailers: lista.map((item) => ({
|
|
347
|
+
retailers: lista.map((item) => ({
|
|
348
|
+
value: item.id,
|
|
349
|
+
label: item.name,
|
|
350
|
+
})),
|
|
332
351
|
};
|
|
333
352
|
}
|
|
334
353
|
if (!department) {
|
|
@@ -343,7 +362,10 @@ export const LinkVariantsModal = ({
|
|
|
343
362
|
}
|
|
344
363
|
return {
|
|
345
364
|
...current,
|
|
346
|
-
categories: lista.map((item) => ({
|
|
365
|
+
categories: lista.map((item) => ({
|
|
366
|
+
value: item.id,
|
|
367
|
+
label: item.name,
|
|
368
|
+
})),
|
|
347
369
|
};
|
|
348
370
|
});
|
|
349
371
|
} catch (error) {
|
|
@@ -412,6 +434,12 @@ export const LinkVariantsModal = ({
|
|
|
412
434
|
Las parejas cadena+categoría viajan como objetos, no como ids, porque pueden
|
|
413
435
|
ser cadenas que el padre no tiene: ahí no hay nada que heredar, se asigna.
|
|
414
436
|
*/
|
|
437
|
+
// Las que de verdad se pueden heredar: la base puede listar filas cuyo archivo
|
|
438
|
+
// ya no existe, y esas no cuentan ni para la lista ni para el "todas".
|
|
439
|
+
const imagenesUtiles = offered.images.filter(
|
|
440
|
+
(image) => !brokenImages.includes(image.id)
|
|
441
|
+
);
|
|
442
|
+
|
|
415
443
|
const buildInherit = (seleccion) => {
|
|
416
444
|
const parejas = [
|
|
417
445
|
...offered.categoryRetailers.map((item) => ({
|
|
@@ -431,7 +459,8 @@ export const LinkVariantsModal = ({
|
|
|
431
459
|
seleccion.modes.descriptions === "all"
|
|
432
460
|
? "all"
|
|
433
461
|
: seleccion.chosen.descriptions,
|
|
434
|
-
images:
|
|
462
|
+
images:
|
|
463
|
+
seleccion.modes.images === "all" ? "all" : seleccion.chosen.images,
|
|
435
464
|
categoryRetailers: parejas
|
|
436
465
|
.filter((par) => seleccion.chosen.categoryRetailers.includes(par.clave))
|
|
437
466
|
.map(({ retailerId, categoryId }) => ({ retailerId, categoryId })),
|
|
@@ -484,7 +513,9 @@ export const LinkVariantsModal = ({
|
|
|
484
513
|
!applyToAll && mode === "children"
|
|
485
514
|
? selected.reduce((acc, id) => {
|
|
486
515
|
const guardado =
|
|
487
|
-
id === activeChild
|
|
516
|
+
id === activeChild
|
|
517
|
+
? { modes, chosen, extraRetailers }
|
|
518
|
+
: perChild[id];
|
|
488
519
|
if (guardado) acc[id] = buildInherit(guardado);
|
|
489
520
|
return acc;
|
|
490
521
|
}, {})
|
|
@@ -521,11 +552,16 @@ export const LinkVariantsModal = ({
|
|
|
521
552
|
|
|
522
553
|
// Una dimensión está decidida si el usuario eligió un modo, o si el padre no
|
|
523
554
|
// ofrece nada en ella —no se puede pedir que decida sobre una lista vacía—.
|
|
524
|
-
const decidida = (dimension) =>
|
|
525
|
-
modes[dimension] !== null
|
|
526
|
-
(dimension === "categoryRetailers"
|
|
527
|
-
|
|
528
|
-
|
|
555
|
+
const decidida = (dimension) => {
|
|
556
|
+
if (modes[dimension] !== null) return true;
|
|
557
|
+
if (dimension === "categoryRetailers") {
|
|
558
|
+
return offered.categoryRetailers.length === 0 && extraRetailers.length === 0;
|
|
559
|
+
}
|
|
560
|
+
// Una imagen cuyo archivo no existe no es algo sobre lo que se pueda decidir.
|
|
561
|
+
return dimension === "images"
|
|
562
|
+
? imagenesUtiles.length === 0
|
|
563
|
+
: offered[dimension].length === 0;
|
|
564
|
+
};
|
|
529
565
|
|
|
530
566
|
const DIMENSIONES = [
|
|
531
567
|
"categoryRetailers",
|
|
@@ -544,9 +580,14 @@ export const LinkVariantsModal = ({
|
|
|
544
580
|
|
|
545
581
|
return DIMENSIONES.every((dimension) => {
|
|
546
582
|
if (guardado.modes[dimension] !== null) return true;
|
|
547
|
-
|
|
548
|
-
|
|
549
|
-
|
|
583
|
+
if (dimension === "categoryRetailers") {
|
|
584
|
+
return (
|
|
585
|
+
offered.categoryRetailers.length === 0 &&
|
|
586
|
+
(guardado.extraRetailers ?? []).length === 0
|
|
587
|
+
);
|
|
588
|
+
}
|
|
589
|
+
return dimension === "images"
|
|
590
|
+
? imagenesUtiles.length === 0
|
|
550
591
|
: offered[dimension].length === 0;
|
|
551
592
|
});
|
|
552
593
|
};
|
|
@@ -582,7 +623,12 @@ export const LinkVariantsModal = ({
|
|
|
582
623
|
{articleUpc ? ` · UPC ${articleUpc}` : ""}
|
|
583
624
|
</p>
|
|
584
625
|
</div>
|
|
585
|
-
<button
|
|
626
|
+
<button
|
|
627
|
+
type="button"
|
|
628
|
+
className="close"
|
|
629
|
+
onClick={onClose}
|
|
630
|
+
aria-label="Cerrar"
|
|
631
|
+
>
|
|
586
632
|
×
|
|
587
633
|
</button>
|
|
588
634
|
</Header>
|
|
@@ -654,7 +700,9 @@ export const LinkVariantsModal = ({
|
|
|
654
700
|
{candidate.name || `Producto ${candidate.articleId}`}
|
|
655
701
|
<span className="meta">
|
|
656
702
|
UPC {candidate.upc || "—"}
|
|
657
|
-
{candidate.categoryName
|
|
703
|
+
{candidate.categoryName
|
|
704
|
+
? ` · ${candidate.categoryName}`
|
|
705
|
+
: ""}
|
|
658
706
|
</span>
|
|
659
707
|
</span>
|
|
660
708
|
{candidate.variantsCount > 0 && (
|
|
@@ -677,7 +725,11 @@ export const LinkVariantsModal = ({
|
|
|
677
725
|
Mostrando {candidates.length} de {total}
|
|
678
726
|
</span>
|
|
679
727
|
{hasMore && (
|
|
680
|
-
<button
|
|
728
|
+
<button
|
|
729
|
+
type="button"
|
|
730
|
+
onClick={loadMore}
|
|
731
|
+
disabled={loadingMore}
|
|
732
|
+
>
|
|
681
733
|
{loadingMore ? "Cargando…" : "Cargar más"}
|
|
682
734
|
</button>
|
|
683
735
|
)}
|
|
@@ -748,6 +800,7 @@ export const LinkVariantsModal = ({
|
|
|
748
800
|
<label>
|
|
749
801
|
<input
|
|
750
802
|
type="radio"
|
|
803
|
+
name={`categoryRetailers-${activeChild ?? "todos"}`}
|
|
751
804
|
checked={modes.categoryRetailers === "all"}
|
|
752
805
|
onChange={() =>
|
|
753
806
|
chooseMode(
|
|
@@ -769,8 +822,11 @@ export const LinkVariantsModal = ({
|
|
|
769
822
|
<label>
|
|
770
823
|
<input
|
|
771
824
|
type="radio"
|
|
825
|
+
name={`categoryRetailers-${activeChild ?? "todos"}`}
|
|
772
826
|
checked={modes.categoryRetailers === "some"}
|
|
773
|
-
onChange={() =>
|
|
827
|
+
onChange={() =>
|
|
828
|
+
chooseMode("categoryRetailers", "some", [])
|
|
829
|
+
}
|
|
774
830
|
/>
|
|
775
831
|
<span>
|
|
776
832
|
Elegir cuáles
|
|
@@ -788,14 +844,19 @@ export const LinkVariantsModal = ({
|
|
|
788
844
|
clave: `${item.retailerId}-${item.categoryId}`,
|
|
789
845
|
delPadre: true,
|
|
790
846
|
})),
|
|
791
|
-
...extraRetailers.map((item) => ({
|
|
847
|
+
...extraRetailers.map((item) => ({
|
|
848
|
+
...item,
|
|
849
|
+
delPadre: false,
|
|
850
|
+
})),
|
|
792
851
|
].map((item) => (
|
|
793
852
|
<li key={item.clave}>
|
|
794
853
|
<label>
|
|
795
854
|
<input
|
|
796
855
|
type="checkbox"
|
|
797
856
|
disabled={modes.categoryRetailers === "all"}
|
|
798
|
-
checked={chosen.categoryRetailers.includes(
|
|
857
|
+
checked={chosen.categoryRetailers.includes(
|
|
858
|
+
item.clave
|
|
859
|
+
)}
|
|
799
860
|
onChange={() =>
|
|
800
861
|
toggleChoice("categoryRetailers", item.clave)
|
|
801
862
|
}
|
|
@@ -884,6 +945,7 @@ export const LinkVariantsModal = ({
|
|
|
884
945
|
<label>
|
|
885
946
|
<input
|
|
886
947
|
type="radio"
|
|
948
|
+
name={`descriptions-${activeChild ?? "todos"}`}
|
|
887
949
|
checked={modes.descriptions === "all"}
|
|
888
950
|
onChange={() =>
|
|
889
951
|
chooseMode(
|
|
@@ -904,8 +966,11 @@ export const LinkVariantsModal = ({
|
|
|
904
966
|
<label>
|
|
905
967
|
<input
|
|
906
968
|
type="radio"
|
|
969
|
+
name={`descriptions-${activeChild ?? "todos"}`}
|
|
907
970
|
checked={modes.descriptions === "some"}
|
|
908
|
-
onChange={() =>
|
|
971
|
+
onChange={() =>
|
|
972
|
+
chooseMode("descriptions", "some", [])
|
|
973
|
+
}
|
|
909
974
|
/>
|
|
910
975
|
<span>
|
|
911
976
|
Elegir cuáles
|
|
@@ -925,7 +990,9 @@ export const LinkVariantsModal = ({
|
|
|
925
990
|
type="checkbox"
|
|
926
991
|
disabled={modes.descriptions === "all"}
|
|
927
992
|
checked={chosen.descriptions.includes(item.id)}
|
|
928
|
-
onChange={() =>
|
|
993
|
+
onChange={() =>
|
|
994
|
+
toggleChoice("descriptions", item.id)
|
|
995
|
+
}
|
|
929
996
|
/>
|
|
930
997
|
<span>
|
|
931
998
|
{item.name}
|
|
@@ -961,6 +1028,7 @@ export const LinkVariantsModal = ({
|
|
|
961
1028
|
<label>
|
|
962
1029
|
<input
|
|
963
1030
|
type="radio"
|
|
1031
|
+
name={`attributes-${activeChild ?? "todos"}`}
|
|
964
1032
|
checked={modes.attributes === "all"}
|
|
965
1033
|
onChange={() =>
|
|
966
1034
|
chooseMode(
|
|
@@ -973,14 +1041,15 @@ export const LinkVariantsModal = ({
|
|
|
973
1041
|
<span>
|
|
974
1042
|
Todos los que tienen valor
|
|
975
1043
|
<span className="hint">
|
|
976
|
-
{offered.attributes.length} atributo(s) con
|
|
977
|
-
en el padre.
|
|
1044
|
+
{offered.attributes.length} atributo(s) con
|
|
1045
|
+
información en el padre.
|
|
978
1046
|
</span>
|
|
979
1047
|
</span>
|
|
980
1048
|
</label>
|
|
981
1049
|
<label>
|
|
982
1050
|
<input
|
|
983
1051
|
type="radio"
|
|
1052
|
+
name={`attributes-${activeChild ?? "todos"}`}
|
|
984
1053
|
checked={modes.attributes === "some"}
|
|
985
1054
|
onChange={() => chooseMode("attributes", "some", [])}
|
|
986
1055
|
/>
|
|
@@ -996,8 +1065,9 @@ export const LinkVariantsModal = ({
|
|
|
996
1065
|
|
|
997
1066
|
{activeRetailerId && !nameScope && (
|
|
998
1067
|
<Message>
|
|
999
|
-
El producto padre no comparte la cadena que tienes
|
|
1000
|
-
así que los nombres de abajo son los de otra de
|
|
1068
|
+
El producto padre no comparte la cadena que tienes
|
|
1069
|
+
abierta, así que los nombres de abajo son los de otra de
|
|
1070
|
+
sus cadenas.
|
|
1001
1071
|
</Message>
|
|
1002
1072
|
)}
|
|
1003
1073
|
|
|
@@ -1009,7 +1079,9 @@ export const LinkVariantsModal = ({
|
|
|
1009
1079
|
type="checkbox"
|
|
1010
1080
|
disabled={modes.attributes === "all"}
|
|
1011
1081
|
checked={chosen.attributes.includes(attribute.id)}
|
|
1012
|
-
onChange={() =>
|
|
1082
|
+
onChange={() =>
|
|
1083
|
+
toggleChoice("attributes", attribute.id)
|
|
1084
|
+
}
|
|
1013
1085
|
/>
|
|
1014
1086
|
<span>
|
|
1015
1087
|
{attribute.name}
|
|
@@ -1043,45 +1115,48 @@ export const LinkVariantsModal = ({
|
|
|
1043
1115
|
<h3>
|
|
1044
1116
|
<span className="paso">4</span> Imágenes
|
|
1045
1117
|
</h3>
|
|
1046
|
-
{
|
|
1118
|
+
{imagenesUtiles.length ? (
|
|
1047
1119
|
<>
|
|
1048
1120
|
<Choices>
|
|
1049
1121
|
<label>
|
|
1050
1122
|
<input
|
|
1051
1123
|
type="radio"
|
|
1124
|
+
name={`images-${activeChild ?? "todos"}`}
|
|
1052
1125
|
checked={modes.images === "all"}
|
|
1053
1126
|
onChange={() =>
|
|
1054
1127
|
chooseMode(
|
|
1055
1128
|
"images",
|
|
1056
1129
|
"all",
|
|
1057
|
-
|
|
1130
|
+
imagenesUtiles.map((item) => item.id)
|
|
1058
1131
|
)
|
|
1059
1132
|
}
|
|
1060
1133
|
/>
|
|
1061
1134
|
<span>
|
|
1062
1135
|
Todas
|
|
1063
1136
|
<span className="hint">
|
|
1064
|
-
{
|
|
1137
|
+
{imagenesUtiles.length} imagen(es) en el padre.
|
|
1065
1138
|
</span>
|
|
1066
1139
|
</span>
|
|
1067
1140
|
</label>
|
|
1068
1141
|
<label>
|
|
1069
1142
|
<input
|
|
1070
1143
|
type="radio"
|
|
1144
|
+
name={`images-${activeChild ?? "todos"}`}
|
|
1071
1145
|
checked={modes.images === "some"}
|
|
1072
1146
|
onChange={() => chooseMode("images", "some", [])}
|
|
1073
1147
|
/>
|
|
1074
1148
|
<span>
|
|
1075
1149
|
Elegir cuáles
|
|
1076
1150
|
<span className="hint">
|
|
1077
|
-
Útil cuando la variante cambia de color o de
|
|
1151
|
+
Útil cuando la variante cambia de color o de
|
|
1152
|
+
empaque.
|
|
1078
1153
|
</span>
|
|
1079
1154
|
</span>
|
|
1080
1155
|
</label>
|
|
1081
1156
|
</Choices>
|
|
1082
1157
|
|
|
1083
1158
|
<ImageGrid>
|
|
1084
|
-
{
|
|
1159
|
+
{imagenesUtiles.map((image) => (
|
|
1085
1160
|
<label
|
|
1086
1161
|
key={image.id}
|
|
1087
1162
|
className={[
|
|
@@ -1099,7 +1174,14 @@ export const LinkVariantsModal = ({
|
|
|
1099
1174
|
/>
|
|
1100
1175
|
<img
|
|
1101
1176
|
src={getProductImage(image.src)}
|
|
1102
|
-
alt=
|
|
1177
|
+
alt=""
|
|
1178
|
+
onError={() =>
|
|
1179
|
+
setBrokenImages((current) =>
|
|
1180
|
+
current.includes(image.id)
|
|
1181
|
+
? current
|
|
1182
|
+
: [...current, image.id]
|
|
1183
|
+
)
|
|
1184
|
+
}
|
|
1103
1185
|
/>
|
|
1104
1186
|
<span>{image.imageType || "Sin tipo de toma"}</span>
|
|
1105
1187
|
</label>
|
|
@@ -1131,7 +1213,9 @@ export const LinkVariantsModal = ({
|
|
|
1131
1213
|
: todoDecidido
|
|
1132
1214
|
? "Todo revisado. Puedes ligar."
|
|
1133
1215
|
: !applyToAll && mode === "children"
|
|
1134
|
-
? `Faltan ${
|
|
1216
|
+
? `Faltan ${
|
|
1217
|
+
selected.filter((id) => !decididoPara(id)).length
|
|
1218
|
+
} producto(s) por revisar.`
|
|
1135
1219
|
: `Falta decidir: ${faltantes.join(", ")}.`}
|
|
1136
1220
|
</span>
|
|
1137
1221
|
<div className="actions">
|
|
@@ -1145,7 +1229,12 @@ export const LinkVariantsModal = ({
|
|
|
1145
1229
|
Atrás
|
|
1146
1230
|
</button>
|
|
1147
1231
|
)}
|
|
1148
|
-
<button
|
|
1232
|
+
<button
|
|
1233
|
+
type="button"
|
|
1234
|
+
className="secondary"
|
|
1235
|
+
onClick={onClose}
|
|
1236
|
+
disabled={loading}
|
|
1237
|
+
>
|
|
1149
1238
|
Cancelar
|
|
1150
1239
|
</button>
|
|
1151
1240
|
{step === "search" ? (
|