This commit is contained in:
2024-10-15 17:54:13 +05:00
parent ba69acf1ad
commit 45e972780a
11 changed files with 126 additions and 199 deletions
+35 -12
View File
@@ -1,14 +1,8 @@
/* eslint-disable react-hooks/exhaustive-deps */
/* eslint-disable @typescript-eslint/no-explicit-any */
import { useEffect, useState } from "react";
import { ChangeEvent, useEffect, useState } from "react";
import Timeline from "./Timeline";
import {
format,
getDate,
setDate,
setHours,
startOfDay,
} from "date-fns";
import { format, getDate, setDate, setHours, startOfDay } from "date-fns";
// import useEventStore from "../stores/useEventStore";
import Button from "./Button";
import CloseIcon from "./icons/CloseIcon";
@@ -16,15 +10,17 @@ import Input from "./Input";
import Label from "./Label";
import api from "../utils/api";
import useStore from "../stores/useStore";
import Select2 from "./Select2";
import IScheduledSession from "../types/IScheduledSession";
interface Props {
selectedDay: Date;
slots: number;
events: any[];
events: IScheduledSession[];
}
function Schedule({ selectedDay, slots, events }: Props) {
const { selectedBuild } = useStore();
const { company, builds, selectedBuild, setSelectedBuild } = useStore();
const [draftMode, setDraftMode] = useState<boolean>(false);
const [slot, setSlot] = useState<number>();
const [startAt, setStartAt] = useState<Date>();
@@ -77,6 +73,7 @@ function Schedule({ selectedDay, slots, events }: Props) {
await api
.post(`scheduled_sessions`, {
json: {
companyId: company!.id,
buildId: selectedBuild?.id,
slot,
startAt,
@@ -97,9 +94,17 @@ function Schedule({ selectedDay, slots, events }: Props) {
// setModal(null);
}
useEffect(() => {
if (!builds) return;
setSelectedBuild(builds[0]);
console.log(builds);
}, [builds]);
useEffect(() => {
console.log("events", events);
}, []);
}, [events]);
return (
<div className="relative h-screen overflow-y-auto bg-[#F2F2F2] text-sm">
@@ -114,7 +119,7 @@ function Schedule({ selectedDay, slots, events }: Props) {
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>
<p className="font-semibold">Сервер {index + 1}</p>
</div>
))}
</div>
@@ -166,6 +171,24 @@ function Schedule({ selectedDay, slots, events }: Props) {
<div className="px-4 space-y-2">
<p className="font-semibold">Демонстрация</p>
<div className="">
<div className="py-1 space-y-1 text-xs">
<p className="text-[#77828C]">Выберите ЖК</p>
<Select2
defaultValue={selectedBuild?.build}
options={builds.map((build) => ({
value: build.build,
text: build.name,
}))}
onChange={(e: ChangeEvent<HTMLSelectElement>) =>
setSelectedBuild(
builds.find(
(build) =>
build.build === e.target.selectedOptions[0].value
)!
)
}
/>
</div>
<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>
+34
View File
@@ -0,0 +1,34 @@
import { ChangeEvent } from "react";
interface IOption {
value: string;
text: string;
}
interface Props {
defaultValue?: string;
options: IOption[];
onChange: (e: ChangeEvent<HTMLSelectElement>) => void;
}
function Select2({ defaultValue, options, onChange }: Props) {
function handleChange(e: ChangeEvent<HTMLSelectElement>) {
onChange(e);
}
return (
<select
onChange={handleChange}
defaultValue={defaultValue}
className="px-2 py-2.5 outline-none rounded-lg border border-[#DAE0E5] focus:border-[#49A1F5] transition-colors text-sm w-full h-10"
>
{options.map((option, index) => (
<option key={index} value={option.value}>
{option.text}
</option>
))}
</select>
);
}
export default Select2;
+11 -5
View File
@@ -4,9 +4,11 @@ import { useState, MouseEvent, useEffect, useRef } from "react";
import TimelineSlot from "./TimelineSlot";
import { addMinutes, differenceInMinutes, format, startOfDay } from "date-fns";
import Button from "./Button";
import IScheduledSession from "../types/IScheduledSession";
import useStore from "../stores/useStore";
interface Props {
timelineEvents: any[];
timelineEvents: IScheduledSession[];
slot: number;
draftMode: boolean;
onChangeDraftMode: (draftMode: boolean) => void;
@@ -33,6 +35,7 @@ function Timeline({
const ref = useRef<HTMLDivElement>(null);
const [startAt, setStartAt] = useState<string>();
const [duration, setDuration] = useState<number>(30); // min
const { builds } = useStore();
function handleMouseDown(e: MouseEvent<HTMLDivElement>) {
if (draftMode) return;
@@ -168,10 +171,13 @@ function Timeline({
}}
>
<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>
<div className="space-y-1">
<p>
{format(new Date(event.startAt), "HH:mm")} -{" "}
{format(new Date(event.endAt), "HH:mm")}
</p>
<p>{builds.find((build) => build.id === event.buildId)?.name}</p>
</div>
<a
href={`https://stream.graff.tech/scheduled/${event.id}?admin=true`}
target="_blank"
@@ -55,7 +55,7 @@ function CreateScheduledSessionModal({ buildId, startAt, duration }: Props) {
onSubmit={addSchesuledSession}
>
<div className="p-2 pl-6 flex justify-between items-center border-b border-[#DAE0E5]">
<p className="font-semibold text-sm">Запланировать демонстрацию</p>
<p className="text-sm font-semibold">Запланировать демонстрацию</p>
<span className="text-[#77828C]">
<Button
color="tertiary"
@@ -68,18 +68,18 @@ function CreateScheduledSessionModal({ buildId, startAt, duration }: Props) {
<div className="px-6 py-8 space-y-8 border-b border-[#DAE0E5]">
<div className="flex gap-4">
<div className="w-[240px]">
<p className="font-semibold text-sm">Демонстрация</p>
<p className="text-sm font-semibold">Демонстрация</p>
</div>
<div className="w-[296px]">
<div className="grid grid-cols-2 gap-4 text-xs py-1">
<div className="grid 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 grid-cols-2 gap-4 text-xs py-1">
<div className="grid grid-cols-2 gap-4 py-1 text-xs">
<p className="text-[#77828C]">Длительность сеанса</p>
<p className="">{duration} мин.</p>
</div>
{/* <div className="grid grid-cols-2 gap-4 text-xs py-1">
{/* <div className="grid grid-cols-2 gap-4 py-1 text-xs">
<p className="text-[#77828C]">Жилой комплекс</p>
<p className="">Название ЖК</p>
</div> */}
@@ -90,7 +90,7 @@ function CreateScheduledSessionModal({ buildId, startAt, duration }: Props) {
<div className="w-[240px]">
<div className="flex flex-col justify-between h-full">
<div className="space-y-2">
<p className="font-semibold text-sm">Укажите данные клиента*</p>
<p className="text-sm font-semibold">Укажите данные клиента*</p>
<p className="text-xs text-[#77828C]">
На указанный почтовый адрес будет отправлена информация
необходимая для подключения
+23 -157
View File
@@ -1,15 +1,7 @@
/* eslint-disable react-hooks/exhaustive-deps */
/* eslint-disable @typescript-eslint/no-explicit-any */
import {
eachMinuteOfInterval,
format,
parse,
addDays,
subDays,
isEqual,
} from "date-fns";
import { format, addDays, subDays } from "date-fns";
import { useEffect, useRef, useState } from "react";
import TabButton from "../components/TabButton";
import api from "../utils/api";
import { ru } from "date-fns/locale";
import useStore from "../stores/useStore";
@@ -24,6 +16,7 @@ import ChevronRightIcon from "../components/icons/ChevronRightIcon";
import IUser from "../types/IUser";
import IError from "../types/IError";
import Schedule from "../components/Schedule";
import IScheduledSession from "../types/IScheduledSession";
function DashboardPage() {
const { user } = useAuthStore();
@@ -31,20 +24,19 @@ function DashboardPage() {
company,
setCompany,
selectedBuild,
setSelectedBuild,
builds,
setBuilds,
managers,
setManagers,
schedules,
setSchedules,
selectedDay,
setSelectedDay,
} = useStore();
const [, setDuration] = useState<number>();
const [scheduledSessions, setScheduledSessions] = useState<any[]>();
const [, setGeneratedScheduledSessions] = useState<any[]>();
const [dateTimes, setDateTimes] = useState<Date[]>();
// const [, setDuration] = useState<number>();
const [scheduledSessions, setScheduledSessions] = useState<
IScheduledSession[]
>([]);
// const [, setGeneratedScheduledSessions] = useState<any[]>();
// const [dateTimes, setDateTimes] = useState<Date[]>();
const [, setCurrentTime] = useState<string>(format(new Date(), "HH:mm"));
const [, setIsLoadingScheduledSessions] = useState(true);
const scheduledSessionsRef = useRef<HTMLDivElement>(null);
@@ -61,78 +53,6 @@ function DashboardPage() {
setSelectedDay(new Date());
}
function findSessionsByTime(time: Date) {
if (!scheduledSessions || !scheduledSessions.length) return [];
const foundScheduledSessions = scheduledSessions?.filter(
(scheduledSession) => isEqual(new Date(scheduledSession.startAt), time)
);
return foundScheduledSessions;
}
function generateScheduledSessions() {
if (!selectedBuild) return;
const newGeneratedScheduledSession: any[] = [];
dateTimes?.forEach((time) => {
const foundSessionsByTime = findSessionsByTime(time);
const sessions: any[] = [];
if (foundSessionsByTime) {
sessions.push(...foundSessionsByTime);
}
for (
let index = 0;
index < selectedBuild.sessionLimit - foundSessionsByTime.length;
index++
) {
sessions.push({});
}
newGeneratedScheduledSession.push({
time,
sessions,
});
});
setGeneratedScheduledSessions(newGeneratedScheduledSession);
}
useEffect(() => {
if (!scheduledSessions || !selectedBuild || !selectedDay) return;
generateScheduledSessions();
}, [scheduledSessions, selectedBuild, selectedDay]);
useEffect(() => {
if (!schedules || !selectedDay || !selectedBuild) return;
console.log("schedules", schedules);
const foundSchedule = schedules.find(
(schedule) => schedule.buildId === selectedBuild.id
);
if (!foundSchedule) {
setDateTimes([]);
return;
}
const step = foundSchedule.sessionDuration + foundSchedule.sessionBreak; // 35
const newTimes = eachMinuteOfInterval(
{
start: parse(foundSchedule.startTime, "HH:mm", new Date(selectedDay)), // 11:00
end: parse(foundSchedule.endTime, "HH:mm", new Date(selectedDay)), // 20:00
},
{ step }
);
setDateTimes(newTimes);
setDuration(foundSchedule.sessionDuration);
}, [schedules, selectedDay, selectedBuild]);
async function getCompany() {
if (!user) {
console.log("No User", user);
@@ -141,6 +61,7 @@ function DashboardPage() {
try {
const result: any = await api.get(`companies/${user.companyId}`).json();
setCompany(result);
} catch (error) {
if (error instanceof Error) {
@@ -159,6 +80,7 @@ function DashboardPage() {
const result: any = await api
.get(`companies/${user.companyId}/builds`)
.json();
setBuilds(result);
} catch (error) {
if (error instanceof Error) {
@@ -196,22 +118,19 @@ function DashboardPage() {
return;
}
if (!selectedBuild || !selectedBuild.id) {
console.log("No selectedBuild");
return;
}
if (useLoader) setIsLoadingScheduledSessions(true);
try {
const result: any = await api
.get(
`companies/${company.id}/builds/${
selectedBuild.id
`companies/${
company.id
}/scheduled_sessions?date=${selectedDay.toISOString()}`
)
.json();
console.log(result);
setScheduledSessions(result);
} catch (error) {
if (error instanceof Error) {
@@ -222,44 +141,6 @@ function DashboardPage() {
if (useLoader) setIsLoadingScheduledSessions(false);
}
// async function updateScheduledSessionManager(
// scheduledSessionId: string,
// managerId: string | null
// ) {
// if (!company || !scheduledSessions) return;
// try {
// const result: any = await api
// .put(
// `companies/${company.id}/scheduled_sessions/${scheduledSessionId}`,
// {
// json: { userId: managerId },
// }
// )
// .json();
// setScheduledSessions(
// scheduledSessions.map((scheduledSession) =>
// scheduledSession.id === result.id ? result : scheduledSession
// )
// );
// } catch (error) {
// if (error instanceof Error) {
// console.log("Error: ", error.message);
// }
// }
// }
async function getSchedules() {
if (!company || !selectedBuild) return;
const result: any[] = await api
.get(`companies/${company.id}/builds/${selectedBuild.id}/schedules`)
.json();
setSchedules(result);
}
useEffect(() => {
getCompany();
@@ -274,16 +155,19 @@ function DashboardPage() {
useEffect(() => {
if (!company) return;
getBuilds();
}, [company]);
useEffect(() => {
if (!builds) return;
setSelectedBuild(builds[0]);
getManagers();
}, [builds]);
useEffect(() => {
if (!managers || !selectedDay || !selectedBuild) return;
if (!managers || !selectedDay || !company || !builds) return;
getScheduledSessions(true);
const interval = setInterval(() => {
@@ -293,35 +177,17 @@ function DashboardPage() {
return () => {
clearInterval(interval);
};
}, [managers, selectedDay, selectedBuild]);
useEffect(() => {
if (!company || !selectedBuild) return;
getSchedules();
getManagers();
}, [selectedBuild]);
}, [managers, selectedDay, company, builds]);
useEffect(() => {
scheduledSessionsRef.current?.scrollTo({ top: 0, behavior: "smooth" });
}, [selectedDay, selectedBuild]);
}, [selectedDay]);
return (
<div className="flex h-screen main">
<div className="flex flex-col w-full left">
<div className="flex bg-[#F0F1F2]">
<Menu />
{builds?.map((build) => (
<TabButton
key={build.id}
handleClick={() => setSelectedBuild(build)}
active={
(selectedBuild && build.id === selectedBuild?.id) || false
}
>
{build.name}
</TabButton>
))}
</div>
<div className="flex justify-between items-center px-4 py-2 h-12 border-r border-b border-[#DAE0E5]">
@@ -436,10 +302,10 @@ function DashboardPage() {
)}
</div> */}
{selectedBuild?.sessionLimit && scheduledSessions && (
{company && scheduledSessions && (
<Schedule
selectedDay={selectedDay}
slots={selectedBuild.sessionLimit}
slots={company.sessionLimit}
events={scheduledSessions}
/>
)}
+1
View File
@@ -1,6 +1,7 @@
interface ICompany {
id: string;
name: string;
sessionLimit: number;
}
export default ICompany;
+1
View File
@@ -2,6 +2,7 @@ interface IScheduledSession {
id: string;
companyId: string;
buildId: string;
slot: number;
startAt: string;
endAt: string;
client?: unknown;
+4 -4
View File
@@ -21,14 +21,14 @@ companySchema.virtual("users", {
foreignField: "companyId",
});
companySchema.virtual("scheduledSessions", {
ref: "Scheduled_Session",
companySchema.virtual("builds", {
ref: "Build",
localField: "_id",
foreignField: "companyId",
});
companySchema.virtual("builds", {
ref: "Build",
companySchema.virtual("scheduledSessions", {
ref: "Scheduled_Session",
localField: "_id",
foreignField: "companyId",
});
+1 -1
View File
@@ -5,7 +5,7 @@ const scheduledSessionSchema = new Schema(
companyId: {
type: Schema.Types.ObjectId,
ref: "Company",
// required: true,
required: true,
},
buildId: {
type: Schema.Types.ObjectId,
+6 -12
View File
@@ -68,7 +68,7 @@ router.get("/:id/builds/:buildId/users", async (req, res) => {
res.json(users);
});
router.get("/:id/builds/:buildId/scheduled_sessions", async (req, res) => {
router.get("/:id/scheduled_sessions", async (req, res) => {
if (req.params.id != res.locals.user.companyId) {
res.json({ error: "Access denied" });
return;
@@ -82,22 +82,16 @@ router.get("/:id/builds/:buildId/scheduled_sessions", async (req, res) => {
const date = parseISO(req.query.date as string);
const company: any = await Company.findById(req.params.id).populate({
path: "builds",
path: "scheduledSessions",
match: {
_id: req.params.buildId,
},
populate: {
path: "scheduledSessions",
match: {
startAt: {
$gte: startOfDay(date),
$lte: endOfDay(date),
},
startAt: {
$gte: startOfDay(date),
$lte: endOfDay(date),
},
},
});
const { scheduledSessions } = company.builds[0];
const { scheduledSessions } = company;
res.json(scheduledSessions);
});
+4 -2
View File
@@ -75,9 +75,9 @@ router.get("/:buildId", async (req, res) => {
});
router.post("/", async (req, res) => {
const { buildId, slot, startAt, client, duration } = req.body;
const { companyId, buildId, slot, startAt, client, duration } = req.body;
if (!buildId || !startAt || !slot) {
if (!companyId || !buildId || !startAt || !slot) {
return res.json({
status: "error",
message: "Parameters `buildId`, `startAt`, `slot` are required!", // Параметры `buildId`, `startAt`, `slot` обязательны!
@@ -104,6 +104,7 @@ router.post("/", async (req, res) => {
if (duration) {
const scheduledSessions = await ScheduledSession.find({
companyId,
buildId,
slot,
startAt: {
@@ -141,6 +142,7 @@ router.post("/", async (req, res) => {
}
const scheduledSession = await ScheduledSession.create({
companyId,
buildId,
slot,
startAt: startAtISO,