upd
This commit is contained in:
@@ -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 ${
|
||||
|
||||
@@ -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;
|
||||
@@ -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>
|
||||
|
||||
@@ -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;
|
||||
@@ -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">
|
||||
|
||||
@@ -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 }
|
||||
);
|
||||
|
||||
|
||||
@@ -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);
|
||||
|
||||
Reference in New Issue
Block a user