211 lines
7.7 KiB
TypeScript
211 lines
7.7 KiB
TypeScript
import React from 'react';
|
|
import { useNavigate } from 'react-router-dom';
|
|
import { Card, CardHeader, CardBody } from '../../ui/Card';
|
|
import { Button } from '../../ui/Button';
|
|
import {
|
|
FileText,
|
|
CheckCircle,
|
|
Clock,
|
|
Truck,
|
|
AlertCircle,
|
|
ChevronRight,
|
|
Euro,
|
|
Calendar,
|
|
Package
|
|
} from 'lucide-react';
|
|
import { useProcurementDashboard } from '../../../api/hooks/orders';
|
|
import { useCurrentTenant } from '../../../stores/tenant.store';
|
|
|
|
const PurchaseOrdersTracking: React.FC = () => {
|
|
const navigate = useNavigate();
|
|
const currentTenant = useCurrentTenant();
|
|
const tenantId = currentTenant?.id || '';
|
|
|
|
const { data: dashboard, isLoading } = useProcurementDashboard(tenantId);
|
|
|
|
const getStatusIcon = (status: string) => {
|
|
switch (status) {
|
|
case 'draft':
|
|
return <Clock className="w-4 h-4" />;
|
|
case 'pending_approval':
|
|
return <AlertCircle className="w-4 h-4" />;
|
|
case 'approved':
|
|
return <CheckCircle className="w-4 h-4" />;
|
|
case 'in_execution':
|
|
return <Truck className="w-4 h-4" />;
|
|
case 'completed':
|
|
return <CheckCircle className="w-4 h-4" />;
|
|
default:
|
|
return <FileText className="w-4 h-4" />;
|
|
}
|
|
};
|
|
|
|
const getStatusColor = (status: string) => {
|
|
switch (status) {
|
|
case 'draft':
|
|
return 'text-[var(--text-tertiary)] bg-[var(--bg-tertiary)]';
|
|
case 'pending_approval':
|
|
return 'text-yellow-700 bg-yellow-100';
|
|
case 'approved':
|
|
return 'text-green-700 bg-green-100';
|
|
case 'in_execution':
|
|
return 'text-blue-700 bg-blue-100';
|
|
case 'completed':
|
|
return 'text-green-700 bg-green-100';
|
|
case 'cancelled':
|
|
return 'text-red-700 bg-red-100';
|
|
default:
|
|
return 'text-[var(--text-secondary)] bg-[var(--bg-secondary)]';
|
|
}
|
|
};
|
|
|
|
const getStatusLabel = (status: string) => {
|
|
const labels: Record<string, string> = {
|
|
draft: 'Borrador',
|
|
pending_approval: 'Pendiente Aprobación',
|
|
approved: 'Aprobado',
|
|
in_execution: 'En Ejecución',
|
|
completed: 'Completado',
|
|
cancelled: 'Cancelado'
|
|
};
|
|
return labels[status] || status;
|
|
};
|
|
|
|
const handleViewAllPOs = () => {
|
|
navigate('/app/operations/procurement');
|
|
};
|
|
|
|
const handleViewPODetails = (planId: string) => {
|
|
navigate(`/app/operations/procurement?plan=${planId}`);
|
|
};
|
|
|
|
if (isLoading) {
|
|
return (
|
|
<Card>
|
|
<CardHeader>
|
|
<div className="flex items-center justify-between">
|
|
<div className="flex items-center gap-2">
|
|
<FileText className="w-5 h-5 text-[var(--color-primary)]" />
|
|
<h3 className="text-lg font-semibold text-[var(--text-primary)]">Órdenes de Compra</h3>
|
|
</div>
|
|
</div>
|
|
<p className="text-sm text-[var(--text-secondary)] mt-1">Seguimiento de órdenes de compra</p>
|
|
</CardHeader>
|
|
<CardBody>
|
|
<div className="flex items-center justify-center py-8">
|
|
<div className="animate-spin rounded-full h-8 w-8 border-b-2 border-[var(--color-primary)]"></div>
|
|
</div>
|
|
</CardBody>
|
|
</Card>
|
|
);
|
|
}
|
|
|
|
const recentPlans = dashboard?.recent_plans || [];
|
|
|
|
return (
|
|
<Card>
|
|
<CardHeader>
|
|
<div className="flex items-center justify-between">
|
|
<div className="flex items-center gap-2">
|
|
<FileText className="w-5 h-5 text-[var(--color-primary)]" />
|
|
<h3 className="text-lg font-semibold text-[var(--text-primary)]">Órdenes de Compra</h3>
|
|
</div>
|
|
<Button
|
|
variant="ghost"
|
|
size="sm"
|
|
onClick={handleViewAllPOs}
|
|
className="text-[var(--color-primary)] hover:text-[var(--color-primary)]/80"
|
|
>
|
|
Ver Todas
|
|
<ChevronRight className="w-4 h-4 ml-1" />
|
|
</Button>
|
|
</div>
|
|
<p className="text-sm text-[var(--text-secondary)] mt-1">Seguimiento de órdenes de compra</p>
|
|
</CardHeader>
|
|
<CardBody>
|
|
{recentPlans.length === 0 ? (
|
|
<div className="text-center py-8">
|
|
<Package className="w-12 h-12 text-[var(--text-tertiary)] mx-auto mb-3" />
|
|
<p className="text-[var(--text-secondary)]">No hay órdenes de compra recientes</p>
|
|
<Button
|
|
variant="outline"
|
|
size="sm"
|
|
onClick={handleViewAllPOs}
|
|
className="mt-4"
|
|
>
|
|
Crear Plan de Compras
|
|
</Button>
|
|
</div>
|
|
) : (
|
|
<div className="space-y-3">
|
|
{recentPlans.slice(0, 5).map((plan: any) => (
|
|
<div
|
|
key={plan.id}
|
|
className="flex items-center justify-between p-4 bg-[var(--bg-secondary)] rounded-lg hover:bg-[var(--bg-tertiary)] transition-colors cursor-pointer"
|
|
onClick={() => handleViewPODetails(plan.id)}
|
|
>
|
|
<div className="flex items-start gap-3 flex-1">
|
|
<div className={`p-2 rounded-lg ${getStatusColor(plan.status)}`}>
|
|
{getStatusIcon(plan.status)}
|
|
</div>
|
|
<div className="flex-1 min-w-0">
|
|
<div className="flex items-center gap-2 mb-1">
|
|
<span className="font-medium text-[var(--text-primary)]">
|
|
{plan.plan_number}
|
|
</span>
|
|
<span className={`px-2 py-0.5 rounded-full text-xs ${getStatusColor(plan.status)}`}>
|
|
{getStatusLabel(plan.status)}
|
|
</span>
|
|
</div>
|
|
<div className="flex items-center gap-4 text-sm text-[var(--text-secondary)]">
|
|
<div className="flex items-center gap-1">
|
|
<Calendar className="w-3.5 h-3.5" />
|
|
<span>{new Date(plan.plan_date).toLocaleDateString('es-ES')}</span>
|
|
</div>
|
|
<div className="flex items-center gap-1">
|
|
<Package className="w-3.5 h-3.5" />
|
|
<span>{plan.total_requirements} items</span>
|
|
</div>
|
|
<div className="flex items-center gap-1">
|
|
<Euro className="w-3.5 h-3.5" />
|
|
<span>€{plan.total_estimated_cost?.toFixed(2) || '0.00'}</span>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
<ChevronRight className="w-5 h-5 text-[var(--text-tertiary)] flex-shrink-0" />
|
|
</div>
|
|
))}
|
|
</div>
|
|
)}
|
|
|
|
{/* Summary Stats */}
|
|
{dashboard?.stats && (
|
|
<div className="grid grid-cols-3 gap-4 mt-4 pt-4 border-t border-[var(--border-primary)]">
|
|
<div className="text-center">
|
|
<div className="text-2xl font-bold text-[var(--text-primary)]">
|
|
{dashboard.stats.total_plans || 0}
|
|
</div>
|
|
<div className="text-xs text-[var(--text-secondary)] mt-1">Total Planes</div>
|
|
</div>
|
|
<div className="text-center">
|
|
<div className="text-2xl font-bold text-[var(--color-success)]">
|
|
{dashboard.stats.approved_plans || 0}
|
|
</div>
|
|
<div className="text-xs text-[var(--text-secondary)] mt-1">Aprobados</div>
|
|
</div>
|
|
<div className="text-center">
|
|
<div className="text-2xl font-bold text-[var(--color-warning)]">
|
|
{dashboard.stats.pending_plans || 0}
|
|
</div>
|
|
<div className="text-xs text-[var(--text-secondary)] mt-1">Pendientes</div>
|
|
</div>
|
|
</div>
|
|
)}
|
|
</CardBody>
|
|
</Card>
|
|
);
|
|
};
|
|
|
|
export default PurchaseOrdersTracking;
|