feat: 공차중계 운전자 차량/프로필 관리 기능 구현

This commit is contained in:
dohyeons
2025-12-01 19:03:43 +09:00
parent 9c3f1d26ad
commit cd47f569e2
6 changed files with 540 additions and 89 deletions

View File

@@ -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>
)}
</>
);
}