Compare commits
4
Commits
590d7f6fa1
...
70e483bc45
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
70e483bc45 | ||
|
|
99653371a8 | ||
|
|
3e77426d1d | ||
|
|
550beffba8 |
@@ -9,6 +9,7 @@ export async function APISendCreateBooking(bookingData: NewBooking | null) {
|
||||
};
|
||||
}
|
||||
|
||||
try {
|
||||
const rs = await fetcherWithFilter<NewBookingResponse>(
|
||||
"/api/booking/create",
|
||||
bookingData,
|
||||
@@ -18,4 +19,11 @@ export async function APISendCreateBooking(bookingData: NewBooking | null) {
|
||||
bookingResponse: rs,
|
||||
isError: false,
|
||||
};
|
||||
} catch (error) {
|
||||
return {
|
||||
bookingResponse: null,
|
||||
isError: true,
|
||||
errorMessage: error,
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,28 @@
|
||||
import { ConfigVar } from "@/model/ConfigVar";
|
||||
import { fetcher, fetcherUnwraper } from "./backend";
|
||||
import useSWR from "swr";
|
||||
|
||||
export function useAPIConfigVarList() {
|
||||
const { data, error, isLoading } = useSWR<ConfigVar[]>(
|
||||
"/api/config",
|
||||
fetcherUnwraper,
|
||||
);
|
||||
|
||||
return {
|
||||
configVarList: data,
|
||||
isLoading,
|
||||
isError: error,
|
||||
};
|
||||
}
|
||||
|
||||
export function useAPIConfigVar(key: string) {
|
||||
const url = `/api/config/var/${key}`;
|
||||
|
||||
const { data, error, isLoading } = useSWR<ConfigVar>(url, fetcher);
|
||||
|
||||
return {
|
||||
configVar: data,
|
||||
isLoading,
|
||||
isError: error,
|
||||
};
|
||||
}
|
||||
@@ -40,7 +40,7 @@ export function useAPIRoomPrices(filter: FareFilter | null) {
|
||||
: null;
|
||||
|
||||
const { data, isLoading, error } = useSWR(
|
||||
request ? ["/api/search/prices", request] : null,
|
||||
request ? ["/api/search/pricesCategory", request] : null,
|
||||
([url, request]) => fetcherUnwraperWithFilter<RoomPrice[]>(url, request),
|
||||
);
|
||||
|
||||
|
||||
@@ -2,11 +2,13 @@ import { Separator } from "../ui/separator";
|
||||
import { Card, CardContent } from "../ui/card";
|
||||
import { Typography } from "../ui/typography";
|
||||
import { UserIcon } from "lucide-react";
|
||||
import { RoomCategory, RoomCategoryPhoto } from "@/model/RoomCategory";
|
||||
import { RoomCategory } from "@/model/RoomCategory";
|
||||
import { IconWithTooltip } from "../ui/icon-number-tooltip";
|
||||
import roomImg from "@/res/SomniaRoomImage.svg";
|
||||
import defaultRoomImg from "@/res/SomniaRoomImage.svg";
|
||||
import { cn } from "@/lib/utils";
|
||||
import { Icon } from "@iconify-icon/react";
|
||||
import { useConfigVariable } from "@/utils/hooks";
|
||||
import { eConfigVarKeys } from "@/model/ConfigVar";
|
||||
|
||||
interface RoomItemCardProps {
|
||||
room: RoomCategory;
|
||||
@@ -27,11 +29,18 @@ export function RoomItemCard({
|
||||
room = defaultRoom,
|
||||
className,
|
||||
}: RoomItemCardProps) {
|
||||
const { value: varPhotoAspect } = useConfigVariable(
|
||||
eConfigVarKeys.varPhotoAspectRatio,
|
||||
"4/3",
|
||||
);
|
||||
|
||||
//FIXME: Use PhotoOrder.
|
||||
const mainPhoto: RoomCategoryPhoto | undefined =
|
||||
room.roomCategoryPhotos.length > 0
|
||||
? room.roomCategoryPhotos.at(0)
|
||||
: undefined;
|
||||
const getRoomMainPhoto = () => {
|
||||
if (room.roomCategoryPhotos.length < 0) {
|
||||
return defaultRoomImg;
|
||||
}
|
||||
return room.roomCategoryPhotos.at(0)?.path ?? defaultRoomImg;
|
||||
};
|
||||
|
||||
const bedTypeIconList = room.roomCategoryBedTypes.map((e) => {
|
||||
return (
|
||||
@@ -45,6 +54,7 @@ export function RoomItemCard({
|
||||
);
|
||||
});
|
||||
|
||||
const photoAspectRationClassName = "aspect-" + varPhotoAspect;
|
||||
return (
|
||||
<Card key={room.id} className={cn(className, "relative", "py-0")}>
|
||||
{/* <CardHeader className="p-0 m-0 mb-5">
|
||||
@@ -52,8 +62,10 @@ export function RoomItemCard({
|
||||
<CardContent className="flex h-full w-full flex-col p-0">
|
||||
<div className="">
|
||||
<img
|
||||
src={mainPhoto?.path ?? roomImg}
|
||||
className="aspect-4/3 w-full rounded-t-md object-cover"
|
||||
src={getRoomMainPhoto()}
|
||||
className={
|
||||
photoAspectRationClassName + " w-full rounded-t-md object-cover"
|
||||
}
|
||||
></img>
|
||||
<Separator className="my-0 border-2" />
|
||||
</div>
|
||||
|
||||
@@ -1,16 +1,129 @@
|
||||
import { useConfigVariable } from "@/utils/hooks";
|
||||
import { Separator } from "./ui/separator";
|
||||
import { Typography } from "./ui/typography";
|
||||
import { eConfigVarKeys } from "@/model/ConfigVar";
|
||||
import {
|
||||
c_sominaRooms_webpage,
|
||||
c_somniaRooms_notice_text,
|
||||
} from "@/utils/Consts";
|
||||
import { Skeleton } from "./ui/skeleton";
|
||||
import { Mail, Phone } from "lucide-react";
|
||||
import { Link } from "react-router-dom";
|
||||
|
||||
function AppFooter() {
|
||||
const { value: varCompanyLogo, isLoading: isLogoLoading } = useConfigVariable(
|
||||
eConfigVarKeys.varCompanyLogo,
|
||||
"public/SR2.svg",
|
||||
);
|
||||
const { value: varCompanyLogoSize, isLoading: isLogoSizeLoading } =
|
||||
useConfigVariable(eConfigVarKeys.varCompanyLogoSize, "64px");
|
||||
const { value: varCompanyName, isLoading: isNameLoading } = useConfigVariable(
|
||||
eConfigVarKeys.varCompanyName,
|
||||
"Somnia Rooms",
|
||||
);
|
||||
const { value: varCompanyAddress, isLoading: isAddressLoading } =
|
||||
useConfigVariable(eConfigVarKeys.varCompanyAddress, "");
|
||||
const { value: varCompanyPhone, isLoading: isPhoneLoading } =
|
||||
useConfigVariable(eConfigVarKeys.varCompanyPhone, "");
|
||||
const { value: varCompanyMail, isLoading: isMailLoading } = useConfigVariable(
|
||||
eConfigVarKeys.varCompanyEmail,
|
||||
"",
|
||||
);
|
||||
|
||||
const logoSizeClass = "w-[" + varCompanyLogoSize + "]";
|
||||
|
||||
const isLoading =
|
||||
isLogoLoading ||
|
||||
isLogoSizeLoading ||
|
||||
isNameLoading ||
|
||||
isAddressLoading ||
|
||||
isPhoneLoading ||
|
||||
isMailLoading;
|
||||
const getCompanyElement = () => {
|
||||
if (isLoading) {
|
||||
return <Skeleton className="h-[64px]" />;
|
||||
}
|
||||
|
||||
return (
|
||||
<div className="hidden flex-col bg-secondary">
|
||||
<div className="">Created with Love by me :)</div>
|
||||
<div className="">
|
||||
<p>Más información</p>
|
||||
<ul className="list-outside">
|
||||
<li>Info</li>
|
||||
<li>Info2</li>
|
||||
<li>Info3</li>
|
||||
<>
|
||||
<img
|
||||
src={varCompanyLogo}
|
||||
alt="company-logo"
|
||||
className={logoSizeClass}
|
||||
/>
|
||||
<div>
|
||||
<Typography variant={"p_inline"} className="text-nowrap">
|
||||
{varCompanyName} - © {new Date().getUTCFullYear()}
|
||||
</Typography>
|
||||
<Typography variant={"p_inline"}>{varCompanyAddress}</Typography>
|
||||
</div>
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
||||
const getContactElement = () => {
|
||||
if (isLoading) {
|
||||
return <Skeleton className="w-[64px]" />;
|
||||
}
|
||||
|
||||
return (
|
||||
<>
|
||||
<li className="flex items-center gap-2">
|
||||
<Phone size={"1.2em"} />
|
||||
<Typography variant={"p_inline"}>{varCompanyPhone}</Typography>
|
||||
</li>
|
||||
<li className="flex items-center gap-2">
|
||||
<Mail size={"1.2em"} />
|
||||
<Typography variant={"p_inline"}>{varCompanyMail}</Typography>
|
||||
</li>
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
||||
return (
|
||||
<footer className="w-full p-8 pt-0">
|
||||
<Separator className="my-4" />
|
||||
<div className="flex flex-row flex-wrap items-center justify-center gap-x-12 gap-y-6 text-center lg:justify-between">
|
||||
<div className="flex flex-row items-center gap-2">
|
||||
{getCompanyElement()}
|
||||
</div>
|
||||
<ul className="flex flex-wrap items-start gap-x-10 gap-y-2 px-4 py-4">
|
||||
<li>
|
||||
<ul className="text-start">
|
||||
<li>
|
||||
<Typography variant={"largeText"} className="text-start">
|
||||
Legal
|
||||
</Typography>
|
||||
</li>
|
||||
<li>
|
||||
<Typography asChild className="hover:text-blue-500">
|
||||
<Link to={"terms"}>Terms and conditions</Link>
|
||||
</Typography>
|
||||
</li>
|
||||
<li>
|
||||
<Typography asChild className="hover:text-blue-500">
|
||||
<Link to={"privacy"}>Privacy policy</Link>
|
||||
</Typography>
|
||||
</li>
|
||||
</ul>
|
||||
</li>
|
||||
<li>
|
||||
<ul>
|
||||
<li>
|
||||
<Typography variant={"largeText"} className="text-start">
|
||||
Contact Us
|
||||
</Typography>
|
||||
</li>
|
||||
{getContactElement()}
|
||||
</ul>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
<Separator className="my-4" />
|
||||
<Typography className="text-center" variant={"mutedText"}>
|
||||
<a href={c_sominaRooms_webpage}>{c_somniaRooms_notice_text}</a>
|
||||
</Typography>
|
||||
</footer>
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
@@ -3,37 +3,78 @@ import { Button } from "./ui/button";
|
||||
import { Card, CardContent } from "./ui/card";
|
||||
import { Typography } from "./ui/typography";
|
||||
import { Link } from "react-router-dom";
|
||||
import { BookAIcon, HomeIcon } from "lucide-react";
|
||||
import { Bed, BookAIcon, HomeIcon } from "lucide-react";
|
||||
import { eConfigVarKeys } from "@/model/ConfigVar";
|
||||
import { Skeleton } from "./ui/skeleton";
|
||||
import { useConfigVariable } from "@/utils/hooks";
|
||||
|
||||
function AppHeader() {
|
||||
const { value: varCompanyLogo, isLoading: isLogoLoading } = useConfigVariable(
|
||||
eConfigVarKeys.varCompanyLogo,
|
||||
"public/SR2.svg",
|
||||
);
|
||||
const { value: varCompanyLogoSize, isLoading: isLogoSizeLoading } =
|
||||
useConfigVariable(eConfigVarKeys.varCompanyLogoSize, "64px");
|
||||
const { value: varCompanyName, isLoading: isNameLoading } = useConfigVariable(
|
||||
eConfigVarKeys.varCompanyName,
|
||||
"Somnia Rooms",
|
||||
);
|
||||
const { value: varCompanyWeb, isLoading: isWebLoading } = useConfigVariable(
|
||||
eConfigVarKeys.varCompanyWebsite,
|
||||
"#",
|
||||
);
|
||||
|
||||
const isLoading =
|
||||
isLogoLoading || isNameLoading || isWebLoading || isLogoSizeLoading;
|
||||
|
||||
const getElementLogo = () => {
|
||||
if (isLoading) {
|
||||
return <Skeleton className="w-[64px]" />;
|
||||
}
|
||||
|
||||
const logoSizeClass = "w-[" + varCompanyLogoSize + "]";
|
||||
return (
|
||||
<Card className="mx-10 my-4 flex items-center py-0">
|
||||
<CardContent className="w-full py-2">
|
||||
<div className="flex items-center justify-around">
|
||||
<div className="relative flex flex-row items-center gap-2">
|
||||
<img src="public/SR2.svg" className="w-[64px]"></img>
|
||||
<Typography variant={"largeText"}>Somnia Rooms</Typography>
|
||||
<a
|
||||
href="https://duck.com"
|
||||
className="absolute h-full w-full"
|
||||
></a>
|
||||
<img src={varCompanyLogo} className={logoSizeClass}></img>
|
||||
<Typography variant={"largeText"} className="text-nowrap">
|
||||
{varCompanyName}
|
||||
</Typography>
|
||||
<a href={varCompanyWeb} className="absolute h-full w-full"></a>
|
||||
</div>
|
||||
<Button variant="ghost" className="h-15">
|
||||
);
|
||||
};
|
||||
|
||||
return (
|
||||
<header>
|
||||
<Card className="mx-10 my-4 items-center py-0">
|
||||
<CardContent className="w-full py-2">
|
||||
<div className="flex flex-col items-center justify-around gap-y-3 md:flex-row">
|
||||
{getElementLogo()}
|
||||
<div className="flex flex-row items-center">
|
||||
<Link to={`home`}>
|
||||
<Button variant="ghost" className="h-15 hover:cursor-pointer">
|
||||
<HomeIcon className="mx-auto" />
|
||||
Home
|
||||
</Link>
|
||||
</Button>
|
||||
<Button variant="ghost" className="h-15">
|
||||
</Link>
|
||||
<Link to={`search`}>
|
||||
<Button variant="ghost" className="h-15 hover:cursor-pointer">
|
||||
<BookAIcon className="mx-auto" />
|
||||
Reservar
|
||||
</Link>
|
||||
</Button>
|
||||
</Link>
|
||||
<Link to={`home`}>
|
||||
<Button variant="ghost" className="h-15 hover:cursor-pointer">
|
||||
<Bed className="mx-auto" />
|
||||
Mi reserva
|
||||
</Button>
|
||||
</Link>
|
||||
</div>
|
||||
<ModeToggle />
|
||||
</div>
|
||||
</CardContent>
|
||||
</Card>
|
||||
</header>
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
@@ -2,22 +2,36 @@ import * as React from "react";
|
||||
import * as SeparatorPrimitive from "@radix-ui/react-separator";
|
||||
|
||||
import { cn } from "@/lib/utils";
|
||||
import { cva, VariantProps } from "class-variance-authority";
|
||||
|
||||
const separatorVariants = cva(
|
||||
"shrink-0 bg-border data-[orientation=horizontal]:h-px data-[orientation=horizontal]:w-full data-[orientation=vertical]:h-full data-[orientation=vertical]:w-px",
|
||||
{
|
||||
variants: {
|
||||
variant: {
|
||||
default: "my-10",
|
||||
},
|
||||
},
|
||||
defaultVariants: {
|
||||
variant: "default",
|
||||
},
|
||||
},
|
||||
);
|
||||
|
||||
function Separator({
|
||||
className,
|
||||
variant,
|
||||
orientation = "horizontal",
|
||||
decorative = true,
|
||||
...props
|
||||
}: React.ComponentProps<typeof SeparatorPrimitive.Root>) {
|
||||
}: React.ComponentProps<typeof SeparatorPrimitive.Root> &
|
||||
VariantProps<typeof separatorVariants>) {
|
||||
return (
|
||||
<SeparatorPrimitive.Root
|
||||
data-slot="separator"
|
||||
decorative={decorative}
|
||||
orientation={orientation}
|
||||
className={cn(
|
||||
"shrink-0 bg-border data-[orientation=horizontal]:h-px data-[orientation=horizontal]:w-full data-[orientation=vertical]:h-full data-[orientation=vertical]:w-px",
|
||||
className,
|
||||
)}
|
||||
className={cn(separatorVariants({ variant, className }))}
|
||||
{...props}
|
||||
/>
|
||||
);
|
||||
|
||||
@@ -0,0 +1,20 @@
|
||||
export type ConfigVar = {
|
||||
key: string;
|
||||
value: string;
|
||||
};
|
||||
|
||||
export enum eConfigVarKeys {
|
||||
varCompanyName = "CompanyName",
|
||||
varCompanyWebsite = "CompanyWebsite",
|
||||
varCompanyLogo = "CompanyLogo",
|
||||
varCompanyLogoSize = "CompanyLogoSize",
|
||||
varCompanyEmail = "CompanyEmail",
|
||||
varCompanyPhone = "CompanyPhone",
|
||||
varCompanyAddress = "CompanyAddress",
|
||||
varNightAmountForContactDialog = "NightAmountForContactDialog",
|
||||
varGuestNumberForContactDialog = "GuestNumberForContactDialog",
|
||||
varContactDialogShowBehaviour = "ContactDialogShowBehaviour",
|
||||
varShowNonAvailableRooms = "ShowNonAvailableRooms",
|
||||
varSomniaAttributionNotice = "SomniaAttributionNotice",
|
||||
varPhotoAspectRatio = "PhotoAspectRatio",
|
||||
}
|
||||
+1
-1
@@ -10,7 +10,7 @@ function Root() {
|
||||
<>
|
||||
<ThemeProvider defaultTheme="dark" storageKey="vite-ui-theme">
|
||||
<AppHeader />
|
||||
<div className="mx-auto w-full bg-background p-8 text-center text-foreground sm:w-[90%] md:w-[80%]">
|
||||
<div className="mx-auto w-full min-h-[100vh] bg-background p-8 text-center text-foreground sm:w-[90%] md:w-[80%]">
|
||||
<Outlet />
|
||||
</div>
|
||||
<Toaster />
|
||||
|
||||
@@ -19,19 +19,22 @@ import { useForm } from "react-hook-form";
|
||||
import { useNavigate } from "react-router-dom";
|
||||
import { z } from "zod/v4";
|
||||
import { zodResolver } from "@hookform/resolvers/zod";
|
||||
import { eBookingState } from "@/utils/AppStateManager";
|
||||
|
||||
enum PageState {
|
||||
noData,
|
||||
showForm,
|
||||
sending,
|
||||
created,
|
||||
error,
|
||||
}
|
||||
|
||||
function BookPage() {
|
||||
const [pageState, setPageState] = useState<PageState>(PageState.showForm);
|
||||
const bookingData = useAppStore((state) => state.currentBooking);
|
||||
const setCustomerData = useAppStore((state) => state.currentBookingSetCustomer);
|
||||
const appBookingState = useAppStore((state) => state.bookingState);
|
||||
const setAppBookingState = useAppStore((state) => state.setBookingState);
|
||||
const setCustomerData = useAppStore(
|
||||
(state) => state.currentBookingSetCustomer,
|
||||
);
|
||||
const navigate = useNavigate();
|
||||
|
||||
const bookingForm = useForm<z.infer<typeof BookingFormSchema>>({
|
||||
@@ -67,16 +70,28 @@ function BookPage() {
|
||||
if (rs.isError) {
|
||||
setPageState(PageState.error);
|
||||
} else {
|
||||
setAppBookingState(eBookingState.Booked);
|
||||
navigate("/success", { state: rs.bookingResponse });
|
||||
}
|
||||
};
|
||||
|
||||
if (!bookingData || !bookingData.customerDetails) {
|
||||
return (
|
||||
<Typography variant={"h2"}>You must select some rooms...</Typography>
|
||||
);
|
||||
//Redirect to home if no booking is active.
|
||||
if (
|
||||
!appBookingState ||
|
||||
appBookingState.state != eBookingState.Booking ||
|
||||
!bookingData
|
||||
) {
|
||||
navigate("/home");
|
||||
}
|
||||
|
||||
const errorMsg = (
|
||||
<div>
|
||||
<Typography variant={"inlineCode"}>
|
||||
Server error. Try again later!
|
||||
</Typography>
|
||||
</div>
|
||||
);
|
||||
|
||||
return (
|
||||
<>
|
||||
<Typography variant={"h1"}>Booking Details</Typography>
|
||||
@@ -150,6 +165,7 @@ function BookPage() {
|
||||
</Button>
|
||||
</form>
|
||||
</Form>
|
||||
{pageState == PageState.error ? errorMsg : null}
|
||||
</div>
|
||||
<Separator className="my-10 lg:hidden" />
|
||||
<BookingDetails mode="View"></BookingDetails>
|
||||
|
||||
@@ -13,14 +13,18 @@ import { LoaderIcon } from "lucide-react";
|
||||
import "./home-page.css";
|
||||
import { useAppStore } from "@/store";
|
||||
import { isValidSearchFilter, makeDayUTCDate } from "@/utils/utils";
|
||||
import { useConfigVariable } from "@/utils/hooks";
|
||||
import { eConfigVarKeys } from "@/model/ConfigVar";
|
||||
|
||||
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 { value: varCompanyName } = useConfigVariable(eConfigVarKeys.varCompanyName, "");
|
||||
const { roomCategoryList, isLoading: isLoadingCategories, isError } = useAPIRoomCategoryList();
|
||||
const navigate = useNavigate();
|
||||
|
||||
const onBtSearchClick = (filter: SearchFilter) => {
|
||||
@@ -44,15 +48,15 @@ function Homepage() {
|
||||
});
|
||||
return (
|
||||
<>
|
||||
<Typography variant={"h1"}>Somnia Rooms</Typography>
|
||||
<Separator className="my-10" />
|
||||
<Typography variant={"h1"}>{varCompanyName}</Typography>
|
||||
<Separator />
|
||||
<SearchFilterCard
|
||||
onSearchClick={onBtSearchClick}
|
||||
initialsearchFilter={appSearchFilter}
|
||||
/>
|
||||
<Separator className="my-10" />
|
||||
<Separator />
|
||||
<div className="homePageRoomGrid grid gap-4">{roomList}</div>
|
||||
{isLoading && (
|
||||
{isLoadingCategories && (
|
||||
<>
|
||||
<Skeleton className="mx-auto flex h-[200px] w-full flex-col items-center justify-center">
|
||||
<LoaderIcon />
|
||||
@@ -62,8 +66,8 @@ function Homepage() {
|
||||
</Skeleton>
|
||||
</>
|
||||
)}
|
||||
<Separator className="my-10" />
|
||||
<BackendConnectionStatus isLoading={isLoading} isError={isError} />
|
||||
<Separator />
|
||||
<BackendConnectionStatus isLoading={isLoadingCategories} isError={isError} />
|
||||
</>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -55,6 +55,7 @@ function SearchPage() {
|
||||
};
|
||||
|
||||
const onBookingContinue = () => {
|
||||
setAppBookingState(eBookingState.Booking);
|
||||
navigate("/book");
|
||||
};
|
||||
|
||||
@@ -93,7 +94,7 @@ function SearchPage() {
|
||||
const pageContentLoaded = (
|
||||
<div className="2xl:grid 2xl:grid-cols-60/40 2xl:gap-6">
|
||||
<div className="flex flex-col gap-5">{roomList}</div>
|
||||
<Separator className="my-10 2xl:hidden" />
|
||||
<Separator className="2xl:hidden" />
|
||||
<BookingDetails
|
||||
onBookingConfirm={onBookingContinue}
|
||||
mode={"Book"}
|
||||
@@ -102,7 +103,9 @@ function SearchPage() {
|
||||
);
|
||||
|
||||
const getPageContent = () => {
|
||||
const isSearched = appBookingState.state == eBookingState.Searched;
|
||||
const isSearched =
|
||||
appBookingState.state == eBookingState.Searched ||
|
||||
appBookingState.state == eBookingState.Booking;
|
||||
if (!isSearched) {
|
||||
return pageContentSearch;
|
||||
}
|
||||
@@ -121,9 +124,9 @@ function SearchPage() {
|
||||
onSearchClick={onBtSearchClick}
|
||||
initialsearchFilter={appSearchFilter}
|
||||
/>
|
||||
<Separator className="my-10" />
|
||||
<Separator />
|
||||
{getPageContent()}
|
||||
<Separator className="my-10" />
|
||||
<Separator />
|
||||
<BackendConnectionStatus isLoading={isLoading} isError={isError} />
|
||||
</div>
|
||||
);
|
||||
|
||||
@@ -0,0 +1,3 @@
|
||||
|
||||
export const c_somniaRooms_notice_text = "Powered by SomniaRooms, an Open-Source hospitality booking platform!";
|
||||
export const c_sominaRooms_webpage = "https://somnia.dev";
|
||||
@@ -0,0 +1,17 @@
|
||||
import { useAPIConfigVarList } from "@/api/api-configVars";
|
||||
import { getConfigVarFromList } from "./utils";
|
||||
import { eConfigVarKeys } from "@/model/ConfigVar";
|
||||
|
||||
export function useConfigVariable(key: eConfigVarKeys, defaultValue: string) {
|
||||
const { configVarList, isLoading } = useAPIConfigVarList();
|
||||
|
||||
if (isLoading) {
|
||||
return {
|
||||
value: defaultValue,
|
||||
isLoading: true,
|
||||
};
|
||||
}
|
||||
const configVar = getConfigVarFromList(configVarList, key);
|
||||
|
||||
return { value: configVar?.value ?? defaultValue, isLoading: isLoading };
|
||||
}
|
||||
@@ -1,5 +1,6 @@
|
||||
import { AvailabilityFilter, FareFilter } from "@/model/RoomSearch";
|
||||
import { SearchFilter } from "@/model/InternalTypes";
|
||||
import { ConfigVar } from "@/model/ConfigVar";
|
||||
|
||||
export function makeDayUTCDate(date: Date | undefined): Date {
|
||||
if (!date) {
|
||||
@@ -50,3 +51,14 @@ export function makeFareFilterFromSearchFilter(
|
||||
roomCategory: roomCategoryId,
|
||||
};
|
||||
}
|
||||
|
||||
export function getConfigVarFromList(
|
||||
varList: ConfigVar[] | undefined,
|
||||
key: string,
|
||||
): ConfigVar | undefined {
|
||||
if (!varList) {
|
||||
return undefined;
|
||||
}
|
||||
|
||||
return varList.find((v) => v.key === key);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user