upd
This commit is contained in:
@@ -0,0 +1,42 @@
|
||||
import React from "react";
|
||||
import { clsx as cn } from "clsx";
|
||||
|
||||
interface ButtonProps extends React.ButtonHTMLAttributes<HTMLButtonElement> {
|
||||
children: React.ReactNode;
|
||||
variant?: "link" | "primary" | "secondary" | "tertiary";
|
||||
className?: string;
|
||||
size?: "small" | "medium" | "large";
|
||||
onlyIcon?: boolean;
|
||||
}
|
||||
|
||||
function Button({
|
||||
children,
|
||||
variant = "primary",
|
||||
size = "medium",
|
||||
onlyIcon,
|
||||
className,
|
||||
...props
|
||||
}: ButtonProps) {
|
||||
return (
|
||||
<button
|
||||
{...props}
|
||||
className={cn(
|
||||
"transition-all rounded-lg flex items-center justify-center",
|
||||
variant !== "link" && [
|
||||
size === "small" && (onlyIcon ? "p-2" : "px-3 py-2 gap-2"),
|
||||
size === "medium" && (onlyIcon ? "p-3.5" : "px-5 py-3.5 gap-3.5"),
|
||||
size === "large" && (onlyIcon ? "p-4" : "px-6 py-4 gap-4"),
|
||||
],
|
||||
variant === "link" && "text-sm text-black/50 w-fit",
|
||||
variant === "primary" && "bg-[#1E1E1E] text-white",
|
||||
variant === "secondary" && "bg-white",
|
||||
variant === "tertiary" && "bg-transparent text-[#767676] hover:bg-black/5",
|
||||
className
|
||||
)}
|
||||
>
|
||||
{children}
|
||||
</button>
|
||||
);
|
||||
}
|
||||
|
||||
export default Button;
|
||||
@@ -0,0 +1,72 @@
|
||||
import Button from "./Button";
|
||||
import SessionIcon from "./icons/SessionIcon";
|
||||
import Input from "./Input";
|
||||
import useQueryApps from "../queries/useQueryApps.ts";
|
||||
import useQueryServers from "../queries/useQueryServers.ts";
|
||||
import Select from "./Select.tsx";
|
||||
|
||||
export default function CreateSessionModal() {
|
||||
const { data: apps } = useQueryApps();
|
||||
|
||||
const { data: servers } = useQueryServers();
|
||||
|
||||
return (
|
||||
<div className="w-[27.222vw] h-full rounded-[0.833vw] bg-white p-[1.667vw] flex flex-col justify-between gap-[1.111vw]">
|
||||
<div className="gap-y-[1.111vw] flex flex-col justify-between">
|
||||
<div className="space-y-[0.556vw]">
|
||||
<div className="rounded-[0.556vw] w-fit p-[0.833vw] bg-[#2D68F60D]">
|
||||
<div className="min-w-[1.389vw] min-h-[1.389vw] text-[#2D68F6]">
|
||||
<SessionIcon />
|
||||
</div>
|
||||
</div>
|
||||
<p className="text-[1.389vw]">Создание сеанса</p>
|
||||
<p className="text-[0.833vw] text-black/20">
|
||||
Укажите данные клиента, выберите менеджера и стол
|
||||
</p>
|
||||
</div>
|
||||
<hr className="border-black/10" />
|
||||
<div className="flex justify-between items-center">
|
||||
<p className="text-[0.972vw]">
|
||||
Имя <span className="text-[#C6C6C699]">*</span>
|
||||
</p>
|
||||
<div className="outline outline-black/10 rounded-[0.556vw] w-[13.889vw]">
|
||||
<Input placeholder="Константин" required />
|
||||
</div>
|
||||
</div>
|
||||
<div className="flex justify-between items-center">
|
||||
<p className="text-[0.972vw]">
|
||||
Номер <span className="text-[#C6C6C699]">*</span>
|
||||
</p>
|
||||
<div className="outline outline-black/10 rounded-[0.556vw] w-[13.889vw]">
|
||||
<Input placeholder="+ 7 (999) 99 99 99" required type="tel" />
|
||||
</div>
|
||||
</div>
|
||||
<div className="flex justify-between items-center">
|
||||
<p className="text-[0.972vw]">Электронная почта</p>
|
||||
<div className="outline outline-black/10 rounded-[0.556vw] w-[13.889vw]">
|
||||
<Input placeholder="sample@mail.ru" type="email" />
|
||||
</div>
|
||||
</div>
|
||||
{apps && (
|
||||
<div className="flex justify-between items-center">
|
||||
<p className="text-[0.972vw]">Приложение</p>
|
||||
<div className="outline outline-black/10 rounded-[0.556vw] w-[13.889vw]">
|
||||
<Select
|
||||
options={apps.map(({ id, name }) => ({ id, value: name }))}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
<hr className="border-black/10" />
|
||||
</div>
|
||||
<div className="flex justify-between">
|
||||
<Button className="bg-[#F9F9F9] px-[2.222vw] py-[0.556vw] !rounded-[0.556vw]">
|
||||
<p className="text-black font-medium text-[0.972vw]">Отменить</p>
|
||||
</Button>
|
||||
<Button className="bg-[#2D68F6] px-[2.222vw] py-[0.556vw] !rounded-[0.556vw]">
|
||||
<p className="text-[0.972vw] font-medium">Запустить сеанс</p>
|
||||
</Button>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
@@ -0,0 +1,118 @@
|
||||
import { useMutation, useQueryClient } from "@tanstack/react-query";
|
||||
import Button from "./Button";
|
||||
import MoreIcon from "./icons/MoreIcon";
|
||||
import api from "../utils/api";
|
||||
import PlusIcon from "./icons/PlusIcon";
|
||||
import { IServer } from "../types/IServer";
|
||||
|
||||
interface IDesktopCardProps {
|
||||
server: IServer;
|
||||
}
|
||||
|
||||
export default function DesktopCard({ server }: IDesktopCardProps) {
|
||||
const queryClient = useQueryClient();
|
||||
|
||||
const { mutate: createSession } = useMutation({
|
||||
mutationFn: () =>
|
||||
api.post(`sessions`, {
|
||||
json: {
|
||||
serverId: server.id,
|
||||
clientId: "abcfd570-2fa8-4f55-957b-5007f84f8f96",
|
||||
appId: "b8a9995c-a799-4593-8f96-03942050cb21",
|
||||
},
|
||||
}),
|
||||
onMutate: () => queryClient.invalidateQueries({ queryKey: ["sessions"] }),
|
||||
});
|
||||
|
||||
const { mutate: endSession } = useMutation({
|
||||
mutationKey: ["sessions", server.sessions?.[0]?.id],
|
||||
mutationFn: () =>
|
||||
api.put(`sessions/${server.sessions?.[0]?.id}`, {
|
||||
json: { status: "ending" },
|
||||
}),
|
||||
onMutate: () => queryClient.invalidateQueries({ queryKey: ["sessions"] }),
|
||||
});
|
||||
|
||||
return (
|
||||
<div className="p-[1.111vw] rounded-[0.833vw] flex flex-col justify-between gap-[1.111vw] aspect-[272/149] w-[25.208vw] border border-[#00000014]">
|
||||
<div className="flex justify-between items-start">
|
||||
<div className="space-y-[0.278vw]">
|
||||
<p className="leading-none text-[1.111vw]">{server.name}</p>
|
||||
<p className="opacity-50 leading-none text-[0.694vw]">
|
||||
{server.location}
|
||||
</p>
|
||||
</div>
|
||||
<Button
|
||||
onlyIcon
|
||||
size="small"
|
||||
className="rounded-[0.278vw] bg-[#F5F5F5]"
|
||||
>
|
||||
<div className="min-w-[0.833vw] min-h-[0.833vw] text-black">
|
||||
<MoreIcon />
|
||||
</div>
|
||||
</Button>
|
||||
</div>
|
||||
<div className="flex justify-between items-center">
|
||||
<div className="space-y-[0.556vw]">
|
||||
<div className="px-[0.556vw] py-[0.278vw] flex gap-[0.278vw] items-center bg-[#108C330D] rounded-[0.278vw] w-fit">
|
||||
{server.sessions?.[0]?.status === "started" && (
|
||||
<div className="rounded-full w-[0.278vw] h-[0.278vw] bg-[#108C33]" />
|
||||
)}
|
||||
<p className="font-medium leading-none text-[#108C33] text-[0.556vw]">
|
||||
{server.sessions?.[0]?.status === "started"
|
||||
? "Сеанс запущен"
|
||||
: "Свободен"}
|
||||
</p>
|
||||
</div>
|
||||
{server.sessions?.[0]?.status === "started" && (
|
||||
<>
|
||||
<p className="leading-none">
|
||||
{server.sessions?.[0]?.client?.fullname}
|
||||
</p>
|
||||
<p className="opacity-40 leading-none">
|
||||
{server.sessions?.[0]?.app?.name}
|
||||
</p>
|
||||
</>
|
||||
)}
|
||||
</div>
|
||||
<p className="opacity-40">0 мин</p>
|
||||
</div>
|
||||
<div className="flex gap-[0.278vw]">
|
||||
{server.sessions?.[0]?.status === "started" ? (
|
||||
<div className="flex items-center gap-[0.278vw]">
|
||||
{/* <Button
|
||||
variant="secondary"
|
||||
className="py-[0.694vw] border rounded-lg border-[#2D68F6] w-full"
|
||||
>
|
||||
<p className="text-center leading-none font-medium text-[#2D68F6]">
|
||||
Подробнее о сеансе
|
||||
</p>
|
||||
</Button> */}
|
||||
<Button
|
||||
variant="secondary"
|
||||
className="py-[0.694vw] border rounded-lg border-[#DC0404] w-full"
|
||||
onClick={() => endSession()}
|
||||
>
|
||||
<p className="text-center leading-none font-medium text-[#DC0404]">
|
||||
Завершить сеанс
|
||||
</p>
|
||||
</Button>
|
||||
</div>
|
||||
) : (
|
||||
<Button
|
||||
variant="primary"
|
||||
className="bg-[#798FFF] w-full rounded-[0.556vw]"
|
||||
onClick={() => createSession()}
|
||||
>
|
||||
<div className="flex gap-1 items-center">
|
||||
<span className="w-[1.389vw] h-[1.389vw]">
|
||||
<PlusIcon />
|
||||
</span>
|
||||
<p className="font-medium leading-[115%]">Начать сеанс</p>
|
||||
</div>
|
||||
</Button>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
@@ -0,0 +1,35 @@
|
||||
import { useQueryClient } from "@tanstack/react-query";
|
||||
import ChevronDownIcon from "./icons/ChevronDownIcon";
|
||||
import { IUser } from "../types/IUser";
|
||||
import SearchInput from "./SearchInput";
|
||||
|
||||
function Header() {
|
||||
const queryClient = useQueryClient();
|
||||
const me = queryClient.getQueryData<IUser>(["me"]);
|
||||
|
||||
return (
|
||||
<div className="h-[66px] bg-white">
|
||||
<div className="w-[952px] mx-auto flex items-center justify-between h-full">
|
||||
<div className="flex items-center gap-5">
|
||||
<div className="w-[134px]">
|
||||
<img src="/logo-mate.svg" alt="logo" />
|
||||
</div>
|
||||
<SearchInput placeholder="Поиск по клиентам" />
|
||||
</div>
|
||||
<div className="flex items-center gap-2 cursor-pointer">
|
||||
<div className="flex flex-col gap-1.5 justify-between h-9">
|
||||
<p className="text-sm leading-none">{me?.fullname}</p>
|
||||
<p className="text-[10px] leading-none text-black/40">
|
||||
Старший менеджер
|
||||
</p>
|
||||
</div>
|
||||
<div className="text-[#767676]">
|
||||
<ChevronDownIcon />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
export default Header;
|
||||
@@ -0,0 +1,19 @@
|
||||
import React from "react";
|
||||
|
||||
interface InputProps extends React.InputHTMLAttributes<HTMLInputElement> {
|
||||
label?: string;
|
||||
}
|
||||
|
||||
function Input({ label, ...props }: InputProps) {
|
||||
return (
|
||||
<label className="space-y-2">
|
||||
{label && <p className="text-xs text-black/50">{label}</p>}
|
||||
<input
|
||||
{...props}
|
||||
className="bg-white rounded-lg px-5 py-3.5 outline-none ring-1 ring-transparent focus:ring-[#363636] transition-[ring-color] inline-block w-full"
|
||||
/>
|
||||
</label>
|
||||
);
|
||||
}
|
||||
|
||||
export default Input;
|
||||
@@ -0,0 +1,92 @@
|
||||
/* eslint-disable react-hooks/exhaustive-deps */
|
||||
import { useEffect, useRef } from "react";
|
||||
import useModalStore from "../stores/useModalStore";
|
||||
import { AnimatePresence, motion } from "motion/react";
|
||||
import CloseIcon from "./icons/CloseIcon";
|
||||
import Button from "./Button";
|
||||
|
||||
const duration = 300;
|
||||
|
||||
function ModalContainer() {
|
||||
const { modal, setModal, isOpen, setIsOpen } = useModalStore();
|
||||
const divRef = useRef<HTMLDivElement>(null);
|
||||
const buttonRef = useRef<HTMLDivElement>(null);
|
||||
const popoverRef = useRef<HTMLDivElement>(null);
|
||||
|
||||
function handleResize() {
|
||||
if (!popoverRef.current) return;
|
||||
|
||||
if (divRef.current!.clientHeight > popoverRef.current!.clientHeight) {
|
||||
buttonRef.current!.style.height = `${divRef.current!.clientHeight}px`;
|
||||
} else {
|
||||
buttonRef.current!.style.height = `100%`;
|
||||
}
|
||||
}
|
||||
|
||||
function handleKeydown(e: KeyboardEvent) {
|
||||
if (e.key !== "Escape") return;
|
||||
setIsOpen(false);
|
||||
}
|
||||
|
||||
useEffect(() => setIsOpen(!!modal), [modal]);
|
||||
|
||||
useEffect(() => {
|
||||
if (isOpen) return;
|
||||
|
||||
setTimeout(() => {
|
||||
setModal(null);
|
||||
}, duration);
|
||||
}, [isOpen]);
|
||||
|
||||
useEffect(() => {
|
||||
window.addEventListener("resize", handleResize);
|
||||
window.addEventListener("keydown", handleKeydown);
|
||||
|
||||
return () => {
|
||||
window.removeEventListener("resize", handleResize);
|
||||
window.removeEventListener("keydown", handleKeydown);
|
||||
};
|
||||
}, []);
|
||||
|
||||
return (
|
||||
<AnimatePresence>
|
||||
{isOpen && (
|
||||
<motion.div
|
||||
initial={{ opacity: 0 }}
|
||||
animate={{ opacity: 1 }}
|
||||
exit={{ opacity: 0 }}
|
||||
className={`h-full transition-opacity duration-300`}
|
||||
>
|
||||
<div
|
||||
ref={popoverRef}
|
||||
className="fixed top-0 left-0 w-full h-full bg-black/70 overflow-y-auto flex flex-col items-center justify-center"
|
||||
>
|
||||
<div className="max-h-full">
|
||||
<div ref={divRef} className="p-[0.972vw]">
|
||||
<div
|
||||
ref={buttonRef}
|
||||
className="absolute top-0 left-0 w-full h-full cursor-pointer"
|
||||
onClick={() => setIsOpen(false)}
|
||||
/>
|
||||
<div className="relative w-full">
|
||||
{modal}
|
||||
<Button
|
||||
onlyIcon
|
||||
className="absolute top-[1.667vw] right-[1.667vw] p-[0.556vw] !rounded-full bg-[#F9F9F9]"
|
||||
onClick={() => setIsOpen(false)}
|
||||
>
|
||||
<span className="w-[1.389vw] h-[1.389vw] text-black">
|
||||
<CloseIcon />
|
||||
</span>
|
||||
</Button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</motion.div>
|
||||
)}
|
||||
</AnimatePresence>
|
||||
);
|
||||
}
|
||||
|
||||
export default ModalContainer;
|
||||
@@ -0,0 +1,25 @@
|
||||
import React, { useState } from "react";
|
||||
import SearchIcon from "./icons/SearchIcon";
|
||||
|
||||
function SearchInput(props: React.InputHTMLAttributes<HTMLInputElement>) {
|
||||
const [value, setValue] = useState("");
|
||||
|
||||
return (
|
||||
<div className="relative">
|
||||
<input
|
||||
type="text"
|
||||
className="outline-none pl-2.5 pr-8 pt-[7px] pb-2 bg-[#F6F6F6] rounded-lg leading-none text-sm"
|
||||
value={value}
|
||||
onChange={(e) => setValue(e.target.value)}
|
||||
{...props}
|
||||
/>
|
||||
{!value && (
|
||||
<div className="text-[#858585] absolute top-0 right-0 p-2">
|
||||
<SearchIcon />
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
export default SearchInput;
|
||||
@@ -0,0 +1,28 @@
|
||||
interface IOption {
|
||||
id: string;
|
||||
value: string;
|
||||
}
|
||||
|
||||
interface ISelectProps<T extends IOption>
|
||||
extends React.SelectHTMLAttributes<HTMLSelectElement> {
|
||||
multiple?: boolean;
|
||||
options: T[];
|
||||
selected?: T;
|
||||
// onClose: () => void;
|
||||
}
|
||||
|
||||
export default function Select<T extends IOption>({
|
||||
multiple,
|
||||
options,
|
||||
selected,
|
||||
onClose,
|
||||
...props
|
||||
}: ISelectProps<T>) {
|
||||
return (
|
||||
<select {...props}>
|
||||
{options.map(({ id, value }) => (
|
||||
<option key={id}>{value}</option>
|
||||
))}
|
||||
</select>
|
||||
);
|
||||
}
|
||||
@@ -0,0 +1,45 @@
|
||||
import AddressBookIcon from "./icons/AddressBookIcon";
|
||||
import DisplayIcon from "./icons/DisplayIcon";
|
||||
import HomeIcon from "./icons/HomeIcon";
|
||||
import LibIcon from "./icons/LibIcon";
|
||||
import PeoplesIcon from "./icons/PeopleIcon";
|
||||
import SidebarButton from "./SidebarButton";
|
||||
|
||||
function Sidebar() {
|
||||
return (
|
||||
<div className="space-y-[0.278vw]">
|
||||
<SidebarButton active>
|
||||
<span className="w-[1.111vw] h-[1.111vw]">
|
||||
<HomeIcon />
|
||||
</span>{" "}
|
||||
Главная
|
||||
</SidebarButton>
|
||||
<SidebarButton>
|
||||
<span className="w-[1.111vw] h-[1.111vw]">
|
||||
<DisplayIcon />
|
||||
</span>{" "}
|
||||
Сеансы
|
||||
</SidebarButton>
|
||||
<SidebarButton>
|
||||
<span className="w-[1.111vw] h-[1.111vw]">
|
||||
<PeoplesIcon />
|
||||
</span>{" "}
|
||||
Менеджеры
|
||||
</SidebarButton>
|
||||
<SidebarButton>
|
||||
<span className="w-[1.111vw] h-[1.111vw]">
|
||||
<AddressBookIcon />
|
||||
</span>{" "}
|
||||
Клиенты
|
||||
</SidebarButton>
|
||||
<SidebarButton>
|
||||
<span className="w-[1.111vw] h-[1.111vw]">
|
||||
<LibIcon />
|
||||
</span>{" "}
|
||||
Контент
|
||||
</SidebarButton>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
export default Sidebar;
|
||||
@@ -0,0 +1,28 @@
|
||||
import React from "react";
|
||||
import { clsx as cn } from "clsx";
|
||||
|
||||
interface ButtonProps extends React.ButtonHTMLAttributes<HTMLButtonElement> {
|
||||
children: React.ReactNode;
|
||||
variant?: "link" | "primary" | "secondary" | "tertiary";
|
||||
className?: string;
|
||||
size?: "small" | "medium" | "large";
|
||||
onlyIcon?: boolean;
|
||||
active?: boolean;
|
||||
}
|
||||
|
||||
function SidebarButton({ children, className, active, ...props }: ButtonProps) {
|
||||
return (
|
||||
<button
|
||||
{...props}
|
||||
className={cn(
|
||||
"transition-all flex items-center gap-[0.833vw] px-[1.111vw] py-[0.833vw] text-[0.972vw] rounded-[0.556vw] hover:bg-[#7B60F3]/10 leading-[115%] font-medium tracking-tight w-full",
|
||||
active ? "bg-[#7B60F3]/10 text-[#7B60F3]" : "text-[#242424]",
|
||||
className
|
||||
)}
|
||||
>
|
||||
{children}
|
||||
</button>
|
||||
);
|
||||
}
|
||||
|
||||
export default SidebarButton;
|
||||
@@ -0,0 +1,22 @@
|
||||
function AddressBookIcon() {
|
||||
return (
|
||||
<svg viewBox="0 0 16 16" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<path
|
||||
d="M2.667 11.999v2.666h10.666V1.332H2.667v2.667M2 10h1.333M2 8h1.333M2 6h1.333"
|
||||
stroke="currentColor"
|
||||
strokeWidth={1.2}
|
||||
strokeLinecap="square"
|
||||
strokeLinejoin="round"
|
||||
/>
|
||||
<path
|
||||
d="M8 6.999a1.333 1.333 0 1 0 0-2.667 1.333 1.333 0 0 0 0 2.667Zm2.667 4.668a2.667 2.667 0 1 0-5.334 0"
|
||||
stroke="currentColor"
|
||||
strokeWidth={1.2}
|
||||
strokeLinecap="square"
|
||||
strokeLinejoin="round"
|
||||
/>
|
||||
</svg>
|
||||
);
|
||||
}
|
||||
|
||||
export default AddressBookIcon;
|
||||
@@ -0,0 +1,21 @@
|
||||
function ChevronDownIcon() {
|
||||
return (
|
||||
<svg
|
||||
width={20}
|
||||
height={20}
|
||||
viewBox="0 0 20 20"
|
||||
fill="none"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
>
|
||||
<path
|
||||
d="M5.833 8.333 10 12.5l4.167-4.167"
|
||||
stroke="currentColor"
|
||||
strokeWidth={1.5}
|
||||
strokeLinecap="round"
|
||||
strokeLinejoin="round"
|
||||
/>
|
||||
</svg>
|
||||
);
|
||||
}
|
||||
|
||||
export default ChevronDownIcon;
|
||||
@@ -0,0 +1,13 @@
|
||||
export default function CloseIcon() {
|
||||
return (
|
||||
<svg viewBox="0 0 20 20" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<path
|
||||
d="m10 10 4.167-4.166M10.001 10 5.834 5.834M10.001 10l4.166 4.167M10.001 10l-4.167 4.167"
|
||||
stroke="currentColor"
|
||||
strokeWidth={1.5}
|
||||
strokeLinecap="round"
|
||||
strokeLinejoin="round"
|
||||
/>
|
||||
</svg>
|
||||
);
|
||||
}
|
||||
@@ -0,0 +1,26 @@
|
||||
function CogIcon() {
|
||||
return (
|
||||
<svg
|
||||
width={16}
|
||||
height={16}
|
||||
viewBox="0 0 16 16"
|
||||
fill="none"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
>
|
||||
<path
|
||||
d="M6.095 14.39a6.67 6.67 0 0 1-2.899-1.768 2 2 0 0 0-1.727-3.28A6.7 6.7 0 0 1 1.639 6h.028a2 2 0 0 0 1.795-2.883 6.66 6.66 0 0 1 2.755-1.543 2 2 0 0 0 3.566 0 6.66 6.66 0 0 1 2.756 1.543A2 2 0 0 0 14.362 6a6.7 6.7 0 0 1 .17 3.343 2 2 0 0 0-1.727 3.28 6.67 6.67 0 0 1-2.9 1.767 2.001 2.001 0 0 0-3.81 0Z"
|
||||
stroke="currentColor"
|
||||
strokeWidth={1.2}
|
||||
strokeLinejoin="round"
|
||||
/>
|
||||
<path
|
||||
d="M8 10.333a2.333 2.333 0 1 0 0-4.666 2.333 2.333 0 0 0 0 4.666Z"
|
||||
stroke="currentColor"
|
||||
strokeWidth={1.2}
|
||||
strokeLinejoin="round"
|
||||
/>
|
||||
</svg>
|
||||
);
|
||||
}
|
||||
|
||||
export default CogIcon;
|
||||
@@ -0,0 +1,20 @@
|
||||
export default function DesktopIcon() {
|
||||
return (
|
||||
<svg
|
||||
width={20}
|
||||
height={20}
|
||||
viewBox="0 0 20 20"
|
||||
fill="none"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
>
|
||||
<path
|
||||
d="M17.083 2.5H2.917c-.69 0-1.25.56-1.25 1.25v10c0 .69.56 1.25 1.25 1.25h14.166c.69 0 1.25-.56 1.25-1.25v-10c0-.69-.56-1.25-1.25-1.25ZM10 15v2.917m-5.833 0h11.666"
|
||||
opacity={0.8}
|
||||
stroke="currentColor"
|
||||
strokeWidth={1.5}
|
||||
strokeLinecap="square"
|
||||
strokeLinejoin="round"
|
||||
/>
|
||||
</svg>
|
||||
);
|
||||
}
|
||||
@@ -0,0 +1,15 @@
|
||||
function DisplayIcon() {
|
||||
return (
|
||||
<svg viewBox="0 0 16 16" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<path
|
||||
d="M12.632 3H3.368c-.32 0-.578.26-.578.579V8.21c0 .32.259.58.578.58h9.264a.58.58 0 0 0 .579-.58V3.58a.58.58 0 0 0-.58-.579M3.368 13.999l1.416-1.737h6.304l1.544 1.736M8 8.79V14"
|
||||
stroke="currentColor"
|
||||
strokeWidth={1.2}
|
||||
strokeLinecap="round"
|
||||
strokeLinejoin="round"
|
||||
/>
|
||||
</svg>
|
||||
);
|
||||
}
|
||||
|
||||
export default DisplayIcon;
|
||||
@@ -0,0 +1,14 @@
|
||||
function HomeIcon() {
|
||||
return (
|
||||
<svg viewBox="0 0 16 16" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<path
|
||||
d="M13.333 6.645v5.022a1 1 0 0 1-1 1H10.6a1 1 0 0 1-1-1v-1.2a1 1 0 0 0-1-1H7.4a1 1 0 0 0-1 1v1.2a1 1 0 0 1-1 1H3.667a1 1 0 0 1-1-1V6.645a1 1 0 0 1 .385-.788l4.333-3.378a1 1 0 0 1 1.23 0l4.333 3.378a1 1 0 0 1 .385.788Z"
|
||||
stroke="currentColor"
|
||||
strokeWidth={1.2}
|
||||
strokeLinecap="square"
|
||||
/>
|
||||
</svg>
|
||||
);
|
||||
}
|
||||
|
||||
export default HomeIcon;
|
||||
@@ -0,0 +1,14 @@
|
||||
function LibIcon() {
|
||||
return (
|
||||
<svg viewBox="0 0 16 16" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<path
|
||||
d="M3.688 9.371h2.295c.38 0 .688.308.688.688v2.295c0 .38-.308.689-.688.689H3.688A.69.69 0 0 1 3 12.354V10.06c0-.38.308-.689.688-.689Zm5.829 0h2.295c.38 0 .688.308.688.688v2.295c0 .38-.308.689-.688.689H9.517a.69.69 0 0 1-.689-.689V10.06a.69.69 0 0 1 .689-.689Zm0-5.414h2.295c.38 0 .688.308.688.688V6.94c0 .38-.308.689-.688.689H9.517a.69.69 0 0 1-.689-.689V4.645c0-.38.309-.688.689-.688Zm-5.829 0h2.295c.38 0 .688.308.688.688V6.94c0 .38-.308.689-.688.689H3.688A.69.69 0 0 1 3 6.94V4.645c0-.38.308-.688.688-.688Z"
|
||||
stroke="currentColor"
|
||||
strokeWidth={1.2}
|
||||
strokeLinecap="round"
|
||||
/>
|
||||
</svg>
|
||||
);
|
||||
}
|
||||
|
||||
export default LibIcon;
|
||||
@@ -0,0 +1,32 @@
|
||||
export default function MoreIcon() {
|
||||
return (
|
||||
<svg
|
||||
width={16}
|
||||
height={16}
|
||||
viewBox="0 0 16 16"
|
||||
fill="none"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
>
|
||||
<circle
|
||||
cx={1}
|
||||
cy={1}
|
||||
transform="matrix(1 0 0 -1 2.333 9)"
|
||||
fill="currentColor"
|
||||
r={1}
|
||||
/>
|
||||
<circle
|
||||
cx={1}
|
||||
cy={1}
|
||||
r={1}
|
||||
transform="matrix(1 0 0 -1 7 9)"
|
||||
fill="currentColor"
|
||||
/>
|
||||
<circle
|
||||
cx={1}
|
||||
cy={1}
|
||||
r={1}
|
||||
transform="matrix(1 0 0 -1 11.667 9)"
|
||||
fill="currentColor"
|
||||
/>
|
||||
</svg> )
|
||||
}
|
||||
@@ -0,0 +1,15 @@
|
||||
function PeopleIcon() {
|
||||
return (
|
||||
<svg viewBox="0 0 16 16" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<path
|
||||
d="M6.611 6.889a1.944 1.944 0 1 0 0-3.889 1.944 1.944 0 0 0 0 3.889Zm3.779-3.612a1.943 1.943 0 0 1 0 3.334m-7.945 6.054v.333h8.333v-.333c0-1.245 0-1.867-.242-2.342a2.22 2.22 0 0 0-.972-.971c-.475-.243-1.097-.243-2.342-.243H6c-1.244 0-1.867 0-2.342.243a2.22 2.22 0 0 0-.971.97c-.242.476-.242 1.098-.242 2.343Zm11.11.333v-.333c0-1.245 0-1.867-.242-2.342a2.22 2.22 0 0 0-.971-.971"
|
||||
stroke="currentColor"
|
||||
strokeWidth={1.2}
|
||||
strokeLinecap="square"
|
||||
strokeLinejoin="round"
|
||||
/>
|
||||
</svg>
|
||||
);
|
||||
}
|
||||
|
||||
export default PeopleIcon;
|
||||
@@ -0,0 +1,17 @@
|
||||
export default function PlusIcon() {
|
||||
return (
|
||||
<svg
|
||||
viewBox="0 0 17 16"
|
||||
fill="none"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
>
|
||||
<path
|
||||
d="M8.5 8h4m-4 0V4m0 4v4m0-4h-4"
|
||||
stroke="currentColor"
|
||||
strokeWidth={1.5}
|
||||
strokeLinecap="round"
|
||||
strokeLinejoin="round"
|
||||
/>
|
||||
</svg>
|
||||
);
|
||||
}
|
||||
@@ -0,0 +1,20 @@
|
||||
function SearchIcon() {
|
||||
return (
|
||||
<svg
|
||||
width={16}
|
||||
height={16}
|
||||
viewBox="0 0 16 16"
|
||||
fill="none"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
>
|
||||
<path
|
||||
d="M7 12.667A5.667 5.667 0 1 0 7 1.333a5.667 5.667 0 0 0 0 11.334Zm4.074-1.593 2.828 2.828"
|
||||
stroke="currentColor"
|
||||
strokeWidth={1.2}
|
||||
strokeLinejoin="round"
|
||||
/>
|
||||
</svg>
|
||||
);
|
||||
}
|
||||
|
||||
export default SearchIcon;
|
||||
@@ -0,0 +1,72 @@
|
||||
import Button from "../Button.tsx";
|
||||
import SessionIcon from "../icons/SessionIcon.tsx";
|
||||
import Input from "../Input.tsx";
|
||||
import useQueryApps from "../../queries/useQueryApps.ts";
|
||||
import useQueryServers from "../../queries/useQueryServers.ts";
|
||||
import Select from "../Select.tsx";
|
||||
import DisplayIcon from "../icons/DisplayIcon.tsx";
|
||||
|
||||
export default function CreateSessionModal() {
|
||||
const { data: apps } = useQueryApps();
|
||||
const { data: servers } = useQueryServers();
|
||||
|
||||
return (
|
||||
<div className="w-[34.375vw] h-full rounded-[0.833vw] bg-white p-[1.667vw] flex flex-col justify-between gap-[1.111vw]">
|
||||
<div className="gap-y-[1.111vw] flex flex-col justify-between">
|
||||
<div className="space-y-[0.556vw]">
|
||||
<div className="p-[0.833vw] ring-1 w-fit rounded-[0.556vw]">
|
||||
<div className="w-[1.389vw] h-[1.389vw]">
|
||||
<DisplayIcon />
|
||||
</div>
|
||||
</div>
|
||||
<p className="text-[1.389vw]">Создание сеанса</p>
|
||||
<p className="text-[0.833vw] text-black/20">
|
||||
Укажите данные клиента, выберите менеджера и стол
|
||||
</p>
|
||||
</div>
|
||||
<hr className="border-black/10" />
|
||||
<div className="flex justify-between items-center">
|
||||
<p className="text-[0.972vw]">
|
||||
Имя <span className="text-[#C6C6C699]">*</span>
|
||||
</p>
|
||||
<div className="outline outline-black/10 rounded-[0.556vw] w-[13.889vw]">
|
||||
<Input placeholder="Константин" required />
|
||||
</div>
|
||||
</div>
|
||||
<div className="flex justify-between items-center">
|
||||
<p className="text-[0.972vw]">
|
||||
Номер <span className="text-[#C6C6C699]">*</span>
|
||||
</p>
|
||||
<div className="outline outline-black/10 rounded-[0.556vw] w-[13.889vw]">
|
||||
<Input placeholder="+ 7 (999) 99 99 99" required type="tel" />
|
||||
</div>
|
||||
</div>
|
||||
<div className="flex justify-between items-center">
|
||||
<p className="text-[0.972vw]">Электронная почта</p>
|
||||
<div className="outline outline-black/10 rounded-[0.556vw] w-[13.889vw]">
|
||||
<Input placeholder="sample@mail.ru" type="email" />
|
||||
</div>
|
||||
</div>
|
||||
{apps && (
|
||||
<div className="flex justify-between items-center">
|
||||
<p className="text-[0.972vw]">Приложение</p>
|
||||
<div className="outline outline-black/10 rounded-[0.556vw] w-[13.889vw]">
|
||||
<Select
|
||||
options={apps.map(({ id, name }) => ({ id, value: name }))}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
<hr className="border-black/10" />
|
||||
</div>
|
||||
<div className="flex justify-between">
|
||||
<Button className="bg-[#F9F9F9] px-[2.222vw] py-[0.556vw] !rounded-[0.556vw]">
|
||||
<p className="text-black font-medium text-[0.972vw]">Отменить</p>
|
||||
</Button>
|
||||
<Button className="bg-[#2D68F6] px-[2.222vw] py-[0.556vw] !rounded-[0.556vw]">
|
||||
<p className="text-[0.972vw] font-medium">Запустить сеанс</p>
|
||||
</Button>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
Reference in New Issue
Block a user