General improvements:
- Search control - Improved error messages. - Fixed lint errors.
This commit is contained in:
@@ -8,7 +8,6 @@ import { SearchIcon } from "lucide-react";
|
|||||||
import { useState } from "react";
|
import { useState } from "react";
|
||||||
import { SearchFilter } from "@/model/InternalTypes";
|
import { SearchFilter } from "@/model/InternalTypes";
|
||||||
import { DateRange } from "react-day-picker";
|
import { DateRange } from "react-day-picker";
|
||||||
import { useToast } from "../ui/use-toast";
|
|
||||||
import { isValidSearchFilter } from "@/utils/utils";
|
import { isValidSearchFilter } from "@/utils/utils";
|
||||||
import { useNavigate } from "react-router-dom";
|
import { useNavigate } from "react-router-dom";
|
||||||
import { ConstsNav, ConstsTexts } from "@/utils/Consts";
|
import { ConstsNav, ConstsTexts } from "@/utils/Consts";
|
||||||
@@ -28,6 +27,7 @@ export function SearchFilterCard({
|
|||||||
const [searchFilter, setSearchFilter] = useState<SearchFilter | null>(
|
const [searchFilter, setSearchFilter] = useState<SearchFilter | null>(
|
||||||
initialsearchFilter ?? null,
|
initialsearchFilter ?? null,
|
||||||
);
|
);
|
||||||
|
const [errorMsg, setErrorMsg] = useState<string | null>(null);
|
||||||
const [isContactDialogOpen, setIsContactDialogOpen] =
|
const [isContactDialogOpen, setIsContactDialogOpen] =
|
||||||
useState<boolean>(false);
|
useState<boolean>(false);
|
||||||
|
|
||||||
@@ -37,18 +37,12 @@ export function SearchFilterCard({
|
|||||||
shouldShowContactDialog: nightCountShowDialog,
|
shouldShowContactDialog: nightCountShowDialog,
|
||||||
} = useCheckForNightCount(searchFilter);
|
} = useCheckForNightCount(searchFilter);
|
||||||
|
|
||||||
const { toast } = useToast();
|
|
||||||
const navigate = useNavigate();
|
const navigate = useNavigate();
|
||||||
|
|
||||||
// Validate and call callback on btSearch click.
|
// Validate and call callback on btSearch click.
|
||||||
//TODOME: Show validation messages as form instead of toasts.
|
|
||||||
const btSearchClick = () => {
|
const btSearchClick = () => {
|
||||||
if (!isValidSearchFilter(searchFilter)) {
|
if (!isValidSearchFilter(searchFilter)) {
|
||||||
toast({
|
setErrorMsg("Select a valid 'from' and 'to' dates!");
|
||||||
variant: "destructive",
|
|
||||||
title: "Uh oh! Invalid Search.",
|
|
||||||
description: "You should select a 'from' and 'to' dates",
|
|
||||||
});
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -76,10 +70,16 @@ export function SearchFilterCard({
|
|||||||
|
|
||||||
// Handle internal Searchfilter updates.
|
// Handle internal Searchfilter updates.
|
||||||
const updateDateRange = (newRange: DateRange) => {
|
const updateDateRange = (newRange: DateRange) => {
|
||||||
setSearchFilter({
|
const newSearchFilter = {
|
||||||
...searchFilter,
|
...searchFilter,
|
||||||
dateRange: newRange,
|
dateRange: newRange,
|
||||||
});
|
};
|
||||||
|
|
||||||
|
setSearchFilter(newSearchFilter);
|
||||||
|
|
||||||
|
if (isValidSearchFilter(newSearchFilter)) {
|
||||||
|
setErrorMsg(null);
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
const onContactDialogClose = () => {
|
const onContactDialogClose = () => {
|
||||||
@@ -110,6 +110,13 @@ export function SearchFilterCard({
|
|||||||
></DatePickerWithRange>
|
></DatePickerWithRange>
|
||||||
);
|
);
|
||||||
|
|
||||||
|
const errorMsgLabel = (
|
||||||
|
<Typography variant={"errorText"} className="mt-2 text-lg">
|
||||||
|
{errorMsg}
|
||||||
|
</Typography>
|
||||||
|
);
|
||||||
|
const searchHasErrors = errorMsg != null;
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
<ContactUsDialog
|
<ContactUsDialog
|
||||||
@@ -125,11 +132,13 @@ export function SearchFilterCard({
|
|||||||
</CardHeader>
|
</CardHeader>
|
||||||
<CardContent>
|
<CardContent>
|
||||||
{datePickerElement}
|
{datePickerElement}
|
||||||
|
{searchHasErrors && errorMsgLabel}
|
||||||
<Separator className="my-4" />
|
<Separator className="my-4" />
|
||||||
<Button
|
<Button
|
||||||
size={"lg"}
|
size={"lg"}
|
||||||
onClick={btSearchClick}
|
onClick={btSearchClick}
|
||||||
disabled={isSearchButtonDisabled}
|
disabled={isSearchButtonDisabled}
|
||||||
|
variant={searchHasErrors ? "destructive" : "default"}
|
||||||
>
|
>
|
||||||
<SearchIcon className="me-2 text-primary-foreground" />
|
<SearchIcon className="me-2 text-primary-foreground" />
|
||||||
<Typography variant={"h3"} className="text-primary-foreground">
|
<Typography variant={"h3"} className="text-primary-foreground">
|
||||||
|
|||||||
@@ -90,7 +90,7 @@ export function DatePickerWithRange({
|
|||||||
id="dateFrom"
|
id="dateFrom"
|
||||||
variant="outline"
|
variant="outline"
|
||||||
data-empty={!dateRange}
|
data-empty={!dateRange}
|
||||||
className="w-[100%] justify-start text-left font-normal data-[empty=true]:text-muted-foreground"
|
className="w-full justify-start text-left font-normal data-[empty=true]:text-muted-foreground"
|
||||||
>
|
>
|
||||||
<CalendarIcon />
|
<CalendarIcon />
|
||||||
{dateRange?.from ? (
|
{dateRange?.from ? (
|
||||||
@@ -117,11 +117,11 @@ export function DatePickerWithRange({
|
|||||||
id="dateTo"
|
id="dateTo"
|
||||||
variant={"outline"}
|
variant={"outline"}
|
||||||
data-empty={!dateRange}
|
data-empty={!dateRange}
|
||||||
className="w-[100%] justify-start text-left font-normal data-[empty=true]:text-muted-foreground"
|
className="w-full justify-start text-left font-normal data-[empty=true]:text-muted-foreground"
|
||||||
>
|
>
|
||||||
<CalendarIcon className="mr-2 h-4 w-4" />
|
<CalendarIcon className="mr-2 h-4 w-4" />
|
||||||
{dateRange?.to ? (
|
{dateRange?.to ? (
|
||||||
format(dateRange.to, "LLL dd, y")
|
format(dateRange.to, "PPP")
|
||||||
) : (
|
) : (
|
||||||
<span>Pick a date</span>
|
<span>Pick a date</span>
|
||||||
)}
|
)}
|
||||||
|
|||||||
@@ -24,6 +24,7 @@ const typographyVariants = cva("text-foreground", {
|
|||||||
largeText: "text-lg font-semibold",
|
largeText: "text-lg font-semibold",
|
||||||
smallText: "text-sm leading-none font-medium",
|
smallText: "text-sm leading-none font-medium",
|
||||||
mutedText: "text-muted-foreground text-sm",
|
mutedText: "text-muted-foreground text-sm",
|
||||||
|
errorText: "text-destructive text-sm font-normal",
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
defaultVariants: {
|
defaultVariants: {
|
||||||
@@ -51,11 +52,13 @@ const variantElementMap: Record<
|
|||||||
smallText: "small",
|
smallText: "small",
|
||||||
lead: "p",
|
lead: "p",
|
||||||
mutedText: "p",
|
mutedText: "p",
|
||||||
|
errorText: "p",
|
||||||
ul: "ul",
|
ul: "ul",
|
||||||
};
|
};
|
||||||
|
|
||||||
export interface TypographyProps
|
export interface TypographyProps
|
||||||
extends React.HTMLAttributes<HTMLElement>,
|
extends
|
||||||
|
React.HTMLAttributes<HTMLElement>,
|
||||||
VariantProps<typeof typographyVariants> {
|
VariantProps<typeof typographyVariants> {
|
||||||
asChild?: boolean;
|
asChild?: boolean;
|
||||||
as?: string;
|
as?: string;
|
||||||
|
|||||||
@@ -22,6 +22,7 @@ import { zodResolver } from "@hookform/resolvers/zod";
|
|||||||
import { eBookingState } from "@/utils/AppStateManager";
|
import { eBookingState } from "@/utils/AppStateManager";
|
||||||
import { useBookingStateDispacher } from "@/utils/hooks";
|
import { useBookingStateDispacher } from "@/utils/hooks";
|
||||||
import { ConstsNav } from "@/utils/Consts";
|
import { ConstsNav } from "@/utils/Consts";
|
||||||
|
import { NewBookingCustomerDetails } from "@/model/Bookings";
|
||||||
|
|
||||||
enum PageState {
|
enum PageState {
|
||||||
showForm,
|
showForm,
|
||||||
@@ -53,12 +54,13 @@ function BookPage() {
|
|||||||
if (bookingData == null) {
|
if (bookingData == null) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
const newCustomerData = bookingData.customerDetails;
|
const newCustomerData: NewBookingCustomerDetails = {
|
||||||
newCustomerData.name = values.name;
|
...bookingData.customerDetails,
|
||||||
newCustomerData.surname = values.surname;
|
name: values.name,
|
||||||
newCustomerData.email = values.email;
|
surname: values.surname,
|
||||||
newCustomerData.phoneNumber = values.phone;
|
email: values.email,
|
||||||
|
phoneNumber: values.phone,
|
||||||
|
};
|
||||||
setCustomerData(newCustomerData);
|
setCustomerData(newCustomerData);
|
||||||
|
|
||||||
setPageState(PageState.sending);
|
setPageState(PageState.sending);
|
||||||
@@ -80,8 +82,9 @@ function BookPage() {
|
|||||||
//Redirect to home if no booking is active.
|
//Redirect to home if no booking is active.
|
||||||
if (
|
if (
|
||||||
!appBookingState ||
|
!appBookingState ||
|
||||||
appBookingState.state != eBookingState.Booking ||
|
!bookingData ||
|
||||||
!bookingData
|
(appBookingState.state != eBookingState.Booking &&
|
||||||
|
appBookingState.state != eBookingState.Booked)
|
||||||
) {
|
) {
|
||||||
navigate(ConstsNav.page_Home);
|
navigate(ConstsNav.page_Home);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -14,11 +14,14 @@ function SuccessPage() {
|
|||||||
const location = useLocation();
|
const location = useLocation();
|
||||||
const navigate = useNavigate();
|
const navigate = useNavigate();
|
||||||
|
|
||||||
|
if (pageState == null && location.state != null) {
|
||||||
|
setPageState(location.state);
|
||||||
|
}
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (location.state == null) {
|
if (location.state == null) {
|
||||||
navigate(ConstsNav.page_Home);
|
navigate(ConstsNav.page_Home);
|
||||||
}
|
}
|
||||||
setPageState(location.state);
|
|
||||||
}, [location, navigate]);
|
}, [location, navigate]);
|
||||||
|
|
||||||
const handleBtBackClick = () => {
|
const handleBtBackClick = () => {
|
||||||
|
|||||||
+1
-1
@@ -13,7 +13,7 @@ export const ConstsNav = {
|
|||||||
//TMP - The following texts should be replaced for a proper intl system.
|
//TMP - The following texts should be replaced for a proper intl system.
|
||||||
export const ConstsTexts = {
|
export const ConstsTexts = {
|
||||||
navToContact_maxAllowedNightCount:
|
navToContact_maxAllowedNightCount:
|
||||||
"We are sorry, but the selected date range is too large. Search on a smaller range or contact us for a personalized quote.",
|
"We are sorry, but the selected date range is too large. Search for a smaller range or contact us for a personalized quote.",
|
||||||
navToContact_maxAllowedGuestNumber:
|
navToContact_maxAllowedGuestNumber:
|
||||||
"We are sorry, group bookings are not allowed. Contact us for a personalized quote.",
|
"We are sorry, group bookings are not allowed. Contact us for a personalized quote.",
|
||||||
contactUsAlertDialog_title: "Contact us!",
|
contactUsAlertDialog_title: "Contact us!",
|
||||||
|
|||||||
Reference in New Issue
Block a user