Spaces:
Running
Running
| 'use client'; | |
| /** | |
| * @license | |
| * SPDX-License-Identifier: Apache-2.0 | |
| */ | |
| import { Radar, RadarChart, PolarGrid, PolarAngleAxis, PolarRadiusAxis, ResponsiveContainer, PieChart, Pie, Legend } from 'recharts'; | |
| interface RadarData { | |
| subject: string; | |
| target: number; | |
| user: number; | |
| } | |
| export function DiagnosisRadarChart({ data }: { data: RadarData[] }) { | |
| const renderCustomAxisTick = ({ x, y, payload, cx, cy, ...rest }: any) => { | |
| const { value } = payload; | |
| const words = value.split(' '); | |
| // Calculate alignment based on position relative to center | |
| const textAnchor = x > cx ? 'start' : x < cx ? 'end' : 'middle'; | |
| const dy = y > cy ? 14 : y < cy ? -4 : 4; | |
| return ( | |
| <text | |
| x={x} | |
| y={y} | |
| textAnchor={textAnchor} | |
| fill="#64748b" | |
| fontSize={10} | |
| fontWeight={600} | |
| className="recharts-polar-angle-axis-tick-value" | |
| > | |
| {words.map((word: string, index: number) => ( | |
| <tspan x={x} dy={index === 0 ? dy : 11} key={index}> | |
| {word} | |
| </tspan> | |
| ))} | |
| </text> | |
| ); | |
| }; | |
| const renderLegend = (props: any) => { | |
| const { payload } = props; | |
| return ( | |
| <div className="flex flex-wrap justify-center gap-3 sm:gap-6 mt-5 sm:mt-6 px-2"> | |
| {payload.map((entry: any, index: number) => { | |
| const fillOpacity = entry.dataKey === 'target' ? 0.1 : 0.4; | |
| // Target: #fbbf24 -> rgba(251, 191, 36) | |
| // User: #06b6d4 -> rgba(6, 182, 212) | |
| const bgColor = entry.dataKey === 'target' | |
| ? `rgba(251, 191, 36, ${fillOpacity})` | |
| : `rgba(6, 182, 212, ${fillOpacity})`; | |
| return ( | |
| <div key={`item-${index}`} className="flex items-center gap-2"> | |
| <div | |
| className="w-3 h-3 rounded-full" | |
| style={{ | |
| backgroundColor: bgColor, | |
| border: `1.5px solid ${entry.color}` | |
| }} | |
| /> | |
| <span className="text-[11px] font-bold text-slate-500 uppercase tracking-wider">{entry.value}</span> | |
| </div> | |
| ); | |
| })} | |
| </div> | |
| ); | |
| }; | |
| return ( | |
| <div className="h-[260px] sm:h-[320px] w-full bg-white rounded-xl pb-4 sm:pb-5"> | |
| <ResponsiveContainer width="100%" height="100%"> | |
| <RadarChart cx="50%" cy="45%" outerRadius="75%" data={data}> | |
| <PolarGrid stroke="#e2e8f0" /> | |
| <PolarAngleAxis | |
| dataKey="subject" | |
| tick={renderCustomAxisTick} | |
| /> | |
| <PolarRadiusAxis domain={[1, 5]} tick={false} axisLine={false} /> | |
| <Radar name="Target" dataKey="target" stroke="#fbbf24" fill="#fbbf24" fillOpacity={0.1} /> | |
| <Radar name="User" dataKey="user" stroke="#06b6d4" fill="#06b6d4" fillOpacity={0.4} /> | |
| <Legend content={renderLegend} verticalAlign="bottom" /> | |
| </RadarChart> | |
| </ResponsiveContainer> | |
| </div> | |
| ); | |
| } | |
| export function MarketPositionGaugeChart({ percentile }: { percentile: number }) { | |
| const data = [ | |
| { name: 'Value', value: percentile, fill: '#134e6f' }, | |
| { name: 'Remaining', value: 100 - percentile, fill: '#f1f5f9' }, | |
| ]; | |
| return ( | |
| <div className="h-[132px] sm:h-[148px] w-full relative flex items-center justify-center overflow-visible"> | |
| <ResponsiveContainer width="100%" height="100%"> | |
| <PieChart> | |
| <Pie | |
| dataKey="value" | |
| startAngle={180} | |
| endAngle={0} | |
| data={data} | |
| cx="50%" | |
| cy="84%" | |
| innerRadius={75} | |
| outerRadius={95} | |
| stroke="none" | |
| paddingAngle={0} | |
| /> | |
| </PieChart> | |
| </ResponsiveContainer> | |
| <div className="absolute top-[46%] left-1/2 -translate-x-1/2 text-center"> | |
| <p className="text-[12px] font-bold text-slate-500">시장 내 위치</p> | |
| <p className="text-xl sm:text-2xl font-bold text-slate-800 tracking-tight">{percentile.toFixed(1)}%</p> | |
| </div> | |
| </div> | |
| ); | |
| } | |