Skip to content

Commit 95bf204

Browse files
author
Alexandra Zwinger
committed
Make Meeting edit-able & delete-able
1 parent 2c89104 commit 95bf204

File tree

5 files changed

+108
-70
lines changed

5 files changed

+108
-70
lines changed

src/api/MeetingApi.tsx

Lines changed: 14 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,15 +1,25 @@
1-
import {MeetingDto} from "../dtos/MeetingDto";
2-
import { AxiosInstance } from "axios";
1+
import {CreateMeetingDto, MeetingDto} from "../dtos/MeetingDto";
2+
import {AxiosInstance} from "axios";
33
import {handleErrorResponse, handleSuccessResponse} from "./ErrorHandling";
44
import {Resources} from "../App";
55

6-
export function createMeeting(axios: AxiosInstance, meetingDto: MeetingDto): Promise<MeetingDto> {
6+
export function createMeeting(axios: AxiosInstance, meetingDto: CreateMeetingDto): Promise<CreateMeetingDto> {
77
return axios.post(`/${Resources.MEETING}`, meetingDto)
88
.then(handleSuccessResponse, handleErrorResponse);
99
}
1010

11+
export function updateMeeting(axios: AxiosInstance, meetingId: number, meetingDto: CreateMeetingDto): Promise<CreateMeetingDto> {
12+
return axios.put(`/${Resources.MEETING}?id=${meetingId}`, meetingDto)
13+
.then(handleSuccessResponse, handleErrorResponse);
14+
}
15+
16+
export function deleteMeeting(axios: AxiosInstance, meetingId: number): Promise<CreateMeetingDto> {
17+
return axios.delete(`/${Resources.MEETING}?id=${meetingId}`)
18+
.then(handleSuccessResponse, handleErrorResponse);
19+
}
20+
1121
export function getMeetings(axios: AxiosInstance): Promise<MeetingDto[]> {
12-
const url = "/meeting";
22+
const url = `/${Resources.MEETING}`;
1323
return axios.get(url)
1424
.then(handleSuccessResponse, handleErrorResponse)
1525
.then((listRes) => {

src/components/CalendarComponent.tsx

Lines changed: 15 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -8,23 +8,27 @@ import {useTheme} from '@mui/material/styles';
88
import useMediaQuery from '@mui/material/useMediaQuery';
99
import {getMeetings} from "../api/MeetingApi";
1010
import axiosInstance from "../AxiosConfig";
11+
import {MeetingForm} from "../form/MeetingForm";
1112

1213
export default function CalendarComponent(props: { isDialogOpen: boolean }) {
1314
const [selectedMeeting, setSelectedMeeting] = useState<MeetingDto | null>(null);
1415
const [isModalOpen, setIsModalOpen] = useState(false);
16+
const [isMeetingFormOpen, setIsMeetingFormOpen] = useState(false);
1517
const [events, setEvents] = useState<any[]>([]);
1618
const mdAndUp = useMediaQuery(useTheme().breakpoints.up('md'));
1719

1820
const fetchMeetings = async () => {
1921
try {
2022
const response = await getMeetings(axiosInstance);
2123

22-
setEvents(response.map(({title, date_from, date_until, description, place}: any) => ({
24+
setEvents(response.map(({id, title, date_from, date_until, description, place, repeatable}: MeetingDto) => ({
25+
id,
2326
title,
2427
start: new Date(date_from).toISOString(),
2528
end: new Date(date_until).toISOString(),
2629
description,
2730
room: place,
31+
repeatable: repeatable
2832
})));
2933
} catch (error) {
3034
alert(error);
@@ -33,22 +37,25 @@ export default function CalendarComponent(props: { isDialogOpen: boolean }) {
3337

3438
useEffect(() => {
3539
fetchMeetings();
36-
}, [props.isDialogOpen]);
40+
}, [props.isDialogOpen, isMeetingFormOpen]);
3741

3842
const Eventhandler = (info: any) => {
3943
const {event} = info;
44+
const id = event.id;
4045
const title = event.title;
4146
const date_from = new Date(event.start).toLocaleString();
4247
const date_until = new Date(event.end).toLocaleString();
4348
const description = event.extendedProps?.description || '';
4449
const place = event.extendedProps?.room || '';
50+
const repeatable = event.extendedProps?.repeatable || 'never';
4551
setSelectedMeeting({
52+
id,
4653
title,
4754
date_from,
4855
date_until,
4956
description,
5057
place,
51-
repeatable: 'false',
58+
repeatable: repeatable,
5259
});
5360
setIsModalOpen(true);
5461
};
@@ -92,8 +99,13 @@ export default function CalendarComponent(props: { isDialogOpen: boolean }) {
9299
isOpen={isModalOpen}
93100
meeting={selectedMeeting}
94101
onClose={closeModal}
102+
setIsMeetingFormOpen={setIsMeetingFormOpen}
95103
/>
96104
)}
105+
{selectedMeeting && isMeetingFormOpen &&
106+
<MeetingForm open={true} onClose={() => setIsMeetingFormOpen(false)} meeting={selectedMeeting}/>
107+
}
108+
97109
</div>
98110
);
99111
}

src/components/Modal.tsx

Lines changed: 23 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,23 +1,24 @@
11
import React from 'react';
22
import '../styles/Modal.css';
3-
import { CuteButton } from "./CuteButton";
4-
import { MeetingDto } from "../dtos/MeetingDto";
3+
import {CuteButton} from "./CuteButton";
4+
import {CreateMeetingDto} from "../dtos/MeetingDto";
55

66
interface ModalProps {
77
isOpen: boolean;
8-
meeting: MeetingDto | null;
8+
meeting: CreateMeetingDto | null;
99
onClose: () => void;
10+
setIsMeetingFormOpen: React.Dispatch<React.SetStateAction<boolean>>;
1011
}
1112

12-
const Modal: React.FC<ModalProps> = ({ isOpen, meeting, onClose }) => {
13-
if (!isOpen || !meeting) return null;
13+
const Modal: React.FC<ModalProps> = ({isOpen, meeting, onClose, setIsMeetingFormOpen}) => {
14+
if (!isOpen || !meeting) return null;
1415
return (
1516
<div className="modal-overlay" onClick={onClose}>
16-
<div className="modal-content" onClick={(e) => e.stopPropagation()}>
17+
<div className="modal-content max-w-[90%] w-[450px]" onClick={(e) => e.stopPropagation()}>
1718

1819
<h2 className="font-bold text-2xl text-white mb-4">{meeting.title}</h2>
1920

20-
<div className="flex flex-wrap gap-4 mb-4">
21+
<div className="flex flex-col gap-4 mb-4">
2122
<p className="text-bs font-medium text-white">
2223
<strong className="text-[#CAE8FF] font-semibold">Start:</strong> {meeting.date_from}
2324
</p>
@@ -32,8 +33,21 @@ const Modal: React.FC<ModalProps> = ({ isOpen, meeting, onClose }) => {
3233
</p>
3334
</div>
3435

35-
<div className="mt-auto flex justify-end">
36-
<CuteButton onClick={onClose} text={"Schließen"} textColor={"#CAE8FF"} bgColor={"#425E74"} classname={"text-base"} />
36+
<div className="flex flex-row gap-4 mb-4 justify-end mt-auto">
37+
<div>
38+
<CuteButton onClick={onClose} text={"Abbrechen"} textColor={"#CAE8FF"} bgColor={"#425E74"}
39+
classname={"text-base"}/>
40+
</div>
41+
<div>
42+
<CuteButton
43+
onClick={() => {
44+
onClose();
45+
setIsMeetingFormOpen(true)
46+
}}
47+
text={"Meeting Bearbeiten"} textColor={"#e3f1ef"}
48+
bgColor={"#506D69"}
49+
classname={"text-base"}/>
50+
</div>
3751
</div>
3852

3953
</div>

src/dtos/MeetingDto.tsx

Lines changed: 10 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
export type MeetingDto = {
1+
export type CreateMeetingDto = {
22
title: string,
33
description: string,
44
date_from: string,
@@ -7,19 +7,12 @@ export type MeetingDto = {
77
place: string
88
}
99

10-
export interface Meeting {
11-
title: string;
12-
start: string;
13-
end: string;
14-
description: string;
15-
room: string;
16-
}
17-
18-
export const defaultMeetingDto: MeetingDto = ({
19-
title: "",
20-
description: "",
21-
date_from: "",
22-
date_until: "",
23-
repeatable: "",
24-
place: ""
25-
});
10+
export type MeetingDto = {
11+
id: number,
12+
title: string,
13+
description: string,
14+
date_from: string,
15+
date_until: string,
16+
repeatable: string,
17+
place: string
18+
}

src/form/MeetingForm.tsx

Lines changed: 46 additions & 37 deletions
Original file line numberDiff line numberDiff line change
@@ -10,40 +10,31 @@ import {Dayjs} from 'dayjs';
1010
import dayjs from 'dayjs';
1111
import {AdapterDayjs} from '@mui/x-date-pickers/AdapterDayjs';
1212
import {deDE} from "@mui/x-date-pickers/locales";
13-
import {createMeeting} from "../api/MeetingApi";
14-
import {useForm} from "@pankod/refine-react-hook-form";
15-
import {defaultMeetingDto, MeetingDto} from "../dtos/MeetingDto";
16-
import {HttpError} from "@refinedev/core";
13+
import {createMeeting, deleteMeeting, updateMeeting} from "../api/MeetingApi";
14+
import {CreateMeetingDto, MeetingDto} from "../dtos/MeetingDto";
1715
import axiosInstance from "../AxiosConfig";
1816

1917
interface MeetingFormProps {
2018
open: boolean;
2119
onClose: () => void;
20+
meeting?: MeetingDto;
2221
}
2322

2423
dayjs.locale('de');
2524

26-
export function MeetingForm({open, onClose}: MeetingFormProps) {
25+
export function MeetingForm({open, onClose, meeting}: MeetingFormProps) {
26+
const dateFrom = dayjs(meeting?.date_from, "D.M.YYYY, H:mm:ss");
27+
const dateUntil = dayjs(meeting?.date_until, "D.M.YYYY, H:mm:ss");
2728

28-
const [meetingTitle, setMeetingTitle] = useState('');
29-
const [repeatable, setRepeatable] = useState('never');
30-
const [meetingDescription, setMeetingDescription] = useState('');
31-
const [meetingRoom, setMeetingRoom] = useState('');
32-
const [date1, setDate1] = useState<Dayjs>(dayjs());
33-
const [time1, setTime1] = useState<Dayjs>(dayjs().hour(12).minute(0).second(0));
34-
const [date2, setDate2] = useState<Dayjs>(dayjs());
35-
const [time2, setTime2] = useState<Dayjs>(dayjs().hour(12).minute(0).second(0));
29+
const [meetingTitle, setMeetingTitle] = useState(meeting ? meeting.title : "");
30+
const [repeatable, setRepeatable] = useState(meeting ? meeting.repeatable : "never");
31+
const [meetingDescription, setMeetingDescription] = useState(meeting ? meeting.description : '');
32+
const [meetingRoom, setMeetingRoom] = useState(meeting ? meeting.place : '');
33+
const [date1, setDate1] = useState<Dayjs>(meeting ? dateFrom : dayjs());
34+
const [time1, setTime1] = useState<Dayjs>(meeting ? dateFrom : dayjs().hour(12).minute(0).second(0));
35+
const [date2, setDate2] = useState<Dayjs>(meeting ? dateUntil : dayjs());
36+
const [time2, setTime2] = useState<Dayjs>(meeting ? dateUntil : dayjs().hour(13).minute(0).second(0));
3637

37-
const methods = useForm<MeetingDto, HttpError>({
38-
refineCoreProps: {
39-
redirect: "show",
40-
},
41-
defaultValues: defaultMeetingDto
42-
});
43-
const {
44-
handleSubmit,
45-
refineCore: {onFinish},
46-
} = methods;
4738

4839
const handleDate1Change = (newDate: Dayjs | null) => {
4940
if (newDate) {
@@ -60,7 +51,7 @@ export function MeetingForm({open, onClose}: MeetingFormProps) {
6051
const handleTime1Change = (newTime: Dayjs | null) => {
6152
if (newTime) {
6253
setTime1(newTime);
63-
setTime2(newTime);
54+
setTime2(newTime.add(1, 'hour'));
6455
}
6556
};
6657

@@ -74,14 +65,18 @@ export function MeetingForm({open, onClose}: MeetingFormProps) {
7465
setRepeatable(event.target.value);
7566
};
7667

77-
const handleClick = async () => {
78-
await handleSubmit(async (values) => {
79-
await onFinish({
80-
...values,
81-
});
82-
})();
68+
const handleDelete = async () => {
69+
try {
70+
await deleteMeeting(axiosInstance, meeting!!.id);
71+
onClose();
72+
} catch (error) {
73+
console.error('Fehler:', error);
74+
alert('Es gab einen Fehler beim Löschen des Meetings.');
75+
}
76+
}
8377

84-
const meetingData: MeetingDto = {
78+
const handleSave = async () => {
79+
const meetingData: CreateMeetingDto = {
8580
title: meetingTitle,
8681
description: meetingDescription,
8782
place: meetingRoom,
@@ -91,7 +86,11 @@ export function MeetingForm({open, onClose}: MeetingFormProps) {
9186
};
9287

9388
try {
94-
await createMeeting(axiosInstance, meetingData);
89+
if (meeting) {
90+
await updateMeeting(axiosInstance, meeting.id, meetingData);
91+
} else {
92+
await createMeeting(axiosInstance, meetingData);
93+
}
9594
onClose();
9695
} catch (error) {
9796
console.error('Fehler:', error);
@@ -133,8 +132,9 @@ export function MeetingForm({open, onClose}: MeetingFormProps) {
133132
className="font-semibold block text-lg text-white">Beschreibung</label>
134133
<textarea
135134
id="meeting-description"
135+
rows={3}
136136
placeholder="Hier könnte Ihre Beschreibung stehen"
137-
className="ml-5 mt-1 text-gray-300 block bg-[#333C4F] w-11/12 px-10 mb-4 border rounded-full shadow-sm border-[#333C4F] placeholder-gray-400 placeholder:text-xs placeholder:py-1 py-2"
137+
className="ml-5 resize-none mt-1 text-gray-300 block bg-[#333C4F] w-11/12 px-10 mb-4 border rounded-full shadow-sm border-[#333C4F] placeholder-gray-400 placeholder:text-xs placeholder:py-1 py-2"
138138
value={meetingDescription}
139139
onChange={(e) => setMeetingDescription(e.target.value)}
140140
/>
@@ -281,10 +281,19 @@ export function MeetingForm({open, onClose}: MeetingFormProps) {
281281
</form>
282282
</DialogContent>
283283
<DialogActions>
284-
<CuteButton onClick={onClose} text={"Abbrechen"} textColor={"#CAE8FF"} bgColor={"#425E74"}
285-
classname={"text-base"}/>
286-
<CuteButton onClick={handleClick} text={"Speichern"} textColor={"#e3f1ef"} bgColor={"#506D69"}
287-
classname={"text-2xl"}/>
284+
<div className={"flex flex-row gap-4 w-full items-center"}>
285+
{meeting && (
286+
<CuteButton onClick={handleDelete} text={"Meeting Löschen"} textColor={"#f2f2f2"}
287+
bgColor={"#974242"}
288+
classname={"text-xl"}/>
289+
)}
290+
<div className={"gap-2 ml-auto flex items-center"}>
291+
<CuteButton onClick={onClose} text={"Abbrechen"} textColor={"#CAE8FF"} bgColor={"#425E74"}
292+
classname={"text-base"}/>
293+
<CuteButton onClick={handleSave} text={"Speichern"} textColor={"#e3f1ef"} bgColor={"#506D69"}
294+
classname={"text-2xl"}/>
295+
</div>
296+
</div>
288297
</DialogActions>
289298
</Dialog>
290299
}

0 commit comments

Comments
 (0)