mirror of
https://github.com/christianvidalwolf-prog/Craze-Data-check.git
synced 2026-08-03 15:45:24 +02:00
feat: persist edits to Supabase database
This commit is contained in:
+31
-3
@@ -8,6 +8,7 @@ import { DataCompleteness } from './components/DataCompleteness';
|
|||||||
import { MatrixView } from './components/MatrixView';
|
import { MatrixView } from './components/MatrixView';
|
||||||
import { UploadReload } from './components/UploadReload';
|
import { UploadReload } from './components/UploadReload';
|
||||||
import { EditPanel } from './components/EditPanel';
|
import { EditPanel } from './components/EditPanel';
|
||||||
|
import { getAllSyncedRows, saveRowToSupabase } from './lib/supabase';
|
||||||
|
|
||||||
export default function App() {
|
export default function App() {
|
||||||
const [appState, setAppState] = useState<AppState>({
|
const [appState, setAppState] = useState<AppState>({
|
||||||
@@ -60,9 +61,22 @@ export default function App() {
|
|||||||
const data = XLSX.utils.sheet_to_json<any[]>(ws, { header: 1 });
|
const data = XLSX.utils.sheet_to_json<any[]>(ws, { header: 1 });
|
||||||
|
|
||||||
if (data.length > 0) {
|
if (data.length > 0) {
|
||||||
|
const rawHeaders = data[0];
|
||||||
|
const rawRows = data.slice(1);
|
||||||
|
|
||||||
|
// Apply Supabase overrides
|
||||||
|
console.log("Applying Supabase overrides...");
|
||||||
|
const syncedData = await getAllSyncedRows();
|
||||||
|
const articleNoIdx = COLUMNS.ARTICLE_NO;
|
||||||
|
|
||||||
|
const processedRows = rawRows.map(row => {
|
||||||
|
const articleNo = String(row[articleNoIdx]);
|
||||||
|
return syncedData[articleNo] || row;
|
||||||
|
});
|
||||||
|
|
||||||
setAppState({
|
setAppState({
|
||||||
headers: data[0],
|
headers: rawHeaders,
|
||||||
data: data.slice(1),
|
data: processedRows,
|
||||||
fileName: 'Data-Matrix.xlsx (Cloud Sync)',
|
fileName: 'Data-Matrix.xlsx (Cloud Sync)',
|
||||||
fileDate: new Date(),
|
fileDate: new Date(),
|
||||||
hasUnsavedChanges: false
|
hasUnsavedChanges: false
|
||||||
@@ -132,7 +146,8 @@ export default function App() {
|
|||||||
setAppState(prev => ({ ...prev, hasUnsavedChanges: false }));
|
setAppState(prev => ({ ...prev, hasUnsavedChanges: false }));
|
||||||
};
|
};
|
||||||
|
|
||||||
const handleSaveRow = (rowIndex: number, updatedRow: ExcelRow) => {
|
const handleSaveRow = async (rowIndex: number, updatedRow: ExcelRow) => {
|
||||||
|
// 1. Update UI state
|
||||||
setAppState(prev => {
|
setAppState(prev => {
|
||||||
const newData = [...prev.data];
|
const newData = [...prev.data];
|
||||||
newData[rowIndex] = updatedRow;
|
newData[rowIndex] = updatedRow;
|
||||||
@@ -143,6 +158,19 @@ export default function App() {
|
|||||||
};
|
};
|
||||||
});
|
});
|
||||||
setEditingRowIndex(null);
|
setEditingRowIndex(null);
|
||||||
|
|
||||||
|
// 2. Persist to Supabase
|
||||||
|
const articleNo = String(updatedRow[COLUMNS.ARTICLE_NO]);
|
||||||
|
console.log(`Saving article ${articleNo} to Supabase...`);
|
||||||
|
const success = await saveRowToSupabase(articleNo, updatedRow);
|
||||||
|
|
||||||
|
if (success) {
|
||||||
|
console.log(`Successfully saved ${articleNo}`);
|
||||||
|
setAppState(prev => ({ ...prev, hasUnsavedChanges: false }));
|
||||||
|
} else {
|
||||||
|
console.error(`Failed to save ${articleNo} to Supabase`);
|
||||||
|
alert("Error saving to database. Local changes will be lost on refresh if not saved.");
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
const stats = useMemo(() => {
|
const stats = useMemo(() => {
|
||||||
|
|||||||
@@ -0,0 +1,69 @@
|
|||||||
|
import { ExcelRow } from '../types';
|
||||||
|
|
||||||
|
const SUPABASE_URL = 'https://hwithddwaapyhnfwcesj.supabase.co';
|
||||||
|
const SUPABASE_KEY = 'sb_publishable_fGXkh0bSrAOqSk2jWKAzSg_NJD9YPCv';
|
||||||
|
|
||||||
|
export async function saveRowToSupabase(articleNo: string, rowData: ExcelRow) {
|
||||||
|
try {
|
||||||
|
const response = await fetch(`${SUPABASE_URL}/rest/v1/products_sync?id=eq.${encodeURIComponent(articleNo)}`, {
|
||||||
|
method: 'PATCH',
|
||||||
|
headers: {
|
||||||
|
'apikey': SUPABASE_KEY,
|
||||||
|
'Authorization': `Bearer ${SUPABASE_KEY}`,
|
||||||
|
'Content-Type': 'application/json',
|
||||||
|
'Prefer': 'resolution=merge-duplicates'
|
||||||
|
},
|
||||||
|
body: JSON.stringify({
|
||||||
|
id: articleNo,
|
||||||
|
data: rowData,
|
||||||
|
updated_at: new Date().toISOString()
|
||||||
|
})
|
||||||
|
});
|
||||||
|
|
||||||
|
if (response.status === 204 || response.ok) {
|
||||||
|
// If PATCH didn't find the record, try UPSERT
|
||||||
|
const upsertResponse = await fetch(`${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,
|
||||||
|
updated_at: new Date().toISOString()
|
||||||
|
})
|
||||||
|
});
|
||||||
|
return upsertResponse.ok;
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
} catch (error) {
|
||||||
|
console.error('Error saving to Supabase:', error);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function getAllSyncedRows(): Promise<Record<string, ExcelRow>> {
|
||||||
|
try {
|
||||||
|
const response = await fetch(`${SUPABASE_URL}/rest/v1/products_sync`, {
|
||||||
|
headers: {
|
||||||
|
'apikey': SUPABASE_KEY,
|
||||||
|
'Authorization': `Bearer ${SUPABASE_KEY}`
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
if (!response.ok) return {};
|
||||||
|
|
||||||
|
const data = await response.json();
|
||||||
|
const result: Record<string, ExcelRow> = {};
|
||||||
|
data.forEach((item: any) => {
|
||||||
|
result[item.id] = item.data;
|
||||||
|
});
|
||||||
|
return result;
|
||||||
|
} catch (error) {
|
||||||
|
console.error('Error fetching from Supabase:', error);
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user