From c20613a8bef00be61c5eb36064e905fc4be40456 Mon Sep 17 00:00:00 2001 From: "Builder.io" Date: Wed, 6 Aug 2025 00:04:05 +0000 Subject: [PATCH] Create Supabase service helpers cgen-b271b1ef9e5a4a1a96416f389455706a --- client/lib/supabase-service.ts | 321 +++++++++++++++++++++++++++++++++ 1 file changed, 321 insertions(+) create mode 100644 client/lib/supabase-service.ts diff --git a/client/lib/supabase-service.ts b/client/lib/supabase-service.ts new file mode 100644 index 00000000..98a56ff0 --- /dev/null +++ b/client/lib/supabase-service.ts @@ -0,0 +1,321 @@ +import { supabase } from './supabase'; +import type { Database, UserProfile, Project, Achievement, CommunityPost } from './database.types'; + +// User Profile Services +export const userProfileService = { + async getProfile(userId: string): Promise { + const { data, error } = await supabase + .from('user_profiles') + .select('*') + .eq('id', userId) + .single(); + + if (error && error.code !== 'PGRST116') { + throw error; + } + + return data; + }, + + async updateProfile(userId: string, updates: Partial): Promise { + const { data, error } = await supabase + .from('user_profiles') + .update(updates) + .eq('id', userId) + .select() + .single(); + + if (error) throw error; + return data; + }, + + async createProfile(profile: Omit): Promise { + const { data, error } = await supabase + .from('user_profiles') + .insert(profile) + .select() + .single(); + + if (error) throw error; + return data; + }, + + async addInterests(userId: string, interests: string[]): Promise { + const interestRows = interests.map(interest => ({ + user_id: userId, + interest, + })); + + const { error } = await supabase + .from('user_interests') + .insert(interestRows); + + if (error) throw error; + }, + + async getUserInterests(userId: string): Promise { + const { data, error } = await supabase + .from('user_interests') + .select('interest') + .eq('user_id', userId); + + if (error) throw error; + return data.map(item => item.interest); + }, +}; + +// Project Services +export const projectService = { + async getUserProjects(userId: string): Promise { + const { data, error } = await supabase + .from('projects') + .select('*') + .eq('user_id', userId) + .order('created_at', { ascending: false }); + + if (error) throw error; + return data; + }, + + async createProject(project: Omit): Promise { + const { data, error } = await supabase + .from('projects') + .insert(project) + .select() + .single(); + + if (error) throw error; + return data; + }, + + async updateProject(projectId: string, updates: Partial): Promise { + const { data, error } = await supabase + .from('projects') + .update(updates) + .eq('id', projectId) + .select() + .single(); + + if (error) throw error; + return data; + }, + + async deleteProject(projectId: string): Promise { + const { error } = await supabase + .from('projects') + .delete() + .eq('id', projectId); + + if (error) throw error; + }, + + async getAllProjects(limit = 10): Promise { + const { data, error } = await supabase + .from('projects') + .select(` + *, + user_profiles ( + username, + full_name, + avatar_url + ) + `) + .eq('status', 'completed') + .order('created_at', { ascending: false }) + .limit(limit); + + if (error) throw error; + return data; + }, +}; + +// Achievement Services +export const achievementService = { + async getAllAchievements(): Promise { + const { data, error } = await supabase + .from('achievements') + .select('*') + .order('xp_reward', { ascending: false }); + + if (error) throw error; + return data; + }, + + async getUserAchievements(userId: string): Promise { + const { data, error } = await supabase + .from('user_achievements') + .select(` + earned_at, + achievements (*) + `) + .eq('user_id', userId) + .order('earned_at', { ascending: false }); + + if (error) throw error; + return data.map(item => item.achievements).filter(Boolean) as Achievement[]; + }, + + 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 + throw error; + } + }, + + async checkAndAwardAchievements(userId: string): Promise { + // Check for various achievement conditions + const profile = await userProfileService.getProfile(userId); + const projects = await projectService.getUserProjects(userId); + + if (!profile) return; + + const achievements = await this.getAllAchievements(); + + // Welcome achievement + if (profile.full_name && profile.user_type) { + const welcomeAchievement = achievements.find(a => a.name === 'Welcome to AeThex'); + if (welcomeAchievement) { + await this.awardAchievement(userId, welcomeAchievement.id); + } + } + + // First project achievement + if (projects.length >= 1) { + const firstProjectAchievement = achievements.find(a => a.name === 'First Project'); + if (firstProjectAchievement) { + await this.awardAchievement(userId, firstProjectAchievement.id); + } + } + + // Experienced developer achievement + const completedProjects = projects.filter(p => p.status === 'completed'); + if (completedProjects.length >= 5) { + const experiencedAchievement = achievements.find(a => a.name === 'Experienced Developer'); + if (experiencedAchievement) { + await this.awardAchievement(userId, experiencedAchievement.id); + } + } + }, +}; + +// Community Services +export const communityService = { + async getPosts(limit = 10): Promise { + const { data, error } = await supabase + .from('community_posts') + .select(` + *, + user_profiles ( + username, + full_name, + avatar_url + ) + `) + .eq('is_published', true) + .order('created_at', { ascending: false }) + .limit(limit); + + if (error) throw error; + return data; + }, + + async createPost(post: Omit): Promise { + const { data, error } = await supabase + .from('community_posts') + .insert(post) + .select() + .single(); + + if (error) throw error; + return data; + }, + + async getUserPosts(userId: string): Promise { + const { data, error } = await supabase + .from('community_posts') + .select('*') + .eq('author_id', userId) + .order('created_at', { ascending: false }); + + if (error) throw error; + return data; + }, +}; + +// Notification Services +export const notificationService = { + 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) throw error; + return data; + }, + + async markAsRead(notificationId: string): Promise { + const { error } = await supabase + .from('notifications') + .update({ read: true }) + .eq('id', notificationId); + + if (error) throw error; + }, + + async createNotification(userId: string, title: string, message?: string, type = 'info'): Promise { + const { error } = await supabase + .from('notifications') + .insert({ + user_id: userId, + title, + message, + type, + }); + + if (error) throw error; + }, +}; + +// Real-time subscriptions +export const realtimeService = { + 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(); + }, + + subscribeToCommunityPosts(callback: (post: any) => void) { + return supabase + .channel('community_posts') + .on( + 'postgres_changes', + { + event: 'INSERT', + schema: 'public', + table: 'community_posts', + filter: 'is_published=eq.true', + }, + callback + ) + .subscribe(); + }, +};