@omnikit-js/ui 0.9.21 → 0.9.23
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/{chunk-NHN5YSUE.js → chunk-7AYD5TRL.js} +3 -3
- package/dist/{chunk-NHN5YSUE.js.map → chunk-7AYD5TRL.js.map} +1 -1
- package/dist/{chunk-MTFFRTQW.js → chunk-FETPTCD3.js} +7 -7
- package/dist/chunk-FETPTCD3.js.map +1 -0
- package/dist/components/client/index.js +2 -2
- package/dist/components/server/index.js +2 -2
- package/dist/index.js +2 -2
- package/package.json +4 -2
- package/dist/chunk-2MKNXIZG.mjs +0 -6154
- package/dist/chunk-2MKNXIZG.mjs.map +0 -1
- package/dist/chunk-FA7CH4TP.mjs +0 -511
- package/dist/chunk-FA7CH4TP.mjs.map +0 -1
- package/dist/chunk-MTFFRTQW.js.map +0 -1
- package/dist/components/client/index.mjs +0 -1350
- package/dist/components/client/index.mjs.map +0 -1
- package/dist/components/server/index.mjs +0 -4
- package/dist/components/server/index.mjs.map +0 -1
- package/dist/index.mjs +0 -4
- package/dist/index.mjs.map +0 -1
|
@@ -1,1350 +0,0 @@
|
|
|
1
|
-
import { Button, Modal, TextInput, Spinner } from '../../chunk-2MKNXIZG.mjs';
|
|
2
|
-
export { AddPaymentMethodForm, BillingContent, Button, Modal } from '../../chunk-2MKNXIZG.mjs';
|
|
3
|
-
import { useState, useRef, useEffect, useCallback } from 'react';
|
|
4
|
-
import { jsxs, jsx, Fragment } from 'react/jsx-runtime';
|
|
5
|
-
|
|
6
|
-
function Tabs({ tabs, defaultTab, className = "" }) {
|
|
7
|
-
const [activeTab, setActiveTab] = useState(defaultTab || tabs[0]?.id);
|
|
8
|
-
return /* @__PURE__ */ jsxs("div", { className, children: [
|
|
9
|
-
/* @__PURE__ */ jsx("div", { className: "border-b border-gray-200 dark:border-gray-700", children: /* @__PURE__ */ jsx("nav", { className: "-mb-px flex space-x-8", children: tabs.map((tab) => /* @__PURE__ */ jsx(
|
|
10
|
-
"button",
|
|
11
|
-
{
|
|
12
|
-
onClick: () => setActiveTab(tab.id),
|
|
13
|
-
className: `
|
|
14
|
-
whitespace-nowrap border-b-2 px-1 pb-4 text-sm font-medium transition-colors
|
|
15
|
-
${activeTab === tab.id ? "border-blue-500 text-blue-600 dark:text-blue-400" : "border-transparent text-gray-500 hover:border-gray-300 hover:text-gray-700 dark:text-gray-400 dark:hover:text-gray-300"}
|
|
16
|
-
`,
|
|
17
|
-
children: tab.label
|
|
18
|
-
},
|
|
19
|
-
tab.id
|
|
20
|
-
)) }) }),
|
|
21
|
-
/* @__PURE__ */ jsx("div", { className: "mt-6", children: tabs.find((tab) => tab.id === activeTab)?.content })
|
|
22
|
-
] });
|
|
23
|
-
}
|
|
24
|
-
function PaymentMethodManager({ currentMethod }) {
|
|
25
|
-
const [isModalOpen, setIsModalOpen] = useState(false);
|
|
26
|
-
return /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
27
|
-
/* @__PURE__ */ jsxs("div", { children: [
|
|
28
|
-
/* @__PURE__ */ jsx("h3", { className: "text-lg font-semibold mb-4", children: "Current Payment Method" }),
|
|
29
|
-
/* @__PURE__ */ jsxs("div", { className: "flex items-center justify-between p-4 border border-gray-200 dark:border-gray-700 rounded-lg mb-6", children: [
|
|
30
|
-
/* @__PURE__ */ jsxs("div", { className: "flex items-center gap-4", children: [
|
|
31
|
-
/* @__PURE__ */ jsx("div", { className: "h-12 w-16 rounded bg-gradient-to-br from-blue-600 to-blue-800 flex items-center justify-center text-white font-bold text-sm", children: currentMethod.type }),
|
|
32
|
-
/* @__PURE__ */ jsxs("div", { children: [
|
|
33
|
-
/* @__PURE__ */ jsxs("p", { className: "font-medium", children: [
|
|
34
|
-
"\u2022\u2022\u2022\u2022 \u2022\u2022\u2022\u2022 \u2022\u2022\u2022\u2022 ",
|
|
35
|
-
currentMethod.last4
|
|
36
|
-
] }),
|
|
37
|
-
/* @__PURE__ */ jsxs("p", { className: "text-sm text-gray-600 dark:text-gray-400", children: [
|
|
38
|
-
"Expires ",
|
|
39
|
-
currentMethod.expiry
|
|
40
|
-
] })
|
|
41
|
-
] })
|
|
42
|
-
] }),
|
|
43
|
-
/* @__PURE__ */ jsx(
|
|
44
|
-
Button,
|
|
45
|
-
{
|
|
46
|
-
onClick: () => setIsModalOpen(true),
|
|
47
|
-
variant: "secondary",
|
|
48
|
-
size: "sm",
|
|
49
|
-
children: "Update"
|
|
50
|
-
}
|
|
51
|
-
)
|
|
52
|
-
] }),
|
|
53
|
-
/* @__PURE__ */ jsx(
|
|
54
|
-
Button,
|
|
55
|
-
{
|
|
56
|
-
onClick: () => setIsModalOpen(true),
|
|
57
|
-
className: "w-full",
|
|
58
|
-
children: "Add New Payment Method"
|
|
59
|
-
}
|
|
60
|
-
)
|
|
61
|
-
] }),
|
|
62
|
-
/* @__PURE__ */ jsx(
|
|
63
|
-
Modal,
|
|
64
|
-
{
|
|
65
|
-
isOpen: isModalOpen,
|
|
66
|
-
onClose: () => setIsModalOpen(false),
|
|
67
|
-
title: "Add Payment Method",
|
|
68
|
-
children: /* @__PURE__ */ jsxs("div", { className: "space-y-4", children: [
|
|
69
|
-
/* @__PURE__ */ jsxs("div", { children: [
|
|
70
|
-
/* @__PURE__ */ jsx("label", { className: "block text-sm font-medium text-gray-700 dark:text-gray-300 mb-2", children: "Cardholder Name" }),
|
|
71
|
-
/* @__PURE__ */ jsx(
|
|
72
|
-
"input",
|
|
73
|
-
{
|
|
74
|
-
type: "text",
|
|
75
|
-
placeholder: "John Doe",
|
|
76
|
-
className: "w-full rounded-lg border border-gray-300 dark:border-gray-600 bg-white dark:bg-gray-800 px-4 py-2 text-sm focus:border-blue-500 focus:outline-none focus:ring-2 focus:ring-blue-500/20"
|
|
77
|
-
}
|
|
78
|
-
)
|
|
79
|
-
] }),
|
|
80
|
-
/* @__PURE__ */ jsxs("div", { children: [
|
|
81
|
-
/* @__PURE__ */ jsx("label", { className: "block text-sm font-medium text-gray-700 dark:text-gray-300 mb-2", children: "Card Number" }),
|
|
82
|
-
/* @__PURE__ */ jsx(
|
|
83
|
-
"input",
|
|
84
|
-
{
|
|
85
|
-
type: "text",
|
|
86
|
-
placeholder: "1234 5678 9012 3456",
|
|
87
|
-
className: "w-full rounded-lg border border-gray-300 dark:border-gray-600 bg-white dark:bg-gray-800 px-4 py-2 text-sm focus:border-blue-500 focus:outline-none focus:ring-2 focus:ring-blue-500/20"
|
|
88
|
-
}
|
|
89
|
-
)
|
|
90
|
-
] }),
|
|
91
|
-
/* @__PURE__ */ jsxs("div", { className: "grid grid-cols-2 gap-4", children: [
|
|
92
|
-
/* @__PURE__ */ jsxs("div", { children: [
|
|
93
|
-
/* @__PURE__ */ jsx("label", { className: "block text-sm font-medium text-gray-700 dark:text-gray-300 mb-2", children: "Expiry Date" }),
|
|
94
|
-
/* @__PURE__ */ jsx(
|
|
95
|
-
"input",
|
|
96
|
-
{
|
|
97
|
-
type: "text",
|
|
98
|
-
placeholder: "MM/YY",
|
|
99
|
-
className: "w-full rounded-lg border border-gray-300 dark:border-gray-600 bg-white dark:bg-gray-800 px-4 py-2 text-sm focus:border-blue-500 focus:outline-none focus:ring-2 focus:ring-blue-500/20"
|
|
100
|
-
}
|
|
101
|
-
)
|
|
102
|
-
] }),
|
|
103
|
-
/* @__PURE__ */ jsxs("div", { children: [
|
|
104
|
-
/* @__PURE__ */ jsx("label", { className: "block text-sm font-medium text-gray-700 dark:text-gray-300 mb-2", children: "CVC" }),
|
|
105
|
-
/* @__PURE__ */ jsx(
|
|
106
|
-
"input",
|
|
107
|
-
{
|
|
108
|
-
type: "text",
|
|
109
|
-
placeholder: "123",
|
|
110
|
-
className: "w-full rounded-lg border border-gray-300 dark:border-gray-600 bg-white dark:bg-gray-800 px-4 py-2 text-sm focus:border-blue-500 focus:outline-none focus:ring-2 focus:ring-blue-500/20"
|
|
111
|
-
}
|
|
112
|
-
)
|
|
113
|
-
] })
|
|
114
|
-
] }),
|
|
115
|
-
/* @__PURE__ */ jsxs("div", { className: "flex items-center gap-2", children: [
|
|
116
|
-
/* @__PURE__ */ jsx(
|
|
117
|
-
"input",
|
|
118
|
-
{
|
|
119
|
-
type: "checkbox",
|
|
120
|
-
id: "setDefaultModal",
|
|
121
|
-
className: "rounded border-gray-300 dark:border-gray-600 text-blue-600 focus:ring-blue-500"
|
|
122
|
-
}
|
|
123
|
-
),
|
|
124
|
-
/* @__PURE__ */ jsx("label", { htmlFor: "setDefaultModal", className: "text-sm text-gray-700 dark:text-gray-300", children: "Set as default payment method" })
|
|
125
|
-
] }),
|
|
126
|
-
/* @__PURE__ */ jsxs("div", { className: "flex gap-3 pt-4", children: [
|
|
127
|
-
/* @__PURE__ */ jsx(
|
|
128
|
-
Button,
|
|
129
|
-
{
|
|
130
|
-
onClick: () => setIsModalOpen(false),
|
|
131
|
-
variant: "secondary",
|
|
132
|
-
className: "flex-1",
|
|
133
|
-
children: "Cancel"
|
|
134
|
-
}
|
|
135
|
-
),
|
|
136
|
-
/* @__PURE__ */ jsx(
|
|
137
|
-
Button,
|
|
138
|
-
{
|
|
139
|
-
onClick: () => setIsModalOpen(false),
|
|
140
|
-
className: "flex-1",
|
|
141
|
-
children: "Add Card"
|
|
142
|
-
}
|
|
143
|
-
)
|
|
144
|
-
] })
|
|
145
|
-
] })
|
|
146
|
-
}
|
|
147
|
-
)
|
|
148
|
-
] });
|
|
149
|
-
}
|
|
150
|
-
function LoginForm({
|
|
151
|
-
apiUrl,
|
|
152
|
-
projectId,
|
|
153
|
-
onSuccess,
|
|
154
|
-
onError,
|
|
155
|
-
showRememberMe = false,
|
|
156
|
-
allowUsername = false,
|
|
157
|
-
className = "",
|
|
158
|
-
registerLink,
|
|
159
|
-
forgotPasswordLink
|
|
160
|
-
}) {
|
|
161
|
-
const [state, setState] = useState({
|
|
162
|
-
email: "",
|
|
163
|
-
password: "",
|
|
164
|
-
rememberMe: false,
|
|
165
|
-
isLoading: false,
|
|
166
|
-
error: null
|
|
167
|
-
});
|
|
168
|
-
const handleSubmit = async (e) => {
|
|
169
|
-
e.preventDefault();
|
|
170
|
-
setState((prev) => ({ ...prev, isLoading: true, error: null }));
|
|
171
|
-
try {
|
|
172
|
-
const response = await fetch(`${apiUrl}/auth/login`, {
|
|
173
|
-
method: "POST",
|
|
174
|
-
headers: { "Content-Type": "application/json" },
|
|
175
|
-
body: JSON.stringify({
|
|
176
|
-
[allowUsername && !state.email.includes("@") ? "username" : "email"]: state.email,
|
|
177
|
-
password: state.password,
|
|
178
|
-
project_id: projectId
|
|
179
|
-
})
|
|
180
|
-
});
|
|
181
|
-
const data = await response.json();
|
|
182
|
-
if (!response.ok) {
|
|
183
|
-
throw new Error(data.error || "Login failed");
|
|
184
|
-
}
|
|
185
|
-
onSuccess?.(data);
|
|
186
|
-
} catch (err) {
|
|
187
|
-
const error = err instanceof Error ? err : new Error("Login failed");
|
|
188
|
-
setState((prev) => ({ ...prev, error: error.message }));
|
|
189
|
-
onError?.(error);
|
|
190
|
-
} finally {
|
|
191
|
-
setState((prev) => ({ ...prev, isLoading: false }));
|
|
192
|
-
}
|
|
193
|
-
};
|
|
194
|
-
return /* @__PURE__ */ jsxs("form", { onSubmit: handleSubmit, className: `space-y-4 ${className}`, children: [
|
|
195
|
-
state.error && /* @__PURE__ */ jsx("div", { className: "p-3 text-sm text-red-600 bg-red-50 dark:bg-red-900/20 dark:text-red-400 rounded-md", children: state.error }),
|
|
196
|
-
/* @__PURE__ */ jsx(
|
|
197
|
-
TextInput,
|
|
198
|
-
{
|
|
199
|
-
label: allowUsername ? "Email or Username" : "Email",
|
|
200
|
-
type: allowUsername ? "text" : "email",
|
|
201
|
-
value: state.email,
|
|
202
|
-
onChange: (e) => setState((prev) => ({ ...prev, email: e.target.value })),
|
|
203
|
-
placeholder: allowUsername ? "you@example.com or username" : "you@example.com",
|
|
204
|
-
required: true,
|
|
205
|
-
disabled: state.isLoading,
|
|
206
|
-
fullWidth: true,
|
|
207
|
-
autoComplete: "username"
|
|
208
|
-
}
|
|
209
|
-
),
|
|
210
|
-
/* @__PURE__ */ jsx(
|
|
211
|
-
TextInput,
|
|
212
|
-
{
|
|
213
|
-
label: "Password",
|
|
214
|
-
type: "password",
|
|
215
|
-
value: state.password,
|
|
216
|
-
onChange: (e) => setState((prev) => ({ ...prev, password: e.target.value })),
|
|
217
|
-
placeholder: "Enter your password",
|
|
218
|
-
required: true,
|
|
219
|
-
disabled: state.isLoading,
|
|
220
|
-
fullWidth: true,
|
|
221
|
-
autoComplete: "current-password"
|
|
222
|
-
}
|
|
223
|
-
),
|
|
224
|
-
showRememberMe && /* @__PURE__ */ jsxs("label", { className: "flex items-center gap-2 text-sm", children: [
|
|
225
|
-
/* @__PURE__ */ jsx(
|
|
226
|
-
"input",
|
|
227
|
-
{
|
|
228
|
-
type: "checkbox",
|
|
229
|
-
checked: state.rememberMe,
|
|
230
|
-
onChange: (e) => setState((prev) => ({ ...prev, rememberMe: e.target.checked })),
|
|
231
|
-
className: "rounded border-gray-300 text-blue-600 focus:ring-blue-500",
|
|
232
|
-
disabled: state.isLoading
|
|
233
|
-
}
|
|
234
|
-
),
|
|
235
|
-
/* @__PURE__ */ jsx("span", { className: "text-gray-700 dark:text-gray-300", children: "Remember me" })
|
|
236
|
-
] }),
|
|
237
|
-
/* @__PURE__ */ jsx(
|
|
238
|
-
Button,
|
|
239
|
-
{
|
|
240
|
-
type: "submit",
|
|
241
|
-
variant: "primary",
|
|
242
|
-
disabled: state.isLoading,
|
|
243
|
-
className: "w-full",
|
|
244
|
-
children: state.isLoading ? /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
245
|
-
/* @__PURE__ */ jsx(Spinner, { size: "sm", color: "white", className: "mr-2" }),
|
|
246
|
-
"Signing in..."
|
|
247
|
-
] }) : "Sign in"
|
|
248
|
-
}
|
|
249
|
-
),
|
|
250
|
-
(forgotPasswordLink || registerLink) && /* @__PURE__ */ jsxs("div", { className: "flex items-center justify-between text-sm", children: [
|
|
251
|
-
forgotPasswordLink && /* @__PURE__ */ jsx(
|
|
252
|
-
"a",
|
|
253
|
-
{
|
|
254
|
-
href: forgotPasswordLink,
|
|
255
|
-
className: "text-blue-600 hover:text-blue-700 dark:text-blue-400 dark:hover:text-blue-300",
|
|
256
|
-
children: "Forgot password?"
|
|
257
|
-
}
|
|
258
|
-
),
|
|
259
|
-
registerLink && /* @__PURE__ */ jsx(
|
|
260
|
-
"a",
|
|
261
|
-
{
|
|
262
|
-
href: registerLink,
|
|
263
|
-
className: "text-blue-600 hover:text-blue-700 dark:text-blue-400 dark:hover:text-blue-300",
|
|
264
|
-
children: "Create an account"
|
|
265
|
-
}
|
|
266
|
-
)
|
|
267
|
-
] })
|
|
268
|
-
] });
|
|
269
|
-
}
|
|
270
|
-
function RegisterForm({
|
|
271
|
-
apiUrl,
|
|
272
|
-
projectId,
|
|
273
|
-
onSuccess,
|
|
274
|
-
onError,
|
|
275
|
-
requireUsername = false,
|
|
276
|
-
showDisplayName = false,
|
|
277
|
-
showOrganizationName = false,
|
|
278
|
-
showTerms = false,
|
|
279
|
-
termsLink,
|
|
280
|
-
className = "",
|
|
281
|
-
loginLink
|
|
282
|
-
}) {
|
|
283
|
-
const [state, setState] = useState({
|
|
284
|
-
email: "",
|
|
285
|
-
username: "",
|
|
286
|
-
password: "",
|
|
287
|
-
confirmPassword: "",
|
|
288
|
-
displayName: "",
|
|
289
|
-
organizationName: "",
|
|
290
|
-
acceptTerms: false,
|
|
291
|
-
isLoading: false,
|
|
292
|
-
error: null,
|
|
293
|
-
fieldErrors: {}
|
|
294
|
-
});
|
|
295
|
-
const validateForm = () => {
|
|
296
|
-
const errors = {};
|
|
297
|
-
if (!state.email) {
|
|
298
|
-
errors.email = "Email is required";
|
|
299
|
-
} else if (!/^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(state.email)) {
|
|
300
|
-
errors.email = "Please enter a valid email address";
|
|
301
|
-
}
|
|
302
|
-
if (requireUsername && !state.username) {
|
|
303
|
-
errors.username = "Username is required";
|
|
304
|
-
} else if (state.username && state.username.length < 3) {
|
|
305
|
-
errors.username = "Username must be at least 3 characters";
|
|
306
|
-
}
|
|
307
|
-
if (!state.password) {
|
|
308
|
-
errors.password = "Password is required";
|
|
309
|
-
} else if (state.password.length < 6) {
|
|
310
|
-
errors.password = "Password must be at least 6 characters";
|
|
311
|
-
}
|
|
312
|
-
if (state.password !== state.confirmPassword) {
|
|
313
|
-
errors.confirmPassword = "Passwords do not match";
|
|
314
|
-
}
|
|
315
|
-
setState((prev) => ({ ...prev, fieldErrors: errors }));
|
|
316
|
-
return Object.keys(errors).length === 0;
|
|
317
|
-
};
|
|
318
|
-
const handleSubmit = async (e) => {
|
|
319
|
-
e.preventDefault();
|
|
320
|
-
if (!validateForm()) return;
|
|
321
|
-
if (showTerms && !state.acceptTerms) {
|
|
322
|
-
setState((prev) => ({ ...prev, error: "You must accept the terms and conditions" }));
|
|
323
|
-
return;
|
|
324
|
-
}
|
|
325
|
-
setState((prev) => ({ ...prev, isLoading: true, error: null }));
|
|
326
|
-
try {
|
|
327
|
-
const payload = {
|
|
328
|
-
email: state.email,
|
|
329
|
-
password: state.password,
|
|
330
|
-
project_id: projectId
|
|
331
|
-
};
|
|
332
|
-
if (state.username) {
|
|
333
|
-
payload.username = state.username;
|
|
334
|
-
}
|
|
335
|
-
if (state.displayName) {
|
|
336
|
-
payload.display_name = state.displayName;
|
|
337
|
-
}
|
|
338
|
-
if (state.organizationName) {
|
|
339
|
-
payload.organization_name = state.organizationName;
|
|
340
|
-
}
|
|
341
|
-
const response = await fetch(`${apiUrl}/auth/register`, {
|
|
342
|
-
method: "POST",
|
|
343
|
-
headers: { "Content-Type": "application/json" },
|
|
344
|
-
body: JSON.stringify(payload)
|
|
345
|
-
});
|
|
346
|
-
const data = await response.json();
|
|
347
|
-
if (!response.ok) {
|
|
348
|
-
throw new Error(data.error || "Registration failed");
|
|
349
|
-
}
|
|
350
|
-
onSuccess?.(data);
|
|
351
|
-
} catch (err) {
|
|
352
|
-
const error = err instanceof Error ? err : new Error("Registration failed");
|
|
353
|
-
setState((prev) => ({ ...prev, error: error.message }));
|
|
354
|
-
onError?.(error);
|
|
355
|
-
} finally {
|
|
356
|
-
setState((prev) => ({ ...prev, isLoading: false }));
|
|
357
|
-
}
|
|
358
|
-
};
|
|
359
|
-
return /* @__PURE__ */ jsxs("form", { onSubmit: handleSubmit, className: `space-y-4 ${className}`, children: [
|
|
360
|
-
state.error && /* @__PURE__ */ jsx("div", { className: "p-3 text-sm text-red-600 bg-red-50 dark:bg-red-900/20 dark:text-red-400 rounded-md", children: state.error }),
|
|
361
|
-
/* @__PURE__ */ jsx(
|
|
362
|
-
TextInput,
|
|
363
|
-
{
|
|
364
|
-
label: "Email",
|
|
365
|
-
type: "email",
|
|
366
|
-
value: state.email,
|
|
367
|
-
onChange: (e) => setState((prev) => ({ ...prev, email: e.target.value, fieldErrors: { ...prev.fieldErrors, email: void 0 } })),
|
|
368
|
-
placeholder: "you@example.com",
|
|
369
|
-
required: true,
|
|
370
|
-
disabled: state.isLoading,
|
|
371
|
-
fullWidth: true,
|
|
372
|
-
autoComplete: "email",
|
|
373
|
-
error: state.fieldErrors.email
|
|
374
|
-
}
|
|
375
|
-
),
|
|
376
|
-
requireUsername && /* @__PURE__ */ jsx(
|
|
377
|
-
TextInput,
|
|
378
|
-
{
|
|
379
|
-
label: "Username",
|
|
380
|
-
type: "text",
|
|
381
|
-
value: state.username,
|
|
382
|
-
onChange: (e) => setState((prev) => ({ ...prev, username: e.target.value, fieldErrors: { ...prev.fieldErrors, username: void 0 } })),
|
|
383
|
-
placeholder: "Choose a username",
|
|
384
|
-
required: true,
|
|
385
|
-
disabled: state.isLoading,
|
|
386
|
-
fullWidth: true,
|
|
387
|
-
autoComplete: "username",
|
|
388
|
-
error: state.fieldErrors.username
|
|
389
|
-
}
|
|
390
|
-
),
|
|
391
|
-
showDisplayName && /* @__PURE__ */ jsx(
|
|
392
|
-
TextInput,
|
|
393
|
-
{
|
|
394
|
-
label: "Display Name",
|
|
395
|
-
type: "text",
|
|
396
|
-
value: state.displayName,
|
|
397
|
-
onChange: (e) => setState((prev) => ({ ...prev, displayName: e.target.value })),
|
|
398
|
-
placeholder: "Your display name",
|
|
399
|
-
disabled: state.isLoading,
|
|
400
|
-
fullWidth: true
|
|
401
|
-
}
|
|
402
|
-
),
|
|
403
|
-
showOrganizationName && /* @__PURE__ */ jsx(
|
|
404
|
-
TextInput,
|
|
405
|
-
{
|
|
406
|
-
label: "Organization Name",
|
|
407
|
-
type: "text",
|
|
408
|
-
value: state.organizationName,
|
|
409
|
-
onChange: (e) => setState((prev) => ({ ...prev, organizationName: e.target.value })),
|
|
410
|
-
placeholder: "Your organization (optional)",
|
|
411
|
-
disabled: state.isLoading,
|
|
412
|
-
fullWidth: true
|
|
413
|
-
}
|
|
414
|
-
),
|
|
415
|
-
/* @__PURE__ */ jsx(
|
|
416
|
-
TextInput,
|
|
417
|
-
{
|
|
418
|
-
label: "Password",
|
|
419
|
-
type: "password",
|
|
420
|
-
value: state.password,
|
|
421
|
-
onChange: (e) => setState((prev) => ({ ...prev, password: e.target.value, fieldErrors: { ...prev.fieldErrors, password: void 0 } })),
|
|
422
|
-
placeholder: "At least 6 characters",
|
|
423
|
-
required: true,
|
|
424
|
-
disabled: state.isLoading,
|
|
425
|
-
fullWidth: true,
|
|
426
|
-
autoComplete: "new-password",
|
|
427
|
-
error: state.fieldErrors.password
|
|
428
|
-
}
|
|
429
|
-
),
|
|
430
|
-
/* @__PURE__ */ jsx(
|
|
431
|
-
TextInput,
|
|
432
|
-
{
|
|
433
|
-
label: "Confirm Password",
|
|
434
|
-
type: "password",
|
|
435
|
-
value: state.confirmPassword,
|
|
436
|
-
onChange: (e) => setState((prev) => ({ ...prev, confirmPassword: e.target.value, fieldErrors: { ...prev.fieldErrors, confirmPassword: void 0 } })),
|
|
437
|
-
placeholder: "Confirm your password",
|
|
438
|
-
required: true,
|
|
439
|
-
disabled: state.isLoading,
|
|
440
|
-
fullWidth: true,
|
|
441
|
-
autoComplete: "new-password",
|
|
442
|
-
error: state.fieldErrors.confirmPassword
|
|
443
|
-
}
|
|
444
|
-
),
|
|
445
|
-
showTerms && /* @__PURE__ */ jsxs("label", { className: "flex items-start gap-2 text-sm", children: [
|
|
446
|
-
/* @__PURE__ */ jsx(
|
|
447
|
-
"input",
|
|
448
|
-
{
|
|
449
|
-
type: "checkbox",
|
|
450
|
-
checked: state.acceptTerms,
|
|
451
|
-
onChange: (e) => setState((prev) => ({ ...prev, acceptTerms: e.target.checked })),
|
|
452
|
-
className: "mt-1 rounded border-gray-300 text-blue-600 focus:ring-blue-500",
|
|
453
|
-
disabled: state.isLoading
|
|
454
|
-
}
|
|
455
|
-
),
|
|
456
|
-
/* @__PURE__ */ jsxs("span", { className: "text-gray-700 dark:text-gray-300", children: [
|
|
457
|
-
"I agree to the",
|
|
458
|
-
" ",
|
|
459
|
-
termsLink ? /* @__PURE__ */ jsx("a", { href: termsLink, className: "text-blue-600 hover:underline", target: "_blank", rel: "noopener noreferrer", children: "terms and conditions" }) : "terms and conditions"
|
|
460
|
-
] })
|
|
461
|
-
] }),
|
|
462
|
-
/* @__PURE__ */ jsx(
|
|
463
|
-
Button,
|
|
464
|
-
{
|
|
465
|
-
type: "submit",
|
|
466
|
-
variant: "primary",
|
|
467
|
-
disabled: state.isLoading,
|
|
468
|
-
className: "w-full",
|
|
469
|
-
children: state.isLoading ? /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
470
|
-
/* @__PURE__ */ jsx(Spinner, { size: "sm", color: "white", className: "mr-2" }),
|
|
471
|
-
"Creating account..."
|
|
472
|
-
] }) : "Create account"
|
|
473
|
-
}
|
|
474
|
-
),
|
|
475
|
-
loginLink && /* @__PURE__ */ jsxs("p", { className: "text-center text-sm text-gray-600 dark:text-gray-400", children: [
|
|
476
|
-
"Already have an account?",
|
|
477
|
-
" ",
|
|
478
|
-
/* @__PURE__ */ jsx(
|
|
479
|
-
"a",
|
|
480
|
-
{
|
|
481
|
-
href: loginLink,
|
|
482
|
-
className: "text-blue-600 hover:text-blue-700 dark:text-blue-400 dark:hover:text-blue-300",
|
|
483
|
-
children: "Sign in"
|
|
484
|
-
}
|
|
485
|
-
)
|
|
486
|
-
] })
|
|
487
|
-
] });
|
|
488
|
-
}
|
|
489
|
-
function ForgotPasswordForm({
|
|
490
|
-
apiUrl,
|
|
491
|
-
projectId,
|
|
492
|
-
resetPasswordUrl,
|
|
493
|
-
onSuccess,
|
|
494
|
-
onError,
|
|
495
|
-
className = "",
|
|
496
|
-
loginLink
|
|
497
|
-
}) {
|
|
498
|
-
const [state, setState] = useState({
|
|
499
|
-
email: "",
|
|
500
|
-
isLoading: false,
|
|
501
|
-
isSuccess: false,
|
|
502
|
-
error: null
|
|
503
|
-
});
|
|
504
|
-
const handleSubmit = async (e) => {
|
|
505
|
-
e.preventDefault();
|
|
506
|
-
setState((prev) => ({ ...prev, isLoading: true, error: null }));
|
|
507
|
-
try {
|
|
508
|
-
const response = await fetch(`${apiUrl}/auth/forgot-password`, {
|
|
509
|
-
method: "POST",
|
|
510
|
-
headers: { "Content-Type": "application/json" },
|
|
511
|
-
body: JSON.stringify({
|
|
512
|
-
email: state.email,
|
|
513
|
-
project_id: projectId,
|
|
514
|
-
frontendUrl: resetPasswordUrl
|
|
515
|
-
})
|
|
516
|
-
});
|
|
517
|
-
const data = await response.json();
|
|
518
|
-
if (!response.ok) {
|
|
519
|
-
throw new Error(data.error || "Failed to send reset email");
|
|
520
|
-
}
|
|
521
|
-
setState((prev) => ({ ...prev, isSuccess: true }));
|
|
522
|
-
onSuccess?.();
|
|
523
|
-
} catch (err) {
|
|
524
|
-
const error = err instanceof Error ? err : new Error("Failed to send reset email");
|
|
525
|
-
setState((prev) => ({ ...prev, error: error.message }));
|
|
526
|
-
onError?.(error);
|
|
527
|
-
} finally {
|
|
528
|
-
setState((prev) => ({ ...prev, isLoading: false }));
|
|
529
|
-
}
|
|
530
|
-
};
|
|
531
|
-
if (state.isSuccess) {
|
|
532
|
-
return /* @__PURE__ */ jsxs("div", { className: `space-y-4 ${className}`, children: [
|
|
533
|
-
/* @__PURE__ */ jsxs("div", { className: "p-4 text-center rounded-md bg-green-50 dark:bg-green-900/20", children: [
|
|
534
|
-
/* @__PURE__ */ jsx(
|
|
535
|
-
"svg",
|
|
536
|
-
{
|
|
537
|
-
className: "w-12 h-12 mx-auto mb-3 text-green-500",
|
|
538
|
-
fill: "none",
|
|
539
|
-
stroke: "currentColor",
|
|
540
|
-
viewBox: "0 0 24 24",
|
|
541
|
-
children: /* @__PURE__ */ jsx(
|
|
542
|
-
"path",
|
|
543
|
-
{
|
|
544
|
-
strokeLinecap: "round",
|
|
545
|
-
strokeLinejoin: "round",
|
|
546
|
-
strokeWidth: 2,
|
|
547
|
-
d: "M3 8l7.89 5.26a2 2 0 002.22 0L21 8M5 19h14a2 2 0 002-2V7a2 2 0 00-2-2H5a2 2 0 00-2 2v10a2 2 0 002 2z"
|
|
548
|
-
}
|
|
549
|
-
)
|
|
550
|
-
}
|
|
551
|
-
),
|
|
552
|
-
/* @__PURE__ */ jsx("h3", { className: "text-lg font-medium text-green-800 dark:text-green-200", children: "Check your email" }),
|
|
553
|
-
/* @__PURE__ */ jsx("p", { className: "mt-2 text-sm text-green-700 dark:text-green-300", children: "If an account with that email exists, we've sent you a password reset link." })
|
|
554
|
-
] }),
|
|
555
|
-
loginLink && /* @__PURE__ */ jsx("p", { className: "text-center text-sm text-gray-600 dark:text-gray-400", children: /* @__PURE__ */ jsx(
|
|
556
|
-
"a",
|
|
557
|
-
{
|
|
558
|
-
href: loginLink,
|
|
559
|
-
className: "text-blue-600 hover:text-blue-700 dark:text-blue-400 dark:hover:text-blue-300",
|
|
560
|
-
children: "Back to sign in"
|
|
561
|
-
}
|
|
562
|
-
) })
|
|
563
|
-
] });
|
|
564
|
-
}
|
|
565
|
-
return /* @__PURE__ */ jsxs("form", { onSubmit: handleSubmit, className: `space-y-4 ${className}`, children: [
|
|
566
|
-
/* @__PURE__ */ jsxs("div", { className: "text-center mb-6", children: [
|
|
567
|
-
/* @__PURE__ */ jsx("h2", { className: "text-xl font-semibold text-gray-900 dark:text-white", children: "Forgot your password?" }),
|
|
568
|
-
/* @__PURE__ */ jsx("p", { className: "mt-2 text-sm text-gray-600 dark:text-gray-400", children: "Enter your email and we'll send you a reset link." })
|
|
569
|
-
] }),
|
|
570
|
-
state.error && /* @__PURE__ */ jsx("div", { className: "p-3 text-sm text-red-600 bg-red-50 dark:bg-red-900/20 dark:text-red-400 rounded-md", children: state.error }),
|
|
571
|
-
/* @__PURE__ */ jsx(
|
|
572
|
-
TextInput,
|
|
573
|
-
{
|
|
574
|
-
label: "Email",
|
|
575
|
-
type: "email",
|
|
576
|
-
value: state.email,
|
|
577
|
-
onChange: (e) => setState((prev) => ({ ...prev, email: e.target.value })),
|
|
578
|
-
placeholder: "you@example.com",
|
|
579
|
-
required: true,
|
|
580
|
-
disabled: state.isLoading,
|
|
581
|
-
fullWidth: true,
|
|
582
|
-
autoComplete: "email"
|
|
583
|
-
}
|
|
584
|
-
),
|
|
585
|
-
/* @__PURE__ */ jsx(
|
|
586
|
-
Button,
|
|
587
|
-
{
|
|
588
|
-
type: "submit",
|
|
589
|
-
variant: "primary",
|
|
590
|
-
disabled: state.isLoading,
|
|
591
|
-
className: "w-full",
|
|
592
|
-
children: state.isLoading ? /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
593
|
-
/* @__PURE__ */ jsx(Spinner, { size: "sm", color: "white", className: "mr-2" }),
|
|
594
|
-
"Sending..."
|
|
595
|
-
] }) : "Send reset link"
|
|
596
|
-
}
|
|
597
|
-
),
|
|
598
|
-
loginLink && /* @__PURE__ */ jsxs("p", { className: "text-center text-sm text-gray-600 dark:text-gray-400", children: [
|
|
599
|
-
"Remember your password?",
|
|
600
|
-
" ",
|
|
601
|
-
/* @__PURE__ */ jsx(
|
|
602
|
-
"a",
|
|
603
|
-
{
|
|
604
|
-
href: loginLink,
|
|
605
|
-
className: "text-blue-600 hover:text-blue-700 dark:text-blue-400 dark:hover:text-blue-300",
|
|
606
|
-
children: "Sign in"
|
|
607
|
-
}
|
|
608
|
-
)
|
|
609
|
-
] })
|
|
610
|
-
] });
|
|
611
|
-
}
|
|
612
|
-
function ResetPasswordForm({
|
|
613
|
-
apiUrl,
|
|
614
|
-
token,
|
|
615
|
-
onSuccess,
|
|
616
|
-
onError,
|
|
617
|
-
className = "",
|
|
618
|
-
loginLink
|
|
619
|
-
}) {
|
|
620
|
-
const [state, setState] = useState({
|
|
621
|
-
password: "",
|
|
622
|
-
confirmPassword: "",
|
|
623
|
-
isLoading: false,
|
|
624
|
-
isSuccess: false,
|
|
625
|
-
error: null,
|
|
626
|
-
fieldErrors: {}
|
|
627
|
-
});
|
|
628
|
-
const validateForm = () => {
|
|
629
|
-
const errors = {};
|
|
630
|
-
if (!state.password) {
|
|
631
|
-
errors.password = "Password is required";
|
|
632
|
-
} else if (state.password.length < 6) {
|
|
633
|
-
errors.password = "Password must be at least 6 characters";
|
|
634
|
-
}
|
|
635
|
-
if (state.password !== state.confirmPassword) {
|
|
636
|
-
errors.confirmPassword = "Passwords do not match";
|
|
637
|
-
}
|
|
638
|
-
setState((prev) => ({ ...prev, fieldErrors: errors }));
|
|
639
|
-
return Object.keys(errors).length === 0;
|
|
640
|
-
};
|
|
641
|
-
const handleSubmit = async (e) => {
|
|
642
|
-
e.preventDefault();
|
|
643
|
-
if (!validateForm()) return;
|
|
644
|
-
setState((prev) => ({ ...prev, isLoading: true, error: null }));
|
|
645
|
-
try {
|
|
646
|
-
const response = await fetch(`${apiUrl}/auth/reset-password`, {
|
|
647
|
-
method: "POST",
|
|
648
|
-
headers: { "Content-Type": "application/json" },
|
|
649
|
-
body: JSON.stringify({
|
|
650
|
-
token,
|
|
651
|
-
password: state.password,
|
|
652
|
-
sendConfirmationEmail: true
|
|
653
|
-
})
|
|
654
|
-
});
|
|
655
|
-
const data = await response.json();
|
|
656
|
-
if (!response.ok) {
|
|
657
|
-
throw new Error(data.error || "Failed to reset password");
|
|
658
|
-
}
|
|
659
|
-
setState((prev) => ({ ...prev, isSuccess: true }));
|
|
660
|
-
onSuccess?.();
|
|
661
|
-
} catch (err) {
|
|
662
|
-
const error = err instanceof Error ? err : new Error("Failed to reset password");
|
|
663
|
-
setState((prev) => ({ ...prev, error: error.message }));
|
|
664
|
-
onError?.(error);
|
|
665
|
-
} finally {
|
|
666
|
-
setState((prev) => ({ ...prev, isLoading: false }));
|
|
667
|
-
}
|
|
668
|
-
};
|
|
669
|
-
if (state.isSuccess) {
|
|
670
|
-
return /* @__PURE__ */ jsxs("div", { className: `space-y-4 ${className}`, children: [
|
|
671
|
-
/* @__PURE__ */ jsxs("div", { className: "p-4 text-center rounded-md bg-green-50 dark:bg-green-900/20", children: [
|
|
672
|
-
/* @__PURE__ */ jsx(
|
|
673
|
-
"svg",
|
|
674
|
-
{
|
|
675
|
-
className: "w-12 h-12 mx-auto mb-3 text-green-500",
|
|
676
|
-
fill: "none",
|
|
677
|
-
stroke: "currentColor",
|
|
678
|
-
viewBox: "0 0 24 24",
|
|
679
|
-
children: /* @__PURE__ */ jsx(
|
|
680
|
-
"path",
|
|
681
|
-
{
|
|
682
|
-
strokeLinecap: "round",
|
|
683
|
-
strokeLinejoin: "round",
|
|
684
|
-
strokeWidth: 2,
|
|
685
|
-
d: "M9 12l2 2 4-4m6 2a9 9 0 11-18 0 9 9 0 0118 0z"
|
|
686
|
-
}
|
|
687
|
-
)
|
|
688
|
-
}
|
|
689
|
-
),
|
|
690
|
-
/* @__PURE__ */ jsx("h3", { className: "text-lg font-medium text-green-800 dark:text-green-200", children: "Password reset successful" }),
|
|
691
|
-
/* @__PURE__ */ jsx("p", { className: "mt-2 text-sm text-green-700 dark:text-green-300", children: "Your password has been updated. You can now sign in with your new password." })
|
|
692
|
-
] }),
|
|
693
|
-
loginLink && /* @__PURE__ */ jsx("div", { className: "text-center", children: /* @__PURE__ */ jsx(
|
|
694
|
-
"a",
|
|
695
|
-
{
|
|
696
|
-
href: loginLink,
|
|
697
|
-
className: "inline-flex items-center justify-center px-4 py-2 text-sm font-medium text-white bg-blue-600 rounded-md hover:bg-blue-700",
|
|
698
|
-
children: "Go to sign in"
|
|
699
|
-
}
|
|
700
|
-
) })
|
|
701
|
-
] });
|
|
702
|
-
}
|
|
703
|
-
if (!token) {
|
|
704
|
-
return /* @__PURE__ */ jsxs("div", { className: `space-y-4 ${className}`, children: [
|
|
705
|
-
/* @__PURE__ */ jsxs("div", { className: "p-4 text-center rounded-md bg-red-50 dark:bg-red-900/20", children: [
|
|
706
|
-
/* @__PURE__ */ jsx(
|
|
707
|
-
"svg",
|
|
708
|
-
{
|
|
709
|
-
className: "w-12 h-12 mx-auto mb-3 text-red-500",
|
|
710
|
-
fill: "none",
|
|
711
|
-
stroke: "currentColor",
|
|
712
|
-
viewBox: "0 0 24 24",
|
|
713
|
-
children: /* @__PURE__ */ jsx(
|
|
714
|
-
"path",
|
|
715
|
-
{
|
|
716
|
-
strokeLinecap: "round",
|
|
717
|
-
strokeLinejoin: "round",
|
|
718
|
-
strokeWidth: 2,
|
|
719
|
-
d: "M12 9v2m0 4h.01m-6.938 4h13.856c1.54 0 2.502-1.667 1.732-3L13.732 4c-.77-1.333-2.694-1.333-3.464 0L3.34 16c-.77 1.333.192 3 1.732 3z"
|
|
720
|
-
}
|
|
721
|
-
)
|
|
722
|
-
}
|
|
723
|
-
),
|
|
724
|
-
/* @__PURE__ */ jsx("h3", { className: "text-lg font-medium text-red-800 dark:text-red-200", children: "Invalid reset link" }),
|
|
725
|
-
/* @__PURE__ */ jsx("p", { className: "mt-2 text-sm text-red-700 dark:text-red-300", children: "This password reset link is invalid or has expired." })
|
|
726
|
-
] }),
|
|
727
|
-
loginLink && /* @__PURE__ */ jsx("p", { className: "text-center text-sm text-gray-600 dark:text-gray-400", children: /* @__PURE__ */ jsx(
|
|
728
|
-
"a",
|
|
729
|
-
{
|
|
730
|
-
href: loginLink,
|
|
731
|
-
className: "text-blue-600 hover:text-blue-700 dark:text-blue-400 dark:hover:text-blue-300",
|
|
732
|
-
children: "Back to sign in"
|
|
733
|
-
}
|
|
734
|
-
) })
|
|
735
|
-
] });
|
|
736
|
-
}
|
|
737
|
-
return /* @__PURE__ */ jsxs("form", { onSubmit: handleSubmit, className: `space-y-4 ${className}`, children: [
|
|
738
|
-
/* @__PURE__ */ jsxs("div", { className: "text-center mb-6", children: [
|
|
739
|
-
/* @__PURE__ */ jsx("h2", { className: "text-xl font-semibold text-gray-900 dark:text-white", children: "Reset your password" }),
|
|
740
|
-
/* @__PURE__ */ jsx("p", { className: "mt-2 text-sm text-gray-600 dark:text-gray-400", children: "Enter your new password below." })
|
|
741
|
-
] }),
|
|
742
|
-
state.error && /* @__PURE__ */ jsx("div", { className: "p-3 text-sm text-red-600 bg-red-50 dark:bg-red-900/20 dark:text-red-400 rounded-md", children: state.error }),
|
|
743
|
-
/* @__PURE__ */ jsx(
|
|
744
|
-
TextInput,
|
|
745
|
-
{
|
|
746
|
-
label: "New Password",
|
|
747
|
-
type: "password",
|
|
748
|
-
value: state.password,
|
|
749
|
-
onChange: (e) => setState((prev) => ({ ...prev, password: e.target.value, fieldErrors: { ...prev.fieldErrors, password: void 0 } })),
|
|
750
|
-
placeholder: "At least 6 characters",
|
|
751
|
-
required: true,
|
|
752
|
-
disabled: state.isLoading,
|
|
753
|
-
fullWidth: true,
|
|
754
|
-
autoComplete: "new-password",
|
|
755
|
-
error: state.fieldErrors.password
|
|
756
|
-
}
|
|
757
|
-
),
|
|
758
|
-
/* @__PURE__ */ jsx(
|
|
759
|
-
TextInput,
|
|
760
|
-
{
|
|
761
|
-
label: "Confirm Password",
|
|
762
|
-
type: "password",
|
|
763
|
-
value: state.confirmPassword,
|
|
764
|
-
onChange: (e) => setState((prev) => ({ ...prev, confirmPassword: e.target.value, fieldErrors: { ...prev.fieldErrors, confirmPassword: void 0 } })),
|
|
765
|
-
placeholder: "Confirm your new password",
|
|
766
|
-
required: true,
|
|
767
|
-
disabled: state.isLoading,
|
|
768
|
-
fullWidth: true,
|
|
769
|
-
autoComplete: "new-password",
|
|
770
|
-
error: state.fieldErrors.confirmPassword
|
|
771
|
-
}
|
|
772
|
-
),
|
|
773
|
-
/* @__PURE__ */ jsx(
|
|
774
|
-
Button,
|
|
775
|
-
{
|
|
776
|
-
type: "submit",
|
|
777
|
-
variant: "primary",
|
|
778
|
-
disabled: state.isLoading,
|
|
779
|
-
className: "w-full",
|
|
780
|
-
children: state.isLoading ? /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
781
|
-
/* @__PURE__ */ jsx(Spinner, { size: "sm", color: "white", className: "mr-2" }),
|
|
782
|
-
"Resetting password..."
|
|
783
|
-
] }) : "Reset password"
|
|
784
|
-
}
|
|
785
|
-
),
|
|
786
|
-
loginLink && /* @__PURE__ */ jsxs("p", { className: "text-center text-sm text-gray-600 dark:text-gray-400", children: [
|
|
787
|
-
"Remember your password?",
|
|
788
|
-
" ",
|
|
789
|
-
/* @__PURE__ */ jsx(
|
|
790
|
-
"a",
|
|
791
|
-
{
|
|
792
|
-
href: loginLink,
|
|
793
|
-
className: "text-blue-600 hover:text-blue-700 dark:text-blue-400 dark:hover:text-blue-300",
|
|
794
|
-
children: "Sign in"
|
|
795
|
-
}
|
|
796
|
-
)
|
|
797
|
-
] })
|
|
798
|
-
] });
|
|
799
|
-
}
|
|
800
|
-
|
|
801
|
-
// src/components/client/FileUploader/upload-utils.ts
|
|
802
|
-
var DEFAULT_PART_SIZE = 5 * 1024 * 1024;
|
|
803
|
-
var DEFAULT_MULTIPART_THRESHOLD = 5 * 1024 * 1024;
|
|
804
|
-
var DEFAULT_MAX_CONCURRENT_PARTS = 5;
|
|
805
|
-
var EMA_ALPHA = 0.3;
|
|
806
|
-
function getAuthHeaders(config) {
|
|
807
|
-
const headers = {
|
|
808
|
-
"Content-Type": "application/json"
|
|
809
|
-
};
|
|
810
|
-
if (config.accessToken) {
|
|
811
|
-
headers["Authorization"] = `Bearer ${config.accessToken}`;
|
|
812
|
-
} else if (config.apiKey) {
|
|
813
|
-
headers["X-API-Key"] = config.apiKey;
|
|
814
|
-
}
|
|
815
|
-
return headers;
|
|
816
|
-
}
|
|
817
|
-
async function generatePresignedUrl(file, config, isMultipart, numParts) {
|
|
818
|
-
const response = await fetch(`${config.apiUrl}/storage/generate-presigned-url`, {
|
|
819
|
-
method: "POST",
|
|
820
|
-
headers: getAuthHeaders(config),
|
|
821
|
-
body: JSON.stringify({
|
|
822
|
-
file_name: file.name,
|
|
823
|
-
file_size: file.size,
|
|
824
|
-
mime_type: file.type || "application/octet-stream",
|
|
825
|
-
project_id: config.projectId,
|
|
826
|
-
bucket_id: config.bucketId,
|
|
827
|
-
folder_id: config.folderId,
|
|
828
|
-
access_level: config.accessLevel || "private",
|
|
829
|
-
is_multipart: isMultipart,
|
|
830
|
-
num_parts: numParts
|
|
831
|
-
})
|
|
832
|
-
});
|
|
833
|
-
if (!response.ok) {
|
|
834
|
-
const errorData = await response.json().catch(() => ({}));
|
|
835
|
-
throw new Error(errorData.error || "Failed to generate presigned URL");
|
|
836
|
-
}
|
|
837
|
-
return response.json();
|
|
838
|
-
}
|
|
839
|
-
async function confirmUpload(fileId, config, parts) {
|
|
840
|
-
const response = await fetch(`${config.apiUrl}/storage/confirm-upload`, {
|
|
841
|
-
method: "POST",
|
|
842
|
-
headers: getAuthHeaders(config),
|
|
843
|
-
body: JSON.stringify({
|
|
844
|
-
file_id: fileId,
|
|
845
|
-
parts
|
|
846
|
-
})
|
|
847
|
-
});
|
|
848
|
-
if (!response.ok) {
|
|
849
|
-
const errorData = await response.json().catch(() => ({}));
|
|
850
|
-
throw new Error(errorData.error || "Failed to confirm upload");
|
|
851
|
-
}
|
|
852
|
-
return response.json();
|
|
853
|
-
}
|
|
854
|
-
async function uploadSingleFile(file, config) {
|
|
855
|
-
const presignedResponse = await generatePresignedUrl(file, config, false);
|
|
856
|
-
const { file_id, upload_url } = presignedResponse.data;
|
|
857
|
-
if (!upload_url) {
|
|
858
|
-
throw new Error("No upload URL received");
|
|
859
|
-
}
|
|
860
|
-
const uploadResponse = await fetch(upload_url, {
|
|
861
|
-
method: "PUT",
|
|
862
|
-
body: file,
|
|
863
|
-
headers: {
|
|
864
|
-
"Content-Type": file.type || "application/octet-stream"
|
|
865
|
-
}
|
|
866
|
-
});
|
|
867
|
-
if (!uploadResponse.ok) {
|
|
868
|
-
throw new Error(`Upload failed: ${uploadResponse.status}`);
|
|
869
|
-
}
|
|
870
|
-
config.onProgress?.(file.size, file.size, 0);
|
|
871
|
-
const confirmResponse = await confirmUpload(file_id, config);
|
|
872
|
-
return {
|
|
873
|
-
id: confirmResponse.data.id,
|
|
874
|
-
name: confirmResponse.data.name,
|
|
875
|
-
size: confirmResponse.data.size_bytes,
|
|
876
|
-
mimeType: confirmResponse.data.mime_type,
|
|
877
|
-
url: confirmResponse.data.storage_url
|
|
878
|
-
};
|
|
879
|
-
}
|
|
880
|
-
async function uploadMultipartFile(file, config, abortSignal) {
|
|
881
|
-
const partSize = config.partSize || DEFAULT_PART_SIZE;
|
|
882
|
-
const maxConcurrentParts = config.maxConcurrentParts || DEFAULT_MAX_CONCURRENT_PARTS;
|
|
883
|
-
const numParts = Math.ceil(file.size / partSize);
|
|
884
|
-
const presignedResponse = await generatePresignedUrl(file, config, true, numParts);
|
|
885
|
-
const { file_id, parts } = presignedResponse.data;
|
|
886
|
-
if (!parts || parts.length === 0) {
|
|
887
|
-
throw new Error("No part URLs received");
|
|
888
|
-
}
|
|
889
|
-
const uploadedParts = [];
|
|
890
|
-
let bytesUploaded = 0;
|
|
891
|
-
const uploadStartTime = Date.now();
|
|
892
|
-
let smoothedSpeed = 0;
|
|
893
|
-
const partsQueue = parts.map((p) => ({ part_number: p.part_number, url: p.url }));
|
|
894
|
-
const inFlight = /* @__PURE__ */ new Map();
|
|
895
|
-
const uploadPart = async (part) => {
|
|
896
|
-
if (abortSignal?.aborted) {
|
|
897
|
-
throw new Error("Upload cancelled");
|
|
898
|
-
}
|
|
899
|
-
const start = (part.part_number - 1) * partSize;
|
|
900
|
-
const end = Math.min(start + partSize, file.size);
|
|
901
|
-
const partBlob = file.slice(start, end);
|
|
902
|
-
const partBytes = partBlob.size;
|
|
903
|
-
const uploadResponse = await fetch(part.url, {
|
|
904
|
-
method: "PUT",
|
|
905
|
-
body: partBlob,
|
|
906
|
-
headers: {
|
|
907
|
-
"Content-Type": file.type || "application/octet-stream"
|
|
908
|
-
},
|
|
909
|
-
signal: abortSignal
|
|
910
|
-
});
|
|
911
|
-
if (!uploadResponse.ok) {
|
|
912
|
-
throw new Error(`Part ${part.part_number} upload failed: ${uploadResponse.status}`);
|
|
913
|
-
}
|
|
914
|
-
const etag = uploadResponse.headers.get("ETag")?.replace(/"/g, "") || "";
|
|
915
|
-
return {
|
|
916
|
-
partNumber: part.part_number,
|
|
917
|
-
etag,
|
|
918
|
-
bytes: partBytes
|
|
919
|
-
};
|
|
920
|
-
};
|
|
921
|
-
const waitForAny = async () => {
|
|
922
|
-
const entries = Array.from(inFlight.entries());
|
|
923
|
-
const result = await Promise.race(entries.map(
|
|
924
|
-
([partNum, promise]) => promise.then((result2) => ({ partNum, result: result2 }))
|
|
925
|
-
));
|
|
926
|
-
inFlight.delete(result.partNum);
|
|
927
|
-
return result.result;
|
|
928
|
-
};
|
|
929
|
-
while (inFlight.size < maxConcurrentParts && partsQueue.length > 0) {
|
|
930
|
-
const part = partsQueue.shift();
|
|
931
|
-
inFlight.set(part.part_number, uploadPart(part));
|
|
932
|
-
}
|
|
933
|
-
while (inFlight.size > 0) {
|
|
934
|
-
const completed = await waitForAny();
|
|
935
|
-
uploadedParts.push({
|
|
936
|
-
partNumber: completed.partNumber,
|
|
937
|
-
etag: completed.etag,
|
|
938
|
-
bytes: completed.bytes
|
|
939
|
-
});
|
|
940
|
-
bytesUploaded += completed.bytes;
|
|
941
|
-
const elapsed = (Date.now() - uploadStartTime) / 1e3;
|
|
942
|
-
const instantSpeed = elapsed > 0 ? bytesUploaded / elapsed : 0;
|
|
943
|
-
smoothedSpeed = smoothedSpeed === 0 ? instantSpeed : EMA_ALPHA * instantSpeed + (1 - EMA_ALPHA) * smoothedSpeed;
|
|
944
|
-
config.onProgress?.(bytesUploaded, file.size, smoothedSpeed);
|
|
945
|
-
if (partsQueue.length > 0) {
|
|
946
|
-
const nextPart = partsQueue.shift();
|
|
947
|
-
inFlight.set(nextPart.part_number, uploadPart(nextPart));
|
|
948
|
-
}
|
|
949
|
-
}
|
|
950
|
-
const sortedParts = uploadedParts.sort((a, b) => a.partNumber - b.partNumber).map((p) => ({ partNumber: p.partNumber, etag: p.etag }));
|
|
951
|
-
const confirmResponse = await confirmUpload(file_id, config, sortedParts);
|
|
952
|
-
return {
|
|
953
|
-
id: confirmResponse.data.id,
|
|
954
|
-
name: confirmResponse.data.name,
|
|
955
|
-
size: confirmResponse.data.size_bytes,
|
|
956
|
-
mimeType: confirmResponse.data.mime_type,
|
|
957
|
-
url: confirmResponse.data.storage_url
|
|
958
|
-
};
|
|
959
|
-
}
|
|
960
|
-
async function uploadFile(file, config, abortSignal) {
|
|
961
|
-
const multipartThreshold = DEFAULT_MULTIPART_THRESHOLD;
|
|
962
|
-
if (file.size >= multipartThreshold) {
|
|
963
|
-
return uploadMultipartFile(file, config, abortSignal);
|
|
964
|
-
} else {
|
|
965
|
-
return uploadSingleFile(file, config);
|
|
966
|
-
}
|
|
967
|
-
}
|
|
968
|
-
function formatFileSize(bytes) {
|
|
969
|
-
if (bytes === 0) return "0 B";
|
|
970
|
-
const k = 1024;
|
|
971
|
-
const sizes = ["B", "KB", "MB", "GB", "TB"];
|
|
972
|
-
const i = Math.floor(Math.log(bytes) / Math.log(k));
|
|
973
|
-
return `${(bytes / Math.pow(k, i)).toFixed(1)} ${sizes[i]}`;
|
|
974
|
-
}
|
|
975
|
-
function formatSpeed(bytesPerSec) {
|
|
976
|
-
return `${formatFileSize(bytesPerSec)}/s`;
|
|
977
|
-
}
|
|
978
|
-
function generateUploadId() {
|
|
979
|
-
return `upload-${Date.now()}-${Math.random().toString(36).substr(2, 9)}`;
|
|
980
|
-
}
|
|
981
|
-
var DEFAULT_MAX_CONCURRENT_FILES = 3;
|
|
982
|
-
var DEFAULT_MULTIPART_THRESHOLD2 = 5 * 1024 * 1024;
|
|
983
|
-
function FileUploader({
|
|
984
|
-
apiUrl,
|
|
985
|
-
projectId,
|
|
986
|
-
bucketId,
|
|
987
|
-
folderId,
|
|
988
|
-
accessLevel = "private",
|
|
989
|
-
autoUpload = true,
|
|
990
|
-
maxConcurrentFiles = DEFAULT_MAX_CONCURRENT_FILES,
|
|
991
|
-
maxConcurrentParts = 5,
|
|
992
|
-
multipartThreshold = DEFAULT_MULTIPART_THRESHOLD2,
|
|
993
|
-
partSize = 5 * 1024 * 1024,
|
|
994
|
-
accept,
|
|
995
|
-
maxFileSize,
|
|
996
|
-
maxFiles = 10,
|
|
997
|
-
onUploadComplete,
|
|
998
|
-
onUploadError,
|
|
999
|
-
onAllUploadsComplete,
|
|
1000
|
-
onProgress,
|
|
1001
|
-
onFilesSelected,
|
|
1002
|
-
apiKey,
|
|
1003
|
-
accessToken,
|
|
1004
|
-
className = "",
|
|
1005
|
-
showFileList = true,
|
|
1006
|
-
showProgress = true,
|
|
1007
|
-
label,
|
|
1008
|
-
helperText,
|
|
1009
|
-
disabled = false
|
|
1010
|
-
}) {
|
|
1011
|
-
const [uploads, setUploads] = useState([]);
|
|
1012
|
-
const [isDragging, setIsDragging] = useState(false);
|
|
1013
|
-
const [overallProgress, setOverallProgress] = useState(null);
|
|
1014
|
-
const fileInputRef = useRef(null);
|
|
1015
|
-
const abortControllersRef = useRef(/* @__PURE__ */ new Map());
|
|
1016
|
-
const uploadedFilesRef = useRef([]);
|
|
1017
|
-
useEffect(() => {
|
|
1018
|
-
return () => {
|
|
1019
|
-
abortControllersRef.current.forEach((controller) => controller.abort());
|
|
1020
|
-
};
|
|
1021
|
-
}, []);
|
|
1022
|
-
const validateFile = useCallback((file) => {
|
|
1023
|
-
if (maxFileSize && file.size > maxFileSize) {
|
|
1024
|
-
return `File exceeds maximum size of ${formatFileSize(maxFileSize)}`;
|
|
1025
|
-
}
|
|
1026
|
-
return null;
|
|
1027
|
-
}, [maxFileSize]);
|
|
1028
|
-
const updateUploadState = useCallback((id, updates) => {
|
|
1029
|
-
setUploads((prev) => prev.map((u) => u.id === id ? { ...u, ...updates } : u));
|
|
1030
|
-
}, []);
|
|
1031
|
-
const calculateOverallProgress = useCallback(() => {
|
|
1032
|
-
setUploads((currentUploads) => {
|
|
1033
|
-
const totalBytes = currentUploads.reduce((sum, u) => sum + u.file.size, 0);
|
|
1034
|
-
const bytesUploaded = currentUploads.reduce((sum, u) => sum + u.bytesUploaded, 0);
|
|
1035
|
-
const completedFiles = currentUploads.filter((u) => u.status === "completed").length;
|
|
1036
|
-
const percentage = totalBytes > 0 ? Math.round(bytesUploaded / totalBytes * 100) : 0;
|
|
1037
|
-
const progress = {
|
|
1038
|
-
totalFiles: currentUploads.length,
|
|
1039
|
-
completedFiles,
|
|
1040
|
-
bytesUploaded,
|
|
1041
|
-
totalBytes,
|
|
1042
|
-
percentage,
|
|
1043
|
-
speed: 0,
|
|
1044
|
-
eta: 0
|
|
1045
|
-
};
|
|
1046
|
-
setOverallProgress(progress);
|
|
1047
|
-
onProgress?.(progress);
|
|
1048
|
-
return currentUploads;
|
|
1049
|
-
});
|
|
1050
|
-
}, [onProgress]);
|
|
1051
|
-
const uploadSingleFile2 = useCallback(async (uploadState) => {
|
|
1052
|
-
const { id, file } = uploadState;
|
|
1053
|
-
const abortController = new AbortController();
|
|
1054
|
-
abortControllersRef.current.set(id, abortController);
|
|
1055
|
-
const config = {
|
|
1056
|
-
apiUrl,
|
|
1057
|
-
projectId,
|
|
1058
|
-
bucketId,
|
|
1059
|
-
folderId,
|
|
1060
|
-
accessLevel,
|
|
1061
|
-
apiKey,
|
|
1062
|
-
accessToken,
|
|
1063
|
-
partSize,
|
|
1064
|
-
maxConcurrentParts,
|
|
1065
|
-
onProgress: (bytesUploaded, totalBytes, _speed) => {
|
|
1066
|
-
const progress = Math.round(bytesUploaded / totalBytes * 100);
|
|
1067
|
-
updateUploadState(id, { progress, bytesUploaded });
|
|
1068
|
-
calculateOverallProgress();
|
|
1069
|
-
}
|
|
1070
|
-
};
|
|
1071
|
-
try {
|
|
1072
|
-
updateUploadState(id, { status: "uploading", progress: 0 });
|
|
1073
|
-
const result = await uploadFile(file, config, abortController.signal);
|
|
1074
|
-
updateUploadState(id, {
|
|
1075
|
-
status: "completed",
|
|
1076
|
-
progress: 100,
|
|
1077
|
-
bytesUploaded: file.size,
|
|
1078
|
-
result
|
|
1079
|
-
});
|
|
1080
|
-
uploadedFilesRef.current.push(result);
|
|
1081
|
-
onUploadComplete?.(result);
|
|
1082
|
-
return result;
|
|
1083
|
-
} catch (error) {
|
|
1084
|
-
const errorMessage = error instanceof Error ? error.message : "Upload failed";
|
|
1085
|
-
updateUploadState(id, { status: "error", error: errorMessage });
|
|
1086
|
-
onUploadError?.(file, error instanceof Error ? error : new Error(errorMessage));
|
|
1087
|
-
throw error;
|
|
1088
|
-
} finally {
|
|
1089
|
-
abortControllersRef.current.delete(id);
|
|
1090
|
-
calculateOverallProgress();
|
|
1091
|
-
}
|
|
1092
|
-
}, [
|
|
1093
|
-
apiUrl,
|
|
1094
|
-
projectId,
|
|
1095
|
-
bucketId,
|
|
1096
|
-
folderId,
|
|
1097
|
-
accessLevel,
|
|
1098
|
-
apiKey,
|
|
1099
|
-
accessToken,
|
|
1100
|
-
partSize,
|
|
1101
|
-
maxConcurrentParts,
|
|
1102
|
-
updateUploadState,
|
|
1103
|
-
calculateOverallProgress,
|
|
1104
|
-
onUploadComplete,
|
|
1105
|
-
onUploadError
|
|
1106
|
-
]);
|
|
1107
|
-
const startUploads = useCallback(async (uploadStates) => {
|
|
1108
|
-
uploadedFilesRef.current = [];
|
|
1109
|
-
const pending = [...uploadStates];
|
|
1110
|
-
const inProgress = [];
|
|
1111
|
-
const processNext = async () => {
|
|
1112
|
-
while (pending.length > 0 && inProgress.length < maxConcurrentFiles) {
|
|
1113
|
-
const next = pending.shift();
|
|
1114
|
-
const promise = uploadSingleFile2(next).catch(() => {
|
|
1115
|
-
}).finally(() => {
|
|
1116
|
-
const index = inProgress.indexOf(promise);
|
|
1117
|
-
if (index > -1) inProgress.splice(index, 1);
|
|
1118
|
-
processNext();
|
|
1119
|
-
});
|
|
1120
|
-
inProgress.push(promise);
|
|
1121
|
-
}
|
|
1122
|
-
};
|
|
1123
|
-
await processNext();
|
|
1124
|
-
while (inProgress.length > 0) {
|
|
1125
|
-
await Promise.race(inProgress);
|
|
1126
|
-
}
|
|
1127
|
-
if (uploadedFilesRef.current.length > 0) {
|
|
1128
|
-
onAllUploadsComplete?.(uploadedFilesRef.current);
|
|
1129
|
-
}
|
|
1130
|
-
}, [maxConcurrentFiles, uploadSingleFile2, onAllUploadsComplete]);
|
|
1131
|
-
const handleFiles = useCallback((files) => {
|
|
1132
|
-
const fileArray = Array.from(files);
|
|
1133
|
-
if (uploads.length + fileArray.length > maxFiles) {
|
|
1134
|
-
const allowed = maxFiles - uploads.length;
|
|
1135
|
-
if (allowed <= 0) return;
|
|
1136
|
-
fileArray.splice(allowed);
|
|
1137
|
-
}
|
|
1138
|
-
const newUploads = [];
|
|
1139
|
-
for (const file of fileArray) {
|
|
1140
|
-
const error = validateFile(file);
|
|
1141
|
-
if (error) {
|
|
1142
|
-
onUploadError?.(file, new Error(error));
|
|
1143
|
-
continue;
|
|
1144
|
-
}
|
|
1145
|
-
newUploads.push({
|
|
1146
|
-
id: generateUploadId(),
|
|
1147
|
-
file,
|
|
1148
|
-
status: "pending",
|
|
1149
|
-
progress: 0,
|
|
1150
|
-
bytesUploaded: 0
|
|
1151
|
-
});
|
|
1152
|
-
}
|
|
1153
|
-
if (newUploads.length === 0) return;
|
|
1154
|
-
setUploads((prev) => [...prev, ...newUploads]);
|
|
1155
|
-
onFilesSelected?.(newUploads.map((u) => u.file));
|
|
1156
|
-
if (autoUpload) {
|
|
1157
|
-
setTimeout(() => startUploads(newUploads), 0);
|
|
1158
|
-
}
|
|
1159
|
-
}, [uploads.length, maxFiles, validateFile, autoUpload, startUploads, onFilesSelected, onUploadError]);
|
|
1160
|
-
const handleDrop = useCallback((e) => {
|
|
1161
|
-
e.preventDefault();
|
|
1162
|
-
setIsDragging(false);
|
|
1163
|
-
if (!disabled) {
|
|
1164
|
-
handleFiles(e.dataTransfer.files);
|
|
1165
|
-
}
|
|
1166
|
-
}, [disabled, handleFiles]);
|
|
1167
|
-
const handleDragOver = useCallback((e) => {
|
|
1168
|
-
e.preventDefault();
|
|
1169
|
-
if (!disabled) setIsDragging(true);
|
|
1170
|
-
}, [disabled]);
|
|
1171
|
-
const handleDragLeave = useCallback((e) => {
|
|
1172
|
-
e.preventDefault();
|
|
1173
|
-
setIsDragging(false);
|
|
1174
|
-
}, []);
|
|
1175
|
-
const handleClick = useCallback(() => {
|
|
1176
|
-
if (!disabled) {
|
|
1177
|
-
fileInputRef.current?.click();
|
|
1178
|
-
}
|
|
1179
|
-
}, [disabled]);
|
|
1180
|
-
const handleInputChange = useCallback((e) => {
|
|
1181
|
-
if (e.target.files) {
|
|
1182
|
-
handleFiles(e.target.files);
|
|
1183
|
-
e.target.value = "";
|
|
1184
|
-
}
|
|
1185
|
-
}, [handleFiles]);
|
|
1186
|
-
const handleRemoveFile = useCallback((id) => {
|
|
1187
|
-
const controller = abortControllersRef.current.get(id);
|
|
1188
|
-
if (controller) {
|
|
1189
|
-
controller.abort();
|
|
1190
|
-
abortControllersRef.current.delete(id);
|
|
1191
|
-
}
|
|
1192
|
-
setUploads((prev) => prev.filter((u) => u.id !== id));
|
|
1193
|
-
}, []);
|
|
1194
|
-
const handleRetry = useCallback((uploadState) => {
|
|
1195
|
-
updateUploadState(uploadState.id, { status: "pending", progress: 0, error: void 0, bytesUploaded: 0 });
|
|
1196
|
-
startUploads([uploadState]);
|
|
1197
|
-
}, [updateUploadState, startUploads]);
|
|
1198
|
-
const handleStartUpload = useCallback(() => {
|
|
1199
|
-
const pendingUploads = uploads.filter((u) => u.status === "pending");
|
|
1200
|
-
if (pendingUploads.length > 0) {
|
|
1201
|
-
startUploads(pendingUploads);
|
|
1202
|
-
}
|
|
1203
|
-
}, [uploads, startUploads]);
|
|
1204
|
-
const isUploading = uploads.some((u) => u.status === "uploading");
|
|
1205
|
-
const hasPendingFiles = uploads.some((u) => u.status === "pending");
|
|
1206
|
-
return /* @__PURE__ */ jsxs("div", { className: `w-full ${className}`, children: [
|
|
1207
|
-
label && /* @__PURE__ */ jsx("label", { className: "block text-sm font-medium text-gray-900 dark:text-gray-100 mb-2", children: label }),
|
|
1208
|
-
/* @__PURE__ */ jsxs(
|
|
1209
|
-
"div",
|
|
1210
|
-
{
|
|
1211
|
-
onDrop: handleDrop,
|
|
1212
|
-
onDragOver: handleDragOver,
|
|
1213
|
-
onDragLeave: handleDragLeave,
|
|
1214
|
-
onClick: handleClick,
|
|
1215
|
-
className: `
|
|
1216
|
-
relative border-2 border-dashed rounded-lg p-8 text-center cursor-pointer transition-all
|
|
1217
|
-
${isDragging ? "border-blue-500 bg-blue-50 dark:bg-blue-900/20" : "border-gray-300 dark:border-gray-600 hover:border-gray-400 dark:hover:border-gray-500"}
|
|
1218
|
-
${disabled ? "opacity-50 cursor-not-allowed" : ""}
|
|
1219
|
-
`,
|
|
1220
|
-
children: [
|
|
1221
|
-
/* @__PURE__ */ jsx(
|
|
1222
|
-
"input",
|
|
1223
|
-
{
|
|
1224
|
-
ref: fileInputRef,
|
|
1225
|
-
type: "file",
|
|
1226
|
-
accept,
|
|
1227
|
-
multiple: maxFiles > 1,
|
|
1228
|
-
onChange: handleInputChange,
|
|
1229
|
-
disabled,
|
|
1230
|
-
className: "hidden"
|
|
1231
|
-
}
|
|
1232
|
-
),
|
|
1233
|
-
/* @__PURE__ */ jsxs("div", { className: "flex flex-col items-center gap-2", children: [
|
|
1234
|
-
/* @__PURE__ */ jsx("div", { className: "w-12 h-12 rounded-full bg-gray-100 dark:bg-gray-800 flex items-center justify-center", children: /* @__PURE__ */ jsx("svg", { className: "w-6 h-6 text-gray-400", fill: "none", stroke: "currentColor", viewBox: "0 0 24 24", children: /* @__PURE__ */ jsx("path", { strokeLinecap: "round", strokeLinejoin: "round", strokeWidth: 2, d: "M7 16a4 4 0 01-.88-7.903A5 5 0 1115.9 6L16 6a5 5 0 011 9.9M15 13l-3-3m0 0l-3 3m3-3v12" }) }) }),
|
|
1235
|
-
/* @__PURE__ */ jsxs("div", { children: [
|
|
1236
|
-
/* @__PURE__ */ jsxs("p", { className: "text-sm font-medium text-gray-900 dark:text-gray-100", children: [
|
|
1237
|
-
/* @__PURE__ */ jsx("span", { className: "text-blue-600 dark:text-blue-400", children: "Click to upload" }),
|
|
1238
|
-
" or drag and drop"
|
|
1239
|
-
] }),
|
|
1240
|
-
/* @__PURE__ */ jsxs("p", { className: "text-xs text-gray-500 dark:text-gray-400 mt-1", children: [
|
|
1241
|
-
accept ? `Accepted: ${accept}` : "Any file type",
|
|
1242
|
-
maxFileSize && ` \xB7 Max: ${formatFileSize(maxFileSize)}`
|
|
1243
|
-
] })
|
|
1244
|
-
] })
|
|
1245
|
-
] })
|
|
1246
|
-
]
|
|
1247
|
-
}
|
|
1248
|
-
),
|
|
1249
|
-
helperText && /* @__PURE__ */ jsx("p", { className: "mt-2 text-sm text-gray-500 dark:text-gray-400", children: helperText }),
|
|
1250
|
-
showProgress && overallProgress && isUploading && /* @__PURE__ */ jsxs("div", { className: "mt-4 p-3 bg-gray-50 dark:bg-gray-800 rounded-lg", children: [
|
|
1251
|
-
/* @__PURE__ */ jsxs("div", { className: "flex items-center justify-between text-sm mb-2", children: [
|
|
1252
|
-
/* @__PURE__ */ jsxs("span", { className: "text-gray-700 dark:text-gray-300", children: [
|
|
1253
|
-
"Uploading ",
|
|
1254
|
-
overallProgress.completedFiles + 1,
|
|
1255
|
-
" of ",
|
|
1256
|
-
overallProgress.totalFiles
|
|
1257
|
-
] }),
|
|
1258
|
-
/* @__PURE__ */ jsxs("span", { className: "text-gray-500 dark:text-gray-400", children: [
|
|
1259
|
-
overallProgress.percentage,
|
|
1260
|
-
"%"
|
|
1261
|
-
] })
|
|
1262
|
-
] }),
|
|
1263
|
-
/* @__PURE__ */ jsx("div", { className: "w-full h-2 bg-gray-200 dark:bg-gray-700 rounded-full overflow-hidden", children: /* @__PURE__ */ jsx(
|
|
1264
|
-
"div",
|
|
1265
|
-
{
|
|
1266
|
-
className: "h-full bg-blue-600 transition-all duration-300",
|
|
1267
|
-
style: { width: `${overallProgress.percentage}%` }
|
|
1268
|
-
}
|
|
1269
|
-
) }),
|
|
1270
|
-
/* @__PURE__ */ jsxs("div", { className: "flex items-center justify-between text-xs text-gray-500 dark:text-gray-400 mt-1", children: [
|
|
1271
|
-
/* @__PURE__ */ jsxs("span", { children: [
|
|
1272
|
-
formatFileSize(overallProgress.bytesUploaded),
|
|
1273
|
-
" / ",
|
|
1274
|
-
formatFileSize(overallProgress.totalBytes)
|
|
1275
|
-
] }),
|
|
1276
|
-
overallProgress.speed > 0 && /* @__PURE__ */ jsx("span", { children: formatSpeed(overallProgress.speed) })
|
|
1277
|
-
] })
|
|
1278
|
-
] }),
|
|
1279
|
-
showFileList && uploads.length > 0 && /* @__PURE__ */ jsx("div", { className: "mt-4 space-y-2", children: uploads.map((upload) => /* @__PURE__ */ jsxs(
|
|
1280
|
-
"div",
|
|
1281
|
-
{
|
|
1282
|
-
className: "flex items-center gap-3 p-3 bg-gray-50 dark:bg-gray-800 rounded-lg border border-gray-200 dark:border-gray-700",
|
|
1283
|
-
children: [
|
|
1284
|
-
/* @__PURE__ */ jsx("div", { className: "w-10 h-10 rounded bg-gray-200 dark:bg-gray-700 flex items-center justify-center flex-shrink-0", children: upload.status === "uploading" ? /* @__PURE__ */ jsx(Spinner, { size: "sm" }) : upload.status === "completed" ? /* @__PURE__ */ jsx("svg", { className: "w-5 h-5 text-green-500", fill: "none", stroke: "currentColor", viewBox: "0 0 24 24", children: /* @__PURE__ */ jsx("path", { strokeLinecap: "round", strokeLinejoin: "round", strokeWidth: 2, d: "M5 13l4 4L19 7" }) }) : upload.status === "error" ? /* @__PURE__ */ jsx("svg", { className: "w-5 h-5 text-red-500", fill: "none", stroke: "currentColor", viewBox: "0 0 24 24", children: /* @__PURE__ */ jsx("path", { strokeLinecap: "round", strokeLinejoin: "round", strokeWidth: 2, d: "M6 18L18 6M6 6l12 12" }) }) : /* @__PURE__ */ jsx("svg", { className: "w-5 h-5 text-gray-400", fill: "none", stroke: "currentColor", viewBox: "0 0 24 24", children: /* @__PURE__ */ jsx("path", { strokeLinecap: "round", strokeLinejoin: "round", strokeWidth: 2, d: "M9 12h6m-6 4h6m2 5H7a2 2 0 01-2-2V5a2 2 0 012-2h5.586a1 1 0 01.707.293l5.414 5.414a1 1 0 01.293.707V19a2 2 0 01-2 2z" }) }) }),
|
|
1285
|
-
/* @__PURE__ */ jsxs("div", { className: "flex-1 min-w-0", children: [
|
|
1286
|
-
/* @__PURE__ */ jsx("p", { className: "text-sm font-medium text-gray-900 dark:text-gray-100 truncate", children: upload.file.name }),
|
|
1287
|
-
/* @__PURE__ */ jsxs("div", { className: "flex items-center gap-2 text-xs text-gray-500 dark:text-gray-400", children: [
|
|
1288
|
-
/* @__PURE__ */ jsx("span", { children: formatFileSize(upload.file.size) }),
|
|
1289
|
-
upload.status === "uploading" && /* @__PURE__ */ jsxs("span", { children: [
|
|
1290
|
-
"\xB7 ",
|
|
1291
|
-
upload.progress,
|
|
1292
|
-
"%"
|
|
1293
|
-
] }),
|
|
1294
|
-
upload.status === "error" && /* @__PURE__ */ jsx("span", { className: "text-red-500", children: upload.error }),
|
|
1295
|
-
upload.file.size >= multipartThreshold && upload.status === "pending" && /* @__PURE__ */ jsx("span", { className: "text-blue-500", children: "Multipart" })
|
|
1296
|
-
] }),
|
|
1297
|
-
upload.status === "uploading" && /* @__PURE__ */ jsx("div", { className: "w-full h-1 bg-gray-200 dark:bg-gray-700 rounded-full mt-2 overflow-hidden", children: /* @__PURE__ */ jsx(
|
|
1298
|
-
"div",
|
|
1299
|
-
{
|
|
1300
|
-
className: "h-full bg-blue-600 transition-all duration-300",
|
|
1301
|
-
style: { width: `${upload.progress}%` }
|
|
1302
|
-
}
|
|
1303
|
-
) })
|
|
1304
|
-
] }),
|
|
1305
|
-
/* @__PURE__ */ jsxs("div", { className: "flex items-center gap-1 flex-shrink-0", children: [
|
|
1306
|
-
upload.status === "error" && /* @__PURE__ */ jsx(
|
|
1307
|
-
"button",
|
|
1308
|
-
{
|
|
1309
|
-
onClick: () => handleRetry(upload),
|
|
1310
|
-
className: "p-1 text-gray-400 hover:text-blue-500 transition-colors",
|
|
1311
|
-
title: "Retry",
|
|
1312
|
-
children: /* @__PURE__ */ jsx("svg", { className: "w-4 h-4", fill: "none", stroke: "currentColor", viewBox: "0 0 24 24", children: /* @__PURE__ */ jsx("path", { strokeLinecap: "round", strokeLinejoin: "round", strokeWidth: 2, d: "M4 4v5h.582m15.356 2A8.001 8.001 0 004.582 9m0 0H9m11 11v-5h-.581m0 0a8.003 8.003 0 01-15.357-2m15.357 2H15" }) })
|
|
1313
|
-
}
|
|
1314
|
-
),
|
|
1315
|
-
/* @__PURE__ */ jsx(
|
|
1316
|
-
"button",
|
|
1317
|
-
{
|
|
1318
|
-
onClick: () => handleRemoveFile(upload.id),
|
|
1319
|
-
className: "p-1 text-gray-400 hover:text-red-500 transition-colors",
|
|
1320
|
-
title: "Remove",
|
|
1321
|
-
children: /* @__PURE__ */ jsx("svg", { className: "w-4 h-4", fill: "none", stroke: "currentColor", viewBox: "0 0 24 24", children: /* @__PURE__ */ jsx("path", { strokeLinecap: "round", strokeLinejoin: "round", strokeWidth: 2, d: "M6 18L18 6M6 6l12 12" }) })
|
|
1322
|
-
}
|
|
1323
|
-
)
|
|
1324
|
-
] })
|
|
1325
|
-
]
|
|
1326
|
-
},
|
|
1327
|
-
upload.id
|
|
1328
|
-
)) }),
|
|
1329
|
-
!autoUpload && hasPendingFiles && /* @__PURE__ */ jsx("div", { className: "mt-4", children: /* @__PURE__ */ jsx(
|
|
1330
|
-
Button,
|
|
1331
|
-
{
|
|
1332
|
-
onClick: handleStartUpload,
|
|
1333
|
-
disabled: isUploading || disabled,
|
|
1334
|
-
variant: "primary",
|
|
1335
|
-
className: "w-full",
|
|
1336
|
-
children: isUploading ? /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
1337
|
-
/* @__PURE__ */ jsx(Spinner, { size: "sm", color: "white", className: "mr-2" }),
|
|
1338
|
-
"Uploading..."
|
|
1339
|
-
] }) : `Upload ${uploads.filter((u) => u.status === "pending").length} file(s)`
|
|
1340
|
-
}
|
|
1341
|
-
) })
|
|
1342
|
-
] });
|
|
1343
|
-
}
|
|
1344
|
-
|
|
1345
|
-
// src/components/client/index.ts
|
|
1346
|
-
var OMNIKIT_CLIENT_VERSION = "0.9.11";
|
|
1347
|
-
|
|
1348
|
-
export { FileUploader, ForgotPasswordForm, LoginForm, OMNIKIT_CLIENT_VERSION, PaymentMethodManager, RegisterForm, ResetPasswordForm, Tabs };
|
|
1349
|
-
//# sourceMappingURL=index.mjs.map
|
|
1350
|
-
//# sourceMappingURL=index.mjs.map
|