This commit is contained in:
2024-06-10 19:35:04 +05:00
parent 7e692fee4c
commit ac1a705187
11 changed files with 298 additions and 70 deletions
+3 -3
View File
@@ -30,12 +30,12 @@ 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 active:bg-[#49A1F5] disabled:bg-[#F2F2F2] disabled:text-[#CCCCCC] ${
className={`relative 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] active:text-white") ||
"bg-[#F0F1F2] text-[#77828C] hover:bg-[#E6ECF2]") ||
(color === "tertiary" &&
"text-[#77828C] hover:bg-[#E6ECF2] active:text-white")
"text-[#77828C] hover:bg-[#E6ECF2]")
} ${
(size === "small" &&
`h-8 ${
+113
View File
@@ -0,0 +1,113 @@
import {
addMonths,
format,
getDaysInMonth,
getISODay,
isEqual,
isToday,
setDate,
startOfDay,
startOfMonth,
subMonths,
} from "date-fns";
import { useEffect, useState } from "react";
import ChevronLeftIcon from "./icons/ChevronLeftIcon";
import ChevronRightIcon from "./icons/ChevronRightIcon";
import _ from "lodash";
import { ru } from "date-fns/locale";
interface Props {
defaultValue?: Date;
onChange?: (date: Date) => void;
}
function Calendar({ defaultValue, onChange }: Props) {
const [selectedDate, setSelectedDate] = useState<Date>(
startOfDay(defaultValue || new Date())
);
const [selectedMonth, setSelectedMonth] = useState<Date>(
startOfMonth(defaultValue || new Date())
);
function selectPrevMonth() {
setSelectedMonth(subMonths(selectedMonth, 1));
}
function selectNextMonth() {
setSelectedMonth(addMonths(selectedMonth, 1));
}
function hadndleClick(date: Date) {
setSelectedDate(date);
onChange && onChange(date);
}
useEffect(() => {
if (!defaultValue) return;
setSelectedDate(startOfDay(defaultValue));
}, [defaultValue]);
useEffect(() => {
setSelectedMonth(startOfMonth(selectedDate));
}, [selectedDate]);
return (
<div className="">
<div className="flex items-center justify-between">
<button
className="text-[#77828C] p-1 hover:bg-[#E6ECF2] rounded-lg transition-colors"
onClick={selectPrevMonth}
>
<ChevronLeftIcon />
</button>
<p className="text-sm font-semibold">
{_.capitalize(format(selectedMonth, "LLLL, yyyy", { locale: ru }))}
</p>
<button
className="text-[#77828C] p-1 hover:bg-[#E6ECF2] rounded-lg transition-colors"
onClick={selectNextMonth}
>
<ChevronRightIcon />
</button>
</div>
<div className="grid grid-cols-7 gap-2">
{["Пн", "Вт", "Ср", "Чт", "Пт", "Сб", "Вс"].map((value, index) => (
<div
key={index}
className="w-8 h-8 flex items-center justify-center text-xs font-semibold"
>
{value}
</div>
))}
</div>
<div className="grid grid-cols-7 gap-2">
{Array.from({ length: getISODay(selectedMonth) - 1 }).map(
(_, index) => (
<div key={index} className="w-8 h-8"></div>
)
)}
{Array.from({ length: getDaysInMonth(selectedMonth) }).map(
(_, index) => (
<button
key={index}
className={`w-8 h-8 rounded-full flex items-center justify-center text-xs disabled:text-[#DAE0E5] ${
isEqual(setDate(selectedMonth, index + 1), selectedDate)
? "bg-[#49A1F5] text-white transition-colors"
: "enabled:hover:bg-[#E6ECF2]"
} ${
isToday(setDate(selectedMonth, index + 1))
? "border border-[#49A1F5]"
: ""
} `}
onClick={() => hadndleClick(setDate(selectedMonth, index + 1))}
>
{index + 1}
</button>
)
)}
</div>
</div>
);
}
export default Calendar;
+12
View File
@@ -1,13 +1,25 @@
/* eslint-disable @typescript-eslint/no-explicit-any */
import useModalStore from "../stores/useModalStore";
import Button from "./Button";
import PlusIcon from "./icons/PlusIcon";
import CreateScheduledSessionModal from "./modals/CreateScheduledSessionModal";
function EmptyCard() {
const { setModal } = useModalStore();
return (
<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
color="tertiary"
icon={<PlusIcon />}
className="group-hover:opacity-100 opacity-0"
handleClick={() =>
setModal(
<CreateScheduledSessionModal />
)
}
>
Добавить сеанс
</Button>
+1 -1
View File
@@ -4,7 +4,7 @@ interface LabelProps {
function Label({ value }: LabelProps) {
return (
<label className="text-xs text-[#697C8C]">{value}</label>
<p className="text-xs text-[#697C8C]">{value}</p>
);
}
@@ -1,4 +1,4 @@
function ChevronLeftIcon() {
function ChevronRightIcon() {
return (
<svg
width="24"
@@ -18,4 +18,4 @@ function ChevronLeftIcon() {
);
}
export default ChevronLeftIcon;
export default ChevronRightIcon;
@@ -0,0 +1,21 @@
function ChevronUpIcon() {
return (
<svg
width={24}
height={24}
viewBox="0 0 24 24"
fill="none"
xmlns="http://www.w3.org/2000/svg"
>
<path
d="M7 13L12 8L17 13"
stroke="currentColor"
strokeWidth={1.5}
strokeLinecap="round"
strokeLinejoin="round"
/>
</svg>
);
}
export default ChevronUpIcon;
@@ -181,7 +181,7 @@ function CreateScheduleModal({
<div className="w-[296px]">
<ChoiceChips
options={["Пн", "Вт", "Ср", "Чт", "Пт", "Сб", "Вс"]}
selected={["Сб", "Вс"]}
// selected={["Сб", "Вс"]}
onChange={(options) => setWeekends(options)}
/>
</div>
@@ -0,0 +1,90 @@
/* eslint-disable @typescript-eslint/no-explicit-any */
import { format } from "date-fns";
import Button from "../Button";
import CloseIcon from "../icons/CloseIcon";
import Input from "../Input";
import Label from "../Label";
import useModalStore from "../../stores/useModalStore";
function CreateScheduledSessionModal() {
const { setModal } = useModalStore();
return (
<form className="w-[600px] bg-white shadow-md rounded-lg">
<div className="p-2 pl-6 flex justify-between items-center border-b border-[#DAE0E5]">
<p className="font-semibold text-sm">Запланировать демонстрацию</p>
<span className="text-[#77828C]">
<Button
color="tertiary"
icon={<CloseIcon />}
onlyIcon
handleClick={() => setModal(null)}
/>
</span>
</div>
<div className="px-6 py-8 space-y-8 border-b border-[#DAE0E5]">
<div className="flex gap-4">
<div className="w-[240px]">
<p className="font-semibold text-sm">Демонстрация</p>
</div>
<div className="w-[296px]">
<div className="grid grid-cols-2 gap-4 text-xs py-1">
<p className="text-[#77828C]">Дата и время</p>
<p className="">{format(new Date(), "dd.MM.yyyy HH:mm")}</p>
</div>
<div className="grid grid-cols-2 gap-4 text-xs py-1">
<p className="text-[#77828C]">Длительность сеанса</p>
<p className="">30 мин.</p>
</div>
<div className="grid grid-cols-2 gap-4 text-xs py-1">
<p className="text-[#77828C]">Жилой комплекс</p>
<p className="">Название ЖК</p>
</div>
</div>
</div>
<hr className="bg-[#DAE0E5]" />
<div className="flex gap-4">
<div className="w-[240px]">
<div className="flex flex-col justify-between h-full">
<div className="space-y-2">
<p className="font-semibold text-sm">Укажите данные клиента*</p>
<p className="text-xs text-[#77828C]">
На указанный почтовый адрес будет отправлена информация
необходимая для подключения
</p>
</div>
<p className="text-xs font-semibold text-[#77828C]">
*не обязательно
</p>
</div>
</div>
<div className="w-[296px]">
<div className="space-y-4">
<div className="space-y-1">
<Label value="Email" />
<Input type="email" className="h-10" />
</div>
<div className="space-y-1">
<Label value="Телефон" />
<Input className="h-10" />
</div>
<div className="space-y-1">
<Label value="Имя" />
<Input className="h-10" />
</div>
</div>
</div>
</div>
</div>
<div className="px-6 py-3 flex gap-2 border-b border-[#DAE0E5]">
<Button handleClick={() => console.log("Click")}>Запланировать</Button>
<Button type="submit" color="secondary" handleClick={() => setModal(null)}>
Отмена
</Button>
</div>
</form>
);
}
export default CreateScheduledSessionModal;
+53 -39
View File
@@ -26,6 +26,8 @@ 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";
function DashboardPage() {
const [user, setAccessToken] = useAuthStore((state) => [
@@ -66,43 +68,48 @@ function DashboardPage() {
setSelectedDate(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]);
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);
}
if (!schedules) return;
// const schedule = schedules.find(schedule => schedule.startDate);
}, [selectedDate, selectedBuild, schedules]);
async function getCompany() {
@@ -402,7 +409,7 @@ function DashboardPage() {
return (
<div
key={index2}
className="w-[84px] h-[128px] flex justify-center items-center text-sm font-semibold bg-[#F0F1F2] border-r border-b border-[#DAE0E5]"
className="w-[84px] h-[164px] flex justify-center items-center text-sm font-semibold bg-[#F0F1F2] border-r border-b border-[#DAE0E5]"
>
{format(scheduledSession, "HH:mm")}
</div>
@@ -466,8 +473,15 @@ function DashboardPage() {
</button>
</div>
<div className="overflow-y-auto overflow-x-hidden">
<div className="p-4">
<p className="text-sm font-semibold opacity-50">Календарь</p>
<div className="p-4 space-y-2">
<div className="flex justify-between items-center">
<p className="text-sm font-semibold">Календарь</p>
<Button color="tertiary" icon={<ChevronUpIcon />} onlyIcon />
</div>
<Calendar
defaultValue={selectedDate}
onChange={(date) => setSelectedDate(date)}
/>
</div>
<div className="p-4 flex flex-col gap-4">
+1 -21
View File
@@ -221,29 +221,9 @@ scheduledSessionsRouter.put("/:id", async (req, res) => {
const scheduledSessionId = req.params.id;
let {
startAt,
endAt,
activeSessionId,
}: { startAt: string; endAt: string; activeSessionId: string } = req.body;
if (!startAt && !endAt) {
return res.json({
status: "error",
message: "Parameter `startAt` or `duration` is required, or both",
});
}
if (startAt && !isValid(parseISO(startAt))) {
return res.json({
status: "error",
message: "Invalid value of the `startAt` parameter",
});
}
// function isInteger(num: number) {
// return (num ^ 0) === num;
// }
const scheduledSession = await ScheduledSession.findById(scheduledSessionId);
if (!scheduledSession) {
@@ -256,7 +236,7 @@ scheduledSessionsRouter.put("/:id", async (req, res) => {
try {
const scheduledSession = await ScheduledSession.findByIdAndUpdate(
scheduledSessionId,
{ startAt, endAt, activeSessionId },
{ activeSessionId },
{ new: true }
);
+1 -3
View File
@@ -7,12 +7,11 @@ schedulesRouter.get(
"/companies/:companyId/builds/:buildId",
async (req, res) => {
const { companyId, buildId } = req.params;
const date = new Date();
// const date = new Date();
const schedules = await Schedule.find({
companyId,
buildId,
startDate: { $lte: date },
});
if (!schedules.length) {
@@ -24,7 +23,6 @@ schedulesRouter.get(
const schedule = await Schedule.findOne({
companyId,
buildId,
startDate: { $lte: date },
});
res.json(schedule);