75 lines
2.8 KiB
TypeScript
75 lines
2.8 KiB
TypeScript
/* =============================================================================
|
|
* B01_Dashboard_Logic_Pages.ts
|
|
* 대시보드 세부 페이지 계산(549-1) — 화면 · fetch 없음 · Node 로 바로 시험.
|
|
* 배지 글(역할 · 상태) · 보관함 남은 날 · 가입 요청 수.
|
|
* ========================================================================== */
|
|
|
|
export type BadgeTone = "accent" | "neutral" | "success" | "warning" | "danger";
|
|
|
|
const DAY_MS = 86_400_000;
|
|
|
|
/** 역할 → 한글 */
|
|
export function roleText(role: string | null | undefined): string {
|
|
if (role === "SYSTEM_ADMIN") return "시스템 관리자";
|
|
if (role === "ADMIN") return "회사 관리자";
|
|
if (role === "USER") return "일반 사용자";
|
|
return role ?? "-";
|
|
}
|
|
|
|
/** 상태 → 한글 · 배지 색 (모르는 값은 그대로 · 회색) */
|
|
export function statusBadgeOf(status: string | null | undefined): {
|
|
text: string;
|
|
tone: BadgeTone;
|
|
} {
|
|
switch ((status ?? "").toUpperCase()) {
|
|
case "ACTIVE":
|
|
return { text: "활성", tone: "success" };
|
|
case "INACTIVE":
|
|
return { text: "비활성", tone: "neutral" };
|
|
case "PENDING":
|
|
return { text: "대기", tone: "warning" };
|
|
case "APPROVED":
|
|
return { text: "승인", tone: "success" };
|
|
case "REJECTED":
|
|
return { text: "거절", tone: "danger" };
|
|
default:
|
|
return { text: status || "-", tone: "neutral" };
|
|
}
|
|
}
|
|
|
|
/** 남은 날 — 만료 시각까지 올림 일수 · 지난 것 0 · 만료 없음 null */
|
|
export function daysLeft(
|
|
expiresAt: string | null | undefined,
|
|
now: number = Date.now(),
|
|
): number | null {
|
|
if (!expiresAt) return null;
|
|
const at = new Date(expiresAt).getTime();
|
|
if (Number.isNaN(at)) return null;
|
|
return Math.max(0, Math.ceil((at - now) / DAY_MS));
|
|
}
|
|
|
|
/** 막대 채움 0~100 — 남은 날 ÷ 보관 기간 */
|
|
export function leftPercent(left: number | null, retentionDays: number): number {
|
|
if (left === null || retentionDays <= 0) return 0;
|
|
return Math.max(0, Math.min(100, Math.round((left / retentionDays) * 100)));
|
|
}
|
|
|
|
/** 7 일 안 만료 = 강조 */
|
|
export const EXPIRY_WARN_DAYS = 7;
|
|
export const isExpiringSoon = (left: number | null): boolean =>
|
|
left !== null && left <= EXPIRY_WARN_DAYS;
|
|
|
|
/** 가입 요청 중 처리할 것(대기) 수 — 상태 값이 없으면 대기로 봄 */
|
|
export function pendingCount(requests: { status?: string | null }[]): number {
|
|
return requests.filter((r) => !r.status || r.status.toUpperCase() === "PENDING").length;
|
|
}
|
|
|
|
/** 바이트 → 글 */
|
|
export function bytesText(bytes: number): string {
|
|
const gb = bytes / 1024 / 1024 / 1024;
|
|
if (gb >= 1) return `${gb.toFixed(2)} GB`;
|
|
const mb = bytes / 1024 / 1024;
|
|
if (mb >= 1) return `${mb.toFixed(1)} MB`;
|
|
return `${Math.max(1, Math.round(bytes / 1024))} KB`;
|
|
}
|