Shih-hungg's picture
Add hover effect
c5b3050
'use client';
import { GeneratedQuestion, QuestionType } from '@/types/quiz';
import { Card } from '@/components/ui/card';
import { Button } from '@/components/ui/button';
import { Checkbox } from '@/components/ui/checkbox';
import { cn } from '@/lib/utils';
export interface QuestionCardProps {
question: GeneratedQuestion;
questionTypes: QuestionType[];
onEdit?: (question: GeneratedQuestion) => void;
onDuplicate?: (question: GeneratedQuestion) => void;
onPreview?: (question: GeneratedQuestion) => void;
onRemove?: (questionId: string) => void;
isSelected?: boolean;
onSelect?: (questionId: string, selected: boolean) => void;
onDragStart?: (questionId: string) => void;
onDragEnd?: () => void;
onDragOver?: (e: React.DragEvent) => void;
onDrop?: (questionId: string) => void;
isDragging?: boolean;
isEditing?: boolean;
className?: string;
}
export default function QuestionCard({
question,
questionTypes,
onEdit,
onDuplicate,
onPreview,
onRemove,
isSelected = false,
onSelect,
onDragStart,
onDragEnd,
onDragOver,
onDrop,
isDragging = false,
isEditing = false,
className = '',
}: QuestionCardProps) {
const questionType = questionTypes.find(t => t.id === question.type);
const handleSelect = () => {
if (onSelect) {
onSelect(question.id, !isSelected);
}
};
const handleDragStart = (e: React.DragEvent) => {
e.dataTransfer.setData('text/plain', question.id);
if (onDragStart) {
onDragStart(question.id);
}
};
const handleDragEnd = () => {
if (onDragEnd) {
onDragEnd();
}
};
const handleDragOver = (e: React.DragEvent) => {
e.preventDefault();
if (onDragOver) {
onDragOver(e);
}
};
const handleDrop = (e: React.DragEvent) => {
e.preventDefault();
const draggedId = e.dataTransfer.getData('text/plain');
if (onDrop && draggedId !== question.id) {
onDrop(question.id);
}
};
return (
<Card
draggable
onDragStart={handleDragStart}
onDragEnd={handleDragEnd}
onDragOver={handleDragOver}
onDrop={handleDrop}
className={cn(
"p-4 hover:bg-accent group transition-all duration-200 cursor-move",
isSelected && "ring-2 ring-primary bg-primary/5",
isDragging && "opacity-50 scale-95 rotate-2",
className
)}
>
<div className="flex items-start justify-between">
<div className="flex items-start space-x-3 flex-1">
{/* Drag Handle */}
<div className="mt-1 text-gray-400 hover:text-gray-600 dark:hover:text-gray-300 cursor-move">
<svg width="12" height="12" viewBox="0 0 12 12" fill="currentColor">
<circle cx="2" cy="2" r="1"/>
<circle cx="6" cy="2" r="1"/>
<circle cx="10" cy="2" r="1"/>
<circle cx="2" cy="6" r="1"/>
<circle cx="6" cy="6" r="1"/>
<circle cx="10" cy="6" r="1"/>
<circle cx="2" cy="10" r="1"/>
<circle cx="6" cy="10" r="1"/>
<circle cx="10" cy="10" r="1"/>
</svg>
</div>
{/* Selection Checkbox */}
{onSelect && (
<Checkbox
checked={isSelected}
onCheckedChange={handleSelect}
className="mt-1"
/>
)}
{/* Question Content */}
<div className="flex-1 min-w-0">
<div className="flex items-center space-x-2 mb-2">
<span className="text-sm font-medium text-primary">
{questionType?.name || 'Unknown Type'}
</span>
<span className="text-xs text-muted-foreground">
{question.points} pts
</span>
<span className="text-xs text-muted-foreground">
{new Date(question.createdAt).toLocaleDateString()}
</span>
</div>
<div className="text-foreground text-sm leading-relaxed">
<p className="font-medium mb-2">{question.stem}</p>
{/* Display Options */}
{question.content && question.content.Options && (
<div className="mt-3 space-y-1">
{Object.entries(question.content.Options).map(([key, value]) => (
<div key={key} className="text-xs text-muted-foreground">
<span className="font-medium">{key})</span> {value as string}
</div>
))}
</div>
)}
{/* Display Answer */}
{question.content && question.content.Answer && (
<div className="mt-2 text-xs">
<span className="font-medium text-green-600 dark:text-green-400">Answer: {question.content.Answer}</span>
</div>
)}
</div>
</div>
</div>
{/* Action Buttons */}
<div className="flex items-center space-x-1 opacity-0 group-hover:opacity-100 transition-opacity duration-200 ml-2">
{onEdit && (
<Button
onClick={() => onEdit(question)}
variant={isEditing ? "default" : "ghost"}
size="icon"
className={cn(
"h-8 w-8 transition-all duration-200",
isEditing
? "bg-primary text-primary-foreground hover:bg-primary/90 shadow-md"
: "hover:bg-blue-100 hover:text-blue-600 dark:hover:bg-blue-900/30 dark:hover:text-blue-400 hover:scale-105 hover:shadow-sm"
)}
title={isEditing ? "Currently editing" : "Edit question"}
>
<span className="text-sm transition-transform duration-200 group-hover:scale-110">
โœ๏ธ
</span>
</Button>
)}
{onDuplicate && (
<Button
onClick={() => onDuplicate(question)}
variant="ghost"
size="icon"
className="h-8 w-8 transition-all duration-200 hover:bg-green-100 hover:text-green-600 dark:hover:bg-green-900/30 dark:hover:text-green-400 hover:scale-105 hover:shadow-sm"
title="Duplicate question"
>
<span className="text-sm transition-transform duration-200 group-hover:scale-110">๐Ÿ“„</span>
</Button>
)}
{onPreview && (
<Button
onClick={() => onPreview(question)}
variant="ghost"
size="icon"
className="h-8 w-8 transition-all duration-200 hover:bg-purple-100 hover:text-purple-600 dark:hover:bg-purple-900/30 dark:hover:text-purple-400 hover:scale-105 hover:shadow-sm"
title="Preview question"
>
<span className="text-sm transition-transform duration-200 group-hover:scale-110">๐Ÿ‘๏ธ</span>
</Button>
)}
{onRemove && (
<Button
onClick={() => onRemove(question.id)}
variant="ghost"
size="icon"
className="h-8 w-8 transition-all duration-200 hover:bg-red-100 hover:text-red-600 dark:hover:bg-red-900/30 dark:hover:text-red-400 hover:scale-105 hover:shadow-sm"
title="Remove question"
>
<span className="text-sm transition-transform duration-200 group-hover:scale-110">โœ•</span>
</Button>
)}
</div>
</div>
</Card>
);
}