Merge branch 'main' of http://192.168.1.163:3000/inmake/graff-mate-client
This commit is contained in:
@@ -9,6 +9,7 @@ import CogIcon from "./icons/CogIcon";
|
||||
import ChevronLeftIcon from "./icons/ChevronLeftIcon";
|
||||
import PlusIcon from "./icons/PlusIcon";
|
||||
import EditTable from "./modals/EditTable";
|
||||
|
||||
interface IDesktopCardProps {
|
||||
server: IServer;
|
||||
}
|
||||
@@ -45,56 +46,56 @@ export default function DesktopCard({ server }: IDesktopCardProps) {
|
||||
}
|
||||
|
||||
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='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]'>
|
||||
<NewButton
|
||||
variant="secondary"
|
||||
size="medium"
|
||||
className="absolute top-[0.347vw] right-[0.347vw] cursor-pointer flex items-center justify-center"
|
||||
onClick={() => setModal(<EditTable />)}
|
||||
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={() => endSession()}
|
||||
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'>
|
||||
<ChevronLeftIcon />
|
||||
</span>
|
||||
</NewButton>
|
||||
</div>
|
||||
) : (
|
||||
<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>
|
||||
)}
|
||||
|
||||
@@ -45,7 +45,7 @@ function ModalContainer() {
|
||||
initial={{ opacity: 0 }}
|
||||
animate={{ opacity: 1 }}
|
||||
exit={{ opacity: 0 }}
|
||||
className="h-full"
|
||||
className='h-full'
|
||||
>
|
||||
<div
|
||||
ref={popoverRef}
|
||||
@@ -55,28 +55,27 @@ function ModalContainer() {
|
||||
position === "right" && "items-end"
|
||||
)}
|
||||
>
|
||||
<div className="max-h-full">
|
||||
<div ref={divRef} className="p-[0.972vw]">
|
||||
<div className='max-h-full'>
|
||||
<div ref={divRef} className='p-[0.972vw]'>
|
||||
<div
|
||||
ref={backdropRef}
|
||||
className="absolute inset-0 cursor-pointer"
|
||||
className='absolute inset-0 cursor-pointer'
|
||||
onClick={() => setModal(null)}
|
||||
/>
|
||||
<div
|
||||
ref={containerRef}
|
||||
className="relative w-full"
|
||||
className='relative w-full'
|
||||
// style={{
|
||||
// height: `calc(${backdropRef.current?.clientHeight}px - 0.972vw * 2)`,
|
||||
// }}
|
||||
>
|
||||
{modal}
|
||||
<NewButton
|
||||
className="absolute top-[1.667vw] right-[1.667vw] p-[0.556vw] bg-[#F9F9F9]"
|
||||
size="small"
|
||||
variant="secondary"
|
||||
variant='secondary'
|
||||
className='absolute top-[1.389vw] right-[1.389vw] p-[0.556vw]'
|
||||
onClick={() => setModal(null)}
|
||||
>
|
||||
<span className="w-[1.389vw] h-[1.389vw] text-[#7D7D7D]">
|
||||
<span className='w-[0.972vw] h-[0.972vw] text-[#7D7D7D]'>
|
||||
<CloseIcon />
|
||||
</span>
|
||||
</NewButton>
|
||||
|
||||
@@ -0,0 +1,21 @@
|
||||
import { ISession } from "../types/ISession";
|
||||
|
||||
function SessionCard({ session }: { session: ISession }) {
|
||||
console.log(session);
|
||||
return (
|
||||
<div className='w-full h-[4.444vw] border-1 border-l-0 border-r-0 border-t-0 border-b-[#F6F6F6] flex py-[0.278vw] items-center gap-[0.556vw] cursor-pointer group'>
|
||||
<div className='rounded-xl w-full h-full flex items-center gap-[0.556vw] group-hover:bg-[#F6F6F6] transition-colors duration-200'>
|
||||
<div className='size-[2.5vw] bg-[#F6F6F6] rounded-full'></div>
|
||||
<div className='flex flex-col w-full gap-[0.278vw]'>
|
||||
<p className='button-m font-medium'>{session.owner.fullname}</p>
|
||||
<p className='caption-s font-medium text-[#7D7D7D]'>
|
||||
Клиент: {session.client.name} •
|
||||
{session.app.name}
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
export default SessionCard;
|
||||
@@ -1,21 +1,82 @@
|
||||
import { useState } from "react";
|
||||
import NewInput from "../NewInput";
|
||||
import NewButton from "../NewButton";
|
||||
import useModalStore from "../../stores/useModalStore";
|
||||
import { IServer } from "../../types/IServer";
|
||||
import { useQueryClient } from "@tanstack/react-query";
|
||||
import { useMutation } from "@tanstack/react-query";
|
||||
import api from "../../utils/api";
|
||||
|
||||
function EditTable() {
|
||||
const [name, setName] = useState("");
|
||||
function EditTable({ table }: { table: IServer }) {
|
||||
const [tableName, setTableName] = useState(table.name);
|
||||
const [tableDescription, setTableDescription] = useState(table.location);
|
||||
const { setModal } = useModalStore();
|
||||
const queryClient = useQueryClient();
|
||||
|
||||
const { mutate: updateTable } = useMutation({
|
||||
mutationFn: () => {
|
||||
return api.put(`servers/${table.id}`, {
|
||||
json: {
|
||||
name: tableName,
|
||||
location: tableDescription,
|
||||
},
|
||||
});
|
||||
},
|
||||
onSuccess: () => {
|
||||
queryClient.invalidateQueries({ queryKey: ["servers"] });
|
||||
setModal(null);
|
||||
},
|
||||
});
|
||||
return (
|
||||
<div className='bg-[#F0F0F0] rounded-4xl w-[25vw] flex flex-col justify-center items-center'>
|
||||
<h3 className='title-s font-medium'>Редатирование стола</h3>
|
||||
<h3 className='title-s font-medium py-[1.806vw]'>Редатирование стола</h3>
|
||||
<div className='bg-[url("/images/Table.png")] bg-no-repeat bg-contain bg-center w-full h-[6.944vw]'></div>
|
||||
<div className='bg-[#FFFFFF] w-full rounded-4xl p-[1.389vw]'>
|
||||
<NewInput
|
||||
placeholder='Название стола*'
|
||||
className='w-full h-[3.889vw]'
|
||||
value={name}
|
||||
onChange={(e) => setName(e.target.value)}
|
||||
isError={name.length > 20}
|
||||
errorMessage='Что-то пошло не так'
|
||||
/>
|
||||
<div className='bg-[#FFFFFF] w-full rounded-4xl p-[1.389vw] flex flex-col gap-[0.833vw]'>
|
||||
<div className='space-y-[0.556vw]'>
|
||||
<NewInput
|
||||
placeholder='Название стола*'
|
||||
value={tableName}
|
||||
onChange={(e) => setTableName(e.target.value)}
|
||||
isError={tableName.length > 16}
|
||||
errorMessage='Не больше 16 символов'
|
||||
/>
|
||||
<p className='caption-s text-[#BDBDBD] font-medium w-full tracking-[-0.02em]'>
|
||||
Придумайте название до 16 символов, например «Тузик»
|
||||
</p>
|
||||
</div>
|
||||
<div className='space-y-[0.556vw]'>
|
||||
<NewInput
|
||||
placeholder='Описание'
|
||||
value={tableDescription}
|
||||
onChange={(e) => setTableDescription(e.target.value)}
|
||||
isError={tableDescription.length > 20}
|
||||
errorMessage='Не больше 20 символов'
|
||||
/>
|
||||
<p className='caption-s text-[#BDBDBD] font-medium w-full tracking-[-0.02em]'>
|
||||
Придумайте описание до 20 символов, например «Расположен в офисе»
|
||||
</p>
|
||||
</div>
|
||||
<div className='flex flex-col gap-[0.556vw]'>
|
||||
<NewButton
|
||||
variant='cta'
|
||||
disabled={
|
||||
tableName.length < 1 ||
|
||||
tableName.length > 16 ||
|
||||
tableDescription.length > 20
|
||||
}
|
||||
className='flex justify-center'
|
||||
onClick={() => updateTable()}
|
||||
>
|
||||
<p>Сохранить изменения</p>
|
||||
</NewButton>
|
||||
<NewButton
|
||||
variant='primary'
|
||||
className='flex justify-center'
|
||||
onClick={() => setModal(null)}
|
||||
>
|
||||
<p>Отменить</p>
|
||||
</NewButton>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
|
||||
+23
-13
@@ -4,6 +4,8 @@ import api from "../utils/api";
|
||||
import { IServer } from "../types/IServer";
|
||||
import DesktopCard from "../components/DesktopCard";
|
||||
import Badge from "../components/Badge";
|
||||
import { ISession } from "../types/ISession";
|
||||
import SessionCard from "../components/SessionCard";
|
||||
|
||||
function DashboardPage() {
|
||||
const { data: me } = useQuery({
|
||||
@@ -18,6 +20,13 @@ function DashboardPage() {
|
||||
refetchInterval: 1000,
|
||||
});
|
||||
|
||||
const { data: sessions } = useQuery({
|
||||
queryKey: ["sessions", { limit: 5 }],
|
||||
queryFn: () =>
|
||||
api.get("sessions", { searchParams: { limit: 5 } }).json<ISession[]>(),
|
||||
enabled: !!me,
|
||||
});
|
||||
|
||||
// async function logout() {
|
||||
// return await api.get("auth/logout").json();
|
||||
// }
|
||||
@@ -32,19 +41,8 @@ function DashboardPage() {
|
||||
// }
|
||||
|
||||
return (
|
||||
<div className='flex gap-[0.833vw] min-h-dvh'>
|
||||
<div className='w-[3.333vw] flex flex-col justify-between'></div>
|
||||
<div className='bg-white w-full flex justify-between'>
|
||||
{/* <div className="w-[13.889vw] space-y-[1.667vw]">
|
||||
<div className="px-[1.111vw] py-[0.556vw]">
|
||||
<img
|
||||
src="/logo-mate.svg"
|
||||
alt="logo"
|
||||
className="w-[4.569vw] h-[1.944vw]"
|
||||
/>
|
||||
</div>
|
||||
<Sidebar />
|
||||
</div> */}
|
||||
<div className='flex flex-col gap-[5vw] min-h-dvh'>
|
||||
<div className='w-full flex justify-between'>
|
||||
<div className='w-[42.5vw] flex flex-col gap-[1.667vw]'>
|
||||
<div className='flex items-center gap-[0.833vw]'>
|
||||
<h1 className='title-l font-[500] '>Интерактивные столы</h1>
|
||||
@@ -57,6 +55,18 @@ function DashboardPage() {
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div className='w-full'>
|
||||
<div className='flex flex-col gap-[1.667vw]'>
|
||||
<h1 className='title-l font-[500] '>Последние сеансы</h1>
|
||||
<div className='flex flex-col gap-[0.278vw]'>
|
||||
{sessions
|
||||
?.filter((session) => session.status === "ended")
|
||||
.map((session) => (
|
||||
<SessionCard key={session.id} session={session} />
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -0,0 +1,3 @@
|
||||
export interface IOwner {
|
||||
fullname: string;
|
||||
}
|
||||
@@ -1,4 +1,5 @@
|
||||
import { IApp } from "./IApp";
|
||||
import { IOwner } from "./IOwner";
|
||||
import { IServer } from "./IServer";
|
||||
import { IUser } from "./IUser";
|
||||
|
||||
@@ -11,5 +12,6 @@ export interface ISession {
|
||||
status: "starting" | "started" | "restarted" | "ending" | "ended";
|
||||
server: IServer;
|
||||
client: IUser;
|
||||
app: IApp
|
||||
app: IApp;
|
||||
owner: IOwner;
|
||||
}
|
||||
|
||||
+1
-1
@@ -3,7 +3,7 @@ import { ICompany } from "./ICompany";
|
||||
export interface IUser {
|
||||
id: string;
|
||||
email: string;
|
||||
fullname: string;
|
||||
name: string;
|
||||
companyId: string;
|
||||
company?: ICompany;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user