From ec6e00b5534ffaf33ce66fcba0bba9c9c8a40fdd Mon Sep 17 00:00:00 2001 From: dshal20 Date: Mon, 24 Nov 2025 03:50:22 -0500 Subject: [PATCH] Feat: Profile logic and design update --- src/app/profile/page.tsx | 67 ++++---- src/components/back-to-dashboard-link.tsx | 11 +- src/components/edit-class.tsx | 177 ++++++++++++++++++++++ src/lib/types/types.ts | 7 + 4 files changed, 221 insertions(+), 41 deletions(-) create mode 100644 src/components/edit-class.tsx diff --git a/src/app/profile/page.tsx b/src/app/profile/page.tsx index 5116bab..f4fabd4 100644 --- a/src/app/profile/page.tsx +++ b/src/app/profile/page.tsx @@ -1,7 +1,9 @@ -import { Shield, Star, Trophy, Swords, Pencil } from "lucide-react"; +import { Shield, Star, Trophy, Swords } from "lucide-react"; import { createClient as createServerClient } from "../../lib/supabase/server"; import { redirect } from "next/navigation"; import { Cinzel } from "next/font/google"; +import EditClass from "../../components/edit-class"; +import BackToDashBoardLink from "../../components/back-to-dashboard-link"; //font for words const cinzel = Cinzel({ @@ -19,6 +21,24 @@ export default async function ProfilePage() { redirect("/login"); } + // Fetch user profile from database + const { data: profile, error: profileError } = await supabase + .from('profiles') + .select('class') + .eq('id', user.id) + .maybeSingle(); // Use maybeSingle() instead of single() to handle case where profile doesn't exist + + // Map enum values to display names + const classDisplayMap: Record = { + 'warrior': 'Python Warrior', + 'mage': 'Java Mage', + 'rogue': 'C++ Rogue', + }; + + // Get class from database, default to null (will show as empty or default) + const userClassEnum = profile?.class as string | null; + const userClass = userClassEnum ? classDisplayMap[userClassEnum] || userClassEnum : null; + const username = user.email?.split('@')[0] || 'User'; const joinedDate = user.created_at @@ -48,6 +68,9 @@ export default async function ProfilePage() { {/* Content */}
+ {/* Back Button */} + + {/* Header */}

@@ -197,19 +220,6 @@ export default async function ProfilePage() { border: "0.5px solid rgba(190, 150, 97, 0.3)", }} > - {/* Edit Profile Button */} - - {/* Username */}

{username} @@ -236,7 +246,7 @@ export default async function ProfilePage() { Level - 42 + 0

@@ -255,7 +265,7 @@ export default async function ProfilePage() { XP - 12,847 + 0
@@ -274,32 +284,14 @@ export default async function ProfilePage() { Quests - 127 + 0/8 {/* Detailed Info */} {/* Class */} -
-
-
- - Class - - - Python Warrior - -
-
+ {/* Guild */}
@@ -369,5 +361,4 @@ export default async function ProfilePage() {
); -} - +} \ No newline at end of file diff --git a/src/components/back-to-dashboard-link.tsx b/src/components/back-to-dashboard-link.tsx index 30d0d8e..76e0aa4 100644 --- a/src/components/back-to-dashboard-link.tsx +++ b/src/components/back-to-dashboard-link.tsx @@ -5,10 +5,15 @@ export default function BackToDashBoardLink() { return ( - - Back to Dashboard + + Back to Dashboard ); } diff --git a/src/components/edit-class.tsx b/src/components/edit-class.tsx new file mode 100644 index 0000000..59d9d70 --- /dev/null +++ b/src/components/edit-class.tsx @@ -0,0 +1,177 @@ +"use client"; + +import { useState, useEffect } from "react"; +import { createClient } from "../lib/supabase/client"; +import { Pencil, Check, X } from "lucide-react"; + +// Map enum values to display names +const CLASS_OPTIONS = [ + { value: 'warrior', label: 'Python Warrior', comingSoon: false }, + { value: 'mage', label: 'Java Mage', comingSoon: true }, + { value: 'rogue', label: 'C++ Rogue', comingSoon: true }, +] as const; + +interface EditClassProps { + currentClass: string; + currentClassEnum: string | null; + userId: string; +} + +export default function EditClass({ currentClass, currentClassEnum, userId }: EditClassProps) { + const [isEditing, setIsEditing] = useState(false); + // Default to 'warrior' (Python Warrior) which is available + const [selectedClassEnum, setSelectedClassEnum] = useState(currentClassEnum || 'warrior'); + const [isSaving, setIsSaving] = useState(false); + const supabase = createClient(); + + useEffect(() => { + // If current class is coming soon or null, default to warrior (Python Warrior) + const currentOption = CLASS_OPTIONS.find(opt => opt.value === currentClassEnum); + if (!currentClassEnum || currentOption?.comingSoon) { + setSelectedClassEnum('warrior'); + } else { + setSelectedClassEnum(currentClassEnum); + } + }, [currentClassEnum]); + + const handleSave = async () => { + setIsSaving(true); + try { + // Use upsert to insert if profile doesn't exist, or update if it does + const { data, error } = await supabase + .from('profiles') + .upsert({ + id: userId, + class: selectedClassEnum + }, { + onConflict: 'id' + }) + .select(); + + if (error) { + console.error("Supabase error:", error); + throw error; + } + + console.log("Class saved successfully:", data); + setIsEditing(false); + // Refresh the page to show updated class + window.location.reload(); + } catch (error) { + console.error("Error updating class:", error); + alert(`Failed to update class: ${error instanceof Error ? error.message : 'Unknown error'}`); + } finally { + setIsSaving(false); + } + }; + + const handleCancel = () => { + // Reset to current class or default to warrior if coming soon + const currentOption = CLASS_OPTIONS.find(opt => opt.value === currentClassEnum); + if (!currentClassEnum || currentOption?.comingSoon) { + setSelectedClassEnum('warrior'); + } else { + setSelectedClassEnum(currentClassEnum); + } + setIsEditing(false); + }; + + if (isEditing) { + return ( +
+
+
+ + Class + + +
+ + +
+
+
+ ); + } + + return ( +
+
+
+
+
+ + Class + + + {currentClass || 'Not set'} + +
+ +
+
+
+ ); +} diff --git a/src/lib/types/types.ts b/src/lib/types/types.ts index f09bfd3..0d880ab 100644 --- a/src/lib/types/types.ts +++ b/src/lib/types/types.ts @@ -9,3 +9,10 @@ export type QuizData = { id: string; questions: Question[]; }; + +export type Profile = { + id: string; + class: string; + created_at: string; + updated_at: string; +};