비즈니스컴포넌트 업데이트

This commit is contained in:
leedj312
2025-05-29 19:56:18 +09:00
parent ee7e441931
commit 41df013bb3
11 changed files with 117 additions and 97 deletions
@@ -64,10 +64,10 @@ const CustomLegend = (props: any) => {
const { payload } = props;
return (
<div style={customStyles.legendContainer}>
{payload.map((entry, index: number) => (
{payload.map((entry: unknown, index: number) => (
<div key={`legend-${index}`} className="flex items-center gap-2">
<div className="w-[14px] h-[14px] ml-10" style={{ backgroundColor: entry.color }}></div>
<span className="text-[12px]">{entry.value}</span>
<div className="w-[14px] h-[14px] ml-10" style={{ backgroundColor: (entry as {color: string}).color }}></div>
<span className="text-[12px]">{(entry as {value: string}).value}</span>
</div>
))}
</div>
@@ -76,7 +76,15 @@ const CustomLegend = (props: any) => {
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" ? (
@@ -102,7 +110,8 @@ export default function Chart({chartConfig=undefined, chartData=[], chartTitle='
axisLine={true}
fontSize={12}
fontWeight={500}
label={yFieldText ? { value: yFieldText, className:"text-[13px] foont-bold", position: 'top', textAnchor: 'middle', offset: 20 } : undefined}
domain={[0, Math.ceil(barMaxValue * 1)]}
label={yFieldText ? { value: yFieldText, className:"text-[13px] font-bold", position: 'insideLeft', angle: -90, offset: 20 } : undefined}
/>
<ChartTooltip cursor={false} content={<CustomTooltip active={undefined} payload={undefined} label={undefined} />}/>
{chartData && chartData.length > 0 && (
@@ -113,20 +113,21 @@ export default function Linechart({
}, []);
// 데이터의 최대값 계산
const getMaxValue = (data: ChartDataType, keyPrefix: string) => {
return Math.max(...Object.values(data).map(item =>
Object.keys(item)
.filter(key => key.includes(keyPrefix))
.map(key => item[key] as unknown as number)
).flat());
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, 'lineVal');
const lineMaxValue = getMaxValue(chartData);
return (
<div className={chartClassName}>
{chartTitle && chartTitle !== "" && chartTitle !== "undefined" ? (
<div style={{ textAlign: "left", fontSize: "13px", fontWeight: "bold", marginTop:"13px", marginBottom: "10px" }}>
<div className="text-left text-[13px] font-bold mt-[13px] mb-[10px]">
{chartTitle}
</div>
) : null}
@@ -1,5 +1,6 @@
import type { sheetParameter } from '@/index';
// import type { sheetParameter } from '@/index';
import * as XlsxPopulate from '@xlsx/xlsx-populate';
import type { SheetParameter } from './index';
@@ -38,13 +39,13 @@ export const excelStyle = async (data: ArrayBuffer, range: string = "A1:V3", fil
}
export const excelArrayStyle = async (data: ArrayBuffer, range: [typeof sheetParameter], fileNm:string) => {
export const excelArrayStyle = async (data: ArrayBuffer, range: SheetParameter[], fileNm: string) => {
const workbook = await XlsxPopulate.Workbook.fromData(data);
if(range){
range.forEach((item , index)=>{
if (range) {
range.forEach((item, index) => {
const rangeCells = workbook.activeSheet(index)?.sheet(index)?.range(item.headerRange);
console.log(rangeCells);
rangeCells?.style((styleQ)=>{
rangeCells?.style((styleQ) => {
makeCellStyle(styleQ)
});
@@ -5,7 +5,7 @@ type ExcelDownloadProps = {
data: JSON | JSON[];
fileName: string;
sheetNameArray: string[];
headerRange: string;
headerRange?: string;
}
export const downloadExcel = ({ data, fileName = new Date().getTime().toString() + '.xlsx', sheetNameArray = ["Sheet1"], headerRange='A1:V3'}: ExcelDownloadProps) => {
@@ -15,17 +15,21 @@ export const downloadExcel = ({ data, fileName = new Date().getTime().toString()
excelStyle(XLSX.write(workbook, { bookType: "xlsx", type: "buffer" }), headerRange, fileName);
};
export const sheetParameter ={
data:[[]], headerNum:0, headerRange:'A1:V3', sheetName:'Sheet1', startCol:0
}
export type SheetParameter = {
data: any[][];
headerNum: number;
headerRange: string;
sheetName: string;
startCol?: string;
};
export const downloadMultipleExcel = (fileName = new Date().getTime().toString() + '.xlsx', sheetArray: Array<typeof sheetParameter>): Promise<JSON | undefined> => {
export const downloadMultipleExcel = (fileName = new Date().getTime().toString() + '.xlsx', sheetArray: SheetParameter[]): Promise<JSON | undefined> => {
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) {
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
@@ -37,7 +41,6 @@ export const downloadMultipleExcel = (fileName = new Date().getTime().toString()
return Promise.resolve(undefined);
};
export const filePicked=(oFile: File, headerNum:number=0, dateType="yyyymmdd"):Promise<JSON | undefined> =>{
// Create A File Reader HTML5
const reader = new FileReader();
@@ -80,8 +83,6 @@ export const filePicked=(oFile: File, headerNum:number=0, dateType="yyyymmdd"):P
});
}
export const filePickedArray=(oFile: File, headerNum:number=0):Promise<JSON[] | undefined> =>{
// Create A File Reader HTML5
const reader = new FileReader();
@@ -123,8 +124,6 @@ export const filePickedArray=(oFile: File, headerNum:number=0):Promise<JSON[] |
});
}
export const filePickedBlob=(oFile: ArrayBuffer, data:[][],fileNm:string, headerNum:number=0, headerRange='A1:V3', startCol?:number):Promise<JSON | undefined> =>{ // Create A File Reader HTML5
return new Promise<JSON | undefined>(() => {
// Ready The Event For When A File Gets Selected
@@ -149,28 +148,20 @@ export const filePickedBlob=(oFile: ArrayBuffer, data:[][],fileNm:string, header
});
}
export const filePickedBlobArray= (oFile: ArrayBuffer,fileNm:string, sheetArray: [typeof sheetParameter]):Promise<JSON | undefined> =>{ // Create A File Reader HTML5
return new Promise<JSON | undefined> (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);
const startCol = sheetArray[index].startCol?sheetArray[index].startCol.toString():'A';
XLSX.utils.sheet_add_aoa(sheet, sheetArray[index].data, { origin: startCol+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)
});
}
export const filePickedBlobArray = (oFile: ArrayBuffer, fileNm: string, sheetArray: SheetParameter[]): Promise<JSON | undefined> => {
return new Promise<JSON | undefined>(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)
});
}
@@ -12,10 +12,12 @@ const dropZoneConfig = {
files: File[]; // 파일 배열
setFiles: (files: File[] | null) => void; // 파일 배열 세터
fileTypes?: string; // 옵션 (파일 확장자 문자열)
onFileClick?:(index:number)=> void; // 파일 클릭 핸들러
}
export const FileUpload =
({ files, setFiles, fileTypes }: FileUploadProps) => {
({ files, setFiles, fileTypes ,onFileClick}: FileUploadProps) => {
return (
<FileUploader
value={files}
@@ -43,9 +45,9 @@ export const FileUpload =
{files&&
files.length > 0 &&
files.map((file:File, i:number) => (
<FileUploaderItem key={i} index={i}>
<FileUploaderItem key={i} index={i} onFileClick={onFileClick }>
<Paperclip className="h-4 w-4 stroke-current" />
<span className="w-4/5 overflow-hidden">{file.name}</span>
<span className="w-4/5 overflow-hidden">{file.name}</span>
</FileUploaderItem>
))}
</FileUploaderContent>
@@ -286,9 +286,9 @@ FileUploaderContent.displayName = "FileUploaderContent";
export const FileUploaderItem = forwardRef<
HTMLDivElement,
{ index: number } & React.HTMLAttributes<HTMLDivElement>
>(({ className, index, children, ...props }, ref) => {
const { removeFileFromSet, activeIndex, direction } = useFileUpload();
{ index: number } & React.HTMLAttributes<HTMLDivElement> &{onFileClick?:(index:number)=>void}
>(({ className, index, children, onFileClick, ...props }, ref) => {
const { removeFileFromSet , activeIndex, direction } = useFileUpload();
const isSelected = index === activeIndex;
return (
<div
@@ -300,11 +300,13 @@ export const FileUploaderItem = forwardRef<
isSelected ? "bg-muted" : "",
)}
{...props}
onClick={() =>{
console.log('onclick');
removeFileFromSet(index)}}
>
<div className="font-medium leading-none tracking-tight flex items-center gap-1.5 h-full !w-4/5 overflow-hidden ">
<div className="font-medium leading-none tracking-tight flex items-center gap-1.5 h-full !w-4/5 overflow-hidden "
onClick={() =>{
}}>
{children}
</div>
<button
@@ -313,7 +315,10 @@ export const FileUploaderItem = forwardRef<
"absolute",
"!w-1/5 z-50",
direction === "rtl" ? "top-1 left-1" : "top-1 right-1",
)}
onClick={() =>{
removeFileFromSet(index)}}
>
<span className="sr-only">remove item {index}</span>
<RemoveIcon className="w-4 h-4 hover:stroke-destructive duration-200 ease-in-out" />
@@ -1,3 +1,5 @@
"use client"
import { Button } from "@common/components/ui";
import { useCallback, useEffect, useState } from "react";
import { SearchFormLeft, type EnvPartSelectDataType } from "../search_form_left";
@@ -1,4 +1,6 @@
import React, { useEffect, useState } from 'react';
"use client"
import React, { useEffect, useState } from 'react';
import type { SelectDataType } from '../search_input_select';
import { SelectBox } from '../select';
@@ -6,37 +6,41 @@ interface TabItem {
label: string;
}
interface UITabProps extends React.HTMLAttributes<HTMLDivElement> {
interface UITabProps extends Omit<React.HTMLAttributes<HTMLDivElement>, 'dir'> {
defaultValue?: string;
tabList: TabItem[];
children: React.ReactNode;
onValueChange?: (value: string) => void;
}
const UITab = React.forwardRef<
const UITab = React.forwardRef<
HTMLDivElement,
UITabProps
>(({defaultValue, tabList, children, onValueChange, ...props}, ref)=>{
return (
<Tabs
defaultValue={defaultValue || tabList[0]?.value}
className={"w-full space-x-1 border-b border-gray-200"}
onValueChange={onValueChange}
{...props}
>
<TabsList className="flex space-x-1">
{tabList.map((tab) => (
<TabsTrigger
key={tab.value}
value={tab.value}
className="py-2 px-4 text-sm font-medium border-b-2 focus:outline-none data-[state=active]:border-blue-500 data-[state=active]:text-blue-600 data-[state=inactive]:border-transparent data-[state=inactive]:text-gray-500 hover:text-gray-700 hover:border-gray-300"
>
{tab.label}
</TabsTrigger>
))}
</TabsList>
{children}
</Tabs>
)
})
>(({defaultValue, tabList, children, onValueChange, ...props}, ref) => {
return (
<Tabs
defaultValue={defaultValue || tabList[0]?.value}
className={"w-full space-x-1 border-b border-gray-200"}
onValueChange={onValueChange}
ref={ref}
{...props}
>
<TabsList className="flex space-x-1">
{tabList.map((tab) => (
<TabsTrigger
key={tab.value}
value={tab.value}
className="py-2 px-4 text-sm font-medium border-b-2 focus:outline-none data-[state=active]:border-blue-500 data-[state=active]:text-blue-600 data-[state=inactive]:border-transparent data-[state=inactive]:text-gray-500 hover:text-gray-700 hover:border-gray-300"
>
{tab.label}
</TabsTrigger>
))}
</TabsList>
{children}
</Tabs>
);
});
UITab.displayName = 'UITab';
export { UITab };
@@ -1,3 +1,5 @@
"use client"
import { Table, TableBody, TableCell, TableHeader, TableRow } from "@common/components";
import { useEffect, useState } from "react";
import { HeadMakeColSpan } from "./HeadMakeColSpan";
@@ -8,7 +8,7 @@ const Pagination = ({ className, ...props }: React.ComponentProps<"nav">) => (
<nav
role="navigation"
aria-label="pagination"
className={cn("mx-auto flex w-full justify-center", className)}
className={cn("mx-auto flex w-full justify-center text-lg mt-2", className)}
{...props}
/>
)
@@ -42,7 +42,7 @@ type PaginationLinkProps = {
const PaginationLink = ({
className,
isActive,
size = "icon",
size = "default",
...props
}: PaginationLinkProps) => (
<a
@@ -52,6 +52,7 @@ const PaginationLink = ({
variant: isActive ? "outline" : "ghost",
size,
}),
"px-4 py-2 text-lg",
className
)}
{...props}
@@ -66,10 +67,10 @@ const PaginationPrevious = ({
<PaginationLink
aria-label="Go to previous page"
size="default"
className={cn("gap-1 pl-2.5", className)}
className={cn("gap-2 pl-4 text-lg", className)}
{...props}
>
<ChevronLeft className="h-4 w-4" />
<ChevronLeft className="h-6 w-6" />
<span>Previous</span>
</PaginationLink>
)
@@ -82,11 +83,11 @@ const PaginationNext = ({
<PaginationLink
aria-label="Go to next page"
size="default"
className={cn("gap-1 pr-2.5", className)}
className={cn("gap-2 pr-4 text-lg", className)}
{...props}
>
<span>Next</span>
<ChevronRight className="h-4 w-4" />
<ChevronRight className="h-6 w-6" />
</PaginationLink>
)
PaginationNext.displayName = "PaginationNext"
@@ -97,10 +98,10 @@ const PaginationEllipsis = ({
}: React.ComponentProps<"span">) => (
<span
aria-hidden
className={cn("flex h-9 w-9 items-center justify-center", className)}
className={cn("flex h-12 w-12 items-center justify-center text-lg", className)}
{...props}
>
<MoreHorizontal className="h-4 w-4" />
<MoreHorizontal className="h-6 w-6" />
<span className="sr-only">More pages</span>
</span>
)