@mesob/auth-react 0.4.4 → 0.4.6

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 (182) hide show
  1. package/dist/{chunk-XEFPJKBB.js → chunk-4ABXALRN.js} +2 -2
  2. package/dist/{chunk-DXTZQ2VY.js → chunk-4NUO6F3J.js} +2 -2
  3. package/dist/{chunk-CC3HI442.js → chunk-5AEV7RAN.js} +3 -3
  4. package/dist/{chunk-VA2XUED4.js → chunk-5HAABEAS.js} +2 -2
  5. package/dist/{chunk-EKZYBYZQ.js → chunk-5SDS2E3F.js} +2 -2
  6. package/dist/{chunk-ODDTS4RN.js → chunk-6IEX2RLA.js} +3 -3
  7. package/dist/{chunk-HM4MAF2I.js → chunk-A3GIMM2R.js} +3 -3
  8. package/dist/{chunk-4QMUNU5E.js → chunk-A7ORWWM5.js} +6 -4
  9. package/dist/{chunk-4QMUNU5E.js.map → chunk-A7ORWWM5.js.map} +1 -1
  10. package/dist/{chunk-XCJ3ZH7D.js → chunk-AWSAC7RT.js} +1 -1
  11. package/dist/{chunk-XCJ3ZH7D.js.map → chunk-AWSAC7RT.js.map} +1 -1
  12. package/dist/{chunk-V4VX55TT.js → chunk-CP4TTRV4.js} +2 -2
  13. package/dist/{chunk-GB3Q5IWT.js → chunk-CXMPZWMX.js} +4 -4
  14. package/dist/{chunk-AKEBT5PJ.js → chunk-DG6GRTPG.js} +2 -2
  15. package/dist/{chunk-KTZSXSHV.js → chunk-DY3NVBLJ.js} +2 -2
  16. package/dist/{chunk-OK7WPIHQ.js → chunk-EPEXIGKB.js} +2 -2
  17. package/dist/{chunk-F6IMR5XS.js → chunk-EQWOGD4F.js} +2 -2
  18. package/dist/{chunk-RZFSMM3X.js → chunk-ETOCBXDT.js} +3 -3
  19. package/dist/{chunk-LNG736CV.js → chunk-EWXK56WQ.js} +1 -1
  20. package/dist/chunk-FPYQ7XGV.js +415 -0
  21. package/dist/chunk-FPYQ7XGV.js.map +1 -0
  22. package/dist/chunk-G74DDR4O.js +153 -0
  23. package/dist/chunk-G74DDR4O.js.map +1 -0
  24. package/dist/{chunk-HH347MJI.js → chunk-GVEBIL3O.js} +2 -2
  25. package/dist/{chunk-M6ZUEZJT.js → chunk-HXHI4FU6.js} +3 -3
  26. package/dist/{chunk-VU4Z2XSQ.js → chunk-IFAVSKHE.js} +3 -3
  27. package/dist/{chunk-WD2COCQW.js → chunk-KOBZ34XU.js} +4 -4
  28. package/dist/{chunk-ZE3R37PI.js → chunk-L32Z3TPA.js} +213 -54
  29. package/dist/chunk-L32Z3TPA.js.map +1 -0
  30. package/dist/{chunk-CDECLPL4.js → chunk-LHZ4EEN6.js} +3 -3
  31. package/dist/{chunk-REHGRIUT.js → chunk-MQI6Q2S4.js} +7 -25
  32. package/dist/chunk-MQI6Q2S4.js.map +1 -0
  33. package/dist/{chunk-6F5LEXYI.js → chunk-MVVAPYUD.js} +2 -2
  34. package/dist/chunk-NUWAI3FE.js +350 -0
  35. package/dist/chunk-NUWAI3FE.js.map +1 -0
  36. package/dist/{chunk-S3KIMVDE.js → chunk-OQUUS5ZX.js} +4 -4
  37. package/dist/chunk-OW75JENQ.js +19 -0
  38. package/dist/chunk-OW75JENQ.js.map +1 -0
  39. package/dist/{chunk-KM5GOJ75.js → chunk-PG65ZD7A.js} +4 -4
  40. package/dist/{chunk-NFBOIG3D.js → chunk-RBXITSE2.js} +5 -5
  41. package/dist/{chunk-BMZTYXGO.js → chunk-RMJNENJB.js} +2 -2
  42. package/dist/{chunk-5NKPFZ2Q.js → chunk-SGDXNT7M.js} +3 -3
  43. package/dist/{chunk-SJHJ63HK.js → chunk-SW7WD64K.js} +15 -6
  44. package/dist/chunk-SW7WD64K.js.map +1 -0
  45. package/dist/{chunk-ABF34UFW.js → chunk-SXVTYYUT.js} +3 -3
  46. package/dist/{chunk-ABF34UFW.js.map → chunk-SXVTYYUT.js.map} +1 -1
  47. package/dist/{chunk-GIGXCAGH.js → chunk-TB2ZPGLW.js} +3 -3
  48. package/dist/{chunk-RJBNA7OH.js → chunk-TLQMK2QF.js} +4 -4
  49. package/dist/{chunk-RXHL7LUV.js → chunk-UAKGEJUN.js} +3 -3
  50. package/dist/{chunk-3HV5KQFZ.js → chunk-UGQP733V.js} +2 -2
  51. package/dist/{chunk-YHZLPAYD.js → chunk-UY55LEIG.js} +2 -2
  52. package/dist/{chunk-QJVZHT27.js → chunk-W3D4HG5W.js} +3 -3
  53. package/dist/{chunk-B6RVTHW2.js → chunk-WQ3UUUKF.js} +3 -3
  54. package/dist/{chunk-JRHJNMG3.js → chunk-WY2JJNZW.js} +2 -2
  55. package/dist/{chunk-4BFHC4JI.js → chunk-X2BHF4KC.js} +2 -2
  56. package/dist/{chunk-SYT5Z7EF.js → chunk-YFQNNSSC.js} +2 -2
  57. package/dist/{chunk-JED4SG3W.js → chunk-YN7OEQI7.js} +4 -4
  58. package/dist/{chunk-JED4SG3W.js.map → chunk-YN7OEQI7.js.map} +1 -1
  59. package/dist/{chunk-PS2KBGDB.js → chunk-Z34NJZRL.js} +3 -3
  60. package/dist/{chunk-EGZYJMSA.js → chunk-ZXKEG3X5.js} +2 -2
  61. package/dist/{chunk-L2W6FXSZ.js → chunk-ZZ6D4KE4.js} +2 -2
  62. package/dist/components/auth/countdown.js +3 -3
  63. package/dist/components/auth/forgot-password.js +3 -3
  64. package/dist/components/auth/reset-password-form.js +3 -3
  65. package/dist/components/auth/set-password.js +3 -3
  66. package/dist/components/auth/sign-in.js +3 -3
  67. package/dist/components/auth/sign-up.js +3 -3
  68. package/dist/components/auth/verification-form.js +4 -4
  69. package/dist/components/auth/verify-email.js +5 -5
  70. package/dist/components/auth/verify-phone.js +5 -5
  71. package/dist/components/authorization/deny.js +2 -2
  72. package/dist/components/authorization/grant.js +2 -2
  73. package/dist/components/iam/permission-selector.js +2 -2
  74. package/dist/components/iam/permissions-page.js +2 -2
  75. package/dist/components/iam/permissions.js +2 -2
  76. package/dist/components/iam/role-detail-layout.js +2 -2
  77. package/dist/components/iam/role-detail-page.js +2 -2
  78. package/dist/components/iam/role-permissions-page.js +3 -3
  79. package/dist/components/iam/roles-page.js +6 -5
  80. package/dist/components/iam/roles.js +2 -2
  81. package/dist/components/iam/sessions-page.js +4 -4
  82. package/dist/components/iam/sessions.js +2 -2
  83. package/dist/components/iam/tenants-page.js +5 -5
  84. package/dist/components/iam/tenants.js +2 -2
  85. package/dist/components/iam/users-page.js +6 -5
  86. package/dist/components/iam/users.js +2 -2
  87. package/dist/components/profile/account.js +2 -2
  88. package/dist/components/profile/change-email-form.js +8 -8
  89. package/dist/components/profile/change-password-form.js +2 -2
  90. package/dist/components/profile/change-phone-form.js +8 -8
  91. package/dist/components/profile/otp-verification-modal.js +5 -5
  92. package/dist/components/profile/profile-layout.js +3 -3
  93. package/dist/components/profile/request-change-email-form.js +2 -2
  94. package/dist/components/profile/request-change-phone-form.js +2 -2
  95. package/dist/components/profile/security.js +13 -13
  96. package/dist/components/profile/verify-change-email-form.js +6 -6
  97. package/dist/components/profile/verify-change-phone-form.js +6 -6
  98. package/dist/index.js +50 -48
  99. package/dist/index.js.map +1 -1
  100. package/dist/pages/auth/forgot-password.js +3 -3
  101. package/dist/pages/auth/layout.js +1 -1
  102. package/dist/pages/auth/reset-password.js +3 -3
  103. package/dist/pages/auth/set-password.js +3 -3
  104. package/dist/pages/auth/sign-in.js +3 -3
  105. package/dist/pages/auth/sign-up.js +3 -3
  106. package/dist/pages/auth/verify-email.js +5 -5
  107. package/dist/pages/auth/verify-phone.js +5 -5
  108. package/dist/pages/iam/permissions.js +2 -2
  109. package/dist/pages/iam/role-detail-layout.js +2 -2
  110. package/dist/pages/iam/role-detail.js +2 -2
  111. package/dist/pages/iam/role-permissions.js +3 -3
  112. package/dist/pages/iam/role-users.js +7 -5
  113. package/dist/pages/iam/role-users.js.map +1 -1
  114. package/dist/pages/iam/roles.js +6 -5
  115. package/dist/pages/iam/sessions.js +4 -4
  116. package/dist/pages/iam/tenant-detail.js +3 -3
  117. package/dist/pages/iam/tenants/tenant-selector.js +150 -0
  118. package/dist/pages/iam/tenants/tenant-selector.js.map +1 -0
  119. package/dist/pages/iam/tenants/tenants-data.js +7 -0
  120. package/dist/pages/iam/tenants/tenants-data.js.map +1 -0
  121. package/dist/pages/iam/tenants.js +5 -5
  122. package/dist/pages/iam/user-activity.js +6 -5
  123. package/dist/pages/iam/user-activity.js.map +1 -1
  124. package/dist/pages/iam/user-detail-layout.js +2 -2
  125. package/dist/pages/iam/user-detail.js +2 -2
  126. package/dist/pages/iam/users/_components/users-data.d.ts +4 -4
  127. package/dist/pages/iam/users/user-selector.js +14 -0
  128. package/dist/pages/iam/users/user-selector.js.map +1 -0
  129. package/dist/pages/iam/users/users-data.js +1 -0
  130. package/dist/pages/iam/users/users-data.js.map +1 -0
  131. package/dist/pages/iam/users.js +6 -5
  132. package/dist/pages/profile/account.js +2 -2
  133. package/dist/pages/profile/layout.js +3 -3
  134. package/dist/pages/profile/security.js +13 -13
  135. package/dist/types.d.ts +2 -0
  136. package/package.json +20 -3
  137. package/dist/chunk-GYFHM72X.js +0 -486
  138. package/dist/chunk-GYFHM72X.js.map +0 -1
  139. package/dist/chunk-REHGRIUT.js.map +0 -1
  140. package/dist/chunk-SJHJ63HK.js.map +0 -1
  141. package/dist/chunk-UCNNUST6.js +0 -271
  142. package/dist/chunk-UCNNUST6.js.map +0 -1
  143. package/dist/chunk-ZE3R37PI.js.map +0 -1
  144. /package/dist/{chunk-XEFPJKBB.js.map → chunk-4ABXALRN.js.map} +0 -0
  145. /package/dist/{chunk-DXTZQ2VY.js.map → chunk-4NUO6F3J.js.map} +0 -0
  146. /package/dist/{chunk-CC3HI442.js.map → chunk-5AEV7RAN.js.map} +0 -0
  147. /package/dist/{chunk-VA2XUED4.js.map → chunk-5HAABEAS.js.map} +0 -0
  148. /package/dist/{chunk-EKZYBYZQ.js.map → chunk-5SDS2E3F.js.map} +0 -0
  149. /package/dist/{chunk-ODDTS4RN.js.map → chunk-6IEX2RLA.js.map} +0 -0
  150. /package/dist/{chunk-HM4MAF2I.js.map → chunk-A3GIMM2R.js.map} +0 -0
  151. /package/dist/{chunk-V4VX55TT.js.map → chunk-CP4TTRV4.js.map} +0 -0
  152. /package/dist/{chunk-GB3Q5IWT.js.map → chunk-CXMPZWMX.js.map} +0 -0
  153. /package/dist/{chunk-AKEBT5PJ.js.map → chunk-DG6GRTPG.js.map} +0 -0
  154. /package/dist/{chunk-KTZSXSHV.js.map → chunk-DY3NVBLJ.js.map} +0 -0
  155. /package/dist/{chunk-OK7WPIHQ.js.map → chunk-EPEXIGKB.js.map} +0 -0
  156. /package/dist/{chunk-F6IMR5XS.js.map → chunk-EQWOGD4F.js.map} +0 -0
  157. /package/dist/{chunk-RZFSMM3X.js.map → chunk-ETOCBXDT.js.map} +0 -0
  158. /package/dist/{chunk-LNG736CV.js.map → chunk-EWXK56WQ.js.map} +0 -0
  159. /package/dist/{chunk-HH347MJI.js.map → chunk-GVEBIL3O.js.map} +0 -0
  160. /package/dist/{chunk-M6ZUEZJT.js.map → chunk-HXHI4FU6.js.map} +0 -0
  161. /package/dist/{chunk-VU4Z2XSQ.js.map → chunk-IFAVSKHE.js.map} +0 -0
  162. /package/dist/{chunk-WD2COCQW.js.map → chunk-KOBZ34XU.js.map} +0 -0
  163. /package/dist/{chunk-CDECLPL4.js.map → chunk-LHZ4EEN6.js.map} +0 -0
  164. /package/dist/{chunk-6F5LEXYI.js.map → chunk-MVVAPYUD.js.map} +0 -0
  165. /package/dist/{chunk-S3KIMVDE.js.map → chunk-OQUUS5ZX.js.map} +0 -0
  166. /package/dist/{chunk-KM5GOJ75.js.map → chunk-PG65ZD7A.js.map} +0 -0
  167. /package/dist/{chunk-NFBOIG3D.js.map → chunk-RBXITSE2.js.map} +0 -0
  168. /package/dist/{chunk-BMZTYXGO.js.map → chunk-RMJNENJB.js.map} +0 -0
  169. /package/dist/{chunk-5NKPFZ2Q.js.map → chunk-SGDXNT7M.js.map} +0 -0
  170. /package/dist/{chunk-GIGXCAGH.js.map → chunk-TB2ZPGLW.js.map} +0 -0
  171. /package/dist/{chunk-RJBNA7OH.js.map → chunk-TLQMK2QF.js.map} +0 -0
  172. /package/dist/{chunk-RXHL7LUV.js.map → chunk-UAKGEJUN.js.map} +0 -0
  173. /package/dist/{chunk-3HV5KQFZ.js.map → chunk-UGQP733V.js.map} +0 -0
  174. /package/dist/{chunk-YHZLPAYD.js.map → chunk-UY55LEIG.js.map} +0 -0
  175. /package/dist/{chunk-QJVZHT27.js.map → chunk-W3D4HG5W.js.map} +0 -0
  176. /package/dist/{chunk-B6RVTHW2.js.map → chunk-WQ3UUUKF.js.map} +0 -0
  177. /package/dist/{chunk-JRHJNMG3.js.map → chunk-WY2JJNZW.js.map} +0 -0
  178. /package/dist/{chunk-4BFHC4JI.js.map → chunk-X2BHF4KC.js.map} +0 -0
  179. /package/dist/{chunk-SYT5Z7EF.js.map → chunk-YFQNNSSC.js.map} +0 -0
  180. /package/dist/{chunk-PS2KBGDB.js.map → chunk-Z34NJZRL.js.map} +0 -0
  181. /package/dist/{chunk-EGZYJMSA.js.map → chunk-ZXKEG3X5.js.map} +0 -0
  182. /package/dist/{chunk-L2W6FXSZ.js.map → chunk-ZZ6D4KE4.js.map} +0 -0
