vchikalkin c7648e8bf9 Enhance contact management by adding surname input and updating customer handling
- Introduced a new input for capturing the surname of the user during contact addition.
- Updated the contact parsing logic to include surname alongside name and phone number.
- Modified the customer creation and update processes to accommodate surname, ensuring full name is used in confirmation messages.
- Adjusted localization files to reflect the new surname input prompt and updated confirmation messages.
- Refactored components to utilize a unified method for retrieving full customer names, improving consistency across the application.
2025-10-07 12:36:03 +03:00

172 lines
5.4 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.

/* eslint-disable canonical/id-match */
'use client';
import { DataNotFound } from '@/components/shared/alert';
import { UserAvatar } from '@/components/shared/user-avatar';
import { CardSectionHeader } from '@/components/ui';
import { useContactsInfiniteQuery, useCustomerQuery } from '@/hooks/api/customers';
import { useOrderStore } from '@/stores/order';
import { type CustomerFieldsFragment, Enum_Customer_Role } from '@repo/graphql/types';
import { Button } from '@repo/ui/components/ui/button';
import { Card } from '@repo/ui/components/ui/card';
import { Label } from '@repo/ui/components/ui/label';
import { LoadingSpinner } from '@repo/ui/components/ui/spinner';
import { cn } from '@repo/ui/lib/utils';
import { getCustomerFullName } from '@repo/utils/customer';
import { sift } from 'radashi';
type ContactsGridProps = {
readonly contacts: CustomerFieldsFragment[];
readonly hasNextPage?: boolean;
readonly isLoading?: boolean;
readonly onClick: () => void;
readonly onFetchNextPage?: () => void;
readonly onSelect: (contactId: null | string) => void;
readonly selected?: null | string;
readonly title: string;
};
export function ClientsGrid() {
const { contacts, fetchNextPage, hasNextPage, isLoading } = useContacts();
const clientId = useOrderStore((store) => store.clientId);
const setClientId = useOrderStore((store) => store.setClientId);
const masterId = useOrderStore((store) => store.masterId);
return (
<ContactsGridBase
contacts={contacts.filter((contact) => contact.documentId !== masterId)}
hasNextPage={Boolean(hasNextPage)}
isLoading={isLoading}
onClick={() => {
if (clientId) setClientId(null);
}}
onFetchNextPage={fetchNextPage}
onSelect={(contactId) => setClientId(contactId)}
selected={clientId}
title="Выбор клиента"
/>
);
}
export function ContactsGridBase({
contacts,
hasNextPage,
isLoading,
onClick,
onFetchNextPage,
onSelect,
selected,
title,
}: ContactsGridProps) {
const { data: { customer } = {} } = useCustomerQuery();
return (
<Card className="p-4">
<div className="flex flex-col gap-4">
<CardSectionHeader title={title} />
{isLoading && <LoadingSpinner />}
{!isLoading && (!contacts || contacts.length === 0) ? (
<DataNotFound title="Контакты не найдены" />
) : null}
<div className="grid max-h-screen grid-cols-4 gap-2 overflow-y-auto">
{!isLoading &&
contacts?.map((contact) => {
if (!contact) return null;
const isCurrentUser = contact.documentId === customer?.documentId;
return (
<Label
className="flex cursor-pointer flex-col items-center"
key={contact.documentId}
>
<input
checked={selected === contact.documentId}
className="hidden"
name="user"
onChange={() => onSelect(contact.documentId)}
onClick={onClick}
type="radio"
value={contact.documentId}
/>
<div
className={cn(
'rounded-full border-2 transition-all duration-75',
selected === contact.documentId ? 'border-primary' : 'border-transparent',
)}
>
<UserAvatar {...contact} size="md" />
</div>
<span
className={cn(
'mt-2 max-w-20 break-words text-center text-sm font-medium',
isCurrentUser && 'font-bold',
)}
>
{getCustomerFullName(contact)}
</span>
</Label>
);
})}
</div>
{hasNextPage && onFetchNextPage && (
<Button onClick={onFetchNextPage} variant="ghost">
Загрузить еще
</Button>
)}
</div>
</Card>
);
}
export function MastersGrid() {
const { contacts, fetchNextPage, hasNextPage, isLoading } = useContacts();
const masterId = useOrderStore((store) => store.masterId);
const setMasterId = useOrderStore((store) => store.setMasterId);
const clientId = useOrderStore((store) => store.clientId);
return (
<ContactsGridBase
contacts={contacts.filter(
(contact) => contact.documentId !== clientId && contact.role !== Enum_Customer_Role.Client,
)}
hasNextPage={Boolean(hasNextPage)}
isLoading={isLoading}
onClick={() => {
if (masterId) setMasterId(null);
}}
onFetchNextPage={fetchNextPage}
onSelect={(contactId) => setMasterId(contactId)}
selected={masterId}
title="Выбор мастера"
/>
);
}
function useContacts() {
const { data: { customer } = {}, isLoading: isLoadingCustomer } = useCustomerQuery();
const {
data: { pages } = { pages: [] },
isLoading: isLoadingContacts,
...query
} = useContactsInfiniteQuery();
const isLoading = isLoadingContacts || isLoadingCustomer;
const contacts = sift(
pages.flatMap((page) => page.customers).filter((contact) => Boolean(contact && contact.active)),
);
return {
isLoading,
...query,
contacts: [
{ ...customer, name: 'Я', surname: undefined } as CustomerFieldsFragment,
...contacts,
],
};
}