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