aethex-forge/client/lib/aethex-database-adapter.ts
Builder.io 931158d9dd Create database adapter for existing AeThex schema
cgen-3c850819cf154266a319eebb1dbea822
2025-08-06 00:29:35 +00:00

470 lines
12 KiB
TypeScript

// Database adapter for existing AeThex community platform
// Maps existing schema to our application needs
import { supabase } from './supabase';
import type { Database } from './database.types';
import { aethexToast } from './aethex-toast';
// Extended user profile type that matches existing + new schema
export interface AethexUserProfile {
id: string;
username: string;
email?: string;
role: string;
onboarded: boolean;
bio?: string;
skills?: string[];
avatar_url?: string;
banner_url?: string;
social_links?: any;
loyalty_points: number;
created_at: string;
updated_at: string;
// New AeThex app fields
user_type?: 'game_developer' | 'client' | 'community_member' | 'customer';
experience_level?: 'beginner' | 'intermediate' | 'advanced' | 'expert';
full_name?: string;
location?: string;
website_url?: string;
github_url?: string;
twitter_url?: string;
linkedin_url?: string;
total_xp?: number;
level?: number;
}
export interface AethexProject {
id: string;
user_id: string;
title: string;
description?: string;
status: 'planning' | 'in_progress' | 'completed' | 'on_hold';
technologies?: string[];
github_url?: string;
demo_url?: string;
image_url?: string;
start_date?: string;
end_date?: string;
created_at: string;
updated_at: string;
}
export interface AethexAchievement {
id: string;
name: string;
description: string;
icon?: string;
points_reward: number;
badge_color?: string;
created_at: string;
}
export interface AethexUserAchievement {
id: string;
user_id: string;
achievement_id: string;
unlocked_at: string;
}
// User Profile Services
export const aethexUserService = {
async getCurrentUser(): Promise<AethexUserProfile | null> {
const { data: { user } } = await supabase.auth.getUser();
if (!user) return null;
const { data, error } = await supabase
.from('profiles')
.select('*')
.eq('id', user.id)
.single();
if (error) {
console.error('Error fetching user profile:', error);
return null;
}
return data as AethexUserProfile;
},
async updateProfile(userId: string, updates: Partial<AethexUserProfile>): Promise<AethexUserProfile | null> {
const { data, error } = await supabase
.from('profiles')
.update(updates)
.eq('id', userId)
.select()
.single();
if (error) {
console.error('Error updating profile:', error);
throw error;
}
return data as AethexUserProfile;
},
async createInitialProfile(userId: string, profileData: Partial<AethexUserProfile>): Promise<AethexUserProfile | null> {
const { data, error } = await supabase
.from('profiles')
.insert({
id: userId,
username: profileData.username || `user_${Date.now()}`,
user_type: profileData.user_type || 'community_member',
experience_level: profileData.experience_level || 'beginner',
full_name: profileData.full_name,
email: profileData.email,
...profileData
})
.select()
.single();
if (error) {
console.error('Error creating profile:', error);
throw error;
}
return data as AethexUserProfile;
},
async addUserInterests(userId: string, interests: string[]): Promise<void> {
// First, delete existing interests
await supabase
.from('user_interests')
.delete()
.eq('user_id', userId);
// Insert new interests
const interestRows = interests.map(interest => ({
user_id: userId,
interest,
}));
const { error } = await supabase
.from('user_interests')
.insert(interestRows);
if (error) {
console.error('Error adding interests:', error);
throw error;
}
},
async getUserInterests(userId: string): Promise<string[]> {
const { data, error } = await supabase
.from('user_interests')
.select('interest')
.eq('user_id', userId);
if (error) {
console.error('Error fetching interests:', error);
return [];
}
return data.map(item => item.interest);
},
};
// Project Services
export const aethexProjectService = {
async getUserProjects(userId: string): Promise<AethexProject[]> {
const { data, error } = await supabase
.from('projects')
.select('*')
.eq('user_id', userId)
.order('created_at', { ascending: false });
if (error) {
console.error('Error fetching projects:', error);
return [];
}
return data as AethexProject[];
},
async createProject(project: Omit<AethexProject, 'id' | 'created_at' | 'updated_at'>): Promise<AethexProject | null> {
const { data, error } = await supabase
.from('projects')
.insert(project)
.select()
.single();
if (error) {
console.error('Error creating project:', error);
throw error;
}
return data as AethexProject;
},
async updateProject(projectId: string, updates: Partial<AethexProject>): Promise<AethexProject | null> {
const { data, error } = await supabase
.from('projects')
.update(updates)
.eq('id', projectId)
.select()
.single();
if (error) {
console.error('Error updating project:', error);
throw error;
}
return data as AethexProject;
},
async deleteProject(projectId: string): Promise<boolean> {
const { error } = await supabase
.from('projects')
.delete()
.eq('id', projectId);
if (error) {
console.error('Error deleting project:', error);
return false;
}
return true;
},
async getAllProjects(limit = 10): Promise<AethexProject[]> {
const { data, error } = await supabase
.from('projects')
.select(`
*,
profiles!projects_user_id_fkey (
username,
full_name,
avatar_url
)
`)
.eq('status', 'completed')
.order('created_at', { ascending: false })
.limit(limit);
if (error) {
console.error('Error fetching all projects:', error);
return [];
}
return data as AethexProject[];
},
};
// Achievement Services (maps to existing achievements table)
export const aethexAchievementService = {
async getAllAchievements(): Promise<AethexAchievement[]> {
const { data, error } = await supabase
.from('achievements')
.select('*')
.order('points_reward', { ascending: false });
if (error) {
console.error('Error fetching achievements:', error);
return [];
}
return data as AethexAchievement[];
},
async getUserAchievements(userId: string): Promise<AethexAchievement[]> {
const { data, error } = await supabase
.from('user_achievements')
.select(`
unlocked_at,
achievements (*)
`)
.eq('user_id', userId)
.order('unlocked_at', { ascending: false });
if (error) {
console.error('Error fetching user achievements:', error);
return [];
}
return data.map(item => item.achievements).filter(Boolean) as AethexAchievement[];
},
async awardAchievement(userId: string, achievementId: string): Promise<void> {
const { error } = await supabase
.from('user_achievements')
.insert({
user_id: userId,
achievement_id: achievementId,
});
if (error && error.code !== '23505') { // Ignore duplicate key error
console.error('Error awarding achievement:', error);
throw error;
}
// Get achievement details for toast
const { data: achievement } = await supabase
.from('achievements')
.select('*')
.eq('id', achievementId)
.single();
if (achievement) {
aethexToast.aethex({
title: 'Achievement Unlocked! 🎉',
description: `${achievement.icon} ${achievement.name} - ${achievement.description}`,
duration: 8000,
});
// Update user's total XP and level
await this.updateUserXPAndLevel(userId, achievement.points_reward);
}
},
async updateUserXPAndLevel(userId: string, xpGained: number): Promise<void> {
// Get current user data
const { data: profile } = await supabase
.from('profiles')
.select('total_xp, level, loyalty_points')
.eq('id', userId)
.single();
if (!profile) return;
const newTotalXP = (profile.total_xp || 0) + xpGained;
const newLevel = Math.floor(newTotalXP / 1000) + 1; // 1000 XP per level
const newLoyaltyPoints = (profile.loyalty_points || 0) + xpGained;
// Update profile
await supabase
.from('profiles')
.update({
total_xp: newTotalXP,
level: newLevel,
loyalty_points: newLoyaltyPoints,
})
.eq('id', userId);
// Check for level-up achievements
if (newLevel > (profile.level || 1)) {
if (newLevel >= 5) {
const levelUpAchievement = await supabase
.from('achievements')
.select('id')
.eq('name', 'Level Master')
.single();
if (levelUpAchievement.data) {
await this.awardAchievement(userId, levelUpAchievement.data.id);
}
}
}
},
async checkAndAwardOnboardingAchievement(userId: string): Promise<void> {
const { data: achievement } = await supabase
.from('achievements')
.select('id')
.eq('name', 'AeThex Explorer')
.single();
if (achievement) {
await this.awardAchievement(userId, achievement.id);
}
},
async checkAndAwardProjectAchievements(userId: string): Promise<void> {
const projects = await aethexProjectService.getUserProjects(userId);
// First project achievement
if (projects.length >= 1) {
const { data: achievement } = await supabase
.from('achievements')
.select('id')
.eq('name', 'Portfolio Creator')
.single();
if (achievement) {
await this.awardAchievement(userId, achievement.id);
}
}
// Project master achievement
const completedProjects = projects.filter(p => p.status === 'completed');
if (completedProjects.length >= 10) {
const { data: achievement } = await supabase
.from('achievements')
.select('id')
.eq('name', 'Project Master')
.single();
if (achievement) {
await this.awardAchievement(userId, achievement.id);
}
}
},
};
// Notification Service (uses existing notifications table)
export const aethexNotificationService = {
async getUserNotifications(userId: string): Promise<any[]> {
const { data, error } = await supabase
.from('notifications')
.select('*')
.eq('user_id', userId)
.order('created_at', { ascending: false })
.limit(10);
if (error) {
console.error('Error fetching notifications:', error);
return [];
}
return data;
},
async markAsRead(notificationId: string): Promise<void> {
await supabase
.from('notifications')
.update({ is_read: true })
.eq('id', notificationId);
},
async createNotification(userId: string, type: string, data: any): Promise<void> {
await supabase
.from('notifications')
.insert({
user_id: userId,
type,
data,
});
},
};
// Real-time subscriptions
export const aethexRealtimeService = {
subscribeToUserNotifications(userId: string, callback: (notification: any) => void) {
return supabase
.channel(`notifications:${userId}`)
.on(
'postgres_changes',
{
event: 'INSERT',
schema: 'public',
table: 'notifications',
filter: `user_id=eq.${userId}`,
},
callback
)
.subscribe();
},
subscribeToProjects(callback: (project: any) => void) {
return supabase
.channel('projects')
.on(
'postgres_changes',
{
event: 'INSERT',
schema: 'public',
table: 'projects',
},
callback
)
.subscribe();
},
};