upd
This commit is contained in:
@@ -0,0 +1,226 @@
|
|||||||
|
/* eslint-disable react-hooks/exhaustive-deps */
|
||||||
|
/* eslint-disable @typescript-eslint/no-explicit-any */
|
||||||
|
import { useEffect, useState } from "react";
|
||||||
|
import Timeline from "./Timeline";
|
||||||
|
import { format, setHours, startOfDay } from "date-fns";
|
||||||
|
// import useEventStore from "../stores/useEventStore";
|
||||||
|
import Button from "./Button";
|
||||||
|
import CloseIcon from "./icons/CloseIcon";
|
||||||
|
import Input from "./Input";
|
||||||
|
import Label from "./Label";
|
||||||
|
import api from "../utils/api";
|
||||||
|
import useStore from "../stores/useStore";
|
||||||
|
|
||||||
|
interface Props {
|
||||||
|
slots: number;
|
||||||
|
events: any[];
|
||||||
|
}
|
||||||
|
|
||||||
|
function Schedule({ slots, events }: Props) {
|
||||||
|
const { selectedBuild } = useStore();
|
||||||
|
const [draftMode, setDraftMode] = useState<boolean>(false);
|
||||||
|
const [slot, setSlot] = useState<number>();
|
||||||
|
const [startAt, setStartAt] = useState<Date>();
|
||||||
|
const [duration, setDuration] = useState<number>();
|
||||||
|
const [email, setEmail] = useState<string>("");
|
||||||
|
const [name, setName] = useState<string>("");
|
||||||
|
const [phone, setPhone] = useState<string>("");
|
||||||
|
|
||||||
|
function handleChangeSlot(slot: number) {
|
||||||
|
setSlot(slot);
|
||||||
|
}
|
||||||
|
|
||||||
|
function handleChangeDraftMode(draftMode: boolean) {
|
||||||
|
setDraftMode(draftMode);
|
||||||
|
}
|
||||||
|
|
||||||
|
function handleChangeStartAt(startAt: Date) {
|
||||||
|
setStartAt(startAt);
|
||||||
|
}
|
||||||
|
|
||||||
|
function handleChangeDuration(duration: number) {
|
||||||
|
setDuration(duration);
|
||||||
|
}
|
||||||
|
|
||||||
|
async function handleClickSave() {
|
||||||
|
if (!slot || !startAt || !duration) return;
|
||||||
|
|
||||||
|
await addSchesuledSession();
|
||||||
|
|
||||||
|
// setEvents([
|
||||||
|
// ...events,
|
||||||
|
// {
|
||||||
|
// slot,
|
||||||
|
// startAt,
|
||||||
|
// endAt: addMinutes(startAt, duration),
|
||||||
|
// },
|
||||||
|
// ]);
|
||||||
|
|
||||||
|
setDraftMode(false);
|
||||||
|
}
|
||||||
|
|
||||||
|
function handleClickCancel() {
|
||||||
|
setDraftMode(false);
|
||||||
|
}
|
||||||
|
|
||||||
|
async function addSchesuledSession() {
|
||||||
|
// setIsLoading(true);
|
||||||
|
|
||||||
|
try {
|
||||||
|
await api
|
||||||
|
.post(`scheduled_sessions`, {
|
||||||
|
json: {
|
||||||
|
buildId: selectedBuild?.id,
|
||||||
|
slot,
|
||||||
|
startAt,
|
||||||
|
duration,
|
||||||
|
client: {
|
||||||
|
email,
|
||||||
|
phone,
|
||||||
|
name,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
})
|
||||||
|
.json();
|
||||||
|
} catch (error) {
|
||||||
|
alert((error as Error).message);
|
||||||
|
}
|
||||||
|
|
||||||
|
// setIsLoading(false);
|
||||||
|
// setModal(null);
|
||||||
|
}
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
console.log("events", events);
|
||||||
|
}, []);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div className="relative h-screen overflow-y-auto bg-[#F2F2F2] text-sm">
|
||||||
|
<div className="fixed z-10 flex">
|
||||||
|
<div className="bg-white h-10 w-[84px] flex items-center justify-center border-r border-b border-[#DAE0E5]">
|
||||||
|
<p className="font-semibold">{format(new Date(), "HH:mm")}</p>
|
||||||
|
</div>
|
||||||
|
<div className="flex">
|
||||||
|
{slots &&
|
||||||
|
Array.from({ length: slots }).map((_, index) => (
|
||||||
|
<div
|
||||||
|
key={index}
|
||||||
|
className="border-r border-b border-[#DAE0E5] w-[264px] h-10 flex items-center pl-3 bg-[#F0F1F2]"
|
||||||
|
>
|
||||||
|
<p className="font-semibold">Слот {index + 1}</p>
|
||||||
|
</div>
|
||||||
|
))}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div className="flex mt-10">
|
||||||
|
<div className="">
|
||||||
|
{Array.from({ length: 24 }).map((_, index) => (
|
||||||
|
<div
|
||||||
|
key={index}
|
||||||
|
className="flex items-center justify-center h-[180px] w-[84px] border-r border-b border-[#DAE0E5]"
|
||||||
|
>
|
||||||
|
<p className="font-semibold">
|
||||||
|
{format(setHours(startOfDay(new Date()), index), "HH:mm")}
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
))}
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{slots &&
|
||||||
|
Array.from({ length: slots }).map((_, index) => (
|
||||||
|
<Timeline
|
||||||
|
key={index}
|
||||||
|
slot={index + 1}
|
||||||
|
timelineEvents={events.filter(
|
||||||
|
(event) => event.slot === index + 1
|
||||||
|
)}
|
||||||
|
draftMode={draftMode}
|
||||||
|
onChangeSlot={handleChangeSlot}
|
||||||
|
onChangeDraftMode={handleChangeDraftMode}
|
||||||
|
onChangeStartAt={handleChangeStartAt}
|
||||||
|
onChangeDuration={handleChangeDuration}
|
||||||
|
/>
|
||||||
|
))}
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{draftMode && startAt && (
|
||||||
|
<div className="fixed top-0 right-0 flex flex-col justify-between h-screen overflow-y-auto bg-white shadow w-[320px]">
|
||||||
|
<div className="space-y-4">
|
||||||
|
<div className="p-2 pl-4 flex items-center justify-between border-b border-[#DAE0E5]">
|
||||||
|
<p className="font-semibold">Запланировать демонстрацию</p>
|
||||||
|
<Button
|
||||||
|
color="tertiary"
|
||||||
|
icon={<CloseIcon />}
|
||||||
|
onlyIcon
|
||||||
|
handleClick={handleClickCancel}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
<div className="px-4 space-y-2">
|
||||||
|
<p className="font-semibold">Демонстрация</p>
|
||||||
|
<div className="">
|
||||||
|
<div className="grid items-center grid-cols-2 gap-4 py-1 text-xs">
|
||||||
|
<p className="text-[#77828C]">Дата и время</p>
|
||||||
|
<p className="">{format(startAt, "dd.MM.yyyy HH:mm")}</p>
|
||||||
|
</div>
|
||||||
|
<div className="grid items-center grid-cols-2 gap-4 py-1 text-xs">
|
||||||
|
<p className="text-[#77828C]">Длительность сеанса</p>
|
||||||
|
<p className="">{duration} мин.</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div className="px-4 space-y-2">
|
||||||
|
<p className="font-semibold">Клиент</p>
|
||||||
|
<div className="space-y-4">
|
||||||
|
<div className="space-y-1">
|
||||||
|
<Label value="Email" />
|
||||||
|
<Input
|
||||||
|
type="email"
|
||||||
|
className="w-full h-10"
|
||||||
|
value={email}
|
||||||
|
handleChange={(value) => setEmail(value)}
|
||||||
|
/>
|
||||||
|
<span className="text-[#77828C] text-xs">
|
||||||
|
На указанный почтовый адрес придет необходимая для
|
||||||
|
подключения информация
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
<div className="space-y-1">
|
||||||
|
<Label value="Телефон" />
|
||||||
|
<Input
|
||||||
|
type="tel"
|
||||||
|
className="w-full h-10"
|
||||||
|
value={phone}
|
||||||
|
handleChange={(value) => setPhone(value)}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
<div className="space-y-1">
|
||||||
|
<Label value="Имя" />
|
||||||
|
<Input
|
||||||
|
className="w-full h-10"
|
||||||
|
value={name}
|
||||||
|
handleChange={(value) => setName(value)}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div className="flex gap-2 p-4">
|
||||||
|
<Button type="submit" handleClick={handleClickSave}>
|
||||||
|
Запланировать
|
||||||
|
</Button>
|
||||||
|
<Button
|
||||||
|
type="button"
|
||||||
|
color="secondary"
|
||||||
|
handleClick={handleClickCancel}
|
||||||
|
>
|
||||||
|
Отмена
|
||||||
|
</Button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
export default Schedule;
|
||||||
@@ -0,0 +1,203 @@
|
|||||||
|
/* eslint-disable @typescript-eslint/no-explicit-any */
|
||||||
|
/* eslint-disable react-hooks/exhaustive-deps */
|
||||||
|
import { useState, MouseEvent, useEffect, useRef } from "react";
|
||||||
|
import TimelineSlot from "./TimelineSlot";
|
||||||
|
import { addMinutes, differenceInMinutes, format, startOfDay } from "date-fns";
|
||||||
|
import Button from "./Button";
|
||||||
|
|
||||||
|
interface Props {
|
||||||
|
timelineEvents: any[];
|
||||||
|
slot: number;
|
||||||
|
draftMode: boolean;
|
||||||
|
onChangeDraftMode: (draftMode: boolean) => void;
|
||||||
|
onChangeStartAt: (startAt: Date) => void;
|
||||||
|
onChangeDuration: (duration: number) => void;
|
||||||
|
onChangeSlot: (slot: number) => void;
|
||||||
|
}
|
||||||
|
|
||||||
|
const timelineSlotHeight = 180;
|
||||||
|
const minutePx = timelineSlotHeight / 60;
|
||||||
|
|
||||||
|
function Timeline({
|
||||||
|
timelineEvents,
|
||||||
|
slot,
|
||||||
|
draftMode,
|
||||||
|
onChangeDraftMode,
|
||||||
|
onChangeStartAt,
|
||||||
|
onChangeDuration,
|
||||||
|
onChangeSlot,
|
||||||
|
}: Props) {
|
||||||
|
const [pressed, setPressed] = useState(false);
|
||||||
|
const [startPosY, setStartPosY] = useState<number>();
|
||||||
|
const [currentPosY, setCurrentPosY] = useState<number>();
|
||||||
|
const ref = useRef<HTMLDivElement>(null);
|
||||||
|
const [startAt, setStartAt] = useState<string>();
|
||||||
|
const [duration, setDuration] = useState<number>(30); // min
|
||||||
|
|
||||||
|
function handleMouseDown(e: MouseEvent<HTMLDivElement>) {
|
||||||
|
if (draftMode) return;
|
||||||
|
|
||||||
|
const rect = e.currentTarget.getBoundingClientRect();
|
||||||
|
const y = e.clientY - rect.top;
|
||||||
|
const roundedY = Math.floor(y / (minutePx * 10)) * (minutePx * 10);
|
||||||
|
|
||||||
|
ref.current!.style.top = `${roundedY}px`;
|
||||||
|
|
||||||
|
setStartPosY(roundedY);
|
||||||
|
setCurrentPosY(roundedY + minutePx * duration);
|
||||||
|
setPressed(true);
|
||||||
|
|
||||||
|
if (!duration) {
|
||||||
|
setDuration(30);
|
||||||
|
}
|
||||||
|
|
||||||
|
setStartAt(
|
||||||
|
addMinutes(startOfDay(new Date()), roundedY / minutePx).toISOString()
|
||||||
|
);
|
||||||
|
|
||||||
|
onChangeSlot(slot);
|
||||||
|
}
|
||||||
|
|
||||||
|
function handleMouseMove(e: MouseEvent) {
|
||||||
|
if (!pressed || startPosY === undefined) return;
|
||||||
|
|
||||||
|
const rect = e.currentTarget.getBoundingClientRect();
|
||||||
|
const y = e.clientY - rect.top;
|
||||||
|
|
||||||
|
// if (y < startPosY + minutePx * 30) return;
|
||||||
|
|
||||||
|
// if (y < startPosY + minutePx * 30) return;
|
||||||
|
|
||||||
|
if (y < startPosY + minutePx * 30) {
|
||||||
|
setDuration(30);
|
||||||
|
} else {
|
||||||
|
const roundedY =
|
||||||
|
Math.round((y - startPosY) / (minutePx * 10)) * (minutePx * 10);
|
||||||
|
|
||||||
|
setCurrentPosY(y);
|
||||||
|
setDuration(roundedY / minutePx);
|
||||||
|
|
||||||
|
ref.current!.style.height = `${roundedY}px`;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function handleMouseUp() {
|
||||||
|
if (!pressed) return;
|
||||||
|
|
||||||
|
setPressed(false);
|
||||||
|
onChangeDraftMode(true);
|
||||||
|
}
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
if (!startPosY) return;
|
||||||
|
|
||||||
|
console.log("startPosY", startPosY);
|
||||||
|
}, [startPosY]);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
if (!currentPosY) return;
|
||||||
|
|
||||||
|
console.log("currentPosY", currentPosY);
|
||||||
|
}, [currentPosY]);
|
||||||
|
|
||||||
|
// useEffect(() => {
|
||||||
|
// setTimelineEvents(
|
||||||
|
// timelineEvents.filter((event: IEvent) => event.slot === slot)
|
||||||
|
// );
|
||||||
|
// }, [timelineEvents]);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
console.log("startAt", startAt);
|
||||||
|
|
||||||
|
if (!startAt) return;
|
||||||
|
|
||||||
|
onChangeStartAt(new Date(startAt));
|
||||||
|
}, [startAt]);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
if (!duration || !ref.current) return;
|
||||||
|
|
||||||
|
if (ref.current.clientHeight === 0) {
|
||||||
|
ref.current.style.height = `${minutePx * 30}px`;
|
||||||
|
}
|
||||||
|
|
||||||
|
onChangeDuration(duration);
|
||||||
|
}, [duration]);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
if (draftMode || !ref.current) return;
|
||||||
|
|
||||||
|
setStartAt(undefined);
|
||||||
|
setDuration(0);
|
||||||
|
ref.current.style.height = "0px";
|
||||||
|
console.log(ref.current.clientHeight);
|
||||||
|
}, [draftMode]);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div className="relative text-xs select-none">
|
||||||
|
<div
|
||||||
|
className={`${pressed ? "cursor-move" : ""}`}
|
||||||
|
onMouseDown={handleMouseDown}
|
||||||
|
onMouseMove={handleMouseMove}
|
||||||
|
onMouseUp={handleMouseUp}
|
||||||
|
>
|
||||||
|
{Array.from({ length: 24 }).map((_, index) => (
|
||||||
|
<TimelineSlot
|
||||||
|
key={index}
|
||||||
|
height={timelineSlotHeight}
|
||||||
|
// date={setHours(setMinutes(new Date(), 0), index)}
|
||||||
|
/>
|
||||||
|
))}
|
||||||
|
|
||||||
|
<div ref={ref} className="absolute w-full">
|
||||||
|
{ref.current?.clientHeight !== 0 && (
|
||||||
|
<div className="h-full bg-black/20 animate-pulse border border-[#DAE0E5] p-3">
|
||||||
|
{startAt && format(new Date(startAt), "HH:mm")} -{" "}
|
||||||
|
{startAt &&
|
||||||
|
duration &&
|
||||||
|
format(addMinutes(new Date(startAt), duration), "HH:mm")}
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{timelineEvents.map((event) => (
|
||||||
|
<div
|
||||||
|
key={event.id}
|
||||||
|
data-type="event"
|
||||||
|
className="absolute w-full bg-white border-b border-r border-[#DAE0E5] p-3"
|
||||||
|
style={{
|
||||||
|
top: `${
|
||||||
|
differenceInMinutes(
|
||||||
|
new Date(event.startAt),
|
||||||
|
startOfDay(new Date(event.startAt))
|
||||||
|
) * minutePx
|
||||||
|
}px`,
|
||||||
|
height: `${
|
||||||
|
differenceInMinutes(
|
||||||
|
new Date(event.endAt),
|
||||||
|
new Date(event.startAt)
|
||||||
|
) * minutePx
|
||||||
|
}px`,
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<div className="flex flex-col justify-between h-full">
|
||||||
|
<p>
|
||||||
|
{format(new Date(event.startAt), "HH:mm")} -{" "}
|
||||||
|
{format(new Date(event.endAt), "HH:mm")}
|
||||||
|
</p>
|
||||||
|
<a
|
||||||
|
href={`https://stream.graff.tech/scheduled/${event.id}?admin=true`}
|
||||||
|
target="_blank"
|
||||||
|
className="self-end"
|
||||||
|
>
|
||||||
|
<Button className="">Начать</Button>
|
||||||
|
</a>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
))}
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
export default Timeline;
|
||||||
@@ -0,0 +1,14 @@
|
|||||||
|
interface Props {
|
||||||
|
height: number; // px
|
||||||
|
}
|
||||||
|
|
||||||
|
function TimelineSlot({ height }: Props) {
|
||||||
|
return (
|
||||||
|
<div
|
||||||
|
style={{ height: `${height}px` }}
|
||||||
|
className="border-r border-b border-[#DAE0E5] w-[264px]"
|
||||||
|
></div>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
export default TimelineSlot;
|
||||||
@@ -9,26 +9,21 @@ import {
|
|||||||
isEqual,
|
isEqual,
|
||||||
} from "date-fns";
|
} from "date-fns";
|
||||||
import { useEffect, useRef, useState } from "react";
|
import { useEffect, useRef, useState } from "react";
|
||||||
import Card from "../components/Card";
|
|
||||||
import EmptyCard from "../components/EmptyCard";
|
|
||||||
import TabButton from "../components/TabButton";
|
import TabButton from "../components/TabButton";
|
||||||
import api from "../utils/api";
|
import api from "../utils/api";
|
||||||
import { ru } from "date-fns/locale";
|
import { ru } from "date-fns/locale";
|
||||||
import { Transition } from "react-transition-group";
|
|
||||||
import _ from "lodash";
|
|
||||||
import useStore from "../stores/useStore";
|
import useStore from "../stores/useStore";
|
||||||
import useAuthStore from "../stores/useAuthStore";
|
import useAuthStore from "../stores/useAuthStore";
|
||||||
import Menu from "../components/Menu";
|
import Menu from "../components/Menu";
|
||||||
import Button from "../components/Button";
|
import Button from "../components/Button";
|
||||||
import Calendar from "../components/Calendar";
|
import Calendar from "../components/Calendar";
|
||||||
import Managers from "../components/Managers";
|
import Managers from "../components/Managers";
|
||||||
import Schedules from "../components/Schedules";
|
|
||||||
import SpinnerIcon from "../components/icons/SpinnerIcon";
|
|
||||||
import ModalContainer from "../components/ModalContainer";
|
import ModalContainer from "../components/ModalContainer";
|
||||||
import ChevronLeftIcon from "../components/icons/ChevronLeftIcon";
|
import ChevronLeftIcon from "../components/icons/ChevronLeftIcon";
|
||||||
import ChevronRightIcon from "../components/icons/ChevronRightIcon";
|
import ChevronRightIcon from "../components/icons/ChevronRightIcon";
|
||||||
import IUser from "../types/IUser";
|
import IUser from "../types/IUser";
|
||||||
import IError from "../types/IError";
|
import IError from "../types/IError";
|
||||||
|
import Schedule from "../components/Schedule";
|
||||||
|
|
||||||
function DashboardPage() {
|
function DashboardPage() {
|
||||||
const { user } = useAuthStore();
|
const { user } = useAuthStore();
|
||||||
@@ -46,16 +41,12 @@ function DashboardPage() {
|
|||||||
selectedDay,
|
selectedDay,
|
||||||
setSelectedDay,
|
setSelectedDay,
|
||||||
} = useStore();
|
} = useStore();
|
||||||
const [duration, setDuration] = useState<number>();
|
const [, setDuration] = useState<number>();
|
||||||
const [scheduledSessions, setScheduledSessions] = useState<any[]>();
|
const [scheduledSessions, setScheduledSessions] = useState<any[]>();
|
||||||
const [generatedScheduledSessions, setGeneratedScheduledSessions] =
|
const [, setGeneratedScheduledSessions] = useState<any[]>();
|
||||||
useState<any[]>();
|
|
||||||
const [dateTimes, setDateTimes] = useState<Date[]>();
|
const [dateTimes, setDateTimes] = useState<Date[]>();
|
||||||
const [currentTime, setCurrentTime] = useState<string>(
|
const [, setCurrentTime] = useState<string>(format(new Date(), "HH:mm"));
|
||||||
format(new Date(), "HH:mm")
|
const [, setIsLoadingScheduledSessions] = useState(true);
|
||||||
);
|
|
||||||
const [isLoadingScheduledSessions, setIsLoadingScheduledSessions] =
|
|
||||||
useState(true);
|
|
||||||
const scheduledSessionsRef = useRef<HTMLDivElement>(null);
|
const scheduledSessionsRef = useRef<HTMLDivElement>(null);
|
||||||
|
|
||||||
function selectNextDay() {
|
function selectNextDay() {
|
||||||
@@ -231,33 +222,33 @@ function DashboardPage() {
|
|||||||
if (useLoader) setIsLoadingScheduledSessions(false);
|
if (useLoader) setIsLoadingScheduledSessions(false);
|
||||||
}
|
}
|
||||||
|
|
||||||
async function updateScheduledSessionManager(
|
// async function updateScheduledSessionManager(
|
||||||
scheduledSessionId: string,
|
// scheduledSessionId: string,
|
||||||
managerId: string | null
|
// managerId: string | null
|
||||||
) {
|
// ) {
|
||||||
if (!company || !scheduledSessions) return;
|
// if (!company || !scheduledSessions) return;
|
||||||
|
|
||||||
try {
|
// try {
|
||||||
const result: any = await api
|
// const result: any = await api
|
||||||
.put(
|
// .put(
|
||||||
`companies/${company.id}/scheduled_sessions/${scheduledSessionId}`,
|
// `companies/${company.id}/scheduled_sessions/${scheduledSessionId}`,
|
||||||
{
|
// {
|
||||||
json: { userId: managerId },
|
// json: { userId: managerId },
|
||||||
}
|
// }
|
||||||
)
|
// )
|
||||||
.json();
|
// .json();
|
||||||
|
|
||||||
setScheduledSessions(
|
// setScheduledSessions(
|
||||||
scheduledSessions.map((scheduledSession) =>
|
// scheduledSessions.map((scheduledSession) =>
|
||||||
scheduledSession.id === result.id ? result : scheduledSession
|
// scheduledSession.id === result.id ? result : scheduledSession
|
||||||
)
|
// )
|
||||||
);
|
// );
|
||||||
} catch (error) {
|
// } catch (error) {
|
||||||
if (error instanceof Error) {
|
// if (error instanceof Error) {
|
||||||
console.log("Error: ", error.message);
|
// console.log("Error: ", error.message);
|
||||||
}
|
// }
|
||||||
}
|
// }
|
||||||
}
|
// }
|
||||||
|
|
||||||
async function getSchedules() {
|
async function getSchedules() {
|
||||||
if (!company || !selectedBuild) return;
|
if (!company || !selectedBuild) return;
|
||||||
@@ -316,8 +307,8 @@ function DashboardPage() {
|
|||||||
}, [selectedDay, selectedBuild]);
|
}, [selectedDay, selectedBuild]);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="main h-screen flex">
|
<div className="flex h-screen main">
|
||||||
<div className="left flex flex-col w-full">
|
<div className="flex flex-col w-full left">
|
||||||
<div className="flex bg-[#F0F1F2]">
|
<div className="flex bg-[#F0F1F2]">
|
||||||
<Menu />
|
<Menu />
|
||||||
{builds?.map((build) => (
|
{builds?.map((build) => (
|
||||||
@@ -358,7 +349,7 @@ function DashboardPage() {
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div className="flex bg-[#F2F2F2]">
|
{/* <div className="flex bg-[#F2F2F2]">
|
||||||
<div className="w-[84px] h-[40px] flex justify-center items-center text-sm font-semibold bg-white border-r border-b border-[#DAE0E5]">
|
<div className="w-[84px] h-[40px] flex justify-center items-center text-sm font-semibold bg-white border-r border-b border-[#DAE0E5]">
|
||||||
{currentTime}
|
{currentTime}
|
||||||
</div>
|
</div>
|
||||||
@@ -371,9 +362,9 @@ function DashboardPage() {
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
))}
|
))}
|
||||||
</div>
|
</div> */}
|
||||||
|
|
||||||
<div
|
{/* <div
|
||||||
ref={scheduledSessionsRef}
|
ref={scheduledSessionsRef}
|
||||||
className={`overflow-y-auto overflow-x-hidden flex-1 bg-[#F2F2F2] border-r border-[#DAE0E5]`}
|
className={`overflow-y-auto overflow-x-hidden flex-1 bg-[#F2F2F2] border-r border-[#DAE0E5]`}
|
||||||
>
|
>
|
||||||
@@ -443,7 +434,14 @@ function DashboardPage() {
|
|||||||
</div>
|
</div>
|
||||||
)
|
)
|
||||||
)}
|
)}
|
||||||
</div>
|
</div> */}
|
||||||
|
|
||||||
|
{selectedBuild?.sessionLimit && scheduledSessions && (
|
||||||
|
<Schedule
|
||||||
|
slots={selectedBuild.sessionLimit}
|
||||||
|
events={scheduledSessions}
|
||||||
|
/>
|
||||||
|
)}
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div className="right w-[384px] flex flex-col">
|
<div className="right w-[384px] flex flex-col">
|
||||||
@@ -457,9 +455,9 @@ function DashboardPage() {
|
|||||||
Статистика
|
Статистика
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
<div className="overflow-y-auto overflow-x-hidden">
|
<div className="overflow-x-hidden overflow-y-auto">
|
||||||
<Calendar />
|
<Calendar />
|
||||||
<Schedules />
|
{/* <Schedules /> */}
|
||||||
<Managers />
|
<Managers />
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -0,0 +1,14 @@
|
|||||||
|
import { create } from "zustand";
|
||||||
|
import IEvent from "../types/IEvent";
|
||||||
|
|
||||||
|
interface EventState {
|
||||||
|
events: IEvent[];
|
||||||
|
setEvents: (events: IEvent[]) => void;
|
||||||
|
}
|
||||||
|
|
||||||
|
const useEventStore = create<EventState>()((set) => ({
|
||||||
|
events: [],
|
||||||
|
setEvents: (events) => set({ events }),
|
||||||
|
}));
|
||||||
|
|
||||||
|
export default useEventStore;
|
||||||
@@ -0,0 +1,7 @@
|
|||||||
|
interface IEvent {
|
||||||
|
slot: number;
|
||||||
|
startAt: Date;
|
||||||
|
endAt: Date;
|
||||||
|
}
|
||||||
|
|
||||||
|
export default IEvent;
|
||||||
@@ -16,10 +16,18 @@ const scheduledSessionSchema = new Schema(
|
|||||||
type: Schema.Types.ObjectId,
|
type: Schema.Types.ObjectId,
|
||||||
ref: "User",
|
ref: "User",
|
||||||
},
|
},
|
||||||
|
slot: {
|
||||||
|
type: Number,
|
||||||
|
required: true,
|
||||||
|
},
|
||||||
startAt: {
|
startAt: {
|
||||||
type: Date,
|
type: Date,
|
||||||
required: true,
|
required: true,
|
||||||
},
|
},
|
||||||
|
duration: {
|
||||||
|
type: Number,
|
||||||
|
required: true,
|
||||||
|
},
|
||||||
endAt: {
|
endAt: {
|
||||||
type: Date,
|
type: Date,
|
||||||
required: true,
|
required: true,
|
||||||
|
|||||||
@@ -75,12 +75,12 @@ router.get("/:buildId", async (req, res) => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
router.post("/", async (req, res) => {
|
router.post("/", async (req, res) => {
|
||||||
const { buildId, startAt, client, duration } = req.body;
|
const { buildId, slot, startAt, client, duration } = req.body;
|
||||||
|
|
||||||
if (!buildId || !startAt) {
|
if (!buildId || !startAt || !slot) {
|
||||||
return res.json({
|
return res.json({
|
||||||
status: "error",
|
status: "error",
|
||||||
message: "Parameters `buildId`, `startAt` are required!", // Параметры `buildId`, `startAt` обязательны!
|
message: "Parameters `buildId`, `startAt`, `slot` are required!", // Параметры `buildId`, `startAt`, `slot` обязательны!
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -105,6 +105,7 @@ router.post("/", async (req, res) => {
|
|||||||
if (duration) {
|
if (duration) {
|
||||||
const scheduledSessions = await ScheduledSession.find({
|
const scheduledSessions = await ScheduledSession.find({
|
||||||
buildId,
|
buildId,
|
||||||
|
slot,
|
||||||
startAt: {
|
startAt: {
|
||||||
$gte: startOfDay(startAtISO),
|
$gte: startOfDay(startAtISO),
|
||||||
$lte: endOfDay(startAtISO),
|
$lte: endOfDay(startAtISO),
|
||||||
@@ -141,7 +142,9 @@ router.post("/", async (req, res) => {
|
|||||||
|
|
||||||
const scheduledSession = await ScheduledSession.create({
|
const scheduledSession = await ScheduledSession.create({
|
||||||
buildId,
|
buildId,
|
||||||
|
slot,
|
||||||
startAt: startAtISO,
|
startAt: startAtISO,
|
||||||
|
duration,
|
||||||
endAt: endAtISO,
|
endAt: endAtISO,
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user