Check accommodation availability directly in create reservation dialog
This commit is contained in:
		@@ -1,5 +1,6 @@
 | 
			
		||||
import { APIClient } from "../ApiClient";
 | 
			
		||||
import { Family } from "../FamilyApi";
 | 
			
		||||
import { Accommodation } from "./AccommodationListApi";
 | 
			
		||||
 | 
			
		||||
export interface AccommodationReservation {
 | 
			
		||||
  id: number;
 | 
			
		||||
@@ -59,6 +60,7 @@ export interface UpdateAccommodationReservation {
 | 
			
		||||
  start: number;
 | 
			
		||||
  end: number;
 | 
			
		||||
  accommodation_id: number;
 | 
			
		||||
  reservation_id?: number;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
export class AccommodationsReservationsApi {
 | 
			
		||||
@@ -77,4 +79,23 @@ export class AccommodationsReservationsApi {
 | 
			
		||||
 | 
			
		||||
    return new AccommodationsReservationsList(data);
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  /**
 | 
			
		||||
   * Get the reservations of a given time interval for an accommodation
 | 
			
		||||
   */
 | 
			
		||||
  static async ReservationsForInterval(
 | 
			
		||||
    family: Family,
 | 
			
		||||
    accommodation: Accommodation,
 | 
			
		||||
    start: number,
 | 
			
		||||
    end: number
 | 
			
		||||
  ): Promise<AccommodationsReservationsList> {
 | 
			
		||||
    const data = (
 | 
			
		||||
      await APIClient.exec({
 | 
			
		||||
        method: "GET",
 | 
			
		||||
        uri: `/family/${family.family_id}/accommodations/reservations/accommodation/${accommodation.id}/for_interval?start=${start}&end=${end}`,
 | 
			
		||||
      })
 | 
			
		||||
    ).data;
 | 
			
		||||
 | 
			
		||||
    return new AccommodationsReservationsList(data);
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
@@ -1,15 +1,24 @@
 | 
			
		||||
import {
 | 
			
		||||
  Alert,
 | 
			
		||||
  Button,
 | 
			
		||||
  Dialog,
 | 
			
		||||
  DialogActions,
 | 
			
		||||
  DialogContent,
 | 
			
		||||
  DialogTitle,
 | 
			
		||||
  Typography,
 | 
			
		||||
} from "@mui/material";
 | 
			
		||||
import React from "react";
 | 
			
		||||
import { UpdateAccommodationReservation } from "../../api/accommodations/AccommodationsReservationsApi";
 | 
			
		||||
import {
 | 
			
		||||
  AccommodationReservation,
 | 
			
		||||
  AccommodationsReservationsApi,
 | 
			
		||||
  UpdateAccommodationReservation,
 | 
			
		||||
} from "../../api/accommodations/AccommodationsReservationsApi";
 | 
			
		||||
import { useAlert } from "../../hooks/context_providers/AlertDialogProvider";
 | 
			
		||||
import { useFamily } from "../../widgets/BaseFamilyRoute";
 | 
			
		||||
import { useAccommodations } from "../../widgets/accommodations/BaseAccommodationsRoute";
 | 
			
		||||
import { PropDateInput } from "../../widgets/forms/PropDateInput";
 | 
			
		||||
import { PropSelect } from "../../widgets/forms/PropSelect";
 | 
			
		||||
import { fmtUnixDate } from "../../utils/time_utils";
 | 
			
		||||
 | 
			
		||||
export function UpdateReservationDialog(p: {
 | 
			
		||||
  open: boolean;
 | 
			
		||||
@@ -18,12 +27,19 @@ export function UpdateReservationDialog(p: {
 | 
			
		||||
  onClose: () => void;
 | 
			
		||||
  onSubmitted: (c: UpdateAccommodationReservation) => void;
 | 
			
		||||
}): React.ReactElement {
 | 
			
		||||
  const alert = useAlert();
 | 
			
		||||
 | 
			
		||||
  const family = useFamily();
 | 
			
		||||
  const accommodations = useAccommodations();
 | 
			
		||||
 | 
			
		||||
  const [reservation, setReservation] = React.useState<
 | 
			
		||||
    UpdateAccommodationReservation | undefined
 | 
			
		||||
  >();
 | 
			
		||||
 | 
			
		||||
  const [conflicts, setConflicts] = React.useState<
 | 
			
		||||
    AccommodationReservation[] | undefined
 | 
			
		||||
  >(undefined);
 | 
			
		||||
 | 
			
		||||
  const clearForm = () => {
 | 
			
		||||
    setReservation(undefined);
 | 
			
		||||
  };
 | 
			
		||||
@@ -42,7 +58,46 @@ export function UpdateReservationDialog(p: {
 | 
			
		||||
    if (!reservation) setReservation(p.reservation);
 | 
			
		||||
  }, [p.open, p.reservation]);
 | 
			
		||||
 | 
			
		||||
  // TODO : check availability
 | 
			
		||||
  React.useEffect(() => {
 | 
			
		||||
    setConflicts(undefined);
 | 
			
		||||
    (async () => {
 | 
			
		||||
      try {
 | 
			
		||||
        if (
 | 
			
		||||
          !reservation ||
 | 
			
		||||
          reservation.accommodation_id < 1 ||
 | 
			
		||||
          reservation.start < 1 ||
 | 
			
		||||
          reservation.start > reservation.end
 | 
			
		||||
        ) {
 | 
			
		||||
          setConflicts([]);
 | 
			
		||||
          return;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        setConflicts(
 | 
			
		||||
          (
 | 
			
		||||
            await AccommodationsReservationsApi.ReservationsForInterval(
 | 
			
		||||
              family.family,
 | 
			
		||||
              accommodations.accommodations.get(reservation.accommodation_id)!,
 | 
			
		||||
              reservation.start,
 | 
			
		||||
              reservation.end
 | 
			
		||||
            )
 | 
			
		||||
          ).filter(
 | 
			
		||||
            (r) =>
 | 
			
		||||
              r.id !== p.reservation?.reservation_id && r.validated !== false
 | 
			
		||||
          )
 | 
			
		||||
        );
 | 
			
		||||
      } catch (e) {
 | 
			
		||||
        console.error(e);
 | 
			
		||||
        alert(
 | 
			
		||||
          "Echec de la vérification de la présence de conflits de calendrier !"
 | 
			
		||||
        );
 | 
			
		||||
      }
 | 
			
		||||
    })();
 | 
			
		||||
  }, [
 | 
			
		||||
    p.open,
 | 
			
		||||
    reservation?.accommodation_id,
 | 
			
		||||
    reservation?.start,
 | 
			
		||||
    reservation?.end,
 | 
			
		||||
  ]);
 | 
			
		||||
 | 
			
		||||
  return (
 | 
			
		||||
    <Dialog open={p.open} onClose={cancel}>
 | 
			
		||||
@@ -94,7 +149,22 @@ export function UpdateReservationDialog(p: {
 | 
			
		||||
          minDate={reservation?.start}
 | 
			
		||||
        />
 | 
			
		||||
 | 
			
		||||
        {/* TODO : la suite */}
 | 
			
		||||
        {conflicts && conflicts.length > 0 && (
 | 
			
		||||
          <Alert severity="error">
 | 
			
		||||
            <p>
 | 
			
		||||
              Cette réservation est en conflit avec d'autres réservations sur
 | 
			
		||||
              les intervalles suivants :
 | 
			
		||||
            </p>
 | 
			
		||||
            <ul>
 | 
			
		||||
              {conflicts.map((c, num) => (
 | 
			
		||||
                <li key={num}>
 | 
			
		||||
                  Réservation du {fmtUnixDate(c.reservation_start)} au{" "}
 | 
			
		||||
                  {fmtUnixDate(c.reservation_end)}
 | 
			
		||||
                </li>
 | 
			
		||||
              ))}
 | 
			
		||||
            </ul>
 | 
			
		||||
          </Alert>
 | 
			
		||||
        )}
 | 
			
		||||
      </DialogContent>
 | 
			
		||||
      <DialogActions>
 | 
			
		||||
        <Button onClick={cancel}>Annuler</Button>
 | 
			
		||||
@@ -104,7 +174,8 @@ export function UpdateReservationDialog(p: {
 | 
			
		||||
            !(
 | 
			
		||||
              (reservation?.accommodation_id ?? -1) > 0 &&
 | 
			
		||||
              (reservation?.start ?? -1) > 0 &&
 | 
			
		||||
              (reservation?.end ?? -1) > (reservation?.start ?? 0)
 | 
			
		||||
              (reservation?.end ?? -1) > (reservation?.start ?? 0) &&
 | 
			
		||||
              (conflicts?.length ?? 0) === 0
 | 
			
		||||
            )
 | 
			
		||||
          }
 | 
			
		||||
        >
 | 
			
		||||
 
 | 
			
		||||
@@ -74,7 +74,7 @@ export function AccommodationsReservationsRoute(): React.ReactElement {
 | 
			
		||||
 | 
			
		||||
  return (
 | 
			
		||||
    <>
 | 
			
		||||
      <FamilyPageTitle title="Réservation" />
 | 
			
		||||
      <FamilyPageTitle title="Réservations" />
 | 
			
		||||
      <AsyncWidget
 | 
			
		||||
        loadKey={loadKey.current}
 | 
			
		||||
        load={load}
 | 
			
		||||
 
 | 
			
		||||
@@ -39,7 +39,6 @@ export function PropDateInput(p: {
 | 
			
		||||
  const maxDate = p.maxDate ? dayjs(new Date(p.maxDate * 1000)) : undefined;
 | 
			
		||||
 | 
			
		||||
  return (
 | 
			
		||||
    <div style={{ margin: "10px auto" }}>
 | 
			
		||||
    <LocalizationProvider dateAdapter={AdapterDayjs} adapterLocale="fr">
 | 
			
		||||
      <DatePicker
 | 
			
		||||
        label={p.label}
 | 
			
		||||
@@ -55,7 +54,7 @@ export function PropDateInput(p: {
 | 
			
		||||
        minDate={minDate}
 | 
			
		||||
        maxDate={maxDate}
 | 
			
		||||
      />
 | 
			
		||||
      <div style={{ height: "10px" }}></div>
 | 
			
		||||
    </LocalizationProvider>
 | 
			
		||||
    </div>
 | 
			
		||||
  );
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
@@ -93,6 +93,31 @@ pub async fn get_accommodation_reservations(a: FamilyAndAccommodationInPath) ->
 | 
			
		||||
        .json(accommodations_reservations_service::get_all_of_accommodation(a.id()).await?))
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
#[derive(serde::Deserialize)]
 | 
			
		||||
pub struct CheckAvailabilityQuery {
 | 
			
		||||
    start: usize,
 | 
			
		||||
    end: usize,
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/// Check reservation availability
 | 
			
		||||
pub async fn get_accommodation_reservations_for_interval(
 | 
			
		||||
    a: FamilyAndAccommodationInPath,
 | 
			
		||||
    req: web::Query<CheckAvailabilityQuery>,
 | 
			
		||||
) -> HttpResult {
 | 
			
		||||
    if req.start > req.end {
 | 
			
		||||
        return Ok(HttpResponse::BadRequest().json("start should be smaller than end!"));
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    let res = accommodations_reservations_service::get_reservations_for_time_interval(
 | 
			
		||||
        a.id(),
 | 
			
		||||
        req.start,
 | 
			
		||||
        req.end,
 | 
			
		||||
    )
 | 
			
		||||
    .await?;
 | 
			
		||||
 | 
			
		||||
    Ok(HttpResponse::Ok().json(res))
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/// Get the full list of accommodations reservations for a family
 | 
			
		||||
pub async fn full_list(m: FamilyInPath) -> HttpResult {
 | 
			
		||||
    Ok(HttpResponse::Ok()
 | 
			
		||||
 
 | 
			
		||||
@@ -233,6 +233,11 @@ async fn main() -> std::io::Result<()> {
 | 
			
		||||
                web::get()
 | 
			
		||||
                    .to(accommodations_reservations_controller::get_accommodation_reservations),
 | 
			
		||||
            )
 | 
			
		||||
            .route(
 | 
			
		||||
                "/family/{id}/accommodations/reservations/accommodation/{accommodation_id}/for_interval",
 | 
			
		||||
                web::get()
 | 
			
		||||
                    .to(accommodations_reservations_controller::get_accommodation_reservations_for_interval),
 | 
			
		||||
            )
 | 
			
		||||
            .route(
 | 
			
		||||
                "/family/{id}/accommodations/reservations/full_list",
 | 
			
		||||
                web::get().to(accommodations_reservations_controller::full_list),
 | 
			
		||||
 
 | 
			
		||||
		Reference in New Issue
	
	Block a user