Merge branch 'dev' into render-main-page-data
This commit is contained in:
commit
2821025a3e
@ -25,8 +25,10 @@
|
|||||||
"@radix-ui/react-toast": "^1.2.11",
|
"@radix-ui/react-toast": "^1.2.11",
|
||||||
"@reduxjs/toolkit": "^2.7.0",
|
"@reduxjs/toolkit": "^2.7.0",
|
||||||
"aos": "^2.3.4",
|
"aos": "^2.3.4",
|
||||||
|
"axios": "^1.9.0",
|
||||||
"class-variance-authority": "^0.7.1",
|
"class-variance-authority": "^0.7.1",
|
||||||
"clsx": "^2.1.1",
|
"clsx": "^2.1.1",
|
||||||
|
"cookies-next": "^5.1.0",
|
||||||
"date-fns": "^4.1.0",
|
"date-fns": "^4.1.0",
|
||||||
"embla-carousel-autoplay": "^8.6.0",
|
"embla-carousel-autoplay": "^8.6.0",
|
||||||
"embla-carousel-react": "^8.6.0",
|
"embla-carousel-react": "^8.6.0",
|
||||||
|
|||||||
95
pnpm-lock.yaml
generated
95
pnpm-lock.yaml
generated
@ -53,12 +53,18 @@ importers:
|
|||||||
aos:
|
aos:
|
||||||
specifier: ^2.3.4
|
specifier: ^2.3.4
|
||||||
version: 2.3.4
|
version: 2.3.4
|
||||||
|
axios:
|
||||||
|
specifier: ^1.9.0
|
||||||
|
version: 1.9.0
|
||||||
class-variance-authority:
|
class-variance-authority:
|
||||||
specifier: ^0.7.1
|
specifier: ^0.7.1
|
||||||
version: 0.7.1
|
version: 0.7.1
|
||||||
clsx:
|
clsx:
|
||||||
specifier: ^2.1.1
|
specifier: ^2.1.1
|
||||||
version: 2.1.1
|
version: 2.1.1
|
||||||
|
cookies-next:
|
||||||
|
specifier: ^5.1.0
|
||||||
|
version: 5.1.0(next@15.3.1(react-dom@19.1.0(react@19.1.0))(react@19.1.0))(react@19.1.0)
|
||||||
date-fns:
|
date-fns:
|
||||||
specifier: ^4.1.0
|
specifier: ^4.1.0
|
||||||
version: 4.1.0
|
version: 4.1.0
|
||||||
@ -1334,6 +1340,9 @@ packages:
|
|||||||
resolution: {integrity: sha512-hsU18Ae8CDTR6Kgu9DYf0EbCr/a5iGL0rytQDobUcdpYOKokk8LEjVphnXkDkgpi0wYVsqrXuP0bZxJaTqdgoA==}
|
resolution: {integrity: sha512-hsU18Ae8CDTR6Kgu9DYf0EbCr/a5iGL0rytQDobUcdpYOKokk8LEjVphnXkDkgpi0wYVsqrXuP0bZxJaTqdgoA==}
|
||||||
engines: {node: '>= 0.4'}
|
engines: {node: '>= 0.4'}
|
||||||
|
|
||||||
|
asynckit@0.4.0:
|
||||||
|
resolution: {integrity: sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q==}
|
||||||
|
|
||||||
available-typed-arrays@1.0.7:
|
available-typed-arrays@1.0.7:
|
||||||
resolution: {integrity: sha512-wvUjBtSGN7+7SjNpq/9M2Tg350UZD3q62IFZLbRAR1bSMlCo1ZaeW+BJ+D090e4hIIZLBcTDWe4Mh4jvUDajzQ==}
|
resolution: {integrity: sha512-wvUjBtSGN7+7SjNpq/9M2Tg350UZD3q62IFZLbRAR1bSMlCo1ZaeW+BJ+D090e4hIIZLBcTDWe4Mh4jvUDajzQ==}
|
||||||
engines: {node: '>= 0.4'}
|
engines: {node: '>= 0.4'}
|
||||||
@ -1342,6 +1351,9 @@ packages:
|
|||||||
resolution: {integrity: sha512-Xm7bpRXnDSX2YE2YFfBk2FnF0ep6tmG7xPh8iHee8MIcrgq762Nkce856dYtJYLkuIoYZvGfTs/PbZhideTcEg==}
|
resolution: {integrity: sha512-Xm7bpRXnDSX2YE2YFfBk2FnF0ep6tmG7xPh8iHee8MIcrgq762Nkce856dYtJYLkuIoYZvGfTs/PbZhideTcEg==}
|
||||||
engines: {node: '>=4'}
|
engines: {node: '>=4'}
|
||||||
|
|
||||||
|
axios@1.9.0:
|
||||||
|
resolution: {integrity: sha512-re4CqKTJaURpzbLHtIi6XpDv20/CnpXOtjRY5/CU32L8gU8ek9UIivcfvSWvmKEngmVbrUtPpdDwWDWL7DNHvg==}
|
||||||
|
|
||||||
axobject-query@4.1.0:
|
axobject-query@4.1.0:
|
||||||
resolution: {integrity: sha512-qIj0G9wZbMGNLjLmg1PT6v2mE9AH2zlnADJD/2tC6E00hgmhUOfEB6greHPAfLRSufHqROIUTkw6E+M3lH0PTQ==}
|
resolution: {integrity: sha512-qIj0G9wZbMGNLjLmg1PT6v2mE9AH2zlnADJD/2tC6E00hgmhUOfEB6greHPAfLRSufHqROIUTkw6E+M3lH0PTQ==}
|
||||||
engines: {node: '>= 0.4'}
|
engines: {node: '>= 0.4'}
|
||||||
@ -1413,9 +1425,23 @@ packages:
|
|||||||
resolution: {integrity: sha512-1rXeuUUiGGrykh+CeBdu5Ie7OJwinCgQY0bc7GCRxy5xVHy+moaqkpL/jqQq0MtQOeYcrqEz4abc5f0KtU7W4A==}
|
resolution: {integrity: sha512-1rXeuUUiGGrykh+CeBdu5Ie7OJwinCgQY0bc7GCRxy5xVHy+moaqkpL/jqQq0MtQOeYcrqEz4abc5f0KtU7W4A==}
|
||||||
engines: {node: '>=12.5.0'}
|
engines: {node: '>=12.5.0'}
|
||||||
|
|
||||||
|
combined-stream@1.0.8:
|
||||||
|
resolution: {integrity: sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==}
|
||||||
|
engines: {node: '>= 0.8'}
|
||||||
|
|
||||||
concat-map@0.0.1:
|
concat-map@0.0.1:
|
||||||
resolution: {integrity: sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==}
|
resolution: {integrity: sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==}
|
||||||
|
|
||||||
|
cookie@1.0.2:
|
||||||
|
resolution: {integrity: sha512-9Kr/j4O16ISv8zBBhJoi4bXOYNTkFLOqSL3UDB0njXxCXNezjeyVrJyGOWtgfs/q2km1gwBcfH8q1yEGoMYunA==}
|
||||||
|
engines: {node: '>=18'}
|
||||||
|
|
||||||
|
cookies-next@5.1.0:
|
||||||
|
resolution: {integrity: sha512-9Ekne+q8hfziJtnT9c1yDUBqT0eDMGgPrfPl4bpR3xwQHLTd/8gbSf6+IEkP/pjGsDZt1TGbC6emYmFYRbIXwQ==}
|
||||||
|
peerDependencies:
|
||||||
|
next: '>=15.0.0'
|
||||||
|
react: '>= 16.8.0'
|
||||||
|
|
||||||
cross-spawn@7.0.6:
|
cross-spawn@7.0.6:
|
||||||
resolution: {integrity: sha512-uV2QOWP2nWzsy2aMp8aRibhi9dlzF5Hgh5SHaB9OiTGEyDTiJJyx0uy51QXdyWbtAHNua4XJzUKca3OzKUd3vA==}
|
resolution: {integrity: sha512-uV2QOWP2nWzsy2aMp8aRibhi9dlzF5Hgh5SHaB9OiTGEyDTiJJyx0uy51QXdyWbtAHNua4XJzUKca3OzKUd3vA==}
|
||||||
engines: {node: '>= 8'}
|
engines: {node: '>= 8'}
|
||||||
@ -1469,6 +1495,10 @@ packages:
|
|||||||
resolution: {integrity: sha512-8QmQKqEASLd5nx0U1B1okLElbUuuttJ/AnYmRXbbbGDWh6uS208EjD4Xqq/I9wK7u0v6O08XhTWnt5XtEbR6Dg==}
|
resolution: {integrity: sha512-8QmQKqEASLd5nx0U1B1okLElbUuuttJ/AnYmRXbbbGDWh6uS208EjD4Xqq/I9wK7u0v6O08XhTWnt5XtEbR6Dg==}
|
||||||
engines: {node: '>= 0.4'}
|
engines: {node: '>= 0.4'}
|
||||||
|
|
||||||
|
delayed-stream@1.0.0:
|
||||||
|
resolution: {integrity: sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ==}
|
||||||
|
engines: {node: '>=0.4.0'}
|
||||||
|
|
||||||
detect-libc@2.0.3:
|
detect-libc@2.0.3:
|
||||||
resolution: {integrity: sha512-bwy0MGW55bG41VqxxypOsdSdGqLwXPI/focwgTYCFMbdUiBAxLg9CFzG08sz2aqzknwiX7Hkl0bQENjg8iLByw==}
|
resolution: {integrity: sha512-bwy0MGW55bG41VqxxypOsdSdGqLwXPI/focwgTYCFMbdUiBAxLg9CFzG08sz2aqzknwiX7Hkl0bQENjg8iLByw==}
|
||||||
engines: {node: '>=8'}
|
engines: {node: '>=8'}
|
||||||
@ -1739,10 +1769,23 @@ packages:
|
|||||||
flatted@3.3.3:
|
flatted@3.3.3:
|
||||||
resolution: {integrity: sha512-GX+ysw4PBCz0PzosHDepZGANEuFCMLrnRTiEy9McGjmkCQYwRq4A/X786G/fjM/+OjsWSU1ZrY5qyARZmO/uwg==}
|
resolution: {integrity: sha512-GX+ysw4PBCz0PzosHDepZGANEuFCMLrnRTiEy9McGjmkCQYwRq4A/X786G/fjM/+OjsWSU1ZrY5qyARZmO/uwg==}
|
||||||
|
|
||||||
|
follow-redirects@1.15.9:
|
||||||
|
resolution: {integrity: sha512-gew4GsXizNgdoRyqmyfMHyAmXsZDk6mHkSxZFCzW9gwlbtOW44CDtYavM+y+72qD/Vq2l550kMF52DT8fOLJqQ==}
|
||||||
|
engines: {node: '>=4.0'}
|
||||||
|
peerDependencies:
|
||||||
|
debug: '*'
|
||||||
|
peerDependenciesMeta:
|
||||||
|
debug:
|
||||||
|
optional: true
|
||||||
|
|
||||||
for-each@0.3.5:
|
for-each@0.3.5:
|
||||||
resolution: {integrity: sha512-dKx12eRCVIzqCxFGplyFKJMPvLEWgmNtUrpTiJIR5u97zEhRG8ySrtboPHZXx7daLxQVrl643cTzbab2tkQjxg==}
|
resolution: {integrity: sha512-dKx12eRCVIzqCxFGplyFKJMPvLEWgmNtUrpTiJIR5u97zEhRG8ySrtboPHZXx7daLxQVrl643cTzbab2tkQjxg==}
|
||||||
engines: {node: '>= 0.4'}
|
engines: {node: '>= 0.4'}
|
||||||
|
|
||||||
|
form-data@4.0.2:
|
||||||
|
resolution: {integrity: sha512-hGfm/slu0ZabnNt4oaRZ6uREyfCj6P4fT/n6A1rGV+Z0VdGXjfOhVUpkn6qVQONHGIFwmveGXyDs75+nr6FM8w==}
|
||||||
|
engines: {node: '>= 6'}
|
||||||
|
|
||||||
function-bind@1.1.2:
|
function-bind@1.1.2:
|
||||||
resolution: {integrity: sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA==}
|
resolution: {integrity: sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA==}
|
||||||
|
|
||||||
@ -2118,6 +2161,14 @@ packages:
|
|||||||
resolution: {integrity: sha512-PXwfBhYu0hBCPw8Dn0E+WDYb7af3dSLVWKi3HGv84IdF4TyFoC0ysxFd0Goxw7nSv4T/PzEJQxsYsEiFCKo2BA==}
|
resolution: {integrity: sha512-PXwfBhYu0hBCPw8Dn0E+WDYb7af3dSLVWKi3HGv84IdF4TyFoC0ysxFd0Goxw7nSv4T/PzEJQxsYsEiFCKo2BA==}
|
||||||
engines: {node: '>=8.6'}
|
engines: {node: '>=8.6'}
|
||||||
|
|
||||||
|
mime-db@1.52.0:
|
||||||
|
resolution: {integrity: sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==}
|
||||||
|
engines: {node: '>= 0.6'}
|
||||||
|
|
||||||
|
mime-types@2.1.35:
|
||||||
|
resolution: {integrity: sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==}
|
||||||
|
engines: {node: '>= 0.6'}
|
||||||
|
|
||||||
minimatch@10.0.1:
|
minimatch@10.0.1:
|
||||||
resolution: {integrity: sha512-ethXTt3SGGR+95gudmqJ1eNhRO7eGEGIgYA9vnPatK4/etz2MEVDno5GMCibdMTuBMyElzIlgxMna3K94XDIDQ==}
|
resolution: {integrity: sha512-ethXTt3SGGR+95gudmqJ1eNhRO7eGEGIgYA9vnPatK4/etz2MEVDno5GMCibdMTuBMyElzIlgxMna3K94XDIDQ==}
|
||||||
engines: {node: 20 || >=22}
|
engines: {node: 20 || >=22}
|
||||||
@ -2335,6 +2386,9 @@ packages:
|
|||||||
prop-types@15.8.1:
|
prop-types@15.8.1:
|
||||||
resolution: {integrity: sha512-oj87CgZICdulUohogVAR7AjlC0327U4el4L6eAvOqCeudMDVU0NThNaV+b9Df4dXgSP1gXMTnPdhfe/2qDH5cg==}
|
resolution: {integrity: sha512-oj87CgZICdulUohogVAR7AjlC0327U4el4L6eAvOqCeudMDVU0NThNaV+b9Df4dXgSP1gXMTnPdhfe/2qDH5cg==}
|
||||||
|
|
||||||
|
proxy-from-env@1.1.0:
|
||||||
|
resolution: {integrity: sha512-D+zkORCbA9f1tdWRK0RaCR3GPv50cMxcrz4X8k5LTSUD1Dkw47mKJEZQNunItRTkWwgtaUSo1RVFRIG9ZXiFYg==}
|
||||||
|
|
||||||
punycode@2.3.1:
|
punycode@2.3.1:
|
||||||
resolution: {integrity: sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg==}
|
resolution: {integrity: sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg==}
|
||||||
engines: {node: '>=6'}
|
engines: {node: '>=6'}
|
||||||
@ -3836,12 +3890,22 @@ snapshots:
|
|||||||
|
|
||||||
async-function@1.0.0: {}
|
async-function@1.0.0: {}
|
||||||
|
|
||||||
|
asynckit@0.4.0: {}
|
||||||
|
|
||||||
available-typed-arrays@1.0.7:
|
available-typed-arrays@1.0.7:
|
||||||
dependencies:
|
dependencies:
|
||||||
possible-typed-array-names: 1.1.0
|
possible-typed-array-names: 1.1.0
|
||||||
|
|
||||||
axe-core@4.10.3: {}
|
axe-core@4.10.3: {}
|
||||||
|
|
||||||
|
axios@1.9.0:
|
||||||
|
dependencies:
|
||||||
|
follow-redirects: 1.15.9
|
||||||
|
form-data: 4.0.2
|
||||||
|
proxy-from-env: 1.1.0
|
||||||
|
transitivePeerDependencies:
|
||||||
|
- debug
|
||||||
|
|
||||||
axobject-query@4.1.0: {}
|
axobject-query@4.1.0: {}
|
||||||
|
|
||||||
balanced-match@1.0.2: {}
|
balanced-match@1.0.2: {}
|
||||||
@ -3917,8 +3981,20 @@ snapshots:
|
|||||||
color-string: 1.9.1
|
color-string: 1.9.1
|
||||||
optional: true
|
optional: true
|
||||||
|
|
||||||
|
combined-stream@1.0.8:
|
||||||
|
dependencies:
|
||||||
|
delayed-stream: 1.0.0
|
||||||
|
|
||||||
concat-map@0.0.1: {}
|
concat-map@0.0.1: {}
|
||||||
|
|
||||||
|
cookie@1.0.2: {}
|
||||||
|
|
||||||
|
cookies-next@5.1.0(next@15.3.1(react-dom@19.1.0(react@19.1.0))(react@19.1.0))(react@19.1.0):
|
||||||
|
dependencies:
|
||||||
|
cookie: 1.0.2
|
||||||
|
next: 15.3.1(react-dom@19.1.0(react@19.1.0))(react@19.1.0)
|
||||||
|
react: 19.1.0
|
||||||
|
|
||||||
cross-spawn@7.0.6:
|
cross-spawn@7.0.6:
|
||||||
dependencies:
|
dependencies:
|
||||||
path-key: 3.1.1
|
path-key: 3.1.1
|
||||||
@ -3971,6 +4047,8 @@ snapshots:
|
|||||||
has-property-descriptors: 1.0.2
|
has-property-descriptors: 1.0.2
|
||||||
object-keys: 1.1.1
|
object-keys: 1.1.1
|
||||||
|
|
||||||
|
delayed-stream@1.0.0: {}
|
||||||
|
|
||||||
detect-libc@2.0.3: {}
|
detect-libc@2.0.3: {}
|
||||||
|
|
||||||
detect-node-es@1.1.0: {}
|
detect-node-es@1.1.0: {}
|
||||||
@ -4396,10 +4474,19 @@ snapshots:
|
|||||||
|
|
||||||
flatted@3.3.3: {}
|
flatted@3.3.3: {}
|
||||||
|
|
||||||
|
follow-redirects@1.15.9: {}
|
||||||
|
|
||||||
for-each@0.3.5:
|
for-each@0.3.5:
|
||||||
dependencies:
|
dependencies:
|
||||||
is-callable: 1.2.7
|
is-callable: 1.2.7
|
||||||
|
|
||||||
|
form-data@4.0.2:
|
||||||
|
dependencies:
|
||||||
|
asynckit: 0.4.0
|
||||||
|
combined-stream: 1.0.8
|
||||||
|
es-set-tostringtag: 2.1.0
|
||||||
|
mime-types: 2.1.35
|
||||||
|
|
||||||
function-bind@1.1.2: {}
|
function-bind@1.1.2: {}
|
||||||
|
|
||||||
function.prototype.name@1.1.8:
|
function.prototype.name@1.1.8:
|
||||||
@ -4752,6 +4839,12 @@ snapshots:
|
|||||||
braces: 3.0.3
|
braces: 3.0.3
|
||||||
picomatch: 2.3.1
|
picomatch: 2.3.1
|
||||||
|
|
||||||
|
mime-db@1.52.0: {}
|
||||||
|
|
||||||
|
mime-types@2.1.35:
|
||||||
|
dependencies:
|
||||||
|
mime-db: 1.52.0
|
||||||
|
|
||||||
minimatch@10.0.1:
|
minimatch@10.0.1:
|
||||||
dependencies:
|
dependencies:
|
||||||
brace-expansion: 2.0.1
|
brace-expansion: 2.0.1
|
||||||
@ -4921,6 +5014,8 @@ snapshots:
|
|||||||
object-assign: 4.1.1
|
object-assign: 4.1.1
|
||||||
react-is: 16.13.1
|
react-is: 16.13.1
|
||||||
|
|
||||||
|
proxy-from-env@1.1.0: {}
|
||||||
|
|
||||||
punycode@2.3.1: {}
|
punycode@2.3.1: {}
|
||||||
|
|
||||||
queue-microtask@1.2.3: {}
|
queue-microtask@1.2.3: {}
|
||||||
|
|||||||
@ -1,5 +1,17 @@
|
|||||||
import AboutPage from "@/pages-templates/about";
|
import AboutPage from '@/pages-templates/about';
|
||||||
|
|
||||||
export default function About(){
|
import { mainPageApi } from '@/features/pages/api/pages.api';
|
||||||
return <AboutPage/>
|
|
||||||
|
import { makeStore } from '@/shared/store';
|
||||||
|
|
||||||
|
export default async function About() {
|
||||||
|
const store = makeStore();
|
||||||
|
|
||||||
|
const { data } = await store.dispatch(
|
||||||
|
mainPageApi.endpoints.fetchAboutUsPageContent.initiate(),
|
||||||
|
);
|
||||||
|
|
||||||
|
if (!data) return null;
|
||||||
|
|
||||||
|
return <AboutPage content={data} />;
|
||||||
}
|
}
|
||||||
|
|||||||
8
src/app/api-utlities/@types/about-us.ts
Normal file
8
src/app/api-utlities/@types/about-us.ts
Normal file
@ -0,0 +1,8 @@
|
|||||||
|
import { HistoryItems, Reviews, Stations, TeamMembers } from '.';
|
||||||
|
|
||||||
|
export type AboutUsPageData = {
|
||||||
|
team: TeamMembers;
|
||||||
|
history: HistoryItems;
|
||||||
|
stations: Stations;
|
||||||
|
reviews: Reviews;
|
||||||
|
};
|
||||||
@ -1,3 +1,13 @@
|
|||||||
|
import {
|
||||||
|
presentDiscounts,
|
||||||
|
presentHistoryItems,
|
||||||
|
presentJobs,
|
||||||
|
presentPartners,
|
||||||
|
presentReviews,
|
||||||
|
presentStations,
|
||||||
|
presentTeamMembers,
|
||||||
|
} from '../presenters';
|
||||||
|
|
||||||
export type Root<T> = { records: T[] };
|
export type Root<T> = { records: T[] };
|
||||||
|
|
||||||
export interface Image {
|
export interface Image {
|
||||||
@ -53,3 +63,30 @@ export type TextResponse = Root<{
|
|||||||
_name: string;
|
_name: string;
|
||||||
_znachenie: string | null;
|
_znachenie: string | null;
|
||||||
}>;
|
}>;
|
||||||
|
|
||||||
|
export type Team = Root<{
|
||||||
|
_foto: Image[];
|
||||||
|
_zvanie: string;
|
||||||
|
_name: string;
|
||||||
|
}>;
|
||||||
|
|
||||||
|
export type History = Root<{
|
||||||
|
_name: string;
|
||||||
|
_god: string;
|
||||||
|
_opisanie: string;
|
||||||
|
}>;
|
||||||
|
|
||||||
|
export type Review = Root<{
|
||||||
|
id: number;
|
||||||
|
_name: string;
|
||||||
|
_otzyv: string;
|
||||||
|
_rejting: number;
|
||||||
|
}>;
|
||||||
|
|
||||||
|
export type TeamMembers = ReturnType<typeof presentTeamMembers>;
|
||||||
|
export type HistoryItems = ReturnType<typeof presentHistoryItems>;
|
||||||
|
export type Stations = ReturnType<typeof presentStations>;
|
||||||
|
export type Partners = ReturnType<typeof presentPartners>;
|
||||||
|
export type Jobs = ReturnType<typeof presentJobs>;
|
||||||
|
export type Discounts = ReturnType<typeof presentDiscounts>;
|
||||||
|
export type Reviews = ReturnType<typeof presentReviews>;
|
||||||
|
|||||||
@ -1,14 +1,4 @@
|
|||||||
import {
|
import { Discounts, Jobs, Partners, Stations } from '.';
|
||||||
presentDiscounts,
|
|
||||||
presentJobs,
|
|
||||||
presentPartners,
|
|
||||||
presentStations,
|
|
||||||
} from '../presenters';
|
|
||||||
|
|
||||||
export type Partners = ReturnType<typeof presentPartners>;
|
|
||||||
export type Jobs = ReturnType<typeof presentJobs>;
|
|
||||||
export type Discounts = ReturnType<typeof presentDiscounts>;
|
|
||||||
export type Stations = ReturnType<typeof presentStations>;
|
|
||||||
|
|
||||||
export type MainPageData = {
|
export type MainPageData = {
|
||||||
discounts: Discounts;
|
discounts: Discounts;
|
||||||
|
|||||||
@ -2,16 +2,23 @@ import { isEmpty } from 'lodash';
|
|||||||
|
|
||||||
import {
|
import {
|
||||||
Discount,
|
Discount,
|
||||||
|
History,
|
||||||
Image,
|
Image,
|
||||||
Job,
|
Job,
|
||||||
Partner,
|
Partner,
|
||||||
|
Review,
|
||||||
|
Select,
|
||||||
Station,
|
Station,
|
||||||
|
Team,
|
||||||
TextResponse,
|
TextResponse,
|
||||||
} from '../@types';
|
} from '../@types';
|
||||||
|
|
||||||
export const presentImage = (images: Image[]) =>
|
export const presentImage = (images: Image[]) =>
|
||||||
isEmpty(images) ? null : `${process.env.TAYLOR_MEDIA_URL}/${images[0].url}`;
|
isEmpty(images) ? null : `${process.env.TAYLOR_MEDIA_URL}/${images[0].url}`;
|
||||||
|
|
||||||
|
export const presentSelect = (selectItems: Select[]) =>
|
||||||
|
!isEmpty(selectItems) ? selectItems[0].name : null;
|
||||||
|
|
||||||
export const presentPartners = (partners: Partner) =>
|
export const presentPartners = (partners: Partner) =>
|
||||||
partners.records.map((record, index) => ({
|
partners.records.map((record, index) => ({
|
||||||
id: index + 1,
|
id: index + 1,
|
||||||
@ -24,8 +31,22 @@ export const presentJobs = (jobs: Job) =>
|
|||||||
id: index + 1,
|
id: index + 1,
|
||||||
name: job._name,
|
name: job._name,
|
||||||
tags: job._tags.map((tag) => tag.name),
|
tags: job._tags.map((tag) => tag.name),
|
||||||
location: !isEmpty(job._localtio) ? job._localtio[0].name : null,
|
location: presentSelect(job._localtio),
|
||||||
type: !isEmpty(job._type) ? job._type[0].name : null,
|
type: presentSelect(job._type),
|
||||||
|
}));
|
||||||
|
|
||||||
|
export const presentTeamMembers = (members: Team) =>
|
||||||
|
members.records.map((member) => ({
|
||||||
|
name: member._name,
|
||||||
|
photo: presentImage(member._foto),
|
||||||
|
profession: member._zvanie,
|
||||||
|
}));
|
||||||
|
|
||||||
|
export const presentHistoryItems = (historyItems: History) =>
|
||||||
|
historyItems.records.map((item) => ({
|
||||||
|
name: item._name,
|
||||||
|
year: item._god,
|
||||||
|
description: item._opisanie,
|
||||||
}));
|
}));
|
||||||
|
|
||||||
export const presentDiscounts = (discounts: Discount) =>
|
export const presentDiscounts = (discounts: Discount) =>
|
||||||
@ -54,7 +75,7 @@ export const presentStations = (stations: Station) =>
|
|||||||
electricCharge: station._propanCopy || false,
|
electricCharge: station._propanCopy || false,
|
||||||
miniMarket: station._zaryadnayaStanci || false,
|
miniMarket: station._zaryadnayaStanci || false,
|
||||||
toilet: station._miniMarketCop || false,
|
toilet: station._miniMarketCop || false,
|
||||||
region: !isEmpty(station._region) ? station._region[0].name : null,
|
region: presentSelect(station._region),
|
||||||
image: presentImage(station._foto),
|
image: presentImage(station._foto),
|
||||||
}));
|
}));
|
||||||
|
|
||||||
@ -63,3 +84,11 @@ export const presentTexts = (texts: TextResponse) =>
|
|||||||
key: item._name,
|
key: item._name,
|
||||||
value: item._znachenie,
|
value: item._znachenie,
|
||||||
}));
|
}));
|
||||||
|
|
||||||
|
export const presentReviews = (reviews: Review) =>
|
||||||
|
reviews.records.map((review) => ({
|
||||||
|
id: review.id,
|
||||||
|
fullname: review._name,
|
||||||
|
review: review._otzyv,
|
||||||
|
rating: review._rejting,
|
||||||
|
}));
|
||||||
|
|||||||
13
src/app/api-utlities/requests/about-us-page.request.ts
Normal file
13
src/app/api-utlities/requests/about-us-page.request.ts
Normal file
@ -0,0 +1,13 @@
|
|||||||
|
import {
|
||||||
|
historyRequest,
|
||||||
|
reviewsRequest,
|
||||||
|
stationsWithImageRequest,
|
||||||
|
teamRequest,
|
||||||
|
} from './common';
|
||||||
|
|
||||||
|
export const aboutUsPageRequest = {
|
||||||
|
...teamRequest,
|
||||||
|
...historyRequest,
|
||||||
|
...stationsWithImageRequest,
|
||||||
|
...reviewsRequest,
|
||||||
|
};
|
||||||
@ -1,3 +1,5 @@
|
|||||||
|
import { EnumType } from 'json-to-graphql-query';
|
||||||
|
|
||||||
export const stationsRequest = {
|
export const stationsRequest = {
|
||||||
_azs: {
|
_azs: {
|
||||||
records: {
|
records: {
|
||||||
@ -27,6 +29,48 @@ export const stationsRequest = {
|
|||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
|
export const stationsWithImageRequest = {
|
||||||
|
_azs: {
|
||||||
|
__args: {
|
||||||
|
filtersSet: {
|
||||||
|
conjunction: new EnumType('and'),
|
||||||
|
filtersSet: [
|
||||||
|
{
|
||||||
|
field: new EnumType('_foto'),
|
||||||
|
operator: 'isNotEmpty',
|
||||||
|
value: [],
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
},
|
||||||
|
|
||||||
|
records: {
|
||||||
|
_name: true,
|
||||||
|
_opisanie: true,
|
||||||
|
_adress: true,
|
||||||
|
_chasyRaboty: {
|
||||||
|
name: true,
|
||||||
|
},
|
||||||
|
_lat: true,
|
||||||
|
_long: true,
|
||||||
|
_avtomojka: true,
|
||||||
|
_dtCopy: true, // ai92
|
||||||
|
_ai92Copy: true, // ai95
|
||||||
|
_ai95Copy: true, // z100
|
||||||
|
_z100Copy: true, // propan
|
||||||
|
_propanCopy: true, // electricCharge
|
||||||
|
_zaryadnayaStanci: true, // miniMarket
|
||||||
|
_miniMarketCop: true, // toilet
|
||||||
|
_region: {
|
||||||
|
name: true,
|
||||||
|
},
|
||||||
|
_foto: {
|
||||||
|
url: true,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
export const partnersRequest = {
|
export const partnersRequest = {
|
||||||
_partners: {
|
_partners: {
|
||||||
records: {
|
records: {
|
||||||
@ -76,3 +120,36 @@ export const textsRequest = {
|
|||||||
},
|
},
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
|
export const teamRequest = {
|
||||||
|
_komanda: {
|
||||||
|
records: {
|
||||||
|
_foto: {
|
||||||
|
url: true,
|
||||||
|
},
|
||||||
|
_zvanie: true,
|
||||||
|
_name: true,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
export const historyRequest = {
|
||||||
|
_istoriya: {
|
||||||
|
records: {
|
||||||
|
_name: true,
|
||||||
|
_god: true,
|
||||||
|
_opisanie: true,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
export const reviewsRequest = {
|
||||||
|
_otzyvy: {
|
||||||
|
records: {
|
||||||
|
id: true,
|
||||||
|
_name: true,
|
||||||
|
_otzyv: true,
|
||||||
|
_rejting: true,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|||||||
10
src/app/api-utlities/utilities/oriyo.client.ts
Normal file
10
src/app/api-utlities/utilities/oriyo.client.ts
Normal file
@ -0,0 +1,10 @@
|
|||||||
|
import { Axios } from 'axios';
|
||||||
|
|
||||||
|
const oriyoClient = new Axios({
|
||||||
|
baseURL: process.env.ORIOYO_API_ENDPOINT || '',
|
||||||
|
headers: {
|
||||||
|
'Content-type': 'application/json',
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
export default oriyoClient;
|
||||||
@ -1,54 +1,39 @@
|
|||||||
import { NextRequest, NextResponse } from 'next/server';
|
import { NextRequest, NextResponse } from 'next/server';
|
||||||
|
import { z } from 'zod';
|
||||||
|
|
||||||
import { LoginData } from '@/entities/auth/model/types';
|
import oriyoClient from '@/app/api-utlities/utilities/oriyo.client';
|
||||||
|
|
||||||
export const GET = async (req: NextRequest) => {
|
import { loginFormSchema } from '@/entities/auth/model/validation/login-form.schema';
|
||||||
if (req.method !== 'GET') {
|
|
||||||
return NextResponse.json(
|
|
||||||
{ error: 'Method is not supported' },
|
|
||||||
{ status: 405 },
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
const { searchParams } = req.nextUrl;
|
import { validationErrorHandler } from '../../middlewares/error-handler.middleware';
|
||||||
|
|
||||||
const phoneNumber = searchParams.get('phoneNumber');
|
const routeHandler = async (req: NextRequest) => {
|
||||||
const cardNumber = searchParams.get('cardNumber');
|
const body = await req.json();
|
||||||
const type = searchParams.get('type');
|
|
||||||
|
|
||||||
if (!phoneNumber || !cardNumber || !type) {
|
const validatedBody = loginFormSchema
|
||||||
return NextResponse.json({ error: 'Bad request' }, { status: 400 });
|
.merge(z.object({ type: z.enum(['bonus', 'corporate']) }))
|
||||||
}
|
.parse(body);
|
||||||
|
|
||||||
try {
|
try {
|
||||||
const loginRes = await fetch(
|
const oriyoResponse = await oriyoClient.get('/client/login', {
|
||||||
`https://test.oriyo.tj/api/client/login?type=${type}&phone=${phoneNumber}&uid=${cardNumber}`,
|
params: {
|
||||||
{
|
type: validatedBody.type,
|
||||||
method: 'GET',
|
phone: validatedBody.phoneNumber,
|
||||||
|
uid: validatedBody.cardNumber,
|
||||||
},
|
},
|
||||||
);
|
});
|
||||||
|
|
||||||
if (!loginRes.ok) {
|
const parsedResponse = JSON.parse(oriyoResponse.data);
|
||||||
return NextResponse.json(
|
|
||||||
{ error: 'Error during login' },
|
|
||||||
{ status: 400 },
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
const data = (await loginRes.json()) as LoginData;
|
if (!parsedResponse.token) {
|
||||||
|
return NextResponse.json({ error: 'Credentials error' }, { status: 401 });
|
||||||
const token = data.token;
|
|
||||||
if (!token) {
|
|
||||||
return NextResponse.json({ error: 'No auth token' }, { status: 401 });
|
|
||||||
}
|
}
|
||||||
|
|
||||||
const response = NextResponse.json({ success: true });
|
const response = NextResponse.json({ success: true });
|
||||||
|
|
||||||
response.cookies.set('token', token, {
|
response.cookies.set(`${validatedBody.type}__token`, oriyoResponse.data, {
|
||||||
httpOnly: true,
|
|
||||||
path: '/',
|
path: '/',
|
||||||
maxAge: 2 * 60 * 60,
|
maxAge: 2 * 60 * 60,
|
||||||
secure: process.env.NODE_ENV === 'production',
|
|
||||||
});
|
});
|
||||||
|
|
||||||
return response;
|
return response;
|
||||||
@ -57,3 +42,5 @@ export const GET = async (req: NextRequest) => {
|
|||||||
return NextResponse.json({ error: 'Server error' }, { status: 500 });
|
return NextResponse.json({ error: 'Server error' }, { status: 500 });
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
export const POST = validationErrorHandler(routeHandler);
|
||||||
|
|||||||
31
src/app/api/bonus/info/route.ts
Normal file
31
src/app/api/bonus/info/route.ts
Normal file
@ -0,0 +1,31 @@
|
|||||||
|
import { NextRequest, NextResponse } from 'next/server';
|
||||||
|
|
||||||
|
import oriyoClient from '@/app/api-utlities/utilities/oriyo.client';
|
||||||
|
|
||||||
|
import { validationErrorHandler } from '../../middlewares/error-handler.middleware';
|
||||||
|
|
||||||
|
const routeHandler = async (req: NextRequest) => {
|
||||||
|
const bonusTokenData = req.cookies.get('bonus__token');
|
||||||
|
|
||||||
|
if (!bonusTokenData) {
|
||||||
|
return NextResponse.json(
|
||||||
|
{ error: 'User does not have access' },
|
||||||
|
{ status: 401 },
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
const { card_id, token } = JSON.parse(bonusTokenData.value);
|
||||||
|
|
||||||
|
const oriyoResponse = await oriyoClient.get('/client/info', {
|
||||||
|
params: {
|
||||||
|
card_id,
|
||||||
|
token,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
return new Response(oriyoResponse.data, {
|
||||||
|
headers: { 'Content-Type': 'application/json' },
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
export const GET = validationErrorHandler(routeHandler);
|
||||||
63
src/app/api/bonus/transactions/route.ts
Normal file
63
src/app/api/bonus/transactions/route.ts
Normal file
@ -0,0 +1,63 @@
|
|||||||
|
import { NextRequest, NextResponse } from 'next/server';
|
||||||
|
import { z } from 'zod';
|
||||||
|
|
||||||
|
import oriyoClient from '@/app/api-utlities/utilities/oriyo.client';
|
||||||
|
|
||||||
|
import { validationErrorHandler } from '../../middlewares/error-handler.middleware';
|
||||||
|
|
||||||
|
const validatedSchema = z.object({
|
||||||
|
start_date: z.string().optional(),
|
||||||
|
end_date: z.string().optional(),
|
||||||
|
limit: z.coerce.number(),
|
||||||
|
page: z.coerce.number(),
|
||||||
|
});
|
||||||
|
|
||||||
|
const routeHandler = async (req: NextRequest) => {
|
||||||
|
const bonusTokenData = req.cookies.get('bonus__token');
|
||||||
|
|
||||||
|
if (!bonusTokenData) {
|
||||||
|
return NextResponse.json(
|
||||||
|
{ error: 'User does not have access' },
|
||||||
|
{ status: 401 },
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
const params = Array.from(req.nextUrl.searchParams.entries()).reduce(
|
||||||
|
(pr, cr) => {
|
||||||
|
pr[cr[0]] = cr[1];
|
||||||
|
|
||||||
|
return pr;
|
||||||
|
},
|
||||||
|
{} as Record<string, string>,
|
||||||
|
);
|
||||||
|
|
||||||
|
const validatedRequest = validatedSchema.parse(params);
|
||||||
|
|
||||||
|
const { card_id, token } = JSON.parse(bonusTokenData.value);
|
||||||
|
|
||||||
|
const oriyoResponse = await oriyoClient.get('/client/transactions', {
|
||||||
|
params: {
|
||||||
|
card_id,
|
||||||
|
token,
|
||||||
|
limit: validatedRequest.limit,
|
||||||
|
page: validatedRequest.page,
|
||||||
|
type: 'bonus',
|
||||||
|
sort: 'id',
|
||||||
|
direction: 'desc',
|
||||||
|
start_date: validatedRequest.start_date,
|
||||||
|
end_date: validatedRequest.end_date,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
const parsedResponse = JSON.parse(oriyoResponse.data);
|
||||||
|
|
||||||
|
if (parsedResponse.error) {
|
||||||
|
return NextResponse.json({ message: 'Fetch error' }, { status: 400 });
|
||||||
|
}
|
||||||
|
|
||||||
|
return new Response(oriyoResponse.data, {
|
||||||
|
headers: { 'Content-Type': 'application/json' },
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
export const GET = validationErrorHandler(routeHandler);
|
||||||
23
src/app/api/corporate/info/route.ts
Normal file
23
src/app/api/corporate/info/route.ts
Normal file
@ -0,0 +1,23 @@
|
|||||||
|
import { omit } from 'lodash';
|
||||||
|
import { NextRequest, NextResponse } from 'next/server';
|
||||||
|
|
||||||
|
import { validationErrorHandler } from '../../middlewares/error-handler.middleware';
|
||||||
|
|
||||||
|
const routeHandler = async (req: NextRequest) => {
|
||||||
|
const bonusTokenData = req.cookies.get('corporate__token');
|
||||||
|
|
||||||
|
if (!bonusTokenData) {
|
||||||
|
return NextResponse.json(
|
||||||
|
{ error: 'User does not have access' },
|
||||||
|
{ status: 401 },
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
const parsedData = JSON.parse(bonusTokenData.value);
|
||||||
|
|
||||||
|
return new Response(JSON.stringify(omit(parsedData, 'token')), {
|
||||||
|
headers: { 'Content-Type': 'application/json' },
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
export const GET = validationErrorHandler(routeHandler);
|
||||||
19
src/app/api/middlewares/error-handler.middleware.ts
Normal file
19
src/app/api/middlewares/error-handler.middleware.ts
Normal file
@ -0,0 +1,19 @@
|
|||||||
|
import { NextRequest, NextResponse } from 'next/server';
|
||||||
|
import { ZodError } from 'zod';
|
||||||
|
|
||||||
|
export const validationErrorHandler =
|
||||||
|
(handler: Function) =>
|
||||||
|
async (req: NextRequest, ...args: any[]) => {
|
||||||
|
try {
|
||||||
|
return await handler(req, ...args);
|
||||||
|
} catch (error) {
|
||||||
|
if (error instanceof ZodError)
|
||||||
|
return NextResponse.json({ message: error.format() }, { status: 400 });
|
||||||
|
|
||||||
|
console.error(error);
|
||||||
|
return NextResponse.json(
|
||||||
|
{ message: 'Server died for some reason' },
|
||||||
|
{ status: 500 },
|
||||||
|
);
|
||||||
|
}
|
||||||
|
};
|
||||||
@ -1,24 +0,0 @@
|
|||||||
import {
|
|
||||||
presentDiscounts,
|
|
||||||
presentJobs,
|
|
||||||
presentPartners,
|
|
||||||
presentStations,
|
|
||||||
} from '@/app/api-utlities/presenters';
|
|
||||||
import { mainPageRequest } from '@/app/api-utlities/requests/main-page.request';
|
|
||||||
import { requestTaylor } from '@/app/api-utlities/utilities/taylor.client';
|
|
||||||
|
|
||||||
export async function GET(request: Request) {
|
|
||||||
const response = await requestTaylor(mainPageRequest);
|
|
||||||
|
|
||||||
return new Response(
|
|
||||||
JSON.stringify({
|
|
||||||
partners: presentPartners(response.data._partners),
|
|
||||||
jobs: presentJobs(response.data._vacancies),
|
|
||||||
discounts: presentDiscounts(response.data._akcii),
|
|
||||||
stations: presentStations(response.data._azs),
|
|
||||||
}),
|
|
||||||
{
|
|
||||||
headers: { 'Content-Type': 'application/json' },
|
|
||||||
},
|
|
||||||
);
|
|
||||||
}
|
|
||||||
@ -1,14 +0,0 @@
|
|||||||
import { presentTexts } from '@/app/api-utlities/presenters';
|
|
||||||
import { textsRequest } from '@/app/api-utlities/requests/common';
|
|
||||||
import { requestTaylor } from '@/app/api-utlities/utilities/taylor.client';
|
|
||||||
|
|
||||||
export async function GET(request: Request) {
|
|
||||||
const response = await requestTaylor(textsRequest);
|
|
||||||
|
|
||||||
return new Response(
|
|
||||||
JSON.stringify(presentTexts(response.data._kontentSajta)),
|
|
||||||
{
|
|
||||||
headers: { 'Content-Type': 'application/json' },
|
|
||||||
},
|
|
||||||
);
|
|
||||||
}
|
|
||||||
@ -37,7 +37,7 @@ export default async function RootLayout({
|
|||||||
className='scroll-smooth'
|
className='scroll-smooth'
|
||||||
style={{ scrollBehavior: 'smooth' }}
|
style={{ scrollBehavior: 'smooth' }}
|
||||||
>
|
>
|
||||||
<body className={`${inter.className} antialiased`}>
|
<body className={`${inter.className} min-w-2xs antialiased`}>
|
||||||
<Providers textItems={response.data as TextItem[]}>
|
<Providers textItems={response.data as TextItem[]}>
|
||||||
<Header />
|
<Header />
|
||||||
{children}
|
{children}
|
||||||
|
|||||||
@ -1,3 +1,7 @@
|
|||||||
|
import { mainPageApi } from '@/features/pages/api/pages.api';
|
||||||
|
|
||||||
|
import { makeStore } from '@/shared/store';
|
||||||
|
|
||||||
import { AboutSection } from '@/widgets/about-section';
|
import { AboutSection } from '@/widgets/about-section';
|
||||||
import { CharitySection } from '@/widgets/charity-section';
|
import { CharitySection } from '@/widgets/charity-section';
|
||||||
import { CtaSection } from '@/widgets/cta-section';
|
import { CtaSection } from '@/widgets/cta-section';
|
||||||
@ -8,23 +12,24 @@ import { PromotionsSection } from '@/widgets/promotions-section';
|
|||||||
import { StatsSection } from '@/widgets/stats-section';
|
import { StatsSection } from '@/widgets/stats-section';
|
||||||
import { VacanciesSection } from '@/widgets/vacancies-section';
|
import { VacanciesSection } from '@/widgets/vacancies-section';
|
||||||
|
|
||||||
import { MainPageData } from './api-utlities/@types/main';
|
|
||||||
|
|
||||||
export default async function Home() {
|
export default async function Home() {
|
||||||
const mainPageData = (await fetch(
|
const store = makeStore();
|
||||||
`${process.env.NEXT_PUBLIC_API_URL}/pages/main`,
|
|
||||||
{ method: 'GET' },
|
const { data, isLoading, error } = await store.dispatch(
|
||||||
).then((res) => res.json())) as MainPageData;
|
mainPageApi.endpoints.fetchMainPageContent.initiate(),
|
||||||
|
);
|
||||||
|
|
||||||
|
if (isLoading || !data) return null;
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<main>
|
<main>
|
||||||
<HeroSection />
|
<HeroSection />
|
||||||
<StatsSection />
|
<StatsSection />
|
||||||
<MapSection stations={mainPageData.stations} />
|
<MapSection stations={data.stations} />
|
||||||
<AboutSection />
|
<AboutSection />
|
||||||
<PromotionsSection discounts={mainPageData.discounts} />
|
<PromotionsSection discounts={data.discounts} />
|
||||||
<VacanciesSection jobs={mainPageData.jobs} />
|
<VacanciesSection jobs={data.jobs} />
|
||||||
<PartnersSection partners={mainPageData.partners} />
|
<PartnersSection partners={data.partners} />
|
||||||
<CharitySection />
|
<CharitySection />
|
||||||
<CtaSection />
|
<CtaSection />
|
||||||
</main>
|
</main>
|
||||||
|
|||||||
@ -1,24 +1,23 @@
|
|||||||
import { createApi, fetchBaseQuery } from '@reduxjs/toolkit/query/react';
|
import { baseAPI } from '@/shared/api/base-api';
|
||||||
|
|
||||||
import { LoginParams, LoginResponse } from '../model/contracts/login.contract';
|
import { LoginParams, LoginResponse } from '../model/contracts/login.contract';
|
||||||
|
|
||||||
export const loginAPI = createApi({
|
export const authenticationApi = baseAPI.injectEndpoints({
|
||||||
baseQuery: fetchBaseQuery({ baseUrl: '/api' }),
|
endpoints: (builder) => ({
|
||||||
endpoints: (build) => ({
|
login: builder.query<LoginResponse, LoginParams>({
|
||||||
login: build.query<LoginResponse, LoginParams>({
|
|
||||||
query: (data) => {
|
query: (data) => {
|
||||||
const params = new URLSearchParams({
|
|
||||||
type: data.type,
|
|
||||||
phoneNumber: data.phoneNumber,
|
|
||||||
cardNumber: data.cardNumber,
|
|
||||||
}).toString();
|
|
||||||
|
|
||||||
return {
|
return {
|
||||||
url: `/auth/login?${params}`,
|
method: 'POST',
|
||||||
|
body: {
|
||||||
|
type: data.type,
|
||||||
|
phoneNumber: data.phoneNumber,
|
||||||
|
cardNumber: data.cardNumber,
|
||||||
|
},
|
||||||
|
url: '/auth/login',
|
||||||
};
|
};
|
||||||
},
|
},
|
||||||
}),
|
}),
|
||||||
}),
|
}),
|
||||||
});
|
});
|
||||||
|
|
||||||
export const { useLazyLoginQuery } = loginAPI;
|
export const { useLazyLoginQuery } = authenticationApi;
|
||||||
|
|||||||
@ -1,4 +1,4 @@
|
|||||||
import { LoginFormData } from '@/features/auth/login-form/model/login-form.schema';
|
import { LoginFormData } from '@/entities/auth/model/validation/login-form.schema';
|
||||||
|
|
||||||
export interface LoginResponse {
|
export interface LoginResponse {
|
||||||
success: boolean;
|
success: boolean;
|
||||||
|
|||||||
33
src/entities/bonus/api/bonus.api.ts
Normal file
33
src/entities/bonus/api/bonus.api.ts
Normal file
@ -0,0 +1,33 @@
|
|||||||
|
import { baseAPI } from '@/shared/api/base-api';
|
||||||
|
|
||||||
|
import {
|
||||||
|
ClientInfo,
|
||||||
|
TransactionRequest,
|
||||||
|
TransactionResponse,
|
||||||
|
} from '../model/types/bonus-client-info.type';
|
||||||
|
|
||||||
|
export const bonusApi = baseAPI.injectEndpoints({
|
||||||
|
endpoints: (builder) => ({
|
||||||
|
fetchMyBonusInfo: builder.query<ClientInfo, any>({
|
||||||
|
query: () => {
|
||||||
|
return {
|
||||||
|
url: '/bonus/info',
|
||||||
|
};
|
||||||
|
},
|
||||||
|
}),
|
||||||
|
fetchBonusTransactions: builder.query<
|
||||||
|
TransactionResponse,
|
||||||
|
TransactionRequest
|
||||||
|
>({
|
||||||
|
query: (request) => {
|
||||||
|
return {
|
||||||
|
url: '/bonus/transactions',
|
||||||
|
params: request,
|
||||||
|
};
|
||||||
|
},
|
||||||
|
}),
|
||||||
|
}),
|
||||||
|
});
|
||||||
|
|
||||||
|
export const { useFetchMyBonusInfoQuery, useFetchBonusTransactionsQuery } =
|
||||||
|
bonusApi;
|
||||||
34
src/entities/bonus/model/types/bonus-client-info.type.ts
Normal file
34
src/entities/bonus/model/types/bonus-client-info.type.ts
Normal file
@ -0,0 +1,34 @@
|
|||||||
|
export interface ClientInfo {
|
||||||
|
card_id: number;
|
||||||
|
fullname: string;
|
||||||
|
cardno: string;
|
||||||
|
reg_date: string;
|
||||||
|
end_date: string;
|
||||||
|
bonuses: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface TransactionResponse {
|
||||||
|
transactions: Transaction[];
|
||||||
|
card_id: string;
|
||||||
|
current_page: number;
|
||||||
|
limit: number;
|
||||||
|
total_records: number;
|
||||||
|
total_pages: number;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface Transaction {
|
||||||
|
id: number;
|
||||||
|
date_create: string;
|
||||||
|
station: string;
|
||||||
|
product_name: string;
|
||||||
|
amount: string;
|
||||||
|
price_real: string;
|
||||||
|
sum_real: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface TransactionRequest {
|
||||||
|
start_date?: string;
|
||||||
|
end_date?: string;
|
||||||
|
page: number;
|
||||||
|
limit: number;
|
||||||
|
}
|
||||||
17
src/entities/corporate/api/corporate.api.ts
Normal file
17
src/entities/corporate/api/corporate.api.ts
Normal file
@ -0,0 +1,17 @@
|
|||||||
|
import { baseAPI } from '@/shared/api/base-api';
|
||||||
|
|
||||||
|
import { CorporateInfoResponse } from '../model/types/corporate-client-info.type';
|
||||||
|
|
||||||
|
export const corporateApi = baseAPI.injectEndpoints({
|
||||||
|
endpoints: (builder) => ({
|
||||||
|
fetchMyCorporateInfo: builder.query<CorporateInfoResponse, any>({
|
||||||
|
query: () => {
|
||||||
|
return {
|
||||||
|
url: '/corporate/info',
|
||||||
|
};
|
||||||
|
},
|
||||||
|
}),
|
||||||
|
}),
|
||||||
|
});
|
||||||
|
|
||||||
|
export const { useFetchMyCorporateInfoQuery } = corporateApi;
|
||||||
@ -0,0 +1,9 @@
|
|||||||
|
export interface CorporateInfoResponse {
|
||||||
|
created_at: string;
|
||||||
|
fund: string;
|
||||||
|
fund_total: string;
|
||||||
|
group_id: number;
|
||||||
|
group_name: string;
|
||||||
|
overdraft: string;
|
||||||
|
total_cards: number;
|
||||||
|
}
|
||||||
@ -19,7 +19,10 @@ import {
|
|||||||
} from '@/shared/shadcn-ui/form';
|
} from '@/shared/shadcn-ui/form';
|
||||||
import { Input } from '@/shared/shadcn-ui/input';
|
import { Input } from '@/shared/shadcn-ui/input';
|
||||||
|
|
||||||
import { LoginFormData, loginFormSchema } from '../model/login-form.schema';
|
import {
|
||||||
|
LoginFormData,
|
||||||
|
loginFormSchema,
|
||||||
|
} from '../../../../entities/auth/model/validation/login-form.schema';
|
||||||
|
|
||||||
interface LoginFormProps {
|
interface LoginFormProps {
|
||||||
type: 'bonus' | 'corporate';
|
type: 'bonus' | 'corporate';
|
||||||
@ -40,16 +43,12 @@ export const LoginForm = ({ type }: LoginFormProps) => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
const onSubmit = async (data: LoginFormData) => {
|
const onSubmit = async (data: LoginFormData) => {
|
||||||
try {
|
await login({ ...data, type }).unwrap();
|
||||||
await login({ ...data, type }).unwrap();
|
|
||||||
|
|
||||||
toast.success('Logged in successfully!');
|
toast.success('Logged in successfully!');
|
||||||
router.push(
|
router.push(
|
||||||
type === 'bonus' ? '/customer-dashboard' : '/corporate-dashboard',
|
type === 'bonus' ? '/customer-dashboard' : '/corporate-dashboard',
|
||||||
);
|
);
|
||||||
} catch (error) {
|
|
||||||
toast.error('An error occured during login');
|
|
||||||
}
|
|
||||||
};
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
|
|||||||
@ -10,7 +10,7 @@ import {
|
|||||||
} from 'lucide-react';
|
} from 'lucide-react';
|
||||||
import { useMemo, useState } from 'react';
|
import { useMemo, useState } from 'react';
|
||||||
|
|
||||||
import { Stations } from '@/app/api-utlities/@types/main';
|
import { Stations } from '@/app/api-utlities/@types';
|
||||||
|
|
||||||
import { useTextController } from '@/shared/language/hooks/use-text-controller';
|
import { useTextController } from '@/shared/language/hooks/use-text-controller';
|
||||||
import { Badge } from '@/shared/shadcn-ui/badge';
|
import { Badge } from '@/shared/shadcn-ui/badge';
|
||||||
|
|||||||
59
src/features/pages/api/pages.api.ts
Normal file
59
src/features/pages/api/pages.api.ts
Normal file
@ -0,0 +1,59 @@
|
|||||||
|
import { jsonToGraphQLQuery } from 'json-to-graphql-query';
|
||||||
|
|
||||||
|
import { AboutUsPageData } from '@/app/api-utlities/@types/about-us';
|
||||||
|
import { MainPageData } from '@/app/api-utlities/@types/main';
|
||||||
|
import {
|
||||||
|
presentDiscounts,
|
||||||
|
presentHistoryItems,
|
||||||
|
presentJobs,
|
||||||
|
presentPartners,
|
||||||
|
presentReviews,
|
||||||
|
presentStations,
|
||||||
|
presentTeamMembers,
|
||||||
|
} from '@/app/api-utlities/presenters';
|
||||||
|
import { aboutUsPageRequest } from '@/app/api-utlities/requests/about-us-page.request';
|
||||||
|
import { mainPageRequest } from '@/app/api-utlities/requests/main-page.request';
|
||||||
|
|
||||||
|
import { taylorAPI } from '@/shared/api/taylor-api';
|
||||||
|
|
||||||
|
export const mainPageApi = taylorAPI.injectEndpoints({
|
||||||
|
endpoints: (builder) => ({
|
||||||
|
fetchMainPageContent: builder.query<MainPageData, void>({
|
||||||
|
query: () => ({
|
||||||
|
url: '',
|
||||||
|
method: 'POST',
|
||||||
|
body: {
|
||||||
|
query: jsonToGraphQLQuery({ query: mainPageRequest }),
|
||||||
|
},
|
||||||
|
}),
|
||||||
|
|
||||||
|
transformResponse: (response: any) => {
|
||||||
|
return {
|
||||||
|
partners: presentPartners(response.data._partners),
|
||||||
|
jobs: presentJobs(response.data._vacancies),
|
||||||
|
discounts: presentDiscounts(response.data._akcii),
|
||||||
|
stations: presentStations(response.data._azs),
|
||||||
|
};
|
||||||
|
},
|
||||||
|
}),
|
||||||
|
|
||||||
|
fetchAboutUsPageContent: builder.mutation<AboutUsPageData, void>({
|
||||||
|
query: () => ({
|
||||||
|
url: '',
|
||||||
|
method: 'POST',
|
||||||
|
body: {
|
||||||
|
query: jsonToGraphQLQuery({ query: aboutUsPageRequest }),
|
||||||
|
},
|
||||||
|
}),
|
||||||
|
|
||||||
|
transformResponse: (response: any) => {
|
||||||
|
return {
|
||||||
|
team: presentTeamMembers(response.data._komanda),
|
||||||
|
history: presentHistoryItems(response.data._istoriya),
|
||||||
|
stations: presentStations(response.data._azs),
|
||||||
|
reviews: presentReviews(response.data._otzyvy),
|
||||||
|
};
|
||||||
|
},
|
||||||
|
}),
|
||||||
|
}),
|
||||||
|
});
|
||||||
@ -4,6 +4,8 @@ import { subMonths } from 'date-fns';
|
|||||||
import { Building2, LogOut, Wallet } from 'lucide-react';
|
import { Building2, LogOut, Wallet } from 'lucide-react';
|
||||||
import { useState } from 'react';
|
import { useState } from 'react';
|
||||||
|
|
||||||
|
import { useFetchMyCorporateInfoQuery } from '@/entities/corporate/api/corporate.api';
|
||||||
|
|
||||||
import { useTextController } from '@/shared/language/hooks/use-text-controller';
|
import { useTextController } from '@/shared/language/hooks/use-text-controller';
|
||||||
import { Button } from '@/shared/shadcn-ui/button';
|
import { Button } from '@/shared/shadcn-ui/button';
|
||||||
import {
|
import {
|
||||||
@ -96,8 +98,10 @@ export function CorporateDashboard() {
|
|||||||
|
|
||||||
const { t } = useTextController();
|
const { t } = useTextController();
|
||||||
|
|
||||||
|
const { data, isLoading } = useFetchMyCorporateInfoQuery({});
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className='flex min-h-screen flex-col'>
|
<div className='flex min-h-screen flex-col px-2.5'>
|
||||||
<main className='flex-1 py-10'>
|
<main className='flex-1 py-10'>
|
||||||
<div className='container mx-auto max-w-6xl'>
|
<div className='container mx-auto max-w-6xl'>
|
||||||
<div className='mb-8 flex items-center justify-between'>
|
<div className='mb-8 flex items-center justify-between'>
|
||||||
@ -110,7 +114,11 @@ export function CorporateDashboard() {
|
|||||||
|
|
||||||
<div className='mb-10 grid gap-3 md:grid-cols-3 md:gap-6'>
|
<div className='mb-10 grid gap-3 md:grid-cols-3 md:gap-6'>
|
||||||
{/* Company Card */}
|
{/* Company Card */}
|
||||||
<Card className='md:col-span-2'>
|
<Card
|
||||||
|
data-aos='zoom-in'
|
||||||
|
data-aos-mirror='true'
|
||||||
|
className='md:col-span-2'
|
||||||
|
>
|
||||||
<CardHeader className='pb-2'>
|
<CardHeader className='pb-2'>
|
||||||
<CardTitle className='flex items-center gap-2'>
|
<CardTitle className='flex items-center gap-2'>
|
||||||
<Building2 className='h-5 w-5 text-red-600' />
|
<Building2 className='h-5 w-5 text-red-600' />
|
||||||
@ -118,55 +126,67 @@ export function CorporateDashboard() {
|
|||||||
</CardTitle>
|
</CardTitle>
|
||||||
</CardHeader>
|
</CardHeader>
|
||||||
<CardContent>
|
<CardContent>
|
||||||
<div className='grid gap-6 md:grid-cols-2'>
|
{!data ? (
|
||||||
<div>
|
<>Loading</>
|
||||||
<div className='mb-4 space-y-1'>
|
) : (
|
||||||
<p className='text-sm text-gray-500'>
|
<div className='grid gap-6 md:grid-cols-2'>
|
||||||
{t('corporate.companyCard.companyNameLabel')}
|
<div>
|
||||||
</p>
|
<div className='mb-4 space-y-1'>
|
||||||
<p className='font-medium'>{companyData.companyName}</p>
|
<p className='text-sm text-gray-500'>
|
||||||
|
{t('corporate.companyCard.companyNameLabel')}
|
||||||
|
</p>
|
||||||
|
<p className='truncate font-medium'>
|
||||||
|
{data.group_name}
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
<div className='mb-4 space-y-1'>
|
||||||
|
<p className='text-sm text-gray-500'>
|
||||||
|
{t('corporate.companyCard.cardsCountLabel')}
|
||||||
|
</p>
|
||||||
|
<p className='font-medium'>{data.total_cards}</p>
|
||||||
|
</div>
|
||||||
|
<div className='space-y-1'>
|
||||||
|
<p className='text-sm text-gray-500'>
|
||||||
|
{t('corporate.companyCard.registrationDateLabel')}
|
||||||
|
</p>
|
||||||
|
|
||||||
|
<p className='font-medium'>
|
||||||
|
{new Date(data.created_at).toLocaleDateString(
|
||||||
|
'en-GB',
|
||||||
|
)}
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div className='mb-4 space-y-1'>
|
<div>
|
||||||
<p className='text-sm text-gray-500'>
|
<div className='mb-4 space-y-1'>
|
||||||
{t('corporate.companyCard.cardsCountLabel')}
|
<p className='text-sm text-gray-500'>
|
||||||
</p>
|
{t('corporate.companyCard.fundLabel')}
|
||||||
<p className='font-medium'>{companyData.numberOfCards}</p>
|
</p>
|
||||||
</div>
|
<p className='font-medium'>
|
||||||
<div className='space-y-1'>
|
{data.fund.toLocaleString()} {t('corporate.currency')}
|
||||||
<p className='text-sm text-gray-500'>
|
</p>
|
||||||
{t('corporate.companyCard.registrationDateLabel')}
|
</div>
|
||||||
</p>
|
<div className='mb-4 space-y-1'>
|
||||||
<p className='font-medium'>
|
<p className='text-sm text-gray-500'>
|
||||||
{companyData.registrationDate}
|
{t('corporate.companyCard.overdraftLabel')}
|
||||||
</p>
|
</p>
|
||||||
|
<p className='font-medium'>
|
||||||
|
{data.overdraft.toLocaleString()}{' '}
|
||||||
|
{t('corporate.currency')}
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div>
|
)}
|
||||||
<div className='mb-4 space-y-1'>
|
|
||||||
<p className='text-sm text-gray-500'>
|
|
||||||
{t('corporate.companyCard.fundLabel')}
|
|
||||||
</p>
|
|
||||||
<p className='font-medium'>
|
|
||||||
{companyData.fund.toLocaleString()}{' '}
|
|
||||||
{t('corporate.currency')}
|
|
||||||
</p>
|
|
||||||
</div>
|
|
||||||
<div className='mb-4 space-y-1'>
|
|
||||||
<p className='text-sm text-gray-500'>
|
|
||||||
{t('corporate.companyCard.overdraftLabel')}
|
|
||||||
</p>
|
|
||||||
<p className='font-medium'>
|
|
||||||
{companyData.overdraft.toLocaleString()}{' '}
|
|
||||||
{t('corporate.currency')}
|
|
||||||
</p>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</CardContent>
|
</CardContent>
|
||||||
</Card>
|
</Card>
|
||||||
|
|
||||||
{/* Fund Card */}
|
{/* Fund Card */}
|
||||||
<Card className='bg-gradient-to-br from-red-600 to-red-800 text-white'>
|
<Card
|
||||||
|
data-aos='zoom-in'
|
||||||
|
data-aos-mirror='true'
|
||||||
|
className='bg-gradient-to-br from-red-600 to-red-800 text-white'
|
||||||
|
>
|
||||||
<CardHeader>
|
<CardHeader>
|
||||||
<CardTitle className='flex items-center gap-2'>
|
<CardTitle className='flex items-center gap-2'>
|
||||||
<Wallet className='h-5 w-5' />
|
<Wallet className='h-5 w-5' />
|
||||||
@ -177,14 +197,18 @@ export function CorporateDashboard() {
|
|||||||
</CardDescription>
|
</CardDescription>
|
||||||
</CardHeader>
|
</CardHeader>
|
||||||
<CardContent>
|
<CardContent>
|
||||||
<div className='text-center'>
|
{!data ? (
|
||||||
<p className='mb-2 text-4xl font-bold'>
|
<>Loading</>
|
||||||
{companyData.totalFund.toLocaleString()}
|
) : (
|
||||||
</p>
|
<div className='text-center'>
|
||||||
<p className='text-white/80'>
|
<p className='mb-2 text-4xl font-bold'>
|
||||||
{t('corporate.fundCard.currency')}
|
{data.fund_total?.toLocaleString()}
|
||||||
</p>
|
</p>
|
||||||
</div>
|
<p className='text-white/80'>
|
||||||
|
{t('corporate.fundCard.currency')}
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
</CardContent>
|
</CardContent>
|
||||||
</Card>
|
</Card>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@ -2,6 +2,8 @@
|
|||||||
|
|
||||||
import { ArrowUpRight, Clock, CreditCard, LogOut, User } from 'lucide-react';
|
import { ArrowUpRight, Clock, CreditCard, LogOut, User } from 'lucide-react';
|
||||||
|
|
||||||
|
import { useFetchMyBonusInfoQuery } from '@/entities/bonus/api/bonus.api';
|
||||||
|
|
||||||
import { useTextController } from '@/shared/language/hooks/use-text-controller';
|
import { useTextController } from '@/shared/language/hooks/use-text-controller';
|
||||||
import { Button } from '@/shared/shadcn-ui/button';
|
import { Button } from '@/shared/shadcn-ui/button';
|
||||||
import {
|
import {
|
||||||
@ -14,22 +16,13 @@ import {
|
|||||||
|
|
||||||
import { TransactionsTable } from '@/widgets/transactions-table';
|
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 function CustomerDashboard() {
|
export function CustomerDashboard() {
|
||||||
const { t } = useTextController();
|
const { t } = useTextController();
|
||||||
|
|
||||||
|
const { data, isLoading } = useFetchMyBonusInfoQuery({});
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className='flex min-h-screen flex-col'>
|
<div className='flex min-h-screen flex-col px-2.5'>
|
||||||
<main className='flex-1 py-10'>
|
<main className='flex-1 py-10'>
|
||||||
<div className='container mx-auto max-w-6xl'>
|
<div className='container mx-auto max-w-6xl'>
|
||||||
<div className='mb-8 flex items-center justify-between'>
|
<div className='mb-8 flex items-center justify-between'>
|
||||||
@ -41,37 +34,46 @@ export function CustomerDashboard() {
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div className='mb-10 grid gap-3 md:grid-cols-3 md:gap-6'>
|
<div className='mb-10 grid gap-3 md:grid-cols-3 md:gap-6'>
|
||||||
{/* Bonus Card */}
|
<Card data-aos="zoom-in" data-aos-mirror="true" className='bg-gradient-to-br from-red-600 to-red-800 text-white'>
|
||||||
<Card className='bg-gradient-to-br from-red-600 to-red-800 text-white'>
|
{!data || isLoading ? (
|
||||||
<CardHeader>
|
// TODO: Bunyod please add loader here
|
||||||
<CardTitle className='flex items-center gap-2'>
|
<>Loader here</>
|
||||||
<CreditCard className='h-5 w-5' />
|
) : (
|
||||||
{t('customer.bonusCard.title')}
|
<>
|
||||||
</CardTitle>
|
<CardHeader>
|
||||||
<CardDescription className='text-white/80'>
|
<CardTitle className='flex items-center gap-2'>
|
||||||
{t('customer.bonusCard.description')}
|
<CreditCard className='h-5 w-5' />
|
||||||
</CardDescription>
|
{t('customer.bonusCard.title')}
|
||||||
</CardHeader>
|
</CardTitle>
|
||||||
<CardContent>
|
<CardDescription className='text-white/80'>
|
||||||
<div className='text-center'>
|
{t('customer.bonusCard.description')}
|
||||||
<p className='mb-2 text-4xl font-bold'>
|
</CardDescription>
|
||||||
{customerData.bonusPoints}
|
</CardHeader>
|
||||||
</p>
|
<CardContent>
|
||||||
<p className='text-white/80'>
|
<div className='text-center'>
|
||||||
{t('customer.bonusCard.points')}
|
<p className='mb-2 text-4xl font-bold'>{data.bonuses}</p>
|
||||||
</p>
|
<p className='text-white/80'>
|
||||||
</div>
|
{t('customer.bonusCard.points')}
|
||||||
<div className='mt-6 flex items-center justify-between'>
|
</p>
|
||||||
<div className='flex items-center gap-1 text-sm text-white/80'>
|
</div>
|
||||||
<Clock className='h-4 w-4' />
|
<div className='mt-6 flex items-center justify-between'>
|
||||||
<span>{t('customer.bonusCard.validUntil')}</span>
|
<div className='flex items-center gap-1 text-sm text-white/80'>
|
||||||
</div>
|
<Clock className='h-4 w-4' />
|
||||||
<ArrowUpRight className='h-5 w-5 text-white/60' />
|
<span>
|
||||||
</div>
|
{t('customer.bonusCard.validUntil')}{' '}
|
||||||
</CardContent>
|
{new Date(data.end_date).toLocaleDateString('en-GB')}
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
<ArrowUpRight className='h-5 w-5 text-white/60' />
|
||||||
|
</div>
|
||||||
|
</CardContent>
|
||||||
|
</>
|
||||||
|
)}
|
||||||
</Card>
|
</Card>
|
||||||
|
|
||||||
|
{/* Bonus Card */}
|
||||||
{/* Customer Card */}
|
{/* Customer Card */}
|
||||||
<Card className='md:col-span-2'>
|
<Card data-aos="zoom-in" data-aos-mirror="true" className='md:col-span-2'>
|
||||||
<CardHeader className='pb-2'>
|
<CardHeader className='pb-2'>
|
||||||
<CardTitle className='flex items-center gap-2'>
|
<CardTitle className='flex items-center gap-2'>
|
||||||
<User className='h-5 w-5 text-red-600' />
|
<User className='h-5 w-5 text-red-600' />
|
||||||
@ -79,40 +81,37 @@ export function CustomerDashboard() {
|
|||||||
</CardTitle>
|
</CardTitle>
|
||||||
</CardHeader>
|
</CardHeader>
|
||||||
<CardContent>
|
<CardContent>
|
||||||
<div className='grid gap-6 md:grid-cols-2'>
|
{!data || isLoading ? (
|
||||||
<div>
|
// TODO: Bunyod please add loader here
|
||||||
<div className='mb-4 space-y-1'>
|
<>Loader here</>
|
||||||
<p className='text-sm text-gray-500'>
|
) : (
|
||||||
{t('customer.infoCard.regDateLabel')}
|
<div className='grid gap-6 md:grid-cols-2'>
|
||||||
</p>
|
<div>
|
||||||
<p className='font-medium'>
|
<div className='mb-4 space-y-1'>
|
||||||
{customerData.firstName} {customerData.lastName}
|
<p className='text-sm text-gray-500'>
|
||||||
</p>
|
{t('customer.infoCard.regDateLabel')}
|
||||||
|
</p>
|
||||||
|
<p className='font-medium'>{data.fullname}</p>
|
||||||
|
</div>
|
||||||
|
<div className='space-y-1'>
|
||||||
|
<p className='text-sm text-gray-500'>
|
||||||
|
{t('customer.infoCard.regDateLabel')}
|
||||||
|
</p>
|
||||||
|
<p className='font-medium'>
|
||||||
|
{new Date(data.reg_date).toLocaleDateString('en-GB')}
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div className='space-y-1'>
|
<div>
|
||||||
<p className='text-sm text-gray-500'>
|
<div className='mb-4 space-y-1'>
|
||||||
{t('customer.infoCard.regDateLabel')}
|
<p className='text-sm text-gray-500'>
|
||||||
</p>
|
{t('customer.infoCard.cardNumberLabel')}
|
||||||
<p className='font-medium'>
|
</p>
|
||||||
{customerData.registrationDate}
|
<p className='font-medium'>{data.cardno}</p>
|
||||||
</p>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div>
|
)}
|
||||||
<div className='mb-4 space-y-1'>
|
|
||||||
<p className='text-sm text-gray-500'>
|
|
||||||
{t('customer.infoCard.cardNumberLabel')}
|
|
||||||
</p>
|
|
||||||
<p className='font-medium'>{customerData.cardNumber}</p>
|
|
||||||
</div>
|
|
||||||
<div className='mb-4 space-y-1'>
|
|
||||||
<p className='text-sm text-gray-500'>
|
|
||||||
{t('customer.infoCard.expiryDateLabel')}
|
|
||||||
</p>
|
|
||||||
<p className='font-medium'>{customerData.expiryDate}</p>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</CardContent>
|
</CardContent>
|
||||||
</Card>
|
</Card>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@ -3,12 +3,13 @@
|
|||||||
import { Fuel, History, MapPin, Star, Target, Users } from 'lucide-react';
|
import { Fuel, History, MapPin, Star, Target, Users } from 'lucide-react';
|
||||||
import Image from 'next/image';
|
import Image from 'next/image';
|
||||||
|
|
||||||
// import { useTranslation } from 'next-i18next';
|
import { AboutUsPageData } from '@/app/api-utlities/@types/about-us';
|
||||||
|
|
||||||
import AnimatedCounter from '@/shared/components/animated-counter';
|
import AnimatedCounter from '@/shared/components/animated-counter';
|
||||||
import { useTextController } from '@/shared/language/hooks/use-text-controller';
|
import { useTextController } from '@/shared/language/hooks/use-text-controller';
|
||||||
import { Button } from '@/shared/shadcn-ui/button';
|
import { Button } from '@/shared/shadcn-ui/button';
|
||||||
import { Card, CardContent } from '@/shared/shadcn-ui/card';
|
import { Card, CardContent } from '@/shared/shadcn-ui/card';
|
||||||
|
import Container from '@/shared/shadcn-ui/conteiner';
|
||||||
|
|
||||||
import { CompanyTimeline } from '@/widgets/about-page/company-timeline';
|
import { CompanyTimeline } from '@/widgets/about-page/company-timeline';
|
||||||
import { StationGallery } from '@/widgets/about-page/station-gallery';
|
import { StationGallery } from '@/widgets/about-page/station-gallery';
|
||||||
@ -19,7 +20,11 @@ export const metadata = {
|
|||||||
description: 'about.metadata.description',
|
description: 'about.metadata.description',
|
||||||
};
|
};
|
||||||
|
|
||||||
export default function AboutPage() {
|
export interface AboutPageProps {
|
||||||
|
content: AboutUsPageData;
|
||||||
|
}
|
||||||
|
|
||||||
|
export default function AboutPage({ content }: AboutPageProps) {
|
||||||
const { t } = useTextController();
|
const { t } = useTextController();
|
||||||
|
|
||||||
return (
|
return (
|
||||||
@ -36,8 +41,12 @@ export default function AboutPage() {
|
|||||||
className='object-cover'
|
className='object-cover'
|
||||||
priority
|
priority
|
||||||
/>
|
/>
|
||||||
<div className='absolute inset-0 flex items-center bg-gradient-to-r from-black/70 to-black/30'>
|
<div className='absolute inset-0 flex items-center bg-gradient-to-r from-black/70 to-black/30 px-2'>
|
||||||
<div className='container mx-auto'>
|
<div
|
||||||
|
data-aos='fade-down'
|
||||||
|
data-aos-duration='1000'
|
||||||
|
className='container mx-auto'
|
||||||
|
>
|
||||||
<div className='max-w-2xl space-y-4 text-white'>
|
<div className='max-w-2xl space-y-4 text-white'>
|
||||||
<h1 className='text-4xl font-bold tracking-tight sm:text-5xl md:text-6xl'>
|
<h1 className='text-4xl font-bold tracking-tight sm:text-5xl md:text-6xl'>
|
||||||
{t('about.hero.title')}
|
{t('about.hero.title')}
|
||||||
@ -52,55 +61,60 @@ export default function AboutPage() {
|
|||||||
</section>
|
</section>
|
||||||
|
|
||||||
{/* Company Overview */}
|
{/* Company Overview */}
|
||||||
<section className='py-16'>
|
<Container>
|
||||||
<div className='container mx-auto'>
|
<section className='py-16'>
|
||||||
<div className='grid items-center gap-12 md:grid-cols-2'>
|
<div className='container mx-auto'>
|
||||||
<div>
|
<div className='grid items-center gap-12 md:grid-cols-2'>
|
||||||
<div className='mb-4 inline-flex items-center justify-center rounded-full bg-red-100 p-2'>
|
<div data-aos='fade-right'>
|
||||||
<Fuel className='h-6 w-6 text-red-600' />
|
<div className='mb-4 inline-flex items-center justify-center rounded-full bg-red-100 p-2'>
|
||||||
</div>
|
<Fuel className='h-6 w-6 text-red-600' />
|
||||||
<h2 className='mb-6 text-3xl font-bold tracking-tight sm:text-4xl'>
|
</div>
|
||||||
{t('about.overview.title')}
|
<h2 className='mb-6 text-3xl font-bold tracking-tight sm:text-4xl'>
|
||||||
</h2>
|
{t('about.overview.title')}
|
||||||
<p className='mb-6 text-gray-600'>
|
</h2>
|
||||||
{t('about.overview.description1')}
|
<p className='mb-6 text-gray-600'>
|
||||||
</p>
|
{t('about.overview.description1')}
|
||||||
<p className='mb-6 text-gray-600'>
|
</p>
|
||||||
{t('about.overview.description2')}
|
<p className='mb-6 text-gray-600'>
|
||||||
</p>
|
{t('about.overview.description2')}
|
||||||
<p className='mb-6 text-gray-600'>
|
</p>
|
||||||
{t('about.overview.description3')}
|
<p className='mb-6 text-gray-600'>
|
||||||
</p>
|
{t('about.overview.description3')}
|
||||||
|
</p>
|
||||||
|
|
||||||
<div className='mb-6 grid grid-cols-2 gap-4'>
|
<div className='mb-6 grid grid-cols-1 gap-4 md:grid-cols-2'>
|
||||||
{[0, 1, 2, 3].map((index) => (
|
{[0, 1, 2, 3].map((index) => (
|
||||||
<div key={index} className='flex items-start'>
|
<div key={index} className='flex items-start'>
|
||||||
<div className='mt-1 flex h-6 w-6 flex-shrink-0 items-center justify-center rounded-full bg-red-600'>
|
<div className='mt-1 flex h-6 w-6 flex-shrink-0 items-center justify-center rounded-full bg-red-600'>
|
||||||
<span className='text-xs text-white'>✓</span>
|
<span className='text-xs text-white'>✓</span>
|
||||||
|
</div>
|
||||||
|
<div className='ml-3'>
|
||||||
|
<h3 className='text-lg font-medium'>
|
||||||
|
{t(`about.overview.benefits.${index}.title`)}
|
||||||
|
</h3>
|
||||||
|
<p className='text-gray-600'>
|
||||||
|
{t(`about.overview.benefits.${index}.description`)}
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div className='ml-3'>
|
))}
|
||||||
<h3 className='text-lg font-medium'>
|
</div>
|
||||||
{t(`about.overview.benefits.${index}.title`)}
|
</div>
|
||||||
</h3>
|
<div
|
||||||
<p className='text-gray-600'>
|
data-aos='zoom-out-right'
|
||||||
{t(`about.overview.benefits.${index}.description`)}
|
className='relative h-[500px] overflow-hidden rounded-xl shadow-xl'
|
||||||
</p>
|
>
|
||||||
</div>
|
<Image
|
||||||
</div>
|
src='/placeholder.svg?height=500&width=600&text=Главный+офис'
|
||||||
))}
|
alt={t('about.overview.imageAlt')}
|
||||||
|
fill
|
||||||
|
className='object-cover'
|
||||||
|
/>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
|
||||||
<div className='relative h-[500px] overflow-hidden rounded-xl shadow-xl'>
|
|
||||||
<Image
|
|
||||||
src='/placeholder.svg?height=500&width=600&text=Главный+офис'
|
|
||||||
alt={t('about.overview.imageAlt')}
|
|
||||||
fill
|
|
||||||
className='object-cover'
|
|
||||||
/>
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</section>
|
||||||
</section>
|
</Container>
|
||||||
|
|
||||||
{/* Stats Section */}
|
{/* Stats Section */}
|
||||||
<section className='bg-red-600 py-16 text-white'>
|
<section className='bg-red-600 py-16 text-white'>
|
||||||
@ -113,7 +127,7 @@ export default function AboutPage() {
|
|||||||
{t('about.stats.subtitle')}
|
{t('about.stats.subtitle')}
|
||||||
</p>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
<div className='grid grid-cols-2 gap-8 text-center md:grid-cols-4'>
|
<div className='grid grid-cols-1 gap-8 text-center sm:grid-cols-2 md:grid-cols-4'>
|
||||||
{[0, 1, 2, 3].map((index) => (
|
{[0, 1, 2, 3].map((index) => (
|
||||||
<div key={index} className='space-y-2'>
|
<div key={index} className='space-y-2'>
|
||||||
<h3 className='text-4xl font-bold'>
|
<h3 className='text-4xl font-bold'>
|
||||||
@ -139,176 +153,195 @@ export default function AboutPage() {
|
|||||||
{/* Our History */}
|
{/* Our History */}
|
||||||
<section className='py-16'>
|
<section className='py-16'>
|
||||||
<div className='container mx-auto'>
|
<div className='container mx-auto'>
|
||||||
<div className='mb-12 flex flex-col items-center justify-center text-center'>
|
<Container>
|
||||||
<div className='mb-4 inline-flex items-center justify-center rounded-full bg-red-100 p-2'>
|
<div className='mb-12 flex flex-col items-center justify-center text-center'>
|
||||||
<History className='h-6 w-6 text-red-600' />
|
<div className='mb-4 inline-flex items-center justify-center rounded-full bg-red-100 p-2'>
|
||||||
|
<History className='h-6 w-6 text-red-600' />
|
||||||
|
</div>
|
||||||
|
<h2 className='mb-4 text-3xl font-bold tracking-tight sm:text-4xl'>
|
||||||
|
{t('about.history.title')}
|
||||||
|
</h2>
|
||||||
|
<p className='max-w-2xl text-gray-600'>
|
||||||
|
{t('about.history.subtitle')}
|
||||||
|
</p>
|
||||||
</div>
|
</div>
|
||||||
<h2 className='mb-4 text-3xl font-bold tracking-tight sm:text-4xl'>
|
</Container>
|
||||||
{t('about.history.title')}
|
|
||||||
</h2>
|
|
||||||
<p className='max-w-2xl text-gray-600'>
|
|
||||||
{t('about.history.subtitle')}
|
|
||||||
</p>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<CompanyTimeline />
|
<Container>
|
||||||
|
<CompanyTimeline timeline={content.history} />
|
||||||
|
</Container>
|
||||||
</div>
|
</div>
|
||||||
</section>
|
</section>
|
||||||
|
|
||||||
{/* Our Stations */}
|
{/* Our Stations */}
|
||||||
<section className='bg-gray-50 py-16'>
|
<Container>
|
||||||
<div className='container mx-auto'>
|
<section className='bg-gray-50 py-16'>
|
||||||
<div className='mb-12 flex flex-col items-center justify-center text-center'>
|
<div className='container mx-auto'>
|
||||||
<div className='mb-4 inline-flex items-center justify-center rounded-full bg-red-100 p-2'>
|
<div className='mb-12 flex flex-col items-center justify-center text-center'>
|
||||||
<MapPin className='h-6 w-6 text-red-600' />
|
<div className='mb-4 inline-flex items-center justify-center rounded-full bg-red-100 p-2'>
|
||||||
|
<MapPin className='h-6 w-6 text-red-600' />
|
||||||
|
</div>
|
||||||
|
<h2 className='mb-4 text-3xl font-bold tracking-tight sm:text-4xl'>
|
||||||
|
{t('about.stations.title')}
|
||||||
|
</h2>
|
||||||
|
<p className='max-w-2xl text-gray-600'>
|
||||||
|
{t('about.stations.subtitle')}
|
||||||
|
</p>
|
||||||
</div>
|
</div>
|
||||||
<h2 className='mb-4 text-3xl font-bold tracking-tight sm:text-4xl'>
|
|
||||||
{t('about.stations.title')}
|
|
||||||
</h2>
|
|
||||||
<p className='max-w-2xl text-gray-600'>
|
|
||||||
{t('about.stations.subtitle')}
|
|
||||||
</p>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<StationGallery />
|
<StationGallery stations={content.stations} />
|
||||||
|
|
||||||
<div className='mt-12 text-center'>
|
<div className='mt-12 text-center'>
|
||||||
<p className='mx-auto mb-6 max-w-2xl text-gray-600'>
|
<p className='mx-auto mb-6 max-w-2xl text-gray-600'>
|
||||||
{t('about.stations.description')}
|
{t('about.stations.description')}
|
||||||
</p>
|
</p>
|
||||||
<Button className='bg-red-600 hover:bg-red-700'>
|
<Button className='bg-red-600 hover:bg-red-700'>
|
||||||
{t('about.stations.buttonText')}{' '}
|
{t('about.stations.buttonText')}{' '}
|
||||||
<MapPin className='ml-2 h-4 w-4' />
|
<MapPin className='ml-2 h-4 w-4' />
|
||||||
</Button>
|
</Button>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</section>
|
||||||
</section>
|
</Container>
|
||||||
|
|
||||||
{/* Our Values */}
|
{/* Our Values */}
|
||||||
<section className='py-16'>
|
<Container>
|
||||||
<div className='container mx-auto'>
|
<section className='py-16'>
|
||||||
<div className='mb-12 flex flex-col items-center justify-center text-center'>
|
<div className='container mx-auto'>
|
||||||
<div className='mb-4 inline-flex items-center justify-center rounded-full bg-red-100 p-2'>
|
<div className='mb-12 flex flex-col items-center justify-center text-center'>
|
||||||
<Target className='h-6 w-6 text-red-600' />
|
<div className='mb-4 inline-flex items-center justify-center rounded-full bg-red-100 p-2'>
|
||||||
|
<Target className='h-6 w-6 text-red-600' />
|
||||||
|
</div>
|
||||||
|
<h2 className='mb-4 text-3xl font-bold tracking-tight sm:text-4xl'>
|
||||||
|
{t('about.values.title')}
|
||||||
|
</h2>
|
||||||
|
<p className='max-w-2xl text-gray-600'>
|
||||||
|
{t('about.values.subtitle')}
|
||||||
|
</p>
|
||||||
</div>
|
</div>
|
||||||
<h2 className='mb-4 text-3xl font-bold tracking-tight sm:text-4xl'>
|
|
||||||
{t('about.values.title')}
|
|
||||||
</h2>
|
|
||||||
<p className='max-w-2xl text-gray-600'>
|
|
||||||
{t('about.values.subtitle')}
|
|
||||||
</p>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div className='grid gap-8 md:grid-cols-3'>
|
<div
|
||||||
{[0, 1, 2].map((index) => (
|
data-aos='flip-up'
|
||||||
<Card
|
data-aos-duration='600'
|
||||||
key={index}
|
className='grid gap-8 md:grid-cols-3'
|
||||||
className='overflow-hidden transition-all hover:shadow-lg'
|
>
|
||||||
>
|
{[0, 1, 2].map((index) => (
|
||||||
<CardContent className='p-6'>
|
<Card
|
||||||
<div className='mb-4 flex h-12 w-12 items-center justify-center rounded-full bg-red-100'>
|
key={index}
|
||||||
<Star className='h-6 w-6 text-red-600' />
|
className='overflow-hidden transition-all hover:shadow-lg'
|
||||||
</div>
|
>
|
||||||
<h3 className='mb-2 text-xl font-bold'>
|
<CardContent className='p-6'>
|
||||||
{t(`about.values.items.${index}.title`)}
|
<div className='mb-4 flex h-12 w-12 items-center justify-center rounded-full bg-red-100'>
|
||||||
</h3>
|
<Star className='h-6 w-6 text-red-600' />
|
||||||
<p className='text-gray-600'>
|
</div>
|
||||||
{t(`about.values.items.${index}.description`)}
|
<h3 className='mb-2 text-xl font-bold'>
|
||||||
</p>
|
{t(`about.values.items.${index}.title`)}
|
||||||
</CardContent>
|
</h3>
|
||||||
</Card>
|
<p className='text-gray-600'>
|
||||||
))}
|
{t(`about.values.items.${index}.description`)}
|
||||||
|
</p>
|
||||||
|
</CardContent>
|
||||||
|
</Card>
|
||||||
|
))}
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</section>
|
||||||
</section>
|
</Container>
|
||||||
|
|
||||||
{/* Our Team */}
|
{/* Our Team */}
|
||||||
<section className='bg-gray-50 py-16'>
|
<Container>
|
||||||
<div className='container mx-auto'>
|
<section className='bg-gray-50 py-16'>
|
||||||
<div className='mb-12 flex flex-col items-center justify-center text-center'>
|
<div className='container mx-auto'>
|
||||||
<div className='mb-4 inline-flex items-center justify-center rounded-full bg-red-100 p-2'>
|
<div className='mb-12 flex flex-col items-center justify-center text-center'>
|
||||||
<Users className='h-6 w-6 text-red-600' />
|
<div className='mb-4 inline-flex items-center justify-center rounded-full bg-red-100 p-2'>
|
||||||
</div>
|
<Users className='h-6 w-6 text-red-600' />
|
||||||
<h2 className='mb-4 text-3xl font-bold tracking-tight sm:text-4xl'>
|
|
||||||
{t('about.team.title')}
|
|
||||||
</h2>
|
|
||||||
<p className='max-w-2xl text-gray-600'>
|
|
||||||
{t('about.team.subtitle')}
|
|
||||||
</p>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div className='grid grid-cols-1 gap-8 sm:grid-cols-2 lg:grid-cols-4'>
|
|
||||||
{[0, 1, 2, 3].map((index) => (
|
|
||||||
<div
|
|
||||||
key={index}
|
|
||||||
className='overflow-hidden rounded-lg bg-white shadow-md transition-transform hover:scale-105'
|
|
||||||
>
|
|
||||||
<div className='relative h-64 w-full'>
|
|
||||||
<Image
|
|
||||||
src={`/placeholder.svg?height=300&width=300&text=${t(`about.team.members.${index}.name`)}`}
|
|
||||||
alt={t(`about.team.members.${index}.name`)}
|
|
||||||
fill
|
|
||||||
className='object-cover'
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
<div className='p-4 text-center'>
|
|
||||||
<h3 className='text-lg font-bold'>
|
|
||||||
{t(`about.team.members.${index}.name`)}
|
|
||||||
</h3>
|
|
||||||
<p className='text-gray-600'>
|
|
||||||
{t(`about.team.members.${index}.position`)}
|
|
||||||
</p>
|
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
))}
|
<h2 className='mb-4 text-3xl font-bold tracking-tight sm:text-4xl'>
|
||||||
|
{t('about.team.title')}
|
||||||
|
</h2>
|
||||||
|
<p className='max-w-2xl text-gray-600'>
|
||||||
|
{t('about.team.subtitle')}
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div
|
||||||
|
data-aos='flip-down'
|
||||||
|
data-aos-duration='600'
|
||||||
|
className='grid grid-cols-1 gap-8 sm:grid-cols-2 lg:grid-cols-4'
|
||||||
|
>
|
||||||
|
{content.team.map((member, index) => (
|
||||||
|
<div
|
||||||
|
key={index}
|
||||||
|
className='overflow-hidden rounded-lg bg-white shadow-md transition-transform hover:scale-105'
|
||||||
|
>
|
||||||
|
<div className='relative h-64 w-full'>
|
||||||
|
{member.photo && (
|
||||||
|
<Image
|
||||||
|
src={member.photo}
|
||||||
|
alt={t(`about.team.members.${index}.name`)}
|
||||||
|
fill
|
||||||
|
className='object-cover'
|
||||||
|
/>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
<div className='p-4 text-center'>
|
||||||
|
<h3 className='text-lg font-bold'>{member.name}</h3>
|
||||||
|
<p className='text-gray-600'>{member.profession}</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
))}
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</section>
|
||||||
</section>
|
</Container>
|
||||||
|
|
||||||
{/* Testimonials */}
|
{/* Testimonials */}
|
||||||
<section className='py-16'>
|
<Container>
|
||||||
<div className='container mx-auto'>
|
<section className='py-16'>
|
||||||
<div className='mb-12 flex flex-col items-center justify-center text-center'>
|
<div className='container mx-auto'>
|
||||||
<div className='mb-4 inline-flex items-center justify-center rounded-full bg-red-100 p-2'>
|
<div className='mb-12 flex flex-col items-center justify-center text-center'>
|
||||||
<Star className='h-6 w-6 text-red-600' />
|
<div className='mb-4 inline-flex items-center justify-center rounded-full bg-red-100 p-2'>
|
||||||
|
<Star className='h-6 w-6 text-red-600' />
|
||||||
|
</div>
|
||||||
|
<h2 className='mb-4 text-3xl font-bold tracking-tight sm:text-4xl'>
|
||||||
|
{t('about.testimonials.title')}
|
||||||
|
</h2>
|
||||||
|
<p className='max-w-2xl text-gray-600'>
|
||||||
|
{t('about.testimonials.subtitle')}
|
||||||
|
</p>
|
||||||
</div>
|
</div>
|
||||||
<h2 className='mb-4 text-3xl font-bold tracking-tight sm:text-4xl'>
|
|
||||||
{t('about.testimonials.title')}
|
|
||||||
</h2>
|
|
||||||
<p className='max-w-2xl text-gray-600'>
|
|
||||||
{t('about.testimonials.subtitle')}
|
|
||||||
</p>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div className='grid gap-8 md:grid-cols-3'>
|
<div
|
||||||
{[0, 1, 2].map((index) => (
|
data-aos='zoom-out-right'
|
||||||
<Card
|
className='grid gap-8 md:grid-cols-3'
|
||||||
key={index}
|
>
|
||||||
className='overflow-hidden transition-all hover:shadow-lg'
|
{content.reviews.map((review, index) => (
|
||||||
>
|
<Card
|
||||||
<CardContent className='p-6'>
|
key={index}
|
||||||
<div className='mb-4 flex'>
|
className='overflow-hidden transition-all hover:shadow-lg'
|
||||||
{Array(5)
|
>
|
||||||
.fill(0)
|
<CardContent className='p-6'>
|
||||||
.map((_, i) => (
|
<div className='mb-4 flex'>
|
||||||
<Star
|
{Array(5)
|
||||||
key={i}
|
.fill(0)
|
||||||
className={`h-5 w-5 ${i < Number(t(`about.testimonials.items.${index}.rating`)) ? 'fill-yellow-400 text-yellow-400' : 'text-gray-300'}`}
|
.map((_, i) => (
|
||||||
/>
|
<Star
|
||||||
))}
|
key={i}
|
||||||
</div>
|
className={`h-5 w-5 ${i < Number(review.rating) ? 'fill-yellow-400 text-yellow-400' : 'text-gray-300'}`}
|
||||||
<p className='mb-4 text-gray-600 italic'>
|
/>
|
||||||
"{t(`about.testimonials.items.${index}.text`)}"
|
))}
|
||||||
</p>
|
</div>
|
||||||
<p className='font-semibold'>
|
<p className='mb-4 text-gray-600 italic'>
|
||||||
{t(`about.testimonials.items.${index}.name`)}
|
"{review.review}"
|
||||||
</p>
|
</p>
|
||||||
</CardContent>
|
<p className='font-semibold'>{review.fullname}</p>
|
||||||
</Card>
|
</CardContent>
|
||||||
))}
|
</Card>
|
||||||
|
))}
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</section>
|
||||||
</section>
|
</Container>
|
||||||
|
|
||||||
<CtaSection />
|
<CtaSection />
|
||||||
</main>
|
</main>
|
||||||
|
|||||||
@ -21,6 +21,7 @@ import {
|
|||||||
} from '@/shared/shadcn-ui/card';
|
} from '@/shared/shadcn-ui/card';
|
||||||
|
|
||||||
import { CtaSection } from '@/widgets/cta-section';
|
import { CtaSection } from '@/widgets/cta-section';
|
||||||
|
import Container from '@/shared/shadcn-ui/conteiner';
|
||||||
|
|
||||||
export const metadata = {
|
export const metadata = {
|
||||||
title: 'Благотворительность | GasNetwork - Сеть заправок в Таджикистане',
|
title: 'Благотворительность | GasNetwork - Сеть заправок в Таджикистане',
|
||||||
@ -46,68 +47,72 @@ export function CharityPage() {
|
|||||||
priority
|
priority
|
||||||
/>
|
/>
|
||||||
<div className='absolute inset-0 flex items-center bg-gradient-to-r from-black/70 to-black/30'>
|
<div className='absolute inset-0 flex items-center bg-gradient-to-r from-black/70 to-black/30'>
|
||||||
<div className='container mx-auto'>
|
<Container>
|
||||||
<div className='max-w-2xl space-y-6 text-white'>
|
<div data-aos='fade-down' data-aos-duration='800' className='container mx-auto'>
|
||||||
<div className='inline-flex items-center justify-center rounded-full bg-red-600/20 p-2'>
|
<div className='max-w-2xl space-y-6 text-white'>
|
||||||
<Heart className='size-6 text-red-500' />
|
<div className='inline-flex items-center justify-center rounded-full bg-red-600/20 p-2'>
|
||||||
|
<Heart className='size-6 text-red-500' />
|
||||||
|
</div>
|
||||||
|
<h1 className='text-3xl font-bold tracking-tight sm:text-5xl md:text-6xl'>
|
||||||
|
{t('charity.hero.title')}
|
||||||
|
</h1>
|
||||||
|
<p className='text-lg sm:text-xl text-gray-200'>
|
||||||
|
{t('charity.hero.subtitle')}
|
||||||
|
</p>
|
||||||
</div>
|
</div>
|
||||||
<h1 className='text-4xl font-bold tracking-tight sm:text-5xl md:text-6xl'>
|
|
||||||
{t('charity.hero.title')}
|
|
||||||
</h1>
|
|
||||||
<p className='text-xl text-gray-200'>
|
|
||||||
{t('charity.hero.subtitle')}
|
|
||||||
</p>
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</Container>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</section>
|
</section>
|
||||||
|
|
||||||
{/* Mission Section */}
|
{/* Mission Section */}
|
||||||
<section className='py-16'>
|
<Container>
|
||||||
<div className='container mx-auto'>
|
<section className='py-16'>
|
||||||
<div className='grid items-center gap-12 md:grid-cols-2'>
|
<div className='container mx-auto'>
|
||||||
<div>
|
<div className='grid items-center gap-12 md:grid-cols-2'>
|
||||||
<div className='mb-4 inline-flex items-center justify-center rounded-full bg-red-100 p-2'>
|
<div>
|
||||||
<Heart className='h-6 w-6 text-red-600' />
|
<div className='mb-4 inline-flex items-center justify-center rounded-full bg-red-100 p-2'>
|
||||||
</div>
|
<Heart className='h-6 w-6 text-red-600' />
|
||||||
<h2 className='mb-6 text-3xl font-bold tracking-tight sm:text-4xl'>
|
</div>
|
||||||
{t('charity.mission.title')}
|
<h2 className='mb-6 text-3xl font-bold tracking-tight sm:text-4xl'>
|
||||||
</h2>
|
{t('charity.mission.title')}
|
||||||
<p className='mb-6 text-gray-600'>
|
</h2>
|
||||||
{t('charity.mission.description1')}
|
<p className='mb-6 text-gray-600'>
|
||||||
</p>
|
{t('charity.mission.description1')}
|
||||||
<p className='mb-6 text-gray-600'>
|
</p>
|
||||||
{t('charity.mission.description2')}
|
<p className='mb-6 text-gray-600'>
|
||||||
</p>
|
{t('charity.mission.description2')}
|
||||||
|
</p>
|
||||||
|
|
||||||
<div className='space-y-4'>
|
<div className='space-y-4'>
|
||||||
{[0, 1, 2].map((index) => (
|
{[0, 1, 2].map((index) => (
|
||||||
<div key={index} className='flex items-start'>
|
<div key={index} className='flex items-start'>
|
||||||
<CheckCircle className='mr-3 h-6 w-6 flex-shrink-0 text-red-600' />
|
<CheckCircle className='mr-3 h-6 w-6 flex-shrink-0 text-red-600' />
|
||||||
<div>
|
<div>
|
||||||
<h3 className='text-lg font-medium'>
|
<h3 className='text-lg font-medium'>
|
||||||
{t(`charity.mission.principles.${index}.title`)}
|
{t(`charity.mission.principles.${index}.title`)}
|
||||||
</h3>
|
</h3>
|
||||||
<p className='text-gray-600'>
|
<p className='text-gray-600'>
|
||||||
{t(`charity.mission.principles.${index}.description`)}
|
{t(`charity.mission.principles.${index}.description`)}
|
||||||
</p>
|
</p>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
))}
|
||||||
))}
|
</div>
|
||||||
|
</div>
|
||||||
|
<div className='relative h-[500px] overflow-hidden rounded-xl shadow-xl'>
|
||||||
|
<Image
|
||||||
|
src='/placeholder.svg?height=500&width=600&text=Наша+миссия'
|
||||||
|
alt={t('charity.mission.imageAlt')}
|
||||||
|
fill
|
||||||
|
className='object-cover'
|
||||||
|
/>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
|
||||||
<div className='relative h-[500px] overflow-hidden rounded-xl shadow-xl'>
|
|
||||||
<Image
|
|
||||||
src='/placeholder.svg?height=500&width=600&text=Наша+миссия'
|
|
||||||
alt={t('charity.mission.imageAlt')}
|
|
||||||
fill
|
|
||||||
className='object-cover'
|
|
||||||
/>
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</section>
|
||||||
</section>
|
</Container>
|
||||||
|
|
||||||
{/* Key Figures */}
|
{/* Key Figures */}
|
||||||
<section className='bg-red-600 py-16 text-white'>
|
<section className='bg-red-600 py-16 text-white'>
|
||||||
@ -136,140 +141,146 @@ export function CharityPage() {
|
|||||||
</section>
|
</section>
|
||||||
|
|
||||||
{/* Upcoming Events */}
|
{/* Upcoming Events */}
|
||||||
<section className='bg-gray-50 py-16'>
|
<Container>
|
||||||
<div className='container mx-auto'>
|
<section className='bg-gray-50 py-16'>
|
||||||
<div className='mb-12 text-center'>
|
<div className='container mx-auto'>
|
||||||
<div className='mb-4 inline-flex items-center justify-center rounded-full bg-red-100 p-2'>
|
<div className='mb-12 text-center'>
|
||||||
<Calendar className='h-6 w-6 text-red-600' />
|
<div className='mb-4 inline-flex items-center justify-center rounded-full bg-red-100 p-2'>
|
||||||
|
<Calendar className='h-6 w-6 text-red-600' />
|
||||||
|
</div>
|
||||||
|
<h2 className='mb-4 text-3xl font-bold tracking-tight sm:text-4xl'>
|
||||||
|
{t('charity.events.title')}
|
||||||
|
</h2>
|
||||||
|
<p className='mx-auto max-w-2xl text-gray-600'>
|
||||||
|
{t('charity.events.subtitle')}
|
||||||
|
</p>
|
||||||
</div>
|
</div>
|
||||||
<h2 className='mb-4 text-3xl font-bold tracking-tight sm:text-4xl'>
|
|
||||||
{t('charity.events.title')}
|
|
||||||
</h2>
|
|
||||||
<p className='mx-auto max-w-2xl text-gray-600'>
|
|
||||||
{t('charity.events.subtitle')}
|
|
||||||
</p>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div className='grid gap-6 md:grid-cols-3'>
|
<div className='grid gap-6 md:grid-cols-3'>
|
||||||
{[
|
{[
|
||||||
{
|
{
|
||||||
title: 'Благотворительный марафон',
|
title: 'Благотворительный марафон',
|
||||||
description:
|
description:
|
||||||
'Ежегодный благотворительный марафон в поддержку детей с особыми потребностями.',
|
'Ежегодный благотворительный марафон в поддержку детей с особыми потребностями.',
|
||||||
date: '15 июня 2023',
|
date: '15 июня 2023',
|
||||||
location: 'Парк Рудаки, Душанбе',
|
location: 'Парк Рудаки, Душанбе',
|
||||||
image: '/placeholder.svg?height=200&width=300&text=Марафон',
|
image: '/placeholder.svg?height=200&width=300&text=Марафон',
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
title: 'Экологическая акция',
|
title: 'Экологическая акция',
|
||||||
description:
|
description:
|
||||||
'Очистка берегов реки Варзоб от мусора и посадка деревьев.',
|
'Очистка берегов реки Варзоб от мусора и посадка деревьев.',
|
||||||
date: '22 июля 2023',
|
date: '22 июля 2023',
|
||||||
location: 'Река Варзоб, Душанбе',
|
location: 'Река Варзоб, Душанбе',
|
||||||
image:
|
image:
|
||||||
'/placeholder.svg?height=200&width=300&text=Экологическая+акция',
|
'/placeholder.svg?height=200&width=300&text=Экологическая+акция',
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
title: 'Сбор школьных принадлежностей',
|
title: 'Сбор школьных принадлежностей',
|
||||||
description:
|
description:
|
||||||
'Сбор школьных принадлежностей для детей из малообеспеченных семей к новому учебному году.',
|
'Сбор школьных принадлежностей для детей из малообеспеченных семей к новому учебному году.',
|
||||||
date: '1-20 августа 2023',
|
date: '1-20 августа 2023',
|
||||||
location: 'Все заправки GasNetwork',
|
location: 'Все заправки GasNetwork',
|
||||||
image:
|
image:
|
||||||
'/placeholder.svg?height=200&width=300&text=Школьные+принадлежности',
|
'/placeholder.svg?height=200&width=300&text=Школьные+принадлежности',
|
||||||
},
|
},
|
||||||
].map((event, index) => (
|
].map((event, index) => (
|
||||||
<Card key={index} className='overflow-hidden'>
|
<Card key={index} className='overflow-hidden flex flex-col justify-between'>
|
||||||
<div className='relative h-48 w-full'>
|
<div>
|
||||||
<Image
|
<div className='relative h-48 w-full'>
|
||||||
src={event.image || '/placeholder.svg'}
|
<Image
|
||||||
alt={event.title}
|
src={event.image || '/placeholder.svg'}
|
||||||
fill
|
alt={event.title}
|
||||||
className='object-cover'
|
fill
|
||||||
/>
|
className='object-cover'
|
||||||
</div>
|
/>
|
||||||
<CardHeader>
|
</div>
|
||||||
<CardTitle>{event.title}</CardTitle>
|
<CardHeader>
|
||||||
</CardHeader>
|
<CardTitle className='text-xl lg:text-2xl'>{event.title}</CardTitle>
|
||||||
<CardContent className='space-y-4'>
|
</CardHeader>
|
||||||
<p className='text-gray-600'>{event.description}</p>
|
<CardContent className='space-y-4'>
|
||||||
<div className='flex items-center text-sm text-gray-500'>
|
<p className='text-gray-600'>{event.description}</p>
|
||||||
<Calendar className='mr-2 h-4 w-4' />
|
<div className='flex items-center text-sm text-gray-500'>
|
||||||
{event.date}
|
<Calendar className='mr-2 h-4 w-4' />
|
||||||
|
{event.date}
|
||||||
|
</div>
|
||||||
|
<div className='flex items-center text-sm text-gray-500'>
|
||||||
|
<MapPin className='mr-2 h-4 w-4' />
|
||||||
|
{event.location}
|
||||||
|
</div>
|
||||||
|
</CardContent>
|
||||||
</div>
|
</div>
|
||||||
<div className='flex items-center text-sm text-gray-500'>
|
<CardFooter>
|
||||||
<MapPin className='mr-2 h-4 w-4' />
|
<Button className='w-full bg-red-600 hover:bg-red-700'>
|
||||||
{event.location}
|
{t(`charity.events.button`)}
|
||||||
</div>
|
</Button>
|
||||||
</CardContent>
|
</CardFooter>
|
||||||
<CardFooter>
|
</Card>
|
||||||
<Button className='w-full bg-red-600 hover:bg-red-700'>
|
))}
|
||||||
{t(`charity.events.button`)}
|
</div>
|
||||||
</Button>
|
|
||||||
</CardFooter>
|
|
||||||
</Card>
|
|
||||||
))}
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</section>
|
||||||
</section>
|
</Container>
|
||||||
|
|
||||||
{/* How to Help */}
|
{/* How to Help */}
|
||||||
<section className='py-16'>
|
<Container>
|
||||||
<div className='container mx-auto'>
|
<section className='py-16'>
|
||||||
<div className='mb-12 text-center'>
|
<div className='container mx-auto'>
|
||||||
<div className='mb-4 inline-flex items-center justify-center rounded-full bg-red-100 p-2'>
|
<div className='mb-12 text-center'>
|
||||||
<Users className='h-6 w-6 text-red-600' />
|
<div className='mb-4 inline-flex items-center justify-center rounded-full bg-red-100 p-2'>
|
||||||
|
<Users className='h-6 w-6 text-red-600' />
|
||||||
|
</div>
|
||||||
|
<h2 className='mb-4 text-3xl font-bold tracking-tight sm:text-4xl'>
|
||||||
|
{t('charity.help.title')}
|
||||||
|
</h2>
|
||||||
|
<p className='mx-auto max-w-2xl text-gray-600'>
|
||||||
|
{t('charity.help.subtitle')}
|
||||||
|
</p>
|
||||||
</div>
|
</div>
|
||||||
<h2 className='mb-4 text-3xl font-bold tracking-tight sm:text-4xl'>
|
|
||||||
{t('charity.help.title')}
|
|
||||||
</h2>
|
|
||||||
<p className='mx-auto max-w-2xl text-gray-600'>
|
|
||||||
{t('charity.help.subtitle')}
|
|
||||||
</p>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div className='grid gap-3 md:grid-cols-2 md:gap-6 lg:grid-cols-4'>
|
<div className='grid gap-3 md:grid-cols-2 md:gap-6 lg:grid-cols-4'>
|
||||||
{[
|
{[
|
||||||
{
|
{
|
||||||
title: 'Сделать пожертвование',
|
title: 'Сделать пожертвование',
|
||||||
description:
|
description:
|
||||||
'Ваше пожертвование поможет нам реализовать больше проектов и помочь большему количеству людей.',
|
'Ваше пожертвование поможет нам реализовать больше проектов и помочь большему количеству людей.',
|
||||||
icon: <Landmark className='h-10 w-10 text-red-600' />,
|
icon: <Landmark className='h-10 w-10 text-red-600' />,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
title: 'Стать волонтером',
|
title: 'Стать волонтером',
|
||||||
description:
|
description:
|
||||||
'Присоединяйтесь к нашей команде волонтеров и помогайте нам в реализации благотворительных проектов.',
|
'Присоединяйтесь к нашей команде волонтеров и помогайте нам в реализации благотворительных проектов.',
|
||||||
icon: <Users className='h-10 w-10 text-red-600' />,
|
icon: <Users className='h-10 w-10 text-red-600' />,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
title: 'Участвовать в мероприятиях',
|
title: 'Участвовать в мероприятиях',
|
||||||
description:
|
description:
|
||||||
'Принимайте участие в наших благотворительных мероприятиях и акциях.',
|
'Принимайте участие в наших благотворительных мероприятиях и акциях.',
|
||||||
icon: <Calendar className='h-10 w-10 text-red-600' />,
|
icon: <Calendar className='h-10 w-10 text-red-600' />,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
title: 'Распространять информацию',
|
title: 'Распространять информацию',
|
||||||
description:
|
description:
|
||||||
'Расскажите о нашем фонде и его деятельности своим друзьям и знакомым.',
|
'Расскажите о нашем фонде и его деятельности своим друзьям и знакомым.',
|
||||||
icon: <Heart className='h-10 w-10 text-red-600' />,
|
icon: <Heart className='h-10 w-10 text-red-600' />,
|
||||||
},
|
},
|
||||||
].map((item, index) => (
|
].map((item, index) => (
|
||||||
<Card key={index} className='text-center'>
|
<Card key={index} className='text-center'>
|
||||||
<CardHeader>
|
<CardHeader>
|
||||||
<div className='mb-4 flex justify-center'>{item.icon}</div>
|
<div className='mb-4 flex justify-center'>{item.icon}</div>
|
||||||
<CardTitle className='break-words hyphens-auto'>
|
<CardTitle className='break-words hyphens-auto'>
|
||||||
{item.title}
|
{item.title}
|
||||||
</CardTitle>
|
</CardTitle>
|
||||||
</CardHeader>
|
</CardHeader>
|
||||||
<CardContent>
|
<CardContent>
|
||||||
<p className='text-gray-600'>{item.description}</p>
|
<p className='text-gray-600'>{item.description}</p>
|
||||||
</CardContent>
|
</CardContent>
|
||||||
</Card>
|
</Card>
|
||||||
))}
|
))}
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</section>
|
||||||
</section>
|
</Container>
|
||||||
<CtaSection />
|
<CtaSection />
|
||||||
</main>
|
</main>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@ -6,6 +6,7 @@ import Image from 'next/image';
|
|||||||
import { useTextController } from '@/shared/language/hooks/use-text-controller';
|
import { useTextController } from '@/shared/language/hooks/use-text-controller';
|
||||||
import { Button } from '@/shared/shadcn-ui/button';
|
import { Button } from '@/shared/shadcn-ui/button';
|
||||||
import { Card, CardContent } from '@/shared/shadcn-ui/card';
|
import { Card, CardContent } from '@/shared/shadcn-ui/card';
|
||||||
|
import Container from '@/shared/shadcn-ui/conteiner';
|
||||||
|
|
||||||
export function CertificatesPage() {
|
export function CertificatesPage() {
|
||||||
const { t } = useTextController();
|
const { t } = useTextController();
|
||||||
@ -64,7 +65,7 @@ export function CertificatesPage() {
|
|||||||
];
|
];
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<Container>
|
||||||
<main className='container mx-auto py-10'>
|
<main className='container mx-auto py-10'>
|
||||||
<div className='mb-10 text-center'>
|
<div className='mb-10 text-center'>
|
||||||
<h1 className='mb-4 text-4xl font-bold'>{t('certificates.title')}</h1>
|
<h1 className='mb-4 text-4xl font-bold'>{t('certificates.title')}</h1>
|
||||||
@ -76,6 +77,7 @@ export function CertificatesPage() {
|
|||||||
<div className='grid gap-8 md:grid-cols-2 lg:grid-cols-3'>
|
<div className='grid gap-8 md:grid-cols-2 lg:grid-cols-3'>
|
||||||
{certificates.map((certificate) => (
|
{certificates.map((certificate) => (
|
||||||
<Card
|
<Card
|
||||||
|
data-aos='zoom-in'
|
||||||
key={certificate.id}
|
key={certificate.id}
|
||||||
className='overflow-hidden transition-all duration-300 hover:shadow-lg'
|
className='overflow-hidden transition-all duration-300 hover:shadow-lg'
|
||||||
>
|
>
|
||||||
@ -122,6 +124,6 @@ export function CertificatesPage() {
|
|||||||
))}
|
))}
|
||||||
</div>
|
</div>
|
||||||
</main>
|
</main>
|
||||||
</>
|
</Container>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
@ -7,6 +7,7 @@ import { useTextController } from '@/shared/language/hooks/use-text-controller';
|
|||||||
import { BenefitsSection } from '@/widgets/clients/ui/benefits-section';
|
import { BenefitsSection } from '@/widgets/clients/ui/benefits-section';
|
||||||
import { ServicesOverviewSection } from '@/widgets/clients/ui/services-overview-section';
|
import { ServicesOverviewSection } from '@/widgets/clients/ui/services-overview-section';
|
||||||
import { CtaSection } from '@/widgets/cta-section';
|
import { CtaSection } from '@/widgets/cta-section';
|
||||||
|
import Container from '@/shared/shadcn-ui/conteiner';
|
||||||
|
|
||||||
export const metadata = {
|
export const metadata = {
|
||||||
title: 'Клиентам | GasNetwork - Сеть заправок в Таджикистане',
|
title: 'Клиентам | GasNetwork - Сеть заправок в Таджикистане',
|
||||||
@ -32,16 +33,18 @@ export function ClientsPage() {
|
|||||||
priority
|
priority
|
||||||
/>
|
/>
|
||||||
<div className='absolute inset-0 flex items-center bg-gradient-to-r from-black/70 to-black/30'>
|
<div className='absolute inset-0 flex items-center bg-gradient-to-r from-black/70 to-black/30'>
|
||||||
<div className='container mx-auto'>
|
<Container>
|
||||||
<div className='max-w-2xl space-y-4 text-white'>
|
<div data-aos='fade-down' data-aos-duration="1000" className='container mx-auto'>
|
||||||
<h1 className='text-4xl font-bold tracking-tight sm:text-5xl md:text-6xl'>
|
<div className='max-w-2xl space-y-4 text-white'>
|
||||||
{t('clients.title')}
|
<h1 className='text-4xl font-bold tracking-tight sm:text-5xl md:text-6xl'>
|
||||||
</h1>
|
{t('clients.title')}
|
||||||
<p className='text-lg text-gray-200'>
|
</h1>
|
||||||
{t('clients.description')}
|
<p className='text-lg text-gray-200'>
|
||||||
</p>
|
{t('clients.description')}
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</Container>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</section>
|
</section>
|
||||||
|
|||||||
@ -7,6 +7,7 @@ import { useTextController } from '@/shared/language/hooks/use-text-controller';
|
|||||||
import { Card, CardContent } from '@/shared/shadcn-ui/card';
|
import { Card, CardContent } from '@/shared/shadcn-ui/card';
|
||||||
|
|
||||||
import { CtaSection } from '@/widgets/cta-section';
|
import { CtaSection } from '@/widgets/cta-section';
|
||||||
|
import Container from '@/shared/shadcn-ui/conteiner';
|
||||||
|
|
||||||
export const metadata = {
|
export const metadata = {
|
||||||
title: 'Программа лояльности | GasNetwork - Сеть заправок в Таджикистане',
|
title: 'Программа лояльности | GasNetwork - Сеть заправок в Таджикистане',
|
||||||
@ -32,105 +33,108 @@ export function LoyaltyPage() {
|
|||||||
priority
|
priority
|
||||||
/>
|
/>
|
||||||
<div className='absolute inset-0 flex items-center bg-gradient-to-r from-black/70 to-black/30'>
|
<div className='absolute inset-0 flex items-center bg-gradient-to-r from-black/70 to-black/30'>
|
||||||
<div className='container mx-auto'>
|
<Container>
|
||||||
<div className='max-w-2xl space-y-4 text-white'>
|
<div data-aos='fade-down' data-aos-duration="800" className='container mx-auto'>
|
||||||
<h1 className='text-4xl font-bold tracking-tight sm:text-5xl md:text-6xl'>
|
<div className='max-w-2xl space-y-4 text-white'>
|
||||||
{t('clients.loyalty.title')}
|
<h1 className='text-4xl font-bold tracking-tight sm:text-5xl md:text-6xl'>
|
||||||
</h1>
|
{t('clients.loyalty.title')}
|
||||||
<p className='text-lg text-gray-200'>
|
</h1>
|
||||||
{t('clients.loyalty.description')}
|
<p className='text-lg text-gray-200'>
|
||||||
|
{t('clients.loyalty.description')}
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</Container>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</section>
|
||||||
|
|
||||||
|
<Container>
|
||||||
|
{/* Program Overview */}
|
||||||
|
<section className='py-16'>
|
||||||
|
<div className='container mx-auto'>
|
||||||
|
<div className='grid items-center gap-12 md:grid-cols-2'>
|
||||||
|
<div data-aos='fade-right'>
|
||||||
|
<div className='mb-4 inline-flex items-center justify-center rounded-full bg-red-100 p-2'>
|
||||||
|
<Percent className='h-6 w-6 text-red-600' />
|
||||||
|
</div>
|
||||||
|
<h2 className='mb-6 text-3xl font-bold tracking-tight sm:text-4xl'>
|
||||||
|
{t('clients.loyalty.programm.about')}
|
||||||
|
</h2>
|
||||||
|
<p className='mb-6 text-gray-600'>
|
||||||
|
{t('clients.loyalty.programm.about-description')}
|
||||||
</p>
|
</p>
|
||||||
|
<p className='mb-6 text-gray-600'>
|
||||||
|
{t('clients.loyalty.programm.about-description-2')}
|
||||||
|
</p>
|
||||||
|
|
||||||
|
<div className='space-y-4'>
|
||||||
|
<div className='flex items-start'>
|
||||||
|
<div className='mt-1 flex h-6 w-6 flex-shrink-0 items-center justify-center rounded-full bg-red-600'>
|
||||||
|
<span className='text-xs text-white'>✓</span>
|
||||||
|
</div>
|
||||||
|
<div className='ml-3'>
|
||||||
|
<h3 className='text-lg font-medium'>
|
||||||
|
{t('clients.loyalty.programm.conditions-1')}
|
||||||
|
</h3>
|
||||||
|
<p className='text-gray-600'>
|
||||||
|
{t('clients.loyalty.programm.conditions.description-1')}
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div className='flex items-start'>
|
||||||
|
<div className='mt-1 flex h-6 w-6 flex-shrink-0 items-center justify-center rounded-full bg-red-600'>
|
||||||
|
<span className='text-xs text-white'>✓</span>
|
||||||
|
</div>
|
||||||
|
<div className='ml-3'>
|
||||||
|
<h3 className='text-lg font-medium'>
|
||||||
|
{t('clients.loyalty.programm.conditions-2')}
|
||||||
|
</h3>
|
||||||
|
<p className='text-gray-600'>
|
||||||
|
{t('clients.loyalty.programm.conditions.description-2')}
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div className='flex items-start'>
|
||||||
|
<div className='mt-1 flex h-6 w-6 flex-shrink-0 items-center justify-center rounded-full bg-red-600'>
|
||||||
|
<span className='text-xs text-white'>✓</span>
|
||||||
|
</div>
|
||||||
|
<div className='ml-3'>
|
||||||
|
<h3 className='text-lg font-medium'>
|
||||||
|
{t('clients.loyalty.programm.conditions-3')}
|
||||||
|
</h3>
|
||||||
|
<p className='text-gray-600'>
|
||||||
|
{t('clients.loyalty.programm.conditions.description-3')}
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div data-aos='fade-up' className='relative h-[400px] overflow-hidden rounded-xl shadow-xl'>
|
||||||
|
<Image
|
||||||
|
src='/placeholder.svg?height=400&width=600&text=Программа+лояльности'
|
||||||
|
alt='Программа лояльности'
|
||||||
|
fill
|
||||||
|
className='object-cover'
|
||||||
|
/>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</section>
|
||||||
</section>
|
|
||||||
|
|
||||||
{/* Program Overview */}
|
{/* How It Works */}
|
||||||
<section className='py-16'>
|
<section className='bg-gray-50 py-16'>
|
||||||
<div className='container mx-auto'>
|
<div className='container mx-auto'>
|
||||||
<div className='grid items-center gap-12 md:grid-cols-2'>
|
<div className='mb-12 text-center'>
|
||||||
<div>
|
<h2 className='mb-4 text-3xl font-bold tracking-tight sm:text-4xl'>
|
||||||
<div className='mb-4 inline-flex items-center justify-center rounded-full bg-red-100 p-2'>
|
{t('clients.loyalty.works.title')}
|
||||||
<Percent className='h-6 w-6 text-red-600' />
|
|
||||||
</div>
|
|
||||||
<h2 className='mb-6 text-3xl font-bold tracking-tight sm:text-4xl'>
|
|
||||||
{t('clients.loyalty.programm.about')}
|
|
||||||
</h2>
|
</h2>
|
||||||
<p className='mb-6 text-gray-600'>
|
<p className='mx-auto max-w-2xl text-gray-600'>
|
||||||
{t('clients.loyalty.programm.about-description')}
|
{t('clients.loyalty.works.description')}
|
||||||
</p>
|
</p>
|
||||||
<p className='mb-6 text-gray-600'>
|
|
||||||
{t('clients.loyalty.programm.about-description-2')}
|
|
||||||
</p>
|
|
||||||
|
|
||||||
<div className='space-y-4'>
|
|
||||||
<div className='flex items-start'>
|
|
||||||
<div className='mt-1 flex h-6 w-6 flex-shrink-0 items-center justify-center rounded-full bg-red-600'>
|
|
||||||
<span className='text-xs text-white'>✓</span>
|
|
||||||
</div>
|
|
||||||
<div className='ml-3'>
|
|
||||||
<h3 className='text-lg font-medium'>
|
|
||||||
{t('clients.loyalty.programm.conditions-1')}
|
|
||||||
</h3>
|
|
||||||
<p className='text-gray-600'>
|
|
||||||
{t('clients.loyalty.programm.conditions.description-1')}
|
|
||||||
</p>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div className='flex items-start'>
|
|
||||||
<div className='mt-1 flex h-6 w-6 flex-shrink-0 items-center justify-center rounded-full bg-red-600'>
|
|
||||||
<span className='text-xs text-white'>✓</span>
|
|
||||||
</div>
|
|
||||||
<div className='ml-3'>
|
|
||||||
<h3 className='text-lg font-medium'>
|
|
||||||
{t('clients.loyalty.programm.conditions-2')}
|
|
||||||
</h3>
|
|
||||||
<p className='text-gray-600'>
|
|
||||||
{t('clients.loyalty.programm.conditions.description-2')}
|
|
||||||
</p>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div className='flex items-start'>
|
|
||||||
<div className='mt-1 flex h-6 w-6 flex-shrink-0 items-center justify-center rounded-full bg-red-600'>
|
|
||||||
<span className='text-xs text-white'>✓</span>
|
|
||||||
</div>
|
|
||||||
<div className='ml-3'>
|
|
||||||
<h3 className='text-lg font-medium'>
|
|
||||||
{t('clients.loyalty.programm.conditions-3')}
|
|
||||||
</h3>
|
|
||||||
<p className='text-gray-600'>
|
|
||||||
{t('clients.loyalty.programm.conditions.description-3')}
|
|
||||||
</p>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
<div className='relative h-[400px] overflow-hidden rounded-xl shadow-xl'>
|
|
||||||
<Image
|
|
||||||
src='/placeholder.svg?height=400&width=600&text=Программа+лояльности'
|
|
||||||
alt='Программа лояльности'
|
|
||||||
fill
|
|
||||||
className='object-cover'
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</section>
|
|
||||||
|
|
||||||
{/* How It Works */}
|
<div data-aos='zoom-in-right' className='grid gap-8 md:grid-cols-4'>
|
||||||
<section className='bg-gray-50 py-16'>
|
|
||||||
<div className='container mx-auto'>
|
|
||||||
<div className='mb-12 text-center'>
|
|
||||||
<h2 className='mb-4 text-3xl font-bold tracking-tight sm:text-4xl'>
|
|
||||||
{t('clients.loyalty.works.title')}
|
|
||||||
</h2>
|
|
||||||
<p className='mx-auto max-w-2xl text-gray-600'>
|
|
||||||
{t('clients.loyalty.works.description')}
|
|
||||||
</p>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div className='grid gap-8 md:grid-cols-4'>
|
|
||||||
<div className='text-center'>
|
<div className='text-center'>
|
||||||
<div className='mx-auto mb-4 flex h-16 w-16 items-center justify-center rounded-full bg-red-600 text-2xl font-bold text-white'>
|
<div className='mx-auto mb-4 flex h-16 w-16 items-center justify-center rounded-full bg-red-600 text-2xl font-bold text-white'>
|
||||||
1
|
1
|
||||||
@ -177,149 +181,150 @@ export function LoyaltyPage() {
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</section>
|
</section>
|
||||||
|
|
||||||
{/* Loyalty Levels */}
|
{/* Loyalty Levels */}
|
||||||
<section className='py-16'>
|
<section className='py-16'>
|
||||||
<div className='container mx-auto'>
|
<div className='container mx-auto'>
|
||||||
<div className='mb-12 text-center'>
|
<div className='mb-12 text-center'>
|
||||||
<h2 className='mb-4 text-3xl font-bold tracking-tight sm:text-4xl'>
|
<h2 className='mb-4 text-3xl font-bold tracking-tight sm:text-4xl'>
|
||||||
{t('clients.loyalty.works.levels.title')}
|
{t('clients.loyalty.works.levels.title')}
|
||||||
</h2>
|
</h2>
|
||||||
<p className='mx-auto max-w-2xl text-gray-600'>
|
<p className='mx-auto max-w-2xl text-gray-600'>
|
||||||
{t('clients.loyalty.works.levels.description')}
|
{t('clients.loyalty.works.levels.description')}
|
||||||
</p>
|
</p>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div data-aos='flip-down' className='grid gap-8 md:grid-cols-3'>
|
||||||
|
<Card className='overflow-hidden border-t-4 border-t-gray-400 transition-all hover:shadow-lg'>
|
||||||
|
<CardContent className='p-6'>
|
||||||
|
<h3 className='mb-4 text-center text-2xl font-bold'>
|
||||||
|
{t('clients.loyalty.works.levels.card-1.title')}
|
||||||
|
</h3>
|
||||||
|
<div className='mb-6 text-center'>
|
||||||
|
<span className='text-4xl font-bold'>
|
||||||
|
{t('clients.loyalty.works.levels.card-1.percent')}
|
||||||
|
</span>
|
||||||
|
<p className='text-sm text-gray-600'>
|
||||||
|
{t('clients.loyalty.works.levels.card.mark')}
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
<ul className='space-y-2'>
|
||||||
|
<li className='flex items-center'>
|
||||||
|
<Check className='mr-2 h-5 w-5 text-green-500' />
|
||||||
|
<span>
|
||||||
|
{t('clients.loyalty.works.levels.card-1.bonus-1')}
|
||||||
|
</span>
|
||||||
|
</li>
|
||||||
|
<li className='flex items-center'>
|
||||||
|
<Check className='mr-2 h-5 w-5 text-green-500' />
|
||||||
|
<span>
|
||||||
|
{t('clients.loyalty.works.levels.card-1.bonus-2')}
|
||||||
|
</span>
|
||||||
|
</li>
|
||||||
|
<li className='flex items-center'>
|
||||||
|
<Check className='mr-2 h-5 w-5 text-green-500' />
|
||||||
|
<span>
|
||||||
|
{t('clients.loyalty.works.levels.card-1.bonus-3')}
|
||||||
|
</span>
|
||||||
|
</li>
|
||||||
|
</ul>
|
||||||
|
</CardContent>
|
||||||
|
</Card>
|
||||||
|
|
||||||
|
<Card className='overflow-hidden border-t-4 border-t-yellow-500 transition-all hover:shadow-lg'>
|
||||||
|
<CardContent className='p-6'>
|
||||||
|
<h3 className='mb-4 text-center text-2xl font-bold'>
|
||||||
|
{t('clients.loyalty.works.levels.card-2.title')}
|
||||||
|
</h3>
|
||||||
|
<div className='mb-6 text-center'>
|
||||||
|
<span className='text-4xl font-bold'>
|
||||||
|
{t('clients.loyalty.works.levels.card-2.percent')}
|
||||||
|
</span>
|
||||||
|
<p className='text-sm text-gray-600'>
|
||||||
|
{t('clients.loyalty.works.levels.card.mark')}
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
<ul className='space-y-2'>
|
||||||
|
<li className='flex items-center'>
|
||||||
|
<Check className='mr-2 h-5 w-5 text-green-500' />
|
||||||
|
<span>
|
||||||
|
{t('clients.loyalty.works.levels.card-2.bonus-1')}
|
||||||
|
</span>
|
||||||
|
</li>
|
||||||
|
<li className='flex items-center'>
|
||||||
|
<Check className='mr-2 h-5 w-5 text-green-500' />
|
||||||
|
<span>
|
||||||
|
{t('clients.loyalty.works.levels.card-2.bonus-2')}
|
||||||
|
</span>
|
||||||
|
</li>
|
||||||
|
<li className='flex items-center'>
|
||||||
|
<Check className='mr-2 h-5 w-5 text-green-500' />
|
||||||
|
<span>
|
||||||
|
{t('clients.loyalty.works.levels.card-2.bonus-3')}
|
||||||
|
</span>
|
||||||
|
</li>
|
||||||
|
<li className='flex items-center'>
|
||||||
|
<Check className='mr-2 h-5 w-5 text-green-500' />
|
||||||
|
<span>
|
||||||
|
{t('clients.loyalty.works.levels.card-2.bonus-4')}
|
||||||
|
</span>
|
||||||
|
</li>
|
||||||
|
</ul>
|
||||||
|
</CardContent>
|
||||||
|
</Card>
|
||||||
|
|
||||||
|
<Card className='overflow-hidden border-t-4 border-t-red-600 transition-all hover:shadow-lg'>
|
||||||
|
<CardContent className='p-6'>
|
||||||
|
<h3 className='mb-4 text-center text-2xl font-bold'>
|
||||||
|
{t('clients.loyalty.works.levels.card-3.title')}
|
||||||
|
</h3>
|
||||||
|
<div className='mb-6 text-center'>
|
||||||
|
<span className='text-4xl font-bold'>
|
||||||
|
{t('clients.loyalty.works.levels.card-3.percent')}
|
||||||
|
</span>
|
||||||
|
<p className='text-sm text-gray-600'>
|
||||||
|
{t('clients.loyalty.works.levels.card.mark')}
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
<ul className='space-y-2'>
|
||||||
|
<li className='flex items-center'>
|
||||||
|
<Check className='mr-2 h-5 w-5 text-green-500' />
|
||||||
|
<span>
|
||||||
|
{t('clients.loyalty.works.levels.card-3.bonus-1')}
|
||||||
|
</span>
|
||||||
|
</li>
|
||||||
|
<li className='flex items-center'>
|
||||||
|
<Check className='mr-2 h-5 w-5 text-green-500' />
|
||||||
|
<span>
|
||||||
|
{t('clients.loyalty.works.levels.card-3.bonus-2')}
|
||||||
|
</span>
|
||||||
|
</li>
|
||||||
|
<li className='flex items-center'>
|
||||||
|
<Check className='mr-2 h-5 w-5 text-green-500' />
|
||||||
|
<span>
|
||||||
|
{t('clients.loyalty.works.levels.card-3.bonus-3')}
|
||||||
|
</span>
|
||||||
|
</li>
|
||||||
|
<li className='flex items-center'>
|
||||||
|
<Check className='mr-2 h-5 w-5 text-green-500' />
|
||||||
|
<span>
|
||||||
|
{t('clients.loyalty.works.levels.card-3.bonus-4')}
|
||||||
|
</span>
|
||||||
|
</li>
|
||||||
|
<li className='flex items-center'>
|
||||||
|
<Check className='mr-2 h-5 w-5 text-green-500' />
|
||||||
|
<span>
|
||||||
|
{t('clients.loyalty.works.levels.card-3.bonus-5')}
|
||||||
|
</span>
|
||||||
|
</li>
|
||||||
|
</ul>
|
||||||
|
</CardContent>
|
||||||
|
</Card>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
</section>
|
||||||
<div className='grid gap-8 md:grid-cols-3'>
|
</Container>
|
||||||
<Card className='overflow-hidden border-t-4 border-t-gray-400 transition-all hover:shadow-lg'>
|
|
||||||
<CardContent className='p-6'>
|
|
||||||
<h3 className='mb-4 text-center text-2xl font-bold'>
|
|
||||||
{t('clients.loyalty.works.levels.card-1.title')}
|
|
||||||
</h3>
|
|
||||||
<div className='mb-6 text-center'>
|
|
||||||
<span className='text-4xl font-bold'>
|
|
||||||
{t('clients.loyalty.works.levels.card-1.percent')}
|
|
||||||
</span>
|
|
||||||
<p className='text-sm text-gray-600'>
|
|
||||||
{t('clients.loyalty.works.levels.card.mark')}
|
|
||||||
</p>
|
|
||||||
</div>
|
|
||||||
<ul className='space-y-2'>
|
|
||||||
<li className='flex items-center'>
|
|
||||||
<Check className='mr-2 h-5 w-5 text-green-500' />
|
|
||||||
<span>
|
|
||||||
{t('clients.loyalty.works.levels.card-1.bonus-1')}
|
|
||||||
</span>
|
|
||||||
</li>
|
|
||||||
<li className='flex items-center'>
|
|
||||||
<Check className='mr-2 h-5 w-5 text-green-500' />
|
|
||||||
<span>
|
|
||||||
{t('clients.loyalty.works.levels.card-1.bonus-2')}
|
|
||||||
</span>
|
|
||||||
</li>
|
|
||||||
<li className='flex items-center'>
|
|
||||||
<Check className='mr-2 h-5 w-5 text-green-500' />
|
|
||||||
<span>
|
|
||||||
{t('clients.loyalty.works.levels.card-1.bonus-3')}
|
|
||||||
</span>
|
|
||||||
</li>
|
|
||||||
</ul>
|
|
||||||
</CardContent>
|
|
||||||
</Card>
|
|
||||||
|
|
||||||
<Card className='overflow-hidden border-t-4 border-t-yellow-500 transition-all hover:shadow-lg'>
|
|
||||||
<CardContent className='p-6'>
|
|
||||||
<h3 className='mb-4 text-center text-2xl font-bold'>
|
|
||||||
{t('clients.loyalty.works.levels.card-2.title')}
|
|
||||||
</h3>
|
|
||||||
<div className='mb-6 text-center'>
|
|
||||||
<span className='text-4xl font-bold'>
|
|
||||||
{t('clients.loyalty.works.levels.card-2.percent')}
|
|
||||||
</span>
|
|
||||||
<p className='text-sm text-gray-600'>
|
|
||||||
{t('clients.loyalty.works.levels.card.mark')}
|
|
||||||
</p>
|
|
||||||
</div>
|
|
||||||
<ul className='space-y-2'>
|
|
||||||
<li className='flex items-center'>
|
|
||||||
<Check className='mr-2 h-5 w-5 text-green-500' />
|
|
||||||
<span>
|
|
||||||
{t('clients.loyalty.works.levels.card-1.bonus-1')}
|
|
||||||
</span>
|
|
||||||
</li>
|
|
||||||
<li className='flex items-center'>
|
|
||||||
<Check className='mr-2 h-5 w-5 text-green-500' />
|
|
||||||
<span>
|
|
||||||
{t('clients.loyalty.works.levels.card-2.bonus-2')}
|
|
||||||
</span>
|
|
||||||
</li>
|
|
||||||
<li className='flex items-center'>
|
|
||||||
<Check className='mr-2 h-5 w-5 text-green-500' />
|
|
||||||
<span>
|
|
||||||
{t('clients.loyalty.works.levels.card-3.bonus-3')}
|
|
||||||
</span>
|
|
||||||
</li>
|
|
||||||
<li className='flex items-center'>
|
|
||||||
<Check className='mr-2 h-5 w-5 text-green-500' />
|
|
||||||
<span>
|
|
||||||
{t('clients.loyalty.works.levels.card-4.bonus-4')}
|
|
||||||
</span>
|
|
||||||
</li>
|
|
||||||
</ul>
|
|
||||||
</CardContent>
|
|
||||||
</Card>
|
|
||||||
|
|
||||||
<Card className='overflow-hidden border-t-4 border-t-red-600 transition-all hover:shadow-lg'>
|
|
||||||
<CardContent className='p-6'>
|
|
||||||
<h3 className='mb-4 text-center text-2xl font-bold'>
|
|
||||||
{t('clients.loyalty.works.levels.card-3.title')}
|
|
||||||
</h3>
|
|
||||||
<div className='mb-6 text-center'>
|
|
||||||
<span className='text-4xl font-bold'>
|
|
||||||
{t('clients.loyalty.works.levels.card-3.percent')}
|
|
||||||
</span>
|
|
||||||
<p className='text-sm text-gray-600'>
|
|
||||||
{t('clients.loyalty.works.levels.card.mark')}
|
|
||||||
</p>
|
|
||||||
</div>
|
|
||||||
<ul className='space-y-2'>
|
|
||||||
<li className='flex items-center'>
|
|
||||||
<Check className='mr-2 h-5 w-5 text-green-500' />
|
|
||||||
<span>
|
|
||||||
{t('clients.loyalty.works.levels.card-3.bonus-1')}
|
|
||||||
</span>
|
|
||||||
</li>
|
|
||||||
<li className='flex items-center'>
|
|
||||||
<Check className='mr-2 h-5 w-5 text-green-500' />
|
|
||||||
<span>
|
|
||||||
{t('clients.loyalty.works.levels.card-3.bonus-2')}
|
|
||||||
</span>
|
|
||||||
</li>
|
|
||||||
<li className='flex items-center'>
|
|
||||||
<Check className='mr-2 h-5 w-5 text-green-500' />
|
|
||||||
<span>
|
|
||||||
{t('clients.loyalty.works.levels.card-3.bonus-3')}
|
|
||||||
</span>
|
|
||||||
</li>
|
|
||||||
<li className='flex items-center'>
|
|
||||||
<Check className='mr-2 h-5 w-5 text-green-500' />
|
|
||||||
<span>
|
|
||||||
{t('clients.loyalty.works.levels.card-3.bonus-4')}
|
|
||||||
</span>
|
|
||||||
</li>
|
|
||||||
<li className='flex items-center'>
|
|
||||||
<Check className='mr-2 h-5 w-5 text-green-500' />
|
|
||||||
<span>
|
|
||||||
{t('clients.loyalty.works.levels.card-3.bonus-5')}
|
|
||||||
</span>
|
|
||||||
</li>
|
|
||||||
</ul>
|
|
||||||
</CardContent>
|
|
||||||
</Card>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</section>
|
|
||||||
|
|
||||||
<CtaSection />
|
<CtaSection />
|
||||||
</main>
|
</main>
|
||||||
|
|||||||
@ -1,11 +1,15 @@
|
|||||||
'use client';
|
'use client';
|
||||||
|
|
||||||
|
import { deleteCookie, getCookie } from 'cookies-next';
|
||||||
import { Building2, Fuel, User } from 'lucide-react';
|
import { Building2, Fuel, User } from 'lucide-react';
|
||||||
import Link from 'next/link';
|
import Link from 'next/link';
|
||||||
|
import { useRouter, useSearchParams } from 'next/navigation';
|
||||||
|
import { Suspense } from 'react';
|
||||||
|
|
||||||
import { LoginForm } from '@/features/auth/login-form';
|
import { LoginForm } from '@/features/auth/login-form';
|
||||||
|
|
||||||
import { useTextController } from '@/shared/language/hooks/use-text-controller';
|
import { useTextController } from '@/shared/language/hooks/use-text-controller';
|
||||||
|
import { Button } from '@/shared/shadcn-ui/button';
|
||||||
import {
|
import {
|
||||||
Card,
|
Card,
|
||||||
CardContent,
|
CardContent,
|
||||||
@ -13,6 +17,7 @@ import {
|
|||||||
CardHeader,
|
CardHeader,
|
||||||
CardTitle,
|
CardTitle,
|
||||||
} from '@/shared/shadcn-ui/card';
|
} from '@/shared/shadcn-ui/card';
|
||||||
|
import Container from '@/shared/shadcn-ui/conteiner';
|
||||||
import {
|
import {
|
||||||
Tabs,
|
Tabs,
|
||||||
TabsContent,
|
TabsContent,
|
||||||
@ -37,67 +42,132 @@ const tabs = [
|
|||||||
},
|
},
|
||||||
];
|
];
|
||||||
|
|
||||||
|
function LoginPageTabs() {
|
||||||
|
const { t } = useTextController();
|
||||||
|
const router = useRouter();
|
||||||
|
const searchParams = useSearchParams();
|
||||||
|
|
||||||
|
const defaultTab = searchParams.get('tab') || 'bonus';
|
||||||
|
|
||||||
|
const handleTabChange = (tabType: string) => {
|
||||||
|
router.push(`?tab=${tabType}`, undefined);
|
||||||
|
};
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Tabs
|
||||||
|
defaultValue={defaultTab}
|
||||||
|
value={defaultTab}
|
||||||
|
onValueChange={handleTabChange}
|
||||||
|
className='w-full'
|
||||||
|
>
|
||||||
|
<TabsList className='mb-8 flex h-fit w-full flex-col sm:flex-row'>
|
||||||
|
{tabs.map((tab) => {
|
||||||
|
return (
|
||||||
|
<TabsTrigger
|
||||||
|
key={tab.label}
|
||||||
|
value={tab.type}
|
||||||
|
className='w-full text-base'
|
||||||
|
>
|
||||||
|
<tab.Icon className='mr-2 h-4 w-4' /> {t(tab.label)}
|
||||||
|
</TabsTrigger>
|
||||||
|
);
|
||||||
|
})}
|
||||||
|
</TabsList>
|
||||||
|
|
||||||
|
{tabs.map((tab) => {
|
||||||
|
const tabCookieName = `${tab.type}__token`;
|
||||||
|
|
||||||
|
const authenticationCookie = getCookie(tabCookieName);
|
||||||
|
|
||||||
|
if (authenticationCookie) {
|
||||||
|
return (
|
||||||
|
<TabsContent key={tab.label} value={tab.type}>
|
||||||
|
<Card>
|
||||||
|
<CardHeader>
|
||||||
|
<CardTitle>{t(tab.title)}</CardTitle>
|
||||||
|
</CardHeader>
|
||||||
|
<CardContent className='flex justify-center gap-2 space-y-4'>
|
||||||
|
<Link
|
||||||
|
href={
|
||||||
|
tab.type === 'bonus'
|
||||||
|
? '/customer-dashboard'
|
||||||
|
: '/corporate-dashboard'
|
||||||
|
}
|
||||||
|
>
|
||||||
|
<Button className='flex items-center'>Открыть</Button>
|
||||||
|
</Link>
|
||||||
|
<Button
|
||||||
|
variant='outline'
|
||||||
|
className='flex items-center gap-2'
|
||||||
|
onClick={() => {
|
||||||
|
deleteCookie(tabCookieName);
|
||||||
|
window.location.reload();
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
Выйти
|
||||||
|
</Button>
|
||||||
|
</CardContent>
|
||||||
|
</Card>
|
||||||
|
</TabsContent>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
return (
|
||||||
|
<TabsContent key={tab.label} value={tab.type}>
|
||||||
|
<Card>
|
||||||
|
<CardHeader>
|
||||||
|
<CardTitle>{t(tab.title)}</CardTitle>
|
||||||
|
<CardDescription>{t(tab.description)}</CardDescription>
|
||||||
|
</CardHeader>
|
||||||
|
<CardContent className='space-y-4'>
|
||||||
|
<LoginForm type={tab.type} />
|
||||||
|
</CardContent>
|
||||||
|
</Card>
|
||||||
|
</TabsContent>
|
||||||
|
);
|
||||||
|
})}
|
||||||
|
</Tabs>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
export default function LoginPage() {
|
export default function LoginPage() {
|
||||||
const { t } = useTextController();
|
const { t } = useTextController();
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className='flex min-h-screen flex-col items-center justify-center'>
|
<Container>
|
||||||
<main className='flex-1'>
|
<div className='flex min-h-screen flex-col items-center justify-center'>
|
||||||
<div className='container max-w-6xl py-16'>
|
<main className='flex-1'>
|
||||||
<div className='mb-12 flex flex-col items-center text-center'>
|
<div className='container max-w-6xl py-16'>
|
||||||
<div className='mb-4 inline-flex items-center justify-center rounded-full bg-red-100 p-2'>
|
<div className='mb-12 flex flex-col items-center text-center'>
|
||||||
<Fuel className='h-6 w-6 text-red-600' />
|
<div className='mb-4 inline-flex items-center justify-center rounded-full bg-red-100 p-2'>
|
||||||
|
<Fuel className='h-6 w-6 text-red-600' />
|
||||||
|
</div>
|
||||||
|
<h1 className='mb-4 text-3xl font-bold tracking-tight sm:text-4xl'>
|
||||||
|
{t('auth.title')}
|
||||||
|
</h1>
|
||||||
|
<p className='max-w-2xl text-gray-600'>{t('auth.description')}</p>
|
||||||
</div>
|
</div>
|
||||||
<h1 className='mb-4 text-3xl font-bold tracking-tight sm:text-4xl'>
|
|
||||||
{t('auth.title')}
|
|
||||||
</h1>
|
|
||||||
<p className='max-w-2xl text-gray-600'>{t('auth.description')}</p>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div className='mx-auto max-w-lg'>
|
<div data-aos='zoom-in' className='mx-auto max-w-lg'>
|
||||||
<Tabs defaultValue='bonus' className='w-full'>
|
<Suspense>
|
||||||
<TabsList className='mb-8 flex h-fit w-full flex-col sm:flex-row'>
|
<LoginPageTabs />
|
||||||
{tabs.map((tab) => {
|
</Suspense>
|
||||||
return (
|
|
||||||
<TabsTrigger
|
|
||||||
key={tab.label}
|
|
||||||
value={tab.type}
|
|
||||||
className='w-full text-base'
|
|
||||||
>
|
|
||||||
<tab.Icon className='mr-2 h-4 w-4' /> {t(tab.label)}
|
|
||||||
</TabsTrigger>
|
|
||||||
);
|
|
||||||
})}
|
|
||||||
</TabsList>
|
|
||||||
|
|
||||||
{tabs.map((tab) => {
|
<div className='mt-8 text-center text-sm text-gray-500'>
|
||||||
return (
|
<p>
|
||||||
<TabsContent key={tab.label} value={tab.type}>
|
{t('auth.loginIssues')}{' '}
|
||||||
<Card>
|
<Link
|
||||||
<CardHeader>
|
href='/contact'
|
||||||
<CardTitle>{t(tab.title)}</CardTitle>
|
className='text-red-600 hover:underline'
|
||||||
<CardDescription>{t(tab.description)}</CardDescription>
|
>
|
||||||
</CardHeader>
|
{t('auth.contactLink')}
|
||||||
<CardContent className='space-y-4'>
|
</Link>
|
||||||
<LoginForm type={tab.type} />
|
</p>
|
||||||
</CardContent>
|
</div>
|
||||||
</Card>
|
|
||||||
</TabsContent>
|
|
||||||
);
|
|
||||||
})}
|
|
||||||
</Tabs>
|
|
||||||
|
|
||||||
<div className='mt-8 text-center text-sm text-gray-500'>
|
|
||||||
<p>
|
|
||||||
{t('auth.loginIssues')}{' '}
|
|
||||||
<Link href='/contact' className='text-red-600 hover:underline'>
|
|
||||||
{t('auth.contactLink')}
|
|
||||||
</Link>
|
|
||||||
</p>
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</main>
|
||||||
</main>
|
</div>
|
||||||
</div>
|
</Container>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,4 +1,5 @@
|
|||||||
import { createApi, fetchBaseQuery } from '@reduxjs/toolkit/query/react';
|
import { createApi, fetchBaseQuery } from '@reduxjs/toolkit/query/react';
|
||||||
|
import { toast } from 'sonner';
|
||||||
|
|
||||||
const baseQuery = fetchBaseQuery({
|
const baseQuery = fetchBaseQuery({
|
||||||
baseUrl: process.env.NEXT_PUBLIC_API_URL,
|
baseUrl: process.env.NEXT_PUBLIC_API_URL,
|
||||||
@ -15,7 +16,25 @@ export const TAGS = {
|
|||||||
|
|
||||||
export const baseAPI = createApi({
|
export const baseAPI = createApi({
|
||||||
reducerPath: 'baseAPI',
|
reducerPath: 'baseAPI',
|
||||||
baseQuery,
|
baseQuery: async (args, api, extraOptions) => {
|
||||||
|
const result = await baseQuery(args, api, extraOptions);
|
||||||
|
|
||||||
|
if (result.error) {
|
||||||
|
switch (result.error.status) {
|
||||||
|
case 401:
|
||||||
|
toast.error('Login credentials error');
|
||||||
|
break;
|
||||||
|
|
||||||
|
case 500:
|
||||||
|
toast.error('Server error, please try later');
|
||||||
|
break;
|
||||||
|
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
},
|
||||||
tagTypes: Object.values(TAGS),
|
tagTypes: Object.values(TAGS),
|
||||||
endpoints: () => ({}),
|
endpoints: () => ({}),
|
||||||
});
|
});
|
||||||
|
|||||||
14
src/shared/api/taylor-api.ts
Normal file
14
src/shared/api/taylor-api.ts
Normal file
@ -0,0 +1,14 @@
|
|||||||
|
import { createApi, fetchBaseQuery } from '@reduxjs/toolkit/query';
|
||||||
|
|
||||||
|
const baseQuery = fetchBaseQuery({
|
||||||
|
baseUrl: process.env.TAYLOR_API_ENDPOINT,
|
||||||
|
headers: {
|
||||||
|
Authorization: process.env.TAYLOR_API_TOKEN || '',
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
export const taylorAPI = createApi({
|
||||||
|
reducerPath: 'taylorAPI',
|
||||||
|
baseQuery,
|
||||||
|
endpoints: () => ({}),
|
||||||
|
});
|
||||||
@ -5,7 +5,7 @@ import Image from 'next/image';
|
|||||||
import Link from 'next/link';
|
import Link from 'next/link';
|
||||||
import { useEffect, useState } from 'react';
|
import { useEffect, useState } from 'react';
|
||||||
|
|
||||||
import { Discounts } from '@/app/api-utlities/@types/main';
|
import { Discounts } from '@/app/api-utlities/@types';
|
||||||
|
|
||||||
import { useTextController } from '@/shared/language/hooks/use-text-controller';
|
import { useTextController } from '@/shared/language/hooks/use-text-controller';
|
||||||
import { Button } from '@/shared/shadcn-ui/button';
|
import { Button } from '@/shared/shadcn-ui/button';
|
||||||
|
|||||||
@ -1,12 +1,25 @@
|
|||||||
import { baseAPI } from '@/shared/api/base-api';
|
import { jsonToGraphQLQuery } from 'json-to-graphql-query';
|
||||||
|
|
||||||
|
import { presentTexts } from '@/app/api-utlities/presenters';
|
||||||
|
import { textsRequest } from '@/app/api-utlities/requests/common';
|
||||||
|
|
||||||
|
import { taylorAPI } from '@/shared/api/taylor-api';
|
||||||
import { TextItem } from '@/shared/types/text.types';
|
import { TextItem } from '@/shared/types/text.types';
|
||||||
|
|
||||||
export const textControlApi = baseAPI.injectEndpoints({
|
export const textControlApi = taylorAPI.injectEndpoints({
|
||||||
endpoints: (builder) => ({
|
endpoints: (builder) => ({
|
||||||
fetchText: builder.query<TextItem[], void>({
|
fetchText: builder.query<TextItem[], void>({
|
||||||
query: () => '/text',
|
query: () => ({
|
||||||
|
url: '',
|
||||||
|
method: 'POST',
|
||||||
|
body: {
|
||||||
|
query: jsonToGraphQLQuery({ query: textsRequest }),
|
||||||
|
},
|
||||||
|
}),
|
||||||
|
|
||||||
|
transformResponse: (response: any) => {
|
||||||
|
return presentTexts(response.data._kontentSajta);
|
||||||
|
},
|
||||||
}),
|
}),
|
||||||
}),
|
}),
|
||||||
});
|
});
|
||||||
|
|
||||||
export const { useFetchTextQuery } = textControlApi;
|
|
||||||
|
|||||||
10
src/shared/shadcn-ui/conteiner.tsx
Normal file
10
src/shared/shadcn-ui/conteiner.tsx
Normal file
@ -0,0 +1,10 @@
|
|||||||
|
"use client"
|
||||||
|
|
||||||
|
interface ContainerProps {
|
||||||
|
children: React.ReactNode
|
||||||
|
}
|
||||||
|
export default function Container({children}: ContainerProps) {
|
||||||
|
return (
|
||||||
|
<div className="container mx-auto px-2.5 py-1">{children}</div>
|
||||||
|
)
|
||||||
|
}
|
||||||
@ -4,13 +4,16 @@ import { createWrapper } from 'next-redux-wrapper';
|
|||||||
|
|
||||||
import { baseAPI } from '@/shared/api/base-api';
|
import { baseAPI } from '@/shared/api/base-api';
|
||||||
|
|
||||||
|
import { taylorAPI } from '../api/taylor-api';
|
||||||
import { rootReducer } from './root-reducer';
|
import { rootReducer } from './root-reducer';
|
||||||
|
|
||||||
export const makeStore = () =>
|
export const makeStore = () =>
|
||||||
configureStore({
|
configureStore({
|
||||||
reducer: rootReducer,
|
reducer: rootReducer,
|
||||||
middleware: (getDefaultMiddleware) =>
|
middleware: (getDefaultMiddleware) =>
|
||||||
getDefaultMiddleware().concat(baseAPI.middleware),
|
getDefaultMiddleware()
|
||||||
|
.concat(baseAPI.middleware)
|
||||||
|
.concat(taylorAPI.middleware),
|
||||||
devTools: process.env.NODE_ENV === 'development',
|
devTools: process.env.NODE_ENV === 'development',
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|||||||
@ -1,10 +1,10 @@
|
|||||||
import { combineReducers } from '@reduxjs/toolkit';
|
import { combineReducers } from '@reduxjs/toolkit';
|
||||||
|
|
||||||
import { loginAPI } from '@/entities/auth/api/login.api';
|
|
||||||
|
|
||||||
import { baseAPI } from '@/shared/api/base-api';
|
import { baseAPI } from '@/shared/api/base-api';
|
||||||
|
|
||||||
|
import { taylorAPI } from '../api/taylor-api';
|
||||||
|
|
||||||
export const rootReducer = combineReducers({
|
export const rootReducer = combineReducers({
|
||||||
[baseAPI.reducerPath]: baseAPI.reducer,
|
[baseAPI.reducerPath]: baseAPI.reducer,
|
||||||
[loginAPI.reducerPath]: loginAPI.reducer,
|
[taylorAPI.reducerPath]: taylorAPI.reducer,
|
||||||
});
|
});
|
||||||
|
|||||||
@ -3,64 +3,19 @@
|
|||||||
import { Calendar, ChevronDown, ChevronUp } from 'lucide-react';
|
import { Calendar, ChevronDown, ChevronUp } from 'lucide-react';
|
||||||
import { useState } from 'react';
|
import { useState } from 'react';
|
||||||
|
|
||||||
|
import { HistoryItems } from '@/app/api-utlities/@types';
|
||||||
|
|
||||||
import { useTextController } from '@/shared/language/hooks/use-text-controller';
|
import { useTextController } from '@/shared/language/hooks/use-text-controller';
|
||||||
import { Button } from '@/shared/shadcn-ui/button';
|
import { Button } from '@/shared/shadcn-ui/button';
|
||||||
import { Card, CardContent } from '@/shared/shadcn-ui/card';
|
import { Card, CardContent } from '@/shared/shadcn-ui/card';
|
||||||
|
|
||||||
const timelineEvents = [
|
export interface CompanyTimelineProps {
|
||||||
{
|
timeline: HistoryItems;
|
||||||
year: '2008',
|
}
|
||||||
title: 'Основание компании',
|
|
||||||
description:
|
|
||||||
'GasNetwork была основана с открытием первых трех заправочных станций в Душанбе. С самого начала компания поставила перед собой цель предоставлять качественное топливо и отличный сервис.',
|
|
||||||
},
|
|
||||||
{
|
|
||||||
year: '2010',
|
|
||||||
title: 'Расширение сети',
|
|
||||||
description:
|
|
||||||
'Открытие еще пяти заправочных станций в различных регионах Таджикистана. Начало формирования единого стандарта обслуживания на всех станциях сети.',
|
|
||||||
},
|
|
||||||
{
|
|
||||||
year: '2012',
|
|
||||||
title: 'Внедрение программы лояльности',
|
|
||||||
description:
|
|
||||||
'Запуск первой в Таджикистане программы лояльности для клиентов сети заправок. Введение карт постоянного клиента с накопительной системой бонусов.',
|
|
||||||
},
|
|
||||||
{
|
|
||||||
year: '2014',
|
|
||||||
title: 'Модернизация оборудования',
|
|
||||||
description:
|
|
||||||
'Масштабная программа по обновлению оборудования на всех заправочных станциях сети. Внедрение современных технологий для повышения качества обслуживания.',
|
|
||||||
},
|
|
||||||
{
|
|
||||||
year: '2016',
|
|
||||||
title: 'Открытие 15-й заправки',
|
|
||||||
description:
|
|
||||||
'Значительное расширение сети с открытием юбилейной 15-й заправочной станции. GasNetwork становится одной из крупнейших сетей заправок в Таджикистане.',
|
|
||||||
},
|
|
||||||
{
|
|
||||||
year: '2018',
|
|
||||||
title: 'Запуск мобильного приложения',
|
|
||||||
description:
|
|
||||||
'Разработка и запуск мобильного приложения для клиентов сети. Возможность отслеживать бонусы, находить ближайшие заправки и получать специальные предложения.',
|
|
||||||
},
|
|
||||||
{
|
|
||||||
year: '2020',
|
|
||||||
title: 'Создание благотворительного фонда',
|
|
||||||
description:
|
|
||||||
'Основание благотворительного фонда GasNetwork для поддержки социальных проектов в Таджикистане. Начало активной социальной деятельности компании.',
|
|
||||||
},
|
|
||||||
{
|
|
||||||
year: '2023',
|
|
||||||
title: 'Современное развитие',
|
|
||||||
description:
|
|
||||||
'Сегодня GasNetwork - это 25+ современных заправочных станций по всему Таджикистану, более 150 сотрудников и тысячи довольных клиентов ежедневно.',
|
|
||||||
},
|
|
||||||
];
|
|
||||||
|
|
||||||
export function CompanyTimeline() {
|
export function CompanyTimeline({ timeline }: CompanyTimelineProps) {
|
||||||
const [expanded, setExpanded] = useState(false);
|
const [expanded, setExpanded] = useState(false);
|
||||||
const displayEvents = expanded ? timelineEvents : timelineEvents.slice(0, 4);
|
const displayEvents = expanded ? timeline : timeline.slice(0, 4);
|
||||||
|
|
||||||
const { t } = useTextController();
|
const { t } = useTextController();
|
||||||
|
|
||||||
@ -80,7 +35,7 @@ export function CompanyTimeline() {
|
|||||||
<Calendar className='size-5 text-red-600' />
|
<Calendar className='size-5 text-red-600' />
|
||||||
</div>
|
</div>
|
||||||
<h3 className='text-xl font-bold'>{event.year}</h3>
|
<h3 className='text-xl font-bold'>{event.year}</h3>
|
||||||
<h4 className='mb-2 text-lg font-semibold'>{event.title}</h4>
|
<h4 className='mb-2 text-lg font-semibold'>{event.name}</h4>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div
|
<div
|
||||||
@ -108,7 +63,7 @@ export function CompanyTimeline() {
|
|||||||
))}
|
))}
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{timelineEvents.length > 4 && (
|
{timeline.length > 4 && (
|
||||||
<div className='mt-8 text-center'>
|
<div className='mt-8 text-center'>
|
||||||
<Button
|
<Button
|
||||||
variant='outline'
|
variant='outline'
|
||||||
|
|||||||
@ -4,6 +4,8 @@ import { ChevronLeft, ChevronRight, Maximize } from 'lucide-react';
|
|||||||
import Image from 'next/image';
|
import Image from 'next/image';
|
||||||
import { useState } from 'react';
|
import { useState } from 'react';
|
||||||
|
|
||||||
|
import { Stations } from '@/app/api-utlities/@types';
|
||||||
|
|
||||||
import { useTextController } from '@/shared/language/hooks/use-text-controller';
|
import { useTextController } from '@/shared/language/hooks/use-text-controller';
|
||||||
import { Button } from '@/shared/shadcn-ui/button';
|
import { Button } from '@/shared/shadcn-ui/button';
|
||||||
import {
|
import {
|
||||||
@ -15,55 +17,15 @@ import {
|
|||||||
DialogTrigger,
|
DialogTrigger,
|
||||||
} from '@/shared/shadcn-ui/dialog';
|
} from '@/shared/shadcn-ui/dialog';
|
||||||
|
|
||||||
interface Station {
|
export interface StationGalleryProps {
|
||||||
id: number;
|
stations: Stations;
|
||||||
name: string;
|
|
||||||
image: string;
|
|
||||||
location: string;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
const stations: Array<Station> = [
|
export function StationGallery({ stations }: StationGalleryProps) {
|
||||||
{
|
|
||||||
id: 1,
|
|
||||||
name: 'АЗС Душанбе-Центр',
|
|
||||||
image: '/placeholder.svg?height=400&width=600&text=АЗС+Душанбе-Центр',
|
|
||||||
location: 'ул. Рудаки 150, Душанбе',
|
|
||||||
},
|
|
||||||
{
|
|
||||||
id: 2,
|
|
||||||
name: 'АЗС Худжанд',
|
|
||||||
image: '/placeholder.svg?height=400&width=600&text=АЗС+Худжанд',
|
|
||||||
location: 'ул. Ленина 45, Худжанд',
|
|
||||||
},
|
|
||||||
{
|
|
||||||
id: 3,
|
|
||||||
name: 'АЗС Куляб',
|
|
||||||
image: '/placeholder.svg?height=400&width=600&text=АЗС+Куляб',
|
|
||||||
location: 'ул. Сомони 78, Куляб',
|
|
||||||
},
|
|
||||||
{
|
|
||||||
id: 4,
|
|
||||||
name: 'АЗС Бохтар',
|
|
||||||
image: '/placeholder.svg?height=400&width=600&text=АЗС+Бохтар',
|
|
||||||
location: 'ул. Айни 23, Бохтар',
|
|
||||||
},
|
|
||||||
{
|
|
||||||
id: 5,
|
|
||||||
name: 'АЗС Хорог',
|
|
||||||
image: '/placeholder.svg?height=400&width=600&text=АЗС+Хорог',
|
|
||||||
location: 'ул. Горная 12, Хорог',
|
|
||||||
},
|
|
||||||
{
|
|
||||||
id: 6,
|
|
||||||
name: 'АЗС Истаравшан',
|
|
||||||
image: '/placeholder.svg?height=400&width=600&text=АЗС+Истаравшан',
|
|
||||||
location: 'ул. Исмоили Сомони 34, Истаравшан',
|
|
||||||
},
|
|
||||||
];
|
|
||||||
|
|
||||||
export function StationGallery() {
|
|
||||||
const [currentImage, setCurrentImage] = useState(0);
|
const [currentImage, setCurrentImage] = useState(0);
|
||||||
const [selectedStation, setSelectedStation] = useState<Station | null>(null);
|
const [selectedStation, setSelectedStation] = useState<Stations[0] | null>(
|
||||||
|
null,
|
||||||
|
);
|
||||||
|
|
||||||
const nextImage = () => {
|
const nextImage = () => {
|
||||||
setCurrentImage((prev) => (prev === stations.length - 1 ? 0 : prev + 1));
|
setCurrentImage((prev) => (prev === stations.length - 1 ? 0 : prev + 1));
|
||||||
@ -76,7 +38,7 @@ export function StationGallery() {
|
|||||||
const { t } = useTextController();
|
const { t } = useTextController();
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className='space-y-8'>
|
<div className='space-y-8 px-2'>
|
||||||
<div className='relative h-[400px] overflow-hidden rounded-xl shadow-xl md:h-[500px]'>
|
<div className='relative h-[400px] overflow-hidden rounded-xl shadow-xl md:h-[500px]'>
|
||||||
<Image
|
<Image
|
||||||
src={stations[currentImage].image || '/placeholder.svg'}
|
src={stations[currentImage].image || '/placeholder.svg'}
|
||||||
@ -86,7 +48,7 @@ export function StationGallery() {
|
|||||||
/>
|
/>
|
||||||
<div className='absolute inset-0 flex flex-col justify-end bg-gradient-to-t from-black/70 to-transparent p-6 text-white'>
|
<div className='absolute inset-0 flex flex-col justify-end bg-gradient-to-t from-black/70 to-transparent p-6 text-white'>
|
||||||
<h3 className='text-2xl font-bold'>{stations[currentImage].name}</h3>
|
<h3 className='text-2xl font-bold'>{stations[currentImage].name}</h3>
|
||||||
<p className='text-white/80'>{stations[currentImage].location}</p>
|
<p className='text-white/80'>{stations[currentImage].address}</p>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<Button
|
<Button
|
||||||
@ -125,7 +87,7 @@ export function StationGallery() {
|
|||||||
<DialogHeader>
|
<DialogHeader>
|
||||||
<DialogTitle>{stations[currentImage].name}</DialogTitle>
|
<DialogTitle>{stations[currentImage].name}</DialogTitle>
|
||||||
<DialogDescription>
|
<DialogDescription>
|
||||||
{stations[currentImage].location}
|
{stations[currentImage].address}
|
||||||
</DialogDescription>
|
</DialogDescription>
|
||||||
</DialogHeader>
|
</DialogHeader>
|
||||||
<div className='relative h-[60vh] w-full'>
|
<div className='relative h-[60vh] w-full'>
|
||||||
|
|||||||
@ -4,6 +4,7 @@ import { Percent } from 'lucide-react';
|
|||||||
import Image from 'next/image';
|
import Image from 'next/image';
|
||||||
|
|
||||||
import { useTextController } from '@/shared/language/hooks/use-text-controller';
|
import { useTextController } from '@/shared/language/hooks/use-text-controller';
|
||||||
|
import Container from '@/shared/shadcn-ui/conteiner';
|
||||||
|
|
||||||
interface Benefit {
|
interface Benefit {
|
||||||
title: string;
|
title: string;
|
||||||
@ -33,46 +34,48 @@ export const BenefitsSection = () => {
|
|||||||
const { t } = useTextController();
|
const { t } = useTextController();
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<section className='bg-gray-50 py-16'>
|
<Container>
|
||||||
<div className='container mx-auto'>
|
<section className='bg-gray-50 py-16'>
|
||||||
<div className='grid items-center gap-12 md:grid-cols-2'>
|
<div className='container mx-auto'>
|
||||||
<div className='order-2 md:order-1'>
|
<div className='grid items-center gap-12 md:grid-cols-2'>
|
||||||
<div className='mb-4 inline-flex items-center justify-center rounded-full bg-red-100 p-2'>
|
<div data-aos='fade-right' data-aos-duration='4000' className='order-2 md:order-1'>
|
||||||
<Percent className='h-6 w-6 text-red-600' />
|
<div className='mb-4 inline-flex items-center justify-center rounded-full bg-red-100 p-2'>
|
||||||
</div>
|
<Percent className='h-6 w-6 text-red-600' />
|
||||||
<h2 className='mb-6 text-3xl font-bold tracking-tight sm:text-4xl'>
|
</div>
|
||||||
{t('clients.benefits.title')}
|
<h2 className='mb-6 text-3xl font-bold tracking-tight sm:text-4xl'>
|
||||||
</h2>
|
{t('clients.benefits.title')}
|
||||||
<p className='mb-6 text-gray-600'>
|
</h2>
|
||||||
{t('clients.benefits.subtitle')}
|
<p className='mb-6 text-gray-600'>
|
||||||
</p>
|
{t('clients.benefits.subtitle')}
|
||||||
|
</p>
|
||||||
|
|
||||||
<div className='space-y-4'>
|
<div className='space-y-4'>
|
||||||
{benefits.map(({ title, description }) => {
|
{benefits.map(({ title, description }) => {
|
||||||
return (
|
return (
|
||||||
<div key={title} className='flex items-start'>
|
<div key={title} className='flex items-start'>
|
||||||
<div className='mt-1 flex h-6 w-6 flex-shrink-0 items-center justify-center rounded-full bg-red-600'>
|
<div className='mt-1 flex h-6 w-6 flex-shrink-0 items-center justify-center rounded-full bg-red-600'>
|
||||||
<span className='text-xs text-white'>✓</span>
|
<span className='text-xs text-white'>✓</span>
|
||||||
|
</div>
|
||||||
|
<div className='ml-3'>
|
||||||
|
<h3 className='text-lg font-medium'>{title}</h3>
|
||||||
|
<p className='text-gray-600'>{description}</p>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div className='ml-3'>
|
);
|
||||||
<h3 className='text-lg font-medium'>{title}</h3>
|
})}
|
||||||
<p className='text-gray-600'>{description}</p>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
<div data-aos='fade-up' className='relative order-1 h-[400px] overflow-hidden rounded-xl shadow-xl md:order-2'>
|
||||||
);
|
<Image
|
||||||
})}
|
src='/placeholder.svg?height=400&width=600&text=Преимущества+для+клиентов'
|
||||||
|
alt='Преимущества для клиентов'
|
||||||
|
fill
|
||||||
|
className='object-cover'
|
||||||
|
/>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
|
||||||
<div className='relative order-1 h-[400px] overflow-hidden rounded-xl shadow-xl md:order-2'>
|
|
||||||
<Image
|
|
||||||
src='/placeholder.svg?height=400&width=600&text=Преимущества+для+клиентов'
|
|
||||||
alt='Преимущества для клиентов'
|
|
||||||
fill
|
|
||||||
className='object-cover'
|
|
||||||
/>
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</section>
|
||||||
</section>
|
</Container>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|||||||
@ -11,6 +11,7 @@ import {
|
|||||||
CardHeader,
|
CardHeader,
|
||||||
CardTitle,
|
CardTitle,
|
||||||
} from '@/shared/shadcn-ui/card';
|
} from '@/shared/shadcn-ui/card';
|
||||||
|
import Container from '@/shared/shadcn-ui/conteiner';
|
||||||
|
|
||||||
interface ServiceOverview {
|
interface ServiceOverview {
|
||||||
title: string;
|
title: string;
|
||||||
@ -49,39 +50,41 @@ export const ServicesOverviewSection = () => {
|
|||||||
const { t } = useTextController();
|
const { t } = useTextController();
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<section className='py-16'>
|
<Container>
|
||||||
<div className='container mx-auto'>
|
<section className='py-16'>
|
||||||
<div className='mb-12 text-center'>
|
<div className='container mx-auto'>
|
||||||
<h2 className='mb-4 text-3xl font-bold tracking-tight sm:text-4xl'>
|
<div className='mb-12 text-center'>
|
||||||
{t('clients.services.title')}
|
<h2 className='mb-4 text-3xl font-bold tracking-tight sm:text-4xl'>
|
||||||
</h2>
|
{t('clients.services.title')}
|
||||||
<p className='mx-auto max-w-2xl text-gray-600'>
|
</h2>
|
||||||
{t('clients.services.subtitle')}
|
<p className='mx-auto max-w-2xl text-gray-600'>
|
||||||
</p>
|
{t('clients.services.subtitle')}
|
||||||
</div>
|
</p>
|
||||||
|
</div>
|
||||||
|
|
||||||
<div className='grid gap-3 md:grid-cols-2 md:gap-6 lg:grid-cols-3'>
|
<div data-aos='flip-up' data-aos-duration='600' className='grid gap-3 md:grid-cols-2 md:gap-6 lg:grid-cols-3'>
|
||||||
{servicesOverview.map(({ description, Icon, contentText, title }) => {
|
{servicesOverview.map(({ description, Icon, contentText, title }) => {
|
||||||
return (
|
return (
|
||||||
<Card
|
<Card
|
||||||
key={title}
|
key={title}
|
||||||
className='overflow-hidden transition-all hover:shadow-lg'
|
className='overflow-hidden transition-all hover:shadow-lg'
|
||||||
>
|
>
|
||||||
<CardHeader className='pb-3'>
|
<CardHeader className='pb-3'>
|
||||||
<div className='mb-4 flex h-12 w-12 items-center justify-center rounded-full bg-red-100'>
|
<div className='mb-4 flex h-12 w-12 items-center justify-center rounded-full bg-red-100'>
|
||||||
<Icon className='h-6 w-6 text-red-600' />
|
<Icon className='h-6 w-6 text-red-600' />
|
||||||
</div>
|
</div>
|
||||||
<CardTitle>{title}</CardTitle>
|
<CardTitle>{title}</CardTitle>
|
||||||
<CardDescription>{description}</CardDescription>
|
<CardDescription>{description}</CardDescription>
|
||||||
</CardHeader>
|
</CardHeader>
|
||||||
<CardContent className='text-sm text-gray-600'>
|
<CardContent className='text-sm text-gray-600'>
|
||||||
<p>{contentText}</p>
|
<p>{contentText}</p>
|
||||||
</CardContent>
|
</CardContent>
|
||||||
</Card>
|
</Card>
|
||||||
);
|
);
|
||||||
})}
|
})}
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</section>
|
||||||
</section>
|
</Container>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|||||||
@ -11,113 +11,119 @@ export const Footer = () => {
|
|||||||
return (
|
return (
|
||||||
<footer className='bg-gray-900 px-4 py-12 text-white'>
|
<footer className='bg-gray-900 px-4 py-12 text-white'>
|
||||||
<div className='containe mx-autor'>
|
<div className='containe mx-autor'>
|
||||||
<div className='grid grid-cols-1 gap-8 md:grid-cols-3'>
|
<div className='grid grid-cols-1 gap-8 md:gap-4 md:grid-cols-3'>
|
||||||
<div>
|
<div className='flex md:justify-center md:items-center'>
|
||||||
<div className='mb-4 flex items-center gap-2'>
|
<div>
|
||||||
<Fuel className='h-6 w-6 text-red-500' />
|
<div className='mb-4 flex items-center gap-2'>
|
||||||
<span className='text-xl font-bold'>{t('common.name')}</span>
|
<Fuel className='h-6 w-6 text-red-500' />
|
||||||
</div>
|
<span className='text-xl font-bold'>{t('common.name')}</span>
|
||||||
<p className='mb-4 text-gray-400'>{t('home.hero.description')}</p>
|
|
||||||
<div className='flex space-x-4'>
|
|
||||||
<a href='#' className='text-gray-400 hover:text-white'>
|
|
||||||
<svg
|
|
||||||
className='h-6 w-6'
|
|
||||||
fill='currentColor'
|
|
||||||
viewBox='0 0 24 24'
|
|
||||||
aria-hidden='true'
|
|
||||||
>
|
|
||||||
<path
|
|
||||||
fillRule='evenodd'
|
|
||||||
d='M22 12c0-5.523-4.477-10-10-10S2 6.477 2 12c0 4.991 3.657 9.128 8.438 9.878v-6.987h-2.54V12h2.54V9.797c0-2.506 1.492-3.89 3.777-3.89 1.094 0 2.238.195 2.238.195v2.46h-1.26c-1.243 0-1.63.771-1.63 1.562V12h2.773l-.443 2.89h-2.33v6.988C18.343 21.128 22 16.991 22 12z'
|
|
||||||
clipRule='evenodd'
|
|
||||||
/>
|
|
||||||
</svg>
|
|
||||||
</a>
|
|
||||||
<a href='#' className='text-gray-400 hover:text-white'>
|
|
||||||
<svg
|
|
||||||
className='h-6 w-6'
|
|
||||||
fill='currentColor'
|
|
||||||
viewBox='0 0 24 24'
|
|
||||||
aria-hidden='true'
|
|
||||||
>
|
|
||||||
<path
|
|
||||||
fillRule='evenodd'
|
|
||||||
d='M12.315 2c2.43 0 2.784.013 3.808.06 1.064.049 1.791.218 2.427.465a4.902 4.902 0 011.772 1.153 4.902 4.902 0 011.153 1.772c.247.636.416 1.363.465 2.427.048 1.067.06 1.407.06 4.123v.08c0 2.643-.012 2.987-.06 4.043-.049 1.064-.218 1.791-.465 2.427a4.902 4.902 0 01-1.153 1.772 4.902 4.902 0 01-1.772 1.153c-.636.247-1.363.416-2.427.465-1.067.048-1.407.06-4.123.06h-.08c-2.643 0-2.987-.012-4.043-.06-1.064-.049-1.791-.218-2.427-.465a4.902 4.902 0 01-1.772-1.153 4.902 4.902 0 01-1.153-1.772c-.247-.636-.416-1.363-.465-2.427-.047-1.024-.06-1.379-.06-3.808v-.63c0-2.43.013-2.784.06-3.808.049-1.064.218-1.791.465-2.427a4.902 4.902 0 011.153-1.772A4.902 4.902 0 015.45 2.525c.636-.247 1.363-.416 2.427-.465C8.901 2.013 9.256 2 11.685 2h.63zm-.081 1.802h-.468c-2.456 0-2.784.011-3.807.058-.975.045-1.504.207-1.857.344-.467.182-.8.398-1.15.748-.35.35-.566.683-.748 1.15-.137.353-.3.882-.344 1.857-.047 1.023-.058 1.351-.058 3.807v.468c0 2.456.011 2.784.058 3.807.045.975.207 1.504.344 1.857.182.466.399.8.748 1.15.35.35.683.566 1.15.748.353.137.882.3 1.857.344 1.054.048 1.37.058 4.041.058h.08c2.597 0 2.917-.01 3.96-.058.976-.045 1.505-.207 1.858-.344.466-.182.8-.398 1.15-.748.35-.35.566-.683.748-1.15.137-.353.3-.882.344-1.857.048-1.055.058-1.37.058-4.041v-.08c0-2.597-.01-2.917-.058-3.96-.045-.976-.207-1.505-.344-1.858a3.097 3.097 0 00-.748-1.15 3.098 3.098 0 00-1.15-.748c-.353-.137-.882-.3-1.857-.344-1.023-.047-1.351-.058-3.807-.058zM12 6.865a5.135 5.135 0 110 10.27 5.135 5.135 0 010-10.27zm0 1.802a3.333 3.333 0 100 6.666 3.333 3.333 0 000-6.666zm5.338-3.205a1.2 1.2 0 110 2.4 1.2 1.2 0 010-2.4z'
|
|
||||||
clipRule='evenodd'
|
|
||||||
/>
|
|
||||||
</svg>
|
|
||||||
</a>
|
|
||||||
<a href='#' className='text-gray-400 hover:text-white'>
|
|
||||||
<svg
|
|
||||||
className='h-6 w-6'
|
|
||||||
fill='currentColor'
|
|
||||||
viewBox='0 0 24 24'
|
|
||||||
aria-hidden='true'
|
|
||||||
>
|
|
||||||
<path d='M8.29 20.251c7.547 0 11.675-6.253 11.675-11.675 0-.178 0-.355-.012-.53A8.348 8.348 0 0022 5.92a8.19 8.19 0 01-2.357.646 4.118 4.118 0 001.804-2.27 8.224 8.224 0 01-2.605.996 4.107 4.107 0 00-6.993 3.743 11.65 11.65 0 01-8.457-4.287 4.106 4.106 0 001.27 5.477A4.072 4.072 0 012.8 9.713v.052a4.105 4.105 0 003.292 4.022 4.095 4.095 0 01-1.853.07 4.108 4.108 0 003.834 2.85A8.233 8.233 0 012 18.407a11.616 11.616 0 006.29 1.84' />
|
|
||||||
</svg>
|
|
||||||
</a>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div>
|
|
||||||
<h3 className='mb-4 text-lg font-semibold'>
|
|
||||||
{t('common.footer.contacts')}
|
|
||||||
</h3>
|
|
||||||
<div className='space-y-3'>
|
|
||||||
<div className='flex items-start'>
|
|
||||||
<MapPin className='mt-0.5 mr-3 h-5 w-5 text-red-500' />
|
|
||||||
<p>{t('common.contacts.address')}</p>
|
|
||||||
</div>
|
</div>
|
||||||
<div className='flex items-start'>
|
<p className='mb-4 text-gray-400'>{t('home.hero.description')}</p>
|
||||||
<Phone className='mt-0.5 mr-3 h-5 w-5 text-red-500' />
|
<div className='flex space-x-4'>
|
||||||
<p>{t('common.contacts.tel')}</p>
|
<a href='#' className='text-gray-400 hover:text-white'>
|
||||||
</div>
|
<svg
|
||||||
<div className='flex items-start'>
|
className='h-6 w-6'
|
||||||
<Mail className='mt-0.5 mr-3 h-5 w-5 text-red-500' />
|
fill='currentColor'
|
||||||
<p>{t('common.contacts.email')}</p>
|
viewBox='0 0 24 24'
|
||||||
|
aria-hidden='true'
|
||||||
|
>
|
||||||
|
<path
|
||||||
|
fillRule='evenodd'
|
||||||
|
d='M22 12c0-5.523-4.477-10-10-10S2 6.477 2 12c0 4.991 3.657 9.128 8.438 9.878v-6.987h-2.54V12h2.54V9.797c0-2.506 1.492-3.89 3.777-3.89 1.094 0 2.238.195 2.238.195v2.46h-1.26c-1.243 0-1.63.771-1.63 1.562V12h2.773l-.443 2.89h-2.33v6.988C18.343 21.128 22 16.991 22 12z'
|
||||||
|
clipRule='evenodd'
|
||||||
|
/>
|
||||||
|
</svg>
|
||||||
|
</a>
|
||||||
|
<a href='#' className='text-gray-400 hover:text-white'>
|
||||||
|
<svg
|
||||||
|
className='h-6 w-6'
|
||||||
|
fill='currentColor'
|
||||||
|
viewBox='0 0 24 24'
|
||||||
|
aria-hidden='true'
|
||||||
|
>
|
||||||
|
<path
|
||||||
|
fillRule='evenodd'
|
||||||
|
d='M12.315 2c2.43 0 2.784.013 3.808.06 1.064.049 1.791.218 2.427.465a4.902 4.902 0 011.772 1.153 4.902 4.902 0 011.153 1.772c.247.636.416 1.363.465 2.427.048 1.067.06 1.407.06 4.123v.08c0 2.643-.012 2.987-.06 4.043-.049 1.064-.218 1.791-.465 2.427a4.902 4.902 0 01-1.153 1.772 4.902 4.902 0 01-1.772 1.153c-.636.247-1.363.416-2.427.465-1.067.048-1.407.06-4.123.06h-.08c-2.643 0-2.987-.012-4.043-.06-1.064-.049-1.791-.218-2.427-.465a4.902 4.902 0 01-1.772-1.153 4.902 4.902 0 01-1.153-1.772c-.247-.636-.416-1.363-.465-2.427-.047-1.024-.06-1.379-.06-3.808v-.63c0-2.43.013-2.784.06-3.808.049-1.064.218-1.791.465-2.427a4.902 4.902 0 011.153-1.772A4.902 4.902 0 015.45 2.525c.636-.247 1.363-.416 2.427-.465C8.901 2.013 9.256 2 11.685 2h.63zm-.081 1.802h-.468c-2.456 0-2.784.011-3.807.058-.975.045-1.504.207-1.857.344-.467.182-.8.398-1.15.748-.35.35-.566.683-.748 1.15-.137.353-.3.882-.344 1.857-.047 1.023-.058 1.351-.058 3.807v.468c0 2.456.011 2.784.058 3.807.045.975.207 1.504.344 1.857.182.466.399.8.748 1.15.35.35.683.566 1.15.748.353.137.882.3 1.857.344 1.054.048 1.37.058 4.041.058h.08c2.597 0 2.917-.01 3.96-.058.976-.045 1.505-.207 1.858-.344.466-.182.8-.398 1.15-.748.35-.35.566-.683.748-1.15.137-.353.3-.882.344-1.857.048-1.055.058-1.37.058-4.041v-.08c0-2.597-.01-2.917-.058-3.96-.045-.976-.207-1.505-.344-1.858a3.097 3.097 0 00-.748-1.15 3.098 3.098 0 00-1.15-.748c-.353-.137-.882-.3-1.857-.344-1.023-.047-1.351-.058-3.807-.058zM12 6.865a5.135 5.135 0 110 10.27 5.135 5.135 0 010-10.27zm0 1.802a3.333 3.333 0 100 6.666 3.333 3.333 0 000-6.666zm5.338-3.205a1.2 1.2 0 110 2.4 1.2 1.2 0 010-2.4z'
|
||||||
|
clipRule='evenodd'
|
||||||
|
/>
|
||||||
|
</svg>
|
||||||
|
</a>
|
||||||
|
<a href='#' className='text-gray-400 hover:text-white'>
|
||||||
|
<svg
|
||||||
|
className='h-6 w-6'
|
||||||
|
fill='currentColor'
|
||||||
|
viewBox='0 0 24 24'
|
||||||
|
aria-hidden='true'
|
||||||
|
>
|
||||||
|
<path d='M8.29 20.251c7.547 0 11.675-6.253 11.675-11.675 0-.178 0-.355-.012-.53A8.348 8.348 0 0022 5.92a8.19 8.19 0 01-2.357.646 4.118 4.118 0 001.804-2.27 8.224 8.224 0 01-2.605.996 4.107 4.107 0 00-6.993 3.743 11.65 11.65 0 01-8.457-4.287 4.106 4.106 0 001.27 5.477A4.072 4.072 0 012.8 9.713v.052a4.105 4.105 0 003.292 4.022 4.095 4.095 0 01-1.853.07 4.108 4.108 0 003.834 2.85A8.233 8.233 0 012 18.407a11.616 11.616 0 006.29 1.84' />
|
||||||
|
</svg>
|
||||||
|
</a>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div>
|
<div className='flex md:justify-center md:items-center'>
|
||||||
<h3 className='mb-4 text-lg font-semibold'>
|
<div>
|
||||||
{t('common.footer.navigation')}
|
<h3 className='mb-4 text-lg font-semibold'>
|
||||||
</h3>
|
{t('common.footer.contacts')}
|
||||||
<ul className='space-y-2'>
|
</h3>
|
||||||
<li>
|
<div className='space-y-3'>
|
||||||
<Link href='/' className='text-gray-400 hover:text-white'>
|
<div className='flex items-start'>
|
||||||
{t('common.navigation.home')}
|
<MapPin className='mt-0.5 mr-3 h-5 w-5 text-red-500' />
|
||||||
</Link>
|
<p>{t('common.contacts.address')}</p>
|
||||||
</li>
|
</div>
|
||||||
<li>
|
<div className='flex items-start'>
|
||||||
<Link href='/about' className='text-gray-400 hover:text-white'>
|
<Phone className='mt-0.5 mr-3 h-5 w-5 text-red-500' />
|
||||||
{t('common.navigation.about')}
|
<p>{t('common.contacts.tel')}</p>
|
||||||
</Link>
|
</div>
|
||||||
</li>
|
<div className='flex items-start'>
|
||||||
<li>
|
<Mail className='mt-0.5 mr-3 h-5 w-5 text-red-500' />
|
||||||
<Link
|
<p>{t('common.contacts.email')}</p>
|
||||||
href='/clients/loyalty'
|
</div>
|
||||||
className='text-gray-400 hover:text-white'
|
</div>
|
||||||
>
|
</div>
|
||||||
{t('common.navigation.clients')}
|
</div>
|
||||||
</Link>
|
<div className='flex md:justify-center md:items-center'>
|
||||||
</li>
|
<div>
|
||||||
<li>
|
<h3 className='mb-4 text-lg font-semibold'>
|
||||||
<Link
|
{t('common.footer.navigation')}
|
||||||
href='/#stations'
|
</h3>
|
||||||
className='text-gray-400 hover:text-white'
|
<ul className='space-y-2'>
|
||||||
>
|
<li>
|
||||||
{t('common.navigation.stations')}
|
<Link href='/' className='text-gray-400 hover:text-white'>
|
||||||
</Link>
|
{t('common.navigation.home')}
|
||||||
</li>
|
</Link>
|
||||||
<li>
|
</li>
|
||||||
<Link
|
<li>
|
||||||
href='/#vacancies'
|
<Link href='/about' className='text-gray-400 hover:text-white'>
|
||||||
className='text-gray-400 hover:text-white'
|
{t('common.navigation.about')}
|
||||||
>
|
</Link>
|
||||||
{t('common.navigation.vacancies')}
|
</li>
|
||||||
</Link>
|
<li>
|
||||||
</li>
|
<Link
|
||||||
</ul>
|
href='/clients/loyalty'
|
||||||
|
className='text-gray-400 hover:text-white'
|
||||||
|
>
|
||||||
|
{t('common.navigation.clients')}
|
||||||
|
</Link>
|
||||||
|
</li>
|
||||||
|
<li>
|
||||||
|
<Link
|
||||||
|
href='/#stations'
|
||||||
|
className='text-gray-400 hover:text-white'
|
||||||
|
>
|
||||||
|
{t('common.navigation.stations')}
|
||||||
|
</Link>
|
||||||
|
</li>
|
||||||
|
<li>
|
||||||
|
<Link
|
||||||
|
href='/#vacancies'
|
||||||
|
className='text-gray-400 hover:text-white'
|
||||||
|
>
|
||||||
|
{t('common.navigation.vacancies')}
|
||||||
|
</Link>
|
||||||
|
</li>
|
||||||
|
</ul>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div className='mt-8 border-t border-gray-800 pt-8 text-center text-gray-400'>
|
<div className='mt-8 border-t border-gray-800 pt-8 text-center text-gray-400'>
|
||||||
|
|||||||
@ -2,7 +2,7 @@
|
|||||||
|
|
||||||
import { MapPin } from 'lucide-react';
|
import { MapPin } from 'lucide-react';
|
||||||
|
|
||||||
import { Stations } from '@/app/api-utlities/@types/main';
|
import { Stations } from '@/app/api-utlities/@types';
|
||||||
|
|
||||||
import { GasStationMap } from '@/features/map';
|
import { GasStationMap } from '@/features/map';
|
||||||
|
|
||||||
|
|||||||
@ -4,7 +4,7 @@ import { Handshake } from 'lucide-react';
|
|||||||
import Image from 'next/image';
|
import Image from 'next/image';
|
||||||
import Link from 'next/link';
|
import Link from 'next/link';
|
||||||
|
|
||||||
import { Partners } from '@/app/api-utlities/@types/main';
|
import { Partners } from '@/app/api-utlities/@types';
|
||||||
|
|
||||||
import { useTextController } from '@/shared/language/hooks/use-text-controller';
|
import { useTextController } from '@/shared/language/hooks/use-text-controller';
|
||||||
import { Button } from '@/shared/shadcn-ui/button';
|
import { Button } from '@/shared/shadcn-ui/button';
|
||||||
|
|||||||
@ -2,7 +2,7 @@
|
|||||||
|
|
||||||
import { Gift } from 'lucide-react';
|
import { Gift } from 'lucide-react';
|
||||||
|
|
||||||
import { Discounts } from '@/app/api-utlities/@types/main';
|
import { Discounts } from '@/app/api-utlities/@types';
|
||||||
|
|
||||||
import PromotionSlider from '@/shared/components/promotion-slider';
|
import PromotionSlider from '@/shared/components/promotion-slider';
|
||||||
import { useTextController } from '@/shared/language/hooks/use-text-controller';
|
import { useTextController } from '@/shared/language/hooks/use-text-controller';
|
||||||
|
|||||||
@ -3,7 +3,9 @@
|
|||||||
import { format, subMonths } from 'date-fns';
|
import { format, subMonths } from 'date-fns';
|
||||||
import { ru } from 'date-fns/locale';
|
import { ru } from 'date-fns/locale';
|
||||||
import { CalendarIcon } from 'lucide-react';
|
import { CalendarIcon } from 'lucide-react';
|
||||||
import { useState } from 'react';
|
import { useEffect, useState } from 'react';
|
||||||
|
|
||||||
|
import { useFetchBonusTransactionsQuery } from '@/entities/bonus/api/bonus.api';
|
||||||
|
|
||||||
import { useTextController } from '@/shared/language/hooks/use-text-controller';
|
import { useTextController } from '@/shared/language/hooks/use-text-controller';
|
||||||
import { Button } from '@/shared/shadcn-ui/button';
|
import { Button } from '@/shared/shadcn-ui/button';
|
||||||
@ -23,85 +25,30 @@ import {
|
|||||||
TableRow,
|
TableRow,
|
||||||
} from '@/shared/shadcn-ui/table';
|
} from '@/shared/shadcn-ui/table';
|
||||||
|
|
||||||
// Sample customer data
|
|
||||||
const customerData = {
|
|
||||||
firstName: 'Алишер',
|
|
||||||
lastName: 'Рахмонов',
|
|
||||||
passportNumber: 'A12345678',
|
|
||||||
bonusPoints: 1250,
|
|
||||||
cardNumber: '5678-9012-3456-7890',
|
|
||||||
expiryDate: '12/2025',
|
|
||||||
registrationDate: '15.06.2020',
|
|
||||||
};
|
|
||||||
|
|
||||||
// Sample transaction data
|
|
||||||
const generateTransactions = () => {
|
|
||||||
const stations = [
|
|
||||||
'АЗС Душанбе-Центр',
|
|
||||||
'АЗС Душанбе-Запад',
|
|
||||||
'АЗС Душанбе-Восток',
|
|
||||||
'АЗС Худжанд',
|
|
||||||
'АЗС Куляб',
|
|
||||||
];
|
|
||||||
|
|
||||||
const products = [
|
|
||||||
{ name: 'ДТ', price: 8.5 },
|
|
||||||
{ name: 'АИ-92', price: 9.2 },
|
|
||||||
{ name: 'АИ-95', price: 10.5 },
|
|
||||||
{ name: 'Z-100 Power', price: 11.8 },
|
|
||||||
{ name: 'Пропан', price: 6.3 },
|
|
||||||
];
|
|
||||||
|
|
||||||
const transactions = [];
|
|
||||||
|
|
||||||
// Generate 50 random transactions over the last 6 months
|
|
||||||
for (let i = 0; i < 50; i++) {
|
|
||||||
const date = subMonths(new Date(), Math.random() * 6);
|
|
||||||
const station = stations[Math.floor(Math.random() * stations.length)];
|
|
||||||
const product = products[Math.floor(Math.random() * products.length)];
|
|
||||||
const quantity = Math.floor(Math.random() * 40) + 10; // 10-50 liters
|
|
||||||
const cost = product.price;
|
|
||||||
const total = quantity * cost;
|
|
||||||
|
|
||||||
transactions.push({
|
|
||||||
id: i + 1,
|
|
||||||
date,
|
|
||||||
station,
|
|
||||||
product: product.name,
|
|
||||||
quantity,
|
|
||||||
cost,
|
|
||||||
total,
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
// Sort by date (newest first)
|
|
||||||
return transactions.sort((a, b) => b.date.getTime() - a.date.getTime());
|
|
||||||
};
|
|
||||||
|
|
||||||
const transactions = generateTransactions();
|
|
||||||
|
|
||||||
export const TransactionsTable = () => {
|
export const TransactionsTable = () => {
|
||||||
const [startDate, setStartDate] = useState<Date | undefined>(
|
const [startDate, setStartDate] = useState<Date>(subMonths(new Date(), 1));
|
||||||
subMonths(new Date(), 1),
|
const [endDate, setEndDate] = useState<Date>(new Date());
|
||||||
);
|
|
||||||
const [endDate, setEndDate] = useState<Date | undefined>(new Date());
|
const { data, refetch } = useFetchBonusTransactionsQuery({
|
||||||
const [filteredTransactions, setFilteredTransactions] =
|
limit: 100,
|
||||||
useState(transactions);
|
page: 1,
|
||||||
|
start_date: format(startDate, 'yyyy-MM-dd'),
|
||||||
|
end_date: format(endDate, 'yyyy-MM-dd'),
|
||||||
|
});
|
||||||
|
|
||||||
// Filter transactions by date range
|
// Filter transactions by date range
|
||||||
const filterTransactions = () => {
|
const filterTransactions = () => {
|
||||||
if (!startDate || !endDate) return;
|
if (!startDate || !endDate) return;
|
||||||
|
|
||||||
const filtered = transactions.filter((transaction) => {
|
refetch();
|
||||||
const transactionDate = new Date(transaction.date);
|
|
||||||
return transactionDate >= startDate && transactionDate <= endDate;
|
|
||||||
});
|
|
||||||
|
|
||||||
setFilteredTransactions(filtered);
|
|
||||||
};
|
};
|
||||||
|
|
||||||
const { t } = useTextController();
|
const { t } = useTextController();
|
||||||
|
|
||||||
|
useEffect(() => {}, [startDate, endDate]);
|
||||||
|
|
||||||
|
if (!data) return null;
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className='space-y-6'>
|
<div className='space-y-6'>
|
||||||
<div className='flex flex-col items-start justify-between gap-4 md:flex-row md:items-center'>
|
<div className='flex flex-col items-start justify-between gap-4 md:flex-row md:items-center'>
|
||||||
@ -200,22 +147,22 @@ export const TransactionsTable = () => {
|
|||||||
</TableRow>
|
</TableRow>
|
||||||
</TableHeader>
|
</TableHeader>
|
||||||
<TableBody>
|
<TableBody>
|
||||||
{filteredTransactions.length > 0 ? (
|
{data.transactions.length > 0 ? (
|
||||||
filteredTransactions.map((transaction) => (
|
data.transactions.map((transaction) => (
|
||||||
<TableRow key={transaction.id}>
|
<TableRow key={transaction.id}>
|
||||||
<TableCell>
|
<TableCell>
|
||||||
{format(transaction.date, 'dd.MM.yyyy')}
|
{format(new Date(transaction.date_create), 'dd.MM.yyyy')}
|
||||||
</TableCell>
|
</TableCell>
|
||||||
<TableCell>{transaction.station}</TableCell>
|
<TableCell>{transaction.station}</TableCell>
|
||||||
<TableCell>{transaction.product}</TableCell>
|
<TableCell>{transaction.product_name}</TableCell>
|
||||||
<TableCell className='text-right'>
|
<TableCell className='text-right'>
|
||||||
{transaction.quantity}
|
{transaction.price_real}
|
||||||
</TableCell>
|
</TableCell>
|
||||||
<TableCell className='text-right'>
|
<TableCell className='text-right'>
|
||||||
{transaction.cost.toFixed(2)} {t('corporate.currency')}
|
{transaction.amount} {t('corporate.currency')}
|
||||||
</TableCell>
|
</TableCell>
|
||||||
<TableCell className='text-right font-medium'>
|
<TableCell className='text-right font-medium'>
|
||||||
{transaction.total.toFixed(2)} {t('corporate.currency')}
|
{transaction.sum_real} {t('corporate.currency')}
|
||||||
</TableCell>
|
</TableCell>
|
||||||
</TableRow>
|
</TableRow>
|
||||||
))
|
))
|
||||||
|
|||||||
@ -2,7 +2,7 @@
|
|||||||
|
|
||||||
import { Briefcase } from 'lucide-react';
|
import { Briefcase } from 'lucide-react';
|
||||||
|
|
||||||
import { Jobs } from '@/app/api-utlities/@types/main';
|
import { Jobs } from '@/app/api-utlities/@types';
|
||||||
|
|
||||||
import { useTextController } from '@/shared/language/hooks/use-text-controller';
|
import { useTextController } from '@/shared/language/hooks/use-text-controller';
|
||||||
import { cn } from '@/shared/lib/utils';
|
import { cn } from '@/shared/lib/utils';
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user