diff --git a/messages/fa.json b/messages/fa.json index 7677c3c..5f0ab5a 100644 --- a/messages/fa.json +++ b/messages/fa.json @@ -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", diff --git a/next.config.ts b/next.config.ts index 1f12a26..9b34d5a 100644 --- a/next.config.ts +++ b/next.config.ts @@ -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); diff --git a/package.json b/package.json index cc026a9..1ab1ddf 100644 --- a/package.json +++ b/package.json @@ -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", diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 245c31a..4defd3d 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -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) diff --git a/public/icons/arrow-tl.svg b/public/icons/arrow-tl.svg new file mode 100644 index 0000000..a5167ef --- /dev/null +++ b/public/icons/arrow-tl.svg @@ -0,0 +1,3 @@ + + + diff --git a/public/icons/banner-line.svg b/public/icons/banner-line.svg new file mode 100644 index 0000000..6f95404 --- /dev/null +++ b/public/icons/banner-line.svg @@ -0,0 +1,9 @@ + + + + + + + + + diff --git a/public/icons/banner-star.svg b/public/icons/banner-star.svg new file mode 100644 index 0000000..728ea3a --- /dev/null +++ b/public/icons/banner-star.svg @@ -0,0 +1,3 @@ + + + diff --git a/public/icons/calender.svg b/public/icons/calender.svg new file mode 100644 index 0000000..ed53666 --- /dev/null +++ b/public/icons/calender.svg @@ -0,0 +1,9 @@ + + + + + + + + + diff --git a/public/icons/location.svg b/public/icons/location.svg new file mode 100644 index 0000000..3f42e81 --- /dev/null +++ b/public/icons/location.svg @@ -0,0 +1,3 @@ + + + diff --git a/public/icons/logo.svg b/public/icons/logo.svg new file mode 100644 index 0000000..dc3c09b --- /dev/null +++ b/public/icons/logo.svg @@ -0,0 +1,5 @@ + + + + + diff --git a/public/icons/marker-icon.svg b/public/icons/marker-icon.svg new file mode 100644 index 0000000..5876012 --- /dev/null +++ b/public/icons/marker-icon.svg @@ -0,0 +1,21 @@ + + + + + + + + + + + + + + + + + + + + + diff --git a/public/icons/my-order.svg b/public/icons/my-order.svg new file mode 100644 index 0000000..11204fb --- /dev/null +++ b/public/icons/my-order.svg @@ -0,0 +1,3 @@ + + + diff --git a/public/icons/search.svg b/public/icons/search.svg new file mode 100644 index 0000000..55e7e54 --- /dev/null +++ b/public/icons/search.svg @@ -0,0 +1,3 @@ + + + diff --git a/public/images/homePageBannerImage.png b/public/images/homePageBannerImage.png new file mode 100644 index 0000000..1004d48 Binary files /dev/null and b/public/images/homePageBannerImage.png differ diff --git a/src/app/(main)/layout.tsx b/src/app/(main)/layout.tsx new file mode 100644 index 0000000..6ecdd63 --- /dev/null +++ b/src/app/(main)/layout.tsx @@ -0,0 +1,11 @@ +import BottomSheet from "@/components/UI/BottomSheet"; +import MapProviderComponent from "@/providers/MapProviderComponent"; + +export default function RootLayout({ children }: { children: React.ReactNode }) { + return ( + + {children} + + + ); +} diff --git a/src/app/layout.tsx b/src/app/layout.tsx index 7901112..f08db63 100644 --- a/src/app/layout.tsx +++ b/src/app/layout.tsx @@ -50,7 +50,11 @@ export default async function RootLayout({ children }: { children: React.ReactNo const messages = await getMessages({ locale }); return ( - + diff --git a/src/assets/index.ts b/src/assets/index.ts index 9afd110..a294c08 100644 --- a/src/assets/index.ts +++ b/src/assets/index.ts @@ -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"; diff --git a/src/components/Home/Desktop/Banner/LocationMarker.tsx b/src/components/Home/Desktop/Banner/LocationMarker.tsx new file mode 100644 index 0000000..317ddfb --- /dev/null +++ b/src/components/Home/Desktop/Banner/LocationMarker.tsx @@ -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 ( + + + + {/* Drop pointer inside the marker (shown only when dragging) */} + {isDragging && ( + + )} + + + + + ); +} diff --git a/src/components/Home/Desktop/Banner/LocationModal.tsx b/src/components/Home/Desktop/Banner/LocationModal.tsx new file mode 100644 index 0000000..5be350a --- /dev/null +++ b/src/components/Home/Desktop/Banner/LocationModal.tsx @@ -0,0 +1,16 @@ +"use client" +import MainMap from "@/components/MainMap"; +import LocationMarker from "./LocationMarker"; + +const LocationModal = () => { + return ( +
+ + { + console.log("hello")}} /> + +
+ + ); +}; +export default LocationModal; diff --git a/src/components/Home/Desktop/Banner/SearchBar.tsx b/src/components/Home/Desktop/Banner/SearchBar.tsx new file mode 100644 index 0000000..5a2e565 --- /dev/null +++ b/src/components/Home/Desktop/Banner/SearchBar.tsx @@ -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 ( +
+ {/* آدرس */} + }> + + + + {/* تاریخ */} + }> + + + + {/* دسته بندی */} + }> + + +
+ ); +} diff --git a/src/components/Home/Desktop/Banner/SearchItem.tsx b/src/components/Home/Desktop/Banner/SearchItem.tsx new file mode 100644 index 0000000..508fc58 --- /dev/null +++ b/src/components/Home/Desktop/Banner/SearchItem.tsx @@ -0,0 +1,15 @@ +interface SearchItemProps { + icon: React.ReactNode; + children: React.ReactNode; +} + +function SearchItem({ icon, children }: SearchItemProps) { + return ( +
+
{icon}
+ +
{children}
+
+ ); +} +export default SearchItem; diff --git a/src/components/Home/Desktop/Banner/index.tsx b/src/components/Home/Desktop/Banner/index.tsx new file mode 100644 index 0000000..51da40d --- /dev/null +++ b/src/components/Home/Desktop/Banner/index.tsx @@ -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 ( + + {/* ================= Header ================= */} +
+
+ + + + + + + {["home", "search", "salons", "about_us", "contact_us"].map((item, index) => ( + + {t(item)} + + ))} +
+ +
+ {isAuth ? ( + + + + ) : ( + + + +

{t("my_order")}

+
+ + + +

{t("auth")}

+
+
+ )} +
+
+ + {/* ================= Main ================= */} + + {/* ===== Left Content ===== */} +
+ {/* Title */} + + + {t("main_title_line1")} + + + + {t("main_title_line2")} + + + + + + + + + + + + {/* Description */} + + {t("des")} + + + {/* CTA Button */} + +

{t("see_salons")}

+ + + + +
+ +
+ + {/* ===== Right Image ===== */} + + main_image + +
+
+ +
+
+ ); +} diff --git a/src/components/Home/Desktop/index.tsx b/src/components/Home/Desktop/index.tsx index 6a5ed7e..1a9ea2a 100644 --- a/src/components/Home/Desktop/index.tsx +++ b/src/components/Home/Desktop/index.tsx @@ -1,3 +1,9 @@ +import BannerComponent from "./Banner"; + export default function DesktopHome() { - return <>; + return ( +
+ +
+ ); } diff --git a/src/components/MainMap/index.tsx b/src/components/MainMap/index.tsx new file mode 100644 index 0000000..939334c --- /dev/null +++ b/src/components/MainMap/index.tsx @@ -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; + 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 ( +
+ { + 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} + +
+ ); +} diff --git a/src/components/UI/GroupAvatar.tsx b/src/components/UI/GroupAvatar.tsx new file mode 100644 index 0000000..7c01a17 --- /dev/null +++ b/src/components/UI/GroupAvatar.tsx @@ -0,0 +1,36 @@ +import { Avatar, AvatarFallback, AvatarImage } from "@/components/UI/avatar"; + +export default function GroupAvatar() { + return ( +
+ + + U1 + + + + U2 + + + + U3 + + + + U3 + +
+ ); +} diff --git a/src/components/UI/avatar.tsx b/src/components/UI/avatar.tsx new file mode 100644 index 0000000..14e6290 --- /dev/null +++ b/src/components/UI/avatar.tsx @@ -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 ( + + ); +} + +function AvatarImage({ className, ...props }: AvatarPrimitive.Image.Props) { + return ( + + ); +} + +function AvatarFallback({ className, ...props }: AvatarPrimitive.Fallback.Props) { + return ( + + ); +} + +export { Avatar, AvatarImage, AvatarFallback }; diff --git a/src/stores/useMapStore.tsx b/src/stores/useMapStore.tsx new file mode 100644 index 0000000..190f93e --- /dev/null +++ b/src/stores/useMapStore.tsx @@ -0,0 +1,11 @@ +import { create } from "zustand"; + +type MapStoreState = { + isMapMoving: boolean; + setIsMapMoving: (isMoving: boolean) => void; +}; + +export const useMapStore = create((set) => ({ + isMapMoving: false, + setIsMapMoving: (isMapMoving) => set({ isMapMoving }), +})); diff --git a/src/styles/globals.css b/src/styles/globals.css index 35ca8a7..c9d859e 100644 --- a/src/styles/globals.css +++ b/src/styles/globals.css @@ -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;