forked from Somnia/SomniaRoomsApp
Add routes & navigation.
This commit is contained in:
@@ -5,25 +5,33 @@ 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 { useState } from "react";
|
import { useEffect, 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;
|
||||||
|
|
||||||
export function SearchFilterCard({ onSearchClick }: { onSearchClick?: funOnSearchClick }) {
|
export function SearchFilterCard({ onSearchClick }: { onSearchClick?: funOnSearchClick }) {
|
||||||
|
|
||||||
const { toast } = useToast();
|
//App filter. Updated on btSearchClick.
|
||||||
|
const appSearchFilter = useAppStore((state) => (state.searchFilter));
|
||||||
|
const setAppSearchFilter = useAppStore((state) => (state.setSearchFilter));
|
||||||
|
|
||||||
//Local Filter state.
|
//Local Filter state.
|
||||||
const [searchFilter, setSearchFilter] = useState<SearchFilter>({
|
const [searchFilter, setSearchFilter] = useState<SearchFilter>(appSearchFilter)
|
||||||
dateRange: { from: new Date() }
|
|
||||||
})
|
|
||||||
|
|
||||||
//App filter. Updated on btSearchClick.
|
//On location change, try to load searchFilter from state.
|
||||||
const setAppSearchFilter = useAppStore((state) => (state.setSearchFilter));
|
//FIXME: Si llegamos sin state, hay que borrar el state global...
|
||||||
|
const location = useLocation();
|
||||||
|
useEffect(() => {
|
||||||
|
setSearchFilter(location.state);
|
||||||
|
}, [location]);
|
||||||
|
|
||||||
|
|
||||||
|
const { toast } = useToast();
|
||||||
|
|
||||||
// Validate and call callback on btSearch click.
|
// Validate and call callback on btSearch click.
|
||||||
//TODOME: Add data validations and show messages.
|
//TODOME: Add data validations and show messages.
|
||||||
|
|||||||
@@ -1,22 +1,28 @@
|
|||||||
import { BookmarkIcon } from "@radix-ui/react-icons"
|
|
||||||
import { ModeToggle } from "./mode-toggle"
|
import { ModeToggle } from "./mode-toggle"
|
||||||
import { Button } from "./ui/button"
|
import { Button } from "./ui/button"
|
||||||
import { Card, CardContent } from "./ui/card"
|
import { Card, CardContent } from "./ui/card"
|
||||||
import { Typography } from "./ui/typography"
|
import { Typography } from "./ui/typography"
|
||||||
|
import { Link } from "react-router-dom"
|
||||||
|
import { BookAIcon, HomeIcon } from "lucide-react"
|
||||||
|
|
||||||
function AppHeader() {
|
function AppHeader() {
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Card className="flex items-center mx-10 my-4">
|
<Card className="flex items-center mx-10 my-4">
|
||||||
<CardContent className="w-full">
|
<CardContent className="w-full">
|
||||||
<div className="flex justify-around">
|
<div className="flex justify-around items-center">
|
||||||
<div className="flex flex-row items-center gap-2">
|
<div className="flex flex-row items-center gap-2 relative">
|
||||||
<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 href="https://duck.com" className="absolute w-[100%] h-[100%]"></a>
|
||||||
</div>
|
</div>
|
||||||
<Button variant="ghost">
|
<Button variant="ghost">
|
||||||
<BookmarkIcon />
|
<HomeIcon />
|
||||||
<Typography variant={"largeText"}>Reservar</Typography>
|
<Link to={`home`}>Home</Link>
|
||||||
|
</Button>
|
||||||
|
<Button variant="ghost">
|
||||||
|
<BookAIcon />
|
||||||
|
<Link to={`search`}>Reservar</Link>
|
||||||
</Button>
|
</Button>
|
||||||
<ModeToggle />
|
<ModeToggle />
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -6,10 +6,18 @@ import { Separator } from "../ui/separator";
|
|||||||
import { Table, TableBody, TableCell, TableHead, TableHeader, TableRow } from "../ui/table";
|
import { Table, TableBody, TableCell, TableHead, TableHeader, TableRow } from "../ui/table";
|
||||||
import { Button } from "../ui/button";
|
import { Button } from "../ui/button";
|
||||||
|
|
||||||
export function BookingDetails() {
|
type OnBookingClickFunction = () => void;
|
||||||
|
|
||||||
|
export function BookingDetails({ onBookingConfirm }: { onBookingConfirm?: OnBookingClickFunction }) {
|
||||||
|
|
||||||
const bookingData = useAppStore((state) => state.newBookingData);
|
const bookingData = useAppStore((state) => state.newBookingData);
|
||||||
|
|
||||||
|
const onBtBookingClick = () => {
|
||||||
|
if (onBookingConfirm) {
|
||||||
|
onBookingConfirm();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if (!bookingData || bookingData.rooms.length <= 0) {
|
if (!bookingData || bookingData.rooms.length <= 0) {
|
||||||
return (
|
return (
|
||||||
<Card>
|
<Card>
|
||||||
@@ -72,7 +80,9 @@ export function BookingDetails() {
|
|||||||
<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>
|
||||||
<Button className="my-2"><Typography variant={"largeText"} className="text-primary-foreground">Book it!</Typography></Button>
|
<Button className="my-2" onClick={onBtBookingClick}>
|
||||||
|
<Typography variant={"largeText"} className="text-primary-foreground">Book it!</Typography>
|
||||||
|
</Button>
|
||||||
</CardContent>
|
</CardContent>
|
||||||
</Card>
|
</Card>
|
||||||
)
|
)
|
||||||
|
|||||||
@@ -6,6 +6,7 @@ import Root from './routes/Root';
|
|||||||
import ErrorPage from './error-page';
|
import ErrorPage from './error-page';
|
||||||
import Homepage from './routes/home-page';
|
import Homepage from './routes/home-page';
|
||||||
import SearchPage from './routes/search-page';
|
import SearchPage from './routes/search-page';
|
||||||
|
import BookPage from './routes/book-page';
|
||||||
|
|
||||||
const router = createBrowserRouter([
|
const router = createBrowserRouter([
|
||||||
{
|
{
|
||||||
@@ -13,6 +14,10 @@ const router = createBrowserRouter([
|
|||||||
element: <Root />,
|
element: <Root />,
|
||||||
errorElement: <ErrorPage />,
|
errorElement: <ErrorPage />,
|
||||||
children: [
|
children: [
|
||||||
|
{
|
||||||
|
path: "/",
|
||||||
|
element: <Homepage />
|
||||||
|
},
|
||||||
{
|
{
|
||||||
path: "/home",
|
path: "/home",
|
||||||
element: <Homepage />
|
element: <Homepage />
|
||||||
@@ -20,11 +25,17 @@ const router = createBrowserRouter([
|
|||||||
{
|
{
|
||||||
path: "/search",
|
path: "/search",
|
||||||
element: <SearchPage />
|
element: <SearchPage />
|
||||||
|
},
|
||||||
|
{
|
||||||
|
path: "/book",
|
||||||
|
element: <BookPage />
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
]);
|
]);
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
ReactDOM.createRoot(document.getElementById('root')!).render(
|
ReactDOM.createRoot(document.getElementById('root')!).render(
|
||||||
<React.StrictMode>
|
<React.StrictMode>
|
||||||
<RouterProvider router={router} />
|
<RouterProvider router={router} />
|
||||||
|
|||||||
@@ -0,0 +1,39 @@
|
|||||||
|
import { BookingDetails } from "@/components/booking/booking-details"
|
||||||
|
import { Button } from "@/components/ui/button"
|
||||||
|
import { Input } from "@/components/ui/input"
|
||||||
|
import { Label } from "@/components/ui/label"
|
||||||
|
import { Separator } from "@/components/ui/separator"
|
||||||
|
import { Typography } from "@/components/ui/typography"
|
||||||
|
|
||||||
|
function BookPage() {
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
return (
|
||||||
|
<>
|
||||||
|
<Typography variant={"h1"}>Booking Details</Typography>
|
||||||
|
<Separator className="my-10" />
|
||||||
|
<div>
|
||||||
|
<Typography variant={"h3"}>Personal Details</Typography>
|
||||||
|
<div>
|
||||||
|
<Label>Name</Label>
|
||||||
|
<Input></Input>
|
||||||
|
<Label>Surname</Label>
|
||||||
|
<Input></Input>
|
||||||
|
<Label>Email</Label>
|
||||||
|
<Input></Input>
|
||||||
|
<Label>Phone Number</Label>
|
||||||
|
<Input></Input>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<Separator className="my-10" />
|
||||||
|
<BookingDetails></BookingDetails>
|
||||||
|
<Separator className="my-10" />
|
||||||
|
<Button>
|
||||||
|
<Typography variant={"h3"} className="text-primary-foreground">Book it now!</Typography>
|
||||||
|
</Button>
|
||||||
|
</>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
export default BookPage
|
||||||
@@ -6,10 +6,17 @@ import { Separator } from "@/components/ui/separator"
|
|||||||
import { Skeleton } from "@/components/ui/skeleton"
|
import { Skeleton } from "@/components/ui/skeleton"
|
||||||
import { Typography } from "@/components/ui/typography"
|
import { Typography } from "@/components/ui/typography"
|
||||||
import { RoomCategory } from "@/model/RoomCategory"
|
import { RoomCategory } from "@/model/RoomCategory"
|
||||||
|
import { SearchFilter } from "@/model/SearchFilter"
|
||||||
|
import { useNavigate } from "react-router-dom"
|
||||||
|
|
||||||
function Homepage() {
|
function Homepage() {
|
||||||
|
|
||||||
const { roomCategoryList, isLoading, isError } = useAPIRoomCategories();
|
const { roomCategoryList, isLoading, isError } = useAPIRoomCategories();
|
||||||
|
const navigate = useNavigate();
|
||||||
|
|
||||||
|
const onBtSearchClick = (filter: SearchFilter) => {
|
||||||
|
navigate("/search", {state: filter})
|
||||||
|
}
|
||||||
|
|
||||||
const roomList = roomCategoryList?.map((element: RoomCategory) => {
|
const roomList = roomCategoryList?.map((element: RoomCategory) => {
|
||||||
return (
|
return (
|
||||||
@@ -21,7 +28,7 @@ function Homepage() {
|
|||||||
<>
|
<>
|
||||||
<Typography variant={"h1"}>Hostal del Val</Typography>
|
<Typography variant={"h1"}>Hostal del Val</Typography>
|
||||||
<Separator className="my-10" />
|
<Separator className="my-10" />
|
||||||
<SearchFilterCard />
|
<SearchFilterCard onSearchClick={onBtSearchClick}/>
|
||||||
<Separator className="my-10" />
|
<Separator className="my-10" />
|
||||||
<div className="grid grid-cols-2 gap-4">
|
<div className="grid grid-cols-2 gap-4">
|
||||||
{roomList}
|
{roomList}
|
||||||
|
|||||||
@@ -10,6 +10,7 @@ import { AvailabilityFilter } from "@/model/RoomSearch"
|
|||||||
import { SearchFilter } from "@/model/SearchFilter"
|
import { SearchFilter } from "@/model/SearchFilter"
|
||||||
import { useAppStore } from "@/store"
|
import { useAppStore } from "@/store"
|
||||||
import { useMemo, useState } from "react"
|
import { useMemo, useState } from "react"
|
||||||
|
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: Igual podemos crear un StateMachine conjunta para los siguiente estados de la página: Sin Buscar, Sin disponibilidad, error, cargando.
|
||||||
function SearchPage() {
|
function SearchPage() {
|
||||||
@@ -17,6 +18,7 @@ function SearchPage() {
|
|||||||
//const [availabilityFilter, setAvailabilityFilter] = useState<AvailabilityFilter>();
|
//const [availabilityFilter, setAvailabilityFilter] = useState<AvailabilityFilter>();
|
||||||
const searchFilter = useAppStore((state) => (state.searchFilter));
|
const searchFilter = useAppStore((state) => (state.searchFilter));
|
||||||
const resetBooking = useAppStore((state) => (state.resetBookingState));
|
const resetBooking = useAppStore((state) => (state.resetBookingState));
|
||||||
|
const navigate = useNavigate();
|
||||||
|
|
||||||
const [isSearched, setIsSearched] = useState<boolean>(false);
|
const [isSearched, setIsSearched] = useState<boolean>(false);
|
||||||
|
|
||||||
@@ -31,6 +33,10 @@ function SearchPage() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
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(
|
||||||
() => {
|
() => {
|
||||||
@@ -79,7 +85,7 @@ function SearchPage() {
|
|||||||
{hasRooms ? roomList : (isSearched && noRoomsAvailable)}
|
{hasRooms ? roomList : (isSearched && noRoomsAvailable)}
|
||||||
</div>
|
</div>
|
||||||
<Separator className="my-10" />
|
<Separator className="my-10" />
|
||||||
<BookingDetails></BookingDetails>
|
<BookingDetails onBookingConfirm={onBookingContinue}></BookingDetails>
|
||||||
<Separator className="my-10" />
|
<Separator className="my-10" />
|
||||||
<BackendConnectionStatus isLoading={isLoading} isError={isError} />
|
<BackendConnectionStatus isLoading={isLoading} isError={isError} />
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
Reference in New Issue
Block a user