@sparkstudio/storage-ui 1.0.18 → 1.0.19

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 CHANGED
@@ -29,7 +29,9 @@ __export(index_exports, {
29
29
  Home: () => Home,
30
30
  HomeView: () => HomeView,
31
31
  S3: () => S3,
32
+ SingleFileProcessUploader: () => SingleFileProcessUploader,
32
33
  SparkStudioStorageSDK: () => SparkStudioStorageSDK,
34
+ TemporaryFileDTO: () => TemporaryFileDTO,
33
35
  UploadContainer: () => UploadContainer,
34
36
  UploadDropzone: () => UploadDropzone,
35
37
  UploadFileToS3: () => UploadFileToS3,
@@ -191,6 +193,21 @@ var S3 = class {
191
193
  if (!response.ok) throw new Error(await response.text());
192
194
  return await response.json();
193
195
  }
196
+ async GetTemporaryPreSignedUrl(temporaryFileDTO) {
197
+ const url = `${this.baseUrl}/api/S3/GetTemporaryPreSignedUrl`;
198
+ const token = localStorage.getItem("auth_token");
199
+ const requestOptions = {
200
+ method: "POST",
201
+ headers: {
202
+ "Content-Type": "application/json",
203
+ ...token && { Authorization: `Bearer ${token}` }
204
+ },
205
+ body: JSON.stringify(temporaryFileDTO)
206
+ };
207
+ const response = await fetch(url, requestOptions);
208
+ if (!response.ok) throw new Error(await response.text());
209
+ return await response.json();
210
+ }
194
211
  };
195
212
 
196
213
  // src/api/SparkStudioStorageSDK.ts
@@ -241,6 +258,16 @@ var ContainerDTO = class {
241
258
  }
242
259
  };
243
260
 
261
+ // src/api/DTOs/TemporaryFileDTO.ts
262
+ var TemporaryFileDTO = class {
263
+ Name;
264
+ ContentType;
265
+ constructor(init) {
266
+ this.Name = init.Name;
267
+ this.ContentType = init.ContentType;
268
+ }
269
+ };
270
+
244
271
  // src/api/Enums/ContainerType.ts
