aboutsummaryrefslogtreecommitdiffstats
path: root/frontend/src/app/(main)/layout.tsx
diff options
context:
space:
mode:
authorLibravatarLibravatar Biswa Kalyan Bhuyan <biswa@surgot.in> 2025-04-24 09:33:48 +0530
committerLibravatarLibravatar Biswa Kalyan Bhuyan <biswa@surgot.in> 2025-04-24 09:33:48 +0530
commita3703c30807621ddd4dce7b3cae0918d79fa8de1 (patch)
tree6037877bc281b25245b1cdb7a5d43cc8bb1056c4 /frontend/src/app/(main)/layout.tsx
parentcaace928ac81c284629ee50942d72179d4da9784 (diff)
downloadfinance-a3703c30807621ddd4dce7b3cae0918d79fa8de1.tar.gz
finance-a3703c30807621ddd4dce7b3cae0918d79fa8de1.tar.bz2
finance-a3703c30807621ddd4dce7b3cae0918d79fa8de1.zip
feat: updated advance ui and animation
Diffstat (limited to 'frontend/src/app/(main)/layout.tsx')
-rw-r--r--frontend/src/app/(main)/layout.tsx246
1 files changed, 227 insertions, 19 deletions
diff --git a/frontend/src/app/(main)/layout.tsx b/frontend/src/app/(main)/layout.tsx
index 846a118..5902dd6 100644
--- a/frontend/src/app/(main)/layout.tsx
+++ b/frontend/src/app/(main)/layout.tsx
@@ -6,6 +6,18 @@ import Link from 'next/link';
import { Button } from '@/components/ui/button';
import { authApi, userApi } from '@/lib/api';
import { ThemeToggle } from '@/components/shared/ThemeToggle';
+import { PageTransition } from '@/components/shared/PageTransition';
+import {
+ ChevronLeftIcon,
+ ChevronRightIcon,
+ LayoutDashboardIcon,
+ CoinsIcon,
+ TargetIcon,
+ SettingsIcon,
+ LogOutIcon,
+ MenuIcon
+} from 'lucide-react';
+import { usePathname } from 'next/navigation';
export default function MainLayout({
@@ -14,8 +26,11 @@ export default function MainLayout({
children: React.ReactNode;
}>) {
const router = useRouter();
+ const pathname = usePathname();
const [userName, setUserName] = useState<string>('');
const [isLoading, setIsLoading] = useState(true);
+ const [isSidebarCollapsed, setIsSidebarCollapsed] = useState(false);
+ const [isMobileMenuOpen, setIsMobileMenuOpen] = useState(false);
useEffect(() => {
// Check if user is authenticated
@@ -35,58 +50,251 @@ export default function MainLayout({
// If error fetching profile, redirect to login
router.push('/login');
});
+
+ // Load sidebar state from localStorage
+ const savedSidebarState = localStorage.getItem('sidebarCollapsed');
+ if (savedSidebarState !== null) {
+ setIsSidebarCollapsed(savedSidebarState === 'true');
+ }
}, [router]);
+ // Close mobile menu when changing routes
+ useEffect(() => {
+ setIsMobileMenuOpen(false);
+ }, [pathname]);
+
const handleLogout = () => {
authApi.logout();
};
+ const toggleSidebar = () => {
+ const newState = !isSidebarCollapsed;
+ setIsSidebarCollapsed(newState);
+ localStorage.setItem('sidebarCollapsed', newState.toString());
+ };
+
+ const toggleMobileMenu = () => {
+ setIsMobileMenuOpen(!isMobileMenuOpen);
+ };
+
if (isLoading) {
- return <div className="flex h-screen items-center justify-center">Loading...</div>;
+ return (
+ <div className="flex h-screen items-center justify-center bg-background">
+ <div className="flex flex-col items-center space-y-4">
+ <div className="relative h-12 w-12">
+ <div className="absolute top-0 h-full w-full rounded-full border-4 border-t-primary border-r-transparent border-b-transparent border-l-transparent animate-spin"></div>
+ </div>
+ <p className="text-sm font-medium text-muted-foreground animate-pulse">Loading...</p>
+ </div>
+ </div>
+ );
}
return (
- <div className="min-h-screen bg-background">
+ <div className="min-h-screen bg-background flex flex-col">
{/* Top Navigation */}
- <header className="border-b">
- <div className="w-full flex h-16 items-center px-4">
+ <header className="border-b shadow-sm backdrop-blur-sm bg-background/90 sticky top-0 z-50">
+ <div className="w-full flex h-16 items-center px-4 md:px-6">
<div className="flex items-center gap-4">
- <div className="font-semibold text-xl">Finance Management</div>
+ {/* Mobile menu button - only visible on small screens */}
+ <button
+ className="md:hidden rounded-full p-2 hover:bg-muted transition-colors"
+ onClick={toggleMobileMenu}
+ aria-label="Toggle mobile menu"
+ >
+ <MenuIcon size={20} />
+ </button>
+ <div className="font-semibold text-xl">
+ <span className="bg-clip-text text-transparent bg-gradient-to-r from-primary to-primary/70">
+ Finance Management
+ </span>
+ </div>
</div>
<div className="flex items-center ms-auto gap-4">
- <span className='text-sm font-medium'>Welcome!, {userName}</span>
+ <div className="hidden md:flex items-center gap-1 bg-muted/50 px-3 py-1.5 rounded-full">
+ <span className="relative flex h-2 w-2">
+ <span className="animate-ping absolute inline-flex h-full w-full rounded-full bg-primary/50 opacity-75"></span>
+ <span className="relative inline-flex rounded-full h-2 w-2 bg-primary"></span>
+ </span>
+ <span className='text-sm font-medium ml-1.5 transition-all duration-300'>
+ {userName}
+ </span>
+ </div>
<ThemeToggle />
- <Button variant="ghost" size="sm" onClick={handleLogout}>
- Logout
+ <Button
+ variant="ghost"
+ size="icon"
+ onClick={handleLogout}
+ className="hover:bg-destructive/10 transition-colors duration-300"
+ aria-label="Logout"
+ >
+ <LogOutIcon size={18} className="text-destructive/90 transition-transform hover:scale-110 duration-300" />
</Button>
</div>
</div>
</header>
- {/* Main Content */}
- <div className="container flex flex-row">
- {/* Sidebar */}
- <aside className="w-64 pt-8 pr-6">
- <nav className="space-y-2">
- <Link href="/dashboard" className="block p-2 rounded-md hover:bg-muted">
+ {/* Mobile Menu - outside the normal flow and only visible when toggled */}
+ <div
+ className={`
+ md:hidden fixed inset-0 z-40 bg-background/95 backdrop-blur-sm transition-all duration-300
+ ${isMobileMenuOpen ? 'opacity-100 pointer-events-auto' : 'opacity-0 pointer-events-none'}
+ `}
+ >
+ <div className="pt-20 px-4">
+ <nav className="space-y-4">
+ <Link
+ href="/dashboard"
+ className={`
+ flex items-center p-3 rounded-lg transition-colors
+ ${pathname === '/dashboard' ? 'bg-primary/10 text-primary' : 'hover:bg-muted'}
+ `}
+ >
+ <LayoutDashboardIcon size={20} className="mr-3" />
Dashboard
</Link>
- <Link href="/loans" className="block p-2 rounded-md hover:bg-muted">
+ <Link
+ href="/loans"
+ className={`
+ flex items-center p-3 rounded-lg transition-colors
+ ${pathname === '/loans' ? 'bg-primary/10 text-primary' : 'hover:bg-muted'}
+ `}
+ >
+ <CoinsIcon size={20} className="mr-3" />
Loans
</Link>
- <Link href="/goals" className="block p-2 rounded-md hover:bg-muted">
+ <Link
+ href="/goals"
+ className={`
+ flex items-center p-3 rounded-lg transition-colors
+ ${pathname === '/goals' ? 'bg-primary/10 text-primary' : 'hover:bg-muted'}
+ `}
+ >
+ <TargetIcon size={20} className="mr-3" />
Goals
</Link>
- <Link href="/settings" className="block p-2 rounded-md hover:bg-muted">
+ <Link
+ href="/settings"
+ className={`
+ flex items-center p-3 rounded-lg transition-colors
+ ${pathname === '/settings' ? 'bg-primary/10 text-primary' : 'hover:bg-muted'}
+ `}
+ >
+ <SettingsIcon size={20} className="mr-3" />
Settings
</Link>
</nav>
+ <div className="mt-8 border-t pt-4">
+ <Button
+ variant="outline"
+ className="w-full justify-start text-destructive hover:text-destructive hover:bg-destructive/10"
+ onClick={handleLogout}
+ >
+ <LogOutIcon size={18} className="mr-2" />
+ Logout
+ </Button>
+ </div>
+ </div>
+ </div>
+
+ {/* Main Content */}
+ <div className="flex flex-1 h-[calc(100vh-4rem)]">
+ {/* Sidebar - hidden on mobile */}
+ <aside
+ className={`
+ hidden md:block relative h-full transition-all duration-300 ease-in-out border-r
+ ${isSidebarCollapsed ? 'w-16' : 'w-64'}
+ `}
+ >
+ {/* Sidebar Toggle Button */}
+ <button
+ onClick={toggleSidebar}
+ className="absolute -right-3 top-10 bg-primary text-primary-foreground rounded-full p-1 shadow-md hover:bg-primary/90 transition-colors duration-300 hover:scale-110 group z-10"
+ aria-label={isSidebarCollapsed ? "Expand sidebar" : "Collapse sidebar"}
+ >
+ <div className="transition-transform duration-300 group-hover:rotate-[360deg]">
+ {isSidebarCollapsed ? <ChevronRightIcon size={16} /> : <ChevronLeftIcon size={16} />}
+ </div>
+ </button>
+
+ <nav className="space-y-2 px-2 mt-4">
+ <Link
+ href="/dashboard"
+ className={`
+ flex items-center p-2 rounded-md transition-all duration-200
+ ${pathname === '/dashboard'
+ ? 'bg-primary/10 text-primary font-medium'
+ : 'hover:bg-muted text-foreground/80 hover:text-foreground'
+ }
+ ${isSidebarCollapsed ? 'justify-center' : 'justify-start'}
+ `}
+ title="Dashboard"
+ >
+ <LayoutDashboardIcon size={18} className={`transition-transform duration-300 ${pathname === '/dashboard' ? 'scale-110' : ''}`} />
+ {!isSidebarCollapsed && (
+ <span className="ml-2 transition-opacity duration-300 opacity-100">Dashboard</span>
+ )}
+ </Link>
+ <Link
+ href="/loans"
+ className={`
+ flex items-center p-2 rounded-md transition-all duration-200
+ ${pathname === '/loans'
+ ? 'bg-primary/10 text-primary font-medium'
+ : 'hover:bg-muted text-foreground/80 hover:text-foreground'
+ }
+ ${isSidebarCollapsed ? 'justify-center' : 'justify-start'}
+ `}
+ title="Loans"
+ >
+ <CoinsIcon size={18} className={`transition-transform duration-300 ${pathname === '/loans' ? 'scale-110' : ''}`} />
+ {!isSidebarCollapsed && (
+ <span className="ml-2 transition-opacity duration-300 opacity-100">Loans</span>
+ )}
+ </Link>
+ <Link
+ href="/goals"
+ className={`
+ flex items-center p-2 rounded-md transition-all duration-200
+ ${pathname === '/goals'
+ ? 'bg-primary/10 text-primary font-medium'
+ : 'hover:bg-muted text-foreground/80 hover:text-foreground'
+ }
+ ${isSidebarCollapsed ? 'justify-center' : 'justify-start'}
+ `}
+ title="Goals"
+ >
+ <TargetIcon size={18} className={`transition-transform duration-300 ${pathname === '/goals' ? 'scale-110' : ''}`} />
+ {!isSidebarCollapsed && (
+ <span className="ml-2 transition-opacity duration-300 opacity-100">Goals</span>
+ )}
+ </Link>
+ <Link
+ href="/settings"
+ className={`
+ flex items-center p-2 rounded-md transition-all duration-200
+ ${pathname === '/settings'
+ ? 'bg-primary/10 text-primary font-medium'
+ : 'hover:bg-muted text-foreground/80 hover:text-foreground'
+ }
+ ${isSidebarCollapsed ? 'justify-center' : 'justify-start'}
+ `}
+ title="Settings"
+ >
+ <SettingsIcon size={18} className={`transition-transform duration-300 ${pathname === '/settings' ? 'scale-110' : ''}`} />
+ {!isSidebarCollapsed && (
+ <span className="ml-2 transition-opacity duration-300 opacity-100">Settings</span>
+ )}
+ </Link>
+ </nav>
</aside>
{/* Page Content */}
- <main className="flex-1 p-8">
- {children}
+ <main className="flex-1 p-4 md:p-8 overflow-auto transition-all duration-300">
+ <PageTransition>
+ {children}
+ </PageTransition>
</main>
</div>
</div>