feat(frontend): Add comprehensive error handling for admin API
Phase 2: User-Friendly Error Handling ✅ Error Handler Service: - Created adminErrorHandler.js with handleAdminError() - User-friendly SweetAlert2 dialogs for all error types: * 403 Unauthorized - Clear admin token instructions * 429 Rate Limit - Wait and retry message * 404 Not Found - Resource not found * 500 Server Error - Internal server error * Generic errors with context ✅ Integrated Error Handling in all Admin Components: - ModerationGroupsPage.js (all 6 admin operations) - ModerationGroupImagesPage.js (group loading) - DeletionLogSection.js (log loading + statistics) - ConsentCheckboxes.js (platform loading) ✅ Error Context Messages: - "Gruppe laden" - "Gruppe freigeben" - "Gruppe löschen" - "Bild löschen" - "Consent-Export" - "Plattformen laden" - "Lösch-Log laden" - "Statistiken laden" ✨ Benefits: - Clear technical details for admins in error dialogs - Context-specific error messages - Consistent error handling across all admin features - Better debugging with detailed 403 instructions
This commit is contained in:
parent
cb640576f4
commit
6effded8bf
|
|
@ -21,6 +21,7 @@ import InfoIcon from '@mui/icons-material/Info';
|
||||||
|
|
||||||
// Services
|
// Services
|
||||||
import { adminGet } from '../../services/adminApi';
|
import { adminGet } from '../../services/adminApi';
|
||||||
|
import { handleAdminError } from '../../services/adminErrorHandler';
|
||||||
|
|
||||||
const DeletionLogSection = () => {
|
const DeletionLogSection = () => {
|
||||||
const [deletions, setDeletions] = useState([]);
|
const [deletions, setDeletions] = useState([]);
|
||||||
|
|
@ -46,7 +47,7 @@ const DeletionLogSection = () => {
|
||||||
setDeletions(data.deletions || []);
|
setDeletions(data.deletions || []);
|
||||||
setError(null);
|
setError(null);
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error('Fehler beim Laden des Lösch-Logs:', error);
|
await handleAdminError(error, 'Lösch-Log laden');
|
||||||
setError('Fehler beim Laden des Lösch-Logs');
|
setError('Fehler beim Laden des Lösch-Logs');
|
||||||
} finally {
|
} finally {
|
||||||
setLoading(false);
|
setLoading(false);
|
||||||
|
|
@ -58,7 +59,7 @@ const DeletionLogSection = () => {
|
||||||
const data = await adminGet('/api/admin/deletion-log/stats');
|
const data = await adminGet('/api/admin/deletion-log/stats');
|
||||||
setStatistics(data.statistics || null);
|
setStatistics(data.statistics || null);
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error('Fehler beim Laden der Statistiken:', error);
|
await handleAdminError(error, 'Statistiken laden');
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -11,6 +11,7 @@ import {
|
||||||
|
|
||||||
// Services
|
// Services
|
||||||
import { adminGet } from '../../../services/adminApi';
|
import { adminGet } from '../../../services/adminApi';
|
||||||
|
import { handleAdminError } from '../../../services/adminErrorHandler';
|
||||||
import InfoIcon from '@mui/icons-material/Info';
|
import InfoIcon from '@mui/icons-material/Info';
|
||||||
import FacebookIcon from '@mui/icons-material/Facebook';
|
import FacebookIcon from '@mui/icons-material/Facebook';
|
||||||
import InstagramIcon from '@mui/icons-material/Instagram';
|
import InstagramIcon from '@mui/icons-material/Instagram';
|
||||||
|
|
@ -59,7 +60,7 @@ function ConsentCheckboxes({
|
||||||
setPlatforms(data);
|
setPlatforms(data);
|
||||||
setError(null);
|
setError(null);
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error('Error loading platforms:', error);
|
await handleAdminError(error, 'Plattformen laden');
|
||||||
setError('Plattformen konnten nicht geladen werden');
|
setError('Plattformen konnten nicht geladen werden');
|
||||||
} finally {
|
} finally {
|
||||||
setLoading(false);
|
setLoading(false);
|
||||||
|
|
|
||||||
|
|
@ -4,6 +4,7 @@ import { Container, Box } from '@mui/material';
|
||||||
|
|
||||||
// Services
|
// Services
|
||||||
import { adminGet, adminRequest } from '../../services/adminApi';
|
import { adminGet, adminRequest } from '../../services/adminApi';
|
||||||
|
import { handleAdminError } from '../../services/adminErrorHandler';
|
||||||
|
|
||||||
// Components
|
// Components
|
||||||
import Navbar from '../ComponentUtils/Headers/Navbar';
|
import Navbar from '../ComponentUtils/Headers/Navbar';
|
||||||
|
|
@ -51,6 +52,7 @@ const ModerationGroupImagesPage = () => {
|
||||||
|
|
||||||
setGroup(transformedData);
|
setGroup(transformedData);
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
|
await handleAdminError(e, 'Gruppe laden');
|
||||||
setError('Fehler beim Laden der Gruppe');
|
setError('Fehler beim Laden der Gruppe');
|
||||||
} finally {
|
} finally {
|
||||||
setLoading(false);
|
setLoading(false);
|
||||||
|
|
|
||||||
|
|
@ -7,6 +7,7 @@ import Swal from 'sweetalert2/dist/sweetalert2.js';
|
||||||
|
|
||||||
// Services
|
// Services
|
||||||
import { adminGet, adminRequest, adminDownload } from '../../services/adminApi';
|
import { adminGet, adminRequest, adminDownload } from '../../services/adminApi';
|
||||||
|
import { handleAdminError } from '../../services/adminErrorHandler';
|
||||||
|
|
||||||
// Components
|
// Components
|
||||||
import Navbar from '../ComponentUtils/Headers/Navbar';
|
import Navbar from '../ComponentUtils/Headers/Navbar';
|
||||||
|
|
@ -40,7 +41,7 @@ const ModerationGroupsPage = () => {
|
||||||
const data = await adminGet('/api/admin/social-media/platforms');
|
const data = await adminGet('/api/admin/social-media/platforms');
|
||||||
setPlatforms(data);
|
setPlatforms(data);
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error('Fehler beim Laden der Plattformen:', error);
|
await handleAdminError(error, 'Plattformen laden');
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
@ -68,7 +69,7 @@ const ModerationGroupsPage = () => {
|
||||||
const data = await adminGet(url);
|
const data = await adminGet(url);
|
||||||
setGroups(data.groups);
|
setGroups(data.groups);
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error('Fehler beim Laden der Moderations-Gruppen:', error);
|
await handleAdminError(error, 'Moderations-Gruppen laden');
|
||||||
setError('Fehler beim Laden der Gruppen');
|
setError('Fehler beim Laden der Gruppen');
|
||||||
} finally {
|
} finally {
|
||||||
setLoading(false);
|
setLoading(false);
|
||||||
|
|
@ -101,7 +102,7 @@ const ModerationGroupsPage = () => {
|
||||||
showConfirmButton: false
|
showConfirmButton: false
|
||||||
});
|
});
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error('Fehler beim Freigeben der Gruppe:', error);
|
await handleAdminError(error, 'Gruppe freigeben');
|
||||||
await Swal.fire({
|
await Swal.fire({
|
||||||
icon: 'error',
|
icon: 'error',
|
||||||
title: 'Fehler',
|
title: 'Fehler',
|
||||||
|
|
@ -139,9 +140,7 @@ const ModerationGroupsPage = () => {
|
||||||
));
|
));
|
||||||
|
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error('Fehler beim Löschen des Bildes:', error);
|
await handleAdminError(error, 'Bild löschen');
|
||||||
console.error('Error details:', error.message, error.stack);
|
|
||||||
alert('Fehler beim Löschen des Bildes: ' + error.message);
|
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
@ -159,8 +158,7 @@ const ModerationGroupsPage = () => {
|
||||||
setShowImages(false);
|
setShowImages(false);
|
||||||
}
|
}
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error('Fehler beim Löschen der Gruppe:', error);
|
await handleAdminError(error, 'Gruppe löschen');
|
||||||
alert('Fehler beim Löschen der Gruppe');
|
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
@ -190,11 +188,7 @@ const ModerationGroupsPage = () => {
|
||||||
showConfirmButton: false
|
showConfirmButton: false
|
||||||
});
|
});
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error('Fehler beim Export:', error);
|
await handleAdminError(error, 'Consent-Export');
|
||||||
await Swal.fire({
|
|
||||||
icon: 'error',
|
|
||||||
title: 'Fehler',
|
|
||||||
text: 'Fehler beim Export der Consent-Daten: ' + error.message
|
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
|
||||||
94
frontend/src/services/adminErrorHandler.js
Normal file
94
frontend/src/services/adminErrorHandler.js
Normal file
|
|
@ -0,0 +1,94 @@
|
||||||
|
import Swal from 'sweetalert2/dist/sweetalert2.js';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Zentrale Error-Handler für Admin API Fehler
|
||||||
|
*/
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Behandelt Admin API Fehler mit benutzerfreundlichen Meldungen
|
||||||
|
* @param {Error} error - Der aufgetretene Fehler
|
||||||
|
* @param {string} context - Kontext der Operation (z.B. "Gruppe laden")
|
||||||
|
* @returns {Promise<void>}
|
||||||
|
*/
|
||||||
|
export const handleAdminError = async (error, context = 'Operation') => {
|
||||||
|
console.error(`Admin API Error [${context}]:`, error);
|
||||||
|
|
||||||
|
// 403 Unauthorized - Admin Token fehlt oder ungültig
|
||||||
|
if (error.message.includes('Unauthorized') || error.message.includes('403')) {
|
||||||
|
await Swal.fire({
|
||||||
|
icon: 'error',
|
||||||
|
title: 'Authentifizierung fehlgeschlagen',
|
||||||
|
html: `
|
||||||
|
<p><strong>Admin-Token fehlt oder ist ungültig.</strong></p>
|
||||||
|
<p>Bitte kontaktieren Sie den Administrator.</p>
|
||||||
|
<hr>
|
||||||
|
<small>
|
||||||
|
<strong>Technische Details:</strong><br>
|
||||||
|
- Prüfen Sie die REACT_APP_ADMIN_API_KEY Variable<br>
|
||||||
|
- Token muss mit Backend ADMIN_API_KEY übereinstimmen<br>
|
||||||
|
- Kontext: ${context}
|
||||||
|
</small>
|
||||||
|
`,
|
||||||
|
confirmButtonText: 'OK',
|
||||||
|
confirmButtonColor: '#d33'
|
||||||
|
});
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 429 Rate Limit
|
||||||
|
if (error.message.includes('Too many requests') || error.message.includes('429')) {
|
||||||
|
await Swal.fire({
|
||||||
|
icon: 'warning',
|
||||||
|
title: 'Zu viele Anfragen',
|
||||||
|
text: 'Bitte warten Sie einen Moment und versuchen Sie es erneut.',
|
||||||
|
timer: 3000,
|
||||||
|
showConfirmButton: false
|
||||||
|
});
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 404 Not Found
|
||||||
|
if (error.message.includes('404')) {
|
||||||
|
await Swal.fire({
|
||||||
|
icon: 'error',
|
||||||
|
title: 'Nicht gefunden',
|
||||||
|
text: `Die angeforderte Ressource wurde nicht gefunden.`,
|
||||||
|
confirmButtonText: 'OK'
|
||||||
|
});
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 500 Server Error
|
||||||
|
if (error.message.includes('500')) {
|
||||||
|
await Swal.fire({
|
||||||
|
icon: 'error',
|
||||||
|
title: 'Server-Fehler',
|
||||||
|
text: 'Ein interner Server-Fehler ist aufgetreten. Bitte versuchen Sie es später erneut.',
|
||||||
|
confirmButtonText: 'OK'
|
||||||
|
});
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Generischer Fehler
|
||||||
|
await Swal.fire({
|
||||||
|
icon: 'error',
|
||||||
|
title: `Fehler: ${context}`,
|
||||||
|
text: error.message || 'Ein unbekannter Fehler ist aufgetreten.',
|
||||||
|
confirmButtonText: 'OK'
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Wrapper für async Operationen mit automatischem Error-Handling
|
||||||
|
* @param {Function} operation - Die auszuführende async Operation
|
||||||
|
* @param {string} context - Kontext der Operation
|
||||||
|
* @returns {Promise<any>} - Ergebnis der Operation oder null bei Fehler
|
||||||
|
*/
|
||||||
|
export const withErrorHandling = async (operation, context) => {
|
||||||
|
try {
|
||||||
|
return await operation();
|
||||||
|
} catch (error) {
|
||||||
|
await handleAdminError(error, context);
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
};
|
||||||
Loading…
Reference in New Issue
Block a user