diff options
Diffstat (limited to 'lib/compliance')
| -rw-r--r-- | lib/compliance/compliance-response-detail.tsx | 147 | ||||
| -rw-r--r-- | lib/compliance/services.ts | 29 |
2 files changed, 96 insertions, 80 deletions
diff --git a/lib/compliance/compliance-response-detail.tsx b/lib/compliance/compliance-response-detail.tsx index af12469c..709f3ede 100644 --- a/lib/compliance/compliance-response-detail.tsx +++ b/lib/compliance/compliance-response-detail.tsx @@ -6,21 +6,7 @@ import { ko } from "date-fns/locale" import { Card, CardContent, CardHeader, CardTitle } from "@/components/ui/card" import { Badge } from "@/components/ui/badge" -import { Separator } from "@/components/ui/separator" -import { - Table, - TableBody, - TableCell, - TableHead, - TableHeader, - TableRow -} from "@/components/ui/table" -import { - Accordion, - AccordionContent, - AccordionItem, - AccordionTrigger, -} from "@/components/ui/accordion" +import { Button } from "@/components/ui/button" import { FileText, Users, @@ -37,15 +23,15 @@ import { getComplianceResponseFilesByResponseId, getComplianceSurveyTemplate, getComplianceQuestions, - getComplianceQuestionOptions } from "./services" interface ComplianceResponseDetailProps { templateId: number responseId: number + promises?: Promise<any[]> } -export function ComplianceResponseDetail({ templateId, responseId }: ComplianceResponseDetailProps) { +export function ComplianceResponseDetail({ templateId, responseId, promises }: ComplianceResponseDetailProps) { const [response, setResponse] = React.useState<any>(null) const [answers, setAnswers] = React.useState<any[]>([]) const [files, setFiles] = React.useState<any[]>([]) @@ -56,13 +42,19 @@ export function ComplianceResponseDetail({ templateId, responseId }: ComplianceR React.useEffect(() => { const fetchResponseData = async () => { try { - const [responseData, answersData, filesData, templateData, questionsData] = await Promise.all([ - getComplianceResponse(responseId), - getComplianceResponseAnswers(responseId), - getComplianceResponseFilesByResponseId(responseId), - getComplianceSurveyTemplate(templateId), - getComplianceQuestions(templateId) - ]) + let responseData, answersData, filesData, templateData, questionsData; + + if (promises) { + [responseData, answersData, filesData, templateData, questionsData] = await promises; + } else { + [responseData, answersData, filesData, templateData, questionsData] = await Promise.all([ + getComplianceResponse(responseId), + getComplianceResponseAnswers(responseId), + getComplianceResponseFilesByResponseId(responseId), + getComplianceSurveyTemplate(templateId), + getComplianceQuestions(templateId) + ]); + } setResponse(responseData) setAnswers(answersData) @@ -77,7 +69,7 @@ export function ComplianceResponseDetail({ templateId, responseId }: ComplianceR } fetchResponseData() - }, [templateId, responseId]) + }, [templateId, responseId, promises]) const getStatusIcon = (status: string) => { switch (status) { @@ -115,9 +107,9 @@ export function ComplianceResponseDetail({ templateId, responseId }: ComplianceR return question ? question.questionNumber : '-' } - const getQuestionType = (questionId: number) => { + const hasFileUpload = (questionId: number) => { const question = questions.find(q => q.id === questionId) - return question ? question.questionType : '-' + return question ? question.hasFileUpload : false } // 파일 다운로드 핸들러 @@ -252,24 +244,18 @@ export function ComplianceResponseDetail({ templateId, responseId }: ComplianceR 아직 답변이 없습니다. </div> ) : ( - <Accordion type="single" collapsible className="w-full"> - {answers.map((answer, index) => ( - <AccordionItem key={answer.id} value={`answer-${answer.id}`}> - <AccordionTrigger className="text-left"> - <div className="flex items-center gap-2"> - <Badge variant="outline"> - {getQuestionNumber(answer.questionId)} - </Badge> - <span className="font-medium"> - {getQuestionText(answer.questionId)} - </span> - <Badge variant="secondary"> - {getQuestionType(answer.questionId)} - </Badge> - </div> - </AccordionTrigger> - <AccordionContent> - <div className="space-y-3 pt-2"> + <div className="space-y-4"> + {answers.map((answer) => ( + <div key={answer.id} className="border rounded-lg p-4 space-y-3"> + <div className="flex items-center gap-2 pb-3 border-b"> + <Badge variant="outline"> + {getQuestionNumber(answer.questionId)} + </Badge> + <span className="font-medium"> + {getQuestionText(answer.questionId)} + </span> + </div> + <div className="space-y-3"> {/* 답변 값 */} {answer.answerValue && ( <div> @@ -302,39 +288,41 @@ export function ComplianceResponseDetail({ templateId, responseId }: ComplianceR </div> )} - {/* 첨부파일 */} - <div> - <label className="text-sm font-medium text-muted-foreground">첨부파일</label> - <div className="mt-1"> - {files.filter(file => file.answerId === answer.id).length > 0 ? ( - <div className="space-y-2"> - {files - .filter(file => file.answerId === answer.id) - .map((file) => ( - <div key={file.id} className="flex items-center justify-between p-2 bg-muted rounded"> - <div className="flex items-center gap-2"> - <File className="h-4 w-4 text-muted-foreground" /> - <span className="text-sm">{file.fileName}</span> - <span className="text-xs text-muted-foreground"> - ({file.fileSize ? `${(file.fileSize / 1024).toFixed(1)} KB` : '크기 정보 없음'}) - </span> + + {hasFileUpload(answer.questionId) && ( + <div> + <label className="text-sm font-medium text-muted-foreground">첨부파일</label> + <div className="mt-1"> + {files.filter(file => file.answerId === answer.id).length > 0 ? ( + <div className="space-y-2"> + {files + .filter(file => file.answerId === answer.id) + .map((file) => ( + <div key={file.id} className="flex items-center justify-between p-2 bg-muted rounded"> + <div className="flex items-center gap-2"> + <File className="h-4 w-4 text-muted-foreground" /> + <span className="text-sm">{file.fileName}</span> + <span className="text-xs text-muted-foreground"> + ({file.fileSize ? `${(file.fileSize / 1024).toFixed(1)} KB` : '크기 정보 없음'}) + </span> + </div> + <Button + variant="ghost" + size="sm" + onClick={() => handleFileDownload(file)} + className="h-6 w-6 p-0" + > + <Download className="h-3 w-3" /> + </Button> </div> - <Button - variant="ghost" - size="sm" - onClick={() => handleFileDownload(file)} - className="h-6 w-6 p-0" - > - <Download className="h-3 w-3" /> - </Button> - </div> - ))} - </div> - ) : ( - <p className="text-sm text-muted-foreground">첨부된 파일이 없습니다</p> - )} + ))} + </div> + ) : ( + <p className="text-sm text-muted-foreground">첨부된 파일이 없습니다</p> + )} + </div> </div> - </div> + )} {/* 답변 생성일 */} <div className="text-xs text-muted-foreground"> @@ -343,11 +331,10 @@ export function ComplianceResponseDetail({ templateId, responseId }: ComplianceR '-' } </div> - </div> - </AccordionContent> - </AccordionItem> + </div> + </div> ))} - </Accordion> + </div> )} </CardContent> </Card> diff --git a/lib/compliance/services.ts b/lib/compliance/services.ts index de67598b..2d3ec092 100644 --- a/lib/compliance/services.ts +++ b/lib/compliance/services.ts @@ -932,3 +932,32 @@ export async function getTemplatesRelatedDataCount(templateIds: number[]) { return { totalQuestions: 0, totalResponses: 0, details: [] }; } } + +// basic_contract_id로 compliance response 조회 +export async function getComplianceResponseByBasicContractId(basicContractId: number) { + try { + if (!basicContractId || isNaN(basicContractId) || basicContractId <= 0) { + console.error(`Invalid basicContractId: ${basicContractId}`); + return null; + } + + const [response] = await db + .select({ + id: complianceResponses.id, + templateId: complianceResponses.templateId, + basicContractId: complianceResponses.basicContractId, + status: complianceResponses.status, + completedAt: complianceResponses.completedAt, + createdAt: complianceResponses.createdAt, + updatedAt: complianceResponses.updatedAt, + }) + .from(complianceResponses) + .where(eq(complianceResponses.basicContractId, basicContractId)) + .limit(1); + + return response || null; + } catch (error) { + console.error("Error fetching compliance response by basic contract id:", error); + return null; + } +} |
