diff --git a/frontend/components/admin/dashboard/types.ts b/frontend/components/admin/dashboard/types.ts
index 2e753d1b..6d01fc01 100644
--- a/frontend/components/admin/dashboard/types.ts
+++ b/frontend/components/admin/dashboard/types.ts
@@ -16,7 +16,8 @@ export type ElementSubtype =
| "weather"
| "clock"
| "calendar"
- | "calculator"; // 위젯 타입
+ | "calculator"
+ | "driver-management"; // 위젯 타입
export interface Position {
x: number;
@@ -40,6 +41,7 @@ export interface DashboardElement {
chartConfig?: ChartConfig; // 차트 설정
clockConfig?: ClockConfig; // 시계 설정
calendarConfig?: CalendarConfig; // 달력 설정
+ driverManagementConfig?: DriverManagementConfig; // 기사 관리 설정
}
export interface DragData {
@@ -101,3 +103,30 @@ export interface CalendarConfig {
customColor?: string; // 사용자 지정 색상
showWeekNumbers?: boolean; // 주차 표시 (선택)
}
+
+// 기사 관리 위젯 설정
+export interface DriverManagementConfig {
+ viewType: "list"; // 뷰 타입 (현재는 리스트만)
+ autoRefreshInterval: number; // 자동 새로고침 간격 (초)
+ visibleColumns: string[]; // 표시할 컬럼 목록
+ theme: "light" | "dark" | "custom"; // 테마
+ customColor?: string; // 사용자 지정 색상
+ statusFilter: "all" | "driving" | "standby" | "resting" | "maintenance"; // 상태 필터
+ sortBy: "name" | "vehicleNumber" | "status" | "departureTime"; // 정렬 기준
+ sortOrder: "asc" | "desc"; // 정렬 순서
+}
+
+// 기사 정보
+export interface DriverInfo {
+ id: string; // 기사 고유 ID
+ name: string; // 기사 이름
+ vehicleNumber: string; // 차량 번호
+ vehicleType: string; // 차량 유형
+ phone: string; // 연락처
+ status: "standby" | "driving" | "resting" | "maintenance"; // 운행 상태
+ departure?: string; // 출발지
+ destination?: string; // 목적지
+ departureTime?: string; // 출발 시간
+ estimatedArrival?: string; // 예상 도착 시간
+ progress?: number; // 운행 진행률 (0-100)
+}
diff --git a/frontend/components/admin/dashboard/widgets/DriverListView.tsx b/frontend/components/admin/dashboard/widgets/DriverListView.tsx
new file mode 100644
index 00000000..f5df6944
--- /dev/null
+++ b/frontend/components/admin/dashboard/widgets/DriverListView.tsx
@@ -0,0 +1,161 @@
+"use client";
+
+import React from "react";
+import { DriverInfo, DriverManagementConfig } from "../types";
+import { getStatusColor, getStatusLabel, formatTime, COLUMN_LABELS } from "./driverUtils";
+import { Progress } from "@/components/ui/progress";
+
+interface DriverListViewProps {
+ drivers: DriverInfo[];
+ config: DriverManagementConfig;
+ isCompact?: boolean; // 작은 크기 (2x2 등)
+}
+
+export function DriverListView({ drivers, config, isCompact = false }: DriverListViewProps) {
+ const { visibleColumns } = config;
+
+ // 컴팩트 모드: 요약 정보만 표시
+ if (isCompact) {
+ const stats = {
+ driving: drivers.filter((d) => d.status === "driving").length,
+ standby: drivers.filter((d) => d.status === "standby").length,
+ resting: drivers.filter((d) => d.status === "resting").length,
+ maintenance: drivers.filter((d) => d.status === "maintenance").length,
+ };
+
+ return (
+
+
+
{drivers.length}
+
전체 기사
+
+
+
+
{stats.driving}
+
운행중
+
+
+
{stats.standby}
+
대기중
+
+
+
{stats.resting}
+
휴식중
+
+
+
{stats.maintenance}
+
점검중
+
+
+
+ );
+ }
+
+ // 빈 데이터 처리
+ if (drivers.length === 0) {
+ return (
+
조회된 기사 정보가 없습니다
+ );
+ }
+
+ return (
+
+
+
+
+ {visibleColumns.includes("status") && (
+ | {COLUMN_LABELS.status} |
+ )}
+ {visibleColumns.includes("name") && (
+ {COLUMN_LABELS.name} |
+ )}
+ {visibleColumns.includes("vehicleNumber") && (
+ {COLUMN_LABELS.vehicleNumber} |
+ )}
+ {visibleColumns.includes("vehicleType") && (
+ {COLUMN_LABELS.vehicleType} |
+ )}
+ {visibleColumns.includes("departure") && (
+ {COLUMN_LABELS.departure} |
+ )}
+ {visibleColumns.includes("destination") && (
+ {COLUMN_LABELS.destination} |
+ )}
+ {visibleColumns.includes("departureTime") && (
+ {COLUMN_LABELS.departureTime} |
+ )}
+ {visibleColumns.includes("estimatedArrival") && (
+
+ {COLUMN_LABELS.estimatedArrival}
+ |
+ )}
+ {visibleColumns.includes("phone") && (
+ {COLUMN_LABELS.phone} |
+ )}
+ {visibleColumns.includes("progress") && (
+ {COLUMN_LABELS.progress} |
+ )}
+
+
+
+ {drivers.map((driver) => {
+ const statusColors = getStatusColor(driver.status);
+ return (
+
+ {visibleColumns.includes("status") && (
+ |
+
+ {getStatusLabel(driver.status)}
+
+ |
+ )}
+ {visibleColumns.includes("name") && (
+ {driver.name} |
+ )}
+ {visibleColumns.includes("vehicleNumber") && (
+ {driver.vehicleNumber} |
+ )}
+ {visibleColumns.includes("vehicleType") && (
+ {driver.vehicleType} |
+ )}
+ {visibleColumns.includes("departure") && (
+
+ {driver.departure || -}
+ |
+ )}
+ {visibleColumns.includes("destination") && (
+
+ {driver.destination || -}
+ |
+ )}
+ {visibleColumns.includes("departureTime") && (
+ {formatTime(driver.departureTime)} |
+ )}
+ {visibleColumns.includes("estimatedArrival") && (
+ {formatTime(driver.estimatedArrival)} |
+ )}
+ {visibleColumns.includes("phone") && (
+ {driver.phone} |
+ )}
+ {visibleColumns.includes("progress") && (
+
+ {driver.progress !== undefined ? (
+
+ ) : (
+ -
+ )}
+ |
+ )}
+
+ );
+ })}
+
+
+
+ );
+}
diff --git a/frontend/components/admin/dashboard/widgets/DriverManagementSettings.tsx b/frontend/components/admin/dashboard/widgets/DriverManagementSettings.tsx
new file mode 100644
index 00000000..a77dfda5
--- /dev/null
+++ b/frontend/components/admin/dashboard/widgets/DriverManagementSettings.tsx
@@ -0,0 +1,195 @@
+"use client";
+
+import { useState } from "react";
+import { DriverManagementConfig } from "../types";
+import { Button } from "@/components/ui/button";
+import { Label } from "@/components/ui/label";
+import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from "@/components/ui/select";
+import { Switch } from "@/components/ui/switch";
+import { Input } from "@/components/ui/input";
+import { Card } from "@/components/ui/card";
+import { COLUMN_LABELS, DEFAULT_VISIBLE_COLUMNS } from "./driverUtils";
+
+interface DriverManagementSettingsProps {
+ config: DriverManagementConfig;
+ onSave: (config: DriverManagementConfig) => void;
+ onClose: () => void;
+}
+
+export function DriverManagementSettings({ config, onSave, onClose }: DriverManagementSettingsProps) {
+ const [localConfig, setLocalConfig] = useState
(config);
+
+ const handleSave = () => {
+ onSave(localConfig);
+ };
+
+ // 컬럼 토글
+ const toggleColumn = (column: string) => {
+ const newColumns = localConfig.visibleColumns.includes(column)
+ ? localConfig.visibleColumns.filter((c) => c !== column)
+ : [...localConfig.visibleColumns, column];
+ setLocalConfig({ ...localConfig, visibleColumns: newColumns });
+ };
+
+ return (
+
+
+ {/* 자동 새로고침 */}
+
+
+
+
+
+ {/* 정렬 설정 */}
+
+
+
+
+
+
+
+
+
+ {/* 테마 설정 */}
+
+
+
+
+
+
+
+
+ {/* 사용자 지정 색상 */}
+ {localConfig.theme === "custom" && (
+
+
+
+
setLocalConfig({ ...localConfig, customColor: e.target.value })}
+ className="h-12 w-20 cursor-pointer"
+ />
+
+
setLocalConfig({ ...localConfig, customColor: e.target.value })}
+ placeholder="#3b82f6"
+ className="font-mono"
+ />
+
테이블 배경색으로 사용됩니다
+
+
+
+ )}
+
+
+ {/* 표시 컬럼 선택 */}
+
+
+
+
+
+
+ {Object.entries(COLUMN_LABELS).map(([key, label]) => (
+
toggleColumn(key)}
+ >
+
+
+ toggleColumn(key)}
+ />
+
+
+ ))}
+
+
+
+
+ {/* 푸터 - 고정 */}
+
+
+
+
+
+ );
+}
diff --git a/frontend/components/admin/dashboard/widgets/DriverManagementWidget.tsx b/frontend/components/admin/dashboard/widgets/DriverManagementWidget.tsx
new file mode 100644
index 00000000..60d5c615
--- /dev/null
+++ b/frontend/components/admin/dashboard/widgets/DriverManagementWidget.tsx
@@ -0,0 +1,159 @@
+"use client";
+
+import React, { useState, useEffect } from "react";
+import { DashboardElement, DriverManagementConfig, DriverInfo } from "../types";
+import { DriverListView } from "./DriverListView";
+import { DriverManagementSettings } from "./DriverManagementSettings";
+import { MOCK_DRIVERS } from "./driverMockData";
+import { filterDrivers, sortDrivers, DEFAULT_VISIBLE_COLUMNS } from "./driverUtils";
+import { Popover, PopoverContent, PopoverTrigger } from "@/components/ui/popover";
+import { Button } from "@/components/ui/button";
+import { Input } from "@/components/ui/input";
+import { Settings, Search, RefreshCw } from "lucide-react";
+import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from "@/components/ui/select";
+
+interface DriverManagementWidgetProps {
+ element: DashboardElement;
+ onConfigUpdate?: (config: DriverManagementConfig) => void;
+}
+
+export function DriverManagementWidget({ element, onConfigUpdate }: DriverManagementWidgetProps) {
+ const [drivers, setDrivers] = useState(MOCK_DRIVERS);
+ const [searchTerm, setSearchTerm] = useState("");
+ const [settingsOpen, setSettingsOpen] = useState(false);
+ const [lastRefresh, setLastRefresh] = useState(new Date());
+
+ // 기본 설정
+ const config = element.driverManagementConfig || {
+ viewType: "list",
+ autoRefreshInterval: 30,
+ visibleColumns: DEFAULT_VISIBLE_COLUMNS,
+ theme: "light",
+ statusFilter: "all",
+ sortBy: "name",
+ sortOrder: "asc",
+ };
+
+ // 자동 새로고침
+ useEffect(() => {
+ if (config.autoRefreshInterval <= 0) return;
+
+ const interval = setInterval(() => {
+ // 실제 환경에서는 API 호출
+ setDrivers(MOCK_DRIVERS);
+ setLastRefresh(new Date());
+ }, config.autoRefreshInterval * 1000);
+
+ return () => clearInterval(interval);
+ }, [config.autoRefreshInterval]);
+
+ // 수동 새로고침
+ const handleRefresh = () => {
+ setDrivers(MOCK_DRIVERS);
+ setLastRefresh(new Date());
+ };
+
+ // 설정 저장
+ const handleSaveSettings = (newConfig: DriverManagementConfig) => {
+ onConfigUpdate?.(newConfig);
+ setSettingsOpen(false);
+ };
+
+ // 필터링 및 정렬
+ const filteredDrivers = sortDrivers(
+ filterDrivers(drivers, config.statusFilter, searchTerm),
+ config.sortBy,
+ config.sortOrder,
+ );
+
+ // 컴팩트 모드 판단 (위젯 크기가 작을 때)
+ const isCompact = element.size.width < 400 || element.size.height < 300;
+
+ return (
+
+ {/* 헤더 - 컴팩트 모드가 아닐 때만 표시 */}
+ {!isCompact && (
+
+
+ {/* 검색 */}
+
+
+ setSearchTerm(e.target.value)}
+ className="h-8 pl-8 text-xs"
+ />
+
+
+ {/* 상태 필터 */}
+
+
+ {/* 새로고침 버튼 */}
+
+
+ {/* 설정 버튼 */}
+
+
+
+
+
+ setSettingsOpen(false)}
+ />
+
+
+
+
+ {/* 통계 정보 */}
+
+
+ 전체 {filteredDrivers.length}명
+
+ |
+
+ 운행중{" "}
+
+ {filteredDrivers.filter((d) => d.status === "driving").length}
+
+ 명
+
+ |
+ 최근 업데이트: {lastRefresh.toLocaleTimeString("ko-KR")}
+
+
+ )}
+
+ {/* 리스트 뷰 */}
+
+
+
+
+ );
+}
diff --git a/frontend/components/admin/dashboard/widgets/driverMockData.ts b/frontend/components/admin/dashboard/widgets/driverMockData.ts
new file mode 100644
index 00000000..85271e16
--- /dev/null
+++ b/frontend/components/admin/dashboard/widgets/driverMockData.ts
@@ -0,0 +1,181 @@
+import { DriverInfo } from "../types";
+
+/**
+ * 기사 관리 목업 데이터
+ * 실제 환경에서는 REST API로 대체됨
+ */
+export const MOCK_DRIVERS: DriverInfo[] = [
+ {
+ id: "DRV001",
+ name: "홍길동",
+ vehicleNumber: "12가 3456",
+ vehicleType: "1톤 트럭",
+ phone: "010-1234-5678",
+ status: "driving",
+ departure: "서울시 강남구",
+ destination: "경기도 성남시",
+ departureTime: "2025-10-14T09:00:00",
+ estimatedArrival: "2025-10-14T11:30:00",
+ progress: 65,
+ },
+ {
+ id: "DRV002",
+ name: "김철수",
+ vehicleNumber: "34나 7890",
+ vehicleType: "2.5톤 트럭",
+ phone: "010-2345-6789",
+ status: "standby",
+ },
+ {
+ id: "DRV003",
+ name: "이영희",
+ vehicleNumber: "56다 1234",
+ vehicleType: "5톤 트럭",
+ phone: "010-3456-7890",
+ status: "driving",
+ departure: "인천광역시",
+ destination: "충청남도 천안시",
+ departureTime: "2025-10-14T08:30:00",
+ estimatedArrival: "2025-10-14T10:00:00",
+ progress: 85,
+ },
+ {
+ id: "DRV004",
+ name: "박민수",
+ vehicleNumber: "78라 5678",
+ vehicleType: "카고",
+ phone: "010-4567-8901",
+ status: "resting",
+ },
+ {
+ id: "DRV005",
+ name: "정수진",
+ vehicleNumber: "90마 9012",
+ vehicleType: "냉동차",
+ phone: "010-5678-9012",
+ status: "maintenance",
+ },
+ {
+ id: "DRV006",
+ name: "최동욱",
+ vehicleNumber: "11아 3344",
+ vehicleType: "1톤 트럭",
+ phone: "010-6789-0123",
+ status: "driving",
+ departure: "부산광역시",
+ destination: "울산광역시",
+ departureTime: "2025-10-14T07:45:00",
+ estimatedArrival: "2025-10-14T09:15:00",
+ progress: 92,
+ },
+ {
+ id: "DRV007",
+ name: "강미선",
+ vehicleNumber: "22자 5566",
+ vehicleType: "탑차",
+ phone: "010-7890-1234",
+ status: "standby",
+ },
+ {
+ id: "DRV008",
+ name: "윤성호",
+ vehicleNumber: "33차 7788",
+ vehicleType: "2.5톤 트럭",
+ phone: "010-8901-2345",
+ status: "driving",
+ departure: "대전광역시",
+ destination: "세종특별자치시",
+ departureTime: "2025-10-14T10:20:00",
+ estimatedArrival: "2025-10-14T11:00:00",
+ progress: 45,
+ },
+ {
+ id: "DRV009",
+ name: "장혜진",
+ vehicleNumber: "44카 9900",
+ vehicleType: "냉동차",
+ phone: "010-9012-3456",
+ status: "resting",
+ },
+ {
+ id: "DRV010",
+ name: "임태양",
+ vehicleNumber: "55타 1122",
+ vehicleType: "5톤 트럭",
+ phone: "010-0123-4567",
+ status: "driving",
+ departure: "광주광역시",
+ destination: "전라남도 목포시",
+ departureTime: "2025-10-14T06:30:00",
+ estimatedArrival: "2025-10-14T08:45:00",
+ progress: 78,
+ },
+ {
+ id: "DRV011",
+ name: "오준석",
+ vehicleNumber: "66파 3344",
+ vehicleType: "카고",
+ phone: "010-1111-2222",
+ status: "standby",
+ },
+ {
+ id: "DRV012",
+ name: "한소희",
+ vehicleNumber: "77하 5566",
+ vehicleType: "1톤 트럭",
+ phone: "010-2222-3333",
+ status: "maintenance",
+ },
+ {
+ id: "DRV013",
+ name: "송민재",
+ vehicleNumber: "88거 7788",
+ vehicleType: "탑차",
+ phone: "010-3333-4444",
+ status: "driving",
+ departure: "경기도 수원시",
+ destination: "경기도 평택시",
+ departureTime: "2025-10-14T09:50:00",
+ estimatedArrival: "2025-10-14T11:20:00",
+ progress: 38,
+ },
+ {
+ id: "DRV014",
+ name: "배수지",
+ vehicleNumber: "99너 9900",
+ vehicleType: "2.5톤 트럭",
+ phone: "010-4444-5555",
+ status: "driving",
+ departure: "강원도 춘천시",
+ destination: "강원도 원주시",
+ departureTime: "2025-10-14T08:00:00",
+ estimatedArrival: "2025-10-14T09:30:00",
+ progress: 72,
+ },
+ {
+ id: "DRV015",
+ name: "신동엽",
+ vehicleNumber: "00더 1122",
+ vehicleType: "5톤 트럭",
+ phone: "010-5555-6666",
+ status: "standby",
+ },
+];
+
+/**
+ * 차량 유형 목록
+ */
+export const VEHICLE_TYPES = ["1톤 트럭", "2.5톤 트럭", "5톤 트럭", "카고", "탑차", "냉동차"];
+
+/**
+ * 운행 상태별 통계 계산
+ */
+export function getDriverStatistics(drivers: DriverInfo[]) {
+ return {
+ total: drivers.length,
+ driving: drivers.filter((d) => d.status === "driving").length,
+ standby: drivers.filter((d) => d.status === "standby").length,
+ resting: drivers.filter((d) => d.status === "resting").length,
+ maintenance: drivers.filter((d) => d.status === "maintenance").length,
+ };
+}
diff --git a/frontend/components/admin/dashboard/widgets/driverUtils.ts b/frontend/components/admin/dashboard/widgets/driverUtils.ts
new file mode 100644
index 00000000..bd2ddbd3
--- /dev/null
+++ b/frontend/components/admin/dashboard/widgets/driverUtils.ts
@@ -0,0 +1,256 @@
+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 = {
+ status: "상태",
+ name: "기사명",
+ vehicleNumber: "차량번호",
+ vehicleType: "차량유형",
+ departure: "출발지",
+ destination: "목적지",
+ departureTime: "출발시간",
+ estimatedArrival: "도착예정",
+ phone: "연락처",
+ progress: "진행률",
+};