From aa86729f9a2ab95346a2851e3837de1c367aae17 Mon Sep 17 00:00:00 2001 From: dujinkim Date: Fri, 20 Jun 2025 11:37:31 +0000 Subject: (대표님) 20250620 작업사항 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../table/evaluation-target-table.tsx | 154 +++++++++++++-------- 1 file changed, 95 insertions(+), 59 deletions(-) (limited to 'lib/evaluation-target-list/table/evaluation-target-table.tsx') diff --git a/lib/evaluation-target-list/table/evaluation-target-table.tsx b/lib/evaluation-target-list/table/evaluation-target-table.tsx index 15837733..fe0b3188 100644 --- a/lib/evaluation-target-list/table/evaluation-target-table.tsx +++ b/lib/evaluation-target-list/table/evaluation-target-table.tsx @@ -25,6 +25,7 @@ import { getEvaluationTargetsColumns } from "./evaluation-targets-columns" import { EvaluationTargetsTableToolbarActions } from "./evaluation-targets-toolbar-actions" import { EvaluationTargetFilterSheet } from "./evaluation-targets-filter-sheet" import { EvaluationTargetWithDepartments } from "@/db/schema" +import { EditEvaluationTargetSheet } from "./update-evaluation-target" interface EvaluationTargetsTableProps { promises: Promise<[Awaited>]> @@ -40,13 +41,13 @@ function EvaluationTargetsStats({ evaluationYear }: { evaluationYear: number }) React.useEffect(() => { let isMounted = true - + async function fetchStats() { try { setIsLoading(true) setError(null) const statsData = await getEvaluationTargetsStats(evaluationYear) - + if (isMounted) { setStats(statsData) } @@ -186,45 +187,59 @@ function EvaluationTargetsStats({ evaluationYear }: { evaluationYear: number }) export function EvaluationTargetsTable({ promises, evaluationYear, className }: EvaluationTargetsTableProps) { const [rowAction, setRowAction] = React.useState | null>(null) const [isFilterPanelOpen, setIsFilterPanelOpen] = React.useState(false) - console.count("E Targets render"); const router = useRouter() const searchParams = useSearchParams() const containerRef = React.useRef(null) const [containerTop, setContainerTop] = React.useState(0) + // ✅ 스크롤 이벤트 throttling으로 성능 최적화 const updateContainerBounds = React.useCallback(() => { if (containerRef.current) { const rect = containerRef.current.getBoundingClientRect() - setContainerTop(rect.top) + const newTop = rect.top + + // ✅ 값이 실제로 변경될 때만 상태 업데이트 + setContainerTop(prevTop => { + if (Math.abs(prevTop - newTop) > 1) { // 1px 이상 차이날 때만 업데이트 + return newTop + } + return prevTop + }) } }, []) + // ✅ throttle 함수 추가 + const throttledUpdateBounds = React.useCallback(() => { + let timeoutId: NodeJS.Timeout + return () => { + clearTimeout(timeoutId) + timeoutId = setTimeout(updateContainerBounds, 16) // ~60fps + } + }, [updateContainerBounds]) + React.useEffect(() => { updateContainerBounds() - + + const throttledHandler = throttledUpdateBounds() + const handleResize = () => { updateContainerBounds() } - + window.addEventListener('resize', handleResize) - window.addEventListener('scroll', updateContainerBounds) - + window.addEventListener('scroll', throttledHandler) // ✅ throttled 함수 사용 + return () => { window.removeEventListener('resize', handleResize) - window.removeEventListener('scroll', updateContainerBounds) + window.removeEventListener('scroll', throttledHandler) } - }, [updateContainerBounds]) + }, [updateContainerBounds, throttledUpdateBounds]) const [promiseData] = React.use(promises) const tableData = promiseData - console.log("Evaluation Targets Table Data:", { - dataLength: tableData.data?.length, - pageCount: tableData.pageCount, - total: tableData.total, - sampleData: tableData.data?.[0] - }) + console.log(tableData) const initialSettings = React.useMemo(() => ({ page: parseInt(searchParams.get('page') || '1'), @@ -232,7 +247,7 @@ export function EvaluationTargetsTable({ promises, evaluationYear, className }: sort: searchParams.get('sort') ? JSON.parse(searchParams.get('sort')!) : [{ id: "createdAt", desc: true }], filters: searchParams.get('filters') ? JSON.parse(searchParams.get('filters')!) : [], joinOperator: (searchParams.get('joinOperator') as "and" | "or") || "and", - basicFilters: searchParams.get('basicFilters') ? + basicFilters: searchParams.get('basicFilters') ? JSON.parse(searchParams.get('basicFilters')!) : [], basicJoinOperator: (searchParams.get('basicJoinOperator') as "and" | "or") || "and", search: searchParams.get('search') || '', @@ -259,8 +274,8 @@ export function EvaluationTargetsTable({ promises, evaluationYear, className }: } = useTablePresets('evaluation-targets-table', initialSettings) const columns = React.useMemo( - () => getEvaluationTargetsColumns(), - [] + () => getEvaluationTargetsColumns({ setRowAction }), + [setRowAction] ) const filterFields: DataTableFilterField[] = [ @@ -271,31 +286,41 @@ export function EvaluationTargetsTable({ promises, evaluationYear, className }: const advancedFilterFields: DataTableAdvancedFilterField[] = [ { id: "evaluationYear", label: "평가년도", type: "number" }, - { id: "division", label: "구분", type: "select", options: [ - { label: "해양", value: "OCEAN" }, - { label: "조선", value: "SHIPYARD" }, - ]}, + { + id: "division", label: "구분", type: "select", options: [ + { label: "해양", value: "OCEAN" }, + { label: "조선", value: "SHIPYARD" }, + ] + }, { id: "vendorCode", label: "벤더 코드", type: "text" }, { id: "vendorName", label: "벤더명", type: "text" }, - { id: "domesticForeign", label: "내외자", type: "select", options: [ - { label: "내자", value: "DOMESTIC" }, - { label: "외자", value: "FOREIGN" }, - ]}, - { id: "materialType", label: "자재구분", type: "select", options: [ - { label: "기자재", value: "EQUIPMENT" }, - { label: "벌크", value: "BULK" }, - { label: "기자재/벌크", value: "EQUIPMENT_BULK" }, - ]}, - { id: "status", label: "상태", type: "select", options: [ - { label: "검토 중", value: "PENDING" }, - { label: "확정", value: "CONFIRMED" }, - { label: "제외", value: "EXCLUDED" }, - ]}, - { id: "consensusStatus", label: "의견 일치", type: "select", options: [ - { label: "의견 일치", value: "true" }, - { label: "의견 불일치", value: "false" }, - { label: "검토 중", value: "null" }, - ]}, + { + id: "domesticForeign", label: "내외자", type: "select", options: [ + { label: "내자", value: "DOMESTIC" }, + { label: "외자", value: "FOREIGN" }, + ] + }, + { + id: "materialType", label: "자재구분", type: "select", options: [ + { label: "기자재", value: "EQUIPMENT" }, + { label: "벌크", value: "BULK" }, + { label: "기자재/벌크", value: "EQUIPMENT_BULK" }, + ] + }, + { + id: "status", label: "상태", type: "select", options: [ + { label: "검토 중", value: "PENDING" }, + { label: "확정", value: "CONFIRMED" }, + { label: "제외", value: "EXCLUDED" }, + ] + }, + { + id: "consensusStatus", label: "의견 일치", type: "select", options: [ + { label: "의견 일치", value: "true" }, + { label: "의견 불일치", value: "false" }, + { label: "검토 중", value: "null" }, + ] + }, { id: "adminComment", label: "관리자 의견", type: "text" }, { id: "consolidatedComment", label: "종합 의견", type: "text" }, { id: "confirmedAt", label: "확정일", type: "date" }, @@ -305,17 +330,21 @@ export function EvaluationTargetsTable({ promises, evaluationYear, className }: const currentSettings = useMemo(() => { return getCurrentSettings() }, [getCurrentSettings]) - + + function getColKey(c: ColumnDef): string | undefined { + if ("accessorKey" in c && c.accessorKey) return c.accessorKey as string + if ("id" in c && c.id) return c.id as string + return undefined + } + const initialState = useMemo(() => { return { - sorting: initialSettings.sort.filter(sortItem => { - const columnExists = columns.some(col => col.accessorKey === sortItem.id || col.id === sortItem.id) - return columnExists - }) as any, + sorting: initialSettings.sort.filter(s => + columns.some(c => getColKey(c) === s.id)), columnVisibility: currentSettings.columnVisibility, columnPinning: currentSettings.pinnedColumns, } - }, [currentSettings, initialSettings.sort, columns]) + }, [columns, currentSettings, initialSettings.sort]) const { table } = useDataTable({ data: tableData.data, @@ -349,12 +378,12 @@ export function EvaluationTargetsTable({ promises, evaluationYear, className }: return ( <> {/* Filter Panel */} -
setIsFilterPanelOpen(false)} onSearch={handleSearch} isLoading={false} @@ -371,12 +400,12 @@ export function EvaluationTargetsTable({ promises, evaluationYear, className }:
{/* Main Content Container */} -
-
-
- +
{tableData && ( 총 {tableData.total || tableData.data.length}건 @@ -437,11 +466,18 @@ export function EvaluationTargetsTable({ promises, evaluationYear, className }: onSetDefaultPreset={setDefaultPreset} onRenamePreset={renamePreset} /> - +
+ + setRowAction(null)} + evaluationTarget={rowAction?.row.original ?? null} + /> +
-- cgit v1.2.3