From ee056ee947cd2db0ac5248d0429e4639cd4fd8d5 Mon Sep 17 00:00:00 2001 From: Mahyar Khanbolooki Date: Fri, 18 Jul 2025 20:20:55 +0330 Subject: [PATCH] add: logout dialog and auth based side menu items --- src/components/button/PrimaryButton.tsx | 2 +- src/components/menu/SideMenu.tsx | 147 +++++++++++++----- .../overlays/BlurredOverlayContainer.tsx | 10 +- 3 files changed, 115 insertions(+), 44 deletions(-) diff --git a/src/components/button/PrimaryButton.tsx b/src/components/button/PrimaryButton.tsx index 2d900cd..5cbcae4 100644 --- a/src/components/button/PrimaryButton.tsx +++ b/src/components/button/PrimaryButton.tsx @@ -27,7 +27,7 @@ function Button({ children, className, disabled, pending, ...rest }: Props) { > diff --git a/src/components/menu/SideMenu.tsx b/src/components/menu/SideMenu.tsx index da67898..6634f25 100644 --- a/src/components/menu/SideMenu.tsx +++ b/src/components/menu/SideMenu.tsx @@ -1,6 +1,6 @@ 'use client'; -import React, { useMemo, useRef } from 'react' +import React, { useMemo, useRef, useState } from 'react' import { motion, PanInfo, Variants } from 'framer-motion'; import MenuItemType from './SideMenuItem' import SideMenuItem from './SideMenuItem'; @@ -17,11 +17,15 @@ import BlurredOverlayContainer from '../overlays/BlurredOverlayContainer'; import SettingIcon from '../icons/SettingsIcon'; import ExitIcon from '../icons/ExitIcon'; import NightModeSwitch from '../button/NightModeSwitch'; +import Button from '../button/PrimaryButton'; +import { useAuthStore } from '@/zustand/authStore'; +import { useRouter } from 'next/navigation'; type MenuItemType = { href: string | undefined; title: string; - icon: React.ReactElement + icon: React.ReactElement; + auth?: boolean; } const menuItems: Array> = [ @@ -57,10 +61,10 @@ const menuItems: Array> = [ [], [ { - href: undefined, title: 'تنظیمات', icon: + auth: true, href: undefined, title: 'تنظیمات', icon: }, { - href: undefined, title: 'خروج', icon: + auth: true, href: '?logout', title: 'خروج', icon: }, ] ] @@ -74,15 +78,24 @@ type Props = { function SideMenu({ menuState, toggleMenuState, nightModeState, togglenightModeState }: Props) { const menuStateMemo = useMemo(() => menuState, [menuState]); + const [logoutModalVisible, setLogoutModalVisible] = useState(false); const closeRef: React.Ref | undefined = useRef(null); + const userIsAuthenticated = true; //useAuthStore((state) => state.isAuthenticated); + const authLogout = useAuthStore((state) => state.logout); + const router = useRouter(); const variants: Variants = { hidden: { x: '100%', + transition: { + duration: 0.1, + ease: 'easeInOut', + }, }, visible: { x: 0, transition: { + delay: 0.075, duration: 0.1, ease: 'easeInOut', }, @@ -90,51 +103,105 @@ function SideMenu({ menuState, toggleMenuState, nightModeState, togglenightModeS }; const onDrag = (_event: MouseEvent | TouchEvent | PointerEvent, info: PanInfo) => { - if (info.offset.x > 100) { + if (info.offset.x > 20) { if (closeRef.current) { closeRef.current.click(); } } } + const toggleLogoutModalVisiblity = (e: React.MouseEvent | React.MouseEvent | React.MouseEvent | undefined) => { + e?.stopPropagation(); + setLogoutModalVisible((state) => !state); + } + + const onLogout = (e: React.MouseEvent | null) => { + if (!e) return; + if (userIsAuthenticated) { + authLogout(); + router.replace('/'); + } + }; + + const hrefOnClicks: Array<{ href: string, handler: React.MouseEventHandler | undefined }> = [ + { href: '?logout', handler: toggleLogoutModalVisiblity } + ] + return ( -
- - { e.stopPropagation(); }} - className={`fixed top-0 bottom-0 -right-full data-[isvisible=true]:right-0 transition-all duration-100 ease-in-out not-xl:!rounded-s-none overflow-clip bg-white w-[200px] h-dvh flex flex-col z-40`}> -
    -
  • -
    -
    -
    منو
    - {menuItems[0].map((v, i) => { - return - })} + <> +
    + + { e.stopPropagation(); }} + className={`fixed top-0 bottom-0 right-0 ease-in-out not-xl:!rounded-s-none overflow-clip bg-white w-[200px] h-dvh flex flex-col z-40`}> +
      +
    • +
      +
      +
      منو
      + {menuItems[0].filter((x) => x.auth ? userIsAuthenticated && x : x).map((v, i) => { + return + })} +
      -
    -
  • -
  • - {menuItems[2].map((v, i) => { - return - })} - }> -
  • -
-
+ +
  • + {menuItems[2].filter((x) => x.auth ? userIsAuthenticated && x : x).map((v, i) => { + return i.href === v.href)?.handler || undefined} + title={v.title} + icon={v.icon} + /> + })} + }> +
  • + + + +
    +
    + +
    + +
    خروج از سیستم
    +
    آیا میخواهید از سیستم خارج شوید؟
    +
    + + + +
    +
    +
    - + ) } diff --git a/src/components/overlays/BlurredOverlayContainer.tsx b/src/components/overlays/BlurredOverlayContainer.tsx index c475279..5f78156 100644 --- a/src/components/overlays/BlurredOverlayContainer.tsx +++ b/src/components/overlays/BlurredOverlayContainer.tsx @@ -10,6 +10,8 @@ export type BlurredOverlayContainerProps = { inDelay?: number outDelay?: number transitionDelay?: number + inDuration?: number + outDuration?: number children: ReactNode } & React.ComponentProps @@ -17,7 +19,9 @@ const BlurredOverlayContainer = ({ visible = true, bgOpacity = 30, inDelay = 0, - outDelay = 0, + outDelay = 200, + inDuration = 300, + outDuration = 300, transitionDelay: transitionDelay = 0, children, className, @@ -45,7 +49,7 @@ const BlurredOverlayContainer = ({ display: 'none', transition: { delay: transitionDelay / 1000, - duration: 0.3, + duration: outDuration / 1000, ease: 'easeInOut', }, }, @@ -55,7 +59,7 @@ const BlurredOverlayContainer = ({ display: 'block', transition: { delay: transitionDelay / 1000, - duration: 0.3, + duration: inDuration / 1000, ease: 'easeInOut', }, },