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