godcrm/backend/services/CalendarTableService.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

214 lines
7 KiB
JavaScript

/**
* CalendarTableService - Creates a calendar table with holidays/weekends for 3 years
* Includes background color and font color for visual customization
*/
import { dbGet, dbRun, dbAll } from '../database/connection.js';
import { apiLogger } from '../utils/logger.js';
// Color constants
const COLORS = {
// Background colors
BG_WEEKEND: '#FECACA', // Light pink/red for weekends
BG_WORKDAY: null, // No fill for workdays (transparent)
// Font colors
FONT_WEEKEND: '#DC2626', // Red text for weekends
FONT_WORKDAY: null, // Default text color for workdays
};
/**
* Generate unique base_id for rows
*/
function generateBaseId(prefix = 'cal') {
return `${prefix}-${Date.now()}-${Math.random().toString(36).substr(2, 9)}`;
}
/**
* Create a calendar table in the given project
*/
async function createTable(projectId, name, icon, description) {
const result = await dbRun(`
INSERT INTO universal_tables (project_id, name, icon, description, created_at, updated_at)
VALUES (?, ?, ?, ?, datetime('now'), datetime('now'))
`, [projectId, name, icon, description]);
apiLogger.debug({ name, tableId: result.lastInsertRowid }, 'Created table');
return result.lastInsertRowid;
}
/**
* Create columns for a table
*/
async function createColumns(tableId, columns) {
for (let i = 0; i < columns.length; i++) {
const col = columns[i];
const config = {
icon: col.icon || null,
...(col.settings || {})
};
await dbRun(`
INSERT INTO table_columns (table_id, column_name, display_name, type, config, width, is_required, order_index, created_at, updated_at)
VALUES (?, ?, ?, ?, ?, ?, ?, ?, datetime('now'), datetime('now'))
`, [
tableId,
col.key || col.name.toLowerCase().replace(/\s+/g, '_'),
col.name,
col.type,
JSON.stringify(config),
col.width || 150,
col.required ? 1 : 0,
i + 1
]);
}
apiLogger.debug({ count: columns.length }, 'Added columns');
}
/**
* Get or create System Data project for a space
*/
async function getOrCreateSystemDataProject(spaceId, ownerId) {
// Try to find existing System Data project
let project = await dbGet(`
SELECT id FROM projects
WHERE space_id = ? AND (type = 'system_data' OR name LIKE '%System Data%')
LIMIT 1
`, [spaceId]);
if (project) {
return project.id;
}
// Create new System Data project
const result = await dbRun(`
INSERT INTO projects (name, type, icon, description, owner_id, space_id, settings, created_at, updated_at)
VALUES (?, ?, ?, ?, ?, ?, '{}', datetime('now'), datetime('now'))
`, ['System Data', 'system_data', '⚙️', 'System tables for automation', ownerId, spaceId]);
apiLogger.info({ projectId: result.lastInsertRowid }, 'Created System Data project');
return result.lastInsertRowid;
}
/**
* Check if day is weekend (Saturday=6, Sunday=0)
*/
function isWeekend(date) {
const day = date.getDay();
return day === 0 || day === 6;
}
/**
* Format date as YYYY-MM-DD
*/
function formatDate(date) {
const year = date.getFullYear();
const month = String(date.getMonth() + 1).padStart(2, '0');
const day = String(date.getDate()).padStart(2, '0');
return `${year}-${month}-${day}`;
}
/**
* Create calendar table with 2 years of data
*/
export async function createCalendarTable(projectId, ownerId, tableName = 'Calendar') {
apiLogger.info({ projectId }, 'Creating Calendar Table');
// Create the calendar table
const tableId = await createTable(projectId, tableName, '📅', 'Calendar with holidays and weekends');
// Create columns - simple structure with colors and tags
await createColumns(tableId, [
{ key: 'date', name: 'Date', type: 'date', icon: '📅', required: true, width: 120 },
{ key: 'day_type', name: 'Day Type', type: 'select', icon: '🏷️', width: 120, settings: {
options: [
{ value: 'workday', label: 'Workday', color: '#10B981' },
{ value: 'weekend', label: 'Weekend', color: '#6B7280' },
{ value: 'holiday', label: 'Holiday', color: '#EF4444' }
]
}},
{ key: 'bg_color', name: 'Цвет столбца', type: 'color', icon: '🎨', width: 100 },
{ key: 'font_color', name: 'Цвет шрифта', type: 'color', icon: '✏️', width: 100 },
{ key: 'tags', name: 'Tags', type: 'multiselect', icon: '🏷️', width: 150, settings: {
options: [
{ value: 'holiday', label: 'Holiday', color: '#EF4444' },
{ value: 'vacation', label: 'Vacation', color: '#3B82F6' },
{ value: 'sick', label: 'Sick Day', color: '#F59E0B' },
{ value: 'important', label: 'Important', color: '#8B5CF6' }
]
}},
{ key: 'note', name: 'Note', type: 'text', icon: '📝', width: 200 }
]);
// Generate calendar from Jan 1 of previous year to Dec 31 of next year
const now = new Date();
const currentYear = now.getFullYear();
const startDate = new Date(currentYear - 1, 0, 1); // Jan 1 of previous year
startDate.setHours(0, 0, 0, 0);
const endDate = new Date(currentYear + 1, 11, 31); // Dec 31 of next year
endDate.setHours(23, 59, 59, 999);
const rows = [];
const currentDate = new Date(startDate);
apiLogger.debug({ startDate: formatDate(startDate), endDate: formatDate(endDate) }, 'Generating calendar');
while (currentDate < endDate) {
const weekend = isWeekend(currentDate);
const dayType = weekend ? 'weekend' : 'workday';
// Only set colors for weekends (red font, light pink bg)
// Workdays have no colors (null = use defaults)
rows.push({
date: formatDate(currentDate),
day_type: dayType,
bg_color: weekend ? COLORS.BG_WEEKEND : null,
font_color: weekend ? COLORS.FONT_WEEKEND : null,
note: ''
});
currentDate.setDate(currentDate.getDate() + 1);
}
// Insert rows in batches
apiLogger.debug({ count: rows.length }, 'Inserting calendar days');
const batchSize = 100;
for (let i = 0; i < rows.length; i += batchSize) {
const batch = rows.slice(i, i + batchSize);
for (const row of batch) {
await dbRun(`
INSERT INTO table_rows (table_id, base_id, data, created_by, created_at, updated_at)
VALUES (?, ?, ?, ?, datetime('now'), datetime('now'))
`, [tableId, generateBaseId(), JSON.stringify(row), ownerId]);
}
// Progress indicator
if ((i + batchSize) % 500 === 0 || i + batchSize >= rows.length) {
apiLogger.debug({ inserted: Math.min(i + batchSize, rows.length), total: rows.length }, 'Insert progress');
}
}
apiLogger.info({ tableId, daysCreated: rows.length }, 'Calendar table created successfully');
return tableId;
}
/**
* Create calendar table in System Data project
*/
export async function createCalendarTableInSystemData(spaceId, ownerId, tableName = 'Calendar') {
const projectId = await getOrCreateSystemDataProject(spaceId, ownerId);
return {
projectId,
tableId: await createCalendarTable(projectId, ownerId, tableName)
};
}
export default {
createCalendarTable,
createCalendarTableInSystemData,
getOrCreateSystemDataProject
};