summaryrefslogtreecommitdiff
path: root/lib/vendors/table/approve-vendor-dialog.tsx
diff options
context:
space:
mode:
Diffstat (limited to 'lib/vendors/table/approve-vendor-dialog.tsx')
-rw-r--r--lib/vendors/table/approve-vendor-dialog.tsx185
1 files changed, 150 insertions, 35 deletions
diff --git a/lib/vendors/table/approve-vendor-dialog.tsx b/lib/vendors/table/approve-vendor-dialog.tsx
index 786399a4..fea5a006 100644
--- a/lib/vendors/table/approve-vendor-dialog.tsx
+++ b/lib/vendors/table/approve-vendor-dialog.tsx
@@ -29,8 +29,10 @@ import {
} from "@/components/ui/drawer"
import { Vendor } from "@/db/schema/vendors"
import { rejectVendors } from "../service"
-import { approveVendorsWithApproval } from "../approval-actions"
+import { approveVendorsWithApproval, prepareVendorApprovalVariables } from "../approval-actions"
import { useSession } from "next-auth/react"
+import { checkVendorsBlacklist } from "../blacklist-check"
+import { ApprovalPreviewDialog } from "@/lib/approval/client"
interface VendorDecisionDialogProps
extends React.ComponentPropsWithoutRef<typeof Dialog> {
@@ -50,6 +52,17 @@ export function VendorDecisionDialog({
const isDesktop = useMediaQuery("(min-width: 640px)")
const { data: session } = useSession()
+ // 결재 미리보기 다이얼로그 상태
+ const [showPreview, setShowPreview] = React.useState(false)
+ const [previewData, setPreviewData] = React.useState<{
+ variables: Record<string, string>
+ title: string
+ description: string
+ } | null>(null)
+
+ /**
+ * 승인 버튼 클릭 - 미리보기 다이얼로그 열기
+ */
function onApprove() {
if (!session?.user?.id) {
toast.error("사용자 인증 정보를 찾을 수 없습니다.")
@@ -63,37 +76,97 @@ export function VendorDecisionDialog({
startApproveTransition(async () => {
try {
- console.log("🔍 [DEBUG] 결재 상신 시작 - vendors:", vendors.map(v => ({ id: v.id, vendorName: v.vendorName, email: v.email })));
- console.log("🔍 [DEBUG] 세션 정보:", { userId: session.user.id, epId: session.user.epId });
+ // 1. 블랙리스트 검사 (최우선 처리)
+ const vendorsToCheck = vendors.map(v => ({
+ id: String(v.id),
+ name: v.vendorName || '',
+ representativeName: v.representativeName,
+ representativeBirth: v.representativeBirth,
+ }));
+
+ const blacklistCheckResult = await checkVendorsBlacklist(vendorsToCheck);
- const result = await approveVendorsWithApproval({
- vendorIds: vendors.map((vendor) => vendor.id),
- currentUser: {
- id: Number(session.user.id),
- epId: session.user.epId as string, // 위에서 검증했으므로 타입 단언
- email: session.user.email || undefined,
- },
- // TODO: 필요시 approvers 배열 추가
- // approvers: ['EP001', 'EP002'],
- })
-
- if (!result.success) {
- console.error("🚨 [DEBUG] 결재 상신 에러:", result.message);
- toast.error(result.message || "결재 상신에 실패했습니다.")
- return
+ if (!blacklistCheckResult.success) {
+ // 블랙리스트에 있는 벤더 목록 표시
+ const blacklistedNames = blacklistCheckResult.blacklistedVendors
+ .map(v => `• ${v.name}: ${v.message}`)
+ .join('\n');
+
+ toast.error(
+ `문제가 있는 데이터가 있습니다:\n${blacklistedNames}`,
+ { duration: 10000 }
+ );
+ return;
}
- console.log("✅ [DEBUG] 결재 상신 성공:", result);
- props.onOpenChange?.(false)
- toast.success(`결재가 상신되었습니다. (결재ID: ${result.approvalId})`)
- onSuccess?.()
+ // 2. 템플릿 변수 준비
+ const { variables, vendorRecords, vendorNames } = await prepareVendorApprovalVariables(
+ vendors.map(v => v.id),
+ session.user.email || undefined
+ );
+
+ // 3. 미리보기 데이터 설정
+ setPreviewData({
+ variables,
+ title: `벤더 가입 승인 요청 - ${vendorRecords.length}개 업체`,
+ description: `${vendorNames} 의 가입을 승인합니다.`,
+ });
+
+ // 4. 미리보기 다이얼로그 열기
+ setShowPreview(true);
+
} catch (error) {
- console.error("🚨 [DEBUG] 예상치 못한 에러:", error);
- toast.error("예상치 못한 오류가 발생했습니다.")
+ console.error("🚨 [Vendor Decision] 미리보기 준비 실패:", error);
+ toast.error(error instanceof Error ? error.message : "미리보기를 준비하는 중 오류가 발생했습니다.")
}
})
}
+ /**
+ * 결재 미리보기에서 확인 클릭 - 실제 결재 상신
+ */
+ async function handleApprovalConfirm(approvalData: {
+ approvers: string[]
+ title: string
+ description?: string
+ }) {
+ if (!session?.user?.id || !session?.user?.epId) {
+ toast.error("사용자 인증 정보가 없습니다.")
+ return
+ }
+
+ try {
+ const result = await approveVendorsWithApproval({
+ vendorIds: vendors.map((vendor) => vendor.id),
+ currentUser: {
+ id: Number(session.user.id),
+ epId: session.user.epId,
+ email: session.user.email || undefined,
+ },
+ approvers: approvalData.approvers, // 미리보기에서 설정한 결재선
+ })
+
+ if (!result.success) {
+ console.error("🚨 [Vendor Decision] 결재 상신 에러:", result.message);
+ toast.error(result.message || "결재 상신에 실패했습니다.")
+ return
+ }
+
+ console.log("✅ [Vendor Decision] 결재 상신 성공:", result);
+
+ // 다이얼로그 모두 닫기
+ setShowPreview(false)
+ props.onOpenChange?.(false)
+
+ toast.success(`결재가 상신되었습니다. (결재ID: ${result.approvalId})`)
+ onSuccess?.()
+
+ } catch (error) {
+ console.error("🚨 [Vendor Decision] 예상치 못한 에러:", error);
+ toast.error("예상치 못한 오류가 발생했습니다.")
+ }
+ }
+
function onReject() {
if (!session?.user?.id) {
toast.error("사용자 인증 정보를 찾을 수 없습니다.")
@@ -129,16 +202,17 @@ export function VendorDecisionDialog({
if (isDesktop) {
return (
- <Dialog {...props}>
- {showTrigger ? (
- <DialogTrigger asChild>
- <Button variant="outline" size="sm" className="gap-2">
- <Check className="size-4" aria-hidden="true" />
- 가입 결정 ({vendors.length})
- </Button>
- </DialogTrigger>
- ) : null}
- <DialogContent className="max-w-2xl">
+ <>
+ <Dialog {...props}>
+ {showTrigger ? (
+ <DialogTrigger asChild>
+ <Button variant="outline" size="sm" className="gap-2">
+ <Check className="size-4" aria-hidden="true" />
+ 가입 결정 ({vendors.length})
+ </Button>
+ </DialogTrigger>
+ ) : null}
+ <DialogContent className="max-w-2xl">
<DialogHeader>
<DialogTitle>협력업체 가입 결정</DialogTitle>
<DialogDescription>
@@ -199,11 +273,32 @@ export function VendorDecisionDialog({
</DialogFooter>
</DialogContent>
</Dialog>
+
+ {/* 결재 미리보기 다이얼로그 */}
+ {previewData && session?.user?.epId && (
+ <ApprovalPreviewDialog
+ open={showPreview}
+ onOpenChange={setShowPreview}
+ templateName="벤더 가입 승인 요청"
+ variables={previewData.variables}
+ title={previewData.title}
+ description={previewData.description}
+ currentUser={{
+ id: Number(session.user.id),
+ epId: session.user.epId,
+ name: session.user.name || undefined,
+ email: session.user.email || undefined,
+ }}
+ onConfirm={handleApprovalConfirm}
+ />
+ )}
+ </>
)
}
return (
- <Drawer {...props}>
+ <>
+ <Drawer {...props}>
{showTrigger ? (
<DrawerTrigger asChild>
<Button variant="outline" size="sm" className="gap-2">
@@ -272,5 +367,25 @@ export function VendorDecisionDialog({
</DrawerFooter>
</DrawerContent>
</Drawer>
+
+ {/* 결재 미리보기 다이얼로그 */}
+ {previewData && session?.user?.epId && (
+ <ApprovalPreviewDialog
+ open={showPreview}
+ onOpenChange={setShowPreview}
+ templateName="벤더 가입 승인 요청"
+ variables={previewData.variables}
+ title={previewData.title}
+ description={previewData.description}
+ currentUser={{
+ id: Number(session.user.id),
+ epId: session.user.epId,
+ name: session.user.name || undefined,
+ email: session.user.email || undefined,
+ }}
+ onConfirm={handleApprovalConfirm}
+ />
+ )}
+ </>
)
} \ No newline at end of file