contentoh-components-library 21.6.36 → 21.6.38
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/atoms/InheritanceTag/index.js +6 -2
- package/dist/components/molecules/TagAndInput/index.js +1 -1
- package/dist/components/molecules/TagAndInputv2/index.js +1 -1
- package/dist/components/organisms/LinkVariantsModal/index.js +29 -119
- package/dist/components/organisms/LinkVariantsModal/styles.js +1 -1
- package/dist/components/organisms/PropagateChangesModal/index.js +36 -3
- package/dist/components/pages/RetailerProductEdition/index.js +107 -29
- package/package.json +1 -1
- package/src/components/atoms/InheritanceTag/index.js +12 -2
- package/src/components/molecules/TagAndInput/index.js +3 -1
- package/src/components/molecules/TagAndInputv2/index.js +3 -1
- package/src/components/organisms/LinkVariantsModal/index.js +37 -117
- package/src/components/organisms/LinkVariantsModal/styles.js +0 -8
- package/src/components/organisms/PropagateChangesModal/index.js +43 -8
- package/src/components/pages/RetailerProductEdition/index.js +65 -9
|
@@ -16,6 +16,10 @@ var _jsxRuntime = require("react/jsx-runtime");
|
|
|
16
16
|
*
|
|
17
17
|
* El estado "propio" es el que carga información —ese campo se separó del padre—
|
|
18
18
|
* así que es el que lleva acento y el que ofrece la acción de volver a heredar.
|
|
19
|
+
*
|
|
20
|
+
* OJO: "propio" ya NO significa "a salvo de la propagación". Desde el cambio del
|
|
21
|
+
* 2026-09-01 la propagación sobrescribe también estos valores; el aviso de que va
|
|
22
|
+
* a pasar vive en el diálogo de propagación, no aquí. El tooltip lo dice así.
|
|
19
23
|
* "Heredado" es el caso normal y se mantiene discreto: si gritara en cada campo,
|
|
20
24
|
* la ficha entera se volvería ruido.
|
|
21
25
|
*
|
|
@@ -51,9 +55,9 @@ var InheritanceTag = function InheritanceTag(_ref) {
|
|
|
51
55
|
className: "inheritance-tag ".concat(state, " ").concat(className),
|
|
52
56
|
children: [/*#__PURE__*/(0, _jsxRuntime.jsx)("span", {
|
|
53
57
|
className: "tag-label",
|
|
54
|
-
title: isOverridden ? "Valor
|
|
58
|
+
title: isOverridden ? "Valor capturado en este producto hijo, distinto al del padre. Si el padre propaga este atributo, se reemplaza." : "Valor heredado".concat(parentUpc ? " del producto ".concat(parentUpc) : " del producto padre", "."),
|
|
55
59
|
children: isOverridden ? "Propio" : "Heredado"
|
|
56
|
-
}), isOverridden && onRestore && /*#__PURE__*/(0, _jsxRuntime.jsx)("button", {
|
|
60
|
+
}), Boolean(isOverridden) && onRestore && /*#__PURE__*/(0, _jsxRuntime.jsx)("button", {
|
|
57
61
|
type: "button",
|
|
58
62
|
className: "tag-action",
|
|
59
63
|
onClick: onRestore,
|
|
@@ -420,7 +420,7 @@ var TagAndInput = function TagAndInput(_ref) {
|
|
|
420
420
|
return /*#__PURE__*/(0, _jsxRuntime.jsxs)(_styles.Container, {
|
|
421
421
|
inputType: inputType,
|
|
422
422
|
className: "input-container",
|
|
423
|
-
children: [(label === null || label === void 0 ? void 0 : label.length) && /*#__PURE__*/(0, _jsxRuntime.jsxs)("div", {
|
|
423
|
+
children: [Boolean(label === null || label === void 0 ? void 0 : label.length) && /*#__PURE__*/(0, _jsxRuntime.jsxs)("div", {
|
|
424
424
|
className: "title-container",
|
|
425
425
|
children: [/*#__PURE__*/(0, _jsxRuntime.jsxs)("div", {
|
|
426
426
|
className: "titles",
|
|
@@ -54,7 +54,7 @@ var TagAndInputv2 = function TagAndInputv2(_ref) {
|
|
|
54
54
|
return /*#__PURE__*/(0, _jsxRuntime.jsxs)(_styles.Container, {
|
|
55
55
|
inputType: inputType,
|
|
56
56
|
className: "input-container",
|
|
57
|
-
children: [(label === null || label === void 0 ? void 0 : label.length) && /*#__PURE__*/(0, _jsxRuntime.jsxs)("div", {
|
|
57
|
+
children: [Boolean(label === null || label === void 0 ? void 0 : label.length) && /*#__PURE__*/(0, _jsxRuntime.jsxs)("div", {
|
|
58
58
|
className: "title-container",
|
|
59
59
|
children: [/*#__PURE__*/(0, _jsxRuntime.jsx)(_ScreenHeaderv.ScreenHeaderv2, {
|
|
60
60
|
text: label,
|
|
@@ -150,8 +150,7 @@ var LinkVariantsModal = function LinkVariantsModal(_ref) {
|
|
|
150
150
|
var _useState21 = (0, _react.useState)({
|
|
151
151
|
attributes: [],
|
|
152
152
|
descriptions: [],
|
|
153
|
-
images: []
|
|
154
|
-
categoryRetailers: []
|
|
153
|
+
images: []
|
|
155
154
|
}),
|
|
156
155
|
_useState22 = (0, _slicedToArray2.default)(_useState21, 2),
|
|
157
156
|
offered = _useState22[0],
|
|
@@ -160,8 +159,7 @@ var LinkVariantsModal = function LinkVariantsModal(_ref) {
|
|
|
160
159
|
var _useState23 = (0, _react.useState)({
|
|
161
160
|
attributes: [],
|
|
162
161
|
descriptions: [],
|
|
163
|
-
images: []
|
|
164
|
-
categoryRetailers: []
|
|
162
|
+
images: []
|
|
165
163
|
}),
|
|
166
164
|
_useState24 = (0, _slicedToArray2.default)(_useState23, 2),
|
|
167
165
|
chosen = _useState24[0],
|
|
@@ -177,7 +175,6 @@ var LinkVariantsModal = function LinkVariantsModal(_ref) {
|
|
|
177
175
|
|
|
178
176
|
|
|
179
177
|
var _useState27 = (0, _react.useState)({
|
|
180
|
-
categoryRetailers: null,
|
|
181
178
|
descriptions: null,
|
|
182
179
|
attributes: null,
|
|
183
180
|
images: null
|
|
@@ -451,19 +448,16 @@ var LinkVariantsModal = function LinkVariantsModal(_ref) {
|
|
|
451
448
|
listas = {
|
|
452
449
|
attributes: data.attributes || [],
|
|
453
450
|
descriptions: data.descriptions || [],
|
|
454
|
-
images: data.images || []
|
|
455
|
-
categoryRetailers: data.categoryRetailers || []
|
|
451
|
+
images: data.images || []
|
|
456
452
|
};
|
|
457
453
|
setOffered(listas);
|
|
458
454
|
setNameScope((_data$nameScope = data.nameScope) !== null && _data$nameScope !== void 0 ? _data$nameScope : null);
|
|
459
455
|
setChosen({
|
|
460
456
|
attributes: [],
|
|
461
457
|
descriptions: [],
|
|
462
|
-
images: []
|
|
463
|
-
categoryRetailers: []
|
|
458
|
+
images: []
|
|
464
459
|
});
|
|
465
460
|
setModes({
|
|
466
|
-
categoryRetailers: null,
|
|
467
461
|
descriptions: null,
|
|
468
462
|
attributes: null,
|
|
469
463
|
images: null
|
|
@@ -544,34 +538,28 @@ var LinkVariantsModal = function LinkVariantsModal(_ref) {
|
|
|
544
538
|
});
|
|
545
539
|
|
|
546
540
|
var buildInherit = function buildInherit(seleccion) {
|
|
547
|
-
var parejas = offered.categoryRetailers.map(function (item) {
|
|
548
|
-
return {
|
|
549
|
-
clave: "".concat(item.retailerId, "-").concat(item.categoryId),
|
|
550
|
-
retailerId: item.retailerId,
|
|
551
|
-
categoryId: item.categoryId
|
|
552
|
-
};
|
|
553
|
-
});
|
|
554
541
|
return {
|
|
555
542
|
attributes: seleccion.modes.attributes === "all" ? "all-filled" : seleccion.chosen.attributes,
|
|
556
543
|
descriptions: seleccion.modes.descriptions === "all" ? "all" : seleccion.chosen.descriptions,
|
|
557
544
|
images: seleccion.modes.images === "all" ? "all" : seleccion.chosen.images,
|
|
558
|
-
|
|
559
|
-
|
|
560
|
-
|
|
561
|
-
|
|
562
|
-
|
|
563
|
-
|
|
564
|
-
|
|
565
|
-
|
|
566
|
-
|
|
567
|
-
|
|
545
|
+
|
|
546
|
+
/*
|
|
547
|
+
Siempre "all", nunca una lista.
|
|
548
|
+
El hijo se queda con las cadenas del padre, sin preguntar. No es una
|
|
549
|
+
preferencia: es lo que hace que la copia de atributos SE VEA. La ficha
|
|
550
|
+
arma su lista desde `attribute_manager` acotado por las cadenas del
|
|
551
|
+
propio producto, así que un valor copiado a un atributo que la cadena del
|
|
552
|
+
hijo no configura queda escrito y invisible.
|
|
553
|
+
Se manda la palabra y no la lista para que signifique "todas" también si
|
|
554
|
+
alguien agregó una cadena al padre entre abrir el modal y guardar.
|
|
555
|
+
*/
|
|
556
|
+
categoryRetailers: "all"
|
|
568
557
|
};
|
|
569
558
|
};
|
|
570
559
|
|
|
571
560
|
var seleccionVacia = function seleccionVacia() {
|
|
572
561
|
return {
|
|
573
562
|
modes: {
|
|
574
|
-
categoryRetailers: null,
|
|
575
563
|
descriptions: null,
|
|
576
564
|
attributes: null,
|
|
577
565
|
images: null
|
|
@@ -579,8 +567,7 @@ var LinkVariantsModal = function LinkVariantsModal(_ref) {
|
|
|
579
567
|
chosen: {
|
|
580
568
|
attributes: [],
|
|
581
569
|
descriptions: [],
|
|
582
|
-
images: []
|
|
583
|
-
categoryRetailers: []
|
|
570
|
+
images: []
|
|
584
571
|
}
|
|
585
572
|
};
|
|
586
573
|
};
|
|
@@ -606,7 +593,7 @@ var LinkVariantsModal = function LinkVariantsModal(_ref) {
|
|
|
606
593
|
};
|
|
607
594
|
|
|
608
595
|
var submit = /*#__PURE__*/function () {
|
|
609
|
-
var
|
|
596
|
+
var _ref6 = (0, _asyncToGenerator2.default)( /*#__PURE__*/_regenerator.default.mark(function _callee5() {
|
|
610
597
|
var inherit, inheritByChild, body, data;
|
|
611
598
|
return _regenerator.default.wrap(function _callee5$(_context5) {
|
|
612
599
|
while (1) {
|
|
@@ -678,24 +665,19 @@ var LinkVariantsModal = function LinkVariantsModal(_ref) {
|
|
|
678
665
|
}));
|
|
679
666
|
|
|
680
667
|
return function submit() {
|
|
681
|
-
return
|
|
668
|
+
return _ref6.apply(this, arguments);
|
|
682
669
|
};
|
|
683
670
|
}(); // Una dimensión está decidida si el usuario eligió un modo, o si el padre no
|
|
684
671
|
// ofrece nada en ella —no se puede pedir que decida sobre una lista vacía—.
|
|
685
672
|
|
|
686
673
|
|
|
687
674
|
var decidida = function decidida(dimension) {
|
|
688
|
-
if (modes[dimension] !== null) return true;
|
|
689
|
-
|
|
690
|
-
if (dimension === "categoryRetailers") {
|
|
691
|
-
return offered.categoryRetailers.length === 0;
|
|
692
|
-
} // Una imagen cuyo archivo no existe no es algo sobre lo que se pueda decidir.
|
|
693
|
-
|
|
675
|
+
if (modes[dimension] !== null) return true; // Una imagen cuyo archivo no existe no es algo sobre lo que se pueda decidir.
|
|
694
676
|
|
|
695
677
|
return dimension === "images" ? imagenesUtiles.length === 0 : offered[dimension].length === 0;
|
|
696
678
|
};
|
|
697
679
|
|
|
698
|
-
var DIMENSIONES = ["
|
|
680
|
+
var DIMENSIONES = ["descriptions", "attributes", "images"]; // Con pestañas, que la que estoy viendo esté completa no basta: ligar escribe
|
|
699
681
|
// en todos, así que todos tienen que estar decididos. Si no, un producto se
|
|
700
682
|
// ligaría sin heredar nada solo porque nadie abrió su pestaña.
|
|
701
683
|
|
|
@@ -707,24 +689,19 @@ var LinkVariantsModal = function LinkVariantsModal(_ref) {
|
|
|
707
689
|
if (!guardado) return false;
|
|
708
690
|
return DIMENSIONES.every(function (dimension) {
|
|
709
691
|
if (guardado.modes[dimension] !== null) return true;
|
|
710
|
-
|
|
711
|
-
if (dimension === "categoryRetailers") {
|
|
712
|
-
return offered.categoryRetailers.length === 0;
|
|
713
|
-
}
|
|
714
|
-
|
|
715
692
|
return dimension === "images" ? imagenesUtiles.length === 0 : offered[dimension].length === 0;
|
|
716
693
|
});
|
|
717
694
|
};
|
|
718
695
|
|
|
719
696
|
var todoDecidido = step === "inherit" && (mode === "parent" ? pushToParent !== null : applyToAll ? DIMENSIONES.every(decidida) : selected.every(decididoPara));
|
|
720
|
-
var faltantes = [["
|
|
721
|
-
var
|
|
722
|
-
clave =
|
|
697
|
+
var faltantes = [["descriptions", "descripciones"], ["attributes", "atributos"], ["images", "imágenes"]].filter(function (_ref7) {
|
|
698
|
+
var _ref8 = (0, _slicedToArray2.default)(_ref7, 1),
|
|
699
|
+
clave = _ref8[0];
|
|
723
700
|
|
|
724
701
|
return !decidida(clave);
|
|
725
|
-
}).map(function (
|
|
726
|
-
var
|
|
727
|
-
nombre =
|
|
702
|
+
}).map(function (_ref9) {
|
|
703
|
+
var _ref10 = (0, _slicedToArray2.default)(_ref9, 2),
|
|
704
|
+
nombre = _ref10[1];
|
|
728
705
|
|
|
729
706
|
return nombre;
|
|
730
707
|
});
|
|
@@ -1003,73 +980,6 @@ var LinkVariantsModal = function LinkVariantsModal(_ref) {
|
|
|
1003
980
|
children: [/*#__PURE__*/(0, _jsxRuntime.jsx)("span", {
|
|
1004
981
|
className: "paso",
|
|
1005
982
|
children: "1"
|
|
1006
|
-
}), " Cadenas y categor\xEDas"]
|
|
1007
|
-
}), /*#__PURE__*/(0, _jsxRuntime.jsxs)(_styles.Choices, {
|
|
1008
|
-
children: [/*#__PURE__*/(0, _jsxRuntime.jsxs)("label", {
|
|
1009
|
-
children: [/*#__PURE__*/(0, _jsxRuntime.jsx)("input", {
|
|
1010
|
-
type: "radio",
|
|
1011
|
-
name: "categoryRetailers-".concat(activeChild !== null && activeChild !== void 0 ? activeChild : "todos"),
|
|
1012
|
-
checked: modes.categoryRetailers === "all",
|
|
1013
|
-
onChange: function onChange() {
|
|
1014
|
-
return chooseMode("categoryRetailers", "all", offered.categoryRetailers.map(function (item) {
|
|
1015
|
-
return "".concat(item.retailerId, "-").concat(item.categoryId);
|
|
1016
|
-
}));
|
|
1017
|
-
}
|
|
1018
|
-
}), /*#__PURE__*/(0, _jsxRuntime.jsxs)("span", {
|
|
1019
|
-
children: ["Todas las del producto padre", /*#__PURE__*/(0, _jsxRuntime.jsxs)("span", {
|
|
1020
|
-
className: "hint",
|
|
1021
|
-
children: [offered.categoryRetailers.length, " cadena(s)."]
|
|
1022
|
-
})]
|
|
1023
|
-
})]
|
|
1024
|
-
}), /*#__PURE__*/(0, _jsxRuntime.jsxs)("label", {
|
|
1025
|
-
children: [/*#__PURE__*/(0, _jsxRuntime.jsx)("input", {
|
|
1026
|
-
type: "radio",
|
|
1027
|
-
name: "categoryRetailers-".concat(activeChild !== null && activeChild !== void 0 ? activeChild : "todos"),
|
|
1028
|
-
checked: modes.categoryRetailers === "some",
|
|
1029
|
-
onChange: function onChange() {
|
|
1030
|
-
return chooseMode("categoryRetailers", "some", []);
|
|
1031
|
-
}
|
|
1032
|
-
}), /*#__PURE__*/(0, _jsxRuntime.jsxs)("span", {
|
|
1033
|
-
children: ["Elegir cu\xE1les", /*#__PURE__*/(0, _jsxRuntime.jsx)("span", {
|
|
1034
|
-
className: "hint",
|
|
1035
|
-
children: "Solo se puede heredar de las que ya tiene el padre."
|
|
1036
|
-
})]
|
|
1037
|
-
})]
|
|
1038
|
-
})]
|
|
1039
|
-
}), /*#__PURE__*/(0, _jsxRuntime.jsx)(_styles.List, {
|
|
1040
|
-
children: offered.categoryRetailers.map(function (item) {
|
|
1041
|
-
return (0, _objectSpread7.default)((0, _objectSpread7.default)({}, item), {}, {
|
|
1042
|
-
clave: "".concat(item.retailerId, "-").concat(item.categoryId)
|
|
1043
|
-
});
|
|
1044
|
-
}).map(function (item) {
|
|
1045
|
-
return /*#__PURE__*/(0, _jsxRuntime.jsx)("li", {
|
|
1046
|
-
children: /*#__PURE__*/(0, _jsxRuntime.jsxs)("label", {
|
|
1047
|
-
children: [/*#__PURE__*/(0, _jsxRuntime.jsx)("input", {
|
|
1048
|
-
type: "checkbox",
|
|
1049
|
-
disabled: modes.categoryRetailers === "all",
|
|
1050
|
-
checked: chosen.categoryRetailers.includes(item.clave),
|
|
1051
|
-
onChange: function onChange() {
|
|
1052
|
-
return toggleChoice("categoryRetailers", item.clave);
|
|
1053
|
-
}
|
|
1054
|
-
}), /*#__PURE__*/(0, _jsxRuntime.jsx)("img", {
|
|
1055
|
-
className: "logo",
|
|
1056
|
-
src: (0, _helper.getRetailerPicture)(item.retailerId, 24, 24),
|
|
1057
|
-
alt: ""
|
|
1058
|
-
}), /*#__PURE__*/(0, _jsxRuntime.jsxs)("span", {
|
|
1059
|
-
children: [item.retailerName, /*#__PURE__*/(0, _jsxRuntime.jsx)("span", {
|
|
1060
|
-
className: "meta",
|
|
1061
|
-
children: item.categoryName || "Sin categoría"
|
|
1062
|
-
})]
|
|
1063
|
-
})]
|
|
1064
|
-
})
|
|
1065
|
-
}, item.clave);
|
|
1066
|
-
})
|
|
1067
|
-
})]
|
|
1068
|
-
}), /*#__PURE__*/(0, _jsxRuntime.jsxs)(_styles.Block, {
|
|
1069
|
-
children: [/*#__PURE__*/(0, _jsxRuntime.jsxs)("h3", {
|
|
1070
|
-
children: [/*#__PURE__*/(0, _jsxRuntime.jsx)("span", {
|
|
1071
|
-
className: "paso",
|
|
1072
|
-
children: "2"
|
|
1073
983
|
}), " Descripciones"]
|
|
1074
984
|
}), offered.descriptions.length ? /*#__PURE__*/(0, _jsxRuntime.jsxs)(_jsxRuntime.Fragment, {
|
|
1075
985
|
children: [/*#__PURE__*/(0, _jsxRuntime.jsxs)(_styles.Choices, {
|
|
@@ -1136,7 +1046,7 @@ var LinkVariantsModal = function LinkVariantsModal(_ref) {
|
|
|
1136
1046
|
children: [/*#__PURE__*/(0, _jsxRuntime.jsxs)("h3", {
|
|
1137
1047
|
children: [/*#__PURE__*/(0, _jsxRuntime.jsx)("span", {
|
|
1138
1048
|
className: "paso",
|
|
1139
|
-
children: "
|
|
1049
|
+
children: "2"
|
|
1140
1050
|
}), " Atributos de la ficha t\xE9cnica"]
|
|
1141
1051
|
}), offered.attributes.length ? /*#__PURE__*/(0, _jsxRuntime.jsxs)(_jsxRuntime.Fragment, {
|
|
1142
1052
|
children: [/*#__PURE__*/(0, _jsxRuntime.jsxs)(_styles.Choices, {
|
|
@@ -1208,7 +1118,7 @@ var LinkVariantsModal = function LinkVariantsModal(_ref) {
|
|
|
1208
1118
|
children: [/*#__PURE__*/(0, _jsxRuntime.jsxs)("h3", {
|
|
1209
1119
|
children: [/*#__PURE__*/(0, _jsxRuntime.jsx)("span", {
|
|
1210
1120
|
className: "paso",
|
|
1211
|
-
children: "
|
|
1121
|
+
children: "3"
|
|
1212
1122
|
}), " Im\xE1genes"]
|
|
1213
1123
|
}), /*#__PURE__*/(0, _jsxRuntime.jsxs)(_styles.Choices, {
|
|
1214
1124
|
children: [/*#__PURE__*/(0, _jsxRuntime.jsxs)("label", {
|
|
@@ -51,7 +51,7 @@ var SearchRow = _styledComponents.default.form(_templateObject6 || (_templateObj
|
|
|
51
51
|
|
|
52
52
|
exports.SearchRow = SearchRow;
|
|
53
53
|
|
|
54
|
-
var List = _styledComponents.default.ul(_templateObject7 || (_templateObject7 = (0, _taggedTemplateLiteral2.default)(["\n list-style: none;\n margin: 0 0 16px;\n padding: 0;\n border: 1px solid ", ";\n border-radius: 6px;\n\n li + li {\n border-top: 1px solid ", ";\n }\n\n label {\n display: flex;\n align-items: center;\n gap: 10px;\n padding: 10px 12px;\n cursor: pointer;\n font-size: 13px;\n color: ", ";\n }\n\n .meta {\n display: block;\n font-size: 12px;\n color: ", ";\n }\n\n
|
|
54
|
+
var List = _styledComponents.default.ul(_templateObject7 || (_templateObject7 = (0, _taggedTemplateLiteral2.default)(["\n list-style: none;\n margin: 0 0 16px;\n padding: 0;\n border: 1px solid ", ";\n border-radius: 6px;\n\n li + li {\n border-top: 1px solid ", ";\n }\n\n label {\n display: flex;\n align-items: center;\n gap: 10px;\n padding: 10px 12px;\n cursor: pointer;\n font-size: 13px;\n color: ", ";\n }\n\n .meta {\n display: block;\n font-size: 12px;\n color: ", ";\n }\n\n li {\n display: flex;\n align-items: center;\n }\n\n li > label {\n flex: 1;\n min-width: 0;\n }\n\n .count {\n margin-left: auto;\n font-size: 12px;\n color: ", ";\n white-space: nowrap;\n }\n"])), _variables.GlobalColors.s2, _variables.GlobalColors.s2, _variables.GlobalColors.deep_gray, _variables.GlobalColors.gray, PINK);
|
|
55
55
|
|
|
56
56
|
exports.List = List;
|
|
57
57
|
var AttributeList = (0, _styledComponents.default)(List)(_templateObject8 || (_templateObject8 = (0, _taggedTemplateLiteral2.default)(["\n max-height: 220px;\n overflow-y: auto;\n\n .note {\n display: block;\n font-size: 11px;\n font-style: italic;\n color: ", ";\n }\n\n .value {\n margin-left: auto;\n max-width: 45%;\n font-size: 12px;\n color: ", ";\n overflow: hidden;\n text-overflow: ellipsis;\n white-space: nowrap;\n }\n"])), _variables.GlobalColors.gray, _variables.GlobalColors.gray);
|
|
@@ -9,6 +9,8 @@ exports.PropagateChangesModal = void 0;
|
|
|
9
9
|
|
|
10
10
|
var _toConsumableArray2 = _interopRequireDefault(require("@babel/runtime/helpers/esm/toConsumableArray"));
|
|
11
11
|
|
|
12
|
+
var _objectSpread2 = _interopRequireDefault(require("@babel/runtime/helpers/esm/objectSpread2"));
|
|
13
|
+
|
|
12
14
|
var _slicedToArray2 = _interopRequireDefault(require("@babel/runtime/helpers/esm/slicedToArray"));
|
|
13
15
|
|
|
14
16
|
var _react = require("react");
|
|
@@ -27,7 +29,10 @@ var _jsxRuntime = require("react/jsx-runtime");
|
|
|
27
29
|
|
|
28
30
|
props (* requerido):
|
|
29
31
|
- *changedAttributes: [{ id, name, value }] lo que se acaba de editar
|
|
30
|
-
- *variants: [{ articleId, name, upc }] los hijos
|
|
32
|
+
- *variants: [{ articleId, name, upc, overriddenAttributes }] los hijos
|
|
33
|
+
activos del padre. `overriddenAttributes` son los atributos que ese hijo
|
|
34
|
+
capturó a mano; la propagación los sobrescribe, y este diálogo es el
|
|
35
|
+
único lugar donde el usuario puede enterarse antes de que pase.
|
|
31
36
|
- *onCancel: cerrar sin guardar nada
|
|
32
37
|
- *onConfirm: (propagation | null) => void
|
|
33
38
|
null → guardar solo el padre
|
|
@@ -67,6 +72,31 @@ var PropagateChangesModal = function PropagateChangesModal(_ref) {
|
|
|
67
72
|
var destinatarios = scope === "all" ? variants.length : chosenChildren.length;
|
|
68
73
|
var puedePropagar = chosenAttributes.length > 0 && destinatarios > 0;
|
|
69
74
|
var enSegundoPlano = destinatarios > UMBRAL_SEGUNDO_PLANO;
|
|
75
|
+
/*
|
|
76
|
+
A quién se le va a pisar un valor capturado a mano.
|
|
77
|
+
La propagación sobrescribe: eso se decidió a propósito, porque el usuario ya
|
|
78
|
+
eligió atributo por atributo y hijo por hijo. Pero sobrescribir en silencio
|
|
79
|
+
es otra cosa, y desde que se quitaron las etiquetas de «Heredado» y «Propio»
|
|
80
|
+
este diálogo es el único momento en que alguien puede saberlo.
|
|
81
|
+
Se recalcula con lo que está marcado ahora, no al abrir: si el usuario
|
|
82
|
+
desmarca el atributo conflictivo, el aviso desaparece — que es justo la
|
|
83
|
+
salida que el aviso debería estar ofreciendo.
|
|
84
|
+
*/
|
|
85
|
+
|
|
86
|
+
var destinos = scope === "all" ? variants : variants.filter(function (item) {
|
|
87
|
+
return chosenChildren.includes(item.articleId);
|
|
88
|
+
});
|
|
89
|
+
var conValorPropio = destinos.map(function (hijo) {
|
|
90
|
+
var _hijo$overriddenAttri;
|
|
91
|
+
|
|
92
|
+
return (0, _objectSpread2.default)((0, _objectSpread2.default)({}, hijo), {}, {
|
|
93
|
+
pisados: ((_hijo$overriddenAttri = hijo.overriddenAttributes) !== null && _hijo$overriddenAttri !== void 0 ? _hijo$overriddenAttri : []).filter(function (id) {
|
|
94
|
+
return chosenAttributes.includes(Number(id));
|
|
95
|
+
})
|
|
96
|
+
});
|
|
97
|
+
}).filter(function (hijo) {
|
|
98
|
+
return hijo.pisados.length > 0;
|
|
99
|
+
});
|
|
70
100
|
|
|
71
101
|
var toggleAttribute = function toggleAttribute(id) {
|
|
72
102
|
setChosenAttributes(function (current) {
|
|
@@ -173,8 +203,11 @@ var PropagateChangesModal = function PropagateChangesModal(_ref) {
|
|
|
173
203
|
})
|
|
174
204
|
}, variant.articleId);
|
|
175
205
|
})
|
|
176
|
-
}), /*#__PURE__*/(0, _jsxRuntime.
|
|
177
|
-
|
|
206
|
+
}), conValorPropio.length > 0 && /*#__PURE__*/(0, _jsxRuntime.jsxs)(_styles.Note, {
|
|
207
|
+
className: "accent",
|
|
208
|
+
children: [conValorPropio.length === 1 ? "Un hijo tiene un valor propio en lo que elegiste y se va a reemplazar: " : "".concat(conValorPropio.length, " hijos tienen valores propios en lo que elegiste y se van a reemplazar: "), conValorPropio.map(function (hijo) {
|
|
209
|
+
return "".concat(hijo.name || "Producto ".concat(hijo.articleId), " (").concat(hijo.pisados.length, ")");
|
|
210
|
+
}).join(", "), ". Si no quieres pisarlos, desmarca ese atributo o ese hijo."]
|
|
178
211
|
}), enSegundoPlano && /*#__PURE__*/(0, _jsxRuntime.jsxs)(_styles.Note, {
|
|
179
212
|
className: "accent",
|
|
180
213
|
children: ["Son m\xE1s de ", UMBRAL_SEGUNDO_PLANO, " hijos, as\xED que la propagaci\xF3n corre en segundo plano: el guardado termina antes de que todas est\xE9n actualizadas."]
|
|
@@ -1597,7 +1597,8 @@ var RetailerProductEditionView = function RetailerProductEditionView(_ref) {
|
|
|
1597
1597
|
|
|
1598
1598
|
var handleOnClickSave = /*#__PURE__*/function () {
|
|
1599
1599
|
var _ref12 = (0, _asyncToGenerator2.default)( /*#__PURE__*/_regenerator.default.mark(function _callee8() {
|
|
1600
|
-
var
|
|
1600
|
+
var _familia$variants, cambios, familia, hijos;
|
|
1601
|
+
|
|
1601
1602
|
return _regenerator.default.wrap(function _callee8$(_context8) {
|
|
1602
1603
|
while (1) {
|
|
1603
1604
|
switch (_context8.prev = _context8.next) {
|
|
@@ -1611,7 +1612,7 @@ var RetailerProductEditionView = function RetailerProductEditionView(_ref) {
|
|
|
1611
1612
|
|
|
1612
1613
|
case 2:
|
|
1613
1614
|
_context8.t0 = state.active_tab;
|
|
1614
|
-
_context8.next = _context8.t0 === "Descripción" ? 5 : _context8.t0 === "Ficha técnica" ? 8 : _context8.t0 === "Imágenes" ?
|
|
1615
|
+
_context8.next = _context8.t0 === "Descripción" ? 5 : _context8.t0 === "Ficha técnica" ? 8 : _context8.t0 === "Imágenes" ? 32 : 35;
|
|
1615
1616
|
break;
|
|
1616
1617
|
|
|
1617
1618
|
case 5:
|
|
@@ -1619,57 +1620,134 @@ var RetailerProductEditionView = function RetailerProductEditionView(_ref) {
|
|
|
1619
1620
|
return saveDescriptions(token);
|
|
1620
1621
|
|
|
1621
1622
|
case 7:
|
|
1622
|
-
return _context8.abrupt("break",
|
|
1623
|
+
return _context8.abrupt("break", 36);
|
|
1623
1624
|
|
|
1624
1625
|
case 8:
|
|
1625
|
-
// Guardar un padre con hijos abre la decisión de propagar. Sin este
|
|
1626
|
-
// paso el cambio se queda solo en el padre y los hijos se van
|
|
1627
|
-
// separando de él sin que nadie lo note.
|
|
1628
|
-
esPadre = hijosDelProducto > 0;
|
|
1629
1626
|
cambios = cambiosDeFichaTecnica();
|
|
1630
1627
|
|
|
1631
|
-
if (!(
|
|
1632
|
-
_context8.next =
|
|
1628
|
+
if (!(cambios.length === 0)) {
|
|
1629
|
+
_context8.next = 13;
|
|
1633
1630
|
break;
|
|
1634
1631
|
}
|
|
1635
1632
|
|
|
1633
|
+
_context8.next = 12;
|
|
1634
|
+
return saveDatasheets(token);
|
|
1635
|
+
|
|
1636
|
+
case 12:
|
|
1637
|
+
return _context8.abrupt("break", 36);
|
|
1638
|
+
|
|
1639
|
+
case 13:
|
|
1640
|
+
/*
|
|
1641
|
+
Guardar un padre con hijos abre la decisión de propagar. Sin este paso
|
|
1642
|
+
el cambio se queda solo en el padre y los hijos se van separando de él
|
|
1643
|
+
sin que nadie lo note.
|
|
1644
|
+
Si tiene hijos se resuelve AQUÍ, esperando la respuesta, en vez de
|
|
1645
|
+
mirar la copia en memoria. `family` es una consulta asíncrona: si
|
|
1646
|
+
todavía no llegó, o si falló, `hijosDelProducto` vale 0 — o sea que
|
|
1647
|
+
«todavía no sé» se leía igual que «no tiene hijos», y el padre se
|
|
1648
|
+
guardaba sin ofrecer la propagación. En silencio, que es lo peor: el
|
|
1649
|
+
usuario creía haber bajado el cambio a los hijos.
|
|
1650
|
+
La ficha de la aplicación de clientes no tiene el problema porque ahí
|
|
1651
|
+
`state.product.variantsCount` viene del listado; aquí el producto sale
|
|
1652
|
+
del endpoint de reportes, que no trae el parentesco.
|
|
1653
|
+
*/
|
|
1636
1654
|
setPreparandoPropagacion(true);
|
|
1637
|
-
|
|
1638
|
-
|
|
1639
|
-
|
|
1640
|
-
|
|
1641
|
-
|
|
1642
|
-
|
|
1655
|
+
familia = null;
|
|
1656
|
+
_context8.prev = 15;
|
|
1657
|
+
_context8.next = 18;
|
|
1658
|
+
return loadFamily(true);
|
|
1659
|
+
|
|
1660
|
+
case 18:
|
|
1661
|
+
familia = _context8.sent;
|
|
1662
|
+
|
|
1663
|
+
case 19:
|
|
1664
|
+
_context8.prev = 19;
|
|
1665
|
+
setPreparandoPropagacion(false);
|
|
1666
|
+
return _context8.finish(19);
|
|
1667
|
+
|
|
1668
|
+
case 22:
|
|
1669
|
+
if (!(!familia || familia.error)) {
|
|
1670
|
+
_context8.next = 25;
|
|
1671
|
+
break;
|
|
1672
|
+
}
|
|
1673
|
+
|
|
1674
|
+
dispatch({
|
|
1675
|
+
type: "SET_MODAL",
|
|
1676
|
+
payload: {
|
|
1677
|
+
show: true,
|
|
1678
|
+
title: "No se pudo consultar la familia",
|
|
1679
|
+
message: "No pudimos saber si este producto tiene hijos, así que no sabemos si hay que bajarles el cambio. Vuelve a intentar el guardado; si sigue fallando, guarda y revisa los hijos a mano.",
|
|
1680
|
+
image: _genericModalWarning.default,
|
|
1681
|
+
buttons: [{
|
|
1682
|
+
text: "Entendido",
|
|
1683
|
+
buttonType: "general-white-button",
|
|
1684
|
+
action: function action() {
|
|
1685
|
+
return dispatch({
|
|
1686
|
+
type: "SET_MODAL",
|
|
1687
|
+
payload: {
|
|
1688
|
+
show: false
|
|
1689
|
+
}
|
|
1690
|
+
});
|
|
1691
|
+
}
|
|
1692
|
+
}, {
|
|
1693
|
+
text: "Guardar solo este producto",
|
|
1694
|
+
buttonType: "general-button-default",
|
|
1695
|
+
action: function action() {
|
|
1696
|
+
dispatch({
|
|
1697
|
+
type: "SET_MODAL",
|
|
1698
|
+
payload: {
|
|
1699
|
+
show: false
|
|
1700
|
+
}
|
|
1701
|
+
});
|
|
1702
|
+
saveDatasheets(token);
|
|
1703
|
+
}
|
|
1704
|
+
}]
|
|
1705
|
+
}
|
|
1643
1706
|
});
|
|
1644
1707
|
return _context8.abrupt("return");
|
|
1645
1708
|
|
|
1646
|
-
case
|
|
1647
|
-
|
|
1709
|
+
case 25:
|
|
1710
|
+
hijos = ((_familia$variants = familia.variants) !== null && _familia$variants !== void 0 ? _familia$variants : []).filter(function (v) {
|
|
1711
|
+
return !v.isCurrent;
|
|
1712
|
+
});
|
|
1713
|
+
|
|
1714
|
+
if (!(hijos.length > 0)) {
|
|
1715
|
+
_context8.next = 29;
|
|
1716
|
+
break;
|
|
1717
|
+
}
|
|
1718
|
+
|
|
1719
|
+
setPendingPropagation({
|
|
1720
|
+
cambios: cambios
|
|
1721
|
+
});
|
|
1722
|
+
return _context8.abrupt("return");
|
|
1723
|
+
|
|
1724
|
+
case 29:
|
|
1725
|
+
_context8.next = 31;
|
|
1648
1726
|
return saveDatasheets(token);
|
|
1649
1727
|
|
|
1650
|
-
case
|
|
1651
|
-
return _context8.abrupt("break",
|
|
1728
|
+
case 31:
|
|
1729
|
+
return _context8.abrupt("break", 36);
|
|
1652
1730
|
|
|
1653
|
-
case
|
|
1654
|
-
_context8.next =
|
|
1731
|
+
case 32:
|
|
1732
|
+
_context8.next = 34;
|
|
1655
1733
|
return updateImages(token);
|
|
1656
1734
|
|
|
1657
|
-
case
|
|
1658
|
-
return _context8.abrupt("break",
|
|
1735
|
+
case 34:
|
|
1736
|
+
return _context8.abrupt("break", 36);
|
|
1659
1737
|
|
|
1660
|
-
case
|
|
1661
|
-
return _context8.abrupt("break",
|
|
1738
|
+
case 35:
|
|
1739
|
+
return _context8.abrupt("break", 36);
|
|
1662
1740
|
|
|
1663
|
-
case
|
|
1664
|
-
_context8.next =
|
|
1741
|
+
case 36:
|
|
1742
|
+
_context8.next = 38;
|
|
1665
1743
|
return loadData(false);
|
|
1666
1744
|
|
|
1667
|
-
case
|
|
1745
|
+
case 38:
|
|
1668
1746
|
case "end":
|
|
1669
1747
|
return _context8.stop();
|
|
1670
1748
|
}
|
|
1671
1749
|
}
|
|
1672
|
-
}, _callee8);
|
|
1750
|
+
}, _callee8, null, [[15,, 19, 22]]);
|
|
1673
1751
|
}));
|
|
1674
1752
|
|
|
1675
1753
|
return function handleOnClickSave() {
|
package/package.json
CHANGED
|
@@ -7,6 +7,10 @@ import { Container } from "./styles";
|
|
|
7
7
|
*
|
|
8
8
|
* El estado "propio" es el que carga información —ese campo se separó del padre—
|
|
9
9
|
* así que es el que lleva acento y el que ofrece la acción de volver a heredar.
|
|
10
|
+
*
|
|
11
|
+
* OJO: "propio" ya NO significa "a salvo de la propagación". Desde el cambio del
|
|
12
|
+
* 2026-09-01 la propagación sobrescribe también estos valores; el aviso de que va
|
|
13
|
+
* a pasar vive en el diálogo de propagación, no aquí. El tooltip lo dice así.
|
|
10
14
|
* "Heredado" es el caso normal y se mantiene discreto: si gritara en cada campo,
|
|
11
15
|
* la ficha entera se volvería ruido.
|
|
12
16
|
*
|
|
@@ -41,14 +45,20 @@ export const InheritanceTag = ({
|
|
|
41
45
|
className="tag-label"
|
|
42
46
|
title={
|
|
43
47
|
isOverridden
|
|
44
|
-
? "Valor
|
|
48
|
+
? "Valor capturado en este producto hijo, distinto al del padre. Si el padre propaga este atributo, se reemplaza."
|
|
45
49
|
: `Valor heredado${parentUpc ? ` del producto ${parentUpc}` : " del producto padre"}.`
|
|
46
50
|
}
|
|
47
51
|
>
|
|
48
52
|
{isOverridden ? "Propio" : "Heredado"}
|
|
49
53
|
</span>
|
|
50
54
|
|
|
51
|
-
{
|
|
55
|
+
{/* `Boolean`, no `isOverridden` a secas.
|
|
56
|
+
|
|
57
|
+
`is_overridden` llega como NÚMERO (0/1) desde la API y el reducer. Con
|
|
58
|
+
`{isOverridden && ...}`, un 0 se evalúa a 0 y React IMPRIME el texto
|
|
59
|
+
«0» — ese era el cero que aparecía en el hueco de la acción justo
|
|
60
|
+
después de pulsar «Volver a heredar», que es cuando el valor pasa a 0. */}
|
|
61
|
+
{Boolean(isOverridden) && onRestore && (
|
|
52
62
|
<button type="button" className="tag-action" onClick={onRestore}>
|
|
53
63
|
{restoreLabel}
|
|
54
64
|
</button>
|
|
@@ -321,7 +321,9 @@ export const TagAndInput = ({
|
|
|
321
321
|
className={"input-container"}
|
|
322
322
|
key={`generalTagInput-${inputType}`}
|
|
323
323
|
>
|
|
324
|
-
{label?.length
|
|
324
|
+
{/* `Boolean`, no `label?.length`: con la etiqueta en cadena vacía la
|
|
325
|
+
expresión valía 0 y React imprimía un «0» en la ficha. */}
|
|
326
|
+
{Boolean(label?.length) && (
|
|
325
327
|
<div className="title-container">
|
|
326
328
|
<div className="titles">
|
|
327
329
|
<ScreenHeader
|
|
@@ -46,7 +46,9 @@ export const TagAndInputv2 = ({
|
|
|
46
46
|
className={"input-container"}
|
|
47
47
|
key={`generalTagInput-${inputType}`}
|
|
48
48
|
>
|
|
49
|
-
{label?.length
|
|
49
|
+
{/* `Boolean`, no `label?.length`: con la etiqueta en cadena vacía la
|
|
50
|
+
expresión valía 0 y React imprimía un «0» en la ficha. */}
|
|
51
|
+
{Boolean(label?.length) && (
|
|
50
52
|
<div className="title-container">
|
|
51
53
|
<ScreenHeaderv2
|
|
52
54
|
text={label}
|
|
@@ -2,7 +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 { getProductImage
|
|
5
|
+
import { getProductImage } from "../../../_utils/helper";
|
|
6
6
|
|
|
7
7
|
import {
|
|
8
8
|
Overlay,
|
|
@@ -106,13 +106,11 @@ export const LinkVariantsModal = ({
|
|
|
106
106
|
attributes: [],
|
|
107
107
|
descriptions: [],
|
|
108
108
|
images: [],
|
|
109
|
-
categoryRetailers: [],
|
|
110
109
|
});
|
|
111
110
|
const [chosen, setChosen] = useState({
|
|
112
111
|
attributes: [],
|
|
113
112
|
descriptions: [],
|
|
114
113
|
images: [],
|
|
115
|
-
categoryRetailers: [],
|
|
116
114
|
});
|
|
117
115
|
const [nameScope, setNameScope] = useState(null);
|
|
118
116
|
|
|
@@ -121,7 +119,6 @@ export const LinkVariantsModal = ({
|
|
|
121
119
|
// se pudiera ligar sin haber mirado nada, que es justo lo que hay que evitar
|
|
122
120
|
// en una operación que sobrescribe valores capturados.
|
|
123
121
|
const [modes, setModes] = useState({
|
|
124
|
-
categoryRetailers: null,
|
|
125
122
|
descriptions: null,
|
|
126
123
|
attributes: null,
|
|
127
124
|
images: null,
|
|
@@ -285,7 +282,6 @@ export const LinkVariantsModal = ({
|
|
|
285
282
|
attributes: data.attributes || [],
|
|
286
283
|
descriptions: data.descriptions || [],
|
|
287
284
|
images: data.images || [],
|
|
288
|
-
categoryRetailers: data.categoryRetailers || [],
|
|
289
285
|
};
|
|
290
286
|
|
|
291
287
|
setOffered(listas);
|
|
@@ -294,10 +290,8 @@ export const LinkVariantsModal = ({
|
|
|
294
290
|
attributes: [],
|
|
295
291
|
descriptions: [],
|
|
296
292
|
images: [],
|
|
297
|
-
categoryRetailers: [],
|
|
298
293
|
});
|
|
299
294
|
setModes({
|
|
300
|
-
categoryRetailers: null,
|
|
301
295
|
descriptions: null,
|
|
302
296
|
attributes: null,
|
|
303
297
|
images: null,
|
|
@@ -367,12 +361,6 @@ export const LinkVariantsModal = ({
|
|
|
367
361
|
);
|
|
368
362
|
|
|
369
363
|
const buildInherit = (seleccion) => {
|
|
370
|
-
const parejas = offered.categoryRetailers.map((item) => ({
|
|
371
|
-
clave: `${item.retailerId}-${item.categoryId}`,
|
|
372
|
-
retailerId: item.retailerId,
|
|
373
|
-
categoryId: item.categoryId,
|
|
374
|
-
}));
|
|
375
|
-
|
|
376
364
|
return {
|
|
377
365
|
attributes:
|
|
378
366
|
seleccion.modes.attributes === "all"
|
|
@@ -384,15 +372,24 @@ export const LinkVariantsModal = ({
|
|
|
384
372
|
: seleccion.chosen.descriptions,
|
|
385
373
|
images:
|
|
386
374
|
seleccion.modes.images === "all" ? "all" : seleccion.chosen.images,
|
|
387
|
-
|
|
388
|
-
|
|
389
|
-
|
|
375
|
+
/*
|
|
376
|
+
Siempre "all", nunca una lista.
|
|
377
|
+
|
|
378
|
+
El hijo se queda con las cadenas del padre, sin preguntar. No es una
|
|
379
|
+
preferencia: es lo que hace que la copia de atributos SE VEA. La ficha
|
|
380
|
+
arma su lista desde `attribute_manager` acotado por las cadenas del
|
|
381
|
+
propio producto, así que un valor copiado a un atributo que la cadena del
|
|
382
|
+
hijo no configura queda escrito y invisible.
|
|
383
|
+
|
|
384
|
+
Se manda la palabra y no la lista para que signifique "todas" también si
|
|
385
|
+
alguien agregó una cadena al padre entre abrir el modal y guardar.
|
|
386
|
+
*/
|
|
387
|
+
categoryRetailers: "all",
|
|
390
388
|
};
|
|
391
389
|
};
|
|
392
390
|
|
|
393
391
|
const seleccionVacia = () => ({
|
|
394
392
|
modes: {
|
|
395
|
-
categoryRetailers: null,
|
|
396
393
|
descriptions: null,
|
|
397
394
|
attributes: null,
|
|
398
395
|
images: null,
|
|
@@ -401,7 +398,6 @@ export const LinkVariantsModal = ({
|
|
|
401
398
|
attributes: [],
|
|
402
399
|
descriptions: [],
|
|
403
400
|
images: [],
|
|
404
|
-
categoryRetailers: [],
|
|
405
401
|
},
|
|
406
402
|
});
|
|
407
403
|
|
|
@@ -478,21 +474,13 @@ export const LinkVariantsModal = ({
|
|
|
478
474
|
// ofrece nada en ella —no se puede pedir que decida sobre una lista vacía—.
|
|
479
475
|
const decidida = (dimension) => {
|
|
480
476
|
if (modes[dimension] !== null) return true;
|
|
481
|
-
if (dimension === "categoryRetailers") {
|
|
482
|
-
return offered.categoryRetailers.length === 0;
|
|
483
|
-
}
|
|
484
477
|
// Una imagen cuyo archivo no existe no es algo sobre lo que se pueda decidir.
|
|
485
478
|
return dimension === "images"
|
|
486
479
|
? imagenesUtiles.length === 0
|
|
487
480
|
: offered[dimension].length === 0;
|
|
488
481
|
};
|
|
489
482
|
|
|
490
|
-
const DIMENSIONES = [
|
|
491
|
-
"categoryRetailers",
|
|
492
|
-
"descriptions",
|
|
493
|
-
"attributes",
|
|
494
|
-
"images",
|
|
495
|
-
];
|
|
483
|
+
const DIMENSIONES = ["descriptions", "attributes", "images"];
|
|
496
484
|
|
|
497
485
|
// Con pestañas, que la que estoy viendo esté completa no basta: ligar escribe
|
|
498
486
|
// en todos, así que todos tienen que estar decididos. Si no, un producto se
|
|
@@ -503,9 +491,6 @@ export const LinkVariantsModal = ({
|
|
|
503
491
|
|
|
504
492
|
return DIMENSIONES.every((dimension) => {
|
|
505
493
|
if (guardado.modes[dimension] !== null) return true;
|
|
506
|
-
if (dimension === "categoryRetailers") {
|
|
507
|
-
return offered.categoryRetailers.length === 0;
|
|
508
|
-
}
|
|
509
494
|
return dimension === "images"
|
|
510
495
|
? imagenesUtiles.length === 0
|
|
511
496
|
: offered[dimension].length === 0;
|
|
@@ -521,7 +506,6 @@ export const LinkVariantsModal = ({
|
|
|
521
506
|
: selected.every(decididoPara));
|
|
522
507
|
|
|
523
508
|
const faltantes = [
|
|
524
|
-
["categoryRetailers", "cadenas"],
|
|
525
509
|
["descriptions", "descripciones"],
|
|
526
510
|
["attributes", "atributos"],
|
|
527
511
|
["images", "imágenes"],
|
|
@@ -858,96 +842,32 @@ export const LinkVariantsModal = ({
|
|
|
858
842
|
</>
|
|
859
843
|
)}
|
|
860
844
|
|
|
861
|
-
{/*
|
|
862
|
-
hijo, porque de eso depende qué atributos le aplican. Las
|
|
863
|
-
imágenes van al final: son la decisión más visual y la que menos
|
|
864
|
-
arrastra a las demás. */}
|
|
845
|
+
{/* Ya no se pregunta por cadenas ni categorías.
|
|
865
846
|
|
|
866
|
-
|
|
867
|
-
|
|
868
|
-
|
|
869
|
-
|
|
870
|
-
|
|
871
|
-
|
|
872
|
-
<input
|
|
873
|
-
type="radio"
|
|
874
|
-
name={`categoryRetailers-${activeChild ?? "todos"}`}
|
|
875
|
-
checked={modes.categoryRetailers === "all"}
|
|
876
|
-
onChange={() =>
|
|
877
|
-
chooseMode(
|
|
878
|
-
"categoryRetailers",
|
|
879
|
-
"all",
|
|
880
|
-
offered.categoryRetailers.map(
|
|
881
|
-
(item) => `${item.retailerId}-${item.categoryId}`
|
|
882
|
-
)
|
|
883
|
-
)
|
|
884
|
-
}
|
|
885
|
-
/>
|
|
886
|
-
<span>
|
|
887
|
-
Todas las del producto padre
|
|
888
|
-
<span className="hint">
|
|
889
|
-
{offered.categoryRetailers.length} cadena(s).
|
|
890
|
-
</span>
|
|
891
|
-
</span>
|
|
892
|
-
</label>
|
|
893
|
-
<label>
|
|
894
|
-
<input
|
|
895
|
-
type="radio"
|
|
896
|
-
name={`categoryRetailers-${activeChild ?? "todos"}`}
|
|
897
|
-
checked={modes.categoryRetailers === "some"}
|
|
898
|
-
onChange={() =>
|
|
899
|
-
chooseMode("categoryRetailers", "some", [])
|
|
900
|
-
}
|
|
901
|
-
/>
|
|
902
|
-
<span>
|
|
903
|
-
Elegir cuáles
|
|
904
|
-
<span className="hint">
|
|
905
|
-
Solo se puede heredar de las que ya tiene el padre.
|
|
906
|
-
</span>
|
|
907
|
-
</span>
|
|
908
|
-
</label>
|
|
909
|
-
</Choices>
|
|
847
|
+
El valor de un atributo vive en `article_data` por (artículo,
|
|
848
|
+
versión, atributo): no está partido por cadena, así que copiar
|
|
849
|
+
el atributo 27 del padre al hijo es lo mismo sin importar en
|
|
850
|
+
qué cadenas esté cada uno. Elegir cadenas aquí no cambiaba nada
|
|
851
|
+
de eso; solo servía para decidir en qué cadenas vive el hijo, y
|
|
852
|
+
la respuesta correcta resultó ser siempre «las del padre».
|
|
910
853
|
|
|
911
|
-
|
|
912
|
-
|
|
913
|
-
|
|
914
|
-
|
|
915
|
-
|
|
916
|
-
|
|
917
|
-
|
|
918
|
-
|
|
919
|
-
<label>
|
|
920
|
-
<input
|
|
921
|
-
type="checkbox"
|
|
922
|
-
disabled={modes.categoryRetailers === "all"}
|
|
923
|
-
checked={chosen.categoryRetailers.includes(
|
|
924
|
-
item.clave
|
|
925
|
-
)}
|
|
926
|
-
onChange={() =>
|
|
927
|
-
toggleChoice("categoryRetailers", item.clave)
|
|
928
|
-
}
|
|
929
|
-
/>
|
|
930
|
-
<img
|
|
931
|
-
className="logo"
|
|
932
|
-
src={getRetailerPicture(item.retailerId, 24, 24)}
|
|
933
|
-
alt=""
|
|
934
|
-
/>
|
|
935
|
-
<span>
|
|
936
|
-
{item.retailerName}
|
|
937
|
-
<span className="meta">
|
|
938
|
-
{item.categoryName || "Sin categoría"}
|
|
939
|
-
</span>
|
|
940
|
-
</span>
|
|
941
|
-
</label>
|
|
942
|
-
</li>
|
|
943
|
-
))}
|
|
944
|
-
</List>
|
|
854
|
+
Medido en dev sobre las 24 familias reales: cuando el hijo
|
|
855
|
+
comparte al menos una cadena con el padre, 0 atributos quedan
|
|
856
|
+
sin mostrarse; cuando no comparte ninguna, quedan entre 3 y 31.
|
|
857
|
+
91 de 394 en total (23%). La razón es que la ficha arma su lista
|
|
858
|
+
desde `attribute_manager` acotado por las cadenas DEL PROPIO
|
|
859
|
+
producto, así que un valor copiado a un atributo que su cadena
|
|
860
|
+
no configura se escribe pero no se ve. Heredar todas las del
|
|
861
|
+
padre es lo que hace visible la copia.
|
|
945
862
|
|
|
946
|
-
|
|
863
|
+
Por eso `buildInherit` manda "all" y esto ya no se pregunta.
|
|
864
|
+
|
|
865
|
+
El orden de lo que queda no es casual: las imágenes van al
|
|
866
|
+
final, son la decisión más visual y la que menos arrastra. */}
|
|
947
867
|
|
|
948
868
|
<Block>
|
|
949
869
|
<h3>
|
|
950
|
-
<span className="paso">
|
|
870
|
+
<span className="paso">1</span> Descripciones
|
|
951
871
|
</h3>
|
|
952
872
|
{offered.descriptions.length ? (
|
|
953
873
|
<>
|
|
@@ -1030,7 +950,7 @@ export const LinkVariantsModal = ({
|
|
|
1030
950
|
|
|
1031
951
|
<Block>
|
|
1032
952
|
<h3>
|
|
1033
|
-
<span className="paso">
|
|
953
|
+
<span className="paso">2</span> Atributos de la ficha técnica
|
|
1034
954
|
</h3>
|
|
1035
955
|
{offered.attributes.length ? (
|
|
1036
956
|
<>
|
|
@@ -1127,7 +1047,7 @@ export const LinkVariantsModal = ({
|
|
|
1127
1047
|
{imagenesUtiles.length > 0 && (
|
|
1128
1048
|
<Block>
|
|
1129
1049
|
<h3>
|
|
1130
|
-
<span className="paso">
|
|
1050
|
+
<span className="paso">3</span> Imágenes
|
|
1131
1051
|
</h3>
|
|
1132
1052
|
<Choices>
|
|
1133
1053
|
<label>
|
|
@@ -184,14 +184,6 @@ export const List = styled.ul`
|
|
|
184
184
|
color: ${GlobalColors.gray};
|
|
185
185
|
}
|
|
186
186
|
|
|
187
|
-
.logo {
|
|
188
|
-
width: 24px;
|
|
189
|
-
height: 24px;
|
|
190
|
-
object-fit: contain;
|
|
191
|
-
border-radius: 3px;
|
|
192
|
-
flex-shrink: 0;
|
|
193
|
-
}
|
|
194
|
-
|
|
195
187
|
li {
|
|
196
188
|
display: flex;
|
|
197
189
|
align-items: center;
|
|
@@ -21,7 +21,10 @@ import {
|
|
|
21
21
|
|
|
22
22
|
props (* requerido):
|
|
23
23
|
- *changedAttributes: [{ id, name, value }] lo que se acaba de editar
|
|
24
|
-
- *variants: [{ articleId, name, upc }] los hijos
|
|
24
|
+
- *variants: [{ articleId, name, upc, overriddenAttributes }] los hijos
|
|
25
|
+
activos del padre. `overriddenAttributes` son los atributos que ese hijo
|
|
26
|
+
capturó a mano; la propagación los sobrescribe, y este diálogo es el
|
|
27
|
+
único lugar donde el usuario puede enterarse antes de que pase.
|
|
25
28
|
- *onCancel: cerrar sin guardar nada
|
|
26
29
|
- *onConfirm: (propagation | null) => void
|
|
27
30
|
null → guardar solo el padre
|
|
@@ -49,6 +52,31 @@ export const PropagateChangesModal = ({
|
|
|
49
52
|
const puedePropagar = chosenAttributes.length > 0 && destinatarios > 0;
|
|
50
53
|
const enSegundoPlano = destinatarios > UMBRAL_SEGUNDO_PLANO;
|
|
51
54
|
|
|
55
|
+
/*
|
|
56
|
+
A quién se le va a pisar un valor capturado a mano.
|
|
57
|
+
|
|
58
|
+
La propagación sobrescribe: eso se decidió a propósito, porque el usuario ya
|
|
59
|
+
eligió atributo por atributo y hijo por hijo. Pero sobrescribir en silencio
|
|
60
|
+
es otra cosa, y desde que se quitaron las etiquetas de «Heredado» y «Propio»
|
|
61
|
+
este diálogo es el único momento en que alguien puede saberlo.
|
|
62
|
+
|
|
63
|
+
Se recalcula con lo que está marcado ahora, no al abrir: si el usuario
|
|
64
|
+
desmarca el atributo conflictivo, el aviso desaparece — que es justo la
|
|
65
|
+
salida que el aviso debería estar ofreciendo.
|
|
66
|
+
*/
|
|
67
|
+
const destinos = scope === "all"
|
|
68
|
+
? variants
|
|
69
|
+
: variants.filter((item) => chosenChildren.includes(item.articleId));
|
|
70
|
+
|
|
71
|
+
const conValorPropio = destinos
|
|
72
|
+
.map((hijo) => ({
|
|
73
|
+
...hijo,
|
|
74
|
+
pisados: (hijo.overriddenAttributes ?? []).filter((id) =>
|
|
75
|
+
chosenAttributes.includes(Number(id))
|
|
76
|
+
),
|
|
77
|
+
}))
|
|
78
|
+
.filter((hijo) => hijo.pisados.length > 0);
|
|
79
|
+
|
|
52
80
|
const toggleAttribute = (id) => {
|
|
53
81
|
setChosenAttributes((current) =>
|
|
54
82
|
current.includes(id)
|
|
@@ -155,13 +183,20 @@ export const PropagateChangesModal = ({
|
|
|
155
183
|
</List>
|
|
156
184
|
)}
|
|
157
185
|
|
|
158
|
-
{/*
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
186
|
+
{/* Lo que sorprende si no se dice: aquí se pisa lo que el hijo
|
|
187
|
+
hubiera capturado a mano. Se nombra a quién, no solo cuántos: con
|
|
188
|
+
«2 hijos» nadie sabe si le importa, con los nombres sí. */}
|
|
189
|
+
{conValorPropio.length > 0 && (
|
|
190
|
+
<Note className="accent">
|
|
191
|
+
{conValorPropio.length === 1
|
|
192
|
+
? "Un hijo tiene un valor propio en lo que elegiste y se va a reemplazar: "
|
|
193
|
+
: `${conValorPropio.length} hijos tienen valores propios en lo que elegiste y se van a reemplazar: `}
|
|
194
|
+
{conValorPropio
|
|
195
|
+
.map((hijo) => `${hijo.name || `Producto ${hijo.articleId}`} (${hijo.pisados.length})`)
|
|
196
|
+
.join(", ")}
|
|
197
|
+
. Si no quieres pisarlos, desmarca ese atributo o ese hijo.
|
|
198
|
+
</Note>
|
|
199
|
+
)}
|
|
165
200
|
|
|
166
201
|
{enSegundoPlano && (
|
|
167
202
|
<Note className="accent">
|
|
@@ -1131,17 +1131,73 @@ const RetailerProductEditionView = ({
|
|
|
1131
1131
|
await saveDescriptions(token);
|
|
1132
1132
|
break;
|
|
1133
1133
|
case "Ficha técnica": {
|
|
1134
|
-
// Guardar un padre con hijos abre la decisión de propagar. Sin este
|
|
1135
|
-
// paso el cambio se queda solo en el padre y los hijos se van
|
|
1136
|
-
// separando de él sin que nadie lo note.
|
|
1137
|
-
const esPadre = hijosDelProducto > 0;
|
|
1138
1134
|
const cambios = cambiosDeFichaTecnica();
|
|
1139
1135
|
|
|
1140
|
-
if (
|
|
1141
|
-
|
|
1142
|
-
|
|
1143
|
-
|
|
1144
|
-
|
|
1136
|
+
if (cambios.length === 0) {
|
|
1137
|
+
await saveDatasheets(token);
|
|
1138
|
+
break;
|
|
1139
|
+
}
|
|
1140
|
+
|
|
1141
|
+
/*
|
|
1142
|
+
Guardar un padre con hijos abre la decisión de propagar. Sin este paso
|
|
1143
|
+
el cambio se queda solo en el padre y los hijos se van separando de él
|
|
1144
|
+
sin que nadie lo note.
|
|
1145
|
+
|
|
1146
|
+
Si tiene hijos se resuelve AQUÍ, esperando la respuesta, en vez de
|
|
1147
|
+
mirar la copia en memoria. `family` es una consulta asíncrona: si
|
|
1148
|
+
todavía no llegó, o si falló, `hijosDelProducto` vale 0 — o sea que
|
|
1149
|
+
«todavía no sé» se leía igual que «no tiene hijos», y el padre se
|
|
1150
|
+
guardaba sin ofrecer la propagación. En silencio, que es lo peor: el
|
|
1151
|
+
usuario creía haber bajado el cambio a los hijos.
|
|
1152
|
+
|
|
1153
|
+
La ficha de la aplicación de clientes no tiene el problema porque ahí
|
|
1154
|
+
`state.product.variantsCount` viene del listado; aquí el producto sale
|
|
1155
|
+
del endpoint de reportes, que no trae el parentesco.
|
|
1156
|
+
*/
|
|
1157
|
+
setPreparandoPropagacion(true);
|
|
1158
|
+
let familia = null;
|
|
1159
|
+
try {
|
|
1160
|
+
familia = await loadFamily(true);
|
|
1161
|
+
} finally {
|
|
1162
|
+
setPreparandoPropagacion(false);
|
|
1163
|
+
}
|
|
1164
|
+
|
|
1165
|
+
// No se pudo saber si tiene hijos. Guardar de todas formas repetiría el
|
|
1166
|
+
// defecto que se está corrigiendo, así que se dice y se deja la decisión.
|
|
1167
|
+
if (!familia || familia.error) {
|
|
1168
|
+
dispatch({
|
|
1169
|
+
type: "SET_MODAL",
|
|
1170
|
+
payload: {
|
|
1171
|
+
show: true,
|
|
1172
|
+
title: "No se pudo consultar la familia",
|
|
1173
|
+
message:
|
|
1174
|
+
"No pudimos saber si este producto tiene hijos, así que no sabemos si hay que bajarles el cambio. Vuelve a intentar el guardado; si sigue fallando, guarda y revisa los hijos a mano.",
|
|
1175
|
+
image: warningIcon,
|
|
1176
|
+
buttons: [
|
|
1177
|
+
{
|
|
1178
|
+
text: "Entendido",
|
|
1179
|
+
buttonType: "general-white-button",
|
|
1180
|
+
action: () =>
|
|
1181
|
+
dispatch({ type: "SET_MODAL", payload: { show: false } }),
|
|
1182
|
+
},
|
|
1183
|
+
{
|
|
1184
|
+
text: "Guardar solo este producto",
|
|
1185
|
+
buttonType: "general-button-default",
|
|
1186
|
+
action: () => {
|
|
1187
|
+
dispatch({ type: "SET_MODAL", payload: { show: false } });
|
|
1188
|
+
saveDatasheets(token);
|
|
1189
|
+
},
|
|
1190
|
+
},
|
|
1191
|
+
],
|
|
1192
|
+
},
|
|
1193
|
+
});
|
|
1194
|
+
return;
|
|
1195
|
+
}
|
|
1196
|
+
|
|
1197
|
+
const hijos = (familia.variants ?? []).filter((v) => !v.isCurrent);
|
|
1198
|
+
|
|
1199
|
+
if (hijos.length > 0) {
|
|
1200
|
+
setPendingPropagation({ cambios });
|
|
1145
1201
|
return;
|
|
1146
1202
|
}
|
|
1147
1203
|
|