fix(history): paginate all records, number oldest=1 newest=N, display newest first

This commit is contained in:
Christian Vidal Wolf
2026-05-15 10:51:01 +02:00
parent f66295f07b
commit 2944e6958c
2 changed files with 384 additions and 56 deletions
+69 -25
View File
@@ -116,6 +116,28 @@ export interface HistoryEntry {
changed_at: string;
}
function normalizeHistoryValue(value: any): any {
if (value === undefined || value === null || value === '') return null;
return value;
}
function valuesEqual(a: any, b: any): boolean {
return normalizeHistoryValue(a) === normalizeHistoryValue(b);
}
function getChangedIndices(oldData: ExcelRow = [], newData: ExcelRow = []): number[] {
const maxLen = Math.max(oldData.length, newData.length);
const changed: number[] = [];
for (let i = 0; i < maxLen; i++) {
if (!valuesEqual(oldData[i], newData[i])) {
changed.push(i);
}
}
return changed;
}
export async function saveHistoryEntry(
productId: string,
articleName: string,
@@ -160,25 +182,34 @@ export async function saveHistoryEntry(
export async function getHistory(): Promise<HistoryEntry[]> {
try {
const response = await safeFetch(
`${SUPABASE_URL}/rest/v1/products_history?select=*&order=changed_at.desc&limit=500`,
{ cache: 'no-store' }
);
const PAGE_SIZE = 1000;
const allRows: HistoryEntry[] = [];
for (let page = 0; page < 20; page++) {
const offset = page * PAGE_SIZE;
const response = await safeFetch(
`${SUPABASE_URL}/rest/v1/products_history?select=*&order=changed_at.asc&limit=${PAGE_SIZE}&offset=${offset}`,
{ cache: 'no-store' }
);
if (!response.ok) {
const errText = await response.text();
console.error('[getHistory] Error:', response.status, errText.substring(0, 200));
return [{
id: 'ERROR',
product_id: 'ERROR',
article_name: `Failed: ${response.status} ${errText.substring(0, 200)}`,
old_data: [],
new_data: [],
changed_at: new Date().toISOString(),
changed_by: 'system'
}];
if (!response.ok) {
const errText = await response.text();
console.error('[getHistory] Error:', response.status, errText.substring(0, 200));
return [{
id: 'ERROR',
product_id: 'ERROR',
article_name: `Failed: ${response.status} ${errText.substring(0, 200)}`,
old_data: [],
new_data: [],
changed_at: new Date().toISOString(),
changed_by: 'system'
}];
}
const batch: HistoryEntry[] = await response.json();
allRows.push(...batch);
if (batch.length < PAGE_SIZE) break;
}
return await response.json();
// Return oldest-first so index+1 = natural chronological number
return allRows;
} catch (error: any) {
console.error('[getHistory] Exception:', error.message);
return [{
@@ -196,7 +227,7 @@ export async function getHistory(): Promise<HistoryEntry[]> {
export async function getHistoryDataForMerge(): Promise<Record<string, ExcelRow>> {
try {
const response = await safeFetch(
`${SUPABASE_URL}/rest/v1/products_history?select=product_id,new_data&order=changed_at.desc`,
`${SUPABASE_URL}/rest/v1/products_history?select=product_id,old_data,new_data,changed_at,id`,
{ cache: 'no-store' }
);
@@ -204,17 +235,30 @@ export async function getHistoryDataForMerge(): Promise<Record<string, ExcelRow>
console.error('[getHistoryDataForMerge] Error:', response.status);
return {};
}
const entries: Array<{ product_id: string; new_data: ExcelRow }> = await response.json();
const entries: HistoryEntry[] = await response.json();
entries.sort((a, b) => {
const timeDelta = new Date(a.changed_at).getTime() - new Date(b.changed_at).getTime();
if (timeDelta !== 0) return timeDelta;
return String(a.id || '').localeCompare(String(b.id || ''));
});
const result: Record<string, ExcelRow> = {};
const seen = new Set<string>();
for (const entry of entries) {
if (seen.has(entry.product_id)) continue;
seen.add(entry.product_id);
if (entry.new_data && entry.new_data.length > 0) {
result[entry.product_id] = entry.new_data;
if (!entry.product_id) continue;
if (!result[entry.product_id]) {
result[entry.product_id] = [];
}
const target = result[entry.product_id];
const changedIndices = getChangedIndices(entry.old_data || [], entry.new_data || []);
for (const idx of changedIndices) {
target[idx] = entry.new_data?.[idx];
}
}
return result;
} catch (error: any) {
console.error('[getHistoryDataForMerge] Exception:', error.message);