From ad589e471c9de0975a19b221a1d566596bb9ad27 Mon Sep 17 00:00:00 2001 From: vchikalkin Date: Sat, 23 Aug 2025 14:40:09 +0300 Subject: [PATCH] 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. --- apps/web/app/(main)/orders/page.tsx | 3 +- .../components/orders/orders-list/index.tsx | 98 ++++--------------- .../orders/orders-list/orders-list.tsx | 78 +++++++++++++++ .../components/orders/orders-list/utils.ts | 65 ++++++++++++ 4 files changed, 164 insertions(+), 80 deletions(-) create mode 100644 apps/web/components/orders/orders-list/orders-list.tsx create mode 100644 apps/web/components/orders/orders-list/utils.ts diff --git a/apps/web/app/(main)/orders/page.tsx b/apps/web/app/(main)/orders/page.tsx index a47d2fa..e482ec9 100644 --- a/apps/web/app/(main)/orders/page.tsx +++ b/apps/web/app/(main)/orders/page.tsx @@ -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() {
- diff --git a/apps/web/components/orders/orders-list/index.tsx b/apps/web/components/orders/orders-list/index.tsx index 6457199..3c98819 100644 --- a/apps/web/components/orders/orders-list/index.tsx +++ b/apps/web/components/orders/orders-list/index.tsx @@ -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 ( -
-

Записи клиентов

- {orders?.map((order) => order && )} - {hasNextPage && ( - - )} -
- ); -} - 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 ( -
-

Ваши записи

- {orders?.map( - (order) => - order && , - )} - {hasNextPage && ( - - )} -
+ fetchNextPage()} + orders={orders} + title={isMaster ? 'Записи клиентов' : 'Ваши записи'} + /> ); } diff --git a/apps/web/components/orders/orders-list/orders-list.tsx b/apps/web/components/orders/orders-list/orders-list.tsx new file mode 100644 index 0000000..d7b3e55 --- /dev/null +++ b/apps/web/components/orders/orders-list/orders-list.tsx @@ -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[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 ( +
+

{title}

+ {orders?.map((order) => { + if (!order) return null; + + const status = getOrderStatus(order, current, next); + + return ( + + + + ); + })} + {hasNextPage && onLoadMore && ( + + )} +
+ ); +} + +function DateStatusWrapper({ + children, + status, +}: { + readonly children: React.ReactNode; + readonly status: OrderStatus; +}) { + if (!status) return <>{children}; + + const statusText = getStatusText(status); + + return ( +
+ {statusText && ( +
+ + {statusText} + +
+ )} + {children} +
+ ); +} diff --git a/apps/web/components/orders/orders-list/utils.ts b/apps/web/components/orders/orders-list/utils.ts new file mode 100644 index 0000000..02bd0cf --- /dev/null +++ b/apps/web/components/orders/orders-list/utils.ts @@ -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 }; +}