diff --git a/package-lock.json b/package-lock.json index 25c08f6..c643178 100644 --- a/package-lock.json +++ b/package-lock.json @@ -14,6 +14,7 @@ "@mui/icons-material": "^7.1.0", "@mui/material": "^7.1.0", "@mui/x-data-grid": "^8.5.0", + "notistack": "^3.0.2", "react": "^19.1.0", "react-dom": "^19.1.0" }, @@ -2993,6 +2994,15 @@ "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": { "version": "1.2.0", "resolved": "https://registry.npmjs.org/gopd/-/gopd-1.2.0.tgz", @@ -3461,6 +3471,37 @@ "dev": true, "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": { "version": "4.1.1", "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz", diff --git a/package.json b/package.json index a7ad2cf..16aa844 100644 --- a/package.json +++ b/package.json @@ -16,6 +16,7 @@ "@mui/icons-material": "^7.1.0", "@mui/material": "^7.1.0", "@mui/x-data-grid": "^8.5.0", + "notistack": "^3.0.2", "react": "^19.1.0", "react-dom": "^19.1.0" }, diff --git a/src/api/mongo/actions.jsx b/src/api/mongo/actions.jsx index 736f8bc..70234d1 100644 --- a/src/api/mongo/actions.jsx +++ b/src/api/mongo/actions.jsx @@ -1,65 +1,44 @@ const API_BASE_URL = 'http://portainer.white-enciso.pro:4001/api/v1/MongoSample'; export async function getExternalData() { - try { - const response = await fetch(`${API_BASE_URL}/GetAll`); - if (!response.ok) throw new Error('Failed to fetch external data'); - const data = await response.json(); - return data; - } catch (error) { - console.error('Error fetching external data:', error); - return []; - } + const response = await fetch(`${API_BASE_URL}/GetAll`); + if (!response.ok) throw new Error('Failed to fetch external data'); + const data = await response.json(); + return data; } export async function createExternalData(data) { - try { - const response = await fetch(`${API_BASE_URL}/Create`, { - method: 'POST', - headers: { - 'Content-Type': 'application/json' - }, - body: JSON.stringify(data) - }); - if (!response.ok) throw new Error('Failed to create external data'); - const result = await response.json(); - return result; - } catch (error) { - console.error('Error creating external data:', error); - throw error; - } + const response = await fetch(`${API_BASE_URL}/Create`, { + method: 'POST', + headers: { + 'Content-Type': 'application/json', + }, + body: JSON.stringify(data), + }); + if (!response.ok) throw new Error('Failed to create external data'); + return await response.json(); } export async function updateExternalData(data) { const response = await fetch(`${API_BASE_URL}/Update`, { method: 'PUT', 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(); } export async function deleteExternalData(_Id) { - try { - const response = await fetch(`${API_BASE_URL}/Delete`, { - method: 'DELETE', - headers: { - 'Content-Type': 'application/json', - }, - body: JSON.stringify({ _Id }), - }); - - if (!response.ok) throw new Error('Failed to delete external data'); - return await response.json(); - } catch (error) { - console.error('Error deleting external data:', error); - throw error; - } + const response = await fetch(`${API_BASE_URL}/Delete`, { + method: 'DELETE', + headers: { + 'Content-Type': 'application/json', + }, + body: JSON.stringify({ _Id }), + }); + if (!response.ok) throw new Error('Failed to delete external data'); + return await response.json(); } \ No newline at end of file diff --git a/src/hooks/useApiToast.jsx b/src/hooks/useApiToast.jsx new file mode 100644 index 0000000..9750f51 --- /dev/null +++ b/src/hooks/useApiToast.jsx @@ -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 }; +} \ No newline at end of file diff --git a/src/main.jsx b/src/main.jsx index f3fdf7f..b0e8272 100644 --- a/src/main.jsx +++ b/src/main.jsx @@ -1,6 +1,6 @@ import { StrictMode } from 'react' import { createRoot } from 'react-dom/client' - +import { SnackbarProvider } from 'notistack'; import { ThemeProvider } from '@mui/material/styles'; import theme from './theme'; import './index.css' @@ -9,7 +9,14 @@ import App from './App.jsx' createRoot(document.getElementById('root')).render( - + + + , ) diff --git a/src/private/mongo/Admin.jsx b/src/private/mongo/Admin.jsx index ffd2a63..29530de 100644 --- a/src/private/mongo/Admin.jsx +++ b/src/private/mongo/Admin.jsx @@ -6,6 +6,7 @@ import EditRoundedIcon from '@mui/icons-material/EditRounded'; import DeleteRoundedIcon from '@mui/icons-material/DeleteRounded'; import AddOrEditAdminForm from './AddOrEditAdminForm'; import { getExternalData, deleteExternalData } from '../../api/mongo/actions'; +import useApiToast from '../../hooks/useApiToast'; const columnsBase = [ { field: 'name', headerName: 'Name', flex: 2 }, @@ -39,6 +40,7 @@ export default function Admin() { const [editingData, setEditingData] = useState(null); const [confirmOpen, setConfirmOpen] = useState(false); const [rowToDelete, setRowToDelete] = useState(null); + const { handleError } = useApiToast(); useEffect(() => { let isMounted = true; @@ -75,6 +77,7 @@ export default function Admin() { setRows(safeData); } catch (error) { console.error('Error loading data:', error); + handleError(error, 'Failed to load data'); setRows([]); } };