Press n or j to go to the next uncovered block, b, p or k for the previous block.
| 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 | 1x 9x 9x 9x 1x 39x 39x 39x 39x 13x 13x 13x 13x 13x 13x 11x 10x 13x 2x 2x 2x 2x 13x 13x 13x 13x 39x 9x 10x | import { useEffect, useState } from "react";
import axios from "axios";
import { Box, Card, CardContent, Chip, CircularProgress, Stack, Typography } from "@mui/material";
import CalendarMonthIcon from "@mui/icons-material/CalendarMonth";
import { API_ENDPOINTS } from "@/api/endpoints";
import { groupAssignmentsByDate } from "./groupAssignmentsByDate";
const _formatDayHeading = (yyyymmdd) => {
const [y, m, d] = yyyymmdd.split("-").map(Number);
const date = new Date(y, m - 1, d);
return date.toLocaleDateString(undefined, { weekday: "short", month: "short", day: "numeric" });
};
const AssignmentsCalendar = ({ refreshKey }) => {
const [buckets, setBuckets] = useState([]);
const [isLoading, setIsLoading] = useState(false);
const [hasError, setHasError] = useState(false);
useEffect(() => {
let cancelled = false;
const fetchUpcoming = async () => {
setIsLoading(true);
setHasError(false);
try {
const response = await axios.get(API_ENDPOINTS.student.dashboard.upcomingAssignments);
if (cancelled) return;
const upcoming = Array.isArray(response?.data?.upcoming) ? response.data.upcoming : [];
setBuckets(groupAssignmentsByDate(upcoming));
} catch (error) {
Iif (cancelled) return;
console.error("EI-2966 calendar fetch error:", error?.response?.data || error?.message);
setHasError(true);
setBuckets([]);
} finally {
if (!cancelled) setIsLoading(false);
}
};
fetchUpcoming();
return () => {
cancelled = true;
};
}, [refreshKey]);
return (
<Card id="student-dashboard-calendar-widget" sx={{ borderRadius: 2 }}>
<CardContent>
<Stack direction="row" alignItems="center" spacing={1} sx={{ mb: 2 }}>
<CalendarMonthIcon color="primary" />
<Typography variant="h6" sx={{ fontWeight: 600 }}>
Upcoming Assignments
</Typography>
</Stack>
{isLoading ? (
<Box sx={{ py: 3, textAlign: "center" }}>
<CircularProgress size={24} />
</Box>
) : hasError ? (
<Typography
id="student-dashboard-calendar-error"
variant="body2"
color="error"
>
Unable to load upcoming assignments. Please try again later.
</Typography>
) : buckets.length === 0 ? (
<Typography
id="student-dashboard-calendar-empty"
variant="body2"
color="text.secondary"
>
No upcoming due dates.
</Typography>
) : (
<Stack spacing={2}>
{buckets.map(({ day, items }) => (
<Box key={day} data-day-bucket={day}>
<Stack direction="row" alignItems="center" spacing={1} sx={{ mb: 0.5 }}>
<Typography
variant="subtitle2"
sx={{ fontWeight: 600, color: "primary.main" }}
>
{_formatDayHeading(day)}
</Typography>
<Chip label={items.length} size="small" />
</Stack>
<Stack spacing={0.5} sx={{ pl: 1 }}>
{items.map((a) => (
<Typography
key={a._id || `${day}-${a.title}`}
variant="body2"
color="text.primary"
>
{a.title}
{a.class_title ? (
<Typography
component="span"
variant="caption"
color="text.secondary"
sx={{ ml: 1 }}
>
ยท {a.class_title}
</Typography>
) : null}
</Typography>
))}
</Stack>
</Box>
))}
</Stack>
)}
</CardContent>
</Card>
);
};
export default AssignmentsCalendar;
|