/** * ๐Ÿ“„ File Upload Form Component * ํŒŒ์ผ ์—…๋กœ๋“œ ํผ ์ปดํฌ๋„ŒํŠธ * * - ํŒŒ์ผ ์„ ํƒ ๋ฐ ์—…๋กœ๋“œ ๊ธฐ๋Šฅ * - ๋…„๋„ ๋ฐ ๋ถ„๊ธฐ ์„ ํƒ ์˜ต์…˜ ์ œ๊ณต * - ํŒŒ์ผ ๋“ฑ๋ก ๋ฐ ์ฝœ๋ฐฑ ์ฒ˜๋ฆฌ * - ์—…๋กœ๋“œ ์ง„ํ–‰๋ฅ  ๋ฐ ์„ฑ๊ณต/์‹คํŒจ ํ•ธ๋“ค๋ง * - ํŒŒ์ผ ํ˜•์‹ ๊ฒ€์ฆ ๋ฐ ๋ฏธ๋ฆฌ๋ณด๊ธฐ ์ง€์› * - TUS ํ”„๋กœํ† ์ฝœ์„ ํ†ตํ•œ ๋Œ€์šฉ๋Ÿ‰ ํŒŒ์ผ ์—…๋กœ๋“œ */ "use client" import { FileUploader } from "@common/components"; import { Button } from '@common/components/ui'; import { handleUpload } from "@/components/lib/utils"; import { useId, useState } from 'react'; import type { HttpResponse, Upload } from "tus-js-client"; interface FileUploadFormProps { onSubmit?: (files: string, year: string, quarter: string, code: string, id: string) => void; onFileSelect?: (file: File | null, atflCd: string | null, atflId: string | null) => void; // ํŒŒ์ผ ์„ ํƒ ์‹œ ํ˜ธ์ถœ๋  ์ฝœ๋ฐฑ ์ถ”๊ฐ€ showButton?: boolean; // ๋ฒ„ํŠผ ํ‘œ์‹œ ์—ฌ๋ถ€ (๊ธฐ๋ณธ๊ฐ’ true) showPreview?: boolean; // ํŒŒ์ผ๋ช… ํ”„๋ฆฌ๋ทฐ ํ‘œ์‹œ ์—ฌ๋ถ€ accept?: string; // ํŒŒ์ผ ํ˜•์‹ ์ง€์ • fileIdx?: number; // ๋ถ€๋ชจ๊ฐ€ ์–ด๋–ค FileUploadForm์ธ์ง€ ๊ตฌ๋ถ„ํ•˜๊ธฐ ์œ„ํ•œ ์ธ๋ฑ์Šค } const FileUploadForm = ({ onSubmit, onFileSelect, showButton = true, showPreview = false, accept = ".jpeg, .jpg, .png", fileIdx // props์—์„œ fileIdx ๋ฐ›๊ธฐ }: FileUploadFormProps) => { const [internalAtflCd, setInternalAtflCd] = useState(null); const [internalAtflId, setInternalAtflId] = useState(null); const [currentFile, setCurrentFile] = useState(null); // ํ˜„์žฌ ์„ ํƒ๋œ ํŒŒ์ผ์„ ์ €์žฅํ•  ์ƒํƒœ const [fileName, setFileName] = useState(''); const [files, setFiles] = useState(null); const [upload, setUpload] = useState(null); const uniqueId = useId(); // ๊ฐ FileUploadForm ์ธ์Šคํ„ด์Šค์— ๋Œ€ํ•œ ๊ณ ์œ  ID ์ƒ์„ฑ // FileUploader ์ปดํฌ๋„ŒํŠธ๋ฅผ ๊ฐ•์ œ๋กœ ์žฌ๋งˆ์šดํŠธํ•˜๊ธฐ ์œ„ํ•œ key ์ƒํƒœ ์ถ”๊ฐ€ const [uploaderKey, setUploaderKey] = useState(0); const quater = [{ text: '1๋ถ„๊ธฐ', val: '1' }, { text: '2๋ถ„๊ธฐ', val: '2' }, { text: '3๋ถ„๊ธฐ', val: '3' }, { text: '4๋ถ„๊ธฐ', val: '4' }]; // ํ˜„์žฌ ๋…„๋„๋ถ€ํ„ฐ 10๋…„ ์ „๊นŒ์ง€์˜ ๋…„๋„ ์˜ต์…˜ ์ƒ์„ฑ const yearOptions = Array.from({ length: 10 }, (_, i) => { const year = new Date().getFullYear() - i; return { text: `${year}๋…„`, val: year.toString() }; }); const [selectedYear, setSelectedYear] = useState(yearOptions[0]?.val || ''); const [selectedQuarter, setSelectedQuarter] = useState(quater[0]?.val || ''); function registFile() { if (!currentFile) { alert('ํŒŒ์ผ์„ ์„ ํƒํ•ด์ฃผ์„ธ์š”.'); return; } if (!selectedYear) { alert('๋“ฑ๋ก๋…„๋„๋ฅผ ์„ ํƒํ•ด์ฃผ์„ธ์š”.'); return; } if (!selectedQuarter) { alert('๋ถ„๊ธฐ๋ฅผ ์„ ํƒํ•ด์ฃผ์„ธ์š”.'); return; } const fileNm = currentFile.name; if (onSubmit && files && currentFile) { // Promise๋กœ handleUpload ์™„๋ฃŒ๋ฅผ ๊ธฐ๋‹ค๋ฆผ handleUploadWithResponse(files, currentFile) .then(({ atflCd, atflId }) => { // ์—…๋กœ๋“œ ์™„๋ฃŒ ํ›„ onSubmit ์‹คํ–‰ console.log('fileNm', fileNm); console.log('selectedYear', selectedYear); console.log('selectedQuarter', selectedQuarter); console.log('atflCd', atflCd); console.log('atflId', atflId); onSubmit(fileNm, selectedYear, selectedQuarter, atflCd || '', atflId || ''); // UI ์ดˆ๊ธฐํ™” setFiles(null); setFileName(''); setUploaderKey(prev => prev + 1); }) .catch((error) => { console.error('ํŒŒ์ผ ์—…๋กœ๋“œ ์‹คํŒจ:', error); alert('ํŒŒ์ผ ์—…๋กœ๋“œ ์ค‘ ์˜ค๋ฅ˜๊ฐ€ ๋ฐœ์ƒํ–ˆ์Šต๋‹ˆ๋‹ค.'); }); } } // handleUpload๋ฅผ Promise๋กœ ๊ฐ์‹ธ๋Š” ํ—ฌํผ ํ•จ์ˆ˜ function handleUploadWithResponse(files: FileList, file: File): Promise<{ atflCd: string | null, atflId: string | null }> { return new Promise((resolve, reject) => { const uploadResponseHandler = async (res: HttpResponse) => { try { const fileInfo = res.getHeader("X-File-Info"); if (!fileInfo) { resolve({ atflCd: null, atflId: null }); return; } const { atflCd: newAtflCd, atflId: newAtflId } = JSON.parse(fileInfo) ?? {}; // ๋‚ด๋ถ€ ์ƒํƒœ๋„ ์—…๋ฐ์ดํŠธ (๋‹ค๋ฅธ ์šฉ๋„๋กœ ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ์Œ) setInternalAtflCd(newAtflCd); setInternalAtflId(newAtflId); setFileName(file.name); if (onFileSelect) { onFileSelect(file, newAtflCd, newAtflId); } // Promise ์™„๋ฃŒ resolve({ atflCd: newAtflCd, atflId: newAtflId }); } catch (error) { reject(error); } }; const uploadErrorHandler = (error: Error) => { console.error('Upload failed:', error); reject(error); }; console.log('Starting upload for files:', files); // ๊ธฐ์กด handleUpload ์œ ํ‹ธ๋ฆฌํ‹ฐ ์‚ฌ์šฉ (์ˆ˜์ •ํ•˜์ง€ ์•Š์Œ) handleUpload(files, { handleUploadStart: setUpload, handleSuccess: setUpload, handleResponse: uploadResponseHandler, }); }); } return (
{/* ์ปดํฌ๋„ŒํŠธ ๋ฃจํŠธ div */}
{/* ์นด๋“œ ์Šคํƒ€์ผ๋ง */}
{/* FileUploader์™€ Button์„ ์ •๋ ฌํ•˜๋Š” ๋ฉ”์ธ flex ์ปจํ…Œ์ด๋„ˆ */}
{/* FileUploader์˜ ๋„ˆ๋น„์™€ ์˜ค๋ฅธ์ชฝ ์—ฌ๋ฐฑ์„ ์ œ์–ดํ•˜๋Š” ์ปจํ…Œ์ด๋„ˆ */} { const selectedFiles = e.target.files; if (!selectedFiles || selectedFiles.length === 0) { setFiles(null); setCurrentFile(null); setInternalAtflCd(null); setInternalAtflId(null); if (onFileSelect) { onFileSelect(null, null, null); // ํŒŒ์ผ ์„ ํƒ ์ทจ์†Œ ์‹œ null ์ „๋‹ฌ } return; } const firstFile = selectedFiles[0]; if (!firstFile) return; // ํƒ€์ž…์Šคํฌ๋ฆฝํŠธ undefined ๊ฐ€๋Šฅ์„ฑ ์ œ๊ฑฐ setFiles(selectedFiles); setFileName(firstFile.name); setCurrentFile(firstFile); console.log('selectedFiles', selectedFiles); console.log('firstFile', firstFile); console.log('firstFile name', firstFile.name); if (onFileSelect) { onFileSelect(firstFile, internalAtflCd, internalAtflId); } }} customPreview={ showPreview ? (
{files && files.length > 0 ? fileName : "ํŒŒ์ผ์„ ์„ ํƒํ•˜์„ธ์š”."}
) : null } />
{showButton ? ( // showButton์ด true์ผ ๋•Œ๋งŒ ๋ฒ„ํŠผ ๋ Œ๋”๋ง ) : null}
) } export default FileUploadForm