feat: add toast when endpoints are failing
This commit is contained in:
		
							
								
								
									
										41
									
								
								package-lock.json
									
									
									
										generated
									
									
									
								
							
							
						
						
									
										41
									
								
								package-lock.json
									
									
									
										generated
									
									
									
								
							| @@ -14,6 +14,7 @@ | |||||||
|         "@mui/icons-material": "^7.1.0", |         "@mui/icons-material": "^7.1.0", | ||||||
|         "@mui/material": "^7.1.0", |         "@mui/material": "^7.1.0", | ||||||
|         "@mui/x-data-grid": "^8.5.0", |         "@mui/x-data-grid": "^8.5.0", | ||||||
|  |         "notistack": "^3.0.2", | ||||||
|         "react": "^19.1.0", |         "react": "^19.1.0", | ||||||
|         "react-dom": "^19.1.0" |         "react-dom": "^19.1.0" | ||||||
|       }, |       }, | ||||||
| @@ -2993,6 +2994,15 @@ | |||||||
|         "url": "https://github.com/sponsors/sindresorhus" |         "url": "https://github.com/sponsors/sindresorhus" | ||||||
|       } |       } | ||||||
|     }, |     }, | ||||||
|  |     "node_modules/goober": { | ||||||
|  |       "version": "2.1.16", | ||||||
|  |       "resolved": "https://registry.npmjs.org/goober/-/goober-2.1.16.tgz", | ||||||
|  |       "integrity": "sha512-erjk19y1U33+XAMe1VTvIONHYoSqE4iS7BYUZfHaqeohLmnC0FdxEh7rQU+6MZ4OajItzjZFSRtVANrQwNq6/g==", | ||||||
|  |       "license": "MIT", | ||||||
|  |       "peerDependencies": { | ||||||
|  |         "csstype": "^3.0.10" | ||||||
|  |       } | ||||||
|  |     }, | ||||||
|     "node_modules/gopd": { |     "node_modules/gopd": { | ||||||
|       "version": "1.2.0", |       "version": "1.2.0", | ||||||
|       "resolved": "https://registry.npmjs.org/gopd/-/gopd-1.2.0.tgz", |       "resolved": "https://registry.npmjs.org/gopd/-/gopd-1.2.0.tgz", | ||||||
| @@ -3461,6 +3471,37 @@ | |||||||
|       "dev": true, |       "dev": true, | ||||||
|       "license": "MIT" |       "license": "MIT" | ||||||
|     }, |     }, | ||||||
|  |     "node_modules/notistack": { | ||||||
|  |       "version": "3.0.2", | ||||||
|  |       "resolved": "https://registry.npmjs.org/notistack/-/notistack-3.0.2.tgz", | ||||||
|  |       "integrity": "sha512-0R+/arLYbK5Hh7mEfR2adt0tyXJcCC9KkA2hc56FeWik2QN6Bm/S4uW+BjzDARsJth5u06nTjelSw/VSnB1YEA==", | ||||||
|  |       "license": "MIT", | ||||||
|  |       "dependencies": { | ||||||
|  |         "clsx": "^1.1.0", | ||||||
|  |         "goober": "^2.0.33" | ||||||
|  |       }, | ||||||
|  |       "engines": { | ||||||
|  |         "node": ">=12.0.0", | ||||||
|  |         "npm": ">=6.0.0" | ||||||
|  |       }, | ||||||
|  |       "funding": { | ||||||
|  |         "type": "opencollective", | ||||||
|  |         "url": "https://opencollective.com/notistack" | ||||||
|  |       }, | ||||||
|  |       "peerDependencies": { | ||||||
|  |         "react": "^17.0.0 || ^18.0.0 || ^19.0.0", | ||||||
|  |         "react-dom": "^17.0.0 || ^18.0.0 || ^19.0.0" | ||||||
|  |       } | ||||||
|  |     }, | ||||||
|  |     "node_modules/notistack/node_modules/clsx": { | ||||||
|  |       "version": "1.2.1", | ||||||
|  |       "resolved": "https://registry.npmjs.org/clsx/-/clsx-1.2.1.tgz", | ||||||
|  |       "integrity": "sha512-EcR6r5a8bj6pu3ycsa/E/cKVGuTgZJZdsyUYHOksG/UHIiKfjxzRxYJpyVBwYaQeOvghal9fcc4PidlgzugAQg==", | ||||||
|  |       "license": "MIT", | ||||||
|  |       "engines": { | ||||||
|  |         "node": ">=6" | ||||||
|  |       } | ||||||
|  |     }, | ||||||
|     "node_modules/object-assign": { |     "node_modules/object-assign": { | ||||||
|       "version": "4.1.1", |       "version": "4.1.1", | ||||||
|       "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz", |       "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz", | ||||||
|   | |||||||
| @@ -16,6 +16,7 @@ | |||||||
|     "@mui/icons-material": "^7.1.0", |     "@mui/icons-material": "^7.1.0", | ||||||
|     "@mui/material": "^7.1.0", |     "@mui/material": "^7.1.0", | ||||||
|     "@mui/x-data-grid": "^8.5.0", |     "@mui/x-data-grid": "^8.5.0", | ||||||
|  |     "notistack": "^3.0.2", | ||||||
|     "react": "^19.1.0", |     "react": "^19.1.0", | ||||||
|     "react-dom": "^19.1.0" |     "react-dom": "^19.1.0" | ||||||
|   }, |   }, | ||||||
|   | |||||||
| @@ -1,53 +1,37 @@ | |||||||
| const API_BASE_URL = 'http://portainer.white-enciso.pro:4001/api/v1/MongoSample'; | const API_BASE_URL = 'http://portainer.white-enciso.pro:4001/api/v1/MongoSample'; | ||||||
|  |  | ||||||
| export async function getExternalData() { | export async function getExternalData() { | ||||||
|   try { |  | ||||||
|   const response = await fetch(`${API_BASE_URL}/GetAll`); |   const response = await fetch(`${API_BASE_URL}/GetAll`); | ||||||
|   if (!response.ok) throw new Error('Failed to fetch external data'); |   if (!response.ok) throw new Error('Failed to fetch external data'); | ||||||
|   const data = await response.json(); |   const data = await response.json(); | ||||||
|   return data; |   return data; | ||||||
|   } catch (error) { |  | ||||||
|     console.error('Error fetching external data:', error); |  | ||||||
|     return []; |  | ||||||
|   } |  | ||||||
| } | } | ||||||
|  |  | ||||||
| export async function createExternalData(data) { | export async function createExternalData(data) { | ||||||
|   try { |  | ||||||
|   const response = await fetch(`${API_BASE_URL}/Create`, { |   const response = await fetch(`${API_BASE_URL}/Create`, { | ||||||
|     method: 'POST', |     method: 'POST', | ||||||
|     headers: { |     headers: { | ||||||
|         'Content-Type': 'application/json' |       'Content-Type': 'application/json', | ||||||
|     }, |     }, | ||||||
|       body: JSON.stringify(data) |     body: JSON.stringify(data), | ||||||
|   }); |   }); | ||||||
|   if (!response.ok) throw new Error('Failed to create external data'); |   if (!response.ok) throw new Error('Failed to create external data'); | ||||||
|     const result = await response.json(); |   return await response.json(); | ||||||
|     return result; |  | ||||||
|   } catch (error) { |  | ||||||
|     console.error('Error creating external data:', error); |  | ||||||
|     throw error; |  | ||||||
|   } |  | ||||||
| } | } | ||||||
|  |  | ||||||
| export async function updateExternalData(data) { | export async function updateExternalData(data) { | ||||||
|   const response = await fetch(`${API_BASE_URL}/Update`, { |   const response = await fetch(`${API_BASE_URL}/Update`, { | ||||||
|     method: 'PUT', |     method: 'PUT', | ||||||
|     headers: { |     headers: { | ||||||
|       'Content-Type': 'application/json' |       'Content-Type': 'application/json', | ||||||
|     }, |     }, | ||||||
|     body: JSON.stringify(data) |     body: JSON.stringify(data), | ||||||
|   }); |   }); | ||||||
|  |   if (!response.ok) throw new Error('Failed to update item'); | ||||||
|   if (!response.ok) { |  | ||||||
|     throw new Error('Failed to update item'); |  | ||||||
|   } |  | ||||||
|  |  | ||||||
|   return await response.json(); |   return await response.json(); | ||||||
| } | } | ||||||
|  |  | ||||||
| export async function deleteExternalData(_Id) { | export async function deleteExternalData(_Id) { | ||||||
|   try { |  | ||||||
|   const response = await fetch(`${API_BASE_URL}/Delete`, { |   const response = await fetch(`${API_BASE_URL}/Delete`, { | ||||||
|     method: 'DELETE', |     method: 'DELETE', | ||||||
|     headers: { |     headers: { | ||||||
| @@ -55,11 +39,6 @@ export async function deleteExternalData(_Id) { | |||||||
|     }, |     }, | ||||||
|     body: JSON.stringify({ _Id }), |     body: JSON.stringify({ _Id }), | ||||||
|   }); |   }); | ||||||
|  |  | ||||||
|   if (!response.ok) throw new Error('Failed to delete external data'); |   if (!response.ok) throw new Error('Failed to delete external data'); | ||||||
|   return await response.json(); |   return await response.json(); | ||||||
|   } catch (error) { |  | ||||||
|     console.error('Error deleting external data:', error); |  | ||||||
|     throw error; |  | ||||||
|   } |  | ||||||
| } | } | ||||||
							
								
								
									
										12
									
								
								src/hooks/useApiToast.jsx
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										12
									
								
								src/hooks/useApiToast.jsx
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,12 @@ | |||||||
|  | import { useSnackbar } from 'notistack'; | ||||||
|  |  | ||||||
|  | export default function useApiToast() { | ||||||
|  |     const { enqueueSnackbar } = useSnackbar(); | ||||||
|  |  | ||||||
|  |     const handleError = (error, defaultMessage = 'API error') => { | ||||||
|  |         console.error(error); | ||||||
|  |         enqueueSnackbar(error.message || defaultMessage, { variant: 'error' }); | ||||||
|  |     }; | ||||||
|  |  | ||||||
|  |     return { handleError }; | ||||||
|  | } | ||||||
| @@ -1,6 +1,6 @@ | |||||||
| import { StrictMode } from 'react' | import { StrictMode } from 'react' | ||||||
| import { createRoot } from 'react-dom/client' | import { createRoot } from 'react-dom/client' | ||||||
|  | import { SnackbarProvider } from 'notistack'; | ||||||
| import { ThemeProvider } from '@mui/material/styles'; | import { ThemeProvider } from '@mui/material/styles'; | ||||||
| import theme from './theme'; | import theme from './theme'; | ||||||
| import './index.css' | import './index.css' | ||||||
| @@ -9,7 +9,14 @@ import App from './App.jsx' | |||||||
| createRoot(document.getElementById('root')).render( | createRoot(document.getElementById('root')).render( | ||||||
|   <StrictMode> |   <StrictMode> | ||||||
|     <ThemeProvider theme={theme}> |     <ThemeProvider theme={theme}> | ||||||
|  |       <SnackbarProvider maxSnack={3} | ||||||
|  |         autoHideDuration={5000} | ||||||
|  |         anchorOrigin={{ | ||||||
|  |           vertical: 'top', | ||||||
|  |           horizontal: 'right', | ||||||
|  |         }}> | ||||||
|         <App /> |         <App /> | ||||||
|  |       </SnackbarProvider> | ||||||
|     </ThemeProvider> |     </ThemeProvider> | ||||||
|   </StrictMode>, |   </StrictMode>, | ||||||
| ) | ) | ||||||
|   | |||||||
| @@ -6,6 +6,7 @@ import EditRoundedIcon from '@mui/icons-material/EditRounded'; | |||||||
| import DeleteRoundedIcon from '@mui/icons-material/DeleteRounded'; | import DeleteRoundedIcon from '@mui/icons-material/DeleteRounded'; | ||||||
| import AddOrEditAdminForm from './AddOrEditAdminForm'; | import AddOrEditAdminForm from './AddOrEditAdminForm'; | ||||||
| import { getExternalData, deleteExternalData } from '../../api/mongo/actions'; | import { getExternalData, deleteExternalData } from '../../api/mongo/actions'; | ||||||
|  | import useApiToast from '../../hooks/useApiToast'; | ||||||
|  |  | ||||||
| const columnsBase = [ | const columnsBase = [ | ||||||
|     { field: 'name', headerName: 'Name', flex: 2 }, |     { field: 'name', headerName: 'Name', flex: 2 }, | ||||||
| @@ -39,6 +40,7 @@ export default function Admin() { | |||||||
|     const [editingData, setEditingData] = useState(null); |     const [editingData, setEditingData] = useState(null); | ||||||
|     const [confirmOpen, setConfirmOpen] = useState(false); |     const [confirmOpen, setConfirmOpen] = useState(false); | ||||||
|     const [rowToDelete, setRowToDelete] = useState(null); |     const [rowToDelete, setRowToDelete] = useState(null); | ||||||
|  |     const { handleError } = useApiToast(); | ||||||
|  |  | ||||||
|     useEffect(() => { |     useEffect(() => { | ||||||
|         let isMounted = true; |         let isMounted = true; | ||||||
| @@ -75,6 +77,7 @@ export default function Admin() { | |||||||
|             setRows(safeData); |             setRows(safeData); | ||||||
|         } catch (error) { |         } catch (error) { | ||||||
|             console.error('Error loading data:', error); |             console.error('Error loading data:', error); | ||||||
|  |             handleError(error, 'Failed to load data'); | ||||||
|             setRows([]); |             setRows([]); | ||||||
|         } |         } | ||||||
|     }; |     }; | ||||||
|   | |||||||
		Reference in New Issue
	
	Block a user
	 Rodolfo Ruiz
					Rodolfo Ruiz