diff options
Diffstat (limited to 'lib/avl/table/vendor-pool-table.tsx')
| -rw-r--r-- | lib/avl/table/vendor-pool-table.tsx | 301 |
1 files changed, 301 insertions, 0 deletions
diff --git a/lib/avl/table/vendor-pool-table.tsx b/lib/avl/table/vendor-pool-table.tsx new file mode 100644 index 00000000..7ad9eb56 --- /dev/null +++ b/lib/avl/table/vendor-pool-table.tsx @@ -0,0 +1,301 @@ +"use client" + +import * as React from "react" +import { useReactTable, getCoreRowModel, getPaginationRowModel, getSortedRowModel, getFilteredRowModel } from "@tanstack/react-table" +import { forwardRef, useImperativeHandle } from "react" +import { DataTable } from "@/components/data-table/data-table" +import { Button } from "@/components/ui/button" +import { Checkbox } from "@/components/ui/checkbox" +import { Input } from "@/components/ui/input" +import { Search } from "lucide-react" +import { getVendorPools } from "../../vendor-pool/service" +import { GetVendorPoolSchema } from "../../vendor-pool/validations" +import { VendorPool } from "../../vendor-pool/types" +import { vendorPoolColumns } from "./vendor-pool-table-columns" + +// Vendor Pool 데이터 타입 (실제 VendorPool 타입 사용) +export type VendorPoolItem = VendorPool + +// ref를 통해 외부에서 접근할 수 있는 메소드들 +export interface VendorPoolTableRef { + getSelectedIds: () => number[] +} + +interface VendorPoolTableProps { + onSelectionChange?: (count: number) => void + resetCounter?: number + reloadTrigger?: number +} + +// 실제 데이터는 API에서 가져옴 + +export const VendorPoolTable = forwardRef<VendorPoolTableRef, VendorPoolTableProps>(({ + onSelectionChange, + resetCounter, + reloadTrigger +}, ref) => { + const [data, setData] = React.useState<VendorPoolItem[]>([]) + const [loading, setLoading] = React.useState(false) + const [pageCount, setPageCount] = React.useState(0) + + // 검색 상태 + const [searchText, setSearchText] = React.useState("") + const [showAll, setShowAll] = React.useState(false) + + // 페이지네이션 상태 + const [pagination, setPagination] = React.useState({ + pageIndex: 0, + pageSize: 10, + }) + + // 데이터 로드 함수 + const loadData = React.useCallback(async (searchParams: Partial<GetVendorPoolSchema> = {}) => { + try { + setLoading(true) + + const params = { + page: searchParams.page ?? 1, + perPage: searchParams.perPage ?? 10, + sort: searchParams.sort ?? [{ id: "registrationDate", desc: true }], + flags: [], + search: searchText || "", + constructionSector: undefined, + shipType: undefined, + htDivision: undefined, + designCategoryCode: undefined, + designCategory: undefined, + equipBulkDivision: undefined, + packageCode: undefined, + packageName: undefined, + materialGroupCode: undefined, + materialGroupName: undefined, + vendorCode: undefined, + vendorName: undefined, + faTarget: undefined, + faStatus: undefined, + tier: undefined, + isAgent: undefined, + isBlacklist: undefined, + isBcc: undefined, + purchaseOpinion: undefined, + shipTypeCommon: undefined, + shipTypeAmax: undefined, + shipTypeSmax: undefined, + shipTypeVlcc: undefined, + shipTypeLngc: undefined, + shipTypeCont: undefined, + offshoreTypeCommon: undefined, + offshoreTypeFpso: undefined, + offshoreTypeFlng: undefined, + offshoreTypeFpu: undefined, + offshoreTypePlatform: undefined, + offshoreTypeWtiv: undefined, + offshoreTypeGom: undefined, + picName: undefined, + picEmail: undefined, + picPhone: undefined, + agentName: undefined, + agentEmail: undefined, + agentPhone: undefined, + recentQuoteDate: undefined, + recentQuoteNumber: undefined, + recentOrderDate: undefined, + recentOrderNumber: undefined, + registrationDate: undefined, + registrant: undefined, + lastModifiedDate: undefined, + lastModifier: undefined, + ...searchParams, + } + console.log('VendorPoolTable - API call params:', params) + const result = await getVendorPools(params as GetVendorPoolSchema) + console.log('VendorPoolTable - API result:', { + dataCount: result.data.length, + pageCount: result.pageCount, + requestedPage: params.page + }) + setData(result.data) + setPageCount(result.pageCount) + } catch (error) { + console.error("Vendor Pool 데이터 로드 실패:", error) + setData([]) + setPageCount(0) + } finally { + setLoading(false) + } + }, [searchText]) + + // 검색 핸들러 + const handleSearch = React.useCallback(() => { + if (showAll) { + // 전체보기 모드에서는 페이징 없이 전체 데이터 로드 + loadData({ perPage: 1000 }) // 충분히 큰 숫자로 전체 데이터 가져오기 + } else { + // 검색 시 페이지를 1페이지로 리셋 + setPagination(prev => ({ ...prev, pageIndex: 0 })) + loadData({ page: 1, perPage: pagination.pageSize }) + } + }, [loadData, showAll, pagination.pageSize]) + + // 전체보기 토글 핸들러 + const handleShowAllToggle = React.useCallback((checked: boolean) => { + setShowAll(checked) + if (checked) { + // 전체보기 활성화 시 전체 데이터 로드 + loadData({ perPage: 1000 }) + setSearchText("") + } else { + // 전체보기 비활성화 시 일반 페이징으로 전환 + loadData({}) + } + }, [loadData]) + + // 초기 데이터 로드 + React.useEffect(() => { + // 초기 로드 시 페이지를 1페이지로 설정 + setPagination(prev => ({ ...prev, pageIndex: 0 })) + loadData({ page: 1, perPage: pagination.pageSize }) + }, [pagination.pageSize]) + + // reloadTrigger가 변경될 때마다 데이터 리로드 + React.useEffect(() => { + if (reloadTrigger && reloadTrigger > 0) { + console.log('VendorPoolTable - reloadTrigger changed, reloading data') + loadData({}) + } + }, [reloadTrigger, loadData]) + + const table = useReactTable({ + data, + columns: vendorPoolColumns, + getCoreRowModel: getCoreRowModel(), + getPaginationRowModel: getPaginationRowModel(), + getSortedRowModel: getSortedRowModel(), + getFilteredRowModel: getFilteredRowModel(), + manualPagination: !showAll, // 전체보기 시에는 수동 페이징 비활성화 + pageCount: showAll ? 1 : pageCount, // 전체보기 시 1페이지, 일반 모드에서는 API에서 받은 pageCount 사용 + state: { + pagination: showAll ? { pageIndex: 0, pageSize: data.length } : pagination, + }, + onPaginationChange: (updater) => { + if (!showAll) { + // 전체보기가 아닐 때만 페이징 변경 처리 + const newPaginationState = typeof updater === 'function' ? updater(pagination) : updater + + console.log('VendorPoolTable - Pagination changed:', { + currentState: pagination, + newPaginationState, + showAll, + willLoadData: !showAll + }) + + setPagination(newPaginationState) + + const apiParams = { + page: newPaginationState.pageIndex + 1, + perPage: newPaginationState.pageSize, + } + console.log('VendorPoolTable - Loading data with params:', apiParams) + loadData(apiParams) + } + }, + }) + + // 외부에서 선택된 ID들을 가져올 수 있도록 ref에 메소드 노출 + useImperativeHandle(ref, () => ({ + getSelectedIds: () => { + const selectedRows = table.getFilteredSelectedRowModel().rows + return selectedRows.map(row => row.original.id) + } + })) + + // 선택된 행 개수 + const selectedRowCount = table.getFilteredSelectedRowModel().rows.length + + // 선택 상태 변경 시 콜백 호출 + React.useEffect(() => { + onSelectionChange?.(selectedRowCount) + }, [selectedRowCount, onSelectionChange]) + + // 선택 해제 요청이 오면 모든 선택 해제 + React.useEffect(() => { + if (resetCounter && resetCounter > 0) { + table.toggleAllPageRowsSelected(false) + } + }, [resetCounter, table]) + + return ( + <div className="h-full flex flex-col"> + <div className="mb-2"> + <div className="flex items-center justify-between mb-2"> + <h4 className="font-medium">Vendor Pool</h4> + <div className="flex gap-1"> + {/* <Button variant="outline" size="sm"> + 신규업체 추가 + </Button> */} + </div> + </div> + </div> + + {/* 검색 UI */} + <div className="mb-4 p-4 border rounded-lg bg-muted/50"> + <div className="flex flex-col sm:flex-row gap-4 items-start sm:items-center"> + {/* 전체보기 체크박스 */} + <div className="flex items-center space-x-2"> + <Checkbox + id="showAll" + checked={showAll} + onCheckedChange={handleShowAllToggle} + /> + <label + htmlFor="showAll" + className="text-sm font-medium leading-none peer-disabled:cursor-not-allowed peer-disabled:opacity-70" + > + 전체보기 + </label> + </div> + + {/* 검색어 입력 */} + {!showAll && ( + <div className="flex gap-2 flex-1 max-w-md"> + <Input + placeholder="설계공종, 업체명, 자재그룹 등으로 검색..." + value={searchText} + onChange={(e) => setSearchText(e.target.value)} + className="flex-1" + onKeyPress={(e) => { + if (e.key === 'Enter') { + handleSearch() + } + }} + /> + <Button + onClick={handleSearch} + disabled={loading} + size="sm" + className="px-3" + > + <Search className="w-4 h-4" /> + </Button> + </div> + )} + + {/* 검색 결과 정보 */} + <div className="text-sm text-muted-foreground"> + {showAll ? ( + `전체 ${data.length}개 항목 표시 중` + ) : ( + `${data.length}개 항목${searchText ? ` (검색어: "${searchText}")` : ""}` + )} + </div> + </div> + </div> + + <div className="flex-1"> + <DataTable table={table} /> + </div> + </div> + ) +}) + +VendorPoolTable.displayName = "VendorPoolTable" |
