From f9dbbecea6b82e77e134eab64c6abedec360caee Mon Sep 17 00:00:00 2001 From: inmake Date: Fri, 20 Dec 2024 20:14:02 +0500 Subject: [PATCH] upd --- client/src/components/Input.tsx | 2 +- client/src/components/Schedule.tsx | 25 +++- client/src/components/Timeline.tsx | 47 +++++-- client/src/components/modals/CompanyModal.tsx | 130 ++++++++++++++---- client/src/types/ICompany.ts | 2 + server/src/models/Company.ts | 6 + server/src/routes/companies.ts | 17 +++ 7 files changed, 191 insertions(+), 38 deletions(-) diff --git a/client/src/components/Input.tsx b/client/src/components/Input.tsx index 7089d50..b7b0752 100644 --- a/client/src/components/Input.tsx +++ b/client/src/components/Input.tsx @@ -43,7 +43,7 @@ function Input({ value={value} onChange={(e) => handleChange && handleChange(e.target.value)} onFocus={handleFocus} - className={`px-3 py-2.5 outline-none rounded-lg ring-1 ring-[#DAE0E5] focus:ring-[#49A1F5] ring-inset transition-all text-sm ${className}`} + className={`px-3 py-[9px] outline-none rounded-lg ring-1 ring-[#DAE0E5] focus:ring-[#49A1F5] ring-inset transition-all text-sm ${className}`} /> ); } diff --git a/client/src/components/Schedule.tsx b/client/src/components/Schedule.tsx index 99026e4..9866321 100644 --- a/client/src/components/Schedule.tsx +++ b/client/src/components/Schedule.tsx @@ -5,7 +5,9 @@ import Timeline from "./Timeline"; import { format, getDate, + getHours, getMonth, + parseISO, setDate, setHours, setMonth, @@ -119,6 +121,15 @@ function Schedule({ selectedDay, slots, events }: Props) { // setModal(null); } + function countWorkingHours() { + if (!company?.startTime || !company?.endTime) return 24; + + const startHour = getHours(parseISO(company.startTime)); + const endHour = getHours(parseISO(company.endTime)); + + return endHour - startHour; + } + useEffect(() => { if (!builds) return; @@ -169,13 +180,21 @@ function Schedule({ selectedDay, slots, events }: Props) {
- {Array.from({ length: 24 }).map((_, index) => ( + {Array.from({ length: countWorkingHours() }).map((_, index) => (

- {format(setHours(startOfDay(new Date()), index), "HH:mm")} + {format( + setHours( + startOfDay(new Date()), + company?.startTime && company.endTime + ? getHours(parseISO(company.startTime)) + index + : index + ), + "HH:mm" + )}

))} @@ -194,6 +213,8 @@ function Schedule({ selectedDay, slots, events }: Props) { onChangeDraftMode={handleChangeDraftMode} onChangeStartAt={handleChangeStartAt} onChangeDuration={handleChangeDuration} + startTime={company?.startTime} + endTime={company?.endTime} /> ))}
diff --git a/client/src/components/Timeline.tsx b/client/src/components/Timeline.tsx index 42c49bb..7b887a0 100644 --- a/client/src/components/Timeline.tsx +++ b/client/src/components/Timeline.tsx @@ -2,7 +2,15 @@ /* eslint-disable react-hooks/exhaustive-deps */ import { useState, MouseEvent, useEffect, useRef } from "react"; import TimelineSlot from "./TimelineSlot"; -import { addMinutes, differenceInMinutes, format, startOfDay } from "date-fns"; +import { + addHours, + addMinutes, + differenceInMinutes, + format, + getHours, + parseISO, + startOfDay, +} from "date-fns"; import Button from "./Button"; import IScheduledSession from "../types/IScheduledSession"; import useStore from "../stores/useStore"; @@ -14,6 +22,8 @@ interface Props { timelineEvents: IScheduledSession[]; slot: number; draftMode: boolean; + startTime?: string; // 10:00 + endTime?: string; onChangeDraftMode: (draftMode: boolean) => void; onChangeStartAt: (startAt: Date) => void; onChangeDuration: (duration: number) => void; @@ -27,6 +37,8 @@ function Timeline({ timelineEvents, slot, draftMode, + startTime, + endTime, onChangeDraftMode, onChangeStartAt, onChangeDuration, @@ -57,9 +69,14 @@ function Timeline({ setDuration(30); } - setStartAt( - addMinutes(startOfDay(new Date()), roundedY / minutePx).toISOString() - ); + if (startTime) { + const newStartOfDay = parseISO(startTime); + setStartAt(addMinutes(newStartOfDay, roundedY / minutePx).toISOString()); + } else { + setStartAt( + addMinutes(startOfDay(new Date()), roundedY / minutePx).toISOString() + ); + } onChangeSlot(slot); } @@ -70,10 +87,6 @@ function Timeline({ const rect = e.currentTarget.getBoundingClientRect(); const y = e.clientY - rect.top; - // if (y < startPosY + minutePx * 30) return; - - // if (y < startPosY + minutePx * 30) return; - if (y < startPosY + minutePx * 30) { setDuration(30); } else { @@ -117,6 +130,15 @@ function Timeline({ // ); // }, [timelineEvents]); + function countWorkingHours() { + if (!startTime || !endTime) return 24; + + const startHour = getHours(parseISO(startTime)); + const endHour = getHours(parseISO(endTime)); + + return endHour - startHour; + } + useEffect(() => { if (!startAt) return; @@ -150,7 +172,7 @@ function Timeline({ onMouseMove={handleMouseMove} onMouseUp={handleMouseUp} > - {Array.from({ length: 24 }).map((_, index) => ( + {Array.from({ length: countWorkingHours() }).map((_, index) => ( ( @@ -16,6 +20,45 @@ function CompanyModal() { const { user } = useAuthStore(); const { setModal } = useModalStore(); const { company, managers } = useStore(); + const [startTime, setStartTime] = useState( + company?.startTime ? format(parseISO(company.startTime), "HH:mm") : "" + ); + const [endTime, setEndTime] = useState( + company?.endTime ? format(parseISO(company.endTime), "HH:mm") : "" + ); + + async function handleClickSaveWorkingHours() { + if (!startTime || !endTime) return; + + try { + await api + .put(`companies/${company?.id}`, { + json: { + startTime: parse(startTime, "HH:mm", new Date()), + endTime: parse(endTime, "HH:mm", new Date()), + }, + }) + .json(); + + toast.success("Изменения сохранены"); + setModal(null); + setTimeout(() => { + window.location.reload(); + }, 2000); + } catch (error) { + toast.error("Что-то пошло не так"); + } + } + + useEffect(() => { + if (startTime) { + setStartTime((prev) => `${prev.split(":")[0]}:00`); + } + + if (endTime) { + setEndTime((prev) => `${prev.split(":")[0]}:00`); + } + }, [startTime, endTime]); return (
@@ -44,9 +87,34 @@ function CompanyModal() {
{selectedTab === "company" && ( -
-
-
+ //
+ //
+ //
+ // {company?.avatar && ( + // + // )} + //
+ //
+ //
+ //
+ // + // + //
+ //
+ // + // + //
+ //
+ //
+ //
+ //
+
+
+
{company?.avatar && ( )}
-
+
+
+
@@ -66,25 +136,35 @@ function CompanyModal() {
- {/* */} +
+
+

Рабочее время

+
+
+
+

Начало

+ setStartTime(value)} + /> +
+

-

+
+

Конец

+ setEndTime(value)} + /> +
+
+ +
- {/*
-
-

Проекты

- {Array.from({ length: 3 }).map((_, index) => ( -
-
-

-
- ))} -
-
-
*/}
)} {selectedTab === "employees" && ( @@ -125,7 +205,7 @@ function CompanyModal() {

{manager.phone}

-
+ {/*
{user?.role === "admin" && (
+
*/}
))}
diff --git a/client/src/types/ICompany.ts b/client/src/types/ICompany.ts index 93c0095..3b6e3fd 100644 --- a/client/src/types/ICompany.ts +++ b/client/src/types/ICompany.ts @@ -7,6 +7,8 @@ interface ICompany { phone?: string; address?: string; avatar?: string; + startTime?: string; + endTime?: string; } export default ICompany; diff --git a/server/src/models/Company.ts b/server/src/models/Company.ts index 2baf423..afa4abe 100644 --- a/server/src/models/Company.ts +++ b/server/src/models/Company.ts @@ -26,6 +26,12 @@ const companySchema = new Schema( address: { type: String, }, + startTime: { + type: String, + }, + endTime: { + type: String, + }, }, { timestamps: true, diff --git a/server/src/routes/companies.ts b/server/src/routes/companies.ts index 585902c..b462e03 100644 --- a/server/src/routes/companies.ts +++ b/server/src/routes/companies.ts @@ -24,6 +24,23 @@ router.get("/:companyId", async (req, res) => { res.json(company); }); +router.put("/:companyId", async (req, res) => { + if (req.params.companyId != res.locals.user.companyId) { + res.json({ error: "Access denied" }); + return; + } + + const company = await Company.findByIdAndUpdate( + req.params.companyId, + req.body, + { + new: true, + } + ); + + res.json(company); +}); + router.get("/:companyId/builds", async (req, res) => { if (req.params.companyId != res.locals.user.companyId) { res.json({ error: "Access denied" });