diff --git a/adminfront/src/features/users/UserCreatePage.tsx b/adminfront/src/features/users/UserCreatePage.tsx index a8e6dfbf..d47a49c5 100644 --- a/adminfront/src/features/users/UserCreatePage.tsx +++ b/adminfront/src/features/users/UserCreatePage.tsx @@ -8,6 +8,8 @@ import { Plus, Save, Trash2, + Mail, + X, } from "lucide-react"; import * as React from "react"; import { useForm } from "react-hook-form"; @@ -58,7 +60,11 @@ import { import type { UserSchemaField } from "./userSchemaFields"; import { resolvePersonalTenant } from "./utils/personalTenant"; -type UserFormValues = UserCreateRequest & { metadata: Record }; +type UserFormValues = UserCreateRequest & { + metadata: Record & { + sub_email?: string[]; + }; +}; type UserCategory = "hanmac" | "external" | "personal"; type PickerTarget = { kind: "appointment"; index: number }; @@ -135,6 +141,8 @@ function UserCreatePage() { ); const [isResolvingTenant, setIsResolvingTenant] = React.useState(false); + const [newSubEmail, setNewSubEmail] = React.useState(""); + const { data: tenantsData } = useQuery({ queryKey: ["tenants", "all"], queryFn: () => fetchAllTenants(), @@ -164,10 +172,35 @@ function UserCreatePage() { position: "", jobTitle: "", role: "user", - metadata: {}, + metadata: { + sub_email: [], + }, }, }); + const currentSubEmails = (watch("metadata.sub_email") as string[]) || []; + + const handleAddSubEmail = (e: React.KeyboardEvent) => { + if (e.key === "Enter" || e.key === "," || e.key === " ") { + e.preventDefault(); + const value = newSubEmail.trim().replace(/,/g, ""); + if (value && value.includes("@") && !currentSubEmails.includes(value)) { + setValue("metadata.sub_email", [...currentSubEmails, value], { + shouldDirty: true, + }); + setNewSubEmail(""); + } + } + }; + + const handleRemoveSubEmail = (emailToRemove: string) => { + setValue( + "metadata.sub_email", + currentSubEmails.filter((e) => e !== emailToRemove), + { shouldDirty: true }, + ); + }; + // Lock company for tenant_admin React.useEffect(() => { if (profile?.role === "tenant_admin" && profile.tenantSlug) { @@ -370,10 +403,15 @@ function UserCreatePage() { const { hanmacFamily: _hanmacFamily, userType: _userType, + sub_email: rawSubEmail, ...formMetadata } = data.metadata ?? {}; + + const sub_email = Array.isArray(rawSubEmail) ? rawSubEmail : []; + const metadata: Record = { ...formMetadata, + ...(sub_email.length > 0 ? { sub_email } : { sub_email: [] }), }; const payload: UserCreateRequest = { @@ -584,6 +622,73 @@ function UserCreatePage() { )} +
+ +
+
+ {currentSubEmails.map((email) => ( +
+ {email} + +
+ ))} +
+
+ setNewSubEmail(e.target.value)} + onKeyDown={handleAddSubEmail} + className="pr-20" + placeholder={t( + "ui.admin.users.create.form.sub_email_placeholder", + "추가할 이메일 입력 후 Enter", + )} + /> + +
+

+ * 여러 개 입력 가능. 입력 후 엔터를 눌러 추가하세요. +

+
+
+
+ {user.metadata?.sub_email && + Array.isArray(user.metadata.sub_email) && + user.metadata.sub_email.length > 0 && ( +
+ + + +{user.metadata.sub_email.length} + +
+ )} {user.phone && (
@@ -1055,6 +1119,80 @@ function UserDetailPage() {
+
+
+ +
+
+ {currentSubEmails.map((email) => ( + + {email} + + + ))} +
+
+ setNewSubEmail(e.target.value)} + onKeyDown={handleAddSubEmail} + className="h-11 shadow-sm pr-20" + placeholder={t( + "ui.admin.users.detail.form.sub_email_placeholder", + "추가할 이메일 입력 후 Enter", + )} + /> + +
+

+ {t( + "msg.admin.users.detail.sub_email_help", + "* 보조 이메일로도 로그인이 가능하며 계정 찾기 등에 활용될 수 있습니다.", + )} +

+
+
+
+