-
-
-
- value.slice(0, 3)}
- />
- }/>
-
-
-
+
+ {payload.map((entry: unknown, index: number) => (
+
+
+
{(entry as {value: string}).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"];
+ const getMaxValue = (data: any[], keyPrefix: string) => {
+ return Math.max(...data.map(item =>
+ Object.keys(item)
+ .filter(key => key.includes(keyPrefix))
+ .map(key => item[key])
+ ).flat());
+ };
+
+ const barMaxValue = getMaxValue(chartData, 'barVal');
+ return (
+ <>
+ {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/LineBarChart.tsx b/packages/common/business_components/src/ui/chart/LineBarChart.tsx
index 5f2ae4715..2e83458d8 100644
--- a/packages/common/business_components/src/ui/chart/LineBarChart.tsx
+++ b/packages/common/business_components/src/ui/chart/LineBarChart.tsx
@@ -1,37 +1,15 @@
"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
} from "./lib/chart";
-import React from "react";
+import type { ContentType } from "recharts/types/component/DefaultLegendContent";
-// 툴팁 커스텀 css
+// 툴크 커스텀 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",
@@ -42,17 +20,39 @@ const styles = {
} as CSSProperties,
};
+type LineBarChartProps = {
+ chartConfig: ChartConfig | undefined;
+ chartData: any[] | undefined;
+ chartTitle: string;
+ chartClassName?: string;
+ graphSize?: number;
+ yFieldText?: string | undefined;
+ yFieldText2?: string | undefined;
+ showLegend?: boolean;
+}
+
+type LegendEntry = {
+ value: string;
+ color: string;
+ type?: string;
+ payload?: unknown;
+};
+
+type LegendProps = {
+ payload?: LegendEntry[];
+};
+
// 커스텀 툴팁
-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.toString().replace(/\B(?=(\d{3})+(?!\d))/g, ",")}`}
))}
@@ -62,47 +62,128 @@ const CustomTooltip = ({ active, payload, label }) => {
};
// 커스텀 범례 (글씨 크기 키우기 + 스타일 추가)
-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) => (
+
))}
);
};
-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,
+ yFieldText2,
+ showLegend = true
+}: LineBarChartProps) {
+ if (!chartData || chartData.length === 0) {
+ return
데이터가 없습니다.
;
+ }
+
+ const barConfig = chartConfig?.barVal;
+ const lineConfig = chartConfig?.lineVal;
+
+ // 데이터의 최대값 계산
+ const getMaxValue = (data: any[], keyPrefix: string) => {
+ return Math.max(...data.map(item =>
+ Object.keys(item)
+ .filter(key => key.includes(keyPrefix))
+ .map(key => item[key])
+ ).flat());
+ };
+
+ const lineMaxValue = getMaxValue(chartData, 'lineVal');
+ const barMaxValue = getMaxValue(chartData, 'barVal');
+
return (
-
-
- {props.chartTitle && props.chartTitle !== "" && props.chartTitle !== "undefined" ? (
+
+ {chartTitle && chartTitle !== "" && chartTitle !== "undefined" ? (
- {props.chartTitle}
-
): null}
-
-
-
-
-
- {/* 왼쪽 Y축: lineVal (정상) */}
-
- {/* 오른쪽 Y축: barVal (위에서 아래로 반전) */}
-
- } />
-
-
+ {chartTitle}
-
-
- )
+ ) : null}
+
+
+
+
+
+ {/* 왼쪽 Y축: lineVal */}
+ {yFieldText && }
+ {/* 오른쪽 Y축: barVal */}
+ {yFieldText2 && }
+ } />
+ {showLegend && }
+ {/* 바 차트 (오른쪽 Y축 사용) */}
+ {Object.keys(chartData[0]).map((key, index) => {
+ const config = chartConfig?.[key];
+ if(key.includes("barVal")) {
+ return (
+
+ );
+ }
+ return null;
+ })}
+ {/* 라인 차트 (왼쪽 Y축 사용) */}
+ {Object.keys(chartData[0]).map((key, index) => {
+ const config = chartConfig?.[key];
+ if(key.includes("lineVal")) {
+ return (
+
+ );
+ }
+ return null;
+ })}
+
+
+
+
+ );
}
\ 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 136034e00..4014702b6 100644
--- a/packages/common/business_components/src/ui/chart/LineChart.tsx
+++ b/packages/common/business_components/src/ui/chart/LineChart.tsx
@@ -1,37 +1,24 @@
"use client"
import type { CSSProperties } from "react";
-import {
- Card,
- CardContent
-} from "./lib/card";
+import { CartesianGrid, Legend, Line, LineChart, Tooltip, XAxis, YAxis } from "recharts";
import {
ChartContainer,
- ChartTooltip,
- ChartTooltipContent,
type ChartConfig
} from "./lib/chart";
-import { CartesianGrid, Legend, Line, LineChart, Tooltip, XAxis, YAxis } from "recharts";
+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",
@@ -42,17 +29,28 @@ const styles = {
} as CSSProperties,
};
+type LegendEntry = {
+ value: string;
+ color: string;
+ type?: string;
+ payload?: unknown;
+};
+
+type LegendProps = {
+ payload?: LegendEntry[];
+};
+
// 커스텀 툴팁
-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}`}
))}
@@ -62,60 +60,108 @@ const CustomTooltip = ({ active, payload, label }) => {
};
// 커스텀 범례 (글씨 크기 키우기 + 스타일 추가)
-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) => (
+
))}
);
};
-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 hasNoData = Object.values(chartData).every(dataset => !dataset || dataset.length === 0);
+ if (hasNoData) {
+ 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[]);
+ }, []);
+
+ // 데이터의 최대값 계산
+ const getMaxValue = (data: ChartDataType): number => {
+ return Math.max(...Object.values(data).flatMap(dataset =>
+ dataset.map(entry => {
+ const val = parseFloat(entry.val);
+ return isNaN(val) ? 0 : val;
+ })
+ ));
+ };
+
+ const lineMaxValue = getMaxValue(chartData);
return (
-
-
- {props.chartTitle && props.chartTitle !== "" && props.chartTitle !== "undefined" ? (
-
- {props.chartTitle}
-
): null}
-
-
-
-
-
-
- } />
-
-
+
+ {chartTitle && chartTitle !== "" && chartTitle !== "undefined" ? (
+
+ {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 fc5747f3a..b30eef3c3 100644
--- a/packages/common/business_components/src/ui/chart/index.tsx
+++ b/packages/common/business_components/src/ui/chart/index.tsx
@@ -18,15 +18,41 @@ const CHART_FORM = {
LINEBAR : 'linebar'
};
-function MainChart(props: { chartForm: string; chartConfig: any; chartData: any; chartTitle:string; }) {
+/**
+ * 차트 컴포넌트
+ * @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;
+ chartData: any;
+ chartTitle: string;
+ chartClassName?: string;
+ graphSize?: number;
+ yFieldText? : string | undefined;
+ yFieldText2? : string | undefined;
+ 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 ){
@@ -36,7 +62,7 @@ function MainChart(props: { chartForm: string; chartConfig: any; chartData: any;
);
} else if (props.chartForm == CHART_FORM.LINEBAR){
return (
-
+
);
}
diff --git a/packages/common/business_components/src/ui/date_picker/index.tsx b/packages/common/business_components/src/ui/date_picker/index.tsx
index 16aa2c075..d3a6a05c1 100644
--- a/packages/common/business_components/src/ui/date_picker/index.tsx
+++ b/packages/common/business_components/src/ui/date_picker/index.tsx
@@ -9,12 +9,23 @@ import type { DayPickerSingleProps } from "react-day-picker";
import { Button, Calendar, Popover, PopoverContent, PopoverTrigger } from "@common/components/ui";
import { cn } from "../../lib/utils";
import { FormControl } from "../form";
+import { ko } from "date-fns/locale";
+import { useState } from "react";
type DatePickerProps = DayPickerSingleProps & { field: { value: Date; onChange: (date: Date | undefined) => void } ,selectClass:string|undefined };
+
export function DatePicker({ field, selectClass }: DatePickerProps) {
+
+
+ const [open, setOpen] = useState(false);
+
+ const handleSelect = (date: Date | undefined) => {
+ field.onChange(date); // React Hook Form 업데이트
+ setOpen(false);
+ };
return (
-
+
diff --git a/packages/common/business_components/src/ui/dialog_ui/index.tsx b/packages/common/business_components/src/ui/dialog_ui/index.tsx
new file mode 100644
index 000000000..b9e341279
--- /dev/null
+++ b/packages/common/business_components/src/ui/dialog_ui/index.tsx
@@ -0,0 +1,46 @@
+import { Dialog, DialogContent, DialogDescription, DialogFooter, DialogHeader } from "@common/components";
+
+type ModalType = {
+ bodyClass: string;
+ body: React.ReactNode;
+ open?: boolean;
+ onOpenChange?: (open: boolean) => void;
+ noHeaderYn?: 'N' | 'Y';
+ noFooterYn?: 'N' | 'Y';
+ headerTitle?: string;
+ footerContent?: React.ReactNode;
+}
+
+export function UiModal({
+ bodyClass,
+ body,
+ open,
+ onOpenChange,
+ noHeaderYn = 'N',
+ noFooterYn = 'N',
+ headerTitle,
+ footerContent
+}: ModalType) {
+
+ return (
+ <>
+
+ >
+ );
+}
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..3c8f8d219
--- /dev/null
+++ b/packages/common/business_components/src/ui/excel/excelStyle.tsx
@@ -0,0 +1,65 @@
+// import type { sheetParameter } from '@/index';
+import * as XlsxPopulate from '@xlsx/xlsx-populate';
+import type { SheetParameter } from './index';
+
+
+
+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(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: 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)
+ });
+
+ // 모든 데이터 셀에 세로 가운데 정렬 적용
+ if (item.data && item.data.length > 0) {
+ const numCols = item.data[0]?.length || 0;
+ const numRows = item.data.length;
+ const dataRange = workbook.activeSheet(index)?.sheet(index)?.range(`A1:${String.fromCharCode(65 + numCols - 1)}${numRows}`);
+ dataRange?.style((styleQ) => {
+ styleQ.verticalAlignment("center");
+ });
+ }
+ });
+ }
+ 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
new file mode 100644
index 000000000..96f3b3e5d
--- /dev/null
+++ b/packages/common/business_components/src/ui/excel/index.tsx
@@ -0,0 +1,167 @@
+import * as XLSX from 'xlsx';
+import { excelArrayStyle, excelStyle } from './excelStyle';
+
+type ExcelDownloadProps = {
+ data: JSON | JSON[];
+ fileName: string;
+ sheetNameArray: string[];
+ headerRange?: string;
+}
+
+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]);
+ excelStyle(XLSX.write(workbook, { bookType: "xlsx", type: "buffer" }), headerRange, fileName);
+};
+
+export type SheetParameter = {
+ data: any[][];
+ headerNum: number;
+ headerRange: string;
+ sheetName: string;
+ startCol?: string;
+};
+
+export const downloadMultipleExcel = (fileName = new Date().getTime().toString() + '.xlsx', sheetArray: SheetParameter[]): Promise => {
+ const workbook = XLSX.utils.book_new();
+ for (let i = 0; i < sheetArray.length; i++) {
+ // Create a new worksheet for each sheet
+ const worksheet = XLSX.utils.aoa_to_sheet(sheetArray[i]?.data || [[]]);
+ // Add data to the worksheet starting from the specified header number
+ if (sheetArray && sheetArray[i]?.headerNum) {
+ XLSX.utils.sheet_add_aoa(worksheet, sheetArray[i]?.data || [[]], { origin: `A${sheetArray[i]?.headerNum}` });
+ }
+ // Append the worksheet to the workbook with the specified sheet name
+ XLSX.utils.book_append_sheet(workbook, worksheet, sheetArray[i]?.sheetName);
+ }
+ if (sheetArray.length > 0) {
+ excelArrayStyle(XLSX.write(workbook, { bookType: "xlsx", type: "buffer" }), sheetArray, fileName);
+ }
+ return Promise.resolve(undefined);
+};
+
+export const filePicked=(oFile: File, headerNum:number=0, dateType="yyyymmdd"):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: dateType });
+ let jsonXls: JSON | undefined;
+ // 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) {
+ jsonXls = XLSX.utils.sheet_to_json(sheet, { range: headerNum, raw:false }) 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 filePickedArray=(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', startCol?:number):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);
+ const excelStartCol = startCol?startCol.toString():'A';
+ XLSX.utils.sheet_add_aoa(sheet, data, { origin: excelStartCol+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.`);
+ }
+ }
+ });
+ });
+ }
+
+export const filePickedBlobArray = (oFile: ArrayBuffer, fileNm: string, sheetArray: SheetParameter[]): Promise => {
+ return new Promise(async () => {
+ const wb = XLSX.read(oFile, { type: 'binary' });
+ wb.SheetNames.forEach(function (sheetName, index) {
+ if (sheetArray[index]) {
+ const sheet = wb.Sheets[sheetName];
+ if (sheet) {
+ const startCol = sheetArray[index].startCol ? sheetArray[index].startCol : 'A';
+ XLSX.utils.sheet_add_aoa(sheet, sheetArray[index].data, { origin: startCol + sheetArray[index].headerNum });
+ } else {
+ console.error(`Sheet "${sheetName}" is undefined.`);
+ }
+ }
+ });
+ await excelArrayStyle(XLSX.write(wb, { bookType: "xlsx", type: "buffer" }), sheetArray, fileNm)
+ });
+}
\ No newline at end of file
diff --git a/packages/common/business_components/src/ui/file-upload/index.tsx b/packages/common/business_components/src/ui/file-upload/index.tsx
index 3d581ed05..22ef56820 100644
--- a/packages/common/business_components/src/ui/file-upload/index.tsx
+++ b/packages/common/business_components/src/ui/file-upload/index.tsx
@@ -6,41 +6,51 @@ const dropZoneConfig = {
maxSize: 1024 * 1024 * 4,
multiple: true,
};
+
+
+ interface FileUploadProps {
+ files: File[]; // 파일 배열
+ setFiles: (files: File[] | null) => void; // 파일 배열 세터
+ fileTypes?: string; // 옵션 (파일 확장자 문자열)
+ onFileClick?:(index:number)=> void; // 파일 클릭 핸들러
+
+ }
+
export const FileUpload =
-(({ ...props }) => {
+({ files, setFiles, fileTypes ,onFileClick}: FileUploadProps) => {
return (
-
-
+
+
- Click to upload
- or drag and drop
+ 클릭 후 업로드
+ 혹은 파일을 드래그 해주세요
- {!props?.fileTypes?"SVG, PNG, JPG, GIF":props?.fileTypes}
+ {!fileTypes?"SVG, PNG, JPG, GIF":fileTypes}
- {props.files &&
- props.files.length > 0 &&
- props.files.map((file:File, i:number) => (
-
+ {files&&
+ files.length > 0 &&
+ files.map((file:File, i:number) => (
+
- {file.name}
+ {file.name}
))}
)
-})
\ No newline at end of file
+}
\ No newline at end of file
diff --git a/packages/common/business_components/src/ui/file-upload/lib/file_upload.tsx b/packages/common/business_components/src/ui/file-upload/lib/file_upload.tsx
index 3db4bcceb..3fe1aafd8 100644
--- a/packages/common/business_components/src/ui/file-upload/lib/file_upload.tsx
+++ b/packages/common/business_components/src/ui/file-upload/lib/file_upload.tsx
@@ -286,9 +286,9 @@ FileUploaderContent.displayName = "FileUploaderContent";
export const FileUploaderItem = forwardRef<
HTMLDivElement,
- { index: number } & React.HTMLAttributes
->(({ className, index, children, ...props }, ref) => {
- const { removeFileFromSet, activeIndex, direction } = useFileUpload();
+ { index: number } & React.HTMLAttributes &{onFileClick?:(index:number)=>void}
+>(({ className, index, children, onFileClick, ...props }, ref) => {
+ const { removeFileFromSet , activeIndex, direction } = useFileUpload();
const isSelected = index === activeIndex;
return (
-
+
{
+
+
+ }}>
{children}