Skip to content

Commit 21a0e7b

Browse files
committed
updated joinstudygroup functionality
1 parent 2ad0128 commit 21a0e7b

File tree

5 files changed

+196
-65
lines changed

5 files changed

+196
-65
lines changed

src/api/MeetingApi.tsx

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -45,3 +45,11 @@ export function getMeetingsForModule(axios: AxiosInstance, module: string): Prom
4545
});
4646
}
4747

48+
export function updateCreator(axios: AxiosInstance, meetingId: string, newCreatorId: string): Promise<CreateMeetingDto> {
49+
const meetingUpdate = { creator: newCreatorId };
50+
51+
return axios.put(`/${Resources.MEETING}?id=${meetingId}`, meetingUpdate)
52+
.then(handleSuccessResponse, handleErrorResponse);
53+
}
54+
55+

src/api/UserGroupApi.tsx

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,3 +16,11 @@ export async function getUserIdsForMeeting(axios: AxiosInstance, uuid: string):
1616
const userIds = data.map((entry: { userId: string }) => entry.userId);
1717
return userIds;
1818
}
19+
20+
21+
22+
export function leaveStudyGroup(axios: AxiosInstance, targetUUID: string): Promise<void> {
23+
return axios.delete(`/${Resources.USERGROUP}`, {
24+
params: { targetUUID } })
25+
.then(handleSuccessResponse, handleErrorResponse);
26+
}

src/components/meeting/MeetingDetails.tsx

Lines changed: 32 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,8 @@ import { CuteButton } from "../CuteButton";
44
import { MeetingDto } from "../../dtos/MeetingDto";
55
import { getUserIdsForMeeting } from "../../api/UserGroupApi";
66
import axiosInstance from "../../AxiosConfig";
7-
import {joinStudyGroup} from "../../api/UserGroupApi"
7+
import { getUser } from "../../api/UserApi";
8+
import { UserDto } from "../../dtos/UserDto";
89

