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