Fixed the timetable finally
This commit is contained in:
@@ -13,6 +13,13 @@ function getDayName(dateStr: string) {
|
|||||||
return new Date(dateStr + 'T00:00:00').toLocaleDateString('en-US', { weekday: 'short' });
|
return new Date(dateStr + 'T00:00:00').toLocaleDateString('en-US', { weekday: 'short' });
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function formatDayLabel(dateStr: string) {
|
||||||
|
const date = new Date(dateStr + 'T00:00:00');
|
||||||
|
const weekday = date.toLocaleDateString('en-US', { weekday: 'short' });
|
||||||
|
const day = date.getDate();
|
||||||
|
return `${weekday} ${day}`;
|
||||||
|
}
|
||||||
|
|
||||||
function formatTimeLabel(mins: number) {
|
function formatTimeLabel(mins: number) {
|
||||||
const hours = Math.floor(mins / 60);
|
const hours = Math.floor(mins / 60);
|
||||||
const minutes = mins % 60;
|
const minutes = mins % 60;
|
||||||
@@ -24,12 +31,6 @@ const Timetable = ({ from_date, to_date, day_start, day_end }: TimetableProps) =
|
|||||||
const [selectedDayIndex, setSelectedDayIndex] = useState(0);
|
const [selectedDayIndex, setSelectedDayIndex] = useState(0);
|
||||||
const [windowWidth, setWindowWidth] = useState(typeof window !== 'undefined' ? window.innerWidth : 1024);
|
const [windowWidth, setWindowWidth] = useState(typeof window !== 'undefined' ? window.innerWidth : 1024);
|
||||||
|
|
||||||
useEffect(() => {
|
|
||||||
const handleResize = () => setWindowWidth(window.innerWidth);
|
|
||||||
window.addEventListener('resize', handleResize);
|
|
||||||
return () => window.removeEventListener('resize', handleResize);
|
|
||||||
}, []);
|
|
||||||
|
|
||||||
const days = useMemo(() => {
|
const days = useMemo(() => {
|
||||||
const start = new Date(from_date + 'T00:00:00');
|
const start = new Date(from_date + 'T00:00:00');
|
||||||
const end = new Date(to_date + 'T23:59:59');
|
const end = new Date(to_date + 'T23:59:59');
|
||||||
@@ -42,6 +43,18 @@ const Timetable = ({ from_date, to_date, day_start, day_end }: TimetableProps) =
|
|||||||
return result; // Chronological (oldest to newest)
|
return result; // Chronological (oldest to newest)
|
||||||
}, [from_date, to_date]);
|
}, [from_date, to_date]);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
const handleResize = () => setWindowWidth(window.innerWidth);
|
||||||
|
window.addEventListener('resize', handleResize);
|
||||||
|
return () => window.removeEventListener('resize', handleResize);
|
||||||
|
}, []);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
if (days.length > 0) {
|
||||||
|
setSelectedDayIndex(days.length - 1);
|
||||||
|
}
|
||||||
|
}, [days]);
|
||||||
|
|
||||||
const reversedDays = useMemo(() => [...days].reverse(), [days]);
|
const reversedDays = useMemo(() => [...days].reverse(), [days]);
|
||||||
|
|
||||||
const timeSteps = useMemo(() => {
|
const timeSteps = useMemo(() => {
|
||||||
@@ -67,7 +80,7 @@ const Timetable = ({ from_date, to_date, day_start, day_end }: TimetableProps) =
|
|||||||
const groups = [];
|
const groups = [];
|
||||||
for (let i = 0; i < activeDays.length; i += 3) {
|
for (let i = 0; i < activeDays.length; i += 3) {
|
||||||
groups.push({
|
groups.push({
|
||||||
label: `${getDayName(activeDays[i])} - ${getDayName(activeDays[Math.min(i + 2, activeDays.length - 1)])}`,
|
label: `${formatDayLabel(activeDays[i])} - ${formatDayLabel(activeDays[Math.min(i + 2, activeDays.length - 1)])}`,
|
||||||
startIndex: i,
|
startIndex: i,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
@@ -79,7 +92,7 @@ const Timetable = ({ from_date, to_date, day_start, day_end }: TimetableProps) =
|
|||||||
|
|
||||||
// Small tier (single day)
|
// Small tier (single day)
|
||||||
const groups = activeDays.map((day, idx) => ({
|
const groups = activeDays.map((day, idx) => ({
|
||||||
label: getDayName(day),
|
label: formatDayLabel(day),
|
||||||
startIndex: idx,
|
startIndex: idx,
|
||||||
}));
|
}));
|
||||||
return {
|
return {
|
||||||
@@ -122,11 +135,11 @@ const Timetable = ({ from_date, to_date, day_start, day_end }: TimetableProps) =
|
|||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="bg-white p-4 rounded-xl shadow-sm border border-gray-200">
|
<div className="bg-white p-4 rounded-xl shadow-sm border border-gray-200">
|
||||||
<h3 className="text-lg font-semibold mb-4 text-gray-800">Sleep Timetable</h3>
|
<h3 className="text-lg font-semibold mb-2 text-gray-800">Sleep Timetable</h3>
|
||||||
|
|
||||||
{/* Tab Selector (Mobile & Medium) */}
|
{/* Tab Selector (Mobile & Medium) */}
|
||||||
{(tier === 'medium' || tier === 'small') && (
|
{(tier === 'medium' || tier === 'small') && (
|
||||||
<div className="flex overflow-x-auto gap-2 mb-4 pb-2 no-scrollbar">
|
<div className="flex overflow-x-auto gap-2 mb-2 pb-1 no-scrollbar">
|
||||||
{tabGroups.map((group, idx) => {
|
{tabGroups.map((group, idx) => {
|
||||||
const isActive = selectedDayIndex === days.indexOf(reversedDays[group.startIndex]);
|
const isActive = selectedDayIndex === days.indexOf(reversedDays[group.startIndex]);
|
||||||
|
|
||||||
@@ -155,9 +168,7 @@ const Timetable = ({ from_date, to_date, day_start, day_end }: TimetableProps) =
|
|||||||
style={{
|
style={{
|
||||||
gridTemplateColumns: tier === 'desktop'
|
gridTemplateColumns: tier === 'desktop'
|
||||||
? `4rem repeat(${days.length}, minmax(40px, 1fr))`
|
? `4rem repeat(${days.length}, minmax(40px, 1fr))`
|
||||||
: tier === 'medium'
|
: `4rem repeat(${visibleDays.length}, 1fr)`
|
||||||
? `4rem repeat(3, 1fr)`
|
|
||||||
: `4rem 1fr`
|
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<div className="h-2" /> {/* Empty corner cell */}
|
<div className="h-2" /> {/* Empty corner cell */}
|
||||||
@@ -171,15 +182,13 @@ const Timetable = ({ from_date, to_date, day_start, day_end }: TimetableProps) =
|
|||||||
{/* Time Grid Container */}
|
{/* Time Grid Container */}
|
||||||
<div className="relative">
|
<div className="relative">
|
||||||
<div
|
<div
|
||||||
className="grid"
|
className="grid"
|
||||||
style={{
|
style={{
|
||||||
gridTemplateColumns: tier === 'desktop'
|
gridTemplateColumns: tier === 'desktop'
|
||||||
? `4rem repeat(${days.length}, minmax(40px, 1fr))`
|
? `4rem repeat(${days.length}, minmax(40px, 1fr))`
|
||||||
: tier === 'medium'
|
: `4rem repeat(${visibleDays.length}, 1fr)`
|
||||||
? `4rem repeat(3, 1fr)`
|
}}
|
||||||
: `4rem 1fr`
|
>
|
||||||
}}
|
|
||||||
>
|
|
||||||
{/* Time Rows */}
|
{/* Time Rows */}
|
||||||
{timeSteps.map((step) => (
|
{timeSteps.map((step) => (
|
||||||
<Fragment key={step}>
|
<Fragment key={step}>
|
||||||
|
|||||||
@@ -63,7 +63,7 @@ describe('SleepTimetable', () => {
|
|||||||
|
|
||||||
// Check for 3-day interval tabs
|
// Check for 3-day interval tabs
|
||||||
// In reversed order (25, 24, 23...), the first group is Fri - Wed
|
// In reversed order (25, 24, 23...), the first group is Fri - Wed
|
||||||
expect(screen.getByRole('button', { name: /FRI - WED/i })).toBeInTheDocument();
|
expect(screen.getByRole('button', { name: /Fri \d+ - Wed \d+/i })).toBeInTheDocument();
|
||||||
});
|
});
|
||||||
|
|
||||||
it('renders in small mode (single-day tabs and 1-column grid)', () => {
|
it('renders in small mode (single-day tabs and 1-column grid)', () => {
|
||||||
|
|||||||
Reference in New Issue
Block a user