Merge branch 'dev' of https://devgit.oriyo.tj/adilovcode/oriyo_next into dev
This commit is contained in:
commit
5bc7d1d901
@ -16,6 +16,7 @@
|
||||
"@radix-ui/react-dropdown-menu": "^2.1.11",
|
||||
"@radix-ui/react-label": "^2.1.4",
|
||||
"@radix-ui/react-navigation-menu": "^1.2.10",
|
||||
"@radix-ui/react-popover": "^1.1.11",
|
||||
"@radix-ui/react-select": "^2.2.2",
|
||||
"@radix-ui/react-slot": "^1.2.0",
|
||||
"@radix-ui/react-tabs": "^1.1.8",
|
||||
@ -24,6 +25,7 @@
|
||||
"aos": "^2.3.4",
|
||||
"class-variance-authority": "^0.7.1",
|
||||
"clsx": "^2.1.1",
|
||||
"date-fns": "^4.1.0",
|
||||
"embla-carousel-autoplay": "^8.6.0",
|
||||
"embla-carousel-react": "^8.6.0",
|
||||
"json-to-graphql-query": "^2.3.0",
|
||||
@ -32,12 +34,14 @@
|
||||
"next": "15.3.1",
|
||||
"next-themes": "^0.4.6",
|
||||
"react": "^19.0.0",
|
||||
"react-day-picker": "8.10.1",
|
||||
"react-dom": "^19.0.0",
|
||||
"react-hook-form": "^7.56.1",
|
||||
"react-redux": "^9.2.0",
|
||||
"sonner": "^2.0.3",
|
||||
"tailwind-merge": "^3.2.0",
|
||||
"tailwindcss-animate": "^1.0.7",
|
||||
"tailwindcss-animated": "^2.0.0",
|
||||
"tw-animate-css": "^1.2.6",
|
||||
"zod": "^3.24.3"
|
||||
},
|
||||
|
||||
73
pnpm-lock.yaml
generated
73
pnpm-lock.yaml
generated
@ -26,6 +26,9 @@ importers:
|
||||
'@radix-ui/react-navigation-menu':
|
||||
specifier: ^1.2.10
|
||||
version: 1.2.10(@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)
|
||||
'@radix-ui/react-popover':
|
||||
specifier: ^1.1.11
|
||||
version: 1.1.11(@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)
|
||||
'@radix-ui/react-select':
|
||||
specifier: ^2.2.2
|
||||
version: 2.2.2(@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)
|
||||
@ -50,6 +53,9 @@ importers:
|
||||
clsx:
|
||||
specifier: ^2.1.1
|
||||
version: 2.1.1
|
||||
date-fns:
|
||||
specifier: ^4.1.0
|
||||
version: 4.1.0
|
||||
embla-carousel-autoplay:
|
||||
specifier: ^8.6.0
|
||||
version: 8.6.0(embla-carousel@8.6.0)
|
||||
@ -74,6 +80,9 @@ importers:
|
||||
react:
|
||||
specifier: ^19.0.0
|
||||
version: 19.1.0
|
||||
react-day-picker:
|
||||
specifier: 8.10.1
|
||||
version: 8.10.1(date-fns@4.1.0)(react@19.1.0)
|
||||
react-dom:
|
||||
specifier: ^19.0.0
|
||||
version: 19.1.0(react@19.1.0)
|
||||
@ -92,6 +101,9 @@ importers:
|
||||
tailwindcss-animate:
|
||||
specifier: ^1.0.7
|
||||
version: 1.0.7(tailwindcss@4.1.4)
|
||||
tailwindcss-animated:
|
||||
specifier: ^2.0.0
|
||||
version: 2.0.0(tailwindcss@4.1.4)
|
||||
tw-animate-css:
|
||||
specifier: ^1.2.6
|
||||
version: 1.2.6
|
||||
@ -690,6 +702,19 @@ packages:
|
||||
'@types/react-dom':
|
||||
optional: true
|
||||
|
||||
'@radix-ui/react-popover@1.1.11':
|
||||
resolution: {integrity: sha512-yFMfZkVA5G3GJnBgb2PxrrcLKm1ZLWXrbYVgdyTl//0TYEIHS9LJbnyz7WWcZ0qCq7hIlJZpRtxeSeIG5T5oJw==}
|
||||
peerDependencies:
|
||||
'@types/react': '*'
|
||||
'@types/react-dom': '*'
|
||||
react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc
|
||||
react-dom: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc
|
||||
peerDependenciesMeta:
|
||||
'@types/react':
|
||||
optional: true
|
||||
'@types/react-dom':
|
||||
optional: true
|
||||
|
||||
'@radix-ui/react-popper@1.2.4':
|
||||
resolution: {integrity: sha512-3p2Rgm/a1cK0r/UVkx5F/K9v/EplfjAeIFCGOPYPO4lZ0jtg4iSQXt/YGTSLWaf4x7NG6Z4+uKFcylcTZjeqDA==}
|
||||
peerDependencies:
|
||||
@ -1382,6 +1407,9 @@ packages:
|
||||
resolution: {integrity: sha512-BS8PfmtDGnrgYdOonGZQdLZslWIeCGFP9tpan0hi1Co2Zr2NKADsvGYA8XxuG/4UWgJ6Cjtv+YJnB6MM69QGlQ==}
|
||||
engines: {node: '>= 0.4'}
|
||||
|
||||
date-fns@4.1.0:
|
||||
resolution: {integrity: sha512-Ukq0owbQXxa/U3EGtsdVBkR1w7KOQ5gIBqdH2hkvknzZPYvBxb/aa6E8L7tmjFtkwZBu3UXBbjIgPo/Ez4xaNg==}
|
||||
|
||||
debug@3.2.7:
|
||||
resolution: {integrity: sha512-CFjzYYAi4ThfiQvizrFQevTTXHtnCqWfe7x1AhgEscTz6ZbLbfoLRLPugTQyBth6f8ZERVUSyWHFD/7Wu4t1XQ==}
|
||||
peerDependencies:
|
||||
@ -2276,6 +2304,12 @@ packages:
|
||||
queue-microtask@1.2.3:
|
||||
resolution: {integrity: sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A==}
|
||||
|
||||
react-day-picker@8.10.1:
|
||||
resolution: {integrity: sha512-TMx7fNbhLk15eqcMt+7Z7S2KF7mfTId/XJDjKE8f+IUcFn0l08/kI4FiYTL/0yuOLmEcbR4Fwe3GJf/NiiMnPA==}
|
||||
peerDependencies:
|
||||
date-fns: ^2.28.0 || ^3.0.0
|
||||
react: ^16.8.0 || ^17.0.0 || ^18.0.0
|
||||
|
||||
react-dom@19.1.0:
|
||||
resolution: {integrity: sha512-Xs1hdnE+DyKgeHJeJznQmYMIBG3TKIHJJT95Q58nHLSrElKlGQqDTR2HQ9fx5CN/Gk6Vh/kupBTDLU11/nDk/g==}
|
||||
peerDependencies:
|
||||
@ -2522,6 +2556,11 @@ packages:
|
||||
peerDependencies:
|
||||
tailwindcss: '>=3.0.0 || insiders'
|
||||
|
||||
tailwindcss-animated@2.0.0:
|
||||
resolution: {integrity: sha512-anNNGpxNgjydD8p1lcJjxxH+XbjW6KR8Xs29owTrbcf3tOJ6IRblpyFob43HBkfxFJJTAfFQqugoEG2b1EsR0A==}
|
||||
peerDependencies:
|
||||
tailwindcss: '>=3.1.0 || >=4.0.0'
|
||||
|
||||
tailwindcss@4.1.4:
|
||||
resolution: {integrity: sha512-1ZIUqtPITFbv/DxRmDr5/agPqJwF69d24m9qmM1939TJehgY539CtzeZRjbLt5G6fSy/7YqqYsfvoTEw9xUI2A==}
|
||||
|
||||
@ -3136,6 +3175,29 @@ snapshots:
|
||||
'@types/react': 19.1.2
|
||||
'@types/react-dom': 19.1.2(@types/react@19.1.2)
|
||||
|
||||
'@radix-ui/react-popover@1.1.11(@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)':
|
||||
dependencies:
|
||||
'@radix-ui/primitive': 1.1.2
|
||||
'@radix-ui/react-compose-refs': 1.1.2(@types/react@19.1.2)(react@19.1.0)
|
||||
'@radix-ui/react-context': 1.1.2(@types/react@19.1.2)(react@19.1.0)
|
||||
'@radix-ui/react-dismissable-layer': 1.1.7(@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)
|
||||
'@radix-ui/react-focus-guards': 1.1.2(@types/react@19.1.2)(react@19.1.0)
|
||||
'@radix-ui/react-focus-scope': 1.1.4(@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)
|
||||
'@radix-ui/react-id': 1.1.1(@types/react@19.1.2)(react@19.1.0)
|
||||
'@radix-ui/react-popper': 1.2.4(@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)
|
||||
'@radix-ui/react-portal': 1.1.6(@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)
|
||||
'@radix-ui/react-presence': 1.1.4(@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)
|
||||
'@radix-ui/react-primitive': 2.1.0(@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)
|
||||
'@radix-ui/react-slot': 1.2.0(@types/react@19.1.2)(react@19.1.0)
|
||||
'@radix-ui/react-use-controllable-state': 1.2.2(@types/react@19.1.2)(react@19.1.0)
|
||||
aria-hidden: 1.2.4
|
||||
react: 19.1.0
|
||||
react-dom: 19.1.0(react@19.1.0)
|
||||
react-remove-scroll: 2.6.3(@types/react@19.1.2)(react@19.1.0)
|
||||
optionalDependencies:
|
||||
'@types/react': 19.1.2
|
||||
'@types/react-dom': 19.1.2(@types/react@19.1.2)
|
||||
|
||||
'@radix-ui/react-popper@1.2.4(@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)':
|
||||
dependencies:
|
||||
'@floating-ui/react-dom': 2.1.2(react-dom@19.1.0(react@19.1.0))(react@19.1.0)
|
||||
@ -3831,6 +3893,8 @@ snapshots:
|
||||
es-errors: 1.3.0
|
||||
is-data-view: 1.0.2
|
||||
|
||||
date-fns@4.1.0: {}
|
||||
|
||||
debug@3.2.7:
|
||||
dependencies:
|
||||
ms: 2.1.3
|
||||
@ -4801,6 +4865,11 @@ snapshots:
|
||||
|
||||
queue-microtask@1.2.3: {}
|
||||
|
||||
react-day-picker@8.10.1(date-fns@4.1.0)(react@19.1.0):
|
||||
dependencies:
|
||||
date-fns: 4.1.0
|
||||
react: 19.1.0
|
||||
|
||||
react-dom@19.1.0(react@19.1.0):
|
||||
dependencies:
|
||||
react: 19.1.0
|
||||
@ -5096,6 +5165,10 @@ snapshots:
|
||||
dependencies:
|
||||
tailwindcss: 4.1.4
|
||||
|
||||
tailwindcss-animated@2.0.0(tailwindcss@4.1.4):
|
||||
dependencies:
|
||||
tailwindcss: 4.1.4
|
||||
|
||||
tailwindcss@4.1.4: {}
|
||||
|
||||
tapable@2.2.1: {}
|
||||
|
||||
BIN
public/clients/loyalty-card.png
Normal file
BIN
public/clients/loyalty-card.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 164 KiB |
BIN
public/oriyo_bg.jpeg
Normal file
BIN
public/oriyo_bg.jpeg
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 112 KiB |
308
src/app/(dashboard)/corporate-dashboard/page.tsx
Normal file
308
src/app/(dashboard)/corporate-dashboard/page.tsx
Normal file
@ -0,0 +1,308 @@
|
||||
'use client';
|
||||
|
||||
import { format, subMonths } from 'date-fns';
|
||||
import { ru } from 'date-fns/locale';
|
||||
import { Building2, CalendarIcon, LogOut, Wallet } from 'lucide-react';
|
||||
import { useState } from 'react';
|
||||
|
||||
import { Button } from '@/shared/shadcn-ui/button';
|
||||
import { Calendar } from '@/shared/shadcn-ui/calendar';
|
||||
import {
|
||||
Card,
|
||||
CardContent,
|
||||
CardDescription,
|
||||
CardHeader,
|
||||
CardTitle,
|
||||
} from '@/shared/shadcn-ui/card';
|
||||
import { Label } from '@/shared/shadcn-ui/label';
|
||||
import {
|
||||
Popover,
|
||||
PopoverContent,
|
||||
PopoverTrigger,
|
||||
} from '@/shared/shadcn-ui/popover';
|
||||
import {
|
||||
Table,
|
||||
TableBody,
|
||||
TableCell,
|
||||
TableHead,
|
||||
TableHeader,
|
||||
TableRow,
|
||||
} from '@/shared/shadcn-ui/table';
|
||||
|
||||
// import { CardsList } from '@/widgets/cards-list';
|
||||
|
||||
// Sample company data
|
||||
const companyData = {
|
||||
companyName: 'ООО «ТаджикТранс»',
|
||||
numberOfCards: 12,
|
||||
fund: 25000,
|
||||
overdraft: 5000,
|
||||
totalFund: 30000,
|
||||
registrationDate: '10.03.2019',
|
||||
};
|
||||
|
||||
// Sample transaction data
|
||||
const generateTransactions = () => {
|
||||
const stations = [
|
||||
'АЗС Душанбе-Центр',
|
||||
'АЗС Душанбе-Запад',
|
||||
'АЗС Душанбе-Восток',
|
||||
'АЗС Худжанд',
|
||||
'АЗС Куляб',
|
||||
];
|
||||
|
||||
const products = [
|
||||
{ name: 'ДТ', price: 8.5 },
|
||||
{ name: 'АИ-92', price: 9.2 },
|
||||
{ name: 'АИ-95', price: 10.5 },
|
||||
{ name: 'Z-100 Power', price: 11.8 },
|
||||
{ name: 'Пропан', price: 6.3 },
|
||||
];
|
||||
|
||||
const transactions = [];
|
||||
|
||||
// Generate 50 random transactions over the last 6 months
|
||||
for (let i = 0; i < 50; i++) {
|
||||
const date = subMonths(new Date(), Math.random() * 6);
|
||||
const station = stations[Math.floor(Math.random() * stations.length)];
|
||||
const product = products[Math.floor(Math.random() * products.length)];
|
||||
const quantity = Math.floor(Math.random() * 40) + 10; // 10-50 liters
|
||||
const cost = product.price;
|
||||
const total = quantity * cost;
|
||||
|
||||
transactions.push({
|
||||
id: i + 1,
|
||||
date,
|
||||
station,
|
||||
product: product.name,
|
||||
quantity,
|
||||
cost,
|
||||
total,
|
||||
});
|
||||
}
|
||||
|
||||
// Sort by date (newest first)
|
||||
return transactions.sort((a, b) => b.date.getTime() - a.date.getTime());
|
||||
};
|
||||
|
||||
const transactions = generateTransactions();
|
||||
|
||||
export default function CorporateDashboard() {
|
||||
const [startDate, setStartDate] = useState<Date | undefined>(
|
||||
subMonths(new Date(), 1),
|
||||
);
|
||||
const [endDate, setEndDate] = useState<Date | undefined>(new Date());
|
||||
const [filteredTransactions, setFilteredTransactions] =
|
||||
useState(transactions);
|
||||
|
||||
// Filter transactions by date range
|
||||
const filterTransactions = () => {
|
||||
if (!startDate || !endDate) return;
|
||||
|
||||
const filtered = transactions.filter((transaction) => {
|
||||
const transactionDate = new Date(transaction.date);
|
||||
return transactionDate >= startDate && transactionDate <= endDate;
|
||||
});
|
||||
|
||||
setFilteredTransactions(filtered);
|
||||
};
|
||||
|
||||
return (
|
||||
<div className='flex min-h-screen flex-col'>
|
||||
<main className='flex-1 py-10'>
|
||||
<div className='container mx-auto max-w-6xl'>
|
||||
<div className='mb-8 flex items-center justify-between'>
|
||||
<h1 className='text-3xl font-bold'>Корпоративный кабинет</h1>
|
||||
<Button variant='outline' className='gap-2'>
|
||||
<LogOut className='h-4 w-4' />
|
||||
Выйти
|
||||
</Button>
|
||||
</div>
|
||||
|
||||
<div className='mb-10 grid gap-3 md:grid-cols-3 md:gap-6'>
|
||||
{/* Company Card */}
|
||||
<Card className='md:col-span-2'>
|
||||
<CardHeader className='pb-2'>
|
||||
<CardTitle className='flex items-center gap-2'>
|
||||
<Building2 className='h-5 w-5 text-red-600' />
|
||||
Информация о компании
|
||||
</CardTitle>
|
||||
</CardHeader>
|
||||
<CardContent>
|
||||
<div className='grid gap-6 md:grid-cols-2'>
|
||||
<div>
|
||||
<div className='mb-4 space-y-1'>
|
||||
<p className='text-sm text-gray-500'>Название компании</p>
|
||||
<p className='font-medium'>{companyData.companyName}</p>
|
||||
</div>
|
||||
<div className='mb-4 space-y-1'>
|
||||
<p className='text-sm text-gray-500'>Количество карт</p>
|
||||
<p className='font-medium'>{companyData.numberOfCards}</p>
|
||||
</div>
|
||||
<div className='space-y-1'>
|
||||
<p className='text-sm text-gray-500'>Дата регистрации</p>
|
||||
<p className='font-medium'>
|
||||
{companyData.registrationDate}
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
<div>
|
||||
<div className='mb-4 space-y-1'>
|
||||
<p className='text-sm text-gray-500'>Фонд</p>
|
||||
<p className='font-medium'>
|
||||
{companyData.fund.toLocaleString()} сомони
|
||||
</p>
|
||||
</div>
|
||||
<div className='mb-4 space-y-1'>
|
||||
<p className='text-sm text-gray-500'>Овердрафт</p>
|
||||
<p className='font-medium'>
|
||||
{companyData.overdraft.toLocaleString()} сомони
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</CardContent>
|
||||
</Card>
|
||||
|
||||
{/* Fund Card */}
|
||||
<Card className='bg-gradient-to-br from-red-600 to-red-800 text-white'>
|
||||
<CardHeader>
|
||||
<CardTitle className='flex items-center gap-2'>
|
||||
<Wallet className='h-5 w-5' />
|
||||
Общий фонд
|
||||
</CardTitle>
|
||||
<CardDescription className='text-white/80'>
|
||||
Доступные средства
|
||||
</CardDescription>
|
||||
</CardHeader>
|
||||
<CardContent>
|
||||
<div className='text-center'>
|
||||
<p className='mb-2 text-4xl font-bold'>
|
||||
{companyData.totalFund.toLocaleString()}
|
||||
</p>
|
||||
<p className='text-white/80'>сомони</p>
|
||||
</div>
|
||||
</CardContent>
|
||||
</Card>
|
||||
</div>
|
||||
|
||||
{/* <CardsList totalCards={companyData.numberOfCards} /> */}
|
||||
|
||||
{/* Transactions */}
|
||||
<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'>История операций</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'>От</Label>
|
||||
<Popover>
|
||||
<PopoverTrigger asChild>
|
||||
<Button
|
||||
variant='outline'
|
||||
className='w-full justify-start text-left font-normal'
|
||||
>
|
||||
<CalendarIcon className='mr-2 h-4 w-4' />
|
||||
{startDate
|
||||
? format(startDate, 'PP', { locale: ru })
|
||||
: 'Выберите дату'}
|
||||
</Button>
|
||||
</PopoverTrigger>
|
||||
<PopoverContent className='w-auto p-0'>
|
||||
<Calendar
|
||||
mode='single'
|
||||
selected={startDate}
|
||||
onSelect={setStartDate}
|
||||
initialFocus
|
||||
/>
|
||||
</PopoverContent>
|
||||
</Popover>
|
||||
</div>
|
||||
|
||||
<div className='flex items-center gap-2'>
|
||||
<Label htmlFor='end-date'>До</Label>
|
||||
<Popover>
|
||||
<PopoverTrigger asChild>
|
||||
<Button
|
||||
variant='outline'
|
||||
className='w-full justify-start text-left font-normal'
|
||||
>
|
||||
<CalendarIcon className='mr-2 h-4 w-4' />
|
||||
{endDate
|
||||
? format(endDate, 'PP', { locale: ru })
|
||||
: 'Выберите дату'}
|
||||
</Button>
|
||||
</PopoverTrigger>
|
||||
<PopoverContent className='w-auto p-0'>
|
||||
<Calendar
|
||||
mode='single'
|
||||
selected={endDate}
|
||||
onSelect={setEndDate}
|
||||
initialFocus
|
||||
/>
|
||||
</PopoverContent>
|
||||
</Popover>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<Button
|
||||
className='mt-auto bg-red-600 hover:bg-red-700'
|
||||
onClick={filterTransactions}
|
||||
>
|
||||
Применить
|
||||
</Button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className='rounded-md border'>
|
||||
<Table>
|
||||
<TableHeader>
|
||||
<TableRow>
|
||||
<TableHead>Дата</TableHead>
|
||||
<TableHead>Станция</TableHead>
|
||||
<TableHead>Продукт</TableHead>
|
||||
<TableHead className='text-right'>Кол-во (л)</TableHead>
|
||||
<TableHead className='text-right'>Стоимость</TableHead>
|
||||
<TableHead className='text-right'>Сумма</TableHead>
|
||||
</TableRow>
|
||||
</TableHeader>
|
||||
<TableBody>
|
||||
{filteredTransactions.length > 0 ? (
|
||||
filteredTransactions.map((transaction) => (
|
||||
<TableRow key={transaction.id}>
|
||||
<TableCell>
|
||||
{format(transaction.date, 'dd.MM.yyyy')}
|
||||
</TableCell>
|
||||
<TableCell>{transaction.station}</TableCell>
|
||||
<TableCell>{transaction.product}</TableCell>
|
||||
<TableCell className='text-right'>
|
||||
{transaction.quantity}
|
||||
</TableCell>
|
||||
<TableCell className='text-right'>
|
||||
{transaction.cost.toFixed(2)} сомони
|
||||
</TableCell>
|
||||
<TableCell className='text-right font-medium'>
|
||||
{transaction.total.toFixed(2)} сомони
|
||||
</TableCell>
|
||||
</TableRow>
|
||||
))
|
||||
) : (
|
||||
<TableRow>
|
||||
<TableCell
|
||||
colSpan={6}
|
||||
className='py-6 text-center text-gray-500'
|
||||
>
|
||||
Нет операций за выбранный период
|
||||
</TableCell>
|
||||
</TableRow>
|
||||
)}
|
||||
</TableBody>
|
||||
</Table>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</main>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
110
src/app/(dashboard)/customer-dashboard/page.tsx
Normal file
110
src/app/(dashboard)/customer-dashboard/page.tsx
Normal file
@ -0,0 +1,110 @@
|
||||
import { ArrowUpRight, Clock, CreditCard, LogOut, User } from 'lucide-react';
|
||||
|
||||
import { Button } from '@/shared/shadcn-ui/button';
|
||||
import {
|
||||
Card,
|
||||
CardContent,
|
||||
CardDescription,
|
||||
CardHeader,
|
||||
CardTitle,
|
||||
} from '@/shared/shadcn-ui/card';
|
||||
|
||||
import { TransactionsTable } from '@/widgets/transactions-table';
|
||||
|
||||
// Sample customer data
|
||||
const customerData = {
|
||||
firstName: 'Алишер',
|
||||
lastName: 'Рахмонов',
|
||||
passportNumber: 'A12345678',
|
||||
bonusPoints: 1250,
|
||||
cardNumber: '5678-9012-3456-7890',
|
||||
expiryDate: '12/2025',
|
||||
registrationDate: '15.06.2020',
|
||||
};
|
||||
|
||||
export default function CustomerDashboard() {
|
||||
return (
|
||||
<div className='flex min-h-screen flex-col'>
|
||||
<main className='flex-1 py-10'>
|
||||
<div className='container mx-auto max-w-6xl'>
|
||||
<div className='mb-8 flex items-center justify-between'>
|
||||
<h1 className='text-3xl font-bold'>Личный кабинет</h1>
|
||||
<Button variant='outline' className='gap-2'>
|
||||
<LogOut className='h-4 w-4' />
|
||||
Выйти
|
||||
</Button>
|
||||
</div>
|
||||
|
||||
<div className='mb-10 grid gap-3 md:grid-cols-3 md:gap-6'>
|
||||
{/* Bonus Card */}
|
||||
<Card className='bg-gradient-to-br from-red-600 to-red-800 text-white'>
|
||||
<CardHeader>
|
||||
<CardTitle className='flex items-center gap-2'>
|
||||
<CreditCard className='h-5 w-5' />
|
||||
Бонусная карта
|
||||
</CardTitle>
|
||||
<CardDescription className='text-white/80'>
|
||||
Ваши накопленные бонусы
|
||||
</CardDescription>
|
||||
</CardHeader>
|
||||
<CardContent>
|
||||
<div className='text-center'>
|
||||
<p className='mb-2 text-4xl font-bold'>
|
||||
{customerData.bonusPoints}
|
||||
</p>
|
||||
<p className='text-white/80'>бонусных баллов</p>
|
||||
</div>
|
||||
<div className='mt-6 flex items-center justify-between'>
|
||||
<div className='flex items-center gap-1 text-sm text-white/80'>
|
||||
<Clock className='h-4 w-4' />
|
||||
<span>Действует до: 31.12.2023</span>
|
||||
</div>
|
||||
<ArrowUpRight className='h-5 w-5 text-white/60' />
|
||||
</div>
|
||||
</CardContent>
|
||||
</Card>
|
||||
{/* Customer Card */}
|
||||
<Card className='md:col-span-2'>
|
||||
<CardHeader className='pb-2'>
|
||||
<CardTitle className='flex items-center gap-2'>
|
||||
<User className='h-5 w-5 text-red-600' />
|
||||
Информация о клиенте
|
||||
</CardTitle>
|
||||
</CardHeader>
|
||||
<CardContent>
|
||||
<div className='grid gap-6 md:grid-cols-2'>
|
||||
<div>
|
||||
<div className='mb-4 space-y-1'>
|
||||
<p className='text-sm text-gray-500'>ФИО</p>
|
||||
<p className='font-medium'>
|
||||
{customerData.firstName} {customerData.lastName}
|
||||
</p>
|
||||
</div>
|
||||
<div className='space-y-1'>
|
||||
<p className='text-sm text-gray-500'>Дата регистрации</p>
|
||||
<p className='font-medium'>
|
||||
{customerData.registrationDate}
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
<div>
|
||||
<div className='mb-4 space-y-1'>
|
||||
<p className='text-sm text-gray-500'>Номер карты</p>
|
||||
<p className='font-medium'>{customerData.cardNumber}</p>
|
||||
</div>
|
||||
<div className='mb-4 space-y-1'>
|
||||
<p className='text-sm text-gray-500'>Срок действия</p>
|
||||
<p className='font-medium'>{customerData.expiryDate}</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</CardContent>
|
||||
</Card>
|
||||
</div>
|
||||
|
||||
<TransactionsTable />
|
||||
</div>
|
||||
</main>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
@ -1,404 +1,5 @@
|
||||
import {
|
||||
Award,
|
||||
Fuel,
|
||||
History,
|
||||
MapPin,
|
||||
Star,
|
||||
Target,
|
||||
Users,
|
||||
} from 'lucide-react';
|
||||
import Image from 'next/image';
|
||||
import AboutPage from "@/pages-templates/about";
|
||||
|
||||
import AnimatedCounter from '@/shared/components/animated-counter';
|
||||
import { Button } from '@/shared/shadcn-ui/button';
|
||||
import { Card, CardContent } from '@/shared/shadcn-ui/card';
|
||||
|
||||
import { CompanyTimeline } from '@/widgets/about-page/company-timeline';
|
||||
import { StationGallery } from '@/widgets/about-page/station-gallery';
|
||||
import { CtaSection } from '@/widgets/cta-section';
|
||||
|
||||
export const metadata = {
|
||||
title: 'О нас | GasNetwork - Сеть заправок в Таджикистане',
|
||||
description:
|
||||
'Узнайте больше о нашей компании, истории и ценностях. Качественное топливо и отличный сервис.',
|
||||
};
|
||||
|
||||
export default function AboutPage() {
|
||||
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=400&width=1920&text=Наша+История'
|
||||
alt='О нашей компании'
|
||||
width={1920}
|
||||
height={400}
|
||||
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-4 text-white'>
|
||||
<h1 className='text-4xl font-bold tracking-tight sm:text-5xl md:text-6xl'>
|
||||
О нашей компании
|
||||
</h1>
|
||||
<p className='text-lg text-gray-200'>
|
||||
Узнайте больше о нашей истории, ценностях и миссии. Мы
|
||||
стремимся предоставлять лучший сервис и качественное топливо
|
||||
для наших клиентов.
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
{/* Company Overview */}
|
||||
<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'>
|
||||
<Fuel className='h-6 w-6 text-red-600' />
|
||||
</div>
|
||||
<h2 className='mb-6 text-3xl font-bold tracking-tight sm:text-4xl'>
|
||||
Лидер на рынке Таджикистана
|
||||
</h2>
|
||||
<p className='mb-6 text-gray-600'>
|
||||
GasNetwork - ведущая сеть автозаправочных станций в
|
||||
Таджикистане, предоставляющая высококачественное топливо и
|
||||
превосходный сервис. Наша компания была основана в 2008 году и
|
||||
с тех пор стала символом надежности и качества в
|
||||
энергетическом секторе страны.
|
||||
</p>
|
||||
<p className='mb-6 text-gray-600'>
|
||||
Мы гордимся тем, что предлагаем нашим клиентам только лучшее
|
||||
топливо, соответствующее международным стандартам качества.
|
||||
Наши заправочные станции оснащены современным оборудованием,
|
||||
которое обеспечивает быстрое и безопасное обслуживание.
|
||||
</p>
|
||||
<p className='mb-6 text-gray-600'>
|
||||
Наша миссия - сделать поездки наших клиентов комфортными и
|
||||
безопасными, предоставляя качественное топливо и отличный
|
||||
сервис по всей стране.
|
||||
</p>
|
||||
|
||||
<div className='mb-6 grid grid-cols-2 gap-4'>
|
||||
<div className='flex items-start'>
|
||||
<div className='mt-1 flex h-6 w-6 flex-shrink-0 items-center justify-center rounded-full bg-red-600'>
|
||||
<span className='text-xs text-white'>✓</span>
|
||||
</div>
|
||||
<div className='ml-3'>
|
||||
<h3 className='text-lg font-medium'>Качество</h3>
|
||||
<p className='text-gray-600'>Топливо высшего стандарта</p>
|
||||
</div>
|
||||
</div>
|
||||
<div className='flex items-start'>
|
||||
<div className='mt-1 flex h-6 w-6 flex-shrink-0 items-center justify-center rounded-full bg-red-600'>
|
||||
<span className='text-xs text-white'>✓</span>
|
||||
</div>
|
||||
<div className='ml-3'>
|
||||
<h3 className='text-lg font-medium'>Сервис</h3>
|
||||
<p className='text-gray-600'>
|
||||
Профессиональное обслуживание
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
<div className='flex items-start'>
|
||||
<div className='mt-1 flex h-6 w-6 flex-shrink-0 items-center justify-center rounded-full bg-red-600'>
|
||||
<span className='text-xs text-white'>✓</span>
|
||||
</div>
|
||||
<div className='ml-3'>
|
||||
<h3 className='text-lg font-medium'>Инновации</h3>
|
||||
<p className='text-gray-600'>Современные технологии</p>
|
||||
</div>
|
||||
</div>
|
||||
<div className='flex items-start'>
|
||||
<div className='mt-1 flex h-6 w-6 flex-shrink-0 items-center justify-center rounded-full bg-red-600'>
|
||||
<span className='text-xs text-white'>✓</span>
|
||||
</div>
|
||||
<div className='ml-3'>
|
||||
<h3 className='text-lg font-medium'>Доступность</h3>
|
||||
<p className='text-gray-600'>Станции по всей стране</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='Главный офис GasNetwork'
|
||||
fill
|
||||
className='object-cover'
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
{/* Stats Section */}
|
||||
<section className='bg-red-600 py-16 text-white'>
|
||||
<div className='container mx-auto'>
|
||||
<div className='mb-12 text-center'>
|
||||
<h2 className='mb-4 text-3xl font-bold tracking-tight sm:text-4xl'>
|
||||
GasNetwork в цифрах
|
||||
</h2>
|
||||
<p className='mx-auto max-w-2xl text-white/80'>
|
||||
Наши достижения и рост за годы работы на рынке Таджикистана
|
||||
</p>
|
||||
</div>
|
||||
<div className='grid grid-cols-2 gap-8 text-center md:grid-cols-4'>
|
||||
<div className='space-y-2'>
|
||||
<h3 className='text-4xl font-bold'>
|
||||
<AnimatedCounter end={25} suffix='+' />
|
||||
</h3>
|
||||
<p className='text-sm text-white/80'>Заправок по стране</p>
|
||||
</div>
|
||||
<div className='space-y-2'>
|
||||
<h3 className='text-4xl font-bold'>
|
||||
<AnimatedCounter end={15} />
|
||||
</h3>
|
||||
<p className='text-sm text-white/80'>Лет на рынке</p>
|
||||
</div>
|
||||
<div className='space-y-2'>
|
||||
<h3 className='text-4xl font-bold'>
|
||||
<AnimatedCounter end={150} suffix='+' />
|
||||
</h3>
|
||||
<p className='text-sm text-white/80'>Сотрудников</p>
|
||||
</div>
|
||||
<div className='space-y-2'>
|
||||
<h3 className='text-4xl font-bold'>
|
||||
<AnimatedCounter end={1000000} suffix='+' />
|
||||
</h3>
|
||||
<p className='text-sm text-white/80'>Клиентов в год</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
{/* Our History */}
|
||||
<section className='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'>
|
||||
<History className='h-6 w-6 text-red-600' />
|
||||
</div>
|
||||
<h2 className='mb-4 text-3xl font-bold tracking-tight sm:text-4xl'>
|
||||
Наша история
|
||||
</h2>
|
||||
<p className='max-w-2xl text-gray-600'>
|
||||
История развития нашей компании от небольшой сети до лидера
|
||||
рынка
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<CompanyTimeline />
|
||||
</div>
|
||||
</section>
|
||||
|
||||
{/* Our Stations */}
|
||||
<section className='bg-gray-50 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'>
|
||||
<MapPin className='h-6 w-6 text-red-600' />
|
||||
</div>
|
||||
<h2 className='mb-4 text-3xl font-bold tracking-tight sm:text-4xl'>
|
||||
Наши заправочные станции
|
||||
</h2>
|
||||
<p className='max-w-2xl text-gray-600'>
|
||||
Современные заправочные станции, оснащенные по последнему слову
|
||||
техники
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<StationGallery />
|
||||
|
||||
<div className='mt-12 text-center'>
|
||||
<p className='mx-auto mb-6 max-w-2xl text-gray-600'>
|
||||
Наши заправочные станции расположены в стратегически важных
|
||||
точках по всему Таджикистану, обеспечивая удобный доступ для
|
||||
всех наших клиентов.
|
||||
</p>
|
||||
<Button className='bg-red-600 hover:bg-red-700'>
|
||||
Найти ближайшую заправку <MapPin className='ml-2 h-4 w-4' />
|
||||
</Button>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
{/* Our Values */}
|
||||
<section className='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'>
|
||||
<Target className='h-6 w-6 text-red-600' />
|
||||
</div>
|
||||
<h2 className='mb-4 text-3xl font-bold tracking-tight sm:text-4xl'>
|
||||
Наши ценности
|
||||
</h2>
|
||||
<p className='max-w-2xl text-gray-600'>
|
||||
Принципы, которыми мы руководствуемся в нашей работе
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<div className='grid gap-8 md:grid-cols-3'>
|
||||
<Card className='overflow-hidden transition-all hover:shadow-lg'>
|
||||
<CardContent className='p-6'>
|
||||
<div className='mb-4 flex h-12 w-12 items-center justify-center rounded-full bg-red-100'>
|
||||
<Star className='h-6 w-6 text-red-600' />
|
||||
</div>
|
||||
<h3 className='mb-2 text-xl font-bold'>Качество</h3>
|
||||
<p className='text-gray-600'>
|
||||
Мы предлагаем только высококачественное топливо,
|
||||
соответствующее международным стандартам. Регулярные
|
||||
проверки и контроль качества гарантируют, что наши клиенты
|
||||
получают лучшее.
|
||||
</p>
|
||||
</CardContent>
|
||||
</Card>
|
||||
|
||||
<Card className='overflow-hidden transition-all hover:shadow-lg'>
|
||||
<CardContent className='p-6'>
|
||||
<div className='mb-4 flex h-12 w-12 items-center justify-center rounded-full bg-red-100'>
|
||||
<Users className='h-6 w-6 text-red-600' />
|
||||
</div>
|
||||
<h3 className='mb-2 text-xl font-bold'>
|
||||
Клиентоориентированность
|
||||
</h3>
|
||||
<p className='text-gray-600'>
|
||||
Наши клиенты - наш главный приоритет. Мы стремимся
|
||||
предоставить лучший сервис, удобные условия и приятную
|
||||
атмосферу на каждой нашей заправке.
|
||||
</p>
|
||||
</CardContent>
|
||||
</Card>
|
||||
|
||||
<Card className='overflow-hidden transition-all hover:shadow-lg'>
|
||||
<CardContent className='p-6'>
|
||||
<div className='mb-4 flex h-12 w-12 items-center justify-center rounded-full bg-red-100'>
|
||||
<Award className='h-6 w-6 text-red-600' />
|
||||
</div>
|
||||
<h3 className='mb-2 text-xl font-bold'>Профессионализм</h3>
|
||||
<p className='text-gray-600'>
|
||||
Наши сотрудники - профессионалы своего дела. Мы постоянно
|
||||
инвестируем в их обучение и развитие, чтобы обеспечить
|
||||
высокий уровень обслуживания.
|
||||
</p>
|
||||
</CardContent>
|
||||
</Card>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
{/* Our Team */}
|
||||
<section className='bg-gray-50 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'>
|
||||
<Users className='h-6 w-6 text-red-600' />
|
||||
</div>
|
||||
<h2 className='mb-4 text-3xl font-bold tracking-tight sm:text-4xl'>
|
||||
Наша команда
|
||||
</h2>
|
||||
<p className='max-w-2xl text-gray-600'>
|
||||
Познакомьтесь с профессионалами, которые делают GasNetwork
|
||||
лучшей сетью заправок в Таджикистане
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<div className='grid grid-cols-1 gap-8 sm:grid-cols-2 lg:grid-cols-4'>
|
||||
{[
|
||||
{ name: 'Алишер Рахмонов', position: 'Генеральный директор' },
|
||||
{ name: 'Фарида Каримова', position: 'Финансовый директор' },
|
||||
{ name: 'Рустам Назаров', position: 'Технический директор' },
|
||||
{ name: 'Зарина Шарипова', position: 'Директор по маркетингу' },
|
||||
].map((person, index) => (
|
||||
<div
|
||||
key={index}
|
||||
className='overflow-hidden rounded-lg bg-white shadow-md transition-transform hover:scale-105'
|
||||
>
|
||||
<div className='relative h-64 w-full'>
|
||||
<Image
|
||||
src={`/placeholder.svg?height=300&width=300&text=${person.name}`}
|
||||
alt={person.name}
|
||||
fill
|
||||
className='object-cover'
|
||||
/>
|
||||
</div>
|
||||
<div className='p-4 text-center'>
|
||||
<h3 className='text-lg font-bold'>{person.name}</h3>
|
||||
<p className='text-gray-600'>{person.position}</p>
|
||||
</div>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
{/* Testimonials */}
|
||||
<section className='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'>
|
||||
<Star className='h-6 w-6 text-red-600' />
|
||||
</div>
|
||||
<h2 className='mb-4 text-3xl font-bold tracking-tight sm:text-4xl'>
|
||||
Отзывы клиентов
|
||||
</h2>
|
||||
<p className='max-w-2xl text-gray-600'>
|
||||
Что говорят о нас наши клиенты
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<div className='grid gap-8 md:grid-cols-3'>
|
||||
{[
|
||||
{
|
||||
name: 'Фархад К.',
|
||||
text: 'Я всегда заправляюсь только на GasNetwork. Качество топлива на высоте, а обслуживание всегда приветливое и быстрое.',
|
||||
rating: 5,
|
||||
},
|
||||
{
|
||||
name: 'Нигина М.',
|
||||
text: 'Очень удобно, что заправки расположены по всему городу. Всегда чисто, есть кафе и магазин. Рекомендую!',
|
||||
rating: 5,
|
||||
},
|
||||
{
|
||||
name: 'Джамшед Р.',
|
||||
text: 'Пользуюсь картой лояльности GasNetwork уже 3 года. Накопил много бонусов и получил немало приятных подарков. Отличный сервис!',
|
||||
rating: 4,
|
||||
},
|
||||
].map((testimonial, index) => (
|
||||
<Card
|
||||
key={index}
|
||||
className='overflow-hidden transition-all hover:shadow-lg'
|
||||
>
|
||||
<CardContent className='p-6'>
|
||||
<div className='mb-4 flex'>
|
||||
{Array(5)
|
||||
.fill(0)
|
||||
.map((_, i) => (
|
||||
<Star
|
||||
key={i}
|
||||
className={`h-5 w-5 ${i < testimonial.rating ? 'fill-yellow-400 text-yellow-400' : 'text-gray-300'}`}
|
||||
/>
|
||||
))}
|
||||
</div>
|
||||
<p className='mb-4 text-gray-600 italic'>
|
||||
"{testimonial.text}"
|
||||
</p>
|
||||
<p className='font-semibold'>{testimonial.name}</p>
|
||||
</CardContent>
|
||||
</Card>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<CtaSection />
|
||||
</main>
|
||||
</div>
|
||||
);
|
||||
export default function About(){
|
||||
return <AboutPage/>
|
||||
}
|
||||
|
||||
59
src/app/api/auth/login/route.ts
Normal file
59
src/app/api/auth/login/route.ts
Normal file
@ -0,0 +1,59 @@
|
||||
import { NextRequest, NextResponse } from 'next/server';
|
||||
|
||||
import { LoginData } from '@/entities/auth/model/types';
|
||||
|
||||
export const GET = async (req: NextRequest) => {
|
||||
if (req.method !== 'GET') {
|
||||
return NextResponse.json(
|
||||
{ error: 'Method is not supported' },
|
||||
{ status: 405 },
|
||||
);
|
||||
}
|
||||
|
||||
const { searchParams } = req.nextUrl;
|
||||
|
||||
const phoneNumber = searchParams.get('phoneNumber');
|
||||
const cardNumber = searchParams.get('cardNumber');
|
||||
const type = searchParams.get('type');
|
||||
|
||||
if (!phoneNumber || !cardNumber || !type) {
|
||||
return NextResponse.json({ error: 'Bad request' }, { status: 400 });
|
||||
}
|
||||
|
||||
try {
|
||||
const loginRes = await fetch(
|
||||
`https://test.oriyo.tj/api/client/login?type=${type}&phone=${phoneNumber}&uid=${cardNumber}`,
|
||||
{
|
||||
method: 'GET',
|
||||
},
|
||||
);
|
||||
|
||||
if (!loginRes.ok) {
|
||||
return NextResponse.json(
|
||||
{ error: 'Error during login' },
|
||||
{ status: 400 },
|
||||
);
|
||||
}
|
||||
|
||||
const data = (await loginRes.json()) as LoginData;
|
||||
|
||||
const token = data.token;
|
||||
if (!token) {
|
||||
return NextResponse.json({ error: 'No auth token' }, { status: 401 });
|
||||
}
|
||||
|
||||
const response = NextResponse.json({ success: true });
|
||||
|
||||
response.cookies.set('token', token, {
|
||||
httpOnly: true,
|
||||
path: '/',
|
||||
maxAge: 2 * 60 * 60,
|
||||
secure: process.env.NODE_ENV === 'production',
|
||||
});
|
||||
|
||||
return response;
|
||||
} catch (error) {
|
||||
console.error('login error:', error);
|
||||
return NextResponse.json({ error: 'Server error' }, { status: 500 });
|
||||
}
|
||||
};
|
||||
304
src/app/charity/page.tsx
Normal file
304
src/app/charity/page.tsx
Normal file
@ -0,0 +1,304 @@
|
||||
import {
|
||||
Calendar,
|
||||
CheckCircle,
|
||||
Heart,
|
||||
Landmark,
|
||||
MapPin,
|
||||
Users,
|
||||
} from 'lucide-react';
|
||||
import Image from 'next/image';
|
||||
|
||||
import { Button } from '@/shared/shadcn-ui/button';
|
||||
import {
|
||||
Card,
|
||||
CardContent,
|
||||
CardFooter,
|
||||
CardHeader,
|
||||
CardTitle,
|
||||
} from '@/shared/shadcn-ui/card';
|
||||
|
||||
import { CtaSection } from '@/widgets/cta-section';
|
||||
|
||||
export const metadata = {
|
||||
title: 'Благотворительность | GasNetwork - Сеть заправок в Таджикистане',
|
||||
description:
|
||||
'Благотворительные проекты и инициативы GasNetwork. Мы помогаем обществу и заботимся о будущем.',
|
||||
};
|
||||
|
||||
export default function CharityPage() {
|
||||
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='Благотворительный фонд GasNetwork'
|
||||
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'>
|
||||
Благотворительный фонд GasNetwork
|
||||
</h1>
|
||||
<p className='text-xl text-gray-200'>
|
||||
Мы верим, что бизнес должен быть социально ответственным.
|
||||
Наш фонд поддерживает образование, здравоохранение и
|
||||
экологические инициативы в Таджикистане.
|
||||
</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'>
|
||||
Наша миссия
|
||||
</h2>
|
||||
<p className='mb-6 text-gray-600'>
|
||||
Благотворительный фонд GasNetwork был создан в 2020 году с
|
||||
целью поддержки социально значимых проектов в Таджикистане. Мы
|
||||
стремимся внести свой вклад в развитие общества и помочь тем,
|
||||
кто в этом нуждается.
|
||||
</p>
|
||||
<p className='mb-6 text-gray-600'>
|
||||
Наша миссия — создавать возможности для улучшения жизни людей
|
||||
через образование, здравоохранение, экологические инициативы и
|
||||
поддержку уязвимых групп населения.
|
||||
</p>
|
||||
|
||||
<div className='space-y-4'>
|
||||
<div 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'>Прозрачность</h3>
|
||||
<p className='text-gray-600'>
|
||||
Мы публикуем ежегодные отчеты о всех наших проектах и
|
||||
расходах, обеспечивая полную прозрачность нашей
|
||||
деятельности.
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
<div 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'>Эффективность</h3>
|
||||
<p className='text-gray-600'>
|
||||
Мы тщательно выбираем проекты, которые могут принести
|
||||
максимальную пользу обществу и имеют долгосрочное
|
||||
влияние.
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
<div 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'>Сотрудничество</h3>
|
||||
<p className='text-gray-600'>
|
||||
Мы сотрудничаем с местными и международными
|
||||
организациями для достижения наибольшего эффекта от
|
||||
наших инициатив.
|
||||
</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='Наша миссия'
|
||||
fill
|
||||
className='object-cover'
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
{/* Key Figures */}
|
||||
<section className='bg-red-600 py-16 text-white'>
|
||||
<div className='container mx-auto'>
|
||||
<div className='mb-12 text-center'>
|
||||
<h2 className='mb-4 text-3xl font-bold tracking-tight sm:text-4xl'>
|
||||
Наш вклад в цифрах
|
||||
</h2>
|
||||
<p className='mx-auto max-w-2xl text-white/80'>
|
||||
За время существования нашего фонда мы достигли значительных
|
||||
результатов
|
||||
</p>
|
||||
</div>
|
||||
<div className='grid grid-cols-1 gap-8 text-center md:grid-cols-3'>
|
||||
<div className='space-y-2'>
|
||||
<h3 className='text-4xl font-bold'>15+</h3>
|
||||
<p className='text-white/80'>Реализованных проектов</p>
|
||||
</div>
|
||||
<div className='space-y-2'>
|
||||
<h3 className='text-4xl font-bold'>1.2M</h3>
|
||||
<p className='text-white/80'>Сомони пожертвований</p>
|
||||
</div>
|
||||
<div className='space-y-2'>
|
||||
<h3 className='text-4xl font-bold'>5000+</h3>
|
||||
<p className='text-white/80'>Людей получили помощь</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
{/* Upcoming Events */}
|
||||
<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'>
|
||||
Предстоящие мероприятия
|
||||
</h2>
|
||||
<p className='mx-auto max-w-2xl text-gray-600'>
|
||||
Присоединяйтесь к нашим благотворительным мероприятиям и внесите
|
||||
свой вклад в общее дело
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<div className='grid gap-6 md:grid-cols-3'>
|
||||
{[
|
||||
{
|
||||
title: 'Благотворительный марафон',
|
||||
description:
|
||||
'Ежегодный благотворительный марафон в поддержку детей с особыми потребностями.',
|
||||
date: '15 июня 2023',
|
||||
location: 'Парк Рудаки, Душанбе',
|
||||
image: '/placeholder.svg?height=200&width=300&text=Марафон',
|
||||
},
|
||||
{
|
||||
title: 'Экологическая акция',
|
||||
description:
|
||||
'Очистка берегов реки Варзоб от мусора и посадка деревьев.',
|
||||
date: '22 июля 2023',
|
||||
location: 'Река Варзоб, Душанбе',
|
||||
image:
|
||||
'/placeholder.svg?height=200&width=300&text=Экологическая+акция',
|
||||
},
|
||||
{
|
||||
title: 'Сбор школьных принадлежностей',
|
||||
description:
|
||||
'Сбор школьных принадлежностей для детей из малообеспеченных семей к новому учебному году.',
|
||||
date: '1-20 августа 2023',
|
||||
location: 'Все заправки GasNetwork',
|
||||
image:
|
||||
'/placeholder.svg?height=200&width=300&text=Школьные+принадлежности',
|
||||
},
|
||||
].map((event, index) => (
|
||||
<Card key={index} className='overflow-hidden'>
|
||||
<div className='relative h-48 w-full'>
|
||||
<Image
|
||||
src={event.image || '/placeholder.svg'}
|
||||
alt={event.title}
|
||||
fill
|
||||
className='object-cover'
|
||||
/>
|
||||
</div>
|
||||
<CardHeader>
|
||||
<CardTitle>{event.title}</CardTitle>
|
||||
</CardHeader>
|
||||
<CardContent className='space-y-4'>
|
||||
<p className='text-gray-600'>{event.description}</p>
|
||||
<div className='flex items-center text-sm text-gray-500'>
|
||||
<Calendar className='mr-2 h-4 w-4' />
|
||||
{event.date}
|
||||
</div>
|
||||
<div className='flex items-center text-sm text-gray-500'>
|
||||
<MapPin className='mr-2 h-4 w-4' />
|
||||
{event.location}
|
||||
</div>
|
||||
</CardContent>
|
||||
<CardFooter>
|
||||
<Button className='w-full bg-red-600 hover:bg-red-700'>
|
||||
Принять участие
|
||||
</Button>
|
||||
</CardFooter>
|
||||
</Card>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
{/* How to Help */}
|
||||
<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'>
|
||||
Как вы можете помочь
|
||||
</h2>
|
||||
<p className='mx-auto max-w-2xl text-gray-600'>
|
||||
Есть много способов внести свой вклад в наши благотворительные
|
||||
инициативы
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<div className='grid gap-3 md:grid-cols-2 md:gap-6 lg:grid-cols-4'>
|
||||
{[
|
||||
{
|
||||
title: 'Сделать пожертвование',
|
||||
description:
|
||||
'Ваше пожертвование поможет нам реализовать больше проектов и помочь большему количеству людей.',
|
||||
icon: <Landmark className='h-10 w-10 text-red-600' />,
|
||||
},
|
||||
{
|
||||
title: 'Стать волонтером',
|
||||
description:
|
||||
'Присоединяйтесь к нашей команде волонтеров и помогайте нам в реализации благотворительных проектов.',
|
||||
icon: <Users className='h-10 w-10 text-red-600' />,
|
||||
},
|
||||
{
|
||||
title: 'Участвовать в мероприятиях',
|
||||
description:
|
||||
'Принимайте участие в наших благотворительных мероприятиях и акциях.',
|
||||
icon: <Calendar className='h-10 w-10 text-red-600' />,
|
||||
},
|
||||
{
|
||||
title: 'Распространять информацию',
|
||||
description:
|
||||
'Расскажите о нашем фонде и его деятельности своим друзьям и знакомым.',
|
||||
icon: <Heart className='h-10 w-10 text-red-600' />,
|
||||
},
|
||||
].map((item, index) => (
|
||||
<Card key={index} className='text-center'>
|
||||
<CardHeader>
|
||||
<div className='mb-4 flex justify-center'>{item.icon}</div>
|
||||
<CardTitle className='break-words hyphens-auto'>
|
||||
{item.title}
|
||||
</CardTitle>
|
||||
</CardHeader>
|
||||
<CardContent>
|
||||
<p className='text-gray-600'>{item.description}</p>
|
||||
</CardContent>
|
||||
</Card>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
<CtaSection />
|
||||
</main>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
5
src/app/clients/certificates/page.tsx
Normal file
5
src/app/clients/certificates/page.tsx
Normal file
@ -0,0 +1,5 @@
|
||||
import { CertificatesPage } from '@/pages-templates/clients/certificates';
|
||||
|
||||
export default function Certificates() {
|
||||
return <CertificatesPage />;
|
||||
}
|
||||
@ -1,285 +1,5 @@
|
||||
import { Check, Percent } from 'lucide-react';
|
||||
import Image from 'next/image';
|
||||
import { LoyaltyPage } from '@/pages-templates/clients/loyalty';
|
||||
|
||||
import { Card, CardContent } from '@/shared/shadcn-ui/card';
|
||||
|
||||
import { CtaSection } from '@/widgets/cta-section';
|
||||
|
||||
export const metadata = {
|
||||
title: 'Программа лояльности | GasNetwork - Сеть заправок в Таджикистане',
|
||||
description:
|
||||
'Программа лояльности GasNetwork: накапливайте баллы и получайте скидки на топливо и услуги.',
|
||||
};
|
||||
|
||||
export default function LoyaltyPage() {
|
||||
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=400&width=1920&text=Программа+лояльности'
|
||||
alt='Программа лояльности'
|
||||
width={1920}
|
||||
height={400}
|
||||
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-4 text-white'>
|
||||
<h1 className='text-4xl font-bold tracking-tight sm:text-5xl md:text-6xl'>
|
||||
Программа лояльности
|
||||
</h1>
|
||||
<p className='text-lg text-gray-200'>
|
||||
Накапливайте баллы и получайте скидки на топливо и услуги
|
||||
нашей сети
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
{/* Program Overview */}
|
||||
<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'>
|
||||
<Percent className='h-6 w-6 text-red-600' />
|
||||
</div>
|
||||
<h2 className='mb-6 text-3xl font-bold tracking-tight sm:text-4xl'>
|
||||
О программе лояльности
|
||||
</h2>
|
||||
<p className='mb-6 text-gray-600'>
|
||||
Программа лояльности GasNetwork — это возможность получать
|
||||
баллы за каждую покупку топлива и услуг на наших заправочных
|
||||
станциях. Накопленные баллы можно обменять на скидки, подарки
|
||||
или дополнительные услуги.
|
||||
</p>
|
||||
<p className='mb-6 text-gray-600'>
|
||||
Участие в программе абсолютно бесплатное. Вам нужно только
|
||||
получить карту лояльности в любой нашей заправочной станции
|
||||
или зарегистрироваться в мобильном приложении.
|
||||
</p>
|
||||
|
||||
<div className='space-y-4'>
|
||||
<div className='flex items-start'>
|
||||
<div className='mt-1 flex h-6 w-6 flex-shrink-0 items-center justify-center rounded-full bg-red-600'>
|
||||
<span className='text-xs text-white'>✓</span>
|
||||
</div>
|
||||
<div className='ml-3'>
|
||||
<h3 className='text-lg font-medium'>1 литр = 1 балл</h3>
|
||||
<p className='text-gray-600'>
|
||||
За каждый литр топлива вы получаете 1 балл
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
<div className='flex items-start'>
|
||||
<div className='mt-1 flex h-6 w-6 flex-shrink-0 items-center justify-center rounded-full bg-red-600'>
|
||||
<span className='text-xs text-white'>✓</span>
|
||||
</div>
|
||||
<div className='ml-3'>
|
||||
<h3 className='text-lg font-medium'>
|
||||
Дополнительные баллы
|
||||
</h3>
|
||||
<p className='text-gray-600'>
|
||||
За покупки в магазине и кафе на заправке
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
<div className='flex items-start'>
|
||||
<div className='mt-1 flex h-6 w-6 flex-shrink-0 items-center justify-center rounded-full bg-red-600'>
|
||||
<span className='text-xs text-white'>✓</span>
|
||||
</div>
|
||||
<div className='ml-3'>
|
||||
<h3 className='text-lg font-medium'>Специальные акции</h3>
|
||||
<p className='text-gray-600'>
|
||||
Удвоенные и утроенные баллы в праздничные дни
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div className='relative h-[400px] overflow-hidden rounded-xl shadow-xl'>
|
||||
<Image
|
||||
src='/placeholder.svg?height=400&width=600&text=Программа+лояльности'
|
||||
alt='Программа лояльности'
|
||||
fill
|
||||
className='object-cover'
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
{/* How It Works */}
|
||||
<section className='bg-gray-50 py-16'>
|
||||
<div className='container mx-auto'>
|
||||
<div className='mb-12 text-center'>
|
||||
<h2 className='mb-4 text-3xl font-bold tracking-tight sm:text-4xl'>
|
||||
Как это работает
|
||||
</h2>
|
||||
<p className='mx-auto max-w-2xl text-gray-600'>
|
||||
Простые шаги для участия в программе лояльности GasNetwork
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<div className='grid gap-8 md:grid-cols-4'>
|
||||
<div className='text-center'>
|
||||
<div className='mx-auto mb-4 flex h-16 w-16 items-center justify-center rounded-full bg-red-600 text-2xl font-bold text-white'>
|
||||
1
|
||||
</div>
|
||||
<h3 className='mb-2 text-xl font-bold'>Получите карту</h3>
|
||||
<p className='text-gray-600'>
|
||||
Получите карту лояльности на любой заправке GasNetwork или
|
||||
зарегистрируйтесь в мобильном приложении
|
||||
</p>
|
||||
</div>
|
||||
<div className='text-center'>
|
||||
<div className='mx-auto mb-4 flex h-16 w-16 items-center justify-center rounded-full bg-red-600 text-2xl font-bold text-white'>
|
||||
2
|
||||
</div>
|
||||
<h3 className='mb-2 text-xl font-bold'>Заправляйтесь</h3>
|
||||
<p className='text-gray-600'>
|
||||
Используйте карту при каждой заправке и покупке в магазинах на
|
||||
наших АЗС
|
||||
</p>
|
||||
</div>
|
||||
<div className='text-center'>
|
||||
<div className='mx-auto mb-4 flex h-16 w-16 items-center justify-center rounded-full bg-red-600 text-2xl font-bold text-white'>
|
||||
3
|
||||
</div>
|
||||
<h3 className='mb-2 text-xl font-bold'>Накапливайте баллы</h3>
|
||||
<p className='text-gray-600'>
|
||||
Получайте баллы за каждую покупку и следите за их накоплением
|
||||
в приложении
|
||||
</p>
|
||||
</div>
|
||||
<div className='text-center'>
|
||||
<div className='mx-auto mb-4 flex h-16 w-16 items-center justify-center rounded-full bg-red-600 text-2xl font-bold text-white'>
|
||||
4
|
||||
</div>
|
||||
<h3 className='mb-2 text-xl font-bold'>Получайте выгоду</h3>
|
||||
<p className='text-gray-600'>
|
||||
Обменивайте накопленные баллы на скидки, подарки или
|
||||
дополнительные услуги
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
{/* Loyalty Levels */}
|
||||
<section className='py-16'>
|
||||
<div className='container mx-auto'>
|
||||
<div className='mb-12 text-center'>
|
||||
<h2 className='mb-4 text-3xl font-bold tracking-tight sm:text-4xl'>
|
||||
Уровни лояльности
|
||||
</h2>
|
||||
<p className='mx-auto max-w-2xl text-gray-600'>
|
||||
Чем больше вы заправляетесь, тем больше преимуществ получаете
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<div className='grid gap-8 md:grid-cols-3'>
|
||||
<Card className='overflow-hidden border-t-4 border-t-gray-400 transition-all hover:shadow-lg'>
|
||||
<CardContent className='p-6'>
|
||||
<h3 className='mb-4 text-center text-2xl font-bold'>
|
||||
Стандарт
|
||||
</h3>
|
||||
<div className='mb-6 text-center'>
|
||||
<span className='text-4xl font-bold'>1%</span>
|
||||
<p className='text-sm text-gray-600'>возврат баллами</p>
|
||||
</div>
|
||||
<ul className='space-y-2'>
|
||||
<li className='flex items-center'>
|
||||
<Check className='mr-2 h-5 w-5 text-green-500' />
|
||||
<span>1 балл за каждый литр топлива</span>
|
||||
</li>
|
||||
<li className='flex items-center'>
|
||||
<Check className='mr-2 h-5 w-5 text-green-500' />
|
||||
<span>Участие в акциях</span>
|
||||
</li>
|
||||
<li className='flex items-center'>
|
||||
<Check className='mr-2 h-5 w-5 text-green-500' />
|
||||
<span>Доступ к мобильному приложению</span>
|
||||
</li>
|
||||
</ul>
|
||||
</CardContent>
|
||||
</Card>
|
||||
|
||||
<Card className='overflow-hidden border-t-4 border-t-yellow-500 transition-all hover:shadow-lg'>
|
||||
<CardContent className='p-6'>
|
||||
<h3 className='mb-4 text-center text-2xl font-bold'>
|
||||
Золотой
|
||||
</h3>
|
||||
<div className='mb-6 text-center'>
|
||||
<span className='text-4xl font-bold'>2%</span>
|
||||
<p className='text-sm text-gray-600'>возврат баллами</p>
|
||||
</div>
|
||||
<ul className='space-y-2'>
|
||||
<li className='flex items-center'>
|
||||
<Check className='mr-2 h-5 w-5 text-green-500' />
|
||||
<span>2 балла за каждый литр топлива</span>
|
||||
</li>
|
||||
<li className='flex items-center'>
|
||||
<Check className='mr-2 h-5 w-5 text-green-500' />
|
||||
<span>Скидка 5% в кафе на заправках</span>
|
||||
</li>
|
||||
<li className='flex items-center'>
|
||||
<Check className='mr-2 h-5 w-5 text-green-500' />
|
||||
<span>Приоритетное обслуживание</span>
|
||||
</li>
|
||||
<li className='flex items-center'>
|
||||
<Check className='mr-2 h-5 w-5 text-green-500' />
|
||||
<span>Эксклюзивные акции</span>
|
||||
</li>
|
||||
</ul>
|
||||
</CardContent>
|
||||
</Card>
|
||||
|
||||
<Card className='overflow-hidden border-t-4 border-t-red-600 transition-all hover:shadow-lg'>
|
||||
<CardContent className='p-6'>
|
||||
<h3 className='mb-4 text-center text-2xl font-bold'>
|
||||
Платиновый
|
||||
</h3>
|
||||
<div className='mb-6 text-center'>
|
||||
<span className='text-4xl font-bold'>3%</span>
|
||||
<p className='text-sm text-gray-600'>возврат баллами</p>
|
||||
</div>
|
||||
<ul className='space-y-2'>
|
||||
<li className='flex items-center'>
|
||||
<Check className='mr-2 h-5 w-5 text-green-500' />
|
||||
<span>3 балла за каждый литр топлива</span>
|
||||
</li>
|
||||
<li className='flex items-center'>
|
||||
<Check className='mr-2 h-5 w-5 text-green-500' />
|
||||
<span>Скидка 10% в кафе на заправках</span>
|
||||
</li>
|
||||
<li className='flex items-center'>
|
||||
<Check className='mr-2 h-5 w-5 text-green-500' />
|
||||
<span>Бесплатная мойка раз в месяц</span>
|
||||
</li>
|
||||
<li className='flex items-center'>
|
||||
<Check className='mr-2 h-5 w-5 text-green-500' />
|
||||
<span>Персональный менеджер</span>
|
||||
</li>
|
||||
<li className='flex items-center'>
|
||||
<Check className='mr-2 h-5 w-5 text-green-500' />
|
||||
<span>VIP-обслуживание</span>
|
||||
</li>
|
||||
</ul>
|
||||
</CardContent>
|
||||
</Card>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<CtaSection />
|
||||
</main>
|
||||
</div>
|
||||
);
|
||||
export default function Loyalty() {
|
||||
return <LoyaltyPage />;
|
||||
}
|
||||
|
||||
@ -1,254 +1,5 @@
|
||||
import { ArrowRight, CreditCard, Gift, Percent, Wallet } from 'lucide-react';
|
||||
import Image from 'next/image';
|
||||
import Link from 'next/link';
|
||||
import { ClientsPage } from "@/pages-templates/clients"
|
||||
|
||||
import { Button } from '@/shared/shadcn-ui/button';
|
||||
import {
|
||||
Card,
|
||||
CardContent,
|
||||
CardDescription,
|
||||
CardFooter,
|
||||
CardHeader,
|
||||
CardTitle,
|
||||
} from '@/shared/shadcn-ui/card';
|
||||
|
||||
import { CtaSection } from '@/widgets/cta-section';
|
||||
|
||||
export const metadata = {
|
||||
title: 'Клиентам | GasNetwork - Сеть заправок в Таджикистане',
|
||||
description:
|
||||
'Информация для клиентов: программа лояльности, топливные карты, сертификаты и способы оплаты.',
|
||||
};
|
||||
|
||||
export default function ClientsPage() {
|
||||
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=400&width=1920&text=Для+наших+клиентов'
|
||||
alt='Для наших клиентов'
|
||||
width={1920}
|
||||
height={400}
|
||||
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-4 text-white'>
|
||||
<h1 className='text-4xl font-bold tracking-tight sm:text-5xl md:text-6xl'>
|
||||
Для наших клиентов
|
||||
</h1>
|
||||
<p className='text-lg text-gray-200'>
|
||||
Специальные предложения, программы лояльности и удобные
|
||||
способы оплаты для наших клиентов
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
{/* Services Overview */}
|
||||
<section className='py-16'>
|
||||
<div className='container mx-auto'>
|
||||
<div className='mb-12 text-center'>
|
||||
<h2 className='mb-4 text-3xl font-bold tracking-tight sm:text-4xl'>
|
||||
Наши услуги для клиентов
|
||||
</h2>
|
||||
<p className='mx-auto max-w-2xl text-gray-600'>
|
||||
Мы стремимся сделать обслуживание на наших заправках максимально
|
||||
удобным и выгодным для вас
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<div className='grid gap-6 md:grid-cols-2 lg:grid-cols-4'>
|
||||
<Card className='overflow-hidden transition-all hover:shadow-lg'>
|
||||
<CardHeader className='pb-3'>
|
||||
<div className='mb-4 flex h-12 w-12 items-center justify-center rounded-full bg-red-100'>
|
||||
<Percent className='h-6 w-6 text-red-600' />
|
||||
</div>
|
||||
<CardTitle>Программа лояльности</CardTitle>
|
||||
<CardDescription>
|
||||
Накапливайте баллы и получайте скидки
|
||||
</CardDescription>
|
||||
</CardHeader>
|
||||
<CardContent className='text-sm text-gray-600'>
|
||||
<p>
|
||||
Наша программа лояльности позволяет накапливать баллы за
|
||||
каждую покупку и обменивать их на скидки и подарки.
|
||||
</p>
|
||||
</CardContent>
|
||||
<CardFooter>
|
||||
<Link href='/clients/loyalty'>
|
||||
<Button variant='outline' className='w-full'>
|
||||
Подробнее <ArrowRight className='ml-2 h-4 w-4' />
|
||||
</Button>
|
||||
</Link>
|
||||
</CardFooter>
|
||||
</Card>
|
||||
|
||||
<Card className='overflow-hidden transition-all hover:shadow-lg'>
|
||||
<CardHeader className='pb-3'>
|
||||
<div className='mb-4 flex h-12 w-12 items-center justify-center rounded-full bg-red-100'>
|
||||
<CreditCard className='h-6 w-6 text-red-600' />
|
||||
</div>
|
||||
<CardTitle>Топливная карта</CardTitle>
|
||||
<CardDescription>
|
||||
Удобный способ оплаты топлива
|
||||
</CardDescription>
|
||||
</CardHeader>
|
||||
<CardContent className='text-sm text-gray-600'>
|
||||
<p>
|
||||
Топливные карты для физических и юридических лиц.
|
||||
Контролируйте расходы и получайте дополнительные
|
||||
преимущества.
|
||||
</p>
|
||||
</CardContent>
|
||||
<CardFooter>
|
||||
<Link href='/clients/fuel-card'>
|
||||
<Button variant='outline' className='w-full'>
|
||||
Подробнее <ArrowRight className='ml-2 h-4 w-4' />
|
||||
</Button>
|
||||
</Link>
|
||||
</CardFooter>
|
||||
</Card>
|
||||
|
||||
<Card className='overflow-hidden transition-all hover:shadow-lg'>
|
||||
<CardHeader className='pb-3'>
|
||||
<div className='mb-4 flex h-12 w-12 items-center justify-center rounded-full bg-red-100'>
|
||||
<Gift className='h-6 w-6 text-red-600' />
|
||||
</div>
|
||||
<CardTitle>Сертификаты</CardTitle>
|
||||
<CardDescription>
|
||||
Подарочные сертификаты на топливо и услуги
|
||||
</CardDescription>
|
||||
</CardHeader>
|
||||
<CardContent className='text-sm text-gray-600'>
|
||||
<p>
|
||||
Подарите близким или партнерам сертификат на топливо или
|
||||
услуги нашей сети. Идеальный подарок для автовладельцев.
|
||||
</p>
|
||||
</CardContent>
|
||||
<CardFooter>
|
||||
<Link href='/clients/certificates'>
|
||||
<Button variant='outline' className='w-full'>
|
||||
Подробнее <ArrowRight className='ml-2 h-4 w-4' />
|
||||
</Button>
|
||||
</Link>
|
||||
</CardFooter>
|
||||
</Card>
|
||||
|
||||
<Card className='overflow-hidden transition-all hover:shadow-lg'>
|
||||
<CardHeader className='pb-3'>
|
||||
<div className='mb-4 flex h-12 w-12 items-center justify-center rounded-full bg-red-100'>
|
||||
<Wallet className='h-6 w-6 text-red-600' />
|
||||
</div>
|
||||
<CardTitle>Способы оплаты</CardTitle>
|
||||
<CardDescription>
|
||||
Различные способы оплаты на наших заправках
|
||||
</CardDescription>
|
||||
</CardHeader>
|
||||
<CardContent className='text-sm text-gray-600'>
|
||||
<p>
|
||||
Мы предлагаем различные способы оплаты: наличные, банковские
|
||||
карты, мобильные платежи и топливные карты.
|
||||
</p>
|
||||
</CardContent>
|
||||
<CardFooter>
|
||||
<Link href='/clients/payment'>
|
||||
<Button variant='outline' className='w-full'>
|
||||
Подробнее <ArrowRight className='ml-2 h-4 w-4' />
|
||||
</Button>
|
||||
</Link>
|
||||
</CardFooter>
|
||||
</Card>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
{/* Benefits Section */}
|
||||
<section className='bg-gray-50 py-16'>
|
||||
<div className='container mx-auto'>
|
||||
<div className='grid items-center gap-12 md:grid-cols-2'>
|
||||
<div className='order-2 md:order-1'>
|
||||
<div className='mb-4 inline-flex items-center justify-center rounded-full bg-red-100 p-2'>
|
||||
<Percent className='h-6 w-6 text-red-600' />
|
||||
</div>
|
||||
<h2 className='mb-6 text-3xl font-bold tracking-tight sm:text-4xl'>
|
||||
Преимущества для клиентов
|
||||
</h2>
|
||||
<p className='mb-6 text-gray-600'>
|
||||
Став клиентом GasNetwork, вы получаете множество преимуществ,
|
||||
которые делают заправку вашего автомобиля более выгодной и
|
||||
удобной.
|
||||
</p>
|
||||
|
||||
<div className='space-y-4'>
|
||||
<div className='flex items-start'>
|
||||
<div className='mt-1 flex h-6 w-6 flex-shrink-0 items-center justify-center rounded-full bg-red-600'>
|
||||
<span className='text-xs text-white'>✓</span>
|
||||
</div>
|
||||
<div className='ml-3'>
|
||||
<h3 className='text-lg font-medium'>Экономия</h3>
|
||||
<p className='text-gray-600'>
|
||||
Скидки и бонусы для постоянных клиентов
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
<div className='flex items-start'>
|
||||
<div className='mt-1 flex h-6 w-6 flex-shrink-0 items-center justify-center rounded-full bg-red-600'>
|
||||
<span className='text-xs text-white'>✓</span>
|
||||
</div>
|
||||
<div className='ml-3'>
|
||||
<h3 className='text-lg font-medium'>Удобство</h3>
|
||||
<p className='text-gray-600'>
|
||||
Быстрая оплата и обслуживание
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
<div className='flex items-start'>
|
||||
<div className='mt-1 flex h-6 w-6 flex-shrink-0 items-center justify-center rounded-full bg-red-600'>
|
||||
<span className='text-xs text-white'>✓</span>
|
||||
</div>
|
||||
<div className='ml-3'>
|
||||
<h3 className='text-lg font-medium'>Качество</h3>
|
||||
<p className='text-gray-600'>
|
||||
Гарантированно высокое качество топлива
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
<div className='flex items-start'>
|
||||
<div className='mt-1 flex h-6 w-6 flex-shrink-0 items-center justify-center rounded-full bg-red-600'>
|
||||
<span className='text-xs text-white'>✓</span>
|
||||
</div>
|
||||
<div className='ml-3'>
|
||||
<h3 className='text-lg font-medium'>
|
||||
Дополнительные услуги
|
||||
</h3>
|
||||
<p className='text-gray-600'>
|
||||
Кафе, магазины и другие услуги на наших заправках
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div className='relative order-1 h-[400px] overflow-hidden rounded-xl shadow-xl md:order-2'>
|
||||
<Image
|
||||
src='/placeholder.svg?height=400&width=600&text=Преимущества+для+клиентов'
|
||||
alt='Преимущества для клиентов'
|
||||
fill
|
||||
className='object-cover'
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<CtaSection />
|
||||
</main>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
export default function Clients() {
|
||||
return <ClientsPage />;
|
||||
}
|
||||
@ -1,5 +1,6 @@
|
||||
@import 'tailwindcss';
|
||||
@import "tw-animate-css";
|
||||
@import 'tw-animate-css';
|
||||
@import 'tailwindcss-animated';
|
||||
|
||||
@custom-variant dark (&:is(.dark *));
|
||||
|
||||
@ -49,7 +50,7 @@
|
||||
--card-foreground: oklch(0.145 0 0);
|
||||
--popover: oklch(1 0 0);
|
||||
--popover-foreground: oklch(0.145 0 0);
|
||||
--primary: oklch(0.577 0.245 27.325) ;
|
||||
--primary: oklch(0.577 0.245 27.325);
|
||||
--primary-foreground: oklch(0.985 0 0);
|
||||
--secondary: oklch(0.97 0 0);
|
||||
--secondary-foreground: oklch(0.205 0 0);
|
||||
|
||||
@ -22,7 +22,12 @@ export default function RootLayout({
|
||||
children: React.ReactNode;
|
||||
}>) {
|
||||
return (
|
||||
<html lang='en' suppressHydrationWarning>
|
||||
<html
|
||||
lang='en'
|
||||
suppressHydrationWarning
|
||||
className='scroll-smooth'
|
||||
style={{ scrollBehavior: 'smooth' }}
|
||||
>
|
||||
<body className={`${inter.className} antialiased`}>
|
||||
<Providers>
|
||||
<Header />
|
||||
|
||||
@ -1,93 +1,5 @@
|
||||
import { Building2, Fuel, User } from 'lucide-react';
|
||||
import Link from 'next/link';
|
||||
import LoginPage from '@/pages-templates/login';
|
||||
|
||||
import { LoginForm } from '@/features/auth/login-form';
|
||||
|
||||
import {
|
||||
Card,
|
||||
CardContent,
|
||||
CardDescription,
|
||||
CardHeader,
|
||||
CardTitle,
|
||||
} from '@/shared/shadcn-ui/card';
|
||||
import {
|
||||
Tabs,
|
||||
TabsContent,
|
||||
TabsList,
|
||||
TabsTrigger,
|
||||
} from '@/shared/shadcn-ui/tabs';
|
||||
|
||||
export default function LoginPage() {
|
||||
return (
|
||||
<div className='flex min-h-screen flex-col'>
|
||||
<main className='flex-1'>
|
||||
<div className='container max-w-6xl py-16'>
|
||||
<div className='mb-12 flex flex-col items-center text-center'>
|
||||
<div className='mb-4 inline-flex items-center justify-center rounded-full bg-red-100 p-2'>
|
||||
<Fuel className='h-6 w-6 text-red-600' />
|
||||
</div>
|
||||
<h1 className='mb-4 text-3xl font-bold tracking-tight sm:text-4xl'>
|
||||
Вход в личный кабинет
|
||||
</h1>
|
||||
<p className='max-w-2xl text-gray-600'>
|
||||
Войдите в личный кабинет, чтобы получить доступ к информации о
|
||||
ваших бонусах, истории операций и другим возможностям.
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<div className='mx-auto max-w-md'>
|
||||
<Tabs defaultValue='bonus' className='w-full'>
|
||||
<TabsList className='mb-8 grid w-full grid-cols-2'>
|
||||
<TabsTrigger value='bonus' className='text-base'>
|
||||
<User className='mr-2 h-4 w-4' /> Бонусный клиент
|
||||
</TabsTrigger>
|
||||
<TabsTrigger value='corporate' className='text-base'>
|
||||
<Building2 className='mr-2 h-4 w-4' /> Корпоративный клиент
|
||||
</TabsTrigger>
|
||||
</TabsList>
|
||||
|
||||
<TabsContent value='bonus'>
|
||||
<Card>
|
||||
<CardHeader>
|
||||
<CardTitle>Вход для бонусных клиентов</CardTitle>
|
||||
<CardDescription>
|
||||
Введите номер телефона и номер бонусной карты для входа в
|
||||
личный кабинет.
|
||||
</CardDescription>
|
||||
</CardHeader>
|
||||
<CardContent className='space-y-4'>
|
||||
<LoginForm />
|
||||
</CardContent>
|
||||
</Card>
|
||||
</TabsContent>
|
||||
|
||||
<TabsContent value='corporate'>
|
||||
<Card>
|
||||
<CardHeader>
|
||||
<CardTitle>Вход для корпоративных клиентов</CardTitle>
|
||||
<CardDescription>
|
||||
Введите номер телефона и номер корпоративной карты для
|
||||
входа в личный кабинет.
|
||||
</CardDescription>
|
||||
</CardHeader>
|
||||
<CardContent className='space-y-4'>
|
||||
<LoginForm />
|
||||
</CardContent>
|
||||
</Card>
|
||||
</TabsContent>
|
||||
</Tabs>
|
||||
|
||||
<div className='mt-8 text-center text-sm text-gray-500'>
|
||||
<p>
|
||||
Возникли проблемы со входом?{' '}
|
||||
<Link href='/contact' className='text-red-600 hover:underline'>
|
||||
Свяжитесь с нами
|
||||
</Link>
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</main>
|
||||
</div>
|
||||
);
|
||||
export default function Login() {
|
||||
return <LoginPage />;
|
||||
}
|
||||
|
||||
24
src/entities/auth/api/login.api.ts
Normal file
24
src/entities/auth/api/login.api.ts
Normal file
@ -0,0 +1,24 @@
|
||||
import { createApi, fetchBaseQuery } from '@reduxjs/toolkit/query/react';
|
||||
|
||||
import { LoginParams, LoginResponse } from '../model/contracts/login.contract';
|
||||
|
||||
export const loginAPI = createApi({
|
||||
baseQuery: fetchBaseQuery({ baseUrl: '/api' }),
|
||||
endpoints: (build) => ({
|
||||
login: build.query<LoginResponse, LoginParams>({
|
||||
query: (data) => {
|
||||
const params = new URLSearchParams({
|
||||
type: data.type,
|
||||
phoneNumber: data.phoneNumber,
|
||||
cardNumber: data.cardNumber,
|
||||
}).toString();
|
||||
|
||||
return {
|
||||
url: `/auth/login?${params}`,
|
||||
};
|
||||
},
|
||||
}),
|
||||
}),
|
||||
});
|
||||
|
||||
export const { useLazyLoginQuery } = loginAPI;
|
||||
1
src/entities/auth/index.ts
Normal file
1
src/entities/auth/index.ts
Normal file
@ -0,0 +1 @@
|
||||
export { useLazyLoginQuery } from './api/login.api';
|
||||
9
src/entities/auth/model/contracts/login.contract.ts
Normal file
9
src/entities/auth/model/contracts/login.contract.ts
Normal file
@ -0,0 +1,9 @@
|
||||
import { LoginFormData } from '@/features/auth/login-form/model/login-form.schema';
|
||||
|
||||
export interface LoginResponse {
|
||||
success: boolean;
|
||||
}
|
||||
|
||||
export interface LoginParams extends LoginFormData {
|
||||
type: 'bonus' | 'corporate';
|
||||
}
|
||||
7
src/entities/auth/model/types/index.ts
Normal file
7
src/entities/auth/model/types/index.ts
Normal file
@ -0,0 +1,7 @@
|
||||
export interface LoginData {
|
||||
card_id: number;
|
||||
created_at: string;
|
||||
phone: string;
|
||||
token: string;
|
||||
uid: string;
|
||||
}
|
||||
@ -6,13 +6,15 @@ export const loginFormSchema = z.object({
|
||||
.trim()
|
||||
.regex(/^[0-9+\-() ]*$/, {
|
||||
message:
|
||||
'Phone number can only contain numbers, spaces, and the following symbols: + - ( )',
|
||||
'Номер телефона может содержать только цифры, пробелы и следующие символы: + - ( )',
|
||||
})
|
||||
.refine((val) => !val || val.length >= 5, {
|
||||
message:
|
||||
'Phone number is too short. Please enter a complete phone number',
|
||||
}),
|
||||
cardNumber: z.string().min(16).trim(),
|
||||
.min(5, 'Номер телефона слишком короткий. Введите полный номер телефона')
|
||||
.max(13, 'Номер телефона не может быть длиннее 13 символов'),
|
||||
cardNumber: z
|
||||
.string()
|
||||
.min(6, 'Неверный номер карты. Введите полный номер карты')
|
||||
.max(20, 'Номер карты не может быть длиннее 20 символов')
|
||||
.trim(),
|
||||
});
|
||||
|
||||
export type LoginFormData = z.infer<typeof loginFormSchema>;
|
||||
|
||||
@ -5,7 +5,10 @@ import { useRouter } from 'next/navigation';
|
||||
import { useForm } from 'react-hook-form';
|
||||
import { toast } from 'sonner';
|
||||
|
||||
import { Button } from '@/shared/shadcn-ui/button';
|
||||
import { useLazyLoginQuery } from '@/entities/auth';
|
||||
|
||||
import { SubmitButton } from '@/shared/components/submit-button';
|
||||
import { useLanguage } from '@/shared/language';
|
||||
import {
|
||||
Form,
|
||||
FormControl,
|
||||
@ -19,12 +22,14 @@ import { Input } from '@/shared/shadcn-ui/input';
|
||||
import { LoginFormData, loginFormSchema } from '../model/login-form.schema';
|
||||
|
||||
interface LoginFormProps {
|
||||
// onSubmit: (data: any) => Promise<void>;
|
||||
type: 'bonus' | 'corporate';
|
||||
}
|
||||
|
||||
export const LoginForm = ({}: LoginFormProps) => {
|
||||
export const LoginForm = ({ type }: LoginFormProps) => {
|
||||
const { t } = useLanguage();
|
||||
|
||||
const router = useRouter();
|
||||
// const [login, results] = useLoginMutation();
|
||||
const [login, { isLoading: isLoginLoading }] = useLazyLoginQuery();
|
||||
|
||||
const form = useForm<LoginFormData>({
|
||||
resolver: zodResolver(loginFormSchema),
|
||||
@ -35,34 +40,30 @@ export const LoginForm = ({}: LoginFormProps) => {
|
||||
});
|
||||
|
||||
const onSubmit = async (data: LoginFormData) => {
|
||||
// const response = await login(data).unwrap();
|
||||
// const user = response.data;
|
||||
// dispatch(
|
||||
// setCredentials({
|
||||
// user: {
|
||||
// accessToken: user.accessToken,
|
||||
// affiliateId: user.affiliateId,
|
||||
// email: user.email,
|
||||
// id: user.id,
|
||||
// role: user.role,
|
||||
// username: user.username,
|
||||
// },
|
||||
// }),
|
||||
// );
|
||||
toast.success('Logged in successfully!');
|
||||
try {
|
||||
await login({ ...data, type }).unwrap();
|
||||
|
||||
router.push('/customer-dashboard');
|
||||
toast.success('Logged in successfully!');
|
||||
router.push(
|
||||
type === 'bonus' ? '/customer-dashboard' : '/corporate-dashboard',
|
||||
);
|
||||
} catch (error) {
|
||||
toast.error('An error occured during login');
|
||||
}
|
||||
};
|
||||
|
||||
return (
|
||||
<Form {...form}>
|
||||
<form onSubmit={form.handleSubmit(onSubmit)} className='space-y-4'>
|
||||
<form
|
||||
onSubmit={form.handleSubmit(onSubmit)}
|
||||
className='mx-auto space-y-4'
|
||||
>
|
||||
<FormField
|
||||
control={form.control}
|
||||
name='phoneNumber'
|
||||
render={({ field }) => (
|
||||
<FormItem>
|
||||
<FormLabel>Номер телефона</FormLabel>
|
||||
<FormItem className='flex flex-col'>
|
||||
<FormLabel>{t('auth.phoneNumber')}</FormLabel>
|
||||
<FormControl>
|
||||
<Input
|
||||
type='tel'
|
||||
@ -79,8 +80,8 @@ export const LoginForm = ({}: LoginFormProps) => {
|
||||
control={form.control}
|
||||
name='cardNumber'
|
||||
render={({ field }) => (
|
||||
<FormItem>
|
||||
<FormLabel>Номер карты</FormLabel>
|
||||
<FormItem className='flex flex-col'>
|
||||
<FormLabel>{t('auth.cardNumber')}</FormLabel>
|
||||
<FormControl>
|
||||
<Input
|
||||
type='text'
|
||||
@ -92,16 +93,12 @@ export const LoginForm = ({}: LoginFormProps) => {
|
||||
</FormItem>
|
||||
)}
|
||||
/>
|
||||
<Button
|
||||
// isLoading={results.isLoading}
|
||||
// title='Login'
|
||||
<SubmitButton
|
||||
isLoading={isLoginLoading}
|
||||
type='submit'
|
||||
className='w-full'
|
||||
// variant={'default'}
|
||||
// disabled={loading}
|
||||
>
|
||||
Войти
|
||||
</Button>
|
||||
disabled={isLoginLoading}
|
||||
/>
|
||||
</form>
|
||||
</Form>
|
||||
);
|
||||
|
||||
@ -1,10 +1,231 @@
|
||||
'use client';
|
||||
|
||||
import { MapPin } from 'lucide-react';
|
||||
import { useEffect, useRef } from 'react';
|
||||
import {
|
||||
Check,
|
||||
ChevronLeft,
|
||||
ChevronRight,
|
||||
Filter,
|
||||
List,
|
||||
MapPin,
|
||||
} from 'lucide-react';
|
||||
import { useEffect, useRef, useState } from 'react';
|
||||
|
||||
import { useLanguage } from '@/shared/language';
|
||||
import { Badge } from '@/shared/shadcn-ui/badge';
|
||||
import { Button } from '@/shared/shadcn-ui/button';
|
||||
import {
|
||||
Tabs,
|
||||
TabsContent,
|
||||
TabsList,
|
||||
TabsTrigger,
|
||||
} from '@/shared/shadcn-ui/tabs';
|
||||
|
||||
// Sample data for gas stations
|
||||
const stations = [
|
||||
{
|
||||
id: 1,
|
||||
name: 'АЗС Душанбе-Центр',
|
||||
address: 'ул. Рудаки 150, Душанбе',
|
||||
city: 'Душанбе',
|
||||
coordinates: { x: 0.2, y: 0.3 },
|
||||
services: ['ДТ', 'АИ-92', 'АИ-95', 'Z-100 Power', 'Минимаркет', 'Туалет'],
|
||||
},
|
||||
{
|
||||
id: 2,
|
||||
name: 'АЗС Худжанд',
|
||||
address: 'ул. Ленина 45, Худжанд',
|
||||
city: 'Худжанд',
|
||||
coordinates: { x: 0.5, y: 0.2 },
|
||||
services: [
|
||||
'ДТ',
|
||||
'АИ-92',
|
||||
'АИ-95',
|
||||
'Пропан',
|
||||
'Минимаркет',
|
||||
'Автомойка',
|
||||
'Туалет',
|
||||
],
|
||||
},
|
||||
{
|
||||
id: 3,
|
||||
name: 'АЗС Куляб',
|
||||
address: 'ул. Сомони 78, Куляб',
|
||||
city: 'Куляб',
|
||||
coordinates: { x: 0.7, y: 0.4 },
|
||||
services: ['ДТ', 'АИ-92', 'Пропан', 'Туалет'],
|
||||
},
|
||||
{
|
||||
id: 4,
|
||||
name: 'АЗС Бохтар',
|
||||
address: 'ул. Айни 23, Бохтар',
|
||||
city: 'Бохтар',
|
||||
coordinates: { x: 0.3, y: 0.6 },
|
||||
services: [
|
||||
'ДТ',
|
||||
'АИ-92',
|
||||
'АИ-95',
|
||||
'Z-100 Power',
|
||||
'Минимаркет',
|
||||
'Зарядная станция',
|
||||
'Туалет',
|
||||
],
|
||||
},
|
||||
{
|
||||
id: 5,
|
||||
name: 'АЗС Хорог',
|
||||
address: 'ул. Горная 12, Хорог',
|
||||
city: 'Хорог',
|
||||
coordinates: { x: 0.6, y: 0.7 },
|
||||
services: ['ДТ', 'АИ-92', 'Автомойка', 'Туалет'],
|
||||
},
|
||||
{
|
||||
id: 6,
|
||||
name: 'АЗС Истаравшан',
|
||||
address: 'ул. Исмоили Сомони 34, Истаравшан',
|
||||
city: 'Истаравшан',
|
||||
coordinates: { x: 0.8, y: 0.8 },
|
||||
services: ['ДТ', 'АИ-92', 'АИ-95', 'Минимаркет', 'Туалет'],
|
||||
},
|
||||
{
|
||||
id: 7,
|
||||
name: 'АЗС Пенджикент',
|
||||
address: 'ул. Рудаки 56, Пенджикент',
|
||||
city: 'Пенджикент',
|
||||
coordinates: { x: 0.1, y: 0.9 },
|
||||
services: ['ДТ', 'АИ-92', 'АИ-95', 'Пропан', 'Минимаркет', 'Туалет'],
|
||||
},
|
||||
{
|
||||
id: 8,
|
||||
name: 'АЗС Душанбе-Запад',
|
||||
address: 'ул. Джами 23, Душанбе',
|
||||
city: 'Душанбе',
|
||||
coordinates: { x: 0.25, y: 0.35 },
|
||||
services: [
|
||||
'ДТ',
|
||||
'АИ-92',
|
||||
'АИ-95',
|
||||
'Z-100 Power',
|
||||
'Пропан',
|
||||
'Минимаркет',
|
||||
'Автомойка',
|
||||
'Туалет',
|
||||
],
|
||||
},
|
||||
{
|
||||
id: 9,
|
||||
name: 'АЗС Душанбе-Восток',
|
||||
address: 'ул. Айни 78, Душанбе',
|
||||
city: 'Душанбе',
|
||||
coordinates: { x: 0.15, y: 0.25 },
|
||||
services: [
|
||||
'ДТ',
|
||||
'АИ-92',
|
||||
'АИ-95',
|
||||
'Зарядная станция',
|
||||
'Минимаркет',
|
||||
'Туалет',
|
||||
],
|
||||
},
|
||||
{
|
||||
id: 10,
|
||||
name: 'АЗС Гиссар',
|
||||
address: 'ул. Центральная 12, Гиссар',
|
||||
city: 'Гиссар',
|
||||
coordinates: { x: 0.4, y: 0.4 },
|
||||
services: ['ДТ', 'АИ-92', 'Пропан', 'Туалет'],
|
||||
},
|
||||
{
|
||||
id: 11,
|
||||
name: 'АЗС Вахдат',
|
||||
address: 'ул. Сомони 45, Вахдат',
|
||||
city: 'Вахдат',
|
||||
coordinates: { x: 0.55, y: 0.45 },
|
||||
services: ['ДТ', 'АИ-92', 'АИ-95', 'Минимаркет', 'Туалет'],
|
||||
},
|
||||
{
|
||||
id: 12,
|
||||
name: 'АЗС Турсунзаде',
|
||||
address: 'ул. Ленина 34, Турсунзаде',
|
||||
city: 'Турсунзаде',
|
||||
coordinates: { x: 0.65, y: 0.55 },
|
||||
services: ['ДТ', 'АИ-92', 'АИ-95', 'Z-100 Power', 'Автомойка', 'Туалет'],
|
||||
},
|
||||
];
|
||||
|
||||
// All available filters
|
||||
const allFilters = [
|
||||
'ДТ',
|
||||
'АИ-92',
|
||||
'АИ-95',
|
||||
'Z-100 Power',
|
||||
'Пропан',
|
||||
'Зарядная станция',
|
||||
'Минимаркет',
|
||||
'Автомойка',
|
||||
'Туалет',
|
||||
];
|
||||
|
||||
// Extract unique cities from stations
|
||||
const allCities = [...new Set(stations.map((station) => station.city))].sort();
|
||||
|
||||
export default function GasStationMap() {
|
||||
const { t } = useLanguage();
|
||||
const mapRef = useRef<HTMLDivElement>(null);
|
||||
const [activeFilters, setActiveFilters] = useState<string[]>([]);
|
||||
const [activeCities, setActiveCities] = useState<string[]>([]);
|
||||
const [filteredStations, setFilteredStations] = useState(stations);
|
||||
const [selectedStation, setSelectedStation] = useState<number | null>(null);
|
||||
const [isFilterOpen, setIsFilterOpen] = useState(false);
|
||||
const [isStationListOpen, setIsStationListOpen] = useState(false);
|
||||
const [activeFilterTab, setActiveFilterTab] = useState('cities');
|
||||
|
||||
// Toggle service filter
|
||||
const toggleFilter = (filter: string) => {
|
||||
if (activeFilters.includes(filter)) {
|
||||
setActiveFilters(activeFilters.filter((f) => f !== filter));
|
||||
} else {
|
||||
setActiveFilters([...activeFilters, filter]);
|
||||
}
|
||||
};
|
||||
|
||||
// Toggle city filter
|
||||
const toggleCity = (city: string) => {
|
||||
if (activeCities.includes(city)) {
|
||||
setActiveCities(activeCities.filter((c) => c !== city));
|
||||
} else {
|
||||
setActiveCities([...activeCities, city]);
|
||||
}
|
||||
};
|
||||
|
||||
// Select all cities
|
||||
const selectAllCities = () => {
|
||||
if (activeCities.length === allCities.length) {
|
||||
setActiveCities([]);
|
||||
} else {
|
||||
setActiveCities([...allCities]);
|
||||
}
|
||||
};
|
||||
|
||||
// Filter stations based on active filters and cities
|
||||
useEffect(() => {
|
||||
let filtered = stations;
|
||||
|
||||
// Filter by services
|
||||
if (activeFilters.length > 0) {
|
||||
filtered = filtered.filter((station) =>
|
||||
activeFilters.every((filter) => station.services.includes(filter)),
|
||||
);
|
||||
}
|
||||
|
||||
// Filter by cities
|
||||
if (activeCities.length > 0) {
|
||||
filtered = filtered.filter((station) =>
|
||||
activeCities.includes(station.city),
|
||||
);
|
||||
}
|
||||
|
||||
setFilteredStations(filtered);
|
||||
}, [activeFilters, activeCities]);
|
||||
|
||||
useEffect(() => {
|
||||
// This is a placeholder for a real map implementation
|
||||
@ -42,24 +263,15 @@ export default function GasStationMap() {
|
||||
}
|
||||
|
||||
// Draw gas station markers
|
||||
const stations = [
|
||||
{ x: 0.2, y: 0.3 },
|
||||
{ x: 0.5, y: 0.2 },
|
||||
{ x: 0.7, y: 0.4 },
|
||||
{ x: 0.3, y: 0.6 },
|
||||
{ x: 0.6, y: 0.7 },
|
||||
{ x: 0.8, y: 0.8 },
|
||||
{ x: 0.1, y: 0.9 },
|
||||
];
|
||||
|
||||
stations.forEach((station) => {
|
||||
filteredStations.forEach((station) => {
|
||||
const isSelected = selectedStation === station.id;
|
||||
// Draw marker
|
||||
ctx.fillStyle = '#ef4444';
|
||||
ctx.fillStyle = isSelected ? '#3b82f6' : '#ef4444';
|
||||
ctx.beginPath();
|
||||
ctx.arc(
|
||||
station.x * canvas.width,
|
||||
station.y * canvas.height,
|
||||
10,
|
||||
station.coordinates.x * canvas.width,
|
||||
station.coordinates.y * canvas.height,
|
||||
isSelected ? 12 : 10,
|
||||
0,
|
||||
2 * Math.PI,
|
||||
);
|
||||
@ -70,9 +282,9 @@ export default function GasStationMap() {
|
||||
ctx.lineWidth = 2;
|
||||
ctx.beginPath();
|
||||
ctx.arc(
|
||||
station.x * canvas.width,
|
||||
station.y * canvas.height,
|
||||
10,
|
||||
station.coordinates.x * canvas.width,
|
||||
station.coordinates.y * canvas.height,
|
||||
isSelected ? 12 : 10,
|
||||
0,
|
||||
2 * Math.PI,
|
||||
);
|
||||
@ -99,17 +311,257 @@ export default function GasStationMap() {
|
||||
}
|
||||
}
|
||||
};
|
||||
}, []);
|
||||
}, [filteredStations, selectedStation]);
|
||||
|
||||
return (
|
||||
<div className='relative h-full w-full'>
|
||||
<div ref={mapRef} className='h-full w-full'></div>
|
||||
<div className='absolute right-4 bottom-4 rounded-lg bg-white p-3 shadow-lg'>
|
||||
{/* Filter panel - slides from left */}
|
||||
<div
|
||||
className={`absolute top-0 bottom-0 left-0 z-20 transform bg-white shadow-lg transition-transform duration-300 ${
|
||||
isFilterOpen ? 'translate-x-0' : '-translate-x-full'
|
||||
}`}
|
||||
style={{ width: '300px' }}
|
||||
>
|
||||
<div className='flex items-center justify-between border-b border-gray-200 p-4'>
|
||||
<div className='flex items-center gap-2'>
|
||||
<Filter className='h-5 w-5 text-red-600' />
|
||||
<span className='font-medium'>{t('map.filters')}</span>
|
||||
</div>
|
||||
<Button
|
||||
variant='ghost'
|
||||
size='sm'
|
||||
onClick={() => setIsFilterOpen(false)}
|
||||
>
|
||||
<ChevronLeft className='h-5 w-5' />
|
||||
</Button>
|
||||
</div>
|
||||
|
||||
<div className='p-4'>
|
||||
<Tabs
|
||||
value={activeFilterTab}
|
||||
onValueChange={setActiveFilterTab}
|
||||
className='w-full'
|
||||
>
|
||||
<TabsList className='mb-4 grid w-full grid-cols-2'>
|
||||
<TabsTrigger value='cities'>{t('map.cities')}</TabsTrigger>
|
||||
<TabsTrigger value='services'>{t('map.services')}</TabsTrigger>
|
||||
</TabsList>
|
||||
|
||||
<TabsContent value='cities' className='mt-0'>
|
||||
<Button
|
||||
variant='outline'
|
||||
size='sm'
|
||||
className='mb-3 flex w-full items-center justify-between'
|
||||
onClick={selectAllCities}
|
||||
>
|
||||
<span>{t('map.allCities')}</span>
|
||||
{activeCities.length === allCities.length && (
|
||||
<Check className='h-4 w-4 text-green-600' />
|
||||
)}
|
||||
</Button>
|
||||
|
||||
<div className='flex max-h-[calc(100vh-250px)] flex-col gap-2 overflow-y-auto'>
|
||||
{allCities.map((city) => (
|
||||
<Button
|
||||
key={city}
|
||||
variant={
|
||||
activeCities.includes(city) ? 'default' : 'outline'
|
||||
}
|
||||
size='sm'
|
||||
className={`justify-between ${activeCities.includes(city) ? 'bg-red-600 hover:bg-red-700' : ''}`}
|
||||
onClick={() => toggleCity(city)}
|
||||
>
|
||||
<span>{city}</span>
|
||||
{activeCities.includes(city) && (
|
||||
<Check className='ml-2 h-4 w-4' />
|
||||
)}
|
||||
</Button>
|
||||
))}
|
||||
</div>
|
||||
|
||||
{activeCities.length > 0 && (
|
||||
<Button
|
||||
variant='link'
|
||||
className='mt-4 p-0 text-red-600'
|
||||
onClick={() => setActiveCities([])}
|
||||
>
|
||||
{t('common.buttons.resetFilters')}
|
||||
</Button>
|
||||
)}
|
||||
</TabsContent>
|
||||
|
||||
<TabsContent value='services' className='mt-0'>
|
||||
<div className='flex flex-wrap gap-2'>
|
||||
{allFilters.map((filter) => (
|
||||
<Button
|
||||
key={filter}
|
||||
variant={
|
||||
activeFilters.includes(filter) ? 'default' : 'outline'
|
||||
}
|
||||
size='sm'
|
||||
className={
|
||||
activeFilters.includes(filter)
|
||||
? 'bg-red-600 hover:bg-red-700'
|
||||
: ''
|
||||
}
|
||||
onClick={() => toggleFilter(filter)}
|
||||
>
|
||||
{filter}
|
||||
</Button>
|
||||
))}
|
||||
</div>
|
||||
{activeFilters.length > 0 && (
|
||||
<Button
|
||||
variant='link'
|
||||
className='mt-4 p-0 text-red-600'
|
||||
onClick={() => setActiveFilters([])}
|
||||
>
|
||||
{t('common.buttons.resetFilters')}
|
||||
</Button>
|
||||
)}
|
||||
</TabsContent>
|
||||
</Tabs>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Station list panel - slides from right */}
|
||||
<div
|
||||
className={`absolute top-0 right-0 bottom-0 z-20 transform bg-white shadow-lg transition-transform duration-300 ${
|
||||
isStationListOpen ? 'translate-x-0' : 'translate-x-full'
|
||||
}`}
|
||||
style={{ width: '350px' }}
|
||||
>
|
||||
<div className='flex items-center justify-between border-b border-gray-200 p-4'>
|
||||
<Button
|
||||
variant='ghost'
|
||||
size='sm'
|
||||
onClick={() => setIsStationListOpen(false)}
|
||||
>
|
||||
<ChevronRight className='h-5 w-5' />
|
||||
</Button>
|
||||
<div className='flex items-center gap-2'>
|
||||
<span className='font-medium'>{t('map.stationsList')}</span>
|
||||
<Badge>{filteredStations.length}</Badge>
|
||||
</div>
|
||||
</div>
|
||||
<div
|
||||
className='overflow-y-auto'
|
||||
style={{ height: 'calc(100% - 60px)' }}
|
||||
>
|
||||
{filteredStations.length > 0 ? (
|
||||
<div className='p-2'>
|
||||
{filteredStations.map((station) => (
|
||||
<div
|
||||
key={station.id}
|
||||
className={`mb-2 cursor-pointer rounded-lg border p-3 transition-colors ${
|
||||
selectedStation === station.id
|
||||
? 'border-blue-500 bg-blue-50'
|
||||
: 'border-gray-200 hover:bg-gray-50'
|
||||
}`}
|
||||
onClick={() => setSelectedStation(station.id)}
|
||||
>
|
||||
<div className='flex items-start justify-between'>
|
||||
<h4 className='font-medium'>{station.name}</h4>
|
||||
<ChevronRight className='h-4 w-4 text-gray-400' />
|
||||
</div>
|
||||
<p className='mb-2 text-sm text-gray-500'>
|
||||
{station.address}
|
||||
</p>
|
||||
<div className='flex flex-wrap gap-1'>
|
||||
<Badge className='mb-1 border-blue-200 bg-blue-100 text-blue-800'>
|
||||
{station.city}
|
||||
</Badge>
|
||||
{station.services.map((service) => (
|
||||
<Badge
|
||||
key={service}
|
||||
variant='outline'
|
||||
className={
|
||||
activeFilters.includes(service)
|
||||
? 'border-red-200 bg-red-100 text-red-800'
|
||||
: ''
|
||||
}
|
||||
>
|
||||
{service}
|
||||
</Badge>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
) : (
|
||||
<div className='p-4 text-center text-gray-500'>
|
||||
<p>{t('map.noStations')}</p>
|
||||
<div className='mt-2 flex justify-center gap-2'>
|
||||
{activeFilters.length > 0 && (
|
||||
<Button
|
||||
variant='link'
|
||||
className='text-red-600'
|
||||
onClick={() => setActiveFilters([])}
|
||||
>
|
||||
{t('common.buttons.resetFilters')}
|
||||
</Button>
|
||||
)}
|
||||
{activeCities.length > 0 && (
|
||||
<Button
|
||||
variant='link'
|
||||
className='text-red-600'
|
||||
onClick={() => setActiveCities([])}
|
||||
>
|
||||
{t('map.allCities')}
|
||||
</Button>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Map */}
|
||||
<div className='h-full w-full'>
|
||||
<div ref={mapRef} className='h-full w-full'></div>
|
||||
</div>
|
||||
|
||||
{/* Control buttons */}
|
||||
<div className='absolute top-4 left-4 z-10'>
|
||||
<Button
|
||||
variant='default'
|
||||
size='sm'
|
||||
className='border border-gray-200 bg-white text-gray-800 shadow-md hover:bg-gray-100'
|
||||
onClick={() => setIsFilterOpen(true)}
|
||||
>
|
||||
<Filter className='h-4 w-4 text-red-600 sm:mr-2' />
|
||||
<span className='hidden sm:flex'>{t('map.filters')}</span>
|
||||
{(activeFilters.length > 0 || activeCities.length > 0) && (
|
||||
<Badge className='ml-2 bg-red-600' variant='default'>
|
||||
{activeFilters.length + activeCities.length}
|
||||
</Badge>
|
||||
)}
|
||||
</Button>
|
||||
</div>
|
||||
|
||||
<div className='absolute top-4 right-4 z-10'>
|
||||
<Button
|
||||
variant='default'
|
||||
size='sm'
|
||||
className='border border-gray-200 bg-white text-gray-800 shadow-md hover:bg-gray-100'
|
||||
onClick={() => setIsStationListOpen(true)}
|
||||
>
|
||||
<List className='h-4 w-4 text-red-600 sm:mr-2' />
|
||||
<span className='hidden sm:flex'>{t('map.stationsList')}</span>
|
||||
<Badge className='ml-2 bg-red-600' variant='default'>
|
||||
{filteredStations.length}
|
||||
</Badge>
|
||||
</Button>
|
||||
</div>
|
||||
|
||||
<div className='absolute bottom-4 left-4 rounded-lg bg-white p-3 shadow-lg'>
|
||||
<div className='flex items-center gap-2 text-sm font-medium'>
|
||||
<MapPin className='h-5 w-5 text-red-600' />
|
||||
<span>Наши заправки</span>
|
||||
<span>{t('map.ourStations')}</span>
|
||||
</div>
|
||||
<p className='mt-1 text-xs text-gray-500'>Всего станций: 25</p>
|
||||
<p className='mt-1 text-xs text-gray-500'>
|
||||
{t('map.totalStations')}: {stations.length}
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
|
||||
325
src/pages-templates/about/index.tsx
Normal file
325
src/pages-templates/about/index.tsx
Normal file
@ -0,0 +1,325 @@
|
||||
'use client';
|
||||
|
||||
import {
|
||||
Award,
|
||||
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 { Button } from '@/shared/shadcn-ui/button';
|
||||
import { Card, CardContent } from '@/shared/shadcn-ui/card';
|
||||
|
||||
import { CompanyTimeline } from '@/widgets/about-page/company-timeline';
|
||||
import { StationGallery } from '@/widgets/about-page/station-gallery';
|
||||
import { CtaSection } from '@/widgets/cta-section';
|
||||
|
||||
export const metadata = {
|
||||
title: 'about.metadata.title',
|
||||
description: 'about.metadata.description',
|
||||
};
|
||||
|
||||
export default function AboutPage() {
|
||||
const { t } = useLanguage();
|
||||
|
||||
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=400&width=1920&text=Наша+История'
|
||||
alt={t('about.hero.imageAlt')}
|
||||
width={1920}
|
||||
height={400}
|
||||
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-4 text-white'>
|
||||
<h1 className='text-4xl font-bold tracking-tight sm:text-5xl md:text-6xl'>
|
||||
{t('about.hero.title')}
|
||||
</h1>
|
||||
<p className='text-lg text-gray-200'>
|
||||
{t('about.hero.subtitle')}
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
{/* Company Overview */}
|
||||
<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'>
|
||||
<Fuel className='h-6 w-6 text-red-600' />
|
||||
</div>
|
||||
<h2 className='mb-6 text-3xl font-bold tracking-tight sm:text-4xl'>
|
||||
{t('about.overview.title')}
|
||||
</h2>
|
||||
<p className='mb-6 text-gray-600'>
|
||||
{t('about.overview.description1')}
|
||||
</p>
|
||||
<p className='mb-6 text-gray-600'>
|
||||
{t('about.overview.description2')}
|
||||
</p>
|
||||
<p className='mb-6 text-gray-600'>
|
||||
{t('about.overview.description3')}
|
||||
</p>
|
||||
|
||||
<div className='mb-6 grid grid-cols-2 gap-4'>
|
||||
{[0, 1, 2, 3].map((index) => (
|
||||
<div key={index} className='flex items-start'>
|
||||
<div className='mt-1 flex h-6 w-6 flex-shrink-0 items-center justify-center rounded-full bg-red-600'>
|
||||
<span className='text-xs text-white'>✓</span>
|
||||
</div>
|
||||
<div className='ml-3'>
|
||||
<h3 className='text-lg font-medium'>
|
||||
{t(`about.overview.benefits.${index}.title`)}
|
||||
</h3>
|
||||
<p className='text-gray-600'>
|
||||
{t(`about.overview.benefits.${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('about.overview.imageAlt')}
|
||||
fill
|
||||
className='object-cover'
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
{/* Stats Section */}
|
||||
<section className='bg-red-600 py-16 text-white'>
|
||||
<div className='container mx-auto'>
|
||||
<div className='mb-12 text-center'>
|
||||
<h2 className='mb-4 text-3xl font-bold tracking-tight sm:text-4xl'>
|
||||
{t('about.stats.title')}
|
||||
</h2>
|
||||
<p className='mx-auto max-w-2xl text-white/80'>
|
||||
{t('about.stats.subtitle')}
|
||||
</p>
|
||||
</div>
|
||||
<div className='grid grid-cols-2 gap-8 text-center md:grid-cols-4'>
|
||||
{[0, 1, 2, 3].map((index) => (
|
||||
<div key={index} className='space-y-2'>
|
||||
<h3 className='text-4xl font-bold'>
|
||||
<AnimatedCounter
|
||||
end={Number(t(`about.stats.items.${index}.value`))}
|
||||
suffix={
|
||||
t(`about.stats.items.${index}.suffix`) ===
|
||||
`about.stats.items.${index}.suffix`
|
||||
? ''
|
||||
: t(`about.stats.items.${index}.suffix`) || ''
|
||||
}
|
||||
/>
|
||||
</h3>
|
||||
<p className='text-sm text-white/80'>
|
||||
{t(`about.stats.items.${index}.label`)}
|
||||
</p>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
{/* Our History */}
|
||||
<section className='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'>
|
||||
<History className='h-6 w-6 text-red-600' />
|
||||
</div>
|
||||
<h2 className='mb-4 text-3xl font-bold tracking-tight sm:text-4xl'>
|
||||
{t('about.history.title')}
|
||||
</h2>
|
||||
<p className='max-w-2xl text-gray-600'>
|
||||
{t('about.history.subtitle')}
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<CompanyTimeline />
|
||||
</div>
|
||||
</section>
|
||||
|
||||
{/* Our Stations */}
|
||||
<section className='bg-gray-50 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'>
|
||||
<MapPin className='h-6 w-6 text-red-600' />
|
||||
</div>
|
||||
<h2 className='mb-4 text-3xl font-bold tracking-tight sm:text-4xl'>
|
||||
{t('about.stations.title')}
|
||||
</h2>
|
||||
<p className='max-w-2xl text-gray-600'>
|
||||
{t('about.stations.subtitle')}
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<StationGallery />
|
||||
|
||||
<div className='mt-12 text-center'>
|
||||
<p className='mx-auto mb-6 max-w-2xl text-gray-600'>
|
||||
{t('about.stations.description')}
|
||||
</p>
|
||||
<Button className='bg-red-600 hover:bg-red-700'>
|
||||
{t('about.stations.buttonText')}{' '}
|
||||
<MapPin className='ml-2 h-4 w-4' />
|
||||
</Button>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
{/* Our Values */}
|
||||
<section className='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'>
|
||||
<Target className='h-6 w-6 text-red-600' />
|
||||
</div>
|
||||
<h2 className='mb-4 text-3xl font-bold tracking-tight sm:text-4xl'>
|
||||
{t('about.values.title')}
|
||||
</h2>
|
||||
<p className='max-w-2xl text-gray-600'>
|
||||
{t('about.values.subtitle')}
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<div className='grid gap-8 md:grid-cols-3'>
|
||||
{[0, 1, 2].map((index) => (
|
||||
<Card
|
||||
key={index}
|
||||
className='overflow-hidden transition-all hover:shadow-lg'
|
||||
>
|
||||
<CardContent className='p-6'>
|
||||
<div className='mb-4 flex h-12 w-12 items-center justify-center rounded-full bg-red-100'>
|
||||
<Star className='h-6 w-6 text-red-600' />
|
||||
</div>
|
||||
<h3 className='mb-2 text-xl font-bold'>
|
||||
{t(`about.values.items.${index}.title`)}
|
||||
</h3>
|
||||
<p className='text-gray-600'>
|
||||
{t(`about.values.items.${index}.description`)}
|
||||
</p>
|
||||
</CardContent>
|
||||
</Card>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
{/* Our Team */}
|
||||
<section className='bg-gray-50 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'>
|
||||
<Users className='h-6 w-6 text-red-600' />
|
||||
</div>
|
||||
<h2 className='mb-4 text-3xl font-bold tracking-tight sm:text-4xl'>
|
||||
{t('about.team.title')}
|
||||
</h2>
|
||||
<p className='max-w-2xl text-gray-600'>
|
||||
{t('about.team.subtitle')}
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<div className='grid grid-cols-1 gap-8 sm:grid-cols-2 lg:grid-cols-4'>
|
||||
{[0, 1, 2, 3].map((index) => (
|
||||
<div
|
||||
key={index}
|
||||
className='overflow-hidden rounded-lg bg-white shadow-md transition-transform hover:scale-105'
|
||||
>
|
||||
<div className='relative h-64 w-full'>
|
||||
<Image
|
||||
src={`/placeholder.svg?height=300&width=300&text=${t(`about.team.members.${index}.name`)}`}
|
||||
alt={t(`about.team.members.${index}.name`)}
|
||||
fill
|
||||
className='object-cover'
|
||||
/>
|
||||
</div>
|
||||
<div className='p-4 text-center'>
|
||||
<h3 className='text-lg font-bold'>
|
||||
{t(`about.team.members.${index}.name`)}
|
||||
</h3>
|
||||
<p className='text-gray-600'>
|
||||
{t(`about.team.members.${index}.position`)}
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
{/* Testimonials */}
|
||||
<section className='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'>
|
||||
<Star className='h-6 w-6 text-red-600' />
|
||||
</div>
|
||||
<h2 className='mb-4 text-3xl font-bold tracking-tight sm:text-4xl'>
|
||||
{t('about.testimonials.title')}
|
||||
</h2>
|
||||
<p className='max-w-2xl text-gray-600'>
|
||||
{t('about.testimonials.subtitle')}
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<div className='grid gap-8 md:grid-cols-3'>
|
||||
{[0, 1, 2].map((index) => (
|
||||
<Card
|
||||
key={index}
|
||||
className='overflow-hidden transition-all hover:shadow-lg'
|
||||
>
|
||||
<CardContent className='p-6'>
|
||||
<div className='mb-4 flex'>
|
||||
{Array(5)
|
||||
.fill(0)
|
||||
.map((_, i) => (
|
||||
<Star
|
||||
key={i}
|
||||
className={`h-5 w-5 ${i < Number(t(`about.testimonials.items.${index}.rating`)) ? 'fill-yellow-400 text-yellow-400' : 'text-gray-300'}`}
|
||||
/>
|
||||
))}
|
||||
</div>
|
||||
<p className='mb-4 text-gray-600 italic'>
|
||||
"{t(`about.testimonials.items.${index}.text`)}"
|
||||
</p>
|
||||
<p className='font-semibold'>
|
||||
{t(`about.testimonials.items.${index}.name`)}
|
||||
</p>
|
||||
</CardContent>
|
||||
</Card>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<CtaSection />
|
||||
</main>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
127
src/pages-templates/clients/certificates/index.tsx
Normal file
127
src/pages-templates/clients/certificates/index.tsx
Normal file
@ -0,0 +1,127 @@
|
||||
'use client';
|
||||
|
||||
import { Download, Eye } from 'lucide-react';
|
||||
import Image from 'next/image';
|
||||
|
||||
import { useLanguage } from '@/shared/language';
|
||||
import { Button } from '@/shared/shadcn-ui/button';
|
||||
import { Card, CardContent } from '@/shared/shadcn-ui/card';
|
||||
|
||||
export function CertificatesPage() {
|
||||
const { t } = useLanguage();
|
||||
|
||||
// This data would typically come from an API or CMS
|
||||
// We're keeping it as-is since it's dynamic content
|
||||
const certificates = [
|
||||
{
|
||||
id: 1,
|
||||
title: 'ISO 9001:2015',
|
||||
description: 'Сертификат системы менеджмента качества',
|
||||
image: '/placeholder.svg?height=400&width=300',
|
||||
issueDate: '15.03.2022',
|
||||
expiryDate: '15.03.2025',
|
||||
},
|
||||
{
|
||||
id: 2,
|
||||
title: 'ISO 14001:2015',
|
||||
description: 'Сертификат экологического менеджмента',
|
||||
image: '/placeholder.svg?height=400&width=300',
|
||||
issueDate: '10.05.2022',
|
||||
expiryDate: '10.05.2025',
|
||||
},
|
||||
{
|
||||
id: 3,
|
||||
title: 'OHSAS 18001',
|
||||
description: 'Сертификат системы управления охраной труда',
|
||||
image: '/placeholder.svg?height=400&width=300',
|
||||
issueDate: '22.07.2022',
|
||||
expiryDate: '22.07.2025',
|
||||
},
|
||||
{
|
||||
id: 4,
|
||||
title: 'Сертификат качества топлива',
|
||||
description: 'Подтверждение соответствия топлива стандартам качества',
|
||||
image: '/placeholder.svg?height=400&width=300',
|
||||
issueDate: '05.01.2023',
|
||||
expiryDate: '05.01.2024',
|
||||
},
|
||||
{
|
||||
id: 5,
|
||||
title: 'Сертификат соответствия',
|
||||
description: 'Соответствие услуг национальным стандартам',
|
||||
image: '/placeholder.svg?height=400&width=300',
|
||||
issueDate: '18.09.2022',
|
||||
expiryDate: '18.09.2025',
|
||||
},
|
||||
{
|
||||
id: 6,
|
||||
title: 'Лицензия на хранение ГСМ',
|
||||
description: 'Разрешение на хранение горюче-смазочных материалов',
|
||||
image: '/placeholder.svg?height=400&width=300',
|
||||
issueDate: '30.11.2021',
|
||||
expiryDate: '30.11.2026',
|
||||
},
|
||||
];
|
||||
|
||||
return (
|
||||
<>
|
||||
<main className='container mx-auto py-10'>
|
||||
<div className='mb-10 text-center'>
|
||||
<h1 className='mb-4 text-4xl font-bold'>{t('certificates.title')}</h1>
|
||||
<p className='mx-auto max-w-2xl text-lg text-gray-600'>
|
||||
{t('certificates.description')}
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<div className='grid gap-8 md:grid-cols-2 lg:grid-cols-3'>
|
||||
{certificates.map((certificate) => (
|
||||
<Card
|
||||
key={certificate.id}
|
||||
className='overflow-hidden transition-all duration-300 hover:shadow-lg'
|
||||
>
|
||||
<div className='relative h-[300px] w-full overflow-hidden bg-gray-100'>
|
||||
<Image
|
||||
src={certificate.image || '/placeholder.svg'}
|
||||
alt={certificate.title}
|
||||
fill
|
||||
className='object-contain p-4'
|
||||
sizes='(max-width: 768px) 100vw, (max-width: 1200px) 50vw, 33vw'
|
||||
/>
|
||||
</div>
|
||||
<CardContent className='p-6'>
|
||||
<h3 className='mb-2 text-xl font-bold'>{certificate.title}</h3>
|
||||
<p className='mb-4 text-gray-600'>{certificate.description}</p>
|
||||
<div className='mb-4 text-sm text-gray-500'>
|
||||
<p>
|
||||
{t('certificates.issueDate')}: {certificate.issueDate}
|
||||
</p>
|
||||
<p>
|
||||
{t('certificates.expiryDate')}: {certificate.expiryDate}
|
||||
</p>
|
||||
</div>
|
||||
<div className='flex gap-2'>
|
||||
<Button
|
||||
variant='outline'
|
||||
size='sm'
|
||||
className='flex items-center gap-1'
|
||||
>
|
||||
<Eye size={16} />
|
||||
<span>{t('common.buttons.view')}</span>
|
||||
</Button>
|
||||
<Button
|
||||
variant='outline'
|
||||
size='sm'
|
||||
className='flex items-center gap-1'
|
||||
>
|
||||
<Download size={16} />
|
||||
<span>{t('common.buttons.download')}</span>
|
||||
</Button>
|
||||
</div>
|
||||
</CardContent>
|
||||
</Card>
|
||||
))}
|
||||
</div>
|
||||
</main>
|
||||
</>
|
||||
);
|
||||
}
|
||||
55
src/pages-templates/clients/index.tsx
Normal file
55
src/pages-templates/clients/index.tsx
Normal file
@ -0,0 +1,55 @@
|
||||
"use client"
|
||||
|
||||
import Image from 'next/image';
|
||||
|
||||
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 - Сеть заправок в Таджикистане',
|
||||
description:
|
||||
'Информация для клиентов: программа лояльности, топливные карты, сертификаты и способы оплаты.',
|
||||
};
|
||||
|
||||
export function ClientsPage() {
|
||||
|
||||
const { t } = useLanguage()
|
||||
|
||||
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=400&width=1920&text=Для+наших+клиентов'
|
||||
alt='Для наших клиентов'
|
||||
width={1920}
|
||||
height={400}
|
||||
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-4 text-white'>
|
||||
<h1 className='text-4xl font-bold tracking-tight sm:text-5xl md:text-6xl'>
|
||||
{t('clients.title')}
|
||||
</h1>
|
||||
<p className='text-lg text-gray-200'>
|
||||
{t('clients.description')}
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<ServicesOverviewSection />
|
||||
<BenefitsSection />
|
||||
<CtaSection />
|
||||
</main>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
328
src/pages-templates/clients/loyalty/index.tsx
Normal file
328
src/pages-templates/clients/loyalty/index.tsx
Normal file
@ -0,0 +1,328 @@
|
||||
'use client';
|
||||
|
||||
import { Check, Percent } from 'lucide-react';
|
||||
import Image from 'next/image';
|
||||
|
||||
import { useLanguage } from '@/shared/language';
|
||||
import { Card, CardContent } from '@/shared/shadcn-ui/card';
|
||||
|
||||
import { CtaSection } from '@/widgets/cta-section';
|
||||
|
||||
export const metadata = {
|
||||
title: 'Программа лояльности | GasNetwork - Сеть заправок в Таджикистане',
|
||||
description:
|
||||
'Программа лояльности GasNetwork: накапливайте баллы и получайте скидки на топливо и услуги.',
|
||||
};
|
||||
|
||||
export function LoyaltyPage() {
|
||||
const { t } = useLanguage();
|
||||
|
||||
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=400&width=1920&text=Программа+лояльности'
|
||||
alt='Программа лояльности'
|
||||
width={1920}
|
||||
height={400}
|
||||
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-4 text-white'>
|
||||
<h1 className='text-4xl font-bold tracking-tight sm:text-5xl md:text-6xl'>
|
||||
{t('clients.loyalty.title')}
|
||||
</h1>
|
||||
<p className='text-lg text-gray-200'>
|
||||
{t('clients.loyalty.description')}
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
{/* Program Overview */}
|
||||
<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'>
|
||||
<Percent className='h-6 w-6 text-red-600' />
|
||||
</div>
|
||||
<h2 className='mb-6 text-3xl font-bold tracking-tight sm:text-4xl'>
|
||||
{t('clients.loyalty.programm.about')}
|
||||
</h2>
|
||||
<p className='mb-6 text-gray-600'>
|
||||
{t('clients.loyalty.programm.about-description')}
|
||||
</p>
|
||||
<p className='mb-6 text-gray-600'>
|
||||
{t('clients.loyalty.programm.about-description-2')}
|
||||
</p>
|
||||
|
||||
<div className='space-y-4'>
|
||||
<div className='flex items-start'>
|
||||
<div className='mt-1 flex h-6 w-6 flex-shrink-0 items-center justify-center rounded-full bg-red-600'>
|
||||
<span className='text-xs text-white'>✓</span>
|
||||
</div>
|
||||
<div className='ml-3'>
|
||||
<h3 className='text-lg font-medium'>
|
||||
{t('clients.loyalty.programm.conditions-1')}
|
||||
</h3>
|
||||
<p className='text-gray-600'>
|
||||
{t('clients.loyalty.programm.conditions.description-1')}
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
<div className='flex items-start'>
|
||||
<div className='mt-1 flex h-6 w-6 flex-shrink-0 items-center justify-center rounded-full bg-red-600'>
|
||||
<span className='text-xs text-white'>✓</span>
|
||||
</div>
|
||||
<div className='ml-3'>
|
||||
<h3 className='text-lg font-medium'>
|
||||
{t('clients.loyalty.programm.conditions-2')}
|
||||
</h3>
|
||||
<p className='text-gray-600'>
|
||||
{t('clients.loyalty.programm.conditions.description-2')}
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
<div className='flex items-start'>
|
||||
<div className='mt-1 flex h-6 w-6 flex-shrink-0 items-center justify-center rounded-full bg-red-600'>
|
||||
<span className='text-xs text-white'>✓</span>
|
||||
</div>
|
||||
<div className='ml-3'>
|
||||
<h3 className='text-lg font-medium'>
|
||||
{t('clients.loyalty.programm.conditions-3')}
|
||||
</h3>
|
||||
<p className='text-gray-600'>
|
||||
{t('clients.loyalty.programm.conditions.description-3')}
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div className='relative h-[400px] overflow-hidden rounded-xl shadow-xl'>
|
||||
<Image
|
||||
src='/placeholder.svg?height=400&width=600&text=Программа+лояльности'
|
||||
alt='Программа лояльности'
|
||||
fill
|
||||
className='object-cover'
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
{/* How It Works */}
|
||||
<section className='bg-gray-50 py-16'>
|
||||
<div className='container mx-auto'>
|
||||
<div className='mb-12 text-center'>
|
||||
<h2 className='mb-4 text-3xl font-bold tracking-tight sm:text-4xl'>
|
||||
{t('clients.loyalty.works.title')}
|
||||
</h2>
|
||||
<p className='mx-auto max-w-2xl text-gray-600'>
|
||||
{t('clients.loyalty.works.description')}
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<div className='grid gap-8 md:grid-cols-4'>
|
||||
<div className='text-center'>
|
||||
<div className='mx-auto mb-4 flex h-16 w-16 items-center justify-center rounded-full bg-red-600 text-2xl font-bold text-white'>
|
||||
1
|
||||
</div>
|
||||
<h3 className='mb-2 text-xl font-bold'>
|
||||
{t('clients.loyalty.works.stage-1')}
|
||||
</h3>
|
||||
<p className='text-gray-600'>
|
||||
{t('clients.loyalty.works.stage.description-1')}
|
||||
</p>
|
||||
</div>
|
||||
<div className='text-center'>
|
||||
<div className='mx-auto mb-4 flex h-16 w-16 items-center justify-center rounded-full bg-red-600 text-2xl font-bold text-white'>
|
||||
2
|
||||
</div>
|
||||
<h3 className='mb-2 text-xl font-bold'>
|
||||
{t('clients.loyalty.works.stage-2')}
|
||||
</h3>
|
||||
<p className='text-gray-600'>
|
||||
{t('clients.loyalty.works.stage.description-2')}
|
||||
</p>
|
||||
</div>
|
||||
<div className='text-center'>
|
||||
<div className='mx-auto mb-4 flex h-16 w-16 items-center justify-center rounded-full bg-red-600 text-2xl font-bold text-white'>
|
||||
3
|
||||
</div>
|
||||
<h3 className='mb-2 text-xl font-bold'>
|
||||
{t('clients.loyalty.works.stage-3')}
|
||||
</h3>
|
||||
<p className='text-gray-600'>
|
||||
{t('clients.loyalty.works.stage.description-3')}
|
||||
</p>
|
||||
</div>
|
||||
<div className='text-center'>
|
||||
<div className='mx-auto mb-4 flex h-16 w-16 items-center justify-center rounded-full bg-red-600 text-2xl font-bold text-white'>
|
||||
4
|
||||
</div>
|
||||
<h3 className='mb-2 text-xl font-bold'>
|
||||
{t('clients.loyalty.works.stage-4')}
|
||||
</h3>
|
||||
<p className='text-gray-600'>
|
||||
{t('clients.loyalty.works.stage.description-4')}
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
{/* Loyalty Levels */}
|
||||
<section className='py-16'>
|
||||
<div className='container mx-auto'>
|
||||
<div className='mb-12 text-center'>
|
||||
<h2 className='mb-4 text-3xl font-bold tracking-tight sm:text-4xl'>
|
||||
{t('clients.loyalty.works.levels.title')}
|
||||
</h2>
|
||||
<p className='mx-auto max-w-2xl text-gray-600'>
|
||||
{t('clients.loyalty.works.levels.description')}
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<div className='grid gap-8 md:grid-cols-3'>
|
||||
<Card className='overflow-hidden border-t-4 border-t-gray-400 transition-all hover:shadow-lg'>
|
||||
<CardContent className='p-6'>
|
||||
<h3 className='mb-4 text-center text-2xl font-bold'>
|
||||
{t('clients.loyalty.works.levels.card-1.title')}
|
||||
</h3>
|
||||
<div className='mb-6 text-center'>
|
||||
<span className='text-4xl font-bold'>
|
||||
{t('clients.loyalty.works.levels.card-1.percent')}
|
||||
</span>
|
||||
<p className='text-sm text-gray-600'>
|
||||
{t('clients.loyalty.works.levels.card.mark')}
|
||||
</p>
|
||||
</div>
|
||||
<ul className='space-y-2'>
|
||||
<li className='flex items-center'>
|
||||
<Check className='mr-2 h-5 w-5 text-green-500' />
|
||||
<span>
|
||||
{t('clients.loyalty.works.levels.card-1.bonus-1')}
|
||||
</span>
|
||||
</li>
|
||||
<li className='flex items-center'>
|
||||
<Check className='mr-2 h-5 w-5 text-green-500' />
|
||||
<span>
|
||||
{t('clients.loyalty.works.levels.card-1.bonus-2')}
|
||||
</span>
|
||||
</li>
|
||||
<li className='flex items-center'>
|
||||
<Check className='mr-2 h-5 w-5 text-green-500' />
|
||||
<span>
|
||||
{t('clients.loyalty.works.levels.card-1.bonus-3')}
|
||||
</span>
|
||||
</li>
|
||||
</ul>
|
||||
</CardContent>
|
||||
</Card>
|
||||
|
||||
<Card className='overflow-hidden border-t-4 border-t-yellow-500 transition-all hover:shadow-lg'>
|
||||
<CardContent className='p-6'>
|
||||
<h3 className='mb-4 text-center text-2xl font-bold'>
|
||||
{t('clients.loyalty.works.levels.card-2.title')}
|
||||
</h3>
|
||||
<div className='mb-6 text-center'>
|
||||
<span className='text-4xl font-bold'>
|
||||
{t('clients.loyalty.works.levels.card-2.percent')}
|
||||
</span>
|
||||
<p className='text-sm text-gray-600'>
|
||||
{t('clients.loyalty.works.levels.card.mark')}
|
||||
</p>
|
||||
</div>
|
||||
<ul className='space-y-2'>
|
||||
<li className='flex items-center'>
|
||||
<Check className='mr-2 h-5 w-5 text-green-500' />
|
||||
<span>
|
||||
{t('clients.loyalty.works.levels.card-1.bonus-1')}
|
||||
</span>
|
||||
</li>
|
||||
<li className='flex items-center'>
|
||||
<Check className='mr-2 h-5 w-5 text-green-500' />
|
||||
<span>
|
||||
{t('clients.loyalty.works.levels.card-2.bonus-2')}
|
||||
</span>
|
||||
</li>
|
||||
<li className='flex items-center'>
|
||||
<Check className='mr-2 h-5 w-5 text-green-500' />
|
||||
<span>
|
||||
{t('clients.loyalty.works.levels.card-3.bonus-3')}
|
||||
</span>
|
||||
</li>
|
||||
<li className='flex items-center'>
|
||||
<Check className='mr-2 h-5 w-5 text-green-500' />
|
||||
<span>
|
||||
{t('clients.loyalty.works.levels.card-4.bonus-4')}
|
||||
</span>
|
||||
</li>
|
||||
</ul>
|
||||
</CardContent>
|
||||
</Card>
|
||||
|
||||
<Card className='overflow-hidden border-t-4 border-t-red-600 transition-all hover:shadow-lg'>
|
||||
<CardContent className='p-6'>
|
||||
<h3 className='mb-4 text-center text-2xl font-bold'>
|
||||
{t('clients.loyalty.works.levels.card-3.title')}
|
||||
</h3>
|
||||
<div className='mb-6 text-center'>
|
||||
<span className='text-4xl font-bold'>
|
||||
{t('clients.loyalty.works.levels.card-3.percent')}
|
||||
</span>
|
||||
<p className='text-sm text-gray-600'>
|
||||
{t('clients.loyalty.works.levels.card.mark')}
|
||||
</p>
|
||||
</div>
|
||||
<ul className='space-y-2'>
|
||||
<li className='flex items-center'>
|
||||
<Check className='mr-2 h-5 w-5 text-green-500' />
|
||||
<span>
|
||||
{t('clients.loyalty.works.levels.card-3.bonus-1')}
|
||||
</span>
|
||||
</li>
|
||||
<li className='flex items-center'>
|
||||
<Check className='mr-2 h-5 w-5 text-green-500' />
|
||||
<span>
|
||||
{t('clients.loyalty.works.levels.card-3.bonus-2')}
|
||||
</span>
|
||||
</li>
|
||||
<li className='flex items-center'>
|
||||
<Check className='mr-2 h-5 w-5 text-green-500' />
|
||||
<span>
|
||||
{t('clients.loyalty.works.levels.card-3.bonus-3')}
|
||||
</span>
|
||||
</li>
|
||||
<li className='flex items-center'>
|
||||
<Check className='mr-2 h-5 w-5 text-green-500' />
|
||||
<span>
|
||||
{t('clients.loyalty.works.levels.card-3.bonus-4')}
|
||||
</span>
|
||||
</li>
|
||||
<li className='flex items-center'>
|
||||
<Check className='mr-2 h-5 w-5 text-green-500' />
|
||||
<span>
|
||||
{t('clients.loyalty.works.levels.card-3.bonus-5')}
|
||||
</span>
|
||||
</li>
|
||||
</ul>
|
||||
</CardContent>
|
||||
</Card>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<CtaSection />
|
||||
</main>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
103
src/pages-templates/login/index.tsx
Normal file
103
src/pages-templates/login/index.tsx
Normal file
@ -0,0 +1,103 @@
|
||||
'use client';
|
||||
|
||||
import { Building2, Fuel, User } from 'lucide-react';
|
||||
import Link from 'next/link';
|
||||
|
||||
import { LoginForm } from '@/features/auth/login-form';
|
||||
|
||||
import { useLanguage } from '@/shared/language';
|
||||
import {
|
||||
Card,
|
||||
CardContent,
|
||||
CardDescription,
|
||||
CardHeader,
|
||||
CardTitle,
|
||||
} from '@/shared/shadcn-ui/card';
|
||||
import {
|
||||
Tabs,
|
||||
TabsContent,
|
||||
TabsList,
|
||||
TabsTrigger,
|
||||
} from '@/shared/shadcn-ui/tabs';
|
||||
|
||||
const tabs = [
|
||||
{
|
||||
label: 'auth.bonusClient',
|
||||
type: 'bonus' as const,
|
||||
title: 'auth.bonusLogin.title',
|
||||
description: 'auth.bonusLogin.description',
|
||||
Icon: User,
|
||||
},
|
||||
{
|
||||
label: 'auth.corporateClient',
|
||||
type: 'corporate' as const,
|
||||
title: 'auth.corporateLogin.title',
|
||||
description: 'auth.corporateLogin.description',
|
||||
Icon: Building2,
|
||||
},
|
||||
];
|
||||
|
||||
export default function LoginPage() {
|
||||
const { t } = useLanguage();
|
||||
|
||||
return (
|
||||
<div className='flex min-h-screen flex-col items-center justify-center'>
|
||||
<main className='flex-1'>
|
||||
<div className='container max-w-6xl py-16'>
|
||||
<div className='mb-12 flex flex-col items-center text-center'>
|
||||
<div className='mb-4 inline-flex items-center justify-center rounded-full bg-red-100 p-2'>
|
||||
<Fuel className='h-6 w-6 text-red-600' />
|
||||
</div>
|
||||
<h1 className='mb-4 text-3xl font-bold tracking-tight sm:text-4xl'>
|
||||
{t('auth.title')}
|
||||
</h1>
|
||||
<p className='max-w-2xl text-gray-600'>{t('auth.description')}</p>
|
||||
</div>
|
||||
|
||||
<div className='mx-auto max-w-lg'>
|
||||
<Tabs defaultValue='bonus' className='w-full'>
|
||||
<TabsList className='mb-8 flex h-fit w-full flex-col sm:flex-row'>
|
||||
{tabs.map((tab) => {
|
||||
return (
|
||||
<TabsTrigger
|
||||
key={tab.label}
|
||||
value={tab.type}
|
||||
className='w-full text-base'
|
||||
>
|
||||
<tab.Icon className='mr-2 h-4 w-4' /> {t(tab.label)}
|
||||
</TabsTrigger>
|
||||
);
|
||||
})}
|
||||
</TabsList>
|
||||
|
||||
{tabs.map((tab) => {
|
||||
return (
|
||||
<TabsContent key={tab.label} value={tab.type}>
|
||||
<Card>
|
||||
<CardHeader>
|
||||
<CardTitle>{t(tab.title)}</CardTitle>
|
||||
<CardDescription>{t(tab.description)}</CardDescription>
|
||||
</CardHeader>
|
||||
<CardContent className='space-y-4'>
|
||||
<LoginForm type={tab.type} />
|
||||
</CardContent>
|
||||
</Card>
|
||||
</TabsContent>
|
||||
);
|
||||
})}
|
||||
</Tabs>
|
||||
|
||||
<div className='mt-8 text-center text-sm text-gray-500'>
|
||||
<p>
|
||||
{t('auth.loginIssues')}{' '}
|
||||
<Link href='/contact' className='text-red-600 hover:underline'>
|
||||
{t('auth.contactLink')}
|
||||
</Link>
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</main>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
@ -4,11 +4,14 @@ import { Users } from 'lucide-react';
|
||||
import { useEffect, useRef, useState } from 'react';
|
||||
|
||||
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()
|
||||
|
||||
useEffect(() => {
|
||||
const observer = new IntersectionObserver(
|
||||
(entries) => {
|
||||
@ -33,26 +36,26 @@ export default function AboutCounter() {
|
||||
}, []);
|
||||
|
||||
return (
|
||||
<div ref={sectionRef} className='my-8 grid grid-cols-3 gap-6 text-center'>
|
||||
<div className='transform rounded-lg bg-white p-6 shadow-md transition-transform hover:scale-105'>
|
||||
<div ref={sectionRef} className='my-8 grid grid-cols-1 gap-6 text-center'>
|
||||
<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'>
|
||||
<Users className='h-6 w-6 text-red-600' />
|
||||
</div>
|
||||
<h3 className='text-2xl font-bold text-gray-900'>
|
||||
{isVisible ? <AnimatedCounter end={150} suffix='+' /> : '0+'}
|
||||
</h3>
|
||||
<p className='text-gray-600'>Сотрудников</p>
|
||||
<p className='text-gray-600'>{t("about.stats.items.2.label")}</p>
|
||||
</div>
|
||||
<div className='transform rounded-lg bg-white p-6 shadow-md transition-transform hover:scale-105'>
|
||||
<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'>
|
||||
<Users className='h-6 w-6 text-red-600' />
|
||||
</div>
|
||||
<h3 className='text-2xl font-bold text-gray-900'>
|
||||
{isVisible ? <AnimatedCounter end={5} suffix='M+' /> : '0M+'}
|
||||
</h3>
|
||||
<p className='text-gray-600'>Литров топлива в месяц</p>
|
||||
<p className='text-gray-600'>{t("about.stats.items.4.label")}</p>
|
||||
</div>
|
||||
<div className='transform rounded-lg bg-white p-6 shadow-md transition-transform hover:scale-105'>
|
||||
<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'>
|
||||
<Users className='h-6 w-6 text-red-600' />
|
||||
</div>
|
||||
@ -63,7 +66,7 @@ export default function AboutCounter() {
|
||||
'0%'
|
||||
)}
|
||||
</h3>
|
||||
<p className='text-gray-600'>Довольных клиентов</p>
|
||||
<p className='text-gray-600'>{t("about.stats.items.5.label")}</p>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
|
||||
@ -1,11 +1,13 @@
|
||||
'use client';
|
||||
|
||||
import { ChevronLeft, ChevronRight } from 'lucide-react';
|
||||
import { ChevronLeft, ChevronRight} from 'lucide-react';
|
||||
import Image from 'next/image';
|
||||
import { useEffect, useState } from 'react';
|
||||
|
||||
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 = [
|
||||
{
|
||||
@ -43,6 +45,8 @@ export default function PromotionSlider() {
|
||||
const [currentIndex, setCurrentIndex] = useState(0);
|
||||
const [visibleItems, setVisibleItems] = useState(3);
|
||||
|
||||
const { t } = useLanguage()
|
||||
|
||||
useEffect(() => {
|
||||
const handleResize = () => {
|
||||
if (window.innerWidth < 640) {
|
||||
@ -74,68 +78,66 @@ export default function PromotionSlider() {
|
||||
};
|
||||
|
||||
return (
|
||||
<div className='relative'>
|
||||
<div className='overflow-hidden'>
|
||||
<div
|
||||
className='flex transition-transform duration-300 ease-in-out'
|
||||
style={{
|
||||
transform: `translateX(-${currentIndex * (100 / visibleItems)}%)`,
|
||||
}}
|
||||
>
|
||||
{promotions.map((promo) => (
|
||||
<div
|
||||
key={promo.id}
|
||||
className='w-full flex-none p-2 sm:w-1/2 lg:w-1/3'
|
||||
data-aos='zoom-in-right'
|
||||
data-aos-duration='700'
|
||||
>
|
||||
<Card className='h-full overflow-hidden transition-shadow hover:shadow-lg'>
|
||||
<div className='relative h-48'>
|
||||
<Image
|
||||
src={promo.image || '/placeholder.svg'}
|
||||
alt={promo.title}
|
||||
fill
|
||||
className='object-cover'
|
||||
/>
|
||||
</div>
|
||||
<CardContent className='p-4'>
|
||||
<h3 className='mb-2 text-lg font-bold'>{promo.title}</h3>
|
||||
<p className='mb-3 text-sm text-gray-600'>
|
||||
{promo.description}
|
||||
</p>
|
||||
<div className='flex items-center justify-between'>
|
||||
<span className='text-xs text-gray-500'>
|
||||
Действует до: {promo.validUntil}
|
||||
</span>
|
||||
<Button
|
||||
<div className='relative overflow-hidden'>
|
||||
<div
|
||||
className='flex transition-transform duration-300 ease-in-out'
|
||||
style={{
|
||||
transform: `translateX(-${currentIndex * (100 / visibleItems)}%)`,
|
||||
}}
|
||||
>
|
||||
{promotions.map((promo) => (
|
||||
<div
|
||||
key={promo.id}
|
||||
className='w-full flex-none p-2 sm:w-1/2 lg:w-1/3'
|
||||
data-aos='zoom-in-right'
|
||||
data-aos-duration='700'
|
||||
>
|
||||
<Card className='h-full overflow-hidden transition-shadow hover:shadow-lg'>
|
||||
<div className='relative h-48'>
|
||||
<Image
|
||||
src={promo.image || '/placeholder.svg'}
|
||||
alt={promo.title}
|
||||
fill
|
||||
className='object-cover'
|
||||
/>
|
||||
</div>
|
||||
<CardContent className='p-4'>
|
||||
<h3 className='mb-2 text-lg font-bold'>{promo.title}</h3>
|
||||
<p className='mb-3 text-sm text-gray-600'>
|
||||
{promo.description}
|
||||
</p>
|
||||
<div className='flex items-center justify-between'>
|
||||
<span className='text-xs text-gray-500'>
|
||||
Действует до: {promo.validUntil}
|
||||
</span>
|
||||
<Link href='#'>
|
||||
<Button
|
||||
variant='outline'
|
||||
size='sm'
|
||||
className='border-red-600 text-red-600 hover:bg-red-50'
|
||||
>
|
||||
Подробнее
|
||||
{t('common.buttons.readMore')}
|
||||
</Button>
|
||||
</div>
|
||||
</CardContent>
|
||||
</Card>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
</Link>
|
||||
</div>
|
||||
</CardContent>
|
||||
</Card>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
|
||||
<Button
|
||||
variant='outline'
|
||||
size='icon'
|
||||
className='absolute top-1/2 left-0 z-10 -translate-x-1/2 -translate-y-1/2 border-gray-200 bg-white shadow-lg'
|
||||
className='absolute top-1/2 left-0 z-10 -translate-y-1/2 border-gray-200 bg-white shadow-lg'
|
||||
onClick={prevSlide}
|
||||
>
|
||||
<ChevronLeft className='h-4 w-4' />
|
||||
<span className='sr-only'>Предыдущий</span>
|
||||
</Button>
|
||||
|
||||
<Button
|
||||
variant='outline'
|
||||
size='icon'
|
||||
className='absolute top-1/2 right-0 z-10 translate-x-1/2 -translate-y-1/2 border-gray-200 bg-white shadow-lg'
|
||||
className='absolute top-1/2 right-0 z-10 -translate-y-1/2 border-gray-200 bg-white shadow-lg'
|
||||
onClick={nextSlide}
|
||||
>
|
||||
<ChevronRight className='h-4 w-4' />
|
||||
|
||||
42
src/shared/components/submit-button.tsx
Normal file
42
src/shared/components/submit-button.tsx
Normal file
@ -0,0 +1,42 @@
|
||||
'use client';
|
||||
|
||||
import { Loader2Icon } from 'lucide-react';
|
||||
|
||||
import { Button, type ButtonProps } from '@/shared/shadcn-ui/button';
|
||||
|
||||
import { useLanguage } from '../language';
|
||||
|
||||
interface SubmitButtonProps extends ButtonProps {
|
||||
title?: string;
|
||||
isLoading: boolean;
|
||||
}
|
||||
|
||||
export const SubmitButton = ({
|
||||
title = 'common.buttons.login',
|
||||
size = 'default',
|
||||
type = 'submit',
|
||||
className,
|
||||
disabled,
|
||||
isLoading,
|
||||
onClick,
|
||||
...props
|
||||
}: SubmitButtonProps) => {
|
||||
const { t } = useLanguage();
|
||||
|
||||
return (
|
||||
<Button
|
||||
onClick={onClick}
|
||||
type={type}
|
||||
size={size}
|
||||
className={className}
|
||||
disabled={isLoading || disabled}
|
||||
{...props}
|
||||
>
|
||||
{isLoading ? (
|
||||
<Loader2Icon className='animate-spin' />
|
||||
) : (
|
||||
(props.children ?? t(title))
|
||||
)}
|
||||
</Button>
|
||||
);
|
||||
};
|
||||
73
src/shared/language/context/language-provider.tsx
Normal file
73
src/shared/language/context/language-provider.tsx
Normal file
@ -0,0 +1,73 @@
|
||||
'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>
|
||||
);
|
||||
}
|
||||
18
src/shared/language/hooks/use-language.ts
Normal file
18
src/shared/language/hooks/use-language.ts
Normal file
@ -0,0 +1,18 @@
|
||||
'use client';
|
||||
|
||||
import { useContext } from 'react';
|
||||
|
||||
import { LanguageContext } from '../context/language-provider';
|
||||
|
||||
export function useLanguage() {
|
||||
const context = useContext(LanguageContext);
|
||||
if (context === undefined) {
|
||||
throw new Error('useLanguage must be used within a LanguageProvider');
|
||||
}
|
||||
|
||||
if (typeof context.t !== 'function') {
|
||||
throw new Error('Translation function (t) is not available');
|
||||
}
|
||||
|
||||
return context;
|
||||
}
|
||||
6
src/shared/language/index.ts
Normal file
6
src/shared/language/index.ts
Normal file
@ -0,0 +1,6 @@
|
||||
export {
|
||||
LanguageProvider,
|
||||
languages,
|
||||
type Language,
|
||||
} from './context/language-provider';
|
||||
export { useLanguage } from './hooks/use-language';
|
||||
108
src/shared/language/locales/en.json
Normal file
108
src/shared/language/locales/en.json
Normal file
@ -0,0 +1,108 @@
|
||||
{
|
||||
"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"
|
||||
}
|
||||
247
src/shared/language/locales/ru.json
Normal file
247
src/shared/language/locales/ru.json
Normal file
@ -0,0 +1,247 @@
|
||||
{
|
||||
"common.contacts.address": "ул. Рудаки 137, Душанбе, Таджикистан",
|
||||
"common.contacts.tel": "+992 (37) 223-45-67",
|
||||
"common.contacts.email": "info@gasnetwork.tj",
|
||||
|
||||
"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.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.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": "Станьте частью нашей сети. Получайте специальные предложения, бонусы и скидки.",
|
||||
|
||||
"clients.title": "Для наших клиентов",
|
||||
"clients.description": "Информация для клиентов: программа лояльности, топливные карты, сертификаты и способы оплаты.",
|
||||
"clients.services.title": "Наши услуги для клиентов",
|
||||
"clients.services.subtitle": "Мы стремимся сделать обслуживание на наших заправках максимально удобным и выгодным для вас",
|
||||
"clients.benefits.title": "Преимущества для клиентов",
|
||||
"clients.benefits.subtitle": "Став клиентом GasNetwork, вы получаете множество преимуществ, которые делают заправку вашего автомобиля более выгодной и удобной.",
|
||||
|
||||
"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": "Все города"
|
||||
}
|
||||
44
src/shared/language/ui/language-switcher.tsx
Normal file
44
src/shared/language/ui/language-switcher.tsx
Normal file
@ -0,0 +1,44 @@
|
||||
'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,6 +2,7 @@
|
||||
|
||||
import { Provider } from 'react-redux';
|
||||
|
||||
import { LanguageProvider } from '../language';
|
||||
import { store } from '../store';
|
||||
import { ThemeProvider } from '../theme/theme-provider';
|
||||
import { AosProvider } from './aos-provider';
|
||||
@ -14,17 +15,19 @@ type ProvidersProps = {
|
||||
export const Providers = ({ children }: ProvidersProps) => {
|
||||
return (
|
||||
<Provider store={store}>
|
||||
<ThemeProvider
|
||||
attribute='class'
|
||||
defaultTheme='system'
|
||||
enableSystem
|
||||
disableTransitionOnChange
|
||||
>
|
||||
<AosProvider>
|
||||
{children}
|
||||
<Toaster />
|
||||
</AosProvider>
|
||||
</ThemeProvider>
|
||||
<LanguageProvider>
|
||||
<ThemeProvider
|
||||
attribute='class'
|
||||
defaultTheme='system'
|
||||
enableSystem
|
||||
disableTransitionOnChange
|
||||
>
|
||||
<AosProvider>
|
||||
{children}
|
||||
<Toaster />
|
||||
</AosProvider>
|
||||
</ThemeProvider>
|
||||
</LanguageProvider>
|
||||
</Provider>
|
||||
);
|
||||
};
|
||||
|
||||
36
src/shared/shadcn-ui/badge.tsx
Normal file
36
src/shared/shadcn-ui/badge.tsx
Normal file
@ -0,0 +1,36 @@
|
||||
import { cva, type VariantProps } from 'class-variance-authority';
|
||||
import * as React from 'react';
|
||||
|
||||
import { cn } from '@/shared/lib/utils';
|
||||
|
||||
const badgeVariants = cva(
|
||||
'focus:ring-ring inline-flex items-center rounded-full border px-2.5 py-0.5 text-xs font-semibold transition-colors focus:ring-2 focus:ring-offset-2 focus:outline-none',
|
||||
{
|
||||
variants: {
|
||||
variant: {
|
||||
default:
|
||||
'bg-primary text-primary-foreground hover:bg-primary/80 border-transparent',
|
||||
secondary:
|
||||
'bg-secondary text-secondary-foreground hover:bg-secondary/80 border-transparent',
|
||||
destructive:
|
||||
'bg-destructive text-destructive-foreground hover:bg-destructive/80 border-transparent',
|
||||
outline: 'text-foreground',
|
||||
},
|
||||
},
|
||||
defaultVariants: {
|
||||
variant: 'default',
|
||||
},
|
||||
},
|
||||
);
|
||||
|
||||
export interface BadgeProps
|
||||
extends React.HTMLAttributes<HTMLDivElement>,
|
||||
VariantProps<typeof badgeVariants> {}
|
||||
|
||||
function Badge({ className, variant, ...props }: BadgeProps) {
|
||||
return (
|
||||
<div className={cn(badgeVariants({ variant }), className)} {...props} />
|
||||
);
|
||||
}
|
||||
|
||||
export { Badge, badgeVariants };
|
||||
70
src/shared/shadcn-ui/calendar.tsx
Normal file
70
src/shared/shadcn-ui/calendar.tsx
Normal file
@ -0,0 +1,70 @@
|
||||
'use client';
|
||||
|
||||
import { ChevronLeft, ChevronRight } from 'lucide-react';
|
||||
import * as React from 'react';
|
||||
import { DayPicker } from 'react-day-picker';
|
||||
|
||||
import { cn } from '@/shared/lib/utils';
|
||||
import { buttonVariants } from '@/shared/shadcn-ui/button';
|
||||
|
||||
export type CalendarProps = React.ComponentProps<typeof DayPicker>;
|
||||
|
||||
function Calendar({
|
||||
className,
|
||||
classNames,
|
||||
showOutsideDays = true,
|
||||
...props
|
||||
}: CalendarProps) {
|
||||
return (
|
||||
<DayPicker
|
||||
showOutsideDays={showOutsideDays}
|
||||
className={cn('p-3', className)}
|
||||
classNames={{
|
||||
months: 'flex flex-col sm:flex-row space-y-4 sm:space-x-4 sm:space-y-0',
|
||||
month: 'space-y-4',
|
||||
caption: 'flex justify-center pt-1 relative items-center',
|
||||
caption_label: 'text-sm font-medium',
|
||||
nav: 'space-x-1 flex items-center',
|
||||
nav_button: cn(
|
||||
buttonVariants({ variant: 'outline' }),
|
||||
'h-7 w-7 bg-transparent p-0 opacity-50 hover:opacity-100',
|
||||
),
|
||||
nav_button_previous: 'absolute left-1',
|
||||
nav_button_next: 'absolute right-1',
|
||||
table: 'w-full border-collapse space-y-1',
|
||||
head_row: 'flex',
|
||||
head_cell:
|
||||
'text-muted-foreground rounded-md w-9 font-normal text-[0.8rem]',
|
||||
row: 'flex w-full mt-2',
|
||||
cell: 'h-9 w-9 text-center text-sm p-0 relative [&:has([aria-selected].day-range-end)]:rounded-r-md [&:has([aria-selected].day-outside)]:bg-accent/50 [&:has([aria-selected])]:bg-accent first:[&:has([aria-selected])]:rounded-l-md last:[&:has([aria-selected])]:rounded-r-md focus-within:relative focus-within:z-20',
|
||||
day: cn(
|
||||
buttonVariants({ variant: 'ghost' }),
|
||||
'h-9 w-9 p-0 font-normal aria-selected:opacity-100',
|
||||
),
|
||||
day_range_end: 'day-range-end',
|
||||
day_selected:
|
||||
'bg-primary text-primary-foreground hover:bg-primary hover:text-primary-foreground focus:bg-primary focus:text-primary-foreground',
|
||||
day_today: 'bg-accent text-accent-foreground',
|
||||
day_outside:
|
||||
'day-outside text-muted-foreground aria-selected:bg-accent/50 aria-selected:text-muted-foreground',
|
||||
day_disabled: 'text-muted-foreground opacity-50',
|
||||
day_range_middle:
|
||||
'aria-selected:bg-accent aria-selected:text-accent-foreground',
|
||||
day_hidden: 'invisible',
|
||||
...classNames,
|
||||
}}
|
||||
components={{
|
||||
IconLeft: ({ className, ...props }) => (
|
||||
<ChevronLeft className={cn('h-4 w-4', className)} {...props} />
|
||||
),
|
||||
IconRight: ({ className, ...props }) => (
|
||||
<ChevronRight className={cn('h-4 w-4', className)} {...props} />
|
||||
),
|
||||
}}
|
||||
{...props}
|
||||
/>
|
||||
);
|
||||
}
|
||||
Calendar.displayName = 'Calendar';
|
||||
|
||||
export { Calendar };
|
||||
30
src/shared/shadcn-ui/popover.tsx
Normal file
30
src/shared/shadcn-ui/popover.tsx
Normal file
@ -0,0 +1,30 @@
|
||||
'use client';
|
||||
|
||||
import { cn } from '@/shared/lib/utils';
|
||||
import * as PopoverPrimitive from '@radix-ui/react-popover';
|
||||
import * as React from 'react';
|
||||
|
||||
const Popover = PopoverPrimitive.Root;
|
||||
|
||||
const PopoverTrigger = PopoverPrimitive.Trigger;
|
||||
|
||||
const PopoverContent = React.forwardRef<
|
||||
React.ComponentRef<typeof PopoverPrimitive.Content>,
|
||||
React.ComponentPropsWithoutRef<typeof PopoverPrimitive.Content>
|
||||
>(({ className, align = 'center', sideOffset = 4, ...props }, ref) => (
|
||||
<PopoverPrimitive.Portal>
|
||||
<PopoverPrimitive.Content
|
||||
ref={ref}
|
||||
align={align}
|
||||
sideOffset={sideOffset}
|
||||
className={cn(
|
||||
'bg-popover text-popover-foreground data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-95 data-[side=bottom]:slide-in-from-top-2 data-[side=left]:slide-in-from-right-2 data-[side=right]:slide-in-from-left-2 data-[side=top]:slide-in-from-bottom-2 z-50 w-72 origin-[--radix-popover-content-transform-origin] rounded-md border p-4 shadow-md outline-none',
|
||||
className,
|
||||
)}
|
||||
{...props}
|
||||
/>
|
||||
</PopoverPrimitive.Portal>
|
||||
));
|
||||
PopoverContent.displayName = PopoverPrimitive.Content.displayName;
|
||||
|
||||
export { Popover, PopoverTrigger, PopoverContent };
|
||||
117
src/shared/shadcn-ui/table.tsx
Normal file
117
src/shared/shadcn-ui/table.tsx
Normal file
@ -0,0 +1,117 @@
|
||||
import * as React from 'react';
|
||||
|
||||
import { cn } from '@/shared/lib/utils';
|
||||
|
||||
const Table = React.forwardRef<
|
||||
HTMLTableElement,
|
||||
React.HTMLAttributes<HTMLTableElement>
|
||||
>(({ className, ...props }, ref) => (
|
||||
<div className='relative w-full overflow-auto'>
|
||||
<table
|
||||
ref={ref}
|
||||
className={cn('w-full caption-bottom text-sm', className)}
|
||||
{...props}
|
||||
/>
|
||||
</div>
|
||||
));
|
||||
Table.displayName = 'Table';
|
||||
|
||||
const TableHeader = React.forwardRef<
|
||||
HTMLTableSectionElement,
|
||||
React.HTMLAttributes<HTMLTableSectionElement>
|
||||
>(({ className, ...props }, ref) => (
|
||||
<thead ref={ref} className={cn('[&_tr]:border-b', className)} {...props} />
|
||||
));
|
||||
TableHeader.displayName = 'TableHeader';
|
||||
|
||||
const TableBody = React.forwardRef<
|
||||
HTMLTableSectionElement,
|
||||
React.HTMLAttributes<HTMLTableSectionElement>
|
||||
>(({ className, ...props }, ref) => (
|
||||
<tbody
|
||||
ref={ref}
|
||||
className={cn('[&_tr:last-child]:border-0', className)}
|
||||
{...props}
|
||||
/>
|
||||
));
|
||||
TableBody.displayName = 'TableBody';
|
||||
|
||||
const TableFooter = React.forwardRef<
|
||||
HTMLTableSectionElement,
|
||||
React.HTMLAttributes<HTMLTableSectionElement>
|
||||
>(({ className, ...props }, ref) => (
|
||||
<tfoot
|
||||
ref={ref}
|
||||
className={cn(
|
||||
'bg-muted/50 border-t font-medium [&>tr]:last:border-b-0',
|
||||
className,
|
||||
)}
|
||||
{...props}
|
||||
/>
|
||||
));
|
||||
TableFooter.displayName = 'TableFooter';
|
||||
|
||||
const TableRow = React.forwardRef<
|
||||
HTMLTableRowElement,
|
||||
React.HTMLAttributes<HTMLTableRowElement>
|
||||
>(({ className, ...props }, ref) => (
|
||||
<tr
|
||||
ref={ref}
|
||||
className={cn(
|
||||
'hover:bg-muted/50 data-[state=selected]:bg-muted border-b transition-colors',
|
||||
className,
|
||||
)}
|
||||
{...props}
|
||||
/>
|
||||
));
|
||||
TableRow.displayName = 'TableRow';
|
||||
|
||||
const TableHead = React.forwardRef<
|
||||
HTMLTableCellElement,
|
||||
React.ThHTMLAttributes<HTMLTableCellElement>
|
||||
>(({ className, ...props }, ref) => (
|
||||
<th
|
||||
ref={ref}
|
||||
className={cn(
|
||||
'text-muted-foreground h-12 px-4 text-left align-middle font-medium [&:has([role=checkbox])]:pr-0',
|
||||
className,
|
||||
)}
|
||||
{...props}
|
||||
/>
|
||||
));
|
||||
TableHead.displayName = 'TableHead';
|
||||
|
||||
const TableCell = React.forwardRef<
|
||||
HTMLTableCellElement,
|
||||
React.TdHTMLAttributes<HTMLTableCellElement>
|
||||
>(({ className, ...props }, ref) => (
|
||||
<td
|
||||
ref={ref}
|
||||
className={cn('p-4 align-middle [&:has([role=checkbox])]:pr-0', className)}
|
||||
{...props}
|
||||
/>
|
||||
));
|
||||
TableCell.displayName = 'TableCell';
|
||||
|
||||
const TableCaption = React.forwardRef<
|
||||
HTMLTableCaptionElement,
|
||||
React.HTMLAttributes<HTMLTableCaptionElement>
|
||||
>(({ className, ...props }, ref) => (
|
||||
<caption
|
||||
ref={ref}
|
||||
className={cn('text-muted-foreground mt-4 text-sm', className)}
|
||||
{...props}
|
||||
/>
|
||||
));
|
||||
TableCaption.displayName = 'TableCaption';
|
||||
|
||||
export {
|
||||
Table,
|
||||
TableHeader,
|
||||
TableBody,
|
||||
TableFooter,
|
||||
TableHead,
|
||||
TableRow,
|
||||
TableCell,
|
||||
TableCaption,
|
||||
};
|
||||
@ -1,6 +1,10 @@
|
||||
import { combineReducers } from '@reduxjs/toolkit';
|
||||
|
||||
import { loginAPI } from '@/entities/auth/api/login.api';
|
||||
|
||||
import { baseAPI } from '@/shared/api/base-api';
|
||||
|
||||
export const rootReducer = combineReducers({
|
||||
[baseAPI.reducerPath]: baseAPI.reducer,
|
||||
[loginAPI.reducerPath]: loginAPI.reducer,
|
||||
});
|
||||
|
||||
@ -74,18 +74,22 @@ export function CompanyTimeline() {
|
||||
data-aos='zoom-in-down'
|
||||
>
|
||||
<div className='mb-2 inline-flex items-center justify-center rounded-full bg-red-100 p-2'>
|
||||
<Calendar className='h-5 w-5 text-red-600' />
|
||||
<Calendar className='size-5 text-red-600' />
|
||||
</div>
|
||||
<h3 className='text-xl font-bold'>{event.year}</h3>
|
||||
<h4 className='mb-2 text-lg font-semibold'>{event.title}</h4>
|
||||
</div>
|
||||
|
||||
<div
|
||||
className={`${index % 2 === 0 ? 'md:pl-10' : 'md:order-first md:pr-10 md:text-right'}`}
|
||||
className={
|
||||
index % 2 === 0
|
||||
? 'md:pl-10'
|
||||
: 'md:order-first md:pr-10 md:text-right'
|
||||
}
|
||||
>
|
||||
<Card
|
||||
className='overflow-hidden transition-all hover:shadow-md'
|
||||
data-aos={index % 2 === 0 ? 'fade-left' : 'fade-right'}
|
||||
data-aos={'fade-up'}
|
||||
>
|
||||
<CardContent className='p-4'>
|
||||
<p className='text-gray-600'>{event.description}</p>
|
||||
@ -94,8 +98,8 @@ export function CompanyTimeline() {
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className='absolute top-5 left-1/2 -ml-3 hidden h-6 w-6 items-center justify-center rounded-full bg-red-600 md:flex'>
|
||||
<div className='h-3 w-3 rounded-full bg-white'></div>
|
||||
<div className='absolute top-5 left-1/2 -ml-3 hidden size-6 items-center justify-center rounded-full bg-red-600 md:flex'>
|
||||
<div className='size-3 rounded-full bg-white'></div>
|
||||
</div>
|
||||
</div>
|
||||
))}
|
||||
@ -105,16 +109,18 @@ export function CompanyTimeline() {
|
||||
<div className='mt-8 text-center'>
|
||||
<Button
|
||||
variant='outline'
|
||||
onClick={() => setExpanded(!expanded)}
|
||||
onClick={() => {
|
||||
setExpanded(!expanded);
|
||||
}}
|
||||
className='inline-flex items-center'
|
||||
>
|
||||
{expanded ? (
|
||||
<>
|
||||
Свернуть <ChevronUp className='ml-2 h-4 w-4' />
|
||||
Свернуть <ChevronUp className='ml-2 size-4' />
|
||||
</>
|
||||
) : (
|
||||
<>
|
||||
Показать больше <ChevronDown className='ml-2 h-4 w-4' />
|
||||
Показать больше <ChevronDown className='ml-2 size-4' />
|
||||
</>
|
||||
)}
|
||||
</Button>
|
||||
|
||||
@ -1,75 +1,34 @@
|
||||
'use client';
|
||||
|
||||
import { Users } from 'lucide-react';
|
||||
import Image from 'next/image';
|
||||
|
||||
import AboutCounter from '@/shared/components/about-counter';
|
||||
import { useLanguage } from '@/shared/language';
|
||||
|
||||
export const AboutSection = () => {
|
||||
const { t } = useLanguage();
|
||||
|
||||
return (
|
||||
<section id='about' className='py-16'>
|
||||
<section id='about' className='px-2 py-8 sm:py-16'>
|
||||
<div className='container mx-auto'>
|
||||
<div className='grid items-center gap-12 md:grid-cols-2'>
|
||||
<div data-aos='fade-right'>
|
||||
<div>
|
||||
<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-6 text-3xl font-bold tracking-tight sm:text-4xl'>
|
||||
О нашей компании
|
||||
{t('home.about.title')}
|
||||
</h2>
|
||||
<p className='mb-6 text-gray-600'>
|
||||
Наша сеть заправок является одной из ведущих в Таджикистане. Мы
|
||||
предоставляем качественное топливо и высокий уровень обслуживания
|
||||
для наших клиентов уже более 15 лет.
|
||||
</p>
|
||||
<p className='mb-6 text-gray-600'>
|
||||
Мы постоянно развиваемся, открывая новые станции и улучшая сервис
|
||||
на существующих. Наша цель - сделать заправку автомобиля
|
||||
максимально удобной и быстрой для каждого клиента.
|
||||
</p>
|
||||
<p className='mb-6 text-gray-600'>{t('home.about.description1')}</p>
|
||||
<p className='mb-6 text-gray-600'>{t('home.about.description2')}</p>
|
||||
|
||||
<AboutCounter />
|
||||
<div className='space-y-4'>
|
||||
<div className='flex items-start'>
|
||||
<div className='mt-1 flex h-6 w-6 flex-shrink-0 items-center justify-center rounded-full bg-red-600'>
|
||||
<span className='text-xs text-white'>✓</span>
|
||||
</div>
|
||||
<div className='ml-3'>
|
||||
<h3 className='text-lg font-medium'>Качественное топливо</h3>
|
||||
<p className='text-gray-600'>
|
||||
Мы гарантируем высокое качество нашего топлива
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
<div className='flex items-start'>
|
||||
<div className='mt-1 flex h-6 w-6 flex-shrink-0 items-center justify-center rounded-full bg-red-600'>
|
||||
<span className='text-xs text-white'>✓</span>
|
||||
</div>
|
||||
<div className='ml-3'>
|
||||
<h3 className='text-lg font-medium'>
|
||||
Современное оборудование
|
||||
</h3>
|
||||
<p className='text-gray-600'>
|
||||
Все наши станции оснащены современным оборудованием
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
<div className='flex items-start'>
|
||||
<div className='mt-1 flex h-6 w-6 flex-shrink-0 items-center justify-center rounded-full bg-red-600'>
|
||||
<span className='text-xs text-white'>✓</span>
|
||||
</div>
|
||||
<div className='ml-3'>
|
||||
<h3 className='text-lg font-medium'>
|
||||
Профессиональный персонал
|
||||
</h3>
|
||||
<p className='text-gray-600'>
|
||||
Наши сотрудники - профессионалы своего дела
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<Features />
|
||||
</div>
|
||||
<div
|
||||
data-aos='fade-left'
|
||||
className='relative h-[400px] overflow-hidden rounded-xl shadow-xl'
|
||||
data-aos='zoom-in-down'
|
||||
>
|
||||
<Image
|
||||
src='/placeholder.svg?height=400&width=600'
|
||||
@ -83,3 +42,45 @@ export const AboutSection = () => {
|
||||
</section>
|
||||
);
|
||||
};
|
||||
|
||||
interface Feature {
|
||||
title: string;
|
||||
description: string;
|
||||
}
|
||||
|
||||
const features: Array<Feature> = [
|
||||
{
|
||||
title: 'home.about.features.quality.title',
|
||||
description: 'home.about.features.quality.description',
|
||||
},
|
||||
{
|
||||
title: 'home.about.features.equipment.title',
|
||||
description: 'home.about.features.equipment.description',
|
||||
},
|
||||
{
|
||||
title: 'home.about.features.staff.title',
|
||||
description: 'home.about.features.staff.description',
|
||||
},
|
||||
];
|
||||
|
||||
const Features = () => {
|
||||
const { t } = useLanguage();
|
||||
|
||||
return (
|
||||
<div className='space-y-4'>
|
||||
{features.map(({ title, description }) => {
|
||||
return (
|
||||
<div key={title} className='flex items-start'>
|
||||
<div className='mt-1 flex h-6 w-6 flex-shrink-0 items-center justify-center rounded-full bg-red-600'>
|
||||
<span className='text-xs text-white'>✓</span>
|
||||
</div>
|
||||
<div className='ml-3'>
|
||||
<h3 className='text-lg font-medium'>{t(title)}</h3>
|
||||
<p className='text-gray-600'>{t(description)}</p>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
})}
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
76
src/widgets/cards-list.tsx
Normal file
76
src/widgets/cards-list.tsx
Normal file
@ -0,0 +1,76 @@
|
||||
'use client';
|
||||
|
||||
import { BarChart, CreditCard, Download, FileText } from 'lucide-react';
|
||||
|
||||
import { Button } from '@/shared/shadcn-ui/button';
|
||||
import { Card, CardContent } from '@/shared/shadcn-ui/card';
|
||||
|
||||
interface CardsListProps {
|
||||
totalCards: number;
|
||||
}
|
||||
|
||||
export const CardsList = ({ totalCards }: CardsListProps) => {
|
||||
return (
|
||||
<div className='mb-10'>
|
||||
<div className='mb-4 flex items-center justify-between'>
|
||||
<h2 className='text-2xl font-bold'>Топливные карты</h2>
|
||||
<div className='flex gap-2'>
|
||||
<Button variant='outline' className='gap-2'>
|
||||
<Download className='h-4 w-4' />
|
||||
Экспорт данных
|
||||
</Button>
|
||||
<Button variant='outline' className='gap-2'>
|
||||
<FileText className='h-4 w-4' />
|
||||
Отчеты
|
||||
</Button>
|
||||
<Button variant='outline' className='gap-2'>
|
||||
<BarChart className='h-4 w-4' />
|
||||
Аналитика
|
||||
</Button>
|
||||
</div>
|
||||
</div>
|
||||
<div className='grid grid-cols-1 gap-4 sm:grid-cols-2 md:grid-cols-3 lg:grid-cols-4'>
|
||||
{Array.from({ length: 4 }).map((_, index) => (
|
||||
<Card key={index} className='overflow-hidden'>
|
||||
<CardContent className='p-0'>
|
||||
<div className='bg-gradient-to-r from-gray-800 to-gray-900 p-4 text-white'>
|
||||
<div className='flex items-start justify-between'>
|
||||
<div>
|
||||
<p className='mb-1 text-xs text-gray-300'>
|
||||
Карта #{index + 1}
|
||||
</p>
|
||||
<p className='font-medium'>**** **** **** {1000 + index}</p>
|
||||
</div>
|
||||
<CreditCard className='h-6 w-6 text-gray-300' />
|
||||
</div>
|
||||
</div>
|
||||
<div className='p-4'>
|
||||
<div className='mb-2 flex items-center justify-between'>
|
||||
<p className='text-sm text-gray-500'>Лимит:</p>
|
||||
<p className='font-medium'>
|
||||
{(5000 * (index + 1)).toLocaleString()} сомони
|
||||
</p>
|
||||
</div>
|
||||
<div className='flex items-center justify-between'>
|
||||
<p className='text-sm text-gray-500'>Статус:</p>
|
||||
<span className='inline-flex items-center rounded-full bg-green-100 px-2.5 py-0.5 text-xs font-medium text-green-800'>
|
||||
Активна
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
</CardContent>
|
||||
</Card>
|
||||
))}
|
||||
{/* Show more cards button */}
|
||||
{totalCards > 4 && (
|
||||
<Button
|
||||
variant='outline'
|
||||
className='h-full min-h-[120px] border-dashed'
|
||||
>
|
||||
Показать все карты ({totalCards})
|
||||
</Button>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
@ -1,16 +1,22 @@
|
||||
'use client';
|
||||
|
||||
import { ChevronRight, Heart } from 'lucide-react';
|
||||
import Image from 'next/image';
|
||||
import Link from 'next/link';
|
||||
|
||||
import { useLanguage } from '@/shared/language';
|
||||
import { Button } from '@/shared/shadcn-ui/button';
|
||||
|
||||
export const CharitySection = () => {
|
||||
const { t } = useLanguage();
|
||||
|
||||
return (
|
||||
<section id='charity' className='py-16'>
|
||||
<section id='charity' className='px-2 py-8 sm:py-16'>
|
||||
<div className='container mx-auto'>
|
||||
<div className='grid items-center gap-12 md:grid-cols-2'>
|
||||
<div
|
||||
className='relative order-2 h-[400px] w-full overflow-hidden rounded-xl shadow-xl md:order-1'
|
||||
data-aos='zoom-in'
|
||||
className='relative order-2 h-[400px] overflow-hidden rounded-xl shadow-xl md:order-1'
|
||||
>
|
||||
<Image
|
||||
src='/placeholder.svg?height=400&width=600'
|
||||
@ -23,38 +29,34 @@ export const CharitySection = () => {
|
||||
<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'>
|
||||
Благотворительный фонд
|
||||
<h2 className='mb-6 text-xl font-bold tracking-tight sm:text-4xl'>
|
||||
{t('home.charity.title')}
|
||||
</h2>
|
||||
<p className='mb-6 text-gray-600'>
|
||||
Наш благотворительный фонд был создан для поддержки социально
|
||||
значимых проектов в Таджикистане. Мы стремимся внести свой вклад в
|
||||
развитие общества и помочь тем, кто в этом нуждается.
|
||||
</p>
|
||||
<p className='mb-6 text-gray-600'>
|
||||
Основные направления деятельности нашего фонда:
|
||||
{t('home.charity.description')}
|
||||
</p>
|
||||
<p className='mb-6 text-gray-600'>{t('home.charity.directions')}</p>
|
||||
<ul className='mb-6 space-y-2'>
|
||||
<li className='flex items-center'>
|
||||
<ChevronRight className='mr-2 h-5 w-5 text-red-600' />
|
||||
<span>Поддержка образовательных программ</span>
|
||||
<span>{t('home.charity.education')}</span>
|
||||
</li>
|
||||
<li className='flex items-center'>
|
||||
<ChevronRight className='mr-2 h-5 w-5 text-red-600' />
|
||||
<span>Помощь детям из малообеспеченных семей</span>
|
||||
<span>{t('home.charity.children')}</span>
|
||||
</li>
|
||||
<li className='flex items-center'>
|
||||
<ChevronRight className='mr-2 h-5 w-5 text-red-600' />
|
||||
<span>Экологические инициативы</span>
|
||||
<span>{t('home.charity.ecology')}</span>
|
||||
</li>
|
||||
<li className='flex items-center'>
|
||||
<ChevronRight className='mr-2 h-5 w-5 text-red-600' />
|
||||
<span>Поддержка спортивных мероприятий</span>
|
||||
<span>{t('home.charity.sports')}</span>
|
||||
</li>
|
||||
</ul>
|
||||
<Button className='bg-red-600 hover:bg-red-700'>
|
||||
Подробнее о фонде
|
||||
</Button>
|
||||
<Link href={'/charity'}>
|
||||
<Button>{t('home.charity.learnMore')}</Button>
|
||||
</Link>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
78
src/widgets/clients/ui/benefits-section.tsx
Normal file
78
src/widgets/clients/ui/benefits-section.tsx
Normal file
@ -0,0 +1,78 @@
|
||||
"use client"
|
||||
|
||||
import { useLanguage } from '@/shared/language';
|
||||
import { Percent } from 'lucide-react';
|
||||
import Image from 'next/image';
|
||||
|
||||
interface Benefit {
|
||||
title: string;
|
||||
description: string;
|
||||
}
|
||||
|
||||
const benefits: Array<Benefit> = [
|
||||
{
|
||||
title: 'Экономия',
|
||||
description: 'Скидки и бонусы для постоянных клиентов',
|
||||
},
|
||||
{
|
||||
title: 'Удобство',
|
||||
description: 'Быстрая оплата и обслуживание',
|
||||
},
|
||||
{
|
||||
title: 'Качество',
|
||||
description: 'Гарантированно высокое качество топлива',
|
||||
},
|
||||
{
|
||||
title: 'Дополнительные услуги',
|
||||
description: 'Кафе, магазины и другие услуги на наших заправках',
|
||||
},
|
||||
];
|
||||
|
||||
export const BenefitsSection = () => {
|
||||
|
||||
const {t} = useLanguage()
|
||||
|
||||
return (
|
||||
<section className='bg-gray-50 py-16'>
|
||||
<div className='container mx-auto'>
|
||||
<div className='grid items-center gap-12 md:grid-cols-2'>
|
||||
<div className='order-2 md:order-1'>
|
||||
<div className='mb-4 inline-flex items-center justify-center rounded-full bg-red-100 p-2'>
|
||||
<Percent className='h-6 w-6 text-red-600' />
|
||||
</div>
|
||||
<h2 className='mb-6 text-3xl font-bold tracking-tight sm:text-4xl'>
|
||||
{t('clients.benefits.title')}
|
||||
</h2>
|
||||
<p className='mb-6 text-gray-600'>
|
||||
{t('clients.benefits.subtitle')}
|
||||
</p>
|
||||
|
||||
<div className='space-y-4'>
|
||||
{benefits.map(({ title, description }) => {
|
||||
return (
|
||||
<div key={title} className='flex items-start'>
|
||||
<div className='mt-1 flex h-6 w-6 flex-shrink-0 items-center justify-center rounded-full bg-red-600'>
|
||||
<span className='text-xs text-white'>✓</span>
|
||||
</div>
|
||||
<div className='ml-3'>
|
||||
<h3 className='text-lg font-medium'>{title}</h3>
|
||||
<p className='text-gray-600'>{description}</p>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
})}
|
||||
</div>
|
||||
</div>
|
||||
<div className='relative order-1 h-[400px] overflow-hidden rounded-xl shadow-xl md:order-2'>
|
||||
<Image
|
||||
src='/placeholder.svg?height=400&width=600&text=Преимущества+для+клиентов'
|
||||
alt='Преимущества для клиентов'
|
||||
fill
|
||||
className='object-cover'
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
);
|
||||
};
|
||||
88
src/widgets/clients/ui/services-overview-section.tsx
Normal file
88
src/widgets/clients/ui/services-overview-section.tsx
Normal file
@ -0,0 +1,88 @@
|
||||
"use client"
|
||||
|
||||
import { CreditCard, type LucideProps, Percent, Wallet } from 'lucide-react';
|
||||
import { type ForwardRefExoticComponent, type RefAttributes } from 'react';
|
||||
|
||||
import {
|
||||
Card,
|
||||
CardContent,
|
||||
CardDescription,
|
||||
CardHeader,
|
||||
CardTitle,
|
||||
} from '@/shared/shadcn-ui/card';
|
||||
import { useLanguage } from '@/shared/language';
|
||||
|
||||
interface ServiceOverview {
|
||||
title: string;
|
||||
description: string;
|
||||
contentText: string;
|
||||
Icon: ForwardRefExoticComponent<
|
||||
Omit<LucideProps, 'ref'> & RefAttributes<SVGSVGElement>
|
||||
>;
|
||||
}
|
||||
|
||||
const servicesOverview: Array<ServiceOverview> = [
|
||||
{
|
||||
title: 'Программа лояльности',
|
||||
description: 'Накапливайте баллы и получайте скидки',
|
||||
contentText:
|
||||
'Наша программа лояльности позволяет накапливать баллы за каждую покупку и обменивать их на скидки и подарки.',
|
||||
Icon: Percent,
|
||||
},
|
||||
{
|
||||
title: 'Топливная карта',
|
||||
description: 'Удобный способ оплаты топлива',
|
||||
contentText:
|
||||
'Топливные карты для физических и юридических лиц. Контролируйте расходы и получайте дополнительные преимущества.',
|
||||
Icon: CreditCard,
|
||||
},
|
||||
{
|
||||
title: 'Способы оплаты',
|
||||
description: 'Различные способы оплаты на наших заправках',
|
||||
contentText:
|
||||
'Мы предлагаем различные способы оплаты: наличные, банковские карты, мобильные платежи и топливные карты.',
|
||||
Icon: Wallet,
|
||||
},
|
||||
];
|
||||
|
||||
export const ServicesOverviewSection = () => {
|
||||
|
||||
const {t} = useLanguage()
|
||||
|
||||
return (
|
||||
<section className='py-16'>
|
||||
<div className='container mx-auto'>
|
||||
<div className='mb-12 text-center'>
|
||||
<h2 className='mb-4 text-3xl font-bold tracking-tight sm:text-4xl'>
|
||||
{t('clients.services.title')}
|
||||
</h2>
|
||||
<p className='mx-auto max-w-2xl text-gray-600'>
|
||||
{t('clients.services.subtitle')}
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<div className='grid gap-3 md:grid-cols-2 md:gap-6 lg:grid-cols-3'>
|
||||
{servicesOverview.map(({ description, Icon, contentText, title }) => {
|
||||
return (
|
||||
<Card
|
||||
key={title}
|
||||
className='overflow-hidden transition-all hover:shadow-lg'
|
||||
>
|
||||
<CardHeader className='pb-3'>
|
||||
<div className='mb-4 flex h-12 w-12 items-center justify-center rounded-full bg-red-100'>
|
||||
<Icon className='h-6 w-6 text-red-600' />
|
||||
</div>
|
||||
<CardTitle>{title}</CardTitle>
|
||||
<CardDescription>{description}</CardDescription>
|
||||
</CardHeader>
|
||||
<CardContent className='text-sm text-gray-600'>
|
||||
<p>{contentText}</p>
|
||||
</CardContent>
|
||||
</Card>
|
||||
);
|
||||
})}
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
);
|
||||
};
|
||||
@ -1,22 +1,27 @@
|
||||
'use client';
|
||||
|
||||
import Link from 'next/link';
|
||||
|
||||
import { useLanguage } from '@/shared/language';
|
||||
import { Button } from '@/shared/shadcn-ui/button';
|
||||
|
||||
export const CtaSection = () => {
|
||||
const { t } = useLanguage();
|
||||
|
||||
return (
|
||||
<section className='bg-red-600 py-16 text-white'>
|
||||
<section className='bg-red-600 px-2 py-8 text-white sm:py-16'>
|
||||
<div className='container mx-auto'>
|
||||
<div className='flex flex-col items-center text-center'>
|
||||
<h2 className='mb-4 text-3xl font-bold tracking-tight sm:text-4xl'>
|
||||
Присоединяйтесь к нам
|
||||
{t('home.cta.title')}
|
||||
</h2>
|
||||
<p className='mb-8 max-w-2xl'>
|
||||
Станьте частью нашей сети. Получайте специальные предложения, бонусы
|
||||
и скидки.
|
||||
</p>
|
||||
<p className='mb-8 max-w-2xl'>{t('home.cta.description')}</p>
|
||||
<div className='flex flex-col gap-4 sm:flex-row'>
|
||||
<Button variant='outline'>Скачать приложение</Button>
|
||||
<Button className='bg-white text-red-600 hover:bg-gray-100'>
|
||||
Получить карту лояльности
|
||||
</Button>
|
||||
<Link href='#'>
|
||||
<Button variant='secondary'>
|
||||
{t('common.buttons.purchaseCardAtGasStations')}
|
||||
</Button>
|
||||
</Link>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@ -1,22 +1,23 @@
|
||||
'use client';
|
||||
|
||||
import { Fuel, Mail, MapPin, Phone } from 'lucide-react';
|
||||
import Link from 'next/link';
|
||||
|
||||
import { Button } from '@/shared/shadcn-ui/button';
|
||||
import { useLanguage } from '@/shared/language';
|
||||
|
||||
export const Footer = () => {
|
||||
const { t } = useLanguage();
|
||||
|
||||
return (
|
||||
<footer className='bg-gray-900 py-12 text-white'>
|
||||
<div className='container mx-auto'>
|
||||
<div className='grid grid-cols-1 gap-8 md:grid-cols-4'>
|
||||
<footer className='bg-gray-900 px-4 py-12 text-white'>
|
||||
<div className='containe mx-autor'>
|
||||
<div className='grid grid-cols-1 gap-8 md:grid-cols-3'>
|
||||
<div>
|
||||
<div className='mb-4 flex items-center gap-2'>
|
||||
<Fuel className='h-6 w-6 text-red-500' />
|
||||
<span className='text-xl font-bold'>GasNetwork</span>
|
||||
</div>
|
||||
<p className='mb-4 text-gray-400'>
|
||||
Сеть современных заправок в Таджикистане. Качественное топливо и
|
||||
отличный сервис.
|
||||
</p>
|
||||
<p className='mb-4 text-gray-400'>{t('home.hero.description')}</p>
|
||||
<div className='flex space-x-4'>
|
||||
<a href='#' className='text-gray-400 hover:text-white'>
|
||||
<svg
|
||||
@ -59,93 +60,70 @@ export const Footer = () => {
|
||||
</div>
|
||||
</div>
|
||||
<div>
|
||||
<h3 className='mb-4 text-lg font-semibold'>Контакты</h3>
|
||||
<h3 className='mb-4 text-lg font-semibold'>
|
||||
{t('common.footer.contacts')}
|
||||
</h3>
|
||||
<div className='space-y-3'>
|
||||
<div className='flex items-start'>
|
||||
<MapPin className='mt-0.5 mr-3 h-5 w-5 text-red-500' />
|
||||
<p>ул. Рудаки 137, Душанбе, Таджикистан</p>
|
||||
<p>{t('common.contacts.address')}</p>
|
||||
</div>
|
||||
<div className='flex items-start'>
|
||||
<Phone className='mt-0.5 mr-3 h-5 w-5 text-red-500' />
|
||||
<p>+992 (37) 223-45-67</p>
|
||||
<p>{t('common.contacts.tel')}</p>
|
||||
</div>
|
||||
<div className='flex items-start'>
|
||||
<Mail className='mt-0.5 mr-3 h-5 w-5 text-red-500' />
|
||||
<p>info@gasnetwork.tj</p>
|
||||
<p>{t('common.contacts.email')}</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div>
|
||||
<h3 className='mb-4 text-lg font-semibold'>Навигация</h3>
|
||||
<h3 className='mb-4 text-lg font-semibold'>
|
||||
{t('common.footer.navigation')}
|
||||
</h3>
|
||||
<ul className='space-y-2'>
|
||||
<li>
|
||||
<Link
|
||||
href='#stations'
|
||||
className='text-gray-400 hover:text-white'
|
||||
>
|
||||
Наши заправки
|
||||
<Link href='/' className='text-gray-400 hover:text-white'>
|
||||
{t('common.navigation.home')}
|
||||
</Link>
|
||||
</li>
|
||||
<li>
|
||||
<Link href='#about' className='text-gray-400 hover:text-white'>
|
||||
О нас
|
||||
<Link href='/about' className='text-gray-400 hover:text-white'>
|
||||
{t('common.navigation.about')}
|
||||
</Link>
|
||||
</li>
|
||||
<li>
|
||||
<Link
|
||||
href='#vacancies'
|
||||
href='/clients/loyalty'
|
||||
className='text-gray-400 hover:text-white'
|
||||
>
|
||||
Вакансии
|
||||
{t('common.navigation.clients')}
|
||||
</Link>
|
||||
</li>
|
||||
<li>
|
||||
<Link
|
||||
href='#promotions'
|
||||
href='/#stations'
|
||||
className='text-gray-400 hover:text-white'
|
||||
>
|
||||
Акции
|
||||
{t('common.navigation.stations')}
|
||||
</Link>
|
||||
</li>
|
||||
<li>
|
||||
<Link
|
||||
href='#partners'
|
||||
href='/#vacancies'
|
||||
className='text-gray-400 hover:text-white'
|
||||
>
|
||||
Партнеры
|
||||
</Link>
|
||||
</li>
|
||||
<li>
|
||||
<Link
|
||||
href='#charity'
|
||||
className='text-gray-400 hover:text-white'
|
||||
>
|
||||
Благотворительность
|
||||
{t('common.navigation.vacancies')}
|
||||
</Link>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
<div>
|
||||
<h3 className='mb-4 text-lg font-semibold'>Подписка</h3>
|
||||
<p className='mb-4 text-gray-400'>
|
||||
Подпишитесь на нашу рассылку, чтобы получать новости и специальные
|
||||
предложения.
|
||||
</p>
|
||||
<form className='space-y-2'>
|
||||
<input
|
||||
type='email'
|
||||
placeholder='Ваш email'
|
||||
className='w-full rounded-md border border-gray-700 bg-gray-800 px-4 py-2 text-white'
|
||||
/>
|
||||
<Button className='w-full bg-red-600 hover:bg-red-700'>
|
||||
Подписаться
|
||||
</Button>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
<div className='mt-8 border-t border-gray-800 pt-8 text-center text-gray-400'>
|
||||
<p>
|
||||
© {new Date().getFullYear()} GasNetwork. Все права защищены.
|
||||
© {new Date().getFullYear()} GasNetwork.{' '}
|
||||
{t('common.footer.rights')}
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@ -11,10 +11,14 @@ import {
|
||||
NavigationMenuTrigger,
|
||||
navigationMenuTriggerStyle,
|
||||
} from '@/shared/shadcn-ui/navigation-menu';
|
||||
import { useLanguage } from '@/shared/language';
|
||||
|
||||
export function DesktopNav() {
|
||||
|
||||
const { t } = useLanguage();
|
||||
|
||||
return (
|
||||
<NavigationMenu className='hidden md:flex'>
|
||||
<NavigationMenu className='hidden lg:flex'>
|
||||
<NavigationMenuList>
|
||||
<NavigationMenuItem>
|
||||
<Link href='/' scroll>
|
||||
@ -22,72 +26,55 @@ export function DesktopNav() {
|
||||
asChild
|
||||
className={navigationMenuTriggerStyle()}
|
||||
>
|
||||
<span>Главная</span>
|
||||
<span>{t('common.navigation.home')}</span>
|
||||
</NavigationMenuLink>
|
||||
</Link>
|
||||
</NavigationMenuItem>
|
||||
<NavigationMenuItem>
|
||||
<Link href='/about' scroll>
|
||||
<NavigationMenuLink
|
||||
asChild
|
||||
className={navigationMenuTriggerStyle()}
|
||||
>
|
||||
<span>О нас</span>
|
||||
</NavigationMenuLink>
|
||||
</Link>
|
||||
</NavigationMenuItem>
|
||||
<NavigationMenuItem>
|
||||
<NavigationMenuTrigger>Клиентам</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'>
|
||||
<NavigationMenuLink asChild>
|
||||
<a
|
||||
className='flex h-full w-full flex-col justify-end rounded-md bg-gradient-to-b from-red-500 to-red-700 p-6 no-underline outline-none select-none focus:shadow-md'
|
||||
href='/clients'
|
||||
>
|
||||
<div className='mt-4 mb-2 text-lg font-medium text-white'>
|
||||
Для наших клиентов
|
||||
</div>
|
||||
<p className='text-sm leading-tight text-white/90'>
|
||||
Специальные предложения, программы лояльности и удобные
|
||||
способы оплаты для наших клиентов
|
||||
</p>
|
||||
</a>
|
||||
</NavigationMenuLink>
|
||||
</li>
|
||||
<ListItem href='/clients/loyalty' title='Программа лояльности'>
|
||||
Накапливайте баллы и получайте скидки на топливо и услуги
|
||||
</ListItem>
|
||||
<ListItem href='/clients/fuel-card' title='Топливная карта'>
|
||||
Удобный способ оплаты топлива для физических и юридических лиц
|
||||
</ListItem>
|
||||
<ListItem href='/clients/certificates' title='Сертификаты'>
|
||||
Подарочные сертификаты на топливо и услуги нашей сети
|
||||
</ListItem>
|
||||
<ListItem href='/clients/payment' title='Способы оплаты'>
|
||||
Различные способы оплаты на наших заправочных станциях
|
||||
</ListItem>
|
||||
</ul>
|
||||
</NavigationMenuContent>
|
||||
</NavigationMenuItem>
|
||||
<NavigationMenuItem>
|
||||
<Link href='/#stations' scroll>
|
||||
<NavigationMenuLink
|
||||
asChild
|
||||
className={navigationMenuTriggerStyle()}
|
||||
>
|
||||
<span>Наши заправки</span>
|
||||
<span>{t('common.navigation.stations')}</span>
|
||||
</NavigationMenuLink>
|
||||
</Link>
|
||||
</NavigationMenuItem>
|
||||
<NavigationMenuItem>
|
||||
<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'>
|
||||
<NavigationMenuLink asChild>
|
||||
<Link
|
||||
className='flex h-full w-full flex-col justify-end rounded-md bg-gradient-to-b from-red-500 to-red-700 p-6 no-underline outline-none select-none focus:shadow-md'
|
||||
href='/clients'
|
||||
>
|
||||
<div className='mt-4 mb-2 text-lg font-medium text-white'>
|
||||
{t('clients.title')}
|
||||
</div>
|
||||
<p className='text-sm leading-tight text-white/90'>
|
||||
{t('clients.description')}
|
||||
</p>
|
||||
</Link>
|
||||
</NavigationMenuLink>
|
||||
</li>
|
||||
<ListItem href='/clients/loyalty' title='Программа лояльности'>
|
||||
Накапливайте баллы и получайте скидки на топливо и услуги
|
||||
</ListItem>
|
||||
<ListItem href='/clients/certificates' title='Сертификаты'>
|
||||
Подарочные сертификаты на топливо и услуги нашей сети
|
||||
</ListItem>
|
||||
</ul>
|
||||
</NavigationMenuContent>
|
||||
</NavigationMenuItem>
|
||||
<NavigationMenuItem>
|
||||
<Link href='/#vacancies' scroll={true}>
|
||||
<NavigationMenuLink
|
||||
asChild
|
||||
className={navigationMenuTriggerStyle()}
|
||||
>
|
||||
<span>Вакансии</span>
|
||||
<span>{t('common.navigation.vacancies')}</span>
|
||||
</NavigationMenuLink>
|
||||
</Link>
|
||||
</NavigationMenuItem>
|
||||
@ -97,7 +84,17 @@ export function DesktopNav() {
|
||||
asChild
|
||||
className={navigationMenuTriggerStyle()}
|
||||
>
|
||||
<span>Акции</span>
|
||||
<span>{t('common.navigation.promotions')}</span>
|
||||
</NavigationMenuLink>
|
||||
</Link>
|
||||
</NavigationMenuItem>
|
||||
<NavigationMenuItem>
|
||||
<Link href='/about' scroll>
|
||||
<NavigationMenuLink
|
||||
asChild
|
||||
className={navigationMenuTriggerStyle()}
|
||||
>
|
||||
<span>{t('common.navigation.about')}</span>
|
||||
</NavigationMenuLink>
|
||||
</Link>
|
||||
</NavigationMenuItem>
|
||||
|
||||
@ -1,22 +1,35 @@
|
||||
'use client';
|
||||
|
||||
import { UserCircle } from 'lucide-react';
|
||||
import Link from 'next/link';
|
||||
|
||||
import { Logo } from '@/shared/assets/logo';
|
||||
import { useLanguage } from '@/shared/language';
|
||||
// import { LanguageSwitcher } from '@/shared/language/ui/language-switcher';
|
||||
import { Button } from '@/shared/shadcn-ui/button';
|
||||
|
||||
import { DesktopNav } from './desktop-nav';
|
||||
import { MobileNav } from './mobile-nav';
|
||||
|
||||
export function Header() {
|
||||
const { t } = useLanguage();
|
||||
|
||||
return (
|
||||
<header className='sticky top-0 z-40 w-full border-b bg-white'>
|
||||
<div className='container mx-auto flex h-16 items-center justify-between p-4'>
|
||||
<Logo />
|
||||
<DesktopNav />
|
||||
<div className='flex items-center gap-6 md:contents'>
|
||||
<div className='flex items-center gap-3 sm:gap-6 lg:contents'>
|
||||
<MobileNav />
|
||||
<Link href={'/login'}>
|
||||
<Button>Вход</Button>
|
||||
</Link>
|
||||
<div className='flex items-center gap-3 sm:gap-6'>
|
||||
{/* <LanguageSwitcher /> */}
|
||||
<Link href={'/login'}>
|
||||
<Button className='flex items-center gap-2'>
|
||||
<UserCircle className='size-4' />
|
||||
{t('common.buttons.login')}
|
||||
</Button>
|
||||
</Link>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</header>
|
||||
|
||||
@ -19,7 +19,7 @@ export function MobileNav() {
|
||||
return (
|
||||
<Sheet open={open} onOpenChange={setOpen}>
|
||||
<SheetTrigger asChild>
|
||||
<Button variant='ghost' className='md:hidden' size='icon'>
|
||||
<Button variant='ghost' className='lg:hidden' size='icon'>
|
||||
<Menu className='h-6 w-6' />
|
||||
<span className='sr-only'>Открыть меню</span>
|
||||
</Button>
|
||||
|
||||
@ -1,40 +1,48 @@
|
||||
'use client';
|
||||
|
||||
import { MapPin } from 'lucide-react';
|
||||
import Image from 'next/image';
|
||||
import Link from 'next/link';
|
||||
|
||||
import { useLanguage } from '@/shared/language';
|
||||
import { Button } from '@/shared/shadcn-ui/button';
|
||||
|
||||
export const HeroSection = () => {
|
||||
const { t } = useLanguage();
|
||||
|
||||
return (
|
||||
<section className='relative'>
|
||||
<div className='relative h-[500px] w-full overflow-hidden'>
|
||||
<div className='relative h-[300px] w-full overflow-hidden md:h-[500px] xl:h-[700px]'>
|
||||
<Image
|
||||
src='/placeholder.svg?height=500&width=1920'
|
||||
src='/oriyo_bg.jpeg'
|
||||
alt='Gas Station Network'
|
||||
width={1920}
|
||||
height={500}
|
||||
fill
|
||||
className='object-cover'
|
||||
priority
|
||||
/>
|
||||
<div className='absolute inset-0 flex items-center bg-gradient-to-r from-black/70 to-black/30'>
|
||||
<div className='absolute inset-0 flex items-center bg-gradient-to-r from-black/70 to-black/30 px-2'>
|
||||
<div className='container mx-auto'>
|
||||
<div className='max-w-lg space-y-4 text-white'>
|
||||
<h1 className='text-4xl font-bold tracking-tight sm:text-5xl md:text-6xl'>
|
||||
Сеть современных заправок в Таджикистане
|
||||
</h1>
|
||||
<p className='text-lg text-gray-200'>
|
||||
Качественное топливо, удобное расположение и отличный сервис для
|
||||
наших клиентов
|
||||
<div className='animate-fade animate-duration-[3000ms] animate-ease-in-out'>
|
||||
<h1 className='text-2xl font-bold tracking-tight sm:text-4xl md:text-6xl'>
|
||||
{t('home.hero.title')}
|
||||
</h1>
|
||||
</div>
|
||||
<p className='text-gray-200 sm:text-lg'>
|
||||
{t('home.hero.description')}
|
||||
</p>
|
||||
<div className='flex gap-4'>
|
||||
<Button className='bg-red-600 hover:bg-red-700'>
|
||||
Найти заправку <MapPin className='ml-2 h-4 w-4' />
|
||||
</Button>
|
||||
<Button
|
||||
variant='outline'
|
||||
className='border-white text-white hover:bg-white/10'
|
||||
>
|
||||
Узнать больше
|
||||
</Button>
|
||||
<div className='flex flex-col gap-2 sm:flex-row sm:gap-4'>
|
||||
<Link href='#'>
|
||||
<Button className='bg-red-600 hover:bg-red-700'>
|
||||
{t('common.buttons.findStation')}{' '}
|
||||
<MapPin className='ml-2 h-4 w-4' />
|
||||
</Button>
|
||||
</Link>
|
||||
<Link href='#'>
|
||||
<Button variant='outline' className='animate-pulse'>
|
||||
{t('common.buttons.learnMore')}
|
||||
</Button>
|
||||
</Link>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@ -1,36 +1,34 @@
|
||||
import { ChevronRight, MapPin } from 'lucide-react';
|
||||
'use client';
|
||||
|
||||
import { MapPin } from 'lucide-react';
|
||||
|
||||
import { GasStationMap } from '@/features/map';
|
||||
|
||||
import { Button } from '@/shared/shadcn-ui/button';
|
||||
import { useLanguage } from '@/shared/language';
|
||||
|
||||
export const MapSection = () => {
|
||||
const { t } = useLanguage();
|
||||
|
||||
return (
|
||||
<section id='stations' className='bg-gray-50 py-16'>
|
||||
<section id='stations' 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'>
|
||||
<MapPin className='h-6 w-6 text-red-600' />
|
||||
</div>
|
||||
<h2 className='mb-4 text-3xl font-bold tracking-tight sm:text-4xl'>
|
||||
Наши заправки
|
||||
{t('home.stations.title')}
|
||||
</h2>
|
||||
<p className='max-w-2xl text-gray-600'>
|
||||
Найдите ближайшую к вам заправку нашей сети. Мы расположены в
|
||||
удобных местах по всему Таджикистану.
|
||||
{t('home.stations.description')}
|
||||
</p>
|
||||
</div>
|
||||
<div
|
||||
data-aos='fade-up'
|
||||
className='h-[500px] overflow-hidden rounded-xl border shadow-lg'
|
||||
data-aos='fade-up'
|
||||
>
|
||||
<GasStationMap />
|
||||
</div>
|
||||
<div className='mt-8 flex justify-center'>
|
||||
<Button className='bg-red-600 hover:bg-red-700'>
|
||||
Показать все заправки <ChevronRight className='ml-2 h-4 w-4' />
|
||||
</Button>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
);
|
||||
|
||||
@ -1,31 +1,36 @@
|
||||
'use client';
|
||||
|
||||
import { Handshake } from 'lucide-react';
|
||||
import Image from 'next/image';
|
||||
import Link from 'next/link';
|
||||
|
||||
import { useLanguage } from '@/shared/language';
|
||||
import { Button } from '@/shared/shadcn-ui/button';
|
||||
|
||||
export const PartnersSection = () => {
|
||||
const { t } = useLanguage();
|
||||
|
||||
return (
|
||||
<section id='partners' className='bg-gray-50 py-16'>
|
||||
<section id='partners' 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'>
|
||||
<Handshake className='h-6 w-6 text-red-600' />
|
||||
</div>
|
||||
<h2 className='mb-4 text-3xl font-bold tracking-tight sm:text-4xl'>
|
||||
Наши партнеры
|
||||
{t('home.partners.title')}
|
||||
</h2>
|
||||
<p className='max-w-2xl text-gray-600'>
|
||||
Мы сотрудничаем с ведущими компаниями для предоставления лучших
|
||||
услуг нашим клиентам.
|
||||
{t('home.partners.description')}
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<div className='grid grid-cols-2 gap-8 md:grid-cols-4'>
|
||||
<div className='grid grid-cols-2 gap-4 sm:gap-8 md:grid-cols-4'>
|
||||
{[1, 2, 3, 4, 5, 6, 7, 8].map((partner) => (
|
||||
<div
|
||||
key={partner}
|
||||
className='flex h-32 items-center justify-center rounded-lg bg-white p-6 shadow-md transition-transform hover:scale-105'
|
||||
data-aos='flip-up'
|
||||
className='flex h-32 flex-col items-center justify-center gap-0.5 rounded-lg bg-white p-6 shadow-md transition-transform hover:scale-105'
|
||||
data-aos='flip-left'
|
||||
>
|
||||
<Image
|
||||
src={`/placeholder.svg?height=80&width=160&text=Partner ${partner}`}
|
||||
@ -34,19 +39,23 @@ export const PartnersSection = () => {
|
||||
height={80}
|
||||
className='max-h-16 w-auto'
|
||||
/>
|
||||
<h4 className='font-extralight'>Название</h4>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
|
||||
<div className='mt-12 text-center'>
|
||||
<h3 className='mb-4 text-xl font-bold'>Станьте нашим партнером</h3>
|
||||
<h3 className='mb-4 text-xl font-bold'>
|
||||
{t('home.partners.becomePartner')}
|
||||
</h3>
|
||||
<p className='mx-auto mb-6 max-w-2xl text-gray-600'>
|
||||
Мы открыты для сотрудничества и новых партнерских отношений.
|
||||
Свяжитесь с нами для обсуждения возможностей.
|
||||
{t('home.partners.becomePartnerText')}
|
||||
</p>
|
||||
<Button className='bg-red-600 hover:bg-red-700'>
|
||||
Связаться с нами
|
||||
</Button>
|
||||
<Link href='#'>
|
||||
<Button className='bg-red-600 hover:bg-red-700'>
|
||||
{t('common.buttons.contactUs')}
|
||||
</Button>
|
||||
</Link>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
@ -1,30 +1,28 @@
|
||||
import { ArrowRight, Gift } from 'lucide-react';
|
||||
'use client';
|
||||
|
||||
import { Gift } from 'lucide-react';
|
||||
|
||||
import PromotionSlider from '@/shared/components/promotion-slider';
|
||||
import { Button } from '@/shared/shadcn-ui/button';
|
||||
import { useLanguage } from '@/shared/language';
|
||||
|
||||
export const PromotionsSection = () => {
|
||||
const { t } = useLanguage();
|
||||
|
||||
return (
|
||||
<section id='promotions' className='bg-gray-50 py-16'>
|
||||
<section id='promotions' className='bg-gray-50 py-8 px-2 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'>
|
||||
<Gift className='h-6 w-6 text-red-600' />
|
||||
</div>
|
||||
<h2 className='mb-4 text-3xl font-bold tracking-tight sm:text-4xl'>
|
||||
Актуальные акции
|
||||
{t('home.promotions.title')}
|
||||
</h2>
|
||||
<p className='max-w-2xl text-gray-600'>
|
||||
Специальные предложения и акции для наших клиентов. Заправляйтесь
|
||||
выгодно!
|
||||
{t('home.promotions.description')}
|
||||
</p>
|
||||
</div>
|
||||
<PromotionSlider />
|
||||
<div className='mt-8 flex justify-center'>
|
||||
<Button className='bg-red-600 hover:bg-red-700'>
|
||||
Все акции <ArrowRight className='ml-2 h-4 w-4' />
|
||||
</Button>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
);
|
||||
|
||||
@ -3,11 +3,14 @@
|
||||
import { useEffect, useRef, useState } from 'react';
|
||||
|
||||
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()
|
||||
|
||||
useEffect(() => {
|
||||
const observer = new IntersectionObserver(
|
||||
(entries) => {
|
||||
@ -32,32 +35,32 @@ export function StatsSection() {
|
||||
}, []);
|
||||
|
||||
return (
|
||||
<section ref={sectionRef} className='bg-red-600 py-12 text-white'>
|
||||
<section ref={sectionRef} className='bg-red-600 py-6 sm:py-12 px-2 text-white'>
|
||||
<div className='container mx-auto'>
|
||||
<div className='grid grid-cols-2 gap-8 text-center md:grid-cols-4'>
|
||||
<div className='grid grid-cols-2 gap-4 sm:gap-8 text-center md:grid-cols-4'>
|
||||
<div className='space-y-2'>
|
||||
<h3 className='text-3xl font-bold'>
|
||||
{isVisible ? <AnimatedCounter end={25} suffix='+' /> : '0+'}
|
||||
</h3>
|
||||
<p className='text-sm text-white/80'>Заправок по стране</p>
|
||||
<p className='text-sm text-white/80'>{t('home.stats.stations')}</p>
|
||||
</div>
|
||||
<div className='space-y-2'>
|
||||
<h3 className='text-3xl font-bold'>
|
||||
{isVisible ? <AnimatedCounter end={10000} suffix='+' /> : '0+'}
|
||||
</h3>
|
||||
<p className='text-sm text-white/80'>Клиентов ежедневно</p>
|
||||
<p className='text-sm text-white/80'>{t('home.stats.daily')}</p>
|
||||
</div>
|
||||
<div className='space-y-2'>
|
||||
<h3 className='text-3xl font-bold'>
|
||||
{isVisible ? <AnimatedCounter end={15} /> : '0'}
|
||||
</h3>
|
||||
<p className='text-sm text-white/80'>Лет на рынке</p>
|
||||
<p className='text-sm text-white/80'>{t('home.stats.years')}</p>
|
||||
</div>
|
||||
<div className='space-y-2'>
|
||||
<h3 className='text-3xl font-bold'>
|
||||
{isVisible ? <AnimatedCounter end={24} suffix='/7' /> : '0/7'}
|
||||
</h3>
|
||||
<p className='text-sm text-white/80'>Работаем круглосуточно</p>
|
||||
<p className='text-sm text-white/80'>{t('home.stats.mode')}</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
216
src/widgets/transactions-table.tsx
Normal file
216
src/widgets/transactions-table.tsx
Normal file
@ -0,0 +1,216 @@
|
||||
'use client';
|
||||
|
||||
import { format, subMonths } from 'date-fns';
|
||||
import { ru } from 'date-fns/locale';
|
||||
import { CalendarIcon } from 'lucide-react';
|
||||
import { useState } from 'react';
|
||||
|
||||
import { Button } from '@/shared/shadcn-ui/button';
|
||||
import { Calendar } from '@/shared/shadcn-ui/calendar';
|
||||
import { Label } from '@/shared/shadcn-ui/label';
|
||||
import {
|
||||
Popover,
|
||||
PopoverContent,
|
||||
PopoverTrigger,
|
||||
} from '@/shared/shadcn-ui/popover';
|
||||
import {
|
||||
Table,
|
||||
TableBody,
|
||||
TableCell,
|
||||
TableHead,
|
||||
TableHeader,
|
||||
TableRow,
|
||||
} from '@/shared/shadcn-ui/table';
|
||||
|
||||
// Sample customer data
|
||||
const customerData = {
|
||||
firstName: 'Алишер',
|
||||
lastName: 'Рахмонов',
|
||||
passportNumber: 'A12345678',
|
||||
bonusPoints: 1250,
|
||||
cardNumber: '5678-9012-3456-7890',
|
||||
expiryDate: '12/2025',
|
||||
registrationDate: '15.06.2020',
|
||||
};
|
||||
|
||||
// Sample transaction data
|
||||
const generateTransactions = () => {
|
||||
const stations = [
|
||||
'АЗС Душанбе-Центр',
|
||||
'АЗС Душанбе-Запад',
|
||||
'АЗС Душанбе-Восток',
|
||||
'АЗС Худжанд',
|
||||
'АЗС Куляб',
|
||||
];
|
||||
|
||||
const products = [
|
||||
{ name: 'ДТ', price: 8.5 },
|
||||
{ name: 'АИ-92', price: 9.2 },
|
||||
{ name: 'АИ-95', price: 10.5 },
|
||||
{ name: 'Z-100 Power', price: 11.8 },
|
||||
{ name: 'Пропан', price: 6.3 },
|
||||
];
|
||||
|
||||
const transactions = [];
|
||||
|
||||
// Generate 50 random transactions over the last 6 months
|
||||
for (let i = 0; i < 50; i++) {
|
||||
const date = subMonths(new Date(), Math.random() * 6);
|
||||
const station = stations[Math.floor(Math.random() * stations.length)];
|
||||
const product = products[Math.floor(Math.random() * products.length)];
|
||||
const quantity = Math.floor(Math.random() * 40) + 10; // 10-50 liters
|
||||
const cost = product.price;
|
||||
const total = quantity * cost;
|
||||
|
||||
transactions.push({
|
||||
id: i + 1,
|
||||
date,
|
||||
station,
|
||||
product: product.name,
|
||||
quantity,
|
||||
cost,
|
||||
total,
|
||||
});
|
||||
}
|
||||
|
||||
// Sort by date (newest first)
|
||||
return transactions.sort((a, b) => b.date.getTime() - a.date.getTime());
|
||||
};
|
||||
|
||||
const transactions = generateTransactions();
|
||||
|
||||
export const TransactionsTable = () => {
|
||||
const [startDate, setStartDate] = useState<Date | undefined>(
|
||||
subMonths(new Date(), 1),
|
||||
);
|
||||
const [endDate, setEndDate] = useState<Date | undefined>(new Date());
|
||||
const [filteredTransactions, setFilteredTransactions] =
|
||||
useState(transactions);
|
||||
|
||||
// Filter transactions by date range
|
||||
const filterTransactions = () => {
|
||||
if (!startDate || !endDate) return;
|
||||
|
||||
const filtered = transactions.filter((transaction) => {
|
||||
const transactionDate = new Date(transaction.date);
|
||||
return transactionDate >= startDate && transactionDate <= endDate;
|
||||
});
|
||||
|
||||
setFilteredTransactions(filtered);
|
||||
};
|
||||
|
||||
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'>История операций</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'>От</Label>
|
||||
<Popover>
|
||||
<PopoverTrigger asChild>
|
||||
<Button
|
||||
variant='outline'
|
||||
className='w-full justify-start text-left font-normal'
|
||||
>
|
||||
<CalendarIcon className='mr-2 h-4 w-4' />
|
||||
{startDate
|
||||
? format(startDate, 'PP', { locale: ru })
|
||||
: 'Выберите дату'}
|
||||
</Button>
|
||||
</PopoverTrigger>
|
||||
<PopoverContent className='w-auto p-0'>
|
||||
<Calendar
|
||||
mode='single'
|
||||
selected={startDate}
|
||||
onSelect={setStartDate}
|
||||
initialFocus
|
||||
/>
|
||||
</PopoverContent>
|
||||
</Popover>
|
||||
</div>
|
||||
|
||||
<div className='flex items-center gap-2'>
|
||||
<Label htmlFor='end-date'>До</Label>
|
||||
<Popover>
|
||||
<PopoverTrigger asChild>
|
||||
<Button
|
||||
variant='outline'
|
||||
className='w-full justify-start text-left font-normal'
|
||||
>
|
||||
<CalendarIcon className='mr-2 h-4 w-4' />
|
||||
{endDate
|
||||
? format(endDate, 'PP', { locale: ru })
|
||||
: 'Выберите дату'}
|
||||
</Button>
|
||||
</PopoverTrigger>
|
||||
<PopoverContent className='w-auto p-0'>
|
||||
<Calendar
|
||||
mode='single'
|
||||
selected={endDate}
|
||||
onSelect={setEndDate}
|
||||
initialFocus
|
||||
/>
|
||||
</PopoverContent>
|
||||
</Popover>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<Button
|
||||
className='mt-auto bg-red-600 hover:bg-red-700'
|
||||
onClick={filterTransactions}
|
||||
>
|
||||
Применить
|
||||
</Button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className='rounded-md border'>
|
||||
<Table>
|
||||
<TableHeader>
|
||||
<TableRow>
|
||||
<TableHead>Дата</TableHead>
|
||||
<TableHead>Станция</TableHead>
|
||||
<TableHead>Продукт</TableHead>
|
||||
<TableHead className='text-right'>Кол-во (л)</TableHead>
|
||||
<TableHead className='text-right'>Стоимость</TableHead>
|
||||
<TableHead className='text-right'>Сумма</TableHead>
|
||||
</TableRow>
|
||||
</TableHeader>
|
||||
<TableBody>
|
||||
{filteredTransactions.length > 0 ? (
|
||||
filteredTransactions.map((transaction) => (
|
||||
<TableRow key={transaction.id}>
|
||||
<TableCell>
|
||||
{format(transaction.date, 'dd.MM.yyyy')}
|
||||
</TableCell>
|
||||
<TableCell>{transaction.station}</TableCell>
|
||||
<TableCell>{transaction.product}</TableCell>
|
||||
<TableCell className='text-right'>
|
||||
{transaction.quantity}
|
||||
</TableCell>
|
||||
<TableCell className='text-right'>
|
||||
{transaction.cost.toFixed(2)} сомони
|
||||
</TableCell>
|
||||
<TableCell className='text-right font-medium'>
|
||||
{transaction.total.toFixed(2)} сомони
|
||||
</TableCell>
|
||||
</TableRow>
|
||||
))
|
||||
) : (
|
||||
<TableRow>
|
||||
<TableCell
|
||||
colSpan={6}
|
||||
className='py-6 text-center text-gray-500'
|
||||
>
|
||||
Нет операций за выбранный период
|
||||
</TableCell>
|
||||
</TableRow>
|
||||
)}
|
||||
</TableBody>
|
||||
</Table>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
@ -1,5 +1,10 @@
|
||||
import { ArrowRight, Briefcase } from 'lucide-react';
|
||||
'use client';
|
||||
|
||||
import { Briefcase } from 'lucide-react';
|
||||
|
||||
import { useLanguage } from '@/shared/language';
|
||||
import { cn } from '@/shared/lib/utils';
|
||||
import { Badge } from '@/shared/shadcn-ui/badge';
|
||||
import { Button } from '@/shared/shadcn-ui/button';
|
||||
import { Card, CardContent } from '@/shared/shadcn-ui/card';
|
||||
import {
|
||||
@ -10,27 +15,28 @@ import {
|
||||
} from '@/shared/shadcn-ui/tabs';
|
||||
|
||||
export const VacanciesSection = () => {
|
||||
const { t } = useLanguage();
|
||||
|
||||
return (
|
||||
<section id='vacancies' className='py-16'>
|
||||
<section id='vacancies' className='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'>
|
||||
<Briefcase className='h-6 w-6 text-red-600' />
|
||||
</div>
|
||||
<h2 className='mb-4 text-3xl font-bold tracking-tight sm:text-4xl'>
|
||||
Вакансии
|
||||
{t('home.vacancies.title')}
|
||||
</h2>
|
||||
<p className='max-w-2xl text-gray-600'>
|
||||
Присоединяйтесь к нашей команде профессионалов. Мы предлагаем
|
||||
стабильную работу и возможности для роста.
|
||||
{t('home.vacancies.description')}
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<Tabs defaultValue='all' className='mx-auto w-full max-w-3xl'>
|
||||
<TabsList className='mb-8 grid grid-cols-3'>
|
||||
<TabsTrigger value='all'>Все вакансии</TabsTrigger>
|
||||
<TabsTrigger value='office'>Офис</TabsTrigger>
|
||||
<TabsTrigger value='stations'>Заправки</TabsTrigger>
|
||||
<TabsTrigger value='all'>{t('home.vacancies.all')}</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'>
|
||||
{[
|
||||
@ -39,35 +45,12 @@ export const VacanciesSection = () => {
|
||||
'Бухгалтер',
|
||||
'Специалист по логистике',
|
||||
].map((job, index) => (
|
||||
<Card
|
||||
<Vacancy
|
||||
key={index}
|
||||
className='overflow-hidden transition-all hover:shadow-md'
|
||||
data-aos={index % 2 === 0 ? 'fade-right' : 'fade-left'}
|
||||
>
|
||||
<CardContent className='p-0'>
|
||||
<div className='p-6'>
|
||||
<div className='flex items-start justify-between'>
|
||||
<div>
|
||||
<h3 className='mb-2 text-lg font-bold'>{job}</h3>
|
||||
<p className='mb-4 text-sm text-gray-500'>
|
||||
Душанбе, Таджикистан
|
||||
</p>
|
||||
<div className='mb-4 flex flex-wrap gap-2'>
|
||||
<span className='inline-flex items-center rounded-full bg-gray-100 px-2.5 py-0.5 text-xs font-medium text-gray-800'>
|
||||
Полный день
|
||||
</span>
|
||||
<span className='inline-flex items-center rounded-full bg-red-100 px-2.5 py-0.5 text-xs font-medium text-red-800'>
|
||||
Опыт от 1 года
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
<Button variant='outline' size='sm'>
|
||||
Подробнее
|
||||
</Button>
|
||||
</div>
|
||||
</div>
|
||||
</CardContent>
|
||||
</Card>
|
||||
jobTitle={job}
|
||||
location='Душанбе, Таджикистан'
|
||||
tags={['Полный день', 'Опыт от 1 года']}
|
||||
/>
|
||||
))}
|
||||
</TabsContent>
|
||||
<TabsContent value='office' className='space-y-4'>
|
||||
@ -76,78 +59,74 @@ export const VacanciesSection = () => {
|
||||
'Бухгалтер',
|
||||
'Специалист по логистике',
|
||||
].map((job, index) => (
|
||||
<Card
|
||||
<Vacancy
|
||||
key={index}
|
||||
className='overflow-hidden transition-all hover:shadow-md'
|
||||
>
|
||||
<CardContent className='p-0'>
|
||||
<div className='p-6'>
|
||||
<div className='flex items-start justify-between'>
|
||||
<div>
|
||||
<h3 className='mb-2 text-lg font-bold'>{job}</h3>
|
||||
<p className='mb-4 text-sm text-gray-500'>
|
||||
Душанбе, Таджикистан
|
||||
</p>
|
||||
<div className='mb-4 flex flex-wrap gap-2'>
|
||||
<span className='inline-flex items-center rounded-full bg-gray-100 px-2.5 py-0.5 text-xs font-medium text-gray-800'>
|
||||
Полный день
|
||||
</span>
|
||||
<span className='inline-flex items-center rounded-full bg-red-100 px-2.5 py-0.5 text-xs font-medium text-red-800'>
|
||||
Опыт от 1 года
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
<Button variant='outline' size='sm'>
|
||||
Подробнее
|
||||
</Button>
|
||||
</div>
|
||||
</div>
|
||||
</CardContent>
|
||||
</Card>
|
||||
jobTitle={job}
|
||||
location='Душанбе, Таджикистан'
|
||||
tags={['Полный день', 'Опыт от 1 года']}
|
||||
/>
|
||||
))}
|
||||
</TabsContent>
|
||||
<TabsContent value='stations' className='space-y-4'>
|
||||
{['Оператор АЗС', 'Заправщик', 'Менеджер станции'].map(
|
||||
(job, index) => (
|
||||
<Card
|
||||
<Vacancy
|
||||
key={index}
|
||||
className='overflow-hidden transition-all hover:shadow-md'
|
||||
>
|
||||
<CardContent className='p-0'>
|
||||
<div className='p-6'>
|
||||
<div className='flex items-start justify-between'>
|
||||
<div>
|
||||
<h3 className='mb-2 text-lg font-bold'>{job}</h3>
|
||||
<p className='mb-4 text-sm text-gray-500'>
|
||||
Душанбе, Таджикистан
|
||||
</p>
|
||||
<div className='mb-4 flex flex-wrap gap-2'>
|
||||
<span className='inline-flex items-center rounded-full bg-gray-100 px-2.5 py-0.5 text-xs font-medium text-gray-800'>
|
||||
Сменный график
|
||||
</span>
|
||||
<span className='inline-flex items-center rounded-full bg-red-100 px-2.5 py-0.5 text-xs font-medium text-red-800'>
|
||||
Обучение
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
<Button variant='outline' size='sm'>
|
||||
Подробнее
|
||||
</Button>
|
||||
</div>
|
||||
</div>
|
||||
</CardContent>
|
||||
</Card>
|
||||
jobTitle={job}
|
||||
location='Душанбе, Таджикистан'
|
||||
tags={['Сменный график', 'Обучение']}
|
||||
/>
|
||||
),
|
||||
)}
|
||||
</TabsContent>
|
||||
</Tabs>
|
||||
|
||||
<div className='mt-8 flex justify-center'>
|
||||
<Button className='bg-red-600 hover:bg-red-700'>
|
||||
Отправить резюме <ArrowRight className='ml-2 h-4 w-4' />
|
||||
</Button>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
);
|
||||
};
|
||||
|
||||
interface VacancyProps {
|
||||
jobTitle: string;
|
||||
location: string;
|
||||
tags: Array<string>;
|
||||
}
|
||||
|
||||
const Vacancy = ({ jobTitle, location, tags }: VacancyProps) => {
|
||||
const { t } = useLanguage();
|
||||
|
||||
return (
|
||||
<Card
|
||||
className='overflow-hidden transition-all hover:shadow-md'
|
||||
data-aos='zoom-in'
|
||||
>
|
||||
<CardContent className='p-0'>
|
||||
<div className='p-6'>
|
||||
<div className='flex flex-col items-start justify-between sm:flex-row'>
|
||||
<div>
|
||||
<h3 className='mb-2 text-lg font-bold'>{jobTitle}</h3>
|
||||
<p className='mb-4 text-sm text-gray-500'>{location}</p>
|
||||
<div className='mb-4 flex flex-wrap gap-2'>
|
||||
{tags.map((tag, index) => {
|
||||
return (
|
||||
<Badge
|
||||
key={index}
|
||||
className={cn(
|
||||
'inline-flex items-center rounded-full bg-gray-100 px-2.5 py-0.5 text-xs font-medium',
|
||||
`${index % 2 === 0 ? 'bg-gray-100 text-gray-800' : 'bg-red-100 text-red-800'}`,
|
||||
)}
|
||||
>
|
||||
{tag}
|
||||
</Badge>
|
||||
);
|
||||
})}
|
||||
</div>
|
||||
</div>
|
||||
<Button variant='outline' size='sm'>
|
||||
{t('common.buttons.apply')}
|
||||
</Button>
|
||||
</div>
|
||||
</div>
|
||||
</CardContent>
|
||||
</Card>
|
||||
);
|
||||
};
|
||||
|
||||
@ -1,4 +1,5 @@
|
||||
/** @type {import('tailwindcss').Config} */
|
||||
import tailwindcssAnimated from 'tailwindcss-animated';
|
||||
|
||||
module.exports = {
|
||||
darkMode: ['class'],
|
||||
@ -102,5 +103,5 @@ module.exports = {
|
||||
},
|
||||
},
|
||||
},
|
||||
plugins: [require('tailwindcss-animate')],
|
||||
plugins: [require('tailwindcss-animate'), tailwindcssAnimated],
|
||||
};
|
||||
|
||||
@ -24,7 +24,8 @@
|
||||
"@/entities/*": ["./src/entities/*"],
|
||||
"@/features/*": ["./src/features/*"],
|
||||
"@/shared/*": ["./src/shared/*"],
|
||||
"@/widgets/*": ["./src/widgets/*"]
|
||||
"@/widgets/*": ["./src/widgets/*"],
|
||||
"@/pages-templates/*": ["./src/pages-templates/*"]
|
||||
}
|
||||
},
|
||||
"include": ["next-env.d.ts", "**/*.ts", "**/*.tsx", ".next/types/**/*.ts"],
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user