- 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.
172 lines
5.4 KiB
TypeScript
172 lines
5.4 KiB
TypeScript
/* 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,
|
||
],
|
||
};
|
||
}
|