@mesob/auth-react 0.3.4 → 0.4.0

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 (142) hide show
  1. package/dist/components/auth/auth-layout.d.ts +1 -1
  2. package/dist/components/auth/auth-layout.js +10 -2
  3. package/dist/components/auth/auth-layout.js.map +1 -1
  4. package/dist/components/auth/countdown.js +8 -6
  5. package/dist/components/auth/countdown.js.map +1 -1
  6. package/dist/components/auth/forgot-password.js +21 -19
  7. package/dist/components/auth/forgot-password.js.map +1 -1
  8. package/dist/components/auth/reset-password-form.js +22 -21
  9. package/dist/components/auth/reset-password-form.js.map +1 -1
  10. package/dist/components/auth/set-password.d.ts +9 -0
  11. package/dist/components/auth/set-password.js +527 -0
  12. package/dist/components/auth/set-password.js.map +1 -0
  13. package/dist/components/auth/sign-in.js +45 -26
  14. package/dist/components/auth/sign-in.js.map +1 -1
  15. package/dist/components/auth/sign-up.js +25 -29
  16. package/dist/components/auth/sign-up.js.map +1 -1
  17. package/dist/components/auth/verification-form.js +24 -27
  18. package/dist/components/auth/verification-form.js.map +1 -1
  19. package/dist/components/auth/verify-email.js +40 -31
  20. package/dist/components/auth/verify-email.js.map +1 -1
  21. package/dist/components/auth/verify-phone.js +40 -31
  22. package/dist/components/auth/verify-phone.js.map +1 -1
  23. package/dist/components/authorization/deny.d.ts +11 -0
  24. package/dist/components/authorization/deny.js +52 -0
  25. package/dist/components/authorization/deny.js.map +1 -0
  26. package/dist/components/authorization/grant.d.ts +12 -0
  27. package/dist/components/authorization/grant.js +57 -0
  28. package/dist/components/authorization/grant.js.map +1 -0
  29. package/dist/components/iam/permission-selector.d.ts +19 -0
  30. package/dist/components/iam/permission-selector.js +122 -0
  31. package/dist/components/iam/permission-selector.js.map +1 -0
  32. package/dist/components/iam/permissions.js +21 -33
  33. package/dist/components/iam/permissions.js.map +1 -1
  34. package/dist/components/iam/role-detail-layout.d.ts +11 -0
  35. package/dist/components/iam/role-detail-layout.js +137 -0
  36. package/dist/components/iam/role-detail-layout.js.map +1 -0
  37. package/dist/components/iam/role-detail-page.d.ts +9 -0
  38. package/dist/components/iam/role-detail-page.js +229 -0
  39. package/dist/components/iam/role-detail-page.js.map +1 -0
  40. package/dist/components/iam/role-permissions-page.d.ts +8 -0
  41. package/dist/components/iam/role-permissions-page.js +397 -0
  42. package/dist/components/iam/role-permissions-page.js.map +1 -0
  43. package/dist/components/iam/roles.js +20 -10
  44. package/dist/components/iam/roles.js.map +1 -1
  45. package/dist/components/iam/tenants.js +9 -2
  46. package/dist/components/iam/tenants.js.map +1 -1
  47. package/dist/components/iam/users.js +10 -9
  48. package/dist/components/iam/users.js.map +1 -1
  49. package/dist/components/profile/account.js +110 -19
  50. package/dist/components/profile/account.js.map +1 -1
  51. package/dist/components/profile/change-email-form.js +26 -29
  52. package/dist/components/profile/change-email-form.js.map +1 -1
  53. package/dist/components/profile/change-phone-form.js +26 -29
  54. package/dist/components/profile/change-phone-form.js.map +1 -1
  55. package/dist/components/profile/change-profile.d.ts +2 -1
  56. package/dist/components/profile/change-profile.js +16 -8
  57. package/dist/components/profile/change-profile.js.map +1 -1
  58. package/dist/components/profile/otp-verification-modal.js +24 -27
  59. package/dist/components/profile/otp-verification-modal.js.map +1 -1
  60. package/dist/components/profile/security.js +88 -57
  61. package/dist/components/profile/security.js.map +1 -1
  62. package/dist/components/profile/verify-change-email-form.js +24 -27
  63. package/dist/components/profile/verify-change-email-form.js.map +1 -1
  64. package/dist/components/profile/verify-change-phone-form.js +24 -27
  65. package/dist/components/profile/verify-change-phone-form.js.map +1 -1
  66. package/dist/index.d.ts +9 -1
  67. package/dist/index.js +1897 -821
  68. package/dist/index.js.map +1 -1
  69. package/dist/pages/auth/forgot-password.d.ts +7 -0
  70. package/dist/pages/auth/forgot-password.js +784 -0
  71. package/dist/pages/auth/forgot-password.js.map +1 -0
  72. package/dist/pages/auth/layout.d.ts +8 -0
  73. package/dist/pages/auth/layout.js +562 -0
  74. package/dist/pages/auth/layout.js.map +1 -0
  75. package/dist/pages/auth/reset-password.d.ts +10 -0
  76. package/dist/pages/auth/reset-password.js +913 -0
  77. package/dist/pages/auth/reset-password.js.map +1 -0
  78. package/dist/pages/auth/set-password.d.ts +10 -0
  79. package/dist/pages/auth/set-password.js +946 -0
  80. package/dist/pages/auth/set-password.js.map +1 -0
  81. package/dist/pages/auth/sign-in.d.ts +10 -0
  82. package/dist/pages/auth/sign-in.js +984 -0
  83. package/dist/pages/auth/sign-in.js.map +1 -0
  84. package/dist/pages/auth/sign-up.d.ts +10 -0
  85. package/dist/pages/auth/sign-up.js +940 -0
  86. package/dist/pages/auth/sign-up.js.map +1 -0
  87. package/dist/pages/auth/verify-email.d.ts +10 -0
  88. package/dist/pages/auth/verify-email.js +950 -0
  89. package/dist/pages/auth/verify-email.js.map +1 -0
  90. package/dist/pages/auth/verify-phone.d.ts +10 -0
  91. package/dist/pages/auth/verify-phone.js +964 -0
  92. package/dist/pages/auth/verify-phone.js.map +1 -0
  93. package/dist/pages/iam/permissions.d.ts +5 -0
  94. package/dist/pages/iam/permissions.js +308 -0
  95. package/dist/pages/iam/permissions.js.map +1 -0
  96. package/dist/pages/iam/role-detail-layout.d.ts +12 -0
  97. package/dist/pages/iam/role-detail-layout.js +145 -0
  98. package/dist/pages/iam/role-detail-layout.js.map +1 -0
  99. package/dist/pages/iam/role-detail.d.ts +12 -0
  100. package/dist/pages/iam/role-detail.js +241 -0
  101. package/dist/pages/iam/role-detail.js.map +1 -0
  102. package/dist/pages/iam/role-permissions.d.ts +12 -0
  103. package/dist/pages/iam/role-permissions.js +409 -0
  104. package/dist/pages/iam/role-permissions.js.map +1 -0
  105. package/dist/pages/iam/role-users.d.ts +12 -0
  106. package/dist/pages/iam/role-users.js +825 -0
  107. package/dist/pages/iam/role-users.js.map +1 -0
  108. package/dist/pages/iam/roles.d.ts +5 -0
  109. package/dist/pages/iam/roles.js +684 -0
  110. package/dist/pages/iam/roles.js.map +1 -0
  111. package/dist/pages/iam/sessions.d.ts +5 -0
  112. package/dist/pages/iam/sessions.js +315 -0
  113. package/dist/pages/iam/sessions.js.map +1 -0
  114. package/dist/pages/iam/tenant-detail.d.ts +10 -0
  115. package/dist/pages/iam/tenant-detail.js +186 -0
  116. package/dist/pages/iam/tenant-detail.js.map +1 -0
  117. package/dist/pages/iam/tenants.d.ts +5 -0
  118. package/dist/pages/iam/tenants.js +610 -0
  119. package/dist/pages/iam/tenants.js.map +1 -0
  120. package/dist/pages/iam/user-activity.d.ts +10 -0
  121. package/dist/pages/iam/user-activity.js +850 -0
  122. package/dist/pages/iam/user-activity.js.map +1 -0
  123. package/dist/pages/iam/user-detail-layout.d.ts +12 -0
  124. package/dist/pages/iam/user-detail-layout.js +106 -0
  125. package/dist/pages/iam/user-detail-layout.js.map +1 -0
  126. package/dist/pages/iam/user-detail.d.ts +10 -0
  127. package/dist/pages/iam/user-detail.js +102 -0
  128. package/dist/pages/iam/user-detail.js.map +1 -0
  129. package/dist/pages/iam/users.d.ts +5 -0
  130. package/dist/pages/iam/users.js +1275 -0
  131. package/dist/pages/iam/users.js.map +1 -0
  132. package/dist/pages/profile/account.d.ts +5 -0
  133. package/dist/pages/profile/account.js +182 -0
  134. package/dist/pages/profile/account.js.map +1 -0
  135. package/dist/pages/profile/layout.d.ts +8 -0
  136. package/dist/pages/profile/layout.js +133 -0
  137. package/dist/pages/profile/layout.js.map +1 -0
  138. package/dist/pages/profile/security.d.ts +5 -0
  139. package/dist/pages/profile/security.js +1539 -0
  140. package/dist/pages/profile/security.js.map +1 -0
  141. package/dist/{types-vcfvnAzQ.d.ts → types-g9QcNRxT.d.ts} +13 -7
  142. package/package.json +102 -3
