summaryrefslogtreecommitdiff
path: root/components/vendor-data/vendor-data-container.tsx
diff options
context:
space:
mode:
authordujinkim <dujin.kim@dtsolution.co.kr>2025-03-26 00:37:41 +0000
committerdujinkim <dujin.kim@dtsolution.co.kr>2025-03-26 00:37:41 +0000
commite0dfb55c5457aec489fc084c4567e791b4c65eb1 (patch)
tree68543a65d88f5afb3a0202925804103daa91bc6f /components/vendor-data/vendor-data-container.tsx
3/25 까지의 대표님 작업사항
Diffstat (limited to 'components/vendor-data/vendor-data-container.tsx')
-rw-r--r--components/vendor-data/vendor-data-container.tsx231
1 files changed, 231 insertions, 0 deletions
diff --git a/components/vendor-data/vendor-data-container.tsx b/components/vendor-data/vendor-data-container.tsx
new file mode 100644
index 00000000..69c22b79
--- /dev/null
+++ b/components/vendor-data/vendor-data-container.tsx
@@ -0,0 +1,231 @@
+"use client"
+
+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 { useParams, usePathname, useRouter } from "next/navigation"
+import { getFormsByContractItemId, type FormInfo } from "@/lib/forms/services"
+import { Separator } from "@/components/ui/separator"
+
+interface PackageData {
+ itemId: number
+ itemName: string
+}
+
+interface ContractData {
+ contractId: number
+ contractName: string
+ packages: PackageData[]
+}
+
+interface ProjectData {
+ projectId: number
+ projectCode: string
+ projectName: string
+ contracts: ContractData[]
+}
+
+interface VendorDataContainerProps {
+ projects: ProjectData[]
+ defaultLayout?: number[]
+ defaultCollapsed?: boolean
+ navCollapsedSize: number
+ children: React.ReactNode
+}
+
+function getTagIdFromPathname(path: string): number | 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],
+ defaultCollapsed = false,
+ navCollapsedSize,
+ children
+}: VendorDataContainerProps) {
+ const pathname = usePathname()
+ const router = useRouter()
+ const tagIdNumber = getTagIdFromPathname(pathname)
+
+ const [isCollapsed, setIsCollapsed] = React.useState(defaultCollapsed)
+ // 폼 로드 요청 추적
+ const lastRequestIdRef = React.useRef(0)
+
+ // 기본 상태
+ const [selectedProjectId, setSelectedProjectId] = React.useState(projects[0]?.projectId || 0)
+ const [selectedContractId, setSelectedContractId] = React.useState(
+ projects[0]?.contracts[0]?.contractId || 0
+ )
+ // URL에서 들어온 tagIdNumber를 우선으로 설정하기 위해 초기에 null로 두고, 뒤에서 useEffect로 세팅
+ 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]
+
+ const isTagOrFormRoute = pathname.includes("/tag/") || pathname.includes("/form/")
+ 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])
+
+ // (1) 새로고침 시 URL 파라미터(tagIdNumber) → selectedPackageId 세팅
+ // URL에 tagIdNumber가 있으면 그걸 우선으로, 아니면 기존 로직대로 '첫 번째 패키지' 사용
+ React.useEffect(() => {
+ if (!currentContract) return
+
+ if (tagIdNumber) {
+ setSelectedPackageId(tagIdNumber)
+ } else {
+ // tagIdNumber가 없으면, 현재 계약의 첫 번째 패키지로
+ if (currentContract.packages?.length) {
+ setSelectedPackageId(currentContract.packages[0].itemId)
+ } else {
+ setSelectedPackageId(null)
+ }
+ }
+ }, [tagIdNumber, currentContract])
+
+ // (2) 프로젝트 변경 시 계약 초기화
+ React.useEffect(() => {
+ if (currentProject?.contracts.length) {
+ setSelectedContractId(currentProject.contracts[0].contractId)
+ } else {
+ setSelectedContractId(0)
+ }
+ }, [currentProject])
+
+ // // (3) selectedPackageId 바뀔 때 URL도 같이 업데이트
+ // React.useEffect(() => {
+ // if (!selectedPackageId) return
+ // const basePath = pathname.includes("/partners/")
+ // ? "/partners/vendor-data/tag/"
+ // : "/vendor-data/tag/"
+
+ // router.push(`${basePath}${selectedPackageId}`)
+ // }, [selectedPackageId, router, pathname])
+
+ // (4) 패키지 ID가 정해질 때마다 폼 로딩
+ React.useEffect(() => {
+ const packageId = getTagIdFromPathname(pathname)
+
+ if (packageId) {
+ setSelectedPackageId(packageId)
+
+ // URL에서 패키지 ID를 얻었을 때 즉시 폼 로드
+ loadFormsList(packageId);
+ } else if (currentContract?.packages?.length) {
+ setSelectedPackageId(currentContract.packages[0].itemId)
+ }
+ }, [pathname, currentContract])
+
+ // 폼 로드 함수를 컴포넌트 내부에 정의하고 재사용
+ const loadFormsList = async (packageId: number) => {
+ if (!packageId) return;
+
+ setIsLoadingForms(true);
+ try {
+ const result = await getFormsByContractItemId(packageId);
+ setFormList(result.forms || []);
+ } catch (error) {
+ console.error("폼 로딩 오류:", error);
+ setFormList([]);
+ } finally {
+ setIsLoadingForms(false);
+ }
+ };
+ // 핸들러들
+ function handleSelectContract(projId: number, cId: number) {
+ setSelectedProjectId(projId)
+ setSelectedContractId(cId)
+ }
+
+ function handleSelectPackage(itemId: number) {
+ setSelectedPackageId(itemId)
+ }
+
+ function handleSelectForm(formName: string) {
+ const form = formList.find((f) => f.formName === formName)
+ if (form) {
+ setSelectedFormCode(form.formCode)
+ }
+ }
+
+ return (
+ <TooltipProvider delayDuration={0}>
+ <ResizablePanelGroup direction="horizontal" className="h-full">
+ <ResizablePanel
+ defaultSize={defaultLayout[0]}
+ collapsedSize={navCollapsedSize}
+ collapsible
+ minSize={15}
+ maxSize={25}
+ onCollapse={() => setIsCollapsed(true)}
+ onResize={() => setIsCollapsed(false)}
+ className={cn(isCollapsed && "min-w-[50px] transition-all duration-300 ease-in-out")}
+ >
+ <div
+ className={cn(
+ "flex h-[52px] items-center justify-center gap-2",
+ isCollapsed ? "h-[52px]" : "px-2"
+ )}
+ >
+ <ProjectSwitcher
+ isCollapsed={isCollapsed}
+ projects={projects}
+ selectedContractId={selectedContractId}
+ onSelectContract={handleSelectContract}
+ />
+ </div>
+ <Separator />
+ <Sidebar
+ isCollapsed={isCollapsed}
+ packages={currentContract?.packages || []}
+ selectedPackageId={selectedPackageId}
+ onSelectPackage={handleSelectPackage}
+ forms={formList}
+ selectedForm={
+ selectedFormCode
+ ? formList.find((f) => f.formCode === selectedFormCode)?.formName || null
+ : null
+ }
+ onSelectForm={handleSelectForm}
+ isLoadingForms={isLoadingForms}
+ className="hidden lg:block"
+ />
+ </ResizablePanel>
+
+ <ResizableHandle withHandle />
+
+ <ResizablePanel defaultSize={defaultLayout[1]} minSize={40}>
+ <div className="p-4 h-full overflow-auto flex flex-col">
+ <div className="flex items-center justify-between mb-4">
+ <h2 className="text-lg font-bold">Package: {currentPackageName}</h2>
+ </div>
+ {children}
+ </div>
+ </ResizablePanel>
+ </ResizablePanelGroup>
+ </TooltipProvider>
+ )
+} \ No newline at end of file