145 lines
4.8 KiB
TypeScript
145 lines
4.8 KiB
TypeScript
import { useQuery } from "@tanstack/react-query";
|
||
import api from "../utils/api";
|
||
import { IUser } from "../types/User";
|
||
import { Session } from "../types/Session";
|
||
import { useState } from "react";
|
||
import { IApp } from "../types/App";
|
||
import { useDebounce } from "@uidotdev/usehooks";
|
||
import SessionCard from "../components/SessionCard";
|
||
import { groupByCreatedAt } from "../utils/groupByCreatedAt";
|
||
import { format, isToday } from "date-fns";
|
||
import { ru } from "date-fns/locale";
|
||
import MultySelect from "../components/MultySelect";
|
||
import Button from "../components/Button";
|
||
import SearchInput from "../components/SearchInput";
|
||
|
||
function SessionsPage() {
|
||
const [limit, setLimit] = useState(10);
|
||
const [search, setSearch] = useState<string | null>(null);
|
||
const [managerIds, setManagerIds] = useState<string[]>([]);
|
||
const [appIds, setAppIds] = useState<string[]>([]);
|
||
|
||
const debouncedSearch = useDebounce(search, 500);
|
||
|
||
const { data: me } = useQuery({
|
||
queryKey: ["me"],
|
||
queryFn: () => api.get("auth/me").json<IUser>(),
|
||
});
|
||
|
||
const { data: managers } = useQuery({
|
||
queryKey: ["managers"],
|
||
queryFn: () => api.get("users").json<IUser[]>(),
|
||
enabled: !!me,
|
||
});
|
||
|
||
const { data: apps } = useQuery({
|
||
queryKey: ["apps"],
|
||
queryFn: () => api.get("apps").json<IApp[]>(),
|
||
enabled: !!me,
|
||
});
|
||
|
||
const { data: grouppedSessions } = useQuery({
|
||
queryKey: ["sessions", managerIds, appIds, debouncedSearch, limit],
|
||
queryFn: () =>
|
||
api
|
||
.get(
|
||
`sessions?${
|
||
managerIds.length ? `ownerIds=${managerIds.join()}` : ""
|
||
}${appIds.length ? `&appIds=${appIds.join()}` : ""}${
|
||
debouncedSearch ? `&clientSearch=${debouncedSearch}` : ""
|
||
}&limit=${limit}`
|
||
)
|
||
.json<Session[]>(),
|
||
enabled: !!me,
|
||
select: groupByCreatedAt,
|
||
});
|
||
|
||
const { data: count } = useQuery({
|
||
queryKey: ["sessions", "count", managerIds, appIds, debouncedSearch],
|
||
queryFn: () =>
|
||
api
|
||
.get(
|
||
`sessions/count?${
|
||
managerIds.length ? `ownerIds=${managerIds.join()}` : ""
|
||
}${appIds.length ? `&appIds=${appIds.join()}` : ""}${
|
||
debouncedSearch ? `&clientSearch=${debouncedSearch}` : ""
|
||
}`
|
||
)
|
||
.json<number>(),
|
||
enabled: !!me,
|
||
});
|
||
|
||
return (
|
||
<div className="py-[1.667vw] flex flex-col gap-[1.667vw]">
|
||
<h1 className="title-l font-medium">Сеансы</h1>
|
||
<div className="p-[1.389vw] rounded-[2.222vw] shadow-[0_4px_40px_0_rgba(15,16,17,0.05),0_2px_2px_0_rgba(15,16,17,0.05)] w-full">
|
||
<div className="space-y-[1.111vw]">
|
||
<div className="flex flex-col gap-[0.556vw]">
|
||
<SearchInput
|
||
placeholder="Поиск по имени клиента"
|
||
value={search || ""}
|
||
onChange={(e) => setSearch(e.target.value)}
|
||
onEnter={() => {}}
|
||
/>
|
||
<div className="flex gap-[0.556vw]">
|
||
<MultySelect
|
||
data={
|
||
managers?.map(({ fullname: name, ...manager }) => ({
|
||
name,
|
||
...manager,
|
||
})) || []
|
||
}
|
||
isGrid
|
||
placeholder={"Менеджер"}
|
||
resetTitle={"Все менеджеры"}
|
||
onSelect={(values) => setManagerIds(values.map(({ id }) => id))}
|
||
/>
|
||
<MultySelect
|
||
data={apps || []}
|
||
isGrid
|
||
placeholder={"Проект"}
|
||
resetTitle={"Все проекты"}
|
||
onSelect={(values) => setAppIds(values.map(({ id }) => id))}
|
||
/>
|
||
</div>
|
||
</div>
|
||
{!!count && (
|
||
<p className="caption-m font-medium opacity-40">
|
||
Найдено {count} сеансов
|
||
</p>
|
||
)}
|
||
</div>
|
||
</div>
|
||
<div className="space-y-[1.667vw]">
|
||
{Object.entries(grouppedSessions || {}).map(([timestamp, sessions]) => (
|
||
<div key={timestamp} className="space-y-[0.833vw]">
|
||
<p className="caption-m font-medium opacity-40">
|
||
{isToday(new Date(timestamp))
|
||
? "Сегодня"
|
||
: format(new Date(timestamp), "d MMMM", { locale: ru })}
|
||
</p>
|
||
<div className="space-y-[0.278vw]">
|
||
{sessions.map((session) => (
|
||
<SessionCard key={session.id} session={session} />
|
||
))}
|
||
</div>
|
||
</div>
|
||
))}
|
||
</div>
|
||
<Button
|
||
size="large"
|
||
variant="primary"
|
||
className="w-full"
|
||
onClick={() => {
|
||
setLimit((prev) => prev + 10);
|
||
}}
|
||
disabled={!!count && limit >= count}
|
||
>
|
||
Показать еще
|
||
</Button>
|
||
</div>
|
||
);
|
||
}
|
||
|
||
export default SessionsPage;
|