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,59 @@
import { useState } from 'react';
import { useNavigate, useParams } from 'react-router-dom';
import { ArrowLeft } from 'lucide-react';
import { useUi } from '../../context/ui-context.jsx';
import { useAsync } from '../../hooks/useAsync.js';
import { getProject } from '../../services/projects.js';
import { Button } from '../../components/Button.jsx';
import { Tabs } from '../../components/Tabs.jsx';
import { StatusBadge } from '../../components/StatusBadge.jsx';
import { CommentsPanel } from '../../components/CommentsPanel.jsx';
import { AttachmentsPanel } from '../../components/AttachmentsPanel.jsx';
import ProjectInfoTab from './ProjectInfoTab.jsx';
import ProjectActivitiesTab from './ProjectActivitiesTab.jsx';
import ProjectGanttTab from './ProjectGanttTab.jsx';
import ProjectCalendarTab from './ProjectCalendarTab.jsx';
export default function ProyectoDetallePage() {
const { id } = useParams();
const projectId = Number(id);
const { t } = useUi();
const navigate = useNavigate();
const [tab, setTab] = useState('info');
const { data: project, loading, reload } = useAsync(() => getProject(projectId), [projectId]);
if (loading || !project) return <div className="text-muted">{t('common_loading')}</div>;
return (
<div>
<div style={{ display: 'flex', alignItems: 'center', gap: 10, marginBottom: 4 }}>
<Button variant="subtle" icon={ArrowLeft} onClick={() => navigate('/proyectos')}>{t('projects_title')}</Button>
</div>
<div style={{ display: 'flex', alignItems: 'center', gap: 12, marginBottom: 16 }}>
<h2 className="page-title mt-0">{project.code} — {project.name}</h2>
<StatusBadge name={project.status_name} color={project.status_color} />
</div>
<Tabs
tabs={[
{ key: 'info', label: t('tab_info') },
{ key: 'activities', label: t('tab_activities') },
{ key: 'gantt', label: t('tab_gantt') },
{ key: 'calendar', label: t('tab_calendar') },
{ key: 'comments', label: t('tab_comments') },
{ key: 'documents', label: t('tab_documents') },
]}
active={tab}
onChange={setTab}
/>
{tab === 'info' && <ProjectInfoTab project={project} onChanged={reload} />}
{tab === 'activities' && <ProjectActivitiesTab projectId={projectId} />}
{tab === 'gantt' && <ProjectGanttTab projectId={projectId} />}
{tab === 'calendar' && <ProjectCalendarTab projectId={projectId} />}
{tab === 'comments' && <CommentsPanel entityType="project" entityId={projectId} />}
{tab === 'documents' && <AttachmentsPanel entityType="project" entityId={projectId} />}
</div>
);
}