- {isLoadingEngineering ? (
+ {isLoadingForms ? (
Array.from({ length: 3 }).map((_, index) => (
-
+
))
- ) : !isPackageSelected ? (
-
- Select a package first
-
- ) : engineeringForms.length === 0 ? (
-
- No forms available
-
- ) : (
- engineeringForms.map((form) => {
- const isActive =
- currentMode === "engineering" &&
- form.formCode === selectedFormCode
+ ) : mode === "IM" ? (
+ // =========== IM 모드: 폼만 표시 ===========
+ !forms || forms.length === 0 ? (
+
+ (No forms loaded)
+
+ ) : (
+ forms.map((form) => {
+ const isFormActive = form.formCode === currentFormCode
+ const isDisabled = currentItemId === null
- return isCollapsed ? (
-
-
-
-
-
+ return isCollapsed ? (
+
+
+
+
+
+ {form.formName}
+
+
+ ) : (
+
-
- ) : (
-
- )
- })
- )}
-
-
-
-
-
-
- {isCollapsed ? "I" : "IM"}
-
-
-
- {isLoadingIM ? (
- Array.from({ length: 3 }).map((_, index) => (
-
-
-
- ))
- ) : !isPackageSelected ? (
-
- Select a package first
-
- ) : imForms.length === 0 ? (
-
- No forms available
-
+
+ )
+ })
+ )
) : (
- imForms.map((form) => {
- const isActive =
- currentMode === "im" &&
- form.formCode === selectedFormCode
+ // =========== ENG 모드: 패키지 > 폼 계층 구조 ===========
+ packages.length === 0 ? (
+
+ (No packages loaded)
+
+ ) : (
+ packages.map((pkg) => (
+
+ {isCollapsed ? (
+
+
+
+
+
+ {pkg.itemName}
+
+
+ ) : (
+ <>
+ {/* 패키지 이름 (클릭 불가능한 라벨) */}
+
+
+ {/* 폼 목록 바로 표시 */}
+
+ {!forms || forms.length === 0 ? (
+
+ No forms available
+
+ ) : (
+ forms.map((form) => {
+ const isFormPackageActive =
+ pkg.itemId === currentItemId &&
+ form.formCode === currentFormCode
- return isCollapsed ? (
-
-
-
-
-
- {form.formName}
-
-
- ) : (
-
+ )
+ })
+ )}
+
+ >
)}
- onClick={() => onIMFormClick(form.formCode)}
- >
-
- {form.formName}
-
- )
- })
+
+ ))
+ )
)}
diff --git a/components/vendor-data-plant/vendor-data-container.tsx b/components/vendor-data-plant/vendor-data-container.tsx
index 7ce831df..60ec2c94 100644
--- a/components/vendor-data-plant/vendor-data-container.tsx
+++ b/components/vendor-data-plant/vendor-data-container.tsx
@@ -4,14 +4,28 @@ import * as React from "react"
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 } from "next/navigation"
+import { usePathname, useRouter, useSearchParams } from "next/navigation"
+import { getFormsByContractItemId, type FormInfo } from "@/lib/forms/services"
import { Separator } from "@/components/ui/separator"
-import { ProjectSwitcher } from "./project-swicher"
+import { Tabs, TabsList, TabsTrigger, TabsContent } from "@/components/ui/tabs"
+import { ScrollArea } from "@/components/ui/scroll-area"
+import { Button } from "@/components/ui/button"
+import { FormInput } from "lucide-react"
+import { Skeleton } from "@/components/ui/skeleton"
+import { selectedModeAtom } from '@/atoms'
+import { useAtom } from 'jotai'
interface PackageData {
- packageCode: string
- packageName: string | null
+ itemId: number
+ itemName: string
+}
+
+interface ContractData {
+ contractId: number
+ contractName: string
+ packages: PackageData[]
}
interface ProjectData {
@@ -19,7 +33,7 @@ interface ProjectData {
projectCode: string
projectName: string
projectType: string
- packages: PackageData[]
+ contracts: ContractData[]
}
interface VendorDataContainerProps {
@@ -30,39 +44,18 @@ interface VendorDataContainerProps {
children: React.ReactNode
}
-function getInfoFromPathname(path: string | null): {
- projectCode: string | null
- packageCode: string | null
- formCode: string | null
- mode: "master" | "engineering" | "im" | null
-} {
- if (!path) return { projectCode: null, packageCode: null, formCode: null, mode: null }
-
- const segments = path.split("/").filter(Boolean)
- const vendorDataIndex = segments.indexOf("vendor-data-plant")
-
- if (vendorDataIndex === -1) {
- return { projectCode: null, packageCode: null, formCode: null, mode: null }
- }
+function getTagIdFromPathname(path: string | null): number | null {
+ if (!path) return null;
- const projectCode = segments[vendorDataIndex + 1] || null
- const packageCode = segments[vendorDataIndex + 2] || null
+ // 태그 패턴 검사 (/tag/123)
+ const tagMatch = path.match(/\/tag\/(\d+)/)
+ if (tagMatch) return parseInt(tagMatch[1], 10)
- // /eng/{formCode} 또는 /im/{formCode} 패턴 체크
- const modeSegment = segments[vendorDataIndex + 3]
- const formCode = segments[vendorDataIndex + 4] || null
+ // 폼 패턴 검사 (/form/123/...)
+ const formMatch = path.match(/\/form\/(\d+)/)
+ if (formMatch) return parseInt(formMatch[1], 10)
- let mode: "master" | "engineering" | "im" | null = null
-
- if (modeSegment === "eng") {
- mode = "engineering"
- } else if (modeSegment === "im") {
- mode = "im"
- } else if (projectCode && packageCode && !modeSegment) {
- mode = "master"
- }
-
- return { projectCode, packageCode, formCode, mode }
+ return null
}
export function VendorDataContainer({
@@ -74,106 +67,267 @@ export function VendorDataContainer({
}: VendorDataContainerProps) {
const pathname = usePathname()
const router = useRouter()
+ const searchParams = useSearchParams()
- // 상태 관리
+ const tagIdNumber = getTagIdFromPathname(pathname)
+
+ // 기본 상태
const [selectedProjectId, setSelectedProjectId] = React.useState(projects[0]?.projectId || 0)
const [isCollapsed, setIsCollapsed] = React.useState(defaultCollapsed)
- const [selectedPackageCode, setSelectedPackageCode] = React.useState
(null)
+ const [selectedContractId, setSelectedContractId] = React.useState(
+ projects[0]?.contracts[0]?.contractId || 0
+ )
+ const [selectedPackageId, setSelectedPackageId] = React.useState(null)
+ const [formList, setFormList] = React.useState([])
const [selectedFormCode, setSelectedFormCode] = React.useState(null)
- const [currentMode, setCurrentMode] = React.useState<"master" | "engineering" | "im" | null>(null)
+ const [isLoadingForms, setIsLoadingForms] = React.useState(false)
- // 현재 선택된 프로젝트
+ console.log(selectedPackageId,"selectedPackageId")
+
+
+ // 현재 선택된 프로젝트/계약/패키지
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"
+
+ const [selectedMode, setSelectedMode] = useAtom(selectedModeAtom)
+
+ // URL에서 모드 추출 (ship 프로젝트면 무조건 ENG로, 아니면 URL 또는 기본값)
+ const modeFromUrl = searchParams?.get('mode')
+ const initialMode ="ENG"
+
+ // 모드 초기화 (기존의 useState 초기값 대신)
+ 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 변경 시 상태 동기화
+ // URL에서 모드가 변경되면 상태도 업데이트 (ship 프로젝트가 아닐 때만)
React.useEffect(() => {
- const { projectCode, packageCode, formCode, mode } = getInfoFromPathname(pathname)
-
- if (projectCode && packageCode) {
- // 프로젝트 찾기
- const project = projects.find(p => p.projectCode === projectCode)
- if (project) {
- setSelectedProjectId(project.projectId)
- setSelectedPackageCode(packageCode)
+ if (!isShipProject) {
+ const modeFromUrl = searchParams?.get('mode')
+ if (modeFromUrl === "ENG" || modeFromUrl === "IM") {
+ setSelectedMode(modeFromUrl)
}
}
-
- if (formCode) {
- setSelectedFormCode(formCode)
- } else {
- setSelectedFormCode(null)
+ }, [searchParams, isShipProject])
+
+ // 프로젝트 타입이 변경될 때 모드 업데이트
+ React.useEffect(() => {
+ if (isShipProject) {
+ setSelectedMode("ENG")
+
+ // URL 모드 파라미터도 업데이트
+ const url = new URL(window.location.href);
+ url.searchParams.set('mode', 'ENG');
+ router.replace(url.pathname + url.search);
}
-
- if (mode) {
- setCurrentMode(mode)
+ }, [isShipProject, router])
+
+ // (1) 새로고침 시 URL 파라미터(tagIdNumber) → selectedPackageId 세팅
+ React.useEffect(() => {
+ if (!currentContract) return
+
+ if (tagIdNumber) {
+ setSelectedPackageId(tagIdNumber)
} else {
- setCurrentMode(null)
+ // tagIdNumber가 없으면, 현재 계약의 첫 번째 패키지로
+ if (currentContract.packages?.length) {
+ setSelectedPackageId(currentContract.packages[0].itemId)
+ } else {
+ setSelectedPackageId(null)
+ }
}
- }, [pathname, projects])
-
- // 베이스 URL 생성 헬퍼
- const getBaseUrl = () => {
- const segments = pathname?.split("/").filter(Boolean) || []
- const vendorDataIndex = segments.indexOf("vendor-data-plant")
- if (vendorDataIndex === -1) return ""
- return "/" + segments.slice(0, vendorDataIndex + 1).join("/")
- }
+ }, [tagIdNumber, currentContract])
- // 프로젝트 및 패키지 선택 핸들러
- const handleSelectPackage = (projectId: number, packageCode: string) => {
- const project = projects.find(p => p.projectId === projectId)
- if (!project) return
-
- setSelectedProjectId(projectId)
- setSelectedPackageCode(packageCode)
- setSelectedFormCode(null)
- setCurrentMode("master")
+ // (2) 프로젝트 변경 시 계약 초기화
+ // React.useEffect(() => {
+ // if (currentProject?.contracts.length) {
+ // setSelectedContractId(currentProject.contracts[0].contractId)
+ // } else {
+ // setSelectedContractId(0)
+ // }
+ // }, [currentProject])
+
+ // (3) 패키지 ID와 모드가 변경될 때마다 폼 로딩
+ React.useEffect(() => {
+ const packageId = getTagIdFromPathname(pathname)
- const baseUrl = getBaseUrl()
- router.push(`${baseUrl}/${project.projectCode}/${packageCode}`)
- }
+ if (packageId) {
+ setSelectedPackageId(packageId)
+
+ // URL에서 패키지 ID를 얻었을 때 즉시 폼 로드
+ loadFormsList(packageId, selectedMode);
+ } else if (currentContract?.packages?.length) {
+ const firstPackageId = currentContract.packages[0].itemId;
+ setSelectedPackageId(firstPackageId);
+ loadFormsList(firstPackageId, selectedMode);
+ }
+ }, [pathname, currentContract, selectedMode])
- // Master Tag List 클릭 핸들러
- const handleMasterTagListClick = () => {
- if (!selectedPackageCode) return
+ // 모드에 따른 폼 로드 함수
+ const loadFormsList = async (packageId: number, mode: "IM" | "ENG") => {
+ if (!packageId) return;
- const project = projects.find(p => p.projectId === selectedProjectId)
- if (!project) 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)
- setCurrentMode("master")
+ // 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-plant") + 1).join("/")
+ router.push(`/${baseSegments}/form/0/${firstForm.formCode}/${projId}/${cId}?mode=ENG`)
+ } else {
+ // 폼이 없는 경우에도 ENG 모드로 설정
+ setSelectedMode("ENG")
+ setSelectedFormCode(null)
+
+ const baseSegments = pathname?.split("/").filter(Boolean).slice(0, pathname.split("/").filter(Boolean).indexOf("vendor-data-plant") + 1).join("/")
+ router.push(`/${baseSegments}/form/0/0/${projId}/${cId}?mode=ENG`)
+ }
+ } catch (error) {
+ console.error("폼 로딩 오류:", error)
+ setFormList([])
+ setSelectedFormCode(null)
+
+ // 오류 발생 시에도 ENG 모드로 설정
+ setSelectedMode("ENG")
+ } finally {
+ setIsLoadingForms(false)
+ }
+ } else {
+ // 패키지가 없는 경우
+ setSelectedPackageId(null)
+ setFormList([])
setSelectedFormCode(null)
-
- const baseUrl = getBaseUrl()
- router.push(`${baseUrl}/${project.projectCode}/${selectedPackageCode}`)
+ setSelectedMode("ENG")
}
-
- // Engineering 폼 클릭 핸들러
- const handleEngineeringFormClick = (formCode: string) => {
- if (!selectedPackageCode) return
-
- const project = projects.find(p => p.projectId === selectedProjectId)
- if (!project) return
-
- setCurrentMode("engineering")
- setSelectedFormCode(formCode)
-
- const baseUrl = getBaseUrl()
- router.push(`${baseUrl}/${project.projectCode}/${selectedPackageCode}/eng/${formCode}`)
+}
+
+ function handleSelectPackage(itemId: number) {
+ setSelectedPackageId(itemId)
}
+
+ function handleSelectForm(formName: string) {
+ 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;
- // IM 폼 클릭 핸들러
- const handleIMFormClick = (formCode: string) => {
- if (!selectedPackageCode) return
-
- const project = projects.find(p => p.projectId === selectedProjectId)
- if (!project) return
-
- setCurrentMode("im")
- setSelectedFormCode(formCode)
+ setSelectedMode(mode);
+
+ // 모드가 변경될 때 자동 네비게이션
+ if (currentContract?.packages?.length) {
+ const firstPackageId = currentContract.packages[0].itemId;
- const baseUrl = getBaseUrl()
- router.push(`${baseUrl}/${project.projectCode}/${selectedPackageCode}/im/${formCode}`)
+ if (mode === "IM") {
+ // IM 모드: 첫 번째 패키지로 이동
+ const baseSegments = pathname?.split("/").filter(Boolean).slice(0, pathname.split("/").filter(Boolean).indexOf("vendor-data-plant") + 1).join("/");
+ router.push(`/${baseSegments}/tag/${firstPackageId}?mode=${mode}`);
+ } else {
+ // ENG 모드: 폼 목록을 먼저 로드
+ setIsLoadingForms(true);
+ try {
+ const result = await getFormsByContractItemId(firstPackageId, mode);
+ setFormList(result.forms || []);
+
+ // 폼이 있으면 첫 번째 폼으로 이동
+ 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-plant") + 1).join("/");
+ router.push(`/${baseSegments}/form/0/${firstForm.formCode}/${selectedProjectId}/${selectedContractId}?mode=${mode}`);
+ } else {
+ // 폼이 없으면 모드만 변경
+ const baseSegments = pathname?.split("/").filter(Boolean).slice(0, pathname.split("/").filter(Boolean).indexOf("vendor-data-plant") + 1).join("/");
+ router.push(`/${baseSegments}/form/0/0/${selectedProjectId}/${selectedContractId}?mode=${mode}`);
+ }
+ } 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 (
@@ -197,28 +351,151 @@ export function VendorDataContainer({