limbo-component 1.9.1 → 2.0.0
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/limbo.cjs.js +2 -2
- package/dist/limbo.cjs.map +1 -1
- package/dist/limbo.css +1 -1
- package/dist/limbo.es.js +1124 -318
- package/dist/limbo.es.map +1 -1
- package/dist/limbo.min.js +2 -2
- package/dist/limbo.min.js.map +1 -1
- package/dist/limbo.umd.js +2 -2
- package/dist/limbo.umd.js.map +1 -1
- package/dist/types/App.d.ts.map +1 -1
- package/dist/types/components/Breadcrumb.d.ts +14 -0
- package/dist/types/components/Breadcrumb.d.ts.map +1 -0
- package/dist/types/components/CropperView.d.ts +3 -1
- package/dist/types/components/CropperView.d.ts.map +1 -1
- package/dist/types/components/ImagePreview.d.ts +25 -0
- package/dist/types/components/ImagePreview.d.ts.map +1 -0
- package/dist/types/components/TabAI.d.ts +3 -2
- package/dist/types/components/TabAI.d.ts.map +1 -1
- package/dist/types/components/TabPortals.d.ts +2 -2
- package/dist/types/components/TabPortals.d.ts.map +1 -1
- package/dist/types/components/TabStock.d.ts +3 -2
- package/dist/types/components/TabStock.d.ts.map +1 -1
- package/dist/types/components/TabUpload.d.ts +2 -2
- package/dist/types/components/TabUpload.d.ts.map +1 -1
- package/dist/types/components/UploadForm.d.ts +4 -2
- package/dist/types/components/UploadForm.d.ts.map +1 -1
- package/package.json +1 -1
package/dist/limbo.es.js
CHANGED
|
@@ -14322,13 +14322,257 @@ function Gallery({
|
|
|
14322
14322
|
)
|
|
14323
14323
|
] });
|
|
14324
14324
|
}
|
|
14325
|
+
function ImagePreview({
|
|
14326
|
+
image,
|
|
14327
|
+
onDiscard,
|
|
14328
|
+
onRetry,
|
|
14329
|
+
onDownload,
|
|
14330
|
+
onSelect,
|
|
14331
|
+
showRetry = false,
|
|
14332
|
+
disabled = false
|
|
14333
|
+
}) {
|
|
14334
|
+
const [previewUrl, setPreviewUrl] = useState(null);
|
|
14335
|
+
const [imageInfo, setImageInfo] = useState({
|
|
14336
|
+
name: "",
|
|
14337
|
+
size: "",
|
|
14338
|
+
type: ""
|
|
14339
|
+
});
|
|
14340
|
+
const [showLightbox, setShowLightbox] = useState(false);
|
|
14341
|
+
const handleEditName = (e) => {
|
|
14342
|
+
const newName = e.target.value;
|
|
14343
|
+
const originalName = imageInfo.name;
|
|
14344
|
+
const lastDotIndex = originalName.lastIndexOf(".");
|
|
14345
|
+
const extension = lastDotIndex !== -1 ? originalName.substring(lastDotIndex) : ".webp";
|
|
14346
|
+
setImageInfo((prev) => ({
|
|
14347
|
+
...prev,
|
|
14348
|
+
name: newName + extension
|
|
14349
|
+
}));
|
|
14350
|
+
};
|
|
14351
|
+
useEffect(() => {
|
|
14352
|
+
if (!image) {
|
|
14353
|
+
setPreviewUrl(null);
|
|
14354
|
+
return;
|
|
14355
|
+
}
|
|
14356
|
+
const url = URL.createObjectURL(image);
|
|
14357
|
+
setPreviewUrl(url);
|
|
14358
|
+
setImageInfo({
|
|
14359
|
+
name: image.name || "imagen.webp",
|
|
14360
|
+
size: formatFileSize(image.size || 0),
|
|
14361
|
+
type: image.type || "image/webp"
|
|
14362
|
+
});
|
|
14363
|
+
return () => {
|
|
14364
|
+
URL.revokeObjectURL(url);
|
|
14365
|
+
};
|
|
14366
|
+
}, [image]);
|
|
14367
|
+
useEffect(() => {
|
|
14368
|
+
if (!showLightbox) return;
|
|
14369
|
+
const handleEsc = (e) => {
|
|
14370
|
+
if (e.key === "Escape") {
|
|
14371
|
+
setShowLightbox(false);
|
|
14372
|
+
}
|
|
14373
|
+
};
|
|
14374
|
+
document.addEventListener("keydown", handleEsc);
|
|
14375
|
+
return () => document.removeEventListener("keydown", handleEsc);
|
|
14376
|
+
}, [showLightbox]);
|
|
14377
|
+
const formatFileSize = (bytes) => {
|
|
14378
|
+
if (bytes === 0) return "0 Bytes";
|
|
14379
|
+
const k = 1024;
|
|
14380
|
+
const sizes = ["Bytes", "KB", "MB", "GB"];
|
|
14381
|
+
const i = Math.floor(Math.log(bytes) / Math.log(k));
|
|
14382
|
+
return Math.round(bytes / Math.pow(k, i) * 100) / 100 + " " + sizes[i];
|
|
14383
|
+
};
|
|
14384
|
+
const handleDownload = () => {
|
|
14385
|
+
if (!previewUrl || disabled) return;
|
|
14386
|
+
const link = document.createElement("a");
|
|
14387
|
+
link.href = previewUrl;
|
|
14388
|
+
link.download = imageInfo.name;
|
|
14389
|
+
document.body.appendChild(link);
|
|
14390
|
+
link.click();
|
|
14391
|
+
document.body.removeChild(link);
|
|
14392
|
+
if (onDownload) onDownload();
|
|
14393
|
+
};
|
|
14394
|
+
if (!image || !previewUrl) {
|
|
14395
|
+
return null;
|
|
14396
|
+
}
|
|
14397
|
+
return /* @__PURE__ */ jsxs("div", { className: "w-fit md:w-full bg-white border border-gray-200 rounded-lg px-1 sm:px-3 py-2 m-0 shadow-sm", children: [
|
|
14398
|
+
/* @__PURE__ */ jsxs("div", { className: "flex items-center justify-between mb-4 px-1", children: [
|
|
14399
|
+
/* @__PURE__ */ jsx("h3", { className: "text-sm font-medium text-gray-700", children: "Vista previa de imagen" }),
|
|
14400
|
+
/* @__PURE__ */ jsx(
|
|
14401
|
+
"button",
|
|
14402
|
+
{
|
|
14403
|
+
type: "button",
|
|
14404
|
+
onClick: onDiscard,
|
|
14405
|
+
disabled,
|
|
14406
|
+
className: "p-1.5 text-gray-400 cursor-pointer hover:text-gray-600 hover:bg-red-300 rounded transition-colors disabled:opacity-50 disabled:cursor-not-allowed",
|
|
14407
|
+
"aria-label": "Descartar imagen",
|
|
14408
|
+
title: "Descartar imagen",
|
|
14409
|
+
children: /* @__PURE__ */ jsx("span", { className: "icon icon-close-small icon--md" })
|
|
14410
|
+
}
|
|
14411
|
+
)
|
|
14412
|
+
] }),
|
|
14413
|
+
/* @__PURE__ */ jsxs("div", { className: "flex flex-col justify-center md:flex-row gap-2 md:gap-6", children: [
|
|
14414
|
+
/* @__PURE__ */ jsxs("div", { className: "flex-1 md:flex-[0.4] flex flex-col items-center justify-center", children: [
|
|
14415
|
+
/* @__PURE__ */ jsxs(
|
|
14416
|
+
"div",
|
|
14417
|
+
{
|
|
14418
|
+
className: "relative w-full max-w-md aspect-video bg-gray-100 rounded-lg overflow-hidden cursor-zoom-in hover:ring-2 hover:ring-brand-blue-800 transition-all",
|
|
14419
|
+
onClick: () => setShowLightbox(true),
|
|
14420
|
+
role: "button",
|
|
14421
|
+
tabIndex: 0,
|
|
14422
|
+
onKeyDown: (e) => {
|
|
14423
|
+
if (e.key === "Enter" || e.key === " ") {
|
|
14424
|
+
e.preventDefault();
|
|
14425
|
+
setShowLightbox(true);
|
|
14426
|
+
}
|
|
14427
|
+
},
|
|
14428
|
+
"aria-label": "Click para ver imagen en tamaño completo",
|
|
14429
|
+
title: "Click para ver en tamaño completo",
|
|
14430
|
+
children: [
|
|
14431
|
+
/* @__PURE__ */ jsx(
|
|
14432
|
+
"img",
|
|
14433
|
+
{
|
|
14434
|
+
src: previewUrl,
|
|
14435
|
+
alt: "Vista previa",
|
|
14436
|
+
className: "absolute inset-0 w-full h-full object-contain"
|
|
14437
|
+
}
|
|
14438
|
+
),
|
|
14439
|
+
/* @__PURE__ */ jsxs("div", { className: "absolute top-2 right-2 bg-black/50 text-white px-2 py-1 rounded text-xs flex items-center gap-1", children: [
|
|
14440
|
+
/* @__PURE__ */ jsx("span", { className: "icon icon-search-white icon--xs" }),
|
|
14441
|
+
/* @__PURE__ */ jsx("span", { className: "hidden sm:inline", children: "Ampliar" })
|
|
14442
|
+
] })
|
|
14443
|
+
]
|
|
14444
|
+
}
|
|
14445
|
+
),
|
|
14446
|
+
/* @__PURE__ */ jsx("div", { className: "mt-3 text-center space-y-1 w-full", children: /* @__PURE__ */ jsxs("div", { className: "rounded-sm w-fit gap-0.5 p-0.5 min-w-fit mx-auto", children: [
|
|
14447
|
+
/* @__PURE__ */ jsxs(
|
|
14448
|
+
"label",
|
|
14449
|
+
{
|
|
14450
|
+
htmlFor: "uploadImageId",
|
|
14451
|
+
className: "flex items-center w-fit",
|
|
14452
|
+
children: [
|
|
14453
|
+
/* @__PURE__ */ jsx("div", { className: "rounded-full bg-brand-blue-1000 min-w-6 min-h-6 flex items-center align-middle content-center justify-center me-1", children: /* @__PURE__ */ jsx("span", { className: "icon icon-edit-white icon--sm" }) }),
|
|
14454
|
+
/* @__PURE__ */ jsx(
|
|
14455
|
+
"input",
|
|
14456
|
+
{
|
|
14457
|
+
id: "uploadImageId",
|
|
14458
|
+
onChange: (e) => handleEditName(e),
|
|
14459
|
+
className: "w-fit px-1 text-sm font-medium text-gray-800 border border-gray-300 rounded truncate focus:outline focus:outline-neutral-gray-100 focus:transform-none focus:shadow-none focus:opacity-100",
|
|
14460
|
+
defaultValue: imageInfo.name.split(".")[0],
|
|
14461
|
+
title: "Renombra la imagen a tu gusto"
|
|
14462
|
+
}
|
|
14463
|
+
)
|
|
14464
|
+
]
|
|
14465
|
+
}
|
|
14466
|
+
),
|
|
14467
|
+
/* @__PURE__ */ jsxs("div", { className: "flex items-center justify-center gap-3 text-xs text-gray-500", children: [
|
|
14468
|
+
/* @__PURE__ */ jsx("span", { children: imageInfo.size }),
|
|
14469
|
+
/* @__PURE__ */ jsx("span", { children: "•" }),
|
|
14470
|
+
/* @__PURE__ */ jsx("span", { children: imageInfo.type.split("/")[1]?.toUpperCase() || "IMAGE" })
|
|
14471
|
+
] })
|
|
14472
|
+
] }) })
|
|
14473
|
+
] }),
|
|
14474
|
+
/* @__PURE__ */ jsxs("div", { className: "flex flex-row md:flex-col gap-2 justify-items-stretch justify-center md:justify-start items-stretch md:min-w-[140px]", children: [
|
|
14475
|
+
showRetry && onRetry && /* @__PURE__ */ jsxs(
|
|
14476
|
+
"button",
|
|
14477
|
+
{
|
|
14478
|
+
type: "button",
|
|
14479
|
+
onClick: onRetry,
|
|
14480
|
+
disabled,
|
|
14481
|
+
className: "flex-1 md:flex-none flex items-center justify-center gap-2 px-4 py-2.5 bg-white border border-gray-300 text-gray-700 rounded-lg hover:bg-gray-50 cursor-pointer hover:border-gray-400 transition-colors disabled:opacity-50 disabled:cursor-not-allowed text-sm font-medium",
|
|
14482
|
+
"aria-label": "Intentarlo de nuevo",
|
|
14483
|
+
title: "Generar de nuevo con el mismo prompt",
|
|
14484
|
+
children: [
|
|
14485
|
+
/* @__PURE__ */ jsx("span", { className: "icon icon-refresh icon--sm" }),
|
|
14486
|
+
/* @__PURE__ */ jsx("span", { className: "hidden sm:inline", children: "Reintentar" }),
|
|
14487
|
+
/* @__PURE__ */ jsx("span", { className: "sm:hidden", children: "Retry" })
|
|
14488
|
+
]
|
|
14489
|
+
}
|
|
14490
|
+
),
|
|
14491
|
+
/* @__PURE__ */ jsxs(
|
|
14492
|
+
"button",
|
|
14493
|
+
{
|
|
14494
|
+
type: "button",
|
|
14495
|
+
onClick: handleDownload,
|
|
14496
|
+
disabled,
|
|
14497
|
+
className: "flex-1 cursor-pointer md:flex-none flex items-center justify-center gap-2 px-4 py-2.5 bg-white border border-gray-300 text-gray-700 rounded-lg hover:bg-gray-50 hover:border-gray-400 transition-colors disabled:opacity-50 disabled:cursor-not-allowed text-sm font-medium",
|
|
14498
|
+
"aria-label": "Descargar imagen",
|
|
14499
|
+
children: [
|
|
14500
|
+
/* @__PURE__ */ jsx("span", { className: "icon icon-download icon--sm" }),
|
|
14501
|
+
/* @__PURE__ */ jsx("span", { children: "Descargar" })
|
|
14502
|
+
]
|
|
14503
|
+
}
|
|
14504
|
+
),
|
|
14505
|
+
/* @__PURE__ */ jsxs(
|
|
14506
|
+
"button",
|
|
14507
|
+
{
|
|
14508
|
+
type: "button",
|
|
14509
|
+
onClick: () => onSelect && onSelect(imageInfo.name),
|
|
14510
|
+
disabled,
|
|
14511
|
+
className: "flex-1 md:flex-none flex items-center justify-center gap-2 px-4 py-2.5 bg-brand-blue-800 border border-brand-blue-800 text-white rounded-lg hover:bg-brand-blue-1000 cursor-pointer hover:border-brand-blue-1000 transition-colors disabled:opacity-50 disabled:cursor-not-allowed text-sm font-medium shadow-sm",
|
|
14512
|
+
"aria-label": "Seleccionar imagen para recortar",
|
|
14513
|
+
children: [
|
|
14514
|
+
/* @__PURE__ */ jsx("span", { className: "icon icon-tick-white icon--sm" }),
|
|
14515
|
+
/* @__PURE__ */ jsx("span", { children: "Seleccionar" })
|
|
14516
|
+
]
|
|
14517
|
+
}
|
|
14518
|
+
)
|
|
14519
|
+
] })
|
|
14520
|
+
] }),
|
|
14521
|
+
showLightbox && /* @__PURE__ */ jsxs(
|
|
14522
|
+
"div",
|
|
14523
|
+
{
|
|
14524
|
+
className: "fixed inset-0 bg-black/90 z-[10000] flex items-center justify-center p-4",
|
|
14525
|
+
onClick: () => setShowLightbox(false),
|
|
14526
|
+
role: "dialog",
|
|
14527
|
+
"aria-modal": "true",
|
|
14528
|
+
"aria-label": "Vista de imagen en tamaño completo",
|
|
14529
|
+
children: [
|
|
14530
|
+
/* @__PURE__ */ jsx(
|
|
14531
|
+
"button",
|
|
14532
|
+
{
|
|
14533
|
+
onClick: () => setShowLightbox(false),
|
|
14534
|
+
className: "absolute top-4 right-4 p-2 bg-white/10 hover:bg-white/20 rounded-full transition-colors cursor-pointer z-10",
|
|
14535
|
+
"aria-label": "Cerrar vista ampliada",
|
|
14536
|
+
title: "Cerrar (Esc)",
|
|
14537
|
+
children: /* @__PURE__ */ jsx("span", { className: "icon icon-close-small-white icon--lg" })
|
|
14538
|
+
}
|
|
14539
|
+
),
|
|
14540
|
+
/* @__PURE__ */ jsxs("div", { className: "absolute top-4 left-4 bg-black/50 text-white px-4 py-2 rounded-lg text-sm", children: [
|
|
14541
|
+
/* @__PURE__ */ jsx("p", { className: "font-medium", children: imageInfo.name }),
|
|
14542
|
+
/* @__PURE__ */ jsxs("p", { className: "text-xs text-gray-300 mt-1", children: [
|
|
14543
|
+
imageInfo.size,
|
|
14544
|
+
" • ",
|
|
14545
|
+
imageInfo.type.split("/")[1]?.toUpperCase()
|
|
14546
|
+
] })
|
|
14547
|
+
] }),
|
|
14548
|
+
/* @__PURE__ */ jsx(
|
|
14549
|
+
"div",
|
|
14550
|
+
{
|
|
14551
|
+
className: "relative max-w-7xl max-h-[90vh] w-full h-full flex items-center justify-center",
|
|
14552
|
+
onClick: (e) => e.stopPropagation(),
|
|
14553
|
+
children: /* @__PURE__ */ jsx(
|
|
14554
|
+
"img",
|
|
14555
|
+
{
|
|
14556
|
+
src: previewUrl,
|
|
14557
|
+
alt: "Vista completa",
|
|
14558
|
+
className: "max-w-full max-h-full object-contain rounded-lg shadow-2xl"
|
|
14559
|
+
}
|
|
14560
|
+
)
|
|
14561
|
+
}
|
|
14562
|
+
),
|
|
14563
|
+
/* @__PURE__ */ jsx("div", { className: "absolute bottom-4 left-1/2 -translate-x-1/2 bg-black/50 text-white px-4 py-2 rounded-lg text-xs text-center", children: "Click fuera de la imagen o presiona ESC para cerrar" })
|
|
14564
|
+
]
|
|
14565
|
+
}
|
|
14566
|
+
)
|
|
14567
|
+
] });
|
|
14568
|
+
}
|
|
14325
14569
|
function TabUpload({
|
|
14326
14570
|
file,
|
|
14327
14571
|
setFile,
|
|
14328
14572
|
previewUrl,
|
|
14329
14573
|
setPreviewUrl,
|
|
14330
14574
|
fileInputRef,
|
|
14331
|
-
|
|
14575
|
+
onSelect,
|
|
14332
14576
|
disabled
|
|
14333
14577
|
}) {
|
|
14334
14578
|
const handleFileChange = (e) => {
|
|
@@ -14341,36 +14585,32 @@ function TabUpload({
|
|
|
14341
14585
|
setPreviewUrl(null);
|
|
14342
14586
|
}
|
|
14343
14587
|
};
|
|
14344
|
-
const
|
|
14345
|
-
const confirmClear = window.confirm(
|
|
14346
|
-
"¿Estás seguro de que deseas descartar la imagen seleccionada?"
|
|
14347
|
-
);
|
|
14348
|
-
if (!confirmClear) return;
|
|
14588
|
+
const handleDiscard = () => {
|
|
14349
14589
|
setFile(null);
|
|
14350
14590
|
setPreviewUrl(null);
|
|
14351
14591
|
if (fileInputRef.current) fileInputRef.current.value = "";
|
|
14352
14592
|
};
|
|
14353
|
-
const
|
|
14354
|
-
|
|
14355
|
-
|
|
14356
|
-
|
|
14357
|
-
|
|
14358
|
-
|
|
14359
|
-
|
|
14593
|
+
const handleSelect = (editedName) => {
|
|
14594
|
+
if (file && !disabled && onSelect) {
|
|
14595
|
+
if (editedName && editedName !== file.name) {
|
|
14596
|
+
const newFile = new File([file], editedName, { type: file.type });
|
|
14597
|
+
onSelect(newFile);
|
|
14598
|
+
} else {
|
|
14599
|
+
onSelect(file);
|
|
14600
|
+
}
|
|
14360
14601
|
}
|
|
14361
14602
|
};
|
|
14362
14603
|
return /* @__PURE__ */ jsxs(
|
|
14363
|
-
"
|
|
14604
|
+
"div",
|
|
14364
14605
|
{
|
|
14365
|
-
onSubmit: handleSubmit,
|
|
14366
14606
|
className: "flex flex-col items-center gap-6",
|
|
14367
14607
|
"aria-label": "Subir imagen desde dispositivo",
|
|
14368
14608
|
children: [
|
|
14369
|
-
|
|
14609
|
+
/* @__PURE__ */ jsxs(
|
|
14370
14610
|
"label",
|
|
14371
14611
|
{
|
|
14372
14612
|
htmlFor: "file-input",
|
|
14373
|
-
className: "w-full flex flex-col items-center justify-center border-2 border-dashed border-brand-blue-200 rounded-xl p-8 cursor-pointer hover:border-brand-blue-1000 hover:to-bright-blue-200 hover:from-brand-blue-050 focus-within:border-brand-blue-800 from-bright-blue-050 to-brand-blue-050 bg-linear-to-br transition",
|
|
14613
|
+
className: "w-fit md:w-full flex flex-col items-center justify-center border-2 border-dashed border-brand-blue-200 rounded-xl p-8 cursor-pointer hover:border-brand-blue-1000 hover:to-bright-blue-200 hover:from-brand-blue-050 focus-within:border-brand-blue-800 from-bright-blue-050 to-brand-blue-050 bg-linear-to-br transition",
|
|
14374
14614
|
tabIndex: 0,
|
|
14375
14615
|
style: { outline: "none" },
|
|
14376
14616
|
children: [
|
|
@@ -14393,71 +14633,16 @@ function TabUpload({
|
|
|
14393
14633
|
]
|
|
14394
14634
|
}
|
|
14395
14635
|
),
|
|
14396
|
-
|
|
14397
|
-
|
|
14398
|
-
/* @__PURE__ */ jsx(
|
|
14399
|
-
"img",
|
|
14400
|
-
{
|
|
14401
|
-
src: previewUrl,
|
|
14402
|
-
alt: file?.name || "Previsualización",
|
|
14403
|
-
className: "rounded-lg max-h-64 object-contain bg-white w-fit mx-auto",
|
|
14404
|
-
style: { maxWidth: "100%" }
|
|
14405
|
-
}
|
|
14406
|
-
),
|
|
14407
|
-
/* @__PURE__ */ jsx(
|
|
14408
|
-
"button",
|
|
14409
|
-
{
|
|
14410
|
-
type: "button",
|
|
14411
|
-
onClick: handleClearFile,
|
|
14412
|
-
disabled,
|
|
14413
|
-
className: "absolute cursor-pointer top-2 right-2 bg-red-600 hover:bg-red-700 text-white rounded-full p-2 shadow-lg transition-colors duration-200 disabled:opacity-50 disabled:cursor-not-allowed",
|
|
14414
|
-
title: "Descartar imagen",
|
|
14415
|
-
"aria-label": "Descartar imagen seleccionada",
|
|
14416
|
-
children: /* @__PURE__ */ jsx("span", { className: "icon icon-close-small-white icon--md m-0 p-0" })
|
|
14417
|
-
}
|
|
14418
|
-
)
|
|
14419
|
-
] }),
|
|
14420
|
-
/* @__PURE__ */ jsx("div", { className: "text-sm text-neutral-800 bg-brand-blue-050 p-3 rounded-lg w-full", children: /* @__PURE__ */ jsxs("div", { className: "grid grid-cols-1 md:grid-cols-3 gap-2 text-center", children: [
|
|
14421
|
-
/* @__PURE__ */ jsxs("p", { children: [
|
|
14422
|
-
/* @__PURE__ */ jsx("span", { className: "font-medium", children: "Archivo:" }),
|
|
14423
|
-
" ",
|
|
14424
|
-
file?.name
|
|
14425
|
-
] }),
|
|
14426
|
-
/* @__PURE__ */ jsxs("p", { children: [
|
|
14427
|
-
/* @__PURE__ */ jsx("span", { className: "font-medium", children: "Tamaño:" }),
|
|
14428
|
-
" ",
|
|
14429
|
-
file ? (file.size / 1024 / 1024).toFixed(2) : "--",
|
|
14430
|
-
" MB"
|
|
14431
|
-
] }),
|
|
14432
|
-
/* @__PURE__ */ jsxs("p", { children: [
|
|
14433
|
-
/* @__PURE__ */ jsx("span", { className: "font-medium", children: "Tipo:" }),
|
|
14434
|
-
" ",
|
|
14435
|
-
file?.type
|
|
14436
|
-
] })
|
|
14437
|
-
] }) }),
|
|
14438
|
-
/* @__PURE__ */ jsx(
|
|
14439
|
-
"button",
|
|
14440
|
-
{
|
|
14441
|
-
type: "submit",
|
|
14442
|
-
disabled: !file || disabled,
|
|
14443
|
-
title: !file ? "Proporcione una imagen antes de continuar" : "Subir imagen a la galeria",
|
|
14444
|
-
className: `limbo-btn w-full ${!file ? "cursor-not-allowed limbo-btn-disabled" : "limbo-btn-primary"}`,
|
|
14445
|
-
style: { minHeight: 44 },
|
|
14446
|
-
children: disabled ? "Subiendo..." : "Subir imagen"
|
|
14447
|
-
}
|
|
14448
|
-
)
|
|
14449
|
-
] }),
|
|
14450
|
-
previewUrl && /* @__PURE__ */ jsx(
|
|
14451
|
-
"input",
|
|
14636
|
+
file && /* @__PURE__ */ jsx(
|
|
14637
|
+
ImagePreview,
|
|
14452
14638
|
{
|
|
14453
|
-
|
|
14454
|
-
|
|
14455
|
-
|
|
14456
|
-
|
|
14457
|
-
|
|
14458
|
-
|
|
14459
|
-
|
|
14460
|
-
"aria-label": "Seleccionar imagen"
|
|
14639
|
+
image: file,
|
|
14640
|
+
onDiscard: handleDiscard,
|
|
14641
|
+
onDownload: () => {
|
|
14642
|
+
},
|
|
14643
|
+
onSelect: handleSelect,
|
|
14644
|
+
showRetry: false,
|
|
14645
|
+
disabled
|
|
14461
14646
|
}
|
|
14462
14647
|
)
|
|
14463
14648
|
]
|
|
@@ -14600,16 +14785,23 @@ function LoadingOverlay({
|
|
|
14600
14785
|
}
|
|
14601
14786
|
);
|
|
14602
14787
|
}
|
|
14603
|
-
function TabAI({ prod, disabled,
|
|
14788
|
+
function TabAI({ prod, disabled, onSelect, onServiceChange }) {
|
|
14604
14789
|
const aiServicesHook = useAiServices(prod);
|
|
14605
14790
|
const imageParamsHook = useImageParams(prod);
|
|
14791
|
+
const [lastPrompt, setLastPrompt] = useState("");
|
|
14606
14792
|
const [selectedService, setSelectedService] = useState("");
|
|
14607
14793
|
const [dynamicForm, setDynamicForm] = useState({});
|
|
14608
14794
|
const [aiLoading, setAiLoading] = useState(false);
|
|
14609
14795
|
const [aiError, setAiError] = useState(null);
|
|
14610
14796
|
const [aiImage, setAiImage] = useState(null);
|
|
14797
|
+
const [generatedFile, setGeneratedFile] = useState(null);
|
|
14611
14798
|
const [showServiceSelection, setShowServiceSelection] = useState(true);
|
|
14612
14799
|
const [showAdvancedOptions, setShowAdvancedOptions] = useState(false);
|
|
14800
|
+
React.useEffect(() => {
|
|
14801
|
+
if (onServiceChange && selectedService) {
|
|
14802
|
+
onServiceChange(selectedService);
|
|
14803
|
+
}
|
|
14804
|
+
}, [selectedService]);
|
|
14613
14805
|
React.useEffect(() => {
|
|
14614
14806
|
if (!aiServicesHook.loading && aiServicesHook.services.length === 1) {
|
|
14615
14807
|
const service = aiServicesHook.services[0];
|
|
@@ -14633,12 +14825,17 @@ function TabAI({ prod, disabled, onGenerated }) {
|
|
|
14633
14825
|
const { name, value } = e.target;
|
|
14634
14826
|
setDynamicForm((prev) => ({ ...prev, [name]: value }));
|
|
14635
14827
|
};
|
|
14636
|
-
const handleDynamicFormSubmit = async (e) => {
|
|
14828
|
+
const handleDynamicFormSubmit = async (e, retry = false) => {
|
|
14637
14829
|
e.preventDefault();
|
|
14638
14830
|
setAiLoading(true);
|
|
14639
14831
|
setAiError(null);
|
|
14640
14832
|
setAiImage(null);
|
|
14641
14833
|
try {
|
|
14834
|
+
if (retry) {
|
|
14835
|
+
dynamicForm.prompt = lastPrompt;
|
|
14836
|
+
} else {
|
|
14837
|
+
setLastPrompt(dynamicForm.prompt || "");
|
|
14838
|
+
}
|
|
14642
14839
|
const response = await generateAiImage(dynamicForm, prod);
|
|
14643
14840
|
let imageData = null;
|
|
14644
14841
|
if (response?.data?.images && Array.isArray(response.data.images) && response.data.images.length > 0) {
|
|
@@ -14650,7 +14847,7 @@ function TabAI({ prod, disabled, onGenerated }) {
|
|
|
14650
14847
|
} else if (typeof response === "string") {
|
|
14651
14848
|
imageData = response;
|
|
14652
14849
|
}
|
|
14653
|
-
if (imageData
|
|
14850
|
+
if (imageData) {
|
|
14654
14851
|
if (imageData.startsWith("http")) {
|
|
14655
14852
|
try {
|
|
14656
14853
|
const apiBaseUrl = window.limboCore?.config?.getGlobal()?.prod ? "https://limbo.lefebvre.com" : "http://localhost";
|
|
@@ -14669,10 +14866,10 @@ function TabAI({ prod, disabled, onGenerated }) {
|
|
|
14669
14866
|
);
|
|
14670
14867
|
}
|
|
14671
14868
|
const blob = await imageResponse.blob();
|
|
14672
|
-
const file = new File([blob], "ai-image.
|
|
14673
|
-
type: blob.type || "image/
|
|
14869
|
+
const file = new File([blob], "ai-image.webp", {
|
|
14870
|
+
type: blob.type || "image/webp"
|
|
14674
14871
|
});
|
|
14675
|
-
|
|
14872
|
+
setGeneratedFile(file);
|
|
14676
14873
|
} catch (downloadError) {
|
|
14677
14874
|
throw new Error(
|
|
14678
14875
|
`Error downloading image via proxy: ${downloadError.message}`
|
|
@@ -14689,7 +14886,7 @@ function TabAI({ prod, disabled, onGenerated }) {
|
|
|
14689
14886
|
const file = new File([byteArray], "ai-image.webp", {
|
|
14690
14887
|
type: "image/webp"
|
|
14691
14888
|
});
|
|
14692
|
-
|
|
14889
|
+
setGeneratedFile(file);
|
|
14693
14890
|
}
|
|
14694
14891
|
} else {
|
|
14695
14892
|
throw new Error("No se pudo extraer la imagen de la respuesta");
|
|
@@ -14704,7 +14901,9 @@ function TabAI({ prod, disabled, onGenerated }) {
|
|
|
14704
14901
|
const renderDynamicForm = () => {
|
|
14705
14902
|
const params = imageParamsHook.params?.[selectedService]?.parameters;
|
|
14706
14903
|
if (!params) return null;
|
|
14707
|
-
const allFields = Object.entries(params).filter(
|
|
14904
|
+
const allFields = Object.entries(params).filter(
|
|
14905
|
+
([_, config]) => !config.hidden
|
|
14906
|
+
);
|
|
14708
14907
|
const promptFields = allFields.filter(
|
|
14709
14908
|
([key]) => key.toLowerCase().includes("prompt") || key.toLowerCase().includes("query")
|
|
14710
14909
|
);
|
|
@@ -14792,7 +14991,7 @@ function TabAI({ prod, disabled, onGenerated }) {
|
|
|
14792
14991
|
)
|
|
14793
14992
|
] }, key);
|
|
14794
14993
|
};
|
|
14795
|
-
return /* @__PURE__ */ jsxs("div", { children: [
|
|
14994
|
+
return /* @__PURE__ */ jsxs("div", { className: "h-fit w-full", children: [
|
|
14796
14995
|
aiServicesHook.services.length > 1 && /* @__PURE__ */ jsxs(
|
|
14797
14996
|
"button",
|
|
14798
14997
|
{
|
|
@@ -14803,6 +15002,7 @@ function TabAI({ prod, disabled, onGenerated }) {
|
|
|
14803
15002
|
setDynamicForm({});
|
|
14804
15003
|
setAiImage(null);
|
|
14805
15004
|
setAiError(null);
|
|
15005
|
+
setGeneratedFile(null);
|
|
14806
15006
|
},
|
|
14807
15007
|
className: "flex cursor-pointer items-center gap-2 text-brand-blue-600 hover:text-brand-blue-700 mb-4 font-medium",
|
|
14808
15008
|
children: [
|
|
@@ -14856,17 +15056,19 @@ function TabAI({ prod, disabled, onGenerated }) {
|
|
|
14856
15056
|
]
|
|
14857
15057
|
}
|
|
14858
15058
|
),
|
|
14859
|
-
showAdvancedOptions && /* @__PURE__ */ jsx("div", { className: "grid grid-cols-2 lg:grid-cols-3 xl:grid-cols-4 gap-3 animate-fadeIn", children: advancedFields.map(
|
|
15059
|
+
showAdvancedOptions && /* @__PURE__ */ jsx("div", { className: "grid grid-cols-2 lg:grid-cols-3 xl:grid-cols-4 gap-3 animate-fadeIn", children: advancedFields.map(
|
|
15060
|
+
([key, config]) => renderField(key, config)
|
|
15061
|
+
) })
|
|
14860
15062
|
] }),
|
|
14861
15063
|
/* @__PURE__ */ jsx(
|
|
14862
15064
|
"button",
|
|
14863
15065
|
{
|
|
14864
15066
|
type: "submit",
|
|
14865
15067
|
disabled: aiLoading || disabled,
|
|
14866
|
-
title: aiLoading ? "Generando imagen..." : "Generar imagen",
|
|
15068
|
+
title: aiLoading ? "Generando imagen..." : generatedFile ? "Generar otra imagen" : "Generar imagen",
|
|
14867
15069
|
className: `limbo-btn w-full mt-2 ${aiLoading ? "cursor-not-allowed limbo-btn-disabled" : "limbo-btn-primary"}`,
|
|
14868
15070
|
style: { minHeight: 44 },
|
|
14869
|
-
children: aiLoading ? "Generando..." : "Generar imagen"
|
|
15071
|
+
children: aiLoading ? "Generando imagen..." : generatedFile ? "Generar otra imagen" : "Generar imagen"
|
|
14870
15072
|
}
|
|
14871
15073
|
)
|
|
14872
15074
|
]
|
|
@@ -14891,12 +15093,12 @@ function TabAI({ prod, disabled, onGenerated }) {
|
|
|
14891
15093
|
if (!aiServicesHook.services.length) {
|
|
14892
15094
|
return /* @__PURE__ */ jsx("div", { className: "alert alert-warning", children: "No hay servicios IA disponibles." });
|
|
14893
15095
|
}
|
|
14894
|
-
return /* @__PURE__ */ jsx("div", { className: "flex flex-col
|
|
15096
|
+
return /* @__PURE__ */ jsx("div", { className: "flex flex-col mx-4 pb-2", children: showServiceSelection ? /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
14895
15097
|
/* @__PURE__ */ jsx(
|
|
14896
15098
|
"h3",
|
|
14897
15099
|
{
|
|
14898
15100
|
id: "aiSelectDescription",
|
|
14899
|
-
className: "text-
|
|
15101
|
+
className: "text-2xl font-semibold text-brand-blue-1000",
|
|
14900
15102
|
children: "Selecciona el modelo IA"
|
|
14901
15103
|
}
|
|
14902
15104
|
),
|
|
@@ -14911,6 +15113,7 @@ function TabAI({ prod, disabled, onGenerated }) {
|
|
|
14911
15113
|
setDynamicForm({});
|
|
14912
15114
|
setAiImage(null);
|
|
14913
15115
|
setAiError(null);
|
|
15116
|
+
setGeneratedFile(null);
|
|
14914
15117
|
imageParamsHook.fetchParams(service.slug, true);
|
|
14915
15118
|
},
|
|
14916
15119
|
disabled: aiServicesHook.loading || disabled,
|
|
@@ -14936,7 +15139,7 @@ function TabAI({ prod, disabled, onGenerated }) {
|
|
|
14936
15139
|
),
|
|
14937
15140
|
/* @__PURE__ */ jsxs("div", { className: "relative z-10 flex flex-col items-center justify-center gap-2 text-center", children: [
|
|
14938
15141
|
/* @__PURE__ */ jsx("span", { className: "text-base font-semibold text-gray-800", children: service.label }),
|
|
14939
|
-
service.description && /* @__PURE__ */ jsx("span", { className: "text-xs text-gray-
|
|
15142
|
+
service.description && /* @__PURE__ */ jsx("span", { className: "text-xs text-neutral-gray-800 line-clamp-2 drop-shadow drop-shadow-neutral-white-000", children: service.description })
|
|
14940
15143
|
] })
|
|
14941
15144
|
]
|
|
14942
15145
|
},
|
|
@@ -14945,22 +15148,38 @@ function TabAI({ prod, disabled, onGenerated }) {
|
|
|
14945
15148
|
] }) : (
|
|
14946
15149
|
/* Vista del formulario del servicio seleccionado */
|
|
14947
15150
|
/* @__PURE__ */ jsxs(Fragment, { children: [
|
|
14948
|
-
/* @__PURE__ */ jsx("h3", { className: "text-
|
|
15151
|
+
/* @__PURE__ */ jsx("h3", { className: "text-2xl font-semibold text-brand-blue-1000", children: aiServicesHook.services.find((s) => s.slug === selectedService)?.label || "Generación IA" }),
|
|
14949
15152
|
imageParamsHook.loading && /* @__PURE__ */ jsx("div", { children: "Cargando parámetros..." }),
|
|
14950
15153
|
imageParamsHook.error && /* @__PURE__ */ jsx("div", { className: "alert alert-danger", children: imageParamsHook.error }),
|
|
14951
15154
|
selectedService && renderDynamicForm(),
|
|
14952
|
-
aiError && /* @__PURE__ */ jsx("div", { className: "alert alert-danger", children: aiError }),
|
|
14953
|
-
|
|
14954
|
-
|
|
14955
|
-
|
|
14956
|
-
|
|
14957
|
-
|
|
14958
|
-
|
|
14959
|
-
|
|
14960
|
-
|
|
14961
|
-
|
|
14962
|
-
|
|
14963
|
-
|
|
15155
|
+
aiError && /* @__PURE__ */ jsx("div", { className: "alert alert-danger mt-4", children: aiError }),
|
|
15156
|
+
generatedFile && /* @__PURE__ */ jsx("div", { className: "mt-6 md:w-full mx-auto", children: /* @__PURE__ */ jsx(
|
|
15157
|
+
ImagePreview,
|
|
15158
|
+
{
|
|
15159
|
+
image: generatedFile,
|
|
15160
|
+
onDiscard: () => {
|
|
15161
|
+
setGeneratedFile(null);
|
|
15162
|
+
setAiImage(null);
|
|
15163
|
+
setAiError(null);
|
|
15164
|
+
},
|
|
15165
|
+
onRetry: (e) => handleDynamicFormSubmit(e, true),
|
|
15166
|
+
onDownload: () => {
|
|
15167
|
+
},
|
|
15168
|
+
onSelect: (editedName) => {
|
|
15169
|
+
if (onSelect) {
|
|
15170
|
+
const fileToSelect = generatedFile || aiImage;
|
|
15171
|
+
if (editedName && fileToSelect && editedName !== fileToSelect.name) {
|
|
15172
|
+
const newFile = new File([fileToSelect], editedName, { type: fileToSelect.type });
|
|
15173
|
+
onSelect(newFile);
|
|
15174
|
+
} else {
|
|
15175
|
+
onSelect(fileToSelect);
|
|
15176
|
+
}
|
|
15177
|
+
}
|
|
15178
|
+
},
|
|
15179
|
+
showRetry: true,
|
|
15180
|
+
disabled: aiLoading || disabled
|
|
15181
|
+
}
|
|
15182
|
+
) })
|
|
14964
15183
|
] })
|
|
14965
15184
|
) });
|
|
14966
15185
|
}
|
|
@@ -15033,7 +15252,7 @@ function useStockServices(prod = false) {
|
|
|
15033
15252
|
};
|
|
15034
15253
|
return { services, loading, error, invalidateCache };
|
|
15035
15254
|
}
|
|
15036
|
-
function TabStock({ prod, disabled,
|
|
15255
|
+
function TabStock({ prod, disabled, onSelect, onServiceChange }) {
|
|
15037
15256
|
const lastSearchPayloadRef = React.useRef(null);
|
|
15038
15257
|
const stockServicesHook = useStockServices(prod);
|
|
15039
15258
|
const loadSavedState = (key, defaultValue) => {
|
|
@@ -15059,12 +15278,14 @@ function TabStock({ prod, disabled, onSelected }) {
|
|
|
15059
15278
|
const [paginationInfo, setPaginationInfo] = useState(
|
|
15060
15279
|
() => loadSavedState("paginationInfo", null)
|
|
15061
15280
|
);
|
|
15281
|
+
const [lastQuery, setLastQuery] = useState(null);
|
|
15062
15282
|
const imageParamsHook = useImageParams(prod);
|
|
15063
15283
|
const [stockLoading, setStockLoading] = useState(false);
|
|
15064
15284
|
const [stockError, setStockError] = useState(null);
|
|
15065
15285
|
const [downloadingId, setDownloadingId] = useState(null);
|
|
15066
15286
|
const [showServiceSelection, setShowServiceSelection] = useState(true);
|
|
15067
15287
|
const [showAdvancedOptions, setShowAdvancedOptions] = useState(false);
|
|
15288
|
+
const [selectedFile, setSelectedFile] = useState(null);
|
|
15068
15289
|
React.useEffect(() => {
|
|
15069
15290
|
sessionStorage.setItem(
|
|
15070
15291
|
"limbo_stock_selectedService",
|
|
@@ -15114,6 +15335,11 @@ function TabStock({ prod, disabled, onSelected }) {
|
|
|
15114
15335
|
setDynamicForm(initialValues);
|
|
15115
15336
|
}
|
|
15116
15337
|
}, [selectedService, imageParamsHook.params]);
|
|
15338
|
+
React.useEffect(() => {
|
|
15339
|
+
if (onServiceChange && selectedService) {
|
|
15340
|
+
onServiceChange(selectedService);
|
|
15341
|
+
}
|
|
15342
|
+
}, [selectedService]);
|
|
15117
15343
|
const handleDynamicFormChange = (e) => {
|
|
15118
15344
|
const { name, value } = e.target;
|
|
15119
15345
|
setDynamicForm((prev) => ({ ...prev, [name]: value }));
|
|
@@ -15184,6 +15410,7 @@ function TabStock({ prod, disabled, onSelected }) {
|
|
|
15184
15410
|
setStockError("La búsqueda debe tener al menos 5 caracteres");
|
|
15185
15411
|
return;
|
|
15186
15412
|
}
|
|
15413
|
+
setLastQuery(query);
|
|
15187
15414
|
await performSearch(1);
|
|
15188
15415
|
};
|
|
15189
15416
|
const handlePageChange = (newPage) => {
|
|
@@ -15207,7 +15434,7 @@ function TabStock({ prod, disabled, onSelected }) {
|
|
|
15207
15434
|
const file = new File([blob], filename, {
|
|
15208
15435
|
type: blob.type || "image/jpeg"
|
|
15209
15436
|
});
|
|
15210
|
-
|
|
15437
|
+
setSelectedFile(file);
|
|
15211
15438
|
} else {
|
|
15212
15439
|
throw new Error("No se pudo obtener la URL de descarga");
|
|
15213
15440
|
}
|
|
@@ -15434,8 +15661,8 @@ function TabStock({ prod, disabled, onSelected }) {
|
|
|
15434
15661
|
if (!stockServicesHook.services.length) {
|
|
15435
15662
|
return /* @__PURE__ */ jsx("div", { className: "alert alert-warning", children: "No hay servicios de Stock disponibles." });
|
|
15436
15663
|
}
|
|
15437
|
-
return /* @__PURE__ */ jsx("div", { className: "flex flex-col
|
|
15438
|
-
/* @__PURE__ */ jsx("h3", { className: "text-
|
|
15664
|
+
return /* @__PURE__ */ jsx("div", { className: "flex flex-col mx-4 pb-2", children: showServiceSelection ? /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
15665
|
+
/* @__PURE__ */ jsx("h3", { className: "text-2xl font-semibold text-brand-blue-1000", children: "Selecciona el servicio de Stock" }),
|
|
15439
15666
|
stockServicesHook.error && /* @__PURE__ */ jsx("div", { className: "alert alert-danger", children: stockServicesHook.error }),
|
|
15440
15667
|
/* @__PURE__ */ jsx("div", { className: "grid grid-cols-2 md:grid-cols-3 gap-3", children: stockServicesHook.services.map((service) => /* @__PURE__ */ jsxs(
|
|
15441
15668
|
"button",
|
|
@@ -15483,12 +15710,36 @@ function TabStock({ prod, disabled, onSelected }) {
|
|
|
15483
15710
|
] }) : (
|
|
15484
15711
|
/* Vista del formulario del servicio seleccionado */
|
|
15485
15712
|
/* @__PURE__ */ jsxs(Fragment, { children: [
|
|
15486
|
-
/* @__PURE__ */ jsx("h3", { className: "text-
|
|
15713
|
+
/* @__PURE__ */ jsx("h3", { className: "text-2xl font-semibold text-brand-blue-1000", children: stockServicesHook.services.find((s) => s.slug === selectedService)?.label || "Búsqueda Stock" }),
|
|
15487
15714
|
imageParamsHook.loading && /* @__PURE__ */ jsx("div", { children: "Cargando parámetros..." }),
|
|
15488
15715
|
imageParamsHook.error && /* @__PURE__ */ jsx("div", { className: "alert alert-danger", children: imageParamsHook.error }),
|
|
15489
15716
|
selectedService && renderDynamicForm(),
|
|
15490
|
-
stockError && /* @__PURE__ */ jsx("div", { className: "alert alert-danger", children: stockError }),
|
|
15491
|
-
|
|
15717
|
+
stockError && /* @__PURE__ */ jsx("div", { className: "alert alert-danger mt-4", children: stockError }),
|
|
15718
|
+
selectedFile && /* @__PURE__ */ jsx("div", { className: "mt-6 md:w-full mx-auto", children: /* @__PURE__ */ jsx(
|
|
15719
|
+
ImagePreview,
|
|
15720
|
+
{
|
|
15721
|
+
image: selectedFile,
|
|
15722
|
+
onDiscard: () => {
|
|
15723
|
+
setSelectedFile(null);
|
|
15724
|
+
setStockError(null);
|
|
15725
|
+
},
|
|
15726
|
+
onDownload: () => {
|
|
15727
|
+
},
|
|
15728
|
+
onSelect: (editedName) => {
|
|
15729
|
+
if (onSelect) {
|
|
15730
|
+
if (editedName && selectedFile && editedName !== selectedFile.name) {
|
|
15731
|
+
const newFile = new File([selectedFile], editedName, { type: selectedFile.type });
|
|
15732
|
+
onSelect(newFile);
|
|
15733
|
+
} else {
|
|
15734
|
+
onSelect(selectedFile);
|
|
15735
|
+
}
|
|
15736
|
+
}
|
|
15737
|
+
},
|
|
15738
|
+
showRetry: false,
|
|
15739
|
+
disabled
|
|
15740
|
+
}
|
|
15741
|
+
) }),
|
|
15742
|
+
!selectedFile && stockImages.length > 0 && /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
15492
15743
|
/* @__PURE__ */ jsxs(
|
|
15493
15744
|
"div",
|
|
15494
15745
|
{
|
|
@@ -15524,7 +15775,19 @@ function TabStock({ prod, disabled, onSelected }) {
|
|
|
15524
15775
|
/* @__PURE__ */ jsx("div", { className: "absolute inset-0 border-4 border-brand-blue-600 rounded-full border-t-transparent animate-spin" })
|
|
15525
15776
|
] }),
|
|
15526
15777
|
/* @__PURE__ */ jsx("span", { className: "text-sm font-medium text-brand-blue-900", children: "Descargando..." })
|
|
15527
|
-
] }) : /* @__PURE__ */
|
|
15778
|
+
] }) : /* @__PURE__ */ jsxs("div", { className: "absolute inset-0 bg-brand-blue-800/0 group-hover:bg-brand-blue-800/50 transition-colors duration-200 flex items-center justify-center flex-col", children: [
|
|
15779
|
+
/* @__PURE__ */ jsx("span", { className: "icon icon-round-check-filled icon--2xl max-w-full max-h-full opacity-0 group-hover:opacity-100 transition-opacity duration-200" }),
|
|
15780
|
+
/* @__PURE__ */ jsx(
|
|
15781
|
+
"span",
|
|
15782
|
+
{
|
|
15783
|
+
style: {
|
|
15784
|
+
textShadow: "1px 0 #0d542b, -1px 0 #0d542b, 0 1px #0d542b, 0 -0.5px #0d542b, 0.5px 0.5px #0d542b, -1px -1px #0d542b, 1px -1px #0d542b, -1px 1px #0d542b"
|
|
15785
|
+
},
|
|
15786
|
+
className: "max-w-full max-h-full opacity-0 group-hover:opacity-100 transition-opacity duration-200 text-neutral-white-000",
|
|
15787
|
+
children: "Seleccionar"
|
|
15788
|
+
}
|
|
15789
|
+
)
|
|
15790
|
+
] })
|
|
15528
15791
|
] })
|
|
15529
15792
|
},
|
|
15530
15793
|
img.id || idx
|
|
@@ -15580,7 +15843,7 @@ function TabStock({ prod, disabled, onSelected }) {
|
|
|
15580
15843
|
)
|
|
15581
15844
|
] })
|
|
15582
15845
|
] }),
|
|
15583
|
-
!stockLoading && stockImages.length === 0 && selectedService &&
|
|
15846
|
+
!stockLoading && stockImages.length === 0 && selectedService && lastQuery && /* @__PURE__ */ jsxs("div", { className: "mt-6 text-center text-neutral-600 py-8 bg-neutral-50 rounded-lg", children: [
|
|
15584
15847
|
/* @__PURE__ */ jsx("span", { className: "icon icon-search icon--lg mb-2" }),
|
|
15585
15848
|
/* @__PURE__ */ jsxs("p", { children: [
|
|
15586
15849
|
'No se encontraron imágenes para "',
|
|
@@ -15676,7 +15939,7 @@ const ValidatedImage = ({ src, alt, className, onError }) => {
|
|
|
15676
15939
|
}
|
|
15677
15940
|
);
|
|
15678
15941
|
};
|
|
15679
|
-
function TabPortals({ prod, disabled,
|
|
15942
|
+
function TabPortals({ prod, disabled, onSelect }) {
|
|
15680
15943
|
const lastSearchPayloadRef = React.useRef(null);
|
|
15681
15944
|
const portalSourcesHook = usePortalSources(prod);
|
|
15682
15945
|
const loadSavedState = (key, defaultValue) => {
|
|
@@ -15707,6 +15970,7 @@ function TabPortals({ prod, disabled, onSelected }) {
|
|
|
15707
15970
|
() => loadSavedState("paginationInfo", null)
|
|
15708
15971
|
);
|
|
15709
15972
|
const [downloadingUrl, setDownloadingUrl] = useState(null);
|
|
15973
|
+
const [selectedFile, setSelectedFile] = useState(null);
|
|
15710
15974
|
const searchCacheRef = useRef({});
|
|
15711
15975
|
const [failedImages, setFailedImages] = useState(/* @__PURE__ */ new Set());
|
|
15712
15976
|
React.useEffect(() => {
|
|
@@ -15882,7 +16146,7 @@ function TabPortals({ prod, disabled, onSelected }) {
|
|
|
15882
16146
|
const file = new File([blob], filename, {
|
|
15883
16147
|
type: blob.type || "image/jpeg"
|
|
15884
16148
|
});
|
|
15885
|
-
|
|
16149
|
+
setSelectedFile(file);
|
|
15886
16150
|
} catch (err) {
|
|
15887
16151
|
setError(err.message || "No se pudo recuperar la imagen del portal");
|
|
15888
16152
|
} finally {
|
|
@@ -15898,7 +16162,7 @@ function TabPortals({ prod, disabled, onSelected }) {
|
|
|
15898
16162
|
if (!portalSourcesHook.sources.length) {
|
|
15899
16163
|
return /* @__PURE__ */ jsx("div", { className: "alert alert-warning", children: "No hay portales externos disponibles." });
|
|
15900
16164
|
}
|
|
15901
|
-
return /* @__PURE__ */ jsxs("div", { className: "flex flex-col gap-
|
|
16165
|
+
return /* @__PURE__ */ jsxs("div", { className: "flex flex-col gap-2", children: [
|
|
15902
16166
|
/* @__PURE__ */ jsx("h3", { className: "text-lg font-semibold text-brand-blue-1000", children: "Buscar en Portales Externos" }),
|
|
15903
16167
|
/* @__PURE__ */ jsxs(
|
|
15904
16168
|
"form",
|
|
@@ -15932,33 +16196,20 @@ function TabPortals({ prod, disabled, onSelected }) {
|
|
|
15932
16196
|
onClick: () => handlePortalToggle(portal.id),
|
|
15933
16197
|
disabled,
|
|
15934
16198
|
className: `
|
|
15935
|
-
relative p-4 rounded-lg border-2 transition-all duration-200
|
|
15936
|
-
flex flex-col items-center justify-center min-h-[100px] gap-2
|
|
15937
|
-
${isSelected ? "border-brand-blue-600 bg-brand-blue-50" : "border-gray-200 bg-white hover:border-brand-blue-300"}
|
|
16199
|
+
relative p-4 rounded-lg border-2 transition-all duration-200 flex flex-col items-center justify-center min-h-[100px] gap-2 max-h-[110px] box-border ${isSelected ? "border-brand-blue-600 bg-brand-blue-50" : "border-gray-200 bg-white hover:border-brand-blue-300"}
|
|
15938
16200
|
${disabled ? "opacity-50 cursor-not-allowed" : "cursor-pointer"}
|
|
15939
16201
|
hover:shadow-md
|
|
15940
16202
|
`,
|
|
15941
16203
|
children: [
|
|
15942
|
-
isSelected && /* @__PURE__ */ jsx("div", { className: "absolute top-2 right-2 w-6 h-6 bg-brand-blue-600 rounded-full flex items-center justify-center", children: /* @__PURE__ */ jsx("span", { className: "icon icon-
|
|
16204
|
+
isSelected && /* @__PURE__ */ jsx("div", { className: "absolute top-2 right-2 w-6 h-6 bg-brand-blue-600 rounded-full flex items-center justify-center bg-black", children: /* @__PURE__ */ jsx("span", { className: "icon icon-tick-white icon--sm" }) }),
|
|
15943
16205
|
/* @__PURE__ */ jsx(
|
|
15944
|
-
"
|
|
16206
|
+
"img",
|
|
15945
16207
|
{
|
|
15946
|
-
className:
|
|
15947
|
-
|
|
15948
|
-
|
|
15949
|
-
viewBox: "0 0 24 24",
|
|
15950
|
-
children: /* @__PURE__ */ jsx(
|
|
15951
|
-
"path",
|
|
15952
|
-
{
|
|
15953
|
-
strokeLinecap: "round",
|
|
15954
|
-
strokeLinejoin: "round",
|
|
15955
|
-
strokeWidth: 2,
|
|
15956
|
-
d: "M19 11H5m14 0a2 2 0 012 2v6a2 2 0 01-2 2H5a2 2 0 01-2-2v-6a2 2 0 012-2m14 0V9a2 2 0 00-2-2M5 11V9a2 2 0 012-2m0 0V5a2 2 0 012-2h6a2 2 0 012 2v2M7 7h10"
|
|
15957
|
-
}
|
|
15958
|
-
)
|
|
16208
|
+
className: "object-auto bg-no-repeat bg-center max-w-9/12 max-h-9/12",
|
|
16209
|
+
src: portal.image,
|
|
16210
|
+
alt: portal.title
|
|
15959
16211
|
}
|
|
15960
|
-
)
|
|
15961
|
-
/* @__PURE__ */ jsx("span", { className: `text-sm font-medium text-center ${isSelected ? "text-brand-blue-900" : "text-gray-700"}`, children: portal.title })
|
|
16212
|
+
)
|
|
15962
16213
|
]
|
|
15963
16214
|
},
|
|
15964
16215
|
portal.id
|
|
@@ -15987,7 +16238,7 @@ function TabPortals({ prod, disabled, onSelected }) {
|
|
|
15987
16238
|
}
|
|
15988
16239
|
)
|
|
15989
16240
|
] }),
|
|
15990
|
-
/* @__PURE__ */ jsxs("div", { className: "flex
|
|
16241
|
+
/* @__PURE__ */ jsxs("div", { className: "flex-col gap-1 hidden", children: [
|
|
15991
16242
|
/* @__PURE__ */ jsx(
|
|
15992
16243
|
"label",
|
|
15993
16244
|
{
|
|
@@ -16000,7 +16251,7 @@ function TabPortals({ prod, disabled, onSelected }) {
|
|
|
16000
16251
|
"select",
|
|
16001
16252
|
{
|
|
16002
16253
|
id: "portal-limit",
|
|
16003
|
-
value:
|
|
16254
|
+
value: 10,
|
|
16004
16255
|
onChange: (e) => setLimit(Number(e.target.value)),
|
|
16005
16256
|
className: "limbo-input",
|
|
16006
16257
|
disabled,
|
|
@@ -16035,7 +16286,14 @@ function TabPortals({ prod, disabled, onSelected }) {
|
|
|
16035
16286
|
{
|
|
16036
16287
|
className: `flex items-center justify-between p-2 rounded ${result.status === 200 ? "bg-green-50" : "bg-red-50"}`,
|
|
16037
16288
|
children: [
|
|
16038
|
-
/* @__PURE__ */ jsx(
|
|
16289
|
+
result.image ? /* @__PURE__ */ jsx(
|
|
16290
|
+
"img",
|
|
16291
|
+
{
|
|
16292
|
+
src: result.image,
|
|
16293
|
+
alt: result.title,
|
|
16294
|
+
className: "w-10 h-10 object-cover rounded"
|
|
16295
|
+
}
|
|
16296
|
+
) : /* @__PURE__ */ jsx("span", { className: "font-medium", children: result.title }),
|
|
16039
16297
|
/* @__PURE__ */ jsx(
|
|
16040
16298
|
"span",
|
|
16041
16299
|
{
|
|
@@ -16048,20 +16306,38 @@ function TabPortals({ prod, disabled, onSelected }) {
|
|
|
16048
16306
|
portalId
|
|
16049
16307
|
)) })
|
|
16050
16308
|
] }),
|
|
16051
|
-
|
|
16309
|
+
selectedFile && /* @__PURE__ */ jsx("div", { className: "mt-6 md:w-full mx-auto", children: /* @__PURE__ */ jsx(
|
|
16310
|
+
ImagePreview,
|
|
16311
|
+
{
|
|
16312
|
+
image: selectedFile,
|
|
16313
|
+
onDiscard: () => {
|
|
16314
|
+
setSelectedFile(null);
|
|
16315
|
+
setError(null);
|
|
16316
|
+
},
|
|
16317
|
+
onDownload: () => {
|
|
16318
|
+
},
|
|
16319
|
+
onSelect: (editedName) => {
|
|
16320
|
+
if (onSelect) {
|
|
16321
|
+
if (editedName && selectedFile && editedName !== selectedFile.name) {
|
|
16322
|
+
const newFile = new File([selectedFile], editedName, { type: selectedFile.type });
|
|
16323
|
+
onSelect(newFile);
|
|
16324
|
+
} else {
|
|
16325
|
+
onSelect(selectedFile);
|
|
16326
|
+
}
|
|
16327
|
+
}
|
|
16328
|
+
},
|
|
16329
|
+
showRetry: false,
|
|
16330
|
+
disabled
|
|
16331
|
+
}
|
|
16332
|
+
) }),
|
|
16333
|
+
!selectedFile && (images.length > 0 || loading) && /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
16052
16334
|
/* @__PURE__ */ jsxs("div", { className: "mt-6 relative", "aria-live": "polite", children: [
|
|
16053
16335
|
loading && /* @__PURE__ */ jsx("div", { className: "absolute inset-0 bg-white/80 z-10 flex items-center justify-center rounded-lg", children: /* @__PURE__ */ jsxs("div", { className: "flex flex-col items-center gap-2", children: [
|
|
16054
16336
|
/* @__PURE__ */ jsx("span", { className: "limbo-loader" }),
|
|
16055
16337
|
/* @__PURE__ */ jsx("span", { className: "text-sm text-brand-blue-800", children: "Buscando imágenes..." })
|
|
16056
16338
|
] }) }),
|
|
16057
16339
|
/* @__PURE__ */ jsxs("div", { className: "relative grid grid-cols-2 md:grid-cols-3 lg:grid-cols-4 gap-2", children: [
|
|
16058
|
-
/* @__PURE__ */ jsx(
|
|
16059
|
-
LoadingOverlay,
|
|
16060
|
-
{
|
|
16061
|
-
show: loading,
|
|
16062
|
-
message: "Cargando imágenes..."
|
|
16063
|
-
}
|
|
16064
|
-
),
|
|
16340
|
+
/* @__PURE__ */ jsx(LoadingOverlay, { show: loading, message: "Cargando imágenes..." }),
|
|
16065
16341
|
images.map((img, idx) => {
|
|
16066
16342
|
const imageKey = `${img.source}-${img.id || idx}`;
|
|
16067
16343
|
const imageUrl = img.preview || img.thumbnail || img.url || img.full;
|
|
@@ -16188,7 +16464,11 @@ const TABS = [
|
|
|
16188
16464
|
{ id: "portals", label: "Otros portales" }
|
|
16189
16465
|
];
|
|
16190
16466
|
function UploadForm({
|
|
16191
|
-
|
|
16467
|
+
onSelect,
|
|
16468
|
+
onSubTabChange = null,
|
|
16469
|
+
// Callback para notificar cambio de sub-tab a App.jsx
|
|
16470
|
+
onServiceChange = null,
|
|
16471
|
+
// Callback para notificar cambio de servicio (modelo IA/Stock)
|
|
16192
16472
|
disabled = false,
|
|
16193
16473
|
apiKey,
|
|
16194
16474
|
prod = false
|
|
@@ -16197,6 +16477,16 @@ function UploadForm({
|
|
|
16197
16477
|
const [file, setFile] = useState(null);
|
|
16198
16478
|
const [previewUrl, setPreviewUrl] = useState(null);
|
|
16199
16479
|
const fileInputRef = useRef();
|
|
16480
|
+
React.useEffect(() => {
|
|
16481
|
+
if (onSubTabChange) {
|
|
16482
|
+
onSubTabChange(activeTab);
|
|
16483
|
+
}
|
|
16484
|
+
}, [activeTab, onSubTabChange]);
|
|
16485
|
+
const handleServiceChange = (serviceSlug) => {
|
|
16486
|
+
if (onServiceChange && serviceSlug) {
|
|
16487
|
+
onServiceChange(activeTab, serviceSlug);
|
|
16488
|
+
}
|
|
16489
|
+
};
|
|
16200
16490
|
const handleTabKeyDown = (event, tabId) => {
|
|
16201
16491
|
const currentIndex = TABS.findIndex((tab) => tab.id === tabId);
|
|
16202
16492
|
let nextIndex = currentIndex;
|
|
@@ -16229,7 +16519,9 @@ function UploadForm({
|
|
|
16229
16519
|
if (nextTab) {
|
|
16230
16520
|
setActiveTab(nextTab.id);
|
|
16231
16521
|
setTimeout(() => {
|
|
16232
|
-
const tabButton = document.querySelector(
|
|
16522
|
+
const tabButton = document.querySelector(
|
|
16523
|
+
`[data-upload-tab-id="${nextTab.id}"]`
|
|
16524
|
+
);
|
|
16233
16525
|
if (tabButton) {
|
|
16234
16526
|
tabButton.focus();
|
|
16235
16527
|
}
|
|
@@ -16240,7 +16532,7 @@ function UploadForm({
|
|
|
16240
16532
|
/* @__PURE__ */ jsx("div", { className: "limbo-tabs-container", children: /* @__PURE__ */ jsx(
|
|
16241
16533
|
"div",
|
|
16242
16534
|
{
|
|
16243
|
-
className: "limbo-tabs mb-
|
|
16535
|
+
className: "limbo-tabs mb-1 min-w-fit",
|
|
16244
16536
|
role: "tablist",
|
|
16245
16537
|
"aria-label": "Opciones de subida de imagen",
|
|
16246
16538
|
children: TABS.map((tab) => /* @__PURE__ */ jsx(
|
|
@@ -16263,59 +16555,58 @@ function UploadForm({
|
|
|
16263
16555
|
))
|
|
16264
16556
|
}
|
|
16265
16557
|
) }),
|
|
16266
|
-
/* @__PURE__ */ jsxs(
|
|
16267
|
-
|
|
16268
|
-
|
|
16269
|
-
|
|
16270
|
-
|
|
16271
|
-
|
|
16272
|
-
|
|
16273
|
-
|
|
16274
|
-
|
|
16275
|
-
|
|
16276
|
-
|
|
16277
|
-
|
|
16278
|
-
|
|
16279
|
-
|
|
16280
|
-
|
|
16281
|
-
|
|
16282
|
-
|
|
16283
|
-
|
|
16284
|
-
|
|
16285
|
-
|
|
16286
|
-
|
|
16287
|
-
|
|
16288
|
-
|
|
16289
|
-
|
|
16290
|
-
|
|
16291
|
-
|
|
16292
|
-
|
|
16293
|
-
|
|
16294
|
-
|
|
16295
|
-
|
|
16296
|
-
|
|
16297
|
-
|
|
16298
|
-
|
|
16299
|
-
|
|
16300
|
-
|
|
16301
|
-
|
|
16302
|
-
|
|
16303
|
-
|
|
16304
|
-
|
|
16305
|
-
|
|
16306
|
-
|
|
16307
|
-
|
|
16308
|
-
|
|
16309
|
-
|
|
16310
|
-
|
|
16311
|
-
|
|
16312
|
-
|
|
16313
|
-
|
|
16314
|
-
|
|
16315
|
-
|
|
16316
|
-
|
|
16317
|
-
|
|
16318
|
-
] })
|
|
16558
|
+
/* @__PURE__ */ jsxs(
|
|
16559
|
+
"div",
|
|
16560
|
+
{
|
|
16561
|
+
className: "limbo-tab-content px-2 py-2",
|
|
16562
|
+
role: "tabpanel",
|
|
16563
|
+
id: `upload-tabpanel-${activeTab}`,
|
|
16564
|
+
"aria-labelledby": `upload-tab-${activeTab}`,
|
|
16565
|
+
children: [
|
|
16566
|
+
activeTab === "upload" && /* @__PURE__ */ jsx(
|
|
16567
|
+
TabUpload,
|
|
16568
|
+
{
|
|
16569
|
+
file,
|
|
16570
|
+
setFile,
|
|
16571
|
+
previewUrl,
|
|
16572
|
+
setPreviewUrl,
|
|
16573
|
+
fileInputRef,
|
|
16574
|
+
onSelect,
|
|
16575
|
+
disabled
|
|
16576
|
+
}
|
|
16577
|
+
),
|
|
16578
|
+
activeTab === "ai" && /* @__PURE__ */ jsx(
|
|
16579
|
+
TabAI,
|
|
16580
|
+
{
|
|
16581
|
+
apiKey,
|
|
16582
|
+
prod,
|
|
16583
|
+
disabled,
|
|
16584
|
+
onSelect,
|
|
16585
|
+
onServiceChange: handleServiceChange
|
|
16586
|
+
}
|
|
16587
|
+
),
|
|
16588
|
+
activeTab === "stock" && /* @__PURE__ */ jsx(
|
|
16589
|
+
TabStock,
|
|
16590
|
+
{
|
|
16591
|
+
apiKey,
|
|
16592
|
+
prod,
|
|
16593
|
+
disabled,
|
|
16594
|
+
onSelect,
|
|
16595
|
+
onServiceChange: handleServiceChange
|
|
16596
|
+
}
|
|
16597
|
+
),
|
|
16598
|
+
activeTab === "portals" && /* @__PURE__ */ jsx(
|
|
16599
|
+
TabPortals,
|
|
16600
|
+
{
|
|
16601
|
+
apiKey,
|
|
16602
|
+
prod,
|
|
16603
|
+
disabled,
|
|
16604
|
+
onSelect
|
|
16605
|
+
}
|
|
16606
|
+
)
|
|
16607
|
+
]
|
|
16608
|
+
}
|
|
16609
|
+
)
|
|
16319
16610
|
] });
|
|
16320
16611
|
}
|
|
16321
16612
|
const IS_BROWSER = typeof window !== "undefined" && typeof window.document !== "undefined";
|
|
@@ -19739,14 +20030,22 @@ function CropperView({
|
|
|
19739
20030
|
onError = null,
|
|
19740
20031
|
// Callback para manejar errores
|
|
19741
20032
|
deleting = false,
|
|
19742
|
-
onVariantCreated = null
|
|
20033
|
+
onVariantCreated = null,
|
|
19743
20034
|
// Callback cuando se crea una variante
|
|
20035
|
+
onUpload = null,
|
|
20036
|
+
// Callback para subir imagen nueva (cuando image.file existe)
|
|
20037
|
+
uploading = false
|
|
20038
|
+
// Estado de carga del upload
|
|
19744
20039
|
}) {
|
|
19745
20040
|
const [showPreview, setShowPreview] = useState(false);
|
|
19746
20041
|
const [previewUrl, setPreviewUrl] = useState(null);
|
|
19747
20042
|
const [previewLoading, setPreviewLoading] = useState(false);
|
|
19748
20043
|
const [showGrid, setShowGrid] = useState(true);
|
|
19749
20044
|
const [shade, setShade] = useState(true);
|
|
20045
|
+
const [previewPosition, setPreviewPosition] = useState({ x: 20, y: 100 });
|
|
20046
|
+
const [isDragging, setIsDragging] = useState(false);
|
|
20047
|
+
const [dragStart, setDragStart] = useState({ x: 0, y: 0 });
|
|
20048
|
+
const [showPreviewLightbox, setShowPreviewLightbox] = useState(false);
|
|
19750
20049
|
const [initialLoading, setInitialLoading] = useState(true);
|
|
19751
20050
|
const [cropTransitioning, setcropTransitioning] = useState(false);
|
|
19752
20051
|
const [flipStates, setFlipStates] = useState({
|
|
@@ -20217,8 +20516,92 @@ function CropperView({
|
|
|
20217
20516
|
setPreviewLoading(false);
|
|
20218
20517
|
}
|
|
20219
20518
|
}, [canExport, generatePreview, showPreview]);
|
|
20519
|
+
const handlePreviewMouseDown = useCallback(
|
|
20520
|
+
(e) => {
|
|
20521
|
+
if (!e.target.closest(".preview-modal-header")) return;
|
|
20522
|
+
setIsDragging(true);
|
|
20523
|
+
setDragStart({
|
|
20524
|
+
x: e.clientX - previewPosition.x,
|
|
20525
|
+
y: e.clientY - previewPosition.y
|
|
20526
|
+
});
|
|
20527
|
+
},
|
|
20528
|
+
[previewPosition]
|
|
20529
|
+
);
|
|
20530
|
+
const handlePreviewMouseMove = useCallback(
|
|
20531
|
+
(e) => {
|
|
20532
|
+
if (!isDragging) return;
|
|
20533
|
+
e.preventDefault();
|
|
20534
|
+
setPreviewPosition({
|
|
20535
|
+
x: e.clientX - dragStart.x,
|
|
20536
|
+
y: e.clientY - dragStart.y
|
|
20537
|
+
});
|
|
20538
|
+
},
|
|
20539
|
+
[isDragging, dragStart]
|
|
20540
|
+
);
|
|
20541
|
+
const handlePreviewMouseUp = useCallback(() => {
|
|
20542
|
+
setIsDragging(false);
|
|
20543
|
+
}, []);
|
|
20544
|
+
const handlePreviewTouchStart = useCallback(
|
|
20545
|
+
(e) => {
|
|
20546
|
+
if (!e.target.closest(".preview-modal-header")) return;
|
|
20547
|
+
const touch = e.touches[0];
|
|
20548
|
+
setIsDragging(true);
|
|
20549
|
+
setDragStart({
|
|
20550
|
+
x: touch.clientX - previewPosition.x,
|
|
20551
|
+
y: touch.clientY - previewPosition.y
|
|
20552
|
+
});
|
|
20553
|
+
},
|
|
20554
|
+
[previewPosition]
|
|
20555
|
+
);
|
|
20556
|
+
const handlePreviewTouchMove = useCallback(
|
|
20557
|
+
(e) => {
|
|
20558
|
+
if (!isDragging) return;
|
|
20559
|
+
e.preventDefault();
|
|
20560
|
+
const touch = e.touches[0];
|
|
20561
|
+
setPreviewPosition({
|
|
20562
|
+
x: touch.clientX - dragStart.x,
|
|
20563
|
+
y: touch.clientY - dragStart.y
|
|
20564
|
+
});
|
|
20565
|
+
},
|
|
20566
|
+
[isDragging, dragStart]
|
|
20567
|
+
);
|
|
20568
|
+
const handlePreviewTouchEnd = useCallback(() => {
|
|
20569
|
+
setIsDragging(false);
|
|
20570
|
+
}, []);
|
|
20571
|
+
useEffect(() => {
|
|
20572
|
+
if (isDragging) {
|
|
20573
|
+
document.addEventListener("mousemove", handlePreviewMouseMove);
|
|
20574
|
+
document.addEventListener("mouseup", handlePreviewMouseUp);
|
|
20575
|
+
document.addEventListener("touchmove", handlePreviewTouchMove, {
|
|
20576
|
+
passive: false
|
|
20577
|
+
});
|
|
20578
|
+
document.addEventListener("touchend", handlePreviewTouchEnd);
|
|
20579
|
+
return () => {
|
|
20580
|
+
document.removeEventListener("mousemove", handlePreviewMouseMove);
|
|
20581
|
+
document.removeEventListener("mouseup", handlePreviewMouseUp);
|
|
20582
|
+
document.removeEventListener("touchmove", handlePreviewTouchMove);
|
|
20583
|
+
document.removeEventListener("touchend", handlePreviewTouchEnd);
|
|
20584
|
+
};
|
|
20585
|
+
}
|
|
20586
|
+
}, [
|
|
20587
|
+
isDragging,
|
|
20588
|
+
handlePreviewMouseMove,
|
|
20589
|
+
handlePreviewMouseUp,
|
|
20590
|
+
handlePreviewTouchMove,
|
|
20591
|
+
handlePreviewTouchEnd
|
|
20592
|
+
]);
|
|
20593
|
+
useEffect(() => {
|
|
20594
|
+
if (!showPreviewLightbox) return;
|
|
20595
|
+
const handleEsc = (e) => {
|
|
20596
|
+
if (e.key === "Escape") {
|
|
20597
|
+
setShowPreviewLightbox(false);
|
|
20598
|
+
}
|
|
20599
|
+
};
|
|
20600
|
+
document.addEventListener("keydown", handleEsc);
|
|
20601
|
+
return () => document.removeEventListener("keydown", handleEsc);
|
|
20602
|
+
}, [showPreviewLightbox]);
|
|
20220
20603
|
const processSingleCrop = useCallback(
|
|
20221
|
-
async (cropIndex) => {
|
|
20604
|
+
async (cropIndex, overrideImageId = null) => {
|
|
20222
20605
|
const crop = crops[cropIndex];
|
|
20223
20606
|
if (!crop) {
|
|
20224
20607
|
throw new Error(`Crop ${cropIndex} no encontrado`);
|
|
@@ -20257,9 +20640,10 @@ function CropperView({
|
|
|
20257
20640
|
};
|
|
20258
20641
|
const variantWidth = Math.min(crop.width, 5e3);
|
|
20259
20642
|
const variantHeight = Math.min(crop.height, 5e3);
|
|
20260
|
-
const
|
|
20261
|
-
const variantName = `${
|
|
20262
|
-
const
|
|
20643
|
+
const cropLabel = crop.label || "crop";
|
|
20644
|
+
const variantName = `${cropLabel}_${variantWidth}_${variantHeight}`;
|
|
20645
|
+
const imageId = overrideImageId || image.id;
|
|
20646
|
+
const result = await createCropVariant(imageId, cropParams, {
|
|
20263
20647
|
name: variantName,
|
|
20264
20648
|
width: variantWidth,
|
|
20265
20649
|
height: variantHeight,
|
|
@@ -20268,7 +20652,7 @@ function CropperView({
|
|
|
20268
20652
|
});
|
|
20269
20653
|
if (result) {
|
|
20270
20654
|
accessibilityManager?.announceSuccess(`Recorte creado: ${variantName}`);
|
|
20271
|
-
onVariantCreated?.(
|
|
20655
|
+
onVariantCreated?.(imageId, result);
|
|
20272
20656
|
return result;
|
|
20273
20657
|
}
|
|
20274
20658
|
throw new Error("No se pudo crear la variante");
|
|
@@ -20301,9 +20685,25 @@ function CropperView({
|
|
|
20301
20685
|
saveCurrentCropState();
|
|
20302
20686
|
accessibilityManager?.announce("Creando recorte de la imagen");
|
|
20303
20687
|
try {
|
|
20304
|
-
|
|
20688
|
+
let imageId = image.id;
|
|
20689
|
+
let uploadedAsset = null;
|
|
20690
|
+
if (image.file && onUpload) {
|
|
20691
|
+
accessibilityManager?.announce("Subiendo imagen...");
|
|
20692
|
+
const uploadResult = await onUpload(image.file);
|
|
20693
|
+
if (!uploadResult || !uploadResult.id) {
|
|
20694
|
+
throw new Error("No se pudo subir la imagen al servidor");
|
|
20695
|
+
}
|
|
20696
|
+
imageId = uploadResult.id;
|
|
20697
|
+
uploadedAsset = uploadResult;
|
|
20698
|
+
accessibilityManager?.announceSuccess("Imagen subida correctamente");
|
|
20699
|
+
}
|
|
20700
|
+
const result = await processSingleCrop(activeCropIndex, imageId);
|
|
20305
20701
|
if (result) {
|
|
20306
|
-
|
|
20702
|
+
if (uploadedAsset) {
|
|
20703
|
+
onSave({ crops: [result], asset: uploadedAsset });
|
|
20704
|
+
} else {
|
|
20705
|
+
onSave(result);
|
|
20706
|
+
}
|
|
20307
20707
|
}
|
|
20308
20708
|
} catch (error) {
|
|
20309
20709
|
console.warn("Error creating crop variant:", error);
|
|
@@ -20320,7 +20720,10 @@ function CropperView({
|
|
|
20320
20720
|
processSingleCrop,
|
|
20321
20721
|
activeCropIndex,
|
|
20322
20722
|
onSave,
|
|
20323
|
-
onError
|
|
20723
|
+
onError,
|
|
20724
|
+
image.file,
|
|
20725
|
+
image.id,
|
|
20726
|
+
onUpload
|
|
20324
20727
|
]);
|
|
20325
20728
|
const performSaveMultipleCrops = useCallback(
|
|
20326
20729
|
async (cropIndexes) => {
|
|
@@ -20328,34 +20731,55 @@ function CropperView({
|
|
|
20328
20731
|
accessibilityManager?.announce(
|
|
20329
20732
|
`Guardando ${cropIndexes.length} recortes...`
|
|
20330
20733
|
);
|
|
20331
|
-
|
|
20332
|
-
|
|
20333
|
-
|
|
20334
|
-
|
|
20335
|
-
|
|
20336
|
-
|
|
20337
|
-
|
|
20734
|
+
try {
|
|
20735
|
+
let imageId = image.id;
|
|
20736
|
+
let uploadedAsset = null;
|
|
20737
|
+
if (image.file && onUpload) {
|
|
20738
|
+
accessibilityManager?.announce("Subiendo imagen...");
|
|
20739
|
+
const uploadResult = await onUpload(image.file);
|
|
20740
|
+
if (!uploadResult || !uploadResult.id) {
|
|
20741
|
+
throw new Error("No se pudo subir la imagen al servidor");
|
|
20338
20742
|
}
|
|
20339
|
-
|
|
20340
|
-
|
|
20341
|
-
|
|
20342
|
-
error: error.message
|
|
20343
|
-
});
|
|
20743
|
+
imageId = uploadResult.id;
|
|
20744
|
+
uploadedAsset = uploadResult;
|
|
20745
|
+
accessibilityManager?.announceSuccess("Imagen subida correctamente");
|
|
20344
20746
|
}
|
|
20345
|
-
|
|
20346
|
-
|
|
20347
|
-
|
|
20348
|
-
|
|
20349
|
-
|
|
20350
|
-
|
|
20351
|
-
|
|
20747
|
+
const results = [];
|
|
20748
|
+
const errors = [];
|
|
20749
|
+
for (const index of cropIndexes) {
|
|
20750
|
+
try {
|
|
20751
|
+
const result = await processSingleCrop(index, imageId);
|
|
20752
|
+
if (result) {
|
|
20753
|
+
results.push(result);
|
|
20754
|
+
}
|
|
20755
|
+
} catch (error) {
|
|
20756
|
+
errors.push({
|
|
20757
|
+
crop: crops[index]?.label || `Crop ${index}`,
|
|
20758
|
+
error: error.message
|
|
20759
|
+
});
|
|
20760
|
+
}
|
|
20352
20761
|
}
|
|
20353
|
-
|
|
20354
|
-
|
|
20355
|
-
|
|
20762
|
+
if (results.length > 0) {
|
|
20763
|
+
accessibilityManager?.announceSuccess(
|
|
20764
|
+
`${results.length} recorte(s) guardado(s) correctamente`
|
|
20765
|
+
);
|
|
20766
|
+
if (uploadedAsset) {
|
|
20767
|
+
onSave({ crops: results, asset: uploadedAsset });
|
|
20768
|
+
} else {
|
|
20769
|
+
onSave(results);
|
|
20770
|
+
}
|
|
20771
|
+
}
|
|
20772
|
+
if (errors.length > 0) {
|
|
20773
|
+
const errorMsg = `Errores al guardar algunos recortes:
|
|
20356
20774
|
${errors.map((e) => `- ${e.crop}: ${e.error}`).join("\n")}`;
|
|
20775
|
+
accessibilityManager?.announceError(errorMsg);
|
|
20776
|
+
alert(errorMsg);
|
|
20777
|
+
}
|
|
20778
|
+
} catch (error) {
|
|
20779
|
+
const errorMsg = error.message || "Error al procesar los recortes";
|
|
20357
20780
|
accessibilityManager?.announceError(errorMsg);
|
|
20358
20781
|
alert(errorMsg);
|
|
20782
|
+
onError?.(error);
|
|
20359
20783
|
}
|
|
20360
20784
|
},
|
|
20361
20785
|
[
|
|
@@ -20363,7 +20787,11 @@ ${errors.map((e) => `- ${e.crop}: ${e.error}`).join("\n")}`;
|
|
|
20363
20787
|
processSingleCrop,
|
|
20364
20788
|
crops,
|
|
20365
20789
|
accessibilityManager,
|
|
20366
|
-
onSave
|
|
20790
|
+
onSave,
|
|
20791
|
+
image.file,
|
|
20792
|
+
image.id,
|
|
20793
|
+
onUpload,
|
|
20794
|
+
onError
|
|
20367
20795
|
]
|
|
20368
20796
|
);
|
|
20369
20797
|
const saveCrop = useCallback(async () => {
|
|
@@ -20440,8 +20868,8 @@ ${errors.map((e) => `- ${e.crop}: ${e.error}`).join("\n")}`;
|
|
|
20440
20868
|
`image/${globalThis.downloadFormat || image.mime_type.split("/")[1] || "webp"}`,
|
|
20441
20869
|
0.9
|
|
20442
20870
|
);
|
|
20443
|
-
const cropName = crop.label.replace(/\.[^/.]+$/, "").replace(/\s+/g, "-").trim();
|
|
20444
|
-
const filename = `${
|
|
20871
|
+
const cropName = (crop.label || "crop").replace(/\.[^/.]+$/, "").replace(/\s+/g, "-").trim();
|
|
20872
|
+
const filename = `${cropName}_${crop.width}_${crop.height}`;
|
|
20445
20873
|
await downloadImage(downloadUrl, filename, {
|
|
20446
20874
|
accessibilityManager,
|
|
20447
20875
|
onSuccess: (finalFilename) => {
|
|
@@ -20564,6 +20992,27 @@ ${errors.map((e) => `- ${e.crop}: ${e.error}`).join("\n")}`;
|
|
|
20564
20992
|
}
|
|
20565
20993
|
await performDownload();
|
|
20566
20994
|
}, [validateCropNames, crops, performDownload, activeCropIndex]);
|
|
20995
|
+
const handleKeepOriginal = useCallback(async () => {
|
|
20996
|
+
if (!image.file || !onUpload) {
|
|
20997
|
+
onCancel();
|
|
20998
|
+
return;
|
|
20999
|
+
}
|
|
21000
|
+
try {
|
|
21001
|
+
accessibilityManager?.announce("Subiendo imagen original...");
|
|
21002
|
+
const uploadResult = await onUpload(image.file);
|
|
21003
|
+
if (!uploadResult || !uploadResult.id) {
|
|
21004
|
+
throw new Error("No se pudo subir la imagen al servidor");
|
|
21005
|
+
}
|
|
21006
|
+
accessibilityManager?.announceSuccess("Imagen guardada correctamente");
|
|
21007
|
+
onSave({ asset: uploadResult, crops: [] });
|
|
21008
|
+
} catch (error) {
|
|
21009
|
+
console.error("Error guardando imagen original:", error);
|
|
21010
|
+
const errorMsg = error.message || "No se pudo guardar la imagen. Inténtalo de nuevo.";
|
|
21011
|
+
accessibilityManager?.announceError(errorMsg);
|
|
21012
|
+
alert(errorMsg);
|
|
21013
|
+
onError?.(error);
|
|
21014
|
+
}
|
|
21015
|
+
}, [image.file, onUpload, onCancel, onSave, onError, accessibilityManager]);
|
|
20567
21016
|
useEffect(() => {
|
|
20568
21017
|
setShowPreview(false);
|
|
20569
21018
|
setPreviewUrl(null);
|
|
@@ -20694,7 +21143,7 @@ ${errors.map((e) => `- ${e.crop}: ${e.error}`).join("\n")}`;
|
|
|
20694
21143
|
/* @__PURE__ */ jsx("div", { className: "", children: editableFilename + "." + (globalThis.downloadFormat || image.mime_type.split("/")[1] || "webp") })
|
|
20695
21144
|
] })
|
|
20696
21145
|
] }),
|
|
20697
|
-
/* @__PURE__ */ jsxs("div", { className: "flex flex-row self-end gap-2 w-full max-w-fit sm:w-auto", children: [
|
|
21146
|
+
/* @__PURE__ */ jsxs("div", { className: "flex flex-row self-end gap-2 w-full max-w-fit sm:w-auto flex-wrap", children: [
|
|
20698
21147
|
/* @__PURE__ */ jsxs(
|
|
20699
21148
|
"button",
|
|
20700
21149
|
{
|
|
@@ -20868,37 +21317,6 @@ ${errors.map((e) => `- ${e.crop}: ${e.error}`).join("\n")}`;
|
|
|
20868
21317
|
] }),
|
|
20869
21318
|
/* @__PURE__ */ jsxs("div", { className: "flex flex-col lg:flex-row flex-1 w-full overflow-hidden lg:max-h-130 mx-2 py-1", children: [
|
|
20870
21319
|
/* @__PURE__ */ jsx("div", { className: "relative flex-1 lg:flex-[2] h-full flex-shrink-0 flex-grow-1 order-0 min-h-100 lg:min-h-130", children: /* @__PURE__ */ jsxs("div", { className: "absolute inset-0 bg-white rounded-lg border-2 border-gray-200 overflow-hidden", children: [
|
|
20871
|
-
showPreview && /* @__PURE__ */ jsxs("div", { className: "absolute lg:top-2 sm:top-4 right-2 sm:right-4 bg-white rounded-lg border border-gray-300 p-2 sm:p-3 z-50 shadow-xl w-48 sm:w-64 max-w-[calc(50%-1rem)] sm:max-w-[calc(50%-2rem)] md:min-w-1/4", children: [
|
|
20872
|
-
/* @__PURE__ */ jsxs("div", { className: "flex justify-between items-center mb-2", children: [
|
|
20873
|
-
/* @__PURE__ */ jsx("h3", { className: "text-xs font-semibold text-gray-700", children: "Vista previa" }),
|
|
20874
|
-
/* @__PURE__ */ jsx(
|
|
20875
|
-
"button",
|
|
20876
|
-
{
|
|
20877
|
-
onClick: () => {
|
|
20878
|
-
setShowPreview(false);
|
|
20879
|
-
setPreviewUrl(null);
|
|
20880
|
-
},
|
|
20881
|
-
className: "text-gray-400 hover:text-gray-600 p-1 -m-1",
|
|
20882
|
-
"aria-label": "Cerrar vista previa",
|
|
20883
|
-
children: /* @__PURE__ */ jsx("span", { className: "icon icon-close-small text-xs" })
|
|
20884
|
-
}
|
|
20885
|
-
)
|
|
20886
|
-
] }),
|
|
20887
|
-
/* @__PURE__ */ jsx("div", { className: "bg-gray-50 rounded p-2 flex justify-center items-center min-h-[80px] relative", children: !previewUrl ? /* @__PURE__ */ jsxs("div", { className: "flex flex-col items-center justify-center", children: [
|
|
20888
|
-
/* @__PURE__ */ jsx("div", { className: "animate-spin w-6 h-6 border-2 border-blue-500 border-t-transparent rounded-full mb-2" }),
|
|
20889
|
-
/* @__PURE__ */ jsx("span", { className: "text-xs text-gray-500", children: "Generando..." })
|
|
20890
|
-
] }) : /* @__PURE__ */ jsxs("div", { className: "relative w-full h-full flex justify-center items-center", children: [
|
|
20891
|
-
/* @__PURE__ */ jsx(
|
|
20892
|
-
"img",
|
|
20893
|
-
{
|
|
20894
|
-
src: previewUrl,
|
|
20895
|
-
alt: "Vista previa del recorte",
|
|
20896
|
-
className: "max-w-full max-h-80 object-contain rounded shadow-sm"
|
|
20897
|
-
}
|
|
20898
|
-
),
|
|
20899
|
-
previewLoading && /* @__PURE__ */ jsx("div", { className: "absolute top-1 right-1 bg-blue-500 rounded-full p-1", children: /* @__PURE__ */ jsx("div", { className: "animate-spin w-3 h-3 border border-white border-t-transparent rounded-full" }) })
|
|
20900
|
-
] }) })
|
|
20901
|
-
] }),
|
|
20902
21320
|
/* @__PURE__ */ jsx(
|
|
20903
21321
|
LoadingOverlay,
|
|
20904
21322
|
{
|
|
@@ -21403,7 +21821,7 @@ ${errors.map((e) => `- ${e.crop}: ${e.error}`).join("\n")}`;
|
|
|
21403
21821
|
] })
|
|
21404
21822
|
] }) })
|
|
21405
21823
|
] }),
|
|
21406
|
-
/* @__PURE__ */ jsxs("div", { className: "flex justify-between flex-shrink-0 border-t border-gray-200 bg-white mx-2 mt-1 py-2", children: [
|
|
21824
|
+
/* @__PURE__ */ jsxs("div", { className: "flex justify-between flex-shrink-0 border-t border-gray-200 bg-white mx-2 mt-1 py-2 lg:gap-0", children: [
|
|
21407
21825
|
/* @__PURE__ */ jsxs(
|
|
21408
21826
|
"button",
|
|
21409
21827
|
{
|
|
@@ -21417,32 +21835,46 @@ ${errors.map((e) => `- ${e.crop}: ${e.error}`).join("\n")}`;
|
|
|
21417
21835
|
]
|
|
21418
21836
|
}
|
|
21419
21837
|
),
|
|
21420
|
-
/* @__PURE__ */
|
|
21838
|
+
/* @__PURE__ */ jsx(
|
|
21839
|
+
"button",
|
|
21840
|
+
{
|
|
21841
|
+
onClick: preview,
|
|
21842
|
+
disabled: creatingVariant || !canExport,
|
|
21843
|
+
className: `px-3 cursor-pointer py-1.5 text-sm rounded transition-colors ${showPreview ? "bg-red-600 hover:bg-red-700 text-white" : "bg-blue-600 hover:bg-blue-700 text-white"} disabled:opacity-50 disabled:cursor-not-allowed`,
|
|
21844
|
+
"aria-label": "Vista previa",
|
|
21845
|
+
title: "Mostrar/Ocultar vista previa",
|
|
21846
|
+
children: /* @__PURE__ */ jsxs("div", { className: "flex items-center gap-1", children: [
|
|
21847
|
+
/* @__PURE__ */ jsx("span", { className: "icon icon-search-white icon--sm" }),
|
|
21848
|
+
/* @__PURE__ */ jsx("span", { className: "hidden sm:inline whitespace-nowrap", children: showPreview ? "Cerrar previa" : "Vista previa" })
|
|
21849
|
+
] })
|
|
21850
|
+
}
|
|
21851
|
+
),
|
|
21852
|
+
/* @__PURE__ */ jsxs("div", { className: "flex flex-row gap-1 lg:gap-2 items-center justify-end", children: [
|
|
21421
21853
|
/* @__PURE__ */ jsx(
|
|
21422
21854
|
"button",
|
|
21423
21855
|
{
|
|
21424
|
-
onClick:
|
|
21856
|
+
onClick: handleDownload,
|
|
21425
21857
|
disabled: creatingVariant || !canExport,
|
|
21426
|
-
className:
|
|
21427
|
-
"aria-label": "
|
|
21428
|
-
title: "
|
|
21858
|
+
className: "cursor-pointer px-3 py-1.5 text-sm rounded bg-brand-blue-800 hover:bg-brand-blue-1000 text-white transition-colors disabled:opacity-50 disabled:cursor-not-allowed",
|
|
21859
|
+
"aria-label": "Descargar",
|
|
21860
|
+
title: "Descargar recorte sin guardar",
|
|
21429
21861
|
children: /* @__PURE__ */ jsxs("div", { className: "flex items-center gap-1", children: [
|
|
21430
|
-
/* @__PURE__ */ jsx("span", { className: "icon icon-
|
|
21431
|
-
/* @__PURE__ */ jsx("span", { className: "hidden
|
|
21862
|
+
/* @__PURE__ */ jsx("span", { className: "icon icon-download-white icon--sm" }),
|
|
21863
|
+
/* @__PURE__ */ jsx("span", { className: "hidden md:inline whitespace-nowrap", children: "Descargar" })
|
|
21432
21864
|
] })
|
|
21433
21865
|
}
|
|
21434
21866
|
),
|
|
21435
|
-
/* @__PURE__ */ jsx(
|
|
21867
|
+
image.file && /* @__PURE__ */ jsx(
|
|
21436
21868
|
"button",
|
|
21437
21869
|
{
|
|
21438
|
-
onClick:
|
|
21439
|
-
disabled: creatingVariant ||
|
|
21440
|
-
className:
|
|
21441
|
-
"aria-label": "
|
|
21442
|
-
title: "
|
|
21870
|
+
onClick: handleKeepOriginal,
|
|
21871
|
+
disabled: creatingVariant || uploading,
|
|
21872
|
+
className: `px-3 cursor-pointer py-1.5 text-sm rounded bg-teal-600 hover:bg-teal-700 text-white transition-colors disabled:opacity-50 disabled:cursor-not-allowed`,
|
|
21873
|
+
"aria-label": "Conservar solo imagen original sin crear recortes",
|
|
21874
|
+
title: "Subir y guardar la imagen original sin crear recortes",
|
|
21443
21875
|
children: /* @__PURE__ */ jsxs("div", { className: "flex items-center gap-1", children: [
|
|
21444
|
-
/* @__PURE__ */ jsx("span", { className: "icon icon-
|
|
21445
|
-
/* @__PURE__ */ jsx("span", { className: "hidden
|
|
21876
|
+
/* @__PURE__ */ jsx("span", { className: "icon icon-tick-white icon--sm" }),
|
|
21877
|
+
/* @__PURE__ */ jsx("span", { className: "hidden md:inline whitespace-nowrap", children: "Conservar original" })
|
|
21446
21878
|
] })
|
|
21447
21879
|
}
|
|
21448
21880
|
),
|
|
@@ -21452,7 +21884,6 @@ ${errors.map((e) => `- ${e.crop}: ${e.error}`).join("\n")}`;
|
|
|
21452
21884
|
onClick: saveCrop,
|
|
21453
21885
|
disabled: creatingVariant || !cropData || !effectiveImageInfo || !canExport || !state.isReady,
|
|
21454
21886
|
className: "px-3 cursor-pointer py-1.5 text-sm rounded bg-green-600 hover:bg-green-700 text-white transition-colors disabled:opacity-50 disabled:cursor-not-allowed",
|
|
21455
|
-
"aria-label": "Guardar",
|
|
21456
21887
|
title: "Guardar imagen recortada",
|
|
21457
21888
|
children: /* @__PURE__ */ jsx("div", { className: "flex items-center gap-1", children: creatingVariant ? /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
21458
21889
|
/* @__PURE__ */ jsx("span", { className: "icon icon-save-white icon--sm" }),
|
|
@@ -21774,7 +22205,165 @@ ${errors.map((e) => `- ${e.crop}: ${e.error}`).join("\n")}`;
|
|
|
21774
22205
|
children: "Entendido"
|
|
21775
22206
|
}
|
|
21776
22207
|
) })
|
|
21777
|
-
] }) })
|
|
22208
|
+
] }) }),
|
|
22209
|
+
showPreview && /* @__PURE__ */ jsx(
|
|
22210
|
+
"div",
|
|
22211
|
+
{
|
|
22212
|
+
className: "min-w-[90%] fixed md:absolute inset-0 md:inset-auto bg-black/50 md:bg-transparent flex items-center justify-center md:block z-[9999] md:z-50 pointer-events-none",
|
|
22213
|
+
onClick: (e) => {
|
|
22214
|
+
if (e.target === e.currentTarget && window.innerWidth < 768) {
|
|
22215
|
+
setShowPreview(false);
|
|
22216
|
+
setPreviewUrl(null);
|
|
22217
|
+
}
|
|
22218
|
+
},
|
|
22219
|
+
children: /* @__PURE__ */ jsxs(
|
|
22220
|
+
"div",
|
|
22221
|
+
{
|
|
22222
|
+
className: "pointer-events-auto bg-white rounded-lg shadow-2xl border-2 border-brand-blue-800 overflow-hidden flex flex-col max-w-md w-full mx-4 md:mx-0 max-h-[80vh] md:max-h-[600px]",
|
|
22223
|
+
style: {
|
|
22224
|
+
// En desktop: posicionamiento absoluto con drag y width fijo
|
|
22225
|
+
// En móvil: centrado con flexbox (sin drag)
|
|
22226
|
+
...window.innerWidth >= 768 ? {
|
|
22227
|
+
position: "absolute",
|
|
22228
|
+
left: `${previewPosition.x}px`,
|
|
22229
|
+
top: `${previewPosition.y}px`,
|
|
22230
|
+
width: "420px",
|
|
22231
|
+
cursor: isDragging ? "grabbing" : "default"
|
|
22232
|
+
} : {}
|
|
22233
|
+
},
|
|
22234
|
+
onMouseDown: handlePreviewMouseDown,
|
|
22235
|
+
onTouchStart: handlePreviewTouchStart,
|
|
22236
|
+
children: [
|
|
22237
|
+
/* @__PURE__ */ jsxs(
|
|
22238
|
+
"div",
|
|
22239
|
+
{
|
|
22240
|
+
className: "preview-modal-header px-4 py-3 bg-gradient-to-r from-brand-blue-800 to-brand-blue-1000 text-white flex items-center justify-between cursor-grab active:cursor-grabbing select-none",
|
|
22241
|
+
style: { cursor: isDragging ? "grabbing" : "grab" },
|
|
22242
|
+
children: [
|
|
22243
|
+
/* @__PURE__ */ jsxs("div", { className: "flex items-center gap-2", children: [
|
|
22244
|
+
/* @__PURE__ */ jsx("span", { className: "icon icon-search-white icon--sm" }),
|
|
22245
|
+
/* @__PURE__ */ jsx("h3", { className: "text-sm font-semibold", children: "Vista previa" })
|
|
22246
|
+
] }),
|
|
22247
|
+
/* @__PURE__ */ jsx(
|
|
22248
|
+
"button",
|
|
22249
|
+
{
|
|
22250
|
+
onClick: () => {
|
|
22251
|
+
setShowPreview(false);
|
|
22252
|
+
setPreviewUrl(null);
|
|
22253
|
+
},
|
|
22254
|
+
className: "flex p-1 hover:bg-white/20 rounded transition-colors cursor-pointer aspect-square min-w-fit min-h-fit",
|
|
22255
|
+
"aria-label": "Cerrar vista previa",
|
|
22256
|
+
title: "Cerrar vista previa",
|
|
22257
|
+
children: /* @__PURE__ */ jsx("span", { className: "icon icon-close-small-white icon--sm p-0 m-0 py-auto my-auto mx-auto" })
|
|
22258
|
+
}
|
|
22259
|
+
)
|
|
22260
|
+
]
|
|
22261
|
+
}
|
|
22262
|
+
),
|
|
22263
|
+
/* @__PURE__ */ jsx("div", { className: "flex-1 overflow-auto bg-gray-50 p-4", children: previewLoading ? /* @__PURE__ */ jsx("div", { className: "flex items-center justify-center h-full min-h-[200px]", children: /* @__PURE__ */ jsxs("div", { className: "text-center", children: [
|
|
22264
|
+
/* @__PURE__ */ jsx("div", { className: "animate-spin rounded-full h-12 w-12 border-b-2 border-blue-600 mx-auto mb-3" }),
|
|
22265
|
+
/* @__PURE__ */ jsx("p", { className: "text-sm text-gray-600", children: "Generando vista previa..." })
|
|
22266
|
+
] }) }) : previewUrl ? /* @__PURE__ */ jsxs("div", { className: "flex flex-col items-center gap-3", children: [
|
|
22267
|
+
/* @__PURE__ */ jsxs(
|
|
22268
|
+
"div",
|
|
22269
|
+
{
|
|
22270
|
+
className: "relative w-full bg-white rounded-lg border border-gray-200 overflow-hidden shadow-sm cursor-zoom-in hover:ring-2 hover:ring-brand-blue-800 transition-all",
|
|
22271
|
+
onClick: () => setShowPreviewLightbox(true),
|
|
22272
|
+
role: "button",
|
|
22273
|
+
tabIndex: 0,
|
|
22274
|
+
onKeyDown: (e) => {
|
|
22275
|
+
if (e.key === "Enter" || e.key === " ") {
|
|
22276
|
+
e.preventDefault();
|
|
22277
|
+
setShowPreviewLightbox(true);
|
|
22278
|
+
}
|
|
22279
|
+
},
|
|
22280
|
+
"aria-label": "Click para ver recorte en tamaño completo",
|
|
22281
|
+
title: "Click para ampliar",
|
|
22282
|
+
children: [
|
|
22283
|
+
/* @__PURE__ */ jsx(
|
|
22284
|
+
"img",
|
|
22285
|
+
{
|
|
22286
|
+
src: previewUrl,
|
|
22287
|
+
alt: "Vista previa del recorte",
|
|
22288
|
+
className: "w-full h-auto object-contain max-h-[400px]"
|
|
22289
|
+
}
|
|
22290
|
+
),
|
|
22291
|
+
/* @__PURE__ */ jsxs("div", { className: "absolute top-2 right-2 bg-black/60 text-white px-2 py-1 rounded text-xs flex items-center gap-1", children: [
|
|
22292
|
+
/* @__PURE__ */ jsx("span", { className: "icon icon-search-white icon--xs" }),
|
|
22293
|
+
/* @__PURE__ */ jsx("span", { children: "Ampliar" })
|
|
22294
|
+
] })
|
|
22295
|
+
]
|
|
22296
|
+
}
|
|
22297
|
+
),
|
|
22298
|
+
/* @__PURE__ */ jsxs("div", { className: "text-center text-xs text-gray-500", children: [
|
|
22299
|
+
/* @__PURE__ */ jsx("p", { className: "font-medium text-gray-700 mb-1", children: activeCrop?.label || "Recorte" }),
|
|
22300
|
+
/* @__PURE__ */ jsxs("p", { children: [
|
|
22301
|
+
activeCrop?.width,
|
|
22302
|
+
" × ",
|
|
22303
|
+
activeCrop?.height,
|
|
22304
|
+
" px"
|
|
22305
|
+
] })
|
|
22306
|
+
] })
|
|
22307
|
+
] }) : /* @__PURE__ */ jsx("div", { className: "flex items-center justify-center h-full min-h-[200px]", children: /* @__PURE__ */ jsx("p", { className: "text-sm text-gray-500", children: "No se pudo generar la vista previa" }) }) }),
|
|
22308
|
+
/* @__PURE__ */ jsx("div", { className: "px-4 py-2 bg-gray-100 border-t border-gray-200", children: /* @__PURE__ */ jsxs("p", { className: "text-xs text-gray-600 text-center", children: [
|
|
22309
|
+
/* @__PURE__ */ jsxs("span", { className: "hidden md:inline", children: [
|
|
22310
|
+
"Arrastra desde el título para mover •",
|
|
22311
|
+
" "
|
|
22312
|
+
] }),
|
|
22313
|
+
"Actualización automática al editar"
|
|
22314
|
+
] }) })
|
|
22315
|
+
]
|
|
22316
|
+
}
|
|
22317
|
+
)
|
|
22318
|
+
}
|
|
22319
|
+
),
|
|
22320
|
+
showPreviewLightbox && previewUrl && /* @__PURE__ */ jsxs(
|
|
22321
|
+
"div",
|
|
22322
|
+
{
|
|
22323
|
+
className: "fixed inset-0 bg-black/95 z-[10001] flex items-center justify-center p-4",
|
|
22324
|
+
onClick: () => setShowPreviewLightbox(false),
|
|
22325
|
+
role: "dialog",
|
|
22326
|
+
"aria-modal": "true",
|
|
22327
|
+
"aria-label": "Vista de recorte en tamaño completo",
|
|
22328
|
+
children: [
|
|
22329
|
+
/* @__PURE__ */ jsx(
|
|
22330
|
+
"button",
|
|
22331
|
+
{
|
|
22332
|
+
onClick: () => setShowPreviewLightbox(false),
|
|
22333
|
+
className: "absolute top-4 right-4 p-3 bg-white/10 hover:bg-white/20 rounded-full transition-colors cursor-pointer z-10",
|
|
22334
|
+
"aria-label": "Cerrar vista ampliada",
|
|
22335
|
+
title: "Cerrar (Esc)",
|
|
22336
|
+
children: /* @__PURE__ */ jsx("span", { className: "icon icon-close-small-white icon--lg" })
|
|
22337
|
+
}
|
|
22338
|
+
),
|
|
22339
|
+
/* @__PURE__ */ jsxs("div", { className: "absolute top-4 left-4 bg-black/60 text-white px-4 py-3 rounded-lg text-sm backdrop-blur-sm", children: [
|
|
22340
|
+
/* @__PURE__ */ jsx("p", { className: "font-semibold text-base mb-1", children: activeCrop?.label || "Recorte" }),
|
|
22341
|
+
/* @__PURE__ */ jsxs("p", { className: "text-xs text-gray-300", children: [
|
|
22342
|
+
activeCrop?.width,
|
|
22343
|
+
" × ",
|
|
22344
|
+
activeCrop?.height,
|
|
22345
|
+
" px"
|
|
22346
|
+
] })
|
|
22347
|
+
] }),
|
|
22348
|
+
/* @__PURE__ */ jsx(
|
|
22349
|
+
"div",
|
|
22350
|
+
{
|
|
22351
|
+
className: "relative max-w-[95vw] max-h-[95vh] w-full h-full flex items-center justify-center",
|
|
22352
|
+
onClick: (e) => e.stopPropagation(),
|
|
22353
|
+
children: /* @__PURE__ */ jsx(
|
|
22354
|
+
"img",
|
|
22355
|
+
{
|
|
22356
|
+
src: previewUrl,
|
|
22357
|
+
alt: "Vista completa del recorte",
|
|
22358
|
+
className: "max-w-full max-h-full object-contain rounded-lg shadow-2xl"
|
|
22359
|
+
}
|
|
22360
|
+
)
|
|
22361
|
+
}
|
|
22362
|
+
),
|
|
22363
|
+
/* @__PURE__ */ jsx("div", { className: "absolute bottom-4 left-1/2 -translate-x-1/2 bg-black/60 text-white px-4 py-2 rounded-lg text-xs text-center backdrop-blur-sm", children: "Click fuera de la imagen o presiona ESC para cerrar" })
|
|
22364
|
+
]
|
|
22365
|
+
}
|
|
22366
|
+
)
|
|
21778
22367
|
] });
|
|
21779
22368
|
}
|
|
21780
22369
|
function Pagination({
|
|
@@ -21922,6 +22511,27 @@ function TokenExpiredModal({ isOpen, onClose }) {
|
|
|
21922
22511
|
}
|
|
21923
22512
|
);
|
|
21924
22513
|
}
|
|
22514
|
+
function Breadcrumb({ items = [], currentLabel }) {
|
|
22515
|
+
if (!items.length && !currentLabel) {
|
|
22516
|
+
return null;
|
|
22517
|
+
}
|
|
22518
|
+
return /* @__PURE__ */ jsx("nav", { "aria-label": "Breadcrumb", className: "mb-4", children: /* @__PURE__ */ jsxs("ol", { className: "flex items-center gap-2 text-sm flex-wrap px-4 pt-2 pb-0", children: [
|
|
22519
|
+
items.map((item, index) => /* @__PURE__ */ jsxs("li", { className: "flex items-center gap-2", children: [
|
|
22520
|
+
/* @__PURE__ */ jsx(
|
|
22521
|
+
"button",
|
|
22522
|
+
{
|
|
22523
|
+
type: "button",
|
|
22524
|
+
onClick: item.onClick,
|
|
22525
|
+
className: "text-brand-blue-800 hover:text-brand-blue-1000 hover:underline cursor-pointer transition-colors font-medium",
|
|
22526
|
+
"aria-label": `Ir a ${item.label}`,
|
|
22527
|
+
children: item.label
|
|
22528
|
+
}
|
|
22529
|
+
),
|
|
22530
|
+
/* @__PURE__ */ jsx("span", { className: "text-gray-400", "aria-hidden": "true", children: "/" })
|
|
22531
|
+
] }, index)),
|
|
22532
|
+
currentLabel && /* @__PURE__ */ jsx("li", { className: "text-gray-700 font-medium", "aria-current": "page", children: currentLabel })
|
|
22533
|
+
] }) });
|
|
22534
|
+
}
|
|
21925
22535
|
function useUploadImage() {
|
|
21926
22536
|
const [loading, setLoading] = useState(false);
|
|
21927
22537
|
const [error, setError] = useState(null);
|
|
@@ -22158,6 +22768,29 @@ function App({
|
|
|
22158
22768
|
}
|
|
22159
22769
|
};
|
|
22160
22770
|
const activeFeatures = getFilteredFeatures();
|
|
22771
|
+
const getTabLabel = (tabName) => {
|
|
22772
|
+
const labels = {
|
|
22773
|
+
gallery: "Galería",
|
|
22774
|
+
upload: "Subir",
|
|
22775
|
+
cropper: "Recortar",
|
|
22776
|
+
// Sub-tabs de Upload
|
|
22777
|
+
"upload:upload": "Subir archivo",
|
|
22778
|
+
"upload:ai": "Generar con IA",
|
|
22779
|
+
"upload:stock": "Buscar en Stock",
|
|
22780
|
+
"upload:portals": "Otros portales"
|
|
22781
|
+
};
|
|
22782
|
+
if (tabName && tabName.includes(":") && tabName.split(":").length === 3) {
|
|
22783
|
+
const serviceName = tabName.split(":")[2];
|
|
22784
|
+
return serviceName.charAt(0).toUpperCase() + serviceName.slice(1);
|
|
22785
|
+
}
|
|
22786
|
+
return labels[tabName] || tabName;
|
|
22787
|
+
};
|
|
22788
|
+
const isUploadSubTab = (tabName) => {
|
|
22789
|
+
return tabName && tabName.startsWith("upload:") && tabName.split(":").length === 2;
|
|
22790
|
+
};
|
|
22791
|
+
const isUploadServiceTab = (tabName) => {
|
|
22792
|
+
return tabName && tabName.startsWith("upload:") && tabName.split(":").length === 3;
|
|
22793
|
+
};
|
|
22161
22794
|
const getInitialTab = () => {
|
|
22162
22795
|
if (activeFeatures.includes("gallery")) return "gallery";
|
|
22163
22796
|
if (activeFeatures.includes("upload")) return "upload";
|
|
@@ -22167,6 +22800,79 @@ function App({
|
|
|
22167
22800
|
};
|
|
22168
22801
|
const [activeTab, setActiveTab] = useState(getInitialTab());
|
|
22169
22802
|
const [selectedImage, setSelectedImage] = useState(null);
|
|
22803
|
+
const [navigationHistory, setNavigationHistory] = useState([
|
|
22804
|
+
{ tab: getInitialTab(), label: getTabLabel(getInitialTab()) }
|
|
22805
|
+
]);
|
|
22806
|
+
const addToNavigationHistory = (newTab) => {
|
|
22807
|
+
setNavigationHistory((prev) => {
|
|
22808
|
+
const existingIndex = prev.findIndex((item) => item.tab === newTab);
|
|
22809
|
+
if (existingIndex !== -1) {
|
|
22810
|
+
if (existingIndex === prev.length - 1) {
|
|
22811
|
+
return prev;
|
|
22812
|
+
}
|
|
22813
|
+
return prev.slice(0, existingIndex + 1);
|
|
22814
|
+
}
|
|
22815
|
+
if (isUploadServiceTab(newTab)) {
|
|
22816
|
+
const parts = newTab.split(":");
|
|
22817
|
+
const subTab = `${parts[0]}:${parts[1]}`;
|
|
22818
|
+
const subTabIndex = prev.findIndex((item) => item.tab === subTab);
|
|
22819
|
+
let newHistory2;
|
|
22820
|
+
if (subTabIndex === -1) {
|
|
22821
|
+
const uploadIndex = prev.findIndex((item) => item.tab === "upload");
|
|
22822
|
+
if (uploadIndex === -1) {
|
|
22823
|
+
newHistory2 = [
|
|
22824
|
+
...prev,
|
|
22825
|
+
{ tab: "upload", label: getTabLabel("upload") },
|
|
22826
|
+
{ tab: subTab, label: getTabLabel(subTab) }
|
|
22827
|
+
];
|
|
22828
|
+
} else {
|
|
22829
|
+
newHistory2 = [
|
|
22830
|
+
...prev.slice(0, uploadIndex + 1),
|
|
22831
|
+
{ tab: subTab, label: getTabLabel(subTab) }
|
|
22832
|
+
];
|
|
22833
|
+
}
|
|
22834
|
+
} else {
|
|
22835
|
+
newHistory2 = prev.slice(0, subTabIndex + 1);
|
|
22836
|
+
}
|
|
22837
|
+
newHistory2.push({ tab: newTab, label: getTabLabel(newTab) });
|
|
22838
|
+
const MAX_HISTORY2 = 5;
|
|
22839
|
+
if (newHistory2.length > MAX_HISTORY2) {
|
|
22840
|
+
return newHistory2.slice(newHistory2.length - MAX_HISTORY2);
|
|
22841
|
+
}
|
|
22842
|
+
return newHistory2;
|
|
22843
|
+
}
|
|
22844
|
+
if (isUploadSubTab(newTab)) {
|
|
22845
|
+
const uploadIndex = prev.findIndex((item) => item.tab === "upload");
|
|
22846
|
+
let newHistory2;
|
|
22847
|
+
if (uploadIndex === -1) {
|
|
22848
|
+
newHistory2 = [
|
|
22849
|
+
...prev,
|
|
22850
|
+
{ tab: "upload", label: getTabLabel("upload") }
|
|
22851
|
+
];
|
|
22852
|
+
} else {
|
|
22853
|
+
newHistory2 = prev.slice(0, uploadIndex + 1);
|
|
22854
|
+
}
|
|
22855
|
+
newHistory2.push({ tab: newTab, label: getTabLabel(newTab) });
|
|
22856
|
+
const MAX_HISTORY2 = 5;
|
|
22857
|
+
if (newHistory2.length > MAX_HISTORY2) {
|
|
22858
|
+
return newHistory2.slice(newHistory2.length - MAX_HISTORY2);
|
|
22859
|
+
}
|
|
22860
|
+
return newHistory2;
|
|
22861
|
+
}
|
|
22862
|
+
if (newTab === "upload") {
|
|
22863
|
+
const uploadIndex = prev.findIndex((item) => item.tab === "upload");
|
|
22864
|
+
if (uploadIndex !== -1) {
|
|
22865
|
+
return prev.slice(0, uploadIndex + 1);
|
|
22866
|
+
}
|
|
22867
|
+
}
|
|
22868
|
+
const newHistory = [...prev, { tab: newTab, label: getTabLabel(newTab) }];
|
|
22869
|
+
const MAX_HISTORY = 5;
|
|
22870
|
+
if (newHistory.length > MAX_HISTORY) {
|
|
22871
|
+
return newHistory.slice(newHistory.length - MAX_HISTORY);
|
|
22872
|
+
}
|
|
22873
|
+
return newHistory;
|
|
22874
|
+
});
|
|
22875
|
+
};
|
|
22170
22876
|
useEffect(() => {
|
|
22171
22877
|
if (_externalImage) {
|
|
22172
22878
|
console.log("📸 Imagen externa detectada:", _externalImage);
|
|
@@ -22231,28 +22937,62 @@ function App({
|
|
|
22231
22937
|
const tabs = availableTabs.filter(
|
|
22232
22938
|
(tab) => activeFeatures.includes(tab.feature)
|
|
22233
22939
|
);
|
|
22234
|
-
const
|
|
22235
|
-
const
|
|
22236
|
-
|
|
22237
|
-
|
|
22238
|
-
|
|
22239
|
-
|
|
22240
|
-
|
|
22241
|
-
|
|
22242
|
-
|
|
22243
|
-
|
|
22244
|
-
|
|
22245
|
-
|
|
22246
|
-
|
|
22247
|
-
|
|
22248
|
-
|
|
22249
|
-
|
|
22250
|
-
|
|
22940
|
+
const handleUploadAndCrop = async (file) => {
|
|
22941
|
+
const url2 = URL.createObjectURL(file);
|
|
22942
|
+
const dimensions = await new Promise((resolve) => {
|
|
22943
|
+
const img = new Image();
|
|
22944
|
+
img.onload = () => {
|
|
22945
|
+
resolve({ width: img.naturalWidth, height: img.naturalHeight });
|
|
22946
|
+
URL.revokeObjectURL(url2);
|
|
22947
|
+
};
|
|
22948
|
+
img.onerror = () => {
|
|
22949
|
+
resolve({ width: 1920, height: 1080 });
|
|
22950
|
+
URL.revokeObjectURL(url2);
|
|
22951
|
+
};
|
|
22952
|
+
img.src = url2;
|
|
22953
|
+
});
|
|
22954
|
+
const tempImage = {
|
|
22955
|
+
file,
|
|
22956
|
+
// File object (indica que es imagen nueva sin confirmar)
|
|
22957
|
+
filename: file.name,
|
|
22958
|
+
mime_type: file.type,
|
|
22959
|
+
url: URL.createObjectURL(file),
|
|
22960
|
+
// Nueva URL temporal para preview
|
|
22961
|
+
width: dimensions.width,
|
|
22962
|
+
// Dimensiones reales
|
|
22963
|
+
height: dimensions.height
|
|
22964
|
+
// Dimensiones reales
|
|
22965
|
+
// NO tiene id - el cropper lo generará al subir
|
|
22966
|
+
};
|
|
22967
|
+
setSelectedImage(tempImage);
|
|
22968
|
+
if (activeFeatures.includes("cropper")) {
|
|
22969
|
+
setActiveTab("cropper");
|
|
22970
|
+
addToNavigationHistory("cropper");
|
|
22971
|
+
} else {
|
|
22972
|
+
const result = await upload(file);
|
|
22973
|
+
if (result) {
|
|
22974
|
+
invalidateCache();
|
|
22975
|
+
setCurrentPage(1);
|
|
22976
|
+
setImages((prev) => [result, ...prev]);
|
|
22977
|
+
setActiveTab("gallery");
|
|
22978
|
+
if (callbacks.onUpload) {
|
|
22979
|
+
callbacks.onUpload({
|
|
22980
|
+
assetId: result.id,
|
|
22981
|
+
url: result.url,
|
|
22982
|
+
fileName: result.filename,
|
|
22983
|
+
mime: result.mime_type,
|
|
22984
|
+
width: result.width,
|
|
22985
|
+
height: result.height,
|
|
22986
|
+
instanceId
|
|
22987
|
+
});
|
|
22988
|
+
}
|
|
22251
22989
|
}
|
|
22252
22990
|
}
|
|
22253
22991
|
};
|
|
22254
22992
|
const handleDelete = async (imageId) => {
|
|
22255
|
-
if (!confirm(
|
|
22993
|
+
if (!confirm(
|
|
22994
|
+
"¿Estás seguro de que deseas eliminar esta imagen? Esta acción también eliminará todos sus recortes."
|
|
22995
|
+
)) {
|
|
22256
22996
|
return;
|
|
22257
22997
|
}
|
|
22258
22998
|
const success = await deleteImg(imageId);
|
|
@@ -22283,12 +23023,14 @@ function App({
|
|
|
22283
23023
|
setSelectedImage(img);
|
|
22284
23024
|
if (activeFeatures.includes("cropper")) {
|
|
22285
23025
|
setActiveTab("cropper");
|
|
23026
|
+
addToNavigationHistory("cropper");
|
|
22286
23027
|
}
|
|
22287
23028
|
};
|
|
22288
23029
|
const handleTabChange = (tabId) => {
|
|
22289
23030
|
setActiveTab(tabId);
|
|
22290
23031
|
if (tabId !== "upload") resetUpload();
|
|
22291
23032
|
if (tabId !== "gallery") resetDelete();
|
|
23033
|
+
addToNavigationHistory(tabId);
|
|
22292
23034
|
};
|
|
22293
23035
|
const determineScenario = () => {
|
|
22294
23036
|
if (modeUI === "crop-only" || activeFeatures.length === 1 && activeFeatures[0] === "cropper") {
|
|
@@ -22307,21 +23049,45 @@ function App({
|
|
|
22307
23049
|
const globalConfig2 = window.limboCore?.config?.getGlobal() || {};
|
|
22308
23050
|
const mode = globalConfig2.mode || "embed";
|
|
22309
23051
|
const autoHide = globalConfig2.autoHideOnComplete || false;
|
|
22310
|
-
|
|
23052
|
+
let crops = [];
|
|
23053
|
+
let uploadedAsset = null;
|
|
23054
|
+
if (result.crops && result.asset) {
|
|
23055
|
+
crops = result.crops;
|
|
23056
|
+
uploadedAsset = result.asset;
|
|
23057
|
+
} else if (Array.isArray(result)) {
|
|
23058
|
+
crops = result;
|
|
23059
|
+
} else {
|
|
23060
|
+
crops = [result];
|
|
23061
|
+
}
|
|
23062
|
+
if (uploadedAsset && scenario === "with-gallery") {
|
|
23063
|
+
setImages((prev) => [uploadedAsset, ...prev]);
|
|
23064
|
+
setCurrentPage(1);
|
|
23065
|
+
if (callbacks.onUpload) {
|
|
23066
|
+
callbacks.onUpload({
|
|
23067
|
+
assetId: uploadedAsset.id,
|
|
23068
|
+
url: uploadedAsset.url,
|
|
23069
|
+
fileName: uploadedAsset.filename,
|
|
23070
|
+
mime: uploadedAsset.mime_type,
|
|
23071
|
+
width: uploadedAsset.width,
|
|
23072
|
+
height: uploadedAsset.height,
|
|
23073
|
+
instanceId
|
|
23074
|
+
});
|
|
23075
|
+
}
|
|
23076
|
+
}
|
|
22311
23077
|
if (scenario === "with-gallery") {
|
|
22312
23078
|
invalidateCache();
|
|
22313
23079
|
}
|
|
22314
23080
|
if (callbacks.onCropsSaved) {
|
|
22315
23081
|
callbacks.onCropsSaved({
|
|
22316
23082
|
crops,
|
|
22317
|
-
assetId: selectedImage?.id,
|
|
23083
|
+
assetId: uploadedAsset?.id || selectedImage?.id,
|
|
22318
23084
|
instanceId
|
|
22319
23085
|
});
|
|
22320
23086
|
}
|
|
22321
23087
|
if (window.limboCore?.events) {
|
|
22322
23088
|
window.limboCore.events.emit("cropsSaved", {
|
|
22323
23089
|
crops,
|
|
22324
|
-
assetId: selectedImage?.id,
|
|
23090
|
+
assetId: uploadedAsset?.id || selectedImage?.id,
|
|
22325
23091
|
instanceId
|
|
22326
23092
|
});
|
|
22327
23093
|
}
|
|
@@ -22367,7 +23133,9 @@ function App({
|
|
|
22367
23133
|
});
|
|
22368
23134
|
}
|
|
22369
23135
|
if (autoHide) {
|
|
22370
|
-
const container = document.querySelector(
|
|
23136
|
+
const container = document.querySelector(
|
|
23137
|
+
`#limbo-instance-${instanceId}`
|
|
23138
|
+
);
|
|
22371
23139
|
if (container) container.style.display = "none";
|
|
22372
23140
|
}
|
|
22373
23141
|
}
|
|
@@ -22430,7 +23198,37 @@ function App({
|
|
|
22430
23198
|
}
|
|
22431
23199
|
console.error("Cropper error:", error);
|
|
22432
23200
|
};
|
|
23201
|
+
const navigateToHistoryPoint = (index) => {
|
|
23202
|
+
const historyPoint = navigationHistory[index];
|
|
23203
|
+
if (!historyPoint) return;
|
|
23204
|
+
if (historyPoint.tab !== "cropper") {
|
|
23205
|
+
setSelectedImage(null);
|
|
23206
|
+
}
|
|
23207
|
+
setActiveTab(historyPoint.tab);
|
|
23208
|
+
setNavigationHistory((prev) => prev.slice(0, index + 1));
|
|
23209
|
+
};
|
|
23210
|
+
const getBreadcrumbs = () => {
|
|
23211
|
+
if (navigationHistory.length <= 1) {
|
|
23212
|
+
return null;
|
|
23213
|
+
}
|
|
23214
|
+
const items = navigationHistory.slice(0, -1).map((item, index) => {
|
|
23215
|
+
return {
|
|
23216
|
+
label: item.label,
|
|
23217
|
+
onClick: item.tab !== "subir" || item.tab !== "upload" ? () => navigateToHistoryPoint(index) : void 0
|
|
23218
|
+
};
|
|
23219
|
+
});
|
|
23220
|
+
const current = navigationHistory[navigationHistory.length - 1].label;
|
|
23221
|
+
return { items, current };
|
|
23222
|
+
};
|
|
23223
|
+
const breadcrumbData = getBreadcrumbs();
|
|
22433
23224
|
return /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
23225
|
+
breadcrumbData && /* @__PURE__ */ jsx(
|
|
23226
|
+
Breadcrumb,
|
|
23227
|
+
{
|
|
23228
|
+
items: breadcrumbData.items,
|
|
23229
|
+
currentLabel: breadcrumbData.current
|
|
23230
|
+
}
|
|
23231
|
+
),
|
|
22434
23232
|
ui.showTabs && activeTab != "cropper" && /* @__PURE__ */ jsx(Tabs, { tabs, active: activeTab, onChange: handleTabChange }),
|
|
22435
23233
|
imagesError && /* @__PURE__ */ jsxs("div", { className: "alert alert-danger", children: [
|
|
22436
23234
|
"Error al cargar imágenes: ",
|
|
@@ -22484,14 +23282,20 @@ function App({
|
|
|
22484
23282
|
"Error al subir imagen: ",
|
|
22485
23283
|
uploadError
|
|
22486
23284
|
] }),
|
|
22487
|
-
uploadedImage && /* @__PURE__ */ jsxs("div", { className: "alert alert-success", children: [
|
|
23285
|
+
uploadedImage && /* @__PURE__ */ jsxs("div", { className: "alert alert-success absolute top-0 right-0 m-4", children: [
|
|
22488
23286
|
"✅ Imagen subida correctamente: ",
|
|
22489
23287
|
uploadedImage.filename
|
|
22490
23288
|
] }),
|
|
22491
23289
|
/* @__PURE__ */ jsx(
|
|
22492
23290
|
UploadForm,
|
|
22493
23291
|
{
|
|
22494
|
-
|
|
23292
|
+
onSelect: handleUploadAndCrop,
|
|
23293
|
+
onSubTabChange: (subTabId) => {
|
|
23294
|
+
addToNavigationHistory(`upload:${subTabId}`);
|
|
23295
|
+
},
|
|
23296
|
+
onServiceChange: (subTabId, serviceSlug) => {
|
|
23297
|
+
addToNavigationHistory(`upload:${subTabId}:${serviceSlug}`);
|
|
23298
|
+
},
|
|
22495
23299
|
disabled: uploading,
|
|
22496
23300
|
apiKey,
|
|
22497
23301
|
prod
|
|
@@ -22504,10 +23308,12 @@ function App({
|
|
|
22504
23308
|
image: selectedImage,
|
|
22505
23309
|
onSave: handleCropSave,
|
|
22506
23310
|
onCancel: handleCropCancel,
|
|
22507
|
-
onDelete: () => handleDelete(selectedImage
|
|
23311
|
+
onDelete: selectedImage?.id ? () => handleDelete(selectedImage.id) : null,
|
|
22508
23312
|
onError: handleCropError,
|
|
22509
23313
|
deleting,
|
|
22510
|
-
onVariantCreated: handleVariantCreated
|
|
23314
|
+
onVariantCreated: handleVariantCreated,
|
|
23315
|
+
onUpload: upload,
|
|
23316
|
+
uploading
|
|
22511
23317
|
}
|
|
22512
23318
|
),
|
|
22513
23319
|
activeTab === "cropper" && !selectedImage && /* @__PURE__ */ jsx("div", { className: "limbo-empty-state", children: /* @__PURE__ */ jsx("p", { children: "Selecciona una imagen de la galería para comenzar a recortar." }) }),
|