From 4c83933741032fcd58fbdd9c8ac3c07b41cf4adc Mon Sep 17 00:00:00 2001 From: inmake Date: Mon, 16 Oct 2023 12:23:31 +0500 Subject: [PATCH] upd --- client/src/components/Card.tsx | 143 ++++++++++++------ client/src/components/SelectUser.tsx | 64 ++++++++ client/src/components/icons/CheckIcon.tsx | 20 +++ client/src/components/icons/ChevronDown.tsx | 21 +++ client/src/hooks/useOutsideClick.ts | 23 +++ client/src/pages/DashboardPage.tsx | 157 +++++++++++++------- server/src/routes/app.ts | 3 +- server/src/routes/companies.ts | 99 ++++++++++++ 8 files changed, 435 insertions(+), 95 deletions(-) create mode 100644 client/src/components/SelectUser.tsx create mode 100644 client/src/components/icons/CheckIcon.tsx create mode 100644 client/src/components/icons/ChevronDown.tsx create mode 100644 client/src/hooks/useOutsideClick.ts diff --git a/client/src/components/Card.tsx b/client/src/components/Card.tsx index 563c5bc..10846a9 100644 --- a/client/src/components/Card.tsx +++ b/client/src/components/Card.tsx @@ -1,7 +1,15 @@ +/* eslint-disable react-hooks/exhaustive-deps */ /* eslint-disable @typescript-eslint/no-explicit-any */ +import { useEffect, useState } from "react"; +import SelectUser from "./SelectUser"; import MoreIcon from "./icons/MoreIcon"; +import api from "../utils/api"; interface CardProps { + companyId: string; + buildId: string; + scheduledSessionId: string; + scheduleSessionStartAt: string; client: { name: string; phone: string; @@ -12,57 +20,106 @@ interface CardProps { name: string; avatar: string; }; + managers: any[]; + handleSelect: (scheduledSessionId: string, managerId: string) => void; } -function Card({ client, manager }: CardProps) { +function Card({ + companyId, + buildId, + scheduledSessionId, + scheduleSessionStartAt, + client, + manager, + managers, + handleSelect, +}: CardProps) { + const [isShow, setIsShow] = useState(false); + const [availableManagers, setAvailableManagers] = useState(); + + async function getAvailableManagers() { + const result: any[] = await api + .get( + `companies/${companyId}/builds/${buildId}/scheduled_sessions/${scheduledSessionId}/availableManagers?startAt=${scheduleSessionStartAt}` + ) + .json(); + + const filteredManagers = managers.filter( + (manager) => !result.includes(manager.id) + ); + + setAvailableManagers(filteredManagers); + } + + useEffect(() => { + if (!isShow) return; + getAvailableManagers(); + }, [isShow]); + return ( -
-
-

{client.name}

- {manager ? ( -
-
-

- Сеанс начат -

-
- ) : ( -
-
-

- Нет менеджена -

-
- )} -
-
-

{client.phone}

-

{client.email}

-
-
-
+
+
+
+

{client.name}

{manager ? ( -
- -
+
+
+

+ Сеанс начат +

) : ( -
+
+
+

+ Нет менеджера +

+
)} -

- {manager ? manager.name : "Не назначен"} -

- +
+

{client.phone}

+

{client.email}

+
+
+
+ {manager ? ( +
+ + {/*
*/} +
+ ) : ( +
+ )} +

+ {manager ? manager.name : "Не назначен"} +

+
+ +
+
+ +
+ {availableManagers?.length && ( + ( + handleSelect(scheduledSessionId, managerId), setIsShow(false) + )} + handleShown={() => setIsShow((prev) => !prev)} + /> + )}
); diff --git a/client/src/components/SelectUser.tsx b/client/src/components/SelectUser.tsx new file mode 100644 index 0000000..264df01 --- /dev/null +++ b/client/src/components/SelectUser.tsx @@ -0,0 +1,64 @@ +/* eslint-disable react-hooks/exhaustive-deps */ +/* eslint-disable @typescript-eslint/no-explicit-any */ +interface SelectUserProps { + shown: boolean; + selectedManagerId?: string; + managers: any[]; + handleClick: (managerId: string) => void; + handleShown: () => void; +} + +import { Transition } from "react-transition-group"; +import CheckIcon from "./icons/CheckIcon"; +import useOutsideClick from "../hooks/useOutsideClick"; + +function SelectUser({ + shown, + selectedManagerId, + managers, + handleClick, + handleShown, +}: SelectUserProps) { + const selectUserRef = useOutsideClick(handleShown); + + return ( + + {(state) => ( +
+ {managers.map((manager) => ( + + ))} +
+ )} +
+ ); +} + +export default SelectUser; diff --git a/client/src/components/icons/CheckIcon.tsx b/client/src/components/icons/CheckIcon.tsx new file mode 100644 index 0000000..8477d31 --- /dev/null +++ b/client/src/components/icons/CheckIcon.tsx @@ -0,0 +1,20 @@ +function CheckIcon() { + return ( + + + + ); +} + +export default CheckIcon; diff --git a/client/src/components/icons/ChevronDown.tsx b/client/src/components/icons/ChevronDown.tsx new file mode 100644 index 0000000..c975d27 --- /dev/null +++ b/client/src/components/icons/ChevronDown.tsx @@ -0,0 +1,21 @@ +function ChevronDown() { + return ( + + + + ); +} + +export default ChevronDown; diff --git a/client/src/hooks/useOutsideClick.ts b/client/src/hooks/useOutsideClick.ts new file mode 100644 index 0000000..e3fd8ab --- /dev/null +++ b/client/src/hooks/useOutsideClick.ts @@ -0,0 +1,23 @@ +import { useEffect, useRef } from "react"; + +function useOutsideClick(callback: () => void) { + const ref = useRef(null); + + useEffect(() => { + const handleClickOutside = (event: MouseEvent) => { + if (ref.current && !ref.current.contains(event.target as Node)) { + callback(); + } + }; + + document.addEventListener("mousedown", handleClickOutside); + + return () => { + document.removeEventListener("mousedown", handleClickOutside); + }; + }, [callback]); + + return ref; +} + +export default useOutsideClick; diff --git a/client/src/pages/DashboardPage.tsx b/client/src/pages/DashboardPage.tsx index d9af43c..843e796 100644 --- a/client/src/pages/DashboardPage.tsx +++ b/client/src/pages/DashboardPage.tsx @@ -1,6 +1,6 @@ /* eslint-disable react-hooks/exhaustive-deps */ /* eslint-disable @typescript-eslint/no-explicit-any */ -import { useEffect, useState } from "react"; +import { useEffect, useRef, useState } from "react"; import Card from "../components/Card"; import EmptyCard from "../components/EmptyCard"; import TabButton from "../components/TabButton"; @@ -48,6 +48,7 @@ function DashboardPage() { ); const [isLoadingScheduledSessions, setIsLoadingScheduledSessions] = useState(true); + const scheduledSessionsRef = useRef(null); // const [selectedDate, setSelectedDate] = useState( // format(new Date(), "d MMMM, yyyy", { locale: ru }) @@ -73,7 +74,6 @@ function DashboardPage() { try { const result: any = await api.get(`companies/${user.companyId}`).json(); - console.log("getCompany result: ", result); setCompany(result); } catch (error) { if (error instanceof Error) { @@ -92,7 +92,6 @@ function DashboardPage() { const result: any = await api .get(`companies/${user.companyId}/builds`) .json(); - console.log("getBuilds result: ", result); setBuilds(result); } catch (error) { if (error instanceof Error) { @@ -109,7 +108,6 @@ function DashboardPage() { try { const result: any = await api.get(`companies/${company.id}/users`).json(); - console.log("getManagers result: ", result); setManagers(result); } catch (error) { if (error instanceof Error) { @@ -118,20 +116,18 @@ function DashboardPage() { } } - async function getScheduledSessions() { + async function getScheduledSessions(useLoader?: boolean) { if (!company) { console.log("No ScheduledSessions"); return; } - console.log("selectedBuild", selectedBuild); - if (!selectedBuild || !selectedBuild.id) { console.log("No selectedBuild"); return; } - setIsLoadingScheduledSessions(true); + if (useLoader) setIsLoadingScheduledSessions(true); try { const result: any = await api @@ -141,16 +137,15 @@ function DashboardPage() { }/scheduled_sessions?date=${selectedDate.toISOString()}` ) .json(); - console.log("getScheduledSessions result: ", result); setScheduledSessions(result); - setIsLoadingScheduledSessions(false); } catch (error) { - setIsLoadingScheduledSessions(false); if (error instanceof Error) { console.log("Error: ", error.message); } } + + if (useLoader) setIsLoadingScheduledSessions(false); } function logout() { @@ -185,6 +180,36 @@ function DashboardPage() { setGeneratedScheduledSessions(arr); } + async function updateScheduledSessionManager( + scheduledSessionId: string, + managerId: string + ) { + if (!company || !scheduledSessions) return; + + try { + const result: any = await api + .put( + `companies/${company.id}/scheduled_sessions/${scheduledSessionId}`, + { + json: { userId: managerId }, + } + ) + .json(); + + console.log(scheduledSessions, result); + + setScheduledSessions( + scheduledSessions.map((scheduledSession) => + scheduledSession.id === result.id ? result : scheduledSession + ) + ); + } catch (error) { + if (error instanceof Error) { + console.log("Error: ", error.message); + } + } + } + useEffect(() => { getCompany(); @@ -210,18 +235,27 @@ function DashboardPage() { useEffect(() => { if (!managers || !selectedDate || !selectedBuild) return; - getScheduledSessions(); + getScheduledSessions(true); - console.log(selectedBuild); + const interval = setInterval(() => { + getScheduledSessions(); + }, 3000); + + return () => { + clearInterval(interval); + }; }, [managers, selectedDate, selectedBuild]); useEffect(() => { if (!scheduledSessions) return; generateScheduledSessions(); - - console.log(scheduledSessions); }, [scheduledSessions]); + useEffect(() => { + // setIsLoadingScheduledSessions(true); + scheduledSessionsRef.current?.scrollTo({ top: 0, behavior: "smooth" }); + }, [selectedDate, selectedBuild]); + return (
@@ -282,6 +316,7 @@ function DashboardPage() {
- {!isLoadingScheduledSessions && + {company && + selectedBuild && + user && + managers?.length && generatedScheduledSessions?.map( - (generatedScheduledSession, index) => ( -
- {generatedScheduledSession.map((scheduledSession, index2) => { - if (index2 === 0) { - return ( -
- {format(scheduledSession, "HH:mm")} -
- ); - } else { - if (Object.keys(scheduledSession).length) { - return ( - - manager.id === scheduledSession.userId - )} - /> - ); - } else { - return ; + (generatedScheduledSession, index) => { + return ( +
+ {generatedScheduledSession.map( + (scheduledSession, index2) => { + if (index2 === 0) { + return ( +
+ {format(scheduledSession, "HH:mm")} +
+ ); + } else { + if (Object.keys(scheduledSession).length) { + const selectedManager = managers.find( + (manager) => manager.id == scheduledSession.userId + ); + + return ( + + updateScheduledSessionManager( + scheduledSessionId, + managerId + ) + } + /> + ); + } else { + return ; + } + } } - } - })} -
- ) + )} +
+ ); + } )}
diff --git a/server/src/routes/app.ts b/server/src/routes/app.ts index 222cfc4..508977b 100644 --- a/server/src/routes/app.ts +++ b/server/src/routes/app.ts @@ -4,8 +4,7 @@ import Company from "../models/Company.js"; const appRouter = Router(); appRouter.post("/", async (_req, res) => { - const companies = await Company.find(); - console.log(companies); + await Company.find(); res.json({ route: "app" }); }); diff --git a/server/src/routes/companies.ts b/server/src/routes/companies.ts index 6870a0a..c82d197 100644 --- a/server/src/routes/companies.ts +++ b/server/src/routes/companies.ts @@ -1,6 +1,7 @@ import { Router } from "express"; import Company from "../models/Company.js"; import { parseISO, startOfDay, endOfDay } from "date-fns"; +import ScheduledSession from "../models/ScheduledSession.js"; const companiesRouter = Router(); @@ -82,4 +83,102 @@ companiesRouter.get( } ); +companiesRouter.put( + "/:id/scheduled_sessions/:scheduledSessionId", + async (req, res) => { + if (req.params.id != res.locals.user.companyId) { + res.json({ error: "Access denied!" }); + return; + } + + try { + const scheduledSession = await ScheduledSession.findById( + req.params.scheduledSessionId + ); + + const scheduledSessionAtSameTime = await ScheduledSession.find({ + startAt: scheduledSession?.startAt, + userId: req.body.userId, + }); + + if (scheduledSessionAtSameTime.length) { + res.json({ error: "Scheduled session at same time" }); + return; + } + + const updatedScheduledSession = await ScheduledSession.findOneAndUpdate( + { + _id: req.params.scheduledSessionId, + companyId: req.params.id, + }, + req.body, + { + new: true, + upsert: true, + } + ); + + res.json(updatedScheduledSession); + } catch (error) { + if (error instanceof Error) { + res.json({ error }); + } + } + } +); + +companiesRouter.get( + "/:id/builds/:buildId/scheduled_sessions/:scheduledSessionId/availableManagers", + async (req, res) => { + if (!req.query.startAt) { + res.json({ error: "No dateTime" }); + return; + } + + try { + const scheduledSession = await ScheduledSession.findById( + req.params.scheduledSessionId + ); + + const scheduledSessionsAtSameTime = await ScheduledSession.find({ + companyId: req.params.id, + buildId: req.params.buildId, + startAt: req.query.startAt, + }); + + const { users }: any = await Company.findById(req.params.id).populate( + "users" + ); + + let userIds = users.map((user: any) => user.id); + + console.log(scheduledSession?.userId); + + for (const userId of userIds) { + console.log(userId); + } + + let sessionsUserIds: any[] = []; + + for (const session of scheduledSessionsAtSameTime) { + if (session.userId) { + sessionsUserIds.push(session.userId.toString()); + } + } + + const filteredUserIds = userIds.filter( + (userId: any) => !sessionsUserIds.includes(userId) + ); + + filteredUserIds.push(scheduledSession?.userId); + + res.json(filteredUserIds); + } catch (error) { + if (error instanceof Error) { + res.json({ error }); + } + } + } +); + export default companiesRouter;