245
272
  var ContainerType = /* @__PURE__ */ ((ContainerType2) => {
246
273
  ContainerType2[ContainerType2["File"] = 0] = "File";
@@ -1003,31 +1030,214 @@ var ContainerUploadPanel = ({
1003
1030
  );
1004
1031
  };
1005
1032
 
1033
+ // src/components/SingleFileProcessUploader.tsx
1034
+ var import_react8 = require("react");
1035
+ var import_jsx_runtime6 = require("react/jsx-runtime");
1036
+ var SingleFileProcessUploader = ({ getPresignedUrl, onUploadComplete, accept, label, disabled }) => {
1037
+ const [selectedFile, setSelectedFile] = (0, import_react8.useState)(null);
1038
+ const [isDragging, setIsDragging] = (0, import_react8.useState)(false);
1039
+ const [progress, setProgress] = (0, import_react8.useState)(null);
1040
+ const [status, setStatus] = (0, import_react8.useState)("idle");
1041
+ const [errorMessage, setErrorMessage] = (0, import_react8.useState)(null);
1042
+ const fileInputRef = (0, import_react8.useRef)(null);
1043
+ const handleFilesSelected = (0, import_react8.useCallback)((files) => {
1044
+ if (!files || files.length === 0) return;
1045
+ const file = files[0];
1046
+ setSelectedFile(file);
1047
+ setStatus("idle");
1048
+ setProgress(null);
1049
+ setErrorMessage(null);
1050
+ }, []);
1051
+ const handleBrowseClick = () => {
1052
+ if (disabled) return;
1053
+ fileInputRef.current?.click();
1054
+ };
1055
+ const handleInputChange = (e) => {
1056
+ handleFilesSelected(e.target.files);
1057
+ e.target.value = "";
1058
+ };
1059
+ const handleDragOver = (e) => {
1060
+ e.preventDefault();
1061
+ if (disabled) return;
1062
+ setIsDragging(true);
1063
+ };
1064
+ const handleDragLeave = (e) => {
1065
+ e.preventDefault();
1066
+ setIsDragging(false);
1067
+ };
1068
+ const handleDrop = (e) => {
1069
+ e.preventDefault();
1070
+ if (disabled) return;
1071
+ setIsDragging(false);
1072
+ const files = e.dataTransfer.files;
1073
+ handleFilesSelected(files);
1074
+ };
1075
+ const handleProcessClick = async () => {
1076
+ if (!selectedFile || disabled) return;
1077
+ try {
1078
+ setStatus("uploading");
1079
+ setProgress(0);
1080
+ setErrorMessage(null);
1081
+ const presignedUrl = await getPresignedUrl(selectedFile);
1082
+ await UploadFileToS3(selectedFile, presignedUrl, (p) => setProgress(p));
1083
+ setStatus("success");
1084
+ onUploadComplete?.(selectedFile, presignedUrl);
1085
+ } catch (err) {
1086
+ const message = getErrorMessage(err);
1087
+ console.error("Upload failed:", message);
1088
+ setStatus("error");
1089
+ setErrorMessage(message);
1090
+ }
1091
+ };
1092
+ function getErrorMessage(err) {
1093
+ if (err instanceof Error) return err.message;
1094
+ if (typeof err === "string") return err;
1095
+ return "Unknown error during upload.";
1096
+ }
1097
+ const isUploading = status === "uploading";
1098
+ return /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)("div", { style: { display: "flex", flexDirection: "column", gap: 8 }, children: [
1099
+ label && /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("label", { style: { fontWeight: 600 }, children: label }),
1100
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)(
1101
+ "div",
1102
+ {
1103
+ onDragOver: handleDragOver,
1104
+ onDragLeave: handleDragLeave,
1105
+ onDrop: handleDrop,
1106
+ onClick: handleBrowseClick,
1107
+ style: {
1108
+ border: "2px dashed #ccc",
1109
+ borderRadius: 8,
1110
+ padding: 16,
1111
+ textAlign: "center",
1112
+ cursor: disabled ? "not-allowed" : "pointer",
1113
+ backgroundColor: isDragging ? "#f0f8ff" : "#fafafa"
1114
+ },
1115
+ children: [
1116
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(
1117
+ "input",
1118
+ {
1119
+ ref: fileInputRef,
1120
+ type: "file",
1121
+ accept,
1122
+ style: { display: "none" },
1123
+ onChange: handleInputChange,
1124
+ disabled
1125
+ }
1126
+ ),
1127
+ selectedFile ? /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)("div", { children: [
1128
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)("div", { children: [
1129
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("strong", { children: "Selected file:" }),
1130
+ " ",
1131
+ selectedFile.name
1132
+ ] }),
1133
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("div", { style: { fontSize: 12, color: "#666" }, children: "Click here to change file or drag a new one." })
1134
+ ] }) : /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)("div", { children: [
1135
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("div", { children: "Drag & drop a file here" }),
1136
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("div", { style: { fontSize: 12, color: "#666" }, children: "or click to browse" })
1137
+ ] })
1138
+ ]
1139
+ }
1140
+ ),
1141
+ isUploading && /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)("div", { style: { fontSize: 12 }, children: [
1142
+ "Uploading... ",
1143
+ progress ?? 0,
1144
+ "%",
1145
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(
1146
+ "div",
1147
+ {
1148
+ style: {
1149
+ marginTop: 4,
1150
+ height: 6,
1151
+ borderRadius: 999,
1152
+ backgroundColor: "#eee",
1153
+ overflow: "hidden"
1154
+ },
1155
+ children: /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(
1156
+ "div",
1157
+ {
1158
+ style: {
1159
+ height: "100%",
1160
+ width: `${progress ?? 0}%`,
1161
+ backgroundColor: "#007bff",
1162
+ transition: "width 0.2s ease-out"
1163
+ }
1164
+ }
1165
+ )
1166
+ }
1167
+ )
1168
+ ] }),
1169
+ status === "success" && /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("div", { style: { fontSize: 12, color: "green" }, children: "Upload complete \u2705" }),
1170
+ status === "error" && /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)("div", { style: { fontSize: 12, color: "red" }, children: [
1171
+ "Upload failed: ",
1172
+ errorMessage
1173
+ ] }),
1174
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(
1175
+ "button",
1176
+ {
1177
+ type: "button",
1178
+ onClick: handleProcessClick,
1179
+ disabled: !selectedFile || isUploading || disabled,
1180
+ style: {
1181
+ padding: "8px 16px",
1182
+ borderRadius: 4,
1183
+ border: "none",
1184
+ backgroundColor: !selectedFile || isUploading || disabled ? "#ccc" : "#007bff",
1185
+ color: "#fff",
1186
+ fontWeight: 600,
1187
+ cursor: !selectedFile || isUploading || disabled ? "not-allowed" : "pointer"
1188
+ },
1189
+ children: isUploading ? "Processing..." : "Process"
1190
+ }
1191
+ )
1192
+ ] });
1193
+ };
1194
+
1006
1195
  // src/views/HomeView.tsx
