import { Document, Packer, Paragraph, TextRun, HeadingLevel, AlignmentType } from 'docx'; import { GeneratedQuestion } from '@/types/quiz'; export async function exportQuestionsToDocx( questions: GeneratedQuestion[], filename: string = 'quiz-questions.docx', includeAnswers: boolean = true ) { // Create document const doc = new Document({ sections: [{ properties: {}, children: [ // Title new Paragraph({ text: 'Quiz Questions', heading: HeadingLevel.HEADING_1, alignment: AlignmentType.CENTER, spacing: { after: 400, } }), // Date created new Paragraph({ text: `Generated: ${new Date().toLocaleDateString()}`, alignment: AlignmentType.CENTER, spacing: { after: 200 } }), // Total questions new Paragraph({ text: `Total Questions: ${questions.length}`, alignment: AlignmentType.CENTER, spacing: { after: 300 } }), // Questions ...questions.map((question, index) => renderQuestion(question, index + 1, includeAnswers) ).flat() ] }] }); // Generate and download the document const blob = await Packer.toBlob(doc); const url = URL.createObjectURL(blob); const link = document.createElement('a'); link.href = url; link.download = filename; document.body.appendChild(link); link.click(); document.body.removeChild(link); URL.revokeObjectURL(url); } function renderQuestion(question: GeneratedQuestion, questionNumber: number, includeAnswers: boolean = true): Paragraph[] { const elements: Paragraph[] = []; // Question number and type elements.push( new Paragraph({ children: [ new TextRun({ text: `Question ${questionNumber}`, bold: true, size: 24 }), new TextRun({ text: ` (${question.type})`, size: 20, color: '666666' }) ], spacing: { before: 300, after: 150 } }) ); // Question stem elements.push( new Paragraph({ children: [ new TextRun({ text: question.stem, size: 22 }) ], spacing: { after: 200 } }) ); // Points elements.push( new Paragraph({ children: [ new TextRun({ text: `Points: ${question.points}`, size: 18, italics: true, color: '666666' }) ], spacing: { after: 150 } }) ); // Question content elements.push(...renderQuestionContent(question, includeAnswers)); // Separator elements.push( new Paragraph({ children: [ new TextRun({ text: '─'.repeat(50), color: 'CCCCCC' }) ], spacing: { before: 200, after: 200 } }) ); return elements; } function renderQuestionContent(question: GeneratedQuestion, includeAnswers: boolean = true): Paragraph[] { const elements: Paragraph[] = []; // Handle the actual content structure for multiple choice questions if (question.content && typeof question.content === 'object') { // Handle multiple choice questions with the actual structure if ('Options' in question.content && question.content.Options) { // Options is an object with keys A, B, C, D const options = question.content.Options as { A: string; B: string; C: string; D: string; }; const correctAnswer = (question.content as { Answer?: string }).Answer; Object.entries(options).forEach(([key, value]) => { const isCorrect = key === correctAnswer; elements.push( new Paragraph({ children: [ new TextRun({ text: `${key}. ${value}`, size: 20, color: includeAnswers && isCorrect ? '008000' : '000000', bold: includeAnswers && isCorrect }) ], spacing: { after: 100 } }) ); }); // Add answer key if including answers if (includeAnswers && correctAnswer) { elements.push( new Paragraph({ children: [ new TextRun({ text: `Answer: ${correctAnswer}`, bold: true, size: 20, color: '008000' }) ], spacing: { before: 200, after: 200 } }) ); } } } return elements; } export async function exportQuestionsToJson(questions: GeneratedQuestion[], filename: string = 'quiz-questions.json') { const data = { metadata: { exportDate: new Date().toISOString(), totalQuestions: questions.length, totalPoints: questions.reduce((sum, q) => sum + q.points, 0) }, questions: questions.map(q => ({ id: q.id, type: q.type, stem: q.stem, content: q.content, points: q.points, createdAt: q.createdAt })) }; const blob = new Blob([JSON.stringify(data, null, 2)], { type: 'application/json' }); const url = URL.createObjectURL(blob); const link = document.createElement('a'); link.href = url; link.download = filename; document.body.appendChild(link); link.click(); document.body.removeChild(link); URL.revokeObjectURL(url); } export async function exportQuestionsToMarkdown( questions: GeneratedQuestion[], filename: string = 'quiz-questions.md', includeAnswers: boolean = true ) { let markdown = '# Quiz Questions\n\n'; markdown += `Generated: ${new Date().toLocaleDateString()}\n\n`; markdown += `Total Questions: ${questions.length}\n\n`; markdown += '---\n\n'; questions.forEach((question, index) => { markdown += `## Question ${index + 1} (${question.type})\n\n`; markdown += `**Points:** ${question.points}\n\n`; markdown += `${question.stem}\n\n`; if (question.content && typeof question.content === 'object') { if ('Options' in question.content && question.content.Options) { const options = question.content.Options as { A: string; B: string; C: string; D: string; }; const correctAnswer = (question.content as { Answer?: string }).Answer; Object.entries(options).forEach(([key, value]) => { const isCorrect = includeAnswers && key === correctAnswer; markdown += `${key}. ${value}${isCorrect ? ' ✓' : ''}\n`; }); if (includeAnswers && correctAnswer) { markdown += `\n**Answer:** ${correctAnswer}\n`; } } } markdown += '\n---\n\n'; }); const blob = new Blob([markdown], { type: 'text/markdown' }); const url = URL.createObjectURL(blob); const link = document.createElement('a'); link.href = url; link.download = filename; document.body.appendChild(link); link.click(); document.body.removeChild(link); URL.revokeObjectURL(url); } export async function exportQuestionsToCsv(questions: GeneratedQuestion[], filename: string = 'quiz-questions.csv') { // CSV header let csv = 'Question Number,Type,Points,Question,Option A,Option B,Option C,Option D,Correct Answer\n'; questions.forEach((question, index) => { const row = []; row.push(index + 1); row.push(question.type); row.push(question.points); row.push(`"${question.stem.replace(/"/g, '""')}"`); if (question.content && typeof question.content === 'object' && 'Options' in question.content) { const options = question.content.Options as { A: string; B: string; C: string; D: string; }; const correctAnswer = (question.content as { Answer?: string }).Answer; row.push(`"${options.A.replace(/"/g, '""')}"`); row.push(`"${options.B.replace(/"/g, '""')}"`); row.push(`"${options.C.replace(/"/g, '""')}"`); row.push(`"${options.D.replace(/"/g, '""')}"`); row.push(correctAnswer || ''); } else { // Add empty cells for non-multiple choice questions row.push('', '', '', '', ''); } csv += row.join(',') + '\n'; }); const blob = new Blob([csv], { type: 'text/csv' }); const url = URL.createObjectURL(blob); const link = document.createElement('a'); link.href = url; link.download = filename; document.body.appendChild(link); link.click(); document.body.removeChild(link); URL.revokeObjectURL(url); }