parent
5c99f318e8
commit
da8118efc5
3 changed files with 101 additions and 17 deletions
@ -0,0 +1,89 @@ |
||||
import type { AICourseListItem } from '../../queries/ai-course'; |
||||
import type { DifficultyLevel } from './AICourse'; |
||||
import { BookOpen, Calendar } from 'lucide-react'; |
||||
|
||||
type AICourseCardProps = { |
||||
course: AICourseListItem; |
||||
}; |
||||
|
||||
export function AICourseCard(props: AICourseCardProps) { |
||||
const { course } = props; |
||||
|
||||
// Format date if available
|
||||
const formattedDate = course.createdAt |
||||
? new Date(course.createdAt).toLocaleDateString('en-US', { |
||||
month: 'short', |
||||
day: 'numeric', |
||||
}) |
||||
: null; |
||||
|
||||
// Map difficulty to color
|
||||
const difficultyColor = |
||||
{ |
||||
beginner: 'bg-green-100 text-green-700', |
||||
intermediate: 'bg-blue-100 text-blue-700', |
||||
advanced: 'bg-purple-100 text-purple-700', |
||||
}[course.difficulty as DifficultyLevel] || 'bg-gray-100 text-gray-700'; |
||||
|
||||
// Get a short description preview if available
|
||||
const descriptionPreview = course.data |
||||
? JSON.parse(course.data)?.description?.substring(0, 100) + |
||||
(JSON.parse(course.data)?.description?.length > 100 ? '...' : '') |
||||
: null; |
||||
|
||||
// Calculate progress percentage
|
||||
const totalTopics = course.lessonCount || 0; |
||||
const completedTopics = course.progress?.done?.length || 0; |
||||
const progressPercentage = |
||||
totalTopics > 0 ? Math.round((completedTopics / totalTopics) * 100) : 0; |
||||
|
||||
return ( |
||||
<a |
||||
href={`/ai-tutor/${course.slug}`} |
||||
className="group relative flex w-full flex-col overflow-hidden rounded-md border border-gray-300 bg-white p-4 text-left transition-all hover:border-gray-300 hover:bg-gray-50" |
||||
> |
||||
<div className="mb-2 flex items-center justify-between"> |
||||
<span |
||||
className={`rounded-full px-2.5 py-1 text-xs font-medium capitalize ${difficultyColor}`} |
||||
> |
||||
{course.difficulty} |
||||
</span> |
||||
{formattedDate && ( |
||||
<span className="flex items-center text-xs text-gray-500"> |
||||
<Calendar className="mr-1 h-3 w-3" /> |
||||
{formattedDate} |
||||
</span> |
||||
)} |
||||
</div> |
||||
|
||||
<h3 className="mb-2 text-base font-semibold text-gray-900"> |
||||
{course.title} |
||||
</h3> |
||||
|
||||
{descriptionPreview && ( |
||||
<p className="mb-3 text-xs text-gray-600">{descriptionPreview}</p> |
||||
)} |
||||
|
||||
<div className="mt-auto flex items-center justify-between pt-2"> |
||||
<div className="flex items-center text-xs text-gray-600"> |
||||
<BookOpen className="mr-1 h-3.5 w-3.5" /> |
||||
<span>{totalTopics} topics</span> |
||||
</div> |
||||
|
||||
{totalTopics > 0 && ( |
||||
<div className="flex items-center"> |
||||
<div className="mr-2 h-1.5 w-16 overflow-hidden rounded-full bg-gray-200"> |
||||
<div |
||||
className="h-full rounded-full bg-blue-600" |
||||
style={{ width: `${progressPercentage}%` }} |
||||
/> |
||||
</div> |
||||
<span className="text-xs font-medium text-gray-700"> |
||||
{progressPercentage}% |
||||
</span> |
||||
</div> |
||||
)} |
||||
</div> |
||||
</a> |
||||
); |
||||
} |
Loading…
Reference in new issue