chore: estructura inicial del MVP

Backend (Express + MariaDB) y frontend (React + Vite, identidad Fluent/Windows) con el
nucleo funcional: seguridad, organizacion, proyectos, actividades, subtareas, dependencias,
dashboard, calendario, gantt, comentarios, adjuntos, notificaciones in-app y catalogos.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
This commit is contained in:
Milton Gonzalez
2026-09-03 08:37:58 -05:00
commit 1d22adc42e
135 changed files with 10913 additions and 0 deletions
@@ -0,0 +1,35 @@
import { ok, fail, asyncHandler } from '../../utils/http.js';
import * as repo from './repository.js';
import { listByProject } from '../activities/service.js';
import { dueStatus } from '../../utils/dates.js';
export const getSummary = asyncHandler(async (req, res) => {
const [kpis, myActivities, byStatus] = await Promise.all([
repo.summary(),
repo.myPendingActivities(req.currentUser.id),
repo.projectsByStatus(),
]);
ok(res, {
kpis,
myActivities: myActivities.map((a) => ({ ...a, dueStatus: dueStatus(a.due_date) })),
projectsByStatus: byStatus,
});
});
export const getCalendar = asyncHandler(async (req, res) => {
const month = req.query.month; // formato YYYY-MM
if (!month || !/^\d{4}-\d{2}$/.test(month)) return fail(res, 400, 'Parametro month invalido (YYYY-MM)');
const start = `${month}-01`;
const [year, m] = month.split('-').map(Number);
const end = new Date(year, m, 0).toISOString().slice(0, 10);
const items = await repo.calendarActivities(start, end);
ok(res, items.map((a) => ({ ...a, dueStatus: dueStatus(a.due_date, a.status_is_closed) })));
});
export const getGantt = asyncHandler(async (req, res) => {
const activities = await listByProject(Number(req.params.id));
ok(res, activities.map((a) => ({
id: a.id, code: a.code, name: a.name, startDate: a.start_date, dueDate: a.due_date,
progressPct: a.progress_pct, isMilestone: !!a.is_milestone, responsibleName: a.responsible_name,
})));
});