diff --git a/public/images/mock_manager_photo_1_c.png b/public/images/mock_manager_photo_1_c.png new file mode 100644 index 0000000..8c55032 Binary files /dev/null and b/public/images/mock_manager_photo_1_c.png differ diff --git a/public/images/mock_manager_photo_c.png b/public/images/mock_manager_photo_c.png new file mode 100644 index 0000000..62830c2 Binary files /dev/null and b/public/images/mock_manager_photo_c.png differ diff --git a/src/components/Input.tsx b/src/components/Input.tsx index 08e9c89..0b77a65 100644 --- a/src/components/Input.tsx +++ b/src/components/Input.tsx @@ -6,6 +6,7 @@ interface NewInputProps extends React.InputHTMLAttributes { isError?: boolean; errorMessage?: string; isLoading?: boolean; + children?: React.ReactNode; } function Input({ @@ -13,6 +14,7 @@ function Input({ isError, errorMessage, isLoading, + children, ...props }: NewInputProps) { return ( @@ -27,6 +29,7 @@ function Input({ "peer bg-[#F6F6F6] rounded-[0.833vw] px-[1.111vw] pt-[19px] pb-[11px] outline-none ring-1 ring-transparent transition-all inline-block w-full h-[3.889vw] text-m" )} /> + {children} {placeholder && ( -
+

{session.manager.fullname}

diff --git a/src/components/icons/CopyIcon.tsx b/src/components/icons/CopyIcon.tsx new file mode 100644 index 0000000..f2b923d --- /dev/null +++ b/src/components/icons/CopyIcon.tsx @@ -0,0 +1,21 @@ +function CopyIcon() { + return ( + + + + + ); +} + +export default CopyIcon; diff --git a/src/components/modals/ClientModal.tsx b/src/components/modals/ClientModal.tsx new file mode 100644 index 0000000..a1ba9df --- /dev/null +++ b/src/components/modals/ClientModal.tsx @@ -0,0 +1,200 @@ +import { format, isToday } from "date-fns"; +import { Client } from "../../types/Client"; +import Badge from "../Badge"; +import Button from "../Button"; +import PeopleIcon from "../icons/PeopleIcon"; +import PlusIcon from "../icons/PlusIcon"; +import Input from "../Input"; +import { ru } from "date-fns/locale"; +import ChevronRightIcon from "../icons/ChevronRightIcon"; +import CopyIcon from "../icons/CopyIcon"; +import { useState } from "react"; +import { useMutation, useQueryClient } from "@tanstack/react-query"; +import api from "../../utils/api"; +import SpinIcon from "../icons/SpinIcon"; +import useModalStore from "../../stores/useModalStore"; +import CreateSessionModal from "./CreateSessionModal"; + +function ClientModal({ client }: { client: Client }) { + const queryClient = useQueryClient(); + const { setModal } = useModalStore(); + const [clientData, setClientData] = useState({ + name: client.name, + phone: client.phone, + email: client.email, + }); + + const { mutate: updateClientData, isPending } = useMutation({ + mutationKey: ["clients", client.id], + mutationFn: () => api.put(`clients/${client.id}`, { json: clientData }), + onSuccess: () => { + queryClient.invalidateQueries({ queryKey: ["clients"] }); + }, + }); + + return ( +

+
+

{client.name}

+
+
+
+
+
+

Данные клиента

+

+ Вы можете изменить данные клиента +

+
+
+ { + setClientData({ ...clientData, name: e.target.value }); + }} + className="relative" + > + { + navigator.clipboard.writeText(clientData.name); + }} + > + + + + { + setClientData({ ...clientData, phone: e.target.value }); + }} + className="relative" + > + { + navigator.clipboard.writeText(clientData.phone); + }} + > + + + + { + setClientData({ ...clientData, email: e.target.value }); + }} + /> +
+
+ +
+
+
+
+

Управление доступом

+

+ Выберите, кто может видеть и редактировать данные этого клиента +

+
+
+
+
+
+
+ +
+
+
+
+

История сеансов

+ {client.sessions.length > 0 && ( + + )} +
+
+ {client.sessions.length === 0 && ( +

+ Пока не было сеансов +

+ )} + {client.sessions.map((session) => ( +
+
+
+
+
+

+ {session.manager.fullname} +

+

+ {isToday(new Date(session.updatedAt)) + ? "Сегодня" + : format(new Date(session.updatedAt), "d MMMM", { + locale: ru, + })} +

+
+
+ + + +
+
+ ))} +
+ +
+
+
+
+
+ ); +} + +export default ClientModal; diff --git a/src/components/modals/CreateSessionModal.tsx b/src/components/modals/CreateSessionModal.tsx index 3228d89..3696ae1 100644 --- a/src/components/modals/CreateSessionModal.tsx +++ b/src/components/modals/CreateSessionModal.tsx @@ -16,13 +16,14 @@ import { AnimatePresence, motion } from "motion/react"; interface Props { targetServerId: string | null; + client?: Client | null; } -export default function CreateSessionModal({ targetServerId }: Props) { +export default function CreateSessionModal({ targetServerId, client }: Props) { const { setModal } = useModalStore(); - const [name, setName] = useState(null); - const [phone, setPhone] = useState(null); - const [email, setEmail] = useState(null); + 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 queryClient = useQueryClient(); diff --git a/src/components/modals/SessionModal.tsx b/src/components/modals/SessionModal.tsx index b5d1491..c792691 100644 --- a/src/components/modals/SessionModal.tsx +++ b/src/components/modals/SessionModal.tsx @@ -43,7 +43,7 @@ function SessionModal({ session }: { session: Session }) {
-
+

{session.manager.fullname}

diff --git a/src/pages/ClientsPage.tsx b/src/pages/ClientsPage.tsx index 4820fd8..81343c0 100644 --- a/src/pages/ClientsPage.tsx +++ b/src/pages/ClientsPage.tsx @@ -11,10 +11,14 @@ import { Client } from "../types/Client"; import { useDebounce } from "@uidotdev/usehooks"; import pluralize from "../utils/pluralize"; import ChevronRightIcon from "../components/icons/ChevronRightIcon"; +import useModalStore from "../stores/useModalStore"; +import ClientModal from "../components/modals/ClientModal"; +import clsx from "clsx"; function ClientsPage() { const [limit, setLimit] = useState(10); const [search, setSearch] = useState(null); + const { setModal, setPosition } = useModalStore(); const debouncedSearch = useDebounce(search, 500); @@ -102,17 +106,27 @@ function ClientsPage() {

) : clients?.length ? ( - clients?.map(({ name, email, phone }) => ( -
+ clients?.map((client, index) => ( +
{ + setPosition("right"); + setModal(); + }} + >
-

{name}

+

{client.name}

-

{phone}

-

{email ? "• " + email : ""}

+

{client.phone}

+

{client.email ? "• " + client.email : ""}

diff --git a/src/types/Client.ts b/src/types/Client.ts index 3a245e0..495f281 100644 --- a/src/types/Client.ts +++ b/src/types/Client.ts @@ -1,3 +1,5 @@ +import { Session } from "./Session"; + export interface Client { id: string; name: string; @@ -6,4 +8,5 @@ export interface Client { companyId: string; createdAt: string; updatedAt: string; + sessions: Session[]; }