From c62ec046327fd388ebce04571b55910747e69a3b Mon Sep 17 00:00:00 2001
From: dujinkim <dujin.kim@dtsolution.co.kr>
Date: Tue, 9 Sep 2025 10:32:34 +0000
Subject: (정희성, 최겸, 대표님) formatDate 변경 등
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

---
 components/form-data-stat/form-data-stat-table.tsx | 261 +++++++++++++++++----
 1 file changed, 219 insertions(+), 42 deletions(-)

(limited to 'components/form-data-stat/form-data-stat-table.tsx')

diff --git a/components/form-data-stat/form-data-stat-table.tsx b/components/form-data-stat/form-data-stat-table.tsx
index fe59785d..a56a4e88 100644
--- a/components/form-data-stat/form-data-stat-table.tsx
+++ b/components/form-data-stat/form-data-stat-table.tsx
@@ -1,10 +1,11 @@
+// components/form-data-stat/form-data-stat-table.tsx
 "use client";
 
 import * as React from "react";
 import { Button } from "@/components/ui/button";
 import { Card, CardContent, CardHeader, CardTitle } from "@/components/ui/card";
 import { Badge } from "@/components/ui/badge";
-import { RefreshCw } from "lucide-react";
+import { RefreshCw, Check, ChevronsUpDown } from "lucide-react";
 import { type ColumnDef } from "@tanstack/react-table";
 import { ClientDataTableColumnHeaderSimple } from "@/components/client-data-table/data-table-column-simple-header";
 import { ClientDataTable } from "@/components/client-data-table/data-table";
@@ -12,17 +13,25 @@ import { cn } from "@/lib/utils";
 import { toast } from "sonner";
 import type { DataTableAdvancedFilterField } from "@/types/table";
 import { Progress } from "@/components/ui/progress";
-import { getVendorFormStatus } from "@/lib/forms/stat";
+import { getVendorFormStatus, getProjectsWithContracts } from "@/lib/forms/stat";
+import { Popover, PopoverTrigger, PopoverContent } from "@/components/ui/popover";
+import { Command, CommandInput, CommandList, CommandEmpty, CommandGroup, CommandItem } from "@/components/ui/command";
 
 // 타입 정의
 interface VendorFormStatus {
   vendorId: number;
   vendorName: string;
-  formCount: number; // 벤더가 가진 form 개수
-  tagCount: number; // 벤더가 가진 tag 개수
-  totalFields: number; // 입력해야 하는 총 필드 개수
-  completedFields: number; // 입력 완료된 필드 개수
-  completionRate: number; // 완료율 (%)
+  formCount: number;
+  tagCount: number;
+  totalFields: number;
+  completedFields: number;
+  completionRate: number;
+}
+
+interface Project {
+  id: number;
+  projectCode: string;
+  projectName: string;
 }
 
 interface VendorFormStatusTableProps {
@@ -45,35 +54,164 @@ const getCompletionBadgeVariant = (rate: number): "default" | "secondary" | "des
   return "destructive";
 };
 
