From 458a06a620cab301290ac732b3c27050d4bbcaee Mon Sep 17 00:00:00 2001 From: vchikalkin Date: Tue, 7 Oct 2025 13:28:40 +0300 Subject: [PATCH] Refactor async components to synchronous functions for improved performance - Converted several async components to synchronous functions, including `Layout`, `AddOrdersPage`, `ProfilePage`, `SlotPage`, and `ServicePage`, enhancing rendering efficiency. - Removed unnecessary prefetching logic and hydration boundaries, simplifying component structure and improving maintainability. - Updated the `TelegramProvider` to return null during the initial mount instead of a loading message, streamlining the loading state handling. - Enhanced loading state management in order-related components by adding loading spinners and data not found alerts, improving user experience during data fetching. --- apps/web/app/(auth)/telegram/layout.tsx | 2 +- .../app/(main)/orders/[documentId]/page.tsx | 14 ++------------ apps/web/app/(main)/orders/add/page.tsx | 2 +- .../schedule/slots/[documentId]/page.tsx | 14 ++------------ .../profile/services/[serviceId]/page.tsx | 12 ++---------- apps/web/components/orders/order-contacts.tsx | 12 ++++++++---- apps/web/components/orders/order-datetime.tsx | 11 ++++++++++- apps/web/components/orders/order-services.tsx | 10 ++++++---- apps/web/components/orders/order-status.tsx | 9 ++++++++- .../components/profile/data-card/index.tsx | 2 ++ .../profile/services/service-data-card.tsx | 19 ++++++++++++++++++- .../schedule/slot-datetime/index.tsx | 12 ++++++++++++ apps/web/providers/telegram.tsx | 2 +- 13 files changed, 73 insertions(+), 48 deletions(-) diff --git a/apps/web/app/(auth)/telegram/layout.tsx b/apps/web/app/(auth)/telegram/layout.tsx index e75b962..8ccb7c0 100644 --- a/apps/web/app/(auth)/telegram/layout.tsx +++ b/apps/web/app/(auth)/telegram/layout.tsx @@ -1,6 +1,6 @@ import { TelegramProvider } from '@/providers/telegram'; import { type PropsWithChildren } from 'react'; -export default async function Layout({ children }: Readonly) { +export default function Layout({ children }: Readonly) { return {children}; } diff --git a/apps/web/app/(main)/orders/[documentId]/page.tsx b/apps/web/app/(main)/orders/[documentId]/page.tsx index 7ec03c3..97cd27e 100644 --- a/apps/web/app/(main)/orders/[documentId]/page.tsx +++ b/apps/web/app/(main)/orders/[documentId]/page.tsx @@ -1,4 +1,3 @@ -import { getOrder } from '@/actions/api/orders'; import { Container } from '@/components/layout'; import { PageHeader } from '@/components/navigation'; import { @@ -9,23 +8,14 @@ import { OrderStatus, } from '@/components/orders'; import { type OrderPageParameters } from '@/components/orders/types'; -import { dehydrate, HydrationBoundary, QueryClient } from '@tanstack/react-query'; type Props = { params: Promise }; export default async function ProfilePage(props: Readonly) { const parameters = await props.params; - const documentId = parameters.documentId; - - const queryClient = new QueryClient(); - - await queryClient.prefetchQuery({ - queryFn: () => getOrder({ documentId }), - queryKey: ['order', documentId], - }); return ( - + <> @@ -35,6 +25,6 @@ export default async function ProfilePage(props: Readonly) {
- + ); } diff --git a/apps/web/app/(main)/orders/add/page.tsx b/apps/web/app/(main)/orders/add/page.tsx index a917e2d..54e8156 100644 --- a/apps/web/app/(main)/orders/add/page.tsx +++ b/apps/web/app/(main)/orders/add/page.tsx @@ -2,7 +2,7 @@ import { Container } from '@/components/layout'; import { PageHeader } from '@/components/navigation'; import { OrderForm } from '@/components/orders'; -export default async function AddOrdersPage() { +export default function AddOrdersPage() { return ( <> diff --git a/apps/web/app/(main)/profile/schedule/slots/[documentId]/page.tsx b/apps/web/app/(main)/profile/schedule/slots/[documentId]/page.tsx index d248303..893c354 100644 --- a/apps/web/app/(main)/profile/schedule/slots/[documentId]/page.tsx +++ b/apps/web/app/(main)/profile/schedule/slots/[documentId]/page.tsx @@ -1,26 +1,16 @@ -import { getSlot } from '@/actions/api/slots'; import { Container } from '@/components/layout'; import { PageHeader } from '@/components/navigation'; import { SlotButtons, SlotDateTime, SlotOrdersList } from '@/components/schedule'; import { type SlotPageParameters } from '@/components/schedule/types'; import { BookButton } from '@/components/shared/book-button'; -import { dehydrate, HydrationBoundary, QueryClient } from '@tanstack/react-query'; type Props = { params: Promise }; export default async function SlotPage(props: Readonly) { const parameters = await props.params; - const documentId = parameters.documentId; - - const queryClient = new QueryClient(); - - await queryClient.prefetchQuery({ - queryFn: () => getSlot({ documentId }), - queryKey: ['slot', documentId], - }); return ( - + <> @@ -29,6 +19,6 @@ export default async function SlotPage(props: Readonly) {
- + ); } diff --git a/apps/web/app/(main)/profile/services/[serviceId]/page.tsx b/apps/web/app/(main)/profile/services/[serviceId]/page.tsx index 471d092..72ac7d4 100644 --- a/apps/web/app/(main)/profile/services/[serviceId]/page.tsx +++ b/apps/web/app/(main)/profile/services/[serviceId]/page.tsx @@ -1,28 +1,20 @@ -import { getService } from '@/actions/api/services'; import { Container } from '@/components/layout'; import { PageHeader } from '@/components/navigation'; import { ServiceButtons, ServiceDataCard } from '@/components/profile/services'; -import { dehydrate, HydrationBoundary, QueryClient } from '@tanstack/react-query'; // Тип параметров страницы type Props = { params: Promise<{ serviceId: string }> }; export default async function ProfilePage(props: Readonly) { const { serviceId } = await props.params; - const queryClient = new QueryClient(); - - await queryClient.prefetchQuery({ - queryFn: () => getService({ documentId: serviceId }), - queryKey: ['service', serviceId], - }); return ( - + <> - + ); } diff --git a/apps/web/components/orders/order-contacts.tsx b/apps/web/components/orders/order-contacts.tsx index b90c90a..cbbb8e4 100644 --- a/apps/web/components/orders/order-contacts.tsx +++ b/apps/web/components/orders/order-contacts.tsx @@ -1,25 +1,29 @@ 'use client'; +import { DataNotFound } from '../shared/alert'; import { ContactRow } from '../shared/contact-row'; import { type OrderComponentProps } from './types'; import { useOrderQuery } from '@/hooks/api/orders'; +import { LoadingSpinner } from '@repo/ui/components/ui/spinner'; export function OrderContacts({ documentId }: Readonly) { - const { data: { order } = {} } = useOrderQuery({ documentId }); + const { data: { order } = {}, isLoading } = useOrderQuery({ documentId }); - if (!order) return null; + const noContacts = !order?.slot?.master && !order?.client; return (

Участники

- {order.slot?.master && ( + {isLoading && } + {!isLoading && noContacts ? : null} + {order?.slot?.master && ( )} - {order.client && ( + {order?.client && ( ) { - const { data: { order } = {} } = useOrderQuery({ documentId }); + const { data: { order } = {}, isLoading } = useOrderQuery({ documentId }); + + if (isLoading) { + return ( +
+
+
+
+ ); + } if (!order) return null; diff --git a/apps/web/components/orders/order-services.tsx b/apps/web/components/orders/order-services.tsx index 35ac9d1..97d7194 100644 --- a/apps/web/components/orders/order-services.tsx +++ b/apps/web/components/orders/order-services.tsx @@ -1,18 +1,20 @@ 'use client'; +import { DataNotFound } from '../shared/alert'; import { ServiceCard } from '../shared/service-card'; import { type OrderComponentProps } from './types'; import { useOrderQuery } from '@/hooks/api/orders'; +import { LoadingSpinner } from '@repo/ui/components/ui/spinner'; export function OrderServices({ documentId }: Readonly) { - const { data: { order } = {} } = useOrderQuery({ documentId }); - - if (!order) return null; + const { data: { order } = {}, isLoading } = useOrderQuery({ documentId }); return (

Услуги

- {order.services?.map( + {isLoading && } + {!isLoading && !order?.services?.length ? : null} + {order?.services?.map( (service) => service && , )}
diff --git a/apps/web/components/orders/order-status.tsx b/apps/web/components/orders/order-status.tsx index a4ed9d9..77ae9ce 100644 --- a/apps/web/components/orders/order-status.tsx +++ b/apps/web/components/orders/order-status.tsx @@ -5,7 +5,14 @@ import { getAlert } from '@/components/shared/status'; import { useOrderQuery } from '@/hooks/api/orders'; export function OrderStatus({ documentId }: Readonly) { - const { data: { order } = {} } = useOrderQuery({ documentId }); + const { data: { order } = {}, isLoading } = useOrderQuery({ documentId }); + + if (isLoading) + return ( +
+
+
+ ); return order?.state && getAlert(order.state); } diff --git a/apps/web/components/profile/data-card/index.tsx b/apps/web/components/profile/data-card/index.tsx index 14e9657..5af3204 100644 --- a/apps/web/components/profile/data-card/index.tsx +++ b/apps/web/components/profile/data-card/index.tsx @@ -57,6 +57,8 @@ export function ProfileDataCard() {
+
+
diff --git a/apps/web/components/profile/services/service-data-card.tsx b/apps/web/components/profile/services/service-data-card.tsx index 0f4dcc2..f4fc880 100644 --- a/apps/web/components/profile/services/service-data-card.tsx +++ b/apps/web/components/profile/services/service-data-card.tsx @@ -12,10 +12,27 @@ type Props = { }; export function ServiceDataCard({ serviceId }: Readonly) { - const { data: { service } = {} } = useServiceQuery({ documentId: serviceId }); + const { data: { service } = {}, isLoading } = useServiceQuery({ documentId: serviceId }); const { cancelChanges, hasChanges, isPending, resetTrigger, saveChanges, updateField } = useServiceEdit(serviceId); + if (isLoading) { + return ( + +
+
+
+
+
+
+
+
+
+
+ + ); + } + if (!service) return null; return ( diff --git a/apps/web/components/schedule/slot-datetime/index.tsx b/apps/web/components/schedule/slot-datetime/index.tsx index 4c84846..722c9e3 100644 --- a/apps/web/components/schedule/slot-datetime/index.tsx +++ b/apps/web/components/schedule/slot-datetime/index.tsx @@ -3,12 +3,24 @@ import { type SlotComponentProps } from '../types'; import { SlotDate } from './slot-date'; import { SlotTime } from './slot-time'; +import { useSlotQuery } from '@/hooks/api/slots'; import { ScheduleStoreProvider } from '@/stores/schedule'; import { withContext } from '@/utils/context'; export const SlotDateTime = withContext(ScheduleStoreProvider)(function ( props: Readonly, ) { + const { isLoading } = useSlotQuery(props); + + if (isLoading) { + return ( +
+
+
+
+ ); + } + return (
diff --git a/apps/web/providers/telegram.tsx b/apps/web/providers/telegram.tsx index 26aefba..365fa76 100644 --- a/apps/web/providers/telegram.tsx +++ b/apps/web/providers/telegram.tsx @@ -13,7 +13,7 @@ export function TelegramProvider(props: Readonly) { // side. const didMount = useDidMount(); - if (!didMount) return
Loading
; + if (!didMount) return null; return ; }