refactor(profile): simplify profile page structure and enhance loading states
- Removed unnecessary data fetching and hydration logic from the main profile page. - Updated the rendering of components to improve clarity and performance. - Enhanced loading states in various profile components for better user experience. - Refactored service list handling to utilize telegramId instead of masterId for consistency.
This commit is contained in:
parent
a669e1846e
commit
106fdc0da5
@ -1,10 +1,7 @@
|
||||
import { getCustomer } from '@/actions/api/customers';
|
||||
import { getSessionUser } from '@/actions/session';
|
||||
import { Container } from '@/components/layout';
|
||||
import { PageHeader } from '@/components/navigation';
|
||||
import { ContactDataCard, PersonCard, ProfileOrdersList } from '@/components/profile';
|
||||
import { ReadonlyServicesList } from '@/components/profile/services';
|
||||
import { dehydrate, HydrationBoundary, QueryClient } from '@tanstack/react-query';
|
||||
|
||||
// Тип параметров страницы
|
||||
type Props = { params: Promise<{ telegramId: string }> };
|
||||
@ -12,33 +9,16 @@ type Props = { params: Promise<{ telegramId: string }> };
|
||||
export default async function ProfilePage(props: Readonly<Props>) {
|
||||
const { telegramId } = await props.params;
|
||||
const contactTelegramId = Number(telegramId);
|
||||
const queryClient = new QueryClient();
|
||||
|
||||
// Получаем профиль контакта
|
||||
const { customer: profile } = await queryClient.fetchQuery({
|
||||
queryFn: () => getCustomer({ telegramId: contactTelegramId }),
|
||||
queryKey: ['customer', contactTelegramId],
|
||||
});
|
||||
|
||||
// Получаем текущего пользователя
|
||||
const sessionUser = await getSessionUser();
|
||||
const { customer: currentUser } = await queryClient.fetchQuery({
|
||||
queryFn: () => getCustomer({ telegramId: sessionUser.telegramId }),
|
||||
queryKey: ['customer', sessionUser.telegramId],
|
||||
});
|
||||
|
||||
// Проверка наличия данных
|
||||
if (!profile || !currentUser) return null;
|
||||
|
||||
return (
|
||||
<HydrationBoundary state={dehydrate(queryClient)}>
|
||||
<>
|
||||
<PageHeader title="Профиль контакта" />
|
||||
<Container className="px-0">
|
||||
<PersonCard telegramId={contactTelegramId} />
|
||||
<ContactDataCard telegramId={contactTelegramId} />
|
||||
<ReadonlyServicesList masterId={profile.documentId} />
|
||||
<ReadonlyServicesList telegramId={contactTelegramId} />
|
||||
<ProfileOrdersList telegramId={contactTelegramId} />
|
||||
</Container>
|
||||
</HydrationBoundary>
|
||||
</>
|
||||
);
|
||||
}
|
||||
|
||||
@ -1,34 +1,13 @@
|
||||
import { getCustomer } from '@/actions/api/customers';
|
||||
import { getSubscriptionSettings } from '@/actions/api/subscriptions';
|
||||
import { getSessionUser } from '@/actions/session';
|
||||
import { Container } from '@/components/layout';
|
||||
import { LinksCard, PersonCard, ProfileDataCard, SubscriptionInfoBar } from '@/components/profile';
|
||||
import { dehydrate, HydrationBoundary, QueryClient } from '@tanstack/react-query';
|
||||
|
||||
export default async function ProfilePage() {
|
||||
const queryClient = new QueryClient();
|
||||
const { telegramId } = await getSessionUser();
|
||||
|
||||
await queryClient.prefetchQuery({
|
||||
queryFn: () => getCustomer({ telegramId }),
|
||||
queryKey: ['customer', telegramId],
|
||||
});
|
||||
|
||||
const { subscriptionSetting } = await queryClient.fetchQuery({
|
||||
queryFn: getSubscriptionSettings,
|
||||
queryKey: ['subscriptionSetting'],
|
||||
});
|
||||
|
||||
const proEnabled = subscriptionSetting?.proEnabled;
|
||||
|
||||
export default function ProfilePage() {
|
||||
return (
|
||||
<HydrationBoundary state={dehydrate(queryClient)}>
|
||||
<Container className="px-0">
|
||||
<PersonCard />
|
||||
{proEnabled && <SubscriptionInfoBar />}
|
||||
<ProfileDataCard />
|
||||
<LinksCard />
|
||||
</Container>
|
||||
</HydrationBoundary>
|
||||
<Container className="px-0">
|
||||
<PersonCard />
|
||||
<SubscriptionInfoBar />
|
||||
<ProfileDataCard />
|
||||
<LinksCard />
|
||||
</Container>
|
||||
);
|
||||
}
|
||||
|
||||
@ -12,7 +12,18 @@ import Link from 'next/link';
|
||||
import { useState } from 'react';
|
||||
|
||||
export function ContactDataCard({ telegramId }: Readonly<ProfileProps>) {
|
||||
const { data: { customer } = {} } = useCustomerQuery({ telegramId });
|
||||
const { data: { customer } = {}, isLoading } = useCustomerQuery({ telegramId });
|
||||
|
||||
if (isLoading)
|
||||
return (
|
||||
<Card className="p-4">
|
||||
<div className="flex animate-pulse flex-col gap-4">
|
||||
<div className="h-4 w-16 rounded bg-muted" />
|
||||
<div className="h-6 w-32 rounded bg-muted" />
|
||||
<div className="h-10 w-full rounded bg-muted" />
|
||||
</div>
|
||||
</Card>
|
||||
);
|
||||
|
||||
if (!customer) return null;
|
||||
|
||||
@ -33,10 +44,24 @@ export function ContactDataCard({ telegramId }: Readonly<ProfileProps>) {
|
||||
}
|
||||
|
||||
export function ProfileDataCard() {
|
||||
const { data: { customer } = {} } = useCustomerQuery();
|
||||
const { data: { customer } = {}, isLoading } = useCustomerQuery();
|
||||
const { cancelChanges, hasChanges, isPending, resetTrigger, saveChanges, updateField } =
|
||||
useProfileEdit();
|
||||
|
||||
if (isLoading)
|
||||
return (
|
||||
<Card className="p-4">
|
||||
<div className="flex animate-pulse flex-col gap-4">
|
||||
<div className="h-6 w-32 rounded bg-muted" />
|
||||
<div className="h-4 w-16 rounded bg-muted" />
|
||||
<div className="h-10 w-full rounded bg-muted" />
|
||||
<div className="h-4 w-16 rounded bg-muted" />
|
||||
<div className="h-10 w-full rounded bg-muted" />
|
||||
<div className="h-5 w-60 rounded bg-muted" />
|
||||
</div>
|
||||
</Card>
|
||||
);
|
||||
|
||||
if (!customer) return null;
|
||||
|
||||
return (
|
||||
|
||||
@ -4,16 +4,18 @@ import { type ProfileProps } from './types';
|
||||
import { UserAvatar } from '@/components/shared/user-avatar';
|
||||
import { useCustomerQuery } from '@/hooks/api/customers';
|
||||
import { Card } from '@repo/ui/components/ui/card';
|
||||
import { LoadingSpinner } from '@repo/ui/components/ui/spinner';
|
||||
|
||||
export function PersonCard({ telegramId }: Readonly<ProfileProps>) {
|
||||
const { data: { customer } = {}, isLoading } = useCustomerQuery({ telegramId });
|
||||
|
||||
if (isLoading)
|
||||
return (
|
||||
<div className="p-4">
|
||||
<LoadingSpinner />
|
||||
</div>
|
||||
<Card className="bg-transparent p-4 shadow-none">
|
||||
<div className="flex animate-pulse flex-col items-center space-y-2">
|
||||
<div className="size-32 rounded-full bg-muted" />
|
||||
<div className="h-6 w-40 rounded bg-muted" />
|
||||
</div>
|
||||
</Card>
|
||||
);
|
||||
|
||||
if (!customer) return null;
|
||||
|
||||
@ -1,5 +1,6 @@
|
||||
'use client';
|
||||
|
||||
import { type ProfileProps } from '../types';
|
||||
import { DataNotFound } from '@/components/shared/alert';
|
||||
import { ServiceCard } from '@/components/shared/service-card';
|
||||
import { useCustomerQuery } from '@/hooks/api/customers';
|
||||
@ -7,13 +8,9 @@ import { useServicesQuery } from '@/hooks/api/services';
|
||||
import { LoadingSpinner } from '@repo/ui/components/ui/spinner';
|
||||
import Link from 'next/link';
|
||||
|
||||
type MasterServicesListProps = {
|
||||
masterId: string;
|
||||
};
|
||||
|
||||
// Компонент для отображения услуг мастера (без ссылок, только просмотр)
|
||||
export function ReadonlyServicesList({ masterId }: Readonly<MasterServicesListProps>) {
|
||||
const { isLoading, services } = useServices(masterId);
|
||||
export function ReadonlyServicesList({ telegramId }: Readonly<ProfileProps>) {
|
||||
const { isLoading, services } = useServices(telegramId);
|
||||
|
||||
return (
|
||||
<div className="space-y-2 px-4">
|
||||
@ -54,17 +51,17 @@ export function ServicesList() {
|
||||
);
|
||||
}
|
||||
|
||||
function useServices(masterId?: string) {
|
||||
function useServices(telegramId?: Readonly<ProfileProps>['telegramId']) {
|
||||
const { data: { customer } = {}, isLoading: isLoadingCustomer } = useCustomerQuery();
|
||||
|
||||
// Используем переданный masterId или текущего пользователя
|
||||
const targetMasterId = masterId || customer?.documentId;
|
||||
const targetTelegramId = telegramId || customer?.telegramId;
|
||||
|
||||
const { data: { services } = {}, isLoading: isLoadingServices } = useServicesQuery({
|
||||
filters: {
|
||||
master: {
|
||||
documentId: {
|
||||
eq: targetMasterId,
|
||||
telegramId: {
|
||||
eq: targetTelegramId,
|
||||
},
|
||||
},
|
||||
},
|
||||
|
||||
@ -2,26 +2,26 @@
|
||||
'use client';
|
||||
|
||||
import { useCustomerQuery } from '@/hooks/api/customers';
|
||||
import { useSubscriptionQuery } from '@/hooks/api/subscriptions';
|
||||
import { useSubscriptionQuery, useSubscriptionSettingQuery } from '@/hooks/api/subscriptions';
|
||||
import { Enum_Customer_Role } from '@repo/graphql/types';
|
||||
import { cn } from '@repo/ui/lib/utils';
|
||||
import { ChevronRight } from 'lucide-react';
|
||||
import Link from 'next/link';
|
||||
|
||||
export function SubscriptionInfoBar() {
|
||||
const { data, error, isLoading } = useSubscriptionQuery();
|
||||
const { data: { subscriptionSetting } = {}, isLoading: isLoadingSubscriptionSetting } =
|
||||
useSubscriptionSettingQuery();
|
||||
const { data, isLoading: isLoadingSubscription } = useSubscriptionQuery();
|
||||
|
||||
const { data: { customer } = {} } = useCustomerQuery();
|
||||
const { data: { customer } = {}, isLoading: isLoadingCustomer } = useCustomerQuery();
|
||||
|
||||
const isLoading = isLoadingCustomer || isLoadingSubscription || isLoadingSubscriptionSetting;
|
||||
|
||||
const isActive = data?.hasActiveSubscription;
|
||||
const remainingOrdersCount = data?.remainingOrdersCount;
|
||||
const remainingDays = data?.remainingDays;
|
||||
const maxOrdersPerMonth = data?.maxOrdersPerMonth;
|
||||
|
||||
if (customer?.role === Enum_Customer_Role.Client) return null;
|
||||
|
||||
if (error) return null;
|
||||
|
||||
const title = isActive ? 'Pro доступ активен' : 'Pro доступ неактивен';
|
||||
|
||||
let description = 'Попробуйте бесплатно';
|
||||
@ -34,6 +34,10 @@ export function SubscriptionInfoBar() {
|
||||
description = `Осталось ${remainingDays} дней`;
|
||||
}
|
||||
|
||||
if (!subscriptionSetting?.proEnabled) return null;
|
||||
|
||||
if (customer?.role === Enum_Customer_Role.Client) return null;
|
||||
|
||||
return (
|
||||
<Link href="/pro" rel="noopener noreferrer">
|
||||
<div className={cn('px-4', isLoading && 'animate-pulse')}>
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user