summaryrefslogtreecommitdiff
path: root/lib/material/vendor-material/add-confirmed-material.tsx
diff options
context:
space:
mode:
Diffstat (limited to 'lib/material/vendor-material/add-confirmed-material.tsx')
-rw-r--r--lib/material/vendor-material/add-confirmed-material.tsx183
1 files changed, 183 insertions, 0 deletions
diff --git a/lib/material/vendor-material/add-confirmed-material.tsx b/lib/material/vendor-material/add-confirmed-material.tsx
new file mode 100644
index 00000000..bc232a1b
--- /dev/null
+++ b/lib/material/vendor-material/add-confirmed-material.tsx
@@ -0,0 +1,183 @@
+"use client";
+
+import * as React from "react";
+import { useState } from "react";
+import { useSession } from "next-auth/react";
+import { Button } from "@/components/ui/button";
+import {
+ Dialog,
+ DialogContent,
+ DialogDescription,
+ DialogFooter,
+ DialogHeader,
+ DialogTitle,
+ DialogTrigger,
+} from "@/components/ui/dialog";
+import { Label } from "@/components/ui/label";
+import { toast } from "sonner";
+import { Plus, Loader2 } from "lucide-react";
+import { MaterialSelector } from "@/components/common/material/material-selector";
+import { MaterialSearchItem } from "@/lib/material/material-group-service";
+import { addConfirmedMaterial, VendorPossibleMaterial } from "../vendor-possible-material-service";
+
+interface AddConfirmedMaterialProps {
+ vendorId: number;
+ existingConfirmedMaterials: VendorPossibleMaterial[];
+ onMaterialAdded?: () => void;
+}
+
+export function AddConfirmedMaterial({
+ vendorId,
+ existingConfirmedMaterials,
+ onMaterialAdded,
+}: AddConfirmedMaterialProps) {
+ const { data: session } = useSession();
+ const [open, setOpen] = useState(false);
+ const [isLoading, setIsLoading] = useState(false);
+ const [selectedMaterials, setSelectedMaterials] = useState<MaterialSearchItem[]>([]);
+
+ // 이미 등록된 자재그룹코드들의 Set
+ const existingMaterialCodes = new Set(
+ existingConfirmedMaterials.map(material => material.itemCode).filter(Boolean)
+ );
+
+ // 자재 선택 시 중복 체크
+ const handleMaterialsChange = (materials: MaterialSearchItem[]) => {
+ // 이미 등록된 자재가 있는지 확인
+ const duplicatedMaterials = materials.filter(material =>
+ existingMaterialCodes.has(material.materialGroupCode)
+ );
+
+ if (duplicatedMaterials.length > 0) {
+ const duplicatedCodes = duplicatedMaterials.map(m => m.materialGroupCode).join(', ');
+ toast.error(`이미 등록된 자재그룹코드입니다: ${duplicatedCodes}`);
+
+ // 중복되지 않은 자재만 선택
+ const validMaterials = materials.filter(material =>
+ !existingMaterialCodes.has(material.materialGroupCode)
+ );
+ setSelectedMaterials(validMaterials);
+ } else {
+ setSelectedMaterials(materials);
+ }
+ };
+
+ const handleSubmit = async () => {
+ if (!session?.user) {
+ toast.error("로그인이 필요합니다.");
+ return;
+ }
+
+ if (selectedMaterials.length === 0) {
+ toast.error("추가할 자재를 선택해주세요.");
+ return;
+ }
+
+ setIsLoading(true);
+
+ try {
+ // 선택된 자재들을 각각 추가
+ for (const material of selectedMaterials) {
+ const materialData = {
+ itemCode: material.materialGroupCode,
+ itemName: material.materialName,
+ };
+
+ await addConfirmedMaterial(
+ vendorId,
+ materialData,
+ Number(session.user.id),
+ session.user.name || "알 수 없음"
+ );
+ }
+
+ toast.success(`${selectedMaterials.length}개의 자재가 확정정보에 추가되었습니다.`);
+
+ // 폼 리셋
+ setSelectedMaterials([]);
+ setOpen(false);
+
+ // 부모 컴포넌트에 추가 완료 알림
+ onMaterialAdded?.();
+
+ } catch (error) {
+ console.error("자재 추가 실패:", error);
+
+ // 에러 메시지를 더 구체적으로 표시
+ if (error instanceof Error) {
+ if (error.message.includes("이미 확정정보에 등록되어 있습니다")) {
+ toast.error(error.message);
+ } else {
+ toast.error(`자재 추가 실패: ${error.message}`);
+ }
+ } else {
+ toast.error("자재 추가 중 알 수 없는 오류가 발생했습니다.");
+ }
+ } finally {
+ setIsLoading(false);
+ }
+ };
+
+ return (
+ <Dialog open={open} onOpenChange={setOpen}>
+ <DialogTrigger asChild>
+ <Button size="sm" className="gap-2">
+ <Plus className="h-4 w-4" />
+ 추가 등록
+ </Button>
+ </DialogTrigger>
+ <DialogContent className="max-w-2xl max-h-[90vh] overflow-y-auto">
+ <DialogHeader>
+ <DialogTitle>확정정보 자재 추가</DialogTitle>
+ <DialogDescription>
+ 구매담당자 권한으로 확정 공급품목을 추가합니다. 상세 정보는 I/F를 통해 업데이트됩니다.
+ </DialogDescription>
+ </DialogHeader>
+
+ <div className="space-y-4">
+ {/* 자재 선택 */}
+ <div className="space-y-2">
+ <Label>자재 선택 *</Label>
+ <MaterialSelector
+ selectedMaterials={selectedMaterials}
+ onMaterialsChange={handleMaterialsChange}
+ singleSelect={false}
+ placeholder="자재그룹코드 또는 자재명으로 검색..."
+ noValuePlaceHolder="자재를 선택해주세요"
+ maxSelections={10}
+ closeOnSelect={false}
+ excludeMaterialCodes={existingMaterialCodes}
+ />
+ {existingMaterialCodes.size > 0 && (
+ <p className="text-xs text-muted-foreground">
+ 💡 이미 등록된 자재그룹코드는 자동으로 제외됩니다.
+ </p>
+ )}
+ <p className="text-sm text-muted-foreground">
+ 최대 10개까지 선택 가능합니다. 등록자는 현재 로그인한 사용자로 자동 설정됩니다.
+ </p>
+ </div>
+ </div>
+
+ <DialogFooter>
+ <Button
+ type="button"
+ variant="outline"
+ onClick={() => setOpen(false)}
+ disabled={isLoading}
+ >
+ 취소
+ </Button>
+ <Button
+ type="button"
+ onClick={handleSubmit}
+ disabled={isLoading || selectedMaterials.length === 0}
+ >
+ {isLoading && <Loader2 className="mr-2 h-4 w-4 animate-spin" />}
+ {isLoading ? "추가 중..." : "추가"}
+ </Button>
+ </DialogFooter>
+ </DialogContent>
+ </Dialog>
+ );
+}