@datatechsolutions/ui 2.11.86 → 2.11.88

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.
Files changed (75) hide show
  1. package/dist/billing-panel-DsHhhJqG.d.mts +18 -0
  2. package/dist/billing-panel-DsHhhJqG.d.ts +18 -0
  3. package/dist/chunk-4667D2ZT.mjs +61 -0
  4. package/dist/chunk-4667D2ZT.mjs.map +1 -0
  5. package/dist/chunk-5HXDJBVX.mjs +1330 -0
  6. package/dist/chunk-5HXDJBVX.mjs.map +1 -0
  7. package/dist/chunk-DJ33CSGJ.mjs +126 -0
  8. package/dist/chunk-DJ33CSGJ.mjs.map +1 -0
  9. package/dist/chunk-F4TOOARV.mjs +503 -0
  10. package/dist/chunk-F4TOOARV.mjs.map +1 -0
  11. package/dist/chunk-GEUGFYLO.mjs +237 -0
  12. package/dist/chunk-GEUGFYLO.mjs.map +1 -0
  13. package/dist/chunk-LBALE4JX.js +1342 -0
  14. package/dist/chunk-LBALE4JX.js.map +1 -0
  15. package/dist/chunk-MXFEU7A6.js +148 -0
  16. package/dist/chunk-MXFEU7A6.js.map +1 -0
  17. package/dist/chunk-NBCOVUQP.mjs +142 -0
  18. package/dist/chunk-NBCOVUQP.mjs.map +1 -0
  19. package/dist/chunk-P4RVGMZL.js +128 -0
  20. package/dist/chunk-P4RVGMZL.js.map +1 -0
  21. package/dist/chunk-Q2MG7S2E.js +239 -0
  22. package/dist/chunk-Q2MG7S2E.js.map +1 -0
  23. package/dist/chunk-RV555OEO.mjs +1009 -0
  24. package/dist/chunk-RV555OEO.mjs.map +1 -0
  25. package/dist/chunk-SAYVWIMJ.js +63 -0
  26. package/dist/chunk-SAYVWIMJ.js.map +1 -0
  27. package/dist/chunk-SUHNSUMH.mjs +1021 -0
  28. package/dist/chunk-SUHNSUMH.mjs.map +1 -0
  29. package/dist/chunk-TOEMSC4P.mjs +99 -0
  30. package/dist/chunk-TOEMSC4P.mjs.map +1 -0
  31. package/dist/chunk-UUHV5KHF.js +505 -0
  32. package/dist/chunk-UUHV5KHF.js.map +1 -0
  33. package/dist/chunk-UVEPTYZC.js +101 -0
  34. package/dist/chunk-UVEPTYZC.js.map +1 -0
  35. package/dist/chunk-X2KCCQPL.js +1049 -0
  36. package/dist/chunk-X2KCCQPL.js.map +1 -0
  37. package/dist/chunk-ZARCUQA6.js +1015 -0
  38. package/dist/chunk-ZARCUQA6.js.map +1 -0
  39. package/dist/platform/admin/index.d.mts +17 -0
  40. package/dist/platform/admin/index.d.ts +17 -0
  41. package/dist/platform/admin/index.js +39 -0
  42. package/dist/platform/admin/index.js.map +1 -0
  43. package/dist/platform/admin/index.mjs +10 -0
  44. package/dist/platform/admin/index.mjs.map +1 -0
  45. package/dist/platform/auth/index.d.mts +73 -0
  46. package/dist/platform/auth/index.d.ts +73 -0
  47. package/dist/platform/auth/index.js +107 -0
  48. package/dist/platform/auth/index.js.map +1 -0
  49. package/dist/platform/auth/index.mjs +10 -0
  50. package/dist/platform/auth/index.mjs.map +1 -0
  51. package/dist/platform/billing/index.d.mts +29 -0
  52. package/dist/platform/billing/index.d.ts +29 -0
  53. package/dist/platform/billing/index.js +22 -0
  54. package/dist/platform/billing/index.js.map +1 -0
  55. package/dist/platform/billing/index.mjs +9 -0
  56. package/dist/platform/billing/index.mjs.map +1 -0
  57. package/dist/platform/impersonation/index.d.mts +19 -0
  58. package/dist/platform/impersonation/index.d.ts +19 -0
  59. package/dist/platform/impersonation/index.js +17 -0
  60. package/dist/platform/impersonation/index.js.map +1 -0
  61. package/dist/platform/impersonation/index.mjs +8 -0
  62. package/dist/platform/impersonation/index.mjs.map +1 -0
  63. package/dist/platform/index.d.mts +45 -2
  64. package/dist/platform/index.d.ts +45 -2
  65. package/dist/platform/index.js +4850 -0
  66. package/dist/platform/index.js.map +1 -1
  67. package/dist/platform/index.mjs +4716 -3
  68. package/dist/platform/index.mjs.map +1 -1
  69. package/dist/platform/settings/index.d.mts +31 -0
  70. package/dist/platform/settings/index.d.ts +31 -0
  71. package/dist/platform/settings/index.js +21 -0
  72. package/dist/platform/settings/index.js.map +1 -0
  73. package/dist/platform/settings/index.mjs +12 -0
  74. package/dist/platform/settings/index.mjs.map +1 -0
  75. package/package.json +26 -1
