summaryrefslogtreecommitdiff
path: root/components/vendor-data/sidebar.tsx
diff options
context:
space:
mode:
authorjoonhoekim <26rote@gmail.com>2025-03-25 15:55:45 +0900
committerjoonhoekim <26rote@gmail.com>2025-03-25 15:55:45 +0900
commit1a2241c40e10193c5ff7008a7b7b36cc1d855d96 (patch)
tree8a5587f10ca55b162d7e3254cb088b323a34c41b /components/vendor-data/sidebar.tsx
initial commit
Diffstat (limited to 'components/vendor-data/sidebar.tsx')
-rw-r--r--components/vendor-data/sidebar.tsx235
1 files changed, 235 insertions, 0 deletions
diff --git a/components/vendor-data/sidebar.tsx b/components/vendor-data/sidebar.tsx
new file mode 100644
index 00000000..b9e14b65
--- /dev/null
+++ b/components/vendor-data/sidebar.tsx
@@ -0,0 +1,235 @@
+"use client"
+
+import * as React from "react"
+import { cn } from "@/lib/utils"
+import { Button } from "@/components/ui/button"
+import { ScrollArea } from "@/components/ui/scroll-area"
+import { Separator } from "@/components/ui/separator"
+import {
+ Tooltip,
+ TooltipTrigger,
+ TooltipContent,
+} from "@/components/ui/tooltip"
+import { Package2, FormInput } from "lucide-react"
+import { useRouter, usePathname } from "next/navigation"
+import { Skeleton } from "@/components/ui/skeleton"
+import { type FormInfo } from "@/lib/forms/services"
+
+interface PackageData {
+ itemId: number
+ itemName: string
+}
+
+interface SidebarProps extends React.HTMLAttributes<HTMLDivElement> {
+ isCollapsed: boolean
+ packages: PackageData[]
+ selectedPackageId: number | null
+ onSelectPackage: (itemId: number) => void
+ forms: FormInfo[]
+ selectedForm: string | null
+ onSelectForm: (formName: string) => void
+ isLoadingForms?: boolean
+}
+
+export function Sidebar({
+ className,
+ isCollapsed,
+ packages,
+ selectedPackageId,
+ onSelectPackage,
+ forms,
+ selectedForm,
+ onSelectForm,
+ isLoadingForms = false,
+}: SidebarProps) {
+ const router = useRouter()
+ const pathname = usePathname()
+
+ /**
+ * ---------------------------
+ * 1) URL에서 현재 패키지 / 폼 코드 추출
+ * ---------------------------
+ */
+ const segments = pathname.split("/").filter(Boolean)
+ // 예) "/partners/vendor-data/tag/123" => ["partners","vendor-data","tag","123"]
+
+ let currentItemId: number | null = null
+ let currentFormCode: string | null = null
+
+ const tagIndex = segments.indexOf("tag")
+ if (tagIndex !== -1 && segments[tagIndex + 1]) {
+ // tag 뒤에 오는 값이 패키지 itemId
+ currentItemId = parseInt(segments[tagIndex + 1], 10)
+ }
+
+ const formIndex = segments.indexOf("form")
+ if (formIndex !== -1) {
+ // form 뒤 첫 파라미터 => itemId, 그 다음 파라미터 => formCode
+ const itemSegment = segments[formIndex + 1]
+ const codeSegment = segments[formIndex + 2]
+
+ if (itemSegment) {
+ currentItemId = parseInt(itemSegment, 10)
+ }
+ if (codeSegment) {
+ currentFormCode = codeSegment
+ }
+ }
+
+ /**
+ * ---------------------------
+ * 2) 패키지 클릭 핸들러
+ * ---------------------------
+ */
+ const handlePackageClick = (itemId: number) => {
+ // 상위 컴포넌트 상태 업데이트
+ onSelectPackage(itemId)
+
+ // 해당 태그 페이지로 라우팅
+ // 예: /vendor-data/tag/123
+ const baseSegments = segments.slice(0, segments.indexOf("vendor-data") + 1).join("/")
+ router.push(`/${baseSegments}/tag/${itemId}`)
+ }
+
+ /**
+ * ---------------------------
+ * 3) 폼 클릭 핸들러
+ * ---------------------------
+ */
+ const handleFormClick = (form: FormInfo) => {
+ // 패키지가 선택되어 있을 때만 동작
+ if (selectedPackageId === null) return
+
+ // 상위 컴포넌트 상태 업데이트
+ onSelectForm(form.formName)
+
+ // 해당 폼 페이지로 라우팅
+ // 예: /vendor-data/form/[packageId]/[formCode]
+
+ const baseSegments = segments.slice(0, segments.indexOf("vendor-data") + 1).join("/")
+
+ router.push(`/${baseSegments}/form/${selectedPackageId}/${form.formCode}`)
+ }
+
+ return (
+ <div className={cn("pb-12", className)}>
+ <div className="space-y-4 py-4">
+ {/* ---------- 패키지(Items) 목록 ---------- */}
+ <div className="py-1">
+ <h2 className="relative px-7 text-lg font-semibold tracking-tight">
+ {isCollapsed ? "P" : "Package Lists"}
+ </h2>
+ <ScrollArea className="h-[150px] px-1">
+ <div className="space-y-1 p-2">
+ {packages.map((pkg) => {
+ // URL 기준으로 active 여부 판단
+ const isActive = pkg.itemId === currentItemId
+
+ return (
+ <div key={pkg.itemId}>
+ {isCollapsed ? (
+ <Tooltip delayDuration={0}>
+ <TooltipTrigger asChild>
+ <Button
+ variant="ghost"
+ className={cn(
+ "w-full justify-start font-normal",
+ isActive && "bg-accent text-accent-foreground"
+ )}
+ onClick={() => handlePackageClick(pkg.itemId)}
+ >
+ <Package2 className="mr-2 h-4 w-4" />
+ </Button>
+ </TooltipTrigger>
+ <TooltipContent side="right">
+ {pkg.itemName}
+ </TooltipContent>
+ </Tooltip>
+ ) : (
+ <Button
+ variant="ghost"
+ className={cn(
+ "w-full justify-start font-normal",
+ isActive && "bg-accent text-accent-foreground"
+ )}
+ onClick={() => handlePackageClick(pkg.itemId)}
+ >
+ <Package2 className="mr-2 h-4 w-4" />
+ {pkg.itemName}
+ </Button>
+ )}
+ </div>
+ )
+ })}
+ </div>
+ </ScrollArea>
+ </div>
+
+ <Separator />
+
+ {/* ---------- 폼 목록 ---------- */}
+ <div className="py-1">
+ <h2 className="relative px-7 text-lg font-semibold tracking-tight">
+ {isCollapsed ? "F" : "Form Lists"}
+ </h2>
+ <ScrollArea className="h-[300px] px-1">
+ <div className="space-y-1 p-2">
+ {isLoadingForms ? (
+ // 로딩 중 스켈레톤 UI 표시
+ Array.from({ length: 3 }).map((_, index) => (
+ <div key={`form-skeleton-${index}`} className="px-2 py-1.5">
+ <Skeleton className="h-8 w-full" />
+ </div>
+ ))
+ ) : forms.length === 0 ? (
+ <p className="text-sm text-muted-foreground px-2">
+ (No forms loaded)
+ </p>
+ ) : (
+ forms.map((form) => {
+ // URL 기준으로 active 여부 판단
+ const isActive = form.formCode === currentFormCode
+
+ return isCollapsed ? (
+ <Tooltip key={form.formCode} delayDuration={0}>
+ <TooltipTrigger asChild>
+ <Button
+ variant="ghost"
+ className={cn(
+ "w-full justify-start font-normal",
+ isActive && "bg-accent text-accent-foreground"
+ )}
+ onClick={() => handleFormClick(form)}
+ disabled={currentItemId === null}
+ >
+ <FormInput className="mr-2 h-4 w-4" />
+ </Button>
+ </TooltipTrigger>
+ <TooltipContent side="right">
+ {form.formName}
+ </TooltipContent>
+ </Tooltip>
+ ) : (
+ <Button
+ key={form.formCode}
+ variant="ghost"
+ className={cn(
+ "w-full justify-start font-normal",
+ isActive && "bg-accent text-accent-foreground"
+ )}
+ onClick={() => handleFormClick(form)}
+ disabled={currentItemId === null}
+ >
+ <FormInput className="mr-2 h-4 w-4" />
+ {form.formName}
+ </Button>
+ )
+ })
+ )}
+ </div>
+ </ScrollArea>
+ </div>
+ </div>
+ </div>
+ )
+} \ No newline at end of file