@sparkstudio/storage-ui 1.0.28 → 1.0.29
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/index.cjs +990 -275
- package/dist/index.d.cts +84 -18
- package/dist/index.d.ts +84 -18
- package/dist/index.js +981 -264
- package/package.json +1 -1
package/dist/index.cjs
CHANGED
|
@@ -23,9 +23,13 @@ __export(index_exports, {
|
|
|
23
23
|
AWSPresignedUrlDTO: () => AWSPresignedUrlDTO,
|
|
24
24
|
Container: () => Container,
|
|
25
25
|
ContainerDTO: () => ContainerDTO,
|
|
26
|
+
ContainerIdGridPanel: () => ContainerIdGridPanel,
|
|
26
27
|
ContainerType: () => ContainerType,
|
|
27
28
|
ContainerUploadPanel: () => ContainerUploadPanel,
|
|
28
29
|
DesktopFileIcon: () => DesktopFileIcon,
|
|
30
|
+
FileGridUploadPanel: () => FileGridUploadPanel,
|
|
31
|
+
FileIconCard: () => FileIconCard,
|
|
32
|
+
FileIconGrid: () => FileIconGrid,
|
|
29
33
|
Home: () => Home,
|
|
30
34
|
HomeView: () => HomeView,
|
|
31
35
|
S3: () => S3,
|
|
@@ -276,54 +280,11 @@ var ContainerType = /* @__PURE__ */ ((ContainerType2) => {
|
|
|
276
280
|
return ContainerType2;
|
|
277
281
|
})(ContainerType || {});
|
|
278
282
|
|
|
279
|
-
// src/components/
|
|
280
|
-
var import_react7 = require("react");
|
|
281
|
-
|
|
282
|
-
// src/components/UploadContainer.tsx
|
|
283
|
+
// src/components/ContainerIdGridPanel.tsx
|
|
283
284
|
var import_react5 = require("react");
|
|
284
285
|
|
|
285
|
-
// src/components/UploadDropzone.tsx
|
|
286
|
-
var import_react = require("react");
|
|
287
|
-
var import_jsx_runtime = require("react/jsx-runtime");
|
|
288
|
-
var UploadDropzone = ({
|
|
289
|
-
isDragging,
|
|
290
|
-
onDragOver,
|
|
291
|
-
onDragLeave,
|
|
292
|
-
onDrop,
|
|
293
|
-
className = "",
|
|
294
|
-
style,
|
|
295
|
-
children
|
|
296
|
-
}) => {
|
|
297
|
-
const baseClass = "rounded-3 d-flex flex-column align-items-center justify-content-center";
|
|
298
|
-
const stateClass = isDragging ? "bg-body-secondary border-dashed border-2 border-secondary" : "bg-body-trasparent border-solid border-transparent border-2";
|
|
299
|
-
const combinedClassName = `${baseClass} ${stateClass} ${className}`.trim();
|
|
300
|
-
const handleDragOver = (e) => {
|
|
301
|
-
e.preventDefault();
|
|
302
|
-
onDragOver?.(e);
|
|
303
|
-
};
|
|
304
|
-
const handleDragLeave = (e) => {
|
|
305
|
-
e.preventDefault();
|
|
306
|
-
onDragLeave?.(e);
|
|
307
|
-
};
|
|
308
|
-
const handleDrop = (e) => {
|
|
309
|
-
e.preventDefault();
|
|
310
|
-
onDrop?.(e);
|
|
311
|
-
};
|
|
312
|
-
return /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
|
|
313
|
-
"div",
|
|
314
|
-
{
|
|
315
|
-
className: combinedClassName,
|
|
316
|
-
style: { minHeight: "140px", ...style },
|
|
317
|
-
onDragOver: handleDragOver,
|
|
318
|
-
onDragLeave: handleDragLeave,
|
|
319
|
-
onDrop: handleDrop,
|
|
320
|
-
children
|
|
321
|
-
}
|
|
322
|
-
);
|
|
323
|
-
};
|
|
324
|
-
|
|
325
286
|
// src/hooks/UseUploadManager.ts
|
|
326
|
-
var
|
|
287
|
+
var import_react = require("react");
|
|
327
288
|
|
|
328
289
|
// src/engines/UploadFileToS3.ts
|
|
329
290
|
async function UploadFileToS3(file, presignedUrl, onProgress, maxRetries = 3) {
|
|
@@ -380,14 +341,14 @@ function UseUploadManager({
|
|
|
380
341
|
onUploadComplete,
|
|
381
342
|
onUploadError
|
|
382
343
|
}) {
|
|
383
|
-
const [uploads, setUploads] = (0,
|
|
344
|
+
const [uploads, setUploads] = (0, import_react.useState)([]);
|
|
384
345
|
const createUploadStates = (files) => Array.from(files).map((file) => ({
|
|
385
346
|
id: `${file.name}-${file.size}-${file.lastModified}-${Math.random()}`,
|
|
386
347
|
file,
|
|
387
348
|
progress: 0,
|
|
388
349
|
status: "pending"
|
|
389
350
|
}));
|
|
390
|
-
const uploadFile = (0,
|
|
351
|
+
const uploadFile = (0, import_react.useCallback)(
|
|
391
352
|
async (upload) => {
|
|
392
353
|
setUploads(
|
|
393
354
|
(prev) => prev.map(
|
|
@@ -453,7 +414,7 @@ function UseUploadManager({
|
|
|
453
414
|
}
|
|
454
415
|
throw lastError instanceof Error ? lastError : new Error("Failed to fetch presigned URL");
|
|
455
416
|
}
|
|
456
|
-
const startUploadsIfNeeded = (0,
|
|
417
|
+
const startUploadsIfNeeded = (0, import_react.useCallback)(
|
|
457
418
|
(files) => {
|
|
458
419
|
if (!autoUpload || !getPresignedUrl) return;
|
|
459
420
|
const newUploads = createUploadStates(files);
|
|
@@ -464,7 +425,7 @@ function UseUploadManager({
|
|
|
464
425
|
},
|
|
465
426
|
[autoUpload, getPresignedUrl, uploadFile]
|
|
466
427
|
);
|
|
467
|
-
const resetUploads = (0,
|
|
428
|
+
const resetUploads = (0, import_react.useCallback)(() => setUploads([]), []);
|
|
468
429
|
return {
|
|
469
430
|
uploads,
|
|
470
431
|
startUploadsIfNeeded,
|
|
@@ -473,19 +434,19 @@ function UseUploadManager({
|
|
|
473
434
|
}
|
|
474
435
|
|
|
475
436
|
// src/components/UploadProgressList.tsx
|
|
476
|
-
var
|
|
477
|
-
var
|
|
437
|
+
var import_react2 = require("react");
|
|
438
|
+
var import_jsx_runtime = require("react/jsx-runtime");
|
|
478
439
|
var UploadProgressList = ({
|
|
479
440
|
uploads
|
|
480
441
|
}) => {
|
|
481
442
|
const visibleUploads = uploads.filter((u) => u.status !== "success");
|
|
482
443
|
if (visibleUploads.length === 0) return null;
|
|
483
|
-
return /* @__PURE__ */ (0,
|
|
444
|
+
return /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
|
|
484
445
|
"div",
|
|
485
446
|
{
|
|
486
447
|
className: "w-100 d-flex flex-wrap gap-4 align-content-start mt-3",
|
|
487
448
|
style: { minHeight: "80px" },
|
|
488
|
-
children: visibleUploads.map((u) => /* @__PURE__ */ (0,
|
|
449
|
+
children: visibleUploads.map((u) => /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(
|
|
489
450
|
"div",
|
|
490
451
|
{
|
|
491
452
|
className: "d-flex flex-column align-items-center",
|
|
@@ -495,7 +456,7 @@ var UploadProgressList = ({
|
|
|
495
456
|
},
|
|
496
457
|
title: u.file.name,
|
|
497
458
|
children: [
|
|
498
|
-
/* @__PURE__ */ (0,
|
|
459
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsxs)(
|
|
499
460
|
"div",
|
|
500
461
|
{
|
|
501
462
|
className: "border rounded-3 d-flex align-items-center justify-content-center mb-1 shadow-sm position-relative",
|
|
@@ -504,20 +465,20 @@ var UploadProgressList = ({
|
|
|
504
465
|
height: 64
|
|
505
466
|
},
|
|
506
467
|
children: [
|
|
507
|
-
/* @__PURE__ */ (0,
|
|
508
|
-
u.status === "uploading" && /* @__PURE__ */ (0,
|
|
468
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("i", { className: "bi bi-file-earmark fs-2" }),
|
|
469
|
+
u.status === "uploading" && /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
|
|
509
470
|
"div",
|
|
510
471
|
{
|
|
511
472
|
className: "position-absolute top-50 start-50 translate-middle",
|
|
512
473
|
style: { pointerEvents: "none" },
|
|
513
|
-
children: /* @__PURE__ */ (0,
|
|
474
|
+
children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { className: "spinner-border spinner-border-sm text-primary" })
|
|
514
475
|
}
|
|
515
476
|
),
|
|
516
|
-
u.status === "error" && /* @__PURE__ */ (0,
|
|
477
|
+
u.status === "error" && /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { className: "position-absolute top-0 start-100 translate-middle badge rounded-pill bg-danger", children: "!" })
|
|
517
478
|
]
|
|
518
479
|
}
|
|
519
480
|
),
|
|
520
|
-
/* @__PURE__ */ (0,
|
|
481
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)(
|
|
521
482
|
"div",
|
|
522
483
|
{
|
|
523
484
|
className: "small text-center text-truncate",
|
|
@@ -525,7 +486,7 @@ var UploadProgressList = ({
|
|
|
525
486
|
children: u.file.name
|
|
526
487
|
}
|
|
527
488
|
),
|
|
528
|
-
/* @__PURE__ */ (0,
|
|
489
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { className: "w-100 mt-1", children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { className: "progress", style: { height: "4px" }, children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
|
|
529
490
|
"div",
|
|
530
491
|
{
|
|
531
492
|
className: "progress-bar " + (u.status === "error" ? "bg-danger" : ""),
|
|
@@ -536,7 +497,7 @@ var UploadProgressList = ({
|
|
|
536
497
|
"aria-valuemax": 100
|
|
537
498
|
}
|
|
538
499
|
) }) }),
|
|
539
|
-
u.status === "error" && /* @__PURE__ */ (0,
|
|
500
|
+
u.status === "error" && /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
|
|
540
501
|
"div",
|
|
541
502
|
{
|
|
542
503
|
className: "text-danger small mt-1 text-center",
|
|
@@ -552,11 +513,556 @@ var UploadProgressList = ({
|
|
|
552
513
|
);
|
|
553
514
|
};
|
|
554
515
|
|
|
516
|
+
// src/components/FileIconGrid.tsx
|
|
517
|
+
var import_react4 = require("react");
|
|
518
|
+
|
|
519
|
+
// src/components/FileIconCard.tsx
|
|
520
|
+
var import_react3 = require("react");
|
|
521
|
+
var import_jsx_runtime2 = require("react/jsx-runtime");
|
|
522
|
+
function sanitizeIconHtml(input) {
|
|
523
|
+
return input.replace(/<script[\s\S]*?>[\s\S]*?<\/script>/gi, "").replace(/\son\w+="[^"]*"/gi, "").replace(/\son\w+='[^']*'/gi, "").replace(/javascript:/gi, "");
|
|
524
|
+
}
|
|
525
|
+
var FileIconCard = (props) => {
|
|
526
|
+
const {
|
|
527
|
+
file,
|
|
528
|
+
deleteDisabled,
|
|
529
|
+
selected,
|
|
530
|
+
onSelect,
|
|
531
|
+
onDeleteFile,
|
|
532
|
+
onClickFile,
|
|
533
|
+
icon,
|
|
534
|
+
iconHtml,
|
|
535
|
+
title,
|
|
536
|
+
className
|
|
537
|
+
} = props;
|
|
538
|
+
const [deleting, setDeleting] = (0, import_react3.useState)(false);
|
|
539
|
+
const [error, setError] = (0, import_react3.useState)(null);
|
|
540
|
+
const safeIconHtml = (0, import_react3.useMemo)(() => {
|
|
541
|
+
if (!iconHtml) return null;
|
|
542
|
+
return sanitizeIconHtml(iconHtml);
|
|
543
|
+
}, [iconHtml]);
|
|
544
|
+
const canDelete = !!onDeleteFile && !deleteDisabled && !deleting;
|
|
545
|
+
const handleClick = () => {
|
|
546
|
+
onSelect?.(file);
|
|
547
|
+
onClickFile?.(file);
|
|
548
|
+
};
|
|
549
|
+
const handleDelete = async (ev) => {
|
|
550
|
+
ev.stopPropagation();
|
|
551
|
+
if (!canDelete) return;
|
|
552
|
+
setError(null);
|
|
553
|
+
setDeleting(true);
|
|
554
|
+
try {
|
|
555
|
+
await onDeleteFile?.(file);
|
|
556
|
+
} catch (e) {
|
|
557
|
+
setError(e instanceof Error ? e.message : "Delete failed");
|
|
558
|
+
} finally {
|
|
559
|
+
setDeleting(false);
|
|
560
|
+
}
|
|
561
|
+
};
|
|
562
|
+
const borderColor = selected ? "rgba(13,110,253,0.8)" : "rgba(0,0,0,0.12)";
|
|
563
|
+
const boxShadow = selected ? "0 0 0 3px rgba(13,110,253,0.2)" : "none";
|
|
564
|
+
return /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)(
|
|
565
|
+
"div",
|
|
566
|
+
{
|
|
567
|
+
className: [
|
|
568
|
+
"file-icon-card",
|
|
569
|
+
deleteDisabled ? "is-delete-disabled" : "",
|
|
570
|
+
deleting ? "is-deleting" : "",
|
|
571
|
+
className ?? ""
|
|
572
|
+
].join(" "),
|
|
573
|
+
role: "button",
|
|
574
|
+
tabIndex: 0,
|
|
575
|
+
onClick: handleClick,
|
|
576
|
+
onKeyDown: (e) => {
|
|
577
|
+
if (e.key === "Enter" || e.key === " ") {
|
|
578
|
+
e.preventDefault();
|
|
579
|
+
handleClick();
|
|
580
|
+
}
|
|
581
|
+
},
|
|
582
|
+
title: title ?? file.Name ?? "File",
|
|
583
|
+
style: {
|
|
584
|
+
display: "flex",
|
|
585
|
+
alignItems: "center",
|
|
586
|
+
gap: 12,
|
|
587
|
+
padding: 12,
|
|
588
|
+
border: `1px solid ${borderColor}`,
|
|
589
|
+
boxShadow,
|
|
590
|
+
borderRadius: 12,
|
|
591
|
+
userSelect: "none",
|
|
592
|
+
cursor: "pointer",
|
|
593
|
+
background: "white"
|
|
594
|
+
},
|
|
595
|
+
children: [
|
|
596
|
+
/* @__PURE__ */ (0, import_jsx_runtime2.jsx)(
|
|
597
|
+
"div",
|
|
598
|
+
{
|
|
599
|
+
style: {
|
|
600
|
+
width: 44,
|
|
601
|
+
height: 44,
|
|
602
|
+
borderRadius: 10,
|
|
603
|
+
display: "grid",
|
|
604
|
+
placeItems: "center",
|
|
605
|
+
background: "rgba(0,0,0,0.04)",
|
|
606
|
+
flex: "0 0 auto"
|
|
607
|
+
},
|
|
608
|
+
children: icon ? icon : safeIconHtml ? /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("span", { "aria-hidden": true, dangerouslySetInnerHTML: { __html: safeIconHtml } }) : /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(DefaultFileIcon, {})
|
|
609
|
+
}
|
|
610
|
+
),
|
|
611
|
+
/* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("div", { style: { minWidth: 0, flex: "1 1 auto" }, children: [
|
|
612
|
+
/* @__PURE__ */ (0, import_jsx_runtime2.jsx)(
|
|
613
|
+
"div",
|
|
614
|
+
{
|
|
615
|
+
style: {
|
|
616
|
+
fontWeight: 600,
|
|
617
|
+
overflow: "hidden",
|
|
618
|
+
textOverflow: "ellipsis",
|
|
619
|
+
whiteSpace: "nowrap"
|
|
620
|
+
},
|
|
621
|
+
children: title ?? file.Name ?? "Untitled"
|
|
622
|
+
}
|
|
623
|
+
),
|
|
624
|
+
/* @__PURE__ */ (0, import_jsx_runtime2.jsx)("div", { style: { fontSize: 12, opacity: 0.7 }, children: typeof file.FileSize === "number" ? formatBytes(file.FileSize) : "" }),
|
|
625
|
+
error ? /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("div", { style: { fontSize: 12, color: "crimson", marginTop: 4 }, children: error }) : null
|
|
626
|
+
] }),
|
|
627
|
+
!deleteDisabled ? /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)(
|
|
628
|
+
"button",
|
|
629
|
+
{
|
|
630
|
+
type: "button",
|
|
631
|
+
onClick: handleDelete,
|
|
632
|
+
disabled: !canDelete,
|
|
633
|
+
"aria-label": "Delete file",
|
|
634
|
+
style: {
|
|
635
|
+
border: "1px solid rgba(0,0,0,0.18)",
|
|
636
|
+
background: canDelete ? "white" : "rgba(0,0,0,0.04)",
|
|
637
|
+
borderRadius: 10,
|
|
638
|
+
padding: "8px 10px",
|
|
639
|
+
cursor: canDelete ? "pointer" : "not-allowed",
|
|
640
|
+
display: "flex",
|
|
641
|
+
alignItems: "center",
|
|
642
|
+
gap: 8
|
|
643
|
+
},
|
|
644
|
+
children: [
|
|
645
|
+
deleting ? /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(SmallSpinner, {}) : /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(TrashIcon, {}),
|
|
646
|
+
/* @__PURE__ */ (0, import_jsx_runtime2.jsx)("span", { style: { fontSize: 13 }, children: deleting ? "Deleting\u2026" : "Delete" })
|
|
647
|
+
]
|
|
648
|
+
}
|
|
649
|
+
) : null
|
|
650
|
+
]
|
|
651
|
+
}
|
|
652
|
+
);
|
|
653
|
+
};
|
|
654
|
+
function formatBytes(bytes) {
|
|
655
|
+
if (!Number.isFinite(bytes) || bytes < 0) return "";
|
|
656
|
+
const units = ["B", "KB", "MB", "GB", "TB"];
|
|
657
|
+
let v = bytes;
|
|
658
|
+
let i = 0;
|
|
659
|
+
while (v >= 1024 && i < units.length - 1) {
|
|
660
|
+
v /= 1024;
|
|
661
|
+
i++;
|
|
662
|
+
}
|
|
663
|
+
return `${v.toFixed(i === 0 ? 0 : 1)} ${units[i]}`;
|
|
664
|
+
}
|
|
665
|
+
function SmallSpinner() {
|
|
666
|
+
return /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(
|
|
667
|
+
"span",
|
|
668
|
+
{
|
|
669
|
+
"aria-hidden": true,
|
|
670
|
+
style: {
|
|
671
|
+
width: 14,
|
|
672
|
+
height: 14,
|
|
673
|
+
borderRadius: "50%",
|
|
674
|
+
border: "2px solid rgba(0,0,0,0.2)",
|
|
675
|
+
borderTopColor: "rgba(0,0,0,0.7)",
|
|
676
|
+
display: "inline-block",
|
|
677
|
+
animation: "fileIconSpin 0.8s linear infinite"
|
|
678
|
+
}
|
|
679
|
+
}
|
|
680
|
+
);
|
|
681
|
+
}
|
|
682
|
+
function DefaultFileIcon() {
|
|
683
|
+
return /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("svg", { width: "22", height: "22", viewBox: "0 0 24 24", fill: "none", "aria-hidden": true, children: [
|
|
684
|
+
/* @__PURE__ */ (0, import_jsx_runtime2.jsx)(
|
|
685
|
+
"path",
|
|
686
|
+
{
|
|
687
|
+
d: "M7 3h7l3 3v15a2 2 0 0 1-2 2H7a2 2 0 0 1-2-2V5a2 2 0 0 1 2-2Z",
|
|
688
|
+
stroke: "currentColor",
|
|
689
|
+
strokeWidth: "2"
|
|
690
|
+
}
|
|
691
|
+
),
|
|
692
|
+
/* @__PURE__ */ (0, import_jsx_runtime2.jsx)("path", { d: "M14 3v4a1 1 0 0 0 1 1h4", stroke: "currentColor", strokeWidth: "2" })
|
|
693
|
+
] });
|
|
694
|
+
}
|
|
695
|
+
function TrashIcon() {
|
|
696
|
+
return /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("svg", { width: "16", height: "16", viewBox: "0 0 24 24", fill: "none", "aria-hidden": true, children: [
|
|
697
|
+
/* @__PURE__ */ (0, import_jsx_runtime2.jsx)("path", { d: "M3 6h18", stroke: "currentColor", strokeWidth: "2", strokeLinecap: "round" }),
|
|
698
|
+
/* @__PURE__ */ (0, import_jsx_runtime2.jsx)(
|
|
699
|
+
"path",
|
|
700
|
+
{
|
|
701
|
+
d: "M8 6V4a1 1 0 0 1 1-1h6a1 1 0 0 1 1 1v2",
|
|
702
|
+
stroke: "currentColor",
|
|
703
|
+
strokeWidth: "2"
|
|
704
|
+
}
|
|
705
|
+
),
|
|
706
|
+
/* @__PURE__ */ (0, import_jsx_runtime2.jsx)(
|
|
707
|
+
"path",
|
|
708
|
+
{
|
|
709
|
+
d: "M6 6l1 16a2 2 0 0 0 2 2h6a2 2 0 0 0 2-2l1-16",
|
|
710
|
+
stroke: "currentColor",
|
|
711
|
+
strokeWidth: "2"
|
|
712
|
+
}
|
|
713
|
+
),
|
|
714
|
+
/* @__PURE__ */ (0, import_jsx_runtime2.jsx)(
|
|
715
|
+
"path",
|
|
716
|
+
{
|
|
717
|
+
d: "M10 11v7M14 11v7",
|
|
718
|
+
stroke: "currentColor",
|
|
719
|
+
strokeWidth: "2",
|
|
720
|
+
strokeLinecap: "round"
|
|
721
|
+
}
|
|
722
|
+
)
|
|
723
|
+
] });
|
|
724
|
+
}
|
|
725
|
+
|
|
726
|
+
// src/components/FileIconGrid.tsx
|
|
727
|
+
var import_jsx_runtime3 = require("react/jsx-runtime");
|
|
728
|
+
function FileIconGrid(props) {
|
|
729
|
+
const {
|
|
730
|
+
files,
|
|
731
|
+
deleteDisabled,
|
|
732
|
+
onDeleted,
|
|
733
|
+
selectedId,
|
|
734
|
+
onSelect,
|
|
735
|
+
icon,
|
|
736
|
+
iconHtml,
|
|
737
|
+
className
|
|
738
|
+
} = props;
|
|
739
|
+
return /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
|
|
740
|
+
"div",
|
|
741
|
+
{
|
|
742
|
+
className,
|
|
743
|
+
style: {
|
|
744
|
+
display: "grid",
|
|
745
|
+
gridTemplateColumns: "repeat(auto-fill, minmax(280px, 1fr))",
|
|
746
|
+
gap: 12
|
|
747
|
+
},
|
|
748
|
+
children: files.map((f) => /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
|
|
749
|
+
FileIconCard,
|
|
750
|
+
{
|
|
751
|
+
file: f,
|
|
752
|
+
deleteDisabled,
|
|
753
|
+
onDeleteFile: onDeleted,
|
|
754
|
+
onSelect,
|
|
755
|
+
selected: selectedId === f.Id,
|
|
756
|
+
icon,
|
|
757
|
+
iconHtml
|
|
758
|
+
},
|
|
759
|
+
f.Id
|
|
760
|
+
))
|
|
761
|
+
}
|
|
762
|
+
);
|
|
763
|
+
}
|
|
764
|
+
|
|
765
|
+
// src/components/ContainerIdGridPanel.tsx
|
|
766
|
+
var import_jsx_runtime4 = require("react/jsx-runtime");
|
|
767
|
+
function ContainerIdGridPanel(props) {
|
|
768
|
+
const {
|
|
769
|
+
containerApiBaseUrl,
|
|
770
|
+
storageApiBaseUrl,
|
|
771
|
+
containerIds,
|
|
772
|
+
onContainerIdsChange,
|
|
773
|
+
accept = "*/*",
|
|
774
|
+
multiple = true,
|
|
775
|
+
selectedId,
|
|
776
|
+
onSelect,
|
|
777
|
+
icon,
|
|
778
|
+
iconHtml,
|
|
779
|
+
deleteDisabled,
|
|
780
|
+
className
|
|
781
|
+
} = props;
|
|
782
|
+
const sdkDb = (0, import_react5.useMemo)(
|
|
783
|
+
() => new SparkStudioStorageSDK(containerApiBaseUrl),
|
|
784
|
+
[containerApiBaseUrl]
|
|
785
|
+
);
|
|
786
|
+
const sdkS3 = (0, import_react5.useMemo)(
|
|
787
|
+
() => new SparkStudioStorageSDK(storageApiBaseUrl),
|
|
788
|
+
[storageApiBaseUrl]
|
|
789
|
+
);
|
|
790
|
+
const [loading, setLoading] = (0, import_react5.useState)(false);
|
|
791
|
+
const [files, setFiles] = (0, import_react5.useState)([]);
|
|
792
|
+
const [dragOver, setDragOver] = (0, import_react5.useState)(false);
|
|
793
|
+
const [errMsg, setErrMsg] = (0, import_react5.useState)(null);
|
|
794
|
+
const idsRef = (0, import_react5.useRef)(containerIds);
|
|
795
|
+
(0, import_react5.useEffect)(() => {
|
|
796
|
+
idsRef.current = containerIds;
|
|
797
|
+
}, [containerIds]);
|
|
798
|
+
const createdByFileRef = (0, import_react5.useRef)(/* @__PURE__ */ new WeakMap());
|
|
799
|
+
(0, import_react5.useEffect)(() => {
|
|
800
|
+
let cancelled = false;
|
|
801
|
+
async function loadByIds(ids) {
|
|
802
|
+
setErrMsg(null);
|
|
803
|
+
if (!ids || ids.length === 0) {
|
|
804
|
+
setFiles([]);
|
|
805
|
+
return;
|
|
806
|
+
}
|
|
807
|
+
setLoading(true);
|
|
808
|
+
try {
|
|
809
|
+
const results = await Promise.all(
|
|
810
|
+
ids.map(async (id) => {
|
|
811
|
+
try {
|
|
812
|
+
const dto = await sdkDb.container.Read?.(id);
|
|
813
|
+
return dto ?? null;
|
|
814
|
+
} catch {
|
|
815
|
+
return null;
|
|
816
|
+
}
|
|
817
|
+
})
|
|
818
|
+
);
|
|
819
|
+
if (cancelled) return;
|
|
820
|
+
const map = /* @__PURE__ */ new Map();
|
|
821
|
+
for (const r of results) if (r?.Id) map.set(r.Id, r);
|
|
822
|
+
setFiles(
|
|
823
|
+
ids.map((id) => map.get(id)).filter(Boolean)
|
|
824
|
+
);
|
|
825
|
+
} catch (e) {
|
|
826
|
+
if (!cancelled)
|
|
827
|
+
setErrMsg(e instanceof Error ? e.message : "Failed to load files");
|
|
828
|
+
} finally {
|
|
829
|
+
if (!cancelled) setLoading(false);
|
|
830
|
+
}
|
|
831
|
+
}
|
|
832
|
+
void loadByIds(containerIds);
|
|
833
|
+
return () => {
|
|
834
|
+
cancelled = true;
|
|
835
|
+
};
|
|
836
|
+
}, [containerIds, sdkDb]);
|
|
837
|
+
const getPresignedUrl = async (file) => {
|
|
838
|
+
const contentType = file.type || "application/octet-stream";
|
|
839
|
+
const containerDTO = await sdkDb.container.CreateFileContainer(
|
|
840
|
+
file.name,
|
|
841
|
+
file.size,
|
|
842
|
+
encodeURIComponent(contentType)
|
|
843
|
+
);
|
|
844
|
+
createdByFileRef.current.set(file, containerDTO);
|
|
845
|
+
let lastError;
|
|
846
|
+
for (let i = 1; i <= 3; i++) {
|
|
847
|
+
try {
|
|
848
|
+
return await sdkS3.s3.GetPreSignedUrl(containerDTO);
|
|
849
|
+
} catch (e) {
|
|
850
|
+
lastError = e;
|
|
851
|
+
if (i < 3) await new Promise((r) => setTimeout(r, 500 * i));
|
|
852
|
+
}
|
|
853
|
+
}
|
|
854
|
+
throw lastError instanceof Error ? lastError : new Error("Failed to fetch presigned URL");
|
|
855
|
+
};
|
|
856
|
+
const { uploads, startUploadsIfNeeded } = UseUploadManager({
|
|
857
|
+
autoUpload: true,
|
|
858
|
+
getPresignedUrl,
|
|
859
|
+
onUploadComplete: async (file) => {
|
|
860
|
+
setErrMsg(null);
|
|
861
|
+
const created = createdByFileRef.current.get(file);
|
|
862
|
+
if (created?.Id) {
|
|
863
|
+
const prev = idsRef.current ?? [];
|
|
864
|
+
const next = prev.includes(created.Id) ? prev : [...prev, created.Id];
|
|
865
|
+
onContainerIdsChange(next);
|
|
866
|
+
}
|
|
867
|
+
if (created?.Id) {
|
|
868
|
+
try {
|
|
869
|
+
const refreshed = await sdkDb.container.Read?.(created.Id);
|
|
870
|
+
if (refreshed) {
|
|
871
|
+
setFiles((prevFiles) => {
|
|
872
|
+
const nextFiles = prevFiles.slice();
|
|
873
|
+
const idx = nextFiles.findIndex((x) => x.Id === created.Id);
|
|
874
|
+
if (idx >= 0) nextFiles[idx] = refreshed;
|
|
875
|
+
else nextFiles.push(refreshed);
|
|
876
|
+
return nextFiles;
|
|
877
|
+
});
|
|
878
|
+
}
|
|
879
|
+
} catch {
|
|
880
|
+
}
|
|
881
|
+
}
|
|
882
|
+
},
|
|
883
|
+
onUploadError: (_file, error) => {
|
|
884
|
+
setErrMsg(error?.message ?? "Upload failed");
|
|
885
|
+
}
|
|
886
|
+
});
|
|
887
|
+
const handleDelete = async (file) => {
|
|
888
|
+
if (deleteDisabled) return;
|
|
889
|
+
await sdkDb.container.DeleteContainer(file.Id);
|
|
890
|
+
await sdkS3.s3.DeleteS3(file);
|
|
891
|
+
const prev = idsRef.current ?? [];
|
|
892
|
+
onContainerIdsChange(prev.filter((id) => id !== file.Id));
|
|
893
|
+
setFiles((prevFiles) => prevFiles.filter((x) => x.Id !== file.Id));
|
|
894
|
+
};
|
|
895
|
+
const openPicker = () => {
|
|
896
|
+
const input = document.createElement("input");
|
|
897
|
+
input.type = "file";
|
|
898
|
+
input.multiple = multiple;
|
|
899
|
+
input.accept = accept;
|
|
900
|
+
input.onchange = () => {
|
|
901
|
+
if (input.files) startUploadsIfNeeded(input.files);
|
|
902
|
+
};
|
|
903
|
+
input.click();
|
|
904
|
+
};
|
|
905
|
+
const onDrop = (ev) => {
|
|
906
|
+
ev.preventDefault();
|
|
907
|
+
ev.stopPropagation();
|
|
908
|
+
setDragOver(false);
|
|
909
|
+
const list = ev.dataTransfer.files;
|
|
910
|
+
if (!list || list.length === 0) return;
|
|
911
|
+
startUploadsIfNeeded(list);
|
|
912
|
+
};
|
|
913
|
+
return /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)("div", { className, style: { display: "grid", gap: 12 }, children: [
|
|
914
|
+
/* @__PURE__ */ (0, import_jsx_runtime4.jsxs)("div", { children: [
|
|
915
|
+
/* @__PURE__ */ (0, import_jsx_runtime4.jsx)(UploadProgressList, { uploads }),
|
|
916
|
+
errMsg ? /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("div", { style: { fontSize: 12, color: "crimson", marginTop: 6 }, children: errMsg }) : null
|
|
917
|
+
] }),
|
|
918
|
+
/* @__PURE__ */ (0, import_jsx_runtime4.jsxs)(
|
|
919
|
+
"div",
|
|
920
|
+
{
|
|
921
|
+
onDragEnter: (e) => {
|
|
922
|
+
e.preventDefault();
|
|
923
|
+
e.stopPropagation();
|
|
924
|
+
setDragOver(true);
|
|
925
|
+
},
|
|
926
|
+
onDragOver: (e) => {
|
|
927
|
+
e.preventDefault();
|
|
928
|
+
e.stopPropagation();
|
|
929
|
+
setDragOver(true);
|
|
930
|
+
},
|
|
931
|
+
onDragLeave: (e) => {
|
|
932
|
+
e.preventDefault();
|
|
933
|
+
e.stopPropagation();
|
|
934
|
+
setDragOver(false);
|
|
935
|
+
},
|
|
936
|
+
onDrop,
|
|
937
|
+
style: {
|
|
938
|
+
position: "relative",
|
|
939
|
+
borderRadius: 14,
|
|
940
|
+
border: `2px dashed ${dragOver ? "rgba(13,110,253,0.9)" : "rgba(0,0,0,0.15)"}`,
|
|
941
|
+
background: dragOver ? "rgba(13,110,253,0.06)" : "transparent",
|
|
942
|
+
padding: 12
|
|
943
|
+
},
|
|
944
|
+
children: [
|
|
945
|
+
dragOver ? /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(
|
|
946
|
+
"div",
|
|
947
|
+
{
|
|
948
|
+
style: {
|
|
949
|
+
position: "absolute",
|
|
950
|
+
inset: 0,
|
|
951
|
+
borderRadius: 14,
|
|
952
|
+
display: "grid",
|
|
953
|
+
placeItems: "center",
|
|
954
|
+
pointerEvents: "none",
|
|
955
|
+
background: "rgba(13,110,253,0.10)",
|
|
956
|
+
fontWeight: 800
|
|
957
|
+
},
|
|
958
|
+
children: "Drop files to upload"
|
|
959
|
+
}
|
|
960
|
+
) : null,
|
|
961
|
+
/* @__PURE__ */ (0, import_jsx_runtime4.jsxs)(
|
|
962
|
+
"div",
|
|
963
|
+
{
|
|
964
|
+
style: {
|
|
965
|
+
display: "flex",
|
|
966
|
+
justifyContent: "space-between",
|
|
967
|
+
marginBottom: 10
|
|
968
|
+
},
|
|
969
|
+
children: [
|
|
970
|
+
/* @__PURE__ */ (0, import_jsx_runtime4.jsx)("div", { style: { fontWeight: 700 }, children: "Files" }),
|
|
971
|
+
/* @__PURE__ */ (0, import_jsx_runtime4.jsx)(
|
|
972
|
+
"button",
|
|
973
|
+
{
|
|
974
|
+
type: "button",
|
|
975
|
+
onClick: openPicker,
|
|
976
|
+
style: {
|
|
977
|
+
border: "1px solid rgba(0,0,0,0.18)",
|
|
978
|
+
background: "white",
|
|
979
|
+
borderRadius: 10,
|
|
980
|
+
padding: "8px 10px",
|
|
981
|
+
cursor: "pointer",
|
|
982
|
+
fontWeight: 600
|
|
983
|
+
},
|
|
984
|
+
children: "Browse\u2026"
|
|
985
|
+
}
|
|
986
|
+
)
|
|
987
|
+
]
|
|
988
|
+
}
|
|
989
|
+
),
|
|
990
|
+
loading ? /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(
|
|
991
|
+
"div",
|
|
992
|
+
{
|
|
993
|
+
className: "d-flex justify-content-center align-items-center",
|
|
994
|
+
style: { minHeight: 120 },
|
|
995
|
+
children: /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("div", { className: "spinner-border text-secondary", role: "status" })
|
|
996
|
+
}
|
|
997
|
+
) : /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(
|
|
998
|
+
FileIconGrid,
|
|
999
|
+
{
|
|
1000
|
+
files,
|
|
1001
|
+
deleteDisabled,
|
|
1002
|
+
onDeleted: handleDelete,
|
|
1003
|
+
selectedId,
|
|
1004
|
+
onSelect,
|
|
1005
|
+
icon,
|
|
1006
|
+
iconHtml
|
|
1007
|
+
}
|
|
1008
|
+
)
|
|
1009
|
+
]
|
|
1010
|
+
}
|
|
1011
|
+
)
|
|
1012
|
+
] });
|
|
1013
|
+
}
|
|
1014
|
+
|
|
1015
|
+
// src/components/ContainerUploadPanel.tsx
|
|
1016
|
+
var import_react10 = require("react");
|
|
1017
|
+
|
|
1018
|
+
// src/components/UploadContainer.tsx
|
|
1019
|
+
var import_react8 = require("react");
|
|
1020
|
+
|
|
1021
|
+
// src/components/UploadDropzone.tsx
|
|
1022
|
+
var import_react6 = require("react");
|
|
1023
|
+
var import_jsx_runtime5 = require("react/jsx-runtime");
|
|
1024
|
+
var UploadDropzone = ({
|
|
1025
|
+
isDragging,
|
|
1026
|
+
onDragOver,
|
|
1027
|
+
onDragLeave,
|
|
1028
|
+
onDrop,
|
|
1029
|
+
className = "",
|
|
1030
|
+
style,
|
|
1031
|
+
children
|
|
1032
|
+
}) => {
|
|
1033
|
+
const baseClass = "rounded-3 d-flex flex-column align-items-center justify-content-center";
|
|
1034
|
+
const stateClass = isDragging ? "bg-body-secondary border-dashed border-2 border-secondary" : "bg-body-trasparent border-solid border-transparent border-2";
|
|
1035
|
+
const combinedClassName = `${baseClass} ${stateClass} ${className}`.trim();
|
|
1036
|
+
const handleDragOver = (e) => {
|
|
1037
|
+
e.preventDefault();
|
|
1038
|
+
onDragOver?.(e);
|
|
1039
|
+
};
|
|
1040
|
+
const handleDragLeave = (e) => {
|
|
1041
|
+
e.preventDefault();
|
|
1042
|
+
onDragLeave?.(e);
|
|
1043
|
+
};
|
|
1044
|
+
const handleDrop = (e) => {
|
|
1045
|
+
e.preventDefault();
|
|
1046
|
+
onDrop?.(e);
|
|
1047
|
+
};
|
|
1048
|
+
return /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(
|
|
1049
|
+
"div",
|
|
1050
|
+
{
|
|
1051
|
+
className: combinedClassName,
|
|
1052
|
+
style: { minHeight: "140px", ...style },
|
|
1053
|
+
onDragOver: handleDragOver,
|
|
1054
|
+
onDragLeave: handleDragLeave,
|
|
1055
|
+
onDrop: handleDrop,
|
|
1056
|
+
children
|
|
1057
|
+
}
|
|
1058
|
+
);
|
|
1059
|
+
};
|
|
1060
|
+
|
|
555
1061
|
// src/components/DesktopFileIcon.tsx
|
|
556
1062
|
var import_free_solid_svg_icons = require("@fortawesome/free-solid-svg-icons");
|
|
557
1063
|
var import_react_fontawesome = require("@fortawesome/react-fontawesome");
|
|
558
|
-
var
|
|
559
|
-
var
|
|
1064
|
+
var import_react7 = require("react");
|
|
1065
|
+
var import_jsx_runtime6 = require("react/jsx-runtime");
|
|
560
1066
|
function getFileExtension(name) {
|
|
561
1067
|
if (!name) return null;
|
|
562
1068
|
const lastDot = name.lastIndexOf(".");
|
|
@@ -635,13 +1141,13 @@ var DesktopFileIcon = ({
|
|
|
635
1141
|
onOpen,
|
|
636
1142
|
onDelete
|
|
637
1143
|
}) => {
|
|
638
|
-
const [contextMenuPos, setContextMenuPos] = (0,
|
|
1144
|
+
const [contextMenuPos, setContextMenuPos] = (0, import_react7.useState)(
|
|
639
1145
|
null
|
|
640
1146
|
);
|
|
641
|
-
const [isHovered, setIsHovered] = (0,
|
|
642
|
-
const [isDeleting, setIsDeleting] = (0,
|
|
643
|
-
const iconRef = (0,
|
|
644
|
-
const menuRef = (0,
|
|
1147
|
+
const [isHovered, setIsHovered] = (0, import_react7.useState)(false);
|
|
1148
|
+
const [isDeleting, setIsDeleting] = (0, import_react7.useState)(false);
|
|
1149
|
+
const iconRef = (0, import_react7.useRef)(null);
|
|
1150
|
+
const menuRef = (0, import_react7.useRef)(null);
|
|
645
1151
|
const handleDoubleClick = () => {
|
|
646
1152
|
if (isDeleting) return;
|
|
647
1153
|
if (onOpen) {
|
|
@@ -699,7 +1205,7 @@ var DesktopFileIcon = ({
|
|
|
699
1205
|
const formattedSize = typeof sizeBytes === "number" ? `${(sizeBytes / 1024).toFixed(1)} KB` : void 0;
|
|
700
1206
|
const ext = getFileExtension(name);
|
|
701
1207
|
const iconToRender = getIconForExtension(ext);
|
|
702
|
-
(0,
|
|
1208
|
+
(0, import_react7.useEffect)(() => {
|
|
703
1209
|
if (!contextMenuPos) return;
|
|
704
1210
|
const handleGlobalClick = (e) => {
|
|
705
1211
|
const target = e.target;
|
|
@@ -716,8 +1222,8 @@ var DesktopFileIcon = ({
|
|
|
716
1222
|
document.removeEventListener("mousedown", handleGlobalClick);
|
|
717
1223
|
};
|
|
718
1224
|
}, [contextMenuPos]);
|
|
719
|
-
return /* @__PURE__ */ (0,
|
|
720
|
-
/* @__PURE__ */ (0,
|
|
1225
|
+
return /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)(import_jsx_runtime6.Fragment, { children: [
|
|
1226
|
+
/* @__PURE__ */ (0, import_jsx_runtime6.jsxs)(
|
|
721
1227
|
"div",
|
|
722
1228
|
{
|
|
723
1229
|
ref: iconRef,
|
|
@@ -738,7 +1244,7 @@ var DesktopFileIcon = ({
|
|
|
738
1244
|
onMouseEnter: () => setIsHovered(true),
|
|
739
1245
|
onMouseLeave: () => setIsHovered(false),
|
|
740
1246
|
children: [
|
|
741
|
-
/* @__PURE__ */ (0,
|
|
1247
|
+
/* @__PURE__ */ (0, import_jsx_runtime6.jsxs)(
|
|
742
1248
|
"div",
|
|
743
1249
|
{
|
|
744
1250
|
className: "border rounded-3 d-flex align-items-center justify-content-center mb-1 shadow-sm position-relative",
|
|
@@ -747,17 +1253,17 @@ var DesktopFileIcon = ({
|
|
|
747
1253
|
height: 64
|
|
748
1254
|
},
|
|
749
1255
|
children: [
|
|
750
|
-
/* @__PURE__ */ (0,
|
|
751
|
-
isDeleting && /* @__PURE__ */ (0,
|
|
1256
|
+
/* @__PURE__ */ (0, import_jsx_runtime6.jsx)(import_react_fontawesome.FontAwesomeIcon, { icon: iconToRender, className: "fs-2" }),
|
|
1257
|
+
isDeleting && /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("div", { className: "position-absolute top-50 start-50 translate-middle", children: /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("div", { className: "spinner-border spinner-border-sm text-danger" }) })
|
|
752
1258
|
]
|
|
753
1259
|
}
|
|
754
1260
|
),
|
|
755
|
-
/* @__PURE__ */ (0,
|
|
756
|
-
formattedSize && /* @__PURE__ */ (0,
|
|
1261
|
+
/* @__PURE__ */ (0, import_jsx_runtime6.jsx)("div", { className: "small text-center text-truncate", style: { width: "100%" }, children: name }),
|
|
1262
|
+
formattedSize && /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("small", { className: "text-muted mt-1", children: formattedSize })
|
|
757
1263
|
]
|
|
758
1264
|
}
|
|
759
1265
|
),
|
|
760
|
-
contextMenuPos && !isDeleting && /* @__PURE__ */ (0,
|
|
1266
|
+
contextMenuPos && !isDeleting && /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)(
|
|
761
1267
|
"div",
|
|
762
1268
|
{
|
|
763
1269
|
ref: menuRef,
|
|
@@ -770,7 +1276,7 @@ var DesktopFileIcon = ({
|
|
|
770
1276
|
},
|
|
771
1277
|
onClick: (e) => e.stopPropagation(),
|
|
772
1278
|
children: [
|
|
773
|
-
/* @__PURE__ */ (0,
|
|
1279
|
+
/* @__PURE__ */ (0, import_jsx_runtime6.jsx)(
|
|
774
1280
|
"button",
|
|
775
1281
|
{
|
|
776
1282
|
type: "button",
|
|
@@ -780,7 +1286,7 @@ var DesktopFileIcon = ({
|
|
|
780
1286
|
children: "Download file"
|
|
781
1287
|
}
|
|
782
1288
|
),
|
|
783
|
-
/* @__PURE__ */ (0,
|
|
1289
|
+
/* @__PURE__ */ (0, import_jsx_runtime6.jsx)(
|
|
784
1290
|
"button",
|
|
785
1291
|
{
|
|
786
1292
|
type: "button",
|
|
@@ -790,8 +1296,8 @@ var DesktopFileIcon = ({
|
|
|
790
1296
|
children: "Copy download URL"
|
|
791
1297
|
}
|
|
792
1298
|
),
|
|
793
|
-
/* @__PURE__ */ (0,
|
|
794
|
-
/* @__PURE__ */ (0,
|
|
1299
|
+
/* @__PURE__ */ (0, import_jsx_runtime6.jsx)("div", { className: "dropdown-divider" }),
|
|
1300
|
+
/* @__PURE__ */ (0, import_jsx_runtime6.jsx)(
|
|
795
1301
|
"button",
|
|
796
1302
|
{
|
|
797
1303
|
type: "button",
|
|
@@ -807,160 +1313,165 @@ var DesktopFileIcon = ({
|
|
|
807
1313
|
};
|
|
808
1314
|
|
|
809
1315
|
// src/components/UploadContainer.tsx
|
|
810
|
-
var
|
|
811
|
-
|
|
812
|
-
|
|
813
|
-
|
|
814
|
-
|
|
815
|
-
|
|
816
|
-
|
|
817
|
-
|
|
818
|
-
|
|
819
|
-
|
|
820
|
-
|
|
821
|
-
|
|
822
|
-
|
|
823
|
-
|
|
824
|
-
|
|
1316
|
+
var import_jsx_runtime7 = require("react/jsx-runtime");
|
|
1317
|
+
function filesToFileList(files) {
|
|
1318
|
+
const dt = new DataTransfer();
|
|
1319
|
+
for (const f of files) dt.items.add(f);
|
|
1320
|
+
return dt.files;
|
|
1321
|
+
}
|
|
1322
|
+
var UploadContainer = (0, import_react8.forwardRef)(
|
|
1323
|
+
({
|
|
1324
|
+
multiple = true,
|
|
1325
|
+
accept = "*/*",
|
|
1326
|
+
onFilesSelected,
|
|
1327
|
+
existingFiles = [],
|
|
1328
|
+
existingFilesLoading = false,
|
|
1329
|
+
onExistingFileClick,
|
|
1330
|
+
onDeleteFile,
|
|
1331
|
+
autoUpload = false,
|
|
825
1332
|
getPresignedUrl,
|
|
826
1333
|
onUploadComplete,
|
|
827
1334
|
onUploadError
|
|
828
|
-
})
|
|
829
|
-
|
|
830
|
-
|
|
831
|
-
|
|
832
|
-
|
|
833
|
-
|
|
834
|
-
|
|
835
|
-
|
|
836
|
-
|
|
837
|
-
|
|
838
|
-
|
|
839
|
-
|
|
840
|
-
|
|
841
|
-
|
|
842
|
-
|
|
843
|
-
|
|
844
|
-
|
|
845
|
-
|
|
846
|
-
|
|
847
|
-
|
|
848
|
-
|
|
849
|
-
|
|
850
|
-
|
|
851
|
-
|
|
852
|
-
|
|
853
|
-
|
|
854
|
-
|
|
855
|
-
|
|
856
|
-
|
|
857
|
-
|
|
858
|
-
|
|
859
|
-
|
|
860
|
-
|
|
861
|
-
|
|
862
|
-
|
|
863
|
-
|
|
864
|
-
|
|
865
|
-
|
|
866
|
-
|
|
867
|
-
|
|
868
|
-
|
|
869
|
-
|
|
870
|
-
|
|
871
|
-
|
|
1335
|
+
}, ref) => {
|
|
1336
|
+
const [isDragging, setIsDragging] = (0, import_react8.useState)(false);
|
|
1337
|
+
const inputId = (0, import_react8.useMemo)(() => `filePicker_${crypto.randomUUID()}`, []);
|
|
1338
|
+
const inputRef = (0, import_react8.useRef)(null);
|
|
1339
|
+
const { uploads, startUploadsIfNeeded } = UseUploadManager({
|
|
1340
|
+
autoUpload,
|
|
1341
|
+
getPresignedUrl,
|
|
1342
|
+
onUploadComplete,
|
|
1343
|
+
onUploadError
|
|
1344
|
+
});
|
|
1345
|
+
const selectAndUpload = (files) => {
|
|
1346
|
+
if (!files || files.length === 0) return;
|
|
1347
|
+
onFilesSelected?.(files);
|
|
1348
|
+
startUploadsIfNeeded(files);
|
|
1349
|
+
};
|
|
1350
|
+
(0, import_react8.useImperativeHandle)(ref, () => ({
|
|
1351
|
+
enqueueFiles(files) {
|
|
1352
|
+
const list = Array.isArray(files) ? filesToFileList(files) : files;
|
|
1353
|
+
selectAndUpload(list);
|
|
1354
|
+
},
|
|
1355
|
+
openFilePicker() {
|
|
1356
|
+
inputRef.current?.click();
|
|
1357
|
+
}
|
|
1358
|
+
}));
|
|
1359
|
+
const handleDragOver = (e) => {
|
|
1360
|
+
e.preventDefault();
|
|
1361
|
+
setIsDragging(true);
|
|
1362
|
+
};
|
|
1363
|
+
const handleDragLeave = (e) => {
|
|
1364
|
+
e.preventDefault();
|
|
1365
|
+
setIsDragging(false);
|
|
1366
|
+
};
|
|
1367
|
+
const handleDrop = (e) => {
|
|
1368
|
+
e.preventDefault();
|
|
1369
|
+
setIsDragging(false);
|
|
1370
|
+
const files = e.dataTransfer.files;
|
|
1371
|
+
if (!files || files.length === 0) return;
|
|
1372
|
+
selectAndUpload(files);
|
|
1373
|
+
};
|
|
1374
|
+
const handleExistingFileOpen = (file) => {
|
|
1375
|
+
if (onExistingFileClick) {
|
|
1376
|
+
onExistingFileClick(file);
|
|
1377
|
+
return;
|
|
1378
|
+
}
|
|
1379
|
+
const a = document.createElement("a");
|
|
1380
|
+
a.href = file.PublicUrl ?? "";
|
|
1381
|
+
a.download = file.Name ?? "";
|
|
1382
|
+
a.target = "_blank";
|
|
1383
|
+
a.rel = "noopener noreferrer";
|
|
1384
|
+
document.body.appendChild(a);
|
|
1385
|
+
a.click();
|
|
1386
|
+
document.body.removeChild(a);
|
|
1387
|
+
};
|
|
1388
|
+
return /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)(import_jsx_runtime7.Fragment, { children: [
|
|
1389
|
+
/* @__PURE__ */ (0, import_jsx_runtime7.jsxs)("div", { className: "w-100", children: [
|
|
1390
|
+
/* @__PURE__ */ (0, import_jsx_runtime7.jsx)(
|
|
1391
|
+
"input",
|
|
1392
|
+
{
|
|
1393
|
+
ref: inputRef,
|
|
1394
|
+
id: inputId,
|
|
1395
|
+
type: "file",
|
|
1396
|
+
multiple,
|
|
1397
|
+
accept,
|
|
1398
|
+
className: "d-none",
|
|
1399
|
+
onChange: (e) => {
|
|
1400
|
+
if (!e.target.files) return;
|
|
1401
|
+
selectAndUpload(e.target.files);
|
|
1402
|
+
e.currentTarget.value = "";
|
|
1403
|
+
}
|
|
872
1404
|
}
|
|
873
|
-
|
|
874
|
-
|
|
875
|
-
|
|
876
|
-
|
|
1405
|
+
),
|
|
1406
|
+
/* @__PURE__ */ (0, import_jsx_runtime7.jsx)("div", { className: "text-start", children: /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(
|
|
1407
|
+
"button",
|
|
1408
|
+
{
|
|
1409
|
+
type: "button",
|
|
1410
|
+
className: "btn btn-primary float-start",
|
|
1411
|
+
onClick: () => inputRef.current?.click(),
|
|
1412
|
+
children: "Browse files\u2026"
|
|
1413
|
+
}
|
|
1414
|
+
) })
|
|
1415
|
+
] }),
|
|
1416
|
+
/* @__PURE__ */ (0, import_jsx_runtime7.jsxs)(
|
|
1417
|
+
UploadDropzone,
|
|
877
1418
|
{
|
|
878
|
-
|
|
879
|
-
|
|
880
|
-
|
|
881
|
-
|
|
882
|
-
|
|
883
|
-
|
|
884
|
-
|
|
885
|
-
|
|
886
|
-
|
|
887
|
-
|
|
888
|
-
|
|
889
|
-
|
|
890
|
-
|
|
891
|
-
|
|
892
|
-
|
|
893
|
-
|
|
894
|
-
|
|
895
|
-
|
|
896
|
-
|
|
897
|
-
|
|
898
|
-
|
|
899
|
-
|
|
900
|
-
|
|
901
|
-
|
|
902
|
-
|
|
903
|
-
|
|
904
|
-
|
|
905
|
-
|
|
906
|
-
|
|
907
|
-
|
|
908
|
-
|
|
909
|
-
|
|
910
|
-
|
|
911
|
-
|
|
912
|
-
|
|
913
|
-
|
|
914
|
-
|
|
915
|
-
transition: "background 0.12s, border-color 0.12s"
|
|
1419
|
+
isDragging,
|
|
1420
|
+
onDragOver: handleDragOver,
|
|
1421
|
+
onDragLeave: handleDragLeave,
|
|
1422
|
+
onDrop: handleDrop,
|
|
1423
|
+
className: "w-100",
|
|
1424
|
+
style: { minHeight: "100px", alignItems: "stretch" },
|
|
1425
|
+
children: [
|
|
1426
|
+
/* @__PURE__ */ (0, import_jsx_runtime7.jsx)("div", { className: "w-100 mb-3", children: /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("div", { className: "d-flex flex-column flex-md-row align-items-start align-items-md-center justify-content-between gap-3", children: /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("div", { className: "flex-grow-1 w-100", children: /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(UploadProgressList, { uploads }) }) }) }),
|
|
1427
|
+
/* @__PURE__ */ (0, import_jsx_runtime7.jsx)(
|
|
1428
|
+
"div",
|
|
1429
|
+
{
|
|
1430
|
+
className: "w-100 d-flex flex-wrap gap-4 align-content-start",
|
|
1431
|
+
style: { minHeight: "140px" },
|
|
1432
|
+
children: existingFilesLoading ? /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("div", { className: "w-100 d-flex justify-content-center align-items-center", children: /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("div", { className: "spinner-border text-secondary", role: "status" }) }) : existingFiles.length === 0 ? /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)(
|
|
1433
|
+
"div",
|
|
1434
|
+
{
|
|
1435
|
+
className: "w-100 d-flex flex-column align-items-center justify-content-center text-muted",
|
|
1436
|
+
style: {
|
|
1437
|
+
minHeight: "160px",
|
|
1438
|
+
padding: "20px",
|
|
1439
|
+
cursor: "pointer",
|
|
1440
|
+
transition: "background 0.12s, border-color 0.12s"
|
|
1441
|
+
},
|
|
1442
|
+
onClick: () => inputRef.current?.click(),
|
|
1443
|
+
children: [
|
|
1444
|
+
/* @__PURE__ */ (0, import_jsx_runtime7.jsx)("strong", { children: "Drag & drop files here" }),
|
|
1445
|
+
/* @__PURE__ */ (0, import_jsx_runtime7.jsx)("small", { className: "mt-1", children: "\u2026or click to browse" })
|
|
1446
|
+
]
|
|
1447
|
+
}
|
|
1448
|
+
) : existingFiles.map((file) => /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(
|
|
1449
|
+
DesktopFileIcon,
|
|
1450
|
+
{
|
|
1451
|
+
name: file.Name,
|
|
1452
|
+
sizeBytes: file.FileSize,
|
|
1453
|
+
downloadUrl: file.PublicUrl,
|
|
1454
|
+
onOpen: () => handleExistingFileOpen(file),
|
|
1455
|
+
onDelete: () => onDeleteFile?.(file)
|
|
916
1456
|
},
|
|
917
|
-
|
|
918
|
-
|
|
919
|
-
|
|
920
|
-
|
|
921
|
-
|
|
922
|
-
|
|
923
|
-
|
|
924
|
-
|
|
925
|
-
|
|
926
|
-
|
|
927
|
-
|
|
928
|
-
onChange: (e) => {
|
|
929
|
-
if (!e.target.files) return;
|
|
930
|
-
onFilesSelected?.(e.target.files);
|
|
931
|
-
startUploadsIfNeeded(e.target.files);
|
|
932
|
-
}
|
|
933
|
-
}
|
|
934
|
-
),
|
|
935
|
-
/* @__PURE__ */ (0, import_jsx_runtime4.jsx)("strong", { children: "Drag & drop files here" }),
|
|
936
|
-
/* @__PURE__ */ (0, import_jsx_runtime4.jsx)("small", { className: "mt-1", children: "\u2026or click to browse" })
|
|
937
|
-
]
|
|
938
|
-
}
|
|
939
|
-
) : existingFiles.map((file) => /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(
|
|
940
|
-
DesktopFileIcon,
|
|
941
|
-
{
|
|
942
|
-
name: file.Name,
|
|
943
|
-
sizeBytes: file.FileSize,
|
|
944
|
-
downloadUrl: file.PublicUrl,
|
|
945
|
-
onOpen: () => handleExistingFileOpen(file),
|
|
946
|
-
onDelete: () => onDeleteFile?.(file)
|
|
947
|
-
},
|
|
948
|
-
file.Id
|
|
949
|
-
))
|
|
950
|
-
}
|
|
951
|
-
)
|
|
952
|
-
]
|
|
953
|
-
}
|
|
954
|
-
)
|
|
955
|
-
] });
|
|
956
|
-
};
|
|
1457
|
+
file.Id
|
|
1458
|
+
))
|
|
1459
|
+
}
|
|
1460
|
+
)
|
|
1461
|
+
]
|
|
1462
|
+
}
|
|
1463
|
+
)
|
|
1464
|
+
] });
|
|
1465
|
+
}
|
|
1466
|
+
);
|
|
1467
|
+
UploadContainer.displayName = "UploadContainer";
|
|
957
1468
|
|
|
958
1469
|
// src/hooks/UseContainers.ts
|
|
959
|
-
var
|
|
1470
|
+
var import_react9 = require("react");
|
|
960
1471
|
function UseContainers({ apiBaseUrl, parentId }) {
|
|
961
|
-
const [containers, setContainers] = (0,
|
|
962
|
-
const [loading, setLoading] = (0,
|
|
963
|
-
const [error, setError] = (0,
|
|
1472
|
+
const [containers, setContainers] = (0, import_react9.useState)([]);
|
|
1473
|
+
const [loading, setLoading] = (0, import_react9.useState)(false);
|
|
1474
|
+
const [error, setError] = (0, import_react9.useState)(null);
|
|
964
1475
|
const load = async () => {
|
|
965
1476
|
setLoading(true);
|
|
966
1477
|
setError(null);
|
|
@@ -976,7 +1487,7 @@ function UseContainers({ apiBaseUrl, parentId }) {
|
|
|
976
1487
|
setLoading(false);
|
|
977
1488
|
}
|
|
978
1489
|
};
|
|
979
|
-
(0,
|
|
1490
|
+
(0, import_react9.useEffect)(() => {
|
|
980
1491
|
void load();
|
|
981
1492
|
}, [apiBaseUrl, parentId]);
|
|
982
1493
|
return {
|
|
@@ -989,11 +1500,12 @@ function UseContainers({ apiBaseUrl, parentId }) {
|
|
|
989
1500
|
}
|
|
990
1501
|
|
|
991
1502
|
// src/components/ContainerUploadPanel.tsx
|
|
992
|
-
var
|
|
1503
|
+
var import_jsx_runtime8 = require("react/jsx-runtime");
|
|
993
1504
|
var ContainerUploadPanel = ({
|
|
994
1505
|
containerApiBaseUrl,
|
|
995
1506
|
storageApiBaseUrl,
|
|
996
|
-
parentContainerId
|
|
1507
|
+
parentContainerId,
|
|
1508
|
+
uploadRef
|
|
997
1509
|
}) => {
|
|
998
1510
|
const { containers, setContainers, reload, loading } = UseContainers({
|
|
999
1511
|
apiBaseUrl: containerApiBaseUrl,
|
|
@@ -1048,9 +1560,10 @@ var ContainerUploadPanel = ({
|
|
|
1048
1560
|
await sdkS3.s3.DeleteS3(file);
|
|
1049
1561
|
setContainers((prev) => prev.filter((c) => c.Id !== file.Id));
|
|
1050
1562
|
};
|
|
1051
|
-
return /* @__PURE__ */ (0,
|
|
1563
|
+
return /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(
|
|
1052
1564
|
UploadContainer,
|
|
1053
1565
|
{
|
|
1566
|
+
ref: uploadRef,
|
|
1054
1567
|
existingFiles: containers,
|
|
1055
1568
|
existingFilesLoading: loading,
|
|
1056
1569
|
onExistingFileClick: handleExistingFileClick,
|
|
@@ -1063,9 +1576,172 @@ var ContainerUploadPanel = ({
|
|
|
1063
1576
|
);
|
|
1064
1577
|
};
|
|
1065
1578
|
|
|
1579
|
+
// src/components/FileGridUploadPanel.tsx
|
|
1580
|
+
var import_react11 = require("react");
|
|
1581
|
+
var import_jsx_runtime9 = require("react/jsx-runtime");
|
|
1582
|
+
function FileGridUploadPanel(props) {
|
|
1583
|
+
const {
|
|
1584
|
+
containerApiBaseUrl,
|
|
1585
|
+
storageApiBaseUrl,
|
|
1586
|
+
parentContainerId,
|
|
1587
|
+
accept = "*/*",
|
|
1588
|
+
multiple = true,
|
|
1589
|
+
selectedId,
|
|
1590
|
+
onSelect,
|
|
1591
|
+
icon,
|
|
1592
|
+
iconHtml,
|
|
1593
|
+
deleteDisabled
|
|
1594
|
+
} = props;
|
|
1595
|
+
const { containers, setContainers, reload, loading } = UseContainers({
|
|
1596
|
+
apiBaseUrl: containerApiBaseUrl,
|
|
1597
|
+
parentId: parentContainerId
|
|
1598
|
+
});
|
|
1599
|
+
const [isDragging, setIsDragging] = (0, import_react11.useState)(false);
|
|
1600
|
+
const [error, setError] = (0, import_react11.useState)(null);
|
|
1601
|
+
const sdkDb = (0, import_react11.useMemo)(() => new SparkStudioStorageSDK(containerApiBaseUrl), [containerApiBaseUrl]);
|
|
1602
|
+
const sdkS3 = (0, import_react11.useMemo)(() => new SparkStudioStorageSDK(storageApiBaseUrl), [storageApiBaseUrl]);
|
|
1603
|
+
const getPresignedUrl = async (file) => {
|
|
1604
|
+
const contentType = file.type || "application/octet-stream";
|
|
1605
|
+
const containerDTO = await sdkDb.container.CreateFileContainer(
|
|
1606
|
+
file.name,
|
|
1607
|
+
file.size,
|
|
1608
|
+
encodeURIComponent(contentType)
|
|
1609
|
+
);
|
|
1610
|
+
let lastError;
|
|
1611
|
+
for (let i = 1; i <= 3; i++) {
|
|
1612
|
+
try {
|
|
1613
|
+
return await sdkS3.s3.GetPreSignedUrl(containerDTO);
|
|
1614
|
+
} catch (e) {
|
|
1615
|
+
lastError = e;
|
|
1616
|
+
if (i < 3) await new Promise((r) => setTimeout(r, 500 * i));
|
|
1617
|
+
}
|
|
1618
|
+
}
|
|
1619
|
+
throw lastError instanceof Error ? lastError : new Error("Failed to fetch presigned URL");
|
|
1620
|
+
};
|
|
1621
|
+
const { uploads, startUploadsIfNeeded } = UseUploadManager({
|
|
1622
|
+
autoUpload: true,
|
|
1623
|
+
getPresignedUrl,
|
|
1624
|
+
onUploadComplete: async () => {
|
|
1625
|
+
setError(null);
|
|
1626
|
+
await reload();
|
|
1627
|
+
},
|
|
1628
|
+
onUploadError: (_file, err) => {
|
|
1629
|
+
setError(err.message ?? "Upload failed");
|
|
1630
|
+
}
|
|
1631
|
+
});
|
|
1632
|
+
const handleDeleteFile = async (file) => {
|
|
1633
|
+
const sdkDb2 = new SparkStudioStorageSDK(containerApiBaseUrl);
|
|
1634
|
+
const sdkS32 = new SparkStudioStorageSDK(storageApiBaseUrl);
|
|
1635
|
+
await sdkDb2.container.DeleteContainer(file.Id);
|
|
1636
|
+
await sdkS32.s3.DeleteS3(file);
|
|
1637
|
+
setContainers((prev) => prev.filter((c) => c.Id !== file.Id));
|
|
1638
|
+
};
|
|
1639
|
+
const openPicker = () => {
|
|
1640
|
+
const input = document.createElement("input");
|
|
1641
|
+
input.type = "file";
|
|
1642
|
+
input.multiple = multiple;
|
|
1643
|
+
input.accept = accept;
|
|
1644
|
+
input.onchange = () => {
|
|
1645
|
+
if (!input.files || input.files.length === 0) return;
|
|
1646
|
+
startUploadsIfNeeded(input.files);
|
|
1647
|
+
};
|
|
1648
|
+
input.click();
|
|
1649
|
+
};
|
|
1650
|
+
return /* @__PURE__ */ (0, import_jsx_runtime9.jsxs)("div", { style: { display: "grid", gap: 12 }, children: [
|
|
1651
|
+
/* @__PURE__ */ (0, import_jsx_runtime9.jsxs)("div", { children: [
|
|
1652
|
+
/* @__PURE__ */ (0, import_jsx_runtime9.jsx)(UploadProgressList, { uploads }),
|
|
1653
|
+
error ? /* @__PURE__ */ (0, import_jsx_runtime9.jsx)("div", { style: { fontSize: 12, color: "crimson", marginTop: 6 }, children: error }) : null
|
|
1654
|
+
] }),
|
|
1655
|
+
/* @__PURE__ */ (0, import_jsx_runtime9.jsxs)(
|
|
1656
|
+
"div",
|
|
1657
|
+
{
|
|
1658
|
+
onDragEnter: (e) => {
|
|
1659
|
+
e.preventDefault();
|
|
1660
|
+
e.stopPropagation();
|
|
1661
|
+
setIsDragging(true);
|
|
1662
|
+
},
|
|
1663
|
+
onDragOver: (e) => {
|
|
1664
|
+
e.preventDefault();
|
|
1665
|
+
e.stopPropagation();
|
|
1666
|
+
setIsDragging(true);
|
|
1667
|
+
},
|
|
1668
|
+
onDragLeave: (e) => {
|
|
1669
|
+
e.preventDefault();
|
|
1670
|
+
e.stopPropagation();
|
|
1671
|
+
setIsDragging(false);
|
|
1672
|
+
},
|
|
1673
|
+
onDrop: (e) => {
|
|
1674
|
+
e.preventDefault();
|
|
1675
|
+
e.stopPropagation();
|
|
1676
|
+
setIsDragging(false);
|
|
1677
|
+
const files = e.dataTransfer.files;
|
|
1678
|
+
if (!files || files.length === 0) return;
|
|
1679
|
+
startUploadsIfNeeded(files);
|
|
1680
|
+
},
|
|
1681
|
+
style: {
|
|
1682
|
+
position: "relative",
|
|
1683
|
+
borderRadius: 14,
|
|
1684
|
+
border: isDragging ? "2px dashed rgba(13,110,253,0.9)" : "2px dashed rgba(0,0,0,0.15)",
|
|
1685
|
+
background: isDragging ? "rgba(13,110,253,0.06)" : "transparent",
|
|
1686
|
+
padding: 12
|
|
1687
|
+
},
|
|
1688
|
+
children: [
|
|
1689
|
+
isDragging ? /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(
|
|
1690
|
+
"div",
|
|
1691
|
+
{
|
|
1692
|
+
style: {
|
|
1693
|
+
position: "absolute",
|
|
1694
|
+
inset: 0,
|
|
1695
|
+
borderRadius: 14,
|
|
1696
|
+
display: "grid",
|
|
1697
|
+
placeItems: "center",
|
|
1698
|
+
pointerEvents: "none",
|
|
1699
|
+
background: "rgba(13,110,253,0.08)",
|
|
1700
|
+
fontWeight: 800
|
|
1701
|
+
},
|
|
1702
|
+
children: "Drop files to upload"
|
|
1703
|
+
}
|
|
1704
|
+
) : null,
|
|
1705
|
+
/* @__PURE__ */ (0, import_jsx_runtime9.jsxs)("div", { style: { display: "flex", justifyContent: "space-between", marginBottom: 10 }, children: [
|
|
1706
|
+
/* @__PURE__ */ (0, import_jsx_runtime9.jsx)("div", { style: { fontWeight: 700 }, children: "Files" }),
|
|
1707
|
+
/* @__PURE__ */ (0, import_jsx_runtime9.jsx)(
|
|
1708
|
+
"button",
|
|
1709
|
+
{
|
|
1710
|
+
type: "button",
|
|
1711
|
+
onClick: openPicker,
|
|
1712
|
+
style: {
|
|
1713
|
+
border: "1px solid rgba(0,0,0,0.18)",
|
|
1714
|
+
background: "white",
|
|
1715
|
+
borderRadius: 10,
|
|
1716
|
+
padding: "8px 10px",
|
|
1717
|
+
cursor: "pointer",
|
|
1718
|
+
fontWeight: 600
|
|
1719
|
+
},
|
|
1720
|
+
children: "Browse\u2026"
|
|
1721
|
+
}
|
|
1722
|
+
)
|
|
1723
|
+
] }),
|
|
1724
|
+
loading ? /* @__PURE__ */ (0, import_jsx_runtime9.jsx)("div", { className: "d-flex justify-content-center align-items-center", style: { minHeight: 120 }, children: /* @__PURE__ */ (0, import_jsx_runtime9.jsx)("div", { className: "spinner-border text-secondary", role: "status" }) }) : /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(
|
|
1725
|
+
FileIconGrid,
|
|
1726
|
+
{
|
|
1727
|
+
files: containers,
|
|
1728
|
+
deleteDisabled,
|
|
1729
|
+
onDeleted: handleDeleteFile,
|
|
1730
|
+
selectedId,
|
|
1731
|
+
onSelect,
|
|
1732
|
+
icon,
|
|
1733
|
+
iconHtml
|
|
1734
|
+
}
|
|
1735
|
+
)
|
|
1736
|
+
]
|
|
1737
|
+
}
|
|
1738
|
+
)
|
|
1739
|
+
] });
|
|
1740
|
+
}
|
|
1741
|
+
|
|
1066
1742
|
// src/components/SingleFileProcessUploader.tsx
|
|
1067
|
-
var
|
|
1068
|
-
var
|
|
1743
|
+
var import_react12 = require("react");
|
|
1744
|
+
var import_jsx_runtime10 = require("react/jsx-runtime");
|
|
1069
1745
|
var SingleFileProcessUploader = ({
|
|
1070
1746
|
getPresignedUrl,
|
|
1071
1747
|
onUploadComplete,
|
|
@@ -1074,28 +1750,28 @@ var SingleFileProcessUploader = ({
|
|
|
1074
1750
|
disabled,
|
|
1075
1751
|
uploadOnDrop = false
|
|
1076
1752
|
}) => {
|
|
1077
|
-
const [selectedFile, setSelectedFile] = (0,
|
|
1078
|
-
const [isDragging, setIsDragging] = (0,
|
|
1079
|
-
const [progress, setProgress] = (0,
|
|
1080
|
-
const [status, setStatus] = (0,
|
|
1081
|
-
const fileInputRef = (0,
|
|
1753
|
+
const [selectedFile, setSelectedFile] = (0, import_react12.useState)(null);
|
|
1754
|
+
const [isDragging, setIsDragging] = (0, import_react12.useState)(false);
|
|
1755
|
+
const [progress, setProgress] = (0, import_react12.useState)(null);
|
|
1756
|
+
const [status, setStatus] = (0, import_react12.useState)("idle");
|
|
1757
|
+
const fileInputRef = (0, import_react12.useRef)(null);
|
|
1082
1758
|
function getErrorMessage(err) {
|
|
1083
1759
|
if (err instanceof Error) return err.message;
|
|
1084
1760
|
if (typeof err === "string") return err;
|
|
1085
1761
|
return "Unknown error during upload.";
|
|
1086
1762
|
}
|
|
1087
1763
|
const isUploading = status === "uploading";
|
|
1088
|
-
const resetUiForNewFile = (0,
|
|
1764
|
+
const resetUiForNewFile = (0, import_react12.useCallback)((file) => {
|
|
1089
1765
|
setSelectedFile(file);
|
|
1090
1766
|
setStatus("idle");
|
|
1091
1767
|
setProgress(null);
|
|
1092
1768
|
}, []);
|
|
1093
|
-
const handleBrowseClick = (0,
|
|
1769
|
+
const handleBrowseClick = (0, import_react12.useCallback)(() => {
|
|
1094
1770
|
if (disabled) return;
|
|
1095
1771
|
if (isUploading) return;
|
|
1096
1772
|
fileInputRef.current?.click();
|
|
1097
1773
|
}, [disabled, isUploading]);
|
|
1098
|
-
const startUpload = (0,
|
|
1774
|
+
const startUpload = (0, import_react12.useCallback)(
|
|
1099
1775
|
async (file) => {
|
|
1100
1776
|
if (disabled) return;
|
|
1101
1777
|
if (isUploading) return;
|
|
@@ -1119,7 +1795,7 @@ var SingleFileProcessUploader = ({
|
|
|
1119
1795
|
},
|
|
1120
1796
|
[disabled, getPresignedUrl, isUploading, onUploadComplete, onUploadFailed]
|
|
1121
1797
|
);
|
|
1122
|
-
const handleFilesSelected = (0,
|
|
1798
|
+
const handleFilesSelected = (0, import_react12.useCallback)(
|
|
1123
1799
|
(files) => {
|
|
1124
1800
|
if (!files || files.length === 0) return;
|
|
1125
1801
|
if (disabled) return;
|
|
@@ -1164,8 +1840,8 @@ var SingleFileProcessUploader = ({
|
|
|
1164
1840
|
disabled ? "opacity-50" : "cursor-pointer",
|
|
1165
1841
|
isDragging ? "bg-body-secondary border-dashed border-2 border-secondary" : "bg-body-trasparent border-dashed border-2"
|
|
1166
1842
|
].join(" ");
|
|
1167
|
-
return /* @__PURE__ */ (0,
|
|
1168
|
-
isUploading ? /* @__PURE__ */ (0,
|
|
1843
|
+
return /* @__PURE__ */ (0, import_jsx_runtime10.jsxs)("div", { className: "d-flex flex-column gap-2", children: [
|
|
1844
|
+
isUploading ? /* @__PURE__ */ (0, import_jsx_runtime10.jsx)("div", { className: "small", children: /* @__PURE__ */ (0, import_jsx_runtime10.jsx)("div", { className: "progress", children: /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(
|
|
1169
1845
|
"div",
|
|
1170
1846
|
{
|
|
1171
1847
|
className: "progress-bar progress-bar-striped progress-bar-animated",
|
|
@@ -1175,7 +1851,7 @@ var SingleFileProcessUploader = ({
|
|
|
1175
1851
|
"aria-valuenow": progress ?? 0,
|
|
1176
1852
|
style: { width: `${progress ?? 0}%` }
|
|
1177
1853
|
}
|
|
1178
|
-
) }) }) : /* @__PURE__ */ (0,
|
|
1854
|
+
) }) }) : /* @__PURE__ */ (0, import_jsx_runtime10.jsxs)(
|
|
1179
1855
|
"div",
|
|
1180
1856
|
{
|
|
1181
1857
|
className: dropzoneClasses,
|
|
@@ -1186,7 +1862,7 @@ var SingleFileProcessUploader = ({
|
|
|
1186
1862
|
role: "button",
|
|
1187
1863
|
"aria-disabled": disabled,
|
|
1188
1864
|
children: [
|
|
1189
|
-
/* @__PURE__ */ (0,
|
|
1865
|
+
/* @__PURE__ */ (0, import_jsx_runtime10.jsx)(
|
|
1190
1866
|
"input",
|
|
1191
1867
|
{
|
|
1192
1868
|
ref: fileInputRef,
|
|
@@ -1197,8 +1873,8 @@ var SingleFileProcessUploader = ({
|
|
|
1197
1873
|
disabled: disabled || isUploading
|
|
1198
1874
|
}
|
|
1199
1875
|
),
|
|
1200
|
-
selectedFile ? /* @__PURE__ */ (0,
|
|
1201
|
-
/* @__PURE__ */ (0,
|
|
1876
|
+
selectedFile ? /* @__PURE__ */ (0, import_jsx_runtime10.jsxs)("div", { className: "small", children: [
|
|
1877
|
+
/* @__PURE__ */ (0, import_jsx_runtime10.jsx)(
|
|
1202
1878
|
"button",
|
|
1203
1879
|
{
|
|
1204
1880
|
type: "button",
|
|
@@ -1207,15 +1883,15 @@ var SingleFileProcessUploader = ({
|
|
|
1207
1883
|
children: "Browse file\u2026"
|
|
1208
1884
|
}
|
|
1209
1885
|
),
|
|
1210
|
-
/* @__PURE__ */ (0,
|
|
1211
|
-
/* @__PURE__ */ (0,
|
|
1886
|
+
/* @__PURE__ */ (0, import_jsx_runtime10.jsxs)("div", { children: [
|
|
1887
|
+
/* @__PURE__ */ (0, import_jsx_runtime10.jsx)("strong", { children: "Selected file:" }),
|
|
1212
1888
|
" ",
|
|
1213
1889
|
selectedFile.name
|
|
1214
1890
|
] }),
|
|
1215
|
-
/* @__PURE__ */ (0,
|
|
1216
|
-
uploadOnDrop && /* @__PURE__ */ (0,
|
|
1217
|
-
] }) : /* @__PURE__ */ (0,
|
|
1218
|
-
/* @__PURE__ */ (0,
|
|
1891
|
+
/* @__PURE__ */ (0, import_jsx_runtime10.jsx)("div", { className: "text-muted", children: "Click here to change file or drag a new one." }),
|
|
1892
|
+
uploadOnDrop && /* @__PURE__ */ (0, import_jsx_runtime10.jsx)("div", { className: "text-muted", children: "Upload starts automatically." })
|
|
1893
|
+
] }) : /* @__PURE__ */ (0, import_jsx_runtime10.jsxs)("div", { className: "small", children: [
|
|
1894
|
+
/* @__PURE__ */ (0, import_jsx_runtime10.jsx)(
|
|
1219
1895
|
"button",
|
|
1220
1896
|
{
|
|
1221
1897
|
type: "button",
|
|
@@ -1224,13 +1900,13 @@ var SingleFileProcessUploader = ({
|
|
|
1224
1900
|
children: "Browse file\u2026"
|
|
1225
1901
|
}
|
|
1226
1902
|
),
|
|
1227
|
-
/* @__PURE__ */ (0,
|
|
1228
|
-
/* @__PURE__ */ (0,
|
|
1903
|
+
/* @__PURE__ */ (0, import_jsx_runtime10.jsx)("div", { children: "Drag & drop a file here" }),
|
|
1904
|
+
/* @__PURE__ */ (0, import_jsx_runtime10.jsx)("div", { className: "text-muted", children: "or click to browse" })
|
|
1229
1905
|
] })
|
|
1230
1906
|
]
|
|
1231
1907
|
}
|
|
1232
1908
|
),
|
|
1233
|
-
!uploadOnDrop && /* @__PURE__ */ (0,
|
|
1909
|
+
!uploadOnDrop && /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(
|
|
1234
1910
|
"button",
|
|
1235
1911
|
{
|
|
1236
1912
|
type: "button",
|
|
@@ -1244,34 +1920,44 @@ var SingleFileProcessUploader = ({
|
|
|
1244
1920
|
};
|
|
1245
1921
|
|
|
1246
1922
|
// src/views/HomeView.tsx
|
|
1923
|
+
var import_react13 = require("react");
|
|
1247
1924
|
var import_authentication_ui = require("@sparkstudio/authentication-ui");
|
|
1248
|
-
var
|
|
1925
|
+
var import_jsx_runtime11 = require("react/jsx-runtime");
|
|
1926
|
+
var CONTAINER_API = "https://lf9zyufpuk.execute-api.us-east-2.amazonaws.com/Prod";
|
|
1927
|
+
var STORAGE_API = "https://iq0gmcn0pd.execute-api.us-east-2.amazonaws.com/Prod";
|
|
1249
1928
|
function HomeView() {
|
|
1250
|
-
return /* @__PURE__ */ (0,
|
|
1929
|
+
return /* @__PURE__ */ (0, import_jsx_runtime11.jsx)(
|
|
1251
1930
|
import_authentication_ui.AuthenticatorProvider,
|
|
1252
1931
|
{
|
|
1253
1932
|
googleClientId: import_authentication_ui.AppSettings.GoogleClientId,
|
|
1254
1933
|
authenticationUrl: import_authentication_ui.AppSettings.AuthenticationUrl,
|
|
1255
1934
|
accountsUrl: import_authentication_ui.AppSettings.AccountsUrl,
|
|
1256
|
-
children: /* @__PURE__ */ (0,
|
|
1935
|
+
children: /* @__PURE__ */ (0, import_jsx_runtime11.jsx)(HomeContent, {})
|
|
1257
1936
|
}
|
|
1258
1937
|
);
|
|
1259
1938
|
}
|
|
1260
1939
|
function HomeContent() {
|
|
1261
1940
|
const { user } = (0, import_authentication_ui.useUser)();
|
|
1941
|
+
const [ids, setIds] = (0, import_react13.useState)([]);
|
|
1942
|
+
const [selectedId, setSelectedId] = (0, import_react13.useState)(void 0);
|
|
1943
|
+
const [selectedFile, setSelectedFile] = (0, import_react13.useState)(null);
|
|
1944
|
+
(0, import_react13.useEffect)(() => {
|
|
1945
|
+
if (selectedId && !ids.includes(selectedId)) {
|
|
1946
|
+
setSelectedId(void 0);
|
|
1947
|
+
setSelectedFile(null);
|
|
1948
|
+
}
|
|
1949
|
+
}, [ids, selectedId]);
|
|
1262
1950
|
async function getPresignedUrlFromApi(file) {
|
|
1263
|
-
const
|
|
1264
|
-
"https://iq0gmcn0pd.execute-api.us-east-2.amazonaws.com/Prod"
|
|
1265
|
-
//"https://localhost:5001"
|
|
1266
|
-
);
|
|
1951
|
+
const sdk = new SparkStudioStorageSDK(STORAGE_API);
|
|
1267
1952
|
const contentType = file.type || "application/octet-stream";
|
|
1268
|
-
|
|
1269
|
-
|
|
1953
|
+
return sdk.s3.GetTemporaryPreSignedUrl(
|
|
1954
|
+
new TemporaryFileDTO({ Name: file.name, ContentType: contentType })
|
|
1955
|
+
);
|
|
1270
1956
|
}
|
|
1271
|
-
return /* @__PURE__ */ (0,
|
|
1272
|
-
/* @__PURE__ */ (0,
|
|
1273
|
-
user
|
|
1274
|
-
/* @__PURE__ */ (0,
|
|
1957
|
+
return /* @__PURE__ */ (0, import_jsx_runtime11.jsxs)(import_jsx_runtime11.Fragment, { children: [
|
|
1958
|
+
/* @__PURE__ */ (0, import_jsx_runtime11.jsx)(import_authentication_ui.UserInfoCard, {}),
|
|
1959
|
+
user ? /* @__PURE__ */ (0, import_jsx_runtime11.jsxs)(import_jsx_runtime11.Fragment, { children: [
|
|
1960
|
+
/* @__PURE__ */ (0, import_jsx_runtime11.jsx)(
|
|
1275
1961
|
SingleFileProcessUploader,
|
|
1276
1962
|
{
|
|
1277
1963
|
uploadOnDrop: true,
|
|
@@ -1282,14 +1968,39 @@ function HomeContent() {
|
|
|
1282
1968
|
}
|
|
1283
1969
|
}
|
|
1284
1970
|
),
|
|
1285
|
-
/* @__PURE__ */ (0,
|
|
1286
|
-
|
|
1971
|
+
/* @__PURE__ */ (0, import_jsx_runtime11.jsx)(
|
|
1972
|
+
ContainerIdGridPanel,
|
|
1287
1973
|
{
|
|
1288
|
-
containerApiBaseUrl:
|
|
1289
|
-
storageApiBaseUrl:
|
|
1974
|
+
containerApiBaseUrl: CONTAINER_API,
|
|
1975
|
+
storageApiBaseUrl: STORAGE_API,
|
|
1976
|
+
containerIds: ids,
|
|
1977
|
+
onContainerIdsChange: setIds,
|
|
1978
|
+
multiple: true,
|
|
1979
|
+
accept: "*/*",
|
|
1980
|
+
selectedId,
|
|
1981
|
+
onSelect: (file) => {
|
|
1982
|
+
setSelectedId(file.Id);
|
|
1983
|
+
setSelectedFile(file);
|
|
1984
|
+
}
|
|
1290
1985
|
}
|
|
1291
|
-
)
|
|
1292
|
-
|
|
1986
|
+
),
|
|
1987
|
+
selectedFile ? /* @__PURE__ */ (0, import_jsx_runtime11.jsxs)(
|
|
1988
|
+
"div",
|
|
1989
|
+
{
|
|
1990
|
+
style: {
|
|
1991
|
+
marginTop: 12,
|
|
1992
|
+
padding: 12,
|
|
1993
|
+
border: "1px solid rgba(0,0,0,0.12)",
|
|
1994
|
+
borderRadius: 12
|
|
1995
|
+
},
|
|
1996
|
+
children: [
|
|
1997
|
+
/* @__PURE__ */ (0, import_jsx_runtime11.jsx)("div", { style: { fontWeight: 700 }, children: "Selected file" }),
|
|
1998
|
+
/* @__PURE__ */ (0, import_jsx_runtime11.jsx)("div", { children: selectedFile.Name ?? "(no name)" }),
|
|
1999
|
+
/* @__PURE__ */ (0, import_jsx_runtime11.jsx)("div", { style: { fontSize: 12, opacity: 0.7 }, children: selectedFile.PublicUrl ?? "(no public url)" })
|
|
2000
|
+
]
|
|
2001
|
+
}
|
|
2002
|
+
) : null
|
|
2003
|
+
] }) : null
|
|
1293
2004
|
] });
|
|
1294
2005
|
}
|
|
1295
2006
|
// Annotate the CommonJS export names for ESM import in node:
|
|
@@ -1297,9 +2008,13 @@ function HomeContent() {
|
|
|
1297
2008
|
AWSPresignedUrlDTO,
|
|
1298
2009
|
Container,
|
|
1299
2010
|
ContainerDTO,
|
|
2011
|
+
ContainerIdGridPanel,
|
|
1300
2012
|
ContainerType,
|
|
1301
2013
|
ContainerUploadPanel,
|
|
1302
2014
|
DesktopFileIcon,
|
|
2015
|
+
FileGridUploadPanel,
|
|
2016
|
+
FileIconCard,
|
|
2017
|
+
FileIconGrid,
|
|
1303
2018
|
Home,
|
|
1304
2019
|
HomeView,
|
|
1305
2020
|
S3,
|