Fix new services implementation 2

This commit is contained in:
Urtzi Alfaro
2025-08-14 13:26:59 +02:00
parent 262b3dc9c4
commit 0951547e92
39 changed files with 1203 additions and 917 deletions

View File

@@ -16,7 +16,9 @@ import {
CheckCircle2,
XCircle,
ArrowRight,
Lightbulb
Lightbulb,
Building2,
Truck
} from 'lucide-react';
import toast from 'react-hot-toast';
@@ -236,6 +238,43 @@ const SmartHistoricalDataImport: React.FC<SmartHistoricalDataImportProps> = ({
const renderBusinessModelInsight = (analysis: BusinessModelAnalysis) => {
const modelConfig = {
individual_bakery: {
icon: Factory,
title: 'Panadería Individual',
description: 'Producción completa desde ingredientes básicos (harina, levadura, etc.)',
color: 'blue',
bgColor: 'bg-blue-50',
borderColor: 'border-blue-200',
textColor: 'text-blue-900'
},
central_baker_satellite: {
icon: Truck,
title: 'Punto de Venta - Obrador Central',
description: 'Recibe productos semi-elaborados y los finaliza (horneado, decoración)',
color: 'amber',
bgColor: 'bg-amber-50',
borderColor: 'border-amber-200',
textColor: 'text-amber-900'
},
retail_bakery: {
icon: Store,
title: 'Panadería de Distribución',
description: 'Vende productos terminados de proveedores externos',
color: 'green',
bgColor: 'bg-green-50',
borderColor: 'border-green-200',
textColor: 'text-green-900'
},
hybrid_bakery: {
icon: Settings2,
title: 'Modelo Mixto',
description: 'Combina producción propia con productos de proveedores',
color: 'purple',
bgColor: 'bg-purple-50',
borderColor: 'border-purple-200',
textColor: 'text-purple-900'
},
// Legacy fallbacks
production: {
icon: Factory,
title: 'Panadería de Producción',
@@ -265,7 +304,8 @@ const SmartHistoricalDataImport: React.FC<SmartHistoricalDataImportProps> = ({
}
};
const config = modelConfig[analysis.model];
// Provide fallback if analysis.model is not in modelConfig
const config = modelConfig[analysis.model as keyof typeof modelConfig] || modelConfig.hybrid_bakery;
const IconComponent = config.icon;
return (

View File

@@ -43,8 +43,8 @@ const SalesAnalyticsDashboard: React.FC = () => {
} = useSales();
const {
ingredients: products,
loadIngredients: loadProducts,
items: products,
loadItems: loadProducts,
isLoading: inventoryLoading
} = useInventory();
@@ -159,11 +159,14 @@ const SalesAnalyticsDashboard: React.FC = () => {
// Top products
const topProducts = Object.entries(productPerformance)
.map(([productId, data]) => ({
productId,
...data as any,
avgPrice: data.revenue / data.units
}))
.map(([productId, data]) => {
const perf = data as { revenue: number; units: number; orders: number };
return {
productId,
...perf,
avgPrice: perf.revenue / perf.units
};
})
.sort((a, b) => b.revenue - a.revenue)
.slice(0, 5);

View File

@@ -160,7 +160,7 @@ const SalesDashboardWidget: React.FC<SalesDashboardWidgetProps> = ({
<div className="flex items-center justify-between mb-3">
<h3 className="font-semibold text-gray-900">Ventas de Hoy</h3>
{onViewAll && (
<Button variant="ghost" size="sm" onClick={onViewAll}>
<Button variant="outline" size="sm" onClick={onViewAll}>
<Eye className="w-4 h-4" />
</Button>
)}

View File

@@ -10,7 +10,7 @@ import {
Package,
ShoppingCart,
MapPin,
Grid3X3,
Grid,
List,
AlertCircle,
TrendingUp,
@@ -51,8 +51,8 @@ const SalesManagementPage: React.FC = () => {
} = useSales();
const {
ingredients: products,
loadIngredients: loadProducts,
items: products,
loadItems: loadProducts,
isLoading: inventoryLoading
} = useInventory();
@@ -89,7 +89,9 @@ const SalesManagementPage: React.FC = () => {
const loadSalesData = async () => {
if (!user?.tenant_id) return;
const query: SalesDataQuery = {};
const query: SalesDataQuery = {
tenant_id: user.tenant_id
};
if (filters.search) {
query.search_term = filters.search;
@@ -155,7 +157,9 @@ const SalesManagementPage: React.FC = () => {
const handleExport = async () => {
if (!user?.tenant_id) return;
const query: SalesDataQuery = {};
const query: SalesDataQuery = {
tenant_id: user.tenant_id
};
if (filters.date_from) query.start_date = filters.date_from;
if (filters.date_to) query.end_date = filters.date_to;
if (filters.channel) query.sales_channel = filters.channel;
@@ -386,7 +390,7 @@ const SalesManagementPage: React.FC = () => {
onClick={() => setViewMode('grid')}
className={`p-2 rounded ${viewMode === 'grid' ? 'bg-blue-100 text-blue-600' : 'text-gray-400 hover:text-gray-600'}`}
>
<Grid3X3 className="w-4 h-4" />
<Grid className="w-4 h-4" />
</button>
<button
onClick={() => setViewMode('list')}

View File

@@ -11,7 +11,7 @@ import {
Clock,
Star,
ArrowRight,
LightBulb,
Lightbulb,
Calendar,
Package
} from 'lucide-react';
@@ -53,10 +53,10 @@ const SalesPerformanceInsights: React.FC<SalesPerformanceInsightsProps> = ({
} = useSales();
const {
predictions,
loadPredictions,
performance,
loadPerformance,
forecasts,
getForecasts,
quickForecasts,
getQuickForecasts,
isLoading: forecastLoading
} = useForecast();
@@ -87,8 +87,8 @@ const SalesPerformanceInsights: React.FC<SalesPerformanceInsightsProps> = ({
end_date: endDate,
limit: 1000
}),
loadPredictions(),
loadPerformance()
getForecasts(user.tenant_id),
getQuickForecasts(user.tenant_id)
]);
setSalesAnalytics(analytics);
@@ -200,9 +200,9 @@ const SalesPerformanceInsights: React.FC<SalesPerformanceInsightsProps> = ({
}
// Forecasting insights
if (predictions.length > 0) {
const todayPrediction = predictions.find(p => {
const predDate = new Date(p.date).toDateString();
if (forecasts.length > 0) {
const todayPrediction = forecasts.find(p => {
const predDate = new Date(p.forecast_date).toDateString();
const today = new Date().toDateString();
return predDate === today;
});
@@ -213,8 +213,8 @@ const SalesPerformanceInsights: React.FC<SalesPerformanceInsightsProps> = ({
type: 'forecast',
title: 'Predicción para hoy',
description: `La IA predice ${todayPrediction.predicted_demand} unidades de demanda con ${
todayPrediction.confidence === 'high' ? 'alta' :
todayPrediction.confidence === 'medium' ? 'media' : 'baja'
(todayPrediction.confidence_level || 0) > 0.8 ? 'alta' :
(todayPrediction.confidence_level || 0) > 0.6 ? 'media' : 'baja'
} confianza.`,
value: `${todayPrediction.predicted_demand} unidades`,
priority: 'high',
@@ -227,8 +227,9 @@ const SalesPerformanceInsights: React.FC<SalesPerformanceInsightsProps> = ({
}
// Performance vs forecast insight
if (performance) {
const accuracy = performance.accuracy || 0;
if (quickForecasts.length > 0) {
const latestForecast = quickForecasts[0];
const accuracy = latestForecast.confidence_score || 0;
if (accuracy > 85) {
insights.push({
id: 'forecast_accuracy',
@@ -288,7 +289,7 @@ const SalesPerformanceInsights: React.FC<SalesPerformanceInsightsProps> = ({
// Sort by priority
const priorityOrder = { high: 3, medium: 2, low: 1 };
return insights.sort((a, b) => priorityOrder[b.priority] - priorityOrder[a.priority]);
}, [salesAnalytics, salesData, predictions, performance, onActionClick]);
}, [salesAnalytics, salesData, forecasts, quickForecasts, onActionClick]);
// Get insight icon
const getInsightIcon = (type: PerformanceInsight['type']) => {
@@ -301,7 +302,7 @@ const SalesPerformanceInsights: React.FC<SalesPerformanceInsightsProps> = ({
return Brain;
case 'info':
default:
return LightBulb;
return Lightbulb;
}
};

View File

@@ -11,7 +11,7 @@ import {
CheckCircle,
Calendar,
TrendingUp,
Grid3X3,
Grid,
List,
Download,
MapPin,
@@ -486,7 +486,7 @@ const DeliveryTrackingPage: React.FC = () => {
onClick={() => setViewMode('grid')}
className={`p-2 rounded ${viewMode === 'grid' ? 'bg-blue-100 text-blue-600' : 'text-gray-400 hover:text-gray-600'}`}
>
<Grid3X3 className="w-4 h-4" />
<Grid className="w-4 h-4" />
</button>
<button
onClick={() => setViewMode('list')}

View File

@@ -100,7 +100,7 @@ const PurchaseOrderForm: React.FC<PurchaseOrderFormProps> = ({
const [activeTab, setActiveTab] = useState<'basic' | 'delivery' | 'items' | 'financial'>('basic');
const { activeSuppliers, loadActiveSuppliers } = useSuppliers();
const { ingredients, loadInventoryItems } = useInventory();
const { items: ingredients, loadItems: loadInventoryItems } = useInventory();
// Initialize form data when order changes
useEffect(() => {

View File

@@ -12,7 +12,7 @@ import {
AlertCircle,
Package,
DollarSign,
Grid3X3,
Grid,
List
} from 'lucide-react';
@@ -446,7 +446,7 @@ const PurchaseOrderManagementPage: React.FC = () => {
onClick={() => setViewMode('grid')}
className={`p-2 rounded ${viewMode === 'grid' ? 'bg-blue-100 text-blue-600' : 'text-gray-400 hover:text-gray-600'}`}
>
<Grid3X3 className="w-4 h-4" />
<Grid className="w-4 h-4" />
</button>
<button
onClick={() => setViewMode('list')}
@@ -599,7 +599,7 @@ const PurchaseOrderManagementPage: React.FC = () => {
isOpen={showPurchaseOrderForm}
isCreating={isCreating}
onSubmit={selectedOrder ?
(data) => {
async (data) => {
// Handle update logic here if needed
setShowPurchaseOrderForm(false);
setSelectedOrder(null);

View File

@@ -43,6 +43,8 @@ interface SupplierCostData extends SupplierSummary {
market_share_percentage: number;
cost_trend: 'increasing' | 'decreasing' | 'stable';
cost_efficiency_score: number;
total_amount: number;
quality_rating: number;
}
const SupplierCostAnalysis: React.FC = () => {

View File

@@ -12,7 +12,7 @@ import {
AlertCircle,
Package,
DollarSign,
Grid3X3,
Grid,
List
} from 'lucide-react';
@@ -422,7 +422,7 @@ const SupplierManagementPage: React.FC = () => {
onClick={() => setViewMode('grid')}
className={`p-2 rounded ${viewMode === 'grid' ? 'bg-blue-100 text-blue-600' : 'text-gray-400 hover:text-gray-600'}`}
>
<Grid3X3 className="w-4 h-4" />
<Grid className="w-4 h-4" />
</button>
<button
onClick={() => setViewMode('list')}

View File

@@ -42,6 +42,10 @@ interface SupplierPerformance extends SupplierSummary {
cost_efficiency: number;
response_time: number;
quality_consistency: number;
total_orders: number;
total_amount: number;
quality_rating: number;
delivery_rating: number;
}
const SupplierPerformanceReport: React.FC = () => {