From 65a68325658401dd8a90ea900c1542c17c63d7ce Mon Sep 17 00:00:00 2001
From: joonhoekim <26rote@gmail.com>
Date: Mon, 27 Oct 2025 09:49:34 +0900
Subject: (김준회) swp-upload 다이얼로그로 결과 알림, vendorCode 자동 선택 처리
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

---
 lib/swp/table/swp-table-toolbar.tsx | 179 +++++++++++++++++-------------------
 1 file changed, 85 insertions(+), 94 deletions(-)

(limited to 'lib/swp/table/swp-table-toolbar.tsx')

diff --git a/lib/swp/table/swp-table-toolbar.tsx b/lib/swp/table/swp-table-toolbar.tsx
index 03082b26..fc8337f5 100644
--- a/lib/swp/table/swp-table-toolbar.tsx
+++ b/lib/swp/table/swp-table-toolbar.tsx
@@ -3,13 +3,6 @@
 import { useState, useTransition, useMemo } from "react";
 import { Button } from "@/components/ui/button";
 import { Input } from "@/components/ui/input";
-import {
-  Select,
-  SelectContent,
-  SelectItem,
-  SelectTrigger,
-  SelectValue,
-} from "@/components/ui/select";
 import {
   Popover,
   PopoverContent,
@@ -23,17 +16,20 @@ import { useRouter } from "next/navigation";
 import { cn } from "@/lib/utils";
 import { useRef } from "react";
 import { SwpUploadHelpDialog } from "./swp-help-dialog";
+import { SwpUploadResultDialog } from "./swp-upload-result-dialog";
 
 interface SwpTableToolbarProps {
   filters: SwpTableFilters;
   onFiltersChange: (filters: SwpTableFilters) => void;
   projects?: Array<{ PROJ_NO: string; PROJ_NM: string }>;
+  vendorCode?: string; // 벤더가 접속했을 때 고정할 벤더 코드
 }
 
 export function SwpTableToolbar({
   filters,
   onFiltersChange,
   projects = [],
+  vendorCode,
 }: SwpTableToolbarProps) {
   const [isSyncing, startSync] = useTransition();
   const [isUploading, startUpload] = useTransition();
@@ -43,6 +39,8 @@ export function SwpTableToolbar({
   const [projectSearchOpen, setProjectSearchOpen] = useState(false);
   const [projectSearch, setProjectSearch] = useState("");
   const fileInputRef = useRef<HTMLInputElement>(null);
+  const [uploadResults, setUploadResults] = useState<Array<{ fileName: string; success: boolean; error?: string }>>([]);
+  const [showResultDialog, setShowResultDialog] = useState(false);
 
   // 동기화 핸들러
   const handleSync = () => {
@@ -96,7 +94,7 @@ export function SwpTableToolbar({
   const handleUploadFiles = () => {
     // 프로젝트와 벤더 코드 체크
     const projectNo = localFilters.projNo;
-    const vndrCd = localFilters.vndrCd;
+    const vndrCd = vendorCode || localFilters.vndrCd;
 
     if (!projectNo) {
       toast({
@@ -130,7 +128,7 @@ export function SwpTableToolbar({
     }
 
     const projectNo = localFilters.projNo!;
-    const vndrCd = localFilters.vndrCd!;
+    const vndrCd = vendorCode || localFilters.vndrCd!;
 
     startUpload(async () => {
       try {
@@ -153,41 +151,27 @@ export function SwpTableToolbar({
         // 서버 액션 호출
         const result = await uploadSwpFilesAction(projectNo, vndrCd, fileInfos);
 
-        if (result.success) {
-          toast({
-            title: "업로드 완료",
-            description: result.message,
-          });
+        // 결과 저장 및 다이얼로그 표시
+        setUploadResults(result.details);
+        setShowResultDialog(true);
 
-          // 페이지 새로고침
+        // 성공한 파일이 있으면 페이지 새로고침
+        const successCount = result.details.filter((d) => d.success).length;
+        if (successCount > 0) {
           router.refresh();
-        } else {
-          toast({
-            variant: "destructive",
-            title: "업로드 실패",
-            description: result.message,
-          });
-        }
-
-        // 실패한 파일이 있으면 상세 정보 표시
-        const failedFiles = result.details.filter((d) => !d.success);
-        if (failedFiles.length > 0) {
-          console.error("실패한 파일:", failedFiles);
-          failedFiles.forEach((f) => {
-            toast({
-              variant: "destructive",
-              title: `${f.fileName} 업로드 실패`,
-              description: f.error || "알 수 없는 오류",
-            });
-          });
         }
       } catch (error) {
         console.error("파일 업로드 실패:", error);
-        toast({
-          variant: "destructive",
-          title: "업로드 실패",
-          description: error instanceof Error ? error.message : "알 수 없는 오류",
-        });
+        
+        // 예외 발생 시에도 결과 다이얼로그 표시
+        const errorResults = Array.from(selectedFiles).map((file) => ({
+          fileName: file.name,
+          success: false,
+          error: error instanceof Error ? error.message : "알 수 없는 오류",
+        }));
+        
+        setUploadResults(errorResults);
+        setShowResultDialog(true);
       } finally {
         // 파일 입력 초기화
         if (fileInputRef.current) {
@@ -222,46 +206,59 @@ export function SwpTableToolbar({
   }, [projects, projectSearch]);
 
   return (
-    <div className="space-y-4">
-      {/* 상단 액션 바 */}
-      <div className="flex items-center justify-between">
-        <div className="flex items-center gap-2">
-          <Button
-            onClick={handleSync}
-            disabled={isSyncing || !localFilters.projNo}
-            size="sm"
-          >
-            <RefreshCw className={`h-4 w-4 mr-2 ${isSyncing ? "animate-spin" : ""}`} />
-            {isSyncing ? "동기화 중..." : "SWP 동기화"}
-          </Button>
-          
-        </div>
+    <>
+      {/* 업로드 결과 다이얼로그 */}
+      <SwpUploadResultDialog
+        open={showResultDialog}
+        onOpenChange={setShowResultDialog}
+        results={uploadResults}
+      />
+
+      <div className="space-y-4">
+        {/* 상단 액션 바 */}
+        <div className="flex items-center justify-between">
+          <div className="flex items-center gap-2">
+            <Button
+              onClick={handleSync}
+              disabled={isSyncing || !localFilters.projNo}
+              size="sm"
+            >
+              <RefreshCw className={`h-4 w-4 mr-2 ${isSyncing ? "animate-spin" : ""}`} />
+              {isSyncing ? "동기화 중..." : "SWP 동기화"}
+            </Button>
+          </div>
 
-        <div className="text-sm text-muted-foreground">
-          SWP 문서 관리 시스템
-        </div>
-        <div className="flex items-center gap-2">
-          <input
-            ref={fileInputRef}
-            type="file"
-            multiple
-            className="hidden"
-            onChange={handleFileChange}
-            accept="*/*"
-          />
-          <Button 
-            variant="outline" 
-            size="sm" 
-            onClick={handleUploadFiles}
-            disabled={isUploading || !localFilters.projNo || !localFilters.vndrCd}
-          >
-            <Upload className={`h-4 w-4 mr-2 ${isUploading ? "animate-pulse" : ""}`} />
-            {isUploading ? "업로드 중..." : "파일 업로드"}
-          </Button>
+          <div className="text-sm text-muted-foreground">
+            SWP 문서 관리 시스템
+          </div>
           
-          <SwpUploadHelpDialog />
+          <div className="flex items-center gap-2">
+            {/* 벤더만 파일 업로드 기능 사용 가능 */}
+            {vendorCode && (
+              <>
+                <input
+                  ref={fileInputRef}
+                  type="file"
+                  multiple
+                  className="hidden"
+                  onChange={handleFileChange}
+                  accept="*/*"
+                />
+                <Button
+                  variant="outline"
+                  size="sm"
+                  onClick={handleUploadFiles}
+                  disabled={isUploading || !localFilters.projNo || (!vendorCode && !localFilters.vndrCd)}
+                >
+                  <Upload className={`h-4 w-4 mr-2 ${isUploading ? "animate-pulse" : ""}`} />
+                  {isUploading ? "업로드 중..." : "파일 업로드"}
+                </Button>
+
+                <SwpUploadHelpDialog />
+              </>
+            )}
+          </div>
         </div>
-      </div>
 
       {/* 검색 필터 */}
       <div className="rounded-lg border p-4 space-y-4">
@@ -423,33 +420,26 @@ export function SwpTableToolbar({
             <Input
               id="vndrCd"
               placeholder="업체 코드"
-              value={localFilters.vndrCd || ""}
+              value={vendorCode || localFilters.vndrCd || ""}
               onChange={(e) =>
                 setLocalFilters({ ...localFilters, vndrCd: e.target.value })
               }
+              disabled={!!vendorCode} // 벤더 코드가 제공되면 입력 비활성화
+              className={vendorCode ? "bg-muted" : ""}
             />
           </div>
 
           {/* 스테이지 */}
           <div className="space-y-2">
             <Label htmlFor="stage">스테이지</Label>
-            <Select
-              value={localFilters.stage || "__all__"}
-              onValueChange={(value) =>
-                setLocalFilters({ ...localFilters, stage: value === "__all__" ? undefined : value })
+            <Input
+              id="stage"
+              placeholder="스테이지 입력"
+              value={localFilters.stage || ""}
+              onChange={(e) =>
+                setLocalFilters({ ...localFilters, stage: e.target.value })
               }
-            >
-              <SelectTrigger id="stage">
-                <SelectValue placeholder="전체" />
-              </SelectTrigger>
-              <SelectContent>
-                <SelectItem value="__all__">전체</SelectItem>
-                <SelectItem value="IFA">IFA</SelectItem>
-                <SelectItem value="IFC">IFC</SelectItem>
-                <SelectItem value="AFC">AFC</SelectItem>
-                <SelectItem value="BFC">BFC</SelectItem>
-              </SelectContent>
-            </Select>
+            />
           </div>
         </div>
 
@@ -459,8 +449,9 @@ export function SwpTableToolbar({
             검색
           </Button>
         </div>
+        </div>
       </div>
-    </div>
+    </>
   );
 }
 
-- 
cgit v1.2.3

