This commit is contained in:
2025-06-04 15:49:56 +05:00
9 changed files with 158 additions and 56 deletions
Binary file not shown.

After

Width:  |  Height:  |  Size: 3.3 KiB

+31 -29
View File
@@ -6,12 +6,12 @@ import CreateSessionModal from "./modals/CreateSessionModal";
import NewButton from "./NewButton"; import NewButton from "./NewButton";
import FlashIcon from "./icons/FlashIcon"; import FlashIcon from "./icons/FlashIcon";
import CogIcon from "./icons/CogIcon"; import CogIcon from "./icons/CogIcon";
import ChevronLeftIcon from "./icons/ChevronLeftIcon";
import PlusIcon from "./icons/PlusIcon"; import PlusIcon from "./icons/PlusIcon";
import EditTable from "./modals/EditTable"; import EditTable from "./modals/EditTableModal";
import UnlinkIcon from "./icons/UnlinkIcon"; import UnlinkIcon from "./icons/UnlinkIcon";
import clsx from "clsx"; import clsx from "clsx";
import ChevronRightIcon from "./icons/ChevronRightIcon";
import CurrentSessionModal from "./modals/CurrentSessionModal";
interface IDesktopCardProps { interface IDesktopCardProps {
server: IServer; server: IServer;
} }
@@ -48,7 +48,7 @@ export default function DesktopCard({ server }: IDesktopCardProps) {
} }
return ( return (
<div className="flex flex-col gap-[0.833vw] aspect-[300/211] w-[20.833vw] h-[14.653vw]"> <div className='flex flex-col gap-[0.833vw] aspect-[300/211] w-[20.833vw] h-[14.653vw]'>
<div <div
className={clsx( className={clsx(
'relative w-full h-[8.056vw] aspect-[300/116] bg-[#F6F6F6] rounded-3xl bg-[url("/images/Table.png")] bg-no-repeat bg-[top_1.111vw_left_50%] bg-[length:8.125vw]', 'relative w-full h-[8.056vw] aspect-[300/116] bg-[#F6F6F6] rounded-3xl bg-[url("/images/Table.png")] bg-no-repeat bg-[top_1.111vw_left_50%] bg-[length:8.125vw]',
@@ -56,60 +56,62 @@ export default function DesktopCard({ server }: IDesktopCardProps) {
)} )}
> >
<NewButton <NewButton
variant="secondary" variant='secondary'
size="medium" size='medium'
className="absolute top-[0.347vw] right-[0.347vw] cursor-pointer flex items-center justify-center" className='absolute top-[0.347vw] right-[0.347vw] cursor-pointer flex items-center justify-center'
onClick={() => setModal(<EditTable table={server} />)} onClick={() => setModal(<EditTable table={server} />)}
> >
<span className="text-[#7D7D7D] w-[0.972vw] h-[0.972vw]"> <span className='text-[#7D7D7D] w-[0.972vw] h-[0.972vw]'>
<CogIcon /> <CogIcon />
</span> </span>
</NewButton> </NewButton>
</div> </div>
<div className="flex self-center justify-between items-start"> <div className='flex self-center justify-between items-start'>
<div className="space-y-[0.278vw] text-center"> <div className='space-y-[0.278vw] text-center'>
<p className="leading-none font-[500] text-[1.111vw] title-s"> <p className='leading-none font-[500] text-[1.111vw] title-s'>
{server.name} {server.name}
</p> </p>
<p className="caption-s text-[#7D7D7D]">{server.location}</p> <p className='caption-s text-[#7D7D7D]'>{server.location}</p>
</div> </div>
</div> </div>
<div className="flex gap-[0.278vw] justify-center -mt-[0.278vw]"> <div className='flex gap-[0.278vw] justify-center -mt-[0.278vw]'>
{server.sessions?.[0]?.status === "started" ? ( {server.sessions?.[0]?.status === "started" ? (
<div className="flex items-center gap-[0.278vw]"> <div className='flex items-center gap-[0.278vw]'>
<NewButton <NewButton
variant="primary" variant='primary'
onClick={() => endSession()} onClick={() => {
className="flex gap-[0.556vw] items-center" setModal(<CurrentSessionModal server={server} />);
}}
className='flex gap-[0.556vw] items-center'
> >
<span className="w-[0.972vw] h-[0.972vw] flex items-center justify-center"> <span className='w-[0.972vw] h-[0.972vw] flex items-center justify-center'>
<FlashIcon /> <FlashIcon />
</span> </span>
<p className="button-s font-medium">Идёт сеанс</p> <p className='button-s font-medium'>Идёт сеанс</p>
<span className="w-[0.972vw] h-[0.972vw] flex items-center justify-center"> <span className='w-[0.972vw] h-[0.972vw] flex items-center justify-center'>
<ChevronLeftIcon /> <ChevronRightIcon />
</span> </span>
</NewButton> </NewButton>
</div> </div>
) : server.status === "offline" ? ( ) : server.status === "offline" ? (
<NewButton variant="critical"> <NewButton variant='critical'>
<span className="text-[#FF4517] size-[0.972vw]"> <span className='text-[#FF4517] size-[0.972vw]'>
<UnlinkIcon /> <UnlinkIcon />
</span> </span>
<span>Проверьте соединение</span> <span>Проверьте соединение</span>
</NewButton> </NewButton>
) : ( ) : (
<NewButton <NewButton
variant="cta" variant='cta'
size="medium" size='medium'
className="self-center" className='self-center'
onClick={handleClickCreateSession} onClick={handleClickCreateSession}
> >
<div className="flex gap-[0.556vw] items-center justify-center"> <div className='flex gap-[0.556vw] items-center justify-center'>
<span className="w-[0.972vw] h-[0.972vw] flex items-center justify-center"> <span className='w-[0.972vw] h-[0.972vw] flex items-center justify-center'>
<PlusIcon /> <PlusIcon />
</span> </span>
<p className="button-s">Создать сеанс</p> <p className='button-s'>Создать сеанс</p>
</div> </div>
</NewButton> </NewButton>
)} )}
+21
View File
@@ -0,0 +1,21 @@
function ChevronRightIcon() {
return (
<svg
width={20}
height={20}
viewBox='0 0 20 20'
fill='none'
xmlns='http://www.w3.org/2000/svg'
>
<path
d='m8.75 5 5 5-5 5'
stroke='currentColor'
strokeWidth={1.2}
strokeLinecap='round'
strokeLinejoin='round'
/>
</svg>
);
}
export default ChevronRightIcon;
+20 -20
View File
@@ -72,45 +72,45 @@ export default function CreateSessionModal({
return ( return (
<form <form
className="rounded-[2.222vw] w-[25vw] min-h-[calc(100dvh-2.222vw)] bg-[#F0F0F0] flex flex-col overflow-hidden" className='rounded-[2.222vw] w-[25vw] min-h-[calc(100dvh-2.222vw)] bg-[#F0F0F0] flex flex-col overflow-hidden'
onSubmit={handleClickCreateSession} onSubmit={handleClickCreateSession}
ref={ref} ref={ref}
> >
<div className="w-full h-[4.861vw] flex items-center justify-center"> <div className='w-full h-[4.861vw] flex items-center justify-center'>
<p className="title-s font-medium">Новый сеанс</p> <p className='title-s font-medium'>Новый сеанс</p>
</div> </div>
<div className="w-full h-[6.944vw] bg-[url(/images/Table.png)] bg-no-repeat bg-top bg-[length:9.306vw]" /> <div className='w-full h-[6.944vw] bg-[url(/images/Table.png)] bg-no-repeat bg-top bg-[length:9.306vw]' />
<div className="bg-white rounded-t-[2.222vw] p-[1.389vw] flex flex-col gap-y-[1.667vw] flex-1 overflow-y-auto"> <div className='bg-white rounded-t-[2.222vw] p-[1.389vw] flex flex-col gap-y-[1.667vw] flex-1 overflow-y-auto'>
<TableSelector <TableSelector
tables={servers || []} tables={servers || []}
selectedTable={selectedServer} selectedTable={selectedServer}
onSelect={setSelectedServer} onSelect={setSelectedServer}
/> />
<div className="flex flex-col gap-y-[0.833vw]"> <div className='flex flex-col gap-y-[0.833vw]'>
<p className="title-s font-medium">Укажите данные клиента</p> <p className='title-s font-medium'>Укажите данные клиента</p>
<div className="flex flex-col gap-y-[0.556vw]"> <div className='flex flex-col gap-y-[0.556vw]'>
<NewInput <NewInput
value={phone} value={phone}
onChange={(e) => setPhone(e.target.value)} onChange={(e) => setPhone(e.target.value)}
placeholder="Номер телефона" placeholder='Номер телефона'
required required
/> />
<NewInput <NewInput
value={name} value={name}
onChange={(e) => setName(e.target.value)} onChange={(e) => setName(e.target.value)}
placeholder="Имя" placeholder='Имя'
required required
/> />
<NewInput <NewInput
type="email" type='email'
value={email} value={email}
onChange={(e) => setEmail(e.target.value)} onChange={(e) => setEmail(e.target.value)}
placeholder="Электронная почта" placeholder='Электронная почта'
/> />
</div> </div>
</div> </div>
<div className="flex flex-col gap-y-[0.833vw]"> <div className='flex flex-col gap-y-[0.833vw]'>
<p className="title-s font-medium">Выберите параметры сеанса</p> <p className='title-s font-medium'>Выберите параметры сеанса</p>
{selectedServer?.apps?.length && selectedServer?.apps?.length > 0 && ( {selectedServer?.apps?.length && selectedServer?.apps?.length > 0 && (
<ProjectSelector <ProjectSelector
projects={selectedServer?.apps || []} projects={selectedServer?.apps || []}
@@ -120,16 +120,16 @@ export default function CreateSessionModal({
)} )}
</div> </div>
<NewButton <NewButton
type="submit" type='submit'
disabled={ disabled={
!ref.current?.checkValidity() || !selectedServer || !selectedApp !ref.current?.checkValidity() || !selectedServer || !selectedApp
} }
variant="cta" variant='cta'
size="large" size='large'
className="sticky bottom-[1.111vw]" className='sticky bottom-[1.111vw]'
> >
<div className="rounded-full bg-[#9184F6] in-disabled:bg-transparent px-[0.278vw] py-[0.208vw] size-[1.111vw]"> <div className='rounded-full bg-[#9184F6] in-disabled:bg-transparent px-[0.278vw] py-[0.208vw] size-[1.111vw]'>
<div className="w-[0.694vw] h-[0.556vw]"> <div className='w-[0.694vw] h-[0.556vw]'>
<StartSessionIcon /> <StartSessionIcon />
</div> </div>
</div> </div>
@@ -0,0 +1,60 @@
import { IServer } from "../../types/IServer";
import FlashIcon from "../icons/FlashIcon";
import NewButton from "../NewButton";
import ChevronRightIcon from "../icons/ChevronRightIcon";
function CurrentSessionModal({ server }: { server: IServer }) {
console.log(server.client?.name);
return (
<div className='w-[25vw] bg-[#FFFFFF] rounded-4xl px-[1.389vw]'>
<h2 className='title-s font-medium text-center py-[1.806vw]'>
Текущий сеанс
</h2>
<div className='flex flex-col gap-[1.667vw]'>
<div className='py-[1.389vw] flex gap-[0.833vw] border border-b-[#F6F6F6] border-t-0 border-r-0 border-l-0'>
<div className='size-[4.444vw] bg-[#F6F6F6] rounded-xl bg-[url("/images/super-table.png")] bg-no-repeat bg-[length:2.222vw] bg-center'></div>
<div className='flex flex-col gap-[0.278vw] self-center '>
<div className='flex gap-[0.278vw]'>
<p className='title-s font-medium'>{server.name}</p>
<p className='flex justify-center items-center gap-[0.139vw] caption-s font-medium text-[#7B60F3]'>
<FlashIcon />
Сеанс идёт 24:05
</p>
</div>
<div>
<p className='caption-s font-medium text-[#BDBDBD]'>
{server.location}
</p>
</div>
</div>
</div>
<div className='flex flex-col gap-[0.833vw]'>
<h2 className='title-s font-medium'>Параметры сеанса</h2>
<div>
<NewButton variant='secondary' className='w-full'>
<div className='flex flex-col gap-[0.278vw] w-full text-left h-[2.222vw]'>
<p className='caption-s font-medium text-[#BDBDBD]'>Клиент</p>
<p className='text-s font-medium'>{server.client?.name}</p>
</div>
<div className='flex gap-[0.556vw] items-center w-[7.639vw]'>
{!server.client?.email && (
<p className='caption-s font-medium text-[#7B60F3] whitespace-nowrap'>
Добавьте email
</p>
)}
<span className='w-[1.389vw] h-[1.389vw] flex items-center justify-center text-[#7B60F3]'>
<ChevronRightIcon />
</span>
</div>
</NewButton>
</div>
</div>
<div>
<h2 className='title-s font-medium'>Детали</h2>
</div>
</div>
</div>
);
}
export default CurrentSessionModal;
+20 -6
View File
@@ -6,6 +6,8 @@ import DesktopCard from "../components/DesktopCard";
import Badge from "../components/Badge"; import Badge from "../components/Badge";
import { ISession } from "../types/ISession"; import { ISession } from "../types/ISession";
import SessionCard from "../components/SessionCard"; import SessionCard from "../components/SessionCard";
import NewButton from "../components/NewButton";
import ChevronRightIcon from "../components/icons/ChevronRightIcon";
function DashboardPage() { function DashboardPage() {
const { data: me } = useQuery({ const { data: me } = useQuery({
@@ -58,12 +60,24 @@ function DashboardPage() {
<div className='w-full'> <div className='w-full'>
<div className='flex flex-col gap-[1.667vw]'> <div className='flex flex-col gap-[1.667vw]'>
<h1 className='title-l font-[500] '>Последние сеансы</h1> <h1 className='title-l font-[500] '>Последние сеансы</h1>
<div className='flex flex-col gap-[0.278vw]'> <div className='w-full flex flex-col gap-[0.833vw]'>
{sessions <div className='flex flex-col gap-[0.278vw]'>
?.filter((session) => session.status === "ended") {sessions
.map((session) => ( ?.filter((session) => session.status === "ended")
<SessionCard key={session.id} session={session} /> .map((session) => (
))} <SessionCard key={session.id} session={session} />
))}
</div>
<NewButton
variant='primary'
size='large'
className='flex gap-[0.556vw] items-center justify-center'
>
<p>Смотреть всё</p>
<span className='w-[1.111vw] h-[1.111vw] flex items-center justify-center'>
<ChevronRightIcon />
</span>
</NewButton>
</div> </div>
</div> </div>
</div> </div>
+4 -1
View File
@@ -1,7 +1,10 @@
export interface IClient { export interface IClient {
id: string; id: string;
fullname: string; name: string;
email: string; email: string;
phone: string; phone: string;
companyId: string; companyId: string;
createdAt: string;
ownerId: string;
updatedAt: string;
} }
+2
View File
@@ -1,4 +1,5 @@
import { IApp } from "./IApp"; import { IApp } from "./IApp";
import { IClient } from "./IClient";
import { ISession } from "./ISession"; import { ISession } from "./ISession";
export interface IServer { export interface IServer {
@@ -10,4 +11,5 @@ export interface IServer {
sessions?: ISession[]; sessions?: ISession[];
apps?: IApp[]; apps?: IApp[];
status: "online" | "offline"; status: "online" | "offline";
client?: IClient;
} }