Fix new services implementation 2
This commit is contained in:
@@ -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 (
|
||||
|
||||
@@ -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);
|
||||
|
||||
|
||||
@@ -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>
|
||||
)}
|
||||
|
||||
@@ -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')}
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
@@ -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')}
|
||||
|
||||
@@ -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(() => {
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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 = () => {
|
||||
|
||||
@@ -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')}
|
||||
|
||||
@@ -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 = () => {
|
||||
|
||||
Reference in New Issue
Block a user