/** * ๐Ÿ“„ Table Modal * ๋…ธํ›„ ํ•˜์ˆ˜๊ด€๋กœ ์—‘์…€ ๋ฐ์ดํ„ฐ ํ…Œ์ด๋ธ” ๋ชจ๋‹ฌ ์ปดํฌ๋„ŒํŠธ * * - ์—‘์…€ ํŒŒ์ผ ๋‹ค์šด๋กœ๋“œ ๋ฐ ํŠน์ • ์‹œํŠธ ํŒŒ์‹ฑ ๊ธฐ๋Šฅ * - ํƒญ ๊ธฐ๋ฐ˜ ํ…Œ์ด๋ธ” ํ‘œ์‹œ (row data, ์ •๋ฐ€์กฐ์‚ฌ, ์ •๋น„์‚ฌ์—… ๋“ฑ) * - ๋ฐ์ดํ„ฐ ํ•„ํ„ฐ๋ง ๋ฐ ๋นˆ ํ–‰ ์ œ๊ฑฐ ์ฒ˜๋ฆฌ * - ๋กœ๋”ฉ ์˜ค๋ฒ„๋ ˆ์ด ๋ฐ ์—๋Ÿฌ ํ•ธ๋“ค๋ง ์ง€์› * - XLSX ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ๋ฅผ ํ†ตํ•œ ์—‘์…€ ๋ฐ์ดํ„ฐ ๋ณ€ํ™˜ * - ํ…Œ์ด๋ธ” ํ—ค๋” ๋ฐ ๋ฐ์ดํ„ฐ ๋™์  ์ƒ์„ฑ * * ๐Ÿ”— ์‚ฌ์šฉ API * - ์ฒจ๋ถ€ํŒŒ์ผ ๋‹ค์šด๋กœ๋“œ(ํŒŒ์ผID): GET /attach-service/api/v1/download/{atflId} (attach-service) */ 'use client' import { useState, useEffect } from 'react' import LoadingOverlay from '@/components/ui/LoadingBar' import { UITab, UiTable } from '@common/business_components/ui' import * as XLSX from 'xlsx'; import { HeadMakeColSpan } from '@common/business_components/ui/table/HeadMakeColSpan'; import { oldPpTable2HeadList, oldPpTable2NodeList, oldPpTable6HeadList, oldPpTable6NodeList } from './headList'; const TABS = [ { val: "1", text: "row data" }, { val: "2", text: "1.์ •๋ฐ€์กฐ์‚ฌ" }, { val: "3", text: "2.์ •๋ฐ€์กฐ์‚ฌ_์ •๋น„์‚ฌ์—…์—ฐ๊ณ„" }, { val: "4", text: " 3.์ •๋น„์‚ฌ์—…" }, { val: "5", text: "4.์ •๋น„์‚ฌ์—…์š”์•ฝ" }, { val: "6", text: "4.์ •๋น„์‚ฌ์—…์ตœ์ข…" }, ]; interface TableModalProps { fileNm: string; atflId: string; } const TableModal: React.FC = ({ fileNm, atflId }) => { const [activeTab, setActiveTab] = useState(TABS[0]?.val ?? "1"); const [tableHeader1, setTableHeader1] = useState<{ id: string; title: string }[]>([]) const [tableHeader2, setTableHeader2] = useState<{ id: string; title: string }[]>([]) const [tableHeader4, setTableHeader4] = useState<{ id: string; title: string }[]>([]) const [tableHeader5, setTableHeader5] = useState<{ id: string; title: string }[]>([]) const [tableData1, setTableData1] = useState([{}]) const [tableData2, setTableData2] = useState([{}]) const [tableData3, setTableData3] = useState([{}]) const [tableData4, setTableData4] = useState([{}]) const [tableData5, setTableData5] = useState([{}]) const [tableData6, setTableData6] = useState([{}]) const [isLoading, setIsLoading] = useState(false) useEffect(() => { isChckFile(); }, []); // ํŒŒ์ผ ๋‹ค์šด๋กœ๋“œ const isChckFile = async () => { setIsLoading(true); try { if (fileNm === '' || fileNm === undefined) { alert('ํŒŒ์ผ์„ ์„ ํƒํ•ด์ฃผ์„ธ์š”'); setIsLoading(false); return false; } const response = await fetch(`/attach-service/api/v1/download/${atflId}`); if (!response.ok) { throw new Error('ํŒŒ์ผ์„ ๊ฐ€์ ธ์˜ค๋Š” ์ค‘ ์˜ค๋ฅ˜๊ฐ€ ๋ฐœ์ƒํ–ˆ์Šต๋‹ˆ๋‹ค.'); } const blob = await response.blob(); const file = new File([blob], fileNm, { type: blob.type || 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet' }); const targetSheets = ['row data', '1.์ •๋ฐ€์กฐ์‚ฌ', '2.์ •๋ฐ€์กฐ์‚ฌ_์ •๋น„์‚ฌ์—…์—ฐ๊ณ„', ' 3.์ •๋น„์‚ฌ์—…', '4.์ •๋น„์‚ฌ์—…์š”์•ฝ', '4.์ •๋น„์‚ฌ์—…์ตœ์ข…']; const jsonDataBySheet = await filePickedSelectedSheets(file, 0, targetSheets); const header1 = jsonDataBySheet['row data']?.[1] || []; const header2 = jsonDataBySheet['1.์ •๋ฐ€์กฐ์‚ฌ']?.[1] || []; const header4 = jsonDataBySheet[' 3.์ •๋น„์‚ฌ์—…']?.[1] || []; const header5 = jsonDataBySheet['4.์ •๋น„์‚ฌ์—…์š”์•ฝ']?.[2] || []; // row data const allEntries1 = Object.entries(header1); const filteredHeader1 = [ allEntries1[0], allEntries1[1], allEntries1[2], allEntries1[3], allEntries1[5], allEntries1[6], allEntries1[7], allEntries1[8], allEntries1[9], allEntries1[10], allEntries1[11], allEntries1[12], allEntries1[13], allEntries1[14], allEntries1[15] ].filter((e): e is [string, any] => Boolean(e)) .map(([key, value]) => ({ id: key, title: String(value).replace(/\r\n/g, '\n') })); filteredHeader1.splice(0, 0, { id: "rowNumber", title: '์ผ๋ จ๋ฒˆํ˜ธ' }); setTableHeader1(filteredHeader1); const data1 = Array.isArray(jsonDataBySheet['row data']) ? jsonDataBySheet['row data'].slice(2) : []; const filteredData1 = filterEmptyRows(data1).map((item: any, index: number) => ({ ...item, rowNumber: index + 1, __EMPTY_1: item.__EMPTY_1 || '-' })); setTableData1(filteredData1); // 1.์ •๋ฐ€์กฐ์‚ฌ const allEntries2 = Object.entries(header2); let filteredHeader2 = [ allEntries2[0], allEntries2[1], allEntries2[2], allEntries2[3], allEntries2[5], allEntries2[6], allEntries2[7], allEntries2[8], allEntries2[9], allEntries2[10], allEntries2[11], allEntries2[12], allEntries2[13], allEntries2[14], allEntries2[15] ].filter((e): e is [string, any] => Boolean(e)) .map(([key, value]) => ({ id: key, title: String(value).replace(/\r\n/g, '\n') })); filteredHeader2.splice(0, 0, { id: "rowNumber", title: '์ผ๋ จ๋ฒˆํ˜ธ' }); setTableHeader2(filteredHeader2); const data2 = Array.isArray(jsonDataBySheet['1.์ •๋ฐ€์กฐ์‚ฌ']) ? jsonDataBySheet['1.์ •๋ฐ€์กฐ์‚ฌ'].slice(2) : []; const filteredData2 = filterEmptyRows(data2).map((item: any, index: number) => ({ ...item, rowNumber: index + 1 })); setTableData2(filteredData2); // 2.์ •๋ฐ€์กฐ์‚ฌ_์ •๋น„์‚ฌ์—…์—ฐ๊ณ„ (ํ—ค๋”๋Š” import) const data3 = Array.isArray(jsonDataBySheet['2.์ •๋ฐ€์กฐ์‚ฌ_์ •๋น„์‚ฌ์—…์—ฐ๊ณ„']) ? jsonDataBySheet['2.์ •๋ฐ€์กฐ์‚ฌ_์ •๋น„์‚ฌ์—…์—ฐ๊ณ„'].slice(5) : []; const filteredData3 = filterEmptyRows(data3).map((item: any, index: number) => { const newItem = { ...item }; // ๋งคํ•‘ ์—๋Ÿฌ๋กœ ์ธํ•ด ํŠน์ • ํ‚ค๋ฅผ "__EMPTY_0"์œผ๋กœ ๋ณ€๊ฒฝ const targetKey = "*1์ฐจ ์ถœ์ฒ˜: ๊ณต๋‹จ์ž๋ฃŒ\r\n*2์ฐจ ์ถœ์ฒ˜: ์ •์‚ฐ์„œ, ์ •๋ฐ€์กฐ์‚ฌ๋ณด๊ณ ์„œ ๋“ฑ.\r\n (ํŒŒ๋ž‘) 2024๋…„๋ง ๊ธฐ์ค€ ์‹ค์  ์ œ์ถœ ์ž๋ฃŒ. ์ค€๊ณต ํ›„ ์žฌํ™•์ธ ํ•„์š”\r\n (๋…ธ๋ž‘์…€) ์ค€๊ณต(๋˜๋Š” ์ •์‚ฐ) ํ›„์— ์žฌํ™•์ธ ํ•„์š”"; if (newItem[targetKey] !== undefined) { newItem["__EMPTY_0"] = newItem[targetKey]; delete newItem[targetKey]; } return { ...newItem, __EMPTY_4: newItem?.__EMPTY_4 || '-', rowNumber: index + 1 }; }); setTableData3(filteredData3); // 3.์ •๋น„์‚ฌ์—… const allEntries4 = Object.entries(header4); const filteredHeader4 = allEntries4.filter((e): e is [string, any] => Boolean(e)) .map(([key, value]) => ({ id: key, title: String(value).replace(/\r\n/g, '\n') })); filteredHeader4.splice(0, 0, { id: "rowNumber", title: '์ผ๋ จ๋ฒˆํ˜ธ' }); setTableHeader4(filteredHeader4); const data4 = Array.isArray(jsonDataBySheet[' 3.์ •๋น„์‚ฌ์—…']) ? jsonDataBySheet[' 3.์ •๋น„์‚ฌ์—…'].slice(2) : []; const filteredData4 = filterEmptyRows(data4).map((item: any, index: number) => ({ ...item, rowNumber: index + 1, __EMPTY_2: item?.__EMPTY_2 || '-', __EMPTY_11: item?.__EMPTY_11 || '-', })); setTableData4(filteredData4); // 4.์ •๋น„์‚ฌ์—…์š”์•ฝ const allEntries5 = Object.entries(header5); const excludeKeys5 = [ ": \"์ •๋น„์‚ฌ์—…\"sheet์—์„œ ์‚ฌ์—…์ฝ”๋“œ ctrl+c, Ctrl+v ํ•œ ๋‹ค์Œ ์ค‘๋ณต๋œ ๋ฐ์ดํ„ฐ ์ œ๊ฑฐ์‹ค์‹œ", "__EMPTY", "__EMPTY_8", "__EMPTY_15" ]; const filteredHeader5 = allEntries5 .filter(([key]) => !excludeKeys5.includes(key)) .filter((e): e is [string, any] => Boolean(e)) .map(([key, value]) => ({ id: key, title: String(value).replace(/\r\n/g, '\n'), })); filteredHeader5.splice(0, 0, { id: "rowNumber", title: '์ผ๋ จ๋ฒˆํ˜ธ' }); setTableHeader5(filteredHeader5); const data5 = Array.isArray(jsonDataBySheet['4.์ •๋น„์‚ฌ์—…์š”์•ฝ']) ? jsonDataBySheet['4.์ •๋น„์‚ฌ์—…์š”์•ฝ'].slice(3) : []; const filteredData5 = filterEmptyRows(data5).map((item: any, index: number) => ({ ...item, rowNumber: index + 1, })); setTableData5(filteredData5); // 4.์ •๋น„์‚ฌ์—…์ตœ์ข… (ํ—ค๋”๋Š” import) const data6 = Array.isArray(jsonDataBySheet['4.์ •๋น„์‚ฌ์—…์ตœ์ข…']) ? jsonDataBySheet['4.์ •๋น„์‚ฌ์—…์ตœ์ข…'].slice(6) : []; const filteredData6 = filterEmptyRows(data6).map((item: any, index: number) => ({ ...item, rowNumber: index + 1, })); setTableData6(filteredData6); } catch (error) { console.error('ํŒŒ์ผ ์ฒ˜๋ฆฌ ์ค‘ ์˜ค๋ฅ˜ ๋ฐœ์ƒ:', error); alert('ํŒŒ์ผ ์ฒ˜๋ฆฌ ์ค‘ ์˜ค๋ฅ˜๊ฐ€ ๋ฐœ์ƒํ–ˆ์Šต๋‹ˆ๋‹ค.'); } finally { setIsLoading(false); } } function filterEmptyRows(data: any[]) { return data .filter(row => { return Object.entries(row).some(([key, value]) => { const match = key.match(/^__EMPTY_(\d+)$/); if (!match || parseInt(match[1] ?? '', 10) < 2) return false; return value !== undefined && value !== null && String(value).trim() !== '' && String(value).trim() !== '-' && String(value).trim() !== ''; }); }) .map((row) => { const processedRow = { ...row }; const keys = Object.keys(processedRow); keys.forEach((key, keyIndex) => { // ๋งˆ์ง€๋ง‰ ์†์„ฑ(์‚ฌ์—… ๊ทผ๊ฑฐ)์ด ์•„๋‹Œ ๊ฒฝ์šฐ์—๋งŒ ๋นˆ ๊ฐ’์„ '-'์œผ๋กœ ๋ณ€๊ฒฝ if (keyIndex !== keys.length - 1) { const value = processedRow[key]; if (value === undefined || value === null || value === '' || String(value).trim() === '') { processedRow[key] = '-'; } } }); return processedRow; }); }; // ํŠน์ • ์‹œํŠธ๋งŒ ์„ ํƒ์ ์œผ๋กœ JSON์œผ๋กœ ๋ณ€ํ™˜ํ•˜๋Š” ํ•จ์ˆ˜ function filePickedSelectedSheets(file: File, headerNum: number = 0, sheetNames: string[]): Promise> { const reader = new FileReader(); return new Promise((resolve, reject) => { reader.onload = function (e) { const data = e.target?.result; if (!data) { console.error("Failed to read file: e.target.result is null"); reject(new Error("Failed to read file")); return; } // Excel ํŒŒ์ผ ์ฝ๊ธฐ const wb = XLSX.read(data, { type: 'binary', cellDates: true }); const result: Record = {}; // ์ง€์ •๋œ ์‹œํŠธ ์ด๋ฆ„๋งŒ ์ฐพ์•„์„œ ์ฒ˜๋ฆฌ sheetNames.forEach(targetSheetName => { // ๋Œ€์†Œ๋ฌธ์ž ๊ตฌ๋ถ„ ์—†์ด ์‹œํŠธ ์ด๋ฆ„ ์ฐพ๊ธฐ const sheetName = wb.SheetNames.find(name => name.toLowerCase() === targetSheetName.toLowerCase() ); if (sheetName) { const sheet = wb.Sheets[sheetName]; if (sheet) { const jsonData = XLSX.utils.sheet_to_json(sheet, { range: headerNum, raw: false }); result[targetSheetName] = jsonData; } } else { console.warn(`Sheet "${targetSheetName}" not found in workbook`); result[targetSheetName] = []; } }); resolve(result); }; reader.onerror = function () { console.error("Error reading file"); reject(new Error("Error reading file")); }; // ํŒŒ์ผ ์ฝ๊ธฐ ์‹œ์ž‘ reader.readAsBinaryString(file); }); }; return ( <> {/* ํƒญ ์˜์—ญ */} ({ value: tab.val, label: tab.text }))} onValueChange={(value: string) => setActiveTab(value)} /> {/* row data ํƒญ */} {activeTab === '1' && (
{ }} />
)} {/* 1.์ •๋ฐ€์กฐ์‚ฌ ํƒญ */} {activeTab === '2' && (
{ }} />
)} {/* 2.์ •๋ฐ€์กฐ์‚ฌ_์ •๋น„์‚ฌ์—…์—ฐ๊ณ„ ํƒญ */} {activeTab === '3' && (
{ }} >
)} {/* 3.์ •๋น„์‚ฌ์—… ํƒญ */} {activeTab === '4' && (
{ }} />
)} {/* 4.์ •๋น„์‚ฌ์—…์š”์•ฝ ํƒญ */} {activeTab === '5' && (
{ }} />
)} {/* 4.์ •๋น„์‚ฌ์—…์ตœ์ข… ํƒญ */} {activeTab === '6' && (
{ }} >
)} ) }; export default TableModal;