From a46547d1fe07e81caf3b756ac47ddfa1977deec7 Mon Sep 17 00:00:00 2001 From: inmake Date: Wed, 12 Jun 2024 20:06:30 +0500 Subject: [PATCH] upd --- client/src/components/Calendar.tsx | 161 ++++--- client/src/components/Card.tsx | 106 +++-- client/src/components/Input.tsx | 2 +- client/src/components/Managers.tsx | 67 +++ client/src/components/Menu.tsx | 33 +- client/src/components/Schedules.tsx | 84 ++++ client/src/components/SelectUser.tsx | 33 +- client/src/components/icons/EntryIcon.tsx | 21 + .../components/modals/CreateScheduleModal.tsx | 61 ++- client/src/pages/DashboardPage.tsx | 439 +++++------------- client/src/stores/useAuthStore.ts | 7 +- client/src/stores/useSettingsStore.ts | 29 ++ client/src/stores/useStore.ts | 37 ++ client/src/types/IBuild.ts | 9 + client/src/types/ICompany.ts | 6 + client/src/types/IUser.ts | 10 + server/src/models/Roles.ts | 23 + server/src/routes/companies.ts | 15 +- server/src/routes/users.ts | 4 +- 19 files changed, 634 insertions(+), 513 deletions(-) create mode 100644 client/src/components/Managers.tsx create mode 100644 client/src/components/Schedules.tsx create mode 100644 client/src/components/icons/EntryIcon.tsx create mode 100644 client/src/stores/useSettingsStore.ts create mode 100644 client/src/stores/useStore.ts create mode 100644 client/src/types/IBuild.ts create mode 100644 client/src/types/ICompany.ts create mode 100644 client/src/types/IUser.ts create mode 100644 server/src/models/Roles.ts diff --git a/client/src/components/Calendar.tsx b/client/src/components/Calendar.tsx index 9c50422..7aef853 100644 --- a/client/src/components/Calendar.tsx +++ b/client/src/components/Calendar.tsx @@ -3,109 +3,106 @@ import { format, getDaysInMonth, getISODay, - isEqual, + isSameDay, isToday, setDate, - startOfDay, - startOfMonth, subMonths, } from "date-fns"; -import { useEffect, useState } from "react"; +import { useState } from "react"; import ChevronLeftIcon from "./icons/ChevronLeftIcon"; import ChevronRightIcon from "./icons/ChevronRightIcon"; import _ from "lodash"; import { ru } from "date-fns/locale"; +import Button from "./Button"; +import ChevronDownIcon from "./icons/ChevronDownIcon"; +import ChevronUpIcon from "./icons/ChevronUpIcon"; +import useSettingsStore from "../stores/useSettingsStore"; +import useStore from "../stores/useStore"; -interface Props { - defaultValue?: Date; - onChange?: (date: Date) => void; -} - -function Calendar({ defaultValue, onChange }: Props) { - const [selectedDate, setSelectedDate] = useState( - startOfDay(defaultValue || new Date()) - ); - const [selectedMonth, setSelectedMonth] = useState( - startOfMonth(defaultValue || new Date()) - ); +function Calendar() { + const { selectedDay, setSelectedDay } = useStore(); + const { isShowCalendar, setIsShowCalendar } = useSettingsStore(); + const [currentMonth, setCurrentMonth] = useState(new Date()); function selectPrevMonth() { - setSelectedMonth(subMonths(selectedMonth, 1)); + setCurrentMonth(subMonths(currentMonth, 1)); } function selectNextMonth() { - setSelectedMonth(addMonths(selectedMonth, 1)); + setCurrentMonth(addMonths(currentMonth, 1)); } - function hadndleClick(date: Date) { - setSelectedDate(date); - onChange && onChange(date); - } - - useEffect(() => { - if (!defaultValue) return; - setSelectedDate(startOfDay(defaultValue)); - }, [defaultValue]); - - useEffect(() => { - setSelectedMonth(startOfMonth(selectedDate)); - }, [selectedDate]); - return ( -
-
- -

- {_.capitalize(format(selectedMonth, "LLLL, yyyy", { locale: ru }))} -

- +
+
+

Календарь

+
-
- {["Пн", "Вт", "Ср", "Чт", "Пт", "Сб", "Вс"].map((value, index) => ( -
- {value} -
- ))} -
-
- {Array.from({ length: getISODay(selectedMonth) - 1 }).map( - (_, index) => ( -
- ) - )} - {Array.from({ length: getDaysInMonth(selectedMonth) }).map( - (_, index) => ( + + {isShowCalendar && ( +
+
- ) - )} -
+

+ {_.capitalize(format(currentMonth, "LLLL, yyyy", { locale: ru }))} +

+ +
+
+ {["Пн", "Вт", "Ср", "Чт", "Пт", "Сб", "Вс"].map((value, index) => ( +
+ {value} +
+ ))} +
+
+ {Array.from({ length: getISODay(currentMonth) - 1 }).map( + (_, index) => ( +
+ ) + )} + {Array.from({ length: getDaysInMonth(currentMonth) }).map( + (_, index) => ( + + ) + )} +
+
+ )}
); } diff --git a/client/src/components/Card.tsx b/client/src/components/Card.tsx index f7d293f..338f51a 100644 --- a/client/src/components/Card.tsx +++ b/client/src/components/Card.tsx @@ -6,6 +6,10 @@ import MoreIcon from "./icons/MoreIcon"; import api from "../utils/api"; import Button from "./Button"; import { Link } from "react-router-dom"; +import IUser from "../types/IUser"; +import useAuthStore from "../stores/useAuthStore"; +import EntryIcon from "./icons/EntryIcon"; +import { isAfter } from "date-fns"; interface CardProps { companyId: string; @@ -17,13 +21,9 @@ interface CardProps { phone: string; email: string; }; - manager?: { - id: string; - name: string; - avatar: string; - }; - managers: any[]; - handleSelect: (scheduledSessionId: string, managerId: string) => void; + manager?: IUser; + managers: IUser[]; + handleSelect: (scheduledSessionId: string, managerId: string | null) => void; } function Card({ @@ -36,8 +36,9 @@ function Card({ managers, handleSelect, }: CardProps) { + const { user } = useAuthStore(); const [isShow, setIsShow] = useState(false); - const [availableManagers, setAvailableManagers] = useState(); + const [availableManagers, setAvailableManagers] = useState(); async function getAvailableManagers() { const result: any[] = await api @@ -59,7 +60,7 @@ function Card({ }, [isShow]); return ( -
+
@@ -83,7 +84,7 @@ function Card({
)}
-
+

{client?.phone || "Телефон не указан"}

@@ -92,19 +93,16 @@ function Card({

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

{manager ? manager.name : "Не назначен"} @@ -112,27 +110,61 @@ function Card({

- {manager && ( - - - + {user?.role === "manager" && + (manager ? ( + isAfter(new Date(), new Date(scheduleSessionStartAt)) ? ( + + + + ) : ( + manager.id === user.id && ( + + ) + ) + ) : ( + + ))} + + {user?.role === "admin" && ( +
+ {manager && ( + + +
)} -
-
+
{availableManagers && availableManagers.length > 0 && ( setValue(e.target.value)} onFocus={handleFocus} - className={`px-3 py-2.5 outline-none rounded-lg border border-[#DAE0E5] focus:border-[#49A1F5] transition-colors ${className}`} + className={`px-3 py-2.5 outline-none rounded-lg border border-[#DAE0E5] focus:border-[#49A1F5] transition-colors text-sm ${className}`} /> ); } diff --git a/client/src/components/Managers.tsx b/client/src/components/Managers.tsx new file mode 100644 index 0000000..b7ccc21 --- /dev/null +++ b/client/src/components/Managers.tsx @@ -0,0 +1,67 @@ +import useAuthStore from "../stores/useAuthStore"; +import useSettingsStore from "../stores/useSettingsStore"; +import useStore from "../stores/useStore"; +import Button from "./Button"; +import ChevronDownIcon from "./icons/ChevronDownIcon"; +import ChevronUpIcon from "./icons/ChevronUpIcon"; +import MoreIcon from "./icons/MoreIcon"; + +function Managers() { + const { user } = useAuthStore(); + const { managers } = useStore(); + const { isShowManagers, setIsShowManagers } = useSettingsStore(); + + return ( +
+
+

Менеджеры

+
+ + {isShowManagers && ( +
+
+ {managers?.map((manager) => ( +
+
+ +

{manager.name}

+
+ {user?.role === "admin" && ( +
+ ))} +
+ {user?.role === "admin" && ( + + )} +
+ )} +
+ ); +} + +export default Managers; diff --git a/client/src/components/Menu.tsx b/client/src/components/Menu.tsx index 569182c..b63477a 100644 --- a/client/src/components/Menu.tsx +++ b/client/src/components/Menu.tsx @@ -6,32 +6,39 @@ import ParamsIcon from "./icons/ParamsIcon"; import WorkIcon from "./icons/WorkIcon"; import ExitIcon from "./icons/ExitIcon"; import useAuthStore from "../stores/useAuthStore"; +import { useClickAway } from "@uidotdev/usehooks"; function Menu() { const [isShow, setIsShow] = useState(false); - const { user, setAccessToken } = useAuthStore(); + const ref = useClickAway(() => { + setIsShow(false); + }); function logout() { setAccessToken(null); } return ( -
- +
+ + + {(state) => (
-
+
-

{user?.name[0]}

+

+ {user?.name[0]} +

{user?.name}

diff --git a/client/src/components/Schedules.tsx b/client/src/components/Schedules.tsx new file mode 100644 index 0000000..67e0eb2 --- /dev/null +++ b/client/src/components/Schedules.tsx @@ -0,0 +1,84 @@ +import { format } from "date-fns"; +import Button from "./Button"; +import CreateScheduleModal from "./modals/CreateScheduleModal"; +import useModalStore from "../stores/useModalStore"; +import useSettingsStore from "../stores/useSettingsStore"; +import ChevronDownIcon from "./icons/ChevronDownIcon"; +import ChevronUpIcon from "./icons/ChevronUpIcon"; +import useStore from "../stores/useStore"; +import useAuthStore from "../stores/useAuthStore"; + +function Schedules() { + const { user } = useAuthStore(); + const { schedules } = useStore(); + const { setModal } = useModalStore(); + const { isShowSchedules, setIsShowSchedules } = useSettingsStore(); + + return ( +
+
+

Расписание

+
+ + {isShowSchedules && ( +
+ {schedules?.map((schedule) => ( +
+

+ + Действует с{" "} + {format(new Date(schedule.startDate), "dd.MM.yyyy")} + + {/* - */} + + {/* {format(parseISO(schedule.endDate), "dd.MM.yyyy")} */} + +

+ +
+
+

Сеансов в день

+

{schedule.sessionsPerDay}

+
+
+

+ Длительность сеанса +

+

{schedule.sessionDuration} мин.

+
+
+

Между сеансами

+

{schedule.sessionBreak} мин.

+
+
+

Время работы

+

+ {schedule.startTime} - {schedule.endTime} +

+
+
+
+ ))} + + {user?.role === "admin" && ( + + )} +
+ )} +
+ ); +} + +export default Schedules; diff --git a/client/src/components/SelectUser.tsx b/client/src/components/SelectUser.tsx index 298a7d6..dbdb106 100644 --- a/client/src/components/SelectUser.tsx +++ b/client/src/components/SelectUser.tsx @@ -4,7 +4,7 @@ interface SelectUserProps { shown: boolean; selectedManagerId?: string; managers: any[]; - handleClick: (managerId: string) => void; + handleClick: (managerId: string | null) => void; handleShown: () => void; } @@ -26,7 +26,7 @@ function SelectUser({ {(state) => (
{managers.map((manager) => ( ))} + +
)} diff --git a/client/src/components/icons/EntryIcon.tsx b/client/src/components/icons/EntryIcon.tsx new file mode 100644 index 0000000..4457fa5 --- /dev/null +++ b/client/src/components/icons/EntryIcon.tsx @@ -0,0 +1,21 @@ +function EntryIcon() { + return ( + + + + ); +} + +export default EntryIcon; diff --git a/client/src/components/modals/CreateScheduleModal.tsx b/client/src/components/modals/CreateScheduleModal.tsx index 9cbdf31..063fef5 100644 --- a/client/src/components/modals/CreateScheduleModal.tsx +++ b/client/src/components/modals/CreateScheduleModal.tsx @@ -20,20 +20,11 @@ import ChoiceChips from "../ChoiceChips"; // import ISchedule from "../../types/ISchedule"; import DatePicker from "../DatePicker"; import IScheduledSession from "../../types/IScheduledSession"; +import useStore from "../../stores/useStore"; +import ISchedule from "../../types/ISchedule"; -interface Props { - companyId: string; - buildId: string; - // schedules: ISchedule[]; - handleCreate: () => void; -} - -function CreateScheduleModal({ - companyId, - buildId, - // schedules, - handleCreate, -}: Props) { +function CreateScheduleModal() { + const { company, selectedBuild, schedules, setSchedules } = useStore(); const setModal = useModalStore((state) => state.setModal); const [date, setDate] = useState(startOfDay(new Date())); const [startDate, setStartDate] = useState(startOfDay(new Date())); @@ -47,7 +38,9 @@ function CreateScheduleModal({ async function getLastScheduledSessionDate() { try { const result: IScheduledSession | null = await api - .get(`companies/${companyId}/builds/${buildId}/last_scheduled_session`) + .get( + `companies/${company?.id}/builds/${selectedBuild?.id}/last_scheduled_session` + ) .json(); if (!result || !result.startAt) return; @@ -84,27 +77,33 @@ function CreateScheduleModal({ } } - async function createSchedule() { - console.log("date", date); + async function addSchedule() { + try { + const result: ISchedule = await api + .post( + `companies/${company?.id}/builds/${selectedBuild?.id}/schedules`, + { + json: { + startDate: date, + startTime, + endTime, + weekends, + sessionDuration, + sessionBreak, + sessionsPerDay, + }, + } + ) + .json(); - await api.post(`companies/${companyId}/builds/${buildId}/schedules`, { - json: { - startDate: date, - startTime, - endTime, - weekends, - sessionDuration, - sessionBreak, - sessionsPerDay, - }, - }); + setSchedules([...schedules, result]); + } catch (error) { + alert((error as Error).message); + } } async function handleClickCreateSchedule() { - // if (!startDate) return; - - await createSchedule(); - handleCreate(); + await addSchedule(); setModal(null); } diff --git a/client/src/pages/DashboardPage.tsx b/client/src/pages/DashboardPage.tsx index 4dbf2ab..50a6f3b 100644 --- a/client/src/pages/DashboardPage.tsx +++ b/client/src/pages/DashboardPage.tsx @@ -1,52 +1,53 @@ /* eslint-disable react-hooks/exhaustive-deps */ /* eslint-disable @typescript-eslint/no-explicit-any */ -import { useEffect, useRef, useState } from "react"; -import Card from "../components/Card"; -import EmptyCard from "../components/EmptyCard"; -import TabButton from "../components/TabButton"; -import BurgerIcon from "../components/icons/BurgerIcon"; -import ChevronLeftIcon from "../components/icons/ChevronLeftIcon"; -import ChevronRightIcon from "../components/icons/ChevronRightIcon"; -import api from "../utils/api"; -import useAuthStore from "../stores/useAuthStore"; import { eachMinuteOfInterval, format, parse, addDays, subDays, - isAfter, + isEqual, } from "date-fns"; -import Button from "../components/Button"; +import { useEffect, useRef, useState } from "react"; +import Card from "../components/Card"; +import EmptyCard from "../components/EmptyCard"; +import TabButton from "../components/TabButton"; +import api from "../utils/api"; import { ru } from "date-fns/locale"; import { Transition } from "react-transition-group"; -import SpinnerIcon from "../components/icons/SpinnerIcon"; -import useModalStore from "../stores/useModalStore"; -import ModalContainer from "../components/ModalContainer"; -import CreateScheduleModal from "../components/modals/CreateScheduleModal"; -import MoreIcon from "../components/icons/MoreIcon"; -import ISchedule from "../types/ISchedule"; -import Calendar from "../components/Calendar"; -import ChevronUpIcon from "../components/icons/ChevronUpIcon"; -import { isEqual } from "lodash"; import _ from "lodash"; +import useStore from "../stores/useStore"; +import useAuthStore from "../stores/useAuthStore"; import Menu from "../components/Menu"; +import Button from "../components/Button"; +import Calendar from "../components/Calendar"; +import Managers from "../components/Managers"; +import Schedules from "../components/Schedules"; +import SpinnerIcon from "../components/icons/SpinnerIcon"; +import ModalContainer from "../components/ModalContainer"; +import ChevronLeftIcon from "../components/icons/ChevronLeftIcon"; +import ChevronRightIcon from "../components/icons/ChevronRightIcon"; function DashboardPage() { - const [user, setAccessToken] = useAuthStore((state) => [ - state.user, - state.setAccessToken, - ]); - const [company, setCompany] = useState<{ [key: string]: any }>(); - const [selectedBuildManagers, setSelectedBuildManagers] = useState(); - const [builds, setBuilds] = useState(); - const [selectedBuild, setSelectedBuild] = useState<{ [key: string]: any }>(); - const [schedules, setSchedules] = useState(); + const { user } = useAuthStore(); + const { + company, + setCompany, + selectedBuild, + setSelectedBuild, + builds, + setBuilds, + managers, + setManagers, + schedules, + setSchedules, + selectedDay, + setSelectedDay, + } = useStore(); const [duration, setDuration] = useState(); const [scheduledSessions, setScheduledSessions] = useState(); const [generatedScheduledSessions, setGeneratedScheduledSessions] = useState(); - const [selectedDate, setSelectedDate] = useState(new Date()); const [dateTimes, setDateTimes] = useState(); const [currentTime, setCurrentTime] = useState( format(new Date(), "HH:mm") @@ -54,63 +55,19 @@ function DashboardPage() { const [isLoadingScheduledSessions, setIsLoadingScheduledSessions] = useState(true); const scheduledSessionsRef = useRef(null); - const setModal = useModalStore((state) => state.setModal); - - // const [selectedDate, setSelectedDate] = useState( - // format(new Date(), "d MMMM, yyyy", { locale: ru }) - // ); function selectNextDay() { - setSelectedDate((prev) => addDays(prev, 1)); + setSelectedDay(addDays(selectedDay, 1)); } function selectPrevDay() { - setSelectedDate((prev) => subDays(prev, 1)); + setSelectedDay(subDays(selectedDay, 1)); } function selectCurrentDate() { - setSelectedDate(new Date()); + setSelectedDay(new Date()); } - // useEffect(() => { - // console.log("schedules", schedules); - - // if (!selectedDate || !selectedBuild || !schedules?.length) return; - - // const foundSchedule = schedules.find( - // (schedule) => - // isWithinInterval(selectedDate, { - // start: new Date(schedule.startDate), - // end: addDays(new Date(schedule.startDate), 14), - // }) && selectedBuild.id === schedule.buildId - // ); - - // console.log("foundSchedule", foundSchedule); - - // if (foundSchedule) { - // const startDateTime = parse( - // foundSchedule.startTime, - // "HH:mm", - // selectedDate - // ); - // const endDateTime = parse(foundSchedule.endTime, "HH:mm", selectedDate); - // const step = foundSchedule.sessionDuration + foundSchedule.sessionBreak; - - // setDateTimes( - // eachMinuteOfInterval( - // { - // start: startDateTime, - // end: endDateTime, - // }, - // { step } - // ) - // ); - // } else { - // setDateTimes(undefined); - // setGeneratedScheduledSessions(undefined); - // } - // }, [selectedDate, selectedBuild, schedules]); - function findSessionsByTime(time: Date) { if (!scheduledSessions) return []; @@ -122,6 +79,8 @@ function DashboardPage() { } function generateScheduledSessions() { + if (!selectedBuild) return; + const newGeneratedScheduledSession: any[] = []; dateTimes?.forEach((time) => { @@ -134,7 +93,7 @@ function DashboardPage() { for ( let index = 0; - index < selectedBuild?.sessionLimit - foundSessionsByTime.length; + index < selectedBuild.sessionLimit - foundSessionsByTime.length; index++ ) { sessions.push({}); @@ -150,36 +109,36 @@ function DashboardPage() { } useEffect(() => { - if (!scheduledSessions) return; + if (!scheduledSessions || !selectedBuild || !selectedDay) return; generateScheduledSessions(); - }, [scheduledSessions]); + }, [scheduledSessions, selectedBuild, selectedDay]); useEffect(() => { - if (!schedules || !selectedDate || !selectedBuild) return; + if (!schedules || !selectedDay || !selectedBuild) return; - const foundSchedule = schedules.find((schedule) => - isAfter(new Date(), new Date(schedule.startDate)) + console.log("schedules", schedules); + + const foundSchedule = schedules.find( + (schedule) => schedule.buildId === selectedBuild.id ); - if (!foundSchedule) return; + if (!foundSchedule) { + setDateTimes([]); + return; + } const step = foundSchedule.sessionDuration + foundSchedule.sessionBreak; // 35 const newTimes = eachMinuteOfInterval( { - start: parse(foundSchedule.startTime, "HH:mm", new Date(selectedDate)), // 11:00 - end: parse(foundSchedule.endTime, "HH:mm", new Date(selectedDate)), // 20:00 + 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, selectedDate, selectedBuild]); - - useEffect(() => { - if (!schedules) return; - // const schedule = schedules.find(schedule => schedule.startDate); - }, [selectedDate, selectedBuild, schedules]); + }, [schedules, selectedDay, selectedBuild]); async function getCompany() { if (!user) { @@ -224,7 +183,7 @@ function DashboardPage() { const result: any = await api .get(`companies/${company.id}/builds/${selectedBuild.id}/users`) .json(); - setSelectedBuildManagers(result); + setManagers(result); } catch (error) { if (error instanceof Error) { console.log("Error: ", error.message); @@ -250,7 +209,7 @@ function DashboardPage() { .get( `companies/${company.id}/builds/${ selectedBuild.id - }/scheduled_sessions?date=${selectedDate.toISOString()}` + }/scheduled_sessions?date=${selectedDay.toISOString()}` ) .json(); @@ -264,39 +223,9 @@ function DashboardPage() { if (useLoader) setIsLoadingScheduledSessions(false); } - // function generateScheduledSessions() { - // if (!dateTimes || !scheduledSessions || !selectedBuild) return; - - // const arr: any[][] = []; - - // dateTimes.forEach((dateTime) => { - // const arr2 = []; - - // const foundSessionsCount = scheduledSessions.filter((session) => - // isEqual(session.startAt, dateTime) - // ); - - // arr2.push(dateTime); - - // for (let i = 0; i < selectedBuild.sessionLimit; i++) { - // if (foundSessionsCount[i]) { - // arr2.push(foundSessionsCount[i]); - // } else { - // arr2.push({}); - // } - // } - - // arr.push(arr2); - - // console.log("arr2", arr2); - // }); - - // setGeneratedScheduledSessions(arr); - // } - async function updateScheduledSessionManager( scheduledSessionId: string, - managerId: string + managerId: string | null ) { if (!company || !scheduledSessions) return; @@ -325,6 +254,8 @@ function DashboardPage() { async function getSchedules() { if (!company || !selectedBuild) return; + console.log("selectedBuild.id", selectedBuild.id); + const result: any[] = await api .get(`companies/${company.id}/builds/${selectedBuild.id}/schedules`) .json(); @@ -355,33 +286,28 @@ function DashboardPage() { }, [builds]); useEffect(() => { - if (!selectedBuildManagers || !selectedDate || !selectedBuild) return; + if (!managers || !selectedDay || !selectedBuild) return; getScheduledSessions(true); const interval = setInterval(() => { getScheduledSessions(); - }, 3000); + }, 1000); return () => { clearInterval(interval); }; - }, [selectedBuildManagers, selectedDate, selectedBuild]); + }, [managers, selectedDay, selectedBuild]); useEffect(() => { if (!company || !selectedBuild) return; - getManagers(); + console.log("selectedBuild", selectedBuild); getSchedules(); + getManagers(); }, [selectedBuild]); - // useEffect(() => { - // if (!scheduledSessions) return; - // generateScheduledSessions(); - // }, [scheduledSessions]); - useEffect(() => { - // setIsLoadingScheduledSessions(true); scheduledSessionsRef.current?.scrollTo({ top: 0, behavior: "smooth" }); - }, [selectedDate, selectedBuild]); + }, [selectedDay, selectedBuild]); return (
@@ -392,7 +318,9 @@ function DashboardPage() { setSelectedBuild(build)} - active={selectedBuild && build.id === selectedBuild.id} + active={ + (selectedBuild && build.id === selectedBuild?.id) || false + } > {build.name} @@ -403,7 +331,7 @@ function DashboardPage() {

Расписание

- {format(selectedDate, "PPPP", { locale: ru })} + {format(selectedDay, "PPPP", { locale: ru })}

- {generatedScheduledSession.sessions.map( - (session: any, index2: number) => { - const selectedManager = selectedBuildManagers?.find( - (manager) => manager.id == session.userId - ); + {company && + managers && + selectedBuild && + generatedScheduledSession.sessions.map( + (session: any, index2: number) => { + const selectedManager = managers.find( + (manager) => manager.id == session.userId + ); - if (!_.isEmpty(session)) { - return ( - - updateScheduledSessionManager( - scheduledSessionId, - managerId - ) - } - /> - ); - } else { - return ( - - ); + if (!_.isEmpty(session)) { + return ( + + updateScheduledSessionManager( + scheduledSessionId, + managerId + ) + } + /> + ); + } else { + return ( + + ); + } } - } - )} + )}
) )} - - {/* {company && - selectedBuild && - user && - generatedScheduledSessions?.map( - (generatedScheduledSession, index) => { - return ( -
- {generatedScheduledSession.map( - (scheduledSession, index2) => { - if (index2 === 0) { - return ( -
- {format(scheduledSession, "HH:mm")} -
- ); - } else { - if (Object.keys(scheduledSession).length) { - const selectedManager = selectedBuildManagers?.find( - (manager) => manager.id == scheduledSession.userId - ); - - return ( - - updateScheduledSessionManager( - scheduledSessionId, - managerId - ) - } - /> - ); - } else { - return ; - } - } - } - )} -
- ); - } - )} */}
@@ -576,110 +452,9 @@ function DashboardPage() {
-
-
-

Календарь

-
- setSelectedDate(date)} - /> -
- -
-

Расписание

- - {schedules?.map((schedule) => ( -
-

- - Действует с{" "} - {format(new Date(schedule.startDate), "dd.MM.yyyy")} - - {/* - */} - - {/* {format(parseISO(schedule.endDate), "dd.MM.yyyy")} */} - -

- -
-
-

Сеансов в день

-

{schedule.sessionsPerDay}

-
-
-

- Длительность сеанса -

-

{schedule.sessionDuration} мин.

-
-
-

Между сеансами

-

{schedule.sessionBreak} мин.

-
-
-

Время работы

-

- {schedule.startTime} - {schedule.endTime} -

-
-
-
- ))} - - -
- -
-

Менеджеры

- -
- {selectedBuildManagers?.map((manager) => ( -
-
- -

{manager.name}

-
-
- ))} -
- - -
+ + +
diff --git a/client/src/stores/useAuthStore.ts b/client/src/stores/useAuthStore.ts index 167143c..2791148 100644 --- a/client/src/stores/useAuthStore.ts +++ b/client/src/stores/useAuthStore.ts @@ -1,19 +1,20 @@ import { create } from "zustand"; import { persist } from "zustand/middleware"; +import IUser from "../types/IUser"; interface AuthState { accessToken: string | null; - user: Record | null; setAccessToken: (accessToken: string | null) => void; - setUser: (user: Record | null) => void; + user: IUser | null; + setUser: (user: IUser | null) => void; } const useAuthStore = create()( persist( (set) => ({ accessToken: null, - user: null, setAccessToken: (accessToken) => set({ accessToken }), + user: null, setUser: (user) => set({ user }), }), { diff --git a/client/src/stores/useSettingsStore.ts b/client/src/stores/useSettingsStore.ts new file mode 100644 index 0000000..0a16dbb --- /dev/null +++ b/client/src/stores/useSettingsStore.ts @@ -0,0 +1,29 @@ +import { create } from "zustand"; +import { persist } from "zustand/middleware"; + +interface SettingsState { + isShowCalendar: boolean; + setIsShowCalendar: (isShowCalendar: boolean) => void; + isShowSchedules: boolean; + setIsShowSchedules: (isShowSchedules: boolean) => void; + isShowManagers: boolean; + setIsShowManagers: (isShowManagers: boolean) => void; +} + +const useSettingsStore = create()( + persist( + (set) => ({ + isShowCalendar: true, + setIsShowCalendar: (isShowCalendar) => set({ isShowCalendar }), + isShowSchedules: true, + setIsShowSchedules: (isShowSchedules) => set({ isShowSchedules }), + isShowManagers: true, + setIsShowManagers: (isShowManagers) => set({ isShowManagers }), + }), + { + name: "settings", + } + ) +); + +export default useSettingsStore; diff --git a/client/src/stores/useStore.ts b/client/src/stores/useStore.ts new file mode 100644 index 0000000..5e5b1f8 --- /dev/null +++ b/client/src/stores/useStore.ts @@ -0,0 +1,37 @@ +import { create } from "zustand"; +import ICompany from "../types/ICompany"; +import IBuild from "../types/IBuild"; +import ISchedule from "../types/ISchedule"; +import IUser from "../types/IUser"; + +interface State { + company: ICompany | null; + setCompany: (company: ICompany) => void; + builds: IBuild[]; + setBuilds: (builds: IBuild[]) => void; + managers: IUser[]; + setManagers: (managers: IUser[]) => void; + schedules: ISchedule[]; + setSchedules: (schedules: ISchedule[]) => void; + selectedBuild: IBuild | null; + setSelectedBuild: (selectedBuild: IBuild) => void; + selectedDay: Date; + setSelectedDay: (selectedDay: Date) => void; +} + +const useStore = create((set) => ({ + company: null, + setCompany: (company) => set({ company }), + builds: [], + setBuilds: (builds) => set({ builds }), + managers: [], + setManagers: (managers) => set({ managers }), + schedules: [], + setSchedules: (schedules) => set({ schedules }), + selectedBuild: null, + setSelectedBuild: (selectedBuild) => set({ selectedBuild }), + selectedDay: new Date(), + setSelectedDay: (selectedDay) => set({ selectedDay }), +})); + +export default useStore; diff --git a/client/src/types/IBuild.ts b/client/src/types/IBuild.ts new file mode 100644 index 0000000..873145f --- /dev/null +++ b/client/src/types/IBuild.ts @@ -0,0 +1,9 @@ +interface IBuild { + id: string; + companyId: string; + build: string; + name: string; + sessionLimit: number; +} + +export default IBuild; diff --git a/client/src/types/ICompany.ts b/client/src/types/ICompany.ts new file mode 100644 index 0000000..dee960e --- /dev/null +++ b/client/src/types/ICompany.ts @@ -0,0 +1,6 @@ +interface ICompany { + id: string; + name: string; +} + +export default ICompany; diff --git a/client/src/types/IUser.ts b/client/src/types/IUser.ts new file mode 100644 index 0000000..ecff65a --- /dev/null +++ b/client/src/types/IUser.ts @@ -0,0 +1,10 @@ +interface IUser { + id: string; + companyId: string; + avatar: string; + name: string; + username: string; + role: string; +} + +export default IUser; diff --git a/server/src/models/Roles.ts b/server/src/models/Roles.ts new file mode 100644 index 0000000..1715a12 --- /dev/null +++ b/server/src/models/Roles.ts @@ -0,0 +1,23 @@ +import { model, Schema } from "mongoose"; + +const rolesSchema = new Schema( + { + userId: { + type: Schema.Types.ObjectId, + required: true, + }, + name: { + type: String, + required: true, + }, + }, + { + timestamps: true, + toJSON: { virtuals: true }, + toObject: { virtuals: true }, + } +); + +const Roles = model("Roles", rolesSchema); + +export default Roles; diff --git a/server/src/routes/companies.ts b/server/src/routes/companies.ts index dfb35d4..92fefea 100644 --- a/server/src/routes/companies.ts +++ b/server/src/routes/companies.ts @@ -142,7 +142,20 @@ router.put("/:id/scheduled_sessions/:scheduledSessionId", async (req, res) => { userId: req.body.userId, }); - if (scheduledSessionAtSameTime) { + 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; } diff --git a/server/src/routes/users.ts b/server/src/routes/users.ts index 2d2feac..e25cf71 100644 --- a/server/src/routes/users.ts +++ b/server/src/routes/users.ts @@ -1,7 +1,7 @@ import { Router } from "express"; // import User from "../models/User.js"; -const usersRouter = Router(); +const router = Router(); // usersRouter.get("/", async (_req, res) => { // const users = await User.find(); @@ -17,4 +17,6 @@ const usersRouter = Router(); // res.json(user); // }); +const usersRouter = router; + export default usersRouter;