@@ -0,0 +1,415 @@
1
+ import {
2
+ str
3
+ } from "./chunk-OW75JENQ.js";
4
+ import {
5
+ Link,
6
+ authApi$
7
+ } from "./chunk-W3D4HG5W.js";
8
+
9
+ // src/pages/iam/users/_components/user-card.tsx
10
+ import {
11
+ Button,
12
+ Card,
13
+ CardContent,
14
+ CardHeader,
15
+ DropdownMenu,
16
+ DropdownMenuContent,
17
+ DropdownMenuItem,
18
+ DropdownMenuPortal,
19
+ DropdownMenuTrigger,
20
+ Tooltip,
21
+ TooltipContent,
22
+ TooltipTrigger
23
+ } from "@mesob/ui/components";
24
+ import { IconCircleCheck, IconDots, IconPencil } from "@tabler/icons-react";
25
+ import { useState } from "react";
26
+
27
+ // src/pages/iam/users/_components/user-form.tsx
28
+ import { zodResolver } from "@hookform/resolvers/zod";
29
+ import {
30
+ Checkbox,
31
+ EntityDrawer,
32
+ EntityFormActions,
33
+ Form,
34
+ FormControl,
35
+ FormField,
36
+ FormItem,
37
+ FormLabel,
38
+ FormMessage,
39
+ Input,
40
+ Skeleton,
41
+ Stack
42
+ } from "@mesob/ui/components";
43
+ import { useQueryClient } from "@tanstack/react-query";
44
+ import { useEffect } from "react";
45
+ import { useForm, useWatch } from "react-hook-form";
46
+ import { z } from "zod";
47
+ import { jsx, jsxs } from "react/jsx-runtime";
48
+ var schema = z.object({
49
+ fullName: z.string().trim().min(1, "Full name is required"),
50
+ email: z.string().email("Invalid email").nullable().or(z.literal("")),
51
+ phone: z.string().nullable(),
52
+ emailVerified: z.boolean(),
53
+ phoneVerified: z.boolean(),
54
+ roleIds: z.array(z.string().uuid())
55
+ });
56
+ var defaults = {
57
+ fullName: "",
58
+ email: "",
59
+ phone: null,
60
+ emailVerified: false,
61
+ phoneVerified: false,
62
+ roleIds: []
63
+ };
64
+ function UserForm({
65
+ mode,
66
+ userId,
67
+ open,
68
+ onClose,
69
+ onSuccess
70
+ }) {
71
+ const qc = useQueryClient();
72
+ const { data: userData, isLoading: userLoading } = authApi$.useQuery(
73
+ "get",
74
+ "/users/{id}",
75
+ { params: { path: { id: userId ?? "" } } },
76
+ { enabled: mode === "edit" && !!userId && open }
77
+ );
78
+ const { data: rolesData } = authApi$.useQuery(
79
+ "get",
80
+ "/roles",
81
+ { params: { query: { limit: 100 } } },
82
+ { enabled: open }
83
+ );
84
+ const allRoles = rolesData?.roles ?? [];
85
+ const roles = allRoles.filter((r) => (r.code ?? "").toLowerCase() !== "owner").sort(
86
+ (a, b) => (str(a.name) || a.code).toLowerCase().localeCompare((str(b.name) || b.code).toLowerCase())
87
+ );
88
+ const user = userData?.user;
89
+ const form = useForm({
90
+ resolver: zodResolver(schema),
91
+ defaultValues: defaults
92
+ });
93
+ const { control, formState, reset, setValue } = form;
94
+ const email = useWatch({ control, name: "email" }) ?? "";
95
+ const phone = useWatch({ control, name: "phone" }) ?? "";
96
+ const roleIds = useWatch({ control, name: "roleIds" }) ?? [];
97
+ const hasEmail = (email ?? "").toString().trim().length > 0;
98
+ const hasPhone = (phone ?? "").toString().trim().length > 0;
99
+ useEffect(() => {
100
+ if (!hasEmail) {
101
+ setValue("emailVerified", false, { shouldDirty: false });
102
+ }
103
+ if (!hasPhone) {
104
+ setValue("phoneVerified", false, { shouldDirty: false });
105
+ }
106
+ }, [hasEmail, hasPhone, setValue]);
107
+ useEffect(() => {
108
+ if (!open) {
109
+ return;
110
+ }
111
+ if (mode === "edit" && user && !userLoading) {
112
+ reset({
113
+ fullName: user.fullName,
114
+ email: user.email ?? "",
115
+ phone: user.phone ?? null,
116
+ emailVerified: user.emailVerified ?? false,
117
+ phoneVerified: user.phoneVerified ?? false,
118
+ roleIds: user.roles ?? []
119
+ });
120
+ } else {
121
+ reset(defaults);
122
+ }
123
+ }, [mode, user, open, userLoading, reset]);
124
+ const create = authApi$.useMutation("post", "/users", {
125
+ onSuccess: () => {
126
+ qc.invalidateQueries({ queryKey: ["get", "/users"] });
127
+ }
128
+ });
129
+ const update = authApi$.useMutation("put", "/users/{id}", {
130
+ onSuccess: () => {
131
+ qc.invalidateQueries({ queryKey: ["get", "/users"] });
132
+ if (userId) {
133
+ qc.invalidateQueries({ queryKey: ["get", "/users/{id}"] });
134
+ }
135
+ }
136
+ });
137
+ const del = authApi$.useMutation("delete", "/users/{id}", {
138
+ onSuccess: () => {
139
+ qc.invalidateQueries({ queryKey: ["get", "/users"] });
140
+ }
141
+ });
142
+ const onSubmit = form.handleSubmit(async (d) => {
143
+ const base = {
144
+ fullName: d.fullName,
145
+ email: d.email?.trim() || void 0,
146
+ phone: d.phone?.trim() || void 0,
147
+ emailVerified: d.emailVerified,
148
+ phoneVerified: d.phoneVerified
149
+ };
150
+ if (mode === "new") {
151
+ await create.mutateAsync({
152
+ body: {
153
+ ...base,
154
+ email: base.email || void 0,
155
+ phone: base.phone || void 0
156
+ }
157
+ });
158
+ } else if (userId) {
159
+ await update.mutateAsync({
160
+ params: { path: { id: userId } },
161
+ body: {
162
+ ...base,
163
+ email: base.email ?? null,
164
+ phone: base.phone ?? null,
165
+ roleIds: d.roleIds
166
+ }
167
+ });
168
+ }
169
+ onSuccess?.();
170
+ onClose();
171
+ });
172
+ const onDelete = async () => {
173
+ if (!userId) {
174
+ return;
175
+ }
176
+ await del.mutateAsync({ params: { path: { id: userId } } });
177
+ onSuccess?.();
178
+ onClose();
179
+ };
180
+ const toggleRole = (roleId, checked) => {
181
+ const next = checked ? [...roleIds, roleId] : roleIds.filter((id) => id !== roleId);
182
+ setValue("roleIds", next, { shouldDirty: true });
183
+ };
184
+ const isSubmitting = create.isPending || update.isPending;
185
+ return /* @__PURE__ */ jsx(
186
+ EntityDrawer,
187
+ {
188
+ title: mode === "new" ? "New user" : "Edit user",
189
+ open,
190
+ onClose,
191
+ isDirty: formState.isDirty,
192
+ size: "lg",
193
+ form: userLoading && mode === "edit" ? /* @__PURE__ */ jsx(FormSkeleton, {}) : /* @__PURE__ */ jsx(Form, { ...form, children: /* @__PURE__ */ jsxs("form", { onSubmit, className: "space-y-6", children: [
194
+ /* @__PURE__ */ jsxs(Stack, { children: [
195
+ /* @__PURE__ */ jsx(
196
+ FormField,
197
+ {
198
+ control,
199
+ name: "fullName",
200
+ render: ({ field }) => /* @__PURE__ */ jsxs(FormItem, { children: [
201
+ /* @__PURE__ */ jsxs(FormLabel, { children: [
202
+ "Full name ",
203
+ /* @__PURE__ */ jsx("span", { className: "text-destructive", children: "*" })
204
+ ] }),
205
+ /* @__PURE__ */ jsx(FormControl, { children: /* @__PURE__ */ jsx(Input, { placeholder: "Full name", ...field }) }),
206
+ /* @__PURE__ */ jsx(FormMessage, {})
207
+ ] })
208
+ }
209
+ ),
210
+ /* @__PURE__ */ jsx(
211
+ FormField,
212
+ {
213
+ control,
214
+ name: "email",
215
+ render: ({ field }) => /* @__PURE__ */ jsxs(FormItem, { children: [
216
+ /* @__PURE__ */ jsx(FormLabel, { children: "Email" }),
217
+ /* @__PURE__ */ jsx(FormControl, { children: /* @__PURE__ */ jsx(
218
+ Input,
219
+ {
220
+ type: "email",
221
+ placeholder: "user@example.com",
222
+ ...field,
223
+ value: field.value ?? ""
224
+ }
225
+ ) }),
226
+ /* @__PURE__ */ jsx(FormMessage, {})
227
+ ] })
228
+ }
229
+ ),
230
+ /* @__PURE__ */ jsx(
231
+ FormField,
232
+ {
233
+ control,
234
+ name: "phone",
235
+ render: ({ field }) => /* @__PURE__ */ jsxs(FormItem, { children: [
236
+ /* @__PURE__ */ jsx(FormLabel, { children: "Phone" }),
237
+ /* @__PURE__ */ jsx(FormControl, { children: /* @__PURE__ */ jsx(
238
+ Input,
239
+ {
240
+ placeholder: "+251911223344",
241
+ ...field,
242
+ value: field.value ?? ""
243
+ }
244
+ ) }),
245
+ /* @__PURE__ */ jsx(FormMessage, {})
246
+ ] })
247
+ }
248
+ ),
249
+ /* @__PURE__ */ jsxs("div", { className: "space-y-4", children: [
250
+ /* @__PURE__ */ jsx(FormLabel, { children: "Verification" }),
251
+ /* @__PURE__ */ jsxs("div", { className: "flex flex-wrap gap-6 pt-1", children: [
252
+ /* @__PURE__ */ jsx(
253
+ FormField,
254
+ {
255
+ control,
256
+ name: "emailVerified",
257
+ render: ({ field }) => /* @__PURE__ */ jsxs(FormItem, { className: "flex flex-row items-center gap-2 space-y-0", children: [
258
+ /* @__PURE__ */ jsx(FormControl, { children: /* @__PURE__ */ jsx(
259
+ Checkbox,
260
+ {
261
+ size: "lg",
262
+ checked: field.value,
263
+ disabled: !hasEmail,
264
+ onCheckedChange: (c) => field.onChange(c === true)
265
+ }
266
+ ) }),
267
+ /* @__PURE__ */ jsx(FormLabel, { className: "cursor-pointer font-normal", children: "Email verified" }),
268
+ /* @__PURE__ */ jsx(FormMessage, {})
269
+ ] })
270
+ }
271
+ ),
272
+ /* @__PURE__ */ jsx(
273
+ FormField,
274
+ {
275
+ control,
276
+ name: "phoneVerified",
277
+ render: ({ field }) => /* @__PURE__ */ jsxs(FormItem, { className: "flex flex-row items-center gap-2 space-y-0", children: [
278
+ /* @__PURE__ */ jsx(FormControl, { children: /* @__PURE__ */ jsx(
279
+ Checkbox,
280
+ {
281
+ size: "lg",
282
+ checked: field.value,
283
+ disabled: !hasPhone,
284
+ onCheckedChange: (c) => field.onChange(c === true)
285
+ }
286
+ ) }),
287
+ /* @__PURE__ */ jsx(FormLabel, { className: "cursor-pointer font-normal", children: "Phone verified" }),
288
+ /* @__PURE__ */ jsx(FormMessage, {})
289
+ ] })
290
+ }
291
+ )
292
+ ] })
293
+ ] })
294
+ ] }),
295
+ mode === "edit" && roles.length > 0 && /* @__PURE__ */ jsxs("div", { className: "space-y-4", children: [
296
+ /* @__PURE__ */ jsx(FormLabel, { children: "Roles" }),
297
+ /* @__PURE__ */ jsx("div", { className: "flex flex-col gap-3 pt-1", children: roles.map((role) => {
298
+ const desc = str(role.description);
299
+ return /* @__PURE__ */ jsxs("div", { className: "flex items-start gap-3", children: [
300
+ /* @__PURE__ */ jsx(
301
+ Checkbox,
302
+ {
303
+ size: "lg",
304
+ id: `role-${role.id}`,
305
+ checked: roleIds.includes(role.id),
306
+ onCheckedChange: (c) => toggleRole(role.id, c === true),
307
+ className: "mt-0.5"
308
+ }
309
+ ),
310
+ /* @__PURE__ */ jsxs(
311
+ "label",
312
+ {
313
+ htmlFor: `role-${role.id}`,
314
+ className: "flex min-w-0 flex-1 cursor-pointer flex-col gap-0.5",
315
+ children: [
316
+ /* @__PURE__ */ jsx("span", { className: "text-sm font-medium", children: str(role.name) || role.code }),
317
+ desc ? /* @__PURE__ */ jsx("span", { className: "text-xs text-muted-foreground", children: desc }) : null
318
+ ]
319
+ }
320
+ )
321
+ ] }, role.id);
322
+ }) })
323
+ ] })
324
+ ] }) }),
325
+ actions: /* @__PURE__ */ jsx(
326
+ EntityFormActions,
327
+ {
328
+ mode,
329
+ onSubmit,
330
+ onReset: mode === "new" ? () => reset(defaults) : void 0,
331
+ onDelete: mode === "edit" ? onDelete : void 0,
332
+ isSubmitting,
333
+ isDeleting: del.isPending,
334
+ disabled: userLoading && mode === "edit",
335
+ itemName: "user"
336
+ }
337
+ )
338
+ }
339
+ );
340
+ }
341
+ function FormSkeleton() {
342
+ return /* @__PURE__ */ jsx("div", { className: "space-y-4", children: [1, 2, 3, 4, 5].map((i) => /* @__PURE__ */ jsxs("div", { className: "space-y-2", children: [
343
+ /* @__PURE__ */ jsx(Skeleton, { className: "h-4 w-20" }),
344
+ /* @__PURE__ */ jsx(Skeleton, { className: "h-10 w-full" })
345
+ ] }, i)) });
346
+ }
347
+
348
+ // src/pages/iam/users/_components/user-card.tsx
349
+ import { Fragment, jsx as jsx2, jsxs as jsxs2 } from "react/jsx-runtime";
350
+ function UserCard({ user }) {
351
+ const [editOpen, setEditOpen] = useState(false);
352
+ return /* @__PURE__ */ jsxs2(Fragment, { children: [
353
+ /* @__PURE__ */ jsxs2(Card, { className: "group hover:shadow-md transition-shadow", children: [
354
+ /* @__PURE__ */ jsx2(CardHeader, { className: "pb-2", children: /* @__PURE__ */ jsxs2("div", { className: "flex items-start justify-between", children: [
355
+ /* @__PURE__ */ jsx2(
356
+ Link,
357
+ {
358
+ href: `/iam/users/${user.id}`,
359
+ className: "text-left font-semibold hover:text-primary hover:underline",
360
+ children: user.fullName
361
+ }
362
+ ),
363
+ /* @__PURE__ */ jsxs2(DropdownMenu, { children: [
364
+ /* @__PURE__ */ jsx2(
365
+ DropdownMenuTrigger,
366
+ {
367
+ render: /* @__PURE__ */ jsx2(
368
+ Button,
369
+ {
370
+ variant: "ghost",
371
+ size: "icon",
372
+ className: "h-8 w-8 opacity-0 group-hover:opacity-100 transition-opacity"
373
+ }
374
+ ),
375
+ children: /* @__PURE__ */ jsx2(IconDots, { className: "h-4 w-4" })
376
+ }
377
+ ),
378
+ /* @__PURE__ */ jsx2(DropdownMenuPortal, { children: /* @__PURE__ */ jsx2(DropdownMenuContent, { children: /* @__PURE__ */ jsxs2(DropdownMenuItem, { onClick: () => setEditOpen(true), children: [
379
+ /* @__PURE__ */ jsx2(IconPencil, { className: "mr-2 h-4 w-4" }),
380
+ "Edit"
381
+ ] }) }) })
382
+ ] })
383
+ ] }) }),
384
+ /* @__PURE__ */ jsxs2(CardContent, { className: "space-y-2", children: [
385
+ user.email && /* @__PURE__ */ jsxs2("div", { className: "flex items-center gap-2", children: [
386
+ /* @__PURE__ */ jsx2("span", { className: "text-sm", children: user.email }),
387
+ user.emailVerified && /* @__PURE__ */ jsxs2(Tooltip, { children: [
388
+ /* @__PURE__ */ jsx2(TooltipTrigger, { children: /* @__PURE__ */ jsx2(IconCircleCheck, { className: "size-4 text-muted-foreground" }) }),
389
+ /* @__PURE__ */ jsx2(TooltipContent, { children: "Email verified" })
390
+ ] })
391
+ ] }),
392
+ /* @__PURE__ */ jsxs2("p", { className: "text-xs text-muted-foreground", children: [
393
+ "Last sign in",
394
+ " ",
395
+ user.lastSignInAt ? new Date(user.lastSignInAt).toLocaleDateString() : "never"
396
+ ] })
397
+ ] })
398
+ ] }),
399
+ editOpen && /* @__PURE__ */ jsx2(
400
+ UserForm,
401
+ {
402
+ mode: "edit",
403
+ userId: user.id,
404
+ open: editOpen,
405
+ onClose: () => setEditOpen(false)
406
+ }
407
+ )
408
+ ] });
409
+ }
410
+
411
+ export {
412
+ UserForm,
413
+ UserCard
414
+ };
415
+ //# sourceMappingURL=chunk-FPYQ7XGV.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/pages/iam/users/_components/user-card.tsx","../src/pages/iam/users/_components/user-form.tsx"],"sourcesContent":["'use client';\n\nimport {\n Button,\n Card,\n CardContent,\n CardHeader,\n DropdownMenu,\n DropdownMenuContent,\n DropdownMenuItem,\n DropdownMenuPortal,\n DropdownMenuTrigger,\n Tooltip,\n TooltipContent,\n TooltipTrigger,\n} from '@mesob/ui/components';\nimport { IconCircleCheck, IconDots, IconPencil } from '@tabler/icons-react';\nimport { useState } from 'react';\nimport { Link } from '../../shared/page-helpers';\nimport { UserForm } from './user-form';\nimport type { User } from './users-data';\n\ntype UserCardProps = { user: User };\n\nexport function UserCard({ user }: UserCardProps) {\n const [editOpen, setEditOpen] = useState(false);\n return (\n <>\n <Card className=\"group hover:shadow-md transition-shadow\">\n <CardHeader className=\"pb-2\">\n <div className=\"flex items-start justify-between\">\n <Link\n href={`/iam/users/${user.id}`}\n className=\"text-left font-semibold hover:text-primary hover:underline\"\n >\n {user.fullName}\n </Link>\n <DropdownMenu>\n <DropdownMenuTrigger\n render={\n <Button\n variant=\"ghost\"\n size=\"icon\"\n className=\"h-8 w-8 opacity-0 group-hover:opacity-100 transition-opacity\"\n />\n }\n >\n <IconDots className=\"h-4 w-4\" />\n </DropdownMenuTrigger>\n <DropdownMenuPortal>\n <DropdownMenuContent>\n <DropdownMenuItem onClick={() => setEditOpen(true)}>\n <IconPencil className=\"mr-2 h-4 w-4\" />\n Edit\n </DropdownMenuItem>\n </DropdownMenuContent>\n </DropdownMenuPortal>\n </DropdownMenu>\n </div>\n </CardHeader>\n <CardContent className=\"space-y-2\">\n {user.email && (\n <div className=\"flex items-center gap-2\">\n <span className=\"text-sm\">{user.email}</span>\n {user.emailVerified && (\n <Tooltip>\n <TooltipTrigger>\n <IconCircleCheck className=\"size-4 text-muted-foreground\" />\n </TooltipTrigger>\n <TooltipContent>Email verified</TooltipContent>\n </Tooltip>\n )}\n </div>\n )}\n <p className=\"text-xs text-muted-foreground\">\n Last sign in{' '}\n {user.lastSignInAt\n ? new Date(user.lastSignInAt).toLocaleDateString()\n : 'never'}\n </p>\n </CardContent>\n </Card>\n {editOpen && (\n <UserForm\n mode=\"edit\"\n userId={user.id}\n open={editOpen}\n onClose={() => setEditOpen(false)}\n />\n )}\n </>\n );\n}\n","'use client';\n\nimport { zodResolver } from '@hookform/resolvers/zod';\nimport {\n Checkbox,\n EntityDrawer,\n EntityFormActions,\n Form,\n FormControl,\n FormField,\n FormItem,\n FormLabel,\n FormMessage,\n Input,\n Skeleton,\n Stack,\n} from '@mesob/ui/components';\nimport { useQueryClient } from '@tanstack/react-query';\nimport { useEffect } from 'react';\nimport { useForm, useWatch } from 'react-hook-form';\nimport { z } from 'zod';\nimport type { Role } from '../../roles/_components/roles-data';\nimport { str } from '../../roles/_components/roles-data';\nimport { authApi$ } from '../../shared/page-helpers';\n\nconst schema = z.object({\n fullName: z.string().trim().min(1, 'Full name is required'),\n email: z.string().email('Invalid email').nullable().or(z.literal('')),\n phone: z.string().nullable(),\n emailVerified: z.boolean(),\n phoneVerified: z.boolean(),\n roleIds: z.array(z.string().uuid()),\n});\n\ntype FormData = z.infer<typeof schema>;\n\nconst defaults: FormData = {\n fullName: '',\n email: '',\n phone: null,\n emailVerified: false,\n phoneVerified: false,\n roleIds: [],\n};\n\ntype UserFormProps = {\n mode: 'new' | 'edit';\n userId?: string;\n open: boolean;\n onClose: () => void;\n onSuccess?: () => void;\n};\n\n// biome-ignore lint/complexity/noExcessiveCognitiveComplexity: form + roles section\nexport function UserForm({\n mode,\n userId,\n open,\n onClose,\n onSuccess,\n}: UserFormProps) {\n const qc = useQueryClient();\n const { data: userData, isLoading: userLoading } = authApi$.useQuery(\n 'get',\n '/users/{id}',\n { params: { path: { id: userId ?? '' } } },\n { enabled: mode === 'edit' && !!userId && open },\n );\n const { data: rolesData } = authApi$.useQuery(\n 'get',\n '/roles',\n { params: { query: { limit: 100 } } },\n { enabled: open },\n );\n const allRoles = rolesData?.roles ?? [];\n const roles = allRoles\n .filter((r: Role) => (r.code ?? '').toLowerCase() !== 'owner')\n .sort((a: Role, b: Role) =>\n (str(a.name) || a.code)\n .toLowerCase()\n .localeCompare((str(b.name) || b.code).toLowerCase()),\n );\n const user = userData?.user;\n\n const form = useForm<FormData>({\n resolver: zodResolver(schema),\n defaultValues: defaults,\n });\n const { control, formState, reset, setValue } = form;\n const email = useWatch({ control, name: 'email' }) ?? '';\n const phone = useWatch({ control, name: 'phone' }) ?? '';\n const roleIds = useWatch({ control, name: 'roleIds' }) ?? [];\n const hasEmail = (email ?? '').toString().trim().length > 0;\n const hasPhone = (phone ?? '').toString().trim().length > 0;\n\n useEffect(() => {\n if (!hasEmail) {\n setValue('emailVerified', false, { shouldDirty: false });\n }\n if (!hasPhone) {\n setValue('phoneVerified', false, { shouldDirty: false });\n }\n }, [hasEmail, hasPhone, setValue]);\n\n useEffect(() => {\n if (!open) {\n return;\n }\n if (mode === 'edit' && user && !userLoading) {\n reset({\n fullName: user.fullName,\n email: user.email ?? '',\n phone: user.phone ?? null,\n emailVerified: user.emailVerified ?? false,\n phoneVerified: user.phoneVerified ?? false,\n roleIds: user.roles ?? [],\n });\n } else {\n reset(defaults);\n }\n }, [mode, user, open, userLoading, reset]);\n\n const create = authApi$.useMutation('post', '/users', {\n onSuccess: () => {\n qc.invalidateQueries({ queryKey: ['get', '/users'] });\n },\n });\n const update = authApi$.useMutation('put', '/users/{id}', {\n onSuccess: () => {\n qc.invalidateQueries({ queryKey: ['get', '/users'] });\n if (userId) {\n qc.invalidateQueries({ queryKey: ['get', '/users/{id}'] });\n }\n },\n });\n const del = authApi$.useMutation('delete', '/users/{id}', {\n onSuccess: () => {\n qc.invalidateQueries({ queryKey: ['get', '/users'] });\n },\n });\n\n const onSubmit = form.handleSubmit(async (d) => {\n const base = {\n fullName: d.fullName,\n email: d.email?.trim() || undefined,\n phone: d.phone?.trim() || undefined,\n emailVerified: d.emailVerified,\n phoneVerified: d.phoneVerified,\n };\n if (mode === 'new') {\n await create.mutateAsync({\n body: {\n ...base,\n email: base.email || undefined,\n phone: base.phone || undefined,\n },\n });\n } else if (userId) {\n await update.mutateAsync({\n params: { path: { id: userId } },\n body: {\n ...base,\n email: base.email ?? null,\n phone: base.phone ?? null,\n roleIds: d.roleIds,\n },\n });\n }\n onSuccess?.();\n onClose();\n });\n\n const onDelete = async () => {\n if (!userId) {\n return;\n }\n await del.mutateAsync({ params: { path: { id: userId } } });\n onSuccess?.();\n onClose();\n };\n\n const toggleRole = (roleId: string, checked: boolean) => {\n const next = checked\n ? [...roleIds, roleId]\n : roleIds.filter((id) => id !== roleId);\n setValue('roleIds', next, { shouldDirty: true });\n };\n\n const isSubmitting = create.isPending || update.isPending;\n\n return (\n <EntityDrawer\n title={mode === 'new' ? 'New user' : 'Edit user'}\n open={open}\n onClose={onClose}\n isDirty={formState.isDirty}\n size=\"lg\"\n form={\n userLoading && mode === 'edit' ? (\n <FormSkeleton />\n ) : (\n <Form {...form}>\n <form onSubmit={onSubmit} className=\"space-y-6\">\n <Stack>\n <FormField\n control={control}\n name=\"fullName\"\n render={({ field }) => (\n <FormItem>\n <FormLabel>\n Full name <span className=\"text-destructive\">*</span>\n </FormLabel>\n <FormControl>\n <Input placeholder=\"Full name\" {...field} />\n </FormControl>\n <FormMessage />\n </FormItem>\n )}\n />\n <FormField\n control={control}\n name=\"email\"\n render={({ field }) => (\n <FormItem>\n <FormLabel>Email</FormLabel>\n <FormControl>\n <Input\n type=\"email\"\n placeholder=\"user@example.com\"\n {...field}\n value={field.value ?? ''}\n />\n </FormControl>\n <FormMessage />\n </FormItem>\n )}\n />\n <FormField\n control={control}\n name=\"phone\"\n render={({ field }) => (\n <FormItem>\n <FormLabel>Phone</FormLabel>\n <FormControl>\n <Input\n placeholder=\"+251911223344\"\n {...field}\n value={field.value ?? ''}\n />\n </FormControl>\n <FormMessage />\n </FormItem>\n )}\n />\n <div className=\"space-y-4\">\n <FormLabel>Verification</FormLabel>\n <div className=\"flex flex-wrap gap-6 pt-1\">\n <FormField\n control={control}\n name=\"emailVerified\"\n render={({ field }) => (\n <FormItem className=\"flex flex-row items-center gap-2 space-y-0\">\n <FormControl>\n <Checkbox\n size=\"lg\"\n checked={field.value}\n disabled={!hasEmail}\n onCheckedChange={(c) =>\n field.onChange(c === true)\n }\n />\n </FormControl>\n <FormLabel className=\"cursor-pointer font-normal\">\n Email verified\n </FormLabel>\n <FormMessage />\n </FormItem>\n )}\n />\n <FormField\n control={control}\n name=\"phoneVerified\"\n render={({ field }) => (\n <FormItem className=\"flex flex-row items-center gap-2 space-y-0\">\n <FormControl>\n <Checkbox\n size=\"lg\"\n checked={field.value}\n disabled={!hasPhone}\n onCheckedChange={(c) =>\n field.onChange(c === true)\n }\n />\n </FormControl>\n <FormLabel className=\"cursor-pointer font-normal\">\n Phone verified\n </FormLabel>\n <FormMessage />\n </FormItem>\n )}\n />\n </div>\n </div>\n </Stack>\n\n {mode === 'edit' && roles.length > 0 && (\n <div className=\"space-y-4\">\n <FormLabel>Roles</FormLabel>\n <div className=\"flex flex-col gap-3 pt-1\">\n {roles.map((role: Role) => {\n const desc = str(role.description);\n return (\n <div key={role.id} className=\"flex items-start gap-3\">\n <Checkbox\n size=\"lg\"\n id={`role-${role.id}`}\n checked={roleIds.includes(role.id)}\n onCheckedChange={(c) =>\n toggleRole(role.id, c === true)\n }\n className=\"mt-0.5\"\n />\n <label\n htmlFor={`role-${role.id}`}\n className=\"flex min-w-0 flex-1 cursor-pointer flex-col gap-0.5\"\n >\n <span className=\"text-sm font-medium\">\n {str(role.name) || role.code}\n </span>\n {desc ? (\n <span className=\"text-xs text-muted-foreground\">\n {desc}\n </span>\n ) : null}\n </label>\n </div>\n );\n })}\n </div>\n </div>\n )}\n </form>\n </Form>\n )\n }\n actions={\n <EntityFormActions\n mode={mode}\n onSubmit={onSubmit}\n onReset={mode === 'new' ? () => reset(defaults) : undefined}\n onDelete={mode === 'edit' ? onDelete : undefined}\n isSubmitting={isSubmitting}\n isDeleting={del.isPending}\n disabled={userLoading && mode === 'edit'}\n itemName=\"user\"\n />\n }\n />\n );\n}\n\nfunction FormSkeleton() {\n return (\n <div className=\"space-y-4\">\n {[1, 2, 3, 4, 5].map((i) => (\n <div key={i} className=\"space-y-2\">\n <Skeleton className=\"h-4 w-20\" />\n <Skeleton className=\"h-10 w-full\" />\n </div>\n ))}\n </div>\n );\n}\n"],"mappings":"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","names":["jsx","jsxs"]}
@@ -0,0 +1,153 @@
1
+ import {
2
+ UserCard
3
+ } from "./chunk-FPYQ7XGV.js";
4
+ import {
5
+ authApi$
6
+ } from "./chunk-W3D4HG5W.js";
7
+ import {
8
+ defaultEntityQueryOptions
9
+ } from "./chunk-NPW7D2HZ.js";
10
+
11
+ // src/pages/iam/users/_components/user-selector.tsx
12
+ import {
13
+ EntitySelector,
14
+ Tooltip,
15
+ TooltipContent,
16
+ TooltipTrigger,
17
+ useEntitySectionState
18
+ } from "@mesob/ui/components";
19
+ import { cn } from "@mesob/ui/lib/utils";
20
+ import { IconCalendar, IconCircleCheck, IconUsers } from "@tabler/icons-react";
21
+ import { jsx, jsxs } from "react/jsx-runtime";
22
+ function SelectableUserCard({
23
+ user,
24
+ selected,
25
+ onToggle
26
+ }) {
27
+ return (
28
+ // biome-ignore lint/a11y/useSemanticElements: div needed to avoid nested buttons from UserCard
29
+ /* @__PURE__ */ jsx(
30
+ "div",
31
+ {
32
+ role: "button",
33
+ tabIndex: 0,
34
+ onClick: onToggle,
35
+ onKeyDown: (e) => {
36
+ if (e.key === "Enter") {
37
+ onToggle();
38
+ }
39
+ },
40
+ className: cn(
41
+ "cursor-pointer rounded-lg transition-shadow focus:outline-none focus-visible:ring-2 focus-visible:ring-ring",
42
+ selected && "ring-primary ring-2"
43
+ ),
44
+ children: /* @__PURE__ */ jsx(UserCard, { user })
45
+ }
46
+ )
47
+ );
48
+ }
49
+ var userColumns = [
50
+ {
51
+ key: "name",
52
+ header: "Name",
53
+ cell: (user) => /* @__PURE__ */ jsx("p", { className: "font-medium", children: user.fullName })
54
+ },
55
+ {
56
+ key: "contact",
57
+ header: "Contact",
58
+ cell: (user) => /* @__PURE__ */ jsxs("div", { className: "space-y-1", children: [
59
+ user.email && /* @__PURE__ */ jsxs("div", { className: "flex items-center gap-2", children: [
60
+ /* @__PURE__ */ jsx("span", { className: "text-sm", children: user.email }),
61
+ user.emailVerified && /* @__PURE__ */ jsxs(Tooltip, { children: [
62
+ /* @__PURE__ */ jsx(TooltipTrigger, { children: /* @__PURE__ */ jsx(IconCircleCheck, { className: "size-4 text-muted-foreground" }) }),
63
+ /* @__PURE__ */ jsx(TooltipContent, { children: "Email verified" })
64
+ ] })
65
+ ] }),
66
+ user.phone && /* @__PURE__ */ jsxs("div", { className: "flex items-center gap-2", children: [
67
+ /* @__PURE__ */ jsx("span", { className: "text-sm", children: user.phone }),
68
+ user.phoneVerified && /* @__PURE__ */ jsxs(Tooltip, { children: [
69
+ /* @__PURE__ */ jsx(TooltipTrigger, { children: /* @__PURE__ */ jsx(IconCircleCheck, { className: "size-4 text-muted-foreground" }) }),
70
+ /* @__PURE__ */ jsx(TooltipContent, { children: "Phone verified" })
71
+ ] })
72
+ ] }),
73
+ !(user.email || user.phone) && /* @__PURE__ */ jsx("span", { className: "text-muted-foreground", children: "\u2014" })
74
+ ] })
75
+ },
76
+ {
77
+ key: "lastSignIn",
78
+ header: "Last sign in",
79
+ cell: (user) => /* @__PURE__ */ jsxs("div", { className: "flex items-center gap-1 text-muted-foreground", children: [
80
+ /* @__PURE__ */ jsx(IconCalendar, { className: "h-4 w-4" }),
81
+ user.lastSignInAt ? new Date(user.lastSignInAt).toLocaleDateString() : "Never"
82
+ ] })
83
+ }
84
+ ];
85
+ var EMPTY_EXCLUDE_IDS = [];
86
+ function UserSelector({
87
+ trigger,
88
+ multiple = false,
89
+ onSelect,
90
+ excludeIds = EMPTY_EXCLUDE_IDS,
91
+ modalSize = "xl",
92
+ contentClassName
93
+ }) {
94
+ const state = useEntitySectionState({
95
+ defaultSort: "createdAt",
96
+ defaultOrder: "desc",
97
+ defaultPageSize: 10,
98
+ searchParamName: "handle"
99
+ });
100
+ const usersQuery = state.queryConfig;
101
+ const { data, isPending, isFetching } = authApi$.useQuery(
102
+ "get",
103
+ "/users",
104
+ usersQuery,
105
+ defaultEntityQueryOptions
106
+ );
107
+ const users = (data?.users ?? []).filter((user) => {
108
+ return !excludeIds.includes(user.id);
109
+ });
110
+ const config = {
111
+ title: "Select user(s)",
112
+ modalSize,
113
+ contentClassName,
114
+ multiple,
115
+ entityName: "user",
116
+ entityIcon: IconUsers,
117
+ columns: userColumns,
118
+ columnCount: 4,
119
+ getItemLabel: (user) => user.fullName,
120
+ searchPlaceholder: "Search users...",
121
+ filterOptions: [
122
+ { label: "All", value: "" },
123
+ { label: "Email Verified", value: "emailVerified" },
124
+ { label: "Phone Verified", value: "phoneVerified" },
125
+ { label: "Not Verified", value: "notVerified" }
126
+ ],
127
+ sortOptions: [
128
+ { label: "Created", value: "createdAt" },
129
+ { label: "Updated", value: "updatedAt" },
130
+ { label: "Name", value: "fullName" },
131
+ { label: "Last Sign In", value: "lastSignInAt" }
132
+ ],
133
+ showViewToggle: false,
134
+ renderCard: (user, selected, onToggle) => /* @__PURE__ */ jsx(SelectableUserCard, { user, selected, onToggle })
135
+ };
136
+ return /* @__PURE__ */ jsx(
137
+ EntitySelector,
138
+ {
139
+ trigger,
140
+ config,
141
+ onSelect,
142
+ items: users,
143
+ total: data?.total,
144
+ isLoading: isPending || isFetching,
145
+ state
146
+ }
147
+ );
148
+ }
149
+
150
+ export {
151
+ UserSelector
152
+ };
153
+ //# sourceMappingURL=chunk-G74DDR4O.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/pages/iam/users/_components/user-selector.tsx"],"sourcesContent":["'use client';\n\nimport {\n EntitySelector,\n type EntitySelectorColumn,\n type EntitySelectorConfig,\n Tooltip,\n TooltipContent,\n TooltipTrigger,\n useEntitySectionState,\n} from '@mesob/ui/components';\nimport { cn } from '@mesob/ui/lib/utils';\nimport { IconCalendar, IconCircleCheck, IconUsers } from '@tabler/icons-react';\nimport type { ReactNode } from 'react';\nimport type { paths } from '../../../../data/openapi';\nimport { authApi$, defaultEntityQueryOptions } from '../../shared/page-helpers';\nimport { UserCard } from './user-card';\nimport type { User } from './users-data';\n\nfunction SelectableUserCard({\n user,\n selected,\n onToggle,\n}: {\n user: User;\n selected: boolean;\n onToggle: () => void;\n}) {\n return (\n // biome-ignore lint/a11y/useSemanticElements: div needed to avoid nested buttons from UserCard\n <div\n role=\"button\"\n tabIndex={0}\n onClick={onToggle}\n onKeyDown={(e) => {\n if (e.key === 'Enter') {\n onToggle();\n }\n }}\n className={cn(\n 'cursor-pointer rounded-lg transition-shadow focus:outline-none focus-visible:ring-2 focus-visible:ring-ring',\n selected && 'ring-primary ring-2',\n )}\n >\n <UserCard user={user} />\n </div>\n );\n}\n\nconst userColumns: EntitySelectorColumn<User>[] = [\n {\n key: 'name',\n header: 'Name',\n cell: (user: User) => <p className=\"font-medium\">{user.fullName}</p>,\n },\n {\n key: 'contact',\n header: 'Contact',\n cell: (user: User) => (\n <div className=\"space-y-1\">\n {user.email && (\n <div className=\"flex items-center gap-2\">\n <span className=\"text-sm\">{user.email}</span>\n {user.emailVerified && (\n <Tooltip>\n <TooltipTrigger>\n <IconCircleCheck className=\"size-4 text-muted-foreground\" />\n </TooltipTrigger>\n <TooltipContent>Email verified</TooltipContent>\n </Tooltip>\n )}\n </div>\n )}\n {user.phone && (\n <div className=\"flex items-center gap-2\">\n <span className=\"text-sm\">{user.phone}</span>\n {user.phoneVerified && (\n <Tooltip>\n <TooltipTrigger>\n <IconCircleCheck className=\"size-4 text-muted-foreground\" />\n </TooltipTrigger>\n <TooltipContent>Phone verified</TooltipContent>\n </Tooltip>\n )}\n </div>\n )}\n {!(user.email || user.phone) && (\n <span className=\"text-muted-foreground\">—</span>\n )}\n </div>\n ),\n },\n {\n key: 'lastSignIn',\n header: 'Last sign in',\n cell: (user: User) => (\n <div className=\"flex items-center gap-1 text-muted-foreground\">\n <IconCalendar className=\"h-4 w-4\" />\n {user.lastSignInAt\n ? new Date(user.lastSignInAt).toLocaleDateString()\n : 'Never'}\n </div>\n ),\n },\n];\n\nconst EMPTY_EXCLUDE_IDS: string[] = [];\n\ntype UserSelectorProps = {\n trigger: ReactNode;\n multiple?: boolean;\n onSelect: (users: User[]) => void;\n excludeIds?: string[];\n modalSize?: 'sm' | 'md' | 'lg' | 'xl' | 'full';\n contentClassName?: string;\n};\n\nexport function UserSelector({\n trigger,\n multiple = false,\n onSelect,\n excludeIds = EMPTY_EXCLUDE_IDS,\n modalSize = 'xl',\n contentClassName,\n}: UserSelectorProps) {\n const state = useEntitySectionState({\n defaultSort: 'createdAt',\n defaultOrder: 'desc',\n defaultPageSize: 10,\n searchParamName: 'handle',\n });\n const usersQuery = state.queryConfig as {\n params: {\n query: NonNullable<paths['/users']['get']['parameters']['query']>;\n };\n };\n\n const { data, isPending, isFetching } = authApi$.useQuery(\n 'get',\n '/users',\n usersQuery,\n defaultEntityQueryOptions,\n );\n\n const users = (data?.users ?? []).filter((user: User) => {\n return !excludeIds.includes(user.id);\n });\n\n const config: EntitySelectorConfig<User> = {\n title: 'Select user(s)',\n modalSize,\n contentClassName,\n multiple,\n entityName: 'user',\n entityIcon: IconUsers,\n columns: userColumns,\n columnCount: 4,\n getItemLabel: (user: User) => user.fullName,\n searchPlaceholder: 'Search users...',\n filterOptions: [\n { label: 'All', value: '' },\n { label: 'Email Verified', value: 'emailVerified' },\n { label: 'Phone Verified', value: 'phoneVerified' },\n { label: 'Not Verified', value: 'notVerified' },\n ],\n sortOptions: [\n { label: 'Created', value: 'createdAt' },\n { label: 'Updated', value: 'updatedAt' },\n { label: 'Name', value: 'fullName' },\n { label: 'Last Sign In', value: 'lastSignInAt' },\n ],\n showViewToggle: false,\n renderCard: (user: User, selected: boolean, onToggle: () => void) => (\n <SelectableUserCard user={user} selected={selected} onToggle={onToggle} />\n ),\n };\n\n return (\n <EntitySelector<User>\n trigger={trigger}\n config={config}\n onSelect={onSelect}\n items={users}\n total={data?.total}\n isLoading={isPending || isFetching}\n state={state}\n />\n );\n}\n"],"mappings":"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","names":[]}
@@ -1,6 +1,6 @@
1
1
  import {
2
2
  useApi
3
- } from "./chunk-XCJ3ZH7D.js";
3
+ } from "./chunk-AWSAC7RT.js";
4
4
 
5
5
  // src/components/iam/permission-selector.tsx
6
6
  import {
@@ -90,4 +90,4 @@ function PermissionSelector({
90
90
  export {
91
91
  PermissionSelector
92
92
  };
93
- //# sourceMappingURL=chunk-HH347MJI.js.map
93
+ //# sourceMappingURL=chunk-GVEBIL3O.js.map