diff --git a/src/components/vehicle/types.ts b/src/components/vehicle/types.ts new file mode 100644 index 00000000..c392da1a --- /dev/null +++ b/src/components/vehicle/types.ts @@ -0,0 +1,442 @@ +/** + * 차량관리 공통 타입 정의 + * DB 마이그레이션 스키마 기반 (corporate_vehicles, vehicle_logs, vehicle_maintenances) + */ + +// ===== 차량 목록 (Corporate Vehicles) ===== + +export type OwnershipType = 'corporate' | 'rent' | 'lease'; +export type VehicleStatus = 'active' | 'maintenance' | 'disposed'; +export type VehicleType = '승용차' | '승합차' | '화물차' | 'SUV'; + +export interface CorporateVehicle { + id: number; + plateNumber: string; + model: string; + vehicleType: VehicleType; + ownershipType: OwnershipType; + year: number | null; + driver: string | null; + status: VehicleStatus; + mileage: number; + memo: string | null; + // 법인 전용 + purchaseDate: string | null; + purchasePrice: number; + // 렌트/리스 전용 + contractDate: string | null; + rentCompany: string | null; + rentCompanyTel: string | null; + rentPeriod: string | null; + agreedMileage: string | null; + vehiclePrice: number; + residualValue: number; + deposit: number; + monthlyRent: number; + monthlyRentTax: number; + insuranceCompany: string | null; + insuranceCompanyTel: string | null; + createdAt: string; + updatedAt: string; +} + +// API 응답 (snake_case) +export interface CorporateVehicleApi { + id: number; + plate_number: string; + model: string; + vehicle_type: string; + ownership_type: string; + year: number | null; + driver: string | null; + status: string; + mileage: number; + memo: string | null; + purchase_date: string | null; + purchase_price: number; + contract_date: string | null; + rent_company: string | null; + rent_company_tel: string | null; + rent_period: string | null; + agreed_mileage: string | null; + vehicle_price: number; + residual_value: number; + deposit: number; + monthly_rent: number; + monthly_rent_tax: number; + insurance_company: string | null; + insurance_company_tel: string | null; + created_at: string; + updated_at: string; +} + +export function transformVehicleApi(api: CorporateVehicleApi): CorporateVehicle { + return { + id: api.id, + plateNumber: api.plate_number, + model: api.model, + vehicleType: api.vehicle_type as VehicleType, + ownershipType: api.ownership_type as OwnershipType, + year: api.year, + driver: api.driver, + status: api.status as VehicleStatus, + mileage: api.mileage, + memo: api.memo, + purchaseDate: api.purchase_date, + purchasePrice: api.purchase_price, + contractDate: api.contract_date, + rentCompany: api.rent_company, + rentCompanyTel: api.rent_company_tel, + rentPeriod: api.rent_period, + agreedMileage: api.agreed_mileage, + vehiclePrice: api.vehicle_price, + residualValue: api.residual_value, + deposit: api.deposit, + monthlyRent: api.monthly_rent, + monthlyRentTax: api.monthly_rent_tax, + insuranceCompany: api.insurance_company, + insuranceCompanyTel: api.insurance_company_tel, + createdAt: api.created_at, + updatedAt: api.updated_at, + }; +} + +export interface VehicleFormData { + plateNumber: string; + vehicleType: VehicleType | ''; + ownershipType: OwnershipType | ''; + model: string; + year: string; + // 법인: 취득일, 렌트/리스: 계약일자 + purchaseDate: string; + contractDate: string; + // 법인: 구매처, 렌트/리스: 렌트회사명 + rentCompany: string; + // 법인: 계약기간, 렌트/리스: 렌트기간 + rentPeriod: string; + // 법인: 취득가(공급가), 렌트/리스: 월 렌트료(공급가) + purchasePrice: string; + monthlyRent: string; + monthlyRentTax: string; + rentCompanyTel: string; + agreedMileage: string; + vehiclePrice: string; + residualValue: string; + deposit: string; + mileage: string; + insuranceCompany: string; + insuranceCompanyTel: string; + driver: string; + status: VehicleStatus | ''; + memo: string; +} + +export const EMPTY_VEHICLE_FORM: VehicleFormData = { + plateNumber: '', + vehicleType: '', + ownershipType: '', + model: '', + year: '', + purchaseDate: '', + contractDate: '', + rentCompany: '', + rentPeriod: '', + purchasePrice: '', + monthlyRent: '', + monthlyRentTax: '', + rentCompanyTel: '', + agreedMileage: '', + vehiclePrice: '', + residualValue: '', + deposit: '', + mileage: '', + insuranceCompany: '', + insuranceCompanyTel: '', + driver: '', + status: '', + memo: '', +}; + +// 드롭다운용 차량 목록 +export interface VehicleDropdownItem { + id: number; + plateNumber: string; + model: string; +} + +interface VehicleDropdownApi { + id: number; + plate_number: string; + model: string; +} + +export function transformVehicleDropdown(api: VehicleDropdownApi): VehicleDropdownItem { + return { + id: api.id, + plateNumber: api.plate_number, + model: api.model, + }; +} + +// ===== 차량일지 (Vehicle Logs) ===== + +export type TripType = + | 'commute_to' + | 'commute_from' + | 'business' + | 'personal' + | 'commute_round' + | 'business_round' + | 'personal_round'; + +export type LocationType = 'home' | 'office' | 'client' | 'other'; + +export const TRIP_TYPE_LABELS: Record = { + commute_to: '출근', + commute_from: '퇴근', + business: '업무용', + personal: '비업무', + commute_round: '출퇴근(왕복)', + business_round: '업무(왕복)', + personal_round: '비업무(왕복)', +}; + +export const TRIP_TYPE_COLORS: Record = { + commute_to: 'bg-green-100 text-green-700', + commute_from: 'bg-green-100 text-green-700', + business: 'bg-blue-100 text-blue-700', + personal: 'bg-gray-100 text-gray-700', + commute_round: 'bg-green-100 text-green-700', + business_round: 'bg-blue-100 text-blue-700', + personal_round: 'bg-gray-100 text-gray-700', +}; + +export const LOCATION_TYPE_LABELS: Record = { + home: '자택', + office: '회사', + client: '거래처', + other: '기타', +}; + +export interface VehicleLog { + id: number; + vehicleId: number; + logDate: string; + department: string | null; + driverName: string; + tripType: TripType; + departureType: LocationType; + departureName: string | null; + departureAddress: string | null; + arrivalType: LocationType; + arrivalName: string | null; + arrivalAddress: string | null; + distanceKm: number; + note: string | null; + // joined + vehicle?: VehicleDropdownItem; +} + +export interface VehicleLogApi { + id: number; + vehicle_id: number; + log_date: string; + department: string | null; + driver_name: string; + trip_type: string; + departure_type: string; + departure_name: string | null; + departure_address: string | null; + arrival_type: string; + arrival_name: string | null; + arrival_address: string | null; + distance_km: number; + note: string | null; + vehicle?: VehicleDropdownApi; +} + +export function transformVehicleLogApi(api: VehicleLogApi): VehicleLog { + return { + id: api.id, + vehicleId: api.vehicle_id, + logDate: api.log_date, + department: api.department, + driverName: api.driver_name, + tripType: api.trip_type as TripType, + departureType: api.departure_type as LocationType, + departureName: api.departure_name, + departureAddress: api.departure_address, + arrivalType: api.arrival_type as LocationType, + arrivalName: api.arrival_name, + arrivalAddress: api.arrival_address, + distanceKm: api.distance_km, + note: api.note, + vehicle: api.vehicle ? transformVehicleDropdown(api.vehicle) : undefined, + }; +} + +export interface VehicleLogFormData { + vehicleId: string; + logDate: string; + department: string; + driverName: string; + tripType: TripType | ''; + departureType: LocationType | ''; + departureName: string; + departureAddress: string; + arrivalType: LocationType | ''; + arrivalName: string; + arrivalAddress: string; + distanceKm: string; + note: string; +} + +export const EMPTY_LOG_FORM: VehicleLogFormData = { + vehicleId: '', + logDate: new Date().toISOString().slice(0, 10), + department: '', + driverName: '', + tripType: '', + departureType: '', + departureName: '', + departureAddress: '', + arrivalType: '', + arrivalName: '', + arrivalAddress: '', + distanceKm: '', + note: '', +}; + +export const NOTE_PRESETS = ['거래처방문', '제조시설등', '회의참석', '판촉활동', '교육등']; + +export interface VehicleLogSummary { + totalDistance: number; + totalCount: number; + commuteToDistance: number; + commuteToCount: number; + commuteFromDistance: number; + commuteFromCount: number; + businessDistance: number; + businessCount: number; + personalDistance: number; + personalCount: number; +} + +// ===== 정비이력 (Vehicle Maintenance) ===== + +export type MaintenanceCategory = '주유' | '정비' | '보험' | '세차' | '주차' | '통행료' | '검사' | '기타'; + +export const MAINTENANCE_CATEGORIES: MaintenanceCategory[] = [ + '주유', '정비', '보험', '세차', '주차', '통행료', '검사', '기타', +]; + +export const CATEGORY_COLORS: Record = { + '주유': 'bg-amber-100 text-amber-700', + '정비': 'bg-blue-100 text-blue-700', + '보험': 'bg-emerald-100 text-emerald-700', + '세차': 'bg-cyan-100 text-cyan-700', + '주차': 'bg-purple-100 text-purple-700', + '통행료': 'bg-orange-100 text-orange-700', + '검사': 'bg-indigo-100 text-indigo-700', + '기타': 'bg-gray-100 text-gray-700', +}; + +export interface VehicleMaintenance { + id: number; + vehicleId: number; + date: string; + category: MaintenanceCategory; + description: string; + amount: number; + mileage: number; + vendor: string | null; + memo: string | null; + // joined + vehicle?: VehicleDropdownItem; +} + +export interface VehicleMaintenanceApi { + id: number; + vehicle_id: number; + date: string; + category: string; + description: string; + amount: number; + mileage: number; + vendor: string | null; + memo: string | null; + vehicle?: { id: number; plate_number: string; model: string }; +} + +export function transformMaintenanceApi(api: VehicleMaintenanceApi): VehicleMaintenance { + return { + id: api.id, + vehicleId: api.vehicle_id, + date: api.date, + category: api.category as MaintenanceCategory, + description: api.description, + amount: api.amount, + mileage: api.mileage, + vendor: api.vendor, + memo: api.memo, + vehicle: api.vehicle ? transformVehicleDropdown(api.vehicle) : undefined, + }; +} + +export interface MaintenanceFormData { + vehicleId: string; + date: string; + category: MaintenanceCategory | ''; + description: string; + amount: string; + mileage: string; + vendor: string; + memo: string; +} + +export const EMPTY_MAINTENANCE_FORM: MaintenanceFormData = { + vehicleId: '', + date: new Date().toISOString().slice(0, 10), + category: '', + description: '', + amount: '', + mileage: '', + vendor: '', + memo: '', +}; + +// ===== 공통 유틸 ===== + +export const OWNERSHIP_LABELS: Record = { + corporate: '법인차량', + rent: '렌트차량', + lease: '리스차량', +}; + +export const OWNERSHIP_COLORS: Record = { + corporate: 'bg-purple-100 text-purple-700', + rent: 'bg-blue-100 text-blue-700', + lease: 'bg-green-100 text-green-700', +}; + +export const STATUS_LABELS: Record = { + active: '운행중', + maintenance: '정비중', + disposed: '처분', +}; + +export const STATUS_COLORS: Record = { + active: 'bg-green-100 text-green-700', + maintenance: 'bg-yellow-100 text-yellow-700', + disposed: 'bg-red-100 text-red-700', +}; + +export const VEHICLE_TYPES: VehicleType[] = ['승용차', '승합차', '화물차', 'SUV']; + +export function formatCurrency(value: number): string { + return value.toLocaleString('ko-KR') + '원'; +} + +export function formatDistance(value: number): string { + return value.toLocaleString('ko-KR') + 'km'; +}