From e1550665349288fb0e8adef4a27d349349dd2bee Mon Sep 17 00:00:00 2001 From: inmake Date: Tue, 11 Jun 2024 21:18:54 +0500 Subject: [PATCH] upd --- client/.env | 4 +- client/package.json | 1 + client/src/components/Button.tsx | 2 +- client/src/components/Card.tsx | 8 +- client/src/components/DatePicker.tsx | 6 +- client/src/components/EmptyCard.tsx | 14 +- client/src/components/Input.tsx | 15 +- client/src/components/Menu.tsx | 111 ++++++++++ client/src/components/icons/BellIcon.tsx | 21 ++ client/src/components/icons/BurgerIcon.tsx | 2 +- client/src/components/icons/ExitIcon.tsx | 21 ++ client/src/components/icons/ParamsIcon.tsx | 26 +++ client/src/components/icons/WorkIcon.tsx | 26 +++ .../components/modals/CreateScheduleModal.tsx | 23 +- .../modals/CreateScheduledSessionModal.tsx | 82 +++++++- client/src/pages/DashboardPage.tsx | 197 +++++++++++++----- client/yarn.lock | 12 ++ server/src/index.ts | 4 +- server/src/routes/companies.ts | 3 +- server/src/routes/scheduledSessions.ts | 55 ++--- 20 files changed, 520 insertions(+), 113 deletions(-) create mode 100644 client/src/components/Menu.tsx create mode 100644 client/src/components/icons/BellIcon.tsx create mode 100644 client/src/components/icons/ExitIcon.tsx create mode 100644 client/src/components/icons/ParamsIcon.tsx create mode 100644 client/src/components/icons/WorkIcon.tsx diff --git a/client/.env b/client/.env index 0038af3..30293ab 100644 --- a/client/.env +++ b/client/.env @@ -1,4 +1,4 @@ # VITE_API_URL=http://localhost:3001 -# VITE_API_URL=http://192.168.1.171:3001 -VITE_API_URL=https://crm.stream.graff.tech/api +VITE_API_URL=http://192.168.1.171:3001 +# VITE_API_URL=https://crm.stream.graff.tech/api VITE_STREAM_URL=https://stream.graff.tech diff --git a/client/package.json b/client/package.json index 5510550..097c808 100644 --- a/client/package.json +++ b/client/package.json @@ -10,6 +10,7 @@ "preview": "vite preview" }, "dependencies": { + "@react-input/mask": "^1.2.5", "@uidotdev/usehooks": "^2.4.1", "date-fns": "^2.30.0", "ky": "^1.0.1", diff --git a/client/src/components/Button.tsx b/client/src/components/Button.tsx index a175470..9b94fe7 100644 --- a/client/src/components/Button.tsx +++ b/client/src/components/Button.tsx @@ -30,7 +30,7 @@ function Button({ disabled={disabled || loading} type={type} onClick={handleClick} - className={`relative outline-none rounded-lg transition-all font-semibold flex justify-center items-center gap-1 disabled:bg-[#F2F2F2] disabled:text-[#CCCCCC] ${ + className={`outline-none rounded-lg transition-all font-semibold flex justify-center items-center gap-1 disabled:bg-[#F2F2F2] disabled:text-[#CCCCCC] ${ (color === "primary" && "bg-[#49A1F5] text-white hover:bg-[#4190DB]") || (color === "secondary" && "bg-[#F0F1F2] text-[#77828C] hover:bg-[#E6ECF2]") || diff --git a/client/src/components/Card.tsx b/client/src/components/Card.tsx index c237b67..f7d293f 100644 --- a/client/src/components/Card.tsx +++ b/client/src/components/Card.tsx @@ -12,7 +12,7 @@ interface CardProps { buildId: string; scheduledSessionId: string; scheduleSessionStartAt: string; - client: { + client?: { name: string; phone: string; email: string; @@ -65,7 +65,7 @@ function Card({

Клиент

-

{client.name}

+

{client?.name || "Имя не указано"}

