summaryrefslogtreecommitdiff
path: root/lib/avl/table/avl-registration-area.tsx
diff options
context:
space:
mode:
Diffstat (limited to 'lib/avl/table/avl-registration-area.tsx')
-rw-r--r--lib/avl/table/avl-registration-area.tsx316
1 files changed, 303 insertions, 13 deletions
diff --git a/lib/avl/table/avl-registration-area.tsx b/lib/avl/table/avl-registration-area.tsx
index def3d30a..52912a2c 100644
--- a/lib/avl/table/avl-registration-area.tsx
+++ b/lib/avl/table/avl-registration-area.tsx
@@ -5,15 +5,27 @@ import { Card } from "@/components/ui/card"
import { Button } from "@/components/ui/button"
import { ChevronLeft, ChevronRight, ChevronsLeft, ChevronsRight } from "lucide-react"
import { useAtom } from "jotai"
-import { ProjectAvlTable } from "./project-avl-table"
-import { StandardAvlTable } from "./standard-avl-table"
-import { VendorPoolTable } from "./vendor-pool-table"
+import { ProjectAvlTable, ProjectAvlTableRef } from "./project-avl-table"
+import { StandardAvlTable, StandardAvlTableRef } from "./standard-avl-table"
+import { VendorPoolTable, VendorPoolTableRef } from "./vendor-pool-table"
import { selectedAvlRecordAtom } from "../avl-atoms"
-import type { AvlListItem } from "../types"
+import { copyToProjectAvl, copyToStandardAvl, copyToVendorPool, copyFromVendorPoolToProjectAvl, copyFromVendorPoolToStandardAvl, copyFromStandardAvlToVendorPool } from "../service"
+import { useSession } from "next-auth/react"
+import { toast } from "sonner"
// 선택된 테이블 타입
type SelectedTable = 'project' | 'standard' | 'vendor' | null
+// TODO: 나머지 테이블들도 ref 지원 추가 시 인터페이스 추가 필요
+// interface StandardAvlTableRef {
+// getSelectedIds?: () => number[]
+// }
+//
+// interface VendorPoolTableRef {
+// getSelectedIds?: () => number[]
+// }
+
+
// 선택 상태 액션 타입
type SelectionAction =
| { type: 'SELECT_PROJECT'; count: number }
@@ -105,9 +117,13 @@ interface AvlRegistrationAreaProps {
}
export function AvlRegistrationArea({ disabled = false }: AvlRegistrationAreaProps) {
+
// 선택된 AVL 레코드 구독
const [selectedAvlRecord] = useAtom(selectedAvlRecordAtom)
+ // 세션 정보
+ const { data: session } = useSession()
+
// 단일 선택 상태 관리 (useReducer 사용)
const [selectionState, dispatch] = React.useReducer(selectionReducer, {
selectedTable: null,
@@ -121,10 +137,12 @@ export function AvlRegistrationArea({ disabled = false }: AvlRegistrationAreaPro
// 선택 핸들러들
const handleProjectSelection = React.useCallback((count: number) => {
+ console.log('handleProjectSelection called with count:', count)
dispatch({ type: 'SELECT_PROJECT', count })
}, [])
const handleStandardSelection = React.useCallback((count: number) => {
+ console.log('handleStandardSelection called with count:', count)
dispatch({ type: 'SELECT_STANDARD', count })
}, [])
@@ -134,6 +152,8 @@ export function AvlRegistrationArea({ disabled = false }: AvlRegistrationAreaPro
const { selectedTable, selectedRowCount, resetCounters } = selectionState
+ console.log('selectedTable', selectedTable);
+
// 선택된 AVL에 따른 필터 값들
const [currentProjectCode, setCurrentProjectCode] = React.useState<string>("")
const constructionSector = selectedAvlRecord?.constructionSector || ""
@@ -142,6 +162,38 @@ export function AvlRegistrationArea({ disabled = false }: AvlRegistrationAreaPro
const htDivision = selectedAvlRecord?.htDivision || ""
const avlListId = selectedAvlRecord?.id ? String(selectedAvlRecord.id) : ""
+ // 선종별 표준 AVL 검색 조건 상태 (복사 버튼 활성화용)
+ const [standardSearchConditions, setStandardSearchConditions] = React.useState({
+ constructionSector: "",
+ shipType: "",
+ avlKind: "",
+ htDivision: ""
+ })
+
+ // 검색 조건이 모두 입력되었는지 확인
+ const isStandardSearchConditionsComplete = React.useMemo(() => {
+ return (
+ standardSearchConditions.constructionSector.trim() !== "" &&
+ standardSearchConditions.shipType.trim() !== "" &&
+ standardSearchConditions.avlKind.trim() !== "" &&
+ standardSearchConditions.htDivision.trim() !== ""
+ )
+ }, [standardSearchConditions])
+
+ // 벤더 풀 리로드 트리거
+ const [vendorPoolReloadTrigger, setVendorPoolReloadTrigger] = React.useState(0)
+
+ // 선종별 표준 AVL 리로드 트리거
+ const [standardAvlReloadTrigger, setStandardAvlReloadTrigger] = React.useState(0)
+
+ // 프로젝트 AVL 리로드 트리거
+ const [projectAvlReloadTrigger, setProjectAvlReloadTrigger] = React.useState(0)
+
+ // 테이블 ref들 (선택된 행 정보 가져오기용)
+ const projectTableRef = React.useRef<ProjectAvlTableRef>(null)
+ const standardTableRef = React.useRef<StandardAvlTableRef>(null)
+ const vendorTableRef = React.useRef<VendorPoolTableRef>(null)
+
// 선택된 AVL 레코드가 변경될 때 프로젝트 코드 초기화
React.useEffect(() => {
setCurrentProjectCode(selectedAvlRecord?.projectCode || "")
@@ -152,6 +204,231 @@ export function AvlRegistrationArea({ disabled = false }: AvlRegistrationAreaPro
setCurrentProjectCode(projectCode)
}, [])
+ // 선택된 ID들을 가져오는 헬퍼 함수들
+ const getSelectedIds = React.useCallback((tableType: 'project' | 'standard' | 'vendor') => {
+ // 각 테이블 컴포넌트에서 선택된 행들의 ID를 가져오는 로직
+ switch (tableType) {
+ case 'project':
+ return projectTableRef.current?.getSelectedIds?.() || []
+ case 'standard':
+ return standardTableRef.current?.getSelectedIds?.() || []
+ case 'vendor':
+ return vendorTableRef.current?.getSelectedIds?.() || []
+ default:
+ return []
+ }
+ }, [])
+
+ // 복사 버튼 핸들러들
+ const handleCopyToProject = React.useCallback(async () => {
+ if (selectedTable !== 'standard' || selectedRowCount === 0) return
+
+ const selectedIds = getSelectedIds('standard')
+ if (!selectedIds.length) {
+ toast.error("복사할 항목을 선택해주세요.")
+ return
+ }
+
+ if (!currentProjectCode) {
+ toast.error("프로젝트 코드가 설정되지 않았습니다.")
+ return
+ }
+
+ try {
+ const result = await copyToProjectAvl(
+ selectedIds,
+ currentProjectCode,
+ parseInt(avlListId) || 1,
+ session?.user?.name || "unknown"
+ )
+
+ if (result.success) {
+ toast.success(result.message)
+ // 선택 해제
+ dispatch({ type: 'CLEAR_SELECTION' })
+ } else {
+ toast.error(result.message)
+ }
+ } catch (error) {
+ console.error('프로젝트AVL로 복사 실패:', error)
+ toast.error("복사 중 오류가 발생했습니다.")
+ }
+ }, [selectedTable, selectedRowCount, getSelectedIds, currentProjectCode, avlListId, session])
+
+ const handleCopyToStandard = React.useCallback(async () => {
+ if (selectedTable !== 'project' || selectedRowCount === 0) return
+
+ const selectedIds = getSelectedIds('project')
+ if (!selectedIds.length) {
+ toast.error("복사할 항목을 선택해주세요.")
+ return
+ }
+
+ const targetStandardInfo = {
+ constructionSector: standardSearchConditions.constructionSector || "조선",
+ shipType: standardSearchConditions.shipType || "",
+ avlKind: standardSearchConditions.avlKind || "",
+ htDivision: standardSearchConditions.htDivision || "H"
+ }
+
+ try {
+ const result = await copyToStandardAvl(
+ selectedIds,
+ targetStandardInfo,
+ session?.user?.name || "unknown"
+ )
+
+ if (result.success) {
+ toast.success(result.message)
+ // 선종별 표준 AVL 데이터 리로드
+ setStandardAvlReloadTrigger(prev => prev + 1)
+ // 선택 해제
+ dispatch({ type: 'CLEAR_SELECTION' })
+ } else {
+ toast.error(result.message)
+ }
+ } catch (error) {
+ console.error('선종별표준AVL로 복사 실패:', error)
+ toast.error("복사 중 오류가 발생했습니다.")
+ }
+ }, [selectedTable, selectedRowCount, getSelectedIds, standardSearchConditions, session])
+
+ const handleCopyToVendorPool = React.useCallback(async () => {
+ if (selectedTable !== 'project' || selectedRowCount === 0) return
+
+ const selectedIds = getSelectedIds('project')
+ if (!selectedIds.length) {
+ toast.error("복사할 항목을 선택해주세요.")
+ return
+ }
+
+ try {
+ const result = await copyToVendorPool(
+ selectedIds,
+ session?.user?.name || "unknown"
+ )
+
+ if (result.success) {
+ toast.success(result.message)
+ // 벤더 풀 데이터 리로드
+ setVendorPoolReloadTrigger(prev => prev + 1)
+ // 선택 해제
+ dispatch({ type: 'CLEAR_SELECTION' })
+ } else {
+ toast.error(result.message)
+ }
+ } catch (error) {
+ console.error('벤더풀로 복사 실패:', error)
+ toast.error("복사 중 오류가 발생했습니다.")
+ }
+ }, [selectedTable, selectedRowCount, getSelectedIds, session])
+
+ // 추가 복사 버튼 핸들러들
+ const handleCopyFromVendorToProject = React.useCallback(async () => {
+ if (selectedTable !== 'vendor' || selectedRowCount === 0) return
+
+ const selectedIds = getSelectedIds('vendor')
+ if (!selectedIds.length) {
+ toast.error("복사할 항목을 선택해주세요.")
+ return
+ }
+
+ if (!currentProjectCode) {
+ toast.error("프로젝트 코드가 설정되지 않았습니다.")
+ return
+ }
+
+ try {
+ const result = await copyFromVendorPoolToProjectAvl(
+ selectedIds,
+ currentProjectCode,
+ parseInt(avlListId) || 1,
+ session?.user?.name || "unknown"
+ )
+
+ if (result.success) {
+ toast.success(result.message)
+ // 프로젝트 AVL 리로드
+ setProjectAvlReloadTrigger(prev => prev + 1)
+ // 선택 해제
+ dispatch({ type: 'CLEAR_SELECTION' })
+ } else {
+ toast.error(result.message)
+ }
+ } catch (error) {
+ console.error('벤더풀 → 프로젝트AVL 복사 실패:', error)
+ toast.error("복사 중 오류가 발생했습니다.")
+ }
+ }, [selectedTable, selectedRowCount, getSelectedIds, currentProjectCode, avlListId, session])
+
+ const handleCopyFromVendorToStandard = React.useCallback(async () => {
+ if (selectedTable !== 'vendor' || selectedRowCount === 0) return
+
+ const selectedIds = getSelectedIds('vendor')
+ if (!selectedIds.length) {
+ toast.error("복사할 항목을 선택해주세요.")
+ return
+ }
+
+ const targetStandardInfo = {
+ constructionSector: standardSearchConditions.constructionSector || "조선",
+ shipType: standardSearchConditions.shipType || "",
+ avlKind: standardSearchConditions.avlKind || "",
+ htDivision: standardSearchConditions.htDivision || "H"
+ }
+
+ try {
+ const result = await copyFromVendorPoolToStandardAvl(
+ selectedIds,
+ targetStandardInfo,
+ session?.user?.name || "unknown"
+ )
+
+ if (result.success) {
+ toast.success(result.message)
+ // 선종별 표준 AVL 리로드
+ setStandardAvlReloadTrigger(prev => prev + 1)
+ // 선택 해제
+ dispatch({ type: 'CLEAR_SELECTION' })
+ } else {
+ toast.error(result.message)
+ }
+ } catch (error) {
+ console.error('벤더풀 → 선종별표준AVL 복사 실패:', error)
+ toast.error("복사 중 오류가 발생했습니다.")
+ }
+ }, [selectedTable, selectedRowCount, getSelectedIds, standardSearchConditions, session])
+
+ const handleCopyFromStandardToVendor = React.useCallback(async () => {
+ if (selectedTable !== 'standard' || selectedRowCount === 0) return
+
+ const selectedIds = getSelectedIds('standard')
+ if (!selectedIds.length) {
+ toast.error("복사할 항목을 선택해주세요.")
+ return
+ }
+
+ try {
+ const result = await copyFromStandardAvlToVendorPool(
+ selectedIds,
+ session?.user?.name || "unknown"
+ )
+
+ if (result.success) {
+ toast.success(result.message)
+ // 벤더 풀 리로드
+ setVendorPoolReloadTrigger(prev => prev + 1)
+ // 선택 해제
+ dispatch({ type: 'CLEAR_SELECTION' })
+ } else {
+ toast.error(result.message)
+ }
+ } catch (error) {
+ console.error('선종별표준AVL → 벤더풀 복사 실패:', error)
+ toast.error("복사 중 오류가 발생했습니다.")
+ }
+ }, [selectedTable, selectedRowCount, getSelectedIds, session])
+
return (
<Card className={`h-full min-w-full overflow-visible ${disabled ? 'opacity-50 pointer-events-none' : ''}`}>
{/* 고정 헤더 영역 */}
@@ -159,9 +436,9 @@ export function AvlRegistrationArea({ disabled = false }: AvlRegistrationAreaPro
<div className="flex items-center justify-between">
<h3 className="text-lg font-semibold">AVL 등록 {disabled ? "(비활성화)" : ""}</h3>
<div className="flex gap-2">
- <Button variant="outline" size="sm" disabled={disabled}>
+ {/* <Button variant="outline" size="sm" disabled={disabled}>
AVL 불러오기
- </Button>
+ </Button> */}
</div>
</div>
</div>
@@ -172,11 +449,13 @@ export function AvlRegistrationArea({ disabled = false }: AvlRegistrationAreaPro
{/* 프로젝트 AVL 테이블 - 9개 컬럼 */}
<div className="p-4 border-r relative">
<ProjectAvlTable
+ ref={projectTableRef}
onSelectionChange={handleProjectSelection}
resetCounter={resetCounters.project}
projectCode={currentProjectCode}
avlListId={parseInt(avlListId) || 1}
onProjectCodeChange={handleProjectCodeChange}
+ reloadTrigger={projectAvlReloadTrigger}
/>
{/* 이동 버튼들 - 첫 번째 border 위에 오버레이 */}
@@ -188,7 +467,8 @@ export function AvlRegistrationArea({ disabled = false }: AvlRegistrationAreaPro
size="sm"
className="w-8 h-8 p-0"
title="프로젝트AVL로 복사"
- disabled={disabled || selectedTable !== 'standard' || selectedRowCount === 0}
+ disabled={disabled || selectedTable === 'project' || selectedRowCount === 0}
+ onClick={handleCopyToProject}
>
<ChevronLeft className="w-4 h-4" />
</Button>
@@ -198,7 +478,8 @@ export function AvlRegistrationArea({ disabled = false }: AvlRegistrationAreaPro
size="sm"
className="w-8 h-8 p-0"
title="선종별표준AVL로 복사"
- disabled={disabled || selectedTable !== 'project' || selectedRowCount === 0}
+ disabled={disabled || selectedTable !== 'project' || selectedRowCount === 0 || !isStandardSearchConditionsComplete}
+ onClick={handleCopyToStandard}
>
<ChevronRight className="w-4 h-4" />
</Button>
@@ -209,6 +490,7 @@ export function AvlRegistrationArea({ disabled = false }: AvlRegistrationAreaPro
className="w-8 h-8 p-0"
title="벤더풀로 복사"
disabled={disabled || selectedTable !== 'project' || selectedRowCount === 0}
+ onClick={handleCopyToVendorPool}
>
<ChevronsRight className="w-4 h-4" />
</Button>
@@ -220,12 +502,15 @@ export function AvlRegistrationArea({ disabled = false }: AvlRegistrationAreaPro
{/* 선종별 표준 AVL 테이블 - 8개 컬럼 */}
<div className="p-4 border-r relative">
<StandardAvlTable
+ ref={standardTableRef}
onSelectionChange={handleStandardSelection}
resetCounter={resetCounters.standard}
constructionSector={constructionSector}
shipType={shipType}
avlKind={avlKind}
htDivision={htDivision}
+ onSearchConditionsChange={setStandardSearchConditions}
+ reloadTrigger={standardAvlReloadTrigger}
/>
{/* 이동 버튼들 - 두 번째 border 위에 오버레이 */}
@@ -235,8 +520,9 @@ export function AvlRegistrationArea({ disabled = false }: AvlRegistrationAreaPro
variant="outline"
size="sm"
className="w-8 h-8 p-0"
- title="프로젝트AVL로 복사"
- disabled={disabled || selectedTable !== 'vendor' || selectedRowCount === 0}
+ title="벤더풀의 항목을 프로젝트AVL로 복사"
+ disabled={disabled || selectedTable !== 'vendor' || selectedRowCount === 0 || !currentProjectCode}
+ onClick={handleCopyFromVendorToProject}
>
<ChevronsLeft className="w-4 h-4" />
</Button>
@@ -245,8 +531,9 @@ export function AvlRegistrationArea({ disabled = false }: AvlRegistrationAreaPro
variant="outline"
size="sm"
className="w-8 h-8 p-0"
- title="선종별표준AVL로 복사"
- disabled={disabled || selectedTable !== 'vendor' || selectedRowCount === 0}
+ title="벤더풀의 항목을 선종별표준AVL로 복사"
+ disabled={disabled || selectedTable !== 'vendor' || selectedRowCount === 0 || !isStandardSearchConditionsComplete}
+ onClick={handleCopyFromVendorToStandard}
>
<ChevronLeft className="w-4 h-4" />
</Button>
@@ -254,8 +541,9 @@ export function AvlRegistrationArea({ disabled = false }: AvlRegistrationAreaPro
variant="outline"
size="sm"
className="w-8 h-8 p-0"
- title="벤더풀로 복사"
+ title="선종별표준AVL의 항목을 벤더풀로 복사"
disabled={disabled || selectedTable !== 'standard' || selectedRowCount === 0}
+ onClick={handleCopyFromStandardToVendor}
>
<ChevronRight className="w-4 h-4" />
</Button>
@@ -267,8 +555,10 @@ export function AvlRegistrationArea({ disabled = false }: AvlRegistrationAreaPro
{/* Vendor Pool 테이블 - 10개 컬럼 */}
<div className="p-4 relative">
<VendorPoolTable
+ ref={vendorTableRef}
onSelectionChange={handleVendorSelection}
resetCounter={resetCounters.vendor}
+ reloadTrigger={vendorPoolReloadTrigger}
/>
</div>
</div>