import React, { useState } from 'react'; import { Plus, ShoppingCart, Truck, Euro, Calendar, Clock, CheckCircle, AlertCircle, Package, Eye, Loader, Edit, ArrowRight, X, Save, Building2, Play, Zap, User } from 'lucide-react'; import { Button, Input, Card, StatsGrid, StatusCard, getStatusColor, StatusModal } from '../../../../components/ui'; import { formatters } from '../../../../components/ui/Stats/StatsPresets'; import { PageHeader } from '../../../../components/layout'; import { CreatePurchaseOrderModal } from '../../../../components/domain/procurement/CreatePurchaseOrderModal'; import { useProcurementDashboard, useProcurementPlans, usePlanRequirements, useGenerateProcurementPlan, useUpdateProcurementPlanStatus, useTriggerDailyScheduler } from '../../../../api'; import { useTenantStore } from '../../../../stores/tenant.store'; const ProcurementPage: React.FC = () => { const [searchTerm, setSearchTerm] = useState(''); const [showForm, setShowForm] = useState(false); const [modalMode, setModalMode] = useState<'view' | 'edit'>('view'); const [selectedPlan, setSelectedPlan] = useState(null); const [editingPlan, setEditingPlan] = useState(null); const [editFormData, setEditFormData] = useState({}); const [selectedPlanForRequirements, setSelectedPlanForRequirements] = useState(null); const [showCriticalRequirements, setShowCriticalRequirements] = useState(false); const [showGeneratePlanModal, setShowGeneratePlanModal] = useState(false); const [showRequirementDetailsModal, setShowRequirementDetailsModal] = useState(false); const [selectedRequirement, setSelectedRequirement] = useState(null); const [showCreatePurchaseOrderModal, setShowCreatePurchaseOrderModal] = useState(false); const [selectedRequirementsForPO, setSelectedRequirementsForPO] = useState([]); const [isAIMode, setIsAIMode] = useState(true); const [generatePlanForm, setGeneratePlanForm] = useState({ plan_date: new Date().toISOString().split('T')[0], planning_horizon_days: 14, include_safety_stock: true, safety_stock_percentage: 20, force_regenerate: false }); // Requirement details functionality const handleViewRequirementDetails = (requirement: any) => { setSelectedRequirement(requirement); setShowRequirementDetailsModal(true); }; const { currentTenant } = useTenantStore(); const tenantId = currentTenant?.id || ''; // Real API data hooks const { data: dashboardData, isLoading: isDashboardLoading } = useProcurementDashboard(tenantId); const { data: procurementPlans, isLoading: isPlansLoading } = useProcurementPlans({ tenant_id: tenantId, limit: 50, offset: 0 }); // Get plan requirements for selected plan const { data: allPlanRequirements, isLoading: isPlanRequirementsLoading } = usePlanRequirements({ tenant_id: tenantId, plan_id: selectedPlanForRequirements || '' // Remove status filter to get all requirements }, { enabled: !!selectedPlanForRequirements && !!tenantId }); // Filter critical requirements client-side const planRequirements = allPlanRequirements?.filter(req => { // Check various conditions that might make a requirement critical const isLowStock = req.current_stock_level && req.required_quantity && (req.current_stock_level / req.required_quantity) < 0.5; const isNearDeadline = req.required_by_date && (new Date(req.required_by_date).getTime() - Date.now()) / (1000 * 60 * 60 * 24) < 7; const hasHighPriority = req.priority === 'high'; return isLowStock || isNearDeadline || hasHighPriority; }); const generatePlanMutation = useGenerateProcurementPlan(); const updatePlanStatusMutation = useUpdateProcurementPlanStatus(); const triggerSchedulerMutation = useTriggerDailyScheduler(); // Helper functions for stage transitions and edit functionality const getNextStage = (currentStatus: string): string | null => { const stageFlow: { [key: string]: string } = { 'draft': 'pending_approval', 'pending_approval': 'approved', 'approved': 'in_execution', 'in_execution': 'completed' }; return stageFlow[currentStatus] || null; }; const getStageActionConfig = (status: string) => { const configs: { [key: string]: { label: string; icon: any; variant: 'primary' | 'outline'; color?: string } } = { 'draft': { label: 'Enviar a Aprobación', icon: ArrowRight, variant: 'primary' }, 'pending_approval': { label: 'Aprobar', icon: CheckCircle, variant: 'primary' }, 'approved': { label: 'Iniciar Ejecución', icon: Play, variant: 'primary' }, 'in_execution': { label: 'Completar', icon: CheckCircle, variant: 'primary' } }; return configs[status]; }; const canEdit = (status: string): boolean => { return status === 'draft'; }; const handleStageTransition = (planId: string, currentStatus: string) => { const nextStage = getNextStage(currentStatus); if (nextStage) { updatePlanStatusMutation.mutate({ tenant_id: tenantId, plan_id: planId, status: nextStage as any }); } }; const handleCancelPlan = (planId: string) => { updatePlanStatusMutation.mutate({ tenant_id: tenantId, plan_id: planId, status: 'cancelled' }); }; const handleEditPlan = (plan: any) => { setEditingPlan(plan); setEditFormData({ special_requirements: plan.special_requirements || '', planning_horizon_days: plan.planning_horizon_days || 14, priority: plan.priority || 'medium' }); }; const handleSaveEdit = () => { // For now, we'll just update the special requirements since that's the main editable field // In a real implementation, you might have a separate API endpoint for updating plan details setEditingPlan(null); setEditFormData({}); // Here you would typically call an update API }; const handleCancelEdit = () => { setEditingPlan(null); setEditFormData({}); }; const handleShowCriticalRequirements = (planId: string) => { setSelectedPlanForRequirements(planId); setShowCriticalRequirements(true); }; const handleCloseCriticalRequirements = () => { setShowCriticalRequirements(false); setSelectedPlanForRequirements(null); }; if (!tenantId) { return (

No hay tenant seleccionado

Selecciona un tenant para ver los datos de procurement

); } const getPlanStatusConfig = (status: string) => { const statusConfig = { draft: { text: 'Borrador', icon: Clock }, pending_approval: { text: 'Pendiente Aprobación', icon: Clock }, approved: { text: 'Aprobado', icon: CheckCircle }, in_execution: { text: 'En Ejecución', icon: Truck }, completed: { text: 'Completado', icon: CheckCircle }, cancelled: { text: 'Cancelado', icon: AlertCircle }, }; const config = statusConfig[status as keyof typeof statusConfig]; const Icon = config?.icon; return { color: getStatusColor(status === 'in_execution' ? 'inTransit' : status === 'pending_approval' ? 'pending' : status), text: config?.text || status, icon: Icon, isCritical: status === 'cancelled', isHighlight: status === 'pending_approval' }; }; const filteredPlans = procurementPlans?.plans?.filter(plan => { const matchesSearch = plan.plan_number.toLowerCase().includes(searchTerm.toLowerCase()) || plan.status.toLowerCase().includes(searchTerm.toLowerCase()) || (plan.special_requirements && plan.special_requirements.toLowerCase().includes(searchTerm.toLowerCase())); return matchesSearch; }) || []; const stats = { totalPlans: dashboardData?.summary?.total_plans || 0, activePlans: dashboardData?.summary?.active_plans || 0, pendingRequirements: dashboardData?.summary?.pending_requirements || 0, criticalRequirements: dashboardData?.summary?.critical_requirements || 0, totalEstimatedCost: dashboardData?.summary?.total_estimated_cost || 0, totalApprovedCost: dashboardData?.summary?.total_approved_cost || 0, }; const procurementStats = [ { title: 'Planes Totales', value: stats.totalPlans, variant: 'default' as const, icon: Package, }, { title: 'Planes Activos', value: stats.activePlans, variant: 'success' as const, icon: CheckCircle, }, { title: 'Requerimientos Pendientes', value: stats.pendingRequirements, variant: 'warning' as const, icon: Clock, }, { title: 'Críticos', value: stats.criticalRequirements, variant: 'warning' as const, icon: AlertCircle, }, { title: 'Costo Estimado', value: formatters.currency(stats.totalEstimatedCost), variant: 'info' as const, icon: Euro, }, { title: 'Costo Aprobado', value: formatters.currency(stats.totalApprovedCost), variant: 'success' as const, icon: Euro, }, ]; return (
{/* AI/Manual Mode Segmented Control */}
{/* Action Buttons */} {!isAIMode && ( )} {/* Testing button - keep for development */}
{/* Stats Grid */} {isDashboardLoading ? (
) : ( )}
setSearchTerm(e.target.value)} className="w-full" />
{/* Procurement Plans Grid - Mobile-Optimized */}
{isPlansLoading ? (
) : ( filteredPlans.map((plan) => { const statusConfig = getPlanStatusConfig(plan.status); const nextStageConfig = getStageActionConfig(plan.status); const isEditing = editingPlan?.id === plan.id; const isEditable = canEdit(plan.status); // Build actions array with proper priority hierarchy for better UX const actions = []; // Edit mode actions (highest priority when editing) if (isEditing) { actions.push( { label: 'Guardar', icon: Save, variant: 'primary' as const, priority: 'primary' as const, onClick: handleSaveEdit }, { label: 'Cancelar', icon: X, variant: 'outline' as const, priority: 'primary' as const, destructive: true, onClick: handleCancelEdit } ); } else { // Primary action: Stage transition (most important) if (nextStageConfig) { actions.push({ label: nextStageConfig.label, icon: nextStageConfig.icon, variant: nextStageConfig.variant, priority: 'primary' as const, onClick: () => handleStageTransition(plan.id, plan.status) }); } // Secondary actions: Edit and View if (isEditable) { actions.push({ label: 'Editar', icon: Edit, variant: 'outline' as const, priority: 'secondary' as const, onClick: () => handleEditPlan(plan) }); } actions.push({ label: 'Ver', icon: Eye, variant: 'outline' as const, priority: 'secondary' as const, onClick: () => { setSelectedPlan(plan); setModalMode('view'); setShowForm(true); } }); // Show Critical Requirements button actions.push({ label: 'Req. Críticos', icon: AlertCircle, variant: 'outline' as const, priority: 'secondary' as const, onClick: () => handleShowCriticalRequirements(plan.id) }); // Tertiary action: Cancel (least prominent, destructive) if (!['completed', 'cancelled'].includes(plan.status)) { actions.push({ label: 'Cancelar', icon: X, variant: 'outline' as const, priority: 'tertiary' as const, destructive: true, onClick: () => handleCancelPlan(plan.id) }); } } return (
14 ? '#10b981' : plan.planning_horizon_days > 7 ? '#f59e0b' : '#ef4444' } : undefined} metadata={[ `Período: ${new Date(plan.plan_period_start).toLocaleDateString('es-ES', { day: '2-digit', month: '2-digit' })} - ${new Date(plan.plan_period_end).toLocaleDateString('es-ES', { day: '2-digit', month: '2-digit' })}`, `Creado: ${new Date(plan.created_at).toLocaleDateString('es-ES', { day: '2-digit', month: '2-digit' })}`, ...(plan.special_requirements ? [`Req. especiales: ${plan.special_requirements}`] : []) ]} actions={actions} /> {/* Inline Edit Form for Draft Plans */} {isEditing && (

Editando Plan {plan.plan_number}