From 6447afd1629b6be0ed3ea82d11d8d1641d89e86c Mon Sep 17 00:00:00 2001 From: truthwing Date: Wed, 30 Apr 2025 15:59:05 +0900 Subject: [PATCH 01/12] =?UTF-8?q?excel=20populate=20=EC=B6=94=EA=B0=80=20?= =?UTF-8?q?=EB=B0=8F=20=EC=83=81=EB=8B=A8=20range=EC=97=90=20=ED=97=A4?= =?UTF-8?q?=EB=8D=94=20=EC=8A=A4=ED=83=80=EC=9D=BC=20=EC=9E=85=ED=9E=88?= =?UTF-8?q?=EB=8A=94=20=EC=9E=91=EC=97=85=20=EC=99=84=EB=A3=8C.?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../common/business_components/package.json | 2 + .../src/ui/excel/excelStyle.tsx | 24 ++++++ .../src/ui/excel/index.tsx | 84 +++++++++++++++++++ .../business_components/src/ui/index.ts | 3 +- 4 files changed, 112 insertions(+), 1 deletion(-) create mode 100644 packages/common/business_components/src/ui/excel/excelStyle.tsx create mode 100644 packages/common/business_components/src/ui/excel/index.tsx diff --git a/packages/common/business_components/package.json b/packages/common/business_components/package.json index 1f02a708e..a23f31058 100644 --- a/packages/common/business_components/package.json +++ b/packages/common/business_components/package.json @@ -43,6 +43,7 @@ "sonner": "^2.0.1", "tailwind-merge": "^3.0.1", "xlsx": "^0.18.5", + "xlsx-populate": "^1.21.0", "zod": "^3.24.2" }, "devDependencies": { @@ -58,6 +59,7 @@ "@storybook/test": "^8.5.3", "@storybook/theming": "^8.5.5", "@vitejs/plugin-react": "^4.3.4", + "@xlsx/xlsx-populate": "^0.0.8", "autoprefixer": "^10.4.20", "eslint": "^9.17.0", "eslint-plugin-react-hooks": "^5.0.0", diff --git a/packages/common/business_components/src/ui/excel/excelStyle.tsx b/packages/common/business_components/src/ui/excel/excelStyle.tsx new file mode 100644 index 000000000..2475d1453 --- /dev/null +++ b/packages/common/business_components/src/ui/excel/excelStyle.tsx @@ -0,0 +1,24 @@ +import * as XlsxPopulate from '@xlsx/xlsx-populate'; +export const excelStyle = async (data: ArrayBuffer, range: string = "A1:V3", fileNm:string) => { + console.log("excelStyle", data, range, fileNm); + const workbook = await XlsxPopulate.Workbook.fromData(data); + const rangeCells = workbook.activeSheet(0)?.sheet(0)?.range(range); + console.log(rangeCells); + rangeCells?.style((styleQ)=>{ + console.log(styleQ); + styleQ.fill({ type: "solid", color: {rgb:"9bc1e7"} }); + styleQ.borderEach( { style: "thin", color: { rgb: "FFFFFF" } } ); + styleQ.wrapText(true); + // styleQ.fontColor({rgb:"FFFFFF"}); + styleQ.fontSize(15); + styleQ.horizontalAlignment("center"); + styleQ.verticalAlignment("center"); + styleQ.bold(true); + }); + const outputData = await workbook.output("array-buffer"); + const blob = new Blob([outputData], { type: "application/octet-stream" }); + const link = document.createElement("a"); + link.href = URL.createObjectURL(blob); + link.download = fileNm+".xlsx"; + link.click(); +} \ No newline at end of file diff --git a/packages/common/business_components/src/ui/excel/index.tsx b/packages/common/business_components/src/ui/excel/index.tsx new file mode 100644 index 000000000..0aa547c5c --- /dev/null +++ b/packages/common/business_components/src/ui/excel/index.tsx @@ -0,0 +1,84 @@ +import * as XLSX from 'xlsx'; +import { excelStyle } from './excelStyle'; + +type ExcelDownloadProps = { + data: JSON | JSON[]; + fileName: string; + sheetNameArray: string[]; +} + +export const downloadExcel = ({ data, fileName = new Date().getTime().toString() + '.xlsx', sheetNameArray = ["Sheet1"] }: ExcelDownloadProps) => { + const worksheet = XLSX.utils.json_to_sheet(Array.isArray(data) ? data : [data]); + const workbook = XLSX.utils.book_new(); + XLSX.utils.book_append_sheet(workbook, worksheet, sheetNameArray[0]); + //let buffer = XLSX.write(workbook, { bookType: "xlsx", type: "buffer" }); + //XLSX.write(workbook, { bookType: "xlsx", type: "binary" }); + XLSX.writeFile(workbook, fileName + '.xlsx'); +}; + + + +export const filePicked=(oFile: File, headerNum:number=0):Promise =>{ + // Create A File Reader HTML5 + const reader = new FileReader(); + return new Promise((resolve, reject) => { + // Ready The Event For When A File Gets Selected + reader.onload = function (e) { + const data = e.target?.result; + if (!data) { + console.error("Failed to read file: e.target.result is null"); + resolve(undefined); + return; + } + const wb = XLSX.read(data, { type: 'binary' }); + let jsonXls: JSON | undefined; + + //TODO 시트가 2개이상일 경우 마지막 시트의 데이터만 표출됨 + // Loop Over Each Sheet + wb.SheetNames.forEach(function (sheetName) { + // Obtain The Current Row As CSV + const sheet = wb.Sheets[sheetName]; + + if (sheet) { + jsonXls = XLSX.utils.sheet_to_json(sheet, { range: headerNum }) as unknown as JSON; + } else { + console.error(`Sheet "${sheetName}" is undefined.`); + } + }); + resolve(jsonXls); + }; + + reader.onerror = function () { + console.error("Error reading file"); + reject(undefined); + }; + + // Tell JS To Start Reading The File.. You could delay this if desired + reader.readAsBinaryString(oFile); + }); +} + + + +export const filePickedBlob=(oFile: ArrayBuffer, data:[][],fileNm:string, headerNum:number=0, headerRange='A1:V3'):Promise =>{ // Create A File Reader HTML5 + return new Promise(() => { + // Ready The Event For When A File Gets Selected + const wb = XLSX.read(oFile, { type: 'binary' }); + // Loop Over Each Sheet + wb.SheetNames.forEach(async function (sheetName, index) { + // Obtain The Current Row As CSV + if(index==0){ + const sheet = wb.Sheets[sheetName]; + if (sheet) { + //오리진 확인 test 필요 + // const sheet = XLSX.utils.json_to_sheet(data); + XLSX.utils.sheet_add_aoa(sheet, data, { origin: 'A'+headerNum }); + // XLSX.utils.book_append_sheet(wb, sheet, 'test'); + await excelStyle(XLSX.write(wb, { bookType: "xlsx", type: "buffer" }), headerRange, fileNm) + } else { + console.error(`Sheet "${sheetName}" is undefined.`); + } + } + }); + }); + } \ No newline at end of file diff --git a/packages/common/business_components/src/ui/index.ts b/packages/common/business_components/src/ui/index.ts index e591be5b5..90bf029c0 100644 --- a/packages/common/business_components/src/ui/index.ts +++ b/packages/common/business_components/src/ui/index.ts @@ -1,7 +1,7 @@ export * from "./chart"; export * from "./date_picker"; export * from "./dialog_ui"; -export * from "./excel_download"; +export * from "./excel"; export * from "./file-upload"; export * from "./form"; export * from "./form_ui"; @@ -19,3 +19,4 @@ export * from "./tab"; export * from "./table"; export * from "./table_div"; export * from "./title"; + From 370a1076b0351b92e26c75dc94cd133b7a3eb881 Mon Sep 17 00:00:00 2001 From: truthwing Date: Wed, 30 Apr 2025 16:08:22 +0900 Subject: [PATCH 02/12] =?UTF-8?q?excel=5Fdownload=20=ED=8F=B4=EB=8D=94?= =?UTF-8?q?=EB=AA=85=20=EB=B3=80=EA=B2=BD.?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../src/ui/excel/index.tsx | 7 +- .../src/ui/excel_download/index.tsx | 84 ------------------- 2 files changed, 3 insertions(+), 88 deletions(-) delete mode 100644 packages/common/business_components/src/ui/excel_download/index.tsx diff --git a/packages/common/business_components/src/ui/excel/index.tsx b/packages/common/business_components/src/ui/excel/index.tsx index 0aa547c5c..13a814114 100644 --- a/packages/common/business_components/src/ui/excel/index.tsx +++ b/packages/common/business_components/src/ui/excel/index.tsx @@ -5,15 +5,14 @@ type ExcelDownloadProps = { data: JSON | JSON[]; fileName: string; sheetNameArray: string[]; + headerRange: string; } -export const downloadExcel = ({ data, fileName = new Date().getTime().toString() + '.xlsx', sheetNameArray = ["Sheet1"] }: ExcelDownloadProps) => { +export const downloadExcel = ({ data, fileName = new Date().getTime().toString() + '.xlsx', sheetNameArray = ["Sheet1"], headerRange='A1:V3'}: ExcelDownloadProps) => { const worksheet = XLSX.utils.json_to_sheet(Array.isArray(data) ? data : [data]); const workbook = XLSX.utils.book_new(); XLSX.utils.book_append_sheet(workbook, worksheet, sheetNameArray[0]); - //let buffer = XLSX.write(workbook, { bookType: "xlsx", type: "buffer" }); - //XLSX.write(workbook, { bookType: "xlsx", type: "binary" }); - XLSX.writeFile(workbook, fileName + '.xlsx'); + excelStyle(XLSX.write(workbook, { bookType: "xlsx", type: "buffer" }), headerRange, fileName); }; diff --git a/packages/common/business_components/src/ui/excel_download/index.tsx b/packages/common/business_components/src/ui/excel_download/index.tsx deleted file mode 100644 index 58ecf8a52..000000000 --- a/packages/common/business_components/src/ui/excel_download/index.tsx +++ /dev/null @@ -1,84 +0,0 @@ -import * as XLSX from 'xlsx'; - -type ExcelDownloadProps = { - data: JSON | JSON[]; - fileName: string; - sheetNameArray: string[]; -} - -export const downloadExcel = ({ data, fileName = new Date().getTime().toString() + '.xlsx', sheetNameArray = ["Sheet1"] }: ExcelDownloadProps) => { - const worksheet = XLSX.utils.json_to_sheet(Array.isArray(data) ? data : [data]); - const workbook = XLSX.utils.book_new(); - XLSX.utils.book_append_sheet(workbook, worksheet, sheetNameArray[0]); - //let buffer = XLSX.write(workbook, { bookType: "xlsx", type: "buffer" }); - //XLSX.write(workbook, { bookType: "xlsx", type: "binary" }); - XLSX.writeFile(workbook, fileName + '.xlsx'); -}; - - - -export const filePicked=(oFile: File, headerNum:number=0):Promise =>{ - // Create A File Reader HTML5 - const reader = new FileReader(); - return new Promise((resolve, reject) => { - // Ready The Event For When A File Gets Selected - reader.onload = function (e) { - const data = e.target?.result; - if (!data) { - console.error("Failed to read file: e.target.result is null"); - resolve(undefined); - return; - } - const wb = XLSX.read(data, { type: 'binary' }); - let jsonXls: JSON | undefined; - - //TODO 시트가 2개이상일 경우 마지막 시트의 데이터만 표출됨 - // Loop Over Each Sheet - wb.SheetNames.forEach(function (sheetName) { - // Obtain The Current Row As CSV - const sheet = wb.Sheets[sheetName]; - - if (sheet) { - jsonXls = XLSX.utils.sheet_to_json(sheet, { range: headerNum }) as unknown as JSON; - } else { - console.error(`Sheet "${sheetName}" is undefined.`); - } - }); - resolve(jsonXls); - }; - - reader.onerror = function () { - console.error("Error reading file"); - reject(undefined); - }; - - // Tell JS To Start Reading The File.. You could delay this if desired - reader.readAsBinaryString(oFile); - }); -} - - - -export const filePickedBlob=(oFile: ArrayBuffer, data:[][],fileNm:string, headerNum:number=0):Promise =>{ - // Create A File Reader HTML5 - return new Promise(() => { - // Ready The Event For When A File Gets Selected - const wb = XLSX.read(oFile, { type: 'binary' }); - // Loop Over Each Sheet - wb.SheetNames.forEach(function (sheetName, index) { - // Obtain The Current Row As CSV - if(index==0){ - const sheet = wb.Sheets[sheetName]; - if (sheet) { - //오리진 확인 test 필요 - // const sheet = XLSX.utils.json_to_sheet(data); - XLSX.utils.sheet_add_aoa(sheet, data, { origin: 'A'+headerNum }); - // XLSX.utils.book_append_sheet(wb, sheet, 'test'); - XLSX.writeFile(wb, `${fileNm}.xlsx`); - } else { - console.error(`Sheet "${sheetName}" is undefined.`); - } - } - }); - }); - } \ No newline at end of file From 826b49d72afc6137643f7d449b240d4e8e9379e3 Mon Sep 17 00:00:00 2001 From: leedj312 Date: Fri, 2 May 2025 16:19:36 +0900 Subject: [PATCH 03/12] =?UTF-8?q?=EC=97=91=EC=85=80=20to=20json=20=200?= =?UTF-8?q?=EB=B2=88=EC=A7=B8=20=EC=8B=9C=ED=8A=B8=EB=A7=8C=20=EB=B0=94?= =?UTF-8?q?=EB=9D=BC=EB=B3=B4=EA=B2=8C=20=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../src/ui/excel/index.tsx | 21 +++++++++++-------- 1 file changed, 12 insertions(+), 9 deletions(-) diff --git a/packages/common/business_components/src/ui/excel/index.tsx b/packages/common/business_components/src/ui/excel/index.tsx index 13a814114..c17aa0660 100644 --- a/packages/common/business_components/src/ui/excel/index.tsx +++ b/packages/common/business_components/src/ui/excel/index.tsx @@ -29,19 +29,22 @@ export const filePicked=(oFile: File, headerNum:number=0):Promise Date: Tue, 6 May 2025 23:09:34 +0900 Subject: [PATCH 04/12] =?UTF-8?q?=ED=85=8C=EC=9D=B4=EB=B8=94=EC=97=90?= =?UTF-8?q?=EC=84=9C=20visible=20=EB=B0=8F=20filter=EC=B2=98=EB=A6=AC?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../src/ui/table/HeadMakeColSpan.tsx | 12 +++++++++--- .../business_components/src/ui/table/index.tsx | 5 ++++- 2 files changed, 13 insertions(+), 4 deletions(-) diff --git a/packages/common/business_components/src/ui/table/HeadMakeColSpan.tsx b/packages/common/business_components/src/ui/table/HeadMakeColSpan.tsx index dc859a55d..ebd611c1d 100644 --- a/packages/common/business_components/src/ui/table/HeadMakeColSpan.tsx +++ b/packages/common/business_components/src/ui/table/HeadMakeColSpan.tsx @@ -8,16 +8,20 @@ interface TableUpperProps { row? : number; rowSpan?: number; colSpan?: number; + visible?:boolean; } const HeadMakeColSpan = ({ upHeadList }: { upHeadList: TableUpperProps[] }) => { - const maxRow = upHeadList.reduce((max, head) => Math.max(max, head.row || 0), 0); + const maxRow = upHeadList?.reduce((max, head) => Math.max(max, head.row || 0), 0); + //upHeadList.forEach((item) => item.visible ?item.visible:true) if(maxRow == 0){ return ( <> - {upHeadList.map((head: TableUpperProps) => { + {upHeadList + .filter((item) => item.visible != false) + .map((head: TableUpperProps) => { return ( { <> {Array.from({ length: maxRow + 1 }, (_, rowIndex) => ( - {upHeadList.map((head: TableUpperProps) => { + {upHeadList + .filter((item) => item.visible != false) + .map((head: TableUpperProps) => { if (head.row === rowIndex) { return ( {tableData.length > 0 ? tableData?.map((el: { [key: string]: any}, index) => ( - {headlist?.map((head) => ( + {headlist + .filter((item) => item.visible != false) + .map((head) => ( head.id && head.id.length > 1 && ( Date: Fri, 9 May 2025 16:55:09 +0900 Subject: [PATCH 05/12] =?UTF-8?q?=ED=85=8C=EC=9D=B4=EB=B8=94=20boldArray?= =?UTF-8?q?=20property=20=EC=B6=94=EA=B0=80=20(=EC=97=AD=ED=95=A0=20:=20?= =?UTF-8?q?=EC=A7=80=EC=A0=95=EB=90=9C=20array=EC=9D=98=20=EA=B8=80?= =?UTF-8?q?=EC=94=A8=EC=83=89=20=EA=B5=B5=EA=B2=8C=20=EB=B3=80=EA=B2=BD)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../business_components/src/ui/table/index.tsx | 13 ++++++++++++- 1 file changed, 12 insertions(+), 1 deletion(-) diff --git a/packages/common/business_components/src/ui/table/index.tsx b/packages/common/business_components/src/ui/table/index.tsx index 00fc376e6..f02c1d3ac 100644 --- a/packages/common/business_components/src/ui/table/index.tsx +++ b/packages/common/business_components/src/ui/table/index.tsx @@ -14,6 +14,7 @@ type UiTableProps = { className?: string; resetPageNo?: boolean; children?: React.ReactNode | undefined; + boldArray?: number[]; } type UiHeadProps = { @@ -23,7 +24,7 @@ type UiHeadProps = { visible?:boolean; } -export function UiTable({ tableData, headlist, pageSize, total, headName, children, cellClick, onPageChange, className = '', resetPageNo = false }: UiTableProps) { +export function UiTable({ tableData, headlist, pageSize, total, headName, children, cellClick, onPageChange, className = '', resetPageNo = false, boldArray = [] }: UiTableProps) { const [cur, setCur] = useState(1); const [idx, setIdx] = useState(-1); @@ -63,6 +64,16 @@ export function UiTable({ tableData, headlist, pageSize, total, headName, childr .filter((item) => item.visible != false) .map((head) => ( head.id && head.id.length > 1 && ( + boldArray.includes(index) ? handleCellClick(index, head.id)} + key={`cell-${head.id}-${index}`} + > + {typeof el[head.id] === 'number' ? String(el[head.id]).replace(/\B(?=(\d{3})+(?!\d))/g, ",") : String(el[head.id] ?? '')} + : Date: Tue, 13 May 2025 09:33:29 +0900 Subject: [PATCH 06/12] =?UTF-8?q?=EC=97=91=EC=85=80=EB=8B=A4=EC=9A=B4?= =?UTF-8?q?=EB=A1=9C=EB=93=9C=202=EA=B0=9C=EC=9D=B4=EC=83=81=20=EC=8B=9C?= =?UTF-8?q?=ED=8A=B8=20=ED=95=A8=EC=88=98=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../src/ui/excel/excelStyle.tsx | 48 +++++++++--- .../src/ui/excel/index.tsx | 76 ++++++++++++++++++- 2 files changed, 112 insertions(+), 12 deletions(-) diff --git a/packages/common/business_components/src/ui/excel/excelStyle.tsx b/packages/common/business_components/src/ui/excel/excelStyle.tsx index 2475d1453..adc21880b 100644 --- a/packages/common/business_components/src/ui/excel/excelStyle.tsx +++ b/packages/common/business_components/src/ui/excel/excelStyle.tsx @@ -1,24 +1,54 @@ +import type { sheetParameter } from '@/index'; import * as XlsxPopulate from '@xlsx/xlsx-populate'; -export const excelStyle = async (data: ArrayBuffer, range: string = "A1:V3", fileNm:string) => { - console.log("excelStyle", data, range, fileNm); - const workbook = await XlsxPopulate.Workbook.fromData(data); - const rangeCells = workbook.activeSheet(0)?.sheet(0)?.range(range); - console.log(rangeCells); - rangeCells?.style((styleQ)=>{ - console.log(styleQ); + + + +const makeCellStyle =(styleQ: XlsxPopulate.StyleQuery )=>{ styleQ.fill({ type: "solid", color: {rgb:"9bc1e7"} }); styleQ.borderEach( { style: "thin", color: { rgb: "FFFFFF" } } ); styleQ.wrapText(true); // styleQ.fontColor({rgb:"FFFFFF"}); - styleQ.fontSize(15); + styleQ.fontSize(13); styleQ.horizontalAlignment("center"); styleQ.verticalAlignment("center"); styleQ.bold(true); - }); +} + +const makeExcelFile = async(workbook: XlsxPopulate.Workbook,fileNm:string)=>{ const outputData = await workbook.output("array-buffer"); const blob = new Blob([outputData], { type: "application/octet-stream" }); const link = document.createElement("a"); link.href = URL.createObjectURL(blob); link.download = fileNm+".xlsx"; link.click(); + +} + + +export const excelStyle = async (data: ArrayBuffer, range: string = "A1:V3", fileNm:string) => { + const workbook = await XlsxPopulate.Workbook.fromData(data); + const rangeCells = workbook.activeSheet(0)?.sheet(0)?.range(range); + console.log(rangeCells); + rangeCells?.style((styleQ)=>{ + makeCellStyle(styleQ) + }); + makeExcelFile(workbook, fileNm) + + +} + + +export const excelArrayStyle = async (data: ArrayBuffer, range: [typeof sheetParameter], fileNm:string) => { + const workbook = await XlsxPopulate.Workbook.fromData(data); + if(range){ + range.forEach((item , index)=>{ + const rangeCells = workbook.activeSheet(index)?.sheet(index)?.range(item.headerRange); + console.log(rangeCells); + rangeCells?.style((styleQ)=>{ + makeCellStyle(styleQ) + }); + } ) + } + makeExcelFile(workbook, fileNm) + } \ No newline at end of file diff --git a/packages/common/business_components/src/ui/excel/index.tsx b/packages/common/business_components/src/ui/excel/index.tsx index c17aa0660..e33b0c88d 100644 --- a/packages/common/business_components/src/ui/excel/index.tsx +++ b/packages/common/business_components/src/ui/excel/index.tsx @@ -1,5 +1,5 @@ import * as XLSX from 'xlsx'; -import { excelStyle } from './excelStyle'; +import { excelArrayStyle, excelStyle } from './excelStyle'; type ExcelDownloadProps = { data: JSON | JSON[]; @@ -32,7 +32,6 @@ export const filePicked=(oFile: File, headerNum:number=0):Promise =>{ + // Create A File Reader HTML5 + const reader = new FileReader(); + return new Promise((resolve, reject) => { + // Ready The Event For When A File Gets Selected + reader.onload = function (e) { + const data = e.target?.result; + if (!data) { + console.error("Failed to read file: e.target.result is null"); + resolve(undefined); + return; + } + //셀데이터에서 데이트 형식으로 입력하면 yyyymmdd 형식으로 변환 + const wb = XLSX.read(data, { type: 'binary', cellDates: true, dateNF: "yyyymmdd" }); + const arrayXls: JSON[] = [] + let jsonXls: JSON | undefined; + // Loop Over Each Sheet + wb.SheetNames.forEach(function (sheetName,index ) { + const sheet = wb.Sheets[sheetName]; + if (sheet) { + console.log(sheet) + jsonXls = XLSX.utils.sheet_to_json(sheet, { range: headerNum, raw:false }) as unknown as JSON; + arrayXls.push(jsonXls) + } else { + console.error(`Sheet "${sheetName}" is undefined.`); + } + }); + resolve(arrayXls); + + }; + + reader.onerror = function () { + console.error("Error reading file"); + reject(undefined); + }; + + // Tell JS To Start Reading The File.. You could delay this if desired + reader.readAsBinaryString(oFile); + }); +} + + + export const filePickedBlob=(oFile: ArrayBuffer, data:[][],fileNm:string, headerNum:number=0, headerRange='A1:V3'):Promise =>{ // Create A File Reader HTML5 return new Promise(() => { // Ready The Event For When A File Gets Selected @@ -83,4 +125,32 @@ export const filePickedBlob=(oFile: ArrayBuffer, data:[][],fileNm:string, header } }); }); - } \ No newline at end of file + } + +export const sheetParameter ={ + data:[[]], headerNum:0, headerRange:'A1:V3' + } + export const filePickedBlobArray= (oFile: ArrayBuffer,fileNm:string, sheetArray: [typeof sheetParameter]):Promise =>{ // Create A File Reader HTML5 + return new Promise (async () => { + // Ready The Event For When A File Gets Selected + const wb = XLSX.read(oFile, { type: 'binary' }); + // Loop Over Each Sheet + wb.SheetNames.forEach( function (sheetName, index) { + // Obtain The Current Row As CSV + if(sheetArray[index]){ + const sheet = wb.Sheets[sheetName]; + if (sheet) { + //오리진 확인 test 필요 + // const sheet = XLSX.utils.json_to_sheet(data); + XLSX.utils.sheet_add_aoa(sheet, sheetArray[index].data, { origin: 'A'+sheetArray[index].headerNum }); + // XLSX.utils.book_append_sheet(wb, sheet, 'test'); + } else { + console.error(`Sheet "${sheetName}" is undefined.`); + } + } + }); + + + await excelArrayStyle(XLSX.write(wb, { bookType: "xlsx", type: "buffer" }), sheetArray, fileNm) + }); + } \ No newline at end of file From 4bf62a3ee3a801b9a3a8885e9bf10af08a27838f Mon Sep 17 00:00:00 2001 From: YangJinSoo94 Date: Tue, 13 May 2025 11:08:29 +0900 Subject: [PATCH 07/12] =?UTF-8?q?=EB=B0=94=EC=B0=A8=ED=8A=B8=20=EC=88=98?= =?UTF-8?q?=EC=A0=95=EC=99=84=EB=A3=8C=20-=20=EC=83=89=EC=83=81=20?= =?UTF-8?q?=EB=B0=8F=20=EC=83=81=EC=84=B8=20=EB=94=94=EC=9E=90=EC=9D=B8=20?= =?UTF-8?q?=EC=88=98=EC=A0=95=EC=98=88=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../src/ui/chart/BarChart.tsx | 90 ++++++++++++------- .../src/ui/chart/index.tsx | 17 +++- 2 files changed, 74 insertions(+), 33 deletions(-) diff --git a/packages/common/business_components/src/ui/chart/BarChart.tsx b/packages/common/business_components/src/ui/chart/BarChart.tsx index 82499306c..7d55ca4af 100644 --- a/packages/common/business_components/src/ui/chart/BarChart.tsx +++ b/packages/common/business_components/src/ui/chart/BarChart.tsx @@ -1,42 +1,72 @@ "use client" -import type { CSSProperties } from "react"; -import { - Card, - CardContent -} from "./lib/card"; +import { Bar, BarChart, CartesianGrid, Legend, XAxis, YAxis } from "recharts"; import { ChartContainer, ChartTooltip, ChartTooltipContent, type ChartConfig } from "./lib/chart"; -import { Bar, BarChart, CartesianGrid, XAxis } from "recharts"; -export default function Chart(props: { chartConfig: ChartConfig; chartData: any[] | undefined; chartTitle: string}) { +const tooltipClass = "!p-4 text-[12px] w-[9vw] h-[5vw]"; + +type BarChartProps = { + chartConfig: ChartConfig | undefined; + chartData: any[] | undefined; + chartTitle: string; + chartClassName?: string; + graphSize?: number; + yFieldText?: string; + showLegend?: boolean; +} + +export default function Chart({chartConfig=undefined, chartData=[], chartTitle='차트', chartClassName='w-full h-full', graphSize=40, yFieldText='', showLegend=false} : BarChartProps) { + const colors = ["#FF6B6B", "#4ECDC4", "#45B7D1", "#96CEB4", "#FFEEAD", "#D4A5A5"]; + return ( - - - {props.chartTitle && props.chartTitle !== "" && props.chartTitle !== "undefined" ? ( -
- {props.chartTitle} -
): null} -
- - - - value.slice(0, 3)} + <> + {chartTitle && chartTitle !== "" && chartTitle !== "undefined" ? ( +
+ {chartTitle} +
): null} +
+ + + + + + }/> + {chartData && chartData.length > 0 && ( + Object.keys(chartData[0]) + .filter(key => key !== "label") + .map((key, idx) => ( + - }/> - - - -
- - + )) + )} + {showLegend && } +
+
+
+ ) } \ No newline at end of file diff --git a/packages/common/business_components/src/ui/chart/index.tsx b/packages/common/business_components/src/ui/chart/index.tsx index fc5747f3a..0ed410630 100644 --- a/packages/common/business_components/src/ui/chart/index.tsx +++ b/packages/common/business_components/src/ui/chart/index.tsx @@ -18,15 +18,26 @@ const CHART_FORM = { LINEBAR : 'linebar' }; -function MainChart(props: { chartForm: string; chartConfig: any; chartData: any; chartTitle:string; }) { +type MainChartProps = { + chartForm: string; + chartConfig: any; + chartData: any; + chartTitle: string; + chartClassName?: string; + graphSize?: number; + yFieldText? : string; + showLegend? : boolean; +} + +function MainChart(props: MainChartProps) { if( props.chartForm == CHART_FORM.BAR ){ return ( - + ); } else if( props.chartForm == CHART_FORM.LINE ){ return ( - + ); } else if( props.chartForm == CHART_FORM.DN ){ From 8c5f0a98956eb8f0b4ad9be033e387a6719ec350 Mon Sep 17 00:00:00 2001 From: YangJinSoo94 Date: Thu, 15 May 2025 13:40:57 +0900 Subject: [PATCH 08/12] =?UTF-8?q?barChart=20=EB=B2=94=EB=A1=80=20=ED=95=98?= =?UTF-8?q?=EB=8B=A8=EC=97=90=20=EC=9C=84=EC=B9=98=20=EB=B0=8F=20=EC=88=98?= =?UTF-8?q?=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../src/ui/chart/BarChart.tsx | 30 ++++++++++++++++++- 1 file changed, 29 insertions(+), 1 deletion(-) diff --git a/packages/common/business_components/src/ui/chart/BarChart.tsx b/packages/common/business_components/src/ui/chart/BarChart.tsx index 7d55ca4af..814e12620 100644 --- a/packages/common/business_components/src/ui/chart/BarChart.tsx +++ b/packages/common/business_components/src/ui/chart/BarChart.tsx @@ -6,6 +6,19 @@ import { ChartTooltipContent, type ChartConfig } from "./lib/chart"; +import { styles } from "@common/assets"; +import type { CSSProperties } from "react"; + +const styles = { + legendContainer: { + display: "flex", + justifyContent: "center", + gap: "20px", + fontSize: "9px", + fontWeight : "bold", + marginTop: "10px", + } as CSSProperties, +}; const tooltipClass = "!p-4 text-[12px] w-[9vw] h-[5vw]"; @@ -19,6 +32,21 @@ type BarChartProps = { showLegend?: boolean; } +// 커스텀 범례 (글씨 크기 키우기 + 스타일 추가) +const CustomLegend = (props: any) => { + const { payload } = props; + return ( +
+ {payload.map((entry, index) => ( +
+
+ {entry.value} +
+ ))} +
+ ); +}; + export default function Chart({chartConfig=undefined, chartData=[], chartTitle='차트', chartClassName='w-full h-full', graphSize=40, yFieldText='', showLegend=false} : BarChartProps) { const colors = ["#FF6B6B", "#4ECDC4", "#45B7D1", "#96CEB4", "#FFEEAD", "#D4A5A5"]; @@ -63,7 +91,7 @@ export default function Chart({chartConfig=undefined, chartData=[], chartTitle=' /> )) )} - {showLegend && } + {showLegend && }
From d199166e275e3fc6c132d943a11e8c62f988afa0 Mon Sep 17 00:00:00 2001 From: YangJinSoo94 Date: Thu, 15 May 2025 16:53:31 +0900 Subject: [PATCH 09/12] =?UTF-8?q?=EB=B0=94=EC=B0=A8=ED=8A=B8,=20=EB=9D=BC?= =?UTF-8?q?=EC=9D=B8=EC=B0=A8=ED=8A=B8=20=ED=88=B4=ED=8C=81=20=EC=BB=A4?= =?UTF-8?q?=EC=8A=A4=ED=85=80=ED=99=94=20=EC=A7=84=ED=96=89(=EA=B8=80?= =?UTF-8?q?=EC=94=A8=ED=81=AC=EA=B8=B0,=20=EC=9C=84=EC=B9=98,=20=EC=82=AC?= =?UTF-8?q?=EC=9D=B4=EC=A6=88=20=EB=93=B1)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../src/ui/chart/BarChart.tsx | 35 +++++++++++++++++-- .../src/ui/chart/LineChart.tsx | 19 +++++++--- 2 files changed, 47 insertions(+), 7 deletions(-) diff --git a/packages/common/business_components/src/ui/chart/BarChart.tsx b/packages/common/business_components/src/ui/chart/BarChart.tsx index 814e12620..5d4f902e8 100644 --- a/packages/common/business_components/src/ui/chart/BarChart.tsx +++ b/packages/common/business_components/src/ui/chart/BarChart.tsx @@ -32,12 +32,43 @@ type BarChartProps = { showLegend?: boolean; } +// 커스텀 툴팁 +const CustomTooltip = ({ + active, + payload, + label +}: { + active?: boolean; + payload?: Array<{ + name: string; + value: number; + color: string; + }>; + label?: string; +}) => { + if (active && payload && payload.length) { + return ( +
+

{label}

+ {payload.map((item, index) => ( +
+ {/* 색상 박스 추가 (범례 색상과 동일) */} +
+ {` ${item.name} : ${item.value}`} +
+ ))} +
+ ); + } + return null; +}; + // 커스텀 범례 (글씨 크기 키우기 + 스타일 추가) const CustomLegend = (props: any) => { const { payload } = props; return (
- {payload.map((entry, index) => ( + {payload.map((entry, index: number) => (
{entry.value} @@ -77,7 +108,7 @@ export default function Chart({chartConfig=undefined, chartData=[], chartTitle=' fontWeight={500} label={yFieldText ? { value: yFieldText, className:"text-[13px] foont-bold", position: 'top', textAnchor: 'middle', offset: 20 } : undefined} /> - }/> + }/> {chartData && chartData.length > 0 && ( Object.keys(chartData[0]) .filter(key => key !== "label") diff --git a/packages/common/business_components/src/ui/chart/LineChart.tsx b/packages/common/business_components/src/ui/chart/LineChart.tsx index 136034e00..c005efbdf 100644 --- a/packages/common/business_components/src/ui/chart/LineChart.tsx +++ b/packages/common/business_components/src/ui/chart/LineChart.tsx @@ -1,16 +1,14 @@ "use client" import type { CSSProperties } from "react"; +import { CartesianGrid, Legend, Line, LineChart, Tooltip, XAxis, YAxis } from "recharts"; import { Card, CardContent } from "./lib/card"; import { ChartContainer, - ChartTooltip, - ChartTooltipContent, type ChartConfig } from "./lib/chart"; -import { CartesianGrid, Legend, Line, LineChart, Tooltip, XAxis, YAxis } from "recharts"; /** 차트 기본 스타일 */ const styles = { @@ -41,9 +39,20 @@ const styles = { marginTop: "10px", } as CSSProperties, }; - // 커스텀 툴팁 -const CustomTooltip = ({ active, payload, label }) => { +const CustomTooltip = ({ + active, + payload, + label +}: { + active?: boolean; + payload?: Array<{ + name: string; + value: number; + color: string; + }>; + label?: string; +}) => { if (active && payload && payload.length) { return (
From 7678c722c413544b876ec450d9540a57868e09c6 Mon Sep 17 00:00:00 2001 From: YangJinSoo94 Date: Fri, 16 May 2025 14:17:34 +0900 Subject: [PATCH 10/12] =?UTF-8?q?=EB=9D=BC=EC=9D=B8=EB=B0=94,=20=EB=B0=94?= =?UTF-8?q?=EC=B0=A8=ED=8A=B8=20class=20=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../src/ui/chart/BarChart.tsx | 10 ++-- .../src/ui/chart/LineBarChart.tsx | 51 ++++++++----------- 2 files changed, 25 insertions(+), 36 deletions(-) diff --git a/packages/common/business_components/src/ui/chart/BarChart.tsx b/packages/common/business_components/src/ui/chart/BarChart.tsx index 5d4f902e8..1b75beb8b 100644 --- a/packages/common/business_components/src/ui/chart/BarChart.tsx +++ b/packages/common/business_components/src/ui/chart/BarChart.tsx @@ -3,13 +3,11 @@ import { Bar, BarChart, CartesianGrid, Legend, XAxis, YAxis } from "recharts"; import { ChartContainer, ChartTooltip, - ChartTooltipContent, type ChartConfig } from "./lib/chart"; -import { styles } from "@common/assets"; import type { CSSProperties } from "react"; -const styles = { +const customStyles = { legendContainer: { display: "flex", justifyContent: "center", @@ -20,8 +18,6 @@ const styles = { } as CSSProperties, }; -const tooltipClass = "!p-4 text-[12px] w-[9vw] h-[5vw]"; - type BarChartProps = { chartConfig: ChartConfig | undefined; chartData: any[] | undefined; @@ -46,7 +42,7 @@ const CustomTooltip = ({ }>; label?: string; }) => { - if (active && payload && payload.length) { + if (active && payload && payload.length) {ㅇ return (

{label}

@@ -67,7 +63,7 @@ const CustomTooltip = ({ const CustomLegend = (props: any) => { const { payload } = props; return ( -
+
{payload.map((entry, index: number) => (
diff --git a/packages/common/business_components/src/ui/chart/LineBarChart.tsx b/packages/common/business_components/src/ui/chart/LineBarChart.tsx index 5f2ae4715..98f804abf 100644 --- a/packages/common/business_components/src/ui/chart/LineBarChart.tsx +++ b/packages/common/business_components/src/ui/chart/LineBarChart.tsx @@ -9,29 +9,10 @@ import { ChartContainer, type ChartConfig } from "./lib/chart"; -import React from "react"; // 툴팁 커스텀 css /** 차트 기본 스타일 */ -const styles = { - tooltipDiv: { - borderRadius: "5px", - backgroundColor: "white", - padding: "10px", - boxShadow: "0px 2px 10px rgba(0,0,0,0.2)", - width: "180px", // 적절한 크기로 설정 - } as React.CSSProperties, - tooltipLabel: { - fontWeight: "bold", - fontSize: "12px", - marginBottom: "5px", - borderBottom: "1px solid #ddd", - paddingBottom: "3px", - } as React.CSSProperties, - tooltipText: { - fontSize: "11px", - fontWeight:"bold", - } as React.CSSProperties, +const customStyles = { legendContainer: { display: "flex", justifyContent: "center", @@ -43,16 +24,28 @@ const styles = { }; // 커스텀 툴팁 -const CustomTooltip = ({ active, payload, label }) => { +const CustomTooltip = ({ + active, + payload, + label +}: { + active?: boolean; + payload?: Array<{ + name: string; + value: number; + color: string; + }>; + label?: string; +}) => { if (active && payload && payload.length) { return ( -
-

{label}

+
+

{label}

{payload.map((item, index) => (
{/* 색상 박스 추가 (범례 색상과 동일) */}
- {`${item.name}: ${item.value}`} + {` ${item.name} : ${item.value}`}
))}
@@ -65,11 +58,11 @@ const CustomTooltip = ({ active, payload, label }) => { const CustomLegend = (props: any) => { const { payload } = props; return ( -
- {payload.map((entry, index) => ( -
-
- {entry.value} +
+ {payload.map((entry: any, index: number) => ( +
+
+ {entry.value}
))}
From c2a2ca8bafb77730b2e60710a3a02be753f385e2 Mon Sep 17 00:00:00 2001 From: YangJinSoo94 Date: Fri, 16 May 2025 17:38:23 +0900 Subject: [PATCH 11/12] =?UTF-8?q?-=EB=A9=94=EC=9D=B8=EA=B7=B8=EB=9E=98?= =?UTF-8?q?=ED=94=84=20=EA=B0=95=EC=9A=B0=EB=9F=89-bod,=20=EC=9C=A0?= =?UTF-8?q?=EC=9E=85=EB=B0=A9=EC=B6=9CTP=20=EC=99=84=EB=A3=8C,=20-?= =?UTF-8?q?=EA=B3=B5=ED=86=B5=20=EC=B0=A8=ED=8A=B8=20=EC=BB=A4=EC=8A=A4?= =?UTF-8?q?=ED=85=80=20=EC=A7=84=ED=96=89=EC=A4=91?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../src/ui/chart/BarChart.tsx | 2 +- .../src/ui/chart/LineBarChart.tsx | 130 +++++++++----- .../src/ui/chart/LineChart.tsx | 169 ++++++++++-------- .../src/ui/chart/index.tsx | 4 +- 4 files changed, 182 insertions(+), 123 deletions(-) diff --git a/packages/common/business_components/src/ui/chart/BarChart.tsx b/packages/common/business_components/src/ui/chart/BarChart.tsx index 1b75beb8b..b75bf373c 100644 --- a/packages/common/business_components/src/ui/chart/BarChart.tsx +++ b/packages/common/business_components/src/ui/chart/BarChart.tsx @@ -42,7 +42,7 @@ const CustomTooltip = ({ }>; label?: string; }) => { - if (active && payload && payload.length) {ㅇ + if (active && payload && payload.length) { return (

{label}

diff --git a/packages/common/business_components/src/ui/chart/LineBarChart.tsx b/packages/common/business_components/src/ui/chart/LineBarChart.tsx index 98f804abf..35d757619 100644 --- a/packages/common/business_components/src/ui/chart/LineBarChart.tsx +++ b/packages/common/business_components/src/ui/chart/LineBarChart.tsx @@ -1,10 +1,6 @@ "use client" import type { CSSProperties } from "react"; import { Bar, CartesianGrid, ComposedChart, Legend, Line, Tooltip, XAxis, YAxis } from "recharts"; -import { - Card, - CardContent -} from "./lib/card"; import { ChartContainer, type ChartConfig @@ -23,20 +19,29 @@ const customStyles = { } as CSSProperties, }; +type LineBarChartProps = { + chartConfig: ChartConfig | undefined; + chartData: any[] | undefined; + chartTitle: string; + chartClassName?: string; + graphSize?: number; + yFieldText?: string; + showLegend?: boolean; +} + +type LegendEntry = { + value: string; + color: string; + type?: string; + payload?: unknown; +}; + +type LegendProps = { + payload?: LegendEntry[]; +}; + // 커스텀 툴팁 -const CustomTooltip = ({ - active, - payload, - label -}: { - active?: boolean; - payload?: Array<{ - name: string; - value: number; - color: string; - }>; - label?: string; -}) => { +const CustomTooltip = ({ active, payload, label }: { active?: boolean; payload?: Array<{ name: string; value: number; color: string }>; label?: string }) => { if (active && payload && payload.length) { return (
@@ -55,11 +60,13 @@ const CustomTooltip = ({ }; // 커스텀 범례 (글씨 크기 키우기 + 스타일 추가) -const CustomLegend = (props: any) => { +const CustomLegend = (props: LegendProps) => { const { payload } = props; + if (!payload) return null; + return (
- {payload.map((entry: any, index: number) => ( + {payload.map((entry: LegendEntry, index: number) => (
{entry.value} @@ -69,33 +76,66 @@ const CustomLegend = (props: any) => { ); }; -export default function LineBarchart(props: { chartConfig: ChartConfig; chartData: any[] | undefined; chartTitle:string }) { +export default function LineBarchart({ + chartConfig, + chartData, + chartTitle, + chartClassName = 'w-full h-full', + graphSize = 40, + yFieldText, + showLegend = true +}: LineBarChartProps) { + if (!chartData || chartData.length === 0) { + return
데이터가 없습니다.
; + } + + const barConfig = chartConfig?.barVal; + const lineConfig = chartConfig?.lineVal; + return ( - - - {props.chartTitle && props.chartTitle !== "" && props.chartTitle !== "undefined" ? ( +
+ {chartTitle && chartTitle !== "" && chartTitle !== "undefined" ? (
- {props.chartTitle} -
): null} -
- - - - - {/* 왼쪽 Y축: lineVal (정상) */} - - {/* 오른쪽 Y축: barVal (위에서 아래로 반전) */} - - } /> - - {/* 바 차트 (오른쪽 Y축 사용, 위에서 아래로) */} - - {/* 라인 차트 (왼쪽 Y축 사용) */} - - - + {chartTitle}
- - - ) + ) : null} +
+ + + + + {/* 왼쪽 Y축: lineVal */} + + {/* 오른쪽 Y축: barVal */} + + } /> + {showLegend && } + {/* 바 차트 (오른쪽 Y축 사용) */} + + {/* 라인 차트 (왼쪽 Y축 사용) */} + + + +
+
+ ); } \ No newline at end of file diff --git a/packages/common/business_components/src/ui/chart/LineChart.tsx b/packages/common/business_components/src/ui/chart/LineChart.tsx index c005efbdf..1679f8d45 100644 --- a/packages/common/business_components/src/ui/chart/LineChart.tsx +++ b/packages/common/business_components/src/ui/chart/LineChart.tsx @@ -1,35 +1,24 @@ "use client" import type { CSSProperties } from "react"; import { CartesianGrid, Legend, Line, LineChart, Tooltip, XAxis, YAxis } from "recharts"; -import { - Card, - CardContent -} from "./lib/card"; import { ChartContainer, type ChartConfig } from "./lib/chart"; +import type { ContentType } from "recharts/types/component/DefaultLegendContent"; + +type LineChartProps = { + chartConfig: ChartConfig | undefined; + chartData: ChartDataType | undefined; + chartTitle: string; + chartClassName?: string; + graphSize?: number; + yFieldText?: string; + showLegend?: boolean; +} /** 차트 기본 스타일 */ -const styles = { - tooltipDiv: { - borderRadius: "5px", - backgroundColor: "white", - padding: "10px", - boxShadow: "0px 2px 10px rgba(0,0,0,0.2)", - width: "180px", // 적절한 크기로 설정 - } as React.CSSProperties, - tooltipLabel: { - fontWeight: "bold", - fontSize: "12px", - marginBottom: "5px", - borderBottom: "1px solid #ddd", - paddingBottom: "3px", - } as React.CSSProperties, - tooltipText: { - fontSize: "11px", - fontWeight:"bold", - } as React.CSSProperties, +const customStyles = { legendContainer: { display: "flex", justifyContent: "center", @@ -39,29 +28,29 @@ const styles = { marginTop: "10px", } as CSSProperties, }; + +type LegendEntry = { + value: string; + color: string; + type?: string; + payload?: unknown; +}; + +type LegendProps = { + payload?: LegendEntry[]; +}; + // 커스텀 툴팁 -const CustomTooltip = ({ - active, - payload, - label -}: { - active?: boolean; - payload?: Array<{ - name: string; - value: number; - color: string; - }>; - label?: string; -}) => { +const CustomTooltip = ({ active, payload, label }: { active?: boolean; payload?: Array<{ name: string; value: number; color: string }>; label?: string }) => { if (active && payload && payload.length) { return ( -
-

{label}

+
+

{label}

{payload.map((item, index) => (
{/* 색상 박스 추가 (범례 색상과 동일) */}
- {`${item.name}: ${item.value}`} + {` ${item.name} : ${item.value}`}
))}
@@ -71,60 +60,90 @@ const CustomTooltip = ({ }; // 커스텀 범례 (글씨 크기 키우기 + 스타일 추가) -const CustomLegend = (props: any) => { +const CustomLegend = (props: LegendProps) => { const { payload } = props; + if (!payload) return null; + return ( -
- {payload.map((entry, index) => ( -
-
- {entry.value} +
+ {payload.map((entry: LegendEntry, index: number) => ( +
+
+ {entry.value}
))}
); }; -type DataType = {label : string; val:number}; +type DataType = {label: string; val: string}; + type ChartDataType = { - chartData1: DataType[]; - chartData2: DataType[]; + [key: string]: DataType[]; }; -export default function Linechart(props: { chartConfig: ChartConfig; chartData: ChartDataType; chartTitle:string}) { - const mergedData = Object.values(props.chartData).reduce((acc, dataset) => { +export default function Linechart({ + chartConfig, + chartData, + chartTitle, + chartClassName = 'w-full h-full', + graphSize = 40, + yFieldText, + showLegend = true +}: LineChartProps) { + if(!chartData || Object.keys(chartData).length === 0){ + return
데이터가 없습니다.
+ } + + // 데이터 병합 및 변환 + const mergedData = Object.entries(chartData).reduce>((acc, [key, dataset]) => { dataset.forEach((entry, index) => { if (!acc[index]) { - acc[index] = { label: entry.label }; // label 추가 + acc[index] = { label: entry.label }; } - const key = Object.keys(props.chartData).find(k => props.chartData[k] === dataset); - if (key) acc[index][key] = entry.val; + acc[index][key] = parseFloat(entry.val); }); return acc; - }, [] as any[]); + }, []); return ( - - - {props.chartTitle && props.chartTitle !== "" && props.chartTitle !== "undefined" ? ( +
+ {chartTitle && chartTitle !== "" && chartTitle !== "undefined" ? (
- {props.chartTitle} -
): null} -
- - - - - - } /> - - {Object.keys(props.chartData).map((key, index) => ( - - ))} - - + {chartTitle}
- - - ) + ) : null} +
+ + + + + + } /> + {showLegend && } + {Object.keys(chartData).map((key) => { + const config = chartConfig?.[key]; + return ( + + ); + })} + + +
+
+ ); } \ No newline at end of file diff --git a/packages/common/business_components/src/ui/chart/index.tsx b/packages/common/business_components/src/ui/chart/index.tsx index 0ed410630..87cabc18a 100644 --- a/packages/common/business_components/src/ui/chart/index.tsx +++ b/packages/common/business_components/src/ui/chart/index.tsx @@ -37,7 +37,7 @@ function MainChart(props: MainChartProps) { ); } else if( props.chartForm == CHART_FORM.LINE ){ return ( - + ); } else if( props.chartForm == CHART_FORM.DN ){ @@ -47,7 +47,7 @@ function MainChart(props: MainChartProps) { ); } else if (props.chartForm == CHART_FORM.LINEBAR){ return ( - + ); } From 7c7cc47d20c3a7b440ba3bb576407605b29233ca Mon Sep 17 00:00:00 2001 From: YangJinSoo94 Date: Mon, 19 May 2025 09:52:14 +0900 Subject: [PATCH 12/12] =?UTF-8?q?-chart=20yFieldText2=20=EC=B6=94=EA=B0=80?= =?UTF-8?q?(=EB=9D=BC=EC=9D=B8=EB=B0=94=EB=8A=94=202=EA=B0=9C)=20-?= =?UTF-8?q?=EB=8C=80=EC=8B=9C=EB=B3=B4=EB=93=9C=20=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../src/ui/chart/LineBarChart.tsx | 11 +++++++---- .../src/ui/chart/LineChart.tsx | 2 +- .../src/ui/chart/index.tsx | 19 +++++++++++++++++-- 3 files changed, 25 insertions(+), 7 deletions(-) diff --git a/packages/common/business_components/src/ui/chart/LineBarChart.tsx b/packages/common/business_components/src/ui/chart/LineBarChart.tsx index 35d757619..6db4c3b0a 100644 --- a/packages/common/business_components/src/ui/chart/LineBarChart.tsx +++ b/packages/common/business_components/src/ui/chart/LineBarChart.tsx @@ -5,6 +5,7 @@ import { ChartContainer, type ChartConfig } from "./lib/chart"; +import type { ContentType } from "recharts/types/component/DefaultLegendContent"; // 툴팁 커스텀 css /** 차트 기본 스타일 */ @@ -25,7 +26,8 @@ type LineBarChartProps = { chartTitle: string; chartClassName?: string; graphSize?: number; - yFieldText?: string; + yFieldText?: string | undefined; + yFieldText2?: string | undefined; showLegend?: boolean; } @@ -83,6 +85,7 @@ export default function LineBarchart({ chartClassName = 'w-full h-full', graphSize = 40, yFieldText, + yFieldText2, showLegend = true }: LineBarChartProps) { if (!chartData || chartData.length === 0) { @@ -110,11 +113,11 @@ export default function LineBarchart({ {/* 왼쪽 Y축: lineVal */} - + {yFieldText && } {/* 오른쪽 Y축: barVal */} - + {yFieldText2 && } } /> - {showLegend && } + {showLegend && } {/* 바 차트 (오른쪽 Y축 사용) */} - + } /> {showLegend && } {Object.keys(chartData).map((key) => { diff --git a/packages/common/business_components/src/ui/chart/index.tsx b/packages/common/business_components/src/ui/chart/index.tsx index 87cabc18a..b30eef3c3 100644 --- a/packages/common/business_components/src/ui/chart/index.tsx +++ b/packages/common/business_components/src/ui/chart/index.tsx @@ -18,6 +18,20 @@ const CHART_FORM = { LINEBAR : 'linebar' }; +/** + * 차트 컴포넌트 + * @param chartForm 차트 형태 + * @param chartConfig 차트 설정 + * @param chartData 차트 데이터 + * @param chartTitle 차트 제목 + * @param chartClassName 차트 클래스명 + * @param graphSize 차트 크기 + * @param yFieldText 차트 Y축 텍스트 + * @param yFieldText2 차트 Y축 텍스트2(라인바 차트때 사용) + * @param showLegend 차트 범례 표시 여부 + * @returns + */ + type MainChartProps = { chartForm: string; chartConfig: any; @@ -25,7 +39,8 @@ type MainChartProps = { chartTitle: string; chartClassName?: string; graphSize?: number; - yFieldText? : string; + yFieldText? : string | undefined; + yFieldText2? : string | undefined; showLegend? : boolean; } @@ -47,7 +62,7 @@ function MainChart(props: MainChartProps) { ); } else if (props.chartForm == CHART_FORM.LINEBAR){ return ( - + ); }