add banner of home page

This commit is contained in:
2026-02-05 12:32:20 +03:30
parent 2dbaf6790e
commit cf51b14681
28 changed files with 762 additions and 9 deletions

View File

@@ -4,10 +4,17 @@
"Header": {},
"HomePage": {
"title": "سلام",
"about141": "درباره 141",
"blog": "وبلاگ",
"searchThisArea": "جست و جو در این محدوده",
"allRightsReserved": "تمامی حقوق این وبسایت متعلق به مرکز مدیریت راه‌های کشور می‌باشد."
"home": "خانه",
"search": "جست و جو",
"salons": "سالن ها",
"see_salons": "مشاهده سالن ها",
"about_us": "درباره ما",
"my_order": "سفارش های من",
"auth": "ورود و ثبت نام",
"des": "بهترین سالن های زیبایی و کلینیک ها را ببین ، مقایسه کن و انلاین نوبت بگیر",
"main_title_line1": "رزرو آنلاین خدمات زیبایی،",
"main_title_line2": "سریع و مطمئن",
"contact_us": "تماس با ما"
},
"Application": {
"title": "دانلود نرم افزار تلفن همراه 141",

View File

@@ -3,5 +3,21 @@ import createNextIntlPlugin from "next-intl/plugin";
const withNextIntl = createNextIntlPlugin();
const nextConfig: NextConfig = {};
const nextConfig: NextConfig = {
turbopack: {
rules: {
"*.svg": {
loaders: [
{
loader: "@svgr/webpack",
options: {
icon: true,
},
},
],
as: "*.ts",
},
},
},
};
export default withNextIntl(nextConfig);

View File

@@ -10,6 +10,7 @@
"format": "npx prettier . --write"
},
"dependencies": {
"@base-ui/react": "^1.1.0",
"@dnd-kit/core": "^6.3.1",
"@dnd-kit/modifiers": "^9.0.0",
"@dnd-kit/sortable": "^10.0.0",
@@ -30,6 +31,7 @@
"next": "16.1.1",
"next-intl": "^4.6.1",
"next-pwa": "^5.6.0",
"next-sitemap": "^4.2.3",
"next-themes": "^0.4.6",
"nextjs-toploader": "^3.8.16",
"postcss": "^8.5.3",

117
pnpm-lock.yaml generated
View File

@@ -7,6 +7,9 @@ settings:
importers:
.:
dependencies:
"@base-ui/react":
specifier: ^1.1.0
version: 1.1.0(@types/react@19.2.7)(react-dom@19.2.3(react@19.2.3))(react@19.2.3)
"@dnd-kit/core":
specifier: ^6.3.1
version: 6.3.1(react-dom@19.2.3(react@19.2.3))(react@19.2.3)
@@ -67,6 +70,9 @@ importers:
next-pwa:
specifier: ^5.6.0
version: 5.6.0(@babel/core@7.28.5)(next@16.1.1(@babel/core@7.28.5)(react-dom@19.2.3(react@19.2.3))(react@19.2.3))(webpack@5.104.1)
next-sitemap:
specifier: ^4.2.3
version: 4.2.3(next@16.1.1(@babel/core@7.28.5)(react-dom@19.2.3(react@19.2.3))(react@19.2.3))
next-themes:
specifier: ^0.4.6
version: 0.4.6(react-dom@19.2.3(react@19.2.3))(react@19.2.3)
@@ -99,7 +105,7 @@ importers:
version: 4.1.18
zustand:
specifier: ^5.0.9
version: 5.0.9(@types/react@19.2.7)(react@19.2.3)
version: 5.0.9(@types/react@19.2.7)(react@19.2.3)(use-sync-external-store@1.6.0(react@19.2.3))
devDependencies:
"@types/js-cookie":
specifier: ^3.0.6
@@ -1004,6 +1010,39 @@ packages:
}
engines: { node: ">=6.9.0" }
"@base-ui/react@1.1.0":
resolution:
{
integrity: sha512-ikcJRNj1mOiF2HZ5jQHrXoVoHcNHdBU5ejJljcBl+VTLoYXR6FidjTN86GjO6hyshi6TZFuNvv0dEOgaOFv6Lw==,
}
engines: { node: ">=14.0.0" }
peerDependencies:
"@types/react": ^17 || ^18 || ^19
react: ^17 || ^18 || ^19
react-dom: ^17 || ^18 || ^19
peerDependenciesMeta:
"@types/react":
optional: true
"@base-ui/utils@0.2.4":
resolution:
{
integrity: sha512-smZwpMhjO29v+jrZusBSc5T+IJ3vBb9cjIiBjtKcvWmRj9Z4DWGVR3efr1eHR56/bqY5a4qyY9ElkOY5ljo3ng==,
}
peerDependencies:
"@types/react": ^17 || ^18 || ^19
react: ^17 || ^18 || ^19
react-dom: ^17 || ^18 || ^19
peerDependenciesMeta:
"@types/react":
optional: true
"@corex/deepmerge@4.0.43":
resolution:
{
integrity: sha512-N8uEMrMPL0cu/bdboEWpQYb/0i2K5Qn8eCsxzOmxSggJbbQte7ljMRoXm917AbntqTGOzdTu+vP3KOOzoC70HQ==,
}
"@date-fns/tz@1.4.1":
resolution:
{
@@ -5584,6 +5623,16 @@ packages:
peerDependencies:
next: ">=9.0.0"
next-sitemap@4.2.3:
resolution:
{
integrity: sha512-vjdCxeDuWDzldhCnyFCQipw5bfpl4HmZA7uoo3GAaYGjGgfL4Cxb1CiztPuWGmS+auYs7/8OekRS8C2cjdAsjQ==,
}
engines: { node: ">=14.18" }
hasBin: true
peerDependencies:
next: "*"
next-themes@0.4.6:
resolution:
{
@@ -6212,6 +6261,12 @@ packages:
}
engines: { node: ">=0.10.0" }
reselect@5.1.1:
resolution:
{
integrity: sha512-K/BG6eIky/SBpzfHZv/dd+9JBFiS4SWV7FIujVyJRux6e45+73RaUHXLmIR1f7WOMaQ0U1km6qwklRQxpJJY0w==,
}
resolve-from@4.0.0:
resolution:
{
@@ -6685,6 +6740,12 @@ packages:
engines: { node: ">=14.0.0" }
hasBin: true
tabbable@6.4.0:
resolution:
{
integrity: sha512-05PUHKSNE8ou2dwIxTngl4EzcnsCDZGJ/iCLtDflR/SHB/ny14rXc+qU5P4mG9JkusiV7EivzY9Mhm55AzAvCg==,
}
tailwind-merge@3.4.0:
resolution:
{
@@ -6997,6 +7058,14 @@ packages:
"@types/react":
optional: true
use-sync-external-store@1.6.0:
resolution:
{
integrity: sha512-Pp6GSwGP/NrPIrxVFAIkOQeyw8lFenOHijQWkUTrDvrF4ALqylP2C/KCkeS9dpUM3KvYRQhna5vt7IL95+ZQ9w==,
}
peerDependencies:
react: ^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0
watchpack@2.4.0:
resolution:
{
@@ -7997,6 +8066,33 @@ snapshots:
"@babel/helper-string-parser": 7.27.1
"@babel/helper-validator-identifier": 7.28.5
"@base-ui/react@1.1.0(@types/react@19.2.7)(react-dom@19.2.3(react@19.2.3))(react@19.2.3)":
dependencies:
"@babel/runtime": 7.28.4
"@base-ui/utils": 0.2.4(@types/react@19.2.7)(react-dom@19.2.3(react@19.2.3))(react@19.2.3)
"@floating-ui/react-dom": 2.1.6(react-dom@19.2.3(react@19.2.3))(react@19.2.3)
"@floating-ui/utils": 0.2.10
react: 19.2.3
react-dom: 19.2.3(react@19.2.3)
reselect: 5.1.1
tabbable: 6.4.0
use-sync-external-store: 1.6.0(react@19.2.3)
optionalDependencies:
"@types/react": 19.2.7
"@base-ui/utils@0.2.4(@types/react@19.2.7)(react-dom@19.2.3(react@19.2.3))(react@19.2.3)":
dependencies:
"@babel/runtime": 7.28.4
"@floating-ui/utils": 0.2.10
react: 19.2.3
react-dom: 19.2.3(react@19.2.3)
reselect: 5.1.1
use-sync-external-store: 1.6.0(react@19.2.3)
optionalDependencies:
"@types/react": 19.2.7
"@corex/deepmerge@4.0.43": {}
"@date-fns/tz@1.4.1": {}
"@dnd-kit/accessibility@3.1.1(react@19.2.3)":
@@ -10855,6 +10951,14 @@ snapshots:
- uglify-js
- webpack
next-sitemap@4.2.3(next@16.1.1(@babel/core@7.28.5)(react-dom@19.2.3(react@19.2.3))(react@19.2.3)):
dependencies:
"@corex/deepmerge": 4.0.43
"@next/env": 13.5.11
fast-glob: 3.3.1
minimist: 1.2.8
next: 16.1.1(@babel/core@7.28.5)(react-dom@19.2.3(react@19.2.3))(react@19.2.3)
next-themes@0.4.6(react-dom@19.2.3(react@19.2.3))(react@19.2.3):
dependencies:
react: 19.2.3
@@ -11207,6 +11311,8 @@ snapshots:
require-from-string@2.0.2: {}
reselect@5.1.1: {}
resolve-from@4.0.0: {}
resolve-pkg-maps@1.0.0: {}
@@ -11546,6 +11652,8 @@ snapshots:
csso: 5.0.5
picocolors: 1.1.1
tabbable@6.4.0: {}
tailwind-merge@3.4.0: {}
tailwind-scrollbar@4.0.2(react@19.2.3)(tailwindcss@4.1.18):
@@ -11761,6 +11869,10 @@ snapshots:
optionalDependencies:
"@types/react": 19.2.7
use-sync-external-store@1.6.0(react@19.2.3):
dependencies:
react: 19.2.3
watchpack@2.4.0:
dependencies:
glob-to-regexp: 0.4.1
@@ -12002,7 +12114,8 @@ snapshots:
zod@4.2.1: {}
zustand@5.0.9(@types/react@19.2.7)(react@19.2.3):
zustand@5.0.9(@types/react@19.2.7)(react@19.2.3)(use-sync-external-store@1.6.0(react@19.2.3)):
optionalDependencies:
"@types/react": 19.2.7
react: 19.2.3
use-sync-external-store: 1.6.0(react@19.2.3)

View File

@@ -0,0 +1,3 @@
<svg width="20" height="20" viewBox="0 0 20 20" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M15.4419 14.5581C15.686 14.8021 15.686 15.1979 15.4419 15.4419C15.1979 15.686 14.8021 15.686 14.5581 15.4419L8.75 9.63388L5.44194 12.9419C5.26319 13.1207 4.99437 13.1742 4.76082 13.0774C4.52728 12.9807 4.375 12.7528 4.375 12.5V5C4.375 4.65482 4.65482 4.375 5 4.375L12.5 4.375C12.7528 4.375 12.9807 4.52728 13.0774 4.76082C13.1742 4.99437 13.1207 5.26319 12.9419 5.44194L9.63388 8.75L15.4419 14.5581Z" fill="currentColor"/>
</svg>

After

Width:  |  Height:  |  Size: 539 B

View File

@@ -0,0 +1,9 @@
<svg width="195" height="2" viewBox="0 0 195 2" fill="none" xmlns="http://www.w3.org/2000/svg">
<rect width="195" height="2" rx="1" fill="url(#paint0_linear_1658_15851)"/>
<defs>
<linearGradient id="paint0_linear_1658_15851" x1="0" y1="1" x2="195" y2="1" gradientUnits="userSpaceOnUse">
<stop stop-color="#9480DF"/>
<stop offset="1" stop-color="#9480DF" stop-opacity="0"/>
</linearGradient>
</defs>
</svg>

After

Width:  |  Height:  |  Size: 458 B

View File

@@ -0,0 +1,3 @@
<svg width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M12 0L13.3262 3.58401C14.5416 6.86864 17.1314 9.45837 20.416 10.6738L24 12L20.416 13.3262C17.1314 14.5416 14.5416 17.1314 13.3262 20.416L12 24L10.6738 20.416C9.45837 17.1314 6.86864 14.5416 3.58401 13.3262L0 12L3.58401 10.6738C6.86864 9.45837 9.45837 6.86864 10.6738 3.58401L12 0Z" fill="#9480DF"/>
</svg>

After

Width:  |  Height:  |  Size: 415 B

View File

@@ -0,0 +1,9 @@
<svg width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M17 14C17.5523 14 18 13.5523 18 13C18 12.4477 17.5523 12 17 12C16.4477 12 16 12.4477 16 13C16 13.5523 16.4477 14 17 14Z" fill="currentColor"/>
<path d="M17 18C17.5523 18 18 17.5523 18 17C18 16.4477 17.5523 16 17 16C16.4477 16 16 16.4477 16 17C16 17.5523 16.4477 18 17 18Z" fill="currentColor"/>
<path d="M13 13C13 13.5523 12.5523 14 12 14C11.4477 14 11 13.5523 11 13C11 12.4477 11.4477 12 12 12C12.5523 12 13 12.4477 13 13Z" fill="currentColor"/>
<path d="M13 17C13 17.5523 12.5523 18 12 18C11.4477 18 11 17.5523 11 17C11 16.4477 11.4477 16 12 16C12.5523 16 13 16.4477 13 17Z" fill="currentColor"/>
<path d="M7 14C7.55229 14 8 13.5523 8 13C8 12.4477 7.55229 12 7 12C6.44772 12 6 12.4477 6 13C6 13.5523 6.44772 14 7 14Z" fill="currentColor"/>
<path d="M7 18C7.55229 18 8 17.5523 8 17C8 16.4477 7.55229 16 7 16C6.44772 16 6 16.4477 6 17C6 17.5523 6.44772 18 7 18Z" fill="currentColor"/>
<path fill-rule="evenodd" clip-rule="evenodd" d="M7 1.75C7.41421 1.75 7.75 2.08579 7.75 2.5V3.26272C8.412 3.24999 9.14133 3.24999 9.94346 3.25H14.0564C14.8586 3.24999 15.588 3.24999 16.25 3.26272V2.5C16.25 2.08579 16.5858 1.75 17 1.75C17.4142 1.75 17.75 2.08579 17.75 2.5V3.32709C18.0099 3.34691 18.2561 3.37182 18.489 3.40313C19.6614 3.56076 20.6104 3.89288 21.3588 4.64124C22.1071 5.38961 22.4392 6.33855 22.5969 7.51098C22.75 8.65018 22.75 10.1058 22.75 11.9435V14.0564C22.75 15.8941 22.75 17.3498 22.5969 18.489C22.4392 19.6614 22.1071 20.6104 21.3588 21.3588C20.6104 22.1071 19.6614 22.4392 18.489 22.5969C17.3498 22.75 15.8942 22.75 14.0565 22.75H9.94359C8.10585 22.75 6.65018 22.75 5.51098 22.5969C4.33856 22.4392 3.38961 22.1071 2.64124 21.3588C1.89288 20.6104 1.56076 19.6614 1.40314 18.489C1.24997 17.3498 1.24998 15.8942 1.25 14.0564V11.9436C1.24998 10.1058 1.24997 8.65019 1.40314 7.51098C1.56076 6.33855 1.89288 5.38961 2.64124 4.64124C3.38961 3.89288 4.33856 3.56076 5.51098 3.40313C5.7439 3.37182 5.99006 3.34691 6.25 3.32709V2.5C6.25 2.08579 6.58579 1.75 7 1.75ZM5.71085 4.88976C4.70476 5.02502 4.12511 5.27869 3.7019 5.7019C3.27869 6.12511 3.02502 6.70476 2.88976 7.71085C2.86685 7.88123 2.8477 8.06061 2.83168 8.25H21.1683C21.1523 8.06061 21.1331 7.88124 21.1102 7.71085C20.975 6.70476 20.7213 6.12511 20.2981 5.7019C19.8749 5.27869 19.2952 5.02502 18.2892 4.88976C17.2615 4.75159 15.9068 4.75 14 4.75H10C8.09318 4.75 6.73851 4.75159 5.71085 4.88976ZM2.75 12C2.75 11.146 2.75032 10.4027 2.76309 9.75H21.2369C21.2497 10.4027 21.25 11.146 21.25 12V14C21.25 15.9068 21.2484 17.2615 21.1102 18.2892C20.975 19.2952 20.7213 19.8749 20.2981 20.2981C19.8749 20.7213 19.2952 20.975 18.2892 21.1102C17.2615 21.2484 15.9068 21.25 14 21.25H10C8.09318 21.25 6.73851 21.2484 5.71085 21.1102C4.70476 20.975 4.12511 20.7213 3.7019 20.2981C3.27869 19.8749 3.02502 19.2952 2.88976 18.2892C2.75159 17.2615 2.75 15.9068 2.75 14V12Z" fill="currentColor"/>
</svg>

After

Width:  |  Height:  |  Size: 2.9 KiB

View File

@@ -0,0 +1,3 @@
<svg width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
<path fill-rule="evenodd" clip-rule="evenodd" d="M12 1.25C12.4142 1.25 12.75 1.58579 12.75 2V3.28169C16.9842 3.64113 20.3589 7.01581 20.7183 11.25H22C22.4142 11.25 22.75 11.5858 22.75 12C22.75 12.4142 22.4142 12.75 22 12.75H20.7183C20.3589 16.9842 16.9842 20.3589 12.75 20.7183V22C12.75 22.4142 12.4142 22.75 12 22.75C11.5858 22.75 11.25 22.4142 11.25 22V20.7183C7.01581 20.3589 3.64113 16.9842 3.28169 12.75H2C1.58579 12.75 1.25 12.4142 1.25 12C1.25 11.5858 1.58579 11.25 2 11.25H3.28169C3.64113 7.01581 7.01581 3.64113 11.25 3.28169V2C11.25 1.58579 11.5858 1.25 12 1.25ZM12 4.75C7.99594 4.75 4.75 7.99594 4.75 12C4.75 16.0041 7.99594 19.25 12 19.25C16.0041 19.25 19.25 16.0041 19.25 12C19.25 7.99594 16.0041 4.75 12 4.75ZM12 9.75C10.7574 9.75 9.75 10.7574 9.75 12C9.75 13.2426 10.7574 14.25 12 14.25C13.2426 14.25 14.25 13.2426 14.25 12C14.25 10.7574 13.2426 9.75 12 9.75ZM8.25 12C8.25 9.92893 9.92893 8.25 12 8.25C14.0711 8.25 15.75 9.92893 15.75 12C15.75 14.0711 14.0711 15.75 12 15.75C9.92893 15.75 8.25 14.0711 8.25 12Z" fill="currentColor"/>
</svg>

After

Width:  |  Height:  |  Size: 1.1 KiB

5
public/icons/logo.svg Normal file
View File

@@ -0,0 +1,5 @@
<svg width="59" height="36" viewBox="0 0 59 36" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M58.417 17.7236L58.4238 17.7324L58.4316 17.7412C58.4817 17.8004 58.4921 17.8306 58.4941 17.8369C58.4964 17.8439 58.5 17.8568 58.5 17.9053C58.5 17.9544 58.4964 17.9668 58.4941 17.9736C58.4921 17.9799 58.4815 18.0094 58.4316 18.0684L58.4238 18.0771L58.417 18.0869L47.8486 31.8223L37.2822 18.0869L37.2744 18.0771L37.2676 18.0684L37.2178 18.001C37.2089 17.9857 37.2061 17.9767 37.2051 17.9736C37.2028 17.9668 37.1992 17.9545 37.1992 17.9053C37.1992 17.8567 37.2028 17.8439 37.2051 17.8369C37.2071 17.8307 37.2176 17.8004 37.2676 17.7412L37.2744 17.7324L37.2822 17.7236L47.8496 3.96973L58.417 17.7236Z" fill="#140B4E" stroke="#140B4E"/>
<path d="M21.7178 17.7236L21.7246 17.7324L21.7324 17.7412C21.7825 17.8004 21.7929 17.8306 21.7949 17.8369C21.7972 17.8439 21.8008 17.8568 21.8008 17.9053C21.8008 17.9544 21.7972 17.9668 21.7949 17.9736C21.7929 17.9799 21.7823 18.0094 21.7324 18.0684L21.7246 18.0771L21.7178 18.0869L11.1494 31.8223L0.583008 18.0869L0.575195 18.0771L0.568359 18.0684L0.518555 18.001C0.50972 17.9857 0.50687 17.9767 0.505859 17.9736C0.503628 17.9668 0.50001 17.9545 0.5 17.9053C0.5 17.8567 0.503619 17.8439 0.505859 17.8369C0.507889 17.8307 0.518333 17.8004 0.568359 17.7412L0.575195 17.7324L0.583008 17.7236L11.1504 3.96973L21.7178 17.7236Z" fill="#140B4E" stroke="#140B4E"/>
<path d="M43.5322 17.7197L43.5479 17.7373C43.6225 17.82 43.644 17.8685 43.6514 17.8896C43.6587 17.9108 43.6631 17.936 43.6631 18C43.6631 18.064 43.6587 18.0892 43.6514 18.1104C43.644 18.1315 43.6225 18.18 43.5479 18.2627L43.5322 18.2803L29.5449 35.3164L15.5586 18.2803L15.543 18.2627C15.4683 18.1799 15.4468 18.1315 15.4395 18.1104C15.4322 18.0893 15.4277 18.0641 15.4277 18C15.4277 17.9359 15.4322 17.9107 15.4395 17.8896C15.4468 17.8685 15.4683 17.8201 15.543 17.7373L15.5586 17.7197L29.5449 0.660156L43.5322 17.7197Z" fill="#EAE6F9" stroke="#140B4E"/>
</svg>

After

Width:  |  Height:  |  Size: 1.9 KiB

View File

@@ -0,0 +1,21 @@
<svg width="42" height="66" viewBox="0 0 42 66" fill="none" xmlns="http://www.w3.org/2000/svg">
<path opacity="0.3" d="M21 66C23.7614 66 26 64.8807 26 63.5C26 62.1193 23.7614 61 21 61C18.2386 61 16 62.1193 16 63.5C16 64.8807 18.2386 66 21 66Z" fill="#273343"/>
<path fill-rule="evenodd" clip-rule="evenodd" d="M21 0C32.598 0 42 9.40202 42 21C42 31.9234 33.6599 40.8989 23 41.906V62C23 63.1046 22.1046 64 21 64C19.8954 64 19 63.1046 19 62V41.906C8.34014 40.8989 0 31.9234 0 21C0 9.40202 9.40202 0 21 0Z" fill="white"/>
<foreignObject x="-61" y="-61" width="164" height="164"></foreignObject><g filter="url(#filter0_d_173_69)" data-figma-bg-blur-radius="65">
<path d="M38 21C38 11.6112 30.3888 4 21 4C11.6112 4 4 11.6112 4 21C4 30.3888 11.6112 38 21 38C30.3888 38 38 30.3888 38 21Z" fill="currentColor"/>
</g>
<path d="M28 21C28 17.134 24.866 14 21 14C17.134 14 14 17.134 14 21C14 24.866 17.134 28 21 28C24.866 28 28 24.866 28 21Z" fill="white"/>
<defs>
<filter id="filter0_d_173_69" x="-61" y="-61" width="164" height="164" filterUnits="userSpaceOnUse" color-interpolation-filters="sRGB">
<feFlood flood-opacity="0" result="BackgroundImageFix"/>
<feColorMatrix in="SourceAlpha" type="matrix" values="0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 127 0" result="hardAlpha"/>
<feOffset dy="1"/>
<feGaussianBlur stdDeviation="1"/>
<feComposite in2="hardAlpha" operator="out"/>
<feColorMatrix type="matrix" values="0 0 0 0 0.235294 0 0 0 0 0.25098 0 0 0 0 0.262745 0 0 0 0.3 0"/>
<feBlend mode="normal" in2="BackgroundImageFix" result="effect1_dropShadow_173_69"/>
<feBlend mode="normal" in="SourceGraphic" in2="effect1_dropShadow_173_69" result="shape"/>
</filter>
<clipPath id="bgblur_0_173_69_clip_path" transform="translate(61 61)"><path d="M38 21C38 11.6112 30.3888 4 21 4C11.6112 4 4 11.6112 4 21C4 30.3888 11.6112 38 21 38C30.3888 38 38 30.3888 38 21Z"/>
</clipPath></defs>
</svg>

After

Width:  |  Height:  |  Size: 2.0 KiB

View File

@@ -0,0 +1,3 @@
<svg width="20" height="20" viewBox="0 0 20 20" fill="none" xmlns="http://www.w3.org/2000/svg">
<path fill-rule="evenodd" clip-rule="evenodd" d="M9.95153 1.04102H10.0472C11.9708 1.04101 13.4784 1.041 14.6546 1.19914C15.8585 1.361 16.8086 1.69879 17.5542 2.44446C18.2999 3.19014 18.6377 4.14016 18.7996 5.3441C18.9577 6.52034 18.9577 8.02788 18.9577 9.95154V10.0472C18.9577 11.9708 18.9577 13.4784 18.7996 14.6546C18.6377 15.8585 18.2999 16.8086 17.5542 17.5542C16.8086 18.2999 15.8585 18.6377 14.6546 18.7996C13.4784 18.9577 11.9708 18.9577 10.0472 18.9577H9.95154C8.02788 18.9577 6.52034 18.9577 5.3441 18.7996C4.14016 18.6377 3.19014 18.2999 2.44446 17.5542C1.69879 16.8086 1.361 15.8585 1.19914 14.6546C1.041 13.4784 1.04101 11.9708 1.04102 10.0472V9.95153C1.04101 8.02787 1.041 6.52034 1.19914 5.3441C1.361 4.14016 1.69879 3.19014 2.44446 2.44446C3.19014 1.69879 4.14016 1.361 5.3441 1.19914C6.52034 1.041 8.02787 1.04101 9.95153 1.04102ZM5.51066 2.43799C4.44533 2.58122 3.80306 2.85363 3.32835 3.32835C2.85363 3.80306 2.58122 4.44533 2.43799 5.51066C2.29234 6.59398 2.29102 8.01749 2.29102 9.99935C2.29102 11.9812 2.29234 13.4047 2.43799 14.488C2.58122 15.5534 2.85363 16.1956 3.32835 16.6704C3.80306 17.1451 4.44533 17.4175 5.51066 17.5607C6.59398 17.7064 8.01749 17.7077 9.99935 17.7077C11.9812 17.7077 13.4047 17.7064 14.488 17.5607C15.5534 17.4175 16.1956 17.1451 16.6704 16.6704C17.1451 16.1956 17.4175 15.5534 17.5607 14.488C17.7064 13.4047 17.7077 11.9812 17.7077 9.99935C17.7077 8.01749 17.7064 6.59398 17.5607 5.51066C17.4175 4.44533 17.1451 3.80306 16.6704 3.32835C16.1956 2.85363 15.5534 2.58122 14.488 2.43799C13.4047 2.29234 11.9812 2.29102 9.99935 2.29102C8.01749 2.29102 6.59398 2.29234 5.51066 2.43799ZM8.76372 5.3801C9.01367 5.61815 9.02332 6.01376 8.78527 6.26372L6.40432 8.76372C6.28635 8.88758 6.12278 8.95768 5.95173 8.95768C5.78068 8.95768 5.61711 8.88758 5.49914 8.76372L4.54676 7.76372C4.30871 7.51376 4.31836 7.11815 4.56831 6.8801C4.81827 6.64204 5.21388 6.65169 5.45194 6.90165L5.95173 7.42643L7.8801 5.40165C8.11815 5.15169 8.51376 5.14204 8.76372 5.3801ZM10.2077 7.49935C10.2077 7.15417 10.4875 6.87435 10.8327 6.87435H14.9993C15.3445 6.87435 15.6243 7.15417 15.6243 7.49935C15.6243 7.84453 15.3445 8.12435 14.9993 8.12435H10.8327C10.4875 8.12435 10.2077 7.84453 10.2077 7.49935ZM8.76372 11.2134C9.01367 11.4515 9.02332 11.8471 8.78527 12.097L6.40432 14.5971C6.28635 14.7209 6.12278 14.791 5.95173 14.791C5.78068 14.791 5.61711 14.7209 5.49914 14.5971L4.54676 13.5971C4.30871 13.3471 4.31836 12.9515 4.56831 12.7134C4.81827 12.4754 5.21388 12.485 5.45194 12.735L5.95173 13.2598L7.8801 11.235C8.11815 10.985 8.51376 10.9754 8.76372 11.2134ZM10.2077 13.3327C10.2077 12.9875 10.4875 12.7077 10.8327 12.7077H14.9993C15.3445 12.7077 15.6243 12.9875 15.6243 13.3327C15.6243 13.6779 15.3445 13.9577 14.9993 13.9577H10.8327C10.4875 13.9577 10.2077 13.6779 10.2077 13.3327Z" fill="currentColor"/>
</svg>

After

Width:  |  Height:  |  Size: 2.9 KiB

3
public/icons/search.svg Normal file
View File

@@ -0,0 +1,3 @@
<svg width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
<path fill-rule="evenodd" clip-rule="evenodd" d="M11.5 2.75C6.66751 2.75 2.75 6.66751 2.75 11.5C2.75 16.3325 6.66751 20.25 11.5 20.25C16.3325 20.25 20.25 16.3325 20.25 11.5C20.25 6.66751 16.3325 2.75 11.5 2.75ZM1.25 11.5C1.25 5.83908 5.83908 1.25 11.5 1.25C17.1609 1.25 21.75 5.83908 21.75 11.5C21.75 14.0605 20.8111 16.4017 19.2589 18.1982L22.5303 21.4697C22.8232 21.7626 22.8232 22.2374 22.5303 22.5303C22.2374 22.8232 21.7626 22.8232 21.4697 22.5303L18.1982 19.2589C16.4017 20.8111 14.0605 21.75 11.5 21.75C5.83908 21.75 1.25 17.1609 1.25 11.5Z" fill="currentColor"/>
</svg>

After

Width:  |  Height:  |  Size: 678 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.0 MiB

11
src/app/(main)/layout.tsx Normal file
View File

@@ -0,0 +1,11 @@
import BottomSheet from "@/components/UI/BottomSheet";
import MapProviderComponent from "@/providers/MapProviderComponent";
export default function RootLayout({ children }: { children: React.ReactNode }) {
return (
<MapProviderComponent>
{children}
<BottomSheet />
</MapProviderComponent>
);
}

View File

@@ -50,7 +50,11 @@ export default async function RootLayout({ children }: { children: React.ReactNo
const messages = await getMessages({ locale });
return (
<html lang={locale} dir="rtl">
<body>
<body
style={{
backgroundColor: "var(--color-map-bg)",
}}
>
<ServiceWorkerRegister />
<NextTopLoader color="#16a795" />
<NextIntlClientProvider locale={locale} messages={messages}>

View File

@@ -6,3 +6,13 @@ export { default as LogoutIcon } from "&/icons/logout.svg";
export { default as AvatarIcon } from "&/icons/avatar-icon.svg";
export { default as DarkModeIcon } from "&/icons/dark-mode-icon.svg";
export { default as LightModeIcon } from "&/icons/light-mode-icon.svg";
export { default as LogoIcon } from "&/icons/logo.svg";
export { default as MyOrderIcon } from "&/icons/my-order.svg";
export { default as BannerImage } from "&/images/homePageBannerImage.png";
export { default as BannerStar } from "&/icons/banner-star.svg";
export { default as BannerLine } from "&/icons/banner-line.svg";
export { default as ArrowTopLeft } from "&/icons/arrow-tl.svg";
export { default as SearchIcon } from "&/icons/search.svg";
export { default as CalenderIcon } from "&/icons/calender.svg";
export { default as LocationIcon } from "&/icons/location.svg";
export { default as MarkerIcon } from "&/icons/marker-icon.svg";

View File

@@ -0,0 +1,79 @@
import { MarkerIcon } from "@/assets";
import { LatLng } from "@/stores/LocationStore";
import { Marker, MarkerDragEvent } from "@vis.gl/react-maplibre";
import { AnimatePresence, motion } from "framer-motion";
import { useState } from "react";
type Props = {
location: LatLng;
onDragEnd: (e: MarkerDragEvent) => void;
};
const dropInVariants = {
initial: { y: -50, opacity: 0, scale: 0.8 },
animate: {
y: 0,
opacity: 1,
scale: 1,
transition: {
type: "spring" as const,
stiffness: 600,
damping: 30,
},
},
};
export default function LocationMarker({ location, onDragEnd }: Props) {
const [isDragging, setIsDragging] = useState(false);
const handleDragStart = () => {
setIsDragging(true);
};
const handleDragEnd = (e: MarkerDragEvent) => {
setIsDragging(false);
onDragEnd(e);
};
return (
<Marker
latitude={location.lat}
longitude={location.lng}
draggable
anchor="bottom"
onDragStart={handleDragStart}
onDragEnd={handleDragEnd}
>
<AnimatePresence mode="wait">
<motion.div
variants={dropInVariants}
initial="initial"
animate={{
y: isDragging ? -20 : 0,
opacity: 1,
transition: {
type: "spring" as const,
stiffness: 500,
damping: 25,
},
}}
exit="exit"
className="relative flex flex-col items-center"
>
{/* Drop pointer inside the marker (shown only when dragging) */}
{isDragging && (
<motion.div
layoutId="drop-pointer"
initial={{ scale: 0, opacity: 0 }}
animate={{ scale: 1, opacity: 0.5, y: 10 }}
exit={{ scale: 0, opacity: 0 }}
transition={{ type: "spring", stiffness: 300, damping: 20 }}
className="absolute -bottom-4.5 z-[-1] size-1.5 rounded-full bg-black shadow-md"
/>
)}
<MarkerIcon className="text-neo-aqua size-8" />
</motion.div>
</AnimatePresence>
</Marker>
);
}

View File

@@ -0,0 +1,16 @@
"use client"
import MainMap from "@/components/MainMap";
import LocationMarker from "./LocationMarker";
const LocationModal = () => {
return (
<div style={{ width: "100%", height: "100dvh", position: "relative" }}>
<MainMap>
<LocationMarker location={{lat : 36.541, lng : 54.56}} onDragEnd={()=> {
console.log("hello")}} />
</MainMap>
</div>
);
};
export default LocationModal;

View File

@@ -0,0 +1,36 @@
import SearchItem from "./SearchItem";
import { CalenderIcon, LocationIcon, SearchIcon } from "@/assets";
import { useModalStore } from "@/stores/useModalStore";
import LocationModal from "./LocationModal";
export default function SearchBar() {
const openModal = useModalStore((s) => s.openModal);
return (
<div className="bg-card flex max-w-2xl items-center justify-between gap-1 rounded-full p-4">
{/* آدرس */}
<SearchItem icon={<LocationIcon className="size-5" />}>
<button
type="button"
onClick={() => openModal(<LocationModal />)}
className="w-full text-right text-gray-500"
>
آدرس
</button>
</SearchItem>
{/* تاریخ */}
<SearchItem icon={<CalenderIcon className="size-5" />}>
<input type="date" className="w-full bg-transparent text-gray-500 outline-none" />
</SearchItem>
{/* دسته بندی */}
<SearchItem icon={<SearchIcon className="size-5" />}>
<select className="w-full bg-transparent text-gray-500 outline-none">
<option>دسته بندی</option>
<option>رستوران</option>
<option>کافیشاپ</option>
</select>
</SearchItem>
</div>
);
}

View File

@@ -0,0 +1,15 @@
interface SearchItemProps {
icon: React.ReactNode;
children: React.ReactNode;
}
function SearchItem({ icon, children }: SearchItemProps) {
return (
<div className="flex min-w-[180px] items-center gap-2 rounded-full bg-white px-4 py-3 shadow-sm">
<div className="text-gray-400">{icon}</div>
<div className="flex-1">{children}</div>
</div>
);
}
export default SearchItem;

View File

@@ -0,0 +1,169 @@
"use client";
import { ArrowTopLeft, AvatarIcon, BannerImage, BannerLine, BannerStar, LogoIcon, MyOrderIcon } from "@/assets";
import { useTranslations } from "next-intl";
import userStore from "@/stores/userStore";
import Image from "next/image";
import ThemeToggle from "@/components/ThemeToggle";
import { motion } from "framer-motion";
import GroupAvatar from "@/components/UI/GroupAvatar";
import SearchBar from "@/components/Home/Desktop/Banner/SearchBar";
export default function BannerComponent() {
const t = useTranslations("HomePage");
const isAuth = userStore((s) => s.isAuth);
return (
<motion.div
initial={{ opacity: 0, y: 60 }}
animate={{ opacity: 1, y: 0 }}
transition={{ duration: 0.9, ease: "easeOut" }}
className="bg-home-page-banner relative mx-16 mt-16 rounded-tr-[200px] rounded-bl-[200px] px-2 pt-2"
>
{/* ================= Header ================= */}
<header className="flex w-full items-center justify-center pt-8">
<div className="flex w-full items-center justify-center space-x-5">
<motion.div
initial={{ scale: 0 }}
animate={{ scale: 1 }}
transition={{ type: "spring", stiffness: 200 }}
>
<LogoIcon className="size-12" />
</motion.div>
<ThemeToggle />
{["home", "search", "salons", "about_us", "contact_us"].map((item, index) => (
<motion.p
key={item}
initial={{ opacity: 0, y: -10 }}
animate={{ opacity: 1, y: 0 }}
transition={{ delay: 0.1 * index }}
className="text-text-primary hover:text-primary-200 cursor-pointer transition"
>
{t(item)}
</motion.p>
))}
</div>
<div className="flex w-full items-center justify-center">
{isAuth ? (
<motion.div whileHover={{ scale: 1.1 }} className="cursor-pointer">
<AvatarIcon className="h-6 w-6" />
</motion.div>
) : (
<span className="flex w-full items-center justify-center gap-x-2">
<motion.button
whileHover={{ scale: 1.05 }}
whileTap={{ scale: 0.95 }}
className="bg-bg flex items-center justify-center gap-x-2 rounded-lg p-3"
>
<MyOrderIcon className="h-6 w-6" />
<p>{t("my_order")}</p>
</motion.button>
<motion.button
whileHover={{ scale: 1.05 }}
whileTap={{ scale: 0.95 }}
className="bg-primary-200 flex items-center justify-center gap-x-2 rounded-lg p-3"
>
<AvatarIcon className="h-6 w-6" />
<p>{t("auth")}</p>
</motion.button>
</span>
)}
</div>
</header>
{/* ================= Main ================= */}
<motion.main initial="hidden" animate="visible" className="flex w-full items-center justify-center py-12">
{/* ===== Left Content ===== */}
<div className="flex w-full flex-col items-center justify-center space-y-7">
{/* Title */}
<motion.div className="text-4xl font-bold">
<motion.span
variants={{
hidden: { opacity: 0, y: 20 },
visible: { opacity: 1, y: 0 },
}}
className="block"
>
{t("main_title_line1")}
</motion.span>
<motion.div
variants={{
hidden: { opacity: 0, y: 20 },
visible: { opacity: 1, y: 0 },
}}
className="flex items-center justify-between"
>
{t("main_title_line2")}
<motion.div
initial={{ scaleX: 0 }}
animate={{ scaleX: 1 }}
transition={{ delay: 0.6, duration: 0.4 }}
className="origin-right"
>
<BannerLine className="h-1 w-28" />
</motion.div>
<motion.div
animate={{ rotate: [0, 10, -10, 0] }}
transition={{ repeat: Infinity, duration: 3 }}
>
<BannerStar className="h-6 w-6" />
</motion.div>
</motion.div>
</motion.div>
{/* Description */}
<motion.p
variants={{
hidden: { opacity: 0 },
visible: { opacity: 1 },
}}
className="text-center text-xs"
>
{t("des")}
</motion.p>
{/* CTA Button */}
<motion.button
whileHover={{ scale: 1.07 }}
whileTap={{ scale: 0.95 }}
transition={{ type: "spring", stiffness: 300 }}
className="bg-primary-200 flex items-center justify-center gap-x-3 rounded-lg p-3"
>
<p className="text-white">{t("see_salons")}</p>
<motion.div whileHover={{ x: -4, y: -4 }}>
<ArrowTopLeft className="size-5 text-white" />
</motion.div>
</motion.button>
<GroupAvatar />
</div>
{/* ===== Right Image ===== */}
<motion.div
animate={{ y: [0, -12, 0] }}
transition={{ repeat: Infinity, duration: 4, ease: "easeInOut" }}
className="flex w-full items-center justify-center"
>
<Image
src={BannerImage}
alt="main_image"
className="object-cover"
width={400}
height={200}
priority
/>
</motion.div>
</motion.main>
<div className="absolute -bottom-10 left-24">
<SearchBar />
</div>
</motion.div>
);
}

View File

@@ -1,3 +1,9 @@
import BannerComponent from "./Banner";
export default function DesktopHome() {
return <></>;
return (
<div className="container mx-auto h-full w-full">
<BannerComponent />
</div>
);
}

View File

@@ -0,0 +1,113 @@
"use client"
import { Map, MapLayerMouseEvent } from "@vis.gl/react-maplibre";
import { ReactNode, useMemo } from "react";
import maplibregl from "maplibre-gl";
import "maplibre-gl/dist/maplibre-gl.css";
import { useMapMove } from "@/utils/useMapMove";
import { useMapLayersStore } from "@/stores/useMapLayersStore";
import { useMapStore } from "@/stores/useMapStore";
// Read From Environment
const BRIGHT_MAP_TILE = process.env.NEXT_PUBLIC_MAP_BRIGHT!;
const DARK_MAP_TILE = process.env.NEXT_PUBLIC_MAP_DARK!;
const STORAGE_KEY = process.env.NEXT_PUBLIC_MAP_VIEW_LOCAL_STORAGE_KEY!;
const MIN_ZOOM = parseFloat(process.env.NEXT_PUBLIC_MAP_MIN_ZOOM!);
const MAX_ZOOM = parseFloat(process.env.NEXT_PUBLIC_MAP_MAX_ZOOM!);
type MainMapProps = {
id?: string;
mapLayer?: string;
children?: ReactNode;
initialBound?: [number, number];
initialZoom?: number;
ref?: React.Ref<any>;
interactiveLayerIds?: string[];
handleMapClick?: (e: maplibregl.MapLayerMouseEvent) => void;
handleContextMenu?: (e: maplibregl.MapMouseEvent & maplibregl.Event) => void;
handleMapInteraction?: () => void;
onMouseMove?: (e: MapLayerMouseEvent) => void;
onMouseLeave?: () => void;
cursor?: string | null;
};
type MapView = {
longitude: number;
latitude: number;
zoom: number;
};
export default function MainMap({
id,
ref,
onMouseLeave,
onMouseMove,
children,
initialBound = [34.5468992, 52.7300532],
initialZoom = 5.5,
handleMapClick,
interactiveLayerIds,
mapLayer,
handleContextMenu,
handleMapInteraction,
cursor,
}: MainMapProps) {
const { mapMove } = useMapMove();
const activeLayer = useMapLayersStore((s) => s.activeLayer);
const setIsMapMoving = useMapStore((s) => s.setIsMapMoving);
const parsed = useMemo(() => {
if (typeof window === "undefined") return null;
const saved = localStorage.getItem(STORAGE_KEY);
return saved ? (JSON.parse(saved) as MapView) : null;
}, []);
const MAP_STYLES = useMemo(
() => ({
dark: DARK_MAP_TILE,
bright: BRIGHT_MAP_TILE,
}),
[]
);
return (
<div className="relative h-full w-full">
<Map
ref={ref}
id={id ? id : "homePageMap"}
interactiveLayerIds={interactiveLayerIds}
initialViewState={{
longitude: parsed ? parsed.longitude : initialBound[1],
latitude: parsed ? parsed.latitude : initialBound[0],
zoom: parsed ? parsed.zoom : initialZoom,
}}
onError={(e) => {
console.error("Map error:", e);
}}
maxBounds={[
[35.0, 18.0], // west, south
[74.5, 44.0], // east, north
]}
onMoveStart={() => setIsMapMoving(true)}
RTLTextPlugin={"/mapbox-gl-rtl-text.js"}
onDragStart={handleMapInteraction}
onMouseMove={onMouseMove}
onMouseLeave={onMouseLeave}
onZoomStart={handleMapInteraction}
onMoveEnd={(e) => {
setIsMapMoving(false);
mapMove(e);
}}
onClick={handleMapClick}
onContextMenu={handleContextMenu}
cursor={cursor === "default" ? undefined : "auto"}
mapStyle={mapLayer ? mapLayer : MAP_STYLES[activeLayer]}
minZoom={MIN_ZOOM}
maxZoom={MAX_ZOOM}
attributionControl={false}
style={{ width: "100%", height: "100%" }}
>
{children}
</Map>
</div>
);
}

View File

@@ -0,0 +1,36 @@
import { Avatar, AvatarFallback, AvatarImage } from "@/components/UI/avatar";
export default function GroupAvatar() {
return (
<div className="bg-card flex -space-x-[0.6rem] rounded-2xl rounded-bl-none p-3">
<Avatar className="ring-primary-200 ring-1">
<AvatarImage
alt="U1"
src="https://images.unsplash.com/photo-1543610892-0b1f7e6d8ac1?w=96&h=96&dpr=2&q=80"
/>
<AvatarFallback>U1</AvatarFallback>
</Avatar>
<Avatar className="ring-primary-200 ring-1">
<AvatarImage
alt="U2"
src="https://images.unsplash.com/photo-1628157588553-5eeea00af15c?w=96&h=96&dpr=2&q=80"
/>
<AvatarFallback>U2</AvatarFallback>
</Avatar>
<Avatar className="ring-primary-200 ring-1">
<AvatarImage
alt="U3"
src="https://images.unsplash.com/photo-1655874819398-c6dfbec68ac7?w=96&h=96&dpr=2&q=80"
/>
<AvatarFallback>U3</AvatarFallback>
</Avatar>
<Avatar className="ring-primary-200 ring-1">
<AvatarImage
alt="U3"
src="https://images.unsplash.com/photo-1655874819398-c6dfbec68ac7?w=96&h=96&dpr=2&q=80"
/>
<AvatarFallback>U3</AvatarFallback>
</Avatar>
</div>
);
}

View File

@@ -0,0 +1,40 @@
"use client";
import { Avatar as AvatarPrimitive } from "@base-ui/react/avatar";
import { cn } from "@/lib/utils";
function Avatar({ className, ...props }: AvatarPrimitive.Root.Props) {
return (
<AvatarPrimitive.Root
className={cn(
"bg-background inline-flex size-8 shrink-0 items-center justify-center overflow-hidden rounded-full align-middle text-xs font-medium select-none",
className
)}
data-slot="avatar"
{...props}
/>
);
}
function AvatarImage({ className, ...props }: AvatarPrimitive.Image.Props) {
return (
<AvatarPrimitive.Image
className={cn("size-full object-cover", className)}
data-slot="avatar-image"
{...props}
/>
);
}
function AvatarFallback({ className, ...props }: AvatarPrimitive.Fallback.Props) {
return (
<AvatarPrimitive.Fallback
className={cn("bg-muted flex size-full items-center justify-center rounded-full", className)}
data-slot="avatar-fallback"
{...props}
/>
);
}
export { Avatar, AvatarImage, AvatarFallback };

View File

@@ -0,0 +1,11 @@
import { create } from "zustand";
type MapStoreState = {
isMapMoving: boolean;
setIsMapMoving: (isMoving: boolean) => void;
};
export const useMapStore = create<MapStoreState>((set) => ({
isMapMoving: false,
setIsMapMoving: (isMapMoving) => set({ isMapMoving }),
}));

View File

@@ -152,6 +152,11 @@ body {
--color-text-secondary: #6b7280;
--color-text-muted: #9ca3af;
/* =====================
home page banner color
=========================*/
--color-home-page-banner: #ede9ff;
/* =====================
State colors
====================== */
@@ -189,6 +194,11 @@ body {
--color-primary-200: #9a5cff;
--color-primary-300: #7d34b9;
/* =====================
home page banner color
=========================*/
--color-home-page-banner: #1d1c1d;
--color-gray-50: #1f1f1f;
--color-gray-100: #262626;
--color-gray-200: #2a2a2a;