Shih-hungg's picture
Create edit page
733e040
'use client';
import { useState } from 'react';
import { GeneratedQuestion, QuestionType } from '@/types/quiz';
import QuestionCard from './QuestionCard';
import { exportQuestionsToDocx } from '@/lib/exportUtils';
import { Button } from '@/components/ui/button';
import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from '@/components/ui/select';
import { Checkbox } from '@/components/ui/checkbox';
import { Label } from '@/components/ui/label';
import { Card, CardContent, CardHeader, CardTitle } from '@/components/ui/card';
export interface QuestionListProps {
questions: GeneratedQuestion[];
questionTypes: QuestionType[];
selectedQuestions?: Set<string>;
onQuestionSelect?: (questionId: string, selected: boolean) => void;
onQuestionEdit?: (question: GeneratedQuestion) => void;
onQuestionDuplicate?: (question: GeneratedQuestion) => void;
onQuestionPreview?: (question: GeneratedQuestion) => void;
onQuestionRemove?: (questionId: string) => void;
onQuestionReorder?: (questions: GeneratedQuestion[]) => void;
editingQuestionId?: string | null;
emptyStateMessage?: string;
emptyStateIcon?: string;
className?: string;
title?: string;
showCount?: boolean;
}
export default function QuestionList({
questions,
questionTypes,
selectedQuestions = new Set(),
onQuestionSelect,
onQuestionEdit,
onQuestionDuplicate,
onQuestionPreview,
onQuestionRemove,
onQuestionReorder,
editingQuestionId = null,
emptyStateMessage = "No questions yet. Start by creating your first question!",
emptyStateIcon = "๐Ÿ“",
className = '',
title = "Question List",
showCount = true,
}: QuestionListProps) {
const [draggedId, setDraggedId] = useState<string | null>(null);
const [isExporting, setIsExporting] = useState(false);
const [includeAnswers, setIncludeAnswers] = useState(true);
const [exportFormat, setExportFormat] = useState<'docx' | 'txt'>('docx');
const handleSelectAll = () => {
if (onQuestionSelect) {
const allSelected = questions.length > 0 && questions.every(q => selectedQuestions.has(q.id));
questions.forEach(question => {
onQuestionSelect(question.id, !allSelected);
});
}
};
const handleDragStart = (questionId: string) => {
setDraggedId(questionId);
};
const handleDragEnd = () => {
setDraggedId(null);
};
const handleDrop = (targetId: string) => {
if (!draggedId || !onQuestionReorder || draggedId === targetId) return;
const newQuestions = [...questions];
const draggedIndex = newQuestions.findIndex(q => q.id === draggedId);
const targetIndex = newQuestions.findIndex(q => q.id === targetId);
if (draggedIndex !== -1 && targetIndex !== -1) {
// Remove the dragged item and insert it at the target position
const [draggedItem] = newQuestions.splice(draggedIndex, 1);
newQuestions.splice(targetIndex, 0, draggedItem);
onQuestionReorder(newQuestions);
}
};
const allSelected = questions.length > 0 && questions.every(q => selectedQuestions.has(q.id));
const someSelected = questions.some(q => selectedQuestions.has(q.id));
return (
<Card className={className}>
<CardHeader className="border-b">
<div className="flex items-center justify-between">
<CardTitle className="text-lg font-semibold">
๐Ÿ—‚๏ธ {title} {showCount && `(${questions.length})`}
</CardTitle>
{/* Export and Bulk Actions */}
{questions.length > 0 && (
<div className="flex items-center space-x-3">
{/* Export Options */}
<div className="flex items-center space-x-3">
{/* Format Selector */}
<Select value={exportFormat} onValueChange={(value) => setExportFormat(value as 'docx' | 'txt')}>
<SelectTrigger className="w-[100px]">
<SelectValue />
</SelectTrigger>
<SelectContent>
<SelectItem value="docx">DOCX</SelectItem>
<SelectItem value="txt">TXT</SelectItem>
</SelectContent>
</Select>
{/* Include Answers Toggle */}
<div className="flex items-center space-x-2">
<Checkbox
id="include-answers"
checked={includeAnswers}
onCheckedChange={(checked) => setIncludeAnswers(checked as boolean)}
/>
<Label htmlFor="include-answers" className="text-sm cursor-pointer">
Include Answers
</Label>
</div>
{/* Export Button */}
<Button
onClick={async () => {
if (isExporting) return;
setIsExporting(true);
try {
const questionsToExport = someSelected && selectedQuestions.size > 0
? questions.filter(q => selectedQuestions.has(q.id))
: questions;
const filename = someSelected && selectedQuestions.size > 0
? `selected-questions-${selectedQuestions.size}${includeAnswers ? '-with-answers' : '-no-answers'}.${exportFormat}`
: `all-questions-${questions.length}${includeAnswers ? '-with-answers' : '-no-answers'}.${exportFormat}`;
// Debug: Log the first question structure
if (questionsToExport.length > 0) {
console.log('First question structure:', questionsToExport[0]);
console.log('First question content:', questionsToExport[0].content);
}
if (exportFormat === 'docx') {
await exportQuestionsToDocx(questionsToExport, filename, includeAnswers);
} else {
// For now, just show a message that TXT export is not implemented
alert('TXT export is not yet implemented. Please use DOCX format.');
}
} catch (error) {
console.error('Export failed:', error);
// You could add a toast notification here
} finally {
setIsExporting(false);
}
}}
disabled={isExporting}
variant="default"
size="sm"
title={someSelected && selectedQuestions.size > 0
? `Export ${selectedQuestions.size} selected questions to ${exportFormat.toUpperCase()} file`
: `Export all questions to ${exportFormat.toUpperCase()} file`
}
>
<span>{isExporting ? 'โณ' : '๐Ÿ“„'}</span>
<span>
{isExporting
? 'Exporting...'
: someSelected && selectedQuestions.size > 0
? `Export Selected (${selectedQuestions.size})`
: `Export to ${exportFormat.toUpperCase()}`
}
</span>
</Button>
</div>
{/* Bulk Actions */}
{onQuestionSelect && (
<div className="flex items-center space-x-2">
<Button
onClick={handleSelectAll}
variant="ghost"
size="sm"
>
{allSelected ? 'Deselect All' : 'Select All'}
</Button>
{someSelected && (
<span className="text-xs text-primary">
{selectedQuestions.size} selected
</span>
)}
</div>
)}
</div>
)}
</div>
</CardHeader>
{/* Content */}
<CardContent className="h-[calc(100%-80px)] overflow-y-auto p-4">
{questions.length === 0 ? (
<EmptyState message={emptyStateMessage} icon={emptyStateIcon} />
) : (
<div className="space-y-3">
{questions.map((question) => (
<QuestionCard
key={question.id}
question={question}
questionTypes={questionTypes}
isSelected={selectedQuestions.has(question.id)}
onSelect={onQuestionSelect}
onEdit={onQuestionEdit}
onDuplicate={onQuestionDuplicate}
onPreview={onQuestionPreview}
onRemove={onQuestionRemove}
onDragStart={handleDragStart}
onDragEnd={handleDragEnd}
onDrop={handleDrop}
isDragging={draggedId === question.id}
isEditing={editingQuestionId === question.id}
/>
))}
</div>
)}
</CardContent>
</Card>
);
}
// Empty State Component
interface EmptyStateProps {
message: string;
icon: string;
}
function EmptyState({ message, icon }: EmptyStateProps) {
return (
<div className="text-center text-gray-500 dark:text-gray-400 mt-8">
<div className="text-4xl mb-4">{icon}</div>
<p className="text-sm leading-relaxed max-w-md mx-auto">{message}</p>
</div>
);
}