import { useCallback, useEffect, useMemo, useState } from "react"; import { __ } from "@wordpress/i18n"; import { useDispatch, useSelect } from "@wordpress/data"; import DatePicker from "react-datepicker"; import Select from "react-select"; import classNames from "classnames"; import "react-datepicker/dist/react-datepicker.css"; import { store_name } from "../../../../store/backend"; import { getAdminSelectStyles } from "../../../utils/adminSelectStyles"; import { formatWbkDate, getReadableTime, } from "../../../components/Form/utils/dateTime"; import { Button } from "../../../components/Button/Button"; import { useSidebar } from "../../../components/Sidebar/SidebarContext"; import closeIcon from "../../../../../public/images/icon-close.svg"; import "../../../components/Form/Form.scss"; import "./BulkTimeSlotsLockPopup.scss"; type SelectOption = { value: string; label: string; }; type BulkTimeSlotsLockPopupProps = { onSuccess?: () => Promise | void; }; const DAYS_OF_WEEK: SelectOption[] = [ { value: "1", label: __("Monday", "webba-booking-lite") }, { value: "2", label: __("Tuesday", "webba-booking-lite") }, { value: "3", label: __("Wednesday", "webba-booking-lite") }, { value: "4", label: __("Thursday", "webba-booking-lite") }, { value: "5", label: __("Friday", "webba-booking-lite") }, { value: "6", label: __("Saturday", "webba-booking-lite") }, { value: "7", label: __("Sunday", "webba-booking-lite") }, ]; const TIME_STEP_SECONDS = 900; const TIME_MAX_SECONDS = 86400; const selectStyles = getAdminSelectStyles(); const buildTimeOptions = (timeFormat: string): SelectOption[] => { const options: SelectOption[] = []; for (let time = 0; time <= TIME_MAX_SECONDS; time += TIME_STEP_SECONDS) { options.push({ value: String(time), label: getReadableTime(time, timeFormat), }); } return options; }; export const BulkTimeSlotsLockPopup = ({ onSuccess, }: BulkTimeSlotsLockPopupProps) => { const sidebar = useSidebar(); const { scheduleToolsAction, setToastNotification } = useDispatch(store_name); const { services, categories, settings } = useSelect( (select) => { // @ts-ignore const preset = select(store_name).getPreset() || {}; // @ts-ignore const storeServices = select(store_name).getItems("services") || []; // @ts-ignore const storeCategories = select(store_name).getItems("service_categories") || []; return { services: Array.isArray(storeServices) && storeServices.length ? storeServices : preset.services || [], categories: Array.isArray(storeCategories) && storeCategories.length ? storeCategories : preset.categories || [], settings: preset.settings || {}, }; }, [] ); const timeFormat = settings?.time_format || "g:i a"; const timeOptions = useMemo( () => buildTimeOptions(timeFormat), [timeFormat] ); const defaultTimeFrom = useMemo( () => timeOptions.find((option) => Number(option.value) >= 900) || timeOptions[0] || null, [timeOptions] ); const defaultTimeTo = useMemo( () => timeOptions[timeOptions.length - 1] || null, [timeOptions] ); const [action, setAction] = useState<"lock" | "unlock">("lock"); const [serviceId, setServiceId] = useState(null); const [categoryId, setCategoryId] = useState(null); const [dateRange, setDateRange] = useState<[Date | null, Date | null]>([ null, null, ]); const [timeFrom, setTimeFrom] = useState(null); const [timeTo, setTimeTo] = useState(null); const [daysOfWeek, setDaysOfWeek] = useState(DAYS_OF_WEEK); const [dateRangeOpen, setDateRangeOpen] = useState(false); const [isSubmitting, setIsSubmitting] = useState(false); const [fieldErrors, setFieldErrors] = useState>({}); const [errorMessage, setErrorMessage] = useState(""); useEffect(() => { if (!timeFrom && defaultTimeFrom) { setTimeFrom(defaultTimeFrom); } if (!timeTo && defaultTimeTo) { setTimeTo(defaultTimeTo); } }, [defaultTimeFrom, defaultTimeTo, timeFrom, timeTo]); const serviceOptions = useMemo( () => (services || []) .map((service: any) => ({ value: String(service.id ?? service.value ?? ""), label: String(service.name ?? service.label ?? ""), })) .filter((option: SelectOption) => option.value && option.label), [services] ); const categoryOptions = useMemo( () => (categories || []) .map((category: any) => ({ value: String(category.id ?? category.value ?? ""), label: String(category.name ?? category.label ?? ""), })) .filter((option: SelectOption) => option.value && option.label), [categories] ); const validate = useCallback(() => { const errors: Record = {}; if (!serviceId && !categoryId) { errors.service = true; errors.category = true; } if (!dateRange[0] || !dateRange[1]) { errors.dateRange = true; } if (!timeFrom) { errors.timeFrom = true; } if (!timeTo) { errors.timeTo = true; } if ( timeFrom && timeTo && Number(timeFrom.value) >= Number(timeTo.value) ) { errors.timeFrom = true; errors.timeTo = true; } if (!daysOfWeek.length) { errors.daysOfWeek = true; } setFieldErrors(errors); return Object.keys(errors).length === 0; }, [serviceId, categoryId, dateRange, timeFrom, timeTo, daysOfWeek]); const handleSubmit = useCallback(async () => { setErrorMessage(""); if (!validate() || isSubmitting || !timeFrom || !timeTo) { return; } setIsSubmitting(true); try { const response = (await scheduleToolsAction({ lock_action: action, lock_target: "timeslots", service: serviceId?.value || "0", category: categoryId?.value || "0", date_range: `${formatWbkDate(dateRange[0]!)} - ${formatWbkDate(dateRange[1]!)}`, days_of_week: daysOfWeek.map((day) => day.value).join(","), from: timeFrom.value, to: timeTo.value, })) as { status?: number; message?: string }; if (!response?.status) { const message = response?.message || __("Something went wrong.", "webba-booking-lite"); setErrorMessage(message); setToastNotification({ type: "error", message, }); return; } setToastNotification({ type: "success", message: response.message || __("Done.", "webba-booking-lite"), }); await onSuccess?.(); sidebar.close(); } catch (e: any) { const message = e?.message || e?.data?.message || __("Failed to update time slots.", "webba-booking-lite"); setErrorMessage(String(message)); setToastNotification({ type: "error", message: String(message), }); } finally { setIsSubmitting(false); } }, [ validate, isSubmitting, timeFrom, timeTo, scheduleToolsAction, action, serviceId, categoryId, dateRange, daysOfWeek, onSuccess, sidebar, setToastNotification, ]); return (
{__("Lock / Unlock multiple time slots", "webba-booking-lite")}
{ setCategoryId(option as SelectOption | null); setFieldErrors((prev) => ({ ...prev, service: false, category: false, })); }} placeholder={__("Select category", "webba-booking-lite")} isClearable />
{ setDateRange(range); if (range[0] && range[1]) { setDateRangeOpen(false); setFieldErrors((prev) => ({ ...prev, dateRange: false })); } }} open={dateRangeOpen} onClickOutside={() => setDateRangeOpen(false)} onInputClick={() => setDateRangeOpen(true)} />
{ setTimeTo(option as SelectOption | null); setFieldErrors((prev) => ({ ...prev, timeFrom: false, timeTo: false, })); }} placeholder={__("To", "webba-booking-lite")} />