contentoh-components-library 21.6.49 → 21.6.50

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.
Files changed (90) hide show
  1. package/.claude/launch.json +14 -0
  2. package/.env.test +1 -0
  3. package/dist/ai/prompts/attribute.prompt.js +28 -0
  4. package/dist/ai/prompts/description.prompt.js +33 -0
  5. package/dist/ai/schemas/attribute.schema.js +35 -0
  6. package/dist/ai/schemas/description.schema.js +27 -0
  7. package/dist/components/atoms/AssetPreview/AssetPreview.stories.js +118 -0
  8. package/dist/components/atoms/AssetPreview/index.js +150 -0
  9. package/dist/components/atoms/AssetPreview/styles.js +28 -0
  10. package/dist/components/atoms/IconFile/index.js +85 -2
  11. package/dist/components/atoms/IconFile/styles.js +1 -1
  12. package/dist/components/atoms/ProductImage/ProductImage.stories.js +70 -2
  13. package/dist/components/atoms/ProductImage/index.js +100 -27
  14. package/dist/components/atoms/ProductImage/styles.js +3 -1
  15. package/dist/components/molecules/EditionActiveImage/index.js +4 -2
  16. package/dist/components/molecules/GalleryAssetElement/GalleryAssetElement.stories.js +147 -0
  17. package/dist/components/molecules/GalleryAssetElement/index.js +178 -0
  18. package/dist/components/molecules/GalleryAssetElement/styles.js +26 -0
  19. package/dist/components/molecules/GalleryElement/index.js +13 -4
  20. package/dist/components/molecules/GalleryHeader/index.js +96 -24
  21. package/dist/components/molecules/GalleryHeader/styles.js +4 -1
  22. package/dist/components/molecules/GalleryTrayTabs/GalleryTrayTabs.stories.js +56 -0
  23. package/dist/components/molecules/GalleryTrayTabs/index.js +60 -0
  24. package/dist/components/molecules/GalleryTrayTabs/styles.js +20 -0
  25. package/dist/components/molecules/ImageSelector/index.js +1 -0
  26. package/dist/components/organisms/DigitalAssetsTray/DigitalAssetsTray.stories.js +171 -0
  27. package/dist/components/organisms/DigitalAssetsTray/index.js +211 -0
  28. package/dist/components/organisms/DigitalAssetsTray/styles.js +22 -0
  29. package/dist/components/organisms/ImagePreviewer/index.js +1 -0
  30. package/dist/components/organisms/ProductImageModal/index.js +3 -2
  31. package/dist/components/organisms/PropagateChangesModal/index.js +12 -1
  32. package/dist/components/organisms/PropagateChangesModal/non-propagable.js +45 -0
  33. package/dist/components/pages/ProviderProductEdition/index.js +11 -1
  34. package/dist/components/pages/RetailerProductEdition/context/provider-product-edition.context.js +330 -15
  35. package/dist/components/pages/RetailerProductEdition/context/provider-product-edition.reducer.js +12 -2
  36. package/dist/components/pages/RetailerProductEdition/index.js +605 -414
  37. package/dist/components/pages/RetailerProductEdition/stories/ImageEditor.stories.js +1 -0
  38. package/dist/components/pages/RetailerProductEdition/stories/TextEditor.stories.js +45 -145
  39. package/dist/global-files/data.js +16 -3
  40. package/dist/global-files/digital-assets/assetTypes.js +480 -0
  41. package/dist/global-files/digital-assets/assets.actions.js +64 -0
  42. package/dist/global-files/digital-assets/assets.adapter.js +304 -0
  43. package/dist/global-files/digital-assets/assets.mock.js +136 -0
  44. package/dist/global-files/digital-assets/assets.reducer.js +220 -0
  45. package/dist/global-files/digital-assets/uploadAssets.js +179 -0
  46. package/dist/global-files/digital-assets/useDigitalAssets.js +522 -0
  47. package/dist/index.js +142 -90
  48. package/docs/activos-digitales.md +273 -0
  49. package/package.json +1 -1
  50. package/src/components/atoms/AssetPreview/AssetPreview.stories.js +78 -0
  51. package/src/components/atoms/AssetPreview/index.js +119 -0
  52. package/src/components/atoms/AssetPreview/styles.js +190 -0
  53. package/src/components/atoms/IconFile/index.js +28 -1
  54. package/src/components/atoms/IconFile/styles.js +8 -0
  55. package/src/components/atoms/ProductImage/ProductImage.stories.js +38 -0
  56. package/src/components/atoms/ProductImage/index.js +95 -20
  57. package/src/components/atoms/ProductImage/styles.js +92 -7
  58. package/src/components/molecules/EditionActiveImage/index.js +2 -2
  59. package/src/components/molecules/GalleryAssetElement/GalleryAssetElement.stories.js +102 -0
  60. package/src/components/molecules/GalleryAssetElement/index.js +162 -0
  61. package/src/components/molecules/GalleryAssetElement/styles.js +159 -0
  62. package/src/components/molecules/GalleryElement/index.js +9 -0
  63. package/src/components/molecules/GalleryHeader/index.js +65 -13
  64. package/src/components/molecules/GalleryHeader/styles.js +2 -1
  65. package/src/components/molecules/GalleryTrayTabs/GalleryTrayTabs.stories.js +32 -0
  66. package/src/components/molecules/GalleryTrayTabs/index.js +41 -0
  67. package/src/components/molecules/GalleryTrayTabs/styles.js +52 -0
  68. package/src/components/molecules/ImageSelector/index.js +1 -0
  69. package/src/components/organisms/DigitalAssetsTray/DigitalAssetsTray.stories.js +101 -0
  70. package/src/components/organisms/DigitalAssetsTray/index.js +189 -0
  71. package/src/components/organisms/DigitalAssetsTray/styles.js +183 -0
  72. package/src/components/organisms/ImagePreviewer/index.js +1 -0
  73. package/src/components/organisms/ProductImageModal/index.js +1 -0
  74. package/src/components/organisms/PropagateChangesModal/index.js +11 -1
  75. package/src/components/organisms/PropagateChangesModal/non-propagable.js +29 -0
  76. package/src/components/pages/ProviderProductEdition/index.js +10 -1
  77. package/src/components/pages/RetailerProductEdition/context/provider-product-edition.context.jsx +201 -0
  78. package/src/components/pages/RetailerProductEdition/context/provider-product-edition.reducer.js +12 -0
  79. package/src/components/pages/RetailerProductEdition/index.js +366 -140
  80. package/src/components/pages/RetailerProductEdition/stories/ImageEditor.stories.js +1 -0
  81. package/src/components/pages/RetailerProductEdition/stories/TextEditor.stories.js +51 -104
  82. package/src/global-files/data.js +17 -2
  83. package/src/global-files/digital-assets/assetTypes.js +419 -0
  84. package/src/global-files/digital-assets/assets.actions.js +50 -0
  85. package/src/global-files/digital-assets/assets.adapter.js +229 -0
  86. package/src/global-files/digital-assets/assets.mock.js +122 -0
  87. package/src/global-files/digital-assets/assets.reducer.js +197 -0
  88. package/src/global-files/digital-assets/uploadAssets.js +109 -0
  89. package/src/global-files/digital-assets/useDigitalAssets.js +329 -0
  90. package/src/index.js +4 -0
