Skip to content

Commit 543d1ac

Browse files
authored
Merge pull request #67 from PentabyteDevAlign/refactor
ref: layout and some fix
2 parents 7961d02 + c26ab86 commit 543d1ac

8 files changed

Lines changed: 404 additions & 195 deletions

File tree

Frontend/src/components/Loading.jsx

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -17,10 +17,10 @@ export default function Loading({
1717
>
1818
<div className="flex flex-col justify-center items-center h-full">
1919
<div className="relative">
20-
<div className="animate-spin rounded-full h-16 w-16 border-b-4 border-blue-600"></div>
20+
<div className="animate-spin rounded-full h-16 w-16 border-b-4 border-primer"></div>
2121

2222
<div className="absolute inset-0 flex items-center justify-center">
23-
<FolderKanban className="w-6 h-6 text-blue-600" />
23+
<FolderKanban className="w-6 h-6 text-primer" />
2424
</div>
2525
</div>
2626
<p className="mt-4 text-white font-medium">{text}</p>

Frontend/src/components/layouts/AppNavbar.jsx

Lines changed: 164 additions & 52 deletions
Original file line numberDiff line numberDiff line change
@@ -1,15 +1,21 @@
1+
import { useState, useEffect } from "react";
2+
import { Link, useNavigate } from "react-router-dom";
13
import { SidebarTrigger } from "@/components/ui/sidebar";
2-
import { Bell, Search, User, ChevronDown } from "lucide-react";
3-
import { useEffect, useState } from "react";
4+
import { Badge } from "@/components/ui/badge";
5+
import {
6+
DropdownMenu,
7+
DropdownMenuContent,
8+
DropdownMenuItem,
9+
DropdownMenuSeparator,
10+
DropdownMenuTrigger,
11+
} from "@/components/ui/dropdown-menu";
12+
import { Bell, ChevronDown, LogOut, KeyRound, UserCircle } from "lucide-react";
413
import { useAuthStore } from "@/store/useAuthStore";
514
import { useNotifCountStore } from "@/store/useNotifCountStore";
6-
import { Link, useNavigate } from "react-router-dom";
715

816
export default function AppNavbar() {
9-
const [isProfileOpen, setIsProfileOpen] = useState(false);
1017
const { logout, name, role } = useAuthStore();
1118
const { unreadCount, fetchUnreadCount } = useNotifCountStore();
12-
1319
const navigate = useNavigate();
1420

1521
const handleLogout = () => {
@@ -20,60 +26,166 @@ export default function AppNavbar() {
2026
fetchUnreadCount();
2127
}, [fetchUnreadCount]);
2228

29+
// Get initials from name
30+
const getInitials = (name) => {
31+
if (!name) return "U";
32+
return name
33+
.split(" ")
34+
.map((n) => n[0])
35+
.join("")
36+
.toUpperCase()
37+
.slice(0, 2);
38+
};
39+
40+
// Get role badge color
41+
const getRoleBadgeColor = (role) => {
42+
switch (role?.toLowerCase()) {
43+
case "hr":
44+
return "bg-purple-100 text-purple-700 border-purple-200";
45+
case "manager":
46+
return "bg-blue-100 text-blue-700 border-blue-200";
47+
case "staff":
48+
return "bg-green-100 text-green-700 border-green-200";
49+
default:
50+
return "bg-gray-100 text-gray-700 border-gray-200";
51+
}
52+
};
53+
2354
return (
24-
<header className="flex justify-between sticky p-5 top-0 z-40 h-20 items-center gap-4 border-b bg-tersier shadow-sm">
25-
<SidebarTrigger className="text-gray-700 hover:text-sekunder cursor-pointer" />
26-
<div className="flex items-center gap-4">
27-
<button
28-
onClick={() => navigate("/announcement")}
29-
className="relative rounded-lg p-2 text-gray-600 hover:bg-gray-100 hover:text-gray-900 cursor-pointer"
30-
>
31-
<Bell className="h-5 w-5" />
32-
{unreadCount > 0 && (
33-
<span className="absolute -top-0.5 -right-0.5 flex h-4 w-4 items-center justify-center rounded-full bg-red-500 text-[10px] font-bold text-white">
34-
{unreadCount > 9 ? "9+" : unreadCount}
35-
</span>
36-
)}
37-
</button>
55+
<header className="sticky top-0 z-40 border-b border-gray-200 bg-gradient-to-bl from-blue-50 via-purple-50 to-pink-50 backdrop-blur-lg shadow-sm py-3.5">
56+
<div className="flex h-16 items-center justify-between px-4 md:px-6">
57+
{/* Left Section */}
58+
<div className="flex items-center gap-4">
59+
<SidebarTrigger className="text-gray-600 hover:text-blue-600 hover:bg-blue-50 rounded-lg p-2 transition-all duration-200 hover:scale-105 cursor-pointer" />
60+
</div>
3861

39-
<div className="relative">
62+
{/* Right Section */}
63+
<div className="flex items-center gap-3">
64+
{/* Notification Button */}
4065
<button
41-
onClick={() => setIsProfileOpen(!isProfileOpen)}
42-
className="flex items-center gap-2 rounded-lg px-3 py-2 text-sm hover:bg-gray-100 cursor-pointer"
66+
onClick={() => navigate("/announcement")}
67+
className="relative rounded-xl p-2.5 text-gray-600 hover:bg-gradient-to-r hover:from-blue-50 hover:to-purple-50 hover:text-blue-600 transition-all duration-200 hover:scale-105 group cursor-pointer"
4368
>
44-
<div className="h-8 w-8 rounded-full bg-teal-100 flex items-center justify-center ">
45-
<User className="h-4 w-4 text-teal-600" />
46-
</div>
47-
<div className="text-left hidden md:block">
48-
<p className="text-sm font-medium text-gray-700">{name}</p>
49-
<p className="text-xs text-gray-500">{role}</p>
50-
</div>
51-
<ChevronDown className="h-4 w-4 text-gray-500" />
69+
<Bell className="h-5 w-5" />
70+
{unreadCount > 0 && (
71+
<span className="absolute -top-1 -right-1 flex h-5 w-5 items-center justify-center rounded-full bg-gradient-to-r from-red-500 to-pink-500 text-[10px] font-bold text-white shadow-lg animate-pulse">
72+
{unreadCount > 9 ? "9+" : unreadCount}
73+
</span>
74+
)}
75+
{/* Tooltip */}
76+
<span className="absolute -bottom-8 right-0 px-2 py-1 bg-gray-900 text-white text-xs rounded-md opacity-0 group-hover:opacity-100 transition-opacity whitespace-nowrap pointer-events-none">
77+
Announcements
78+
</span>
5279
</button>
5380

54-
{isProfileOpen && (
55-
<div className="absolute right-0 mt-2 w-48 rounded-lg border border-gray-200 bg-white shadow-lg py-1">
56-
<Link
57-
to="/profile"
58-
className="block px-4 py-2 text-sm text-gray-700 hover:bg-gray-100"
59-
>
60-
Profile
61-
</Link>
62-
<Link
63-
to="/change-password"
64-
className="block px-4 py-2 text-sm text-gray-700 hover:bg-gray-100"
65-
>
66-
Change Password
67-
</Link>
68-
<hr className="my-1" />
69-
<div
70-
onClick={handleLogout}
71-
className="block px-4 py-2 text-sm text-red-600 hover:bg-gray-100 cursor-pointer"
72-
>
73-
Logout
81+
{/* Profile Dropdown */}
82+
<DropdownMenu>
83+
<DropdownMenuTrigger asChild>
84+
<button className="flex items-center gap-3 rounded-xl px-3 py-2 hover:bg-gradient-to-br hover:from-blue-100 hover:to-purple-100 transition-all duration-200 group outline-none cursor-pointer">
85+
<div className="relative">
86+
<div className="h-9 w-9 rounded-full bg-gradient-to-br from-blue-500 to-purple-500 flex items-center justify-center text-white text-sm font-bold shadow-md group-hover:shadow-lg transition-shadow">
87+
{getInitials(name)}
88+
</div>
89+
<div className="absolute -bottom-0.5 -right-0.5 h-3 w-3 rounded-full bg-green-500 border-2 border-white"></div>
90+
</div>
91+
<div className="text-left hidden lg:block">
92+
<p className="text-sm font-semibold text-gray-900">
93+
{name || "User Name"}
94+
</p>
95+
<div className="flex items-center gap-1">
96+
<Badge
97+
variant="outline"
98+
className={`text-[10px] px-1.5 py-0 h-4 ${getRoleBadgeColor(
99+
role
100+
)}`}
101+
>
102+
{role || "User"}
103+
</Badge>
104+
</div>
105+
</div>
106+
<ChevronDown className="h-4 w-4 text-gray-500 hidden md:block group-hover:text-blue-600 transition-colors" />
107+
</button>
108+
</DropdownMenuTrigger>
109+
110+
<DropdownMenuContent
111+
align="end"
112+
className="w-64 p-2 bg-white border border-gray-200 shadow-xl rounded-xl"
113+
>
114+
{/* User Info Header */}
115+
<div className="px-3 py-3 mb-2 bg-gradient-to-r from-blue-50 to-purple-50 rounded-lg">
116+
<div className="flex items-center gap-3">
117+
<div className="h-12 w-12 rounded-full bg-gradient-to-br from-blue-500 to-purple-500 flex items-center justify-center text-white text-base font-bold shadow-md">
118+
{getInitials(name)}
119+
</div>
120+
<div className="flex-1 min-w-0">
121+
<p className="text-sm font-semibold text-gray-900 truncate">
122+
{name || "User Name"}
123+
</p>
124+
<Badge
125+
variant="outline"
126+
className={`text-xs mt-1 ${getRoleBadgeColor(role)}`}
127+
>
128+
{role || "User"}
129+
</Badge>
130+
</div>
131+
</div>
74132
</div>
75-
</div>
76-
)}
133+
134+
<DropdownMenuSeparator className="my-2" />
135+
136+
{/* Menu Items */}
137+
<DropdownMenuItem asChild>
138+
<Link
139+
to="/profile"
140+
className="flex items-center gap-3 px-3 py-2.5 rounded-lg text-sm font-medium text-gray-700 hover:bg-gradient-to-r hover:from-blue-50 hover:to-purple-50 hover:text-blue-600 transition-all cursor-pointer"
141+
>
142+
<div className="p-1.5 bg-blue-100 rounded-lg">
143+
<UserCircle className="h-4 w-4 text-blue-600" />
144+
</div>
145+
<div>
146+
<p className="font-medium">Profile</p>
147+
<p className="text-xs text-gray-500">View your profile</p>
148+
</div>
149+
</Link>
150+
</DropdownMenuItem>
151+
152+
<DropdownMenuItem asChild>
153+
<Link
154+
to="/change-password"
155+
className="flex items-center gap-3 px-3 py-2.5 rounded-lg text-sm font-medium text-gray-700 hover:bg-gradient-to-r hover:from-blue-50 hover:to-purple-50 hover:text-blue-600 transition-all cursor-pointer"
156+
>
157+
<div className="p-1.5 bg-purple-100 rounded-lg">
158+
<KeyRound className="h-4 w-4 text-purple-600" />
159+
</div>
160+
<div>
161+
<p className="font-medium">Change Password</p>
162+
<p className="text-xs text-gray-500">
163+
Update your password
164+
</p>
165+
</div>
166+
</Link>
167+
</DropdownMenuItem>
168+
169+
<DropdownMenuSeparator className="my-2" />
170+
171+
<DropdownMenuItem asChild>
172+
<button
173+
onClick={handleLogout}
174+
className="w-full flex items-center gap-3 px-3 py-2.5 rounded-lg text-sm font-medium text-red-600 hover:bg-red-50 transition-all cursor-pointer"
175+
>
176+
<div className="p-1.5 bg-red-100 rounded-lg">
177+
<LogOut className="h-4 w-4 text-red-600" />
178+
</div>
179+
<div className="text-left">
180+
<p className="font-medium">Logout</p>
181+
<p className="text-xs text-red-500">
182+
Sign out of your account
183+
</p>
184+
</div>
185+
</button>
186+
</DropdownMenuItem>
187+
</DropdownMenuContent>
188+
</DropdownMenu>
77189
</div>
78190
</div>
79191
</header>

0 commit comments

Comments
 (0)