Can delete a reservation
This commit is contained in:
		| @@ -117,4 +117,14 @@ export class AccommodationsReservationsApi { | |||||||
|  |  | ||||||
|     return new AccommodationsReservationsList(data); |     return new AccommodationsReservationsList(data); | ||||||
|   } |   } | ||||||
|  |  | ||||||
|  |   /** | ||||||
|  |    * Delete a reservation | ||||||
|  |    */ | ||||||
|  |   static async Delete(r: AccommodationReservation): Promise<void> { | ||||||
|  |     await APIClient.exec({ | ||||||
|  |       method: "DELETE", | ||||||
|  |       uri: `/family/${r.family_id}/accommodations/reservation/${r.id}`, | ||||||
|  |     }); | ||||||
|  |   } | ||||||
| } | } | ||||||
|   | |||||||
| @@ -4,29 +4,44 @@ import dayGridPlugin from "@fullcalendar/daygrid"; | |||||||
| import interactionPlugin from "@fullcalendar/interaction"; | import interactionPlugin from "@fullcalendar/interaction"; | ||||||
| import listPlugin from "@fullcalendar/list"; | import listPlugin from "@fullcalendar/list"; | ||||||
| import FullCalendar from "@fullcalendar/react"; | import FullCalendar from "@fullcalendar/react"; | ||||||
|  | import DeleteIcon from "@mui/icons-material/Delete"; | ||||||
| import { | import { | ||||||
|   Alert, |   Alert, | ||||||
|  |   Avatar, | ||||||
|  |   Card, | ||||||
|  |   CardActions, | ||||||
|  |   CardContent, | ||||||
|  |   CardHeader, | ||||||
|   Checkbox, |   Checkbox, | ||||||
|   FormControl, |   FormControl, | ||||||
|   FormControlLabel, |   FormControlLabel, | ||||||
|   FormGroup, |   FormGroup, | ||||||
|   FormLabel, |   FormLabel, | ||||||
|  |   IconButton, | ||||||
|   Popover, |   Popover, | ||||||
|  |   Tooltip, | ||||||
|   Typography, |   Typography, | ||||||
|   fabClasses, |  | ||||||
| } from "@mui/material"; | } from "@mui/material"; | ||||||
|  | import { red } from "@mui/material/colors"; | ||||||
| import React from "react"; | import React from "react"; | ||||||
| import { FamilyApi, FamilyUser } from "../../../api/FamilyApi"; | import { FamilyApi, FamilyUser } from "../../../api/FamilyApi"; | ||||||
|  | import { Accommodation } from "../../../api/accommodations/AccommodationListApi"; | ||||||
| import { | import { | ||||||
|  |   AccommodationReservation, | ||||||
|   AccommodationsReservationsApi, |   AccommodationsReservationsApi, | ||||||
|   AccommodationsReservationsList, |   AccommodationsReservationsList, | ||||||
| } from "../../../api/accommodations/AccommodationsReservationsApi"; | } from "../../../api/accommodations/AccommodationsReservationsApi"; | ||||||
| import { useAlert } from "../../../hooks/context_providers/AlertDialogProvider"; | import { useAlert } from "../../../hooks/context_providers/AlertDialogProvider"; | ||||||
|  | import { useConfirm } from "../../../hooks/context_providers/ConfirmDialogProvider"; | ||||||
| import { useLoadingMessage } from "../../../hooks/context_providers/LoadingMessageProvider"; | import { useLoadingMessage } from "../../../hooks/context_providers/LoadingMessageProvider"; | ||||||
| import { useSnackbar } from "../../../hooks/context_providers/SnackbarProvider"; | import { useSnackbar } from "../../../hooks/context_providers/SnackbarProvider"; | ||||||
| import { useUpdateAccommodationReservation } from "../../../hooks/context_providers/accommodations/UpdateReservationDialogProvider"; | import { useUpdateAccommodationReservation } from "../../../hooks/context_providers/accommodations/UpdateReservationDialogProvider"; | ||||||
| import { fmtUnixDateFullCalendar } from "../../../utils/time_utils"; | import { | ||||||
|  |   fmtUnixDate, | ||||||
|  |   fmtUnixDateFullCalendar, | ||||||
|  | } from "../../../utils/time_utils"; | ||||||
| import { AsyncWidget } from "../../../widgets/AsyncWidget"; | import { AsyncWidget } from "../../../widgets/AsyncWidget"; | ||||||
|  | import { useUser } from "../../../widgets/BaseAuthenticatedPage"; | ||||||
| import { useFamily } from "../../../widgets/BaseFamilyRoute"; | import { useFamily } from "../../../widgets/BaseFamilyRoute"; | ||||||
| import { FamilyPageTitle } from "../../../widgets/FamilyPageTitle"; | import { FamilyPageTitle } from "../../../widgets/FamilyPageTitle"; | ||||||
| import { useAccommodations } from "../../../widgets/accommodations/BaseAccommodationsRoute"; | import { useAccommodations } from "../../../widgets/accommodations/BaseAccommodationsRoute"; | ||||||
| @@ -34,10 +49,12 @@ import { useAccommodations } from "../../../widgets/accommodations/BaseAccommoda | |||||||
| export function AccommodationsReservationsRoute(): React.ReactElement { | export function AccommodationsReservationsRoute(): React.ReactElement { | ||||||
|   const snackbar = useSnackbar(); |   const snackbar = useSnackbar(); | ||||||
|   const alert = useAlert(); |   const alert = useAlert(); | ||||||
|  |   const confirm = useConfirm(); | ||||||
|   const loadingMessage = useLoadingMessage(); |   const loadingMessage = useLoadingMessage(); | ||||||
|  |  | ||||||
|   const loadKey = React.useRef(1); |   const loadKey = React.useRef(1); | ||||||
|  |  | ||||||
|  |   const user = useUser(); | ||||||
|   const family = useFamily(); |   const family = useFamily(); | ||||||
|   const accommodations = useAccommodations(); |   const accommodations = useAccommodations(); | ||||||
|   const updateReservation = useUpdateAccommodationReservation(); |   const updateReservation = useUpdateAccommodationReservation(); | ||||||
| @@ -62,6 +79,10 @@ export function AccommodationsReservationsRoute(): React.ReactElement { | |||||||
|   const [activeEvent, setActiveEvent] = React.useState< |   const [activeEvent, setActiveEvent] = React.useState< | ||||||
|     | undefined |     | undefined | ||||||
|     | { |     | { | ||||||
|  |         user: FamilyUser; | ||||||
|  |         accommodation: Accommodation; | ||||||
|  |         reservation: AccommodationReservation; | ||||||
|  |  | ||||||
|         x: number; |         x: number; | ||||||
|         y: number; |         y: number; | ||||||
|         w: number; |         w: number; | ||||||
| @@ -127,9 +148,52 @@ export function AccommodationsReservationsRoute(): React.ReactElement { | |||||||
|   }; |   }; | ||||||
|  |  | ||||||
|   const onEventClick = (ev: EventClickArg) => { |   const onEventClick = (ev: EventClickArg) => { | ||||||
|  |     const id: number = ev.event.extendedProps.id; | ||||||
|  |     const resa = reservations?.get(id)!; | ||||||
|  |     const acc = accommodations.accommodations.get(resa.accommodation_id)!; | ||||||
|  |  | ||||||
|  |     const user = users?.find((u) => u.user_id === resa.user_id); | ||||||
|  |  | ||||||
|  |     if (!user) { | ||||||
|  |       console.error(`User ${resa.user_id} not found!`); | ||||||
|  |       return; | ||||||
|  |     } | ||||||
|  |  | ||||||
|     const loc = ev.el.getBoundingClientRect(); |     const loc = ev.el.getBoundingClientRect(); | ||||||
|     setActiveEvent({ x: loc.left, y: loc.top, w: loc.width, h: loc.height }); |     setActiveEvent({ | ||||||
|     console.log(ev); |       reservation: resa, | ||||||
|  |       accommodation: acc, | ||||||
|  |       user: user, | ||||||
|  |  | ||||||
|  |       x: loc.left, | ||||||
|  |       y: loc.top, | ||||||
|  |       w: loc.width, | ||||||
|  |       h: loc.height, | ||||||
|  |     }); | ||||||
|  |   }; | ||||||
|  |  | ||||||
|  |   const deleteReservation = async (r: AccommodationReservation) => { | ||||||
|  |     try { | ||||||
|  |       if ( | ||||||
|  |         !(await confirm( | ||||||
|  |           "Voulez-vous vraiment supprimer cette réservation ? L'opération n'est pas réversible !" | ||||||
|  |         )) | ||||||
|  |       ) | ||||||
|  |         return; | ||||||
|  |  | ||||||
|  |       setActiveEvent(undefined); | ||||||
|  |       loadingMessage.show("Suppression de la réservation en cours..."); | ||||||
|  |  | ||||||
|  |       await AccommodationsReservationsApi.Delete(r); | ||||||
|  |  | ||||||
|  |       reload(); | ||||||
|  |       snackbar("La réservation a été supprimée avec succès !"); | ||||||
|  |     } catch (e) { | ||||||
|  |       console.error("Failed to delete a reservation!", e); | ||||||
|  |       alert("Échec de la suppression de la réservation!"); | ||||||
|  |     } finally { | ||||||
|  |       loadingMessage.hide(); | ||||||
|  |     } | ||||||
|   }; |   }; | ||||||
|  |  | ||||||
|   return ( |   return ( | ||||||
| @@ -283,6 +347,9 @@ export function AccommodationsReservationsRoute(): React.ReactElement { | |||||||
|                         : r.validated === false |                         : r.validated === false | ||||||
|                         ? "red dotted" |                         ? "red dotted" | ||||||
|                         : "grey dotted", |                         : "grey dotted", | ||||||
|  |                     extendedProps: { | ||||||
|  |                       id: r.id, | ||||||
|  |                     }, | ||||||
|                   }; |                   }; | ||||||
|                 })} |                 })} | ||||||
|               /> |               /> | ||||||
| @@ -299,6 +366,7 @@ export function AccommodationsReservationsRoute(): React.ReactElement { | |||||||
|                 width: activeEvent?.w + "px", |                 width: activeEvent?.w + "px", | ||||||
|                 height: activeEvent?.h + "px", |                 height: activeEvent?.h + "px", | ||||||
|                 backgroundColor: "pink", |                 backgroundColor: "pink", | ||||||
|  |                 zIndex: 0, | ||||||
|               }} |               }} | ||||||
|             ></div> |             ></div> | ||||||
|             <Popover |             <Popover | ||||||
| @@ -312,7 +380,67 @@ export function AccommodationsReservationsRoute(): React.ReactElement { | |||||||
|                 horizontal: "left", |                 horizontal: "left", | ||||||
|               }} |               }} | ||||||
|             > |             > | ||||||
|               <Typography sx={{ p: 2 }}>The content of the Popover.</Typography> |               <Card sx={{ maxWidth: 345 }} elevation={6}> | ||||||
|  |                 <CardHeader | ||||||
|  |                   avatar={ | ||||||
|  |                     <Avatar sx={{ bgcolor: red[500] }}> | ||||||
|  |                       {activeEvent?.user.user_name | ||||||
|  |                         .substring(0, 1) | ||||||
|  |                         .toLocaleUpperCase()} | ||||||
|  |                     </Avatar> | ||||||
|  |                   } | ||||||
|  |                   title={activeEvent?.user.user_name} | ||||||
|  |                   subheader={activeEvent?.user.user_mail} | ||||||
|  |                 /> | ||||||
|  |  | ||||||
|  |                 <CardContent> | ||||||
|  |                   <Typography variant="body2" color="text.secondary"> | ||||||
|  |                     <p> | ||||||
|  |                       Réservation de {activeEvent?.accommodation.name} | ||||||
|  |                       <br /> | ||||||
|  |                       <em>{activeEvent?.accommodation.description}</em> | ||||||
|  |                     </p> | ||||||
|  |                     <p> | ||||||
|  |                       Du{" "} | ||||||
|  |                       {fmtUnixDate( | ||||||
|  |                         activeEvent?.reservation.reservation_start ?? 0 | ||||||
|  |                       )}{" "} | ||||||
|  |                       <br /> | ||||||
|  |                       Au{" "} | ||||||
|  |                       {fmtUnixDate( | ||||||
|  |                         activeEvent?.reservation.reservation_end ?? 0 | ||||||
|  |                       )} | ||||||
|  |                     </p> | ||||||
|  |                     <p> | ||||||
|  |                       <strong> | ||||||
|  |                         {activeEvent?.reservation.validated === false ? ( | ||||||
|  |                           <span style={{ color: "#f44336" }}>Refusée</span> | ||||||
|  |                         ) : activeEvent?.reservation.validated === true ? ( | ||||||
|  |                           <span style={{ color: "#66bb6a" }}>Validée</span> | ||||||
|  |                         ) : ( | ||||||
|  |                           <span style={{ color: "#29b6f6" }}> | ||||||
|  |                             En attente de validation | ||||||
|  |                           </span> | ||||||
|  |                         )} | ||||||
|  |                       </strong> | ||||||
|  |                     </p> | ||||||
|  |                   </Typography> | ||||||
|  |                 </CardContent> | ||||||
|  |                 <CardActions disableSpacing> | ||||||
|  |                   {user.user.id === activeEvent?.reservation.user_id && ( | ||||||
|  |                     <Tooltip title="Supprimer la réservation" arrow> | ||||||
|  |                       <IconButton | ||||||
|  |                         color="error" | ||||||
|  |                         onClick={() => | ||||||
|  |                           deleteReservation(activeEvent?.reservation) | ||||||
|  |                         } | ||||||
|  |                       > | ||||||
|  |                         <DeleteIcon /> | ||||||
|  |                       </IconButton> | ||||||
|  |                     </Tooltip> | ||||||
|  |                   )} | ||||||
|  |                 </CardActions> | ||||||
|  |               </Card> | ||||||
|             </Popover> |             </Popover> | ||||||
|           </div> |           </div> | ||||||
|         )} |         )} | ||||||
|   | |||||||
		Reference in New Issue
	
	Block a user