From ee73b85100abae1c3dffcb07335731ae346b9a33 Mon Sep 17 00:00:00 2001 From: Christian Vidal Wolf Date: Fri, 10 Apr 2026 12:03:12 +0200 Subject: [PATCH] Fix save changes looping issue and authorize all Supabase API calls --- src/App.tsx | 61 +++++++++++++++++++++++----------- src/components/HistoryView.tsx | 5 +-- src/lib/supabase.ts | 25 +++++++------- 3 files changed, 57 insertions(+), 34 deletions(-) diff --git a/src/App.tsx b/src/App.tsx index ab2d2ee..a8e44bd 100644 --- a/src/App.tsx +++ b/src/App.tsx @@ -111,7 +111,7 @@ export default function App() { } console.log('Fetching synced data from Supabase...'); - const syncedData = await getAllSyncedRows(); + const syncedData = await getAllSyncedRows(session?.access_token); const articleNoIdx = COLUMNS.ARTICLE_NO; const processedRows = rows.map(row => { @@ -267,7 +267,7 @@ export default function App() { // 3. Post-export: Reset pending statuses in Supabase console.log('Resetting pending statuses in Supabase...'); - resetAllPendingRows().then(success => { + resetAllPendingRows(session?.access_token).then(success => { if (success) { console.log('Successfully reset all pending statuses'); setRowStatuses({}); // Clear local statuses @@ -319,27 +319,47 @@ export default function App() { console.log('[handleSaveAll] No entries to save, returning'); return; } + setIsSavingAll(true); - let allSuccess = true; - for (const [articleNo, { newData, originalData, articleName }] of entries) { - console.log('[handleSaveAll] Saving article:', articleNo); - const success = await saveRowToSupabase(articleNo, newData); - console.log('[handleSaveAll] Save result for', articleNo, ':', success); - if (success) { - // Also save to history - await saveHistoryEntry(articleNo, articleName, originalData, newData, session?.user?.email || 'unknown'); + let failedArticles: string[] = []; + const token = session?.access_token; + + try { + for (const [articleNo, { newData, originalData, articleName }] of entries) { + console.log('[handleSaveAll] Saving article:', articleNo); + const success = await saveRowToSupabase(articleNo, newData, token); + console.log('[handleSaveAll] Save result for', articleNo, ':', success); - setRowStatuses(prev => ({ ...prev, [articleNo]: 'saved' })); - setPendingRows(prev => { const n = { ...prev }; delete n[articleNo]; return n; }); - } else { - setRowStatuses(prev => ({ ...prev, [articleNo]: 'error' })); - allSuccess = false; + if (success) { + // Also save to history + await saveHistoryEntry(articleNo, articleName, originalData, newData, session?.user?.email || 'unknown', token); + + setRowStatuses(prev => ({ ...prev, [articleNo]: 'saved' })); + setPendingRows(prev => { + const n = { ...prev }; + delete n[articleNo]; + return n; + }); + } else { + setRowStatuses(prev => ({ ...prev, [articleNo]: 'error' })); + failedArticles.push(articleNo); + } } + + console.log('[handleSaveAll] Finished loop. Failed:', failedArticles.length); + + if (failedArticles.length > 0) { + alert(`Failed to save ${failedArticles.length} items: ${failedArticles.join(', ')}. Please try again.`); + } else { + setAppState(prev => ({ ...prev, hasUnsavedChanges: false })); + } + } catch (err) { + console.error('[handleSaveAll] Critical error:', err); + alert('A critical error occurred while saving. Please check your connection and try again.'); + } finally { + setIsSavingAll(false); + console.log('[handleSaveAll] isSavingAll set to false'); } - console.log('[handleSaveAll] Finished, allSuccess:', allSuccess); - if (allSuccess) setAppState(prev => ({ ...prev, hasUnsavedChanges: false })); - setIsSavingAll(false); - console.log('[handleSaveAll] isSavingAll set to false'); }; const captureState = (message: string) => { @@ -506,6 +526,7 @@ export default function App() { { // Find the row in appState.data and update it const rowIndex = appState.data.findIndex(r => String(r[COLUMNS.ARTICLE_NO]) === articleNo); @@ -528,7 +549,7 @@ export default function App() { })); // Delete the history entry after revert if (historyId) { - await deleteHistoryEntry(String(historyId)); + await deleteHistoryEntry(String(historyId), session?.access_token); } } }} diff --git a/src/components/HistoryView.tsx b/src/components/HistoryView.tsx index 559f026..24f10e8 100644 --- a/src/components/HistoryView.tsx +++ b/src/components/HistoryView.tsx @@ -8,9 +8,10 @@ interface HistoryViewProps { headers: string[]; data: ExcelRow[]; onRevert: (articleNo: string, oldData: ExcelRow, historyId?: number) => void; + sessionToken?: string; } -export function HistoryView({ headers, data, onRevert }: HistoryViewProps) { +export function HistoryView({ headers, data, onRevert, sessionToken }: HistoryViewProps) { const [history, setHistory] = useState([]); const [loading, setLoading] = useState(true); const [expandedId, setExpandedId] = useState(null); @@ -22,7 +23,7 @@ export function HistoryView({ headers, data, onRevert }: HistoryViewProps) { const loadHistory = async () => { setLoading(true); - const data = await getHistory(); + const data = await getHistory(sessionToken); setHistory(data); setLoading(false); }; diff --git a/src/lib/supabase.ts b/src/lib/supabase.ts index 831f14b..82c0339 100644 --- a/src/lib/supabase.ts +++ b/src/lib/supabase.ts @@ -8,14 +8,14 @@ export interface SyncedRow { status?: 'pending' | 'synced'; } -export async function getAllSyncedRows(): Promise> { +export async function getAllSyncedRows(token?: string): Promise> { 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}` + 'Authorization': `Bearer ${token || SUPABASE_KEY}` } } ); @@ -33,7 +33,7 @@ export async function getAllSyncedRows(): Promise> { } } -export async function saveRowToSupabase(articleNo: string, rowData: ExcelRow): Promise { +export async function saveRowToSupabase(articleNo: string, rowData: ExcelRow, token?: string): Promise { try { const response = await fetch( `${SUPABASE_URL}/rest/v1/products`, @@ -42,7 +42,7 @@ export async function saveRowToSupabase(articleNo: string, rowData: ExcelRow): P headers: { 'Content-Type': 'application/json', 'apikey': SUPABASE_KEY, - 'Authorization': `Bearer ${SUPABASE_KEY}`, + 'Authorization': `Bearer ${token || SUPABASE_KEY}`, 'Prefer': 'resolution=merge-duplicates' }, body: JSON.stringify({ @@ -76,7 +76,8 @@ export async function saveHistoryEntry( articleName: string, oldData: ExcelRow, newData: ExcelRow, - changedBy: string + changedBy: string, + token?: string ): Promise { try { const response = await fetch( @@ -86,7 +87,7 @@ export async function saveHistoryEntry( headers: { 'Content-Type': 'application/json', 'apikey': SUPABASE_KEY, - 'Authorization': `Bearer ${SUPABASE_KEY}`, + 'Authorization': `Bearer ${token || SUPABASE_KEY}`, 'Prefer': 'return=minimal' }, body: JSON.stringify({ @@ -107,14 +108,14 @@ export async function saveHistoryEntry( } } -export async function getHistory(): Promise { +export async function getHistory(token?: string): Promise { try { const response = await fetch( `${SUPABASE_URL}/rest/v1/products_history?select=*&order=changed_at.desc&limit=100`, { headers: { 'apikey': SUPABASE_KEY, - 'Authorization': `Bearer ${SUPABASE_KEY}` + 'Authorization': `Bearer ${token || SUPABASE_KEY}` } } ); @@ -127,7 +128,7 @@ export async function getHistory(): Promise { } } -export async function deleteHistoryEntry(id: string): Promise { +export async function deleteHistoryEntry(id: string, token?: string): Promise { try { const response = await fetch( `${SUPABASE_URL}/rest/v1/products_history?id=eq.${encodeURIComponent(id)}`, @@ -135,7 +136,7 @@ export async function deleteHistoryEntry(id: string): Promise { method: 'DELETE', headers: { 'apikey': SUPABASE_KEY, - 'Authorization': `Bearer ${SUPABASE_KEY}` + 'Authorization': `Bearer ${token || SUPABASE_KEY}` } } ); @@ -147,7 +148,7 @@ export async function deleteHistoryEntry(id: string): Promise { } } -export async function resetAllPendingRows(): Promise { +export async function resetAllPendingRows(token?: string): Promise { try { const response = await fetch( `${SUPABASE_URL}/rest/v1/products?status=eq.pending`, @@ -156,7 +157,7 @@ export async function resetAllPendingRows(): Promise { headers: { 'Content-Type': 'application/json', 'apikey': SUPABASE_KEY, - 'Authorization': `Bearer ${SUPABASE_KEY}`, + 'Authorization': `Bearer ${token || SUPABASE_KEY}`, 'Prefer': 'return=minimal' }, body: JSON.stringify({ status: 'synced' })