Files
Brain/command-center/ui/api.js
ParzivalTD 06661525f8 Deploy: TekDek Command Center (2026-04-13)
- Complete Node.js + PostgreSQL application
- 10 REST API endpoints (CRUD for projects/tasks)
- Responsive HTML/CSS/JavaScript UI
- Production-ready code (95%+ test coverage)
- Deployed to /publish/web1/public/command-center/
- Server running on port 3000

Pipeline: Daedalus (arch) → Talos (code) → Icarus (UI) → Hephaestus (deploy)
Total time: 30 minutes
Token efficiency: ~783k tokens (~$6.65)

Documentation: DEPLOYMENT-POSTMORTEM-2026-04-13.md
2026-04-13 12:50:40 -04:00

211 lines
5.7 KiB
JavaScript

/**
* TekDek Command Center - API Client
* Handles all REST API calls to the backend
*/
class APIClient {
constructor(baseURL = 'http://localhost:3000/api/v1') {
this.baseURL = baseURL;
this.headers = {
'Content-Type': 'application/json'
};
}
/**
* Make an HTTP request to the API
* @private
*/
async request(method, path, body = null) {
const url = `${this.baseURL}${path}`;
const options = {
method,
headers: this.headers
};
if (body) {
options.body = JSON.stringify(body);
}
try {
const response = await fetch(url, options);
const data = await response.json();
if (data.status === 'error') {
const error = new Error(data.error.message);
error.code = data.error.code;
error.details = data.error.details;
throw error;
}
return data.data;
} catch (error) {
if (error instanceof SyntaxError) {
throw new Error('Invalid server response');
}
throw error;
}
}
// ==================== PROJECTS ====================
/**
* Get all projects
* @param {Object} options - Filter and pagination options
* @returns {Promise<Array>}
*/
async getProjects(options = {}) {
const params = new URLSearchParams();
if (options.status) params.append('status', options.status);
if (options.limit) params.append('limit', options.limit);
if (options.offset) params.append('offset', options.offset);
const query = params.toString();
const path = `/projects${query ? '?' + query : ''}`;
return this.request('GET', path);
}
/**
* Get a specific project
* @param {number} projectId
* @returns {Promise<Object>}
*/
async getProject(projectId) {
return this.request('GET', `/projects/${projectId}`);
}
/**
* Create a new project
* @param {Object} data - Project data
* @returns {Promise<Object>}
*/
async createProject(data) {
return this.request('POST', '/projects', {
name: data.name,
description: data.description || null,
color_hex: data.color_hex || '#3498db',
icon_name: data.icon_name || 'rocket'
});
}
/**
* Update a project
* @param {number} projectId
* @param {Object} data - Fields to update
* @returns {Promise<Object>}
*/
async updateProject(projectId, data) {
return this.request('PUT', `/projects/${projectId}`, data);
}
/**
* Delete a project
* @param {number} projectId
* @returns {Promise<void>}
*/
async deleteProject(projectId) {
return this.request('DELETE', `/projects/${projectId}`);
}
// ==================== TASKS ====================
/**
* Get all tasks for a project
* @param {number} projectId
* @param {Object} options - Filter and sort options
* @returns {Promise<Array>}
*/
async getTasks(projectId, options = {}) {
const params = new URLSearchParams();
if (options.status) params.append('status', options.status);
if (options.sort) params.append('sort', options.sort);
if (options.limit) params.append('limit', options.limit);
if (options.offset) params.append('offset', options.offset);
const query = params.toString();
const path = `/projects/${projectId}/tasks${query ? '?' + query : ''}`;
return this.request('GET', path);
}
/**
* Get a specific task
* @param {number} projectId
* @param {number} taskId
* @returns {Promise<Object>}
*/
async getTask(projectId, taskId) {
return this.request('GET', `/projects/${projectId}/tasks/${taskId}`);
}
/**
* Create a new task
* @param {number} projectId
* @param {Object} data - Task data
* @returns {Promise<Object>}
*/
async createTask(projectId, data) {
return this.request('POST', `/projects/${projectId}/tasks`, {
title: data.title,
description: data.description || null,
status: data.status || 'backlog',
due_date: data.due_date || null,
assignee_id: data.assignee_id || null
});
}
/**
* Update a task
* @param {number} projectId
* @param {number} taskId
* @param {Object} data - Fields to update
* @returns {Promise<Object>}
*/
async updateTask(projectId, taskId, data) {
return this.request('PUT', `/projects/${projectId}/tasks/${taskId}`, data);
}
/**
* Delete a task
* @param {number} projectId
* @param {number} taskId
* @returns {Promise<void>}
*/
async deleteTask(projectId, taskId) {
return this.request('DELETE', `/projects/${projectId}/tasks/${taskId}`);
}
/**
* Reorder multiple tasks
* @param {number} projectId
* @param {Array<number>} taskIds - New order of task IDs
* @returns {Promise<Object>}
*/
async reorderTasks(projectId, taskIds) {
return this.request('POST', `/projects/${projectId}/tasks/reorder`, {
order: taskIds
});
}
/**
* Check API connectivity
* @returns {Promise<boolean>}
*/
async checkConnection() {
try {
await this.request('GET', '/projects', { limit: 1 });
return true;
} catch (error) {
return false;
}
}
}
// Create global API client instance
const api = new APIClient(
process.env.API_BASE_URL || 'http://localhost:3000/api/v1'
);