This commit is contained in:
2024-06-11 21:18:54 +05:00
parent ac1a705187
commit e155066534
20 changed files with 520 additions and 113 deletions
+2 -2
View File
@@ -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
+1
View File
@@ -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",
+1 -1
View File
@@ -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]") ||
+4 -4
View File
@@ -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({
<div className="flex justify-between">
<div className="">
<p className="text-[10px] font-semibold text-[#77828C]">Клиент</p>
<p className="text-sm">{client.name}</p>
<p className="text-sm">{client?.name || "Имя не указано"}</p>
</div>
{manager ? (
<div className="bg-[#E6F2FE] rounded-full px-2 h-[20px] flex items-center gap-1">
@@ -85,10 +85,10 @@ function Card({
</div>
<div className="border-b border-[#DAE0E5] pb-2">
<p className="text-xs text-[#77828C] leading-tight">
{client.phone}
{client?.phone || "Телефон не указан"}
</p>
<p className="text-xs text-[#77828C] leading-tight">
{client.email}
{client?.email || "Email не указан"}
</p>
</div>
</div>
+3 -3
View File
@@ -29,9 +29,9 @@ interface Props {
function DatePicker({ defaultValue, startDate, onChange }: Props) {
const [value, setValue] = useState<Date>(
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<boolean>(false);
+11 -3
View File
@@ -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(
<CreateScheduledSessionModal />
<CreateScheduledSessionModal
buildId={buildId}
startAt={startAt}
duration={duration}
/>
)
}
>
+10 -5
View File
@@ -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<string | undefined>(defaultValue);
const inputRef = useMask({
mask: "+7 (___) ___-__-__",
replacement: { _: /\d/ },
});
const [value, setValue] = useState<string>(defaultValue || "");
useEffect(() => {
if (value && handleChange) {
handleChange(value);
}
handleChange && handleChange(value);
}, [value]);
return (
<input
ref={type === "tel" ? inputRef : undefined}
type={type}
placeholder={placeholder}
autoFocus={autoFocus}
+111
View File
@@ -0,0 +1,111 @@
import { useState } from "react";
import BurgerIcon from "./icons/BurgerIcon";
import { Transition } from "react-transition-group";
import BellIcon from "./icons/BellIcon";
import ParamsIcon from "./icons/ParamsIcon";
import WorkIcon from "./icons/WorkIcon";
import ExitIcon from "./icons/ExitIcon";
import useAuthStore from "../stores/useAuthStore";
function Menu() {
const [isShow, setIsShow] = useState<boolean>(false);
const { user, setAccessToken } = useAuthStore();
function logout() {
setAccessToken(null);
}
return (
<div className="">
<button
onClick={() => setIsShow((prev) => !prev)}
className={`p-3 transition-colors relative z-20 ${
isShow ? "bg-[#49A1F5] text-white" : "hover:bg-neutral-200"
}`}
>
<BurgerIcon />
</button>
<Transition in={isShow} timeout={150} mountOnEnter unmountOnExit>
{(state) => (
<div className={`transition-opacity ${state}`}>
<div className="absolute top-0 left-0 w-full h-full bg-black bg-opacity-10 z-10"></div>
<div className="absolute z-20 ml-2 mt-3.5">
<div className="relative">
<svg
width="12"
height="10"
viewBox="0 0 12 10"
fill="none"
xmlns="http://www.w3.org/2000/svg"
className="absolute -top-2.5 left-2.5 drop-shadow"
>
<path
d="M5.14251 1.42916C5.53091 0.781817 6.46909 0.781816 6.85749 1.42915L12 10H0L5.14251 1.42916Z"
fill="white"
/>
</svg>
<div className="relative w-[240px] bg-white rounded-lg shadow">
<div className="border-b border-[#DAE0E5] p-6 flex flex-col items-center justify-center gap-4">
<div className="rounded-full bg-[#E6ECF2] w-[88px] h-[88px] flex justify-center items-center">
<p className="text-2xl font-semibold ml-0.5 mt-0.5">{user?.name[0]}</p>
</div>
<div className="space-y-1 text-center">
<p className="text-sm">{user?.name}</p>
<p className="text-[#77828C] text-xs">{user?.username}</p>
</div>
</div>
<div className="border-b border-[#DAE0E5] py-3 space-y-2">
<button
disabled
className="text-sm flex items-center gap-2 px-4 w-full hover:bg-[#E6ECF2] transition-colors disabled:hover:bg-inherit disabled:opacity-50"
>
<span className="text-[#77828C] py-1">
<BellIcon />
</span>
Уведомления
</button>
<button
disabled
className="text-sm flex items-center gap-2 px-4 w-full hover:bg-[#E6ECF2] transition-colors disabled:hover:bg-inherit disabled:opacity-50"
>
<span className="text-[#77828C] py-1">
<ParamsIcon />
</span>
Настройки
</button>
</div>
<div className="border-b border-[#DAE0E5] py-2">
<button
disabled
className="text-sm flex items-center gap-2 px-4 w-full hover:bg-[#E6ECF2] transition-colors disabled:hover:bg-inherit disabled:opacity-50"
>
<span className="text-[#77828C] py-1">
<WorkIcon />
</span>
Компания
</button>
</div>
<div className="py-2">
<button
onClick={logout}
className="text-sm flex items-center gap-2 px-4 w-full hover:bg-[#E6ECF2] transition-colors disabled:hover:bg-inherit disabled:opacity-50"
>
<span className="text-[#77828C] py-1">
<ExitIcon />
</span>
Выход
</button>
</div>
</div>
</div>
</div>
</div>
)}
</Transition>
</div>
);
}
export default Menu;
+21
View File
@@ -0,0 +1,21 @@
function BellIcon() {
return (
<svg
width={24}
height={24}
viewBox="0 0 24 24"
fill="none"
xmlns="http://www.w3.org/2000/svg"
>
<path
d="M14.857 17.082L20 17C18.5094 15.3488 17.9967 11.9745 18 9.75V9.05V9C18 7.4087 17.3679 5.88258 16.2426 4.75736C15.1174 3.63214 13.5913 3 12 3C10.4087 3 8.88258 3.63214 7.75736 4.75736C6.63215 5.88258 6 7.4087 6 9V9.75C6.00302 11.9746 5.49099 15.431 4 17.082H9.143M14.857 17.082H9.143M14.857 17.082C15.0011 17.5319 15.0369 18.0094 14.9616 18.4757C14.8862 18.942 14.7018 19.384 14.4234 19.7656C14.1449 20.1472 13.7803 20.4576 13.3592 20.6716C12.9381 20.8856 12.4724 20.9972 12 20.9972C11.5276 20.9972 11.0619 20.8856 10.6408 20.6716C10.2197 20.4576 9.85508 20.1472 9.57662 19.7656C9.29817 19.384 9.11377 18.942 9.03842 18.4757C8.96307 18.0094 8.9989 17.5319 9.143 17.082"
stroke="currentColor"
strokeWidth={1.5}
strokeLinecap="round"
strokeLinejoin="round"
/>
</svg>
);
}
export default BellIcon;
+1 -1
View File
@@ -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"
/>
</svg>
);
+21
View File
@@ -0,0 +1,21 @@
function ExitIcon() {
return (
<svg
width={24}
height={24}
viewBox="0 0 24 24"
fill="none"
xmlns="http://www.w3.org/2000/svg"
>
<path
d="M15.5 20L7 20C5.89543 20 5 19.1046 5 18L5 6C5 4.89543 5.89543 4 7 4L15.5 4M19 12L8.50016 11.9997M19 12L15.5 15.5556M19 12L15.5 8.44444"
stroke="currentColor"
strokeWidth={1.5}
strokeLinecap="round"
strokeLinejoin="round"
/>
</svg>
);
}
export default ExitIcon;
@@ -0,0 +1,26 @@
function ParamsIcon() {
return (
<svg
width={24}
height={24}
viewBox="0 0 24 24"
fill="none"
xmlns="http://www.w3.org/2000/svg"
>
<path
d="M8 17C9.10457 17 10 16.1046 10 15C10 13.8954 9.10457 13 8 13M8 17C6.89543 17 6 16.1046 6 15C6 13.8954 6.89543 13 8 13M8 17V19M8 13V5"
stroke="currentColor"
strokeWidth={1.5}
strokeLinecap="round"
/>
<path
d="M16 7C17.1046 7 18 7.89543 18 9C18 10.1046 17.1046 11 16 11M16 7C14.8954 7 14 7.89543 14 9C14 10.1046 14.8954 11 16 11M16 7V5M16 11V19"
stroke="currentColor"
strokeWidth={1.5}
strokeLinecap="round"
/>
</svg>
);
}
export default ParamsIcon;
+26
View File
@@ -0,0 +1,26 @@
function WorkIcon() {
return (
<svg
width={24}
height={24}
viewBox="0 0 24 24"
fill="none"
xmlns="http://www.w3.org/2000/svg"
>
<path
d="M3.75 9C3.75 8.30964 4.30964 7.75 5 7.75H19C19.6904 7.75 20.25 8.30964 20.25 9V18C20.25 18.6904 19.6904 19.25 19 19.25H5C4.30964 19.25 3.75 18.6904 3.75 18V9Z"
stroke="currentColor"
strokeWidth={1.5}
/>
<path
d="M7.89482 6.90002C8.13696 5.99633 8.67053 5.19779 9.41276 4.62825C10.155 4.05871 11.0644 3.75 12 3.75C12.9356 3.75 13.845 4.05871 14.5872 4.62825C15.3295 5.19779 15.863 5.99633 16.1052 6.90002"
stroke="currentColor"
strokeWidth={1.5}
strokeLinecap="round"
strokeLinejoin="round"
/>
</svg>
);
}
export default WorkIcon;
@@ -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({
<div className="w-[296px] flex flex-col gap-3">
<div className="flex flex-col">
<Label value="Начало" />
<DatePicker
startDate={startDate}
onChange={(date) => (setDate(date), console.log(date))}
/>
{startDate && (
<DatePicker
defaultValue={addDays(startDate, 1)}
startDate={addDays(startDate, 1)}
onChange={(date) => (setDate(date), console.log(date))}
/>
)}
</div>
</div>
</div>
@@ -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<string>("");
const [phone, setPhone] = useState<string>("");
const [name, setName] = useState<string>("");
const [isLoading, setIsLoading] = useState<boolean>(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 (
<form className="w-[600px] bg-white shadow-md rounded-lg">
<form
className="w-[600px] bg-white shadow-md rounded-lg"
onSubmit={addSchesuledSession}
>
<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]">
@@ -31,16 +75,16 @@ function CreateScheduledSessionModal() {
<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>
<p className="">{format(startAt, "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>
<p className="">{duration} мин.</p>
</div>
<div className="grid grid-cols-2 gap-4 text-xs py-1">
{/* <div className="grid grid-cols-2 gap-4 text-xs py-1">
<p className="text-[#77828C]">Жилой комплекс</p>
<p className="">Название ЖК</p>
</div>
</div> */}
</div>
</div>
<hr className="bg-[#DAE0E5]" />
@@ -63,23 +107,39 @@ function CreateScheduledSessionModal() {
<div className="space-y-4">
<div className="space-y-1">
<Label value="Email" />
<Input type="email" className="h-10" />
<Input
type="email"
className="h-10"
defaultValue={email}
handleChange={(value) => setEmail(value)}
/>
</div>
<div className="space-y-1">
<Label value="Телефон" />
<Input className="h-10" />
<Input
type="tel"
className="h-10"
defaultValue={phone}
handleChange={(value) => setPhone(value)}
/>
</div>
<div className="space-y-1">
<Label value="Имя" />
<Input className="h-10" />
<Input
className="h-10"
defaultValue={name}
handleChange={(value) => setName(value)}
/>
</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 disabled={isLoading} type="submit">
Запланировать
</Button>
<Button color="secondary" handleClick={() => setModal(null)}>
Отмена
</Button>
</div>
+150 -47
View File
@@ -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<any[]>();
const [selectedBuild, setSelectedBuild] = useState<{ [key: string]: any }>();
const [schedules, setSchedules] = useState<ISchedule[]>();
const [duration, setDuration] = useState<number>();
const [scheduledSessions, setScheduledSessions] = useState<any[]>();
const [generatedScheduledSessions, setGeneratedScheduledSessions] =
useState<any[][]>();
useState<any[]>();
const [selectedDate, setSelectedDate] = useState(new Date());
const [dateTimes, setDateTimes] = useState<Date[]>();
const [currentTime, setCurrentTime] = useState<string>(
@@ -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 (
<div className="main h-screen flex">
<div className="left flex flex-col w-full">
<div className="flex bg-[#F0F1F2] ">
<button
onClick={() => alert("В разработке")}
className="p-3 transition-colors hover:bg-neutral-200"
>
<BurgerIcon />
</button>
<div className="flex bg-[#F0F1F2]">
<Menu />
{builds?.map((build) => (
<TabButton
key={build.id}
@@ -396,7 +458,56 @@ function DashboardPage() {
)}
</Transition>
{company &&
{generatedScheduledSessions?.map(
(generatedScheduledSession, index) => (
<div key={index} className="flex">
<div className="w-[84px] h-[164px] flex justify-center items-center text-sm font-semibold bg-white border-r border-b border-[#DAE0E5]">
<p>{format(generatedScheduledSession.time, "HH:mm")}</p>
</div>
<div className="flex">
{generatedScheduledSession.sessions.map(
(session: any, index2: number) => {
const selectedManager = selectedBuildManagers?.find(
(manager) => manager.id == session.userId
);
if (!_.isEmpty(session)) {
return (
<Card
key={index2}
companyId={company?.id}
buildId={selectedBuild?.id}
scheduledSessionId={session.id}
scheduleSessionStartAt={session.startAt}
client={session.client}
manager={selectedManager}
managers={selectedBuildManagers || []}
handleSelect={(scheduledSessionId, managerId) =>
updateScheduledSessionManager(
scheduledSessionId,
managerId
)
}
/>
);
} else {
return (
<EmptyCard
key={index2}
buildId={selectedBuild?.id}
startAt={generatedScheduledSession.time}
duration={duration!}
/>
);
}
}
)}
</div>
</div>
)
)}
{/* {company &&
selectedBuild &&
user &&
generatedScheduledSessions?.map(
@@ -449,20 +560,12 @@ function DashboardPage() {
</div>
);
}
)}
)} */}
</div>
</div>
<div className="right w-[384px] flex flex-col">
<div className="min-h-[48px] bg-[#F0F1F2] flex justify-between items-center">
<p className="text-xs font-semibold px-3 text-green-600">
{company?.name}
</p>
<p className="text-xs font-semibold px-3">{user?.username}</p>
<TabButton handleClick={logout} className="text-red-600">
Выйти
</TabButton>
</div>
<div className="min-h-[48px] bg-[#F0F1F2] flex justify-between items-center"></div>
<div className="flex gap-2 h-[48px] border-b border-[#DAE0E5]">
<button className="p-4 text-sm font-semibold">Параметры</button>
<button
+12
View File
@@ -238,6 +238,18 @@
resolved "https://registry.npmjs.org/@popperjs/core/-/core-2.11.8.tgz"
integrity sha512-P1st0aksCrn9sGZhp8GMYwBnQsbvAWsZAX44oXNNvLHGqAOcoVxmjZiohstwQ7SqKnbR47akdNi+uleWD8+g6A==
"@react-input/core@^1.0.12":
version "1.0.12"
resolved "https://registry.yarnpkg.com/@react-input/core/-/core-1.0.12.tgz#b76e2474ad544a02e476baa9184ee7ff788c3c3c"
integrity sha512-lZDQjphsJenWCD0mcflsyneLnb2a7bFAgAVzih9diEcUjKopcK+QOXhz0a0PHEp8k5LEKpYadjwyltAGOOhL2g==
"@react-input/mask@^1.2.5":
version "1.2.5"
resolved "https://registry.yarnpkg.com/@react-input/mask/-/mask-1.2.5.tgz#0ce9c48e36dfcb56705eda811b0143454e146e55"
integrity sha512-xouBATnitQqhgKLgu6/J2IZRu7kzqm/tFw2ToFnk6EkRzWs5dawlcF8asASMu7LCDrCqSkjwN+/sscEMG7IXog==
dependencies:
"@react-input/core" "^1.0.12"
"@remix-run/router@1.8.0":
version "1.8.0"
resolved "https://registry.npmjs.org/@remix-run/router/-/router-1.8.0.tgz"