@mesob/auth-react 0.4.3 → 0.4.5
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.
- package/dist/{chunk-XEFPJKBB.js → chunk-4ABXALRN.js} +2 -2
- package/dist/{chunk-DXTZQ2VY.js → chunk-4NUO6F3J.js} +2 -2
- package/dist/{chunk-CC3HI442.js → chunk-5AEV7RAN.js} +3 -3
- package/dist/{chunk-VA2XUED4.js → chunk-5HAABEAS.js} +2 -2
- package/dist/{chunk-EKZYBYZQ.js → chunk-5SDS2E3F.js} +2 -2
- package/dist/{chunk-ODDTS4RN.js → chunk-6IEX2RLA.js} +3 -3
- package/dist/{chunk-HM4MAF2I.js → chunk-A3GIMM2R.js} +3 -3
- package/dist/{chunk-4QMUNU5E.js → chunk-A7ORWWM5.js} +6 -4
- package/dist/{chunk-4QMUNU5E.js.map → chunk-A7ORWWM5.js.map} +1 -1
- package/dist/{chunk-XCJ3ZH7D.js → chunk-AWSAC7RT.js} +1 -1
- package/dist/{chunk-XCJ3ZH7D.js.map → chunk-AWSAC7RT.js.map} +1 -1
- package/dist/{chunk-V4VX55TT.js → chunk-CP4TTRV4.js} +2 -2
- package/dist/{chunk-GB3Q5IWT.js → chunk-CXMPZWMX.js} +4 -4
- package/dist/{chunk-AKEBT5PJ.js → chunk-DG6GRTPG.js} +2 -2
- package/dist/{chunk-KTZSXSHV.js → chunk-DY3NVBLJ.js} +2 -2
- package/dist/{chunk-OK7WPIHQ.js → chunk-EPEXIGKB.js} +2 -2
- package/dist/{chunk-F6IMR5XS.js → chunk-EQWOGD4F.js} +2 -2
- package/dist/{chunk-RZFSMM3X.js → chunk-ETOCBXDT.js} +3 -3
- package/dist/{chunk-LNG736CV.js → chunk-EWXK56WQ.js} +1 -1
- package/dist/chunk-FPYQ7XGV.js +415 -0
- package/dist/chunk-FPYQ7XGV.js.map +1 -0
- package/dist/chunk-G74DDR4O.js +153 -0
- package/dist/chunk-G74DDR4O.js.map +1 -0
- package/dist/{chunk-HH347MJI.js → chunk-GVEBIL3O.js} +2 -2
- package/dist/{chunk-M6ZUEZJT.js → chunk-HXHI4FU6.js} +3 -3
- package/dist/{chunk-VU4Z2XSQ.js → chunk-IFAVSKHE.js} +3 -3
- package/dist/{chunk-WD2COCQW.js → chunk-KOBZ34XU.js} +4 -4
- package/dist/{chunk-ZE3R37PI.js → chunk-L32Z3TPA.js} +213 -54
- package/dist/chunk-L32Z3TPA.js.map +1 -0
- package/dist/{chunk-CDECLPL4.js → chunk-LHZ4EEN6.js} +3 -3
- package/dist/{chunk-REHGRIUT.js → chunk-MQI6Q2S4.js} +7 -25
- package/dist/chunk-MQI6Q2S4.js.map +1 -0
- package/dist/{chunk-6F5LEXYI.js → chunk-MVVAPYUD.js} +2 -2
- package/dist/chunk-NUWAI3FE.js +350 -0
- package/dist/chunk-NUWAI3FE.js.map +1 -0
- package/dist/{chunk-S3KIMVDE.js → chunk-OQUUS5ZX.js} +4 -4
- package/dist/chunk-OW75JENQ.js +19 -0
- package/dist/chunk-OW75JENQ.js.map +1 -0
- package/dist/{chunk-KM5GOJ75.js → chunk-PG65ZD7A.js} +4 -4
- package/dist/{chunk-NFBOIG3D.js → chunk-RBXITSE2.js} +5 -5
- package/dist/{chunk-BMZTYXGO.js → chunk-RMJNENJB.js} +2 -2
- package/dist/{chunk-5NKPFZ2Q.js → chunk-SGDXNT7M.js} +3 -3
- package/dist/{chunk-SJHJ63HK.js → chunk-SW7WD64K.js} +15 -6
- package/dist/chunk-SW7WD64K.js.map +1 -0
- package/dist/{chunk-ABF34UFW.js → chunk-SXVTYYUT.js} +3 -3
- package/dist/{chunk-ABF34UFW.js.map → chunk-SXVTYYUT.js.map} +1 -1
- package/dist/{chunk-GIGXCAGH.js → chunk-TB2ZPGLW.js} +3 -3
- package/dist/{chunk-RJBNA7OH.js → chunk-TLQMK2QF.js} +4 -4
- package/dist/{chunk-RXHL7LUV.js → chunk-UAKGEJUN.js} +3 -3
- package/dist/{chunk-3HV5KQFZ.js → chunk-UGQP733V.js} +2 -2
- package/dist/{chunk-YHZLPAYD.js → chunk-UY55LEIG.js} +2 -2
- package/dist/{chunk-QJVZHT27.js → chunk-W3D4HG5W.js} +3 -3
- package/dist/{chunk-YLWCA2WK.js → chunk-WQ3UUUKF.js} +10 -3
- package/dist/chunk-WQ3UUUKF.js.map +1 -0
- package/dist/{chunk-JRHJNMG3.js → chunk-WY2JJNZW.js} +2 -2
- package/dist/{chunk-4BFHC4JI.js → chunk-X2BHF4KC.js} +2 -2
- package/dist/{chunk-SYT5Z7EF.js → chunk-YFQNNSSC.js} +2 -2
- package/dist/{chunk-JED4SG3W.js → chunk-YN7OEQI7.js} +4 -4
- package/dist/{chunk-JED4SG3W.js.map → chunk-YN7OEQI7.js.map} +1 -1
- package/dist/{chunk-PS2KBGDB.js → chunk-Z34NJZRL.js} +3 -3
- package/dist/{chunk-EGZYJMSA.js → chunk-ZXKEG3X5.js} +2 -2
- package/dist/{chunk-L2W6FXSZ.js → chunk-ZZ6D4KE4.js} +2 -2
- package/dist/components/auth/countdown.js +3 -3
- package/dist/components/auth/forgot-password.js +3 -3
- package/dist/components/auth/reset-password-form.js +3 -3
- package/dist/components/auth/set-password.js +3 -3
- package/dist/components/auth/sign-in.js +3 -3
- package/dist/components/auth/sign-up.js +3 -3
- package/dist/components/auth/verification-form.js +4 -4
- package/dist/components/auth/verify-email.js +5 -5
- package/dist/components/auth/verify-phone.js +5 -5
- package/dist/components/authorization/deny.js +2 -2
- package/dist/components/authorization/grant.js +2 -2
- package/dist/components/iam/permission-selector.js +2 -2
- package/dist/components/iam/permissions-page.js +2 -2
- package/dist/components/iam/permissions.js +2 -2
- package/dist/components/iam/role-detail-layout.js +2 -2
- package/dist/components/iam/role-detail-page.js +2 -2
- package/dist/components/iam/role-permissions-page.js +3 -3
- package/dist/components/iam/roles-page.js +6 -5
- package/dist/components/iam/roles.js +2 -2
- package/dist/components/iam/sessions-page.js +4 -4
- package/dist/components/iam/sessions.js +2 -2
- package/dist/components/iam/tenants-page.js +5 -5
- package/dist/components/iam/tenants.js +2 -2
- package/dist/components/iam/users-page.js +6 -5
- package/dist/components/iam/users.js +2 -2
- package/dist/components/profile/account.js +2 -2
- package/dist/components/profile/change-email-form.js +8 -8
- package/dist/components/profile/change-password-form.js +2 -2
- package/dist/components/profile/change-phone-form.js +8 -8
- package/dist/components/profile/otp-verification-modal.js +5 -5
- package/dist/components/profile/profile-layout.js +3 -3
- package/dist/components/profile/request-change-email-form.js +2 -2
- package/dist/components/profile/request-change-phone-form.js +2 -2
- package/dist/components/profile/security.js +13 -13
- package/dist/components/profile/verify-change-email-form.js +6 -6
- package/dist/components/profile/verify-change-phone-form.js +6 -6
- package/dist/index.js +50 -48
- package/dist/index.js.map +1 -1
- package/dist/pages/auth/forgot-password.js +3 -3
- package/dist/pages/auth/layout.js +1 -1
- package/dist/pages/auth/reset-password.js +3 -3
- package/dist/pages/auth/set-password.js +3 -3
- package/dist/pages/auth/sign-in.js +3 -3
- package/dist/pages/auth/sign-up.js +3 -3
- package/dist/pages/auth/verify-email.js +5 -5
- package/dist/pages/auth/verify-phone.js +5 -5
- package/dist/pages/iam/permissions.js +2 -2
- package/dist/pages/iam/role-detail-layout.js +2 -2
- package/dist/pages/iam/role-detail.js +2 -2
- package/dist/pages/iam/role-permissions.js +3 -3
- package/dist/pages/iam/role-users.js +7 -5
- package/dist/pages/iam/role-users.js.map +1 -1
- package/dist/pages/iam/roles.js +6 -5
- package/dist/pages/iam/sessions.js +4 -4
- package/dist/pages/iam/tenant-detail.js +3 -3
- package/dist/pages/iam/tenants/tenant-selector.js +150 -0
- package/dist/pages/iam/tenants/tenant-selector.js.map +1 -0
- package/dist/pages/iam/tenants/tenants-data.js +7 -0
- package/dist/pages/iam/tenants/tenants-data.js.map +1 -0
- package/dist/pages/iam/tenants.js +5 -5
- package/dist/pages/iam/user-activity.js +6 -5
- package/dist/pages/iam/user-activity.js.map +1 -1
- package/dist/pages/iam/user-detail-layout.js +2 -2
- package/dist/pages/iam/user-detail.js +2 -2
- package/dist/pages/iam/users/_components/users-data.d.ts +4 -4
- package/dist/pages/iam/users/user-selector.js +14 -0
- package/dist/pages/iam/users/user-selector.js.map +1 -0
- package/dist/pages/iam/users/users-data.js +1 -0
- package/dist/pages/iam/users/users-data.js.map +1 -0
- package/dist/pages/iam/users.js +6 -5
- package/dist/pages/profile/account.js +2 -2
- package/dist/pages/profile/layout.js +3 -3
- package/dist/pages/profile/security.js +13 -13
- package/dist/types.d.ts +2 -0
- package/package.json +20 -3
- package/dist/chunk-GYFHM72X.js +0 -486
- package/dist/chunk-GYFHM72X.js.map +0 -1
- package/dist/chunk-REHGRIUT.js.map +0 -1
- package/dist/chunk-SJHJ63HK.js.map +0 -1
- package/dist/chunk-UCNNUST6.js +0 -271
- package/dist/chunk-UCNNUST6.js.map +0 -1
- package/dist/chunk-YLWCA2WK.js.map +0 -1
- package/dist/chunk-ZE3R37PI.js.map +0 -1
- /package/dist/{chunk-XEFPJKBB.js.map → chunk-4ABXALRN.js.map} +0 -0
- /package/dist/{chunk-DXTZQ2VY.js.map → chunk-4NUO6F3J.js.map} +0 -0
- /package/dist/{chunk-CC3HI442.js.map → chunk-5AEV7RAN.js.map} +0 -0
- /package/dist/{chunk-VA2XUED4.js.map → chunk-5HAABEAS.js.map} +0 -0
- /package/dist/{chunk-EKZYBYZQ.js.map → chunk-5SDS2E3F.js.map} +0 -0
- /package/dist/{chunk-ODDTS4RN.js.map → chunk-6IEX2RLA.js.map} +0 -0
- /package/dist/{chunk-HM4MAF2I.js.map → chunk-A3GIMM2R.js.map} +0 -0
- /package/dist/{chunk-V4VX55TT.js.map → chunk-CP4TTRV4.js.map} +0 -0
- /package/dist/{chunk-GB3Q5IWT.js.map → chunk-CXMPZWMX.js.map} +0 -0
- /package/dist/{chunk-AKEBT5PJ.js.map → chunk-DG6GRTPG.js.map} +0 -0
- /package/dist/{chunk-KTZSXSHV.js.map → chunk-DY3NVBLJ.js.map} +0 -0
- /package/dist/{chunk-OK7WPIHQ.js.map → chunk-EPEXIGKB.js.map} +0 -0
- /package/dist/{chunk-F6IMR5XS.js.map → chunk-EQWOGD4F.js.map} +0 -0
- /package/dist/{chunk-RZFSMM3X.js.map → chunk-ETOCBXDT.js.map} +0 -0
- /package/dist/{chunk-LNG736CV.js.map → chunk-EWXK56WQ.js.map} +0 -0
- /package/dist/{chunk-HH347MJI.js.map → chunk-GVEBIL3O.js.map} +0 -0
- /package/dist/{chunk-M6ZUEZJT.js.map → chunk-HXHI4FU6.js.map} +0 -0
- /package/dist/{chunk-VU4Z2XSQ.js.map → chunk-IFAVSKHE.js.map} +0 -0
- /package/dist/{chunk-WD2COCQW.js.map → chunk-KOBZ34XU.js.map} +0 -0
- /package/dist/{chunk-CDECLPL4.js.map → chunk-LHZ4EEN6.js.map} +0 -0
- /package/dist/{chunk-6F5LEXYI.js.map → chunk-MVVAPYUD.js.map} +0 -0
- /package/dist/{chunk-S3KIMVDE.js.map → chunk-OQUUS5ZX.js.map} +0 -0
- /package/dist/{chunk-KM5GOJ75.js.map → chunk-PG65ZD7A.js.map} +0 -0
- /package/dist/{chunk-NFBOIG3D.js.map → chunk-RBXITSE2.js.map} +0 -0
- /package/dist/{chunk-BMZTYXGO.js.map → chunk-RMJNENJB.js.map} +0 -0
- /package/dist/{chunk-5NKPFZ2Q.js.map → chunk-SGDXNT7M.js.map} +0 -0
- /package/dist/{chunk-GIGXCAGH.js.map → chunk-TB2ZPGLW.js.map} +0 -0
- /package/dist/{chunk-RJBNA7OH.js.map → chunk-TLQMK2QF.js.map} +0 -0
- /package/dist/{chunk-RXHL7LUV.js.map → chunk-UAKGEJUN.js.map} +0 -0
- /package/dist/{chunk-3HV5KQFZ.js.map → chunk-UGQP733V.js.map} +0 -0
- /package/dist/{chunk-YHZLPAYD.js.map → chunk-UY55LEIG.js.map} +0 -0
- /package/dist/{chunk-QJVZHT27.js.map → chunk-W3D4HG5W.js.map} +0 -0
- /package/dist/{chunk-JRHJNMG3.js.map → chunk-WY2JJNZW.js.map} +0 -0
- /package/dist/{chunk-4BFHC4JI.js.map → chunk-X2BHF4KC.js.map} +0 -0
- /package/dist/{chunk-SYT5Z7EF.js.map → chunk-YFQNNSSC.js.map} +0 -0
- /package/dist/{chunk-PS2KBGDB.js.map → chunk-Z34NJZRL.js.map} +0 -0
- /package/dist/{chunk-EGZYJMSA.js.map → chunk-ZXKEG3X5.js.map} +0 -0
- /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":";;;;;;;;;;;AAEA;AAAA,EACE;AAAA,EAGA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,OACK;AACP,SAAS,UAAU;AACnB,SAAS,cAAc,iBAAiB,iBAAiB;AAgCnD,cAoBQ,YApBR;AAzBN,SAAS,mBAAmB;AAAA,EAC1B;AAAA,EACA;AAAA,EACA;AACF,GAIG;AACD;AAAA;AAAA,IAEE;AAAA,MAAC;AAAA;AAAA,QACC,MAAK;AAAA,QACL,UAAU;AAAA,QACV,SAAS;AAAA,QACT,WAAW,CAAC,MAAM;AAChB,cAAI,EAAE,QAAQ,SAAS;AACrB,qBAAS;AAAA,UACX;AAAA,QACF;AAAA,QACA,WAAW;AAAA,UACT;AAAA,UACA,YAAY;AAAA,QACd;AAAA,QAEA,8BAAC,YAAS,MAAY;AAAA;AAAA,IACxB;AAAA;AAEJ;AAEA,IAAM,cAA4C;AAAA,EAChD;AAAA,IACE,KAAK;AAAA,IACL,QAAQ;AAAA,IACR,MAAM,CAAC,SAAe,oBAAC,OAAE,WAAU,eAAe,eAAK,UAAS;AAAA,EAClE;AAAA,EACA;AAAA,IACE,KAAK;AAAA,IACL,QAAQ;AAAA,IACR,MAAM,CAAC,SACL,qBAAC,SAAI,WAAU,aACZ;AAAA,WAAK,SACJ,qBAAC,SAAI,WAAU,2BACb;AAAA,4BAAC,UAAK,WAAU,WAAW,eAAK,OAAM;AAAA,QACrC,KAAK,iBACJ,qBAAC,WACC;AAAA,8BAAC,kBACC,8BAAC,mBAAgB,WAAU,gCAA+B,GAC5D;AAAA,UACA,oBAAC,kBAAe,4BAAc;AAAA,WAChC;AAAA,SAEJ;AAAA,MAED,KAAK,SACJ,qBAAC,SAAI,WAAU,2BACb;AAAA,4BAAC,UAAK,WAAU,WAAW,eAAK,OAAM;AAAA,QACrC,KAAK,iBACJ,qBAAC,WACC;AAAA,8BAAC,kBACC,8BAAC,mBAAgB,WAAU,gCAA+B,GAC5D;AAAA,UACA,oBAAC,kBAAe,4BAAc;AAAA,WAChC;AAAA,SAEJ;AAAA,MAED,EAAE,KAAK,SAAS,KAAK,UACpB,oBAAC,UAAK,WAAU,yBAAwB,oBAAC;AAAA,OAE7C;AAAA,EAEJ;AAAA,EACA;AAAA,IACE,KAAK;AAAA,IACL,QAAQ;AAAA,IACR,MAAM,CAAC,SACL,qBAAC,SAAI,WAAU,iDACb;AAAA,0BAAC,gBAAa,WAAU,WAAU;AAAA,MACjC,KAAK,eACF,IAAI,KAAK,KAAK,YAAY,EAAE,mBAAmB,IAC/C;AAAA,OACN;AAAA,EAEJ;AACF;AAEA,IAAM,oBAA8B,CAAC;AAW9B,SAAS,aAAa;AAAA,EAC3B;AAAA,EACA,WAAW;AAAA,EACX;AAAA,EACA,aAAa;AAAA,EACb,YAAY;AAAA,EACZ;AACF,GAAsB;AACpB,QAAM,QAAQ,sBAAsB;AAAA,IAClC,aAAa;AAAA,IACb,cAAc;AAAA,IACd,iBAAiB;AAAA,IACjB,iBAAiB;AAAA,EACnB,CAAC;AACD,QAAM,aAAa,MAAM;AAMzB,QAAM,EAAE,MAAM,WAAW,WAAW,IAAI,SAAS;AAAA,IAC/C;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,EACF;AAEA,QAAM,SAAS,MAAM,SAAS,CAAC,GAAG,OAAO,CAAC,SAAe;AACvD,WAAO,CAAC,WAAW,SAAS,KAAK,EAAE;AAAA,EACrC,CAAC;AAED,QAAM,SAAqC;AAAA,IACzC,OAAO;AAAA,IACP;AAAA,IACA;AAAA,IACA;AAAA,IACA,YAAY;AAAA,IACZ,YAAY;AAAA,IACZ,SAAS;AAAA,IACT,aAAa;AAAA,IACb,cAAc,CAAC,SAAe,KAAK;AAAA,IACnC,mBAAmB;AAAA,IACnB,eAAe;AAAA,MACb,EAAE,OAAO,OAAO,OAAO,GAAG;AAAA,MAC1B,EAAE,OAAO,kBAAkB,OAAO,gBAAgB;AAAA,MAClD,EAAE,OAAO,kBAAkB,OAAO,gBAAgB;AAAA,MAClD,EAAE,OAAO,gBAAgB,OAAO,cAAc;AAAA,IAChD;AAAA,IACA,aAAa;AAAA,MACX,EAAE,OAAO,WAAW,OAAO,YAAY;AAAA,MACvC,EAAE,OAAO,WAAW,OAAO,YAAY;AAAA,MACvC,EAAE,OAAO,QAAQ,OAAO,WAAW;AAAA,MACnC,EAAE,OAAO,gBAAgB,OAAO,eAAe;AAAA,IACjD;AAAA,IACA,gBAAgB;AAAA,IAChB,YAAY,CAAC,MAAY,UAAmB,aAC1C,oBAAC,sBAAmB,MAAY,UAAoB,UAAoB;AAAA,EAE5E;AAEA,SACE;AAAA,IAAC;AAAA;AAAA,MACC;AAAA,MACA;AAAA,MACA;AAAA,MACA,OAAO;AAAA,MACP,OAAO,MAAM;AAAA,MACb,WAAW,aAAa;AAAA,MACxB;AAAA;AAAA,EACF;AAEJ;","names":[]}
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import {
|
|
2
2
|
useApi
|
|
3
|
-
} from "./chunk-
|
|
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-
|
|
93
|
+
//# sourceMappingURL=chunk-GVEBIL3O.js.map
|