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 (