"use client";
import React, { useState, useCallback, useEffect, Fragment } from "react";
import { Datepicker, CalendarPrev, CalendarNav, CalendarNext, CalendarToday, setOptions, localeNl, formatDate } from "@mobiscroll/react";
import { cn } from "@/lib/utils";
import useDateStore from "../../../state/useDateStore";
import { Button } from "./ui/Button";
import "@/styles/datepicker.modules.scss";
import "@mobiscroll/react/dist/css/mobiscroll.min.css";
import useSWR from "swr";
type DateRangeSelectorProps = {
openChange: (isOpen: boolean) => void;
isOpen: boolean;
};
type ValidProps = {
start: Date;
end: Date;
};
setOptions({
locale: localeNl,
theme: "ios",
themeVariant: "light",
});
const now = new Date();
const dateFormat = "YYYY-MM-DD";
let startingFromDate = formatDate(dateFormat, new Date(now.getFullYear(), now.getMonth(), 1)); // format datum naar YYYY-MM-DD
let startingEndDate = formatDate(dateFormat, new Date(now.getFullYear(), now.getMonth() + 2, 0)); // format datum naar YYYY-MM-DD
/**
* Updates the valid dates for the DateRangeSelector based on the arrivals data.
* @param arrivals - An array of arrival data.
* @returns An array of ValidProps objects representing the valid date ranges.
*/
const updateValidDates = (arrivals: any[]): ValidProps[] => {
const valids: ValidProps[] = [];
arrivals.map((arrival: any) => {
if (arrival.departures) {
arrival.departures.map((depature: any) => {
valids.push({ start: arrival.date, end: depature.date });
});
}
});
return valids;
};
/**
* A component that allows the user to select a date range, it fetches arrival data from the API and updates the valid dates accordingly.
* Then the user can select a date range that is only within the valid dates.
* @param openChange - A callback function to handle changes in the open state of the DateRangeSelector.
* @param isOpen - A boolean indicating whether the DateRangeSelector is open or not.
* @returns A DateRangeSelector component.
*/
export const DateRangeSelector = ({ openChange, isOpen }: DateRangeSelectorProps) => {
const addDate = useDateStore((state) => state.addDate);
const [fromDate, setFromDate] = useState(startingFromDate);
const [tillDate, setTillDate] = useState(startingEndDate);
const [selectedDate, setSelectedDate] = useState<[string, string] | null>(null);
const [inputValue, setInputValue] = useState();
const [calenderOpen, setCalenderOpen] = useState(false);
/**
* Handles the new date range on page switch, and updates the state accordingly.
* @param event - The event object containing information about the next selected date.
*/
const handlePageChange = (event: any) => {
const newCurrMonth = event.firstDay.toLocaleDateString("en-CA");
const newNextMonth = new Date(event.firstDay.getFullYear(), event.firstDay.getMonth() + 2, 0).toLocaleDateString("en-CA");
// setTimeout nodig om errors in the console te voorkomen
setTimeout(() => {
setFromDate(newCurrMonth);
setTillDate(newNextMonth);
}, 0);
};
function fetcher(url: string) {
return fetch(url).then((res) => res.json());
}
function useFetchDates() {
const { data } = useSWR(`/api/availability?fromDate=${fromDate}&tillDate=${tillDate}`, fetcher);
return data;
}
/**
* Retrieves the availability data for the given date range.
* @param fromDate - The start date of the date range.
* @param tillDate - The end date of the date range.
*/
function useAvailableDates(data: any) {
const [valid, setValid] = useState<ValidProps[]>([]);
useEffect(() => {
if (isOpen && data) {
const valids = updateValidDates(data.data.arrivals);
setValid(valids);
}
}, [isOpen, data]);
return valid;
}
const valid = useAvailableDates(useFetchDates());
const applyClick = useCallback(() => {
if (!selectedDate) return;
openChange(false);
setCalenderOpen(false);
addDate(selectedDate);
}, [selectedDate]);
const onDateChange = useCallback(
(event: any) => {
const date = event.value;
const selectedFrom = formatDate(dateFormat, new Date(date[0] as string));
const selectedTill = formatDate(dateFormat, new Date(date[1] as string));
setSelectedDate([selectedFrom, selectedTill]);
},
[selectedDate]
);
const cancelClick = () => {
openChange(false);
};
const calendarHeader = useCallback(() => {
return (
<Fragment>
<CalendarNav />
<CalendarPrev />
<CalendarToday />
<CalendarNext />
</Fragment>
);
}, []);
return (
<div
className={cn(
`
${isOpen === true ? "block" : "hidden"}
fixed
left-0
top-0
bg-white
w-full
h-full
flex
flex-col
z-20
overflow-y-scroll
`
)}
>
<Datepicker
controls={["calendar"]}
cssClass="md-book-rental"
select="range"
display="inline"
calendarType="month"
calendarSize={2}
refDate={startingFromDate}
min={now}
startInput={now}
showRangeLabels={false}
inRangeInvalid={false}
rangeEndInvalid={true}
valid={valid}
onClose={cancelClick}
renderCalendarHeader={calendarHeader}
onPageChange={handlePageChange}
onChange={onDateChange}
/>
<div
className={cn(
`
h-20
p-5
flex
space-around
justify-around
bg-white
fixed
md:absolute
z-50
bottom-0
left-0
w-full
!shadow-lg
!border-t-2
!border-gray-300
`
)}
>
<Button onClick={cancelClick} variant="outline" style={{ backgroundColor: "var(--theme-color-tertiary)" }}>
Selectie wissen
</Button>
<Button onClick={applyClick} style={{ backgroundColor: "var(--theme-color-primary)" }}>
Selecteren
</Button>
</div>
</div>
);
};
export default DateRangeSelector;