upd
This commit is contained in:
@@ -1,5 +1,4 @@
|
||||
import { useMutation, useQueryClient } from "@tanstack/react-query";
|
||||
import api from "../utils/api";
|
||||
import { useQueryClient } from "@tanstack/react-query";
|
||||
import { IServer } from "../types/IServer";
|
||||
import useModalStore from "../stores/useModalStore";
|
||||
import CreateSessionModal from "./modals/CreateSessionModal";
|
||||
@@ -33,22 +32,13 @@ export default function DesktopCard({ server }: IDesktopCardProps) {
|
||||
// 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"] }),
|
||||
});
|
||||
|
||||
async function handleClickCreateSession() {
|
||||
setPosition("right");
|
||||
setModal(<CreateSessionModal servers={servers} targetServer={server} />);
|
||||
}
|
||||
|
||||
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
|
||||
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]',
|
||||
@@ -56,62 +46,62 @@ export default function DesktopCard({ server }: IDesktopCardProps) {
|
||||
)}
|
||||
>
|
||||
<NewButton
|
||||
variant='secondary'
|
||||
size='medium'
|
||||
className='absolute top-[0.347vw] right-[0.347vw] cursor-pointer flex items-center justify-center'
|
||||
variant="secondary"
|
||||
size="medium"
|
||||
className="absolute top-[0.347vw] right-[0.347vw] cursor-pointer flex items-center justify-center"
|
||||
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 />
|
||||
</span>
|
||||
</NewButton>
|
||||
</div>
|
||||
<div className='flex self-center justify-between items-start'>
|
||||
<div className='space-y-[0.278vw] text-center'>
|
||||
<p className='leading-none font-[500] text-[1.111vw] title-s'>
|
||||
<div className="flex self-center justify-between items-start">
|
||||
<div className="space-y-[0.278vw] text-center">
|
||||
<p className="leading-none font-[500] text-[1.111vw] title-s">
|
||||
{server.name}
|
||||
</p>
|
||||
<p className='caption-s text-[#7D7D7D]'>{server.location}</p>
|
||||
<p className="caption-s text-[#7D7D7D]">{server.location}</p>
|
||||
</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" ? (
|
||||
<div className='flex items-center gap-[0.278vw]'>
|
||||
<div className="flex items-center gap-[0.278vw]">
|
||||
<NewButton
|
||||
variant='primary'
|
||||
variant="primary"
|
||||
onClick={() => {
|
||||
setModal(<CurrentSessionModal server={server} />);
|
||||
}}
|
||||
className='flex gap-[0.556vw] items-center'
|
||||
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 />
|
||||
</span>
|
||||
<p className='button-s font-medium'>Идёт сеанс</p>
|
||||
<span className='w-[0.972vw] h-[0.972vw] flex items-center justify-center'>
|
||||
<p className="button-s font-medium">Идёт сеанс</p>
|
||||
<span className="w-[0.972vw] h-[0.972vw] flex items-center justify-center">
|
||||
<ChevronRightIcon />
|
||||
</span>
|
||||
</NewButton>
|
||||
</div>
|
||||
) : server.status === "offline" ? (
|
||||
<NewButton variant='critical'>
|
||||
<span className='text-[#FF4517] size-[0.972vw]'>
|
||||
<NewButton variant="critical">
|
||||
<span className="text-[#FF4517] size-[0.972vw]">
|
||||
<UnlinkIcon />
|
||||
</span>
|
||||
<span>Проверьте соединение</span>
|
||||
</NewButton>
|
||||
) : (
|
||||
<NewButton
|
||||
variant='cta'
|
||||
size='medium'
|
||||
className='self-center'
|
||||
variant="cta"
|
||||
size="medium"
|
||||
className="self-center"
|
||||
onClick={handleClickCreateSession}
|
||||
>
|
||||
<div className='flex gap-[0.556vw] items-center justify-center'>
|
||||
<span className='w-[0.972vw] h-[0.972vw] flex 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">
|
||||
<PlusIcon />
|
||||
</span>
|
||||
<p className='button-s'>Создать сеанс</p>
|
||||
<p className="button-s">Создать сеанс</p>
|
||||
</div>
|
||||
</NewButton>
|
||||
)}
|
||||
|
||||
@@ -10,7 +10,7 @@ import NewInput from "../NewInput.tsx";
|
||||
import StartSessionIcon from "../icons/StartSessionIcon.tsx";
|
||||
import NewButton from "../NewButton.tsx";
|
||||
import ProjectSelector from "../ProjectSelector.tsx";
|
||||
|
||||
import { useQueryClient, useMutation } from "@tanstack/react-query";
|
||||
interface Props {
|
||||
servers: IServer[] | undefined;
|
||||
targetServer: IServer | null;
|
||||
@@ -28,89 +28,99 @@ export default function CreateSessionModal({
|
||||
targetServer
|
||||
);
|
||||
const [selectedApp, setSelectedApp] = useState<IApp | null>(null);
|
||||
const queryClient = useQueryClient();
|
||||
|
||||
async function createClient() {
|
||||
return await api
|
||||
.post("clients", {
|
||||
json: {
|
||||
name,
|
||||
phone,
|
||||
email,
|
||||
},
|
||||
})
|
||||
.json<IClient>();
|
||||
}
|
||||
const { mutate: createClient } = useMutation({
|
||||
mutationFn: () => {
|
||||
return api
|
||||
.post("clients", {
|
||||
json: {
|
||||
name,
|
||||
phone,
|
||||
email,
|
||||
},
|
||||
})
|
||||
.json<IClient>();
|
||||
},
|
||||
});
|
||||
|
||||
async function createSession(clientId: string) {
|
||||
return await api
|
||||
.post("sessions", {
|
||||
json: {
|
||||
clientId,
|
||||
serverId: selectedServer?.id,
|
||||
appId: selectedApp?.id,
|
||||
},
|
||||
})
|
||||
.json<ISession>();
|
||||
}
|
||||
const { mutate: createSession } = useMutation({
|
||||
mutationFn: (clientId: string) => {
|
||||
return api
|
||||
.post("sessions", {
|
||||
json: {
|
||||
clientId,
|
||||
serverId: selectedServer?.id,
|
||||
appId: selectedApp?.id,
|
||||
},
|
||||
})
|
||||
.json<ISession>();
|
||||
},
|
||||
onSuccess: () => {
|
||||
queryClient.invalidateQueries({ queryKey: ["sessions"] });
|
||||
queryClient.invalidateQueries({ queryKey: ["servers"] });
|
||||
setModal(null);
|
||||
},
|
||||
});
|
||||
|
||||
async function handleClickCreateSession(e: React.FormEvent<HTMLFormElement>) {
|
||||
e.preventDefault();
|
||||
|
||||
if (!name || !phone || !selectedServer || !selectedApp) return;
|
||||
|
||||
try {
|
||||
const client = await createClient();
|
||||
await createSession(client.id);
|
||||
|
||||
setModal(null);
|
||||
} catch (error) {
|
||||
console.log(error);
|
||||
}
|
||||
createClient(undefined, {
|
||||
onSuccess: (client) => {
|
||||
createSession(client.id);
|
||||
},
|
||||
onError: (error) => {
|
||||
console.log(error);
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
const ref = useRef<HTMLFormElement>(null);
|
||||
|
||||
return (
|
||||
<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}
|
||||
ref={ref}
|
||||
>
|
||||
<div className='w-full h-[4.861vw] flex items-center justify-center'>
|
||||
<p className='title-s font-medium'>Новый сеанс</p>
|
||||
<div className="w-full h-[4.861vw] flex items-center justify-center">
|
||||
<p className="title-s font-medium">Новый сеанс</p>
|
||||
</div>
|
||||
<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="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">
|
||||
<TableSelector
|
||||
tables={servers || []}
|
||||
selectedTable={selectedServer}
|
||||
onSelect={setSelectedServer}
|
||||
/>
|
||||
<div className='flex flex-col gap-y-[0.833vw]'>
|
||||
<p className='title-s font-medium'>Укажите данные клиента</p>
|
||||
<div className='flex flex-col gap-y-[0.556vw]'>
|
||||
<div className="flex flex-col gap-y-[0.833vw]">
|
||||
<p className="title-s font-medium">Укажите данные клиента</p>
|
||||
<div className="flex flex-col gap-y-[0.556vw]">
|
||||
<NewInput
|
||||
value={phone}
|
||||
onChange={(e) => setPhone(e.target.value)}
|
||||
placeholder='Номер телефона'
|
||||
placeholder="Номер телефона"
|
||||
required
|
||||
/>
|
||||
<NewInput
|
||||
value={name}
|
||||
onChange={(e) => setName(e.target.value)}
|
||||
placeholder='Имя'
|
||||
placeholder="Имя"
|
||||
required
|
||||
/>
|
||||
<NewInput
|
||||
type='email'
|
||||
type="email"
|
||||
value={email}
|
||||
onChange={(e) => setEmail(e.target.value)}
|
||||
placeholder='Электронная почта'
|
||||
placeholder="Электронная почта"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
<div className='flex flex-col gap-y-[0.833vw]'>
|
||||
<p className='title-s font-medium'>Выберите параметры сеанса</p>
|
||||
<div className="flex flex-col gap-y-[0.833vw]">
|
||||
<p className="title-s font-medium">Выберите параметры сеанса</p>
|
||||
{selectedServer?.apps?.length && selectedServer?.apps?.length > 0 && (
|
||||
<ProjectSelector
|
||||
projects={selectedServer?.apps || []}
|
||||
@@ -120,16 +130,16 @@ export default function CreateSessionModal({
|
||||
)}
|
||||
</div>
|
||||
<NewButton
|
||||
type='submit'
|
||||
type="submit"
|
||||
disabled={
|
||||
!ref.current?.checkValidity() || !selectedServer || !selectedApp
|
||||
}
|
||||
variant='cta'
|
||||
size='large'
|
||||
className='sticky bottom-[1.111vw]'
|
||||
variant="cta"
|
||||
size="large"
|
||||
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='w-[0.694vw] h-[0.556vw]'>
|
||||
<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]">
|
||||
<StartSessionIcon />
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -2,55 +2,109 @@ import { IServer } from "../../types/IServer";
|
||||
import FlashIcon from "../icons/FlashIcon";
|
||||
import NewButton from "../NewButton";
|
||||
import ChevronRightIcon from "../icons/ChevronRightIcon";
|
||||
import { useMutation, useQuery, useQueryClient } from "@tanstack/react-query";
|
||||
import api from "../../utils/api";
|
||||
import useModalStore from "../../stores/useModalStore";
|
||||
import { ISession } from "../../types/ISession";
|
||||
|
||||
function CurrentSessionModal({ server }: { server: IServer }) {
|
||||
console.log(server.client?.name);
|
||||
const queryClient = useQueryClient();
|
||||
const { setModal } = useModalStore();
|
||||
|
||||
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"] }),
|
||||
});
|
||||
|
||||
const { data: currentServer } = useQuery({
|
||||
queryKey: ["sessions", server.sessions?.[0]?.id],
|
||||
queryFn: () =>
|
||||
api.get(`sessions/${server.sessions?.[0]?.id}`).json<ISession>(),
|
||||
});
|
||||
|
||||
console.log(currentServer);
|
||||
|
||||
return (
|
||||
<div className='w-[25vw] bg-[#FFFFFF] rounded-4xl px-[1.389vw]'>
|
||||
<h2 className='title-s font-medium text-center py-[1.806vw]'>
|
||||
<div className="w-[25vw] bg-[#FFFFFF] rounded-4xl px-[1.389vw] pb-[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="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]'>
|
||||
<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]'>
|
||||
<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 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>
|
||||
<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]'>
|
||||
<div className="flex gap-[0.556vw] items-center">
|
||||
{!server.client?.email && (
|
||||
<p className='caption-s font-medium text-[#7B60F3] whitespace-nowrap'>
|
||||
<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]'>
|
||||
<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 className="flex flex-col gap-[0.833vw]">
|
||||
<h2 className="title-s font-medium">Детали</h2>
|
||||
<div>
|
||||
<div className="flex gap-[0.556vw]">
|
||||
<p className="caption-s font-medium text-[#BDBDBD]">Менеджер:</p>
|
||||
<p className="caption-s font-medium">{server.owner?.fullname}</p>
|
||||
</div>
|
||||
<div className="flex gap-[0.556vw]">
|
||||
<p className="caption-s font-medium text-[#BDBDBD]">
|
||||
Сценарии проживания:
|
||||
</p>
|
||||
<p className="caption-s font-medium">Добавить в бэкенд</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div className="flex flex-col gap-[0.556vw]">
|
||||
<NewButton
|
||||
variant="critical"
|
||||
className="w-full"
|
||||
onClick={() => {
|
||||
endSession();
|
||||
setModal(null);
|
||||
}}
|
||||
>
|
||||
Завершить сеанс
|
||||
</NewButton>
|
||||
<NewButton
|
||||
variant="primary"
|
||||
className="w-full"
|
||||
onClick={() => setModal(null)}
|
||||
>
|
||||
Закрыть
|
||||
</NewButton>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -27,6 +27,7 @@ function EditTable({ table }: { table: IServer }) {
|
||||
setModal(null);
|
||||
},
|
||||
});
|
||||
|
||||
return (
|
||||
<div className='bg-[#F0F0F0] rounded-4xl w-[25vw] flex flex-col justify-center items-center'>
|
||||
<h3 className='title-s font-medium py-[1.806vw]'>Редатирование стола</h3>
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
import { IApp } from "./IApp";
|
||||
import { IClient } from "./IClient";
|
||||
import { IOwner } from "./IOwner";
|
||||
import { ISession } from "./ISession";
|
||||
|
||||
export interface IServer {
|
||||
@@ -12,4 +13,5 @@ export interface IServer {
|
||||
apps?: IApp[];
|
||||
status: "online" | "offline";
|
||||
client?: IClient;
|
||||
owner?: IOwner;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user