316 lines
12 KiB
TypeScript
316 lines
12 KiB
TypeScript
/* eslint-disable @typescript-eslint/no-explicit-any */
|
||
/* eslint-disable react-hooks/exhaustive-deps */
|
||
/* eslint-disable no-irregular-whitespace */
|
||
import Button from "../Button";
|
||
import CloseIcon from "../icons/CloseIcon";
|
||
import Label from "../Label";
|
||
import { useEffect, useState } from "react";
|
||
import Select from "../Select";
|
||
import useModalStore from "../../stores/useModalStore";
|
||
import Input from "../Input";
|
||
import {
|
||
addDays,
|
||
eachMinuteOfInterval,
|
||
parse,
|
||
parseISO,
|
||
startOfDay,
|
||
} from "date-fns";
|
||
import api from "../../utils/api";
|
||
import ChoiceChips from "../ChoiceChips";
|
||
// import ISchedule from "../../types/ISchedule";
|
||
import DatePicker from "../DatePicker";
|
||
import IScheduledSession from "../../types/IScheduledSession";
|
||
import useStore from "../../stores/useStore";
|
||
import ISchedule from "../../types/ISchedule";
|
||
|
||
function CreateScheduleModal() {
|
||
const { company, selectedBuild, schedules, setSchedules } = useStore();
|
||
const setModal = useModalStore((state) => state.setModal);
|
||
const [date, setDate] = useState<Date>(startOfDay(new Date()));
|
||
const [startDate, setStartDate] = useState<Date>(startOfDay(new Date()));
|
||
const [sessionDuration, setSessionDuration] = useState<number>(30);
|
||
const [sessionBreak, setSessionBreak] = useState<number>(5);
|
||
const [startTime, setStartTime] = useState<string>("10:00");
|
||
const [endTime, setEndTime] = useState<string>("20:00");
|
||
const [weekends, setWeekends] = useState(["Сб", "Вс"]);
|
||
const [sessionsPerDay, setSessionsPerDay] = useState<number>();
|
||
|
||
async function getLastScheduledSessionDate() {
|
||
try {
|
||
const result: IScheduledSession | null = await api
|
||
.get(
|
||
`companies/${company?.id}/builds/${selectedBuild?.id}/last_scheduled_session`
|
||
)
|
||
.json();
|
||
|
||
if (!result || !result.startAt) return;
|
||
|
||
setStartDate(startOfDay(parseISO(result.startAt)));
|
||
} catch (error) {
|
||
alert((error as Error).message);
|
||
}
|
||
}
|
||
|
||
useEffect(() => {
|
||
getLastScheduledSessionDate();
|
||
}, []);
|
||
|
||
function calculateSessionsPerDay() {
|
||
// if (!startDate) return;
|
||
|
||
const sessionsPerDay = eachMinuteOfInterval(
|
||
{
|
||
start: parse(startTime, "HH:mm", new Date()),
|
||
end: parse(endTime, "HH:mm", new Date()),
|
||
},
|
||
{ step: sessionDuration + sessionBreak }
|
||
).length;
|
||
|
||
setSessionsPerDay(sessionsPerDay);
|
||
}
|
||
|
||
function changeEndTime(value: string) {
|
||
if (value.split(":")[0] > startTime.split(":")[0]) {
|
||
setEndTime(value);
|
||
}
|
||
}
|
||
|
||
async function addSchedule() {
|
||
try {
|
||
const result: ISchedule = await api
|
||
.post(
|
||
`companies/${company?.id}/builds/${selectedBuild?.id}/schedules`,
|
||
{
|
||
json: {
|
||
startDate: date,
|
||
startTime,
|
||
endTime,
|
||
weekends,
|
||
sessionDuration,
|
||
sessionBreak,
|
||
sessionsPerDay,
|
||
},
|
||
}
|
||
)
|
||
.json();
|
||
|
||
setSchedules([...schedules, result]);
|
||
} catch (error) {
|
||
alert((error as Error).message);
|
||
}
|
||
}
|
||
|
||
async function handleClickCreateSchedule() {
|
||
await addSchedule();
|
||
setModal(null);
|
||
}
|
||
|
||
useEffect(() => {
|
||
calculateSessionsPerDay();
|
||
}, [startDate, endTime, sessionDuration, sessionBreak, weekends]);
|
||
|
||
return (
|
||
<div className="bg-white text-sm relative z-10 rounded-lg w-[896px] shadow-md">
|
||
<div className="border-b border-[#DAE0E5] pl-2 pr-3 h-12 flex justify-between items-center">
|
||
<p className="p-4 font-semibold leading-[115%]">Создание расписания</p>
|
||
<Button
|
||
variant="secondary"
|
||
icon={<CloseIcon />}
|
||
onlyIcon
|
||
onClick={() => setModal(null)}
|
||
/>
|
||
</div>
|
||
|
||
<div className="border-b border-[#DAE0E5] flex">
|
||
<div className="w-full px-6 border-r border-[#DAE0E5]">
|
||
<div className="py-8 border-b border-[#DAE0E5] flex justify-between">
|
||
<div className="space-y-2">
|
||
<p className="font-semibold">Начало действия расписания</p>
|
||
<div className="w-[240px]">
|
||
<p className="text-xs text-[#77828C]">
|
||
Максимальный доступный клиенту промежуток для записи — две
|
||
календарные недели
|
||
</p>
|
||
</div>
|
||
</div>
|
||
|
||
<div className="w-[296px] flex flex-col gap-3">
|
||
<div className="flex flex-col">
|
||
<Label value="Начало" />
|
||
{startDate && (
|
||
<DatePicker
|
||
defaultValue={addDays(startDate, 1)}
|
||
startDate={addDays(startDate, 1)}
|
||
onChange={(date) => setDate(date)}
|
||
/>
|
||
)}
|
||
</div>
|
||
</div>
|
||
</div>
|
||
|
||
<div className="py-8 border-b border-[#DAE0E5] space-y-4">
|
||
<div className="flex justify-between">
|
||
<div className="">
|
||
<p className="font-semibold">Рабочее время</p>
|
||
</div>
|
||
<div className="w-[296px]">
|
||
<div className="flex items-center gap-2">
|
||
<div className="flex flex-col gap-1">
|
||
<Label value="Начало" />
|
||
<Input
|
||
type="time"
|
||
value={startTime}
|
||
onChange={(value) => setStartTime(value)}
|
||
className="w-[137px]"
|
||
/>
|
||
</div>
|
||
<p className="mt-4 text-[#77828C]">-</p>
|
||
<div className="flex flex-col gap-1">
|
||
<Label value="Конец" />
|
||
<Input
|
||
type="time"
|
||
value={endTime}
|
||
onChange={changeEndTime}
|
||
className="w-[137px]"
|
||
/>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
<div className="flex justify-between">
|
||
<div className="space-y-2">
|
||
<p className="font-semibold">Выходные дни</p>
|
||
<div className="w-[240px]">
|
||
<p className="text-xs font-[#77828C]">
|
||
В выбранные дни запись на демонстрацию для клиента будет
|
||
недоступна
|
||
</p>
|
||
</div>
|
||
</div>
|
||
<div className="w-[296px]">
|
||
<ChoiceChips
|
||
options={["Пн", "Вт", "Ср", "Чт", "Пт", "Сб", "Вс"]}
|
||
// selected={["Сб", "Вс"]}
|
||
onChange={(options) => setWeekends(options)}
|
||
/>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
|
||
<div className="py-8 border-b border-[#DAE0E5] flex flex-col gap-8">
|
||
<div className="flex justify-between">
|
||
<div className="flex flex-col gap-2">
|
||
<p className="font-semibold">Продолжительность сеанса</p>
|
||
<p className="text-[#77828C] text-xs w-[240px]">
|
||
Влияет на количество ежедневно проводимых сессий
|
||
</p>
|
||
</div>
|
||
|
||
<div className="w-[296px]">
|
||
<Select
|
||
defaultValue={sessionDuration}
|
||
options={[
|
||
{ value: 5, text: "5 мин." },
|
||
{ value: 10, text: "10 мин." },
|
||
{ value: 15, text: "15 мин." },
|
||
{ value: 20, text: "20 мин." },
|
||
{ value: 25, text: "25 мин." },
|
||
{ value: 30, text: "30 мин." },
|
||
{ value: 35, text: "35 мин." },
|
||
{ value: 40, text: "40 мин." },
|
||
{ value: 45, text: "45 мин." },
|
||
{ value: 50, text: "50 мин." },
|
||
{ value: 55, text: "55 мин." },
|
||
{ value: 60, text: "60 мин." },
|
||
]}
|
||
handleChange={(value) => setSessionDuration(value)}
|
||
/>
|
||
</div>
|
||
</div>
|
||
|
||
<div className="flex justify-between">
|
||
<div className="flex flex-col gap-2">
|
||
<p className="font-semibold">Перерыв между сеансами</p>
|
||
<p className="text-[#77828C] text-xs w-[240px]">
|
||
Нужен, чтобы менеджеры успевали завершать один сеанс и
|
||
переходить к следующему
|
||
</p>
|
||
</div>
|
||
|
||
<div className="w-[296px]">
|
||
<Select
|
||
defaultValue={5}
|
||
options={[
|
||
{ value: 5, text: "5 мин." },
|
||
{ value: 10, text: "10 мин." },
|
||
{ value: 15, text: "15 мин." },
|
||
{ value: 20, text: "20 мин." },
|
||
{ value: 25, text: "25 мин." },
|
||
{ value: 30, text: "30 мин." },
|
||
{ value: 35, text: "35 мин." },
|
||
{ value: 40, text: "40 мин." },
|
||
{ value: 45, text: "45 мин." },
|
||
{ value: 50, text: "50 мин." },
|
||
{ value: 55, text: "55 мин." },
|
||
{ value: 60, text: "60 мин." },
|
||
]}
|
||
handleChange={(value) => setSessionBreak(value)}
|
||
/>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
<div className="min-w-[296px]">
|
||
{/* <div className="p-4 border-b border-[#DAE0E5] ">
|
||
<p className="text-sm font-semibold">Статистика</p>
|
||
</div> */}
|
||
<div className="flex flex-col gap-4 p-4">
|
||
<p className="text-sm font-semibold">Предварительный просмотр</p>
|
||
<div className="flex flex-col gap-3 text-xs">
|
||
<div className="flex flex-col gap-2">
|
||
<div className="grid grid-cols-3">
|
||
<p className="col-span-2 text-[#77828C]">Cеансов в день</p>
|
||
<p>{sessionsPerDay}</p>
|
||
</div>
|
||
<div className="grid grid-cols-3">
|
||
<p className="col-span-2 text-[#77828C]">
|
||
Длительность сеанса
|
||
</p>
|
||
<p>{sessionDuration} мин.</p>
|
||
</div>
|
||
<div className="grid grid-cols-3">
|
||
<p className="col-span-2 text-[#77828C]">Между сеансами</p>
|
||
<p>{sessionBreak} мин.</p>
|
||
</div>
|
||
<div className="grid grid-cols-3">
|
||
<p className="col-span-2 text-[#77828C]">Время работы</p>
|
||
<p>
|
||
{startTime} - {endTime}
|
||
</p>
|
||
</div>
|
||
{/* <div className="grid grid-cols-3">
|
||
<p className="col-span-2 text-[#77828C]">Выходные дни</p>
|
||
<p className="flex flex-wrap gap-1">
|
||
{weekends.map((value) => (
|
||
<span>{value}</span>
|
||
))}
|
||
</p>
|
||
</div> */}
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
|
||
<div className="flex gap-2 px-6 py-3">
|
||
<Button onClick={handleClickCreateSchedule}>Создать</Button>
|
||
<Button variant="secondary" onClick={() => setModal(null)}>
|
||
Отмена
|
||
</Button>
|
||
</div>
|
||
</div>
|
||
);
|
||
}
|
||
|
||
export default CreateScheduleModal;
|