Skip to content

Commit 7fef92b

Browse files
committed
feat(auth): implement sign in, sign up, and profile management
- Add sync_user_to_member database migration trigger - Create /login page with Google and GitHub OAuth - Create /profile page for editing member details - Update Header to show Sign In/Profile status - Use existing design system components
1 parent 9eb76c5 commit 7fef92b

6 files changed

Lines changed: 883 additions & 1 deletion

File tree

app/components/Header.tsx

Lines changed: 57 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,11 +4,33 @@ import styled from "styled-components"
44
import { links } from "../siteConfig"
55
import { GiveATalkCTA } from "./GiveATalkCTA"
66
import { Button } from "./Button"
7+
import { supabaseClient } from "../../lib/supabaseClient"
78

89
// Components //
910

1011
export const Header = () => {
1112
const [isMenuOpen, setIsMenuOpen] = useState(false)
13+
const [user, setUser] = useState<any>(null)
14+
15+
useEffect(() => {
16+
if (!supabaseClient) return
17+
18+
// Check initial session
19+
supabaseClient.auth.getUser().then(({ data: { user } }) => {
20+
setUser(user)
21+
})
22+
23+
// Listen for auth changes
24+
const {
25+
data: { subscription }
26+
} = supabaseClient.auth.onAuthStateChange((_event, session) => {
27+
setUser(session?.user ?? null)
28+
})
29+
30+
return () => {
31+
subscription.unsubscribe()
32+
}
33+
}, [])
1234

1335
const toggleMenu = () => {
1436
setIsMenuOpen(!isMenuOpen)
@@ -81,7 +103,18 @@ export const Header = () => {
81103
</MenuList>
82104
</NavCenter>
83105
<NavEnd>
84-
<Button href={links.discord}>Join Us on Discord</Button>
106+
<ButtonGroup>
107+
{user ? (
108+
<Button href="/profile" variant="secondary">
109+
Profile
110+
</Button>
111+
) : (
112+
<Button href="/login" variant="secondary">
113+
Sign In
114+
</Button>
115+
)}
116+
<Button href={links.discord}>Join Us on Discord</Button>
117+
</ButtonGroup>
85118
</NavEnd>
86119
</Nav>
87120
</Container>
@@ -108,6 +141,17 @@ export const Header = () => {
108141
</SidebarHeader>
109142
<SidebarContent>
110143
<NavLinks />
144+
<MobileAuthItem>
145+
{user ? (
146+
<Button href="/profile" variant="secondary" size="default">
147+
Profile
148+
</Button>
149+
) : (
150+
<Button href="/login" variant="secondary" size="default">
151+
Sign In
152+
</Button>
153+
)}
154+
</MobileAuthItem>
111155
</SidebarContent>
112156
</MobileSidebar>
113157
</>
@@ -172,6 +216,12 @@ const NavEnd = styled.div`
172216
justify-content: flex-end;
173217
`
174218

219+
const ButtonGroup = styled.div`
220+
display: flex;
221+
align-items: center;
222+
gap: 1rem;
223+
`
224+
175225
const MenuButton = styled.button`
176226
display: flex;
177227
align-items: center;
@@ -314,3 +364,9 @@ const MenuLink = styled.a`
314364
}
315365
}
316366
`
367+
368+
const MobileAuthItem = styled.li`
369+
margin: 1rem 0;
370+
display: flex;
371+
justify-content: center;
372+
`

app/login/page.tsx

Lines changed: 130 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,130 @@
1+
"use client"
2+
import styled from "styled-components"
3+
import { useState, useEffect } from "react"
4+
import { useRouter } from "next/navigation"
5+
import { supabaseClient } from "../../lib/supabaseClient"
6+
import { PotionBackground } from "../components/PotionBackground"
7+
import { Button } from "../components/Button"
8+
9+
export default function Login() {
10+
const [error, setError] = useState<string | null>(null)
11+
const router = useRouter()
12+
13+
useEffect(() => {
14+
// Redirect if already logged in
15+
const checkUser = async () => {
16+
const { data } = (await supabaseClient?.auth.getUser()) || {}
17+
if (data?.user) {
18+
router.push("/profile")
19+
}
20+
}
21+
checkUser()
22+
}, [router])
23+
24+
const handleLogin = async (provider: "google" | "github") => {
25+
try {
26+
const { error } = await supabaseClient!.auth.signInWithOAuth({
27+
provider,
28+
options: {
29+
redirectTo: `${window.location.origin}/profile`
30+
}
31+
})
32+
if (error) throw error
33+
} catch (err: any) {
34+
setError(err.message)
35+
}
36+
}
37+
38+
return (
39+
<>
40+
<BackgroundContainer>
41+
<PotionBackground />
42+
</BackgroundContainer>
43+
<Container>
44+
<LoginCard>
45+
<Title>Sign In</Title>
46+
<Subtitle>Join the DEVx community</Subtitle>
47+
48+
{error && <ErrorMessage>{error}</ErrorMessage>}
49+
50+
<ButtonContainer>
51+
<Button onClick={() => handleLogin("google")} size="default" variant="primary">
52+
Continue with Google
53+
</Button>
54+
55+
<Button onClick={() => handleLogin("github")} size="default" variant="secondary">
56+
Continue with GitHub
57+
</Button>
58+
</ButtonContainer>
59+
</LoginCard>
60+
</Container>
61+
</>
62+
)
63+
}
64+
65+
const BackgroundContainer = styled.section`
66+
background-color: #0a0a0a;
67+
position: fixed;
68+
height: 100vh;
69+
width: 100vw;
70+
top: 0;
71+
left: 0;
72+
z-index: -1;
73+
`
74+
75+
const Container = styled.main`
76+
min-height: 100vh;
77+
display: flex;
78+
align-items: center;
79+
justify-content: center;
80+
padding: 1rem;
81+
`
82+
83+
const LoginCard = styled.div`
84+
background-color: rgba(0, 0, 0, 0.75);
85+
backdrop-filter: blur(8px);
86+
padding: 3rem;
87+
border-radius: 1rem;
88+
width: 100%;
89+
max-width: 400px;
90+
display: flex;
91+
flex-direction: column;
92+
align-items: center;
93+
gap: 2rem;
94+
box-shadow: 0 12px 40px rgba(0, 0, 0, 0.4);
95+
`
96+
97+
const Title = styled.h1`
98+
font-size: 2rem;
99+
font-weight: 700;
100+
color: white;
101+
margin: 0;
102+
`
103+
104+
const Subtitle = styled.p`
105+
color: rgba(255, 255, 255, 0.7);
106+
margin: -1rem 0 0 0;
107+
text-align: center;
108+
`
109+
110+
const ButtonContainer = styled.div`
111+
display: flex;
112+
flex-direction: column;
113+
gap: 1rem;
114+
width: 100%;
115+
116+
button {
117+
width: 100%;
118+
justify-content: center;
119+
}
120+
`
121+
122+
const ErrorMessage = styled.div`
123+
color: #ff6b6b;
124+
background-color: rgba(255, 107, 107, 0.1);
125+
padding: 0.75rem;
126+
border-radius: 0.5rem;
127+
font-size: 0.875rem;
128+
text-align: center;
129+
width: 100%;
130+
`

0 commit comments

Comments
 (0)