diff options
| author | dujinkim <dujin.kim@dtsolution.co.kr> | 2025-11-20 07:16:17 +0000 |
|---|---|---|
| committer | dujinkim <dujin.kim@dtsolution.co.kr> | 2025-11-20 07:16:17 +0000 |
| commit | 088431a6572d87f7ee287252a4357e6c332d04f7 (patch) | |
| tree | 703ee88b89b6e7abe16d9cad5ed1c85a89dc3cab /lib/basic-contract | |
| parent | dfad0b56b6ad1ec1db23f36fc56eef2f530515c2 (diff) | |
(임수민) 준법설문조사 다운로드 수정
Diffstat (limited to 'lib/basic-contract')
| -rw-r--r-- | lib/basic-contract/viewer/SurveyComponent.tsx | 263 | ||||
| -rw-r--r-- | lib/basic-contract/viewer/basic-contract-sign-viewer.tsx | 4 |
2 files changed, 225 insertions, 42 deletions
diff --git a/lib/basic-contract/viewer/SurveyComponent.tsx b/lib/basic-contract/viewer/SurveyComponent.tsx index 950519ad..e67b8790 100644 --- a/lib/basic-contract/viewer/SurveyComponent.tsx +++ b/lib/basic-contract/viewer/SurveyComponent.tsx @@ -40,8 +40,6 @@ interface SurveyComponentProps { onSurveyDataUpdate: (data: any) => void; onLoadSurveyTemplate: () => void; setActiveTab: (tab: string) => void; - contractFilePath?: string; // 계약서 파일 경로 추가 - contractFileName?: string; // 계약서 파일 이름 추가 } export const SurveyComponent: React.FC<SurveyComponentProps> = ({ @@ -53,9 +51,7 @@ export const SurveyComponent: React.FC<SurveyComponentProps> = ({ onSurveyComplete, onSurveyDataUpdate, onLoadSurveyTemplate, - setActiveTab, - contractFilePath, - contractFileName + setActiveTab }) => { const [uploadedFiles, setUploadedFiles] = useState<Record<number, File[]>>({}); const [existingResponse, setExistingResponse] = useState<ExistingResponse | null>(null); @@ -325,39 +321,232 @@ export const SurveyComponent: React.FC<SurveyComponentProps> = ({ ); }, [control, visibleQuestions]); - // 계약서 다운로드 핸들러 - const handleDownloadContract = useCallback(async () => { - if (!contractFilePath) { - toast.error("다운로드할 파일이 없습니다."); + // 설문조사 내용 다운로드 핸들러 + const handleDownloadSurvey = useCallback(async () => { + if (!surveyTemplate) { + toast.error("설문조사 템플릿이 없습니다."); return; } try { - // 파일 경로를 API 경로로 변환 - const normalizedPath = contractFilePath.startsWith('/') - ? contractFilePath.substring(1) - : contractFilePath; - const encodedPath = normalizedPath.split('/').map(part => encodeURIComponent(part)).join('/'); - const apiFilePath = `/api/files/${encodedPath}`; + const currentValues = getValues(); + + // 설문조사 응답이 없으면 경고 + const hasAnswers = Object.keys(currentValues).some(key => { + const value = currentValues[key]; + return value?.answerValue || value?.detailText || (value?.files && value?.files.length > 0); + }); - // 파일 경로에서 실제 파일명 추출 - const actualFileName = contractFileName || contractFilePath.split('/').pop() || '계약서.pdf'; + if (!hasAnswers && !existingResponse) { + toast.error("다운로드할 설문조사 내용이 없습니다."); + return; + } - // 다운로드 링크 생성 및 클릭 + // HTML 생성 + let htmlContent = ` +<!DOCTYPE html> +<html lang="ko"> +<head> + <meta charset="UTF-8"> + <meta name="viewport" content="width=device-width, initial-scale=1.0"> + <title>${surveyTemplate.name}</title> + <style> + body { + font-family: 'Malgun Gothic', '맑은 고딕', Arial, sans-serif; + line-height: 1.6; + max-width: 800px; + margin: 0 auto; + padding: 20px; + color: #333; + } + h1 { + color: #1a1a1a; + border-bottom: 3px solid #2563eb; + padding-bottom: 10px; + margin-bottom: 30px; + } + .question { + margin-bottom: 25px; + padding: 15px; + background-color: #f9fafb; + border-left: 4px solid #2563eb; + border-radius: 4px; + } + .question-number { + font-weight: bold; + color: #2563eb; + margin-bottom: 8px; + } + .question-text { + font-size: 16px; + font-weight: 600; + margin-bottom: 12px; + color: #1a1a1a; + } + .answer { + margin-top: 10px; + padding: 10px; + background-color: #ffffff; + border: 1px solid #e5e7eb; + border-radius: 4px; + } + .answer-label { + font-weight: 600; + color: #4b5563; + margin-bottom: 5px; + } + .answer-value { + color: #1f2937; + white-space: pre-wrap; + } + .file-list { + margin-top: 8px; + padding-left: 20px; + } + .file-item { + color: #2563eb; + margin: 4px 0; + } + .conditional-badge { + display: inline-block; + background-color: #fef3c7; + color: #92400e; + padding: 2px 8px; + border-radius: 4px; + font-size: 12px; + margin-left: 8px; + } + .metadata { + margin-top: 30px; + padding: 15px; + background-color: #f3f4f6; + border-radius: 4px; + font-size: 14px; + color: #6b7280; + } + </style> +</head> +<body> + <h1>${surveyTemplate.name}</h1> +`; + + // 질문과 답변 생성 + const questionsToShow = visibleQuestions.length > 0 ? visibleQuestions : surveyTemplate.questions; + + questionsToShow.forEach((question: any) => { + const fieldName = `${question.id}`; + const answerData = currentValues[fieldName] || + (existingResponse?.answers.find(a => a.questionId === question.id)); + + // 답변이 없으면 스킵 (조건부 질문일 수 있음) + if (!answerData && !existingResponse) { + return; + } + + const isConditional = !!question.parentQuestionId; + const questionNumber = question.questionNumber || question.id; + + htmlContent += ` + <div class="question"> + <div class="question-number"> + Q${questionNumber} + ${isConditional ? '<span class="conditional-badge">조건부 질문</span>' : ''} + ${question.isRequired ? '<span style="color: #dc2626;">*</span>' : ''} + </div> + <div class="question-text">${question.questionText || ''}</div> +`; + + if (answerData) { + // 답변 값 표시 + if (answerData.answerValue) { + const option = question.options?.find((opt: any) => opt.optionValue === answerData.answerValue); + const answerText = option ? option.optionText : answerData.answerValue; + + htmlContent += ` + <div class="answer"> + <div class="answer-label">선택한 답변:</div> + <div class="answer-value">${answerText}</div> + </div>`; + } + + // 기타 입력 텍스트 + if (answerData.otherText) { + htmlContent += ` + <div class="answer"> + <div class="answer-label">기타 입력:</div> + <div class="answer-value">${answerData.otherText}</div> + </div>`; + } + + // 상세 내용 + if (answerData.detailText) { + htmlContent += ` + <div class="answer"> + <div class="answer-label">상세 내용:</div> + <div class="answer-value">${answerData.detailText}</div> + </div>`; + } + + // 첨부 파일 + const files = answerData.files || []; + if (files.length > 0) { + htmlContent += ` + <div class="answer"> + <div class="answer-label">첨부 파일:</div> + <div class="file-list">`; + + files.forEach((file: any) => { + const fileName = file.fileName || file.name || '파일'; + htmlContent += ` + <div class="file-item">📎 ${fileName}</div>`; + }); + + htmlContent += ` + </div> + </div>`; + } + } else { + htmlContent += ` + <div class="answer"> + <div class="answer-value" style="color: #9ca3af; font-style: italic;">답변 없음</div> + </div>`; + } + + htmlContent += ` + </div>`; + }); + + // 메타데이터 추가 + const completedDate = existingResponse?.completedAt + ? new Date(existingResponse.completedAt).toLocaleString('ko-KR') + : new Date().toLocaleString('ko-KR'); + + htmlContent += ` + <div class="metadata"> + <div><strong>생성일시:</strong> ${completedDate}</div> + ${existingResponse ? `<div><strong>상태:</strong> ${existingResponse.status === 'COMPLETED' ? '완료' : '작성중'}</div>` : ''} + <div><strong>총 질문 수:</strong> ${questionsToShow.length}개</div> + </div> +</body> +</html>`; + + // Blob 생성 및 다운로드 + const blob = new Blob([htmlContent], { type: 'text/html;charset=utf-8' }); + const url = URL.createObjectURL(blob); const link = document.createElement('a'); - link.href = apiFilePath; - link.download = actualFileName; - link.target = '_blank'; + link.href = url; + link.download = `${surveyTemplate.name}_${new Date().toISOString().split('T')[0]}.html`; document.body.appendChild(link); link.click(); document.body.removeChild(link); + URL.revokeObjectURL(url); - toast.success("파일 다운로드가 시작되었습니다."); + toast.success("설문조사 내용이 다운로드되었습니다."); } catch (error) { - console.error("파일 다운로드 실패:", error); - toast.error("파일 다운로드에 실패했습니다."); + console.error("설문조사 다운로드 실패:", error); + toast.error("설문조사 다운로드에 실패했습니다."); } - }, [contractFilePath, contractFileName]); + }, [surveyTemplate, getValues, visibleQuestions, existingResponse]); // 설문조사 완료 핸들러 const handleSurveyComplete = useCallback(async () => { @@ -581,19 +770,17 @@ export const SurveyComponent: React.FC<SurveyComponentProps> = ({ </CardTitle> <div className="flex items-center space-x-3"> - {/* Word 파일 다운로드 버튼 */} - {contractFilePath && ( - <Button - variant="outline" - size="sm" - onClick={handleDownloadContract} - className="h-8 text-xs" - title="계약서 문서 다운로드" - > - <Download className="h-3 w-3 mr-1" /> - 문서 다운로드 - </Button> - )} + {/* 설문조사 내용 다운로드 버튼 */} + <Button + variant="outline" + size="sm" + onClick={handleDownloadSurvey} + className="h-8 text-xs" + title="설문조사 내용 다운로드" + > + <Download className="h-3 w-3 mr-1" /> + 설문조사 다운로드 + </Button> {/* 컴팩트 진행률 표시 */} <div className="flex items-center space-x-2"> diff --git a/lib/basic-contract/viewer/basic-contract-sign-viewer.tsx b/lib/basic-contract/viewer/basic-contract-sign-viewer.tsx index 7401a32c..f21eac38 100644 --- a/lib/basic-contract/viewer/basic-contract-sign-viewer.tsx +++ b/lib/basic-contract/viewer/basic-contract-sign-viewer.tsx @@ -1448,8 +1448,6 @@ export function BasicContractSignViewer({ onSurveyDataUpdate={setSurveyData} onLoadSurveyTemplate={loadSurveyTemplate} setActiveTab={setActiveTab} - contractFilePath={filePath} - contractFileName={filePath ? filePath.split('/').pop() : undefined} /> </div> @@ -1677,8 +1675,6 @@ export function BasicContractSignViewer({ onSurveyDataUpdate={setSurveyData} onLoadSurveyTemplate={loadSurveyTemplate} setActiveTab={setActiveTab} - contractFilePath={filePath} - contractFileName={filePath ? filePath.split('/').pop() : undefined} /> </div> |
