216 lines
5.5 KiB
JavaScript
216 lines
5.5 KiB
JavaScript
import React, { useState } from 'react';
|
|
import { View, Text, StyleSheet, TouchableOpacity, ScrollView } from 'react-native';
|
|
import { FontAwesome } from '@expo/vector-icons';
|
|
import Colors from '../../../constants';
|
|
|
|
const days = ['Sun', 'Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat'];
|
|
const months = [
|
|
'January', 'February', 'March', 'April', 'May', 'June',
|
|
'July', 'August', 'September', 'October', 'November', 'December'
|
|
];
|
|
|
|
const CalendarView = ({ onSelectDate, events = [] }) => {
|
|
const [currentMonth, setCurrentMonth] = useState(new Date());
|
|
const today = new Date();
|
|
|
|
const getDaysInMonth = (date) => {
|
|
const year = date.getFullYear();
|
|
const month = date.getMonth();
|
|
const daysInMonth = new Date(year, month + 1, 0).getDate();
|
|
const firstDay = new Date(year, month, 1).getDay();
|
|
|
|
const days = [];
|
|
|
|
// Add empty days for the start of the month
|
|
for (let i = 0; i < firstDay; i++) {
|
|
days.push({ day: '', date: null });
|
|
}
|
|
|
|
// Add days of the month
|
|
for (let i = 1; i <= daysInMonth; i++) {
|
|
const date = new Date(year, month, i);
|
|
const hasEvent = events.some(event => {
|
|
const eventDate = new Date(event.startDate);
|
|
return eventDate.getDate() === i &&
|
|
eventDate.getMonth() === month &&
|
|
eventDate.getFullYear() === year;
|
|
});
|
|
|
|
const isToday = today.getDate() === i &&
|
|
today.getMonth() === month &&
|
|
today.getFullYear() === year;
|
|
|
|
days.push({
|
|
day: i,
|
|
date,
|
|
hasEvent,
|
|
isToday
|
|
});
|
|
}
|
|
|
|
return days;
|
|
};
|
|
|
|
const handlePrevMonth = () => {
|
|
const prevMonth = new Date(currentMonth);
|
|
prevMonth.setMonth(prevMonth.getMonth() - 1);
|
|
setCurrentMonth(prevMonth);
|
|
};
|
|
|
|
const handleNextMonth = () => {
|
|
const nextMonth = new Date(currentMonth);
|
|
nextMonth.setMonth(nextMonth.getMonth() + 1);
|
|
setCurrentMonth(nextMonth);
|
|
};
|
|
|
|
const calendarDays = getDaysInMonth(currentMonth);
|
|
|
|
return (
|
|
<View style={styles.container}>
|
|
<View style={styles.header}>
|
|
<TouchableOpacity
|
|
onPress={handlePrevMonth}
|
|
style={styles.navButton}
|
|
hitSlop={{ top: 10, bottom: 10, left: 10, right: 10 }}
|
|
>
|
|
<FontAwesome name="chevron-left" size={14} color={Colors.primary} />
|
|
</TouchableOpacity>
|
|
<Text style={styles.monthYear}>
|
|
{months[currentMonth.getMonth()]} {currentMonth.getFullYear()}
|
|
</Text>
|
|
<TouchableOpacity
|
|
onPress={handleNextMonth}
|
|
style={styles.navButton}
|
|
hitSlop={{ top: 10, bottom: 10, left: 10, right: 10 }}
|
|
>
|
|
<FontAwesome name="chevron-right" size={14} color={Colors.primary} />
|
|
</TouchableOpacity>
|
|
</View>
|
|
|
|
<View style={styles.daysHeader}>
|
|
{days.map(day => (
|
|
<Text key={day} style={styles.dayHeader}>{day}</Text>
|
|
))}
|
|
</View>
|
|
|
|
<View style={styles.calendarGrid}>
|
|
{calendarDays.map((item, index) => (
|
|
<TouchableOpacity
|
|
key={index}
|
|
style={[
|
|
styles.day,
|
|
!item.date && styles.emptyDay,
|
|
item.isToday && styles.todayDay,
|
|
item.hasEvent && styles.eventDay
|
|
]}
|
|
onPress={() => item.date && onSelectDate(item.date)}
|
|
disabled={!item.date}
|
|
activeOpacity={item.date ? 0.7 : 1}
|
|
>
|
|
<Text style={[
|
|
styles.dayText,
|
|
item.isToday && styles.todayText
|
|
]}>
|
|
{item.day}
|
|
</Text>
|
|
{item.hasEvent && <View style={styles.eventDot} />}
|
|
</TouchableOpacity>
|
|
))}
|
|
</View>
|
|
</View>
|
|
);
|
|
};
|
|
|
|
const styles = StyleSheet.create({
|
|
container: {
|
|
backgroundColor: Colors.card,
|
|
borderRadius: 10,
|
|
padding: 14,
|
|
margin: 16,
|
|
shadowColor: Colors.cardShadow,
|
|
shadowOffset: { width: 0, height: 3 },
|
|
shadowOpacity: 0.1,
|
|
shadowRadius: 6,
|
|
elevation: 3,
|
|
borderWidth: 1,
|
|
borderColor: Colors.border,
|
|
},
|
|
header: {
|
|
flexDirection: 'row',
|
|
justifyContent: 'space-between',
|
|
alignItems: 'center',
|
|
marginBottom: 16,
|
|
paddingHorizontal: 8,
|
|
},
|
|
navButton: {
|
|
width: 28,
|
|
height: 28,
|
|
borderRadius: 14,
|
|
justifyContent: 'center',
|
|
alignItems: 'center',
|
|
backgroundColor: Colors.background,
|
|
borderWidth: 1,
|
|
borderColor: Colors.border,
|
|
},
|
|
monthYear: {
|
|
fontSize: 16,
|
|
fontWeight: '600',
|
|
color: Colors.text,
|
|
},
|
|
daysHeader: {
|
|
flexDirection: 'row',
|
|
justifyContent: 'space-around',
|
|
marginBottom: 10,
|
|
borderBottomWidth: 1,
|
|
borderBottomColor: Colors.divider,
|
|
paddingBottom: 8,
|
|
},
|
|
dayHeader: {
|
|
width: 36,
|
|
textAlign: 'center',
|
|
fontWeight: '600',
|
|
fontSize: 12,
|
|
color: Colors.secondary,
|
|
},
|
|
calendarGrid: {
|
|
flexDirection: 'row',
|
|
flexWrap: 'wrap',
|
|
},
|
|
day: {
|
|
width: '14.28%',
|
|
height: 44,
|
|
justifyContent: 'center',
|
|
alignItems: 'center',
|
|
marginVertical: 2,
|
|
position: 'relative',
|
|
},
|
|
emptyDay: {
|
|
backgroundColor: 'transparent',
|
|
},
|
|
todayDay: {
|
|
backgroundColor: Colors.primary,
|
|
borderRadius: 22,
|
|
},
|
|
todayText: {
|
|
color: '#fff',
|
|
fontWeight: '600',
|
|
},
|
|
eventDay: {
|
|
position: 'relative',
|
|
},
|
|
dayText: {
|
|
textAlign: 'center',
|
|
fontSize: 14,
|
|
color: Colors.text,
|
|
},
|
|
eventDot: {
|
|
position: 'absolute',
|
|
bottom: 6,
|
|
width: 6,
|
|
height: 6,
|
|
borderRadius: 3,
|
|
backgroundColor: Colors.accent,
|
|
},
|
|
});
|
|
|
|
export default CalendarView;
|