| | |
| | import React from "react"; |
| | import { Edit2, Trash2, Users } from "lucide-react"; |
| |
|
| | export default function ClassesTable({ |
| | classes, |
| | isLoading, |
| | onEdit, |
| | onDelete, |
| | onManageStudents, |
| | }) { |
| | if (isLoading) { |
| | return ( |
| | <div className="space-y-3"> |
| | {[1, 2, 3].map((i) => ( |
| | <div |
| | key={i} |
| | className="h-16 w-full animate-pulse rounded-md bg-stone-100" |
| | /> |
| | ))} |
| | </div> |
| | ); |
| | } |
| |
|
| | if (!classes || classes.length === 0) { |
| | return ( |
| | <div className="text-center py-12 text-stone-500"> |
| | <p>No classes created yet.</p> |
| | </div> |
| | ); |
| | } |
| |
|
| | return ( |
| | <> |
| | {/* Desktop table */} |
| | <div className="hidden md:block overflow-x-auto"> |
| | <table className="min-w-full text-sm border border-stone-200 rounded-lg overflow-hidden"> |
| | <thead className="bg-stone-50"> |
| | <tr> |
| | <th className="px-4 py-2 text-left font-semibold text-stone-800"> |
| | Class Name |
| | </th> |
| | <th className="px-4 py-2 text-left font-semibold text-stone-800"> |
| | Schedule |
| | </th> |
| | <th className="px-4 py-2 text-left font-semibold text-stone-800"> |
| | Status |
| | </th> |
| | <th className="px-4 py-2 text-right font-semibold text-stone-800"> |
| | Actions |
| | </th> |
| | </tr> |
| | </thead> |
| | <tbody> |
| | {classes.map((cls) => ( |
| | <tr key={cls.id} className="border-t border-stone-200"> |
| | <td className="px-4 py-3 align-top"> |
| | <div> |
| | <div className="font-medium text-stone-900"> |
| | {cls.name} |
| | </div> |
| | {cls.description && ( |
| | <div className="text-xs text-stone-500 max-w-xs truncate"> |
| | {cls.description} |
| | </div> |
| | )} |
| | </div> |
| | </td> |
| | <td className="px-4 py-3 align-top"> |
| | <div className="space-y-1 text-sm"> |
| | {(cls.schedule || []).map((slot, idx) => ( |
| | <div key={idx}> |
| | <span className="font-medium">{slot.day}:</span>{" "} |
| | {slot.start_time && slot.end_time |
| | ? `${slot.start_time} - ${slot.end_time}` |
| | : slot.time || ""} |
| | </div> |
| | ))} |
| | </div> |
| | </td> |
| | <td className="px-4 py-3 align-top"> |
| | <span |
| | className={`inline-flex items-center rounded-full px-2.5 py-0.5 text-xs font-medium ${ |
| | cls.is_active |
| | ? "bg-green-100 text-green-800" |
| | : "bg-stone-200 text-stone-700" |
| | }`} |
| | > |
| | {cls.is_active ? "Active" : "Inactive"} |
| | </span> |
| | </td> |
| | <td className="px-4 py-3 align-top text-right"> |
| | <div className="flex justify-end gap-2"> |
| | <button |
| | type="button" |
| | onClick={() => onManageStudents(cls)} |
| | className="inline-flex items-center gap-1 rounded-md border border-stone-300 px-2.5 py-1 text-xs font-medium text-stone-800 hover:bg-stone-50" |
| | > |
| | <Users className="w-4 h-4" /> |
| | Students |
| | </button> |
| | <button |
| | type="button" |
| | onClick={() => onEdit(cls)} |
| | className="inline-flex items-center justify-center rounded-md border border-stone-300 px-2.5 py-1 text-xs text-stone-800 hover:bg-stone-50" |
| | > |
| | <Edit2 className="w-4 h-4" /> |
| | </button> |
| | <button |
| | type="button" |
| | onClick={() => onDelete(cls.id)} |
| | className="inline-flex items-center justify-center rounded-md border border-red-200 px-2.5 py-1 text-xs text-red-700 hover:bg-red-50" |
| | > |
| | <Trash2 className="w-4 h-4" /> |
| | </button> |
| | </div> |
| | </td> |
| | </tr> |
| | ))} |
| | </tbody> |
| | </table> |
| | </div> |
| | |
| | {/* Mobile cards */} |
| | <div className="md:hidden space-y-3"> |
| | {classes.map((cls) => ( |
| | <div |
| | key={cls.id} |
| | className="rounded-lg border border-stone-200 p-4 bg-white shadow-sm" |
| | > |
| | <div className="flex justify-between items-start mb-2"> |
| | <div className="font-semibold text-stone-900">{cls.name}</div> |
| | <span |
| | className={`inline-flex items-center rounded-full px-2.5 py-0.5 text-xs font-medium ${ |
| | cls.is_active |
| | ? "bg-green-100 text-green-800" |
| | : "bg-stone-200 text-stone-700" |
| | }`} |
| | > |
| | {cls.is_active ? "Active" : "Inactive"} |
| | </span> |
| | </div> |
| | {cls.description && ( |
| | <p className="text-sm text-stone-600 mb-3"> |
| | {cls.description} |
| | </p> |
| | )} |
| | <div className="space-y-1 mb-3 text-sm"> |
| | {(cls.schedule || []).map((slot, idx) => ( |
| | <div key={idx}> |
| | <span className="font-medium">{slot.day}:</span>{" "} |
| | {slot.start_time && slot.end_time |
| | ? `${slot.start_time} - ${slot.end_time}` |
| | : slot.time || ""} |
| | </div> |
| | ))} |
| | </div> |
| | <div className="flex gap-2"> |
| | <button |
| | type="button" |
| | onClick={() => onManageStudents(cls)} |
| | className="flex-1 inline-flex items-center justify-center gap-1 rounded-md border border-stone-300 px-2.5 py-1.5 text-xs font-medium text-stone-800 hover:bg-stone-50" |
| | > |
| | <Users className="w-4 h-4" /> |
| | Students |
| | </button> |
| | <button |
| | type="button" |
| | onClick={() => onEdit(cls)} |
| | className="inline-flex items-center justify-center rounded-md border border-stone-300 px-2.5 py-1.5 text-xs text-stone-800 hover:bg-stone-50" |
| | > |
| | <Edit2 className="w-4 h-4" /> |
| | </button> |
| | <button |
| | type="button" |
| | onClick={() => onDelete(cls.id)} |
| | className="inline-flex items-center justify-center rounded-md border border-red-200 px-2.5 py-1.5 text-xs text-red-700 hover:bg-red-50" |
| | > |
| | <Trash2 className="w-4 h-4" /> |
| | </button> |
| | </div> |
| | </div> |
| | ))} |
| | </div> |
| | </> |
| | ); |
| | } |
| |
|