128 lines
3.6 KiB
TypeScript
128 lines
3.6 KiB
TypeScript
|
|
"use client";
|
||
|
|
|
||
|
|
import { useState, useEffect, useMemo } from "react";
|
||
|
|
import { FolderKanban, RefreshCw } from "lucide-react";
|
||
|
|
|
||
|
|
import { Project } from "@/types/project";
|
||
|
|
import { getProjects } from "@/lib/projectsService";
|
||
|
|
import { DataTable } from "./data-table";
|
||
|
|
import { DataTableToolbar } from "./data-table-toolbar";
|
||
|
|
import { DataTableSkeleton } from "./data-table-skeleton";
|
||
|
|
import { EmptyState } from "./empty-state";
|
||
|
|
import { projectColumns } from "./columns";
|
||
|
|
import { Button } from "@/components/ui/button";
|
||
|
|
|
||
|
|
export function ProjectsTable() {
|
||
|
|
const [projects, setProjects] = useState<Project[]>([]);
|
||
|
|
const [isLoading, setIsLoading] = useState(true);
|
||
|
|
const [error, setError] = useState<string | null>(null);
|
||
|
|
|
||
|
|
// Filtros
|
||
|
|
const [searchValue, setSearchValue] = useState("");
|
||
|
|
const [statusFilter, setStatusFilter] = useState("all");
|
||
|
|
|
||
|
|
// Cargar proyectos
|
||
|
|
const fetchProjects = async () => {
|
||
|
|
setIsLoading(true);
|
||
|
|
setError(null);
|
||
|
|
try {
|
||
|
|
const data = await getProjects();
|
||
|
|
setProjects(data);
|
||
|
|
} catch (err) {
|
||
|
|
console.error("Error fetching projects:", err);
|
||
|
|
setError("Error al cargar los proyectos. Por favor, intenta de nuevo.");
|
||
|
|
} finally {
|
||
|
|
setIsLoading(false);
|
||
|
|
}
|
||
|
|
};
|
||
|
|
|
||
|
|
useEffect(() => {
|
||
|
|
fetchProjects();
|
||
|
|
}, []);
|
||
|
|
|
||
|
|
// Filtrar proyectos
|
||
|
|
const filteredProjects = useMemo(() => {
|
||
|
|
let result = [...projects];
|
||
|
|
|
||
|
|
// Filtro de búsqueda (nombre o referencia)
|
||
|
|
if (searchValue) {
|
||
|
|
const search = searchValue.toLowerCase();
|
||
|
|
result = result.filter(
|
||
|
|
(project) =>
|
||
|
|
project.name.toLowerCase().includes(search) ||
|
||
|
|
project.ref.toLowerCase().includes(search) ||
|
||
|
|
project.client.toLowerCase().includes(search)
|
||
|
|
);
|
||
|
|
}
|
||
|
|
|
||
|
|
// Filtro de estado
|
||
|
|
if (statusFilter !== "all") {
|
||
|
|
result = result.filter((project) => project.status === statusFilter);
|
||
|
|
}
|
||
|
|
|
||
|
|
return result;
|
||
|
|
}, [projects, searchValue, statusFilter]);
|
||
|
|
|
||
|
|
// Limpiar filtros
|
||
|
|
const handleClearFilters = () => {
|
||
|
|
setSearchValue("");
|
||
|
|
setStatusFilter("all");
|
||
|
|
};
|
||
|
|
|
||
|
|
// Contar seleccionados (placeholder - el DataTable lo maneja internamente)
|
||
|
|
const selectedCount = 0;
|
||
|
|
|
||
|
|
// Estado de error
|
||
|
|
if (error) {
|
||
|
|
return (
|
||
|
|
<div className="flex flex-col items-center justify-center p-8 text-center">
|
||
|
|
<div className="mx-auto flex h-12 w-12 items-center justify-center rounded-full bg-destructive/10">
|
||
|
|
<FolderKanban className="h-6 w-6 text-destructive" />
|
||
|
|
</div>
|
||
|
|
<h3 className="mt-4 text-lg font-semibold">Error al cargar</h3>
|
||
|
|
<p className="mt-2 text-sm text-muted-foreground max-w-sm">{error}</p>
|
||
|
|
<Button onClick={fetchProjects} variant="outline" className="mt-4">
|
||
|
|
<RefreshCw className="mr-2 h-4 w-4" />
|
||
|
|
Reintentar
|
||
|
|
</Button>
|
||
|
|
</div>
|
||
|
|
);
|
||
|
|
}
|
||
|
|
|
||
|
|
// Estado de carga
|
||
|
|
if (isLoading) {
|
||
|
|
return <DataTableSkeleton />;
|
||
|
|
}
|
||
|
|
|
||
|
|
// Estado vacío
|
||
|
|
if (projects.length === 0) {
|
||
|
|
return (
|
||
|
|
<EmptyState
|
||
|
|
title="No hay proyectos"
|
||
|
|
description="Aún no tienes proyectos creados. Comienza creando tu primer proyecto."
|
||
|
|
actionLabel="Crear proyecto"
|
||
|
|
onAction={() => console.log("Create project")}
|
||
|
|
/>
|
||
|
|
);
|
||
|
|
}
|
||
|
|
|
||
|
|
return (
|
||
|
|
<div className="space-y-4">
|
||
|
|
<DataTableToolbar
|
||
|
|
searchValue={searchValue}
|
||
|
|
onSearchChange={setSearchValue}
|
||
|
|
statusFilter={statusFilter}
|
||
|
|
onStatusFilterChange={setStatusFilter}
|
||
|
|
selectedCount={selectedCount}
|
||
|
|
onClearFilters={handleClearFilters}
|
||
|
|
/>
|
||
|
|
<DataTable
|
||
|
|
columns={projectColumns}
|
||
|
|
data={filteredProjects}
|
||
|
|
searchKey="name"
|
||
|
|
searchValue={searchValue}
|
||
|
|
/>
|
||
|
|
</div>
|
||
|
|
);
|
||
|
|
}
|