feat: Add GitHub and Google OAuth Login#223
Conversation
- Add signInWithGitHub and signInWithGoogle methods to AuthContext - Update LoginForm with working OAuth buttons (GitHub + Google) - Update SignupForm with same OAuth functionality - Add proper loading states for OAuth flows - Disable buttons during any auth operation to prevent double-clicks - OAuth redirects to /dashboard after successful authentication Supabase handles the OAuth flow automatically via onAuthStateChange listener.
|
@DevanshuNEU is attempting to deploy a commit to the Dev's projects Team on Vercel. A member of the Team first needs to authorize it. |
📝 WalkthroughWalkthroughAdds GitHub and Google OAuth sign-in to LoginForm/SignupForm and exposes signInWithGitHub/signInWithGoogle in AuthContext (Supabase OAuth redirect to /dashboard). Also moves layout spacing to CSS variables and updates dashboard sidebar/topnav for mobile overlay and collapse behavior. Changes
Sequence Diagram(s)sequenceDiagram
participant User as User
participant Form as LoginForm/SignupForm
participant Auth as AuthContext
participant Supabase as Supabase OAuth
User->>Form: Click "GitHub" or "Google"
Form->>Form: set oauthLoading(provider), clear provider error
Form->>Auth: call signInWithGitHub()/signInWithGoogle()
Auth->>Supabase: initiate OAuth sign-in (provider, redirect:/dashboard)
Supabase->>Supabase: redirect to provider for authorization
Supabase-->>Auth: callback with session/token
Auth-->>Form: return result (success or error)
Form->>Form: clear oauthLoading
Form->>User: navigate to /dashboard on success
Estimated Code Review Effort🎯 3 (Moderate) | ⏱️ ~20 minutes Poem
🚥 Pre-merge checks | ✅ 2 | ❌ 1❌ Failed checks (1 warning)
✅ Passed checks (2 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. ✨ Finishing touches
Comment |
There was a problem hiding this comment.
Actionable comments posted: 4
🤖 Fix all issues with AI agents
In `@frontend/src/components/auth/LoginForm.tsx`:
- Around line 35-55: The handlers handleGitHubSignIn and handleGoogleSignIn can
leave oauthLoading set if the OAuth flow returns without navigation; move the
setOauthLoading(null) into a finally block so it always runs regardless of
success or error, keeping the existing setError behavior in the catch; ensure
you call signInWithGitHub and signInWithGoogle inside the try and clear
oauthLoading in finally to prevent the buttons from remaining disabled.
- Around line 148-198: The social sign-in buttons lose their accessible name
when showing only the Loader2 icon (because the visible text is removed); update
the Button rendering for both GitHub and Google in LoginForm.tsx to preserve an
accessible label when oauthLoading === 'github' or 'google' by either (a) adding
an aria-label prop on the Button (e.g., aria-label={`Signing in with GitHub`} /
`Signing in with Google`) or (b) keeping a visually-hidden text node (sr-only
span) next to the Loader2 that reads "Signing in with GitHub" / "Signing in with
Google"; target the Button elements and Loader2 usage and ensure the label text
reflects the provider and only appears during loading.
In `@frontend/src/components/auth/SignupForm.tsx`:
- Around line 46-66: The oauthLoading state is not cleared if signInWithGitHub
or signInWithGoogle throws before navigation, leaving buttons disabled; update
handleGitHubSignIn and handleGoogleSignIn to clear setOauthLoading(null) in a
finally block (after the try/catch) so oauthLoading is reset regardless of
success or error, keeping the existing setError behavior in the catch.
- Around line 176-226: The Github/Google buttons drop their textual accessible
name when showing only the Loader2 icon; update the Button rendering logic in
SignupForm.tsx (the buttons using oauthLoading, handleGitHubSignIn,
handleGoogleSignIn) so that when oauthLoading === 'github' or 'google' you still
provide an accessible label—either add an aria-label that reflects the loading
state (e.g., "Signing in with GitHub"/"Signing in with Google") or render a
visually-hidden span with the provider name alongside the Loader2
spinner—ensuring the Button always exposes an accessible name while preserving
the visual spinner.
- Move setOauthLoading(null) to finally block in both forms Ensures loading state is always reset, even if OAuth redirects or errors - Add aria-label for screen readers when OAuth buttons show spinner Buttons now have 'Signing in with GitHub/Google' accessible name during loading - Add sr-only span with provider name alongside Loader2 spinner Provides accessible text for assistive technologies
- Add pt-14 to main element in DashboardLayout to account for fixed TopNav - Remove duplicate pt-14 from DashboardHome (layout now handles it) The 'Repositories' heading was being cut off by the fixed navbar because the main content area didn't have top padding.
Replaced hardcoded Tailwind classes with CSS variables for maintainability: - --navbar-height: 3.5rem (56px) - --sidebar-width: 15rem (240px) - --sidebar-width-collapsed: 4rem (64px) Now if navbar height changes, update ONE variable instead of hunting through multiple files. Uses Tailwind arbitrary values syntax: h-[var(--navbar-height)], pt-[var(--navbar-height)], etc. Files updated: - index.css: Added layout CSS variables - TopNav.tsx: h-14 → h-[var(--navbar-height)] - DashboardLayout.tsx: pt-14, ml-16/60 → CSS variables - Sidebar.tsx: top-14, w-16/60 → CSS variables
|
The latest updates on your projects. Learn more about Vercel for GitHub.
|
Mobile behavior (< 1024px / lg breakpoint): - Sidebar hidden by default - Hamburger menu in navbar opens sidebar as overlay - Dark backdrop behind sidebar when open - Tap backdrop or press Escape to close - Auto-closes when navigating to a new page - Body scroll locked when menu is open - Full-width sidebar with close button Desktop behavior (>= 1024px): - Sidebar always visible - Can be collapsed to icon-only mode - Collapse toggle in sidebar footer Also: - Added --mobile-breakpoint CSS variable for consistency - Reduced padding on mobile (p-4) vs desktop (md:p-6) - Main content has no left margin on mobile
- Fixed main content margin: use ternary to apply only ONE lg:ml-* class (before: both classes were applied, causing conflicts) - Removed invalid CSS comment from Sidebar className
There was a problem hiding this comment.
Actionable comments posted: 2
🤖 Fix all issues with AI agents
In `@frontend/src/components/dashboard/DashboardLayout.tsx`:
- Around line 110-116: The main element's className currently adds both
"lg:ml-[var(--sidebar-width)]" and conditionally
"lg:ml-[var(--sidebar-width-collapsed)]", which can conflict; update the
className expression in the DashboardLayout component so the lg:ml-* is chosen
via a single ternary tied to sidebarCollapsed (e.g., use sidebarCollapsed ?
'lg:ml-[var(--sidebar-width-collapsed)]' : 'lg:ml-[var(--sidebar-width)]') so
only one lg:ml class is rendered; keep the other classes (flex-1,
transition-all, duration-300, pt-[var(--navbar-height)], ml-0) unchanged.
In `@frontend/src/components/dashboard/Sidebar.tsx`:
- Around line 108-118: The string passed to the aside element's className
contains a CSS-style comment which will be rendered literally; remove the "/*
Mobile: hidden by default, shown when mobileOpen */" from the template literal
(or convert it to a JS/TS comment above the template literal) so the comment
isn't included in the generated class attribute; update the className
construction that uses getWidthClass() and mobileOpen accordingly (or place
explanatory text in a separate comment line before the aside or in a const
variable) to preserve intent without injecting literal text into the className.
🧹 Nitpick comments (1)
frontend/src/components/dashboard/DashboardLayout.tsx (1)
75-83: Consider extracting the breakpoint constant.The hardcoded
1024matches the--mobile-breakpointCSS variable and Tailwind'slg:breakpoint, but this relationship is implicit. Consider extracting a shared constant to prevent accidental drift.Suggested approach
// At top of file or in a shared constants file const MOBILE_BREAKPOINT = 1024 const handleToggleSidebar = () => { if (window.innerWidth < MOBILE_BREAKPOINT) { setMobileMenuOpen(!mobileMenuOpen) } else { setSidebarCollapsed(!sidebarCollapsed) } }
There was a problem hiding this comment.
Actionable comments posted: 0
Caution
Some comments are outside the diff and can’t be posted inline due to platform limitations.
⚠️ Outside diff range comments (1)
frontend/src/components/dashboard/DashboardLayout.tsx (1)
48-83: Close mobile menu on viewport resize to prevent locked scroll and update state toggles using functional updates.The code has two issues:
Viewport breakpoint not handled: If the mobile menu opens on mobile and the viewport crosses the lg breakpoint (1024px),
mobileMenuOpenremains true, keeping body scroll locked and the full-width sidebar forced on desktop until Escape or route change.State toggles use captured state: Lines 79 and 81 capture
mobileMenuOpenandsidebarCollapseddirectly instead of functional updates. React batching can cause stale state issues. (Line 72 already correctly uses the functional pattern.)Add a resize listener to close the menu when crossing the breakpoint, and convert the state setters to use functional updates:
Proposed fix
// 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]) + // Close mobile menu when switching to desktop breakpoint + useEffect(() => { + const handleResize = () => { + if (window.innerWidth >= 1024) setMobileMenuOpen(false) + } + window.addEventListener('resize', handleResize) + handleResize() + return () => window.removeEventListener('resize', handleResize) + }, []) + const handleToggleSidebar = () => { // On mobile: toggle overlay menu // On desktop: toggle collapsed state if (window.innerWidth < 1024) { - setMobileMenuOpen(!mobileMenuOpen) + setMobileMenuOpen((prev) => !prev) } else { - setSidebarCollapsed(!sidebarCollapsed) + setMobileMenuOpen(false) + setSidebarCollapsed((prev) => !prev) } }
Summary
Adds GitHub and Google OAuth login via Supabase, plus fixes for dashboard layout and mobile responsiveness.
Changes
OAuth
signInWithGitHub()andsignInWithGoogle()to AuthContextLayout Fixes
--navbar-height,--sidebar-width,--sidebar-width-collapsedMobile Responsiveness
Testing
Supabase Config Required
https://opencodeintel.com/*,http://localhost:3000/*