Skip to content
Projects
Groups
Snippets
Help
This project
Loading...
Sign in / Register
Toggle navigation
A
AppUms_Student
Overview
Overview
Details
Activity
Cycle Analytics
Repository
Repository
Files
Commits
Branches
Tags
Contributors
Graph
Compare
Charts
Issues
0
Issues
0
List
Board
Labels
Milestones
Merge Requests
0
Merge Requests
0
CI / CD
CI / CD
Pipelines
Jobs
Schedules
Charts
Wiki
Wiki
Snippets
Snippets
Members
Members
Collapse sidebar
Close sidebar
Activity
Graph
Charts
Create a new issue
Jobs
Commits
Issue Boards
Open sidebar
tungnq
AppUms_Student
Commits
19c4050a
Commit
19c4050a
authored
Sep 26, 2025
by
tungnq
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
TODO: Tối ưu bổ sung thêm modal cho tuần khi nhấn tìm kiếm và chỉnh sửa giao diện của drawerView
parent
e10510ea
Expand all
Show whitespace changes
Inline
Side-by-side
Showing
8 changed files
with
654 additions
and
84 deletions
+654
-84
date.svg
src/assets/icon/date.svg
+2
-2
Functions.js
src/config/Functions.js
+2
-3
drawerView.js
src/routers/Drawer/drawerView.js
+179
-19
itemDrawer.js
src/routers/Drawer/itemDrawer.js
+61
-56
ScreenNames.js
src/routers/ScreenNames.js
+3
-0
index.js
src/screens/class_schedule/filter_week/index.js
+229
-4
style.js
src/screens/class_schedule/filter_week/style.js
+178
-0
view.js
src/screens/class_schedule/filter_week/view.js
+0
-0
No files found.
src/assets/icon/date.svg
View file @
19c4050a
<svg
width=
"
38"
height=
"38
"
viewBox=
"0 0 38 38"
fill=
"none"
xmlns=
"http://www.w3.org/2000/svg"
>
<path
d=
"M1.5 13.1667H36.5M9.27778 1.5V5.38889M28.7222 1.5V5.38889M7.33333 19H11.2222M17.0556 19H20.9444M26.7778 19H30.6667M7.33333 24.8333H11.2222M17.0556 24.8333H20.9444M26.7778 24.8333H30.6667M7.33333 30.6667H11.2222M17.0556 30.6667H20.9444M26.7778 30.6667H30.6667M7.72222 36.5H30.2778C32.4557 36.5 33.5448 36.5 34.3767 36.0761C35.1084 35.7034 35.7034 35.1084 36.0761 34.3767C36.5 33.5448 36.5 32.4557 36.5 30.2778V11.6111C36.5 9.43312 36.5 8.34413 36.0761 7.51226C35.7034 6.78051 35.1084 6.18559 34.3767 5.81276C33.5448 5.38889 32.4557 5.38889 30.2778 5.38889H7.72222C5.54425 5.38889 4.45524 5.38889 3.62337 5.81276C2.89162 6.18559 2.2967 6.78051 1.92387 7.51226C1.5 8.34413 1.5 9.43312 1.5 11.6111V30.2778C1.5 32.4557 1.5 33.5448 1.92387 34.3767C2.2967 35.1084 2.89162 35.7034 3.62337 36.0761C4.45524 36.5 5.54423 36.5 7.72222 36.5Z"
stroke=
"
none
"
stroke-width=
"2"
stroke-linecap=
"round"
/>
<svg
width=
"
24"
height=
"24
"
viewBox=
"0 0 38 38"
fill=
"none"
xmlns=
"http://www.w3.org/2000/svg"
>
<path
d=
"M1.5 13.1667H36.5M9.27778 1.5V5.38889M28.7222 1.5V5.38889M7.33333 19H11.2222M17.0556 19H20.9444M26.7778 19H30.6667M7.33333 24.8333H11.2222M17.0556 24.8333H20.9444M26.7778 24.8333H30.6667M7.33333 30.6667H11.2222M17.0556 30.6667H20.9444M26.7778 30.6667H30.6667M7.72222 36.5H30.2778C32.4557 36.5 33.5448 36.5 34.3767 36.0761C35.1084 35.7034 35.7034 35.1084 36.0761 34.3767C36.5 33.5448 36.5 32.4557 36.5 30.2778V11.6111C36.5 9.43312 36.5 8.34413 36.0761 7.51226C35.7034 6.78051 35.1084 6.18559 34.3767 5.81276C33.5448 5.38889 32.4557 5.38889 30.2778 5.38889H7.72222C5.54425 5.38889 4.45524 5.38889 3.62337 5.81276C2.89162 6.18559 2.2967 6.78051 1.92387 7.51226C1.5 8.34413 1.5 9.43312 1.5 11.6111V30.2778C1.5 32.4557 1.5 33.5448 1.92387 34.3767C2.2967 35.1084 2.89162 35.7034 3.62337 36.0761C4.45524 36.5 5.54423 36.5 7.72222 36.5Z"
stroke=
"
black
"
stroke-width=
"2"
stroke-linecap=
"round"
/>
</svg>
src/config/Functions.js
View file @
19c4050a
import
React
,
{
useRef
,
useEffect
}
from
'react'
;
import
React
,
{
useRef
,
useEffect
}
from
'react'
;
import
{
Dimensions
,
Platform
,
...
...
@@ -738,4 +738,4 @@ export const monthNames = [
'Tháng 10'
,
'Tháng 11'
,
'Tháng 12'
,
]
\ No newline at end of file
];
src/routers/Drawer/drawerView.js
View file @
19c4050a
...
...
@@ -6,33 +6,39 @@ import {
Text
,
TouchableOpacity
,
View
,
Modal
,
TextInput
,
Alert
,
}
from
"react-native"
;
import
{
createDrawerNavigator
}
from
"@react-navigation/drawer"
;
import
{
createDrawerNavigator
,
}
from
"@react-navigation/drawer"
;
import
{
connect
}
from
"react-redux"
;
import
ClassSchedule
from
"../../screens/class_schedule"
;
import
FilterDateView
from
'../../screens/class_schedule/filter_date'
;
import
*
as
ScreenName
from
"../ScreenNames"
;
import
CustomDrawerContent
from
"./itemDrawer"
;
import
R
from
"../../assets/R"
;
import
FilterWeek
View
from
"../../screens/class_schedule/filter_week/view
"
;
import
FilterWeek
from
"../../screens/class_schedule/filter_week
"
;
import
Filter3Date
from
"../../screens/class_schedule/filter_3_date"
;
const
Drawer
=
createDrawerNavigator
();
const
ArrowLeft
=
R
.
images
.
icBack
;
const
IconMenu
=
R
.
images
.
icMenu
;
const
ArrowLeft
=
R
.
images
.
icBack
;
const
IconSearch
=
R
.
images
.
icSearch
;
const
DrawerNavigatorView
=
(
props
)
=>
{
const
[
reload
,
setReload
]
=
useState
(
false
);
const
[
currentDate
,
setCurrentDate
]
=
useState
(
new
Date
());
const
[
searchModalVisible
,
setSearchModalVisible
]
=
useState
(
false
);
const
[
searchText
,
setSearchText
]
=
useState
(
''
);
const
HeaderBackButton
=
({
onPress
,
canGoBack
})
=>
(
<
TouchableOpacity
style
=
{
styles
.
headerBackButton
}
onPress
=
{
onPress
}
activeOpacity
=
{
0.7
}
>
<
ArrowLeft
fill
=
{
"white"
}
/
>
<
ArrowLeft
fill
=
{
'white'
}
/
>
<
/TouchableOpacity
>
);
...
...
@@ -51,11 +57,21 @@ const DrawerNavigatorView = (props) => {
}
);
const
updateHeaderMonthListener
=
DeviceEventEmitter
.
addListener
(
"updateHeaderMonth"
,
(
monthIndex
)
=>
{
const
newDate
=
new
Date
(
currentDate
);
newDate
.
setMonth
(
monthIndex
);
setCurrentDate
(
newDate
);
}
);
return
()
=>
{
setLanguageListener
.
remove
();
dateChangeListener
.
remove
();
updateHeaderMonthListener
.
remove
();
};
},
[]);
},
[
currentDate
]);
const
MenuButton
=
({
onPress
})
=>
(
<
TouchableOpacity
style
=
{
styles
.
menuButton
}
onPress
=
{
onPress
}
>
...
...
@@ -77,6 +93,7 @@ const DrawerNavigatorView = (props) => {
return
''
;
};
...
...
@@ -89,10 +106,37 @@ const DrawerNavigatorView = (props) => {
);
};
const
handleSearchPress
=
()
=>
{
setSearchModalVisible
(
true
);
};
const
handleSearchSubmit
=
()
=>
{
if
(
searchText
.
trim
())
{
// Emit event để các screen khác có thể lắng nghe và xử lý tìm kiếm
DeviceEventEmitter
.
emit
(
'onScheduleSearch'
,
{
searchText
:
searchText
.
trim
(),
currentScreen
:
'schedule'
});
setSearchModalVisible
(
false
);
setSearchText
(
''
);
Alert
.
alert
(
'Tìm kiếm'
,
`Đang tìm kiếm: "
${
searchText
.
trim
()}
"`
);
}
else
{
Alert
.
alert
(
'Thông báo'
,
'Vui lòng nhập từ khóa tìm kiếm'
);
}
};
const
handleSearchCancel
=
()
=>
{
setSearchModalVisible
(
false
);
setSearchText
(
''
);
};
return
(
<>
<
Drawer
.
Navigator
drawerContent
=
{(
drawerProps
)
=>
<
CustomDrawerContent
{...
drawerProps
}
/>
}
initialRouteName
=
{
ScreenName
.
CLASSSCHEDULE
}
initialRouteName
=
{
ScreenName
.
FILTERMONTH
}
screenOptions
=
{({
navigation
,
route
})
=>
({
headerLeft
:
()
=>
(
<
View
style
=
{
styles
.
headerLeftContainer
}
>
...
...
@@ -115,12 +159,17 @@ const DrawerNavigatorView = (props) => {
),
headerTitle
:
()
=>
(
<
View
style
=
{
styles
.
headerTitleContainer
}
>
<
HeaderTitle
navigation
=
{
navigation
}
route
=
{
route
}
/
>
<
/View
>
),
headerRight
:
()
=>
(
<
View
style
=
{
styles
.
headerRightContainer
}
>
<
TouchableOpacity
style
=
{
styles
.
searchButton
}
>
<
TouchableOpacity
style
=
{
styles
.
searchButton
}
onPress
=
{
handleSearchPress
}
>
<
IconSearch
stroke
=
{
R
.
colors
.
white
}
width
=
{
24
}
height
=
{
24
}
/
>
<
/TouchableOpacity
>
<
View
style
=
{
styles
.
avatarButton
}
/
>
...
...
@@ -128,12 +177,12 @@ const DrawerNavigatorView = (props) => {
),
headerStyle
:
{
backgroundColor
:
R
.
colors
.
blue500
,
backgroundColor
:
R
.
colors
.
main
,
},
})}
>
<
Drawer
.
Screen
name
=
{
ScreenName
.
CLASSSCHEDULE
}
name
=
{
ScreenName
.
FILTERMONTH
}
component
=
{
ClassSchedule
}
options
=
{{
drawerLabel
:
()
=>
null
,
...
...
@@ -159,12 +208,54 @@ const DrawerNavigatorView = (props) => {
/
>
<
Drawer
.
Screen
name
=
{
ScreenName
.
FILTERWEEK
}
component
=
{
FilterWeekView
}
component
=
{
FilterWeek
}
options
=
{{
drawerItemStyle
:
{
height
:
0
},
}}
/
>
<
/Drawer.Navigator
>
{
/* Search Modal */
}
<
Modal
visible
=
{
searchModalVisible
}
transparent
=
{
true
}
animationType
=
"fade"
onRequestClose
=
{
handleSearchCancel
}
>
<
View
style
=
{
styles
.
modalContainer
}
>
<
View
style
=
{
styles
.
modalContent
}
>
<
Text
style
=
{
styles
.
modalTitle
}
>
T
ì
m
ki
ế
m
l
ị
ch
<
/Text
>
<
TextInput
style
=
{
styles
.
searchInput
}
value
=
{
searchText
}
onChangeText
=
{
setSearchText
}
placeholder
=
"Nhập từ khóa tìm kiếm..."
placeholderTextColor
=
{
R
.
colors
.
gray150
}
autoFocus
=
{
true
}
returnKeyType
=
"search"
onSubmitEditing
=
{
handleSearchSubmit
}
/
>
<
View
style
=
{
styles
.
modalButtonContainer
}
>
<
TouchableOpacity
style
=
{
styles
.
cancelButton
}
onPress
=
{
handleSearchCancel
}
>
<
Text
style
=
{
styles
.
cancelButtonText
}
>
H
ủ
y
<
/Text
>
<
/TouchableOpacity
>
<
TouchableOpacity
style
=
{
styles
.
searchButtonModal
}
onPress
=
{
handleSearchSubmit
}
>
<
Text
style
=
{
styles
.
searchButtonText
}
>
T
ì
m
ki
ế
m
<
/Text
>
<
/TouchableOpacity
>
<
/View
>
<
/View
>
<
/View
>
<
/Modal
>
<
/
>
);
};
...
...
@@ -174,13 +265,11 @@ const mapStateToProps = (state) => ({
const
styles
=
StyleSheet
.
create
({
headerBackButton
:
{
padding
:
10
,
marginLeft
:
10
,
borderRadius
:
8
,
marginLeft
:
15
},
menuButton
:
{
padding
:
10
,
marginLeft
:
5
,
marginLeft
:
15
,
},
headerLeftContainer
:
{
flexDirection
:
"row"
,
...
...
@@ -196,19 +285,90 @@ const styles = StyleSheet.create({
marginRight
:
10
,
},
avatarButton
:
{
width
:
30
,
height
:
30
,
width
:
25
,
height
:
25
,
borderRadius
:
30
,
backgroundColor
:
R
.
colors
.
white
,
},
headerTitleButton
:
{
alignItems
:
'center'
,
justifyContent
:
'center'
,
flexDirection
:
'row'
,
},
headerTitleText
:
{
fontSize
:
R
.
fontsize
.
fontsSize1
6
,
fontSize
:
R
.
fontsize
.
fontsSize1
4
,
color
:
R
.
colors
.
white
,
fontFamily
:
R
.
fonts
.
InterMedium
,
fontWeight
:
'600'
,
},
headerTitleContainer
:
{
flexDirection
:
'row'
,
},
icon
:
{
width
:
20
,
height
:
20
,
},
iconSearch
:
{
width
:
20
,
height
:
20
,
},
modalContainer
:
{
flex
:
1
,
justifyContent
:
'center'
,
alignItems
:
'center'
,
backgroundColor
:
'rgba(0, 0, 0, 0.5)'
,
},
modalContent
:
{
backgroundColor
:
R
.
colors
.
white
,
borderRadius
:
10
,
padding
:
20
,
width
:
'80%'
,
},
modalTitle
:
{
fontSize
:
R
.
fontsize
.
fontsSize12
,
color
:
R
.
colors
.
black
,
fontFamily
:
R
.
fonts
.
InterMedium
,
fontWeight
:
'600'
,
marginBottom
:
10
,
},
searchInput
:
{
height
:
40
,
borderColor
:
R
.
colors
.
gray150
,
borderWidth
:
1
,
paddingHorizontal
:
10
,
paddingVertical
:
5
,
marginBottom
:
10
,
},
modalButtonContainer
:
{
flexDirection
:
'row'
,
justifyContent
:
'flex-end'
},
cancelButton
:
{
backgroundColor
:
R
.
colors
.
orange
,
borderRadius
:
5
,
marginRight
:
10
,
height
:
30
,
width
:
80
,
alignItems
:
'center'
,
justifyContent
:
'center'
,
},
cancelButtonText
:
{
fontSize
:
R
.
fontsize
.
fontsSize12
,
color
:
R
.
colors
.
white
,
fontFamily
:
R
.
fonts
.
InterMedium
,
},
searchButtonModal
:
{
backgroundColor
:
R
.
colors
.
main
,
borderRadius
:
5
,
height
:
30
,
width
:
80
,
alignItems
:
'center'
,
justifyContent
:
'center'
,
},
searchButtonText
:
{
fontSize
:
R
.
fontsize
.
fontsSize12
,
color
:
R
.
colors
.
white
,
fontFamily
:
R
.
fonts
.
InterMedium
},
});
...
...
src/routers/Drawer/itemDrawer.js
View file @
19c4050a
import
React
,
{
useState
,
useMemo
,
useCallback
}
from
"react"
;
import
React
,
{
useState
,
useMemo
,
useCallback
}
from
'react'
;
import
{
DeviceEventEmitter
,
View
,
...
...
@@ -6,59 +6,59 @@ import {
Image
,
TouchableOpacity
,
StyleSheet
,
}
from
"react-native"
;
}
from
'react-native'
;
import
{
DrawerContentScrollView
,
useDrawerProgress
,
}
from
"@react-navigation/drawer"
;
import
R
from
"../../assets/R"
;
import
Checkbox
from
"../../components/CheckBox"
;
import
*
as
ScreenName
from
"../ScreenNames"
;
}
from
'@react-navigation/drawer'
;
import
R
from
'../../assets/R'
;
import
*
as
ScreenName
from
'../ScreenNames'
;
import
CheckBox
from
'../../components/CheckBox'
;
const
IcDate
=
R
.
images
.
icDate
Drawer
;
const
IcDate
=
R
.
images
.
icDate
;
const
Ic3Date
=
R
.
images
.
ic3Date
;
const
IcWeek
=
R
.
images
.
icWeek
;
const
IcMonth
=
R
.
images
.
icMonth
;
const
CustomDrawerContent
=
(
props
)
=>
{
const
CustomDrawerContent
=
props
=>
{
const
progress
=
useDrawerProgress
();
const
{
navigation
,
state
}
=
props
;
const
{
navigation
,
state
}
=
props
;
const
checkboxConfigs
=
useMemo
(
()
=>
[
{
key
:
"departmentCalendar"
,
label
:
"Lịch phòng ban"
},
{
key
:
"personalCalendar"
,
label
:
"Lịch cá nhân"
},
{
key
:
"teachingCalendar"
,
label
:
"Lịch giảng dạy"
},
{
key
:
"examCalendar"
,
label
:
"Lịch coi thi"
},
{
key
:
"examRoomCalendar"
,
label
:
"Lịch sử dụng phòng thi"
},
{
key
:
'departmentCalendar'
,
label
:
'Lịch phòng ban'
},
{
key
:
'personalCalendar'
,
label
:
'Lịch cá nhân'
},
{
key
:
'teachingCalendar'
,
label
:
'Lịch giảng dạy'
},
{
key
:
'examCalendar'
,
label
:
'Lịch coi thi'
},
{
key
:
'examRoomCalendar'
,
label
:
'Lịch sử dụng phòng thi'
},
],
[]
[]
,
);
const
filterConfigs
=
useMemo
(
()
=>
[
{
label
:
"Theo ngày"
,
label
:
'Theo ngày'
,
screenName
:
ScreenName
.
FILTERDATE
,
icon
:
<
IcDate
stroke
=
{
'black'
}
/>
icon
:
<
IcDate
stroke
=
{
'black'
}
/>
,
},
{
label
:
"3 ngày"
,
label
:
'3 ngày'
,
screenName
:
ScreenName
.
FILTER3DATE
,
icon
:
<
Ic3Date
/>
icon
:
<
Ic3Date
/>
,
},
{
label
:
"Tuần"
,
label
:
'Tuần'
,
screenName
:
ScreenName
.
FILTERWEEK
,
icon
:
<
IcWeek
/>
icon
:
<
IcWeek
/>
,
},
{
label
:
"Tháng"
,
screenName
:
ScreenName
.
CLASSSCHEDULE
,
icon
:
<
IcMonth
/>
label
:
'Tháng'
,
screenName
:
ScreenName
.
FILTERMONTH
,
icon
:
<
IcMonth
/>
,
},
],
[]
[]
,
);
const
[
checkboxStates
,
setCheckboxStates
]
=
useState
({
...
...
@@ -72,70 +72,70 @@ const CustomDrawerContent = (props) => {
const
currentRouteName
=
state
.
routes
[
state
.
index
]?.
name
;
const
handleCheckboxChange
=
useCallback
((
key
,
newValue
)
=>
{
setCheckboxStates
(
(
prev
)
=>
({
setCheckboxStates
(
prev
=>
({
...
prev
,
[
key
]:
newValue
,
}));
DeviceEventEmitter
.
emit
(
"onCheckboxChange"
,
{
key
,
value
:
newValue
});
DeviceEventEmitter
.
emit
(
'onCheckboxChange'
,
{
key
,
value
:
newValue
});
},
[]);
const
handleScreenNavigation
=
useCallback
((
screenName
)
=>
{
const
handleScreenNavigation
=
useCallback
(
screenName
=>
{
navigation
.
navigate
(
screenName
);
navigation
.
closeDrawer
();
},
[
navigation
]);
},
[
navigation
],
);
const
renderCheckbox
=
useCallback
(
(
config
)
=>
(
<
Check
b
ox
config
=>
(
<
Check
B
ox
key
=
{
config
.
key
}
value
=
{
checkboxStates
[
config
.
key
]}
onValueChange
=
{
(
newValue
)
=>
handleCheckboxChange
(
config
.
key
,
newValue
)}
onValueChange
=
{
newValue
=>
handleCheckboxChange
(
config
.
key
,
newValue
)}
label
=
{
config
.
label
}
labelSpacing
=
{
30
}
size
=
{
2
5
}
labelSize
=
{
R
.
fontsize
.
fontsSize1
6
}
size
=
{
2
0
}
labelSize
=
{
R
.
fontsize
.
fontsSize1
2
}
checkedColor
=
{
R
.
colors
.
black
}
marginBottom
=
{
20
}
labelFontFamily
=
{
R
.
fonts
.
InterRegular
}
labelWeight
=
{
400
}
/
>
),
[
checkboxStates
,
handleCheckboxChange
]
[
checkboxStates
,
handleCheckboxChange
]
,
);
const
renderFilterItem
=
useCallback
(
(
item
)
=>
{
item
=>
{
const
isSelected
=
currentRouteName
===
item
.
screenName
;
return
(
<
TouchableOpacity
key
=
{
item
.
screenName
}
style
=
{[
styles
.
filterItem
,
isSelected
&&
styles
.
filterItemSelected
,
]}
style
=
{[
styles
.
filterItem
,
isSelected
&&
styles
.
filterItemSelected
]}
onPress
=
{()
=>
handleScreenNavigation
(
item
.
screenName
)}
activeOpacity
=
{
0.7
}
>
activeOpacity
=
{
0.7
}
>
{
item
.
icon
}
<
Text
style
=
{[
<
Text
style
=
{[
styles
.
filterLabel
,
isSelected
&&
styles
.
filterLabelSelected
isSelected
&&
styles
.
filterLabelSelected
,
]}
>
{
item
.
label
}
<
/Text
>
<
/TouchableOpacity
>
);
},
[
currentRouteName
,
handleScreenNavigation
]
[
currentRouteName
,
handleScreenNavigation
]
,
);
return
(
<
DrawerContentScrollView
{...
props
}
contentContainerStyle
=
{
styles
.
scrollContainer
}
>
contentContainerStyle
=
{
styles
.
scrollContainer
}
>
<
View
style
=
{
styles
.
logoContainer
}
>
<
Image
source
=
{
R
.
images
.
igLogo
}
...
...
@@ -164,38 +164,43 @@ const styles = StyleSheet.create({
paddingBottom
:
20
,
},
logoContainer
:
{
alignItems
:
"center"
,
alignItems
:
'center'
,
marginVertical
:
20
,
},
logo
:
{
width
:
49
,
width
:
1
49
,
height
:
24
,
},
filterSection
:
{
paddingHorizontal
:
0
,
},
filterItem
:
{
flexDirection
:
"row"
,
alignItems
:
"center"
,
flexDirection
:
'row'
,
alignItems
:
'center'
,
paddingVertical
:
10
,
paddingHorizontal
:
15
,
marginVertical
:
4
,
},
filterIcon
:
{
width
:
20
,
height
:
20
,
},
filterItemSelected
:
{
backgroundColor
:
R
.
colors
.
blue100
,
backgroundColor
:
R
.
colors
.
main
,
borderRadius
:
50
,
marginHorizontal
:
15
,
},
filterLabel
:
{
marginLeft
:
30
,
fontSize
:
R
.
fontsize
.
fontsSize1
6
,
fontSize
:
R
.
fontsize
.
fontsSize1
2
,
color
:
R
.
colors
.
black
,
flex
:
1
,
fontFamily
:
R
.
fonts
.
InterMedium
,
fontWeight
:
'
4
00'
,
fontWeight
:
'
6
00'
,
},
filterLabelSelected
:
{
color
:
R
.
colors
.
blue
,
color
:
R
.
colors
.
white
,
fontWeight
:
'500'
,
fontFamily
:
R
.
fonts
.
InterSemiBold
,
},
divider
:
{
...
...
@@ -209,7 +214,7 @@ const styles = StyleSheet.create({
marginTop
:
24
,
},
sectionTitle
:
{
fontWeight
:
"400"
,
fontWeight
:
'600'
,
fontFamily
:
R
.
fonts
.
InterMedium
,
marginBottom
:
10
,
fontSize
:
R
.
fontsize
.
fontsSize16
,
...
...
src/routers/ScreenNames.js
View file @
19c4050a
...
...
@@ -22,4 +22,6 @@ export const CLASSSCHEDULE = 'CLASSSCHEDULE';
export
const
FILTERDATE
=
'FILTERDATE'
;
export
const
FILTERWEEK
=
'FILTERWEEK'
;
export
const
FILTER3DATE
=
'FILTER3DATE'
;
export
const
FILTERMONTH
=
'FILTERMONTH'
;
export
const
DETAILCLASSSCHEDULE
=
'DETAILCLASSSCHEDULE'
;
export
const
DETAILSCHEDULE
=
'DETAILSCHEDULE'
;
\ No newline at end of file
src/screens/class_schedule/filter_week/index.js
View file @
19c4050a
import
React
from
'react'
;
import
{
Text
,
View
,
StyleSheet
}
from
'react-native'
;
import
React
,
{
useState
,
useRef
,
useEffect
}
from
'react'
;
import
{
Dimensions
,
PanResponder
}
from
'react-native'
;
import
{
useNavigation
,
useFocusEffect
}
from
'@react-navigation/native'
;
import
{
DeviceEventEmitter
}
from
'react-native'
;
import
FilterWeekView
from
'./view'
;
const
FilterWeek
=
(
props
)
=>
{
// ==================== HẰNG SỐ ====================
const
{
width
:
screenWidth
,
height
:
screenHeight
}
=
Dimensions
.
get
(
'window'
);
const
HOUR_HEIGHT
=
80
;
const
DAY_COLUMN_WIDTH
=
(
screenWidth
-
70
)
/
7
;
const
FilterWeek
=
({
navigation
})
=>
{
const
navigationHook
=
useNavigation
();
// ==================== QUẢN LÝ STATE ====================
// B1: State ngày tháng và lịch
const
[
currentDate
,
setCurrentDate
]
=
useState
(
new
Date
());
const
[
selectedDate
,
setSelectedDate
]
=
useState
(
new
Date
());
// B2: State month picker
const
[
showMonthPicker
,
setShowMonthPicker
]
=
useState
(
false
);
// B3: Tham chiếu scroll
const
scrollViewRef
=
useRef
(
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
(
today
);
DeviceEventEmitter
.
emit
(
'onDateChange'
,
today
);
// Cập nhật header drawer với tháng hiện tại
DeviceEventEmitter
.
emit
(
'updateHeaderMonth'
,
today
.
getMonth
());
},
[])
);
useEffect
(()
=>
{
DeviceEventEmitter
.
emit
(
'onDateChange'
,
selectedDate
);
},
[
selectedDate
]);
// ==================== 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: Kiểm tra ngày hôm nay
const
isToday
=
someDate
=>
{
const
today
=
new
Date
();
return
(
someDate
.
getDate
()
===
today
.
getDate
()
&&
someDate
.
getMonth
()
===
today
.
getMonth
()
&&
someDate
.
getFullYear
()
===
today
.
getFullYear
()
);
};
// T3: Lấy tên ngày trong tuần
const
getDayName
=
date
=>
{
const
days
=
[
'CN'
,
'T2'
,
'T3'
,
'T4'
,
'T5'
,
'T6'
,
'T7'
];
return
days
[
date
.
getDay
()];
};
// T4: Lấy tên tháng
const
getMonthName
=
monthIndex
=>
{
const
months
=
[
'Tháng 1'
,
'Tháng 2'
,
'Tháng 3'
,
'Tháng 4'
,
'Tháng 5'
,
'Tháng 6'
,
'Tháng 7'
,
'Tháng 8'
,
'Tháng 9'
,
'Tháng 10'
,
'Tháng 11'
,
'Tháng 12'
,
];
return
months
[
monthIndex
];
};
// T5: Lấy các ngày trong tuần
const
getWeekDates
=
date
=>
{
const
startOfWeek
=
new
Date
(
date
);
startOfWeek
.
setDate
(
date
.
getDate
()
-
date
.
getDay
());
const
weekDates
=
[];
for
(
let
i
=
0
;
i
<
7
;
i
++
)
{
const
weekDate
=
new
Date
(
startOfWeek
);
weekDate
.
setDate
(
startOfWeek
.
getDate
()
+
i
);
weekDates
.
push
(
weekDate
);
}
return
weekDates
;
};
// T6: Tính toán vị trí sự kiện
const
calculateEventPosition
=
(
startTime
,
endTime
)
=>
{
const
[
startHour
,
startMinute
]
=
startTime
.
split
(
':'
).
map
(
Number
);
const
[
endHour
,
endMinute
]
=
endTime
.
split
(
':'
).
map
(
Number
);
const
startTotalMinutes
=
startHour
*
60
+
startMinute
;
const
endTotalMinutes
=
endHour
*
60
+
endMinute
;
const
durationMinutes
=
endTotalMinutes
-
startTotalMinutes
;
const
topPosition
=
(
startTotalMinutes
/
60
)
*
HOUR_HEIGHT
;
const
height
=
(
durationMinutes
/
60
)
*
HOUR_HEIGHT
;
return
{
topPosition
,
height
};
};
// ==================== QUẢN LÝ DỮ LIỆU ====================
// D1: Tạo dữ liệu sự kiện mẫu
const
createMockEvents
=
()
=>
{
return
[
{
id
:
'1'
,
title
:
'Lịch vào trực lớp TTCĐT 445.T1'
,
subtitle
:
'CS Địa lý 4D'
,
time
:
'07:00'
,
endTime
:
'09:00'
,
date
:
'2025-09-26'
,
type
:
'class'
,
},
{
id
:
'2'
,
title
:
'Meeting team development'
,
subtitle
:
'Phòng họp A1'
,
time
:
'10:00'
,
endTime
:
'11:30'
,
date
:
'2025-09-26'
,
type
:
'meeting'
,
},
{
id
:
'3'
,
title
:
'Training React Native'
,
subtitle
:
'Online Zoom'
,
time
:
'14:00'
,
endTime
:
'16:00'
,
date
:
'2025-09-26'
,
type
:
'training'
,
},
];
};
// D2: Xử lý dữ liệu sự kiện
const
mockEvents
=
createMockEvents
();
// D3: Hàm truy vấn sự kiện
const
getEventsForDate
=
date
=>
{
const
dateStr
=
formatDateToString
(
date
);
return
mockEvents
.
filter
(
event
=>
event
.
date
===
dateStr
);
};
// ==================== HỆ THỐNG ANIMATION ====================
// A1: Thiết lập PanResponder
const
panResponder
=
PanResponder
.
create
({
onMoveShouldSetPanResponder
:
(
evt
,
gestureState
)
=>
{
return
Math
.
abs
(
gestureState
.
dx
)
>
30
&&
Math
.
abs
(
gestureState
.
dy
)
<
100
;
},
onPanResponderMove
:
(
evt
,
gestureState
)
=>
{},
onPanResponderRelease
:
(
evt
,
gestureState
)
=>
{
if
(
gestureState
.
dx
>
50
)
{
swipeToPrevWeek
();
}
else
if
(
gestureState
.
dx
<
-
50
)
{
swipeToNextWeek
();
}
},
});
// ==================== XỬ LÝ SỰ KIỆN ====================
// X1: Xử lý chọn tháng
const
handleMonthSelect
=
monthIndex
=>
{
const
newDate
=
new
Date
(
currentDate
);
newDate
.
setMonth
(
monthIndex
);
setCurrentDate
(
newDate
);
setSelectedDate
(
newDate
);
setShowMonthPicker
(
false
);
DeviceEventEmitter
.
emit
(
'onDateChange'
,
newDate
);
// Cập nhật header drawer với tháng mới
DeviceEventEmitter
.
emit
(
'updateHeaderMonth'
,
monthIndex
);
};
// X2: Xử lý chuyển tuần
const
swipeToNextWeek
=
()
=>
{
const
nextWeek
=
new
Date
(
currentDate
);
nextWeek
.
setDate
(
currentDate
.
getDate
()
+
7
);
setCurrentDate
(
nextWeek
);
setSelectedDate
(
nextWeek
);
DeviceEventEmitter
.
emit
(
'onDateChange'
,
nextWeek
);
// Cập nhật header drawer nếu tháng thay đổi
DeviceEventEmitter
.
emit
(
'updateHeaderMonth'
,
nextWeek
.
getMonth
());
};
const
swipeToPrevWeek
=
()
=>
{
const
prevWeek
=
new
Date
(
currentDate
);
prevWeek
.
setDate
(
currentDate
.
getDate
()
-
7
);
setCurrentDate
(
prevWeek
);
setSelectedDate
(
prevWeek
);
DeviceEventEmitter
.
emit
(
'onDateChange'
,
prevWeek
);
// Cập nhật header drawer nếu tháng thay đổi
DeviceEventEmitter
.
emit
(
'updateHeaderMonth'
,
prevWeek
.
getMonth
());
};
// X3: Xử lý toggle month picker
const
toggleMonthPicker
=
()
=>
{
setShowMonthPicker
(
!
showMonthPicker
);
};
return
(
<
FilterWeekView
/>
<
FilterWeekView
navigation
=
{
navigation
}
currentDate
=
{
currentDate
}
selectedDate
=
{
selectedDate
}
showMonthPicker
=
{
showMonthPicker
}
scrollViewRef
=
{
scrollViewRef
}
panResponder
=
{
panResponder
}
isToday
=
{
isToday
}
getDayName
=
{
getDayName
}
getMonthName
=
{
getMonthName
}
getWeekDates
=
{
getWeekDates
}
getEventsForDate
=
{
getEventsForDate
}
calculateEventPosition
=
{
calculateEventPosition
}
handleMonthSelect
=
{
handleMonthSelect
}
toggleMonthPicker
=
{
toggleMonthPicker
}
HOUR_HEIGHT
=
{
HOUR_HEIGHT
}
DAY_COLUMN_WIDTH
=
{
DAY_COLUMN_WIDTH
}
/
>
);
};
...
...
src/screens/class_schedule/filter_week/style.js
0 → 100644
View file @
19c4050a
import
{
StyleSheet
,
Dimensions
}
from
'react-native'
;
import
R
from
'../../../assets/R'
;
const
{
width
}
=
Dimensions
.
get
(
'window'
);
const
DAY_COLUMN_WIDTH
=
(
width
-
70
)
/
7
;
const
styles
=
StyleSheet
.
create
({
// ==================== CONTAINER CHÍNH ====================
// Tương ứng với View chính trong FilterWeekView (dòng 166)
container
:
{
flex
:
1
,
backgroundColor
:
R
.
colors
.
white
,
},
// ==================== MONTH PICKER ====================
// Tương ứng với renderMonthPicker() (dòng 19-49)
monthPickerContainer
:
{
backgroundColor
:
R
.
colors
.
white
,
borderBottomWidth
:
1
,
borderBottomColor
:
R
.
colors
.
gray
,
paddingVertical
:
10
,
},
monthPickerContent
:
{
paddingHorizontal
:
15
,
},
monthItem
:
{
paddingHorizontal
:
20
,
paddingVertical
:
8
,
marginRight
:
10
,
borderRadius
:
20
,
backgroundColor
:
R
.
colors
.
gray220
,
},
monthItemSelected
:
{
backgroundColor
:
R
.
colors
.
main
,
},
monthItemText
:
{
fontSize
:
R
.
fontsize
.
fontSizeContent
,
fontFamily
:
R
.
fonts
.
InterRegular
,
color
:
R
.
colors
.
black
,
},
monthItemTextSelected
:
{
color
:
R
.
colors
.
white
,
fontFamily
:
R
.
fonts
.
fontMedium
,
},
// ==================== WEEK HEADER ====================
// Tương ứng với renderWeekHeader() (dòng 51-87)
weekHeaderContainer
:
{
flexDirection
:
'row'
,
backgroundColor
:
R
.
colors
.
gray220
,
borderBottomWidth
:
1
,
borderBottomColor
:
R
.
colors
.
gray220
,
},
timeColumnHeader
:
{
width
:
70
,
},
dayHeaderCell
:
{
width
:
DAY_COLUMN_WIDTH
,
alignItems
:
'center'
,
justifyContent
:
'center'
,
borderRightWidth
:
1
,
borderRightColor
:
R
.
colors
.
gray220
,
},
dayHeaderText
:
{
fontSize
:
R
.
fontsize
.
fontsSize12
,
fontFamily
:
R
.
fonts
.
InterMedium
,
fontWeight
:
'600'
,
color
:
R
.
colors
.
black
,
},
dayHeaderNumber
:
{
fontSize
:
R
.
fontsize
.
fontsSize12
,
fontFamily
:
R
.
fonts
.
InterMedium
,
color
:
R
.
colors
.
black
,
fontWeight
:
'600'
,
},
dayHeaderNumberContainerToday
:
{
borderRadius
:
15
,
justifyContent
:
'center'
,
alignItems
:
'center'
,
},
dayHeaderNumberToday
:
{
color
:
R
.
colors
.
white
,
fontFamily
:
R
.
fonts
.
InterMedium
,
fontSize
:
R
.
fontsize
.
fontsSize12
,
fontWeight
:
'600'
,
backgroundColor
:
R
.
colors
.
main
,
borderRadius
:
30
,
paddingHorizontal
:
6
,
paddingVertical
:
4
,
marginBottom
:
2
,
},
dayHeaderTextToday
:
{
},
// ==================== TIME SLOTS CONTAINER ====================
// Tương ứng với renderTimeSlots() (dòng 89-163)
timeSlotsContainer
:
{
flex
:
1
,
backgroundColor
:
R
.
colors
.
white
,
},
scrollContent
:
{
paddingBottom
:
50
,
},
timelineContainer
:
{
flexDirection
:
'row'
,
position
:
'relative'
,
},
// ==================== TIME LABELS COLUMN ====================
// Tương ứng với timeLabelsColumn (dòng 100-109)
timeLabelsColumn
:
{
width
:
70
,
borderRightWidth
:
1
,
borderRightColor
:
R
.
colors
.
gray220
,
},
timeSlot
:
{
height
:
80
,
alignItems
:
'center'
,
justifyContent
:
'center'
,
borderBottomWidth
:
1
,
borderBottomColor
:
R
.
colors
.
gray220
,
},
timeText
:
{
fontSize
:
R
.
fontsize
.
fontsSize12
,
fontFamily
:
R
.
fonts
.
InterRegular
,
fontWeight
:
'400'
,
color
:
R
.
colors
.
black
,
},
// ==================== WEEK GRID CONTAINER ====================
// Tương ứng với weekGridContainer (dòng 111-158)
weekGridContainer
:
{
flex
:
1
,
flexDirection
:
'row'
,
},
dayColumn
:
{
width
:
DAY_COLUMN_WIDTH
,
position
:
'relative'
,
borderRightWidth
:
1
,
borderRightColor
:
R
.
colors
.
gray220
,
},
gridCell
:
{
height
:
80
,
borderBottomWidth
:
1
,
borderBottomColor
:
R
.
colors
.
gray220
,
width
:
'100%'
,
},
// ==================== EVENT CARDS ====================
// Tương ứng với event rendering (dòng 118-155)
eventCard
:
{
borderRadius
:
10
,
paddingHorizontal
:
4
,
paddingVertical
:
2
,
justifyContent
:
'center'
,
},
eventTitle
:
{
fontSize
:
R
.
fontsize
.
fontsSize14
,
fontFamily
:
R
.
fonts
.
InterMedium
,
color
:
R
.
colors
.
white
,
fontWeight
:
'600'
,
marginBottom
:
1
,
},
eventSubtitle
:
{
fontSize
:
R
.
fontsize
.
fontsSize12
,
fontFamily
:
R
.
fonts
.
InterRegular
,
color
:
R
.
colors
.
white
,
fontWeight
:
'400'
,
},
eventTime
:
{
fontSize
:
R
.
fontsize
.
fontsSize12
,
fontFamily
:
R
.
fonts
.
InterRegular
,
color
:
R
.
colors
.
white
,
fontWeight
:
'400'
,
},
});
export
default
styles
;
src/screens/class_schedule/filter_week/view.js
View file @
19c4050a
This diff is collapsed.
Click to expand it.
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment