Commit 10a23b76 by tungnq

TODO: Đã refactor xong tháng

parent 03cfc11f
import React, {useState, useMemo, useRef} from 'react';
import {Animated, PanResponder, Dimensions} from 'react-native';
import React, {useState, useMemo, useRef, useEffect} from 'react';
import {Animated, PanResponder, Dimensions, DeviceEventEmitter} from 'react-native';
import {useFocusEffect} from '@react-navigation/native';
import ClassScheduleView from './view';
// ==================== HẰNG SỐ ====================
const {height: screenHeight} = Dimensions.get('window');
const BOTTOM_SHEET_HEIGHT = screenHeight * 0.6;
const ClassSchedule = ({events = [], onDateSelect, onEventPress}) => {
const [currentDate, setCurrentDate] = useState(new Date(2025, 7, 1));
// ==================== QUẢN LÝ STATE ====================
// B1: State ngày tháng và lịch
const [currentDate, setCurrentDate] = useState(new Date());
const [selectedDate, setSelectedDate] = useState(null);
// ==================== EFFECTS ====================
// Reset về ngày hiện tại khi chuyển màn hình
useFocusEffect(
React.useCallback(() => {
const today = new Date();
setCurrentDate(today);
setSelectedDate(null);
DeviceEventEmitter.emit('onDateChange', today);
// Cập nhật header drawer với tháng hiện tại
DeviceEventEmitter.emit('updateHeaderMonth', today.getMonth());
}, [])
);
// B2: State bottom sheet
const [showBottomSheet, setShowBottomSheet] = useState(false);
const bottomSheetTranslateY = useRef(new Animated.Value(BOTTOM_SHEET_HEIGHT)).current;
// B3: Tham chiếu animation
const bottomSheetTranslateY = useRef(
new Animated.Value(BOTTOM_SHEET_HEIGHT),
).current;
const formatDateToString = (date) => {
// ==================== HÀM TIỆN ÍCH ====================
// T1: Định dạng ngày thành chuỗi
const formatDateToString = date => {
const year = date.getFullYear();
const month = (date.getMonth() + 1).toString().padStart(2, '0');
const day = date.getDate().toString().padStart(2, '0');
return `${year}-${month}-${day}`;
};
// T2: Định dạng ngày để hiển thị
const formatDateToDisplay = date => {
const day = date.getDate().toString().padStart(2, '0');
const month = (date.getMonth() + 1).toString().padStart(2, '0');
const year = date.getFullYear();
return `Lịch ngày ${day}/${month}/${year}`;
};
// T3: Chuyển đổi chuỗi thành ngày
const parseLocalDate = dateString => {
const [year, month, day] = dateString.split('-').map(Number);
return new Date(year, month - 1, day);
};
// ==================== QUẢN LÝ DỮ LIỆU ====================
// D1: Tạo dữ liệu sự kiện mẫu
const createMockEvents = () => {
const today = new Date();
const todayStr = formatDateToString(today);
......@@ -147,32 +187,25 @@ const ClassSchedule = ({events = [], onDateSelect, onEventPress}) => {
];
};
// D2: Xử lý dữ liệu sự kiện
const mockEvents = createMockEvents();
const allEvents = [...events, ...mockEvents];
const panResponder = useRef(
PanResponder.create({
onMoveShouldSetPanResponder: (evt, gestureState) => {
return Math.abs(gestureState.dy) > 10;
},
onPanResponderMove: (evt, gestureState) => {
if (gestureState.dy > 0) {
bottomSheetTranslateY.setValue(gestureState.dy);
}
},
onPanResponderRelease: (evt, gestureState) => {
if (gestureState.dy > 100) {
hideBottomSheetModal();
} else {
Animated.spring(bottomSheetTranslateY, {
toValue: 0,
useNativeDriver: true,
}).start();
}
},
})
).current;
// D3: Hàm truy vấn sự kiện
const getEventsForDate = date => {
const dateStr = formatDateToString(date);
return allEvents.filter(event => event.date === dateStr);
};
const getSelectedEvents = () => {
if (!selectedDate) return [];
return allEvents
.filter(event => event.date === selectedDate)
.sort((a, b) => a.time.localeCompare(b.time));
};
// ==================== LOGIC LỊCH ====================
// L1: Tạo dữ liệu tháng
const getMonthData = useMemo(() => {
const year = currentDate.getFullYear();
const month = currentDate.getMonth();
......@@ -198,28 +231,12 @@ const ClassSchedule = ({events = [], onDateSelect, onEventPress}) => {
};
}, [currentDate]);
const getEventsForDate = (date) => {
const dateStr = formatDateToString(date);
return allEvents.filter(event => event.date === dateStr);
};
const parseLocalDate = (dateString) => {
const [year, month, day] = dateString.split('-').map(Number);
return new Date(year, month - 1, day);
};
const formatDateToDisplay = (date) => {
const day = date.getDate().toString().padStart(2, '0');
const month = (date.getMonth() + 1).toString().padStart(2, '0');
const year = date.getFullYear();
return `Lịch ngày ${day}/${month}/${year}`;
};
const isCurrentMonth = (date) => {
// L2: Hàm kiểm tra ngày tháng
const isCurrentMonth = date => {
return date.getMonth() === currentDate.getMonth();
};
const isToday = (date) => {
const isToday = date => {
const today = new Date();
return (
date.getDate() === today.getDate() &&
......@@ -228,20 +245,32 @@ const ClassSchedule = ({events = [], onDateSelect, onEventPress}) => {
);
};
const navigateMonth = (direction) => {
const newDate = new Date(currentDate);
if (direction === 'prev') {
newDate.setMonth(newDate.getMonth() - 1);
} else {
newDate.setMonth(newDate.getMonth() + 1);
// ==================== HỆ THỐNG ANIMATION ====================
// A1: Thiết lập PanResponder
const panResponder = useRef(
PanResponder.create({
onMoveShouldSetPanResponder: (evt, gestureState) => {
return Math.abs(gestureState.dy) > 10;
},
onPanResponderMove: (evt, gestureState) => {
if (gestureState.dy > 0) {
bottomSheetTranslateY.setValue(gestureState.dy);
}
setCurrentDate(newDate);
setSelectedDate(null);
if (showBottomSheet) {
},
onPanResponderRelease: (evt, gestureState) => {
if (gestureState.dy > 100) {
hideBottomSheetModal();
} else {
Animated.spring(bottomSheetTranslateY, {
toValue: 0,
useNativeDriver: true,
}).start();
}
};
},
}),
).current;
// A2: Hàm animation Bottom Sheet
const showBottomSheetModal = () => {
setShowBottomSheet(true);
Animated.spring(bottomSheetTranslateY, {
......@@ -262,7 +291,30 @@ const ClassSchedule = ({events = [], onDateSelect, onEventPress}) => {
});
};
const handleDatePress = (date) => {
// ==================== XỬ LÝ SỰ KIỆN ====================
// X1: Xử lý điều hướng
const navigateMonth = direction => {
const newDate = new Date(currentDate);
if (direction === 'prev') {
newDate.setMonth(newDate.getMonth() - 1);
} else {
newDate.setMonth(newDate.getMonth() + 1);
}
setCurrentDate(newDate);
setSelectedDate(null);
// Phát sự kiện để cập nhật header title
DeviceEventEmitter.emit('onDateChange', newDate.toISOString());
// Cập nhật header drawer với tháng mới
DeviceEventEmitter.emit('updateHeaderMonth', newDate.getMonth());
if (showBottomSheet) {
hideBottomSheetModal();
}
};
// X2: Xử lý chọn ngày
const handleDatePress = date => {
const dateStr = formatDateToString(date);
const dayEvents = getEventsForDate(date);
......@@ -274,7 +326,8 @@ const ClassSchedule = ({events = [], onDateSelect, onEventPress}) => {
}
};
const handleEventPress = (event) => {
// X3: Xử lý tương tác sự kiện
const handleEventPress = event => {
onEventPress?.(event);
};
......@@ -282,13 +335,6 @@ const ClassSchedule = ({events = [], onDateSelect, onEventPress}) => {
hideBottomSheetModal();
};
const getSelectedEvents = () => {
if (!selectedDate) return [];
return allEvents
.filter(event => event.date === selectedDate)
.sort((a, b) => a.time.localeCompare(b.time));
};
return (
<ClassScheduleView
currentDate={currentDate}
......@@ -300,6 +346,7 @@ const ClassSchedule = ({events = [], onDateSelect, onEventPress}) => {
getEventsForDate={getEventsForDate}
parseLocalDate={parseLocalDate}
formatDateToDisplay={formatDateToDisplay}
formatDateToString={formatDateToString}
isCurrentMonth={isCurrentMonth}
isToday={isToday}
navigateMonth={navigateMonth}
......
......@@ -3,15 +3,18 @@ import R from '../../assets/R';
const {width: screenWidth, height: screenHeight} = Dimensions.get('window');
const CELL_WIDTH = (screenWidth - 30) / 7;
const CELL_HEIGHT = (screenHeight - 140) / 6;
const CELL_HEIGHT = (screenHeight - 160) / 6;
const BOTTOM_SHEET_HEIGHT = screenHeight * 0.6;
const styles = StyleSheet.create({
// Container chính của màn hình
container: {
flex: 1,
backgroundColor: R.colors.white,
alignItems: 'center',
},
// Header tháng/năm với nút điều hướng
header: {
flexDirection: 'row',
alignItems: 'center',
......@@ -19,24 +22,28 @@ const styles = StyleSheet.create({
paddingVertical: 15,
},
header_title: {
fontSize: R.fontsize.fontsSize16,
fontFamily: R.fonts.InterMedium,
fontSize: R.fontsize.fontsSizeTitle,
fontFamily: R.fonts.fontMedium,
color: R.colors.black,
fontWeight: '600',
},
// Nút điều hướng tháng trước/sau
navButton: {
width: 30,
height: 30,
borderRadius: 20,
backgroundColor: R.colors.blue500,
backgroundColor: R.colors.blue,
alignItems: 'center',
justifyContent: 'center',
},
navButtonText: {
color: R.colors.white,
fontSize: R.fontsize.fontsSize16,
fontFamily: R.fonts.InterMedium,
fontSize: R.fontsize.fontsSizeTitle,
fontFamily: R.fonts.fontMedium,
height: '100%',
},
// Tiêu đề các ngày trong tuần
weekDaysContainer: {
flexDirection: 'row',
paddingBottom: 5,
......@@ -47,78 +54,94 @@ const styles = StyleSheet.create({
alignItems: 'center',
},
weekDayText: {
fontFamily: R.fonts.InterRegular,
fontSize: R.fontsize.fontsSize10,
fontWeight: '400',
fontFamily: R.fonts.fontMedium,
fontSize: R.fontsize.fontSizeLabel,
fontWeight: '600',
color: R.colors.black,
},
calendarGrid: {
},
// Lưới lịch
calendarGrid: {},
weekRow: {
flexDirection: 'row',
},
// Ô ngày trong lịch
dayCell: {
width: CELL_WIDTH,
minHeight: CELL_HEIGHT,
borderWidth: 1,
borderColor: R.colors.grey_200,
padding: 4,
borderColor: R.colors.grayBorderInputTextHeader,
padding: 6,
alignItems: 'center',
},
// Ô ngày được chọn
selectedDayCell: {
borderColor: R.colors.blue500,
borderColor: R.colors.blue,
borderWidth: 1,
},
// Text số ngày
dayText: {
fontSize: R.fontsize.fontsSize12,
fontWeight: '500',
fontFamily:R.fonts.InterMedium,
fontSize: R.fontsize.fontSizeLabel,
fontWeight: '400',
fontFamily: R.fonts.fontRegular,
color: R.colors.black,
marginBottom: 2,
},
// Text ngày không thuộc tháng hiện tại
dayTextInactive: {
color: R.colors.grey_100,
color: R.colors.black,
opacity: 1,
},
// Text ngày được chọn
selectedDayText: {
color: R.colors.black,
fontWeight: 'bold',
fontFamily: R.fonts.InterSemiBold,
fontWeight: '500',
fontFamily: R.fonts.fontSemiBold,
},
// Text ngày hôm nay
todayText: {
color: R.colors.white,
fontWeight: 'bold',
fontFamily: R.fonts.InterSemiBold,
backgroundColor: R.colors.blue500,
borderRadius: 10,
paddingHorizontal: 5,
fontFamily: R.fonts.fontSemiBold,
backgroundColor: R.colors.blue,
borderRadius: 15,
paddingHorizontal: 6,
paddingVertical: 4,
},
// Sự kiện trong ô ngày
eventsContainer: {
width: '100%',
flex: 1,
},
// Thanh sự kiện nhỏ trong ô ngày
eventBar: {
paddingVertical: 2,
paddingHorizontal: 5,
borderRadius: 5,
borderRadius: 10,
marginBottom: 2,
backgroundColor: R.colors.blue,
},
eventBarText: {
fontSize: R.fontsize.fontsSize10,
fontSize: R.fontsize.fontSizeLabel,
color: R.colors.white,
fontWeight: '500',
fontFamily: R.fonts.InterRegular
fontWeight: '400',
fontFamily: R.fonts.fontRegular,
},
// Text hiển thị số sự kiện còn lại
moreEventsText: {
fontSize: R.fontsize.fontsSize10,
color: R.colors.grey_100,
fontSize: R.fontsize.fontSizeLabel,
color: R.colors.gray,
fontWeight: '400',
fontFamily: R.fonts.fontRegular,
textAlign: 'center',
},
// Modal bottom sheet
modalBackdrop: {
flex: 1,
backgroundColor: R.colors.grey_200,
backgroundColor: R.colors.grayBorderInputTextHeader,
justifyContent: 'flex-end',
},
bottomSheet: {
......@@ -126,46 +149,63 @@ const styles = StyleSheet.create({
backgroundColor: R.colors.white,
borderTopLeftRadius: 20,
borderTopRightRadius: 20,
},
// Nội dung bottom sheet
bottomSheetContent: {
paddingHorizontal: 15,
height: BOTTOM_SHEET_HEIGHT,
},
// Thanh kéo
dragHandle: {
width: 40,
height: 4,
backgroundColor: R.colors.grey_200,
borderRadius: 2,
backgroundColor: R.colors.gray,
borderRadius: 5,
alignSelf: 'center',
marginTop: 10,
marginBottom: 15,
},
// Header của bottom sheet
bottomSheetHeader: {
flexDirection: 'row',
alignItems: 'center',
justifyContent: 'space-between',
marginBottom: 20,
paddingHorizontal: 15,
},
bottomSheetTitle: {
fontSize: R.fontsize.fontSizeHeader1,
fontFamily: R.fonts.InterMedium,
fontSize: R.fontsize.fontSizeContent,
fontFamily: R.fonts.fontMedium,
color: R.colors.black,
fontWeight: '600',
flex: 1,
},
// Nút đóng bottom sheet
closeButton: {
width: 30,
height: 30,
borderRadius: 15,
backgroundColor: R.colors.grey_200,
backgroundColor: R.colors.gray,
alignItems: 'center',
justifyContent: 'center',
},
closeButtonText: {
fontSize: R.fontsize.fontsSize12,
color: R.colors.grey_800,
fontFamily: R.fonts.InterRegular,
fontSize: R.fontsize.fontSizeContent,
color: R.colors.black,
fontFamily: R.fonts.fontRegular,
fontWeight: '400',
},
// Danh sách sự kiện trong bottom sheet
// ScrollView chứa danh sách sự kiện
eventsScrollView: {
paddingTop: 10,
},
// Container cho từng sự kiện
containerBottomSheet: {
flex: 1,
marginBottom: 10,
},
// Trạng thái không có sự kiện
noEventsContainer: {
flex: 1,
alignItems: 'center',
......@@ -173,19 +213,22 @@ const styles = StyleSheet.create({
paddingVertical: 40,
},
noEventsText: {
fontSize: R.fontsize.fontsSize12,
fontFamily: R.fonts.InterRegular,
color: R.colors.grey_800,
fontWeight: '400'
fontSize: R.fontsize.fontSizeContent,
fontFamily: R.fonts.fontRegular,
color: R.colors.gray,
fontWeight: '400',
},
// Card sự kiện chi tiết
eventCard: {
flexDirection: 'row',
backgroundColor: R.colors.white,
borderRadius: 12,
padding: 15,
marginBottom: 12,
marginBottom: 10,
marginHorizontal: 15,
borderLeftWidth: 4,
borderLeftColor: R.colors.blue500,
borderLeftColor: R.colors.blue,
shadowColor: R.colors.black,
shadowOffset: {
width: 0,
......@@ -195,6 +238,7 @@ const styles = StyleSheet.create({
shadowRadius: 1,
elevation: 2,
},
// Container thời gian sự kiện
eventTimeContainer: {
minWidth: 80,
alignItems: 'flex-start',
......@@ -202,25 +246,27 @@ const styles = StyleSheet.create({
marginRight: 15,
},
eventTime: {
fontSize: R.fontsize.fontsSize12,
fontFamily: R.fonts.InterMedium,
color: R.colors.blue500,
fontSize: R.fontsize.fontSizeContent,
fontFamily: R.fonts.fontMedium,
color: R.colors.blue,
fontWeight: '600',
},
// Container nội dung sự kiện
eventContent: {
flex: 1,
},
eventTitle: {
fontSize: R.fontsize.fontsSize12,
fontFamily: R.fonts.InterMedium,
fontSize: R.fontsize.fontSizeContent,
fontFamily: R.fonts.fontMedium,
color: R.colors.black,
fontWeight: '600',
marginBottom: 4,
},
eventDescription: {
fontSize: R.fontsize.fontsSize12,
fontFamily: R.fonts.InterRegular,
color: R.colors.grey_800,
fontSize: R.fontsize.fontSizeContent,
fontFamily: R.fonts.fontRegular,
color: R.colors.gray2,
fontWeight: '400',
},
});
......
......@@ -3,18 +3,19 @@ import {
Text,
View,
TouchableOpacity,
StyleSheet,
ScrollView,
Dimensions,
Modal,
Animated,
SafeAreaView,
LogBox,
} from 'react-native';
import R from '../../assets/R';
import { styles, CELL_WIDTH, BOTTOM_SHEET_HEIGHT } from './style';
import { useNavigation } from '@react-navigation/native';
import * as SCREENNAME from '../../routers/ScreenNames';
LogBox.ignoreAllLogs(
true
);
const ClassScheduleView = ({
currentDate,
selectedDate,
......@@ -150,7 +151,7 @@ const ClassScheduleView = ({
};
const renderBottomSheetContent = () => {
if (!selectedDate) return null;
if (!selectedDate || typeof selectedDate !== 'string') return null;
const selectedDateObj = parseLocalDate(selectedDate);
const selectedEvents = getSelectedEvents();
......
......@@ -20,7 +20,6 @@ import ItemGrid from './item';
import styles from './style';
import {useNavigation} from '@react-navigation/native';
import * as SCREENNAME from '../../routers/ScreenNames';
import TextSearchView from '../../components/Input/TextSearch/TextSearch';
const HomeView = props => {
const {
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment