diff options
| author | dujinkim <dujin.kim@dtsolution.co.kr> | 2025-08-21 06:57:36 +0000 |
|---|---|---|
| committer | dujinkim <dujin.kim@dtsolution.co.kr> | 2025-08-21 06:57:36 +0000 |
| commit | 02b1cf005cf3e1df64183d20ba42930eb2767a9f (patch) | |
| tree | e932c54d5260b0e6fda2b46be2a6ba1c3ee30434 /components/vendor-data | |
| parent | d78378ecd7ceede1429359f8058c7a99ac34b1b7 (diff) | |
(대표님, 최겸) 설계메뉴추가, 작업사항 업데이트
설계메뉴 - 문서관리
설계메뉴 - 벤더 데이터
gtc 메뉴 업데이트
정보시스템 - 메뉴리스트 및 정보 업데이트
파일 라우트 업데이트
엑셀임포트 개선
기본계약 개선
벤더 가입과정 변경 및 개선
벤더 기본정보 - pq
돌체 오류 수정 및 개선
벤더 로그인 과정 이메일 오류 수정
Diffstat (limited to 'components/vendor-data')
| -rw-r--r-- | components/vendor-data/vendor-data-container.tsx | 333 |
1 files changed, 252 insertions, 81 deletions
diff --git a/components/vendor-data/vendor-data-container.tsx b/components/vendor-data/vendor-data-container.tsx index 3974b791..207abcf1 100644 --- a/components/vendor-data/vendor-data-container.tsx +++ b/components/vendor-data/vendor-data-container.tsx @@ -1,42 +1,63 @@ "use client" import * as React from "react" -import { usePathname, useRouter, useSearchParams, useParams } from "next/navigation" -import { useAtom } from "jotai" -import { selectedModeAtom } from "@/atoms" -import { Sidebar } from "./sidebar" -import { ProjectSwitcher } from "./project-swicher" +import { TooltipProvider } from "@/components/ui/tooltip" import { ResizableHandle, ResizablePanel, ResizablePanelGroup } from "@/components/ui/resizable" import { cn } from "@/lib/utils" +import { ProjectSwitcher } from "./project-swicher" +import { Sidebar } from "./sidebar" +import { usePathname, useRouter, useSearchParams } from "next/navigation" +import { getFormsByContractItemId, type FormInfo } from "@/lib/forms/services" import { Separator } from "@/components/ui/separator" import { Tabs, TabsList, TabsTrigger, TabsContent } from "@/components/ui/tabs" +import { ScrollArea } from "@/components/ui/scroll-area" import { Button } from "@/components/ui/button" -import { TooltipProvider } from "@/components/ui/tooltip" +import { FormInput } from "lucide-react" +import { Skeleton } from "@/components/ui/skeleton" +import { selectedModeAtom } from '@/atoms' +import { useAtom } from 'jotai' interface PackageData { itemId: number itemName: string } +interface ContractData { + contractId: number + contractName: string + packages: PackageData[] +} + +interface ProjectData { + projectId: number + projectCode: string + projectName: string + projectType: string + contracts: ContractData[] +} + interface VendorDataContainerProps { - projects: { - projectId: number - projectCode: string - projectName: string - projectType: string - contracts: { - contractId: number - contractNo: string - contractName: string - packages: PackageData[] - }[] - }[] + projects: ProjectData[] defaultLayout?: number[] defaultCollapsed?: boolean - navCollapsedSize?: number + navCollapsedSize: number children: React.ReactNode } +function getTagIdFromPathname(path: string | null): number | null { + if (!path) return null; + + // 태그 패턴 검사 (/tag/123) + const tagMatch = path.match(/\/tag\/(\d+)/) + if (tagMatch) return parseInt(tagMatch[1], 10) + + // 폼 패턴 검사 (/form/123/...) + const formMatch = path.match(/\/form\/(\d+)/) + if (formMatch) return parseInt(formMatch[1], 10) + + return null +} + export function VendorDataContainer({ projects, defaultLayout = [20, 80], @@ -47,8 +68,8 @@ export function VendorDataContainer({ const pathname = usePathname() const router = useRouter() const searchParams = useSearchParams() - const params = useParams() - const currentLng = params?.lng as string || 'en' + + const tagIdNumber = getTagIdFromPathname(pathname) // 기본 상태 const [selectedProjectId, setSelectedProjectId] = React.useState(projects[0]?.projectId || 0) @@ -57,12 +78,15 @@ export function VendorDataContainer({ projects[0]?.contracts[0]?.contractId || 0 ) const [selectedPackageId, setSelectedPackageId] = React.useState<number | null>(null) + const [formList, setFormList] = React.useState<FormInfo[]>([]) + const [selectedFormCode, setSelectedFormCode] = React.useState<string | null>(null) + const [isLoadingForms, setIsLoadingForms] = React.useState(false) // 현재 선택된 프로젝트/계약/패키지 const currentProject = projects.find((p) => p.projectId === selectedProjectId) ?? projects[0] const currentContract = currentProject?.contracts.find((c) => c.contractId === selectedContractId) ?? currentProject?.contracts[0] - + // 프로젝트 타입 확인 - ship인 경우 항상 ENG 모드 const isShipProject = currentProject?.projectType === "ship" @@ -78,6 +102,30 @@ export function VendorDataContainer({ React.useEffect(() => { setSelectedMode(initialMode as "IM" | "ENG") }, [initialMode, setSelectedMode]) + + const isTagOrFormRoute = pathname ? (pathname.includes("/tag/") || pathname.includes("/form/")) : false + const currentPackageName = isTagOrFormRoute + ? currentContract?.packages.find((pkg) => pkg.itemId === selectedPackageId)?.itemName || "None" + : "None" + + // 폼 목록에서 고유한 폼 이름만 추출 + const formNames = React.useMemo(() => { + return [...new Set(formList.map((form) => form.formName))] + }, [formList]) + + // URL에서 현재 폼 코드 추출 + const getCurrentFormCode = (path: string): string | null => { + const segments = path.split("/").filter(Boolean) + const formIndex = segments.indexOf("form") + if (formIndex !== -1 && segments[formIndex + 2]) { + return segments[formIndex + 2] + } + return null + } + + const currentFormCode = React.useMemo(() => { + return pathname ? getCurrentFormCode(pathname) : null + }, [pathname]) // URL에서 모드가 변경되면 상태도 업데이트 (ship 프로젝트가 아닐 때만) React.useEffect(() => { @@ -101,89 +149,184 @@ export function VendorDataContainer({ } }, [isShipProject, router]) - // (1) 프로젝트 변경 시 계약 초기화 + // (1) 새로고침 시 URL 파라미터(tagIdNumber) → selectedPackageId 세팅 React.useEffect(() => { - if (currentProject?.contracts.length) { - setSelectedContractId(currentProject.contracts[0].contractId) + if (!currentContract) return + + if (tagIdNumber) { + setSelectedPackageId(tagIdNumber) } else { - setSelectedContractId(0) + // tagIdNumber가 없으면, 현재 계약의 첫 번째 패키지로 + if (currentContract.packages?.length) { + setSelectedPackageId(currentContract.packages[0].itemId) + } else { + setSelectedPackageId(null) + } } - }, [currentProject]) + }, [tagIdNumber, currentContract]) - // 핸들러들 - function handleSelectContract(projId: number, cId: number) { - setSelectedProjectId(projId) - setSelectedContractId(cId) - } + // (2) 프로젝트 변경 시 계약 초기화 + // React.useEffect(() => { + // if (currentProject?.contracts.length) { + // setSelectedContractId(currentProject.contracts[0].contractId) + // } else { + // setSelectedContractId(0) + // } + // }, [currentProject]) - function handleSelectPackage(itemId: number) { - setSelectedPackageId(itemId) + // (3) 패키지 ID와 모드가 변경될 때마다 폼 로딩 + React.useEffect(() => { + const packageId = getTagIdFromPathname(pathname) - // partners와 동일하게: 패키지 선택 시 해당 페이지로 이동 - if (itemId && pathname) { - // 더 안전한 URL 생성 로직 - let baseSegments: string; - const vendorDataIndex = pathname.split("/").filter(Boolean).indexOf("vendor-data"); + if (packageId) { + setSelectedPackageId(packageId) - if (vendorDataIndex !== -1) { - baseSegments = pathname.split("/").filter(Boolean).slice(0, vendorDataIndex + 1).join("/"); + // URL에서 패키지 ID를 얻었을 때 즉시 폼 로드 + loadFormsList(packageId, selectedMode); + } else if (currentContract?.packages?.length) { + const firstPackageId = currentContract.packages[0].itemId; + setSelectedPackageId(firstPackageId); + loadFormsList(firstPackageId, selectedMode); + } + }, [pathname, currentContract, selectedMode]) + + // 모드에 따른 폼 로드 함수 + const loadFormsList = async (packageId: number, mode: "IM" | "ENG") => { + if (!packageId) return; + + setIsLoadingForms(true); + try { + const result = await getFormsByContractItemId(packageId, mode); + setFormList(result.forms || []); + } catch (error) { + console.error(`폼 로딩 오류 (${mode} 모드):`, error); + setFormList([]); + } finally { + setIsLoadingForms(false); + } + }; + + // 핸들러들 +// 수정된 handleSelectContract 함수 +async function handleSelectContract(projId: number, cId: number) { + setSelectedProjectId(projId) + setSelectedContractId(cId) + + // 선택된 계약의 첫 번째 패키지 찾기 + const selectedProject = projects.find(p => p.projectId === projId) + const selectedContract = selectedProject?.contracts.find(c => c.contractId === cId) + + if (selectedContract?.packages?.length) { + const firstPackageId = selectedContract.packages[0].itemId + setSelectedPackageId(firstPackageId) + + // ENG 모드로 폼 목록 로드 + setIsLoadingForms(true) + try { + const result = await getFormsByContractItemId(firstPackageId, "ENG") + setFormList(result.forms || []) + + // 첫 번째 폼이 있으면 자동 선택 및 네비게이션 + if (result.forms && result.forms.length > 0) { + const firstForm = result.forms[0] + setSelectedFormCode(firstForm.formCode) + + // ENG 모드로 설정 + setSelectedMode("ENG") + + // 첫 번째 폼으로 네비게이션 + const baseSegments = pathname?.split("/").filter(Boolean).slice(0, pathname.split("/").filter(Boolean).indexOf("vendor-data") + 1).join("/") + router.push(`/${baseSegments}/form/0/${firstForm.formCode}/${projId}/${cId}?mode=ENG`) } else { - // vendor-data가 없으면 기본 경로 사용 - baseSegments = `${currentLng}/evcp/vendor-data`; + // 폼이 없는 경우에도 ENG 모드로 설정 + setSelectedMode("ENG") + setSelectedFormCode(null) + + const baseSegments = pathname?.split("/").filter(Boolean).slice(0, pathname.split("/").filter(Boolean).indexOf("vendor-data") + 1).join("/") + router.push(`/${baseSegments}/form/0/0/${projId}/${cId}?mode=ENG`) } + } catch (error) { + console.error("폼 로딩 오류:", error) + setFormList([]) + setSelectedFormCode(null) - const targetUrl = `/${baseSegments}/tag/${itemId}?mode=${selectedMode}`; - router.push(targetUrl); + // 오류 발생 시에도 ENG 모드로 설정 + setSelectedMode("ENG") + } finally { + setIsLoadingForms(false) } + } else { + // 패키지가 없는 경우 + setSelectedPackageId(null) + setFormList([]) + setSelectedFormCode(null) + setSelectedMode("ENG") + } +} + + function handleSelectPackage(itemId: number) { + setSelectedPackageId(itemId) } function handleSelectForm(formName: string) { - // partners와 동일하게: 폼 선택 시 해당 페이지로 이동 - if (selectedPackageId && pathname) { - // 더 안전한 URL 생성 로직 - let baseSegments: string; - const vendorDataIndex = pathname.split("/").filter(Boolean).indexOf("vendor-data"); - - if (vendorDataIndex !== -1) { - baseSegments = pathname.split("/").filter(Boolean).slice(0, vendorDataIndex + 1).join("/"); - } else { - // vendor-data가 없으면 기본 경로 사용 - baseSegments = `${currentLng}/evcp/vendor-data`; - } - - const targetUrl = `/${baseSegments}/form/${selectedPackageId}/${formName}/${selectedProjectId}/${selectedContractId}?mode=${selectedMode}`; - router.push(targetUrl); + const form = formList.find((f) => f.formName === formName) + if (form) { + setSelectedFormCode(form.formCode) } } // 모드 변경 핸들러 - const handleModeChange = async (mode: "IM" | "ENG") => { - // ship 프로젝트인 경우 모드 변경 금지 - if (isShipProject && mode !== "ENG") return; - - setSelectedMode(mode); +// 모드 변경 핸들러 +const handleModeChange = async (mode: "IM" | "ENG") => { + // ship 프로젝트인 경우 모드 변경 금지 + if (isShipProject && mode !== "ENG") return; + + setSelectedMode(mode); + + // 모드가 변경될 때 자동 네비게이션 + if (currentContract?.packages?.length) { + const firstPackageId = currentContract.packages[0].itemId; - // 모드가 변경될 때 자동 네비게이션 - if (currentContract?.packages?.length) { - const firstPackageId = currentContract.packages[0].itemId; - - if (pathname) { - // 더 안전한 URL 생성 로직 - let baseSegments: string; - const vendorDataIndex = pathname.split("/").filter(Boolean).indexOf("vendor-data"); + if (mode === "IM") { + // IM 모드: 첫 번째 패키지로 이동 + const baseSegments = pathname?.split("/").filter(Boolean).slice(0, pathname.split("/").filter(Boolean).indexOf("vendor-data") + 1).join("/"); + router.push(`/${baseSegments}/tag/${firstPackageId}?mode=${mode}`); + } else { + // ENG 모드: 폼 목록을 먼저 로드 + setIsLoadingForms(true); + try { + const result = await getFormsByContractItemId(firstPackageId, mode); + setFormList(result.forms || []); - if (vendorDataIndex !== -1) { - baseSegments = pathname.split("/").filter(Boolean).slice(0, vendorDataIndex + 1).join("/"); + // 폼이 있으면 첫 번째 폼으로 이동 + if (result.forms && result.forms.length > 0) { + const firstForm = result.forms[0]; + setSelectedFormCode(firstForm.formCode); + + const baseSegments = pathname?.split("/").filter(Boolean).slice(0, pathname.split("/").filter(Boolean).indexOf("vendor-data") + 1).join("/"); + router.push(`/${baseSegments}/form/0/${firstForm.formCode}/${selectedProjectId}/${selectedContractId}?mode=${mode}`); } else { - // vendor-data가 없으면 기본 경로 사용 - baseSegments = `${currentLng}/evcp/vendor-data`; + // 폼이 없으면 모드만 변경 + const baseSegments = pathname?.split("/").filter(Boolean).slice(0, pathname.split("/").filter(Boolean).indexOf("vendor-data") + 1).join("/"); + router.push(`/${baseSegments}/form/0/0/${selectedProjectId}/${selectedContractId}?mode=${mode}`); } - - const targetUrl = `/${baseSegments}/tag/${firstPackageId}?mode=${mode}`; - router.push(targetUrl); + } catch (error) { + console.error(`폼 로딩 오류 (${mode} 모드):`, error); + // 오류 발생 시 모드만 변경 + const url = new URL(window.location.href); + url.searchParams.set('mode', mode); + router.replace(url.pathname + url.search); + } finally { + setIsLoadingForms(false); } } + } else { + // 패키지가 없는 경우, 모드만 변경 + const url = new URL(window.location.href); + url.searchParams.set('mode', mode); + router.replace(url.pathname + url.search); } +}; return ( <TooltipProvider delayDuration={0}> @@ -224,7 +367,14 @@ export function VendorDataContainer({ selectedProjectId={selectedProjectId} selectedContractId={selectedContractId} onSelectPackage={handleSelectPackage} + forms={formList} + selectedForm={ + selectedFormCode + ? formList.find((f) => f.formCode === selectedFormCode)?.formName || null + : null + } onSelectForm={handleSelectForm} + isLoadingForms={isLoadingForms} mode="ENG" className="hidden lg:block" /> @@ -250,7 +400,14 @@ export function VendorDataContainer({ selectedContractId={selectedContractId} selectedProjectId={selectedProjectId} onSelectPackage={handleSelectPackage} + forms={formList} + selectedForm={ + selectedFormCode + ? formList.find((f) => f.formCode === selectedFormCode)?.formName || null + : null + } onSelectForm={handleSelectForm} + isLoadingForms={isLoadingForms} mode="IM" className="hidden lg:block" /> @@ -264,7 +421,14 @@ export function VendorDataContainer({ selectedContractId={selectedContractId} selectedProjectId={selectedProjectId} onSelectPackage={handleSelectPackage} + forms={formList} + selectedForm={ + selectedFormCode + ? formList.find((f) => f.formCode === selectedFormCode)?.formName || null + : null + } onSelectForm={handleSelectForm} + isLoadingForms={isLoadingForms} mode="ENG" className="hidden lg:block" /> @@ -303,7 +467,14 @@ export function VendorDataContainer({ selectedProjectId={selectedProjectId} selectedContractId={selectedContractId} onSelectPackage={handleSelectPackage} + forms={formList} + selectedForm={ + selectedFormCode + ? formList.find((f) => f.formCode === selectedFormCode)?.formName || null + : null + } onSelectForm={handleSelectForm} + isLoadingForms={isLoadingForms} mode={isShipProject ? "ENG" : selectedMode} className="hidden lg:block" /> @@ -319,7 +490,7 @@ export function VendorDataContainer({ <h2 className="text-lg font-bold"> {isShipProject || selectedMode === "ENG" ? "Engineering Mode" - : `Package: ${currentContract?.packages.find((pkg) => pkg.itemId === selectedPackageId)?.itemName || "None"}`} + : `Package: ${currentPackageName}`} </h2> </div> {children} |
