271 lines
8.0 KiB
TypeScript
271 lines
8.0 KiB
TypeScript
"use client";
|
|
|
|
import React, { useState } from "react";
|
|
import {
|
|
Dialog,
|
|
DialogContent,
|
|
DialogDescription,
|
|
DialogFooter,
|
|
DialogHeader,
|
|
DialogTitle,
|
|
} from "@/components/ui/dialog";
|
|
import { Button } from "@/components/ui/button";
|
|
import { Label } from "@/components/ui/label";
|
|
import { Input } from "@/components/ui/input";
|
|
import {
|
|
Select,
|
|
SelectContent,
|
|
SelectItem,
|
|
SelectTrigger,
|
|
SelectValue,
|
|
} from "@/components/ui/select";
|
|
import { Textarea } from "@/components/ui/textarea";
|
|
import { OrderCustomerSearch } from "./OrderCustomerSearch";
|
|
import { OrderItemRepeaterTable } from "./OrderItemRepeaterTable";
|
|
import { toast } from "sonner";
|
|
import { apiClient } from "@/lib/api/client";
|
|
|
|
interface OrderRegistrationModalProps {
|
|
open: boolean;
|
|
onOpenChange: (open: boolean) => void;
|
|
onSuccess?: () => void;
|
|
}
|
|
|
|
export function OrderRegistrationModal({
|
|
open,
|
|
onOpenChange,
|
|
onSuccess,
|
|
}: OrderRegistrationModalProps) {
|
|
// 입력 방식
|
|
const [inputMode, setInputMode] = useState<string>("customer_first");
|
|
|
|
// 폼 데이터
|
|
const [formData, setFormData] = useState<any>({
|
|
customerCode: "",
|
|
customerName: "",
|
|
deliveryDate: "",
|
|
memo: "",
|
|
});
|
|
|
|
// 선택된 품목 목록
|
|
const [selectedItems, setSelectedItems] = useState<any[]>([]);
|
|
|
|
// 저장 중
|
|
const [isSaving, setIsSaving] = useState(false);
|
|
|
|
// 저장 처리
|
|
const handleSave = async () => {
|
|
try {
|
|
// 유효성 검사
|
|
if (!formData.customerCode) {
|
|
toast.error("거래처를 선택해주세요");
|
|
return;
|
|
}
|
|
|
|
if (selectedItems.length === 0) {
|
|
toast.error("품목을 추가해주세요");
|
|
return;
|
|
}
|
|
|
|
setIsSaving(true);
|
|
|
|
// 수주 등록 API 호출
|
|
const response = await apiClient.post("/orders", {
|
|
inputMode,
|
|
customerCode: formData.customerCode,
|
|
deliveryDate: formData.deliveryDate,
|
|
items: selectedItems,
|
|
memo: formData.memo,
|
|
});
|
|
|
|
if (response.data.success) {
|
|
toast.success("수주가 등록되었습니다");
|
|
onOpenChange(false);
|
|
onSuccess?.();
|
|
|
|
// 폼 초기화
|
|
resetForm();
|
|
} else {
|
|
toast.error(response.data.message || "수주 등록에 실패했습니다");
|
|
}
|
|
} catch (error: any) {
|
|
console.error("수주 등록 오류:", error);
|
|
toast.error(
|
|
error.response?.data?.message || "수주 등록 중 오류가 발생했습니다"
|
|
);
|
|
} finally {
|
|
setIsSaving(false);
|
|
}
|
|
};
|
|
|
|
// 취소 처리
|
|
const handleCancel = () => {
|
|
onOpenChange(false);
|
|
resetForm();
|
|
};
|
|
|
|
// 폼 초기화
|
|
const resetForm = () => {
|
|
setInputMode("customer_first");
|
|
setFormData({
|
|
customerCode: "",
|
|
customerName: "",
|
|
deliveryDate: "",
|
|
memo: "",
|
|
});
|
|
setSelectedItems([]);
|
|
};
|
|
|
|
// 전체 금액 계산
|
|
const totalAmount = selectedItems.reduce(
|
|
(sum, item) => sum + (item.amount || 0),
|
|
0
|
|
);
|
|
|
|
return (
|
|
<Dialog open={open} onOpenChange={onOpenChange}>
|
|
<DialogContent className="max-w-[95vw] sm:max-w-[1200px] max-h-[90vh] overflow-y-auto">
|
|
<DialogHeader>
|
|
<DialogTitle className="text-base sm:text-lg">수주 등록</DialogTitle>
|
|
<DialogDescription className="text-xs sm:text-sm">
|
|
새로운 수주를 등록합니다
|
|
</DialogDescription>
|
|
</DialogHeader>
|
|
|
|
<div className="space-y-6">
|
|
{/* 입력 방식 선택 */}
|
|
<div className="space-y-2">
|
|
<Label htmlFor="inputMode" className="text-xs sm:text-sm">
|
|
입력 방식 *
|
|
</Label>
|
|
<Select value={inputMode} onValueChange={setInputMode}>
|
|
<SelectTrigger className="h-8 text-xs sm:h-10 sm:text-sm">
|
|
<SelectValue placeholder="입력 방식 선택" />
|
|
</SelectTrigger>
|
|
<SelectContent>
|
|
<SelectItem value="customer_first">거래처 우선</SelectItem>
|
|
<SelectItem value="quotation">견대 방식</SelectItem>
|
|
<SelectItem value="unit_price">단가 방식</SelectItem>
|
|
</SelectContent>
|
|
</Select>
|
|
</div>
|
|
|
|
{/* 입력 방식에 따른 동적 폼 */}
|
|
{inputMode === "customer_first" && (
|
|
<div className="grid grid-cols-1 sm:grid-cols-2 gap-4">
|
|
{/* 거래처 검색 */}
|
|
<div className="space-y-2">
|
|
<Label className="text-xs sm:text-sm">거래처 *</Label>
|
|
<OrderCustomerSearch
|
|
value={formData.customerCode}
|
|
onChange={(code, fullData) => {
|
|
setFormData({
|
|
...formData,
|
|
customerCode: code || "",
|
|
customerName: fullData?.customer_name || "",
|
|
});
|
|
}}
|
|
/>
|
|
</div>
|
|
|
|
{/* 납품일 */}
|
|
<div className="space-y-2">
|
|
<Label htmlFor="deliveryDate" className="text-xs sm:text-sm">
|
|
납품일
|
|
</Label>
|
|
<Input
|
|
id="deliveryDate"
|
|
type="date"
|
|
value={formData.deliveryDate}
|
|
onChange={(e) =>
|
|
setFormData({ ...formData, deliveryDate: e.target.value })
|
|
}
|
|
className="h-8 text-xs sm:h-10 sm:text-sm"
|
|
/>
|
|
</div>
|
|
</div>
|
|
)}
|
|
|
|
{inputMode === "quotation" && (
|
|
<div className="grid grid-cols-1 sm:grid-cols-2 gap-4">
|
|
<div className="space-y-2">
|
|
<Label className="text-xs sm:text-sm">견대 번호 *</Label>
|
|
<Input
|
|
placeholder="견대 번호를 입력하세요"
|
|
className="h-8 text-xs sm:h-10 sm:text-sm"
|
|
/>
|
|
</div>
|
|
</div>
|
|
)}
|
|
|
|
{inputMode === "unit_price" && (
|
|
<div className="grid grid-cols-1 sm:grid-cols-2 gap-4">
|
|
<div className="space-y-2">
|
|
<Label className="text-xs sm:text-sm">단가 방식 설정</Label>
|
|
<Input
|
|
placeholder="단가 정보 입력"
|
|
className="h-8 text-xs sm:h-10 sm:text-sm"
|
|
/>
|
|
</div>
|
|
</div>
|
|
)}
|
|
|
|
{/* 추가된 품목 */}
|
|
<div className="space-y-2">
|
|
<Label className="text-xs sm:text-sm">추가된 품목</Label>
|
|
<OrderItemRepeaterTable
|
|
value={selectedItems}
|
|
onChange={setSelectedItems}
|
|
/>
|
|
</div>
|
|
|
|
{/* 전체 금액 표시 */}
|
|
{selectedItems.length > 0 && (
|
|
<div className="flex justify-end">
|
|
<div className="text-sm sm:text-base font-semibold">
|
|
전체 금액: {totalAmount.toLocaleString()}원
|
|
</div>
|
|
</div>
|
|
)}
|
|
|
|
{/* 메모 */}
|
|
<div className="space-y-2">
|
|
<Label htmlFor="memo" className="text-xs sm:text-sm">
|
|
메모
|
|
</Label>
|
|
<Textarea
|
|
id="memo"
|
|
placeholder="메모를 입력하세요"
|
|
value={formData.memo}
|
|
onChange={(e) =>
|
|
setFormData({ ...formData, memo: e.target.value })
|
|
}
|
|
className="text-xs sm:text-sm"
|
|
rows={3}
|
|
/>
|
|
</div>
|
|
</div>
|
|
|
|
<DialogFooter className="gap-2 sm:gap-0">
|
|
<Button
|
|
variant="outline"
|
|
onClick={handleCancel}
|
|
disabled={isSaving}
|
|
className="h-8 flex-1 text-xs sm:h-10 sm:flex-none sm:text-sm"
|
|
>
|
|
취소
|
|
</Button>
|
|
<Button
|
|
onClick={handleSave}
|
|
disabled={isSaving}
|
|
className="h-8 flex-1 text-xs sm:h-10 sm:flex-none sm:text-sm"
|
|
>
|
|
{isSaving ? "저장 중..." : "저장"}
|
|
</Button>
|
|
</DialogFooter>
|
|
</DialogContent>
|
|
</Dialog>
|
|
);
|
|
}
|
|
|