ui fixes
This commit is contained in:
Binary file not shown.
|
After Width: | Height: | Size: 16 KiB |
@@ -1,6 +1,6 @@
|
||||
function Badge({ count }: { count: number }) {
|
||||
return (
|
||||
<div className="size-[1.389vw] rounded-full bg-[#F8F7FE] caption-xs text-[#7B60F3] flex justify-center items-center font-medium font-mono">
|
||||
<div className="size-[1.389vw] rounded-full bg-[#F8F7FE] caption-xs text-[#7B60F3] flex justify-center items-center font-medium afont-mono">
|
||||
{count}
|
||||
</div>
|
||||
);
|
||||
|
||||
@@ -34,8 +34,8 @@ export default function DesktopCard({ server }: IDesktopCardProps) {
|
||||
>
|
||||
<Button
|
||||
variant="secondary"
|
||||
size="medium"
|
||||
className="absolute top-[0.347vw] right-[0.347vw] cursor-pointer flex items-center justify-center"
|
||||
size="small"
|
||||
className="absolute top-[0.556vw] right-[0.556vw] cursor-pointer flex items-center justify-center"
|
||||
onClick={() => setModal(<EditTable table={server} />)}
|
||||
>
|
||||
<span className="text-[#7D7D7D] w-[0.972vw] h-[0.972vw]">
|
||||
@@ -84,7 +84,10 @@ export default function DesktopCard({ server }: IDesktopCardProps) {
|
||||
</Button>
|
||||
</div>
|
||||
) : server.status === "offline" ? (
|
||||
<Button variant="critical" className="hover:bg-[#FEF3F2]">
|
||||
<Button
|
||||
variant="critical"
|
||||
className="hover:bg-[#FEF3F2] !cursor-default"
|
||||
>
|
||||
<span className="text-[#FF4517] size-[0.972vw]">
|
||||
<UnlinkIcon />
|
||||
</span>
|
||||
|
||||
@@ -14,8 +14,7 @@ function SearchInput(
|
||||
return (
|
||||
<div
|
||||
className={clsx(
|
||||
"p-[0.556vw] bg-[#F6F6F6] rounded-[0.833vw] w-full flex items-center gap-[1.111vw] hover:bg-[#F0F0F0]",
|
||||
!props.onEnter && "px-[1.111vw] py-[1.215vw]"
|
||||
"p-[0.556vw] bg-[#F6F6F6] rounded-[0.833vw] w-full flex items-center gap-[1.111vw] hover:bg-[#F0F0F0] px-[1.111vw] py-[1.215vw]"
|
||||
)}
|
||||
>
|
||||
<div className="flex gap-[0.566vw] items-center flex-1">
|
||||
@@ -51,11 +50,11 @@ function SearchInput(
|
||||
<CloseIcon />
|
||||
</div>
|
||||
</button>
|
||||
{props.onEnter && (
|
||||
{/* {pr ops.onEnter && (
|
||||
<Button size="small" disabled={!props.value} onClick={props.onEnter}>
|
||||
Искать
|
||||
</Button>
|
||||
)}
|
||||
)} */}
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import { motion } from "motion/react";
|
||||
import { Comment } from "../types/Comments";
|
||||
import { Comment } from "../types/Comment";
|
||||
import { format } from "date-fns";
|
||||
|
||||
function SessionCommentItem({ comment }: { comment: Comment }) {
|
||||
|
||||
@@ -1,11 +1,14 @@
|
||||
import { useRef } from "react";
|
||||
import { useRef, useState } from "react";
|
||||
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/Comments";
|
||||
import { Comment } from "../types/Comment";
|
||||
import SessionCommentItem from "./SessionCommentItem";
|
||||
import { AnimatePresence } from "motion/react";
|
||||
import { AnimatePresence, motion } from "motion/react";
|
||||
import { groupByCreatedAt } from "../utils/groupByCreatedAt";
|
||||
import { format, isToday } from "date-fns";
|
||||
import { ru } from "date-fns/locale";
|
||||
|
||||
function SessionComments({ sessionId }: { sessionId: string }) {
|
||||
const textareaRef = useRef<HTMLTextAreaElement>(null);
|
||||
@@ -49,9 +52,10 @@ function SessionComments({ sessionId }: { sessionId: string }) {
|
||||
|
||||
const queryClient = useQueryClient();
|
||||
|
||||
const { data: comments } = useQuery({
|
||||
const { data: grouppedComments } = useQuery({
|
||||
queryKey: ["sessions", "comments", sessionId],
|
||||
queryFn: () => api.get(`comments/${sessionId}`).json<Comment[]>(),
|
||||
select: groupByCreatedAt,
|
||||
});
|
||||
|
||||
const { mutate: createComment } = useMutation({
|
||||
@@ -76,6 +80,7 @@ function SessionComments({ sessionId }: { sessionId: string }) {
|
||||
|
||||
createComment(textarea.value);
|
||||
textarea.value = "";
|
||||
setValue("");
|
||||
handleTextareaInput();
|
||||
};
|
||||
|
||||
@@ -86,13 +91,26 @@ function SessionComments({ sessionId }: { sessionId: string }) {
|
||||
}
|
||||
};
|
||||
|
||||
const [value, setValue] = useState("");
|
||||
|
||||
return (
|
||||
<div className="outline flex flex-col flex-1 outline-[#D6D6D6]">
|
||||
<div className="relative h-full flex flex-col-reverse gap-[0.833vw] overflow-y-auto p-[1.111vw] [scrollbar-width:thin]">
|
||||
<AnimatePresence mode="wait">
|
||||
{comments && comments.length > 0 ? (
|
||||
comments.map((comment) => (
|
||||
<SessionCommentItem key={comment.id} comment={comment} />
|
||||
{grouppedComments && grouppedComments.length > 0 ? (
|
||||
grouppedComments.map(([timestamp, comments], index) => (
|
||||
<motion.div layout className="space-y-[1.111vw]" key={index}>
|
||||
<p className="text-center text-[#BDBDBD] caption-s">
|
||||
{isToday(new Date(timestamp))
|
||||
? "Сегодня"
|
||||
: format(new Date(timestamp), "d MMMM", { locale: ru })}
|
||||
</p>
|
||||
<div className="flex flex-col-reverse gap-[0.833vw]">
|
||||
{comments.map((comment) => (
|
||||
<SessionCommentItem key={comment.id} comment={comment} />
|
||||
))}
|
||||
</div>
|
||||
</motion.div>
|
||||
))
|
||||
) : (
|
||||
<div className="flex flex-col gap-[1.111vw] items-center justify-center h-full">
|
||||
@@ -124,6 +142,9 @@ function SessionComments({ sessionId }: { sessionId: string }) {
|
||||
name="comment"
|
||||
className="w-[17.083vw] outline-none text-s resize-none self-center"
|
||||
placeholder="Расскажите, как все прошло"
|
||||
onChange={(e) => {
|
||||
setValue(e.target.value);
|
||||
}}
|
||||
style={{
|
||||
wordWrap: "break-word",
|
||||
overflowY: "hidden",
|
||||
@@ -131,8 +152,8 @@ function SessionComments({ sessionId }: { sessionId: string }) {
|
||||
onInput={handleTextareaInput}
|
||||
onKeyDown={handleKeyDown}
|
||||
/>
|
||||
<Button variant="cta" size="large" type="submit">
|
||||
<span className="w-[1.111vw] h-[1.111vw] text-white">
|
||||
<Button variant="cta" size="large" type="submit" disabled={!value}>
|
||||
<span className="size-[1.111vw] text-white">
|
||||
<SendIcon />
|
||||
</span>
|
||||
</Button>
|
||||
|
||||
@@ -1,8 +1,9 @@
|
||||
function StartSessionIcon() {
|
||||
return (
|
||||
<svg viewBox="0 0 7 8" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<svg viewBox="0 0 20 20" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<rect width={20} height={20} rx={10} fill="#fff" fillOpacity={0.1} />
|
||||
<path
|
||||
d="M5.938 3.595a.5.5 0 0 1 0 .81L1.293 7.758A.5.5 0 0 1 .5 7.353V.647a.5.5 0 0 1 .793-.405z"
|
||||
d="M13.438 9.595a.5.5 0 0 1 0 .81l-4.645 3.353A.5.5 0 0 1 8 13.353V6.647a.5.5 0 0 1 .793-.405z"
|
||||
fill="currentColor"
|
||||
/>
|
||||
</svg>
|
||||
|
||||
@@ -226,26 +226,26 @@ export default function CreateSessionModal({ targetServerId }: Props) {
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
<Button
|
||||
type="submit"
|
||||
disabled={
|
||||
!ref.current?.checkValidity() ||
|
||||
!selectedServer ||
|
||||
!selectedApp ||
|
||||
servers?.find((server) => server.id === selectedServer?.id)
|
||||
?.sessions?.[0]?.status === "ending"
|
||||
}
|
||||
variant="cta"
|
||||
size="large"
|
||||
className="sticky bottom-0"
|
||||
>
|
||||
<div className="rounded-full bg-[#9184F6] in-disabled:bg-transparent px-[0.278vw] py-[0.208vw] size-[1.111vw]">
|
||||
<div className="w-[0.694vw] h-[0.556vw]">
|
||||
<div className="flex-1 flex flex-col justify-end">
|
||||
<Button
|
||||
type="submit"
|
||||
disabled={
|
||||
!ref.current?.checkValidity() ||
|
||||
!selectedServer ||
|
||||
!selectedApp ||
|
||||
servers?.find((server) => server.id === selectedServer?.id)
|
||||
?.sessions?.[0]?.status === "ending"
|
||||
}
|
||||
variant="cta"
|
||||
size="large"
|
||||
className="sticky bottom-0"
|
||||
>
|
||||
<div className="size-[1.111vw]">
|
||||
<StartSessionIcon />
|
||||
</div>
|
||||
</div>
|
||||
<p>Запустить сеанс</p>
|
||||
</Button>
|
||||
<span>Запустить сеанс</span>
|
||||
</Button>
|
||||
</div>
|
||||
</div>
|
||||
</form>
|
||||
);
|
||||
|
||||
@@ -18,7 +18,7 @@ function EditTable({ table }: { table: Server }) {
|
||||
return api.put(`servers/${table.id}`, {
|
||||
json: {
|
||||
name: tableName,
|
||||
location: tableDescription,
|
||||
description: tableDescription,
|
||||
},
|
||||
});
|
||||
},
|
||||
|
||||
+54
-25
@@ -12,6 +12,7 @@ import { ru } from "date-fns/locale";
|
||||
import MultySelect from "../components/MultySelect";
|
||||
import Button from "../components/Button";
|
||||
import SearchInput from "../components/SearchInput";
|
||||
import CloseIcon from "../components/icons/CloseIcon";
|
||||
|
||||
function SessionsPage() {
|
||||
const [limit, setLimit] = useState(10);
|
||||
@@ -69,6 +70,12 @@ function SessionsPage() {
|
||||
enabled: !!me,
|
||||
});
|
||||
|
||||
function reset() {
|
||||
setSearch("");
|
||||
setAppIds([]);
|
||||
setManagerIds([]);
|
||||
}
|
||||
|
||||
return (
|
||||
<div className="py-[1.667vw] flex flex-col gap-[1.667vw]">
|
||||
<h1 className="title-l font-medium">Сеансы</h1>
|
||||
@@ -103,40 +110,62 @@ function SessionsPage() {
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
{!!count && (
|
||||
<div className="flex justify-between items-center">
|
||||
<p className="caption-m font-medium opacity-40">
|
||||
Найдено {count} сеансов
|
||||
</p>
|
||||
)}
|
||||
<button className="flex gap-[0.278vw] items-center" onClick={reset}>
|
||||
<div className="size-[1.111vw] text-[#7D7D7D]">
|
||||
<CloseIcon />
|
||||
</div>
|
||||
<p className="text-[#7D7D7D] text-[0.972vw] font-medium">
|
||||
Сбросить все
|
||||
</p>
|
||||
</button>
|
||||
</div>
|
||||
</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} />
|
||||
))}
|
||||
{grouppedSessions?.length ? (
|
||||
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 className="m-auto flex flex-col items-center gap-[1.111vw] w-[18.333vw] mt-[6.111vw]">
|
||||
<img src="/images/sad_ghost.png" alt="" className="w-[13.889vw]" />
|
||||
<div className="space-y-[0.556vw]">
|
||||
<p className="text-center font-medium title-m">Ничего не нашли</p>
|
||||
<p className="text-[#BDBDBD] caption-s font-medium">
|
||||
Попробуйте изменить параметры поиска
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
))}
|
||||
)}
|
||||
</div>
|
||||
<Button
|
||||
size="large"
|
||||
variant="primary"
|
||||
className="w-full"
|
||||
onClick={() => {
|
||||
setLimit((prev) => prev + 10);
|
||||
}}
|
||||
disabled={!!count && limit >= count}
|
||||
>
|
||||
Показать еще
|
||||
</Button>
|
||||
{!!count && (
|
||||
<Button
|
||||
size="large"
|
||||
variant="primary"
|
||||
className="w-full"
|
||||
onClick={() => {
|
||||
setLimit((prev) => prev + 10);
|
||||
}}
|
||||
disabled={!!count && limit >= count}
|
||||
>
|
||||
Показать еще
|
||||
</Button>
|
||||
)}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import { IApp as App } from "./App";
|
||||
import { Comment } from "./Comments";
|
||||
import { Comment } from "./Comment";
|
||||
import { IOwner as Owner } from "./Owner";
|
||||
import { Server } from "./Server";
|
||||
import { Client } from "./Client";
|
||||
|
||||
@@ -1,8 +1,10 @@
|
||||
export function groupByCreatedAt<T extends { createdAt: Date }>(items: T[]) {
|
||||
return items.reduce((acc, session) => {
|
||||
const date = session.createdAt.toString().split("T")[0];
|
||||
acc[date] = acc[date] || [];
|
||||
acc[date].push(session);
|
||||
return acc;
|
||||
}, {} as Record<string, T[]>);
|
||||
return Object.entries(
|
||||
items.reduce((acc, session) => {
|
||||
const date = session.createdAt.toString().split("T")[0];
|
||||
acc[date] = acc[date] || [];
|
||||
acc[date].push(session);
|
||||
return acc;
|
||||
}, {} as Record<string, T[]>)
|
||||
);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user