contentoh-components-library 21.6.35 → 21.6.37
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/PropagateChangesModal/index.js +36 -3
- package/dist/components/organisms/UnlinkVariantModal/index.js +178 -0
- package/dist/components/organisms/UnlinkVariantModal/styles.js +42 -0
- package/dist/components/pages/ProviderProductEdition/index.js +186 -59
- package/dist/components/pages/ProviderProductEdition/styles.js +2 -2
- package/dist/components/pages/RetailerProductEdition/index.js +290 -97
- package/dist/components/pages/RetailerProductEdition/styles.js +2 -2
- package/dist/index.js +25 -12
- 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/PropagateChangesModal/index.js +43 -8
- package/src/components/organisms/UnlinkVariantModal/index.js +141 -0
- package/src/components/organisms/UnlinkVariantModal/styles.js +106 -0
- package/src/components/pages/ProviderProductEdition/index.js +166 -37
- package/src/components/pages/ProviderProductEdition/styles.js +30 -0
- package/src/components/pages/RetailerProductEdition/index.js +220 -42
- package/src/components/pages/RetailerProductEdition/styles.js +30 -0
- package/src/index.js +1 -0
|
@@ -39,6 +39,7 @@ import { ImageDataTable } from "../../organisms/ImageDataTable";
|
|
|
39
39
|
import { ImagePreviewer } from "../../organisms/ImagePreviewer";
|
|
40
40
|
import { InputGroup } from "../../organisms/InputGroup";
|
|
41
41
|
import { LinkVariantsModal } from "../../organisms/LinkVariantsModal";
|
|
42
|
+
import { UnlinkVariantModal } from "../../organisms/UnlinkVariantModal";
|
|
42
43
|
import { PropagateChangesModal } from "../../organisms/PropagateChangesModal";
|
|
43
44
|
import { readPayload } from "../../organisms/LinkVariantsModal/api";
|
|
44
45
|
import { ProductImageModal } from "../../organisms/ProductImageModal";
|
|
@@ -781,8 +782,8 @@ const ProviderProductEditionView = ({
|
|
|
781
782
|
productos no son familia, y una llamada por ficha para no mostrar nada es
|
|
782
783
|
peso que paga todo el mundo por el caso de unos pocos.
|
|
783
784
|
*/
|
|
784
|
-
const loadFamily = async () => {
|
|
785
|
-
if (family) return family;
|
|
785
|
+
const loadFamily = async (forzar = false) => {
|
|
786
|
+
if (family && !forzar) return family;
|
|
786
787
|
try {
|
|
787
788
|
const res = await axios.post(
|
|
788
789
|
`${process.env.REACT_APP_VARIANTS_ENDPOINT}/family`,
|
|
@@ -875,6 +876,71 @@ const ProviderProductEditionView = ({
|
|
|
875
876
|
}
|
|
876
877
|
};
|
|
877
878
|
|
|
879
|
+
/*
|
|
880
|
+
Qué vínculo se está por romper, o null si no hay diálogo abierto.
|
|
881
|
+
|
|
882
|
+
Guarda al HIJO, porque la relación vive en él (`article.parent_article_id`)
|
|
883
|
+
y es lo que pide la acción `unlink`. `mode` solo cambia el texto: no es lo
|
|
884
|
+
mismo soltarse de un padre que soltar a un hijo, aunque por dentro sea la
|
|
885
|
+
misma llamada.
|
|
886
|
+
*/
|
|
887
|
+
const [porDesvincular, setPorDesvincular] = useState(null);
|
|
888
|
+
|
|
889
|
+
/*
|
|
890
|
+
Después de desvincular, esta ficha cambia de forma.
|
|
891
|
+
|
|
892
|
+
Si el producto se soltó de su padre, deja de ser hijo y vuelve a poder ligar
|
|
893
|
+
—el botón reaparece—. Si soltó a un hijo, pierde uno de la cuenta. En los dos
|
|
894
|
+
casos hay que repreguntar la familia: lo que se ve en la lista ya no es lo
|
|
895
|
+
que hay en el servidor.
|
|
896
|
+
*/
|
|
897
|
+
const handleOnUnlinked = (result) => {
|
|
898
|
+
if (!result) return;
|
|
899
|
+
|
|
900
|
+
const updatedProduct =
|
|
901
|
+
result.mode === "self"
|
|
902
|
+
? { ...state.product, parentArticleId: null, variantsCount: 0 }
|
|
903
|
+
: {
|
|
904
|
+
...state.product,
|
|
905
|
+
variantsCount: Math.max((state.product?.variantsCount ?? 1) - 1, 0),
|
|
906
|
+
};
|
|
907
|
+
|
|
908
|
+
dispatch({ type: "SET_PRODUCT", payload: updatedProduct });
|
|
909
|
+
|
|
910
|
+
try {
|
|
911
|
+
sessionStorage.setItem("productSelected", JSON.stringify(updatedProduct));
|
|
912
|
+
|
|
913
|
+
const productEdit = JSON.parse(sessionStorage.getItem("productEdit"));
|
|
914
|
+
if (productEdit) {
|
|
915
|
+
sessionStorage.setItem(
|
|
916
|
+
"productEdit",
|
|
917
|
+
JSON.stringify({ ...productEdit, product: updatedProduct })
|
|
918
|
+
);
|
|
919
|
+
}
|
|
920
|
+
} catch (error) {
|
|
921
|
+
console.error("unlinkVariant/sessionStorage: ", error);
|
|
922
|
+
}
|
|
923
|
+
|
|
924
|
+
/*
|
|
925
|
+
Si ya no queda familia, se cierra el desplegable: su botón desaparece con
|
|
926
|
+
el vínculo, y dejarlo abierto pinta una caja que dice «no tiene hijos» y
|
|
927
|
+
que nadie puede cerrar.
|
|
928
|
+
*/
|
|
929
|
+
if (
|
|
930
|
+
!updatedProduct.parentArticleId &&
|
|
931
|
+
(updatedProduct.variantsCount ?? 0) === 0
|
|
932
|
+
) {
|
|
933
|
+
setShowFamily(false);
|
|
934
|
+
}
|
|
935
|
+
|
|
936
|
+
loadFamily(true);
|
|
937
|
+
|
|
938
|
+
// Y los servicios: al desvincular, el servidor pone `is_overridden = 0` en
|
|
939
|
+
// todas las versiones, así que los chips de herencia tienen que dejar de
|
|
940
|
+
// dibujarse. Sin esto la ficha sigue mostrándolos hasta que se recarga.
|
|
941
|
+
setLinkNonce((n) => n + 1);
|
|
942
|
+
};
|
|
943
|
+
|
|
878
944
|
const openVariant = async (articleId) => {
|
|
879
945
|
setLoadingVariant(articleId);
|
|
880
946
|
try {
|
|
@@ -950,8 +1016,14 @@ const ProviderProductEditionView = ({
|
|
|
950
1016
|
};
|
|
951
1017
|
|
|
952
1018
|
dispatch({ type: "SET_PRODUCT", payload: updatedProduct });
|
|
953
|
-
|
|
954
|
-
|
|
1019
|
+
/*
|
|
1020
|
+
Se vuelve a pedir, no se vacía.
|
|
1021
|
+
|
|
1022
|
+
`setFamily(null)` invalidaba la caché, pero la lista solo se pide al pulsar
|
|
1023
|
+
el desplegable: si estaba abierto al ligar, se quedaba en «Cargando
|
|
1024
|
+
familia…» para siempre, porque ya nadie iba a volver a pedirla.
|
|
1025
|
+
*/
|
|
1026
|
+
loadFamily(true);
|
|
955
1027
|
// Y los servicios, para que los chips reflejen la herencia recién creada.
|
|
956
1028
|
setLinkNonce((n) => n + 1);
|
|
957
1029
|
|
|
@@ -1513,6 +1585,14 @@ const ProviderProductEditionView = ({
|
|
|
1513
1585
|
onLinked={handleOnLinkedVariants}
|
|
1514
1586
|
/>
|
|
1515
1587
|
)}
|
|
1588
|
+
{porDesvincular && (
|
|
1589
|
+
<UnlinkVariantModal
|
|
1590
|
+
{...porDesvincular}
|
|
1591
|
+
token={token}
|
|
1592
|
+
onClose={() => setPorDesvincular(null)}
|
|
1593
|
+
onUnlinked={handleOnUnlinked}
|
|
1594
|
+
/>
|
|
1595
|
+
)}
|
|
1516
1596
|
{showModal && (
|
|
1517
1597
|
<ProductImageModal
|
|
1518
1598
|
images={state.images_values}
|
|
@@ -1759,43 +1839,92 @@ const ProviderProductEditionView = ({
|
|
|
1759
1839
|
</p>
|
|
1760
1840
|
)}
|
|
1761
1841
|
|
|
1842
|
+
{/* Desvincular se ofrece donde se ve el vínculo, y solo en
|
|
1843
|
+
la dirección que corresponde a esta ficha: desde un hijo,
|
|
1844
|
+
sobre su padre —«me suelto»—; desde un padre, sobre cada
|
|
1845
|
+
hijo. A un hermano no: para eso se abre su ficha, donde la
|
|
1846
|
+
acción dice lo que de verdad va a pasar. */}
|
|
1762
1847
|
{family?.parent && (
|
|
1763
|
-
<
|
|
1764
|
-
|
|
1765
|
-
|
|
1766
|
-
|
|
1767
|
-
|
|
1768
|
-
|
|
1769
|
-
|
|
1770
|
-
|
|
1771
|
-
|
|
1772
|
-
|
|
1773
|
-
|
|
1774
|
-
|
|
1775
|
-
|
|
1776
|
-
|
|
1848
|
+
<div className="family-item">
|
|
1849
|
+
<button
|
|
1850
|
+
type="button"
|
|
1851
|
+
className="family-row parent"
|
|
1852
|
+
disabled={loadingVariant !== null}
|
|
1853
|
+
onClick={() => openVariant(family.parent.articleId)}
|
|
1854
|
+
>
|
|
1855
|
+
<span className="role">
|
|
1856
|
+
{loadingVariant === family.parent.articleId
|
|
1857
|
+
? "Abriendo…"
|
|
1858
|
+
: "Producto padre"}
|
|
1859
|
+
</span>
|
|
1860
|
+
<span className="name">{family.parent.name}</span>
|
|
1861
|
+
<span className="upc">{family.parent.upc}</span>
|
|
1862
|
+
</button>
|
|
1863
|
+
|
|
1864
|
+
<button
|
|
1865
|
+
type="button"
|
|
1866
|
+
className="desvincular"
|
|
1867
|
+
title="Dejar de ser hijo de este producto"
|
|
1868
|
+
onClick={() =>
|
|
1869
|
+
setPorDesvincular({
|
|
1870
|
+
mode: "self",
|
|
1871
|
+
childArticleId: state.product?.id_article,
|
|
1872
|
+
childName: state.product?.name,
|
|
1873
|
+
childUpc: state.product?.upc,
|
|
1874
|
+
parentName: family.parent.name,
|
|
1875
|
+
parentUpc: family.parent.upc,
|
|
1876
|
+
})
|
|
1877
|
+
}
|
|
1878
|
+
>
|
|
1879
|
+
Desvincular
|
|
1880
|
+
</button>
|
|
1881
|
+
</div>
|
|
1777
1882
|
)}
|
|
1778
1883
|
|
|
1779
1884
|
{family?.variants?.map((variant) => (
|
|
1780
|
-
<
|
|
1781
|
-
|
|
1782
|
-
|
|
1783
|
-
|
|
1784
|
-
|
|
1785
|
-
|
|
1786
|
-
|
|
1787
|
-
|
|
1788
|
-
|
|
1789
|
-
|
|
1790
|
-
|
|
1791
|
-
|
|
1792
|
-
|
|
1793
|
-
|
|
1794
|
-
|
|
1795
|
-
|
|
1796
|
-
|
|
1797
|
-
|
|
1798
|
-
|
|
1885
|
+
<div className="family-item" key={variant.articleId}>
|
|
1886
|
+
<button
|
|
1887
|
+
type="button"
|
|
1888
|
+
className={`family-row${
|
|
1889
|
+
variant.isCurrent ? " current" : ""
|
|
1890
|
+
}`}
|
|
1891
|
+
disabled={variant.isCurrent || loadingVariant !== null}
|
|
1892
|
+
onClick={() => openVariant(variant.articleId)}
|
|
1893
|
+
>
|
|
1894
|
+
<span className="role">
|
|
1895
|
+
{loadingVariant === variant.articleId
|
|
1896
|
+
? "Abriendo…"
|
|
1897
|
+
: variant.isCurrent
|
|
1898
|
+
? "Estás aquí"
|
|
1899
|
+
: "Hijo"}
|
|
1900
|
+
</span>
|
|
1901
|
+
<span className="name">{variant.name}</span>
|
|
1902
|
+
<span className="upc">{variant.upc}</span>
|
|
1903
|
+
</button>
|
|
1904
|
+
|
|
1905
|
+
{/* Solo cuando esta ficha ES el padre: si hay `parent`, lo
|
|
1906
|
+
que se lista son hermanos y este producto no manda
|
|
1907
|
+
sobre su vínculo. */}
|
|
1908
|
+
{!family.parent && !variant.isCurrent && (
|
|
1909
|
+
<button
|
|
1910
|
+
type="button"
|
|
1911
|
+
className="desvincular"
|
|
1912
|
+
title="Desvincular este hijo"
|
|
1913
|
+
onClick={() =>
|
|
1914
|
+
setPorDesvincular({
|
|
1915
|
+
mode: "child",
|
|
1916
|
+
childArticleId: variant.articleId,
|
|
1917
|
+
childName: variant.name,
|
|
1918
|
+
childUpc: variant.upc,
|
|
1919
|
+
parentName: state.product?.name,
|
|
1920
|
+
parentUpc: state.product?.upc,
|
|
1921
|
+
})
|
|
1922
|
+
}
|
|
1923
|
+
>
|
|
1924
|
+
Desvincular
|
|
1925
|
+
</button>
|
|
1926
|
+
)}
|
|
1927
|
+
</div>
|
|
1799
1928
|
))}
|
|
1800
1929
|
|
|
1801
1930
|
{family && !family.error && !family.variants?.length && (
|
|
@@ -208,6 +208,36 @@ export const Container = styled.div`
|
|
|
208
208
|
color: ${GlobalColors.gray};
|
|
209
209
|
}
|
|
210
210
|
|
|
211
|
+
/* La fila es un botón que navega; desvincular tiene que quedar FUERA de
|
|
212
|
+
él —un botón dentro de otro no es HTML válido y el clic se confunde—. */
|
|
213
|
+
.family-item {
|
|
214
|
+
display: flex;
|
|
215
|
+
align-items: center;
|
|
216
|
+
gap: 4px;
|
|
217
|
+
}
|
|
218
|
+
|
|
219
|
+
.family-item .family-row {
|
|
220
|
+
flex: 1;
|
|
221
|
+
min-width: 0;
|
|
222
|
+
}
|
|
223
|
+
|
|
224
|
+
.desvincular {
|
|
225
|
+
flex-shrink: 0;
|
|
226
|
+
border: none;
|
|
227
|
+
background: none;
|
|
228
|
+
padding: 4px 6px;
|
|
229
|
+
cursor: pointer;
|
|
230
|
+
font-family: ${FontFamily.Raleway_500};
|
|
231
|
+
font-size: 10px;
|
|
232
|
+
color: ${GlobalColors.gray};
|
|
233
|
+
text-decoration: underline;
|
|
234
|
+
white-space: nowrap;
|
|
235
|
+
}
|
|
236
|
+
|
|
237
|
+
.desvincular:hover {
|
|
238
|
+
color: ${PINK};
|
|
239
|
+
}
|
|
240
|
+
|
|
211
241
|
.family-row {
|
|
212
242
|
display: flex;
|
|
213
243
|
align-items: baseline;
|
|
@@ -51,6 +51,7 @@ import { ProductImageModal } from "../../organisms/ProductImageModal";
|
|
|
51
51
|
import { VersionSelector } from "../../organisms/VersionSelector";
|
|
52
52
|
import { Modal } from "../../organisms/Modal";
|
|
53
53
|
import { LinkVariantsModal } from "../../organisms/LinkVariantsModal";
|
|
54
|
+
import { UnlinkVariantModal } from "../../organisms/UnlinkVariantModal";
|
|
54
55
|
import { PropagateChangesModal } from "../../organisms/PropagateChangesModal";
|
|
55
56
|
import { readPayload } from "../../organisms/LinkVariantsModal/api";
|
|
56
57
|
|
|
@@ -186,6 +187,9 @@ const RetailerProductEditionView = ({
|
|
|
186
187
|
const [family, setFamily] = useState(null);
|
|
187
188
|
const [showFamily, setShowFamily] = useState(false);
|
|
188
189
|
const [loadingVariant, setLoadingVariant] = useState(null);
|
|
190
|
+
// Qué vínculo se está por romper, o null si no hay diálogo abierto. Guarda al
|
|
191
|
+
// HIJO, que es quien lleva la relación y lo que pide la acción `unlink`.
|
|
192
|
+
const [porDesvincular, setPorDesvincular] = useState(null);
|
|
189
193
|
// Por qué un salto no se pudo hacer. Vive aparte de `family.error` porque no
|
|
190
194
|
// es un fallo al leer la familia, sino al abrir uno de sus miembros.
|
|
191
195
|
const [avisoFamilia, setAvisoFamilia] = useState(null);
|
|
@@ -858,6 +862,69 @@ const RetailerProductEditionView = ({
|
|
|
858
862
|
En ese caso no hay nada que abrir aquí, y se dice en vez de navegar a una
|
|
859
863
|
pantalla que mostraría el producto equivocado.
|
|
860
864
|
*/
|
|
865
|
+
/*
|
|
866
|
+
Después de desvincular, esta ficha cambia de forma: si se soltó de su padre
|
|
867
|
+
vuelve a poder ligar, y si soltó a un hijo pierde uno de la cuenta. En esta
|
|
868
|
+
pantalla el bloque de parentesco se decide con `family`, así que hay que
|
|
869
|
+
repreguntarla —vaciarla lo haría desaparecer, que es el error que ya se
|
|
870
|
+
cometió una vez aquí—.
|
|
871
|
+
*/
|
|
872
|
+
const handleOnUnlinked = (result) => {
|
|
873
|
+
if (!result) return;
|
|
874
|
+
|
|
875
|
+
const updatedProduct =
|
|
876
|
+
result.mode === "self"
|
|
877
|
+
? { ...state.product, parentArticleId: null, variantsCount: 0 }
|
|
878
|
+
: {
|
|
879
|
+
...state.product,
|
|
880
|
+
variantsCount: Math.max((state.product?.variantsCount ?? 1) - 1, 0),
|
|
881
|
+
};
|
|
882
|
+
|
|
883
|
+
dispatch({ type: "SET_PRODUCT", payload: updatedProduct });
|
|
884
|
+
|
|
885
|
+
// Se PARCHEA lo guardado: ahí vive el producto con la forma del listado
|
|
886
|
+
// —`article` anidado, servicios, cadenas— y escribir encima la versión
|
|
887
|
+
// aplanada de esta pantalla lo dejaría mutilado en el siguiente F5.
|
|
888
|
+
try {
|
|
889
|
+
const guardado = JSON.parse(
|
|
890
|
+
sessionStorage.getItem("productSelected") ?? "null"
|
|
891
|
+
);
|
|
892
|
+
|
|
893
|
+
if (guardado) {
|
|
894
|
+
sessionStorage.setItem(
|
|
895
|
+
"productSelected",
|
|
896
|
+
JSON.stringify({
|
|
897
|
+
...guardado,
|
|
898
|
+
variantsCount: updatedProduct.variantsCount,
|
|
899
|
+
parentArticleId: updatedProduct.parentArticleId ?? null,
|
|
900
|
+
})
|
|
901
|
+
);
|
|
902
|
+
}
|
|
903
|
+
} catch (error) {
|
|
904
|
+
console.error("unlinkVariant/sessionStorage: ", error);
|
|
905
|
+
}
|
|
906
|
+
|
|
907
|
+
/*
|
|
908
|
+
Si ya no queda familia, se cierra el desplegable: su botón desaparece con
|
|
909
|
+
el vínculo, y dejarlo abierto pinta una caja que dice «no tiene hijos» y
|
|
910
|
+
que nadie puede cerrar.
|
|
911
|
+
*/
|
|
912
|
+
if (
|
|
913
|
+
!updatedProduct.parentArticleId &&
|
|
914
|
+
(updatedProduct.variantsCount ?? 0) === 0
|
|
915
|
+
) {
|
|
916
|
+
setShowFamily(false);
|
|
917
|
+
}
|
|
918
|
+
|
|
919
|
+
loadFamily(true);
|
|
920
|
+
|
|
921
|
+
// Al desvincular el servidor pone `is_overridden = 0` en todas las
|
|
922
|
+
// versiones, así que los chips de herencia tienen que dejar de dibujarse.
|
|
923
|
+
// Sin `enableLoading` para no parpadear el esqueleto encima de una pantalla
|
|
924
|
+
// que ya está pintada.
|
|
925
|
+
loadData(false);
|
|
926
|
+
};
|
|
927
|
+
|
|
861
928
|
const openFamilyMember = async (articleId) => {
|
|
862
929
|
setLoadingVariant(articleId);
|
|
863
930
|
setAvisoFamilia(null);
|
|
@@ -1064,17 +1131,73 @@ const RetailerProductEditionView = ({
|
|
|
1064
1131
|
await saveDescriptions(token);
|
|
1065
1132
|
break;
|
|
1066
1133
|
case "Ficha técnica": {
|
|
1067
|
-
// Guardar un padre con hijos abre la decisión de propagar. Sin este
|
|
1068
|
-
// paso el cambio se queda solo en el padre y los hijos se van
|
|
1069
|
-
// separando de él sin que nadie lo note.
|
|
1070
|
-
const esPadre = hijosDelProducto > 0;
|
|
1071
1134
|
const cambios = cambiosDeFichaTecnica();
|
|
1072
1135
|
|
|
1073
|
-
if (
|
|
1074
|
-
|
|
1075
|
-
|
|
1076
|
-
|
|
1077
|
-
|
|
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 });
|
|
1078
1201
|
return;
|
|
1079
1202
|
}
|
|
1080
1203
|
|
|
@@ -1857,6 +1980,14 @@ const RetailerProductEditionView = ({
|
|
|
1857
1980
|
onLinked={handleOnLinkedVariants}
|
|
1858
1981
|
/>
|
|
1859
1982
|
)}
|
|
1983
|
+
{porDesvincular && (
|
|
1984
|
+
<UnlinkVariantModal
|
|
1985
|
+
{...porDesvincular}
|
|
1986
|
+
token={token}
|
|
1987
|
+
onClose={() => setPorDesvincular(null)}
|
|
1988
|
+
onUnlinked={handleOnUnlinked}
|
|
1989
|
+
/>
|
|
1990
|
+
)}
|
|
1860
1991
|
{showRejectModal && (
|
|
1861
1992
|
<Modal
|
|
1862
1993
|
title={
|
|
@@ -2117,43 +2248,90 @@ const RetailerProductEditionView = ({
|
|
|
2117
2248
|
</p>
|
|
2118
2249
|
)}
|
|
2119
2250
|
|
|
2251
|
+
{/* Desvincular se ofrece donde se ve el vínculo, y solo en la
|
|
2252
|
+
dirección que corresponde a esta ficha: desde un hijo, sobre
|
|
2253
|
+
su padre; desde un padre, sobre cada hijo. A un hermano no:
|
|
2254
|
+
para eso se abre su ficha. */}
|
|
2120
2255
|
{family?.parent && (
|
|
2121
|
-
<
|
|
2122
|
-
|
|
2123
|
-
|
|
2124
|
-
|
|
2125
|
-
|
|
2126
|
-
|
|
2127
|
-
|
|
2128
|
-
|
|
2129
|
-
|
|
2130
|
-
|
|
2131
|
-
|
|
2132
|
-
|
|
2133
|
-
|
|
2134
|
-
|
|
2256
|
+
<div className="family-item">
|
|
2257
|
+
<button
|
|
2258
|
+
type="button"
|
|
2259
|
+
className="family-row parent"
|
|
2260
|
+
disabled={loadingVariant !== null}
|
|
2261
|
+
onClick={() => openFamilyMember(family.parent.articleId)}
|
|
2262
|
+
>
|
|
2263
|
+
<span className="role">
|
|
2264
|
+
{loadingVariant === family.parent.articleId
|
|
2265
|
+
? "Abriendo…"
|
|
2266
|
+
: "Producto padre"}
|
|
2267
|
+
</span>
|
|
2268
|
+
<span className="name">{family.parent.name}</span>
|
|
2269
|
+
<span className="upc">{family.parent.upc}</span>
|
|
2270
|
+
</button>
|
|
2271
|
+
|
|
2272
|
+
<button
|
|
2273
|
+
type="button"
|
|
2274
|
+
className="desvincular"
|
|
2275
|
+
title="Dejar de ser hijo de este producto"
|
|
2276
|
+
onClick={() =>
|
|
2277
|
+
setPorDesvincular({
|
|
2278
|
+
mode: "self",
|
|
2279
|
+
childArticleId: state.product?.id_article,
|
|
2280
|
+
childName: state.product?.name,
|
|
2281
|
+
childUpc: state.product?.upc,
|
|
2282
|
+
parentName: family.parent.name,
|
|
2283
|
+
parentUpc: family.parent.upc,
|
|
2284
|
+
})
|
|
2285
|
+
}
|
|
2286
|
+
>
|
|
2287
|
+
Desvincular
|
|
2288
|
+
</button>
|
|
2289
|
+
</div>
|
|
2135
2290
|
)}
|
|
2136
2291
|
|
|
2137
2292
|
{family?.variants?.map((variant) => (
|
|
2138
|
-
<
|
|
2139
|
-
|
|
2140
|
-
|
|
2141
|
-
|
|
2142
|
-
|
|
2143
|
-
|
|
2144
|
-
|
|
2145
|
-
|
|
2146
|
-
|
|
2147
|
-
|
|
2148
|
-
|
|
2149
|
-
|
|
2150
|
-
|
|
2151
|
-
|
|
2152
|
-
|
|
2153
|
-
|
|
2154
|
-
|
|
2155
|
-
|
|
2156
|
-
|
|
2293
|
+
<div className="family-item" key={variant.articleId}>
|
|
2294
|
+
<button
|
|
2295
|
+
type="button"
|
|
2296
|
+
className={`family-row${
|
|
2297
|
+
variant.isCurrent ? " current" : ""
|
|
2298
|
+
}`}
|
|
2299
|
+
disabled={variant.isCurrent || loadingVariant !== null}
|
|
2300
|
+
onClick={() => openFamilyMember(variant.articleId)}
|
|
2301
|
+
>
|
|
2302
|
+
<span className="role">
|
|
2303
|
+
{loadingVariant === variant.articleId
|
|
2304
|
+
? "Abriendo…"
|
|
2305
|
+
: variant.isCurrent
|
|
2306
|
+
? "Estás aquí"
|
|
2307
|
+
: "Hijo"}
|
|
2308
|
+
</span>
|
|
2309
|
+
<span className="name">{variant.name}</span>
|
|
2310
|
+
<span className="upc">{variant.upc}</span>
|
|
2311
|
+
</button>
|
|
2312
|
+
|
|
2313
|
+
{/* Solo cuando esta ficha ES el padre: si hay `parent`, lo
|
|
2314
|
+
que se lista son hermanos. */}
|
|
2315
|
+
{!family.parent && !variant.isCurrent && (
|
|
2316
|
+
<button
|
|
2317
|
+
type="button"
|
|
2318
|
+
className="desvincular"
|
|
2319
|
+
title="Desvincular este hijo"
|
|
2320
|
+
onClick={() =>
|
|
2321
|
+
setPorDesvincular({
|
|
2322
|
+
mode: "child",
|
|
2323
|
+
childArticleId: variant.articleId,
|
|
2324
|
+
childName: variant.name,
|
|
2325
|
+
childUpc: variant.upc,
|
|
2326
|
+
parentName: state.product?.name,
|
|
2327
|
+
parentUpc: state.product?.upc,
|
|
2328
|
+
})
|
|
2329
|
+
}
|
|
2330
|
+
>
|
|
2331
|
+
Desvincular
|
|
2332
|
+
</button>
|
|
2333
|
+
)}
|
|
2334
|
+
</div>
|
|
2157
2335
|
))}
|
|
2158
2336
|
|
|
2159
2337
|
{family && !family.error && !family.variants?.length && (
|
|
@@ -215,6 +215,36 @@ export const Container = styled.div`
|
|
|
215
215
|
color: ${GlobalColors.gray};
|
|
216
216
|
}
|
|
217
217
|
|
|
218
|
+
/* La fila es un botón que navega; desvincular tiene que quedar FUERA de
|
|
219
|
+
él —un botón dentro de otro no es HTML válido y el clic se confunde—. */
|
|
220
|
+
.family-item {
|
|
221
|
+
display: flex;
|
|
222
|
+
align-items: center;
|
|
223
|
+
gap: 4px;
|
|
224
|
+
}
|
|
225
|
+
|
|
226
|
+
.family-item .family-row {
|
|
227
|
+
flex: 1;
|
|
228
|
+
min-width: 0;
|
|
229
|
+
}
|
|
230
|
+
|
|
231
|
+
.desvincular {
|
|
232
|
+
flex-shrink: 0;
|
|
233
|
+
border: none;
|
|
234
|
+
background: none;
|
|
235
|
+
padding: 4px 6px;
|
|
236
|
+
cursor: pointer;
|
|
237
|
+
font-family: ${FontFamily.Raleway_500};
|
|
238
|
+
font-size: 10px;
|
|
239
|
+
color: ${GlobalColors.gray};
|
|
240
|
+
text-decoration: underline;
|
|
241
|
+
white-space: nowrap;
|
|
242
|
+
}
|
|
243
|
+
|
|
244
|
+
.desvincular:hover {
|
|
245
|
+
color: ${PINK};
|
|
246
|
+
}
|
|
247
|
+
|
|
218
248
|
.family-row {
|
|
219
249
|
display: flex;
|
|
220
250
|
align-items: baseline;
|
package/src/index.js
CHANGED
|
@@ -89,6 +89,7 @@ export * from "./components/organisms/ImagesGroup/index";
|
|
|
89
89
|
export * from "./components/organisms/InputGroup/index";
|
|
90
90
|
export * from "./components/organisms/LinkVariantsModal/index";
|
|
91
91
|
export * from "./components/organisms/PropagateChangesModal/index";
|
|
92
|
+
export * from "./components/organisms/UnlinkVariantModal/index";
|
|
92
93
|
export * from "./components/organisms/ProductImageModal/index";
|
|
93
94
|
export * from "./components/organisms/Chat/index";
|
|
94
95
|
export * from "./components/organisms/Modal/index";
|