Integrated certificates & charity page with backend

This commit is contained in:
Umar Adilov 2025-05-03 14:24:21 +05:00
parent 5cdcb8bb02
commit d6f7fbff37
16 changed files with 213 additions and 122 deletions

View File

@ -1,8 +0,0 @@
import { HistoryItems, Reviews, Stations, TeamMembers } from '.';
export type AboutUsPageData = {
team: TeamMembers;
history: HistoryItems;
stations: Stations;
reviews: Reviews;
};

View File

@ -1,4 +1,6 @@
import { import {
presentCertificates,
presentCharities,
presentDiscounts, presentDiscounts,
presentHistoryItems, presentHistoryItems,
presentJobs, presentJobs,
@ -83,6 +85,22 @@ export type Review = Root<{
_rejting: number; _rejting: number;
}>; }>;
export type Charity = Root<{
_name: string;
_opisanie: string;
_data: string;
_lokaciya: string;
_foto: Image[];
}>;
export type Certificate = Root<{
_name: string;
_opisanie: string;
_dataVydachi: string;
_dejstvitelenDo: string;
_foto: Image[];
}>;
export type TeamMembers = ReturnType<typeof presentTeamMembers>; export type TeamMembers = ReturnType<typeof presentTeamMembers>;
export type HistoryItems = ReturnType<typeof presentHistoryItems>; export type HistoryItems = ReturnType<typeof presentHistoryItems>;
export type Stations = ReturnType<typeof presentStations>; export type Stations = ReturnType<typeof presentStations>;
@ -90,3 +108,5 @@ export type Partners = ReturnType<typeof presentPartners>;
export type Jobs = ReturnType<typeof presentJobs>; export type Jobs = ReturnType<typeof presentJobs>;
export type Discounts = ReturnType<typeof presentDiscounts>; export type Discounts = ReturnType<typeof presentDiscounts>;
export type Reviews = ReturnType<typeof presentReviews>; export type Reviews = ReturnType<typeof presentReviews>;
export type Charities = ReturnType<typeof presentCharities>;
export type Certificates = ReturnType<typeof presentCertificates>;

View File

@ -1,8 +0,0 @@
import { Discounts, Jobs, Partners, Stations } from '.';
export type MainPageData = {
discounts: Discounts;
jobs: Jobs;
partners: Partners;
stations: Stations;
};

View File

@ -0,0 +1,33 @@
import {
Certificates,
Charities,
Discounts,
HistoryItems,
Jobs,
Partners,
Reviews,
Stations,
TeamMembers,
} from '.';
export type AboutUsPageData = {
team: TeamMembers;
history: HistoryItems;
stations: Stations;
reviews: Reviews;
};
export type MainPageData = {
discounts: Discounts;
jobs: Jobs;
partners: Partners;
stations: Stations;
};
export type CharityPageData = {
charities: Charities;
};
export type CertificatesPageData = {
certificates: Certificates;
};

View File

@ -1,6 +1,8 @@
import { isEmpty } from 'lodash'; import { isEmpty } from 'lodash';
import { import {
Certificate,
Charity,
Discount, Discount,
History, History,
Image, Image,
@ -92,3 +94,23 @@ export const presentReviews = (reviews: Review) =>
review: review._otzyv, review: review._otzyv,
rating: review._rejting, rating: review._rejting,
})); }));
export const presentCharities = (charities: Charity) =>
charities.records.map((charity, index) => ({
id: index + 1,
name: charity._name,
description: charity._opisanie,
date: charity._data,
location: charity._lokaciya,
image: presentImage(charity._foto),
}));
export const presentCertificates = (certificates: Certificate) =>
certificates.records.map((certificate, index) => ({
id: index + 1,
name: certificate._name,
description: certificate._opisanie,
issuedAt: certificate._dataVydachi,
validUntil: certificate._dejstvitelenDo,
image: presentImage(certificate._foto),
}));

View File

@ -0,0 +1,5 @@
import { certificatesRequest } from './common';
export const certificatesPageRequest = {
...certificatesRequest,
};

View File

@ -0,0 +1,5 @@
import { charityRequest } from './common';
export const charityPageRequest = {
...charityRequest,
};

View File

