From c5aa7bd5245e83889621d26dfc0efd75a20e524f Mon Sep 17 00:00:00 2001 From: inmake Date: Tue, 17 Oct 2023 18:58:51 +0500 Subject: [PATCH] upd --- client/package.json | 2 + client/src/components/Button.tsx | 12 +- client/src/components/Card.tsx | 12 +- client/src/components/Input.tsx | 22 +- client/src/components/ModalContainer.tsx | 31 ++ client/src/components/Select.tsx | 73 +++++ client/src/components/icons/CloseIcon.tsx | 21 ++ .../src/components/modals/CreateSchedule.tsx | 274 ++++++++++++++++++ client/src/index.css | 20 +- client/src/pages/DashboardPage.tsx | 27 +- client/src/pages/RegistrationPage.tsx | 4 +- client/src/stores/useModalStore.ts | 13 + client/yarn.lock | 63 +++- server/src/routes/builds.ts | 8 +- server/src/routes/companies.ts | 48 ++- 15 files changed, 589 insertions(+), 41 deletions(-) create mode 100644 client/src/components/ModalContainer.tsx create mode 100644 client/src/components/Select.tsx create mode 100644 client/src/components/icons/CloseIcon.tsx create mode 100644 client/src/components/modals/CreateSchedule.tsx create mode 100644 client/src/stores/useModalStore.ts diff --git a/client/package.json b/client/package.json index 2fe8100..17bf2f3 100644 --- a/client/package.json +++ b/client/package.json @@ -13,6 +13,7 @@ "date-fns": "^2.30.0", "ky": "^1.0.1", "react": "^18.2.0", + "react-datepicker": "^4.20.0", "react-dom": "^18.2.0", "react-google-recaptcha": "^3.1.0", "react-router-dom": "^6.15.0", @@ -21,6 +22,7 @@ }, "devDependencies": { "@types/react": "^18.2.15", + "@types/react-datepicker": "^4.19.0", "@types/react-dom": "^18.2.7", "@types/react-google-recaptcha": "^2.1.6", "@types/react-transition-group": "^4.4.7", diff --git a/client/src/components/Button.tsx b/client/src/components/Button.tsx index 3b21200..ea1de92 100644 --- a/client/src/components/Button.tsx +++ b/client/src/components/Button.tsx @@ -6,6 +6,7 @@ interface ButtonProps { size?: "small" | "medium"; disabled?: boolean; loading?: boolean; + onlyIcon?: boolean; icon?: React.ReactNode; children?: React.ReactNode; className?: string; @@ -18,6 +19,7 @@ function Button({ size = "small", disabled, loading = false, + onlyIcon, icon, children, className, @@ -33,8 +35,14 @@ function Button({ (color === "secondary" && "bg-[#F0F1F2] text-[#77828C] active:text-white") } ${ - (size === "small" && `h-[32px] px-3 py-2.5 text-xs`) || - (size === "medium" && "h-[40px] px-6 py-3 text-sm") + (size === "small" && + `h-8 ${ + onlyIcon ? "p-1" : icon ? "pl-2 pr-4 py-1" : "px-3 py-2.5" + } text-xs`) || + (size === "medium" && + `h-10 ${ + onlyIcon ? "p-2" : icon ? "pl-4 pr-6 py-2" : "px-6 py-3" + } text-sm`) } ${className}`} > {loading ? : icon} {children} diff --git a/client/src/components/Card.tsx b/client/src/components/Card.tsx index 10846a9..de06748 100644 --- a/client/src/components/Card.tsx +++ b/client/src/components/Card.tsx @@ -44,8 +44,8 @@ function Card({ ) .json(); - const filteredManagers = managers.filter( - (manager) => !result.includes(manager.id) + const filteredManagers = managers.filter((manager) => + result.includes(manager.id) ); setAvailableManagers(filteredManagers); @@ -62,10 +62,10 @@ function Card({

{client.name}

{manager ? ( -
-
-

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

+
+

+ Готов

) : ( diff --git a/client/src/components/Input.tsx b/client/src/components/Input.tsx index 388caa4..352cd57 100644 --- a/client/src/components/Input.tsx +++ b/client/src/components/Input.tsx @@ -1,11 +1,16 @@ +/* eslint-disable react-hooks/exhaustive-deps */ import { useEffect, useState } from "react"; interface InputProps { - type?: "text" | "email" | "password"; + type?: "text" | "email" | "password" | "time"; placeholder?: string; autoFocus?: boolean; required?: boolean; + readOnly?: boolean; + defaultValue?: string; + className?: string; handleChange?: (value: string) => void; + handleFocus?: () => void; } function Input({ @@ -13,15 +18,18 @@ function Input({ placeholder, autoFocus, required, + readOnly, + defaultValue, + className, handleChange, + handleFocus, }: InputProps) { - const [value, setValue] = useState(""); + const [value, setValue] = useState(defaultValue); useEffect(() => { - if (handleChange) { + if (value && handleChange) { handleChange(value); } - // eslint-disable-next-line react-hooks/exhaustive-deps }, [value]); return ( @@ -30,9 +38,11 @@ function Input({ placeholder={placeholder} autoFocus={autoFocus} required={required} - value={value} + readOnly={readOnly} + value={defaultValue} onChange={(e) => setValue(e.target.value)} - className="px-3 py-2.5 outline-none rounded-lg border border-[#DAE0E5]" + onFocus={handleFocus} + className={`px-3 py-2.5 outline-none rounded-lg border border-[#DAE0E5] focus:border-[#49A1F5] transition-colors ${className}`} /> ); } diff --git a/client/src/components/ModalContainer.tsx b/client/src/components/ModalContainer.tsx new file mode 100644 index 0000000..aad54e6 --- /dev/null +++ b/client/src/components/ModalContainer.tsx @@ -0,0 +1,31 @@ +import { Transition } from "react-transition-group"; +import useModalStore from "../stores/useModalStore"; + +function ModalContainer() { + const [modal, setModal] = useModalStore((state) => [ + state.modal, + state.setModal, + ]); + + return ( + + {(state) => ( +
setModal(null)} + className={`min-h-screen p-8 absolute top-0 left-0 w-full flex justify-center items-center bg-black bg-opacity-30 overflow-auto cursor-pointer transition-opacity ${state}`} + > +
e.stopPropagation()} className="cursor-default"> + {modal} +
+
+ )} +
+ ); +} + +export default ModalContainer; diff --git a/client/src/components/Select.tsx b/client/src/components/Select.tsx new file mode 100644 index 0000000..bef933b --- /dev/null +++ b/client/src/components/Select.tsx @@ -0,0 +1,73 @@ +/* eslint-disable react-hooks/exhaustive-deps */ +import { useEffect, useState } from "react"; +import Input from "./Input"; +import CheckIcon from "./icons/CheckIcon"; +import { Transition } from "react-transition-group"; +import useOutsideClick from "../hooks/useOutsideClick"; +import ChevronDown from "./icons/ChevronDown"; + +/* eslint-disable @typescript-eslint/no-explicit-any */ +interface SelectProps { + defaultValue?: number; + options?: any[]; + handleChange: (value: number) => void; +} + +function Select({ defaultValue, options, handleChange }: SelectProps) { + const [value, setValue] = useState(defaultValue); + const [isShow, setIsShow] = useState(false); + const selectRef = useOutsideClick(() => setIsShow(false)); + + function handleClick(option: { [key: string]: any }) { + setValue(option.value); + setIsShow(false); + } + + useEffect(() => { + if (!value) return; + handleChange(value); + }, [value]); + + return ( +
+ setIsShow(true)} + defaultValue={options?.find((option) => option.value === value).text} + className="w-full cursor-pointer" + /> + + + {(state) => ( +
+ {options?.map((option, index) => ( + + ))} +
+ )} +
+
+ ); +} + +export default Select; diff --git a/client/src/components/icons/CloseIcon.tsx b/client/src/components/icons/CloseIcon.tsx new file mode 100644 index 0000000..a8016ac --- /dev/null +++ b/client/src/components/icons/CloseIcon.tsx @@ -0,0 +1,21 @@ +function CloseIcon() { + return ( + + + + ); +} + +export default CloseIcon; diff --git a/client/src/components/modals/CreateSchedule.tsx b/client/src/components/modals/CreateSchedule.tsx new file mode 100644 index 0000000..0e3176c --- /dev/null +++ b/client/src/components/modals/CreateSchedule.tsx @@ -0,0 +1,274 @@ +/* eslint-disable react-hooks/exhaustive-deps */ +/* eslint-disable no-irregular-whitespace */ +import { ru } from "date-fns/locale"; +import Button from "../Button"; +import CloseIcon from "../icons/CloseIcon"; +import DatePicker from "react-datepicker"; +import "react-datepicker/dist/react-datepicker.css"; +import Label from "../Label"; +import { useEffect, useState } from "react"; +import Select from "../Select"; +import useModalStore from "../../stores/useModalStore"; +import Input from "../Input"; +import { + addMonths, + addWeeks, + differenceInDays, + eachMinuteOfInterval, + format, + parse, +} from "date-fns"; + +function CreateSchedule() { + const setModal = useModalStore((state) => state.setModal); + const [selectedScheduleDate, setSelectedScheduleDate] = useState( + new Date() + ); + const [selectedScheduleDuration, setSelectedScheduleDuration] = + useState(3); + const [scheduleStartDate, setScheduleStartDate] = useState(); + const [scheduleEndDate, setScheduleEndDate] = useState(); + const [selectedSessionDuration, setSelectedSessionDuration] = + useState(30); + const [selectedSessionsBreak, setSelectedSessionsBreak] = useState(5); + const [selectedWorkTimeStart, setSelectedWorkTimeStart] = + useState("10:00"); + const [selectedWorkTimeEnd, setSelectedWorkTimeEnd] = + useState("20:00"); + const [sessionsCount, setSessionsCount] = useState(); + + useEffect(() => { + if (!selectedScheduleDate || !selectedScheduleDuration) return; + + setScheduleStartDate(selectedScheduleDate); + setScheduleEndDate( + selectedScheduleDuration !== 3 + ? addWeeks(selectedScheduleDate, selectedScheduleDuration) + : addMonths(selectedScheduleDate, 1) + ); + }, [selectedScheduleDate, selectedScheduleDuration]); + + function calculateSessionCount() { + if (!scheduleStartDate || !scheduleEndDate) return; + + const sessionsPerDay = eachMinuteOfInterval( + { + start: parse(selectedWorkTimeStart, "HH:mm", new Date()), + end: parse(selectedWorkTimeEnd, "HH:mm", new Date()), + }, + { step: selectedSessionDuration + selectedSessionsBreak } + ).length; + + const days = differenceInDays(scheduleEndDate, scheduleStartDate); + + setSessionsCount(days * sessionsPerDay); + } + + useEffect(() => { + calculateSessionCount(); + }, [ + scheduleStartDate, + scheduleEndDate, + selectedWorkTimeEnd, + selectedSessionDuration, + selectedSessionsBreak, + ]); + + return ( +
+
+

Создание расписания

+
+ +
+
+
+
+

Срок действия расписания

+
+
+ +
+
+
+ +
+
+

-

+
+
+
+
+
+ +
+
+
+

Продолжительность сеанса

+

+ Влияет на количество ежедневно проводимых сессий +

+
+ +
+ setSelectedSessionsBreak(value)} + /> +
+
+
+
+
+ {/*
+

Статистика

+
*/} +
+

Предварительный просмотр

+
+ {scheduleStartDate && scheduleEndDate && ( +

+ {format(scheduleStartDate, "dd.MM.yyyy")} + - + {format(scheduleEndDate, "dd.MM.yyyy")} +

+ )} +
+
+

+ Количество сеансов +

+

{sessionsCount}

+
+
+

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

+

{selectedSessionDuration} мин.

+
+
+

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

+

{selectedSessionsBreak} мин.

+
+
+

Время работы

+

+ {selectedWorkTimeStart} - {selectedWorkTimeEnd} +

+
+
+
+
+
+
+ +
+ + +
+
+ ); +} + +export default CreateSchedule; diff --git a/client/src/index.css b/client/src/index.css index fdf6f44..9fa9c09 100644 --- a/client/src/index.css +++ b/client/src/index.css @@ -1,4 +1,3 @@ -/* @import url("https://fonts.googleapis.com/css2?family=Manrope:wght@400;600&display=swap"); */ @import url("https://fonts.googleapis.com/css2?family=Inter:wght@400;600&display=swap"); @tailwind base; @@ -6,11 +5,12 @@ @tailwind utilities; body { - /* font-family: "Manrope", sans-serif; */ font-family: "Inter", sans-serif; color: #111c26; } +/* Scrollbar */ + *::-webkit-scrollbar { width: 8px; } @@ -25,6 +25,8 @@ body { border-width: 2px; } +/* Transition */ + .entering { opacity: 1; } @@ -40,3 +42,17 @@ body { .exited { opacity: 0; } + +/* DatePicker */ + +.react-datepicker { + font-family: inherit !important; +} + +.react-datepicker__day--selected { + background-color: #49a1f5 !important; +} + +.react-datepicker__day--selected:hover { + background-color: #4190db !important; +} diff --git a/client/src/pages/DashboardPage.tsx b/client/src/pages/DashboardPage.tsx index 843e796..2ce250b 100644 --- a/client/src/pages/DashboardPage.tsx +++ b/client/src/pages/DashboardPage.tsx @@ -20,6 +20,9 @@ import Button from "../components/Button"; 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 CreateSchedule from "../components/modals/CreateSchedule"; function DashboardPage() { const [user, setAccessToken] = useAuthStore((state) => [ @@ -49,6 +52,7 @@ 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 }) @@ -288,11 +292,13 @@ function DashboardPage() { handleClick={selectPrevDay} icon={} color="secondary" + onlyIcon />
@@ -375,7 +381,6 @@ function DashboardPage() { }} manager={selectedManager} managers={managers} - // managers={managers} handleSelect={(scheduledSessionId, managerId) => updateScheduledSessionManager( scheduledSessionId, @@ -417,9 +422,27 @@ function DashboardPage() {
-

