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