@sparkstudio/storage-ui 1.0.12 → 1.0.13
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 +404 -172
- package/dist/index.d.cts +75 -16
- package/dist/index.d.ts +75 -16
- package/dist/index.js +402 -172
- package/package.json +1 -1
package/dist/index.cjs
CHANGED
|
@@ -24,11 +24,18 @@ __export(index_exports, {
|
|
|
24
24
|
Container: () => Container,
|
|
25
25
|
ContainerDTO: () => ContainerDTO,
|
|
26
26
|
ContainerType: () => ContainerType,
|
|
27
|
+
ContainerUploadPanel: () => ContainerUploadPanel,
|
|
27
28
|
Home: () => Home,
|
|
28
29
|
HomeView: () => HomeView,
|
|
29
30
|
S3: () => S3,
|
|
30
31
|
SparkStudioStorageSDK: () => SparkStudioStorageSDK,
|
|
31
|
-
UploadContainer: () => UploadContainer
|
|
32
|
+
UploadContainer: () => UploadContainer,
|
|
33
|
+
UploadDropzone: () => UploadDropzone,
|
|
34
|
+
UploadFilePicker: () => UploadFilePicker,
|
|
35
|
+
UploadFileToS3: () => UploadFileToS3,
|
|
36
|
+
UploadProgressList: () => UploadProgressList,
|
|
37
|
+
UseContainers: () => UseContainers,
|
|
38
|
+
UseUploadManager: () => UseUploadManager
|
|
32
39
|
});
|
|
33
40
|
module.exports = __toCommonJS(index_exports);
|
|
34
41
|
|
|
@@ -240,75 +247,246 @@ var ContainerType = /* @__PURE__ */ ((ContainerType2) => {
|
|
|
240
247
|
return ContainerType2;
|
|
241
248
|
})(ContainerType || {});
|
|
242
249
|
|
|
250
|
+
// src/components/ContainerUploadPanel.tsx
|
|
251
|
+
var import_react7 = require("react");
|
|
252
|
+
|
|
243
253
|
// src/components/UploadContainer.tsx
|
|
254
|
+
var import_react5 = require("react");
|
|
255
|
+
|
|
256
|
+
// src/components/UploadDropzone.tsx
|
|
244
257
|
var import_react = require("react");
|
|
245
258
|
var import_jsx_runtime = require("react/jsx-runtime");
|
|
246
|
-
var
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
259
|
+
var UploadDropzone = ({
|
|
260
|
+
isDragging,
|
|
261
|
+
onDragOver,
|
|
262
|
+
onDragLeave,
|
|
263
|
+
onDrop
|
|
264
|
+
}) => {
|
|
265
|
+
return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(
|
|
266
|
+
"div",
|
|
267
|
+
{
|
|
268
|
+
className: "border rounded-3 p-4 text-center mb-3 d-flex flex-column align-items-center justify-content-center " + (isDragging ? "bg-light border-primary" : "border-secondary border-dashed"),
|
|
269
|
+
style: { cursor: "pointer", minHeight: "140px" },
|
|
270
|
+
onDragOver,
|
|
271
|
+
onDragLeave,
|
|
272
|
+
onDrop,
|
|
273
|
+
children: [
|
|
274
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("i", { className: "bi bi-cloud-arrow-up fs-1 mb-2" }),
|
|
275
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("p", { className: "mb-2", children: "Drop files here" }),
|
|
276
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("small", { className: "text-muted", children: "or click the button below" })
|
|
277
|
+
]
|
|
278
|
+
}
|
|
279
|
+
);
|
|
280
|
+
};
|
|
281
|
+
|
|
282
|
+
// src/components/UploadFilePicker.tsx
|
|
283
|
+
var import_react2 = require("react");
|
|
284
|
+
var import_jsx_runtime2 = require("react/jsx-runtime");
|
|
285
|
+
var UploadFilePicker = ({
|
|
286
|
+
multiple,
|
|
250
287
|
accept,
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
getPresignedUrl,
|
|
254
|
-
onUploadComplete,
|
|
255
|
-
onUploadError
|
|
288
|
+
fileNames,
|
|
289
|
+
onFileChange
|
|
256
290
|
}) => {
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
uploadFile(upload);
|
|
271
|
-
});
|
|
272
|
-
};
|
|
273
|
-
const uploadFile = async (upload) => {
|
|
274
|
-
setUploads(
|
|
275
|
-
(prev) => prev.map(
|
|
276
|
-
(u) => u.id === upload.id ? { ...u, status: "uploading", progress: 0 } : u
|
|
291
|
+
return /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("div", { className: "d-flex gap-2 align-items-center", children: [
|
|
292
|
+
/* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("label", { className: "btn btn-primary mb-0", children: [
|
|
293
|
+
/* @__PURE__ */ (0, import_jsx_runtime2.jsx)("i", { className: "bi bi-folder2-open me-2" }),
|
|
294
|
+
"Browse files",
|
|
295
|
+
/* @__PURE__ */ (0, import_jsx_runtime2.jsx)(
|
|
296
|
+
"input",
|
|
297
|
+
{
|
|
298
|
+
type: "file",
|
|
299
|
+
className: "d-none",
|
|
300
|
+
multiple,
|
|
301
|
+
accept,
|
|
302
|
+
onChange: onFileChange
|
|
303
|
+
}
|
|
277
304
|
)
|
|
278
|
-
)
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
305
|
+
] }),
|
|
306
|
+
fileNames.length > 0 && /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("div", { className: "flex-grow-1", children: [
|
|
307
|
+
/* @__PURE__ */ (0, import_jsx_runtime2.jsx)("div", { className: "small text-muted", children: "Selected:" }),
|
|
308
|
+
/* @__PURE__ */ (0, import_jsx_runtime2.jsx)("ul", { className: "mb-0 small", children: fileNames.map((name) => /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("li", { children: name }, name)) })
|
|
309
|
+
] })
|
|
310
|
+
] });
|
|
311
|
+
};
|
|
312
|
+
|
|
313
|
+
// src/components/UploadProgressList.tsx
|
|
314
|
+
var import_react3 = require("react");
|
|
315
|
+
var import_jsx_runtime3 = require("react/jsx-runtime");
|
|
316
|
+
var UploadProgressList = ({
|
|
317
|
+
uploads
|
|
318
|
+
}) => {
|
|
319
|
+
if (uploads.length === 0) return null;
|
|
320
|
+
return /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("div", { className: "mt-3", children: uploads.map((u) => /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("div", { className: "mb-2", children: [
|
|
321
|
+
/* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("div", { className: "d-flex justify-content-between small mb-1", children: [
|
|
322
|
+
/* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("span", { children: [
|
|
323
|
+
u.file.name,
|
|
324
|
+
" - ",
|
|
325
|
+
u?.publicUrl ?? ""
|
|
326
|
+
] }),
|
|
327
|
+
/* @__PURE__ */ (0, import_jsx_runtime3.jsx)("span", { children: u.status === "success" ? "Completed" : u.status === "error" ? "Error" : `${u.progress}%` })
|
|
328
|
+
] }),
|
|
329
|
+
/* @__PURE__ */ (0, import_jsx_runtime3.jsx)("div", { className: "progress", style: { height: "6px" }, children: /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
|
|
330
|
+
"div",
|
|
331
|
+
{
|
|
332
|
+
className: "progress-bar " + (u.status === "success" ? "bg-success" : u.status === "error" ? "bg-danger" : ""),
|
|
333
|
+
role: "progressbar",
|
|
334
|
+
style: { width: `${u.progress}%` },
|
|
335
|
+
"aria-valuenow": u.progress,
|
|
336
|
+
"aria-valuemin": 0,
|
|
337
|
+
"aria-valuemax": 100
|
|
282
338
|
}
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
339
|
+
) }),
|
|
340
|
+
u.status === "error" && /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("div", { className: "text-danger small mt-1", children: u.error ?? "Upload failed" })
|
|
341
|
+
] }, u.id)) });
|
|
342
|
+
};
|
|
343
|
+
|
|
344
|
+
// src/hooks/UseUploadManager.ts
|
|
345
|
+
var import_react4 = require("react");
|
|
346
|
+
|
|
347
|
+
// src/engines/UploadFileToS3.ts
|
|
348
|
+
async function UploadFileToS3(file, presignedUrl, onProgress) {
|
|
349
|
+
return new Promise((resolve, reject) => {
|
|
350
|
+
const xhr = new XMLHttpRequest();
|
|
351
|
+
xhr.open("PUT", presignedUrl);
|
|
352
|
+
xhr.upload.onprogress = (event) => {
|
|
353
|
+
if (!event.lengthComputable) return;
|
|
354
|
+
const percent = Math.round(event.loaded / event.total * 100);
|
|
355
|
+
onProgress(percent);
|
|
356
|
+
};
|
|
357
|
+
xhr.onload = () => {
|
|
358
|
+
if (xhr.status >= 200 && xhr.status < 300) {
|
|
359
|
+
onProgress(100);
|
|
360
|
+
resolve();
|
|
361
|
+
} else {
|
|
362
|
+
reject(
|
|
363
|
+
new Error(
|
|
364
|
+
`S3 upload failed with status ${xhr.status}: ${xhr.statusText}`
|
|
289
365
|
)
|
|
290
366
|
);
|
|
291
|
-
});
|
|
292
|
-
const fileUrl = url.split("?")[0];
|
|
293
|
-
setUploads(
|
|
294
|
-
(prev) => prev.map(
|
|
295
|
-
(u) => u.id === upload.id ? { ...u, status: "success", progress: 100, s3Url: fileUrl, publicUrl: presignedUrl.PublicUrl } : u
|
|
296
|
-
)
|
|
297
|
-
);
|
|
298
|
-
onUploadComplete?.(upload.file, fileUrl);
|
|
299
|
-
} catch (err) {
|
|
300
|
-
let message = "Upload failed";
|
|
301
|
-
if (err instanceof Error) {
|
|
302
|
-
message = err.message;
|
|
303
367
|
}
|
|
368
|
+
};
|
|
369
|
+
xhr.onerror = () => {
|
|
370
|
+
reject(new Error("Network error while uploading to S3"));
|
|
371
|
+
};
|
|
372
|
+
xhr.setRequestHeader(
|
|
373
|
+
"Content-Type",
|
|
374
|
+
file.type || "application/octet-stream"
|
|
375
|
+
);
|
|
376
|
+
xhr.send(file);
|
|
377
|
+
});
|
|
378
|
+
}
|
|
379
|
+
|
|
380
|
+
// src/hooks/UseUploadManager.ts
|
|
381
|
+
function UseUploadManager({
|
|
382
|
+
autoUpload = false,
|
|
383
|
+
getPresignedUrl,
|
|
384
|
+
onUploadComplete,
|
|
385
|
+
onUploadError
|
|
386
|
+
}) {
|
|
387
|
+
const [uploads, setUploads] = (0, import_react4.useState)([]);
|
|
388
|
+
const createUploadStates = (files) => Array.from(files).map((file) => ({
|
|
389
|
+
id: `${file.name}-${file.size}-${file.lastModified}-${Math.random()}`,
|
|
390
|
+
file,
|
|
391
|
+
progress: 0,
|
|
392
|
+
status: "pending"
|
|
393
|
+
}));
|
|
394
|
+
const uploadFile = (0, import_react4.useCallback)(
|
|
395
|
+
async (upload) => {
|
|
304
396
|
setUploads(
|
|
305
397
|
(prev) => prev.map(
|
|
306
|
-
(u) => u.id === upload.id ? { ...u, status: "
|
|
398
|
+
(u) => u.id === upload.id ? { ...u, status: "uploading", progress: 0 } : u
|
|
307
399
|
)
|
|
308
400
|
);
|
|
309
|
-
|
|
310
|
-
|
|
401
|
+
try {
|
|
402
|
+
if (!getPresignedUrl) {
|
|
403
|
+
throw new Error("getPresignedUrl is not provided.");
|
|
404
|
+
}
|
|
405
|
+
const presignedUrl = await getPresignedUrl(
|
|
406
|
+
upload.file
|
|
407
|
+
);
|
|
408
|
+
const url = presignedUrl?.PresignedUrl ?? "";
|
|
409
|
+
await UploadFileToS3(upload.file, url, (progress) => {
|
|
410
|
+
setUploads(
|
|
411
|
+
(prev) => prev.map(
|
|
412
|
+
(u) => u.id === upload.id ? { ...u, progress } : u
|
|
413
|
+
)
|
|
414
|
+
);
|
|
415
|
+
});
|
|
416
|
+
const fileUrl = url.split("?")[0];
|
|
417
|
+
setUploads(
|
|
418
|
+
(prev) => prev.map(
|
|
419
|
+
(u) => u.id === upload.id ? {
|
|
420
|
+
...u,
|
|
421
|
+
status: "success",
|
|
422
|
+
progress: 100,
|
|
423
|
+
s3Url: fileUrl,
|
|
424
|
+
// assumes your DTO has PublicUrl
|
|
425
|
+
publicUrl: presignedUrl.PublicUrl
|
|
426
|
+
} : u
|
|
427
|
+
)
|
|
428
|
+
);
|
|
429
|
+
onUploadComplete?.(upload.file, fileUrl);
|
|
430
|
+
} catch (err) {
|
|
431
|
+
let message = "Upload failed";
|
|
432
|
+
if (err instanceof Error) {
|
|
433
|
+
message = err.message;
|
|
434
|
+
}
|
|
435
|
+
setUploads(
|
|
436
|
+
(prev) => prev.map(
|
|
437
|
+
(u) => u.id === upload.id ? { ...u, status: "error", error: message } : u
|
|
438
|
+
)
|
|
439
|
+
);
|
|
440
|
+
onUploadError?.(
|
|
441
|
+
upload.file,
|
|
442
|
+
err instanceof Error ? err : new Error(message)
|
|
443
|
+
);
|
|
444
|
+
}
|
|
445
|
+
},
|
|
446
|
+
[getPresignedUrl, onUploadComplete, onUploadError]
|
|
447
|
+
);
|
|
448
|
+
const startUploadsIfNeeded = (0, import_react4.useCallback)(
|
|
449
|
+
(files) => {
|
|
450
|
+
if (!autoUpload || !getPresignedUrl) return;
|
|
451
|
+
const newUploads = createUploadStates(files);
|
|
452
|
+
setUploads((prev) => [...prev, ...newUploads]);
|
|
453
|
+
newUploads.forEach((upload) => {
|
|
454
|
+
void uploadFile(upload);
|
|
455
|
+
});
|
|
456
|
+
},
|
|
457
|
+
[autoUpload, getPresignedUrl, uploadFile]
|
|
458
|
+
);
|
|
459
|
+
const resetUploads = (0, import_react4.useCallback)(() => setUploads([]), []);
|
|
460
|
+
return {
|
|
461
|
+
uploads,
|
|
462
|
+
startUploadsIfNeeded,
|
|
463
|
+
resetUploads
|
|
311
464
|
};
|
|
465
|
+
}
|
|
466
|
+
|
|
467
|
+
// src/components/UploadContainer.tsx
|
|
468
|
+
var import_jsx_runtime4 = require("react/jsx-runtime");
|
|
469
|
+
var UploadContainer = ({
|
|
470
|
+
title = "Upload files",
|
|
471
|
+
description = "Drag and drop files here, or click the button to browse.",
|
|
472
|
+
multiple = true,
|
|
473
|
+
accept,
|
|
474
|
+
onFilesSelected,
|
|
475
|
+
existingFiles = [],
|
|
476
|
+
onExistingFileClick,
|
|
477
|
+
autoUpload = false,
|
|
478
|
+
getPresignedUrl,
|
|
479
|
+
onUploadComplete,
|
|
480
|
+
onUploadError
|
|
481
|
+
}) => {
|
|
482
|
+
const [isDragging, setIsDragging] = (0, import_react5.useState)(false);
|
|
483
|
+
const [fileNames, setFileNames] = (0, import_react5.useState)([]);
|
|
484
|
+
const { uploads, startUploadsIfNeeded } = UseUploadManager({
|
|
485
|
+
autoUpload,
|
|
486
|
+
getPresignedUrl,
|
|
487
|
+
onUploadComplete,
|
|
488
|
+
onUploadError
|
|
489
|
+
});
|
|
312
490
|
const handleDragOver = (e) => {
|
|
313
491
|
e.preventDefault();
|
|
314
492
|
setIsDragging(true);
|
|
@@ -333,151 +511,205 @@ var UploadContainer = ({
|
|
|
333
511
|
onFilesSelected?.(files);
|
|
334
512
|
startUploadsIfNeeded(files);
|
|
335
513
|
};
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
|
|
514
|
+
const handleExistingFileClickInternal = (file) => {
|
|
515
|
+
if (onExistingFileClick) {
|
|
516
|
+
onExistingFileClick(file);
|
|
517
|
+
return;
|
|
518
|
+
}
|
|
519
|
+
const a = document.createElement("a");
|
|
520
|
+
a.download = file.Name ?? "";
|
|
521
|
+
a.target = "_blank";
|
|
522
|
+
a.rel = "noopener noreferrer";
|
|
523
|
+
document.body.appendChild(a);
|
|
524
|
+
a.click();
|
|
525
|
+
document.body.removeChild(a);
|
|
526
|
+
};
|
|
527
|
+
return /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)(import_jsx_runtime4.Fragment, { children: [
|
|
528
|
+
/* @__PURE__ */ (0, import_jsx_runtime4.jsx)("h5", { className: "card-title mb-2", children: title }),
|
|
529
|
+
/* @__PURE__ */ (0, import_jsx_runtime4.jsx)("p", { className: "card-text text-muted", children: description }),
|
|
530
|
+
existingFiles.length > 0 && /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)("div", { className: "mb-3", children: [
|
|
531
|
+
/* @__PURE__ */ (0, import_jsx_runtime4.jsx)("h6", { className: "mb-2", children: "Existing files" }),
|
|
532
|
+
/* @__PURE__ */ (0, import_jsx_runtime4.jsx)("ul", { className: "list-group", children: existingFiles.map((file) => /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)(
|
|
533
|
+
"li",
|
|
534
|
+
{
|
|
535
|
+
className: "list-group-item d-flex justify-content-between align-items-center",
|
|
536
|
+
style: { cursor: "pointer" },
|
|
537
|
+
onClick: () => handleExistingFileClickInternal(file),
|
|
538
|
+
children: [
|
|
539
|
+
/* @__PURE__ */ (0, import_jsx_runtime4.jsx)("span", { children: file.Name }),
|
|
540
|
+
typeof file.FileSize === "number" && /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)("small", { className: "text-muted", children: [
|
|
541
|
+
(file.FileSize / 1024).toFixed(1),
|
|
542
|
+
" KB"
|
|
543
|
+
] })
|
|
544
|
+
]
|
|
545
|
+
},
|
|
546
|
+
file.Id
|
|
547
|
+
)) })
|
|
548
|
+
] }),
|
|
549
|
+
/* @__PURE__ */ (0, import_jsx_runtime4.jsx)(
|
|
550
|
+
UploadDropzone,
|
|
341
551
|
{
|
|
342
|
-
|
|
343
|
-
style: { cursor: "pointer", minHeight: "140px" },
|
|
552
|
+
isDragging,
|
|
344
553
|
onDragOver: handleDragOver,
|
|
345
554
|
onDragLeave: handleDragLeave,
|
|
346
|
-
onDrop: handleDrop
|
|
347
|
-
children: [
|
|
348
|
-
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("i", { className: "bi bi-cloud-arrow-up fs-1 mb-2" }),
|
|
349
|
-
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("p", { className: "mb-2", children: "Drop files here" }),
|
|
350
|
-
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("small", { className: "text-muted", children: "or click the button below" })
|
|
351
|
-
]
|
|
555
|
+
onDrop: handleDrop
|
|
352
556
|
}
|
|
353
557
|
),
|
|
354
|
-
/* @__PURE__ */ (0,
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
|
|
360
|
-
|
|
361
|
-
type: "file",
|
|
362
|
-
className: "d-none",
|
|
363
|
-
multiple,
|
|
364
|
-
accept,
|
|
365
|
-
onChange: handleFileChange
|
|
366
|
-
}
|
|
367
|
-
)
|
|
368
|
-
] }),
|
|
369
|
-
fileNames.length > 0 && /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "flex-grow-1", children: [
|
|
370
|
-
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { className: "small text-muted", children: "Selected:" }),
|
|
371
|
-
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("ul", { className: "mb-0 small", children: fileNames.map((name) => /* @__PURE__ */ (0, import_jsx_runtime.jsx)("li", { children: name }, name)) })
|
|
372
|
-
] })
|
|
373
|
-
] }),
|
|
374
|
-
uploads.length > 0 && /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { className: "mt-3", children: uploads.map((u) => /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "mb-2", children: [
|
|
375
|
-
/* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "d-flex justify-content-between small mb-1", children: [
|
|
376
|
-
/* @__PURE__ */ (0, import_jsx_runtime.jsxs)("span", { children: [
|
|
377
|
-
u.file.name,
|
|
378
|
-
" - ",
|
|
379
|
-
u?.publicUrl ?? ""
|
|
380
|
-
] }),
|
|
381
|
-
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { children: u.status === "success" ? "Completed" : u.status === "error" ? "Error" : `${u.progress}%` })
|
|
382
|
-
] }),
|
|
383
|
-
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { className: "progress", style: { height: "6px" }, children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
|
|
384
|
-
"div",
|
|
385
|
-
{
|
|
386
|
-
className: "progress-bar " + (u.status === "success" ? "bg-success" : u.status === "error" ? "bg-danger" : ""),
|
|
387
|
-
role: "progressbar",
|
|
388
|
-
style: { width: `${u.progress}%` },
|
|
389
|
-
"aria-valuenow": u.progress,
|
|
390
|
-
"aria-valuemin": 0,
|
|
391
|
-
"aria-valuemax": 100
|
|
392
|
-
}
|
|
393
|
-
) }),
|
|
394
|
-
u.status === "error" && /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { className: "text-danger small mt-1", children: u.error ?? "Upload failed" })
|
|
395
|
-
] }, u.id)) })
|
|
396
|
-
] }) }) });
|
|
397
|
-
};
|
|
398
|
-
async function uploadFileToS3(file, presignedUrl, onProgress) {
|
|
399
|
-
return new Promise((resolve, reject) => {
|
|
400
|
-
const xhr = new XMLHttpRequest();
|
|
401
|
-
xhr.open("PUT", presignedUrl);
|
|
402
|
-
xhr.upload.onprogress = (event) => {
|
|
403
|
-
if (!event.lengthComputable) return;
|
|
404
|
-
const percent = Math.round(event.loaded / event.total * 100);
|
|
405
|
-
onProgress(percent);
|
|
406
|
-
};
|
|
407
|
-
xhr.onload = () => {
|
|
408
|
-
if (xhr.status >= 200 && xhr.status < 300) {
|
|
409
|
-
onProgress(100);
|
|
410
|
-
resolve();
|
|
411
|
-
} else {
|
|
412
|
-
reject(
|
|
413
|
-
new Error(`S3 upload failed with status ${xhr.status}: ${xhr.statusText}`)
|
|
414
|
-
);
|
|
558
|
+
/* @__PURE__ */ (0, import_jsx_runtime4.jsx)(
|
|
559
|
+
UploadFilePicker,
|
|
560
|
+
{
|
|
561
|
+
multiple,
|
|
562
|
+
accept,
|
|
563
|
+
fileNames,
|
|
564
|
+
onFileChange: handleFileChange
|
|
415
565
|
}
|
|
416
|
-
|
|
417
|
-
|
|
418
|
-
|
|
419
|
-
|
|
420
|
-
|
|
421
|
-
|
|
422
|
-
|
|
423
|
-
|
|
424
|
-
|
|
425
|
-
|
|
566
|
+
),
|
|
567
|
+
/* @__PURE__ */ (0, import_jsx_runtime4.jsx)(UploadProgressList, { uploads })
|
|
568
|
+
] });
|
|
569
|
+
};
|
|
570
|
+
|
|
571
|
+
// src/hooks/UseContainers.ts
|
|
572
|
+
var import_react6 = require("react");
|
|
573
|
+
function UseContainers({ apiBaseUrl, parentId }) {
|
|
574
|
+
const [containers, setContainers] = (0, import_react6.useState)([]);
|
|
575
|
+
const [loading, setLoading] = (0, import_react6.useState)(false);
|
|
576
|
+
const [error, setError] = (0, import_react6.useState)(null);
|
|
577
|
+
const load = async () => {
|
|
578
|
+
setLoading(true);
|
|
579
|
+
setError(null);
|
|
580
|
+
try {
|
|
581
|
+
const sdkDb = new SparkStudioStorageSDK(
|
|
582
|
+
"https://lf9zyufpuk.execute-api.us-east-2.amazonaws.com/Prod"
|
|
583
|
+
);
|
|
584
|
+
const result = await sdkDb.container.ReadRootContainers();
|
|
585
|
+
setContainers(result);
|
|
586
|
+
} catch (err) {
|
|
587
|
+
setError(err);
|
|
588
|
+
} finally {
|
|
589
|
+
setLoading(false);
|
|
590
|
+
}
|
|
591
|
+
};
|
|
592
|
+
(0, import_react6.useEffect)(() => {
|
|
593
|
+
void load();
|
|
594
|
+
}, [apiBaseUrl, parentId]);
|
|
595
|
+
return {
|
|
596
|
+
containers,
|
|
597
|
+
loading,
|
|
598
|
+
error,
|
|
599
|
+
reload: load
|
|
600
|
+
};
|
|
426
601
|
}
|
|
427
602
|
|
|
428
|
-
// src/
|
|
429
|
-
var
|
|
430
|
-
var
|
|
431
|
-
|
|
432
|
-
|
|
433
|
-
|
|
434
|
-
|
|
603
|
+
// src/components/ContainerUploadPanel.tsx
|
|
604
|
+
var import_jsx_runtime5 = require("react/jsx-runtime");
|
|
605
|
+
var ContainerUploadPanel = ({
|
|
606
|
+
containerApiBaseUrl,
|
|
607
|
+
parentContainerId,
|
|
608
|
+
title = "Upload files",
|
|
609
|
+
description = "Drop files to upload. Existing files are listed below."
|
|
610
|
+
}) => {
|
|
611
|
+
const { containers, loading, error, reload } = UseContainers({
|
|
612
|
+
apiBaseUrl: containerApiBaseUrl,
|
|
613
|
+
parentId: parentContainerId
|
|
614
|
+
});
|
|
435
615
|
const getPresignedUrl = async (file) => {
|
|
436
|
-
const sdkDb = new SparkStudioStorageSDK(
|
|
437
|
-
|
|
438
|
-
|
|
616
|
+
const sdkDb = new SparkStudioStorageSDK(
|
|
617
|
+
"https://lf9zyufpuk.execute-api.us-east-2.amazonaws.com/Prod"
|
|
618
|
+
);
|
|
619
|
+
const sdkS3 = new SparkStudioStorageSDK(
|
|
620
|
+
"https://iq0gmcn0pd.execute-api.us-east-2.amazonaws.com/Prod"
|
|
621
|
+
);
|
|
622
|
+
const containerDTO = await sdkDb.container.CreateFileContainer(
|
|
623
|
+
file.name,
|
|
624
|
+
file.size,
|
|
625
|
+
encodeURIComponent(file.type)
|
|
626
|
+
);
|
|
439
627
|
const resultS3 = await sdkS3.s3.GetPreSignedUrl(containerDTO);
|
|
440
628
|
return resultS3;
|
|
441
629
|
};
|
|
442
|
-
|
|
630
|
+
const handleUploadComplete = async (file, s3Url) => {
|
|
631
|
+
console.log("Upload complete:", file.name, s3Url);
|
|
632
|
+
await reload();
|
|
633
|
+
};
|
|
634
|
+
const handleUploadError = (file, err) => {
|
|
635
|
+
console.error("Upload failed:", file.name, err);
|
|
636
|
+
};
|
|
637
|
+
const handleExistingFileClick = (file) => {
|
|
638
|
+
const downloadUrl = `${containerApiBaseUrl}/api/Container/Download/${file.Id}`;
|
|
639
|
+
const a = document.createElement("a");
|
|
640
|
+
a.href = downloadUrl;
|
|
641
|
+
a.download = file.Name ?? "";
|
|
642
|
+
a.target = "_blank";
|
|
643
|
+
a.rel = "noopener noreferrer";
|
|
644
|
+
document.body.appendChild(a);
|
|
645
|
+
a.click();
|
|
646
|
+
document.body.removeChild(a);
|
|
647
|
+
};
|
|
648
|
+
return /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)("div", { children: [
|
|
649
|
+
loading && /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("p", { children: "Loading existing files\u2026" }),
|
|
650
|
+
error && /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)("p", { className: "text-danger", children: [
|
|
651
|
+
"Failed to load containers: ",
|
|
652
|
+
error.message
|
|
653
|
+
] }),
|
|
654
|
+
/* @__PURE__ */ (0, import_jsx_runtime5.jsx)(
|
|
655
|
+
UploadContainer,
|
|
656
|
+
{
|
|
657
|
+
title,
|
|
658
|
+
description,
|
|
659
|
+
existingFiles: containers,
|
|
660
|
+
onExistingFileClick: handleExistingFileClick,
|
|
661
|
+
autoUpload: true,
|
|
662
|
+
getPresignedUrl,
|
|
663
|
+
onUploadComplete: handleUploadComplete,
|
|
664
|
+
onUploadError: handleUploadError
|
|
665
|
+
}
|
|
666
|
+
)
|
|
667
|
+
] });
|
|
668
|
+
};
|
|
669
|
+
|
|
670
|
+
// src/views/HomeView.tsx
|
|
671
|
+
var import_authentication_ui = require("@sparkstudio/authentication-ui");
|
|
672
|
+
var import_jsx_runtime6 = require("react/jsx-runtime");
|
|
673
|
+
function HomeView() {
|
|
674
|
+
return /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(
|
|
443
675
|
import_authentication_ui.AuthenticatorProvider,
|
|
444
676
|
{
|
|
445
677
|
googleClientId: import_authentication_ui.AppSettings.GoogleClientId,
|
|
446
678
|
authenticationUrl: import_authentication_ui.AppSettings.AuthenticationUrl,
|
|
447
679
|
accountsUrl: import_authentication_ui.AppSettings.AccountsUrl,
|
|
448
|
-
|
|
449
|
-
children: [
|
|
450
|
-
/* @__PURE__ */ (0, import_jsx_runtime2.jsx)(import_authentication_ui.UserInfoCard, {}),
|
|
451
|
-
/* @__PURE__ */ (0, import_jsx_runtime2.jsx)(
|
|
452
|
-
UploadContainer,
|
|
453
|
-
{
|
|
454
|
-
title: "Upload your files to S3",
|
|
455
|
-
description: "Drag & drop or browse files. Each file will upload with its own progress bar.",
|
|
456
|
-
multiple: true,
|
|
457
|
-
accept: "*/*",
|
|
458
|
-
autoUpload: true,
|
|
459
|
-
getPresignedUrl,
|
|
460
|
-
onUploadComplete: (file, s3Url) => {
|
|
461
|
-
console.log("Uploaded", file.name, "to", s3Url);
|
|
462
|
-
},
|
|
463
|
-
onUploadError: (file, error) => {
|
|
464
|
-
console.error("Failed to upload", file.name, error);
|
|
465
|
-
}
|
|
466
|
-
}
|
|
467
|
-
)
|
|
468
|
-
]
|
|
680
|
+
children: /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(HomeContent, {})
|
|
469
681
|
}
|
|
470
682
|
);
|
|
471
683
|
}
|
|
684
|
+
function HomeContent() {
|
|
685
|
+
const { user } = (0, import_authentication_ui.useUser)();
|
|
686
|
+
return /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)(import_jsx_runtime6.Fragment, { children: [
|
|
687
|
+
/* @__PURE__ */ (0, import_jsx_runtime6.jsx)(import_authentication_ui.UserInfoCard, {}),
|
|
688
|
+
user && /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(
|
|
689
|
+
ContainerUploadPanel,
|
|
690
|
+
{
|
|
691
|
+
containerApiBaseUrl: "https://lf9zyufpuk.execute-api.us-east-2.amazonaws.com/Prod",
|
|
692
|
+
storageApiBaseUrl: "https://iq0gmcn0pd.execute-api.us-east-2.amazonaws.com/Prod"
|
|
693
|
+
}
|
|
694
|
+
)
|
|
695
|
+
] });
|
|
696
|
+
}
|
|
472
697
|
// Annotate the CommonJS export names for ESM import in node:
|
|
473
698
|
0 && (module.exports = {
|
|
474
699
|
AWSPresignedUrlDTO,
|
|
475
700
|
Container,
|
|
476
701
|
ContainerDTO,
|
|
477
702
|
ContainerType,
|
|
703
|
+
ContainerUploadPanel,
|
|
478
704
|
Home,
|
|
479
705
|
HomeView,
|
|
480
706
|
S3,
|
|
481
707
|
SparkStudioStorageSDK,
|
|
482
|
-
UploadContainer
|
|
708
|
+
UploadContainer,
|
|
709
|
+
UploadDropzone,
|
|
710
|
+
UploadFilePicker,
|
|
711
|
+
UploadFileToS3,
|
|
712
|
+
UploadProgressList,
|
|
713
|
+
UseContainers,
|
|
714
|
+
UseUploadManager
|
|
483
715
|
});
|