From 95a21b84ababd5bbc11d1a5d5f62e76e6144a23a Mon Sep 17 00:00:00 2001 From: Mahyar Khanbolooki Date: Fri, 8 Aug 2025 19:41:29 +0330 Subject: [PATCH] add: add address page --- package-lock.json | 57 ++++ package.json | 5 + public/marker-icon-2x.png | Bin 0 -> 4033 bytes public/marker-icon.png | Bin 0 -> 4033 bytes public/marker-shadow.png | Bin 0 -> 608 bytes public/sw.js | 2 +- .../(Profile)/profile/address/new/page.tsx | 267 +++++++++++++++ .../bottomsheet/AnimatedBottomSheet.tsx | 28 +- src/components/input/ActiveSearchbox.tsx | 69 ++++ src/components/input/InputField.tsx | 1 - src/components/input/SearchBox.tsx | 14 +- src/components/map/CustomMap.css | 269 +++++++++++++++ src/components/map/CustomMap.tsx | 305 ++++++++++++++++++ .../overlays/BlurredOverlayContainer.tsx | 5 +- src/components/ui/skeleton.tsx | 15 + 15 files changed, 1021 insertions(+), 16 deletions(-) create mode 100644 public/marker-icon-2x.png create mode 100644 public/marker-icon.png create mode 100644 public/marker-shadow.png create mode 100644 src/app/[name]/(Profile)/profile/address/new/page.tsx create mode 100644 src/components/input/ActiveSearchbox.tsx create mode 100644 src/components/map/CustomMap.css create mode 100644 src/components/map/CustomMap.tsx create mode 100644 src/components/ui/skeleton.tsx diff --git a/package-lock.json b/package-lock.json index d872d95..43e8f20 100644 --- a/package-lock.json +++ b/package-lock.json @@ -18,6 +18,8 @@ "@tanstack/react-query": "^5.81.5", "@tanstack/react-query-devtools": "^5.81.5", "@tanstack/react-table": "^8.21.3", + "@types/leaflet": "^1.9.20", + "@types/lodash": "^4.17.20", "axios": "^1.10.0", "class-variance-authority": "^0.7.1", "clsx": "^2.1.1", @@ -26,6 +28,8 @@ "i18next": "^25.3.2", "i18next-resources-to-backend": "^1.2.1", "iconsax-react": "^0.0.8", + "leaflet": "^1.9.4", + "lodash": "^4.17.21", "lucide-react": "^0.525.0", "next": "15.3.4", "next-i18n-router": "^5.5.3", @@ -35,6 +39,7 @@ "react-day-picker": "^9.8.0", "react-dom": "^19.0.0", "react-i18next": "^15.6.1", + "react-leaflet": "^5.0.0", "react-virtualized": "^9.22.6", "tailwind-merge": "^3.3.1", "use-long-press": "^3.3.0", @@ -2749,6 +2754,17 @@ "version": "1.1.1", "license": "MIT" }, + "node_modules/@react-leaflet/core": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/@react-leaflet/core/-/core-3.0.0.tgz", + "integrity": "sha512-3EWmekh4Nz+pGcr+xjf0KNyYfC3U2JjnkWsh0zcqaexYqmmB5ZhH37kz41JXGmKzpaMZCnPofBBm64i+YrEvGQ==", + "license": "Hippocratic-2.1", + "peerDependencies": { + "leaflet": "^1.9.0", + "react": "^19.0.0", + "react-dom": "^19.0.0" + } + }, "node_modules/@rollup/plugin-babel": { "version": "5.3.1", "resolved": "https://registry.npmjs.org/@rollup/plugin-babel/-/plugin-babel-5.3.1.tgz", @@ -3247,6 +3263,12 @@ "dev": true, "license": "MIT" }, + "node_modules/@types/geojson": { + "version": "7946.0.16", + "resolved": "https://registry.npmjs.org/@types/geojson/-/geojson-7946.0.16.tgz", + "integrity": "sha512-6C8nqWur3j98U6+lXDfTUWIfgvZU+EumvpHKcYjujKH7woYyLj2sUmff0tRhrqM7BohUw7Pz3ZB1jj2gW9Fvmg==", + "license": "MIT" + }, "node_modules/@types/glob": { "version": "7.2.0", "resolved": "https://registry.npmjs.org/@types/glob/-/glob-7.2.0.tgz", @@ -3266,6 +3288,21 @@ "dev": true, "license": "MIT" }, + "node_modules/@types/leaflet": { + "version": "1.9.20", + "resolved": "https://registry.npmjs.org/@types/leaflet/-/leaflet-1.9.20.tgz", + "integrity": "sha512-rooalPMlk61LCaLOvBF2VIf9M47HgMQqi5xQ9QRi7c8PkdIe0WrIi5IxXUXQjAdL0c+vcQ01mYWbthzmp9GHWw==", + "license": "MIT", + "dependencies": { + "@types/geojson": "*" + } + }, + "node_modules/@types/lodash": { + "version": "4.17.20", + "resolved": "https://registry.npmjs.org/@types/lodash/-/lodash-4.17.20.tgz", + "integrity": "sha512-H3MHACvFUEiujabxhaI/ImO6gUrd8oOurg7LQtS7mbwIXA/cUqWrvBsaeJ23aZEPk1TAYkurjfMbSELfoCXlGA==", + "license": "MIT" + }, "node_modules/@types/minimatch": { "version": "5.1.2", "resolved": "https://registry.npmjs.org/@types/minimatch/-/minimatch-5.1.2.tgz", @@ -6470,6 +6507,12 @@ "node": ">=0.10" } }, + "node_modules/leaflet": { + "version": "1.9.4", + "resolved": "https://registry.npmjs.org/leaflet/-/leaflet-1.9.4.tgz", + "integrity": "sha512-nxS1ynzJOmOlHp+iL3FyWqK89GtNL8U8rvlMOsQdTTssxZwCXh8N2NB3GDQOL+YR3XnWyZAxwQixURb+FA74PA==", + "license": "BSD-2-Clause" + }, "node_modules/leven": { "version": "3.1.0", "resolved": "https://registry.npmjs.org/leven/-/leven-3.1.0.tgz", @@ -7650,6 +7693,20 @@ "version": "16.13.1", "license": "MIT" }, + "node_modules/react-leaflet": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/react-leaflet/-/react-leaflet-5.0.0.tgz", + "integrity": "sha512-CWbTpr5vcHw5bt9i4zSlPEVQdTVcML390TjeDG0cK59z1ylexpqC6M1PJFjV8jD7CF+ACBFsLIDs6DRMoLEofw==", + "license": "Hippocratic-2.1", + "dependencies": { + "@react-leaflet/core": "^3.0.0" + }, + "peerDependencies": { + "leaflet": "^1.9.0", + "react": "^19.0.0", + "react-dom": "^19.0.0" + } + }, "node_modules/react-lifecycles-compat": { "version": "3.0.4", "license": "MIT" diff --git a/package.json b/package.json index 9987903..863c28e 100644 --- a/package.json +++ b/package.json @@ -19,6 +19,8 @@ "@tanstack/react-query": "^5.81.5", "@tanstack/react-query-devtools": "^5.81.5", "@tanstack/react-table": "^8.21.3", + "@types/leaflet": "^1.9.20", + "@types/lodash": "^4.17.20", "axios": "^1.10.0", "class-variance-authority": "^0.7.1", "clsx": "^2.1.1", @@ -27,6 +29,8 @@ "i18next": "^25.3.2", "i18next-resources-to-backend": "^1.2.1", "iconsax-react": "^0.0.8", + "leaflet": "^1.9.4", + "lodash": "^4.17.21", "lucide-react": "^0.525.0", "next": "15.3.4", "next-i18n-router": "^5.5.3", @@ -36,6 +40,7 @@ "react-day-picker": "^9.8.0", "react-dom": "^19.0.0", "react-i18next": "^15.6.1", + "react-leaflet": "^5.0.0", "react-virtualized": "^9.22.6", "tailwind-merge": "^3.3.1", "use-long-press": "^3.3.0", diff --git a/public/marker-icon-2x.png b/public/marker-icon-2x.png new file mode 100644 index 0000000000000000000000000000000000000000..0015b6495fa458ad39d51cb4b913430016f48d33 GIT binary patch literal 4033 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 uBWCvJ(a=new URL(a+".js",n).href,s[a]||new Promise(s=>{if("document"in self){const e=document.createElement("script");e.src=a,e.onload=s,document.head.appendChild(e)}else e=a,importScripts(a),s()}).then(()=>{let e=s[a];if(!e)throw new Error(`Module ${a} didn’t register its module`);return e}));self.define=(n,i)=>{const t=e||("document"in self?document.currentScript.src:"")||location.href;if(s[t])return;let c={};const r=e=>a(e,t),b={module:{uri:t},exports:c,require:r};s[t]=Promise.all(n.map(e=>b[e]||r(e))).then(e=>(i(...e),c))}}define(["./workbox-4754cb34"],function(e){"use strict";importScripts(),self.skipWaiting(),e.clientsClaim(),e.precacheAndRoute([{url:"/_next/app-build-manifest.json",revision:"7e00aaa3899b227449d9346185ac034d"},{url:"/_next/static/JvnZUjaWGe2jIbNqb1jqi/_buildManifest.js",revision:"67ca8384d76b1163a1ecf8412d9d6e1c"},{url:"/_next/static/JvnZUjaWGe2jIbNqb1jqi/_ssgManifest.js",revision:"b6652df95db52feb4daf4eca35380933"},{url:"/_next/static/chunks/1144-d2b9e811690fb439.js",revision:"JvnZUjaWGe2jIbNqb1jqi"},{url:"/_next/static/chunks/1684-244afa2c743dc3d2.js",revision:"JvnZUjaWGe2jIbNqb1jqi"},{url:"/_next/static/chunks/1858-f8fd5a6492fc3675.js",revision:"JvnZUjaWGe2jIbNqb1jqi"},{url:"/_next/static/chunks/2113.f3779f78993185a6.js",revision:"f3779f78993185a6"},{url:"/_next/static/chunks/2389-9c554bd3b6b3bd9d.js",revision:"JvnZUjaWGe2jIbNqb1jqi"},{url:"/_next/static/chunks/2467-7bfc1d8c24280b6a.js",revision:"JvnZUjaWGe2jIbNqb1jqi"},{url:"/_next/static/chunks/2617.b8837c5676152b99.js",revision:"b8837c5676152b99"},{url:"/_next/static/chunks/3342-ddc072ea7ff7c107.js",revision:"JvnZUjaWGe2jIbNqb1jqi"},{url:"/_next/static/chunks/3463-1f5380e3b3429c65.js",revision:"JvnZUjaWGe2jIbNqb1jqi"},{url:"/_next/static/chunks/3629-7c92b1c7fc1ee583.js",revision:"JvnZUjaWGe2jIbNqb1jqi"},{url:"/_next/static/chunks/3686.592e97b9e6cb0f55.js",revision:"592e97b9e6cb0f55"},{url:"/_next/static/chunks/4262-e5c6c6aba9cd27b2.js",revision:"JvnZUjaWGe2jIbNqb1jqi"},{url:"/_next/static/chunks/472.a3826d29d6854395.js",revision:"a3826d29d6854395"},{url:"/_next/static/chunks/4bd1b696-28d5bbbfa6b7370e.js",revision:"JvnZUjaWGe2jIbNqb1jqi"},{url:"/_next/static/chunks/5070.7a5b7c95a73e3852.js",revision:"7a5b7c95a73e3852"},{url:"/_next/static/chunks/5128-bb5a9311c38effee.js",revision:"JvnZUjaWGe2jIbNqb1jqi"},{url:"/_next/static/chunks/540-19989f3322bbfeb1.js",revision:"JvnZUjaWGe2jIbNqb1jqi"},{url:"/_next/static/chunks/5413-9383a795d8d9d90d.js",revision:"JvnZUjaWGe2jIbNqb1jqi"},{url:"/_next/static/chunks/5579.8d10efcb0bcc5a99.js",revision:"8d10efcb0bcc5a99"},{url:"/_next/static/chunks/5674-c8b40a0f1aee40ec.js",revision:"JvnZUjaWGe2jIbNqb1jqi"},{url:"/_next/static/chunks/655-32ce9af232fcaefb.js",revision:"JvnZUjaWGe2jIbNqb1jqi"},{url:"/_next/static/chunks/6664-c06dab9e3e2e0e88.js",revision:"JvnZUjaWGe2jIbNqb1jqi"},{url:"/_next/static/chunks/6766-d66d69f2e2c7dd02.js",revision:"JvnZUjaWGe2jIbNqb1jqi"},{url:"/_next/static/chunks/6874-ce1aa5bbecf0220a.js",revision:"JvnZUjaWGe2jIbNqb1jqi"},{url:"/_next/static/chunks/6914.4e4ece51feee1de1.js",revision:"4e4ece51feee1de1"},{url:"/_next/static/chunks/7493.6298379ce0758927.js",revision:"6298379ce0758927"},{url:"/_next/static/chunks/7566-de7244f58b1e1f9b.js",revision:"JvnZUjaWGe2jIbNqb1jqi"},{url:"/_next/static/chunks/7582-631f54ae11ed6d60.js",revision:"JvnZUjaWGe2jIbNqb1jqi"},{url:"/_next/static/chunks/8016.4099535a1f89c7a8.js",revision:"4099535a1f89c7a8"},{url:"/_next/static/chunks/8476-24e5f46d1ad5ec8e.js",revision:"JvnZUjaWGe2jIbNqb1jqi"},{url:"/_next/static/chunks/8722.6bd44e81e045a845.js",revision:"6bd44e81e045a845"},{url:"/_next/static/chunks/9161-7982fa2919957bea.js",revision:"JvnZUjaWGe2jIbNqb1jqi"},{url:"/_next/static/chunks/9341.3de5922501c09e3c.js",revision:"3de5922501c09e3c"},{url:"/_next/static/chunks/9713-02090b4576c82851.js",revision:"JvnZUjaWGe2jIbNqb1jqi"},{url:"/_next/static/chunks/9725-c2aa3c7c6f99c345.js",revision:"JvnZUjaWGe2jIbNqb1jqi"},{url:"/_next/static/chunks/app/%5Bname%5D/(Dialogs)/cart/page-162d74ac39b8092b.js",revision:"JvnZUjaWGe2jIbNqb1jqi"},{url:"/_next/static/chunks/app/%5Bname%5D/(Dialogs)/layout-fe30abd7853ccb4d.js",revision:"JvnZUjaWGe2jIbNqb1jqi"},{url:"/_next/static/chunks/app/%5Bname%5D/(Dialogs)/notifications/page-23fcbc1c9660cf6a.js",revision:"JvnZUjaWGe2jIbNqb1jqi"},{url:"/_next/static/chunks/app/%5Bname%5D/(Dialogs)/report/page-093a19b36fde3f48.js",revision:"JvnZUjaWGe2jIbNqb1jqi"},{url:"/_next/static/chunks/app/%5Bname%5D/(Main)/%5Bid%5D/page-ce0416781aa59d74.js",revision:"JvnZUjaWGe2jIbNqb1jqi"},{url:"/_next/static/chunks/app/%5Bname%5D/(Main)/about/layout-11840f22e655b293.js",revision:"JvnZUjaWGe2jIbNqb1jqi"},{url:"/_next/static/chunks/app/%5Bname%5D/(Main)/about/page-436b86f1e635b4e5.js",revision:"JvnZUjaWGe2jIbNqb1jqi"},{url:"/_next/static/chunks/app/%5Bname%5D/(Main)/chat/%5Bid%5D/page-3f1eddca453b31d0.js",revision:"JvnZUjaWGe2jIbNqb1jqi"},{url:"/_next/static/chunks/app/%5Bname%5D/(Main)/chat/nearby/page-b29b74fcd46d0d91.js",revision:"JvnZUjaWGe2jIbNqb1jqi"},{url:"/_next/static/chunks/app/%5Bname%5D/(Main)/chat/page-4cedcc710f52986a.js",revision:"JvnZUjaWGe2jIbNqb1jqi"},{url:"/_next/static/chunks/app/%5Bname%5D/(Main)/layout-925351825523b7c0.js",revision:"JvnZUjaWGe2jIbNqb1jqi"},{url:"/_next/static/chunks/app/%5Bname%5D/(Main)/loading-d4b3f0d283159f00.js",revision:"JvnZUjaWGe2jIbNqb1jqi"},{url:"/_next/static/chunks/app/%5Bname%5D/(Main)/orders/layout-60174a0bed9f646f.js",revision:"JvnZUjaWGe2jIbNqb1jqi"},{url:"/_next/static/chunks/app/%5Bname%5D/(Main)/orders/page-e67ec9a1191d9d16.js",revision:"JvnZUjaWGe2jIbNqb1jqi"},{url:"/_next/static/chunks/app/%5Bname%5D/(Main)/page-34f5ae73a3c1422a.js",revision:"JvnZUjaWGe2jIbNqb1jqi"},{url:"/_next/static/chunks/app/%5Bname%5D/(Main)/rating/%5BorderId%5D/page-ea01d9f3a228149d.js",revision:"JvnZUjaWGe2jIbNqb1jqi"},{url:"/_next/static/chunks/app/%5Bname%5D/(Main)/transactions/page-e652e9dee5334bf9.js",revision:"JvnZUjaWGe2jIbNqb1jqi"},{url:"/_next/static/chunks/app/%5Bname%5D/(Main)/transactions/review/page-054ea010c964654e.js",revision:"JvnZUjaWGe2jIbNqb1jqi"},{url:"/_next/static/chunks/app/%5Bname%5D/(Profile)/layout-5a4c86d4832d337f.js",revision:"JvnZUjaWGe2jIbNqb1jqi"},{url:"/_next/static/chunks/app/%5Bname%5D/(Profile)/profile/edit/page-061474d28271a93b.js",revision:"JvnZUjaWGe2jIbNqb1jqi"},{url:"/_next/static/chunks/app/%5Bname%5D/(Profile)/profile/page-5c106b3b64ccdedc.js",revision:"JvnZUjaWGe2jIbNqb1jqi"},{url:"/_next/static/chunks/app/_not-found/page-a47c98d6edc79259.js",revision:"JvnZUjaWGe2jIbNqb1jqi"},{url:"/_next/static/chunks/app/auth/layout-e0479735512ce592.js",revision:"JvnZUjaWGe2jIbNqb1jqi"},{url:"/_next/static/chunks/app/auth/page-0c68c4e761692e93.js",revision:"JvnZUjaWGe2jIbNqb1jqi"},{url:"/_next/static/chunks/app/layout-9d13fdca13c55b6e.js",revision:"JvnZUjaWGe2jIbNqb1jqi"},{url:"/_next/static/chunks/app/loading-4475343093f4ed9c.js",revision:"JvnZUjaWGe2jIbNqb1jqi"},{url:"/_next/static/chunks/app/page-d69176d0b6b95de4.js",revision:"JvnZUjaWGe2jIbNqb1jqi"},{url:"/_next/static/chunks/framework-fda66049c8bbcca5.js",revision:"JvnZUjaWGe2jIbNqb1jqi"},{url:"/_next/static/chunks/main-592ffda5d1d5a75d.js",revision:"JvnZUjaWGe2jIbNqb1jqi"},{url:"/_next/static/chunks/main-app-4afdf4040eb15622.js",revision:"JvnZUjaWGe2jIbNqb1jqi"},{url:"/_next/static/chunks/pages/_app-5eef2bc2d7659273.js",revision:"JvnZUjaWGe2jIbNqb1jqi"},{url:"/_next/static/chunks/pages/_error-7c851e5682ba33fe.js",revision:"JvnZUjaWGe2jIbNqb1jqi"},{url:"/_next/static/chunks/polyfills-42372ed130431b0a.js",revision:"846118c33b2c0e922d7b3a7676f81f6f"},{url:"/_next/static/chunks/webpack-6de9709fb9624af1.js",revision:"JvnZUjaWGe2jIbNqb1jqi"},{url:"/_next/static/css/eda769f505711181.css",revision:"eda769f505711181"},{url:"/apple-icon.png",revision:"0c11163d9876662afd598cdb681bb2e4"},{url:"/assets/css/fonts.css",revision:"767635567236928958c4fe946b9ddcfd"},{url:"/assets/fonts/Irancell_Bold.ttf",revision:"e5160b0c919e044bfbcf18a58dd04ad2"},{url:"/assets/fonts/Irancell_Bold.woff",revision:"ed06d53485fd8044e79894bedc597104"},{url:"/assets/fonts/Irancell_Bold.woff2",revision:"04e0058cbeb4dfe083dbf98370ae885e"},{url:"/assets/fonts/Irancell_ExtraBold.ttf",revision:"aca587f4f429c26e887e102f4d46bd27"},{url:"/assets/fonts/Irancell_ExtraBold.woff",revision:"eb70794e10fd476987930ef523ed8a33"},{url:"/assets/fonts/Irancell_ExtraBold.woff2",revision:"5f66486a89926925c4eabcb76aea6ca1"},{url:"/assets/fonts/Irancell_ExtraLight.ttf",revision:"85dd3b644afbabbea860e70d91adb665"},{url:"/assets/fonts/Irancell_ExtraLight.woff",revision:"7491ed7a70e2b9f8fa19c20eac497c12"},{url:"/assets/fonts/Irancell_ExtraLight.woff2",revision:"807b703a3924972991de43d76764f404"},{url:"/assets/fonts/Irancell_Light.ttf",revision:"911a07e375667ba6feeca9a527bc537d"},{url:"/assets/fonts/Irancell_Light.woff",revision:"9c1e83ee95bb25d5adc97011bbab2ae5"},{url:"/assets/fonts/Irancell_Light.woff2",revision:"34404e8d01d1ee40a5bc00311ae2add4"},{url:"/assets/fonts/Irancell_Medium.ttf",revision:"2b8b7463718f1930afc053208efe986e"},{url:"/assets/fonts/Irancell_Medium.woff",revision:"e952c2a8c784e1b13c606f4b9b300310"},{url:"/assets/fonts/Irancell_Medium.woff2",revision:"1955af7b1d4244b4034b793c4435a7e3"},{url:"/assets/fonts/Irancell_Regular.ttf",revision:"3c29a31eaec7ddd32d41879dfcce95b8"},{url:"/assets/fonts/Irancell_Regular.woff",revision:"c9d7d00bddfd0595b68e750574b28312"},{url:"/assets/fonts/Irancell_Regular.woff2",revision:"89eb5fb2d1409229407d1a75426acda0"},{url:"/assets/images/danak-logo.png",revision:"bfbbcd927b3e359c7ab8047211f7c3a3"},{url:"/assets/images/food-image.png",revision:"34520f66d5224a61eca7e83646bdb31e"},{url:"/assets/images/food-preview-hq.png",revision:"42968e21a3044f80db7399459768a16c"},{url:"/assets/images/food-preview.png",revision:"bfc910395c189bfc145fa950dbcca749"},{url:"/assets/images/login-banner.png",revision:"29a467b532dbf3c6e8bcd45e08ea3cde"},{url:"/assets/images/report-hero.svg",revision:"199d4622a1abea7a0d83e6b5df0caf89"},{url:"/assets/images/user-avatar.png",revision:"0d4b4612ad00442263d2a41754fcadcc"},{url:"/favicon.ico",revision:"28202080f3527f526a24b1119885536e"},{url:"/file.svg",revision:"d09f95206c3fa0bb9bd9fefabfd0ea71"},{url:"/globe.svg",revision:"2aaafa6a49b6563925fe440891e32717"},{url:"/icon0.svg",revision:"6d2cbb302b9f3ef7f2b43ae7d355f6c9"},{url:"/icon1.png",revision:"14e00a23129ccbbe02a835997ecca6c0"},{url:"/icons/web-app-manifest-192x192.png",revision:"2bd31374f516557201d060ad4c5695c4"},{url:"/icons/web-app-manifest-512x512.png",revision:"d0f9aa93e12fc2ed5b220efa3db4e689"},{url:"/manifest.json",revision:"9498454e8c03e70a4ed7fa0472e339ba"},{url:"/next.svg",revision:"8e061864f388b47f33a1c3780831193e"},{url:"/vercel.svg",revision:"c0af2f507b369b085b35ef4bbe3bcf1e"},{url:"/window.svg",revision:"a2760511c65806022ad20adf74370ff3"}],{ignoreURLParametersMatching:[]}),e.cleanupOutdatedCaches(),e.registerRoute("/",new e.NetworkFirst({cacheName:"start-url",plugins:[{cacheWillUpdate:async({request:e,response:s,event:a,state:n})=>s&&"opaqueredirect"===s.type?new Response(s.body,{status:200,statusText:"OK",headers:s.headers}):s}]}),"GET"),e.registerRoute(/^https:\/\/fonts\.(?:gstatic)\.com\/.*/i,new e.CacheFirst({cacheName:"google-fonts-webfonts",plugins:[new e.ExpirationPlugin({maxEntries:4,maxAgeSeconds:31536e3})]}),"GET"),e.registerRoute(/^https:\/\/fonts\.(?:googleapis)\.com\/.*/i,new e.StaleWhileRevalidate({cacheName:"google-fonts-stylesheets",plugins:[new e.ExpirationPlugin({maxEntries:4,maxAgeSeconds:604800})]}),"GET"),e.registerRoute(/\.(?:eot|otf|ttc|ttf|woff|woff2|font.css)$/i,new e.StaleWhileRevalidate({cacheName:"static-font-assets",plugins:[new e.ExpirationPlugin({maxEntries:4,maxAgeSeconds:604800})]}),"GET"),e.registerRoute(/\.(?:jpg|jpeg|gif|png|svg|ico|webp)$/i,new e.StaleWhileRevalidate({cacheName:"static-image-assets",plugins:[new e.ExpirationPlugin({maxEntries:64,maxAgeSeconds:86400})]}),"GET"),e.registerRoute(/\/_next\/image\?url=.+$/i,new e.StaleWhileRevalidate({cacheName:"next-image",plugins:[new e.ExpirationPlugin({maxEntries:64,maxAgeSeconds:86400})]}),"GET"),e.registerRoute(/\.(?:mp3|wav|ogg)$/i,new e.CacheFirst({cacheName:"static-audio-assets",plugins:[new e.RangeRequestsPlugin,new e.ExpirationPlugin({maxEntries:32,maxAgeSeconds:86400})]}),"GET"),e.registerRoute(/\.(?:mp4)$/i,new e.CacheFirst({cacheName:"static-video-assets",plugins:[new e.RangeRequestsPlugin,new e.ExpirationPlugin({maxEntries:32,maxAgeSeconds:86400})]}),"GET"),e.registerRoute(/\.(?:js)$/i,new e.StaleWhileRevalidate({cacheName:"static-js-assets",plugins:[new e.ExpirationPlugin({maxEntries:32,maxAgeSeconds:86400})]}),"GET"),e.registerRoute(/\.(?:css|less)$/i,new e.StaleWhileRevalidate({cacheName:"static-style-assets",plugins:[new e.ExpirationPlugin({maxEntries:32,maxAgeSeconds:86400})]}),"GET"),e.registerRoute(/\/_next\/data\/.+\/.+\.json$/i,new e.StaleWhileRevalidate({cacheName:"next-data",plugins:[new e.ExpirationPlugin({maxEntries:32,maxAgeSeconds:86400})]}),"GET"),e.registerRoute(/\.(?:json|xml|csv)$/i,new e.NetworkFirst({cacheName:"static-data-assets",plugins:[new e.ExpirationPlugin({maxEntries:32,maxAgeSeconds:86400})]}),"GET"),e.registerRoute(({url:e})=>{if(!(self.origin===e.origin))return!1;const s=e.pathname;return!s.startsWith("/api/auth/")&&!!s.startsWith("/api/")},new e.NetworkFirst({cacheName:"apis",networkTimeoutSeconds:10,plugins:[new e.ExpirationPlugin({maxEntries:16,maxAgeSeconds:86400})]}),"GET"),e.registerRoute(({url:e})=>{if(!(self.origin===e.origin))return!1;return!e.pathname.startsWith("/api/")},new e.NetworkFirst({cacheName:"others",networkTimeoutSeconds:10,plugins:[new e.ExpirationPlugin({maxEntries:32,maxAgeSeconds:86400})]}),"GET"),e.registerRoute(({url:e})=>!(self.origin===e.origin),new e.NetworkFirst({cacheName:"cross-origin",networkTimeoutSeconds:10,plugins:[new e.ExpirationPlugin({maxEntries:32,maxAgeSeconds:3600})]}),"GET")}); +if(!self.define){let e,s={};const a=(a,n)=>(a=new URL(a+".js",n).href,s[a]||new Promise(s=>{if("document"in self){const e=document.createElement("script");e.src=a,e.onload=s,document.head.appendChild(e)}else e=a,importScripts(a),s()}).then(()=>{let e=s[a];if(!e)throw new Error(`Module ${a} didn’t register its module`);return e}));self.define=(n,t)=>{const i=e||("document"in self?document.currentScript.src:"")||location.href;if(s[i])return;let c={};const r=e=>a(e,i),d={module:{uri:i},exports:c,require:r};s[i]=Promise.all(n.map(e=>d[e]||r(e))).then(e=>(t(...e),c))}}define(["./workbox-4754cb34"],function(e){"use strict";importScripts(),self.skipWaiting(),e.clientsClaim(),e.precacheAndRoute([{url:"/_next/app-build-manifest.json",revision:"b5321e205a1a4073b70fe1a02e098012"},{url:"/_next/static/MDjUdUv1dkMz5Dz4Vwtfu/_buildManifest.js",revision:"67ca8384d76b1163a1ecf8412d9d6e1c"},{url:"/_next/static/MDjUdUv1dkMz5Dz4Vwtfu/_ssgManifest.js",revision:"b6652df95db52feb4daf4eca35380933"},{url:"/_next/static/chunks/1144-171ce621aabae165.js",revision:"MDjUdUv1dkMz5Dz4Vwtfu"},{url:"/_next/static/chunks/1684-ffcaef4aa0c3922b.js",revision:"MDjUdUv1dkMz5Dz4Vwtfu"},{url:"/_next/static/chunks/2113.f3779f78993185a6.js",revision:"f3779f78993185a6"},{url:"/_next/static/chunks/2389-0657d56665b1f5b8.js",revision:"MDjUdUv1dkMz5Dz4Vwtfu"},{url:"/_next/static/chunks/2467-c6ef6bfe1ee94b6b.js",revision:"MDjUdUv1dkMz5Dz4Vwtfu"},{url:"/_next/static/chunks/2617.b8837c5676152b99.js",revision:"b8837c5676152b99"},{url:"/_next/static/chunks/3342-24c32b3c4de17f46.js",revision:"MDjUdUv1dkMz5Dz4Vwtfu"},{url:"/_next/static/chunks/3463-2cd6c2977b4d5bee.js",revision:"MDjUdUv1dkMz5Dz4Vwtfu"},{url:"/_next/static/chunks/3629-d4af4d56bc54c929.js",revision:"MDjUdUv1dkMz5Dz4Vwtfu"},{url:"/_next/static/chunks/3686.f3530ed9b494a670.js",revision:"f3530ed9b494a670"},{url:"/_next/static/chunks/4262-2d530de20fa4ecfb.js",revision:"MDjUdUv1dkMz5Dz4Vwtfu"},{url:"/_next/static/chunks/472.a3826d29d6854395.js",revision:"a3826d29d6854395"},{url:"/_next/static/chunks/4bd1b696-a38a8be7401e6000.js",revision:"MDjUdUv1dkMz5Dz4Vwtfu"},{url:"/_next/static/chunks/5070.7a5b7c95a73e3852.js",revision:"7a5b7c95a73e3852"},{url:"/_next/static/chunks/5128-333af6c0f78fbaa2.js",revision:"MDjUdUv1dkMz5Dz4Vwtfu"},{url:"/_next/static/chunks/5413-d5f4b1b8a5ad2f7e.js",revision:"MDjUdUv1dkMz5Dz4Vwtfu"},{url:"/_next/static/chunks/5579.8d10efcb0bcc5a99.js",revision:"8d10efcb0bcc5a99"},{url:"/_next/static/chunks/5872-aae329783ae5a534.js",revision:"MDjUdUv1dkMz5Dz4Vwtfu"},{url:"/_next/static/chunks/6291-759ca2f746557bca.js",revision:"MDjUdUv1dkMz5Dz4Vwtfu"},{url:"/_next/static/chunks/6418-f901248c89d581c2.js",revision:"MDjUdUv1dkMz5Dz4Vwtfu"},{url:"/_next/static/chunks/655-32ce9af232fcaefb.js",revision:"MDjUdUv1dkMz5Dz4Vwtfu"},{url:"/_next/static/chunks/6766-1e387420e29e20bf.js",revision:"MDjUdUv1dkMz5Dz4Vwtfu"},{url:"/_next/static/chunks/6874-63e751b081f8e305.js",revision:"MDjUdUv1dkMz5Dz4Vwtfu"},{url:"/_next/static/chunks/6914.4e4ece51feee1de1.js",revision:"4e4ece51feee1de1"},{url:"/_next/static/chunks/6919-e4ab9adddd8b4faa.js",revision:"MDjUdUv1dkMz5Dz4Vwtfu"},{url:"/_next/static/chunks/6940.deea722dce835a26.js",revision:"deea722dce835a26"},{url:"/_next/static/chunks/7493.6298379ce0758927.js",revision:"6298379ce0758927"},{url:"/_next/static/chunks/7820-f97c5220c0687f86.js",revision:"MDjUdUv1dkMz5Dz4Vwtfu"},{url:"/_next/static/chunks/8016.4099535a1f89c7a8.js",revision:"4099535a1f89c7a8"},{url:"/_next/static/chunks/8021-c4457d3d52511f40.js",revision:"MDjUdUv1dkMz5Dz4Vwtfu"},{url:"/_next/static/chunks/8476-37b457958b167476.js",revision:"MDjUdUv1dkMz5Dz4Vwtfu"},{url:"/_next/static/chunks/8722.6bd44e81e045a845.js",revision:"6bd44e81e045a845"},{url:"/_next/static/chunks/8736-147b18bf8e873d61.js",revision:"MDjUdUv1dkMz5Dz4Vwtfu"},{url:"/_next/static/chunks/8777-06b19ddc57185f22.js",revision:"MDjUdUv1dkMz5Dz4Vwtfu"},{url:"/_next/static/chunks/8905-30fda9ceec41fef5.js",revision:"MDjUdUv1dkMz5Dz4Vwtfu"},{url:"/_next/static/chunks/9341.3de5922501c09e3c.js",revision:"3de5922501c09e3c"},{url:"/_next/static/chunks/9725-a8d96124b45b9886.js",revision:"MDjUdUv1dkMz5Dz4Vwtfu"},{url:"/_next/static/chunks/app/%5Bname%5D/(Dialogs)/cart/page-91e477f2d9c0fabd.js",revision:"MDjUdUv1dkMz5Dz4Vwtfu"},{url:"/_next/static/chunks/app/%5Bname%5D/(Dialogs)/layout-1ce0755cb235daf5.js",revision:"MDjUdUv1dkMz5Dz4Vwtfu"},{url:"/_next/static/chunks/app/%5Bname%5D/(Dialogs)/notifications/page-0fc0460353da360c.js",revision:"MDjUdUv1dkMz5Dz4Vwtfu"},{url:"/_next/static/chunks/app/%5Bname%5D/(Dialogs)/order/checkout/%5Bid%5D/page-73e05b9f4450b21b.js",revision:"MDjUdUv1dkMz5Dz4Vwtfu"},{url:"/_next/static/chunks/app/%5Bname%5D/(Dialogs)/order/track/%5Bid%5D/page-227ba64e4e4d0256.js",revision:"MDjUdUv1dkMz5Dz4Vwtfu"},{url:"/_next/static/chunks/app/%5Bname%5D/(Dialogs)/report/page-fcb6febb0cc1fd2f.js",revision:"MDjUdUv1dkMz5Dz4Vwtfu"},{url:"/_next/static/chunks/app/%5Bname%5D/(Main)/%5Bid%5D/page-5cdef79567eea6a3.js",revision:"MDjUdUv1dkMz5Dz4Vwtfu"},{url:"/_next/static/chunks/app/%5Bname%5D/(Main)/about/layout-1529ef92b6e5dceb.js",revision:"MDjUdUv1dkMz5Dz4Vwtfu"},{url:"/_next/static/chunks/app/%5Bname%5D/(Main)/about/page-325905a3e90de8a1.js",revision:"MDjUdUv1dkMz5Dz4Vwtfu"},{url:"/_next/static/chunks/app/%5Bname%5D/(Main)/chat/%5Bid%5D/page-73c0e81dc0846d10.js",revision:"MDjUdUv1dkMz5Dz4Vwtfu"},{url:"/_next/static/chunks/app/%5Bname%5D/(Main)/chat/nearby/page-844c1c72b30779fa.js",revision:"MDjUdUv1dkMz5Dz4Vwtfu"},{url:"/_next/static/chunks/app/%5Bname%5D/(Main)/chat/page-204e8444df7e3124.js",revision:"MDjUdUv1dkMz5Dz4Vwtfu"},{url:"/_next/static/chunks/app/%5Bname%5D/(Main)/layout-ddb241374fc9f179.js",revision:"MDjUdUv1dkMz5Dz4Vwtfu"},{url:"/_next/static/chunks/app/%5Bname%5D/(Main)/loading-ad8b652b8b0caa93.js",revision:"MDjUdUv1dkMz5Dz4Vwtfu"},{url:"/_next/static/chunks/app/%5Bname%5D/(Main)/order/history/layout-0527334368f23614.js",revision:"MDjUdUv1dkMz5Dz4Vwtfu"},{url:"/_next/static/chunks/app/%5Bname%5D/(Main)/order/history/page-1631e9e33fc097ce.js",revision:"MDjUdUv1dkMz5Dz4Vwtfu"},{url:"/_next/static/chunks/app/%5Bname%5D/(Main)/page-7187465185590912.js",revision:"MDjUdUv1dkMz5Dz4Vwtfu"},{url:"/_next/static/chunks/app/%5Bname%5D/(Main)/rating/%5BorderId%5D/page-20737d9c0970ff91.js",revision:"MDjUdUv1dkMz5Dz4Vwtfu"},{url:"/_next/static/chunks/app/%5Bname%5D/(Main)/transactions/page-f244c71a1f3a708a.js",revision:"MDjUdUv1dkMz5Dz4Vwtfu"},{url:"/_next/static/chunks/app/%5Bname%5D/(Main)/transactions/review/page-8af4483434ff266a.js",revision:"MDjUdUv1dkMz5Dz4Vwtfu"},{url:"/_next/static/chunks/app/%5Bname%5D/(Profile)/layout-1933e0236712effd.js",revision:"MDjUdUv1dkMz5Dz4Vwtfu"},{url:"/_next/static/chunks/app/%5Bname%5D/(Profile)/profile/edit/page-18a241985dc0c61e.js",revision:"MDjUdUv1dkMz5Dz4Vwtfu"},{url:"/_next/static/chunks/app/%5Bname%5D/(Profile)/profile/page-3a6dc97788719fd5.js",revision:"MDjUdUv1dkMz5Dz4Vwtfu"},{url:"/_next/static/chunks/app/_not-found/page-19683a9b97b3bf13.js",revision:"MDjUdUv1dkMz5Dz4Vwtfu"},{url:"/_next/static/chunks/app/auth/layout-e652eddbf45deb13.js",revision:"MDjUdUv1dkMz5Dz4Vwtfu"},{url:"/_next/static/chunks/app/auth/page-56a5d2de789e4285.js",revision:"MDjUdUv1dkMz5Dz4Vwtfu"},{url:"/_next/static/chunks/app/layout-f5975388d871325b.js",revision:"MDjUdUv1dkMz5Dz4Vwtfu"},{url:"/_next/static/chunks/app/loading-f60914b06206fe55.js",revision:"MDjUdUv1dkMz5Dz4Vwtfu"},{url:"/_next/static/chunks/app/page-af1c7c046ff85a11.js",revision:"MDjUdUv1dkMz5Dz4Vwtfu"},{url:"/_next/static/chunks/d0deef33.aff3d46b768f00e5.js",revision:"aff3d46b768f00e5"},{url:"/_next/static/chunks/framework-fda66049c8bbcca5.js",revision:"MDjUdUv1dkMz5Dz4Vwtfu"},{url:"/_next/static/chunks/main-592ffda5d1d5a75d.js",revision:"MDjUdUv1dkMz5Dz4Vwtfu"},{url:"/_next/static/chunks/main-app-4afdf4040eb15622.js",revision:"MDjUdUv1dkMz5Dz4Vwtfu"},{url:"/_next/static/chunks/pages/_app-5eef2bc2d7659273.js",revision:"MDjUdUv1dkMz5Dz4Vwtfu"},{url:"/_next/static/chunks/pages/_error-7c851e5682ba33fe.js",revision:"MDjUdUv1dkMz5Dz4Vwtfu"},{url:"/_next/static/chunks/polyfills-42372ed130431b0a.js",revision:"846118c33b2c0e922d7b3a7676f81f6f"},{url:"/_next/static/chunks/webpack-da93b9fc426f8242.js",revision:"MDjUdUv1dkMz5Dz4Vwtfu"},{url:"/_next/static/css/00bae1e936d73e11.css",revision:"00bae1e936d73e11"},{url:"/_next/static/css/85ff3e2932617f14.css",revision:"85ff3e2932617f14"},{url:"/_next/static/media/layers-2x.9859cd12.png",revision:"9859cd12"},{url:"/_next/static/media/layers.ef6db872.png",revision:"ef6db872"},{url:"/_next/static/media/marker-icon.d577052a.png",revision:"d577052a"},{url:"/apple-icon.png",revision:"0c11163d9876662afd598cdb681bb2e4"},{url:"/assets/css/fonts.css",revision:"767635567236928958c4fe946b9ddcfd"},{url:"/assets/fonts/Irancell_Bold.ttf",revision:"e5160b0c919e044bfbcf18a58dd04ad2"},{url:"/assets/fonts/Irancell_Bold.woff",revision:"ed06d53485fd8044e79894bedc597104"},{url:"/assets/fonts/Irancell_Bold.woff2",revision:"04e0058cbeb4dfe083dbf98370ae885e"},{url:"/assets/fonts/Irancell_ExtraBold.ttf",revision:"aca587f4f429c26e887e102f4d46bd27"},{url:"/assets/fonts/Irancell_ExtraBold.woff",revision:"eb70794e10fd476987930ef523ed8a33"},{url:"/assets/fonts/Irancell_ExtraBold.woff2",revision:"5f66486a89926925c4eabcb76aea6ca1"},{url:"/assets/fonts/Irancell_ExtraLight.ttf",revision:"85dd3b644afbabbea860e70d91adb665"},{url:"/assets/fonts/Irancell_ExtraLight.woff",revision:"7491ed7a70e2b9f8fa19c20eac497c12"},{url:"/assets/fonts/Irancell_ExtraLight.woff2",revision:"807b703a3924972991de43d76764f404"},{url:"/assets/fonts/Irancell_Light.ttf",revision:"911a07e375667ba6feeca9a527bc537d"},{url:"/assets/fonts/Irancell_Light.woff",revision:"9c1e83ee95bb25d5adc97011bbab2ae5"},{url:"/assets/fonts/Irancell_Light.woff2",revision:"34404e8d01d1ee40a5bc00311ae2add4"},{url:"/assets/fonts/Irancell_Medium.ttf",revision:"2b8b7463718f1930afc053208efe986e"},{url:"/assets/fonts/Irancell_Medium.woff",revision:"e952c2a8c784e1b13c606f4b9b300310"},{url:"/assets/fonts/Irancell_Medium.woff2",revision:"1955af7b1d4244b4034b793c4435a7e3"},{url:"/assets/fonts/Irancell_Regular.ttf",revision:"3c29a31eaec7ddd32d41879dfcce95b8"},{url:"/assets/fonts/Irancell_Regular.woff",revision:"c9d7d00bddfd0595b68e750574b28312"},{url:"/assets/fonts/Irancell_Regular.woff2",revision:"89eb5fb2d1409229407d1a75426acda0"},{url:"/assets/images/danak-logo.png",revision:"bfbbcd927b3e359c7ab8047211f7c3a3"},{url:"/assets/images/food-image.png",revision:"34520f66d5224a61eca7e83646bdb31e"},{url:"/assets/images/food-preview-hq.png",revision:"42968e21a3044f80db7399459768a16c"},{url:"/assets/images/food-preview.png",revision:"bfc910395c189bfc145fa950dbcca749"},{url:"/assets/images/login-banner.png",revision:"29a467b532dbf3c6e8bcd45e08ea3cde"},{url:"/assets/images/report-hero.svg",revision:"199d4622a1abea7a0d83e6b5df0caf89"},{url:"/assets/images/user-avatar.png",revision:"0d4b4612ad00442263d2a41754fcadcc"},{url:"/favicon.ico",revision:"28202080f3527f526a24b1119885536e"},{url:"/file.svg",revision:"d09f95206c3fa0bb9bd9fefabfd0ea71"},{url:"/globe.svg",revision:"2aaafa6a49b6563925fe440891e32717"},{url:"/icon0.svg",revision:"6d2cbb302b9f3ef7f2b43ae7d355f6c9"},{url:"/icon1.png",revision:"14e00a23129ccbbe02a835997ecca6c0"},{url:"/icons/web-app-manifest-192x192.png",revision:"2bd31374f516557201d060ad4c5695c4"},{url:"/icons/web-app-manifest-512x512.png",revision:"d0f9aa93e12fc2ed5b220efa3db4e689"},{url:"/manifest.json",revision:"9498454e8c03e70a4ed7fa0472e339ba"},{url:"/marker-icon-2x.png",revision:"1c824216f354218b04b25a57e0f7ab1f"},{url:"/marker-icon.png",revision:"1c824216f354218b04b25a57e0f7ab1f"},{url:"/marker-shadow.png",revision:"44014eaa598e7050354603adfb44f812"},{url:"/next.svg",revision:"8e061864f388b47f33a1c3780831193e"},{url:"/vercel.svg",revision:"c0af2f507b369b085b35ef4bbe3bcf1e"},{url:"/window.svg",revision:"a2760511c65806022ad20adf74370ff3"}],{ignoreURLParametersMatching:[]}),e.cleanupOutdatedCaches(),e.registerRoute("/",new e.NetworkFirst({cacheName:"start-url",plugins:[{cacheWillUpdate:async({request:e,response:s,event:a,state:n})=>s&&"opaqueredirect"===s.type?new Response(s.body,{status:200,statusText:"OK",headers:s.headers}):s}]}),"GET"),e.registerRoute(/^https:\/\/fonts\.(?:gstatic)\.com\/.*/i,new e.CacheFirst({cacheName:"google-fonts-webfonts",plugins:[new e.ExpirationPlugin({maxEntries:4,maxAgeSeconds:31536e3})]}),"GET"),e.registerRoute(/^https:\/\/fonts\.(?:googleapis)\.com\/.*/i,new e.StaleWhileRevalidate({cacheName:"google-fonts-stylesheets",plugins:[new e.ExpirationPlugin({maxEntries:4,maxAgeSeconds:604800})]}),"GET"),e.registerRoute(/\.(?:eot|otf|ttc|ttf|woff|woff2|font.css)$/i,new e.StaleWhileRevalidate({cacheName:"static-font-assets",plugins:[new e.ExpirationPlugin({maxEntries:4,maxAgeSeconds:604800})]}),"GET"),e.registerRoute(/\.(?:jpg|jpeg|gif|png|svg|ico|webp)$/i,new e.StaleWhileRevalidate({cacheName:"static-image-assets",plugins:[new e.ExpirationPlugin({maxEntries:64,maxAgeSeconds:86400})]}),"GET"),e.registerRoute(/\/_next\/image\?url=.+$/i,new e.StaleWhileRevalidate({cacheName:"next-image",plugins:[new e.ExpirationPlugin({maxEntries:64,maxAgeSeconds:86400})]}),"GET"),e.registerRoute(/\.(?:mp3|wav|ogg)$/i,new e.CacheFirst({cacheName:"static-audio-assets",plugins:[new e.RangeRequestsPlugin,new e.ExpirationPlugin({maxEntries:32,maxAgeSeconds:86400})]}),"GET"),e.registerRoute(/\.(?:mp4)$/i,new e.CacheFirst({cacheName:"static-video-assets",plugins:[new e.RangeRequestsPlugin,new e.ExpirationPlugin({maxEntries:32,maxAgeSeconds:86400})]}),"GET"),e.registerRoute(/\.(?:js)$/i,new e.StaleWhileRevalidate({cacheName:"static-js-assets",plugins:[new e.ExpirationPlugin({maxEntries:32,maxAgeSeconds:86400})]}),"GET"),e.registerRoute(/\.(?:css|less)$/i,new e.StaleWhileRevalidate({cacheName:"static-style-assets",plugins:[new e.ExpirationPlugin({maxEntries:32,maxAgeSeconds:86400})]}),"GET"),e.registerRoute(/\/_next\/data\/.+\/.+\.json$/i,new e.StaleWhileRevalidate({cacheName:"next-data",plugins:[new e.ExpirationPlugin({maxEntries:32,maxAgeSeconds:86400})]}),"GET"),e.registerRoute(/\.(?:json|xml|csv)$/i,new e.NetworkFirst({cacheName:"static-data-assets",plugins:[new e.ExpirationPlugin({maxEntries:32,maxAgeSeconds:86400})]}),"GET"),e.registerRoute(({url:e})=>{if(!(self.origin===e.origin))return!1;const s=e.pathname;return!s.startsWith("/api/auth/")&&!!s.startsWith("/api/")},new e.NetworkFirst({cacheName:"apis",networkTimeoutSeconds:10,plugins:[new e.ExpirationPlugin({maxEntries:16,maxAgeSeconds:86400})]}),"GET"),e.registerRoute(({url:e})=>{if(!(self.origin===e.origin))return!1;return!e.pathname.startsWith("/api/")},new e.NetworkFirst({cacheName:"others",networkTimeoutSeconds:10,plugins:[new e.ExpirationPlugin({maxEntries:32,maxAgeSeconds:86400})]}),"GET"),e.registerRoute(({url:e})=>!(self.origin===e.origin),new e.NetworkFirst({cacheName:"cross-origin",networkTimeoutSeconds:10,plugins:[new e.ExpirationPlugin({maxEntries:32,maxAgeSeconds:3600})]}),"GET")}); diff --git a/src/app/[name]/(Profile)/profile/address/new/page.tsx b/src/app/[name]/(Profile)/profile/address/new/page.tsx new file mode 100644 index 0000000..c60b88c --- /dev/null +++ b/src/app/[name]/(Profile)/profile/address/new/page.tsx @@ -0,0 +1,267 @@ +'use client'; + +import { useSearchParams } from 'next/navigation'; +import React, { useState, useEffect } from 'react'; +import type { Icon } from 'leaflet'; +import dynamic from 'next/dynamic'; +import AnimatedBottomSheet from '@/components/bottomsheet/AnimatedBottomSheet'; +import Button from '@/components/button/PrimaryButton'; +import InputField from '@/components/input/InputField'; +import { Skeleton } from '@/components/ui/skeleton'; + +const CustomMap = dynamic( + () => import('@/components/map/CustomMap'), + { ssr: false } // Leaflet requires browser APIs +); + +export interface MarkerData { + position: [number, number]; + title: string; + icon?: Icon; +} + +export interface NominatimReverseGeocodingResponse { + place_id: number + licence: string + osm_type: string + osm_id: number + lat: string + lon: string + class: string + type: string + place_rank: number + importance: number + addresstype: string + name: string + display_name: string + address: NominatimReverseGeocodingResponseAddress + boundingbox: string[] +} + +export interface NominatimReverseGeocodingResponseAddress { + road: string + neighbourhood: string + suburb: string + city: string + district: string + county: string + province: string + "ISO3166-2-lvl4": string + postcode: string + country: string + state: string + country_code: string +} + +function OrderTrackingPage() { + const params = useSearchParams(); + const [selectedPosition, setSelectedPosition] = useState<[number, number] | null>(null); + const [selectedAddress, setSelectedAddress] = useState(null); + const [markers, setMarkers] = useState([]); + // eslint-disable-next-line @typescript-eslint/no-unused-vars + const [mapCenter, setMapCenter] = useState<[number, number]>([Number(params?.get('lon')) || 35.6892, Number(params?.get('lon')) || 51.3890]); + const [bottomSheet1State, setBottomSheet1State] = useState(false); + const [bottomSheet2State, setBottomSheet2State] = useState(false); + + // Example initial position (Tehran) + const initialPosition = React.useMemo<[number, number]>(() => [35.6892, 51.3890], []); + + useEffect(() => { + // Initialize markers on the client side + const initializeMarkers = async () => { + // Dynamic import of Leaflet on the client side + const L = (await import('leaflet')).default; + + setMarkers([ + { + position: initialPosition, + title: 'Restaurant Location', + icon: new L.Icon({ + iconUrl: '/icons/restaurant-marker.png', + iconSize: [25, 41], + iconAnchor: [12, 41], + popupAnchor: [1, -34], + }) + } + ]); + }; + + initializeMarkers(); + }, [initialPosition]); + + const handlePositionSelect = (position: [number, number]) => { + setSelectedPosition((prev) => { + if(prev === position) { + return prev; + } + setSelectedAddress(null); + return position; + }); + setBottomSheet1State(position && true); + setBottomSheet2State(false); + // You can handle the selected position here + console.log('Selected position:', position); + }; + + const handleZoomChange = (zoom: number) => { + console.log('Zoom level:', zoom); + }; + + const handleMapClick = (e: L.LeafletMouseEvent) => { + console.log('Map clicked at:', e.latlng); + }; + + const toggleBottomSheet1 = () => { + setBottomSheet1State((prev) => !prev); + } + + const toggleBottomSheet2 = async () => { + if (!selectedPosition) { + setBottomSheet1State(false); + setBottomSheet2State(false); + return; + } + setBottomSheet1State(false); + setBottomSheet2State((prev) => !prev); + if (!bottomSheet2State) { + const address = await fetchAddressFromCoordinates(selectedPosition[0], selectedPosition[1]); + setSelectedAddress(address); + } + } + + const fetchAddressFromCoordinates = async (lat: unknown, lon: unknown) => { + try { + const response = await fetch( + `https://nominatim.openstreetmap.org/reverse?lat=${lat}&lon=${lon}&format=json&addressdetails=1&accept-language=fa` + ); + const data = await response.json(); + return data; // Full formatted address + } catch (error) { + console.error('Error fetching address:', error); + return null; + } + }; + + const submitAddressAction = () => { + + } + + return ( +
+ +
+ +
+ + {/* {selectedPosition && ( +
+

موقعیت انتخاب شده: {selectedPosition[0].toFixed(4)}, {selectedPosition[1].toFixed(4)}

+
+ )} */} + +
+
+ +
+ + +
+
+ + +
+
+
+ + {!selectedAddress ? + + : + [ + selectedAddress.address.state ?? selectedAddress.address.province, + selectedAddress.address.city, + selectedAddress.address.neighbourhood, + !selectedAddress.address.road.startsWith('میدان') && + !selectedAddress.address.road.startsWith('بلوار') && + !selectedAddress.address.road.startsWith('خیابان') && + !selectedAddress.address.road.startsWith('کوچه') ? + selectedAddress.type === 'secondary' ? `بلوار ${selectedAddress.address.road}` : + selectedAddress.type === 'tertiary' ? `خیابان ${selectedAddress.address.road}` : + selectedAddress.type === 'residential' ? `کوچه ${selectedAddress.address.road}` : + selectedAddress.address.road : selectedAddress.address.road + ].filter(x => x).join('، ') + } + +
+ { }} + /> + { }} + /> +
+
+ + +
+
+
+
+
+
+
+ ); +} + +export default OrderTrackingPage; \ No newline at end of file diff --git a/src/components/bottomsheet/AnimatedBottomSheet.tsx b/src/components/bottomsheet/AnimatedBottomSheet.tsx index 7003c70..6bb19ca 100644 --- a/src/components/bottomsheet/AnimatedBottomSheet.tsx +++ b/src/components/bottomsheet/AnimatedBottomSheet.tsx @@ -5,10 +5,14 @@ import CloseIcon from '../icons/CloseIcon' import { motion, PanInfo, Variants } from 'framer-motion' type Props = { - title: string + title?: string, + showCloseButton?: boolean, + showTitle?: boolean, + blurOpacity?: string | number | null, + overrideClassName?: string, } & BlurredOverlayContainerProps -const AnimatedBottomSheet = (props: Props) => { +const AnimatedBottomSheet = ({ blurOpacity = 64, overrideClassName = '', showTitle = true, showCloseButton = true, ...props }: Props) => { const closeRef: React.Ref | undefined = useRef(null); const variants: Variants = { @@ -43,18 +47,26 @@ const AnimatedBottomSheet = (props: Props) => { onDragEnd={onDrag} data-visible={props.visible} className={clsx( - 'absolute left-0 w-full bg-white/64 rounded-t-4xl pt-[39px] pb-6', - 'bottom-0' + overrideClassName ?? '', + 'absolute left-0 w-full rounded-t-4xl pt-[39px] pb-6', + 'bottom-0', + blurOpacity ? `bg-container/${blurOpacity}` : 'bg-container' )} initial="hidden" animate={props.visible ? 'visible' : 'hidden'} variants={variants} >
-
{props.title}
-
- -
+ {showTitle && +
+ {props.title} +
+ } + {showCloseButton && +
+ +
+ }
{props.children} diff --git a/src/components/input/ActiveSearchbox.tsx b/src/components/input/ActiveSearchbox.tsx new file mode 100644 index 0000000..07b27dc --- /dev/null +++ b/src/components/input/ActiveSearchbox.tsx @@ -0,0 +1,69 @@ +'use client'; + +import { useState } from 'react'; +import { Skeleton } from '@/components/ui/skeleton'; +import SearchBox from './SearchBox'; + +interface ActiveSearchboxProps extends Omit, 'results'> { + onSearch?: (value: string) => void; + results?: Array<{ id: string; title: string }>; + isLoading?: boolean; + onResultSelect?: (result: { id: string; title: string }) => void; +} + +export function ActiveSearchbox({ + className, + onSearch, + results = [], + isLoading = false, + onResultSelect, + ...props +}: ActiveSearchboxProps) { + const [isOpen, setIsOpen] = useState(false); + + const handleInputChange = (e: React.ChangeEvent) => { + const value = e.target.value; + onSearch?.(value); + setIsOpen(true); + }; + + return ( +
+ setIsOpen(true)} + onChange={handleInputChange} + {...props} + /> + {isOpen && (isLoading || results.length > 0) && ( +
+ {isLoading ? ( + <> +
+ + +
+
+ + +
+ + ) : ( + results.map((result) => ( + + )) + )} +
+ )} +
+ ); +} diff --git a/src/components/input/InputField.tsx b/src/components/input/InputField.tsx index 989db27..855708b 100644 --- a/src/components/input/InputField.tsx +++ b/src/components/input/InputField.tsx @@ -6,7 +6,6 @@ type Props = { htmlFor: string; labelText?: React.ReactNode; value?: string; - onChange: (e: React.ChangeEvent) => void; valid?: boolean } & Omit, "id">; diff --git a/src/components/input/SearchBox.tsx b/src/components/input/SearchBox.tsx index 26e3211..436593d 100644 --- a/src/components/input/SearchBox.tsx +++ b/src/components/input/SearchBox.tsx @@ -1,22 +1,26 @@ import React from 'react'; import SearchIcon from '../icons/SearchIcon'; +import clsx from 'clsx'; -type Props = { - value: string; +interface SearchboxProps extends Omit, 'results'> { placeholder?: string; onChange: (e: React.ChangeEvent) => void; }; -export default function SearchBox({ value, placeholder = '', onChange }: Props) { +export default function SearchBox({ className, value, placeholder = '', onChange, ...props }: SearchboxProps) { return ( -
+
); diff --git a/src/components/map/CustomMap.css b/src/components/map/CustomMap.css new file mode 100644 index 0000000..a68bdfd --- /dev/null +++ b/src/components/map/CustomMap.css @@ -0,0 +1,269 @@ +.map-google-style { + background-color: #f8f9fa; + font-family: 'irancell', system-ui, -apple-system, sans-serif; +} + +/* Map tiles styling */ +.map-google-style .leaflet-tile-pane { + filter: saturate(0.95) contrast(0.95) brightness(1.02); +} + +/* Base font for all map elements */ +.map-google-style, +.map-google-style .leaflet-container { + font-family: 'irancell', system-ui, -apple-system, sans-serif !important; +} + +/* Improve tile loading appearance */ +.map-google-style .leaflet-tile-loading { + opacity: 0; + transition: opacity 0.2s ease-in-out; +} + +/* Custom popup style */ +.map-google-style .leaflet-popup-content-wrapper { + border-radius: 16px; + padding: 0; + box-shadow: 0 4px 16px rgba(0,0,0,0.08), + 0 2px 4px rgba(0,0,0,0.05); + border: 1px solid rgba(0,0,0,0.08); + backdrop-filter: blur(8px); + background: rgba(255, 255, 255, 0.95); +} + +.map-google-style .leaflet-popup-content { + margin: 16px 20px; + line-height: 1.6; + font-size: 14px; + color: #1f2937; + font-weight: 500; + text-align: right; +} + +.map-google-style .leaflet-popup-tip { + background: rgba(255, 255, 255, 0.95); + backdrop-filter: blur(8px); + box-shadow: 0 4px 16px rgba(0,0,0,0.08); + border: 1px solid rgba(0,0,0,0.08); +} + +.map-google-style .leaflet-popup-close-button { + padding: 12px !important; + color: #6b7280 !important; + opacity: 0.7; + transition: opacity 0.2s ease; +} + +.map-google-style .leaflet-popup-close-button:hover { + opacity: 1; +} + +/* Marker styles */ +.map-google-style .leaflet-marker-icon, +.map-google-style .leaflet-marker-shadow { + transition: all 0.2s cubic-bezier(0.4, 0, 0.2, 1); + transform-origin: bottom center; +} + +.map-google-style .leaflet-marker-icon { + filter: drop-shadow(0 2px 4px rgba(0,0,0,0.1)); +} + +.map-google-style .leaflet-marker-icon:hover { + transform: scale(1.1) translateY(-4px); + filter: drop-shadow(0 4px 6px rgba(0,0,0,0.15)); +} + +.map-google-style .leaflet-marker-icon:hover + .leaflet-marker-shadow { + transform: scale(1.1) translateY(-4px); + opacity: 0.6; +} + +/* Attribution styling */ +.map-google-style .leaflet-control-attribution { + display: none; + /* background: rgba(255,255,255,0.9) !important; + backdrop-filter: blur(8px); + padding: 6px 12px !important; + border-radius: 12px; + margin: 8px !important; + font-size: 11px !important; + color: #4b5563 !important; + box-shadow: 0 2px 8px rgba(0,0,0,0.05); + border: 1px solid rgba(0,0,0,0.08); */ +} + +/* Zoom controls */ +.map-zoom-controls { + position: absolute; + right: 20px; + bottom: 40px; + background: rgba(255, 255, 255, 0.95); + border-radius: 16px; + box-shadow: 0 4px 16px rgba(0,0,0,0.08), + 0 2px 4px rgba(0,0,0,0.05); + border: 1px solid rgba(0,0,0,0.08); + overflow: hidden; + backdrop-filter: blur(8px); + z-index: 1000; +} + +.zoom-button { + width: 44px; + height: 44px; + display: flex; + align-items: center; + justify-content: center; + background: transparent; + border: none; + cursor: pointer; + transition: all 0.2s cubic-bezier(0.4, 0, 0.2, 1); + color: #4b5563; + font-size: 22px; + font-weight: 500; + -webkit-tap-highlight-color: transparent; +} + +.zoom-button:first-child { + border-bottom: 1px solid rgba(0,0,0,0.08); +} + +.zoom-button:hover { + background-color: rgba(59, 130, 246, 0.05); + color: #2563eb; +} + +.zoom-button:active { + background-color: rgba(59, 130, 246, 0.1); +} + +/* Selected location indicator */ +.selected-location-indicator { + position: fixed; + bottom: 24px; + left: 50%; + transform: translateX(-50%); + background: rgba(255, 255, 255, 0.95); + backdrop-filter: blur(8px); + padding: 14px 24px; + border-radius: 16px; + box-shadow: 0 4px 16px rgba(0,0,0,0.08), + 0 2px 4px rgba(0,0,0,0.05); + border: 1px solid rgba(0,0,0,0.08); + color: #1f2937; + font-size: 14px; + font-weight: 500; + line-height: 1.6; + max-width: 90%; + width: auto; + text-align: center; + z-index: 1000; + animation: slideUp 0.3s cubic-bezier(0.4, 0, 0.2, 1); +} + +@keyframes slideUp { + from { + transform: translate(-50%, 100%); + opacity: 0; + } + to { + transform: translate(-50%, 0); + opacity: 1; + } +} + +/* Loading state */ +.map-loading { + position: absolute; + inset: 0; + background: rgba(255, 255, 255, 0.9); + backdrop-filter: blur(4px); + z-index: 1000; + display: flex; + align-items: center; + justify-content: center; +} + +/* Container styling */ +.map-container { + position: relative; + width: 100%; + height: 100%; + background: linear-gradient(to bottom, #e5e7eb 0%, #f3f4f6 100%); +} + +/* Search box styling */ +.map-search-container { + position: absolute; + top: 20px; + left: 50%; + transform: translateX(-50%); + width: 90%; + max-width: 480px; + z-index: 1000; +} + +.map-search-input { + width: 100%; + padding: 14px 20px; + background: rgba(255, 255, 255, 0.95); + border: 1px solid rgba(0,0,0,0.08); + border-radius: 16px; + box-shadow: 0 4px 16px rgba(0,0,0,0.08), + 0 2px 4px rgba(0,0,0,0.05); + transition: all 0.3s cubic-bezier(0.4, 0, 0.2, 1); + font-size: 15px; + font-weight: 500; + text-align: right; + backdrop-filter: blur(8px); + color: #1f2937; +} + +.map-search-input:focus { + box-shadow: 0 6px 24px rgba(0,0,0,0.12), + 0 2px 8px rgba(0,0,0,0.08); + border-color: rgba(59, 130, 246, 0.5); + background: rgba(255, 255, 255, 0.98); +} + +.map-search-input::placeholder { + color: #6b7280; + font-weight: 400; +} + +.map-search-results { + margin-top: 12px; + background: rgba(255, 255, 255, 0.95); + border-radius: 16px; + box-shadow: 0 4px 20px rgba(0,0,0,0.1), + 0 2px 8px rgba(0,0,0,0.05); + overflow: hidden; + border: 1px solid rgba(0,0,0,0.08); + backdrop-filter: blur(8px); +} + +.map-search-result-item { + padding: 14px 20px; + transition: all 0.2s cubic-bezier(0.4, 0, 0.2, 1); + border-bottom: 1px solid rgba(0,0,0,0.05); + font-size: 13px; + font-weight: 400; + color: #1f2937; + cursor: pointer; + display: block; + width: 100%; + text-align: right; +} + +.map-search-result-item:last-child { + border-bottom: none; +} + +.map-search-result-item:hover { + background-color: rgba(59, 130, 246, 0.05); + color: #2563eb; +} + +.map-search-result-item:active { + background-color: rgba(59, 130, 246, 0.1); +} diff --git a/src/components/map/CustomMap.tsx b/src/components/map/CustomMap.tsx new file mode 100644 index 0000000..d25e23b --- /dev/null +++ b/src/components/map/CustomMap.tsx @@ -0,0 +1,305 @@ +'use client'; + +import React, { useEffect, useState } from 'react'; +import { MapContainer, TileLayer, Marker, Popup, useMap, useMapEvents } from 'react-leaflet'; +import L from 'leaflet'; +import { Skeleton } from '@/components/ui/skeleton'; +import 'leaflet/dist/leaflet.css'; +import './CustomMap.css'; +import { ActiveSearchbox } from '../input/ActiveSearchbox'; + +// Define types for our props +interface CustomMapProps { + initialPosition: [number, number]; + zoom?: number; + markers?: Array<{ + position: [number, number]; + title: string; + icon?: L.Icon; + }>; + onPositionSelect?: (position: [number, number]) => void; + onZoomChange?: (zoom: number) => void; + onClick?: (event: L.LeafletMouseEvent) => void; +} + +// Component to handle map events and position updates +function MapEvents({ + onZoomChange, + onClick, + center, + setMapRef +}: Pick & { + center: [number, number]; + setMapRef: (map: L.Map) => void; +}) { + const map = useMapEvents({ + zoom: () => { + onZoomChange?.(map.getZoom()); + }, + click: (e) => { + onClick?.(e); + }, + }); + + // Update map center when center prop changes + React.useEffect(() => { + map.setView(center, map.getZoom(), { + animate: true, + duration: 1 + }); + }, [map, center]); + + // Set map reference + React.useEffect(() => { + setMapRef(map); + }, [map, setMapRef]); + + return null; +} + +// Component to handle marker updates on zoom +function MarkerUpdater({ markers }: { markers?: CustomMapProps['markers'] }) { + const map = useMap(); + + useEffect(() => { + // You can implement custom marker scaling or clustering logic here + const zoom = map.getZoom(); + // Example: Scale markers based on zoom level + markers?.forEach(marker => { + const element = document.querySelector(`[title="${marker.title}"]`); + if (element) { + const scale = Math.min(1 + (zoom / 20), 1.5); + (element as HTMLElement).style.transform = `scale(${scale})`; + } + }); + }, [map, markers]); + + return null; +} + +const CustomMap: React.FC = ({ + initialPosition, + zoom = 13, + markers = [], + onPositionSelect, + onZoomChange, + onClick, +}) => { + const [selectedPosition, setSelectedPosition] = useState<[number, number] | null>(null); + const [mapInstance, setMapInstance] = useState(null); + const [isSearching, setIsSearching] = useState(false); + const [searchResults, setSearchResults] = useState>([]); + const [isMapLoading, setIsMapLoading] = useState(true); + + // Handle map load complete + useEffect(() => { + const checkTiles = () => { + if (mapInstance) { + const container = document.querySelector('.leaflet-tile-container'); + if (container && container.children.length > 0) { + setIsMapLoading(false); + } else { + setTimeout(checkTiles, 100); + } + } + }; + checkTiles(); + }, [mapInstance]); + + // Initialize default icon on client side only + useEffect(() => { + // Dynamic import of Leaflet on the client side + // Fix for default marker icon in Next.js + delete ((L.Icon.Default.prototype) as { _getIconUrl?: unknown })._getIconUrl; + L.Icon.Default.mergeOptions({ + iconRetinaUrl: '/marker-icon-2x.png', + iconUrl: '/marker-icon.png', + shadowUrl: '/marker-shadow.png', + }); + }, []); + + const handleMapClick = (e: L.LeafletMouseEvent) => { + const newPosition: [number, number] = [e.latlng.lat, e.latlng.lng]; + setSelectedPosition(newPosition); + onPositionSelect?.(newPosition); + onClick?.(e); + }; + + const [searchTimeout, setSearchTimeout] = useState(); + + const handleSearch = async (value: string) => { + if (!value || value.trim().length === 0) { + setSearchResults([]); + return; + } + + // Clear previous timeout + if (searchTimeout) { + clearTimeout(searchTimeout); + } + + setIsSearching(true); + + // Set new timeout + const timeout = setTimeout(async () => { + try { + // Iran's bounding box coordinates (roughly) + const viewbox = '44.0,25.0,63.0,40.0'; // [min lon, min lat, max lon, max lat] + const response = await fetch( + `https://nominatim.openstreetmap.org/search?` + + `format=json&` + + `q=${encodeURIComponent(value)}&` + + `countrycodes=ir&` + // Prioritize Iran + `viewbox=${viewbox}&` + + `bounded=0&` + // Allow results outside viewbox but prioritize inside + `limit=10&` + // Limit results to 10 + `namedetails=1&` + // Get native names + `accept-language=fa` // Request Persian results + , { + headers: { + 'Accept-Language': 'fa,en;q=0.9' // Prefer Persian, fallback to English + } + }); + interface SearchResult { + place_id: string; + display_name: string; + lat: string; + lon: string; + namedetails?: { + name?: string; + 'name:fa'?: string; + alt_name?: string; + 'alt_name:fa'?: string; + }; + } + const data: SearchResult[] = await response.json(); + setSearchResults(data.map((item) => ({ + id: item.place_id, + // Use Persian name if available, fallback to display_name + title: /* item.namedetails?.['name:fa'] || + item.namedetails?.name || + item.namedetails?.['alt_name:fa'] || + item.namedetails?.alt_name || */ + item.display_name, + position: [parseFloat(item.lat), parseFloat(item.lon)] as [number, number] + }))); + } catch (error) { + console.error('Search error:', error); + setSearchResults([]); + } finally { + setIsSearching(false); + } + }, 500); // Wait 500ms after last keystroke + + setSearchTimeout(timeout); + }; + + const handleSearchResultSelect = (result: { id: string; title: string; position: [number, number] }) => { + console.log(result); + setSelectedPosition(result.position); + if (mapInstance) { + mapInstance.setView(result.position, mapInstance.getZoom()); + } + }; + + return ( +
+ { + // Find the full result object with position + const result = searchResults.find(r => r.id === v.id && r.title === v.title); + if (result) { + handleSearchResultSelect(result); + } + }} + /> +
+ + +
+ {isMapLoading && ( +
+
+ + + +
+
+ )} + + + + {/* Custom markers */} + {markers.map((marker, index) => ( + + +
{marker.title}
+
+
+ ))} + + {/* Selected position marker */} + {selectedPosition && ( + + +
موقعیت انتخاب شده
+
+
+ )} + + {/* Event handlers */} + + {/* Marker updater for zoom effects */} + +
+
+ ); +}; + +export default CustomMap; diff --git a/src/components/overlays/BlurredOverlayContainer.tsx b/src/components/overlays/BlurredOverlayContainer.tsx index 5f78156..1f83cbf 100644 --- a/src/components/overlays/BlurredOverlayContainer.tsx +++ b/src/components/overlays/BlurredOverlayContainer.tsx @@ -13,6 +13,7 @@ export type BlurredOverlayContainerProps = { inDuration?: number outDuration?: number children: ReactNode + noBlur?: boolean } & React.ComponentProps const BlurredOverlayContainer = ({ @@ -26,6 +27,7 @@ const BlurredOverlayContainer = ({ children, className, onClick, + noBlur = false, ...props }: BlurredOverlayContainerProps) => { const [loaded, setLoaded] = useState(visible) @@ -69,10 +71,11 @@ const BlurredOverlayContainer = ({ ) { + return ( +
+ ) +} + +export { Skeleton }