From 3f11fcd8299e692377352b39bffbb62ff4405b69 Mon Sep 17 00:00:00 2001 From: c00b3r Date: Fri, 20 Jun 2025 15:07:00 +0500 Subject: [PATCH 01/10] fix: update StartSessionIcon color and adjust text color in CreateSessionModal --- src/components/icons/StartSessionIcon.tsx | 2 +- src/components/modals/CreateSessionModal.tsx | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/components/icons/StartSessionIcon.tsx b/src/components/icons/StartSessionIcon.tsx index 3c9cf7c..0be6698 100644 --- a/src/components/icons/StartSessionIcon.tsx +++ b/src/components/icons/StartSessionIcon.tsx @@ -4,7 +4,7 @@ function StartSessionIcon() { ); diff --git a/src/components/modals/CreateSessionModal.tsx b/src/components/modals/CreateSessionModal.tsx index 3696ae1..8081ff2 100644 --- a/src/components/modals/CreateSessionModal.tsx +++ b/src/components/modals/CreateSessionModal.tsx @@ -288,7 +288,7 @@ export default function CreateSessionModal({ targetServerId, client }: Props) { variant="cta" size="large" > -
+
Запустить сеанс From 4c2369ce8848d65867bd2e9015466bb88a35b177 Mon Sep 17 00:00:00 2001 From: inmake Date: Mon, 23 Jun 2025 18:13:00 +0500 Subject: [PATCH 02/10] refactor: rename apps to appsToServers in Server type and update CreateSessionModal to reflect changes in session handling and state management --- src/components/modals/CreateSessionModal.tsx | 130 ++++++++++--------- src/types/Server.ts | 2 +- 2 files changed, 68 insertions(+), 64 deletions(-) diff --git a/src/components/modals/CreateSessionModal.tsx b/src/components/modals/CreateSessionModal.tsx index 8081ff2..a946528 100644 --- a/src/components/modals/CreateSessionModal.tsx +++ b/src/components/modals/CreateSessionModal.tsx @@ -24,7 +24,7 @@ export default function CreateSessionModal({ targetServerId, client }: Props) { const [name, setName] = useState(client?.name || null); const [phone, setPhone] = useState(client?.phone || null); const [email, setEmail] = useState(client?.email || null); - // const [isSessionExists, setIsSessionExists] = useState(false); + const [isSessionExists, setIsSessionExists] = useState(false); const queryClient = useQueryClient(); @@ -46,7 +46,7 @@ export default function CreateSessionModal({ targetServerId, client }: Props) { useEffect(() => { setSelectedApp( selectedServer?.sessions?.[0]?.app || - selectedServer?.apps?.[0].app || + selectedServer?.appsToServers?.[0]?.app || null ); }, [selectedServer]); @@ -114,70 +114,74 @@ export default function CreateSessionModal({ targetServerId, client }: Props) { }, }); - // const { mutate: endSession } = useMutation({ - // mutationKey: ["end-session", selectedServer?.sessions?.[0]?.id], - // mutationFn: () => - // api.put(`sessions/${selectedServer?.sessions?.[0]?.id}`, { - // json: { status: "ending" }, - // }), - // onMutate: () => queryClient.invalidateQueries({ queryKey: ["sessions"] }), - // }); + const { mutate: endSession } = useMutation({ + mutationKey: ["end-session", selectedServer?.sessions?.[0]?.id], + mutationFn: () => + api.put(`sessions/${selectedServer?.sessions?.[0]?.id}`, { + json: { status: "ending" }, + }), + onMutate: () => queryClient.invalidateQueries({ queryKey: ["sessions"] }), + }); async function handleClickCreateSession(e: React.FormEvent) { e.preventDefault(); if (!name || !phone || !selectedServer || !selectedApp) return; - // if (selectedServer?.sessions?.[0]?.status !== "started") { - createClient(undefined, { - onSuccess: (client) => { - createSession({ - clientId: client.id, - serverId: selectedServer.id, - appId: selectedApp.id, - }); + 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; + } + + endSession(undefined, { + onError: (error) => { + console.log("Ошибка при завершении сессии:", error); }, }); - // return; - // } - - // if (!isSessionExists) { - // setIsSessionExists(true); - // return; - // } - - // endSession(undefined, { - // onError: (error) => { - // console.log("Ошибка при завершении сессии:", error); - // }, - // }); } - // useEffect(() => { - // if ( - // selectedServer && - // servers?.find((server) => server.id === selectedServer?.id)?.sessions?.[0] - // ?.status === "ended" && - // selectedApp - // // && isSessionExists - // ) - // createClient(undefined, { - // onSuccess: (client) => { - // createSession({ - // clientId: client.id, - // serverId: selectedServer?.id, - // appId: selectedApp.id, - // }); - // }, - // }); - // }, [ - // selectedApp, - // servers, - // createClient, - // createSession, - // // isSessionExists, - // selectedServer, - // ]); + useEffect(() => { + if ( + selectedServer && + servers?.find((server) => server.id === selectedServer?.id)?.sessions?.[0] + ?.status === "ended" && + selectedApp && + isSessionExists + ) + createClient(undefined, { + onSuccess: (client) => { + createSession({ + clientId: client.id, + serverId: selectedServer?.id, + appId: selectedApp.id, + }); + }, + }); + }, [ + selectedApp, + servers, + createClient, + createSession, + isSessionExists, + selectedServer, + ]); + + useEffect(() => { + console.log(selectedServer); + }, [selectedServer]); const ref = useRef(null); @@ -244,22 +248,22 @@ export default function CreateSessionModal({ targetServerId, client }: Props) {

Выберите параметры сеанса

{selectedServer && - selectedServer?.apps && - selectedServer?.apps?.length > 0 && ( + selectedServer?.appsToServers && + selectedServer.appsToServers?.length > 0 && ( app)} + projects={selectedServer?.appsToServers.map(({ app }) => app)} selectedProject={selectedApp} setSelectedProject={setSelectedApp} /> )}
- {/* {isSessionExists && ( */} - {/*
+ {isSessionExists && ( +
ghost error
-
*/} - {/* )} */} +
+ )}
{isOpen && ( -
+
-
-
+
+
{projects.map((project) => ( + {managerIds.length === 0 && appIds.length === 0 ? null : ( + + )}
From c82f1dfbb512cb6517bf293dbf6d69a251a09145 Mon Sep 17 00:00:00 2001 From: c00b3r Date: Wed, 25 Jun 2025 17:10:02 +0500 Subject: [PATCH 05/10] feat: implement client search hook for phone input validation in CreateSessionModal --- src/components/Selector.tsx | 3 ++ src/components/modals/ClientModal.tsx | 12 +++-- src/components/modals/CreateSessionModal.tsx | 33 ++++++-------- src/hooks/useClientSearch.tsx | 47 ++++++++++++++++++++ 4 files changed, 73 insertions(+), 22 deletions(-) create mode 100644 src/components/Selector.tsx create mode 100644 src/hooks/useClientSearch.tsx diff --git a/src/components/Selector.tsx b/src/components/Selector.tsx new file mode 100644 index 0000000..c61c08f --- /dev/null +++ b/src/components/Selector.tsx @@ -0,0 +1,3 @@ +export default function Selector() { + return
Selector
; +} diff --git a/src/components/modals/ClientModal.tsx b/src/components/modals/ClientModal.tsx index 758cdd3..f3adbee 100644 --- a/src/components/modals/ClientModal.tsx +++ b/src/components/modals/ClientModal.tsx @@ -25,7 +25,14 @@ function ClientModal({ client }: { client: Client }) { const { mutate: updateClientData, isPending } = useMutation({ mutationKey: ["clients", client.id], - mutationFn: () => api.put(`clients/${client.id}`, { json: clientData }), + mutationFn: () => + api.put(`clients/${client.id}`, { + json: { + name: clientData.name, + phone: clientData.phone.replace(/\D/g, ""), + email: clientData.email, + }, + }), onSuccess: () => { queryClient.invalidateQueries({ queryKey: ["clients"] }); }, @@ -58,7 +65,6 @@ function ClientModal({ client }: { client: Client }) { onChange={(e) => { setClientData({ ...clientData, name: e.target.value }); }} - className="relative" required > { setClientData({ ...clientData, phone: e.target.value }); }} - className="relative" required + mask="+7 (999) 999-99-99" > (client?.name || null); const [phone, setPhone] = useState(client?.phone || null); const [email, setEmail] = useState(client?.email || null); + const [isFullPhone, setIsFullPhone] = useState(false); const [isSessionExists, setIsSessionExists] = useState(false); const queryClient = useQueryClient(); @@ -43,6 +44,8 @@ export default function CreateSessionModal({ targetServerId, client }: Props) { ); const [selectedApp, setSelectedApp] = useState(null); + const { data, isLoading, error } = useClientSearch(phone); + useEffect(() => { setSelectedApp( selectedServer?.sessions?.[0]?.app || @@ -51,28 +54,15 @@ export default function CreateSessionModal({ targetServerId, client }: Props) { ); }, [selectedServer]); - const debouncedPhone = useDebounce(phone, 500); - - const { data, isLoading, error } = useQuery({ - queryKey: ["get-user-by-phone", debouncedPhone], - queryFn: () => - api - .get("clients/by-phone", { - searchParams: debouncedPhone ? { phone: debouncedPhone } : {}, - }) - .json(), - enabled: !!debouncedPhone, - }); - useEffect(() => { if (!error && data) { setName(data.name); setEmail(data.email); } else { - setName(null); - setEmail(null); + setName(isFullPhone ? name : null); + setEmail(isFullPhone ? email : null); } - }, [data, error]); + }, [data, error, isFullPhone, name, email]); const { mutate: createClient } = useMutation({ mutationFn: () => @@ -206,14 +196,19 @@ export default function CreateSessionModal({ targetServerId, client }: Props) {
setPhone(e.target.value)} + onChange={(e) => { + setPhone(e.target.value); + if (e.target.value.replace(/\D/g, "").length === 11) { + setIsFullPhone(true); + } + }} placeholder="Номер телефона" mask="+7 (999) 999-99-99" required isLoading={isLoading} /> - {phone && ( + {isFullPhone && ( <> { + setIsSearching(isPhoneComplete); + }, [isPhoneComplete]); + + const debouncedPhone = useDebounce(phone, 500); + + const { data, isLoading, error } = useQuery({ + queryKey: ["get-user-by-phone", debouncedPhone], + queryFn: () => + api + .get("clients/by-phone", { + searchParams: + debouncedPhone && debouncedPhone.replace(/\D/g, "").length === 11 + ? { phone: debouncedPhone.replace(/\D/g, "") } + : {}, + }) + .json(), + enabled: Boolean(debouncedPhone && isPhoneComplete), + }); + + useEffect(() => { + if (!isLoading && isSearching) { + setIsSearching(false); + } + }, [isLoading, isSearching]); + + return { + data, + isLoading: isSearching || isLoading, + error, + }; +} + +export default useClientSearch; From fc7d55b10f1b16a3c0a319a7bcde7f29d01f6c6d Mon Sep 17 00:00:00 2001 From: c00b3r Date: Fri, 27 Jun 2025 13:38:32 +0500 Subject: [PATCH 06/10] feat: add ManagerSelect component and integrate it into CreateSessionModal, replacing the deprecated Selector component --- src/components/ManagerSelect.tsx | 114 +++++++++++++++++++ src/components/Selector.tsx | 3 - src/components/icons/SortIcon.tsx | 16 +++ src/components/modals/CreateSessionModal.tsx | 61 ++++++++++ src/pages/DashboardPage.tsx | 1 + 5 files changed, 192 insertions(+), 3 deletions(-) create mode 100644 src/components/ManagerSelect.tsx delete mode 100644 src/components/Selector.tsx create mode 100644 src/components/icons/SortIcon.tsx diff --git a/src/components/ManagerSelect.tsx b/src/components/ManagerSelect.tsx new file mode 100644 index 0000000..71eff34 --- /dev/null +++ b/src/components/ManagerSelect.tsx @@ -0,0 +1,114 @@ +import { useEffect, useState } from "react"; +import { Manager } from "../types/Manager"; +import SortIcon from "./icons/SortIcon"; +import clsx from "clsx"; +import CheckIcon from "./icons/CheckIcon"; +import { useClickAway } from "@uidotdev/usehooks"; +import { AnimatePresence, motion } from "motion/react"; + +function ManagerSelect({ + placeholder, + data, +}: { + placeholder: string; + data: Manager[]; +}) { + const [isOpen, setIsOpen] = useState(false); + const [selectedManager, setSelectedManager] = useState( + data[0] || null + ); + const [position, setPosition] = useState<"top" | "bottom">("bottom"); + const selectRef = useClickAway(() => setIsOpen(false)); + + useEffect(() => { + const rect = selectRef.current?.getBoundingClientRect(); + if (rect) { + setPosition(rect.top > window.innerHeight / 2 ? "top" : "bottom"); + } + }, [isOpen, selectRef]); + + useEffect(() => { + const handleScroll = () => { + if (isOpen) { + const rect = selectRef.current?.getBoundingClientRect(); + if (rect) { + setPosition(rect.top > window.innerHeight / 2 ? "top" : "bottom"); + } + } + }; + + window.addEventListener("scroll", handleScroll, true); + return () => window.removeEventListener("scroll", handleScroll, true); + }, [isOpen, selectRef]); + + const handleToggle = () => { + setIsOpen(!isOpen); + }; + + return ( +
+
+
+ {placeholder} +
+
+
+
+ {selectedManager?.fullname || data[0].fullname} +
+
+
+ + + + + {isOpen && ( + +
+ {data.map((item) => ( +
{ + e.stopPropagation(); + setSelectedManager(item); + setIsOpen(false); + }} + > +
+ {selectedManager?.id === item.id && } +
+
+
{item.fullname}
+
+ ))} +
+ + )} + +
+ ); +} + +export default ManagerSelect; diff --git a/src/components/Selector.tsx b/src/components/Selector.tsx deleted file mode 100644 index c61c08f..0000000 --- a/src/components/Selector.tsx +++ /dev/null @@ -1,3 +0,0 @@ -export default function Selector() { - return
Selector
; -} diff --git a/src/components/icons/SortIcon.tsx b/src/components/icons/SortIcon.tsx new file mode 100644 index 0000000..6560e79 --- /dev/null +++ b/src/components/icons/SortIcon.tsx @@ -0,0 +1,16 @@ +function SortIcon() { + return ( + + + + ); +} + +export default SortIcon; diff --git a/src/components/modals/CreateSessionModal.tsx b/src/components/modals/CreateSessionModal.tsx index 8e5ffe8..0c59ce5 100644 --- a/src/components/modals/CreateSessionModal.tsx +++ b/src/components/modals/CreateSessionModal.tsx @@ -13,6 +13,7 @@ import ProjectSelector from "../ProjectSelector.tsx"; import { useQueryClient, useMutation, useQuery } from "@tanstack/react-query"; import { AnimatePresence, motion } from "motion/react"; import useClientSearch from "../../hooks/useClientSearch.tsx"; +import ManagerSelect from "../ManagerSelect.tsx"; interface Props { targetServerId: string | null; @@ -207,6 +208,65 @@ export default function CreateSessionModal({ targetServerId, client }: Props) { required isLoading={isLoading} /> + {isFullPhone && ( <> @@ -258,6 +318,7 @@ export default function CreateSessionModal({ targetServerId, client }: Props) { /> )}
+ {isSessionExists && (
+

Последние сеансы

From e8060c594d7d59bec87741732963facbd359fd53 Mon Sep 17 00:00:00 2001 From: c00b3r Date: Fri, 27 Jun 2025 17:32:11 +0500 Subject: [PATCH 07/10] feat: update ManagerSelect positioning and enhance ProjectSelector with dynamic positioning and animations --- src/components/ManagerSelect.tsx | 4 +- src/components/MultySelect.tsx | 5 +- src/components/ProjectSelector.tsx | 193 ++++++++----------- src/components/modals/CreateSessionModal.tsx | 70 +------ 4 files changed, 98 insertions(+), 174 deletions(-) diff --git a/src/components/ManagerSelect.tsx b/src/components/ManagerSelect.tsx index 71eff34..8e1ffe1 100644 --- a/src/components/ManagerSelect.tsx +++ b/src/components/ManagerSelect.tsx @@ -78,8 +78,8 @@ function ManagerSelect({ className={clsx( "absolute left-0 w-full z-10", position === "top" - ? "top-[calc(-11.389vw-0.278vw)]" - : "top-[calc(100%+0.278vw)]" + ? "bottom-[calc(100%+4px)]" + : "top-[calc(100%+4px)]" )} >
({ - {isOpen && ( -
-
- -

Смена проекта

-
+ + + + + {isOpen && ( + +
- - - - -
-
-
{projects.map((project) => ( - + +
+
{project.name}
+ {activeProject && project.name === activeProject.name && ( + + )} +
+
))}
-
- - -
-
-
- )} - + + )} + +
); } diff --git a/src/components/modals/CreateSessionModal.tsx b/src/components/modals/CreateSessionModal.tsx index 0c59ce5..79ded1b 100644 --- a/src/components/modals/CreateSessionModal.tsx +++ b/src/components/modals/CreateSessionModal.tsx @@ -14,6 +14,7 @@ import { useQueryClient, useMutation, useQuery } from "@tanstack/react-query"; import { AnimatePresence, motion } from "motion/react"; import useClientSearch from "../../hooks/useClientSearch.tsx"; import ManagerSelect from "../ManagerSelect.tsx"; +import { Manager } from "../../types/Manager.ts"; interface Props { targetServerId: string | null; @@ -36,6 +37,11 @@ export default function CreateSessionModal({ targetServerId, client }: Props) { refetchInterval: 1000, }); + const { data: managers } = useQuery({ + queryKey: ["managers"], + queryFn: () => api.get("managers").json(), + }); + const targetServer = targetServerId ? servers?.find((server) => server.id === targetServerId) || null : null; @@ -170,10 +176,6 @@ export default function CreateSessionModal({ targetServerId, client }: Props) { selectedServer, ]); - useEffect(() => { - console.log(selectedServer); - }, [selectedServer]); - const ref = useRef(null); return ( @@ -208,65 +210,6 @@ export default function CreateSessionModal({ targetServerId, client }: Props) { required isLoading={isLoading} /> - {isFullPhone && ( <> @@ -303,6 +246,7 @@ export default function CreateSessionModal({ targetServerId, client }: Props) {

Выберите параметры сеанса

+ {selectedServer && selectedServer?.appsToServers && selectedServer.appsToServers?.length > 0 && ( From fc1858a6edf3be2d795ab618799380d00ef6d342 Mon Sep 17 00:00:00 2001 From: c00b3r Date: Fri, 27 Jun 2025 17:59:06 +0500 Subject: [PATCH 08/10] feat: refactor ManagerSelect and ProjectSelector for improved positioning logic and integrate new utility function --- src/components/ManagerSelect.tsx | 13 +++----- src/components/ProjectSelector.tsx | 35 +++++++++++--------- src/components/modals/CreateSessionModal.tsx | 34 ++++++++++++------- src/utils/getPositionAbove.ts | 11 ++++++ 4 files changed, 58 insertions(+), 35 deletions(-) create mode 100644 src/utils/getPositionAbove.ts diff --git a/src/components/ManagerSelect.tsx b/src/components/ManagerSelect.tsx index 8e1ffe1..918f71b 100644 --- a/src/components/ManagerSelect.tsx +++ b/src/components/ManagerSelect.tsx @@ -5,6 +5,7 @@ import clsx from "clsx"; import CheckIcon from "./icons/CheckIcon"; import { useClickAway } from "@uidotdev/usehooks"; import { AnimatePresence, motion } from "motion/react"; +import { getPositionAbove } from "../utils/getPositionAbove"; function ManagerSelect({ placeholder, @@ -21,19 +22,15 @@ function ManagerSelect({ const selectRef = useClickAway(() => setIsOpen(false)); useEffect(() => { - const rect = selectRef.current?.getBoundingClientRect(); - if (rect) { - setPosition(rect.top > window.innerHeight / 2 ? "top" : "bottom"); - } + if (!isOpen || !selectRef.current) return; + + setPosition(getPositionAbove(selectRef) ? "top" : "bottom"); }, [isOpen, selectRef]); useEffect(() => { const handleScroll = () => { if (isOpen) { - const rect = selectRef.current?.getBoundingClientRect(); - if (rect) { - setPosition(rect.top > window.innerHeight / 2 ? "top" : "bottom"); - } + setPosition(getPositionAbove(selectRef) ? "top" : "bottom"); } }; diff --git a/src/components/ProjectSelector.tsx b/src/components/ProjectSelector.tsx index 889d8ae..fc6ff96 100644 --- a/src/components/ProjectSelector.tsx +++ b/src/components/ProjectSelector.tsx @@ -1,10 +1,12 @@ import { useEffect, useState } from "react"; import { App } from "../types/App"; -import ChevronDownIcon from "./icons/ChevronDownIcon"; import CheckIcon from "./icons/CheckIcon"; import { useClickAway } from "@uidotdev/usehooks"; import { AnimatePresence, motion } from "motion/react"; import clsx from "clsx"; +import FlashIcon from "./icons/FlashIcon"; +import SortIcon from "./icons/SortIcon"; +import { getPositionAbove } from "../utils/getPositionAbove"; interface Props { projects: App[]; @@ -24,19 +26,13 @@ function ProjectSelector({ const selectRef = useClickAway(() => setIsOpen(false)); useEffect(() => { - const rect = selectRef.current?.getBoundingClientRect(); - if (rect) { - setPosition(rect.top > window.innerHeight / 2 ? "top" : "bottom"); - } + setPosition(getPositionAbove(selectRef) ? "top" : "bottom"); }, [isOpen, selectRef]); useEffect(() => { const handleScroll = () => { if (isOpen) { - const rect = selectRef.current?.getBoundingClientRect(); - if (rect) { - setPosition(rect.top > window.innerHeight / 2 ? "top" : "bottom"); - } + setPosition(getPositionAbove(selectRef) ? "top" : "bottom"); } }; @@ -61,13 +57,20 @@ function ProjectSelector({
Проект
-
{selectedProject?.name}
- - - +
+ app_image + + + +
+ {isOpen && (
{project.name}
{activeProject && project.name === activeProject.name && ( - + + + )}
diff --git a/src/components/modals/CreateSessionModal.tsx b/src/components/modals/CreateSessionModal.tsx index 79ded1b..290fbdc 100644 --- a/src/components/modals/CreateSessionModal.tsx +++ b/src/components/modals/CreateSessionModal.tsx @@ -39,7 +39,7 @@ export default function CreateSessionModal({ targetServerId, client }: Props) { const { data: managers } = useQuery({ queryKey: ["managers"], - queryFn: () => api.get("managers").json(), + queryFn: () => api.get("users").json(), }); const targetServer = targetServerId @@ -176,6 +176,10 @@ export default function CreateSessionModal({ targetServerId, client }: Props) { selectedServer, ]); + useEffect(() => { + console.log(managers); + }, [managers]); + const ref = useRef(null); return ( @@ -246,20 +250,26 @@ export default function CreateSessionModal({ targetServerId, client }: Props) {

Выберите параметры сеанса

- + {selectedServer && selectedServer?.appsToServers && selectedServer.appsToServers?.length > 0 && ( - app)} - selectedProject={selectedApp} - setSelectedProject={setSelectedApp} - /> + <> + + app)} + selectedProject={selectedApp} + setSelectedProject={setSelectedApp} + /> + )}
diff --git a/src/utils/getPositionAbove.ts b/src/utils/getPositionAbove.ts new file mode 100644 index 0000000..e1d35ed --- /dev/null +++ b/src/utils/getPositionAbove.ts @@ -0,0 +1,11 @@ +export function getPositionAbove(selectRef: React.RefObject) { + const rect = selectRef.current.getBoundingClientRect(); + const dropdownHeight = 200; + const margin = 8; + + const spaceBelow = window.innerHeight - rect.bottom - margin; + + const spaceAbove = rect.top - margin; + + return spaceBelow < dropdownHeight && spaceAbove >= dropdownHeight; +} From a0e737aa11e2506a18af0a3dcceffbcaba96db31 Mon Sep 17 00:00:00 2001 From: c00b3r Date: Mon, 30 Jun 2025 13:11:15 +0500 Subject: [PATCH 09/10] fix: update ClientModal submit button logic to include email comparison and adjust ManagerSelect to ensure selected manager displays correctly --- src/components/ManagerSelect.tsx | 6 ++---- src/components/modals/ClientModal.tsx | 10 +++++++++- src/pages/SessionsPage.tsx | 2 +- 3 files changed, 12 insertions(+), 6 deletions(-) diff --git a/src/components/ManagerSelect.tsx b/src/components/ManagerSelect.tsx index 918f71b..fc8db42 100644 --- a/src/components/ManagerSelect.tsx +++ b/src/components/ManagerSelect.tsx @@ -16,7 +16,7 @@ function ManagerSelect({ }) { const [isOpen, setIsOpen] = useState(false); const [selectedManager, setSelectedManager] = useState( - data[0] || null + data[0] ); const [position, setPosition] = useState<"top" | "bottom">("bottom"); const selectRef = useClickAway(() => setIsOpen(false)); @@ -58,9 +58,7 @@ function ManagerSelect({
-
- {selectedManager?.fullname || data[0].fullname} -
+
{selectedManager?.fullname}
diff --git a/src/components/modals/ClientModal.tsx b/src/components/modals/ClientModal.tsx index f3adbee..285de47 100644 --- a/src/components/modals/ClientModal.tsx +++ b/src/components/modals/ClientModal.tsx @@ -108,7 +108,15 @@ function ClientModal({ client }: { client: Client }) { size="large" className="w-full" type="submit" - disabled={!clientData.name || !clientData.phone || isPending} + disabled={ + !clientData.name || + !clientData.phone || + isPending || + (clientData.name === client.name && + clientData.phone.replace(/\D/g, "") === + client.phone.replace(/\D/g, "") && + clientData.email === client.email) + } > {isPending ? ( diff --git a/src/pages/SessionsPage.tsx b/src/pages/SessionsPage.tsx index 21e9869..89fa99d 100644 --- a/src/pages/SessionsPage.tsx +++ b/src/pages/SessionsPage.tsx @@ -146,7 +146,7 @@ function SessionsPage() { ) : grouppedSessions?.length ? ( grouppedSessions?.map(([timestamp, sessions]) => (
-

+

{isToday(new Date(timestamp)) ? "Сегодня" : format(new Date(timestamp), "d MMMM", { locale: ru })} From c9357962c61b21173edd57b49e932b3b33e5ebde Mon Sep 17 00:00:00 2001 From: c00b3r Date: Wed, 2 Jul 2025 12:07:38 +0500 Subject: [PATCH 10/10] feat: enhance TableSelector with automatic selection logic and update CreateSessionModal and EndSessionModal for improved session handling and user feedback --- src/components/TableSelector.tsx | 11 +++++- src/components/modals/CreateSessionModal.tsx | 5 +-- src/components/modals/EndSessionModal.tsx | 40 ++++++++++++++------ src/pages/LoginPage.tsx | 2 +- 4 files changed, 41 insertions(+), 17 deletions(-) diff --git a/src/components/TableSelector.tsx b/src/components/TableSelector.tsx index e00c6fd..df9e64b 100644 --- a/src/components/TableSelector.tsx +++ b/src/components/TableSelector.tsx @@ -1,11 +1,12 @@ import clsx from "clsx"; import { Server } from "../types/Server"; import LightningIcon from "./icons/LightningIcon"; +import { useEffect } from "react"; interface TableSelectorProps { tables: Server[]; selectedTable: Server | null; - onSelect: (table: Server) => void; + onSelect: (table: Server | null) => void; } function TableSelector({ @@ -13,6 +14,14 @@ function TableSelector({ selectedTable, onSelect, }: TableSelectorProps) { + useEffect(() => { + if (selectedTable !== null) { + onSelect(selectedTable); + } else { + onSelect(tables.find((table) => table.status === "online") || null); + } + }, [onSelect, selectedTable, tables]); + return (

{tables.map((table) => ( diff --git a/src/components/modals/CreateSessionModal.tsx b/src/components/modals/CreateSessionModal.tsx index 290fbdc..5dfbbce 100644 --- a/src/components/modals/CreateSessionModal.tsx +++ b/src/components/modals/CreateSessionModal.tsx @@ -65,11 +65,8 @@ export default function CreateSessionModal({ targetServerId, client }: Props) { if (!error && data) { setName(data.name); setEmail(data.email); - } else { - setName(isFullPhone ? name : null); - setEmail(isFullPhone ? email : null); } - }, [data, error, isFullPhone, name, email]); + }, [data, error]); const { mutate: createClient } = useMutation({ mutationFn: () => diff --git a/src/components/modals/EndSessionModal.tsx b/src/components/modals/EndSessionModal.tsx index c1f6d6d..278722f 100644 --- a/src/components/modals/EndSessionModal.tsx +++ b/src/components/modals/EndSessionModal.tsx @@ -6,12 +6,17 @@ import Button from "../Button"; import CurrentSessionModal from "./CurrentSessionModal"; import api from "../../utils/api"; import SpinIcon from "../icons/SpinIcon"; +import SessionModal from "./SessionModal"; function EndSessionModal({ session }: { session: Session }) { const queryClient = useQueryClient(); const { setModal } = useModalStore(); - const { mutate: endSession, isPending } = useMutation({ + const { + mutate: endSession, + isPending, + isSuccess, + } = useMutation({ mutationKey: ["sessions", session.id], mutationFn: () => api.put(`sessions/${session.id}`, { json: { status: "ending" } }), @@ -22,7 +27,6 @@ function EndSessionModal({ session }: { session: Session }) { }, onSuccess: () => { queryClient.invalidateQueries({ queryKey: ["last-sessions"] }); - setModal(null); }, }); @@ -48,31 +52,45 @@ function EndSessionModal({ session }: { session: Session }) {

- Точно хотите завершить сеанс? + {isSuccess + ? "Доступен отчет по встрече" + : "Точно хотите завершить сеанс?"}

- Текущий сеанс будет завершен немедленно + {isSuccess + ? "Вы можете просмотреть отчет или создать новый сеанс, перейдя на главную страницу" + : "Текущий сеанс будет завершен немедленно"}

diff --git a/src/pages/LoginPage.tsx b/src/pages/LoginPage.tsx index 4ca0f5b..08bde66 100644 --- a/src/pages/LoginPage.tsx +++ b/src/pages/LoginPage.tsx @@ -71,7 +71,7 @@ function LoginPage() { />
-