mirror of
https://github.com/christianvidalwolf-prog/Craze-Data-check.git
synced 2026-08-03 13:55:23 +02:00
Delete history entry after revert
This commit is contained in:
+114
-116
@@ -1,149 +1,104 @@
|
||||
import { ExcelRow } from '../types';
|
||||
|
||||
const SUPABASE_URL = 'https://hwithddwaapyhnfwcesj.supabase.co';
|
||||
const SUPABASE_KEY = 'sb_publishable_fGXkh0bSrAOqSk2jWKAzSg_NJD9YPCv';
|
||||
|
||||
async function fetchWithRetry(
|
||||
url: string,
|
||||
options: RequestInit,
|
||||
retries = 2,
|
||||
delayMs = 1000
|
||||
): Promise<Response> {
|
||||
let lastError: Error | null = null;
|
||||
for (let attempt = 0; attempt <= retries; attempt++) {
|
||||
try {
|
||||
const res = await fetch(url, options);
|
||||
if (res.ok || res.status < 500 || attempt === retries) return res;
|
||||
} catch (err) {
|
||||
lastError = err as Error;
|
||||
if (attempt === retries) throw lastError;
|
||||
export interface ExcelRow extends Array<any> {}
|
||||
|
||||
export interface SyncedRow {
|
||||
data: ExcelRow;
|
||||
status?: 'pending' | 'synced';
|
||||
}
|
||||
|
||||
export async function getAllSyncedRows(): Promise<Record<string, SyncedRow>> {
|
||||
try {
|
||||
const response = await fetch(
|
||||
`${SUPABASE_URL}/rest/v1/products?select=product_id,data,status&order=updated_at.desc`,
|
||||
{
|
||||
headers: {
|
||||
'apikey': SUPABASE_KEY,
|
||||
'Authorization': `Bearer ${SUPABASE_KEY}`
|
||||
}
|
||||
}
|
||||
);
|
||||
|
||||
if (!response.ok) return {};
|
||||
const rows = await response.json();
|
||||
const result: Record<string, SyncedRow> = {};
|
||||
for (const row of rows) {
|
||||
result[row.product_id] = { data: row.data, status: row.status };
|
||||
}
|
||||
await new Promise(r => setTimeout(r, delayMs));
|
||||
return result;
|
||||
} catch (error) {
|
||||
console.error('Error fetching synced rows from Supabase:', error);
|
||||
return {};
|
||||
}
|
||||
throw lastError ?? new Error('fetch failed');
|
||||
}
|
||||
|
||||
export async function saveRowToSupabase(articleNo: string, rowData: ExcelRow): Promise<boolean> {
|
||||
try {
|
||||
// Single upsert: POST with Prefer=resolution=merge-duplicates
|
||||
// This handles both INSERT (new article) and UPDATE (existing) atomically.
|
||||
// The old PATCH approach silently failed for new articles because Supabase
|
||||
// returns 200 OK with an empty body when no rows match — indistinguishable
|
||||
// from a successful update.
|
||||
const response = await fetchWithRetry(`${SUPABASE_URL}/rest/v1/products_sync`, {
|
||||
method: 'POST',
|
||||
headers: {
|
||||
'apikey': SUPABASE_KEY,
|
||||
'Authorization': `Bearer ${SUPABASE_KEY}`,
|
||||
'Content-Type': 'application/json',
|
||||
'Prefer': 'resolution=merge-duplicates'
|
||||
},
|
||||
body: JSON.stringify({
|
||||
id: articleNo,
|
||||
data: rowData,
|
||||
status_check: 'pending',
|
||||
updated_at: new Date().toISOString()
|
||||
})
|
||||
});
|
||||
const response = await fetch(
|
||||
`${SUPABASE_URL}/rest/v1/products?product_id=eq.${encodeURIComponent(articleNo)}`,
|
||||
{
|
||||
method: 'PATCH',
|
||||
headers: {
|
||||
'Content-Type': 'application/json',
|
||||
'apikey': SUPABASE_KEY,
|
||||
'Authorization': `Bearer ${SUPABASE_KEY}`,
|
||||
'Prefer': 'return=minimal'
|
||||
},
|
||||
body: JSON.stringify({
|
||||
product_id: articleNo,
|
||||
data: rowData,
|
||||
status: 'synced',
|
||||
updated_at: new Date().toISOString()
|
||||
})
|
||||
}
|
||||
);
|
||||
|
||||
if (!response.ok) {
|
||||
const errorData = await response.json().catch(() => ({}));
|
||||
console.error('Supabase upsert failed:', response.status, errorData);
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
return response.ok;
|
||||
} catch (error) {
|
||||
console.error('Error saving to Supabase:', error);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
export async function getAllSyncedRows(): Promise<Record<string, { data: ExcelRow, status: string }>> {
|
||||
try {
|
||||
// Explicit limit to avoid Supabase's default 1000-row cap
|
||||
const response = await fetch(
|
||||
`${SUPABASE_URL}/rest/v1/products_sync?select=id,data,status_check&limit=10000`,
|
||||
{
|
||||
headers: {
|
||||
'apikey': SUPABASE_KEY,
|
||||
'Authorization': `Bearer ${SUPABASE_KEY}`,
|
||||
'Range': '0-9999'
|
||||
}
|
||||
}
|
||||
);
|
||||
|
||||
if (!response.ok) return {};
|
||||
|
||||
const data = await response.json();
|
||||
const result: Record<string, { data: ExcelRow, status: string }> = {};
|
||||
data.forEach((item: any) => {
|
||||
result[item.id] = {
|
||||
data: item.data,
|
||||
status: item.status_check || 'original'
|
||||
};
|
||||
});
|
||||
return result;
|
||||
} catch (error) {
|
||||
console.error('Error fetching from Supabase:', error);
|
||||
return {};
|
||||
}
|
||||
}
|
||||
|
||||
export async function resetAllPendingRows(): Promise<boolean> {
|
||||
try {
|
||||
const response = await fetch(`${SUPABASE_URL}/rest/v1/products_sync?status_check=eq.pending`, {
|
||||
method: 'PATCH',
|
||||
headers: {
|
||||
'apikey': SUPABASE_KEY,
|
||||
'Authorization': `Bearer ${SUPABASE_KEY}`,
|
||||
'Content-Type': 'application/json'
|
||||
},
|
||||
body: JSON.stringify({
|
||||
status_check: 'original',
|
||||
updated_at: new Date().toISOString()
|
||||
})
|
||||
});
|
||||
return response.ok;
|
||||
} catch (error) {
|
||||
console.error('Error resetting statuses in Supabase:', error);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
export interface HistoryEntry {
|
||||
id: string;
|
||||
id?: number;
|
||||
product_id: string;
|
||||
article_name: string;
|
||||
old_data: ExcelRow;
|
||||
new_data: ExcelRow;
|
||||
changed_at: string;
|
||||
changed_by: string;
|
||||
changed_at: string;
|
||||
}
|
||||
|
||||
export async function saveHistoryEntry(
|
||||
articleNo: string,
|
||||
productId: string,
|
||||
articleName: string,
|
||||
oldData: ExcelRow,
|
||||
newData: ExcelRow,
|
||||
userEmail: string
|
||||
changedBy: string
|
||||
): Promise<boolean> {
|
||||
try {
|
||||
const response = await fetch(`${SUPABASE_URL}/rest/v1/products_history`, {
|
||||
method: 'POST',
|
||||
headers: {
|
||||
'apikey': SUPABASE_KEY,
|
||||
'Authorization': `Bearer ${SUPABASE_KEY}`,
|
||||
'Content-Type': 'application/json'
|
||||
},
|
||||
body: JSON.stringify({
|
||||
product_id: articleNo,
|
||||
article_name: articleName,
|
||||
old_data: oldData,
|
||||
new_data: newData,
|
||||
changed_at: new Date().toISOString(),
|
||||
changed_by: userEmail
|
||||
})
|
||||
});
|
||||
const response = await fetch(
|
||||
`${SUPABASE_URL}/rest/v1/products_history`,
|
||||
{
|
||||
method: 'POST',
|
||||
headers: {
|
||||
'Content-Type': 'application/json',
|
||||
'apikey': SUPABASE_KEY,
|
||||
'Authorization': `Bearer ${SUPABASE_KEY}`,
|
||||
'Prefer': 'return=minimal'
|
||||
},
|
||||
body: JSON.stringify({
|
||||
product_id: productId,
|
||||
article_name: articleName,
|
||||
old_data: oldData,
|
||||
new_data: newData,
|
||||
changed_by: changedBy,
|
||||
changed_at: new Date().toISOString()
|
||||
})
|
||||
}
|
||||
);
|
||||
|
||||
return response.ok;
|
||||
} catch (error) {
|
||||
@@ -171,3 +126,46 @@ export async function getHistory(): Promise<HistoryEntry[]> {
|
||||
return [];
|
||||
}
|
||||
}
|
||||
|
||||
export async function deleteHistoryEntry(id: string): Promise<boolean> {
|
||||
try {
|
||||
const response = await fetch(
|
||||
`${SUPABASE_URL}/rest/v1/products_history?id=eq.${encodeURIComponent(id)}`,
|
||||
{
|
||||
method: 'DELETE',
|
||||
headers: {
|
||||
'apikey': SUPABASE_KEY,
|
||||
'Authorization': `Bearer ${SUPABASE_KEY}`
|
||||
}
|
||||
}
|
||||
);
|
||||
|
||||
return response.ok;
|
||||
} catch (error) {
|
||||
console.error('Error deleting history from Supabase:', error);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
export async function resetAllPendingRows(): Promise<boolean> {
|
||||
try {
|
||||
const response = await fetch(
|
||||
`${SUPABASE_URL}/rest/v1/products?status=eq.pending`,
|
||||
{
|
||||
method: 'PATCH',
|
||||
headers: {
|
||||
'Content-Type': 'application/json',
|
||||
'apikey': SUPABASE_KEY,
|
||||
'Authorization': `Bearer ${SUPABASE_KEY}`,
|
||||
'Prefer': 'return=minimal'
|
||||
},
|
||||
body: JSON.stringify({ status: 'synced' })
|
||||
}
|
||||
);
|
||||
|
||||
return response.ok;
|
||||
} catch (error) {
|
||||
console.error('Error resetting pending rows in Supabase:', error);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user