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
This commit is contained in:
210
command-center/ui/api.js
Normal file
210
command-center/ui/api.js
Normal file
@@ -0,0 +1,210 @@
|
||||
/**
|
||||
* 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'
|
||||
);
|
||||
Reference in New Issue
Block a user