Add an accommodations reservations module #188

Merged
pierre merged 81 commits from accomodation_module into master 2024-06-22 21:30:26 +00:00
6 changed files with 177 additions and 3 deletions
Showing only changes of commit b8a7401309 - Show all commits

View File

@ -45,6 +45,10 @@ export class AccommodationsList {
return this.list.filter(predicate); return this.list.filter(predicate);
} }
get openToReservationList(): Accommodation[] {
return this.filter((a) => a.open_to_reservations);
}
get(id: number): Accommodation | undefined { get(id: number): Accommodation | undefined {
return this.map.get(id); return this.map.get(id);
} }

View File

@ -1,6 +1,5 @@
import { APIClient } from "../ApiClient"; import { APIClient } from "../ApiClient";
import { Family } from "../FamilyApi"; import { Family } from "../FamilyApi";
import moment from "moment";
export interface AccommodationReservation { export interface AccommodationReservation {
id: number; id: number;
@ -56,6 +55,12 @@ export class AccommodationsReservationsList {
} }
} }
export interface UpdateAccommodationReservation {
start: number;
end: number;
accommodation_id: number;
}
export class AccommodationsReservationsApi { export class AccommodationsReservationsApi {
/** /**
* Get the entire list of accommodations of a family * Get the entire list of accommodations of a family

View File

@ -0,0 +1,86 @@
import {
Button,
Dialog,
DialogActions,
DialogContent,
DialogTitle,
} from "@mui/material";
import React from "react";
import { UpdateAccommodationReservation } from "../../api/accommodations/AccommodationsReservationsApi";
import { useAccommodations } from "../../widgets/accommodations/BaseAccommodationsRoute";
import { PropSelect } from "../../widgets/forms/PropSelect";
export function UpdateReservationDialog(p: {
open: boolean;
create: boolean;
reservation?: UpdateAccommodationReservation;
onClose: () => void;
onSubmitted: (c: UpdateAccommodationReservation) => void;
}): React.ReactElement {
const accommodations = useAccommodations();
const [reservation, setReservation] = React.useState<
UpdateAccommodationReservation | undefined
>();
const clearForm = () => {
setReservation(undefined);
};
const cancel = () => {
clearForm();
p.onClose();
};
const submit = async () => {
clearForm();
p.onSubmitted(reservation!);
};
React.useEffect(() => {
if (!reservation) setReservation(p.reservation);
}, [p.open, p.reservation]);
return (
<Dialog open={p.open} onClose={cancel}>
<DialogTitle>
{p.create ? "Création" : "Mise à jour"} d'une réservation
</DialogTitle>
<DialogContent style={{ display: "flex", flexDirection: "column" }}>
<PropSelect
editing={p.create}
label="Logement ciblé"
onValueChange={(v) => {
setReservation((a) => {
return {
...a!,
accommodation_id: Number(v),
};
});
}}
options={accommodations.accommodations.openToReservationList.map(
(a) => {
return { label: a.name, value: a.id.toString() };
}
)}
value={reservation?.accommodation_id?.toString()}
/>
{/* TODO : la suite */}
</DialogContent>
<DialogActions>
<Button onClick={cancel}>Annuler</Button>
<Button
onClick={submit}
disabled={
(reservation?.accommodation_id ?? -1) > 0 &&
(reservation?.start ?? -1) > 0 &&
(reservation?.end ?? -1) > (reservation?.start ?? 0)
}
>
{p.create ? "Créer" : "Mettre à jour"}
</Button>
</DialogActions>
</Dialog>
);
}

View File

@ -0,0 +1,64 @@
import React, { PropsWithChildren } from "react";
import { UpdateAccommodationReservation } from "../../../api/accommodations/AccommodationsReservationsApi";
import { UpdateReservationDialog } from "../../../dialogs/accommodations/UpdateReservationDialog";
type DialogContext = (
reservation: UpdateAccommodationReservation,
create: boolean
) => Promise<UpdateAccommodationReservation | undefined>;
const DialogContextK = React.createContext<DialogContext | null>(null);
export function UpdateReservationDialogProvider(
p: PropsWithChildren
): React.ReactElement {
const [open, setOpen] = React.useState(false);
const [reservation, setReservation] = React.useState<
UpdateAccommodationReservation | undefined
>(undefined);
const [create, setCreate] = React.useState(false);
const cb = React.useRef<
null | ((a: UpdateAccommodationReservation | undefined) => void)
>(null);
const handleClose = (res?: UpdateAccommodationReservation) => {
setOpen(false);
if (cb.current !== null) cb.current(res);
cb.current = null;
};
const hook: DialogContext = (accommodation, create) => {
setReservation(accommodation);
setCreate(create);
setOpen(true);
return new Promise((res) => {
cb.current = res;
});
};
return (
<>
<DialogContextK.Provider value={hook}>
{p.children}
</DialogContextK.Provider>
{open && (
<UpdateReservationDialog
open={open}
reservation={reservation}
create={create}
onClose={handleClose}
onSubmitted={handleClose}
/>
)}
</>
);
}
export function useUpdateAccommodationReservation(): DialogContext {
return React.useContext(DialogContextK)!;
}

