Skip to content
Projects
Groups
Snippets
Help
This project
Loading...
Sign in / Register
Toggle navigation
A
AppUms_Lecturer
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_Lecturer
Commits
e5a34df6
Commit
e5a34df6
authored
Aug 25, 2025
by
tungnq
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
TODO: Đã hoàn thiện giao diện lịch theo tuần và theo tháng
parent
6e9a6de1
Expand all
Show whitespace changes
Inline
Side-by-side
Showing
5 changed files
with
779 additions
and
43 deletions
+779
-43
style.js
src/screens/class_schedule/filterWeek/style.js
+0
-14
view.js
src/screens/class_schedule/filterWeek/view.js
+0
-0
index.js
src/screens/class_schedule/index.js
+306
-7
style.js
src/screens/class_schedule/style.js
+226
-11
view.js
src/screens/class_schedule/view.js
+247
-11
No files found.
src/screens/class_schedule/filterWeek/style.js
View file @
e5a34df6
import
{
StyleSheet
}
from
'react-native'
import
R
from
'../../../assets/R'
;
const
styles
=
StyleSheet
.
create
({
container
:{
flex
:
1
,
backgroundColor
:
R
.
colors
.
white
},
body
:{
flex
:
1
,
backgroundColor
:
R
.
colors
.
white
}
})
export
default
styles
src/screens/class_schedule/filterWeek/view.js
View file @
e5a34df6
This diff is collapsed.
Click to expand it.
src/screens/class_schedule/index.js
View file @
e5a34df6
import
React
from
'react'
;
import
{
Text
,
View
,
StyleSheet
}
from
'react-native'
;
import
React
,
{
useState
,
useMemo
,
useRef
}
from
'react'
;
import
{
Animated
,
PanResponder
,
Dimensions
}
from
'react-native'
;
import
ClassScheduleView
from
'./view'
;
const
ClassSchedule
=
(
props
)
=>
{
const
monthNames
=
[
'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'
,
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
));
const
[
selectedDate
,
setSelectedDate
]
=
useState
(
null
);
const
[
showBottomSheet
,
setShowBottomSheet
]
=
useState
(
false
);
const
bottomSheetTranslateY
=
useRef
(
new
Animated
.
Value
(
BOTTOM_SHEET_HEIGHT
)).
current
;
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
}
`
;
};
const
createMockEvents
=
()
=>
{
const
today
=
new
Date
();
const
todayStr
=
formatDateToString
(
today
);
const
tomorrow
=
new
Date
(
today
);
tomorrow
.
setDate
(
tomorrow
.
getDate
()
+
1
);
const
tomorrowStr
=
formatDateToString
(
tomorrow
);
return
[
{
id
:
'1'
,
title
:
'Meeting hôm nay'
,
date
:
todayStr
,
time
:
'10:00'
,
endTime
:
'11:00'
,
description
:
'Họp team development'
,
type
:
'meeting'
,
},
{
id
:
'2'
,
title
:
'Demo hôm nay'
,
date
:
todayStr
,
time
:
'15:00'
,
endTime
:
'16:00'
,
description
:
'Present tính năng mới'
,
type
:
'demo'
,
},
{
id
:
'11'
,
title
:
'Lịch học lớp IT47.8F7'
,
date
:
'2025-08-04'
,
time
:
'07:00'
,
endTime
:
'08:30'
,
description
:
'Môn học chuyên ngành'
,
type
:
'class'
,
},
{
id
:
'12'
,
title
:
'Meeting team'
,
date
:
'2025-08-04'
,
time
:
'10:00'
,
endTime
:
'11:00'
,
description
:
'Họp team development'
,
type
:
'meeting'
,
},
{
id
:
'13'
,
title
:
'Training React Native'
,
date
:
'2025-08-05'
,
time
:
'14:00'
,
endTime
:
'16:00'
,
description
:
'Học New Architecture'
,
type
:
'training'
,
},
{
id
:
'14'
,
title
:
'Code Review'
,
date
:
'2025-08-05'
,
time
:
'10:30'
,
endTime
:
'11:30'
,
description
:
'Review PR #123'
,
type
:
'review'
,
},
{
id
:
'15'
,
title
:
'Họp nội bộ giữa giảng viên bộ môn công nghệ phần mềm'
,
date
:
'2025-08-05'
,
time
:
'09:00'
,
endTime
:
'11:30'
,
description
:
'Thảo luận chương trình đào tạo mới'
,
type
:
'meeting'
,
},
{
id
:
'16'
,
title
:
'Sự kiện thắp sáng ước mơ kỹ thuật'
,
date
:
'2025-08-05'
,
time
:
'13:00'
,
endTime
:
'15:30'
,
description
:
'Chương trình định hướng nghề nghiệp cho sinh viên'
,
type
:
'event'
,
},
{
id
:
'17'
,
title
:
'Lịch học lớp EWC45.364.L1'
,
date
:
'2025-08-05'
,
time
:
'14:00'
,
endTime
:
'15:30'
,
description
:
'Tiếng Anh chuyên ngành'
,
type
:
'class'
,
},
{
id
:
'18'
,
title
:
'Họp tổng kết quả chấm nghiệm cứu khoa học sinh viên khóa K18'
,
date
:
'2025-08-05'
,
time
:
'17:00'
,
endTime
:
'20:30'
,
description
:
'Đánh giá kết quả nghiên cứu khoa học của sinh viên'
,
type
:
'meeting'
,
},
{
id
:
'3'
,
title
:
'Training React Native'
,
date
:
tomorrowStr
,
time
:
'14:00'
,
endTime
:
'16:00'
,
description
:
'Học New Architecture'
,
type
:
'training'
,
},
{
id
:
'4'
,
title
:
'Code Review'
,
date
:
tomorrowStr
,
time
:
'10:30'
,
endTime
:
'11:30'
,
description
:
'Review PR #123'
,
type
:
'review'
,
},
{
id
:
'10'
,
title
:
'Demo sản phẩm'
,
date
:
'2025-08-25'
,
time
:
'15:00'
,
endTime
:
'16:30'
,
description
:
'Present tính năng mới'
,
type
:
'demo'
,
},
];
};
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
;
const
getMonthData
=
useMemo
(()
=>
{
const
year
=
currentDate
.
getFullYear
();
const
month
=
currentDate
.
getMonth
();
const
firstDay
=
new
Date
(
year
,
month
,
1
);
const
lastDay
=
new
Date
(
year
,
month
+
1
,
0
);
const
startDate
=
new
Date
(
firstDay
);
startDate
.
setDate
(
firstDay
.
getDate
()
-
firstDay
.
getDay
());
const
days
=
[];
const
currentDateObj
=
new
Date
(
startDate
);
for
(
let
i
=
0
;
i
<
42
;
i
++
)
{
days
.
push
(
new
Date
(
currentDateObj
));
currentDateObj
.
setDate
(
currentDateObj
.
getDate
()
+
1
);
}
return
{
year
,
month
,
firstDay
,
lastDay
,
days
,
};
},
[
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
)
=>
{
return
date
.
getMonth
()
===
currentDate
.
getMonth
();
};
const
isToday
=
(
date
)
=>
{
const
today
=
new
Date
();
return
(
date
.
getDate
()
===
today
.
getDate
()
&&
date
.
getMonth
()
===
today
.
getMonth
()
&&
date
.
getFullYear
()
===
today
.
getFullYear
()
);
};
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
);
if
(
showBottomSheet
)
{
hideBottomSheetModal
();
}
};
const
showBottomSheetModal
=
()
=>
{
setShowBottomSheet
(
true
);
Animated
.
spring
(
bottomSheetTranslateY
,
{
toValue
:
0
,
tension
:
100
,
friction
:
8
,
useNativeDriver
:
true
,
}).
start
();
};
const
hideBottomSheetModal
=
()
=>
{
Animated
.
timing
(
bottomSheetTranslateY
,
{
toValue
:
BOTTOM_SHEET_HEIGHT
,
duration
:
300
,
useNativeDriver
:
true
,
}).
start
(()
=>
{
setShowBottomSheet
(
false
);
});
};
const
handleDatePress
=
(
date
)
=>
{
const
dateStr
=
formatDateToString
(
date
);
const
dayEvents
=
getEventsForDate
(
date
);
setSelectedDate
(
dateStr
);
onDateSelect
?.(
dateStr
);
if
(
dayEvents
.
length
>
0
)
{
showBottomSheetModal
();
}
};
const
handleEventPress
=
(
event
)
=>
{
onEventPress
?.(
event
);
};
const
handleCloseBottomSheet
=
()
=>
{
hideBottomSheetModal
();
};
const
getSelectedEvents
=
()
=>
{
if
(
!
selectedDate
)
return
[];
return
allEvents
.
filter
(
event
=>
event
.
date
===
selectedDate
)
.
sort
((
a
,
b
)
=>
a
.
time
.
localeCompare
(
b
.
time
));
};
return
(
<
ClassScheduleView
monthNames
=
{
monthNames
}
/
>
<
ClassScheduleView
currentDate
=
{
currentDate
}
selectedDate
=
{
selectedDate
}
showBottomSheet
=
{
showBottomSheet
}
bottomSheetTranslateY
=
{
bottomSheetTranslateY
}
panResponder
=
{
panResponder
}
getMonthData
=
{
getMonthData
}
getEventsForDate
=
{
getEventsForDate
}
parseLocalDate
=
{
parseLocalDate
}
formatDateToDisplay
=
{
formatDateToDisplay
}
isCurrentMonth
=
{
isCurrentMonth
}
isToday
=
{
isToday
}
navigateMonth
=
{
navigateMonth
}
handleDatePress
=
{
handleDatePress
}
handleEventPress
=
{
handleEventPress
}
handleCloseBottomSheet
=
{
handleCloseBottomSheet
}
getSelectedEvents
=
{
getSelectedEvents
}
/
>
);
};
...
...
src/screens/class_schedule/style.js
View file @
e5a34df6
import
{
StyleSheet
}
from
'react-native'
import
{
StyleSheet
,
Dimensions
}
from
'react-native'
;
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
BOTTOM_SHEET_HEIGHT
=
screenHeight
*
0.6
;
const
styles
=
StyleSheet
.
create
({
container
:{
flex
:
1
,
backgroundColor
:
R
.
colors
.
white
container
:
{
flex
:
1
,
backgroundColor
:
R
.
colors
.
white
,
alignItems
:
'center'
,
},
header
:
{
flexDirection
:
'row'
,
alignItems
:
'center'
,
justifyContent
:
'space-between'
,
paddingVertical
:
15
,
},
header_title
:
{
fontSize
:
R
.
fontsize
.
fontsSizeTitle
,
fontFamily
:
R
.
fonts
.
fontMedium
,
color
:
R
.
colors
.
black
,
fontWeight
:
'600'
,
},
navButton
:
{
width
:
30
,
height
:
30
,
borderRadius
:
20
,
backgroundColor
:
R
.
colors
.
blue
,
alignItems
:
'center'
,
justifyContent
:
'center'
,
},
navButtonText
:
{
color
:
R
.
colors
.
white
,
fontSize
:
R
.
fontsize
.
fontsSizeTitle
,
fontFamily
:
R
.
fonts
.
fontMedium
,
},
weekDaysContainer
:
{
flexDirection
:
'row'
,
paddingBottom
:
5
,
marginBottom
:
5
,
},
weekDayCell
:
{
width
:
CELL_WIDTH
,
alignItems
:
'center'
,
},
weekDayText
:
{
fontFamily
:
R
.
fonts
.
fontRegular
,
fontSize
:
R
.
fontsize
.
fontSizeLabel
,
fontWeight
:
'400'
,
color
:
R
.
colors
.
black
,
},
calendarGrid
:
{
},
weekRow
:
{
flexDirection
:
'row'
,
},
dayCell
:
{
width
:
CELL_WIDTH
,
minHeight
:
CELL_HEIGHT
,
borderWidth
:
1
,
borderColor
:
R
.
colors
.
grayBorderInputTextHeader
,
padding
:
4
,
alignItems
:
'center'
,
},
selectedDayCell
:
{
borderColor
:
R
.
colors
.
blue
,
borderWidth
:
1
,
},
dayText
:
{
fontSize
:
R
.
fontsize
.
fontSizeLabel
,
fontWeight
:
'500'
,
fontFamily
:
R
.
fonts
.
fontSemiBold
,
color
:
R
.
colors
.
black
,
marginBottom
:
2
,
},
dayTextInactive
:
{
color
:
R
.
colors
.
black
,
opacity
:
1
,
},
selectedDayText
:
{
color
:
R
.
colors
.
black
,
fontWeight
:
'bold'
,
fontFamily
:
R
.
fonts
.
fontSemiBold
,
},
todayText
:
{
color
:
R
.
colors
.
white
,
fontWeight
:
'bold'
,
fontFamily
:
R
.
fonts
.
fontSemiBold
,
backgroundColor
:
R
.
colors
.
blue
,
borderRadius
:
15
,
paddingHorizontal
:
4
,
paddingVertical
:
3
,
},
eventsContainer
:
{
width
:
'100%'
,
flex
:
1
,
},
eventBar
:
{
paddingVertical
:
2
,
paddingHorizontal
:
5
,
borderRadius
:
10
,
marginBottom
:
2
,
backgroundColor
:
R
.
colors
.
blue
},
eventBarText
:
{
fontSize
:
R
.
fontsize
.
fontSizeLabel
,
color
:
R
.
colors
.
white
,
fontWeight
:
'500'
,
fontFamily
:
R
.
fonts
.
fontRegular
},
moreEventsText
:
{
fontSize
:
R
.
sizes
.
xs
,
color
:
R
.
colors
.
grey_100
,
textAlign
:
'center'
,
},
modalBackdrop
:
{
flex
:
1
,
backgroundColor
:
R
.
colors
.
grey_200
,
justifyContent
:
'flex-end'
,
},
bottomSheet
:
{
height
:
BOTTOM_SHEET_HEIGHT
,
backgroundColor
:
R
.
colors
.
white
,
borderTopLeftRadius
:
20
,
borderTopRightRadius
:
20
,
},
bottomSheetContent
:
{
paddingHorizontal
:
15
,
},
dragHandle
:
{
width
:
40
,
height
:
4
,
backgroundColor
:
R
.
colors
.
grey_200
,
borderRadius
:
2
,
alignSelf
:
'center'
,
marginTop
:
10
,
marginBottom
:
15
,
},
bottomSheetHeader
:
{
flexDirection
:
'row'
,
alignItems
:
'center'
,
justifyContent
:
'space-between'
,
marginBottom
:
20
,
},
bottomSheetTitle
:
{
fontSize
:
R
.
fontsize
.
fontSizeHeader1
,
fontFamily
:
R
.
fonts
.
InterMedium
,
color
:
R
.
colors
.
black
,
flex
:
1
,
},
closeButton
:
{
width
:
30
,
height
:
30
,
borderRadius
:
15
,
backgroundColor
:
R
.
colors
.
grey_200
,
alignItems
:
'center'
,
justifyContent
:
'center'
,
},
closeButtonText
:
{
fontSize
:
R
.
fontsize
.
fontsSize12
,
color
:
R
.
colors
.
grey_800
,
fontFamily
:
R
.
fonts
.
InterRegular
,
},
noEventsContainer
:
{
flex
:
1
,
alignItems
:
'center'
,
justifyContent
:
'center'
,
paddingVertical
:
40
,
},
noEventsText
:
{
fontSize
:
R
.
fontsize
.
fontsSize12
,
fontFamily
:
R
.
fonts
.
InterRegular
,
color
:
R
.
colors
.
grey_800
,
fontWeight
:
'400'
},
eventCard
:
{
flexDirection
:
'row'
,
backgroundColor
:
R
.
colors
.
white
,
borderRadius
:
12
,
padding
:
15
,
marginBottom
:
12
,
borderLeftWidth
:
4
,
borderLeftColor
:
R
.
colors
.
blue500
,
shadowColor
:
R
.
colors
.
black
,
shadowOffset
:
{
width
:
0
,
height
:
2
,
},
shadowOpacity
:
1
,
shadowRadius
:
1
,
elevation
:
2
,
},
eventTimeContainer
:
{
minWidth
:
80
,
alignItems
:
'flex-start'
,
justifyContent
:
'flex-start'
,
marginRight
:
15
,
},
eventTime
:
{
fontSize
:
R
.
fontsize
.
fontsSize12
,
fontFamily
:
R
.
fonts
.
InterMedium
,
color
:
R
.
colors
.
blue500
,
fontWeight
:
'600'
,
},
eventContent
:
{
flex
:
1
,
},
eventTitle
:
{
fontSize
:
R
.
fontsize
.
fontsSize12
,
fontFamily
:
R
.
fonts
.
InterMedium
,
color
:
R
.
colors
.
black
,
fontWeight
:
'600'
,
marginBottom
:
4
,
},
eventDescription
:
{
fontSize
:
R
.
fontsize
.
fontsSize12
,
fontFamily
:
R
.
fonts
.
InterRegular
,
color
:
R
.
colors
.
grey_800
,
},
body
:{
flex
:
1
,
backgroundColor
:
R
.
colors
.
white
}
})
});
export
default
styles
\ No newline at end of file
export
{
styles
,
CELL_WIDTH
,
BOTTOM_SHEET_HEIGHT
};
src/screens/class_schedule/view.js
View file @
e5a34df6
import
React
from
'react'
;
import
{
Text
,
View
,
TouchableOpacity
,
StyleSheet
}
from
'react-native'
;
import
styles
from
'./style'
;
const
ClassScheduleView
=
(
props
)
=>
{
const
{
}
=
props
;
const
renderHeader
=
()
=>
{
return
(
<
View
>
import
{
Text
,
View
,
TouchableOpacity
,
StyleSheet
,
ScrollView
,
Dimensions
,
Modal
,
Animated
,
SafeAreaView
,
}
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'
;
const
ClassScheduleView
=
({
currentDate
,
selectedDate
,
showBottomSheet
,
bottomSheetTranslateY
,
panResponder
,
getMonthData
,
getEventsForDate
,
parseLocalDate
,
formatDateToDisplay
,
isCurrentMonth
,
isToday
,
navigateMonth
,
handleDatePress
,
handleEventPress
,
handleCloseBottomSheet
,
getSelectedEvents
,
})
=>
{
const
navigation
=
useNavigation
();
const
renderHeader
=
()
=>
{
const
monthNames
=
[
'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
(
<
View
style
=
{
styles
.
header
}
>
<
TouchableOpacity
style
=
{
styles
.
navButton
}
onPress
=
{()
=>
navigateMonth
(
'prev'
)}
>
<
Text
style
=
{
styles
.
navButtonText
}
>
‹
<
/Text
>
<
/TouchableOpacity
>
<
Text
style
=
{
styles
.
header_title
}
>
{
monthNames
[
getMonthData
.
month
]}
{
getMonthData
.
year
}
<
/Text
>
<
TouchableOpacity
style
=
{
styles
.
navButton
}
onPress
=
{()
=>
navigateMonth
(
'next'
)}
>
<
Text
style
=
{
styles
.
navButtonText
}
>
›
<
/Text
>
<
/TouchableOpacity
>
<
/View
>
)
);
};
const
renderWeekDays
=
()
=>
{
const
weekDays
=
[
'CN'
,
'T2'
,
'T3'
,
'T4'
,
'T5'
,
'T6'
,
'T7'
];
return
(
<
View
style
=
{
styles
.
weekDaysContainer
}
>
{
weekDays
.
map
((
day
,
index
)
=>
(
<
View
key
=
{
index
}
style
=
{
styles
.
weekDayCell
}
>
<
Text
style
=
{
styles
.
weekDayText
}
>
{
day
}
<
/Text
>
<
/View
>
))}
<
/View
>
);
};
const
renderDayCell
=
(
date
,
index
)
=>
{
const
dayEvents
=
getEventsForDate
(
date
);
const
isSelected
=
selectedDate
===
formatDateToString
(
date
);
const
isTodayDate
=
isToday
(
date
);
const
isInCurrentMonth
=
isCurrentMonth
(
date
);
return
(
<
TouchableOpacity
key
=
{
index
}
style
=
{[
styles
.
dayCell
,
isSelected
&&
styles
.
selectedDayCell
,
isTodayDate
&&
styles
.
todayCell
,
!
isInCurrentMonth
&&
{
backgroundColor
:
R
.
colors
.
gray
},
]}
onPress
=
{()
=>
handleDatePress
(
date
)}
activeOpacity
=
{
0.7
}
>
<
Text
style
=
{[
styles
.
dayText
,
!
isInCurrentMonth
&&
styles
.
dayTextInactive
,
isSelected
&&
styles
.
selectedDayText
,
isTodayDate
&&
styles
.
todayText
,
]}
>
{
date
.
getDate
()}
<
/Text
>
{
dayEvents
.
length
>
0
&&
(
<
View
style
=
{
styles
.
eventsContainer
}
>
{
dayEvents
.
slice
(
0
,
2
).
map
((
event
,
eventIndex
)
=>
(
<
TouchableOpacity
key
=
{
event
.
id
}
style
=
{[
styles
.
eventBar
,
]}
onPress
=
{()
=>
handleEventPress
(
event
)}
>
<
Text
style
=
{
styles
.
eventBarText
}
numberOfLines
=
{
1
}
>
{
event
.
title
}
<
/Text
>
<
/TouchableOpacity
>
))}
{
dayEvents
.
length
>
2
&&
(
<
Text
style
=
{
styles
.
moreEventsText
}
>+
{
dayEvents
.
length
-
2
}
<
/Text
>
)}
<
/View
>
)}
<
/TouchableOpacity
>
);
};
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
}
`
;
};
const
renderCalendarGrid
=
()
=>
{
const
weeks
=
[];
for
(
let
i
=
0
;
i
<
6
;
i
++
)
{
const
week
=
getMonthData
.
days
.
slice
(
i
*
7
,
(
i
+
1
)
*
7
);
weeks
.
push
(
<
View
key
=
{
i
}
style
=
{
styles
.
weekRow
}
>
{
week
.
map
((
date
,
dayIndex
)
=>
renderDayCell
(
date
,
i
*
7
+
dayIndex
),
)}
<
/View>
,
);
}
return
<
View
style
=
{
styles
.
calendarGrid
}
>
{
weeks
}
<
/View>
;
};
const
renderBottomSheetContent
=
()
=>
{
if
(
!
selectedDate
)
return
null
;
const
selectedDateObj
=
parseLocalDate
(
selectedDate
);
const
selectedEvents
=
getSelectedEvents
();
return
(
<
View
style
=
{
styles
.
container
}
>
<
View
style
=
{
styles
.
body
}
>
<
View
style
=
{
styles
.
bottomSheetContent
}
>
<
View
style
=
{
styles
.
dragHandle
}
/
>
<
View
style
=
{
styles
.
bottomSheetHeader
}
>
<
Text
style
=
{
styles
.
bottomSheetTitle
}
>
{
formatDateToDisplay
(
selectedDateObj
)}
<
/Text
>
<
TouchableOpacity
style
=
{
styles
.
closeButton
}
onPress
=
{
handleCloseBottomSheet
}
>
<
Text
style
=
{
styles
.
closeButtonText
}
>
✕
<
/Text
>
<
/TouchableOpacity
>
<
/View
>
<
ScrollView
style
=
{
styles
.
eventsScrollView
}
showsVerticalScrollIndicator
=
{
false
}
>
{
selectedEvents
.
length
===
0
?
(
<
View
style
=
{
styles
.
noEventsContainer
}
>
<
Text
style
=
{
styles
.
noEventsText
}
>
Kh
ô
ng
c
ó
s
ự
ki
ệ
n
n
à
o
<
/Text
>
<
/View
>
)
:
(
selectedEvents
.
map
((
event
,
index
)
=>
(
<
TouchableOpacity
key
=
{
event
.
id
}
style
=
{
styles
.
eventCard
}
onPress
=
{()
=>
navigation
.
navigate
(
SCREENNAME
.
DETAILCLASSSCHEDULE
,
{
event
})}
activeOpacity
=
{
0.7
}
>
<
View
style
=
{
styles
.
eventTimeContainer
}
>
<
Text
style
=
{
styles
.
eventTime
}
>
{
event
.
time
}
{
event
.
endTime
&&
` -
${
event
.
endTime
}
`
}
<
/Text
>
<
/View
>
<
View
style
=
{
styles
.
eventContent
}
>
<
Text
style
=
{
styles
.
eventTitle
}
numberOfLines
=
{
2
}
>
{
event
.
title
}
<
/Text
>
{
event
.
description
&&
(
<
Text
style
=
{
styles
.
eventDescription
}
numberOfLines
=
{
3
}
>
{
event
.
description
}
<
/Text
>
)}
<
/View
>
<
/TouchableOpacity
>
))
)}
<
/ScrollView
>
<
/View
>
);
};
const
renderBottomSheet
=
()
=>
{
return
(
<
Modal
visible
=
{
showBottomSheet
}
transparent
=
{
true
}
animationType
=
"none"
onRequestClose
=
{
handleCloseBottomSheet
}
>
<
TouchableOpacity
style
=
{
styles
.
modalBackdrop
}
activeOpacity
=
{
1
}
onPress
=
{
handleCloseBottomSheet
}
>
<
Animated
.
View
style
=
{[
styles
.
bottomSheet
,
{
transform
:
[{
translateY
:
bottomSheetTranslateY
}],
},
]}
{...
panResponder
.
panHandlers
}
>
<
TouchableOpacity
activeOpacity
=
{
1
}
>
{
renderBottomSheetContent
()}
<
/TouchableOpacity
>
<
/Animated.View
>
<
/TouchableOpacity
>
<
/Modal
>
);
};
return
(
<
SafeAreaView
style
=
{
styles
.
container
}
>
<
ScrollView
showsVerticalScrollIndicator
=
{
false
}
>
{
renderHeader
()}
{
renderWeekDays
()}
{
renderCalendarGrid
()}
<
/ScrollView
>
{
renderBottomSheet
()}
<
/SafeAreaView
>
);
};
export
default
ClassScheduleView
;
\ No newline at end of file
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