910
interface ModalProps {
1011
isOpen: boolean;
@@ -15,8 +16,19 @@ interface ModalProps {
1516
}
1617

1718
const MeetingDetails: React.FC<ModalProps> = ({ isOpen, meeting, onClose, openMeetingForm, openChooseMeetingModal }) => {
18-
1919
const [userIds, setUserIds] = useState<string[]>([]);
20+
const [myUser, setMyUser] = useState<UserDto | null>(null);
21+
const myUserId = myUser ? myUser.uuid : "";
22+
23+
useEffect(() => {
24+
if (isOpen) {
25+
getUser(axiosInstance)
26+
.then(setMyUser)
27+
.catch(err => {
28+
console.error("Fehler beim Laden des Nutzers:", err);
29+
});
30+
}
31+
}, [isOpen]);
2032

2133
useEffect(() => {
2234
if (isOpen && meeting) {
@@ -28,55 +40,51 @@ const MeetingDetails: React.FC<ModalProps> = ({ isOpen, meeting, onClose, openMe
2840
}
2941
}, [isOpen, meeting]);
3042

31-
if (!isOpen || !meeting) return null;
3243

33-
const updateMeeting = () => {
44+
const updateMeeting = () => {
3445
onClose();
35-
if (meeting.repeatable !== "NEVER")
36-
openChooseMeetingModal();
37-
else
38-
openMeetingForm();
39-
}
40-
41-
const joinMeeting = () => {
46+
if (meeting?.repeatable !== "NEVER") openChooseMeetingModal();
47+
else openMeetingForm();
48+
};
4249

43-
joinStudyGroup(axiosInstance, meeting.id)
44-
45-
}
50+
if (!isOpen || !meeting || !myUser) return null;
51+
if (!isOpen || !meeting || !myUser) return null;
4652

4753
return (
4854
<div className="modal-overlay" onClick={onClose}>
4955
<div className="modal-content max-w-[90%] w-[700px] relative p-7" onClick={(e) => e.stopPropagation()}>
50-
5156
<button
5257
onClick={onClose}
5358
className="absolute top-4 right-4 text-white text-xxl hover:text-red-400"
5459
>
5560
×
5661
</button>
5762

58-
<h2 className="font-bold text-2xl text-white mb-4">{meeting.title}</h2>
63+
<h2 className="font-bold text-2xl text-white mb-4">{meeting?.title}</h2>
5964

6065
<div className="flex flex-col gap-4 mb-4">
6166
<p className="text-bs font-medium text-white">
62-
<strong className="text-[#CAE8FF] font-semibold">Start:</strong> {meeting.dateFrom}
67+
<strong className="text-[#CAE8FF] font-semibold">Start:</strong> {meeting?.dateFrom}
6368
</p>
6469
<p className="text-bs font-medium text-white">
65-
<strong className="text-[#CAE8FF] font-semibold">Ende:</strong> {meeting.dateUntil}
70+
<strong className="text-[#CAE8FF] font-semibold">Ende:</strong> {meeting?.dateUntil}
6671
</p>
6772
<p className="text-bs font-medium text-white">
68-
<strong className="text-[#CAE8FF] font-semibold">Beschreibung:</strong> {meeting.description}
73+
<strong className="text-[#CAE8FF] font-semibold">Beschreibung:</strong> {meeting?.description}
6974
</p>
7075
<p className="text-bs font-medium text-white">
71-
<strong className="text-[#CAE8FF] font-semibold">Raum:</strong> {meeting.place}
76+
<strong className="text-[#CAE8FF] font-semibold">Raum:</strong> {meeting?.place}
7277
</p>
7378

7479
<p className="text-bs font-medium text-white">
7580
<strong className="text-[#CAE8FF] font-semibold">Teilnehmer:</strong>
7681
</p>
7782
<ul className="text-white text-sm list-disc list-inside">
78-
{userIds.map(id => (<li key={id}>{id}</li>))}
79-
{userIds.length === 0 && <li>(noch keine Teilnehmer)</li>}
83+
<li key={meeting?.creator}>{meeting?.creator}</li>
84+
{userIds.map(id => (
85+
<li key={id}>{id}</li>
86+
))}
87+
{userIds.length === 0 && <li>-</li>}
8088
</ul>
8189
</div>
8290

@@ -85,22 +93,13 @@ const MeetingDetails: React.FC<ModalProps> = ({ isOpen, meeting, onClose, openMe
8593
<CuteButton
8694
onClick={updateMeeting}
8795
text={"Meeting bearbeiten"}
88-
bgColor={"#598BB1"}
96+
bgColor={"#56A095"}
8997
textColor={"#e6ebfc"}
9098
classname={"md:text-base text-sm"}
9199
/>
92100
</div>
93-
<div>
94-
<CuteButton
95-
onClick={joinMeeting}
96-
text={"Beitreten"}
97-
bgColor={"#56A095"}
98-
textColor={"#e8fcf6"}
99-
classname={"md:text-lg text-base"}
100-
/>
101-
</div>
101+
102102
</div>
103-
104103
</div>
105104
</div>
106105
);
Lines changed: 143 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -1,40 +1,152 @@
1-
import {MeetingDto} from "../../dtos/MeetingDto";
2-
import {CuteButton} from "../CuteButton";
3-
import React from "react";
1+
import React, { useEffect, useState } from 'react';
2+
import { MeetingDto } from "../../dtos/MeetingDto";
3+
import { UserDto } from "../../dtos/UserDto";
4+
import { getUser } from "../../api/UserApi";
5+
import { getUserIdsForMeeting, joinStudyGroup, leaveStudyGroup } from "../../api/UserGroupApi";
6+
import axiosInstance from "../../AxiosConfig";
7+
import { CuteButton } from "../CuteButton";
8+
import { deleteMeeting, updateCreator } from '../../api/MeetingApi';
9+
10+
interface Props {
11+
meeting: MeetingDto;
12+
}
13+
14+
export default function MeetingSearchResult({ meeting }: Props) {
15+
const [userIds, setUserIds] = useState<string[]>([]);
16+
const [myUser, setMyUser] = useState<UserDto | null>(null);
17+
const [loading, setLoading] = useState<boolean>(false);
18+
19+
useEffect(() => {
20+
getUser(axiosInstance)
21+
.then(setMyUser)
22+
.catch(err => console.error(err));
23+
}, []);
24+
25+
useEffect(() => {
26+
getUserIdsForMeeting(axiosInstance, meeting.id)
27+
.then(setUserIds)
28+
.catch(err => console.error(err));
29+
}, [meeting.id]);
30+
31+
if (!myUser) return null;
32+
33+
const myUserId = myUser.uuid;
34+
35+
const joinMeeting = () => {
36+
setLoading(true);
37+
joinStudyGroup(axiosInstance, meeting.id)
38+
.then(() => setUserIds(prev => [...prev, myUserId]))
39+
.finally(() => setLoading(false));
40+
};
41+
42+
const leaveMeeting = () => {
43+
setLoading(true);
44+
leaveStudyGroup(axiosInstance, meeting.id)
45+
.then(() => setUserIds(prev => prev.filter(id => id !== myUserId)))
46+
.finally(() => setLoading(false));
47+
};
48+
49+
const leaveMeetingAsCreator = () => {
50+
if (userIds.length === 0) {
51+
deleteMeeting(axiosInstance, meeting.id)
52+
.then(() => console.log("Meeting gelöscht"))
53+
.catch(err => console.error(err));
54+
} else {
55+
const newCreatorId = userIds[0];
56+
updateCreator(axiosInstance, meeting.id, newCreatorId)
57+
.then(() => console.log("Creator geändert"))
58+
.catch(err => console.error(err));
59+
}
60+
};
61+
62+
const isMember = userIds.includes(myUserId);
63+
const isCreator = meeting.creator === myUserId;
464

5-
export default function MeetingSearchResult(props: { meeting: MeetingDto }) {
665
return (
766
<div className="bg-[#333C4F] p-4 flex flex-col gap-4">
867
<div>
9-
<div className="text-white">
10-
{new Date(props.meeting.dateFrom).toLocaleString('de-DE', {
11-
day: '2-digit',
12-
month: '2-digit',
13-
year: 'numeric',
14-
hour: '2-digit',
15-
minute: '2-digit',
16-
})}
17-
{" - "}
18-
{new Date(props.meeting.dateUntil).toLocaleString('de-DE', {
19-
day: '2-digit',
20-
month: '2-digit',
21-
year: 'numeric',
22-
hour: '2-digit',
23-
minute: '2-digit',
24-
})}
25-
</div>
26-
<div className="text-white space-y-1">
27-
<p>{props.meeting.description}</p>
28-
<p>{props.meeting.member}</p>
29-
<p>{props.meeting.place}</p>
68+
<h2 className="font-bold text-2xl text-white mb-4">{meeting.title}</h2>
69+
70+
<div className="flex flex-col gap-4 mb-4">
71+
<p className="text-bs font-medium text-white">
72+
<strong className="text-[#CAE8FF] font-semibold">Start: </strong>
73+
{new Date(meeting.dateFrom).toLocaleString('de-DE', {
74+
day: '2-digit',
75+
month: '2-digit',
76+
year: 'numeric',
77+
hour: '2-digit',
78+
minute: '2-digit',
79+
})}
80+
</p>
81+
<p className="text-bs font-medium text-white">
82+
<strong className="text-[#CAE8FF] font-semibold">Ende: </strong>
83+
{new Date(meeting.dateUntil).toLocaleString('de-DE', {
84+
day: '2-digit',
85+
month: '2-digit',
86+
year: 'numeric',
87+
hour: '2-digit',
88+
minute: '2-digit',
89+
})}
90+
</p>
91+
<p className="text-bs font-medium text-white">
92+
<strong className="text-[#CAE8FF] font-semibold">Beschreibung:</strong> {meeting.description}
93+
</p>
94+
<p className="text-bs font-medium text-white">
95+
<strong className="text-[#CAE8FF] font-semibold">Raum:</strong> {meeting.place}
96+
</p>
97+
98+
<p className="text-bs font-medium text-white">
99+
<strong className="text-[#CAE8FF] font-semibold">Teilnehmer:</strong>
100+
</p>
101+
<ul className="text-white text-sm list-disc list-inside">
102+
<li key={meeting.creator}>{meeting.creator}</li>
103+
{userIds.map(id => (
104+
<li key={id}>{id}</li>
105+
))}
106+
{userIds.length === 0 && <li>-</li>}
107+
</ul>
30108
</div>
31109
</div>
32-
<div className="flex flex-col">
33-
<CuteButton onClick={() => {
34-
}} text={"Teilnehmen"} textColor={"#e8fcf6"}
35-
bgColor={"#56A095"}
36-
classname={"text-sm"}/>
110+
111+
<div className="flex gap-4">
112+
{!isMember && !isCreator && !loading && (
113+
<CuteButton
114+
onClick={joinMeeting}
115+
text={"Teilnehmen"}
116+
textColor={"#e8fcf6"}
117+
bgColor={"#56A095"}
118+
classname={"text-sm w-full"}
119+
/>
120+
)}
121+
{loading && (
122+
<CuteButton
123+
text={"Lade..."}
124+
textColor={"#e8fcf6"}
125+
bgColor={"#56A095"}
126+
classname={"text-sm w-full"}
127+
/>
128+
)}
129+
130+
{isCreator && !loading && (
131+
<CuteButton
132+
onClick={leaveMeetingAsCreator}
133+
text={"Meeting verlassen"}
134+
textColor={"#e8fcf6"}
135+
bgColor={"#974242"}
136+
classname={"text-sm w-full"}
137+
/>
138+
)}
139+
140+
{isMember && !isCreator && !loading && (
141+
<CuteButton
142+
onClick={leaveMeeting}
143+
text={"Meeting verlassen"}
144+
textColor={"#e8fcf6"}
145+
bgColor={"#974242"}
146+
classname={"text-sm w-full"}
147+
/>
148+
)}
37149
</div>
38150
</div>
39-
)
151+
);
40152
}

src/styles/Modal.css

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,14 +11,18 @@
1111
z-index: 9999;
1212
}
1313

14+
1415
.modal-content {
1516
background-color: #1C212C;
16-
padding: 50px;
17+
padding: 40px;
18+
padding-bottom: 20px;
1719
border-radius: 8px;
1820
box-shadow: 0 2px 10px rgba(0, 0, 0, 0.1);
1921
display: flex;
2022
flex-direction: column;
2123
gap: 20px;
24+
z-index: 10000;
25+
2226
}
2327

2428
.checkbox-group {

0 commit comments

Comments
 (0)