diff options
Diffstat (limited to 'lib/vendors')
| -rw-r--r-- | lib/vendors/service.ts | 7 | ||||
| -rw-r--r-- | lib/vendors/table/approve-vendor-dialog.tsx | 31 | ||||
| -rw-r--r-- | lib/vendors/table/request-pq-dialog.tsx | 115 | ||||
| -rw-r--r-- | lib/vendors/table/vendors-table-toolbar-actions.tsx | 2 |
4 files changed, 128 insertions, 27 deletions
diff --git a/lib/vendors/service.ts b/lib/vendors/service.ts index 4cca3b12..e3a38891 100644 --- a/lib/vendors/service.ts +++ b/lib/vendors/service.ts @@ -1576,7 +1576,7 @@ export async function approveVendors(input: ApproveVendorsInput & { userId: numb // 기존 사용자-역할 관계 확인 const existingUserRole = await tx - .select({ id: userRoles.id }) + .select({ userId: userRoles.userId }) .from(userRoles) .where( and( @@ -1621,7 +1621,10 @@ export async function approveVendors(input: ApproveVendorsInput & { userId: numb try { // 사용자 언어 확인 const userInfo = await tx - .select({ language: users.language }) + .select({ + id: users.id, + language: users.language + }) .from(users) .where(eq(users.email, vendor.email)) .limit(1); diff --git a/lib/vendors/table/approve-vendor-dialog.tsx b/lib/vendors/table/approve-vendor-dialog.tsx index 9c175dc5..940710f5 100644 --- a/lib/vendors/table/approve-vendor-dialog.tsx +++ b/lib/vendors/table/approve-vendor-dialog.tsx @@ -55,20 +55,29 @@ export function ApproveVendorsDialog({ } startApproveTransition(async () => { - const { error } = await approveVendors({ - ids: vendors.map((vendor) => vendor.id), - userId: Number(session.user.id) + try { + console.log("🔍 [DEBUG] 승인 요청 시작 - vendors:", vendors.map(v => ({ id: v.id, vendorName: v.vendorName, email: v.email }))); + console.log("🔍 [DEBUG] 세션 정보:", { userId: session.user.id, userType: typeof session.user.id }); + + const { error } = await approveVendors({ + ids: vendors.map((vendor) => vendor.id), + userId: Number(session.user.id) + }) - }) + if (error) { + console.error("🚨 [DEBUG] 승인 처리 에러:", error); + toast.error(error) + return + } - if (error) { - toast.error(error) - return + console.log("✅ [DEBUG] 승인 처리 성공"); + props.onOpenChange?.(false) + toast.success("Vendors successfully approved for review") + onSuccess?.() + } catch (error) { + console.error("🚨 [DEBUG] 예상치 못한 에러:", error); + toast.error("예상치 못한 오류가 발생했습니다.") } - - props.onOpenChange?.(false) - toast.success("Vendors successfully approved for review") - onSuccess?.() }) } diff --git a/lib/vendors/table/request-pq-dialog.tsx b/lib/vendors/table/request-pq-dialog.tsx index 20388f71..14a1cd01 100644 --- a/lib/vendors/table/request-pq-dialog.tsx +++ b/lib/vendors/table/request-pq-dialog.tsx @@ -37,6 +37,7 @@ import { Checkbox } from "@/components/ui/checkbox" import { Label } from "@/components/ui/label"
import { Input } from "@/components/ui/input"
import { Badge } from "@/components/ui/badge"
+import { Progress } from "@/components/ui/progress"
import { Vendor } from "@/db/schema/vendors"
import { requestBasicContractInfo, requestPQVendors } from "../service"
import { getProjectsWithPQList } from "@/lib/pq/service"
@@ -98,6 +99,11 @@ export function RequestPQDialog({ vendors, showTrigger = true, onSuccess, ...pro // 비밀유지 계약서 첨부파일 관련 상태
const [ndaAttachments, setNdaAttachments] = React.useState<File[]>([])
const [isUploadingNdaFiles, setIsUploadingNdaFiles] = React.useState(false)
+
+ // 프로그레스 관련 상태
+ const [progressValue, setProgressValue] = React.useState(0)
+ const [currentStep, setCurrentStep] = React.useState("")
+ const [showProgress, setShowProgress] = React.useState(false)
// 아이템 검색 필터링
React.useEffect(() => {
@@ -180,6 +186,9 @@ export function RequestPQDialog({ vendors, showTrigger = true, onSuccess, ...pro setShowItemDropdown(false)
setNdaAttachments([])
setIsUploadingNdaFiles(false)
+ setProgressValue(0)
+ setCurrentStep("")
+ setShowProgress(false)
}
}, [props.open])
@@ -235,9 +244,22 @@ export function RequestPQDialog({ vendors, showTrigger = true, onSuccess, ...pro if (!dueDate) return toast.error("마감일을 선택하세요.")
if (!session?.user?.id) return toast.error("인증 실패")
+ // 프로그레스 바를 즉시 표시
+ setShowProgress(true)
+ setProgressValue(0)
+ setCurrentStep("시작 중...")
+
startApproveTransition(async () => {
try {
+
+ // 전체 단계 수 계산
+ const totalSteps = 1 +
+ (selectedTemplateIds.length > 0 ? 1 : 0) +
+ (isNdaTemplateSelected() && ndaAttachments.length > 0 ? 1 : 0)
+ let completedSteps = 0
+
// 1단계: PQ 생성
+ setCurrentStep("PQ 생성 중...")
console.log("🚀 PQ 생성 시작")
const { error: pqError } = await requestPQVendors({
ids: vendors.map((v) => v.id),
@@ -252,9 +274,13 @@ export function RequestPQDialog({ vendors, showTrigger = true, onSuccess, ...pro })
if (pqError) {
+ setShowProgress(false)
toast.error(`PQ 생성 실패: ${pqError}`)
return
}
+
+ completedSteps++
+ setProgressValue((completedSteps / totalSteps) * 100)
console.log("✅ PQ 생성 완료")
toast.success("PQ가 성공적으로 요청되었습니다")
@@ -264,12 +290,17 @@ export function RequestPQDialog({ vendors, showTrigger = true, onSuccess, ...pro selectedTemplateIds.includes(t.id)
)
+ setCurrentStep(`기본계약서 생성 중... (${templates.length}개 템플릿)`)
console.log("📋 기본계약서 백그라운드 처리 시작", templates.length, "개 템플릿")
await processBasicContractsInBackground(templates, vendors)
+
+ completedSteps++
+ setProgressValue((completedSteps / totalSteps) * 100)
}
// 3단계: 비밀유지 계약서 첨부파일이 있는 경우 저장
if (isNdaTemplateSelected() && ndaAttachments.length > 0) {
+ setCurrentStep(`비밀유지 계약서 첨부파일 저장 중... (${ndaAttachments.length}개 파일)`)
console.log("📎 비밀유지 계약서 첨부파일 처리 시작", ndaAttachments.length, "개 파일")
const ndaResult = await saveNdaAttachments({
@@ -283,14 +314,24 @@ export function RequestPQDialog({ vendors, showTrigger = true, onSuccess, ...pro } else {
toast.error(`첨부파일 처리 중 일부 오류가 발생했습니다: ${ndaResult.error}`)
}
+
+ completedSteps++
+ setProgressValue((completedSteps / totalSteps) * 100)
}
- // 완료 후 다이얼로그 닫기
- props.onOpenChange?.(false)
- onSuccess?.()
+ setCurrentStep("완료!")
+ setProgressValue(100)
+
+ // 잠시 완료 상태를 보여준 후 다이얼로그 닫기
+ setTimeout(() => {
+ setShowProgress(false)
+ props.onOpenChange?.(false)
+ onSuccess?.()
+ }, 1000)
} catch (error) {
console.error('PQ 생성 오류:', error)
+ setShowProgress(false)
toast.error(`처리 중 오류가 발생했습니다: ${error instanceof Error ? error.message : '알 수 없는 오류'}`)
}
})
@@ -328,6 +369,13 @@ export function RequestPQDialog({ vendors, showTrigger = true, onSuccess, ...pro const template = templates[templateIndex]
processedCount++
+ // 진행률 업데이트 (2단계 범위 내에서)
+ const baseProgress = 33.33 // 1단계 완료 후
+ const contractProgress = (processedCount / totalContracts) * 33.33 // 2단계는 33.33% 차지
+ const newProgress = baseProgress + contractProgress
+ setProgressValue(newProgress)
+ setCurrentStep(`기본계약서 생성 중... (${processedCount}/${totalContracts})`)
+
console.log(`📄 처리 중: ${vendor.vendorName} - ${template.templateName} (${processedCount}/${totalContracts})`)
// 개별 벤더에 대한 기본계약 생성
@@ -720,11 +768,31 @@ export function RequestPQDialog({ vendors, showTrigger = true, onSuccess, ...pro <div className="flex-1 overflow-y-auto">
{dialogContent}
</div>
- <DialogFooter>
- <DialogClose asChild><Button variant="outline">취소</Button></DialogClose>
- <Button onClick={onApprove} disabled={isApprovePending || !type || (type === "PROJECT" && !selectedProjectId)}>
- {isApprovePending && <Loader className="mr-2 size-4 animate-spin" />}요청하기
- </Button>
+ <DialogFooter className="flex-col gap-4">
+ {/* 프로그레스 바 */}
+ {(showProgress || isApprovePending) && (
+ <div className="w-full space-y-2">
+ <div className="flex items-center justify-between text-sm">
+ <span className="text-muted-foreground">{currentStep || "처리 중..."}</span>
+ <span className="font-medium">{Math.round(progressValue)}%</span>
+ </div>
+ <Progress value={progressValue} className="w-full" />
+ </div>
+ )}
+
+ {/* 버튼들 */}
+ <div className="flex justify-end gap-2">
+ <DialogClose asChild>
+ <Button variant="outline" disabled={isApprovePending}>취소</Button>
+ </DialogClose>
+ <Button
+ onClick={onApprove}
+ disabled={isApprovePending || !type || (type === "PROJECT" && !selectedProjectId)}
+ >
+ {isApprovePending && <Loader className="mr-2 size-4 animate-spin" />}
+ 요청하기
+ </Button>
+ </div>
</DialogFooter>
</DialogContent>
@@ -753,11 +821,32 @@ export function RequestPQDialog({ vendors, showTrigger = true, onSuccess, ...pro <div className="flex-1 overflow-y-auto px-4">
{dialogContent}
</div>
- <DrawerFooter>
- <DrawerClose asChild><Button variant="outline">취소</Button></DrawerClose>
- <Button onClick={onApprove} disabled={isApprovePending || !type || (type === "PROJECT" && !selectedProjectId)}>
- {isApprovePending && <Loader className="mr-2 size-4 animate-spin" />}요청하기
- </Button>
+ <DrawerFooter className="gap-4">
+ {/* 프로그레스 바 */}
+ {(showProgress || isApprovePending) && (
+ <div className="w-full space-y-2">
+ <div className="flex items-center justify-between text-sm">
+ <span className="text-muted-foreground">{currentStep || "처리 중..."}</span>
+ <span className="font-medium">{Math.round(progressValue)}%</span>
+ </div>
+ <Progress value={progressValue} className="w-full" />
+ </div>
+ )}
+
+ {/* 버튼들 */}
+ <div className="flex gap-2">
+ <DrawerClose asChild>
+ <Button variant="outline" disabled={isApprovePending} className="flex-1">취소</Button>
+ </DrawerClose>
+ <Button
+ onClick={onApprove}
+ disabled={isApprovePending || !type || (type === "PROJECT" && !selectedProjectId)}
+ className="flex-1"
+ >
+ {isApprovePending && <Loader className="mr-2 size-4 animate-spin" />}
+ 요청하기
+ </Button>
+ </div>
</DrawerFooter>
</DrawerContent>
diff --git a/lib/vendors/table/vendors-table-toolbar-actions.tsx b/lib/vendors/table/vendors-table-toolbar-actions.tsx index 6d5f7425..3d77486d 100644 --- a/lib/vendors/table/vendors-table-toolbar-actions.tsx +++ b/lib/vendors/table/vendors-table-toolbar-actions.tsx @@ -83,7 +83,7 @@ export function VendorsTableToolbarActions({ table }: VendorsTableToolbarActions .rows .map(row => row.original) .filter(vendor => - ["PENDING_REVIEW", "IN_REVIEW", "IN_PQ", "PQ_APPROVED", "APPROVED", "READY_TO_SEND", "ACTIVE"].includes(vendor.status) + ["IN_REVIEW", "IN_PQ", "PQ_APPROVED", "APPROVED", "READY_TO_SEND", "ACTIVE"].includes(vendor.status) ); }, [table.getFilteredSelectedRowModel().rows]); |
