@sparkstudio/storage-ui 1.0.11 → 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 +437 -193
- package/dist/index.d.cts +101 -33
- package/dist/index.d.ts +101 -33
- package/dist/index.js +434 -193
- package/package.json +1 -1
package/dist/index.cjs
CHANGED
|
@@ -24,10 +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,
|
|
30
|
+
S3: () => S3,
|
|
29
31
|
SparkStudioStorageSDK: () => SparkStudioStorageSDK,
|
|
30
|
-
UploadContainer: () => UploadContainer
|
|
32
|
+
UploadContainer: () => UploadContainer,
|
|
33
|
+
UploadDropzone: () => UploadDropzone,
|
|
34
|
+
UploadFilePicker: () => UploadFilePicker,
|
|
35
|
+
UploadFileToS3: () => UploadFileToS3,
|
|
36
|
+
UploadProgressList: () => UploadProgressList,
|
|
37
|
+
UseContainers: () => UseContainers,
|
|
38
|
+
UseUploadManager: () => UseUploadManager
|
|
31
39
|
});
|
|
32
40
|
module.exports = __toCommonJS(index_exports);
|
|
33
41
|
|
|
@@ -109,23 +117,22 @@ var Container = class {
|
|
|
109
117
|
if (!response.ok) throw new Error(await response.text());
|
|
110
118
|
return await response.json();
|
|
111
119
|
}
|
|
112
|
-
async
|
|
113
|
-
const url = `${this.baseUrl}/api/Container/
|
|
120
|
+
async DeleteContainer(id) {
|
|
121
|
+
const url = `${this.baseUrl}/api/Container/DeleteContainer/` + id;
|
|
114
122
|
const token = localStorage.getItem("auth_token");
|
|
115
123
|
const requestOptions = {
|
|
116
|
-
method: "
|
|
124
|
+
method: "GET",
|
|
117
125
|
headers: {
|
|
118
126
|
"Content-Type": "application/json",
|
|
119
127
|
...token && { Authorization: `Bearer ${token}` }
|
|
120
|
-
}
|
|
121
|
-
body: JSON.stringify(containerDTO)
|
|
128
|
+
}
|
|
122
129
|
};
|
|
123
130
|
const response = await fetch(url, requestOptions);
|
|
124
131
|
if (!response.ok) throw new Error(await response.text());
|
|
125
132
|
return await response.json();
|
|
126
133
|
}
|
|
127
|
-
async
|
|
128
|
-
const url = `${this.baseUrl}/api/Container/
|
|
134
|
+
async CreateFileContainer(fileName, size, contentType) {
|
|
135
|
+
const url = `${this.baseUrl}/api/Container/CreateFileContainer/` + fileName + `/` + size + `/` + contentType;
|
|
129
136
|
const token = localStorage.getItem("auth_token");
|
|
130
137
|
const requestOptions = {
|
|
131
138
|
method: "GET",
|
|
@@ -138,22 +145,39 @@ var Container = class {
|
|
|
138
145
|
if (!response.ok) throw new Error(await response.text());
|
|
139
146
|
return await response.json();
|
|
140
147
|
}
|
|
141
|
-
|
|
142
|
-
|
|
148
|
+
};
|
|
149
|
+
|
|
150
|
+
// src/api/Controllers/Home.ts
|
|
151
|
+
var Home = class {
|
|
152
|
+
baseUrl;
|
|
153
|
+
constructor(baseUrl) {
|
|
154
|
+
this.baseUrl = baseUrl;
|
|
155
|
+
}
|
|
156
|
+
};
|
|
157
|
+
|
|
158
|
+
// src/api/Controllers/S3.ts
|
|
159
|
+
var S3 = class {
|
|
160
|
+
baseUrl;
|
|
161
|
+
constructor(baseUrl) {
|
|
162
|
+
this.baseUrl = baseUrl;
|
|
163
|
+
}
|
|
164
|
+
async DeleteS3(containerDTO) {
|
|
165
|
+
const url = `${this.baseUrl}/api/S3/DeleteS3`;
|
|
143
166
|
const token = localStorage.getItem("auth_token");
|
|
144
167
|
const requestOptions = {
|
|
145
|
-
method: "
|
|
168
|
+
method: "POST",
|
|
146
169
|
headers: {
|
|
147
170
|
"Content-Type": "application/json",
|
|
148
171
|
...token && { Authorization: `Bearer ${token}` }
|
|
149
|
-
}
|
|
172
|
+
},
|
|
173
|
+
body: JSON.stringify(containerDTO)
|
|
150
174
|
};
|
|
151
175
|
const response = await fetch(url, requestOptions);
|
|
152
176
|
if (!response.ok) throw new Error(await response.text());
|
|
153
177
|
return await response.json();
|
|
154
178
|
}
|
|
155
179
|
async GetPreSignedUrl(containerDTO) {
|
|
156
|
-
const url = `${this.baseUrl}/api/
|
|
180
|
+
const url = `${this.baseUrl}/api/S3/GetPreSignedUrl`;
|
|
157
181
|
const token = localStorage.getItem("auth_token");
|
|
158
182
|
const requestOptions = {
|
|
159
183
|
method: "POST",
|
|
@@ -169,21 +193,15 @@ var Container = class {
|
|
|
169
193
|
}
|
|
170
194
|
};
|
|
171
195
|
|
|
172
|
-
// src/api/Controllers/Home.ts
|
|
173
|
-
var Home = class {
|
|
174
|
-
baseUrl;
|
|
175
|
-
constructor(baseUrl) {
|
|
176
|
-
this.baseUrl = baseUrl;
|
|
177
|
-
}
|
|
178
|
-
};
|
|
179
|
-
|
|
180
196
|
// src/api/SparkStudioStorageSDK.ts
|
|
181
197
|
var SparkStudioStorageSDK = class {
|
|
182
198
|
container;
|
|
183
199
|
home;
|
|
200
|
+
s3;
|
|
184
201
|
constructor(baseUrl) {
|
|
185
202
|
this.container = new Container(baseUrl);
|
|
186
203
|
this.home = new Home(baseUrl);
|
|
204
|
+
this.s3 = new S3(baseUrl);
|
|
187
205
|
}
|
|
188
206
|
};
|
|
189
207
|
|
|
@@ -229,75 +247,246 @@ var ContainerType = /* @__PURE__ */ ((ContainerType2) => {
|
|
|
229
247
|
return ContainerType2;
|
|
230
248
|
})(ContainerType || {});
|
|
231
249
|
|
|
250
|
+
// src/components/ContainerUploadPanel.tsx
|
|
251
|
+
var import_react7 = require("react");
|
|
252
|
+
|
|
232
253
|
// src/components/UploadContainer.tsx
|
|
254
|
+
var import_react5 = require("react");
|
|
255
|
+
|
|
256
|
+
// src/components/UploadDropzone.tsx
|
|
233
257
|
var import_react = require("react");
|
|
234
258
|
var import_jsx_runtime = require("react/jsx-runtime");
|
|
235
|
-
var
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
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,
|
|
239
287
|
accept,
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
getPresignedUrl,
|
|
243
|
-
onUploadComplete,
|
|
244
|
-
onUploadError
|
|
288
|
+
fileNames,
|
|
289
|
+
onFileChange
|
|
245
290
|
}) => {
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
uploadFile(upload);
|
|
260
|
-
});
|
|
261
|
-
};
|
|
262
|
-
const uploadFile = async (upload) => {
|
|
263
|
-
setUploads(
|
|
264
|
-
(prev) => prev.map(
|
|
265
|
-
(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
|
+
}
|
|
266
304
|
)
|
|
267
|
-
)
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
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
|
|
271
338
|
}
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
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}`
|
|
278
365
|
)
|
|
279
366
|
);
|
|
280
|
-
});
|
|
281
|
-
const fileUrl = url.split("?")[0];
|
|
282
|
-
setUploads(
|
|
283
|
-
(prev) => prev.map(
|
|
284
|
-
(u) => u.id === upload.id ? { ...u, status: "success", progress: 100, s3Url: fileUrl, publicUrl: presignedUrl.PublicUrl } : u
|
|
285
|
-
)
|
|
286
|
-
);
|
|
287
|
-
onUploadComplete?.(upload.file, fileUrl);
|
|
288
|
-
} catch (err) {
|
|
289
|
-
let message = "Upload failed";
|
|
290
|
-
if (err instanceof Error) {
|
|
291
|
-
message = err.message;
|
|
292
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) => {
|
|
293
396
|
setUploads(
|
|
294
397
|
(prev) => prev.map(
|
|
295
|
-
(u) => u.id === upload.id ? { ...u, status: "
|
|
398
|
+
(u) => u.id === upload.id ? { ...u, status: "uploading", progress: 0 } : u
|
|
296
399
|
)
|
|
297
400
|
);
|
|
298
|
-
|
|
299
|
-
|
|
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
|
|
300
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
|
+
});
|
|
301
490
|
const handleDragOver = (e) => {
|
|
302
491
|
e.preventDefault();
|
|
303
492
|
setIsDragging(true);
|
|
@@ -322,150 +511,205 @@ var UploadContainer = ({
|
|
|
322
511
|
onFilesSelected?.(files);
|
|
323
512
|
startUploadsIfNeeded(files);
|
|
324
513
|
};
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
|
|
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,
|
|
330
551
|
{
|
|
331
|
-
|
|
332
|
-
style: { cursor: "pointer", minHeight: "140px" },
|
|
552
|
+
isDragging,
|
|
333
553
|
onDragOver: handleDragOver,
|
|
334
554
|
onDragLeave: handleDragLeave,
|
|
335
|
-
onDrop: handleDrop
|
|
336
|
-
children: [
|
|
337
|
-
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("i", { className: "bi bi-cloud-arrow-up fs-1 mb-2" }),
|
|
338
|
-
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("p", { className: "mb-2", children: "Drop files here" }),
|
|
339
|
-
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("small", { className: "text-muted", children: "or click the button below" })
|
|
340
|
-
]
|
|
555
|
+
onDrop: handleDrop
|
|
341
556
|
}
|
|
342
557
|
),
|
|
343
|
-
/* @__PURE__ */ (0,
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
type: "file",
|
|
351
|
-
className: "d-none",
|
|
352
|
-
multiple,
|
|
353
|
-
accept,
|
|
354
|
-
onChange: handleFileChange
|
|
355
|
-
}
|
|
356
|
-
)
|
|
357
|
-
] }),
|
|
358
|
-
fileNames.length > 0 && /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "flex-grow-1", children: [
|
|
359
|
-
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { className: "small text-muted", children: "Selected:" }),
|
|
360
|
-
/* @__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)) })
|
|
361
|
-
] })
|
|
362
|
-
] }),
|
|
363
|
-
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: [
|
|
364
|
-
/* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "d-flex justify-content-between small mb-1", children: [
|
|
365
|
-
/* @__PURE__ */ (0, import_jsx_runtime.jsxs)("span", { children: [
|
|
366
|
-
u.file.name,
|
|
367
|
-
" - ",
|
|
368
|
-
u?.publicUrl ?? ""
|
|
369
|
-
] }),
|
|
370
|
-
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { children: u.status === "success" ? "Completed" : u.status === "error" ? "Error" : `${u.progress}%` })
|
|
371
|
-
] }),
|
|
372
|
-
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { className: "progress", style: { height: "6px" }, children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
|
|
373
|
-
"div",
|
|
374
|
-
{
|
|
375
|
-
className: "progress-bar " + (u.status === "success" ? "bg-success" : u.status === "error" ? "bg-danger" : ""),
|
|
376
|
-
role: "progressbar",
|
|
377
|
-
style: { width: `${u.progress}%` },
|
|
378
|
-
"aria-valuenow": u.progress,
|
|
379
|
-
"aria-valuemin": 0,
|
|
380
|
-
"aria-valuemax": 100
|
|
381
|
-
}
|
|
382
|
-
) }),
|
|
383
|
-
u.status === "error" && /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { className: "text-danger small mt-1", children: u.error ?? "Upload failed" })
|
|
384
|
-
] }, u.id)) })
|
|
385
|
-
] }) }) });
|
|
386
|
-
};
|
|
387
|
-
async function uploadFileToS3(file, presignedUrl, onProgress) {
|
|
388
|
-
return new Promise((resolve, reject) => {
|
|
389
|
-
const xhr = new XMLHttpRequest();
|
|
390
|
-
xhr.open("PUT", presignedUrl);
|
|
391
|
-
xhr.upload.onprogress = (event) => {
|
|
392
|
-
if (!event.lengthComputable) return;
|
|
393
|
-
const percent = Math.round(event.loaded / event.total * 100);
|
|
394
|
-
onProgress(percent);
|
|
395
|
-
};
|
|
396
|
-
xhr.onload = () => {
|
|
397
|
-
if (xhr.status >= 200 && xhr.status < 300) {
|
|
398
|
-
onProgress(100);
|
|
399
|
-
resolve();
|
|
400
|
-
} else {
|
|
401
|
-
reject(
|
|
402
|
-
new Error(`S3 upload failed with status ${xhr.status}: ${xhr.statusText}`)
|
|
403
|
-
);
|
|
558
|
+
/* @__PURE__ */ (0, import_jsx_runtime4.jsx)(
|
|
559
|
+
UploadFilePicker,
|
|
560
|
+
{
|
|
561
|
+
multiple,
|
|
562
|
+
accept,
|
|
563
|
+
fileNames,
|
|
564
|
+
onFileChange: handleFileChange
|
|
404
565
|
}
|
|
405
|
-
|
|
406
|
-
|
|
407
|
-
|
|
408
|
-
|
|
409
|
-
|
|
410
|
-
|
|
411
|
-
|
|
412
|
-
|
|
413
|
-
|
|
414
|
-
|
|
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
|
+
};
|
|
415
601
|
}
|
|
416
602
|
|
|
417
|
-
// src/
|
|
418
|
-
var
|
|
419
|
-
var
|
|
420
|
-
|
|
421
|
-
|
|
422
|
-
|
|
423
|
-
|
|
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
|
+
});
|
|
424
615
|
const getPresignedUrl = async (file) => {
|
|
425
|
-
const sdkDb = new SparkStudioStorageSDK(
|
|
426
|
-
|
|
427
|
-
|
|
428
|
-
const
|
|
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
|
+
);
|
|
627
|
+
const resultS3 = await sdkS3.s3.GetPreSignedUrl(containerDTO);
|
|
429
628
|
return resultS3;
|
|
430
629
|
};
|
|
431
|
-
|
|
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)(
|
|
432
675
|
import_authentication_ui.AuthenticatorProvider,
|
|
433
676
|
{
|
|
434
677
|
googleClientId: import_authentication_ui.AppSettings.GoogleClientId,
|
|
435
678
|
authenticationUrl: import_authentication_ui.AppSettings.AuthenticationUrl,
|
|
436
679
|
accountsUrl: import_authentication_ui.AppSettings.AccountsUrl,
|
|
437
|
-
|
|
438
|
-
children: [
|
|
439
|
-
/* @__PURE__ */ (0, import_jsx_runtime2.jsx)(import_authentication_ui.UserInfoCard, {}),
|
|
440
|
-
/* @__PURE__ */ (0, import_jsx_runtime2.jsx)(
|
|
441
|
-
UploadContainer,
|
|
442
|
-
{
|
|
443
|
-
title: "Upload your files to S3",
|
|
444
|
-
description: "Drag & drop or browse files. Each file will upload with its own progress bar.",
|
|
445
|
-
multiple: true,
|
|
446
|
-
accept: "*/*",
|
|
447
|
-
autoUpload: true,
|
|
448
|
-
getPresignedUrl,
|
|
449
|
-
onUploadComplete: (file, s3Url) => {
|
|
450
|
-
console.log("Uploaded", file.name, "to", s3Url);
|
|
451
|
-
},
|
|
452
|
-
onUploadError: (file, error) => {
|
|
453
|
-
console.error("Failed to upload", file.name, error);
|
|
454
|
-
}
|
|
455
|
-
}
|
|
456
|
-
)
|
|
457
|
-
]
|
|
680
|
+
children: /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(HomeContent, {})
|
|
458
681
|
}
|
|
459
682
|
);
|
|
460
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
|
+
}
|
|
461
697
|
// Annotate the CommonJS export names for ESM import in node:
|
|
462
698
|
0 && (module.exports = {
|
|
463
699
|
AWSPresignedUrlDTO,
|
|
464
700
|
Container,
|
|
465
701
|
ContainerDTO,
|
|
466
702
|
ContainerType,
|
|
703
|
+
ContainerUploadPanel,
|
|
467
704
|
Home,
|
|
468
705
|
HomeView,
|
|
706
|
+
S3,
|
|
469
707
|
SparkStudioStorageSDK,
|
|
470
|
-
UploadContainer
|
|
708
|
+
UploadContainer,
|
|
709
|
+
UploadDropzone,
|
|
710
|
+
UploadFilePicker,
|
|
711
|
+
UploadFileToS3,
|
|
712
|
+
UploadProgressList,
|
|
713
|
+
UseContainers,
|
|
714
|
+
UseUploadManager
|
|
471
715
|
});
|