mirror of
https://github.com/christianvidalwolf-prog/Craze-Data-check.git
synced 2026-08-03 12:35:25 +02:00
feat: manual Save All button in TopBar with pending change tracking
Changes are now queued locally (yellow highlight) and only persisted to Supabase when the user clicks the Save button in the top-right corner. The button shows the count of pending changes and a spinner while saving. EditPanel closes immediately after queuing — no Supabase call per edit. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Sonnet 4.6
parent
c544b9b709
commit
acc1d06269
+27
-16
@@ -39,6 +39,8 @@ export default function App() {
|
||||
const [isLoadingDefault, setIsLoadingDefault] = useState(true);
|
||||
const [defaultLoadError, setDefaultLoadError] = useState<string | null>(null);
|
||||
const [rowStatuses, setRowStatuses] = useState<Record<string, string>>({});
|
||||
const [pendingRows, setPendingRows] = useState<Record<string, ExcelRow>>({});
|
||||
const [isSavingAll, setIsSavingAll] = useState(false);
|
||||
|
||||
useEffect(() => {
|
||||
const loadDefaultData = async () => {
|
||||
@@ -238,31 +240,37 @@ export default function App() {
|
||||
setAppState(prev => ({ ...prev, hasUnsavedChanges: false }));
|
||||
};
|
||||
|
||||
const handleSaveRow = async (rowIndex: number, updatedRow: ExcelRow): Promise<boolean> => {
|
||||
// 1. Optimistically update UI
|
||||
const handleSaveRow = (rowIndex: number, updatedRow: ExcelRow) => {
|
||||
// Update local UI state only — no Supabase call here.
|
||||
// Changes are queued in pendingRows and saved manually via handleSaveAll.
|
||||
setAppState(prev => {
|
||||
const newData = [...prev.data];
|
||||
newData[rowIndex] = updatedRow;
|
||||
return { ...prev, data: newData, hasUnsavedChanges: true };
|
||||
});
|
||||
|
||||
const articleNo = String(updatedRow[COLUMNS.ARTICLE_NO]);
|
||||
setPendingRows(prev => ({ ...prev, [articleNo]: updatedRow }));
|
||||
setRowStatuses(prev => ({ ...prev, [articleNo]: 'pending' }));
|
||||
setEditingRowIndex(null);
|
||||
};
|
||||
|
||||
// 2. Persist to Supabase
|
||||
const success = await saveRowToSupabase(articleNo, updatedRow);
|
||||
|
||||
if (success) {
|
||||
setRowStatuses(prev => ({ ...prev, [articleNo]: 'saved' }));
|
||||
setAppState(prev => ({ ...prev, hasUnsavedChanges: false }));
|
||||
setEditingRowIndex(null); // Close panel only after confirmed save
|
||||
} else {
|
||||
console.error(`Failed to save ${articleNo} to Supabase`);
|
||||
setRowStatuses(prev => ({ ...prev, [articleNo]: 'error' }));
|
||||
// Panel stays open so user can retry
|
||||
const handleSaveAll = async () => {
|
||||
const entries = Object.entries(pendingRows) as [string, ExcelRow][];
|
||||
if (entries.length === 0) return;
|
||||
setIsSavingAll(true);
|
||||
let allSuccess = true;
|
||||
for (const [articleNo, rowData] of entries) {
|
||||
const success = await saveRowToSupabase(articleNo, rowData);
|
||||
if (success) {
|
||||
setRowStatuses(prev => ({ ...prev, [articleNo]: 'saved' }));
|
||||
setPendingRows(prev => { const n = { ...prev }; delete n[articleNo]; return n; });
|
||||
} else {
|
||||
setRowStatuses(prev => ({ ...prev, [articleNo]: 'error' }));
|
||||
allSuccess = false;
|
||||
}
|
||||
}
|
||||
|
||||
return success;
|
||||
if (allSuccess) setAppState(prev => ({ ...prev, hasUnsavedChanges: false }));
|
||||
setIsSavingAll(false);
|
||||
};
|
||||
|
||||
const captureState = (message: string) => {
|
||||
@@ -334,6 +342,9 @@ export default function App() {
|
||||
onUndo={handleUndo}
|
||||
undoMessage={undoHistory[0]?.message}
|
||||
undoSteps={undoHistory.length}
|
||||
pendingCount={Object.keys(pendingRows).length}
|
||||
onSaveAll={handleSaveAll}
|
||||
isSavingAll={isSavingAll}
|
||||
/>
|
||||
<div className="flex flex-1 overflow-hidden">
|
||||
<Sidebar activeModule={activeModule} setActiveModule={setActiveModule} />
|
||||
|
||||
Reference in New Issue
Block a user