From 424373bc3f7ff72641aca590ece23c1fdb421eb0 Mon Sep 17 00:00:00 2001 From: Mahyar Khanbolooki Date: Fri, 4 Jul 2025 15:49:38 +0330 Subject: [PATCH] feat: add side menu --- src/app/globals.css | 2 + src/app/layout.tsx | 1 + src/app/loading.tsx | 2 +- src/app/menu/layout.tsx | 14 ++- src/components/button/VisualSwitch.jsx | 19 ++++ src/components/icons/CalendarSearchIcon.tsx | 84 +++++++++++++++++ src/components/icons/CupIcon.tsx | 58 ++++++++++++ src/components/icons/DirectboxReceiveIcon.tsx | 55 +++++++++++ src/components/icons/ExitIcon.tsx | 38 ++++++++ src/components/icons/GameControllerIcon.tsx | 79 ++++++++++++++++ src/components/icons/NoteBoardIcon.tsx | 45 +++++++++ src/components/icons/NotificationBellIcon.tsx | 38 ++++++++ src/components/icons/ReceiptIcon.tsx | 52 +++++++++++ src/components/icons/SettingsIcon.tsx | 33 +++++++ src/components/icons/ThumbsUpIcon.tsx | 30 ++++++ src/components/icons/TickerDiscountIcon.tsx | 45 +++++++++ src/components/input/SearchBox.jsx | 11 +++ src/components/menu/Menu.tsx | 92 +++++++++++++++++++ src/components/menu/MenuItem.tsx | 20 ++++ src/components/navigation/BottomNavBar.tsx | 10 +- .../overlays/BlurredOverlayContainer.tsx | 37 ++++++++ .../{loading => overlays}/LoadingOverlay.tsx | 0 .../animations/PingAnimation.tsx | 0 src/components/utils/Tooltip.tsx | 2 +- .../auth/components/AuthFormWrapper.tsx | 2 +- 25 files changed, 758 insertions(+), 11 deletions(-) create mode 100644 src/components/button/VisualSwitch.jsx create mode 100644 src/components/icons/CalendarSearchIcon.tsx create mode 100644 src/components/icons/CupIcon.tsx create mode 100644 src/components/icons/DirectboxReceiveIcon.tsx create mode 100644 src/components/icons/ExitIcon.tsx create mode 100644 src/components/icons/GameControllerIcon.tsx create mode 100644 src/components/icons/NoteBoardIcon.tsx create mode 100644 src/components/icons/NotificationBellIcon.tsx create mode 100644 src/components/icons/ReceiptIcon.tsx create mode 100644 src/components/icons/SettingsIcon.tsx create mode 100644 src/components/icons/ThumbsUpIcon.tsx create mode 100644 src/components/icons/TickerDiscountIcon.tsx create mode 100644 src/components/input/SearchBox.jsx create mode 100644 src/components/menu/Menu.tsx create mode 100644 src/components/menu/MenuItem.tsx create mode 100644 src/components/overlays/BlurredOverlayContainer.tsx rename src/components/{loading => overlays}/LoadingOverlay.tsx (100%) rename src/components/{loading => overlays}/animations/PingAnimation.tsx (100%) diff --git a/src/app/globals.css b/src/app/globals.css index 211731c..3edc26d 100644 --- a/src/app/globals.css +++ b/src/app/globals.css @@ -13,6 +13,8 @@ --color-foreground: #333333; --color-disabled: #E7E7E7; --color-disabled-text: #8C90A3; + --color-menu-header: #C3C7DD; + --color-icon-deactive: #A8ABBF; --radius-normal: 10px; --text-sm2: 13px; --text-xs2: 10px; diff --git a/src/app/layout.tsx b/src/app/layout.tsx index 01ca00a..634c0f6 100644 --- a/src/app/layout.tsx +++ b/src/app/layout.tsx @@ -16,6 +16,7 @@ export default function RootLayout({ {children} diff --git a/src/app/loading.tsx b/src/app/loading.tsx index 49906a7..a21843d 100644 --- a/src/app/loading.tsx +++ b/src/app/loading.tsx @@ -1,4 +1,4 @@ -import LoadingOverlay from '@/components/loading/LoadingOverlay' +import LoadingOverlay from '@/components/overlays/LoadingOverlay' import React from 'react' function Loading() { diff --git a/src/app/menu/layout.tsx b/src/app/menu/layout.tsx index b92855f..21d28ed 100644 --- a/src/app/menu/layout.tsx +++ b/src/app/menu/layout.tsx @@ -1,5 +1,6 @@ import BottomNavBar from "@/components/navigation/BottomNavBar"; import { ReactQueryProvider } from "@/components/providers/ReactQueryProvider"; +import { Menu } from '@/components/menu/Menu'; export const metadata = { title: 'Menu', @@ -13,10 +14,17 @@ export default function MenuLayout({ return ( -
- {children} -
+
+ {children} +
+ +
+ +
); } diff --git a/src/components/button/VisualSwitch.jsx b/src/components/button/VisualSwitch.jsx new file mode 100644 index 0000000..0982036 --- /dev/null +++ b/src/components/button/VisualSwitch.jsx @@ -0,0 +1,19 @@ +import React from 'react' +import { PiSun, PiMoon } from 'react-icons/pi' + +export default function NightModeSwitch({ state, toggle }) { + return ( + + ) +} diff --git a/src/components/icons/CalendarSearchIcon.tsx b/src/components/icons/CalendarSearchIcon.tsx new file mode 100644 index 0000000..8bab4d2 --- /dev/null +++ b/src/components/icons/CalendarSearchIcon.tsx @@ -0,0 +1,84 @@ +import React from 'react'; + +type Props = React.SVGProps; + +const CalendarSearchIcon = (props: Props) => ( + + + + + + + + + + + +); + +export default CalendarSearchIcon; diff --git a/src/components/icons/CupIcon.tsx b/src/components/icons/CupIcon.tsx new file mode 100644 index 0000000..d8fc720 --- /dev/null +++ b/src/components/icons/CupIcon.tsx @@ -0,0 +1,58 @@ +import React from 'react'; + +type Props = React.SVGProps; + +const CupIcon = (props: Props) => ( + + + + + + + + +); + +export default CupIcon; diff --git a/src/components/icons/DirectboxReceiveIcon.tsx b/src/components/icons/DirectboxReceiveIcon.tsx new file mode 100644 index 0000000..5a1c678 --- /dev/null +++ b/src/components/icons/DirectboxReceiveIcon.tsx @@ -0,0 +1,55 @@ +import React from 'react'; + +type Props = React.SVGProps; + +const DirectboxReceiveIcon = (props: Props) => ( + + + + + + + +); + +export default DirectboxReceiveIcon; diff --git a/src/components/icons/ExitIcon.tsx b/src/components/icons/ExitIcon.tsx new file mode 100644 index 0000000..47f556d --- /dev/null +++ b/src/components/icons/ExitIcon.tsx @@ -0,0 +1,38 @@ +import React from 'react'; + +type Props = React.SVGProps; + +const ExitIcon = (props: Props) => ( + + + + + +); + +export default ExitIcon; diff --git a/src/components/icons/GameControllerIcon.tsx b/src/components/icons/GameControllerIcon.tsx new file mode 100644 index 0000000..72fe06f --- /dev/null +++ b/src/components/icons/GameControllerIcon.tsx @@ -0,0 +1,79 @@ +import React from 'react'; + +type Props = React.SVGProps; + +const GameControllerIcon = (props: Props) => ( + + + + + + + + + + +); + +export default GameControllerIcon; diff --git a/src/components/icons/NoteBoardIcon.tsx b/src/components/icons/NoteBoardIcon.tsx new file mode 100644 index 0000000..bd18d35 --- /dev/null +++ b/src/components/icons/NoteBoardIcon.tsx @@ -0,0 +1,45 @@ +import React from 'react'; + +type Props = React.SVGProps; + +const NoteBoardIcon = (props: Props) => ( + + + + + + +); + +export default NoteBoardIcon; diff --git a/src/components/icons/NotificationBellIcon.tsx b/src/components/icons/NotificationBellIcon.tsx new file mode 100644 index 0000000..b5440c7 --- /dev/null +++ b/src/components/icons/NotificationBellIcon.tsx @@ -0,0 +1,38 @@ +import React from 'react'; + +type Props = React.SVGProps; + +const NotificationBellIcon = (props: Props) => ( + + + + + +); + +export default NotificationBellIcon; diff --git a/src/components/icons/ReceiptIcon.tsx b/src/components/icons/ReceiptIcon.tsx new file mode 100644 index 0000000..c5df684 --- /dev/null +++ b/src/components/icons/ReceiptIcon.tsx @@ -0,0 +1,52 @@ +import React from 'react'; + +type Props = React.SVGProps; + +const ReceiptIcon = (props: Props) => ( + + + + + + + +); + +export default ReceiptIcon; diff --git a/src/components/icons/SettingsIcon.tsx b/src/components/icons/SettingsIcon.tsx new file mode 100644 index 0000000..9cf8ce3 --- /dev/null +++ b/src/components/icons/SettingsIcon.tsx @@ -0,0 +1,33 @@ +import React from 'react'; + +type Props = React.SVGProps; + +const SettingIcon = (props: Props) => ( + + + + +); + +export default SettingIcon; diff --git a/src/components/icons/ThumbsUpIcon.tsx b/src/components/icons/ThumbsUpIcon.tsx new file mode 100644 index 0000000..beff36a --- /dev/null +++ b/src/components/icons/ThumbsUpIcon.tsx @@ -0,0 +1,30 @@ +import React from 'react'; + +type Props = React.SVGProps; + +const ThumbsUpIcon = (props: Props) => ( + + + + +); + +export default ThumbsUpIcon; diff --git a/src/components/icons/TickerDiscountIcon.tsx b/src/components/icons/TickerDiscountIcon.tsx new file mode 100644 index 0000000..88d2ded --- /dev/null +++ b/src/components/icons/TickerDiscountIcon.tsx @@ -0,0 +1,45 @@ +import React from 'react'; + +type Props = React.SVGProps; + +const TicketDiscountIcon = (props: Props) => ( + + + + + + +); + +export default TicketDiscountIcon; diff --git a/src/components/input/SearchBox.jsx b/src/components/input/SearchBox.jsx new file mode 100644 index 0000000..952a570 --- /dev/null +++ b/src/components/input/SearchBox.jsx @@ -0,0 +1,11 @@ +import React from 'react' +import { CiSearch } from "react-icons/ci"; + +export default function SearchBox() { + return ( +
+ + +
+ ) +} diff --git a/src/components/menu/Menu.tsx b/src/components/menu/Menu.tsx new file mode 100644 index 0000000..ef7ce09 --- /dev/null +++ b/src/components/menu/Menu.tsx @@ -0,0 +1,92 @@ +'use client'; + +import React, { useState } from 'react' +import MenuItemType from './MenuItem' +import MenuItem from './MenuItem'; +import NotificationBellIcon from '../icons/NotificationBellIcon'; +import CupIcon from '../icons/CupIcon'; +import TicketDiscountIcon from '../icons/TickerDiscountIcon'; +import CalendarSearchIcon from '../icons/CalendarSearchIcon'; +import ReceiptIcon from '../icons/ReceiptIcon'; +import GameControllerIcon from '../icons/GameControllerIcon'; +import ThumbsUpIcon from '../icons/ThumbsUpIcon'; +import NoteBoardIcon from '../icons/NoteBoardIcon'; +import DirectboxReceiveIcon from '../icons/DirectboxReceiveIcon'; +import BlurredOverlayContainer from '../overlays/BlurredOverlayContainer'; +import SettingIcon from '../icons/SettingsIcon'; +import ExitIcon from '../icons/ExitIcon'; + +type MenuItemType = { + href: string | undefined; + title: string; + icon: React.ReactElement +} + +const menuItems: Array> = [ + [ + { + href: '/', title: 'صفحه اصلی', icon: + }, + { + href: 'my-services', title: 'سرویس های من', icon: + }, + { + href: 'services', title: 'سایر سرویس ها', icon: + }, + { + href: 'invoices', title: 'لیست', icon: + }, + { + href: 'transactions', title: 'تراکنش ها', icon: + }, + { + href: '/', title: 'صفحه اصلی', icon: + }, + { + href: 'my-services', title: 'سرویس های من', icon: + }, + { + href: 'services', title: 'سایر سرویس ها', icon: + }, + { + href: 'invoices', title: 'نصب اپلیکیشن', icon: + }, + ], + [], + [ + { + href: undefined, title: 'تنظیمات', icon: + }, + { + href: undefined, title: 'خروج', icon: + }, + ] +] + +export function Menu() { + const [menuState, setMenuState] = useState(true); + + return ( + setMenuState((state) => !state)} visible={menuState}> +
setMenuState((state) => !state)} className='absolute top-0 -right-full data-[visible]:right-0 transition-all duration-200 ease-in-out not-xl:!rounded-s-none overflow-clip bg-white w-[200px] h-full flex flex-col z-40'> +
    +
  • +
    +
    +
    منو
    + {menuItems[0].map((v, i) => { + return + })} +
    +
    +
  • +
  • + {menuItems[2].map((v, i) => { + return + })} +
  • +
