feat: initial SPA frontend for network app
This commit is contained in:
198
src/lib/api.ts
Normal file
198
src/lib/api.ts
Normal file
@@ -0,0 +1,198 @@
|
||||
import type { Profile, Client, ClientCreate, Event, EventCreate, Email, EmailGenerate, User } from '@/types';
|
||||
|
||||
const API_BASE = import.meta.env.PROD
|
||||
? 'https://api.donovankelly.xyz/api'
|
||||
: '/api';
|
||||
|
||||
const AUTH_BASE = import.meta.env.PROD
|
||||
? 'https://api.donovankelly.xyz'
|
||||
: '';
|
||||
|
||||
class ApiClient {
|
||||
private async fetch<T>(path: string, options: RequestInit = {}): Promise<T> {
|
||||
const headers: HeadersInit = {
|
||||
'Content-Type': 'application/json',
|
||||
...options.headers,
|
||||
};
|
||||
|
||||
const response = await fetch(`${API_BASE}${path}`, {
|
||||
...options,
|
||||
headers,
|
||||
credentials: 'include',
|
||||
});
|
||||
|
||||
if (!response.ok) {
|
||||
const error = await response.json().catch(() => ({ error: 'Unknown error' }));
|
||||
throw new Error(error.error || error.message || 'Request failed');
|
||||
}
|
||||
|
||||
const text = await response.text();
|
||||
if (!text) return {} as T;
|
||||
return JSON.parse(text);
|
||||
}
|
||||
|
||||
// Auth
|
||||
async login(email: string, password: string) {
|
||||
const response = await fetch(`${AUTH_BASE}/api/auth/sign-in/email`, {
|
||||
method: 'POST',
|
||||
headers: { 'Content-Type': 'application/json' },
|
||||
body: JSON.stringify({ email, password }),
|
||||
credentials: 'include',
|
||||
});
|
||||
if (!response.ok) {
|
||||
const error = await response.json().catch(() => ({ message: 'Login failed' }));
|
||||
throw new Error(error.message || 'Login failed');
|
||||
}
|
||||
return response.json();
|
||||
}
|
||||
|
||||
async logout() {
|
||||
await fetch(`${AUTH_BASE}/api/auth/sign-out`, {
|
||||
method: 'POST',
|
||||
credentials: 'include',
|
||||
});
|
||||
}
|
||||
|
||||
async getSession(): Promise<{ user: User } | null> {
|
||||
try {
|
||||
const response = await fetch(`${AUTH_BASE}/api/auth/get-session`, {
|
||||
credentials: 'include',
|
||||
});
|
||||
if (!response.ok) return null;
|
||||
return response.json();
|
||||
} catch {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
// Profile
|
||||
async getProfile(): Promise<Profile> {
|
||||
return this.fetch('/profile');
|
||||
}
|
||||
|
||||
async updateProfile(data: Partial<Profile>): Promise<Profile> {
|
||||
return this.fetch('/profile', {
|
||||
method: 'PUT',
|
||||
body: JSON.stringify(data),
|
||||
});
|
||||
}
|
||||
|
||||
// Clients
|
||||
async getClients(params?: { search?: string; tag?: string }): Promise<Client[]> {
|
||||
const searchParams = new URLSearchParams();
|
||||
if (params?.search) searchParams.set('search', params.search);
|
||||
if (params?.tag) searchParams.set('tag', params.tag);
|
||||
const query = searchParams.toString();
|
||||
return this.fetch(`/clients${query ? `?${query}` : ''}`);
|
||||
}
|
||||
|
||||
async getClient(id: string): Promise<Client> {
|
||||
return this.fetch(`/clients/${id}`);
|
||||
}
|
||||
|
||||
async createClient(data: ClientCreate): Promise<Client> {
|
||||
return this.fetch('/clients', {
|
||||
method: 'POST',
|
||||
body: JSON.stringify(data),
|
||||
});
|
||||
}
|
||||
|
||||
async updateClient(id: string, data: Partial<ClientCreate>): Promise<Client> {
|
||||
return this.fetch(`/clients/${id}`, {
|
||||
method: 'PUT',
|
||||
body: JSON.stringify(data),
|
||||
});
|
||||
}
|
||||
|
||||
async deleteClient(id: string): Promise<void> {
|
||||
await this.fetch(`/clients/${id}`, { method: 'DELETE' });
|
||||
}
|
||||
|
||||
async markContacted(id: string): Promise<Client> {
|
||||
return this.fetch(`/clients/${id}/contacted`, { method: 'POST' });
|
||||
}
|
||||
|
||||
// Events
|
||||
async getEvents(params?: { clientId?: string; type?: string; upcoming?: number }): Promise<Event[]> {
|
||||
const searchParams = new URLSearchParams();
|
||||
if (params?.clientId) searchParams.set('clientId', params.clientId);
|
||||
if (params?.type) searchParams.set('type', params.type);
|
||||
if (params?.upcoming) searchParams.set('upcoming', String(params.upcoming));
|
||||
const query = searchParams.toString();
|
||||
return this.fetch(`/events${query ? `?${query}` : ''}`);
|
||||
}
|
||||
|
||||
async getEvent(id: string): Promise<Event> {
|
||||
return this.fetch(`/events/${id}`);
|
||||
}
|
||||
|
||||
async createEvent(data: EventCreate): Promise<Event> {
|
||||
return this.fetch('/events', {
|
||||
method: 'POST',
|
||||
body: JSON.stringify(data),
|
||||
});
|
||||
}
|
||||
|
||||
async updateEvent(id: string, data: Partial<EventCreate>): Promise<Event> {
|
||||
return this.fetch(`/events/${id}`, {
|
||||
method: 'PUT',
|
||||
body: JSON.stringify(data),
|
||||
});
|
||||
}
|
||||
|
||||
async deleteEvent(id: string): Promise<void> {
|
||||
await this.fetch(`/events/${id}`, { method: 'DELETE' });
|
||||
}
|
||||
|
||||
async syncAllEvents(): Promise<void> {
|
||||
await this.fetch('/events/sync-all', { method: 'POST' });
|
||||
}
|
||||
|
||||
async syncClientEvents(clientId: string): Promise<void> {
|
||||
await this.fetch(`/events/sync/${clientId}`, { method: 'POST' });
|
||||
}
|
||||
|
||||
// Emails
|
||||
async getEmails(params?: { status?: string; clientId?: string }): Promise<Email[]> {
|
||||
const searchParams = new URLSearchParams();
|
||||
if (params?.status) searchParams.set('status', params.status);
|
||||
if (params?.clientId) searchParams.set('clientId', params.clientId);
|
||||
const query = searchParams.toString();
|
||||
return this.fetch(`/emails${query ? `?${query}` : ''}`);
|
||||
}
|
||||
|
||||
async getEmail(id: string): Promise<Email> {
|
||||
return this.fetch(`/emails/${id}`);
|
||||
}
|
||||
|
||||
async generateEmail(data: EmailGenerate): Promise<Email> {
|
||||
return this.fetch('/emails/generate', {
|
||||
method: 'POST',
|
||||
body: JSON.stringify(data),
|
||||
});
|
||||
}
|
||||
|
||||
async generateBirthdayEmail(clientId: string, provider?: string): Promise<Email> {
|
||||
return this.fetch('/emails/generate-birthday', {
|
||||
method: 'POST',
|
||||
body: JSON.stringify({ clientId, provider }),
|
||||
});
|
||||
}
|
||||
|
||||
async updateEmail(id: string, data: { subject?: string; content?: string }): Promise<Email> {
|
||||
return this.fetch(`/emails/${id}`, {
|
||||
method: 'PUT',
|
||||
body: JSON.stringify(data),
|
||||
});
|
||||
}
|
||||
|
||||
async sendEmail(id: string): Promise<Email> {
|
||||
return this.fetch(`/emails/${id}/send`, { method: 'POST' });
|
||||
}
|
||||
|
||||
async deleteEmail(id: string): Promise<void> {
|
||||
await this.fetch(`/emails/${id}`, { method: 'DELETE' });
|
||||
}
|
||||
}
|
||||
|
||||
export const api = new ApiClient();
|
||||
Reference in New Issue
Block a user