@open-kingdom/shared-frontend-feature-user-management 0.0.2-14 → 0.0.2-17

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 (61) hide show
  1. package/README.md +136 -4
  2. package/dist/index.d.ts +4 -1
  3. package/dist/index.d.ts.map +1 -1
  4. package/dist/index.js +654 -288
  5. package/dist/lib/components/invitations/AcceptInvitation.component.d.ts.map +1 -1
  6. package/dist/lib/components/invitations/InvitationList.component.d.ts +2 -0
  7. package/dist/lib/components/invitations/InvitationList.component.d.ts.map +1 -0
  8. package/dist/lib/components/invitations/InviteUserModal.component.d.ts.map +1 -1
  9. package/dist/lib/components/invitations/index.d.ts +1 -0
  10. package/dist/lib/components/invitations/index.d.ts.map +1 -1
  11. package/dist/lib/components/roles/RoleList.component.d.ts +2 -0
  12. package/dist/lib/components/roles/RoleList.component.d.ts.map +1 -0
  13. package/dist/lib/components/roles/RolePermissionsModal.component.d.ts +11 -0
  14. package/dist/lib/components/roles/RolePermissionsModal.component.d.ts.map +1 -0
  15. package/dist/lib/components/roles/index.d.ts +3 -0
  16. package/dist/lib/components/roles/index.d.ts.map +1 -0
  17. package/dist/lib/components/shared/ConfirmDialog.component.d.ts.map +1 -1
  18. package/dist/lib/components/shared/FormField.component.d.ts.map +1 -1
  19. package/dist/lib/components/shared/RoleBadge.component.d.ts +1 -2
  20. package/dist/lib/components/shared/RoleBadge.component.d.ts.map +1 -1
  21. package/dist/lib/components/shared/StatusBadge.component.d.ts +7 -0
  22. package/dist/lib/components/shared/StatusBadge.component.d.ts.map +1 -0
  23. package/dist/lib/components/shared/StatusCard.component.d.ts.map +1 -1
  24. package/dist/lib/components/users/RoleChangeModal.component.d.ts +12 -0
  25. package/dist/lib/components/users/RoleChangeModal.component.d.ts.map +1 -0
  26. package/dist/lib/components/users/UserList.component.d.ts.map +1 -1
  27. package/dist/lib/hooks/useHasPermission.d.ts +2 -0
  28. package/dist/lib/hooks/useHasPermission.d.ts.map +1 -0
  29. package/dist/lib/types.d.ts +11 -1
  30. package/dist/lib/types.d.ts.map +1 -1
  31. package/package.json +24 -1
  32. package/.babelrc +0 -12
  33. package/dist/lib/components/shared/ModalOverlay.component.d.ts +0 -9
  34. package/dist/lib/components/shared/ModalOverlay.component.d.ts.map +0 -1
  35. package/dist/lib/styles.d.ts +0 -8
  36. package/dist/lib/styles.d.ts.map +0 -1
  37. package/jest.config.cts +0 -14
  38. package/src/index.ts +0 -4
  39. package/src/lib/components/invitations/AcceptInvitation.component.spec.tsx +0 -154
  40. package/src/lib/components/invitations/AcceptInvitation.component.tsx +0 -197
  41. package/src/lib/components/invitations/InviteUserModal.component.spec.tsx +0 -79
  42. package/src/lib/components/invitations/InviteUserModal.component.tsx +0 -121
  43. package/src/lib/components/invitations/index.ts +0 -2
  44. package/src/lib/components/shared/ConfirmDialog.component.spec.tsx +0 -45
  45. package/src/lib/components/shared/ConfirmDialog.component.tsx +0 -58
  46. package/src/lib/components/shared/FormField.component.spec.tsx +0 -50
  47. package/src/lib/components/shared/FormField.component.tsx +0 -34
  48. package/src/lib/components/shared/ModalOverlay.component.spec.tsx +0 -81
  49. package/src/lib/components/shared/ModalOverlay.component.tsx +0 -45
  50. package/src/lib/components/shared/RoleBadge.component.spec.tsx +0 -20
  51. package/src/lib/components/shared/RoleBadge.component.tsx +0 -25
  52. package/src/lib/components/shared/StatusCard.component.spec.tsx +0 -44
  53. package/src/lib/components/shared/StatusCard.component.tsx +0 -47
  54. package/src/lib/components/users/UserList.component.spec.tsx +0 -216
  55. package/src/lib/components/users/UserList.component.tsx +0 -153
  56. package/src/lib/styles.ts +0 -19
  57. package/src/lib/types.ts +0 -9
  58. package/tsconfig.json +0 -13
  59. package/tsconfig.lib.json +0 -47
  60. package/tsconfig.spec.json +0 -27
  61. package/vite.config.mts +0 -58
package/dist/index.js CHANGED
@@ -1,410 +1,356 @@
1
1
  import { jsx as e, jsxs as i } from "react/jsx-runtime";
