feat: add toast when endpoints are failing
This commit is contained in:
parent
2849ee2e6b
commit
eb49416000
41
package-lock.json
generated
41
package-lock.json
generated
@ -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",
|
||||
|
||||
@ -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"
|
||||
},
|
||||
|
||||
@ -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();
|
||||
}
|
||||
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 };
|
||||
}
|
||||
11
src/main.jsx
11
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(
|
||||
<StrictMode>
|
||||
<ThemeProvider theme={theme}>
|
||||
<App />
|
||||
<SnackbarProvider maxSnack={3}
|
||||
autoHideDuration={5000}
|
||||
anchorOrigin={{
|
||||
vertical: 'top',
|
||||
horizontal: 'right',
|
||||
}}>
|
||||
<App />
|
||||
</SnackbarProvider>
|
||||
</ThemeProvider>
|
||||
</StrictMode>,
|
||||
)
|
||||
|
||||
@ -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([]);
|
||||
}
|
||||
};
|
||||
|
||||
Loading…
Reference in New Issue
Block a user