app/src/pages/timetable/TimetablePage.tsx
Agent Zero c7207eb805 fix(icons): replace lucide-react with @mui/icons-material
Replace lucide-react icon imports with @mui/icons-material equivalents
across all timetable pages and common Modal component.

Icons replaced:
- lucide-react Check, X, UserPlus, Users, Filter, Search -> MUI equivalents
- lucide-react ChevronDown, ChevronLeft, ChevronRight -> MUI equivalents
- lucide-react BookOpen, Clock, GraduationCap, School -> MUI equivalents
- lucide-react Calendar, MapPin, Edit, Trash2, Plus -> MUI equivalents
- lucide-react ArrowLeft, FileText, CheckCircle, XCircle -> MUI equivalents

Fixes build error: Failed to resolve import 'lucide-react'
2026-02-26 07:06:11 +00:00

196 lines
7.5 KiB
TypeScript

import React, { useEffect } from 'react';
import { useParams, Link, useNavigate } from 'react-router-dom';
import { AccessTimeIcon, AddIcon, ArrowBackIcon, CalendarTodayIcon, DeleteIcon, EditIcon, KeyboardArrowLeftIcon, KeyboardArrowRightIcon, LocationOnIcon } from '@mui/icons-material';
import useTimetableStore from '../../stores/timetableStore';
import { useProfile } from '../../contexts/ProfileContext';
import { format, parseISO, addDays, startOfWeek, isSameDay } from 'date-fns';
const TimetablePage: React.FC = () => {
const { timetableId } = useParams<{ timetableId: string }>();
const navigate = useNavigate();
const { profile } = useProfile();
const {
currentTimetable,
currentLessons,
timetableDetailLoading,
timetableDetailError,
fetchTimetableDetail,
deleteTimetable,
clearCurrentTimetable,
} = useTimetableStore();
useEffect(() => {
if (timetableId) {
fetchTimetableDetail(timetableId);
}
return () => {
clearCurrentTimetable();
};
}, [timetableId, fetchTimetableDetail, clearCurrentTimetable]);
const handleDeleteTimetable = async () => {
if (!timetableId) return;
if (confirm('Are you sure you want to delete this timetable?')) {
await deleteTimetable(timetableId);
navigate(`/timetable/classes/${currentTimetable?.class_id}`);
}
};
// Group lessons by day
const lessonsByDay = currentLessons.reduce((acc, lesson) => {
const date = lesson.day_of_week || format(parseISO(lesson.start_time), 'yyyy-MM-dd');
if (!acc[date]) acc[date] = [];
acc[date].push(lesson);
return acc;
}, {} as Record<string, typeof currentLessons>);
// Sort lessons within each day by start time
Object.keys(lessonsByDay).forEach(day => {
lessonsByDay[day].sort((a, b) =>
new Date(a.start_time).getTime() - new Date(b.start_time).getTime()
);
});
if (timetableDetailLoading) {
return (
<div className="flex justify-center items-center h-64">
<div className="animate-spin rounded-full h-12 w-12 border-b-2 border-blue-600"></div>
</div>
);
}
if (timetableDetailError || !currentTimetable) {
return (
<div className="container mx-auto px-4 py-6">
<div className="bg-red-50 border border-red-200 rounded-xl p-6">
<h2 className="text-lg font-semibold text-red-800 mb-2">Error Loading Timetable</h2>
<p className="text-red-600">{timetableDetailError || 'Timetable not found'}</p>
<Link to="/timetable/classes" className="text-blue-600 hover:underline mt-4 inline-block">
Back to Classes
</Link>
</div>
</div>
);
}
return (
<div className="container mx-auto px-4 py-6 max-w-6xl">
{/* Header */}
<div className="mb-6">
<Link
to={`/timetable/classes/${currentTimetable.class_id}`}
className="inline-flex items-center gap-2 text-gray-500 hover:text-gray-700 mb-4"
>
<ArrowBackIcon size={18} />
Back to Class
</Link>
<div className="flex flex-col md:flex-row md:items-start md:justify-between gap-4">
<div>
<h1 className="text-3xl font-bold text-gray-900 mb-2">{currentTimetable.name}</h1>
<div className="flex items-center gap-4 text-gray-500">
<span className="flex items-center gap-1">
<CalendarTodayIcon size={16} />
{format(parseISO(currentTimetable.effective_from), 'MMM d, yyyy')}
{currentTimetable.effective_until && ` - ${format(parseISO(currentTimetable.effective_until), 'MMM d, yyyy')}`}
</span>
{currentTimetable.is_recurring && (
<span className="px-2 py-1 bg-green-100 text-green-700 text-xs font-medium rounded-full">
Recurring
</span>
)}
</div>
</div>
<div className="flex items-center gap-2">
<Link
to={`/timetable/timetables/${timetableId}/lessons/new`}
className="inline-flex items-center gap-2 px-4 py-2 bg-blue-600 text-white rounded-lg hover:bg-blue-700 transition-colors"
>
<AddIcon size={18} />
Add Lesson
</Link>
<button
onClick={handleDeleteTimetable}
className="inline-flex items-center gap-2 px-4 py-2 bg-red-100 text-red-700 rounded-lg hover:bg-red-200 transition-colors"
>
<DeleteIcon size={18} />
Delete
</button>
</div>
</div>
</div>
{/* Lessons List */}
<div className="bg-white rounded-xl shadow-sm border border-gray-200">
<div className="p-6 border-b border-gray-200">
<h2 className="text-lg font-semibold text-gray-900">
Lessons ({currentLessons.length})
</h2>
</div>
{currentLessons.length === 0 ? (
<div className="p-12 text-center text-gray-500">
<AccessTimeIcon size={48} className="mx-auto mb-4 opacity-50" />
<p className="text-lg font-medium mb-2">No lessons scheduled</p>
<p>Add lessons to build your timetable</p>
</div>
) : (
<div className="divide-y divide-gray-200">
{Object.entries(lessonsByDay).map(([day, lessons]) => (
<div key={day} className="p-6">
<h3 className="text-sm font-medium text-gray-500 uppercase tracking-wide mb-4">
{currentTimetable.is_recurring
? `Day ${day}`
: format(parseISO(day), 'EEEE, MMMM d, yyyy')}
</h3>
<div className="space-y-3">
{lessons.map((lesson) => (
<Link
key={lesson.id}
to={`/timetable/lessons/${lesson.id}`}
className="flex items-center gap-4 p-4 bg-gray-50 rounded-lg hover:bg-gray-100 transition-colors"
>
<div className="flex-shrink-0 w-16 text-center">
<div className="text-sm font-medium text-gray-900">
{format(parseISO(lesson.start_time), 'HH:mm')}
</div>
<div className="text-xs text-gray-500">
{format(parseISO(lesson.end_time), 'HH:mm')}
</div>
</div>
<div className="flex-1 min-w-0">
<h4 className="font-medium text-gray-900 truncate">
{lesson.title}
</h4>
{lesson.description && (
<p className="text-sm text-gray-500 truncate">
{lesson.description}
</p>
)}
</div>
{lesson.location && (
<div className="flex items-center gap-1 text-sm text-gray-500">
<LocationOnIcon size={14} />
{lesson.location}
</div>
)}
{lesson.room && (
<span className="px-2 py-1 bg-blue-100 text-blue-700 text-xs font-medium rounded">
Room {lesson.room}
</span>
)}
</Link>
))}
</div>
</div>
))}
</div>
)}
</div>
</div>
);
};
export default TimetablePage;