summaryrefslogtreecommitdiff
path: root/lib/procurement-rfqs/table/rfq-table.tsx
diff options
context:
space:
mode:
Diffstat (limited to 'lib/procurement-rfqs/table/rfq-table.tsx')
-rw-r--r--lib/procurement-rfqs/table/rfq-table.tsx412
1 files changed, 0 insertions, 412 deletions
diff --git a/lib/procurement-rfqs/table/rfq-table.tsx b/lib/procurement-rfqs/table/rfq-table.tsx
deleted file mode 100644
index ca976172..00000000
--- a/lib/procurement-rfqs/table/rfq-table.tsx
+++ /dev/null
@@ -1,412 +0,0 @@
-"use client"
-
-import * as React from "react"
-import { useSearchParams } from "next/navigation"
-import { Button } from "@/components/ui/button"
-import { PanelLeftClose, PanelLeftOpen } from "lucide-react"
-import type {
- DataTableAdvancedFilterField,
- DataTableRowAction,
-} from "@/types/table"
-import {
- ResizablePanelGroup,
- ResizablePanel,
- ResizableHandle,
-} from "@/components/ui/resizable"
-
-import { useDataTable } from "@/hooks/use-data-table"
-import { DataTable } from "@/components/data-table/data-table"
-import { getColumns, EditingCellState } from "./rfq-table-column"
-import { useEffect, useCallback, useRef, useMemo, useLayoutEffect } from "react"
-import { DataTableAdvancedToolbar } from "@/components/data-table/data-table-advanced-toolbar"
-import { RFQTableToolbarActions } from "./rfq-table-toolbar-actions"
-import { ProcurementRfqsView } from "@/db/schema"
-import { getPORfqs } from "../services"
-import { toast } from "sonner"
-import { updateRfqRemark } from "@/lib/procurement-rfqs/services"
-import { useTablePresets } from "@/components/data-table/use-table-presets"
-import { TablePresetManager } from "@/components/data-table/data-table-preset"
-import { Loader2 } from "lucide-react"
-import { RFQFilterSheet } from "./rfq-filter-sheet"
-import { RfqDetailTables } from "./detail-table/rfq-detail-table"
-import { cn } from "@/lib/utils"
-
-interface RFQListTableProps {
- promises: Promise<[Awaited<ReturnType<typeof getPORfqs>>]>
- className?: string;
- calculatedHeight?: string; // 계산된 높이 추가
-}
-
-export function RFQListTable({
- promises,
- className,
- calculatedHeight
-}: RFQListTableProps) {
- const searchParams = useSearchParams()
-
- // 필터 패널 상태
- const [isFilterPanelOpen, setIsFilterPanelOpen] = React.useState(false)
-
- // 선택된 RFQ 상태
- const [selectedRfq, setSelectedRfq] = React.useState<ProcurementRfqsView | null>(null)
-
- // 패널 collapse 상태
- const [isTopCollapsed, setIsTopCollapsed] = React.useState(false)
- const [panelHeight, setPanelHeight] = React.useState<number>(55)
-
- // refs
- const headerRef = React.useRef<HTMLDivElement>(null)
-
- // 고정 높이 설정을 위한 상수 (실제 측정값으로 조정 필요)
- const LAYOUT_HEADER_HEIGHT = 64 // Layout Header 높이
- const LAYOUT_FOOTER_HEIGHT = 60 // Layout Footer 높이 (있다면 실제 값)
- const LOCAL_HEADER_HEIGHT = 72 // 로컬 헤더 바 높이 (p-4 + border)
- const FILTER_PANEL_WIDTH = 400 // 필터 패널 너비
-
- // 높이 계산
- // 필터 패널 높이 - Layout Header와 Footer 사이
- const FIXED_FILTER_HEIGHT = `calc(100vh - ${LAYOUT_HEADER_HEIGHT*2}px)`
-
- console.log(calculatedHeight)
-
- // 테이블 컨텐츠 높이 - 전달받은 높이에서 로컬 헤더 제외
- const FIXED_TABLE_HEIGHT = calculatedHeight
- ? `calc(${calculatedHeight} - ${LOCAL_HEADER_HEIGHT}px)`
- : `calc(100vh - ${LAYOUT_HEADER_HEIGHT + LAYOUT_FOOTER_HEIGHT + LOCAL_HEADER_HEIGHT+76}px)` // fallback
-
- // Suspense 방식으로 데이터 처리
- const [promiseData] = React.use(promises)
- const tableData = promiseData
-
- const [rowAction, setRowAction] = React.useState<DataTableRowAction<ProcurementRfqsView> | null>(null)
- const [editingCell, setEditingCell] = React.useState<EditingCellState | null>(null)
-
- // 초기 설정 정의
- const initialSettings = React.useMemo(() => ({
- page: parseInt(searchParams.get('page') || '1'),
- perPage: parseInt(searchParams.get('perPage') || '10'),
- sort: searchParams.get('sort') ? JSON.parse(searchParams.get('sort')!) : [{ id: "updatedAt", desc: true }],
- filters: searchParams.get('filters') ? JSON.parse(searchParams.get('filters')!) : [],
- joinOperator: (searchParams.get('joinOperator') as "and" | "or") || "and",
- basicFilters: searchParams.get('basicFilters') ? JSON.parse(searchParams.get('basicFilters')!) : [],
- basicJoinOperator: (searchParams.get('basicJoinOperator') as "and" | "or") || "and",
- search: searchParams.get('search') || '',
- from: searchParams.get('from') || undefined,
- to: searchParams.get('to') || undefined,
- columnVisibility: {},
- columnOrder: [],
- pinnedColumns: { left: [], right: [] },
- groupBy: [],
- expandedRows: []
- }), [searchParams])
-
- // DB 기반 프리셋 훅 사용
- const {
- presets,
- activePresetId,
- hasUnsavedChanges,
- isLoading: presetsLoading,
- createPreset,
- applyPreset,
- updatePreset,
- deletePreset,
- setDefaultPreset,
- renamePreset,
- getCurrentSettings,
- } = useTablePresets<ProcurementRfqsView>('rfq-list-table', initialSettings)
-
- // 비고 업데이트 함수
- const updateRemark = async (rfqId: number, remark: string) => {
- try {
- const result = await updateRfqRemark(rfqId, remark);
-
- if (result.success) {
- toast.success("비고가 업데이트되었습니다");
- } else {
- toast.error(result.message || "업데이트 중 오류가 발생했습니다");
- }
- } catch (error) {
- console.error("비고 업데이트 오류:", error);
- toast.error("업데이트 중 오류가 발생했습니다");
- }
- }
-
- // 행 액션 처리
- useEffect(() => {
- if (rowAction) {
- switch (rowAction.type) {
- case "select":
- setSelectedRfq(rowAction.row.original)
- break;
- case "update":
- console.log("Update rfq:", rowAction.row.original)
- break;
- case "delete":
- console.log("Delete rfq:", rowAction.row.original)
- break;
- }
- setRowAction(null)
- }
- }, [rowAction])
-
- const columns = React.useMemo(
- () => getColumns({
- setRowAction,
- editingCell,
- setEditingCell,
- updateRemark
- }),
- [setRowAction, editingCell, setEditingCell, updateRemark]
- )
-
- // 고급 필터 필드 정의
- const advancedFilterFields: DataTableAdvancedFilterField<ProcurementRfqsView>[] = [
- {
- id: "rfqCode",
- label: "RFQ No.",
- type: "text",
- },
- {
- id: "projectCode",
- label: "프로젝트",
- type: "text",
- },
- {
- id: "itemCode",
- label: "자재그룹",
- type: "text",
- },
- {
- id: "itemName",
- label: "자재명",
- type: "text",
- },
- {
- id: "rfqSealedYn",
- label: "RFQ 밀봉여부",
- type: "text",
- },
- {
- id: "majorItemMaterialCode",
- label: "자재코드",
- type: "text",
- },
- {
- id: "rfqSendDate",
- label: "RFQ 전송일",
- type: "date",
- },
- {
- id: "dueDate",
- label: "RFQ 마감일",
- type: "date",
- },
- {
- id: "createdByUserName",
- label: "요청자",
- type: "text",
- },
- ]
-
- // 현재 설정 가져오기
- const currentSettings = useMemo(() => {
- return getCurrentSettings()
- }, [getCurrentSettings])
-
- // useDataTable 초기 상태 설정
- const initialState = useMemo(() => {
- return {
- sorting: initialSettings.sort.filter(sortItem => {
- const columnExists = columns.some(col => col.accessorKey === sortItem.id)
- return columnExists
- }) as any,
- columnVisibility: currentSettings.columnVisibility,
- columnPinning: currentSettings.pinnedColumns,
- }
- }, [currentSettings, initialSettings.sort, columns])
-
- // useDataTable 훅 설정 (PQ와 동일한 설정)
- const { table } = useDataTable({
- data: tableData?.data || [],
- columns,
- pageCount: tableData?.pageCount || 0,
- rowCount: tableData?.total || 0,
- filterFields: [], // PQ와 동일하게 빈 배열
- enablePinning: true,
- enableAdvancedFilter: true,
- initialState,
- getRowId: (originalRow) => String(originalRow.id),
- shallow: false, // PQ와 동일하게 false
- clearOnDefault: true,
- })
-
- // 조회 버튼 클릭 핸들러
- const handleSearch = () => {
- setIsFilterPanelOpen(false)
- }
-
- // Get active basic filter count (PQ와 동일한 방식)
- const getActiveBasicFilterCount = () => {
- try {
- const basicFilters = searchParams.get('basicFilters')
- return basicFilters ? JSON.parse(basicFilters).length : 0
- } catch (e) {
- return 0
- }
- }
-
- console.log(panelHeight)
-
- return (
- <div
- className={cn("flex flex-col relative", className)}
- style={{ height: calculatedHeight }}
- >
- {/* Filter Panel - 계산된 높이 적용 */}
- <div
- className={cn(
- "fixed left-0 bg-background border-r z-30 flex flex-col transition-all duration-300 ease-in-out overflow-hidden",
- isFilterPanelOpen ? "border-r shadow-lg" : "border-r-0"
- )}
- style={{
- width: isFilterPanelOpen ? `${FILTER_PANEL_WIDTH}px` : '0px',
- top: `${LAYOUT_HEADER_HEIGHT*2}px`,
- height: FIXED_FILTER_HEIGHT
- }}
- >
- {/* Filter Content */}
- <div className="h-full">
- <RFQFilterSheet
- isOpen={isFilterPanelOpen}
- onClose={() => setIsFilterPanelOpen(false)}
- onSearch={handleSearch}
- isLoading={false}
- />
- </div>
- </div>
-
- {/* Main Content */}
- <div
- className="flex flex-col transition-all duration-300 ease-in-out"
- style={{
- width: isFilterPanelOpen ? `calc(100% - ${FILTER_PANEL_WIDTH}px)` : '100%',
- marginLeft: isFilterPanelOpen ? `${FILTER_PANEL_WIDTH}px` : '0px',
- height: '100%'
- }}
- >
- {/* Header Bar - 고정 높이 */}
- <div
- ref={headerRef}
- className="flex items-center justify-between p-4 bg-background border-b"
- style={{
- height: `${LOCAL_HEADER_HEIGHT}px`,
- flexShrink: 0
- }}
- >
- <div className="flex items-center gap-3">
- <Button
- variant="outline"
- size="sm"
- type='button'
- onClick={() => setIsFilterPanelOpen(!isFilterPanelOpen)}
- className="flex items-center shadow-sm"
- >
- {isFilterPanelOpen ? <PanelLeftClose className="size-4"/> : <PanelLeftOpen className="size-4"/>}
- {getActiveBasicFilterCount() > 0 && (
- <span className="ml-2 bg-primary text-primary-foreground rounded-full px-2 py-0.5 text-xs">
- {getActiveBasicFilterCount()}
- </span>
- )}
- </Button>
- </div>
-
- {/* Right side info */}
- <div className="text-sm text-muted-foreground">
- {tableData && (
- <span>총 {tableData.total || 0}건</span>
- )}
- </div>
- </div>
-
- {/* Table Content Area - 계산된 높이 사용 */}
- <div
- className="relative bg-background"
- style={{
- height: FIXED_TABLE_HEIGHT,
- display: 'grid',
- gridTemplateRows: '1fr',
- gridTemplateColumns: '1fr'
- }}
- >
- <ResizablePanelGroup
- direction="vertical"
- className="w-full h-full"
- >
- <ResizablePanel
- defaultSize={60}
- minSize={25}
- maxSize={75}
- collapsible={false}
- onResize={(size) => {
- setPanelHeight(size)
- }}
- className="flex flex-col overflow-hidden"
- >
- {/* 상단 테이블 영역 */}
- <div className="flex-1 min-h-0 overflow-hidden">
- <DataTable
- table={table}
- // className="h-full"
- maxHeight={`${panelHeight*0.5}vh`}
- >
- <DataTableAdvancedToolbar
- table={table}
- filterFields={advancedFilterFields}
- shallow={false}
- >
- <div className="flex items-center gap-2">
- <TablePresetManager<ProcurementRfqsView>
- presets={presets}
- activePresetId={activePresetId}
- currentSettings={currentSettings}
- hasUnsavedChanges={hasUnsavedChanges}
- isLoading={presetsLoading}
- onCreatePreset={createPreset}
- onUpdatePreset={updatePreset}
- onDeletePreset={deletePreset}
- onApplyPreset={applyPreset}
- onSetDefaultPreset={setDefaultPreset}
- onRenamePreset={renamePreset}
- />
-
- <RFQTableToolbarActions
- table={table}
- localData={tableData}
- setLocalData={() => {}}
- onSuccess={() => {}}
- />
- </div>
- </DataTableAdvancedToolbar>
- </DataTable>
- </div>
- </ResizablePanel>
-
- <ResizableHandle withHandle />
-
- <ResizablePanel
- minSize={25}
- defaultSize={40}
- collapsible={false}
- className="flex flex-col overflow-hidden"
- >
- {/* 하단 상세 테이블 영역 */}
- <div className="flex-1 min-h-0 overflow-hidden bg-background">
- <RfqDetailTables selectedRfq={selectedRfq} maxHeight={`${(100-panelHeight)*0.4}vh`}/>
- </div>
- </ResizablePanel>
- </ResizablePanelGroup>
- </div>
- </div>
- </div>
- )
-} \ No newline at end of file