This commit is contained in:
@@ -1,6 +1,5 @@
|
||||
import { useAPIRoomCategory } from "@/api/api-roomCategories";
|
||||
import { Card, CardContent } from "../ui/card";
|
||||
import { useMemo } from "react";
|
||||
import { Typography } from "../ui/typography";
|
||||
import {
|
||||
Table,
|
||||
@@ -12,14 +11,14 @@ import {
|
||||
} from "../ui/table";
|
||||
import { Skeleton } from "../ui/skeleton";
|
||||
import { useAppStore } from "@/store";
|
||||
import { DateRange } from "react-day-picker";
|
||||
import { FareFilter } from "@/model/RoomSearch";
|
||||
import { useAPIRoomPrices } from "@/api/api-roomSearch";
|
||||
import { NumberSelector } from "../ui/number-selector";
|
||||
import { NewBooking, NewBookingRoom } from "@/model/Bookings";
|
||||
import { RoomItemCard } from "./room-item-card";
|
||||
import { IconWithTooltip } from "../ui/icon-number-tooltip";
|
||||
import { UserIcon } from "lucide-react";
|
||||
import { makeFareFilterFromSearchFilter } from "@/utils/utils";
|
||||
import { SearchFilter } from "@/model/InternalTypes";
|
||||
|
||||
function RoomBooking({
|
||||
roomCategoryId,
|
||||
@@ -28,12 +27,12 @@ function RoomBooking({
|
||||
roomCategoryId: number;
|
||||
availableAmount: number;
|
||||
}) {
|
||||
const currentDates: DateRange = useAppStore(
|
||||
(state) => state.searchFilter.dateRange,
|
||||
const appSearchFilter: SearchFilter = useAppStore(
|
||||
(state) => state.searchFilter,
|
||||
);
|
||||
const addNewRoom = useAppStore((state) => state.addNewBookingRoom);
|
||||
const addNewRoom = useAppStore((state) => state.currentBookingAddRoom);
|
||||
const bookingRooms: NewBooking | null = useAppStore(
|
||||
(state) => state.newBookingData,
|
||||
(state) => state.currentBooking,
|
||||
);
|
||||
|
||||
function onSelectedAmountUpdate(
|
||||
@@ -59,29 +58,29 @@ function RoomBooking({
|
||||
addNewRoom(newRoom);
|
||||
}
|
||||
|
||||
const totalSelected = () => {
|
||||
const getSelectedRoomAmount = (guestNumber: number | null) => {
|
||||
let sum = 0;
|
||||
if (bookingRooms) {
|
||||
//Rooms with same category and, if guestNumber is set, rooms with same guestNumber.
|
||||
bookingRooms.rooms
|
||||
.filter((v) => v.roomCategory == roomCategoryId)
|
||||
.filter((v) => {
|
||||
const isSameCat = v.roomCategory == roomCategoryId;
|
||||
const isSameGuestNum =
|
||||
guestNumber != null ? v.guestNumber == guestNumber : true;
|
||||
return isSameCat && isSameGuestNum;
|
||||
})
|
||||
.forEach((v) => (sum += v.amount));
|
||||
}
|
||||
return sum;
|
||||
};
|
||||
const canAddMore: boolean = availableAmount > totalSelected();
|
||||
const totalSelectAmount = getSelectedRoomAmount(null);
|
||||
const canAddMoreRooms: boolean = availableAmount > totalSelectAmount;
|
||||
|
||||
//This seems a little bit overkill, but IDK.
|
||||
const fareFilter: FareFilter | null = useMemo(() => {
|
||||
if (currentDates && currentDates.from && currentDates.to) {
|
||||
const filter: FareFilter = {
|
||||
startDate: currentDates.from?.toDateString(),
|
||||
endDate: currentDates.to?.toDateString(),
|
||||
roomCategory: roomCategoryId,
|
||||
};
|
||||
return filter;
|
||||
}
|
||||
return null;
|
||||
}, [currentDates, roomCategoryId]);
|
||||
const fareFilter = makeFareFilterFromSearchFilter(
|
||||
appSearchFilter,
|
||||
roomCategoryId,
|
||||
);
|
||||
|
||||
const {
|
||||
roomCategory,
|
||||
@@ -112,9 +111,15 @@ function RoomBooking({
|
||||
const hasPrices = roomPriceList != null && roomPriceList?.length > 0;
|
||||
let priceTable = null;
|
||||
if (hasPrices) {
|
||||
const tableRows = roomPriceList?.map((el) => {
|
||||
//A row for each room guestNumber.
|
||||
let tableRows = null;
|
||||
if (!isLoadingPrices) {
|
||||
tableRows = roomPriceList?.map((el) => {
|
||||
const selectedRoomAmount =
|
||||
totalSelectAmount > 0 ? getSelectedRoomAmount(el.guestNumber) : 0;
|
||||
|
||||
return (
|
||||
<TableRow key={el.guestNumber}>
|
||||
<TableRow key={roomCategoryId + "-" + el.guestNumber}>
|
||||
<TableCell>
|
||||
<IconWithTooltip
|
||||
label={el.guestNumber?.toString()}
|
||||
@@ -130,13 +135,16 @@ function RoomBooking({
|
||||
updateCount={(count) =>
|
||||
onSelectedAmountUpdate(el.guestNumber, count, el.price)
|
||||
}
|
||||
addEnabled={canAddMore}
|
||||
addEnabled={canAddMoreRooms}
|
||||
initialValue={selectedRoomAmount}
|
||||
></NumberSelector>
|
||||
</TableCell>
|
||||
</TableRow>
|
||||
);
|
||||
});
|
||||
}
|
||||
|
||||
//Element for
|
||||
priceTable = (
|
||||
<Table className="overflow-hidden">
|
||||
<TableHeader>
|
||||
|
||||
@@ -35,7 +35,6 @@ export function RoomItemCard({
|
||||
|
||||
const bedTypeIconList = room.roomCategoryBedTypes.map((e) => {
|
||||
return (
|
||||
<div>
|
||||
<IconWithTooltip
|
||||
key={room.id.toString() + e.bedTypeIcon}
|
||||
tooltipText={e.bedTypeName}
|
||||
@@ -43,7 +42,6 @@ export function RoomItemCard({
|
||||
>
|
||||
<Icon icon={e.bedTypeIcon} height="auto" />
|
||||
</IconWithTooltip>
|
||||
</div>
|
||||
);
|
||||
});
|
||||
|
||||
|
||||
@@ -6,37 +6,28 @@ import { DatePickerWithRange } from "../ui/dateRange-picker";
|
||||
import { Button } from "../ui/button";
|
||||
import { SearchIcon } from "lucide-react";
|
||||
import { useState } from "react";
|
||||
import { SearchFilter } from "@/model/SearchFilter";
|
||||
import { SearchFilter } from "@/model/InternalTypes";
|
||||
import { DateRange } from "react-day-picker";
|
||||
import { useToast } from "../ui/use-toast";
|
||||
import { useAppStore } from "@/store";
|
||||
|
||||
type funOnSearchClick = (newFilter: SearchFilter) => void;
|
||||
|
||||
export function SearchFilterCard({
|
||||
onSearchClick,
|
||||
initialsearchFilter,
|
||||
}: {
|
||||
onSearchClick?: funOnSearchClick;
|
||||
initialsearchFilter?: SearchFilter;
|
||||
}) {
|
||||
//App filter. Updated on btSearchClick.
|
||||
const appSearchFilter = useAppStore((state) => state.searchFilter);
|
||||
const setAppSearchFilter = useAppStore((state) => state.setSearchFilter);
|
||||
|
||||
//Local Filter state.
|
||||
const [searchFilter, setSearchFilter] =
|
||||
useState<SearchFilter>(appSearchFilter);
|
||||
|
||||
//On location change, try to load searchFilter from state.
|
||||
//FIXME: Si llegamos sin state, hay que borrar el state global...
|
||||
// const location = useLocation();
|
||||
// useEffect(() => {
|
||||
// setSearchFilter(location.state);
|
||||
// }, [location]);
|
||||
const [searchFilter, setSearchFilter] = useState<SearchFilter | null>(
|
||||
initialsearchFilter ?? null,
|
||||
);
|
||||
|
||||
const { toast } = useToast();
|
||||
|
||||
// Validate and call callback on btSearch click.
|
||||
//TODOME: Add data validations and show messages.
|
||||
//TODOME: Add data validations and show messages as form instead of toasts.
|
||||
const btSearchClick = () => {
|
||||
if (!searchFilter || !searchFilter.dateRange) {
|
||||
toast({
|
||||
@@ -55,8 +46,6 @@ export function SearchFilterCard({
|
||||
return;
|
||||
}
|
||||
|
||||
setAppSearchFilter(searchFilter);
|
||||
|
||||
if (onSearchClick) {
|
||||
onSearchClick(searchFilter);
|
||||
}
|
||||
|
||||
@@ -18,7 +18,7 @@ export function BookingDetails({
|
||||
onBookingConfirm?: OnBookingClickFunction;
|
||||
mode: BookingDetailsModes;
|
||||
}) {
|
||||
const bookingData = useAppStore((state) => state.newBookingData);
|
||||
const bookingData = useAppStore((state) => state.currentBooking);
|
||||
const roomCategoryList = useAPIRoomCategoryList();
|
||||
|
||||
const onBtBookingClick = () => {
|
||||
|
||||
@@ -8,12 +8,14 @@ export function NumberSelector({
|
||||
updateCount,
|
||||
addEnabled = true,
|
||||
allowNegative = false,
|
||||
initialValue = 0,
|
||||
}: {
|
||||
updateCount: UpdateSelectedNumberFunction;
|
||||
addEnabled?: boolean;
|
||||
allowNegative?: boolean;
|
||||
initialValue?: number;
|
||||
}) {
|
||||
const [count, setCount] = useState<number>(0);
|
||||
const [count, setCount] = useState<number>(initialValue);
|
||||
|
||||
const counterUpdated = (newAmount: number) => {
|
||||
setCount(newAmount);
|
||||
|
||||
+1
-1
@@ -3,7 +3,7 @@ import ReactDOM from "react-dom/client";
|
||||
import "./index.css";
|
||||
import { RouterProvider, createBrowserRouter } from "react-router-dom";
|
||||
import Root from "./routes/Root";
|
||||
import ErrorPage from "./error-page";
|
||||
import ErrorPage from "./routes/error-page";
|
||||
import Homepage from "./routes/home-page";
|
||||
import SearchPage from "./routes/search-page";
|
||||
import BookPage from "./routes/book-page";
|
||||
|
||||
@@ -30,8 +30,8 @@ enum PageState {
|
||||
|
||||
function BookPage() {
|
||||
const [pageState, setPageState] = useState<PageState>(PageState.showForm);
|
||||
const bookingData = useAppStore((state) => state.newBookingData);
|
||||
const setCustomerData = useAppStore((state) => state.setBookingCustomer);
|
||||
const bookingData = useAppStore((state) => state.currentBooking);
|
||||
const setCustomerData = useAppStore((state) => state.currentBookingSetCustomer);
|
||||
const navigate = useNavigate();
|
||||
|
||||
const bookingForm = useForm<z.infer<typeof BookingFormSchema>>({
|
||||
|
||||
@@ -6,16 +6,36 @@ import { Separator } from "@/components/ui/separator";
|
||||
import { Skeleton } from "@/components/ui/skeleton";
|
||||
import { Typography } from "@/components/ui/typography";
|
||||
import { RoomCategory } from "@/model/RoomCategory";
|
||||
import { SearchFilter } from "@/model/SearchFilter";
|
||||
import { SearchFilter } from "@/model/InternalTypes";
|
||||
import { eBookingState } from "@/utils/AppStateManager";
|
||||
import { useNavigate } from "react-router-dom";
|
||||
import { LoaderIcon } from "lucide-react";
|
||||
import "./home-page.css";
|
||||
import { useAppStore } from "@/store";
|
||||
import { isValidSearchFilter, makeDayUTCDate } from "@/utils/utils";
|
||||
|
||||
function Homepage() {
|
||||
const appSearchFilter = useAppStore((state) => state.searchFilter);
|
||||
const setAppSearchFilter = useAppStore((state) => state.setSearchFilter);
|
||||
const setAppBookingState = useAppStore((state) => state.setBookingState);
|
||||
const initAppBooking = useAppStore((state) => state.initCurrentBooking);
|
||||
|
||||
const { roomCategoryList, isLoading, isError } = useAPIRoomCategoryList();
|
||||
const navigate = useNavigate();
|
||||
|
||||
const onBtSearchClick = (filter: SearchFilter) => {
|
||||
if (!isValidSearchFilter(filter)) {
|
||||
return;
|
||||
}
|
||||
|
||||
setAppSearchFilter(filter);
|
||||
setAppBookingState(eBookingState.Searched);
|
||||
|
||||
initAppBooking(
|
||||
makeDayUTCDate(filter.dateRange.from),
|
||||
makeDayUTCDate(filter.dateRange.to),
|
||||
);
|
||||
|
||||
navigate("/search", { state: filter });
|
||||
};
|
||||
|
||||
@@ -26,7 +46,10 @@ function Homepage() {
|
||||
<>
|
||||
<Typography variant={"h1"}>Somnia Rooms</Typography>
|
||||
<Separator className="my-10" />
|
||||
<SearchFilterCard onSearchClick={onBtSearchClick} />
|
||||
<SearchFilterCard
|
||||
onSearchClick={onBtSearchClick}
|
||||
initialsearchFilter={appSearchFilter}
|
||||
/>
|
||||
<Separator className="my-10" />
|
||||
<div className="homePageRoomGrid grid gap-4">{roomList}</div>
|
||||
{isLoading && (
|
||||
|
||||
+63
-62
@@ -6,79 +6,68 @@ import { BookingDetails } from "@/components/booking/booking-details";
|
||||
import { Separator } from "@/components/ui/separator";
|
||||
import { Skeleton } from "@/components/ui/skeleton";
|
||||
import { Typography } from "@/components/ui/typography";
|
||||
import { AvailabilityFilter } from "@/model/RoomSearch";
|
||||
import { SearchFilter } from "@/model/SearchFilter";
|
||||
import { SearchFilter } from "@/model/InternalTypes";
|
||||
import { eBookingState } from "@/utils/AppStateManager";
|
||||
import { useAppStore } from "@/store";
|
||||
import { useMemo, useState } from "react";
|
||||
import {
|
||||
isValidSearchFilter,
|
||||
makeAvailabilityFilterFromSearchFilter,
|
||||
makeDayUTCDate,
|
||||
} from "@/utils/utils";
|
||||
import { useNavigate } from "react-router-dom";
|
||||
|
||||
//TODOME: Igual podemos crear un StateMachine conjunta para los siguiente estados de la página: Sin Buscar, Sin disponibilidad, error, cargando.
|
||||
//TODOME: Al pulsar el botón "buscar", si ya existen datos en la reserva (se han añadido habitaciones) mostrar un cuadro de aviso de que si se busca se borrará la selección actual.
|
||||
//TODOME: Mediante los estados globales de la reserva, si ya está en estado "booking" añadiendo los datos, mostrar un mensaje y permitir redirigir directamente para que continue donde lo dejó.
|
||||
function SearchPage() {
|
||||
//const [availabilityFilter, setAvailabilityFilter] = useState<AvailabilityFilter>();
|
||||
const searchFilter = useAppStore((state) => state.searchFilter);
|
||||
const resetBooking = useAppStore((state) => state.resetBookingState);
|
||||
const appBookingState = useAppStore((state) => state.bookingState);
|
||||
const setAppBookingState = useAppStore((state) => state.setBookingState);
|
||||
const appSearchFilter = useAppStore((state) => state.searchFilter);
|
||||
const setAppSearchFilter = useAppStore((state) => state.setSearchFilter);
|
||||
const initAppBooking = useAppStore((state) => state.initCurrentBooking);
|
||||
const navigate = useNavigate();
|
||||
|
||||
const [isSearched, setIsSearched] = useState<boolean>(false);
|
||||
|
||||
//This seems a little bit overkill, but IDK.
|
||||
const availabilityFilter: AvailabilityFilter | null = useMemo(() => {
|
||||
if (
|
||||
searchFilter &&
|
||||
searchFilter.dateRange &&
|
||||
searchFilter.dateRange.from &&
|
||||
searchFilter.dateRange.to
|
||||
) {
|
||||
const newAvailabilityFilter: AvailabilityFilter = {
|
||||
startDate: searchFilter.dateRange.from.toDateString(),
|
||||
endDate: searchFilter.dateRange.to.toDateString(),
|
||||
};
|
||||
return newAvailabilityFilter;
|
||||
}
|
||||
return null;
|
||||
}, [searchFilter]);
|
||||
|
||||
const {
|
||||
roomAvailabilityList,
|
||||
isLoading: isLoadingAvailability,
|
||||
isError: isErrorAvailability,
|
||||
} = useAPIRoomAvailability(availabilityFilter ?? null);
|
||||
|
||||
} = useAPIRoomAvailability(
|
||||
makeAvailabilityFilterFromSearchFilter(appSearchFilter) ?? null,
|
||||
);
|
||||
const isLoading = isLoadingAvailability;
|
||||
const isError = isErrorAvailability;
|
||||
|
||||
const hasRooms =
|
||||
roomAvailabilityList?.length && roomAvailabilityList?.length > 0;
|
||||
|
||||
//TODOME: Hay que ver para mover esto a un método común.
|
||||
const onBtSearchClick = (newFilter: SearchFilter) => {
|
||||
if (
|
||||
newFilter.dateRange &&
|
||||
newFilter.dateRange.from &&
|
||||
newFilter.dateRange.to
|
||||
) {
|
||||
setIsSearched(true);
|
||||
|
||||
resetBooking(makeDayUTCDate(newFilter.dateRange.from), makeDayUTCDate(newFilter.dateRange.to));
|
||||
if (!isValidSearchFilter(newFilter)) {
|
||||
return;
|
||||
}
|
||||
|
||||
setAppSearchFilter(newFilter);
|
||||
setAppBookingState(eBookingState.Searched);
|
||||
|
||||
initAppBooking(
|
||||
makeDayUTCDate(newFilter.dateRange.from),
|
||||
makeDayUTCDate(newFilter.dateRange.to),
|
||||
);
|
||||
};
|
||||
|
||||
// TODOME: Hay que dar una vuelta a esta solución
|
||||
// - Mirar cómo trabajar con tiempos UTC desde el principio.
|
||||
// - En cualquier caso, mover esta función a otro lugar.
|
||||
const makeDayUTCDate = (date : Date) : Date => {
|
||||
return new Date(date.getFullYear(), date.getMonth(), date.getDate(), 23, 59, 59, 999);
|
||||
}
|
||||
|
||||
const onBookingContinue = () => {
|
||||
navigate("/book");
|
||||
};
|
||||
|
||||
const roomList = roomAvailabilityList?.map((element) => {
|
||||
const roomList =
|
||||
roomAvailabilityList?.map((element) => {
|
||||
return (
|
||||
<RoomBooking
|
||||
key={element.roomCategory}
|
||||
key={element.roomCategory + "-" + appBookingState.lastSearchDate}
|
||||
availableAmount={element.availability}
|
||||
roomCategoryId={element.roomCategory}
|
||||
/>
|
||||
);
|
||||
});
|
||||
}) ?? null;
|
||||
const noRoomsAvailable = (
|
||||
<>
|
||||
<Typography variant={"h3"} as="h3">
|
||||
@@ -87,22 +76,23 @@ function SearchPage() {
|
||||
</Typography>
|
||||
</>
|
||||
);
|
||||
const hasRooms =
|
||||
roomAvailabilityList?.length && roomAvailabilityList?.length > 0;
|
||||
console.log(hasRooms);
|
||||
|
||||
const pageContent = isLoading ? (
|
||||
const pageContentSearch = (
|
||||
<Typography>
|
||||
Realiza una búsqueda para ver la disponibilidad y los precios.
|
||||
</Typography>
|
||||
);
|
||||
const pageContentLoading = (
|
||||
<>
|
||||
<Typography variant={"h3"} as="h2">
|
||||
Loading...
|
||||
</Typography>
|
||||
<Skeleton className="mx-auto h-[200px] w-full" />
|
||||
</>
|
||||
) : (
|
||||
);
|
||||
const pageContentLoaded = (
|
||||
<div className="2xl:grid 2xl:grid-cols-60/40 2xl:gap-6">
|
||||
<div className="flex flex-col gap-5">
|
||||
{hasRooms ? roomList : isSearched && noRoomsAvailable}
|
||||
</div>
|
||||
<div className="flex flex-col gap-5">{roomList}</div>
|
||||
<Separator className="my-10 2xl:hidden" />
|
||||
<BookingDetails
|
||||
onBookingConfirm={onBookingContinue}
|
||||
@@ -111,17 +101,28 @@ function SearchPage() {
|
||||
</div>
|
||||
);
|
||||
|
||||
const getPageContent = () => {
|
||||
const isSearched = appBookingState.state == eBookingState.Searched;
|
||||
if (!isSearched) {
|
||||
return pageContentSearch;
|
||||
}
|
||||
if (isLoading) {
|
||||
return pageContentLoading;
|
||||
}
|
||||
if (!hasRooms) {
|
||||
return noRoomsAvailable;
|
||||
}
|
||||
return pageContentLoaded;
|
||||
};
|
||||
|
||||
return (
|
||||
<div className="bg-background text-foreground">
|
||||
<SearchFilterCard onSearchClick={onBtSearchClick} />
|
||||
<SearchFilterCard
|
||||
onSearchClick={onBtSearchClick}
|
||||
initialsearchFilter={appSearchFilter}
|
||||
/>
|
||||
<Separator className="my-10" />
|
||||
{hasRooms ? (
|
||||
pageContent
|
||||
) : (
|
||||
<Typography>
|
||||
Realiza una búsqueda para ver la disponibilidad y los precios.
|
||||
</Typography>
|
||||
)}
|
||||
{getPageContent()}
|
||||
<Separator className="my-10" />
|
||||
<BackendConnectionStatus isLoading={isLoading} isError={isError} />
|
||||
</div>
|
||||
|
||||
@@ -3,13 +3,12 @@ import { Card, CardContent, CardHeader } from "@/components/ui/card";
|
||||
import { Typography } from "@/components/ui/typography";
|
||||
import { NewBookingResponse } from "@/model/Bookings";
|
||||
import { useAppStore } from "@/store";
|
||||
import { addDays } from "date-fns";
|
||||
import { useEffect, useState } from "react";
|
||||
import { useLocation, useNavigate } from "react-router-dom";
|
||||
|
||||
function SuccessPage() {
|
||||
const [pageState, setPageState] = useState<NewBookingResponse | null>(null);
|
||||
const resetBookingState = useAppStore((state) => state.resetBookingState);
|
||||
const resetBookingState = useAppStore((state) => state.resetCurrentBooking);
|
||||
|
||||
const location = useLocation();
|
||||
const navigate = useNavigate();
|
||||
@@ -22,7 +21,7 @@ function SuccessPage() {
|
||||
}, [location, navigate]);
|
||||
|
||||
const handleBtBackClick = () => {
|
||||
resetBookingState(new Date(), addDays(new Date(), 2));
|
||||
resetBookingState();
|
||||
navigate("/home");
|
||||
};
|
||||
|
||||
|
||||
+59
-34
@@ -1,19 +1,31 @@
|
||||
import { create } from "zustand";
|
||||
import { SearchFilter } from "./model/SearchFilter";
|
||||
import { SearchFilter } from "@/model/InternalTypes";
|
||||
import {
|
||||
BookingState,
|
||||
eBookingState,
|
||||
SetBookingState,
|
||||
} from "./utils/AppStateManager";
|
||||
import {
|
||||
NewBooking,
|
||||
NewBookingCustomerDetails,
|
||||
NewBookingRoom,
|
||||
} from "./model/Bookings";
|
||||
|
||||
interface AppState {
|
||||
interface State {
|
||||
searchFilter: SearchFilter;
|
||||
currentBooking: NewBooking | null;
|
||||
bookingState: BookingState;
|
||||
}
|
||||
|
||||
interface Actions {
|
||||
setSearchFilter: (newFilter: SearchFilter) => void;
|
||||
newBookingData: NewBooking | null;
|
||||
setNewBooking: (newBooking: NewBooking) => void;
|
||||
resetBookingState: (arrival: Date, departure: Date) => void;
|
||||
addNewBookingRoom: (newRoom: NewBookingRoom) => void;
|
||||
setBookingCustomer: (newCustomer: NewBookingCustomerDetails) => void;
|
||||
resetSearchFilter: () => void;
|
||||
setCurrentBooking: (newBooking: NewBooking) => void;
|
||||
resetCurrentBooking: () => void;
|
||||
initCurrentBooking: (arrival: Date, departure: Date) => void;
|
||||
currentBookingAddRoom: (newRoom: NewBookingRoom) => void;
|
||||
currentBookingSetCustomer: (newCustomer: NewBookingCustomerDetails) => void;
|
||||
setBookingState: (newState: eBookingState) => void;
|
||||
}
|
||||
|
||||
const defaultSearchFilter: SearchFilter = {
|
||||
@@ -32,7 +44,12 @@ const defaultNewBooking: NewBooking = {
|
||||
rooms: [],
|
||||
};
|
||||
|
||||
function getDefaultBookingData(arrivalDate: Date, departureDate: Date) {
|
||||
const defaultBookingState: BookingState = {
|
||||
state: eBookingState.None,
|
||||
lastSearchDate: new Date(),
|
||||
};
|
||||
|
||||
function getDefaultBooking(arrivalDate: Date, departureDate: Date) {
|
||||
return {
|
||||
...defaultNewBooking,
|
||||
arrivalDate: arrivalDate,
|
||||
@@ -40,19 +57,19 @@ function getDefaultBookingData(arrivalDate: Date, departureDate: Date) {
|
||||
};
|
||||
}
|
||||
|
||||
function addNewBookingRoomToCurrentState(
|
||||
function addNewBookingRoom(
|
||||
newRoom: NewBookingRoom,
|
||||
currentState: NewBooking | null,
|
||||
currentBookingState: NewBooking | null,
|
||||
): NewBooking | null {
|
||||
if (currentState == null) {
|
||||
return currentState;
|
||||
if (currentBookingState == null) {
|
||||
return currentBookingState;
|
||||
}
|
||||
|
||||
//Remove rooms with "same key".
|
||||
const filteredRooms = currentState.rooms.filter((room) => {
|
||||
const diffRoom = room.roomCategory != newRoom.roomCategory;
|
||||
const diffAmount = room.guestNumber != newRoom.guestNumber;
|
||||
if (diffRoom || (!diffRoom && diffAmount)) {
|
||||
const filteredRooms = currentBookingState.rooms.filter((room) => {
|
||||
const isDiffRoom = room.roomCategory != newRoom.roomCategory;
|
||||
const isDiffAmount = room.guestNumber != newRoom.guestNumber;
|
||||
if (isDiffRoom || (!isDiffRoom && isDiffAmount)) {
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
@@ -64,11 +81,11 @@ function addNewBookingRoomToCurrentState(
|
||||
}
|
||||
|
||||
const newState: NewBooking = {
|
||||
...currentState,
|
||||
...currentBookingState,
|
||||
rooms: filteredRooms ?? [],
|
||||
};
|
||||
|
||||
console.log("AddRoomNewState:");
|
||||
console.log("Updated 'currentBooking' state:");
|
||||
console.log(newState);
|
||||
|
||||
return newState;
|
||||
@@ -76,35 +93,43 @@ function addNewBookingRoomToCurrentState(
|
||||
|
||||
function setBookingCustomer(
|
||||
newCustomer: NewBookingCustomerDetails,
|
||||
currentState: NewBooking | null,
|
||||
currentBookingState: NewBooking | null,
|
||||
) {
|
||||
if (currentState == null) {
|
||||
return currentState;
|
||||
if (currentBookingState == null) {
|
||||
return currentBookingState;
|
||||
}
|
||||
|
||||
const newState: NewBooking = {
|
||||
...currentState,
|
||||
...currentBookingState,
|
||||
customerDetails: newCustomer,
|
||||
};
|
||||
return newState;
|
||||
}
|
||||
|
||||
export const useAppStore = create<AppState>()((set) => ({
|
||||
export const useAppStore = create<State & Actions>()((set) => ({
|
||||
searchFilter: defaultSearchFilter,
|
||||
currentBooking: null,
|
||||
bookingState: defaultBookingState,
|
||||
|
||||
setSearchFilter: (newFilter) => set(() => ({ searchFilter: newFilter })),
|
||||
newBookingData: null,
|
||||
setNewBooking: (newBooking) => set(() => ({ newBookingData: newBooking })),
|
||||
resetBookingState: (arrival, departure) =>
|
||||
set(() => ({ newBookingData: getDefaultBookingData(arrival, departure) })),
|
||||
addNewBookingRoom: (newRoom: NewBookingRoom) =>
|
||||
resetSearchFilter: () => set(() => ({ searchFilter: defaultSearchFilter })),
|
||||
|
||||
setCurrentBooking: (newBooking) =>
|
||||
set(() => ({ currentBooking: newBooking })),
|
||||
resetCurrentBooking: () => set(() => ({ currentBooking: null })),
|
||||
initCurrentBooking: (arrival, departure) =>
|
||||
set(() => ({ currentBooking: getDefaultBooking(arrival, departure) })),
|
||||
currentBookingAddRoom: (newRoom) =>
|
||||
set((state) => ({
|
||||
newBookingData: addNewBookingRoomToCurrentState(
|
||||
newRoom,
|
||||
state.newBookingData,
|
||||
),
|
||||
currentBooking: addNewBookingRoom(newRoom, state.currentBooking),
|
||||
})),
|
||||
setBookingCustomer: (newCustomer: NewBookingCustomerDetails) =>
|
||||
currentBookingSetCustomer: (newCustomer) =>
|
||||
set((state) => ({
|
||||
newBookingData: setBookingCustomer(newCustomer, state.newBookingData),
|
||||
currentBooking: setBookingCustomer(newCustomer, state.currentBooking),
|
||||
})),
|
||||
|
||||
setBookingState: (newState) =>
|
||||
set((state) => ({
|
||||
bookingState: SetBookingState(newState, state.bookingState),
|
||||
})),
|
||||
}));
|
||||
|
||||
@@ -0,0 +1,27 @@
|
||||
export type BookingState = {
|
||||
state: eBookingState;
|
||||
lastSearchDate: Date;
|
||||
};
|
||||
|
||||
export enum eBookingState {
|
||||
None = "NONE",
|
||||
Searched = "SEARCHED",
|
||||
Booking = "BOOKING",
|
||||
Booked = "BOOKED",
|
||||
}
|
||||
|
||||
export function SetBookingState(
|
||||
newState: eBookingState,
|
||||
currentAppBookingState: BookingState,
|
||||
) {
|
||||
const newAppState: BookingState = {
|
||||
...currentAppBookingState,
|
||||
state: newState,
|
||||
};
|
||||
|
||||
if (newState == eBookingState.Searched) {
|
||||
newAppState.lastSearchDate = new Date();
|
||||
}
|
||||
|
||||
return newAppState;
|
||||
}
|
||||
@@ -0,0 +1,52 @@
|
||||
import { AvailabilityFilter, FareFilter } from "@/model/RoomSearch";
|
||||
import { SearchFilter } from "@/model/InternalTypes";
|
||||
|
||||
export function makeDayUTCDate(date: Date | undefined): Date {
|
||||
if (!date) {
|
||||
return new Date();
|
||||
}
|
||||
|
||||
return new Date(
|
||||
date.getFullYear(),
|
||||
date.getMonth(),
|
||||
date.getDate(),
|
||||
23,
|
||||
59,
|
||||
59,
|
||||
999,
|
||||
);
|
||||
}
|
||||
|
||||
export function isValidSearchFilter(sf: SearchFilter): boolean {
|
||||
if (sf?.dateRange?.from && sf?.dateRange?.to) {
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
export function makeAvailabilityFilterFromSearchFilter(
|
||||
sf: SearchFilter,
|
||||
): AvailabilityFilter | null {
|
||||
if (!isValidSearchFilter(sf)) {
|
||||
return null;
|
||||
}
|
||||
return {
|
||||
startDate: sf.dateRange.from!.toISOString(),
|
||||
endDate: sf.dateRange.to!.toISOString(),
|
||||
};
|
||||
}
|
||||
|
||||
export function makeFareFilterFromSearchFilter(
|
||||
sf: SearchFilter,
|
||||
roomCategoryId: number,
|
||||
): FareFilter | null {
|
||||
if (!isValidSearchFilter(sf) || roomCategoryId < 0) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return {
|
||||
startDate: sf.dateRange.from!.toISOString(),
|
||||
endDate: sf.dateRange.to!.toISOString(),
|
||||
roomCategory: roomCategoryId,
|
||||
};
|
||||
}
|
||||
Reference in New Issue
Block a user