Files
CrazeAnalytix/services/geminiService.ts
T

96 lines
3.3 KiB
TypeScript
Raw Normal View History

import { AggregatedData } from '../types';
2025-12-11 14:03:33 +01:00
const formatCurrency = (val: number) => `€${val.toLocaleString('de-DE', { minimumFractionDigits: 0, maximumFractionDigits: 0 })}`;
const formatUnits = (val: number) => `${val.toLocaleString('de-DE')} units`;
export const queryGemini = async (
question: string,
context: AggregatedData,
filteredRecordCount: number
): Promise<string> => {
try {
// --- CONTEXT GENERATION ---
// We construct a structured report mirroring the dashboard charts
// 1. Totals by Year (KPI Cards)
const yearlySummary = Object.entries(context.totalsByYear)
.sort((a, b) => parseInt(b[0]) - parseInt(a[0]))
.map(([year, data]) => ` - ${year}: ${formatCurrency((data as any).sellOut)} | ${formatUnits((data as any).units)}`)
.join('\n');
// 2. Seasonality (Line Chart Data)
const seasonalitySummary = context.seasonality.map(p => {
const yearValues = context.availableYears.map(y => `${y}: ${formatCurrency(p[y] as number || 0)}`).join(', ');
return ` - ${p.name}: [${yearValues}]`;
}).join('\n');
2025-12-11 14:03:33 +01:00
// 3. Top Movers (Growth Table) - Limit to Top 10
const growthSummary = context.topMovers.slice(0, 10).map(m =>
` - ${m.line}: +€${m.sellOutGrowthValue.toLocaleString('de-DE')} (${m.sellOutGrowthPercentage.toFixed(1)}%)`
).join('\n');
2025-12-11 14:03:33 +01:00
// 4. Declining Movers (Decline Table) - Limit to Top 10
const declineSummary = context.bottomMovers.slice(0, 10).map(m =>
` - ${m.line}: -€${Math.abs(m.sellOutGrowthValue).toLocaleString('de-DE')} (${m.sellOutGrowthPercentage.toFixed(1)}%)`
).join('\n');
// 5. Product Lines Overview (Bar Charts) - Limit to Top 50
const topLinesSummary = context.byLine.slice(0, 50).map((l, i) =>
` ${i + 1}. ${l.name}: ${formatCurrency(l.value)} | ${formatUnits(l.units)}`
2025-12-11 14:03:33 +01:00
).join('\n');
// 6. Customer Distribution (Customer Chart)
const customerSummary = context.byCustomer.map(c =>
` - ${c.name}: ${formatCurrency(c.value)}`
2025-12-11 14:03:33 +01:00
).join('\n');
const fullReport = `
2025-12-11 14:03:33 +01:00
REPORT CONTEXT:
----------------
GLOBAL TOTALS:
Total Sell Out: ${formatCurrency(context.totalSellOut)}
Total Units: ${formatUnits(context.totalUnits)}
Records Analyzed: ${filteredRecordCount}
Years Available: ${context.availableYears.join(', ')}
2025-12-11 14:03:33 +01:00
YEARLY BREAKDOWN:
${yearlySummary}
2025-12-11 14:03:33 +01:00
MONTHLY SEASONALITY (Revenue Trends):
${seasonalitySummary}
2025-12-11 14:03:33 +01:00
FASTEST GROWING LINES (Year-over-Year):
${growthSummary}
2025-12-11 14:03:33 +01:00
DECLINING LINES (Year-over-Year):
${declineSummary}
2025-12-11 14:03:33 +01:00
TOP PRODUCT LINES (Revenue & Units):
${topLinesSummary}
PERFORMANCE BY CUSTOMER:
${customerSummary}
`;
// Call our serverless API endpoint (key is safe server-side)
const response = await fetch('/api/ask-gemini', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ question, context: fullReport })
});
if (!response.ok) {
const errorData = await response.json().catch(() => ({ error: 'Unknown error' }));
throw new Error(errorData.error || `Server error: ${response.status}`);
}
const data = await response.json();
return data.response || "I couldn't generate a response based on the data provided.";
} catch (error: any) {
console.error("Gemini API Error:", error);
return `Error: ${error.message || "An unexpected error occurred while analyzing the data."}`;
}
};