+// 프로젝트 선택 컴포넌트
+function ProjectSelectorWithContracts({ 
+  selectedProject,
+  onProjectSelect,
+  projects,
+  isLoading
+}: {
+  selectedProject: Project | null;
+  onProjectSelect: (project: Project | null) => void;
+  projects: Project[];
+  isLoading: boolean;
+}) {
+  const [open, setOpen] = React.useState(false);
+  const [searchTerm, setSearchTerm] = React.useState("");
+
+  // 검색어로 필터링
+  const filteredProjects = React.useMemo(() => {
+    if (!searchTerm.trim()) return projects;
+    
+    const lowerSearch = searchTerm.toLowerCase();
+    return projects.filter(
+      project => 
+        project.projectCode.toLowerCase().includes(lowerSearch) || 
+        project.projectName.toLowerCase().includes(lowerSearch)
+    );
+  }, [projects, searchTerm]);
+
+  const handleSelectProject = (project: Project | null) => {
+    onProjectSelect(project);
+    setOpen(false);
+  };
+
+  return (
+    <Popover open={open} onOpenChange={setOpen}>
+      <PopoverTrigger asChild>
+        <Button
+          variant="outline"
+          role="combobox"
+          aria-expanded={open}
+          className="w-full justify-between"
+          disabled={isLoading}
+        >
+          {selectedProject
+            ? `${selectedProject.projectCode} - ${selectedProject.projectName}`
+            : "프로젝트를 선택하세요..."}
+          <ChevronsUpDown className="ml-2 h-4 w-4 shrink-0 opacity-50" />
+        </Button>
+      </PopoverTrigger>
+      <PopoverContent className="w-[400px] p-0">
+        <Command>
+          <CommandInput
+            placeholder="프로젝트 코드/이름 검색..."
+            onValueChange={setSearchTerm}
+          />
+          <CommandList className="max-h-[300px]">
+            <CommandEmpty>검색 결과가 없습니다</CommandEmpty>
+            {isLoading ? (
+              <div className="py-6 text-center text-sm">로딩 중...</div>
+            ) : (
+              <CommandGroup>
+                {/* 전체 옵션 추가 */}
+                <CommandItem
+                  value="all"
+                  onSelect={() => handleSelectProject(null)}
+                >
+                  <Check
+                    className={cn(
+                      "mr-2 h-4 w-4",
+                      selectedProject === null ? "opacity-100" : "opacity-0"
+                    )}
+                  />
+                  <span className="font-medium">전체 프로젝트</span>
+                </CommandItem>
+                {/* 프로젝트 목록 */}
+                {filteredProjects.map((project) => (
+                  <CommandItem
+                    key={project.id}
+                    value={`${project.projectCode} ${project.projectName}`}
+                    onSelect={() => handleSelectProject(project)}
+                  >
+                    <Check
+                      className={cn(
+                        "mr-2 h-4 w-4",
+                        selectedProject?.id === project.id
+                          ? "opacity-100"
+                          : "opacity-0"
+                      )}
+                    />
+                    <span className="font-medium">{project.projectCode}</span>
+                    <span className="ml-2 text-gray-500 truncate">- {project.projectName}</span>
+                  </CommandItem>
+                ))}
+              </CommandGroup>
+            )}
+          </CommandList>
+        </Command>
+      </PopoverContent>
+    </Popover>
+  );
+}
+
 export function VendorFormStatusTable({
   initialData = [],
 }: VendorFormStatusTableProps) {
   const [data, setData] = React.useState<VendorFormStatus[]>(initialData);
   const [isRefreshing, setIsRefreshing] = React.useState(false);
+  const [selectedProject, setSelectedProject] = React.useState<Project | null>(null);
+  const [projects, setProjects] = React.useState<Project[]>([]);
+  const [isLoadingProjects, setIsLoadingProjects] = React.useState(false);
+
+  // 프로젝트 목록 로드
+  React.useEffect(() => {
+    const loadProjects = async () => {
+      setIsLoadingProjects(true);
+      try {
+        const projectsWithContracts = await getProjectsWithContracts();
+        setProjects(projectsWithContracts);
+        
+        // 프로젝트가 하나만 있으면 자동 선택
+        if (projectsWithContracts.length === 1) {
+          setSelectedProject(projectsWithContracts[0]);
+        }
+      } catch (error) {
+        console.error("프로젝트 목록 로드 오류:", error);
+        toast.error("프로젝트 목록을 불러오는데 실패했습니다.");
+      } finally {
+        setIsLoadingProjects(false);
+      }
+    };
+
+    loadProjects();
+  }, []);
+
+  // 프로젝트 변경 시 데이터 로드
+  React.useEffect(() => {
+    handleRefresh();
+  }, [selectedProject]);
 
   // 데이터 새로고침
   const handleRefresh = React.useCallback(async () => {
     setIsRefreshing(true);
     try {
-      const result = await getVendorFormStatus();
+      const result = await getVendorFormStatus(selectedProject?.id);
       setData(result);
-      toast.success("데이터를 새로고침했습니다.");
+      if (selectedProject) {
+        toast.success(`${selectedProject.projectCode} 데이터를 새로고침했습니다.`);
+      } else {
+        toast.success("전체 프로젝트 데이터를 새로고침했습니다.");
+      }
     } catch (error) {
       console.error("Refresh error:", error);
       toast.error("새로고침 중 오류가 발생했습니다.");
     } finally {
       setIsRefreshing(false);
     }
-  }, []);
+  }, [selectedProject]);
 
