diff --git a/client/.env b/client/.env index 0af9df1..5cf0d17 100644 --- a/client/.env +++ b/client/.env @@ -1 +1,3 @@ -VITE_SERVER_URL=http://192.168.1.170:3001 \ No newline at end of file +# VITE_SERVER_URL=http://localhost:3001 +VITE_SERVER_URL=http://192.168.1.170:3001 +# VITE_SERVER_URL=https://crm.stream.graff.tech/api \ No newline at end of file diff --git a/client/src/components/Card.tsx b/client/src/components/Card.tsx index de06748..f9e43ea 100644 --- a/client/src/components/Card.tsx +++ b/client/src/components/Card.tsx @@ -4,6 +4,8 @@ import { useEffect, useState } from "react"; import SelectUser from "./SelectUser"; import MoreIcon from "./icons/MoreIcon"; import api from "../utils/api"; +import Button from "./Button"; +import { Link } from "react-router-dom"; interface CardProps { companyId: string; @@ -99,12 +101,20 @@ function Card({ {manager ? manager.name : "Не назначен"}

- + +
+ {manager && ( + + + + )} + +
diff --git a/server/package.json b/server/package.json index a166914..c3c73da 100644 --- a/server/package.json +++ b/server/package.json @@ -15,7 +15,8 @@ "dotenv": "^16.3.1", "express": "^4.18.2", "jsonwebtoken": "^9.0.2", - "mongoose": "^7.5.1" + "mongoose": "^7.5.1", + "uuid": "^9.0.1" }, "devDependencies": { "@types/bcrypt": "^5.0.0", @@ -23,6 +24,7 @@ "@types/express": "^4.17.17", "@types/jsonwebtoken": "^9.0.2", "@types/node": "^20.5.9", + "@types/uuid": "^9.0.6", "nodemon": "^3.0.1", "ts-node": "^10.9.1", "typescript": "^5.2.2" diff --git a/server/src/index.ts b/server/src/index.ts index 595fad5..528b2d8 100644 --- a/server/src/index.ts +++ b/server/src/index.ts @@ -24,12 +24,12 @@ app.use(cors()); app.use("/login", loginRouter); app.use("/registration", registrationRouter); app.use("/actions", actionsRouter); +app.use("/builds", buildsRouter); app.use("/scheduled_sessions", scheduledSessionsRouter); app.use("/schedules", schedulesRouter); app.use("/app", authMiddleware, appRouter); app.use("/companies", authMiddleware, companiesRouter); app.use("/users", authMiddleware, usersRouter); -app.use("/builds", authMiddleware, buildsRouter); app.listen(port, () => { console.log(`Server listening on port ${port}`); diff --git a/server/src/models/ScheduledSession.ts b/server/src/models/ScheduledSession.ts index 52634b4..c7c8b6d 100644 --- a/server/src/models/ScheduledSession.ts +++ b/server/src/models/ScheduledSession.ts @@ -5,7 +5,7 @@ const scheduledSessionSchema = new Schema( companyId: { type: Schema.Types.ObjectId, ref: "Company", - required: true, + // required: true, }, buildId: { type: Schema.Types.ObjectId, @@ -36,6 +36,12 @@ const scheduledSessionSchema = new Schema( type: String, required: true, }, + adminInviteKey: { + type: String, + }, + userInviteKey: { + type: String, + }, }, { timestamps: true, diff --git a/server/src/routes/builds.ts b/server/src/routes/builds.ts index 457bfbe..cfa0631 100644 --- a/server/src/routes/builds.ts +++ b/server/src/routes/builds.ts @@ -3,16 +3,10 @@ import Build from "../models/Build.js"; const buildsRouter = Router(); -buildsRouter.get("/", async (_req, res) => { - await Build.find(); +buildsRouter.get("/:id", async (req, res) => { + const build = await Build.findById(req.params.id); - res.json({ ok: 1 }); + res.json(build); }); -// buildsRouter.post("/", async (req, res) => { -// const build = await Build.create(req.body); - -// res.json(build); -// }); - export default buildsRouter; diff --git a/server/src/routes/companies.ts b/server/src/routes/companies.ts index ee3a450..2097120 100644 --- a/server/src/routes/companies.ts +++ b/server/src/routes/companies.ts @@ -16,7 +16,7 @@ const companiesRouter = Router(); companiesRouter.get("/:id", async (req, res) => { if (req.params.id != res.locals.user.companyId) { - res.json({ error: "Access denied!" }); + res.json({ error: "Access denied" }); return; } @@ -27,7 +27,7 @@ companiesRouter.get("/:id", async (req, res) => { companiesRouter.get("/:id/builds", async (req, res) => { if (req.params.id != res.locals.user.companyId) { - res.json({ error: "Access denied!" }); + res.json({ error: "Access denied" }); return; } @@ -39,7 +39,7 @@ companiesRouter.get("/:id/builds", async (req, res) => { companiesRouter.get("/:id/users", async (req, res) => { if (req.params.id != res.locals.user.companyId) { - res.json({ error: "Access denied!" }); + res.json({ error: "Access denied" }); return; } @@ -51,7 +51,7 @@ companiesRouter.get("/:id/users", async (req, res) => { companiesRouter.get("/:id/builds/:buildId/users", async (req, res) => { if (req.params.id != res.locals.user.companyId) { - res.json({ error: "Access denied!" }); + res.json({ error: "Access denied" }); return; } @@ -73,12 +73,12 @@ companiesRouter.get( "/:id/builds/:buildId/scheduled_sessions", async (req, res) => { if (req.params.id != res.locals.user.companyId) { - res.json({ error: "Access denied!" }); + res.json({ error: "Access denied" }); return; } if (!req.query.date) { - res.json({ error: "req.query.date" }); + res.json({ error: "Query parameter `date` is required" }); return; } @@ -106,34 +106,34 @@ companiesRouter.get( } ); -companiesRouter.post( - "/:id/builds/:buildId/scheduled_sessions", - async (req, res) => { - if (req.params.id != res.locals.user.companyId) { - res.json({ error: "Access denied!" }); - return; - } +// companiesRouter.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; - } +// 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, - }); +// const scheduledSession = await ScheduledSession.create({ +// companyId: req.params.id, +// buildId: req.params.buildId, +// ...req.body, +// }); - res.json(scheduledSession); - } -); +// res.json(scheduledSession); +// } +// ); companiesRouter.put( "/:id/scheduled_sessions/:scheduledSessionId", async (req, res) => { if (req.params.id != res.locals.user.companyId) { - res.json({ error: "Access denied!" }); + res.json({ error: "Access denied" }); return; } @@ -142,21 +142,18 @@ companiesRouter.put( req.params.scheduledSessionId ); - const scheduledSessionAtSameTime = await ScheduledSession.find({ + const scheduledSessionAtSameTime = await ScheduledSession.findOne({ startAt: scheduledSession?.startAt, userId: req.body.userId, }); - if (scheduledSessionAtSameTime.length) { + if (scheduledSessionAtSameTime) { res.json({ error: "Scheduled session at same time" }); return; } - const updatedScheduledSession = await ScheduledSession.findOneAndUpdate( - { - _id: req.params.scheduledSessionId, - companyId: req.params.id, - }, + const updatedScheduledSession = await ScheduledSession.findByIdAndUpdate( + req.params.scheduledSessionId, req.body, { new: true, @@ -177,14 +174,11 @@ companiesRouter.get( "/:id/builds/:buildId/scheduled_sessions/:scheduledSessionId/availableManagers", async (req, res) => { if (!req.query.startAt) { - res.json({ error: "No param `startAt`" }); + res.json({ error: "Query parameter `startAt` is required" }); return; } try { - // const date = req.query.startAt; - // const sessions = ScheduledSession.find({ userId: res.locals.user.id }); - const scheduledSession = await ScheduledSession.findById( req.params.scheduledSessionId ); @@ -229,7 +223,7 @@ companiesRouter.get( companiesRouter.get("/:id/builds/:buildId/schedules", async (req, res) => { if (req.params.id != res.locals.user.companyId) { - res.json({ error: "Access denied!" }); + res.json({ error: "Access denied" }); return; } @@ -243,7 +237,7 @@ companiesRouter.get("/:id/builds/:buildId/schedules", async (req, res) => { companiesRouter.post("/:id/builds/:buildId/schedules", async (req, res) => { if (req.params.id != res.locals.user.companyId) { - res.json({ error: "Access denied!" }); + res.json({ error: "Access denied" }); return; } diff --git a/server/src/routes/scheduledSessions.ts b/server/src/routes/scheduledSessions.ts index 2f9bcda..c675d9c 100644 --- a/server/src/routes/scheduledSessions.ts +++ b/server/src/routes/scheduledSessions.ts @@ -3,17 +3,33 @@ import ScheduledSession from "../models/ScheduledSession.js"; import Build from "../models/Build.js"; import Schedule from "../models/Schedule.js"; import { addMinutes, endOfDay, parseISO, startOfDay } from "date-fns"; +import { v4 as uuidv4 } from "uuid"; const scheduledSessionsRouter = Router(); +scheduledSessionsRouter.get("/:id", async (req, res) => { + if (!req.query.inviteKeyType || !req.query.inviteKeyValue) { + res.json({ + error: + "Query parameters `inviteKeyType` and `inviteKeyValue` is required", + }); + return; + } + + const scheduledSessionId = req.params.id; + const scheduledSession = await ScheduledSession.findById(scheduledSessionId); + + res.json(scheduledSession); +}); + scheduledSessionsRouter.get("/builds/:buildId", async (req, res) => { if (!req.params.buildId) { - res.json({ error: "Parameter `buildId` is required!" }); + res.json({ error: "Parameter `buildId` is required" }); return; } if (!req.query.date) { - res.json({ error: "Query parameter `date` is required!" }); + res.json({ error: "Query parameter `date` is required" }); return; } @@ -32,12 +48,12 @@ scheduledSessionsRouter.get("/builds/:buildId", async (req, res) => { scheduledSessionsRouter.get("/:buildId", async (req, res) => { if (!req.params.buildId) { - res.json({ error: "Parameter `buildId` is required!" }); + res.json({ error: "Parameter `buildId` is required" }); return; } if (!req.query.date) { - res.json({ error: "Query parameter `date` is required!" }); + res.json({ error: "Query parameter `date` is required" }); return; } @@ -55,44 +71,43 @@ scheduledSessionsRouter.get("/:buildId", async (req, res) => { }); scheduledSessionsRouter.post("/", async (req, res) => { - if (!req.body.companyId) { - res.json({ error: "Parameter `companyId` is required!" }); - return; - } + // if (!req.body.companyId) { + // res.json({ error: "Parameter `companyId` is required" }); + // return; + // } if (!req.body.buildId) { - res.json({ error: "Parameter `buildId` is required!" }); + res.json({ error: "Parameter `buildId` is required" }); return; } if (!req.body.startAt) { - res.json({ error: "Parameter `startAt` is required!" }); + res.json({ error: "Parameter `startAt` is required" }); return; } if (!req.body.client) { - res.json({ error: "Parameter `client` is required!" }); + res.json({ error: "Parameter `client` is required" }); return; } - const { companyId, buildId, startAt, client } = req.body; + const { buildId, startAt, client } = req.body; const build = await Build.findById(buildId); - const schedule = await Schedule.findOne({ companyId, buildId }); + const schedule = await Schedule.findOne({ buildId }); const scheduledSessions = await ScheduledSession.find({ - companyId, buildId, startAt, }); if (!build) { - res.json({ error: "`Build` not found in the database!" }); + res.json({ error: "`Build` not found in the database" }); return; } if (scheduledSessions.length === build.sessionLimit) { res.json({ - error: "No slots available!", + error: "No slots available", scheduledSessionsLength: scheduledSessions.length, buildSessionLimit: build.sessionLimit, }); @@ -100,20 +115,21 @@ scheduledSessionsRouter.post("/", async (req, res) => { } if (!schedule) { - res.json({ error: "`Schedule` not found in the database!" }); + res.json({ error: "`Schedule` not found in the database" }); return; } const endAt = addMinutes(parseISO(startAt), schedule.sessionDuration); const scheduleSession = await ScheduledSession.create({ - companyId, buildId, startAt, endAt, clientName: client.name, clientEmail: client.email, clientPhone: client.phone, + adminInviteKey: uuidv4(), + userInviteKey: uuidv4(), }); res.json({ ok: 1, scheduleSession }); diff --git a/server/yarn.lock b/server/yarn.lock index 61bb92c..35646b2 100644 --- a/server/yarn.lock +++ b/server/yarn.lock @@ -179,6 +179,11 @@ "@types/mime" "*" "@types/node" "*" +"@types/uuid@^9.0.6": + version "9.0.6" + resolved "https://registry.yarnpkg.com/@types/uuid/-/uuid-9.0.6.tgz#c91ae743d8344a54b2b0c691195f5ff5265f6dfb" + integrity sha512-BT2Krtx4xaO6iwzwMFUYvWBWkV2pr37zD68Vmp1CDV196MzczBRxuEpD6Pr395HAgebC/co7hOphs53r8V7jew== + "@types/webidl-conversions@*": version "7.0.0" resolved "https://registry.npmjs.org/@types/webidl-conversions/-/webidl-conversions-7.0.0.tgz" @@ -1366,6 +1371,11 @@ utils-merge@1.0.1: resolved "https://registry.npmjs.org/utils-merge/-/utils-merge-1.0.1.tgz" integrity sha512-pMZTvIkT1d+TFGvDOqodOclx0QWkkgi6Tdoa8gC8ffGAAqz9pzPTZWAybbsHHoED/ztMtkv/VoYTYyShUn81hA== +uuid@^9.0.1: + version "9.0.1" + resolved "https://registry.yarnpkg.com/uuid/-/uuid-9.0.1.tgz#e188d4c8853cc722220392c424cd637f32293f30" + integrity sha512-b+1eJOlsR9K8HJpow9Ok3fiWOWSIcIzXodvv0rQjVoOVNpWMpxf1wZNpt4y9h10odCNrqnYp1OBzRktckBe3sA== + v8-compile-cache-lib@^3.0.1: version "3.0.1" resolved "https://registry.npmjs.org/v8-compile-cache-lib/-/v8-compile-cache-lib-3.0.1.tgz"