mirror of
https://github.com/christianvidalwolf-prog/CrazeAnalytix.git
synced 2026-08-03 13:35:24 +02:00
feat: automatic ads data loading and attributed sales line in chart
This commit is contained in:
@@ -352,9 +352,11 @@ export const processAdsCSV = (file: File): Promise<AdsRecord[]> => {
|
||||
});
|
||||
};
|
||||
|
||||
export const processAdsExcel = async (file: File): Promise<AdsRecord[]> => {
|
||||
export const processAdsExcel = async (fileOrBuffer: File | ArrayBuffer): Promise<AdsRecord[]> => {
|
||||
try {
|
||||
const arrayBuffer = await file.arrayBuffer();
|
||||
const arrayBuffer = fileOrBuffer instanceof File
|
||||
? await fileOrBuffer.arrayBuffer()
|
||||
: fileOrBuffer;
|
||||
const workbook = XLSX.read(arrayBuffer);
|
||||
const allData: AdsRecord[] = [];
|
||||
|
||||
@@ -542,6 +544,29 @@ export const mergeSalesAndAdsData = (salesData: SalesRecord[], adsData: AdsRecor
|
||||
|
||||
// --- EXISTING HELPERS ---
|
||||
|
||||
// Filter Ads Data by Country, Year, Week, and ASIN
|
||||
export const filterAdsData = (adsData: AdsRecord[], filters: FilterState): AdsRecord[] => {
|
||||
return adsData.filter(ad => {
|
||||
// Country/Customer match (ads use 'country', sales use 'customer')
|
||||
const countryMatch = filters.customer.length === 0 ||
|
||||
filters.customer.some(c => c.toUpperCase() === ad.country.toUpperCase());
|
||||
|
||||
// Year match
|
||||
const yearMatch = filters.year.length === 0 ||
|
||||
filters.year.includes(ad.year.toString());
|
||||
|
||||
// Week match (filters use "W1", "W2" format)
|
||||
const weekStr = `W${ad.week}`;
|
||||
const weekMatch = filters.week.length === 0 || filters.week.includes(weekStr);
|
||||
|
||||
// ASIN match
|
||||
const asinMatch = filters.asin.length === 0 ||
|
||||
filters.asin.some(a => a.toUpperCase() === ad.asin.toUpperCase());
|
||||
|
||||
return countryMatch && yearMatch && weekMatch && asinMatch;
|
||||
});
|
||||
};
|
||||
|
||||
export const filterData = (data: SalesRecord[], filters: FilterState): SalesRecord[] => {
|
||||
return data.filter(item => {
|
||||
// 1. Month Logic: Handle "Apr-23" matching "Apr" filter
|
||||
|
||||
@@ -3,6 +3,7 @@ import { SalesRecord } from '../types';
|
||||
|
||||
const DB_NAME = 'CrazeAnalytixDB';
|
||||
const STORE_NAME = 'salesData';
|
||||
const ADS_STORE_NAME = 'adsData';
|
||||
const DB_VERSION = 1;
|
||||
// Increment this when data processing logic changes (e.g., field mapping changes)
|
||||
// This forces cache invalidation and re-processing of CSV data
|
||||
@@ -17,6 +18,9 @@ const initDB = (): Promise<IDBDatabase> => {
|
||||
if (!db.objectStoreNames.contains(STORE_NAME)) {
|
||||
db.createObjectStore(STORE_NAME);
|
||||
}
|
||||
if (!db.objectStoreNames.contains(ADS_STORE_NAME)) {
|
||||
db.createObjectStore(ADS_STORE_NAME);
|
||||
}
|
||||
};
|
||||
|
||||
request.onsuccess = () => resolve(request.result);
|
||||
@@ -99,3 +103,63 @@ export const clearSalesData = async (): Promise<void> => {
|
||||
console.error(e);
|
||||
}
|
||||
}
|
||||
|
||||
// --- ADS STORAGE ---
|
||||
|
||||
export const saveAdsData = async (data: any[]): Promise<void> => {
|
||||
try {
|
||||
const db = await initDB();
|
||||
return new Promise((resolve, reject) => {
|
||||
const transaction = db.transaction(ADS_STORE_NAME, 'readwrite');
|
||||
const store = transaction.objectStore(ADS_STORE_NAME);
|
||||
|
||||
store.put(data, 'currentData');
|
||||
store.put(new Date().toISOString(), 'lastUpdated');
|
||||
|
||||
transaction.oncomplete = () => resolve();
|
||||
transaction.onerror = () => reject(transaction.error);
|
||||
});
|
||||
} catch (error) {
|
||||
console.error("Error saving ads to IndexedDB:", error);
|
||||
}
|
||||
};
|
||||
|
||||
export const loadAdsData = async (): Promise<{ data: any[]; lastUpdated: string | null }> => {
|
||||
try {
|
||||
const db = await initDB();
|
||||
return new Promise((resolve, reject) => {
|
||||
const transaction = db.transaction(ADS_STORE_NAME, 'readonly');
|
||||
const store = transaction.objectStore(ADS_STORE_NAME);
|
||||
|
||||
const dataReq = store.get('currentData');
|
||||
const dateReq = store.get('lastUpdated');
|
||||
|
||||
transaction.oncomplete = () => {
|
||||
resolve({
|
||||
data: dataReq.result || [],
|
||||
lastUpdated: dateReq.result || null
|
||||
});
|
||||
};
|
||||
|
||||
transaction.onerror = () => reject(transaction.error);
|
||||
});
|
||||
} catch (error) {
|
||||
console.error("Error loading ads from IndexedDB:", error);
|
||||
return { data: [], lastUpdated: null };
|
||||
}
|
||||
};
|
||||
|
||||
export const clearAdsData = async (): Promise<void> => {
|
||||
try {
|
||||
const db = await initDB();
|
||||
return new Promise((resolve, reject) => {
|
||||
const transaction = db.transaction(ADS_STORE_NAME, 'readwrite');
|
||||
const store = transaction.objectStore(ADS_STORE_NAME);
|
||||
store.clear();
|
||||
transaction.oncomplete = () => resolve();
|
||||
transaction.onerror = () => reject(transaction.error);
|
||||
});
|
||||
} catch (e) {
|
||||
console.error(e);
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user