@@ -0,0 +1,189 @@
1
+ import { useDropzone } from "react-dropzone";
2
+
3
+ import { Container } from "./styles";
4
+ import { GalleryAssetElement } from "../../molecules/GalleryAssetElement";
5
+ import { GalleryHeader } from "../../molecules/GalleryHeader";
6
+ import { ButtonV2 } from "../../atoms/ButtonV2";
7
+ import {
8
+ MAX_ASSETS_PER_PRODUCT,
9
+ acceptedFormatsLabel,
10
+ buildDropzoneAccept,
11
+ } from "../../../global-files/digital-assets/assetTypes";
12
+
13
+ const CHECK_ALL_ID = "check-all-assets";
14
+ const ITEM_SELECTOR = "[id^='gallery-asset-']";
15
+
16
+ /**
17
+ * Bandeja de activos digitales: soltar archivos, verlos, reetiquetarlos y
18
+ * quitarlos.
19
+ *
20
+ * Tiene su propio dropzone en vez de compartir el de las imágenes porque los
21
+ * formatos aceptados son distintos: mezclarlos dejaría soltar un PDF sobre la
22
+ * bandeja de fotos y un JPG sobre la de activos.
23
+ *
24
+ * No guarda ni sube nada. Recibe el estado ya armado por `useDigitalAssets`,
25
+ * así que cuando se defina el contrato del backend solo hay que conectarle las
26
+ * llamadas; la interfaz no cambia.
27
+ */
28
+ export const DigitalAssetsTray = ({
29
+ assets = [],
30
+ notices = [],
31
+ unsaved = { nuevos: 0, modificados: 0, borrados: 0, total: 0 },
32
+ remaining = MAX_ASSETS_PER_PRODUCT,
33
+ addFiles,
34
+ removeAsset,
35
+ changeAssetType,
36
+ clearNotices,
37
+ selectedAssets = [],
38
+ setSelectedAssets,
39
+ setCheckAll,
40
+ gridLayout = false,
41
+ readOnly = false,
42
+ emptyMessage = "Este producto no tiene activos digitales",
43
+ }) => {
44
+ const { getRootProps, getInputProps, isDragActive, open } = useDropzone({
45
+ accept: buildDropzoneAccept(),
46
+ noKeyboard: true,
47
+ multiple: true,
48
+ noClick: true,
49
+ disabled: readOnly,
50
+ onDrop: (acceptedFiles) => addFiles?.(acceptedFiles),
51
+ });
52
+
53
+ const noticesBy = (level) => notices.filter((item) => item.level === level);
54
+
55
+ /*
56
+ Se desglosa en vez de mostrar solo el número: "3 cambios sin guardar" no le
57
+ dice al usuario qué perdería si se va, y no es lo mismo dejar sin guardar un
58
+ borrado que un archivo nuevo.
59
+ */
60
+ const detalleSinGuardar = [
61
+ unsaved.nuevos && `${unsaved.nuevos} nuevo${unsaved.nuevos > 1 ? "s" : ""}`,
62
+ unsaved.modificados && `${unsaved.modificados} modificado${unsaved.modificados > 1 ? "s" : ""}`,
63
+ unsaved.borrados && `${unsaved.borrados} por borrar`,
64
+ ].filter(Boolean).join(" · ");
65
+
66
+ return (
67
+ <Container>
68
+ <div className="tray-toolbar">
69
+ {/* La lista completa son 27 formatos: no cabe en una línea, así que va
70
+ en el title y en el texto solo las familias. */}
71
+ <p className="tray-hint" title={`Formatos permitidos: ${acceptedFormatsLabel()}`}>
72
+ Arrastra aquí videos, 360, GIF, documentos, hojas de cálculo o archivos
73
+ editables. Quedan {remaining} de {MAX_ASSETS_PER_PRODUCT}.
74
+ </p>
75
+ {!readOnly && (
76
+ <ButtonV2
77
+ label={"Agregar activos"}
78
+ type={"pink"}
79
+ size={12}
80
+ onClick={open}
81
+ />
82
+ )}
83
+ </div>
84
+
85
+ {/*
86
+ Rechazos y advertencias se separan en dos paneles.
87
+
88
+ Mezclarlos en rojo haría creer que un archivo que sí entró tampoco
89
+ sirvió: un .mov se guarda y se descarga bien, solo que el navegador no
90
+ lo reproduce. Son mensajes distintos y merecen colores distintos.
91
+ */}
92
+ {unsaved.total > 0 && (
93
+ <div className="tray-unsaved" role="status">
94
+ <span className="tray-unsaved-dot" />
95
+ <p>
96
+ <strong>
97
+ {unsaved.total} cambio{unsaved.total > 1 ? "s" : ""} sin guardar
98
+ </strong>
99
+ {detalleSinGuardar && <span className="tray-unsaved-detail"> — {detalleSinGuardar}</span>}
100
+ </p>
101
+ </div>
102
+ )}
103
+
104
+ {noticesBy("error").length > 0 && (
105
+ <div className="tray-notice tray-notice-error">
106
+ <div className="tray-notice-head">
107
+ <p>No se pudieron agregar {noticesBy("error").length} archivo(s):</p>
108
+ <button type="button" onClick={clearNotices} title="Cerrar">
109
+ ×
110
+ </button>
111
+ </div>
112
+ <ul>
113
+ {noticesBy("error").map((item, index) => (
114
+ <li key={`err-${item.name}-${index}`}>{item.message}</li>
115
+ ))}
116
+ </ul>
117
+ </div>
118
+ )}
119
+
120
+ {noticesBy("warning").length > 0 && (
121
+ <div className="tray-notice tray-notice-warning">
122
+ <div className="tray-notice-head">
123
+ <p>Se agregaron, pero ten en cuenta:</p>
124
+ <button type="button" onClick={clearNotices} title="Cerrar">
125
+ ×
126
+ </button>
127
+ </div>
128
+ <ul>
129
+ {noticesBy("warning").map((item, index) => (
130
+ <li key={`warn-${item.name}-${index}`}>{item.message}</li>
131
+ ))}
132
+ </ul>
133
+ </div>
134
+ )}
135
+
136
+ {!gridLayout && assets.length > 0 && (
137
+ <GalleryHeader
138
+ variant="asset"
139
+ checkAllId={CHECK_ALL_ID}
140
+ itemSelector={ITEM_SELECTOR}
141
+ setCheckAll={setCheckAll}
142
+ setSelectedImages={setSelectedAssets}
143
+ />
144
+ )}
145
+
146
+ <div
147
+ {...getRootProps({
148
+ className: `dropzone ${isDragActive ? "drag-active" : ""}`.trim(),
149
+ })}
150
+ >
151
+ <input {...getInputProps()} />
152
+ {isDragActive && (
153
+ <div className="drag-overlay">
154
+ <p>Suelta los activos aquí</p>
155
+ </div>
156
+ )}
157
+ <aside className={gridLayout ? "tray-grid" : "tray-rows"}>
158
+ {assets.map((asset, index) => (
159
+ <GalleryAssetElement
160
+ key={asset.id ?? asset.tempId}
161
+ asset={asset}
162
+ index={index}
163
+ gridLayout={gridLayout}
164
+ checkAllId={CHECK_ALL_ID}
165
+ itemSelector={ITEM_SELECTOR}
166
+ selectedAssets={selectedAssets}
167
+ setSelectedAssets={setSelectedAssets}
168
+ setCheckAll={setCheckAll}
169
+ changeAsset={({ value }) => changeAssetType?.(index, value)}
170
+ onRemove={removeAsset}
171
+ readOnly={readOnly}
172
+ />
173
+ ))}
174
+ </aside>
175
+
176
+ {assets.length === 0 && (
177
+ <div className="tray-empty">
178
+ <p>{emptyMessage}</p>
179
+ {!readOnly && (
180
+ <p className="tray-empty-hint">
181
+ Arrástralos aquí o usa «Agregar activos».
182
+ </p>
183
+ )}
184
+ </div>
185
+ )}
186
+ </div>
187
+ </Container>
188
+ );
189
+ };
@@ -0,0 +1,183 @@
1
+ import styled from "styled-components";
2
+ import { FontFamily, GlobalColors } from "../../../global-files/variables";
3
+
4
+ /* El dropzone replica el de la bandeja de imágenes para que arrastrar se sienta
5
+ igual en las dos. */
6
+ export const Container = styled.div`
7
+ width: 100%;
8
+ display: flex;
9
+ flex-direction: column;
10
+ gap: 8px;
11
+
12
+ .tray-toolbar {
13
+ display: flex;
14
+ align-items: center;
15
+ justify-content: space-between;
16
+ gap: 12px;
17
+ }
18
+
19
+ .tray-hint {
20
+ margin: 0;
21
+ font-family: ${FontFamily.Lato};
22
+ font-size: 12px;
23
+ line-height: 18px;
24
+ color: ${GlobalColors.s4};
25
+ }
26
+
27
+ /*
28
+ Aviso de cambios sin guardar. Va arriba del listado y no junto al botón de
29
+ guardar, que vive en otra barra: el usuario mira la bandeja, no el menú.
30
+ */
31
+ .tray-unsaved {
32
+ display: flex;
33
+ align-items: center;
34
+ gap: 8px;
35
+ padding: 6px 12px;
36
+ border-radius: 6px;
37
+ border: 1px solid ${GlobalColors.in_progress};
38
+ background: rgba(237, 154, 77, 0.08);
39
+
40
+ p {
41
+ margin: 0;
42
+ font-family: ${FontFamily.Lato};
43
+ font-size: 12px;
44
+ line-height: 18px;
45
+ color: ${GlobalColors.s5};
46
+ }
47
+
48
+ strong {
49
+ font-family: ${FontFamily.Raleway_600};
50
+ color: ${GlobalColors.in_progress};
51
+ }
52
+ }
53
+
54
+ .tray-unsaved-dot {
55
+ width: 8px;
56
+ height: 8px;
57
+ border-radius: 50%;
58
+ background: ${GlobalColors.in_progress};
59
+ flex-shrink: 0;
60
+ }
61
+
62
+ .tray-unsaved-detail {
63
+ color: ${GlobalColors.s4};
64
+ }
65
+
66
+ .tray-notice {
67
+ border-radius: 6px;
68
+ padding: 8px 12px;
69
+ border: 1px solid;
70
+
71
+ .tray-notice-head {
72
+ display: flex;
73
+ align-items: center;
74
+ justify-content: space-between;
75
+
76
+ p {
77
+ margin: 0;
78
+ font-family: ${FontFamily.Raleway_600};
79
+ font-size: 12px;
80
+ }
81
+
82
+ button {
83
+ border: none;
84
+ background: none;
85
+ cursor: pointer;
86
+ font-size: 16px;
87
+ line-height: 1;
88
+ color: inherit;
89
+ }
90
+ }
91
+
92
+ ul {
93
+ margin: 4px 0 0;
94
+ padding-left: 18px;
95
+ }
96
+
97
+ li {
98
+ font-family: ${FontFamily.Lato};
99
+ font-size: 12px;
100
+ line-height: 18px;
101
+ color: ${GlobalColors.s5};
102
+ }
103
+ }
104
+
105
+ /* Rojo = no entró. Ámbar = entró, pero hay algo que saber. */
106
+ .tray-notice-error {
107
+ border-color: ${GlobalColors.secondary_magenta};
108
+ background: rgba(${GlobalColors.rgb_color_pink}, 0.05);
109
+ color: ${GlobalColors.secondary_magenta};
110
+ }
111
+
112
+ .tray-notice-warning {
113
+ border-color: ${GlobalColors.in_progress};
114
+ background: rgba(237, 154, 77, 0.08);
115
+ color: ${GlobalColors.in_progress};
116
+ }
117
+
118
+ .dropzone {
119
+ position: relative;
120
+ width: 100%;
121
+ min-height: 160px;
122
+ transition: all 0.5s ease;
123
+
124
+ &.drag-active {
125
+ background-color: rgba(${GlobalColors.rgb_color_pink}, 0.05);
126
+ border: 2px dashed ${GlobalColors.color_pink};
127
+ border-radius: 8px;
128
+
129
+ aside {
130
+ opacity: 0.3;
131
+ pointer-events: none;
132
+ }
133
+ }
134
+
135
+ .drag-overlay {
136
+ position: absolute;
137
+ inset: 0;
138
+ display: flex;
139
+ align-items: center;
140
+ justify-content: center;
141
+ z-index: 1;
142
+
143
+ p {
144
+ font-family: ${FontFamily.AvenirNext};
145
+ font-size: 16px;
146
+ color: ${GlobalColors.color_pink};
147
+ }
148
+ }
149
+ }
150
+
151
+ .tray-rows {
152
+ display: flex;
153
+ flex-direction: column;
154
+ }
155
+
156
+ .tray-grid {
157
+ display: flex;
158
+ flex-wrap: wrap;
159
+ gap: 12px;
160
+ }
161
+
162
+ .tray-empty {
163
+ position: absolute;
164
+ top: 50%;
165
+ left: 50%;
166
+ transform: translate(-50%, -50%);
167
+ text-align: center;
168
+ width: 80%;
169
+
170
+ p {
171
+ margin: 0;
172
+ font-family: ${FontFamily.Lato};
173
+ font-size: 16px;
174
+ color: ${GlobalColors.gray};
175
+ }
176
+
177
+ .tray-empty-hint {
178
+ margin-top: 6px;
179
+ font-size: 13px;
180
+ color: ${GlobalColors.s4};
181
+ }
182
+ }
183
+ `;
@@ -13,6 +13,7 @@ export const ImagePreviewer = ({
13
13
  <EditionActiveImage
14
14
  img={activeImage?.src}
15
15
  altText={activeImage?.name}
16
+ ext={activeImage?.ext}
16
17
  setShowModal={setShowModal}
17
18
  />
18
19
  <ImageSelector images={imagesArray} setActiveImage={setActiveImage} />
@@ -48,6 +48,7 @@ export const ProductImageModal = ({
48
48
  images?.values[activeImage]?.srcDB
49
49
  }
50
50
  altText={images?.values[activeImage]?.name}
51
+ ext={images?.values[activeImage]?.ext}
51
52
  imageType={"expanded-modal-image"}
52
53
  />
53
54
  </div>
@@ -10,6 +10,7 @@ import {
10
10
  Note,
11
11
  Footer,
12
12
  } from "./styles";
13
+ import { soloPropagables } from "./non-propagable";
13
14
 
14
15
  /*
15
16
  Diálogo de propagación al guardar un producto padre.
@@ -38,7 +39,7 @@ import {
38
39
  const UMBRAL_SEGUNDO_PLANO = 20;
39
40
 
40
41
  export const PropagateChangesModal = ({
41
- changedAttributes = [],
42
+ changedAttributes: cambiosRecibidos = [],
42
43
  variants = [],
43
44
  groupMembers = [],
44
45
  groupCode = null,
@@ -46,6 +47,15 @@ export const PropagateChangesModal = ({
46
47
  onConfirm,
47
48
  saving = false,
48
49
  }) => {
50
+ /*
51
+ Segundo filtro, a propósito redundante: quien llama ya debería quitarlos —de
52
+ eso depende que el diálogo no se abra por un cambio que no propaga nada—,
53
+ pero ofrecer el UPC y descartarlo en el servidor es exactamente el camino
54
+ por el que un usuario concluye que la propagación no sirve. Vale más
55
+ repetirlo aquí que confiar en que ningún llamador futuro lo olvide.
56
+ */
57
+ const changedAttributes = soloPropagables(cambiosRecibidos);
58
+
49
59
  const [chosenAttributes, setChosenAttributes] = useState([]);
50
60
 
51
61
  /*
@@ -0,0 +1,29 @@
1
+ /*
2
+ Atributos que la propagación nunca copia.
3
+
4
+ UPC y SKU son la IDENTIDAD del producto, y una variante existe precisamente
5
+ porque la suya es distinta: el acuerdo lo dice literal —"cambian UPC/color/
6
+ talla"—. Bajarlos del padre dejaría dos productos con el mismo código de
7
+ barras, que es lo contrario de una variante.
8
+
9
+ El servidor ya los descarta y esa es la defensa de verdad: la exclusión vive
10
+ allá, no aquí, y aunque esta pantalla los mandara no se copiarían. Lo de este
11
+ archivo es otra cosa —que el diálogo no los OFREZCA—, porque ofrecer una
12
+ opción que se descarta en silencio es peor que no ofrecerla: el usuario elige
13
+ el UPC, guarda, no ve error, y concluye que la propagación está rota.
14
+
15
+ Los ids son los mismos que `variants/utils/constants.js` en el backend. Están
16
+ duplicados porque no hay un contrato compartido entre repos; si allá cambian,
17
+ aquí hay que cambiarlos.
18
+ */
19
+ export const UPC_ATTRIBUTE_ID = 683;
20
+ export const SKU_ATTRIBUTE_ID = 40001;
21
+
22
+ export const NON_PROPAGABLE_ATTRIBUTES = [UPC_ATTRIBUTE_ID, SKU_ATTRIBUTE_ID];
23
+
24
+ /**
25
+ * Quita de una lista de cambios los que no se pueden propagar.
26
+ * @param {Array<{id: number}>} cambios
27
+ */
28
+ export const soloPropagables = (cambios = []) =>
29
+ cambios.filter((c) => !NON_PROPAGABLE_ATTRIBUTES.includes(Number(c?.id)));
@@ -42,6 +42,7 @@ import { LinkVariantsModal } from "../../organisms/LinkVariantsModal";
42
42
  import { UnlinkVariantModal } from "../../organisms/UnlinkVariantModal";
43
43
  import { VariantGroupModal } from "../../organisms/VariantGroupModal";
44
44
  import { PropagateChangesModal } from "../../organisms/PropagateChangesModal";
45
+ import { soloPropagables } from "../../organisms/PropagateChangesModal/non-propagable";
45
46
  import { readPayload } from "../../organisms/LinkVariantsModal/api";
46
47
  import { ProductImageModal } from "../../organisms/ProductImageModal";
47
48
  import { VersionSelector } from "../../organisms/VersionSelector";
@@ -712,7 +713,7 @@ const ProviderProductEditionView = ({
712
713
  const retailerId = state.active_retailer?.id_retailer;
713
714
  const inputs = state.services?.datasheets?.inputs ?? {};
714
715
 
715
- return (state.updated_datasheets_inputs ?? []).map((item) => {
716
+ const cambios = (state.updated_datasheets_inputs ?? []).map((item) => {
716
717
  const input = inputs[`${item.attributeId}_${retailerId}`];
717
718
  return {
718
719
  id: Number(item.attributeId),
@@ -720,6 +721,14 @@ const ProviderProductEditionView = ({
720
721
  value: item.value,
721
722
  };
722
723
  });
724
+
725
+ /*
726
+ UPC y SKU se quitan aquí y no dentro del diálogo porque esta lista también
727
+ decide SI el diálogo se abre. Editar solo el UPC no tiene nada que
728
+ propagar, así que preguntarlo sería pedirle al usuario una decisión sobre
729
+ un conjunto vacío.
730
+ */
731
+ return soloPropagables(cambios);
723
732
  };
724
733
 
725
734
  const handleOnClickSave = () => {