From 240aaa81aeb7f037a1987b332d0ffe0e7131e814 Mon Sep 17 00:00:00 2001 From: BunyodL Date: Tue, 29 Apr 2025 01:22:24 +0500 Subject: [PATCH 1/4] update: get main-page-data --- src/app/api-utlities/@types/index.ts | 2 +- src/app/api-utlities/@types/main.ts | 18 ++++++++++++++++++ src/app/api-utlities/presenters/index.ts | 2 +- src/app/page.tsx | 9 ++++++++- 4 files changed, 28 insertions(+), 3 deletions(-) create mode 100644 src/app/api-utlities/@types/main.ts diff --git a/src/app/api-utlities/@types/index.ts b/src/app/api-utlities/@types/index.ts index 705be9f..326bc14 100644 --- a/src/app/api-utlities/@types/index.ts +++ b/src/app/api-utlities/@types/index.ts @@ -45,6 +45,6 @@ export type Station = Root<{ _propanCopy: boolean; _zaryadnayaStanci: boolean; _miniMarketCop: boolean; - _region: Select; + _region: Select[]; _foto: Image[]; }>; diff --git a/src/app/api-utlities/@types/main.ts b/src/app/api-utlities/@types/main.ts new file mode 100644 index 0000000..881fcc3 --- /dev/null +++ b/src/app/api-utlities/@types/main.ts @@ -0,0 +1,18 @@ +import { + presentDiscounts, + presentJobs, + presentPartners, + presentStations, +} from '../presenters'; + +export type Partners = ReturnType; +export type Jobs = ReturnType; +export type Discounts = ReturnType; +export type Stations = ReturnType; + +export type MainPageData = { + discounts: Discounts; + jobs: Jobs; + partners: Partners; + stations: Stations; +}; diff --git a/src/app/api-utlities/presenters/index.ts b/src/app/api-utlities/presenters/index.ts index 1333fd2..d623ffe 100644 --- a/src/app/api-utlities/presenters/index.ts +++ b/src/app/api-utlities/presenters/index.ts @@ -28,7 +28,7 @@ export const presentDiscounts = (discounts: Discount) => })); export const presentStations = (stations: Station) => - stations.records.map((station: any) => ({ + stations.records.map((station) => ({ name: station._name, description: station._opisanie, address: station._adress, diff --git a/src/app/page.tsx b/src/app/page.tsx index 95e56b6..5126d32 100644 --- a/src/app/page.tsx +++ b/src/app/page.tsx @@ -8,7 +8,14 @@ import { PromotionsSection } from '@/widgets/promotions-section'; import { StatsSection } from '@/widgets/stats-section'; import { VacanciesSection } from '@/widgets/vacancies-section'; -export default function Home() { +import { MainPageData } from './api-utlities/@types/main'; + +export default async function Home() { + const mainPageData = (await fetch( + `${process.env.NEXT_PUBLIC_BASE_URL}/api/pages/main`, + { method: 'GET' }, + ).then((res) => res.json())) as MainPageData; + return (
From 2810c6b9fb30a8f99238feaddefa6fc761b075da Mon Sep 17 00:00:00 2001 From: BunyodL Date: Wed, 30 Apr 2025 17:34:33 +0500 Subject: [PATCH 2/4] update: render main-page data --- next.config.ts | 12 +++- package.json | 1 + pnpm-lock.yaml | 23 ++++++- src/app/api-utlities/presenters/index.ts | 12 ++-- src/app/page.tsx | 8 +-- src/features/map/model/index.ts | 4 ++ src/features/map/ui/gas-station-map.tsx | 10 ++- src/features/map/ui/yandex-map.tsx | 38 +++++++++++ src/shared/components/promotion-slider.tsx | 71 ++++++++++++-------- src/widgets/map-section.tsx | 18 ++++- src/widgets/partners-section.tsx | 21 ++++-- src/widgets/promotions-section.tsx | 12 +++- src/widgets/vacancies-section.tsx | 78 +++++++++++----------- 13 files changed, 217 insertions(+), 91 deletions(-) create mode 100644 src/features/map/model/index.ts create mode 100644 src/features/map/ui/yandex-map.tsx diff --git a/next.config.ts b/next.config.ts index e9ffa30..f7d9caa 100644 --- a/next.config.ts +++ b/next.config.ts @@ -1,7 +1,15 @@ -import type { NextConfig } from "next"; +import type { NextConfig } from 'next'; const nextConfig: NextConfig = { - /* config options here */ + images: { + remotePatterns: [ + { + protocol: 'https', + hostname: 'media.bambooapp.ai', + pathname: '/files/**', + }, + ], + }, }; export default nextConfig; diff --git a/package.json b/package.json index 2c17b8a..191ea7b 100644 --- a/package.json +++ b/package.json @@ -11,6 +11,7 @@ }, "dependencies": { "@hookform/resolvers": "^5.0.1", + "@pbe/react-yandex-maps": "^1.2.5", "@radix-ui/react-collapsible": "^1.1.8", "@radix-ui/react-dialog": "^1.1.11", "@radix-ui/react-dropdown-menu": "^2.1.11", diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 94c0c7a..212d6d8 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -11,6 +11,9 @@ importers: '@hookform/resolvers': specifier: ^5.0.1 version: 5.0.1(react-hook-form@7.56.1(react@19.1.0)) + '@pbe/react-yandex-maps': + specifier: ^1.2.5 + version: 1.2.5(react@19.1.0) '@radix-ui/react-collapsible': specifier: ^1.1.8 version: 1.1.8(@types/react-dom@19.1.2(@types/react@19.1.2))(@types/react@19.1.2)(react-dom@19.1.0(react@19.1.0))(react@19.1.0) @@ -517,6 +520,12 @@ packages: resolution: {integrity: sha512-nn5ozdjYQpUCZlWGuxcJY/KpxkWQs4DcbMCmKojjyrYDEAGy4Ce19NN4v5MduafTwJlbKc99UA8YhSVqq9yPZA==} engines: {node: '>=12.4.0'} + '@pbe/react-yandex-maps@1.2.5': + resolution: {integrity: sha512-cBojin5e1fPx9XVCAqHQJsCnHGMeBNsP0TrNfpWCrPFfxb30ye+JgcGr2mn767Gbr1d+RufBLRiUcX2kaiAwjQ==} + engines: {node: '>=16'} + peerDependencies: + react: ^16.x || ^17.x || ^18.x + '@pkgr/core@0.2.4': resolution: {integrity: sha512-ROFF39F6ZrnzSUEmQQZUar0Jt4xVoP9WnDRdWwF4NNcXs3xBTLgBUDoOwW141y1jP+S8nahIbdxbFC7IShw9Iw==} engines: {node: ^12.20.0 || ^14.18.0 || >=16.0.0} @@ -1106,6 +1115,9 @@ packages: '@types/use-sync-external-store@0.0.6': resolution: {integrity: sha512-zFDAD+tlpf2r4asuHEj0XH6pY6i0g5NeAHPn+15wk3BV6JA69eERFXC1gyGThDkVa1zCyKr5jox1+2LbV/AMLg==} + '@types/yandex-maps@2.1.29': + resolution: {integrity: sha512-nuibRWj3RU/9KXlCzTrRtDE+n6V9l7NbT9JakicqZ5OXIdwyb6blvV2Uwn6lB58WYm3DSUDP2I2AWlnWMc8z2w==} + '@typescript-eslint/eslint-plugin@8.30.1': resolution: {integrity: sha512-v+VWphxMjn+1t48/jO4t950D6KR8JaJuNXzi33Ve6P8sEmPr5k6CEXjdGwT6+LodVnEa91EQCtwjWNUCPweo+Q==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} @@ -2983,6 +2995,11 @@ snapshots: '@nolyfill/is-core-module@1.0.39': {} + '@pbe/react-yandex-maps@1.2.5(react@19.1.0)': + dependencies: + '@types/yandex-maps': 2.1.29 + react: 19.1.0 + '@pkgr/core@0.2.4': {} '@radix-ui/number@1.1.1': {} @@ -3555,6 +3572,8 @@ snapshots: '@types/use-sync-external-store@0.0.6': {} + '@types/yandex-maps@2.1.29': {} + '@typescript-eslint/eslint-plugin@8.30.1(@typescript-eslint/parser@8.30.1(eslint@9.25.0(jiti@2.4.2))(typescript@5.8.3))(eslint@9.25.0(jiti@2.4.2))(typescript@5.8.3)': dependencies: '@eslint-community/regexpp': 4.12.1 @@ -4106,7 +4125,7 @@ snapshots: transitivePeerDependencies: - supports-color - eslint-module-utils@2.12.0(@typescript-eslint/parser@8.30.1(eslint@9.25.0(jiti@2.4.2))(typescript@5.8.3))(eslint-import-resolver-node@0.3.9)(eslint-import-resolver-typescript@3.10.0(eslint-plugin-import-x@4.10.6(eslint@9.25.0(jiti@2.4.2))(typescript@5.8.3))(eslint-plugin-import@2.31.0)(eslint@9.25.0(jiti@2.4.2)))(eslint@9.25.0(jiti@2.4.2)): + eslint-module-utils@2.12.0(@typescript-eslint/parser@8.30.1(eslint@9.25.0(jiti@2.4.2))(typescript@5.8.3))(eslint-import-resolver-node@0.3.9)(eslint-import-resolver-typescript@3.10.0)(eslint@9.25.0(jiti@2.4.2)): dependencies: debug: 3.2.7 optionalDependencies: @@ -4148,7 +4167,7 @@ snapshots: doctrine: 2.1.0 eslint: 9.25.0(jiti@2.4.2) eslint-import-resolver-node: 0.3.9 - eslint-module-utils: 2.12.0(@typescript-eslint/parser@8.30.1(eslint@9.25.0(jiti@2.4.2))(typescript@5.8.3))(eslint-import-resolver-node@0.3.9)(eslint-import-resolver-typescript@3.10.0(eslint-plugin-import-x@4.10.6(eslint@9.25.0(jiti@2.4.2))(typescript@5.8.3))(eslint-plugin-import@2.31.0)(eslint@9.25.0(jiti@2.4.2)))(eslint@9.25.0(jiti@2.4.2)) + eslint-module-utils: 2.12.0(@typescript-eslint/parser@8.30.1(eslint@9.25.0(jiti@2.4.2))(typescript@5.8.3))(eslint-import-resolver-node@0.3.9)(eslint-import-resolver-typescript@3.10.0)(eslint@9.25.0(jiti@2.4.2)) hasown: 2.0.2 is-core-module: 2.16.1 is-glob: 4.0.3 diff --git a/src/app/api-utlities/presenters/index.ts b/src/app/api-utlities/presenters/index.ts index d623ffe..6d18dc7 100644 --- a/src/app/api-utlities/presenters/index.ts +++ b/src/app/api-utlities/presenters/index.ts @@ -6,13 +6,15 @@ export const presentImage = (images: Image[]) => isEmpty(images) ? null : `${process.env.TAYLOR_MEDIA_URL}/${images[0].url}`; export const presentPartners = (partners: Partner) => - partners.records.map((record) => ({ + partners.records.map((record, index) => ({ + id: index + 1, name: record._name, poster: presentImage(record._image), })); export const presentJobs = (jobs: Job) => - jobs.records.map((job) => ({ + jobs.records.map((job, index) => ({ + id: index + 1, name: job._name, tags: job._tags.map((tag) => tag.name), location: !isEmpty(job._localtio) ? job._localtio[0].name : null, @@ -20,7 +22,8 @@ export const presentJobs = (jobs: Job) => })); export const presentDiscounts = (discounts: Discount) => - discounts.records.map((discount) => ({ + discounts.records.map((discount, index) => ({ + id: index + 1, name: discount._name, description: discount._opisanie, expiresAt: discount._do, @@ -28,7 +31,8 @@ export const presentDiscounts = (discounts: Discount) => })); export const presentStations = (stations: Station) => - stations.records.map((station) => ({ + stations.records.map((station, index) => ({ + id: index + 1, name: station._name, description: station._opisanie, address: station._adress, diff --git a/src/app/page.tsx b/src/app/page.tsx index 5126d32..9322edb 100644 --- a/src/app/page.tsx +++ b/src/app/page.tsx @@ -20,11 +20,11 @@ export default async function Home() {
- + - - - + + +
diff --git a/src/features/map/model/index.ts b/src/features/map/model/index.ts new file mode 100644 index 0000000..8d16b29 --- /dev/null +++ b/src/features/map/model/index.ts @@ -0,0 +1,4 @@ +export type Point = { + id: number; + coordinates: [number, number]; +}; diff --git a/src/features/map/ui/gas-station-map.tsx b/src/features/map/ui/gas-station-map.tsx index 5ef8a15..a65a2dd 100644 --- a/src/features/map/ui/gas-station-map.tsx +++ b/src/features/map/ui/gas-station-map.tsx @@ -10,6 +10,8 @@ import { } from 'lucide-react'; import { useEffect, useRef, useState } from 'react'; +import { Stations } from '@/app/api-utlities/@types/main'; + import { useLanguage } from '@/shared/language'; import { Badge } from '@/shared/shadcn-ui/badge'; import { Button } from '@/shared/shadcn-ui/button'; @@ -168,7 +170,13 @@ const allFilters = [ // Extract unique cities from stations const allCities = [...new Set(stations.map((station) => station.city))].sort(); -export default function GasStationMap() { +interface GasStationMapProps { + stations: Stations; +} + +export default function GasStationMap({ + stations: _stations, +}: GasStationMapProps) { const { t } = useLanguage(); const mapRef = useRef(null); const [activeFilters, setActiveFilters] = useState([]); diff --git a/src/features/map/ui/yandex-map.tsx b/src/features/map/ui/yandex-map.tsx new file mode 100644 index 0000000..fb8cacb --- /dev/null +++ b/src/features/map/ui/yandex-map.tsx @@ -0,0 +1,38 @@ +'use client'; + +import { Map, Placemark, YMaps } from '@pbe/react-yandex-maps'; + +import { Point } from '../model'; + +type YandexMapProps = { + points: Point[]; +}; + +export const YandexMap = ({ points }: YandexMapProps) => { + return ( + + + {points.map((point) => ( + + ))} + + + ); +}; diff --git a/src/shared/components/promotion-slider.tsx b/src/shared/components/promotion-slider.tsx index 3a25dd5..8741d51 100644 --- a/src/shared/components/promotion-slider.tsx +++ b/src/shared/components/promotion-slider.tsx @@ -1,12 +1,15 @@ 'use client'; -import { ChevronLeft, ChevronRight} from 'lucide-react'; +import { ChevronLeft, ChevronRight } from 'lucide-react'; import Image from 'next/image'; +import Link from 'next/link'; import { useEffect, useState } from 'react'; +import { Discounts } from '@/app/api-utlities/@types/main'; + import { Button } from '@/shared/shadcn-ui/button'; import { Card, CardContent } from '@/shared/shadcn-ui/card'; -import Link from 'next/link'; + import { useLanguage } from '../language'; const promotions = [ @@ -41,11 +44,15 @@ const promotions = [ }, ]; -export default function PromotionSlider() { +interface PromotionSliderProps { + discounts: Discounts; +} + +export default function PromotionSlider({ discounts }: PromotionSliderProps) { const [currentIndex, setCurrentIndex] = useState(0); const [visibleItems, setVisibleItems] = useState(3); - const { t } = useLanguage() + const { t } = useLanguage(); useEffect(() => { const handleResize = () => { @@ -85,7 +92,7 @@ export default function PromotionSlider() { transform: `translateX(-${currentIndex * (100 / visibleItems)}%)`, }} > - {promotions.map((promo) => ( + {discounts.map((promo) => (
{promo.title}
-

{promo.title}

+

{promo.name}

{promo.description}

- Действует до: {promo.validUntil} + {promo.expiresAt + ? `Действует до: ${promo.expiresAt}` + : null} - - - +
))} - - + {discounts.length > 3 && ( + <> + + + + )} ); } diff --git a/src/widgets/map-section.tsx b/src/widgets/map-section.tsx index 90f5150..0538d15 100644 --- a/src/widgets/map-section.tsx +++ b/src/widgets/map-section.tsx @@ -2,13 +2,26 @@ import { MapPin } from 'lucide-react'; +import { Stations } from '@/app/api-utlities/@types/main'; + import { GasStationMap } from '@/features/map'; +import { Point } from '@/features/map/model'; +import { YandexMap } from '@/features/map/ui/yandex-map'; import { useLanguage } from '@/shared/language'; -export const MapSection = () => { +interface MapSectionProps { + stations: Stations; +} + +export const MapSection = ({ stations }: MapSectionProps) => { const { t } = useLanguage(); + const points = stations.map((st) => ({ + id: st.id, + coordinates: [st.latitude, st.longitude], + })) as Point[]; + return (
@@ -27,7 +40,8 @@ export const MapSection = () => { className='h-[500px] overflow-hidden rounded-xl border shadow-lg' data-aos='fade-up' > - + + {/* */}
diff --git a/src/widgets/partners-section.tsx b/src/widgets/partners-section.tsx index f4ccadc..649ea70 100644 --- a/src/widgets/partners-section.tsx +++ b/src/widgets/partners-section.tsx @@ -4,10 +4,16 @@ import { Handshake } from 'lucide-react'; import Image from 'next/image'; import Link from 'next/link'; +import { Partners } from '@/app/api-utlities/@types/main'; + import { useLanguage } from '@/shared/language'; import { Button } from '@/shared/shadcn-ui/button'; -export const PartnersSection = () => { +interface PartnersSectionProps { + partners: Partners; +} + +export const PartnersSection = ({ partners }: PartnersSectionProps) => { const { t } = useLanguage(); return ( @@ -26,20 +32,23 @@ export const PartnersSection = () => {
- {[1, 2, 3, 4, 5, 6, 7, 8].map((partner) => ( + {partners.map(({ id, name, poster }) => (
{`Partner -

Название

+

{name}

))}
diff --git a/src/widgets/promotions-section.tsx b/src/widgets/promotions-section.tsx index d4b5d2b..10afd21 100644 --- a/src/widgets/promotions-section.tsx +++ b/src/widgets/promotions-section.tsx @@ -2,14 +2,20 @@ import { Gift } from 'lucide-react'; +import { Discounts } from '@/app/api-utlities/@types/main'; + import PromotionSlider from '@/shared/components/promotion-slider'; import { useLanguage } from '@/shared/language'; -export const PromotionsSection = () => { +interface PromotionsSectionProps { + discounts: Discounts; +} + +export const PromotionsSection = ({ discounts }: PromotionsSectionProps) => { const { t } = useLanguage(); return ( -
+
@@ -22,7 +28,7 @@ export const PromotionsSection = () => { {t('home.promotions.description')}

- +
); diff --git a/src/widgets/vacancies-section.tsx b/src/widgets/vacancies-section.tsx index 7888fa8..db2fd6e 100644 --- a/src/widgets/vacancies-section.tsx +++ b/src/widgets/vacancies-section.tsx @@ -2,6 +2,8 @@ import { Briefcase } from 'lucide-react'; +import { Jobs } from '@/app/api-utlities/@types/main'; + import { useLanguage } from '@/shared/language'; import { cn } from '@/shared/lib/utils'; import { Badge } from '@/shared/shadcn-ui/badge'; @@ -14,9 +16,24 @@ import { TabsTrigger, } from '@/shared/shadcn-ui/tabs'; -export const VacanciesSection = () => { +interface VacanciesSectionProps { + jobs: Jobs; +} + +export const VacanciesSection = ({ jobs }: VacanciesSectionProps) => { const { t } = useLanguage(); + const jobsByType = new Map(); + + jobs.forEach((job) => { + const existing = jobsByType.get(job.type) || []; + jobsByType.set(job.type, [...existing, job]); + }); + + const allVacancies = t('home.vacancies.all'); + + const jobsTabsTitle = [allVacancies, ...Array.from(jobsByType.keys())]; + return (
@@ -32,53 +49,38 @@ export const VacanciesSection = () => {

- + - {t('home.vacancies.all')} - {t('home.vacancies.office')} - {t('home.vacancies.stations')} + {jobsTabsTitle.map((type) => ( + + {type} + + ))} - - {[ - 'Оператор АЗС', - 'Менеджер по продажам', - 'Бухгалтер', - 'Специалист по логистике', - ].map((job, index) => ( + + + {jobs.map((job, index) => ( ))} - - {[ - 'Менеджер по продажам', - 'Бухгалтер', - 'Специалист по логистике', - ].map((job, index) => ( - - ))} - - - {['Оператор АЗС', 'Заправщик', 'Менеджер станции'].map( - (job, index) => ( + + {Array.from(jobsByType.entries()).map(([type, jobs]) => ( + + {jobs.map((job: Jobs[number], index: number) => ( - ), - )} - + ))} + + ))}
From 6dc26611c3d95ce7514494757cc63ac62d33671d Mon Sep 17 00:00:00 2001 From: BunyodL Date: Wed, 30 Apr 2025 18:13:25 +0500 Subject: [PATCH 3/4] fix: fix text display --- src/app/layout.tsx | 13 +++++-------- 1 file changed, 5 insertions(+), 8 deletions(-) diff --git a/src/app/layout.tsx b/src/app/layout.tsx index 7766960..b4515b9 100644 --- a/src/app/layout.tsx +++ b/src/app/layout.tsx @@ -1,9 +1,7 @@ import type { Metadata } from 'next'; import { Inter } from 'next/font/google'; -import { textControlApi } from '@/shared/language/api/text-control.api'; import { Providers } from '@/shared/providers/providers'; -import { makeStore } from '@/shared/store'; import { TextItem } from '@/shared/types/text.types'; import { Footer } from '@/widgets/footer'; @@ -24,11 +22,10 @@ export default async function RootLayout({ }: Readonly<{ children: React.ReactNode; }>) { - const store = makeStore(); - - const response = await store.dispatch( - textControlApi.endpoints.fetchText.initiate(), - ); + const response = (await fetch( + `${process.env.NEXT_PUBLIC_BASE_URL}/api/text`, + { method: 'GET' }, + ).then((res) => res.json())) as TextItem[]; return ( - +
{children}