@burdenoff/microfe-auth 2026.529.2 → 2026.529.3

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.
@@ -9,11 +9,7 @@ import { useNavigate as l, useParams as u } from "react-router-dom";
9
9
  import { useI18n as d } from "@burdenoff/fe-libs/shared/providers/shell";
10
10
  import { Fragment as f, jsx as p, jsxs as m } from "react/jsx-runtime";
11
11
  //#region src/auth/pages/UserDetailsPage.tsx
12
- var h = [
13
- "SUPER_ADMIN",
14
- "TENANT_ADMIN",
15
- "ORG_ADMIN"
16
- ], g = (e) => {
12
+ var h = (e) => {
17
13
  switch (e) {
18
14
  case "SUPER_ADMIN": return "bg-accent-purple-subtle text-accent-purple";
19
15
  case "TENANT_ADMIN": return "bg-status-info-bg-subtle text-status-info-text";
@@ -23,7 +19,7 @@ var h = [
23
19
  case "GUEST": return "bg-bg-sunken text-text-primary";
24
20
  default: return "bg-bg-sunken text-text-primary";
25
21
  }
26
- }, _ = (e) => {
22
+ }, g = (e) => {
27
23
  switch (e) {
28
24
  case "ACTIVE": return "bg-status-success-bg-subtle text-status-success-text";
29
25
  case "INACTIVE": return "bg-status-warning-bg-subtle text-status-warning-text";
@@ -32,59 +28,59 @@ var h = [
32
28
  default: return "bg-bg-sunken text-text-primary";
33
29
  }
34
30
  };
35
- function v() {
36
- let { id: v } = u(), y = l(), { t: b } = d(), x = (e, t) => {
37
- let n = b(e);
31
+ function _() {
32
+ let { id: _ } = u(), v = l(), { t: y } = d(), b = (e, t) => {
33
+ let n = y(e);
38
34
  return n === e ? t : n;
39
- }, S = e((e) => e.isAuthenticated), C = e((e) => e.user), { canViewUsers: w, canDeleteUsers: T, isLoading: E } = i(), { getUser: D, deleteUser: O, hardDeleteUser: k } = r(), [A, j] = c(null), [M, N] = c(!0), [P, F] = c(null), [I, L] = c(null), [R, z] = c(null), [B, { loading: V }] = a(), [H, U] = c(null);
35
+ }, x = e((e) => e.isAuthenticated), S = e((e) => e.user), { canViewUsers: C, canDeleteUsers: w, isLoading: T } = i(), { getUser: E, deleteUser: D, hardDeleteUser: O } = r(), [k, A] = c(null), [j, M] = c(!0), [N, P] = c(null), [F, I] = c(null), [L, R] = c(null), [z, { loading: B }] = a(), [V, H] = c(null);
40
36
  s(() => {
41
37
  let e = !1;
42
- if (!v) {
43
- F("Missing user id"), N(!1);
38
+ if (!_) {
39
+ P("Missing user id"), M(!1);
44
40
  return;
45
41
  }
46
- if (!S) {
47
- N(!1);
42
+ if (!x) {
43
+ M(!1);
48
44
  return;
49
45
  }
50
- return N(!0), F(null), D(v).then((t) => {
51
- e || (t ? j(t) : F(x("auth.userDetails.userNotFound", "User not found")));
46
+ return M(!0), P(null), E(_).then((t) => {
47
+ e || (t ? A(t) : P(b("auth.userDetails.userNotFound", "User not found")));
52
48
  }).catch((t) => {
53
- e || F(t instanceof Error ? t.message : "Failed to fetch user details");
49
+ e || P(t instanceof Error ? t.message : "Failed to fetch user details");
54
50
  }).finally(() => {
55
- e || N(!1);
51
+ e || M(!1);
56
52
  }), () => {
57
53
  e = !0;
58
54
  };
59
55
  }, [
60
- v,
61
- D,
62
- S
56
+ _,
57
+ E,
58
+ x
63
59
  ]);
64
- let W = async () => {
65
- if (A) try {
66
- let e = await O(A.id);
60
+ let U = async () => {
61
+ if (k) try {
62
+ let e = await D(k.id);
67
63
  if (e.success) {
68
- L(x("auth.userDetails.userDeactivated", "User \"{email}\" has been deactivated.").replace("{email}", A.email)), z(null);
69
- let e = await D(A.id);
70
- e && j(e);
71
- } else F(e.message);
64
+ I(b("auth.userDetails.userDeactivated", "User \"{email}\" has been deactivated.").replace("{email}", k.email)), R(null);
65
+ let e = await E(k.id);
66
+ e && A(e);
67
+ } else P(e.message);
72
68
  } catch (e) {
73
- F(e instanceof Error ? e.message : "Failed to delete user");
69
+ P(e instanceof Error ? e.message : "Failed to delete user");
74
70
  }
75
- }, G = async () => {
76
- if (A) try {
77
- let e = await k(A.id);
78
- e.success ? (z(null), y("/auth/users")) : F(e.message);
71
+ }, W = async () => {
72
+ if (k) try {
73
+ let e = await O(k.id);
74
+ e.success ? (R(null), v("/auth/users")) : P(e.message);
79
75
  } catch (e) {
80
- F(e instanceof Error ? e.message : "Failed to hard delete user");
76
+ P(e instanceof Error ? e.message : "Failed to hard delete user");
81
77
  }
82
- }, K = async () => {
83
- if (A) {
84
- U(null);
78
+ }, G = async () => {
79
+ if (k) {
80
+ H(null);
85
81
  try {
86
- await B({ variables: { input: {
87
- name: `User Data Export - ${A.email}`,
82
+ await z({ variables: { input: {
83
+ name: `User Data Export - ${k.email}`,
88
84
  entityTypes: [
89
85
  "user_profile",
90
86
  "user_sessions",
@@ -95,23 +91,11 @@ function v() {
95
91
  includeMetadata: !0
96
92
  } } });
97
93
  } catch (e) {
98
- U(e instanceof Error ? e.message : "Failed to start export");
94
+ H(e instanceof Error ? e.message : "Failed to start export");
99
95
  }
100
96
  }
101
97
  };
102
- return S ? C && !h.includes(C.role) ? /* @__PURE__ */ p("div", {
103
- className: "max-w-4xl mx-auto p-6",
104
- children: /* @__PURE__ */ m("div", {
105
- className: "text-center py-16 border-2 border-dashed border-border-subtle rounded-xl",
106
- children: [/* @__PURE__ */ p("h2", {
107
- className: "text-xl font-semibold text-text-primary mb-2",
108
- children: x("auth.userDetails.notAllowed", "You are not allowed to access this page")
109
- }), /* @__PURE__ */ p("p", {
110
- className: "text-text-secondary",
111
- children: x("auth.userDetails.notAllowedDesc", "This page is restricted to administrators only.")
112
- })]
113
- })
114
- }) : E || M ? /* @__PURE__ */ p("div", {
98
+ return x ? T || j ? /* @__PURE__ */ p("div", {
115
99
  className: "max-w-4xl mx-auto p-6",
116
100
  children: /* @__PURE__ */ m("div", {
117
101
  className: "text-center py-16",
@@ -122,14 +106,14 @@ function v() {
122
106
  "aria-hidden": "true"
123
107
  }), /* @__PURE__ */ p("p", {
124
108
  className: "mt-4 text-text-secondary",
125
- children: x("auth.userDetails.loading", "Loading user...")
109
+ children: b("auth.userDetails.loading", "Loading user...")
126
110
  })]
127
111
  })
128
- }) : w ? P || !A ? /* @__PURE__ */ m("div", {
112
+ }) : C ? N || !k ? /* @__PURE__ */ m("div", {
129
113
  className: "max-w-4xl mx-auto p-6",
130
114
  children: [/* @__PURE__ */ m("button", {
131
115
  type: "button",
132
- onClick: () => y("/auth/users"),
116
+ onClick: () => v("/auth/users"),
133
117
  className: "mb-6 flex items-center gap-2 text-text-link hover:underline font-medium",
134
118
  children: [/* @__PURE__ */ p("svg", {
135
119
  className: "size-4",
@@ -143,7 +127,7 @@ function v() {
143
127
  strokeWidth: 2,
144
128
  d: "M10 19l-7-7m0 0l7-7m-7 7h18"
145
129
  })
146
- }), x("auth.userDetails.backToUsers", "Back to Users")]
130
+ }), b("auth.userDetails.backToUsers", "Back to Users")]
147
131
  }), /* @__PURE__ */ m("div", {
148
132
  role: "alert",
149
133
  className: "flex items-start gap-3 p-4 rounded-xl bg-status-error-bg-subtle border border-status-error-border",
@@ -152,30 +136,30 @@ function v() {
152
136
  "aria-hidden": "true"
153
137
  }), /* @__PURE__ */ p("p", {
154
138
  className: "text-sm text-status-error-text",
155
- children: P ?? x("auth.userDetails.userNotFound", "User not found")
139
+ children: N ?? b("auth.userDetails.userNotFound", "User not found")
156
140
  })]
157
141
  })]
158
142
  }) : /* @__PURE__ */ m("div", {
159
143
  className: "max-w-4xl mx-auto p-6",
160
144
  children: [
161
145
  /* @__PURE__ */ p(o, {
162
- open: R !== null,
146
+ open: L !== null,
163
147
  onOpenChange: (e) => {
164
- e || z(null);
148
+ e || R(null);
165
149
  },
166
- title: R === "hard-delete" ? x("auth.userDetails.hardDeleteTitle", "Permanently delete user?") : x("auth.userDetails.deactivateTitle", "Deactivate user?"),
167
- description: R === "hard-delete" ? x("auth.userDetails.hardDeleteDesc", "This permanently deletes \"{email}\" and cannot be undone.").replace("{email}", A.email) : x("auth.userDetails.deactivateDesc", "\"{email}\" will be prevented from signing in until reactivated.").replace("{email}", A.email),
168
- confirmText: R === "hard-delete" ? x("auth.userDetails.hardDeleteConfirm", "Delete permanently") : x("auth.userDetails.deactivateConfirm", "Deactivate user"),
169
- variant: R === "hard-delete" ? "danger" : "warning",
150
+ title: L === "hard-delete" ? b("auth.userDetails.hardDeleteTitle", "Permanently delete user?") : b("auth.userDetails.deactivateTitle", "Deactivate user?"),
151
+ description: L === "hard-delete" ? b("auth.userDetails.hardDeleteDesc", "This permanently deletes \"{email}\" and cannot be undone.").replace("{email}", k.email) : b("auth.userDetails.deactivateDesc", "\"{email}\" will be prevented from signing in until reactivated.").replace("{email}", k.email),
152
+ confirmText: L === "hard-delete" ? b("auth.userDetails.hardDeleteConfirm", "Delete permanently") : b("auth.userDetails.deactivateConfirm", "Deactivate user"),
153
+ variant: L === "hard-delete" ? "danger" : "warning",
170
154
  onConfirm: async () => {
171
- R === "hard-delete" ? await G() : R === "deactivate" && await W();
155
+ L === "hard-delete" ? await W() : L === "deactivate" && await U();
172
156
  }
173
157
  }),
174
158
  /* @__PURE__ */ m("div", {
175
159
  className: "flex items-center justify-between mb-6",
176
160
  children: [/* @__PURE__ */ m("button", {
177
161
  type: "button",
178
- onClick: () => y("/auth/users"),
162
+ onClick: () => v("/auth/users"),
179
163
  className: "flex items-center gap-2 text-text-link hover:underline font-medium",
180
164
  children: [/* @__PURE__ */ p("svg", {
181
165
  className: "size-4",
@@ -189,17 +173,17 @@ function v() {
189
173
  strokeWidth: 2,
190
174
  d: "M10 19l-7-7m0 0l7-7m-7 7h18"
191
175
  })
192
- }), x("auth.userDetails.backToUsers", "Back to Users")]
176
+ }), b("auth.userDetails.backToUsers", "Back to Users")]
193
177
  }), /* @__PURE__ */ p("button", {
194
178
  type: "button",
195
- onClick: () => void K(),
196
- disabled: V,
197
- "aria-label": x("auth.userDetails.exportAriaLabel", "Export user data"),
179
+ onClick: () => void G(),
180
+ disabled: B,
181
+ "aria-label": b("auth.userDetails.exportAriaLabel", "Export user data"),
198
182
  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",
199
- children: V ? /* @__PURE__ */ m(f, { children: [/* @__PURE__ */ p(n, {
183
+ children: B ? /* @__PURE__ */ m(f, { children: [/* @__PURE__ */ p(n, {
200
184
  className: "size-4 animate-spin",
201
185
  "aria-hidden": "true"
202
- }), x("auth.userDetails.exporting", "Exporting...")] }) : /* @__PURE__ */ m(f, { children: [/* @__PURE__ */ p("svg", {
186
+ }), b("auth.userDetails.exporting", "Exporting...")] }) : /* @__PURE__ */ m(f, { children: [/* @__PURE__ */ p("svg", {
203
187
  className: "size-4",
204
188
  fill: "none",
205
189
  viewBox: "0 0 24 24",
@@ -211,10 +195,10 @@ function v() {
211
195
  strokeWidth: 2,
212
196
  d: "M4 16v1a3 3 0 003 3h10a3 3 0 003-3v-1m-4-4l-4 4m0 0l-4-4m4 4V4"
213
197
  })
214
- }), x("auth.userDetails.exportButton", "Export User Data")] })
198
+ }), b("auth.userDetails.exportButton", "Export User Data")] })
215
199
  })]
216
200
  }),
217
- H && /* @__PURE__ */ m("div", {
201
+ V && /* @__PURE__ */ m("div", {
218
202
  role: "alert",
219
203
  className: "mb-6 flex items-start gap-3 p-4 rounded-xl bg-status-error-bg-subtle border border-status-error-border",
220
204
  children: [/* @__PURE__ */ p(t, {
@@ -222,19 +206,19 @@ function v() {
222
206
  "aria-hidden": "true"
223
207
  }), /* @__PURE__ */ p("p", {
224
208
  className: "text-sm text-status-error-text",
225
- children: H
209
+ children: V
226
210
  })]
227
211
  }),
228
- I && /* @__PURE__ */ m("div", {
212
+ F && /* @__PURE__ */ m("div", {
229
213
  className: "mb-6 p-4 rounded-xl bg-status-success-bg-subtle border border-status-success-border",
230
214
  children: [/* @__PURE__ */ p("p", {
231
215
  className: "text-status-success-text",
232
- children: I
216
+ children: F
233
217
  }), /* @__PURE__ */ p("button", {
234
218
  type: "button",
235
- onClick: () => L(null),
219
+ onClick: () => I(null),
236
220
  className: "mt-2 text-sm text-status-success-text hover:underline",
237
- children: x("auth.userDetails.dismiss", "Dismiss")
221
+ children: b("auth.userDetails.dismiss", "Dismiss")
238
222
  })]
239
223
  }),
240
224
  /* @__PURE__ */ m("div", {
@@ -245,25 +229,25 @@ function v() {
245
229
  children: [/* @__PURE__ */ p("div", {
246
230
  className: "auth-brand-badge flex size-20 items-center justify-center rounded-full text-3xl font-semibold",
247
231
  "aria-hidden": "true",
248
- children: (A.name || A.username || A.email).charAt(0).toUpperCase()
232
+ children: (k.name || k.username || k.email).charAt(0).toUpperCase()
249
233
  }), /* @__PURE__ */ m("div", { children: [
250
234
  /* @__PURE__ */ p("h1", {
251
235
  className: "text-2xl font-semibold text-text-primary",
252
- children: A.name || A.username
236
+ children: k.name || k.username
253
237
  }),
254
238
  /* @__PURE__ */ p("p", {
255
239
  className: "text-text-secondary",
256
240
  "data-testid": "user-email",
257
- children: A.email
241
+ children: k.email
258
242
  }),
259
243
  /* @__PURE__ */ m("div", {
260
244
  className: "flex items-center gap-2 mt-2",
261
245
  children: [/* @__PURE__ */ p("span", {
262
- className: `px-2.5 py-1 rounded-full text-xs font-medium ${g(A.role)}`,
263
- children: A.role.replace("_", " ")
246
+ className: `px-2.5 py-1 rounded-full text-xs font-medium ${h(k.role)}`,
247
+ children: k.role.replace("_", " ")
264
248
  }), /* @__PURE__ */ p("span", {
265
- className: `px-2.5 py-1 rounded-full text-xs font-medium ${_(A.status)}`,
266
- children: A.status
249
+ className: `px-2.5 py-1 rounded-full text-xs font-medium ${g(k.status)}`,
250
+ children: k.status
267
251
  })]
268
252
  })
269
253
  ] })]
@@ -275,35 +259,35 @@ function v() {
275
259
  className: "p-4 rounded-lg bg-bg-canvas/50",
276
260
  children: [/* @__PURE__ */ p("dt", {
277
261
  className: "text-sm font-medium text-text-secondary mb-1",
278
- children: x("auth.userDetails.username", "Username")
262
+ children: b("auth.userDetails.username", "Username")
279
263
  }), /* @__PURE__ */ p("dd", {
280
264
  className: "text-text-primary font-medium",
281
- children: A.username
265
+ children: k.username
282
266
  })]
283
267
  }),
284
268
  /* @__PURE__ */ m("div", {
285
269
  className: "p-4 rounded-lg bg-bg-canvas/50",
286
270
  children: [/* @__PURE__ */ p("dt", {
287
271
  className: "text-sm font-medium text-text-secondary mb-1",
288
- children: x("auth.userDetails.email", "Email")
272
+ children: b("auth.userDetails.email", "Email")
289
273
  }), /* @__PURE__ */ p("dd", {
290
274
  className: "text-text-primary font-medium",
291
- children: A.email
275
+ children: k.email
292
276
  })]
293
277
  }),
294
278
  /* @__PURE__ */ m("div", {
295
279
  className: "p-4 rounded-lg bg-bg-canvas/50",
296
280
  children: [/* @__PURE__ */ p("dt", {
297
281
  className: "text-sm font-medium text-text-secondary mb-1",
298
- children: x("auth.userDetails.emailVerified", "Email Verified")
282
+ children: b("auth.userDetails.emailVerified", "Email Verified")
299
283
  }), /* @__PURE__ */ p("dd", {
300
284
  className: "text-text-primary font-medium",
301
- children: A.emailVerified ? /* @__PURE__ */ p("span", {
285
+ children: k.emailVerified ? /* @__PURE__ */ p("span", {
302
286
  className: "text-status-success-text",
303
- children: x("auth.userDetails.yes", "Yes")
287
+ children: b("auth.userDetails.yes", "Yes")
304
288
  }) : /* @__PURE__ */ p("span", {
305
289
  className: "text-status-warning-text",
306
- children: x("auth.userDetails.no", "No")
290
+ children: b("auth.userDetails.no", "No")
307
291
  })
308
292
  })]
309
293
  }),
@@ -311,72 +295,72 @@ function v() {
311
295
  className: "p-4 rounded-lg bg-bg-canvas/50",
312
296
  children: [/* @__PURE__ */ p("dt", {
313
297
  className: "text-sm font-medium text-text-secondary mb-1",
314
- children: x("auth.userDetails.mfaEnabled", "MFA Enabled")
298
+ children: b("auth.userDetails.mfaEnabled", "MFA Enabled")
315
299
  }), /* @__PURE__ */ p("dd", {
316
300
  className: "text-text-primary font-medium",
317
- children: A.mfaEnabled ? /* @__PURE__ */ p("span", {
301
+ children: k.mfaEnabled ? /* @__PURE__ */ p("span", {
318
302
  className: "text-status-success-text",
319
- children: x("auth.userDetails.yes", "Yes")
303
+ children: b("auth.userDetails.yes", "Yes")
320
304
  }) : /* @__PURE__ */ p("span", {
321
305
  className: "text-text-secondary",
322
- children: x("auth.userDetails.no", "No")
306
+ children: b("auth.userDetails.no", "No")
323
307
  })
324
308
  })]
325
309
  }),
326
- A.phoneNumber && /* @__PURE__ */ m("div", {
310
+ k.phoneNumber && /* @__PURE__ */ m("div", {
327
311
  className: "p-4 rounded-lg bg-bg-canvas/50",
328
312
  children: [/* @__PURE__ */ p("dt", {
329
313
  className: "text-sm font-medium text-text-secondary mb-1",
330
- children: x("auth.userDetails.phoneNumber", "Phone Number")
314
+ children: b("auth.userDetails.phoneNumber", "Phone Number")
331
315
  }), /* @__PURE__ */ p("dd", {
332
316
  className: "text-text-primary font-medium",
333
- children: A.phoneNumber
317
+ children: k.phoneNumber
334
318
  })]
335
319
  }),
336
320
  /* @__PURE__ */ m("div", {
337
321
  className: "p-4 rounded-lg bg-bg-canvas/50",
338
322
  children: [/* @__PURE__ */ p("dt", {
339
323
  className: "text-sm font-medium text-text-secondary mb-1",
340
- children: x("auth.userDetails.lastLogin", "Last Login")
324
+ children: b("auth.userDetails.lastLogin", "Last Login")
341
325
  }), /* @__PURE__ */ p("dd", {
342
326
  className: "text-text-primary font-medium",
343
- children: A.lastLoginAt ? new Date(A.lastLoginAt).toLocaleString() : x("auth.userDetails.never", "Never")
327
+ children: k.lastLoginAt ? new Date(k.lastLoginAt).toLocaleString() : b("auth.userDetails.never", "Never")
344
328
  })]
345
329
  }),
346
330
  /* @__PURE__ */ m("div", {
347
331
  className: "p-4 rounded-lg bg-bg-canvas/50",
348
332
  children: [/* @__PURE__ */ p("dt", {
349
333
  className: "text-sm font-medium text-text-secondary mb-1",
350
- children: x("auth.userDetails.created", "Created")
334
+ children: b("auth.userDetails.created", "Created")
351
335
  }), /* @__PURE__ */ p("dd", {
352
336
  className: "text-text-primary font-medium",
353
- children: new Date(A.createdAt).toLocaleString()
337
+ children: new Date(k.createdAt).toLocaleString()
354
338
  })]
355
339
  }),
356
340
  /* @__PURE__ */ m("div", {
357
341
  className: "p-4 rounded-lg bg-bg-canvas/50",
358
342
  children: [/* @__PURE__ */ p("dt", {
359
343
  className: "text-sm font-medium text-text-secondary mb-1",
360
- children: x("auth.userDetails.lastUpdated", "Last Updated")
344
+ children: b("auth.userDetails.lastUpdated", "Last Updated")
361
345
  }), /* @__PURE__ */ p("dd", {
362
346
  className: "text-text-primary font-medium",
363
- children: new Date(A.updatedAt).toLocaleString()
347
+ children: new Date(k.updatedAt).toLocaleString()
364
348
  })]
365
349
  })
366
350
  ]
367
351
  }),
368
352
  /* @__PURE__ */ m("div", {
369
353
  className: "mt-8 pt-6 border-t border-border-subtle flex gap-3",
370
- children: [A.status !== "DELETED" && T && /* @__PURE__ */ p("button", {
354
+ children: [k.status !== "DELETED" && w && /* @__PURE__ */ p("button", {
371
355
  type: "button",
372
- onClick: () => z("deactivate"),
356
+ onClick: () => R("deactivate"),
373
357
  className: "px-4 py-2.5 rounded-xl border border-status-error-border text-status-error-text font-medium hover:bg-status-error-bg-subtle transition-colors",
374
- children: x("auth.userDetails.deactivateUser", "Deactivate User")
375
- }), T && A.status === "DELETED" && C?.role === "SUPER_ADMIN" && /* @__PURE__ */ p("button", {
358
+ children: b("auth.userDetails.deactivateUser", "Deactivate User")
359
+ }), w && k.status === "DELETED" && S?.role === "SUPER_ADMIN" && /* @__PURE__ */ p("button", {
376
360
  type: "button",
377
- onClick: () => z("hard-delete"),
361
+ onClick: () => R("hard-delete"),
378
362
  className: "px-4 py-2.5 rounded-xl bg-status-error-bg text-white font-medium hover:bg-status-error-bg-hover transition-colors",
379
- children: x("auth.userDetails.permanentlyDelete", "Permanently Delete")
363
+ children: b("auth.userDetails.permanentlyDelete", "Permanently Delete")
380
364
  })]
381
365
  })
382
366
  ]
@@ -388,10 +372,10 @@ function v() {
388
372
  className: "text-center py-16 border-2 border-dashed border-border-subtle rounded-xl",
389
373
  children: [/* @__PURE__ */ p("h2", {
390
374
  className: "text-xl font-semibold text-text-primary mb-2",
391
- children: x("auth.userDetails.adminRequired", "Admin Access Required")
375
+ children: b("auth.userDetails.adminRequired", "Admin Access Required")
392
376
  }), /* @__PURE__ */ p("p", {
393
377
  className: "text-text-secondary",
394
- children: x("auth.userDetails.adminRequiredDesc", "You need administrator privileges to view user details.")
378
+ children: b("auth.userDetails.adminRequiredDesc", "You need administrator privileges to view user details.")
395
379
  })]
396
380
  })
397
381
  }) : /* @__PURE__ */ p("div", {
@@ -400,15 +384,15 @@ function v() {
400
384
  className: "text-center py-16 border-2 border-dashed border-border-subtle rounded-xl",
401
385
  children: [/* @__PURE__ */ p("h2", {
402
386
  className: "text-xl font-semibold text-text-primary mb-2",
403
- children: x("auth.userDetails.authRequired", "Authentication Required")
387
+ children: b("auth.userDetails.authRequired", "Authentication Required")
404
388
  }), /* @__PURE__ */ p("p", {
405
389
  className: "text-text-secondary",
406
- children: x("auth.userDetails.signInToView", "Please sign in to view user details")
390
+ children: b("auth.userDetails.signInToView", "Please sign in to view user details")
407
391
  })]
408
392
  })
409
393
  });
410
394
  }
411
395
  //#endregion
412
- export { v as UserDetailsPage };
396
+ export { _ as UserDetailsPage };
413
397
 
414
398
  //# sourceMappingURL=UserDetailsPage.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"UserDetailsPage.js","names":[],"sources":["../../../src/auth/pages/UserDetailsPage.tsx"],"sourcesContent":["import { useState, useEffect } from \"react\";\nimport { useNavigate, useParams } from \"react-router-dom\";\nimport { useI18n } from \"@burdenoff/fe-libs/shared/providers/shell\";\nimport { useAuthPermissions } from \"../hooks/useAuthPermissions\";\nimport { useUserOperations } from \"../hooks/useUserOperations\";\nimport { useAuthStore } from \"../store/authStore\";\nimport { ConfirmActionDialog } from \"../components/ConfirmActionDialog\";\nimport { SpinnerIcon, AlertIcon } from \"../components/AuthIcons\";\nimport { useCreateAuthExportJobMutation } from \"../../generated/wspace-operations\";\nimport type { User, UserRole, UserStatus } from \"../types\";\n\nconst ALLOWED_ROLES: UserRole[] = [\"SUPER_ADMIN\", \"TENANT_ADMIN\", \"ORG_ADMIN\"];\n\nconst getRoleColor = (role: UserRole) => {\n switch (role) {\n case \"SUPER_ADMIN\":\n return \"bg-accent-purple-subtle text-accent-purple\";\n case \"TENANT_ADMIN\":\n return \"bg-status-info-bg-subtle text-status-info-text\";\n case \"ORG_ADMIN\":\n return \"bg-accent-indigo-subtle text-accent-indigo\";\n case \"WORKSPACE_ADMIN\":\n return \"bg-accent-cyan-subtle text-accent-cyan\";\n case \"MEMBER\":\n return \"bg-status-success-bg-subtle text-status-success-text\";\n case \"GUEST\":\n return \"bg-bg-sunken text-text-primary\";\n default:\n return \"bg-bg-sunken text-text-primary\";\n }\n};\n\nconst getStatusColor = (status: UserStatus) => {\n switch (status) {\n case \"ACTIVE\":\n return \"bg-status-success-bg-subtle text-status-success-text\";\n case \"INACTIVE\":\n return \"bg-status-warning-bg-subtle text-status-warning-text\";\n case \"SUSPENDED\":\n return \"bg-status-warning-bg-subtle text-status-warning-text\";\n case \"DELETED\":\n return \"bg-status-error-bg-subtle text-status-error-text\";\n default:\n return \"bg-bg-sunken text-text-primary\";\n }\n};\n\n/**\n * User details page (admin only).\n *\n * Route: /auth/users/:id\n *\n * Renders the full profile of a single user, with admin actions\n * (edit, deactivate, hard-delete, export). Reuses the same getUser query\n * that UsersPage relies on.\n */\nexport function UserDetailsPage() {\n const { id } = useParams<{ id: string }>();\n const navigate = useNavigate();\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 isAuthenticated = useAuthStore((state) => state.isAuthenticated);\n const currentUser = useAuthStore((state) => state.user);\n const {\n canViewUsers,\n canDeleteUsers,\n isLoading: permissionsLoading,\n } = useAuthPermissions();\n const { getUser, deleteUser, hardDeleteUser } = useUserOperations();\n\n const [user, setUser] = useState<User | null>(null);\n const [loading, setLoading] = useState(true);\n const [error, setError] = useState<string | null>(null);\n const [successMessage, setSuccessMessage] = useState<string | null>(null);\n const [pendingAction, setPendingAction] = useState<\n \"deactivate\" | \"hard-delete\" | null\n >(null);\n\n const [createExportJob, { loading: isExporting }] =\n useCreateAuthExportJobMutation();\n const [exportError, setExportError] = useState<string | null>(null);\n\n useEffect(() => {\n let cancelled = false;\n if (!id) {\n setError(\"Missing user id\");\n setLoading(false);\n return;\n }\n if (!isAuthenticated) {\n setLoading(false);\n return;\n }\n\n setLoading(true);\n setError(null);\n getUser(id)\n .then((fetched) => {\n if (cancelled) return;\n if (!fetched) {\n setError(tr(\"auth.userDetails.userNotFound\", \"User not found\"));\n } else {\n setUser(fetched);\n }\n })\n .catch((err: unknown) => {\n if (cancelled) return;\n setError(\n err instanceof Error ? err.message : \"Failed to fetch user details\",\n );\n })\n .finally(() => {\n if (!cancelled) setLoading(false);\n });\n\n return () => {\n cancelled = true;\n };\n // tr is stable within the render, id/getUser/isAuthenticated drive the effect\n // eslint-disable-next-line react-hooks/exhaustive-deps\n }, [id, getUser, isAuthenticated]);\n\n const handleDeactivate = async () => {\n if (!user) return;\n try {\n const result = await deleteUser(user.id);\n if (result.success) {\n setSuccessMessage(\n tr(\n \"auth.userDetails.userDeactivated\",\n 'User \"{email}\" has been deactivated.',\n ).replace(\"{email}\", user.email),\n );\n setPendingAction(null);\n // Refresh\n const fresh = await getUser(user.id);\n if (fresh) setUser(fresh);\n } else {\n setError(result.message);\n }\n } catch (err) {\n setError(err instanceof Error ? err.message : \"Failed to delete user\");\n }\n };\n\n const handleHardDelete = async () => {\n if (!user) return;\n try {\n const result = await hardDeleteUser(user.id);\n if (result.success) {\n setPendingAction(null);\n navigate(\"/auth/users\");\n } else {\n setError(result.message);\n }\n } catch (err) {\n setError(\n err instanceof Error ? err.message : \"Failed to hard delete user\",\n );\n }\n };\n\n const handleExport = async () => {\n if (!user) return;\n setExportError(null);\n try {\n await createExportJob({\n variables: {\n input: {\n name: `User Data Export - ${user.email}`,\n entityTypes: [\n \"user_profile\",\n \"user_sessions\",\n \"permission_audit_logs\",\n ],\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 // Auth + role guards\n if (!isAuthenticated) {\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 <h2 className=\"text-xl font-semibold text-text-primary mb-2\">\n {tr(\"auth.userDetails.authRequired\", \"Authentication Required\")}\n </h2>\n <p className=\"text-text-secondary\">\n {tr(\n \"auth.userDetails.signInToView\",\n \"Please sign in to view user details\",\n )}\n </p>\n </div>\n </div>\n );\n }\n\n if (currentUser && !ALLOWED_ROLES.includes(currentUser.role)) {\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 <h2 className=\"text-xl font-semibold text-text-primary mb-2\">\n {tr(\n \"auth.userDetails.notAllowed\",\n \"You are not allowed to access this page\",\n )}\n </h2>\n <p className=\"text-text-secondary\">\n {tr(\n \"auth.userDetails.notAllowedDesc\",\n \"This page is restricted to administrators only.\",\n )}\n </p>\n </div>\n </div>\n );\n }\n\n if (permissionsLoading || loading) {\n return (\n <div className=\"max-w-4xl mx-auto p-6\">\n <div className=\"text-center py-16\" aria-live=\"polite\" aria-busy=\"true\">\n <SpinnerIcon\n className=\"size-8 mx-auto text-text-secondary animate-spin\"\n aria-hidden=\"true\"\n />\n <p className=\"mt-4 text-text-secondary\">\n {tr(\"auth.userDetails.loading\", \"Loading user...\")}\n </p>\n </div>\n </div>\n );\n }\n\n if (!canViewUsers) {\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 <h2 className=\"text-xl font-semibold text-text-primary mb-2\">\n {tr(\"auth.userDetails.adminRequired\", \"Admin Access Required\")}\n </h2>\n <p className=\"text-text-secondary\">\n {tr(\n \"auth.userDetails.adminRequiredDesc\",\n \"You need administrator privileges to view user details.\",\n )}\n </p>\n </div>\n </div>\n );\n }\n\n if (error || !user) {\n return (\n <div className=\"max-w-4xl mx-auto p-6\">\n <button\n type=\"button\"\n onClick={() => navigate(\"/auth/users\")}\n className=\"mb-6 flex items-center gap-2 text-text-link hover:underline font-medium\"\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=\"M10 19l-7-7m0 0l7-7m-7 7h18\"\n />\n </svg>\n {tr(\"auth.userDetails.backToUsers\", \"Back to Users\")}\n </button>\n <div\n role=\"alert\"\n className=\"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\">\n {error ?? tr(\"auth.userDetails.userNotFound\", \"User not found\")}\n </p>\n </div>\n </div>\n );\n }\n\n return (\n <div className=\"max-w-4xl mx-auto p-6\">\n <ConfirmActionDialog\n open={pendingAction !== null}\n onOpenChange={(open) => {\n if (!open) setPendingAction(null);\n }}\n title={\n pendingAction === \"hard-delete\"\n ? tr(\"auth.userDetails.hardDeleteTitle\", \"Permanently delete user?\")\n : tr(\"auth.userDetails.deactivateTitle\", \"Deactivate user?\")\n }\n description={\n pendingAction === \"hard-delete\"\n ? tr(\n \"auth.userDetails.hardDeleteDesc\",\n 'This permanently deletes \"{email}\" and cannot be undone.',\n ).replace(\"{email}\", user.email)\n : tr(\n \"auth.userDetails.deactivateDesc\",\n '\"{email}\" will be prevented from signing in until reactivated.',\n ).replace(\"{email}\", user.email)\n }\n confirmText={\n pendingAction === \"hard-delete\"\n ? tr(\"auth.userDetails.hardDeleteConfirm\", \"Delete permanently\")\n : tr(\"auth.userDetails.deactivateConfirm\", \"Deactivate user\")\n }\n variant={pendingAction === \"hard-delete\" ? \"danger\" : \"warning\"}\n onConfirm={async () => {\n if (pendingAction === \"hard-delete\") await handleHardDelete();\n else if (pendingAction === \"deactivate\") await handleDeactivate();\n }}\n />\n\n {/* Header row */}\n <div className=\"flex items-center justify-between mb-6\">\n <button\n type=\"button\"\n onClick={() => navigate(\"/auth/users\")}\n className=\"flex items-center gap-2 text-text-link hover:underline font-medium\"\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=\"M10 19l-7-7m0 0l7-7m-7 7h18\"\n />\n </svg>\n {tr(\"auth.userDetails.backToUsers\", \"Back to Users\")}\n </button>\n\n {/* Export button */}\n <button\n type=\"button\"\n onClick={() => void handleExport()}\n disabled={isExporting}\n aria-label={tr(\n \"auth.userDetails.exportAriaLabel\",\n \"Export user data\",\n )}\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.userDetails.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.userDetails.exportButton\", \"Export User Data\")}\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 {successMessage && (\n <div className=\"mb-6 p-4 rounded-xl bg-status-success-bg-subtle border border-status-success-border\">\n <p className=\"text-status-success-text\">{successMessage}</p>\n <button\n type=\"button\"\n onClick={() => setSuccessMessage(null)}\n className=\"mt-2 text-sm text-status-success-text hover:underline\"\n >\n {tr(\"auth.userDetails.dismiss\", \"Dismiss\")}\n </button>\n </div>\n )}\n\n <div className=\"p-6 rounded-xl border border-border-subtle bg-bg-surface shadow-sm\">\n <div className=\"flex items-center gap-4 mb-8\">\n <div\n className=\"auth-brand-badge flex size-20 items-center justify-center rounded-full text-3xl font-semibold\"\n aria-hidden=\"true\"\n >\n {(user.name || user.username || user.email).charAt(0).toUpperCase()}\n </div>\n <div>\n <h1 className=\"text-2xl font-semibold text-text-primary\">\n {user.name || user.username}\n </h1>\n <p className=\"text-text-secondary\" data-testid=\"user-email\">\n {user.email}\n </p>\n <div className=\"flex items-center gap-2 mt-2\">\n <span\n className={`px-2.5 py-1 rounded-full text-xs font-medium ${getRoleColor(user.role)}`}\n >\n {user.role.replace(\"_\", \" \")}\n </span>\n <span\n className={`px-2.5 py-1 rounded-full text-xs font-medium ${getStatusColor(user.status)}`}\n >\n {user.status}\n </span>\n </div>\n </div>\n </div>\n\n <dl className=\"grid grid-cols-2 gap-6\">\n <div className=\"p-4 rounded-lg bg-bg-canvas/50\">\n <dt className=\"text-sm font-medium text-text-secondary mb-1\">\n {tr(\"auth.userDetails.username\", \"Username\")}\n </dt>\n <dd className=\"text-text-primary font-medium\">{user.username}</dd>\n </div>\n <div className=\"p-4 rounded-lg bg-bg-canvas/50\">\n <dt className=\"text-sm font-medium text-text-secondary mb-1\">\n {tr(\"auth.userDetails.email\", \"Email\")}\n </dt>\n <dd className=\"text-text-primary font-medium\">{user.email}</dd>\n </div>\n <div className=\"p-4 rounded-lg bg-bg-canvas/50\">\n <dt className=\"text-sm font-medium text-text-secondary mb-1\">\n {tr(\"auth.userDetails.emailVerified\", \"Email Verified\")}\n </dt>\n <dd className=\"text-text-primary font-medium\">\n {user.emailVerified ? (\n <span className=\"text-status-success-text\">\n {tr(\"auth.userDetails.yes\", \"Yes\")}\n </span>\n ) : (\n <span className=\"text-status-warning-text\">\n {tr(\"auth.userDetails.no\", \"No\")}\n </span>\n )}\n </dd>\n </div>\n <div className=\"p-4 rounded-lg bg-bg-canvas/50\">\n <dt className=\"text-sm font-medium text-text-secondary mb-1\">\n {tr(\"auth.userDetails.mfaEnabled\", \"MFA Enabled\")}\n </dt>\n <dd className=\"text-text-primary font-medium\">\n {user.mfaEnabled ? (\n <span className=\"text-status-success-text\">\n {tr(\"auth.userDetails.yes\", \"Yes\")}\n </span>\n ) : (\n <span className=\"text-text-secondary\">\n {tr(\"auth.userDetails.no\", \"No\")}\n </span>\n )}\n </dd>\n </div>\n {user.phoneNumber && (\n <div className=\"p-4 rounded-lg bg-bg-canvas/50\">\n <dt className=\"text-sm font-medium text-text-secondary mb-1\">\n {tr(\"auth.userDetails.phoneNumber\", \"Phone Number\")}\n </dt>\n <dd className=\"text-text-primary font-medium\">\n {user.phoneNumber}\n </dd>\n </div>\n )}\n <div className=\"p-4 rounded-lg bg-bg-canvas/50\">\n <dt className=\"text-sm font-medium text-text-secondary mb-1\">\n {tr(\"auth.userDetails.lastLogin\", \"Last Login\")}\n </dt>\n <dd className=\"text-text-primary font-medium\">\n {user.lastLoginAt\n ? new Date(user.lastLoginAt).toLocaleString()\n : tr(\"auth.userDetails.never\", \"Never\")}\n </dd>\n </div>\n <div className=\"p-4 rounded-lg bg-bg-canvas/50\">\n <dt className=\"text-sm font-medium text-text-secondary mb-1\">\n {tr(\"auth.userDetails.created\", \"Created\")}\n </dt>\n <dd className=\"text-text-primary font-medium\">\n {new Date(user.createdAt).toLocaleString()}\n </dd>\n </div>\n <div className=\"p-4 rounded-lg bg-bg-canvas/50\">\n <dt className=\"text-sm font-medium text-text-secondary mb-1\">\n {tr(\"auth.userDetails.lastUpdated\", \"Last Updated\")}\n </dt>\n <dd className=\"text-text-primary font-medium\">\n {new Date(user.updatedAt).toLocaleString()}\n </dd>\n </div>\n </dl>\n\n <div className=\"mt-8 pt-6 border-t border-border-subtle flex gap-3\">\n {user.status !== \"DELETED\" && canDeleteUsers && (\n <button\n type=\"button\"\n onClick={() => setPendingAction(\"deactivate\")}\n className=\"px-4 py-2.5 rounded-xl border border-status-error-border text-status-error-text font-medium hover:bg-status-error-bg-subtle transition-colors\"\n >\n {tr(\"auth.userDetails.deactivateUser\", \"Deactivate User\")}\n </button>\n )}\n {canDeleteUsers &&\n user.status === \"DELETED\" &&\n currentUser?.role === \"SUPER_ADMIN\" && (\n <button\n type=\"button\"\n onClick={() => setPendingAction(\"hard-delete\")}\n className=\"px-4 py-2.5 rounded-xl bg-status-error-bg text-white font-medium hover:bg-status-error-bg-hover transition-colors\"\n >\n {tr(\"auth.userDetails.permanentlyDelete\", \"Permanently Delete\")}\n </button>\n )}\n </div>\n </div>\n </div>\n );\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"UserDetailsPage.js","names":[],"sources":["../../../src/auth/pages/UserDetailsPage.tsx"],"sourcesContent":["import { useState, useEffect } from \"react\";\nimport { useNavigate, useParams } from \"react-router-dom\";\nimport { useI18n } from \"@burdenoff/fe-libs/shared/providers/shell\";\nimport { useAuthPermissions } from \"../hooks/useAuthPermissions\";\nimport { useUserOperations } from \"../hooks/useUserOperations\";\nimport { useAuthStore } from \"../store/authStore\";\nimport { ConfirmActionDialog } from \"../components/ConfirmActionDialog\";\nimport { SpinnerIcon, AlertIcon } from \"../components/AuthIcons\";\nimport { useCreateAuthExportJobMutation } from \"../../generated/wspace-operations\";\nimport type { User, UserRole, UserStatus } from \"../types\";\n\nconst getRoleColor = (role: UserRole) => {\n switch (role) {\n case \"SUPER_ADMIN\":\n return \"bg-accent-purple-subtle text-accent-purple\";\n case \"TENANT_ADMIN\":\n return \"bg-status-info-bg-subtle text-status-info-text\";\n case \"ORG_ADMIN\":\n return \"bg-accent-indigo-subtle text-accent-indigo\";\n case \"WORKSPACE_ADMIN\":\n return \"bg-accent-cyan-subtle text-accent-cyan\";\n case \"MEMBER\":\n return \"bg-status-success-bg-subtle text-status-success-text\";\n case \"GUEST\":\n return \"bg-bg-sunken text-text-primary\";\n default:\n return \"bg-bg-sunken text-text-primary\";\n }\n};\n\nconst getStatusColor = (status: UserStatus) => {\n switch (status) {\n case \"ACTIVE\":\n return \"bg-status-success-bg-subtle text-status-success-text\";\n case \"INACTIVE\":\n return \"bg-status-warning-bg-subtle text-status-warning-text\";\n case \"SUSPENDED\":\n return \"bg-status-warning-bg-subtle text-status-warning-text\";\n case \"DELETED\":\n return \"bg-status-error-bg-subtle text-status-error-text\";\n default:\n return \"bg-bg-sunken text-text-primary\";\n }\n};\n\n/**\n * User details page (admin only).\n *\n * Route: /auth/users/:id\n *\n * Renders the full profile of a single user, with admin actions\n * (edit, deactivate, hard-delete, export). Reuses the same getUser query\n * that UsersPage relies on.\n */\nexport function UserDetailsPage() {\n const { id } = useParams<{ id: string }>();\n const navigate = useNavigate();\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 isAuthenticated = useAuthStore((state) => state.isAuthenticated);\n const currentUser = useAuthStore((state) => state.user);\n const {\n canViewUsers,\n canDeleteUsers,\n isLoading: permissionsLoading,\n } = useAuthPermissions();\n const { getUser, deleteUser, hardDeleteUser } = useUserOperations();\n\n const [user, setUser] = useState<User | null>(null);\n const [loading, setLoading] = useState(true);\n const [error, setError] = useState<string | null>(null);\n const [successMessage, setSuccessMessage] = useState<string | null>(null);\n const [pendingAction, setPendingAction] = useState<\n \"deactivate\" | \"hard-delete\" | null\n >(null);\n\n const [createExportJob, { loading: isExporting }] =\n useCreateAuthExportJobMutation();\n const [exportError, setExportError] = useState<string | null>(null);\n\n useEffect(() => {\n let cancelled = false;\n if (!id) {\n setError(\"Missing user id\");\n setLoading(false);\n return;\n }\n if (!isAuthenticated) {\n setLoading(false);\n return;\n }\n\n setLoading(true);\n setError(null);\n getUser(id)\n .then((fetched) => {\n if (cancelled) return;\n if (!fetched) {\n setError(tr(\"auth.userDetails.userNotFound\", \"User not found\"));\n } else {\n setUser(fetched);\n }\n })\n .catch((err: unknown) => {\n if (cancelled) return;\n setError(\n err instanceof Error ? err.message : \"Failed to fetch user details\",\n );\n })\n .finally(() => {\n if (!cancelled) setLoading(false);\n });\n\n return () => {\n cancelled = true;\n };\n // tr is stable within the render, id/getUser/isAuthenticated drive the effect\n // eslint-disable-next-line react-hooks/exhaustive-deps\n }, [id, getUser, isAuthenticated]);\n\n const handleDeactivate = async () => {\n if (!user) return;\n try {\n const result = await deleteUser(user.id);\n if (result.success) {\n setSuccessMessage(\n tr(\n \"auth.userDetails.userDeactivated\",\n 'User \"{email}\" has been deactivated.',\n ).replace(\"{email}\", user.email),\n );\n setPendingAction(null);\n // Refresh\n const fresh = await getUser(user.id);\n if (fresh) setUser(fresh);\n } else {\n setError(result.message);\n }\n } catch (err) {\n setError(err instanceof Error ? err.message : \"Failed to delete user\");\n }\n };\n\n const handleHardDelete = async () => {\n if (!user) return;\n try {\n const result = await hardDeleteUser(user.id);\n if (result.success) {\n setPendingAction(null);\n navigate(\"/auth/users\");\n } else {\n setError(result.message);\n }\n } catch (err) {\n setError(\n err instanceof Error ? err.message : \"Failed to hard delete user\",\n );\n }\n };\n\n const handleExport = async () => {\n if (!user) return;\n setExportError(null);\n try {\n await createExportJob({\n variables: {\n input: {\n name: `User Data Export - ${user.email}`,\n entityTypes: [\n \"user_profile\",\n \"user_sessions\",\n \"permission_audit_logs\",\n ],\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 // Auth + role guards\n if (!isAuthenticated) {\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 <h2 className=\"text-xl font-semibold text-text-primary mb-2\">\n {tr(\"auth.userDetails.authRequired\", \"Authentication Required\")}\n </h2>\n <p className=\"text-text-secondary\">\n {tr(\n \"auth.userDetails.signInToView\",\n \"Please sign in to view user details\",\n )}\n </p>\n </div>\n </div>\n );\n }\n\n if (permissionsLoading || loading) {\n return (\n <div className=\"max-w-4xl mx-auto p-6\">\n <div className=\"text-center py-16\" aria-live=\"polite\" aria-busy=\"true\">\n <SpinnerIcon\n className=\"size-8 mx-auto text-text-secondary animate-spin\"\n aria-hidden=\"true\"\n />\n <p className=\"mt-4 text-text-secondary\">\n {tr(\"auth.userDetails.loading\", \"Loading user...\")}\n </p>\n </div>\n </div>\n );\n }\n\n if (!canViewUsers) {\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 <h2 className=\"text-xl font-semibold text-text-primary mb-2\">\n {tr(\"auth.userDetails.adminRequired\", \"Admin Access Required\")}\n </h2>\n <p className=\"text-text-secondary\">\n {tr(\n \"auth.userDetails.adminRequiredDesc\",\n \"You need administrator privileges to view user details.\",\n )}\n </p>\n </div>\n </div>\n );\n }\n\n if (error || !user) {\n return (\n <div className=\"max-w-4xl mx-auto p-6\">\n <button\n type=\"button\"\n onClick={() => navigate(\"/auth/users\")}\n className=\"mb-6 flex items-center gap-2 text-text-link hover:underline font-medium\"\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=\"M10 19l-7-7m0 0l7-7m-7 7h18\"\n />\n </svg>\n {tr(\"auth.userDetails.backToUsers\", \"Back to Users\")}\n </button>\n <div\n role=\"alert\"\n className=\"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\">\n {error ?? tr(\"auth.userDetails.userNotFound\", \"User not found\")}\n </p>\n </div>\n </div>\n );\n }\n\n return (\n <div className=\"max-w-4xl mx-auto p-6\">\n <ConfirmActionDialog\n open={pendingAction !== null}\n onOpenChange={(open) => {\n if (!open) setPendingAction(null);\n }}\n title={\n pendingAction === \"hard-delete\"\n ? tr(\"auth.userDetails.hardDeleteTitle\", \"Permanently delete user?\")\n : tr(\"auth.userDetails.deactivateTitle\", \"Deactivate user?\")\n }\n description={\n pendingAction === \"hard-delete\"\n ? tr(\n \"auth.userDetails.hardDeleteDesc\",\n 'This permanently deletes \"{email}\" and cannot be undone.',\n ).replace(\"{email}\", user.email)\n : tr(\n \"auth.userDetails.deactivateDesc\",\n '\"{email}\" will be prevented from signing in until reactivated.',\n ).replace(\"{email}\", user.email)\n }\n confirmText={\n pendingAction === \"hard-delete\"\n ? tr(\"auth.userDetails.hardDeleteConfirm\", \"Delete permanently\")\n : tr(\"auth.userDetails.deactivateConfirm\", \"Deactivate user\")\n }\n variant={pendingAction === \"hard-delete\" ? \"danger\" : \"warning\"}\n onConfirm={async () => {\n if (pendingAction === \"hard-delete\") await handleHardDelete();\n else if (pendingAction === \"deactivate\") await handleDeactivate();\n }}\n />\n\n {/* Header row */}\n <div className=\"flex items-center justify-between mb-6\">\n <button\n type=\"button\"\n onClick={() => navigate(\"/auth/users\")}\n className=\"flex items-center gap-2 text-text-link hover:underline font-medium\"\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=\"M10 19l-7-7m0 0l7-7m-7 7h18\"\n />\n </svg>\n {tr(\"auth.userDetails.backToUsers\", \"Back to Users\")}\n </button>\n\n {/* Export button */}\n <button\n type=\"button\"\n onClick={() => void handleExport()}\n disabled={isExporting}\n aria-label={tr(\n \"auth.userDetails.exportAriaLabel\",\n \"Export user data\",\n )}\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.userDetails.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.userDetails.exportButton\", \"Export User Data\")}\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 {successMessage && (\n <div className=\"mb-6 p-4 rounded-xl bg-status-success-bg-subtle border border-status-success-border\">\n <p className=\"text-status-success-text\">{successMessage}</p>\n <button\n type=\"button\"\n onClick={() => setSuccessMessage(null)}\n className=\"mt-2 text-sm text-status-success-text hover:underline\"\n >\n {tr(\"auth.userDetails.dismiss\", \"Dismiss\")}\n </button>\n </div>\n )}\n\n <div className=\"p-6 rounded-xl border border-border-subtle bg-bg-surface shadow-sm\">\n <div className=\"flex items-center gap-4 mb-8\">\n <div\n className=\"auth-brand-badge flex size-20 items-center justify-center rounded-full text-3xl font-semibold\"\n aria-hidden=\"true\"\n >\n {(user.name || user.username || user.email).charAt(0).toUpperCase()}\n </div>\n <div>\n <h1 className=\"text-2xl font-semibold text-text-primary\">\n {user.name || user.username}\n </h1>\n <p className=\"text-text-secondary\" data-testid=\"user-email\">\n {user.email}\n </p>\n <div className=\"flex items-center gap-2 mt-2\">\n <span\n className={`px-2.5 py-1 rounded-full text-xs font-medium ${getRoleColor(user.role)}`}\n >\n {user.role.replace(\"_\", \" \")}\n </span>\n <span\n className={`px-2.5 py-1 rounded-full text-xs font-medium ${getStatusColor(user.status)}`}\n >\n {user.status}\n </span>\n </div>\n </div>\n </div>\n\n <dl className=\"grid grid-cols-2 gap-6\">\n <div className=\"p-4 rounded-lg bg-bg-canvas/50\">\n <dt className=\"text-sm font-medium text-text-secondary mb-1\">\n {tr(\"auth.userDetails.username\", \"Username\")}\n </dt>\n <dd className=\"text-text-primary font-medium\">{user.username}</dd>\n </div>\n <div className=\"p-4 rounded-lg bg-bg-canvas/50\">\n <dt className=\"text-sm font-medium text-text-secondary mb-1\">\n {tr(\"auth.userDetails.email\", \"Email\")}\n </dt>\n <dd className=\"text-text-primary font-medium\">{user.email}</dd>\n </div>\n <div className=\"p-4 rounded-lg bg-bg-canvas/50\">\n <dt className=\"text-sm font-medium text-text-secondary mb-1\">\n {tr(\"auth.userDetails.emailVerified\", \"Email Verified\")}\n </dt>\n <dd className=\"text-text-primary font-medium\">\n {user.emailVerified ? (\n <span className=\"text-status-success-text\">\n {tr(\"auth.userDetails.yes\", \"Yes\")}\n </span>\n ) : (\n <span className=\"text-status-warning-text\">\n {tr(\"auth.userDetails.no\", \"No\")}\n </span>\n )}\n </dd>\n </div>\n <div className=\"p-4 rounded-lg bg-bg-canvas/50\">\n <dt className=\"text-sm font-medium text-text-secondary mb-1\">\n {tr(\"auth.userDetails.mfaEnabled\", \"MFA Enabled\")}\n </dt>\n <dd className=\"text-text-primary font-medium\">\n {user.mfaEnabled ? (\n <span className=\"text-status-success-text\">\n {tr(\"auth.userDetails.yes\", \"Yes\")}\n </span>\n ) : (\n <span className=\"text-text-secondary\">\n {tr(\"auth.userDetails.no\", \"No\")}\n </span>\n )}\n </dd>\n </div>\n {user.phoneNumber && (\n <div className=\"p-4 rounded-lg bg-bg-canvas/50\">\n <dt className=\"text-sm font-medium text-text-secondary mb-1\">\n {tr(\"auth.userDetails.phoneNumber\", \"Phone Number\")}\n </dt>\n <dd className=\"text-text-primary font-medium\">\n {user.phoneNumber}\n </dd>\n </div>\n )}\n <div className=\"p-4 rounded-lg bg-bg-canvas/50\">\n <dt className=\"text-sm font-medium text-text-secondary mb-1\">\n {tr(\"auth.userDetails.lastLogin\", \"Last Login\")}\n </dt>\n <dd className=\"text-text-primary font-medium\">\n {user.lastLoginAt\n ? new Date(user.lastLoginAt).toLocaleString()\n : tr(\"auth.userDetails.never\", \"Never\")}\n </dd>\n </div>\n <div className=\"p-4 rounded-lg bg-bg-canvas/50\">\n <dt className=\"text-sm font-medium text-text-secondary mb-1\">\n {tr(\"auth.userDetails.created\", \"Created\")}\n </dt>\n <dd className=\"text-text-primary font-medium\">\n {new Date(user.createdAt).toLocaleString()}\n </dd>\n </div>\n <div className=\"p-4 rounded-lg bg-bg-canvas/50\">\n <dt className=\"text-sm font-medium text-text-secondary mb-1\">\n {tr(\"auth.userDetails.lastUpdated\", \"Last Updated\")}\n </dt>\n <dd className=\"text-text-primary font-medium\">\n {new Date(user.updatedAt).toLocaleString()}\n </dd>\n </div>\n </dl>\n\n <div className=\"mt-8 pt-6 border-t border-border-subtle flex gap-3\">\n {user.status !== \"DELETED\" && canDeleteUsers && (\n <button\n type=\"button\"\n onClick={() => setPendingAction(\"deactivate\")}\n className=\"px-4 py-2.5 rounded-xl border border-status-error-border text-status-error-text font-medium hover:bg-status-error-bg-subtle transition-colors\"\n >\n {tr(\"auth.userDetails.deactivateUser\", \"Deactivate User\")}\n </button>\n )}\n {canDeleteUsers &&\n user.status === \"DELETED\" &&\n currentUser?.role === \"SUPER_ADMIN\" && (\n <button\n type=\"button\"\n onClick={() => setPendingAction(\"hard-delete\")}\n className=\"px-4 py-2.5 rounded-xl bg-status-error-bg text-white font-medium hover:bg-status-error-bg-hover transition-colors\"\n >\n {tr(\"auth.userDetails.permanentlyDelete\", \"Permanently Delete\")}\n </button>\n )}\n </div>\n </div>\n </div>\n );\n}\n"],"mappings":"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"}