vchikalkin 78e45718a8 refactor(contacts): consolidate customer queries and enhance contact handling
- Replaced use of useCustomersInfiniteQuery with a new useContactsInfiniteQuery hook for improved data fetching.
- Simplified ContactsList and MastersGrid components by removing unnecessary customer documentId filters.
- Deleted outdated contact-related hooks and queries to streamline the codebase.
- Enhanced loading state management across components for better user experience.
2025-09-10 13:44:43 +03:00

168 lines
5.3 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 { 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',
)}
>
{contact.name}
</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: 'Я' } as CustomerFieldsFragment, ...contacts],
};
}