{manager ? (
@@ -85,10 +85,10 @@ function Card({

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

- {client.email} + {client?.email || "Email не указан"}

diff --git a/client/src/components/DatePicker.tsx b/client/src/components/DatePicker.tsx index 7095f33..953e3d6 100644 --- a/client/src/components/DatePicker.tsx +++ b/client/src/components/DatePicker.tsx @@ -29,9 +29,9 @@ interface Props { function DatePicker({ defaultValue, startDate, onChange }: Props) { const [value, setValue] = useState( - startDate && isAfter(startOfDay(startDate), new Date()) - ? startOfDay(startDate) - : defaultValue || startOfDay(new Date()) + (defaultValue && startOfDay(defaultValue)) || + (startDate && startOfDay(startDate)) || + startOfDay(new Date()) ); const [selectedMonth, setSelectedMonth] = useState(startOfMonth(value)); const [isShowCalendar, setIsShowCalendar] = useState(false); diff --git a/client/src/components/EmptyCard.tsx b/client/src/components/EmptyCard.tsx index c423666..818a0de 100644 --- a/client/src/components/EmptyCard.tsx +++ b/client/src/components/EmptyCard.tsx @@ -4,9 +4,13 @@ import Button from "./Button"; import PlusIcon from "./icons/PlusIcon"; import CreateScheduledSessionModal from "./modals/CreateScheduledSessionModal"; +interface Props { + buildId: string; + startAt: Date; + duration: number; +} - -function EmptyCard() { +function EmptyCard({ buildId, startAt, duration }: Props) { const { setModal } = useModalStore(); return ( @@ -17,7 +21,11 @@ function EmptyCard() { className="group-hover:opacity-100 opacity-0" handleClick={() => setModal( - + ) } > diff --git a/client/src/components/Input.tsx b/client/src/components/Input.tsx index 352cd57..c2ac139 100644 --- a/client/src/components/Input.tsx +++ b/client/src/components/Input.tsx @@ -1,8 +1,9 @@ /* eslint-disable react-hooks/exhaustive-deps */ +import { useMask } from "@react-input/mask"; import { useEffect, useState } from "react"; interface InputProps { - type?: "text" | "email" | "password" | "time"; + type?: "text" | "email" | "password" | "time" | "tel"; placeholder?: string; autoFocus?: boolean; required?: boolean; @@ -24,16 +25,20 @@ function Input({ handleChange, handleFocus, }: InputProps) { - const [value, setValue] = useState(defaultValue); + const inputRef = useMask({ + mask: "+7 (___) ___-__-__", + replacement: { _: /\d/ }, + }); + + const [value, setValue] = useState(defaultValue || ""); useEffect(() => { - if (value && handleChange) { - handleChange(value); - } + handleChange && handleChange(value); }, [value]); return ( (false); + + const { user, setAccessToken } = useAuthStore(); + + function logout() { + setAccessToken(null); + } + + return ( +
+ + + + {(state) => ( +
+
+
+
+ + + +
+
+
+

{user?.name[0]}

+
+
+

{user?.name}

+

{user?.username}

+
+
+
+ + +
+
+ +
+
+ +
+
+
+
+
+ )} +
+
+ ); +} + +export default Menu; diff --git a/client/src/components/icons/BellIcon.tsx b/client/src/components/icons/BellIcon.tsx new file mode 100644 index 0000000..f8a79c9 --- /dev/null +++ b/client/src/components/icons/BellIcon.tsx @@ -0,0 +1,21 @@ +function BellIcon() { + return ( + + + + ); +} + +export default BellIcon; diff --git a/client/src/components/icons/BurgerIcon.tsx b/client/src/components/icons/BurgerIcon.tsx index 91cb94e..dbd12fc 100644 --- a/client/src/components/icons/BurgerIcon.tsx +++ b/client/src/components/icons/BurgerIcon.tsx @@ -11,7 +11,7 @@ function BurgerIcon() { fillRule="evenodd" clipRule="evenodd" d="M19 8H5C4.44772 8 4 7.55228 4 7C4 6.44772 4.44772 6 5 6L19 6C19.5523 6 20 6.44772 20 7C20 7.55228 19.5523 8 19 8ZM4 12C4 11.4477 4.44772 11 5 11H19C19.5523 11 20 11.4477 20 12C20 12.5523 19.5523 13 19 13H5C4.44772 13 4 12.5523 4 12ZM4 17C4 16.4477 4.44772 16 5 16H19C19.5523 16 20 16.4477 20 17C20 17.5523 19.5523 18 19 18H5C4.44772 18 4 17.5523 4 17Z" - fill="#111C26" + fill="currentColor" /> ); diff --git a/client/src/components/icons/ExitIcon.tsx b/client/src/components/icons/ExitIcon.tsx new file mode 100644 index 0000000..b2ba861 --- /dev/null +++ b/client/src/components/icons/ExitIcon.tsx @@ -0,0 +1,21 @@ +function ExitIcon() { + return ( + + + + ); +} + +export default ExitIcon; diff --git a/client/src/components/icons/ParamsIcon.tsx b/client/src/components/icons/ParamsIcon.tsx new file mode 100644 index 0000000..0a64618 --- /dev/null +++ b/client/src/components/icons/ParamsIcon.tsx @@ -0,0 +1,26 @@ +function ParamsIcon() { + return ( + + + + + ); +} + +export default ParamsIcon; diff --git a/client/src/components/icons/WorkIcon.tsx b/client/src/components/icons/WorkIcon.tsx new file mode 100644 index 0000000..ad876df --- /dev/null +++ b/client/src/components/icons/WorkIcon.tsx @@ -0,0 +1,26 @@ +function WorkIcon() { + return ( + + + + + ); +} + +export default WorkIcon; diff --git a/client/src/components/modals/CreateScheduleModal.tsx b/client/src/components/modals/CreateScheduleModal.tsx index c582e1b..9cbdf31 100644 --- a/client/src/components/modals/CreateScheduleModal.tsx +++ b/client/src/components/modals/CreateScheduleModal.tsx @@ -8,7 +8,13 @@ import { useEffect, useState } from "react"; import Select from "../Select"; import useModalStore from "../../stores/useModalStore"; import Input from "../Input"; -import { eachMinuteOfInterval, parse, parseISO, startOfDay } from "date-fns"; +import { + addDays, + eachMinuteOfInterval, + parse, + parseISO, + startOfDay, +} from "date-fns"; import api from "../../utils/api"; import ChoiceChips from "../ChoiceChips"; // import ISchedule from "../../types/ISchedule"; @@ -46,6 +52,8 @@ function CreateScheduleModal({ if (!result || !result.startAt) return; + console.log("result.startAt", result.startAt); + setStartDate(startOfDay(parseISO(result.startAt))); } catch (error) { alert((error as Error).message); @@ -77,6 +85,8 @@ function CreateScheduleModal({ } async function createSchedule() { + console.log("date", date); + await api.post(`companies/${companyId}/builds/${buildId}/schedules`, { json: { startDate: date, @@ -131,10 +141,13 @@ function CreateScheduleModal({
diff --git a/client/src/components/modals/CreateScheduledSessionModal.tsx b/client/src/components/modals/CreateScheduledSessionModal.tsx index 2ab83e0..38c7b68 100644 --- a/client/src/components/modals/CreateScheduledSessionModal.tsx +++ b/client/src/components/modals/CreateScheduledSessionModal.tsx @@ -6,12 +6,56 @@ import CloseIcon from "../icons/CloseIcon"; import Input from "../Input"; import Label from "../Label"; import useModalStore from "../../stores/useModalStore"; +import api from "../../utils/api"; +import { FormEvent, useState } from "react"; -function CreateScheduledSessionModal() { +interface Props { + buildId: string; + startAt: Date; + duration: number; +} + +function CreateScheduledSessionModal({ buildId, startAt, duration }: Props) { const { setModal } = useModalStore(); + const [email, setEmail] = useState(""); + const [phone, setPhone] = useState(""); + const [name, setName] = useState(""); + const [isLoading, setIsLoading] = useState(false); + + async function addSchesuledSession(e: FormEvent) { + e.preventDefault(); + + setIsLoading(true); + + try { + const result = await api + .post(`scheduled_sessions`, { + json: { + buildId, + startAt, + client: { + email, + phone, + name, + }, + }, + }) + .json(); + + console.log("result", result); + } catch (error) { + alert((error as Error).message); + } + + setIsLoading(false); + setModal(null); + } return ( -
+

Запланировать демонстрацию

@@ -31,16 +75,16 @@ function CreateScheduledSessionModal() {

Дата и время

-

{format(new Date(), "dd.MM.yyyy HH:mm")}

+

{format(startAt, "dd.MM.yyyy HH:mm")}

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

-

30 мин.

+

{duration} мин.

-
+ {/*

Жилой комплекс

Название ЖК

-
+
*/}

@@ -63,23 +107,39 @@ function CreateScheduledSessionModal() {
- - +
diff --git a/client/src/pages/DashboardPage.tsx b/client/src/pages/DashboardPage.tsx index d6195ca..4dbf2ab 100644 --- a/client/src/pages/DashboardPage.tsx +++ b/client/src/pages/DashboardPage.tsx @@ -15,7 +15,7 @@ import { parse, addDays, subDays, - isWithinInterval, + isAfter, } from "date-fns"; import Button from "../components/Button"; import { ru } from "date-fns/locale"; @@ -28,6 +28,9 @@ 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 Menu from "../components/Menu"; function DashboardPage() { const [user, setAccessToken] = useAuthStore((state) => [ @@ -39,9 +42,10 @@ function DashboardPage() { const [builds, setBuilds] = useState(); const [selectedBuild, setSelectedBuild] = useState<{ [key: string]: any }>(); const [schedules, setSchedules] = useState(); + const [duration, setDuration] = useState(); const [scheduledSessions, setScheduledSessions] = useState(); const [generatedScheduledSessions, setGeneratedScheduledSessions] = - useState(); + useState(); const [selectedDate, setSelectedDate] = useState(new Date()); const [dateTimes, setDateTimes] = useState(); const [currentTime, setCurrentTime] = useState( @@ -107,6 +111,71 @@ function DashboardPage() { // } // }, [selectedDate, selectedBuild, schedules]); + function findSessionsByTime(time: Date) { + if (!scheduledSessions) return []; + + const foundScheduledSessions = scheduledSessions?.filter( + (scheduledSession) => isEqual(new Date(scheduledSession.startAt), time) + ); + + return foundScheduledSessions; + } + + function generateScheduledSessions() { + const newGeneratedScheduledSession: any[] = []; + + dateTimes?.forEach((time) => { + const foundSessionsByTime = findSessionsByTime(time); + const sessions: any[] = []; + + if (foundSessionsByTime) { + sessions.push(...foundSessionsByTime); + } + + for ( + let index = 0; + index < selectedBuild?.sessionLimit - foundSessionsByTime.length; + index++ + ) { + sessions.push({}); + } + + newGeneratedScheduledSession.push({ + time, + sessions, + }); + }); + + setGeneratedScheduledSessions(newGeneratedScheduledSession); + } + + useEffect(() => { + if (!scheduledSessions) return; + generateScheduledSessions(); + }, [scheduledSessions]); + + useEffect(() => { + if (!schedules || !selectedDate || !selectedBuild) return; + + const foundSchedule = schedules.find((schedule) => + isAfter(new Date(), new Date(schedule.startDate)) + ); + + if (!foundSchedule) 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 + }, + { step } + ); + + setDateTimes(newTimes); + setDuration(foundSchedule.sessionDuration); + }, [schedules, selectedDate, selectedBuild]); + useEffect(() => { if (!schedules) return; // const schedule = schedules.find(schedule => schedule.startDate); @@ -195,37 +264,35 @@ function DashboardPage() { if (useLoader) setIsLoadingScheduledSessions(false); } - function logout() { - setAccessToken(null); - } + // function generateScheduledSessions() { + // if (!dateTimes || !scheduledSessions || !selectedBuild) return; - function generateScheduledSessions() { - if (!dateTimes || !scheduledSessions || !selectedBuild) return; + // const arr: any[][] = []; - const arr: any[][] = []; + // dateTimes.forEach((dateTime) => { + // const arr2 = []; - dateTimes.forEach((dateTime) => { - const arr2 = []; + // const foundSessionsCount = scheduledSessions.filter((session) => + // isEqual(session.startAt, dateTime) + // ); - const foundSessionsCount = scheduledSessions.filter( - (session) => session.startAt === dateTime.toISOString() - ); + // arr2.push(dateTime); - arr2.push(dateTime); + // for (let i = 0; i < selectedBuild.sessionLimit; i++) { + // if (foundSessionsCount[i]) { + // arr2.push(foundSessionsCount[i]); + // } else { + // arr2.push({}); + // } + // } - for (let i = 0; i < selectedBuild.sessionLimit; i++) { - if (foundSessionsCount[i]) { - arr2.push(foundSessionsCount[i]); - } else { - arr2.push({}); - } - } + // arr.push(arr2); - arr.push(arr2); - }); + // console.log("arr2", arr2); + // }); - setGeneratedScheduledSessions(arr); - } + // setGeneratedScheduledSessions(arr); + // } async function updateScheduledSessionManager( scheduledSessionId: string, @@ -306,10 +373,10 @@ function DashboardPage() { getSchedules(); }, [selectedBuild]); - useEffect(() => { - if (!scheduledSessions) return; - generateScheduledSessions(); - }, [scheduledSessions]); + // useEffect(() => { + // if (!scheduledSessions) return; + // generateScheduledSessions(); + // }, [scheduledSessions]); useEffect(() => { // setIsLoadingScheduledSessions(true); @@ -319,13 +386,8 @@ function DashboardPage() { return (
-
- +
+ {builds?.map((build) => ( - {company && + {generatedScheduledSessions?.map( + (generatedScheduledSession, index) => ( +
+
+

{format(generatedScheduledSession.time, "HH:mm")}

+
+
+ {generatedScheduledSession.sessions.map( + (session: any, index2: number) => { + const selectedManager = selectedBuildManagers?.find( + (manager) => manager.id == session.userId + ); + + if (!_.isEmpty(session)) { + return ( + + updateScheduledSessionManager( + scheduledSessionId, + managerId + ) + } + /> + ); + } else { + return ( + + ); + } + } + )} +
+
+ ) + )} + + {/* {company && selectedBuild && user && generatedScheduledSessions?.map( @@ -449,20 +560,12 @@ function DashboardPage() {
); } - )} + )} */}
-
-

- {company?.name} -

-

{user?.username}

- - Выйти - -
+