godcrm/backend/services/DashboardService.js
GOD CRM Release f89e074dd1
Some checks failed
CI / Lint / Typecheck / Test / Build (push) Has been cancelled
CI / PostgreSQL Integration Tests (push) Has been cancelled
GOD CRM — public scrubbed snapshot
Governed substrate for autonomous agents: scoped identity (passports),
audited actions, MCP workspace. Infra IPs and secrets redacted for public release.
2026-08-10 04:01:45 +03:00

109 lines
3.2 KiB
JavaScript

// Dashboard Service - v0.003.000
// Manages 3 types of dashboards: USER, SPACE, PROJECT
import { dbRun, dbGet, dbAll, toBool } from '../database/connection.js';
/**
* Get space dashboard (default dashboard for space).
* Lazily creates a default dashboard if the space does not have one yet —
* legacy spaces (and those created via ImportService.importSpace) did not
* always get one, which caused the frontend to render the
* "Dashboard not available" stub instead of the empty add-widget frame.
* @param {number} spaceId - Space ID
* @returns {Promise<object|null>} Dashboard or null if space itself is missing
*/
export async function getSpaceDashboard(spaceId) {
const existing = await dbGet(`
SELECT * FROM dashboards
WHERE space_id = ? AND is_default = ?
ORDER BY id ASC
`, [spaceId, toBool(true)]);
if (existing) return existing;
const space = await dbGet('SELECT name FROM spaces WHERE id = ?', [spaceId]);
if (!space) return null;
await dbRun(`
INSERT INTO dashboards (space_id, name, icon, is_default, order_index)
VALUES (?, ?, ?, ?, 0)
`, [spaceId, `${space.name} Overview`, '📊', toBool(true)]);
return await dbGet(`
SELECT * FROM dashboards
WHERE space_id = ? AND is_default = ?
ORDER BY id ASC
`, [spaceId, toBool(true)]);
}
/**
* Get user dashboard (main dashboard for user)
* @param {number} userId - User ID
* @returns {Promise<object|null>} Dashboard or null
*/
export async function getUserDashboard(userId) {
return await dbGet(`
SELECT * FROM dashboards
WHERE user_id = ? AND is_default = ?
`, [userId, toBool(true)]);
}
/**
* Get project dashboards
* @param {number} projectId - Project ID
* @returns {Promise<array>} Array of dashboards
*/
export async function getProjectDashboards(projectId) {
return await dbAll(`
SELECT * FROM dashboards
WHERE project_id = ?
ORDER BY is_default DESC, order_index ASC
`, [projectId]);
}
/**
* Create dashboard
* @param {object} dashboardData - { user_id?, space_id?, project_id?, name, description?, icon?, is_default? }
* @returns {Promise<object>} Created dashboard
*/
export async function createDashboard(dashboardData) {
const {
user_id = null,
space_id = null,
project_id = null,
name,
description = null,
icon = '📊',
is_default = false
} = dashboardData;
// Validation: exactly one parent
const parents = [user_id, space_id, project_id].filter(id => id != null);
if (parents.length !== 1) {
throw new Error('Dashboard must have exactly one parent (user_id, space_id, or project_id)');
}
const result = await dbRun(`
INSERT INTO dashboards (user_id, space_id, project_id, name, description, icon, is_default, order_index)
VALUES (?, ?, ?, ?, ?, ?, ?, 0)
`, [
user_id,
space_id,
project_id,
name,
description,
icon,
toBool(is_default)
]);
const dashboardId = result.lastInsertRowid || result.lastID;
return await getDashboardById(dashboardId);
}
/**
* Get dashboard by ID
* @param {number} dashboardId - Dashboard ID
* @returns {Promise<object|null>} Dashboard or null
*/
export async function getDashboardById(dashboardId) {
return await dbGet('SELECT * FROM dashboards WHERE id = ?', [dashboardId]);
}