227 lines
6.3 KiB
TypeScript
227 lines
6.3 KiB
TypeScript
import { useState } from 'react';
|
|
import { useNavigate } from 'react-router-dom';
|
|
import {
|
|
Plus,
|
|
MoreVertical,
|
|
Eye,
|
|
Edit,
|
|
Trash2,
|
|
Building2,
|
|
} from 'lucide-react';
|
|
import { Button } from '@components/atoms/Button';
|
|
import { Card, CardHeader, CardTitle, CardContent } from '@components/molecules/Card';
|
|
import { DataTable, type Column } from '@components/organisms/DataTable';
|
|
import { Dropdown, type DropdownItem } from '@components/organisms/Dropdown';
|
|
import { ConfirmModal } from '@components/organisms/Modal';
|
|
import { Breadcrumbs } from '@components/organisms/Breadcrumbs';
|
|
import { NoDataEmptyState, ErrorEmptyState } from '@components/templates/EmptyState';
|
|
import { useCompanies } from '@features/companies/hooks';
|
|
import { CompanyFiltersPanel } from '@features/companies/components/CompanyFiltersPanel';
|
|
import type { Company } from '@features/companies/types';
|
|
import { formatDate } from '@utils/formatters';
|
|
|
|
export function CompaniesListPage() {
|
|
const navigate = useNavigate();
|
|
const [companyToDelete, setCompanyToDelete] = useState<Company | null>(null);
|
|
|
|
const {
|
|
companies,
|
|
total,
|
|
page,
|
|
totalPages,
|
|
isLoading,
|
|
error,
|
|
filters,
|
|
setFilters,
|
|
deleteCompany,
|
|
refresh,
|
|
} = useCompanies({ page: 1, limit: 10 });
|
|
|
|
const getActionsMenu = (company: Company): DropdownItem[] => {
|
|
return [
|
|
{
|
|
key: 'view',
|
|
label: 'Ver detalle',
|
|
icon: <Eye className="h-4 w-4" />,
|
|
onClick: () => navigate(`/companies/${company.id}`),
|
|
},
|
|
{
|
|
key: 'edit',
|
|
label: 'Editar',
|
|
icon: <Edit className="h-4 w-4" />,
|
|
onClick: () => navigate(`/companies/${company.id}/edit`),
|
|
},
|
|
{
|
|
key: 'delete',
|
|
label: 'Eliminar',
|
|
icon: <Trash2 className="h-4 w-4" />,
|
|
danger: true,
|
|
onClick: () => setCompanyToDelete(company),
|
|
},
|
|
];
|
|
};
|
|
|
|
const columns: Column<Company>[] = [
|
|
{
|
|
key: 'company',
|
|
header: 'Empresa',
|
|
render: (company) => (
|
|
<div className="flex items-center gap-3">
|
|
<div className="flex h-10 w-10 items-center justify-center rounded-lg bg-primary-100">
|
|
<Building2 className="h-5 w-5 text-primary-600" />
|
|
</div>
|
|
<div>
|
|
<div className="font-medium text-gray-900">{company.name}</div>
|
|
<div className="text-sm text-gray-500">{company.taxId || '-'}</div>
|
|
</div>
|
|
</div>
|
|
),
|
|
},
|
|
{
|
|
key: 'legalName',
|
|
header: 'Razón social',
|
|
render: (company) => (
|
|
<span className="text-sm text-gray-600">{company.legalName || '-'}</span>
|
|
),
|
|
},
|
|
{
|
|
key: 'parent',
|
|
header: 'Empresa matriz',
|
|
render: (company) => (
|
|
<span className="text-sm text-gray-600">
|
|
{company.parentCompanyName || '-'}
|
|
</span>
|
|
),
|
|
},
|
|
{
|
|
key: 'currency',
|
|
header: 'Moneda',
|
|
render: (company) => (
|
|
<span className="inline-flex rounded-full bg-gray-100 px-2 py-1 text-xs font-medium text-gray-700">
|
|
{company.currencyCode || 'MXN'}
|
|
</span>
|
|
),
|
|
},
|
|
{
|
|
key: 'createdAt',
|
|
header: 'Creado',
|
|
sortable: true,
|
|
render: (company) => (
|
|
<span className="text-sm text-gray-500">
|
|
{formatDate(company.createdAt, 'short')}
|
|
</span>
|
|
),
|
|
},
|
|
{
|
|
key: 'actions',
|
|
header: '',
|
|
render: (company) => (
|
|
<Dropdown
|
|
trigger={
|
|
<button className="rounded p-1 hover:bg-gray-100">
|
|
<MoreVertical className="h-4 w-4 text-gray-500" />
|
|
</button>
|
|
}
|
|
items={getActionsMenu(company)}
|
|
align="right"
|
|
/>
|
|
),
|
|
},
|
|
];
|
|
|
|
const handlePageChange = (newPage: number) => {
|
|
setFilters({ ...filters, page: newPage });
|
|
};
|
|
|
|
const handleSort = (key: string) => {
|
|
const newOrder = filters.sortBy === key && filters.sortOrder === 'asc' ? 'desc' : 'asc';
|
|
setFilters({ ...filters, sortBy: key, sortOrder: newOrder });
|
|
};
|
|
|
|
const handleDeleteConfirm = async () => {
|
|
if (companyToDelete) {
|
|
await deleteCompany(companyToDelete.id);
|
|
setCompanyToDelete(null);
|
|
}
|
|
};
|
|
|
|
if (error) {
|
|
return (
|
|
<div className="p-6">
|
|
<ErrorEmptyState onRetry={refresh} />
|
|
</div>
|
|
);
|
|
}
|
|
|
|
return (
|
|
<div className="space-y-6 p-6">
|
|
<Breadcrumbs items={[{ label: 'Empresas' }]} />
|
|
|
|
<div className="flex items-center justify-between">
|
|
<div>
|
|
<h1 className="text-2xl font-bold text-gray-900">Empresas</h1>
|
|
<p className="text-sm text-gray-500">
|
|
Gestiona las empresas del sistema
|
|
</p>
|
|
</div>
|
|
<Button onClick={() => navigate('/companies/new')}>
|
|
<Plus className="mr-2 h-4 w-4" />
|
|
Nueva empresa
|
|
</Button>
|
|
</div>
|
|
|
|
<Card>
|
|
<CardHeader>
|
|
<CardTitle>Lista de empresas</CardTitle>
|
|
</CardHeader>
|
|
<CardContent>
|
|
<div className="space-y-4">
|
|
<CompanyFiltersPanel
|
|
filters={filters}
|
|
onFiltersChange={setFilters}
|
|
/>
|
|
|
|
{companies.length === 0 && !isLoading ? (
|
|
<NoDataEmptyState
|
|
entityName="empresas"
|
|
onCreateNew={() => navigate('/companies/new')}
|
|
/>
|
|
) : (
|
|
<DataTable
|
|
data={companies}
|
|
columns={columns}
|
|
isLoading={isLoading}
|
|
pagination={{
|
|
page,
|
|
totalPages,
|
|
total,
|
|
limit: filters.limit || 10,
|
|
onPageChange: handlePageChange,
|
|
}}
|
|
sorting={{
|
|
sortBy: filters.sortBy || null,
|
|
sortOrder: (filters.sortOrder as 'asc' | 'desc') || 'asc',
|
|
onSort: handleSort,
|
|
}}
|
|
/>
|
|
)}
|
|
</div>
|
|
</CardContent>
|
|
</Card>
|
|
|
|
{/* Delete confirmation modal */}
|
|
<ConfirmModal
|
|
isOpen={!!companyToDelete}
|
|
onClose={() => setCompanyToDelete(null)}
|
|
onConfirm={handleDeleteConfirm}
|
|
title="Eliminar empresa"
|
|
message={`¿Estás seguro de que deseas eliminar "${companyToDelete?.name}"? Esta acción no se puede deshacer.`}
|
|
variant="danger"
|
|
confirmText="Eliminar"
|
|
/>
|
|
</div>
|
|
);
|
|
}
|
|
|
|
export default CompaniesListPage;
|