import { useMutation, useQuery, useQueryClient } from "@tanstack/react-query"; import type { AxiosError } from "axios"; import { ArrowLeft, ClipboardCopy, Loader2, Save } from "lucide-react"; import * as React from "react"; import { useForm } from "react-hook-form"; import { Link, useNavigate } from "react-router-dom"; import { Button } from "../../components/ui/button"; import { Card, CardContent, CardDescription, CardHeader, CardTitle, } from "../../components/ui/card"; import { Input } from "../../components/ui/input"; import { Label } from "../../components/ui/label"; import { type UserCreateRequest, type UserCreateResponse, createUser, fetchTenant, fetchTenants, fetchMe, } from "../../lib/adminApi"; import { t } from "../../lib/i18n"; type UserSchemaField = { key: string; label?: string; type?: "text" | "number" | "boolean" | "date"; required?: boolean; adminOnly?: boolean; validation?: string; }; type UserFormValues = UserCreateRequest & { metadata: Record }; function UserCreatePage() { const navigate = useNavigate(); const queryClient = useQueryClient(); const [error, setError] = React.useState(null); const [generatedPassword, setGeneratedPassword] = React.useState< string | null >(null); const [createdEmail, setCreatedEmail] = React.useState(null); const [autoPassword, setAutoPassword] = React.useState(true); const { data: tenantsData } = useQuery({ queryKey: ["tenants", { limit: 100 }], queryFn: () => fetchTenants(100, 0), }); const tenants = tenantsData?.items ?? []; const { data: profile } = useQuery({ queryKey: ["me"], queryFn: fetchMe, }); const { register, handleSubmit, watch, setValue, formState: { errors }, } = useForm({ defaultValues: { email: "", password: "", name: "", phone: "", role: "user", companyCode: "", department: "", position: "", jobTitle: "", metadata: {}, }, }); // Lock company for tenant_admin React.useEffect(() => { const p = profile as any; if (p?.role === "tenant_admin" && p.companyCode) { setValue("companyCode", p.companyCode); } }, [profile, setValue]); const selectedCompanyCode = watch("companyCode"); const selectedTenant = tenants.find((t) => t.slug === selectedCompanyCode); const selectedTenantId = selectedTenant?.id ?? ""; const { data: tenantDetail } = useQuery({ queryKey: ["tenant", selectedTenantId], queryFn: () => fetchTenant(selectedTenantId), enabled: selectedTenantId.length > 0, }); const userSchema: UserSchemaField[] = Array.isArray( tenantDetail?.config?.userSchema, ) ? (tenantDetail?.config?.userSchema as UserSchemaField[]) : []; const registerMetadata = (field: UserSchemaField) => register(`metadata.${field.key}` as `metadata.${string}`, { required: field.required ? t( "msg.admin.users.create.form.field_required", "{{label}}은(는) 필수입니다.", { label: field.label || field.key, }, ) : false, pattern: field.validation ? { value: new RegExp(field.validation), message: t( "msg.admin.users.create.form.field_invalid", "{{label}} 형식이 올바르지 않습니다.", { label: field.label || field.key }, ), } : undefined, }); const mutation = useMutation({ mutationFn: createUser, onSuccess: (data: UserCreateResponse) => { queryClient.invalidateQueries({ queryKey: ["users"] }); if (data.initialPassword) { setGeneratedPassword(data.initialPassword); setCreatedEmail(data.email); return; } navigate("/users"); }, onError: (err: AxiosError<{ error?: string }>) => { setError( err.response?.data?.error || t("msg.admin.users.create.error", "사용자 생성에 실패했습니다."), ); }, }); const onSubmit = (data: UserFormValues) => { setError(null); setGeneratedPassword(null); setCreatedEmail(null); const payload = { ...data }; if (autoPassword) { payload.password = ""; } else if (!data.password) { setError( t( "msg.admin.users.create.password_required", "비밀번호를 입력하거나 자동 생성을 사용해 주세요.", ), ); return; } mutation.mutate(payload); }; const onCopyPassword = async () => { if (!generatedPassword) return; try { await navigator.clipboard.writeText(generatedPassword); } catch (_) { // ignore } }; return (

{t("ui.admin.users.create.title", "사용자 추가")}

{generatedPassword && ( {t( "ui.admin.users.create.password_generated.title", "초기 비밀번호 생성 완료", )} {createdEmail ? t( "msg.admin.users.create.password_generated.with_email", "{{email}} 계정의 초기 비밀번호입니다.", { email: createdEmail }, ) : t( "msg.admin.users.create.password_generated.default", "초기 비밀번호가 생성되었습니다.", )}
{generatedPassword}
)} {t("ui.admin.users.create.account.title", "계정 정보")} {t( "msg.admin.users.create.account.subtitle", "새로운 사용자를 시스템에 등록합니다.", )}
{error && (
{error}
)}
{errors.email && (

{errors.email.message}

)}

{autoPassword ? t( "msg.admin.users.create.form.password_auto_help", "비워두면 시스템이 초기 비밀번호를 자동 생성합니다.", ) : t( "msg.admin.users.create.form.password_manual_help", "초기 비밀번호를 직접 설정합니다.", )}

{errors.name && (

{errors.name.message}

)}
{userSchema.length > 0 && (

{t( "ui.admin.users.create.custom_fields.title", "테넌트 확장 정보 (Custom Fields)", )}

{userSchema.map((field) => (
{errors.metadata?.[field.key] && (

{ (errors.metadata[field.key] as { message?: string }) ?.message }

)}
))}
)}

{t( "msg.admin.users.create.form.role_help", "시스템 접근 권한을 결정합니다.", )}

); } export default UserCreatePage;