upd
This commit is contained in:
@@ -5,6 +5,8 @@ import api from "../utils/api";
|
||||
import { useMutation, useQueryClient } from "@tanstack/react-query";
|
||||
import ChevronRightIcon from "./icons/ChevronRightIcon";
|
||||
import { motion } from "motion/react";
|
||||
import CurrentSessionModal from "./modals/CurrentSessionModal";
|
||||
import useModalStore from "../stores/useModalStore";
|
||||
|
||||
function CurrentSessionCard({
|
||||
session,
|
||||
@@ -13,9 +15,11 @@ function CurrentSessionCard({
|
||||
session: ISession;
|
||||
index: number;
|
||||
}) {
|
||||
const { setModal } = useModalStore();
|
||||
|
||||
const queryClient = useQueryClient();
|
||||
|
||||
const { mutate: endSession } = useMutation({
|
||||
const { mutate: endSession, isPending } = useMutation({
|
||||
mutationKey: ["sessions", session.id],
|
||||
mutationFn: () =>
|
||||
api.put(`sessions/${session.id}`, { json: { status: "ending" } }),
|
||||
@@ -36,7 +40,14 @@ function CurrentSessionCard({
|
||||
transition={{ bounce: 0, delay: index * 0.1 }}
|
||||
className="p-[1.389vw] rounded-[1.667vw] bg-white w-[18.889vw] flex flex-col gap-[0.833vw] shadow-[0px_4px_40px_0_rgba(0,0,0,0.05),0px_2px_2px_0_rgba(0,0,0,0.05)]"
|
||||
>
|
||||
<div className="flex justify-between gap-[0.833vw] items-center">
|
||||
<div
|
||||
className="flex justify-between gap-[0.833vw] items-center cursor-pointer"
|
||||
onClick={() => {
|
||||
if (session.status === "started") {
|
||||
setModal(<CurrentSessionModal session={session} />);
|
||||
}
|
||||
}}
|
||||
>
|
||||
<span className="size-[1.111vw] text-[#7B60F3]">
|
||||
<FlashIcon />
|
||||
</span>
|
||||
@@ -58,7 +69,11 @@ function CurrentSessionCard({
|
||||
<ChevronRightIcon />
|
||||
</span>
|
||||
</div>
|
||||
<NewButton variant="critical" onClick={() => endSession()}>
|
||||
<NewButton
|
||||
variant="critical"
|
||||
onClick={() => endSession()}
|
||||
disabled={isPending}
|
||||
>
|
||||
Завершить сеанс
|
||||
</NewButton>
|
||||
</motion.div>
|
||||
|
||||
@@ -10,6 +10,7 @@ import UnlinkIcon from "./icons/UnlinkIcon";
|
||||
import clsx from "clsx";
|
||||
import ChevronRightIcon from "./icons/ChevronRightIcon";
|
||||
import CurrentSessionModal from "./modals/CurrentSessionModal";
|
||||
|
||||
interface IDesktopCardProps {
|
||||
server: IServer;
|
||||
}
|
||||
@@ -17,18 +18,6 @@ interface IDesktopCardProps {
|
||||
export default function DesktopCard({ server }: IDesktopCardProps) {
|
||||
const { setModal, setPosition } = useModalStore();
|
||||
|
||||
// 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"] }),
|
||||
// });
|
||||
|
||||
async function handleClickCreateSession() {
|
||||
setPosition("right");
|
||||
setModal(<CreateSessionModal targetServerId={server.id} />);
|
||||
@@ -62,12 +51,20 @@ export default function DesktopCard({ server }: IDesktopCardProps) {
|
||||
</div>
|
||||
</div>
|
||||
<div className="flex gap-[0.278vw] justify-center -mt-[0.278vw]">
|
||||
{server.sessions?.[0]?.status === "started" ? (
|
||||
{server.sessions?.[0]?.status === "starting" ? (
|
||||
<div className="p-4 bg-white rounded-lg text-2xl outline">
|
||||
загрузка...
|
||||
</div>
|
||||
) : server.sessions?.[0]?.status === "started" ? (
|
||||
<div className="flex items-center gap-[0.278vw]">
|
||||
<NewButton
|
||||
variant="primary"
|
||||
onClick={() => {
|
||||
setModal(<CurrentSessionModal server={server} />);
|
||||
if (server.sessions?.length && server.sessions?.[0]) {
|
||||
setModal(
|
||||
<CurrentSessionModal session={server.sessions[0]} />
|
||||
);
|
||||
}
|
||||
}}
|
||||
className="flex gap-[0.556vw] items-center"
|
||||
>
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { useState } from "react";
|
||||
import { useEffect, useState } from "react";
|
||||
import { IApp } from "../types/IApp";
|
||||
import ChevronLeftIcon from "./icons/ChevronLeftIcon";
|
||||
import CloseIcon from "./icons/CloseIcon";
|
||||
@@ -11,18 +11,22 @@ interface Props {
|
||||
projects: IApp[];
|
||||
selectedProject: IApp | null;
|
||||
setSelectedProject: (project: IApp | null) => void;
|
||||
activeProject: IApp | null;
|
||||
}
|
||||
|
||||
function ProjectSelector({
|
||||
projects,
|
||||
selectedProject,
|
||||
setSelectedProject,
|
||||
activeProject,
|
||||
}: Props) {
|
||||
const [isOpen, setIsOpen] = useState(false);
|
||||
|
||||
const [pointedProject, setPointedProject] = useState<IApp | null>(
|
||||
selectedProject
|
||||
);
|
||||
const [pointedProject, setPointedProject] = useState<IApp | null>(null);
|
||||
|
||||
useEffect(() => {
|
||||
setPointedProject(selectedProject);
|
||||
}, [selectedProject]);
|
||||
|
||||
return (
|
||||
<>
|
||||
@@ -86,16 +90,19 @@ function ProjectSelector({
|
||||
<div className="space-y-[0.278vw]">
|
||||
<div className="flex items-center gap-[0.278vw]">
|
||||
<p className="text-s">{project.name}</p>
|
||||
<span className="size-[0.972vw] text-[#7B60F3]">
|
||||
<LightningIcon />
|
||||
</span>
|
||||
{activeProject &&
|
||||
project.name === activeProject.name && (
|
||||
<span className="size-[0.972vw] text-[#7B60F3]">
|
||||
<LightningIcon />
|
||||
</span>
|
||||
)}
|
||||
</div>
|
||||
<p className="caption-s text-[#7D7D7D] font-medium">
|
||||
Доступно 128 квартир
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
{pointedProject?.id === project.id ? (
|
||||
{pointedProject?.name === project.name ? (
|
||||
<div className="size-[1.389vw] flex items-center justify-center rounded-full bg-[#7B60F3]">
|
||||
<div className="size-[0.833vw] text-white">
|
||||
<CheckIcon />
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import { IServer } from "../../types/IServer.ts";
|
||||
import { useRef, useState } from "react";
|
||||
import { useEffect, useRef, useState } from "react";
|
||||
import { IApp } from "../../types/IApp.ts";
|
||||
import api from "../../utils/api.ts";
|
||||
import { ISession } from "../../types/ISession.ts";
|
||||
@@ -22,11 +22,13 @@ export default function CreateSessionModal({ targetServerId }: Props) {
|
||||
const [phone, setPhone] = useState("");
|
||||
const [email, setEmail] = useState("");
|
||||
const [isSessionExists, setIsSessionExists] = useState(false);
|
||||
|
||||
const queryClient = useQueryClient();
|
||||
|
||||
const { data: servers } = useQuery({
|
||||
queryKey: ["servers"],
|
||||
queryFn: () => api.get("servers?withLastSession=true").json<IServer[]>(),
|
||||
refetchInterval: 1000,
|
||||
});
|
||||
|
||||
const targetServer = targetServerId
|
||||
@@ -38,6 +40,12 @@ export default function CreateSessionModal({ targetServerId }: Props) {
|
||||
);
|
||||
const [selectedApp, setSelectedApp] = useState<IApp | null>(null);
|
||||
|
||||
useEffect(() => {
|
||||
setSelectedApp(
|
||||
selectedServer?.sessions?.[0]?.app || selectedServer?.apps?.[0] || null
|
||||
);
|
||||
}, [selectedServer]);
|
||||
|
||||
const { mutate: createClient } = useMutation({
|
||||
mutationFn: () => {
|
||||
return api
|
||||
@@ -53,20 +61,27 @@ export default function CreateSessionModal({ targetServerId }: Props) {
|
||||
});
|
||||
|
||||
const { mutate: createSession } = useMutation({
|
||||
mutationFn: (clientId: string) => {
|
||||
return api
|
||||
mutationKey: ["create-session", selectedServer?.id],
|
||||
mutationFn: ({
|
||||
clientId,
|
||||
serverId,
|
||||
appId,
|
||||
}: {
|
||||
clientId: string;
|
||||
serverId: string;
|
||||
appId: string;
|
||||
}) =>
|
||||
api
|
||||
.post("sessions", {
|
||||
json: {
|
||||
clientId,
|
||||
serverId: selectedServer?.id,
|
||||
appId: selectedApp?.id,
|
||||
serverId,
|
||||
appId,
|
||||
},
|
||||
})
|
||||
.json<ISession>();
|
||||
},
|
||||
.json<ISession>(),
|
||||
onMutate: () => {
|
||||
queryClient.invalidateQueries({ queryKey: ["sessions"] });
|
||||
queryClient.invalidateQueries({ queryKey: ["last-started"] });
|
||||
queryClient.invalidateQueries({ queryKey: ["servers"] });
|
||||
setModal(null);
|
||||
},
|
||||
@@ -86,40 +101,43 @@ export default function CreateSessionModal({ targetServerId }: Props) {
|
||||
|
||||
if (!name || !phone || !selectedServer || !selectedApp) return;
|
||||
|
||||
if (
|
||||
selectedServer?.sessions?.[0]?.status === "started" &&
|
||||
!isSessionExists
|
||||
) {
|
||||
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;
|
||||
}
|
||||
|
||||
if (isSessionExists) {
|
||||
endSession(undefined, {
|
||||
onSuccess: () => {
|
||||
createClient(undefined, {
|
||||
onSuccess: (client) => {
|
||||
createSession(client.id);
|
||||
},
|
||||
onError: (error) => {
|
||||
console.log(error);
|
||||
},
|
||||
});
|
||||
},
|
||||
onError: (error) => {
|
||||
console.log("Ошибка при завершении сессии:", error);
|
||||
},
|
||||
});
|
||||
} else {
|
||||
createClient(undefined, {
|
||||
onSuccess: (client) => {
|
||||
createSession(client.id);
|
||||
},
|
||||
onError: (error) => {
|
||||
console.log(error);
|
||||
},
|
||||
});
|
||||
}
|
||||
endSession(undefined, {
|
||||
onSuccess: () => {
|
||||
createClient(undefined, {
|
||||
onSuccess: (client) => {
|
||||
createSession({
|
||||
clientId: client.id,
|
||||
serverId: selectedServer.id,
|
||||
appId: selectedApp.id,
|
||||
});
|
||||
},
|
||||
onError: (error) => {
|
||||
console.log(error);
|
||||
},
|
||||
});
|
||||
},
|
||||
onError: (error) => {
|
||||
console.log("Ошибка при завершении сессии:", error);
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
const ref = useRef<HTMLFormElement>(null);
|
||||
@@ -165,13 +183,20 @@ export default function CreateSessionModal({ targetServerId }: Props) {
|
||||
</div>
|
||||
<div className="flex flex-col gap-y-[0.833vw]">
|
||||
<p className="title-s font-medium">Выберите параметры сеанса</p>
|
||||
{selectedServer?.apps && selectedServer?.apps?.length > 0 && (
|
||||
<ProjectSelector
|
||||
projects={selectedServer?.apps || []}
|
||||
selectedProject={selectedApp ?? selectedServer?.apps?.[0] ?? null}
|
||||
setSelectedProject={setSelectedApp}
|
||||
/>
|
||||
)}
|
||||
{selectedServer &&
|
||||
selectedServer?.apps &&
|
||||
selectedServer?.apps?.length > 0 && (
|
||||
<ProjectSelector
|
||||
activeProject={
|
||||
selectedServer?.sessions?.[0]?.status === "started"
|
||||
? selectedApp
|
||||
: null
|
||||
}
|
||||
projects={selectedServer?.apps}
|
||||
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]">
|
||||
|
||||
@@ -1,35 +1,25 @@
|
||||
import { intervalToDuration } from "date-fns";
|
||||
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 { useMutation, useQueryClient } from "@tanstack/react-query";
|
||||
import api from "../../utils/api";
|
||||
import useModalStore from "../../stores/useModalStore";
|
||||
import { ISession } from "../../types/ISession";
|
||||
import { useEffect, useState } from "react";
|
||||
|
||||
function CurrentSessionModal({ server }: { server: IServer }) {
|
||||
function CurrentSessionModal({ session }: { session: ISession }) {
|
||||
const queryClient = useQueryClient();
|
||||
const { setModal } = useModalStore();
|
||||
|
||||
const { mutate: endSession } = useMutation({
|
||||
mutationKey: ["sessions", server.sessions?.[0]?.id],
|
||||
mutationKey: ["sessions", session.id],
|
||||
mutationFn: () =>
|
||||
api.put(`sessions/${server.sessions?.[0]?.id}`, {
|
||||
api.put(`sessions/${session.id}`, {
|
||||
json: { status: "ending" },
|
||||
}),
|
||||
onMutate: () => queryClient.invalidateQueries({ queryKey: ["sessions"] }),
|
||||
});
|
||||
|
||||
const { data: currentSession } = useQuery({
|
||||
queryKey: ["sessions", server.sessions?.[0]?.id],
|
||||
queryFn: () =>
|
||||
api.get(`sessions/${server.sessions?.[0]?.id}`).json<ISession>(),
|
||||
});
|
||||
|
||||
console.log(currentSession);
|
||||
|
||||
const [now, setNow] = useState(Date.now());
|
||||
|
||||
useEffect(() => {
|
||||
@@ -39,19 +29,7 @@ function CurrentSessionModal({ server }: { server: IServer }) {
|
||||
return () => clearInterval(interval);
|
||||
}, []);
|
||||
|
||||
useEffect(() => {
|
||||
if (!currentSession) return;
|
||||
const duration = intervalToDuration({
|
||||
start: currentSession.createdAt,
|
||||
end: now,
|
||||
});
|
||||
const hours = (duration.hours || 0).toString().padStart(2, "0");
|
||||
const minutes = (duration.minutes || 0).toString().padStart(2, "0");
|
||||
const seconds = (duration.seconds || 0).toString().padStart(2, "0");
|
||||
console.log(`${hours}:${minutes}:${seconds}`);
|
||||
}, [currentSession, now]);
|
||||
|
||||
if (!currentSession) return null;
|
||||
if (!session) return null;
|
||||
|
||||
return (
|
||||
<div className="w-[25vw] bg-[#FFFFFF] rounded-4xl px-[1.389vw] pb-[1.389vw]">
|
||||
@@ -63,13 +41,15 @@ function CurrentSessionModal({ server }: { server: IServer }) {
|
||||
<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="title-s font-medium">{session.server.name}</p>
|
||||
<p className="flex justify-center items-center gap-[0.139vw] caption-s font-medium text-[#7B60F3]">
|
||||
<FlashIcon />
|
||||
<span className="size-[0.833vw] text-[#7B60F3]">
|
||||
<FlashIcon />
|
||||
</span>
|
||||
Сеанс идёт{" "}
|
||||
{(() => {
|
||||
const duration = intervalToDuration({
|
||||
start: currentSession.createdAt,
|
||||
start: session.createdAt,
|
||||
end: now,
|
||||
});
|
||||
const hours = (duration.hours || 0)
|
||||
@@ -87,7 +67,7 @@ function CurrentSessionModal({ server }: { server: IServer }) {
|
||||
</div>
|
||||
<div>
|
||||
<p className="caption-s font-medium text-[#BDBDBD]">
|
||||
{server.location}
|
||||
{session.server.location}
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
@@ -98,12 +78,10 @@ function CurrentSessionModal({ server }: { server: IServer }) {
|
||||
<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">
|
||||
{currentSession.client.name}
|
||||
</p>
|
||||
<p className="text-s font-medium">{session.client.name}</p>
|
||||
</div>
|
||||
<div className="flex gap-[0.556vw] items-center">
|
||||
{!currentSession.client.email && (
|
||||
{!session.client.email && (
|
||||
<p className="caption-s font-medium text-[#7B60F3] whitespace-nowrap">
|
||||
Добавьте email
|
||||
</p>
|
||||
@@ -120,9 +98,7 @@ function CurrentSessionModal({ server }: { server: IServer }) {
|
||||
<div>
|
||||
<div className="flex gap-[0.556vw]">
|
||||
<p className="caption-s font-medium text-[#BDBDBD]">Менеджер:</p>
|
||||
<p className="caption-s font-medium">
|
||||
{currentSession.owner.fullname}
|
||||
</p>
|
||||
<p className="caption-s font-medium">{session.owner.fullname}</p>
|
||||
</div>
|
||||
<div className="flex gap-[0.556vw]">
|
||||
<p className="caption-s font-medium text-[#BDBDBD]">
|
||||
|
||||
Reference in New Issue
Block a user