session files

This commit is contained in:
2025-06-16 16:24:45 +05:00
parent e11bcb13f3
commit 7f606cedf2
33 changed files with 153 additions and 67 deletions
+1 -1
View File
@@ -1,4 +1,4 @@
import { Client } from "../types/IClient";
import { Client } from "../types/Client";
import ChevronRightIcon from "./icons/ChevronRightIcon";
import Button from "./Button";
+1 -1
View File
@@ -1,5 +1,5 @@
import FlashIcon from "./icons/FlashIcon";
import { Session } from "../types/ISession";
import { Session } from "../types/Session";
import Button from "./Button";
import ChevronRightIcon from "./icons/ChevronRightIcon";
import { motion } from "motion/react";
+1 -1
View File
@@ -1,4 +1,4 @@
import { Server } from "../types/IServer";
import { Server } from "../types/Server";
import useModalStore from "../stores/useModalStore";
import CreateSessionModal from "./modals/CreateSessionModal";
import Button from "./Button";
+1 -1
View File
@@ -1,6 +1,6 @@
import { useQueryClient } from "@tanstack/react-query";
import ChevronDownIcon from "./icons/ChevronDownIcon";
import { IUser } from "../types/IUser";
import { IUser } from "../types/User";
import SearchInput from "./SearchInput";
function Header() {
+1 -1
View File
@@ -3,7 +3,7 @@ import Navbar from "./Navbar";
import { useQuery } from "@tanstack/react-query";
import api from "../utils/api";
import CurrentSessionCard from "./CurrentSessionCard";
import { Session } from "../types/ISession";
import { Session } from "../types/Session";
import { AnimatePresence } from "motion/react";
import Button from "./Button";
import PlusIcon from "./icons/PlusIcon";
+15 -18
View File
@@ -3,6 +3,7 @@ import ChevronDownIcon from "./icons/ChevronDownIcon";
import ChevronUpIcon from "./icons/ChevronUpIcon";
import clsx from "clsx";
import CloseIcon from "./icons/CloseIcon";
import Button from "./Button";
function NewSelect<T extends { name: string }>({
data,
@@ -51,16 +52,9 @@ function NewSelect<T extends { name: string }>({
)}
onClick={() => setIsSelectVisible(!isSelectVisible)}
>
<div className="button-m font-medium flex gap-[0.278vw] text-ellipsis overflow-hidden">
<div className="button-m font-medium text-ellipsis line-clamp-1">
{selectedValues.length > 0
? selectedValues.map((value, index) => {
return (
<div key={value.name} className="">
{value.name +
(index !== selectedValues.length - 1 ? ", " : "")}
</div>
);
})
? selectedValues.map(({ name }) => name).join(", ")
: placeholder}
</div>
<span
@@ -79,19 +73,22 @@ function NewSelect<T extends { name: string }>({
)}
>
<div
className="flex flex-col gap-[0.278vw] p-[0.833vw] w-full bg-white rounded-[0.833vw]"
className="flex flex-col gap-[0.278vw] px-[0.833vw] pb-[0.833vw] w-full bg-white rounded-[0.833vw] max-h-[300px] overflow-auto"
style={{
boxShadow: "0px 4px 40px 0px #0000000D, 0px 2px 2px 0px #0000000D",
}}
>
<div
className="text-s font-medium p-[0.833vw] text-[#7D7D7D] flex items-center gap-[0.278vw] cursor-pointer rounded-[0.278vw] hover:bg-[#F6F6F6]"
onClick={() => setSelectedValues([])}
>
<span className="size-[1.111vw] flex items-center justify-center">
<CloseIcon />
</span>
Выбрать всё
<div className="bg-white sticky top-0 pt-[0.833vw]">
<Button
variant="secondary"
className="!justify-start w-full text-s font-medium p-[0.833vw] text-[#7D7D7D] flex items-center gap-[0.278vw] cursor-pointer rounded-[0.278vw] hover:bg-[#F6F6F6]"
onClick={() => setSelectedValues([])}
>
<span className="size-[1.111vw] flex items-center justify-center">
<CloseIcon />
</span>
Выбрать всё
</Button>
</div>
<div className="h-[1px] w-full bg-[#F6F6F6]" />
{data.map((item, index) => (
+1 -1
View File
@@ -1,5 +1,5 @@
import { useEffect, useState } from "react";
import { IApp } from "../types/IApp";
import { IApp } from "../types/App";
import ChevronLeftIcon from "./icons/ChevronLeftIcon";
import CloseIcon from "./icons/CloseIcon";
import LightningIcon from "./icons/LightningIcon";
+1 -1
View File
@@ -1,5 +1,5 @@
import useModalStore from "../stores/useModalStore";
import { Session } from "../types/ISession";
import { Session } from "../types/Session";
import SessionModal from "./modals/SessionModal";
function SessionCard({ session }: { session: Session }) {
+1 -1
View File
@@ -1,5 +1,5 @@
import { motion } from "motion/react";
import { Comment } from "../types/IComments";
import { Comment } from "../types/Comments";
import { format } from "date-fns";
function SessionCommentItem({ comment }: { comment: Comment }) {
+1 -1
View File
@@ -3,7 +3,7 @@ import SendIcon from "./icons/SendIcon";
import Button from "./Button";
import { useMutation, useQuery, useQueryClient } from "@tanstack/react-query";
import api from "../utils/api";
import { Comment } from "../types/IComments";
import { Comment } from "../types/Comments";
import SessionCommentItem from "./SessionCommentItem";
import { AnimatePresence } from "motion/react";
+57
View File
@@ -0,0 +1,57 @@
import { Session } from "../types/Session";
import ChevronRightIcon from "./icons/ChevronRightIcon";
import FilledHomeIcon from "./icons/FilledHomeIcon";
interface SessionFilesProps {
files: { filename: string; size: number }[];
session: Session;
}
function SessionFiles({ files, session }: SessionFilesProps) {
return (
<div>
{files.map((file) => (
<SessionFile
{...file}
key={file.filename}
link={`http://${session.server.ipAddress}:3001/files/${
session.app.fileName
}/${new Date(session.createdAt).getTime()}/${file.filename}`}
/>
))}
</div>
);
}
export default SessionFiles;
export function SessionFile({
link,
filename,
size,
}: {
link: string;
filename: string;
size: number;
}) {
return (
<a
href={link}
target="_blank"
className="flex gap-[0.833vw] items-center px-[0.833vw] py-[0.556vw]"
>
<div className="p-[0.972vw] bg-[#F6F6F6] rounded-[0.556vw]">
<span className="size-[1.389vw] text-[#7D7D7D]">
<FilledHomeIcon />
</span>
</div>
<div className="space-y-[0.278vw] flex-1">
<p className="button-m font-medium">{filename}</p>
<p className="text-[#BDBDBD] caption-s font-medium">{size}</p>
</div>
<span className="size-[1.389vw] text-[#7D7D7D]">
<ChevronRightIcon />
</span>
</a>
);
}
+1 -1
View File
@@ -1,5 +1,5 @@
import clsx from "clsx";
import { Server } from "../types/IServer";
import { Server } from "../types/Server";
import LightningIcon from "./icons/LightningIcon";
interface TableSelectorProps {
+12
View File
@@ -0,0 +1,12 @@
function FilledHomeIcon() {
return (
<svg viewBox="0 0 20 20" fill="none" xmlns="http://www.w3.org/2000/svg">
<path
d="M12.1 17v-3.56c0-.565-.221-1.109-.615-1.51A2.08 2.08 0 0 0 10 11.307c-.557 0-1.091.225-1.485.625s-.615.944-.615 1.51V17H4.4a1.4 1.4 0 0 1-.99-.417A1.44 1.44 0 0 1 3 15.576V7.84c0-.245.063-.487.182-.701s.29-.393.498-.52l5.6-3.416a1.38 1.38 0 0 1 1.44 0l5.6 3.417A1.43 1.43 0 0 1 17 7.84v7.736c0 .378-.148.74-.41 1.007a1.4 1.4 0 0 1-.99.417z"
fill="currentColor"
/>
</svg>
);
}
export default FilledHomeIcon;
+4 -4
View File
@@ -1,9 +1,9 @@
import { Server } from "../../types/IServer.ts";
import { Server } from "../../types/Server.ts";
import { useEffect, useRef, useState } from "react";
import { IApp } from "../../types/IApp.ts";
import { IApp } from "../../types/App.ts";
import api from "../../utils/api.ts";
import { Session } from "../../types/ISession.ts";
import { Client } from "../../types/IClient.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 NewInput from "../NewInput.tsx";
@@ -3,7 +3,7 @@ import FlashIcon from "../icons/FlashIcon";
import Button from "../Button";
import ChevronRightIcon from "../icons/ChevronRightIcon";
import useModalStore from "../../stores/useModalStore";
import { Session } from "../../types/ISession";
import { Session } from "../../types/Session";
import { useEffect, useState } from "react";
import EndSessionModal from "./EndSessionModal";
+1 -1
View File
@@ -2,7 +2,7 @@ import { useState } from "react";
import NewInput from "../NewInput";
import Button from "../Button";
import useModalStore from "../../stores/useModalStore";
import { Server } from "../../types/IServer";
import { Server } from "../../types/Server";
import { useQueryClient } from "@tanstack/react-query";
import { useMutation } from "@tanstack/react-query";
import api from "../../utils/api";
+1 -1
View File
@@ -1,7 +1,7 @@
import { useMutation } from "@tanstack/react-query";
import { useQueryClient } from "@tanstack/react-query";
import useModalStore from "../../stores/useModalStore";
import { Session } from "../../types/ISession";
import { Session } from "../../types/Session";
import Button from "../Button";
import CurrentSessionModal from "./CurrentSessionModal";
import api from "../../utils/api";
+24 -5
View File
@@ -1,4 +1,4 @@
import { Session } from "../../types/ISession";
import { Session } from "../../types/Session";
import { format } from "date-fns";
import { ru } from "date-fns/locale";
import getIntervalDuration from "../../utils/interval-duration";
@@ -7,11 +7,29 @@ import Button from "../Button";
import ChevronRightIcon from "../icons/ChevronRightIcon";
import Badge from "../Badge";
import ClientCard from "../ClientCard";
import DownloadIcon from "../icons/DownloadIcon";
import ShareIcon from "../icons/ShareIcon";
import SessionComments from "../SessionComments";
import { useQuery } from "@tanstack/react-query";
import api from "../../utils/api";
import { useEffect } from "react";
import SessionFiles from "../SessionFiles";
function SessionModal({ session }: { session: Session }) {
const { data } = useQuery({
queryKey: ["file-list"],
queryFn: () =>
api
.get("files", {
searchParams: {
sessionId: session.id,
},
})
.json<string[]>(),
});
useEffect(() => {
console.log(data);
}, [data]);
return (
<div className="bg-[#FFFFFF] w-[49.722vw] rounded-[2.222vw]">
<div className="w-full flex justify-center items-center h-[4.861vw] border-b-1 border-[#D6D6D6]">
@@ -111,7 +129,7 @@ function SessionModal({ session }: { session: Session }) {
<h3 className="title-s flex items-center font-medium gap-[0.556vw]">
<span>Документы по сеансу</span> <Badge count={4} />
</h3>
<div className="flex w-full gap-[0.556vw]">
{/* <div className="flex w-full gap-[0.556vw]">
<Button variant="primary" size="large" className="w-full">
<span className="w-[1.111vw] h-[1.111vw] text-[#7B60F3]">
<DownloadIcon />
@@ -123,7 +141,8 @@ function SessionModal({ session }: { session: Session }) {
<ShareIcon />
</span>
</Button>
</div>
</div> */}
{data && <SessionFiles files={data} session={session} />}
</div>
</div>
<SessionComments sessionId={session.id} />
+3 -3
View File
@@ -1,10 +1,10 @@
import { useQuery } from "@tanstack/react-query";
import { IUser } from "../types/IUser";
import { IUser } from "../types/User";
import api from "../utils/api";
import { Server } from "../types/IServer";
import { Server } from "../types/Server";
import DesktopCard from "../components/DesktopCard";
import Badge from "../components/Badge";
import { Session } from "../types/ISession";
import { Session } from "../types/Session";
import SessionCard from "../components/SessionCard";
import Button from "../components/Button";
import ChevronRightIcon from "../components/icons/ChevronRightIcon";
+1 -1
View File
@@ -4,7 +4,7 @@ import useAuthStore from "../stores/useAuthStore";
import api from "../utils/api";
import { useState } from "react";
import { HTTPError } from "ky";
import { IError } from "../types/IError";
import { IError } from "../types/Error";
import { useNavigate } from "react-router";
import Button from "../components/Button";
+7 -7
View File
@@ -1,15 +1,15 @@
import { Navigate, Outlet } from 'react-router';
import useAuthStore from '../stores/useAuthStore';
import api from '../utils/api';
import { useQuery } from '@tanstack/react-query';
import { IUser } from '../types/IUser';
import { Navigate, Outlet } from "react-router";
import useAuthStore from "../stores/useAuthStore";
import api from "../utils/api";
import { useQuery } from "@tanstack/react-query";
import { IUser } from "../types/User";
function ProtectedPage() {
const { token } = useAuthStore();
const { data: user, isLoading } = useQuery({
queryKey: ['me'],
queryFn: () => api.get('auth/me').json<IUser>(),
queryKey: ["me"],
queryFn: () => api.get("auth/me").json<IUser>(),
enabled: !!token,
});
+3 -3
View File
@@ -1,11 +1,11 @@
import { useQuery } from "@tanstack/react-query";
import api from "../utils/api";
import { IUser } from "../types/IUser";
import { Session } from "../types/ISession";
import { IUser } from "../types/User";
import { Session } from "../types/Session";
import Input from "../components/Input";
import Select from "../components/Select";
import { useState } from "react";
import { IApp } from "../types/IApp";
import { IApp } from "../types/App";
import { useDebounce } from "@uidotdev/usehooks";
import SessionCard from "../components/SessionCard";
import { groupByCreatedAt } from "../utils/groupByCreatedAt";
+1 -1
View File
@@ -1,4 +1,4 @@
import { IApp } from "../types/IApp.ts";
import { IApp } from "../types/App.ts";
import api from "../utils/api.ts";
import { useQuery } from "@tanstack/react-query";
+1 -1
View File
@@ -1,4 +1,4 @@
import { Server } from "../types/IServer.ts";
import { Server } from "../types/Server.ts";
import api from "../utils/api.ts";
import { useQuery } from "@tanstack/react-query";
@@ -1,6 +1,6 @@
import { IApp } from "./IApp";
import { Server } from "./IServer";
import { IUser } from "./IUser";
import { IApp } from "./App";
import { Server } from "./Server";
import { IUser } from "./User";
export interface ICompany {
id: string;
+3 -2
View File
@@ -1,5 +1,5 @@
import { IApp as App } from "./IApp";
import { Session } from "./ISession";
import { IApp as App } from "./App";
import { Session } from "./Session";
export interface Server {
id: string;
@@ -10,4 +10,5 @@ export interface Server {
sessions?: Session[];
apps?: App[];
status: "online" | "offline";
ipAddress: string;
}
@@ -1,8 +1,8 @@
import { IApp as App } from "./IApp";
import { Comment } from "./IComments";
import { IOwner as Owner } from "./IOwner";
import { Server } from "./IServer";
import { Client } from "./IClient";
import { IApp as App } from "./App";
import { Comment } from "./Comments";
import { IOwner as Owner } from "./Owner";
import { Server } from "./Server";
import { Client } from "./Client";
export interface Session {
id: string;
+1 -1
View File
@@ -1,4 +1,4 @@
import { ICompany } from "./ICompany";
import { ICompany } from "./Company";
export interface IUser {
id: string;