make calendar view mobile friendly

This commit is contained in:
Joshua Schmucker 2026-02-06 14:17:49 +01:00
parent efaa22a8f1
commit fcaf0c4c8a

View File

@ -12,6 +12,18 @@ const ResourceCalendar: React.FC = () => {
const [loading, setLoading] = useState(true); const [loading, setLoading] = useState(true);
const [error, setError] = useState<string | null>(null); const [error, setError] = useState<string | null>(null);
// Mobile detection
const [isMobile, setIsMobile] = useState(window.innerWidth < 768);
useEffect(() => {
const handleResize = () => {
setIsMobile(window.innerWidth < 768);
};
window.addEventListener('resize', handleResize);
return () => window.removeEventListener('resize', handleResize);
}, []);
useEffect(() => { useEffect(() => {
if (!id) return; if (!id) return;
@ -20,17 +32,13 @@ const ResourceCalendar: React.FC = () => {
const loadData = async () => { const loadData = async () => {
try { try {
setLoading(true); setLoading(true);
console.log('Loading calendar for resource ID:', id);
const [resourceData, reservationsData] = await Promise.all([ const [resourceData, reservationsData] = await Promise.all([
api.getResource(id), api.getResource(id),
api.getReservations(id) api.getReservations(id)
]); ]);
console.log('Resource data:', resourceData);
console.log('Reservations data:', reservationsData);
setResource(resourceData); setResource(resourceData);
setReservations(reservationsData); setReservations(reservationsData);
} catch (err) { } catch (err) {
console.log('Error loading calendar:', err);
setError(err instanceof Error ? err.message : 'Failed to load data'); setError(err instanceof Error ? err.message : 'Failed to load data');
} finally { } finally {
setLoading(false); setLoading(false);
@ -43,8 +51,6 @@ const ResourceCalendar: React.FC = () => {
const weekStart = startOfWeek(currentWeek, { weekStartsOn: 1 }); const weekStart = startOfWeek(currentWeek, { weekStartsOn: 1 });
const weekDays = Array.from({ length: 7 }, (_, i) => addDays(weekStart, i)); const weekDays = Array.from({ length: 7 }, (_, i) => addDays(weekStart, i));
// Generate time slots from 8 AM to 8 PM // Generate time slots from 8 AM to 8 PM
const timeSlots = Array.from({ length: 13 }, (_, i) => { const timeSlots = Array.from({ length: 13 }, (_, i) => {
const hour = i + 8; // Start at 8 AM const hour = i + 8; // Start at 8 AM
@ -69,7 +75,8 @@ const ResourceCalendar: React.FC = () => {
}; };
const navigateWeek = (direction: 'prev' | 'next') => { const navigateWeek = (direction: 'prev' | 'next') => {
setCurrentWeek(prev => addDays(prev, direction === 'next' ? 7 : -7)); const days = direction === 'next' ? 7 : -7;
setCurrentWeek(addDays(currentWeek, days));
}; };
if (loading) { if (loading) {
@ -100,32 +107,44 @@ const ResourceCalendar: React.FC = () => {
} }
return ( return (
<div> <div style={{ padding: '0.5rem', maxWidth: '100vw', overflowX: 'auto' }}>
<div style={{ display: 'flex', justifyContent: 'space-between', alignItems: 'center', marginBottom: '2rem' }}> {/* Header - Mobile Optimized */}
<div> <div style={{ marginBottom: '1.5rem' }}>
<h2>{resource.name} - Calendar</h2> <h2 style={{ margin: '0 0 0.5rem 0', fontSize: '1.5rem' }}>{resource.name}</h2>
{resource.description && ( {resource.description && (
<p style={{ color: '#666', margin: '0.5rem 0' }}>{resource.description}</p> <p style={{ color: '#666', margin: '0 0 1rem 0', fontSize: '0.875rem' }}>{resource.description}</p>
)} )}
</div>
<Link <Link
to={`/reservations/new?resourceId=${resource.id}`} to={`/reservations/new?resourceId=${resource.resourceId}`}
style={{ style={{
display: 'inline-block', display: 'inline-block',
padding: '0.75rem 1.5rem', width: '100%',
padding: '0.75rem',
backgroundColor: '#27ae60', backgroundColor: '#27ae60',
color: 'white', color: 'white',
textDecoration: 'none', textDecoration: 'none',
borderRadius: '4px', borderRadius: '4px',
fontWeight: '500' fontWeight: '500',
textAlign: 'center',
fontSize: '0.875rem'
}} }}
> >
Book This Resource Book This Resource
</Link> </Link>
</div> </div>
{/* Week Navigation */} {/* Week Navigation - Mobile Optimized */}
<div style={{ display: 'flex', justifyContent: 'space-between', alignItems: 'center', marginBottom: '1.5rem', padding: '1rem', backgroundColor: '#f8f9fa', borderRadius: '4px' }}> <div style={{
display: 'flex',
flexDirection: 'column',
gap: '0.75rem',
marginBottom: '1.5rem',
padding: '1rem',
backgroundColor: '#f8f9fa',
borderRadius: '4px',
textAlign: 'center'
}}>
<div style={{ display: 'flex', justifyContent: 'space-between', alignItems: 'center' }}>
<button <button
onClick={() => navigateWeek('prev')} onClick={() => navigateWeek('prev')}
style={{ style={{
@ -134,14 +153,14 @@ const ResourceCalendar: React.FC = () => {
color: 'white', color: 'white',
border: 'none', border: 'none',
borderRadius: '4px', borderRadius: '4px',
cursor: 'pointer' cursor: 'pointer',
fontSize: '0.875rem',
flex: 1,
marginRight: '0.5rem'
}} }}
> >
Previous Week Previous
</button> </button>
<h3 style={{ margin: 0, color: '#2c3e50' }}>
{format(weekStart, 'MMM d')} - {format(addDays(weekStart, 6), 'MMM d, yyyy')}
</h3>
<button <button
onClick={() => navigateWeek('next')} onClick={() => navigateWeek('next')}
style={{ style={{
@ -150,55 +169,70 @@ const ResourceCalendar: React.FC = () => {
color: 'white', color: 'white',
border: 'none', border: 'none',
borderRadius: '4px', borderRadius: '4px',
cursor: 'pointer' cursor: 'pointer',
fontSize: '0.875rem',
flex: 1,
marginLeft: '0.5rem'
}} }}
> >
Next Week Next
</button> </button>
</div> </div>
<h3 style={{ margin: '0.5rem 0 0 0', color: '#2c3e50', fontSize: '1rem' }}>
{format(weekStart, 'MMM d')} - {format(addDays(weekStart, 6), 'MMM d, yyyy')}
</h3>
</div>
{/* Calendar Grid */} {/* Calendar Grid - Mobile Responsive */}
<div style={{ <div style={{
border: '1px solid #ddd', border: '1px solid #ddd',
borderRadius: '4px', borderRadius: '4px',
overflow: 'hidden', overflow: 'hidden',
backgroundColor: 'white' backgroundColor: 'white',
fontSize: '0.75rem'
}}> }}>
{/* Header Row */} {/* Header Row - Mobile Responsive */}
<div style={{ display: 'grid', gridTemplateColumns: '100px repeat(7, 1fr)', backgroundColor: '#2c3e50', color: 'white' }}> <div style={{
<div style={{ padding: '1rem', textAlign: 'center', fontWeight: 'bold', borderRight: '1px solid #ddd' }}> display: 'grid',
gridTemplateColumns: isMobile ? '50px repeat(7, 0.8fr)' : '60px repeat(7, 1fr)',
backgroundColor: '#2c3e50',
color: 'white',
fontSize: isMobile ? '0.6rem' : '0.7rem'
}}>
<div style={{ padding: '0.5rem', textAlign: 'center', fontWeight: 'bold', borderRight: '1px solid #ddd' }}>
Time Time
</div> </div>
{weekDays.map((day, index) => ( {weekDays.map((day, index) => (
<div <div
key={index} key={index}
style={{ style={{
padding: '1rem', padding: '0.5rem',
textAlign: 'center', textAlign: 'center',
fontWeight: 'bold', fontWeight: 'bold',
borderRight: index < 6 ? '1px solid #ddd' : 'none', borderRight: index < 6 ? '1px solid #ddd' : 'none',
backgroundColor: isToday(day) ? '#34495e' : 'transparent' backgroundColor: isToday(day) ? '#34495e' : 'transparent'
}} }}
> >
<div>{format(day, 'EEEE')}</div> <div>{format(day, 'EEE')}</div>
<div style={{ fontSize: '0.875rem', opacity: 0.8 }}> <div style={{ fontSize: '0.7rem', opacity: 0.8 }}>
{format(day, 'MMM d')} {format(day, 'M/d')}
</div> </div>
</div> </div>
))} ))}
</div> </div>
{/* Time Slots */} {/* Time Slots - Mobile Responsive */}
{timeSlots.map((timeSlot, timeIndex) => ( {timeSlots.map((timeSlot, timeIndex) => (
<div key={timeIndex} style={{ display: 'grid', gridTemplateColumns: '100px repeat(7, 1fr)' }}> <div key={timeIndex} style={{ display: 'grid', gridTemplateColumns: isMobile ? '50px repeat(7, 0.8fr)' : '60px repeat(7, 1fr)' }}>
{/* Time Column */} {/* Time Column */}
<div style={{ <div style={{
padding: '0.75rem', padding: '0.5rem',
textAlign: 'center', textAlign: 'center',
borderRight: '1px solid #ddd', borderRight: '1px solid #ddd',
borderBottom: '1px solid #ddd', borderBottom: '1px solid #ddd',
backgroundColor: '#f8f9fa', backgroundColor: '#f8f9fa',
fontSize: '0.875rem' fontSize: '0.7rem',
fontWeight: '500'
}}> }}>
{format(timeSlot, 'h a')} {format(timeSlot, 'h a')}
</div> </div>
@ -215,27 +249,46 @@ const ResourceCalendar: React.FC = () => {
borderRight: dayIndex < 6 ? '1px solid #ddd' : 'none', borderRight: dayIndex < 6 ? '1px solid #ddd' : 'none',
borderBottom: '1px solid #ddd', borderBottom: '1px solid #ddd',
backgroundColor: reservation backgroundColor: reservation
? reservation.status === 'confirmed' ? '#d4edda' ? '#d4edda'
: reservation.status === 'cancelled' ? '#f8d7da'
: '#fff3cd'
: isToday(day) ? '#f8f9fa' : 'white', : isToday(day) ? '#f8f9fa' : 'white',
fontSize: '0.75rem', fontSize: '0.7rem',
minHeight: '40px', minHeight: '35px',
display: 'flex', display: 'flex',
alignItems: 'center', alignItems: 'center',
justifyContent: 'center' justifyContent: 'center',
position: 'relative'
}} }}
> >
{reservation && ( {reservation && (
<div style={{ <div style={{
textAlign: 'center', textAlign: 'center',
color: reservation.status === 'cancelled' ? '#721c24' : '#333', color: '#333',
fontWeight: '500', fontWeight: '500',
padding: '0.25rem' padding: '0.2rem',
width: '100%',
height: '100%',
overflow: 'hidden',
display: 'flex',
flexDirection: 'column',
justifyContent: 'center'
}}> }}>
<div style={{ fontWeight: 'bold' }}>{reservation.title}</div> <div style={{
<div style={{ fontSize: '0.625rem', opacity: 0.8 }}> fontWeight: 'bold',
{format(new Date(reservation.startDate), 'h:mm')} - {format(new Date(reservation.endDate), 'h:mm')} fontSize: '0.6rem',
lineHeight: '1.2',
overflow: 'hidden',
display: '-webkit-box',
WebkitLineClamp: 2,
WebkitBoxOrient: 'vertical'
}}>
{reservation.title}
</div>
<div style={{
fontSize: '0.55rem',
opacity: 0.8,
lineHeight: '1'
}}>
{format(new Date(reservation.startDate), 'h:mm')}
</div> </div>
</div> </div>
)} )}