139 lines
3.5 KiB
TypeScript
139 lines
3.5 KiB
TypeScript
import { createClient } from '@supabase/supabase-js';
|
|
|
|
const supabaseUrl = process.env.NEXT_PUBLIC_SUPABASE_URL!;
|
|
const supabaseAnonKey = process.env.NEXT_PUBLIC_SUPABASE_ANON_KEY!;
|
|
|
|
export const supabase = createClient(supabaseUrl, supabaseAnonKey);
|
|
|
|
// Database types
|
|
export interface DatabaseProject {
|
|
id: number;
|
|
title: string;
|
|
description: string;
|
|
content: string;
|
|
tags: string[];
|
|
featured: boolean;
|
|
category: string;
|
|
date: string;
|
|
github?: string;
|
|
live?: string;
|
|
published: boolean;
|
|
imageUrl?: string;
|
|
metaDescription?: string;
|
|
keywords?: string;
|
|
ogImage?: string;
|
|
schema?: Record<string, unknown>;
|
|
difficulty: 'Beginner' | 'Intermediate' | 'Advanced' | 'Expert';
|
|
timeToComplete?: string;
|
|
technologies: string[];
|
|
challenges: string[];
|
|
lessonsLearned: string[];
|
|
futureImprovements: string[];
|
|
demoVideo?: string;
|
|
screenshots: string[];
|
|
colorScheme: string;
|
|
accessibility: boolean;
|
|
performance: {
|
|
lighthouse: number;
|
|
bundleSize: string;
|
|
loadTime: string;
|
|
};
|
|
analytics: {
|
|
views: number;
|
|
likes: number;
|
|
shares: number;
|
|
};
|
|
created_at: string;
|
|
updated_at: string;
|
|
}
|
|
|
|
// Database operations
|
|
export const projectService = {
|
|
async getAllProjects(): Promise<DatabaseProject[]> {
|
|
const { data, error } = await supabase
|
|
.from('projects')
|
|
.select('*')
|
|
.order('created_at', { ascending: false });
|
|
|
|
if (error) throw error;
|
|
return data || [];
|
|
},
|
|
|
|
async getProjectById(id: number): Promise<DatabaseProject | null> {
|
|
const { data, error } = await supabase
|
|
.from('projects')
|
|
.select('*')
|
|
.eq('id', id)
|
|
.single();
|
|
|
|
if (error) throw error;
|
|
return data;
|
|
},
|
|
|
|
async createProject(project: Omit<DatabaseProject, 'id' | 'created_at' | 'updated_at'>): Promise<DatabaseProject> {
|
|
const { data, error } = await supabase
|
|
.from('projects')
|
|
.insert([project])
|
|
.select()
|
|
.single();
|
|
|
|
if (error) throw error;
|
|
return data;
|
|
},
|
|
|
|
async updateProject(id: number, updates: Partial<DatabaseProject>): Promise<DatabaseProject> {
|
|
const { data, error } = await supabase
|
|
.from('projects')
|
|
.update({ ...updates, updated_at: new Date().toISOString() })
|
|
.eq('id', id)
|
|
.select()
|
|
.single();
|
|
|
|
if (error) throw error;
|
|
return data;
|
|
},
|
|
|
|
async deleteProject(id: number): Promise<void> {
|
|
const { error } = await supabase
|
|
.from('projects')
|
|
.delete()
|
|
.eq('id', id);
|
|
|
|
if (error) throw error;
|
|
},
|
|
|
|
async searchProjects(query: string): Promise<DatabaseProject[]> {
|
|
const { data, error } = await supabase
|
|
.from('projects')
|
|
.select('*')
|
|
.or(`title.ilike.%${query}%,description.ilike.%${query}%,content.ilike.%${query}%,tags.cs.{${query}}`)
|
|
.order('created_at', { ascending: false });
|
|
|
|
if (error) throw error;
|
|
return data || [];
|
|
},
|
|
|
|
async getProjectsByCategory(category: string): Promise<DatabaseProject[]> {
|
|
const { data, error } = await supabase
|
|
.from('projects')
|
|
.select('*')
|
|
.eq('category', category)
|
|
.order('created_at', { ascending: false });
|
|
|
|
if (error) throw error;
|
|
return data || [];
|
|
},
|
|
|
|
async getFeaturedProjects(): Promise<DatabaseProject[]> {
|
|
const { data, error } = await supabase
|
|
.from('projects')
|
|
.select('*')
|
|
.eq('featured', true)
|
|
.eq('published', true)
|
|
.order('created_at', { ascending: false });
|
|
|
|
if (error) throw error;
|
|
return data || [];
|
|
}
|
|
};
|