summaryrefslogtreecommitdiff
path: root/lib/basic-contract/status-detail/basic-contract-detail-table-toolbar-actions.tsx
diff options
context:
space:
mode:
Diffstat (limited to 'lib/basic-contract/status-detail/basic-contract-detail-table-toolbar-actions.tsx')
-rw-r--r--lib/basic-contract/status-detail/basic-contract-detail-table-toolbar-actions.tsx191
1 files changed, 123 insertions, 68 deletions
diff --git a/lib/basic-contract/status-detail/basic-contract-detail-table-toolbar-actions.tsx b/lib/basic-contract/status-detail/basic-contract-detail-table-toolbar-actions.tsx
index e62a6cb7..42fb2b5f 100644
--- a/lib/basic-contract/status-detail/basic-contract-detail-table-toolbar-actions.tsx
+++ b/lib/basic-contract/status-detail/basic-contract-detail-table-toolbar-actions.tsx
@@ -22,11 +22,19 @@ import { prepareFinalApprovalAction, quickFinalApprovalAction, resendContractsAc
import { BasicContractSignDialog } from "../vendor-table/basic-contract-sign-dialog"
import { SSLVWPurInqReqDialog } from "@/components/common/legal/sslvw-pur-inq-req-dialog"
import { requestRedFlagResolution } from "@/lib/compliance/red-flag-resolution"
+import { useRouter } from "next/navigation"
+
+interface RedFlagResolutionState {
+ resolved: boolean
+ resolvedAt: Date | null
+ pendingApprovalId: string | null
+}
interface BasicContractDetailTableToolbarActionsProps {
table: Table<BasicContractView>
gtcData?: Record<number, { gtcDocumentId: number | null; hasComments: boolean }>
redFlagData?: Record<number, boolean>
+ redFlagResolutionData?: Record<number, RedFlagResolutionState>
isComplianceTemplate?: boolean
}
@@ -34,6 +42,7 @@ export function BasicContractDetailTableToolbarActions({
table,
gtcData = {},
redFlagData = {},
+ redFlagResolutionData = {},
isComplianceTemplate = false
}: BasicContractDetailTableToolbarActionsProps) {
// 선택된 행들 가져오기
@@ -47,6 +56,7 @@ export function BasicContractDetailTableToolbarActions({
const [loading, setLoading] = React.useState(false)
const [buyerSignDialog, setBuyerSignDialog] = React.useState(false)
const [contractsToSign, setContractsToSign] = React.useState<any[]>([])
+ const router = useRouter()
// 각 버튼별 활성화 조건 계산
const canBulkDownload = hasSelectedRows && selectedRows.some(row =>
@@ -339,6 +349,11 @@ export function BasicContractDetailTableToolbarActions({
return
}
+ if (selectedRows.length !== 1) {
+ toast.error("계약서 한 건을 선택해주세요.")
+ return
+ }
+
try {
setLoading(true)
@@ -350,7 +365,7 @@ export function BasicContractDetailTableToolbarActions({
if (result.success) {
toast.success(result.message)
- // 테이블 데이터 갱신
+ router.refresh()
table.toggleAllPageRowsSelected(false)
} else {
toast.error(result.message)
@@ -391,27 +406,48 @@ export function BasicContractDetailTableToolbarActions({
}
}
- // RED FLAG 해소요청 가능 여부
- const canRequestRedFlagResolution = hasSelectedRows && isComplianceTemplate && selectedRows.some(row => {
- const contract = row.original
- return redFlagData[contract.id] === true
- })
+ const hasPendingResolution = (contractId: number) => {
+ const state = redFlagResolutionData[contractId]
+ return Boolean(state?.pendingApprovalId && !state?.resolved)
+ }
+
+ const redFlagEligibleContracts = selectedRows
+ .map(row => row.original)
+ .filter(contract => {
+ if (redFlagData[contract.id] !== true) return false
+ return !hasPendingResolution(contract.id)
+ })
- // RED FLAG 해소요청 가능한 계약서들
- const redFlagResolutionContracts = selectedRows
+ const redFlagPendingContracts = selectedRows
.map(row => row.original)
- .filter(contract => redFlagData[contract.id] === true)
+ .filter(contract => hasPendingResolution(contract.id))
+
+ const canRequestRedFlagResolution =
+ hasSelectedRows && isComplianceTemplate && redFlagEligibleContracts.length > 0
// RED FLAG 해소요청
const handleRequestRedFlagResolution = async () => {
if (!canRequestRedFlagResolution) {
- toast.error("RED FLAG가 있는 계약서를 선택해주세요")
+ toast.error("해소요청 가능한 RED FLAG 계약서를 선택해주세요")
return
}
+ if (redFlagPendingContracts.length > 0) {
+ const preview = redFlagPendingContracts
+ .map((contract) => contract.vendorName || `계약 ${contract.id}`)
+ .slice(0, 2)
+ .join(", ")
+ toast.info(
+ `${preview}${redFlagPendingContracts.length > 2 ? ` 외 ${redFlagPendingContracts.length - 2}건` : ""}은 해소요청이 이미 진행 중입니다.`,
+ {
+ description: "진행 중인 계약서는 자동으로 제외하고 요청합니다.",
+ }
+ )
+ }
+
setLoading(true)
try {
- const contractIds = redFlagResolutionContracts.map(c => c.id)
+ const contractIds = redFlagEligibleContracts.map(c => c.id)
const result = await requestRedFlagResolution(contractIds)
toast.success("RED FLAG 해소요청 결재가 상신되었습니다.", {
@@ -458,8 +494,14 @@ export function BasicContractDetailTableToolbarActions({
}
]
- // 법무검토 요청
+ const complianceInquiryUrl = 'http://60.101.207.55/Inquiry/Write/InquiryWrite.aspx'
+
+ // 법무검토 요청 / 준법문의
const handleRequestLegalReview = () => {
+ if (isComplianceTemplate) {
+ window.open(complianceInquiryUrl, '_blank', 'noopener,noreferrer')
+ return
+ }
setLegalReviewDialog(true)
}
@@ -503,13 +545,15 @@ export function BasicContractDetailTableToolbarActions({
title={!hasSelectedRows
? "계약서를 선택해주세요"
: !canRequestRedFlagResolution
- ? "RED FLAG가 있는 계약서를 선택해주세요"
- : `${redFlagResolutionContracts.length}건 RED FLAG 해소요청`
+ ? redFlagPendingContracts.length > 0
+ ? "이미 해소요청이 진행 중인 계약서만 선택되어 있습니다"
+ : "RED FLAG가 있는 계약서를 선택해주세요"
+ : `${redFlagEligibleContracts.length}건 RED FLAG 해소요청`
}
>
<Flag className="size-4" aria-hidden="true" />
<span className="hidden sm:inline">
- RED FLAG 해소요청 {hasSelectedRows ? `(${redFlagResolutionContracts.length})` : ''}
+ RED FLAG 해소요청 {hasSelectedRows ? `(${redFlagEligibleContracts.length})` : ''}
</span>
</Button>
)}
@@ -530,19 +574,28 @@ export function BasicContractDetailTableToolbarActions({
</Button>
{/* 법무검토 버튼 (SSLVW 데이터 조회) */}
- <SSLVWPurInqReqDialog onConfirm={handleSSLVWConfirm} />
+ <SSLVWPurInqReqDialog
+ onConfirm={handleSSLVWConfirm}
+ requireSingleSelection
+ triggerDisabled={selectedRows.length !== 1 || loading}
+ triggerTitle={
+ selectedRows.length !== 1
+ ? "계약서 한 건을 선택해주세요"
+ : undefined
+ }
+ />
- {/* 법무검토 요청 버튼 */}
+ {/* 법무검토 요청 / 준법문의 버튼 */}
<Button
variant="outline"
size="sm"
onClick={handleRequestLegalReview}
className="gap-2"
- title="법무검토 요청 링크 선택"
+ title={isComplianceTemplate ? "준법문의 링크로 이동" : "법무검토 요청 링크 선택"}
>
<FileText className="size-4" aria-hidden="true" />
<span className="hidden sm:inline">
- 법무검토 요청
+ {isComplianceTemplate ? "준법문의" : "법무검토 요청"}
</span>
</Button>
@@ -643,61 +696,63 @@ export function BasicContractDetailTableToolbarActions({
</DialogContent>
</Dialog>
- {/* 법무검토 요청 다이얼로그 */}
- <Dialog open={legalReviewDialog} onOpenChange={setLegalReviewDialog}>
- <DialogContent className="max-w-2xl">
- <DialogHeader>
- <DialogTitle className="flex items-center gap-2">
- <FileText className="size-5" />
- 법무검토 요청
- </DialogTitle>
- <DialogDescription>
- 법무검토 요청 유형을 선택하세요. 선택한 링크가 새 창에서 열립니다.
- </DialogDescription>
- </DialogHeader>
-
- <div className="space-y-4">
- <div className="flex items-start gap-3 p-4 bg-blue-50 border border-blue-200 rounded-lg">
- <Globe className="size-5 text-blue-600 flex-shrink-0 mt-0.5" />
- <div>
- <div className="font-medium text-blue-800">삼성중공업 법무관리시스템</div>
- <div className="text-sm text-blue-700 mt-1">
- 아래 링크 중 해당하는 유형을 선택하여 법무검토를 요청하세요.
+ {/* 법무검토 요청 다이얼로그 (준법 템플릿 제외) */}
+ {!isComplianceTemplate && (
+ <Dialog open={legalReviewDialog} onOpenChange={setLegalReviewDialog}>
+ <DialogContent className="max-w-2xl">
+ <DialogHeader>
+ <DialogTitle className="flex items-center gap-2">
+ <FileText className="size-5" />
+ 법무검토 요청
+ </DialogTitle>
+ <DialogDescription>
+ 법무검토 요청 유형을 선택하세요. 선택한 링크가 새 창에서 열립니다.
+ </DialogDescription>
+ </DialogHeader>
+
+ <div className="space-y-4">
+ <div className="flex items-start gap-3 p-4 bg-blue-50 border border-blue-200 rounded-lg">
+ <Globe className="size-5 text-blue-600 flex-shrink-0 mt-0.5" />
+ <div>
+ <div className="font-medium text-blue-800">삼성중공업 법무관리시스템</div>
+ <div className="text-sm text-blue-700 mt-1">
+ 아래 링크 중 해당하는 유형을 선택하여 법무검토를 요청하세요.
+ </div>
</div>
</div>
- </div>
- <div className="space-y-2">
- {legalReviewLinks.map((link) => (
- <button
- key={link.id}
- onClick={() => handleLegalReviewLinkClick(link.url)}
- className="w-full flex items-center justify-between p-4 rounded-lg border border-gray-200 hover:border-blue-300 hover:bg-blue-50 transition-colors text-left group"
- >
- <div className="flex-1">
- <div className="font-medium text-gray-900 group-hover:text-blue-700">
- {link.label}
- </div>
- <div className="text-sm text-gray-500 mt-1">
- {link.description}
+ <div className="space-y-2">
+ {legalReviewLinks.map((link) => (
+ <button
+ key={link.id}
+ onClick={() => handleLegalReviewLinkClick(link.url)}
+ className="w-full flex items-center justify-between p-4 rounded-lg border border-gray-200 hover:border-blue-300 hover:bg-blue-50 transition-colors text-left group"
+ >
+ <div className="flex-1">
+ <div className="font-medium text-gray-900 group-hover:text-blue-700">
+ {link.label}
+ </div>
+ <div className="text-sm text-gray-500 mt-1">
+ {link.description}
+ </div>
</div>
- </div>
- <ExternalLink className="size-5 text-gray-400 group-hover:text-blue-600 flex-shrink-0 ml-4" />
- </button>
- ))}
+ <ExternalLink className="size-5 text-gray-400 group-hover:text-blue-600 flex-shrink-0 ml-4" />
+ </button>
+ ))}
+ </div>
</div>
- </div>
- <DialogFooter>
- <Button
- variant="outline"
- onClick={() => setLegalReviewDialog(false)}
- >
- 닫기
- </Button>
- </DialogFooter>
- </DialogContent>
- </Dialog>
+ <DialogFooter>
+ <Button
+ variant="outline"
+ onClick={() => setLegalReviewDialog(false)}
+ >
+ 닫기
+ </Button>
+ </DialogFooter>
+ </DialogContent>
+ </Dialog>
+ )}
{/* 최종승인 다이얼로그 */}
<Dialog open={finalApproveDialog} onOpenChange={setFinalApproveDialog}>