feat: wire MKT tab to real data sources

Replace all mock data in the Profitability Dashboard with live data:
- Add 3 new API routes (fetch-deals, fetch-promos, fetch-chargebacks) proxying Dropbox Excel files
- MktDataView now accepts rawData/adsData props and fetches+parses the 3 new files on mount
- SELL OUT and units aggregated globally across all marketplaces from rawData
- PPC Spend aggregated globally from adsData; ACoS calculated as spend/sales
- Deals (col A→C), Promos (col E→K), Chargebacks (col AN→B) parsed and summed per ASIN
- Category filter populated from product line metadata
- Loading skeleton shown while files are fetching

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
Christian Vidal Wolf
2026-03-11 12:34:07 +01:00
co-authored by Claude Sonnet 4.6
parent 5d0edfba5f
commit 49498c5c51
5 changed files with 364 additions and 23 deletions
+37
View File
@@ -0,0 +1,37 @@
import type { VercelRequest, VercelResponse } from '@vercel/node';
const CHARGEBACKS_DROPBOX_URL = "https://www.dropbox.com/scl/fi/m2qr2cxhtgkc48acjrrgx/Amazon-Operational-Chargebacks-2025.xlsx?rlkey=vlrz4qfydsjtikgh0ylivocjq&st=zdhmjvaz&dl=1";
export default async function handler(req: VercelRequest, res: VercelResponse) {
res.setHeader('Access-Control-Allow-Origin', '*');
res.setHeader('Access-Control-Allow-Methods', 'GET, OPTIONS');
res.setHeader('Access-Control-Allow-Headers', 'Content-Type');
if (req.method === 'OPTIONS') {
return res.status(200).end();
}
try {
console.log('[fetch-chargebacks] Fetching Chargebacks from Dropbox...');
const response = await fetch(CHARGEBACKS_DROPBOX_URL, {
cache: 'no-store',
headers: {
'Pragma': 'no-cache',
'Cache-Control': 'no-cache'
}
});
if (!response.ok) {
throw new Error(`Dropbox responded with ${response.status}`);
}
const buffer = await response.arrayBuffer();
console.log('[fetch-chargebacks] Successfully fetched Chargebacks Excel, size:', buffer.byteLength);
res.setHeader('Content-Type', 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet');
res.status(200).send(Buffer.from(buffer));
} catch (error: any) {
console.error('[fetch-chargebacks] Error:', error);
res.status(500).json({ error: error.message });
}
}
+37
View File
@@ -0,0 +1,37 @@
import type { VercelRequest, VercelResponse } from '@vercel/node';
const DEALS_DROPBOX_URL = "https://www.dropbox.com/scl/fi/3mm5m4e04myhqt59piefs/Deals-Amazon-2025.xlsx?rlkey=6llr0xhs6di0c3d8o96ashzmv&st=ggu0z7hu&dl=1";
export default async function handler(req: VercelRequest, res: VercelResponse) {
res.setHeader('Access-Control-Allow-Origin', '*');
res.setHeader('Access-Control-Allow-Methods', 'GET, OPTIONS');
res.setHeader('Access-Control-Allow-Headers', 'Content-Type');
if (req.method === 'OPTIONS') {
return res.status(200).end();
}
try {
console.log('[fetch-deals] Fetching Deals from Dropbox...');
const response = await fetch(DEALS_DROPBOX_URL, {
cache: 'no-store',
headers: {
'Pragma': 'no-cache',
'Cache-Control': 'no-cache'
}
});
if (!response.ok) {
throw new Error(`Dropbox responded with ${response.status}`);
}
const buffer = await response.arrayBuffer();
console.log('[fetch-deals] Successfully fetched Deals Excel, size:', buffer.byteLength);
res.setHeader('Content-Type', 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet');
res.status(200).send(Buffer.from(buffer));
} catch (error: any) {
console.error('[fetch-deals] Error:', error);
res.status(500).json({ error: error.message });
}
}
+37
View File
@@ -0,0 +1,37 @@
import type { VercelRequest, VercelResponse } from '@vercel/node';
const PROMOS_DROPBOX_URL = "https://www.dropbox.com/scl/fi/ufhej9do9w839oyyfrpk3/Promos_Amazon_2025_all-countries.xlsx?rlkey=4foano9dt5h3sl58l35vqg89d&st=wu0t56wh&dl=1";
export default async function handler(req: VercelRequest, res: VercelResponse) {
res.setHeader('Access-Control-Allow-Origin', '*');
res.setHeader('Access-Control-Allow-Methods', 'GET, OPTIONS');
res.setHeader('Access-Control-Allow-Headers', 'Content-Type');
if (req.method === 'OPTIONS') {
return res.status(200).end();
}
try {
console.log('[fetch-promos] Fetching Promos from Dropbox...');
const response = await fetch(PROMOS_DROPBOX_URL, {
cache: 'no-store',
headers: {
'Pragma': 'no-cache',
'Cache-Control': 'no-cache'
}
});
if (!response.ok) {
throw new Error(`Dropbox responded with ${response.status}`);
}
const buffer = await response.arrayBuffer();
console.log('[fetch-promos] Successfully fetched Promos Excel, size:', buffer.byteLength);
res.setHeader('Content-Type', 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet');
res.status(200).send(Buffer.from(buffer));
} catch (error: any) {
console.error('[fetch-promos] Error:', error);
res.status(500).json({ error: error.message });
}
}