contentoh-components-library 21.6.50 → 21.6.51
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/.env.test +0 -1
- package/dist/components/atoms/IconFile/index.js +2 -85
- package/dist/components/atoms/IconFile/styles.js +1 -1
- package/dist/components/atoms/ProductImage/ProductImage.stories.js +2 -70
- package/dist/components/atoms/ProductImage/index.js +27 -100
- package/dist/components/atoms/ProductImage/styles.js +1 -3
- package/dist/components/molecules/EditionActiveImage/index.js +2 -4
- package/dist/components/molecules/GalleryElement/index.js +4 -13
- package/dist/components/molecules/GalleryHeader/index.js +24 -96
- package/dist/components/molecules/GalleryHeader/styles.js +1 -4
- package/dist/components/molecules/ImageSelector/index.js +0 -1
- package/dist/components/organisms/ImagePreviewer/index.js +0 -1
- package/dist/components/organisms/ProductImageModal/index.js +2 -3
- package/dist/components/pages/ProviderProductEdition/index.js +78 -53
- package/dist/components/pages/RetailerProductEdition/context/provider-product-edition.context.js +15 -330
- package/dist/components/pages/RetailerProductEdition/context/provider-product-edition.reducer.js +2 -12
- package/dist/components/pages/RetailerProductEdition/index.js +445 -603
- package/dist/components/pages/RetailerProductEdition/stories/ImageEditor.stories.js +0 -1
- package/dist/components/pages/RetailerProductEdition/stories/TextEditor.stories.js +145 -45
- package/dist/global-files/data.js +3 -16
- package/dist/index.js +90 -142
- package/package.json +1 -1
- package/src/components/atoms/IconFile/index.js +1 -28
- package/src/components/atoms/IconFile/styles.js +0 -8
- package/src/components/atoms/ProductImage/ProductImage.stories.js +0 -38
- package/src/components/atoms/ProductImage/index.js +20 -95
- package/src/components/atoms/ProductImage/styles.js +7 -92
- package/src/components/molecules/EditionActiveImage/index.js +2 -2
- package/src/components/molecules/GalleryElement/index.js +0 -9
- package/src/components/molecules/GalleryHeader/index.js +13 -65
- package/src/components/molecules/GalleryHeader/styles.js +1 -2
- package/src/components/molecules/ImageSelector/index.js +0 -1
- package/src/components/organisms/ImagePreviewer/index.js +0 -1
- package/src/components/organisms/ProductImageModal/index.js +0 -1
- package/src/components/pages/ProviderProductEdition/index.js +36 -3
- package/src/components/pages/RetailerProductEdition/context/provider-product-edition.context.jsx +0 -201
- package/src/components/pages/RetailerProductEdition/context/provider-product-edition.reducer.js +0 -12
- package/src/components/pages/RetailerProductEdition/index.js +179 -360
- package/src/components/pages/RetailerProductEdition/stories/ImageEditor.stories.js +0 -1
- package/src/components/pages/RetailerProductEdition/stories/TextEditor.stories.js +104 -51
- package/src/global-files/data.js +2 -17
- package/src/index.js +0 -4
- package/.claude/launch.json +0 -14
- package/dist/ai/prompts/attribute.prompt.js +0 -28
- package/dist/ai/prompts/description.prompt.js +0 -33
- package/dist/ai/schemas/attribute.schema.js +0 -35
- package/dist/ai/schemas/description.schema.js +0 -27
- package/dist/components/atoms/AssetPreview/AssetPreview.stories.js +0 -118
- package/dist/components/atoms/AssetPreview/index.js +0 -150
- package/dist/components/atoms/AssetPreview/styles.js +0 -28
- package/dist/components/molecules/GalleryAssetElement/GalleryAssetElement.stories.js +0 -147
- package/dist/components/molecules/GalleryAssetElement/index.js +0 -178
- package/dist/components/molecules/GalleryAssetElement/styles.js +0 -26
- package/dist/components/molecules/GalleryTrayTabs/GalleryTrayTabs.stories.js +0 -56
- package/dist/components/molecules/GalleryTrayTabs/index.js +0 -60
- package/dist/components/molecules/GalleryTrayTabs/styles.js +0 -20
- package/dist/components/organisms/DigitalAssetsTray/DigitalAssetsTray.stories.js +0 -171
- package/dist/components/organisms/DigitalAssetsTray/index.js +0 -211
- package/dist/components/organisms/DigitalAssetsTray/styles.js +0 -22
- package/dist/global-files/digital-assets/assetTypes.js +0 -480
- package/dist/global-files/digital-assets/assets.actions.js +0 -64
- package/dist/global-files/digital-assets/assets.adapter.js +0 -304
- package/dist/global-files/digital-assets/assets.mock.js +0 -136
- package/dist/global-files/digital-assets/assets.reducer.js +0 -220
- package/dist/global-files/digital-assets/uploadAssets.js +0 -179
- package/dist/global-files/digital-assets/useDigitalAssets.js +0 -522
- package/docs/activos-digitales.md +0 -273
- package/src/components/atoms/AssetPreview/AssetPreview.stories.js +0 -78
- package/src/components/atoms/AssetPreview/index.js +0 -119
- package/src/components/atoms/AssetPreview/styles.js +0 -190
- package/src/components/molecules/GalleryAssetElement/GalleryAssetElement.stories.js +0 -102
- package/src/components/molecules/GalleryAssetElement/index.js +0 -162
- package/src/components/molecules/GalleryAssetElement/styles.js +0 -159
- package/src/components/molecules/GalleryTrayTabs/GalleryTrayTabs.stories.js +0 -32
- package/src/components/molecules/GalleryTrayTabs/index.js +0 -41
- package/src/components/molecules/GalleryTrayTabs/styles.js +0 -52
- package/src/components/organisms/DigitalAssetsTray/DigitalAssetsTray.stories.js +0 -101
- package/src/components/organisms/DigitalAssetsTray/index.js +0 -189
- package/src/components/organisms/DigitalAssetsTray/styles.js +0 -183
- package/src/global-files/digital-assets/assetTypes.js +0 -419
- package/src/global-files/digital-assets/assets.actions.js +0 -50
- package/src/global-files/digital-assets/assets.adapter.js +0 -229
- package/src/global-files/digital-assets/assets.mock.js +0 -122
- package/src/global-files/digital-assets/assets.reducer.js +0 -197
- package/src/global-files/digital-assets/uploadAssets.js +0 -109
- package/src/global-files/digital-assets/useDigitalAssets.js +0 -329
package/package.json
CHANGED
|
@@ -11,8 +11,6 @@ import {
|
|
|
11
11
|
faFileAudio as IconFileAudio,
|
|
12
12
|
faFileLines as IconFileDefault,
|
|
13
13
|
faFileVideo as IconFileVideo,
|
|
14
|
-
faSwatchbook as IconFileDesign,
|
|
15
|
-
faFilm as IconFileMotion,
|
|
16
14
|
} from "@fortawesome/free-solid-svg-icons";
|
|
17
15
|
import React, { useRef } from "react";
|
|
18
16
|
import { CustomIcon } from "../CustomIcon";
|
|
@@ -43,30 +41,8 @@ export const IconFile = (props) => {
|
|
|
43
41
|
// file csv
|
|
44
42
|
csv: { icon: IconFileCsv, type: "csv" },
|
|
45
43
|
// file image
|
|
44
|
+
svg: { icon: IconFileImg, type: "image" },
|
|
46
45
|
gif: { icon: IconFileImg, type: "image" },
|
|
47
|
-
jpg: { icon: IconFileImg, type: "image" },
|
|
48
|
-
jpeg: { icon: IconFileImg, type: "image" },
|
|
49
|
-
png: { icon: IconFileImg, type: "image" },
|
|
50
|
-
webp: { icon: IconFileImg, type: "image" },
|
|
51
|
-
|
|
52
|
-
// archivos de diseño editables: no se pueden previsualizar en el navegador,
|
|
53
|
-
// así que el icono es lo único que distingue uno de otro en la bandeja.
|
|
54
|
-
psd: { icon: IconFileDesign, type: "design" },
|
|
55
|
-
psb: { icon: IconFileDesign, type: "design" },
|
|
56
|
-
ai: { icon: IconFileDesign, type: "design" },
|
|
57
|
-
eps: { icon: IconFileDesign, type: "design" },
|
|
58
|
-
indd: { icon: IconFileDesign, type: "design" },
|
|
59
|
-
idml: { icon: IconFileDesign, type: "design" },
|
|
60
|
-
sketch: { icon: IconFileDesign, type: "design" },
|
|
61
|
-
fig: { icon: IconFileDesign, type: "design" },
|
|
62
|
-
xd: { icon: IconFileDesign, type: "design" },
|
|
63
|
-
afphoto: { icon: IconFileDesign, type: "design" },
|
|
64
|
-
afdesign: { icon: IconFileDesign, type: "design" },
|
|
65
|
-
svg: { icon: IconFileDesign, type: "design" },
|
|
66
|
-
|
|
67
|
-
// proyectos de animación y edición de video
|
|
68
|
-
aep: { icon: IconFileMotion, type: "motion" },
|
|
69
|
-
prproj: { icon: IconFileMotion, type: "motion" },
|
|
70
46
|
// file pdf
|
|
71
47
|
pdf: { icon: IconFilePdf, type: "pdf" },
|
|
72
48
|
// file power point
|
|
@@ -84,7 +60,6 @@ export const IconFile = (props) => {
|
|
|
84
60
|
rar: { icon: IconFileZip, type: "zip" },
|
|
85
61
|
zip: { icon: IconFileZip, type: "zip" },
|
|
86
62
|
tar: { icon: IconFileZip, type: "zip" },
|
|
87
|
-
"7z": { icon: IconFileZip, type: "zip" },
|
|
88
63
|
|
|
89
64
|
// file code
|
|
90
65
|
xml: { icon: IconFileCode, type: "code" },
|
|
@@ -105,8 +80,6 @@ export const IconFile = (props) => {
|
|
|
105
80
|
// file video
|
|
106
81
|
avi: { icon: IconFileVideo, type: "video" },
|
|
107
82
|
mp4: { icon: IconFileVideo, type: "video" },
|
|
108
|
-
mov: { icon: IconFileVideo, type: "video" },
|
|
109
|
-
webm: { icon: IconFileVideo, type: "video" },
|
|
110
83
|
mpeg: { icon: IconFileVideo, type: "video" },
|
|
111
84
|
mpg: { icon: IconFileVideo, type: "video" },
|
|
112
85
|
wmv: { icon: IconFileVideo, type: "video" },
|
|
@@ -52,14 +52,6 @@ export const Container = styled.div`
|
|
|
52
52
|
color: #1c778e;
|
|
53
53
|
border-color: #1c778e;
|
|
54
54
|
}
|
|
55
|
-
&.icon-design .icon {
|
|
56
|
-
color: #2a5cb8;
|
|
57
|
-
border-color: #2a5cb8;
|
|
58
|
-
}
|
|
59
|
-
&.icon-motion .icon {
|
|
60
|
-
color: #6d3fc0;
|
|
61
|
-
border-color: #6d3fc0;
|
|
62
|
-
}
|
|
63
55
|
&.icon-video .icon {
|
|
64
56
|
color: #a15bb1;
|
|
65
57
|
border-color: #a15bb1;
|
|
@@ -26,41 +26,3 @@ ProductImageDefault.args = {
|
|
|
26
26
|
imageType: "main-image",
|
|
27
27
|
altText: "default image",
|
|
28
28
|
};
|
|
29
|
-
|
|
30
|
-
/*
|
|
31
|
-
Las escalas de video, lado a lado. Las cuatro primeras son miniaturas: sin
|
|
32
|
-
controles, con su duración y un botón de play, y todo el recuadro clicable.
|
|
33
|
-
Las dos últimas son reproductores de verdad.
|
|
34
|
-
*/
|
|
35
|
-
const VIDEO =
|
|
36
|
-
"https://interactive-examples.mdn.mozilla.net/media/cc0-videos/flower.mp4";
|
|
37
|
-
|
|
38
|
-
const Escala = ({ imageType }) => (
|
|
39
|
-
<figure style={{ margin: 0 }}>
|
|
40
|
-
<ProductImage
|
|
41
|
-
img={VIDEO}
|
|
42
|
-
ext="mp4"
|
|
43
|
-
imageType={imageType}
|
|
44
|
-
altText="video de producto"
|
|
45
|
-
onClick={() => console.log("clic en", imageType)}
|
|
46
|
-
/>
|
|
47
|
-
<figcaption style={{ fontFamily: "Lato", fontSize: 11, marginTop: 4 }}>
|
|
48
|
-
{imageType}
|
|
49
|
-
</figcaption>
|
|
50
|
-
</figure>
|
|
51
|
-
);
|
|
52
|
-
|
|
53
|
-
export const VideoMiniaturas = () => (
|
|
54
|
-
<div style={{ display: "flex", gap: 24, alignItems: "flex-start" }}>
|
|
55
|
-
<Escala imageType="carousel-image" />
|
|
56
|
-
<Escala imageType="catalogue-image-small-size" />
|
|
57
|
-
<Escala imageType="catalogue-modal-image" />
|
|
58
|
-
<Escala imageType="catalogue-image-big-size" />
|
|
59
|
-
</div>
|
|
60
|
-
);
|
|
61
|
-
|
|
62
|
-
export const VideoReproductor = () => (
|
|
63
|
-
<div style={{ display: "flex", gap: 24, alignItems: "flex-start" }}>
|
|
64
|
-
<Escala imageType="main-image" />
|
|
65
|
-
</div>
|
|
66
|
-
);
|
|
@@ -1,104 +1,29 @@
|
|
|
1
|
-
import { useState } from "react";
|
|
2
|
-
import ReactImageFallback from "react-image-fallback";
|
|
3
|
-
|
|
4
1
|
import { Container } from "./styles";
|
|
5
|
-
import
|
|
6
|
-
ASSET_PREVIEW,
|
|
7
|
-
formatDuration,
|
|
8
|
-
resolvePreviewKind,
|
|
9
|
-
} from "../../../global-files/digital-assets/assetTypes";
|
|
2
|
+
import ReactImageFallback from "react-image-fallback";
|
|
10
3
|
import defaultProductImage from "../../../assets/images/defaultImages/defaultProductImage.png";
|
|
11
4
|
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
para ser usables; en un recuadro de 60 o de 85 se pintan encimados y no se
|
|
17
|
-
puede ni dar play. Peor aún, un <video> con controles se traga el clic, así
|
|
18
|
-
que un video en el carrusel no se podía ni seleccionar: `onClick` nunca
|
|
19
|
-
llegaba a correr.
|
|
20
|
-
|
|
21
|
-
En estos tamaños se muestra el primer fotograma con su duración y un botón de
|
|
22
|
-
play, y el recuadro completo lleva al visor grande, que es donde el video sí
|
|
23
|
-
se ve y se controla.
|
|
24
|
-
*/
|
|
25
|
-
const THUMBNAIL_TYPES = [
|
|
26
|
-
"carousel-image",
|
|
27
|
-
"catalogue-image-small-size",
|
|
28
|
-
"catalogue-image-big-size",
|
|
29
|
-
"catalogue-modal-image",
|
|
30
|
-
];
|
|
31
|
-
|
|
32
|
-
const PlayBadge = () => (
|
|
33
|
-
<span className="play-badge" aria-hidden="true">
|
|
34
|
-
<svg viewBox="0 0 24 24">
|
|
35
|
-
<circle cx="12" cy="12" r="11" />
|
|
36
|
-
<polygon points="9.5,7.5 17,12 9.5,16.5" />
|
|
37
|
-
</svg>
|
|
38
|
-
</span>
|
|
39
|
-
);
|
|
40
|
-
|
|
41
|
-
/**
|
|
42
|
-
* Imagen —o video— de producto.
|
|
43
|
-
*
|
|
44
|
-
* El tipo de archivo se decide por `ext`/`mime` cuando quien llama los conoce.
|
|
45
|
-
* Antes se comprobaba con `/\.mp4$/` sobre la URL, y nunca acertaba: la URL
|
|
46
|
-
* guardada apunta al redimensionador de CloudFront y no conserva la extensión.
|
|
47
|
-
* El regex queda solo como respaldo para los llamadores que aún mandan `img`
|
|
48
|
-
* a secas.
|
|
49
|
-
*/
|
|
50
|
-
export const ProductImage = ({ img, imageType, altText, onClick, ext, mime, poster }) => {
|
|
51
|
-
// La duración no está en la base: se toma de los metadatos, que el navegador
|
|
52
|
-
// ya descarga por el `preload="metadata"`. No cuesta una petición extra.
|
|
53
|
-
const [duration, setDuration] = useState(null);
|
|
54
|
-
|
|
55
|
-
const kind =
|
|
56
|
-
ext || mime
|
|
57
|
-
? resolvePreviewKind({ ext, mime })
|
|
58
|
-
: /\.mp4($|[?#])/i.test(img || "")
|
|
59
|
-
? ASSET_PREVIEW.VIDEO
|
|
60
|
-
: ASSET_PREVIEW.IMAGE;
|
|
61
|
-
|
|
62
|
-
if (kind === ASSET_PREVIEW.VIDEO) {
|
|
63
|
-
const isThumbnail = THUMBNAIL_TYPES.includes(imageType);
|
|
64
|
-
|
|
5
|
+
export const ProductImage = ({ img, imageType, altText, onClick}) => {
|
|
6
|
+
// Expresión regular para validar que los últimos tres caracteres sean ".mp4"
|
|
7
|
+
const regex = /\.mp4$/;
|
|
8
|
+
if (regex.test(img)){
|
|
65
9
|
return (
|
|
66
|
-
<Container
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
10
|
+
<Container className={imageType}>
|
|
11
|
+
<video controls width="100%" height="auto">
|
|
12
|
+
<source src= {img} type="video/mp4" />
|
|
13
|
+
</video>
|
|
14
|
+
</Container>
|
|
15
|
+
);
|
|
16
|
+
}
|
|
17
|
+
else{
|
|
18
|
+
return (
|
|
19
|
+
<Container className={imageType}>
|
|
20
|
+
<ReactImageFallback
|
|
21
|
+
src={img}
|
|
22
|
+
fallbackImage={defaultProductImage}
|
|
23
|
+
alt={altText}
|
|
24
|
+
onClick={onClick}
|
|
81
25
|
/>
|
|
82
|
-
{isThumbnail && (
|
|
83
|
-
<>
|
|
84
|
-
<PlayBadge />
|
|
85
|
-
{duration ? (
|
|
86
|
-
<span className="video-duration">{formatDuration(duration)}</span>
|
|
87
|
-
) : null}
|
|
88
|
-
</>
|
|
89
|
-
)}
|
|
90
26
|
</Container>
|
|
91
27
|
);
|
|
92
28
|
}
|
|
93
|
-
|
|
94
|
-
return (
|
|
95
|
-
<Container className={imageType}>
|
|
96
|
-
<ReactImageFallback
|
|
97
|
-
src={img}
|
|
98
|
-
fallbackImage={defaultProductImage}
|
|
99
|
-
alt={altText}
|
|
100
|
-
onClick={onClick}
|
|
101
|
-
/>
|
|
102
|
-
</Container>
|
|
103
|
-
);
|
|
104
29
|
};
|
|
@@ -1,92 +1,14 @@
|
|
|
1
1
|
import styled from "styled-components";
|
|
2
|
-
import { FontFamily } from "../../../global-files/variables";
|
|
3
2
|
|
|
4
3
|
export const Container = styled.div`
|
|
5
4
|
width: fit-content;
|
|
6
5
|
|
|
7
|
-
img
|
|
8
|
-
video {
|
|
6
|
+
img {
|
|
9
7
|
object-fit: contain;
|
|
10
8
|
}
|
|
11
9
|
|
|
12
|
-
/*
|
|
13
|
-
Miniatura de video: fotograma quieto, con su duración y un botón de play.
|
|
14
|
-
El clic lo recibe el contenedor, no el <video>, para que lleve al visor
|
|
15
|
-
grande en vez de quedarse peleando con los controles.
|
|
16
|
-
*/
|
|
17
|
-
&.video-thumbnail {
|
|
18
|
-
position: relative;
|
|
19
|
-
cursor: pointer;
|
|
20
|
-
|
|
21
|
-
video {
|
|
22
|
-
pointer-events: none;
|
|
23
|
-
background: #000;
|
|
24
|
-
}
|
|
25
|
-
|
|
26
|
-
.play-badge {
|
|
27
|
-
position: absolute;
|
|
28
|
-
top: 50%;
|
|
29
|
-
left: 50%;
|
|
30
|
-
transform: translate(-50%, -50%);
|
|
31
|
-
width: 32px;
|
|
32
|
-
height: 32px;
|
|
33
|
-
pointer-events: none;
|
|
34
|
-
|
|
35
|
-
svg {
|
|
36
|
-
width: 100%;
|
|
37
|
-
height: 100%;
|
|
38
|
-
display: block;
|
|
39
|
-
}
|
|
40
|
-
|
|
41
|
-
circle {
|
|
42
|
-
fill: rgba(0, 0, 0, 0.55);
|
|
43
|
-
}
|
|
44
|
-
|
|
45
|
-
polygon {
|
|
46
|
-
fill: #ffffff;
|
|
47
|
-
}
|
|
48
|
-
}
|
|
49
|
-
|
|
50
|
-
&:hover .play-badge circle {
|
|
51
|
-
fill: rgba(0, 0, 0, 0.75);
|
|
52
|
-
}
|
|
53
|
-
|
|
54
|
-
.video-duration {
|
|
55
|
-
position: absolute;
|
|
56
|
-
right: 4px;
|
|
57
|
-
bottom: 4px;
|
|
58
|
-
padding: 0 4px;
|
|
59
|
-
border-radius: 3px;
|
|
60
|
-
background: rgba(0, 0, 0, 0.75);
|
|
61
|
-
color: #ffffff;
|
|
62
|
-
font-family: ${FontFamily.Lato};
|
|
63
|
-
font-size: 10px;
|
|
64
|
-
line-height: 15px;
|
|
65
|
-
pointer-events: none;
|
|
66
|
-
}
|
|
67
|
-
}
|
|
68
|
-
|
|
69
|
-
/* En 60px un badge de 32 tapa el fotograma completo. */
|
|
70
|
-
&.catalogue-image-small-size.video-thumbnail .play-badge {
|
|
71
|
-
width: 20px;
|
|
72
|
-
height: 20px;
|
|
73
|
-
}
|
|
74
|
-
|
|
75
|
-
&.carousel-image.video-thumbnail {
|
|
76
|
-
.play-badge {
|
|
77
|
-
width: 22px;
|
|
78
|
-
height: 22px;
|
|
79
|
-
}
|
|
80
|
-
|
|
81
|
-
.video-duration {
|
|
82
|
-
font-size: 9px;
|
|
83
|
-
line-height: 13px;
|
|
84
|
-
}
|
|
85
|
-
}
|
|
86
|
-
|
|
87
10
|
&.carousel-image {
|
|
88
|
-
img
|
|
89
|
-
video {
|
|
11
|
+
img {
|
|
90
12
|
width: 85px;
|
|
91
13
|
height: 55px;
|
|
92
14
|
cursor: pointer;
|
|
@@ -98,8 +20,7 @@ export const Container = styled.div`
|
|
|
98
20
|
}
|
|
99
21
|
|
|
100
22
|
&.main-image {
|
|
101
|
-
img
|
|
102
|
-
video {
|
|
23
|
+
img {
|
|
103
24
|
width: 340px;
|
|
104
25
|
height: 295px;
|
|
105
26
|
}
|
|
@@ -109,16 +30,14 @@ export const Container = styled.div`
|
|
|
109
30
|
display: flex;
|
|
110
31
|
justify-content: center;
|
|
111
32
|
align-items: center;
|
|
112
|
-
img
|
|
113
|
-
video {
|
|
33
|
+
img {
|
|
114
34
|
width: 179px;
|
|
115
35
|
height: 179px;
|
|
116
36
|
}
|
|
117
37
|
}
|
|
118
38
|
|
|
119
39
|
&.catalogue-image-small-size {
|
|
120
|
-
img
|
|
121
|
-
video {
|
|
40
|
+
img {
|
|
122
41
|
width: 60px;
|
|
123
42
|
height: 60px;
|
|
124
43
|
}
|
|
@@ -128,9 +47,7 @@ export const Container = styled.div`
|
|
|
128
47
|
flex: 1 0 50%;
|
|
129
48
|
cursor: pointer;
|
|
130
49
|
|
|
131
|
-
img
|
|
132
|
-
|
|
133
|
-
video {
|
|
50
|
+
img {
|
|
134
51
|
width: 168px;
|
|
135
52
|
height: 108px;
|
|
136
53
|
}
|
|
@@ -143,9 +60,7 @@ export const Container = styled.div`
|
|
|
143
60
|
justify-content: center;
|
|
144
61
|
align-items: center;
|
|
145
62
|
|
|
146
|
-
img
|
|
147
|
-
|
|
148
|
-
video {
|
|
63
|
+
img {
|
|
149
64
|
width: 710px;
|
|
150
65
|
height: 710px;
|
|
151
66
|
}
|
|
@@ -2,10 +2,10 @@ import { Container } from "./styles";
|
|
|
2
2
|
import { ProductImage } from "../../atoms/ProductImage/index";
|
|
3
3
|
import { Button } from "../../atoms/GeneralButton/index";
|
|
4
4
|
|
|
5
|
-
export const EditionActiveImage = ({ img, altText, setShowModal
|
|
5
|
+
export const EditionActiveImage = ({ img, altText, setShowModal }) => {
|
|
6
6
|
return (
|
|
7
7
|
<Container>
|
|
8
|
-
<ProductImage img={img} imageType="main-image" altText={altText}
|
|
8
|
+
<ProductImage img={img} imageType="main-image" altText={altText} />
|
|
9
9
|
<Button
|
|
10
10
|
buttonType="open-modal-button"
|
|
11
11
|
onClick={() => setShowModal(true)}
|
|
@@ -25,7 +25,6 @@ export const GalleryElement = ({
|
|
|
25
25
|
auditImages,
|
|
26
26
|
compare,
|
|
27
27
|
updateApprovedInputsImages,
|
|
28
|
-
onSelect,
|
|
29
28
|
}) => {
|
|
30
29
|
const [showValidationPanel, setShowValidationPanel] = useState(false);
|
|
31
30
|
const [showDivStates, setShowDivStates] = useState({});
|
|
@@ -94,15 +93,7 @@ export const GalleryElement = ({
|
|
|
94
93
|
<ProductImage
|
|
95
94
|
img={compare ? auditImages?.values[number]?.src : image?.src}
|
|
96
95
|
altText={image?.altText}
|
|
97
|
-
// Sin `ext`, un video se pinta como <img> rota: la URL guardada no
|
|
98
|
-
// conserva la extensión del archivo.
|
|
99
|
-
ext={compare ? auditImages?.values[number]?.ext : image?.ext}
|
|
100
96
|
imageType={`catalogue-image-${gridLayout ? "big" : "small"}-size`}
|
|
101
|
-
// La miniatura de un video ya no lleva controles, así que el clic es
|
|
102
|
-
// la única forma de llegar al visor grande. Se aplica también a las
|
|
103
|
-
// imágenes: dos recuadros idénticos donde solo uno responde al clic
|
|
104
|
-
// sería peor que no responder ninguno.
|
|
105
|
-
onClick={onSelect}
|
|
106
97
|
/>
|
|
107
98
|
<div className="information-container">
|
|
108
99
|
<p>{image?.name}</p>
|
|
@@ -1,80 +1,28 @@
|
|
|
1
1
|
import { Container } from "./styles";
|
|
2
2
|
import { CheckBox } from "../../atoms/CheckBox";
|
|
3
3
|
|
|
4
|
-
|
|
5
|
-
Las columnas de la bandeja dependen de qué se está listando: un PDF no tiene
|
|
6
|
-
"tipo de toma" ni alto por ancho, y una foto no tiene "tipo de activo".
|
|
7
|
-
Estaban escritas a mano en el marcado, así que la única forma de cambiarlas
|
|
8
|
-
era duplicar el componente.
|
|
9
|
-
|
|
10
|
-
El preset `image` reproduce exactamente las columnas y clases de antes, para
|
|
11
|
-
que la bandeja de imágenes se vea idéntica sin que nadie tenga que pasar nada.
|
|
12
|
-
*/
|
|
13
|
-
export const GALLERY_COLUMNS = {
|
|
14
|
-
image: {
|
|
15
|
-
template: "0.1fr 1fr 1fr 1fr 1fr 1fr",
|
|
16
|
-
columns: [
|
|
17
|
-
{ key: "preview", label: "Imagen", className: "image-header" },
|
|
18
|
-
{ key: "name", label: "Nombre", className: "image-name" },
|
|
19
|
-
{ key: "size", label: "Tamaño", className: "image-size" },
|
|
20
|
-
{ key: "format", label: "Formato", className: "image-format" },
|
|
21
|
-
{
|
|
22
|
-
key: "legend",
|
|
23
|
-
label: "¿Tiene leyenda?",
|
|
24
|
-
className: "image-description",
|
|
25
|
-
},
|
|
26
|
-
{ key: "shotType", label: "Tipo de toma", className: "shoot-type" },
|
|
27
|
-
],
|
|
28
|
-
},
|
|
29
|
-
asset: {
|
|
30
|
-
template: "0.1fr 1fr 1fr 1fr 1fr",
|
|
31
|
-
columns: [
|
|
32
|
-
{ key: "preview", label: "Activo", className: "asset-header" },
|
|
33
|
-
{ key: "name", label: "Nombre", className: "asset-name" },
|
|
34
|
-
{ key: "weight", label: "Peso", className: "asset-weight" },
|
|
35
|
-
{ key: "format", label: "Formato", className: "asset-format" },
|
|
36
|
-
{ key: "assetType", label: "Tipo de activo", className: "asset-type" },
|
|
37
|
-
],
|
|
38
|
-
},
|
|
39
|
-
};
|
|
40
|
-
|
|
41
|
-
/**
|
|
42
|
-
* Encabezado de la bandeja, con su casilla de "seleccionar todo".
|
|
43
|
-
*
|
|
44
|
-
* `checkAllId` e `itemSelector` se pueden cambiar porque imágenes y activos
|
|
45
|
-
* conviven en la misma pestaña: si las dos listas compartieran el id de la
|
|
46
|
-
* casilla global, marcar todas las imágenes marcaría también los activos.
|
|
47
|
-
*/
|
|
48
|
-
export const GalleryHeader = ({
|
|
49
|
-
setCheckAll,
|
|
50
|
-
setSelectedImages,
|
|
51
|
-
shotThd,
|
|
52
|
-
variant = "image",
|
|
53
|
-
columns,
|
|
54
|
-
checkAllId = "check-all-images",
|
|
55
|
-
itemSelector = "[id^='gallery-element-']",
|
|
56
|
-
}) => {
|
|
57
|
-
const preset = GALLERY_COLUMNS[variant] ?? GALLERY_COLUMNS.image;
|
|
58
|
-
const visibleColumns = columns ?? preset.columns;
|
|
59
|
-
|
|
4
|
+
export const GalleryHeader = ({ setCheckAll, setSelectedImages, shotThd }) => {
|
|
60
5
|
const checkAll = (e) => {
|
|
61
|
-
const checkboxList = document.querySelectorAll(
|
|
6
|
+
const checkboxList = document.querySelectorAll("[id^='gallery-element-']");
|
|
62
7
|
checkboxList.forEach((chk) => (chk.checked = e.target.checked));
|
|
63
8
|
setCheckAll(e.target.checked);
|
|
64
9
|
if (!e.target.checked) setSelectedImages([]);
|
|
65
10
|
};
|
|
66
|
-
|
|
67
11
|
return (
|
|
68
|
-
<Container
|
|
12
|
+
<Container>
|
|
69
13
|
<div className="checkbox">
|
|
70
|
-
<CheckBox id=
|
|
14
|
+
<CheckBox id="check-all-images" onChange={(e) => checkAll(e)} />
|
|
71
15
|
</div>
|
|
72
16
|
<div className="information">
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
17
|
+
<p className="image-header">Imagen</p>
|
|
18
|
+
<p className="image-name">Nombre</p>
|
|
19
|
+
<p className="image-size">Tamaño</p>
|
|
20
|
+
<p className="image-format">Formato</p>
|
|
21
|
+
<p className="image-description">¿Tiene leyenda?</p>
|
|
22
|
+
<p className="shoot-type">Tipo de toma</p>
|
|
23
|
+
{/* <p className="">Tipo de imagen</p> */}
|
|
24
|
+
{/* <p className="">Tipo de empaque</p> */}
|
|
25
|
+
{/* <p className="">Estatus</p> */}
|
|
78
26
|
</div>
|
|
79
27
|
</Container>
|
|
80
28
|
);
|
|
@@ -15,8 +15,7 @@ export const Container = styled.div`
|
|
|
15
15
|
.information{
|
|
16
16
|
width: 90%;
|
|
17
17
|
display: grid;
|
|
18
|
-
|
|
19
|
-
grid-template-columns: ${({ template }) => template || "0.1fr 1fr 1fr 1fr 1fr 1fr"};
|
|
18
|
+
grid-template-columns: 0.1fr 1fr 1fr 1fr 1fr 1fr;
|
|
20
19
|
}
|
|
21
20
|
.image-header{
|
|
22
21
|
|
|
@@ -96,6 +96,17 @@ const ProviderProductEditionView = ({
|
|
|
96
96
|
const [variantGroup, setVariantGroup] = useState(null);
|
|
97
97
|
const [showGroup, setShowGroup] = useState(false);
|
|
98
98
|
const [showGroupModal, setShowGroupModal] = useState(false);
|
|
99
|
+
|
|
100
|
+
/*
|
|
101
|
+
Un hijo no se agrupa como variante (decisión de producto, 2026-09-25).
|
|
102
|
+
|
|
103
|
+
Agrupar declara «estos son el mismo producto en otra presentación», y un
|
|
104
|
+
hijo ya declaró de dónde deriva; tener las dos cosas lo dejaba con dos
|
|
105
|
+
respuestas distintas a la misma pregunta. El servidor lo rechaza con
|
|
106
|
+
VARIANT_GROUP_NOT_FOR_CHILD, así que ofrecer el botón aquí sería ofrecer
|
|
107
|
+
algo que siempre falla.
|
|
108
|
+
*/
|
|
109
|
+
const puedeAgruparse = !state.product?.parentArticleId;
|
|
99
110
|
const [pendingPropagation, setPendingPropagation] = useState(null);
|
|
100
111
|
const [preparandoPropagacion, setPreparandoPropagacion] = useState(false);
|
|
101
112
|
const [preparandoEdicionMultiple, setPreparandoEdicionMultiple] =
|
|
@@ -751,8 +762,24 @@ const ProviderProductEditionView = ({
|
|
|
751
762
|
const companeras = (variantGroup?.members ?? []).filter(
|
|
752
763
|
(m) => !m.isCurrent
|
|
753
764
|
);
|
|
765
|
+
/*
|
|
766
|
+
Un HIJO no propaga a nadie, y hay que decirlo explícitamente.
|
|
767
|
+
|
|
768
|
+
Sin este corte el diálogo se abría desde la ficha de un hijo y le
|
|
769
|
+
ofrecía a sus HERMANOS como si fueran sus hijos: `family.variants`
|
|
770
|
+
mira la familia desde el padre —eso es lo correcto para navegar entre
|
|
771
|
+
hermanos— pero aquí se estaba leyendo como «los destinos de este
|
|
772
|
+
producto». El servidor resolvía los destinos por su cuenta y no
|
|
773
|
+
escribía nada, así que el usuario elegía hermanos, guardaba, y no
|
|
774
|
+
pasaba nada: un no-operativo silencioso.
|
|
775
|
+
|
|
776
|
+
Un hijo tampoco puede estar agrupado desde 2026-09-25, así que para
|
|
777
|
+
él no queda ningún destino posible.
|
|
778
|
+
*/
|
|
779
|
+
const esHijo = !!state.product?.parentArticleId;
|
|
754
780
|
const hayDestinos =
|
|
755
|
-
|
|
781
|
+
!esHijo &&
|
|
782
|
+
(state.product?.variantsCount > 0 || companeras.length > 0);
|
|
756
783
|
const cambios = cambiosDeFichaTecnica();
|
|
757
784
|
|
|
758
785
|
if (hayDestinos && cambios.length > 0) {
|
|
@@ -1659,7 +1686,11 @@ const ProviderProductEditionView = ({
|
|
|
1659
1686
|
{pendingPropagation && (
|
|
1660
1687
|
<PropagateChangesModal
|
|
1661
1688
|
changedAttributes={pendingPropagation.cambios}
|
|
1662
|
-
variants={
|
|
1689
|
+
variants={
|
|
1690
|
+
state.product?.parentArticleId
|
|
1691
|
+
? []
|
|
1692
|
+
: (family?.variants ?? []).filter((v) => !v.isCurrent)
|
|
1693
|
+
}
|
|
1663
1694
|
groupMembers={(variantGroup?.members ?? []).filter(
|
|
1664
1695
|
(m) => !m.isCurrent
|
|
1665
1696
|
)}
|
|
@@ -2067,6 +2098,7 @@ const ProviderProductEditionView = ({
|
|
|
2067
2098
|
código compartido que dice "estos son el mismo producto en
|
|
2068
2099
|
otra presentación". Mezclarlos en una sola caja haría creer
|
|
2069
2100
|
que agrupar hereda algo, que es justo lo que no pasa. */}
|
|
2101
|
+
{puedeAgruparse && (
|
|
2070
2102
|
<div className="variants-actions">
|
|
2071
2103
|
<button
|
|
2072
2104
|
type="button"
|
|
@@ -2088,8 +2120,9 @@ const ProviderProductEditionView = ({
|
|
|
2088
2120
|
</button>
|
|
2089
2121
|
)}
|
|
2090
2122
|
</div>
|
|
2123
|
+
)}
|
|
2091
2124
|
|
|
2092
|
-
{showGroup && variantGroup?.group && (
|
|
2125
|
+
{puedeAgruparse && showGroup && variantGroup?.group && (
|
|
2093
2126
|
<div className="variants-family">
|
|
2094
2127
|
{/* Los hermanos de grupo NO se listan como padre/hijo: aquí
|
|
2095
2128
|
nadie hereda de nadie, así que no hay rol que mostrar,
|