Add an accommodations reservations module #188

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

View File

@ -0,0 +1,75 @@
import { APIClient } from "../ApiClient";
import { Family } from "../FamilyApi";
import moment from "moment";
export interface AccommodationReservation {
id: number;
family_id: number;
accommodation_id: number;
user_id: number;
time_create: number;
time_update: number;
reservation_start: number;
reservation_end: number;
validated?: boolean;
}
export class AccommodationsReservationsList {
private list: AccommodationReservation[];
private map: Map<number, AccommodationReservation>;
constructor(list: AccommodationReservation[]) {
this.list = list;
this.map = new Map();
for (const m of list) {
this.map.set(m.id, m);
}
this.list.sort((a, b) => a.reservation_start - b.reservation_start);
}
public get isEmpty(): boolean {
return this.list.length === 0;
}
public get size(): number {
return this.list.length;
}
public get fullList(): AccommodationReservation[] {
return this.list;
}
filter(
predicate: (m: AccommodationReservation) => boolean
): AccommodationReservation[] {
return this.list.filter(predicate);
}
forAccommodation(id: number): AccommodationReservation[] {
return this.filter((a) => a.accommodation_id === id);
}
get(id: number): AccommodationReservation | undefined {
return this.map.get(id);
}
}
export class AccommodationsReservationsApi {
/**
* Get the entire list of accommodations of a family
*/
static async FullListOfFamily(
family: Family
): Promise<AccommodationsReservationsList> {
const data = (
await APIClient.exec({
method: "GET",
uri: `/family/${family.family_id}/accommodations/reservations/full_list`,
})
).data;
return new AccommodationsReservationsList(data);
}
}

View File

@ -1,3 +1,181 @@
import React from "react";
import { FamilyApi, FamilyUser } from "../../../api/FamilyApi";
import {
AccommodationsReservationsApi,
AccommodationsReservationsList,
} from "../../../api/accommodations/AccommodationsReservationsApi";
import { AsyncWidget } from "../../../widgets/AsyncWidget";
import { useFamily } from "../../../widgets/BaseFamilyRoute";
import { FamilyPageTitle } from "../../../widgets/FamilyPageTitle";
import {
FormControl,
FormLabel,
FormGroup,
FormControlLabel,
Checkbox,
FormHelperText,
} from "@mui/material";
import { useAccommodations } from "../../../widgets/accommodations/BaseAccommodationsRoute";
import {
Calendar,
DateLocalizer,
Views,
momentLocalizer,
} from "react-big-calendar";
import moment from "moment";
const localizer = momentLocalizer(moment);
export function AccommodationsReservationsRoute(): React.ReactElement {
return <>TODO</>;
const loadKey = React.useRef(1);
const family = useFamily();
const accommodations = useAccommodations();
const [reservations, setReservations] = React.useState<
AccommodationsReservationsList | undefined
>();
const [users, setUsers] = React.useState<FamilyUser[] | null>(null);
const [showValidated, setShowValidated] = React.useState(true);
const [showRejected, setShowRejected] = React.useState(true);
const [showPending, setShowPending] = React.useState(true);
const [hiddenPeople, setHiddenPeople] = React.useState<Set<number>>(
new Set()
);
const [hiddenAccommodations, setHiddenAccommodations] = React.useState<
Set<number>
>(new Set());
const load = async () => {
setReservations(
await AccommodationsReservationsApi.FullListOfFamily(family.family)
);
setUsers(await FamilyApi.GetUsersList(family.family.family_id));
};
const reload = async () => {
loadKey.current += 1;
setUsers(null);
};
return (
<>
<FamilyPageTitle title="Réservation" />
<AsyncWidget
loadKey={loadKey.current}
load={load}
errMsg="Echec du chargement de la liste des réservations !"
build={() => (
<div style={{ display: "flex", flexDirection: "row" }}>
<div style={{ flex: 1, maxWidth: "250px" }}>
{/* Invitation status */}
<FormControl
sx={{ m: 3 }}
component="fieldset"
variant="standard"
>
<FormLabel component="legend">Status</FormLabel>
<FormGroup>
<FormControlLabel
control={
<Checkbox
checked={showValidated}
onChange={(_ev, v) => setShowValidated(v)}
/>
}
label="Validé"
/>
<FormControlLabel
control={
<Checkbox
checked={showRejected}
onChange={(_ev, v) => setShowRejected(v)}
/>
}
label="Rejetés"
/>
<FormControlLabel
control={
<Checkbox
checked={showPending}
onChange={(_ev, v) => setShowPending(v)}
/>
}
label="En attente de validation"
/>
</FormGroup>
</FormControl>
{/* Accommodations */}
<FormControl
sx={{ m: 3 }}
component="fieldset"
variant="standard"
>
<FormLabel component="legend">Logements</FormLabel>
<FormGroup>
{accommodations.accommodations.fullList.map((a) => (
<FormControlLabel
key={a.id}
control={
<Checkbox
checked={!hiddenAccommodations.has(a.id)}
onChange={(_ev, v) => {
if (v) hiddenAccommodations.delete(a.id);
else hiddenAccommodations.add(a.id);
setHiddenAccommodations(
new Set(hiddenAccommodations)
);
}}
/>
}
label={a.name}
/>
))}
</FormGroup>
</FormControl>
{/* People */}
<FormControl
sx={{ m: 3 }}
component="fieldset"
variant="standard"
>
<FormLabel component="legend">Personnes</FormLabel>
<FormGroup>
{users?.map((u) => (
<FormControlLabel
key={u.user_id}
control={
<Checkbox
checked={!hiddenPeople.has(u.user_id)}
onChange={(_ev, v) => {
if (v) hiddenPeople.delete(u.user_id);
else hiddenPeople.add(u.user_id);
setHiddenPeople(new Set(hiddenPeople));
}}
/>
}
label={u.user_name}
/>
))}
</FormGroup>
</FormControl>
</div>
{/* The calendar */}
<div style={{ flex: 5 }}>
<Calendar
defaultView={Views.MONTH}
localizer={localizer}
culture={"fr"}
/>
</div>
</div>
)}
/>
</>
);
}