Skip to content

Commit 5c7647b

Browse files
authored
Merge pull request #56 from mrepol742/master
feat: bug fixes
2 parents a9bf364 + 3ed8995 commit 5c7647b

10 files changed

Lines changed: 703 additions & 522 deletions

File tree

.github/workflows/build.yml

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@ name: Build Next.js App
33
on:
44
push:
55
branches: ["master"]
6-
pull_request:
6+
pull_request_target:
77
branches: ["master"]
88
workflow_dispatch:
99

@@ -26,6 +26,7 @@ jobs:
2626
run: npm run lint
2727

2828
- name: Generate types from remote
29+
if: ${{ secrets.SUPABASE_PROJECT_ID != '' && secrets.SUPABASE_ACCESS_TOKEN != '' }}
2930
env:
3031
SUPABASE_ACCESS_TOKEN: ${{ secrets.SUPABASE_ACCESS_TOKEN }}
3132
run: |
@@ -41,11 +42,13 @@ jobs:
4142
# npx supabase db push --project-id ${{ secrets.SUPABASE_PROJECT_ID }}
4243

4344
- name: Create .env file
45+
if: ${{ secrets.NEXT_PUBLIC_SUPABASE_URL != '' && secrets.NEXT_PUBLIC_SUPABASE_ANON_KEY != '' }}
4446
run: |
4547
echo "NEXT_PUBLIC_SUPABASE_URL=${{ secrets.NEXT_PUBLIC_SUPABASE_URL }}" >> .env
4648
echo "NEXT_PUBLIC_SUPABASE_ANON_KEY=${{ secrets.NEXT_PUBLIC_SUPABASE_ANON_KEY }}" >> .env
4749
echo "NEXT_PUBLIC_HCAPTCHA_SITE_KEY=${{ secrets.NEXT_PUBLIC_HCAPTCHA_SITE_KEY }}" >> .env
4850
echo "NEXT_PUBLIC_NORTON_SAFEWEB_SITE_VERIFICATION=${{ secrets.NEXT_PUBLIC_NORTON_SAFEWEB_SITE_VERIFICATION }}" >> .env
4951
5052
- name: Build project
53+
if: ${{ secrets.NEXT_PUBLIC_SUPABASE_URL != '' && secrets.NEXT_PUBLIC_SUPABASE_ANON_KEY != '' }}
5154
run: npm run build

app/components/ProfileDropdown.tsx

