Prettier format pending files
This commit is contained in:
parent
191460bf71
commit
385536e3ad
4 changed files with 180 additions and 123 deletions
|
|
@ -3,7 +3,10 @@ import { User, Session } from "@supabase/supabase-js";
|
|||
import { supabase } from "@/lib/supabase";
|
||||
import { UserProfile } from "@/lib/database.types";
|
||||
import { aethexToast } from "@/lib/aethex-toast";
|
||||
import { aethexUserService, type AethexUserProfile } from "@/lib/aethex-database-adapter";
|
||||
import {
|
||||
aethexUserService,
|
||||
type AethexUserProfile,
|
||||
} from "@/lib/aethex-database-adapter";
|
||||
|
||||
interface AuthContextType {
|
||||
user: User | null;
|
||||
|
|
@ -123,8 +126,12 @@ export const AuthProvider: React.FC<{ children: React.ReactNode }> = ({
|
|||
console.error("SignIn error details:", error);
|
||||
|
||||
let errorMessage = error.message;
|
||||
if (error.message?.includes("Failed to fetch") || error.name === "AuthRetryableFetchError") {
|
||||
errorMessage = "Unable to connect to authentication service. Please check your internet connection and try again.";
|
||||
if (
|
||||
error.message?.includes("Failed to fetch") ||
|
||||
error.name === "AuthRetryableFetchError"
|
||||
) {
|
||||
errorMessage =
|
||||
"Unable to connect to authentication service. Please check your internet connection and try again.";
|
||||
}
|
||||
|
||||
aethexToast.error({
|
||||
|
|
@ -151,7 +158,8 @@ export const AuthProvider: React.FC<{ children: React.ReactNode }> = ({
|
|||
if (data.user) {
|
||||
aethexToast.success({
|
||||
title: "Account created!",
|
||||
description: "Please check your email to verify your account, then sign in.",
|
||||
description:
|
||||
"Please check your email to verify your account, then sign in.",
|
||||
});
|
||||
}
|
||||
} catch (error: any) {
|
||||
|
|
|
|||
|
|
@ -13,7 +13,7 @@ console.log("Supabase Config:", {
|
|||
hasKey: !!supabaseAnonKey,
|
||||
url: supabaseUrl,
|
||||
keyPrefix: supabaseAnonKey?.substring(0, 20) + "...",
|
||||
isSupabaseConfigured
|
||||
isSupabaseConfigured,
|
||||
});
|
||||
|
||||
let supabaseClient: any = null;
|
||||
|
|
@ -31,12 +31,18 @@ if (isSupabaseConfigured) {
|
|||
setTimeout(async () => {
|
||||
try {
|
||||
console.log("🔍 Testing Supabase connection to:", supabaseUrl);
|
||||
const { data, error } = await supabaseClient.from('user_profiles').select('count', { count: 'exact', head: true });
|
||||
const { data, error } = await supabaseClient
|
||||
.from("user_profiles")
|
||||
.select("count", { count: "exact", head: true });
|
||||
if (error) {
|
||||
console.warn("⚠️ Supabase connection test failed:", error.message);
|
||||
console.log("🔄 Falling back to mock authentication for development");
|
||||
} else {
|
||||
console.log("✅ Supabase connection successful - found", data, "user profiles");
|
||||
console.log(
|
||||
"✅ Supabase connection successful - found",
|
||||
data,
|
||||
"user profiles",
|
||||
);
|
||||
}
|
||||
} catch (err: any) {
|
||||
console.warn("⚠️ Supabase connection error:", err.message);
|
||||
|
|
@ -61,7 +67,7 @@ if (isSupabaseConfigured) {
|
|||
// Create a proxy that falls back to mock when Supabase fails
|
||||
export const supabase = new Proxy(supabaseClient || {}, {
|
||||
get(target, prop) {
|
||||
if (prop === 'auth') {
|
||||
if (prop === "auth") {
|
||||
return {
|
||||
signInWithPassword: async (credentials: any) => {
|
||||
if (isSupabaseConfigured && target && target.auth) {
|
||||
|
|
@ -72,17 +78,27 @@ export const supabase = new Proxy(supabaseClient || {}, {
|
|||
return result;
|
||||
} catch (error: any) {
|
||||
console.warn("⚠️ Supabase authentication failed:", error.message);
|
||||
if (error.message?.includes('Failed to fetch') ||
|
||||
error.name === 'AuthRetryableFetchError' ||
|
||||
error.message?.includes('fetch')) {
|
||||
if (
|
||||
error.message?.includes("Failed to fetch") ||
|
||||
error.name === "AuthRetryableFetchError" ||
|
||||
error.message?.includes("fetch")
|
||||
) {
|
||||
console.log("🔄 Falling back to mock authentication");
|
||||
return await mockAuth.signInWithPassword(credentials.email, credentials.password);
|
||||
return await mockAuth.signInWithPassword(
|
||||
credentials.email,
|
||||
credentials.password,
|
||||
);
|
||||
}
|
||||
throw error;
|
||||
}
|
||||
} else {
|
||||
console.log("🔄 Using mock authentication (Supabase not configured)");
|
||||
return await mockAuth.signInWithPassword(credentials.email, credentials.password);
|
||||
console.log(
|
||||
"🔄 Using mock authentication (Supabase not configured)",
|
||||
);
|
||||
return await mockAuth.signInWithPassword(
|
||||
credentials.email,
|
||||
credentials.password,
|
||||
);
|
||||
}
|
||||
},
|
||||
signOut: async () => {
|
||||
|
|
@ -124,12 +140,12 @@ export const supabase = new Proxy(supabaseClient || {}, {
|
|||
}
|
||||
}
|
||||
return mockAuth.onAuthStateChange(callback);
|
||||
}
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
if (prop === 'from') {
|
||||
if (isSupabaseConfigured && target && typeof target.from === 'function') {
|
||||
if (prop === "from") {
|
||||
if (isSupabaseConfigured && target && typeof target.from === "function") {
|
||||
return target.from.bind(target);
|
||||
}
|
||||
|
||||
|
|
@ -147,16 +163,20 @@ export const supabase = new Proxy(supabaseClient || {}, {
|
|||
single: async () => ({ data: rows[0] ?? {}, error: null }),
|
||||
then: (resolve: any) => resolve({ data: rows, error: null }),
|
||||
catch: () => builder,
|
||||
finally: (cb: any) => { cb?.(); return builder; },
|
||||
finally: (cb: any) => {
|
||||
cb?.();
|
||||
return builder;
|
||||
},
|
||||
};
|
||||
return builder;
|
||||
};
|
||||
|
||||
const createMockTable = (table: string) => ({
|
||||
select: (_cols?: any, _opts?: any) => createMockBuilder([]),
|
||||
insert: (payload?: any) => createMockBuilder(
|
||||
Array.isArray(payload) ? payload : payload ? [payload] : [],
|
||||
),
|
||||
insert: (payload?: any) =>
|
||||
createMockBuilder(
|
||||
Array.isArray(payload) ? payload : payload ? [payload] : [],
|
||||
),
|
||||
update: (payload?: any) => createMockBuilder(payload || {}),
|
||||
delete: () => createMockBuilder([]),
|
||||
});
|
||||
|
|
@ -165,7 +185,7 @@ export const supabase = new Proxy(supabaseClient || {}, {
|
|||
}
|
||||
|
||||
return target[prop];
|
||||
}
|
||||
},
|
||||
});
|
||||
|
||||
// Auth helpers
|
||||
|
|
@ -189,7 +209,7 @@ export const channel = supabase.channel;
|
|||
try {
|
||||
const testLogin = await supabase.auth.signInWithPassword({
|
||||
email: "test@example.com",
|
||||
password: "test123"
|
||||
password: "test123",
|
||||
});
|
||||
console.log("Auth test result:", testLogin);
|
||||
} catch (error) {
|
||||
|
|
@ -197,7 +217,10 @@ export const channel = supabase.channel;
|
|||
}
|
||||
|
||||
try {
|
||||
const { data, error } = await supabase.from('user_profiles').select('*').limit(1);
|
||||
const { data, error } = await supabase
|
||||
.from("user_profiles")
|
||||
.select("*")
|
||||
.limit(1);
|
||||
console.log("Database test - data:", data, "error:", error);
|
||||
} catch (dbError) {
|
||||
console.error("Database test error:", dbError);
|
||||
|
|
|
|||
|
|
@ -55,7 +55,11 @@ export default function Dashboard() {
|
|||
});
|
||||
|
||||
useEffect(() => {
|
||||
console.log("Dashboard useEffect:", { user: !!user, profile: !!profile, authLoading });
|
||||
console.log("Dashboard useEffect:", {
|
||||
user: !!user,
|
||||
profile: !!profile,
|
||||
authLoading,
|
||||
});
|
||||
|
||||
// Only redirect to login when auth is resolved and there's no user
|
||||
if (!user && !authLoading) {
|
||||
|
|
@ -97,7 +101,9 @@ export default function Dashboard() {
|
|||
// Load user's achievements with error handling
|
||||
let userAchievements = [];
|
||||
try {
|
||||
userAchievements = await aethexAchievementService.getUserAchievements(user!.id);
|
||||
userAchievements = await aethexAchievementService.getUserAchievements(
|
||||
user!.id,
|
||||
);
|
||||
setAchievements(userAchievements);
|
||||
} catch (achievementError) {
|
||||
console.warn("Could not load achievements:", achievementError);
|
||||
|
|
@ -283,8 +289,12 @@ export default function Dashboard() {
|
|||
<div className="flex items-center space-x-3">
|
||||
<User className="h-5 w-5 text-orange-400" />
|
||||
<div>
|
||||
<h3 className="text-white font-semibold">Complete Your Profile</h3>
|
||||
<p className="text-orange-200 text-sm">Set up your profile to unlock all features</p>
|
||||
<h3 className="text-white font-semibold">
|
||||
Complete Your Profile
|
||||
</h3>
|
||||
<p className="text-orange-200 text-sm">
|
||||
Set up your profile to unlock all features
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
<div className="flex space-x-2">
|
||||
|
|
@ -315,10 +325,12 @@ export default function Dashboard() {
|
|||
<div className="flex flex-col lg:flex-row justify-between items-start lg:items-center gap-4">
|
||||
<div>
|
||||
<h1 className="text-3xl font-bold text-gradient-purple">
|
||||
Welcome back, {profile?.full_name || user.email?.split('@')[0]}
|
||||
Welcome back,{" "}
|
||||
{profile?.full_name || user.email?.split("@")[0]}
|
||||
</h1>
|
||||
<p className="text-muted-foreground">
|
||||
{profile?.role || 'Member'} • Level {profile?.level || 1} • 7 day streak 🔥
|
||||
{profile?.role || "Member"} • Level {profile?.level || 1} • 7
|
||||
day streak 🔥
|
||||
</p>
|
||||
</div>
|
||||
<div className="flex items-center space-x-4">
|
||||
|
|
@ -343,7 +355,10 @@ export default function Dashboard() {
|
|||
<div className="text-center space-y-4">
|
||||
<div className="relative">
|
||||
<img
|
||||
src={profile?.avatar_url || 'https://images.unsplash.com/photo-1472099645785-5658abf4ff4e?w=400&h=400&fit=crop&crop=face'}
|
||||
src={
|
||||
profile?.avatar_url ||
|
||||
"https://images.unsplash.com/photo-1472099645785-5658abf4ff4e?w=400&h=400&fit=crop&crop=face"
|
||||
}
|
||||
alt="User Avatar"
|
||||
className="w-20 h-20 rounded-full mx-auto ring-4 ring-aethex-400/20 hover:ring-aethex-400/50 transition-all duration-300"
|
||||
/>
|
||||
|
|
@ -351,10 +366,10 @@ export default function Dashboard() {
|
|||
</div>
|
||||
<div>
|
||||
<h3 className="font-semibold text-gradient">
|
||||
{profile?.full_name || user.email?.split('@')[0]}
|
||||
{profile?.full_name || user.email?.split("@")[0]}
|
||||
</h3>
|
||||
<p className="text-sm text-muted-foreground">
|
||||
{profile?.role || 'Member'}
|
||||
{profile?.role || "Member"}
|
||||
</p>
|
||||
<Badge
|
||||
variant="outline"
|
||||
|
|
@ -369,7 +384,8 @@ export default function Dashboard() {
|
|||
<div className="flex justify-between text-sm">
|
||||
<span>XP Progress</span>
|
||||
<span>
|
||||
{profile?.total_xp || 0} / {((profile?.level || 1) * 1000)}
|
||||
{profile?.total_xp || 0} /{" "}
|
||||
{(profile?.level || 1) * 1000}
|
||||
</span>
|
||||
</div>
|
||||
<div className="w-full bg-muted rounded-full h-2">
|
||||
|
|
@ -481,58 +497,60 @@ export default function Dashboard() {
|
|||
</div>
|
||||
) : (
|
||||
projects.slice(0, 3).map((project: any, index) => (
|
||||
<div
|
||||
key={index}
|
||||
className="flex items-center justify-between p-4 rounded-lg border border-border/30 hover:border-aethex-400/50 transition-all duration-300 hover-lift animate-slide-right"
|
||||
style={{ animationDelay: `${index * 0.1}s` }}
|
||||
>
|
||||
<div className="flex items-center space-x-4">
|
||||
<div className="w-12 h-12 rounded-lg bg-gradient-to-r from-aethex-500/20 to-neon-blue/20 flex items-center justify-center">
|
||||
<Rocket className="h-6 w-6 text-aethex-400" />
|
||||
</div>
|
||||
<div>
|
||||
<h4 className="font-semibold">{project.title}</h4>
|
||||
<p className="text-sm text-muted-foreground">
|
||||
{project.status?.replace("_", " ").toUpperCase()} •{" "}
|
||||
{project.technologies?.slice(0, 2).join(", ") ||
|
||||
"No tech specified"}
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
<div className="flex items-center space-x-4">
|
||||
<div className="text-right">
|
||||
<p className="text-sm font-medium">
|
||||
{getProgressPercentage(project)}%
|
||||
</p>
|
||||
<div className="w-20 bg-muted rounded-full h-2 mt-1">
|
||||
<div
|
||||
className="bg-gradient-to-r from-aethex-500 to-neon-blue h-2 rounded-full"
|
||||
style={{
|
||||
width: `${getProgressPercentage(project)}%`,
|
||||
}}
|
||||
/>
|
||||
<div
|
||||
key={index}
|
||||
className="flex items-center justify-between p-4 rounded-lg border border-border/30 hover:border-aethex-400/50 transition-all duration-300 hover-lift animate-slide-right"
|
||||
style={{ animationDelay: `${index * 0.1}s` }}
|
||||
>
|
||||
<div className="flex items-center space-x-4">
|
||||
<div className="w-12 h-12 rounded-lg bg-gradient-to-r from-aethex-500/20 to-neon-blue/20 flex items-center justify-center">
|
||||
<Rocket className="h-6 w-6 text-aethex-400" />
|
||||
</div>
|
||||
<div>
|
||||
<h4 className="font-semibold">{project.title}</h4>
|
||||
<p className="text-sm text-muted-foreground">
|
||||
{project.status?.replace("_", " ").toUpperCase()}{" "}
|
||||
•{" "}
|
||||
{project.technologies?.slice(0, 2).join(", ") ||
|
||||
"No tech specified"}
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
<Badge
|
||||
variant={
|
||||
getPriorityFromTech(project.technologies || []) ===
|
||||
"High"
|
||||
? "destructive"
|
||||
: "secondary"
|
||||
}
|
||||
className="animate-pulse"
|
||||
>
|
||||
{getPriorityFromTech(project.technologies || [])}
|
||||
</Badge>
|
||||
<Button
|
||||
variant="ghost"
|
||||
size="sm"
|
||||
className="hover-lift"
|
||||
>
|
||||
<MoreHorizontal className="h-4 w-4" />
|
||||
</Button>
|
||||
<div className="flex items-center space-x-4">
|
||||
<div className="text-right">
|
||||
<p className="text-sm font-medium">
|
||||
{getProgressPercentage(project)}%
|
||||
</p>
|
||||
<div className="w-20 bg-muted rounded-full h-2 mt-1">
|
||||
<div
|
||||
className="bg-gradient-to-r from-aethex-500 to-neon-blue h-2 rounded-full"
|
||||
style={{
|
||||
width: `${getProgressPercentage(project)}%`,
|
||||
}}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
<Badge
|
||||
variant={
|
||||
getPriorityFromTech(
|
||||
project.technologies || [],
|
||||
) === "High"
|
||||
? "destructive"
|
||||
: "secondary"
|
||||
}
|
||||
className="animate-pulse"
|
||||
>
|
||||
{getPriorityFromTech(project.technologies || [])}
|
||||
</Badge>
|
||||
<Button
|
||||
variant="ghost"
|
||||
size="sm"
|
||||
className="hover-lift"
|
||||
>
|
||||
<MoreHorizontal className="h-4 w-4" />
|
||||
</Button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
))
|
||||
)}
|
||||
</CardContent>
|
||||
|
|
@ -551,49 +569,54 @@ export default function Dashboard() {
|
|||
{achievements.length === 0 ? (
|
||||
<div className="col-span-full text-center py-8 text-muted-foreground">
|
||||
<Trophy className="h-12 w-12 mx-auto mb-4 opacity-50" />
|
||||
<p>No achievements unlocked yet. Complete projects to earn achievements!</p>
|
||||
<p>
|
||||
No achievements unlocked yet. Complete projects to
|
||||
earn achievements!
|
||||
</p>
|
||||
</div>
|
||||
) : (
|
||||
achievements.map((achievement: any, index) => {
|
||||
const Icon = getAchievementIcon(achievement.icon || 'star');
|
||||
return (
|
||||
<div
|
||||
key={index}
|
||||
className={`p-4 rounded-lg border transition-all duration-300 hover-lift animate-scale-in ${
|
||||
achievement.earned
|
||||
? "border-aethex-400/50 bg-aethex-500/10"
|
||||
: "border-border/30 opacity-60"
|
||||
}`}
|
||||
style={{ animationDelay: `${index * 0.1}s` }}
|
||||
>
|
||||
<div className="flex items-center space-x-3">
|
||||
<div
|
||||
className={`p-2 rounded-lg ${
|
||||
achievement.earned
|
||||
? "bg-gradient-to-r from-aethex-500 to-neon-blue"
|
||||
: "bg-muted"
|
||||
}`}
|
||||
>
|
||||
<Icon
|
||||
className={`h-5 w-5 ${achievement.earned ? "text-white" : "text-muted-foreground"}`}
|
||||
/>
|
||||
</div>
|
||||
<div>
|
||||
<h4
|
||||
className={`font-semibold ${achievement.earned ? "text-gradient" : ""}`}
|
||||
const Icon = getAchievementIcon(
|
||||
achievement.icon || "star",
|
||||
);
|
||||
return (
|
||||
<div
|
||||
key={index}
|
||||
className={`p-4 rounded-lg border transition-all duration-300 hover-lift animate-scale-in ${
|
||||
achievement.earned
|
||||
? "border-aethex-400/50 bg-aethex-500/10"
|
||||
: "border-border/30 opacity-60"
|
||||
}`}
|
||||
style={{ animationDelay: `${index * 0.1}s` }}
|
||||
>
|
||||
<div className="flex items-center space-x-3">
|
||||
<div
|
||||
className={`p-2 rounded-lg ${
|
||||
achievement.earned
|
||||
? "bg-gradient-to-r from-aethex-500 to-neon-blue"
|
||||
: "bg-muted"
|
||||
}`}
|
||||
>
|
||||
{achievement.title}
|
||||
</h4>
|
||||
<p className="text-sm text-muted-foreground">
|
||||
{achievement.description}
|
||||
</p>
|
||||
<Icon
|
||||
className={`h-5 w-5 ${achievement.earned ? "text-white" : "text-muted-foreground"}`}
|
||||
/>
|
||||
</div>
|
||||
<div>
|
||||
<h4
|
||||
className={`font-semibold ${achievement.earned ? "text-gradient" : ""}`}
|
||||
>
|
||||
{achievement.title}
|
||||
</h4>
|
||||
<p className="text-sm text-muted-foreground">
|
||||
{achievement.description}
|
||||
</p>
|
||||
</div>
|
||||
{achievement.earned && (
|
||||
<Star className="h-5 w-5 text-yellow-500 animate-pulse" />
|
||||
)}
|
||||
</div>
|
||||
{achievement.earned && (
|
||||
<Star className="h-5 w-5 text-yellow-500 animate-pulse" />
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
);
|
||||
})
|
||||
)}
|
||||
</div>
|
||||
|
|
|
|||
|
|
@ -123,8 +123,10 @@ export default function Onboarding() {
|
|||
const existing = await aethexUserService.getCurrentUser();
|
||||
const payload = {
|
||||
username: `${data.personalInfo.firstName || user.email?.split("@")[0] || "user"}`,
|
||||
full_name: `${data.personalInfo.firstName} ${data.personalInfo.lastName}`.trim(),
|
||||
user_type: (userTypeMap[data.userType || "member"] as any) || "community_member",
|
||||
full_name:
|
||||
`${data.personalInfo.firstName} ${data.personalInfo.lastName}`.trim(),
|
||||
user_type:
|
||||
(userTypeMap[data.userType || "member"] as any) || "community_member",
|
||||
experience_level: (data.experience.level as any) || "beginner",
|
||||
bio: data.experience.previousProjects || undefined,
|
||||
} as any;
|
||||
|
|
@ -157,7 +159,8 @@ export default function Onboarding() {
|
|||
function formatError(err: any) {
|
||||
if (!err) return "Unknown error";
|
||||
if (typeof err === "string") return err;
|
||||
if (err instanceof Error) return err.message + (err.stack ? `\n${err.stack}` : "");
|
||||
if (err instanceof Error)
|
||||
return err.message + (err.stack ? `\n${err.stack}` : "");
|
||||
if ((err as any).message) return (err as any).message;
|
||||
try {
|
||||
return JSON.stringify(err);
|
||||
|
|
|
|||
Loading…
Reference in a new issue