Governed substrate for autonomous agents: scoped identity (passports), audited actions, MCP workspace. Infra IPs and secrets redacted for public release.
214 lines
7 KiB
JavaScript
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
|
|
};
|