1007
1196
  var import_authentication_ui = require("@sparkstudio/authentication-ui");
1008
- var import_jsx_runtime6 = require("react/jsx-runtime");
1197
+ var import_jsx_runtime7 = require("react/jsx-runtime");
1009
1198
  function HomeView() {
1010
- return /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(
1199
+ return /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(
1011
1200
  import_authentication_ui.AuthenticatorProvider,
1012
1201
  {
1013
1202
  googleClientId: import_authentication_ui.AppSettings.GoogleClientId,
1014
1203
  authenticationUrl: import_authentication_ui.AppSettings.AuthenticationUrl,
1015
1204
  accountsUrl: import_authentication_ui.AppSettings.AccountsUrl,
1016
- children: /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(HomeContent, {})
1205
+ children: /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(HomeContent, {})
1017
1206
  }
1018
1207
  );
1019
1208
  }
1020
1209
  function HomeContent() {
1021
1210
  const { user } = (0, import_authentication_ui.useUser)();
1022
- return /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)(import_jsx_runtime6.Fragment, { children: [
1023
- /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(import_authentication_ui.UserInfoCard, {}),
1024
- user && /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(
1025
- ContainerUploadPanel,
1026
- {
1027
- containerApiBaseUrl: "https://lf9zyufpuk.execute-api.us-east-2.amazonaws.com/Prod",
1028
- storageApiBaseUrl: "https://iq0gmcn0pd.execute-api.us-east-2.amazonaws.com/Prod"
1029
- }
1030
- )
1211
+ async function getPresignedUrlFromApi(file) {
1212
+ const res = new SparkStudioStorageSDK(
1213
+ // "https://iq0gmcn0pd.execute-api.us-east-2.amazonaws.com/Prod"
1214
+ "https://localhost:5001"
1215
+ );
1216
+ const contentType = file.type || "application/octet-stream";
1217
+ return (await res.s3.GetTemporaryPreSignedUrl(new TemporaryFileDTO({ Name: file.name, ContentType: contentType })))?.PresignedUrl ?? "";
1218
+ }
1219
+ return /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)(import_jsx_runtime7.Fragment, { children: [
1220
+ /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(import_authentication_ui.UserInfoCard, {}),
1221
+ user && /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)(import_jsx_runtime7.Fragment, { children: [
1222
+ /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(
1223
+ SingleFileProcessUploader,
1224
+ {
1225
+ label: "Upload a file to process",
1226
+ accept: "*/*",
1227
+ getPresignedUrl: getPresignedUrlFromApi,
1228
+ onUploadComplete: (file, s3Url) => {
1229
+ console.log("Uploaded:", file.name, "S3 URL:", s3Url);
1230
+ }
1231
+ }
1232
+ ),
1233
+ /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(
1234
+ ContainerUploadPanel,
1235
+ {
1236
+ containerApiBaseUrl: "https://lf9zyufpuk.execute-api.us-east-2.amazonaws.com/Prod",
1237
+ storageApiBaseUrl: "https://iq0gmcn0pd.execute-api.us-east-2.amazonaws.com/Prod"
1238
+ }
1239
+ )
1240
+ ] })
1031
1241
  ] });
1032
1242
  }
1033
1243
  // Annotate the CommonJS export names for ESM import in node:
@@ -1041,7 +1251,9 @@ function HomeContent() {
1041
1251
  Home,
1042
1252
  HomeView,
1043
1253
  S3,
1254
+ SingleFileProcessUploader,
1044
1255
  SparkStudioStorageSDK,
1256
+ TemporaryFileDTO,
1045
1257
  UploadContainer,
1046
1258
  UploadDropzone,
1047
1259
  UploadFileToS3,
package/dist/index.d.cts CHANGED
@@ -75,6 +75,20 @@ declare class AWSPresignedUrlDTO implements IAWSPresignedUrlDTO {
75
75
  constructor(init: AWSPresignedUrlDTOInit);
76
76
  }
77
77
 
78
+ /**
79
+ * Represents an Auto-generated model for TemporaryFileDTO.
80
+ */
81
+ interface ITemporaryFileDTO {
82
+ Name?: string;
83
+ ContentType?: string;
84
+ }
85
+ type TemporaryFileDTOInit = Partial<ITemporaryFileDTO>;
86
+ declare class TemporaryFileDTO implements ITemporaryFileDTO {
87
+ Name?: string;
88
+ ContentType?: string;
89
+ constructor(init: TemporaryFileDTOInit);
90
+ }
91
+
78
92
  /**
79
93
  * Auto-generated client for the S3 controller.
80
94
  */
@@ -83,6 +97,7 @@ declare class S3 {
83
97
  constructor(baseUrl: string);
84
98
  DeleteS3(containerDTO: ContainerDTO): Promise<ContainerDTO>;
85
99
  GetPreSignedUrl(containerDTO: ContainerDTO): Promise<AWSPresignedUrlDTO>;
100
+ GetTemporaryPreSignedUrl(temporaryFileDTO: TemporaryFileDTO): Promise<AWSPresignedUrlDTO>;
86
101
  }
87
102
 
88
103
  /**
@@ -116,6 +131,24 @@ interface DesktopFileIconProps {
116
131
  }
117
132
  declare const DesktopFileIcon: React__default.FC<DesktopFileIconProps>;
118
133
 
134
+ interface SingleFileProcessUploaderProps {
135
+ /** Called to get a presigned S3 URL for the selected file. */
136
+ getPresignedUrl: (file: File) => Promise<string>;
137
+ /**
138
+ * Called after the upload succeeds.
139
+ * You can use s3Url (the presigned URL you provided) in another component
140
+ * or have your backend process the file from S3.
141
+ */
142
+ onUploadComplete?: (file: File, s3Url: string) => void;
143
+ /** Optional: restrict file types, e.g. "image/*" or ".png,.jpg" */
144
+ accept?: string;
145
+ /** Optional: label shown above the control */
146
+ label?: string;
147
+ /** Optional: disable the control entirely */
148
+ disabled?: boolean;
149
+ }
150
+ declare const SingleFileProcessUploader: React__default.FC<SingleFileProcessUploaderProps>;
151
+
119
152
  interface UploadContainerProps {
120
153
  multiple?: boolean;
121
154
  accept?: string;
@@ -192,4 +225,4 @@ declare function UseUploadManager({ autoUpload, getPresignedUrl, onUploadComplet
192
225
 
193
226
  declare function HomeView(): react_jsx_runtime.JSX.Element;
194
227
 
195
- export { AWSPresignedUrlDTO, Container, ContainerDTO, ContainerType, ContainerUploadPanel, DesktopFileIcon, type DesktopFileIconProps, Home, HomeView, type IAWSPresignedUrlDTO, type IContainerDTO, S3, SparkStudioStorageSDK, UploadContainer, type UploadContainerProps, UploadDropzone, UploadFileToS3, UploadProgressList, type UploadState, type UploadStatus, UseContainers, UseUploadManager };
228
+ export { AWSPresignedUrlDTO, Container, ContainerDTO, ContainerType, ContainerUploadPanel, DesktopFileIcon, type DesktopFileIconProps, Home, HomeView, type IAWSPresignedUrlDTO, type IContainerDTO, type ITemporaryFileDTO, S3, SingleFileProcessUploader, type SingleFileProcessUploaderProps, SparkStudioStorageSDK, TemporaryFileDTO, UploadContainer, type UploadContainerProps, UploadDropzone, UploadFileToS3, UploadProgressList, type UploadState, type UploadStatus, UseContainers, UseUploadManager };
package/dist/index.d.ts CHANGED
@@ -75,6 +75,20 @@ declare class AWSPresignedUrlDTO implements IAWSPresignedUrlDTO {
75
75
  constructor(init: AWSPresignedUrlDTOInit);
76
76
  }
77
77
 
78
+ /**
79
+ * Represents an Auto-generated model for TemporaryFileDTO.
80
+ */
81
+ interface ITemporaryFileDTO {
82
+ Name?: string;
83
+ ContentType?: string;
84
+ }
85
+ type TemporaryFileDTOInit = Partial<ITemporaryFileDTO>;
86
+ declare class TemporaryFileDTO implements ITemporaryFileDTO {
87
+ Name?: string;
88
+ ContentType?: string;
89
+ constructor(init: TemporaryFileDTOInit);
90
+ }
91
+
78
92
  /**
79
93
  * Auto-generated client for the S3 controller.
80
94
  */
@@ -83,6 +97,7 @@ declare class S3 {
83
97
  constructor(baseUrl: string);
84
98
  DeleteS3(containerDTO: ContainerDTO): Promise<ContainerDTO>;
85
99
  GetPreSignedUrl(containerDTO: ContainerDTO): Promise<AWSPresignedUrlDTO>;
100
+ GetTemporaryPreSignedUrl(temporaryFileDTO: TemporaryFileDTO): Promise<AWSPresignedUrlDTO>;
86
101
  }
87
102
 
88
103
  /**
@@ -116,6 +131,24 @@ interface DesktopFileIconProps {
116
131
  }
117
132
  declare const DesktopFileIcon: React__default.FC<DesktopFileIconProps>;
118
133
 
134
+ interface SingleFileProcessUploaderProps {
135
+ /** Called to get a presigned S3 URL for the selected file. */
136
+ getPresignedUrl: (file: File) => Promise<string>;
137
+ /**
138
+ * Called after the upload succeeds.
139
+ * You can use s3Url (the presigned URL you provided) in another component
140
+ * or have your backend process the file from S3.
141
+ */
142
+ onUploadComplete?: (file: File, s3Url: string) => void;
143
+ /** Optional: restrict file types, e.g. "image/*" or ".png,.jpg" */
144
+ accept?: string;
145
+ /** Optional: label shown above the control */
146
+ label?: string;
147
+ /** Optional: disable the control entirely */
148
+ disabled?: boolean;
149
+ }
150
+ declare const SingleFileProcessUploader: React__default.FC<SingleFileProcessUploaderProps>;
151
+
119
152
  interface UploadContainerProps {
120
153
  multiple?: boolean;
121
154
  accept?: string;
@@ -192,4 +225,4 @@ declare function UseUploadManager({ autoUpload, getPresignedUrl, onUploadComplet
192
225
 
193
226
  declare function HomeView(): react_jsx_runtime.JSX.Element;
194
227
 
195
- export { AWSPresignedUrlDTO, Container, ContainerDTO, ContainerType, ContainerUploadPanel, DesktopFileIcon, type DesktopFileIconProps, Home, HomeView, type IAWSPresignedUrlDTO, type IContainerDTO, S3, SparkStudioStorageSDK, UploadContainer, type UploadContainerProps, UploadDropzone, UploadFileToS3, UploadProgressList, type UploadState, type UploadStatus, UseContainers, UseUploadManager };
228
+ export { AWSPresignedUrlDTO, Container, ContainerDTO, ContainerType, ContainerUploadPanel, DesktopFileIcon, type DesktopFileIconProps, Home, HomeView, type IAWSPresignedUrlDTO, type IContainerDTO, type ITemporaryFileDTO, S3, SingleFileProcessUploader, type SingleFileProcessUploaderProps, SparkStudioStorageSDK, TemporaryFileDTO, UploadContainer, type UploadContainerProps, UploadDropzone, UploadFileToS3, UploadProgressList, type UploadState, type UploadStatus, UseContainers, UseUploadManager };
package/dist/index.js CHANGED
@@ -150,6 +150,21 @@ var S3 = class {
150
150
  if (!response.ok) throw new Error(await response.text());
151
151
  return await response.json();
152
152
  }
153
+ async GetTemporaryPreSignedUrl(temporaryFileDTO) {
154
+ const url = `${this.baseUrl}/api/S3/GetTemporaryPreSignedUrl`;
155
+ const token = localStorage.getItem("auth_token");
156
+ const requestOptions = {
157
+ method: "POST",
158
+ headers: {
159
+ "Content-Type": "application/json",
160
+ ...token && { Authorization: `Bearer ${token}` }
161
+ },
162
+ body: JSON.stringify(temporaryFileDTO)
163
+ };
164
+ const response = await fetch(url, requestOptions);
165
+ if (!response.ok) throw new Error(await response.text());
166
+ return await response.json();
167
+ }
153
168
  };
154
169
 
155
170
  // src/api/SparkStudioStorageSDK.ts
@@ -200,6 +215,16 @@ var ContainerDTO = class {
200
215
  }
201
216
  };
202
217
 
218
+ // src/api/DTOs/TemporaryFileDTO.ts
219
+ var TemporaryFileDTO = class {
220
+ Name;
221
+ ContentType;
222
+ constructor(init) {
223
+ this.Name = init.Name;
224
+ this.ContentType = init.ContentType;
225
+ }
226
+ };
227
+
203
228
  // src/api/Enums/ContainerType.ts
204
229
  var ContainerType = /* @__PURE__ */ ((ContainerType2) => {
205
230
  ContainerType2[ContainerType2["File"] = 0] = "File";
@@ -972,6 +997,168 @@ var ContainerUploadPanel = ({
972
997
  );
973
998
  };
974
999
 
1000
+ // src/components/SingleFileProcessUploader.tsx
1001
+ import { useCallback as useCallback2, useRef as useRef2, useState as useState5 } from "react";
1002
+ import { jsx as jsx6, jsxs as jsxs4 } from "react/jsx-runtime";
1003
+ var SingleFileProcessUploader = ({ getPresignedUrl, onUploadComplete, accept, label, disabled }) => {
1004
+ const [selectedFile, setSelectedFile] = useState5(null);
1005
+ const [isDragging, setIsDragging] = useState5(false);
1006
+ const [progress, setProgress] = useState5(null);
1007
+ const [status, setStatus] = useState5("idle");
1008
+ const [errorMessage, setErrorMessage] = useState5(null);
1009
+ const fileInputRef = useRef2(null);
1010
+ const handleFilesSelected = useCallback2((files) => {
1011
+ if (!files || files.length === 0) return;
1012
+ const file = files[0];
1013
+ setSelectedFile(file);
1014
+ setStatus("idle");
1015
+ setProgress(null);
1016
+ setErrorMessage(null);
1017
+ }, []);
1018
+ const handleBrowseClick = () => {
1019
+ if (disabled) return;
1020
+ fileInputRef.current?.click();
1021
+ };
1022
+ const handleInputChange = (e) => {
1023
+ handleFilesSelected(e.target.files);
1024
+ e.target.value = "";
1025
+ };
1026
+ const handleDragOver = (e) => {
1027
+ e.preventDefault();
1028
+ if (disabled) return;
1029
+ setIsDragging(true);
1030
+ };
1031
+ const handleDragLeave = (e) => {
1032
+ e.preventDefault();
1033
+ setIsDragging(false);
1034
+ };
1035
+ const handleDrop = (e) => {
1036
+ e.preventDefault();
1037
+ if (disabled) return;
1038
+ setIsDragging(false);
1039
+ const files = e.dataTransfer.files;
1040
+ handleFilesSelected(files);
1041
+ };
1042
+ const handleProcessClick = async () => {
1043
+ if (!selectedFile || disabled) return;
1044
+ try {
1045
+ setStatus("uploading");
1046
+ setProgress(0);
1047
+ setErrorMessage(null);
1048
+ const presignedUrl = await getPresignedUrl(selectedFile);
1049
+ await UploadFileToS3(selectedFile, presignedUrl, (p) => setProgress(p));
1050
+ setStatus("success");
1051
+ onUploadComplete?.(selectedFile, presignedUrl);
1052
+ } catch (err) {
1053
+ const message = getErrorMessage(err);
1054
+ console.error("Upload failed:", message);
1055
+ setStatus("error");
1056
+ setErrorMessage(message);
1057
+ }
1058
+ };
1059
+ function getErrorMessage(err) {
1060
+ if (err instanceof Error) return err.message;
1061
+ if (typeof err === "string") return err;
1062
+ return "Unknown error during upload.";
1063
+ }
1064
+ const isUploading = status === "uploading";
1065
+ return /* @__PURE__ */ jsxs4("div", { style: { display: "flex", flexDirection: "column", gap: 8 }, children: [
1066
+ label && /* @__PURE__ */ jsx6("label", { style: { fontWeight: 600 }, children: label }),
1067
+ /* @__PURE__ */ jsxs4(
1068
+ "div",
1069
+ {
1070
+ onDragOver: handleDragOver,
1071
+ onDragLeave: handleDragLeave,
1072
+ onDrop: handleDrop,
1073
+ onClick: handleBrowseClick,
1074
+ style: {
1075
+ border: "2px dashed #ccc",
1076
+ borderRadius: 8,
1077
+ padding: 16,
1078
+ textAlign: "center",
1079
+ cursor: disabled ? "not-allowed" : "pointer",
1080
+ backgroundColor: isDragging ? "#f0f8ff" : "#fafafa"
1081
+ },
1082
+ children: [
1083
+ /* @__PURE__ */ jsx6(
1084
+ "input",
1085
+ {
1086
+ ref: fileInputRef,
1087
+ type: "file",
1088
+ accept,
1089
+ style: { display: "none" },
1090
+ onChange: handleInputChange,
1091
+ disabled
1092
+ }
1093
+ ),
1094
+ selectedFile ? /* @__PURE__ */ jsxs4("div", { children: [
1095
+ /* @__PURE__ */ jsxs4("div", { children: [
1096
+ /* @__PURE__ */ jsx6("strong", { children: "Selected file:" }),
1097
+ " ",
1098
+ selectedFile.name
1099
+ ] }),
1100
+ /* @__PURE__ */ jsx6("div", { style: { fontSize: 12, color: "#666" }, children: "Click here to change file or drag a new one." })
1101
+ ] }) : /* @__PURE__ */ jsxs4("div", { children: [
1102
+ /* @__PURE__ */ jsx6("div", { children: "Drag & drop a file here" }),
1103
+ /* @__PURE__ */ jsx6("div", { style: { fontSize: 12, color: "#666" }, children: "or click to browse" })
1104
+ ] })
1105
+ ]
1106
+ }
1107
+ ),
1108
+ isUploading && /* @__PURE__ */ jsxs4("div", { style: { fontSize: 12 }, children: [
1109
+ "Uploading... ",
1110
+ progress ?? 0,
1111
+ "%",
1112
+ /* @__PURE__ */ jsx6(
1113
+ "div",
1114
+ {
1115
+ style: {
1116
+ marginTop: 4,
1117
+ height: 6,
1118
+ borderRadius: 999,
1119
+ backgroundColor: "#eee",
1120
+ overflow: "hidden"
1121
+ },
1122
+ children: /* @__PURE__ */ jsx6(
1123
+ "div",
1124
+ {
1125
+ style: {
1126
+ height: "100%",
1127
+ width: `${progress ?? 0}%`,
1128
+ backgroundColor: "#007bff",
1129
+ transition: "width 0.2s ease-out"
1130
+ }
1131
+ }
1132
+ )
1133
+ }
1134
+ )
1135
+ ] }),
1136
+ status === "success" && /* @__PURE__ */ jsx6("div", { style: { fontSize: 12, color: "green" }, children: "Upload complete \u2705" }),
1137
+ status === "error" && /* @__PURE__ */ jsxs4("div", { style: { fontSize: 12, color: "red" }, children: [
1138
+ "Upload failed: ",
1139
+ errorMessage
1140
+ ] }),
1141
+ /* @__PURE__ */ jsx6(
1142
+ "button",
1143
+ {
1144
+ type: "button",
1145
+ onClick: handleProcessClick,
1146
+ disabled: !selectedFile || isUploading || disabled,
1147
+ style: {
1148
+ padding: "8px 16px",
1149
+ borderRadius: 4,
1150
+ border: "none",
1151
+ backgroundColor: !selectedFile || isUploading || disabled ? "#ccc" : "#007bff",
1152
+ color: "#fff",
1153
+ fontWeight: 600,
1154
+ cursor: !selectedFile || isUploading || disabled ? "not-allowed" : "pointer"
1155
+ },
1156
+ children: isUploading ? "Processing..." : "Process"
1157
+ }
1158
+ )
1159
+ ] });
1160
+ };
1161
+
975
1162
  // src/views/HomeView.tsx
976
1163
  import {
977
1164
  AppSettings,
@@ -979,29 +1166,50 @@ import {
979
1166
  UserInfoCard,
980
1167
  useUser
981
1168
  } from "@sparkstudio/authentication-ui";
982
- import { Fragment as Fragment2, jsx as jsx6, jsxs as jsxs4 } from "react/jsx-runtime";
1169
+ import { Fragment as Fragment2, jsx as jsx7, jsxs as jsxs5 } from "react/jsx-runtime";
983
1170
  function HomeView() {
984
- return /* @__PURE__ */ jsx6(
1171
+ return /* @__PURE__ */ jsx7(
985
1172
  AuthenticatorProvider,
986
1173
  {
987
1174
  googleClientId: AppSettings.GoogleClientId,
988
1175
  authenticationUrl: AppSettings.AuthenticationUrl,
989
1176
  accountsUrl: AppSettings.AccountsUrl,
990
- children: /* @__PURE__ */ jsx6(HomeContent, {})
1177
+ children: /* @__PURE__ */ jsx7(HomeContent, {})
991
1178
  }
992
1179
  );
993
1180
  }
994
1181
  function HomeContent() {
995
1182
  const { user } = useUser();
996
- return /* @__PURE__ */ jsxs4(Fragment2, { children: [
997
- /* @__PURE__ */ jsx6(UserInfoCard, {}),
998
- user && /* @__PURE__ */ jsx6(
999
- ContainerUploadPanel,
1000
- {
1001
- containerApiBaseUrl: "https://lf9zyufpuk.execute-api.us-east-2.amazonaws.com/Prod",
1002
- storageApiBaseUrl: "https://iq0gmcn0pd.execute-api.us-east-2.amazonaws.com/Prod"
1003
- }
1004
- )
1183
+ async function getPresignedUrlFromApi(file) {
1184
+ const res = new SparkStudioStorageSDK(
1185
+ // "https://iq0gmcn0pd.execute-api.us-east-2.amazonaws.com/Prod"
1186
+ "https://localhost:5001"
1187
+ );
1188
+ const contentType = file.type || "application/octet-stream";
1189
+ return (await res.s3.GetTemporaryPreSignedUrl(new TemporaryFileDTO({ Name: file.name, ContentType: contentType })))?.PresignedUrl ?? "";
1190
+ }
1191
+ return /* @__PURE__ */ jsxs5(Fragment2, { children: [
1192
+ /* @__PURE__ */ jsx7(UserInfoCard, {}),
1193
+ user && /* @__PURE__ */ jsxs5(Fragment2, { children: [
1194
+ /* @__PURE__ */ jsx7(
1195
+ SingleFileProcessUploader,
1196
+ {
1197
+ label: "Upload a file to process",
1198
+ accept: "*/*",
1199
+ getPresignedUrl: getPresignedUrlFromApi,
1200
+ onUploadComplete: (file, s3Url) => {
1201
+ console.log("Uploaded:", file.name, "S3 URL:", s3Url);
1202
+ }
1203
+ }
1204
+ ),
1205
+ /* @__PURE__ */ jsx7(
1206
+ ContainerUploadPanel,
1207
+ {
1208
+ containerApiBaseUrl: "https://lf9zyufpuk.execute-api.us-east-2.amazonaws.com/Prod",
1209
+ storageApiBaseUrl: "https://iq0gmcn0pd.execute-api.us-east-2.amazonaws.com/Prod"
1210
+ }
1211
+ )
1212
+ ] })
1005
1213
  ] });
1006
1214
  }
1007
1215
  export {
@@ -1014,7 +1222,9 @@ export {
1014
1222
  Home,
1015
1223
  HomeView,
1016
1224
  S3,
1225
+ SingleFileProcessUploader,
1017
1226
  SparkStudioStorageSDK,
1227
+ TemporaryFileDTO,
1018
1228
  UploadContainer,
1019
1229
  UploadDropzone,
1020
1230
  UploadFileToS3,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@sparkstudio/storage-ui",
3
- "version": "1.0.18",
3
+ "version": "1.0.19",
4
4
  "type": "module",
5
5
  "main": "dist/index.cjs",
6
6
  "module": "dist/index.js",