@burdenoff/microfe-auth 2026.830.1 → 2026.904.1

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 (66) hide show
  1. package/dist/auth/AuthRoot.js +43 -41
  2. package/dist/auth/AuthRoot.js.map +1 -1
  3. package/dist/auth/AuthRoutes.js +7 -0
  4. package/dist/auth/AuthRoutes.js.map +1 -1
  5. package/dist/auth/components/AuthLayout.js +65 -67
  6. package/dist/auth/components/AuthLayout.js.map +1 -1
  7. package/dist/auth/components/CaptchaV2.js +21 -24
  8. package/dist/auth/components/CaptchaV2.js.map +1 -1
  9. package/dist/auth/components/ConfirmActionDialog.js +33 -32
  10. package/dist/auth/components/ConfirmActionDialog.js.map +1 -1
  11. package/dist/auth/components/profile/UserProfileMenu.js +44 -46
  12. package/dist/auth/components/profile/UserProfileMenu.js.map +1 -1
  13. package/dist/auth/components/workspace/WorkspaceSelector.js +62 -68
  14. package/dist/auth/components/workspace/WorkspaceSelector.js.map +1 -1
  15. package/dist/auth/pages/AuditLogPage.js +65 -68
  16. package/dist/auth/pages/AuditLogPage.js.map +1 -1
  17. package/dist/auth/pages/EmailVerifyPage.js +51 -54
  18. package/dist/auth/pages/EmailVerifyPage.js.map +1 -1
  19. package/dist/auth/pages/LoginPage.js +223 -225
  20. package/dist/auth/pages/LoginPage.js.map +1 -1
  21. package/dist/auth/pages/MagicLinkPage.js +67 -69
  22. package/dist/auth/pages/MagicLinkPage.js.map +1 -1
  23. package/dist/auth/pages/MfaSetupPage.js +85 -88
  24. package/dist/auth/pages/MfaSetupPage.js.map +1 -1
  25. package/dist/auth/pages/MfaVerifyPage.js +60 -63
  26. package/dist/auth/pages/MfaVerifyPage.js.map +1 -1
  27. package/dist/auth/pages/OAuthCallbackPage.js +37 -40
  28. package/dist/auth/pages/OAuthCallbackPage.js.map +1 -1
  29. package/dist/auth/pages/PasswordResetPage.js +102 -105
  30. package/dist/auth/pages/PasswordResetPage.js.map +1 -1
  31. package/dist/auth/pages/ReactivatePage.js +54 -57
  32. package/dist/auth/pages/ReactivatePage.js.map +1 -1
  33. package/dist/auth/pages/RegisterPage.js +171 -174
  34. package/dist/auth/pages/RegisterPage.js.map +1 -1
  35. package/dist/auth/pages/SSOClearPage.js +21 -20
  36. package/dist/auth/pages/SSOClearPage.js.map +1 -1
  37. package/dist/auth/pages/SSODepositPage.js +27 -26
  38. package/dist/auth/pages/SSODepositPage.js.map +1 -1
  39. package/dist/auth/pages/SSORelayCallbackPage.js +19 -18
  40. package/dist/auth/pages/SSORelayCallbackPage.js.map +1 -1
  41. package/dist/auth/pages/SecurityPage.js +247 -250
  42. package/dist/auth/pages/SecurityPage.js.map +1 -1
  43. package/dist/auth/pages/SessionsPage.js +95 -98
  44. package/dist/auth/pages/SessionsPage.js.map +1 -1
  45. package/dist/auth/pages/SsoCallbackPage.js +53 -56
  46. package/dist/auth/pages/SsoCallbackPage.js.map +1 -1
  47. package/dist/auth/pages/SsoConfigPage.js +296 -299
  48. package/dist/auth/pages/SsoConfigPage.js.map +1 -1
  49. package/dist/auth/pages/SsoLoginPage.js +65 -68
  50. package/dist/auth/pages/SsoLoginPage.js.map +1 -1
  51. package/dist/auth/pages/UserDetailsPage.js +124 -127
  52. package/dist/auth/pages/UserDetailsPage.js.map +1 -1
  53. package/dist/auth/pages/UserProfilePage.js +171 -174
  54. package/dist/auth/pages/UserProfilePage.js.map +1 -1
  55. package/dist/auth/pages/UsersPage.js +322 -325
  56. package/dist/auth/pages/UsersPage.js.map +1 -1
  57. package/dist/index.d.ts +1 -0
  58. package/dist/index.js +2 -1
  59. package/dist/shared/hooks/index.d.ts +1 -6
  60. package/dist/shared/hooks/useTr.d.ts +16 -0
  61. package/dist/shared/hooks/useTr.js +14 -0
  62. package/dist/shared/hooks/useTr.js.map +1 -0
  63. package/dist/translations/en.d.ts +806 -248
  64. package/dist/translations/en.js +870 -0
  65. package/dist/translations/en.js.map +1 -0
  66. package/package.json +1 -1
@@ -1,10 +1,10 @@
1
- import { useAuthAuditLogsQuery as e } from "../../generated/global-operations.js";
2
- import { AlertIcon as t, SpinnerIcon as n } from "../components/AuthIcons.js";
3
- import { formatDateTime as r } from "../utils/formatters.js";
4
- import { ConfirmActionDialog as i } from "../components/ConfirmActionDialog.js";
5
- import { useCreateAuthExportJobMutation as a } from "../../generated/wspace-operations.js";
6
- import { useState as o } from "react";
7
- import { useI18n as s } from "@burdenoff/fe-libs/shared/providers/shell";
1
+ import { useTr as e } from "../../shared/hooks/useTr.js";
2
+ import { useAuthAuditLogsQuery as t } from "../../generated/global-operations.js";
3
+ import { AlertIcon as n, SpinnerIcon as r } from "../components/AuthIcons.js";
4
+ import { formatDateTime as i } from "../utils/formatters.js";
5
+ import { ConfirmActionDialog as a } from "../components/ConfirmActionDialog.js";
6
+ import { useCreateAuthExportJobMutation as o } from "../../generated/wspace-operations.js";
7
+ import { useState as s } from "react";
8
8
  import { Fragment as c, jsx as l, jsxs as u } from "react/jsx-runtime";
9
9
  import { useFeatureFlagDetails as d } from "@burdenoff/fe-libs/shared/feature-flags";
10
10
  import { isQuotaExhaustedError as f } from "@burdenoff/fe-libs/shared/utils";
@@ -13,20 +13,17 @@ import { useGatewayClient as g } from "@burdenoff/fe-libs/shared/graphql";
13
13
  //#region src/auth/pages/AuditLogPage.tsx
14
14
  var _ = 20;
