diff options
Diffstat (limited to 'lib/qna/table/qna-export-actions.tsx')
| -rw-r--r-- | lib/qna/table/qna-export-actions.tsx | 261 |
1 files changed, 261 insertions, 0 deletions
diff --git a/lib/qna/table/qna-export-actions.tsx b/lib/qna/table/qna-export-actions.tsx new file mode 100644 index 00000000..07b692c0 --- /dev/null +++ b/lib/qna/table/qna-export-actions.tsx @@ -0,0 +1,261 @@ +"use server" + +import { QnaViewSelect } from "@/db/schema" + +interface ExportQnaDataParams { + format: "csv" | "excel" + data: QnaViewSelect[] + fields: string[] +} + +/** + * Q&A 데이터 내보내기 + */ +export async function exportQnaData({ + format, + data, + fields +}: ExportQnaDataParams) { + try { + // 필드 매핑 + const fieldMapping: Record<string, string> = { + title: "제목", + authorName: "작성자", + companyName: "회사명", + authorDomain: "도메인", + vendorType: "벤더타입", + totalAnswers: "답변수", + totalComments: "댓글수", + createdAt: "작성일", + lastActivityAt: "최근활동", + hasAnswers: "답변여부", + isPopular: "인기질문", + } + + // 데이터 변환 + const exportData = data.map(qna => { + const row: Record<string, any> = {} + + fields.forEach(field => { + const label = fieldMapping[field] || field + + switch (field) { + case "createdAt": + case "lastActivityAt": + row[label] = qna[field as keyof QnaViewSelect] + ? new Date(qna[field as keyof QnaViewSelect] as string).toLocaleDateString("ko-KR") + : "" + break + case "hasAnswers": + row[label] = qna.hasAnswers ? "예" : "아니오" + break + case "isPopular": + row[label] = qna.isPopular ? "예" : "아니오" + break + case "authorDomain": + const domainLabels: Record<string, string> = { + partners: "협력업체", + tech: "기술업체", + admin: "관리자" + } + row[label] = domainLabels[qna.authorDomain as string] || qna.authorDomain + break + case "vendorType": + const typeLabels: Record<string, string> = { + vendor: "일반벤더", + techVendor: "기술벤더" + } + row[label] = typeLabels[qna.vendorType as string] || qna.vendorType || "" + break + default: + row[label] = qna[field as keyof QnaViewSelect] || "" + } + }) + + return row + }) + + if (format === "csv") { + return generateCSV(exportData) + } else { + return generateExcel(exportData) + } + } catch (error) { + console.error("내보내기 오류:", error) + return { + success: false, + error: "데이터 내보내기 중 오류가 발생했습니다." + } + } +} + +/** + * CSV 파일 생성 + */ +function generateCSV(data: Record<string, any>[]) { + try { + if (data.length === 0) { + return { + success: false, + error: "내보낼 데이터가 없습니다." + } + } + + const headers = Object.keys(data[0]) + const csvContent = [ + headers.join(","), // 헤더 + ...data.map(row => + headers.map(header => { + const value = row[header] + // CSV에서 쉼표와 따옴표 이스케이프 + if (typeof value === "string" && (value.includes(",") || value.includes('"'))) { + return `"${value.replace(/"/g, '""')}"` + } + return value + }).join(",") + ) + ].join("\n") + + // BOM 추가 (한글 인코딩을 위해) + const csvWithBOM = "\uFEFF" + csvContent + const blob = new Blob([csvWithBOM], { type: "text/csv;charset=utf-8;" }) + + // 파일 다운로드 처리는 클라이언트에서 수행 + const url = URL.createObjectURL(blob) + const fileName = `qna_export_${new Date().toISOString().split('T')[0]}.csv` + + // 클라이언트에서 다운로드 처리 + if (typeof window !== "undefined") { + const link = document.createElement("a") + link.href = url + link.download = fileName + document.body.appendChild(link) + link.click() + document.body.removeChild(link) + URL.revokeObjectURL(url) + } + + return { + success: true, + message: "CSV 파일이 다운로드되었습니다." + } + } catch (error) { + return { + success: false, + error: "CSV 생성 중 오류가 발생했습니다." + } + } +} + +/** + * Excel 파일 생성 + */ +function generateExcel(data: Record<string, any>[]) { + try { + if (data.length === 0) { + return { + success: false, + error: "내보낼 데이터가 없습니다." + } + } + + // Excel 생성을 위해 SheetJS 라이브러리 사용 + // 실제 구현에서는 xlsx 라이브러리를 사용해야 함 + + const headers = Object.keys(data[0]) + const worksheet = [ + headers, // 헤더 행 + ...data.map(row => headers.map(header => row[header])) // 데이터 행들 + ] + + // 간단한 CSV 형태로 반환 (실제로는 xlsx 라이브러리 사용 권장) + const csvContent = worksheet.map(row => + row.map(cell => { + if (typeof cell === "string" && (cell.includes(",") || cell.includes('"'))) { + return `"${cell.replace(/"/g, '""')}"` + } + return cell + }).join(",") + ).join("\n") + + const csvWithBOM = "\uFEFF" + csvContent + const blob = new Blob([csvWithBOM], { + type: "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet" + }) + + const url = URL.createObjectURL(blob) + const fileName = `qna_export_${new Date().toISOString().split('T')[0]}.xlsx` + + // 클라이언트에서 다운로드 처리 + if (typeof window !== "undefined") { + const link = document.createElement("a") + link.href = url + link.download = fileName + document.body.appendChild(link) + link.click() + document.body.removeChild(link) + URL.revokeObjectURL(url) + } + + return { + success: true, + message: "Excel 파일이 다운로드되었습니다." + } + } catch (error) { + return { + success: false, + error: "Excel 생성 중 오류가 발생했습니다." + } + } +} + +/** + * Q&A 통계 내보내기 + */ +export async function exportQnaStats(data: QnaViewSelect[]) { + try { + const stats = { + 총질문수: data.length, + 답변된질문: data.filter(q => q.hasAnswers).length, + 답변대기질문: data.filter(q => !q.hasAnswers).length, + 인기질문: data.filter(q => q.isPopular).length, + 평균답변수: data.reduce((sum, q) => sum + (q.totalAnswers || 0), 0) / data.length, + 평균댓글수: data.reduce((sum, q) => sum + (q.totalComments || 0), 0) / data.length, + } + + // 도메인별 통계 + const domainStats = data.reduce((acc, q) => { + const domain = q.authorDomain || "기타" + acc[domain] = (acc[domain] || 0) + 1 + return acc + }, {} as Record<string, number>) + + // 회사별 통계 (상위 10개) + const companyStats = data.reduce((acc, q) => { + const company = q.companyName || "미지정" + acc[company] = (acc[company] || 0) + 1 + return acc + }, {} as Record<string, number>) + + const topCompanies = Object.entries(companyStats) + .sort(([,a], [,b]) => b - a) + .slice(0, 10) + + const exportData = [ + { 구분: "전체 통계", ...stats }, + { 구분: "도메인별", ...domainStats }, + ...topCompanies.map(([company, count]) => ({ + 구분: "회사별", + 회사명: company, + 질문수: count + })) + ] + + return generateCSV(exportData) + } catch (error) { + return { + success: false, + error: "통계 내보내기 중 오류가 발생했습니다." + } + } +}
\ No newline at end of file |