@@ -0,0 +1,1049 @@
1
+ "use client";
2
+ 'use strict';
3
+
4
+ var chunkMXFEU7A6_js = require('./chunk-MXFEU7A6.js');
5
+ var chunkKNXAOJAK_js = require('./chunk-KNXAOJAK.js');
6
+ var chunkUZ3CMNUJ_js = require('./chunk-UZ3CMNUJ.js');
7
+ var chunkYXN2K77G_js = require('./chunk-YXN2K77G.js');
8
+ var react = require('react');
9
+ var outline = require('@heroicons/react/24/outline');
10
+ var client = require('@datatechsolutions/windsock/client');
11
+ var jsxRuntime = require('react/jsx-runtime');
12
+ var solid = require('@heroicons/react/20/solid');
13
+ var sharedI18n = require('@datatechsolutions/shared-domain/i18n');
14
+
15
+ function _interopNamespace(e) {
16
+ if (e && e.__esModule) return e;
17
+ var n = Object.create(null);
18
+ if (e) {
19
+ Object.keys(e).forEach(function (k) {
20
+ if (k !== 'default') {
21
+ var d = Object.getOwnPropertyDescriptor(e, k);
22
+ Object.defineProperty(n, k, d.get ? d : {
23
+ enumerable: true,
24
+ get: function () { return e[k]; }
25
+ });
26
+ }
27
+ });
28
+ }
29
+ n.default = e;
30
+ return Object.freeze(n);
31
+ }
32
+
33
+ var sharedI18n__namespace = /*#__PURE__*/_interopNamespace(sharedI18n);
34
+
35
+ function MfaSetup({
36
+ onSuccess,
37
+ onCancel,
38
+ enforcement = "optional",
39
+ onGenerateSecret,
40
+ onVerifySetup
41
+ }) {
42
+ const t = chunkYXN2K77G_js.useTranslations("windsock");
43
+ const { client: client$1 } = client.useAuth();
44
+ const setupSteps = [
45
+ {
46
+ id: "method",
47
+ title: t("mfa.setup.stepMethodTitle"),
48
+ description: t("mfa.setup.stepMethodDescription")
49
+ },
50
+ {
51
+ id: "scan",
52
+ title: t("mfa.setup.stepScanTitle"),
53
+ description: t("mfa.setup.stepScanDescription")
54
+ },
55
+ {
56
+ id: "verify",
57
+ title: t("mfa.setup.stepVerifyTitle"),
58
+ description: t("mfa.setup.stepVerifyDescription")
59
+ },
60
+ {
61
+ id: "backup",
62
+ title: t("mfa.setup.stepBackupTitle"),
63
+ description: t("mfa.setup.stepBackupDescription")
64
+ }
65
+ ];
66
+ const [currentStep, setCurrentStep] = react.useState(0);
67
+ const [selectedMethod, setSelectedMethod] = react.useState("totp");
68
+ const [qrCodeUri, setQrCodeUri] = react.useState("");
69
+ const [secret, setSecret] = react.useState("");
70
+ const [verifyCode, setVerifyCode] = react.useState("");
71
+ const [verifyError, setVerifyError] = react.useState(null);
72
+ const [backupCodes, setBackupCodes] = react.useState([]);
73
+ const [savedBackupCodes, setSavedBackupCodes] = react.useState(false);
74
+ const [isLoading, setIsLoading] = react.useState(false);
75
+ const handleStepChange = react.useCallback(
76
+ async (step) => {
77
+ if (step === 1 && currentStep === 0) {
78
+ setIsLoading(true);
79
+ try {
80
+ if (onGenerateSecret) {
81
+ const result = await onGenerateSecret();
82
+ setSecret(result.secret);
83
+ setQrCodeUri(result.qrCodeUri);
84
+ } else {
85
+ const result = await client$1.generateMfaSecret();
86
+ setSecret(result.secret);
87
+ setQrCodeUri(result.qrCode);
88
+ setBackupCodes(result.backupCodes);
89
+ }
90
+ } catch {
91
+ return;
92
+ } finally {
93
+ setIsLoading(false);
94
+ }
95
+ }
96
+ chunkUZ3CMNUJ_js.triggerHaptic("light");
97
+ setCurrentStep(step);
98
+ },
99
+ [currentStep, onGenerateSecret, client$1]
100
+ );
101
+ const handleMethodSelect = react.useCallback((method) => {
102
+ chunkUZ3CMNUJ_js.triggerHaptic("light");
103
+ setSelectedMethod(method);
104
+ }, []);
105
+ const handleVerifyComplete = react.useCallback(
106
+ async (code) => {
107
+ setVerifyError(null);
108
+ setIsLoading(true);
109
+ try {
110
+ if (onVerifySetup) {
111
+ const result = await onVerifySetup(code);
112
+ if (result.success) {
113
+ chunkUZ3CMNUJ_js.triggerHaptic("success");
114
+ if (result.backupCodes?.length) {
115
+ setBackupCodes(result.backupCodes);
116
+ }
117
+ setCurrentStep(3);
118
+ } else {
119
+ chunkUZ3CMNUJ_js.triggerHaptic("error");
120
+ setVerifyError(t("mfa.setup.errorInvalidCode"));
121
+ }
122
+ } else {
123
+ const result = await client$1.verifyMfaSetup(code);
124
+ if (result.success) {
125
+ chunkUZ3CMNUJ_js.triggerHaptic("success");
126
+ setCurrentStep(3);
127
+ } else {
128
+ chunkUZ3CMNUJ_js.triggerHaptic("error");
129
+ setVerifyError(t("mfa.setup.errorInvalidCode"));
130
+ }
131
+ }
132
+ } catch {
133
+ chunkUZ3CMNUJ_js.triggerHaptic("error");
134
+ setVerifyError(t("mfa.setup.errorVerificationFailed"));
135
+ } finally {
136
+ setIsLoading(false);
137
+ }
138
+ },
139
+ [onVerifySetup, client$1, t]
140
+ );
141
+ const handleSubmit = react.useCallback(() => {
142
+ chunkUZ3CMNUJ_js.triggerHaptic("success");
143
+ onSuccess?.();
144
+ }, [onSuccess]);
145
+ const handleCancel = react.useCallback(() => {
146
+ chunkUZ3CMNUJ_js.triggerHaptic("light");
147
+ onCancel?.();
148
+ }, [onCancel]);
149
+ const canAdvance = (() => {
150
+ switch (currentStep) {
151
+ case 0:
152
+ return !!selectedMethod;
153
+ case 1:
154
+ return !!qrCodeUri;
155
+ case 2:
156
+ return false;
157
+ // Advance via OTP complete callback
158
+ case 3:
159
+ return savedBackupCodes;
160
+ default:
161
+ return false;
162
+ }
163
+ })();
164
+ return /* @__PURE__ */ jsxRuntime.jsxs(
165
+ chunkKNXAOJAK_js.StepFormPage,
166
+ {
167
+ title: t("mfa.setup.title"),
168
+ subtitle: enforcement === "required" ? t("mfa.setup.subtitleRequired") : t("mfa.setup.subtitleOptional"),
169
+ label: t("mfa.setup.label"),
170
+ icon: /* @__PURE__ */ jsxRuntime.jsx(outline.ShieldCheckIcon, { className: "h-5 w-5" }),
171
+ gradient: "from-emerald-500 via-teal-500 to-cyan-500",
172
+ steps: setupSteps,
173
+ currentStep,
174
+ onStepChange: handleStepChange,
175
+ onSubmit: handleSubmit,
176
+ onCancel: handleCancel,
177
+ isSubmitting: isLoading,
178
+ submitLabel: t("mfa.setup.submitLabel"),
179
+ canAdvance,
180
+ children: [
181
+ currentStep === 0 && /* @__PURE__ */ jsxRuntime.jsx(
182
+ chunkKNXAOJAK_js.OptionGrid,
183
+ {
184
+ icon: /* @__PURE__ */ jsxRuntime.jsx(outline.ShieldCheckIcon, { className: "h-4 w-4" }),
185
+ title: t("mfa.setup.methodGridTitle"),
186
+ options: [
187
+ {
188
+ code: "totp",
189
+ flag: "\u{1F4F1}",
190
+ label: t("mfa.setup.methodAuthenticatorApp")
191
+ }
192
+ ],
193
+ selected: selectedMethod,
194
+ onSelect: handleMethodSelect,
195
+ columns: 2
196
+ }
197
+ ),
198
+ currentStep === 1 && /* @__PURE__ */ jsxRuntime.jsx("div", { className: "space-y-4", children: isLoading ? /* @__PURE__ */ jsxRuntime.jsx("div", { className: "flex justify-center py-8", children: /* @__PURE__ */ jsxRuntime.jsx(chunkKNXAOJAK_js.Spinner, {}) }) : /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
199
+ qrCodeUri && /* @__PURE__ */ jsxRuntime.jsx("div", { className: "flex justify-center", children: /* @__PURE__ */ jsxRuntime.jsx("div", { className: "rounded-xl bg-white p-4", children: /* @__PURE__ */ jsxRuntime.jsx(
200
+ "img",
201
+ {
202
+ src: qrCodeUri,
203
+ alt: t("mfa.setup.qrCodeAlt"),
204
+ className: "h-48 w-48"
205
+ }
206
+ ) }) }),
207
+ secret && /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "text-center", children: [
208
+ /* @__PURE__ */ jsxRuntime.jsx("p", { className: "text-xs text-gray-500 dark:text-gray-400 mb-1", children: t("mfa.setup.manualSecretLabel") }),
209
+ /* @__PURE__ */ jsxRuntime.jsx("code", { className: "font-mono text-sm font-medium text-gray-900 dark:text-white tracking-widest", children: secret })
210
+ ] })
211
+ ] }) }),
212
+ currentStep === 2 && /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "space-y-4", children: [
213
+ /* @__PURE__ */ jsxRuntime.jsx(
214
+ chunkMXFEU7A6_js.OtpInput,
215
+ {
216
+ value: verifyCode,
217
+ onChange: setVerifyCode,
218
+ onComplete: handleVerifyComplete,
219
+ error: verifyError ?? void 0,
220
+ disabled: isLoading,
221
+ autoFocus: true
222
+ }
223
+ ),
224
+ isLoading && /* @__PURE__ */ jsxRuntime.jsx("div", { className: "flex justify-center", children: /* @__PURE__ */ jsxRuntime.jsx(chunkKNXAOJAK_js.Spinner, {}) })
225
+ ] }),
226
+ currentStep === 3 && /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "space-y-4", children: [
227
+ /* @__PURE__ */ jsxRuntime.jsx(
228
+ chunkMXFEU7A6_js.BackupCodeGrid,
229
+ {
230
+ codes: backupCodes,
231
+ revealed: true
232
+ }
233
+ ),
234
+ /* @__PURE__ */ jsxRuntime.jsx(
235
+ chunkKNXAOJAK_js.FormCheckbox,
236
+ {
237
+ label: t("mfa.setup.backupCodesSavedConfirm"),
238
+ checked: savedBackupCodes,
239
+ onChange: setSavedBackupCodes
240
+ }
241
+ )
242
+ ] })
243
+ ]
244
+ }
245
+ );
246
+ }
247
+ function MfaManage({
248
+ mfaEnabled,
249
+ enrolledMethods = [],
250
+ onEnroll,
251
+ onDisable,
252
+ onRegenerateBackupCodes
253
+ }) {
254
+ const t = chunkYXN2K77G_js.useTranslations("windsock");
255
+ const format = chunkYXN2K77G_js.useFormatter();
256
+ const { client: client$1 } = client.useAuth();
257
+ const [isBackupSheetOpen, setIsBackupSheetOpen] = react.useState(false);
258
+ const [newBackupCodes, setNewBackupCodes] = react.useState([]);
259
+ const [isRegenerating, setIsRegenerating] = react.useState(false);
260
+ const [isConfirmDisableOpen, setIsConfirmDisableOpen] = react.useState(false);
261
+ const [disablePassword, setDisablePassword] = react.useState("");
262
+ const [disableError, setDisableError] = react.useState(null);
263
+ const [isDisabling, setIsDisabling] = react.useState(false);
264
+ const handleOpenBackupSheet = react.useCallback(async () => {
265
+ chunkUZ3CMNUJ_js.triggerHaptic("medium");
266
+ setIsRegenerating(true);
267
+ try {
268
+ const codes = onRegenerateBackupCodes ? await onRegenerateBackupCodes() : await client$1.regenerateBackupCodes();
269
+ setNewBackupCodes(codes);
270
+ setIsBackupSheetOpen(true);
271
+ } catch {
272
+ } finally {
273
+ setIsRegenerating(false);
274
+ }
275
+ }, [onRegenerateBackupCodes, client$1]);
276
+ const handleCloseBackupSheet = react.useCallback(() => {
277
+ chunkUZ3CMNUJ_js.triggerHaptic("light");
278
+ setIsBackupSheetOpen(false);
279
+ setNewBackupCodes([]);
280
+ }, []);
281
+ const handleRequestDisable = react.useCallback(() => {
282
+ chunkUZ3CMNUJ_js.triggerHaptic("warning");
283
+ setDisablePassword("");
284
+ setDisableError(null);
285
+ setIsConfirmDisableOpen(true);
286
+ }, []);
287
+ const handleConfirmDisable = react.useCallback(async () => {
288
+ if (!disablePassword.trim()) {
289
+ setDisableError(t("mfa.manage.disablePasswordRequired"));
290
+ chunkUZ3CMNUJ_js.triggerHaptic("error");
291
+ return;
292
+ }
293
+ setDisableError(null);
294
+ setIsDisabling(true);
295
+ chunkUZ3CMNUJ_js.triggerHaptic("medium");
296
+ try {
297
+ if (onDisable) {
298
+ onDisable();
299
+ } else {
300
+ const result = await client$1.disableMfa(disablePassword);
301
+ if (!result.success) {
302
+ setDisableError(result.error ?? t("mfa.manage.disableError"));
303
+ chunkUZ3CMNUJ_js.triggerHaptic("error");
304
+ return;
305
+ }
306
+ }
307
+ setIsConfirmDisableOpen(false);
308
+ chunkUZ3CMNUJ_js.triggerHaptic("success");
309
+ } catch {
310
+ setDisableError(t("mfa.manage.disableError"));
311
+ chunkUZ3CMNUJ_js.triggerHaptic("error");
312
+ } finally {
313
+ setIsDisabling(false);
314
+ }
315
+ }, [disablePassword, onDisable, client$1, t]);
316
+ const handleCancelDisable = react.useCallback(() => {
317
+ chunkUZ3CMNUJ_js.triggerHaptic("light");
318
+ setIsConfirmDisableOpen(false);
319
+ setDisablePassword("");
320
+ setDisableError(null);
321
+ }, []);
322
+ const handleEnroll = react.useCallback(() => {
323
+ chunkUZ3CMNUJ_js.triggerHaptic("light");
324
+ onEnroll?.();
325
+ }, [onEnroll]);
326
+ return /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
327
+ /* @__PURE__ */ jsxRuntime.jsx(
328
+ chunkKNXAOJAK_js.SectionCard,
329
+ {
330
+ header: {
331
+ icon: /* @__PURE__ */ jsxRuntime.jsx(outline.ShieldCheckIcon, { className: "h-5 w-5 text-white" }),
332
+ title: t("mfa.manage.title"),
333
+ subtitle: t("mfa.manage.subtitle"),
334
+ gradient: "from-emerald-500 via-teal-500 to-cyan-500",
335
+ rightContent: mfaEnabled ? /* @__PURE__ */ jsxRuntime.jsx(chunkKNXAOJAK_js.StatusBadge, { status: "active", label: t("mfa.manage.statusEnabled"), size: "sm" }) : /* @__PURE__ */ jsxRuntime.jsx(chunkKNXAOJAK_js.StatusBadge, { status: "inactive", label: t("mfa.manage.statusDisabled"), size: "sm" })
336
+ },
337
+ padded: false,
338
+ children: !mfaEnabled ? (
339
+ /* MFA not enabled — show empty state with enable CTA */
340
+ /* @__PURE__ */ jsxRuntime.jsx("div", { className: "p-4 sm:p-6", children: /* @__PURE__ */ jsxRuntime.jsx(
341
+ chunkKNXAOJAK_js.EmptyState,
342
+ {
343
+ message: t("mfa.manage.emptyStateMessage"),
344
+ description: t("mfa.manage.emptyStateDescription"),
345
+ icon: solid.ShieldExclamationIcon,
346
+ action: {
347
+ label: t("mfa.manage.enableButton"),
348
+ onClick: handleEnroll
349
+ },
350
+ variant: "card"
351
+ }
352
+ ) })
353
+ ) : (
354
+ /* MFA enabled — show enrolled methods list + action buttons */
355
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "space-y-4 p-4 sm:p-6", children: [
356
+ enrolledMethods.length > 0 && /* @__PURE__ */ jsxRuntime.jsx(chunkKNXAOJAK_js.ListCard, { children: enrolledMethods.map((method, index) => /* @__PURE__ */ jsxRuntime.jsxs(
357
+ chunkKNXAOJAK_js.ListCardItem,
358
+ {
359
+ leading: /* @__PURE__ */ jsxRuntime.jsx("div", { className: "h-9 w-9 rounded-xl bg-gradient-to-br from-emerald-500 to-teal-600 flex items-center justify-center shadow-sm", children: /* @__PURE__ */ jsxRuntime.jsx(outline.ShieldCheckIcon, { className: "h-5 w-5 text-white" }) }),
360
+ trailing: /* @__PURE__ */ jsxRuntime.jsx(chunkKNXAOJAK_js.StatusBadge, { status: "active", label: t("mfa.manage.methodStatusActive"), size: "sm" }),
361
+ children: [
362
+ /* @__PURE__ */ jsxRuntime.jsx("p", { className: "text-sm font-medium text-gray-900 dark:text-white", children: method.label }),
363
+ method.createdAt && /* @__PURE__ */ jsxRuntime.jsx("p", { className: "text-xs text-gray-500 dark:text-gray-400 mt-0.5", children: t("mfa.manage.methodEnrolledAt", {
364
+ date: format.dateTime(new Date(method.createdAt), {
365
+ year: "numeric",
366
+ month: "short",
367
+ day: "numeric"
368
+ })
369
+ }) })
370
+ ]
371
+ },
372
+ `${method.type}-${index}`
373
+ )) }),
374
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex flex-col gap-2 sm:flex-row sm:gap-3", children: [
375
+ onEnroll && /* @__PURE__ */ jsxRuntime.jsxs(
376
+ chunkKNXAOJAK_js.Button,
377
+ {
378
+ color: "ios-glass-blue",
379
+ onClick: handleEnroll,
380
+ className: "flex-1",
381
+ children: [
382
+ /* @__PURE__ */ jsxRuntime.jsx(outline.PlusIcon, { className: "h-4 w-4 mr-1.5" }),
383
+ t("mfa.manage.addMethodButton")
384
+ ]
385
+ }
386
+ ),
387
+ /* @__PURE__ */ jsxRuntime.jsxs(
388
+ chunkKNXAOJAK_js.Button,
389
+ {
390
+ outline: true,
391
+ onClick: handleOpenBackupSheet,
392
+ loading: isRegenerating,
393
+ disabled: isRegenerating,
394
+ className: "flex-1",
395
+ children: [
396
+ /* @__PURE__ */ jsxRuntime.jsx(outline.ArrowPathIcon, { className: "h-4 w-4 mr-1.5" }),
397
+ t("mfa.manage.regenerateBackupCodesButton")
398
+ ]
399
+ }
400
+ ),
401
+ /* @__PURE__ */ jsxRuntime.jsx(
402
+ chunkKNXAOJAK_js.Button,
403
+ {
404
+ color: "ios-glass-red",
405
+ onClick: handleRequestDisable,
406
+ className: "flex-1",
407
+ children: t("mfa.manage.disableMfaButton")
408
+ }
409
+ )
410
+ ] })
411
+ ] })
412
+ )
413
+ }
414
+ ),
415
+ /* @__PURE__ */ jsxRuntime.jsx(
416
+ chunkKNXAOJAK_js.Sheet,
417
+ {
418
+ open: isBackupSheetOpen,
419
+ onClose: handleCloseBackupSheet,
420
+ title: t("mfa.manage.backupCodesSheetTitle"),
421
+ side: "bottom",
422
+ size: "md",
423
+ children: /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "space-y-4 p-4 sm:p-6", children: [
424
+ /* @__PURE__ */ jsxRuntime.jsx("p", { className: "text-sm text-gray-500 dark:text-gray-400", children: t("mfa.manage.backupCodesSheetDescription") }),
425
+ /* @__PURE__ */ jsxRuntime.jsx(
426
+ chunkMXFEU7A6_js.BackupCodeGrid,
427
+ {
428
+ codes: newBackupCodes,
429
+ revealed: true
430
+ }
431
+ ),
432
+ /* @__PURE__ */ jsxRuntime.jsx(
433
+ chunkKNXAOJAK_js.Button,
434
+ {
435
+ color: "ios-glass-blue",
436
+ fullWidth: true,
437
+ onClick: handleCloseBackupSheet,
438
+ children: t("mfa.manage.backupCodesDoneButton")
439
+ }
440
+ )
441
+ ] })
442
+ }
443
+ ),
444
+ /* @__PURE__ */ jsxRuntime.jsx(
445
+ chunkKNXAOJAK_js.Sheet,
446
+ {
447
+ open: isConfirmDisableOpen,
448
+ onClose: handleCancelDisable,
449
+ title: t("mfa.manage.confirmDisableTitle"),
450
+ side: "bottom",
451
+ size: "md",
452
+ children: /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "space-y-4 p-4 sm:p-6", children: [
453
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center gap-3", children: [
454
+ /* @__PURE__ */ jsxRuntime.jsx("div", { className: "flex h-10 w-10 items-center justify-center rounded-xl bg-ios-red shadow-sm", children: /* @__PURE__ */ jsxRuntime.jsx(solid.ShieldExclamationIcon, { className: "h-5 w-5 text-white" }) }),
455
+ /* @__PURE__ */ jsxRuntime.jsx("p", { className: "text-sm text-gray-500 dark:text-gray-400", children: t("mfa.manage.confirmDisableDescription") })
456
+ ] }),
457
+ /* @__PURE__ */ jsxRuntime.jsx(
458
+ chunkKNXAOJAK_js.PasswordInput,
459
+ {
460
+ label: t("mfa.manage.disablePasswordLabel"),
461
+ value: disablePassword,
462
+ onChange: (event) => setDisablePassword(event.target.value),
463
+ placeholder: t("mfa.manage.disablePasswordPlaceholder"),
464
+ disabled: isDisabling,
465
+ autoFocus: true,
466
+ autoComplete: "current-password"
467
+ }
468
+ ),
469
+ disableError && /* @__PURE__ */ jsxRuntime.jsx("p", { className: "text-sm text-red-600 dark:text-red-400", role: "alert", children: disableError }),
470
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex gap-3", children: [
471
+ /* @__PURE__ */ jsxRuntime.jsx(
472
+ chunkKNXAOJAK_js.Button,
473
+ {
474
+ outline: true,
475
+ onClick: handleCancelDisable,
476
+ disabled: isDisabling,
477
+ className: "flex-1",
478
+ children: t("mfa.manage.confirmDisableCancelLabel")
479
+ }
480
+ ),
481
+ /* @__PURE__ */ jsxRuntime.jsx(
482
+ chunkKNXAOJAK_js.Button,
483
+ {
484
+ color: "ios-glass-red",
485
+ onClick: handleConfirmDisable,
486
+ loading: isDisabling,
487
+ disabled: isDisabling || !disablePassword.trim(),
488
+ className: "flex-1",
489
+ children: t("mfa.manage.confirmDisableConfirmLabel")
490
+ }
491
+ )
492
+ ] })
493
+ ] })
494
+ }
495
+ )
496
+ ] });
497
+ }
498
+ var ALL_TABS = ["profile", "security", "sessions", "linked-accounts"];
499
+ function ProfileTabContent({ onProfileUpdate }) {
500
+ const t = chunkYXN2K77G_js.useTranslations("windsock");
501
+ const currentLocale = chunkYXN2K77G_js.useLocale();
502
+ const { user, client: client$1 } = client.useAuth();
503
+ const [name, setName] = react.useState(user?.name ?? "");
504
+ const [avatarPreview, setAvatarPreview] = react.useState(user?.image ?? null);
505
+ const [isUploadingAvatar, setIsUploadingAvatar] = react.useState(false);
506
+ const fileInputRef = react.useRef(null);
507
+ const [selectedLocale, setSelectedLocale] = react.useState(currentLocale);
508
+ const [isSaving, setIsSaving] = react.useState(false);
509
+ const [saveError, setSaveError] = react.useState(null);
510
+ const [saveSuccess, setSaveSuccess] = react.useState(false);
511
+ const handleAvatarChange = react.useCallback(async (event) => {
512
+ const file = event.target.files?.[0];
513
+ if (!file) return;
514
+ if (!["image/jpeg", "image/png", "image/webp"].includes(file.type)) {
515
+ setSaveError(t("userProfile.profile.avatarInvalidType"));
516
+ return;
517
+ }
518
+ if (file.size > 5 * 1024 * 1024) {
519
+ setSaveError(t("userProfile.profile.avatarTooLarge"));
520
+ return;
521
+ }
522
+ const previewUrl = URL.createObjectURL(file);
523
+ setAvatarPreview(previewUrl);
524
+ setSaveError(null);
525
+ setIsUploadingAvatar(true);
526
+ chunkUZ3CMNUJ_js.triggerHaptic("light");
527
+ try {
528
+ const uploadResult = await client$1.uploadFile(file, "avatars");
529
+ if (!uploadResult.success || !uploadResult.key) {
530
+ throw new Error(uploadResult.error ?? "Upload failed");
531
+ }
532
+ const updateResult = await client$1.updateProfile({ image: uploadResult.key });
533
+ if (!updateResult.success) {
534
+ throw new Error(updateResult.error ?? "Profile update failed");
535
+ }
536
+ chunkUZ3CMNUJ_js.triggerHaptic("success");
537
+ } catch (error) {
538
+ setSaveError(error instanceof Error ? error.message : "Upload failed");
539
+ setAvatarPreview(user?.image ?? null);
540
+ chunkUZ3CMNUJ_js.triggerHaptic("error");
541
+ } finally {
542
+ setIsUploadingAvatar(false);
543
+ if (fileInputRef.current) fileInputRef.current.value = "";
544
+ }
545
+ }, [client$1, user, t]);
546
+ const handleSave = react.useCallback(async () => {
547
+ setSaveError(null);
548
+ setSaveSuccess(false);
549
+ setIsSaving(true);
550
+ chunkUZ3CMNUJ_js.triggerHaptic("light");
551
+ try {
552
+ if (onProfileUpdate) {
553
+ await onProfileUpdate({ name, email: user?.email ?? "", locale: selectedLocale });
554
+ } else {
555
+ const result = await client$1.updateProfile({ name, locale: selectedLocale });
556
+ if (!result.success) {
557
+ throw new Error(result.error);
558
+ }
559
+ }
560
+ setSaveSuccess(true);
561
+ chunkUZ3CMNUJ_js.triggerHaptic("success");
562
+ if (selectedLocale !== currentLocale) {
563
+ sharedI18n__namespace.setLocaleCookie(selectedLocale);
564
+ window.location.reload();
565
+ }
566
+ } catch {
567
+ setSaveError(t("userProfile.profile.saveError"));
568
+ chunkUZ3CMNUJ_js.triggerHaptic("error");
569
+ } finally {
570
+ setIsSaving(false);
571
+ }
572
+ }, [name, selectedLocale, currentLocale, user, onProfileUpdate, client$1, t]);
573
+ return /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "space-y-6", children: [
574
+ /* @__PURE__ */ jsxRuntime.jsx(
575
+ chunkKNXAOJAK_js.SectionCard,
576
+ {
577
+ header: {
578
+ icon: /* @__PURE__ */ jsxRuntime.jsx(outline.UserIcon, { className: "h-5 w-5 text-white" }),
579
+ title: t("userProfile.profile.title"),
580
+ subtitle: t("userProfile.profile.subtitle"),
581
+ gradient: "from-blue-500 via-indigo-500 to-violet-500"
582
+ },
583
+ children: /* @__PURE__ */ jsxRuntime.jsxs(chunkKNXAOJAK_js.BaseForm, { onSubmit: handleSave, submitLabel: t("userProfile.profile.saveButton"), showFooter: false, children: [
584
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex flex-col items-center gap-3 pb-4", children: [
585
+ /* @__PURE__ */ jsxRuntime.jsxs(
586
+ "button",
587
+ {
588
+ type: "button",
589
+ onClick: () => fileInputRef.current?.click(),
590
+ disabled: isUploadingAvatar,
591
+ className: "group relative",
592
+ children: [
593
+ /* @__PURE__ */ jsxRuntime.jsx(
594
+ chunkKNXAOJAK_js.Avatar,
595
+ {
596
+ src: avatarPreview ?? void 0,
597
+ initials: user?.name?.slice(0, 2).toUpperCase() ?? user?.email?.slice(0, 2).toUpperCase(),
598
+ alt: user?.name ?? "Profile",
599
+ className: "h-20 w-20 text-2xl"
600
+ }
601
+ ),
602
+ /* @__PURE__ */ jsxRuntime.jsx("div", { className: "absolute inset-0 flex items-center justify-center rounded-full bg-black/0 transition group-hover:bg-black/40", children: isUploadingAvatar ? /* @__PURE__ */ jsxRuntime.jsx(chunkKNXAOJAK_js.InlineSpinner, {}) : /* @__PURE__ */ jsxRuntime.jsx(outline.CameraIcon, { className: "h-6 w-6 text-white opacity-0 transition group-hover:opacity-100" }) })
603
+ ]
604
+ }
605
+ ),
606
+ /* @__PURE__ */ jsxRuntime.jsx(
607
+ "input",
608
+ {
609
+ ref: fileInputRef,
610
+ type: "file",
611
+ accept: "image/jpeg,image/png,image/webp",
612
+ onChange: handleAvatarChange,
613
+ className: "hidden"
614
+ }
615
+ ),
616
+ /* @__PURE__ */ jsxRuntime.jsx("p", { className: "text-xs text-gray-500 dark:text-gray-400", children: t("userProfile.profile.avatarHint") })
617
+ ] }),
618
+ /* @__PURE__ */ jsxRuntime.jsx(
619
+ chunkKNXAOJAK_js.Input,
620
+ {
621
+ label: t("userProfile.profile.nameLabel"),
622
+ value: name,
623
+ onChange: (event) => setName(event.target.value),
624
+ disabled: isSaving,
625
+ autoComplete: "name"
626
+ }
627
+ ),
628
+ /* @__PURE__ */ jsxRuntime.jsx(
629
+ chunkKNXAOJAK_js.Input,
630
+ {
631
+ label: t("userProfile.profile.emailLabel"),
632
+ type: "email",
633
+ value: user?.email ?? "",
634
+ readOnly: true,
635
+ disabled: true,
636
+ autoComplete: "email"
637
+ }
638
+ ),
639
+ saveError && /* @__PURE__ */ jsxRuntime.jsx("p", { className: "text-sm text-red-600 dark:text-red-400", children: saveError }),
640
+ saveSuccess && /* @__PURE__ */ jsxRuntime.jsx("p", { className: "text-sm text-green-600 dark:text-green-400", children: t("userProfile.profile.saveSuccess") }),
641
+ /* @__PURE__ */ jsxRuntime.jsx(
642
+ chunkKNXAOJAK_js.Button,
643
+ {
644
+ type: "submit",
645
+ color: "ios-glass-blue",
646
+ fullWidth: true,
647
+ loading: isSaving,
648
+ disabled: isSaving,
649
+ children: t("userProfile.profile.saveButton")
650
+ }
651
+ )
652
+ ] })
653
+ }
654
+ ),
655
+ /* @__PURE__ */ jsxRuntime.jsxs(
656
+ chunkKNXAOJAK_js.SectionCard,
657
+ {
658
+ header: {
659
+ icon: /* @__PURE__ */ jsxRuntime.jsx(outline.GlobeAltIcon, { className: "h-5 w-5 text-white" }),
660
+ title: t("userProfile.profile.languageTitle"),
661
+ subtitle: t("userProfile.profile.languageSubtitle"),
662
+ gradient: "from-lime-400 via-emerald-500 to-green-600"
663
+ },
664
+ children: [
665
+ /* @__PURE__ */ jsxRuntime.jsx("div", { className: "grid grid-cols-3 gap-2", children: sharedI18n__namespace.SUPPORTED_LOCALES.map((code) => {
666
+ const meta = sharedI18n__namespace.LANGUAGE_META[code];
667
+ const checked = selectedLocale === code;
668
+ return /* @__PURE__ */ jsxRuntime.jsxs(
669
+ "button",
670
+ {
671
+ type: "button",
672
+ onClick: () => {
673
+ setSelectedLocale(code);
674
+ chunkUZ3CMNUJ_js.triggerHaptic("light");
675
+ },
676
+ className: `group relative flex flex-col items-center justify-center rounded-xl border px-2 py-2.5 backdrop-blur-xl shadow-[inset_0_1px_0_rgba(255,255,255,0.9),0_10px_22px_-16px_rgba(15,23,42,0.45)] dark:shadow-[inset_0_1px_0_rgba(255,255,255,0.18),0_10px_22px_-16px_rgba(0,0,0,0.75)] transition ${checked ? "border-indigo-400/80 bg-gradient-to-br from-indigo-100/85 via-white/80 to-sky-100/75 dark:border-indigo-300/70 dark:bg-[linear-gradient(140deg,rgba(99,102,241,0.32)_0%,rgba(30,41,59,0.72)_100%)]" : "border-white/55 bg-gradient-to-br from-white/82 via-white/66 to-slate-100/62 hover:from-white/92 hover:to-sky-100/72 dark:border-white/15 dark:bg-[linear-gradient(140deg,rgba(30,41,59,0.72)_0%,rgba(15,23,42,0.62)_100%)] dark:hover:bg-[linear-gradient(140deg,rgba(51,65,85,0.76)_0%,rgba(30,41,59,0.68)_100%)]"}`,
677
+ children: [
678
+ /* @__PURE__ */ jsxRuntime.jsx("span", { className: "text-lg leading-5", children: meta.flag }),
679
+ /* @__PURE__ */ jsxRuntime.jsx("span", { className: `mt-1.5 text-xs font-semibold ${checked ? "text-slate-900 dark:text-indigo-100" : "text-slate-700 dark:text-slate-100"}`, children: meta.nativeName }),
680
+ checked && /* @__PURE__ */ jsxRuntime.jsx(outline.CheckCircleIcon, { className: "absolute right-1 top-1 h-3.5 w-3.5 text-indigo-600 dark:text-indigo-300" })
681
+ ]
682
+ },
683
+ code
684
+ );
685
+ }) }),
686
+ selectedLocale !== currentLocale && /* @__PURE__ */ jsxRuntime.jsx("div", { className: "mt-4", children: /* @__PURE__ */ jsxRuntime.jsx(
687
+ chunkKNXAOJAK_js.Button,
688
+ {
689
+ type: "button",
690
+ color: "ios-glass-blue",
691
+ fullWidth: true,
692
+ loading: isSaving,
693
+ disabled: isSaving,
694
+ onClick: handleSave,
695
+ children: t("userProfile.profile.saveButton")
696
+ }
697
+ ) })
698
+ ]
699
+ }
700
+ )
701
+ ] });
702
+ }
703
+ function SecurityTabContent() {
704
+ const t = chunkYXN2K77G_js.useTranslations("windsock");
705
+ const { client: client$1, user, refresh } = client.useAuth();
706
+ const [showMfaSetup, setShowMfaSetup] = react.useState(false);
707
+ const [currentPassword, setCurrentPassword] = react.useState("");
708
+ const [newPassword, setNewPassword] = react.useState("");
709
+ const [confirmPassword, setConfirmPassword] = react.useState("");
710
+ const [passwordError, setPasswordError] = react.useState(null);
711
+ const [passwordSuccess, setPasswordSuccess] = react.useState(false);
712
+ const [isSavingPassword, setIsSavingPassword] = react.useState(false);
713
+ const handleMfaSetupSuccess = react.useCallback(() => {
714
+ setShowMfaSetup(false);
715
+ refresh();
716
+ }, [refresh]);
717
+ const handleChangePassword = react.useCallback(async () => {
718
+ setPasswordError(null);
719
+ setPasswordSuccess(false);
720
+ if (!currentPassword) {
721
+ setPasswordError(t("userProfile.security.errorCurrentRequired"));
722
+ return;
723
+ }
724
+ if (!newPassword) {
725
+ setPasswordError(t("userProfile.security.errorNewRequired"));
726
+ return;
727
+ }
728
+ if (newPassword !== confirmPassword) {
729
+ setPasswordError(t("userProfile.security.errorPasswordMismatch"));
730
+ return;
731
+ }
732
+ setIsSavingPassword(true);
733
+ chunkUZ3CMNUJ_js.triggerHaptic("light");
734
+ try {
735
+ const result = await client$1.changePassword(currentPassword, newPassword);
736
+ if (!result.success) {
737
+ setPasswordError(result.error ?? t("userProfile.security.errorSaveFailed"));
738
+ chunkUZ3CMNUJ_js.triggerHaptic("error");
739
+ return;
740
+ }
741
+ setCurrentPassword("");
742
+ setNewPassword("");
743
+ setConfirmPassword("");
744
+ setPasswordSuccess(true);
745
+ chunkUZ3CMNUJ_js.triggerHaptic("success");
746
+ } catch {
747
+ setPasswordError(t("userProfile.security.errorSaveFailed"));
748
+ chunkUZ3CMNUJ_js.triggerHaptic("error");
749
+ } finally {
750
+ setIsSavingPassword(false);
751
+ }
752
+ }, [currentPassword, newPassword, confirmPassword, client$1, t]);
753
+ return /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "space-y-6", children: [
754
+ showMfaSetup ? /* @__PURE__ */ jsxRuntime.jsx(
755
+ MfaSetup,
756
+ {
757
+ onSuccess: handleMfaSetupSuccess,
758
+ onCancel: () => setShowMfaSetup(false)
759
+ }
760
+ ) : /* @__PURE__ */ jsxRuntime.jsx(
761
+ MfaManage,
762
+ {
763
+ mfaEnabled: user?.mfaEnabled ?? false,
764
+ enrolledMethods: user?.mfaEnabled ? [{ type: "totp", label: t("userProfile.security.mfaTotpLabel") }] : [],
765
+ onEnroll: () => setShowMfaSetup(true)
766
+ }
767
+ ),
768
+ /* @__PURE__ */ jsxRuntime.jsx(
769
+ chunkKNXAOJAK_js.SectionCard,
770
+ {
771
+ header: {
772
+ icon: /* @__PURE__ */ jsxRuntime.jsx(outline.UserIcon, { className: "h-5 w-5 text-white" }),
773
+ title: t("userProfile.security.changePasswordTitle"),
774
+ subtitle: t("userProfile.security.changePasswordSubtitle"),
775
+ gradient: "from-blue-500 via-sky-500 to-cyan-500"
776
+ },
777
+ children: /* @__PURE__ */ jsxRuntime.jsxs(chunkKNXAOJAK_js.BaseForm, { onSubmit: handleChangePassword, submitLabel: t("userProfile.security.changePasswordButton"), showFooter: false, children: [
778
+ /* @__PURE__ */ jsxRuntime.jsx(
779
+ chunkKNXAOJAK_js.PasswordInput,
780
+ {
781
+ label: t("userProfile.security.currentPasswordLabel"),
782
+ value: currentPassword,
783
+ onChange: (event) => setCurrentPassword(event.target.value),
784
+ disabled: isSavingPassword,
785
+ autoComplete: "current-password"
786
+ }
787
+ ),
788
+ /* @__PURE__ */ jsxRuntime.jsx(
789
+ chunkKNXAOJAK_js.PasswordInput,
790
+ {
791
+ label: t("userProfile.security.newPasswordLabel"),
792
+ value: newPassword,
793
+ onChange: (event) => setNewPassword(event.target.value),
794
+ disabled: isSavingPassword,
795
+ autoComplete: "new-password"
796
+ }
797
+ ),
798
+ /* @__PURE__ */ jsxRuntime.jsx(chunkMXFEU7A6_js.PasswordStrengthMeter, { password: newPassword }),
799
+ /* @__PURE__ */ jsxRuntime.jsx(
800
+ chunkKNXAOJAK_js.PasswordInput,
801
+ {
802
+ label: t("userProfile.security.confirmPasswordLabel"),
803
+ value: confirmPassword,
804
+ onChange: (event) => setConfirmPassword(event.target.value),
805
+ disabled: isSavingPassword,
806
+ autoComplete: "new-password"
807
+ }
808
+ ),
809
+ passwordError && /* @__PURE__ */ jsxRuntime.jsx("p", { className: "text-sm text-red-600 dark:text-red-400", children: passwordError }),
810
+ passwordSuccess && /* @__PURE__ */ jsxRuntime.jsx("p", { className: "text-sm text-green-600 dark:text-green-400", children: t("userProfile.security.changePasswordSuccess") }),
811
+ /* @__PURE__ */ jsxRuntime.jsx(
812
+ chunkKNXAOJAK_js.Button,
813
+ {
814
+ type: "submit",
815
+ color: "ios-glass-blue",
816
+ fullWidth: true,
817
+ loading: isSavingPassword,
818
+ disabled: isSavingPassword,
819
+ children: t("userProfile.security.changePasswordButton")
820
+ }
821
+ )
822
+ ] })
823
+ }
824
+ )
825
+ ] });
826
+ }
827
+ function SessionsTabContent() {
828
+ const t = chunkYXN2K77G_js.useTranslations("windsock");
829
+ const format = chunkYXN2K77G_js.useFormatter();
830
+ const { client: client$1 } = client.useAuth();
831
+ const [sessions, setSessions] = react.useState([]);
832
+ const [isLoading, setIsLoading] = react.useState(true);
833
+ const [revokingId, setRevokingId] = react.useState(null);
834
+ react.useEffect(() => {
835
+ let cancelled = false;
836
+ const fetchSessions = async () => {
837
+ setIsLoading(true);
838
+ try {
839
+ const rawSessions = await client$1.getSessions();
840
+ if (!cancelled) {
841
+ setSessions(rawSessions.map((session) => ({
842
+ id: session.id,
843
+ device: session.userAgent ?? "Unknown",
844
+ location: session.ipAddress ?? "Unknown",
845
+ lastActive: typeof session.createdAt === "string" ? session.createdAt : session.createdAt.toISOString(),
846
+ current: false
847
+ })));
848
+ }
849
+ } catch {
850
+ } finally {
851
+ if (!cancelled) setIsLoading(false);
852
+ }
853
+ };
854
+ fetchSessions();
855
+ return () => {
856
+ cancelled = true;
857
+ };
858
+ }, [client$1]);
859
+ const handleRevoke = react.useCallback(async (sessionId) => {
860
+ setRevokingId(sessionId);
861
+ chunkUZ3CMNUJ_js.triggerHaptic("medium");
862
+ try {
863
+ await client$1.revokeSession(sessionId);
864
+ setSessions((previous) => previous.filter((s) => s.id !== sessionId));
865
+ chunkUZ3CMNUJ_js.triggerHaptic("success");
866
+ } catch {
867
+ chunkUZ3CMNUJ_js.triggerHaptic("error");
868
+ } finally {
869
+ setRevokingId(null);
870
+ }
871
+ }, [client$1]);
872
+ return /* @__PURE__ */ jsxRuntime.jsx(
873
+ chunkKNXAOJAK_js.SectionCard,
874
+ {
875
+ header: {
876
+ icon: /* @__PURE__ */ jsxRuntime.jsx(outline.ComputerDesktopIcon, { className: "h-5 w-5 text-white" }),
877
+ title: t("userProfile.sessions.title"),
878
+ subtitle: t("userProfile.sessions.subtitle"),
879
+ gradient: "from-teal-500 via-cyan-500 to-sky-500"
880
+ },
881
+ padded: false,
882
+ children: isLoading ? /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center justify-center gap-2 p-8 text-sm text-gray-500 dark:text-gray-400", children: [
883
+ /* @__PURE__ */ jsxRuntime.jsx(chunkKNXAOJAK_js.InlineSpinner, {}),
884
+ /* @__PURE__ */ jsxRuntime.jsx("span", { children: t("userProfile.sessions.loading") })
885
+ ] }) : sessions.length === 0 ? /* @__PURE__ */ jsxRuntime.jsx("p", { className: "p-6 text-center text-sm text-gray-500 dark:text-gray-400", children: t("userProfile.sessions.empty") }) : /* @__PURE__ */ jsxRuntime.jsx("div", { className: "overflow-x-auto", children: /* @__PURE__ */ jsxRuntime.jsxs("table", { className: "w-full text-sm", children: [
886
+ /* @__PURE__ */ jsxRuntime.jsx("thead", { children: /* @__PURE__ */ jsxRuntime.jsxs("tr", { className: "border-b border-gray-200/60 dark:border-white/10", children: [
887
+ /* @__PURE__ */ jsxRuntime.jsx("th", { className: "px-4 py-3 text-left text-xs font-medium uppercase tracking-wide text-gray-500 dark:text-gray-400", children: t("userProfile.sessions.columnDevice") }),
888
+ /* @__PURE__ */ jsxRuntime.jsx("th", { className: "px-4 py-3 text-left text-xs font-medium uppercase tracking-wide text-gray-500 dark:text-gray-400", children: t("userProfile.sessions.columnLocation") }),
889
+ /* @__PURE__ */ jsxRuntime.jsx("th", { className: "px-4 py-3 text-left text-xs font-medium uppercase tracking-wide text-gray-500 dark:text-gray-400", children: t("userProfile.sessions.columnLastActive") }),
890
+ /* @__PURE__ */ jsxRuntime.jsx("th", { className: "px-4 py-3 text-right text-xs font-medium uppercase tracking-wide text-gray-500 dark:text-gray-400", children: t("userProfile.sessions.columnAction") })
891
+ ] }) }),
892
+ /* @__PURE__ */ jsxRuntime.jsx("tbody", { className: "divide-y divide-gray-200/60 dark:divide-white/10", children: sessions.map((session) => /* @__PURE__ */ jsxRuntime.jsxs("tr", { children: [
893
+ /* @__PURE__ */ jsxRuntime.jsx("td", { className: "px-4 py-3 text-gray-900 dark:text-white", children: /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center gap-2", children: [
894
+ session.device,
895
+ session.current && /* @__PURE__ */ jsxRuntime.jsx(chunkKNXAOJAK_js.Badge, { color: "green", children: t("userProfile.sessions.currentBadge") })
896
+ ] }) }),
897
+ /* @__PURE__ */ jsxRuntime.jsx("td", { className: "px-4 py-3 text-gray-500 dark:text-gray-400", children: session.location }),
898
+ /* @__PURE__ */ jsxRuntime.jsx("td", { className: "px-4 py-3 text-gray-500 dark:text-gray-400", children: format.relativeTime(new Date(session.lastActive)) }),
899
+ /* @__PURE__ */ jsxRuntime.jsx("td", { className: "px-4 py-3 text-right", children: !session.current && /* @__PURE__ */ jsxRuntime.jsx(
900
+ chunkKNXAOJAK_js.Button,
901
+ {
902
+ size: "sm",
903
+ color: "ios-glass-red",
904
+ onClick: () => handleRevoke(session.id),
905
+ loading: revokingId === session.id,
906
+ disabled: revokingId !== null,
907
+ children: t("userProfile.sessions.revokeButton")
908
+ }
909
+ ) })
910
+ ] }, session.id)) })
911
+ ] }) })
912
+ }
913
+ );
914
+ }
915
+ function LinkedAccountsTabContent() {
916
+ const t = chunkYXN2K77G_js.useTranslations("windsock");
917
+ const { client: client$1 } = client.useAuth();
918
+ const [accounts, setAccounts] = react.useState([]);
919
+ const [isLoading, setIsLoading] = react.useState(true);
920
+ const [actioningProvider, setActioningProvider] = react.useState(null);
921
+ react.useEffect(() => {
922
+ let cancelled = false;
923
+ const fetchAccounts = async () => {
924
+ setIsLoading(true);
925
+ try {
926
+ const rawAccounts = await client$1.getLinkedAccounts();
927
+ if (!cancelled) {
928
+ setAccounts(rawAccounts.map((account) => ({
929
+ provider: account.provider,
930
+ providerUserId: account.providerAccountId,
931
+ connected: true
932
+ })));
933
+ }
934
+ } catch {
935
+ } finally {
936
+ if (!cancelled) setIsLoading(false);
937
+ }
938
+ };
939
+ fetchAccounts();
940
+ return () => {
941
+ cancelled = true;
942
+ };
943
+ }, [client$1]);
944
+ const handleDisconnect = react.useCallback(async (provider) => {
945
+ setActioningProvider(provider);
946
+ chunkUZ3CMNUJ_js.triggerHaptic("medium");
947
+ try {
948
+ await client$1.disconnectProvider(provider);
949
+ setAccounts((previous) => previous.filter((a) => a.provider !== provider));
950
+ chunkUZ3CMNUJ_js.triggerHaptic("success");
951
+ } catch {
952
+ chunkUZ3CMNUJ_js.triggerHaptic("error");
953
+ } finally {
954
+ setActioningProvider(null);
955
+ }
956
+ }, [client$1]);
957
+ const providerInitials = (provider) => provider.slice(0, 2).toUpperCase();
958
+ return /* @__PURE__ */ jsxRuntime.jsx(
959
+ chunkKNXAOJAK_js.SectionCard,
960
+ {
961
+ header: {
962
+ icon: /* @__PURE__ */ jsxRuntime.jsx(outline.LinkIcon, { className: "h-5 w-5 text-white" }),
963
+ title: t("userProfile.linkedAccounts.title"),
964
+ subtitle: t("userProfile.linkedAccounts.subtitle"),
965
+ gradient: "from-violet-500 via-purple-500 to-pink-500"
966
+ },
967
+ padded: false,
968
+ children: isLoading ? /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center justify-center gap-2 p-8 text-sm text-gray-500 dark:text-gray-400", children: [
969
+ /* @__PURE__ */ jsxRuntime.jsx(chunkKNXAOJAK_js.InlineSpinner, {}),
970
+ /* @__PURE__ */ jsxRuntime.jsx("span", { children: t("userProfile.linkedAccounts.loading") })
971
+ ] }) : accounts.length === 0 ? /* @__PURE__ */ jsxRuntime.jsx("p", { className: "p-6 text-center text-sm text-gray-500 dark:text-gray-400", children: t("userProfile.linkedAccounts.empty") }) : /* @__PURE__ */ jsxRuntime.jsx(chunkKNXAOJAK_js.ListCard, { children: accounts.map((account) => /* @__PURE__ */ jsxRuntime.jsx(
972
+ chunkKNXAOJAK_js.ListCardItem,
973
+ {
974
+ leading: /* @__PURE__ */ jsxRuntime.jsx(
975
+ chunkKNXAOJAK_js.Avatar,
976
+ {
977
+ src: account.logoUrl,
978
+ initials: providerInitials(account.provider),
979
+ alt: account.provider
980
+ }
981
+ ),
982
+ trailing: /* @__PURE__ */ jsxRuntime.jsx(
983
+ chunkKNXAOJAK_js.Button,
984
+ {
985
+ size: "sm",
986
+ color: "ios-glass-red",
987
+ onClick: () => handleDisconnect(account.provider),
988
+ loading: actioningProvider === account.provider,
989
+ disabled: actioningProvider !== null,
990
+ children: t("userProfile.linkedAccounts.disconnectButton")
991
+ }
992
+ ),
993
+ children: /* @__PURE__ */ jsxRuntime.jsxs("div", { children: [
994
+ /* @__PURE__ */ jsxRuntime.jsx("p", { className: "text-sm font-medium capitalize text-gray-900 dark:text-white", children: account.provider }),
995
+ /* @__PURE__ */ jsxRuntime.jsx("p", { className: "text-xs text-gray-500 dark:text-gray-400", children: t("userProfile.linkedAccounts.connectedLabel") })
996
+ ] })
997
+ },
998
+ account.provider
999
+ )) })
1000
+ }
1001
+ );
1002
+ }
1003
+ function UserProfile({
1004
+ defaultTab = "profile",
1005
+ tabs = ALL_TABS,
1006
+ onSignOut: _onSignOut,
1007
+ onProfileUpdate
1008
+ }) {
1009
+ const t = chunkYXN2K77G_js.useTranslations("windsock");
1010
+ const { status } = client.useAuth();
1011
+ const [activeTab, setActiveTab] = react.useState(
1012
+ tabs.includes(defaultTab) ? defaultTab : tabs[0] ?? "profile"
1013
+ );
1014
+ if (status === "loading") {
1015
+ return /* @__PURE__ */ jsxRuntime.jsx("div", { className: "flex items-center justify-center py-12", children: /* @__PURE__ */ jsxRuntime.jsx(chunkKNXAOJAK_js.InlineSpinner, {}) });
1016
+ }
1017
+ if (status !== "authenticated") {
1018
+ return null;
1019
+ }
1020
+ const segments = tabs.map((tab) => ({
1021
+ value: tab,
1022
+ label: t(`userProfile.tabs.${tab}`)
1023
+ }));
1024
+ return /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "space-y-6", children: [
1025
+ tabs.length > 1 && /* @__PURE__ */ jsxRuntime.jsx(
1026
+ chunkKNXAOJAK_js.SegmentedControl,
1027
+ {
1028
+ segments,
1029
+ value: activeTab,
1030
+ onChange: (value) => setActiveTab(value),
1031
+ fullWidth: true
1032
+ }
1033
+ ),
1034
+ activeTab === "profile" && /* @__PURE__ */ jsxRuntime.jsx(ProfileTabContent, { onProfileUpdate }),
1035
+ activeTab === "security" && /* @__PURE__ */ jsxRuntime.jsx(SecurityTabContent, {}),
1036
+ activeTab === "sessions" && /* @__PURE__ */ jsxRuntime.jsx(SessionsTabContent, {}),
1037
+ activeTab === "linked-accounts" && /* @__PURE__ */ jsxRuntime.jsx(LinkedAccountsTabContent, {})
1038
+ ] });
1039
+ }
1040
+
1041
+ exports.LinkedAccountsTabContent = LinkedAccountsTabContent;
1042
+ exports.MfaManage = MfaManage;
1043
+ exports.MfaSetup = MfaSetup;
1044
+ exports.ProfileTabContent = ProfileTabContent;
1045
+ exports.SecurityTabContent = SecurityTabContent;
1046
+ exports.SessionsTabContent = SessionsTabContent;
1047
+ exports.UserProfile = UserProfile;
1048
+ //# sourceMappingURL=chunk-X2KCCQPL.js.map
1049
+ //# sourceMappingURL=chunk-X2KCCQPL.js.map