@burdenoff/microfe-auth 2026.624.2 → 2026.625.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 (41) hide show
  1. package/dist/auth/hooks/index.js +1 -0
  2. package/dist/auth/hooks/useActiveContextBootstrapMirror.js +27 -0
  3. package/dist/auth/hooks/useActiveContextBootstrapMirror.js.map +1 -0
  4. package/dist/auth/index.js +1 -0
  5. package/dist/auth/pages/AuditLogPage.js +73 -72
  6. package/dist/auth/pages/AuditLogPage.js.map +1 -1
  7. package/dist/auth/pages/EmailVerifyPage.js +58 -53
  8. package/dist/auth/pages/EmailVerifyPage.js.map +1 -1
  9. package/dist/auth/pages/LoginPage.js +222 -214
  10. package/dist/auth/pages/LoginPage.js.map +1 -1
  11. package/dist/auth/pages/MagicLinkPage.js +75 -68
  12. package/dist/auth/pages/MagicLinkPage.js.map +1 -1
  13. package/dist/auth/pages/MfaSetupPage.js +176 -138
  14. package/dist/auth/pages/MfaSetupPage.js.map +1 -1
  15. package/dist/auth/pages/MfaVerifyPage.js +60 -52
  16. package/dist/auth/pages/MfaVerifyPage.js.map +1 -1
  17. package/dist/auth/pages/PasswordResetPage.js +277 -262
  18. package/dist/auth/pages/PasswordResetPage.js.map +1 -1
  19. package/dist/auth/pages/ReactivatePage.js +54 -46
  20. package/dist/auth/pages/ReactivatePage.js.map +1 -1
  21. package/dist/auth/pages/RegisterPage.js +223 -213
  22. package/dist/auth/pages/RegisterPage.js.map +1 -1
  23. package/dist/auth/pages/SecurityPage.js +298 -267
  24. package/dist/auth/pages/SecurityPage.js.map +1 -1
  25. package/dist/auth/pages/SessionsPage.js +169 -160
  26. package/dist/auth/pages/SessionsPage.js.map +1 -1
  27. package/dist/auth/pages/SsoCallbackPage.js +53 -50
  28. package/dist/auth/pages/SsoCallbackPage.js.map +1 -1
  29. package/dist/auth/pages/SsoConfigPage.js +466 -444
  30. package/dist/auth/pages/SsoConfigPage.js.map +1 -1
  31. package/dist/auth/pages/SsoLoginPage.js +67 -59
  32. package/dist/auth/pages/SsoLoginPage.js.map +1 -1
  33. package/dist/auth/pages/UserDetailsPage.js +154 -144
  34. package/dist/auth/pages/UserDetailsPage.js.map +1 -1
  35. package/dist/auth/pages/UserProfilePage.js +211 -170
  36. package/dist/auth/pages/UserProfilePage.js.map +1 -1
  37. package/dist/auth/pages/UsersPage.js +572 -576
  38. package/dist/auth/pages/UsersPage.js.map +1 -1
  39. package/dist/index.js +7 -6
  40. package/dist/microfe-auth.css +1 -1
  41. package/package.json +2 -2
@@ -9,6 +9,7 @@ import "./useApiKeyOperations.js";
9
9
  import "./useAppOperations.js";
10
10
  import "./useWorkspaceOperations.js";
11
11
  import "./useContextSwitcherData.js";
12
+ import "./useActiveContextBootstrapMirror.js";
12
13
  import "./useDelegationOperations.js";
13
14
  import "./useUserOperations.js";
14
15
  import "./useSsoOperations.js";
