contentoh-components-library 21.6.25 → 21.6.27
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/molecules/SingleSelect/index.js +198 -0
- package/dist/components/molecules/SingleSelect/styles.js +74 -0
- package/dist/components/organisms/LinkVariantsModal/index.js +149 -120
- package/dist/components/organisms/LinkVariantsModal/styles.js +2 -2
- package/dist/index.js +43 -30
- package/package.json +1 -1
- package/src/components/molecules/SingleSelect/index.js +183 -0
- package/src/components/molecules/SingleSelect/styles.js +132 -0
- package/src/components/organisms/LinkVariantsModal/index.js +157 -119
- package/src/components/organisms/LinkVariantsModal/styles.js +29 -19
- package/src/index.js +1 -0
|
@@ -2,6 +2,7 @@ import { useCallback, useEffect, useMemo, useState } from "react";
|
|
|
2
2
|
import axios from "axios";
|
|
3
3
|
|
|
4
4
|
import { readPayload, ApiError } from "./api";
|
|
5
|
+
import { SingleSelect } from "../../molecules/SingleSelect";
|
|
5
6
|
import { getProductImage, getRetailerPicture } from "../../../_utils/helper";
|
|
6
7
|
|
|
7
8
|
import {
|
|
@@ -303,6 +304,19 @@ export const LinkVariantsModal = ({
|
|
|
303
304
|
};
|
|
304
305
|
|
|
305
306
|
const toggleChoice = (dimension, id) => {
|
|
307
|
+
// Marcar un elemento ES elegir cuáles, así que el radio se pone solo. Antes
|
|
308
|
+
// se podían marcar atributos con los dos radios en blanco: la pantalla decía
|
|
309
|
+
// una cosa y el estado era otra.
|
|
310
|
+
//
|
|
311
|
+
// Se hace así y no dejando "elegir cuáles" marcado de entrada porque eso
|
|
312
|
+
// daría las cuatro dimensiones por decididas desde el principio y habilitaría
|
|
313
|
+
// Ligar sin que nadie hubiera revisado nada.
|
|
314
|
+
setModes((current) =>
|
|
315
|
+
current[dimension] === null
|
|
316
|
+
? { ...current, [dimension]: "some" }
|
|
317
|
+
: current
|
|
318
|
+
);
|
|
319
|
+
|
|
306
320
|
setChosen((current) => {
|
|
307
321
|
const lista = current[dimension];
|
|
308
322
|
return {
|
|
@@ -360,11 +374,15 @@ export const LinkVariantsModal = ({
|
|
|
360
374
|
categories: [],
|
|
361
375
|
};
|
|
362
376
|
}
|
|
377
|
+
// El tercer nivel no viene como {id, name} sino como
|
|
378
|
+
// {category_id, subcategory}: con el mapeo de los otros dos, la
|
|
379
|
+
// categoría salía en la lista pero sin texto. Mismo criterio que usa
|
|
380
|
+
// el alta de productos, incluido el separador legible.
|
|
363
381
|
return {
|
|
364
382
|
...current,
|
|
365
383
|
categories: lista.map((item) => ({
|
|
366
|
-
value: item.
|
|
367
|
-
label: item.
|
|
384
|
+
value: item.category_id,
|
|
385
|
+
label: (item.subcategory ?? "").replace("|", " > "),
|
|
368
386
|
})),
|
|
369
387
|
};
|
|
370
388
|
});
|
|
@@ -385,6 +403,22 @@ export const LinkVariantsModal = ({
|
|
|
385
403
|
}
|
|
386
404
|
}, [step, filters.retailers.length, loadFilters]);
|
|
387
405
|
|
|
406
|
+
/*
|
|
407
|
+
Quita una cadena agregada a mano. Solo esas: las del padre se desmarcan con
|
|
408
|
+
su casilla, pero siguen existiendo y volverán a aparecer.
|
|
409
|
+
*/
|
|
410
|
+
const removeExtraRetailer = (clave) => {
|
|
411
|
+
setExtraRetailers((current) =>
|
|
412
|
+
current.filter((item) => item.clave !== clave)
|
|
413
|
+
);
|
|
414
|
+
setChosen((current) => ({
|
|
415
|
+
...current,
|
|
416
|
+
categoryRetailers: current.categoryRetailers.filter(
|
|
417
|
+
(item) => item !== clave
|
|
418
|
+
),
|
|
419
|
+
}));
|
|
420
|
+
};
|
|
421
|
+
|
|
388
422
|
const addRetailer = () => {
|
|
389
423
|
const retailerId = Number(newRetailer);
|
|
390
424
|
const categoryId = Number(newCategory);
|
|
@@ -555,7 +589,9 @@ export const LinkVariantsModal = ({
|
|
|
555
589
|
const decidida = (dimension) => {
|
|
556
590
|
if (modes[dimension] !== null) return true;
|
|
557
591
|
if (dimension === "categoryRetailers") {
|
|
558
|
-
return
|
|
592
|
+
return (
|
|
593
|
+
offered.categoryRetailers.length === 0 && extraRetailers.length === 0
|
|
594
|
+
);
|
|
559
595
|
}
|
|
560
596
|
// Una imagen cuyo archivo no existe no es algo sobre lo que se pueda decidir.
|
|
561
597
|
return dimension === "images"
|
|
@@ -874,63 +910,66 @@ export const LinkVariantsModal = ({
|
|
|
874
910
|
</span>
|
|
875
911
|
</span>
|
|
876
912
|
</label>
|
|
913
|
+
|
|
914
|
+
{/* Solo las agregadas a mano se pueden quitar. Las del
|
|
915
|
+
padre se desmarcan con su casilla, pero siguen ahí. */}
|
|
916
|
+
{!item.delPadre && (
|
|
917
|
+
<button
|
|
918
|
+
type="button"
|
|
919
|
+
className="quitar"
|
|
920
|
+
title="Quitar esta cadena"
|
|
921
|
+
aria-label={`Quitar ${item.retailerName}`}
|
|
922
|
+
onClick={() => removeExtraRetailer(item.clave)}
|
|
923
|
+
>
|
|
924
|
+
×
|
|
925
|
+
</button>
|
|
926
|
+
)}
|
|
877
927
|
</li>
|
|
878
928
|
))}
|
|
879
929
|
</List>
|
|
880
930
|
|
|
881
931
|
<AddRetailer>
|
|
882
|
-
<
|
|
932
|
+
<SingleSelect
|
|
933
|
+
id="link-variants-retailer"
|
|
934
|
+
placeholder="Agregar otra cadena…"
|
|
935
|
+
options={filters.retailers}
|
|
883
936
|
value={newRetailer}
|
|
884
|
-
|
|
885
|
-
|
|
886
|
-
setNewRetailer(valor);
|
|
937
|
+
loading={loadingFilters && !filters.retailers.length}
|
|
938
|
+
onChange={(valor) => {
|
|
939
|
+
setNewRetailer(valor ?? "");
|
|
887
940
|
setNewDepartment("");
|
|
888
941
|
setNewCategory("");
|
|
889
942
|
if (valor) loadFilters(valor, null);
|
|
890
943
|
}}
|
|
891
|
-
|
|
892
|
-
|
|
893
|
-
|
|
894
|
-
|
|
895
|
-
|
|
896
|
-
</option>
|
|
897
|
-
))}
|
|
898
|
-
</select>
|
|
899
|
-
<select
|
|
944
|
+
/>
|
|
945
|
+
<SingleSelect
|
|
946
|
+
id="link-variants-department"
|
|
947
|
+
placeholder="Departamento…"
|
|
948
|
+
options={filters.departments}
|
|
900
949
|
value={newDepartment}
|
|
901
|
-
disabled={!newRetailer
|
|
902
|
-
|
|
903
|
-
|
|
904
|
-
setNewDepartment(valor);
|
|
950
|
+
disabled={!newRetailer}
|
|
951
|
+
loading={loadingFilters && !!newRetailer}
|
|
952
|
+
onChange={(valor) => {
|
|
953
|
+
setNewDepartment(valor ?? "");
|
|
905
954
|
setNewCategory("");
|
|
906
955
|
if (valor) loadFilters(newRetailer, valor);
|
|
907
956
|
}}
|
|
908
|
-
|
|
909
|
-
|
|
910
|
-
|
|
911
|
-
|
|
912
|
-
|
|
913
|
-
</option>
|
|
914
|
-
))}
|
|
915
|
-
</select>
|
|
916
|
-
<select
|
|
957
|
+
/>
|
|
958
|
+
<SingleSelect
|
|
959
|
+
id="link-variants-category"
|
|
960
|
+
placeholder="Categoría…"
|
|
961
|
+
options={filters.categories}
|
|
917
962
|
value={newCategory}
|
|
918
|
-
disabled={!newDepartment
|
|
919
|
-
|
|
920
|
-
|
|
921
|
-
|
|
922
|
-
{filters.categories.map((item) => (
|
|
923
|
-
<option key={item.value} value={item.value}>
|
|
924
|
-
{item.label}
|
|
925
|
-
</option>
|
|
926
|
-
))}
|
|
927
|
-
</select>
|
|
963
|
+
disabled={!newDepartment}
|
|
964
|
+
loading={loadingFilters && !!newDepartment}
|
|
965
|
+
onChange={(valor) => setNewCategory(valor ?? "")}
|
|
966
|
+
/>
|
|
928
967
|
<button
|
|
929
968
|
type="button"
|
|
930
969
|
onClick={addRetailer}
|
|
931
970
|
disabled={!newRetailer || !newCategory || loadingFilters}
|
|
932
971
|
>
|
|
933
|
-
|
|
972
|
+
Agregar
|
|
934
973
|
</button>
|
|
935
974
|
</AddRetailer>
|
|
936
975
|
</Block>
|
|
@@ -1111,89 +1150,85 @@ export const LinkVariantsModal = ({
|
|
|
1111
1150
|
)}
|
|
1112
1151
|
</Block>
|
|
1113
1152
|
|
|
1114
|
-
|
|
1115
|
-
|
|
1116
|
-
|
|
1117
|
-
|
|
1118
|
-
|
|
1119
|
-
|
|
1120
|
-
<
|
|
1121
|
-
|
|
1153
|
+
{/* Sin imágenes utilizables el bloque no se dibuja: un paso que
|
|
1154
|
+
solo sirve para decir que no hay nada es ruido, y ademas ya no
|
|
1155
|
+
hay decision que tomar ahi. */}
|
|
1156
|
+
{imagenesUtiles.length > 0 && (
|
|
1157
|
+
<Block>
|
|
1158
|
+
<h3>
|
|
1159
|
+
<span className="paso">4</span> Imágenes
|
|
1160
|
+
</h3>
|
|
1161
|
+
<Choices>
|
|
1162
|
+
<label>
|
|
1163
|
+
<input
|
|
1164
|
+
type="radio"
|
|
1165
|
+
name={`images-${activeChild ?? "todos"}`}
|
|
1166
|
+
checked={modes.images === "all"}
|
|
1167
|
+
onChange={() =>
|
|
1168
|
+
chooseMode(
|
|
1169
|
+
"images",
|
|
1170
|
+
"all",
|
|
1171
|
+
imagenesUtiles.map((item) => item.id)
|
|
1172
|
+
)
|
|
1173
|
+
}
|
|
1174
|
+
/>
|
|
1175
|
+
<span>
|
|
1176
|
+
Todas
|
|
1177
|
+
<span className="hint">
|
|
1178
|
+
{imagenesUtiles.length} imagen(es) en el padre.
|
|
1179
|
+
</span>
|
|
1180
|
+
</span>
|
|
1181
|
+
</label>
|
|
1182
|
+
<label>
|
|
1183
|
+
<input
|
|
1184
|
+
type="radio"
|
|
1185
|
+
name={`images-${activeChild ?? "todos"}`}
|
|
1186
|
+
checked={modes.images === "some"}
|
|
1187
|
+
onChange={() => chooseMode("images", "some", [])}
|
|
1188
|
+
/>
|
|
1189
|
+
<span>
|
|
1190
|
+
Elegir cuáles
|
|
1191
|
+
<span className="hint">
|
|
1192
|
+
Útil cuando la variante cambia de color o de empaque.
|
|
1193
|
+
</span>
|
|
1194
|
+
</span>
|
|
1195
|
+
</label>
|
|
1196
|
+
</Choices>
|
|
1197
|
+
|
|
1198
|
+
<ImageGrid>
|
|
1199
|
+
{imagenesUtiles.map((image) => (
|
|
1200
|
+
<label
|
|
1201
|
+
key={image.id}
|
|
1202
|
+
className={[
|
|
1203
|
+
chosen.images.includes(image.id) ? "picked" : "",
|
|
1204
|
+
modes.images === "all" ? "locked" : "",
|
|
1205
|
+
]
|
|
1206
|
+
.filter(Boolean)
|
|
1207
|
+
.join(" ")}
|
|
1208
|
+
>
|
|
1122
1209
|
<input
|
|
1123
|
-
type="
|
|
1124
|
-
|
|
1125
|
-
checked={
|
|
1126
|
-
onChange={() =>
|
|
1127
|
-
|
|
1128
|
-
|
|
1129
|
-
|
|
1130
|
-
|
|
1210
|
+
type="checkbox"
|
|
1211
|
+
disabled={modes.images === "all"}
|
|
1212
|
+
checked={chosen.images.includes(image.id)}
|
|
1213
|
+
onChange={() => toggleChoice("images", image.id)}
|
|
1214
|
+
/>
|
|
1215
|
+
<img
|
|
1216
|
+
src={getProductImage(image.src)}
|
|
1217
|
+
alt=""
|
|
1218
|
+
onError={() =>
|
|
1219
|
+
setBrokenImages((current) =>
|
|
1220
|
+
current.includes(image.id)
|
|
1221
|
+
? current
|
|
1222
|
+
: [...current, image.id]
|
|
1131
1223
|
)
|
|
1132
1224
|
}
|
|
1133
1225
|
/>
|
|
1134
|
-
<span>
|
|
1135
|
-
Todas
|
|
1136
|
-
<span className="hint">
|
|
1137
|
-
{imagenesUtiles.length} imagen(es) en el padre.
|
|
1138
|
-
</span>
|
|
1139
|
-
</span>
|
|
1226
|
+
<span>{image.imageType || "Sin tipo de toma"}</span>
|
|
1140
1227
|
</label>
|
|
1141
|
-
|
|
1142
|
-
|
|
1143
|
-
|
|
1144
|
-
|
|
1145
|
-
checked={modes.images === "some"}
|
|
1146
|
-
onChange={() => chooseMode("images", "some", [])}
|
|
1147
|
-
/>
|
|
1148
|
-
<span>
|
|
1149
|
-
Elegir cuáles
|
|
1150
|
-
<span className="hint">
|
|
1151
|
-
Útil cuando la variante cambia de color o de
|
|
1152
|
-
empaque.
|
|
1153
|
-
</span>
|
|
1154
|
-
</span>
|
|
1155
|
-
</label>
|
|
1156
|
-
</Choices>
|
|
1157
|
-
|
|
1158
|
-
<ImageGrid>
|
|
1159
|
-
{imagenesUtiles.map((image) => (
|
|
1160
|
-
<label
|
|
1161
|
-
key={image.id}
|
|
1162
|
-
className={[
|
|
1163
|
-
chosen.images.includes(image.id) ? "picked" : "",
|
|
1164
|
-
modes.images === "all" ? "locked" : "",
|
|
1165
|
-
]
|
|
1166
|
-
.filter(Boolean)
|
|
1167
|
-
.join(" ")}
|
|
1168
|
-
>
|
|
1169
|
-
<input
|
|
1170
|
-
type="checkbox"
|
|
1171
|
-
disabled={modes.images === "all"}
|
|
1172
|
-
checked={chosen.images.includes(image.id)}
|
|
1173
|
-
onChange={() => toggleChoice("images", image.id)}
|
|
1174
|
-
/>
|
|
1175
|
-
<img
|
|
1176
|
-
src={getProductImage(image.src)}
|
|
1177
|
-
alt=""
|
|
1178
|
-
onError={() =>
|
|
1179
|
-
setBrokenImages((current) =>
|
|
1180
|
-
current.includes(image.id)
|
|
1181
|
-
? current
|
|
1182
|
-
: [...current, image.id]
|
|
1183
|
-
)
|
|
1184
|
-
}
|
|
1185
|
-
/>
|
|
1186
|
-
<span>{image.imageType || "Sin tipo de toma"}</span>
|
|
1187
|
-
</label>
|
|
1188
|
-
))}
|
|
1189
|
-
</ImageGrid>
|
|
1190
|
-
</>
|
|
1191
|
-
) : (
|
|
1192
|
-
<Message>
|
|
1193
|
-
El producto padre no tiene imágenes que heredar.
|
|
1194
|
-
</Message>
|
|
1195
|
-
)}
|
|
1196
|
-
</Block>
|
|
1228
|
+
))}
|
|
1229
|
+
</ImageGrid>
|
|
1230
|
+
</Block>
|
|
1231
|
+
)}
|
|
1197
1232
|
|
|
1198
1233
|
{/* Es la diferencia entre link y propagate, y es la que sorprende:
|
|
1199
1234
|
aquí lo elegido se escribe encima aunque la variante lo hubiera
|
|
@@ -1212,7 +1247,10 @@ export const LinkVariantsModal = ({
|
|
|
1212
1247
|
? selectionLabel
|
|
1213
1248
|
: todoDecidido
|
|
1214
1249
|
? "Todo revisado. Puedes ligar."
|
|
1215
|
-
:
|
|
1250
|
+
: /* Contar productos solo tiene sentido cuando hay varios y cada
|
|
1251
|
+
uno lleva su propia selección. Con uno, lo útil es decir qué
|
|
1252
|
+
falta, no repetir que falta "1 producto". */
|
|
1253
|
+
!applyToAll && mode === "children" && selected.length > 1
|
|
1216
1254
|
? `Faltan ${
|
|
1217
1255
|
selected.filter((id) => !decididoPara(id)).length
|
|
1218
1256
|
} producto(s) por revisar.`
|
|
@@ -192,6 +192,32 @@ export const List = styled.ul`
|
|
|
192
192
|
flex-shrink: 0;
|
|
193
193
|
}
|
|
194
194
|
|
|
195
|
+
li {
|
|
196
|
+
display: flex;
|
|
197
|
+
align-items: center;
|
|
198
|
+
}
|
|
199
|
+
|
|
200
|
+
li > label {
|
|
201
|
+
flex: 1;
|
|
202
|
+
min-width: 0;
|
|
203
|
+
}
|
|
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
|
+
|
|
195
221
|
.count {
|
|
196
222
|
margin-left: auto;
|
|
197
223
|
font-size: 12px;
|
|
@@ -363,24 +389,12 @@ export const ImageGrid = styled.div`
|
|
|
363
389
|
export const AddRetailer = styled.div`
|
|
364
390
|
display: flex;
|
|
365
391
|
flex-wrap: wrap;
|
|
392
|
+
align-items: flex-end;
|
|
366
393
|
gap: 8px;
|
|
367
394
|
margin-bottom: 16px;
|
|
368
395
|
|
|
369
|
-
select {
|
|
370
|
-
flex: 1 1 140px;
|
|
371
|
-
min-width: 0;
|
|
372
|
-
height: 32px;
|
|
373
|
-
padding: 0 8px;
|
|
374
|
-
border: 1px solid ${GlobalColors.s2};
|
|
375
|
-
border-radius: 6px;
|
|
376
|
-
font-family: ${FontFamily.Raleway};
|
|
377
|
-
font-size: 13px;
|
|
378
|
-
color: ${GlobalColors.deep_gray};
|
|
379
|
-
background-color: ${GlobalColors.white};
|
|
380
|
-
}
|
|
381
|
-
|
|
382
396
|
button {
|
|
383
|
-
height:
|
|
397
|
+
height: 38px;
|
|
384
398
|
padding: 0 14px;
|
|
385
399
|
border: 1px solid ${PINK};
|
|
386
400
|
border-radius: 6px;
|
|
@@ -389,11 +403,7 @@ export const AddRetailer = styled.div`
|
|
|
389
403
|
font-size: 13px;
|
|
390
404
|
color: ${PINK};
|
|
391
405
|
cursor: pointer;
|
|
392
|
-
|
|
393
|
-
|
|
394
|
-
select:disabled {
|
|
395
|
-
opacity: 0.5;
|
|
396
|
-
cursor: default;
|
|
406
|
+
flex-shrink: 0;
|
|
397
407
|
}
|
|
398
408
|
|
|
399
409
|
button:disabled {
|
package/src/index.js
CHANGED
|
@@ -67,6 +67,7 @@ export * from "./components/molecules/TagAndInput/index";
|
|
|
67
67
|
export * from "./components/molecules/VerificationCodeResetPasswordLogin/index";
|
|
68
68
|
export * from "./components/molecules/RetailerSelector/index";
|
|
69
69
|
export * from "./components/molecules/CustomSelect/index";
|
|
70
|
+
export * from "./components/molecules/SingleSelect/index";
|
|
70
71
|
export * from "./components/molecules/ButtonDownloadFile/index";
|
|
71
72
|
export * from "./components/molecules/ImageTooltip/index";
|
|
72
73
|
export * from "./components/molecules/SelectV2/index";
|