View File

@ -17,6 +17,7 @@ import {
AccommodationsReservationsApi, AccommodationsReservationsApi,
AccommodationsReservationsList, AccommodationsReservationsList,
} from "../../../api/accommodations/AccommodationsReservationsApi"; } from "../../../api/accommodations/AccommodationsReservationsApi";
import { useUpdateAccommodationReservation } from "../../../hooks/context_providers/accommodations/UpdateReservationDialogProvider";
import { AsyncWidget } from "../../../widgets/AsyncWidget"; import { AsyncWidget } from "../../../widgets/AsyncWidget";
import { useFamily } from "../../../widgets/BaseFamilyRoute"; import { useFamily } from "../../../widgets/BaseFamilyRoute";
import { FamilyPageTitle } from "../../../widgets/FamilyPageTitle"; import { FamilyPageTitle } from "../../../widgets/FamilyPageTitle";
@ -27,6 +28,7 @@ export function AccommodationsReservationsRoute(): React.ReactElement {
const family = useFamily(); const family = useFamily();
const accommodations = useAccommodations(); const accommodations = useAccommodations();
const updateReservation = useUpdateAccommodationReservation();
const [reservations, setReservations] = React.useState< const [reservations, setReservations] = React.useState<
AccommodationsReservationsList | undefined AccommodationsReservationsList | undefined
@ -57,7 +59,17 @@ export function AccommodationsReservationsRoute(): React.ReactElement {
}; };
const onSelect = (d: DateSelectArg) => { const onSelect = (d: DateSelectArg) => {
// TODO : render this functional
// TODO : handle busy case
console.info(d); console.info(d);
updateReservation(
{
accommodation_id: -1,
start: Math.floor(d.start.getDate() / 1000),
end: Math.floor(d.end.getDate() / 1000),
},
true
);
}; };
return ( return (

View File

@ -5,10 +5,11 @@ import {
AccommodationsList, AccommodationsList,
} from "../../api/accommodations/AccommodationListApi"; } from "../../api/accommodations/AccommodationListApi";
import { CreateAccommodationCalendarURLDialogProvider } from "../../hooks/context_providers/accommodations/CreateAccommodationCalendarURLDialogProvider"; import { CreateAccommodationCalendarURLDialogProvider } from "../../hooks/context_providers/accommodations/CreateAccommodationCalendarURLDialogProvider";
import { InstallCalendarDialogProvider } from "../../hooks/context_providers/accommodations/InstallCalendarDialogProvider";
import { UpdateAccommodationDialogProvider } from "../../hooks/context_providers/accommodations/UpdateAccommodationDialogProvider"; import { UpdateAccommodationDialogProvider } from "../../hooks/context_providers/accommodations/UpdateAccommodationDialogProvider";
import { UpdateReservationDialogProvider } from "../../hooks/context_providers/accommodations/UpdateReservationDialogProvider";
import { AsyncWidget } from "../AsyncWidget"; import { AsyncWidget } from "../AsyncWidget";
import { useFamily } from "../BaseFamilyRoute"; import { useFamily } from "../BaseFamilyRoute";
import { InstallCalendarDialogProvider } from "../../hooks/context_providers/accommodations/InstallCalendarDialogProvider";
interface AccommodationsContext { interface AccommodationsContext {
accommodations: AccommodationsList; accommodations: AccommodationsList;
@ -65,7 +66,9 @@ export function BaseAccommodationsRoute(): React.ReactElement {
<UpdateAccommodationDialogProvider> <UpdateAccommodationDialogProvider>
<CreateAccommodationCalendarURLDialogProvider> <CreateAccommodationCalendarURLDialogProvider>
<InstallCalendarDialogProvider> <InstallCalendarDialogProvider>
<UpdateReservationDialogProvider>
<Outlet /> <Outlet />
</UpdateReservationDialogProvider>
</InstallCalendarDialogProvider> </InstallCalendarDialogProvider>
</CreateAccommodationCalendarURLDialogProvider> </CreateAccommodationCalendarURLDialogProvider>
</UpdateAccommodationDialogProvider> </UpdateAccommodationDialogProvider>