Compare commits
No commits in common. "c4872b7323b098a2c92ccb69ae335b7861e5628e" and "24dcaa0122556ac3ded78cde7f72458625c0cb1a" have entirely different histories.
c4872b7323
...
24dcaa0122
@ -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 } = await store.dispatch(
|
const { data, isLoading } = await store.dispatch(
|
||||||
mainPageApi.endpoints.fetchAboutUsPageContent.initiate(),
|
mainPageApi.endpoints.fetchAboutUsPageContent.initiate(),
|
||||||
);
|
);
|
||||||
|
|
||||||
if (!data) return null;
|
if (isLoading || !data) return null;
|
||||||
|
|
||||||
return <AboutPage content={data} />;
|
return <AboutPage content={data} />;
|
||||||
}
|
}
|
||||||
|
|||||||
@ -23,18 +23,22 @@ const routeHandler = async (req: NextRequest) => {
|
|||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
const parsedResponse = JSON.parse(oriyoResponse.data);
|
const { token, card_id } = JSON.parse(oriyoResponse.data);
|
||||||
|
|
||||||
if (!parsedResponse.token) {
|
if (!token) {
|
||||||
return NextResponse.json({ error: 'Credentials error' }, { status: 401 });
|
return NextResponse.json({ error: 'Credentials error' }, { status: 401 });
|
||||||
}
|
}
|
||||||
|
|
||||||
const response = NextResponse.json({ success: true });
|
const response = NextResponse.json({ success: true });
|
||||||
|
|
||||||
response.cookies.set(`${validatedBody.type}__token`, oriyoResponse.data, {
|
response.cookies.set(
|
||||||
|
`${validatedBody.type}__token`,
|
||||||
|
JSON.stringify({ token, card_id }),
|
||||||
|
{
|
||||||
path: '/',
|
path: '/',
|
||||||
maxAge: 2 * 60 * 60,
|
maxAge: 2 * 60 * 60,
|
||||||
});
|
},
|
||||||
|
);
|
||||||
|
|
||||||
return response;
|
return response;
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
|
|||||||
@ -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';
|
||||||
|
|
||||||
const routeHandler = async (req: NextRequest) => {
|
export const routeHandler = async (req: NextRequest) => {
|
||||||
const bonusTokenData = req.cookies.get('bonus__token');
|
const bonusTokenData = req.cookies.get('bonus__token');
|
||||||
|
|
||||||
if (!bonusTokenData) {
|
if (!bonusTokenData) {
|
||||||
|
|||||||
@ -1,63 +0,0 @@
|
|||||||
import { NextRequest, NextResponse } from 'next/server';
|
|
||||||
import { z } from 'zod';
|
|
||||||
|
|
||||||
import oriyoClient from '@/app/api-utlities/utilities/oriyo.client';
|
|
||||||
|
|
||||||
import { validationErrorHandler } from '../../middlewares/error-handler.middleware';
|
|
||||||
|
|
||||||
const validatedSchema = z.object({
|
|
||||||
start_date: z.string().optional(),
|
|
||||||
end_date: z.string().optional(),
|
|
||||||
limit: z.coerce.number(),
|
|
||||||
page: z.coerce.number(),
|
|
||||||
});
|
|
||||||
|
|
||||||
const routeHandler = async (req: NextRequest) => {
|
|
||||||
const bonusTokenData = req.cookies.get('bonus__token');
|
|
||||||
|
|
||||||
if (!bonusTokenData) {
|
|
||||||
return NextResponse.json(
|
|
||||||
{ error: 'User does not have access' },
|
|
||||||
{ status: 401 },
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
const params = Array.from(req.nextUrl.searchParams.entries()).reduce(
|
|
||||||
(pr, cr) => {
|
|
||||||
pr[cr[0]] = cr[1];
|
|
||||||
|
|
||||||
return pr;
|
|
||||||
},
|
|
||||||
{} as Record<string, string>,
|
|
||||||
);
|
|
||||||
|
|
||||||
const validatedRequest = validatedSchema.parse(params);
|
|
||||||
|
|
||||||
const { card_id, token } = JSON.parse(bonusTokenData.value);
|
|
||||||
|
|
||||||
const oriyoResponse = await oriyoClient.get('/client/transactions', {
|
|
||||||
params: {
|
|
||||||
card_id,
|
|
||||||
token,
|
|
||||||
limit: validatedRequest.limit,
|
|
||||||
page: validatedRequest.page,
|
|
||||||
type: 'bonus',
|
|
||||||
sort: 'id',
|
|
||||||
direction: 'desc',
|
|
||||||
start_date: validatedRequest.start_date,
|
|
||||||
end_date: validatedRequest.end_date,
|
|
||||||
},
|
|
||||||
});
|
|
||||||
|
|
||||||
const parsedResponse = JSON.parse(oriyoResponse.data);
|
|
||||||
|
|
||||||
if (parsedResponse.error) {
|
|
||||||
return NextResponse.json({ message: 'Fetch error' }, { status: 400 });
|
|
||||||
}
|
|
||||||
|
|
||||||
return new Response(oriyoResponse.data, {
|
|
||||||
headers: { 'Content-Type': 'application/json' },
|
|
||||||
});
|
|
||||||
};
|
|
||||||
|
|
||||||
export const GET = validationErrorHandler(routeHandler);
|
|
||||||
@ -1,23 +0,0 @@
|
|||||||
import { omit } from 'lodash';
|
|
||||||
import { NextRequest, NextResponse } from 'next/server';
|
|
||||||
|
|
||||||
import { validationErrorHandler } from '../../middlewares/error-handler.middleware';
|
|
||||||
|
|
||||||
const routeHandler = async (req: NextRequest) => {
|
|
||||||
const bonusTokenData = req.cookies.get('corporate__token');
|
|
||||||
|
|
||||||
if (!bonusTokenData) {
|
|
||||||
return NextResponse.json(
|
|
||||||
{ error: 'User does not have access' },
|
|
||||||
{ status: 401 },
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
const parsedData = JSON.parse(bonusTokenData.value);
|
|
||||||
|
|
||||||
return new Response(JSON.stringify(omit(parsedData, 'token')), {
|
|
||||||
headers: { 'Content-Type': 'application/json' },
|
|
||||||
});
|
|
||||||
};
|
|
||||||
|
|
||||||
export const GET = validationErrorHandler(routeHandler);
|
|
||||||
@ -2,10 +2,9 @@ import { NextRequest, NextResponse } from 'next/server';
|
|||||||
import { ZodError } from 'zod';
|
import { ZodError } from 'zod';
|
||||||
|
|
||||||
export const validationErrorHandler =
|
export const validationErrorHandler =
|
||||||
(handler: Function) =>
|
(handler: Function) => async (req: NextRequest, res: NextResponse) => {
|
||||||
async (req: NextRequest, ...args: any[]) => {
|
|
||||||
try {
|
try {
|
||||||
return await handler(req, ...args);
|
return await handler(req, res);
|
||||||
} 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 });
|
||||||
|
|||||||
28
src/app/api/pages/about-us/route.ts
Normal file
28
src/app/api/pages/about-us/route.ts
Normal file
@ -0,0 +1,28 @@
|
|||||||
|
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);
|
||||||
28
src/app/api/pages/main/route.ts
Normal file
28
src/app/api/pages/main/route.ts
Normal file
@ -0,0 +1,28 @@
|
|||||||
|
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);
|
||||||
14
src/app/api/text/route.ts
Normal file
14
src/app/api/text/route.ts
Normal file
@ -0,0 +1,14 @@
|
|||||||
|
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' },
|
||||||
|
},
|
||||||
|
);
|
||||||
|
}
|
||||||
@ -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} min-w-2xs antialiased`}>
|
<body className={`${inter.className} antialiased min-w-2xs`}>
|
||||||
<Providers textItems={response.data as TextItem[]}>
|
<Providers textItems={response.data as TextItem[]}>
|
||||||
<Header />
|
<Header />
|
||||||
{children}
|
{children}
|
||||||
|
|||||||
@ -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, error } = await store.dispatch(
|
const { data, isLoading } = await store.dispatch(
|
||||||
mainPageApi.endpoints.fetchMainPageContent.initiate(),
|
mainPageApi.endpoints.fetchMainPageContent.initiate(),
|
||||||
);
|
);
|
||||||
|
|
||||||
|
|||||||
@ -1,10 +1,6 @@
|
|||||||
import { baseAPI } from '@/shared/api/base-api';
|
import { baseAPI } from '@/shared/api/base-api';
|
||||||
|
|
||||||
import {
|
import { ClientInfo } from '../model/types/bonus-client-info.type';
|
||||||
ClientInfo,
|
|
||||||
TransactionRequest,
|
|
||||||
TransactionResponse,
|
|
||||||
} from '../model/types/bonus-client-info.type';
|
|
||||||
|
|
||||||
export const bonusApi = baseAPI.injectEndpoints({
|
export const bonusApi = baseAPI.injectEndpoints({
|
||||||
endpoints: (builder) => ({
|
endpoints: (builder) => ({
|
||||||
@ -15,19 +11,7 @@ export const bonusApi = baseAPI.injectEndpoints({
|
|||||||
};
|
};
|
||||||
},
|
},
|
||||||
}),
|
}),
|
||||||
fetchBonusTransactions: builder.query<
|
|
||||||
TransactionResponse,
|
|
||||||
TransactionRequest
|
|
||||||
>({
|
|
||||||
query: (request) => {
|
|
||||||
return {
|
|
||||||
url: '/bonus/transactions',
|
|
||||||
params: request,
|
|
||||||
};
|
|
||||||
},
|
|
||||||
}),
|
|
||||||
}),
|
}),
|
||||||
});
|
});
|
||||||
|
|
||||||
export const { useFetchMyBonusInfoQuery, useFetchBonusTransactionsQuery } =
|
export const { useFetchMyBonusInfoQuery } = bonusApi;
|
||||||
bonusApi;
|
|
||||||
|
|||||||
@ -6,29 +6,3 @@ export interface ClientInfo {
|
|||||||
end_date: string;
|
end_date: string;
|
||||||
bonuses: string;
|
bonuses: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface TransactionResponse {
|
|
||||||
transactions: Transaction[];
|
|
||||||
card_id: string;
|
|
||||||
current_page: number;
|
|
||||||
limit: number;
|
|
||||||
total_records: number;
|
|
||||||
total_pages: number;
|
|
||||||
}
|
|
||||||
|
|
||||||
export interface Transaction {
|
|
||||||
id: number;
|
|
||||||
date_create: string;
|
|
||||||
station: string;
|
|
||||||
product_name: string;
|
|
||||||
amount: string;
|
|
||||||
price_real: string;
|
|
||||||
sum_real: string;
|
|
||||||
}
|
|
||||||
|
|
||||||
export interface TransactionRequest {
|
|
||||||
start_date?: string;
|
|
||||||
end_date?: string;
|
|
||||||
page: number;
|
|
||||||
limit: number;
|
|
||||||
}
|
|
||||||
|
|||||||
@ -1,17 +0,0 @@
|
|||||||
import { baseAPI } from '@/shared/api/base-api';
|
|
||||||
|
|
||||||
import { CorporateInfoResponse } from '../model/types/corporate-client-info.type';
|
|
||||||
|
|
||||||
export const corporateApi = baseAPI.injectEndpoints({
|
|
||||||
endpoints: (builder) => ({
|
|
||||||
fetchMyCorporateInfo: builder.query<CorporateInfoResponse, any>({
|
|
||||||
query: () => {
|
|
||||||
return {
|
|
||||||
url: '/corporate/info',
|
|
||||||
};
|
|
||||||
},
|
|
||||||
}),
|
|
||||||
}),
|
|
||||||
});
|
|
||||||
|
|
||||||
export const { useFetchMyCorporateInfoQuery } = corporateApi;
|
|
||||||
@ -1,9 +0,0 @@
|
|||||||
export interface CorporateInfoResponse {
|
|
||||||
created_at: string;
|
|
||||||
fund: string;
|
|
||||||
fund_total: string;
|
|
||||||
group_id: number;
|
|
||||||
group_name: string;
|
|
||||||
overdraft: string;
|
|
||||||
total_cards: number;
|
|
||||||
}
|
|
||||||
@ -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';
|
import { Stations } from '@/app/api-utlities/@types/main';
|
||||||
|
|
||||||
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';
|
||||||
|
|||||||
@ -1,59 +1,19 @@
|
|||||||
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 { taylorAPI } from '@/shared/api/taylor-api';
|
import { baseAPI } from '@/shared/api/base-api';
|
||||||
|
|
||||||
export const mainPageApi = taylorAPI.injectEndpoints({
|
export const mainPageApi = baseAPI.injectEndpoints({
|
||||||
endpoints: (builder) => ({
|
endpoints: (builder) => ({
|
||||||
fetchMainPageContent: builder.query<MainPageData, void>({
|
fetchMainPageContent: builder.query<MainPageData, void>({
|
||||||
query: () => ({
|
query: () => '/pages/main',
|
||||||
url: '',
|
|
||||||
method: 'POST',
|
|
||||||
body: {
|
|
||||||
query: jsonToGraphQLQuery({ query: mainPageRequest }),
|
|
||||||
},
|
|
||||||
}),
|
}),
|
||||||
|
|
||||||
transformResponse: (response: any) => {
|
fetchAboutUsPageContent: builder.query<AboutUsPageData, void>({
|
||||||
return {
|
query: () => '/pages/about-us',
|
||||||
partners: presentPartners(response.data._partners),
|
|
||||||
jobs: presentJobs(response.data._vacancies),
|
|
||||||
discounts: presentDiscounts(response.data._akcii),
|
|
||||||
stations: presentStations(response.data._azs),
|
|
||||||
};
|
|
||||||
},
|
|
||||||
}),
|
|
||||||
|
|
||||||
fetchAboutUsPageContent: builder.mutation<AboutUsPageData, void>({
|
|
||||||
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;
|
||||||
|
|||||||
@ -4,8 +4,6 @@ import { subMonths } from 'date-fns';
|
|||||||
import { Building2, LogOut, Wallet } from 'lucide-react';
|
import { Building2, LogOut, Wallet } from 'lucide-react';
|
||||||
import { useState } from 'react';
|
import { useState } from 'react';
|
||||||
|
|
||||||
import { useFetchMyCorporateInfoQuery } from '@/entities/corporate/api/corporate.api';
|
|
||||||
|
|
||||||
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';
|
||||||
import {
|
import {
|
||||||
@ -98,8 +96,6 @@ export function CorporateDashboard() {
|
|||||||
|
|
||||||
const { t } = useTextController();
|
const { t } = useTextController();
|
||||||
|
|
||||||
const { data, isLoading } = useFetchMyCorporateInfoQuery({});
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className='flex min-h-screen flex-col px-2.5'>
|
<div className='flex min-h-screen flex-col px-2.5'>
|
||||||
<main className='flex-1 py-10'>
|
<main className='flex-1 py-10'>
|
||||||
@ -114,7 +110,7 @@ export function CorporateDashboard() {
|
|||||||
|
|
||||||
<div className='mb-10 grid gap-3 md:grid-cols-3 md:gap-6'>
|
<div className='mb-10 grid gap-3 md:grid-cols-3 md:gap-6'>
|
||||||
{/* Company Card */}
|
{/* Company Card */}
|
||||||
<Card data-aos='zoom-in' data-aos-mirror="true" className='md:col-span-2'>
|
<Card data-aos='zoom-in' data-aos-mirror="true" className='md:col-span-2 order-2 md:order-1'>
|
||||||
<CardHeader className='pb-2'>
|
<CardHeader className='pb-2'>
|
||||||
<CardTitle className='flex items-center gap-2'>
|
<CardTitle className='flex items-center gap-2'>
|
||||||
<Building2 className='h-5 w-5 text-red-600' />
|
<Building2 className='h-5 w-5 text-red-600' />
|
||||||
@ -122,34 +118,26 @@ export function CorporateDashboard() {
|
|||||||
</CardTitle>
|
</CardTitle>
|
||||||
</CardHeader>
|
</CardHeader>
|
||||||
<CardContent>
|
<CardContent>
|
||||||
{!data ? (
|
|
||||||
<>Loading</>
|
|
||||||
) : (
|
|
||||||
<div className='grid gap-6 md:grid-cols-2'>
|
<div className='grid gap-6 md:grid-cols-2'>
|
||||||
<div>
|
<div>
|
||||||
<div className='mb-4 space-y-1'>
|
<div className='mb-4 space-y-1'>
|
||||||
<p className='text-sm text-gray-500'>
|
<p className='text-sm text-gray-500'>
|
||||||
{t('corporate.companyCard.companyNameLabel')}
|
{t('corporate.companyCard.companyNameLabel')}
|
||||||
</p>
|
</p>
|
||||||
<p className='truncate font-medium'>
|
<p className='font-medium'>{companyData.companyName}</p>
|
||||||
{data.group_name}
|
|
||||||
</p>
|
|
||||||
</div>
|
</div>
|
||||||
<div className='mb-4 space-y-1'>
|
<div className='mb-4 space-y-1'>
|
||||||
<p className='text-sm text-gray-500'>
|
<p className='text-sm text-gray-500'>
|
||||||
{t('corporate.companyCard.cardsCountLabel')}
|
{t('corporate.companyCard.cardsCountLabel')}
|
||||||
</p>
|
</p>
|
||||||
<p className='font-medium'>{data.total_cards}</p>
|
<p className='font-medium'>{companyData.numberOfCards}</p>
|
||||||
</div>
|
</div>
|
||||||
<div className='space-y-1'>
|
<div className='space-y-1'>
|
||||||
<p className='text-sm text-gray-500'>
|
<p className='text-sm text-gray-500'>
|
||||||
{t('corporate.companyCard.registrationDateLabel')}
|
{t('corporate.companyCard.registrationDateLabel')}
|
||||||
</p>
|
</p>
|
||||||
|
|
||||||
<p className='font-medium'>
|
<p className='font-medium'>
|
||||||
{new Date(data.created_at).toLocaleDateString(
|
{companyData.registrationDate}
|
||||||
'en-GB',
|
|
||||||
)}
|
|
||||||
</p>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@ -159,7 +147,8 @@ export function CorporateDashboard() {
|
|||||||
{t('corporate.companyCard.fundLabel')}
|
{t('corporate.companyCard.fundLabel')}
|
||||||
</p>
|
</p>
|
||||||
<p className='font-medium'>
|
<p className='font-medium'>
|
||||||
{data.fund.toLocaleString()} {t('corporate.currency')}
|
{companyData.fund.toLocaleString()}{' '}
|
||||||
|
{t('corporate.currency')}
|
||||||
</p>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
<div className='mb-4 space-y-1'>
|
<div className='mb-4 space-y-1'>
|
||||||
@ -167,18 +156,17 @@ export function CorporateDashboard() {
|
|||||||
{t('corporate.companyCard.overdraftLabel')}
|
{t('corporate.companyCard.overdraftLabel')}
|
||||||
</p>
|
</p>
|
||||||
<p className='font-medium'>
|
<p className='font-medium'>
|
||||||
{data.overdraft.toLocaleString()}{' '}
|
{companyData.overdraft.toLocaleString()}{' '}
|
||||||
{t('corporate.currency')}
|
{t('corporate.currency')}
|
||||||
</p>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
)}
|
|
||||||
</CardContent>
|
</CardContent>
|
||||||
</Card>
|
</Card>
|
||||||
|
|
||||||
{/* Fund Card */}
|
{/* Fund Card */}
|
||||||
<Card data-aos='zoom-in' data-aos-mirror="true" className='bg-gradient-to-br from-red-600 to-red-800 text-white'>
|
<Card data-aos='zoom-in' data-aos-mirror="true" className='bg-gradient-to-br from-red-600 to-red-800 text-white order-1 md:order-2'>
|
||||||
<CardHeader>
|
<CardHeader>
|
||||||
<CardTitle className='flex items-center gap-2'>
|
<CardTitle className='flex items-center gap-2'>
|
||||||
<Wallet className='h-5 w-5' />
|
<Wallet className='h-5 w-5' />
|
||||||
@ -189,18 +177,14 @@ export function CorporateDashboard() {
|
|||||||
</CardDescription>
|
</CardDescription>
|
||||||
</CardHeader>
|
</CardHeader>
|
||||||
<CardContent>
|
<CardContent>
|
||||||
{!data ? (
|
|
||||||
<>Loading</>
|
|
||||||
) : (
|
|
||||||
<div className='text-center'>
|
<div className='text-center'>
|
||||||
<p className='mb-2 text-4xl font-bold'>
|
<p className='mb-2 text-4xl font-bold'>
|
||||||
{data.fund_total?.toLocaleString()}
|
{companyData.totalFund.toLocaleString()}
|
||||||
</p>
|
</p>
|
||||||
<p className='text-white/80'>
|
<p className='text-white/80'>
|
||||||
{t('corporate.fundCard.currency')}
|
{t('corporate.fundCard.currency')}
|
||||||
</p>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
)}
|
|
||||||
</CardContent>
|
</CardContent>
|
||||||
</Card>
|
</Card>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@ -4,7 +4,6 @@ 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';
|
||||||
|
|
||||||
@ -17,13 +16,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 = [
|
||||||
{
|
{
|
||||||
@ -42,18 +41,32 @@ const tabs = [
|
|||||||
},
|
},
|
||||||
];
|
];
|
||||||
|
|
||||||
function LoginPageTabs() {
|
export default function LoginPage() {
|
||||||
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);
|
router.push(`?tab=${tabType}`, undefined, { shallow: true });
|
||||||
};
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
|
<Container>
|
||||||
|
<div className='flex min-h-screen flex-col items-center justify-center'>
|
||||||
|
<main className='flex-1'>
|
||||||
|
<div className='container max-w-6xl py-16'>
|
||||||
|
<div className='mb-12 flex flex-col items-center text-center'>
|
||||||
|
<div className='mb-4 inline-flex items-center justify-center rounded-full bg-red-100 p-2'>
|
||||||
|
<Fuel className='h-6 w-6 text-red-600' />
|
||||||
|
</div>
|
||||||
|
<h1 className='mb-4 text-3xl font-bold tracking-tight sm:text-4xl'>
|
||||||
|
{t('auth.title')}
|
||||||
|
</h1>
|
||||||
|
<p className='max-w-2xl text-gray-600'>{t('auth.description')}</p>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div data-aos='zoom-in' className='mx-auto max-w-lg'>
|
||||||
<Tabs
|
<Tabs
|
||||||
defaultValue={defaultTab}
|
defaultValue={defaultTab}
|
||||||
value={defaultTab}
|
value={defaultTab}
|
||||||
@ -94,7 +107,9 @@ function LoginPageTabs() {
|
|||||||
: '/corporate-dashboard'
|
: '/corporate-dashboard'
|
||||||
}
|
}
|
||||||
>
|
>
|
||||||
<Button className='flex items-center'>Открыть</Button>
|
<Button className='flex items-center'>
|
||||||
|
Открыть
|
||||||
|
</Button>
|
||||||
</Link>
|
</Link>
|
||||||
<Button
|
<Button
|
||||||
variant='outline'
|
variant='outline'
|
||||||
@ -127,39 +142,11 @@ function LoginPageTabs() {
|
|||||||
);
|
);
|
||||||
})}
|
})}
|
||||||
</Tabs>
|
</Tabs>
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
export default function LoginPage() {
|
|
||||||
const { t } = useTextController();
|
|
||||||
|
|
||||||
return (
|
|
||||||
<Container>
|
|
||||||
<div className='flex min-h-screen flex-col items-center justify-center'>
|
|
||||||
<main className='flex-1'>
|
|
||||||
<div className='container max-w-6xl py-16'>
|
|
||||||
<div className='mb-12 flex flex-col items-center text-center'>
|
|
||||||
<div className='mb-4 inline-flex items-center justify-center rounded-full bg-red-100 p-2'>
|
|
||||||
<Fuel className='h-6 w-6 text-red-600' />
|
|
||||||
</div>
|
|
||||||
<h1 className='mb-4 text-3xl font-bold tracking-tight sm:text-4xl'>
|
|
||||||
{t('auth.title')}
|
|
||||||
</h1>
|
|
||||||
<p className='max-w-2xl text-gray-600'>{t('auth.description')}</p>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div data-aos='zoom-in' className='mx-auto max-w-lg'>
|
|
||||||
<Suspense>
|
|
||||||
<LoginPageTabs />
|
|
||||||
</Suspense>
|
|
||||||
|
|
||||||
<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
|
<Link href='/contact' className='text-red-600 hover:underline'>
|
||||||
href='/contact'
|
|
||||||
className='text-red-600 hover:underline'
|
|
||||||
>
|
|
||||||
{t('auth.contactLink')}
|
{t('auth.contactLink')}
|
||||||
</Link>
|
</Link>
|
||||||
</p>
|
</p>
|
||||||
|
|||||||
@ -1,14 +0,0 @@
|
|||||||
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: () => ({}),
|
|
||||||
});
|
|
||||||
@ -1,25 +1,12 @@
|
|||||||
import { jsonToGraphQLQuery } from 'json-to-graphql-query';
|
import { baseAPI } from '@/shared/api/base-api';
|
||||||
|
|
||||||
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 = taylorAPI.injectEndpoints({
|
export const textControlApi = baseAPI.injectEndpoints({
|
||||||
endpoints: (builder) => ({
|
endpoints: (builder) => ({
|
||||||
fetchText: builder.query<TextItem[], void>({
|
fetchText: builder.query<TextItem[], void>({
|
||||||
query: () => ({
|
query: () => '/text',
|
||||||
url: '',
|
|
||||||
method: 'POST',
|
|
||||||
body: {
|
|
||||||
query: jsonToGraphQLQuery({ query: textsRequest }),
|
|
||||||
},
|
|
||||||
}),
|
|
||||||
|
|
||||||
transformResponse: (response: any) => {
|
|
||||||
return presentTexts(response.data._kontentSajta);
|
|
||||||
},
|
|
||||||
}),
|
}),
|
||||||
}),
|
}),
|
||||||
});
|
});
|
||||||
|
|
||||||
|
export const { useFetchTextQuery } = textControlApi;
|
||||||
|
|||||||
@ -4,16 +4,13 @@ import { createWrapper } from 'next-redux-wrapper';
|
|||||||
|
|
||||||
import { baseAPI } from '@/shared/api/base-api';
|
import { baseAPI } from '@/shared/api/base-api';
|
||||||
|
|
||||||
import { taylorAPI } from '../api/taylor-api';
|
|
||||||
import { rootReducer } from './root-reducer';
|
import { rootReducer } from './root-reducer';
|
||||||
|
|
||||||
export const makeStore = () =>
|
export const makeStore = () =>
|
||||||
configureStore({
|
configureStore({
|
||||||
reducer: rootReducer,
|
reducer: rootReducer,
|
||||||
middleware: (getDefaultMiddleware) =>
|
middleware: (getDefaultMiddleware) =>
|
||||||
getDefaultMiddleware()
|
getDefaultMiddleware().concat(baseAPI.middleware),
|
||||||
.concat(baseAPI.middleware)
|
|
||||||
.concat(taylorAPI.middleware),
|
|
||||||
devTools: process.env.NODE_ENV === 'development',
|
devTools: process.env.NODE_ENV === 'development',
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|||||||
@ -2,9 +2,6 @@ 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,
|
|
||||||
});
|
});
|
||||||
|
|||||||
@ -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';
|
import { HistoryItems } from '@/app/api-utlities/@types/about-us';
|
||||||
|
|
||||||
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';
|
||||||
|
|||||||
@ -2,8 +2,9 @@
|
|||||||
|
|
||||||
import { MapPin } from 'lucide-react';
|
import { MapPin } from 'lucide-react';
|
||||||
|
|
||||||
import { Stations } from '@/app/api-utlities/@types';
|
import { Stations } from '@/app/api-utlities/@types/main';
|
||||||
|
|
||||||
|
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';
|
||||||
|
|
||||||
|
|||||||
@ -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';
|
import { Partners } from '@/app/api-utlities/@types/main';
|
||||||
|
|
||||||
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';
|
||||||
|
|||||||
@ -3,9 +3,7 @@
|
|||||||
import { format, subMonths } from 'date-fns';
|
import { format, subMonths } from 'date-fns';
|
||||||
import { ru } from 'date-fns/locale';
|
import { ru } from 'date-fns/locale';
|
||||||
import { CalendarIcon } from 'lucide-react';
|
import { CalendarIcon } from 'lucide-react';
|
||||||
import { useEffect, useState } from 'react';
|
import { useState } from 'react';
|
||||||
|
|
||||||
import { useFetchBonusTransactionsQuery } from '@/entities/bonus/api/bonus.api';
|
|
||||||
|
|
||||||
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';
|
||||||
@ -25,30 +23,85 @@ import {
|
|||||||
TableRow,
|
TableRow,
|
||||||
} from '@/shared/shadcn-ui/table';
|
} from '@/shared/shadcn-ui/table';
|
||||||
|
|
||||||
export const TransactionsTable = () => {
|
// Sample customer data
|
||||||
const [startDate, setStartDate] = useState<Date>(subMonths(new Date(), 1));
|
const customerData = {
|
||||||
const [endDate, setEndDate] = useState<Date>(new Date());
|
firstName: 'Алишер',
|
||||||
|
lastName: 'Рахмонов',
|
||||||
|
passportNumber: 'A12345678',
|
||||||
|
bonusPoints: 1250,
|
||||||
|
cardNumber: '5678-9012-3456-7890',
|
||||||
|
expiryDate: '12/2025',
|
||||||
|
registrationDate: '15.06.2020',
|
||||||
|
};
|
||||||
|
|
||||||
const { data, refetch } = useFetchBonusTransactionsQuery({
|
// Sample transaction data
|
||||||
limit: 100,
|
const generateTransactions = () => {
|
||||||
page: 1,
|
const stations = [
|
||||||
start_date: format(startDate, 'yyyy-MM-dd'),
|
'АЗС Душанбе-Центр',
|
||||||
end_date: format(endDate, 'yyyy-MM-dd'),
|
'АЗС Душанбе-Запад',
|
||||||
|
'АЗС Душанбе-Восток',
|
||||||
|
'АЗС Худжанд',
|
||||||
|
'АЗС Куляб',
|
||||||
|
];
|
||||||
|
|
||||||
|
const products = [
|
||||||
|
{ name: 'ДТ', price: 8.5 },
|
||||||
|
{ name: 'АИ-92', price: 9.2 },
|
||||||
|
{ name: 'АИ-95', price: 10.5 },
|
||||||
|
{ name: 'Z-100 Power', price: 11.8 },
|
||||||
|
{ name: 'Пропан', price: 6.3 },
|
||||||
|
];
|
||||||
|
|
||||||
|
const transactions = [];
|
||||||
|
|
||||||
|
// Generate 50 random transactions over the last 6 months
|
||||||
|
for (let i = 0; i < 50; i++) {
|
||||||
|
const date = subMonths(new Date(), Math.random() * 6);
|
||||||
|
const station = stations[Math.floor(Math.random() * stations.length)];
|
||||||
|
const product = products[Math.floor(Math.random() * products.length)];
|
||||||
|
const quantity = Math.floor(Math.random() * 40) + 10; // 10-50 liters
|
||||||
|
const cost = product.price;
|
||||||
|
const total = quantity * cost;
|
||||||
|
|
||||||
|
transactions.push({
|
||||||
|
id: i + 1,
|
||||||
|
date,
|
||||||
|
station,
|
||||||
|
product: product.name,
|
||||||
|
quantity,
|
||||||
|
cost,
|
||||||
|
total,
|
||||||
});
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
// Sort by date (newest first)
|
||||||
|
return transactions.sort((a, b) => b.date.getTime() - a.date.getTime());
|
||||||
|
};
|
||||||
|
|
||||||
|
const transactions = generateTransactions();
|
||||||
|
|
||||||
|
export const TransactionsTable = () => {
|
||||||
|
const [startDate, setStartDate] = useState<Date | undefined>(
|
||||||
|
subMonths(new Date(), 1),
|
||||||
|
);
|
||||||
|
const [endDate, setEndDate] = useState<Date | undefined>(new Date());
|
||||||
|
const [filteredTransactions, setFilteredTransactions] =
|
||||||
|
useState(transactions);
|
||||||
|
|
||||||
// Filter transactions by date range
|
// Filter transactions by date range
|
||||||
const filterTransactions = () => {
|
const filterTransactions = () => {
|
||||||
if (!startDate || !endDate) return;
|
if (!startDate || !endDate) return;
|
||||||
|
|
||||||
refetch();
|
const filtered = transactions.filter((transaction) => {
|
||||||
|
const transactionDate = new Date(transaction.date);
|
||||||
|
return transactionDate >= startDate && transactionDate <= endDate;
|
||||||
|
});
|
||||||
|
|
||||||
|
setFilteredTransactions(filtered);
|
||||||
};
|
};
|
||||||
|
|
||||||
const { t } = useTextController();
|
const { t } = useTextController();
|
||||||
|
|
||||||
useEffect(() => {}, [startDate, endDate]);
|
|
||||||
|
|
||||||
if (!data) return null;
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className='space-y-6'>
|
<div className='space-y-6'>
|
||||||
<div className='flex flex-col items-start justify-between gap-4 md:flex-row md:items-center'>
|
<div className='flex flex-col items-start justify-between gap-4 md:flex-row md:items-center'>
|
||||||
@ -147,22 +200,22 @@ export const TransactionsTable = () => {
|
|||||||
</TableRow>
|
</TableRow>
|
||||||
</TableHeader>
|
</TableHeader>
|
||||||
<TableBody>
|
<TableBody>
|
||||||
{data.transactions.length > 0 ? (
|
{filteredTransactions.length > 0 ? (
|
||||||
data.transactions.map((transaction) => (
|
filteredTransactions.map((transaction) => (
|
||||||
<TableRow key={transaction.id}>
|
<TableRow key={transaction.id}>
|
||||||
<TableCell>
|
<TableCell>
|
||||||
{format(new Date(transaction.date_create), 'dd.MM.yyyy')}
|
{format(transaction.date, 'dd.MM.yyyy')}
|
||||||
</TableCell>
|
</TableCell>
|
||||||
<TableCell>{transaction.station}</TableCell>
|
<TableCell>{transaction.station}</TableCell>
|
||||||
<TableCell>{transaction.product_name}</TableCell>
|
<TableCell>{transaction.product}</TableCell>
|
||||||
<TableCell className='text-right'>
|
<TableCell className='text-right'>
|
||||||
{transaction.price_real}
|
{transaction.quantity}
|
||||||
</TableCell>
|
</TableCell>
|
||||||
<TableCell className='text-right'>
|
<TableCell className='text-right'>
|
||||||
{transaction.amount} {t('corporate.currency')}
|
{transaction.cost.toFixed(2)} {t('corporate.currency')}
|
||||||
</TableCell>
|
</TableCell>
|
||||||
<TableCell className='text-right font-medium'>
|
<TableCell className='text-right font-medium'>
|
||||||
{transaction.sum_real} {t('corporate.currency')}
|
{transaction.total.toFixed(2)} {t('corporate.currency')}
|
||||||
</TableCell>
|
</TableCell>
|
||||||
</TableRow>
|
</TableRow>
|
||||||
))
|
))
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user