Files
graff-mate-client/src/components/modals/ClientModal.tsx
T

220 lines
8.9 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
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";
import SessionModal from "./SessionModal";
function ClientModal({ client }: { client: Client }) {
const queryClient = useQueryClient();
const { setModal } = useModalStore();
const [clientData, setClientData] = useState(client);
const { mutate: updateClientData, isPending } = useMutation({
mutationKey: ["clients", client.id],
mutationFn: () =>
api.put(`clients/${client.id}`, {
json: {
name: clientData.name,
phone: clientData.phone.replace(/\D/g, ""),
email: clientData.email,
},
}),
onSuccess: () => {
queryClient.invalidateQueries({ queryKey: ["clients"] });
},
});
const handleSubmit = (e: React.FormEvent<HTMLFormElement>) => {
e.preventDefault();
updateClientData();
};
return (
<div className="w-[49.722vw] bg-[#FFFFFF] rounded-[2.222vw] overflow-hidden">
<div className="flex justify-center items-center py-[1.806vw] border-b border-[#D6D6D6]">
<p className="title-s font-medium">{client.name}</p>
</div>
<div className="flex bg-[#F0F0F0] h-[calc(100vh-8.861vw)] rounded-b-[2.222vw]">
<div className="flex flex-col gap-[1.111vw] p-[1.111vw] flex-1 overflow-y-auto [scrollbar-width:thin]">
<div className="flex flex-col gap-[1.111vw] rounded-[1.667vw] bg-white p-[1.111vw]">
<div className="flex flex-col gap-[0.278vw]">
<p className="title-s font-medium">Данные клиента</p>
<p className="caption-s text-[#BDBDBD] font-medium">
Вы можете изменить данные клиента
</p>
</div>
<form onSubmit={handleSubmit}>
<div className="flex flex-col gap-[0.278vw]">
<Input
placeholder="Имя"
defaultValue={clientData.name || ""}
onChange={(e) => {
setClientData({ ...clientData, name: e.target.value });
}}
required
>
<span
className="absolute z-10 top-[1.25vw] left-[17.917vw] size-[1.389vw] text-[#7D7D7D] cursor-pointer"
onClick={() => {
navigator.clipboard.writeText(clientData.name);
}}
>
<CopyIcon />
</span>
</Input>
<Input
placeholder="Номер телефона"
defaultValue={clientData.phone || ""}
onChange={(e) => {
setClientData({ ...clientData, phone: e.target.value });
}}
required
mask="+7 (999) 999-99-99"
>
<span
className="absolute top-[1.25vw] left-[17.917vw] size-[1.389vw] text-[#7D7D7D] cursor-pointer"
onClick={() => {
navigator.clipboard.writeText(clientData.phone);
}}
>
<CopyIcon />
</span>
</Input>
<Input
placeholder="Эл. почта"
defaultValue={clientData.email || ""}
onChange={(e) => {
setClientData({ ...clientData, email: e.target.value });
}}
/>
</div>
<div className="mt-[1.111vw]">
<Button
variant="primary"
size="large"
className="w-full"
type="submit"
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 ? (
<span className="size-[1.111vw] animate-spin text-[#7B60F3] flex items-center justify-center">
<SpinIcon />
</span>
) : (
"Сохранить изменения"
)}
</Button>
</div>
</form>
</div>
<div className="flex flex-col gap-[1.111vw] rounded-[1.667vw] bg-white p-[1.111vw]">
<div className="flex flex-col gap-[0.278vw]">
<p className="title-s font-medium">Управление доступом</p>
<p className="caption-s text-[#BDBDBD] font-medium">
Выберите, кто может видеть и редактировать данные этого клиента
</p>
</div>
<div className="flex gap-[0.556vw]">
<div className="size-[2.222vw] rounded-full bg-[#F0F0F0] bg-[url(/images/mock_manager_photo_c.png)] bg-cover bg-no-repeat bg-center" />
<div className="size-[2.222vw] rounded-full bg-[#F0F0F0] bg-[url(/images/mock_manager_photo_1_c.png)] bg-cover bg-no-repeat bg-center" />
</div>
<div>
<button className="button-m text-[#7B60F3] font-medium flex items-center gap-[0.278vw]">
<span className="size-[0.972vw]">
<PeopleIcon />
</span>
Настроить доступ
</button>
</div>
</div>
<div className="flex flex-col gap-[1.111vw] rounded-[1.667vw] bg-white p-[1.111vw]">
<div className="flex items-center gap-[0.556vw]">
<p className="title-s font-medium">История сеансов</p>
{client.sessions.length > 0 && (
<Badge count={client.sessions.length} />
)}
</div>
<div className="flex flex-col gap-[0.556vw]">
{client.sessions.length === 0 && (
<p className="caption-s text-[#BDBDBD] font-medium text-center">
Пока не было сеансов
</p>
)}
{client.sessions.map((session) => (
<div
key={session.id}
className="p-[0.278vw] border-b border-[#F6F6F6] cursor-pointer"
onClick={() => {
setModal(<SessionModal session={session} />);
}}
>
<div className="p-[0.833vw] flex justify-between items-center">
<div className="flex gap-[0.556vw] items-center">
<div className="size-[2.5vw] rounded-full bg-[#F0F0F0] bg-[url(/images/mock_manager_photo_c.png)] bg-cover bg-no-repeat bg-center" />
<div className="flex flex-col gap-[0.278vw]">
<p className="button-m font-medium">
{session.manager.fullname}
</p>
<p className="caption-s text-[#BDBDBD] font-medium">
{isToday(new Date(session.updatedAt))
? "Сегодня"
: format(new Date(session.updatedAt), "d MMMM", {
locale: ru,
})}
</p>
</div>
</div>
<span className="size-[1.389vw] text-[#7D7D7D]">
<ChevronRightIcon />
</span>
</div>
</div>
))}
</div>
<Button
variant="cta"
size="large"
className="w-full"
onClick={() =>
setModal(
<CreateSessionModal targetServerId={null} client={client} />
)
}
>
<span className="size-[1.111vw]">
<PlusIcon />
</span>
Новый сеанс с клиентом
</Button>
</div>
</div>
<div className="flex-1"></div>
</div>
</div>
);
}
export default ClientModal;