Merge branch 'main' of http://39.117.244.52:3000/kjs/ERP-node into common/feat/dashboard-map
This commit is contained in:
@@ -112,6 +112,22 @@ export const screenApi = {
|
||||
});
|
||||
},
|
||||
|
||||
// 활성 화면 일괄 삭제 (휴지통으로 이동)
|
||||
bulkDeleteScreens: async (
|
||||
screenIds: number[],
|
||||
deleteReason?: string,
|
||||
force?: boolean,
|
||||
): Promise<{
|
||||
deletedCount: number;
|
||||
skippedCount: number;
|
||||
errors: Array<{ screenId: number; error: string }>;
|
||||
}> => {
|
||||
const response = await apiClient.delete("/screen-management/screens/bulk/delete", {
|
||||
data: { screenIds, deleteReason, force },
|
||||
});
|
||||
return response.data.result;
|
||||
},
|
||||
|
||||
// 휴지통 화면 목록 조회
|
||||
getDeletedScreens: async (params: {
|
||||
page?: number;
|
||||
|
||||
368
frontend/lib/api/vehicleTrip.ts
Normal file
368
frontend/lib/api/vehicleTrip.ts
Normal file
@@ -0,0 +1,368 @@
|
||||
/**
|
||||
* 차량 운행 이력 API 클라이언트
|
||||
*/
|
||||
import { apiClient } from "./client";
|
||||
|
||||
// 타입 정의
|
||||
export interface TripSummary {
|
||||
id: number;
|
||||
trip_id: string;
|
||||
user_id: string;
|
||||
user_name?: string;
|
||||
vehicle_id?: number;
|
||||
vehicle_number?: string;
|
||||
departure?: string;
|
||||
arrival?: string;
|
||||
departure_name?: string;
|
||||
destination_name?: string;
|
||||
start_time: string;
|
||||
end_time?: string;
|
||||
total_distance: number;
|
||||
duration_minutes?: number;
|
||||
status: "active" | "completed" | "cancelled";
|
||||
location_count: number;
|
||||
company_code: string;
|
||||
created_at: string;
|
||||
}
|
||||
|
||||
export interface TripLocation {
|
||||
id: number;
|
||||
latitude: number;
|
||||
longitude: number;
|
||||
accuracy?: number;
|
||||
speed?: number;
|
||||
distance_from_prev?: number;
|
||||
trip_status: "start" | "tracking" | "end";
|
||||
recorded_at: string;
|
||||
}
|
||||
|
||||
export interface TripDetail {
|
||||
summary: TripSummary;
|
||||
route: TripLocation[];
|
||||
}
|
||||
|
||||
export interface TripListFilters {
|
||||
userId?: string;
|
||||
vehicleId?: number;
|
||||
status?: string;
|
||||
startDate?: string;
|
||||
endDate?: string;
|
||||
departure?: string;
|
||||
arrival?: string;
|
||||
limit?: number;
|
||||
offset?: number;
|
||||
}
|
||||
|
||||
export interface StartTripParams {
|
||||
vehicleId?: number;
|
||||
departure?: string;
|
||||
arrival?: string;
|
||||
departureName?: string;
|
||||
destinationName?: string;
|
||||
latitude: number;
|
||||
longitude: number;
|
||||
}
|
||||
|
||||
export interface EndTripParams {
|
||||
tripId: string;
|
||||
latitude: number;
|
||||
longitude: number;
|
||||
}
|
||||
|
||||
export interface AddLocationParams {
|
||||
tripId: string;
|
||||
latitude: number;
|
||||
longitude: number;
|
||||
accuracy?: number;
|
||||
speed?: number;
|
||||
}
|
||||
|
||||
// API 함수들
|
||||
|
||||
/**
|
||||
* 운행 시작
|
||||
*/
|
||||
export async function startTrip(params: StartTripParams) {
|
||||
const response = await apiClient.post("/vehicle/trip/start", params);
|
||||
return response.data;
|
||||
}
|
||||
|
||||
/**
|
||||
* 운행 종료
|
||||
*/
|
||||
export async function endTrip(params: EndTripParams) {
|
||||
const response = await apiClient.post("/vehicle/trip/end", params);
|
||||
return response.data;
|
||||
}
|
||||
|
||||
/**
|
||||
* 위치 기록 추가 (연속 추적)
|
||||
*/
|
||||
export async function addTripLocation(params: AddLocationParams) {
|
||||
const response = await apiClient.post("/vehicle/trip/location", params);
|
||||
return response.data;
|
||||
}
|
||||
|
||||
/**
|
||||
* 활성 운행 조회
|
||||
*/
|
||||
export async function getActiveTrip() {
|
||||
const response = await apiClient.get("/vehicle/trip/active");
|
||||
return response.data;
|
||||
}
|
||||
|
||||
/**
|
||||
* 운행 취소
|
||||
*/
|
||||
export async function cancelTrip(tripId: string) {
|
||||
const response = await apiClient.post("/vehicle/trip/cancel", { tripId });
|
||||
return response.data;
|
||||
}
|
||||
|
||||
/**
|
||||
* 운행 이력 목록 조회
|
||||
*/
|
||||
export async function getTripList(filters?: TripListFilters) {
|
||||
const params = new URLSearchParams();
|
||||
|
||||
if (filters) {
|
||||
if (filters.userId) params.append("userId", filters.userId);
|
||||
if (filters.vehicleId) params.append("vehicleId", String(filters.vehicleId));
|
||||
if (filters.status) params.append("status", filters.status);
|
||||
if (filters.startDate) params.append("startDate", filters.startDate);
|
||||
if (filters.endDate) params.append("endDate", filters.endDate);
|
||||
if (filters.departure) params.append("departure", filters.departure);
|
||||
if (filters.arrival) params.append("arrival", filters.arrival);
|
||||
if (filters.limit) params.append("limit", String(filters.limit));
|
||||
if (filters.offset) params.append("offset", String(filters.offset));
|
||||
}
|
||||
|
||||
const queryString = params.toString();
|
||||
const url = queryString ? `/vehicle/trips?${queryString}` : "/vehicle/trips";
|
||||
|
||||
const response = await apiClient.get(url);
|
||||
return response.data;
|
||||
}
|
||||
|
||||
/**
|
||||
* 운행 상세 조회 (경로 포함)
|
||||
*/
|
||||
export async function getTripDetail(tripId: string): Promise<{ success: boolean; data?: TripDetail; message?: string }> {
|
||||
const response = await apiClient.get(`/vehicle/trips/${tripId}`);
|
||||
return response.data;
|
||||
}
|
||||
|
||||
/**
|
||||
* 거리 포맷팅 (km)
|
||||
*/
|
||||
export function formatDistance(distanceKm: number): string {
|
||||
if (distanceKm < 1) {
|
||||
return `${Math.round(distanceKm * 1000)}m`;
|
||||
}
|
||||
return `${distanceKm.toFixed(2)}km`;
|
||||
}
|
||||
|
||||
/**
|
||||
* 운행 시간 포맷팅
|
||||
*/
|
||||
export function formatDuration(minutes: number): string {
|
||||
if (minutes < 60) {
|
||||
return `${minutes}분`;
|
||||
}
|
||||
const hours = Math.floor(minutes / 60);
|
||||
const mins = minutes % 60;
|
||||
return mins > 0 ? `${hours}시간 ${mins}분` : `${hours}시간`;
|
||||
}
|
||||
|
||||
/**
|
||||
* 상태 한글 변환
|
||||
*/
|
||||
export function getStatusLabel(status: string): string {
|
||||
switch (status) {
|
||||
case "active":
|
||||
return "운행 중";
|
||||
case "completed":
|
||||
return "완료";
|
||||
case "cancelled":
|
||||
return "취소됨";
|
||||
default:
|
||||
return status;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 상태별 색상
|
||||
*/
|
||||
export function getStatusColor(status: string): string {
|
||||
switch (status) {
|
||||
case "active":
|
||||
return "bg-green-100 text-green-800";
|
||||
case "completed":
|
||||
return "bg-blue-100 text-blue-800";
|
||||
case "cancelled":
|
||||
return "bg-gray-100 text-gray-800";
|
||||
default:
|
||||
return "bg-gray-100 text-gray-800";
|
||||
}
|
||||
}
|
||||
|
||||
// ============== 리포트 API ==============
|
||||
|
||||
export interface DailyStat {
|
||||
date: string;
|
||||
tripCount: number;
|
||||
completedCount: number;
|
||||
cancelledCount: number;
|
||||
totalDistance: number;
|
||||
totalDuration: number;
|
||||
avgDistance: number;
|
||||
avgDuration: number;
|
||||
}
|
||||
|
||||
export interface WeeklyStat {
|
||||
weekNumber: number;
|
||||
weekStart: string;
|
||||
weekEnd: string;
|
||||
tripCount: number;
|
||||
completedCount: number;
|
||||
totalDistance: number;
|
||||
totalDuration: number;
|
||||
avgDistance: number;
|
||||
}
|
||||
|
||||
export interface MonthlyStat {
|
||||
month: number;
|
||||
tripCount: number;
|
||||
completedCount: number;
|
||||
cancelledCount: number;
|
||||
totalDistance: number;
|
||||
totalDuration: number;
|
||||
avgDistance: number;
|
||||
avgDuration: number;
|
||||
driverCount: number;
|
||||
}
|
||||
|
||||
export interface SummaryReport {
|
||||
period: string;
|
||||
totalTrips: number;
|
||||
completedTrips: number;
|
||||
activeTrips: number;
|
||||
cancelledTrips: number;
|
||||
completionRate: number;
|
||||
totalDistance: number;
|
||||
totalDuration: number;
|
||||
avgDistance: number;
|
||||
avgDuration: number;
|
||||
activeDrivers: number;
|
||||
}
|
||||
|
||||
export interface DriverStat {
|
||||
userId: string;
|
||||
userName: string;
|
||||
tripCount: number;
|
||||
completedCount: number;
|
||||
totalDistance: number;
|
||||
totalDuration: number;
|
||||
avgDistance: number;
|
||||
}
|
||||
|
||||
export interface RouteStat {
|
||||
departure: string;
|
||||
arrival: string;
|
||||
departureName: string;
|
||||
destinationName: string;
|
||||
tripCount: number;
|
||||
completedCount: number;
|
||||
totalDistance: number;
|
||||
avgDistance: number;
|
||||
avgDuration: number;
|
||||
}
|
||||
|
||||
/**
|
||||
* 요약 통계 조회 (대시보드용)
|
||||
*/
|
||||
export async function getSummaryReport(period?: string) {
|
||||
const url = period ? `/vehicle/reports/summary?period=${period}` : "/vehicle/reports/summary";
|
||||
const response = await apiClient.get(url);
|
||||
return response.data;
|
||||
}
|
||||
|
||||
/**
|
||||
* 일별 통계 조회
|
||||
*/
|
||||
export async function getDailyReport(filters?: { startDate?: string; endDate?: string; userId?: string }) {
|
||||
const params = new URLSearchParams();
|
||||
if (filters?.startDate) params.append("startDate", filters.startDate);
|
||||
if (filters?.endDate) params.append("endDate", filters.endDate);
|
||||
if (filters?.userId) params.append("userId", filters.userId);
|
||||
|
||||
const queryString = params.toString();
|
||||
const url = queryString ? `/vehicle/reports/daily?${queryString}` : "/vehicle/reports/daily";
|
||||
|
||||
const response = await apiClient.get(url);
|
||||
return response.data;
|
||||
}
|
||||
|
||||
/**
|
||||
* 주별 통계 조회
|
||||
*/
|
||||
export async function getWeeklyReport(filters?: { year?: number; month?: number; userId?: string }) {
|
||||
const params = new URLSearchParams();
|
||||
if (filters?.year) params.append("year", String(filters.year));
|
||||
if (filters?.month) params.append("month", String(filters.month));
|
||||
if (filters?.userId) params.append("userId", filters.userId);
|
||||
|
||||
const queryString = params.toString();
|
||||
const url = queryString ? `/vehicle/reports/weekly?${queryString}` : "/vehicle/reports/weekly";
|
||||
|
||||
const response = await apiClient.get(url);
|
||||
return response.data;
|
||||
}
|
||||
|
||||
/**
|
||||
* 월별 통계 조회
|
||||
*/
|
||||
export async function getMonthlyReport(filters?: { year?: number; userId?: string }) {
|
||||
const params = new URLSearchParams();
|
||||
if (filters?.year) params.append("year", String(filters.year));
|
||||
if (filters?.userId) params.append("userId", filters.userId);
|
||||
|
||||
const queryString = params.toString();
|
||||
const url = queryString ? `/vehicle/reports/monthly?${queryString}` : "/vehicle/reports/monthly";
|
||||
|
||||
const response = await apiClient.get(url);
|
||||
return response.data;
|
||||
}
|
||||
|
||||
/**
|
||||
* 운전자별 통계 조회
|
||||
*/
|
||||
export async function getDriverReport(filters?: { startDate?: string; endDate?: string; limit?: number }) {
|
||||
const params = new URLSearchParams();
|
||||
if (filters?.startDate) params.append("startDate", filters.startDate);
|
||||
if (filters?.endDate) params.append("endDate", filters.endDate);
|
||||
if (filters?.limit) params.append("limit", String(filters.limit));
|
||||
|
||||
const queryString = params.toString();
|
||||
const url = queryString ? `/vehicle/reports/by-driver?${queryString}` : "/vehicle/reports/by-driver";
|
||||
|
||||
const response = await apiClient.get(url);
|
||||
return response.data;
|
||||
}
|
||||
|
||||
/**
|
||||
* 구간별 통계 조회
|
||||
*/
|
||||
export async function getRouteReport(filters?: { startDate?: string; endDate?: string; limit?: number }) {
|
||||
const params = new URLSearchParams();
|
||||
if (filters?.startDate) params.append("startDate", filters.startDate);
|
||||
if (filters?.endDate) params.append("endDate", filters.endDate);
|
||||
if (filters?.limit) params.append("limit", String(filters.limit));
|
||||
|
||||
const queryString = params.toString();
|
||||
const url = queryString ? `/vehicle/reports/by-route?${queryString}` : "/vehicle/reports/by-route";
|
||||
|
||||
const response = await apiClient.get(url);
|
||||
return response.data;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user