diff options
Diffstat (limited to 'lib/rfq-last')
| -rw-r--r-- | lib/rfq-last/approval-actions.ts | 26 | ||||
| -rw-r--r-- | lib/rfq-last/service.ts | 2 | ||||
| -rw-r--r-- | lib/rfq-last/vendor-response/editor/vendor-response-editor.tsx | 44 |
3 files changed, 68 insertions, 4 deletions
diff --git a/lib/rfq-last/approval-actions.ts b/lib/rfq-last/approval-actions.ts index be435931..2f9d0843 100644 --- a/lib/rfq-last/approval-actions.ts +++ b/lib/rfq-last/approval-actions.ts @@ -8,6 +8,7 @@ import { ApprovalSubmissionSaga } from '@/lib/approval'; import { mapRfqSendToTemplateVariables } from './approval-handlers'; +import { prepareEmailAttachments } from './service'; interface RfqSendApprovalData { // RFQ 기본 정보 @@ -95,7 +96,27 @@ export async function requestRfqSendWithApproval(data: RfqSendApprovalData) { applicationReason: data.applicationReason, }); - // 3. 결재 상신용 payload 구성 + // 3. Knox 상신용 첨부파일 준비 (실제 파일 객체로 변환) + const emailAttachments = await prepareEmailAttachments(data.rfqId, data.attachmentIds); + type PreparedAttachment = { + filename?: string | null; + content: BlobPart; + contentType?: string | null; + }; + const knoxAttachments = (emailAttachments as PreparedAttachment[]) + .filter((att): att is PreparedAttachment => Boolean(att && att.content)) + .map( + (att) => + new File([att.content], att.filename || 'attachment', { + type: att.contentType || 'application/octet-stream', + }) + ); + + if (knoxAttachments.length === 0) { + throw new Error('상신할 첨부파일을 준비하지 못했습니다.'); + } + + // 4. 결재 상신용 payload 구성 // ⚠️ cronjob 환경에서 실행되므로 currentUser 정보를 포함해야 함 const approvalPayload = { rfqId: data.rfqId, @@ -113,7 +134,7 @@ export async function requestRfqSendWithApproval(data: RfqSendApprovalData) { }, }; - // 4. Saga로 결재 상신 + // 5. Saga로 결재 상신 const saga = new ApprovalSubmissionSaga( 'rfq_send_with_attachments', // 핸들러 키 approvalPayload, // 결재 승인 후 실행될 데이터 @@ -128,6 +149,7 @@ export async function requestRfqSendWithApproval(data: RfqSendApprovalData) { epId: data.currentUser.epId, email: data.currentUser.email, }, + attachments: knoxAttachments, } ); diff --git a/lib/rfq-last/service.ts b/lib/rfq-last/service.ts index 68cfdac7..23f5f63a 100644 --- a/lib/rfq-last/service.ts +++ b/lib/rfq-last/service.ts @@ -3349,7 +3349,7 @@ async function getProjectInfo(projectId: number) { return project; } -async function prepareEmailAttachments(rfqId: number, attachmentIds: number[]) { +export async function prepareEmailAttachments(rfqId: number, attachmentIds: number[]) { const attachments = await db .select({ attachment: rfqLastAttachments, diff --git a/lib/rfq-last/vendor-response/editor/vendor-response-editor.tsx b/lib/rfq-last/vendor-response/editor/vendor-response-editor.tsx index 8c70b8dd..18fc5d50 100644 --- a/lib/rfq-last/vendor-response/editor/vendor-response-editor.tsx +++ b/lib/rfq-last/vendor-response/editor/vendor-response-editor.tsx @@ -9,6 +9,14 @@ import { Card, CardContent, CardDescription, CardHeader, CardTitle } from "@/com import { Button } from "@/components/ui/button" import { Tabs, TabsContent, TabsList, TabsTrigger } from "@/components/ui/tabs" import { Badge } from "@/components/ui/badge" +import { + Dialog, + DialogContent, + DialogDescription, + DialogFooter, + DialogHeader, + DialogTitle, +} from "@/components/ui/dialog" import { toast } from "sonner" import RfqInfoHeader from "./rfq-info-header" import CommercialTermsForm from "./commercial-terms-form" @@ -130,6 +138,7 @@ export default function VendorResponseEditor({ const [deletedAttachments, setDeletedAttachments] = useState<any[]>([]) const [uploadProgress, setUploadProgress] = useState(0) // 추가 const [currencyDecimalPlaces, setCurrencyDecimalPlaces] = useState<number>(2) // 통화별 소수점 자리수 + const [confirmOpen, setConfirmOpen] = useState(false) console.log(existingResponse,"existingResponse") @@ -682,7 +691,7 @@ export default function VendorResponseEditor({ <Button type="button" variant="default" - onClick={() => handleFormSubmit(true)} // 직접 핸들러 호출 + onClick={() => setConfirmOpen(true)} // 제출 전 확인 다이얼로그 disabled={loading || !allContractsSigned || isSubmitted || activeTab !== 'attachments'} > {!allContractsSigned ? ( @@ -714,6 +723,39 @@ export default function VendorResponseEditor({ </Button> </div> + {/* 최종 제출 확인 다이얼로그 */} + <Dialog open={confirmOpen} onOpenChange={setConfirmOpen}> + <DialogContent> + <DialogHeader> + <DialogTitle>최종 제출</DialogTitle> + <DialogDescription> + 최종 제출하시겠습니까? + </DialogDescription> + </DialogHeader> + <DialogFooter className="flex gap-2 sm:justify-end"> + <Button + type="button" + variant="outline" + onClick={() => setConfirmOpen(false)} + disabled={loading} + > + 취소 + </Button> + <Button + type="button" + variant="default" + onClick={() => { + setConfirmOpen(false) + handleFormSubmit(true) + }} + disabled={loading || !allContractsSigned || isSubmitted || activeTab !== 'attachments'} + > + 제출하기 + </Button> + </DialogFooter> + </DialogContent> + </Dialog> + </div> </form> </FormProvider> |