@ -153,3 +153,31 @@ export const reviewsRequest = {
}, },
}, },
}; };
export const charityRequest = {
_blagotvoriteln: {
records: {
_name: true,
_opisanie: true,
_data: true,
_lokaciya: true,
_foto: {
url: true,
},
},
},
};
export const certificatesRequest = {
_sertifikaty: {
records: {
_name: true,
_opisanie: true,
_dataVydachi: true,
_dejstvitelenDo: true,
_foto: {
url: true,
},
},
},
};

View File

@ -41,7 +41,6 @@ const routeHandler = async (req: NextRequest, requestCookie: RequestCookie) => {
}); });
}; };
export const GET = authorizationMiddleware( export const GET = validationErrorHandler(
validationErrorHandler(routeHandler), authorizationMiddleware(routeHandler, 'bonus__token'),
'bonus__token',
); );

View File

@ -13,7 +13,6 @@ const routeHandler = async (req: NextRequest, requestCookie: RequestCookie) => {
}); });
}; };
export const GET = authorizationMiddleware( export const GET = validationErrorHandler(
validationErrorHandler(routeHandler), authorizationMiddleware(routeHandler, 'corporate__token'),
'corporate__token',
); );

View File

@ -1,5 +1,17 @@
import { CharityPage } from "@/pages-templates/charity" import { CharityPage } from '@/pages-templates/charity';
export default function Charity() { import { mainPageApi } from '@/features/pages/api/pages.api';
return <CharityPage />
} import { makeStore } from '@/shared/store';
export default async function Charity() {
const store = makeStore();
const { data, isLoading, error } = await store.dispatch(
mainPageApi.endpoints.fetchCharityPageContent.initiate(),
);
if (isLoading || !data) return null;
return <CharityPage content={data} />;
}

View File

@ -1,5 +1,17 @@
import { CertificatesPage } from '@/pages-templates/clients/certificates'; import { CertificatesPage } from '@/pages-templates/clients/certificates';
export default function Certificates() { import { mainPageApi } from '@/features/pages/api/pages.api';
return <CertificatesPage />;
import { makeStore } from '@/shared/store';
export default async function Certificates() {
const store = makeStore();
const { data, isLoading, error } = await store.dispatch(
mainPageApi.endpoints.fetchCertificatesPageContent.initiate(),
);
if (isLoading || !data) return null;
return <CertificatesPage content={data} />;
} }

View File

@ -1,8 +1,14 @@
import { jsonToGraphQLQuery } from 'json-to-graphql-query'; import { jsonToGraphQLQuery } from 'json-to-graphql-query';
import { AboutUsPageData } from '@/app/api-utlities/@types/about-us';
import { MainPageData } from '@/app/api-utlities/@types/main';
import { import {
AboutUsPageData,
CertificatesPageData,
CharityPageData,
MainPageData,
} from '@/app/api-utlities/@types/pages';
import {
presentCertificates,
presentCharities,
presentDiscounts, presentDiscounts,
presentHistoryItems, presentHistoryItems,
presentJobs, presentJobs,
@ -11,7 +17,9 @@ import {
presentStations, presentStations,
presentTeamMembers, presentTeamMembers,
} from '@/app/api-utlities/presenters'; } from '@/app/api-utlities/presenters';
import { aboutUsPageRequest } from '@/app/api-utlities/requests/about-us-page.request'; import { aboutUsPageRequest } from '@/app/api-utlities/requests/about-us-page.request copy';
import { certificatesPageRequest } from '@/app/api-utlities/requests/certificates-page.request';
import { charityPageRequest } from '@/app/api-utlities/requests/charity-page.request copy';
import { mainPageRequest } from '@/app/api-utlities/requests/main-page.request'; import { mainPageRequest } from '@/app/api-utlities/requests/main-page.request';
import { taylorAPI } from '@/shared/api/taylor-api'; import { taylorAPI } from '@/shared/api/taylor-api';
@ -37,7 +45,7 @@ export const mainPageApi = taylorAPI.injectEndpoints({
}, },
}), }),
fetchAboutUsPageContent: builder.mutation<AboutUsPageData, void>({ fetchAboutUsPageContent: builder.query<AboutUsPageData, void>({
query: () => ({ query: () => ({
url: '', url: '',
method: 'POST', method: 'POST',
@ -55,5 +63,37 @@ export const mainPageApi = taylorAPI.injectEndpoints({
}; };
}, },
}), }),
fetchCharityPageContent: builder.query<CharityPageData, void>({
query: () => ({
url: '',
method: 'POST',
body: {
query: jsonToGraphQLQuery({ query: charityPageRequest }),
},
}),
transformResponse: (response: any) => {
return {
charities: presentCharities(response.data._blagotvoriteln),
};
},
}),
fetchCertificatesPageContent: builder.query<CertificatesPageData, void>({
query: () => ({
url: '',
method: 'POST',
body: {
query: jsonToGraphQLQuery({ query: certificatesPageRequest }),
},
}),
transformResponse: (response: any) => {
return {
certificates: presentCertificates(response.data._sertifikaty),
};
},
}),
}), }),
}); });

