diff --git a/client/.env.development b/client/.env.development index e1eb4c9..79cdb72 100644 --- a/client/.env.development +++ b/client/.env.development @@ -1,2 +1,3 @@ VITE_API_URL=http://localhost:3001 +# VITE_API_URL=https://crm.stream.graff.tech/api VITE_STREAM_URL=https://stream.graff.tech diff --git a/client/public/images/companies/gsbk.png b/client/public/images/companies/gsbk.png new file mode 100644 index 0000000..009aa7c Binary files /dev/null and b/client/public/images/companies/gsbk.png differ diff --git a/client/src/components/Card.tsx b/client/src/components/Card.tsx index 35356f3..20ba6a6 100644 --- a/client/src/components/Card.tsx +++ b/client/src/components/Card.tsx @@ -43,7 +43,7 @@ function Card({ async function getAvailableManagers() { const result: any[] = await api .get( - `companies/${companyId}/builds/${buildId}/scheduled_sessions/${scheduledSessionId}/availableManagers?startAt=${scheduleSessionStartAt}` + `companies/${companyId}/builds/${buildId}/scheduledSessions/${scheduledSessionId}/availableManagers?startAt=${scheduleSessionStartAt}` ) .json(); diff --git a/client/src/components/Input.tsx b/client/src/components/Input.tsx index c8dd462..7089d50 100644 --- a/client/src/components/Input.tsx +++ b/client/src/components/Input.tsx @@ -9,6 +9,7 @@ interface InputProps { required?: boolean; readOnly?: boolean; className?: string; + autoComplete?: string; handleChange?: (value: string) => void; handleFocus?: () => void; } @@ -21,6 +22,7 @@ function Input({ required, readOnly, className, + autoComplete, handleChange, handleFocus, }: InputProps) { @@ -31,6 +33,7 @@ function Input({ return ( @@ -34,19 +37,23 @@ function Managers() {

{manager.name}

