From 64f4942222e1ab414caa068b447f4ddd8fa5ddde Mon Sep 17 00:00:00 2001 From: mitkapanarin Date: Sat, 7 Dec 2024 08:30:26 +0100 Subject: [PATCH 1/5] updated Navigation with user auth and content panel features --- .../reusable-components/_Types/index.ts | 6 + .../navigation/Navigation.tsx | 140 ++++++++++++++---- .../navigation/navigation.module.scss | 121 +++++++++++++-- package-lock.json | 15 ++ package.json | 1 + public/user.png | Bin 0 -> 3390 bytes 6 files changed, 244 insertions(+), 39 deletions(-) create mode 100644 public/user.png diff --git a/components/reusable-components/_Types/index.ts b/components/reusable-components/_Types/index.ts index 9027b54e..ef606c93 100644 --- a/components/reusable-components/_Types/index.ts +++ b/components/reusable-components/_Types/index.ts @@ -44,3 +44,9 @@ export interface BlogDetailsData { publishDate: string; tags: string[]; } + +export interface User { + name: string; + email: string; + avatar: string; +} diff --git a/components/reusable-components/navigation/Navigation.tsx b/components/reusable-components/navigation/Navigation.tsx index 1b0755cd..f6c0f88a 100644 --- a/components/reusable-components/navigation/Navigation.tsx +++ b/components/reusable-components/navigation/Navigation.tsx @@ -3,53 +3,139 @@ 'use client'; -import React from 'react'; +import React, { useState, useEffect, useRef } from 'react'; import Link from 'next/link'; import Image from 'next/image'; -import 'bootstrap-icons/font/bootstrap-icons.css'; - -import styles from './navigation.module.scss'; -import { useTheme } from '../../../app/context/themeContext'; +import { usePathname } from 'next/navigation'; +import { FaUser, FaSignOutAlt, FaChevronDown, FaChevronUp, FaEnvelope } from 'react-icons/fa'; import sunImage from '../../../public/theme/sun.png'; import moonImage from '../../../public/theme/moon.png'; import LogoNavigation from '../../../public/logo/logo-black.svg'; +import styles from './navigation.module.scss'; +import { useTheme } from '../../../app/context/themeContext'; +import { User } from '../_Types'; + +const user: User = { + name: 'John Doe', + email: 'john@example.com', + avatar: '/user.png', +}; -const Navigation = () => { +const Navigation: React.FC = () => { const { theme, toggleTheme } = useTheme(); + const [isMenuOpen, setIsMenuOpen] = useState(false); + const menuRef = useRef(null); + const pathname = usePathname(); + + const isContentPanel = pathname.startsWith('/content-panel'); const isSun = theme === 'light'; + useEffect(() => { + const handleClickOutside = (event: MouseEvent) => { + if (menuRef.current && !menuRef.current.contains(event.target as Node)) { + setIsMenuOpen(false); + } + }; + + document.addEventListener('mousedown', handleClickOutside); + return () => document.removeEventListener('mousedown', handleClickOutside); + }, []); + + const renderAvatarMenu = () => ( +
+ + {user.name} + +
+ {user.email} +
+ +
+ ); + + const handleKeyDown = (callback: () => void) => (e: React.KeyboardEvent) => { + if (e.key === 'Enter' || e.key === ' ') { + callback(); + } + }; + return ( - ); }; + export default Navigation; From 477223a887a3fa7a9e4c1cbbddde23e1a35de7fb Mon Sep 17 00:00:00 2001 From: mitkapanarin Date: Sun, 15 Dec 2024 16:19:55 +0100 Subject: [PATCH 3/5] Refactor navigation routes --- .../reusable-components/_Types/index.ts | 8 ++- .../button/LogoutButton.tsx | 13 +++- .../navigation/Navigation.tsx | 64 +++++++++++-------- 3 files changed, 54 insertions(+), 31 deletions(-) diff --git a/components/reusable-components/_Types/index.ts b/components/reusable-components/_Types/index.ts index 1c951449..be59aa51 100644 --- a/components/reusable-components/_Types/index.ts +++ b/components/reusable-components/_Types/index.ts @@ -45,10 +45,16 @@ export interface BlogDetailsData { tags: string[]; } +export enum UserStatus { + Active = 'active', + Banned = 'banned', + Deleted = 'deleted', +} + export interface User { email: string; first_name: string; last_name: string; - status: 'active' | 'banned' | 'deleted'; + status: UserStatus; image: File | null; } diff --git a/components/reusable-components/button/LogoutButton.tsx b/components/reusable-components/button/LogoutButton.tsx index ccb4d33b..f4ff8fd4 100644 --- a/components/reusable-components/button/LogoutButton.tsx +++ b/components/reusable-components/button/LogoutButton.tsx @@ -1,13 +1,20 @@ 'use client'; -import React from 'react'; +import React, { ReactElement } from 'react'; import useLogout from '../../../apis/mutations/login/useLogout'; -const LogoutButton = ({ redirectUrl }: { redirectUrl: string }) => { +interface LogoutButtonProps { + redirectUrl: string; + className?: string; + icon?: ReactElement; +} + +const LogoutButton: React.FC = ({ redirectUrl, className, icon }) => { const logout = useLogout(redirectUrl); return ( - ); diff --git a/components/reusable-components/navigation/Navigation.tsx b/components/reusable-components/navigation/Navigation.tsx index 0412215f..0d6cbc5d 100644 --- a/components/reusable-components/navigation/Navigation.tsx +++ b/components/reusable-components/navigation/Navigation.tsx @@ -1,36 +1,38 @@ -/* eslint-disable jsx-a11y/no-static-element-interactions */ -/* eslint-disable jsx-a11y/click-events-have-key-events */ - 'use client'; import React, { useState, useRef } from 'react'; import Link from 'next/link'; import Image from 'next/image'; import { usePathname } from 'next/navigation'; +import { useSession } from 'next-auth/react'; import { FaUser, FaSignOutAlt, FaChevronDown, FaChevronUp, FaEnvelope } from 'react-icons/fa'; +import { Session } from 'next-auth'; import sunImage from '../../../public/theme/sun.png'; import moonImage from '../../../public/theme/moon.png'; import LogoNavigation from '../../../public/logo/logo-black.svg'; import styles from './navigation.module.scss'; import { useTheme } from '../../../app/context/themeContext'; -import { User } from '../_Types'; +import { User, UserStatus } from '../_Types'; import useClickOutside from '../../../api/utils/HOC/useClickOutside'; import LogoutButton from '../button/LogoutButton'; -const user: User = { - email: 'john@example.com', - first_name: 'John', - last_name: 'Doe', - status: 'active', - image: null, -}; +function extractUserFromSession(session: Session | null): User { + return { + email: typeof session?.user?.email === 'string' ? session.user.email : '', + firstName: typeof session?.user?.name === 'string' ? session.user.name.split(' ')[0] : 'Name', + lastName: typeof session?.user?.name === 'string' ? session.user.name.split(' ')[1] || '' : '', + status: UserStatus.Active, + image: null, + }; +} const Navigation = () => { + const { data: session } = useSession(); + const user: User = extractUserFromSession(session); const { theme, toggleTheme } = useTheme(); const [isMenuOpen, setIsMenuOpen] = useState(false); const menuRef = useRef(null); const pathname = usePathname(); - const isContentPanel = pathname.startsWith('/content-panel'); const isSun = theme === 'light'; @@ -41,7 +43,7 @@ const Navigation = () => { const renderAvatarMenu = () => (
- {`${user.first_name} ${user.last_name}`} + {`${user.firstName} ${user.lastName}`}
{user.email} @@ -56,27 +58,35 @@ const Navigation = () => { } }; + const getImageSrc = (image: string | File | null): string => { + if (typeof image === 'string') return image; + if (image instanceof File) return URL.createObjectURL(image); + return '/user.png'; + }; + + const contentPanelRoutes = [ + { path: '/content-panel/blogs', name: 'Blogs' }, + { path: '/content-panel/tags', name: 'Tags' }, + ]; + return (