contentoh-components-library 21.6.27 → 21.6.28

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.
@@ -9,7 +9,11 @@ import {
9
9
  useProviderProductEdition,
10
10
  } from "./context/provider-product-edition.context";
11
11
  import { normalizeProduct, getConceptByTab } from "./utils";
12
- import { getMinorStatus, getMinorStatusFromRetailerMap, STATUS_DICTIONARY } from "../../../global-files/statusDictionary";
12
+ import {
13
+ getMinorStatus,
14
+ getMinorStatusFromRetailerMap,
15
+ STATUS_DICTIONARY,
16
+ } from "../../../global-files/statusDictionary";
13
17
 
14
18
  import {
15
19
  getPercentage,
@@ -35,6 +39,7 @@ import { ImageDataTable } from "../../organisms/ImageDataTable";
35
39
  import { ImagePreviewer } from "../../organisms/ImagePreviewer";
36
40
  import { InputGroup } from "../../organisms/InputGroup";
37
41
  import { LinkVariantsModal } from "../../organisms/LinkVariantsModal";
42
+ import { PropagateChangesModal } from "../../organisms/PropagateChangesModal";
38
43
  import { readPayload } from "../../organisms/LinkVariantsModal/api";
39
44
  import { ProductImageModal } from "../../organisms/ProductImageModal";
40
45
  import { VersionSelector } from "../../organisms/VersionSelector";
@@ -79,6 +84,8 @@ const ProviderProductEditionView = ({
79
84
  const [family, setFamily] = useState(null);
80
85
  const [showFamily, setShowFamily] = useState(false);
81
86
  const [loadingVariant, setLoadingVariant] = useState(null);
87
+ const [pendingPropagation, setPendingPropagation] = useState(null);
88
+ const [preparandoPropagacion, setPreparandoPropagacion] = useState(false);
82
89
  const [showRejectModal, setShowRejectModal] = useState(false);
83
90
  const [headerTop, setHeaderTop] = useState(0);
84
91
  const [assig, setAssig] = useState({});
@@ -233,7 +240,7 @@ const ProviderProductEditionView = ({
233
240
  id_category,
234
241
  version,
235
242
  token,
236
- id_retailer,
243
+ id_retailer
237
244
  ),
238
245
  getPercentage({ data, headers }),
239
246
  getServicesData({
@@ -245,11 +252,10 @@ const ProviderProductEditionView = ({
245
252
  ]);
246
253
 
247
254
  const percentageRaw = percentageRes?.[0];
248
- const percentages = (
249
- Array.isArray(percentageRaw)
255
+ const percentages =
256
+ (Array.isArray(percentageRaw)
250
257
  ? percentageRaw[0]?.relations
251
- : JSON.parse(percentageRaw?.body ?? "null")?.[0]?.relations
252
- ) ?? [];
258
+ : JSON.parse(percentageRaw?.body ?? "null")?.[0]?.relations) ?? [];
253
259
 
254
260
  const orderMap =
255
261
  services[2].inputsByRetailer ??
@@ -277,12 +283,16 @@ const ProviderProductEditionView = ({
277
283
 
278
284
  return acc;
279
285
  },
280
- {},
286
+ {}
281
287
  );
282
288
 
283
289
  const getServiceMinorStatus = (serviceName) => {
284
- const records = servicesDataRes.filter((s) => s.service === serviceName);
285
- return records.length > 0 ? getMinorStatus(records.map((s) => s.status)) : undefined;
290
+ const records = servicesDataRes.filter(
291
+ (s) => s.service === serviceName
292
+ );
293
+ return records.length > 0
294
+ ? getMinorStatus(records.map((s) => s.status))
295
+ : undefined;
286
296
  };
287
297
 
288
298
  const imagesMinorStatus = getServiceMinorStatus("images");
@@ -295,9 +305,15 @@ const ProviderProductEditionView = ({
295
305
  ...state.product,
296
306
  statusByRetailer: updatedStatusByRetailer,
297
307
  status: getMinorStatusFromRetailerMap(updatedStatusByRetailer),
298
- ...(imagesMinorStatus !== undefined && { images_status: imagesMinorStatus }),
299
- ...(descriptionMinorStatus !== undefined && { description_status: descriptionMinorStatus }),
300
- ...(datasheetMinorStatus !== undefined && { datasheet_status: datasheetMinorStatus }),
308
+ ...(imagesMinorStatus !== undefined && {
309
+ images_status: imagesMinorStatus,
310
+ }),
311
+ ...(descriptionMinorStatus !== undefined && {
312
+ description_status: descriptionMinorStatus,
313
+ }),
314
+ ...(datasheetMinorStatus !== undefined && {
315
+ datasheet_status: datasheetMinorStatus,
316
+ }),
301
317
  },
302
318
  });
303
319
 
@@ -320,7 +336,7 @@ const ProviderProductEditionView = ({
320
336
  const activePercentage =
321
337
  percentages.find(({ id_retailer: rId }) => rId === id_retailer) ||
322
338
  state.product.percentages?.find(
323
- ({ id_retailer: rId }) => rId === id_retailer,
339
+ ({ id_retailer: rId }) => rId === id_retailer
324
340
  );
325
341
 
326
342
  dispatch({
@@ -364,7 +380,10 @@ const ProviderProductEditionView = ({
364
380
  };
365
381
  dispatch({
366
382
  type: "SET_DATASHEETS_INPUTS",
367
- payload: [datasheetsActiveRetailer, state.services.datasheets.inputs],
383
+ payload: [
384
+ datasheetsActiveRetailer,
385
+ state.services.datasheets.inputs,
386
+ ],
368
387
  });
369
388
  } else {
370
389
  dispatch({ type: "SET_DATASHEETS_INPUTS", payload: [] });
@@ -373,7 +392,7 @@ const ProviderProductEditionView = ({
373
392
 
374
393
  if (state.services.descriptions) {
375
394
  const descriptionsActiveRetailer = state.services.descriptions.filter(
376
- (item) => item.id === state.active_retailer.id_retailer,
395
+ (item) => item.id === state.active_retailer.id_retailer
377
396
  );
378
397
 
379
398
  dispatch({
@@ -388,8 +407,8 @@ const ProviderProductEditionView = ({
388
407
  retailerInput.some(
389
408
  (input) =>
390
409
  input.id_retailer === state.active_retailer.id_retailer &&
391
- input.id_image === value.image_id,
392
- ),
410
+ input.id_image === value.image_id
411
+ )
393
412
  );
394
413
  });
395
414
 
@@ -419,7 +438,7 @@ const ProviderProductEditionView = ({
419
438
  const { status: currentStatus } = state.services_data.find(
420
439
  (service) =>
421
440
  service.service === currentService &&
422
- service.id_retailer === currentRetailer,
441
+ service.id_retailer === currentRetailer
423
442
  );
424
443
 
425
444
  const originTab = origin[state.active_tab];
@@ -488,7 +507,7 @@ const ProviderProductEditionView = ({
488
507
  name: e?.name,
489
508
  required: e?.required,
490
509
  active: state.images_values?.values?.some(
491
- (value) => value?.image_id === e?.id,
510
+ (value) => value?.image_id === e?.id
492
511
  ),
493
512
  }));
494
513
  const imageType = state.images_values?.imageType?.map((e) => ({
@@ -499,7 +518,7 @@ const ProviderProductEditionView = ({
499
518
  (e) => ({
500
519
  value: e?.id,
501
520
  name: e?.name,
502
- }),
521
+ })
503
522
  );
504
523
 
505
524
  return state.images_values?.values?.map((image, index) => (
@@ -658,15 +677,56 @@ const ProviderProductEditionView = ({
658
677
  });
659
678
  };
660
679
 
680
+ /*
681
+ Los atributos que se acaban de tocar, con el nombre que ve el usuario.
682
+
683
+ `updated_datasheets_inputs` trae { attributeId, value } y nada más, así que el
684
+ nombre se busca en los inputs de la cadena activa: un diálogo que preguntara
685
+ por "el atributo 1023" no serviría para decidir nada.
686
+ */
687
+ const cambiosDeFichaTecnica = () => {
688
+ const retailerId = state.active_retailer?.id_retailer;
689
+ const inputs = state.services?.datasheets?.inputs ?? {};
690
+
691
+ return (state.updated_datasheets_inputs ?? []).map((item) => {
692
+ const input = inputs[`${item.attributeId}_${retailerId}`];
693
+ return {
694
+ id: Number(item.attributeId),
695
+ name: input?.name ?? `Atributo ${item.attributeId}`,
696
+ value: item.value,
697
+ };
698
+ });
699
+ };
700
+
661
701
  const handleOnClickSave = () => {
662
- if (state.saving) return;
702
+ // `preparandoPropagacion` cubre la ventana entre el clic y la apertura del
703
+ // diálogo: sin esto, dos clics seguidos lanzan dos consultas de familia.
704
+ if (state.saving || preparandoPropagacion) return;
663
705
  switch (state.active_tab) {
664
706
  case "Descripción":
665
707
  saveDescriptions(token);
666
708
  break;
667
- case "Ficha técnica":
709
+ case "Ficha técnica": {
710
+ // Guardar un padre con variantes abre la decisión de propagar. Sin este
711
+ // paso el cambio se queda solo en el padre y las variantes se van
712
+ // separando de él sin que nadie lo note.
713
+ const esPadre = state.product?.variantsCount > 0;
714
+ const cambios = cambiosDeFichaTecnica();
715
+
716
+ if (esPadre && cambios.length > 0) {
717
+ // Se espera a tener la familia antes de abrir: si no, el diálogo
718
+ // aparece diciendo "0 variantes" hasta que llega la respuesta, que es
719
+ // justo lo contrario de lo que va a preguntar.
720
+ setPreparandoPropagacion(true);
721
+ loadFamily()
722
+ .then(() => setPendingPropagation({ cambios }))
723
+ .finally(() => setPreparandoPropagacion(false));
724
+ return;
725
+ }
726
+
668
727
  saveDatasheets(token);
669
728
  break;
729
+ }
670
730
  case "Imágenes":
671
731
  updateImages(token);
672
732
  break;
@@ -709,7 +769,7 @@ const ProviderProductEditionView = ({
709
769
  peso que paga todo el mundo por el caso de unos pocos.
710
770
  */
711
771
  const loadFamily = async () => {
712
- if (family) return;
772
+ if (family) return family;
713
773
  try {
714
774
  const res = await axios.post(
715
775
  `${process.env.REACT_APP_VARIANTS_ENDPOINT}/family`,
@@ -853,7 +913,10 @@ const ProviderProductEditionView = ({
853
913
  try {
854
914
  const res = await axios.post(
855
915
  `${process.env.REACT_APP_VARIANTS_ENDPOINT}/restore`,
856
- { articleId: state.product.id_article, attributes: [Number(attributeId)] },
916
+ {
917
+ articleId: state.product.id_article,
918
+ attributes: [Number(attributeId)],
919
+ },
857
920
  { headers: { Authorization: token } }
858
921
  );
859
922
 
@@ -929,7 +992,7 @@ const ProviderProductEditionView = ({
929
992
  headers: {
930
993
  Authorization: token,
931
994
  },
932
- },
995
+ }
933
996
  );
934
997
 
935
998
  const newStatuses = JSON.parse(res.data.body);
@@ -1180,10 +1243,13 @@ const ProviderProductEditionView = ({
1180
1243
  const isProvider = !user.is_retailer;
1181
1244
  state.services_data?.forEach((ret) => {
1182
1245
  const { service, id_retailer } = ret;
1183
- const currentEvalStatus = state.product.statusByRetailer[id_retailer]?.[service];
1246
+ const currentEvalStatus =
1247
+ state.product.statusByRetailer[id_retailer]?.[service];
1184
1248
 
1185
- if (isProvider && currentEvalStatus !== STATUS_DICTIONARY.AA.status) return;
1186
- if (!isProvider && currentEvalStatus !== STATUS_DICTIONARY.AP.status) return;
1249
+ if (isProvider && currentEvalStatus !== STATUS_DICTIONARY.AA.status)
1250
+ return;
1251
+ if (!isProvider && currentEvalStatus !== STATUS_DICTIONARY.AP.status)
1252
+ return;
1187
1253
 
1188
1254
  const data = {
1189
1255
  ...dataGeneral,
@@ -1196,7 +1262,7 @@ const ProviderProductEditionView = ({
1196
1262
  headers: {
1197
1263
  Authorization: token,
1198
1264
  },
1199
- }),
1265
+ })
1200
1266
  );
1201
1267
  });
1202
1268
 
@@ -1244,7 +1310,7 @@ const ProviderProductEditionView = ({
1244
1310
  ArticleId: updatedProduct.id_article,
1245
1311
  idCategory: updatedProduct.id_category,
1246
1312
  product: updatedProduct,
1247
- }),
1313
+ })
1248
1314
  );
1249
1315
 
1250
1316
  // Mostrar modal de éxito
@@ -1323,6 +1389,18 @@ const ProviderProductEditionView = ({
1323
1389
  jwt={token}
1324
1390
  />
1325
1391
  )}
1392
+ {pendingPropagation && (
1393
+ <PropagateChangesModal
1394
+ changedAttributes={pendingPropagation.cambios}
1395
+ variants={(family?.variants ?? []).filter((v) => !v.isCurrent)}
1396
+ saving={state.saving}
1397
+ onCancel={() => setPendingPropagation(null)}
1398
+ onConfirm={(propagation) => {
1399
+ setPendingPropagation(null);
1400
+ saveDatasheets(token, propagation ?? undefined);
1401
+ }}
1402
+ />
1403
+ )}
1326
1404
  {showLinkVariants && (
1327
1405
  <LinkVariantsModal
1328
1406
  articleId={state.product?.id_article}
@@ -1418,7 +1496,7 @@ const ProviderProductEditionView = ({
1418
1496
  size={12}
1419
1497
  onClick={async () => {
1420
1498
  const elements = document.querySelectorAll(
1421
- "#modal-message-box .ql-container .ql-editor > p",
1499
+ "#modal-message-box .ql-container .ql-editor > p"
1422
1500
  );
1423
1501
 
1424
1502
  if (!elements || elements.length === 0) {
@@ -1435,7 +1513,7 @@ const ProviderProductEditionView = ({
1435
1513
 
1436
1514
  if (isMessageEmpty) {
1437
1515
  const container = document.querySelector(
1438
- ".container-customComponent",
1516
+ ".container-customComponent"
1439
1517
  );
1440
1518
  const existingAlert =
1441
1519
  container.querySelector(".alert-error");
@@ -1601,7 +1679,9 @@ const ProviderProductEditionView = ({
1601
1679
  <button
1602
1680
  key={variant.articleId}
1603
1681
  type="button"
1604
- className={`family-row${variant.isCurrent ? " current" : ""}`}
1682
+ className={`family-row${
1683
+ variant.isCurrent ? " current" : ""
1684
+ }`}
1605
1685
  disabled={variant.isCurrent || loadingVariant !== null}
1606
1686
  onClick={() => openVariant(variant.articleId)}
1607
1687
  >
@@ -1754,7 +1834,7 @@ const ProviderProductEditionView = ({
1754
1834
  handleOnUpdateApprovedInputs
1755
1835
  }
1756
1836
  />
1757
- ),
1837
+ )
1758
1838
  )
1759
1839
  ) : (
1760
1840
  <ScreenHeader
package/src/index.js CHANGED
@@ -88,6 +88,7 @@ export * from "./components/organisms/ImagePreviewer/index";
88
88
  export * from "./components/organisms/ImagesGroup/index";
89
89
  export * from "./components/organisms/InputGroup/index";
90
90
  export * from "./components/organisms/LinkVariantsModal/index";
91
+ export * from "./components/organisms/PropagateChangesModal/index";
91
92
  export * from "./components/organisms/ProductImageModal/index";
92
93
  export * from "./components/organisms/Chat/index";
93
94
  export * from "./components/organisms/Modal/index";