diff --git a/package.json b/package.json index 8971d74..2c17b8a 100644 --- a/package.json +++ b/package.json @@ -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" }, diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 2a28b61..94c0c7a 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -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: {} diff --git a/public/clients/loyalty-card.png b/public/clients/loyalty-card.png new file mode 100644 index 0000000..77d7665 Binary files /dev/null and b/public/clients/loyalty-card.png differ diff --git a/public/oriyo_bg.jpeg b/public/oriyo_bg.jpeg new file mode 100644 index 0000000..77ed27a Binary files /dev/null and b/public/oriyo_bg.jpeg differ diff --git a/src/app/(dashboard)/corporate-dashboard/page.tsx b/src/app/(dashboard)/corporate-dashboard/page.tsx new file mode 100644 index 0000000..ca8ed83 --- /dev/null +++ b/src/app/(dashboard)/corporate-dashboard/page.tsx @@ -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( + subMonths(new Date(), 1), + ); + const [endDate, setEndDate] = useState(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 ( +
+
+
+
+

Корпоративный кабинет

+ +
+ +
+ {/* Company Card */} + + + + + Информация о компании + + + +
+
+
+

Название компании

+

{companyData.companyName}

+
+
+

Количество карт

+

{companyData.numberOfCards}

+
+
+

Дата регистрации

+

+ {companyData.registrationDate} +

+
+
+
+
+

Фонд

+

+ {companyData.fund.toLocaleString()} сомони +

+
+
+

Овердрафт

+

+ {companyData.overdraft.toLocaleString()} сомони +

+
+
+
+
+
+ + {/* Fund Card */} + + + + + Общий фонд + + + Доступные средства + + + +
+

+ {companyData.totalFund.toLocaleString()} +

+

сомони

+
+
+
+
+ + {/* */} + + {/* Transactions */} +
+
+

История операций

