Files
plumbing-dashy/api.js
T
2026-05-21 15:49:36 +10:00

232 lines
5.2 KiB
JavaScript

class ApiService {
constructor() {
this.baseUrl = '/api';
this.token = localStorage.getItem('dashy_token');
this.subscribers = new Set();
this.connectSSE();
}
connectSSE() {
const url = `${this.baseUrl}/stream`;
this.sse = new EventSource(url);
this.sse.onmessage = (event) => {
try {
const data = JSON.parse(event.data);
if (data.type === 'refresh') {
this.notify();
}
} catch (e) {
// Fallback for non-JSON messages or just generic refresh
this.notify();
}
};
this.sse.onerror = (err) => {
console.error('SSE connection error. EventSource will auto-reconnect.', err);
};
}
subscribe(fn) {
this.subscribers.add(fn);
return () => this.subscribers.delete(fn);
}
notify() {
for (const fn of this.subscribers) fn();
}
async request(endpoint, options = {}) {
const headers = {
'Content-Type': 'application/json',
...options.headers,
};
if (this.token) {
headers['Authorization'] = `Bearer ${this.token}`;
}
const response = await fetch(`${this.baseUrl}${endpoint}`, {
...options,
headers,
});
if (!response.ok) {
if (response.status === 401) {
this.logout();
}
let errorMsg = response.statusText;
try {
const data = await response.json();
if (data && data.detail) errorMsg = data.detail;
} catch (e) {}
throw new Error(`API Error: ${errorMsg}`);
}
return response.json();
}
async login(id, password) {
const response = await fetch(`${this.baseUrl}/token`, {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ id, password }),
});
if (!response.ok) {
throw new Error('Login failed');
}
const data = await response.json();
this.token = data.access_token;
localStorage.setItem('dashy_token', this.token);
this.notify();
return data;
}
logout() {
this.token = null;
localStorage.removeItem('dashy_token');
this.notify();
}
async getUsers() {
return this.request('/users');
}
async getWorkspace() {
return this.request('/workspace');
}
async updateWorkspace(updates) {
const data = await this.request('/workspace', {
method: 'PATCH',
body: JSON.stringify(updates),
});
this.notify();
return data;
}
async createUser(userData) {
const data = await this.request('/users', {
method: 'POST',
body: JSON.stringify(userData),
});
this.notify();
return data;
}
async updateUser(id, updates) {
const data = await this.request(`/users/${id}`, {
method: 'PATCH',
body: JSON.stringify(updates),
});
this.notify();
return data;
}
async deleteUser(id) {
const data = await this.request(`/users/${id}`, {
method: 'DELETE',
});
this.notify();
return data;
}
async changePassword(id, oldPassword, newPassword) {
const response = await fetch(`${this.baseUrl}/users/${id}/password`, {
method: 'POST',
headers: {
'Content-Type': 'application/json',
...(this.token ? { Authorization: `Bearer ${this.token}` } : {}),
},
body: JSON.stringify({ old_password: oldPassword, new_password: newPassword }),
});
if (!response.ok) {
let detail = response.statusText;
try { const j = await response.json(); if (j.detail) detail = j.detail; } catch {}
throw new Error(detail);
}
return response.json();
}
async getTasks() {
return this.request('/tasks');
}
async getDeletedTasks() {
return this.request('/tasks/deleted');
}
async getAudit() {
return this.request('/audit');
}
async createTask(taskData) {
const data = await this.request('/tasks', {
method: 'POST',
body: JSON.stringify(taskData),
});
this.notify();
return data;
}
async updateTask(id, updates) {
const data = await this.request(`/tasks/${id}`, {
method: 'PATCH',
body: JSON.stringify(updates),
});
this.notify();
return data;
}
async restoreTask(id) {
const data = await this.request(`/tasks/${id}/restore`, {
method: 'POST',
});
this.notify();
return data;
}
async deleteTask(id) {
const data = await this.request(`/tasks/${id}`, {
method: 'DELETE',
});
this.notify();
return data;
}
async getTaskNotes(taskId) {
return this.request(`/tasks/${taskId}/notes`);
}
async createTaskNote(taskId, body) {
const data = await this.request(`/tasks/${taskId}/notes`, {
method: 'POST',
body: JSON.stringify({ body }),
});
this.notify();
return data;
}
async getSchedule(startDate, endDate) {
return this.request(`/calendar/schedule?start_date=${startDate}&end_date=${endDate}`);
}
async triggerSync() {
const data = await this.request('/calendar/sync', { method: 'POST' });
this.notify();
return data;
}
async addAudit(auditData) {
const data = await this.request('/audit', {
method: 'POST',
body: JSON.stringify(auditData),
});
this.notify();
return data;
}
}
window.api = new ApiService();