contentoh-components-library 21.6.32 → 21.6.34
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/.claude/launch.json +15 -0
- package/dist/ai/prompts/attribute.prompt.js +28 -0
- package/dist/ai/prompts/description.prompt.js +33 -0
- package/dist/ai/schemas/attribute.schema.js +35 -0
- package/dist/ai/schemas/description.schema.js +27 -0
- package/dist/components/atoms/InheritanceTag/InheritanceTag.stories.js +5 -5
- package/dist/components/atoms/InheritanceTag/index.js +6 -6
- package/dist/components/organisms/InputGroup/index.js +12 -1
- package/dist/components/organisms/LinkVariantsModal/index.js +269 -356
- package/dist/components/organisms/LinkVariantsModal/styles.js +14 -5
- package/dist/components/organisms/PropagateChangesModal/index.js +12 -13
- package/dist/components/pages/ProviderProductEdition/context/provider-product-edition.context.js +5 -5
- package/dist/components/pages/ProviderProductEdition/context/reducers/inputs.js +5 -2
- package/dist/components/pages/ProviderProductEdition/index.js +96 -36
- package/dist/components/pages/RetailerProductEdition/context/provider-product-edition.context.js +5 -5
- package/dist/components/pages/RetailerProductEdition/context/reducers/inputs.js +5 -2
- package/dist/components/pages/RetailerProductEdition/index.js +100 -38
- package/package.json +1 -1
- package/src/components/atoms/InheritanceTag/InheritanceTag.stories.js +3 -3
- package/src/components/atoms/InheritanceTag/index.js +6 -6
- package/src/components/organisms/InputGroup/index.js +12 -0
- package/src/components/organisms/LinkVariantsModal/index.js +260 -287
- package/src/components/organisms/LinkVariantsModal/styles.js +65 -32
- package/src/components/organisms/PropagateChangesModal/index.js +19 -17
- package/src/components/pages/ProviderProductEdition/context/provider-product-edition.context.jsx +4 -4
- package/src/components/pages/ProviderProductEdition/context/reducers/inputs.js +13 -1
- package/src/components/pages/ProviderProductEdition/index.js +61 -20
- package/src/components/pages/RetailerProductEdition/context/provider-product-edition.context.jsx +4 -4
- package/src/components/pages/RetailerProductEdition/context/reducers/inputs.js +13 -1
- package/src/components/pages/RetailerProductEdition/index.js +74 -23
|
@@ -202,22 +202,6 @@ export const List = styled.ul`
|
|
|
202
202
|
min-width: 0;
|
|
203
203
|
}
|
|
204
204
|
|
|
205
|
-
.quitar {
|
|
206
|
-
flex-shrink: 0;
|
|
207
|
-
margin-right: 10px;
|
|
208
|
-
border: none;
|
|
209
|
-
background: none;
|
|
210
|
-
padding: 2px 6px;
|
|
211
|
-
cursor: pointer;
|
|
212
|
-
font-size: 16px;
|
|
213
|
-
line-height: 1;
|
|
214
|
-
color: ${GlobalColors.gray};
|
|
215
|
-
}
|
|
216
|
-
|
|
217
|
-
.quitar:hover {
|
|
218
|
-
color: ${PINK};
|
|
219
|
-
}
|
|
220
|
-
|
|
221
205
|
.count {
|
|
222
206
|
margin-left: auto;
|
|
223
207
|
font-size: 12px;
|
|
@@ -288,6 +272,13 @@ export const Message = styled.p`
|
|
|
288
272
|
background-color: rgba(${PINK_RGB}, 0.1);
|
|
289
273
|
color: ${PINK};
|
|
290
274
|
}
|
|
275
|
+
|
|
276
|
+
/* Aviso, no error: el padre ya tiene hijos y planchar les cambia el origen.
|
|
277
|
+
Se puede continuar, así que no usa el rosa de rechazo. */
|
|
278
|
+
&.aviso {
|
|
279
|
+
background-color: rgba(${PINK_RGB}, 0.06);
|
|
280
|
+
border-left: 3px solid ${PINK};
|
|
281
|
+
}
|
|
291
282
|
`;
|
|
292
283
|
|
|
293
284
|
export const Footer = styled.div`
|
|
@@ -386,29 +377,71 @@ export const ImageGrid = styled.div`
|
|
|
386
377
|
}
|
|
387
378
|
`;
|
|
388
379
|
|
|
389
|
-
|
|
380
|
+
/*
|
|
381
|
+
Padre → hijo, dibujado.
|
|
382
|
+
|
|
383
|
+
El paso de herencia escribe en una dirección sola y hasta ahora no la decía:
|
|
384
|
+
el modo se elige en la pantalla anterior y desaparece de la vista. Con dos
|
|
385
|
+
tarjetas y una flecha se lee de un golpe, sin tener que reconstruirlo del
|
|
386
|
+
texto de cada bloque.
|
|
387
|
+
*/
|
|
388
|
+
export const Direction = styled.div`
|
|
390
389
|
display: flex;
|
|
391
|
-
|
|
392
|
-
|
|
393
|
-
|
|
394
|
-
margin-bottom: 16px;
|
|
390
|
+
align-items: stretch;
|
|
391
|
+
gap: 10px;
|
|
392
|
+
margin-bottom: 12px;
|
|
395
393
|
|
|
396
|
-
|
|
397
|
-
|
|
398
|
-
|
|
399
|
-
|
|
394
|
+
.lado {
|
|
395
|
+
flex: 1 1 0;
|
|
396
|
+
min-width: 0;
|
|
397
|
+
padding: 10px 12px;
|
|
398
|
+
border: 1px solid ${GlobalColors.s2};
|
|
400
399
|
border-radius: 6px;
|
|
401
|
-
background:
|
|
400
|
+
background-color: ${GlobalColors.white};
|
|
401
|
+
}
|
|
402
|
+
|
|
403
|
+
/* El padre es el que manda en esta pantalla, y se marca como tal. */
|
|
404
|
+
.lado:first-of-type {
|
|
405
|
+
border-color: rgba(${PINK_RGB}, 0.45);
|
|
406
|
+
background-color: rgba(${PINK_RGB}, 0.05);
|
|
407
|
+
}
|
|
408
|
+
|
|
409
|
+
.rol {
|
|
410
|
+
display: block;
|
|
402
411
|
font-family: ${FontFamily.Raleway_600};
|
|
403
|
-
font-size:
|
|
412
|
+
font-size: 11px;
|
|
413
|
+
text-transform: uppercase;
|
|
414
|
+
letter-spacing: 0.4px;
|
|
404
415
|
color: ${PINK};
|
|
405
|
-
cursor: pointer;
|
|
406
|
-
flex-shrink: 0;
|
|
407
416
|
}
|
|
408
417
|
|
|
409
|
-
|
|
410
|
-
|
|
411
|
-
|
|
418
|
+
strong {
|
|
419
|
+
display: block;
|
|
420
|
+
margin-top: 2px;
|
|
421
|
+
font-family: ${FontFamily.Raleway_600};
|
|
422
|
+
font-size: 13px;
|
|
423
|
+
color: ${GlobalColors.deep_gray};
|
|
424
|
+
overflow: hidden;
|
|
425
|
+
text-overflow: ellipsis;
|
|
426
|
+
white-space: nowrap;
|
|
427
|
+
}
|
|
428
|
+
|
|
429
|
+
/* Cuál de los dos es la ficha abierta: sin esto, al relacionar un padre no se
|
|
430
|
+
sabe de qué lado está uno. */
|
|
431
|
+
.actual {
|
|
432
|
+
display: block;
|
|
433
|
+
margin-top: 2px;
|
|
434
|
+
font-size: 11px;
|
|
435
|
+
font-style: italic;
|
|
436
|
+
color: ${GlobalColors.gray};
|
|
437
|
+
}
|
|
438
|
+
|
|
439
|
+
.flecha {
|
|
440
|
+
align-self: center;
|
|
441
|
+
flex-shrink: 0;
|
|
442
|
+
font-size: 20px;
|
|
443
|
+
line-height: 1;
|
|
444
|
+
color: ${PINK};
|
|
412
445
|
}
|
|
413
446
|
`;
|
|
414
447
|
|
|
@@ -15,14 +15,13 @@ import {
|
|
|
15
15
|
Diálogo de propagación al guardar un producto padre.
|
|
16
16
|
|
|
17
17
|
Aparece cuando se guardan cambios de ficha técnica en un producto que tiene
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
como estaban.
|
|
18
|
+
hijos. El backend NO propaga nada por su cuenta: solo baja a los hijos lo que
|
|
19
|
+
venga en `propagation`, y esa lista la arma esta pantalla. Guardar sin abrirla,
|
|
20
|
+
o cerrarla sin propagar, deja al padre modificado y a los hijos como estaban.
|
|
22
21
|
|
|
23
22
|
props (* requerido):
|
|
24
23
|
- *changedAttributes: [{ id, name, value }] lo que se acaba de editar
|
|
25
|
-
- *variants: [{ articleId, name, upc }]
|
|
24
|
+
- *variants: [{ articleId, name, upc }] los hijos activos del padre
|
|
26
25
|
- *onCancel: cerrar sin guardar nada
|
|
27
26
|
- *onConfirm: (propagation | null) => void
|
|
28
27
|
null → guardar solo el padre
|
|
@@ -77,16 +76,19 @@ export const PropagateChangesModal = ({
|
|
|
77
76
|
<Overlay>
|
|
78
77
|
<Panel onClick={(event) => event.stopPropagation()}>
|
|
79
78
|
<Header>
|
|
80
|
-
<h2>
|
|
79
|
+
<h2>
|
|
80
|
+
Este producto tiene {variants.length} hijo
|
|
81
|
+
{variants.length === 1 ? "" : "s"}
|
|
82
|
+
</h2>
|
|
81
83
|
<p>
|
|
82
84
|
Cambiaste {changedAttributes.length} atributo
|
|
83
|
-
{changedAttributes.length === 1 ? "" : "s"}. Elige cuáles bajan a
|
|
84
|
-
|
|
85
|
+
{changedAttributes.length === 1 ? "" : "s"}. Elige cuáles bajan a los
|
|
86
|
+
hijos; el resto se guarda solo en este producto.
|
|
85
87
|
</p>
|
|
86
88
|
</Header>
|
|
87
89
|
|
|
88
90
|
<Body>
|
|
89
|
-
<h3>¿Qué se baja a
|
|
91
|
+
<h3>¿Qué se baja a los hijos?</h3>
|
|
90
92
|
<List>
|
|
91
93
|
{changedAttributes.map((attribute) => (
|
|
92
94
|
<li key={attribute.id}>
|
|
@@ -115,9 +117,9 @@ export const PropagateChangesModal = ({
|
|
|
115
117
|
onChange={() => setScope("all")}
|
|
116
118
|
/>
|
|
117
119
|
<span>
|
|
118
|
-
A
|
|
120
|
+
A todos
|
|
119
121
|
<span className="hint">
|
|
120
|
-
{variants.length}
|
|
122
|
+
{variants.length} hijo{variants.length === 1 ? "" : "s"} activo
|
|
121
123
|
{variants.length === 1 ? "" : "s"}.
|
|
122
124
|
</span>
|
|
123
125
|
</span>
|
|
@@ -154,16 +156,16 @@ export const PropagateChangesModal = ({
|
|
|
154
156
|
)}
|
|
155
157
|
|
|
156
158
|
{/* La diferencia con "ligar", y la que sorprende si no se dice: aquí
|
|
157
|
-
lo que
|
|
159
|
+
lo que el hijo editó a mano NO se pisa. */}
|
|
158
160
|
<Note>
|
|
159
|
-
|
|
161
|
+
Un hijo que haya editado alguno de estos atributos por su cuenta
|
|
160
162
|
conserva su valor. Para devolverlo al del padre está la acción
|
|
161
|
-
«Volver a heredar» en la ficha de
|
|
163
|
+
«Volver a heredar» en la ficha de ese hijo.
|
|
162
164
|
</Note>
|
|
163
165
|
|
|
164
166
|
{enSegundoPlano && (
|
|
165
167
|
<Note className="accent">
|
|
166
|
-
Son más de {UMBRAL_SEGUNDO_PLANO}
|
|
168
|
+
Son más de {UMBRAL_SEGUNDO_PLANO} hijos, así que la propagación
|
|
167
169
|
corre en segundo plano: el guardado termina antes de que todas estén
|
|
168
170
|
actualizadas.
|
|
169
171
|
</Note>
|
|
@@ -173,8 +175,8 @@ export const PropagateChangesModal = ({
|
|
|
173
175
|
<Footer>
|
|
174
176
|
<span className="resumen">
|
|
175
177
|
{puedePropagar
|
|
176
|
-
? `${chosenAttributes.length} atributo(s) a ${destinatarios}
|
|
177
|
-
: "No se bajará nada a
|
|
178
|
+
? `${chosenAttributes.length} atributo(s) a ${destinatarios} hijo(s).`
|
|
179
|
+
: "No se bajará nada a los hijos."}
|
|
178
180
|
</span>
|
|
179
181
|
<button
|
|
180
182
|
type="button"
|
package/src/components/pages/ProviderProductEdition/context/provider-product-edition.context.jsx
CHANGED
|
@@ -123,7 +123,7 @@ export const ProviderProductEditionProvider = ({ children }) => {
|
|
|
123
123
|
lo que el usuario eligió en el diálogo: { attributes: [...], targetChildren }.
|
|
124
124
|
|
|
125
125
|
Sin ese bloque el backend no propaga nada. Es deliberado: bajar un cambio a
|
|
126
|
-
|
|
126
|
+
los hijos tiene que ser una decisión, no un efecto secundario de guardar.
|
|
127
127
|
*/
|
|
128
128
|
const saveDatasheets = async (token, propagation) => {
|
|
129
129
|
if (state.updated_datasheets_inputs.length === 0) return;
|
|
@@ -167,17 +167,17 @@ export const ProviderProductEditionProvider = ({ children }) => {
|
|
|
167
167
|
type: "SET_UPDATED_DATASHEETS_INPUTS",
|
|
168
168
|
payload: [],
|
|
169
169
|
});
|
|
170
|
-
// En
|
|
170
|
+
// En un hijo, todo atributo que se toca deja de ser heredado:
|
|
171
171
|
// update-articles-data le pone is_overridden = 1 en la base. Si aquí solo
|
|
172
172
|
// se refresca el valor, el chip sigue diciendo "Heredado" hasta la
|
|
173
173
|
// siguiente carga de la ficha, y el usuario ve una marca que ya es falsa.
|
|
174
|
-
const
|
|
174
|
+
const esHijo =
|
|
175
175
|
state.services?.datasheets?.variant?.isVariant ??
|
|
176
176
|
!!state.product?.parentArticleId;
|
|
177
177
|
|
|
178
178
|
dispatch({
|
|
179
179
|
type: "UPDATE_DATASHEETS_INPUTS",
|
|
180
|
-
payload:
|
|
180
|
+
payload: esHijo
|
|
181
181
|
? state.updated_datasheets_inputs.map((item) => ({
|
|
182
182
|
...item,
|
|
183
183
|
isOverridden: 1,
|
|
@@ -52,7 +52,19 @@ export const inputsReducer = (state, action) => {
|
|
|
52
52
|
const updatedInput = action.payload.find(
|
|
53
53
|
(updated) => updated.attributeId === input.id
|
|
54
54
|
);
|
|
55
|
-
|
|
55
|
+
if (!updatedInput) return input;
|
|
56
|
+
|
|
57
|
+
return {
|
|
58
|
+
...input,
|
|
59
|
+
value: updatedInput.value,
|
|
60
|
+
// Opcional a propósito, igual que en la ficha técnica: quien solo
|
|
61
|
+
// cambia el valor no toca la marca de herencia. La usa "Volver a
|
|
62
|
+
// heredar", que sí necesita devolverla a heredada para que el chip
|
|
63
|
+
// deje de decir "Propio".
|
|
64
|
+
...(updatedInput.isOverridden === undefined
|
|
65
|
+
? {}
|
|
66
|
+
: { isOverridden: updatedInput.isOverridden }),
|
|
67
|
+
};
|
|
56
68
|
}
|
|
57
69
|
);
|
|
58
70
|
|
|
@@ -207,6 +207,16 @@ const ProviderProductEditionView = ({
|
|
|
207
207
|
setProductData();
|
|
208
208
|
}, []);
|
|
209
209
|
|
|
210
|
+
/*
|
|
211
|
+
Contador que fuerza a releer los servicios tras ligar.
|
|
212
|
+
|
|
213
|
+
El efecto de abajo depende de estatus, versión y cadena activa, y ligar no
|
|
214
|
+
cambia ninguno: cambia la HERENCIA de cada campo en el servidor. Al planchar,
|
|
215
|
+
lo que era propio pasa a heredado, y sin releer los chips siguen diciendo lo
|
|
216
|
+
de antes hasta que alguien cierra y reabre el producto.
|
|
217
|
+
*/
|
|
218
|
+
const [linkNonce, setLinkNonce] = useState(0);
|
|
219
|
+
|
|
210
220
|
// Setter de los servicios y porcentages del producto
|
|
211
221
|
useEffect(() => {
|
|
212
222
|
if (!state.product || !state.active_retailer) return;
|
|
@@ -363,6 +373,7 @@ const ProviderProductEditionView = ({
|
|
|
363
373
|
state.product?.version,
|
|
364
374
|
state.active_retailer,
|
|
365
375
|
token,
|
|
376
|
+
linkNonce,
|
|
366
377
|
]);
|
|
367
378
|
|
|
368
379
|
// Actualizador de campos del ui para que coincidan con el retailer activo
|
|
@@ -709,15 +720,15 @@ const ProviderProductEditionView = ({
|
|
|
709
720
|
saveDescriptions(token);
|
|
710
721
|
break;
|
|
711
722
|
case "Ficha técnica": {
|
|
712
|
-
// Guardar un padre con
|
|
713
|
-
// paso el cambio se queda solo en el padre y
|
|
723
|
+
// Guardar un padre con hijos abre la decisión de propagar. Sin este
|
|
724
|
+
// paso el cambio se queda solo en el padre y los hijos se van
|
|
714
725
|
// separando de él sin que nadie lo note.
|
|
715
726
|
const esPadre = state.product?.variantsCount > 0;
|
|
716
727
|
const cambios = cambiosDeFichaTecnica();
|
|
717
728
|
|
|
718
729
|
if (esPadre && cambios.length > 0) {
|
|
719
730
|
// Se espera a tener la familia antes de abrir: si no, el diálogo
|
|
720
|
-
// aparece diciendo "0
|
|
731
|
+
// aparece diciendo "0 hijos" hasta que llega la respuesta, que es
|
|
721
732
|
// justo lo contrario de lo que va a preguntar.
|
|
722
733
|
setPreparandoPropagacion(true);
|
|
723
734
|
loadFamily()
|
|
@@ -763,7 +774,7 @@ const ProviderProductEditionView = ({
|
|
|
763
774
|
};
|
|
764
775
|
|
|
765
776
|
/*
|
|
766
|
-
Trae la familia del producto: sus
|
|
777
|
+
Trae la familia del producto: sus hijos, o sus hermanos si el abierto es
|
|
767
778
|
una de ellas.
|
|
768
779
|
|
|
769
780
|
Se pide al abrir la sección y no al montar la pantalla: la mayoría de los
|
|
@@ -786,7 +797,7 @@ const ProviderProductEditionView = ({
|
|
|
786
797
|
};
|
|
787
798
|
|
|
788
799
|
/*
|
|
789
|
-
Abre
|
|
800
|
+
Abre otro producto de la familia.
|
|
790
801
|
|
|
791
802
|
Esta pantalla no lee el producto de la URL: lo lee de sessionStorage, donde
|
|
792
803
|
lo dejó el listado al hacer clic. Así que navegar a /products/:id sin más
|
|
@@ -801,7 +812,7 @@ const ProviderProductEditionView = ({
|
|
|
801
812
|
raíz —que la ficha lea de la URL— toca todas las plataformas y no cabe aquí.
|
|
802
813
|
*/
|
|
803
814
|
/*
|
|
804
|
-
Abre la edición múltiple con
|
|
815
|
+
Abre la edición múltiple con los hijos de la familia ya cargados.
|
|
805
816
|
|
|
806
817
|
Esa pantalla lee `multipleEditionList` de sessionStorage —así la deja el
|
|
807
818
|
listado al pulsar "editar varios"— y necesita el PRODUCTO COMPLETO de cada
|
|
@@ -855,7 +866,7 @@ const ProviderProductEditionView = ({
|
|
|
855
866
|
payload: {
|
|
856
867
|
show: true,
|
|
857
868
|
title: "",
|
|
858
|
-
message: "No se pudieron cargar
|
|
869
|
+
message: "No se pudieron cargar los hijos para editarlos juntos.",
|
|
859
870
|
image: errorIcon,
|
|
860
871
|
},
|
|
861
872
|
});
|
|
@@ -918,7 +929,7 @@ const ProviderProductEditionView = ({
|
|
|
918
929
|
el usuario tenga sin guardar.
|
|
919
930
|
|
|
920
931
|
Los dos modos tienen efectos opuestos sobre este producto: ligando hijos GANA
|
|
921
|
-
|
|
932
|
+
hijos; ligándose a un padre pasa a SER uno, y entonces el botón de ligar
|
|
922
933
|
desaparece porque la jerarquía es de un solo nivel.
|
|
923
934
|
*/
|
|
924
935
|
const handleOnLinkedVariants = (result) => {
|
|
@@ -941,6 +952,8 @@ const ProviderProductEditionView = ({
|
|
|
941
952
|
dispatch({ type: "SET_PRODUCT", payload: updatedProduct });
|
|
942
953
|
// La lista cacheada quedó vieja: se descarta para que vuelva a pedirse.
|
|
943
954
|
setFamily(null);
|
|
955
|
+
// Y los servicios, para que los chips reflejen la herencia recién creada.
|
|
956
|
+
setLinkNonce((n) => n + 1);
|
|
944
957
|
|
|
945
958
|
// Y en sessionStorage, que es de donde sale el producto al recargar. Sin
|
|
946
959
|
// esto el arreglo dura hasta el siguiente F5 y vuelve el contador viejo,
|
|
@@ -965,7 +978,7 @@ const ProviderProductEditionView = ({
|
|
|
965
978
|
/*
|
|
966
979
|
Acción "Volver a heredar del producto" de los chips de herencia.
|
|
967
980
|
|
|
968
|
-
La inicia
|
|
981
|
+
La inicia el hijo, no el padre, y descarta un valor capturado a mano: por
|
|
969
982
|
eso el endpoint exige la lista explícita de atributos y no acepta un "todos"
|
|
970
983
|
implícito.
|
|
971
984
|
|
|
@@ -973,15 +986,25 @@ const ProviderProductEditionView = ({
|
|
|
973
986
|
alternativa —recargar la ficha— se llevaría por delante lo que el usuario
|
|
974
987
|
tenga sin guardar en las otras pestañas.
|
|
975
988
|
*/
|
|
976
|
-
|
|
989
|
+
/*
|
|
990
|
+
`kind` distingue de qué pestaña salió el chip. Las descripciones viven en
|
|
991
|
+
`rel_article_description` y la ficha técnica en `article_data`: el endpoint
|
|
992
|
+
recibe una lista o la otra, y lo que devuelve se repinta en su propio
|
|
993
|
+
reducer.
|
|
994
|
+
*/
|
|
995
|
+
const handleOnRestoreInheritance = async (attributeId, kind = "attribute") => {
|
|
977
996
|
if (!attributeId || !state.product?.id_article) return;
|
|
978
997
|
|
|
998
|
+
const esDescripcion = kind === "description";
|
|
999
|
+
|
|
979
1000
|
try {
|
|
980
1001
|
const res = await axios.post(
|
|
981
1002
|
`${process.env.REACT_APP_VARIANTS_ENDPOINT}/restore`,
|
|
982
1003
|
{
|
|
983
1004
|
articleId: state.product.id_article,
|
|
984
|
-
|
|
1005
|
+
...(esDescripcion
|
|
1006
|
+
? { descriptions: [Number(attributeId)] }
|
|
1007
|
+
: { attributes: [Number(attributeId)] }),
|
|
985
1008
|
},
|
|
986
1009
|
{ headers: { Authorization: token } }
|
|
987
1010
|
);
|
|
@@ -990,6 +1013,18 @@ const ProviderProductEditionView = ({
|
|
|
990
1013
|
// real viaja en el cuerpo y readPayload lo convierte en excepción.
|
|
991
1014
|
const data = readPayload(res);
|
|
992
1015
|
|
|
1016
|
+
if (esDescripcion) {
|
|
1017
|
+
dispatch({
|
|
1018
|
+
type: "UPDATE_DESCRIPTIONS_INPUTS",
|
|
1019
|
+
payload: (data.descriptionValues || []).map((item) => ({
|
|
1020
|
+
attributeId: item.id_description,
|
|
1021
|
+
value: item.value,
|
|
1022
|
+
isOverridden: 0,
|
|
1023
|
+
})),
|
|
1024
|
+
});
|
|
1025
|
+
return;
|
|
1026
|
+
}
|
|
1027
|
+
|
|
993
1028
|
dispatch({
|
|
994
1029
|
type: "UPDATE_DATASHEETS_INPUTS",
|
|
995
1030
|
payload: (data.values || []).map((item) => ({
|
|
@@ -1664,7 +1699,7 @@ const ProviderProductEditionView = ({
|
|
|
1664
1699
|
</div>
|
|
1665
1700
|
<div className="product-information">
|
|
1666
1701
|
<div className="product-information">
|
|
1667
|
-
{/* Solo cuando el producto NO es ya
|
|
1702
|
+
{/* Solo cuando el producto NO es ya hijo de alguien: ligarlo
|
|
1668
1703
|
otra vez devolvería VARIANT_ALREADY_LINKED sin excepción, y un
|
|
1669
1704
|
botón que siempre falla es peor que no tenerlo. Para salir de
|
|
1670
1705
|
una familia está la desvinculación, no este botón. */}
|
|
@@ -1687,7 +1722,7 @@ const ProviderProductEditionView = ({
|
|
|
1687
1722
|
loadFamily();
|
|
1688
1723
|
}}
|
|
1689
1724
|
>
|
|
1690
|
-
{state.product.variantsCount}
|
|
1725
|
+
{state.product.variantsCount} hijo
|
|
1691
1726
|
{state.product.variantsCount === 1 ? "" : "s"}
|
|
1692
1727
|
<span aria-hidden="true">{showFamily ? " ▴" : " ▾"}</span>
|
|
1693
1728
|
</button>
|
|
@@ -1695,8 +1730,8 @@ const ProviderProductEditionView = ({
|
|
|
1695
1730
|
</div>
|
|
1696
1731
|
)}
|
|
1697
1732
|
|
|
1698
|
-
{/* La ficha de
|
|
1699
|
-
aquí se salta al padre o a
|
|
1733
|
+
{/* La ficha de un hijo también necesita ver la familia: desde
|
|
1734
|
+
aquí se salta al padre o a un hermano sin pasar por el listado. */}
|
|
1700
1735
|
{state.product?.parentArticleId && (
|
|
1701
1736
|
<div className="variants-actions">
|
|
1702
1737
|
<button
|
|
@@ -1708,7 +1743,7 @@ const ProviderProductEditionView = ({
|
|
|
1708
1743
|
loadFamily();
|
|
1709
1744
|
}}
|
|
1710
1745
|
>
|
|
1711
|
-
|
|
1746
|
+
Hijo de otro producto
|
|
1712
1747
|
<span aria-hidden="true">{showFamily ? " ▴" : " ▾"}</span>
|
|
1713
1748
|
</button>
|
|
1714
1749
|
</div>
|
|
@@ -1756,7 +1791,7 @@ const ProviderProductEditionView = ({
|
|
|
1756
1791
|
? "Abriendo…"
|
|
1757
1792
|
: variant.isCurrent
|
|
1758
1793
|
? "Estás aquí"
|
|
1759
|
-
: "
|
|
1794
|
+
: "Hijo"}
|
|
1760
1795
|
</span>
|
|
1761
1796
|
<span className="name">{variant.name}</span>
|
|
1762
1797
|
<span className="upc">{variant.upc}</span>
|
|
@@ -1764,10 +1799,10 @@ const ProviderProductEditionView = ({
|
|
|
1764
1799
|
))}
|
|
1765
1800
|
|
|
1766
1801
|
{family && !family.error && !family.variants?.length && (
|
|
1767
|
-
<p className="loading">Este producto no tiene
|
|
1802
|
+
<p className="loading">Este producto no tiene hijos.</p>
|
|
1768
1803
|
)}
|
|
1769
1804
|
|
|
1770
|
-
{/*
|
|
1805
|
+
{/* Editarlos juntos evita abrir seis fichas para llenar lo
|
|
1771
1806
|
mismo seis veces, que es el caso real de una familia. */}
|
|
1772
1807
|
{family?.variants?.length > 1 && (
|
|
1773
1808
|
<button
|
|
@@ -1778,7 +1813,7 @@ const ProviderProductEditionView = ({
|
|
|
1778
1813
|
>
|
|
1779
1814
|
{preparandoEdicionMultiple
|
|
1780
1815
|
? "Abriendo…"
|
|
1781
|
-
: `Editar
|
|
1816
|
+
: `Editar los ${family.variants.length} hijos juntos`}
|
|
1782
1817
|
</button>
|
|
1783
1818
|
)}
|
|
1784
1819
|
</div>
|
|
@@ -1849,6 +1884,12 @@ const ProviderProductEditionView = ({
|
|
|
1849
1884
|
updatedDescriptions={
|
|
1850
1885
|
state.updated_descriptions_inputs
|
|
1851
1886
|
}
|
|
1887
|
+
/* El parentesco es del producto, no del servicio:
|
|
1888
|
+
los dos endpoints lo devuelven igual. Se toma de
|
|
1889
|
+
la ficha técnica para no depender de que la
|
|
1890
|
+
llamada de descripciones traiga el bloque. */
|
|
1891
|
+
variant={state.services?.datasheets?.variant}
|
|
1892
|
+
onRestoreInheritance={handleOnRestoreInheritance}
|
|
1852
1893
|
articleId={state.product?.id_article}
|
|
1853
1894
|
version={state.product.version}
|
|
1854
1895
|
isShowbox={false}
|
package/src/components/pages/RetailerProductEdition/context/provider-product-edition.context.jsx
CHANGED
|
@@ -140,7 +140,7 @@ export const ProviderProductEditionProvider = ({ children }) => {
|
|
|
140
140
|
/*
|
|
141
141
|
`propagation` es opcional y solo lo manda la ficha de un producto padre, con
|
|
142
142
|
lo que el usuario eligió en el diálogo. Sin ese bloque el backend no propaga
|
|
143
|
-
nada: bajar un cambio a
|
|
143
|
+
nada: bajar un cambio a los hijos es una decisión, no un efecto
|
|
144
144
|
secundario de guardar.
|
|
145
145
|
*/
|
|
146
146
|
const saveDatasheets = async (token, propagation) => {
|
|
@@ -181,17 +181,17 @@ export const ProviderProductEditionProvider = ({ children }) => {
|
|
|
181
181
|
type: "SET_UPDATED_DATASHEETS_INPUTS",
|
|
182
182
|
payload: [],
|
|
183
183
|
});
|
|
184
|
-
// En
|
|
184
|
+
// En un hijo, todo atributo que se toca deja de ser heredado:
|
|
185
185
|
// update-articles-data le pone is_overridden = 1 en la base. Sin
|
|
186
186
|
// reflejarlo aquí, el chip sigue diciendo "Heredado" hasta la siguiente
|
|
187
187
|
// carga de la ficha, y el usuario ve una marca que ya es falsa.
|
|
188
|
-
const
|
|
188
|
+
const esHijo =
|
|
189
189
|
state.services?.datasheets?.variant?.isVariant ??
|
|
190
190
|
!!state.product?.parentArticleId;
|
|
191
191
|
|
|
192
192
|
dispatch({
|
|
193
193
|
type: "UPDATE_DATASHEETS_INPUTS",
|
|
194
|
-
payload:
|
|
194
|
+
payload: esHijo
|
|
195
195
|
? state.updated_datasheets_inputs.map((item) => ({
|
|
196
196
|
...item,
|
|
197
197
|
isOverridden: 1,
|
|
@@ -61,7 +61,19 @@ export const inputsReducer = (state, action) => {
|
|
|
61
61
|
const updatedInput = action.payload.find(
|
|
62
62
|
(updated) => updated.attributeId === input.id
|
|
63
63
|
);
|
|
64
|
-
|
|
64
|
+
if (!updatedInput) return input;
|
|
65
|
+
|
|
66
|
+
return {
|
|
67
|
+
...input,
|
|
68
|
+
value: updatedInput.value,
|
|
69
|
+
// Opcional a propósito, igual que en la ficha técnica: quien solo
|
|
70
|
+
// cambia el valor no toca la marca de herencia. La usa "Volver a
|
|
71
|
+
// heredar", que sí necesita devolverla a heredada para que el chip
|
|
72
|
+
// deje de decir "Propio".
|
|
73
|
+
...(updatedInput.isOverridden === undefined
|
|
74
|
+
? {}
|
|
75
|
+
: { isOverridden: updatedInput.isOverridden }),
|
|
76
|
+
};
|
|
65
77
|
}
|
|
66
78
|
);
|
|
67
79
|
|