Create Supabase service helpers
cgen-b271b1ef9e5a4a1a96416f389455706a
This commit is contained in:
parent
981e25bfb8
commit
c20613a8be
1 changed files with 321 additions and 0 deletions
321
client/lib/supabase-service.ts
Normal file
321
client/lib/supabase-service.ts
Normal file
|
|
@ -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<UserProfile | null> {
|
||||
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<UserProfile>): Promise<UserProfile> {
|
||||
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<UserProfile, 'created_at' | 'updated_at'>): Promise<UserProfile> {
|
||||
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<void> {
|
||||
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<string[]> {
|
||||
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<Project[]> {
|
||||
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<Project, 'id' | 'created_at' | 'updated_at'>): Promise<Project> {
|
||||
const { data, error } = await supabase
|
||||
.from('projects')
|
||||
.insert(project)
|
||||
.select()
|
||||
.single();
|
||||
|
||||
if (error) throw error;
|
||||
return data;
|
||||
},
|
||||
|
||||
async updateProject(projectId: string, updates: Partial<Project>): Promise<Project> {
|
||||
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<void> {
|
||||
const { error } = await supabase
|
||||
.from('projects')
|
||||
.delete()
|
||||
.eq('id', projectId);
|
||||
|
||||
if (error) throw error;
|
||||
},
|
||||
|
||||
async getAllProjects(limit = 10): Promise<Project[]> {
|
||||
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<Achievement[]> {
|
||||
const { data, error } = await supabase
|
||||
.from('achievements')
|
||||
.select('*')
|
||||
.order('xp_reward', { ascending: false });
|
||||
|
||||
if (error) throw error;
|
||||
return data;
|
||||
},
|
||||
|
||||
async getUserAchievements(userId: string): Promise<Achievement[]> {
|
||||
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<void> {
|
||||
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<void> {
|
||||
// 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<CommunityPost[]> {
|
||||
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<CommunityPost, 'id' | 'created_at' | 'updated_at' | 'likes_count' | 'comments_count'>): Promise<CommunityPost> {
|
||||
const { data, error } = await supabase
|
||||
.from('community_posts')
|
||||
.insert(post)
|
||||
.select()
|
||||
.single();
|
||||
|
||||
if (error) throw error;
|
||||
return data;
|
||||
},
|
||||
|
||||
async getUserPosts(userId: string): Promise<CommunityPost[]> {
|
||||
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<any[]> {
|
||||
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<void> {
|
||||
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<void> {
|
||||
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();
|
||||
},
|
||||
};
|
||||
Loading…
Reference in a new issue