refactor(orders): remove ClientsOrdersList and streamline OrdersList component
- Eliminated the ClientsOrdersList component to simplify the orders page structure. - Updated OrdersList to handle both client and master views, enhancing code reusability. - Improved order fetching logic and UI rendering for better performance and user experience.
This commit is contained in:
parent
73dd674815
commit
ad589e471c
@ -1,6 +1,6 @@
|
||||
'use client';
|
||||
import { Container } from '@/components/layout';
|
||||
import { ClientsOrdersList, OrdersList } from '@/components/orders';
|
||||
import { OrdersList } from '@/components/orders';
|
||||
import { DateSelect } from '@/components/orders/orders-list/date-select';
|
||||
import { DateTimeStoreProvider } from '@/stores/datetime';
|
||||
|
||||
@ -10,7 +10,6 @@ export default function ProfilePage() {
|
||||
<DateTimeStoreProvider>
|
||||
<div />
|
||||
<DateSelect />
|
||||
<ClientsOrdersList />
|
||||
<OrdersList />
|
||||
</DateTimeStoreProvider>
|
||||
</Container>
|
||||
|
||||
@ -1,66 +1,16 @@
|
||||
'use client';
|
||||
|
||||
import { OrderCard } from '@/components/shared/order-card';
|
||||
import { OrdersList as OrdersListComponent } from './orders-list';
|
||||
import { useCurrentAndNext } from './utils';
|
||||
import { useCustomerQuery, useIsMaster } from '@/hooks/api/customers';
|
||||
import { useOrdersInfiniteQuery } from '@/hooks/api/orders';
|
||||
import { useDateTimeStore } from '@/stores/datetime';
|
||||
import { Button } from '@repo/ui/components/ui/button';
|
||||
import type * as GQL from '@repo/graphql/types';
|
||||
import { getDateUTCRange } from '@repo/utils/datetime-format';
|
||||
|
||||
export function ClientsOrdersList() {
|
||||
const { data: { customer } = {} } = useCustomerQuery();
|
||||
|
||||
const isMaster = useIsMaster();
|
||||
|
||||
const selectedDate = useDateTimeStore((store) => store.date);
|
||||
const { endOfDay, startOfDay } = getDateUTCRange(selectedDate).day();
|
||||
|
||||
const {
|
||||
data: { pages } = {},
|
||||
fetchNextPage,
|
||||
hasNextPage,
|
||||
isLoading,
|
||||
} = useOrdersInfiniteQuery(
|
||||
{
|
||||
filters: {
|
||||
slot: {
|
||||
datetime_start: selectedDate
|
||||
? {
|
||||
gte: startOfDay,
|
||||
lt: endOfDay,
|
||||
}
|
||||
: undefined,
|
||||
master: {
|
||||
documentId: {
|
||||
eq: isMaster ? customer?.documentId : undefined,
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
{ enabled: Boolean(customer?.documentId) && isMaster },
|
||||
);
|
||||
|
||||
const orders = pages?.flatMap((page) => page.orders) ?? [];
|
||||
|
||||
if (!orders?.length || isLoading || !isMaster) return null;
|
||||
|
||||
return (
|
||||
<div className="flex flex-col space-y-2">
|
||||
<h1 className="font-bold">Записи клиентов</h1>
|
||||
{orders?.map((order) => order && <OrderCard key={order.documentId} showDate {...order} />)}
|
||||
{hasNextPage && (
|
||||
<Button onClick={() => fetchNextPage()} variant="ghost">
|
||||
Загрузить еще
|
||||
</Button>
|
||||
)}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
export function OrdersList() {
|
||||
const { data: { customer } = {} } = useCustomerQuery();
|
||||
|
||||
const isMaster = useIsMaster();
|
||||
const selectedDate = useDateTimeStore((store) => store.date);
|
||||
const { endOfDay, startOfDay } = getDateUTCRange(selectedDate).day();
|
||||
|
||||
@ -72,40 +22,32 @@ export function OrdersList() {
|
||||
} = useOrdersInfiniteQuery(
|
||||
{
|
||||
filters: {
|
||||
client: {
|
||||
documentId: {
|
||||
eq: customer?.documentId,
|
||||
},
|
||||
},
|
||||
client: isMaster ? undefined : { documentId: { eq: customer?.documentId } },
|
||||
slot: {
|
||||
datetime_start: selectedDate
|
||||
? {
|
||||
gte: startOfDay,
|
||||
lt: endOfDay,
|
||||
}
|
||||
: undefined,
|
||||
datetime_start: selectedDate ? { gte: startOfDay, lt: endOfDay } : undefined,
|
||||
master: isMaster ? { documentId: { eq: customer?.documentId } } : undefined,
|
||||
},
|
||||
},
|
||||
},
|
||||
{ enabled: Boolean(customer?.documentId) },
|
||||
);
|
||||
|
||||
const orders = pages?.flatMap((page) => page.orders) ?? [];
|
||||
const orders =
|
||||
(pages?.flatMap((page) => page.orders).filter(Boolean) as GQL.OrderFieldsFragment[]) ?? [];
|
||||
|
||||
const { current, next } = useCurrentAndNext(orders);
|
||||
|
||||
if (!orders?.length || isLoading) return null;
|
||||
|
||||
return (
|
||||
<div className="flex flex-col space-y-2">
|
||||
<h1 className="font-bold">Ваши записи</h1>
|
||||
{orders?.map(
|
||||
(order) =>
|
||||
order && <OrderCard avatarSource="master" key={order.documentId} showDate {...order} />,
|
||||
)}
|
||||
{hasNextPage && (
|
||||
<Button onClick={() => fetchNextPage()} variant="ghost">
|
||||
Загрузить еще
|
||||
</Button>
|
||||
)}
|
||||
</div>
|
||||
<OrdersListComponent
|
||||
avatarSource={isMaster ? 'client' : 'master'}
|
||||
current={current}
|
||||
hasNextPage={hasNextPage}
|
||||
next={next}
|
||||
onLoadMore={() => fetchNextPage()}
|
||||
orders={orders}
|
||||
title={isMaster ? 'Записи клиентов' : 'Ваши записи'}
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
||||
78
apps/web/components/orders/orders-list/orders-list.tsx
Normal file
78
apps/web/components/orders/orders-list/orders-list.tsx
Normal file
@ -0,0 +1,78 @@
|
||||
import { getOrderStatus, getStatusText, type OrderStatus } from './utils';
|
||||
import { OrderCard } from '@/components/shared/order-card';
|
||||
import type * as GQL from '@repo/graphql/types';
|
||||
import { Button } from '@repo/ui/components/ui/button';
|
||||
import { cn } from '@repo/ui/lib/utils';
|
||||
|
||||
type Order = GQL.OrderFieldsFragment;
|
||||
|
||||
type OrdersListProps = Pick<Parameters<typeof OrderCard>[0], 'avatarSource'> & {
|
||||
readonly current: null | Order;
|
||||
readonly hasNextPage?: boolean;
|
||||
readonly next: null | Order;
|
||||
readonly onLoadMore?: () => void;
|
||||
readonly orders: Order[];
|
||||
readonly title: string;
|
||||
};
|
||||
|
||||
export function OrdersList({
|
||||
avatarSource,
|
||||
current,
|
||||
hasNextPage = false,
|
||||
next,
|
||||
onLoadMore,
|
||||
orders,
|
||||
title,
|
||||
}: OrdersListProps) {
|
||||
return (
|
||||
<div className="flex flex-col space-y-2">
|
||||
<h1 className="font-bold">{title}</h1>
|
||||
{orders?.map((order) => {
|
||||
if (!order) return null;
|
||||
|
||||
const status = getOrderStatus(order, current, next);
|
||||
|
||||
return (
|
||||
<DateStatusWrapper key={order.documentId} status={status}>
|
||||
<OrderCard avatarSource={avatarSource} showDate {...order} />
|
||||
</DateStatusWrapper>
|
||||
);
|
||||
})}
|
||||
{hasNextPage && onLoadMore && (
|
||||
<Button onClick={onLoadMore} variant="ghost">
|
||||
Загрузить еще
|
||||
</Button>
|
||||
)}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
function DateStatusWrapper({
|
||||
children,
|
||||
status,
|
||||
}: {
|
||||
readonly children: React.ReactNode;
|
||||
readonly status: OrderStatus;
|
||||
}) {
|
||||
if (!status) return <>{children}</>;
|
||||
|
||||
const statusText = getStatusText(status);
|
||||
|
||||
return (
|
||||
<div className="space-y-1">
|
||||
{statusText && (
|
||||
<div className="px-2">
|
||||
<span
|
||||
className={cn(
|
||||
'text-xs',
|
||||
status === 'current' ? 'text-secondary-foreground' : 'text-muted-foreground',
|
||||
)}
|
||||
>
|
||||
{statusText}
|
||||
</span>
|
||||
</div>
|
||||
)}
|
||||
{children}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
65
apps/web/components/orders/orders-list/utils.ts
Normal file
65
apps/web/components/orders/orders-list/utils.ts
Normal file
@ -0,0 +1,65 @@
|
||||
import * as GQL from '@repo/graphql/types';
|
||||
import dayjs from 'dayjs';
|
||||
import { useMemo } from 'react';
|
||||
|
||||
export type OrderStatus = 'current' | 'next' | undefined;
|
||||
|
||||
type Order = GQL.OrderFieldsFragment;
|
||||
|
||||
export function getCurrentAndNext(orders: Order[]) {
|
||||
const now = dayjs();
|
||||
|
||||
let current: null | Order = null;
|
||||
let next: null | Order = null;
|
||||
|
||||
for (let index = orders.length - 1; index >= 0; index--) {
|
||||
const order = orders[index];
|
||||
const nextOrder = index > 0 ? orders[index - 1] : null;
|
||||
|
||||
if (
|
||||
order &&
|
||||
now.isAfter(dayjs(order.datetime_start)) &&
|
||||
now.isBefore(dayjs(order.datetime_end))
|
||||
) {
|
||||
current = order;
|
||||
}
|
||||
|
||||
if (
|
||||
nextOrder?.state === GQL.Enum_Order_State.Approved &&
|
||||
now.isSame(dayjs(nextOrder?.datetime_start), 'day')
|
||||
) {
|
||||
next = nextOrder;
|
||||
}
|
||||
}
|
||||
|
||||
return { current, next };
|
||||
}
|
||||
|
||||
export function getOrderStatus(
|
||||
order: Order,
|
||||
current: null | Order,
|
||||
next: null | Order,
|
||||
): OrderStatus {
|
||||
if (current && order.documentId === current.documentId) return 'current';
|
||||
if (next && order.documentId === next.documentId) return 'next';
|
||||
return undefined;
|
||||
}
|
||||
|
||||
export function getStatusText(status: OrderStatus): string {
|
||||
switch (status) {
|
||||
case 'current':
|
||||
return 'Текущая запись';
|
||||
case 'next':
|
||||
return 'Следующая запись';
|
||||
default:
|
||||
return '';
|
||||
}
|
||||
}
|
||||
|
||||
export function useCurrentAndNext(orders: Order[]) {
|
||||
const { current, next } = useMemo(() => {
|
||||
return getCurrentAndNext(orders);
|
||||
}, [orders, orders.length]);
|
||||
|
||||
return { current, next };
|
||||
}
|
||||
Loading…
x
Reference in New Issue
Block a user