Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 10 additions & 0 deletions Frontend/src/App.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -223,6 +223,16 @@ function App() {
initialize();
}, [initialize]);

useEffect(() => {
const savedTheme = localStorage.getItem('theme');
const systemPrefersDark = window.matchMedia('(prefers-color-scheme: dark)').matches;
if (savedTheme === 'dark' || (!savedTheme && systemPrefersDark)) {
document.documentElement.classList.add('dark');
} else {
document.documentElement.classList.remove('dark');
}
}, []);

const isDocsSubdomain = window.location.hostname.startsWith('docs.');

if (isDocsSubdomain) {
Expand Down
54 changes: 40 additions & 14 deletions Frontend/src/admin/components/AdminHeader.jsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import React, { useState, useRef, useEffect } from 'react';
import { Search, Bell, Menu, User, ChevronDown, Settings, LogOut, UserCircle, X, PanelLeftClose, PanelLeftOpen } from 'lucide-react';
import { Search, Bell, Menu, User, ChevronDown, Settings, LogOut, UserCircle, X, PanelLeftClose, PanelLeftOpen, Sun, Moon } from 'lucide-react';
import { useNavigate } from 'react-router-dom';
import NotificationPopover from '../../user/components/NotificationPopover';
import useAuthStore from '../../store/authStore';
Expand All @@ -20,6 +20,23 @@ const AdminHeader = ({ onMobileNavToggle, isSidebarCollapsed, onToggleSidebar })
const { logout, profile: adminProfile } = useAuthStore();
const initials = adminProfile?.full_name ? adminProfile.full_name.split(' ').map(n => n[0]).join('').toUpperCase() : 'AD';

const [isDark, setIsDark] = useState(false);
useEffect(() => {
setIsDark(document.documentElement.classList.contains('dark'));
}, []);

const toggleTheme = () => {
const nextDark = !isDark;
setIsDark(nextDark);
if (nextDark) {
document.documentElement.classList.add('dark');
localStorage.setItem('theme', 'dark');
} else {
document.documentElement.classList.remove('dark');
localStorage.setItem('theme', 'light');
}
};

const handleSearchKeyDown = (e) => {
if (e.key === 'Enter' && searchQuery.trim()) {
navigate(`/admin/tickets?q=${encodeURIComponent(searchQuery.trim())}`);
Expand Down Expand Up @@ -52,12 +69,12 @@ const AdminHeader = ({ onMobileNavToggle, isSidebarCollapsed, onToggleSidebar })
};

return (
<header className="h-16 bg-white border-b border-slate-200 sticky top-0 z-30 px-6 md:px-10 flex items-center justify-between">
<header className="h-16 bg-white dark:bg-[#1a2e24] border-b border-slate-200 dark:border-[#2a4034] sticky top-0 z-30 px-6 md:px-10 flex items-center justify-between transition-colors duration-200">
<div className="flex items-center gap-4 flex-1">
{/* Mobile Menu Toggle */}
<button
onClick={onMobileNavToggle}
className="lg:hidden p-2 hover:bg-slate-50 rounded-xl text-slate-500 transition-colors"
className="lg:hidden p-2 hover:bg-slate-50 dark:hover:bg-slate-800 rounded-xl text-slate-500 dark:text-slate-400 transition-colors"
>
<Menu size={20} />
</button>
Expand All @@ -66,7 +83,7 @@ const AdminHeader = ({ onMobileNavToggle, isSidebarCollapsed, onToggleSidebar })
{onToggleSidebar && (
<button
onClick={onToggleSidebar}
className="hidden md:flex p-2 hover:bg-emerald-50 rounded-xl text-slate-400 hover:text-emerald-600 transition-all"
className="hidden md:flex p-2 hover:bg-emerald-50 dark:hover:bg-emerald-950/20 rounded-xl text-slate-400 hover:text-emerald-600 dark:hover:text-emerald-400 transition-all"
title={isSidebarCollapsed ? 'Expand sidebar' : 'Collapse sidebar'}
>
{isSidebarCollapsed ? <PanelLeftOpen size={18} /> : <PanelLeftClose size={18} />}
Expand All @@ -83,7 +100,7 @@ const AdminHeader = ({ onMobileNavToggle, isSidebarCollapsed, onToggleSidebar })
onChange={(e) => setSearchQuery(e.target.value)}
onKeyDown={handleSearchKeyDown}
placeholder="Search tickets, users… (press Enter)"
className="w-full bg-slate-50/50 border border-slate-200 rounded-xl pl-11 pr-9 py-2 text-sm font-medium tracking-tight focus:outline-none focus:ring-4 focus:ring-emerald-600/5 focus:border-emerald-600 focus:bg-white transition-all text-slate-600 placeholder:text-slate-400"
className="w-full bg-slate-50/50 dark:bg-[#102219] border border-slate-200 dark:border-[#2a4034] rounded-xl pl-11 pr-9 py-2 text-sm font-medium tracking-tight focus:outline-none focus:ring-4 focus:ring-emerald-600/5 focus:border-emerald-600 dark:focus:border-emerald-500 focus:bg-white dark:focus:bg-[#1a2e24] transition-all text-slate-600 dark:text-slate-200 placeholder:text-slate-400 dark:placeholder:text-slate-500"
/>
{searchQuery && (
<button
Expand All @@ -99,50 +116,59 @@ const AdminHeader = ({ onMobileNavToggle, isSidebarCollapsed, onToggleSidebar })

{/* Header Operations */}
<div className="flex items-center gap-4 lg:gap-6">
{/* Theme Switcher */}
<button
onClick={toggleTheme}
className="p-2 hover:bg-slate-100 dark:hover:bg-slate-800 rounded-xl text-slate-500 dark:text-slate-400 transition-colors"
title={isDark ? "Switch to Light Mode" : "Switch to Dark Mode"}
>
{isDark ? <Sun size={18} className="text-amber-500" /> : <Moon size={18} />}
</button>

{/* Communications Hub */}
<div className="relative border-r border-slate-200 pr-4 lg:pr-6 hidden sm:block">
<div className="relative border-r border-slate-200 dark:border-[#2a4034] pr-4 lg:pr-6 hidden sm:block">
<NotificationPopover isAdmin={true} />
</div>

{/* Identity Access & Dropdown */}
<div className="relative" ref={dropdownRef}>
<button
onClick={() => setIsProfileOpen(!isProfileOpen)}
className="flex items-center gap-3 hover:bg-slate-50 p-1 rounded-2xl border border-transparent hover:border-slate-100 transition-all group"
className="flex items-center gap-3 hover:bg-slate-50 dark:hover:bg-slate-800 p-1 rounded-2xl border border-transparent hover:border-slate-100 dark:hover:border-[#2a4034] transition-all group"
>
<Avatar className="w-8 h-8 rounded-lg shadow-md group-hover:scale-105 transition-transform">
<AvatarImage src={adminProfile?.profile_picture} className="object-cover" />
<AvatarFallback className="bg-slate-900 text-white font-black text-xs rounded-lg">
<AvatarFallback className="bg-slate-900 dark:bg-[#102219] text-white dark:text-slate-300 font-black text-xs rounded-lg">
{initials}
</AvatarFallback>
</Avatar>
<div className="hidden lg:block text-left">
<div className="flex items-center gap-1">
<p className="text-xs font-black text-slate-900 tracking-tight leading-none italic uppercase">Admin</p>
<p className="text-xs font-black text-slate-900 dark:text-slate-100 tracking-tight leading-none italic uppercase">Admin</p>
<ChevronDown size={12} className={`text-slate-400 transition-transform duration-300 ${isProfileOpen ? 'rotate-180' : ''}`} />
</div>
</div>
</button>

{/* Dropdown Menu */}
{isProfileOpen && (
<div className="absolute right-0 mt-2 w-48 bg-white border border-slate-200 rounded-2xl shadow-xl shadow-slate-200/50 py-2 animate-in fade-in zoom-in-95 duration-200">
<div className="absolute right-0 mt-2 w-48 bg-white dark:bg-[#1a2e24] border border-slate-200 dark:border-[#2a4034] rounded-2xl shadow-xl shadow-slate-200/50 dark:shadow-slate-950/50 py-2 animate-in fade-in zoom-in-95 duration-200">
<button
onClick={() => { navigate('/admin/profile'); setIsProfileOpen(false); }}
className="w-full flex items-center gap-3 px-4 py-2 text-xs font-bold text-slate-600 hover:bg-slate-50 hover:text-emerald-600 transition-colors"
className="w-full flex items-center gap-3 px-4 py-2 text-xs font-bold text-slate-600 dark:text-slate-300 hover:bg-slate-50 dark:hover:bg-slate-800 hover:text-emerald-600 dark:hover:text-emerald-400 transition-colors"
>
<UserCircle size={16} /> Profile
</button>
<button
onClick={() => { navigate('/admin/settings'); setIsProfileOpen(false); }}
className="w-full flex items-center gap-3 px-4 py-2 text-xs font-bold text-slate-600 hover:bg-slate-50 hover:text-emerald-600 transition-colors"
className="w-full flex items-center gap-3 px-4 py-2 text-xs font-bold text-slate-600 dark:text-slate-300 hover:bg-slate-50 dark:hover:bg-slate-800 hover:text-emerald-600 dark:hover:text-emerald-400 transition-colors"
>
<Settings size={16} /> Settings
</button>
<div className="my-1 border-t border-slate-100"></div>
<div className="my-1 border-t border-slate-100 dark:border-[#2a4034]"></div>
<button
onClick={handleLogout}
className="w-full flex items-center gap-3 px-4 py-2 text-xs font-bold text-red-500 hover:bg-red-50 transition-colors"
className="w-full flex items-center gap-3 px-4 py-2 text-xs font-bold text-red-500 hover:bg-red-50 dark:hover:bg-red-950/20 transition-colors"
>
<LogOut size={16} /> Logout
</button>
Expand Down
2 changes: 1 addition & 1 deletion Frontend/src/admin/layout/AdminLayout.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ const AdminLayout = () => {
const [isSidebarCollapsed, setIsSidebarCollapsed] = useState(false);

return (
<div className="flex h-screen bg-[#f8faf9] overflow-hidden font-sans">
<div className="flex h-screen bg-[#f8faf9] dark:bg-[#102219] text-slate-900 dark:text-slate-100 overflow-hidden font-sans transition-colors duration-200">
{/* Master Navigation Column (Responsive) */}
<div
className={`hidden md:block flex-shrink-0 relative z-40 transition-all duration-300`}
Expand Down
4 changes: 2 additions & 2 deletions Frontend/src/components/shared/BugReportWidget.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -386,10 +386,10 @@ const BugReportWidget = ({ advanced = false, customTrigger = null }) => {
whileHover={{ scale: 1.05 }}
whileTap={{ scale: 0.95 }}
onClick={handleOpen}
className="fixed bottom-6 right-6 z-50 flex items-center gap-2 bg-gradient-to-r from-[#13ec80] to-[#0fd472] text-[#111814] px-4 py-3 rounded-full shadow-lg hover:shadow-xl hover:shadow-[#13ec80]/20 transition-all border border-[#13ec80]/50 group"
className="fixed bottom-4 right-4 md:bottom-6 md:right-6 z-50 flex items-center gap-2 bg-gradient-to-r from-[#13ec80] to-[#0fd472] text-[#111814] p-3 md:px-4 md:py-3 rounded-full shadow-lg hover:shadow-xl hover:shadow-[#13ec80]/20 transition-all border border-[#13ec80]/50 group"
>
<Bug className="w-5 h-5 group-hover:rotate-12 transition-transform" />
<span className="font-bold text-sm hidden sm:block">Report Bug</span>
<span className="font-bold text-sm hidden md:block">Report Bug</span>
</motion.button>
)}
</AnimatePresence>
Expand Down
Loading
Loading