feat: 공차중계 운전자 차량/프로필 관리 기능 구현
This commit is contained in:
@@ -11,9 +11,10 @@ import { Input } from "@/components/ui/input";
|
||||
import { Label } from "@/components/ui/label";
|
||||
import { Avatar, AvatarFallback, AvatarImage } from "@/components/ui/avatar";
|
||||
import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from "@/components/ui/select";
|
||||
import { Camera, X, Car, Wrench, Clock } from "lucide-react";
|
||||
import { Camera, X, Car, Wrench, Clock, Plus, Trash2 } from "lucide-react";
|
||||
import { ProfileFormData } from "@/types/profile";
|
||||
import { Separator } from "@/components/ui/separator";
|
||||
import { VehicleRegisterData } from "@/lib/api/driver";
|
||||
|
||||
// 운전자 정보 타입
|
||||
export interface DriverInfo {
|
||||
@@ -22,6 +23,7 @@ export interface DriverInfo {
|
||||
licenseNumber: string;
|
||||
phoneNumber: string;
|
||||
vehicleStatus: string | null;
|
||||
branchName: string | null;
|
||||
}
|
||||
|
||||
// 알림 모달 컴포넌트
|
||||
@@ -70,6 +72,7 @@ export interface DriverFormData {
|
||||
vehicleType: string;
|
||||
licenseNumber: string;
|
||||
phoneNumber: string;
|
||||
branchName: string;
|
||||
}
|
||||
|
||||
interface ProfileModalProps {
|
||||
@@ -90,11 +93,21 @@ interface ProfileModalProps {
|
||||
};
|
||||
// 운전자 관련 props (선택적)
|
||||
isDriver?: boolean;
|
||||
hasVehicle?: boolean;
|
||||
driverInfo?: DriverInfo | null;
|
||||
driverFormData?: DriverFormData;
|
||||
onDriverFormChange?: (field: keyof DriverFormData, value: string) => void;
|
||||
onDriverStatusChange?: (status: "off" | "maintenance") => void;
|
||||
onDriverAccountDelete?: () => void;
|
||||
// 차량 삭제/등록 관련 props
|
||||
onDeleteVehicle?: () => void;
|
||||
onOpenVehicleRegisterModal?: () => void;
|
||||
// 새 차량 등록 모달 관련 props
|
||||
isVehicleRegisterModalOpen?: boolean;
|
||||
newVehicleData?: VehicleRegisterData;
|
||||
onCloseVehicleRegisterModal?: () => void;
|
||||
onNewVehicleDataChange?: (field: keyof VehicleRegisterData, value: string) => void;
|
||||
onRegisterVehicle?: () => void;
|
||||
onClose: () => void;
|
||||
onFormChange: (field: keyof ProfileFormData, value: string) => void;
|
||||
onImageSelect: (event: React.ChangeEvent<HTMLInputElement>) => void;
|
||||
@@ -115,11 +128,19 @@ export function ProfileModal({
|
||||
departments,
|
||||
alertModal,
|
||||
isDriver = false,
|
||||
hasVehicle = false,
|
||||
driverInfo,
|
||||
driverFormData,
|
||||
onDriverFormChange,
|
||||
onDriverStatusChange,
|
||||
onDriverAccountDelete,
|
||||
onDeleteVehicle,
|
||||
onOpenVehicleRegisterModal,
|
||||
isVehicleRegisterModalOpen = false,
|
||||
newVehicleData,
|
||||
onCloseVehicleRegisterModal,
|
||||
onNewVehicleDataChange,
|
||||
onRegisterVehicle,
|
||||
onClose,
|
||||
onFormChange,
|
||||
onImageSelect,
|
||||
@@ -282,96 +303,147 @@ export function ProfileModal({
|
||||
</div>
|
||||
|
||||
{/* 운전자 정보 섹션 (공차중계 사용자만) */}
|
||||
{isDriver && driverFormData && onDriverFormChange && (
|
||||
{isDriver && (
|
||||
<>
|
||||
<Separator className="my-4" />
|
||||
<div className="space-y-4">
|
||||
<div className="flex items-center gap-2">
|
||||
<Car className="h-5 w-5 text-primary" />
|
||||
<h3 className="text-sm font-semibold">차량/운전자 정보</h3>
|
||||
<div className="flex items-center justify-between">
|
||||
<div className="flex items-center gap-2">
|
||||
<Car className="h-5 w-5 text-primary" />
|
||||
<h3 className="text-sm font-semibold">차량/운전자 정보</h3>
|
||||
</div>
|
||||
{/* 차량 유무에 따른 버튼 표시 */}
|
||||
{hasVehicle ? (
|
||||
<Button
|
||||
type="button"
|
||||
variant="destructive"
|
||||
size="sm"
|
||||
onClick={onDeleteVehicle}
|
||||
className="flex items-center gap-1"
|
||||
>
|
||||
<Trash2 className="h-3 w-3" />
|
||||
차량 삭제
|
||||
</Button>
|
||||
) : (
|
||||
<Button
|
||||
type="button"
|
||||
variant="default"
|
||||
size="sm"
|
||||
onClick={onOpenVehicleRegisterModal}
|
||||
className="flex items-center gap-1"
|
||||
>
|
||||
<Plus className="h-3 w-3" />
|
||||
새 차량 등록
|
||||
</Button>
|
||||
)}
|
||||
</div>
|
||||
|
||||
<div className="grid grid-cols-2 gap-4">
|
||||
<div className="space-y-2">
|
||||
<Label htmlFor="vehicleNumber">차량번호</Label>
|
||||
<Input
|
||||
id="vehicleNumber"
|
||||
value={driverFormData.vehicleNumber}
|
||||
onChange={(e) => onDriverFormChange("vehicleNumber", e.target.value)}
|
||||
placeholder="12가1234"
|
||||
/>
|
||||
</div>
|
||||
<div className="space-y-2">
|
||||
<Label htmlFor="vehicleType">차종</Label>
|
||||
<Input
|
||||
id="vehicleType"
|
||||
value={driverFormData.vehicleType}
|
||||
onChange={(e) => onDriverFormChange("vehicleType", e.target.value)}
|
||||
placeholder="1톤 카고"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
{/* 운전자 정보 (항상 수정 가능) */}
|
||||
{driverFormData && onDriverFormChange && (
|
||||
<>
|
||||
{/* 차량 정보 - 차량이 있을 때만 수정 가능 */}
|
||||
{hasVehicle ? (
|
||||
<div className="grid grid-cols-2 gap-4">
|
||||
<div className="space-y-2">
|
||||
<Label htmlFor="vehicleNumber">차량번호</Label>
|
||||
<Input
|
||||
id="vehicleNumber"
|
||||
value={driverFormData.vehicleNumber}
|
||||
onChange={(e) => onDriverFormChange("vehicleNumber", e.target.value)}
|
||||
placeholder="12가1234"
|
||||
/>
|
||||
</div>
|
||||
<div className="space-y-2">
|
||||
<Label htmlFor="vehicleType">차종</Label>
|
||||
<Input
|
||||
id="vehicleType"
|
||||
value={driverFormData.vehicleType}
|
||||
onChange={(e) => onDriverFormChange("vehicleType", e.target.value)}
|
||||
placeholder="1톤 카고"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
) : (
|
||||
/* 차량이 없는 경우: 안내 메시지 */
|
||||
<div className="text-center py-4 text-muted-foreground border rounded-md bg-muted/30">
|
||||
<Car className="h-8 w-8 mx-auto mb-2 opacity-30" />
|
||||
<p className="text-sm">등록된 차량이 없습니다.</p>
|
||||
<p className="text-xs mt-1">새 차량 등록 버튼을 눌러 차량을 등록하세요.</p>
|
||||
</div>
|
||||
)}
|
||||
|
||||
<div className="grid grid-cols-2 gap-4">
|
||||
<div className="space-y-2">
|
||||
<Label htmlFor="driverPhone">연락처</Label>
|
||||
<Input
|
||||
id="driverPhone"
|
||||
value={driverFormData.phoneNumber}
|
||||
onChange={(e) => onDriverFormChange("phoneNumber", e.target.value)}
|
||||
placeholder="010-1234-5678"
|
||||
/>
|
||||
</div>
|
||||
<div className="space-y-2">
|
||||
<Label htmlFor="licenseNumber">면허번호</Label>
|
||||
<Input
|
||||
id="licenseNumber"
|
||||
value={driverFormData.licenseNumber}
|
||||
onChange={(e) => onDriverFormChange("licenseNumber", e.target.value)}
|
||||
placeholder="12-34-567890-12"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* 차량 상태 */}
|
||||
{driverInfo && onDriverStatusChange && (
|
||||
<div className="space-y-2">
|
||||
<Label>현재 차량 상태</Label>
|
||||
<div className="flex items-center gap-4">
|
||||
<span className="text-sm font-medium px-3 py-1 rounded-full bg-muted">
|
||||
{getStatusLabel(driverInfo.vehicleStatus)}
|
||||
</span>
|
||||
<div className="flex gap-2">
|
||||
<Button
|
||||
type="button"
|
||||
variant="outline"
|
||||
size="sm"
|
||||
onClick={() => onDriverStatusChange("off")}
|
||||
disabled={driverInfo.vehicleStatus === "off"}
|
||||
className="flex items-center gap-1"
|
||||
>
|
||||
<Clock className="h-3 w-3" />
|
||||
대기
|
||||
</Button>
|
||||
<Button
|
||||
type="button"
|
||||
variant="outline"
|
||||
size="sm"
|
||||
onClick={() => onDriverStatusChange("maintenance")}
|
||||
disabled={driverInfo.vehicleStatus === "maintenance"}
|
||||
className="flex items-center gap-1"
|
||||
>
|
||||
<Wrench className="h-3 w-3" />
|
||||
정비
|
||||
</Button>
|
||||
{/* 운전자 개인 정보 - 항상 수정 가능 */}
|
||||
<div className="grid grid-cols-2 gap-4">
|
||||
<div className="space-y-2">
|
||||
<Label htmlFor="driverPhone">연락처</Label>
|
||||
<Input
|
||||
id="driverPhone"
|
||||
value={driverFormData.phoneNumber}
|
||||
onChange={(e) => onDriverFormChange("phoneNumber", e.target.value)}
|
||||
placeholder="010-1234-5678"
|
||||
/>
|
||||
</div>
|
||||
<div className="space-y-2">
|
||||
<Label htmlFor="licenseNumber">면허번호</Label>
|
||||
<Input
|
||||
id="licenseNumber"
|
||||
value={driverFormData.licenseNumber}
|
||||
onChange={(e) => onDriverFormChange("licenseNumber", e.target.value)}
|
||||
placeholder="12-34-567890-12"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
<p className="text-xs text-muted-foreground">
|
||||
* 운행/공차 상태는 공차등록 화면에서 변경하세요
|
||||
</p>
|
||||
</div>
|
||||
)}
|
||||
|
||||
<div className="space-y-2">
|
||||
<Label htmlFor="branchName">소속 지점</Label>
|
||||
<Input
|
||||
id="branchName"
|
||||
value={driverFormData.branchName}
|
||||
onChange={(e) => onDriverFormChange("branchName", e.target.value)}
|
||||
placeholder="서울 본점"
|
||||
/>
|
||||
</div>
|
||||
|
||||
{/* 차량 상태 - 차량이 있을 때만 표시 */}
|
||||
{hasVehicle && driverInfo && onDriverStatusChange && (
|
||||
<div className="space-y-2">
|
||||
<Label>현재 차량 상태</Label>
|
||||
<div className="flex items-center gap-4">
|
||||
<span className="text-sm font-medium px-3 py-1 rounded-full bg-muted">
|
||||
{getStatusLabel(driverInfo.vehicleStatus)}
|
||||
</span>
|
||||
<div className="flex gap-2">
|
||||
<Button
|
||||
type="button"
|
||||
variant="outline"
|
||||
size="sm"
|
||||
onClick={() => onDriverStatusChange("off")}
|
||||
disabled={driverInfo.vehicleStatus === "off"}
|
||||
className="flex items-center gap-1"
|
||||
>
|
||||
<Clock className="h-3 w-3" />
|
||||
대기
|
||||
</Button>
|
||||
<Button
|
||||
type="button"
|
||||
variant="outline"
|
||||
size="sm"
|
||||
onClick={() => onDriverStatusChange("maintenance")}
|
||||
disabled={driverInfo.vehicleStatus === "maintenance"}
|
||||
className="flex items-center gap-1"
|
||||
>
|
||||
<Wrench className="h-3 w-3" />
|
||||
정비
|
||||
</Button>
|
||||
</div>
|
||||
</div>
|
||||
<p className="text-xs text-muted-foreground">
|
||||
* 운행/공차 상태는 공차등록 화면에서 변경하세요
|
||||
</p>
|
||||
</div>
|
||||
)}
|
||||
</>
|
||||
)}
|
||||
</div>
|
||||
</>
|
||||
)}
|
||||
@@ -396,6 +468,50 @@ export function ProfileModal({
|
||||
message={alertModal.message}
|
||||
type={alertModal.type}
|
||||
/>
|
||||
|
||||
{/* 새 차량 등록 모달 */}
|
||||
{isVehicleRegisterModalOpen && newVehicleData && onNewVehicleDataChange && onRegisterVehicle && onCloseVehicleRegisterModal && (
|
||||
<ResizableDialog open={isVehicleRegisterModalOpen} onOpenChange={onCloseVehicleRegisterModal}>
|
||||
<ResizableDialogContent className="sm:max-w-[400px]">
|
||||
<ResizableDialogHeader>
|
||||
<ResizableDialogTitle>새 차량 등록</ResizableDialogTitle>
|
||||
<ResizableDialogDescription>
|
||||
새로운 차량 정보를 입력해주세요.
|
||||
</ResizableDialogDescription>
|
||||
</ResizableDialogHeader>
|
||||
|
||||
<div className="grid gap-4 py-4">
|
||||
<div className="space-y-2">
|
||||
<Label htmlFor="newVehicleNumber">차량번호 *</Label>
|
||||
<Input
|
||||
id="newVehicleNumber"
|
||||
value={newVehicleData.vehicleNumber}
|
||||
onChange={(e) => onNewVehicleDataChange("vehicleNumber", e.target.value)}
|
||||
placeholder="12가1234"
|
||||
/>
|
||||
</div>
|
||||
<div className="space-y-2">
|
||||
<Label htmlFor="newVehicleType">차종</Label>
|
||||
<Input
|
||||
id="newVehicleType"
|
||||
value={newVehicleData.vehicleType || ""}
|
||||
onChange={(e) => onNewVehicleDataChange("vehicleType", e.target.value)}
|
||||
placeholder="1톤 카고"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<ResizableDialogFooter>
|
||||
<Button type="button" variant="outline" onClick={onCloseVehicleRegisterModal}>
|
||||
취소
|
||||
</Button>
|
||||
<Button type="button" onClick={onRegisterVehicle}>
|
||||
등록
|
||||
</Button>
|
||||
</ResizableDialogFooter>
|
||||
</ResizableDialogContent>
|
||||
</ResizableDialog>
|
||||
)}
|
||||
</>
|
||||
);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user