2025-12-11 10:50:09 +00:00
|
|
|
"use client";
|
|
|
|
|
|
2026-01-09 17:39:19 +00:00
|
|
|
import { useState, useEffect } from 'react';
|
2025-12-11 10:50:09 +00:00
|
|
|
import DashboardHeader from './dashboard-header';
|
|
|
|
|
import StatsOverview from './stats-overview';
|
|
|
|
|
import ProjectGrid from './project-grid';
|
2026-01-28 10:44:49 +00:00
|
|
|
import ProjectGridSkeleton from './project-grid-skeleton';
|
2026-01-31 13:50:07 +00:00
|
|
|
import { ProjectFormSheet } from '@/components/project-form/project-form-sheet';
|
|
|
|
|
import { getProjects } from '@/lib/projectsService';
|
2025-12-11 10:50:09 +00:00
|
|
|
import { Project } from '@/types/project';
|
2026-01-28 10:44:49 +00:00
|
|
|
import { Skeleton } from '@/components/ui/skeleton';
|
2025-12-11 10:50:09 +00:00
|
|
|
|
|
|
|
|
export default function ProjectDashboard() {
|
|
|
|
|
const [viewMode, setViewMode] = useState<'grid' | 'list'>('grid');
|
|
|
|
|
const [searchTerm, setSearchTerm] = useState('');
|
2026-01-09 17:39:19 +00:00
|
|
|
const [projects, setProjects] = useState<Project[]>([]);
|
|
|
|
|
const [loading, setLoading] = useState(true);
|
|
|
|
|
const [error, setError] = useState<string | null>(null);
|
2026-01-31 13:50:07 +00:00
|
|
|
const [isCreateSheetOpen, setIsCreateSheetOpen] = useState(false);
|
2025-12-11 10:50:09 +00:00
|
|
|
|
2026-01-09 17:39:19 +00:00
|
|
|
useEffect(() => {
|
|
|
|
|
async function loadProjects() {
|
|
|
|
|
try {
|
|
|
|
|
setLoading(true);
|
|
|
|
|
const data = await getProjects();
|
|
|
|
|
setProjects(data);
|
|
|
|
|
setError(null);
|
|
|
|
|
} catch (err) {
|
|
|
|
|
console.error('Error loading projects:', err);
|
|
|
|
|
setError('Error al cargar los proyectos');
|
|
|
|
|
} finally {
|
|
|
|
|
setLoading(false);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
loadProjects();
|
|
|
|
|
}, []);
|
|
|
|
|
|
2026-01-31 13:50:07 +00:00
|
|
|
// Handler para cuando se crea un proyecto
|
|
|
|
|
const handleProjectCreated = (newProject: Project) => {
|
|
|
|
|
setProjects(prevProjects => [newProject, ...prevProjects]);
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
// Handler para cuando se actualiza un proyecto
|
|
|
|
|
const handleProjectUpdated = (updatedProject: Project) => {
|
|
|
|
|
setProjects(prevProjects =>
|
|
|
|
|
prevProjects.map(p => p.id === updatedProject.id ? updatedProject : p)
|
|
|
|
|
);
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
// Handler para cuando se elimina un proyecto
|
|
|
|
|
const handleProjectDeleted = (projectId: number) => {
|
|
|
|
|
setProjects(prevProjects => prevProjects.filter(p => p.id !== projectId));
|
|
|
|
|
};
|
|
|
|
|
|
2026-01-09 17:39:19 +00:00
|
|
|
const filteredProjects = projects.filter(project =>
|
2025-12-11 10:50:09 +00:00
|
|
|
project.name.toLowerCase().includes(searchTerm.toLowerCase()) ||
|
2026-01-09 17:39:19 +00:00
|
|
|
project.client.toLowerCase().includes(searchTerm.toLowerCase()) ||
|
|
|
|
|
project.ref.toLowerCase().includes(searchTerm.toLowerCase())
|
2025-12-11 10:50:09 +00:00
|
|
|
);
|
|
|
|
|
|
2026-01-09 17:39:19 +00:00
|
|
|
if (loading) {
|
|
|
|
|
return (
|
2026-01-31 12:56:51 +00:00
|
|
|
<div className="min-h-screen bg-background">
|
2026-01-28 10:44:49 +00:00
|
|
|
<DashboardHeader
|
|
|
|
|
searchTerm=""
|
|
|
|
|
onSearchChange={() => {}}
|
|
|
|
|
viewMode={viewMode}
|
|
|
|
|
onViewModeChange={setViewMode}
|
2026-01-31 13:50:07 +00:00
|
|
|
onCreateProject={() => {}}
|
2026-01-28 10:44:49 +00:00
|
|
|
/>
|
|
|
|
|
|
2026-01-31 12:56:51 +00:00
|
|
|
<main className="px-4 sm:px-6 lg:px-8 py-8">
|
2026-01-28 10:44:49 +00:00
|
|
|
{/* Stats skeleton */}
|
|
|
|
|
<div className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-4 gap-6 mb-8">
|
|
|
|
|
{Array.from({ length: 4 }).map((_, i) => (
|
2026-01-31 12:56:51 +00:00
|
|
|
<div key={i} className="bg-card border rounded-lg p-6">
|
2026-01-28 10:44:49 +00:00
|
|
|
<div className="flex items-center justify-between mb-4">
|
|
|
|
|
<Skeleton className="h-4 w-24" />
|
|
|
|
|
<Skeleton className="h-4 w-4 rounded" />
|
|
|
|
|
</div>
|
|
|
|
|
<Skeleton className="h-8 w-16 mb-2" />
|
|
|
|
|
<Skeleton className="h-3 w-32" />
|
|
|
|
|
</div>
|
|
|
|
|
))}
|
|
|
|
|
</div>
|
|
|
|
|
|
|
|
|
|
{/* Projects skeleton */}
|
|
|
|
|
<ProjectGridSkeleton viewMode={viewMode} count={6} />
|
|
|
|
|
</main>
|
2026-01-09 17:39:19 +00:00
|
|
|
</div>
|
|
|
|
|
);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (error) {
|
|
|
|
|
return (
|
2026-01-31 12:56:51 +00:00
|
|
|
<div className="min-h-screen bg-background flex items-center justify-center">
|
2026-01-09 17:39:19 +00:00
|
|
|
<div className="text-center">
|
2026-01-31 12:56:51 +00:00
|
|
|
<p className="text-red-600 dark:text-red-400">{error}</p>
|
2026-01-09 17:39:19 +00:00
|
|
|
<button
|
|
|
|
|
onClick={() => window.location.reload()}
|
|
|
|
|
className="mt-4 px-4 py-2 bg-blue-600 text-white rounded-lg hover:bg-blue-700"
|
|
|
|
|
>
|
|
|
|
|
Reintentar
|
|
|
|
|
</button>
|
|
|
|
|
</div>
|
|
|
|
|
</div>
|
|
|
|
|
);
|
|
|
|
|
}
|
|
|
|
|
|
2025-12-11 10:50:09 +00:00
|
|
|
return (
|
2026-01-31 12:56:51 +00:00
|
|
|
<div className="min-h-screen bg-background">
|
2025-12-11 10:50:09 +00:00
|
|
|
<DashboardHeader
|
|
|
|
|
searchTerm={searchTerm}
|
|
|
|
|
onSearchChange={setSearchTerm}
|
|
|
|
|
viewMode={viewMode}
|
|
|
|
|
onViewModeChange={setViewMode}
|
2026-01-31 13:50:07 +00:00
|
|
|
onCreateProject={() => setIsCreateSheetOpen(true)}
|
2025-12-11 10:50:09 +00:00
|
|
|
/>
|
|
|
|
|
|
2026-01-31 12:56:51 +00:00
|
|
|
<main className="px-4 sm:px-6 lg:px-8 py-8">
|
2026-01-09 17:39:19 +00:00
|
|
|
<StatsOverview projects={projects} />
|
2026-01-31 13:50:07 +00:00
|
|
|
<ProjectGrid
|
|
|
|
|
projects={filteredProjects}
|
|
|
|
|
viewMode={viewMode}
|
|
|
|
|
onProjectUpdated={handleProjectUpdated}
|
|
|
|
|
onProjectDeleted={handleProjectDeleted}
|
|
|
|
|
/>
|
2025-12-11 10:50:09 +00:00
|
|
|
</main>
|
2026-01-31 13:50:07 +00:00
|
|
|
|
|
|
|
|
{/* Sheet de creación de proyecto */}
|
|
|
|
|
<ProjectFormSheet
|
|
|
|
|
open={isCreateSheetOpen}
|
|
|
|
|
onOpenChange={setIsCreateSheetOpen}
|
|
|
|
|
mode="create"
|
|
|
|
|
onSuccess={handleProjectCreated}
|
|
|
|
|
/>
|
2025-12-11 10:50:09 +00:00
|
|
|
</div>
|
|
|
|
|
);
|
2026-01-09 17:39:19 +00:00
|
|
|
}
|