feat: Add i18n support for AI insights with structured reasoning
Complete i18n implementation for internal service reasoning: - Update AIInsight interface to include reasoning_data field - Integrate useReasoningTranslation hook in AI Insights page - Add translation keys for safety stock, price forecaster, and optimization Translation coverage (EN/ES/EU): - Safety Stock: statistical z-score, advanced variability, fixed percentage, errors - Price Forecaster: price change predictions, volatility alerts, buying recommendations - Optimization: EOQ calculations, MOQ/max constraints, tier pricing Benefits: - AI insights now display in user's preferred language - Consistent with PO/Batch reasoning translation pattern - Structured parameters enable rich, contextualized translations - Falls back gracefully to description field if translation missing Implementation: - frontend/src/api/services/aiInsights.ts: Add reasoning_data to interface - frontend/src/pages/app/analytics/ai-insights/AIInsightsPage.tsx: Translate insights - frontend/src/locales/*/reasoning.json: Add safetyStock, priceForecaster, optimization keys This completes the full i18n implementation for the bakery AI system.
This commit is contained in:
@@ -6,12 +6,14 @@ import { useCurrentTenant } from '../../../../stores/tenant.store';
|
||||
import { useAuthUser } from '../../../../stores/auth.store';
|
||||
import { useAIInsights, useAIInsightStats, useApplyInsight, useDismissInsight } from '../../../../api/hooks/aiInsights';
|
||||
import { AIInsight } from '../../../../api/services/aiInsights';
|
||||
import { useReasoningTranslation } from '../../../../hooks/useReasoningTranslation';
|
||||
|
||||
const AIInsightsPage: React.FC = () => {
|
||||
const [selectedCategory, setSelectedCategory] = useState('all');
|
||||
const currentTenant = useCurrentTenant();
|
||||
const user = useAuthUser();
|
||||
const tenantId = currentTenant?.id || user?.tenant_id;
|
||||
const { t } = useReasoningTranslation();
|
||||
|
||||
// Fetch real insights from API
|
||||
const { data: insightsData, isLoading, refetch } = useAIInsights(
|
||||
@@ -118,6 +120,32 @@ const AIInsightsPage: React.FC = () => {
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Get translated description for an insight
|
||||
* Uses reasoning_data if available, otherwise falls back to description field
|
||||
*/
|
||||
const getInsightDescription = (insight: AIInsight): string => {
|
||||
if (insight.reasoning_data?.type) {
|
||||
// Determine the category prefix based on source
|
||||
let category = 'aiInsights';
|
||||
if (insight.source_model === 'safety_stock_calculator') {
|
||||
category = 'safetyStock';
|
||||
} else if (insight.source_model === 'price_forecaster') {
|
||||
category = 'priceForecaster';
|
||||
} else if (insight.source_model === 'optimization') {
|
||||
category = 'optimization';
|
||||
}
|
||||
|
||||
try {
|
||||
return t(`${category}.${insight.reasoning_data.type}`, insight.reasoning_data.parameters);
|
||||
} catch (e) {
|
||||
// Fall back to description if translation key not found
|
||||
return insight.description;
|
||||
}
|
||||
}
|
||||
return insight.description;
|
||||
};
|
||||
|
||||
return (
|
||||
<AnalyticsPageLayout
|
||||
title="Inteligencia Artificial"
|
||||
@@ -224,7 +252,7 @@ const AIInsightsPage: React.FC = () => {
|
||||
</div>
|
||||
|
||||
<h3 className="text-lg font-semibold text-[var(--text-primary)] mb-2">{insight.title}</h3>
|
||||
<p className="text-[var(--text-secondary)] mb-3">{insight.description}</p>
|
||||
<p className="text-[var(--text-secondary)] mb-3">{getInsightDescription(insight)}</p>
|
||||
<p className="text-sm font-medium text-[var(--color-success)] mb-4">{insight.impact}</p>
|
||||
|
||||
{/* Metrics */}
|
||||
|
||||
Reference in New Issue
Block a user