diff --git a/client/src/ImageUpload/index.jsx b/client/src/ImageUpload/index.jsx index e82f6e2..231470a 100644 --- a/client/src/ImageUpload/index.jsx +++ b/client/src/ImageUpload/index.jsx @@ -81,23 +81,36 @@ const ImageUpload = ({ onImageUpload, settingsImages }) => { } }; - const deleteImage = async (filename) => { + const deleteImage = async (filename, isNotFound = false) => { try { - const response = await axios.delete(`${config.SERVER_URL}/uploads/${filename}`); - showSnackbar(response.data.message, 'success'); - - // Update the state to remove the deleted image - const updatedImages = images.filter((image) => image.filename !== filename); - setImages(updatedImages); - onImageUpload(updatedImages); - } catch (error) { - if (error?.response?.data) { - showSnackbar(error.response.data.message, 'error'); - } else { - showSnackbar(t("error.server_connection"), 'error'); + if (isNotFound) { + const updatedImages = images.filter((image) => image.filename !== filename); + setImages(updatedImages); + onImageUpload(updatedImages); + } else { + const response = await axios.delete(`${config.SERVER_URL}/uploads/${filename}`); + showSnackbar(response.data.message, 'success'); + + // Update the state to remove the deleted image + const updatedImages = images.filter((image) => image.filename !== filename); + setImages(updatedImages); + onImageUpload(updatedImages); + } + } catch (error) { + if (error?.response?.data) { + showSnackbar(error.response.data.message, 'error'); + } else { + showSnackbar(t("error.server_connection"), 'error'); + } + console.error('Error deleting image:', error); } - console.error('Error deleting image:', error); - } + }; + + const handleImageError = (index) => { + const updatedImages = [...images]; + updatedImages[index].isNotFound = true; + setImages(updatedImages); + showSnackbar(t("error.image_not_found"), 'error'); }; const handleRemoveImage = (index) => { @@ -110,7 +123,7 @@ const ImageUpload = ({ onImageUpload, settingsImages }) => { } } if (imageToRemove && imageToRemove.filename) { - deleteImage(imageToRemove.filename); + deleteImage(imageToRemove.filename, imageToRemove.isNotFound); } else { console.error('Error deleting image: imageToRemove or imageToRemove.filename is undefined'); } @@ -178,6 +191,7 @@ const ImageUpload = ({ onImageUpload, settingsImages }) => { preview handleImageError(index)} style={{ width: '100px', height: '100px', diff --git a/client/src/Localization/translation-de-DE.js b/client/src/Localization/translation-de-DE.js index dc73e58..d965b7f 100644 --- a/client/src/Localization/translation-de-DE.js +++ b/client/src/Localization/translation-de-DE.js @@ -78,7 +78,8 @@ const translationDeDE = { "hide_region": "Region ausblenden", "show_region": "Region anzeigen", "expand_selection": "Auswahl erweitern", - "collapse_selection": "Auswahl verkleinern" + "collapse_selection": "Auswahl verkleinern", + "error.image_not_found": "Bild nicht gefunden", }; export default translationDeDE; diff --git a/client/src/Localization/translation-en-EN.js b/client/src/Localization/translation-en-EN.js index 160254a..2828725 100644 --- a/client/src/Localization/translation-en-EN.js +++ b/client/src/Localization/translation-en-EN.js @@ -80,6 +80,7 @@ const translationEnEN = { "show_region": "Show region", "expand_selection": "Expand selection", "collapse_selection": "Collapse selection", + "error.image_not_found": "Image not found", }; export default translationEnEN; diff --git a/client/src/SnackbarContext/index.jsx b/client/src/SnackbarContext/index.jsx index 4a70eae..f416e64 100644 --- a/client/src/SnackbarContext/index.jsx +++ b/client/src/SnackbarContext/index.jsx @@ -1,39 +1,43 @@ import { createContext, useState, useContext } from 'react'; import Snackbar from '@mui/material/Snackbar'; import Alert from '@mui/material/Alert'; - +import { Box } from '@mui/material'; const SnackbarContext = createContext({ - open: false, - message: '', - severity: 'info', - handleClose: () => {}, + showSnackbar: () => {}, }); export const SnackbarProvider = ({ children }) => { - const [open, setOpen] = useState(false); - const [message, setMessage] = useState(''); - const [severity, setSeverity] = useState('info'); + const [messages, setMessages] = useState([]); - const handleClose = () => { - setOpen(false); + const showSnackbar = (message, severity = 'info') => { + setMessages((prevMessages) => [ + ...prevMessages, + { id: new Date().getTime(), message, severity }, + ]); }; - const showSnackbar = (message, newSeverity = 'info') => { - setMessage(message); - setSeverity(newSeverity); - setOpen(true); + const handleClose = (index) => { + setMessages((prevMessages) => prevMessages.filter((msg, idx) => idx !== index)); }; return ( - - - - {message} - - + + + {messages.map((msg, index) => ( + + handleClose(index)} severity={msg.severity}> + {msg.message} + + + ))} + {children} ); diff --git a/server/app.py b/server/app.py index b50818b..56628b2 100644 --- a/server/app.py +++ b/server/app.py @@ -98,7 +98,11 @@ def upload_file(): @app.route('/uploads/', methods=['GET']) def uploaded_file(filename): - return send_from_directory(app.config['UPLOAD_FOLDER'], filename) + file_path = os.path.join(app.config['UPLOAD_FOLDER'], filename) + if os.path.exists(file_path): + return send_from_directory(app.config['UPLOAD_FOLDER'], filename) + else: + return jsonify({"status": "error", "message": "File not found"}), 404 @app.route('/uploads/', methods=['DELETE']) def delete_file(filename): @@ -698,6 +702,11 @@ def get_images_info(): print('Error:', e) return jsonify({'error': str(e)}), 500 +@app.errorhandler(404) +def not_found(error): + return jsonify({"status": "error", "message": "Resource not found"}), 404 + + @app.route('/', methods=['GET']) def main(): return ''' diff --git a/server/tests/test_app.py b/server/tests/test_app.py index 9b0019a..99d7955 100644 --- a/server/tests/test_app.py +++ b/server/tests/test_app.py @@ -1,4 +1,5 @@ import unittest +from unittest.mock import patch # import os import json from io import BytesIO @@ -42,6 +43,14 @@ def test_images_name(self): self.assertEqual(response.status_code, 200) self.assertIn(b'configuration', response.data) + def test_uploaded_file_not_found(self): + with patch('os.path.exists') as mock_exists: + mock_exists.return_value = False + response = self.app.get('/uploads/example.png') + self.assertEqual(response.status_code, 404) + data = response.get_json() + self.assertEqual(data['status'], 'error') + self.assertEqual(data['message'], 'File not found') # def test_save_annotate_info_success(self): # annotate_data = {