-  // 초기 데이터 로드
-  React.useEffect(() => {
-    if (initialData.length === 0) {
-      handleRefresh();
-    }
-  }, []);
-
-  // 컬럼 정의
+  // 컬럼 정의 (기존과 동일)
   const columns: ColumnDef<VendorFormStatus>[] = React.useMemo(() => [
     {
       accessorKey: "vendorName",
@@ -197,6 +335,39 @@ export function VendorFormStatusTable({
 
   return (
     <div className={cn("w-full space-y-4")}>
+      {/* 프로젝트 선택 */}
+      <Card>
+        <CardHeader>
+          <CardTitle>프로젝트 선택</CardTitle>
+        </CardHeader>
+        <CardContent>
+          <div className="flex items-center gap-4">
+            <div className="flex-1">
+              <ProjectSelectorWithContracts
+                selectedProject={selectedProject}
+                onProjectSelect={setSelectedProject}
+                projects={projects}
+                isLoading={isLoadingProjects}
+              />
+            </div>
+            <Button
+              variant="outline"
+              size="sm"
+              onClick={handleRefresh}
+              disabled={isRefreshing}
+            >
+              <RefreshCw className={cn("h-4 w-4 mr-2", isRefreshing && "animate-spin")} />
+              새로고침
+            </Button>
+          </div>
+          {selectedProject && (
+            <div className="mt-2 text-sm text-muted-foreground">
+              선택된 프로젝트: {selectedProject.projectCode} - {selectedProject.projectName}
+            </div>
+          )}
+        </CardContent>
+      </Card>
+
       {/* 요약 카드 */}
       <div className="grid gap-4 md:grid-cols-5">
         <Card>
@@ -249,33 +420,39 @@ export function VendorFormStatusTable({
       <Card>
         <CardHeader>
           <div className="flex items-center justify-between">
-            <CardTitle>벤더별 Form 입력 현황</CardTitle>
-            <Button
-              variant="outline"
-              size="sm"
-              onClick={handleRefresh}
-              disabled={isRefreshing}
-            >
-              <RefreshCw className={cn("h-4 w-4 mr-2", isRefreshing && "animate-spin")} />
-              새로고침
-            </Button>
+            <CardTitle>
+              벤더별 Form 입력 현황
+              {selectedProject && (
+                <span className="ml-2 text-sm font-normal text-muted-foreground">
+                  ({selectedProject.projectCode})
+                </span>
+              )}
+            </CardTitle>
           </div>
         </CardHeader>
         <CardContent>
-          <ClientDataTable
-            columns={columns}
-            data={data}
-            advancedFilterFields={advancedFilterFields}
-            autoSizeColumns={true}
-            compact={true}
-            maxHeight="34rem"
-            initialColumnPinning={{
-              left: ["vendorName"],
-            }}
-            defaultSorting={[
-              { id: "completionRate", desc: false }, // 완료율 낮은 순으로 정렬
-            ]}
-          />
+          {data.length > 0 ? (
+            <ClientDataTable
+              columns={columns}
+              data={data}
+              advancedFilterFields={advancedFilterFields}
+              autoSizeColumns={true}
+              compact={true}
+              maxHeight="34rem"
+              initialColumnPinning={{
+                left: ["vendorName"],
+              }}
+              defaultSorting={[
+                { id: "completionRate", desc: false }, // 완료율 낮은 순으로 정렬
+              ]}
+            />
+          ) : (
+            <div className="text-center py-8 text-muted-foreground">
+              {selectedProject 
+                ? "선택한 프로젝트에 대한 데이터가 없습니다."
+                : "데이터가 없습니다."}
+            </div>
+          )}
         </CardContent>
       </Card>
     </div>
-- 
cgit v1.2.3