15
15
  function v() {
16
- let { t: v } = s(), y = (e, t) => {
17
- let n = v(e);
18
- return n === e ? t : n;
19
- }, [b, x] = o(1), S = (b - 1) * _, C = d("auth.audit-log")?.value === !0, { data: w, loading: T, error: E } = e({
20
- skip: !C,
16
+ let v = e(), [y, b] = s(1), x = (y - 1) * _, S = d("auth.audit-log")?.value === !0, { data: C, loading: w, error: T } = t({
17
+ skip: !S,
21
18
  fetchPolicy: "cache-and-network",
22
19
  variables: { pagination: {
23
20
  limit: _,
24
- offset: S
21
+ offset: x
25
22
  } }
26
- }), D = w?.authAuditLogs ?? [], O = D.length === _, [k, { loading: A }] = a({ client: g("workspace") }), [j, M] = o(null), [N, P] = o(!1), F = async () => {
27
- M(null);
23
+ }), E = C?.authAuditLogs ?? [], D = E.length === _, [O, { loading: k }] = o({ client: g("workspace") }), [A, j] = s(null), [M, N] = s(!1), P = async () => {
24
+ j(null);
28
25
  try {
29
- await k({ variables: { input: {
26
+ await O({ variables: { input: {
30
27
  name: "Audit Log Export",
31
28
  entityTypes: ["permission_audit_logs"],
32
29
  format: "CSV",
@@ -35,12 +32,12 @@ function v() {
35
32
  } } });
36
33
  } catch (e) {
37
34
  if (f(e)) {
38
- P(!0), M(y("auth.auditLog.exportQuotaReached", "You have reached your export limit for this month."));
35
+ N(!0), j(v("auth.auditLog.exportQuotaReached", "You have reached your export limit for this month."));
39
36
  return;
40
37
  }
41
- M(e instanceof Error ? e.message : "Failed to start export");
38
+ j(e instanceof Error ? e.message : "Failed to start export");
42
39
  }
43
- }, I = (e) => {
40
+ }, F = (e) => {
44
41
  switch (e) {
45
42
  case "LOGIN_SUCCESS": return "OK";
46
43
  case "LOGIN_FAILURE": return "ER";
@@ -52,24 +49,24 @@ function v() {
52
49
  default: return "AU";
53
50
  }
54
51
  };
55
- return C ? /* @__PURE__ */ u("div", {
52
+ return S ? /* @__PURE__ */ u("div", {
56
53
  className: "max-w-4xl mx-auto p-6",
57
54
  children: [
58
55
  /* @__PURE__ */ u("div", {
59
56
  className: "flex items-center justify-between mb-8",
60
57
  children: [/* @__PURE__ */ l("h1", {
61
58
  className: "text-3xl font-semibold text-text-primary",
62
- children: y("auth.auditLog.title", "Security Audit Log")
59
+ children: v("auth.auditLog.title", "Security Audit Log")
63
60
  }), /* @__PURE__ */ l("button", {
64
61
  type: "button",
65
- onClick: () => void F(),
66
- disabled: A,
67
- "aria-label": y("auth.auditLog.exportAriaLabel", "Export audit log"),
62
+ onClick: () => void P(),
63
+ disabled: k,
64
+ "aria-label": v("auth.auditLog.exportAriaLabel", "Export audit log"),
68
65
  className: "flex items-center gap-2 px-4 py-2.5 rounded-xl border border-border-default text-text-primary bg-bg-surface hover:bg-bg-sunken disabled:opacity-50 disabled:cursor-not-allowed transition-colors font-medium",
69
- children: A ? /* @__PURE__ */ u(c, { children: [/* @__PURE__ */ l(n, {
66
+ children: k ? /* @__PURE__ */ u(c, { children: [/* @__PURE__ */ l(r, {
70
67
  className: "size-4 animate-spin",
71
68
  "aria-hidden": "true"
72
- }), y("auth.auditLog.exporting", "Exporting...")] }) : /* @__PURE__ */ u(c, { children: [/* @__PURE__ */ l("svg", {
69
+ }), v("auth.auditLog.exporting", "Exporting...")] }) : /* @__PURE__ */ u(c, { children: [/* @__PURE__ */ l("svg", {
73
70
  className: "size-4",
74
71
  fill: "none",
75
72
  viewBox: "0 0 24 24",
@@ -81,74 +78,74 @@ function v() {
81
78
  strokeWidth: 2,
82
79
  d: "M4 16v1a3 3 0 003 3h10a3 3 0 003-3v-1m-4-4l-4 4m0 0l-4-4m4 4V4"
83
80
  })
84
- }), y("auth.auditLog.export", "Export")] })
81
+ }), v("auth.auditLog.export", "Export")] })
85
82
  })]
86
83
  }),
87
84
  /* @__PURE__ */ l(h, {
88
85
  className: "mb-6",
89
- children: y("auth.auditLog.purpose", "A running record of security-relevant events on your account — sign-ins, password and MFA changes, and anomalies. Scan it to spot anything you didn't do, and export it for your own records or compliance.")
86
+ children: v("auth.auditLog.purpose", "A running record of security-relevant events on your account — sign-ins, password and MFA changes, and anomalies. Scan it to spot anything you didn't do, and export it for your own records or compliance.")
90
87
  }),
91
88
  /* @__PURE__ */ u(p, {
92
89
  className: "mb-6",
93
90
  children: [/* @__PURE__ */ l("p", {
94
91
  className: "text-sm font-medium text-text-primary",
95
- children: y("auth.auditLog.emphasisTitle", "What this log captures")
92
+ children: v("auth.auditLog.emphasisTitle", "What this log captures")
96
93
  }), /* @__PURE__ */ l("p", {
97
94
  className: "mt-1 text-sm text-text-secondary",
98
- children: y("auth.auditLog.emphasisBody", "Successful and failed sign-ins, logouts, password and two-factor changes, and detected anomalies — newest first.")
95
+ children: v("auth.auditLog.emphasisBody", "Successful and failed sign-ins, logouts, password and two-factor changes, and detected anomalies — newest first.")
99
96
  })]
100
97
  }),
101
- j && /* @__PURE__ */ u("div", {
98
+ A && /* @__PURE__ */ u("div", {
102
99
  role: "alert",
103
100
  className: "mb-6 flex items-start gap-3 p-4 rounded-xl bg-status-error-bg-subtle border border-status-error-border",
104
- children: [/* @__PURE__ */ l(t, {
101
+ children: [/* @__PURE__ */ l(n, {
105
102
  className: "size-5 text-status-error-text flex-shrink-0 mt-0.5",
106
103
  "aria-hidden": "true"
107
104
  }), /* @__PURE__ */ l("p", {
108
105
  className: "text-sm text-status-error-text",
109
- children: j
106
+ children: A
110
107
  })]
111
108
  }),
112
- /* @__PURE__ */ l(i, {
113
- open: N,
114
- onOpenChange: P,
115
- title: y("auth.auditLog.exportQuotaTitle", "Export Limit Reached"),
116
- description: y("auth.auditLog.exportQuotaDescription", "You have reached your export limit for this month."),
117
- confirmText: y("auth.auditLog.exportQuotaViewBilling", "View Billing"),
118
- cancelText: y("auth.auditLog.exportQuotaDismiss", "Dismiss"),
109
+ /* @__PURE__ */ l(a, {
110
+ open: M,
111
+ onOpenChange: N,
112
+ title: v("auth.auditLog.exportQuotaTitle", "Export Limit Reached"),
113
+ description: v("auth.auditLog.exportQuotaDescription", "You have reached your export limit for this month."),
114
+ confirmText: v("auth.auditLog.exportQuotaViewBilling", "View Billing"),
115
+ cancelText: v("auth.auditLog.exportQuotaDismiss", "Dismiss"),
119
116
  variant: "warning",
120
117
  onConfirm: () => window.location.assign("/billing")
121
118
  }),
122
- T ? /* @__PURE__ */ u("div", {
119
+ w ? /* @__PURE__ */ u("div", {
123
120
  "aria-live": "polite",
124
121
  "aria-busy": "true",
125
122
  className: "text-center py-12",
126
- children: [/* @__PURE__ */ l(n, {
123
+ children: [/* @__PURE__ */ l(r, {
127
124
  className: "size-8 mx-auto text-text-link animate-spin",
128
125
  "aria-hidden": "true"
129
126
  }), /* @__PURE__ */ l("p", {
130
127
  className: "mt-3 text-text-secondary",
131
- children: y("auth.auditLog.loading", "Loading audit events...")
128
+ children: v("auth.auditLog.loading", "Loading audit events...")
132
129
  })]
133
- }) : E ? /* @__PURE__ */ l("div", {
130
+ }) : T ? /* @__PURE__ */ l("div", {
134
131
  role: "alert",
135
132
  children: /* @__PURE__ */ l(m, {
136
133
  illustration: "empty-generic",
137
- title: y("auth.auditLog.error", "Unable to load audit events"),
138
- description: y("auth.auditLog.errorDesc", "Something went wrong fetching your security events. Please try again in a moment.")
134
+ title: v("auth.auditLog.error", "Unable to load audit events"),
135
+ description: v("auth.auditLog.errorDesc", "Something went wrong fetching your security events. Please try again in a moment.")
139
136
  })
140
- }) : D.length === 0 ? /* @__PURE__ */ l("output", {
137
+ }) : E.length === 0 ? /* @__PURE__ */ l("output", {
141
138
  className: "block",
142
139
  children: /* @__PURE__ */ l(m, {
143
140
  illustration: "empty-notifications",
144
- title: y("auth.auditLog.noEvents", "No security events recorded"),
145
- description: y("auth.auditLog.noEventsDesc", "As you sign in and change security settings, those events will appear here.")
141
+ title: v("auth.auditLog.noEvents", "No security events recorded"),
142
+ description: v("auth.auditLog.noEventsDesc", "As you sign in and change security settings, those events will appear here.")
146
143
  })
147
144
  }) : /* @__PURE__ */ u(c, { children: [/* @__PURE__ */ l("div", {
148
145
  className: "space-y-4",
149
146
  "aria-live": "polite",
150
- "aria-busy": T,
151
- children: D.map((e) => /* @__PURE__ */ l("div", {
147
+ "aria-busy": w,
148
+ children: E.map((e) => /* @__PURE__ */ l("div", {
152
149
  className: "p-6 rounded-xl border border-border-subtle bg-bg-surface shadow-sm",
153
150
  children: /* @__PURE__ */ u("div", {
154
151
  className: "flex items-start gap-4",
@@ -156,7 +153,7 @@ function v() {
156
153
  /* @__PURE__ */ l("div", {
157
154
  className: "flex size-10 shrink-0 items-center justify-center rounded-full bg-bg-sunken text-xs font-semibold text-text-secondary",
158
155
  "aria-hidden": "true",
159
- children: I(e.action)
156
+ children: F(e.action)
160
157
  }),
161
158
  /* @__PURE__ */ u("div", {
162
159
  className: "flex-1",
@@ -169,48 +166,48 @@ function v() {
169
166
  className: "text-sm text-text-secondary mb-2",
170
167
  children: /* @__PURE__ */ l("time", {
171
168
  dateTime: e.createdAt,
172
- children: r(e.createdAt)
169
+ children: i(e.createdAt)
173
170
  })
174
171
  }),
175
172
  /* @__PURE__ */ u("p", {
176
173
  className: "text-sm text-text-secondary",
177
- children: [e.description ?? y("auth.auditLog.defaultDescription", "Authentication event"), e.ipAddress && ` • ${e.ipAddress}`]
174
+ children: [e.description ?? v("auth.auditLog.defaultDescription", "Authentication event"), e.ipAddress && ` • ${e.ipAddress}`]
178
175
  })
179
176
  ]
180
177
  }),
181
178
  e.action.includes("FAILURE") && /* @__PURE__ */ l("output", {
182
179
  className: "px-3 py-1 rounded-full bg-status-error-bg-subtle text-status-error-text text-sm font-medium",
183
- children: y("auth.auditLog.failed", "Failed")
180
+ children: v("auth.auditLog.failed", "Failed")
184
181
  })
185
182
  ]
186
183
  })
187
184
  }, e.id))
188
185
  }), /* @__PURE__ */ u("nav", {
189
- "aria-label": y("auth.auditLog.paginationAriaLabel", "Audit log pagination"),
186
+ "aria-label": v("auth.auditLog.paginationAriaLabel", "Audit log pagination"),
190
187
  className: "mt-6 flex items-center justify-between",
191
188
  children: [/* @__PURE__ */ u("p", {
192
189
  className: "text-sm text-text-secondary",
193
190
  children: [
194
- y("auth.auditLog.page", "Page"),
191
+ v("auth.auditLog.page", "Page"),
195
192
  " ",
196
- b
193
+ y
197
194
  ]
198
195
  }), /* @__PURE__ */ u("div", {
199
196
  className: "flex gap-2",
200
197
  children: [/* @__PURE__ */ l("button", {
201
198
  type: "button",
202
- onClick: () => x((e) => Math.max(1, e - 1)),
203
- disabled: b === 1,
204
- "aria-label": y("auth.auditLog.previousPageAriaLabel", "Go to previous page"),
199
+ onClick: () => b((e) => Math.max(1, e - 1)),
200
+ disabled: y === 1,
201
+ "aria-label": v("auth.auditLog.previousPageAriaLabel", "Go to previous page"),
205
202
  className: "px-4 py-2 rounded-lg border border-border-default text-text-primary bg-bg-surface disabled:opacity-50 disabled:cursor-not-allowed hover:bg-bg-sunken transition-colors",
206
- children: y("auth.auditLog.previous", "Previous")
203
+ children: v("auth.auditLog.previous", "Previous")
207
204
  }), /* @__PURE__ */ l("button", {
208
205
  type: "button",
209
- onClick: () => x((e) => e + 1),
210
- disabled: !O,
211
- "aria-label": y("auth.auditLog.nextPageAriaLabel", "Go to next page"),
206
+ onClick: () => b((e) => e + 1),
207
+ disabled: !D,
208
+ "aria-label": v("auth.auditLog.nextPageAriaLabel", "Go to next page"),
212
209
  className: "px-4 py-2 rounded-lg border border-border-default text-text-primary bg-bg-surface disabled:opacity-50 disabled:cursor-not-allowed hover:bg-bg-sunken transition-colors",
213
- children: y("auth.auditLog.next", "Next")
210
+ children: v("auth.auditLog.next", "Next")
214
211
  })]
215
212
  })]
216
213
  })] })
@@ -219,8 +216,8 @@ function v() {
219
216
  className: "max-w-4xl mx-auto p-6",
220
217
  children: /* @__PURE__ */ l(m, {
221
218
  illustration: "empty-generic",
222
- title: y("auth.auditLog.notAvailable", "Page Not Available"),
223
- description: y("auth.auditLog.notEnabled", "The Security Audit Log is not enabled for your account.")
219
+ title: v("auth.auditLog.notAvailable", "Page Not Available"),
220
+ description: v("auth.auditLog.notEnabled", "The Security Audit Log is not enabled for your account.")
224
221
  })
225
222
  });
226
223
  }
@@ -1 +1 @@
1
- {"version":3,"file":"AuditLogPage.js","names":[],"sources":["../../../src/auth/pages/AuditLogPage.tsx"],"sourcesContent":["import { useState } from \"react\";\nimport { useI18n } from \"@burdenoff/fe-libs/shared/providers/shell\";\nimport { isQuotaExhaustedError } from \"@burdenoff/fe-libs/shared/utils\";\nimport {\n EmphasisPanel,\n IllustratedEmptyState,\n PagePurpose,\n} from \"@burdenoff/fe-libs/ui\";\nimport { useGatewayClient } from \"@burdenoff/fe-libs/shared/graphql\";\nimport { useFeatureFlagDetails } from \"@burdenoff/fe-libs/shared/feature-flags\";\nimport { formatDateTime } from \"../utils/formatters\";\nimport { AlertIcon, SpinnerIcon } from \"../components/AuthIcons\";\nimport { ConfirmActionDialog } from \"../components/ConfirmActionDialog\";\nimport {\n useAuthAuditLogsQuery,\n type AuditLogEntry_AuthAuditLogFragment,\n} from \"../../generated/global-operations\";\nimport { useCreateAuthExportJobMutation } from \"../../generated/wspace-operations\";\n\nconst PAGE_SIZE = 20;\n\n/**\n * Security audit log page with pagination and export\n *\n * Fragment: AuditLogEntry_AuthAuditLog (graphql/operations/global/fragments/AuditLogEntry.fragment.graphql)\n * Query: AuthAuditLogs (graphql/operations/global/queries/auditLog.graphql)\n *\n * TODO: When authAuditLogs backend migrates to AuthAuditLogConnection type,\n * update this query to use: authAuditLogs { auditLogs: [...], totalCount, hasMore }\n * and remove the client-side hasMore approximation below.\n */\nexport function AuditLogPage() {\n const { t } = useI18n();\n const tr = (key: string, fallback: string): string => {\n const translated = t(key);\n return translated === key ? fallback : translated;\n };\n\n const [page, setPage] = useState(1);\n const offset = (page - 1) * PAGE_SIZE;\n\n // Default-deny: only show when the flag is explicitly enabled in the system.\n // useFeatureFlag uses default-allow (returns true when not found), so we use\n // useFeatureFlagDetails + explicit value check instead.\n const auditLogFlag = useFeatureFlagDetails(\"auth.audit-log\");\n const showAuditLog = auditLogFlag?.value === true;\n\n const { data, loading, error } = useAuthAuditLogsQuery({\n skip: !showAuditLog,\n fetchPolicy: \"cache-and-network\",\n variables: { pagination: { limit: PAGE_SIZE, offset } },\n });\n\n const auditLogs = data?.authAuditLogs ?? [];\n // Approximate hasMore: if we received a full page there may be more\n const hasMore = auditLogs.length === PAGE_SIZE;\n\n const wspaceClient = useGatewayClient(\"workspace\");\n const [createExportJob, { loading: isExporting }] =\n useCreateAuthExportJobMutation({ client: wspaceClient });\n const [exportError, setExportError] = useState<string | null>(null);\n const [showExportQuotaDialog, setShowExportQuotaDialog] = useState(false);\n\n const handleExport = async () => {\n setExportError(null);\n try {\n await createExportJob({\n variables: {\n input: {\n name: \"Audit Log Export\",\n // permission_audit_logs covers auth audit logs in the export system\n entityTypes: [\"permission_audit_logs\"],\n format: \"CSV\",\n exportScope: \"INDIVIDUAL\",\n includeMetadata: true,\n },\n },\n });\n } catch (err) {\n if (isQuotaExhaustedError(err)) {\n setShowExportQuotaDialog(true);\n setExportError(\n tr(\n \"auth.auditLog.exportQuotaReached\",\n \"You have reached your export limit for this month.\",\n ),\n );\n return;\n }\n setExportError(\n err instanceof Error ? err.message : \"Failed to start export\",\n );\n }\n };\n\n const getEventMarker = (eventType: string) => {\n switch (eventType) {\n case \"LOGIN_SUCCESS\":\n return \"OK\";\n case \"LOGIN_FAILURE\":\n return \"ER\";\n case \"LOGOUT\":\n return \"LO\";\n case \"PASSWORD_CHANGE\":\n return \"PW\";\n case \"MFA_ENABLED\":\n case \"MFA_VERIFIED\":\n return \"MF\";\n case \"ANOMALY_DETECTED\":\n return \"AL\";\n default:\n return \"AU\";\n }\n };\n\n if (!showAuditLog) {\n return (\n <div className=\"max-w-4xl mx-auto p-6\">\n <IllustratedEmptyState\n illustration=\"empty-generic\"\n title={tr(\"auth.auditLog.notAvailable\", \"Page Not Available\")}\n description={tr(\n \"auth.auditLog.notEnabled\",\n \"The Security Audit Log is not enabled for your account.\",\n )}\n />\n </div>\n );\n }\n\n return (\n <div className=\"max-w-4xl mx-auto p-6\">\n {/* Header */}\n <div className=\"flex items-center justify-between mb-8\">\n <h1 className=\"text-3xl font-semibold text-text-primary\">\n {tr(\"auth.auditLog.title\", \"Security Audit Log\")}\n </h1>\n <button\n type=\"button\"\n onClick={() => void handleExport()}\n disabled={isExporting}\n aria-label={tr(\"auth.auditLog.exportAriaLabel\", \"Export audit log\")}\n className=\"flex items-center gap-2 px-4 py-2.5 rounded-xl border border-border-default text-text-primary bg-bg-surface hover:bg-bg-sunken disabled:opacity-50 disabled:cursor-not-allowed transition-colors font-medium\"\n >\n {isExporting ? (\n <>\n <SpinnerIcon className=\"size-4 animate-spin\" aria-hidden=\"true\" />\n {tr(\"auth.auditLog.exporting\", \"Exporting...\")}\n </>\n ) : (\n <>\n <svg\n className=\"size-4\"\n fill=\"none\"\n viewBox=\"0 0 24 24\"\n stroke=\"currentColor\"\n aria-hidden=\"true\"\n >\n <path\n strokeLinecap=\"round\"\n strokeLinejoin=\"round\"\n strokeWidth={2}\n d=\"M4 16v1a3 3 0 003 3h10a3 3 0 003-3v-1m-4-4l-4 4m0 0l-4-4m4 4V4\"\n />\n </svg>\n {tr(\"auth.auditLog.export\", \"Export\")}\n </>\n )}\n </button>\n </div>\n\n <PagePurpose className=\"mb-6\">\n {tr(\n \"auth.auditLog.purpose\",\n \"A running record of security-relevant events on your account — sign-ins, password and MFA changes, and anomalies. Scan it to spot anything you didn't do, and export it for your own records or compliance.\",\n )}\n </PagePurpose>\n\n {/* Emphasis: what's tracked — the one highlighted zone */}\n <EmphasisPanel className=\"mb-6\">\n <p className=\"text-sm font-medium text-text-primary\">\n {tr(\"auth.auditLog.emphasisTitle\", \"What this log captures\")}\n </p>\n <p className=\"mt-1 text-sm text-text-secondary\">\n {tr(\n \"auth.auditLog.emphasisBody\",\n \"Successful and failed sign-ins, logouts, password and two-factor changes, and detected anomalies — newest first.\",\n )}\n </p>\n </EmphasisPanel>\n\n {/* Export error */}\n {exportError && (\n <div\n role=\"alert\"\n className=\"mb-6 flex items-start gap-3 p-4 rounded-xl bg-status-error-bg-subtle border border-status-error-border\"\n >\n <AlertIcon\n className=\"size-5 text-status-error-text flex-shrink-0 mt-0.5\"\n aria-hidden=\"true\"\n />\n <p className=\"text-sm text-status-error-text\">{exportError}</p>\n </div>\n )}\n\n <ConfirmActionDialog\n open={showExportQuotaDialog}\n onOpenChange={setShowExportQuotaDialog}\n title={tr(\"auth.auditLog.exportQuotaTitle\", \"Export Limit Reached\")}\n description={tr(\n \"auth.auditLog.exportQuotaDescription\",\n \"You have reached your export limit for this month.\",\n )}\n confirmText={tr(\"auth.auditLog.exportQuotaViewBilling\", \"View Billing\")}\n cancelText={tr(\"auth.auditLog.exportQuotaDismiss\", \"Dismiss\")}\n variant=\"warning\"\n onConfirm={() => window.location.assign(\"/billing\")}\n />\n\n {loading ? (\n <div aria-live=\"polite\" aria-busy=\"true\" className=\"text-center py-12\">\n <SpinnerIcon\n className=\"size-8 mx-auto text-text-link animate-spin\"\n aria-hidden=\"true\"\n />\n <p className=\"mt-3 text-text-secondary\">\n {tr(\"auth.auditLog.loading\", \"Loading audit events...\")}\n </p>\n </div>\n ) : error ? (\n <div role=\"alert\">\n <IllustratedEmptyState\n illustration=\"empty-generic\"\n title={tr(\"auth.auditLog.error\", \"Unable to load audit events\")}\n description={tr(\n \"auth.auditLog.errorDesc\",\n \"Something went wrong fetching your security events. Please try again in a moment.\",\n )}\n />\n </div>\n ) : auditLogs.length === 0 ? (\n <output className=\"block\">\n <IllustratedEmptyState\n illustration=\"empty-notifications\"\n title={tr(\"auth.auditLog.noEvents\", \"No security events recorded\")}\n description={tr(\n \"auth.auditLog.noEventsDesc\",\n \"As you sign in and change security settings, those events will appear here.\",\n )}\n />\n </output>\n ) : (\n <>\n <div className=\"space-y-4\" aria-live=\"polite\" aria-busy={loading}>\n {auditLogs.map((event: AuditLogEntry_AuthAuditLogFragment) => (\n <div\n key={event.id}\n className=\"p-6 rounded-xl border border-border-subtle bg-bg-surface shadow-sm\"\n >\n <div className=\"flex items-start gap-4\">\n <div\n className=\"flex size-10 shrink-0 items-center justify-center rounded-full bg-bg-sunken text-xs font-semibold text-text-secondary\"\n aria-hidden=\"true\"\n >\n {getEventMarker(event.action)}\n </div>\n <div className=\"flex-1\">\n <h3 className=\"text-lg font-medium text-text-primary mb-1\">\n {event.action.replace(/_/g, \" \")}\n </h3>\n <p className=\"text-sm text-text-secondary mb-2\">\n <time dateTime={event.createdAt}>\n {formatDateTime(event.createdAt)}\n </time>\n </p>\n <p className=\"text-sm text-text-secondary\">\n {event.description ??\n tr(\n \"auth.auditLog.defaultDescription\",\n \"Authentication event\",\n )}\n {event.ipAddress && ` • ${event.ipAddress}`}\n </p>\n </div>\n {event.action.includes(\"FAILURE\") && (\n <output className=\"px-3 py-1 rounded-full bg-status-error-bg-subtle text-status-error-text text-sm font-medium\">\n {tr(\"auth.auditLog.failed\", \"Failed\")}\n </output>\n )}\n </div>\n </div>\n ))}\n </div>\n\n {/* Pagination */}\n <nav\n aria-label={tr(\n \"auth.auditLog.paginationAriaLabel\",\n \"Audit log pagination\",\n )}\n className=\"mt-6 flex items-center justify-between\"\n >\n <p className=\"text-sm text-text-secondary\">\n {tr(\"auth.auditLog.page\", \"Page\")} {page}\n </p>\n <div className=\"flex gap-2\">\n <button\n type=\"button\"\n onClick={() => setPage((prev) => Math.max(1, prev - 1))}\n disabled={page === 1}\n aria-label={tr(\n \"auth.auditLog.previousPageAriaLabel\",\n \"Go to previous page\",\n )}\n className=\"px-4 py-2 rounded-lg border border-border-default text-text-primary bg-bg-surface disabled:opacity-50 disabled:cursor-not-allowed hover:bg-bg-sunken transition-colors\"\n >\n {tr(\"auth.auditLog.previous\", \"Previous\")}\n </button>\n <button\n type=\"button\"\n onClick={() => setPage((prev) => prev + 1)}\n disabled={!hasMore}\n aria-label={tr(\n \"auth.auditLog.nextPageAriaLabel\",\n \"Go to next page\",\n )}\n className=\"px-4 py-2 rounded-lg border border-border-default text-text-primary bg-bg-surface disabled:opacity-50 disabled:cursor-not-allowed hover:bg-bg-sunken transition-colors\"\n >\n {tr(\"auth.auditLog.next\", \"Next\")}\n </button>\n </div>\n </nav>\n </>\n )}\n </div>\n );\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"AuditLogPage.js","names":[],"sources":["../../../src/auth/pages/AuditLogPage.tsx"],"sourcesContent":["import { useState } from \"react\";\nimport { useTr } from \"../../shared/hooks/useTr\";\nimport { isQuotaExhaustedError } from \"@burdenoff/fe-libs/shared/utils\";\nimport {\n EmphasisPanel,\n IllustratedEmptyState,\n PagePurpose,\n} from \"@burdenoff/fe-libs/ui\";\nimport { useGatewayClient } from \"@burdenoff/fe-libs/shared/graphql\";\nimport { useFeatureFlagDetails } from \"@burdenoff/fe-libs/shared/feature-flags\";\nimport { formatDateTime } from \"../utils/formatters\";\nimport { AlertIcon, SpinnerIcon } from \"../components/AuthIcons\";\nimport { ConfirmActionDialog } from \"../components/ConfirmActionDialog\";\nimport {\n useAuthAuditLogsQuery,\n type AuditLogEntry_AuthAuditLogFragment,\n} from \"../../generated/global-operations\";\nimport { useCreateAuthExportJobMutation } from \"../../generated/wspace-operations\";\n\nconst PAGE_SIZE = 20;\n\n/**\n * Security audit log page with pagination and export\n *\n * Fragment: AuditLogEntry_AuthAuditLog (graphql/operations/global/fragments/AuditLogEntry.fragment.graphql)\n * Query: AuthAuditLogs (graphql/operations/global/queries/auditLog.graphql)\n *\n * TODO: When authAuditLogs backend migrates to AuthAuditLogConnection type,\n * update this query to use: authAuditLogs { auditLogs: [...], totalCount, hasMore }\n * and remove the client-side hasMore approximation below.\n */\nexport function AuditLogPage() {\n const tr = useTr();\n\n const [page, setPage] = useState(1);\n const offset = (page - 1) * PAGE_SIZE;\n\n // Default-deny: only show when the flag is explicitly enabled in the system.\n // useFeatureFlag uses default-allow (returns true when not found), so we use\n // useFeatureFlagDetails + explicit value check instead.\n const auditLogFlag = useFeatureFlagDetails(\"auth.audit-log\");\n const showAuditLog = auditLogFlag?.value === true;\n\n const { data, loading, error } = useAuthAuditLogsQuery({\n skip: !showAuditLog,\n fetchPolicy: \"cache-and-network\",\n variables: { pagination: { limit: PAGE_SIZE, offset } },\n });\n\n const auditLogs = data?.authAuditLogs ?? [];\n // Approximate hasMore: if we received a full page there may be more\n const hasMore = auditLogs.length === PAGE_SIZE;\n\n const wspaceClient = useGatewayClient(\"workspace\");\n const [createExportJob, { loading: isExporting }] =\n useCreateAuthExportJobMutation({ client: wspaceClient });\n const [exportError, setExportError] = useState<string | null>(null);\n const [showExportQuotaDialog, setShowExportQuotaDialog] = useState(false);\n\n const handleExport = async () => {\n setExportError(null);\n try {\n await createExportJob({\n variables: {\n input: {\n name: \"Audit Log Export\",\n // permission_audit_logs covers auth audit logs in the export system\n entityTypes: [\"permission_audit_logs\"],\n format: \"CSV\",\n exportScope: \"INDIVIDUAL\",\n includeMetadata: true,\n },\n },\n });\n } catch (err) {\n if (isQuotaExhaustedError(err)) {\n setShowExportQuotaDialog(true);\n setExportError(\n tr(\n \"auth.auditLog.exportQuotaReached\",\n \"You have reached your export limit for this month.\",\n ),\n );\n return;\n }\n setExportError(\n err instanceof Error ? err.message : \"Failed to start export\",\n );\n }\n };\n\n const getEventMarker = (eventType: string) => {\n switch (eventType) {\n case \"LOGIN_SUCCESS\":\n return \"OK\";\n case \"LOGIN_FAILURE\":\n return \"ER\";\n case \"LOGOUT\":\n return \"LO\";\n case \"PASSWORD_CHANGE\":\n return \"PW\";\n case \"MFA_ENABLED\":\n case \"MFA_VERIFIED\":\n return \"MF\";\n case \"ANOMALY_DETECTED\":\n return \"AL\";\n default:\n return \"AU\";\n }\n };\n\n if (!showAuditLog) {\n return (\n <div className=\"max-w-4xl mx-auto p-6\">\n <IllustratedEmptyState\n illustration=\"empty-generic\"\n title={tr(\"auth.auditLog.notAvailable\", \"Page Not Available\")}\n description={tr(\n \"auth.auditLog.notEnabled\",\n \"The Security Audit Log is not enabled for your account.\",\n )}\n />\n </div>\n );\n }\n\n return (\n <div className=\"max-w-4xl mx-auto p-6\">\n {/* Header */}\n <div className=\"flex items-center justify-between mb-8\">\n <h1 className=\"text-3xl font-semibold text-text-primary\">\n {tr(\"auth.auditLog.title\", \"Security Audit Log\")}\n </h1>\n <button\n type=\"button\"\n onClick={() => void handleExport()}\n disabled={isExporting}\n aria-label={tr(\"auth.auditLog.exportAriaLabel\", \"Export audit log\")}\n className=\"flex items-center gap-2 px-4 py-2.5 rounded-xl border border-border-default text-text-primary bg-bg-surface hover:bg-bg-sunken disabled:opacity-50 disabled:cursor-not-allowed transition-colors font-medium\"\n >\n {isExporting ? (\n <>\n <SpinnerIcon className=\"size-4 animate-spin\" aria-hidden=\"true\" />\n {tr(\"auth.auditLog.exporting\", \"Exporting...\")}\n </>\n ) : (\n <>\n <svg\n className=\"size-4\"\n fill=\"none\"\n viewBox=\"0 0 24 24\"\n stroke=\"currentColor\"\n aria-hidden=\"true\"\n >\n <path\n strokeLinecap=\"round\"\n strokeLinejoin=\"round\"\n strokeWidth={2}\n d=\"M4 16v1a3 3 0 003 3h10a3 3 0 003-3v-1m-4-4l-4 4m0 0l-4-4m4 4V4\"\n />\n </svg>\n {tr(\"auth.auditLog.export\", \"Export\")}\n </>\n )}\n </button>\n </div>\n\n <PagePurpose className=\"mb-6\">\n {tr(\n \"auth.auditLog.purpose\",\n \"A running record of security-relevant events on your account — sign-ins, password and MFA changes, and anomalies. Scan it to spot anything you didn't do, and export it for your own records or compliance.\",\n )}\n </PagePurpose>\n\n {/* Emphasis: what's tracked — the one highlighted zone */}\n <EmphasisPanel className=\"mb-6\">\n <p className=\"text-sm font-medium text-text-primary\">\n {tr(\"auth.auditLog.emphasisTitle\", \"What this log captures\")}\n </p>\n <p className=\"mt-1 text-sm text-text-secondary\">\n {tr(\n \"auth.auditLog.emphasisBody\",\n \"Successful and failed sign-ins, logouts, password and two-factor changes, and detected anomalies — newest first.\",\n )}\n </p>\n </EmphasisPanel>\n\n {/* Export error */}\n {exportError && (\n <div\n role=\"alert\"\n className=\"mb-6 flex items-start gap-3 p-4 rounded-xl bg-status-error-bg-subtle border border-status-error-border\"\n >\n <AlertIcon\n className=\"size-5 text-status-error-text flex-shrink-0 mt-0.5\"\n aria-hidden=\"true\"\n />\n <p className=\"text-sm text-status-error-text\">{exportError}</p>\n </div>\n )}\n\n <ConfirmActionDialog\n open={showExportQuotaDialog}\n onOpenChange={setShowExportQuotaDialog}\n title={tr(\"auth.auditLog.exportQuotaTitle\", \"Export Limit Reached\")}\n description={tr(\n \"auth.auditLog.exportQuotaDescription\",\n \"You have reached your export limit for this month.\",\n )}\n confirmText={tr(\"auth.auditLog.exportQuotaViewBilling\", \"View Billing\")}\n cancelText={tr(\"auth.auditLog.exportQuotaDismiss\", \"Dismiss\")}\n variant=\"warning\"\n onConfirm={() => window.location.assign(\"/billing\")}\n />\n\n {loading ? (\n <div aria-live=\"polite\" aria-busy=\"true\" className=\"text-center py-12\">\n <SpinnerIcon\n className=\"size-8 mx-auto text-text-link animate-spin\"\n aria-hidden=\"true\"\n />\n <p className=\"mt-3 text-text-secondary\">\n {tr(\"auth.auditLog.loading\", \"Loading audit events...\")}\n </p>\n </div>\n ) : error ? (\n <div role=\"alert\">\n <IllustratedEmptyState\n illustration=\"empty-generic\"\n title={tr(\"auth.auditLog.error\", \"Unable to load audit events\")}\n description={tr(\n \"auth.auditLog.errorDesc\",\n \"Something went wrong fetching your security events. Please try again in a moment.\",\n )}\n />\n </div>\n ) : auditLogs.length === 0 ? (\n <output className=\"block\">\n <IllustratedEmptyState\n illustration=\"empty-notifications\"\n title={tr(\"auth.auditLog.noEvents\", \"No security events recorded\")}\n description={tr(\n \"auth.auditLog.noEventsDesc\",\n \"As you sign in and change security settings, those events will appear here.\",\n )}\n />\n </output>\n ) : (\n <>\n <div className=\"space-y-4\" aria-live=\"polite\" aria-busy={loading}>\n {auditLogs.map((event: AuditLogEntry_AuthAuditLogFragment) => (\n <div\n key={event.id}\n className=\"p-6 rounded-xl border border-border-subtle bg-bg-surface shadow-sm\"\n >\n <div className=\"flex items-start gap-4\">\n <div\n className=\"flex size-10 shrink-0 items-center justify-center rounded-full bg-bg-sunken text-xs font-semibold text-text-secondary\"\n aria-hidden=\"true\"\n >\n {getEventMarker(event.action)}\n </div>\n <div className=\"flex-1\">\n <h3 className=\"text-lg font-medium text-text-primary mb-1\">\n {event.action.replace(/_/g, \" \")}\n </h3>\n <p className=\"text-sm text-text-secondary mb-2\">\n <time dateTime={event.createdAt}>\n {formatDateTime(event.createdAt)}\n </time>\n </p>\n <p className=\"text-sm text-text-secondary\">\n {event.description ??\n tr(\n \"auth.auditLog.defaultDescription\",\n \"Authentication event\",\n )}\n {event.ipAddress && ` • ${event.ipAddress}`}\n </p>\n </div>\n {event.action.includes(\"FAILURE\") && (\n <output className=\"px-3 py-1 rounded-full bg-status-error-bg-subtle text-status-error-text text-sm font-medium\">\n {tr(\"auth.auditLog.failed\", \"Failed\")}\n </output>\n )}\n </div>\n </div>\n ))}\n </div>\n\n {/* Pagination */}\n <nav\n aria-label={tr(\n \"auth.auditLog.paginationAriaLabel\",\n \"Audit log pagination\",\n )}\n className=\"mt-6 flex items-center justify-between\"\n >\n <p className=\"text-sm text-text-secondary\">\n {tr(\"auth.auditLog.page\", \"Page\")} {page}\n </p>\n <div className=\"flex gap-2\">\n <button\n type=\"button\"\n onClick={() => setPage((prev) => Math.max(1, prev - 1))}\n disabled={page === 1}\n aria-label={tr(\n \"auth.auditLog.previousPageAriaLabel\",\n \"Go to previous page\",\n )}\n className=\"px-4 py-2 rounded-lg border border-border-default text-text-primary bg-bg-surface disabled:opacity-50 disabled:cursor-not-allowed hover:bg-bg-sunken transition-colors\"\n >\n {tr(\"auth.auditLog.previous\", \"Previous\")}\n </button>\n <button\n type=\"button\"\n onClick={() => setPage((prev) => prev + 1)}\n disabled={!hasMore}\n aria-label={tr(\n \"auth.auditLog.nextPageAriaLabel\",\n \"Go to next page\",\n )}\n className=\"px-4 py-2 rounded-lg border border-border-default text-text-primary bg-bg-surface disabled:opacity-50 disabled:cursor-not-allowed hover:bg-bg-sunken transition-colors\"\n >\n {tr(\"auth.auditLog.next\", \"Next\")}\n </button>\n </div>\n </nav>\n </>\n )}\n </div>\n );\n}\n"],"mappings":"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"}
@@ -1,10 +1,10 @@
1
1
  import { useAuthStore as e } from "../store/authStore.js";
2
- import { useAuthOperations as t } from "../hooks/useAuthOperations.js";
3
- import { AuthLayout as n } from "../components/AuthLayout.js";
4
- import { SpinnerIcon as r } from "../components/AuthIcons.js";
5
- import { useCallback as i, useEffect as a, useRef as o, useState as s } from "react";
6
- import { useSearchParams as c } from "react-router";
7
- import { useI18n as l } from "@burdenoff/fe-libs/shared/providers/shell";
2
+ import { useTr as t } from "../../shared/hooks/useTr.js";
3
+ import { useAuthOperations as n } from "../hooks/useAuthOperations.js";
4
+ import { AuthLayout as r } from "../components/AuthLayout.js";
5
+ import { SpinnerIcon as i } from "../components/AuthIcons.js";
6
+ import { useCallback as a, useEffect as o, useRef as s, useState as c } from "react";
7
+ import { useSearchParams as l } from "react-router";
8
8
  import { Fragment as u, jsx as d, jsxs as f } from "react/jsx-runtime";
9
9
  import { Button as p } from "@burdenoff/fe-libs/button";
10
10
  //#region src/auth/pages/EmailVerifyPage.tsx
@@ -14,64 +14,61 @@ function h(e) {
14
14
  window.location.href = n;
15
15
  }
16
16
  function g() {
17
- let { t: g } = l(), _ = (e, t) => {
18
- let n = g(e);
19
- return n === e ? t : n;
20
- }, [v] = c(), y = v.get("token"), b = v.get("email"), { verifyEmail: x, resendVerification: S } = t(), C = e((e) => e.user), [w, T] = s("verifying"), E = o(b || void 0), [D, O] = s(!1), [k, A] = s(!1), [j, M] = s(m), N = o(!1);
21
- a(() => {
22
- if (!y || N.current) return;
23
- let e = `auth:verifyEmail:inflight:${y}`;
17
+ let g = t(), [_] = l(), v = _.get("token"), y = _.get("email"), { verifyEmail: b, resendVerification: x } = n(), S = e((e) => e.user), [C, w] = c("verifying"), T = s(y || void 0), [E, D] = c(!1), [O, k] = c(!1), [A, j] = c(m), M = s(!1);
18
+ o(() => {
19
+ if (!v || M.current) return;
20
+ let e = `auth:verifyEmail:inflight:${v}`;
24
21
  if (!(typeof window < "u" && window.sessionStorage.getItem(e))) {
25
- N.current = !0;
22
+ M.current = !0;
26
23
  try {
27
24
  window.sessionStorage.setItem(e, "1");
28
25
  } catch {}
29
- x(y).then((e) => {
30
- e.success ? (T("success"), e.email && (E.current = e.email)) : T("error");
31
- }).catch(() => T("error"));
26
+ b(v).then((e) => {
27
+ e.success ? (w("success"), e.email && (T.current = e.email)) : w("error");
28
+ }).catch(() => w("error"));
32
29
  }
33
- }, [y, x]), a(() => {
34
- if (w !== "success") return;
30
+ }, [v, b]), o(() => {
31
+ if (C !== "success") return;
35
32
  let e = setInterval(() => {
36
- M((t) => t <= 1 ? (clearInterval(e), h(E.current), 0) : t - 1);
33
+ j((t) => t <= 1 ? (clearInterval(e), h(T.current), 0) : t - 1);
37
34
  }, 1e3);
38
35
  return () => clearInterval(e);
39
- }, [w]);
40
- let P = async () => {
41
- let e = b || C?.email;
36
+ }, [C]);
37
+ let N = async () => {
38
+ let e = y || S?.email;
42
39
  if (!e) {
43
40
  console.error("No email available for resend");
44
41
  return;
45
42
  }
46
- O(!0);
43
+ D(!0);
47
44
  try {
48
- await S(e), A(!0);
45
+ await x(e), k(!0);
49
46
  } catch (e) {
50
47
  console.error("Failed to resend verification:", e);
51
48
  } finally {
52
- O(!1);
49
+ D(!1);
53
50
  }
54
- }, F = i(() => {
55
- h(E.current);
56
- }, []), I = i(() => {
51
+ }, P = a(() => {
52
+ h(T.current);
53
+ }, []), F = a(() => {
57
54
  h();
58
55
  }, []);
59
- return w === "verifying" ? /* @__PURE__ */ d(n, {
60
- title: _("auth.emailVerify.verifyingTitle", "Verifying your email"),
61
- subtitle: _("auth.emailVerify.verifyingSubtitle", "Please wait while we confirm your email address"),
56
+ return C === "verifying" ? /* @__PURE__ */ d(r, {
57
+ title: g("auth.emailVerify.verifyingTitle", "Verifying your email"),
58
+ subtitle: g("auth.emailVerify.verifyingSubtitle", "Please wait while we confirm your email address"),
62
59
  children: /* @__PURE__ */ f("div", {
63
60
  className: "flex flex-col items-center justify-center py-12",
64
61
  children: [/* @__PURE__ */ d("div", {
65
62
  className: "size-16 rounded-full bg-[var(--color-bg-accent)] flex items-center justify-center mb-6",
66
- children: /* @__PURE__ */ d(r, { className: "size-8 text-[var(--color-text-link)] animate-spin" })
63
+ children: /* @__PURE__ */ d(i, { className: "size-8 text-[var(--color-text-link)] animate-spin" })
67
64
  }), /* @__PURE__ */ d("p", {
68
65
  className: "text-[var(--color-text-secondary)]",
69
- children: _("auth.emailVerify.moment", "This will only take a moment...")
66
+ children: g("auth.emailVerify.moment", "This will only take a moment...")
70
67
  })]
71
68
  })
72
- }) : w === "success" ? /* @__PURE__ */ d(n, {
73
- title: _("auth.emailVerify.verifiedTitle", "Email verified!"),
74
- subtitle: _("auth.emailVerify.verifiedSubtitle", "Your email address has been confirmed"),
69
+ }) : C === "success" ? /* @__PURE__ */ d(r, {
70
+ title: g("auth.emailVerify.verifiedTitle", "Email verified!"),
71
+ subtitle: g("auth.emailVerify.verifiedSubtitle", "Your email address has been confirmed"),
75
72
  children: /* @__PURE__ */ f("div", {
76
73
  className: "flex flex-col items-center justify-center py-6",
77
74
  children: [
@@ -92,34 +89,34 @@ function g() {
92
89
  }),
93
90
  /* @__PURE__ */ d("p", {
94
91
  className: "text-center text-[var(--color-text-secondary)] mb-6",
95
- children: _("auth.emailVerify.successMessage", "Your email has been successfully verified. You can now sign in to access all features.")
92
+ children: g("auth.emailVerify.successMessage", "Your email has been successfully verified. You can now sign in to access all features.")
96
93
  }),
97
94
  /* @__PURE__ */ f("p", {
98
95
  className: "text-center text-sm text-[var(--color-text-tertiary)] mb-6",
99
96
  children: [
100
- _("auth.emailVerify.redirecting", "Redirecting to sign in in"),
97
+ g("auth.emailVerify.redirecting", "Redirecting to sign in in"),
101
98
  " ",
102
99
  /* @__PURE__ */ d("span", {
103
100
  className: "font-semibold text-[var(--color-text-primary)]",
104
- children: j
101
+ children: A
105
102
  }),
106
103
  " ",
107
- _("auth.emailVerify.seconds", "seconds...")
104
+ g("auth.emailVerify.seconds", "seconds...")
108
105
  ]
109
106
  }),
110
107
  /* @__PURE__ */ d(p, {
111
108
  type: "button",
112
- onClick: F,
109
+ onClick: P,
113
110
  variant: "default",
114
111
  size: "lg",
115
112
  className: "w-full font-semibold",
116
- children: _("auth.emailVerify.continueSignIn", "Continue to sign in now")
113
+ children: g("auth.emailVerify.continueSignIn", "Continue to sign in now")
117
114
  })
118
115
  ]
119
116
  })
120
- }) : /* @__PURE__ */ d(n, {
121
- title: _("auth.emailVerify.failedTitle", "Verification failed"),
122
- subtitle: _("auth.emailVerify.failedSubtitle", "We couldn't verify your email address"),
117
+ }) : /* @__PURE__ */ d(r, {
118
+ title: g("auth.emailVerify.failedTitle", "Verification failed"),
119
+ subtitle: g("auth.emailVerify.failedSubtitle", "We couldn't verify your email address"),
123
120
  children: /* @__PURE__ */ f("div", {
124
121
  className: "flex flex-col items-center justify-center py-6",
125
122
  children: [
@@ -140,28 +137,28 @@ function g() {
140
137
  }),
141
138
  /* @__PURE__ */ d("p", {
142
139
  className: "text-center text-[var(--color-text-secondary)] mb-8",
143
- children: _("auth.emailVerify.invalidLink", "The verification link is invalid or has expired. Please request a new verification email.")
140
+ children: g("auth.emailVerify.invalidLink", "The verification link is invalid or has expired. Please request a new verification email.")
144
141
  }),
145
- k ? /* @__PURE__ */ d("div", {
142
+ O ? /* @__PURE__ */ d("div", {
146
143
  className: "w-full p-4 rounded-xl bg-[var(--color-status-success-bg-subtle)] border border-[var(--color-status-success-border)] text-center mb-4",
147
144
  children: /* @__PURE__ */ d("p", {
148
145
  className: "text-sm text-[var(--color-status-success-text)]",
149
- children: _("auth.emailVerify.emailSent", "Verification email sent! Please check your inbox.")
146
+ children: g("auth.emailVerify.emailSent", "Verification email sent! Please check your inbox.")
150
147
  })
151
148
  }) : /* @__PURE__ */ d(p, {
152
149
  type: "button",
153
- onClick: P,
154
- disabled: D,
150
+ onClick: N,
151
+ disabled: E,
155
152
  variant: "default",
156
153
  size: "lg",
157
154
  className: "mb-4 w-full font-semibold",
158
- children: D ? /* @__PURE__ */ f(u, { children: [/* @__PURE__ */ d(r, { className: "size-5 animate-spin" }), /* @__PURE__ */ d("span", { children: "Sending…" })] }) : _("auth.emailVerify.resend", "Resend verification email")
155
+ children: E ? /* @__PURE__ */ f(u, { children: [/* @__PURE__ */ d(i, { className: "size-5 animate-spin" }), /* @__PURE__ */ d("span", { children: g("auth.emailVerify.sending", "Sending…") })] }) : g("auth.emailVerify.resend", "Resend verification email")
159
156
  }),
160
157
  /* @__PURE__ */ d("button", {
161
158
  type: "button",
162
- onClick: I,
159
+ onClick: F,
163
160
  className: "w-full py-3 px-4 rounded-xl border border-[var(--color-border-default)] bg-[var(--color-bg-surface)] text-[var(--color-text-primary)] font-medium hover:bg-[var(--color-bg-sunken)] transition-all",
164
- children: _("auth.emailVerify.backToSignIn", "Back to sign in")
161
+ children: g("auth.emailVerify.backToSignIn", "Back to sign in")
165
162
  })
166
163
  ]
167
164
  })