@@ -0,0 +1,27 @@
1
+ import { useEffect as e } from "react";
2
+ //#region src/auth/hooks/useActiveContextBootstrapMirror.ts
3
+ function t({ storeOrganizationId: t, storeWorkspaceId: n, storeProjectId: r, activeOrganizationId: i, activeWorkspaceId: a, activeProjectId: o, storeWorkspaces: s, storeProjects: c, setOrganization: l, setWorkspace: u, setProject: d }) {
4
+ e(() => {
5
+ t && !i && l(t);
6
+ let e = i || t, f = !!n && !!e && !!s?.some((t) => t.id === n && t.organizationId === e);
7
+ n && !a && f && u(n);
8
+ let p = a || (f ? n : null), m = !!r && !!p && !!c?.some((e) => e.id === r && e.workspaceId === p);
9
+ r && !o && m && d(r);
10
+ }, [
11
+ t,
12
+ n,
13
+ r,
14
+ i,
15
+ a,
16
+ o,
17
+ s,
18
+ c,
19
+ l,
20
+ u,
21
+ d
22
+ ]);
23
+ }
24
+ //#endregion
25
+ export { t as useActiveContextBootstrapMirror };
26
+
27
+ //# sourceMappingURL=useActiveContextBootstrapMirror.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"useActiveContextBootstrapMirror.js","names":[],"sources":["../../../src/auth/hooks/useActiveContextBootstrapMirror.ts"],"sourcesContent":["import { useEffect } from \"react\";\n\ntype ContextId = string | null | undefined;\n\ninterface MirrorWorkspace {\n id: string;\n organizationId?: string | null;\n}\n\ninterface MirrorProject {\n id: string;\n workspaceId?: string | null;\n}\n\nexport interface ActiveContextBootstrapMirrorArgs {\n /** Auth-store `currentOrganizationId` */\n storeOrganizationId: ContextId;\n /** Auth-store `currentWorkspaceId` */\n storeWorkspaceId: ContextId;\n /** Auth-store `currentProjectId` */\n storeProjectId: ContextId;\n /** ActiveContext `organizationId` */\n activeOrganizationId: ContextId;\n /** ActiveContext `workspaceId` */\n activeWorkspaceId: ContextId;\n /** ActiveContext `projectId` */\n activeProjectId: ContextId;\n /**\n * The auth store's loaded `workspaces`. Used to verify that\n * `storeWorkspaceId` actually belongs to the organization being mirrored\n * before it is filled. If omitted, the workspace axis is never mirrored\n * (safe default — better no workspace than one from the wrong org).\n */\n storeWorkspaces?: ReadonlyArray<MirrorWorkspace>;\n /**\n * The auth store's loaded `projects`. Used to verify that `storeProjectId`\n * actually belongs to the workspace being mirrored before it is filled. If\n * omitted, the project axis is never mirrored.\n */\n storeProjects?: ReadonlyArray<MirrorProject>;\n /**\n * ActiveContext setters. Must be **stable references** (they are, coming\n * from `ActiveContextProvider`'s memoized value) — passing inline arrows\n * makes this effect re-run every render. Likewise prefer stable references\n * for `storeWorkspaces` / `storeProjects` (e.g. a shallow-equal Zustand\n * selector or `useMemo`); a new array reference each render re-runs the\n * effect. Re-runs are harmless (the fills are idempotent), just wasted work.\n */\n setOrganization: (organizationId: string | null) => void;\n setWorkspace: (workspaceId: string | null) => void;\n setProject: (projectId: string | null) => void;\n}\n\n/**\n * One-way, **fill-only** mirror of the auth store's org / workspace / project\n * ids into ActiveContext, for app-shell headers / context switchers.\n *\n * Some context ids are derived programmatically in the Zustand auth store\n * without a paired ActiveContext write:\n * - the bootstrap in `useContextSwitcherData` auto-selects an organization\n * (`setCurrentOrganizationId`) and, on the persisted-workspace path, a\n * workspace (`setCurrentWorkspaceId`) without calling `setActiveContext`;\n * - `useProjectSyncEffect` / `selectWorkspace` auto-select a project.\n *\n * ActiveContext is the source of truth for the URL + storage + the props passed\n * to workspace MFEs and the workspace-token context, so those store-only ids\n * must reach it — otherwise e.g. a fresh `defaultWorkspaceId` login leaves\n * `organizationId` undefined and the token / MFE context break until the user\n * reselects.\n *\n * The rule is **fill, never override**: copy a store id into ActiveContext only\n * when ActiveContext has no value for that axis yet. We must NOT overwrite a\n * non-null ActiveContext value, because:\n * 1. it would clobber an explicit selection — a switch handler's write, or a\n * deep-link / browser-back `?workspace=`/`?project=` that the router already\n * applied; and\n * 2. overriding is exactly what turns this into a two-store ping-pong — the\n * store→AC and AC→store effects each read the other's pre-commit value and\n * swap the ids every commit until React throws \"Maximum update depth\n * exceeded\" (error #185). Filling only nulls is self-terminating: once the\n * axis is set the guard is false forever, so it can never alternate.\n *\n * Cross-context safety: a value is filled only when it provably belongs to the\n * parent context being mirrored, validated against the loaded store metadata —\n * the **workspace** must belong to the target organization (`storeWorkspaces`),\n * and the **project** must belong to the target workspace (`storeProjects`).\n * The store can transiently hold a stale child id after its parent advanced\n * (e.g. `useContextSwitcherData` copies a new org into the store before the new\n * org's — unpersisted — workspace list loads, leaving `currentWorkspaceId`\n * pointing at the previous org; or the workspace advances to B while the\n * project still points at A). Gating on loaded metadata means such a stale id\n * isn't in the list yet, so it is not mirrored until it is genuinely valid.\n *\n * See BOFF-2805.\n */\nexport function useActiveContextBootstrapMirror({\n storeOrganizationId,\n storeWorkspaceId,\n storeProjectId,\n activeOrganizationId,\n activeWorkspaceId,\n activeProjectId,\n storeWorkspaces,\n storeProjects,\n setOrganization,\n setWorkspace,\n setProject,\n}: ActiveContextBootstrapMirrorArgs): void {\n useEffect(() => {\n if (storeOrganizationId && !activeOrganizationId) {\n setOrganization(storeOrganizationId);\n }\n\n // Fill the workspace only when it provably belongs to the organization\n // ActiveContext is on (or is being filled this pass). Guards against\n // publishing a (org, workspace) pair from two different orgs while the\n // store's org has advanced but its (unpersisted) workspace list / current\n // workspace id has not caught up.\n const targetOrganizationId = activeOrganizationId || storeOrganizationId;\n const workspaceBelongsToTargetOrganization =\n !!storeWorkspaceId &&\n !!targetOrganizationId &&\n !!storeWorkspaces?.some(\n (workspace) =>\n workspace.id === storeWorkspaceId &&\n workspace.organizationId === targetOrganizationId,\n );\n\n if (\n storeWorkspaceId &&\n !activeWorkspaceId &&\n workspaceBelongsToTargetOrganization\n ) {\n setWorkspace(storeWorkspaceId);\n }\n\n // Fill the project only when it provably belongs to the workspace\n // ActiveContext is on (or is being filled this pass). Prevents a stale\n // project from a previously-selected workspace leaking across a switch.\n // Fall back to the store workspace only when it is being filled this pass;\n // otherwise (e.g. workspace fill skipped because its metadata hasn't\n // loaded) we'd let a project fill without its parent workspace present,\n // an inconsistent (org, null, project) state.\n const targetWorkspaceId =\n activeWorkspaceId ||\n (workspaceBelongsToTargetOrganization ? storeWorkspaceId : null);\n const projectBelongsToTargetWorkspace =\n !!storeProjectId &&\n !!targetWorkspaceId &&\n !!storeProjects?.some(\n (project) =>\n project.id === storeProjectId &&\n project.workspaceId === targetWorkspaceId,\n );\n\n if (storeProjectId && !activeProjectId && projectBelongsToTargetWorkspace) {\n setProject(storeProjectId);\n }\n }, [\n storeOrganizationId,\n storeWorkspaceId,\n storeProjectId,\n activeOrganizationId,\n activeWorkspaceId,\n activeProjectId,\n storeWorkspaces,\n storeProjects,\n setOrganization,\n setWorkspace,\n setProject,\n ]);\n}\n"],"mappings":";;AA+FA,SAAgB,EAAgC,EAC9C,wBACA,qBACA,mBACA,yBACA,sBACA,oBACA,oBACA,kBACA,oBACA,iBACA,iBACyC;AACzC,SAAgB;AACd,EAAI,KAAuB,CAAC,KAC1B,EAAgB,EAAoB;EAQtC,IAAM,IAAuB,KAAwB,GAC/C,IACJ,CAAC,CAAC,KACF,CAAC,CAAC,KACF,CAAC,CAAC,GAAiB,MAChB,MACC,EAAU,OAAO,KACjB,EAAU,mBAAmB,EAChC;AAEH,EACE,KACA,CAAC,KACD,KAEA,EAAa,EAAiB;EAUhC,IAAM,IACJ,MACC,IAAuC,IAAmB,OACvD,IACJ,CAAC,CAAC,KACF,CAAC,CAAC,KACF,CAAC,CAAC,GAAe,MACd,MACC,EAAQ,OAAO,KACf,EAAQ,gBAAgB,EAC3B;AAEH,EAAI,KAAkB,CAAC,KAAmB,KACxC,EAAW,EAAe;IAE3B;EACD;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACD,CAAC"}
@@ -10,6 +10,7 @@ import "./hooks/usePasswordOperations.js";
10
10
  import "./hooks/useSessionOperations.js";
