summaryrefslogtreecommitdiff
path: root/lib/material/vendor-material/add-confirmed-material.tsx
blob: bc232a1b3730f80b37109d60c90992f6fafa2fcf (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
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>
  );
}