contentoh-components-library 21.6.22 → 21.6.23
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 +359 -114
- package/dist/components/organisms/LinkVariantsModal/styles.js +15 -3
- package/dist/components/pages/ProviderProductEdition/context/provider-product-edition.context.js +22 -13
- package/dist/components/pages/ProviderProductEdition/index.js +307 -107
- package/dist/components/pages/ProviderProductEdition/styles.js +2 -2
- package/package.json +1 -1
- package/src/components/organisms/LinkVariantsModal/index.js +347 -53
- package/src/components/organisms/LinkVariantsModal/styles.js +120 -0
- package/src/components/pages/ProviderProductEdition/context/provider-product-edition.context.jsx +14 -1
- package/src/components/pages/ProviderProductEdition/index.js +161 -3
- package/src/components/pages/ProviderProductEdition/styles.js +68 -0
|
@@ -15,9 +15,9 @@ var _variables = require("../../../global-files/variables");
|
|
|
15
15
|
|
|
16
16
|
var _templateObject;
|
|
17
17
|
|
|
18
|
-
var Container = _styledComponents.default.div(_templateObject || (_templateObject = (0, _taggedTemplateLiteral2.default)(["\n display: flex;\n flex-direction: column;\n height: 100%;\n flex: 1;\n overflow: auto;\n\n .data-container {\n display: flex;\n flex: 0%;\n height: calc(100% - ", "px);\n .image-data-panel {\n width: 340px;\n\n & + * {\n margin-left: 10px;\n }\n }\n\n .product-information {\n width: 100%;\n height: 100%;\n display: flex;\n flex-direction: column;\n\n .services-information-container {\n height: 100%;\n overflow: auto;\n }\n\n .image-services {\n aside {\n display: grid;\n grid-template-columns: repeat(auto-fill, 179px);\n column-gap: 15px;\n row-gap: 15px;\n padding: 20px;\n }\n }\n\n .commentary-box {\n display: flex;\n justify-content: space-between;\n align-items: flex-end;\n padding: 10px;\n padding-left: 0;\n\n .commentary {\n display: flex;\n align-items: flex-end;\n\n .input-container {\n width: 500px;\n\n .quill {\n height: 100px;\n }\n\n & + * {\n margin-left: 5px;\n }\n }\n\n .buttons-box {\n display: flex;\n width: 210px;\n flex-wrap: wrap;\n\n .general-transparent-button {\n & + * {\n margin-top: 5px;\n }\n }\n\n .general-transparent-button,\n .general-green-button,\n .general-button-disabled {\n width: fit-content;\n min-width: 201px;\n height: 40px;\n }\n }\n }\n }\n\n .feedback-box {\n display: flex;\n }\n\n .required-inputs-message {\n font-family: ", ";\n font-size: 13px;\n color: ", ";\n padding: 10px;\n display: flex;\n align-items: center;\n\n p + * {\n margin-top: 10px;\n }\n\n button {\n min-width: fit-content;\n }\n }\n }\n }\n #modal-message-box {\n width: 400px;\n height: 100px;\n }\n .container {\n width: 100%;\n height: 90%;\n .dropzone {\n height: 100%;\n width: 100%;\n position: relative;\n transition: all 0.5s ease;\n\n &.drag-active {\n background-color: rgba(", ", 0.05);\n border: 2px dashed ", ";\n border-radius: 8px;\n\n aside {\n opacity: 0.3;\n pointer-events: none;\n }\n }\n\n .drag-overlay {\n position: absolute;\n top: 0;\n left: 0;\n right: 0;\n bottom: 0;\n display: flex;\n align-items: center;\n justify-content: center;\n background-color: rgba(", ", 0.1);\n border-radius: 8px;\n pointer-events: none;\n z-index: 10;\n\n p {\n font-family: ", ";\n font-size: 24px;\n font-weight: 600;\n color: ", ";\n text-align: center;\n padding: 20px;\n background-color: ", ";\n border-radius: 8px;\n box-shadow: 0 4px 12px rgba(0, 0, 0, 0.15);\n }\n }\n }\n }\n .variants-actions {\n display: flex;\n align-items: center;\n gap: 10px;\n margin-bottom: 8px;\n\n .link-variants {\n border: 1px solid ", ";\n border-radius: 6px;\n background: none;\n padding: 6px 12px;\n font-family: ", ";\n font-size: 11px;\n color: ", ";\n cursor: pointer;\n }\n\n .variants-count {\n font-family: ", ";\n font-size: 11px;\n color: ", ";\n }\n }\n"])), function (_ref) {
|
|
18
|
+
var Container = _styledComponents.default.div(_templateObject || (_templateObject = (0, _taggedTemplateLiteral2.default)(["\n display: flex;\n flex-direction: column;\n height: 100%;\n flex: 1;\n overflow: auto;\n\n .data-container {\n display: flex;\n flex: 0%;\n height: calc(100% - ", "px);\n .image-data-panel {\n width: 340px;\n\n & + * {\n margin-left: 10px;\n }\n }\n\n .product-information {\n width: 100%;\n height: 100%;\n display: flex;\n flex-direction: column;\n\n .services-information-container {\n height: 100%;\n overflow: auto;\n }\n\n .image-services {\n aside {\n display: grid;\n grid-template-columns: repeat(auto-fill, 179px);\n column-gap: 15px;\n row-gap: 15px;\n padding: 20px;\n }\n }\n\n .commentary-box {\n display: flex;\n justify-content: space-between;\n align-items: flex-end;\n padding: 10px;\n padding-left: 0;\n\n .commentary {\n display: flex;\n align-items: flex-end;\n\n .input-container {\n width: 500px;\n\n .quill {\n height: 100px;\n }\n\n & + * {\n margin-left: 5px;\n }\n }\n\n .buttons-box {\n display: flex;\n width: 210px;\n flex-wrap: wrap;\n\n .general-transparent-button {\n & + * {\n margin-top: 5px;\n }\n }\n\n .general-transparent-button,\n .general-green-button,\n .general-button-disabled {\n width: fit-content;\n min-width: 201px;\n height: 40px;\n }\n }\n }\n }\n\n .feedback-box {\n display: flex;\n }\n\n .required-inputs-message {\n font-family: ", ";\n font-size: 13px;\n color: ", ";\n padding: 10px;\n display: flex;\n align-items: center;\n\n p + * {\n margin-top: 10px;\n }\n\n button {\n min-width: fit-content;\n }\n }\n }\n }\n #modal-message-box {\n width: 400px;\n height: 100px;\n }\n .container {\n width: 100%;\n height: 90%;\n .dropzone {\n height: 100%;\n width: 100%;\n position: relative;\n transition: all 0.5s ease;\n\n &.drag-active {\n background-color: rgba(", ", 0.05);\n border: 2px dashed ", ";\n border-radius: 8px;\n\n aside {\n opacity: 0.3;\n pointer-events: none;\n }\n }\n\n .drag-overlay {\n position: absolute;\n top: 0;\n left: 0;\n right: 0;\n bottom: 0;\n display: flex;\n align-items: center;\n justify-content: center;\n background-color: rgba(", ", 0.1);\n border-radius: 8px;\n pointer-events: none;\n z-index: 10;\n\n p {\n font-family: ", ";\n font-size: 24px;\n font-weight: 600;\n color: ", ";\n text-align: center;\n padding: 20px;\n background-color: ", ";\n border-radius: 8px;\n box-shadow: 0 4px 12px rgba(0, 0, 0, 0.15);\n }\n }\n }\n }\n .variants-actions {\n display: flex;\n align-items: center;\n gap: 10px;\n margin-bottom: 8px;\n\n .link-variants {\n border: 1px solid ", ";\n border-radius: 6px;\n background: none;\n padding: 6px 12px;\n font-family: ", ";\n font-size: 11px;\n color: ", ";\n cursor: pointer;\n }\n\n .variants-count {\n border: none;\n background: none;\n padding: 0;\n cursor: pointer;\n font-family: ", ";\n font-size: 11px;\n color: ", ";\n text-decoration: underline;\n }\n }\n\n .variants-family {\n display: flex;\n flex-direction: column;\n gap: 4px;\n margin-bottom: 10px;\n padding: 8px;\n border: 1px solid ", ";\n border-radius: 6px;\n\n .loading {\n margin: 0;\n font-family: ", ";\n font-size: 11px;\n color: ", ";\n }\n\n .family-row {\n display: flex;\n align-items: baseline;\n gap: 8px;\n padding: 6px 8px;\n border: none;\n border-radius: 4px;\n background: none;\n cursor: pointer;\n text-align: left;\n font-family: ", ";\n font-size: 12px;\n color: ", ";\n }\n\n .family-row:hover:not(:disabled) {\n background-color: ", ";\n }\n\n .family-row:disabled {\n cursor: default;\n opacity: 0.7;\n }\n\n .family-row .role {\n min-width: 96px;\n font-size: 10px;\n color: ", ";\n }\n\n .family-row.current .role {\n color: ", ";\n }\n\n .family-row .name {\n flex: 1;\n overflow: hidden;\n text-overflow: ellipsis;\n white-space: nowrap;\n }\n\n .family-row .upc {\n font-size: 11px;\n color: ", ";\n }\n }\n"])), function (_ref) {
|
|
19
19
|
var headerTop = _ref.headerTop;
|
|
20
20
|
return headerTop;
|
|
21
|
-
}, _variables.FontFamily.AvenirNext, _variables.GlobalColors.color_gray, _variables.GlobalColors.rgb_color_pink, _variables.GlobalColors.color_pink, _variables.GlobalColors.rgb_color_pink, _variables.FontFamily.AvenirNext, _variables.GlobalColors.color_pink, _variables.GlobalColors.white, _variables.GlobalColors.original_magenta, _variables.FontFamily.Raleway_600, _variables.GlobalColors.original_magenta, _variables.FontFamily.Raleway_500, _variables.GlobalColors.gray);
|
|
21
|
+
}, _variables.FontFamily.AvenirNext, _variables.GlobalColors.color_gray, _variables.GlobalColors.rgb_color_pink, _variables.GlobalColors.color_pink, _variables.GlobalColors.rgb_color_pink, _variables.FontFamily.AvenirNext, _variables.GlobalColors.color_pink, _variables.GlobalColors.white, _variables.GlobalColors.original_magenta, _variables.FontFamily.Raleway_600, _variables.GlobalColors.original_magenta, _variables.FontFamily.Raleway_500, _variables.GlobalColors.gray, _variables.GlobalColors.s2, _variables.FontFamily.Raleway_500, _variables.GlobalColors.gray, _variables.FontFamily.Raleway_500, _variables.GlobalColors.deep_gray, _variables.GlobalColors.s2, _variables.GlobalColors.original_magenta, _variables.GlobalColors.gray, _variables.GlobalColors.gray);
|
|
22
22
|
|
|
23
23
|
exports.Container = Container;
|
package/package.json
CHANGED
|
@@ -13,6 +13,9 @@ import {
|
|
|
13
13
|
List,
|
|
14
14
|
AttributeList,
|
|
15
15
|
Section,
|
|
16
|
+
SectionHead,
|
|
17
|
+
ImageGrid,
|
|
18
|
+
AddRetailer,
|
|
16
19
|
Message,
|
|
17
20
|
Footer,
|
|
18
21
|
} from "./styles";
|
|
@@ -66,6 +69,8 @@ export const LinkVariantsModal = ({
|
|
|
66
69
|
articleName,
|
|
67
70
|
articleUpc,
|
|
68
71
|
activeRetailerId,
|
|
72
|
+
retailersAvailable,
|
|
73
|
+
categoriesAvailable,
|
|
69
74
|
token,
|
|
70
75
|
endpoint = process.env.REACT_APP_VARIANTS_ENDPOINT,
|
|
71
76
|
onClose,
|
|
@@ -80,13 +85,29 @@ export const LinkVariantsModal = ({
|
|
|
80
85
|
const [searched, setSearched] = useState(false);
|
|
81
86
|
const [selected, setSelected] = useState([]);
|
|
82
87
|
|
|
83
|
-
|
|
88
|
+
// Lo que el padre ofrece en cada dimensión, y lo que el usuario eligió de
|
|
89
|
+
// cada una. Se guardan por separado porque se eligen por separado: heredar
|
|
90
|
+
// todos los atributos no dice nada sobre qué fotos quiere la variante.
|
|
91
|
+
const [offered, setOffered] = useState({
|
|
92
|
+
attributes: [],
|
|
93
|
+
descriptions: [],
|
|
94
|
+
images: [],
|
|
95
|
+
categoryRetailers: [],
|
|
96
|
+
});
|
|
97
|
+
const [chosen, setChosen] = useState({
|
|
98
|
+
attributes: [],
|
|
99
|
+
descriptions: [],
|
|
100
|
+
images: [],
|
|
101
|
+
categoryRetailers: [],
|
|
102
|
+
});
|
|
84
103
|
const [nameScope, setNameScope] = useState(null);
|
|
85
104
|
const [inheritMode, setInheritMode] = useState("all-filled");
|
|
86
|
-
const [
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
const [
|
|
105
|
+
const [openSection, setOpenSection] = useState("attributes");
|
|
106
|
+
|
|
107
|
+
// Cadenas que el padre NO tiene y el usuario decidió agregar a la variante.
|
|
108
|
+
const [extraRetailers, setExtraRetailers] = useState([]);
|
|
109
|
+
const [newRetailer, setNewRetailer] = useState("");
|
|
110
|
+
const [newCategory, setNewCategory] = useState("");
|
|
90
111
|
|
|
91
112
|
const [loading, setLoading] = useState(false);
|
|
92
113
|
const [error, setError] = useState(null);
|
|
@@ -160,9 +181,27 @@ export const LinkVariantsModal = ({
|
|
|
160
181
|
parentArticleId,
|
|
161
182
|
retailerId: activeRetailerId,
|
|
162
183
|
});
|
|
163
|
-
|
|
184
|
+
|
|
185
|
+
const listas = {
|
|
186
|
+
attributes: data.attributes || [],
|
|
187
|
+
descriptions: data.descriptions || [],
|
|
188
|
+
images: data.images || [],
|
|
189
|
+
categoryRetailers: data.categoryRetailers || [],
|
|
190
|
+
};
|
|
191
|
+
|
|
192
|
+
setOffered(listas);
|
|
164
193
|
setNameScope(data.nameScope ?? null);
|
|
165
|
-
|
|
194
|
+
// Todo preseleccionado: heredar es el caso normal, y quitar lo que sobra
|
|
195
|
+
// es menos trabajo que marcar de cero lo que casi siempre se quiere.
|
|
196
|
+
setChosen({
|
|
197
|
+
attributes: listas.attributes.map((item) => item.id),
|
|
198
|
+
descriptions: listas.descriptions.map((item) => item.id),
|
|
199
|
+
images: listas.images.map((item) => item.id),
|
|
200
|
+
categoryRetailers: listas.categoryRetailers.map(
|
|
201
|
+
(item) => `${item.retailerId}-${item.categoryId}`
|
|
202
|
+
),
|
|
203
|
+
});
|
|
204
|
+
setExtraRetailers([]);
|
|
166
205
|
setStep("inherit");
|
|
167
206
|
} catch (err) {
|
|
168
207
|
setError(readError(err));
|
|
@@ -171,23 +210,86 @@ export const LinkVariantsModal = ({
|
|
|
171
210
|
}
|
|
172
211
|
};
|
|
173
212
|
|
|
174
|
-
const
|
|
175
|
-
|
|
176
|
-
current
|
|
177
|
-
|
|
178
|
-
|
|
213
|
+
const toggleChoice = (dimension, id) => {
|
|
214
|
+
setChosen((current) => {
|
|
215
|
+
const lista = current[dimension];
|
|
216
|
+
return {
|
|
217
|
+
...current,
|
|
218
|
+
[dimension]: lista.includes(id)
|
|
219
|
+
? lista.filter((item) => item !== id)
|
|
220
|
+
: [...lista, id],
|
|
221
|
+
};
|
|
222
|
+
});
|
|
223
|
+
};
|
|
224
|
+
|
|
225
|
+
const toggleAll = (dimension, ids) => {
|
|
226
|
+
setChosen((current) => ({
|
|
227
|
+
...current,
|
|
228
|
+
[dimension]: current[dimension].length === ids.length ? [] : ids,
|
|
229
|
+
}));
|
|
230
|
+
};
|
|
231
|
+
|
|
232
|
+
const addRetailer = () => {
|
|
233
|
+
const retailerId = Number(newRetailer);
|
|
234
|
+
const categoryId = Number(newCategory);
|
|
235
|
+
if (!retailerId || !categoryId) return;
|
|
236
|
+
|
|
237
|
+
const clave = `${retailerId}-${categoryId}`;
|
|
238
|
+
const yaEsta =
|
|
239
|
+
offered.categoryRetailers.some(
|
|
240
|
+
(item) => `${item.retailerId}-${item.categoryId}` === clave
|
|
241
|
+
) || extraRetailers.some((item) => item.clave === clave);
|
|
242
|
+
|
|
243
|
+
if (yaEsta) return;
|
|
244
|
+
|
|
245
|
+
const retailer = (retailersAvailable || []).find(
|
|
246
|
+
(item) => Number(item.id ?? item.value) === retailerId
|
|
247
|
+
);
|
|
248
|
+
const category = (categoriesAvailable || []).find(
|
|
249
|
+
(item) => Number(item.id ?? item.value) === categoryId
|
|
179
250
|
);
|
|
251
|
+
|
|
252
|
+
setExtraRetailers((current) => [
|
|
253
|
+
...current,
|
|
254
|
+
{
|
|
255
|
+
clave,
|
|
256
|
+
retailerId,
|
|
257
|
+
categoryId,
|
|
258
|
+
retailerName: retailer?.name ?? retailer?.label ?? `Cadena ${retailerId}`,
|
|
259
|
+
categoryName: category?.name ?? category?.label ?? `Categoría ${categoryId}`,
|
|
260
|
+
},
|
|
261
|
+
]);
|
|
262
|
+
setChosen((current) => ({
|
|
263
|
+
...current,
|
|
264
|
+
categoryRetailers: [...current.categoryRetailers, clave],
|
|
265
|
+
}));
|
|
266
|
+
setNewRetailer("");
|
|
267
|
+
setNewCategory("");
|
|
180
268
|
};
|
|
181
269
|
|
|
182
270
|
const submit = async () => {
|
|
183
271
|
setLoading(true);
|
|
184
272
|
setError(null);
|
|
185
273
|
|
|
274
|
+
// Las parejas cadena+categoría viajan como objetos, no como ids: pueden ser
|
|
275
|
+
// cadenas que el padre no tiene, y ahí no hay nada que "heredar" — se asigna.
|
|
276
|
+
const todasLasParejas = [
|
|
277
|
+
...offered.categoryRetailers.map((item) => ({
|
|
278
|
+
clave: `${item.retailerId}-${item.categoryId}`,
|
|
279
|
+
retailerId: item.retailerId,
|
|
280
|
+
categoryId: item.categoryId,
|
|
281
|
+
})),
|
|
282
|
+
...extraRetailers,
|
|
283
|
+
];
|
|
284
|
+
|
|
186
285
|
const inherit = {
|
|
187
|
-
attributes:
|
|
188
|
-
|
|
189
|
-
descriptions:
|
|
190
|
-
|
|
286
|
+
attributes:
|
|
287
|
+
inheritMode === "all-filled" ? "all-filled" : chosen.attributes,
|
|
288
|
+
descriptions: chosen.descriptions,
|
|
289
|
+
images: chosen.images,
|
|
290
|
+
categoryRetailers: todasLasParejas
|
|
291
|
+
.filter((par) => chosen.categoryRetailers.includes(par.clave))
|
|
292
|
+
.map(({ retailerId, categoryId }) => ({ retailerId, categoryId })),
|
|
191
293
|
};
|
|
192
294
|
|
|
193
295
|
const body =
|
|
@@ -197,7 +299,14 @@ export const LinkVariantsModal = ({
|
|
|
197
299
|
|
|
198
300
|
try {
|
|
199
301
|
const data = await request("link", body);
|
|
200
|
-
|
|
302
|
+
// El modo importa para quien escucha: en `children` este producto GANA
|
|
303
|
+
// variantes; en `parent` pasa a SER una. Son efectos opuestos sobre la
|
|
304
|
+
// misma ficha, así que quien refresque el estado necesita distinguirlos.
|
|
305
|
+
onLinked?.({
|
|
306
|
+
...data,
|
|
307
|
+
mode,
|
|
308
|
+
parentArticleId: mode === "parent" ? selected[0] : articleId,
|
|
309
|
+
});
|
|
201
310
|
onClose?.();
|
|
202
311
|
} catch (err) {
|
|
203
312
|
setError(readError(err));
|
|
@@ -314,7 +423,7 @@ export const LinkVariantsModal = ({
|
|
|
314
423
|
{step === "inherit" && (
|
|
315
424
|
<>
|
|
316
425
|
<Section>
|
|
317
|
-
<h3
|
|
426
|
+
<h3>Atributos de la ficha técnica</h3>
|
|
318
427
|
<label className="choice">
|
|
319
428
|
<input
|
|
320
429
|
type="radio"
|
|
@@ -322,9 +431,10 @@ export const LinkVariantsModal = ({
|
|
|
322
431
|
onChange={() => setInheritMode("all-filled")}
|
|
323
432
|
/>
|
|
324
433
|
<span>
|
|
325
|
-
Todos los
|
|
434
|
+
Todos los que tienen valor
|
|
326
435
|
<span className="hint">
|
|
327
|
-
{attributes.length} atributo(s) con información en
|
|
436
|
+
{offered.attributes.length} atributo(s) con información en
|
|
437
|
+
el padre.
|
|
328
438
|
</span>
|
|
329
439
|
</span>
|
|
330
440
|
</label>
|
|
@@ -352,13 +462,13 @@ export const LinkVariantsModal = ({
|
|
|
352
462
|
|
|
353
463
|
{inheritMode === "selected" && (
|
|
354
464
|
<AttributeList>
|
|
355
|
-
{attributes.map((attribute) => (
|
|
465
|
+
{offered.attributes.map((attribute) => (
|
|
356
466
|
<li key={attribute.id}>
|
|
357
467
|
<label>
|
|
358
468
|
<input
|
|
359
469
|
type="checkbox"
|
|
360
|
-
checked={
|
|
361
|
-
onChange={() =>
|
|
470
|
+
checked={chosen.attributes.includes(attribute.id)}
|
|
471
|
+
onChange={() => toggleChoice("attributes", attribute.id)}
|
|
362
472
|
/>
|
|
363
473
|
<span>
|
|
364
474
|
{attribute.name}
|
|
@@ -383,36 +493,220 @@ export const LinkVariantsModal = ({
|
|
|
383
493
|
)}
|
|
384
494
|
|
|
385
495
|
<Section>
|
|
386
|
-
<
|
|
387
|
-
|
|
388
|
-
|
|
389
|
-
|
|
390
|
-
|
|
391
|
-
|
|
392
|
-
|
|
393
|
-
|
|
394
|
-
|
|
395
|
-
|
|
396
|
-
|
|
397
|
-
|
|
398
|
-
|
|
399
|
-
|
|
400
|
-
|
|
401
|
-
|
|
402
|
-
|
|
403
|
-
|
|
404
|
-
|
|
405
|
-
|
|
406
|
-
|
|
407
|
-
|
|
408
|
-
|
|
409
|
-
|
|
496
|
+
<SectionHead>
|
|
497
|
+
<button
|
|
498
|
+
type="button"
|
|
499
|
+
className="toggle"
|
|
500
|
+
aria-expanded={openSection === "descriptions"}
|
|
501
|
+
onClick={() =>
|
|
502
|
+
setOpenSection(
|
|
503
|
+
openSection === "descriptions" ? null : "descriptions"
|
|
504
|
+
)
|
|
505
|
+
}
|
|
506
|
+
>
|
|
507
|
+
Descripciones
|
|
508
|
+
<span className="count">
|
|
509
|
+
{chosen.descriptions.length} de{" "}
|
|
510
|
+
{offered.descriptions.length}
|
|
511
|
+
</span>
|
|
512
|
+
</button>
|
|
513
|
+
{offered.descriptions.length > 0 && (
|
|
514
|
+
<button
|
|
515
|
+
type="button"
|
|
516
|
+
className="all"
|
|
517
|
+
onClick={() =>
|
|
518
|
+
toggleAll(
|
|
519
|
+
"descriptions",
|
|
520
|
+
offered.descriptions.map((item) => item.id)
|
|
521
|
+
)
|
|
522
|
+
}
|
|
523
|
+
>
|
|
524
|
+
{chosen.descriptions.length === offered.descriptions.length
|
|
525
|
+
? "Ninguna"
|
|
526
|
+
: "Todas"}
|
|
527
|
+
</button>
|
|
528
|
+
)}
|
|
529
|
+
</SectionHead>
|
|
530
|
+
|
|
531
|
+
{openSection === "descriptions" &&
|
|
532
|
+
(offered.descriptions.length ? (
|
|
533
|
+
<AttributeList>
|
|
534
|
+
{offered.descriptions.map((item) => (
|
|
535
|
+
<li key={item.id}>
|
|
536
|
+
<label>
|
|
537
|
+
<input
|
|
538
|
+
type="checkbox"
|
|
539
|
+
checked={chosen.descriptions.includes(item.id)}
|
|
540
|
+
onChange={() => toggleChoice("descriptions", item.id)}
|
|
541
|
+
/>
|
|
542
|
+
<span>{item.name}</span>
|
|
543
|
+
<span className="value" title={item.value}>
|
|
544
|
+
{item.value}
|
|
545
|
+
</span>
|
|
546
|
+
</label>
|
|
547
|
+
</li>
|
|
548
|
+
))}
|
|
549
|
+
</AttributeList>
|
|
550
|
+
) : (
|
|
551
|
+
<Message>El producto padre no tiene descripciones.</Message>
|
|
552
|
+
))}
|
|
553
|
+
</Section>
|
|
554
|
+
|
|
555
|
+
<Section>
|
|
556
|
+
<SectionHead>
|
|
557
|
+
<button
|
|
558
|
+
type="button"
|
|
559
|
+
className="toggle"
|
|
560
|
+
aria-expanded={openSection === "images"}
|
|
561
|
+
onClick={() =>
|
|
562
|
+
setOpenSection(openSection === "images" ? null : "images")
|
|
563
|
+
}
|
|
564
|
+
>
|
|
565
|
+
Imágenes
|
|
566
|
+
<span className="count">
|
|
567
|
+
{chosen.images.length} de {offered.images.length}
|
|
568
|
+
</span>
|
|
569
|
+
</button>
|
|
570
|
+
{offered.images.length > 0 && (
|
|
571
|
+
<button
|
|
572
|
+
type="button"
|
|
573
|
+
className="all"
|
|
574
|
+
onClick={() =>
|
|
575
|
+
toggleAll(
|
|
576
|
+
"images",
|
|
577
|
+
offered.images.map((item) => item.id)
|
|
578
|
+
)
|
|
579
|
+
}
|
|
580
|
+
>
|
|
581
|
+
{chosen.images.length === offered.images.length
|
|
582
|
+
? "Ninguna"
|
|
583
|
+
: "Todas"}
|
|
584
|
+
</button>
|
|
585
|
+
)}
|
|
586
|
+
</SectionHead>
|
|
587
|
+
|
|
588
|
+
{openSection === "images" &&
|
|
589
|
+
(offered.images.length ? (
|
|
590
|
+
<ImageGrid>
|
|
591
|
+
{offered.images.map((image) => (
|
|
592
|
+
<label
|
|
593
|
+
key={image.id}
|
|
594
|
+
className={
|
|
595
|
+
chosen.images.includes(image.id) ? "picked" : ""
|
|
596
|
+
}
|
|
597
|
+
>
|
|
598
|
+
<input
|
|
599
|
+
type="checkbox"
|
|
600
|
+
checked={chosen.images.includes(image.id)}
|
|
601
|
+
onChange={() => toggleChoice("images", image.id)}
|
|
602
|
+
/>
|
|
603
|
+
<img src={image.src} alt={image.imageType || ""} />
|
|
604
|
+
<span>{image.imageType || "Sin tipo"}</span>
|
|
605
|
+
</label>
|
|
606
|
+
))}
|
|
607
|
+
</ImageGrid>
|
|
608
|
+
) : (
|
|
609
|
+
<Message>El producto padre no tiene imágenes.</Message>
|
|
610
|
+
))}
|
|
611
|
+
</Section>
|
|
612
|
+
|
|
613
|
+
<Section>
|
|
614
|
+
<SectionHead>
|
|
615
|
+
<button
|
|
616
|
+
type="button"
|
|
617
|
+
className="toggle"
|
|
618
|
+
aria-expanded={openSection === "retailers"}
|
|
619
|
+
onClick={() =>
|
|
620
|
+
setOpenSection(
|
|
621
|
+
openSection === "retailers" ? null : "retailers"
|
|
622
|
+
)
|
|
623
|
+
}
|
|
624
|
+
>
|
|
410
625
|
Cadenas y categorías
|
|
411
|
-
<span className="
|
|
412
|
-
|
|
626
|
+
<span className="count">
|
|
627
|
+
{chosen.categoryRetailers.length} seleccionada(s)
|
|
413
628
|
</span>
|
|
414
|
-
</
|
|
415
|
-
</
|
|
629
|
+
</button>
|
|
630
|
+
</SectionHead>
|
|
631
|
+
|
|
632
|
+
{openSection === "retailers" && (
|
|
633
|
+
<>
|
|
634
|
+
<List>
|
|
635
|
+
{[
|
|
636
|
+
...offered.categoryRetailers.map((item) => ({
|
|
637
|
+
...item,
|
|
638
|
+
clave: `${item.retailerId}-${item.categoryId}`,
|
|
639
|
+
delPadre: true,
|
|
640
|
+
})),
|
|
641
|
+
...extraRetailers.map((item) => ({
|
|
642
|
+
...item,
|
|
643
|
+
delPadre: false,
|
|
644
|
+
})),
|
|
645
|
+
].map((item) => (
|
|
646
|
+
<li key={item.clave}>
|
|
647
|
+
<label>
|
|
648
|
+
<input
|
|
649
|
+
type="checkbox"
|
|
650
|
+
checked={chosen.categoryRetailers.includes(
|
|
651
|
+
item.clave
|
|
652
|
+
)}
|
|
653
|
+
onChange={() =>
|
|
654
|
+
toggleChoice("categoryRetailers", item.clave)
|
|
655
|
+
}
|
|
656
|
+
/>
|
|
657
|
+
<span>
|
|
658
|
+
{item.retailerName}
|
|
659
|
+
<span className="meta">
|
|
660
|
+
{item.categoryName || "Sin categoría"}
|
|
661
|
+
{!item.delPadre && " · no la tiene el padre"}
|
|
662
|
+
</span>
|
|
663
|
+
</span>
|
|
664
|
+
</label>
|
|
665
|
+
</li>
|
|
666
|
+
))}
|
|
667
|
+
</List>
|
|
668
|
+
|
|
669
|
+
{/* Agregar una cadena que el padre no tiene. No es herencia:
|
|
670
|
+
la variante entra a esa cadena por su cuenta. */}
|
|
671
|
+
<AddRetailer>
|
|
672
|
+
<select
|
|
673
|
+
value={newRetailer}
|
|
674
|
+
onChange={(event) => setNewRetailer(event.target.value)}
|
|
675
|
+
>
|
|
676
|
+
<option value="">Agregar otra cadena…</option>
|
|
677
|
+
{(retailersAvailable || []).map((item) => (
|
|
678
|
+
<option
|
|
679
|
+
key={item.id ?? item.value}
|
|
680
|
+
value={item.id ?? item.value}
|
|
681
|
+
>
|
|
682
|
+
{item.name ?? item.label}
|
|
683
|
+
</option>
|
|
684
|
+
))}
|
|
685
|
+
</select>
|
|
686
|
+
<select
|
|
687
|
+
value={newCategory}
|
|
688
|
+
onChange={(event) => setNewCategory(event.target.value)}
|
|
689
|
+
>
|
|
690
|
+
<option value="">Categoría…</option>
|
|
691
|
+
{(categoriesAvailable || []).map((item) => (
|
|
692
|
+
<option
|
|
693
|
+
key={item.id ?? item.value}
|
|
694
|
+
value={item.id ?? item.value}
|
|
695
|
+
>
|
|
696
|
+
{item.name ?? item.label}
|
|
697
|
+
</option>
|
|
698
|
+
))}
|
|
699
|
+
</select>
|
|
700
|
+
<button
|
|
701
|
+
type="button"
|
|
702
|
+
onClick={addRetailer}
|
|
703
|
+
disabled={!newRetailer || !newCategory}
|
|
704
|
+
>
|
|
705
|
+
Agregar
|
|
706
|
+
</button>
|
|
707
|
+
</AddRetailer>
|
|
708
|
+
</>
|
|
709
|
+
)}
|
|
416
710
|
</Section>
|
|
417
711
|
|
|
418
712
|
{/* Es la diferencia entre link y propagate, y es la que sorprende:
|
|
@@ -464,8 +758,8 @@ export const LinkVariantsModal = ({
|
|
|
464
758
|
// Con el padre sin atributos heredables, ligar sigue siendo
|
|
465
759
|
// válido: se heredan imágenes, descripciones y cadenas.
|
|
466
760
|
(inheritMode === "selected" &&
|
|
467
|
-
attributes.length > 0 &&
|
|
468
|
-
!
|
|
761
|
+
offered.attributes.length > 0 &&
|
|
762
|
+
!chosen.attributes.length)
|
|
469
763
|
}
|
|
470
764
|
>
|
|
471
765
|
{loading ? "Ligando…" : "Ligar"}
|
|
@@ -262,3 +262,123 @@ export const Footer = styled.div`
|
|
|
262
262
|
cursor: default;
|
|
263
263
|
}
|
|
264
264
|
`;
|
|
265
|
+
|
|
266
|
+
export const SectionHead = styled.div`
|
|
267
|
+
display: flex;
|
|
268
|
+
align-items: center;
|
|
269
|
+
gap: 8px;
|
|
270
|
+
margin-bottom: 6px;
|
|
271
|
+
|
|
272
|
+
.toggle {
|
|
273
|
+
flex: 1;
|
|
274
|
+
display: flex;
|
|
275
|
+
align-items: center;
|
|
276
|
+
gap: 8px;
|
|
277
|
+
border: none;
|
|
278
|
+
background: none;
|
|
279
|
+
padding: 0;
|
|
280
|
+
cursor: pointer;
|
|
281
|
+
font-family: ${FontFamily.Raleway_600};
|
|
282
|
+
font-size: 13px;
|
|
283
|
+
color: ${GlobalColors.deep_gray};
|
|
284
|
+
text-align: left;
|
|
285
|
+
}
|
|
286
|
+
|
|
287
|
+
.toggle .count {
|
|
288
|
+
font-family: ${FontFamily.Raleway_500};
|
|
289
|
+
font-size: 11px;
|
|
290
|
+
color: ${GlobalColors.gray};
|
|
291
|
+
}
|
|
292
|
+
|
|
293
|
+
.all {
|
|
294
|
+
border: none;
|
|
295
|
+
background: none;
|
|
296
|
+
padding: 0;
|
|
297
|
+
cursor: pointer;
|
|
298
|
+
font-family: ${FontFamily.Raleway_500};
|
|
299
|
+
font-size: 11px;
|
|
300
|
+
color: ${GlobalColors.original_magenta};
|
|
301
|
+
text-decoration: underline;
|
|
302
|
+
}
|
|
303
|
+
`;
|
|
304
|
+
|
|
305
|
+
export const ImageGrid = styled.div`
|
|
306
|
+
display: grid;
|
|
307
|
+
grid-template-columns: repeat(auto-fill, minmax(92px, 1fr));
|
|
308
|
+
gap: 8px;
|
|
309
|
+
margin-bottom: 16px;
|
|
310
|
+
|
|
311
|
+
label {
|
|
312
|
+
position: relative;
|
|
313
|
+
display: flex;
|
|
314
|
+
flex-direction: column;
|
|
315
|
+
gap: 4px;
|
|
316
|
+
padding: 6px;
|
|
317
|
+
border: 1px solid ${GlobalColors.s2};
|
|
318
|
+
border-radius: 6px;
|
|
319
|
+
cursor: pointer;
|
|
320
|
+
}
|
|
321
|
+
|
|
322
|
+
label.picked {
|
|
323
|
+
border-color: ${GlobalColors.original_magenta};
|
|
324
|
+
background-color: rgba(${GlobalColors.rgb_color_pink}, 0.06);
|
|
325
|
+
}
|
|
326
|
+
|
|
327
|
+
label input {
|
|
328
|
+
position: absolute;
|
|
329
|
+
top: 8px;
|
|
330
|
+
left: 8px;
|
|
331
|
+
}
|
|
332
|
+
|
|
333
|
+
label img {
|
|
334
|
+
width: 100%;
|
|
335
|
+
height: 72px;
|
|
336
|
+
object-fit: contain;
|
|
337
|
+
background-color: ${GlobalColors.s2};
|
|
338
|
+
border-radius: 4px;
|
|
339
|
+
}
|
|
340
|
+
|
|
341
|
+
label span {
|
|
342
|
+
font-size: 10px;
|
|
343
|
+
color: ${GlobalColors.gray};
|
|
344
|
+
text-align: center;
|
|
345
|
+
overflow: hidden;
|
|
346
|
+
text-overflow: ellipsis;
|
|
347
|
+
white-space: nowrap;
|
|
348
|
+
}
|
|
349
|
+
`;
|
|
350
|
+
|
|
351
|
+
export const AddRetailer = styled.div`
|
|
352
|
+
display: flex;
|
|
353
|
+
gap: 8px;
|
|
354
|
+
margin-bottom: 16px;
|
|
355
|
+
|
|
356
|
+
select {
|
|
357
|
+
flex: 1;
|
|
358
|
+
height: 32px;
|
|
359
|
+
padding: 0 8px;
|
|
360
|
+
border: 1px solid ${GlobalColors.s2};
|
|
361
|
+
border-radius: 6px;
|
|
362
|
+
font-family: ${FontFamily.Raleway};
|
|
363
|
+
font-size: 12px;
|
|
364
|
+
color: ${GlobalColors.deep_gray};
|
|
365
|
+
background-color: ${GlobalColors.white};
|
|
366
|
+
}
|
|
367
|
+
|
|
368
|
+
button {
|
|
369
|
+
height: 32px;
|
|
370
|
+
padding: 0 14px;
|
|
371
|
+
border: 1px solid ${GlobalColors.original_magenta};
|
|
372
|
+
border-radius: 6px;
|
|
373
|
+
background: none;
|
|
374
|
+
font-family: ${FontFamily.Raleway_600};
|
|
375
|
+
font-size: 12px;
|
|
376
|
+
color: ${GlobalColors.original_magenta};
|
|
377
|
+
cursor: pointer;
|
|
378
|
+
}
|
|
379
|
+
|
|
380
|
+
button:disabled {
|
|
381
|
+
opacity: 0.4;
|
|
382
|
+
cursor: default;
|
|
383
|
+
}
|
|
384
|
+
`;
|
package/src/components/pages/ProviderProductEdition/context/provider-product-edition.context.jsx
CHANGED
|
@@ -159,9 +159,22 @@ export const ProviderProductEditionProvider = ({ children }) => {
|
|
|
159
159
|
type: "SET_UPDATED_DATASHEETS_INPUTS",
|
|
160
160
|
payload: [],
|
|
161
161
|
});
|
|
162
|
+
// En una variante, todo atributo que el hijo toca deja de ser heredado:
|
|
163
|
+
// update-articles-data le pone is_overridden = 1 en la base. Si aquí solo
|
|
164
|
+
// se refresca el valor, el chip sigue diciendo "Heredado" hasta la
|
|
165
|
+
// siguiente carga de la ficha, y el usuario ve una marca que ya es falsa.
|
|
166
|
+
const esVariante =
|
|
167
|
+
state.services?.datasheets?.variant?.isVariant ??
|
|
168
|
+
!!state.product?.parentArticleId;
|
|
169
|
+
|
|
162
170
|
dispatch({
|
|
163
171
|
type: "UPDATE_DATASHEETS_INPUTS",
|
|
164
|
-
payload:
|
|
172
|
+
payload: esVariante
|
|
173
|
+
? state.updated_datasheets_inputs.map((item) => ({
|
|
174
|
+
...item,
|
|
175
|
+
isOverridden: 1,
|
|
176
|
+
}))
|
|
177
|
+
: state.updated_datasheets_inputs,
|
|
165
178
|
});
|
|
166
179
|
|
|
167
180
|
// Mostrar modal de éxito
|