2
- import { useCallback as P, useState as I, useMemo as V } from "react";
3
- import { useDispatch as U } from "react-redux";
4
- import { useInvitationsControllerInviteMutation as B, useInvitationsControllerValidateQuery as E, useInvitationsControllerAcceptMutation as G, useUsersControllerFindAllQuery as q, useUsersControllerDeleteMutation as z } from "@open-kingdom/shared-frontend-data-access-api-client";
5
- import { showSuccessNotification as F } from "@open-kingdom/shared-frontend-data-access-notifications";
6
- import { DataGrid as Y } from "@open-kingdom/shared-frontend-ui-datagrid";
7
- import { useTheme as O } from "@open-kingdom/shared-frontend-ui-theme";
8
- import { useKeyboardEvent as Q } from "@react-hookz/web";
9
- import { useForm as A } from "react-hook-form";
10
- import { zodResolver as L } from "@hookform/resolvers/zod";
11
- import { z as p } from "zod";
12
- function j({
2
+ import { useState as N, useMemo as R, useCallback as W } from "react";
3
+ import { useDispatch as I } from "react-redux";
4
+ import { useInvitationsControllerInviteMutation as X, useRolesControllerFindAllQuery as F, useInvitationsControllerValidateQuery as Z, useInvitationsControllerAcceptMutation as _, useInvitationsControllerFindAllQuery as ee, useInvitationsControllerCancelMutation as te, useUserRolesControllerSetRolesMutation as ne, useAuthControllerGetProfileQuery as ie, useUsersControllerFindAllQuery as ae, useUsersControllerDeleteMutation as se, usePermissionsControllerFindAllQuery as re, useRolesControllerGetPermissionsQuery as le, useRolesControllerSetPermissionsMutation as oe, useRolesControllerDeleteMutation as de } from "@open-kingdom/shared-frontend-data-access-api-client";
5
+ import { showSuccessNotification as A } from "@open-kingdom/shared-frontend-data-access-notifications";
6
+ import { DataGrid as U } from "@open-kingdom/shared-frontend-ui-datagrid";
7
+ import { AlertDialog as ce, AlertDialogContent as ue, AlertDialogHeader as me, AlertDialogTitle as fe, AlertDialogDescription as he, AlertDialogFooter as pe, AlertDialogCancel as ge, AlertDialogAction as ve, Badge as V, Label as be, Dialog as j, DialogContent as E, DialogHeader as O, DialogTitle as G, Input as P, Button as w, Card as M, CardContent as T, DialogDescription as $ } from "@open-kingdom/shared-frontend-ui-primitives";
8
+ import { useForm as B } from "react-hook-form";
9
+ import { zodResolver as q } from "@hookform/resolvers/zod";
10
+ import { z as C } from "zod";
11
+ import { formatDate as z } from "@open-kingdom/shared-poly-util-date";
12
+ import { useDeepCompareEffect as H } from "@react-hookz/web";
13
+ function Q({
13
14
  isOpen: t,
14
- onClose: a,
15
- ariaLabelledBy: r,
16
- children: s
15
+ title: n,
16
+ message: s,
17
+ confirmLabel: m = "Confirm",
18
+ onConfirm: f,
19
+ onCancel: b,
20
+ isLoading: d = !1
17
21
  }) {
18
- Q("Escape", () => {
19
- t && a();
20
- });
21
- const n = P((l) => {
22
- l?.focus();
23
- }, []);
24
- return t ? /* @__PURE__ */ e(
25
- "div",
22
+ return /* @__PURE__ */ e(
23
+ ce,
26
24
  {
27
- className: "fixed inset-0 z-50 flex items-center justify-center bg-black/50",
28
- onClick: a,
29
- children: /* @__PURE__ */ e(
30
- "div",
31
- {
32
- ref: n,
33
- className: "w-full max-w-md rounded-lg bg-white p-6 shadow-xl dark:bg-neutral-800",
34
- role: "dialog",
35
- "aria-modal": "true",
36
- "aria-labelledby": r,
37
- tabIndex: -1,
38
- onClick: (l) => l.stopPropagation(),
39
- children: s
40
- }
41
- )
25
+ open: t,
26
+ onOpenChange: (l) => !l && b(),
27
+ children: /* @__PURE__ */ i(ue, { children: [
28
+ /* @__PURE__ */ i(me, { children: [
29
+ /* @__PURE__ */ e(fe, { children: n }),
30
+ /* @__PURE__ */ e(he, { children: s })
31
+ ] }),
32
+ /* @__PURE__ */ i(pe, { children: [
33
+ /* @__PURE__ */ e(
34
+ ge,
35
+ {
36
+ "data-testid": "confirm-cancel-btn",
37
+ disabled: d,
38
+ onClick: b,
39
+ children: "Cancel"
40
+ }
41
+ ),
42
+ /* @__PURE__ */ e(
43
+ ve,
44
+ {
45
+ "data-testid": "confirm-btn",
46
+ disabled: d,
47
+ onClick: f,
48
+ className: "bg-destructive text-destructive-foreground hover:bg-destructive/90",
49
+ children: m
50
+ }
51
+ )
52
+ ] })
53
+ ] })
42
54
  }
43
- ) : null;
44
- }
45
- const b = "w-full px-3 py-2 border border-neutral-300 dark:border-neutral-600 rounded-md bg-white dark:bg-neutral-700 text-neutral-900 dark:text-neutral-100", J = "block text-sm font-medium text-neutral-700 dark:text-neutral-300 mb-1", k = "max-w-md mx-auto mt-8 p-6 bg-white dark:bg-neutral-800 rounded-lg shadow-lg", S = "text-neutral-600 dark:text-neutral-400", C = "rounded-md px-4 py-2 text-sm font-medium transition-colors", D = `${C} bg-primary-500 text-white hover:bg-primary-600 disabled:opacity-50`, M = `${C} border border-neutral-300 text-neutral-700 hover:bg-neutral-50 dark:border-neutral-600 dark:text-neutral-300 dark:hover:bg-neutral-700`, K = `${C} bg-error-500 text-white hover:bg-error-600 disabled:opacity-50`;
46
- function H({
47
- isOpen: t,
48
- title: a,
49
- message: r,
50
- confirmLabel: s = "Confirm",
51
- onConfirm: n,
52
- onCancel: l,
53
- isLoading: o = !1
54
- }) {
55
- const d = "confirm-dialog-title";
56
- return /* @__PURE__ */ i(j, { isOpen: t, onClose: l, ariaLabelledBy: d, children: [
57
- /* @__PURE__ */ e(
58
- "h3",
59
- {
60
- id: d,
61
- className: "text-lg font-semibold text-neutral-900 dark:text-neutral-100",
62
- children: a
63
- }
64
- ),
65
- /* @__PURE__ */ e("p", { className: "mt-2 text-sm text-neutral-600 dark:text-neutral-400", children: r }),
66
- /* @__PURE__ */ i("div", { className: "mt-6 flex justify-end gap-3", children: [
67
- /* @__PURE__ */ e(
68
- "button",
69
- {
70
- type: "button",
71
- "data-testid": "confirm-cancel-btn",
72
- onClick: l,
73
- disabled: o,
74
- className: M,
75
- children: "Cancel"
76
- }
77
- ),
78
- /* @__PURE__ */ e(
79
- "button",
80
- {
81
- type: "button",
82
- "data-testid": "confirm-btn",
83
- onClick: n,
84
- disabled: o,
85
- className: K,
86
- children: s
87
- }
88
- )
89
- ] })
90
- ] });
55
+ );
91
56
  }
92
- const W = {
93
- admin: "bg-purple-100 text-purple-800 dark:bg-purple-900/30 dark:text-purple-300",
94
- user: "bg-primary-100 text-primary-800 dark:bg-primary-900/30 dark:text-primary-300",
95
- guest: "bg-neutral-100 text-neutral-700 dark:bg-neutral-700 dark:text-neutral-300"
57
+ const xe = {
58
+ admin: "secondary",
59
+ user: "default",
60
+ guest: "muted"
96
61
  };
97
- function X({ role: t }) {
62
+ function Y({ role: t }) {
98
63
  if (!t) return null;
99
- const a = t.charAt(0).toUpperCase() + t.slice(1);
100
- return /* @__PURE__ */ e(
101
- "span",
102
- {
103
- className: `inline-block rounded-full px-2.5 py-0.5 text-xs font-medium ${W[t]}`,
104
- children: a
105
- }
106
- );
64
+ const n = t.charAt(0).toUpperCase() + t.slice(1);
65
+ return /* @__PURE__ */ e(V, { variant: xe[t] ?? "default", children: n });
107
66
  }
108
- function x({
67
+ function D({
109
68
  label: t,
110
- htmlFor: a,
111
- required: r,
112
- error: s,
113
- children: n
69
+ htmlFor: n,
70
+ required: s,
71
+ error: m,
72
+ children: f
114
73
  }) {
115
74
  return /* @__PURE__ */ i("div", { children: [
116
- /* @__PURE__ */ i("label", { htmlFor: a, className: J, children: [
75
+ /* @__PURE__ */ i(be, { htmlFor: n, className: "mb-1", children: [
117
76
  t,
118
77
  " ",
119
- r && /* @__PURE__ */ e("span", { className: "text-error-500", children: "*" })
78
+ s && /* @__PURE__ */ e("span", { className: "text-destructive", children: "*" })
120
79
  ] }),
121
- n,
122
- s && /* @__PURE__ */ e(
123
- "p",
124
- {
125
- "data-testid": "field-error",
126
- className: "mt-1 text-sm text-error-600 dark:text-error-400",
127
- children: s
128
- }
129
- )
80
+ f,
81
+ m && /* @__PURE__ */ e("p", { "data-testid": "field-error", className: "mt-1 text-sm text-destructive", children: m })
130
82
  ] });
131
83
  }
132
- const Z = p.object({
133
- email: p.string().email("Invalid email address"),
134
- role: p.enum(["guest", "user", "admin"])
84
+ const ye = C.object({
85
+ email: C.string().email("Invalid email address"),
86
+ role: C.string().min(1)
135
87
  });
136
- function _({ isOpen: t, onClose: a }) {
137
- const r = U(), [s, { isLoading: n }] = B(), {
88
+ function Ce({ isOpen: t, onClose: n }) {
89
+ const s = I(), [m, { isLoading: f }] = X(), { data: b } = F(), d = b ?? [], {
138
90
  register: l,
139
- handleSubmit: o,
140
- reset: d,
141
- formState: { errors: c }
142
- } = A({
143
- resolver: L(Z),
91
+ handleSubmit: r,
92
+ reset: c,
93
+ formState: { errors: g }
94
+ } = B({
95
+ resolver: q(ye),
144
96
  defaultValues: { email: "", role: "guest" }
145
- }), h = "invite-modal-title", f = () => {
146
- d(), a();
97
+ }), a = () => {
98
+ c(), n();
147
99
  };
148
- return /* @__PURE__ */ i(
100
+ return /* @__PURE__ */ e(
149
101
  j,
150
102
  {
151
- isOpen: t,
152
- onClose: f,
153
- ariaLabelledBy: h,
154
- children: [
155
- /* @__PURE__ */ e(
156
- "h3",
157
- {
158
- id: h,
159
- className: "text-lg font-semibold text-neutral-900 dark:text-neutral-100",
160
- children: "Invite User"
161
- }
162
- ),
163
- /* @__PURE__ */ i("form", { onSubmit: o(async (m) => {
103
+ open: t,
104
+ onOpenChange: (u) => !u && a(),
105
+ children: /* @__PURE__ */ i(E, { children: [
106
+ /* @__PURE__ */ e(O, { children: /* @__PURE__ */ e(G, { children: "Invite User" }) }),
107
+ /* @__PURE__ */ i("form", { onSubmit: r(async (u) => {
164
108
  try {
165
- await s({ inviteUserDto: m }).unwrap(), r(F("Invitation sent successfully")), d(), a();
109
+ await m({ inviteUserDto: u }).unwrap(), s(A("Invitation sent successfully")), c(), n();
166
110
  } catch {
167
111
  }
168
- }), className: "mt-4 space-y-4", children: [
112
+ }), className: "space-y-4", children: [
169
113
  /* @__PURE__ */ e(
170
- x,
114
+ D,
171
115
  {
172
116
  label: "Email",
173
117
  htmlFor: "invite-email",
174
118
  required: !0,
175
- error: c.email?.message,
119
+ error: g.email?.message,
176
120
  children: /* @__PURE__ */ e(
177
- "input",
121
+ P,
178
122
  {
179
123
  id: "invite-email",
180
124
  "data-testid": "invite-email-input",
181
125
  type: "email",
182
126
  placeholder: "user@example.com",
183
- className: b,
184
127
  ...l("email")
185
128
  }
186
129
  )
187
130
  }
188
131
  ),
189
- /* @__PURE__ */ e(x, { label: "Role", htmlFor: "invite-role", children: /* @__PURE__ */ i(
132
+ /* @__PURE__ */ e(D, { label: "Role", htmlFor: "invite-role", children: /* @__PURE__ */ e(
190
133
  "select",
191
134
  {
192
135
  id: "invite-role",
193
136
  "data-testid": "invite-role-select",
194
- className: b,
137
+ className: "flex h-10 w-full rounded-md border border-input bg-background px-3 py-2 text-sm ring-offset-background focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2",
195
138
  ...l("role"),
196
- children: [
197
- /* @__PURE__ */ e("option", { value: "guest", children: "Guest" }),
198
- /* @__PURE__ */ e("option", { value: "user", children: "User" }),
199
- /* @__PURE__ */ e("option", { value: "admin", children: "Admin" })
200
- ]
139
+ children: d.map((u) => /* @__PURE__ */ e("option", { value: u.name, children: u.name.charAt(0).toUpperCase() + u.name.slice(1) }, u.id))
201
140
  }
202
141
  ) }),
203
142
  /* @__PURE__ */ i("div", { className: "flex justify-end gap-3", children: [
204
143
  /* @__PURE__ */ e(
205
- "button",
144
+ w,
206
145
  {
207
146
  type: "button",
147
+ variant: "secondary",
208
148
  "data-testid": "invite-cancel-btn",
209
- onClick: f,
210
- disabled: n,
211
- className: M,
149
+ onClick: a,
150
+ disabled: f,
212
151
  children: "Cancel"
213
152
  }
214
153
  ),
215
154
  /* @__PURE__ */ e(
216
- "button",
155
+ w,
217
156
  {
218
157
  type: "submit",
219
158
  "data-testid": "invite-submit-btn",
220
- disabled: n,
221
- className: D,
222
- children: n ? "Sending..." : "Send Invitation"
159
+ disabled: f,
160
+ children: f ? "Sending..." : "Send Invitation"
223
161
  }
224
162
  )
225
163
  ] })
226
164
  ] })
227
- ]
165
+ ] })
228
166
  }
229
167
  );
230
168
  }
231
- const ee = {
232
- error: "text-xl font-bold text-error-700 dark:text-error-400",
233
- success: "text-xl font-bold text-success-700 dark:text-success-400"
169
+ const Ne = {
170
+ error: "text-xl font-bold text-destructive",
171
+ success: "text-xl font-bold text-success"
234
172
  };
235
- function w({
173
+ function k({
236
174
  variant: t,
237
- title: a,
238
- message: r,
239
- children: s
175
+ title: n,
176
+ message: s,
177
+ children: m
240
178
  }) {
241
- return t === "loading" ? /* @__PURE__ */ e("div", { className: k, children: /* @__PURE__ */ e("p", { className: `text-center ${S}`, children: r }) }) : /* @__PURE__ */ i(
242
- "div",
179
+ return t === "loading" ? /* @__PURE__ */ e(M, { className: "max-w-md mx-auto mt-8", children: /* @__PURE__ */ e(T, { className: "pt-6", children: /* @__PURE__ */ e("p", { className: "text-center text-muted-foreground", children: s }) }) }) : /* @__PURE__ */ e(
180
+ M,
243
181
  {
244
- className: k,
182
+ className: "max-w-md mx-auto mt-8",
245
183
  role: t === "error" ? "alert" : void 0,
246
- children: [
247
- a && /* @__PURE__ */ e("h2", { "data-testid": "status-card-title", className: ee[t], children: a }),
248
- /* @__PURE__ */ e("p", { "data-testid": "status-card-message", className: `mt-2 ${S}`, children: r }),
249
- s
250
- ]
184
+ children: /* @__PURE__ */ i(T, { className: "pt-6", children: [
185
+ n && /* @__PURE__ */ e(
186
+ "h2",
187
+ {
188
+ "data-testid": "status-card-title",
189
+ className: Ne[t],
190
+ children: n
191
+ }
192
+ ),
193
+ /* @__PURE__ */ e(
194
+ "p",
195
+ {
196
+ "data-testid": "status-card-message",
197
+ className: "mt-2 text-muted-foreground",
198
+ children: s
199
+ }
200
+ ),
201
+ m
202
+ ] })
251
203
  }
252
204
  );
253
205
  }
254
- const te = p.object({
255
- firstName: p.string().optional(),
256
- lastName: p.string().optional(),
257
- password: p.string().min(8, "Password must be at least 8 characters"),
258
- confirmPassword: p.string()
206
+ const we = C.object({
207
+ firstName: C.string().optional(),
208
+ lastName: C.string().optional(),
209
+ password: C.string().min(8, "Password must be at least 8 characters"),
210
+ confirmPassword: C.string()
259
211
  }).refine((t) => t.password === t.confirmPassword, {
260
212
  message: "Passwords do not match",
261
213
  path: ["confirmPassword"]
262
214
  });
263
- function pe({ token: t, loginPath: a }) {
215
+ function Ye({ token: t, loginPath: n }) {
264
216
  const {
265
- data: r,
266
- isLoading: s,
267
- error: n
268
- } = E({ token: t }), [l, { isLoading: o, isSuccess: d }] = G(), {
269
- register: c,
270
- handleSubmit: h,
271
- formState: { errors: f }
272
- } = A({
273
- resolver: L(te),
217
+ data: s,
218
+ isLoading: m,
219
+ error: f
220
+ } = Z({ token: t }), [b, { isLoading: d, isSuccess: l }] = _(), {
221
+ register: r,
222
+ handleSubmit: c,
223
+ formState: { errors: g }
224
+ } = B({
225
+ resolver: q(we),
274
226
  defaultValues: {
275
227
  firstName: "",
276
228
  lastName: "",
277
229
  password: "",
278
230
  confirmPassword: ""
279
231
  }
280
- }), g = r?.email ?? "", m = r?.role ?? "user", v = async (y) => {
232
+ }), a = s?.email ?? "", v = async (u) => {
281
233
  try {
282
- await l({
234
+ await b({
283
235
  acceptInvitationDto: {
284
236
  token: t,
285
- password: y.password,
286
- firstName: y.firstName || void 0,
287
- lastName: y.lastName || void 0
237
+ password: u.password,
238
+ firstName: u.firstName || void 0,
239
+ lastName: u.lastName || void 0
288
240
  }
289
241
  }).unwrap();
290
242
  } catch {
291
243
  }
292
244
  };
293
- return d ? /* @__PURE__ */ e(
294
- w,
245
+ return l ? /* @__PURE__ */ e(
246
+ k,
295
247
  {
296
248
  variant: "success",
297
249
  title: "Account Created",
298
250
  message: "Your account has been created successfully. You can now log in with your email and password.",
299
- children: a && /* @__PURE__ */ e(
251
+ children: n && /* @__PURE__ */ e(
300
252
  "a",
301
253
  {
302
- href: a,
254
+ href: n,
303
255
  "data-testid": "accept-login-link",
304
- className: "mt-4 inline-block text-sm font-medium text-primary-600 hover:underline dark:text-primary-400",
256
+ className: "mt-4 inline-block text-sm font-medium text-primary hover:underline",
305
257
  children: "Go to login"
306
258
  }
307
259
  )
308
260
  }
309
- ) : s ? /* @__PURE__ */ e(w, { variant: "loading", message: "Validating invitation..." }) : n ? /* @__PURE__ */ e(
310
- w,
261
+ ) : m ? /* @__PURE__ */ e(k, { variant: "loading", message: "Validating invitation..." }) : f ? /* @__PURE__ */ e(
262
+ k,
311
263
  {
312
264
  variant: "error",
313
265
  title: "Validation Failed",
314
266
  message: "Unable to validate this invitation. Please check your connection and try again."
315
267
  }
316
- ) : r?.valid ? /* @__PURE__ */ i("div", { className: k, children: [
268
+ ) : s?.valid ? /* @__PURE__ */ e(M, { className: "max-w-md mx-auto mt-8", children: /* @__PURE__ */ i(T, { className: "pt-6", children: [
317
269
  /* @__PURE__ */ e(
318
270
  "h2",
319
271
  {
320
272
  "data-testid": "accept-heading",
321
- className: "text-xl font-bold text-neutral-900 dark:text-neutral-100",
273
+ className: "text-xl font-bold text-foreground",
322
274
  children: "Accept Invitation"
323
275
  }
324
276
  ),
325
- /* @__PURE__ */ i("p", { className: `mt-1 text-sm ${S}`, children: [
326
- "You've been invited as ",
327
- /* @__PURE__ */ e("strong", { "data-testid": "accept-role", children: m }),
277
+ /* @__PURE__ */ i("p", { className: "mt-1 text-sm text-muted-foreground", children: [
278
+ "You've been invited with email",
328
279
  " ",
329
- "with email ",
330
- /* @__PURE__ */ e("strong", { "data-testid": "accept-email", children: g })
280
+ /* @__PURE__ */ e("strong", { "data-testid": "accept-email", children: a })
331
281
  ] }),
332
- /* @__PURE__ */ i("form", { onSubmit: h(v), className: "mt-4 space-y-4", children: [
333
- /* @__PURE__ */ e(x, { label: "First Name", htmlFor: "accept-firstName", children: /* @__PURE__ */ e(
334
- "input",
282
+ /* @__PURE__ */ i("form", { onSubmit: c(v), className: "mt-4 space-y-4", children: [
283
+ /* @__PURE__ */ e(D, { label: "First Name", htmlFor: "accept-firstName", children: /* @__PURE__ */ e(
284
+ P,
335
285
  {
336
286
  id: "accept-firstName",
337
287
  "data-testid": "accept-first-name-input",
338
288
  type: "text",
339
289
  placeholder: "John",
340
- className: b,
341
- ...c("firstName")
290
+ ...r("firstName")
342
291
  }
343
292
  ) }),
344
- /* @__PURE__ */ e(x, { label: "Last Name", htmlFor: "accept-lastName", children: /* @__PURE__ */ e(
345
- "input",
293
+ /* @__PURE__ */ e(D, { label: "Last Name", htmlFor: "accept-lastName", children: /* @__PURE__ */ e(
294
+ P,
346
295
  {
347
296
  id: "accept-lastName",
348
297
  "data-testid": "accept-last-name-input",
349
298
  type: "text",
350
299
  placeholder: "Doe",
351
- className: b,
352
- ...c("lastName")
300
+ ...r("lastName")
353
301
  }
354
302
  ) }),
355
303
  /* @__PURE__ */ e(
356
- x,
304
+ D,
357
305
  {
358
306
  label: "Password",
359
307
  htmlFor: "accept-password",
360
308
  required: !0,
361
- error: f.password?.message,
309
+ error: g.password?.message,
362
310
  children: /* @__PURE__ */ e(
363
- "input",
311
+ P,
364
312
  {
365
313
  id: "accept-password",
366
314
  "data-testid": "accept-password-input",
367
315
  type: "password",
368
316
  placeholder: "Min. 8 characters",
369
- className: b,
370
- ...c("password")
317
+ ...r("password")
371
318
  }
372
319
  )
373
320
  }
374
321
  ),
375
322
  /* @__PURE__ */ e(
376
- x,
323
+ D,
377
324
  {
378
325
  label: "Confirm Password",
379
326
  htmlFor: "accept-confirmPassword",
380
327
  required: !0,
381
- error: f.confirmPassword?.message,
328
+ error: g.confirmPassword?.message,
382
329
  children: /* @__PURE__ */ e(
383
- "input",
330
+ P,
384
331
  {
385
332
  id: "accept-confirmPassword",
386
333
  "data-testid": "accept-confirm-password-input",
387
334
  type: "password",
388
335
  placeholder: "Repeat password",
389
- className: b,
390
- ...c("confirmPassword")
336
+ ...r("confirmPassword")
391
337
  }
392
338
  )
393
339
  }
394
340
  ),
395
341
  /* @__PURE__ */ e(
396
- "button",
342
+ w,
397
343
  {
398
344
  type: "submit",
399
345
  "data-testid": "accept-submit-btn",
400
- disabled: o,
401
- className: `w-full ${D}`,
402
- children: o ? "Creating account..." : "Create Account"
346
+ disabled: d,
347
+ className: "w-full",
348
+ children: d ? "Creating account..." : "Create Account"
403
349
  }
404
350
  )
405
351
  ] })
406
- ] }) : /* @__PURE__ */ e(
407
- w,
352
+ ] }) }) : /* @__PURE__ */ e(
353
+ k,
408
354
  {
409
355
  variant: "error",
410
356
  title: "Invalid Invitation",
@@ -412,69 +358,284 @@ function pe({ token: t, loginPath: a }) {
412
358
  }
413
359
  );
414
360
  }
415
- function he({ currentUserId: t }) {
416
- const a = U(), { theme: r, mode: s } = O(), { data: n, isLoading: l, error: o, refetch: d } = q(), [c, { isLoading: h }] = z(), [f, g] = I(!1), [m, v] = I(null), y = n ?? [], $ = P(async () => {
417
- if (m)
361
+ const De = {
362
+ pending: "warning",
363
+ accepted: "success",
364
+ expired: "muted"
365
+ };
366
+ function Se({ status: t }) {
367
+ if (!t) return null;
368
+ const n = t.charAt(0).toUpperCase() + t.slice(1);
369
+ return /* @__PURE__ */ e(V, { variant: De[t], children: n });
370
+ }
371
+ const Re = {
372
+ field: "email",
373
+ headerName: "Email",
374
+ flex: 2
375
+ };
376
+ function Ie(t) {
377
+ return {
378
+ headerName: "Role",
379
+ flex: 1,
380
+ cellRenderer: (n) => n.data ? /* @__PURE__ */ e(Y, { role: t.get(n.data.roleId) ?? null }) : null
381
+ };
382
+ }
383
+ const Ae = {
384
+ field: "status",
385
+ headerName: "Status",
386
+ flex: 1,
387
+ cellRenderer: (t) => t.data ? /* @__PURE__ */ e(Se, { status: t.data.status }) : null
388
+ }, Pe = {
389
+ headerName: "Invited",
390
+ flex: 1,
391
+ valueGetter: (t) => z(t.data?.invitedAt)
392
+ }, ke = {
393
+ headerName: "Expires",
394
+ flex: 1,
395
+ valueGetter: (t) => z(t.data?.tokenExpiry)
396
+ };
397
+ function Le(t) {
398
+ return {
399
+ headerName: "Actions",
400
+ flex: 1,
401
+ sortable: !1,
402
+ filter: !1,
403
+ cellRenderer: (n) => n.data ? /* @__PURE__ */ e(
404
+ "button",
405
+ {
406
+ onClick: () => n.data && t(n.data),
407
+ className: "text-destructive text-xs font-medium rounded px-2 py-1 transition-colors hover:bg-destructive/10 disabled:cursor-not-allowed disabled:opacity-40",
408
+ children: "Cancel"
409
+ }
410
+ ) : null
411
+ };
412
+ }
413
+ function Fe(t, n) {
414
+ return [
415
+ Re,
416
+ Ie(n),
417
+ Ae,
418
+ Pe,
419
+ ke,
420
+ Le(t)
421
+ ];
422
+ }
423
+ function Je() {
424
+ const t = I(), { data: n, isLoading: s, error: m, refetch: f } = ee(), [b, { isLoading: d }] = te(), { data: l } = F(), [r, c] = N(null), g = n ?? [], a = R(() => {
425
+ const y = l ?? [];
426
+ return new Map(y.map((o) => [o.id, o.name]));
427
+ }, [l]);
428
+ async function v() {
429
+ if (r)
430
+ try {
431
+ await b({ id: r.id }).unwrap(), t(A("Invitation cancelled successfully"));
432
+ } catch {
433
+ } finally {
434
+ c(null);
435
+ }
436
+ }
437
+ const u = R(
438
+ () => Fe(c, a),
439
+ [a]
440
+ );
441
+ return m ? /* @__PURE__ */ i(
442
+ "div",
443
+ {
444
+ "data-testid": "invitations-error",
445
+ className: "rounded-lg bg-destructive/10 p-6 text-center",
446
+ role: "alert",
447
+ children: [
448
+ /* @__PURE__ */ e("p", { className: "text-destructive", children: "Failed to load invitations." }),
449
+ /* @__PURE__ */ e(
450
+ "button",
451
+ {
452
+ "data-testid": "invitations-retry-btn",
453
+ onClick: f,
454
+ className: "mt-2 text-sm font-medium text-primary hover:underline",
455
+ children: "Try again"
456
+ }
457
+ )
458
+ ]
459
+ }
460
+ ) : /* @__PURE__ */ i("div", { children: [
461
+ /* @__PURE__ */ e("div", { className: "mb-4 flex items-center justify-between", children: /* @__PURE__ */ e(
462
+ "h2",
463
+ {
464
+ "data-testid": "invitations-heading",
465
+ className: "text-xl font-semibold text-foreground",
466
+ children: "Invitations"
467
+ }
468
+ ) }),
469
+ /* @__PURE__ */ e(
470
+ U,
471
+ {
472
+ rowData: g,
473
+ columnDefs: u,
474
+ loading: s
475
+ }
476
+ ),
477
+ /* @__PURE__ */ e(
478
+ Q,
479
+ {
480
+ isOpen: !!r,
481
+ title: "Cancel Invitation",
482
+ message: `Are you sure you want to cancel the invitation for ${r?.email}? This action cannot be undone.`,
483
+ confirmLabel: d ? "Cancelling..." : "Cancel Invitation",
484
+ onConfirm: v,
485
+ onCancel: () => c(null),
486
+ isLoading: d
487
+ }
488
+ )
489
+ ] });
490
+ }
491
+ function Me({
492
+ isOpen: t,
493
+ onClose: n,
494
+ user: s
495
+ }) {
496
+ const m = I(), { data: f } = F(), [b, { isLoading: d }] = ne(), l = R(
497
+ () => f ?? [],
498
+ [f]
499
+ ), [r, c] = N(null);
500
+ H(() => {
501
+ if (!l.length) return;
502
+ const a = l.find((v) => v.name === s.role);
503
+ c(a?.id ?? null);
504
+ }, [l, s.role]);
505
+ const g = async (a) => {
506
+ if (a.preventDefault(), r !== null)
507
+ try {
508
+ await b({
509
+ userId: s.id,
510
+ updateUserRolesDto: { roleIds: [r] }
511
+ }).unwrap(), m(A("Role updated successfully")), n();
512
+ } catch {
513
+ }
514
+ };
515
+ return /* @__PURE__ */ e(j, { open: t, onOpenChange: (a) => !a && n(), children: /* @__PURE__ */ i(E, { children: [
516
+ /* @__PURE__ */ i(O, { children: [
517
+ /* @__PURE__ */ e(G, { children: "Change Role" }),
518
+ /* @__PURE__ */ i($, { children: [
519
+ "Changing role for ",
520
+ /* @__PURE__ */ e("strong", { children: s.email })
521
+ ] })
522
+ ] }),
523
+ /* @__PURE__ */ i("form", { onSubmit: g, className: "space-y-4", children: [
524
+ /* @__PURE__ */ e(D, { label: "Role", htmlFor: "role-select", children: /* @__PURE__ */ e(
525
+ "select",
526
+ {
527
+ id: "role-select",
528
+ "data-testid": "role-select",
529
+ className: "flex h-10 w-full rounded-md border border-input bg-background px-3 py-2 text-sm ring-offset-background focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2",
530
+ value: r ?? "",
531
+ onChange: (a) => c(Number(a.target.value)),
532
+ children: l.map((a) => /* @__PURE__ */ e("option", { value: a.id, children: a.name.charAt(0).toUpperCase() + a.name.slice(1) }, a.id))
533
+ }
534
+ ) }),
535
+ /* @__PURE__ */ i("div", { className: "flex justify-end gap-3", children: [
536
+ /* @__PURE__ */ e(
537
+ w,
538
+ {
539
+ type: "button",
540
+ variant: "secondary",
541
+ "data-testid": "role-change-cancel-btn",
542
+ onClick: n,
543
+ disabled: d,
544
+ children: "Cancel"
545
+ }
546
+ ),
547
+ /* @__PURE__ */ e(
548
+ w,
549
+ {
550
+ type: "submit",
551
+ "data-testid": "role-change-submit-btn",
552
+ disabled: d || r === null,
553
+ children: d ? "Saving..." : "Save"
554
+ }
555
+ )
556
+ ] })
557
+ ] })
558
+ ] }) });
559
+ }
560
+ function L(t, n) {
561
+ const { data: s } = ie();
562
+ return s?.permissions ? s.permissions.includes(`${t}:${n}`) : !1;
563
+ }
564
+ function Ke({ currentUserId: t }) {
565
+ const n = I(), { data: s, isLoading: m, error: f, refetch: b } = ae(), [d, { isLoading: l }] = se(), r = L("users", "delete"), c = L("roles", "update"), [g, a] = N(!1), [v, u] = N(null), [y, o] = N(null), p = s ?? [], h = W(async () => {
566
+ if (v)
418
567
  try {
419
- await c({ id: m.id }).unwrap(), a(F("User deleted successfully"));
568
+ await d({ id: v.id }).unwrap(), n(A("User deleted successfully"));
420
569
  } catch {
421
570
  } finally {
422
- v(null);
571
+ u(null);
423
572
  }
424
- }, [m, c, a]), R = V(
573
+ }, [v, d, n]), J = R(
425
574
  () => [
426
575
  { field: "email", headerName: "Email", flex: 2 },
427
576
  {
428
577
  headerName: "Name",
429
578
  flex: 2,
430
- valueGetter: (u) => {
431
- const { firstName: N, lastName: T } = u.data ?? {};
432
- return [N, T].filter(Boolean).join(" ") || "—";
579
+ valueGetter: (x) => {
580
+ const { firstName: S, lastName: K } = x.data ?? {};
581
+ return [S, K].filter(Boolean).join(" ") || "—";
433
582
  }
434
583
  },
435
584
  {
436
585
  field: "role",
437
586
  headerName: "Role",
438
587
  flex: 1,
439
- cellRenderer: (u) => u.data ? /* @__PURE__ */ e(X, { role: u.data.role }) : null
588
+ cellRenderer: (x) => x.data ? /* @__PURE__ */ e(Y, { role: x.data.role }) : null
440
589
  },
441
590
  {
442
591
  headerName: "Actions",
443
592
  flex: 1,
444
593
  sortable: !1,
445
594
  filter: !1,
446
- cellRenderer: (u) => {
447
- if (!u.data) return null;
448
- const N = u.data.id === t;
449
- return /* @__PURE__ */ e(
450
- "button",
451
- {
452
- onClick: () => u.data && v(u.data),
453
- disabled: N,
454
- title: N ? "Cannot delete your own account" : "Delete user",
455
- className: "rounded px-2 py-1 text-xs font-medium text-error-600 transition-colors hover:bg-error-50 disabled:cursor-not-allowed disabled:opacity-40 dark:text-error-400 dark:hover:bg-error-900/20",
456
- children: "Delete"
457
- }
458
- );
595
+ cellRenderer: (x) => {
596
+ if (!x.data) return null;
597
+ const S = x.data.id === t;
598
+ return /* @__PURE__ */ i("div", { className: "flex gap-1", children: [
599
+ c && /* @__PURE__ */ e(
600
+ "button",
601
+ {
602
+ onClick: () => x.data && o(x.data),
603
+ disabled: S,
604
+ title: S ? "Cannot change your own role" : "Change user role",
605
+ className: "rounded px-2 py-1 text-xs font-medium text-primary transition-colors hover:bg-primary/10 disabled:cursor-not-allowed disabled:opacity-40",
606
+ children: "Change Role"
607
+ }
608
+ ),
609
+ r && /* @__PURE__ */ e(
610
+ "button",
611
+ {
612
+ onClick: () => x.data && u(x.data),
613
+ disabled: S,
614
+ title: S ? "Cannot delete your own account" : "Delete user",
615
+ className: "rounded px-2 py-1 text-xs font-medium text-destructive transition-colors hover:bg-destructive/10 disabled:cursor-not-allowed disabled:opacity-40",
616
+ children: "Delete"
617
+ }
618
+ )
619
+ ] });
459
620
  }
460
621
  }
461
622
  ],
462
- [t]
623
+ [t, c, r]
463
624
  );
464
- return o ? /* @__PURE__ */ i(
625
+ return f ? /* @__PURE__ */ i(
465
626
  "div",
466
627
  {
467
628
  "data-testid": "users-error",
468
- className: "rounded-lg bg-error-50 p-6 text-center dark:bg-error-900/20",
629
+ className: "rounded-lg bg-destructive/10 p-6 text-center",
469
630
  role: "alert",
470
631
  children: [
471
- /* @__PURE__ */ e("p", { className: "text-error-700 dark:text-error-300", children: "Failed to load users." }),
632
+ /* @__PURE__ */ e("p", { className: "text-destructive", children: "Failed to load users." }),
472
633
  /* @__PURE__ */ e(
473
634
  "button",
474
635
  {
475
636
  "data-testid": "users-retry-btn",
476
- onClick: d,
477
- className: "mt-2 text-sm font-medium text-primary-600 hover:underline dark:text-primary-400",
637
+ onClick: b,
638
+ className: "mt-2 text-sm font-medium text-primary hover:underline",
478
639
  children: "Try again"
479
640
  }
480
641
  )
@@ -486,53 +647,258 @@ function he({ currentUserId: t }) {
486
647
  "h2",
487
648
  {
488
649
  "data-testid": "users-heading",
489
- className: "text-xl font-semibold text-neutral-900 dark:text-neutral-100",
650
+ className: "text-xl font-semibold text-foreground",
490
651
  children: "Users"
491
652
  }
492
653
  ),
493
654
  /* @__PURE__ */ e(
494
- "button",
655
+ w,
495
656
  {
496
657
  "data-testid": "invite-user-btn",
497
- onClick: () => g(!0),
498
- className: D,
658
+ onClick: () => a(!0),
499
659
  children: "Invite User"
500
660
  }
501
661
  )
502
662
  ] }),
663
+ /* @__PURE__ */ e(U, { rowData: p, columnDefs: J, loading: m }),
503
664
  /* @__PURE__ */ e(
504
- Y,
665
+ Ce,
505
666
  {
506
- rowData: y,
507
- columnDefs: R,
508
- mode: s,
509
- theme: r,
510
- loading: l
667
+ isOpen: g,
668
+ onClose: () => a(!1)
511
669
  }
512
670
  ),
513
671
  /* @__PURE__ */ e(
514
- _,
672
+ Me,
515
673
  {
516
- isOpen: f,
517
- onClose: () => g(!1)
674
+ isOpen: !!y,
675
+ onClose: () => o(null),
676
+ user: {
677
+ id: y?.id ?? 0,
678
+ email: y?.email ?? "",
679
+ role: y?.role ?? ""
680
+ }
518
681
  }
519
682
  ),
520
683
  /* @__PURE__ */ e(
521
- H,
684
+ Q,
522
685
  {
523
- isOpen: !!m,
686
+ isOpen: !!v,
524
687
  title: "Delete User",
525
- message: `Are you sure you want to delete ${m?.email}? This action cannot be undone.`,
526
- confirmLabel: h ? "Deleting..." : "Delete",
527
- onConfirm: $,
528
- onCancel: () => v(null),
529
- isLoading: h
688
+ message: `Are you sure you want to delete ${v?.email}? This action cannot be undone.`,
689
+ confirmLabel: l ? "Deleting..." : "Delete",
690
+ onConfirm: h,
691
+ onCancel: () => u(null),
692
+ isLoading: l
693
+ }
694
+ )
695
+ ] });
696
+ }
697
+ function Te({
698
+ isOpen: t,
699
+ onClose: n,
700
+ role: s
701
+ }) {
702
+ const m = I(), { data: f } = re(), { data: b } = le(
703
+ { id: s.id },
704
+ { skip: !t }
705
+ ), [d, { isLoading: l }] = oe(), [r, c] = N(/* @__PURE__ */ new Set()), g = f ?? [], a = b ?? [];
706
+ H(() => {
707
+ c(new Set(a.map((o) => o.id)));
708
+ }, [a]);
709
+ const v = R(() => {
710
+ const o = /* @__PURE__ */ new Map();
711
+ for (const p of g) {
712
+ const h = o.get(p.resource) ?? [];
713
+ h.push(p), o.set(p.resource, h);
714
+ }
715
+ return o;
716
+ }, [g]);
717
+ function u(o) {
718
+ c((p) => {
719
+ const h = new Set(p);
720
+ return h.has(o) ? h.delete(o) : h.add(o), h;
721
+ });
722
+ }
723
+ async function y(o) {
724
+ o.preventDefault();
725
+ try {
726
+ await d({
727
+ id: s.id,
728
+ setRolePermissionsDto: { permissionIds: [...r] }
729
+ }).unwrap(), m(A("Permissions updated successfully")), n();
730
+ } catch {
731
+ }
732
+ }
733
+ return /* @__PURE__ */ e(j, { open: t, onOpenChange: (o) => !o && n(), children: /* @__PURE__ */ i(E, { children: [
734
+ /* @__PURE__ */ i(O, { children: [
735
+ /* @__PURE__ */ e(G, { children: "Manage Permissions" }),
736
+ /* @__PURE__ */ i($, { children: [
737
+ "Editing permissions for ",
738
+ /* @__PURE__ */ e("strong", { children: s.name })
739
+ ] })
740
+ ] }),
741
+ /* @__PURE__ */ i("form", { onSubmit: y, className: "mt-4", children: [
742
+ /* @__PURE__ */ e("div", { className: "max-h-64 space-y-4 overflow-y-auto", children: [...v.entries()].map(([o, p]) => /* @__PURE__ */ i("fieldset", { children: [
743
+ /* @__PURE__ */ e("legend", { className: "text-sm font-medium capitalize text-foreground", children: o }),
744
+ /* @__PURE__ */ e("div", { className: "mt-1 space-y-1", children: p.map((h) => /* @__PURE__ */ i(
745
+ "label",
746
+ {
747
+ className: "flex items-center gap-2 text-sm text-muted-foreground",
748
+ children: [
749
+ /* @__PURE__ */ e(
750
+ "input",
751
+ {
752
+ type: "checkbox",
753
+ checked: r.has(h.id),
754
+ onChange: () => u(h.id),
755
+ className: "rounded border-input text-primary focus:ring-ring"
756
+ }
757
+ ),
758
+ /* @__PURE__ */ e("span", { children: h.action }),
759
+ h.description && /* @__PURE__ */ i("span", { className: "text-xs text-muted-foreground/70", children: [
760
+ "— ",
761
+ h.description
762
+ ] })
763
+ ]
764
+ },
765
+ h.id
766
+ )) })
767
+ ] }, o)) }),
768
+ /* @__PURE__ */ i("div", { className: "mt-4 flex justify-end gap-3", children: [
769
+ /* @__PURE__ */ e(
770
+ w,
771
+ {
772
+ type: "button",
773
+ variant: "secondary",
774
+ "data-testid": "permissions-cancel-btn",
775
+ onClick: n,
776
+ disabled: l,
777
+ children: "Cancel"
778
+ }
779
+ ),
780
+ /* @__PURE__ */ e(
781
+ w,
782
+ {
783
+ type: "submit",
784
+ "data-testid": "permissions-save-btn",
785
+ disabled: l,
786
+ children: l ? "Saving..." : "Save"
787
+ }
788
+ )
789
+ ] })
790
+ ] })
791
+ ] }) });
792
+ }
793
+ function We() {
794
+ const t = I(), { data: n, isLoading: s, error: m, refetch: f } = F(), [b, { isLoading: d }] = de(), l = L("roles", "delete"), r = L("roles", "update"), [c, g] = N(null), [a, v] = N(null), u = n ?? [];
795
+ async function y() {
796
+ if (c)
797
+ try {
798
+ await b({ id: c.id }).unwrap(), t(A("Role deleted successfully"));
799
+ } catch {
800
+ } finally {
801
+ g(null);
802
+ }
803
+ }
804
+ const o = R(
805
+ () => [
806
+ { field: "name", headerName: "Name", flex: 1 },
807
+ { field: "description", headerName: "Description", flex: 2 },
808
+ {
809
+ headerName: "Type",
810
+ flex: 1,
811
+ valueGetter: (p) => p.data?.isSystem ? "System" : "Custom"
812
+ },
813
+ {
814
+ headerName: "Actions",
815
+ flex: 1,
816
+ sortable: !1,
817
+ filter: !1,
818
+ cellRenderer: (p) => {
819
+ if (!p.data) return null;
820
+ const h = !!p.data.isSystem;
821
+ return /* @__PURE__ */ i("div", { className: "flex gap-1", children: [
822
+ r && /* @__PURE__ */ e(
823
+ "button",
824
+ {
825
+ onClick: () => p.data && v(p.data),
826
+ className: "rounded px-2 py-1 text-xs font-medium text-primary transition-colors hover:bg-primary/10",
827
+ children: "Permissions"
828
+ }
829
+ ),
830
+ l && /* @__PURE__ */ e(
831
+ "button",
832
+ {
833
+ onClick: () => p.data && g(p.data),
834
+ disabled: h,
835
+ title: h ? "Cannot delete a system role" : "Delete role",
836
+ className: "rounded px-2 py-1 text-xs font-medium text-destructive transition-colors hover:bg-destructive/10 disabled:cursor-not-allowed disabled:opacity-40",
837
+ children: "Delete"
838
+ }
839
+ )
840
+ ] });
841
+ }
842
+ }
843
+ ],
844
+ [r, l]
845
+ );
846
+ return m ? /* @__PURE__ */ i(
847
+ "div",
848
+ {
849
+ "data-testid": "roles-error",
850
+ className: "rounded-lg bg-destructive/10 p-6 text-center",
851
+ role: "alert",
852
+ children: [
853
+ /* @__PURE__ */ e("p", { className: "text-destructive", children: "Failed to load roles." }),
854
+ /* @__PURE__ */ e(
855
+ "button",
856
+ {
857
+ "data-testid": "roles-retry-btn",
858
+ onClick: f,
859
+ className: "mt-2 text-sm font-medium text-primary hover:underline",
860
+ children: "Try again"
861
+ }
862
+ )
863
+ ]
864
+ }
865
+ ) : /* @__PURE__ */ i("div", { children: [
866
+ /* @__PURE__ */ e("div", { className: "mb-4 flex items-center justify-between", children: /* @__PURE__ */ e(
867
+ "h2",
868
+ {
869
+ "data-testid": "roles-heading",
870
+ className: "text-xl font-semibold text-foreground",
871
+ children: "Roles & Permissions"
872
+ }
873
+ ) }),
874
+ /* @__PURE__ */ e(U, { rowData: u, columnDefs: o, loading: s }),
875
+ /* @__PURE__ */ e(
876
+ Te,
877
+ {
878
+ isOpen: !!a,
879
+ onClose: () => v(null),
880
+ role: a ? { id: a.id, name: a.name } : { id: 0, name: "" }
881
+ }
882
+ ),
883
+ /* @__PURE__ */ e(
884
+ Q,
885
+ {
886
+ isOpen: !!c,
887
+ title: "Delete Role",
888
+ message: `Are you sure you want to delete the "${c?.name}" role? This action cannot be undone.`,
889
+ confirmLabel: d ? "Deleting..." : "Delete",
890
+ onConfirm: y,
891
+ onCancel: () => g(null),
892
+ isLoading: d
530
893
  }
531
894
  )
532
895
  ] });
533
896
  }
534
897
  export {
535
- pe as AcceptInvitation,
536
- w as StatusCard,
537
- he as UserList
898
+ Ye as AcceptInvitation,
899
+ Je as InvitationList,
900
+ We as RoleList,
901
+ k as StatusCard,
902
+ Ke as UserList,
903
+ L as useHasPermission
538
904
  };