Skip to content
Merged
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
55 changes: 22 additions & 33 deletions packages/web/src/components/Layout.tsx
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import React from 'react';
import { Link, useLocation } from 'react-router-dom';
import { Brain, Bot, BarChart3, Settings, Menu, Server, Globe, Shield, Users, Terminal } from 'lucide-react';
import { Brain, Bot, BarChart3, Settings, Server, Globe, Shield, Users, Terminal } from 'lucide-react';
import { UserSelector } from './UserSelector';
import { GraphSelector } from './GraphSelector';
import { useAuth } from '../contexts/AuthContext';
Expand Down Expand Up @@ -48,41 +48,30 @@ export function Layout({ children }: LayoutProps) {
{/* Static gradient background - optimized for all browsers */}
<div className="lagoon-caustics"></div>

{/* Mobile top bar: hamburger + (on the workspace) the project selector, so the
mobile header is just two rows — project selection here, view bar below. */}
<div className="lg:hidden relative z-30 pt-safe">
<div className="flex items-center gap-2 bg-gray-800/90 backdrop-blur-sm px-3 py-2 border-b border-gray-700/50">
<button
type="button"
className="inline-flex items-center justify-center p-2 rounded-md text-gray-400 hover:text-gray-300 hover:bg-gray-700 flex-shrink-0"
onClick={() => setSidebarOpen(!sidebarOpen)}
>
<Menu className="h-6 w-6" aria-hidden="true" />
</button>
{isWorkspaceRoute ? (
{/* Phone top bar: just the project selector on the workspace (no hamburger —
the bottom nav handles navigation). Other pages use their own header. */}
{isWorkspaceRoute && (
<div className="md:hidden relative z-30 pt-safe">
<div className="flex items-center bg-gray-800/90 backdrop-blur-sm px-3 py-2 border-b border-gray-700/50">
<div className="flex-1 min-w-0">
<GraphSelector />
</div>
) : (
<Link to="/" className="text-lg font-bold text-green-400 hover:text-green-300 transition-colors">
GraphDone
</Link>
)}
</div>
</div>
</div>
)}

<div className="flex flex-1 min-h-0">
{/* Sidebar */}
<div className={`
${sidebarOpen ? 'translate-x-0' : '-translate-x-full'}
lg:translate-x-0 lg:static lg:inset-0
md:translate-x-0 md:static md:inset-0
fixed inset-y-0 left-0 z-50 bg-gray-800/95 backdrop-blur-sm border-r border-gray-700/50
transform transition-all duration-200 ease-in-out
w-64 ${desktopSidebarCollapsed ? 'lg:w-16' : ''}
w-64 ${desktopSidebarCollapsed ? 'md:w-16' : ''}
`}>
<div className="flex flex-col h-full">
{/* Logo */}
<div className={`flex items-center h-16 px-6 border-b border-gray-700 ${desktopSidebarCollapsed ? 'lg:justify-center lg:px-4' : ''}`}>
<div className={`flex items-center h-16 px-6 border-b border-gray-700 ${desktopSidebarCollapsed ? 'md:justify-center md:px-4' : ''}`}>
<Link to="/" className="flex items-center hover:opacity-80 transition-opacity">
<img src="/favicon.svg" alt="GraphDone Logo" className="h-8 w-8" />
{!desktopSidebarCollapsed && (
Expand Down Expand Up @@ -114,22 +103,22 @@ export function Layout({ children }: LayoutProps) {
key={item.name}
className={`
flex items-center px-3 py-3 rounded-lg transition-colors group cursor-not-allowed opacity-50 relative
${desktopSidebarCollapsed ? 'lg:justify-center lg:px-2' : ''}
${desktopSidebarCollapsed ? 'md:justify-center md:px-2' : ''}
`}
title={desktopSidebarCollapsed ? `${item.name}: ${restrictionMessage}` : `${item.description} (${restrictionMessage})`}
>
<Icon className="h-5 w-5 flex-shrink-0 text-gray-500" />
{/* Labels show in the expanded mobile drawer; hidden in the
collapsed desktop (zen) rail, which uses hover tooltips. */}
<div className="flex-1 min-w-0 ml-3 lg:hidden">
<div className="flex-1 min-w-0 ml-3 md:hidden">
<div className="text-sm font-medium text-gray-500">{item.name}</div>
<div className="text-xs text-gray-600 truncate">
{restrictionMessage}
</div>
</div>
{/* Tooltip for collapsed mode */}
{desktopSidebarCollapsed && (
<div className="hidden lg:group-hover:block absolute left-full top-1/2 transform -translate-y-1/2 ml-2 px-2 py-1 bg-gray-800 border border-gray-600 rounded shadow-lg text-sm whitespace-nowrap z-50">
<div className="hidden md:group-hover:block absolute left-full top-1/2 transform -translate-y-1/2 ml-2 px-2 py-1 bg-gray-800 border border-gray-600 rounded shadow-lg text-sm whitespace-nowrap z-50">
<div className="font-medium text-gray-500">{item.name}</div>
<div className="text-xs text-gray-600">{restrictionMessage}</div>
</div>
Expand All @@ -144,7 +133,7 @@ export function Layout({ children }: LayoutProps) {
to={item.href}
className={`
flex items-center px-3 py-3 rounded-lg transition-colors group relative
${desktopSidebarCollapsed ? 'lg:justify-center lg:px-2' : ''}
${desktopSidebarCollapsed ? 'md:justify-center md:px-2' : ''}
${isActive
? 'bg-green-900/30 text-green-300 border border-green-500/30'
: 'text-gray-300 hover:bg-gray-700'
Expand All @@ -156,15 +145,15 @@ export function Layout({ children }: LayoutProps) {
<Icon className="h-5 w-5 flex-shrink-0" />
{/* Labels show in the expanded mobile drawer; hidden in the
collapsed desktop (zen) rail, which uses hover tooltips. */}
<div className="flex-1 min-w-0 ml-3 lg:hidden">
<div className="flex-1 min-w-0 ml-3 md:hidden">
<div className="text-sm font-medium">{item.name}</div>
<div className="text-xs text-gray-400 truncate group-hover:text-gray-300">
{item.description}
</div>
</div>
{/* Tooltip for collapsed mode */}
{desktopSidebarCollapsed && (
<div className="hidden lg:group-hover:block absolute left-full top-1/2 transform -translate-y-1/2 ml-2 px-2 py-1 bg-gray-800 border border-gray-600 rounded shadow-lg text-sm whitespace-nowrap z-50">
<div className="hidden md:group-hover:block absolute left-full top-1/2 transform -translate-y-1/2 ml-2 px-2 py-1 bg-gray-800 border border-gray-600 rounded shadow-lg text-sm whitespace-nowrap z-50">
<div className="font-medium">{item.name}</div>
<div className="text-xs text-gray-400">{item.description}</div>
</div>
Expand All @@ -183,7 +172,7 @@ export function Layout({ children }: LayoutProps) {

{/* Guest Mode Indicator (mobile drawer + expanded desktop) */}
{currentUser?.role === 'GUEST' && (
<div className="border-t border-gray-700 p-4 lg:hidden">
<div className="border-t border-gray-700 p-4 md:hidden">
<div className="bg-purple-900/30 border border-purple-500/30 rounded-lg p-3">
<div className="flex items-center space-x-2">
<Users className="h-4 w-4 text-purple-400" />
Expand All @@ -202,7 +191,7 @@ export function Layout({ children }: LayoutProps) {
</div>

{/* Status Section - Section 3 */}
<div className={`p-4 border-t border-gray-700 ${desktopSidebarCollapsed ? 'lg:px-2' : ''}`}>
<div className={`p-4 border-t border-gray-700 ${desktopSidebarCollapsed ? 'md:px-2' : ''}`}>
{!desktopSidebarCollapsed ? (
<>
{/* MCP Health Indicator */}
Expand All @@ -219,7 +208,7 @@ export function Layout({ children }: LayoutProps) {
</div>

{/* Console & Tools Section - Section 4 */}
<div className={`p-4 border-t border-gray-700/50 ${desktopSidebarCollapsed ? 'lg:px-2' : ''}`}>
<div className={`p-4 border-t border-gray-700/50 ${desktopSidebarCollapsed ? 'md:px-2' : ''}`}>
{!desktopSidebarCollapsed ? (
<>
{/* Debug Console Toggle */}
Expand Down Expand Up @@ -271,7 +260,7 @@ export function Layout({ children }: LayoutProps) {
</div>

{/* Footer Info Section */}
<div className={`p-4 border-t border-gray-700/30 ${desktopSidebarCollapsed ? 'lg:px-2' : ''}`}>
<div className={`p-4 border-t border-gray-700/30 ${desktopSidebarCollapsed ? 'md:px-2' : ''}`}>
{!desktopSidebarCollapsed && (
<>
{currentTeam && (
Expand All @@ -297,7 +286,7 @@ export function Layout({ children }: LayoutProps) {
{/* Overlay for mobile */}
{sidebarOpen && (
<div
className="fixed inset-0 z-40 bg-gray-600 bg-opacity-75 lg:hidden"
className="fixed inset-0 z-40 bg-gray-600 bg-opacity-75 md:hidden"
onClick={() => setSidebarOpen(false)}
/>
)}
Expand Down
22 changes: 20 additions & 2 deletions packages/web/src/components/MobileBottomNav.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ import { useNavigate, useLocation } from 'react-router-dom';
import {
CreditCard, Network, MoreHorizontal, LayoutDashboard, Table, Columns,
GanttChartSquare, CalendarDays, Activity, Brain, Settings as SettingsIcon,
Shield, Server, Bot, BarChart3,
Shield, Server, Bot, BarChart3, LogOut, UserCircle,
} from 'lucide-react';
import { useViewMode, ViewMode } from '../contexts/ViewModeContext';
import { useAuth } from '../contexts/AuthContext';
Expand All @@ -16,7 +16,7 @@ import { useAuth } from '../contexts/AuthContext';
*/
export function MobileBottomNav() {
const { viewMode, setViewMode } = useViewMode();
const { currentUser } = useAuth();
const { currentUser, logout } = useAuth();
const navigate = useNavigate();
const location = useLocation();
const [moreOpen, setMoreOpen] = useState(false);
Expand Down Expand Up @@ -121,6 +121,24 @@ export function MobileBottomNav() {
</button>
))}
</div>

<h3 className="text-xs font-semibold uppercase tracking-wide text-gray-500 mt-4 mb-2 px-1">Account</h3>
<div className="flex items-center justify-between gap-2 rounded-xl border border-gray-700/60 bg-gray-800/60 p-3">
<div className="flex items-center gap-2 min-w-0">
<UserCircle className="h-7 w-7 text-gray-400 flex-shrink-0" />
<div className="min-w-0">
<div className="text-sm font-medium text-gray-100 truncate">{currentUser?.name || currentUser?.username || 'Account'}</div>
{currentUser?.role && <div className="text-[11px] text-gray-400">{currentUser.role}</div>}
</div>
</div>
<button
onClick={() => { logout(); setMoreOpen(false); }}
className="flex items-center gap-1.5 px-3 py-2 rounded-lg bg-red-600/20 border border-red-500/30 text-red-300 text-sm font-medium active:bg-red-600/40 flex-shrink-0"
>
<LogOut className="h-4 w-4" />
Sign out
</button>
</div>
</div>
</div>,
document.body
Expand Down
8 changes: 4 additions & 4 deletions packages/web/src/components/ViewManager.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -396,9 +396,9 @@ const ViewManager: React.FC<ViewManagerProps> = ({ viewMode }) => {
</div>

{/* Filter Dropdowns — 2-up grid on phones so all filters fit; inline on desktop */}
<div className="grid grid-cols-2 gap-2 sm:flex sm:gap-3">
<div className="flex gap-2 sm:gap-3">
{/* Type Filter */}
<div className="relative" ref={typeDropdownRef}>
<div className="hidden sm:block relative" ref={typeDropdownRef}>
<button
type="button"
onClick={() => setIsTypeDropdownOpen(!isTypeDropdownOpen)}
Expand Down Expand Up @@ -544,7 +544,7 @@ const ViewManager: React.FC<ViewManagerProps> = ({ viewMode }) => {
</div>

{/* Priority Filter */}
<div className="relative" ref={priorityDropdownRef}>
<div className="hidden sm:block relative" ref={priorityDropdownRef}>
<button
type="button"
onClick={() => setIsPriorityDropdownOpen(!isPriorityDropdownOpen)}
Expand Down Expand Up @@ -616,7 +616,7 @@ const ViewManager: React.FC<ViewManagerProps> = ({ viewMode }) => {
</div>

{/* Contributors Filter */}
<div className="relative" ref={contributorDropdownRef}>
<div className="hidden sm:block relative" ref={contributorDropdownRef}>
<button
type="button"
onClick={() => setIsContributorDropdownOpen(!isContributorDropdownOpen)}
Expand Down
18 changes: 9 additions & 9 deletions packages/web/src/pages/Workspace.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -81,14 +81,14 @@ export function Workspace() {

return (
<div className="h-full flex flex-col">
{/* Header with Graph Context */}
<div className="bg-gray-900/30 backdrop-blur-md border-b border-gray-700/30 px-3 py-2 sm:px-6 sm:py-4">
{/* Header with Graph Context — tablet/desktop only; phones use the Layout
top bar (project selector) + the bottom nav, so this band is hidden there. */}
<div className="hidden md:block bg-gray-900/30 backdrop-blur-md border-b border-gray-700/30 px-3 py-2 sm:px-6 sm:py-4">
{/* Responsive Layout Container */}
<div className="flex flex-col lg:flex-row lg:items-center lg:justify-between gap-2 sm:gap-4">

{/* Left Section: Graph Selector — desktop only; on mobile the project
selector lives in the global top bar (Layout) to keep the header to 2 rows. */}
<div className="hidden lg:block flex-1 min-w-0 lg:order-1 max-w-lg">
<div className="flex flex-col md:flex-row md:items-center md:justify-between gap-2 sm:gap-4">

{/* Left Section: Graph Selector */}
<div className="flex-1 min-w-0 md:order-1 max-w-lg">
<div className="flex items-center space-x-4 h-full">
{/* Title & Version - Compact */}
<div className="flex flex-col justify-center">
Expand Down Expand Up @@ -122,7 +122,7 @@ export function Workspace() {

{/* Center Section: View Mode Buttons — desktop/tablet only. Phones use the
bottom tab bar (List / Graph / More) instead of this strip. */}
<div className="hidden md:flex justify-start sm:justify-center lg:order-2 overflow-x-auto no-scrollbar -mx-3 px-3 sm:mx-0 sm:px-0">
<div className="flex justify-start lg:justify-center md:order-2 overflow-x-auto no-scrollbar -mx-3 px-3 sm:mx-0 sm:px-0">
<div className="flex flex-nowrap bg-gray-700/50 backdrop-blur-sm rounded-lg p-1.5 sm:p-2 gap-1 border border-gray-600/50">
<button
onClick={() => setViewMode('graph')}
Expand Down Expand Up @@ -241,7 +241,7 @@ export function Workspace() {

{/* Right Section: Status and Actions — hidden on mobile (the hamburger
handles nav there); these chips otherwise eat the small-screen width. */}
<div className="hidden md:flex flex-col lg:flex-row lg:items-center gap-3 lg:order-3">
<div className="flex flex-col lg:flex-row lg:items-center gap-3 md:order-3">
{/* Data-store status (provider-agnostic — keyed off the GraphQL API, which is
the actual data layer; the backing store may be D1, Neo4j, etc.) */}
<div className={`flex items-center gap-2 px-3 py-2 rounded-lg text-xs transition-all cursor-help ${
Expand Down
Loading