Skip to content

Commit 58a714e

Browse files
authored
Merge branch 'pointblank-club:staging' into staging
2 parents 25c0a61 + 77e6a4d commit 58a714e

10 files changed

Lines changed: 432 additions & 115 deletions

File tree

app/dashboard/layout.tsx

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -118,6 +118,7 @@ export default function DashboardLayout({
118118
name: user.name,
119119
email: user.email,
120120
role: user.role,
121+
profilePicture: user.profile_picture,
121122
}
122123
: undefined
123124
}

app/login/page.tsx

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@ import { Button } from "@/components/registration/button";
1111
import { StickyAlert } from "@/components/registration/sticky-alert";
1212
import { RecaptchaNotice } from "@/components/registration/recaptcha-notice";
1313
import { DotPattern } from "@/components/registration/dot-pattern";
14+
import { AuthHeader } from "@/components/registration/auth-header";
1415
import { Spinner } from "@/components/ui/spinner";
1516
import { ArrowRight, LogIn, Link2 } from "lucide-react";
1617

@@ -128,6 +129,7 @@ export default function LoginPage() {
128129
return (
129130
<div className="min-h-screen w-full bg-void relative overflow-hidden">
130131
<DotPattern />
132+
<AuthHeader />
131133

132134
<main className="relative z-10 min-h-screen w-full flex flex-col items-center justify-center px-4 sm:px-6 py-10 sm:py-16">
133135
<div className="w-full max-w-[460px] flex flex-col gap-6 anim-fade-up">

app/register/page.tsx

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ import { useRouter } from "next/navigation";
55
import { useAuth } from "@/hooks/use-auth";
66
import { RegistrationContainer } from "@/components/registration/registration-container";
77
import { DotPattern } from "@/components/registration/dot-pattern";
8+
import { AuthHeader } from "@/components/registration/auth-header";
89
import { Spinner } from "@/components/ui/spinner";
910

1011
export default function RegisterPage() {
@@ -26,8 +27,9 @@ export default function RegisterPage() {
2627
return (
2728
<div className="min-h-screen w-full bg-void relative overflow-hidden">
2829
<DotPattern />
30+
<AuthHeader />
2931

30-
<main className="relative z-10 w-full min-h-screen flex flex-col items-center px-4 sm:px-6 md:px-8 py-10 sm:py-14">
32+
<main className="relative z-10 w-full min-h-screen flex flex-col items-center px-4 sm:px-6 md:px-8 pt-24 pb-10 sm:pt-28 sm:pb-14">
3133
<div className="w-full max-w-[1000px] flex flex-col gap-7 items-center anim-fade-up">
3234
<RegistrationContainer />
3335
</div>

components/landing/FinalCTA/FinalCTA.jsx

Lines changed: 15 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -3,12 +3,14 @@ import { gsap } from 'gsap';
33
import { ScrollTrigger } from 'gsap/ScrollTrigger';
44
import { useGSAP } from '@gsap/react';
55
import { useRetroSound } from '../hooks/useRetroSound';
6+
import { useAuth } from '@/hooks/use-auth';
67
import './FinalCTA.css';
78

89
gsap.registerPlugin(ScrollTrigger);
910

1011
export default function FinalCTA() {
1112
const sectionRef = useRef(null);
13+
const { isAuthenticated } = useAuth();
1214
const { playHover, playClick } = useRetroSound();
1315

1416
useGSAP(() => {
@@ -42,13 +44,23 @@ export default function FinalCTA() {
4244
</p>
4345
<div className="final-cta__actions">
4446
<a
45-
href="/register"
46-
className="btn btn--primary"
47+
href={isAuthenticated ? '/dashboard' : '/register'}
48+
className={`btn ${isAuthenticated ? 'btn--secondary' : 'btn--primary'}`}
4749
id="cta-register-now"
4850
onMouseEnter={playHover}
4951
onClick={playClick}
5052
>
51-
Register Now
53+
{isAuthenticated ? (
54+
<>
55+
Access Granted
56+
<svg className="btn__arrow" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round" aria-hidden="true">
57+
<line x1="5" y1="12" x2="19" y2="12" />
58+
<polyline points="12 5 19 12 12 19" />
59+
</svg>
60+
</>
61+
) : (
62+
'Register Now'
63+
)}
5264
</a>
5365

5466
</div>

components/landing/Header/Header.css

Lines changed: 121 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -179,6 +179,122 @@
179179
transform: translateY(-1px);
180180
}
181181

182+
/* --- Authed user controls (avatar tile + logout) --- */
183+
.pbctf-landing .header__user {
184+
display: none;
185+
align-items: center;
186+
gap: var(--space-3);
187+
}
188+
189+
.pbctf-landing .header__user-tile {
190+
display: inline-flex;
191+
align-items: center;
192+
gap: var(--space-2);
193+
height: 36px;
194+
padding: 0 var(--space-3) 0 4px;
195+
border-radius: var(--radius-sm);
196+
background: rgba(255, 255, 255, 0.03);
197+
border: 1px solid rgba(255, 255, 255, 0.06);
198+
text-decoration: none;
199+
transition: background var(--duration-normal) var(--ease-out),
200+
border-color var(--duration-normal) var(--ease-out),
201+
box-shadow var(--duration-normal) var(--ease-out);
202+
}
203+
204+
.pbctf-landing .header__user-tile:hover {
205+
background: rgba(0, 255, 136, 0.08);
206+
border-color: rgba(0, 255, 136, 0.4);
207+
box-shadow: 0 0 15px rgba(0, 255, 136, 0.15);
208+
}
209+
210+
.pbctf-landing .header__user-avatar {
211+
width: 28px;
212+
height: 28px;
213+
border-radius: 4px;
214+
object-fit: cover;
215+
flex-shrink: 0;
216+
}
217+
218+
.pbctf-landing .header__user-avatar--initials {
219+
display: inline-flex;
220+
align-items: center;
221+
justify-content: center;
222+
background: var(--primary);
223+
color: #050505;
224+
font-family: var(--font-mono);
225+
font-weight: 700;
226+
font-size: 11px;
227+
}
228+
229+
.pbctf-landing .header__user-name {
230+
font-family: var(--font-heading);
231+
font-weight: 600;
232+
font-size: 13px;
233+
color: var(--text);
234+
line-height: 1;
235+
max-width: 120px;
236+
overflow: hidden;
237+
text-overflow: ellipsis;
238+
white-space: nowrap;
239+
}
240+
241+
.pbctf-landing .header__logout {
242+
display: inline-flex;
243+
align-items: center;
244+
gap: var(--space-2);
245+
height: 36px;
246+
padding: 0 var(--space-3);
247+
border-radius: var(--radius-sm);
248+
background: rgba(255, 255, 255, 0.03);
249+
border: 1px solid rgba(255, 255, 255, 0.06);
250+
color: var(--muted);
251+
cursor: pointer;
252+
transition: color var(--duration-normal) var(--ease-out),
253+
background var(--duration-normal) var(--ease-out),
254+
border-color var(--duration-normal) var(--ease-out),
255+
box-shadow var(--duration-normal) var(--ease-out);
256+
}
257+
258+
.pbctf-landing .header__logout svg {
259+
width: 16px;
260+
height: 16px;
261+
}
262+
263+
.pbctf-landing .header__logout:hover {
264+
color: var(--primary);
265+
background: rgba(0, 255, 136, 0.08);
266+
border-color: rgba(0, 255, 136, 0.4);
267+
box-shadow: 0 0 15px rgba(0, 255, 136, 0.15);
268+
}
269+
270+
.pbctf-landing .header__logout-text {
271+
font-family: var(--font-mono);
272+
font-weight: 600;
273+
font-size: 11px;
274+
text-transform: uppercase;
275+
letter-spacing: 0.05em;
276+
}
277+
278+
/* --- Mobile overlay logout --- */
279+
.pbctf-landing .header__overlay-logout {
280+
margin-top: var(--space-3);
281+
align-self: flex-start;
282+
background: none;
283+
border: none;
284+
cursor: pointer;
285+
font-family: var(--font-mono);
286+
font-weight: 600;
287+
font-size: var(--text-sm);
288+
text-transform: uppercase;
289+
letter-spacing: 0.05em;
290+
color: var(--muted);
291+
transition: color 0.3s ease;
292+
}
293+
294+
.pbctf-landing .header__overlay-logout:hover {
295+
color: var(--primary);
296+
}
297+
182298
/* --- Custom Tech Hamburger --- */
183299
.pbctf-landing .header__hamburger {
184300
display: flex;
@@ -326,6 +442,11 @@
326442
flex-shrink: 0;
327443
}
328444

445+
.pbctf-landing .header__user {
446+
display: flex;
447+
flex-shrink: 0;
448+
}
449+
329450
.pbctf-landing .header__hamburger {
330451
display: none;
331452
}

components/landing/Header/Header.jsx

Lines changed: 77 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
import { useState, useEffect } from 'react';
22
import { motion, AnimatePresence } from 'framer-motion';
33
import { useRetroSound } from '../hooks/useRetroSound';
4+
import { useAuth } from '@/hooks/use-auth';
45
import './Header.css';
56

67
const NAV_LINKS = [
@@ -12,6 +13,7 @@ const NAV_LINKS = [
1213
];
1314

1415
export default function Header() {
16+
const { isAuthenticated, user, logout } = useAuth();
1517
const [scrolled, setScrolled] = useState(false);
1618
const [menuOpen, setMenuOpen] = useState(false);
1719
const {
@@ -24,6 +26,21 @@ export default function Header() {
2426
muted,
2527
} = useRetroSound();
2628

29+
const firstName = (user?.name || '').trim().split(/\s+/)[0] || '';
30+
const initials =
31+
(user?.name || '')
32+
.split(/\s+/)
33+
.filter(Boolean)
34+
.slice(0, 2)
35+
.map((s) => s[0]?.toUpperCase())
36+
.join('') || 'U';
37+
const avatarSrc = user?.profile_picture || '';
38+
39+
const handleLogout = () => {
40+
playClick();
41+
logout();
42+
};
43+
2744
useEffect(() => {
2845
const handleScroll = () => {
2946
setScrolled(window.scrollY > 50);
@@ -107,16 +124,52 @@ export default function Header() {
107124
)}
108125
</button>
109126

110-
{/* Desktop CTA */}
111-
<a
112-
href="/register"
113-
className="btn btn--primary header__cta"
114-
id="header-cta"
115-
onMouseEnter={playHover}
116-
onClick={playClick}
117-
>
118-
Register Now
119-
</a>
127+
{/* Desktop CTA (logged out) / user controls (logged in) */}
128+
{isAuthenticated ? (
129+
<div className="header__user">
130+
<a
131+
href="/dashboard/profile"
132+
className="header__user-tile"
133+
id="header-profile"
134+
onMouseEnter={playHover}
135+
onClick={playClick}
136+
aria-label="Open profile"
137+
>
138+
{avatarSrc ? (
139+
// eslint-disable-next-line @next/next/no-img-element
140+
<img className="header__user-avatar" src={avatarSrc} alt="" />
141+
) : (
142+
<span className="header__user-avatar header__user-avatar--initials">{initials}</span>
143+
)}
144+
<span className="header__user-name">{firstName}</span>
145+
</a>
146+
<button
147+
type="button"
148+
className="header__logout"
149+
id="header-logout"
150+
onMouseEnter={playHover}
151+
onClick={handleLogout}
152+
aria-label="Log out"
153+
>
154+
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="1.6" strokeLinecap="round" strokeLinejoin="round" aria-hidden="true">
155+
<path d="M9 21H5a2 2 0 0 1-2-2V5a2 2 0 0 1 2-2h4" />
156+
<polyline points="16 17 21 12 16 7" />
157+
<line x1="21" y1="12" x2="9" y2="12" />
158+
</svg>
159+
<span className="header__logout-text">Logout</span>
160+
</button>
161+
</div>
162+
) : (
163+
<a
164+
href="/register"
165+
className="btn btn--primary header__cta"
166+
id="header-cta"
167+
onMouseEnter={playHover}
168+
onClick={playClick}
169+
>
170+
Register Now
171+
</a>
172+
)}
120173

121174
{/* Mobile Hamburger */}
122175
<button
@@ -161,15 +214,27 @@ export default function Header() {
161214
</motion.a>
162215
))}
163216
<motion.a
164-
href="/register"
217+
href={isAuthenticated ? '/dashboard' : '/register'}
165218
className="btn btn--primary header__overlay-cta"
166219
onClick={() => { playClick(); closeMenu(); }}
167220
initial={{ opacity: 0, y: 20 }}
168221
animate={{ opacity: 1, y: 0 }}
169222
transition={{ delay: 0.1 + NAV_LINKS.length * 0.05, duration: 0.4, ease: [0.16, 1, 0.3, 1] }}
170223
>
171-
Register Now
224+
{isAuthenticated ? 'Access Granted' : 'Register Now'}
172225
</motion.a>
226+
{isAuthenticated && (
227+
<motion.button
228+
type="button"
229+
className="header__overlay-logout"
230+
onClick={() => { handleLogout(); closeMenu(); }}
231+
initial={{ opacity: 0, y: 20 }}
232+
animate={{ opacity: 1, y: 0 }}
233+
transition={{ delay: 0.1 + (NAV_LINKS.length + 1) * 0.05, duration: 0.4, ease: [0.16, 1, 0.3, 1] }}
234+
>
235+
Logout
236+
</motion.button>
237+
)}
173238
</div>
174239
</motion.nav>
175240
)}

0 commit comments

Comments
 (0)