From 863f3bd7078daa0a2f16e69873fb43d96a81884a Mon Sep 17 00:00:00 2001 From: FmaresWGU Date: Thu, 7 May 2026 09:48:48 -0700 Subject: [PATCH] Fix sheet delete failing on second identical sheet - Move key from to React.Fragment so React reconciles list by sheet UUID instead of array index, preventing stale onClick on identical sheets - Use functional updater in setUserSheets to avoid stale closure - Replace .single() with .maybe_single() to prevent PGRST116 crash when sheet is not found - Split PermissionError into LookupError (404) and PermissionError (403) so missing sheets return 404 and wrong-owner returns 403 instead of 500 Closes #21 --- backend/routes/sheets.py | 6 +++++- backend/services/sheet_service.py | 6 ++++-- frontend-vite/react-ts/src/components/Inventory.tsx | 10 +++++----- 3 files changed, 14 insertions(+), 8 deletions(-) diff --git a/backend/routes/sheets.py b/backend/routes/sheets.py index c955d1f..8d6f0a8 100644 --- a/backend/routes/sheets.py +++ b/backend/routes/sheets.py @@ -84,6 +84,10 @@ def delete_sheet(user_sheet_id): try: delete_user_sheet(user_sheet_id, request.user_id) return jsonify({'success': True, 'message': 'Sheet deleted successfully'}) + except LookupError: + return jsonify({'error': 'Sheet not found'}), 404 + except PermissionError: + return jsonify({'error': 'Access denied'}), 403 except Exception as e: current_app.logger.error("Error deleting sheet %s: %s", user_sheet_id, e) - return jsonify({'error': f'Failed to delete sheet: {str(e)}'}), 500 + return jsonify({'error': 'Failed to delete sheet'}), 500 diff --git a/backend/services/sheet_service.py b/backend/services/sheet_service.py index 276881f..af364dd 100644 --- a/backend/services/sheet_service.py +++ b/backend/services/sheet_service.py @@ -121,9 +121,11 @@ def delete_user_sheet(user_sheet_id, user_id): sheet_response = supabase_admin.table('user_sheets').select('user_id').eq( 'id', user_sheet_id - ).single().execute() + ).maybe_single().execute() - if not sheet_response.data or sheet_response.data['user_id'] != user_id: + if not sheet_response.data: + raise LookupError('Sheet not found') + if sheet_response.data['user_id'] != user_id: raise PermissionError('Access denied') files_response = supabase_admin.table('sheet_files').select( diff --git a/frontend-vite/react-ts/src/components/Inventory.tsx b/frontend-vite/react-ts/src/components/Inventory.tsx index b17729a..51cf71f 100644 --- a/frontend-vite/react-ts/src/components/Inventory.tsx +++ b/frontend-vite/react-ts/src/components/Inventory.tsx @@ -1,4 +1,4 @@ -import { useState, useEffect, useRef } from 'react'; +import React, { useState, useEffect, useRef } from 'react'; import axios from 'axios'; import { Download, Trash2, RefreshCw, FileSpreadsheet, @@ -153,7 +153,7 @@ function Inventory() { setSheetStates(prev => ({ ...prev, [_userSheetId]: { ...prev[_userSheetId], deleteStatus: 'deleting' } })); try { await axios.delete(`${API_URL}/delete-sheet/${_userSheetId}`, { withCredentials: true }); - setUserSheets(userSheets.filter(s => s.id !== _userSheetId)); + setUserSheets(prev => prev.filter(s => s.id !== _userSheetId)); setSheetStates(prev => { const n = { ...prev }; delete n[_userSheetId]; return n; }); if (expandedSheetId === _userSheetId) setExpandedSheetId(null); } catch { @@ -413,8 +413,8 @@ function Inventory() { const numSelected = selectedSheets[sheet.id]?.size ?? 0; return ( - <> - + +