diff --git a/client/App.tsx b/client/App.tsx index 7dc73b2c..0c98047a 100644 --- a/client/App.tsx +++ b/client/App.tsx @@ -60,7 +60,10 @@ const App = () => ( element={} /> } /> - } /> + } + /> } /> } /> } /> diff --git a/client/components/onboarding/Welcome.tsx b/client/components/onboarding/Welcome.tsx index b5a28258..7c5718d6 100644 --- a/client/components/onboarding/Welcome.tsx +++ b/client/components/onboarding/Welcome.tsx @@ -8,12 +8,7 @@ import { CardTitle, } from "@/components/ui/card"; import { Badge } from "@/components/ui/badge"; -import { - CheckCircle, - ArrowRight, - Sparkles, - ShieldCheck, -} from "lucide-react"; +import { CheckCircle, ArrowRight, Sparkles, ShieldCheck } from "lucide-react"; import { Link } from "react-router-dom"; import type { AethexAchievement } from "@/lib/aethex-database-adapter"; @@ -211,16 +206,23 @@ export default function Welcome({ {achievement.description &&

{achievement.description}

} - {typeof achievement.xp_reward === "number" && achievement.xp_reward > 0 && ( -
- +{achievement.xp_reward} XP added to your passport progression -
- )} + {typeof achievement.xp_reward === "number" && + achievement.xp_reward > 0 && ( +
+ +{achievement.xp_reward} XP added to your passport progression +
+ )}
- + Profile Verified - + Passport Updated
@@ -271,7 +273,10 @@ export default function Welcome({ variant="secondary" className="bg-aethex-500/20 text-aethex-100 border border-aethex-500/40" > - + Link OAuth Accounts diff --git a/client/components/passport/PassportSummary.tsx b/client/components/passport/PassportSummary.tsx index d3e04a06..6fb58869 100644 --- a/client/components/passport/PassportSummary.tsx +++ b/client/components/passport/PassportSummary.tsx @@ -109,18 +109,29 @@ const PassportSummary = ({ {realm?.label || "Innovation Commons"} - + Level {level} - + {totalXp} XP - + {loyaltyPoints} Loyalty @@ -150,7 +161,8 @@ const PassportSummary = ({

- {(profile as any)?.user_type?.replace("_", " ") || "community member"} + {(profile as any)?.user_type?.replace("_", " ") || + "community member"}

@@ -185,9 +197,7 @@ const PassportSummary = ({ className="rounded-xl border border-slate-800 bg-slate-900/40 p-4" >
- - {achievement.icon || "✨"} - + {achievement.icon || "✨"} {achievement.name} @@ -210,7 +220,11 @@ const PassportSummary = ({
{interests.map((interest) => ( - + {interest} ))} @@ -249,14 +263,20 @@ const PassportSummary = ({ <>
-
diff --git a/client/lib/aethex-database-adapter.ts b/client/lib/aethex-database-adapter.ts index cdd95618..6a39f677 100644 --- a/client/lib/aethex-database-adapter.ts +++ b/client/lib/aethex-database-adapter.ts @@ -131,7 +131,7 @@ export const aethexUserService = { if (isTableMissing(error)) { throw new Error( - "Supabase table \"user_profiles\" is missing. Please run the required migrations.", + 'Supabase table "user_profiles" is missing. Please run the required migrations.', ); } @@ -172,7 +172,7 @@ export const aethexUserService = { if (isTableMissing(error)) { throw new Error( - "Supabase table \"user_profiles\" is missing. Please run the required migrations.", + 'Supabase table "user_profiles" is missing. Please run the required migrations.', ); } @@ -194,18 +194,19 @@ export const aethexUserService = { if (error) { if (isTableMissing(error)) { throw new Error( - "Supabase table \"user_profiles\" is missing. Please run the required migrations.", + 'Supabase table "user_profiles" is missing. Please run the required migrations.', ); } throw error; } - return ((data as any[]) || []).map((row) => - ({ - ...(row as AethexUserProfile), - user_type: (row as any).user_type || "community_member", - experience_level: (row as any).experience_level || "beginner", - }) as AethexUserProfile, + return ((data as any[]) || []).map( + (row) => + ({ + ...(row as AethexUserProfile), + user_type: (row as any).user_type || "community_member", + experience_level: (row as any).experience_level || "beginner", + }) as AethexUserProfile, ); }, @@ -238,7 +239,7 @@ export const aethexUserService = { if (isTableMissing(error)) { throw new Error( - "Supabase table \"user_profiles\" is missing. Please run the required migrations.", + 'Supabase table "user_profiles" is missing. Please run the required migrations.', ); } @@ -280,7 +281,7 @@ export const aethexUserService = { if (error) { if (isTableMissing(error)) { throw new Error( - "Supabase table \"user_profiles\" is missing. Please run the required migrations.", + 'Supabase table "user_profiles" is missing. Please run the required migrations.', ); } throw error; @@ -315,7 +316,7 @@ export const aethexUserService = { if (error) { if (isTableMissing(error)) { throw new Error( - "Supabase table \"user_interests\" is missing. Please run the required migrations.", + 'Supabase table "user_interests" is missing. Please run the required migrations.', ); } throw error; @@ -333,7 +334,7 @@ export const aethexUserService = { if (error) { if (isTableMissing(error)) { throw new Error( - "Supabase table \"user_interests\" is missing. Please run the required migrations.", + 'Supabase table "user_interests" is missing. Please run the required migrations.', ); } console.warn("Error fetching interests:", error); @@ -610,7 +611,7 @@ export const aethexAchievementService = { if (error) { if (isTableMissing(error)) { throw new Error( - "Supabase table \"user_profiles\" is missing. Please run the required migrations.", + 'Supabase table "user_profiles" is missing. Please run the required migrations.', ); } console.warn("Unable to load profile for XP update:", error); diff --git a/client/lib/mock-auth.ts b/client/lib/mock-auth.ts index e500a6b3..1048bc1d 100644 --- a/client/lib/mock-auth.ts +++ b/client/lib/mock-auth.ts @@ -252,7 +252,8 @@ class MockAuthService { // Create new profile profile = { id: userId, - username: updates.username || this.currentUser?.email?.split("@")[0] || "user", + username: + updates.username || this.currentUser?.email?.split("@")[0] || "user", email: this.currentUser?.email || "", role: "member", onboarded: true, @@ -351,8 +352,9 @@ class MockAuthService { return { data: { provider: removedProvider }, error: null }; } - private authCallback: ((event: string, session: MockSession | null) => void) | - null = null; + private authCallback: + | ((event: string, session: MockSession | null) => void) + | null = null; private notifyAuthChange(event: string) { if (this.authCallback) { diff --git a/client/lib/onboarding.integration.spec.ts b/client/lib/onboarding.integration.spec.ts index 512f87a3..fd1f2d14 100644 --- a/client/lib/onboarding.integration.spec.ts +++ b/client/lib/onboarding.integration.spec.ts @@ -135,16 +135,24 @@ describe("onboarding passport flow", () => { bio: "Building awesome experiences", }); - const hydratedProfile = (await mockAuth.getUserProfile(user.id as any)) as AethexUserProfile; + const hydratedProfile = (await mockAuth.getUserProfile( + user.id as any, + )) as AethexUserProfile; expect(checkProfileComplete(hydratedProfile as any)).toBe(true); await mockAuth.linkIdentity({ provider: "github" }); const refreshedUser = (await mockAuth.getUser()).data.user; - expect(refreshedUser?.identities?.some((id: any) => id.provider === "github")).toBe(true); + expect( + refreshedUser?.identities?.some((id: any) => id.provider === "github"), + ).toBe(true); await aethexAchievementService.checkAndAwardOnboardingAchievement(user.id); - const achievements = await aethexAchievementService.getUserAchievements(user.id); - const welcomeBadge = achievements.find((item) => item.name === "Welcome to AeThex"); + const achievements = await aethexAchievementService.getUserAchievements( + user.id, + ); + const welcomeBadge = achievements.find( + (item) => item.name === "Welcome to AeThex", + ); expect(welcomeBadge).toBeTruthy(); }); }); diff --git a/client/pages/Onboarding.tsx b/client/pages/Onboarding.tsx index b9ba4791..7923d597 100644 --- a/client/pages/Onboarding.tsx +++ b/client/pages/Onboarding.tsx @@ -80,7 +80,10 @@ export default function Onboarding() { useState(null); const mapProfileToOnboardingData = useCallback( - (profile: AethexUserProfile | null, interests: string[]): OnboardingData => { + ( + profile: AethexUserProfile | null, + interests: string[], + ): OnboardingData => { const email = profile?.email || user?.email || ""; const fullName = profile?.full_name?.trim() || ""; const nameParts = fullName ? fullName.split(/\s+/).filter(Boolean) : []; @@ -120,8 +123,7 @@ export default function Onboarding() { userType: normalizedType, personalInfo: { firstName: - firstName || - (profile?.username ?? email.split("@")[0] ?? ""), + firstName || (profile?.username ?? email.split("@")[0] ?? ""), lastName, email, company: (profile as any)?.company || "", @@ -185,10 +187,7 @@ export default function Onboarding() { }, }; if (typeof parsed.step === "number") { - nextStep = Math.max( - 0, - Math.min(parsed.step, steps.length - 1), - ); + nextStep = Math.max(0, Math.min(parsed.step, steps.length - 1)); } restored = true; } @@ -326,8 +325,7 @@ export default function Onboarding() { username: normalizedFirst.replace(/\s+/g, "_"), full_name: `${normalizedFirst} ${normalizedLast}`.trim(), user_type: - (userTypeMap[data.userType || "member"] as any) || - "community_member", + (userTypeMap[data.userType || "member"] as any) || "community_member", experience_level: (data.experience.level as any) || "beginner", bio: data.experience.previousProjects?.trim() || undefined, } as any; diff --git a/client/pages/ProfilePassport.tsx b/client/pages/ProfilePassport.tsx index b5ae02ed..e5634177 100644 --- a/client/pages/ProfilePassport.tsx +++ b/client/pages/ProfilePassport.tsx @@ -46,7 +46,9 @@ const ProfilePassport = () => { const navigate = useNavigate(); const { user, linkedProviders } = useAuth(); - const [profile, setProfile] = useState<(AethexUserProfile & { email?: string | null }) | null>(null); + const [profile, setProfile] = useState< + (AethexUserProfile & { email?: string | null }) | null + >(null); const [achievements, setAchievements] = useState([]); const [projects, setProjects] = useState([]); const [interests, setInterests] = useState([]); @@ -74,14 +76,15 @@ const ProfilePassport = () => { const loadProfile = async () => { try { setLoading(true); - const [profileData, achievementList, interestList, projectList] = await Promise.all([ - params.id === "me" && profile && profile.id === targetUserId - ? Promise.resolve(profile) - : aethexUserService.getProfileById(targetUserId), - aethexAchievementService.getUserAchievements(targetUserId), - aethexUserService.getUserInterests(targetUserId), - aethexProjectService.getUserProjects(targetUserId).catch(() => []), - ]); + const [profileData, achievementList, interestList, projectList] = + await Promise.all([ + params.id === "me" && profile && profile.id === targetUserId + ? Promise.resolve(profile) + : aethexUserService.getProfileById(targetUserId), + aethexAchievementService.getUserAchievements(targetUserId), + aethexUserService.getUserInterests(targetUserId), + aethexProjectService.getUserProjects(targetUserId).catch(() => []), + ]); if (!profileData) { setNotFound(true); @@ -142,20 +145,29 @@ const ProfilePassport = () => {
-

Highlighted missions

+

+ Highlighted missions +

A snapshot of what this creator has shipped inside AeThex.

{isSelf && ( - )}
{projects.map((project) => ( - +
@@ -165,15 +177,23 @@ const ProfilePassport = () => { {project.description || "AeThex project"}
- + {project.status?.replace("_", " ") ?? "active"}
- {formatDate(project.created_at)} + {" "} + {formatDate(project.created_at)} - ) : ( - )}
{profile.github_url && ( - )} {profile.linkedin_url && ( - )} {profile.twitter_url && ( - )} {profile.website_url && ( - )} {profile.bio && ( - + {profile.bio} )} diff --git a/client/pages/ProfilesDirectory.tsx b/client/pages/ProfilesDirectory.tsx index 16d04019..adbde9e2 100644 --- a/client/pages/ProfilesDirectory.tsx +++ b/client/pages/ProfilesDirectory.tsx @@ -24,13 +24,7 @@ import { type AethexUserProfile, } from "@/lib/aethex-database-adapter"; import { cn } from "@/lib/utils"; -import { - Search, - RefreshCw, - UserRound, - Users, - Sparkles, -} from "lucide-react"; +import { Search, RefreshCw, UserRound, Users, Sparkles } from "lucide-react"; const realmFilters: Array<{ value: string; label: string }> = [ { value: "all", label: "All Realms" }, @@ -52,7 +46,8 @@ interface ProfileCardProps { } const ProfileCard = ({ profile }: ProfileCardProps) => { - const realmStyle = realmBadgeStyles[profile.user_type] || "bg-aethex-500 text-white"; + const realmStyle = + realmBadgeStyles[profile.user_type] || "bg-aethex-500 text-white"; return ( @@ -60,7 +55,10 @@ const ProfileCard = ({ profile }: ProfileCardProps) => { {profile.user_type.replace("_", " ")} - + Level {profile.level ?? 1}
@@ -92,12 +90,19 @@ const ProfileCard = ({ profile }: ProfileCardProps) => {
{(profile as any)?.skills?.slice(0, 3)?.map((skill: string) => ( - + {skill} ))} {((profile as any)?.skills?.length || 0) > 3 && ( - + +{((profile as any)?.skills?.length || 0) - 3} )} @@ -107,7 +112,10 @@ const ProfileCard = ({ profile }: ProfileCardProps) => { variant="outline" className="w-full border-slate-700/70 text-slate-100 transition-colors hover:border-aethex-400/60 hover:text-white" > - + View Passport @@ -129,15 +137,9 @@ const ProfilesDirectory = () => { const matchesRealm = realmFilter === "all" || profile.user_type === realmFilter; const matchesSearch = lowerSearch - ? [ - profile.full_name, - profile.username, - (profile as any)?.company, - ] + ? [profile.full_name, profile.username, (profile as any)?.company] .filter(Boolean) - .some((value) => - String(value).toLowerCase().includes(lowerSearch), - ) + .some((value) => String(value).toLowerCase().includes(lowerSearch)) : true; return matchesRealm && matchesSearch; }); @@ -178,7 +180,8 @@ const ProfilesDirectory = () => { Discover AeThex talent

- Browse verified creators, clients, and community members across every AeThex realm. + Browse verified creators, clients, and community members + across every AeThex realm.

@@ -190,7 +193,10 @@ const ProfilesDirectory = () => { Refresh -
@@ -228,7 +234,8 @@ const ProfilesDirectory = () => { No passports found

- Try adjusting your search or realm filters. New members join AeThex every day! + Try adjusting your search or realm filters. New members join + AeThex every day!

) : (