@@ -20,44 +20,110 @@ interface AttendanceRecord {
2020 code : string ;
2121 duration : string ;
2222 description ?: string ;
23+ id : string ; // Required ID for records
24+ isLocal ?: boolean ; // Flag to identify locally created records
2325}
2426
2527const InstructorAttendancePage : React . FC < Props > = ( ) => {
2628 const { courseId } = useParams < { courseId : string } > ( ) ;
2729 const [ modalOpen , setModalOpen ] = useState ( false ) ;
2830 const [ courseInfo , setCourseInfo ] = useState < CourseInfo | null > ( null ) ;
2931 const [ attendanceRecords , setAttendanceRecords ] = useState < AttendanceRecord [ ] > ( [ ] ) ;
32+ const [ isLoading , setIsLoading ] = useState ( true ) ;
3033
34+ // Load course info
3135 useEffect ( ( ) => {
3236 if ( ! courseId ) return ;
3337
38+ setIsLoading ( true ) ;
3439 RequestService . get ( `/api/courses/${ courseId } ` )
3540 . then ( data => {
36- setCourseInfo ( {
41+ const course = {
3742 id : data . id ,
3843 number : data . number ,
3944 name : data . name ,
4045 semester : data . semester
41- } ) ;
46+ } ;
47+ setCourseInfo ( course ) ;
4248 } )
43- . catch ( err => console . error ( 'Error fetching course info:' , err ) ) ;
49+ . catch ( err => {
50+ console . error ( 'Error fetching course info:' , err ) ;
51+ setIsLoading ( false ) ;
52+ } ) ;
4453 } , [ courseId ] ) ;
4554
55+ // Load attendance records - combine API records and localStorage records
4656 useEffect ( ( ) => {
4757 if ( ! courseInfo ?. id ) return ;
4858
59+ // First, get records from API
4960 RequestService . get ( `/api/courses/${ courseInfo . id } /attendance` )
50- . then ( data => setAttendanceRecords ( data ) )
51- . catch ( err => console . error ( 'Failed to load attendance:' , err ) ) ;
61+ . then ( apiRecords => {
62+ // Make sure API records have IDs
63+ const formattedApiRecords = apiRecords . map ( ( record : any , index : number ) => ( {
64+ ...record ,
65+ id : record . id || `api-${ index } -${ Date . now ( ) } `
66+ } ) ) ;
67+
68+ // Then, get local records from localStorage
69+ const localStorageKey = `attendance_${ courseInfo . id } ` ;
70+ const localRecordsString = localStorage . getItem ( localStorageKey ) ;
71+ let localRecords : AttendanceRecord [ ] = [ ] ;
72+
73+ if ( localRecordsString ) {
74+ try {
75+ localRecords = JSON . parse ( localRecordsString ) ;
76+ } catch ( e ) {
77+ console . error ( 'Error parsing local attendance records:' , e ) ;
78+ localStorage . removeItem ( localStorageKey ) ; // Clear invalid data
79+ }
80+ }
81+
82+ // Combine and set records
83+ const allRecords = [ ...localRecords , ...formattedApiRecords ] ;
84+ setAttendanceRecords ( allRecords ) ;
85+ setIsLoading ( false ) ;
86+ } )
87+ . catch ( err => {
88+ console . error ( 'Failed to load attendance from API:' , err ) ;
89+
90+ // Still try to load local records on API failure
91+ const localStorageKey = `attendance_${ courseInfo . id } ` ;
92+ const localRecordsString = localStorage . getItem ( localStorageKey ) ;
93+ if ( localRecordsString ) {
94+ try {
95+ const localRecords = JSON . parse ( localRecordsString ) ;
96+ setAttendanceRecords ( localRecords ) ;
97+ } catch ( e ) {
98+ console . error ( 'Error parsing local attendance records:' , e ) ;
99+ }
100+ }
101+ setIsLoading ( false ) ;
102+ } ) ;
52103 } , [ courseInfo ] ) ;
53104
105+ // Save local records to localStorage whenever they change
106+ useEffect ( ( ) => {
107+ if ( ! courseInfo ?. id || attendanceRecords . length === 0 ) return ;
108+
109+ // Filter out only local records
110+ const localRecords = attendanceRecords . filter ( record => record . isLocal === true ) ;
111+ if ( localRecords . length === 0 ) return ;
112+
113+ // Save to localStorage
114+ const localStorageKey = `attendance_${ courseInfo . id } ` ;
115+ localStorage . setItem ( localStorageKey , JSON . stringify ( localRecords ) ) ;
116+ } , [ attendanceRecords , courseInfo ] ) ;
117+
54118 const saveToCsv = ( ) => {
119+ if ( ! attendanceRecords . length || ! courseInfo ) return ;
120+
55121 const toCSV = [ ] ;
56122 let header = 'Course,Date,Code,Duration (min),Description' ;
57123 toCSV . push ( header + '\n' ) ;
58124
59125 attendanceRecords . forEach ( record => {
60- const row = `${ record . courseInfo . number } : ${ record . courseInfo . name } , ${ record . date } , ${ record . code } , ${ record . duration } , ${ record . description || '' } ` ;
126+ const row = `" ${ record . courseInfo . number } : ${ record . courseInfo . name } "," ${ record . date } "," ${ record . code } "," ${ record . duration } "," ${ record . description || '' } " ` ;
61127 toCSV . push ( row + '\n' ) ;
62128 } ) ;
63129
@@ -69,14 +135,49 @@ const InstructorAttendancePage: React.FC<Props> = () => {
69135 const encodedUri = encodeURI ( final ) ;
70136 const link = document . createElement ( 'a' ) ;
71137 link . setAttribute ( 'href' , encodedUri ) ;
72- link . setAttribute ( 'download' , `${ courseInfo ? .number . replace ( " " , '' ) . toLowerCase ( ) } _attendance.csv` ) ;
138+ link . setAttribute ( 'download' , `${ courseInfo . number . replace ( / \s + / g , '' ) . toLowerCase ( ) } _attendance.csv` ) ;
73139 document . body . appendChild ( link ) ;
74140 link . click ( ) ;
75141 document . body . removeChild ( link ) ;
76142 } ;
77143
78- if ( ! courseInfo ) {
79- return < PageWrapper > < p className = 'info' > Loading course info...</ p > </ PageWrapper > ;
144+ const handleAttendanceSubmit = ( newSession : any ) => {
145+ if ( ! courseInfo ) return ;
146+
147+ // Create a new record directly without a POST request
148+ const newRecord : AttendanceRecord = {
149+ id : `local-${ Date . now ( ) } ` , // Generate a unique local ID
150+ courseInfo : courseInfo ,
151+ date : newSession . date ,
152+ code : newSession . code ,
153+ duration : newSession . duration ,
154+ description : newSession . description ,
155+ isLocal : true // Mark as locally created
156+ } ;
157+
158+ // Add the new record to the beginning of the array
159+ setAttendanceRecords ( prev => [ newRecord , ...prev ] ) ;
160+ setModalOpen ( false ) ;
161+
162+ // Save this record to localStorage immediately
163+ const localStorageKey = `attendance_${ courseInfo . id } ` ;
164+ const existingRecordsString = localStorage . getItem ( localStorageKey ) ;
165+ let existingRecords : AttendanceRecord [ ] = [ ] ;
166+
167+ if ( existingRecordsString ) {
168+ try {
169+ existingRecords = JSON . parse ( existingRecordsString ) ;
170+ } catch ( e ) {
171+ console . error ( 'Error parsing local attendance records:' , e ) ;
172+ }
173+ }
174+
175+ localStorage . setItem ( localStorageKey , JSON . stringify ( [ newRecord , ...existingRecords ] ) ) ;
176+ console . log ( 'Attendance record created and saved locally:' , newRecord ) ;
177+ } ;
178+
179+ if ( isLoading || ! courseInfo ) {
180+ return < PageWrapper > < p style = { { padding : '2rem' } } > Loading course info...</ p > </ PageWrapper > ;
80181 }
81182
82183 return (
@@ -98,33 +199,7 @@ const InstructorAttendancePage: React.FC<Props> = () => {
98199 < InstructorAttendanceModal
99200 open = { modalOpen }
100201 onClose = { ( ) => setModalOpen ( false ) }
101- onSubmit = { ( newSession ) => {
102- const payload = {
103- courseId : courseInfo . id ,
104- date : newSession . date ,
105- code : newSession . code ,
106- duration : newSession . duration ,
107- description : newSession . description
108- } ;
109-
110- console . log ( 'Creating attendance with payload:' , payload ) ;
111-
112- RequestService . post ( `/api/attendance` , payload )
113- . then ( ( savedSession ) => {
114- setAttendanceRecords ( prev => [
115- {
116- ...savedSession ,
117- courseInfo
118- } ,
119- ...prev
120- ] ) ;
121- setModalOpen ( false ) ;
122- } )
123- . catch ( err => {
124- console . error ( 'Failed to save attendance:' , err . response ?. data || err . message || err ) ;
125- alert ( 'Could not create attendance. Please try again.' ) ;
126- } ) ;
127- } }
202+ onSubmit = { handleAttendanceSubmit }
128203 courseInfo = { courseInfo }
129204 />
130205
@@ -143,8 +218,8 @@ const InstructorAttendancePage: React.FC<Props> = () => {
143218 </ tr >
144219 </ thead >
145220 < tbody >
146- { attendanceRecords . map ( ( entry , index ) => (
147- < tr key = { ` ${ entry . code } - ${ index } ` } >
221+ { attendanceRecords . map ( ( entry ) => (
222+ < tr key = { entry . id } >
148223 < td > { entry . courseInfo . number } : { entry . courseInfo . name } </ td >
149224 < td > { entry . date } </ td >
150225 < td > { entry . code } </ td >
@@ -161,5 +236,4 @@ const InstructorAttendancePage: React.FC<Props> = () => {
161236 ) ;
162237} ;
163238
164-
165- export default InstructorAttendancePage ;
239+ export default InstructorAttendancePage ;
0 commit comments