Add an accommodations reservations module #188
@ -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>
|
||||||
)}
|
)}
|
||||||
|
Loading…
Reference in New Issue
Block a user