+
+
+ ) +} diff --git a/src/components/menu/MenuItem.tsx b/src/components/menu/MenuItem.tsx new file mode 100644 index 0000000..c421a06 --- /dev/null +++ b/src/components/menu/MenuItem.tsx @@ -0,0 +1,20 @@ +import Link, { LinkProps } from 'next/link' +import React from 'react' + +type Props = { + icon: React.ReactElement + title: string +} & LinkProps & React.AnchorHTMLAttributes + +export default function MenuItem({ href, title, icon, className, ...restProps }: Props) { + return ( + + {icon} +
{title}
+ + ) +} \ No newline at end of file diff --git a/src/components/navigation/BottomNavBar.tsx b/src/components/navigation/BottomNavBar.tsx index 7be7ffd..b5cec37 100644 --- a/src/components/navigation/BottomNavBar.tsx +++ b/src/components/navigation/BottomNavBar.tsx @@ -12,7 +12,7 @@ type Props = object function BottomNavBar({ }: Props) { return (
-
+
- } value="پسند ها" /> - } value="اعلان ها" /> - } value="منو" /> - } value="پیجر" /> } value="سبد خرید" /> + } value="پیجر" /> + } value="منو" /> + } value="اعلان ها" /> + } value="پسند ها" />
diff --git a/src/components/overlays/BlurredOverlayContainer.tsx b/src/components/overlays/BlurredOverlayContainer.tsx new file mode 100644 index 0000000..bcf61a2 --- /dev/null +++ b/src/components/overlays/BlurredOverlayContainer.tsx @@ -0,0 +1,37 @@ +'use client' + +import React, { useEffect, useState } from 'react' +import PingAnimation from './animations/PingAnimation'; + +type Props = { + visible?: boolean; + bgOpacity?: number; + delay?: number; +} & React.DetailedHTMLProps, HTMLDivElement> + +const BlurredOverlayContainer = ({ visible = true, bgOpacity = 30, delay = 0, children = }: Props) => { + const [loaded, setLoaded] = useState(false); + + useEffect(() => { + // Trigger fade-in after mount + const timeout = setTimeout(() => setLoaded(visible), 300); + return () => clearTimeout(timeout); + }, [visible]); + + return ( +
+ {children} +
+ ) +} + +export default BlurredOverlayContainer \ No newline at end of file diff --git a/src/components/loading/LoadingOverlay.tsx b/src/components/overlays/LoadingOverlay.tsx similarity index 100% rename from src/components/loading/LoadingOverlay.tsx rename to src/components/overlays/LoadingOverlay.tsx diff --git a/src/components/loading/animations/PingAnimation.tsx b/src/components/overlays/animations/PingAnimation.tsx similarity index 100% rename from src/components/loading/animations/PingAnimation.tsx rename to src/components/overlays/animations/PingAnimation.tsx diff --git a/src/components/utils/Tooltip.tsx b/src/components/utils/Tooltip.tsx index d397a91..ab9347c 100644 --- a/src/components/utils/Tooltip.tsx +++ b/src/components/utils/Tooltip.tsx @@ -23,7 +23,7 @@ const arrowClasses = { const Tooltip = ({ children, - content, + title: content, hidden = false, position = 'top', ...rest diff --git a/src/features/auth/components/AuthFormWrapper.tsx b/src/features/auth/components/AuthFormWrapper.tsx index f2d44df..2fb2db1 100644 --- a/src/features/auth/components/AuthFormWrapper.tsx +++ b/src/features/auth/components/AuthFormWrapper.tsx @@ -1,4 +1,4 @@ -import LoadingOverlay from '@/components/loading/LoadingOverlay'; +import LoadingOverlay from '@/components/overlays/LoadingOverlay'; import React from 'react' type Props = {