Compare commits
3
Commits
b034d2e9c9
...
f476f4307a
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
f476f4307a | ||
|
|
dedfc1cdd8 | ||
|
|
2d2992593a |
+2
-1
@@ -1,3 +1,4 @@
|
|||||||
{
|
{
|
||||||
"endOfLine": "lf"
|
"endOfLine": "lf",
|
||||||
|
"plugins": ["prettier-plugin-tailwindcss"]
|
||||||
}
|
}
|
||||||
|
|||||||
Generated
+97
-12
@@ -44,7 +44,8 @@
|
|||||||
"eslint-plugin-react-hooks": "^4.6.0",
|
"eslint-plugin-react-hooks": "^4.6.0",
|
||||||
"eslint-plugin-react-refresh": "^0.4.6",
|
"eslint-plugin-react-refresh": "^0.4.6",
|
||||||
"postcss": "^8.4.38",
|
"postcss": "^8.4.38",
|
||||||
"prettier": "3.3.3",
|
"prettier": "^3.3.3",
|
||||||
|
"prettier-plugin-tailwindcss": "^0.6.6",
|
||||||
"tailwindcss": "^3.4.3",
|
"tailwindcss": "^3.4.3",
|
||||||
"tailwindcss-animate": "^1.0.7",
|
"tailwindcss-animate": "^1.0.7",
|
||||||
"typescript": "^5.2.2",
|
"typescript": "^5.2.2",
|
||||||
@@ -2397,12 +2398,13 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/braces": {
|
"node_modules/braces": {
|
||||||
"version": "3.0.2",
|
"version": "3.0.3",
|
||||||
"resolved": "https://registry.npmjs.org/braces/-/braces-3.0.2.tgz",
|
"resolved": "https://registry.npmjs.org/braces/-/braces-3.0.3.tgz",
|
||||||
"integrity": "sha512-b8um+L1RzM3WDSzvhm6gIz1yfTbBt6YTlcEKAvsmqCZZFw46z626lVj9j1yEPW33H5H+lBQpZMP1k8l+78Ha0A==",
|
"integrity": "sha512-yQbXgO/OSZVD2IsiLlro+7Hf6Q18EJrKSEsdoMzKePKXct3gvD8oLcOQdIzGupr5Fj+EDe8gO/lxc1BzfMpxvA==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
|
"license": "MIT",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"fill-range": "^7.0.1"
|
"fill-range": "^7.1.1"
|
||||||
},
|
},
|
||||||
"engines": {
|
"engines": {
|
||||||
"node": ">=8"
|
"node": ">=8"
|
||||||
@@ -3068,10 +3070,11 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/fill-range": {
|
"node_modules/fill-range": {
|
||||||
"version": "7.0.1",
|
"version": "7.1.1",
|
||||||
"resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.0.1.tgz",
|
"resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.1.1.tgz",
|
||||||
"integrity": "sha512-qOo9F+dMUmC2Lcb4BbVvnKJxTPjCm+RRpe4gDuGrzkL7mEVl/djYSu2OdQ2Pa302N4oqkSg9ir6jaLWJ2USVpQ==",
|
"integrity": "sha512-YsGpe3WHLK8ZYi4tWDg2Jy3ebRz2rXowDxnld4bkQB00cc/1Zw9AWnC0i9ztDJitivtQvaI9KaLyKrc+hBW0yg==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
|
"license": "MIT",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"to-regex-range": "^5.0.1"
|
"to-regex-range": "^5.0.1"
|
||||||
},
|
},
|
||||||
@@ -3414,6 +3417,7 @@
|
|||||||
"resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz",
|
"resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz",
|
||||||
"integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==",
|
"integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
|
"license": "MIT",
|
||||||
"engines": {
|
"engines": {
|
||||||
"node": ">=0.12.0"
|
"node": ">=0.12.0"
|
||||||
}
|
}
|
||||||
@@ -3594,12 +3598,13 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/micromatch": {
|
"node_modules/micromatch": {
|
||||||
"version": "4.0.5",
|
"version": "4.0.8",
|
||||||
"resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.5.tgz",
|
"resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.8.tgz",
|
||||||
"integrity": "sha512-DMy+ERcEW2q8Z2Po+WNXuw3c5YaUSFjAO5GsJqfEl7UjvtIuFKO6ZrKvcItdy98dwFI2N1tg3zNIdKaQT+aNdA==",
|
"integrity": "sha512-PXwfBhYu0hBCPw8Dn0E+WDYb7af3dSLVWKi3HGv84IdF4TyFoC0ysxFd0Goxw7nSv4T/PzEJQxsYsEiFCKo2BA==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
|
"license": "MIT",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"braces": "^3.0.2",
|
"braces": "^3.0.3",
|
||||||
"picomatch": "^2.3.1"
|
"picomatch": "^2.3.1"
|
||||||
},
|
},
|
||||||
"engines": {
|
"engines": {
|
||||||
@@ -4058,6 +4063,85 @@
|
|||||||
"url": "https://github.com/prettier/prettier?sponsor=1"
|
"url": "https://github.com/prettier/prettier?sponsor=1"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/prettier-plugin-tailwindcss": {
|
||||||
|
"version": "0.6.6",
|
||||||
|
"resolved": "https://registry.npmjs.org/prettier-plugin-tailwindcss/-/prettier-plugin-tailwindcss-0.6.6.tgz",
|
||||||
|
"integrity": "sha512-OPva5S7WAsPLEsOuOWXATi13QrCKACCiIonFgIR6V4lYv4QLp++UXVhZSzRbZxXGimkQtQT86CC6fQqTOybGng==",
|
||||||
|
"dev": true,
|
||||||
|
"license": "MIT",
|
||||||
|
"engines": {
|
||||||
|
"node": ">=14.21.3"
|
||||||
|
},
|
||||||
|
"peerDependencies": {
|
||||||
|
"@ianvs/prettier-plugin-sort-imports": "*",
|
||||||
|
"@prettier/plugin-pug": "*",
|
||||||
|
"@shopify/prettier-plugin-liquid": "*",
|
||||||
|
"@trivago/prettier-plugin-sort-imports": "*",
|
||||||
|
"@zackad/prettier-plugin-twig-melody": "*",
|
||||||
|
"prettier": "^3.0",
|
||||||
|
"prettier-plugin-astro": "*",
|
||||||
|
"prettier-plugin-css-order": "*",
|
||||||
|
"prettier-plugin-import-sort": "*",
|
||||||
|
"prettier-plugin-jsdoc": "*",
|
||||||
|
"prettier-plugin-marko": "*",
|
||||||
|
"prettier-plugin-multiline-arrays": "*",
|
||||||
|
"prettier-plugin-organize-attributes": "*",
|
||||||
|
"prettier-plugin-organize-imports": "*",
|
||||||
|
"prettier-plugin-sort-imports": "*",
|
||||||
|
"prettier-plugin-style-order": "*",
|
||||||
|
"prettier-plugin-svelte": "*"
|
||||||
|
},
|
||||||
|
"peerDependenciesMeta": {
|
||||||
|
"@ianvs/prettier-plugin-sort-imports": {
|
||||||
|
"optional": true
|
||||||
|
},
|
||||||
|
"@prettier/plugin-pug": {
|
||||||
|
"optional": true
|
||||||
|
},
|
||||||
|
"@shopify/prettier-plugin-liquid": {
|
||||||
|
"optional": true
|
||||||
|
},
|
||||||
|
"@trivago/prettier-plugin-sort-imports": {
|
||||||
|
"optional": true
|
||||||
|
},
|
||||||
|
"@zackad/prettier-plugin-twig-melody": {
|
||||||
|
"optional": true
|
||||||
|
},
|
||||||
|
"prettier-plugin-astro": {
|
||||||
|
"optional": true
|
||||||
|
},
|
||||||
|
"prettier-plugin-css-order": {
|
||||||
|
"optional": true
|
||||||
|
},
|
||||||
|
"prettier-plugin-import-sort": {
|
||||||
|
"optional": true
|
||||||
|
},
|
||||||
|
"prettier-plugin-jsdoc": {
|
||||||
|
"optional": true
|
||||||
|
},
|
||||||
|
"prettier-plugin-marko": {
|
||||||
|
"optional": true
|
||||||
|
},
|
||||||
|
"prettier-plugin-multiline-arrays": {
|
||||||
|
"optional": true
|
||||||
|
},
|
||||||
|
"prettier-plugin-organize-attributes": {
|
||||||
|
"optional": true
|
||||||
|
},
|
||||||
|
"prettier-plugin-organize-imports": {
|
||||||
|
"optional": true
|
||||||
|
},
|
||||||
|
"prettier-plugin-sort-imports": {
|
||||||
|
"optional": true
|
||||||
|
},
|
||||||
|
"prettier-plugin-style-order": {
|
||||||
|
"optional": true
|
||||||
|
},
|
||||||
|
"prettier-plugin-svelte": {
|
||||||
|
"optional": true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/punycode": {
|
"node_modules/punycode": {
|
||||||
"version": "2.3.1",
|
"version": "2.3.1",
|
||||||
"resolved": "https://registry.npmjs.org/punycode/-/punycode-2.3.1.tgz",
|
"resolved": "https://registry.npmjs.org/punycode/-/punycode-2.3.1.tgz",
|
||||||
@@ -4701,6 +4785,7 @@
|
|||||||
"resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz",
|
"resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz",
|
||||||
"integrity": "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==",
|
"integrity": "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
|
"license": "MIT",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"is-number": "^7.0.0"
|
"is-number": "^7.0.0"
|
||||||
},
|
},
|
||||||
|
|||||||
+4
-2
@@ -6,7 +6,8 @@
|
|||||||
"scripts": {
|
"scripts": {
|
||||||
"dev": "vite",
|
"dev": "vite",
|
||||||
"build": "tsc && vite build",
|
"build": "tsc && vite build",
|
||||||
"lint": "eslint . --ext ts,tsx --report-unused-disable-directives --max-warnings 0",
|
"lint": "eslint . --ext ts,tsx --report-unused-disable-directives --max-warnings 0 && npx prettier . --check",
|
||||||
|
"pretty": "npx prettier . --write",
|
||||||
"preview": "vite preview"
|
"preview": "vite preview"
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
@@ -46,7 +47,8 @@
|
|||||||
"eslint-plugin-react-hooks": "^4.6.0",
|
"eslint-plugin-react-hooks": "^4.6.0",
|
||||||
"eslint-plugin-react-refresh": "^0.4.6",
|
"eslint-plugin-react-refresh": "^0.4.6",
|
||||||
"postcss": "^8.4.38",
|
"postcss": "^8.4.38",
|
||||||
"prettier": "3.3.3",
|
"prettier": "^3.3.3",
|
||||||
|
"prettier-plugin-tailwindcss": "^0.6.6",
|
||||||
"tailwindcss": "^3.4.3",
|
"tailwindcss": "^3.4.3",
|
||||||
"tailwindcss-animate": "^1.0.7",
|
"tailwindcss-animate": "^1.0.7",
|
||||||
"typescript": "^5.2.2",
|
"typescript": "^5.2.2",
|
||||||
|
|||||||
@@ -1,8 +1,7 @@
|
|||||||
import { useAPIRoomCategoryId } from "@/api/api-roomCategories";
|
import { useAPIRoomCategoryId } from "@/api/api-roomCategories";
|
||||||
import { Card, CardContent, CardHeader } from "../ui/card";
|
import { Card, CardContent } from "../ui/card";
|
||||||
import { useMemo, useState } from "react";
|
import { useMemo } from "react";
|
||||||
import { Typography } from "../ui/typography";
|
import { Typography } from "../ui/typography";
|
||||||
import { RoomCategoryPhoto } from "@/model/RoomCategory";
|
|
||||||
import {
|
import {
|
||||||
Table,
|
Table,
|
||||||
TableBody,
|
TableBody,
|
||||||
@@ -17,7 +16,10 @@ import { DateRange } from "react-day-picker";
|
|||||||
import { FareFilter } from "@/model/RoomSearch";
|
import { FareFilter } from "@/model/RoomSearch";
|
||||||
import { useAPIRoomPrices } from "@/api/api-roomSearch";
|
import { useAPIRoomPrices } from "@/api/api-roomSearch";
|
||||||
import { NumberSelector } from "../ui/number-selector";
|
import { NumberSelector } from "../ui/number-selector";
|
||||||
import { NewBookingRoom } from "@/model/Bookings";
|
import { NewBooking, NewBookingRoom } from "@/model/Bookings";
|
||||||
|
import { RoomItemCard } from "./room-item-card";
|
||||||
|
import { IconWithTooltip } from "../ui/icon-number-tooltip";
|
||||||
|
import { UserIcon } from "lucide-react";
|
||||||
|
|
||||||
function RoomBooking({
|
function RoomBooking({
|
||||||
roomCategoryId,
|
roomCategoryId,
|
||||||
@@ -26,14 +28,13 @@ function RoomBooking({
|
|||||||
roomCategoryId: number;
|
roomCategoryId: number;
|
||||||
availableAmount: number;
|
availableAmount: number;
|
||||||
}) {
|
}) {
|
||||||
const [selectedAmount, setSelectedAmount] = useState<Map<number, number>>(
|
|
||||||
new Map(),
|
|
||||||
);
|
|
||||||
|
|
||||||
const currentDates: DateRange = useAppStore(
|
const currentDates: DateRange = useAppStore(
|
||||||
(state) => state.searchFilter.dateRange,
|
(state) => state.searchFilter.dateRange,
|
||||||
);
|
);
|
||||||
const addNewRoom = useAppStore((state) => state.addNewBookingRoom);
|
const addNewRoom = useAppStore((state) => state.addNewBookingRoom);
|
||||||
|
const bookingRooms: NewBooking | null = useAppStore(
|
||||||
|
(state) => state.newBookingData,
|
||||||
|
);
|
||||||
|
|
||||||
function onSelectedAmountUpdate(
|
function onSelectedAmountUpdate(
|
||||||
guestNumber: number,
|
guestNumber: number,
|
||||||
@@ -49,10 +50,6 @@ function RoomBooking({
|
|||||||
newCount,
|
newCount,
|
||||||
);
|
);
|
||||||
|
|
||||||
const newAmountMap = new Map(selectedAmount);
|
|
||||||
newAmountMap.set(guestNumber, newCount);
|
|
||||||
setSelectedAmount(newAmountMap);
|
|
||||||
|
|
||||||
const newRoom: NewBookingRoom = {
|
const newRoom: NewBookingRoom = {
|
||||||
roomCategory: roomCategoryId,
|
roomCategory: roomCategoryId,
|
||||||
guestNumber: guestNumber,
|
guestNumber: guestNumber,
|
||||||
@@ -64,7 +61,10 @@ function RoomBooking({
|
|||||||
|
|
||||||
const totalSelected = () => {
|
const totalSelected = () => {
|
||||||
let sum = 0;
|
let sum = 0;
|
||||||
selectedAmount.forEach((v) => (sum += v));
|
bookingRooms &&
|
||||||
|
bookingRooms.rooms
|
||||||
|
.filter((v) => v.roomCategory == roomCategoryId)
|
||||||
|
.forEach((v) => (sum += v.amount));
|
||||||
return sum;
|
return sum;
|
||||||
};
|
};
|
||||||
const canAddMore: boolean = availableAmount > totalSelected();
|
const canAddMore: boolean = availableAmount > totalSelected();
|
||||||
@@ -93,12 +93,11 @@ function RoomBooking({
|
|||||||
isError: isErrorPrices,
|
isError: isErrorPrices,
|
||||||
} = useAPIRoomPrices(fareFilter);
|
} = useAPIRoomPrices(fareFilter);
|
||||||
|
|
||||||
//TODOME: Gestionar también los estados al cargar los precios.
|
|
||||||
const isLoading = isLoadingCategory || isLoadingPrices;
|
const isLoading = isLoadingCategory || isLoadingPrices;
|
||||||
const isError = isErrorCategory || isErrorPrices;
|
const isError = isErrorCategory || isErrorPrices;
|
||||||
|
|
||||||
if (isLoading) {
|
if (isLoading) {
|
||||||
return <Skeleton className="w-[600px] h-[150px] mx-auto" />;
|
return <Skeleton className="mx-auto h-[150px] w-[600px]" />;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (isError) {
|
if (isError) {
|
||||||
@@ -109,23 +108,21 @@ function RoomBooking({
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
const mainPhoto: RoomCategoryPhoto | undefined =
|
|
||||||
roomCategory?.roomCategoryPhotos.filter((p) => p.kind == "main")[0];
|
|
||||||
const photoElement = mainPhoto && (
|
|
||||||
<img
|
|
||||||
src={mainPhoto.path}
|
|
||||||
alt={mainPhoto.altText}
|
|
||||||
className="rounded-md mx-0 px-0"
|
|
||||||
></img>
|
|
||||||
);
|
|
||||||
|
|
||||||
const hasPrices = roomPriceList != null && roomPriceList?.length > 0;
|
const hasPrices = roomPriceList != null && roomPriceList?.length > 0;
|
||||||
let priceTable = null;
|
let priceTable = null;
|
||||||
if (hasPrices) {
|
if (hasPrices) {
|
||||||
const tableRows = roomPriceList?.map((el) => {
|
const tableRows = roomPriceList?.map((el) => {
|
||||||
return (
|
return (
|
||||||
<TableRow key={el.guestNumber}>
|
<TableRow key={el.guestNumber}>
|
||||||
<TableCell>{el.guestNumber}</TableCell>
|
<TableCell>
|
||||||
|
<IconWithTooltip
|
||||||
|
label={el.guestNumber?.toString()}
|
||||||
|
tooltipText={"Guest Number"}
|
||||||
|
labelPosition={"right"}
|
||||||
|
>
|
||||||
|
<UserIcon></UserIcon>
|
||||||
|
</IconWithTooltip>
|
||||||
|
</TableCell>
|
||||||
<TableCell>{el.price / 100} €</TableCell>
|
<TableCell>{el.price / 100} €</TableCell>
|
||||||
<TableCell>
|
<TableCell>
|
||||||
<NumberSelector
|
<NumberSelector
|
||||||
@@ -140,51 +137,46 @@ function RoomBooking({
|
|||||||
});
|
});
|
||||||
|
|
||||||
priceTable = (
|
priceTable = (
|
||||||
<>
|
<Table className="overflow-hidden">
|
||||||
<Table>
|
<TableHeader>
|
||||||
<TableHeader>
|
<TableRow>
|
||||||
<TableRow>
|
<TableHead>Guest number</TableHead>
|
||||||
<TableHead>Guest number</TableHead>
|
<TableHead>Price</TableHead>
|
||||||
<TableHead>Price</TableHead>
|
<TableHead>Amount</TableHead>
|
||||||
<TableHead>Amount</TableHead>
|
</TableRow>
|
||||||
</TableRow>
|
</TableHeader>
|
||||||
</TableHeader>
|
<TableBody>
|
||||||
<TableBody>
|
{isLoadingPrices ? <Skeleton className="h-full w-full" /> : tableRows}
|
||||||
{isLoadingPrices ? (
|
</TableBody>
|
||||||
<Skeleton className="w-[80%] h-[100%]" />
|
</Table>
|
||||||
) : (
|
);
|
||||||
tableRows
|
}
|
||||||
)}
|
|
||||||
</TableBody>
|
let roomCard = null;
|
||||||
</Table>
|
if (roomCategory) {
|
||||||
<p>Rooms Available: {availableAmount}</p>
|
roomCard = (
|
||||||
</>
|
<RoomItemCard
|
||||||
|
room={roomCategory}
|
||||||
|
key={roomCategoryId}
|
||||||
|
className="z-20 border-4"
|
||||||
|
></RoomItemCard>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Card>
|
<div className="relative sm:grid sm:grid-cols-40/60">
|
||||||
<CardHeader>
|
{roomCard}
|
||||||
<div className="flex flex-row gap-5 w-full">
|
<Card className="relative mx-auto w-[95%] -translate-y-2 self-center rounded-none rounded-b-xl sm:mx-0 sm:h-4/5 sm:w-full sm:-translate-x-2 sm:translate-y-0 sm:rounded-e-xl">
|
||||||
<div className="w-[40%] flex-none">{photoElement}</div>
|
<CardContent className="flex h-full w-full flex-col justify-center gap-4 p-5">
|
||||||
<div>
|
{hasPrices ? (
|
||||||
<Typography variant={"h3"} as="h3">
|
priceTable
|
||||||
{roomCategory?.name}
|
) : (
|
||||||
</Typography>
|
<Typography variant={"largeText"}>No Prices available</Typography>
|
||||||
<Typography>{roomCategory?.description}</Typography>
|
)}
|
||||||
</div>
|
<p>Rooms Available: {availableAmount}</p>
|
||||||
</div>
|
</CardContent>
|
||||||
</CardHeader>
|
</Card>
|
||||||
<CardContent>
|
</div>
|
||||||
{hasPrices ? (
|
|
||||||
priceTable
|
|
||||||
) : (
|
|
||||||
<Typography variant={"largeText"}>No Prices available</Typography>
|
|
||||||
)}
|
|
||||||
<div></div>
|
|
||||||
<p>Selected: {totalSelected()}</p>
|
|
||||||
</CardContent>
|
|
||||||
</Card>
|
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,12 +1,15 @@
|
|||||||
import { Separator } from "../ui/separator";
|
import { Separator } from "../ui/separator";
|
||||||
import { Card, CardContent, CardHeader } from "../ui/card";
|
import { Card, CardContent } from "../ui/card";
|
||||||
import { Typography } from "../ui/typography";
|
import { Typography } from "../ui/typography";
|
||||||
import { BedDoubleIcon, BedSingleIcon, UserIcon } from "lucide-react";
|
import { BedDoubleIcon, BedSingleIcon, UserIcon } from "lucide-react";
|
||||||
import { RoomCategory, RoomCategoryPhoto } from "@/model/RoomCategory";
|
import { RoomCategory, RoomCategoryPhoto } from "@/model/RoomCategory";
|
||||||
import { IconWithTooltipNumber } from "../ui/icon-number-tooltip";
|
import { IconWithTooltip } from "../ui/icon-number-tooltip";
|
||||||
|
import roomImg from "@/res/SomniaRoomImage.svg";
|
||||||
|
import { cn } from "@/lib/utils";
|
||||||
|
|
||||||
interface RoomItemCardProps {
|
interface RoomItemCardProps {
|
||||||
room: RoomCategory;
|
room: RoomCategory;
|
||||||
|
className?: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
const defaultRoom: RoomCategory = {
|
const defaultRoom: RoomCategory = {
|
||||||
@@ -19,7 +22,10 @@ const defaultRoom: RoomCategory = {
|
|||||||
roomCategoryPhotos: [],
|
roomCategoryPhotos: [],
|
||||||
};
|
};
|
||||||
|
|
||||||
export function RoomItemCard({ room = defaultRoom }: RoomItemCardProps) {
|
export function RoomItemCard({
|
||||||
|
room = defaultRoom,
|
||||||
|
className,
|
||||||
|
}: RoomItemCardProps) {
|
||||||
const mainPhoto: RoomCategoryPhoto | undefined = room.roomCategoryPhotos.find(
|
const mainPhoto: RoomCategoryPhoto | undefined = room.roomCategoryPhotos.find(
|
||||||
(element) => {
|
(element) => {
|
||||||
return (element.kind = "main");
|
return (element.kind = "main");
|
||||||
@@ -34,50 +40,59 @@ export function RoomItemCard({ room = defaultRoom }: RoomItemCardProps) {
|
|||||||
bedTypeList.at(1) !== undefined && bedTypeList[1] > 0;
|
bedTypeList.at(1) !== undefined && bedTypeList[1] > 0;
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Card id={room.id.toString()}>
|
<Card id={room.id.toString()} className={cn(className, "relative")}>
|
||||||
<CardHeader className="p-0 m-0 mb-5">
|
{/* <CardHeader className="p-0 m-0 mb-5">
|
||||||
<img
|
</CardHeader> */}
|
||||||
src={
|
<CardContent className="flex h-full w-full flex-col p-0">
|
||||||
mainPhoto?.path ??
|
<div className="">
|
||||||
"https://images.unsplash.com/photo-1618773928121-c32242e63f39"
|
<img
|
||||||
}
|
src={mainPhoto?.path ?? roomImg}
|
||||||
className="rounded-md"
|
className="aspect-[4/3] w-full rounded-t-md object-cover"
|
||||||
></img>
|
></img>
|
||||||
<Typography variant={"h2"} as="h1" className="mx-5 py-2">
|
<Separator className="my-0 border-2" />
|
||||||
{room.name}
|
</div>
|
||||||
</Typography>
|
<div>
|
||||||
</CardHeader>
|
<Typography variant={"h2"} as="h2" className="border-none px-4 pt-4">
|
||||||
<CardContent>
|
{room.name}
|
||||||
<Typography variant={"p"} as="p">
|
</Typography>
|
||||||
{room.description}
|
</div>
|
||||||
</Typography>
|
<div>
|
||||||
<Separator className="my-4" />
|
<Typography variant={"p"} as="h2" className="px-5">
|
||||||
<div className="flex justify-around">
|
{room.description}
|
||||||
<div className="flex flex-row gap-2">
|
</Typography>
|
||||||
{hasSingleBed && (
|
</div>
|
||||||
<IconWithTooltipNumber
|
<div className="mt-auto pb-4">
|
||||||
altText="Single bed"
|
<Separator className="my-4" />
|
||||||
amount={bedTypeList[0]}
|
<div className="flex justify-around">
|
||||||
>
|
<div className="flex flex-row gap-2">
|
||||||
<BedSingleIcon />
|
{hasSingleBed && (
|
||||||
</IconWithTooltipNumber>
|
<IconWithTooltip
|
||||||
)}
|
tooltipText="Single bed"
|
||||||
{hasDoubleBed && (
|
label={bedTypeList[0]?.toString()}
|
||||||
<>
|
|
||||||
<IconWithTooltipNumber
|
|
||||||
altText="Double bed"
|
|
||||||
amount={bedTypeList[1]}
|
|
||||||
>
|
>
|
||||||
<BedDoubleIcon />
|
<BedSingleIcon />
|
||||||
</IconWithTooltipNumber>
|
</IconWithTooltip>
|
||||||
</>
|
)}
|
||||||
)}
|
{hasDoubleBed && (
|
||||||
</div>
|
<>
|
||||||
<div className="flex flex-row">
|
<IconWithTooltip
|
||||||
<UserIcon />
|
tooltipText="Double bed"
|
||||||
<Typography variant={"p_inline"} className="ml-3">
|
label={bedTypeList[1]?.toString()}
|
||||||
{room.maxGuestNum}
|
>
|
||||||
</Typography>
|
<BedDoubleIcon />
|
||||||
|
</IconWithTooltip>
|
||||||
|
</>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
<div className="flex flex-row">
|
||||||
|
<IconWithTooltip
|
||||||
|
tooltipText="Max. gests"
|
||||||
|
label={room.maxGuestNum?.toString()}
|
||||||
|
labelPosition={"right"}
|
||||||
|
>
|
||||||
|
<UserIcon />
|
||||||
|
</IconWithTooltip>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</CardContent>
|
</CardContent>
|
||||||
|
|||||||
@@ -5,12 +5,11 @@ import "react-day-picker/dist/style.css";
|
|||||||
import { DatePickerWithRange } from "../ui/dateRange-picker";
|
import { DatePickerWithRange } from "../ui/dateRange-picker";
|
||||||
import { Button } from "../ui/button";
|
import { Button } from "../ui/button";
|
||||||
import { SearchIcon } from "lucide-react";
|
import { SearchIcon } from "lucide-react";
|
||||||
import { useEffect, useState } from "react";
|
import { useState } from "react";
|
||||||
import { SearchFilter } from "@/model/SearchFilter";
|
import { SearchFilter } from "@/model/SearchFilter";
|
||||||
import { DateRange } from "react-day-picker";
|
import { DateRange } from "react-day-picker";
|
||||||
import { useToast } from "../ui/use-toast";
|
import { useToast } from "../ui/use-toast";
|
||||||
import { useAppStore } from "@/store";
|
import { useAppStore } from "@/store";
|
||||||
import { useLocation } from "react-router-dom";
|
|
||||||
|
|
||||||
type funOnSearchClick = (newFilter: SearchFilter) => void;
|
type funOnSearchClick = (newFilter: SearchFilter) => void;
|
||||||
|
|
||||||
@@ -29,10 +28,10 @@ export function SearchFilterCard({
|
|||||||
|
|
||||||
//On location change, try to load searchFilter from state.
|
//On location change, try to load searchFilter from state.
|
||||||
//FIXME: Si llegamos sin state, hay que borrar el state global...
|
//FIXME: Si llegamos sin state, hay que borrar el state global...
|
||||||
const location = useLocation();
|
// const location = useLocation();
|
||||||
useEffect(() => {
|
// useEffect(() => {
|
||||||
setSearchFilter(location.state);
|
// setSearchFilter(location.state);
|
||||||
}, [location]);
|
// }, [location]);
|
||||||
|
|
||||||
const { toast } = useToast();
|
const { toast } = useToast();
|
||||||
|
|
||||||
@@ -56,8 +55,9 @@ export function SearchFilterCard({
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
setAppSearchFilter(searchFilter);
|
||||||
|
|
||||||
if (onSearchClick) {
|
if (onSearchClick) {
|
||||||
setAppSearchFilter(searchFilter);
|
|
||||||
onSearchClick(searchFilter);
|
onSearchClick(searchFilter);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
@@ -68,8 +68,6 @@ export function SearchFilterCard({
|
|||||||
...searchFilter,
|
...searchFilter,
|
||||||
dateRange: newRange,
|
dateRange: newRange,
|
||||||
});
|
});
|
||||||
|
|
||||||
console.log("SearchFilterCard filter UPDATED");
|
|
||||||
};
|
};
|
||||||
|
|
||||||
const datePickerElement =
|
const datePickerElement =
|
||||||
@@ -89,7 +87,7 @@ export function SearchFilterCard({
|
|||||||
|
|
||||||
return (
|
return (
|
||||||
<Card>
|
<Card>
|
||||||
<CardHeader className="p-0 m-0 mb-5">
|
<CardHeader className="m-0 mb-5 p-0">
|
||||||
<Typography variant={"h2"} as="h1" className="mx-5 py-2">
|
<Typography variant={"h2"} as="h1" className="mx-5 py-2">
|
||||||
Reserva ahora
|
Reserva ahora
|
||||||
</Typography>
|
</Typography>
|
||||||
@@ -98,7 +96,7 @@ export function SearchFilterCard({
|
|||||||
{datePickerElement}
|
{datePickerElement}
|
||||||
<Separator className="my-4" />
|
<Separator className="my-4" />
|
||||||
<Button size={"lg"} onClick={btSearchClick}>
|
<Button size={"lg"} onClick={btSearchClick}>
|
||||||
<SearchIcon className="text-primary-foreground me-2" />
|
<SearchIcon className="me-2 text-primary-foreground" />
|
||||||
<Typography variant={"h3"} className="text-primary-foreground">
|
<Typography variant={"h3"} className="text-primary-foreground">
|
||||||
Buscar
|
Buscar
|
||||||
</Typography>
|
</Typography>
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
function AppFooter() {
|
function AppFooter() {
|
||||||
return (
|
return (
|
||||||
<div className="bg-secondary flex-col hidden">
|
<div className="hidden flex-col bg-secondary">
|
||||||
<div className="">Created with Love by me :)</div>
|
<div className="">Created with Love by me :)</div>
|
||||||
<div className="">
|
<div className="">
|
||||||
<p>Más información</p>
|
<p>Más información</p>
|
||||||
|
|||||||
@@ -7,15 +7,15 @@ import { BookAIcon, HomeIcon } from "lucide-react";
|
|||||||
|
|
||||||
function AppHeader() {
|
function AppHeader() {
|
||||||
return (
|
return (
|
||||||
<Card className="flex items-center mx-10 my-4">
|
<Card className="mx-10 my-4 flex items-center">
|
||||||
<CardContent className="w-full py-2">
|
<CardContent className="w-full py-2">
|
||||||
<div className="flex justify-around items-center">
|
<div className="flex items-center justify-around">
|
||||||
<div className="flex flex-row items-center gap-2 relative">
|
<div className="relative flex flex-row items-center gap-2">
|
||||||
<img src="public/SR2.svg" className="w-[64px]"></img>
|
<img src="public/SR2.svg" className="w-[64px]"></img>
|
||||||
<Typography variant={"largeText"}>Somnia Rooms</Typography>
|
<Typography variant={"largeText"}>Somnia Rooms</Typography>
|
||||||
<a
|
<a
|
||||||
href="https://duck.com"
|
href="https://duck.com"
|
||||||
className="absolute w-[100%] h-[100%]"
|
className="absolute h-[100%] w-[100%]"
|
||||||
></a>
|
></a>
|
||||||
</div>
|
</div>
|
||||||
<Button variant="ghost" className="h-15">
|
<Button variant="ghost" className="h-15">
|
||||||
|
|||||||
@@ -3,15 +3,10 @@ import { Card, CardContent, CardHeader } from "../ui/card";
|
|||||||
import { Typography } from "../ui/typography";
|
import { Typography } from "../ui/typography";
|
||||||
import { differenceInDays } from "date-fns";
|
import { differenceInDays } from "date-fns";
|
||||||
import { Separator } from "../ui/separator";
|
import { Separator } from "../ui/separator";
|
||||||
import {
|
|
||||||
Table,
|
|
||||||
TableBody,
|
|
||||||
TableCell,
|
|
||||||
TableHead,
|
|
||||||
TableHeader,
|
|
||||||
TableRow,
|
|
||||||
} from "../ui/table";
|
|
||||||
import { Button } from "../ui/button";
|
import { Button } from "../ui/button";
|
||||||
|
import { useAPIRoomCategories } from "@/api/api-roomCategories";
|
||||||
|
import { IconWithTooltip } from "../ui/icon-number-tooltip";
|
||||||
|
import { UserIcon } from "lucide-react";
|
||||||
|
|
||||||
type OnBookingClickFunction = () => void;
|
type OnBookingClickFunction = () => void;
|
||||||
type BookingDetailsModes = "Book" | "View";
|
type BookingDetailsModes = "Book" | "View";
|
||||||
@@ -24,6 +19,7 @@ export function BookingDetails({
|
|||||||
mode: BookingDetailsModes;
|
mode: BookingDetailsModes;
|
||||||
}) {
|
}) {
|
||||||
const bookingData = useAppStore((state) => state.newBookingData);
|
const bookingData = useAppStore((state) => state.newBookingData);
|
||||||
|
const roomCategoryList = useAPIRoomCategories();
|
||||||
|
|
||||||
const onBtBookingClick = () => {
|
const onBtBookingClick = () => {
|
||||||
if (onBookingConfirm) {
|
if (onBookingConfirm) {
|
||||||
@@ -47,32 +43,46 @@ export function BookingDetails({
|
|||||||
}
|
}
|
||||||
|
|
||||||
const roomList = bookingData.rooms.map((room) => {
|
const roomList = bookingData.rooms.map((room) => {
|
||||||
|
const category = roomCategoryList.roomCategoryList
|
||||||
|
?.filter((c) => c.id == room.roomCategory)
|
||||||
|
.at(0);
|
||||||
|
const photo = category?.roomCategoryPhotos.at(0);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<TableRow key={room.roomCategory + "_" + room.guestNumber}>
|
<Card
|
||||||
<TableCell>{room.roomCategory}</TableCell>
|
key={room.roomCategory.toString() + room.guestNumber.toString()}
|
||||||
<TableCell>{room.guestNumber}</TableCell>
|
className="my-3 flex flex-row bg-accent"
|
||||||
<TableCell>{room.amount}</TableCell>
|
>
|
||||||
<TableCell>{room.price / 100} €</TableCell>
|
{photo && (
|
||||||
<TableCell>{(room.price * room.amount) / 100} €</TableCell>
|
<img
|
||||||
</TableRow>
|
src={photo.path}
|
||||||
|
className="max-w-[30%] rounded-s-xl object-cover"
|
||||||
|
/>
|
||||||
|
)}
|
||||||
|
<div className="w-full p-3">
|
||||||
|
<Typography variant={"h3"} as="h3" className="m-2 text-center">
|
||||||
|
{category?.name}
|
||||||
|
</Typography>
|
||||||
|
<div className="flex w-full flex-row justify-around">
|
||||||
|
<IconWithTooltip
|
||||||
|
label={room.guestNumber.toString()}
|
||||||
|
labelPosition={"right"}
|
||||||
|
tooltipText={"Guest Number"}
|
||||||
|
>
|
||||||
|
<UserIcon />
|
||||||
|
</IconWithTooltip>
|
||||||
|
<Typography variant={"largeText"}>
|
||||||
|
{room.amount} x {room.price / 100}€
|
||||||
|
</Typography>
|
||||||
|
<Typography variant={"largeText"}>
|
||||||
|
{(room.amount * room.price) / 100}€
|
||||||
|
</Typography>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</Card>
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
|
||||||
const roomTable = (
|
|
||||||
<Table>
|
|
||||||
<TableHeader>
|
|
||||||
<TableRow>
|
|
||||||
<TableHead>Room</TableHead>
|
|
||||||
<TableHead>Guests</TableHead>
|
|
||||||
<TableHead>Quantity</TableHead>
|
|
||||||
<TableHead>Unit price</TableHead>
|
|
||||||
<TableHead>Total price</TableHead>
|
|
||||||
</TableRow>
|
|
||||||
</TableHeader>
|
|
||||||
<TableBody>{roomList}</TableBody>
|
|
||||||
</Table>
|
|
||||||
);
|
|
||||||
|
|
||||||
const totalPrice = bookingData.rooms.reduce(
|
const totalPrice = bookingData.rooms.reduce(
|
||||||
(acc, room) => acc + room.price * room.amount,
|
(acc, room) => acc + room.price * room.amount,
|
||||||
0,
|
0,
|
||||||
@@ -91,11 +101,14 @@ export function BookingDetails({
|
|||||||
Arrival: {bookingData.arrivalDate.toLocaleDateString()} - Departure:{" "}
|
Arrival: {bookingData.arrivalDate.toLocaleDateString()} - Departure:{" "}
|
||||||
{bookingData.departureDate.toLocaleDateString()} <br></br>
|
{bookingData.departureDate.toLocaleDateString()} <br></br>
|
||||||
Number of nights:{" "}
|
Number of nights:{" "}
|
||||||
{differenceInDays(bookingData.departureDate, bookingData.arrivalDate)}
|
{differenceInDays(
|
||||||
|
bookingData.departureDate,
|
||||||
|
bookingData.arrivalDate,
|
||||||
|
) + 1}
|
||||||
</Typography>
|
</Typography>
|
||||||
<Separator className="my-2"></Separator>
|
<Separator className="my-2"></Separator>
|
||||||
<Typography variant={"h3"}>Your Rooms</Typography>
|
<Typography variant={"h3"}>Your Rooms</Typography>
|
||||||
{roomTable}
|
<div className="">{roomList}</div>
|
||||||
<Separator className="my-2"></Separator>
|
<Separator className="my-2"></Separator>
|
||||||
<Typography variant={"h3"}>Total Price</Typography>
|
<Typography variant={"h3"}>Total Price</Typography>
|
||||||
<Typography variant={"h3"}>{totalPrice / 100} €</Typography>
|
<Typography variant={"h3"}>{totalPrice / 100} €</Typography>
|
||||||
|
|||||||
@@ -204,7 +204,7 @@ const CarouselPrevious = React.forwardRef<
|
|||||||
variant={variant}
|
variant={variant}
|
||||||
size={size}
|
size={size}
|
||||||
className={cn(
|
className={cn(
|
||||||
"absolute h-8 w-8 rounded-full",
|
"absolute h-8 w-8 rounded-full",
|
||||||
orientation === "horizontal"
|
orientation === "horizontal"
|
||||||
? "-left-12 top-1/2 -translate-y-1/2"
|
? "-left-12 top-1/2 -translate-y-1/2"
|
||||||
: "-top-12 left-1/2 -translate-x-1/2 rotate-90",
|
: "-top-12 left-1/2 -translate-x-1/2 rotate-90",
|
||||||
|
|||||||
@@ -46,13 +46,10 @@ export function DatePickerWithRange({
|
|||||||
const handleDayClickFrom = (day: Date, mods: ActiveModifiers) => {
|
const handleDayClickFrom = (day: Date, mods: ActiveModifiers) => {
|
||||||
const newDateRange: DateRange = {
|
const newDateRange: DateRange = {
|
||||||
from: mods.selected ? undefined : day,
|
from: mods.selected ? undefined : day,
|
||||||
to: dateRange?.to,
|
to: undefined,
|
||||||
};
|
};
|
||||||
|
|
||||||
setDateRange(newDateRange);
|
fnUpdateRange(newDateRange);
|
||||||
if (updateDateRange !== undefined) {
|
|
||||||
updateDateRange(newDateRange);
|
|
||||||
}
|
|
||||||
};
|
};
|
||||||
|
|
||||||
const handleDayClickTo = (day: Date, mods: ActiveModifiers) => {
|
const handleDayClickTo = (day: Date, mods: ActiveModifiers) => {
|
||||||
@@ -61,10 +58,13 @@ export function DatePickerWithRange({
|
|||||||
to: mods.selected ? undefined : day,
|
to: mods.selected ? undefined : day,
|
||||||
};
|
};
|
||||||
|
|
||||||
setDateRange(newDateRange);
|
fnUpdateRange(newDateRange);
|
||||||
|
};
|
||||||
|
|
||||||
|
const fnUpdateRange = (range: DateRange) => {
|
||||||
|
setDateRange(range);
|
||||||
if (updateDateRange !== undefined) {
|
if (updateDateRange !== undefined) {
|
||||||
updateDateRange(newDateRange);
|
updateDateRange(range);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -82,7 +82,7 @@ export function DatePickerWithRange({
|
|||||||
}
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className={cn("grid gap-x-2 grid-cols-2 grid-rows-2", className)}>
|
<div className={cn("grid grid-cols-2 grid-rows-2 gap-x-2", className)}>
|
||||||
<Typography variant={"largeText"} as="p">
|
<Typography variant={"largeText"} as="p">
|
||||||
Desde
|
Desde
|
||||||
</Typography>
|
</Typography>
|
||||||
@@ -107,7 +107,7 @@ export function DatePickerWithRange({
|
|||||||
)}
|
)}
|
||||||
</Button>
|
</Button>
|
||||||
</PopoverTrigger>
|
</PopoverTrigger>
|
||||||
<PopoverContent className="w-auto p-0" align="start">
|
<PopoverContent className="w-auto p-0" align="center">
|
||||||
<Calendar
|
<Calendar
|
||||||
initialFocus
|
initialFocus
|
||||||
mode="single"
|
mode="single"
|
||||||
@@ -137,11 +137,11 @@ export function DatePickerWithRange({
|
|||||||
)}
|
)}
|
||||||
</Button>
|
</Button>
|
||||||
</PopoverTrigger>
|
</PopoverTrigger>
|
||||||
<PopoverContent className="w-auto p-0" align="start">
|
<PopoverContent className="w-auto p-0" align="center">
|
||||||
<Calendar
|
<Calendar
|
||||||
initialFocus
|
initialFocus
|
||||||
mode="single"
|
mode="single"
|
||||||
defaultMonth={dateRange?.to}
|
defaultMonth={dateRange?.to ?? dateRange?.from}
|
||||||
selected={dateRange?.to}
|
selected={dateRange?.to}
|
||||||
onDayClick={handleDayClickTo}
|
onDayClick={handleDayClickTo}
|
||||||
numberOfMonths={1}
|
numberOfMonths={1}
|
||||||
|
|||||||
@@ -8,21 +8,23 @@ import {
|
|||||||
import { PropsWithChildren } from "react";
|
import { PropsWithChildren } from "react";
|
||||||
|
|
||||||
interface IconWithTooltipNumberProps {
|
interface IconWithTooltipNumberProps {
|
||||||
amount?: number | null | undefined;
|
label?: string | null | undefined;
|
||||||
altText?: string | null | undefined;
|
labelPosition?: "left" | "right" | null | undefined;
|
||||||
|
tooltipText?: string | null | undefined;
|
||||||
}
|
}
|
||||||
|
|
||||||
export function IconWithTooltipNumber({
|
export function IconWithTooltip({
|
||||||
amount = undefined,
|
label = undefined,
|
||||||
altText = undefined,
|
labelPosition = "left",
|
||||||
|
tooltipText = undefined,
|
||||||
children,
|
children,
|
||||||
}: PropsWithChildren<IconWithTooltipNumberProps>) {
|
}: PropsWithChildren<IconWithTooltipNumberProps>) {
|
||||||
const iconElement = altText ? (
|
const iconElement = tooltipText ? (
|
||||||
<TooltipProvider>
|
<TooltipProvider>
|
||||||
<Tooltip>
|
<Tooltip>
|
||||||
<TooltipTrigger asChild>{children}</TooltipTrigger>
|
<TooltipTrigger asChild>{children}</TooltipTrigger>
|
||||||
<TooltipContent>
|
<TooltipContent>
|
||||||
<p>{altText}</p>
|
<p className="inline">{tooltipText}</p>
|
||||||
</TooltipContent>
|
</TooltipContent>
|
||||||
</Tooltip>
|
</Tooltip>
|
||||||
</TooltipProvider>
|
</TooltipProvider>
|
||||||
@@ -30,10 +32,16 @@ export function IconWithTooltipNumber({
|
|||||||
<>{children}</>
|
<>{children}</>
|
||||||
);
|
);
|
||||||
|
|
||||||
|
const labelElement = (
|
||||||
|
<Typography variant={"p_inline"} className="px-1">
|
||||||
|
{label}
|
||||||
|
</Typography>
|
||||||
|
);
|
||||||
return (
|
return (
|
||||||
<>
|
<div className="flex flex-row items-center gap-1">
|
||||||
{amount && <Typography variant={"p_inline"}>{amount}</Typography>}
|
{label && labelPosition == "left" && labelElement}
|
||||||
{iconElement}
|
{iconElement}
|
||||||
</>
|
{label && labelPosition == "right" && labelElement}
|
||||||
|
</div>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
File diff suppressed because it is too large
Load Diff
|
After Width: | Height: | Size: 133 KiB |
File diff suppressed because it is too large
Load Diff
|
After Width: | Height: | Size: 96 KiB |
+1
-1
@@ -10,7 +10,7 @@ function Root() {
|
|||||||
<>
|
<>
|
||||||
<ThemeProvider defaultTheme="dark" storageKey="vite-ui-theme">
|
<ThemeProvider defaultTheme="dark" storageKey="vite-ui-theme">
|
||||||
<AppHeader />
|
<AppHeader />
|
||||||
<div className="appPage bg-background text-foreground">
|
<div className="mx-auto w-full bg-background p-8 text-center text-foreground sm:w-[90%] md:w-[80%]">
|
||||||
<Outlet />
|
<Outlet />
|
||||||
</div>
|
</div>
|
||||||
<Toaster />
|
<Toaster />
|
||||||
|
|||||||
@@ -0,0 +1,3 @@
|
|||||||
|
.homePageRoomGrid {
|
||||||
|
grid-template-columns: repeat(auto-fit, minmax(300px, 3fr));
|
||||||
|
}
|
||||||
@@ -8,6 +8,8 @@ import { Typography } from "@/components/ui/typography";
|
|||||||
import { RoomCategory } from "@/model/RoomCategory";
|
import { RoomCategory } from "@/model/RoomCategory";
|
||||||
import { SearchFilter } from "@/model/SearchFilter";
|
import { SearchFilter } from "@/model/SearchFilter";
|
||||||
import { useNavigate } from "react-router-dom";
|
import { useNavigate } from "react-router-dom";
|
||||||
|
import { LoaderIcon } from "lucide-react";
|
||||||
|
import "./home-page.css";
|
||||||
|
|
||||||
function Homepage() {
|
function Homepage() {
|
||||||
const { roomCategoryList, isLoading, isError } = useAPIRoomCategories();
|
const { roomCategoryList, isLoading, isError } = useAPIRoomCategories();
|
||||||
@@ -27,13 +29,15 @@ function Homepage() {
|
|||||||
<Separator className="my-10" />
|
<Separator className="my-10" />
|
||||||
<SearchFilterCard onSearchClick={onBtSearchClick} />
|
<SearchFilterCard onSearchClick={onBtSearchClick} />
|
||||||
<Separator className="my-10" />
|
<Separator className="my-10" />
|
||||||
<div className="grid grid-cols-2 gap-4">{roomList}</div>
|
<div className="homePageRoomGrid grid gap-4">{roomList}</div>
|
||||||
{isLoading && (
|
{isLoading && (
|
||||||
<>
|
<>
|
||||||
<Typography variant={"h3"} as="h2">
|
<Skeleton className="mx-auto flex h-[200px] w-full flex-col items-center justify-center">
|
||||||
Loading...
|
<LoaderIcon />
|
||||||
</Typography>
|
<Typography variant={"h3"} as="h2">
|
||||||
<Skeleton className="w-[400px] h-[200px] mx-auto" />
|
Loading...
|
||||||
|
</Typography>
|
||||||
|
</Skeleton>
|
||||||
</>
|
</>
|
||||||
)}
|
)}
|
||||||
<Separator className="my-10" />
|
<Separator className="my-10" />
|
||||||
|
|||||||
+43
-32
@@ -21,25 +21,6 @@ function SearchPage() {
|
|||||||
|
|
||||||
const [isSearched, setIsSearched] = useState<boolean>(false);
|
const [isSearched, setIsSearched] = useState<boolean>(false);
|
||||||
|
|
||||||
const onBtSearchClick = (newFilter: SearchFilter) => {
|
|
||||||
console.log("onBtSearchClick: ");
|
|
||||||
console.log(newFilter);
|
|
||||||
|
|
||||||
if (
|
|
||||||
newFilter.dateRange &&
|
|
||||||
newFilter.dateRange.from &&
|
|
||||||
newFilter.dateRange.to
|
|
||||||
) {
|
|
||||||
setIsSearched(true);
|
|
||||||
|
|
||||||
resetBooking(newFilter.dateRange.from, newFilter.dateRange.to);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
const onBookingContinue = () => {
|
|
||||||
navigate("/book");
|
|
||||||
};
|
|
||||||
|
|
||||||
//This seems a little bit overkill, but IDK.
|
//This seems a little bit overkill, but IDK.
|
||||||
const availabilityFilter: AvailabilityFilter | null = useMemo(() => {
|
const availabilityFilter: AvailabilityFilter | null = useMemo(() => {
|
||||||
if (
|
if (
|
||||||
@@ -66,6 +47,25 @@ function SearchPage() {
|
|||||||
const isLoading = isLoadingAvailability;
|
const isLoading = isLoadingAvailability;
|
||||||
const isError = isErrorAvailability;
|
const isError = isErrorAvailability;
|
||||||
|
|
||||||
|
const onBtSearchClick = (newFilter: SearchFilter) => {
|
||||||
|
console.log("onBtSearchClick: ");
|
||||||
|
console.log(newFilter);
|
||||||
|
|
||||||
|
if (
|
||||||
|
newFilter.dateRange &&
|
||||||
|
newFilter.dateRange.from &&
|
||||||
|
newFilter.dateRange.to
|
||||||
|
) {
|
||||||
|
setIsSearched(true);
|
||||||
|
|
||||||
|
resetBooking(newFilter.dateRange.from, newFilter.dateRange.to);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const onBookingContinue = () => {
|
||||||
|
navigate("/book");
|
||||||
|
};
|
||||||
|
|
||||||
const roomList = roomAvailabilityList?.map((element) => {
|
const roomList = roomAvailabilityList?.map((element) => {
|
||||||
return (
|
return (
|
||||||
<RoomBooking
|
<RoomBooking
|
||||||
@@ -87,26 +87,37 @@ function SearchPage() {
|
|||||||
roomAvailabilityList?.length && roomAvailabilityList?.length > 0;
|
roomAvailabilityList?.length && roomAvailabilityList?.length > 0;
|
||||||
console.log(hasRooms);
|
console.log(hasRooms);
|
||||||
|
|
||||||
return (
|
const pageContent = isLoading ? (
|
||||||
<div className="bg-background text-foreground">
|
<>
|
||||||
<SearchFilterCard onSearchClick={onBtSearchClick} />
|
<Typography variant={"h3"} as="h2">
|
||||||
<Separator className="my-10" />
|
Loading...
|
||||||
{isLoading && (
|
</Typography>
|
||||||
<>
|
<Skeleton className="mx-auto h-[200px] w-full" />
|
||||||
<Typography variant={"h3"} as="h2">
|
</>
|
||||||
Loading...
|
) : (
|
||||||
</Typography>
|
<div className="2xl:grid 2xl:grid-cols-60/40 2xl:gap-6">
|
||||||
<Skeleton className="w-[400px] h-[200px] mx-auto" />
|
|
||||||
</>
|
|
||||||
)}
|
|
||||||
<div className="flex flex-col gap-5">
|
<div className="flex flex-col gap-5">
|
||||||
{hasRooms ? roomList : isSearched && noRoomsAvailable}
|
{hasRooms ? roomList : isSearched && noRoomsAvailable}
|
||||||
</div>
|
</div>
|
||||||
<Separator className="my-10" />
|
<Separator className="my-10 2xl:hidden" />
|
||||||
<BookingDetails
|
<BookingDetails
|
||||||
onBookingConfirm={onBookingContinue}
|
onBookingConfirm={onBookingContinue}
|
||||||
mode={"Book"}
|
mode={"Book"}
|
||||||
></BookingDetails>
|
></BookingDetails>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div className="bg-background text-foreground">
|
||||||
|
<SearchFilterCard onSearchClick={onBtSearchClick} />
|
||||||
|
<Separator className="my-10" />
|
||||||
|
{hasRooms ? (
|
||||||
|
pageContent
|
||||||
|
) : (
|
||||||
|
<Typography>
|
||||||
|
Realiza una búsqueda para ver la disponibilidad y los precios.
|
||||||
|
</Typography>
|
||||||
|
)}
|
||||||
<Separator className="my-10" />
|
<Separator className="my-10" />
|
||||||
<BackendConnectionStatus isLoading={isLoading} isError={isError} />
|
<BackendConnectionStatus isLoading={isLoading} isError={isError} />
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -73,6 +73,12 @@ export default {
|
|||||||
"accordion-down": "accordion-down 0.2s ease-out",
|
"accordion-down": "accordion-down 0.2s ease-out",
|
||||||
"accordion-up": "accordion-up 0.2s ease-out",
|
"accordion-up": "accordion-up 0.2s ease-out",
|
||||||
},
|
},
|
||||||
|
|
||||||
|
//SomniaRooms Extends
|
||||||
|
gridTemplateColumns: {
|
||||||
|
"60/40": "60% 40%",
|
||||||
|
"40/60": "40% 60%",
|
||||||
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
plugins: [plugin],
|
plugins: [plugin],
|
||||||
|
|||||||
Reference in New Issue
Block a user