diff --git a/frontend/src/components/auth/LoginForm.tsx b/frontend/src/components/auth/LoginForm.tsx index d9b3a2c..ca40fb0 100644 --- a/frontend/src/components/auth/LoginForm.tsx +++ b/frontend/src/components/auth/LoginForm.tsx @@ -13,7 +13,8 @@ export function LoginForm() { const [password, setPassword] = useState('') const [error, setError] = useState('') const [loading, setLoading] = useState(false) - const { signIn } = useAuth() + const [oauthLoading, setOauthLoading] = useState<'github' | 'google' | null>(null) + const { signIn, signInWithGitHub, signInWithGoogle } = useAuth() const navigate = useNavigate() const handleSubmit = async (e: React.FormEvent) => { @@ -31,6 +32,30 @@ export function LoginForm() { } } + const handleGitHubSignIn = async () => { + setError('') + setOauthLoading('github') + try { + await signInWithGitHub() + } catch (err: any) { + setError(err.message || 'GitHub sign in failed') + } finally { + setOauthLoading(null) + } + } + + const handleGoogleSignIn = async () => { + setError('') + setOauthLoading('google') + try { + await signInWithGoogle() + } catch (err: any) { + setError(err.message || 'Google sign in failed') + } finally { + setOauthLoading(null) + } + } + return (
@@ -118,20 +143,69 @@ export function LoginForm() {
- or + or continue with
- +
+ + +

diff --git a/frontend/src/components/auth/SignupForm.tsx b/frontend/src/components/auth/SignupForm.tsx index 79bdb94..8f4a8b4 100644 --- a/frontend/src/components/auth/SignupForm.tsx +++ b/frontend/src/components/auth/SignupForm.tsx @@ -14,7 +14,8 @@ export function SignupForm() { const [confirmPassword, setConfirmPassword] = useState('') const [error, setError] = useState('') const [loading, setLoading] = useState(false) - const { signUp } = useAuth() + const [oauthLoading, setOauthLoading] = useState<'github' | 'google' | null>(null) + const { signUp, signInWithGitHub, signInWithGoogle } = useAuth() const navigate = useNavigate() const handleSubmit = async (e: React.FormEvent) => { @@ -42,6 +43,30 @@ export function SignupForm() { } } + const handleGitHubSignIn = async () => { + setError('') + setOauthLoading('github') + try { + await signInWithGitHub() + } catch (err: any) { + setError(err.message || 'GitHub sign in failed') + } finally { + setOauthLoading(null) + } + } + + const handleGoogleSignIn = async () => { + setError('') + setOauthLoading('google') + try { + await signInWithGoogle() + } catch (err: any) { + setError(err.message || 'Google sign in failed') + } finally { + setOauthLoading(null) + } + } + return (

@@ -146,20 +171,69 @@ export function SignupForm() {
- or + or continue with
- +
+ + +

diff --git a/frontend/src/components/dashboard/DashboardHome.tsx b/frontend/src/components/dashboard/DashboardHome.tsx index 62a4c04..d472877 100644 --- a/frontend/src/components/dashboard/DashboardHome.tsx +++ b/frontend/src/components/dashboard/DashboardHome.tsx @@ -117,7 +117,7 @@ export function DashboardHome() { ] as const return ( -

+
{/* Repository List View */} {!isRepoView && ( diff --git a/frontend/src/components/dashboard/DashboardLayout.tsx b/frontend/src/components/dashboard/DashboardLayout.tsx index 29af84b..edaeda4 100644 --- a/frontend/src/components/dashboard/DashboardLayout.tsx +++ b/frontend/src/components/dashboard/DashboardLayout.tsx @@ -1,5 +1,5 @@ import { useState, useEffect } from 'react' -import { Outlet } from 'react-router-dom' +import { Outlet, useLocation } from 'react-router-dom' import { Sidebar } from './Sidebar' import { TopNav } from './TopNav' import { CommandPalette } from './CommandPalette' @@ -15,7 +15,9 @@ const SIDEBAR_STORAGE_KEY = 'codeintel-sidebar-collapsed' export function DashboardLayout({ children }: DashboardLayoutProps) { const { theme } = useTheme() + const location = useLocation() + // Desktop: collapsed state (narrow sidebar) const [sidebarCollapsed, setSidebarCollapsed] = useState(() => { try { const stored = localStorage.getItem(SIDEBAR_STORAGE_KEY) @@ -24,8 +26,12 @@ export function DashboardLayout({ children }: DashboardLayoutProps) { return false } }) + + // Mobile: open/closed state (overlay) + const [mobileMenuOpen, setMobileMenuOpen] = useState(false) const [commandPaletteOpen, setCommandPaletteOpen] = useState(false) + // Persist desktop collapsed state useEffect(() => { try { localStorage.setItem(SIDEBAR_STORAGE_KEY, JSON.stringify(sidebarCollapsed)) @@ -34,6 +40,30 @@ export function DashboardLayout({ children }: DashboardLayoutProps) { } }, [sidebarCollapsed]) + // Close mobile menu on route change + useEffect(() => { + setMobileMenuOpen(false) + }, [location.pathname]) + + // Close mobile menu on escape key + useEffect(() => { + const handleEscape = (e: KeyboardEvent) => { + if (e.key === 'Escape') setMobileMenuOpen(false) + } + document.addEventListener('keydown', handleEscape) + return () => document.removeEventListener('keydown', handleEscape) + }, []) + + // Prevent body scroll when mobile menu is open + useEffect(() => { + if (mobileMenuOpen) { + document.body.style.overflow = 'hidden' + } else { + document.body.style.overflow = '' + } + return () => { document.body.style.overflow = '' } + }, [mobileMenuOpen]) + useKeyboardShortcut(SHORTCUTS.COMMAND_PALETTE, () => { setCommandPaletteOpen(true) }) @@ -42,26 +72,49 @@ export function DashboardLayout({ children }: DashboardLayoutProps) { setSidebarCollapsed((prev: boolean) => !prev) }) + const handleToggleSidebar = () => { + // On mobile: toggle overlay menu + // On desktop: toggle collapsed state + if (window.innerWidth < 1024) { + setMobileMenuOpen(!mobileMenuOpen) + } else { + setSidebarCollapsed(!sidebarCollapsed) + } + } + return (
setSidebarCollapsed(!sidebarCollapsed)} + onToggleSidebar={handleToggleSidebar} sidebarCollapsed={sidebarCollapsed} onOpenCommandPalette={() => setCommandPaletteOpen(true)} />
+ {/* Mobile backdrop */} + {mobileMenuOpen && ( +
setMobileMenuOpen(false)} + aria-hidden="true" + /> + )} + setSidebarCollapsed(!sidebarCollapsed)} + onToggle={handleToggleSidebar} + mobileOpen={mobileMenuOpen} + onMobileClose={() => setMobileMenuOpen(false)} /> + {/* Main content - no margin on mobile, dynamic margin on desktop */}
-
+
{children || }
diff --git a/frontend/src/components/dashboard/Sidebar.tsx b/frontend/src/components/dashboard/Sidebar.tsx index 154c48e..47f9457 100644 --- a/frontend/src/components/dashboard/Sidebar.tsx +++ b/frontend/src/components/dashboard/Sidebar.tsx @@ -6,12 +6,15 @@ import { BookOpen, ChevronLeft, ChevronRight, - ExternalLink + ExternalLink, + X } from 'lucide-react' interface SidebarProps { collapsed: boolean onToggle: () => void + mobileOpen?: boolean + onMobileClose?: () => void } interface NavItem { @@ -31,7 +34,7 @@ const bottomNavItems: NavItem[] = [ { name: 'Settings', href: '/dashboard/settings', icon: }, ] -export function Sidebar({ collapsed, onToggle }: SidebarProps) { +export function Sidebar({ collapsed, onToggle, mobileOpen, onMobileClose }: SidebarProps) { const location = useLocation() const isActive = (href: string) => { @@ -41,6 +44,11 @@ export function Sidebar({ collapsed, onToggle }: SidebarProps) { return location.pathname === href } + const handleNavClick = () => { + // Close mobile menu when navigating + onMobileClose?.() + } + const NavLink = ({ item }: { item: NavItem }) => { const active = isActive(item.href) @@ -52,6 +60,9 @@ export function Sidebar({ collapsed, onToggle }: SidebarProps) { } ` + // On mobile, always show full labels (not collapsed) + const showLabels = !collapsed || mobileOpen + if (item.external) { return ( {item.icon} - {!collapsed && ( + {showLabels && ( <> {item.name} @@ -74,26 +86,47 @@ export function Sidebar({ collapsed, onToggle }: SidebarProps) { } return ( - + {item.icon} - {!collapsed && ( + {showLabels && ( {item.name} )} ) } + // Determine sidebar width class + const getWidthClass = () => { + if (mobileOpen) return 'w-[var(--sidebar-width)]' // Always full width on mobile when open + if (collapsed) return 'w-[var(--sidebar-width-collapsed)]' + return 'w-[var(--sidebar-width)]' + } + return (