diff --git a/apps/web-dashboard/src/components/Database/DatabaseHeader.jsx b/apps/web-dashboard/src/components/Database/DatabaseHeader.jsx index 50c633519..39b7d699d 100644 --- a/apps/web-dashboard/src/components/Database/DatabaseHeader.jsx +++ b/apps/web-dashboard/src/components/Database/DatabaseHeader.jsx @@ -15,10 +15,13 @@ const DatabaseHeader = ({
- -
+ +
{activeCollection?.name !== 'users' && (
{` /* Sidebar Styles - Scoped */ - .db-sidebar { + .db-sidebar { width: 280px; - background: transparent; - border-right: none; + background: var(--color-bg-sidebar); + border-right: 1px solid var(--color-border); display: flex; flex-direction: column; z-index: 100; @@ -137,10 +137,11 @@ export default function DatabaseSidebar({ } .badge { - background: rgba(255,255,255,0.1); + background: var(--color-surface-hover-strong); + border: 1px solid var(--color-border); padding: 2px 6px; - border-radius: 4px; - color: white; + border-radius: 12px; + color: var(--color-text-main); font-size: 0.7rem; } @@ -164,7 +165,7 @@ export default function DatabaseSidebar({ } .collection-item:hover { - background: rgba(255,255,255,0.03); + background: var(--color-surface-hover); color: var(--color-text-main); } diff --git a/apps/web-dashboard/src/components/Layout/Header.jsx b/apps/web-dashboard/src/components/Layout/Header.jsx index cdfa48c55..cd4fe2a6e 100644 --- a/apps/web-dashboard/src/components/Layout/Header.jsx +++ b/apps/web-dashboard/src/components/Layout/Header.jsx @@ -1,10 +1,74 @@ +import { useState, useEffect, useRef } from 'react'; +import { useParams, Link, useSearchParams, useNavigate, useLocation } from 'react-router-dom'; import { useAuth } from '../../context/AuthContext'; -import { Menu } from 'lucide-react'; // Import Menu Icon +import { Menu, ChevronRight, Search } from 'lucide-react'; +import api from '../../utils/api'; -function Header({ onToggleSidebar, showToggle = true, children }) { // Default showToggle to true +function Header({ onToggleSidebar, showToggle = true, isSidebarCollapsed = false }) { const { user } = useAuth(); + const { projectId } = useParams(); + const [projectName, setProjectName] = useState(''); + const [searchParams, setSearchParams] = useSearchParams(); + const navigate = useNavigate(); + const location = useLocation(); + + const [inputValue, setInputValue] = useState(searchParams.get('q') || ''); + const searchInputRef = useRef(null); const initial = user?.email ? user.email[0].toUpperCase() : 'D'; + useEffect(() => { + let isMounted = true; + if (!projectId) { + queueMicrotask(() => { + if (isMounted) setProjectName(''); + }); + return; + } + api.get(`/api/projects/${projectId}`) + .then(res => { + if (isMounted) setProjectName(res.data.name); + }) + .catch(err => console.error("Failed to fetch project name for header:", err)); + return () => { isMounted = false; }; + }, [projectId]); + + // Sync input value with URL search param + useEffect(() => { + let isMounted = true; + queueMicrotask(() => { + if (isMounted) { + setInputValue(searchParams.get('q') || ''); + } + }); + return () => { isMounted = false; }; + }, [searchParams]); + + // Keyboard shortcut to focus search input + useEffect(() => { + const handleKeyPress = (e) => { + if ((e.metaKey || e.ctrlKey) && e.key === 'k') { + e.preventDefault(); + searchInputRef.current?.focus(); + } + }; + window.addEventListener('keydown', handleKeyPress); + return () => window.removeEventListener('keydown', handleKeyPress); + }, []); + + const handleSearchChange = (e) => { + const val = e.target.value; + setInputValue(val); + if (location.pathname === '/dashboard') { + setSearchParams(val ? { q: val } : {}); + } + }; + + const handleSearchKeyDown = (e) => { + if (e.key === 'Enter') { + navigate(`/dashboard?q=${encodeURIComponent(inputValue)}`); + } + }; + return (
{/* CSS override for mobile padding in style tag below */} @@ -35,6 +98,9 @@ function Header({ onToggleSidebar, showToggle = true, children }) { // Default s .mobile-toggle { display: block !important; } + .search-container { + display: none !important; /* Hide search bar on mobile headers to save space */ + } } .mobile-toggle { display: none; @@ -42,28 +108,70 @@ function Header({ onToggleSidebar, showToggle = true, children }) { // Default s `} {/* Mobile Menu Button - Only show if toggle is allowed */} - {showToggle && ( - - )} +
+ {showToggle && ( + + )} + + {/* Breadcrumbs for Project View */} + {projectId && projectName && ( +
+ + Personal + + + + {projectName} + +
+ )} +
- {/* Search / Center Content Slot */} -
- {children} + {/* Persistent Global Search Input */} +
+
+ + +
+ {navigator.platform.includes('Mac') ? '⌘ K' : 'Ctrl K'} +
+
{/* User Profile */}
- + {user?.email || 'Dev'}
{ + setIsSidebarCollapsed(prev => { + const nextVal = !prev; + try { + localStorage.setItem('urbackend-sidebar-collapsed', String(nextVal)); + } catch (err) { + console.warn("localStorage not accessible", err); + } + return nextVal; + }); + }; + return ( -
+
{/* Mobile Overlay - Only visible when sidebar is open on mobile */} - {isSidebarOpen && !isProjectRoute && ( + {isSidebarOpen && (
setIsSidebarOpen(false)} >
)} - {/* Sidebar - Only show if NOT in a project route (or if we want global sidebar always, but plan said hide it) */} - {!isProjectRoute && ( - setIsSidebarOpen(false)} - /> - )} + {/* Sidebar - Always visible */} + setIsSidebarOpen(false)} + isCollapsed={isSidebarCollapsed} + onToggleCollapse={toggleSidebarCollapse} + /> {/* Main Content Area */} - {/* If Project Route, remove margin-left (full width) */} - {/* Add paddingTop to account for fixed global header only if not in project route */} -
+
- {/* Global Header */} - {!isProjectRoute && ( -
setIsSidebarOpen(!isSidebarOpen)} - // Hide toggle button if sidebar is hidden - showToggle={true} - > - {headerContent} -
- )} - - {/* Project Navigation Bar - Only visible in project routes */} - {isProjectRoute && } + {/* Global Header - Always visible */} +
setIsSidebarOpen(!isSidebarOpen)} + showToggle={true} + isSidebarCollapsed={isSidebarCollapsed} + > + {headerContent} +
{/* Dynamic Page Content */} - {/* Remove default margin-top as main-content has padding now. Remove padding for Database page. */}
-
- - - Back - -
-
- - -
- ); -} - -export default ProjectNavbar; diff --git a/apps/web-dashboard/src/components/Layout/Sidebar.jsx b/apps/web-dashboard/src/components/Layout/Sidebar.jsx index 4e7f6718f..a6ce7c2ed 100644 --- a/apps/web-dashboard/src/components/Layout/Sidebar.jsx +++ b/apps/web-dashboard/src/components/Layout/Sidebar.jsx @@ -2,16 +2,21 @@ import { Link, useLocation, useParams } from 'react-router-dom'; import { useAuth } from '../../context/AuthContext'; import { LayoutDashboard, Database, Shield, HardDrive, Settings, BarChart2, - ArrowLeft, LogOut, X, Rocket, Webhook, Users + ArrowLeft, LogOut, X, Rocket, Webhook, Users, Mail, ChevronLeft, ChevronRight } from 'lucide-react'; import ThemeToggle from '../ThemeToggle'; -function Sidebar({ logo, isOpen, onClose }) { +function Sidebar({ logo, isOpen, onClose, isCollapsed, onToggleCollapse }) { const location = useLocation(); const { projectId } = useParams(); const { logout } = useAuth(); - const isActive = (path) => location.pathname === path; + const isActive = (path) => { + if (path === `/project/${projectId}`) { + return location.pathname === path; + } + return location.pathname.startsWith(path); + }; const handleNavClick = () => { if (window.innerWidth <= 768) onClose(); @@ -23,17 +28,17 @@ function Sidebar({ logo, isOpen, onClose }) { }); return ( -