integrated text control provider
This commit is contained in:
parent
e0592eb581
commit
83451cf358
@ -32,6 +32,7 @@
|
||||
"lodash": "^4.17.21",
|
||||
"lucide-react": "^0.501.0",
|
||||
"next": "15.3.1",
|
||||
"next-redux-wrapper": "^8.1.0",
|
||||
"next-themes": "^0.4.6",
|
||||
"react": "^19.0.0",
|
||||
"react-day-picker": "8.10.1",
|
||||
|
||||
16
pnpm-lock.yaml
generated
16
pnpm-lock.yaml
generated
@ -74,6 +74,9 @@ importers:
|
||||
next:
|
||||
specifier: 15.3.1
|
||||
version: 15.3.1(react-dom@19.1.0(react@19.1.0))(react@19.1.0)
|
||||
next-redux-wrapper:
|
||||
specifier: ^8.1.0
|
||||
version: 8.1.0(next@15.3.1(react-dom@19.1.0(react@19.1.0))(react@19.1.0))(react-redux@9.2.0(@types/react@19.1.2)(react@19.1.0)(redux@5.0.1))(react@19.1.0)
|
||||
next-themes:
|
||||
specifier: ^0.4.6
|
||||
version: 0.4.6(react-dom@19.1.0(react@19.1.0))(react@19.1.0)
|
||||
@ -2117,6 +2120,13 @@ packages:
|
||||
natural-compare@1.4.0:
|
||||
resolution: {integrity: sha512-OWND8ei3VtNC9h7V60qff3SVobHr996CTwgxubgyQYEpg290h9J0buyECNNJexkFm5sOajh5G116RYA1c8ZMSw==}
|
||||
|
||||
next-redux-wrapper@8.1.0:
|
||||
resolution: {integrity: sha512-2hIau0hcI6uQszOtrvAFqgc0NkZegKYhBB7ZAKiG3jk7zfuQb4E7OV9jfxViqqojh3SEHdnFfPkN9KErttUKuw==}
|
||||
peerDependencies:
|
||||
next: '>=9'
|
||||
react: '*'
|
||||
react-redux: '*'
|
||||
|
||||
next-themes@0.4.6:
|
||||
resolution: {integrity: sha512-pZvgD5L0IEvX5/9GWyHMf3m8BKiVQwsCMHfoFosXtXBMnaS0ZnIJ9ST4b4NqLVKDEm8QBxoNNGNaBv2JNF6XNA==}
|
||||
peerDependencies:
|
||||
@ -4720,6 +4730,12 @@ snapshots:
|
||||
|
||||
natural-compare@1.4.0: {}
|
||||
|
||||
next-redux-wrapper@8.1.0(next@15.3.1(react-dom@19.1.0(react@19.1.0))(react@19.1.0))(react-redux@9.2.0(@types/react@19.1.2)(react@19.1.0)(redux@5.0.1))(react@19.1.0):
|
||||
dependencies:
|
||||
next: 15.3.1(react-dom@19.1.0(react@19.1.0))(react@19.1.0)
|
||||
react: 19.1.0
|
||||
react-redux: 9.2.0(@types/react@19.1.2)(react@19.1.0)(redux@5.0.1)
|
||||
|
||||
next-themes@0.4.6(react-dom@19.1.0(react@19.1.0))(react@19.1.0):
|
||||
dependencies:
|
||||
react: 19.1.0
|
||||
|
||||
@ -1,7 +1,10 @@
|
||||
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';
|
||||
import { Header } from '@/widgets/header/ui';
|
||||
@ -16,20 +19,26 @@ export const metadata: Metadata = {
|
||||
'Качественное топливо, удобное расположение и отличный сервис для наших клиентов',
|
||||
};
|
||||
|
||||
export default function RootLayout({
|
||||
export default async function RootLayout({
|
||||
children,
|
||||
}: Readonly<{
|
||||
children: React.ReactNode;
|
||||
}>) {
|
||||
const store = makeStore();
|
||||
|
||||
const response = await store.dispatch(
|
||||
textControlApi.endpoints.fetchText.initiate(),
|
||||
);
|
||||
|
||||
return (
|
||||
<html
|
||||
lang='en'
|
||||
lang='ru'
|
||||
suppressHydrationWarning
|
||||
className='scroll-smooth'
|
||||
style={{ scrollBehavior: 'smooth' }}
|
||||
>
|
||||
<body className={`${inter.className} antialiased`}>
|
||||
<Providers>
|
||||
<Providers textItems={response.data as TextItem[]}>
|
||||
<Header />
|
||||
{children}
|
||||
<Footer />
|
||||
|
||||
@ -8,7 +8,7 @@ import { toast } from 'sonner';
|
||||
import { useLazyLoginQuery } from '@/entities/auth';
|
||||
|
||||
import { SubmitButton } from '@/shared/components/submit-button';
|
||||
import { useLanguage } from '@/shared/language';
|
||||
import { useTextController } from '@/shared/language/hooks/use-text-controller';
|
||||
import {
|
||||
Form,
|
||||
FormControl,
|
||||
@ -26,7 +26,7 @@ interface LoginFormProps {
|
||||
}
|
||||
|
||||
export const LoginForm = ({ type }: LoginFormProps) => {
|
||||
const { t } = useLanguage();
|
||||
const { t } = useTextController();
|
||||
|
||||
const router = useRouter();
|
||||
const [login, { isLoading: isLoginLoading }] = useLazyLoginQuery();
|
||||
|
||||
@ -10,7 +10,7 @@ import {
|
||||
} from 'lucide-react';
|
||||
import { useEffect, useRef, useState } from 'react';
|
||||
|
||||
import { useLanguage } from '@/shared/language';
|
||||
import { useTextController } from '@/shared/language/hooks/use-text-controller';
|
||||
import { Badge } from '@/shared/shadcn-ui/badge';
|
||||
import { Button } from '@/shared/shadcn-ui/button';
|
||||
import {
|
||||
@ -169,7 +169,7 @@ const allFilters = [
|
||||
const allCities = [...new Set(stations.map((station) => station.city))].sort();
|
||||
|
||||
export default function GasStationMap() {
|
||||
const { t } = useLanguage();
|
||||
const { t } = useTextController();
|
||||
const mapRef = useRef<HTMLDivElement>(null);
|
||||
const [activeFilters, setActiveFilters] = useState<string[]>([]);
|
||||
const [activeCities, setActiveCities] = useState<string[]>([]);
|
||||
|
||||
@ -1,9 +1,10 @@
|
||||
"use client"
|
||||
'use client';
|
||||
|
||||
import { subMonths } from 'date-fns';
|
||||
import { Building2, LogOut, Wallet } from 'lucide-react';
|
||||
import { useState } from 'react';
|
||||
|
||||
import { useTextController } from '@/shared/language/hooks/use-text-controller';
|
||||
import { Button } from '@/shared/shadcn-ui/button';
|
||||
import {
|
||||
Card,
|
||||
@ -12,7 +13,7 @@ import {
|
||||
CardHeader,
|
||||
CardTitle,
|
||||
} from '@/shared/shadcn-ui/card';
|
||||
import { useLanguage } from '@/shared/language';
|
||||
|
||||
import { TransactionsTable } from '@/widgets/transactions-table';
|
||||
|
||||
// import { CardsList } from '@/widgets/cards-list';
|
||||
@ -93,7 +94,7 @@ export function CorporateDashboard() {
|
||||
setFilteredTransactions(filtered);
|
||||
};
|
||||
|
||||
const {t} = useLanguage()
|
||||
const { t } = useTextController();
|
||||
|
||||
return (
|
||||
<div className='flex min-h-screen flex-col'>
|
||||
@ -120,15 +121,21 @@ export function CorporateDashboard() {
|
||||
<div className='grid gap-6 md:grid-cols-2'>
|
||||
<div>
|
||||
<div className='mb-4 space-y-1'>
|
||||
<p className='text-sm text-gray-500'>{t('corporate.companyCard.companyNameLabel')}</p>
|
||||
<p className='text-sm text-gray-500'>
|
||||
{t('corporate.companyCard.companyNameLabel')}
|
||||
</p>
|
||||
<p className='font-medium'>{companyData.companyName}</p>
|
||||
</div>
|
||||
<div className='mb-4 space-y-1'>
|
||||
<p className='text-sm text-gray-500'>{t('corporate.companyCard.cardsCountLabel')}</p>
|
||||
<p className='text-sm text-gray-500'>
|
||||
{t('corporate.companyCard.cardsCountLabel')}
|
||||
</p>
|
||||
<p className='font-medium'>{companyData.numberOfCards}</p>
|
||||
</div>
|
||||
<div className='space-y-1'>
|
||||
<p className='text-sm text-gray-500'>{t('corporate.companyCard.registrationDateLabel')}</p>
|
||||
<p className='text-sm text-gray-500'>
|
||||
{t('corporate.companyCard.registrationDateLabel')}
|
||||
</p>
|
||||
<p className='font-medium'>
|
||||
{companyData.registrationDate}
|
||||
</p>
|
||||
@ -136,15 +143,21 @@ export function CorporateDashboard() {
|
||||
</div>
|
||||
<div>
|
||||
<div className='mb-4 space-y-1'>
|
||||
<p className='text-sm text-gray-500'>{t('corporate.companyCard.fundLabel')}</p>
|
||||
<p className='text-sm text-gray-500'>
|
||||
{t('corporate.companyCard.fundLabel')}
|
||||
</p>
|
||||
<p className='font-medium'>
|
||||
{companyData.fund.toLocaleString()} {t('corporate.currency')}
|
||||
{companyData.fund.toLocaleString()}{' '}
|
||||
{t('corporate.currency')}
|
||||
</p>
|
||||
</div>
|
||||
<div className='mb-4 space-y-1'>
|
||||
<p className='text-sm text-gray-500'>{t('corporate.companyCard.overdraftLabel')}</p>
|
||||
<p className='text-sm text-gray-500'>
|
||||
{t('corporate.companyCard.overdraftLabel')}
|
||||
</p>
|
||||
<p className='font-medium'>
|
||||
{companyData.overdraft.toLocaleString()} {t('corporate.currency')}
|
||||
{companyData.overdraft.toLocaleString()}{' '}
|
||||
{t('corporate.currency')}
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
@ -168,7 +181,9 @@ export function CorporateDashboard() {
|
||||
<p className='mb-2 text-4xl font-bold'>
|
||||
{companyData.totalFund.toLocaleString()}
|
||||
</p>
|
||||
<p className='text-white/80'>{t('corporate.fundCard.currency')}</p>
|
||||
<p className='text-white/80'>
|
||||
{t('corporate.fundCard.currency')}
|
||||
</p>
|
||||
</div>
|
||||
</CardContent>
|
||||
</Card>
|
||||
|
||||
@ -1,7 +1,8 @@
|
||||
"use client"
|
||||
'use client';
|
||||
|
||||
import { ArrowUpRight, Clock, CreditCard, LogOut, User } from 'lucide-react';
|
||||
|
||||
import { useTextController } from '@/shared/language/hooks/use-text-controller';
|
||||
import { Button } from '@/shared/shadcn-ui/button';
|
||||
import {
|
||||
Card,
|
||||
@ -12,7 +13,6 @@ import {
|
||||
} from '@/shared/shadcn-ui/card';
|
||||
|
||||
import { TransactionsTable } from '@/widgets/transactions-table';
|
||||
import { useLanguage } from '@/shared/language';
|
||||
|
||||
// Sample customer data
|
||||
const customerData = {
|
||||
@ -26,8 +26,7 @@ const customerData = {
|
||||
};
|
||||
|
||||
export function CustomerDashboard() {
|
||||
|
||||
const {t} = useLanguage()
|
||||
const { t } = useTextController();
|
||||
|
||||
return (
|
||||
<div className='flex min-h-screen flex-col'>
|
||||
@ -58,7 +57,9 @@ export function CustomerDashboard() {
|
||||
<p className='mb-2 text-4xl font-bold'>
|
||||
{customerData.bonusPoints}
|
||||
</p>
|
||||
<p className='text-white/80'>{t('customer.bonusCard.points')}</p>
|
||||
<p className='text-white/80'>
|
||||
{t('customer.bonusCard.points')}
|
||||
</p>
|
||||
</div>
|
||||
<div className='mt-6 flex items-center justify-between'>
|
||||
<div className='flex items-center gap-1 text-sm text-white/80'>
|
||||
@ -81,13 +82,17 @@ export function CustomerDashboard() {
|
||||
<div className='grid gap-6 md:grid-cols-2'>
|
||||
<div>
|
||||
<div className='mb-4 space-y-1'>
|
||||
<p className='text-sm text-gray-500'>{t('customer.infoCard.regDateLabel')}</p>
|
||||
<p className='text-sm text-gray-500'>
|
||||
{t('customer.infoCard.regDateLabel')}
|
||||
</p>
|
||||
<p className='font-medium'>
|
||||
{customerData.firstName} {customerData.lastName}
|
||||
</p>
|
||||
</div>
|
||||
<div className='space-y-1'>
|
||||
<p className='text-sm text-gray-500'>{t('customer.infoCard.regDateLabel')}</p>
|
||||
<p className='text-sm text-gray-500'>
|
||||
{t('customer.infoCard.regDateLabel')}
|
||||
</p>
|
||||
<p className='font-medium'>
|
||||
{customerData.registrationDate}
|
||||
</p>
|
||||
@ -95,11 +100,15 @@ export function CustomerDashboard() {
|
||||
</div>
|
||||
<div>
|
||||
<div className='mb-4 space-y-1'>
|
||||
<p className='text-sm text-gray-500'>{t('customer.infoCard.cardNumberLabel')}</p>
|
||||
<p className='text-sm text-gray-500'>
|
||||
{t('customer.infoCard.cardNumberLabel')}
|
||||
</p>
|
||||
<p className='font-medium'>{customerData.cardNumber}</p>
|
||||
</div>
|
||||
<div className='mb-4 space-y-1'>
|
||||
<p className='text-sm text-gray-500'>{t('customer.infoCard.expiryDateLabel')}</p>
|
||||
<p className='text-sm text-gray-500'>
|
||||
{t('customer.infoCard.expiryDateLabel')}
|
||||
</p>
|
||||
<p className='font-medium'>{customerData.expiryDate}</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@ -1,20 +1,12 @@
|
||||
'use client';
|
||||
|
||||
import {
|
||||
Award,
|
||||
Fuel,
|
||||
History,
|
||||
MapPin,
|
||||
Star,
|
||||
Target,
|
||||
Users,
|
||||
} from 'lucide-react';
|
||||
import { Fuel, History, MapPin, Star, Target, Users } from 'lucide-react';
|
||||
import Image from 'next/image';
|
||||
|
||||
// import { useTranslation } from 'next-i18next';
|
||||
|
||||
import AnimatedCounter from '@/shared/components/animated-counter';
|
||||
import { useLanguage } from '@/shared/language';
|
||||
import { useTextController } from '@/shared/language/hooks/use-text-controller';
|
||||
import { Button } from '@/shared/shadcn-ui/button';
|
||||
import { Card, CardContent } from '@/shared/shadcn-ui/card';
|
||||
|
||||
@ -28,7 +20,7 @@ export const metadata = {
|
||||
};
|
||||
|
||||
export default function AboutPage() {
|
||||
const { t } = useLanguage();
|
||||
const { t } = useTextController();
|
||||
|
||||
return (
|
||||
<div className='flex min-h-screen flex-col'>
|
||||
|
||||
@ -1,4 +1,4 @@
|
||||
"use client"
|
||||
'use client';
|
||||
|
||||
import {
|
||||
Calendar,
|
||||
@ -10,6 +10,7 @@ import {
|
||||
} from 'lucide-react';
|
||||
import Image from 'next/image';
|
||||
|
||||
import { useTextController } from '@/shared/language/hooks/use-text-controller';
|
||||
import { Button } from '@/shared/shadcn-ui/button';
|
||||
import {
|
||||
Card,
|
||||
@ -20,7 +21,6 @@ import {
|
||||
} from '@/shared/shadcn-ui/card';
|
||||
|
||||
import { CtaSection } from '@/widgets/cta-section';
|
||||
import { useLanguage } from '@/shared/language';
|
||||
|
||||
export const metadata = {
|
||||
title: 'Благотворительность | GasNetwork - Сеть заправок в Таджикистане',
|
||||
@ -29,86 +29,85 @@ export const metadata = {
|
||||
};
|
||||
|
||||
export function CharityPage() {
|
||||
|
||||
const {t} = useLanguage();
|
||||
const { t } = useTextController();
|
||||
|
||||
return (
|
||||
<div className='flex min-h-screen flex-col'>
|
||||
<main className='flex-1'>
|
||||
{/* Hero Section */}
|
||||
<section className='relative'>
|
||||
<div className='relative h-[400px] w-full overflow-hidden'>
|
||||
<Image
|
||||
src='/placeholder.svg?height=500&width=1920&text=Благотворительный+фонд+GasNetwork'
|
||||
alt={t('charity.hero.imageAlt')}
|
||||
width={1920}
|
||||
height={500}
|
||||
className='object-cover'
|
||||
priority
|
||||
/>
|
||||
<div className='absolute inset-0 flex items-center bg-gradient-to-r from-black/70 to-black/30'>
|
||||
<div className='container mx-auto'>
|
||||
<div className='max-w-2xl space-y-6 text-white'>
|
||||
<div className='inline-flex items-center justify-center rounded-full bg-red-600/20 p-2'>
|
||||
<Heart className='size-6 text-red-500' />
|
||||
</div>
|
||||
<h1 className='text-4xl font-bold tracking-tight sm:text-5xl md:text-6xl'>
|
||||
{t('charity.hero.title')}
|
||||
</h1>
|
||||
<p className='text-xl text-gray-200'>
|
||||
{t('charity.hero.subtitle')}
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
<div className='relative h-[400px] w-full overflow-hidden'>
|
||||
<Image
|
||||
src='/placeholder.svg?height=500&width=1920&text=Благотворительный+фонд+GasNetwork'
|
||||
alt={t('charity.hero.imageAlt')}
|
||||
width={1920}
|
||||
height={500}
|
||||
className='object-cover'
|
||||
priority
|
||||
/>
|
||||
<div className='absolute inset-0 flex items-center bg-gradient-to-r from-black/70 to-black/30'>
|
||||
<div className='container mx-auto'>
|
||||
<div className='max-w-2xl space-y-6 text-white'>
|
||||
<div className='inline-flex items-center justify-center rounded-full bg-red-600/20 p-2'>
|
||||
<Heart className='size-6 text-red-500' />
|
||||
</div>
|
||||
<h1 className='text-4xl font-bold tracking-tight sm:text-5xl md:text-6xl'>
|
||||
{t('charity.hero.title')}
|
||||
</h1>
|
||||
<p className='text-xl text-gray-200'>
|
||||
{t('charity.hero.subtitle')}
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
{/* Mission Section */}
|
||||
<section className='py-16'>
|
||||
<div className='container mx-auto'>
|
||||
<div className='grid items-center gap-12 md:grid-cols-2'>
|
||||
<div>
|
||||
<div className='mb-4 inline-flex items-center justify-center rounded-full bg-red-100 p-2'>
|
||||
<Heart className='h-6 w-6 text-red-600' />
|
||||
</div>
|
||||
<h2 className='mb-6 text-3xl font-bold tracking-tight sm:text-4xl'>
|
||||
{t('charity.mission.title')}
|
||||
</h2>
|
||||
<p className='mb-6 text-gray-600'>
|
||||
{t('charity.mission.description1')}
|
||||
</p>
|
||||
<p className='mb-6 text-gray-600'>
|
||||
{t('charity.mission.description2')}
|
||||
</p>
|
||||
|
||||
<div className='space-y-4'>
|
||||
{[0, 1, 2].map((index) => (
|
||||
<div key={index} className='flex items-start'>
|
||||
<CheckCircle className='mr-3 h-6 w-6 flex-shrink-0 text-red-600' />
|
||||
<div>
|
||||
<h3 className='text-lg font-medium'>
|
||||
{t(`charity.mission.principles.${index}.title`)}
|
||||
</h3>
|
||||
<p className='text-gray-600'>
|
||||
{t(`charity.mission.principles.${index}.description`)}
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
<div className='relative h-[500px] overflow-hidden rounded-xl shadow-xl'>
|
||||
<Image
|
||||
src='/placeholder.svg?height=500&width=600&text=Наша+миссия'
|
||||
alt={t('charity.mission.imageAlt')}
|
||||
fill
|
||||
className='object-cover'
|
||||
/>
|
||||
</div>
|
||||
<div className='container mx-auto'>
|
||||
<div className='grid items-center gap-12 md:grid-cols-2'>
|
||||
<div>
|
||||
<div className='mb-4 inline-flex items-center justify-center rounded-full bg-red-100 p-2'>
|
||||
<Heart className='h-6 w-6 text-red-600' />
|
||||
</div>
|
||||
<h2 className='mb-6 text-3xl font-bold tracking-tight sm:text-4xl'>
|
||||
{t('charity.mission.title')}
|
||||
</h2>
|
||||
<p className='mb-6 text-gray-600'>
|
||||
{t('charity.mission.description1')}
|
||||
</p>
|
||||
<p className='mb-6 text-gray-600'>
|
||||
{t('charity.mission.description2')}
|
||||
</p>
|
||||
|
||||
<div className='space-y-4'>
|
||||
{[0, 1, 2].map((index) => (
|
||||
<div key={index} className='flex items-start'>
|
||||
<CheckCircle className='mr-3 h-6 w-6 flex-shrink-0 text-red-600' />
|
||||
<div>
|
||||
<h3 className='text-lg font-medium'>
|
||||
{t(`charity.mission.principles.${index}.title`)}
|
||||
</h3>
|
||||
<p className='text-gray-600'>
|
||||
{t(`charity.mission.principles.${index}.description`)}
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
<div className='relative h-[500px] overflow-hidden rounded-xl shadow-xl'>
|
||||
<Image
|
||||
src='/placeholder.svg?height=500&width=600&text=Наша+миссия'
|
||||
alt={t('charity.mission.imageAlt')}
|
||||
fill
|
||||
className='object-cover'
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
{/* Key Figures */}
|
||||
<section className='bg-red-600 py-16 text-white'>
|
||||
@ -140,16 +139,16 @@ export function CharityPage() {
|
||||
<section className='bg-gray-50 py-16'>
|
||||
<div className='container mx-auto'>
|
||||
<div className='mb-12 text-center'>
|
||||
<div className='mb-4 inline-flex items-center justify-center rounded-full bg-red-100 p-2'>
|
||||
<Calendar className='h-6 w-6 text-red-600' />
|
||||
</div>
|
||||
<h2 className='mb-4 text-3xl font-bold tracking-tight sm:text-4xl'>
|
||||
{t('charity.events.title')}
|
||||
</h2>
|
||||
<p className='mx-auto max-w-2xl text-gray-600'>
|
||||
{t('charity.events.subtitle')}
|
||||
</p>
|
||||
</div>
|
||||
<div className='mb-4 inline-flex items-center justify-center rounded-full bg-red-100 p-2'>
|
||||
<Calendar className='h-6 w-6 text-red-600' />
|
||||
</div>
|
||||
<h2 className='mb-4 text-3xl font-bold tracking-tight sm:text-4xl'>
|
||||
{t('charity.events.title')}
|
||||
</h2>
|
||||
<p className='mx-auto max-w-2xl text-gray-600'>
|
||||
{t('charity.events.subtitle')}
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<div className='grid gap-6 md:grid-cols-3'>
|
||||
{[
|
||||
@ -205,7 +204,7 @@ export function CharityPage() {
|
||||
</CardContent>
|
||||
<CardFooter>
|
||||
<Button className='w-full bg-red-600 hover:bg-red-700'>
|
||||
{t(`charity.events.button`)}
|
||||
{t(`charity.events.button`)}
|
||||
</Button>
|
||||
</CardFooter>
|
||||
</Card>
|
||||
@ -218,15 +217,15 @@ export function CharityPage() {
|
||||
<section className='py-16'>
|
||||
<div className='container mx-auto'>
|
||||
<div className='mb-12 text-center'>
|
||||
<div className='mb-4 inline-flex items-center justify-center rounded-full bg-red-100 p-2'>
|
||||
<Users className='h-6 w-6 text-red-600' />
|
||||
</div>
|
||||
<h2 className='mb-4 text-3xl font-bold tracking-tight sm:text-4xl'>
|
||||
{t('charity.help.title')}
|
||||
</h2>
|
||||
<p className='mx-auto max-w-2xl text-gray-600'>
|
||||
{t('charity.help.subtitle')}
|
||||
</p>
|
||||
<div className='mb-4 inline-flex items-center justify-center rounded-full bg-red-100 p-2'>
|
||||
<Users className='h-6 w-6 text-red-600' />
|
||||
</div>
|
||||
<h2 className='mb-4 text-3xl font-bold tracking-tight sm:text-4xl'>
|
||||
{t('charity.help.title')}
|
||||
</h2>
|
||||
<p className='mx-auto max-w-2xl text-gray-600'>
|
||||
{t('charity.help.subtitle')}
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<div className='grid gap-3 md:grid-cols-2 md:gap-6 lg:grid-cols-4'>
|
||||
|
||||
@ -3,12 +3,12 @@
|
||||
import { Download, Eye } from 'lucide-react';
|
||||
import Image from 'next/image';
|
||||
|
||||
import { useLanguage } from '@/shared/language';
|
||||
import { useTextController } from '@/shared/language/hooks/use-text-controller';
|
||||
import { Button } from '@/shared/shadcn-ui/button';
|
||||
import { Card, CardContent } from '@/shared/shadcn-ui/card';
|
||||
|
||||
export function CertificatesPage() {
|
||||
const { t } = useLanguage();
|
||||
const { t } = useTextController();
|
||||
|
||||
// This data would typically come from an API or CMS
|
||||
// We're keeping it as-is since it's dynamic content
|
||||
|
||||
@ -1,11 +1,12 @@
|
||||
"use client"
|
||||
'use client';
|
||||
|
||||
import Image from 'next/image';
|
||||
|
||||
import { useTextController } from '@/shared/language/hooks/use-text-controller';
|
||||
|
||||
import { BenefitsSection } from '@/widgets/clients/ui/benefits-section';
|
||||
import { ServicesOverviewSection } from '@/widgets/clients/ui/services-overview-section';
|
||||
import { CtaSection } from '@/widgets/cta-section';
|
||||
import { useLanguage } from '@/shared/language';
|
||||
|
||||
export const metadata = {
|
||||
title: 'Клиентам | GasNetwork - Сеть заправок в Таджикистане',
|
||||
@ -14,8 +15,7 @@ export const metadata = {
|
||||
};
|
||||
|
||||
export function ClientsPage() {
|
||||
|
||||
const { t } = useLanguage()
|
||||
const { t } = useTextController();
|
||||
|
||||
return (
|
||||
<div className='flex min-h-screen flex-col'>
|
||||
|
||||
@ -3,7 +3,7 @@
|
||||
import { Check, Percent } from 'lucide-react';
|
||||
import Image from 'next/image';
|
||||
|
||||
import { useLanguage } from '@/shared/language';
|
||||
import { useTextController } from '@/shared/language/hooks/use-text-controller';
|
||||
import { Card, CardContent } from '@/shared/shadcn-ui/card';
|
||||
|
||||
import { CtaSection } from '@/widgets/cta-section';
|
||||
@ -15,7 +15,7 @@ export const metadata = {
|
||||
};
|
||||
|
||||
export function LoyaltyPage() {
|
||||
const { t } = useLanguage();
|
||||
const { t } = useTextController();
|
||||
|
||||
return (
|
||||
<div className='flex min-h-screen flex-col'>
|
||||
|
||||
@ -5,7 +5,7 @@ import Link from 'next/link';
|
||||
|
||||
import { LoginForm } from '@/features/auth/login-form';
|
||||
|
||||
import { useLanguage } from '@/shared/language';
|
||||
import { useTextController } from '@/shared/language/hooks/use-text-controller';
|
||||
import {
|
||||
Card,
|
||||
CardContent,
|
||||
@ -38,7 +38,7 @@ const tabs = [
|
||||
];
|
||||
|
||||
export default function LoginPage() {
|
||||
const { t } = useLanguage();
|
||||
const { t } = useTextController();
|
||||
|
||||
return (
|
||||
<div className='flex min-h-screen flex-col items-center justify-center'>
|
||||
|
||||
@ -3,14 +3,15 @@
|
||||
import { Users } from 'lucide-react';
|
||||
import { useEffect, useRef, useState } from 'react';
|
||||
|
||||
import { useTextController } from '@/shared/language/hooks/use-text-controller';
|
||||
|
||||
import AnimatedCounter from './animated-counter';
|
||||
import { useLanguage } from '../language';
|
||||
|
||||
export default function AboutCounter() {
|
||||
const [isVisible, setIsVisible] = useState(false);
|
||||
const sectionRef = useRef<HTMLDivElement>(null);
|
||||
|
||||
const {t} = useLanguage()
|
||||
const { t } = useTextController();
|
||||
|
||||
useEffect(() => {
|
||||
const observer = new IntersectionObserver(
|
||||
@ -44,7 +45,7 @@ export default function AboutCounter() {
|
||||
<h3 className='text-2xl font-bold text-gray-900'>
|
||||
{isVisible ? <AnimatedCounter end={150} suffix='+' /> : '0+'}
|
||||
</h3>
|
||||
<p className='text-gray-600'>{t("about.stats.items.2.label")}</p>
|
||||
<p className='text-gray-600'>{t('about.stats.items.2.label')}</p>
|
||||
</div>
|
||||
<div className='transform rounded-lg bg-white p-3 shadow-md transition-transform hover:scale-105 sm:p-6'>
|
||||
<div className='mb-4 inline-flex items-center justify-center rounded-full bg-red-100 p-2'>
|
||||
@ -53,7 +54,7 @@ export default function AboutCounter() {
|
||||
<h3 className='text-2xl font-bold text-gray-900'>
|
||||
{isVisible ? <AnimatedCounter end={5} suffix='M+' /> : '0M+'}
|
||||
</h3>
|
||||
<p className='text-gray-600'>{t("about.stats.items.4.label")}</p>
|
||||
<p className='text-gray-600'>{t('about.stats.items.4.label')}</p>
|
||||
</div>
|
||||
<div className='transform rounded-lg bg-white p-3 shadow-md transition-transform hover:scale-105 sm:p-6'>
|
||||
<div className='mb-4 inline-flex items-center justify-center rounded-full bg-red-100 p-2'>
|
||||
@ -66,7 +67,7 @@ export default function AboutCounter() {
|
||||
'0%'
|
||||
)}
|
||||
</h3>
|
||||
<p className='text-gray-600'>{t("about.stats.items.5.label")}</p>
|
||||
<p className='text-gray-600'>{t('about.stats.items.5.label')}</p>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
|
||||
@ -1,13 +1,13 @@
|
||||
'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 { useTextController } from '@/shared/language/hooks/use-text-controller';
|
||||
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 = [
|
||||
{
|
||||
@ -45,7 +45,7 @@ export default function PromotionSlider() {
|
||||
const [currentIndex, setCurrentIndex] = useState(0);
|
||||
const [visibleItems, setVisibleItems] = useState(3);
|
||||
|
||||
const { t } = useLanguage()
|
||||
const { t } = useTextController();
|
||||
|
||||
useEffect(() => {
|
||||
const handleResize = () => {
|
||||
@ -110,15 +110,15 @@ export default function PromotionSlider() {
|
||||
<span className='text-xs text-gray-500'>
|
||||
Действует до: {promo.validUntil}
|
||||
</span>
|
||||
<Link href='#'>
|
||||
<Button
|
||||
<Link href='#'>
|
||||
<Button
|
||||
variant='outline'
|
||||
size='sm'
|
||||
className='border-red-600 text-red-600 hover:bg-red-50'
|
||||
>
|
||||
{t('common.buttons.readMore')}
|
||||
</Button>
|
||||
</Link>
|
||||
</Link>
|
||||
</div>
|
||||
</CardContent>
|
||||
</Card>
|
||||
|
||||
@ -2,10 +2,9 @@
|
||||
|
||||
import { Loader2Icon } from 'lucide-react';
|
||||
|
||||
import { useTextController } from '@/shared/language/hooks/use-text-controller';
|
||||
import { Button, type ButtonProps } from '@/shared/shadcn-ui/button';
|
||||
|
||||
import { useLanguage } from '../language';
|
||||
|
||||
interface SubmitButtonProps extends ButtonProps {
|
||||
title?: string;
|
||||
isLoading: boolean;
|
||||
@ -21,7 +20,7 @@ export const SubmitButton = ({
|
||||
onClick,
|
||||
...props
|
||||
}: SubmitButtonProps) => {
|
||||
const { t } = useLanguage();
|
||||
const { t } = useTextController();
|
||||
|
||||
return (
|
||||
<Button
|
||||
|
||||
12
src/shared/language/api/text-control.api.ts
Normal file
12
src/shared/language/api/text-control.api.ts
Normal file
@ -0,0 +1,12 @@
|
||||
import { baseAPI } from '@/shared/api/base-api';
|
||||
import { TextItem } from '@/shared/types/text.types';
|
||||
|
||||
export const textControlApi = baseAPI.injectEndpoints({
|
||||
endpoints: (builder) => ({
|
||||
fetchText: builder.query<TextItem[], void>({
|
||||
query: () => '/text',
|
||||
}),
|
||||
}),
|
||||
});
|
||||
|
||||
export const { useFetchTextQuery } = textControlApi;
|
||||
@ -1,73 +0,0 @@
|
||||
'use client';
|
||||
|
||||
import { createContext, type ReactNode, useEffect, useState } from 'react';
|
||||
|
||||
import enTranslations from '../locales/en.json';
|
||||
import ruTranslations from '../locales/ru.json';
|
||||
|
||||
// Define available languages
|
||||
export const languages = {
|
||||
en: { name: 'English', flag: '🇬🇧' },
|
||||
ru: { name: 'Русский', flag: '🇷🇺' },
|
||||
};
|
||||
|
||||
export type Language = keyof typeof languages;
|
||||
|
||||
// Define translations type with flat structure
|
||||
export type Translations = Record<string, string>;
|
||||
|
||||
// Load translations
|
||||
const translations: Record<Language, Translations> = {
|
||||
en: enTranslations,
|
||||
ru: ruTranslations,
|
||||
};
|
||||
|
||||
// Create context
|
||||
type LanguageContextType = {
|
||||
language: Language;
|
||||
setLanguage: (lang: Language) => void;
|
||||
t: (key: string) => string;
|
||||
};
|
||||
|
||||
export const LanguageContext = createContext<LanguageContextType | undefined>(
|
||||
undefined,
|
||||
);
|
||||
|
||||
// Create provider
|
||||
export function LanguageProvider({ children }: { children: ReactNode }) {
|
||||
// Default to Russian, but check localStorage on client
|
||||
const [language, setLanguageState] = useState<Language>('ru');
|
||||
|
||||
useEffect(() => {
|
||||
// Check if we're in the browser
|
||||
if (typeof window !== 'undefined') {
|
||||
const savedLanguage = localStorage.getItem('language') as Language;
|
||||
if (savedLanguage && languages[savedLanguage]) {
|
||||
setLanguageState(savedLanguage);
|
||||
}
|
||||
}
|
||||
}, []);
|
||||
|
||||
const setLanguage = (lang: Language) => {
|
||||
setLanguageState(lang);
|
||||
if (typeof window !== 'undefined') {
|
||||
localStorage.setItem('language', lang);
|
||||
}
|
||||
};
|
||||
|
||||
// Translation function for flat structure
|
||||
const t = (key: string): string => {
|
||||
if (translations[language][key]) {
|
||||
return translations[language][key];
|
||||
}
|
||||
|
||||
console.warn(`Translation key not found: ${key}`);
|
||||
return key;
|
||||
};
|
||||
|
||||
return (
|
||||
<LanguageContext.Provider value={{ language, setLanguage, t }}>
|
||||
{children}
|
||||
</LanguageContext.Provider>
|
||||
);
|
||||
}
|
||||
49
src/shared/language/context/text-control-provider.tsx
Normal file
49
src/shared/language/context/text-control-provider.tsx
Normal file
@ -0,0 +1,49 @@
|
||||
import { createContext, type ReactNode } from 'react';
|
||||
|
||||
import { TextItem } from '@/shared/types/text.types';
|
||||
|
||||
export type Translations = Record<string, string>;
|
||||
|
||||
// Create context
|
||||
type TextControlContextType = {
|
||||
t: (key: string) => string;
|
||||
};
|
||||
|
||||
export const TextControlContext = createContext<
|
||||
TextControlContextType | undefined
|
||||
>(undefined);
|
||||
|
||||
// Create provider
|
||||
|
||||
export function TextControlProvider({
|
||||
children,
|
||||
textItems,
|
||||
}: {
|
||||
children: ReactNode;
|
||||
textItems: TextItem[];
|
||||
}) {
|
||||
const textMap = textItems.reduce(
|
||||
(pr, cr) => {
|
||||
pr[cr.key] = cr.value;
|
||||
|
||||
return pr;
|
||||
},
|
||||
{} as Record<string, string | null>,
|
||||
);
|
||||
|
||||
// Translation function for flat structure
|
||||
const t = (key: string): string => {
|
||||
if (textMap[key]) {
|
||||
return textMap[key];
|
||||
}
|
||||
|
||||
console.warn(`Translation key not found: ${key}`);
|
||||
return key;
|
||||
};
|
||||
|
||||
return (
|
||||
<TextControlContext.Provider value={{ t }}>
|
||||
{children}
|
||||
</TextControlContext.Provider>
|
||||
);
|
||||
}
|
||||
@ -2,10 +2,10 @@
|
||||
|
||||
import { useContext } from 'react';
|
||||
|
||||
import { LanguageContext } from '../context/language-provider';
|
||||
import { TextControlContext } from '../context/text-control-provider';
|
||||
|
||||
export function useLanguage() {
|
||||
const context = useContext(LanguageContext);
|
||||
export function useTextController() {
|
||||
const context = useContext(TextControlContext);
|
||||
if (context === undefined) {
|
||||
throw new Error('useLanguage must be used within a LanguageProvider');
|
||||
}
|
||||
@ -1,6 +1,2 @@
|
||||
export {
|
||||
LanguageProvider,
|
||||
languages,
|
||||
type Language,
|
||||
} from './context/language-provider';
|
||||
export { useLanguage } from './hooks/use-language';
|
||||
export { TextControlProvider } from './context/text-control-provider';
|
||||
export { useTextController } from './hooks/use-text-controller';
|
||||
|
||||
@ -1,108 +0,0 @@
|
||||
{
|
||||
"common.buttons.readMore": "Read More",
|
||||
"common.buttons.findStation": "Find Station",
|
||||
"common.buttons.learnMore": "Learn More",
|
||||
"common.buttons.download": "Download",
|
||||
"common.buttons.view": "View",
|
||||
"common.buttons.contactUs": "Contact Us",
|
||||
"common.buttons.apply": "Apply",
|
||||
"common.buttons.login": "Login",
|
||||
"common.buttons.logout": "Logout",
|
||||
"common.buttons.submit": "Submit",
|
||||
"common.buttons.filter": "Filter",
|
||||
"common.buttons.resetFilters": "Reset Filters",
|
||||
"common.buttons.downloadApp": "Download App",
|
||||
"common.buttons.purchaseCardAtGasStations": "Purchase a card at gas stations",
|
||||
"common.buttons.sendResume": "Send Resume",
|
||||
"common.buttons.showAllStations": "Show All Stations",
|
||||
"common.buttons.allPromotions": "All Promotions",
|
||||
|
||||
"common.navigation.home": "Home",
|
||||
"common.navigation.about": "About Us",
|
||||
"common.navigation.clients": "For Clients",
|
||||
"common.navigation.stations": "Our Stations",
|
||||
"common.navigation.vacancies": "Vacancies",
|
||||
"common.navigation.promotions": "Promotions",
|
||||
"common.navigation.charity": "Charity",
|
||||
"common.navigation.certificates": "Certificates",
|
||||
"common.navigation.contacts": "Contacts",
|
||||
|
||||
"common.footer.contacts": "Contacts",
|
||||
"common.footer.navigation": "Navigation",
|
||||
"common.footer.yourEmail": "Your email",
|
||||
"common.footer.rights": "All rights reserved.",
|
||||
|
||||
"home.hero.title": "Modern Gas Station Network in Tajikistan",
|
||||
"home.hero.description": "Quality fuel, convenient locations, and excellent service for our customers",
|
||||
|
||||
"home.about.title": "About Our Company",
|
||||
"home.about.description1": "Our gas station network is one of the leading in Tajikistan. We provide high-quality fuel and a high level of service for our customers for more than 15 years.",
|
||||
"home.about.description2": "We are constantly developing, opening new stations and improving service at existing ones. Our goal is to make refueling as convenient and fast as possible for every customer.",
|
||||
"home.about.features.quality.title": "Quality Fuel",
|
||||
"home.about.features.quality.description": "We guarantee the high quality of our fuel",
|
||||
"home.about.features.equipment.title": "Modern Equipment",
|
||||
"home.about.features.equipment.description": "All our stations are equipped with modern equipment",
|
||||
"home.about.features.staff.title": "Professional Staff",
|
||||
"home.about.features.staff.description": "Our employees are professionals in their field",
|
||||
|
||||
"home.stations.title": "Our Stations",
|
||||
"home.stations.description": "Find the nearest station in our network. We are located in convenient places throughout Tajikistan.",
|
||||
|
||||
"home.promotions.title": "Current Promotions",
|
||||
"home.promotions.description": "Special offers and promotions for our customers. Refuel profitably!",
|
||||
|
||||
"home.vacancies.title": "Vacancies",
|
||||
"home.vacancies.description": "Join our team of professionals. We offer stable work and opportunities for growth.",
|
||||
"home.vacancies.all": "All vacancies",
|
||||
"home.vacancies.office": "Office",
|
||||
"home.vacancies.stations": "Stations",
|
||||
"home.vacancies.fullTime": "Full time",
|
||||
"home.vacancies.experience": "Experience from 1 year",
|
||||
"home.vacancies.shiftWork": "Shift work",
|
||||
"home.vacancies.training": "Training",
|
||||
|
||||
"home.partners.title": "Our Partners",
|
||||
"home.partners.description": "We cooperate with leading companies to provide the best services to our customers.",
|
||||
"home.partners.becomePartner": "Become Our Partner",
|
||||
"home.partners.becomePartnerText": "We are open to cooperation and new partnerships. Contact us to discuss opportunities.",
|
||||
|
||||
"home.charity.title": "Charity Foundation",
|
||||
"home.charity.description": "Our charity foundation was created to support socially significant projects in Tajikistan. We strive to contribute to the development of society and help those in need.",
|
||||
"home.charity.directions": "Main directions of our foundation's activities:",
|
||||
"home.charity.education": "Support for educational programs",
|
||||
"home.charity.children": "Help for children from low-income families",
|
||||
"home.charity.ecology": "Environmental initiatives",
|
||||
"home.charity.sports": "Support for sports events",
|
||||
"home.charity.learnMore": "Learn More About the Foundation",
|
||||
|
||||
"home.cta.title": "Join Us",
|
||||
"home.cta.description": "Become part of our network. Get special offers, bonuses, and discounts.",
|
||||
|
||||
"certificates.title": "Our Certificates",
|
||||
"certificates.description": "GasNetwork adheres to high standards of quality and safety. Our certificates confirm the compliance of our products and services with international and national standards.",
|
||||
|
||||
"certificates.issueDate": "Issue Date",
|
||||
"certificates.expiryDate": "Valid Until",
|
||||
"certificates.faq": "Frequently Asked Questions",
|
||||
|
||||
"auth.title": "Login to your account",
|
||||
"auth.description": "Log in to your personal account to access information about your bonuses, transaction history and other features.",
|
||||
"auth.bonusClient": "Bonus Client",
|
||||
"auth.corporateClient": "Corporate Client",
|
||||
"auth.bonusLogin.title": "Login for bonus clients",
|
||||
"auth.bonusLogin.description": "Enter your phone number and bonus card number to log in to your personal account.",
|
||||
"auth.corporateLogin.title": "Login for corporate clients",
|
||||
"auth.corporateLogin.description": "Enter your phone number and corporate card number to log in to your personal account.",
|
||||
"auth.phoneNumber": "Phone number",
|
||||
"auth.cardNumber": "Card number",
|
||||
"auth.loginIssues": "Having trouble logging in?",
|
||||
|
||||
"map.filters": "Filters",
|
||||
"map.stationsList": "Stations List",
|
||||
"map.noStations": "No stations matching the selected filters",
|
||||
"map.ourStations": "Our Stations",
|
||||
"map.totalStations": "Total stations",
|
||||
"map.services": "Services",
|
||||
"map.cities": "Cities",
|
||||
"map.allCities": "All Cities"
|
||||
}
|
||||
@ -1,344 +0,0 @@
|
||||
{
|
||||
"common.contacts.address": "ул. Рудаки 137, Душанбе, Таджикистан",
|
||||
"common.contacts.tel": "+992 (37) 223-45-67",
|
||||
"common.contacts.email": "info@gasnetwork.tj",
|
||||
"common.name": "Ориё",
|
||||
|
||||
"common.buttons.readMore": "Подробнее",
|
||||
"common.buttons.findStation": "Найти заправку",
|
||||
"common.buttons.learnMore": "Узнать больше",
|
||||
"common.buttons.download": "Скачать",
|
||||
"common.buttons.view": "Просмотр",
|
||||
"common.buttons.contactUs": "Связаться с нами",
|
||||
"common.buttons.apply": "Подать заявку",
|
||||
"common.buttons.login": "Вход",
|
||||
"common.buttons.logout": "Выйти",
|
||||
"common.buttons.submit": "Отправить",
|
||||
"common.buttons.filter": "Фильтры",
|
||||
"common.buttons.resetFilters": "Сбросить фильтры",
|
||||
"common.buttons.downloadApp": "Скачать приложение",
|
||||
"common.buttons.purchaseCardAtGasStations": "Приобретайте карту в сети АЗС",
|
||||
"common.buttons.sendResume": "Отправить резюме",
|
||||
"common.buttons.showAllStations": "Показать все заправки",
|
||||
"common.buttons.allPromotions": "Все акции",
|
||||
|
||||
"common.navigation.home": "Главная",
|
||||
"common.navigation.about": "О нас",
|
||||
"common.navigation.clients": "Клиентам",
|
||||
"common.navigation.stations": "Наши заправки",
|
||||
"common.navigation.vacancies": "Вакансии",
|
||||
"common.navigation.promotions": "Акции",
|
||||
"common.navigation.charity": "Благотворительность",
|
||||
"common.navigation.certificates": "Сертификаты",
|
||||
"common.navigation.loyatly": "Программа лояльности",
|
||||
"common.navigation.partners": "Партнеры",
|
||||
"common.navigation.contacts": "Контакты",
|
||||
|
||||
"common.footer.contacts": "Контакты",
|
||||
"common.footer.navigation": "Навигация",
|
||||
"common.footer.yourEmail": "Ваш email",
|
||||
"common.footer.rights": "Все права защищены.",
|
||||
|
||||
"home.hero.title": "Сеть современных заправок в Таджикистане",
|
||||
"home.hero.description": "Качественное топливо, удобное расположение и отличный сервис для наших клиентов",
|
||||
|
||||
"home.stats.stations": "Заправок по стране",
|
||||
"home.stats.daily": "Клиентов ежедневно",
|
||||
"home.stats.years": "Лет на рынке",
|
||||
"home.stats.mode": "Работаем круглосуточно",
|
||||
|
||||
"home.about.title": "О нашей компании",
|
||||
"home.about.description1": "Наша сеть заправок является одной из ведущих в Таджикистане. Мы предоставляем качественное топливо и высокий уровень обслуживания для наших клиентов уже более 15 лет.",
|
||||
"home.about.description2": "Мы постоянно развиваемся, открывая новые станции и улучшая сервис на существующих. Наша цель - сделать заправку автомобиля максимально удобной и быстрой для каждого клиента.",
|
||||
"home.about.features.quality.title": "Качественное топливо",
|
||||
"home.about.features.quality.description": "Мы гарантируем высокое качество нашего топлива",
|
||||
"home.about.features.equipment.title": "Современное оборудование",
|
||||
"home.about.features.equipment.description": "Все наши станции оснащены современным оборудованием",
|
||||
"home.about.features.staff.title": "Профессиональный персонал",
|
||||
"home.about.features.staff.description": "Наши сотрудники - профессионалы своего дела",
|
||||
|
||||
"about.companyTimeline.rollUp.button": "Свернуть",
|
||||
"about.companyTimeline.show.button": "Показать больше",
|
||||
"about.gallery.previous": "Предыдущая",
|
||||
"about.gallery.next": "Следующая",
|
||||
"about.hero.imageAlt": "О нашей компании",
|
||||
"about.hero.title": "О нашей компании",
|
||||
"about.hero.subtitle": "Узнайте больше о нашей истории, ценностях и миссии. Мы стремимся предоставлять лучший сервис и качественное топливо для наших клиентов.",
|
||||
|
||||
"about.overview.title": "Лидер на рынке Таджикистана",
|
||||
"about.overview.description1": "GasNetwork - ведущая сеть автозаправочных станций в Таджикистане, предоставляющая высококачественное топливо и превосходный сервис. Наша компания была основана в 2008 году и с тех пор стала символом надежности и качества в энергетическом секторе страны.",
|
||||
"about.overview.description2": "Мы гордимся тем, что предлагаем нашим клиентам только лучшее топливо, соответствующее международным стандартам качества. Наши заправочные станции оснащены современным оборудованием, которое обеспечивает быстрое и безопасное обслуживание.",
|
||||
"about.overview.description3": "Наша миссия - сделать поездки наших клиентов комфортными и безопасными, предоставляя качественное топливо и отличный сервис по всей стране.",
|
||||
"about.overview.imageAlt": "Главный офис GasNetwork",
|
||||
|
||||
"about.overview.benefits.0.title": "Качество",
|
||||
"about.overview.benefits.0.description": "Топливо высшего стандарта",
|
||||
"about.overview.benefits.1.title": "Сервис",
|
||||
"about.overview.benefits.1.description": "Профессиональное обслуживание",
|
||||
"about.overview.benefits.2.title": "Инновации",
|
||||
"about.overview.benefits.2.description": "Современные технологии",
|
||||
"about.overview.benefits.3.title": "Доступность",
|
||||
"about.overview.benefits.3.description": "Станции по всей стране",
|
||||
|
||||
"about.stats.title": "GasNetwork в цифрах",
|
||||
"about.stats.subtitle": "Наши достижения и рост за годы работы на рынке Таджикистана",
|
||||
|
||||
"about.stats.items.0.value": "25",
|
||||
"about.stats.items.0.suffix": "+",
|
||||
"about.stats.items.0.label": "Заправок по стране",
|
||||
"about.stats.items.1.value": "15",
|
||||
"about.stats.items.1.label": "Лет на рынке",
|
||||
"about.stats.items.2.value": "150",
|
||||
"about.stats.items.2.suffix": "+",
|
||||
"about.stats.items.2.label": "Сотрудников",
|
||||
"about.stats.items.3.value": "1000000",
|
||||
"about.stats.items.3.suffix": "+",
|
||||
"about.stats.items.3.label": "Клиентов в год",
|
||||
"about.stats.items.4.label": "Литров топлива в месяц",
|
||||
"about.stats.items.5.label": "Довольных клиентов",
|
||||
|
||||
"about.history.title": "Наша история",
|
||||
"about.history.subtitle": "История развития нашей компании от небольшой сети до лидера рынка",
|
||||
|
||||
"about.stations.title": "Наши заправочные станции",
|
||||
"about.stations.subtitle": "Современные заправочные станции, оснащенные по последнему слову техники",
|
||||
"about.stations.description": "Наши заправочные станции расположены в стратегически важных точках по всему Таджикистану, обеспечивая удобный доступ для всех наших клиентов.",
|
||||
"about.stations.buttonText": "Найти ближайшую заправку",
|
||||
|
||||
"about.values.title": "Наши ценности",
|
||||
"about.values.subtitle": "Принципы, которыми мы руководствуемся в нашей работе",
|
||||
"about.values.items.0.title": "Качество",
|
||||
"about.values.items.0.description": "Мы предлагаем только высококачественное топливо, соответствующее международным стандартам. Регулярные проверки и контроль качества гарантируют, что наши клиенты получают лучшее.",
|
||||
"about.values.items.1.title": "Клиентоориентированность",
|
||||
"about.values.items.1.description": "Наши клиенты - наш главный приоритет. Мы стремимся предоставить лучший сервис, удобные условия и приятную атмосферу на каждой нашей заправке.",
|
||||
"about.values.items.2.title": "Профессионализм",
|
||||
"about.values.items.2.description": "Наши сотрудники - профессионалы своего дела. Мы постоянно инвестируем в их обучение и развитие, чтобы обеспечить высокий уровень обслуживания.",
|
||||
|
||||
"about.team.title": "Наша команда",
|
||||
"about.team.subtitle": "Познакомьтесь с профессионалами, которые делают GasNetwork лучшей сетью заправок в Таджикистане",
|
||||
"about.team.members.0.name": "Алишер Рахмонов",
|
||||
"about.team.members.0.position": "Генеральный директор",
|
||||
"about.team.members.1.name": "Фарида Каримова",
|
||||
"about.team.members.1.position": "Финансовый директор",
|
||||
"about.team.members.2.name": "Рустам Назаров",
|
||||
"about.team.members.2.position": "Технический директор",
|
||||
"about.team.members.3.name": "Зарина Шарипова",
|
||||
"about.team.members.3.position": "Директор по маркетингу",
|
||||
|
||||
"about.testimonials.title": "Отзывы клиентов",
|
||||
"about.testimonials.subtitle": "Что говорят о нас наши клиенты",
|
||||
"about.testimonials.items.0.name": "Фархад К.",
|
||||
"about.testimonials.items.0.text": "Я всегда заправляюсь только на GasNetwork. Качество топлива на высоте, а обслуживание всегда приветливое и быстрое.",
|
||||
"about.testimonials.items.0.rating": "5",
|
||||
"about.testimonials.items.1.name": "Нигина М.",
|
||||
"about.testimonials.items.1.text": "Очень удобно, что заправки расположены по всему городу. Всегда чисто, есть кафе и магазин. Рекомендую!",
|
||||
"about.testimonials.items.1.rating": "5",
|
||||
"about.testimonials.items.2.name": "Джамшед Р.",
|
||||
"about.testimonials.items.2.text": "Пользуюсь картой лояльности GasNetwork уже 3 года. Накопил много бонусов и получил немало приятных подарков. Отличный сервис!",
|
||||
"about.testimonials.items.2.rating": "4",
|
||||
|
||||
"home.stations.title": "Наши заправки",
|
||||
"home.stations.description": "Найдите ближайшую к вам заправку нашей сети. Мы расположены в удобных местах по всему Таджикистану.",
|
||||
|
||||
"home.promotions.title": "Актуальные акции",
|
||||
"home.promotions.description": "Специальные предложения и акции для наших клиентов. Заправляйтесь выгодно!",
|
||||
|
||||
"home.vacancies.title": "Вакансии",
|
||||
"home.vacancies.description": "Присоединяйтесь к нашей команде профессионалов. Мы предлагаем стабильную работу и возможности для роста.",
|
||||
"home.vacancies.all": "Все вакансии",
|
||||
"home.vacancies.office": "Офис",
|
||||
"home.vacancies.stations": "Заправки",
|
||||
"home.vacancies.fullTime": "Полный день",
|
||||
"home.vacancies.experience": "Опыт от 1 года",
|
||||
"home.vacancies.shiftWork": "Сменный график",
|
||||
"home.vacancies.training": "Обучение",
|
||||
|
||||
"home.partners.title": "Наши партнеры",
|
||||
"home.partners.description": "Мы сотрудничаем с ведущими компаниями для предоставления лучших услуг нашим клиентам.",
|
||||
"home.partners.becomePartner": "Станьте нашим партнером",
|
||||
"home.partners.becomePartnerText": "Мы открыты для сотрудничества и новых партнерских отношений. Свяжитесь с нами для обсуждения возможностей.",
|
||||
|
||||
"home.charity.title": "Благотворительный фонд",
|
||||
"home.charity.description": "Наш благотворительный фонд был создан для поддержки социально значимых проектов в Таджикистане. Мы стремимся внести свой вклад в развитие общества и помочь тем, кто в этом нуждается.",
|
||||
"home.charity.directions": "Основные направления деятельности нашего фонда:",
|
||||
"home.charity.education": "Поддержка образовательных программ",
|
||||
"home.charity.children": "Помощь детям из малообеспеченных семей",
|
||||
"home.charity.ecology": "Экологические инициативы",
|
||||
"home.charity.sports": "Поддержка спортивных мероприятий",
|
||||
"home.charity.learnMore": "Подробнее о фонде",
|
||||
|
||||
"home.cta.title": "Присоединяйтесь к нам",
|
||||
"home.cta.description": "Станьте частью нашей сети. Получайте специальные предложения, бонусы и скидки.",
|
||||
|
||||
"charity.events.button": "Принять участие",
|
||||
"charity.hero.imageAlt": "Благотворительный фонд GasNetwork",
|
||||
"charity.hero.title": "Благотворительный фонд GasNetwork",
|
||||
"charity.hero.subtitle": "Мы верим, что бизнес должен быть социально ответственным. Наш фонд поддерживает образование, здравоохранение и экологические инициативы в Таджикистане.",
|
||||
|
||||
"charity.mission.title": "Наша миссия",
|
||||
"charity.mission.description1": "Благотворительный фонд GasNetwork был создан в 2020 году с целью поддержки социально значимых проектов в Таджикистане. Мы стремимся внести свой вклад в развитие общества и помочь тем, кто в этом нуждается.",
|
||||
"charity.mission.description2": "Наша миссия — создавать возможности для улучшения жизни людей через образование, здравоохранение, экологические инициативы и поддержку уязвимых групп населения.",
|
||||
"charity.mission.imageAlt": "Наша миссия",
|
||||
|
||||
"charity.mission.principles.0.title": "Прозрачность",
|
||||
"charity.mission.principles.0.description": "Мы публикуем ежегодные отчеты о всех наших проектах и расходах, обеспечивая полную прозрачность нашей деятельности.",
|
||||
"charity.mission.principles.1.title": "Эффективность",
|
||||
"charity.mission.principles.1.description": "Мы тщательно выбираем проекты, которые могут принести максимальную пользу обществу и имеют долгосрочное влияние.",
|
||||
"charity.mission.principles.2.title": "Сотрудничество",
|
||||
"charity.mission.principles.2.description": "Мы сотрудничаем с местными и международными организациями для достижения наибольшего эффекта от наших инициатив.",
|
||||
|
||||
"charity.stats.title": "Наш вклад в цифрах",
|
||||
"charity.stats.subtitle": "За время существования нашего фонда мы достигли значительных результатов",
|
||||
"charity.stats.items.0.value": "15+",
|
||||
"charity.stats.items.0.label": "Реализованных проектов",
|
||||
"charity.stats.items.1.value": "1.2M",
|
||||
"charity.stats.items.1.label": "Сомони пожертвований",
|
||||
"charity.stats.items.2.value": "5000+",
|
||||
"charity.stats.items.2.label": "Людей получили помощь",
|
||||
|
||||
"charity.events.title": "Предстоящие мероприятия",
|
||||
"charity.events.subtitle": "Присоединяйтесь к нашим благотворительным мероприятиям и внесите свой вклад в общее дело",
|
||||
|
||||
"charity.help.title": "Как вы можете помочь",
|
||||
"charity.help.subtitle": "Есть много способов внести свой вклад в наши благотворительные инициативы",
|
||||
|
||||
"clients.title": "Для наших клиентов",
|
||||
"clients.description": "Информация для клиентов: программа лояльности, топливные карты, сертификаты и способы оплаты.",
|
||||
"clients.services.title": "Наши услуги для клиентов",
|
||||
"clients.services.subtitle": "Мы стремимся сделать обслуживание на наших заправках максимально удобным и выгодным для вас",
|
||||
"clients.benefits.title": "Преимущества для клиентов",
|
||||
"clients.benefits.subtitle": "Став клиентом GasNetwork, вы получаете множество преимуществ, которые делают заправку вашего автомобиля более выгодной и удобной.",
|
||||
"clients.header.loyalty.description": "Накапливайте баллы и получайте скидки на топливо и услуги",
|
||||
"clients.header.certificates.description": "Подарочные сертификаты на топливо и услуги нашей сети",
|
||||
|
||||
"clients.loyalty.title": "Программа лояльности",
|
||||
"clients.loyalty.description": "Накапливайте баллы и получайте скидки на топливо и услуги нашей сети",
|
||||
|
||||
"clients.loyalty.programm.about": "О программе лояльности",
|
||||
"clients.loyalty.programm.about-description": "Программа лояльности GasNetwork — это возможность получать баллы за каждую покупку топлива и услуг на наших заправочных станциях. Накопленные баллы можно обменять на скидки, подарки или дополнительные услуги.",
|
||||
"clients.loyalty.programm.about-description-2": "Участие в программе абсолютно бесплатное. Вам нужно только получить карту лояльности в любой нашей заправочной станции или зарегистрироваться в мобильном приложении.",
|
||||
"clients.loyalty.programm.conditions-1": "1 литр = 1 балл",
|
||||
"clients.loyalty.programm.conditions.description-1": "За каждый литр топлива вы получаете 1 балл",
|
||||
"clients.loyalty.programm.conditions-2": "Дополнительные баллы",
|
||||
"clients.loyalty.programm.conditions.description-2": "За покупки в магазине и кафе на заправке",
|
||||
"clients.loyalty.programm.conditions-3": "Специальные акции",
|
||||
"clients.loyalty.programm.conditions.description-3": "Удвоенные и утроенные баллы в праздничные дни",
|
||||
|
||||
"clients.loyalty.works.title": "Как это работает",
|
||||
"clients.loyalty.works.description": "Простые шаги для участия в программе лояльности GasNetwork",
|
||||
|
||||
"clients.loyalty.works.stage-1": "Получите карту",
|
||||
"clients.loyalty.works.stage.description-1": "Получите карту лояльности на любой заправке GasNetwork или зарегистрируйтесь в мобильном приложении",
|
||||
"clients.loyalty.works.stage-2": "Заправляйтесь",
|
||||
"clients.loyalty.works.stage.description-2": "Используйте карту при каждой заправке и покупке в магазинах на наших АЗС",
|
||||
"clients.loyalty.works.stage-3": "Накапливайте баллы",
|
||||
"clients.loyalty.works.stage.description-3": "Получайте баллы за каждую покупку и следите за их накоплением в приложении",
|
||||
"clients.loyalty.works.stage-4": "Получайте выгоду",
|
||||
"clients.loyalty.works.stage.description-4": "Обменивайте накопленные баллы на скидки, подарки или дополнительные услуги",
|
||||
|
||||
"clients.loyalty.works.levels.title": "Уровни лояльности",
|
||||
"clients.loyalty.works.levels.description": "Чем больше вы заправляетесь, тем больше преимуществ получаете",
|
||||
"clients.loyalty.works.levels.card.mark": "возврат баллами",
|
||||
|
||||
"clients.loyalty.works.levels.card-1.title": "Стандарт",
|
||||
"clients.loyalty.works.levels.card-1.percent": "1%",
|
||||
"clients.loyalty.works.levels.card-1.bonus-1": "1 балл за каждый литр топлива",
|
||||
"clients.loyalty.works.levels.card-1.bonus-2": "Участие в акциях",
|
||||
"clients.loyalty.works.levels.card-1.bonus-3": "Доступ к мобильному приложению",
|
||||
|
||||
"clients.loyalty.works.levels.card-2.title": "Золотой",
|
||||
"clients.loyalty.works.levels.card-2.percent": "2%",
|
||||
"clients.loyalty.works.levels.card-2.bonus-1": "2 балла за каждый литр топлива",
|
||||
"clients.loyalty.works.levels.card-2.bonus-2": "Скидка 5% в кафе на заправках",
|
||||
"clients.loyalty.works.levels.card-2.bonus-3": "Приоритетное обслуживание",
|
||||
"clients.loyalty.works.levels.card-2.bonus-4": "Эксклюзивные акции",
|
||||
|
||||
"clients.loyalty.works.levels.card-3.title": "Платиновый",
|
||||
"clients.loyalty.works.levels.card-3.percent": "3%",
|
||||
"clients.loyalty.works.levels.card-3.bonus-1": "3 балла за каждый литр топлива",
|
||||
"clients.loyalty.works.levels.card-3.bonus-2": "Скидка 10% в кафе на заправках",
|
||||
"clients.loyalty.works.levels.card-3.bonus-3": "Бесплатная мойка раз в месяц",
|
||||
"clients.loyalty.works.levels.card-3.bonus-4": "Персональный менеджер",
|
||||
"clients.loyalty.works.levels.card-3.bonus-5": "VIP-обслуживание",
|
||||
|
||||
"certificates.title": "Наши сертификаты",
|
||||
"certificates.description": "GasNetwork придерживается высоких стандартов качества и безопасности. Наши сертификаты подтверждают соответствие нашей продукции и услуг международным и национальным стандартам.",
|
||||
"certificates.issueDate": "Дата выдачи",
|
||||
"certificates.expiryDate": "Действителен до",
|
||||
"certificates.faq": "Часто задаваемые вопросы",
|
||||
|
||||
"auth.title": "Вход в личный кабинет",
|
||||
"auth.description": "Войдите в личный кабинет, чтобы получить доступ к информации о ваших бонусах, истории операций и другим возможностям.",
|
||||
"auth.bonusClient": "Бонусный клиент",
|
||||
"auth.corporateClient": "Корпоративный клиент",
|
||||
"auth.bonusLogin.title": "Вход для бонусных клиентов",
|
||||
"auth.bonusLogin.description": "Введите номер телефона и номер бонусной карты для входа в личный кабинет.",
|
||||
"auth.corporateLogin.title": "Вход для корпоративных клиентов",
|
||||
"auth.corporateLogin.description": "Введите номер телефона и номер корпоративной карты для входа в личный кабинет.",
|
||||
"auth.phoneNumber": "Номер телефона",
|
||||
"auth.cardNumber": "Номер карты",
|
||||
"auth.loginIssues": "Возникли проблемы со входом?",
|
||||
"auth.contactLink": "Свяжитесь с нами",
|
||||
|
||||
"map.filters": "Фильтры",
|
||||
"map.stationsList": "Список заправок",
|
||||
"map.noStations": "Нет заправок, соответствующих выбранным фильтрам",
|
||||
"map.ourStations": "Наши заправки",
|
||||
"map.totalStations": "Всего станций",
|
||||
"map.services": "Услуги",
|
||||
"map.cities": "Города",
|
||||
"map.allCities": "Все города",
|
||||
|
||||
"corporate.pageTitle": "Корпоративный кабинет",
|
||||
"corporate.logoutButton": "Выйти",
|
||||
|
||||
"corporate.companyCard.title": "Информация о компании",
|
||||
"corporate.companyCard.companyNameLabel": "Название компании",
|
||||
"corporate.companyCard.cardsCountLabel": "Количество карт",
|
||||
"corporate.companyCard.registrationDateLabel": "Дата регистрации",
|
||||
"corporate.companyCard.fundLabel": "Фонд",
|
||||
"corporate.companyCard.overdraftLabel": "Овердрафт",
|
||||
"corporate.currency": "сомони",
|
||||
|
||||
"corporate.fundCard.title": "Общий фонд",
|
||||
"corporate.fundCard.description": "Доступные средства",
|
||||
"corporate.fundCard.currency": "сомони",
|
||||
|
||||
"corporate.transactions.title": "История операций",
|
||||
"corporate.transactions.dateFrom": "От",
|
||||
"corporate.transactions.dateTo": "До",
|
||||
"corporate.transactions.selectDate": "Выберите дату",
|
||||
"corporate.transactions.applyButton": "Применить",
|
||||
|
||||
"corporate.transactions.tableHeaders.date": "Дата",
|
||||
"corporate.transactions.tableHeaders.station": "Станция",
|
||||
"corporate.transactions.tableHeaders.product": "Продукт",
|
||||
"corporate.transactions.tableHeaders.quantity": "Кол-во (л)",
|
||||
"corporate.transactions.tableHeaders.price": "Стоимость",
|
||||
"corporate.transactions.tableHeaders.total": "Сумма",
|
||||
|
||||
"corporate.transactions.noTransactions": "Нет операций за выбранный период",
|
||||
|
||||
"customer.pageTitle": "Личный кабинет",
|
||||
"customer.logoutButton": "Выйти",
|
||||
|
||||
"customer.bonusCard.title": "Бонусная карта",
|
||||
"customer.bonusCard.description": "Ваши накопленные бонусы",
|
||||
"customer.bonusCard.points": "бонусных баллов",
|
||||
"customer.bonusCard.validUntil": "Действует до: 31.12.2023",
|
||||
|
||||
"customer.infoCard.title": "Информация о клиенте",
|
||||
"customer.infoCard.fullNameLabel": "ФИО",
|
||||
"customer.infoCard.regDateLabel": "Дата регистрации",
|
||||
"customer.infoCard.cardNumberLabel": "Номер карты",
|
||||
"customer.infoCard.expiryDateLabel": "Срок действия",
|
||||
|
||||
"customer.transactions.title": "История операций",
|
||||
"customer.transactions.dateColumn": "Дата",
|
||||
"customer.transactions.stationColumn": "АЗС",
|
||||
"customer.transactions.productColumn": "Топливо",
|
||||
"customer.transactions.quantityColumn": "Литры",
|
||||
"customer.transactions.priceColumn": "Цена",
|
||||
"customer.transactions.totalColumn": "Сумма",
|
||||
"customer.transactions.noData": "Нет данных о транзакциях"
|
||||
}
|
||||
@ -1,44 +0,0 @@
|
||||
'use client';
|
||||
|
||||
import { Check, Globe } from 'lucide-react';
|
||||
import { useState } from 'react';
|
||||
|
||||
import { type Language, languages, useLanguage } from '@/shared/language';
|
||||
import { Button } from '@/shared/shadcn-ui/button';
|
||||
import {
|
||||
DropdownMenu,
|
||||
DropdownMenuContent,
|
||||
DropdownMenuItem,
|
||||
DropdownMenuTrigger,
|
||||
} from '@/shared/shadcn-ui/dropdown-menu';
|
||||
|
||||
export function LanguageSwitcher() {
|
||||
const { language, setLanguage } = useLanguage();
|
||||
const [open, setOpen] = useState(false);
|
||||
|
||||
return (
|
||||
<DropdownMenu open={open} onOpenChange={setOpen}>
|
||||
<DropdownMenuTrigger asChild>
|
||||
<Button variant='ghost' size='sm' className='h-8 w-8 px-0'>
|
||||
<Globe className='h-4 w-4' />
|
||||
<span className='sr-only'>Switch language</span>
|
||||
</Button>
|
||||
</DropdownMenuTrigger>
|
||||
<DropdownMenuContent align='end'>
|
||||
{Object.entries(languages).map(([code, { name, flag }]) => (
|
||||
<DropdownMenuItem
|
||||
key={code}
|
||||
onClick={() => {
|
||||
setLanguage(code as Language);
|
||||
setOpen(false);
|
||||
}}
|
||||
>
|
||||
<span className='mr-2'>{flag}</span>
|
||||
{name}
|
||||
{code === language && <Check className='ml-2 h-4 w-4' />}
|
||||
</DropdownMenuItem>
|
||||
))}
|
||||
</DropdownMenuContent>
|
||||
</DropdownMenu>
|
||||
);
|
||||
}
|
||||
@ -2,20 +2,22 @@
|
||||
|
||||
import { Provider } from 'react-redux';
|
||||
|
||||
import { LanguageProvider } from '../language';
|
||||
import { TextControlProvider } from '../language';
|
||||
import { store } from '../store';
|
||||
import { ThemeProvider } from '../theme/theme-provider';
|
||||
import { TextItem } from '../types/text.types';
|
||||
import { AosProvider } from './aos-provider';
|
||||
import { Toaster } from './toaster';
|
||||
|
||||
type ProvidersProps = {
|
||||
children: React.ReactNode;
|
||||
textItems: TextItem[];
|
||||
};
|
||||
|
||||
export const Providers = ({ children }: ProvidersProps) => {
|
||||
export const Providers = ({ children, textItems }: ProvidersProps) => {
|
||||
return (
|
||||
<Provider store={store}>
|
||||
<LanguageProvider>
|
||||
<TextControlProvider textItems={textItems}>
|
||||
<ThemeProvider
|
||||
attribute='class'
|
||||
defaultTheme='light'
|
||||
@ -27,7 +29,7 @@ export const Providers = ({ children }: ProvidersProps) => {
|
||||
<Toaster />
|
||||
</AosProvider>
|
||||
</ThemeProvider>
|
||||
</LanguageProvider>
|
||||
</TextControlProvider>
|
||||
</Provider>
|
||||
);
|
||||
};
|
||||
|
||||
@ -1,15 +1,20 @@
|
||||
import { configureStore } from '@reduxjs/toolkit';
|
||||
import { setupListeners } from '@reduxjs/toolkit/query/react';
|
||||
import { createWrapper } from 'next-redux-wrapper';
|
||||
|
||||
import { baseAPI } from '@/shared/api/base-api';
|
||||
|
||||
import { rootReducer } from './root-reducer';
|
||||
|
||||
export const store = configureStore({
|
||||
reducer: rootReducer,
|
||||
middleware: (getDefaultMiddleware) =>
|
||||
getDefaultMiddleware().concat(baseAPI.middleware),
|
||||
devTools: process.env.NODE_ENV === 'development',
|
||||
});
|
||||
export const makeStore = () =>
|
||||
configureStore({
|
||||
reducer: rootReducer,
|
||||
middleware: (getDefaultMiddleware) =>
|
||||
getDefaultMiddleware().concat(baseAPI.middleware),
|
||||
devTools: process.env.NODE_ENV === 'development',
|
||||
});
|
||||
|
||||
export const store = makeStore();
|
||||
|
||||
setupListeners(store.dispatch);
|
||||
|
||||
@ -17,3 +22,5 @@ setupListeners(store.dispatch);
|
||||
export type RootState = ReturnType<typeof store.getState>;
|
||||
// Inferred type: {posts: PostsState, comments: CommentsState, users: UsersState}
|
||||
export type AppDispatch = typeof store.dispatch;
|
||||
|
||||
export const wrapper = createWrapper(makeStore);
|
||||
|
||||
4
src/shared/types/text.types.ts
Normal file
4
src/shared/types/text.types.ts
Normal file
@ -0,0 +1,4 @@
|
||||
export interface TextItem {
|
||||
key: string;
|
||||
value: string | null;
|
||||
}
|
||||
@ -3,9 +3,9 @@
|
||||
import { Calendar, ChevronDown, ChevronUp } from 'lucide-react';
|
||||
import { useState } from 'react';
|
||||
|
||||
import { useTextController } from '@/shared/language/hooks/use-text-controller';
|
||||
import { Button } from '@/shared/shadcn-ui/button';
|
||||
import { Card, CardContent } from '@/shared/shadcn-ui/card';
|
||||
import { useLanguage } from '@/shared/language';
|
||||
|
||||
const timelineEvents = [
|
||||
{
|
||||
@ -62,7 +62,7 @@ export function CompanyTimeline() {
|
||||
const [expanded, setExpanded] = useState(false);
|
||||
const displayEvents = expanded ? timelineEvents : timelineEvents.slice(0, 4);
|
||||
|
||||
const {t} = useLanguage()
|
||||
const { t } = useTextController();
|
||||
|
||||
return (
|
||||
<div className='relative'>
|
||||
@ -119,11 +119,13 @@ export function CompanyTimeline() {
|
||||
>
|
||||
{expanded ? (
|
||||
<>
|
||||
{t("about.companyTimeline.rollUp.button")} <ChevronUp className='ml-2 size-4' />
|
||||
{t('about.companyTimeline.rollUp.button')}{' '}
|
||||
<ChevronUp className='ml-2 size-4' />
|
||||
</>
|
||||
) : (
|
||||
<>
|
||||
{t("about.companyTimeline.show.button")} <ChevronDown className='ml-2 size-4' />
|
||||
{t('about.companyTimeline.show.button')}{' '}
|
||||
<ChevronDown className='ml-2 size-4' />
|
||||
</>
|
||||
)}
|
||||
</Button>
|
||||
|
||||
@ -4,6 +4,7 @@ import { ChevronLeft, ChevronRight, Maximize } from 'lucide-react';
|
||||
import Image from 'next/image';
|
||||
import { useState } from 'react';
|
||||
|
||||
import { useTextController } from '@/shared/language/hooks/use-text-controller';
|
||||
import { Button } from '@/shared/shadcn-ui/button';
|
||||
import {
|
||||
Dialog,
|
||||
@ -13,7 +14,6 @@ import {
|
||||
DialogTitle,
|
||||
DialogTrigger,
|
||||
} from '@/shared/shadcn-ui/dialog';
|
||||
import { useLanguage } from '@/shared/language';
|
||||
|
||||
interface Station {
|
||||
id: number;
|
||||
@ -73,7 +73,7 @@ export function StationGallery() {
|
||||
setCurrentImage((prev) => (prev === 0 ? stations.length - 1 : prev - 1));
|
||||
};
|
||||
|
||||
const {t} = useLanguage()
|
||||
const { t } = useTextController();
|
||||
|
||||
return (
|
||||
<div className='space-y-8'>
|
||||
|
||||
@ -4,10 +4,10 @@ import { Users } from 'lucide-react';
|
||||
import Image from 'next/image';
|
||||
|
||||
import AboutCounter from '@/shared/components/about-counter';
|
||||
import { useLanguage } from '@/shared/language';
|
||||
import { useTextController } from '@/shared/language/hooks/use-text-controller';
|
||||
|
||||
export const AboutSection = () => {
|
||||
const { t } = useLanguage();
|
||||
const { t } = useTextController();
|
||||
|
||||
return (
|
||||
<section id='about' className='px-2 py-8 sm:py-16'>
|
||||
@ -64,7 +64,7 @@ const features: Array<Feature> = [
|
||||
];
|
||||
|
||||
const Features = () => {
|
||||
const { t } = useLanguage();
|
||||
const { t } = useTextController();
|
||||
|
||||
return (
|
||||
<div className='space-y-4'>
|
||||
|
||||
@ -4,11 +4,11 @@ import { ChevronRight, Heart } from 'lucide-react';
|
||||
import Image from 'next/image';
|
||||
import Link from 'next/link';
|
||||
|
||||
import { useLanguage } from '@/shared/language';
|
||||
import { useTextController } from '@/shared/language/hooks/use-text-controller';
|
||||
import { Button } from '@/shared/shadcn-ui/button';
|
||||
|
||||
export const CharitySection = () => {
|
||||
const { t } = useLanguage();
|
||||
const { t } = useTextController();
|
||||
|
||||
return (
|
||||
<section id='charity' className='px-2 py-8 sm:py-16'>
|
||||
|
||||
@ -1,9 +1,10 @@
|
||||
"use client"
|
||||
'use client';
|
||||
|
||||
import { useLanguage } from '@/shared/language';
|
||||
import { Percent } from 'lucide-react';
|
||||
import Image from 'next/image';
|
||||
|
||||
import { useTextController } from '@/shared/language/hooks/use-text-controller';
|
||||
|
||||
interface Benefit {
|
||||
title: string;
|
||||
description: string;
|
||||
@ -29,8 +30,7 @@ const benefits: Array<Benefit> = [
|
||||
];
|
||||
|
||||
export const BenefitsSection = () => {
|
||||
|
||||
const {t} = useLanguage()
|
||||
const { t } = useTextController();
|
||||
|
||||
return (
|
||||
<section className='bg-gray-50 py-16'>
|
||||
|
||||
@ -1,8 +1,9 @@
|
||||
"use client"
|
||||
'use client';
|
||||
|
||||
import { CreditCard, type LucideProps, Percent, Wallet } from 'lucide-react';
|
||||
import { type ForwardRefExoticComponent, type RefAttributes } from 'react';
|
||||
|
||||
import { useTextController } from '@/shared/language/hooks/use-text-controller';
|
||||
import {
|
||||
Card,
|
||||
CardContent,
|
||||
@ -10,7 +11,6 @@ import {
|
||||
CardHeader,
|
||||
CardTitle,
|
||||
} from '@/shared/shadcn-ui/card';
|
||||
import { useLanguage } from '@/shared/language';
|
||||
|
||||
interface ServiceOverview {
|
||||
title: string;
|
||||
@ -46,8 +46,7 @@ const servicesOverview: Array<ServiceOverview> = [
|
||||
];
|
||||
|
||||
export const ServicesOverviewSection = () => {
|
||||
|
||||
const {t} = useLanguage()
|
||||
const { t } = useTextController();
|
||||
|
||||
return (
|
||||
<section className='py-16'>
|
||||
|
||||
@ -2,11 +2,11 @@
|
||||
|
||||
import Link from 'next/link';
|
||||
|
||||
import { useLanguage } from '@/shared/language';
|
||||
import { useTextController } from '@/shared/language/hooks/use-text-controller';
|
||||
import { Button } from '@/shared/shadcn-ui/button';
|
||||
|
||||
export const CtaSection = () => {
|
||||
const { t } = useLanguage();
|
||||
const { t } = useTextController();
|
||||
|
||||
return (
|
||||
<section className='bg-red-600 px-2 py-8 text-white sm:py-16'>
|
||||
|
||||
@ -3,10 +3,10 @@
|
||||
import { Fuel, Mail, MapPin, Phone } from 'lucide-react';
|
||||
import Link from 'next/link';
|
||||
|
||||
import { useLanguage } from '@/shared/language';
|
||||
import { useTextController } from '@/shared/language/hooks/use-text-controller';
|
||||
|
||||
export const Footer = () => {
|
||||
const { t } = useLanguage();
|
||||
const { t } = useTextController();
|
||||
|
||||
return (
|
||||
<footer className='bg-gray-900 px-4 py-12 text-white'>
|
||||
@ -122,7 +122,7 @@ export const Footer = () => {
|
||||
</div>
|
||||
<div className='mt-8 border-t border-gray-800 pt-8 text-center text-gray-400'>
|
||||
<p>
|
||||
© {new Date().getFullYear()} {t("common.name")}.{' '}
|
||||
© {new Date().getFullYear()} {t('common.name')}.{' '}
|
||||
{t('common.footer.rights')}
|
||||
</p>
|
||||
</div>
|
||||
|
||||
@ -1,6 +1,7 @@
|
||||
import Link from 'next/link';
|
||||
import React from 'react';
|
||||
|
||||
import { useTextController } from '@/shared/language/hooks/use-text-controller';
|
||||
import { cn } from '@/shared/lib/utils';
|
||||
import {
|
||||
NavigationMenu,
|
||||
@ -11,11 +12,9 @@ import {
|
||||
NavigationMenuTrigger,
|
||||
navigationMenuTriggerStyle,
|
||||
} from '@/shared/shadcn-ui/navigation-menu';
|
||||
import { useLanguage } from '@/shared/language';
|
||||
|
||||
export function DesktopNav() {
|
||||
|
||||
const { t } = useLanguage();
|
||||
const { t } = useTextController();
|
||||
|
||||
return (
|
||||
<NavigationMenu className='hidden lg:flex'>
|
||||
@ -41,7 +40,9 @@ export function DesktopNav() {
|
||||
</Link>
|
||||
</NavigationMenuItem>
|
||||
<NavigationMenuItem>
|
||||
<NavigationMenuTrigger>{t('common.navigation.clients')}</NavigationMenuTrigger>
|
||||
<NavigationMenuTrigger>
|
||||
{t('common.navigation.clients')}
|
||||
</NavigationMenuTrigger>
|
||||
<NavigationMenuContent>
|
||||
<ul className='grid w-[400px] gap-3 p-4 md:w-[500px] md:grid-cols-2 lg:w-[600px]'>
|
||||
<li className='row-span-4'>
|
||||
@ -63,7 +64,7 @@ export function DesktopNav() {
|
||||
{t('clients.header.loyalty.description')}
|
||||
</ListItem>
|
||||
<ListItem href='/clients/certificates' title='Сертификаты'>
|
||||
{t('clients.header.certificates.description')}
|
||||
{t('clients.header.certificates.description')}
|
||||
</ListItem>
|
||||
</ul>
|
||||
</NavigationMenuContent>
|
||||
|
||||
@ -4,7 +4,7 @@ import { UserCircle } from 'lucide-react';
|
||||
import Link from 'next/link';
|
||||
|
||||
import { Logo } from '@/shared/assets/logo';
|
||||
import { useLanguage } from '@/shared/language';
|
||||
import { useTextController } from '@/shared/language/hooks/use-text-controller';
|
||||
// import { LanguageSwitcher } from '@/shared/language/ui/language-switcher';
|
||||
import { Button } from '@/shared/shadcn-ui/button';
|
||||
|
||||
@ -12,7 +12,7 @@ import { DesktopNav } from './desktop-nav';
|
||||
import { MobileNav } from './mobile-nav';
|
||||
|
||||
export function Header() {
|
||||
const { t } = useLanguage();
|
||||
const { t } = useTextController();
|
||||
|
||||
return (
|
||||
<header className='sticky top-0 z-40 w-full border-b bg-white'>
|
||||
|
||||
@ -4,6 +4,7 @@ import { ChevronDown, ChevronRight, Menu } from 'lucide-react';
|
||||
import Link from 'next/link';
|
||||
import { useState } from 'react';
|
||||
|
||||
import { useTextController } from '@/shared/language/hooks/use-text-controller';
|
||||
import { Button } from '@/shared/shadcn-ui/button';
|
||||
import {
|
||||
Collapsible,
|
||||
@ -11,13 +12,12 @@ import {
|
||||
CollapsibleTrigger,
|
||||
} from '@/shared/shadcn-ui/collapsible';
|
||||
import { Sheet, SheetContent, SheetTrigger } from '@/shared/shadcn-ui/sheet';
|
||||
import { useLanguage } from '@/shared/language';
|
||||
|
||||
export function MobileNav() {
|
||||
const [open, setOpen] = useState(false);
|
||||
const [clientsOpen, setClientsOpen] = useState(false);
|
||||
|
||||
const {t} = useLanguage();
|
||||
const { t } = useTextController();
|
||||
|
||||
return (
|
||||
<Sheet open={open} onOpenChange={setOpen}>
|
||||
@ -34,19 +34,19 @@ export function MobileNav() {
|
||||
onClick={() => setOpen(false)}
|
||||
className='text-lg font-medium transition-colors hover:text-red-600'
|
||||
>
|
||||
{t("common.navigation.home")}
|
||||
{t('common.navigation.home')}
|
||||
</Link>
|
||||
<Link
|
||||
href='/about'
|
||||
onClick={() => setOpen(false)}
|
||||
className='text-lg font-medium transition-colors hover:text-red-600'
|
||||
>
|
||||
{t("common.navigation.about")}
|
||||
{t('common.navigation.about')}
|
||||
</Link>
|
||||
|
||||
<Collapsible open={clientsOpen} onOpenChange={setClientsOpen}>
|
||||
<CollapsibleTrigger className='data-[state=open]:animate-collapsible-down data-[state=closed]:animate-collapsible-up flex w-full items-center justify-between text-lg font-medium transition-all hover:text-red-600'>
|
||||
<span>{t("common.navigation.clients")}</span>
|
||||
<span>{t('common.navigation.clients')}</span>
|
||||
{clientsOpen ? (
|
||||
<ChevronDown className='h-5 w-5' />
|
||||
) : (
|
||||
@ -59,14 +59,14 @@ export function MobileNav() {
|
||||
onClick={() => setOpen(false)}
|
||||
className='block py-1 text-base transition-colors hover:text-red-600'
|
||||
>
|
||||
{t("common.navigation.loyatly")}
|
||||
{t('common.navigation.loyatly')}
|
||||
</Link>
|
||||
<Link
|
||||
href='/clients/certificates'
|
||||
onClick={() => setOpen(false)}
|
||||
className='block py-1 text-base transition-colors hover:text-red-600'
|
||||
>
|
||||
{t("common.navigation.certificates")}
|
||||
{t('common.navigation.certificates')}
|
||||
</Link>
|
||||
</CollapsibleContent>
|
||||
</Collapsible>
|
||||
@ -76,35 +76,35 @@ export function MobileNav() {
|
||||
onClick={() => setOpen(false)}
|
||||
className='text-lg font-medium transition-colors hover:text-red-600'
|
||||
>
|
||||
{t("common.navigation.stations")}
|
||||
{t('common.navigation.stations')}
|
||||
</Link>
|
||||
<Link
|
||||
href='/#vacancies'
|
||||
onClick={() => setOpen(false)}
|
||||
className='text-lg font-medium transition-colors hover:text-red-600'
|
||||
>
|
||||
{t("common.navigation.vacancies")}
|
||||
{t('common.navigation.vacancies')}
|
||||
</Link>
|
||||
<Link
|
||||
href='/#promotions'
|
||||
onClick={() => setOpen(false)}
|
||||
className='text-lg font-medium transition-colors hover:text-red-600'
|
||||
>
|
||||
{t("common.navigation.promotions")}
|
||||
{t('common.navigation.promotions')}
|
||||
</Link>
|
||||
<Link
|
||||
href='/#partners'
|
||||
onClick={() => setOpen(false)}
|
||||
className='text-lg font-medium transition-colors hover:text-red-600'
|
||||
>
|
||||
{t("common.navigation.partners")}
|
||||
{t('common.navigation.partners')}
|
||||
</Link>
|
||||
<Link
|
||||
href='/#charity'
|
||||
onClick={() => setOpen(false)}
|
||||
className='text-lg font-medium transition-colors hover:text-red-600'
|
||||
>
|
||||
{t("common.navigation.charity")}
|
||||
{t('common.navigation.charity')}
|
||||
</Link>
|
||||
</nav>
|
||||
</SheetContent>
|
||||
|
||||
@ -4,11 +4,11 @@ import { MapPin } from 'lucide-react';
|
||||
import Image from 'next/image';
|
||||
import Link from 'next/link';
|
||||
|
||||
import { useLanguage } from '@/shared/language';
|
||||
import { useTextController } from '@/shared/language/hooks/use-text-controller';
|
||||
import { Button } from '@/shared/shadcn-ui/button';
|
||||
|
||||
export const HeroSection = () => {
|
||||
const { t } = useLanguage();
|
||||
const { t } = useTextController();
|
||||
|
||||
return (
|
||||
<section className='relative'>
|
||||
|
||||
@ -4,10 +4,10 @@ import { MapPin } from 'lucide-react';
|
||||
|
||||
import { GasStationMap } from '@/features/map';
|
||||
|
||||
import { useLanguage } from '@/shared/language';
|
||||
import { useTextController } from '@/shared/language/hooks/use-text-controller';
|
||||
|
||||
export const MapSection = () => {
|
||||
const { t } = useLanguage();
|
||||
const { t } = useTextController();
|
||||
|
||||
return (
|
||||
<section id='stations' className='bg-gray-50 px-2 py-8 sm:py-16'>
|
||||
|
||||
@ -4,11 +4,11 @@ import { Handshake } from 'lucide-react';
|
||||
import Image from 'next/image';
|
||||
import Link from 'next/link';
|
||||
|
||||
import { useLanguage } from '@/shared/language';
|
||||
import { useTextController } from '@/shared/language/hooks/use-text-controller';
|
||||
import { Button } from '@/shared/shadcn-ui/button';
|
||||
|
||||
export const PartnersSection = () => {
|
||||
const { t } = useLanguage();
|
||||
const { t } = useTextController();
|
||||
|
||||
return (
|
||||
<section id='partners' className='bg-gray-50 px-2 py-8 sm:py-16'>
|
||||
|
||||
@ -3,13 +3,13 @@
|
||||
import { Gift } from 'lucide-react';
|
||||
|
||||
import PromotionSlider from '@/shared/components/promotion-slider';
|
||||
import { useLanguage } from '@/shared/language';
|
||||
import { useTextController } from '@/shared/language/hooks/use-text-controller';
|
||||
|
||||
export const PromotionsSection = () => {
|
||||
const { t } = useLanguage();
|
||||
const { t } = useTextController();
|
||||
|
||||
return (
|
||||
<section id='promotions' className='bg-gray-50 py-8 px-2 sm:py-16'>
|
||||
<section id='promotions' className='bg-gray-50 px-2 py-8 sm:py-16'>
|
||||
<div className='container mx-auto'>
|
||||
<div className='mb-12 flex flex-col items-center justify-center text-center'>
|
||||
<div className='mb-4 inline-flex items-center justify-center rounded-full bg-red-100 p-2'>
|
||||
|
||||
@ -2,14 +2,15 @@
|
||||
|
||||
import { useEffect, useRef, useState } from 'react';
|
||||
|
||||
import { useTextController } from '@/shared/language/hooks/use-text-controller';
|
||||
|
||||
import AnimatedCounter from '../shared/components/animated-counter';
|
||||
import { useLanguage } from '@/shared/language';
|
||||
|
||||
export function StatsSection() {
|
||||
const [isVisible, setIsVisible] = useState(false);
|
||||
const sectionRef = useRef<HTMLDivElement>(null);
|
||||
|
||||
const { t } = useLanguage()
|
||||
const { t } = useTextController();
|
||||
|
||||
useEffect(() => {
|
||||
const observer = new IntersectionObserver(
|
||||
@ -35,9 +36,12 @@ export function StatsSection() {
|
||||
}, []);
|
||||
|
||||
return (
|
||||
<section ref={sectionRef} className='bg-red-600 py-6 sm:py-12 px-2 text-white'>
|
||||
<section
|
||||
ref={sectionRef}
|
||||
className='bg-red-600 px-2 py-6 text-white sm:py-12'
|
||||
>
|
||||
<div className='container mx-auto'>
|
||||
<div className='grid grid-cols-2 gap-4 sm:gap-8 text-center md:grid-cols-4'>
|
||||
<div className='grid grid-cols-2 gap-4 text-center sm:gap-8 md:grid-cols-4'>
|
||||
<div className='space-y-2'>
|
||||
<h3 className='text-3xl font-bold'>
|
||||
{isVisible ? <AnimatedCounter end={25} suffix='+' /> : '0+'}
|
||||
|
||||
@ -1,10 +1,11 @@
|
||||
"use client";
|
||||
'use client';
|
||||
|
||||
import { format, subMonths } from 'date-fns';
|
||||
import { ru } from 'date-fns/locale';
|
||||
import { CalendarIcon } from 'lucide-react';
|
||||
import { useState } from 'react';
|
||||
|
||||
import { useTextController } from '@/shared/language/hooks/use-text-controller';
|
||||
import { Button } from '@/shared/shadcn-ui/button';
|
||||
import { Calendar } from '@/shared/shadcn-ui/calendar';
|
||||
import { Label } from '@/shared/shadcn-ui/label';
|
||||
@ -21,7 +22,6 @@ import {
|
||||
TableHeader,
|
||||
TableRow,
|
||||
} from '@/shared/shadcn-ui/table';
|
||||
import { useLanguage } from '@/shared/language';
|
||||
|
||||
// Sample customer data
|
||||
const customerData = {
|
||||
@ -100,17 +100,21 @@ export const TransactionsTable = () => {
|
||||
setFilteredTransactions(filtered);
|
||||
};
|
||||
|
||||
const {t} = useLanguage();
|
||||
const { t } = useTextController();
|
||||
|
||||
return (
|
||||
<div className='space-y-6'>
|
||||
<div className='flex flex-col items-start justify-between gap-4 md:flex-row md:items-center'>
|
||||
<h2 className='text-2xl font-bold'>{t('corporate.transactions.title')}</h2>
|
||||
<h2 className='text-2xl font-bold'>
|
||||
{t('corporate.transactions.title')}
|
||||
</h2>
|
||||
|
||||
<div className='flex w-full flex-col gap-4 md:w-auto md:flex-row'>
|
||||
<div className='grid grid-cols-2 gap-2'>
|
||||
<div className='flex items-center gap-2'>
|
||||
<Label htmlFor='start-date'>{t('corporate.transactions.dateFrom')}</Label>
|
||||
<Label htmlFor='start-date'>
|
||||
{t('corporate.transactions.dateFrom')}
|
||||
</Label>
|
||||
<Popover>
|
||||
<PopoverTrigger asChild>
|
||||
<Button
|
||||
@ -135,7 +139,9 @@ export const TransactionsTable = () => {
|
||||
</div>
|
||||
|
||||
<div className='flex items-center gap-2'>
|
||||
<Label htmlFor='end-date'>{t('corporate.transactions.dateTo')}</Label>
|
||||
<Label htmlFor='end-date'>
|
||||
{t('corporate.transactions.dateTo')}
|
||||
</Label>
|
||||
<Popover>
|
||||
<PopoverTrigger asChild>
|
||||
<Button
|
||||
@ -173,12 +179,24 @@ export const TransactionsTable = () => {
|
||||
<Table>
|
||||
<TableHeader>
|
||||
<TableRow>
|
||||
<TableHead>{t('corporate.transactions.tableHeaders.date')}</TableHead>
|
||||
<TableHead>{t('corporate.transactions.tableHeaders.station')}</TableHead>
|
||||
<TableHead>{t('corporate.transactions.tableHeaders.product')}</TableHead>
|
||||
<TableHead className='text-right'>{t('corporate.transactions.tableHeaders.quantity')}</TableHead>
|
||||
<TableHead className='text-right'>{t('corporate.transactions.tableHeaders.price')}</TableHead>
|
||||
<TableHead className='text-right'>{t('corporate.transactions.tableHeaders.total')}</TableHead>
|
||||
<TableHead>
|
||||
{t('corporate.transactions.tableHeaders.date')}
|
||||
</TableHead>
|
||||
<TableHead>
|
||||
{t('corporate.transactions.tableHeaders.station')}
|
||||
</TableHead>
|
||||
<TableHead>
|
||||
{t('corporate.transactions.tableHeaders.product')}
|
||||
</TableHead>
|
||||
<TableHead className='text-right'>
|
||||
{t('corporate.transactions.tableHeaders.quantity')}
|
||||
</TableHead>
|
||||
<TableHead className='text-right'>
|
||||
{t('corporate.transactions.tableHeaders.price')}
|
||||
</TableHead>
|
||||
<TableHead className='text-right'>
|
||||
{t('corporate.transactions.tableHeaders.total')}
|
||||
</TableHead>
|
||||
</TableRow>
|
||||
</TableHeader>
|
||||
<TableBody>
|
||||
@ -214,6 +232,6 @@ export const TransactionsTable = () => {
|
||||
</TableBody>
|
||||
</Table>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
@ -2,7 +2,7 @@
|
||||
|
||||
import { Briefcase } from 'lucide-react';
|
||||
|
||||
import { useLanguage } from '@/shared/language';
|
||||
import { useTextController } from '@/shared/language/hooks/use-text-controller';
|
||||
import { cn } from '@/shared/lib/utils';
|
||||
import { Badge } from '@/shared/shadcn-ui/badge';
|
||||
import { Button } from '@/shared/shadcn-ui/button';
|
||||
@ -15,7 +15,7 @@ import {
|
||||
} from '@/shared/shadcn-ui/tabs';
|
||||
|
||||
export const VacanciesSection = () => {
|
||||
const { t } = useLanguage();
|
||||
const { t } = useTextController();
|
||||
|
||||
return (
|
||||
<section id='vacancies' className='px-2 py-8 sm:py-16'>
|
||||
@ -35,8 +35,12 @@ export const VacanciesSection = () => {
|
||||
<Tabs defaultValue='all' className='mx-auto w-full max-w-3xl'>
|
||||
<TabsList className='mb-8 grid grid-cols-3'>
|
||||
<TabsTrigger value='all'>{t('home.vacancies.all')}</TabsTrigger>
|
||||
<TabsTrigger value='office'>{t('home.vacancies.office')}</TabsTrigger>
|
||||
<TabsTrigger value='stations'>{t('home.vacancies.stations')}</TabsTrigger>
|
||||
<TabsTrigger value='office'>
|
||||
{t('home.vacancies.office')}
|
||||
</TabsTrigger>
|
||||
<TabsTrigger value='stations'>
|
||||
{t('home.vacancies.stations')}
|
||||
</TabsTrigger>
|
||||
</TabsList>
|
||||
<TabsContent value='all' className='space-y-4'>
|
||||
{[
|
||||
@ -92,7 +96,7 @@ interface VacancyProps {
|
||||
}
|
||||
|
||||
const Vacancy = ({ jobTitle, location, tags }: VacancyProps) => {
|
||||
const { t } = useLanguage();
|
||||
const { t } = useTextController();
|
||||
|
||||
return (
|
||||
<Card
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user