"use client"; /** * KPI 카드 서브타입 컴포넌트 * * 큰 숫자 + 단위 + 증감 표시 * CSS Container Query로 반응형 내부 콘텐츠 */ import React from "react"; import type { DashboardItem } from "../../types"; import { TEXT_ALIGN_CLASSES } from "../../types"; import { abbreviateNumber } from "../utils/formula"; // ===== Props ===== export interface KpiCardProps { item: DashboardItem; data: number | null; /** 이전 기간 대비 증감 퍼센트 (선택) */ trendValue?: number | null; /** 수식 결과 표시 문자열 (formula가 있을 때) */ formulaDisplay?: string | null; } // ===== 증감 표시 ===== function TrendIndicator({ value }: { value: number }) { const isPositive = value > 0; const isZero = value === 0; const color = isPositive ? "text-emerald-600" : isZero ? "text-muted-foreground" : "text-rose-600"; const arrow = isPositive ? "↑" : isZero ? "→" : "↓"; return ( {arrow} {Math.abs(value).toFixed(1)}% ); } // ===== 색상 구간 판정 ===== function getColorForValue( value: number, ranges?: { min: number; max: number; color: string }[] ): string | undefined { if (!ranges?.length) return undefined; const match = ranges.find((r) => value >= r.min && value <= r.max); return match?.color; } // ===== 메인 컴포넌트 ===== export function KpiCardComponent({ item, data, trendValue, formulaDisplay, }: KpiCardProps) { const { visibility, kpiConfig, itemStyle } = item; const displayValue = data ?? 0; const valueColor = getColorForValue(displayValue, kpiConfig?.colorRanges); // 라벨 정렬만 사용자 설정, 나머지는 @container 반응형 자동 const labelAlignClass = TEXT_ALIGN_CLASSES[itemStyle?.labelAlign ?? "center"]; return (
{/* 라벨 - 사용자 정렬 적용 */} {visibility.showLabel && (

{item.label}

)} {/* 메인 값 - @container 반응형 */} {visibility.showValue && (
{formulaDisplay ?? abbreviateNumber(displayValue)} {/* 단위 */} {visibility.showUnit && kpiConfig?.unit && ( {kpiConfig.unit} )}
)} {/* 증감율 */} {visibility.showTrend && trendValue != null && ( )} {/* 보조 라벨 (수식 표시 등) */} {visibility.showSubLabel && formulaDisplay && (

{item.formula?.values.map((v) => v.label).join(" / ")}

)}
); }