Merge commit 'b00e5c48435a3507ad24cf5c8fab12f1d86e5ced' into iipbs/wr/form_fuction

This commit is contained in:
jyha00
2025-06-10 15:54:33 +09:00
2 changed files with 109 additions and 19 deletions
@@ -1,27 +1,116 @@
"use client"
import { Pie, PieChart } from "recharts";
import type { CSSProperties } from "react";
import {
Card,
CardContent
} from "./lib/card";
import {
ChartContainer,
ChartTooltip,
ChartTooltipContent,
type ChartConfig
} from "./lib/chart";
import { ChartContainer, ChartTooltip } from "./lib/chart";
// eslint-disable-next-line react-refresh/only-export-components
export default (props: { chartConfig: ChartConfig; chartData: never[] | undefined; chartTitle:string}) =>{
type ChartDataItem = {
label: string;
val: number;
};
type ChartConfigItem = {
label: string;
fill: string;
};
type ChartConfig = {
[key: string]: ChartConfigItem;
};
type TooltipPayloadItem = {
name: string;
value: number;
payload: {
label: string;
val: number;
fill: string;
};
};
type LegendEntry = {
value: string;
fill: string;
type?: string;
payload: {
label: string;
val: number;
fill: string;
};
};
type LegendProps = {
payload?: LegendEntry[];
};
/** 차트 기본 스타일 */
const customStyles = {
legendContainer: {
display: "flex",
justifyContent: "center",
gap: "20px",
fontSize: "9px",
fontWeight: "bold",
marginTop: "10px",
} as CSSProperties,
};
// 커스텀 툴팁
const CustomTooltip = ({ active, payload, label }: {
active?: boolean;
payload?: TooltipPayloadItem[];
label?: string
}) => {
if (active && payload && payload.length) {
return (
<div className="w-[180px] rounded bg-white p-2.5 shadow-[0px_2px_10px_rgba(0,0,0,0.2)]">
<p className="text-[12px] font-bold m-3">{label}</p>
{payload.map((item, index) => (
<div key={index} style={{ marginTop: "10px", display: "flex", alignItems: "center", gap: "8px" }}>
<div style={{ width: "10px", height: "10px", backgroundColor: item.payload.fill, borderRadius: "50%" }}></div>
<span className="text-[11px] font-bold">{` ${item.name} : ${item.value.toString().replace(/\B(?=(\d{3})+(?!\d))/g, ",")}`}</span>
</div>
))}
</div>
);
}
return null;
};
// 커스텀 범례
const CustomLegend = (props: LegendProps) => {
const { payload } = props;
if (!payload) return null;
return (
<div style={customStyles.legendContainer}>
{payload.map((entry: LegendEntry, index: number) => (
<div key={`legend-${index}`} className="flex items-center gap-2">
<div className="w-[14px] h-[14px] ml-10" style={{ backgroundColor: entry.fill }}></div>
<span className="text-[12px]">{entry.value}</span>
</div>
))}
</div>
);
};
export default function DoughnutChart(props: {
chartConfig: ChartConfig;
chartData: ChartDataItem[] | undefined;
chartTitle: string
}) {
return (
<Card className="flex flex-col">
<CardContent className="flex-1 pb-0">
{props.chartTitle && props.chartTitle !== "" && props.chartTitle !== "undefined" ? (
<div style={{ textAlign: "left", fontSize: "13px", fontWeight: "bold", marginTop:"13px", marginBottom: "10px" }}>
{props.chartTitle}
</div>): null}
{props.chartTitle && props.chartTitle !== "" && props.chartTitle !== "undefined" ? (
<div style={{ textAlign: "left", fontSize: "13px", fontWeight: "bold", marginTop:"13px", marginBottom: "10px" }}>
{props.chartTitle}
</div>
) : null}
<div className="h-[calc(100%-42px)] justify-center">
<ChartContainer
config={props.chartConfig}
@@ -30,18 +119,22 @@ export default (props: { chartConfig: ChartConfig; chartData: never[] | undefine
<PieChart>
<ChartTooltip
cursor={false}
content={<ChartTooltipContent hideLabel />}
content={<CustomTooltip />}
/>
<Pie
data={props.chartData}
data={props.chartData?.map(item => ({
...item,
fill: props.chartConfig[item.label]?.fill || 'blue'
}))}
dataKey="val"
nameKey="label"
innerRadius={60}
/>
<CustomLegend />
</PieChart>
</ChartContainer>
</div>
</CardContent>
</Card>
)
);
}
@@ -92,9 +92,6 @@ export default function LineBarchart({
return <div>데이터가 없습니다.</div>;
}
const barConfig = chartConfig?.barVal;
const lineConfig = chartConfig?.lineVal;
// 데이터의 최대값 계산
const getMaxValue = (data: any[], keyPrefix: string) => {
return Math.max(...data.map(item =>