diff options
| author | joonhoekim <26rote@gmail.com> | 2025-11-07 12:01:16 +0900 |
|---|---|---|
| committer | joonhoekim <26rote@gmail.com> | 2025-11-07 12:01:16 +0900 |
| commit | 18ca4ad784aeeab9ab7a13bbc8b3c13b42ca5e49 (patch) | |
| tree | 6faf1a05d1ae296202ece5f4ca95b4d9c7a0488b /components | |
| parent | 4b6ebdef8281a413fa2bfbdf8f5565eb8b106c62 (diff) | |
(김준회) 결재 미리보기 공통컴포넌트 중복 제거, 기존 코드의 미리보기 호출부 수정, 템플릿 작성 가이드 간략히 추가, 결재 미리보기시 첨부파일 편집 처리
Diffstat (limited to 'components')
| -rw-r--r-- | components/approval/ApprovalPreviewDialog.tsx | 213 |
1 files changed, 0 insertions, 213 deletions
diff --git a/components/approval/ApprovalPreviewDialog.tsx b/components/approval/ApprovalPreviewDialog.tsx deleted file mode 100644 index 7b5ff615..00000000 --- a/components/approval/ApprovalPreviewDialog.tsx +++ /dev/null @@ -1,213 +0,0 @@ -"use client"; - -import * as React from "react"; -import { Button } from "@/components/ui/button"; -import { - Dialog, - DialogContent, - DialogDescription, - DialogFooter, - DialogHeader, - DialogTitle, -} from "@/components/ui/dialog"; -import { Card, CardContent, CardDescription, CardHeader, CardTitle } from "@/components/ui/card"; -import { Loader2, FileText, AlertCircle } from "lucide-react"; -import { toast } from "sonner"; -import { Separator } from "@/components/ui/separator"; - -import ApprovalLineSelector, { - type ApprovalLineItem, -} from "@/components/knox/approval/ApprovalLineSelector"; -import { getApprovalTemplateByName, replaceTemplateVariables } from "@/lib/approval/template-utils"; -import { debugLog, debugError, debugSuccess } from "@/lib/debug-utils"; - -interface ApprovalPreviewDialogProps { - open: boolean; - onOpenChange: (open: boolean) => void; - templateName: string; - variables: Record<string, string>; - title: string; - description?: string; - currentUser: { id: number; epId: string | null; name?: string | null; email?: string }; - onSubmit: (approvers: ApprovalLineItem[]) => Promise<void>; -} - -export function ApprovalPreviewDialog({ - open, - onOpenChange, - templateName, - variables, - title, - description, - currentUser, - onSubmit, -}: ApprovalPreviewDialogProps) { - const [approvalLines, setApprovalLines] = React.useState<ApprovalLineItem[]>([]); - const [isSubmitting, setIsSubmitting] = React.useState(false); - const [templateContent, setTemplateContent] = React.useState<string>(""); - const [isLoadingTemplate, setIsLoadingTemplate] = React.useState(false); - const [templateError, setTemplateError] = React.useState<string | null>(null); - - // 상신자 초기화 - React.useEffect(() => { - if (open && currentUser.epId) { - const drafterLine: ApprovalLineItem = { - id: `drafter-${Date.now()}`, - epId: currentUser.epId, - userId: currentUser.id.toString(), - emailAddress: currentUser.email, - name: currentUser.name || undefined, - role: "0", // 기안 - seq: "0", - opinion: "", - }; - setApprovalLines([drafterLine]); - } - }, [open, currentUser]); - - // 템플릿 로드 및 변수 치환 - React.useEffect(() => { - if (!open) return; - - const loadTemplate = async () => { - setIsLoadingTemplate(true); - setTemplateError(null); - - try { - const template = await getApprovalTemplateByName(templateName); - - if (!template) { - setTemplateError(`템플릿 "${templateName}"을 찾을 수 없습니다.`); - setTemplateContent(`<p class="text-gray-500">${description || "결재 요청"}</p>`); - } else { - // 변수 치환 - const replaced = await replaceTemplateVariables(template.content, variables); - setTemplateContent(replaced); - } - } catch (error) { - console.error("Template load error:", error); - setTemplateError("템플릿을 불러오는 중 오류가 발생했습니다."); - setTemplateContent(`<p class="text-gray-500">${description || "결재 요청"}</p>`); - } finally { - setIsLoadingTemplate(false); - } - }; - - loadTemplate(); - }, [open, templateName, variables, description]); - - const handleSubmit = async () => { - debugLog('[ApprovalPreviewDialog] 결재 제출 시작', { - templateName, - approvalLineCount: approvalLines.length, - }); - - // 결재자가 있는지 확인 (상신자 제외) - const approvers = approvalLines.filter((line) => line.seq !== "0"); - if (approvers.length === 0) { - debugError('[ApprovalPreviewDialog] 결재자가 없음'); - toast.error("결재자를 최소 1명 이상 추가해주세요."); - return; - } - - setIsSubmitting(true); - try { - debugLog('[ApprovalPreviewDialog] onSubmit 호출', { - approversCount: approvers.length, - }); - - await onSubmit(approvalLines); - - debugSuccess('[ApprovalPreviewDialog] 결재 요청 성공'); - toast.success("결재가 성공적으로 요청되었습니다."); - onOpenChange(false); - } catch (error) { - debugError('[ApprovalPreviewDialog] 결재 요청 실패', error); - const errorMessage = error instanceof Error ? error.message : "결재 요청에 실패했습니다."; - toast.error(errorMessage); - // 에러 발생 시 dialog를 닫지 않음 - } finally { - setIsSubmitting(false); - } - }; - - return ( - <Dialog open={open} onOpenChange={onOpenChange}> - <DialogContent className="max-w-5xl max-h-[90vh] overflow-y-auto"> - <DialogHeader> - <DialogTitle className="flex items-center gap-2"> - <FileText className="w-5 h-5" /> - {title} - </DialogTitle> - {description && <DialogDescription>{description}</DialogDescription>} - </DialogHeader> - - <div className="space-y-6"> - - {/* 결재선 선택 */} - <div> - <ApprovalLineSelector - value={approvalLines} - onChange={setApprovalLines} - placeholder="결재자를 검색하세요..." - maxSelections={10} - /> - </div> - - <Separator /> - - {/* 템플릿 미리보기 */} - <Card> - <CardHeader> - <CardTitle>결재 내용 미리보기</CardTitle> - <CardDescription> - 템플릿: <span className="font-mono text-sm">{templateName}</span> - </CardDescription> - </CardHeader> - <CardContent> - {isLoadingTemplate ? ( - <div className="flex items-center justify-center py-8"> - <Loader2 className="w-6 h-6 animate-spin text-gray-400" /> - <span className="ml-2 text-gray-500">템플릿을 불러오는 중...</span> - </div> - ) : templateError ? ( - <div className="p-4 bg-yellow-50 border border-yellow-200 rounded-lg"> - <div className="flex items-center gap-2 text-yellow-700"> - <AlertCircle className="w-4 h-4" /> - <span className="font-medium">경고</span> - </div> - <p className="text-sm text-yellow-600 mt-1">{templateError}</p> - <p className="text-xs text-yellow-500 mt-2"> - 기본 내용으로 대체되었습니다. 결재는 정상적으로 진행됩니다. - </p> - </div> - ) : null} - - <div - className="border rounded-lg p-4 min-h-[200px] [&_table]:w-full [&_table]:border-collapse [&_table]:border [&_table]:border-border [&_th]:border [&_th]:border-border [&_th]:bg-muted/50 [&_th]:px-3 [&_th]:py-2 [&_th]:text-left [&_th]:font-semibold [&_td]:border [&_td]:border-border [&_td]:px-3 [&_td]:py-2 [&_tr:hover]:bg-muted/30" - dangerouslySetInnerHTML={{ __html: templateContent }} - /> - </CardContent> - </Card> - - </div> - - <DialogFooter className="gap-2"> - <Button - type="button" - variant="outline" - onClick={() => onOpenChange(false)} - disabled={isSubmitting} - > - 취소 - </Button> - <Button type="button" onClick={handleSubmit} disabled={isSubmitting || isLoadingTemplate}> - {isSubmitting && <Loader2 className="mr-2 w-4 h-4 animate-spin" />} - 결재 요청 - </Button> - </DialogFooter> - </DialogContent> - </Dialog> - ); -} - |
