WIP calendar integration
This commit is contained in:
		@@ -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>
 | 
				
			||||||
 | 
					        )}
 | 
				
			||||||
 | 
					      />
 | 
				
			||||||
 | 
					    </>
 | 
				
			||||||
 | 
					  );
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 
 | 
				
			|||||||
		Reference in New Issue
	
	Block a user