Lines changed: 149 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,149 @@
1+
"use client";
2+
3+
import {
4+
faArrowRightFromBracket,
5+
faDashboard,
6+
faGear,
7+
faMessage,
8+
faRankingStar,
9+
faStar,
10+
} from "@fortawesome/free-solid-svg-icons";
11+
import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
12+
import Image from "next/image";
13+
import Link from "next/link";
14+
import { usePathname } from "next/navigation";
15+
import { useEffect, useRef, useState } from "react";
16+
17+
export default function ProfileDropdown({
18+
avatar,
19+
name,
20+
email,
21+
}: {
22+
avatar: string | null;
23+
name: string;
24+
email: string;
25+
}) {
26+
const [profileOpen, setProfileOpen] = useState(false);
27+
const profileRef = useRef<HTMLDivElement>(null);
28+
const pathname = usePathname();
29+
const showDashboardLink = !pathname.includes("/dashboard");
30+
31+
useEffect(() => {
32+
function handleClickOutside(event: MouseEvent) {
33+
if (
34+
profileRef.current &&
35+
!profileRef.current.contains(event.target as Node)
36+
) {
37+
setProfileOpen(false);
38+
}
39+
}
40+
document.addEventListener("mousedown", handleClickOutside);
41+
return () => document.removeEventListener("mousedown", handleClickOutside);
42+
}, []);
43+
44+
return (
45+
<div
46+
className="relative flex items-center gap-2 sm:gap-3 pl-2 sm:pl-4 cursor-pointer group"
47+
ref={profileRef}
48+
onClick={() => setProfileOpen(!profileOpen)}
49+
>
50+
{avatar ? (
51+
<Image
52+
src={avatar}
53+
alt="Profile Avatar"
54+
width={32}
55+
height={32}
56+
className="w-8 h-8 rounded-full object-cover"
57+
/>
58+
) : (
59+
<div className="w-8 h-8 rounded-full bg-gradient-to-br from-indigo-500 to-purple-600 flex items-center justify-center text-xs font-bold text-white shrink-0">
60+
{email.charAt(0).toUpperCase()}
61+
</div>
62+
)}
63+
<span className="text-sm font-medium text-gray-300 group-hover:text-white transition-colors hidden sm:block">
64+
{name}
65+
</span>
66+
<svg
67+
className={`w-4 h-4 text-gray-500 group-hover:text-white transition-transform duration-200 hidden sm:block ${profileOpen ? "rotate-180" : ""}`}
68+
fill="none"
69+
viewBox="0 0 24 24"
70+
stroke="currentColor"
71+
>
72+
<path
73+
strokeLinecap="round"
74+
strokeLinejoin="round"
75+
strokeWidth={2}
76+
d="M19 9l-7 7-7-7"
77+
/>
78+
</svg>
79+
80+
{/* Dropdown Menu */}
81+
{profileOpen && (
82+
<div
83+
className="absolute right-0 top-full mt-3 w-48 rounded-xl glass-card py-2 shadow-xl border border-gray-800/60 z-[100] animate-in fade-in slide-in-from-top-2 duration-200"
84+
onClick={(e) => e.stopPropagation()}
85+
>
86+
<div className="px-4 py-2 border-b border-gray-800/60 mb-2 md:hidden">
87+
<p className="text-sm font-medium text-white truncate">{name}</p>
88+
<p className="text-[10px] text-gray-500 truncate">{email}</p>
89+
</div>
90+
{showDashboardLink && (
91+
<>
92+
<Link
93+
href="/dashboard"
94+
className="flex items-center gap-2 px-4 py-2 text-sm text-gray-400 hover:text-white hover:bg-white/[0.03] transition-colors"
95+
onClick={() => setProfileOpen(false)}
96+
>
97+
<FontAwesomeIcon icon={faDashboard} className="w-4 h-4" />
98+
Dashboard
99+
</Link>
100+
<Link
101+
href="/dashboard/leaderboards"
102+
className="flex items-center gap-2 px-4 py-2 text-sm text-gray-400 hover:text-white hover:bg-white/[0.03] transition-colors"
103+
onClick={() => setProfileOpen(false)}
104+
>
105+
<FontAwesomeIcon icon={faRankingStar} className="w-4 h-4" />
106+
Leaderboards
107+
</Link>
108+
<Link
109+
href="/dashboard/flex"
110+
className="flex items-center gap-2 px-4 py-2 text-sm text-gray-400 hover:text-white hover:bg-white/[0.03] transition-colors"
111+
onClick={() => setProfileOpen(false)}
112+
>
113+
<FontAwesomeIcon icon={faStar} className="w-4 h-4" />
114+
Flex
115+
</Link>
116+
<Link
117+
href="/dashboard/chat"
118+
className="flex items-center gap-2 px-4 py-2 text-sm text-gray-400 hover:text-white hover:bg-white/[0.03] transition-colors"
119+
onClick={() => setProfileOpen(false)}
120+
>
121+
<FontAwesomeIcon icon={faMessage} className="w-4 h-4" />
122+
Chat
123+
</Link>
124+
</>
125+
)}
126+
<Link
127+
href="/dashboard/settings"
128+
className="flex items-center gap-2 px-4 py-2 text-sm text-gray-400 hover:text-white hover:bg-white/[0.03] transition-colors"
129+
onClick={() => setProfileOpen(false)}
130+
>
131+
<FontAwesomeIcon icon={faGear} className="w-4 h-4" />
132+
Settings
133+
</Link>
134+
<Link
135+
href="/logout"
136+
className="w-full flex items-center gap-2 px-4 py-2 text-sm text-red-400 hover:text-red-300 hover:bg-red-500/10 transition-colors mt-1"
137+
onClick={() => setProfileOpen(false)}
138+
>
139+
<FontAwesomeIcon
140+
icon={faArrowRightFromBracket}
141+
className="w-4 h-4"
142+
/>
143+
Logout
144+
</Link>
145+
</div>
146+
)}
147+
</div>
148+
);
149+
}

app/components/auth/LoginForm.tsx

Lines changed: 1 addition & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -6,11 +6,6 @@ import { toast } from "react-toastify";
66
import { useRouter } from "next/navigation";
77
import { useSearchParams } from "next/navigation";
88
import HCaptcha from "@hcaptcha/react-hcaptcha";
9-
import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
10-
import { faGoogle } from "@fortawesome/free-brands-svg-icons/faGoogle";
11-
import { faMicrosoft } from "@fortawesome/free-brands-svg-icons/faMicrosoft";
12-
import { faGithub } from "@fortawesome/free-brands-svg-icons";
13-
import Image from "next/image";
149

1510
export default function LoginForm() {
1611
const supabase = createClient();
@@ -122,7 +117,7 @@ export default function LoginForm() {
122117
<span className="text-sm text-gray-500">Or continue with</span>
123118
</div>
124119

125-
<div className="flex flex-row-321 space-x-3">
120+
<div className="flex flex-row-321 gap-3">
126121
<button
127122
type="button"
128123
onClick={handleGoogleSignIn}

0 commit comments

Comments
 (0)