View File

@ -10,6 +10,8 @@ import {
} from 'lucide-react'; } from 'lucide-react';
import Image from 'next/image'; import Image from 'next/image';
import { CharityPageData } from '@/app/api-utlities/@types/pages';
import { Container } from '@/shared/components/container'; import { Container } from '@/shared/components/container';
import { useTextController } from '@/shared/language/hooks/use-text-controller'; import { useTextController } from '@/shared/language/hooks/use-text-controller';
import { import {
@ -27,32 +29,11 @@ export const metadata = {
'Благотворительные проекты и инициативы Ориё. Мы помогаем обществу и заботимся о будущем.', 'Благотворительные проекты и инициативы Ориё. Мы помогаем обществу и заботимся о будущем.',
}; };
const events = [ export interface CharityPageProps {
{ content: CharityPageData;
title: 'Благотворительный марафон', }
description:
'Ежегодный благотворительный марафон в поддержку детей с особыми потребностями.',
date: '15 июня 2023',
location: 'Парк Рудаки, Душанбе',
image: '/placeholder.svg?height=200&width=300&text=Марафон',
},
{
title: 'Экологическая акция',
description: 'Очистка берегов реки Варзоб от мусора и посадка деревьев.',
date: '22 июля 2023',
location: 'Река Варзоб, Душанбе',
image: '/placeholder.svg?height=200&width=300&text=Экологическая+акция',
},
{
title: 'Сбор школьных принадлежностей',
description: 'Сбор школьных принадлежностей для детей из малообеспеченных семей к новому учебному году.',
date: '1-20 августа 2023',
location: 'Все заправки GasNetwork',
image: '/placeholder.svg?height=200&width=300&text=Школьные+принадлежности',
},
];
export function CharityPage() { export function CharityPage({ content }: CharityPageProps) {
const { t } = useTextController(); const { t } = useTextController();
return ( return (
@ -177,7 +158,7 @@ export function CharityPage() {
</div> </div>
<div className='grid gap-6 md:grid-cols-3'> <div className='grid gap-6 md:grid-cols-3'>
{events.map((event, index) => ( {content.charities.map((event, index) => (
<Card <Card
data-aos='zoom-in-up' data-aos='zoom-in-up'
key={index} key={index}
@ -187,14 +168,14 @@ export function CharityPage() {
<div className='relative h-48 w-full'> <div className='relative h-48 w-full'>
<Image <Image
src={event.image || '/placeholder.svg'} src={event.image || '/placeholder.svg'}
alt={event.title} alt={event.name}
fill fill
className='object-cover' className='object-cover'
/> />
</div> </div>
<CardHeader> <CardHeader>
<CardTitle className='text-xl lg:text-2xl'> <CardTitle className='text-xl lg:text-2xl'>
{event.title} {event.name}
</CardTitle> </CardTitle>
</CardHeader> </CardHeader>
<CardContent className='space-y-4'> <CardContent className='space-y-4'>
@ -256,7 +237,7 @@ export function CharityPage() {
{ {
title: 'Распространять информацию', title: 'Распространять информацию',
description: description:
'Расскажите о нашем фонде и его деятельности своим друзьям и знакомым.', 'Расскажите о нашем фонде и его деятельности своим друзьям и знакомым.',
icon: <Heart className='h-10 w-10 text-red-600' />, icon: <Heart className='h-10 w-10 text-red-600' />,
}, },
].map((item, index) => ( ].map((item, index) => (

View File

@ -3,69 +3,20 @@
import { Download, Eye } from 'lucide-react'; import { Download, Eye } from 'lucide-react';
import Image from 'next/image'; import Image from 'next/image';
import { CertificatesPageData } from '@/app/api-utlities/@types/pages';
import { Container } from '@/shared/components/container'; import { Container } from '@/shared/components/container';
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 { Card, CardContent } from '@/shared/shadcn-ui/card'; import { Card, CardContent } from '@/shared/shadcn-ui/card';
export function CertificatesPage() { export interface CertificatesPageProps {
content: CertificatesPageData;
}
export function CertificatesPage({ content }: CertificatesPageProps) {
const { t } = useTextController(); const { t } = useTextController();
// This data would typically come from an API or CMS
// We're keeping it as-is since it's dynamic content
const certificates = [
{
id: 1,
title: 'ISO 9001:2015',
description: 'Сертификат системы менеджмента качества',
image: '/placeholder.svg?height=400&width=300',
issueDate: '15.03.2022',
expiryDate: '15.03.2025',
},
{
id: 2,
title: 'ISO 14001:2015',
description: 'Сертификат экологического менеджмента',
image: '/placeholder.svg?height=400&width=300',
issueDate: '10.05.2022',
expiryDate: '10.05.2025',
},
{
id: 3,
title: 'OHSAS 18001',
description: 'Сертификат системы управления охраной труда',
image: '/placeholder.svg?height=400&width=300',
issueDate: '22.07.2022',
expiryDate: '22.07.2025',
},
{
id: 4,
title: 'Сертификат качества топлива',
description: 'Подтверждение соответствия топлива стандартам качества',
image: '/placeholder.svg?height=400&width=300',
issueDate: '05.01.2023',
expiryDate: '05.01.2024',
},
{
id: 5,
title: 'Сертификат соответствия',
description: 'Соответствие услуг национальным стандартам',
image: '/placeholder.svg?height=400&width=300',
issueDate: '18.09.2022',
expiryDate: '18.09.2025',
},
{
id: 6,
title: 'Лицензия на хранение ГСМ',
description: 'Разрешение на хранение горюче-смазочных материалов',
image: '/placeholder.svg?height=400&width=300',
issueDate: '30.11.2021',
expiryDate: '30.11.2026',
},
];
certificates.length = 0;
return ( return (
<main> <main>
<Container> <Container>
@ -77,7 +28,7 @@ export function CertificatesPage() {
</div> </div>
<div className='grid gap-8 md:grid-cols-2 lg:grid-cols-3'> <div className='grid gap-8 md:grid-cols-2 lg:grid-cols-3'>
{certificates.map((certificate) => ( {content.certificates.map((certificate) => (
<Card <Card
data-aos='zoom-in' data-aos='zoom-in'
key={certificate.id} key={certificate.id}
@ -86,21 +37,21 @@ export function CertificatesPage() {
<div className='relative h-[300px] w-full overflow-hidden bg-gray-100'> <div className='relative h-[300px] w-full overflow-hidden bg-gray-100'>
<Image <Image
src={certificate.image || '/placeholder.svg'} src={certificate.image || '/placeholder.svg'}
alt={certificate.title} alt={certificate.name}
fill fill
className='object-contain p-4' className='object-contain p-4'
sizes='(max-width: 768px) 100vw, (max-width: 1200px) 50vw, 33vw' sizes='(max-width: 768px) 100vw, (max-width: 1200px) 50vw, 33vw'
/> />
</div> </div>
<CardContent className='p-6'> <CardContent className='p-6'>
<h3 className='mb-2 text-xl font-bold'>{certificate.title}</h3> <h3 className='mb-2 text-xl font-bold'>{certificate.name}</h3>
<p className='mb-4 text-gray-600'>{certificate.description}</p> <p className='mb-4 text-gray-600'>{certificate.description}</p>
<div className='mb-4 text-sm text-gray-500'> <div className='mb-4 text-sm text-gray-500'>
<p> <p>
{t('certificates.issueDate')}: {certificate.issueDate} {t('certificates.issueDate')}: {certificate.issuedAt}
</p> </p>
<p> <p>
{t('certificates.expiryDate')}: {certificate.expiryDate} {t('certificates.expiryDate')}: {certificate.validUntil}
</p> </p>
</div> </div>
<div className='flex gap-2'> <div className='flex gap-2'>