257 lines
6.0 KiB
TypeScript
257 lines
6.0 KiB
TypeScript
|
|
import { DriverInfo, DriverManagementConfig } from "../types";
|
||
|
|
|
||
|
|
/**
|
||
|
|
* 운행 상태별 색상 반환
|
||
|
|
*/
|
||
|
|
export function getStatusColor(status: DriverInfo["status"]) {
|
||
|
|
switch (status) {
|
||
|
|
case "driving":
|
||
|
|
return {
|
||
|
|
bg: "bg-green-100",
|
||
|
|
text: "text-green-800",
|
||
|
|
border: "border-green-300",
|
||
|
|
badge: "bg-green-500",
|
||
|
|
};
|
||
|
|
case "standby":
|
||
|
|
return {
|
||
|
|
bg: "bg-gray-100",
|
||
|
|
text: "text-gray-800",
|
||
|
|
border: "border-gray-300",
|
||
|
|
badge: "bg-gray-500",
|
||
|
|
};
|
||
|
|
case "resting":
|
||
|
|
return {
|
||
|
|
bg: "bg-orange-100",
|
||
|
|
text: "text-orange-800",
|
||
|
|
border: "border-orange-300",
|
||
|
|
badge: "bg-orange-500",
|
||
|
|
};
|
||
|
|
case "maintenance":
|
||
|
|
return {
|
||
|
|
bg: "bg-red-100",
|
||
|
|
text: "text-red-800",
|
||
|
|
border: "border-red-300",
|
||
|
|
badge: "bg-red-500",
|
||
|
|
};
|
||
|
|
default:
|
||
|
|
return {
|
||
|
|
bg: "bg-gray-100",
|
||
|
|
text: "text-gray-800",
|
||
|
|
border: "border-gray-300",
|
||
|
|
badge: "bg-gray-500",
|
||
|
|
};
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
/**
|
||
|
|
* 운행 상태 한글 변환
|
||
|
|
*/
|
||
|
|
export function getStatusLabel(status: DriverInfo["status"]) {
|
||
|
|
switch (status) {
|
||
|
|
case "driving":
|
||
|
|
return "운행중";
|
||
|
|
case "standby":
|
||
|
|
return "대기중";
|
||
|
|
case "resting":
|
||
|
|
return "휴식중";
|
||
|
|
case "maintenance":
|
||
|
|
return "점검중";
|
||
|
|
default:
|
||
|
|
return "알 수 없음";
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
/**
|
||
|
|
* 시간 포맷팅 (HH:MM)
|
||
|
|
*/
|
||
|
|
export function formatTime(dateString?: string): string {
|
||
|
|
if (!dateString) return "-";
|
||
|
|
const date = new Date(dateString);
|
||
|
|
return date.toLocaleTimeString("ko-KR", {
|
||
|
|
hour: "2-digit",
|
||
|
|
minute: "2-digit",
|
||
|
|
hour12: false,
|
||
|
|
});
|
||
|
|
}
|
||
|
|
|
||
|
|
/**
|
||
|
|
* 날짜 시간 포맷팅 (MM/DD HH:MM)
|
||
|
|
*/
|
||
|
|
export function formatDateTime(dateString?: string): string {
|
||
|
|
if (!dateString) return "-";
|
||
|
|
const date = new Date(dateString);
|
||
|
|
return date.toLocaleString("ko-KR", {
|
||
|
|
month: "2-digit",
|
||
|
|
day: "2-digit",
|
||
|
|
hour: "2-digit",
|
||
|
|
minute: "2-digit",
|
||
|
|
hour12: false,
|
||
|
|
});
|
||
|
|
}
|
||
|
|
|
||
|
|
/**
|
||
|
|
* 운행 진행률 계산 (실제로는 GPS 데이터 기반)
|
||
|
|
*/
|
||
|
|
export function calculateProgress(driver: DriverInfo): number {
|
||
|
|
if (!driver.departureTime || !driver.estimatedArrival) return 0;
|
||
|
|
|
||
|
|
const now = new Date();
|
||
|
|
const departure = new Date(driver.departureTime);
|
||
|
|
const arrival = new Date(driver.estimatedArrival);
|
||
|
|
|
||
|
|
const totalTime = arrival.getTime() - departure.getTime();
|
||
|
|
const elapsedTime = now.getTime() - departure.getTime();
|
||
|
|
|
||
|
|
const progress = Math.min(100, Math.max(0, (elapsedTime / totalTime) * 100));
|
||
|
|
return Math.round(progress);
|
||
|
|
}
|
||
|
|
|
||
|
|
/**
|
||
|
|
* 기사 필터링
|
||
|
|
*/
|
||
|
|
export function filterDrivers(
|
||
|
|
drivers: DriverInfo[],
|
||
|
|
statusFilter: DriverManagementConfig["statusFilter"],
|
||
|
|
searchTerm: string,
|
||
|
|
): DriverInfo[] {
|
||
|
|
let filtered = drivers;
|
||
|
|
|
||
|
|
// 상태 필터
|
||
|
|
if (statusFilter !== "all") {
|
||
|
|
filtered = filtered.filter((driver) => driver.status === statusFilter);
|
||
|
|
}
|
||
|
|
|
||
|
|
// 검색어 필터
|
||
|
|
if (searchTerm.trim()) {
|
||
|
|
const term = searchTerm.toLowerCase();
|
||
|
|
filtered = filtered.filter(
|
||
|
|
(driver) =>
|
||
|
|
driver.name.toLowerCase().includes(term) ||
|
||
|
|
driver.vehicleNumber.toLowerCase().includes(term) ||
|
||
|
|
driver.phone.includes(term),
|
||
|
|
);
|
||
|
|
}
|
||
|
|
|
||
|
|
return filtered;
|
||
|
|
}
|
||
|
|
|
||
|
|
/**
|
||
|
|
* 기사 정렬
|
||
|
|
*/
|
||
|
|
export function sortDrivers(
|
||
|
|
drivers: DriverInfo[],
|
||
|
|
sortBy: DriverManagementConfig["sortBy"],
|
||
|
|
sortOrder: DriverManagementConfig["sortOrder"],
|
||
|
|
): DriverInfo[] {
|
||
|
|
const sorted = [...drivers];
|
||
|
|
|
||
|
|
sorted.sort((a, b) => {
|
||
|
|
let compareResult = 0;
|
||
|
|
|
||
|
|
switch (sortBy) {
|
||
|
|
case "name":
|
||
|
|
compareResult = a.name.localeCompare(b.name, "ko-KR");
|
||
|
|
break;
|
||
|
|
case "vehicleNumber":
|
||
|
|
compareResult = a.vehicleNumber.localeCompare(b.vehicleNumber);
|
||
|
|
break;
|
||
|
|
case "status":
|
||
|
|
const statusOrder = { driving: 0, resting: 1, standby: 2, maintenance: 3 };
|
||
|
|
compareResult = statusOrder[a.status] - statusOrder[b.status];
|
||
|
|
break;
|
||
|
|
case "departureTime":
|
||
|
|
const timeA = a.departureTime ? new Date(a.departureTime).getTime() : 0;
|
||
|
|
const timeB = b.departureTime ? new Date(b.departureTime).getTime() : 0;
|
||
|
|
compareResult = timeA - timeB;
|
||
|
|
break;
|
||
|
|
}
|
||
|
|
|
||
|
|
return sortOrder === "asc" ? compareResult : -compareResult;
|
||
|
|
});
|
||
|
|
|
||
|
|
return sorted;
|
||
|
|
}
|
||
|
|
|
||
|
|
/**
|
||
|
|
* 테마별 색상 반환
|
||
|
|
*/
|
||
|
|
export function getThemeColors(theme: string, customColor?: string) {
|
||
|
|
if (theme === "custom" && customColor) {
|
||
|
|
const lighterColor = adjustColor(customColor, 40);
|
||
|
|
const darkerColor = adjustColor(customColor, -40);
|
||
|
|
|
||
|
|
return {
|
||
|
|
background: lighterColor,
|
||
|
|
text: darkerColor,
|
||
|
|
border: customColor,
|
||
|
|
hover: customColor,
|
||
|
|
};
|
||
|
|
}
|
||
|
|
|
||
|
|
if (theme === "dark") {
|
||
|
|
return {
|
||
|
|
background: "#1f2937",
|
||
|
|
text: "#f3f4f6",
|
||
|
|
border: "#374151",
|
||
|
|
hover: "#374151",
|
||
|
|
};
|
||
|
|
}
|
||
|
|
|
||
|
|
// light theme (default)
|
||
|
|
return {
|
||
|
|
background: "#ffffff",
|
||
|
|
text: "#1f2937",
|
||
|
|
border: "#e5e7eb",
|
||
|
|
hover: "#f3f4f6",
|
||
|
|
};
|
||
|
|
}
|
||
|
|
|
||
|
|
/**
|
||
|
|
* 색상 밝기 조정
|
||
|
|
*/
|
||
|
|
function adjustColor(color: string, amount: number): string {
|
||
|
|
const clamp = (num: number) => Math.min(255, Math.max(0, num));
|
||
|
|
|
||
|
|
const hex = color.replace("#", "");
|
||
|
|
const r = parseInt(hex.substring(0, 2), 16);
|
||
|
|
const g = parseInt(hex.substring(2, 4), 16);
|
||
|
|
const b = parseInt(hex.substring(4, 6), 16);
|
||
|
|
|
||
|
|
const newR = clamp(r + amount);
|
||
|
|
const newG = clamp(g + amount);
|
||
|
|
const newB = clamp(b + amount);
|
||
|
|
|
||
|
|
return `#${newR.toString(16).padStart(2, "0")}${newG.toString(16).padStart(2, "0")}${newB.toString(16).padStart(2, "0")}`;
|
||
|
|
}
|
||
|
|
|
||
|
|
/**
|
||
|
|
* 기본 표시 컬럼 목록
|
||
|
|
*/
|
||
|
|
export const DEFAULT_VISIBLE_COLUMNS = [
|
||
|
|
"status",
|
||
|
|
"name",
|
||
|
|
"vehicleNumber",
|
||
|
|
"vehicleType",
|
||
|
|
"departure",
|
||
|
|
"destination",
|
||
|
|
"departureTime",
|
||
|
|
"estimatedArrival",
|
||
|
|
"phone",
|
||
|
|
];
|
||
|
|
|
||
|
|
/**
|
||
|
|
* 컬럼 라벨 매핑
|
||
|
|
*/
|
||
|
|
export const COLUMN_LABELS: Record<string, string> = {
|
||
|
|
status: "상태",
|
||
|
|
name: "기사명",
|
||
|
|
vehicleNumber: "차량번호",
|
||
|
|
vehicleType: "차량유형",
|
||
|
|
departure: "출발지",
|
||
|
|
destination: "목적지",
|
||
|
|
departureTime: "출발시간",
|
||
|
|
estimatedArrival: "도착예정",
|
||
|
|
phone: "연락처",
|
||
|
|
progress: "진행률",
|
||
|
|
};
|