feat(WEB): 대시보드 금액 한국식 축약 포맷 적용 (만/억 단위)
- formatKoreanAmount 유틸 함수 추가 (1만 미만: 원, 1만~1억: 만, 1억 이상: 억+만) - CEO 대시보드 일일일보, 당월 지출, 금액카드에 적용 - 기존 formatBillion 로컬 함수 제거 후 공통 유틸로 통합
This commit is contained in:
@@ -12,6 +12,7 @@ import {
|
||||
import { Card, CardContent } from '@/components/ui/card';
|
||||
import { Button } from '@/components/ui/button';
|
||||
import { cn } from '@/lib/utils';
|
||||
import { formatKoreanAmount } from '@/utils/formatAmount';
|
||||
import type { CheckPoint, CheckPointType, AmountCard, HighlightColor } from './types';
|
||||
|
||||
// 섹션별 컬러 테마 타입
|
||||
@@ -245,7 +246,7 @@ export const AmountCardItem = ({
|
||||
if (card.currency === 'USD') {
|
||||
return formatUSD(amount);
|
||||
}
|
||||
return formatBillion(amount);
|
||||
return formatKoreanAmount(amount);
|
||||
};
|
||||
|
||||
// 테마 적용 시 스타일
|
||||
@@ -338,7 +339,7 @@ export const AmountCardItem = ({
|
||||
{card.subItems.map((item, idx) => (
|
||||
<div key={idx} className="flex justify-between gap-2">
|
||||
<span className="shrink-0">{item.label}</span>
|
||||
<span className="text-right">{typeof item.value === 'number' ? formatAmount(item.value, false) : item.value}</span>
|
||||
<span className="text-right">{typeof item.value === 'number' ? formatKoreanAmount(item.value) : item.value}</span>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
@@ -348,7 +349,7 @@ export const AmountCardItem = ({
|
||||
{!showTrend && !card.subItems && (card.subAmount !== undefined || card.previousAmount !== undefined || card.subLabel || card.previousLabel) && (
|
||||
<div className="flex gap-4 mt-auto text-xs text-muted-foreground">
|
||||
{card.subAmount !== undefined && card.subLabel && (
|
||||
<span>{card.subLabel}: {card.unit === '건' ? `${card.subAmount}건` : formatAmount(card.subAmount)}</span>
|
||||
<span>{card.subLabel}: {card.unit === '건' ? `${card.subAmount}건` : formatKoreanAmount(card.subAmount)}</span>
|
||||
)}
|
||||
{card.previousLabel && (
|
||||
<span className="flex items-center gap-1">
|
||||
|
||||
@@ -25,24 +25,13 @@ import {
|
||||
import { useRouter } from 'next/navigation';
|
||||
import { Card, CardContent } from '@/components/ui/card';
|
||||
import { Badge } from '@/components/ui/badge';
|
||||
import { formatKoreanAmount } from '@/utils/formatAmount';
|
||||
import type { DailyReportData, MonthlyExpenseData, TodayIssueItem, TodayIssueSettings } from '../types';
|
||||
|
||||
// ============================================================
|
||||
// 유틸리티 함수
|
||||
// ============================================================
|
||||
|
||||
const formatBillion = (amount: number): string => {
|
||||
const billion = amount / 100000000;
|
||||
if (billion >= 1) {
|
||||
return billion.toFixed(1) + '억원';
|
||||
}
|
||||
const man = amount / 10000;
|
||||
if (man >= 1) {
|
||||
return Math.floor(man).toLocaleString() + '만원';
|
||||
}
|
||||
return amount.toLocaleString() + '원';
|
||||
};
|
||||
|
||||
const formatUSD = (amount: number): string => {
|
||||
return '$ ' + new Intl.NumberFormat('en-US').format(amount);
|
||||
};
|
||||
@@ -106,7 +95,7 @@ export function EnhancedDailyReportSection({ data, onClick }: EnhancedDailyRepor
|
||||
</div>
|
||||
<div className="flex items-end gap-2">
|
||||
<span style={{ color: '#0f172a' }} className="text-2xl font-bold">
|
||||
{formatBillion(data.cards[0]?.amount || 0)}
|
||||
{formatKoreanAmount(data.cards[0]?.amount || 0)}
|
||||
</span>
|
||||
{data.cards[0]?.changeRate && (
|
||||
<span
|
||||
@@ -140,7 +129,7 @@ export function EnhancedDailyReportSection({ data, onClick }: EnhancedDailyRepor
|
||||
<span style={{ color: '#0f172a' }} className="text-2xl font-bold">
|
||||
{data.cards[1]?.currency === 'USD'
|
||||
? formatUSD(data.cards[1]?.amount || 0)
|
||||
: formatBillion(data.cards[1]?.amount || 0)}
|
||||
: formatKoreanAmount(data.cards[1]?.amount || 0)}
|
||||
</span>
|
||||
{data.cards[1]?.changeRate && (
|
||||
<span
|
||||
@@ -172,7 +161,7 @@ export function EnhancedDailyReportSection({ data, onClick }: EnhancedDailyRepor
|
||||
</div>
|
||||
<div className="flex items-end gap-2">
|
||||
<span style={{ color: '#0f172a' }} className="text-2xl font-bold">
|
||||
{formatBillion(data.cards[2]?.amount || 0)}
|
||||
{formatKoreanAmount(data.cards[2]?.amount || 0)}
|
||||
</span>
|
||||
{data.cards[2]?.changeRate && (
|
||||
<span
|
||||
@@ -204,7 +193,7 @@ export function EnhancedDailyReportSection({ data, onClick }: EnhancedDailyRepor
|
||||
</div>
|
||||
<div className="flex items-end gap-2">
|
||||
<span style={{ color: '#0f172a' }} className="text-2xl font-bold">
|
||||
{formatBillion(data.cards[3]?.amount || 0)}
|
||||
{formatKoreanAmount(data.cards[3]?.amount || 0)}
|
||||
</span>
|
||||
{data.cards[3]?.changeRate && (
|
||||
<span
|
||||
@@ -424,7 +413,7 @@ export function EnhancedMonthlyExpenseSection({ data, onCardClick }: EnhancedMon
|
||||
</span>
|
||||
</div>
|
||||
<div style={{ color: '#0f172a' }} className="text-2xl font-bold">
|
||||
{formatBillion(data.cards[0]?.amount || 0)}
|
||||
{formatKoreanAmount(data.cards[0]?.amount || 0)}
|
||||
</div>
|
||||
{data.cards[0]?.previousLabel && (
|
||||
<div style={{ backgroundColor: '#dcfce7', color: '#16a34a' }} className="flex items-center gap-1 px-2 py-1 rounded-full text-xs font-semibold mt-auto w-fit">
|
||||
@@ -449,7 +438,7 @@ export function EnhancedMonthlyExpenseSection({ data, onCardClick }: EnhancedMon
|
||||
</span>
|
||||
</div>
|
||||
<div style={{ color: '#0f172a' }} className="text-2xl font-bold">
|
||||
{formatBillion(data.cards[1]?.amount || 0)}
|
||||
{formatKoreanAmount(data.cards[1]?.amount || 0)}
|
||||
</div>
|
||||
{data.cards[1]?.previousLabel && (
|
||||
<div style={{ backgroundColor: '#dcfce7', color: '#16a34a' }} className="flex items-center gap-1 px-2 py-1 rounded-full text-xs font-semibold mt-auto w-fit">
|
||||
@@ -474,7 +463,7 @@ export function EnhancedMonthlyExpenseSection({ data, onCardClick }: EnhancedMon
|
||||
</span>
|
||||
</div>
|
||||
<div style={{ color: '#0f172a' }} className="text-2xl font-bold">
|
||||
{formatBillion(data.cards[2]?.amount || 0)}
|
||||
{formatKoreanAmount(data.cards[2]?.amount || 0)}
|
||||
</div>
|
||||
{data.cards[2]?.previousLabel && (
|
||||
<div style={{ backgroundColor: '#dcfce7', color: '#16a34a' }} className="flex items-center gap-1 px-2 py-1 rounded-full text-xs font-semibold mt-auto w-fit">
|
||||
@@ -499,7 +488,7 @@ export function EnhancedMonthlyExpenseSection({ data, onCardClick }: EnhancedMon
|
||||
</span>
|
||||
</div>
|
||||
<div style={{ color: '#ffffff' }} className="text-2xl font-bold">
|
||||
{formatBillion(totalAmount)}
|
||||
{formatKoreanAmount(totalAmount)}
|
||||
</div>
|
||||
<div style={{ backgroundColor: 'rgba(255,255,255,0.2)', color: '#ffffff' }} className="flex items-center gap-1 px-2 py-1 rounded-full text-xs font-semibold mt-auto w-fit">
|
||||
<TrendingUp className="h-3 w-3" />
|
||||
|
||||
@@ -40,4 +40,39 @@ export function formatAmountManwon(amount: number): string {
|
||||
}
|
||||
const manwon = Math.round(amount / 10000);
|
||||
return `${manwon.toLocaleString("ko-KR")}만원`;
|
||||
}
|
||||
|
||||
/**
|
||||
* 한국식 금액 축약 포맷
|
||||
*
|
||||
* - 1만 미만: 원 단위 (예: "5,000원")
|
||||
* - 1만 ~ 1억 미만: 만 단위 (예: "5,320만")
|
||||
* - 1억 이상: 억 + 만 단위 (예: "1억 5,000만", "12억 3,453만")
|
||||
* - 만원 나머지가 0이면 "10억"
|
||||
*/
|
||||
export function formatKoreanAmount(amount: number): string {
|
||||
if (amount == null || isNaN(amount)) return "0원";
|
||||
|
||||
const absAmount = Math.abs(amount);
|
||||
const sign = amount < 0 ? "-" : "";
|
||||
|
||||
if (absAmount < 10000) {
|
||||
// 만원 미만: 원 단위
|
||||
return sign + absAmount.toLocaleString("ko-KR") + "원";
|
||||
}
|
||||
|
||||
if (absAmount < 100000000) {
|
||||
// 만원 ~ 억 미만: 만 단위
|
||||
const manwon = Math.round(absAmount / 10000);
|
||||
return sign + manwon.toLocaleString("ko-KR") + "만";
|
||||
}
|
||||
|
||||
// 억 이상: 억 + 만 단위
|
||||
const eok = Math.floor(absAmount / 100000000);
|
||||
const remainder = Math.round((absAmount % 100000000) / 10000);
|
||||
|
||||
if (remainder === 0) {
|
||||
return sign + eok.toLocaleString("ko-KR") + "억";
|
||||
}
|
||||
return sign + eok.toLocaleString("ko-KR") + "억 " + remainder.toLocaleString("ko-KR") + "만";
|
||||
}
|
||||
Reference in New Issue
Block a user