mirror of
https://github.com/christianvidalwolf-prog/Craze-Data-check.git
synced 2026-08-03 16:35:24 +02:00
Fix save changes looping issue and authorize all Supabase API calls
This commit is contained in:
+42
-21
@@ -111,7 +111,7 @@ export default function App() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
console.log('Fetching synced data from Supabase...');
|
console.log('Fetching synced data from Supabase...');
|
||||||
const syncedData = await getAllSyncedRows();
|
const syncedData = await getAllSyncedRows(session?.access_token);
|
||||||
|
|
||||||
const articleNoIdx = COLUMNS.ARTICLE_NO;
|
const articleNoIdx = COLUMNS.ARTICLE_NO;
|
||||||
const processedRows = rows.map(row => {
|
const processedRows = rows.map(row => {
|
||||||
@@ -267,7 +267,7 @@ export default function App() {
|
|||||||
|
|
||||||
// 3. Post-export: Reset pending statuses in Supabase
|
// 3. Post-export: Reset pending statuses in Supabase
|
||||||
console.log('Resetting pending statuses in Supabase...');
|
console.log('Resetting pending statuses in Supabase...');
|
||||||
resetAllPendingRows().then(success => {
|
resetAllPendingRows(session?.access_token).then(success => {
|
||||||
if (success) {
|
if (success) {
|
||||||
console.log('Successfully reset all pending statuses');
|
console.log('Successfully reset all pending statuses');
|
||||||
setRowStatuses({}); // Clear local statuses
|
setRowStatuses({}); // Clear local statuses
|
||||||
@@ -319,27 +319,47 @@ export default function App() {
|
|||||||
console.log('[handleSaveAll] No entries to save, returning');
|
console.log('[handleSaveAll] No entries to save, returning');
|
||||||
return;
|
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');
|
|
||||||
|
|
||||||
setRowStatuses(prev => ({ ...prev, [articleNo]: 'saved' }));
|
setIsSavingAll(true);
|
||||||
setPendingRows(prev => { const n = { ...prev }; delete n[articleNo]; return n; });
|
let failedArticles: string[] = [];
|
||||||
} else {
|
const token = session?.access_token;
|
||||||
setRowStatuses(prev => ({ ...prev, [articleNo]: 'error' }));
|
|
||||||
allSuccess = false;
|
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);
|
||||||
|
|
||||||
|
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) => {
|
const captureState = (message: string) => {
|
||||||
@@ -506,6 +526,7 @@ export default function App() {
|
|||||||
<HistoryView
|
<HistoryView
|
||||||
headers={appState.headers}
|
headers={appState.headers}
|
||||||
data={appState.data}
|
data={appState.data}
|
||||||
|
sessionToken={session?.access_token}
|
||||||
onRevert={async (articleNo, revertedData, historyId) => {
|
onRevert={async (articleNo, revertedData, historyId) => {
|
||||||
// Find the row in appState.data and update it
|
// Find the row in appState.data and update it
|
||||||
const rowIndex = appState.data.findIndex(r => String(r[COLUMNS.ARTICLE_NO]) === articleNo);
|
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
|
// Delete the history entry after revert
|
||||||
if (historyId) {
|
if (historyId) {
|
||||||
await deleteHistoryEntry(String(historyId));
|
await deleteHistoryEntry(String(historyId), session?.access_token);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}}
|
}}
|
||||||
|
|||||||
@@ -8,9 +8,10 @@ interface HistoryViewProps {
|
|||||||
headers: string[];
|
headers: string[];
|
||||||
data: ExcelRow[];
|
data: ExcelRow[];
|
||||||
onRevert: (articleNo: string, oldData: ExcelRow, historyId?: number) => void;
|
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<HistoryEntry[]>([]);
|
const [history, setHistory] = useState<HistoryEntry[]>([]);
|
||||||
const [loading, setLoading] = useState(true);
|
const [loading, setLoading] = useState(true);
|
||||||
const [expandedId, setExpandedId] = useState<string | null>(null);
|
const [expandedId, setExpandedId] = useState<string | null>(null);
|
||||||
@@ -22,7 +23,7 @@ export function HistoryView({ headers, data, onRevert }: HistoryViewProps) {
|
|||||||
|
|
||||||
const loadHistory = async () => {
|
const loadHistory = async () => {
|
||||||
setLoading(true);
|
setLoading(true);
|
||||||
const data = await getHistory();
|
const data = await getHistory(sessionToken);
|
||||||
setHistory(data);
|
setHistory(data);
|
||||||
setLoading(false);
|
setLoading(false);
|
||||||
};
|
};
|
||||||
|
|||||||
+13
-12
@@ -8,14 +8,14 @@ export interface SyncedRow {
|
|||||||
status?: 'pending' | 'synced';
|
status?: 'pending' | 'synced';
|
||||||
}
|
}
|
||||||
|
|
||||||
export async function getAllSyncedRows(): Promise<Record<string, SyncedRow>> {
|
export async function getAllSyncedRows(token?: string): Promise<Record<string, SyncedRow>> {
|
||||||
try {
|
try {
|
||||||
const response = await fetch(
|
const response = await fetch(
|
||||||
`${SUPABASE_URL}/rest/v1/products?select=product_id,data,status&order=updated_at.desc`,
|
`${SUPABASE_URL}/rest/v1/products?select=product_id,data,status&order=updated_at.desc`,
|
||||||
{
|
{
|
||||||
headers: {
|
headers: {
|
||||||
'apikey': SUPABASE_KEY,
|
'apikey': SUPABASE_KEY,
|
||||||
'Authorization': `Bearer ${SUPABASE_KEY}`
|
'Authorization': `Bearer ${token || SUPABASE_KEY}`
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
@@ -33,7 +33,7 @@ export async function getAllSyncedRows(): Promise<Record<string, SyncedRow>> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export async function saveRowToSupabase(articleNo: string, rowData: ExcelRow): Promise<boolean> {
|
export async function saveRowToSupabase(articleNo: string, rowData: ExcelRow, token?: string): Promise<boolean> {
|
||||||
try {
|
try {
|
||||||
const response = await fetch(
|
const response = await fetch(
|
||||||
`${SUPABASE_URL}/rest/v1/products`,
|
`${SUPABASE_URL}/rest/v1/products`,
|
||||||
@@ -42,7 +42,7 @@ export async function saveRowToSupabase(articleNo: string, rowData: ExcelRow): P
|
|||||||
headers: {
|
headers: {
|
||||||
'Content-Type': 'application/json',
|
'Content-Type': 'application/json',
|
||||||
'apikey': SUPABASE_KEY,
|
'apikey': SUPABASE_KEY,
|
||||||
'Authorization': `Bearer ${SUPABASE_KEY}`,
|
'Authorization': `Bearer ${token || SUPABASE_KEY}`,
|
||||||
'Prefer': 'resolution=merge-duplicates'
|
'Prefer': 'resolution=merge-duplicates'
|
||||||
},
|
},
|
||||||
body: JSON.stringify({
|
body: JSON.stringify({
|
||||||
@@ -76,7 +76,8 @@ export async function saveHistoryEntry(
|
|||||||
articleName: string,
|
articleName: string,
|
||||||
oldData: ExcelRow,
|
oldData: ExcelRow,
|
||||||
newData: ExcelRow,
|
newData: ExcelRow,
|
||||||
changedBy: string
|
changedBy: string,
|
||||||
|
token?: string
|
||||||
): Promise<boolean> {
|
): Promise<boolean> {
|
||||||
try {
|
try {
|
||||||
const response = await fetch(
|
const response = await fetch(
|
||||||
@@ -86,7 +87,7 @@ export async function saveHistoryEntry(
|
|||||||
headers: {
|
headers: {
|
||||||
'Content-Type': 'application/json',
|
'Content-Type': 'application/json',
|
||||||
'apikey': SUPABASE_KEY,
|
'apikey': SUPABASE_KEY,
|
||||||
'Authorization': `Bearer ${SUPABASE_KEY}`,
|
'Authorization': `Bearer ${token || SUPABASE_KEY}`,
|
||||||
'Prefer': 'return=minimal'
|
'Prefer': 'return=minimal'
|
||||||
},
|
},
|
||||||
body: JSON.stringify({
|
body: JSON.stringify({
|
||||||
@@ -107,14 +108,14 @@ export async function saveHistoryEntry(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export async function getHistory(): Promise<HistoryEntry[]> {
|
export async function getHistory(token?: string): Promise<HistoryEntry[]> {
|
||||||
try {
|
try {
|
||||||
const response = await fetch(
|
const response = await fetch(
|
||||||
`${SUPABASE_URL}/rest/v1/products_history?select=*&order=changed_at.desc&limit=100`,
|
`${SUPABASE_URL}/rest/v1/products_history?select=*&order=changed_at.desc&limit=100`,
|
||||||
{
|
{
|
||||||
headers: {
|
headers: {
|
||||||
'apikey': SUPABASE_KEY,
|
'apikey': SUPABASE_KEY,
|
||||||
'Authorization': `Bearer ${SUPABASE_KEY}`
|
'Authorization': `Bearer ${token || SUPABASE_KEY}`
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
@@ -127,7 +128,7 @@ export async function getHistory(): Promise<HistoryEntry[]> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export async function deleteHistoryEntry(id: string): Promise<boolean> {
|
export async function deleteHistoryEntry(id: string, token?: string): Promise<boolean> {
|
||||||
try {
|
try {
|
||||||
const response = await fetch(
|
const response = await fetch(
|
||||||
`${SUPABASE_URL}/rest/v1/products_history?id=eq.${encodeURIComponent(id)}`,
|
`${SUPABASE_URL}/rest/v1/products_history?id=eq.${encodeURIComponent(id)}`,
|
||||||
@@ -135,7 +136,7 @@ export async function deleteHistoryEntry(id: string): Promise<boolean> {
|
|||||||
method: 'DELETE',
|
method: 'DELETE',
|
||||||
headers: {
|
headers: {
|
||||||
'apikey': SUPABASE_KEY,
|
'apikey': SUPABASE_KEY,
|
||||||
'Authorization': `Bearer ${SUPABASE_KEY}`
|
'Authorization': `Bearer ${token || SUPABASE_KEY}`
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
@@ -147,7 +148,7 @@ export async function deleteHistoryEntry(id: string): Promise<boolean> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export async function resetAllPendingRows(): Promise<boolean> {
|
export async function resetAllPendingRows(token?: string): Promise<boolean> {
|
||||||
try {
|
try {
|
||||||
const response = await fetch(
|
const response = await fetch(
|
||||||
`${SUPABASE_URL}/rest/v1/products?status=eq.pending`,
|
`${SUPABASE_URL}/rest/v1/products?status=eq.pending`,
|
||||||
@@ -156,7 +157,7 @@ export async function resetAllPendingRows(): Promise<boolean> {
|
|||||||
headers: {
|
headers: {
|
||||||
'Content-Type': 'application/json',
|
'Content-Type': 'application/json',
|
||||||
'apikey': SUPABASE_KEY,
|
'apikey': SUPABASE_KEY,
|
||||||
'Authorization': `Bearer ${SUPABASE_KEY}`,
|
'Authorization': `Bearer ${token || SUPABASE_KEY}`,
|
||||||
'Prefer': 'return=minimal'
|
'Prefer': 'return=minimal'
|
||||||
},
|
},
|
||||||
body: JSON.stringify({ status: 'synced' })
|
body: JSON.stringify({ status: 'synced' })
|
||||||
|
|||||||
Reference in New Issue
Block a user