From 90100a66c6df477b3cd5b93b046bbcd4e24d4056 Mon Sep 17 00:00:00 2001 From: Urtzi Alfaro Date: Fri, 22 Aug 2025 15:31:52 +0200 Subject: [PATCH] Delete legacy alerts --- frontend/src/api/hooks/useInventory.ts | 51 +- .../src/api/services/inventory.service.ts | 59 --- frontend/src/api/services/orders.service.ts | 14 - .../src/api/services/production.service.ts | 20 +- .../src/api/services/suppliers.service.ts | 17 - .../inventory/InventoryDashboardWidget.tsx | 8 +- .../components/inventory/StockAlertsPanel.tsx | 359 --------------- .../src/components/simple/CriticalAlerts.tsx | 180 -------- frontend/src/components/simple/index.ts | 1 - frontend/src/components/ui/AlertCard.tsx | 155 ------- frontend/src/hooks/useRealAlerts.ts | 165 ------- .../src/pages/dashboard/DashboardPage.tsx | 28 +- .../src/pages/inventory/InventoryPage.tsx | 46 +- frontend/src/store/slices/forecastSlice.ts | 82 ---- services/forecasting/app/api/forecasts.py | 64 +-- services/forecasting/app/core/config.py | 4 - services/forecasting/app/models/forecasts.py | 25 - .../forecasting/app/repositories/__init__.py | 2 - .../repositories/forecast_alert_repository.py | 375 --------------- services/forecasting/app/schemas/forecasts.py | 17 - services/forecasting/app/services/__init__.py | 2 - .../app/services/forecasting_service.py | 65 +-- .../forecasting/app/services/messaging.py | 19 - .../app/services/food_safety_service.py | 3 +- services/orders/app/core/config.py | 5 - services/orders/app/core/database.py | 1 - services/orders/app/models/alerts.py | 144 ------ .../orders/app/services/orders_service.py | 63 --- services/production/app/api/production.py | 74 --- services/production/app/core/config.py | 5 - services/production/app/models/__init__.py | 6 +- services/production/app/models/production.py | 84 ---- .../production/app/repositories/__init__.py | 2 - .../production_alert_repository.py | 379 --------------- services/production/app/schemas/production.py | 68 --- services/production/app/services/__init__.py | 4 +- .../app/services/production_alert_service.py | 435 ------------------ .../app/services/production_service.py | 1 - shared/notifications/__init__.py | 16 +- shared/notifications/alert_integration.py | 285 ------------ 40 files changed, 25 insertions(+), 3308 deletions(-) delete mode 100644 frontend/src/components/inventory/StockAlertsPanel.tsx delete mode 100644 frontend/src/components/simple/CriticalAlerts.tsx delete mode 100644 frontend/src/components/ui/AlertCard.tsx delete mode 100644 frontend/src/hooks/useRealAlerts.ts delete mode 100644 services/forecasting/app/repositories/forecast_alert_repository.py delete mode 100644 services/orders/app/models/alerts.py delete mode 100644 services/production/app/repositories/production_alert_repository.py delete mode 100644 services/production/app/services/production_alert_service.py delete mode 100644 shared/notifications/alert_integration.py diff --git a/frontend/src/api/hooks/useInventory.ts b/frontend/src/api/hooks/useInventory.ts index 2b422917..32919f76 100644 --- a/frontend/src/api/hooks/useInventory.ts +++ b/frontend/src/api/hooks/useInventory.ts @@ -12,7 +12,6 @@ import { InventoryItem, StockLevel, StockMovement, - StockAlert, InventorySearchParams, CreateInventoryItemRequest, UpdateInventoryItemRequest, @@ -31,7 +30,6 @@ interface UseInventoryReturn { items: InventoryItem[]; stockLevels: Record; movements: StockMovement[]; - alerts: StockAlert[]; dashboardData: InventoryDashboardData | null; isLoading: boolean; error: string | null; @@ -54,9 +52,6 @@ interface UseInventoryReturn { adjustStock: (itemId: string, adjustment: StockAdjustmentRequest) => Promise; loadMovements: (params?: any) => Promise; - // Alerts - loadAlerts: () => Promise; - acknowledgeAlert: (alertId: string) => Promise; // Dashboard loadDashboard: () => Promise; @@ -69,7 +64,6 @@ interface UseInventoryReturn { interface UseInventoryDashboardReturn { dashboardData: InventoryDashboardData | null; - alerts: StockAlert[]; isLoading: boolean; error: string | null; refresh: () => Promise; @@ -95,7 +89,6 @@ export const useInventory = (autoLoad = true): UseInventoryReturn => { const [items, setItems] = useState([]); const [stockLevels, setStockLevels] = useState>({}); const [movements, setMovements] = useState([]); - const [alerts, setAlerts] = useState([]); const [dashboardData, setDashboardData] = useState(null); const [isLoading, setIsLoading] = useState(false); const [error, setError] = useState(null); @@ -278,34 +271,6 @@ export const useInventory = (autoLoad = true): UseInventoryReturn => { } }, [tenantId]); - // Load alerts - const loadAlerts = useCallback(async () => { - if (!tenantId) return; - - try { - const alertsData = await inventoryService.getStockAlerts(tenantId); - setAlerts(alertsData); - } catch (err: any) { - console.error('Error loading alerts:', err); - // Don't show toast error for this as it's not critical for forecast page - } - }, [tenantId]); - - // Acknowledge alert - const acknowledgeAlert = useCallback(async (alertId: string): Promise => { - if (!tenantId) return false; - - try { - await inventoryService.acknowledgeAlert(tenantId, alertId); - setAlerts(prev => prev.map(a => - a.id === alertId ? { ...a, is_acknowledged: true, acknowledged_at: new Date().toISOString() } : a - )); - return true; - } catch (err: any) { - toast.error('Error acknowledging alert'); - return false; - } - }, [tenantId]); // Load dashboard const loadDashboard = useCallback(async () => { @@ -337,10 +302,9 @@ export const useInventory = (autoLoad = true): UseInventoryReturn => { await Promise.all([ loadItems(), loadStockLevels(), - loadAlerts(), loadDashboard() ]); - }, [loadItems, loadStockLevels, loadAlerts, loadDashboard]); + }, [loadItems, loadStockLevels, loadDashboard]); // Auto-load on mount useEffect(() => { @@ -354,7 +318,6 @@ export const useInventory = (autoLoad = true): UseInventoryReturn => { items, stockLevels, movements, - alerts, dashboardData, isLoading, error, @@ -372,10 +335,6 @@ export const useInventory = (autoLoad = true): UseInventoryReturn => { adjustStock, loadMovements, - // Alerts - loadAlerts, - acknowledgeAlert, - // Dashboard loadDashboard, @@ -391,7 +350,6 @@ export const useInventory = (autoLoad = true): UseInventoryReturn => { export const useInventoryDashboard = (): UseInventoryDashboardReturn => { const { tenantId } = useTenantId(); const [dashboardData, setDashboardData] = useState(null); - const [alerts, setAlerts] = useState([]); const [isLoading, setIsLoading] = useState(false); const [error, setError] = useState(null); @@ -402,13 +360,9 @@ export const useInventoryDashboard = (): UseInventoryDashboardReturn => { setError(null); try { - const [dashboard, alertsData] = await Promise.all([ - inventoryService.getDashboardData(tenantId), - inventoryService.getStockAlerts(tenantId) - ]); + const dashboard = await inventoryService.getDashboardData(tenantId); setDashboardData(dashboard); - setAlerts(alertsData); } catch (err: any) { const errorMessage = err.response?.data?.detail || err.message || 'Error loading dashboard'; setError(errorMessage); @@ -425,7 +379,6 @@ export const useInventoryDashboard = (): UseInventoryDashboardReturn => { return { dashboardData, - alerts, isLoading, error, refresh diff --git a/frontend/src/api/services/inventory.service.ts b/frontend/src/api/services/inventory.service.ts index 380e9839..37a75d3e 100644 --- a/frontend/src/api/services/inventory.service.ts +++ b/frontend/src/api/services/inventory.service.ts @@ -105,22 +105,6 @@ export interface StockMovement { batch_info?: StockBatch; } -export interface StockAlert { - id: string; - item_id: string; - alert_type: 'low_stock' | 'expired' | 'expiring_soon' | 'overstock'; - severity: 'low' | 'medium' | 'high' | 'critical'; - message: string; - threshold_value?: number; - current_value?: number; - is_acknowledged: boolean; - created_at: string; - acknowledged_at?: string; - acknowledged_by?: string; - - // Related data - item?: InventoryItem; -} // ========== REQUEST/RESPONSE TYPES ========== @@ -404,32 +388,6 @@ export class InventoryService { return apiClient.get(url); } - // ========== ALERTS ========== - - /** - * Get current stock alerts - */ - async getStockAlerts(tenantId: string): Promise { - // TODO: Map to correct endpoint when available - return []; - // return apiClient.get(`/tenants/${tenantId}/inventory/alerts`); - } - - /** - * Acknowledge alert - */ - async acknowledgeAlert(tenantId: string, alertId: string): Promise { - return apiClient.post(`${this.baseEndpoint}/tenants/${tenantId}/inventory/alerts/${alertId}/acknowledge`); - } - - /** - * Bulk acknowledge alerts - */ - async bulkAcknowledgeAlerts(tenantId: string, alertIds: string[]): Promise { - return apiClient.post(`${this.baseEndpoint}/tenants/${tenantId}/inventory/alerts/bulk-acknowledge`, { - alert_ids: alertIds - }); - } // ========== DASHBOARD & ANALYTICS ========== @@ -771,23 +729,6 @@ export class InventoryService { } } - /** - * Get inventory alerts - */ - async getInventoryAlerts(tenantId: string, params?: { - alert_type?: string; - severity?: string; - status?: string; - item_id?: string; - limit?: number; - }): Promise { - try { - return await apiClient.get(`/tenants/${tenantId}/inventory/alerts`, { params }); - } catch (error) { - console.error('❌ Error fetching inventory alerts:', error); - throw error; - } - } /** * Get restock recommendations diff --git a/frontend/src/api/services/orders.service.ts b/frontend/src/api/services/orders.service.ts index afb69727..141db902 100644 --- a/frontend/src/api/services/orders.service.ts +++ b/frontend/src/api/services/orders.service.ts @@ -344,20 +344,6 @@ export class OrdersService { return apiClient.get(`${this.basePath}/analytics/seasonal`, { params }); } - // Alerts - async getOrderAlerts(params?: { - severity?: string; - status?: string; - limit?: number; - }): Promise { - return apiClient.get(`${this.basePath}/alerts`, { params }); - } - async acknowledgeAlert(alertId: string): Promise { - return apiClient.post(`${this.basePath}/alerts/${alertId}/acknowledge`); - } - async resolveAlert(alertId: string, resolution?: string): Promise { - return apiClient.post(`${this.basePath}/alerts/${alertId}/resolve`, { resolution }); - } } \ No newline at end of file diff --git a/frontend/src/api/services/production.service.ts b/frontend/src/api/services/production.service.ts index 6f61a25f..f7228f88 100644 --- a/frontend/src/api/services/production.service.ts +++ b/frontend/src/api/services/production.service.ts @@ -66,13 +66,11 @@ export interface ProductionDashboardData { equipment_in_use: number; current_efficiency: number; todays_production: number; - alerts_count: number; }; efficiency_trend: { date: string; efficiency: number }[]; quality_trend: { date: string; quality: number }[]; equipment_status: Equipment[]; active_batches: ProductionBatch[]; - alerts: any[]; } export interface BatchCreateRequest { @@ -174,8 +172,7 @@ export class ProductionService { temperature: number; humidity: number; estimated_completion: string; - alerts: any[]; - }> { + }> { return apiClient.get(`${this.basePath}/batches/${batchId}/status`); } @@ -295,20 +292,5 @@ export class ProductionService { return apiClient.get(`${this.basePath}/analytics/quality`, { params }); } - // Alerts - async getProductionAlerts(params?: { - severity?: string; - status?: string; - limit?: number; - }): Promise { - return apiClient.get(`${this.basePath}/alerts`, { params }); - } - async acknowledgeAlert(alertId: string): Promise { - return apiClient.post(`${this.basePath}/alerts/${alertId}/acknowledge`); - } - - async resolveAlert(alertId: string, resolution?: string): Promise { - return apiClient.post(`${this.basePath}/alerts/${alertId}/resolve`, { resolution }); - } } \ No newline at end of file diff --git a/frontend/src/api/services/suppliers.service.ts b/frontend/src/api/services/suppliers.service.ts index ee069f08..8f3c6321 100644 --- a/frontend/src/api/services/suppliers.service.ts +++ b/frontend/src/api/services/suppliers.service.ts @@ -188,7 +188,6 @@ export interface SupplierDashboardData { quality_scores: number[]; cost_savings: number[]; }; - alerts: any[]; contract_expirations: { supplier_name: string; days_until_expiry: number }[]; } @@ -320,23 +319,7 @@ export class SuppliersService { return apiClient.get(`${this.basePath}/analytics/risk-analysis`); } - // Alerts - async getSupplierAlerts(params?: { - severity?: string; - status?: string; - supplier_id?: string; - limit?: number; - }): Promise { - return apiClient.get(`${this.basePath}/alerts`, { params }); - } - async acknowledgeAlert(alertId: string): Promise { - return apiClient.post(`${this.basePath}/alerts/${alertId}/acknowledge`); - } - - async resolveAlert(alertId: string, resolution?: string): Promise { - return apiClient.post(`${this.basePath}/alerts/${alertId}/resolve`, { resolution }); - } // Additional methods for hooks compatibility async getSupplierStatistics(): Promise { diff --git a/frontend/src/components/inventory/InventoryDashboardWidget.tsx b/frontend/src/components/inventory/InventoryDashboardWidget.tsx index 24f75d16..57814d43 100644 --- a/frontend/src/components/inventory/InventoryDashboardWidget.tsx +++ b/frontend/src/components/inventory/InventoryDashboardWidget.tsx @@ -21,12 +21,12 @@ const InventoryDashboardWidget: React.FC = ({ onViewInventory, className = '' }) => { - const { dashboardData, alerts, isLoading, error, refresh } = useInventoryDashboard(); + const { dashboardData, isLoading, error, refresh } = useInventoryDashboard(); // Get alert counts - const criticalAlerts = alerts.filter(a => !a.is_acknowledged && a.severity === 'critical').length; - const lowStockAlerts = alerts.filter(a => !a.is_acknowledged && a.alert_type === 'low_stock').length; - const expiringAlerts = alerts.filter(a => !a.is_acknowledged && a.alert_type === 'expiring_soon').length; + const criticalAlerts = 0; + const lowStockAlerts = 0; + const expiringAlerts = 0; if (isLoading) { return ( diff --git a/frontend/src/components/inventory/StockAlertsPanel.tsx b/frontend/src/components/inventory/StockAlertsPanel.tsx deleted file mode 100644 index 27a43f9b..00000000 --- a/frontend/src/components/inventory/StockAlertsPanel.tsx +++ /dev/null @@ -1,359 +0,0 @@ -import React, { useState } from 'react'; -import { - AlertTriangle, - Clock, - Package, - TrendingDown, - CheckCircle, - X, - Filter, - Bell, - BellOff, - Calendar -} from 'lucide-react'; - -import { StockAlert } from '../../api/services/inventory.service'; - -interface StockAlertsPanelProps { - alerts: StockAlert[]; - onAcknowledge?: (alertId: string) => void; - onAcknowledgeAll?: (alertIds: string[]) => void; - onViewItem?: (itemId: string) => void; - className?: string; -} - -type AlertFilter = 'all' | 'unacknowledged' | 'low_stock' | 'expired' | 'expiring_soon'; - -const StockAlertsPanel: React.FC = ({ - alerts, - onAcknowledge, - onAcknowledgeAll, - onViewItem, - className = '' -}) => { - const [filter, setFilter] = useState('all'); - const [selectedAlerts, setSelectedAlerts] = useState>(new Set()); - - // Filter alerts based on current filter - const filteredAlerts = alerts.filter(alert => { - switch (filter) { - case 'unacknowledged': - return !alert.is_acknowledged; - case 'low_stock': - return alert.alert_type === 'low_stock'; - case 'expired': - return alert.alert_type === 'expired'; - case 'expiring_soon': - return alert.alert_type === 'expiring_soon'; - default: - return true; - } - }); - - // Get alert icon - const getAlertIcon = (alert: StockAlert) => { - switch (alert.alert_type) { - case 'low_stock': - return ; - case 'expired': - return ; - case 'expiring_soon': - return ; - case 'overstock': - return ; - default: - return ; - } - }; - - // Get alert color classes - const getAlertClasses = (alert: StockAlert) => { - const baseClasses = 'border-l-4'; - - if (alert.is_acknowledged) { - return `${baseClasses} border-gray-300 bg-gray-50`; - } - - switch (alert.severity) { - case 'critical': - return `${baseClasses} border-red-500 bg-red-50`; - case 'high': - return `${baseClasses} border-orange-500 bg-orange-50`; - case 'medium': - return `${baseClasses} border-yellow-500 bg-yellow-50`; - case 'low': - return `${baseClasses} border-blue-500 bg-blue-50`; - default: - return `${baseClasses} border-gray-500 bg-gray-50`; - } - }; - - // Get alert text color - const getAlertTextColor = (alert: StockAlert) => { - if (alert.is_acknowledged) { - return 'text-gray-600'; - } - - switch (alert.severity) { - case 'critical': - return 'text-red-700'; - case 'high': - return 'text-orange-700'; - case 'medium': - return 'text-yellow-700'; - case 'low': - return 'text-blue-700'; - default: - return 'text-gray-700'; - } - }; - - // Get alert icon color - const getAlertIconColor = (alert: StockAlert) => { - if (alert.is_acknowledged) { - return 'text-gray-400'; - } - - switch (alert.severity) { - case 'critical': - return 'text-red-500'; - case 'high': - return 'text-orange-500'; - case 'medium': - return 'text-yellow-500'; - case 'low': - return 'text-blue-500'; - default: - return 'text-gray-500'; - } - }; - - // Handle alert selection - const toggleAlertSelection = (alertId: string) => { - const newSelection = new Set(selectedAlerts); - if (newSelection.has(alertId)) { - newSelection.delete(alertId); - } else { - newSelection.add(alertId); - } - setSelectedAlerts(newSelection); - }; - - // Handle acknowledge all selected - const handleAcknowledgeSelected = () => { - if (onAcknowledgeAll && selectedAlerts.size > 0) { - onAcknowledgeAll(Array.from(selectedAlerts)); - setSelectedAlerts(new Set()); - } - }; - - // Format time ago - const formatTimeAgo = (dateString: string) => { - const date = new Date(dateString); - const now = new Date(); - const diffInHours = Math.floor((now.getTime() - date.getTime()) / (1000 * 60 * 60)); - - if (diffInHours < 1) { - return 'Hace menos de 1 hora'; - } else if (diffInHours < 24) { - return `Hace ${diffInHours} horas`; - } else { - const diffInDays = Math.floor(diffInHours / 24); - return `Hace ${diffInDays} días`; - } - }; - - // Get filter counts - const getFilterCounts = () => { - return { - all: alerts.length, - unacknowledged: alerts.filter(a => !a.is_acknowledged).length, - low_stock: alerts.filter(a => a.alert_type === 'low_stock').length, - expired: alerts.filter(a => a.alert_type === 'expired').length, - expiring_soon: alerts.filter(a => a.alert_type === 'expiring_soon').length, - }; - }; - - const filterCounts = getFilterCounts(); - - return ( -
- {/* Header */} -
-
-
- -

Alertas de Stock

- {filterCounts.unacknowledged > 0 && ( - - {filterCounts.unacknowledged} pendientes - - )} -
- - {selectedAlerts.size > 0 && ( - - )} -
- - {/* Filters */} -
- {[ - { key: 'all', label: 'Todas', count: filterCounts.all }, - { key: 'unacknowledged', label: 'Pendientes', count: filterCounts.unacknowledged }, - { key: 'low_stock', label: 'Stock Bajo', count: filterCounts.low_stock }, - { key: 'expired', label: 'Vencidas', count: filterCounts.expired }, - { key: 'expiring_soon', label: 'Por Vencer', count: filterCounts.expiring_soon }, - ].map(({ key, label, count }) => ( - - ))} -
-
- - {/* Alerts List */} -
- {filteredAlerts.length === 0 ? ( -
- -

- {filter === 'all' ? 'No hay alertas' : 'No hay alertas con este filtro'} -

-

- {filter === 'all' - ? 'Tu inventario está en buen estado' - : 'Prueba con un filtro diferente' - } -

-
- ) : ( - filteredAlerts.map((alert) => ( -
-
- {/* Selection checkbox */} - {!alert.is_acknowledged && ( - toggleAlertSelection(alert.id)} - className="mt-1 w-4 h-4 text-blue-600 rounded border-gray-300 focus:ring-blue-500" - /> - )} - - {/* Alert Icon */} -
- {getAlertIcon(alert)} -
- - {/* Alert Content */} -
-
-
-

- {alert.item?.name || 'Producto desconocido'} -

-

- {alert.message} -

- - {/* Additional Info */} -
-
- - {formatTimeAgo(alert.created_at)} -
- - {alert.threshold_value && alert.current_value && ( - - Umbral: {alert.threshold_value} | Actual: {alert.current_value} - - )} - - - Severidad: {alert.severity} - -
- - {/* Acknowledged Info */} - {alert.is_acknowledged && alert.acknowledged_at && ( -
- ✓ Confirmada {formatTimeAgo(alert.acknowledged_at)} -
- )} -
- - {/* Actions */} -
- {onViewItem && alert.item_id && ( - - )} - - {!alert.is_acknowledged && onAcknowledge && ( - - )} -
-
-
-
-
- )) - )} -
- - {/* Footer with bulk actions */} - {filteredAlerts.length > 0 && filterCounts.unacknowledged > 0 && ( -
-
- - {filterCounts.unacknowledged} alertas pendientes - - - -
-
- )} -
- ); -}; - -export default StockAlertsPanel; \ No newline at end of file diff --git a/frontend/src/components/simple/CriticalAlerts.tsx b/frontend/src/components/simple/CriticalAlerts.tsx deleted file mode 100644 index 690ab6bb..00000000 --- a/frontend/src/components/simple/CriticalAlerts.tsx +++ /dev/null @@ -1,180 +0,0 @@ -import React from 'react'; -import { AlertTriangle, Cloud, Package, Clock, ChevronRight } from 'lucide-react'; - -export interface Alert { - id: string; - type: 'stock' | 'weather' | 'order' | 'production' | 'system'; - severity: 'high' | 'medium' | 'low'; - title: string; - description: string; - action?: string; - time?: string; -} - -interface CriticalAlertsProps { - alerts?: Alert[]; - onAlertClick?: (alertId: string) => void; - className?: string; -} - -const CriticalAlerts: React.FC = ({ - alerts = [ - { - id: '1', - type: 'stock', - severity: 'high', - title: 'Stock Bajo', - description: 'Pan integral: solo 5 unidades', - action: 'Hacer más', - time: '10:30' - }, - { - id: '2', - type: 'weather', - severity: 'medium', - title: 'Lluvia Esperada', - description: 'Precipitaciones 14:00 - 17:00', - action: 'Ajustar producción', - time: '14:00' - }, - { - id: '3', - type: 'order', - severity: 'low', - title: 'Pedido Especial', - description: 'Tarta de cumpleaños Ana - Viernes', - action: 'Ver detalles', - time: 'Viernes' - } - ], - onAlertClick, - className = '' -}) => { - const getAlertIcon = (type: Alert['type']) => { - switch (type) { - case 'stock': return Package; - case 'weather': return Cloud; - case 'order': return Clock; - case 'production': return AlertTriangle; - default: return AlertTriangle; - } - }; - - const getAlertColors = (severity: Alert['severity']) => { - switch (severity) { - case 'high': return { - bg: 'bg-red-50 border-red-200', - icon: 'text-red-600', - title: 'text-red-900', - description: 'text-red-700' - }; - case 'medium': return { - bg: 'bg-yellow-50 border-yellow-200', - icon: 'text-yellow-600', - title: 'text-yellow-900', - description: 'text-yellow-700' - }; - case 'low': return { - bg: 'bg-blue-50 border-blue-200', - icon: 'text-blue-600', - title: 'text-blue-900', - description: 'text-blue-700' - }; - } - }; - - const visibleAlerts = alerts.slice(0, 3); // Show max 3 alerts - - return ( -
- {/* Header */} -
-

- - Atención Requerida -

- {alerts.length > 3 && ( - - +{alerts.length - 3} más - - )} -
- - {/* Alerts List */} - {visibleAlerts.length > 0 ? ( -
- {visibleAlerts.map((alert) => { - const IconComponent = getAlertIcon(alert.type); - const colors = getAlertColors(alert.severity); - - return ( -
onAlertClick?.(alert.id)} - className={`${colors.bg} border rounded-lg p-3 cursor-pointer hover:shadow-sm transition-all duration-200`} - > -
- - -
-
-
-

- {alert.title} -

-

- {alert.description} -

- {alert.action && ( -

- → {alert.action} -

- )} -
- -
- {alert.time && ( - - {alert.time} - - )} - -
-
-
-
-
- ); - })} -
- ) : ( -
-
- -
-

Todo bajo control

-

No hay alertas que requieran atención

-
- )} - - {/* Quick Summary */} -
-
- -
- {alerts.filter(a => a.severity === 'high').length} Urgentes -
- -
- {alerts.filter(a => a.severity === 'medium').length} Importantes -
-
- -
-
- ); -}; - -export default CriticalAlerts; \ No newline at end of file diff --git a/frontend/src/components/simple/index.ts b/frontend/src/components/simple/index.ts index d6b6bde2..3892e2b1 100644 --- a/frontend/src/components/simple/index.ts +++ b/frontend/src/components/simple/index.ts @@ -1,4 +1,3 @@ -export { default as CriticalAlerts } from './CriticalAlerts'; export { default as OrderSuggestions } from './OrderSuggestions'; export { default as QuickActions } from './QuickActions'; export { default as QuickOverview } from './QuickOverview'; diff --git a/frontend/src/components/ui/AlertCard.tsx b/frontend/src/components/ui/AlertCard.tsx deleted file mode 100644 index 3fb54a8e..00000000 --- a/frontend/src/components/ui/AlertCard.tsx +++ /dev/null @@ -1,155 +0,0 @@ -import React from 'react'; -import { AlertTriangle, TrendingDown, Package, Clock, Euro } from 'lucide-react'; - -export interface BusinessAlert { - id: string; - type: 'stockout_risk' | 'overstock' | 'revenue_loss' | 'quality_risk' | 'weather_impact'; - severity: 'low' | 'medium' | 'high' | 'critical'; - product: string; - message: string; - action: string; - impact?: { - type: 'revenue' | 'units' | 'percentage'; - value: number; - currency?: string; - }; - urgency?: 'immediate' | 'today' | 'this_week'; -} - -interface AlertCardProps { - alert: BusinessAlert; - onAction?: (alertId: string, actionType: string) => void; -} - -const getSeverityConfig = (severity: BusinessAlert['severity']) => { - switch (severity) { - case 'critical': - return { - bgColor: 'bg-red-50', - borderColor: 'border-red-200', - iconColor: 'text-red-600', - textColor: 'text-red-900', - actionColor: 'bg-red-100 hover:bg-red-200 text-red-800' - }; - case 'high': - return { - bgColor: 'bg-orange-50', - borderColor: 'border-orange-200', - iconColor: 'text-orange-600', - textColor: 'text-orange-900', - actionColor: 'bg-orange-100 hover:bg-orange-200 text-orange-800' - }; - case 'medium': - return { - bgColor: 'bg-yellow-50', - borderColor: 'border-yellow-200', - iconColor: 'text-yellow-600', - textColor: 'text-yellow-900', - actionColor: 'bg-yellow-100 hover:bg-yellow-200 text-yellow-800' - }; - default: - return { - bgColor: 'bg-blue-50', - borderColor: 'border-blue-200', - iconColor: 'text-blue-600', - textColor: 'text-blue-900', - actionColor: 'bg-blue-100 hover:bg-blue-200 text-blue-800' - }; - } -}; - -const getAlertIcon = (type: BusinessAlert['type']) => { - switch (type) { - case 'stockout_risk': - return Package; - case 'overstock': - return TrendingDown; - case 'revenue_loss': - return Euro; - case 'quality_risk': - return Clock; - case 'weather_impact': - return AlertTriangle; - default: - return AlertTriangle; - } -}; - -const getUrgencyLabel = (urgency?: BusinessAlert['urgency']) => { - switch (urgency) { - case 'immediate': - return { label: 'URGENTE', color: 'bg-red-100 text-red-800' }; - case 'today': - return { label: 'HOY', color: 'bg-orange-100 text-orange-800' }; - case 'this_week': - return { label: 'ESTA SEMANA', color: 'bg-blue-100 text-blue-800' }; - default: - return null; - } -}; - -const AlertCard: React.FC = ({ alert, onAction }) => { - const config = getSeverityConfig(alert.severity); - const Icon = getAlertIcon(alert.type); - const urgencyInfo = getUrgencyLabel(alert.urgency); - - const handleAction = () => { - onAction?.(alert.id, 'primary_action'); - }; - - return ( -
-
-
- -
- -
-
-
-
-

- {alert.product} -

- {urgencyInfo && ( - - {urgencyInfo.label} - - )} -
- -

- {alert.message} -

- - {alert.impact && ( -
- {alert.impact.type === 'revenue' && ( - <>Impacto: -{alert.impact.value}{alert.impact.currency || '€'} - )} - {alert.impact.type === 'units' && ( - <>Unidades afectadas: {alert.impact.value} - )} - {alert.impact.type === 'percentage' && ( - <>Reducción estimada: {alert.impact.value}% - )} -
- )} -
-
- -
- -
-
-
-
- ); -}; - -export default AlertCard; \ No newline at end of file diff --git a/frontend/src/hooks/useRealAlerts.ts b/frontend/src/hooks/useRealAlerts.ts deleted file mode 100644 index 09cbda1d..00000000 --- a/frontend/src/hooks/useRealAlerts.ts +++ /dev/null @@ -1,165 +0,0 @@ -// Real API hook for Critical Alerts using backend forecast alerts -import { useState, useCallback, useEffect } from 'react'; -import { useForecast } from '../api'; -import { useTenantId } from './useTenantId'; - -export interface RealAlert { - id: string; - type: 'stock' | 'weather' | 'order' | 'production' | 'system'; - severity: 'high' | 'medium' | 'low'; - title: string; - description: string; - action?: string; - time: string; -} - -export const useRealAlerts = () => { - const [alerts, setAlerts] = useState([]); - const [isLoading, setIsLoading] = useState(false); - const [error, setError] = useState(null); - - const { tenantId } = useTenantId(); - const { getForecastAlerts, acknowledgeForecastAlert } = useForecast(); - - // Transform backend forecast alerts to frontend alert format - const transformForecastAlert = (alert: any): RealAlert => { - // Map alert types - let type: RealAlert['type'] = 'system'; - if (alert.alert_type?.includes('stock') || alert.alert_type?.includes('demand')) { - type = 'stock'; - } else if (alert.alert_type?.includes('weather')) { - type = 'weather'; - } else if (alert.alert_type?.includes('production')) { - type = 'production'; - } - - // Map severity - let severity: RealAlert['severity'] = 'medium'; - if (alert.severity === 'critical' || alert.severity === 'high') { - severity = 'high'; - } else if (alert.severity === 'low') { - severity = 'low'; - } - - // Generate user-friendly title and description - let title = alert.message; - let description = alert.message; - - if (alert.alert_type?.includes('high_demand')) { - title = 'Alta Demanda Prevista'; - description = `Se prevé alta demanda. ${alert.message}`; - } else if (alert.alert_type?.includes('low_confidence')) { - title = 'Predicción Incierta'; - description = `Baja confianza en predicción. ${alert.message}`; - } else if (alert.alert_type?.includes('stock_risk')) { - title = 'Riesgo de Desabastecimiento'; - description = `Posible falta de stock. ${alert.message}`; - } - - return { - id: alert.id, - type, - severity, - title, - description, - action: 'Ver detalles', - time: new Date(alert.created_at).toLocaleTimeString('es-ES', { - hour: '2-digit', - minute: '2-digit' - }) - }; - }; - - // Load real alerts from backend - const loadAlerts = useCallback(async () => { - if (!tenantId) return; - - setIsLoading(true); - setError(null); - - try { - // Get forecast alerts from backend - const response = await getForecastAlerts(tenantId); - - // Extract alerts array from paginated response - const forecastAlerts = response.alerts || []; - - // Filter only active alerts - const activeAlerts = forecastAlerts.filter(alert => alert.is_active); - - // Transform to frontend format - const transformedAlerts = activeAlerts.map(transformForecastAlert); - - // Sort by severity and time (most recent first) - transformedAlerts.sort((a, b) => { - // First by severity (high > medium > low) - const severityOrder = { high: 3, medium: 2, low: 1 }; - const severityDiff = severityOrder[b.severity] - severityOrder[a.severity]; - if (severityDiff !== 0) return severityDiff; - - // Then by time (most recent first) - return b.time.localeCompare(a.time); - }); - - setAlerts(transformedAlerts.slice(0, 3)); // Show max 3 alerts in dashboard - } catch (error) { - console.error('Error loading alerts:', error); - setError(error instanceof Error ? error.message : 'Failed to load alerts'); - - // Fallback to sample alerts based on common scenarios - setAlerts(generateFallbackAlerts()); - } finally { - setIsLoading(false); - } - }, [tenantId, getForecastAlerts]); - - // Handle alert acknowledgment - const handleAlertAction = useCallback(async (alertId: string) => { - if (!tenantId) return; - - try { - await acknowledgeForecastAlert(tenantId, alertId); - // Remove acknowledged alert from local state - setAlerts(prev => prev.filter(alert => alert.id !== alertId)); - } catch (error) { - console.error('Error acknowledging alert:', error); - } - }, [tenantId, acknowledgeForecastAlert]); - - // Generate fallback alerts when API fails - const generateFallbackAlerts = (): RealAlert[] => { - const now = new Date(); - const timeString = now.toLocaleTimeString('es-ES', { hour: '2-digit', minute: '2-digit' }); - - return [ - { - id: 'fallback-1', - type: 'stock', - severity: 'high', - title: 'Stock Bajo de Croissants', - description: 'Se prevé alta demanda este fin de semana', - action: 'Aumentar producción', - time: timeString - } - ]; - }; - - // Load alerts on mount and when tenant changes - useEffect(() => { - loadAlerts(); - - // Refresh alerts every 5 minutes - const interval = setInterval(loadAlerts, 5 * 60 * 1000); - - return () => clearInterval(interval); - }, [loadAlerts]); - - return { - alerts, - isLoading, - error, - onAlertAction: handleAlertAction, - reload: loadAlerts, - clearError: () => setError(null), - }; -}; \ No newline at end of file diff --git a/frontend/src/pages/dashboard/DashboardPage.tsx b/frontend/src/pages/dashboard/DashboardPage.tsx index 61653494..5c352e4c 100644 --- a/frontend/src/pages/dashboard/DashboardPage.tsx +++ b/frontend/src/pages/dashboard/DashboardPage.tsx @@ -1,11 +1,9 @@ import React from 'react'; import { useDashboard } from '../../hooks/useDashboard'; import { useOrderSuggestions } from '../../hooks/useOrderSuggestions'; -import { useRealAlerts } from '../../hooks/useRealAlerts'; // Import simplified components import TodayRevenue from '../../components/simple/TodayRevenue'; -import CriticalAlerts from '../../components/simple/CriticalAlerts'; import TodayProduction from '../../components/simple/TodayProduction'; import QuickActions from '../../components/simple/QuickActions'; import QuickOverview from '../../components/simple/QuickOverview'; @@ -55,11 +53,6 @@ const DashboardPage: React.FC = ({ error: ordersError }); - // Use real API data for alerts - const { - alerts: realAlerts, - onAlertAction - } = useRealAlerts(); // Transform forecast data for production component @@ -163,11 +156,6 @@ const DashboardPage: React.FC = ({ dailyTarget={350} /> - {/* Alerts - Real API Data */} - {/* Quick Actions - Easy Access */} = ({ {/* Success Message - When Everything is Good */} - {realAlerts.length === 0 && ( -
-
🎉
-

