Fixed issue with SSR

This commit is contained in:
Umar Adilov 2025-05-01 23:25:37 +05:00
parent 148fd20b66
commit 6deb48239e
20 changed files with 189 additions and 177 deletions

View File

@ -7,11 +7,11 @@ import { makeStore } from '@/shared/store';
export default async function About() { export default async function About() {
const store = makeStore(); const store = makeStore();
const { data, isLoading } = await store.dispatch( const { data } = await store.dispatch(
mainPageApi.endpoints.fetchAboutUsPageContent.initiate(), mainPageApi.endpoints.fetchAboutUsPageContent.initiate(),
); );
if (isLoading || !data) return null; if (!data) return null;
return <AboutPage content={data} />; return <AboutPage content={data} />;
} }

View File

@ -4,7 +4,7 @@ import oriyoClient from '@/app/api-utlities/utilities/oriyo.client';
import { validationErrorHandler } from '../../middlewares/error-handler.middleware'; import { validationErrorHandler } from '../../middlewares/error-handler.middleware';
export const routeHandler = async (req: NextRequest) => { const routeHandler = async (req: NextRequest) => {
const bonusTokenData = req.cookies.get('bonus__token'); const bonusTokenData = req.cookies.get('bonus__token');
if (!bonusTokenData) { if (!bonusTokenData) {

View File

@ -2,9 +2,9 @@ import { NextRequest, NextResponse } from 'next/server';
import { ZodError } from 'zod'; import { ZodError } from 'zod';
export const validationErrorHandler = export const validationErrorHandler =
(handler: Function) => async (req: NextRequest, res: NextResponse) => { (handler: Function) => async (req: NextRequest) => {
try { try {
return await handler(req, res); return await handler(req);
} catch (error) { } catch (error) {
if (error instanceof ZodError) if (error instanceof ZodError)
return NextResponse.json({ message: error.format() }, { status: 400 }); return NextResponse.json({ message: error.format() }, { status: 400 });

View File

@ -1,28 +0,0 @@
import {
presentHistoryItems,
presentReviews,
presentStations,
presentTeamMembers,
} from '@/app/api-utlities/presenters';
import { aboutUsPageRequest } from '@/app/api-utlities/requests/about-us-page.request';
import { requestTaylor } from '@/app/api-utlities/utilities/taylor.client';
import { validationErrorHandler } from '../../middlewares/error-handler.middleware';
const routeHandler = async () => {
const response = await requestTaylor(aboutUsPageRequest);
return new Response(
JSON.stringify({
team: presentTeamMembers(response.data._komanda),
history: presentHistoryItems(response.data._istoriya),
stations: presentStations(response.data._azs),
reviews: presentReviews(response.data._otzyvy),
}),
{
headers: { 'Content-Type': 'application/json' },
},
);
};
export const GET = validationErrorHandler(routeHandler);

View File

@ -1,28 +0,0 @@
import {
presentDiscounts,
presentJobs,
presentPartners,
presentStations,
} from '@/app/api-utlities/presenters';
import { mainPageRequest } from '@/app/api-utlities/requests/main-page.request';
import { requestTaylor } from '@/app/api-utlities/utilities/taylor.client';
import { validationErrorHandler } from '../../middlewares/error-handler.middleware';
const routeHandler = async (request: Request) => {
const response = await requestTaylor(mainPageRequest);
return new Response(
JSON.stringify({
partners: presentPartners(response.data._partners),
jobs: presentJobs(response.data._vacancies),
discounts: presentDiscounts(response.data._akcii),
stations: presentStations(response.data._azs),
}),
{
headers: { 'Content-Type': 'application/json' },
},
);
};
export const GET = validationErrorHandler(routeHandler);

View File

@ -1,14 +0,0 @@
import { presentTexts } from '@/app/api-utlities/presenters';
import { textsRequest } from '@/app/api-utlities/requests/common';
import { requestTaylor } from '@/app/api-utlities/utilities/taylor.client';
export async function GET(request: Request) {
const response = await requestTaylor(textsRequest);
return new Response(
JSON.stringify(presentTexts(response.data._kontentSajta)),
{
headers: { 'Content-Type': 'application/json' },
},
);
}

View File

@ -37,7 +37,7 @@ export default async function RootLayout({
className='scroll-smooth' className='scroll-smooth'
style={{ scrollBehavior: 'smooth' }} style={{ scrollBehavior: 'smooth' }}
> >
<body className={`${inter.className} antialiased min-w-2xs`}> <body className={`${inter.className} min-w-2xs antialiased`}>
<Providers textItems={response.data as TextItem[]}> <Providers textItems={response.data as TextItem[]}>
<Header /> <Header />
{children} {children}

View File

@ -15,7 +15,7 @@ import { VacanciesSection } from '@/widgets/vacancies-section';
export default async function Home() { export default async function Home() {
const store = makeStore(); const store = makeStore();
const { data, isLoading } = await store.dispatch( const { data, isLoading, error } = await store.dispatch(
mainPageApi.endpoints.fetchMainPageContent.initiate(), mainPageApi.endpoints.fetchMainPageContent.initiate(),
); );

View File

@ -10,7 +10,7 @@ import {
} from 'lucide-react'; } from 'lucide-react';
import { useEffect, useRef, useState } from 'react'; import { useEffect, useRef, useState } from 'react';
import { Stations } from '@/app/api-utlities/@types/main'; import { Stations } from '@/app/api-utlities/@types';
import { useTextController } from '@/shared/language/hooks/use-text-controller'; import { useTextController } from '@/shared/language/hooks/use-text-controller';
import { Badge } from '@/shared/shadcn-ui/badge'; import { Badge } from '@/shared/shadcn-ui/badge';

View File

@ -1,19 +1,59 @@
import { jsonToGraphQLQuery } from 'json-to-graphql-query';
import { AboutUsPageData } from '@/app/api-utlities/@types/about-us'; import { AboutUsPageData } from '@/app/api-utlities/@types/about-us';
import { MainPageData } from '@/app/api-utlities/@types/main'; import { MainPageData } from '@/app/api-utlities/@types/main';
import {
presentDiscounts,
presentHistoryItems,
presentJobs,
presentPartners,
presentReviews,
presentStations,
presentTeamMembers,
} from '@/app/api-utlities/presenters';
import { aboutUsPageRequest } from '@/app/api-utlities/requests/about-us-page.request';
import { mainPageRequest } from '@/app/api-utlities/requests/main-page.request';
import { baseAPI } from '@/shared/api/base-api'; import { taylorAPI } from '@/shared/api/taylor-api';
export const mainPageApi = baseAPI.injectEndpoints({ export const mainPageApi = taylorAPI.injectEndpoints({
endpoints: (builder) => ({ endpoints: (builder) => ({
fetchMainPageContent: builder.query<MainPageData, void>({ fetchMainPageContent: builder.query<MainPageData, void>({
query: () => '/pages/main', query: () => ({
url: '',
method: 'POST',
body: {
query: jsonToGraphQLQuery({ query: mainPageRequest }),
},
}),
transformResponse: (response: any) => {
return {
partners: presentPartners(response.data._partners),
jobs: presentJobs(response.data._vacancies),
discounts: presentDiscounts(response.data._akcii),
stations: presentStations(response.data._azs),
};
},
}), }),
fetchAboutUsPageContent: builder.query<AboutUsPageData, void>({ fetchAboutUsPageContent: builder.mutation<AboutUsPageData, void>({
query: () => '/pages/about-us', query: () => ({
url: '',
method: 'POST',
body: {
query: jsonToGraphQLQuery({ query: aboutUsPageRequest }),
},
}),
transformResponse: (response: any) => {
return {
team: presentTeamMembers(response.data._komanda),
history: presentHistoryItems(response.data._istoriya),
stations: presentStations(response.data._azs),
reviews: presentReviews(response.data._otzyvy),
};
},
}), }),
}), }),
}); });
export const { useFetchMainPageContentQuery, useFetchAboutUsPageContentQuery } =
mainPageApi;

View File

@ -4,6 +4,7 @@ import { deleteCookie, getCookie } from 'cookies-next';
import { Building2, Fuel, User } from 'lucide-react'; import { Building2, Fuel, User } from 'lucide-react';
import Link from 'next/link'; import Link from 'next/link';
import { useRouter, useSearchParams } from 'next/navigation'; import { useRouter, useSearchParams } from 'next/navigation';
import { Suspense } from 'react';
import { LoginForm } from '@/features/auth/login-form'; import { LoginForm } from '@/features/auth/login-form';
@ -16,13 +17,13 @@ import {
CardHeader, CardHeader,
CardTitle, CardTitle,
} from '@/shared/shadcn-ui/card'; } from '@/shared/shadcn-ui/card';
import Container from '@/shared/shadcn-ui/conteiner';
import { import {
Tabs, Tabs,
TabsContent, TabsContent,
TabsList, TabsList,
TabsTrigger, TabsTrigger,
} from '@/shared/shadcn-ui/tabs'; } from '@/shared/shadcn-ui/tabs';
import Container from '@/shared/shadcn-ui/conteiner';
const tabs = [ const tabs = [
{ {
@ -41,16 +42,97 @@ const tabs = [
}, },
]; ];
export default function LoginPage() { function LoginPageTabs() {
const { t } = useTextController(); const { t } = useTextController();
const router = useRouter(); const router = useRouter();
const searchParams = useSearchParams(); const searchParams = useSearchParams();
const defaultTab = searchParams.get('tab') || 'bonus'; const defaultTab = searchParams.get('tab') || 'bonus';
const handleTabChange = (tabType: string) => { const handleTabChange = (tabType: string) => {
router.push(`?tab=${tabType}`, undefined, { shallow: true }); router.push(`?tab=${tabType}`, undefined);
}; };
return (
<Tabs
defaultValue={defaultTab}
value={defaultTab}
onValueChange={handleTabChange}
className='w-full'
>
<TabsList className='mb-8 flex h-fit w-full flex-col sm:flex-row'>
{tabs.map((tab) => {
return (
<TabsTrigger
key={tab.label}
value={tab.type}
className='w-full text-base'
>
<tab.Icon className='mr-2 h-4 w-4' /> {t(tab.label)}
</TabsTrigger>
);
})}
</TabsList>
{tabs.map((tab) => {
const tabCookieName = `${tab.type}__token`;
const authenticationCookie = getCookie(tabCookieName);
if (authenticationCookie) {
return (
<TabsContent key={tab.label} value={tab.type}>
<Card>
<CardHeader>
<CardTitle>{t(tab.title)}</CardTitle>
</CardHeader>
<CardContent className='flex justify-center gap-2 space-y-4'>
<Link
href={
tab.type === 'bonus'
? '/customer-dashboard'
: '/corporate-dashboard'
}
>
<Button className='flex items-center'>Открыть</Button>
</Link>
<Button
variant='outline'
className='flex items-center gap-2'
onClick={() => {
deleteCookie(tabCookieName);
window.location.reload();
}}
>
Выйти
</Button>
</CardContent>
</Card>
</TabsContent>
);
}
return (
<TabsContent key={tab.label} value={tab.type}>
<Card>
<CardHeader>
<CardTitle>{t(tab.title)}</CardTitle>
<CardDescription>{t(tab.description)}</CardDescription>
</CardHeader>
<CardContent className='space-y-4'>
<LoginForm type={tab.type} />
</CardContent>
</Card>
</TabsContent>
);
})}
</Tabs>
);
}
export default function LoginPage() {
const { t } = useTextController();
return ( return (
<Container> <Container>
<div className='flex min-h-screen flex-col items-center justify-center'> <div className='flex min-h-screen flex-col items-center justify-center'>
@ -67,86 +149,17 @@ export default function LoginPage() {
</div> </div>
<div data-aos='zoom-in' className='mx-auto max-w-lg'> <div data-aos='zoom-in' className='mx-auto max-w-lg'>
<Tabs <Suspense>
defaultValue={defaultTab} <LoginPageTabs />
value={defaultTab} </Suspense>
onValueChange={handleTabChange}
className='w-full'
>
<TabsList className='mb-8 flex h-fit w-full flex-col sm:flex-row'>
{tabs.map((tab) => {
return (
<TabsTrigger
key={tab.label}
value={tab.type}
className='w-full text-base'
>
<tab.Icon className='mr-2 h-4 w-4' /> {t(tab.label)}
</TabsTrigger>
);
})}
</TabsList>
{tabs.map((tab) => {
const tabCookieName = `${tab.type}__token`;
const authenticationCookie = getCookie(tabCookieName);
if (authenticationCookie) {
return (
<TabsContent key={tab.label} value={tab.type}>
<Card>
<CardHeader>
<CardTitle>{t(tab.title)}</CardTitle>
</CardHeader>
<CardContent className='flex justify-center gap-2 space-y-4'>
<Link
href={
tab.type === 'bonus'
? '/customer-dashboard'
: '/corporate-dashboard'
}
>
<Button className='flex items-center'>
Открыть
</Button>
</Link>
<Button
variant='outline'
className='flex items-center gap-2'
onClick={() => {
deleteCookie(tabCookieName);
window.location.reload();
}}
>
Выйти
</Button>
</CardContent>
</Card>
</TabsContent>
);
}
return (
<TabsContent key={tab.label} value={tab.type}>
<Card>
<CardHeader>
<CardTitle>{t(tab.title)}</CardTitle>
<CardDescription>{t(tab.description)}</CardDescription>
</CardHeader>
<CardContent className='space-y-4'>
<LoginForm type={tab.type} />
</CardContent>
</Card>
</TabsContent>
);
})}
</Tabs>
<div className='mt-8 text-center text-sm text-gray-500'> <div className='mt-8 text-center text-sm text-gray-500'>
<p> <p>
{t('auth.loginIssues')}{' '} {t('auth.loginIssues')}{' '}
<Link href='/contact' className='text-red-600 hover:underline'> <Link
href='/contact'
className='text-red-600 hover:underline'
>
{t('auth.contactLink')} {t('auth.contactLink')}
</Link> </Link>
</p> </p>

View File

@ -0,0 +1,14 @@
import { createApi, fetchBaseQuery } from '@reduxjs/toolkit/query';
const baseQuery = fetchBaseQuery({
baseUrl: process.env.TAYLOR_API_ENDPOINT,
headers: {
Authorization: process.env.TAYLOR_API_TOKEN || '',
},
});
export const taylorAPI = createApi({
reducerPath: 'taylorAPI',
baseQuery,
endpoints: () => ({}),
});

View File

@ -5,7 +5,7 @@ import Image from 'next/image';
import Link from 'next/link'; import Link from 'next/link';
import { useEffect, useState } from 'react'; import { useEffect, useState } from 'react';
import { Discounts } from '@/app/api-utlities/@types/main'; import { Discounts } from '@/app/api-utlities/@types';
import { useTextController } from '@/shared/language/hooks/use-text-controller'; import { useTextController } from '@/shared/language/hooks/use-text-controller';
import { Button } from '@/shared/shadcn-ui/button'; import { Button } from '@/shared/shadcn-ui/button';

View File

@ -1,12 +1,25 @@
import { baseAPI } from '@/shared/api/base-api'; import { jsonToGraphQLQuery } from 'json-to-graphql-query';
import { presentTexts } from '@/app/api-utlities/presenters';
import { textsRequest } from '@/app/api-utlities/requests/common';
import { taylorAPI } from '@/shared/api/taylor-api';
import { TextItem } from '@/shared/types/text.types'; import { TextItem } from '@/shared/types/text.types';
export const textControlApi = baseAPI.injectEndpoints({ export const textControlApi = taylorAPI.injectEndpoints({
endpoints: (builder) => ({ endpoints: (builder) => ({
fetchText: builder.query<TextItem[], void>({ fetchText: builder.query<TextItem[], void>({
query: () => '/text', query: () => ({
url: '',
method: 'POST',
body: {
query: jsonToGraphQLQuery({ query: textsRequest }),
},
}),
transformResponse: (response: any) => {
return presentTexts(response.data._kontentSajta);
},
}), }),
}), }),
}); });
export const { useFetchTextQuery } = textControlApi;

View File

@ -2,6 +2,9 @@ import { combineReducers } from '@reduxjs/toolkit';
import { baseAPI } from '@/shared/api/base-api'; import { baseAPI } from '@/shared/api/base-api';
import { taylorAPI } from '../api/taylor-api';
export const rootReducer = combineReducers({ export const rootReducer = combineReducers({
[baseAPI.reducerPath]: baseAPI.reducer, [baseAPI.reducerPath]: baseAPI.reducer,
[taylorAPI.reducerPath]: taylorAPI.reducer,
}); });

View File

@ -3,7 +3,7 @@
import { Calendar, ChevronDown, ChevronUp } from 'lucide-react'; import { Calendar, ChevronDown, ChevronUp } from 'lucide-react';
import { useState } from 'react'; import { useState } from 'react';
import { HistoryItems } from '@/app/api-utlities/@types/about-us'; import { HistoryItems } from '@/app/api-utlities/@types';
import { useTextController } from '@/shared/language/hooks/use-text-controller'; import { useTextController } from '@/shared/language/hooks/use-text-controller';
import { Button } from '@/shared/shadcn-ui/button'; import { Button } from '@/shared/shadcn-ui/button';

View File

@ -2,9 +2,8 @@
import { MapPin } from 'lucide-react'; import { MapPin } from 'lucide-react';
import { Stations } from '@/app/api-utlities/@types/main'; import { Stations } from '@/app/api-utlities/@types';
import { GasStationMap } from '@/features/map';
import { Point } from '@/features/map/model'; import { Point } from '@/features/map/model';
import { YandexMap } from '@/features/map/ui/yandex-map'; import { YandexMap } from '@/features/map/ui/yandex-map';

View File

@ -4,7 +4,7 @@ import { Handshake } from 'lucide-react';
import Image from 'next/image'; import Image from 'next/image';
import Link from 'next/link'; import Link from 'next/link';
import { Partners } from '@/app/api-utlities/@types/main'; import { Partners } from '@/app/api-utlities/@types';
import { useTextController } from '@/shared/language/hooks/use-text-controller'; import { useTextController } from '@/shared/language/hooks/use-text-controller';
import { Button } from '@/shared/shadcn-ui/button'; import { Button } from '@/shared/shadcn-ui/button';

View File

@ -2,7 +2,7 @@
import { Gift } from 'lucide-react'; import { Gift } from 'lucide-react';
import { Discounts } from '@/app/api-utlities/@types/main'; import { Discounts } from '@/app/api-utlities/@types';
import PromotionSlider from '@/shared/components/promotion-slider'; import PromotionSlider from '@/shared/components/promotion-slider';
import { useTextController } from '@/shared/language/hooks/use-text-controller'; import { useTextController } from '@/shared/language/hooks/use-text-controller';

View File

@ -2,7 +2,7 @@
import { Briefcase } from 'lucide-react'; import { Briefcase } from 'lucide-react';
import { Jobs } from '@/app/api-utlities/@types/main'; import { Jobs } from '@/app/api-utlities/@types';
import { useTextController } from '@/shared/language/hooks/use-text-controller'; import { useTextController } from '@/shared/language/hooks/use-text-controller';
import { cn } from '@/shared/lib/utils'; import { cn } from '@/shared/lib/utils';