1- import React , { useEffect , useState } from "react" ;
1+ import React , { useState , useEffect } from "react" ;
22import ProgressBar from "../../components/Progressbar" ;
3- import { getMeetingsOfWeek } from "../../api/MeetingApi" ;
3+ import { getMeetingsOfWeek } from "../../api/MeetingApi" ;
44import axiosInstance from "../../AxiosConfig" ;
5- import { MeetingDto } from "../../dtos/MeetingDto" ;
5+ import { MeetingDto } from "../../dtos/MeetingDto" ;
66import ModuleProgressSettings from "../ModuleProgressSettings" ;
7- import { Chapter , Checkbox , UserModule } from "../../dtos/ModuleDto" ;
8- import { getUser } from "../../api/UserApi" ;
7+ import { Chapter , Checkbox , UserModule as BaseModule } from "../../dtos/ModuleDto" ;
8+ import { getUser , updateUserModules } from "../../api/UserApi" ;
9+ import ExamDateModal from "../../components/meeting/ExamDateModal" ;
910
10- export default function UserInfo ( props : { reload : boolean } ) {
11- const [ weeklyMeetings , setWeeklyMeetings ] = useState < MeetingDto [ ] > ( [ ] ) ;
12- const [ isModalOpen , setIsModalOpen ] = useState < boolean > ( false ) ;
13- const [ modules , setModules ] = useState < UserModule [ ] > ( [ ] ) ;
14- const [ activeModule , setActiveModule ] = useState < UserModule | undefined > ( ) ;
15-
16- const filterMeetingsForCurrentWeek = ( meetings : MeetingDto [ ] ) => {
17- const currentDate = new Date ( ) ;
18- const startOfWeek = new Date ( currentDate . setDate ( currentDate . getDate ( ) - currentDate . getDay ( ) + 1 ) ) ;
19- const endOfWeek = new Date ( currentDate . setDate ( startOfWeek . getDate ( ) + 6 ) ) ;
11+ type UserModule = BaseModule & {
12+ examDate ?: string ;
13+ examTime ?: string ;
14+ examRoom ?: string ;
15+ } ;
2016
21- return meetings . filter ( ( meeting ) => {
22- const meetingDate = new Date ( meeting . dateFrom ) ;
23- return meetingDate >= startOfWeek && meetingDate <= endOfWeek ;
24- } ) ;
25- } ;
26-
27- const fetchUserInfo = async ( ) => {
28- try {
29- const response = await getUser ( axiosInstance ) ;
30- setModules ( response . modules ) ;
31- } catch ( error ) {
32- console . error ( "Fehler beim Abrufen der UserDaten: " + error ) ;
33- }
17+ export default function UserInfo ( props : { reload : boolean } ) {
18+ const [ weeklyMeetings , setWeeklyMeetings ] = useState < MeetingDto [ ] > ( [ ] ) ;
19+ const [ isProgressModalOpen , setIsProgressModalOpen ] = useState ( false ) ;
20+ const [ isExamModalOpen , setIsExamModalOpen ] = useState ( false ) ;
21+ const [ modules , setModules ] = useState < UserModule [ ] > ( [ ] ) ;
22+ const [ activeModule , setActiveModule ] = useState < UserModule | undefined > ( ) ;
23+
24+ useEffect ( ( ) => { fetchUserInfo ( ) ; fetchMeetings ( ) ; } , [ ] ) ;
25+ useEffect ( ( ) => { fetchUserInfo ( ) ; } , [ props . reload ] ) ;
26+
27+ async function fetchUserInfo ( ) {
28+ try {
29+ const user = await getUser ( axiosInstance ) ;
30+ setModules ( user . modules as UserModule [ ] ) ;
31+ } catch ( e ) {
32+ console . error ( e ) ;
3433 }
35-
36- const fetchMeetings = async ( ) => {
37- try {
38- const response = await getMeetingsOfWeek ( axiosInstance ) ;
39- setWeeklyMeetings ( filterMeetingsForCurrentWeek ( response ) ) ;
40- } catch ( error ) {
41- console . error ( "Fehler beim Abrufen der Meetings: " + error ) ;
42- }
43- } ;
44-
45- useEffect ( ( ) => {
46- fetchMeetings ( ) ;
47- fetchUserInfo ( ) ;
48- } , [ ] ) ;
49-
50- useEffect ( ( ) => {
51- fetchUserInfo ( ) ;
52- } , [ props . reload ] ) ;
53-
54- const openModal = ( moduleName : string ) => {
55- setActiveModule ( modules . find ( ( m ) => m . name === moduleName ) ) ;
56- setIsModalOpen ( true ) ;
57- } ;
58-
59- const closeModal = ( ) => {
60- setIsModalOpen ( false ) ;
61- } ;
62-
63- const calculateProgress = ( module : UserModule ) : number => {
64- if ( ! module . chapter ?. length ) return 0 ;
65-
66- let checked = 0 ;
67- let total = 0 ;
68-
69- module . chapter . forEach ( ( chapter : Chapter ) => {
70- total += chapter . checkbox . length ;
71- checked += chapter . checkbox . filter ( ( box : Checkbox ) => box . checked ) . length ;
72- } ) ;
73-
74- return total === 0 ? 0 : checked / total ;
34+ }
35+
36+ async function fetchMeetings ( ) {
37+ try {
38+ const resp = await getMeetingsOfWeek ( axiosInstance ) ;
39+ setWeeklyMeetings ( filterMeetingsForCurrentWeek ( resp ) ) ;
40+ } catch ( e ) {
41+ console . error ( e ) ;
7542 }
76-
77- return (
78- < div className = "lg:w-[60%] w-[80%] sm:px-8 mb-16 justify-center mt-8" >
79- < div className = "w-full md:px-16" >
80- < p className = "text-2xl font-bold text-white text-left lg:mt-4 mt-16 mb-7" > Prüfungstermine</ p >
81-
82- < table className = "w-full border-collapse hidden md:table" >
83- < tbody >
84- { modules && modules . map ( ( subject , index ) => (
85- < tr key = { index } >
86- < td className = "px-1 py-1 text-[#9B9B9B]" > { subject . name } </ td >
87- < td className = "px-1 py-1 text-[#2AB19D]" > DATE</ td >
88- < td className = "px-1 py-1 text-[#9B9B9B]" > TIME</ td >
89- < td className = "px-1 py-1 text-[#9B9B9B]" > ROOM</ td >
90- </ tr >
91- ) ) }
92- </ tbody >
93- </ table >
94- < div className = "md:hidden space-y-4" >
95- { modules && modules . map ( ( subject , index ) => (
96- < div key = { index } className = "p-3 shadow-sm" >
97- < p className = "text-[#9B9B9B]" >
98- { subject . name }
99- </ p >
100- < p className = "text-[#2AB19D] inline" >
101- DATE
102- </ p >
103- < p className = "text-[#9B9B9B] inline ml-2" >
104- TIME ROOM
105- </ p >
106- </ div >
107- ) ) }
108- </ div >
109-
110- < p className = "text-2xl font-bold text-white text-left mt-9" > Lerngruppen in dieser Woche</ p >
111-
112- < table className = "w-full border-collapse hidden md:table" >
113- < tbody >
114- { weeklyMeetings . map ( ( meeting , index ) => (
115- < tr key = { index } >
116- < td className = "px-1 py-1 text-[#9B9B9B]" > { meeting . module } </ td >
117- < td className = "px-1 py-1 text-[#2AB19D]" >
118- { new Date ( meeting . dateFrom ) . toLocaleDateString ( ) }
119- </ td >
120- < td className = "px-1 py-1 text-[#9B9B9B]" >
121- { new Date ( meeting . dateFrom ) . toLocaleTimeString ( [ ] , {
122- hour : '2-digit' ,
123- minute : '2-digit'
124- } ) }
125- </ td >
126- < td className = "px-1 py-1 text-[#9B9B9B]" > { meeting . place } </ td >
127- </ tr >
128- ) ) }
129- </ tbody >
130- </ table >
131-
132- < div className = "md:hidden space-y-4" >
133- { weeklyMeetings . map ( ( meeting , index ) => (
134- < div key = { index } className = "p-3 shadow-sm" >
135- < p className = "text-[#9B9B9B]" >
136- { meeting . module }
137- </ p >
138- < p className = "text-[#2AB19D] inline" >
139- { new Date ( meeting . dateFrom ) . toLocaleDateString ( ) }
140- </ p >
141- < p className = "text-[#9B9B9B] inline ml-2" >
142- { new Date ( meeting . dateFrom ) . toLocaleTimeString ( [ ] , {
143- hour : '2-digit' ,
144- minute : '2-digit'
145- } ) } { meeting . place }
146- </ p >
147- </ div >
148- ) ) }
149- </ div >
150-
151- < p className = "text-2xl font-bold text-white text-left mt-9 mb-6" > Lernfortschritt</ p >
152- { modules && modules . map ( ( subject , index ) => (
153- < div key = { index } className = "mb-6" >
154- < p className = "text-m text-[#9B9B9B]" > { subject . name } </ p >
155- < div onClick = { ( ) => {
156- openModal ( subject . name )
157- } } >
158- < ProgressBar progress = { calculateProgress ( subject ) } />
159- </ div >
160- </ div >
161- ) ) }
43+ }
44+
45+ function filterMeetingsForCurrentWeek ( meetings : MeetingDto [ ] ) {
46+ const now = new Date ( ) ;
47+ const start = new Date ( now . setDate ( now . getDate ( ) - now . getDay ( ) + 1 ) ) ;
48+ const end = new Date ( start ) ;
49+ end . setDate ( start . getDate ( ) + 6 ) ;
50+ return meetings . filter ( m => {
51+ const d = new Date ( m . dateFrom ) ;
52+ return d >= start && d <= end ;
53+ } ) ;
54+ }
55+
56+ function calculateProgress ( module : UserModule ) {
57+ if ( ! module . chapter ) return 0 ;
58+ let total = 0 , checked = 0 ;
59+ module . chapter . forEach ( ch => {
60+ total += ch . checkbox . length ;
61+ checked += ch . checkbox . filter ( b => b . checked ) . length ;
62+ } ) ;
63+ return total === 0 ? 0 : checked / total ;
64+ }
65+
66+ const openProgressModal = ( name : string ) => {
67+ setActiveModule ( modules . find ( m => m . name === name ) ) ;
68+ setIsProgressModalOpen ( true ) ;
69+ } ;
70+ const closeProgressModal = ( ) => setIsProgressModalOpen ( false ) ;
71+
72+ const openExamModal = ( ) => setIsExamModalOpen ( true ) ;
73+ const closeExamModal = ( ) => setIsExamModalOpen ( false ) ;
74+
75+ const handleAddExam = async ( moduleName : string , date : string , time : string , room : string ) => {
76+ const updated = modules . map ( m =>
77+ m . name === moduleName
78+ ? { ...m , examDate : date , examTime : time , examRoom : room }
79+ : m
80+ ) ;
81+ try {
82+ await updateUserModules ( axiosInstance , updated as BaseModule [ ] ) ;
83+ setModules ( updated ) ;
84+ closeExamModal ( ) ;
85+ } catch ( e ) {
86+ console . error ( "Fehler beim Speichern der Prüfung: " , e ) ;
87+ }
88+ } ;
89+
90+ return (
91+ < div className = "lg:w-[60%] w-[80%] sm:px-8 mb-16 justify-center mt-8" >
92+ < div className = "w-full md:px-16" >
93+ < p className = "text-2xl font-bold text-white text-left lg:mt-4 mt-16 mb-7" > Prüfungstermine</ p >
94+ < table className = "w-full border-collapse hidden md:table" >
95+ < tbody >
96+ { modules . map ( ( subject , idx ) => (
97+ < tr key = { idx } >
98+ < td className = "px-1 py-1 text-[#9B9B9B]" > { subject . name } </ td >
99+ < td className = "px-1 py-1 text-[#2AB19D]" > { subject . examDate ?? '--' } </ td >
100+ < td className = "px-1 py-1 text-[#9B9B9B]" > { subject . examTime ?? '--' } </ td >
101+ < td className = "px-1 py-1 text-[#9B9B9B]" > { subject . examRoom ?? '--' } </ td >
102+ </ tr >
103+ ) ) }
104+ </ tbody >
105+ </ table >
106+ < div className = "md:hidden space-y-4" >
107+ { modules . map ( ( subject , idx ) => (
108+ < div key = { idx } className = "p-3 shadow-sm" >
109+ < p className = "text-[#9B9B9B]" > { subject . name } </ p >
110+ < p className = "text-[#2AB19D] inline" > { subject . examDate ?? '--' } </ p >
111+ < p className = "text-[#9B9B9B] inline ml-2" >
112+ { subject . examTime ?? '--' } { subject . examRoom ?? '' }
113+ </ p >
162114 </ div >
163- {
164- isModalOpen && activeModule &&
165- < ModuleProgressSettings onClose = { closeModal } module = { activeModule } allUserModules = { modules } />
166- }
115+ ) ) }
167116 </ div >
168- ) ;
169- }
117+ < button
118+ onClick = { openExamModal }
119+ className = "mt-4 bg-[#56A095] text-white py-2 px-4 rounded-lg hover:bg-[#42907a]"
120+ >
121+ Prüfungstermin bearbeiten
122+ </ button >
123+
124+ < p className = "text-2xl font-bold text-white text-left mt-9 mb-4" > Lerngruppen in dieser Woche</ p >
125+ < table className = "w-full border-collapse hidden md:table" >
126+ < tbody >
127+ { weeklyMeetings . map ( ( meeting , index ) => (
128+ < tr key = { index } >
129+ < td className = "px-1 py-1 text-[#9B9B9B]" > { meeting . module } </ td >
130+ < td className = "px-1 py-1 text-[#2AB19D]" >
131+ { new Date ( meeting . dateFrom ) . toLocaleDateString ( ) }
132+ </ td >
133+ < td className = "px-1 py-1 text-[#9B9B9B]" >
134+ { new Date ( meeting . dateFrom ) . toLocaleTimeString ( [ ] , { hour : '2-digit' , minute : '2-digit' } ) }
135+ </ td >
136+ < td className = "px-1 py-1 text-[#9B9B9B]" > { meeting . place } </ td >
137+ </ tr >
138+ ) ) }
139+ </ tbody >
140+ </ table >
141+ < div className = "md:hidden space-y-4" >
142+ { weeklyMeetings . map ( ( meeting , index ) => (
143+ < div key = { index } className = "p-3 shadow-sm" >
144+ < p className = "text-[#9B9B9B]" > { meeting . module } </ p >
145+ < p className = "text-[#2AB19D] inline" > { new Date ( meeting . dateFrom ) . toLocaleDateString ( ) } </ p >
146+ < p className = "text-[#9B9B9B] inline ml-2" >
147+ { new Date ( meeting . dateFrom ) . toLocaleTimeString ( [ ] , { hour : '2-digit' , minute : '2-digit' } ) } { meeting . place }
148+ </ p >
149+ </ div >
150+ ) ) }
151+ </ div >
152+
153+ < p className = "text-2xl font-bold text-white text-left mt-9 mb-6" > Lernfortschritt</ p >
154+ { modules . map ( ( subject , index ) => (
155+ < div key = { index } className = "mb-6" >
156+ < p className = "text-m text-[#9B9B9B]" > { subject . name } </ p >
157+ < div onClick = { ( ) => openProgressModal ( subject . name ) } >
158+ < ProgressBar progress = { calculateProgress ( subject ) } />
159+ </ div >
160+ </ div >
161+ ) ) }
162+
163+ { isProgressModalOpen && activeModule && (
164+ < ModuleProgressSettings
165+ onClose = { closeProgressModal }
166+ module = { activeModule }
167+ allUserModules = { modules }
168+ />
169+ ) }
170+ { isExamModalOpen && (
171+ < ExamDateModal
172+ isOpen = { isExamModalOpen }
173+ modules = { modules }
174+ onClose = { closeExamModal }
175+ onSubmit = { handleAddExam }
176+ />
177+ ) }
178+ </ div >
179+ </ div >
180+ ) ;
181+ }
0 commit comments