This commit is contained in:
2024-06-06 18:15:51 +05:00
parent e4331fb31f
commit 7e692fee4c
11 changed files with 116 additions and 173 deletions
+2 -2
View File
@@ -1,4 +1,4 @@
# VITE_API_URL=http://localhost:3001 # VITE_API_URL=http://localhost:3001
VITE_API_URL=http://192.168.1.171:3001 # VITE_API_URL=http://192.168.1.171:3001
# VITE_API_URL=https://crm.stream.graff.tech/api VITE_API_URL=https://crm.stream.graff.tech/api
VITE_STREAM_URL=https://stream.graff.tech VITE_STREAM_URL=https://stream.graff.tech
+33 -22
View File
@@ -60,28 +60,37 @@ function Card({
return ( return (
<div className="relative flex flex-col gap-2"> <div className="relative flex flex-col gap-2">
<div className="w-[264px] h-[128px] px-3 py-2 bg-white border-r border-b border-[#DAE0E5] flex flex-col gap-2"> <div className="w-[264px] h-[164px] px-3 py-2 bg-white border-r border-b border-[#DAE0E5] flex flex-col justify-between gap-2">
<div className="flex justify-between"> <div className="space-y-2">
<p className="text-sm font-semibold">{client.name}</p> <div className="flex justify-between">
{manager ? ( <div className="">
<div className="bg-[#E6F2FE] rounded-full px-2 h-[20px] flex items-center gap-1"> <p className="text-[10px] font-semibold text-[#77828C]">Клиент</p>
<div className="w-1 h-1 rounded-full bg-[#49A1F5]"></div> <p className="text-sm">{client.name}</p>
<p className="text-[10px] font-semibold text-[#49A1F5] pt-0.5">
Готов
</p>
</div> </div>
) : ( {manager ? (
<div className="bg-[#F2DADA] rounded-full px-2 h-[20px] flex items-center gap-1"> <div className="bg-[#E6F2FE] rounded-full px-2 h-[20px] flex items-center gap-1">
<div className="w-1 h-1 rounded-full bg-[#EB5757]"></div> <div className="w-1 h-1 rounded-full bg-[#49A1F5]"></div>
<p className="text-[10px] font-semibold text-[#EB5757] pt-0.5"> <p className="text-[10px] font-semibold text-[#49A1F5]">
Нет менеджера Готов
</p> </p>
</div> </div>
)} ) : (
</div> <div className="bg-[#F2DADA] rounded-full px-2 h-[20px] flex items-center gap-1">
<div className="border-b border-[#DAE0E5] pb-2"> <div className="w-1 h-1 rounded-full bg-[#EB5757]"></div>
<p className="text-xs text-[#77828C] leading-tight">{client.phone}</p> <p className="text-[10px] font-semibold text-[#EB5757] pt-0.5">
<p className="text-xs text-[#77828C] leading-tight">{client.email}</p> Нет менеджера
</p>
</div>
)}
</div>
<div className="border-b border-[#DAE0E5] pb-2">
<p className="text-xs text-[#77828C] leading-tight">
{client.phone}
</p>
<p className="text-xs text-[#77828C] leading-tight">
{client.email}
</p>
</div>
</div> </div>
<div className="flex justify-between items-center py-1"> <div className="flex justify-between items-center py-1">
<div className=" flex items-center gap-2"> <div className=" flex items-center gap-2">
@@ -105,7 +114,9 @@ function Card({
<div className="flex gap-2"> <div className="flex gap-2">
{manager && ( {manager && (
<Link <Link
to={`${import.meta.env.VITE_STREAM_URL}/scheduled/${scheduledSessionId}`} to={`${
import.meta.env.VITE_STREAM_URL
}/scheduled/${scheduledSessionId}?admin=true`}
target="_blank" target="_blank"
> >
<Button>Начать</Button> <Button>Начать</Button>
+1 -1
View File
@@ -51,7 +51,7 @@ function DatePicker({ defaultValue, startDate, onChange }: Props) {
function handleClick(date: Date) { function handleClick(date: Date) {
setValue(date); setValue(date);
onChange && onChange(value); onChange && onChange(date);
} }
return ( return (
+1 -1
View File
@@ -3,7 +3,7 @@ import PlusIcon from "./icons/PlusIcon";
function EmptyCard() { function EmptyCard() {
return ( return (
<div className="w-[264px] h-[128px] text-sm font-semibold bg-[#F0F1F2] border-r border-b border-[#DAE0E5] flex items-center justify-center group"> <div className="w-[264px] h-[164px] text-sm font-semibold bg-[#F0F1F2] border-r border-b border-[#DAE0E5] flex items-center justify-center group">
<Button <Button
color="tertiary" color="tertiary"
icon={<PlusIcon />} icon={<PlusIcon />}
@@ -8,36 +8,29 @@ import { useEffect, useState } from "react";
import Select from "../Select"; import Select from "../Select";
import useModalStore from "../../stores/useModalStore"; import useModalStore from "../../stores/useModalStore";
import Input from "../Input"; import Input from "../Input";
import { import { eachMinuteOfInterval, parse, parseISO, startOfDay } from "date-fns";
eachMinuteOfInterval,
isAfter,
isBefore,
parse,
parseISO,
startOfDay,
} from "date-fns";
import api from "../../utils/api"; import api from "../../utils/api";
import ChoiceChips from "../ChoiceChips"; import ChoiceChips from "../ChoiceChips";
import ISchedule from "../../types/ISchedule"; // import ISchedule from "../../types/ISchedule";
import DatePicker from "../DatePicker"; import DatePicker from "../DatePicker";
import IScheduledSession from "../../types/IScheduledSession"; import IScheduledSession from "../../types/IScheduledSession";
interface Props { interface Props {
companyId: string; companyId: string;
buildId: string; buildId: string;
schedules: ISchedule[]; // schedules: ISchedule[];
handleCreate: () => void; handleCreate: () => void;
} }
function CreateScheduleModal({ function CreateScheduleModal({
companyId, companyId,
buildId, buildId,
schedules, // schedules,
handleCreate, handleCreate,
}: Props) { }: Props) {
const setModal = useModalStore((state) => state.setModal); const setModal = useModalStore((state) => state.setModal);
const [date, setDate] = useState<Date>(new Date()); const [date, setDate] = useState<Date>(startOfDay(new Date()));
const [startDate, setStartDate] = useState<Date>(); const [startDate, setStartDate] = useState<Date>(startOfDay(new Date()));
const [sessionDuration, setSessionDuration] = useState<number>(30); const [sessionDuration, setSessionDuration] = useState<number>(30);
const [sessionBreak, setSessionBreak] = useState<number>(5); const [sessionBreak, setSessionBreak] = useState<number>(5);
const [startTime, setStartTime] = useState<string>("10:00"); const [startTime, setStartTime] = useState<string>("10:00");
@@ -47,17 +40,15 @@ function CreateScheduleModal({
async function getLastScheduledSessionDate() { async function getLastScheduledSessionDate() {
try { try {
const { startAt }: IScheduledSession = await api const result: IScheduledSession | null = await api
.get(`companies/${companyId}/builds/${buildId}/last_scheduled_session`) .get(`companies/${companyId}/builds/${buildId}/last_scheduled_session`)
.json(); .json();
if (!startAt) return; if (!result || !result.startAt) return;
setStartDate(startOfDay(parseISO(startAt))); setStartDate(startOfDay(parseISO(result.startAt)));
} catch (error) { } catch (error) {
if (error instanceof Error) { alert((error as Error).message);
alert(error.message);
}
} }
} }
@@ -66,7 +57,7 @@ function CreateScheduleModal({
}, []); }, []);
function calculateSessionsPerDay() { function calculateSessionsPerDay() {
if (!startDate) return; // if (!startDate) return;
const sessionsPerDay = eachMinuteOfInterval( const sessionsPerDay = eachMinuteOfInterval(
{ {
@@ -88,7 +79,7 @@ function CreateScheduleModal({
async function createSchedule() { async function createSchedule() {
await api.post(`companies/${companyId}/builds/${buildId}/schedules`, { await api.post(`companies/${companyId}/builds/${buildId}/schedules`, {
json: { json: {
startDate, startDate: date,
startTime, startTime,
endTime, endTime,
weekends, weekends,
@@ -100,7 +91,7 @@ function CreateScheduleModal({
} }
async function handleClickCreateSchedule() { async function handleClickCreateSchedule() {
if (!startDate) return; // if (!startDate) return;
await createSchedule(); await createSchedule();
handleCreate(); handleCreate();
@@ -141,8 +132,8 @@ function CreateScheduleModal({
<div className="flex flex-col"> <div className="flex flex-col">
<Label value="Начало" /> <Label value="Начало" />
<DatePicker <DatePicker
startDate={startDate || new Date()} startDate={startDate}
onChange={(date) => setDate(date)} onChange={(date) => (setDate(date), console.log(date))}
/> />
</div> </div>
</div> </div>
+2 -6
View File
@@ -422,11 +422,7 @@ function DashboardPage() {
scheduleSessionStartAt={ scheduleSessionStartAt={
scheduledSession.startAt scheduledSession.startAt
} }
client={{ client={scheduledSession.client}
name: scheduledSession.clientName,
phone: scheduledSession.clientPhone,
email: scheduledSession.clientEmail,
}}
manager={selectedManager} manager={selectedManager}
managers={selectedBuildManagers || []} managers={selectedBuildManagers || []}
handleSelect={(scheduledSessionId, managerId) => handleSelect={(scheduledSessionId, managerId) =>
@@ -523,7 +519,7 @@ function DashboardPage() {
<CreateScheduleModal <CreateScheduleModal
companyId={company?.id} companyId={company?.id}
buildId={selectedBuild?.id} buildId={selectedBuild?.id}
schedules={schedules} // schedules={schedules}
handleCreate={getSchedules} handleCreate={getSchedules}
/> />
) )
+3 -3
View File
@@ -21,15 +21,15 @@ connectDB();
app.use(json()); app.use(json());
app.use(cors({ origin: "*" })); app.use(cors({ origin: "*" }));
app.use("/app", authMiddleware, appRouter);
app.use("/companies", authMiddleware, companiesRouter);
app.use("/users", authMiddleware, usersRouter);
app.use("/login", loginRouter); app.use("/login", loginRouter);
app.use("/registration", registrationRouter); app.use("/registration", registrationRouter);
app.use("/actions", actionsRouter); app.use("/actions", actionsRouter);
app.use("/builds", buildsRouter); app.use("/builds", buildsRouter);
app.use("/scheduled_sessions", scheduledSessionsRouter); app.use("/scheduled_sessions", scheduledSessionsRouter);
app.use("/schedules", schedulesRouter); app.use("/schedules", schedulesRouter);
app.use("/app", authMiddleware, appRouter);
app.use("/companies", authMiddleware, companiesRouter);
app.use("/users", authMiddleware, usersRouter);
app.listen(port, () => { app.listen(port, () => {
console.log(`Server listening on port ${port}`); console.log(`Server listening on port ${port}`);
-3
View File
@@ -258,9 +258,6 @@ router.get(
async (req, res) => { async (req, res) => {
const { companyId, buildId } = req.params; const { companyId, buildId } = req.params;
console.log("companyId", companyId);
console.log("buildId", buildId);
try { try {
const lastScheduledSession = await ScheduledSession.findOne({ const lastScheduledSession = await ScheduledSession.findOne({
companyId, companyId,
+33 -88
View File
@@ -5,13 +5,11 @@ import Schedule from "../models/Schedule";
import { import {
addMinutes, addMinutes,
areIntervalsOverlapping, areIntervalsOverlapping,
differenceInMinutes,
endOfDay, endOfDay,
isValid, isValid,
parseISO, parseISO,
startOfDay, startOfDay,
} from "date-fns"; } from "date-fns";
import { isValidObjectId } from "mongoose";
const scheduledSessionsRouter = Router(); const scheduledSessionsRouter = Router();
@@ -26,50 +24,33 @@ scheduledSessionsRouter.get("/", async (_req, res) => {
scheduledSessionsRouter.get("/:id", async (req, res) => { scheduledSessionsRouter.get("/:id", async (req, res) => {
const scheduledSessionId = req.params.id; const scheduledSessionId = req.params.id;
const scheduledSession = await ScheduledSession.findById(scheduledSessionId);
if (!isValidObjectId(scheduledSessionId)) {
return res.json({
status: "error",
message: "Invalid session ID value",
});
}
const scheduledSession = await ScheduledSession.findById(req.params.id);
res.json(scheduledSession); res.json(scheduledSession);
}); });
scheduledSessionsRouter.get("/builds/:buildId", async (req, res) => { scheduledSessionsRouter.get(
if (!req.params.buildId) { "/companies/:companyId/builds/:buildId",
res.json({ error: "Parameter `buildId` is required" }); async (req, res) => {
return; const { companyId, buildId } = req.params;
const { date } = req.query;
try {
const scheduledSessions = await ScheduledSession.find({
companyId,
buildId,
startAt: {
$gte: startOfDay(parseISO(date as string)),
$lt: endOfDay(parseISO(date as string)),
},
});
res.json(scheduledSessions);
} catch (error) {
res.json({ error: (error as Error).message });
}
} }
);
if (!req.query.date) {
res.json({ error: "Query parameter `date` is required" });
return;
}
const buildId = req.params.buildId;
const date = req.query.date as string;
if (!isValidObjectId(buildId)) {
return res.json({
status: "error",
message: "Invalid build ID value",
});
}
const scheduledSessions = await ScheduledSession.find({
buildId,
startAt: {
$gte: startOfDay(parseISO(date)),
$lt: endOfDay(parseISO(date)),
},
});
res.json(scheduledSessions);
});
scheduledSessionsRouter.get("/:buildId", async (req, res) => { scheduledSessionsRouter.get("/:buildId", async (req, res) => {
if (!req.params.buildId) { if (!req.params.buildId) {
@@ -98,13 +79,6 @@ scheduledSessionsRouter.get("/:buildId", async (req, res) => {
scheduledSessionsRouter.post("/", async (req, res) => { scheduledSessionsRouter.post("/", async (req, res) => {
const { buildId, startAt, client, duration } = req.body; const { buildId, startAt, client, duration } = req.body;
if (!isValidObjectId(buildId)) {
return res.json({
status: "error",
message: "Invalid build ID value",
});
}
if (!buildId || !startAt) { if (!buildId || !startAt) {
return res.json({ return res.json({
status: "error", status: "error",
@@ -183,7 +157,7 @@ scheduledSessionsRouter.post("/", async (req, res) => {
const schedule = await Schedule.findOne({ const schedule = await Schedule.findOne({
buildId, buildId,
startDate: { $lte: startAtISO }, startDate: { $lte: startAtISO },
endDate: { $gte: startAtISO }, // endDate: { $gte: startAtISO },
}); });
if (!schedule) { if (!schedule) {
@@ -246,16 +220,13 @@ scheduledSessionsRouter.post("/", async (req, res) => {
scheduledSessionsRouter.put("/:id", async (req, res) => { scheduledSessionsRouter.put("/:id", async (req, res) => {
const scheduledSessionId = req.params.id; const scheduledSessionId = req.params.id;
if (!isValidObjectId(scheduledSessionId)) { let {
return res.json({ startAt,
status: "error", endAt,
message: "Invalid session ID value", activeSessionId,
}); }: { startAt: string; endAt: string; activeSessionId: string } = req.body;
}
let { startAt, duration }: { startAt: string; duration: number } = req.body; if (!startAt && !endAt) {
if (!startAt && !duration) {
return res.json({ return res.json({
status: "error", status: "error",
message: "Parameter `startAt` or `duration` is required, or both", message: "Parameter `startAt` or `duration` is required, or both",
@@ -269,16 +240,9 @@ scheduledSessionsRouter.put("/:id", async (req, res) => {
}); });
} }
function isInteger(num: number) { // function isInteger(num: number) {
return (num ^ 0) === num; // return (num ^ 0) === num;
} // }
if (duration && !isInteger(duration)) {
return res.json({
status: "error",
message: "Parameter `duration` is not an integer",
});
}
const scheduledSession = await ScheduledSession.findById(scheduledSessionId); const scheduledSession = await ScheduledSession.findById(scheduledSessionId);
@@ -289,22 +253,10 @@ scheduledSessionsRouter.put("/:id", async (req, res) => {
}); });
} }
if (!duration) {
duration = differenceInMinutes(
scheduledSession.endAt,
scheduledSession.startAt
);
}
const endAt = addMinutes(
(startAt && parseISO(startAt.toString())) || scheduledSession.startAt,
duration
);
try { try {
const scheduledSession = await ScheduledSession.findByIdAndUpdate( const scheduledSession = await ScheduledSession.findByIdAndUpdate(
scheduledSessionId, scheduledSessionId,
{ startAt, endAt }, { startAt, endAt, activeSessionId },
{ new: true } { new: true }
); );
@@ -319,13 +271,6 @@ scheduledSessionsRouter.put("/:id", async (req, res) => {
scheduledSessionsRouter.delete("/:id", async (req, res) => { scheduledSessionsRouter.delete("/:id", async (req, res) => {
const scheduledSessionId = req.params.id; const scheduledSessionId = req.params.id;
if (!isValidObjectId(scheduledSessionId)) {
return res.json({
status: "error",
message: "Invalid session ID value",
});
}
try { try {
await ScheduledSession.findByIdAndDelete(scheduledSessionId); await ScheduledSession.findByIdAndDelete(scheduledSessionId);
+25 -22
View File
@@ -3,33 +3,36 @@ import Schedule from "../models/Schedule";
const schedulesRouter = Router(); const schedulesRouter = Router();
schedulesRouter.get("/builds/:buildId", async (req, res) => { schedulesRouter.get(
const buildId = req.params.buildId; "/companies/:companyId/builds/:buildId",
const date = new Date(); async (req, res) => {
const { companyId, buildId } = req.params;
const date = new Date();
const schedules = await Schedule.find({ const schedules = await Schedule.find({
buildId, companyId,
startDate: { $lte: date },
endDate: { $gte: date },
});
if (!schedules.length) {
res.json({ error: "No data" });
return;
}
if (req.query.date) {
const schedule = await Schedule.findOne({
buildId, buildId,
startDate: { $lte: date }, startDate: { $lte: date },
endDate: { $gte: date },
}); });
res.json(schedule); if (!schedules.length) {
return; res.json({ error: "No data" });
} return;
}
res.json(schedules); if (req.query.date) {
}); const schedule = await Schedule.findOne({
companyId,
buildId,
startDate: { $lte: date },
});
res.json(schedule);
return;
}
res.json(schedules);
}
);
export default schedulesRouter; export default schedulesRouter;
+1 -1
View File
@@ -10,7 +10,7 @@
"noUnusedLocals": true, "noUnusedLocals": true,
"noUnusedParameters": true, "noUnusedParameters": true,
"noFallthroughCasesInSwitch": true, "noFallthroughCasesInSwitch": true,
"skipLibCheck": true "skipLibCheck": true,
}, },
"ts-node": { "ts-node": {
"transpileOnly": true "transpileOnly": true