Homepage - Initial implementation.

This commit is contained in:
2024-05-05 12:45:59 +02:00
parent 391f2fb3ae
commit e81660914f
13 changed files with 363 additions and 168 deletions
+13
View File
@@ -0,0 +1,13 @@
import { RoomCategory } from "@/model/RoomCategory";
import { fetcherUnwraper } from "./backend";
import useSWR from 'swr';
export function useAPIRoomCategories () {
const { data, error, isLoading } = useSWR<RoomCategory[]>(`/api/room/category`, fetcherUnwraper)
return {
roomCategoryList: data,
isLoading,
isError: error
}
}
+31
View File
@@ -0,0 +1,31 @@
const BACKEND_HOST = import.meta.env.VITE_BACKEND_HOST;
const BACKEND_PORT = import.meta.env.VITE_BACKEND_PORT;
const url = BACKEND_HOST + (BACKEND_PORT == 443 || BACKEND_PORT == 80 ? "" : ":" + BACKEND_PORT)
export function fetcher<T>(endpoint: string): Promise<T> {
return fetch(url + endpoint, {
method: "GET"
})
.then(r => {
if (!r.ok) {
throw new Error(r.statusText)
}
const data = r.json();
return data as Promise<T>;
})
}
export function fetcherUnwraper<T>(endpoint: string): Promise<T> {
return fetch(url + endpoint)
.then(response => {
if (!response.ok) {
throw new Error(response.statusText)
}
return response.json() as Promise<{ results: T }>
})
.then(data => {
return data.results
})
}
+41 -10
View File
@@ -2,30 +2,61 @@ import { Separator } from "../ui/separator"
import { Card, CardContent, CardHeader } from "../ui/card"
import { Typography } from "../ui/typography"
import { BedDoubleIcon, BedSingleIcon, UserIcon } from "lucide-react"
import { RoomCategory, RoomCategoryPhoto } from "@/model/RoomCategory"
import { IconWithTooltipNumber } from "../ui/icon-number-tooltip"
export function RoomItemCard() {
interface RoomItemCardProps {
room: RoomCategory
}
const defaultRoom: RoomCategory = {
id: -1,
name: "Sample Room",
description: "Lorem Ipsum",
bedType: "0,1",
maxGuestNum: 2,
roomCategoryDetails: [],
roomCategoryPhotos: []
}
export function RoomItemCard({ room = defaultRoom }: RoomItemCardProps) {
const mainPhoto: RoomCategoryPhoto | undefined = room.roomCategoryPhotos.find((element) => {
return element.kind = "main"
});
const bedTypeList: number[] = room.bedType.split(",").map((e) => Number.parseInt(e));
const hasSingleBed: boolean = (bedTypeList.at(0) !== undefined && bedTypeList[0] > 0);
const hasDoubleBed: boolean = (bedTypeList.at(1) !== undefined && bedTypeList[1] > 0);
return (
<Card>
<Card id={room.id.toString()}>
<CardHeader className="p-0 m-0 mb-5">
<img src="https://images.unsplash.com/photo-1618773928121-c32242e63f39" className="rounded-md"></img>
<Typography variant={"h2"} as="h1" className="mx-5 py-2">Doble con baño</Typography>
<img src={mainPhoto?.path ?? "https://images.unsplash.com/photo-1618773928121-c32242e63f39"} className="rounded-md"></img>
<Typography variant={"h2"} as="h1" className="mx-5 py-2">{room.name}</Typography>
</CardHeader>
<CardContent>
<Typography variant={"p"} as="p">
Habitación doble con <strong>baño privado</strong>.
{room.description}
</Typography>
<Separator className="my-4" />
<div className="flex justify-around">
<div className="flex flex-row gap-2">
<Typography variant={"p_inline"}>2</Typography>
<BedSingleIcon />
<Typography variant={"p_inline"}>1</Typography>
<BedDoubleIcon />
{hasSingleBed && (
<IconWithTooltipNumber altText="Single bed" amount={bedTypeList[0]}>
<BedSingleIcon />
</IconWithTooltipNumber>
)}
{hasDoubleBed && (
<>
<IconWithTooltipNumber altText="Double bed" amount={bedTypeList[1]}>
<BedDoubleIcon />
</IconWithTooltipNumber>
</>
)}
</div>
<div className="flex flex-row">
<UserIcon />
<Typography variant={"p_inline"} className="ml-3">3</Typography>
<Typography variant={"p_inline"} className="ml-3">{room.maxGuestNum}</Typography>
</div>
</div>
</CardContent>
+1 -1
View File
@@ -2,7 +2,7 @@ import { Separator } from "../ui/separator"
import { Card, CardContent, CardHeader } from "../ui/card"
import { Typography } from "../ui/typography"
import 'react-day-picker/dist/style.css';
import { DatePickerWithRange } from "../ui/DateRangePicker";
import { DatePickerWithRange } from "../ui/dateRange-picker";
import { Button } from "../ui/button";
import { SearchIcon } from "lucide-react";
+1
View File
@@ -63,6 +63,7 @@ export function ThemeProvider({
)
}
// eslint-disable-next-line react-refresh/only-export-components
export const useTheme = () => {
const context = useContext(ThemeProviderContext)
+35
View File
@@ -0,0 +1,35 @@
import { Typography } from "./typography";
import { Tooltip, TooltipContent, TooltipProvider, TooltipTrigger } from "./tooltip";
import { PropsWithChildren } from "react";
interface IconWithTooltipNumberProps {
amount: number | null |undefined,
altText: string | null | undefined
}
export function IconWithTooltipNumber({ amount = undefined, altText = undefined, children }: PropsWithChildren<IconWithTooltipNumberProps>) {
const iconElement = altText ? (
<TooltipProvider>
<Tooltip>
<TooltipTrigger asChild>
{children}
</TooltipTrigger>
<TooltipContent>
<p>{altText}</p>
</TooltipContent>
</Tooltip>
</TooltipProvider>
) : (
<>
{children}
</>
);
return (
<>
{amount && <Typography variant={"p_inline"}>{amount}</Typography>}
{iconElement}
</>
)
}
+1
View File
@@ -78,4 +78,5 @@ const Typography = React.forwardRef<HTMLElement, TypographyProps>(
Typography.displayName = 'Typography';
// eslint-disable-next-line react-refresh/only-export-components
export { Typography, typographyVariants };
+24
View File
@@ -0,0 +1,24 @@
export type RoomCategory = {
id: number,
name: string,
description: string,
bedType: string,
maxGuestNum: number,
roomCategoryPhotos: RoomCategoryPhoto[],
roomCategoryDetails: RoomCategoryDetails[],
}
export type RoomCategoryDetails = {
id: number,
detailsSection: string,
detailValue: string,
}
export type RoomCategoryPhoto = {
id: number,
path: string,
altText: string | undefined,
kind: string,
}
+34 -5
View File
@@ -1,17 +1,46 @@
import { useAPIRoomCategories } from "@/api/api-roomCategories"
import { RoomItemCard } from "@/components/Rooms/room-item-card"
import { SearchFilterCard } from "@/components/Search/SearchFilterCard"
import { Button } from "@/components/ui/button"
import { Separator } from "@/components/ui/separator"
import { Skeleton } from "@/components/ui/skeleton"
import { Typography } from "@/components/ui/typography"
import { RoomCategory } from "@/model/RoomCategory"
function Homepage() {
const { roomCategoryList, isLoading, isError } = useAPIRoomCategories();
const BACKEND_HOST = import.meta.env.VITE_BACKEND_HOST;
const BACKEND_PORT = import.meta.env.VITE_BACKEND_PORT;
const roomList = roomCategoryList?.map((element: RoomCategory) => {
return (
<RoomItemCard key={element.id} room={element}/>
);
});
return (
<div className="bg-background text-foreground">
<Button>Test</Button>
<Separator className="my-10"/>
<Typography variant={"h1"}>Hostal del Val</Typography>
<Separator className="my-10" />
<SearchFilterCard />
<Separator className="my-10"/>
<RoomItemCard />
<Separator className="my-10" />
<div className="grid grid-cols-2 gap-4">
{roomList}
</div>
{isLoading && (
<>
<Typography variant={"h3"} as="h2">Loading...</Typography>
<Skeleton className="w-[400px] h-[200px] mx-auto"/>
</>
)}
<Separator className="my-10" />
<p>
Conected to Back-End at: {BACKEND_HOST} - {BACKEND_PORT}
</p>
<p>
Conection Status: {isError ? "ERROR" : (isLoading ? "LOADING" : "LOADED")}
</p>
</div>
)
}