11
11
  import "./hooks/useWorkspaceOperations.js";
12
12
  import "./hooks/useContextSwitcherData.js";
13
+ import "./hooks/useActiveContextBootstrapMirror.js";
13
14
  import "./hooks/useUserOperations.js";
14
15
  import "./hooks/index.js";
15
16
  import "./hooks/useWorkspaceToken.js";
@@ -6,24 +6,25 @@ import { useState as a } from "react";
6
6
  import { useI18n as o } from "@burdenoff/fe-libs/shared/providers/shell";
7
7
  import { Fragment as s, jsx as c, jsxs as l } from "react/jsx-runtime";
8
8
  import { useFeatureFlagDetails as u } from "@burdenoff/fe-libs/shared/feature-flags";
9
- import { useGatewayClient as d } from "@burdenoff/fe-libs/shared/graphql";
9
+ import { EmphasisPanel as d, IllustratedEmptyState as f, PagePurpose as p } from "@burdenoff/fe-libs/ui";
10
+ import { useGatewayClient as m } from "@burdenoff/fe-libs/shared/graphql";
10
11
  //#region src/auth/pages/AuditLogPage.tsx
11
- var f = 20;
12
- function p() {
13
- let { t: p } = o(), m = (e, t) => {
14
- let n = p(e);
12
+ var h = 20;
13
+ function g() {
14
+ let { t: g } = o(), _ = (e, t) => {
15
+ let n = g(e);
15
16
  return n === e ? t : n;
16
- }, [h, g] = a(1), _ = (h - 1) * f, v = u("auth.audit-log")?.value === !0, { data: y, loading: b, error: x } = e({
17
- skip: !v,
17
+ }, [v, y] = a(1), b = (v - 1) * h, x = u("auth.audit-log")?.value === !0, { data: S, loading: C, error: w } = e({
18
+ skip: !x,
18
19
  fetchPolicy: "cache-and-network",
19
20
  variables: { pagination: {
20
- limit: f,
21
- offset: _
21
+ limit: h,
22
+ offset: b
22
23
  } }
23
- }), S = y?.authAuditLogs ?? [], C = S.length === f, [w, { loading: T }] = i({ client: d("workspace") }), [E, D] = a(null), O = async () => {
24
- D(null);
24
+ }), T = S?.authAuditLogs ?? [], E = T.length === h, [D, { loading: O }] = i({ client: m("workspace") }), [k, A] = a(null), j = async () => {
25
+ A(null);
25
26
  try {
26
- await w({ variables: { input: {
27
+ await D({ variables: { input: {
27
28
  name: "Audit Log Export",
28
29
  entityTypes: ["permission_audit_logs"],
29
30
  format: "CSV",
@@ -31,9 +32,9 @@ function p() {
31
32
  includeMetadata: !0
32
33
  } } });
33
34
  } catch (e) {
34
- D(e instanceof Error ? e.message : "Failed to start export");
35
+ A(e instanceof Error ? e.message : "Failed to start export");
35
36
  }
36
- }, k = (e) => {
37
+ }, M = (e) => {
37
38
  switch (e) {
38
39
  case "LOGIN_SUCCESS": return "OK";
39
40
  case "LOGIN_FAILURE": return "ER";
@@ -45,24 +46,24 @@ function p() {
45
46
  default: return "AU";
46
47
  }
47
48
  };
48
- return v ? /* @__PURE__ */ l("div", {
49
+ return x ? /* @__PURE__ */ l("div", {
49
50
  className: "max-w-4xl mx-auto p-6",
50
51
  children: [
51
52
  /* @__PURE__ */ l("div", {
52
53
  className: "flex items-center justify-between mb-8",
53
54
  children: [/* @__PURE__ */ c("h1", {
54
55
  className: "text-3xl font-semibold text-text-primary",
55
- children: m("auth.auditLog.title", "Security Audit Log")
56
+ children: _("auth.auditLog.title", "Security Audit Log")
56
57
  }), /* @__PURE__ */ c("button", {
57
58
  type: "button",
58
- onClick: () => void O(),
59
- disabled: T,
60
- "aria-label": m("auth.auditLog.exportAriaLabel", "Export audit log"),
59
+ onClick: () => void j(),
60
+ disabled: O,
61
+ "aria-label": _("auth.auditLog.exportAriaLabel", "Export audit log"),
61
62
  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",
62
- children: T ? /* @__PURE__ */ l(s, { children: [/* @__PURE__ */ c(n, {
63
+ children: O ? /* @__PURE__ */ l(s, { children: [/* @__PURE__ */ c(n, {
63
64
  className: "size-4 animate-spin",
64
65
  "aria-hidden": "true"
65
- }), m("auth.auditLog.exporting", "Exporting...")] }) : /* @__PURE__ */ l(s, { children: [/* @__PURE__ */ c("svg", {
66
+ }), _("auth.auditLog.exporting", "Exporting...")] }) : /* @__PURE__ */ l(s, { children: [/* @__PURE__ */ c("svg", {
66
67
  className: "size-4",
67
68
  fill: "none",
68
69
  viewBox: "0 0 24 24",
@@ -74,10 +75,24 @@ function p() {
74
75
  strokeWidth: 2,
75
76
  d: "M4 16v1a3 3 0 003 3h10a3 3 0 003-3v-1m-4-4l-4 4m0 0l-4-4m4 4V4"
76
77
  })
77
- }), m("auth.auditLog.export", "Export")] })
78
+ }), _("auth.auditLog.export", "Export")] })
78
79
  })]
79
80
  }),
80
- E && /* @__PURE__ */ l("div", {
81
+ /* @__PURE__ */ c(p, {
82
+ className: "mb-6",
83
+ children: _("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.")
84
+ }),
85
+ /* @__PURE__ */ l(d, {
86
+ className: "mb-6",
87
+ children: [/* @__PURE__ */ c("p", {
88
+ className: "text-sm font-medium text-text-primary",
89
+ children: _("auth.auditLog.emphasisTitle", "What this log captures")
90
+ }), /* @__PURE__ */ c("p", {
91
+ className: "mt-1 text-sm text-text-secondary",
92
+ children: _("auth.auditLog.emphasisBody", "Successful and failed sign-ins, logouts, password and two-factor changes, and detected anomalies — newest first.")
93
+ })]
94
+ }),
95
+ k && /* @__PURE__ */ l("div", {
81
96
  role: "alert",
82
97
  className: "mb-6 flex items-start gap-3 p-4 rounded-xl bg-status-error-bg-subtle border border-status-error-border",
83
98
  children: [/* @__PURE__ */ c(t, {
@@ -85,10 +100,10 @@ function p() {
85
100
  "aria-hidden": "true"
86
101
  }), /* @__PURE__ */ c("p", {
87
102
  className: "text-sm text-status-error-text",
88
- children: E
103
+ children: k
89
104
  })]
90
105
  }),
91
- b ? /* @__PURE__ */ l("div", {
106
+ C ? /* @__PURE__ */ l("div", {
92
107
  "aria-live": "polite",
93
108
  "aria-busy": "true",
94
109
  className: "text-center py-12",
@@ -97,26 +112,27 @@ function p() {
97
112
  "aria-hidden": "true"
98
113
  }), /* @__PURE__ */ c("p", {
99
114
  className: "mt-3 text-text-secondary",
100
- children: m("auth.auditLog.loading", "Loading audit events...")
115
+ children: _("auth.auditLog.loading", "Loading audit events...")
101
116
  })]
102
- }) : x ? /* @__PURE__ */ c("div", {
117
+ }) : w ? /* @__PURE__ */ c("div", {
103
118
  role: "alert",
104
- className: "text-center py-12 border border-status-error-border rounded-xl bg-status-error-bg-subtle",
105
- children: /* @__PURE__ */ c("p", {
106
- className: "text-status-error-text",
107
- children: m("auth.auditLog.error", "Unable to load audit events")
119
+ children: /* @__PURE__ */ c(f, {
120
+ illustration: "empty-generic",
121
+ title: _("auth.auditLog.error", "Unable to load audit events"),
122
+ description: _("auth.auditLog.errorDesc", "Something went wrong fetching your security events. Please try again in a moment.")
108
123
  })
109
- }) : S.length === 0 ? /* @__PURE__ */ c("output", {
110
- className: "block text-center py-12",
111
- children: /* @__PURE__ */ c("p", {
112
- className: "text-text-secondary",
113
- children: m("auth.auditLog.noEvents", "No security events recorded")
124
+ }) : T.length === 0 ? /* @__PURE__ */ c("output", {
125
+ className: "block",
126
+ children: /* @__PURE__ */ c(f, {
127
+ illustration: "empty-notifications",
128
+ title: _("auth.auditLog.noEvents", "No security events recorded"),
129
+ description: _("auth.auditLog.noEventsDesc", "As you sign in and change security settings, those events will appear here.")
114
130
  })
115
131
  }) : /* @__PURE__ */ l(s, { children: [/* @__PURE__ */ c("div", {
116
132
  className: "space-y-4",
117
133
  "aria-live": "polite",
118
- "aria-busy": b,
119
- children: S.map((e) => /* @__PURE__ */ c("div", {
134
+ "aria-busy": C,
135
+ children: T.map((e) => /* @__PURE__ */ c("div", {
120
136
  className: "p-6 rounded-xl border border-border-subtle bg-bg-surface shadow-sm",
121
137
  children: /* @__PURE__ */ l("div", {
122
138
  className: "flex items-start gap-4",
@@ -124,7 +140,7 @@ function p() {
124
140
  /* @__PURE__ */ c("div", {
125
141
  className: "flex size-10 shrink-0 items-center justify-center rounded-full bg-bg-sunken text-xs font-semibold text-text-secondary",
126
142
  "aria-hidden": "true",
127
- children: k(e.action)
143
+ children: M(e.action)
128
144
  }),
129
145
  /* @__PURE__ */ l("div", {
130
146
  className: "flex-1",
@@ -142,72 +158,57 @@ function p() {
142
158
  }),
143
159
  /* @__PURE__ */ l("p", {
144
160
  className: "text-sm text-text-secondary",
145
- children: [e.description ?? m("auth.auditLog.defaultDescription", "Authentication event"), e.ipAddress && ` • ${e.ipAddress}`]
161
+ children: [e.description ?? _("auth.auditLog.defaultDescription", "Authentication event"), e.ipAddress && ` • ${e.ipAddress}`]
146
162
  })
147
163
  ]
148
164
  }),
149
165
  e.action.includes("FAILURE") && /* @__PURE__ */ c("output", {
150
166
  className: "px-3 py-1 rounded-full bg-status-error-bg-subtle text-status-error-text text-sm font-medium",
151
- children: m("auth.auditLog.failed", "Failed")
167
+ children: _("auth.auditLog.failed", "Failed")
152
168
  })
153
169
  ]
154
170
  })
155
171
  }, e.id))
156
172
  }), /* @__PURE__ */ l("nav", {
157
- "aria-label": m("auth.auditLog.paginationAriaLabel", "Audit log pagination"),
173
+ "aria-label": _("auth.auditLog.paginationAriaLabel", "Audit log pagination"),
158
174
  className: "mt-6 flex items-center justify-between",
159
175
  children: [/* @__PURE__ */ l("p", {
160
176
  className: "text-sm text-text-secondary",
161
177
  children: [
162
- m("auth.auditLog.page", "Page"),
178
+ _("auth.auditLog.page", "Page"),
163
179
  " ",
164
- h
180
+ v
165
181
  ]
166
182
  }), /* @__PURE__ */ l("div", {
167
183
  className: "flex gap-2",
168
184
  children: [/* @__PURE__ */ c("button", {
169
185
  type: "button",
170
- onClick: () => g((e) => Math.max(1, e - 1)),
171
- disabled: h === 1,
172
- "aria-label": m("auth.auditLog.previousPageAriaLabel", "Go to previous page"),
186
+ onClick: () => y((e) => Math.max(1, e - 1)),
187
+ disabled: v === 1,
188
+ "aria-label": _("auth.auditLog.previousPageAriaLabel", "Go to previous page"),
173
189
  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",
174
- children: m("auth.auditLog.previous", "Previous")
190
+ children: _("auth.auditLog.previous", "Previous")
175
191
  }), /* @__PURE__ */ c("button", {
176
192
  type: "button",
177
- onClick: () => g((e) => e + 1),
178
- disabled: !C,
179
- "aria-label": m("auth.auditLog.nextPageAriaLabel", "Go to next page"),
193
+ onClick: () => y((e) => e + 1),
194
+ disabled: !E,
195
+ "aria-label": _("auth.auditLog.nextPageAriaLabel", "Go to next page"),
180
196
  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",
181
- children: m("auth.auditLog.next", "Next")
197
+ children: _("auth.auditLog.next", "Next")
182
198
  })]
183
199
  })]
184
200
  })] })
185
201
  ]
186
202
  }) : /* @__PURE__ */ c("div", {
187
203
  className: "max-w-4xl mx-auto p-6",
188
- children: /* @__PURE__ */ l("div", {
189
- className: "text-center py-16 border-2 border-dashed border-border-subtle rounded-xl",
190
- children: [
191
- /* @__PURE__ */ c("div", {
192
- className: "size-16 mx-auto mb-4 rounded-full bg-status-warning-bg-subtle flex items-center justify-center",
193
- children: /* @__PURE__ */ c(t, {
194
- className: "size-8 text-status-warning-text",
195
- "aria-hidden": "true"
196
- })
197
- }),
198
- /* @__PURE__ */ c("h2", {
199
- className: "text-xl font-semibold text-text-primary mb-2",
200
- children: m("auth.auditLog.notAvailable", "Page Not Available")
201
- }),
202
- /* @__PURE__ */ c("p", {
203
- className: "text-text-secondary",
204
- children: m("auth.auditLog.notEnabled", "The Security Audit Log is not enabled for your account.")
205
- })
206
- ]
204
+ children: /* @__PURE__ */ c(f, {
205
+ illustration: "empty-generic",
206
+ title: _("auth.auditLog.notAvailable", "Page Not Available"),
207
+ description: _("auth.auditLog.notEnabled", "The Security Audit Log is not enabled for your account.")
207
208
  })
208
209
  });
209
210
  }
210
211
  //#endregion
211
- export { p as AuditLogPage };
212
+ export { g as AuditLogPage };
212
213
 
213
214
  //# sourceMappingURL=AuditLogPage.js.map
@@ -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 { 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 {\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\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 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 <div className=\"text-center py-16 border-2 border-dashed border-border-subtle rounded-xl\">\n <div className=\"size-16 mx-auto mb-4 rounded-full bg-status-warning-bg-subtle flex items-center justify-center\">\n <AlertIcon\n className=\"size-8 text-status-warning-text\"\n aria-hidden=\"true\"\n />\n </div>\n <h2 className=\"text-xl font-semibold text-text-primary mb-2\">\n {tr(\"auth.auditLog.notAvailable\", \"Page Not Available\")}\n </h2>\n <p className=\"text-text-secondary\">\n {tr(\n \"auth.auditLog.notEnabled\",\n \"The Security Audit Log is not enabled for your account.\",\n )}\n </p>\n </div>\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 {/* 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 {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\n role=\"alert\"\n className=\"text-center py-12 border border-status-error-border rounded-xl bg-status-error-bg-subtle\"\n >\n <p className=\"text-status-error-text\">\n {tr(\"auth.auditLog.error\", \"Unable to load audit events\")}\n </p>\n </div>\n ) : auditLogs.length === 0 ? (\n <output className=\"block text-center py-12\">\n <p className=\"text-text-secondary\">\n {tr(\"auth.auditLog.noEvents\", \"No security events recorded\")}\n </p>\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 { useI18n } from \"@burdenoff/fe-libs/shared/providers/shell\";\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 {\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\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 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 {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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"}
@@ -6,58 +6,59 @@ import { useCallback as i, useEffect as a, useRef as o, useState as s } from "re
6
6
  import { useSearchParams as c } from "react-router-dom";
7
7
  import { useI18n as l } from "@burdenoff/fe-libs/shared/providers/shell";
8
8
  import { Fragment as u, jsx as d, jsxs as f } from "react/jsx-runtime";
9
+ import { Button as p } from "@burdenoff/fe-libs/button";
9
10
  //#region src/auth/pages/EmailVerifyPage.tsx
10
- var p = 5;
11
- function m(e) {
11
+ var m = 5;
12
+ function h(e) {
12
13
  let t = "/auth/login", n = e ? `${t}?email=${encodeURIComponent(e)}` : t;
13
14
  window.location.href = n;
14
15
  }
15
- function h() {
16
- let { t: h } = l(), g = (e, t) => {
17
- let n = h(e);
16
+ function g() {
17
+ let { t: g } = l(), _ = (e, t) => {
18
+ let n = g(e);
18
19
  return n === e ? t : n;
19
- }, [_] = c(), v = _.get("token"), y = _.get("email"), { verifyEmail: b, resendVerification: x } = t(), S = e((e) => e.user), [C, w] = s("verifying"), T = o(y || void 0), [E, D] = s(!1), [O, k] = s(!1), [A, j] = s(p), M = o(!1);
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);
20
21
  a(() => {
21
- if (!v || M.current) return;
22
- let e = `auth:verifyEmail:inflight:${v}`;
22
+ if (!y || N.current) return;
23
+ let e = `auth:verifyEmail:inflight:${y}`;
23
24
  if (!(typeof window < "u" && window.sessionStorage.getItem(e))) {
24
- M.current = !0;
25
+ N.current = !0;
25
26
  try {
26
27
  window.sessionStorage.setItem(e, "1");
27
28
  } catch {}
28
- b(v).then((e) => {
29
- e.success ? (w("success"), e.email && (T.current = e.email)) : w("error");
30
- }).catch(() => w("error"));
29
+ x(y).then((e) => {
30
+ e.success ? (T("success"), e.email && (E.current = e.email)) : T("error");
31
+ }).catch(() => T("error"));
31
32
  }
32
- }, [v, b]), a(() => {
33
- if (C !== "success") return;
33
+ }, [y, x]), a(() => {
34
+ if (w !== "success") return;
34
35
  let e = setInterval(() => {
35
- j((t) => t <= 1 ? (clearInterval(e), m(T.current), 0) : t - 1);
36
+ M((t) => t <= 1 ? (clearInterval(e), h(E.current), 0) : t - 1);
36
37
  }, 1e3);
37
38
  return () => clearInterval(e);
38
- }, [C]);
39
- let N = async () => {
40
- let e = y || S?.email;
39
+ }, [w]);
40
+ let P = async () => {
41
+ let e = b || C?.email;
41
42
  if (!e) {
42
43
  console.error("No email available for resend");
43
44
  return;
44
45
  }
45
- D(!0);
46
+ O(!0);
46
47
  try {
47
- await x(e), k(!0);
48
+ await S(e), A(!0);
48
49
  } catch (e) {
49
50
  console.error("Failed to resend verification:", e);
50
51
  } finally {
51
- D(!1);
52
+ O(!1);
52
53
  }
53
- }, P = i(() => {
54
- m(T.current);
55
- }, []), F = i(() => {
56
- m();
54
+ }, F = i(() => {
55
+ h(E.current);
56
+ }, []), I = i(() => {
57
+ h();
57
58
  }, []);
58
- return C === "verifying" ? /* @__PURE__ */ d(n, {
59
- title: g("auth.emailVerify.verifyingTitle", "Verifying your email"),
60
- subtitle: g("auth.emailVerify.verifyingSubtitle", "Please wait while we confirm your email address"),
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"),
61
62
  children: /* @__PURE__ */ f("div", {
62
63
  className: "flex flex-col items-center justify-center py-12",
63
64
  children: [/* @__PURE__ */ d("div", {
@@ -65,12 +66,12 @@ function h() {
65
66
  children: /* @__PURE__ */ d(r, { className: "size-8 text-[var(--color-text-link)] animate-spin" })
66
67
  }), /* @__PURE__ */ d("p", {
67
68
  className: "text-[var(--color-text-secondary)]",
68
- children: g("auth.emailVerify.moment", "This will only take a moment...")
69
+ children: _("auth.emailVerify.moment", "This will only take a moment...")
69
70
  })]
70
71
  })
71
- }) : C === "success" ? /* @__PURE__ */ d(n, {
72
- title: g("auth.emailVerify.verifiedTitle", "Email verified!"),
73
- subtitle: g("auth.emailVerify.verifiedSubtitle", "Your email address has been confirmed"),
72
+ }) : w === "success" ? /* @__PURE__ */ d(n, {
73
+ title: _("auth.emailVerify.verifiedTitle", "Email verified!"),
74
+ subtitle: _("auth.emailVerify.verifiedSubtitle", "Your email address has been confirmed"),
74
75
  children: /* @__PURE__ */ f("div", {
75
76
  className: "flex flex-col items-center justify-center py-6",
76
77
  children: [
@@ -91,32 +92,34 @@ function h() {
91
92
  }),
92
93
  /* @__PURE__ */ d("p", {
93
94
  className: "text-center text-[var(--color-text-secondary)] mb-6",
94
- children: g("auth.emailVerify.successMessage", "Your email has been successfully verified. You can now sign in to access all features.")
95
+ children: _("auth.emailVerify.successMessage", "Your email has been successfully verified. You can now sign in to access all features.")
95
96
  }),
96
97
  /* @__PURE__ */ f("p", {
97
98
  className: "text-center text-sm text-[var(--color-text-tertiary)] mb-6",
98
99
  children: [
99
- g("auth.emailVerify.redirecting", "Redirecting to sign in in"),
100
+ _("auth.emailVerify.redirecting", "Redirecting to sign in in"),
100
101
  " ",
101
102
  /* @__PURE__ */ d("span", {
102
103
  className: "font-semibold text-[var(--color-text-primary)]",
103
- children: A
104
+ children: j
104
105
  }),
105
106
  " ",
106
- g("auth.emailVerify.seconds", "seconds...")
107
+ _("auth.emailVerify.seconds", "seconds...")
107
108
  ]
108
109
  }),
109
- /* @__PURE__ */ d("button", {
110
+ /* @__PURE__ */ d(p, {
110
111
  type: "button",
111
- onClick: P,
112
- className: "auth-primary-button w-full px-4 py-3 font-semibold",
113
- children: g("auth.emailVerify.continueSignIn", "Continue to sign in now")
112
+ onClick: F,
113
+ variant: "default",
114
+ size: "lg",
115
+ className: "w-full font-semibold",
116
+ children: _("auth.emailVerify.continueSignIn", "Continue to sign in now")
114
117
  })
115
118
  ]
116
119
  })
117
120
  }) : /* @__PURE__ */ d(n, {
118
- title: g("auth.emailVerify.failedTitle", "Verification failed"),
119
- subtitle: g("auth.emailVerify.failedSubtitle", "We couldn't verify your email address"),
121
+ title: _("auth.emailVerify.failedTitle", "Verification failed"),
122
+ subtitle: _("auth.emailVerify.failedSubtitle", "We couldn't verify your email address"),
120
123
  children: /* @__PURE__ */ f("div", {
121
124
  className: "flex flex-col items-center justify-center py-6",
122
125
  children: [
@@ -137,32 +140,34 @@ function h() {
137
140
  }),
138
141
  /* @__PURE__ */ d("p", {
139
142
  className: "text-center text-[var(--color-text-secondary)] mb-8",
140
- children: g("auth.emailVerify.invalidLink", "The verification link is invalid or has expired. Please request a new verification email.")
143
+ children: _("auth.emailVerify.invalidLink", "The verification link is invalid or has expired. Please request a new verification email.")
141
144
  }),
142
- O ? /* @__PURE__ */ d("div", {
145
+ k ? /* @__PURE__ */ d("div", {
143
146
  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",
144
147
  children: /* @__PURE__ */ d("p", {
145
148
  className: "text-sm text-[var(--color-status-success-text)]",
146
- children: g("auth.emailVerify.emailSent", "Verification email sent! Please check your inbox.")
149
+ children: _("auth.emailVerify.emailSent", "Verification email sent! Please check your inbox.")
147
150
  })
148
- }) : /* @__PURE__ */ d("button", {
151
+ }) : /* @__PURE__ */ d(p, {
149
152
  type: "button",
150
- onClick: N,
151
- disabled: E,
152
- className: "auth-primary-button mb-4 flex w-full items-center justify-center gap-2 px-4 py-3 font-semibold",
153
- children: E ? /* @__PURE__ */ f(u, { children: [/* @__PURE__ */ d(r, { className: "size-5 animate-spin" }), /* @__PURE__ */ d("span", { children: "Sending…" })] }) : g("auth.emailVerify.resend", "Resend verification email")
153
+ onClick: P,
154
+ disabled: D,
155
+ variant: "default",
156
+ size: "lg",
157
+ 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")
154
159
  }),
155
160
  /* @__PURE__ */ d("button", {
156
161
  type: "button",
157
- onClick: F,
162
+ onClick: I,
158
163
  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",
159
- children: g("auth.emailVerify.backToSignIn", "Back to sign in")
164
+ children: _("auth.emailVerify.backToSignIn", "Back to sign in")
160
165
  })
161
166
  ]
162
167
  })
163
168
  });
164
169
  }
165
170
  //#endregion
166
- export { h as EmailVerifyPage };
171
+ export { g as EmailVerifyPage };
167
172
 
168
173
  //# sourceMappingURL=EmailVerifyPage.js.map