301 lines
9.5 KiB
TypeScript
301 lines
9.5 KiB
TypeScript
import { Server } from "../../types/Server.ts";
|
||
import { useEffect, useRef, useState } from "react";
|
||
import { App } from "../../types/App.ts";
|
||
import api from "../../utils/api.ts";
|
||
import { Session } from "../../types/Session.ts";
|
||
import { Client } from "../../types/Client.ts";
|
||
import useModalStore from "../../stores/useModalStore.ts";
|
||
import TableSelector from "../TableSelector.tsx";
|
||
import Input from "../Input.tsx";
|
||
import StartSessionIcon from "../icons/StartSessionIcon.tsx";
|
||
import Button from "../Button.tsx";
|
||
import ProjectSelector from "../ProjectSelector.tsx";
|
||
import { useQueryClient, useMutation, useQuery } from "@tanstack/react-query";
|
||
import { useDebounce } from "@uidotdev/usehooks";
|
||
import { AnimatePresence, motion } from "motion/react";
|
||
|
||
interface Props {
|
||
targetServerId: string | null;
|
||
client?: Client | null;
|
||
}
|
||
|
||
export default function CreateSessionModal({ targetServerId, client }: Props) {
|
||
const { setModal } = useModalStore();
|
||
const [name, setName] = useState<string | null>(client?.name || null);
|
||
const [phone, setPhone] = useState<string | null>(client?.phone || null);
|
||
const [email, setEmail] = useState<string | null>(client?.email || null);
|
||
// const [isSessionExists, setIsSessionExists] = useState(false);
|
||
|
||
const queryClient = useQueryClient();
|
||
|
||
const { data: servers } = useQuery({
|
||
queryKey: ["servers"],
|
||
queryFn: () => api.get("servers?withLastSession=true").json<Server[]>(),
|
||
refetchInterval: 1000,
|
||
});
|
||
|
||
const targetServer = targetServerId
|
||
? servers?.find((server) => server.id === targetServerId) || null
|
||
: null;
|
||
|
||
const [selectedServer, setSelectedServer] = useState<Server | null>(
|
||
targetServer
|
||
);
|
||
const [selectedApp, setSelectedApp] = useState<App | null>(null);
|
||
|
||
useEffect(() => {
|
||
setSelectedApp(
|
||
selectedServer?.sessions?.[0]?.app ||
|
||
selectedServer?.apps?.[0].app ||
|
||
null
|
||
);
|
||
}, [selectedServer]);
|
||
|
||
const debouncedPhone = useDebounce(phone, 500);
|
||
|
||
const { data, isLoading, error } = useQuery({
|
||
queryKey: ["get-user-by-phone", debouncedPhone],
|
||
queryFn: () =>
|
||
api
|
||
.get("clients/by-phone", {
|
||
searchParams: debouncedPhone ? { phone: debouncedPhone } : {},
|
||
})
|
||
.json<Client>(),
|
||
enabled: !!debouncedPhone,
|
||
});
|
||
|
||
useEffect(() => {
|
||
if (!error && data) {
|
||
setName(data.name);
|
||
setEmail(data.email);
|
||
} else {
|
||
setName(null);
|
||
setEmail(null);
|
||
}
|
||
}, [data, error]);
|
||
|
||
const { mutate: createClient } = useMutation({
|
||
mutationFn: () =>
|
||
api
|
||
.post("clients", {
|
||
json: {
|
||
name,
|
||
phone,
|
||
email,
|
||
},
|
||
})
|
||
.json<Client>(),
|
||
});
|
||
|
||
const { mutate: createSession } = useMutation({
|
||
mutationKey: ["create-session", selectedServer?.id],
|
||
mutationFn: ({
|
||
clientId,
|
||
serverId,
|
||
appId,
|
||
}: {
|
||
clientId: string;
|
||
serverId: string;
|
||
appId: string;
|
||
}) =>
|
||
api
|
||
.post("sessions", {
|
||
json: {
|
||
clientId,
|
||
serverId,
|
||
appId,
|
||
},
|
||
})
|
||
.json<Session>(),
|
||
onMutate: () => {
|
||
queryClient.invalidateQueries({ queryKey: ["sessions"] });
|
||
queryClient.invalidateQueries({ queryKey: ["servers"] });
|
||
setModal(null);
|
||
},
|
||
});
|
||
|
||
// const { mutate: endSession } = useMutation({
|
||
// mutationKey: ["end-session", selectedServer?.sessions?.[0]?.id],
|
||
// mutationFn: () =>
|
||
// api.put(`sessions/${selectedServer?.sessions?.[0]?.id}`, {
|
||
// json: { status: "ending" },
|
||
// }),
|
||
// onMutate: () => queryClient.invalidateQueries({ queryKey: ["sessions"] }),
|
||
// });
|
||
|
||
async function handleClickCreateSession(e: React.FormEvent<HTMLFormElement>) {
|
||
e.preventDefault();
|
||
|
||
if (!name || !phone || !selectedServer || !selectedApp) return;
|
||
|
||
// if (selectedServer?.sessions?.[0]?.status !== "started") {
|
||
createClient(undefined, {
|
||
onSuccess: (client) => {
|
||
createSession({
|
||
clientId: client.id,
|
||
serverId: selectedServer.id,
|
||
appId: selectedApp.id,
|
||
});
|
||
},
|
||
});
|
||
// return;
|
||
// }
|
||
|
||
// if (!isSessionExists) {
|
||
// setIsSessionExists(true);
|
||
// return;
|
||
// }
|
||
|
||
// endSession(undefined, {
|
||
// onError: (error) => {
|
||
// console.log("Ошибка при завершении сессии:", error);
|
||
// },
|
||
// });
|
||
}
|
||
|
||
// useEffect(() => {
|
||
// if (
|
||
// selectedServer &&
|
||
// servers?.find((server) => server.id === selectedServer?.id)?.sessions?.[0]
|
||
// ?.status === "ended" &&
|
||
// selectedApp
|
||
// // && isSessionExists
|
||
// )
|
||
// createClient(undefined, {
|
||
// onSuccess: (client) => {
|
||
// createSession({
|
||
// clientId: client.id,
|
||
// serverId: selectedServer?.id,
|
||
// appId: selectedApp.id,
|
||
// });
|
||
// },
|
||
// });
|
||
// }, [
|
||
// selectedApp,
|
||
// servers,
|
||
// createClient,
|
||
// createSession,
|
||
// // isSessionExists,
|
||
// selectedServer,
|
||
// ]);
|
||
|
||
const ref = useRef<HTMLFormElement>(null);
|
||
|
||
return (
|
||
<form
|
||
className="relative rounded-[2.222vw] w-[25vw] 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>
|
||
<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-1moverflow-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]">
|
||
<Input
|
||
value={phone || ""}
|
||
onChange={(e) => setPhone(e.target.value)}
|
||
placeholder="Номер телефона"
|
||
required
|
||
isLoading={isLoading}
|
||
/>
|
||
<AnimatePresence>
|
||
{phone && (
|
||
<>
|
||
<motion.div
|
||
initial={{ opacity: 0 }}
|
||
animate={{ opacity: 1 }}
|
||
exit={{ opacity: 0 }}
|
||
>
|
||
<Input
|
||
value={name || ""}
|
||
disabled={isLoading}
|
||
onChange={(e) => setName(e.target.value)}
|
||
placeholder="Имя"
|
||
required
|
||
/>
|
||
</motion.div>
|
||
<motion.div
|
||
initial={{ opacity: 0 }}
|
||
animate={{ opacity: 1 }}
|
||
exit={{ opacity: 0 }}
|
||
>
|
||
<Input
|
||
disabled={isLoading}
|
||
type="email"
|
||
value={email || ""}
|
||
onChange={(e) => setEmail(e.target.value)}
|
||
placeholder="Электронная почта"
|
||
/>
|
||
</motion.div>
|
||
</>
|
||
)}
|
||
</AnimatePresence>
|
||
</div>
|
||
</div>
|
||
<div className="flex flex-col gap-y-[0.833vw]">
|
||
<p className="title-s font-medium">Выберите параметры сеанса</p>
|
||
{selectedServer &&
|
||
selectedServer?.apps &&
|
||
selectedServer?.apps?.length > 0 && (
|
||
<ProjectSelector
|
||
activeProject={
|
||
selectedServer?.sessions?.[0]?.status === "started"
|
||
? selectedApp
|
||
: null
|
||
}
|
||
projects={selectedServer?.apps.map(({ app }) => app)}
|
||
selectedProject={selectedApp}
|
||
setSelectedProject={setSelectedApp}
|
||
/>
|
||
)}
|
||
</div>
|
||
{/* {isSessionExists && ( */}
|
||
{/* <div className="absolute inset-0 top-[11.806vw] bg-[#FFFFFF] flex flex-col gap-[1.111vw] items-center justify-center h-[31.458vw]">
|
||
<img
|
||
src="/images/ghost.png"
|
||
alt="ghost error"
|
||
className="w-[13.889vw] h-[11.806vw]"
|
||
/>
|
||
<div className="flex flex-col gap-[0.556vw] items-center">
|
||
<h3 className="title-m font-medium">Есть текущий сеанс</h3>
|
||
<p className="caption-s text-[#BDBDBD] text-center whitespace-pre-line">
|
||
{`На выбранном столе есть текущий сеанс.
|
||
При запуске нового текущий будет завершен.`}
|
||
</p>
|
||
</div>
|
||
</div> */}
|
||
{/* )} */}
|
||
<div className="flex-1 flex flex-col justify-end">
|
||
<Button
|
||
type="submit"
|
||
disabled={
|
||
!phone ||
|
||
!name ||
|
||
!selectedServer ||
|
||
!selectedApp ||
|
||
servers?.find((server) => server.id === selectedServer?.id)
|
||
?.sessions?.[0]?.status === "ending"
|
||
}
|
||
variant="cta"
|
||
size="large"
|
||
>
|
||
<div className="size-[1.111vw] text-[#9184F6]">
|
||
<StartSessionIcon />
|
||
</div>
|
||
<span>Запустить сеанс</span>
|
||
</Button>
|
||
</div>
|
||
</div>
|
||
</form>
|
||
);
|
||
}
|