- Revised the full-screen analysis document to reflect the latest updates, including the purpose and core rules for screen development. - Expanded the V2 component usage guide to include a comprehensive catalog of components, their configurations, and usage guidelines for LLM and chatbot applications. - Added a summary of the system architecture and clarified the implementation methods for user business screens and admin menus. - Enhanced the documentation to serve as a reference for AI agents and screen designers, ensuring adherence to the established guidelines. These updates aim to improve clarity and usability for developers and designers working with the WACE ERP screen composition system. Made-with: Cursor
151 lines
4.6 KiB
TypeScript
151 lines
4.6 KiB
TypeScript
/**
|
|
* 중앙 포맷팅 함수.
|
|
* 모든 컴포넌트는 날짜/숫자/통화를 표시할 때 이 함수들만 호출한다.
|
|
*
|
|
* 사용법:
|
|
* import { formatDate, formatNumber, formatCurrency } from "@/lib/formatting";
|
|
* formatDate("2025-01-01") // "2025-01-01"
|
|
* formatDate("2025-01-01T14:30:00Z", "datetime") // "2025-01-01 14:30:00"
|
|
* formatNumber(1234567) // "1,234,567"
|
|
* formatCurrency(50000) // "₩50,000"
|
|
*/
|
|
|
|
export { getFormatRules, setFormatRules, DEFAULT_FORMAT_RULES } from "./rules";
|
|
export type { FormatRules, DateFormatRules, NumberFormatRules, CurrencyFormatRules } from "./rules";
|
|
|
|
import { getFormatRules } from "./rules";
|
|
|
|
// --- 날짜 포맷 ---
|
|
|
|
type DateFormatType = "display" | "datetime" | "input" | "time";
|
|
|
|
/**
|
|
* 날짜 값을 지정된 형식으로 포맷한다.
|
|
* @param value - ISO 문자열, Date, 타임스탬프
|
|
* @param type - "display" | "datetime" | "input" | "time"
|
|
* @returns 포맷된 문자열 (파싱 실패 시 원본 반환)
|
|
*/
|
|
export function formatDate(value: unknown, type: DateFormatType = "display"): string {
|
|
if (value == null || value === "") return "";
|
|
|
|
const rules = getFormatRules();
|
|
const format = rules.date[type];
|
|
|
|
try {
|
|
const date = value instanceof Date ? value : new Date(String(value));
|
|
if (isNaN(date.getTime())) return String(value);
|
|
|
|
return applyDateFormat(date, format);
|
|
} catch {
|
|
return String(value);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* YYYY-MM-DD HH:mm:ss 패턴을 Date 객체에 적용
|
|
*/
|
|
function applyDateFormat(date: Date, pattern: string): string {
|
|
const y = date.getFullYear();
|
|
const M = date.getMonth() + 1;
|
|
const d = date.getDate();
|
|
const H = date.getHours();
|
|
const m = date.getMinutes();
|
|
const s = date.getSeconds();
|
|
|
|
return pattern
|
|
.replace("YYYY", String(y))
|
|
.replace("MM", String(M).padStart(2, "0"))
|
|
.replace("DD", String(d).padStart(2, "0"))
|
|
.replace("HH", String(H).padStart(2, "0"))
|
|
.replace("mm", String(m).padStart(2, "0"))
|
|
.replace("ss", String(s).padStart(2, "0"));
|
|
}
|
|
|
|
// --- 숫자 포맷 ---
|
|
|
|
/** 최대 허용 소수점 자릿수 */
|
|
const MAX_DECIMAL_PLACES = 5;
|
|
|
|
/**
|
|
* 실제 값에서 소수점 자릿수를 감지한다 (최대 MAX_DECIMAL_PLACES).
|
|
*/
|
|
function detectDecimals(num: number): number {
|
|
if (Number.isInteger(num)) return 0;
|
|
const parts = String(num).split(".");
|
|
if (parts.length < 2) return 0;
|
|
return Math.min(parts[1].length, MAX_DECIMAL_PLACES);
|
|
}
|
|
|
|
/**
|
|
* 숫자를 로케일 기반으로 포맷한다 (천단위 구분자 + 소수점 자동감지).
|
|
* @param value - 숫자 또는 숫자 문자열
|
|
* @param decimals - 소수점 자릿수 (미지정 시 실제 값에서 자동감지, 최대 5자리)
|
|
* @returns 포맷된 문자열
|
|
*/
|
|
export function formatNumber(value: unknown, decimals?: number): string {
|
|
if (value == null || value === "") return "";
|
|
|
|
const rules = getFormatRules();
|
|
const num = typeof value === "number" ? value : parseFloat(String(value));
|
|
if (isNaN(num)) return String(value);
|
|
|
|
const dec = decimals ?? detectDecimals(num);
|
|
|
|
return new Intl.NumberFormat(rules.number.locale, {
|
|
minimumFractionDigits: dec,
|
|
maximumFractionDigits: dec,
|
|
}).format(num);
|
|
}
|
|
|
|
// --- 통화 포맷 ---
|
|
|
|
/**
|
|
* 금액을 통화 형식으로 포맷한다.
|
|
* @param value - 숫자 또는 숫자 문자열
|
|
* @param currencyCode - 통화 코드 (미지정 시 기본값 사용)
|
|
* @returns 포맷된 문자열 (예: "₩50,000")
|
|
*/
|
|
export function formatCurrency(value: unknown, currencyCode?: string): string {
|
|
if (value == null || value === "") return "";
|
|
|
|
const rules = getFormatRules();
|
|
const num = typeof value === "number" ? value : parseFloat(String(value));
|
|
if (isNaN(num)) return String(value);
|
|
|
|
const code = currencyCode ?? rules.currency.code;
|
|
|
|
return new Intl.NumberFormat(rules.currency.locale, {
|
|
style: "currency",
|
|
currency: code,
|
|
maximumFractionDigits: code === "KRW" ? 0 : 2,
|
|
}).format(num);
|
|
}
|
|
|
|
// --- 범용 포맷 ---
|
|
|
|
/**
|
|
* 데이터 타입에 따라 자동으로 적절한 포맷을 적용한다.
|
|
* @param value - 포맷할 값
|
|
* @param dataType - "date" | "datetime" | "number" | "currency" | "text"
|
|
*/
|
|
export function formatValue(value: unknown, dataType: string): string {
|
|
switch (dataType) {
|
|
case "date":
|
|
return formatDate(value, "display");
|
|
case "datetime":
|
|
return formatDate(value, "datetime");
|
|
case "time":
|
|
return formatDate(value, "time");
|
|
case "number":
|
|
case "integer":
|
|
case "float":
|
|
case "decimal":
|
|
return formatNumber(value);
|
|
case "currency":
|
|
case "money":
|
|
return formatCurrency(value);
|
|
default:
|
|
return value == null ? "" : String(value);
|
|
}
|
|
}
|