fix(routing): add TimetableListPage for /timetable — detail view required :timetableId
Some checks failed
app-ci-deploy / test-build-deploy (push) Has been cancelled
Some checks failed
app-ci-deploy / test-build-deploy (push) Has been cancelled
Bare /timetable routed to TimetablePage which called useParams<{timetableId}>() and
immediately hit !currentTimetable → "Error Loading Timetable". New TimetableListPage
uses fetchMyTimetables() from timetableStore. AppRoutes now: /timetable →
TimetableListPage, /timetable/:timetableId → TimetablePage.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
parent
5b6e461706
commit
5869c741d7
@ -30,6 +30,7 @@ import DashboardPage from './pages/user/dashboardPage';
|
||||
// Timetable Module Pages
|
||||
import {
|
||||
TimetablePage,
|
||||
TimetableListPage,
|
||||
ClassesPage,
|
||||
LessonPage,
|
||||
TaughtLessonsPage,
|
||||
@ -162,7 +163,8 @@ const AppRoutes: React.FC = () => {
|
||||
<Route element={<RequireAuth />}>
|
||||
<Route element={<FullContextRoutes />}>
|
||||
{/* Timetable Module Routes */}
|
||||
<Route path="/timetable" element={<TimetablePage />} />
|
||||
<Route path="/timetable" element={<TimetableListPage />} />
|
||||
<Route path="/timetable/:timetableId" element={<TimetablePage />} />
|
||||
<Route path="/classes" element={<ClassesPage />} />
|
||||
<Route path="/my-classes" element={<MyClassesPage />} />
|
||||
<Route path="/classes/:classId" element={<ClassDetailPage />} />
|
||||
|
||||
90
src/pages/timetable/TimetableListPage.tsx
Normal file
90
src/pages/timetable/TimetableListPage.tsx
Normal file
@ -0,0 +1,90 @@
|
||||
import React, { useEffect } from 'react';
|
||||
import { Link } from 'react-router-dom';
|
||||
import { CalendarToday, KeyboardArrowRight } from '@mui/icons-material';
|
||||
import useTimetableStore from '../../stores/timetableStore';
|
||||
import { format, parseISO } from 'date-fns';
|
||||
|
||||
const TimetableListPage: React.FC = () => {
|
||||
const { timetables, timetablesLoading, timetablesError, fetchMyTimetables } = useTimetableStore();
|
||||
|
||||
useEffect(() => {
|
||||
fetchMyTimetables();
|
||||
}, [fetchMyTimetables]);
|
||||
|
||||
if (timetablesLoading) {
|
||||
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>
|
||||
);
|
||||
}
|
||||
|
||||
if (timetablesError) {
|
||||
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 Timetables</h2>
|
||||
<p className="text-red-600">{timetablesError}</p>
|
||||
<button
|
||||
onClick={() => fetchMyTimetables()}
|
||||
className="mt-4 text-sm font-medium text-blue-600 hover:underline"
|
||||
>
|
||||
Retry
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
return (
|
||||
<div className="container mx-auto px-4 py-6 max-w-4xl">
|
||||
<div className="flex items-center justify-between mb-6">
|
||||
<div>
|
||||
<h1 className="text-3xl font-bold text-gray-900">Timetables</h1>
|
||||
<p className="mt-1 text-gray-500">Your scheduled timetables</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{timetables.length === 0 ? (
|
||||
<div className="text-center py-16">
|
||||
<CalendarToday sx={{ fontSize: 48 }} className="mx-auto mb-4 text-gray-300" />
|
||||
<h3 className="text-lg font-medium text-gray-900">No timetables yet</h3>
|
||||
<p className="mt-2 text-gray-500">
|
||||
Timetables are created from a class. Go to{' '}
|
||||
<Link to="/classes" className="text-blue-600 hover:underline">Classes</Link>{' '}
|
||||
to get started.
|
||||
</p>
|
||||
</div>
|
||||
) : (
|
||||
<div className="space-y-3">
|
||||
{timetables.map((timetable) => (
|
||||
<Link
|
||||
key={timetable.id}
|
||||
to={`/timetable/${timetable.id}`}
|
||||
className="flex items-center gap-4 p-4 bg-white rounded-xl shadow-sm border border-gray-200 hover:shadow-md transition-shadow"
|
||||
>
|
||||
<CalendarToday className="text-blue-500 flex-shrink-0" />
|
||||
<div className="flex-1 min-w-0">
|
||||
<h3 className="font-semibold text-gray-900 truncate">{timetable.name}</h3>
|
||||
<p className="text-sm text-gray-500">
|
||||
{format(parseISO(timetable.start_date), 'MMM d, yyyy')}
|
||||
{timetable.end_date
|
||||
? ` – ${format(parseISO(timetable.end_date), 'MMM d, yyyy')}`
|
||||
: ' (ongoing)'}
|
||||
</p>
|
||||
</div>
|
||||
{timetable.recurrence_rule && (
|
||||
<span className="px-2 py-1 bg-green-100 text-green-700 text-xs font-medium rounded-full flex-shrink-0">
|
||||
Recurring
|
||||
</span>
|
||||
)}
|
||||
<KeyboardArrowRight className="text-gray-400 flex-shrink-0" />
|
||||
</Link>
|
||||
))}
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export default TimetableListPage;
|
||||
@ -3,6 +3,7 @@
|
||||
*/
|
||||
|
||||
export { default as TimetablePage } from './TimetablePage';
|
||||
export { default as TimetableListPage } from './TimetableListPage';
|
||||
export { default as ClassesPage } from './ClassesPage';
|
||||
export { default as LessonPage } from './LessonPage';
|
||||
export { default as TaughtLessonsPage } from './TaughtLessonsPage';
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user