import { useMutation, useQuery, useQueryClient } from "@tanstack/react-query"; import type { AxiosError } from "axios"; import { Plus, Save, Trash2 } from "lucide-react"; import { useEffect, useState } from "react"; import { useParams } 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 { toast } from "../../../components/ui/use-toast"; import { fetchTenant, updateTenant } from "../../../lib/adminApi"; import { t } from "../../../lib/i18n"; import { useTenantPermission } from "../hooks/useTenantPermission"; import { createSchemaField, isSchemaFieldType, normalizeSchemaField, type SchemaField, } from "./tenantSchemaFields"; export function TenantSchemaPage() { const { tenantId } = useParams<{ tenantId: string }>(); const queryClient = useQueryClient(); const { hasPermission, isLoading: isPermissionLoading } = useTenantPermission( tenantId ?? "", ); const canView = hasPermission("view_schema") || hasPermission("view"); const isWritable = hasPermission("manage_schema") || hasPermission("manage"); const tenantQuery = useQuery({ queryKey: ["tenant", tenantId], queryFn: () => { if (!tenantId) throw new Error("Tenant ID is required"); return fetchTenant(tenantId); }, enabled: !!tenantId && canView, }); const [fields, setFields] = useState([]); useEffect(() => { const rawSchema = tenantQuery.data?.config?.userSchema; if (Array.isArray(rawSchema)) { setFields(rawSchema.map(normalizeSchemaField)); } }, [tenantQuery.data]); const updateMutation = useMutation({ mutationFn: (newFields: SchemaField[]) => { if (!tenantId) throw new Error("Tenant ID is required"); // Remove legacy loginIdField, keep isLoginId natively in userSchema const newConfig = { ...tenantQuery.data?.config }; newConfig.loginIdField = undefined; newConfig.userSchema = newFields; return updateTenant(tenantId, { config: newConfig, }); }, onSuccess: () => { queryClient.invalidateQueries({ queryKey: ["tenant", tenantId] }); toast.success( t( "msg.admin.tenants.schema.update_success", "스키마가 저장되었습니다.", ), ); }, onError: (err: AxiosError<{ error?: string }>) => { toast.error( err.response?.data?.error || t("msg.admin.tenants.schema.update_error", "저장에 실패했습니다."), ); }, }); if (isPermissionLoading) { return (
{t("msg.common.loading", "로딩 중...")}
); } if (!canView) { return (

{t("msg.common.forbidden", "접근 권한이 없습니다.")}

{t( "msg.admin.tenants.schema.forbidden_desc", "사용자 스키마 설정은 관리자만 접근할 수 있습니다.", )}

); } if (!tenantId) { return (
{t("msg.admin.tenants.schema.missing_id", "테넌트 ID가 없습니다.")}
); } const addField = () => { setFields([...fields, createSchemaField()]); }; const removeField = (index: number) => { setFields(fields.filter((_, i) => i !== index)); }; const updateField = (index: number, updates: Partial) => { const newFields = [...fields]; newFields[index] = { ...newFields[index], ...updates }; setFields(newFields); }; return (
{t("ui.admin.tenants.schema.title", "사용자 스키마 확장")} {t( "msg.admin.tenants.schema.subtitle", "이 테넌트 사용자를 위한 커스텀 속성을 정의합니다.", )}
{fields.length === 0 && (
{t( "msg.admin.tenants.schema.empty", '정의된 커스텀 필드가 없습니다. "필드 추가"를 눌러 시작하세요.', )}
)} {fields.map((field, index) => (
updateField(index, { key: e.target.value }) } placeholder={t( "ui.admin.tenants.schema.field.key_placeholder", "예: employee_id", )} className="h-10" disabled={!isWritable} />
updateField(index, { label: e.target.value }) } placeholder={t( "ui.admin.tenants.schema.field.label_placeholder", "예: 사번", )} className="h-10" disabled={!isWritable} />
{(field.type === "number" || field.type === "float") && ( )}
updateField(index, { validation: e.target.value }) } placeholder={t( "ui.admin.tenants.schema.field.validation_placeholder", "정규식 (예: ^[0-9]+$)", )} className="h-9 text-xs font-mono" />
))}
); }