Feature: Added Excel export to Ads and Forecast views

This commit is contained in:
Christian Vidal Wolf
2026-01-27 15:34:06 +01:00
parent 19fbd12e77
commit 7178b57073
2 changed files with 103 additions and 5 deletions
+52 -2
View File
@@ -1,6 +1,7 @@
import React, { useMemo, useState, useEffect } from 'react';
import React, { useMemo, useState, useEffect, useCallback } from 'react';
import * as XLSX from 'xlsx';
import { ProductForecastData, FilterState } from '../types';
import { DownloadIcon } from './Icons';
import {
BarChart, Bar, XAxis, YAxis, CartesianGrid, Tooltip, ResponsiveContainer,
LineChart, Line, Legend, ComposedChart, Area
@@ -168,6 +169,47 @@ const ForecastView: React.FC<ForecastViewProps> = ({ data, filters, top50Ranking
return result;
}, [filteredProducts, showOnlyTop50, top50Mode, top50Ranking]);
const handleExportExcel = useCallback(() => {
const exportData = sortedProducts.map(p => {
const rowData: any = {
ASIN: p.asin,
SKU: p.sku,
Title: p.title,
'Product Line': p.line,
'Annual Forecast': Math.round(p.annualForecast),
'Annual Actual': Math.round(p.actualUnits),
'Annual Fulfillment (%)': p.annualForecast > 0 ? Number(((p.actualUnits / p.annualForecast) * 100).toFixed(1)) : 0,
'Period Forecast': 0,
'Period Actual': 0,
};
p.monthlyData.forEach(md => {
if (activeMonths.includes(md.month)) {
rowData['Period Forecast'] += md.forecastUnits;
rowData['Period Actual'] += md.actualUnits;
}
});
rowData['Period Fulfillment (%)'] = rowData['Period Forecast'] > 0
? Number(((rowData['Period Actual'] / rowData['Period Forecast']) * 100).toFixed(1))
: 0;
// Add monthly details
MONTH_ORDER.forEach(m => {
const md = p.monthlyData.find(d => d.month === m);
rowData[`${m} FC`] = md ? Math.round(md.forecastUnits) : 0;
rowData[`${m} ACT`] = md ? Math.round(md.actualUnits) : 0;
});
return rowData;
});
const ws = XLSX.utils.json_to_sheet(exportData);
const wb = XLSX.utils.book_new();
XLSX.utils.book_append_sheet(wb, ws, 'Forecast Overview');
XLSX.writeFile(wb, `Forecast_Export_${new Date().toISOString().slice(0, 10)}.xlsx`);
}, [sortedProducts, activeMonths]);
return (
<div className="p-6 space-y-6 max-w-7xl mx-auto animate-fade-in pb-24">
@@ -299,6 +341,14 @@ const ForecastView: React.FC<ForecastViewProps> = ({ data, filters, top50Ranking
/>
<svg className="absolute left-3 top-2.5 w-4 h-4 text-slate-500" fill="none" viewBox="0 0 24 24" stroke="currentColor"><path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M21 21l-6-6m2-5a7 7 0 11-14 0 7 7 0 0114 0z" /></svg>
</div>
<button
onClick={handleExportExcel}
className="flex items-center gap-2 px-3 py-1.5 bg-slate-800 hover:bg-slate-700 text-slate-200 rounded-lg text-xs font-bold border border-white/10 transition-all shadow-lg active:scale-95"
title="Export to Excel"
>
<DownloadIcon />
<span>Export</span>
</button>
</div>
</div>
<div className="overflow-x-auto">