summaryrefslogtreecommitdiff
path: root/lib/bidding/failure
diff options
context:
space:
mode:
Diffstat (limited to 'lib/bidding/failure')
-rw-r--r--lib/bidding/failure/biddings-closure-dialog.tsx77
-rw-r--r--lib/bidding/failure/biddings-failure-table.tsx81
2 files changed, 129 insertions, 29 deletions
diff --git a/lib/bidding/failure/biddings-closure-dialog.tsx b/lib/bidding/failure/biddings-closure-dialog.tsx
index 64aba42f..93ba0eda 100644
--- a/lib/bidding/failure/biddings-closure-dialog.tsx
+++ b/lib/bidding/failure/biddings-closure-dialog.tsx
@@ -2,8 +2,9 @@
"use client"
import { useState } from "react"
+import { useSession } from "next-auth/react"
import { toast } from "sonner"
-import { bidClosureAction } from "@/lib/bidding/actions"
+import { requestBiddingClosureWithApproval } from "@/lib/bidding/approval-actions"
import { Dialog, DialogContent, DialogDescription, DialogHeader, DialogTitle } from "@/components/ui/dialog"
import { Label } from "@/components/ui/label"
import { Textarea } from "@/components/ui/textarea"
@@ -19,17 +20,29 @@ interface BiddingsClosureDialogProps {
title: string;
biddingNumber: string;
} | null;
- userId: string;
onSuccess?: () => void;
+ onApprovalPreview?: (data: {
+ templateName: string
+ variables: Record<string, string>
+ title: string
+ description: string
+ files?: File[]
+ }) => void
}
+
+interface ClosureFormData {
+ description: string;
+ files: File[];
+}
export function BiddingsClosureDialog({
open,
onOpenChange,
bidding,
- userId,
- onSuccess
+ onSuccess,
+ onApprovalPreview
}: BiddingsClosureDialogProps) {
+ const { data: session } = useSession()
const [description, setDescription] = useState('')
const [files, setFiles] = useState<File[]>([])
const [isSubmitting, setIsSubmitting] = useState(false)
@@ -42,36 +55,44 @@ interface BiddingsClosureDialogProps {
return
}
- setIsSubmitting(true)
-
+ // 결재 템플릿 변수 준비
+ const { mapBiddingClosureToTemplateVariables } = await import('@/lib/bidding/handlers')
+
try {
- const result = await bidClosureAction(bidding.id, {
+ const variables = await mapBiddingClosureToTemplateVariables({
+ biddingId: bidding.id,
description: description.trim(),
- files
- }, userId)
-
- if (result.success) {
- toast.success(result.message)
- onOpenChange(false)
- onSuccess?.()
- // 페이지 새로고침 또는 상태 업데이트
- window.location.reload()
- } else {
- toast.error(result.error || '폐찰 처리 중 오류가 발생했습니다.')
+ requestedAt: new Date()
+ })
+
+ // 상위 컴포넌트로 결재 미리보기 데이터 전달
+ if (onApprovalPreview) {
+ onApprovalPreview({
+ templateName: '폐찰 품의 요청서',
+ variables,
+ title: `폐찰 - ${bidding.title}`,
+ description: description.trim(),
+ files
+ })
}
+
+ onOpenChange(false)
+ // 폼 초기화
+ setDescription('')
+ setFiles([])
} catch (error) {
- toast.error('폐찰 처리 중 오류가 발생했습니다.')
- } finally {
- setIsSubmitting(false)
+ console.error('폐찰 템플릿 변수 준비 실패:', error)
+ toast.error('결재 문서 준비 중 오류가 발생했습니다.')
}
}
-
+
const handleFileChange = (e: React.ChangeEvent<HTMLInputElement>) => {
if (e.target.files) {
setFiles(Array.from(e.target.files))
}
}
-
+
+
if (!bidding) return null
return (
@@ -99,7 +120,7 @@ interface BiddingsClosureDialogProps {
required
/>
</div>
-
+
<div className="space-y-2">
<Label htmlFor="files">첨부파일</Label>
<Input
@@ -116,7 +137,7 @@ interface BiddingsClosureDialogProps {
</div>
)}
</div>
-
+
<div className="flex justify-end gap-2 pt-4">
<Button
type="button"
@@ -131,12 +152,12 @@ interface BiddingsClosureDialogProps {
variant="destructive"
disabled={isSubmitting || !description.trim()}
>
- {isSubmitting ? '처리 중...' : '폐찰하기'}
+ {isSubmitting ? '상신 중...' : '결재 상신'}
</Button>
</div>
</form>
</DialogContent>
</Dialog>
- )
- }
+ </>
+ )
\ No newline at end of file
diff --git a/lib/bidding/failure/biddings-failure-table.tsx b/lib/bidding/failure/biddings-failure-table.tsx
index 43020322..a0f98466 100644
--- a/lib/bidding/failure/biddings-failure-table.tsx
+++ b/lib/bidding/failure/biddings-failure-table.tsx
@@ -24,6 +24,8 @@ import { Dialog, DialogContent, DialogDescription, DialogFooter, DialogHeader, D
import { FileX, RefreshCw, Undo2 } from "lucide-react"
import { bidClosureAction, cancelDisposalAction } from "@/lib/bidding/actions"
import { increaseRoundOrRebid } from "@/lib/bidding/service"
+import { ApprovalPreviewDialog } from "@/lib/approval/approval-preview-dialog"
+import { requestBiddingClosureWithApproval } from "@/lib/bidding/approval-actions"
import { useToast } from "@/hooks/use-toast"
type BiddingFailureItem = {
@@ -88,6 +90,14 @@ export function BiddingsFailureTable({ promises }: BiddingsFailureTableProps) {
const [selectedBidding, setSelectedBidding] = React.useState<BiddingFailureItem | null>(null)
const [isRebidDialogOpen, setIsRebidDialogOpen] = React.useState(false)
const [selectedBiddingForRebid, setSelectedBiddingForRebid] = React.useState<BiddingFailureItem | null>(null)
+ const [approvalPreviewData, setApprovalPreviewData] = React.useState<{
+ templateName: string
+ variables: Record<string, string>
+ title: string
+ description: string
+ files?: File[]
+ } | null>(null)
+ const [isApprovalPreviewDialogOpen, setIsApprovalPreviewDialogOpen] = React.useState(false)
const { toast } = useToast()
const [rowAction, setRowAction] = React.useState<DataTableRowAction<BiddingFailureItem> | null>(null)
@@ -424,11 +434,14 @@ export function BiddingsFailureTable({ promises }: BiddingsFailureTableProps) {
open={biddingClosureDialogOpen}
onOpenChange={handleBiddingClosureDialogClose}
bidding={selectedBidding}
- userId={session.user.id}
onSuccess={() => {
router.refresh()
handleBiddingClosureDialogClose()
}}
+ onApprovalPreview={(data) => {
+ setApprovalPreviewData(data)
+ setIsApprovalPreviewDialogOpen(true)
+ }}
/>
)}
@@ -465,6 +478,72 @@ export function BiddingsFailureTable({ promises }: BiddingsFailureTableProps) {
</DialogFooter>
</DialogContent>
</Dialog>
+
+ {/* 폐찰 결재 미리보기 다이얼로그 */}
+ {session?.user && session.user.epId && approvalPreviewData && (
+ <ApprovalPreviewDialog
+ open={isApprovalPreviewDialogOpen}
+ onOpenChange={(open) => {
+ setIsApprovalPreviewDialogOpen(open)
+ if (!open) {
+ setApprovalPreviewData(null)
+ }
+ }}
+ templateName={approvalPreviewData.templateName}
+ variables={approvalPreviewData.variables}
+ title={approvalPreviewData.title}
+ currentUser={{
+ id: Number(session.user.id),
+ epId: session.user.epId,
+ name: session.user.name || undefined,
+ email: session.user.email || undefined
+ }}
+ onConfirm={handleClosureApprovalConfirm}
+ />
+ )}
</>
)
+
+ // 폐찰 결재 상신 핸들러
+ const handleClosureApprovalConfirm = async (data: { approvers: string[]; title: string; attachments?: File[] }) => {
+ if (!session?.user?.id || !approvalPreviewData || !selectedBidding) return
+
+ try {
+ const result = await requestBiddingClosureWithApproval({
+ biddingId: selectedBidding.id,
+ description: approvalPreviewData.description,
+ files: approvalPreviewData.files,
+ currentUser: {
+ id: Number(session.user.id),
+ epId: session.user.epId || null,
+ email: session.user.email || undefined
+ },
+ approvers: data.approvers,
+ })
+
+ if (result.status === 'pending_approval') {
+ toast({
+ title: '성공',
+ description: `폐찰 결재가 상신되었습니다. (ID: ${result.approvalId})`,
+ })
+ setIsApprovalPreviewDialogOpen(false)
+ setApprovalPreviewData(null)
+ handleBiddingClosureDialogClose()
+ router.refresh()
+ } else {
+ toast({
+ title: '오류',
+ description: '폐찰 결재 상신 중 오류가 발생했습니다.',
+ variant: 'destructive',
+ })
+ }
+ } catch (error) {
+ console.error('폐찰 결재 상신 실패:', error)
+ toast({
+ title: '오류',
+ description: '폐찰 결재 상신 중 오류가 발생했습니다.',
+ variant: 'destructive',
+ })
+ }
+ }
}