+ +
+
+
+ + + + + + + + + +
+ +
+ + + + + + + + + +
+
+ + +
+
+ +
+ + + + Дата + Станция + Продукт + Кол-во (л) + Стоимость + Сумма + + + + {filteredTransactions.length > 0 ? ( + filteredTransactions.map((transaction) => ( + + + {format(transaction.date, 'dd.MM.yyyy')} + + {transaction.station} + {transaction.product} + + {transaction.quantity} + + + {transaction.cost.toFixed(2)} сомони + + + {transaction.total.toFixed(2)} сомони + + + )) + ) : ( + + + Нет операций за выбранный период + + + )} + +
+
+
+
+
+
+ ); +} diff --git a/src/app/(dashboard)/customer-dashboard/page.tsx b/src/app/(dashboard)/customer-dashboard/page.tsx new file mode 100644 index 0000000..aa2f7ea --- /dev/null +++ b/src/app/(dashboard)/customer-dashboard/page.tsx @@ -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 ( +
+
+
+
+

Личный кабинет

+ +
+ +
+ {/* Bonus Card */} + + + + + Бонусная карта + + + Ваши накопленные бонусы + + + +
+

+ {customerData.bonusPoints} +

+

бонусных баллов

+
+
+
+ + Действует до: 31.12.2023 +
+ +
+
+
+ {/* Customer Card */} + + + + + Информация о клиенте + + + +
+
+
+

ФИО

+

+ {customerData.firstName} {customerData.lastName} +

+
+
+

Дата регистрации

+

+ {customerData.registrationDate} +

+
+
+
+
+

Номер карты

+

{customerData.cardNumber}

+
+
+

Срок действия

+

{customerData.expiryDate}

+
+
+
+
+
+
+ + +
+
+
+ ); +} diff --git a/src/app/about/page.tsx b/src/app/about/page.tsx index 14fa821..418c7de 100644 --- a/src/app/about/page.tsx +++ b/src/app/about/page.tsx @@ -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 ( -
-
- {/* Hero Section */} -
-
- О нашей компании -
-
-
-

- О нашей компании -

-

- Узнайте больше о нашей истории, ценностях и миссии. Мы - стремимся предоставлять лучший сервис и качественное топливо - для наших клиентов. -

-
-
-
-
-
- - {/* Company Overview */} -
-
-
-
-
- -
-

- Лидер на рынке Таджикистана -

-

- GasNetwork - ведущая сеть автозаправочных станций в - Таджикистане, предоставляющая высококачественное топливо и - превосходный сервис. Наша компания была основана в 2008 году и - с тех пор стала символом надежности и качества в - энергетическом секторе страны. -

-

- Мы гордимся тем, что предлагаем нашим клиентам только лучшее - топливо, соответствующее международным стандартам качества. - Наши заправочные станции оснащены современным оборудованием, - которое обеспечивает быстрое и безопасное обслуживание. -

-

- Наша миссия - сделать поездки наших клиентов комфортными и - безопасными, предоставляя качественное топливо и отличный - сервис по всей стране. -

- -
-
-
- -
-
-

Качество

-

Топливо высшего стандарта

-
-
-
-
- -
-
-

Сервис

-

- Профессиональное обслуживание -

-
-
-
-
- -
-
-

Инновации

-

Современные технологии

-
-
-
-
- -
-
-

Доступность

-

Станции по всей стране

-
-
-
-
-
- Главный офис GasNetwork -
-
-
-
- - {/* Stats Section */} -
-
-
-

- GasNetwork в цифрах -

-

- Наши достижения и рост за годы работы на рынке Таджикистана -

-
-
-
-

- -

-

Заправок по стране

-
-
-

- -

-

Лет на рынке

-
-
-

- -

-

Сотрудников

-
-
-

- -

-

Клиентов в год

-
-
-
-
- - {/* Our History */} -
-
-
-
- -
-

- Наша история -

-

- История развития нашей компании от небольшой сети до лидера - рынка -

-
- - -
-
- - {/* Our Stations */} -
-
-
-
- -
-

- Наши заправочные станции -

-

- Современные заправочные станции, оснащенные по последнему слову - техники -

-
- - - -
-

- Наши заправочные станции расположены в стратегически важных - точках по всему Таджикистану, обеспечивая удобный доступ для - всех наших клиентов. -

- -
-
-
- - {/* Our Values */} -
-
-
-
- -
-

- Наши ценности -

-

- Принципы, которыми мы руководствуемся в нашей работе -

-
- -
- - -
- -
-

Качество

-

- Мы предлагаем только высококачественное топливо, - соответствующее международным стандартам. Регулярные - проверки и контроль качества гарантируют, что наши клиенты - получают лучшее. -

-
-
- - - -
- -
-

- Клиентоориентированность -

-

- Наши клиенты - наш главный приоритет. Мы стремимся - предоставить лучший сервис, удобные условия и приятную - атмосферу на каждой нашей заправке. -

-
-
- - - -
- -
-

Профессионализм

-

- Наши сотрудники - профессионалы своего дела. Мы постоянно - инвестируем в их обучение и развитие, чтобы обеспечить - высокий уровень обслуживания. -

-
-
-
-
-
- - {/* Our Team */} -
-
-
-
- -
-

- Наша команда -

-

- Познакомьтесь с профессионалами, которые делают GasNetwork - лучшей сетью заправок в Таджикистане -

-
- -
- {[ - { name: 'Алишер Рахмонов', position: 'Генеральный директор' }, - { name: 'Фарида Каримова', position: 'Финансовый директор' }, - { name: 'Рустам Назаров', position: 'Технический директор' }, - { name: 'Зарина Шарипова', position: 'Директор по маркетингу' }, - ].map((person, index) => ( -
-
- {person.name} -
-
-

{person.name}

-

{person.position}

-
-
- ))} -
-
-
- - {/* Testimonials */} -
-
-
-
- -
-

- Отзывы клиентов -

-

- Что говорят о нас наши клиенты -

-
- -
- {[ - { - name: 'Фархад К.', - text: 'Я всегда заправляюсь только на GasNetwork. Качество топлива на высоте, а обслуживание всегда приветливое и быстрое.', - rating: 5, - }, - { - name: 'Нигина М.', - text: 'Очень удобно, что заправки расположены по всему городу. Всегда чисто, есть кафе и магазин. Рекомендую!', - rating: 5, - }, - { - name: 'Джамшед Р.', - text: 'Пользуюсь картой лояльности GasNetwork уже 3 года. Накопил много бонусов и получил немало приятных подарков. Отличный сервис!', - rating: 4, - }, - ].map((testimonial, index) => ( - - -
- {Array(5) - .fill(0) - .map((_, i) => ( - - ))} -
-

- "{testimonial.text}" -

-

{testimonial.name}

-
-
- ))} -
-
-
- - -
-
- ); +export default function About(){ + return } diff --git a/src/app/api/auth/login/route.ts b/src/app/api/auth/login/route.ts new file mode 100644 index 0000000..774e8e7 --- /dev/null +++ b/src/app/api/auth/login/route.ts @@ -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 }); + } +}; diff --git a/src/app/charity/page.tsx b/src/app/charity/page.tsx new file mode 100644 index 0000000..2b38a2e --- /dev/null +++ b/src/app/charity/page.tsx @@ -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 ( +
+
+ {/* Hero Section */} +
+
+ Благотворительный фонд GasNetwork +
+
+
+
+ +
+

+ Благотворительный фонд GasNetwork +

+

+ Мы верим, что бизнес должен быть социально ответственным. + Наш фонд поддерживает образование, здравоохранение и + экологические инициативы в Таджикистане. +

+
+
+
+
+
+ + {/* Mission Section */} +
+
+
+
+
+ +
+

+ Наша миссия +

+

+ Благотворительный фонд GasNetwork был создан в 2020 году с + целью поддержки социально значимых проектов в Таджикистане. Мы + стремимся внести свой вклад в развитие общества и помочь тем, + кто в этом нуждается. +

+

+ Наша миссия — создавать возможности для улучшения жизни людей + через образование, здравоохранение, экологические инициативы и + поддержку уязвимых групп населения. +

+ +
+
+ +
+

Прозрачность

+

+ Мы публикуем ежегодные отчеты о всех наших проектах и + расходах, обеспечивая полную прозрачность нашей + деятельности. +

+
+
+
+ +
+

Эффективность

+

+ Мы тщательно выбираем проекты, которые могут принести + максимальную пользу обществу и имеют долгосрочное + влияние. +

+
+
+
+ +
+

Сотрудничество

+

+ Мы сотрудничаем с местными и международными + организациями для достижения наибольшего эффекта от + наших инициатив. +

+
+
+
+
+
+ Наша миссия +
+
+
+
+ + {/* Key Figures */} +
+
+
+

+ Наш вклад в цифрах +

+

+ За время существования нашего фонда мы достигли значительных + результатов +

+
+
+
+

15+

+

Реализованных проектов

+
+
+

1.2M

+

Сомони пожертвований

+
+
+

5000+

+

Людей получили помощь

+
+
+
+
+ + {/* Upcoming Events */} +
+
+
+
+ +
+

+ Предстоящие мероприятия +

+

+ Присоединяйтесь к нашим благотворительным мероприятиям и внесите + свой вклад в общее дело +

+
+ +
+ {[ + { + 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) => ( + +
+ {event.title} +
+ + {event.title} + + +

{event.description}

+
+ + {event.date} +
+
+ + {event.location} +
+
+ + + +
+ ))} +
+
+
+ + {/* How to Help */} +
+
+
+
+ +
+

+ Как вы можете помочь +

+

+ Есть много способов внести свой вклад в наши благотворительные + инициативы +

+
+ +
+ {[ + { + title: 'Сделать пожертвование', + description: + 'Ваше пожертвование поможет нам реализовать больше проектов и помочь большему количеству людей.', + icon: , + }, + { + title: 'Стать волонтером', + description: + 'Присоединяйтесь к нашей команде волонтеров и помогайте нам в реализации благотворительных проектов.', + icon: , + }, + { + title: 'Участвовать в мероприятиях', + description: + 'Принимайте участие в наших благотворительных мероприятиях и акциях.', + icon: , + }, + { + title: 'Распространять информацию', + description: + 'Расскажите о нашем фонде и его деятельности своим друзьям и знакомым.', + icon: , + }, + ].map((item, index) => ( + + +
{item.icon}
+ + {item.title} + +
+ +

{item.description}

+
+
+ ))} +
+
+
+ +
+
+ ); +} diff --git a/src/app/clients/certificates/page.tsx b/src/app/clients/certificates/page.tsx new file mode 100644 index 0000000..5d63d14 --- /dev/null +++ b/src/app/clients/certificates/page.tsx @@ -0,0 +1,5 @@ +import { CertificatesPage } from '@/pages-templates/clients/certificates'; + +export default function Certificates() { + return ; +} diff --git a/src/app/clients/loyalty/page.tsx b/src/app/clients/loyalty/page.tsx index e20ecfa..f0a8c09 100644 --- a/src/app/clients/loyalty/page.tsx +++ b/src/app/clients/loyalty/page.tsx @@ -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 ( -
-
- {/* Hero Section */} -
-
- Программа лояльности -
-
-
-

- Программа лояльности -

-

- Накапливайте баллы и получайте скидки на топливо и услуги - нашей сети -

-
-
-
-
-
- - {/* Program Overview */} -
-
-
-
-
- -
-

- О программе лояльности -

-

- Программа лояльности GasNetwork — это возможность получать - баллы за каждую покупку топлива и услуг на наших заправочных - станциях. Накопленные баллы можно обменять на скидки, подарки - или дополнительные услуги. -

-

- Участие в программе абсолютно бесплатное. Вам нужно только - получить карту лояльности в любой нашей заправочной станции - или зарегистрироваться в мобильном приложении. -

- -
-
-
- -
-
-

1 литр = 1 балл

-

- За каждый литр топлива вы получаете 1 балл -

-
-
-
-
- -
-
-

- Дополнительные баллы -

-

- За покупки в магазине и кафе на заправке -

-
-
-
-
- -
-
-

Специальные акции

-

- Удвоенные и утроенные баллы в праздничные дни -

-
-
-
-
-
- Программа лояльности -
-
-
-
- - {/* How It Works */} -
-
-
-

- Как это работает -

-

- Простые шаги для участия в программе лояльности GasNetwork -

-
- -
-
-
- 1 -
-

Получите карту

-

- Получите карту лояльности на любой заправке GasNetwork или - зарегистрируйтесь в мобильном приложении -

-
-
-
- 2 -
-

Заправляйтесь

-

- Используйте карту при каждой заправке и покупке в магазинах на - наших АЗС -

-
-
-
- 3 -
-

Накапливайте баллы

-

- Получайте баллы за каждую покупку и следите за их накоплением - в приложении -

-
-
-
- 4 -
-

Получайте выгоду

-

- Обменивайте накопленные баллы на скидки, подарки или - дополнительные услуги -

-
-
-
-
- - {/* Loyalty Levels */} -
-
-
-

- Уровни лояльности -

-

- Чем больше вы заправляетесь, тем больше преимуществ получаете -

-
- -
- - -

- Стандарт -

-
- 1% -

возврат баллами

-
-
    -
  • - - 1 балл за каждый литр топлива -
  • -
  • - - Участие в акциях -
  • -
  • - - Доступ к мобильному приложению -
  • -
-
-
- - - -

- Золотой -

-
- 2% -

возврат баллами

-
-
    -
  • - - 2 балла за каждый литр топлива -
  • -
  • - - Скидка 5% в кафе на заправках -
  • -
  • - - Приоритетное обслуживание -
  • -
  • - - Эксклюзивные акции -
  • -
-
-
- - - -

- Платиновый -

-
- 3% -

возврат баллами

-
-
    -
  • - - 3 балла за каждый литр топлива -
  • -
  • - - Скидка 10% в кафе на заправках -
  • -
  • - - Бесплатная мойка раз в месяц -
  • -
  • - - Персональный менеджер -
  • -
  • - - VIP-обслуживание -
  • -
-
-
-
-
-
- - -
-
- ); +export default function Loyalty() { + return ; } diff --git a/src/app/clients/page.tsx b/src/app/clients/page.tsx index a4deb55..55d5334 100644 --- a/src/app/clients/page.tsx +++ b/src/app/clients/page.tsx @@ -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 ( -
-
- {/* Hero Section */} -
-
- Для наших клиентов -
-
-
-

- Для наших клиентов -

-

- Специальные предложения, программы лояльности и удобные - способы оплаты для наших клиентов -

-
-
-
-
-
- - {/* Services Overview */} -
-
-
-

- Наши услуги для клиентов -

-

- Мы стремимся сделать обслуживание на наших заправках максимально - удобным и выгодным для вас -

-
- -
- - -
- -
- Программа лояльности - - Накапливайте баллы и получайте скидки - -
- -

- Наша программа лояльности позволяет накапливать баллы за - каждую покупку и обменивать их на скидки и подарки. -

-
- - - - - -
- - - -
- -
- Топливная карта - - Удобный способ оплаты топлива - -
- -

- Топливные карты для физических и юридических лиц. - Контролируйте расходы и получайте дополнительные - преимущества. -

-
- - - - - -
- - - -
- -
- Сертификаты - - Подарочные сертификаты на топливо и услуги - -
- -

- Подарите близким или партнерам сертификат на топливо или - услуги нашей сети. Идеальный подарок для автовладельцев. -

-
- - - - - -
- - - -
- -
- Способы оплаты - - Различные способы оплаты на наших заправках - -
- -

- Мы предлагаем различные способы оплаты: наличные, банковские - карты, мобильные платежи и топливные карты. -

-
- - - - - -
-
-
-
- - {/* Benefits Section */} -
-
-
-
-
- -
-

- Преимущества для клиентов -

-

- Став клиентом GasNetwork, вы получаете множество преимуществ, - которые делают заправку вашего автомобиля более выгодной и - удобной. -

- -
-
-
- -
-
-

Экономия

-

- Скидки и бонусы для постоянных клиентов -

-
-
-
-
- -
-
-

Удобство

-

- Быстрая оплата и обслуживание -

-
-
-
-
- -
-
-

Качество

-

- Гарантированно высокое качество топлива -

-
-
-
-
- -
-
-

- Дополнительные услуги -

-

- Кафе, магазины и другие услуги на наших заправках -

-
-
-
-
-
- Преимущества для клиентов -
-
-
-
- - -
-
- ); -} +export default function Clients() { + return ; +} \ No newline at end of file diff --git a/src/app/globals.css b/src/app/globals.css index ac2e376..52b2213 100644 --- a/src/app/globals.css +++ b/src/app/globals.css @@ -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); diff --git a/src/app/layout.tsx b/src/app/layout.tsx index 60d2721..6fcee9d 100644 --- a/src/app/layout.tsx +++ b/src/app/layout.tsx @@ -22,7 +22,12 @@ export default function RootLayout({ children: React.ReactNode; }>) { return ( - +
diff --git a/src/app/login/page.tsx b/src/app/login/page.tsx index 23db643..bb9a24e 100644 --- a/src/app/login/page.tsx +++ b/src/app/login/page.tsx @@ -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 ( -
-
-
-
-
- -
-

- Вход в личный кабинет -

-

- Войдите в личный кабинет, чтобы получить доступ к информации о - ваших бонусах, истории операций и другим возможностям. -

-
- -
- - - - Бонусный клиент - - - Корпоративный клиент - - - - - - - Вход для бонусных клиентов - - Введите номер телефона и номер бонусной карты для входа в - личный кабинет. - - - - - - - - - - - - Вход для корпоративных клиентов - - Введите номер телефона и номер корпоративной карты для - входа в личный кабинет. - - - - - - - - - -
-

- Возникли проблемы со входом?{' '} - - Свяжитесь с нами - -

-
-
-
-
-
- ); +export default function Login() { + return ; } diff --git a/src/entities/auth/api/login.api.ts b/src/entities/auth/api/login.api.ts new file mode 100644 index 0000000..99ad4ed --- /dev/null +++ b/src/entities/auth/api/login.api.ts @@ -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({ + 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; diff --git a/src/entities/auth/index.ts b/src/entities/auth/index.ts new file mode 100644 index 0000000..d99a63a --- /dev/null +++ b/src/entities/auth/index.ts @@ -0,0 +1 @@ +export { useLazyLoginQuery } from './api/login.api'; diff --git a/src/entities/auth/model/contracts/login.contract.ts b/src/entities/auth/model/contracts/login.contract.ts new file mode 100644 index 0000000..cb3a3da --- /dev/null +++ b/src/entities/auth/model/contracts/login.contract.ts @@ -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'; +} diff --git a/src/entities/auth/model/types/index.ts b/src/entities/auth/model/types/index.ts new file mode 100644 index 0000000..1144cca --- /dev/null +++ b/src/entities/auth/model/types/index.ts @@ -0,0 +1,7 @@ +export interface LoginData { + card_id: number; + created_at: string; + phone: string; + token: string; + uid: string; +} diff --git a/src/features/auth/login-form/model/login-form.schema.ts b/src/features/auth/login-form/model/login-form.schema.ts index eca9731..a8da155 100644 --- a/src/features/auth/login-form/model/login-form.schema.ts +++ b/src/features/auth/login-form/model/login-form.schema.ts @@ -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; diff --git a/src/features/auth/login-form/ui/login-form.tsx b/src/features/auth/login-form/ui/login-form.tsx index b89887b..0e4f2e2 100644 --- a/src/features/auth/login-form/ui/login-form.tsx +++ b/src/features/auth/login-form/ui/login-form.tsx @@ -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; + 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({ 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 (
- + ( - - Номер телефона + + {t('auth.phoneNumber')} { control={form.control} name='cardNumber' render={({ field }) => ( - - Номер карты + + {t('auth.cardNumber')} { )} /> - + disabled={isLoginLoading} + /> ); diff --git a/src/features/map/ui/gas-station-map.tsx b/src/features/map/ui/gas-station-map.tsx index 0f2d763..5ef8a15 100644 --- a/src/features/map/ui/gas-station-map.tsx +++ b/src/features/map/ui/gas-station-map.tsx @@ -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(null); + const [activeFilters, setActiveFilters] = useState([]); + const [activeCities, setActiveCities] = useState([]); + const [filteredStations, setFilteredStations] = useState(stations); + const [selectedStation, setSelectedStation] = useState(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 (
-
-
+ {/* Filter panel - slides from left */} +
+
+
+ + {t('map.filters')} +
+ +
+ +
+ + + {t('map.cities')} + {t('map.services')} + + + + + +
+ {allCities.map((city) => ( + + ))} +
+ + {activeCities.length > 0 && ( + + )} +
+ + +
+ {allFilters.map((filter) => ( + + ))} +
+ {activeFilters.length > 0 && ( + + )} +
+
+
+
+ + {/* Station list panel - slides from right */} +
+
+ +
+ {t('map.stationsList')} + {filteredStations.length} +
+
+
+ {filteredStations.length > 0 ? ( +
+ {filteredStations.map((station) => ( +
setSelectedStation(station.id)} + > +
+

{station.name}

+ +
+

+ {station.address} +

+
+ + {station.city} + + {station.services.map((service) => ( + + {service} + + ))} +
+
+ ))} +
+ ) : ( +
+

{t('map.noStations')}

+
+ {activeFilters.length > 0 && ( + + )} + {activeCities.length > 0 && ( + + )} +
+
+ )} +
+
+ + {/* Map */} +
+
+
+ + {/* Control buttons */} +
+ +
+ +
+ +
+ +
- Наши заправки + {t('map.ourStations')}
-

Всего станций: 25

+

+ {t('map.totalStations')}: {stations.length} +

); diff --git a/src/pages-templates/about/index.tsx b/src/pages-templates/about/index.tsx new file mode 100644 index 0000000..3a852a9 --- /dev/null +++ b/src/pages-templates/about/index.tsx @@ -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 ( +
+
+ {/* Hero Section */} +
+
+ {t('about.hero.imageAlt')} +
+
+
+

+ {t('about.hero.title')} +

+

+ {t('about.hero.subtitle')} +

+
+
+
+
+
+ + {/* Company Overview */} +
+
+
+
+
+ +
+

+ {t('about.overview.title')} +

+

+ {t('about.overview.description1')} +

+

+ {t('about.overview.description2')} +

+

+ {t('about.overview.description3')} +

+ +
+ {[0, 1, 2, 3].map((index) => ( +
+
+ +
+
+

+ {t(`about.overview.benefits.${index}.title`)} +

+

+ {t(`about.overview.benefits.${index}.description`)} +

+
+
+ ))} +
+
+
+ {t('about.overview.imageAlt')} +
+
+
+
+ + {/* Stats Section */} +
+
+
+

+ {t('about.stats.title')} +

+

+ {t('about.stats.subtitle')} +

+
+
+ {[0, 1, 2, 3].map((index) => ( +
+

+ +

+

+ {t(`about.stats.items.${index}.label`)} +

+
+ ))} +
+
+
+ + {/* Our History */} +
+
+
+
+ +
+

+ {t('about.history.title')} +

+

+ {t('about.history.subtitle')} +

+
+ + +
+
+ + {/* Our Stations */} +
+
+
+
+ +
+

+ {t('about.stations.title')} +

+

+ {t('about.stations.subtitle')} +

+
+ + + +
+

+ {t('about.stations.description')} +

+ +
+
+
+ + {/* Our Values */} +
+
+
+
+ +
+

+ {t('about.values.title')} +

+

+ {t('about.values.subtitle')} +

+
+ +
+ {[0, 1, 2].map((index) => ( + + +
+ +
+

+ {t(`about.values.items.${index}.title`)} +

+

+ {t(`about.values.items.${index}.description`)} +

+
+
+ ))} +
+
+
+ + {/* Our Team */} +
+
+
+
+ +
+

+ {t('about.team.title')} +

+

+ {t('about.team.subtitle')} +

+
+ +
+ {[0, 1, 2, 3].map((index) => ( +
+
+ {t(`about.team.members.${index}.name`)} +
+
+

+ {t(`about.team.members.${index}.name`)} +

+

+ {t(`about.team.members.${index}.position`)} +

+
+
+ ))} +
+
+
+ + {/* Testimonials */} +
+
+
+
+ +
+

+ {t('about.testimonials.title')} +

+

+ {t('about.testimonials.subtitle')} +

+
+ +
+ {[0, 1, 2].map((index) => ( + + +
+ {Array(5) + .fill(0) + .map((_, i) => ( + + ))} +
+

+ "{t(`about.testimonials.items.${index}.text`)}" +

+

+ {t(`about.testimonials.items.${index}.name`)} +

+
+
+ ))} +
+
+
+ + +
+
+ ); +} diff --git a/src/pages-templates/clients/certificates/index.tsx b/src/pages-templates/clients/certificates/index.tsx new file mode 100644 index 0000000..2081d98 --- /dev/null +++ b/src/pages-templates/clients/certificates/index.tsx @@ -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 ( + <> +
+
+

{t('certificates.title')}

+

+ {t('certificates.description')} +

+
+ +
+ {certificates.map((certificate) => ( + +
+ {certificate.title} +
+ +

{certificate.title}

+

{certificate.description}

+
+

+ {t('certificates.issueDate')}: {certificate.issueDate} +

+

+ {t('certificates.expiryDate')}: {certificate.expiryDate} +

+
+
+ + +
+
+
+ ))} +
+
+ + ); +} diff --git a/src/pages-templates/clients/index.tsx b/src/pages-templates/clients/index.tsx new file mode 100644 index 0000000..f287229 --- /dev/null +++ b/src/pages-templates/clients/index.tsx @@ -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 ( +
+
+ {/* Hero Section */} +
+
+ Для наших клиентов +
+
+
+

+ {t('clients.title')} +

+

+ {t('clients.description')} +

+
+
+
+
+
+ + + + +
+
+ ); +} diff --git a/src/pages-templates/clients/loyalty/index.tsx b/src/pages-templates/clients/loyalty/index.tsx new file mode 100644 index 0000000..973236f --- /dev/null +++ b/src/pages-templates/clients/loyalty/index.tsx @@ -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 ( +
+
+ {/* Hero Section */} +
+
+ Программа лояльности +
+
+
+

+ {t('clients.loyalty.title')} +

+

+ {t('clients.loyalty.description')} +

+
+
+
+
+
+ + {/* Program Overview */} +
+
+
+
+
+ +
+

+ {t('clients.loyalty.programm.about')} +

+

+ {t('clients.loyalty.programm.about-description')} +

+

+ {t('clients.loyalty.programm.about-description-2')} +

+ +
+
+
+ +
+
+

+ {t('clients.loyalty.programm.conditions-1')} +

+

+ {t('clients.loyalty.programm.conditions.description-1')} +

+
+
+
+
+ +
+
+

+ {t('clients.loyalty.programm.conditions-2')} +

+

+ {t('clients.loyalty.programm.conditions.description-2')} +

+
+
+
+
+ +
+
+

+ {t('clients.loyalty.programm.conditions-3')} +

+

+ {t('clients.loyalty.programm.conditions.description-3')} +

+
+
+
+
+
+ Программа лояльности +
+
+
+
+ + {/* How It Works */} +
+
+
+

+ {t('clients.loyalty.works.title')} +

+

+ {t('clients.loyalty.works.description')} +

+
+ +
+
+
+ 1 +
+

+ {t('clients.loyalty.works.stage-1')} +

+

+ {t('clients.loyalty.works.stage.description-1')} +

+
+
+
+ 2 +
+

+ {t('clients.loyalty.works.stage-2')} +

+

+ {t('clients.loyalty.works.stage.description-2')} +

+
+
+
+ 3 +
+

+ {t('clients.loyalty.works.stage-3')} +

+

+ {t('clients.loyalty.works.stage.description-3')} +

+
+
+
+ 4 +
+

+ {t('clients.loyalty.works.stage-4')} +

+

+ {t('clients.loyalty.works.stage.description-4')} +

+
+
+
+
+ + {/* Loyalty Levels */} +
+
+
+

+ {t('clients.loyalty.works.levels.title')} +

+

+ {t('clients.loyalty.works.levels.description')} +

+
+ +
+ + +

+ {t('clients.loyalty.works.levels.card-1.title')} +

+
+ + {t('clients.loyalty.works.levels.card-1.percent')} + +

+ {t('clients.loyalty.works.levels.card.mark')} +

+
+
    +
  • + + + {t('clients.loyalty.works.levels.card-1.bonus-1')} + +
  • +
  • + + + {t('clients.loyalty.works.levels.card-1.bonus-2')} + +
  • +
  • + + + {t('clients.loyalty.works.levels.card-1.bonus-3')} + +
  • +
+
+
+ + + +

+ {t('clients.loyalty.works.levels.card-2.title')} +

+
+ + {t('clients.loyalty.works.levels.card-2.percent')} + +

+ {t('clients.loyalty.works.levels.card.mark')} +

+
+
    +
  • + + + {t('clients.loyalty.works.levels.card-1.bonus-1')} + +
  • +
  • + + + {t('clients.loyalty.works.levels.card-2.bonus-2')} + +
  • +
  • + + + {t('clients.loyalty.works.levels.card-3.bonus-3')} + +
  • +
  • + + + {t('clients.loyalty.works.levels.card-4.bonus-4')} + +
  • +
+
+
+ + + +

+ {t('clients.loyalty.works.levels.card-3.title')} +

+
+ + {t('clients.loyalty.works.levels.card-3.percent')} + +

+ {t('clients.loyalty.works.levels.card.mark')} +

+
+
    +
  • + + + {t('clients.loyalty.works.levels.card-3.bonus-1')} + +
  • +
  • + + + {t('clients.loyalty.works.levels.card-3.bonus-2')} + +
  • +
  • + + + {t('clients.loyalty.works.levels.card-3.bonus-3')} + +
  • +
  • + + + {t('clients.loyalty.works.levels.card-3.bonus-4')} + +
  • +
  • + + + {t('clients.loyalty.works.levels.card-3.bonus-5')} + +
  • +
+
+
+
+
+
+ + +
+
+ ); +} diff --git a/src/pages-templates/login/index.tsx b/src/pages-templates/login/index.tsx new file mode 100644 index 0000000..195fbfe --- /dev/null +++ b/src/pages-templates/login/index.tsx @@ -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 ( +
+
+
+
+
+ +
+

+ {t('auth.title')} +

+

{t('auth.description')}

+
+ +
+ + + {tabs.map((tab) => { + return ( + + {t(tab.label)} + + ); + })} + + + {tabs.map((tab) => { + return ( + + + + {t(tab.title)} + {t(tab.description)} + + + + + + + ); + })} + + +
+

+ {t('auth.loginIssues')}{' '} + + {t('auth.contactLink')} + +

+
+
+
+
+
+ ); +} diff --git a/src/shared/components/about-counter.tsx b/src/shared/components/about-counter.tsx index 317f7f3..f1c78f6 100644 --- a/src/shared/components/about-counter.tsx +++ b/src/shared/components/about-counter.tsx @@ -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(null); + const {t} = useLanguage() + useEffect(() => { const observer = new IntersectionObserver( (entries) => { @@ -33,26 +36,26 @@ export default function AboutCounter() { }, []); return ( -
-
+
+

{isVisible ? : '0+'}

-

Сотрудников

+

{t("about.stats.items.2.label")}

-
+

{isVisible ? : '0M+'}

-

Литров топлива в месяц

+

{t("about.stats.items.4.label")}

-
+
@@ -63,7 +66,7 @@ export default function AboutCounter() { '0%' )} -

Довольных клиентов

+

{t("about.stats.items.5.label")}

); diff --git a/src/shared/components/promotion-slider.tsx b/src/shared/components/promotion-slider.tsx index 3b6bd1b..3a25dd5 100644 --- a/src/shared/components/promotion-slider.tsx +++ b/src/shared/components/promotion-slider.tsx @@ -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 ( -
-
-
- {promotions.map((promo) => ( -
- -
- {promo.title} -
- -

{promo.title}

-

- {promo.description} -

-
- - Действует до: {promo.validUntil} - - -
-
-
-
- ))} -
+ +
+ + +
+ ))}
- - + ); +}; diff --git a/src/shared/language/context/language-provider.tsx b/src/shared/language/context/language-provider.tsx new file mode 100644 index 0000000..de3e7c9 --- /dev/null +++ b/src/shared/language/context/language-provider.tsx @@ -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; + +// Load translations +const translations: Record = { + en: enTranslations, + ru: ruTranslations, +}; + +// Create context +type LanguageContextType = { + language: Language; + setLanguage: (lang: Language) => void; + t: (key: string) => string; +}; + +export const LanguageContext = createContext( + undefined, +); + +// Create provider +export function LanguageProvider({ children }: { children: ReactNode }) { + // Default to Russian, but check localStorage on client + const [language, setLanguageState] = useState('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 ( + + {children} + + ); +} diff --git a/src/shared/language/hooks/use-language.ts b/src/shared/language/hooks/use-language.ts new file mode 100644 index 0000000..6473281 --- /dev/null +++ b/src/shared/language/hooks/use-language.ts @@ -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; +} diff --git a/src/shared/language/index.ts b/src/shared/language/index.ts new file mode 100644 index 0000000..35d8d88 --- /dev/null +++ b/src/shared/language/index.ts @@ -0,0 +1,6 @@ +export { + LanguageProvider, + languages, + type Language, +} from './context/language-provider'; +export { useLanguage } from './hooks/use-language'; diff --git a/src/shared/language/locales/en.json b/src/shared/language/locales/en.json new file mode 100644 index 0000000..3256384 --- /dev/null +++ b/src/shared/language/locales/en.json @@ -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" +} diff --git a/src/shared/language/locales/ru.json b/src/shared/language/locales/ru.json new file mode 100644 index 0000000..21f87f9 --- /dev/null +++ b/src/shared/language/locales/ru.json @@ -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": "Все города" +} diff --git a/src/shared/language/ui/language-switcher.tsx b/src/shared/language/ui/language-switcher.tsx new file mode 100644 index 0000000..0de4ed5 --- /dev/null +++ b/src/shared/language/ui/language-switcher.tsx @@ -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 ( + + + + + + {Object.entries(languages).map(([code, { name, flag }]) => ( + { + setLanguage(code as Language); + setOpen(false); + }} + > + {flag} + {name} + {code === language && } + + ))} + + + ); +} diff --git a/src/shared/providers/providers.tsx b/src/shared/providers/providers.tsx index 21eca5f..f2c02cd 100644 --- a/src/shared/providers/providers.tsx +++ b/src/shared/providers/providers.tsx @@ -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 ( - - - {children} - - - + + + + {children} + + + + ); }; diff --git a/src/shared/shadcn-ui/badge.tsx b/src/shared/shadcn-ui/badge.tsx new file mode 100644 index 0000000..9c5f1c3 --- /dev/null +++ b/src/shared/shadcn-ui/badge.tsx @@ -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, + VariantProps {} + +function Badge({ className, variant, ...props }: BadgeProps) { + return ( +
+ ); +} + +export { Badge, badgeVariants }; diff --git a/src/shared/shadcn-ui/calendar.tsx b/src/shared/shadcn-ui/calendar.tsx new file mode 100644 index 0000000..2f4b84e --- /dev/null +++ b/src/shared/shadcn-ui/calendar.tsx @@ -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; + +function Calendar({ + className, + classNames, + showOutsideDays = true, + ...props +}: CalendarProps) { + return ( + ( + + ), + IconRight: ({ className, ...props }) => ( + + ), + }} + {...props} + /> + ); +} +Calendar.displayName = 'Calendar'; + +export { Calendar }; diff --git a/src/shared/shadcn-ui/popover.tsx b/src/shared/shadcn-ui/popover.tsx new file mode 100644 index 0000000..7fdf57d --- /dev/null +++ b/src/shared/shadcn-ui/popover.tsx @@ -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, + React.ComponentPropsWithoutRef +>(({ className, align = 'center', sideOffset = 4, ...props }, ref) => ( + + + +)); +PopoverContent.displayName = PopoverPrimitive.Content.displayName; + +export { Popover, PopoverTrigger, PopoverContent }; diff --git a/src/shared/shadcn-ui/table.tsx b/src/shared/shadcn-ui/table.tsx new file mode 100644 index 0000000..d9998b3 --- /dev/null +++ b/src/shared/shadcn-ui/table.tsx @@ -0,0 +1,117 @@ +import * as React from 'react'; + +import { cn } from '@/shared/lib/utils'; + +const Table = React.forwardRef< + HTMLTableElement, + React.HTMLAttributes +>(({ className, ...props }, ref) => ( +
+ + +)); +Table.displayName = 'Table'; + +const TableHeader = React.forwardRef< + HTMLTableSectionElement, + React.HTMLAttributes +>(({ className, ...props }, ref) => ( + +)); +TableHeader.displayName = 'TableHeader'; + +const TableBody = React.forwardRef< + HTMLTableSectionElement, + React.HTMLAttributes +>(({ className, ...props }, ref) => ( + +)); +TableBody.displayName = 'TableBody'; + +const TableFooter = React.forwardRef< + HTMLTableSectionElement, + React.HTMLAttributes +>(({ className, ...props }, ref) => ( + tr]:last:border-b-0', + className, + )} + {...props} + /> +)); +TableFooter.displayName = 'TableFooter'; + +const TableRow = React.forwardRef< + HTMLTableRowElement, + React.HTMLAttributes +>(({ className, ...props }, ref) => ( + +)); +TableRow.displayName = 'TableRow'; + +const TableHead = React.forwardRef< + HTMLTableCellElement, + React.ThHTMLAttributes +>(({ className, ...props }, ref) => ( +
+)); +TableHead.displayName = 'TableHead'; + +const TableCell = React.forwardRef< + HTMLTableCellElement, + React.TdHTMLAttributes +>(({ className, ...props }, ref) => ( + +)); +TableCell.displayName = 'TableCell'; + +const TableCaption = React.forwardRef< + HTMLTableCaptionElement, + React.HTMLAttributes +>(({ className, ...props }, ref) => ( +
+)); +TableCaption.displayName = 'TableCaption'; + +export { + Table, + TableHeader, + TableBody, + TableFooter, + TableHead, + TableRow, + TableCell, + TableCaption, +}; diff --git a/src/shared/store/root-reducer.ts b/src/shared/store/root-reducer.ts index 27360ad..56b999c 100644 --- a/src/shared/store/root-reducer.ts +++ b/src/shared/store/root-reducer.ts @@ -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, }); diff --git a/src/widgets/about-page/company-timeline.tsx b/src/widgets/about-page/company-timeline.tsx index d817b68..4ea937d 100644 --- a/src/widgets/about-page/company-timeline.tsx +++ b/src/widgets/about-page/company-timeline.tsx @@ -74,18 +74,22 @@ export function CompanyTimeline() { data-aos='zoom-in-down' >
- +

{event.year}

{event.title}

{event.description}

@@ -94,8 +98,8 @@ export function CompanyTimeline() {
-
-
+
+
))} @@ -105,16 +109,18 @@ export function CompanyTimeline() {
diff --git a/src/widgets/about-section.tsx b/src/widgets/about-section.tsx index d2930b3..5046a99 100644 --- a/src/widgets/about-section.tsx +++ b/src/widgets/about-section.tsx @@ -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 ( -
+
-
+

- О нашей компании + {t('home.about.title')}

-

- Наша сеть заправок является одной из ведущих в Таджикистане. Мы - предоставляем качественное топливо и высокий уровень обслуживания - для наших клиентов уже более 15 лет. -

-

- Мы постоянно развиваемся, открывая новые станции и улучшая сервис - на существующих. Наша цель - сделать заправку автомобиля - максимально удобной и быстрой для каждого клиента. -

+

{t('home.about.description1')}

+

{t('home.about.description2')}

-
-
-
- -
-
-

Качественное топливо

-

- Мы гарантируем высокое качество нашего топлива -

-
-
-
-
- -
-
-

- Современное оборудование -

-

- Все наши станции оснащены современным оборудованием -

-
-
-
-
- -
-
-

- Профессиональный персонал -

-

- Наши сотрудники - профессионалы своего дела -

-
-
-
+
{
); }; + +interface Feature { + title: string; + description: string; +} + +const features: Array = [ + { + 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 ( +
+ {features.map(({ title, description }) => { + return ( +
+
+ +
+
+

{t(title)}

+

{t(description)}

+
+
+ ); + })} +
+ ); +}; diff --git a/src/widgets/cards-list.tsx b/src/widgets/cards-list.tsx new file mode 100644 index 0000000..0c71a56 --- /dev/null +++ b/src/widgets/cards-list.tsx @@ -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 ( +
+
+

Топливные карты

+
+ + + +
+
+
+ {Array.from({ length: 4 }).map((_, index) => ( + + +
+
+
+

+ Карта #{index + 1} +

+

**** **** **** {1000 + index}

+
+ +
+
+
+
+

Лимит:

+

+ {(5000 * (index + 1)).toLocaleString()} сомони +

+
+
+

Статус:

+ + Активна + +
+
+
+
+ ))} + {/* Show more cards button */} + {totalCards > 4 && ( + + )} +
+
+ ); +}; diff --git a/src/widgets/charity-section.tsx b/src/widgets/charity-section.tsx index 6e27cb6..44b943d 100644 --- a/src/widgets/charity-section.tsx +++ b/src/widgets/charity-section.tsx @@ -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 ( -
+
{
-

- Благотворительный фонд +

+ {t('home.charity.title')}

- Наш благотворительный фонд был создан для поддержки социально - значимых проектов в Таджикистане. Мы стремимся внести свой вклад в - развитие общества и помочь тем, кто в этом нуждается. -

-

- Основные направления деятельности нашего фонда: + {t('home.charity.description')}

+

{t('home.charity.directions')}

  • - Поддержка образовательных программ + {t('home.charity.education')}
  • - Помощь детям из малообеспеченных семей + {t('home.charity.children')}
  • - Экологические инициативы + {t('home.charity.ecology')}
  • - Поддержка спортивных мероприятий + {t('home.charity.sports')}
- + + +
diff --git a/src/widgets/clients/ui/benefits-section.tsx b/src/widgets/clients/ui/benefits-section.tsx new file mode 100644 index 0000000..0e36fc9 --- /dev/null +++ b/src/widgets/clients/ui/benefits-section.tsx @@ -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 = [ + { + title: 'Экономия', + description: 'Скидки и бонусы для постоянных клиентов', + }, + { + title: 'Удобство', + description: 'Быстрая оплата и обслуживание', + }, + { + title: 'Качество', + description: 'Гарантированно высокое качество топлива', + }, + { + title: 'Дополнительные услуги', + description: 'Кафе, магазины и другие услуги на наших заправках', + }, +]; + +export const BenefitsSection = () => { + + const {t} = useLanguage() + + return ( +
+
+
+
+
+ +
+

+ {t('clients.benefits.title')} +

+

+ {t('clients.benefits.subtitle')} +

+ +
+ {benefits.map(({ title, description }) => { + return ( +
+
+ +
+
+

{title}

+

{description}

+
+
+ ); + })} +
+
+
+ Преимущества для клиентов +
+
+
+
+ ); +}; diff --git a/src/widgets/clients/ui/services-overview-section.tsx b/src/widgets/clients/ui/services-overview-section.tsx new file mode 100644 index 0000000..3a6154a --- /dev/null +++ b/src/widgets/clients/ui/services-overview-section.tsx @@ -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 & RefAttributes + >; +} + +const servicesOverview: Array = [ + { + title: 'Программа лояльности', + description: 'Накапливайте баллы и получайте скидки', + contentText: + 'Наша программа лояльности позволяет накапливать баллы за каждую покупку и обменивать их на скидки и подарки.', + Icon: Percent, + }, + { + title: 'Топливная карта', + description: 'Удобный способ оплаты топлива', + contentText: + 'Топливные карты для физических и юридических лиц. Контролируйте расходы и получайте дополнительные преимущества.', + Icon: CreditCard, + }, + { + title: 'Способы оплаты', + description: 'Различные способы оплаты на наших заправках', + contentText: + 'Мы предлагаем различные способы оплаты: наличные, банковские карты, мобильные платежи и топливные карты.', + Icon: Wallet, + }, +]; + +export const ServicesOverviewSection = () => { + + const {t} = useLanguage() + + return ( +
+
+
+

+ {t('clients.services.title')} +

+

+ {t('clients.services.subtitle')} +

+
+ +
+ {servicesOverview.map(({ description, Icon, contentText, title }) => { + return ( + + +
+ +
+ {title} + {description} +
+ +

{contentText}

+
+
+ ); + })} +
+
+
+ ); +}; diff --git a/src/widgets/cta-section.tsx b/src/widgets/cta-section.tsx index bc60660..9faa576 100644 --- a/src/widgets/cta-section.tsx +++ b/src/widgets/cta-section.tsx @@ -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 ( -
+

- Присоединяйтесь к нам + {t('home.cta.title')}

-

- Станьте частью нашей сети. Получайте специальные предложения, бонусы - и скидки. -

+

{t('home.cta.description')}

- - + + +
diff --git a/src/widgets/footer.tsx b/src/widgets/footer.tsx index b7ab63a..f09e22c 100644 --- a/src/widgets/footer.tsx +++ b/src/widgets/footer.tsx @@ -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 ( -