diff options
Diffstat (limited to 'lib')
6 files changed, 397 insertions, 154 deletions
diff --git a/lib/basic-contract/service.ts b/lib/basic-contract/service.ts index 55ac149e..6ae3c237 100644 --- a/lib/basic-contract/service.ts +++ b/lib/basic-contract/service.ts @@ -4,7 +4,7 @@ import { revalidateTag, unstable_noStore } from "next/cache"; import db from "@/db/db"; import { getErrorMessage } from "@/lib/handle-error"; import { unstable_cache } from "@/lib/unstable-cache"; -import { asc, desc, ilike, inArray, and, gte, lte, not, or, sql, eq, isNull, ne, isNotNull, count,like } from "drizzle-orm"; +import { asc, desc, ilike, inArray, and, gte, lte, not, or, sql, eq, isNull, ne, isNotNull, count,like, SQL } from "drizzle-orm"; import { v4 as uuidv4 } from "uuid"; import { basicContract, @@ -70,6 +70,7 @@ import { deleteFile, saveBuffer, saveFile, saveDRMFile } from "@/lib/file-stroag import { decryptWithServerAction } from "@/components/drm/drmUtils"; import { getServerSession } from "next-auth/next" import { authOptions } from "@/app/api/auth/[...nextauth]/route" +import { getSSLVWPurInqReqByRegNo } from "./sslvw-service"; // 템플릿 추가 @@ -858,6 +859,7 @@ export async function getBasicContractsByTemplateId( ) { // return unstable_cache( // async () => { + // 법무검토 요청 데이터 조회 하는 오라클 sql 문에서 id 연결한 상태값 가져와서 try { console.log(input.sort) @@ -906,22 +908,29 @@ export async function getBasicContractsByTemplateId( ) : [asc(basicContractView.createdAt)]; - // 트랜잭션 내부에서 Repository 호출 - const { data, total } = await db.transaction(async (tx) => { - const data = await selectBasicContractsById(tx, { - where, - orderBy, - offset, - limit: input.perPage, - }); + const queryOptions = { + where, + orderBy, + offset, + limit: input.perPage, + } - const total = await countBasicContractsById(tx, where); - return { data, total }; - }); + const initialResult = await loadBasicContractsByTemplate(queryOptions); + let finalData = initialResult.data; + let finalPageCount = Math.ceil(initialResult.total / input.perPage); - const pageCount = Math.ceil(total / input.perPage); + try { + const synced = await syncLegalReviewStatusesForContracts(finalData); + if (synced) { + const refreshedResult = await loadBasicContractsByTemplate(queryOptions); + finalData = refreshedResult.data; + finalPageCount = Math.ceil(refreshedResult.total / input.perPage); + } + } catch (syncError) { + console.error('[getBasicContractsByTemplateId] 법무 상태 동기화 실패:', syncError); + } - return { data, pageCount }; + return { data: finalData, pageCount: finalPageCount }; } catch (err) { // 에러 발생 시 디폴트\ console.log(err) @@ -936,6 +945,75 @@ export async function getBasicContractsByTemplateId( // )(); } +const extractStatusText = (value: unknown): string => { + if (typeof value === 'string') { + return value.trim() + } + return '' +} + +async function syncLegalReviewStatusesForContracts( + contracts: Array<{ + id: number + legalReviewRegNo: string | null + legalReviewProgressStatus?: string | null + }> +): Promise<boolean> { + const targets = contracts.filter((contract) => contract.legalReviewRegNo) + if (targets.length === 0) { + return false + } + + let hasUpdates = false + + for (const contract of targets) { + const regNo = contract.legalReviewRegNo! + const { success, data } = await getSSLVWPurInqReqByRegNo(regNo) + + if (!success || !data) { + continue + } + + const latestStatus = extractStatusText(data.PRGS_STAT_DSC ?? data.prgs_stat_dsc) + if (!latestStatus) { + continue + } + + if (latestStatus === extractStatusText(contract.legalReviewProgressStatus)) { + continue + } + + await persistLegalReviewStatus({ + contractId: contract.id, + regNo, + progressStatus: latestStatus, + }) + + hasUpdates = true + } + + return hasUpdates +} + +async function loadBasicContractsByTemplate(options: { + where: SQL<unknown> | undefined + orderBy: SQL<unknown>[] + offset: number + limit: number +}) { + return db.transaction(async (tx) => { + const data = await selectBasicContractsById(tx, { + where: options.where, + orderBy: options.orderBy, + offset: options.offset, + limit: options.limit, + }); + + const total = await countBasicContractsById(tx, options.where); + return { data, total }; + }); +} + export async function getAllTemplates(): Promise<BasicContractTemplate[]> { try { return await findAllTemplates(); @@ -2784,6 +2862,47 @@ export async function requestLegalReviewAction( } } +const persistLegalReviewStatus = async ({ + contractId, + regNo, + progressStatus, +}: { + contractId: number + regNo: string + progressStatus: string +}) => { + const now = new Date() + + await db.transaction(async (tx) => { + await tx + .update(basicContract) + .set({ + legalReviewRegNo: regNo, + legalReviewProgressStatus: progressStatus, + updatedAt: now, + }) + .where(eq(basicContract.id, contractId)) + + const existingLegalWork = await tx + .select({ id: legalWorks.id }) + .from(legalWorks) + .where(eq(legalWorks.basicContractId, contractId)) + .limit(1) + + if (existingLegalWork[0]) { + await tx + .update(legalWorks) + .set({ + status: progressStatus, + updatedAt: now, + }) + .where(eq(legalWorks.id, existingLegalWork[0].id)) + } + }) + + revalidateTag("basic-contracts") +} + /** * SSLVW 데이터로부터 법무검토 상태 업데이트 * @param sslvwData 선택된 SSLVW 데이터 배열 @@ -2791,7 +2910,7 @@ export async function requestLegalReviewAction( * @returns 성공 여부 및 메시지 */ export async function updateLegalReviewStatusFromSSLVW( - sslvwData: Array<{ VEND_CD?: string; PRGS_STAT_DSC?: string; [key: string]: any }>, + sslvwData: Array<{ REG_NO?: string; reg_no?: string; PRGS_STAT_DSC?: string; prgs_stat_dsc?: string; [key: string]: any }>, selectedContractIds: number[] ): Promise<{ success: boolean; message: string; updatedCount: number; errors: string[] }> { try { @@ -2815,90 +2934,92 @@ export async function updateLegalReviewStatusFromSSLVW( } } - // 선택된 계약서 정보 조회 - const selectedContracts = await db - .select({ - id: basicContractView.id, - vendorCode: basicContractView.vendorCode, - legalReviewStatus: basicContractView.legalReviewStatus - }) - .from(basicContractView) - .where(inArray(basicContractView.id, selectedContractIds)) - - let updatedCount = 0 - const errors: string[] = [] - - // 각 SSLVW 데이터에 대해 처리 - for (const sslvwItem of sslvwData) { - const vendorCode = sslvwItem.VEND_CD || sslvwItem.vendorCode - const prgsStatDsc = sslvwItem.PRGS_STAT_DSC || sslvwItem.prgsStatDsc + if (selectedContractIds.length !== 1) { + return { + success: false, + message: '한 개의 계약서만 선택해 주세요.', + updatedCount: 0, + errors: [] + } + } - if (!vendorCode || !prgsStatDsc) { - errors.push(`벤더코드 또는 상태 정보가 없습니다: ${JSON.stringify(sslvwItem)}`) - continue + if (sslvwData.length !== 1) { + return { + success: false, + message: '법무 시스템 데이터도 한 건만 선택해 주세요.', + updatedCount: 0, + errors: [] } + } - // 해당 벤더의 선택된 계약서들 찾기 - const contractsToUpdate = selectedContracts.filter(contract => - contract.vendorCode === vendorCode - ) + const contractId = selectedContractIds[0] + const sslvwItem = sslvwData[0] + const regNo = String( + sslvwItem.REG_NO ?? + sslvwItem.reg_no ?? + sslvwItem.RegNo ?? + '' + ).trim() + const progressStatus = String( + sslvwItem.PRGS_STAT_DSC ?? + sslvwItem.prgs_stat_dsc ?? + sslvwItem.PrgsStatDsc ?? + '' + ).trim() - if (contractsToUpdate.length === 0) { - console.log(`벤더 ${vendorCode}의 선택된 계약서가 없음`) - continue + if (!regNo) { + return { + success: false, + message: 'REG_NO 값을 찾을 수 없습니다.', + updatedCount: 0, + errors: [] } + } - // PRGS_STAT_DSC를 legalWorks.status로 매핑 - const statusMapping: Record<string, string> = { - '신규등록': '신규등록', - '검토요청': '검토요청', - '담당자배정': '담당자배정', - '검토중': '검토중', - '답변완료': '답변완료', - '재검토요청': '재검토요청', - '보류': '보류', - '취소': '취소' + if (!progressStatus) { + return { + success: false, + message: 'PRGS_STAT_DSC 값을 찾을 수 없습니다.', + updatedCount: 0, + errors: [] } + } - const mappedStatus = statusMapping[prgsStatDsc] || prgsStatDsc + const contract = await db + .select({ + id: basicContract.id, + legalReviewRegNo: basicContract.legalReviewRegNo, + }) + .from(basicContract) + .where(eq(basicContract.id, contractId)) + .limit(1) - // 각 계약서의 legalWorks 상태 업데이트 - for (const contract of contractsToUpdate) { - try { - const updateResult = await db - .update(legalWorks) - .set({ - status: mappedStatus, - updatedAt: new Date() - }) - .where(eq(legalWorks.basicContractId, contract.id)) - .returning({ id: legalWorks.id }) - - if (updateResult.length > 0) { - console.log(`법무작업 상태 업데이트: 계약서 ${contract.id}, 상태 ${mappedStatus}`) - updatedCount++ - } else { - console.log(`법무작업 레코드 없음: 계약서 ${contract.id}`) - errors.push(`계약서 ${contract.id}: 법무작업 레코드가 없습니다`) - } - } catch (contractError) { - console.error(`계약서 ${contract.id} 상태 업데이트 실패:`, contractError) - errors.push(`계약서 ${contract.id}: 업데이트 실패`) - } + if (!contract[0]) { + return { + success: false, + message: `계약서(${contractId})를 찾을 수 없습니다.`, + updatedCount: 0, + errors: [] } } - const message = updatedCount > 0 - ? `${updatedCount}건의 계약서 법무검토 상태가 업데이트되었습니다.` - : '업데이트된 계약서가 없습니다.' + if (contract[0].legalReviewRegNo && contract[0].legalReviewRegNo !== regNo) { + console.warn(`[updateLegalReviewStatusFromSSLVW] REG_NO가 변경됩니다: ${contract[0].legalReviewRegNo} -> ${regNo}`) + } + + await persistLegalReviewStatus({ + contractId, + regNo, + progressStatus, + }) - console.log(`[updateLegalReviewStatusFromSSLVW] 완료: ${message}`) + console.log(`[updateLegalReviewStatusFromSSLVW] 완료: 계약서 ${contractId}, REG_NO ${regNo}, 상태 ${progressStatus}`) return { - success: updatedCount > 0, - message, - updatedCount, - errors + success: true, + message: '법무검토 상태가 업데이트되었습니다.', + updatedCount: 1, + errors: [] } } catch (error) { @@ -2912,6 +3033,66 @@ export async function updateLegalReviewStatusFromSSLVW( } } +export async function refreshLegalReviewStatusFromOracle(contractId: number): Promise<{ + success: boolean + message: string + updated?: boolean +}> { + try { + const contract = await db + .select({ + id: basicContract.id, + legalReviewRegNo: basicContract.legalReviewRegNo, + legalReviewProgressStatus: basicContract.legalReviewProgressStatus, + }) + .from(basicContract) + .where(eq(basicContract.id, contractId)) + .limit(1) + + if (!contract[0]) { + return { success: false, message: '계약서를 찾을 수 없습니다.' } + } + + if (!contract[0].legalReviewRegNo) { + return { success: false, message: '연결된 REG_NO가 없습니다.' } + } + + const { success, data, error } = await getSSLVWPurInqReqByRegNo(contract[0].legalReviewRegNo) + + if (!success || !data) { + return { success: false, message: error ?? '법무 시스템 데이터를 찾을 수 없습니다.' } + } + + const latestStatus = String( + data.PRGS_STAT_DSC ?? + data.prgs_stat_dsc ?? + '' + ).trim() + + if (!latestStatus) { + return { success: false, message: '법무 시스템 상태 정보를 찾을 수 없습니다.' } + } + + if (contract[0].legalReviewProgressStatus === latestStatus) { + return { success: true, message: '변경된 상태가 없습니다.', updated: false } + } + + await persistLegalReviewStatus({ + contractId, + regNo: contract[0].legalReviewRegNo, + progressStatus: latestStatus, + }) + + return { success: true, message: '법무검토 상태가 최신 정보로 업데이트되었습니다.', updated: true } + } catch (error) { + console.error('[refreshLegalReviewStatusFromOracle] 오류:', error) + return { + success: false, + message: error instanceof Error ? error.message : '법무검토 상태 새로고침 중 오류가 발생했습니다.' + } + } +} + export async function resendContractsAction(contractIds: number[]) { try { // 세션 확인 diff --git a/lib/basic-contract/sslvw-service.ts b/lib/basic-contract/sslvw-service.ts index 9650d43a..38ecb67d 100644 --- a/lib/basic-contract/sslvw-service.ts +++ b/lib/basic-contract/sslvw-service.ts @@ -25,6 +25,22 @@ const FALLBACK_TEST_DATA: SSLVWPurInqReq[] = [ } ] +const normalizeOracleRows = (rows: Array<Record<string, unknown>>): SSLVWPurInqReq[] => { + return rows.map((item) => { + const convertedItem: SSLVWPurInqReq = {} + for (const [key, value] of Object.entries(item)) { + if (value instanceof Date) { + convertedItem[key] = value + } else if (value === null) { + convertedItem[key] = null + } else { + convertedItem[key] = String(value) + } + } + return convertedItem + }) +} + /** * SSLVW_PUR_INQ_REQ 테이블 전체 조회 * @returns 테이블 데이터 배열 @@ -51,19 +67,7 @@ export async function getSSLVWPurInqReqData(): Promise<{ console.log(`✅ [getSSLVWPurInqReqData] 조회 성공 - ${rows.length}건`) // 데이터 타입 변환 (필요에 따라 조정) - const cleanedResult = rows.map((item) => { - const convertedItem: SSLVWPurInqReq = {} - for (const [key, value] of Object.entries(item)) { - if (value instanceof Date) { - convertedItem[key] = value - } else if (value === null) { - convertedItem[key] = null - } else { - convertedItem[key] = String(value) - } - } - return convertedItem - }) + const cleanedResult = normalizeOracleRows(rows) return { success: true, @@ -80,3 +84,49 @@ export async function getSSLVWPurInqReqData(): Promise<{ } } } + +export async function getSSLVWPurInqReqByRegNo(regNo: string): Promise<{ + success: boolean + data?: SSLVWPurInqReq + error?: string +}> { + if (!regNo) { + return { + success: false, + error: 'REG_NO는 필수입니다.' + } + } + + try { + console.log(`[getSSLVWPurInqReqByRegNo] REG_NO=${regNo} 조회`) + const result = await oracleKnex.raw( + ` + SELECT * + FROM SSLVW_PUR_INQ_REQ + WHERE REG_NO = :regNo + `, + { regNo } + ) + + const rows = (result.rows || result) as Array<Record<string, unknown>> + const cleanedResult = normalizeOracleRows(rows) + + if (cleanedResult.length === 0) { + return { + success: false, + error: '해당 REG_NO에 대한 데이터가 없습니다.' + } + } + + return { + success: true, + data: cleanedResult[0] + } + } catch (error) { + console.error('[getSSLVWPurInqReqByRegNo] 오류:', error) + return { + success: false, + error: error instanceof Error ? error.message : 'REG_NO 조회 중 오류가 발생했습니다.' + } + } +} 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..b2cc5055 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) + } - // RED FLAG 해소요청 가능한 계약서들 - const redFlagResolutionContracts = selectedRows + const redFlagEligibleContracts = selectedRows .map(row => row.original) - .filter(contract => redFlagData[contract.id] === true) + .filter(contract => { + if (redFlagData[contract.id] !== true) return false + return !hasPendingResolution(contract.id) + }) + + const redFlagPendingContracts = selectedRows + .map(row => row.original) + .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 해소요청 결재가 상신되었습니다.", { @@ -503,13 +539,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,7 +568,16 @@ export function BasicContractDetailTableToolbarActions({ </Button> {/* 법무검토 버튼 (SSLVW 데이터 조회) */} - <SSLVWPurInqReqDialog onConfirm={handleSSLVWConfirm} /> + <SSLVWPurInqReqDialog + onConfirm={handleSSLVWConfirm} + requireSingleSelection + triggerDisabled={selectedRows.length !== 1 || loading} + triggerTitle={ + selectedRows.length !== 1 + ? "계약서 한 건을 선택해주세요" + : undefined + } + /> {/* 법무검토 요청 버튼 */} <Button diff --git a/lib/basic-contract/status-detail/basic-contracts-detail-columns.tsx b/lib/basic-contract/status-detail/basic-contracts-detail-columns.tsx index 2ab39880..d03d0720 100644 --- a/lib/basic-contract/status-detail/basic-contracts-detail-columns.tsx +++ b/lib/basic-contract/status-detail/basic-contracts-detail-columns.tsx @@ -531,50 +531,14 @@ export function getDetailColumns({ ), cell: ({ row }) => { const status = row.getValue("legalReviewStatus") as string | null - const contract = row.original - const requestedDate = contract.legalReviewRequestedAt as Date | null - const completedDate = contract.legalReviewCompletedAt as Date | null - // 법무검토 상태 우선, 없으면 기존 로직으로 판단 + // PRGS_STAT_DSC 연동값 우선 표시 if (status) { - const statusColors: Record<string, string> = { - '신규등록': 'text-blue-600', - '검토요청': 'text-purple-600', - '담당자배정': 'text-orange-600', - '검토중': 'text-yellow-600', - '답변완료': 'text-green-600', - '재검토요청': 'text-red-600', - '보류': 'text-gray-500', - '취소': 'text-red-700' - } - - return ( - <div className={`text-sm ${statusColors[status] || 'text-gray-600'}`}> - <div className="font-medium">{status}</div> - </div> - ) + return <div className="text-sm text-gray-800">{status}</div> } - // legalWorks에 데이터가 없는 경우 기존 로직 사용 - if (completedDate) { - return ( - <div className="text-sm text-green-600"> - <div className="font-medium">완료</div> - <div className="text-xs">{formatDateTime(completedDate, "KR")}</div> - </div> - ) - } else if (requestedDate) { - return ( - <div className="text-sm text-orange-600"> - <div className="font-medium">진행중</div> - <div className="text-xs">검토 대기</div> - </div> - ) - } else { - return ( - <div className="text-sm text-gray-400">-</div> - ) - } + // 동기화된 값이 없으면 빈 값 처리 + return <div className="text-sm text-gray-400">-</div> }, minSize: 140, }, diff --git a/lib/basic-contract/status-detail/basic-contracts-detail-table.tsx b/lib/basic-contract/status-detail/basic-contracts-detail-table.tsx index 010b4713..a2e1c5e4 100644 --- a/lib/basic-contract/status-detail/basic-contracts-detail-table.tsx +++ b/lib/basic-contract/status-detail/basic-contracts-detail-table.tsx @@ -266,6 +266,7 @@ type RedFlagResolutionState = { table={table} gtcData={gtcData} redFlagData={redFlagData} + redFlagResolutionData={redFlagResolutionData} isComplianceTemplate={isComplianceTemplate} /> </DataTableAdvancedToolbar> diff --git a/lib/basic-contract/viewer/basic-contract-sign-viewer.tsx b/lib/basic-contract/viewer/basic-contract-sign-viewer.tsx index 6bf0dfb1..8185e33e 100644 --- a/lib/basic-contract/viewer/basic-contract-sign-viewer.tsx +++ b/lib/basic-contract/viewer/basic-contract-sign-viewer.tsx @@ -1411,7 +1411,7 @@ export function BasicContractSignViewer({ {file.name} </span> {isSurveyTab && !surveyData.completed && ( - <Badge variant="destructive" className="ml-1 h-4 px-1.5 text-xs bg-amber-500 text-white"> + <Badge variant="destructive" className="ml-1 h-4 px-1.5 text-xs bg-red-500 text-white transition-none hover:bg-red-500"> 필수 </Badge> )} @@ -1640,7 +1640,7 @@ export function BasicContractSignViewer({ {file.name} </span> {isSurveyTab && !surveyData.completed && ( - <Badge variant="destructive" className="ml-1 h-4 px-1.5 text-xs bg-amber-500 text-white"> + <Badge variant="destructive" className="ml-1 h-4 px-1.5 text-xs bg-red-500 text-white transition-none hover:bg-red-500"> 필수 </Badge> )} |