- {user?.role === "admin" && ( + {/* {user?.role === "admin" && ( )} diff --git a/client/src/components/ModalContainer.tsx b/client/src/components/ModalContainer.tsx index 3e3f004..5d5bce1 100644 --- a/client/src/components/ModalContainer.tsx +++ b/client/src/components/ModalContainer.tsx @@ -1,5 +1,7 @@ +/* eslint-disable react-hooks/exhaustive-deps */ import { Transition } from "react-transition-group"; import useModalStore from "../stores/useModalStore"; +import { useEffect } from "react"; function ModalContainer() { const [modal, setModal] = useModalStore((state) => [ @@ -7,6 +9,20 @@ function ModalContainer() { state.setModal, ]); + useEffect(() => { + function handleKeyUp(e: KeyboardEvent) { + if (e.key === "Escape") { + setModal(null); + } + } + + document.addEventListener("keyup", handleKeyUp); + + return () => { + document.removeEventListener("keyup", handleKeyUp); + }; + }, []); + return ( {(state) => (
setModal(null)} - className={`min-h-screen p-8 absolute z-20 top-0 left-0 w-full flex justify-center items-center bg-black bg-opacity-30 overflow-auto cursor-pointer transition-opacity ${state}`} + className={`min-h-screen p-8 absolute z-20 top-0 left-0 w-full flex justify-center items-center bg-black bg-opacity-30 overflow-auto transition-opacity ${state}`} > -
e.stopPropagation()} className="cursor-default"> - {modal} -
+ {modal}
)}
diff --git a/client/src/components/Schedule.tsx b/client/src/components/Schedule.tsx index 8a8e15f..99026e4 100644 --- a/client/src/components/Schedule.tsx +++ b/client/src/components/Schedule.tsx @@ -91,7 +91,7 @@ function Schedule({ selectedDay, slots, events }: Props) { try { const result: any = await api - .post(`scheduled_sessions`, { + .post(`scheduledSessions`, { json: { companyId: company!.id, buildId: selectedBuild?.id, diff --git a/client/src/components/modals/AddManagerModal.tsx b/client/src/components/modals/AddManagerModal.tsx index 36aa933..e19dc5d 100644 --- a/client/src/components/modals/AddManagerModal.tsx +++ b/client/src/components/modals/AddManagerModal.tsx @@ -62,7 +62,7 @@ function AddManagerModal() { return (
- Добавить сотрудника + Новый сотрудник
diff --git a/client/src/components/modals/CreateScheduledSessionModal.tsx b/client/src/components/modals/CreateScheduledSessionModal.tsx index fcd3586..f5b97de 100644 --- a/client/src/components/modals/CreateScheduledSessionModal.tsx +++ b/client/src/components/modals/CreateScheduledSessionModal.tsx @@ -29,7 +29,7 @@ function CreateScheduledSessionModal({ buildId, startAt, duration }: Props) { try { await api - .post(`scheduled_sessions`, { + .post(`scheduledSessions`, { json: { buildId, startAt, diff --git a/client/src/components/modals/CreateUserModal.tsx b/client/src/components/modals/CreateUserModal.tsx index 1410990..c48396e 100644 --- a/client/src/components/modals/CreateUserModal.tsx +++ b/client/src/components/modals/CreateUserModal.tsx @@ -94,7 +94,7 @@ function CreateUserModal({ companyId }: Props) { return (
-

Создание пользователя

+

Новый пользователь

(
} + icon={} onlyIcon onClick={() => companyId && diff --git a/client/src/pages/DashboardPage.tsx b/client/src/pages/DashboardPage.tsx index 4cb4ff6..909810e 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 { format, addDays, subDays } from "date-fns"; +import { format, addDays, subDays, startOfDay, endOfDay } from "date-fns"; import { useEffect, useRef, useState } from "react"; import api from "../utils/api"; import { ru } from "date-fns/locale"; @@ -109,7 +109,6 @@ function DashboardPage() { async function getScheduledSessions(useLoader?: boolean) { if (!company) { - // console.log("No ScheduledSessions"); return; } @@ -118,9 +117,9 @@ function DashboardPage() { try { const result: any = await api .get( - `companies/${ - company.id - }/scheduled_sessions?date=${selectedDay.toISOString()}` + `companies/${company.id}/scheduledSessions?startDate=${startOfDay( + selectedDay + ).toISOString()}&endDate=${endOfDay(selectedDay).toISOString()}` ) .json(); diff --git a/server/src/index.ts b/server/src/index.ts index 7a67275..ece924e 100644 --- a/server/src/index.ts +++ b/server/src/index.ts @@ -12,7 +12,6 @@ import usersRouter from "./routes/users.js"; import companiesRouter from "./routes/companies.js"; import buildsRouter from "./routes/builds.js"; import actionsRouter from "./routes/actions.js"; -import schedulesRouter from "./routes/schedules.js"; import scheduledSessionsRoute from "./routes/scheduledSessions.js"; import adminCompaniesRoute from "./routes/admin/adminCompaniesRoute.js"; import adminBuildsRoute from "./routes/admin/adminBuildsRoute.js"; @@ -46,8 +45,7 @@ app.use("/reset", resetRoute); app.use("/resetConfirm", resetConfirmRoute); app.use("/actions", actionsRouter); app.use("/builds", buildsRouter); -app.use("/scheduled_sessions", scheduledSessionsRoute); -app.use("/schedules", schedulesRouter); +app.use("/scheduledSessions", scheduledSessionsRoute); app.use("/admin/companies", adminCompaniesRoute); app.use("/admin/builds", adminBuildsRoute); app.use("/admin/users", adminUsersRoute); diff --git a/server/src/routes/companies.ts b/server/src/routes/companies.ts index 236e7d1..585902c 100644 --- a/server/src/routes/companies.ts +++ b/server/src/routes/companies.ts @@ -13,43 +13,47 @@ const router = Router(); // res.json(companies); // }); -router.get("/:id", async (req, res) => { - if (req.params.id != res.locals.user.companyId) { +router.get("/:companyId", async (req, res) => { + if (req.params.companyId != res.locals.user.companyId) { res.json({ error: "Access denied" }); return; } - const company = await Company.findById(req.params.id); + const company = await Company.findById(req.params.companyId); res.json(company); }); -router.get("/:id/builds", async (req, res) => { - if (req.params.id != res.locals.user.companyId) { +router.get("/:companyId/builds", async (req, res) => { + if (req.params.companyId != res.locals.user.companyId) { res.json({ error: "Access denied" }); return; } - const company: any = await Company.findById(req.params.id).populate("builds"); + const company: any = await Company.findById(req.params.companyId).populate( + "builds" + ); const { builds } = company; res.json(builds); }); -router.get("/:id/users", async (req, res) => { - if (req.params.id != res.locals.user.companyId) { +router.get("/:companyId/users", async (req, res) => { + if (req.params.companyId != res.locals.user.companyId) { res.json({ error: "Access denied" }); return; } - const company: any = await Company.findById(req.params.id).populate("users"); + const company: any = await Company.findById(req.params.companyId).populate( + "users" + ); const { users } = company; res.json(users); }); -router.get("/:id/builds/:buildId/users", async (req, res) => { - if (req.params.id != res.locals.user.companyId) { +router.get("/:companyId/builds/:buildId/users", async (req, res) => { + if (req.params.companyId != res.locals.user.companyId) { res.json({ error: "Access denied" }); return; } @@ -68,25 +72,25 @@ router.get("/:id/builds/:buildId/users", async (req, res) => { res.json(users); }); -router.get("/:id/scheduled_sessions", async (req, res) => { - if (req.params.id != res.locals.user.companyId) { +router.get("/:companyId/scheduledSessions", async (req, res) => { + if (req.params.companyId != res.locals.user.companyId) { res.json({ error: "Access denied" }); return; } - if (!req.query.date) { - res.json({ error: "Query parameter `date` is required" }); + const { startDate, endDate } = req.query; + + if (!startDate || !endDate) { + res.json({ error: "Query parameter `startDate`, `endDate` is required" }); return; } - const date = parseISO(req.query.date as string); - - const company: any = await Company.findById(req.params.id).populate({ + const company: any = await Company.findById(req.params.companyId).populate({ path: "scheduledSessions", match: { startAt: { - $gte: startOfDay(date), - $lte: endOfDay(date), + $gte: startDate, + $lt: endDate, }, }, }); @@ -96,85 +100,11 @@ router.get("/:id/scheduled_sessions", async (req, res) => { res.json(scheduledSessions); }); -// router.post( -// "/:id/builds/:buildId/scheduled_sessions", -// async (req, res) => { -// if (req.params.id != res.locals.user.companyId) { -// res.json({ error: "Access denied" }); -// return; -// } - -// if (!req.params.buildId) { -// res.json({ error: "req.params.buildId" }); -// return; -// } - -// const scheduledSession = await ScheduledSession.create({ -// companyId: req.params.id, -// buildId: req.params.buildId, -// ...req.body, -// }); - -// res.json(scheduledSession); -// } -// ); - -router.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.findOne({ - startAt: scheduledSession?.startAt, - userId: req.body.userId, - }); - - if (scheduledSessionAtSameTime && req.body.userId !== null) { - await ScheduledSession.updateMany( - { - userId: req.body.userId, - startAt: scheduledSession?.startAt, - }, - { - userId: null, - } - ); - - await ScheduledSession.findByIdAndUpdate(req.params.scheduledSessionId, { - userId: req.body.userId, - }); - res.json({ error: "Scheduled session at same time" }); - return; - } - - const updatedScheduledSession = await ScheduledSession.findByIdAndUpdate( - req.params.scheduledSessionId, - req.body, - { - new: true, - upsert: true, - } - ); - - res.json(updatedScheduledSession); - } catch (error) { - if (error instanceof Error) { - res.json({ error }); - } - } -}); - -router.get( - "/:id/builds/:buildId/scheduled_sessions/:scheduledSessionId/availableManagers", +router.put( + "/:companyId/scheduledSessions/:scheduledSessionId", async (req, res) => { - if (!req.query.startAt) { - res.json({ error: "Query parameter `startAt` is required" }); + if (req.params.companyId != res.locals.user.companyId) { + res.json({ error: "Access denied" }); return; } @@ -183,49 +113,42 @@ router.get( req.params.scheduledSessionId ); - if (!scheduledSession) { - res.json({ error: "Scheduled session not found" }); + const scheduledSessionAtSameTime = await ScheduledSession.findOne({ + startAt: scheduledSession?.startAt, + userId: req.body.userId, + }); + + if (scheduledSessionAtSameTime && req.body.userId !== null) { + await ScheduledSession.updateMany( + { + userId: req.body.userId, + startAt: scheduledSession?.startAt, + }, + { + userId: null, + } + ); + + await ScheduledSession.findByIdAndUpdate( + req.params.scheduledSessionId, + { + userId: req.body.userId, + } + ); + res.json({ error: "Scheduled session at same time" }); return; } - const buildUsers = await User.find({ buildIds: req.params.buildId }); - let buildUserIds = buildUsers.map((buildUser) => buildUser.toString()); - - const scheduledSessionsAtTime = await ScheduledSession.find({ - buildId: req.params.buildId, - startAt: req.query.startAt, - }); - - const scheduledSessionAtTimeUserIds: any[] = []; - - for (const scheduledSessionAtTime of scheduledSessionsAtTime) { - if (scheduledSessionAtTime.userId) { - scheduledSessionAtTimeUserIds.push( - scheduledSessionAtTime.userId.toString() - ); + const updatedScheduledSession = await ScheduledSession.findByIdAndUpdate( + req.params.scheduledSessionId, + req.body, + { + new: true, + upsert: true, } - } - - console.log( - "scheduledSessionAtTimeUserIds", - scheduledSessionAtTimeUserIds ); - console.log("buildUserIds", buildUserIds); - - const filteredUserIds: any[] = buildUserIds.filter( - (buildUserId) => !scheduledSessionAtTimeUserIds.includes(buildUserId) - ); - - console.log("filteredUserIds", filteredUserIds); - - if (scheduledSession.userId) { - filteredUserIds.push(scheduledSession.userId); - } - - console.log("filteredUserIds", filteredUserIds); - - res.json(filteredUserIds); + res.json(updatedScheduledSession); } catch (error) { if (error instanceof Error) { res.json({ error }); @@ -234,57 +157,6 @@ router.get( } ); -router.get("/:id/builds/:buildId/schedules", async (req, res) => { - if (req.params.id != res.locals.user.companyId) { - res.json({ error: "Access denied" }); - return; - } - - const schedules = await Schedule.find({ - companyId: req.params.id, - buildId: req.params.buildId, - }); - - res.json(schedules); -}); - -router.post("/:id/builds/:buildId/schedules", async (req, res) => { - if (req.params.id != res.locals.user.companyId) { - res.json({ error: "Access denied" }); - return; - } - - try { - const schedule = await Schedule.create({ - companyId: req.params.id, - buildId: req.params.buildId, - ...req.body, - }); - res.json(schedule); - } catch (error) { - if (error instanceof Error) { - res.json({ error: error.message }); - } - } -}); - -router.get( - "/:companyId/builds/:buildId/last_scheduled_session", - async (req, res) => { - const { buildId } = req.params; - - try { - const lastScheduledSession = await ScheduledSession.findOne({ - buildId, - }).sort({ startAt: -1 }); - - res.json(lastScheduledSession); - } catch (error) { - res.json({ error: (error as Error).message }); - } - } -); - router.get("/:companyId/users", async (req, res) => { try { const companyId = req.params.companyId; diff --git a/server/src/routes/schedules.ts b/server/src/routes/schedules.ts deleted file mode 100644 index 2b56676..0000000 --- a/server/src/routes/schedules.ts +++ /dev/null @@ -1,36 +0,0 @@ -import { Router } from "express"; -import Schedule from "../models/Schedule.js"; - -const schedulesRouter = Router(); - -schedulesRouter.get( - "/companies/:companyId/builds/:buildId", - async (req, res) => { - const { companyId, buildId } = req.params; - // const date = new Date(); - - const schedules = await Schedule.find({ - companyId, - buildId, - }); - - if (!schedules.length) { - res.json({ error: "No data" }); - return; - } - - if (req.query.date) { - const schedule = await Schedule.findOne({ - companyId, - buildId, - }); - - res.json(schedule); - return; - } - - res.json(schedules); - } -); - -export default schedulesRouter;