import { useMutation } from "@tanstack/react-query"; import { AlertCircle, CheckCircle2, Download, FileText, Loader2, Upload, } from "lucide-react"; import * as React from "react"; import { Button } from "../../../components/ui/button"; import { Dialog, DialogContent, DialogDescription, DialogFooter, DialogHeader, DialogTitle, DialogTrigger, } from "../../../components/ui/dialog"; import { ScrollArea } from "../../../components/ui/scroll-area"; import { type BulkUserItem, type BulkUserResult, bulkCreateUsers, } from "../../../lib/adminApi"; import { t } from "../../../lib/i18n"; import { parseUserCSV } from "../utils/csvParser"; interface UserBulkUploadModalProps { onSuccess?: () => void; } export function UserBulkUploadModal({ onSuccess }: UserBulkUploadModalProps) { const [open, setOpen] = React.useState(false); const [file, setFile] = React.useState(null); const [parsing, setParsing] = React.useState(false); const [previewData, setPreviewData] = React.useState([]); const [results, setResults] = React.useState(null); const fileInputRef = React.useRef(null); const mutation = useMutation({ mutationFn: bulkCreateUsers, onSuccess: (data) => { setResults(data.results); onSuccess?.(); }, }); const handleFileChange = (e: React.ChangeEvent) => { const selectedFile = e.target.files?.[0]; if (selectedFile) { setFile(selectedFile); parseCSV(selectedFile); } }; const parseCSV = (file: File) => { setParsing(true); const reader = new FileReader(); reader.onload = (e) => { const text = e.target?.result as string; const data = parseUserCSV(text); setPreviewData(data); setParsing(false); }; reader.readAsText(file); }; const handleUpload = () => { if (previewData.length > 0) { mutation.mutate(previewData); } }; const downloadTemplate = () => { const headers = "email,name,phone,role,tenant,department,position,jobTitle,employee_id"; const example = "user1@example.com,홍길동,010-1234-5678,user,tenant-slug,개발팀,수석,프론트엔드,EMP001"; const blob = new Blob([`${headers}\n${example}`], { type: "text/csv;charset=utf-8;", }); const url = URL.createObjectURL(blob); const a = document.createElement("a"); a.href = url; a.download = "user_bulk_template.csv"; a.click(); URL.revokeObjectURL(url); }; const reset = () => { setFile(null); setPreviewData([]); setResults(null); if (fileInputRef.current) fileInputRef.current.value = ""; }; const successCount = results?.filter((r) => r.success).length ?? 0; const failCount = results ? results.length - successCount : 0; return ( { setOpen(val); if (!val) reset(); }} > {t("ui.admin.users.bulk.title", "사용자 일괄 등록")} {t( "msg.admin.users.bulk.description", "CSV 파일을 업로드하여 여러 사용자를 한 번에 등록합니다.", )} {!results ? (
{file && (
{file.name} ({(file.size / 1024).toFixed(1)} KB)
{parsing ? (
{t("msg.common.parsing", "파싱 중...")}
) : (
{t( "msg.admin.users.bulk.parsed_count", "{{count}}명의 사용자가 감지되었습니다.", { count: previewData.length }, )}
)}
)} {previewData.length > 0 && ( {previewData.slice(0, 10).map((u) => ( ))} {previewData.length > 10 && ( )}
Email Name Tenant
{u.email} {u.name} {u.tenantSlug || "-"}
... and {previewData.length - 10} more users
)}
) : (
{successCount}
{t("ui.common.success", "성공")}
{failCount}
{t("ui.common.fail", "실패")}
{results.map((r) => (
{r.success ? ( ) : ( )}
{r.email}
{!r.success && (
{r.message}
)}
))}
)} {!results ? ( ) : ( )}
); }