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
+39
View File
@@ -0,0 +1,39 @@
import { useState } from 'react';
import { ChevronRight, ChevronDown, Folder } from 'lucide-react';
function TreeNode({ node, level, onSelect, selectedId }) {
const [open, setOpen] = useState(true);
const hasChildren = node.children && node.children.length > 0;
return (
<div>
<div
className="tree-item-row"
style={{ paddingLeft: level * 18 + 6, background: selectedId === node.id ? 'rgb(var(--accent) / .14)' : undefined }}
onClick={() => onSelect?.(node)}
>
{hasChildren ? (
<button className="tree-toggle" onClick={(e) => { e.stopPropagation(); setOpen((v) => !v); }}>
{open ? <ChevronDown size={14} /> : <ChevronRight size={14} />}
</button>
) : (
<span className="tree-toggle" />
)}
<Folder size={15} style={{ opacity: .7 }} />
<span>{node.name}</span>
</div>
{hasChildren && open && node.children.map((child) => (
<TreeNode key={child.id} node={child} level={level + 1} onSelect={onSelect} selectedId={selectedId} />
))}
</div>
);
}
export function Treeview({ nodes, onSelect, selectedId }) {
return (
<div>
{nodes.map((node) => (
<TreeNode key={node.id} node={node} level={0} onSelect={onSelect} selectedId={selectedId} />
))}
</div>
);
}