...

+
+

Календарь

+
+ +
+

Расписание

+ +
+ + +
+ + ); } diff --git a/client/src/pages/RegistrationPage.tsx b/client/src/pages/RegistrationPage.tsx index b4c2ed0..20ac518 100644 --- a/client/src/pages/RegistrationPage.tsx +++ b/client/src/pages/RegistrationPage.tsx @@ -10,12 +10,12 @@ function RegistrationPage() {
- - diff --git a/client/src/stores/useModalStore.ts b/client/src/stores/useModalStore.ts new file mode 100644 index 0000000..aa7ccb0 --- /dev/null +++ b/client/src/stores/useModalStore.ts @@ -0,0 +1,13 @@ +import { create } from "zustand"; + +interface ModalState { + modal: React.ReactNode | null; + setModal: (modal: React.ReactNode) => void; +} + +const useModalStore = create((set) => ({ + modal: null, + setModal: (modal) => set({ modal }), +})); + +export default useModalStore; diff --git a/client/yarn.lock b/client/yarn.lock index 4225d8b..9b6f8dc 100644 --- a/client/yarn.lock +++ b/client/yarn.lock @@ -233,6 +233,11 @@ "@nodelib/fs.scandir" "2.1.5" fastq "^1.6.0" +"@popperjs/core@^2.11.8", "@popperjs/core@^2.9.2": + version "2.11.8" + resolved "https://registry.npmjs.org/@popperjs/core/-/core-2.11.8.tgz" + integrity sha512-P1st0aksCrn9sGZhp8GMYwBnQsbvAWsZAX44oXNNvLHGqAOcoVxmjZiohstwQ7SqKnbR47akdNi+uleWD8+g6A== + "@remix-run/router@1.8.0": version "1.8.0" resolved "https://registry.npmjs.org/@remix-run/router/-/router-1.8.0.tgz" @@ -321,6 +326,16 @@ resolved "https://registry.npmjs.org/@types/prop-types/-/prop-types-15.7.5.tgz" integrity sha512-JCB8C6SnDoQf0cNycqd/35A7MjcnK+ZTqE7judS6o7utxUCg6imJg3QK2qzHKszlTjcj2cn+NwMB2i96ubpj7w== +"@types/react-datepicker@^4.19.0": + version "4.19.0" + resolved "https://registry.npmjs.org/@types/react-datepicker/-/react-datepicker-4.19.0.tgz" + integrity sha512-461OQou2oHJIfqeTPY+8lBn/V4Z7usmJhcGDWO/R8wQYm+H5/Cqsrwto89J2RTbhWdiynmvh78Beh8txN5y0mA== + dependencies: + "@popperjs/core" "^2.9.2" + "@types/react" "*" + date-fns "^2.0.1" + react-popper "^2.2.5" + "@types/react-dom@^18.2.7": version "18.2.7" resolved "https://registry.npmjs.org/@types/react-dom/-/react-dom-18.2.7.tgz" @@ -598,6 +613,11 @@ chokidar@^3.5.3: optionalDependencies: fsevents "~2.3.2" +classnames@^2.2.6: + version "2.3.2" + resolved "https://registry.npmjs.org/classnames/-/classnames-2.3.2.tgz" + integrity sha512-CSbhY4cFEJRe6/GQzIk5qXZ4Jeg5pcsP7b5peFSDpffpe1cqjASH/n9UTjBwOp6XpMSTwQ8Za2K5V02ueA7Tmw== + color-convert@^2.0.1: version "2.0.1" resolved "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz" @@ -639,7 +659,7 @@ csstype@^3.0.2: resolved "https://registry.npmjs.org/csstype/-/csstype-3.1.2.tgz" integrity sha512-I7K1Uu0MBPzaFKg4nI5Q7Vs2t+3gWWW648spaF+Rg7pI9ds18Ugn+lvg4SHczUdKlHI5LWBXyqfS8+DufyBsgQ== -date-fns@^2.30.0: +date-fns@^2.0.1, date-fns@^2.30.0: version "2.30.0" resolved "https://registry.npmjs.org/date-fns/-/date-fns-2.30.0.tgz" integrity sha512-fnULvOpxnC5/Vg3NCiWelDsLiUc9bRwAPs/+LfTLNvetFCtCTN+yQz15C/fs4AwX1R9K5GLtLfn8QW+dWisaAw== @@ -1148,7 +1168,7 @@ lodash.merge@^4.6.2: resolved "https://registry.npmjs.org/lodash.merge/-/lodash.merge-4.6.2.tgz" integrity sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ== -loose-envify@^1.1.0, loose-envify@^1.4.0: +loose-envify@^1.0.0, loose-envify@^1.1.0, loose-envify@^1.4.0: version "1.4.0" resolved "https://registry.npmjs.org/loose-envify/-/loose-envify-1.4.0.tgz" integrity sha512-lyuxPGr/Wfhrlem2CL/UcnUc1zcqKAImBDzukY7Y5F/yQiNdko6+fRLevlw1HgMySw7f611UIY408EtxRSoK3Q== @@ -1374,7 +1394,7 @@ prelude-ls@^1.2.1: resolved "https://registry.npmjs.org/prelude-ls/-/prelude-ls-1.2.1.tgz" integrity sha512-vkcDPrRZo1QZLbn5RLGPpg/WmIQ65qoWWhcGKf/b5eplkkarX0m9z8ppCat4mlOqUsWpyNuYgO3VRyrYHSzX5g== -prop-types@^15.5.0, prop-types@^15.6.2: +prop-types@^15.5.0, prop-types@^15.6.2, prop-types@^15.7.2: version "15.8.1" resolved "https://registry.npmjs.org/prop-types/-/prop-types-15.8.1.tgz" integrity sha512-oj87CgZICdulUohogVAR7AjlC0327U4el4L6eAvOqCeudMDVU0NThNaV+b9Df4dXgSP1gXMTnPdhfe/2qDH5cg== @@ -1401,6 +1421,18 @@ react-async-script@^1.2.0: hoist-non-react-statics "^3.3.0" prop-types "^15.5.0" +react-datepicker@^4.20.0: + version "4.20.0" + resolved "https://registry.npmjs.org/react-datepicker/-/react-datepicker-4.20.0.tgz" + integrity sha512-I29yHN9SabUDSy7Xq3P8+E8E+D2vyeuYAYYWWjeMisGGtsatltV4CSHodyA7W9z0BuGycc/bhSClDbizx4gZHA== + dependencies: + "@popperjs/core" "^2.11.8" + classnames "^2.2.6" + date-fns "^2.30.0" + prop-types "^15.7.2" + react-onclickoutside "^6.13.0" + react-popper "^2.3.0" + react-dom@^18.2.0: version "18.2.0" resolved "https://registry.npmjs.org/react-dom/-/react-dom-18.2.0.tgz" @@ -1409,6 +1441,11 @@ react-dom@^18.2.0: loose-envify "^1.1.0" scheduler "^0.23.0" +react-fast-compare@^3.0.1: + version "3.2.2" + resolved "https://registry.npmjs.org/react-fast-compare/-/react-fast-compare-3.2.2.tgz" + integrity sha512-nsO+KSNgo1SbJqJEYRE9ERzo7YtYbou/OqjSQKxV7jcKox7+usiUVZOAC+XnDOABXggQTno0Y1CpVnuWEc1boQ== + react-google-recaptcha@^3.1.0: version "3.1.0" resolved "https://registry.npmjs.org/react-google-recaptcha/-/react-google-recaptcha-3.1.0.tgz" @@ -1422,6 +1459,19 @@ react-is@^16.13.1, react-is@^16.7.0: resolved "https://registry.npmjs.org/react-is/-/react-is-16.13.1.tgz" integrity sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ== +react-onclickoutside@^6.13.0: + version "6.13.0" + resolved "https://registry.npmjs.org/react-onclickoutside/-/react-onclickoutside-6.13.0.tgz" + integrity sha512-ty8So6tcUpIb+ZE+1HAhbLROvAIJYyJe/1vRrrcmW+jLsaM+/powDRqxzo6hSh9CuRZGSL1Q8mvcF5WRD93a0A== + +react-popper@^2.2.5, react-popper@^2.3.0: + version "2.3.0" + resolved "https://registry.npmjs.org/react-popper/-/react-popper-2.3.0.tgz" + integrity sha512-e1hj8lL3uM+sgSR4Lxzn5h1GxBlpa4CQz0XLF8kx4MDrDRWY0Ena4c97PUeSX9i5W3UAfDP0z0FXCTQkoXUl3Q== + dependencies: + react-fast-compare "^3.0.1" + warning "^4.0.2" + react-router-dom@^6.15.0: version "6.15.0" resolved "https://registry.npmjs.org/react-router-dom/-/react-router-dom-6.15.0.tgz" @@ -1703,6 +1753,13 @@ vite@^4.4.5: optionalDependencies: fsevents "~2.3.2" +warning@^4.0.2: + version "4.0.3" + resolved "https://registry.npmjs.org/warning/-/warning-4.0.3.tgz" + integrity sha512-rpJyN222KWIvHJ/F53XSZv0Zl/accqHR8et1kpaMTD/fLCRxtV8iX8czMzY7sVZupTI3zcUTg8eycS2kNF9l6w== + dependencies: + loose-envify "^1.0.0" + which@^2.0.1: version "2.0.2" resolved "https://registry.npmjs.org/which/-/which-2.0.2.tgz" diff --git a/server/src/routes/builds.ts b/server/src/routes/builds.ts index a22eade..457bfbe 100644 --- a/server/src/routes/builds.ts +++ b/server/src/routes/builds.ts @@ -9,10 +9,10 @@ buildsRouter.get("/", async (_req, res) => { res.json({ ok: 1 }); }); -buildsRouter.post("/", async (req, res) => { - const build = await Build.create(req.body); +// buildsRouter.post("/", async (req, res) => { +// const build = await Build.create(req.body); - res.json(build); -}); +// res.json(build); +// }); export default buildsRouter; diff --git a/server/src/routes/companies.ts b/server/src/routes/companies.ts index c82d197..ece3c60 100644 --- a/server/src/routes/companies.ts +++ b/server/src/routes/companies.ts @@ -83,6 +83,29 @@ 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; + } + + 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); + } +); + companiesRouter.put( "/:id/scheduled_sessions/:scheduledSessionId", async (req, res) => { @@ -140,6 +163,8 @@ companiesRouter.get( req.params.scheduledSessionId ); + const scheduledSessionUserId = scheduledSession?.userId; + const scheduledSessionsAtSameTime = await ScheduledSession.find({ companyId: req.params.id, buildId: req.params.buildId, @@ -150,27 +175,22 @@ companiesRouter.get( "users" ); - let userIds = users.map((user: any) => user.id); + const companyUserIds: any[] = users.map((user: any) => user.id); + const sessionsUserIds: any[] = []; - 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()); + for (const sessionAtSameTime of scheduledSessionsAtSameTime) { + if (sessionAtSameTime.userId) { + sessionsUserIds.push(sessionAtSameTime.userId.toString()); } } - const filteredUserIds = userIds.filter( + const filteredUserIds = companyUserIds.filter( (userId: any) => !sessionsUserIds.includes(userId) ); - filteredUserIds.push(scheduledSession?.userId); + if (scheduledSessionUserId) { + filteredUserIds.push(scheduledSessionUserId.toString()); + } res.json(filteredUserIds); } catch (error) {