@@ -0,0 +1,684 @@
1
+ "use client";
2
+
3
+ // src/pages/iam/roles.tsx
4
+ import {
5
+ Button as Button2,
6
+ EntityDrawerTrigger,
7
+ EntityFilter,
8
+ EntityHeader,
9
+ EntitySearch,
10
+ EntitySort,
11
+ EntityViewToggle,
12
+ PageBody,
13
+ PageContainer,
14
+ useBreadcrumbs,
15
+ useEntityPagination,
16
+ useEntityParams
17
+ } from "@mesob/ui/components";
18
+ import { IconShield as IconShield2 } from "@tabler/icons-react";
19
+ import { useQueryClient as useQueryClient2 } from "@tanstack/react-query";
20
+ import { useState as useState3 } from "react";
21
+ import { toast } from "sonner";
22
+
23
+ // src/lib/query-options.ts
24
+ import { keepPreviousData } from "@tanstack/react-query";
25
+ var defaultEntityQueryOptions = {
26
+ refetchOnMount: false,
27
+ refetchOnWindowFocus: false,
28
+ refetchOnReconnect: false,
29
+ staleTime: 60 * 1e3,
30
+ gcTime: 5 * 60 * 1e3,
31
+ placeholderData: keepPreviousData,
32
+ retry: 1
33
+ };
34
+
35
+ // src/provider.tsx
36
+ import { QueryClient, QueryClientProvider } from "@tanstack/react-query";
37
+ import { deepmerge } from "deepmerge-ts";
38
+ import createFetchClient from "openapi-fetch";
39
+ import createClient from "openapi-react-query";
40
+ import { createContext, useContext, useMemo, useState } from "react";
41
+
42
+ // src/utils/cookie.ts
43
+ var isProduction = typeof process !== "undefined" && process.env.NODE_ENV === "production";
44
+
45
+ // src/provider.tsx
46
+ import { jsx } from "react/jsx-runtime";
47
+ var SessionContext = createContext(null);
48
+ var ApiContext = createContext(null);
49
+ var ConfigContext = createContext(null);
50
+ var queryClient = new QueryClient({
51
+ defaultOptions: {
52
+ queries: {
53
+ refetchOnWindowFocus: false
54
+ }
55
+ }
56
+ });
57
+ function useApi() {
58
+ const context = useContext(ApiContext);
59
+ if (!context) {
60
+ throw new Error("useApi must be used within MesobAuthProvider");
61
+ }
62
+ return context;
63
+ }
64
+ function useConfig() {
65
+ const context = useContext(ConfigContext);
66
+ if (!context) {
67
+ throw new Error("useConfig must be used within MesobAuthProvider");
68
+ }
69
+ return context;
70
+ }
71
+
72
+ // src/pages/iam/roles/_components/role-form.tsx
73
+ import { zodResolver } from "@hookform/resolvers/zod";
74
+ import {
75
+ EntityDrawer,
76
+ EntityFormActions,
77
+ Form,
78
+ FormControl,
79
+ FormField,
80
+ FormItem,
81
+ FormLabel,
82
+ FormMessage,
83
+ Input,
84
+ LocaleInputText,
85
+ LocaleInputTextarea,
86
+ Skeleton
87
+ } from "@mesob/ui/components";
88
+ import { useLocaleConfig, useLocaleSchemas } from "@mesob/ui/providers";
89
+ import { useQueryClient } from "@tanstack/react-query";
90
+ import { useEffect, useMemo as useMemo2 } from "react";
91
+ import { useForm, useWatch } from "react-hook-form";
92
+ import { z } from "zod";
93
+
94
+ // src/pages/iam/shared/navigation.tsx
95
+ import { useMesob } from "@mesob/ui/providers";
96
+ import { jsx as jsx2 } from "react/jsx-runtime";
97
+ function AppLink({ href, children, ...props }) {
98
+ const mesob = useMesob();
99
+ const Link2 = mesob?.linkComponent ?? mesob?.navigation?.Link;
100
+ const locale = mesob?.locale;
101
+ if (Link2) {
102
+ return /* @__PURE__ */ jsx2(Link2, { href, ...locale ? { locale } : {}, ...props, children });
103
+ }
104
+ return /* @__PURE__ */ jsx2("a", { href, ...props, children });
105
+ }
106
+ function useNavigate() {
107
+ const { config } = useConfig();
108
+ return (href) => {
109
+ if (config.navigation?.onNavigate) {
110
+ config.navigation.onNavigate(href);
111
+ return;
112
+ }
113
+ if (typeof window !== "undefined") {
114
+ window.location.href = href;
115
+ }
116
+ };
117
+ }
118
+
119
+ // src/pages/iam/shared/page-helpers.tsx
120
+ import { jsx as jsx3 } from "react/jsx-runtime";
121
+ var authApi$ = {
122
+ useQuery(...args) {
123
+ const { hooks } = useApi();
124
+ return hooks.useQuery(...args);
125
+ },
126
+ useMutation(...args) {
127
+ const { hooks } = useApi();
128
+ return hooks.useMutation(...args);
129
+ }
130
+ };
131
+ function Link(props) {
132
+ return /* @__PURE__ */ jsx3(AppLink, { ...props });
133
+ }
134
+ function useRouter() {
135
+ const navigate = useNavigate();
136
+ return {
137
+ push: navigate
138
+ };
139
+ }
140
+
141
+ // src/pages/iam/roles/_components/role-form.tsx
142
+ import { jsx as jsx4, jsxs } from "react/jsx-runtime";
143
+ function RoleForm({
144
+ mode,
145
+ roleId,
146
+ open,
147
+ onClose,
148
+ onSuccess
149
+ }) {
150
+ const qc = useQueryClient();
151
+ const { localeInputDefault, requiredSchema, optionalSchema } = useLocaleSchemas();
152
+ const { defaultLanguage } = useLocaleConfig();
153
+ const schema = useMemo2(
154
+ () => z.object({
155
+ name: requiredSchema,
156
+ code: z.string().min(1, "Code is required"),
157
+ description: optionalSchema,
158
+ permissionIds: z.array(z.string())
159
+ }),
160
+ [requiredSchema, optionalSchema]
161
+ );
162
+ const defaults = useMemo2(
163
+ () => ({
164
+ name: { ...localeInputDefault },
165
+ code: "",
166
+ description: { ...localeInputDefault },
167
+ permissionIds: []
168
+ }),
169
+ [localeInputDefault]
170
+ );
171
+ const { data, isLoading } = authApi$.useQuery(
172
+ "get",
173
+ "/roles/{id}",
174
+ { params: { path: { id: roleId ?? "" } } },
175
+ { enabled: mode === "edit" && !!roleId }
176
+ );
177
+ const create = authApi$.useMutation("post", "/roles", {
178
+ onSuccess: () => qc.invalidateQueries({ queryKey: ["get", "/roles"] })
179
+ });
180
+ const update = authApi$.useMutation("put", "/roles/{id}", {
181
+ onSuccess: () => {
182
+ qc.invalidateQueries({ queryKey: ["get", "/roles"] });
183
+ if (roleId) {
184
+ qc.invalidateQueries({ queryKey: ["get", "/roles/{id}"] });
185
+ }
186
+ }
187
+ });
188
+ const del = authApi$.useMutation("delete", "/roles/{id}", {
189
+ onSuccess: () => qc.invalidateQueries({ queryKey: ["get", "/roles"] })
190
+ });
191
+ const form = useForm({
192
+ resolver: zodResolver(schema),
193
+ defaultValues: defaults
194
+ });
195
+ const { reset, formState, control, register, setValue } = form;
196
+ const nameVal = useWatch({ control, name: "name" });
197
+ const codeVal = useWatch({ control, name: "code" });
198
+ useEffect(() => {
199
+ const nameDefault = typeof nameVal === "object" && nameVal !== null && defaultLanguage in nameVal ? nameVal[defaultLanguage] ?? "" : "";
200
+ if (mode === "new" && typeof nameDefault === "string" && nameDefault.trim().length > 0 && !codeVal) {
201
+ const code = nameDefault.toLowerCase().replace(/\s+/g, "-").replace(/[^a-z0-9-]/g, "");
202
+ if (code) {
203
+ setValue("code", code);
204
+ }
205
+ }
206
+ }, [mode, nameVal, setValue, codeVal, defaultLanguage]);
207
+ useEffect(() => {
208
+ if (!open) {
209
+ return;
210
+ }
211
+ if (mode === "edit" && data?.role && !isLoading) {
212
+ const r = data.role;
213
+ reset({
214
+ name: r.name ?? {},
215
+ code: r.code,
216
+ description: r.description ?? {},
217
+ permissionIds: r.permissionIds ?? []
218
+ });
219
+ } else {
220
+ reset({ ...defaults, permissionIds: [] });
221
+ }
222
+ }, [mode, data, open, isLoading, reset, defaults]);
223
+ const onSubmit = form.handleSubmit(async (d) => {
224
+ const body = {
225
+ name: d.name,
226
+ code: d.code,
227
+ description: d.description ?? void 0,
228
+ permissionIds: d.permissionIds
229
+ };
230
+ if (mode === "new") {
231
+ await create.mutateAsync({ body });
232
+ } else if (roleId) {
233
+ await update.mutateAsync({
234
+ params: { path: { id: roleId } },
235
+ body
236
+ });
237
+ }
238
+ onSuccess?.();
239
+ onClose();
240
+ });
241
+ const onDelete = async () => {
242
+ if (!roleId) {
243
+ return;
244
+ }
245
+ await del.mutateAsync({ params: { path: { id: roleId } } });
246
+ onSuccess?.();
247
+ onClose();
248
+ };
249
+ const editable = mode === "new" || data?.role?.isEditable !== false;
250
+ return /* @__PURE__ */ jsx4(
251
+ EntityDrawer,
252
+ {
253
+ title: mode === "new" ? "New role" : "Edit role",
254
+ open,
255
+ onClose,
256
+ isDirty: formState.isDirty,
257
+ size: "md",
258
+ form: isLoading ? /* @__PURE__ */ jsx4(FormSkeleton, {}) : /* @__PURE__ */ jsx4(Form, { ...form, children: /* @__PURE__ */ jsxs("form", { onSubmit, className: "space-y-4", children: [
259
+ /* @__PURE__ */ jsx4(
260
+ LocaleInputText,
261
+ {
262
+ label: "Name",
263
+ field: "name",
264
+ required: true,
265
+ register,
266
+ errors: formState.errors,
267
+ placeholder: "e.g. Administrator",
268
+ disabled: !editable
269
+ }
270
+ ),
271
+ /* @__PURE__ */ jsx4(
272
+ FormField,
273
+ {
274
+ control,
275
+ name: "code",
276
+ render: ({ field }) => /* @__PURE__ */ jsxs(FormItem, { children: [
277
+ /* @__PURE__ */ jsxs(FormLabel, { children: [
278
+ "Code ",
279
+ /* @__PURE__ */ jsx4("span", { className: "text-destructive", children: "*" })
280
+ ] }),
281
+ /* @__PURE__ */ jsx4(FormControl, { children: /* @__PURE__ */ jsx4(
282
+ Input,
283
+ {
284
+ placeholder: "e.g. admin",
285
+ disabled: !editable,
286
+ ...field
287
+ }
288
+ ) }),
289
+ /* @__PURE__ */ jsx4(FormMessage, {})
290
+ ] })
291
+ }
292
+ ),
293
+ /* @__PURE__ */ jsx4(
294
+ LocaleInputTextarea,
295
+ {
296
+ label: "Description",
297
+ field: "description",
298
+ register,
299
+ errors: formState.errors,
300
+ placeholder: "Description",
301
+ rows: 3,
302
+ disabled: !editable
303
+ }
304
+ )
305
+ ] }) }),
306
+ actions: /* @__PURE__ */ jsx4(
307
+ EntityFormActions,
308
+ {
309
+ mode,
310
+ onSubmit,
311
+ onReset: mode === "new" ? () => reset(defaults) : void 0,
312
+ onDelete: mode === "edit" ? onDelete : void 0,
313
+ isSubmitting: create.isPending || update.isPending,
314
+ isDeleting: del.isPending,
315
+ disabled: isLoading || !editable,
316
+ itemName: "role"
317
+ }
318
+ )
319
+ }
320
+ );
321
+ }
322
+ function FormSkeleton() {
323
+ return /* @__PURE__ */ jsxs("div", { className: "space-y-4", children: [
324
+ /* @__PURE__ */ jsxs("div", { className: "space-y-2", children: [
325
+ /* @__PURE__ */ jsx4(Skeleton, { className: "h-4 w-16" }),
326
+ /* @__PURE__ */ jsx4(Skeleton, { className: "h-10 w-full" })
327
+ ] }),
328
+ /* @__PURE__ */ jsxs("div", { className: "space-y-2", children: [
329
+ /* @__PURE__ */ jsx4(Skeleton, { className: "h-4 w-14" }),
330
+ /* @__PURE__ */ jsx4(Skeleton, { className: "h-10 w-full" })
331
+ ] }),
332
+ /* @__PURE__ */ jsxs("div", { className: "space-y-2", children: [
333
+ /* @__PURE__ */ jsx4(Skeleton, { className: "h-4 w-24" }),
334
+ /* @__PURE__ */ jsx4(Skeleton, { className: "h-20 w-full" })
335
+ ] })
336
+ ] });
337
+ }
338
+
339
+ // src/pages/iam/roles/_components/roles-list.tsx
340
+ import {
341
+ Badge as Badge2,
342
+ DataTableAction,
343
+ DataTablePagination,
344
+ DisplayTable,
345
+ EntityEmptyState,
346
+ EntityLoadingState,
347
+ Tbody,
348
+ Td,
349
+ Th,
350
+ Thead,
351
+ Tr
352
+ } from "@mesob/ui/components";
353
+ import { IconCalendar, IconShield } from "@tabler/icons-react";
354
+ import { useState as useState2 } from "react";
355
+
356
+ // src/pages/iam/roles/_components/role-card.tsx
357
+ import {
358
+ Badge,
359
+ Button,
360
+ Card,
361
+ CardContent,
362
+ CardHeader,
363
+ DropdownMenu,
364
+ DropdownMenuContent,
365
+ DropdownMenuItem,
366
+ DropdownMenuPortal,
367
+ DropdownMenuTrigger
368
+ } from "@mesob/ui/components";
369
+ import { IconDots, IconExternalLink } from "@tabler/icons-react";
370
+
371
+ // src/pages/iam/roles/_components/roles-data.ts
372
+ function str(v) {
373
+ if (v == null) {
374
+ return "";
375
+ }
376
+ if (typeof v === "string") {
377
+ return v;
378
+ }
379
+ if (typeof v === "object" && v !== null && "en" in v) {
380
+ const o = v;
381
+ return o.en ?? o.am ?? "";
382
+ }
383
+ return String(v);
384
+ }
385
+
386
+ // src/pages/iam/roles/_components/role-card.tsx
387
+ import { jsx as jsx5, jsxs as jsxs2 } from "react/jsx-runtime";
388
+ function RoleCard({ role, mode = "navigate" }) {
389
+ const router = useRouter();
390
+ const canNavigate = mode === "navigate";
391
+ return /* @__PURE__ */ jsxs2(Card, { className: "group transition-shadow hover:shadow-md", children: [
392
+ /* @__PURE__ */ jsxs2(CardHeader, { className: "pb-2", children: [
393
+ /* @__PURE__ */ jsxs2("div", { className: "flex items-start justify-between", children: [
394
+ /* @__PURE__ */ jsx5(
395
+ "button",
396
+ {
397
+ type: "button",
398
+ onClick: () => {
399
+ if (canNavigate) {
400
+ router.push(`/iam/roles/${role.id}`);
401
+ }
402
+ },
403
+ className: "text-left font-semibold hover:text-primary hover:underline",
404
+ children: str(role.name) || role.code
405
+ }
406
+ ),
407
+ canNavigate ? /* @__PURE__ */ jsxs2(DropdownMenu, { children: [
408
+ /* @__PURE__ */ jsx5(
409
+ DropdownMenuTrigger,
410
+ {
411
+ render: /* @__PURE__ */ jsx5(
412
+ Button,
413
+ {
414
+ variant: "ghost",
415
+ size: "icon",
416
+ className: "h-8 w-8 opacity-0 transition-opacity group-hover:opacity-100"
417
+ }
418
+ ),
419
+ children: /* @__PURE__ */ jsx5(IconDots, { className: "h-4 w-4" })
420
+ }
421
+ ),
422
+ /* @__PURE__ */ jsx5(DropdownMenuPortal, { children: /* @__PURE__ */ jsx5(DropdownMenuContent, { children: /* @__PURE__ */ jsxs2(
423
+ DropdownMenuItem,
424
+ {
425
+ onClick: () => router.push(`/iam/roles/${role.id}`),
426
+ children: [
427
+ /* @__PURE__ */ jsx5(IconExternalLink, { className: "mr-2 h-4 w-4" }),
428
+ "Open"
429
+ ]
430
+ }
431
+ ) }) })
432
+ ] }) : null
433
+ ] }),
434
+ /* @__PURE__ */ jsx5(Badge, { variant: "outline", className: "text-xs", children: role.code })
435
+ ] }),
436
+ /* @__PURE__ */ jsxs2(CardContent, { children: [
437
+ /* @__PURE__ */ jsxs2("div", { className: "mb-3 flex flex-wrap gap-2", children: [
438
+ role.isSystem ? /* @__PURE__ */ jsx5(Badge, { children: "System" }) : null,
439
+ /* @__PURE__ */ jsxs2(Badge, { variant: "secondary", children: [
440
+ role.permissionCount ?? role.permissionIds?.length ?? 0,
441
+ " ",
442
+ "permissions"
443
+ ] }),
444
+ /* @__PURE__ */ jsxs2(Badge, { variant: "outline", children: [
445
+ role.userCount ?? 0,
446
+ " users"
447
+ ] })
448
+ ] }),
449
+ str(role.description) && /* @__PURE__ */ jsx5("p", { className: "line-clamp-2 text-sm text-muted-foreground", children: str(role.description) }),
450
+ /* @__PURE__ */ jsxs2("p", { className: "mt-2 text-xs text-muted-foreground", children: [
451
+ "Created ",
452
+ new Date(role.createdAt).toLocaleDateString()
453
+ ] })
454
+ ] })
455
+ ] });
456
+ }
457
+
458
+ // src/pages/iam/roles/_components/roles-list.tsx
459
+ import { jsx as jsx6, jsxs as jsxs3 } from "react/jsx-runtime";
460
+ var TABLE_COLUMN_COUNT = 5;
461
+ function RolesList({
462
+ data,
463
+ isLoading,
464
+ view,
465
+ pageIndex,
466
+ pageSize,
467
+ pageCount,
468
+ totalRows,
469
+ onPageChange,
470
+ onPageSizeChange,
471
+ onCreateNew
472
+ }) {
473
+ const [drawerRole, setDrawerRole] = useState2(null);
474
+ if (isLoading) {
475
+ return /* @__PURE__ */ jsx6(
476
+ EntityLoadingState,
477
+ {
478
+ view,
479
+ rowCount: pageSize,
480
+ columnCount: TABLE_COLUMN_COUNT,
481
+ cardCount: pageSize
482
+ }
483
+ );
484
+ }
485
+ if (totalRows === 0) {
486
+ return /* @__PURE__ */ jsx6(
487
+ EntityEmptyState,
488
+ {
489
+ icon: IconShield,
490
+ entityName: "role",
491
+ title: "No roles yet",
492
+ description: "Create your first role to get started.",
493
+ onAction: onCreateNew
494
+ }
495
+ );
496
+ }
497
+ if (view === "table") {
498
+ return /* @__PURE__ */ jsxs3("div", { className: "space-y-4", children: [
499
+ drawerRole ? /* @__PURE__ */ jsx6(
500
+ RoleForm,
501
+ {
502
+ mode: "edit",
503
+ roleId: drawerRole.id,
504
+ open: true,
505
+ onClose: () => setDrawerRole(null),
506
+ onSuccess: () => setDrawerRole(null)
507
+ }
508
+ ) : null,
509
+ /* @__PURE__ */ jsxs3(DisplayTable, { withTableBorder: true, children: [
510
+ /* @__PURE__ */ jsx6(Thead, { children: /* @__PURE__ */ jsxs3(Tr, { children: [
511
+ /* @__PURE__ */ jsx6(Th, { children: "Role" }),
512
+ /* @__PURE__ */ jsx6(Th, { children: "Description" }),
513
+ /* @__PURE__ */ jsx6(Th, { children: "Access" }),
514
+ /* @__PURE__ */ jsx6(Th, { children: "Created" }),
515
+ /* @__PURE__ */ jsx6(Th, { className: "w-[50px]" })
516
+ ] }) }),
517
+ /* @__PURE__ */ jsx6(Tbody, { children: data.map((role) => /* @__PURE__ */ jsxs3(Tr, { className: "group", children: [
518
+ /* @__PURE__ */ jsx6(Td, { children: /* @__PURE__ */ jsxs3(
519
+ Link,
520
+ {
521
+ href: `/iam/roles/${role.id}`,
522
+ className: "block text-left font-medium hover:text-primary hover:underline cursor-pointer",
523
+ children: [
524
+ /* @__PURE__ */ jsx6("p", { children: str(role.name) || role.code }),
525
+ /* @__PURE__ */ jsx6(Badge2, { variant: "outline", className: "mt-1 text-xs", children: role.code })
526
+ ]
527
+ }
528
+ ) }),
529
+ /* @__PURE__ */ jsx6(Td, { children: /* @__PURE__ */ jsx6("span", { className: "text-muted-foreground line-clamp-1 max-w-[200px]", children: str(role.description) || "\u2014" }) }),
530
+ /* @__PURE__ */ jsx6(Td, { children: /* @__PURE__ */ jsxs3("div", { className: "flex flex-wrap gap-2", children: [
531
+ role.isSystem ? /* @__PURE__ */ jsx6(Badge2, { children: "System" }) : null,
532
+ /* @__PURE__ */ jsxs3(Badge2, { variant: "secondary", children: [
533
+ role.permissionCount ?? role.permissionIds?.length ?? 0,
534
+ " ",
535
+ "permissions"
536
+ ] })
537
+ ] }) }),
538
+ /* @__PURE__ */ jsx6(Td, { children: /* @__PURE__ */ jsxs3("div", { className: "flex items-center gap-1 text-muted-foreground", children: [
539
+ /* @__PURE__ */ jsx6(IconCalendar, { className: "h-4 w-4" }),
540
+ new Date(role.createdAt).toLocaleDateString()
541
+ ] }) }),
542
+ /* @__PURE__ */ jsx6(Td, { children: /* @__PURE__ */ jsx6(DataTableAction, { onClick: () => setDrawerRole(role) }) })
543
+ ] }, role.id)) })
544
+ ] }),
545
+ /* @__PURE__ */ jsx6(
546
+ DataTablePagination,
547
+ {
548
+ pageIndex,
549
+ pageSize,
550
+ pageCount,
551
+ totalRows,
552
+ onPageChange,
553
+ onPageSizeChange
554
+ }
555
+ )
556
+ ] });
557
+ }
558
+ return /* @__PURE__ */ jsxs3("div", { className: "space-y-4", children: [
559
+ /* @__PURE__ */ jsx6("div", { className: "grid grid-cols-1 gap-4 md:grid-cols-2 lg:grid-cols-3 xl:grid-cols-4", children: data.map((r) => /* @__PURE__ */ jsx6(RoleCard, { role: r }, r.id)) }),
560
+ /* @__PURE__ */ jsx6(
561
+ DataTablePagination,
562
+ {
563
+ pageIndex,
564
+ pageSize,
565
+ pageCount,
566
+ totalRows,
567
+ onPageChange,
568
+ onPageSizeChange
569
+ }
570
+ )
571
+ ] });
572
+ }
573
+
574
+ // src/pages/iam/roles.tsx
575
+ import { jsx as jsx7, jsxs as jsxs4 } from "react/jsx-runtime";
576
+ function RolesPage() {
577
+ const { hooks } = useApi();
578
+ const qc = useQueryClient2();
579
+ useBreadcrumbs({
580
+ items: [
581
+ { label: "Home", href: "/dashboard" },
582
+ { label: "IAM", href: "/iam/roles" },
583
+ { label: "Roles" }
584
+ ]
585
+ });
586
+ const [createOpen, setCreateOpen] = useState3(false);
587
+ const { queryConfig, params, setParams } = useEntityParams({
588
+ searchKey: "search"
589
+ });
590
+ const rolesQuery = queryConfig;
591
+ const { data, isPending, isFetching } = hooks.useQuery(
592
+ "get",
593
+ "/roles",
594
+ rolesQuery,
595
+ defaultEntityQueryOptions
596
+ );
597
+ const seedRoles = hooks.useMutation("post", "/roles/seed", {
598
+ onSuccess: () => {
599
+ qc.invalidateQueries({ queryKey: ["get", "/roles"] });
600
+ toast.success("Roles seeded");
601
+ },
602
+ onError: () => {
603
+ toast.error("Failed to seed roles");
604
+ }
605
+ });
606
+ const isLoading = isPending || isFetching;
607
+ const roles = data?.roles ?? [];
608
+ const { total, pageCount } = useEntityPagination({
609
+ items: roles,
610
+ total: data?.total,
611
+ pageSize: params.pageSize
612
+ });
613
+ return /* @__PURE__ */ jsx7(PageContainer, { className: "flex flex-1 flex-col gap-4 p-4 pt-0", children: /* @__PURE__ */ jsxs4(PageBody, { className: "px-0", children: [
614
+ /* @__PURE__ */ jsx7(
615
+ EntityHeader,
616
+ {
617
+ icon: /* @__PURE__ */ jsx7(IconShield2, { className: "h-5 w-5" }),
618
+ title: "Roles",
619
+ actions: /* @__PURE__ */ jsxs4("div", { className: "flex items-center gap-2", children: [
620
+ /* @__PURE__ */ jsx7(
621
+ Button2,
622
+ {
623
+ variant: "outline",
624
+ onClick: () => seedRoles.mutate({}),
625
+ loading: seedRoles.isPending,
626
+ children: "Seed roles"
627
+ }
628
+ ),
629
+ /* @__PURE__ */ jsx7(
630
+ EntityDrawerTrigger,
631
+ {
632
+ mode: "new",
633
+ entity: "Role",
634
+ open: createOpen,
635
+ onOpenChange: setCreateOpen,
636
+ children: (open, onClose) => /* @__PURE__ */ jsx7(RoleForm, { mode: "new", open, onClose })
637
+ }
638
+ )
639
+ ] }),
640
+ search: /* @__PURE__ */ jsx7(EntitySearch, { paramKey: "search", placeholder: "Search roles..." }),
641
+ filter: /* @__PURE__ */ jsx7(
642
+ EntityFilter,
643
+ {
644
+ options: [
645
+ { label: "All", value: "" },
646
+ { label: "By Code", value: "code" }
647
+ ],
648
+ placeholder: "Filter"
649
+ }
650
+ ),
651
+ sort: /* @__PURE__ */ jsx7(
652
+ EntitySort,
653
+ {
654
+ options: [
655
+ { label: "Created", value: "createdAt" },
656
+ { label: "Updated", value: "updatedAt" },
657
+ { label: "Code", value: "code" }
658
+ ]
659
+ }
660
+ ),
661
+ view: /* @__PURE__ */ jsx7(EntityViewToggle, { views: ["table", "card"] })
662
+ }
663
+ ),
664
+ /* @__PURE__ */ jsx7(
665
+ RolesList,
666
+ {
667
+ data: roles,
668
+ isLoading,
669
+ view: params.view || "table",
670
+ pageIndex: params.page - 1,
671
+ pageSize: params.pageSize,
672
+ pageCount,
673
+ totalRows: total,
674
+ onCreateNew: () => setCreateOpen(true),
675
+ onPageChange: (p) => setParams({ page: p + 1 }),
676
+ onPageSizeChange: (size) => setParams({ pageSize: size, page: 1 })
677
+ }
678
+ )
679
+ ] }) });
680
+ }
681
+ export {
682
+ RolesPage as default
683
+ };
684
+ //# sourceMappingURL=roles.js.map