From 931158d9ddc4ec7a94323397719a043b63676336 Mon Sep 17 00:00:00 2001 From: "Builder.io" Date: Wed, 6 Aug 2025 00:29:35 +0000 Subject: [PATCH] Create database adapter for existing AeThex schema cgen-3c850819cf154266a319eebb1dbea822 --- client/lib/aethex-database-adapter.ts | 470 ++++++++++++++++++++++++++ 1 file changed, 470 insertions(+) create mode 100644 client/lib/aethex-database-adapter.ts diff --git a/client/lib/aethex-database-adapter.ts b/client/lib/aethex-database-adapter.ts new file mode 100644 index 00000000..86b95d80 --- /dev/null +++ b/client/lib/aethex-database-adapter.ts @@ -0,0 +1,470 @@ +// 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 { + 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): Promise { + 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): Promise { + 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 { + // 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 { + 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 { + 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): Promise { + 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): Promise { + 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 { + 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 { + 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 { + 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 { + 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 { + 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 { + // 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 { + 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 { + 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 { + 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 { + await supabase + .from('notifications') + .update({ is_read: true }) + .eq('id', notificationId); + }, + + async createNotification(userId: string, type: string, data: any): Promise { + 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(); + }, +};