¡Todo bajo control!

-

- No hay alertas activas. Tu panadería está funcionando perfectamente. -

-
- )} +
+
🎉
+

¡Todo bajo control!

+

+ Tu panadería está funcionando perfectamente. +

+
); }; diff --git a/frontend/src/pages/inventory/InventoryPage.tsx b/frontend/src/pages/inventory/InventoryPage.tsx index 20814b88..2c905eec 100644 --- a/frontend/src/pages/inventory/InventoryPage.tsx +++ b/frontend/src/pages/inventory/InventoryPage.tsx @@ -28,7 +28,6 @@ import { } from '../../api/services/inventory.service'; import InventoryItemCard from '../../components/inventory/InventoryItemCard'; -import StockAlertsPanel from '../../components/inventory/StockAlertsPanel'; type ViewMode = 'grid' | 'list'; @@ -51,7 +50,6 @@ const InventoryPage: React.FC = ({ view = 'stock-levels' }) const { items, stockLevels, - alerts, dashboardData, isLoading, error, @@ -61,14 +59,12 @@ const InventoryPage: React.FC = ({ view = 'stock-levels' }) updateItem, deleteItem, adjustStock, - acknowledgeAlert, refresh, clearError } = useInventory(); // Local state const [viewMode, setViewMode] = useState('grid'); - const [showAlerts, setShowAlerts] = useState(false); const [filters, setFilters] = useState({ search: '', sort_by: 'name', @@ -155,24 +151,12 @@ const InventoryPage: React.FC = ({ view = 'stock-levels' }) } }; - // Handle alert acknowledgment - const handleAcknowledgeAlert = async (alertId: string) => { - await acknowledgeAlert(alertId); - }; - - // Handle bulk acknowledge alerts - const handleBulkAcknowledgeAlerts = async (alertIds: string[]) => { - // TODO: Implement bulk acknowledge - for (const alertId of alertIds) { - await acknowledgeAlert(alertId); - } - }; // Get quick stats const getQuickStats = () => { const totalItems = items.length; - const lowStockItems = alerts.filter(a => a.alert_type === 'low_stock' && !a.is_acknowledged).length; - const expiringItems = alerts.filter(a => a.alert_type === 'expiring_soon' && !a.is_acknowledged).length; + const lowStockItems = 0; + const expiringItems = 0; const totalValue = dashboardData?.total_value || 0; return { totalItems, lowStockItems, expiringItems, totalValue }; @@ -195,19 +179,6 @@ const InventoryPage: React.FC = ({ view = 'stock-levels' })
-