Skip to content
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
99 changes: 87 additions & 12 deletions src/app/(protected)/profile/page.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -38,11 +38,51 @@ import {
Lock,
GraduationCap,
HelpCircle,
Shield,
} from "lucide-react";
import { Roofing, Room } from "@mui/icons-material";
import { jwtDecode } from "jwt-decode";

// Role definitions matching AuthGuard
enum Role {
NONE = 0,
VOLUNTEER = 1,
TEAM = 2,
EXEC = 3,
TECH = 4,
FINANCE = 5,
}

// Utility to get user role from token
function getUserRole(token: string | undefined): number {
if (!token) return Role.NONE;

try {
const decoded: any = jwtDecode(token);
const productionRole = decoded.claims?.production;
const stagingRole = decoded.claims?.staging;
return productionRole ?? stagingRole ?? Role.NONE;
} catch (error) {
console.error("Error decoding token:", error);
return Role.NONE;
}
}

// Get role name for display
function getRoleName(role: number): string {
const roleNames: { [key: number]: string } = {
[Role.NONE]: "Participant",
[Role.VOLUNTEER]: "Volunteer",
[Role.TEAM]: "Team Member",
[Role.EXEC]: "Executive",
[Role.TECH]: "Tech Team",
[Role.FINANCE]: "Finance",
};
return roleNames[role] || "Unknown";
}

export default function Profile() {
const { isAuthenticated, user, logout, isLoading } = useFirebase();
const { isAuthenticated, user, logout, isLoading, token } = useFirebase();
const router = useRouter();
const { isLoading: isUserLoading, data: userData } = useUserInfoMe();
const { data: teams } = useAllTeams();
Expand All @@ -60,14 +100,21 @@ export default function Profile() {
// Feature flag check for HelpDesk
const { data: helpDeskFlag } = useFlagState("HelpDesk");

// Check if user is an organizer (role > 0)
const userRole = getUserRole(token);
const isOrganizer = userRole > Role.NONE;

const toggleQRCode = () => setShowQRCode((prev) => !prev);

useEffect(() => {
// if user data is still loading, do not redirect
if (isUserLoading) return;

if (!userData || !userData.registration) router.push("/register");
}, [userData, router]);
// Only redirect to registration if user is a participant (not an organizer) and not registered
if (!isOrganizer && (!userData || !userData.registration)) {
router.push("/register");
}
}, [userData, router, isUserLoading, isOrganizer]);

// Handle add-to-Google Wallet click
const handleAddToGoogleWallet = async () => {
Expand Down Expand Up @@ -232,17 +279,25 @@ export default function Profile() {
<CardTitle className="text-2xl md:text-3xl font-bold">
{userData?.firstName && userData?.lastName
? `${userData.firstName} ${userData.lastName}`
: "Profile"}
: user.email || "Profile"}
</CardTitle>
<CardDescription className="text-slate-300">
HackPSU Participant
<CardDescription className="text-slate-300 flex items-center justify-center gap-2">
{isOrganizer && <Shield className="h-4 w-4" />}
{isOrganizer ? `HackPSU ${getRoleName(userRole)}` : "HackPSU Participant"}
</CardDescription>
</CardHeader>
<CardContent className="space-y-4">
<div className="flex items-center justify-center space-x-2">
<Mail className="h-5 w-5" />
<span className="text-sm md:text-base">{user.email}</span>
</div>
{isOrganizer && (
<div className="bg-slate-700/50 rounded-lg p-3 mt-4">
<p className="text-sm text-slate-200 text-center">
You are viewing this profile as an organizer. Participant actions are disabled.
</p>
</div>
)}
</CardContent>
</Card>

Expand Down Expand Up @@ -315,8 +370,12 @@ export default function Profile() {
alt="Add to Google Wallet"
width={200}
height={60}
className="cursor-pointer hover:opacity-80 transition-opacity duration-200"
onClick={handleAddToGoogleWallet}
className={`transition-opacity duration-200 ${
isOrganizer
? "opacity-30 cursor-not-allowed"
: "cursor-pointer hover:opacity-80"
}`}
onClick={isOrganizer ? undefined : handleAddToGoogleWallet}
priority
/>
)}
Expand All @@ -333,8 +392,12 @@ export default function Profile() {
alt="Add to Apple Wallet"
width={200}
height={60}
className="cursor-pointer hover:opacity-80 transition-opacity duration-200"
onClick={handleAddToAppleWallet}
className={`transition-opacity duration-200 ${
isOrganizer
? "opacity-30 cursor-not-allowed"
: "cursor-pointer hover:opacity-80"
}`}
onClick={isOrganizer ? undefined : handleAddToAppleWallet}
priority
/>
)}
Expand Down Expand Up @@ -385,6 +448,7 @@ export default function Profile() {
className="w-full"
variant="default"
size="lg"
disabled={isOrganizer}
>
<Users className="mr-2 h-4 w-4" />
Manage Team
Expand All @@ -394,6 +458,7 @@ export default function Profile() {
className="w-full"
variant="default"
size="lg"
disabled={isOrganizer}
>
<Room className="mr-2 h-4 w-4" />
Reserve Room
Expand All @@ -402,13 +467,16 @@ export default function Profile() {
) : (
<>
<p className="text-gray-600">
You&apos;re not part of any team yet.
{isOrganizer
? "Team management is for participants only."
: "You're not part of any team yet."}
</p>
<Button
onClick={handleTeam}
className="w-full"
variant="default"
size="lg"
disabled={isOrganizer}
>
<Users className="mr-2 h-4 w-4" />
Create or Join Team
Expand All @@ -422,14 +490,19 @@ export default function Profile() {
<Card>
<CardHeader>
<CardTitle>Quick Actions</CardTitle>
<CardDescription>Manage your HackPSU experience</CardDescription>
<CardDescription>
{isOrganizer
? "View-only organizer access"
: "Manage your HackPSU experience"}
</CardDescription>
</CardHeader>
<CardContent className="space-y-4">
<Button
onClick={handleProject}
className="w-full"
variant="default"
size="lg"
disabled={isOrganizer}
>
<FileText className="mr-2 h-4 w-4" />
Submit Project
Expand All @@ -450,6 +523,7 @@ export default function Profile() {
className="w-full"
variant="default"
size="lg"
disabled={isOrganizer}
>
<FileText className="mr-2 h-4 w-4" />
Submit Reimbursement Form
Expand All @@ -460,6 +534,7 @@ export default function Profile() {
className="w-full"
variant="default"
size="lg"
disabled={isOrganizer}
>
<GraduationCap className="mr-2 h-4 w-4" />
Manage Extra Credit
Expand Down