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
|
||||
import { adminGet } from '../../services/adminApi';
|
||||
import { handleAdminError } from '../../services/adminErrorHandler';
|
||||
|
||||
const DeletionLogSection = () => {
|
||||
const [deletions, setDeletions] = useState([]);
|
||||
|
|
@ -46,7 +47,7 @@ const DeletionLogSection = () => {
|
|||
setDeletions(data.deletions || []);
|
||||
setError(null);
|
||||
} 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');
|
||||
} finally {
|
||||
setLoading(false);
|
||||
|
|
@ -58,7 +59,7 @@ const DeletionLogSection = () => {
|
|||
const data = await adminGet('/api/admin/deletion-log/stats');
|
||||
setStatistics(data.statistics || null);
|
||||
} catch (error) {
|
||||
console.error('Fehler beim Laden der Statistiken:', error);
|
||||
await handleAdminError(error, 'Statistiken laden');
|
||||
}
|
||||
};
|
||||
|
||||
|
|
|
|||
|
|
@ -11,6 +11,7 @@ import {
|
|||
|
||||
// Services
|
||||
import { adminGet } from '../../../services/adminApi';
|
||||
import { handleAdminError } from '../../../services/adminErrorHandler';
|
||||
import InfoIcon from '@mui/icons-material/Info';
|
||||
import FacebookIcon from '@mui/icons-material/Facebook';
|
||||
import InstagramIcon from '@mui/icons-material/Instagram';
|
||||
|
|
@ -59,7 +60,7 @@ function ConsentCheckboxes({
|
|||
setPlatforms(data);
|
||||
setError(null);
|
||||
} catch (error) {
|
||||
console.error('Error loading platforms:', error);
|
||||
await handleAdminError(error, 'Plattformen laden');
|
||||
setError('Plattformen konnten nicht geladen werden');
|
||||
} finally {
|
||||
setLoading(false);
|
||||
|
|
|
|||
|
|
@ -4,6 +4,7 @@ import { Container, Box } from '@mui/material';
|
|||
|
||||
// Services
|
||||
import { adminGet, adminRequest } from '../../services/adminApi';
|
||||
import { handleAdminError } from '../../services/adminErrorHandler';
|
||||
|
||||
// Components
|
||||
import Navbar from '../ComponentUtils/Headers/Navbar';
|
||||
|
|
@ -51,6 +52,7 @@ const ModerationGroupImagesPage = () => {
|
|||
|
||||
setGroup(transformedData);
|
||||
} catch (e) {
|
||||
await handleAdminError(e, 'Gruppe laden');
|
||||
setError('Fehler beim Laden der Gruppe');
|
||||
} finally {
|
||||
setLoading(false);
|
||||
|
|
|
|||
|
|
@ -7,6 +7,7 @@ import Swal from 'sweetalert2/dist/sweetalert2.js';
|
|||
|
||||
// Services
|
||||
import { adminGet, adminRequest, adminDownload } from '../../services/adminApi';
|
||||
import { handleAdminError } from '../../services/adminErrorHandler';
|
||||
|
||||
// Components
|
||||
import Navbar from '../ComponentUtils/Headers/Navbar';
|
||||
|
|
@ -40,7 +41,7 @@ const ModerationGroupsPage = () => {
|
|||
const data = await adminGet('/api/admin/social-media/platforms');
|
||||
setPlatforms(data);
|
||||
} 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);
|
||||
setGroups(data.groups);
|
||||
} catch (error) {
|
||||
console.error('Fehler beim Laden der Moderations-Gruppen:', error);
|
||||
await handleAdminError(error, 'Moderations-Gruppen laden');
|
||||
setError('Fehler beim Laden der Gruppen');
|
||||
} finally {
|
||||
setLoading(false);
|
||||
|
|
@ -101,7 +102,7 @@ const ModerationGroupsPage = () => {
|
|||
showConfirmButton: false
|
||||
});
|
||||
} catch (error) {
|
||||
console.error('Fehler beim Freigeben der Gruppe:', error);
|
||||
await handleAdminError(error, 'Gruppe freigeben');
|
||||
await Swal.fire({
|
||||
icon: 'error',
|
||||
title: 'Fehler',
|
||||
|
|
@ -139,9 +140,7 @@ const ModerationGroupsPage = () => {
|
|||
));
|
||||
|
||||
} catch (error) {
|
||||
console.error('Fehler beim Löschen des Bildes:', error);
|
||||
console.error('Error details:', error.message, error.stack);
|
||||
alert('Fehler beim Löschen des Bildes: ' + error.message);
|
||||
await handleAdminError(error, 'Bild löschen');
|
||||
}
|
||||
};
|
||||
|
||||
|
|
@ -159,8 +158,7 @@ const ModerationGroupsPage = () => {
|
|||
setShowImages(false);
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('Fehler beim Löschen der Gruppe:', error);
|
||||
alert('Fehler beim Löschen der Gruppe');
|
||||
await handleAdminError(error, 'Gruppe löschen');
|
||||
}
|
||||
};
|
||||
|
||||
|
|
@ -190,11 +188,7 @@ const ModerationGroupsPage = () => {
|
|||
showConfirmButton: false
|
||||
});
|
||||
} catch (error) {
|
||||
console.error('Fehler beim Export:', error);
|
||||
await Swal.fire({
|
||||
icon: 'error',
|
||||
title: 'Fehler',
|
||||
text: 'Fehler beim Export der Consent-Daten: ' + error.message
|
||||
await handleAdminError(error, 'Consent-Export');
|
||||
});
|
||||
}
|
||||
};
|
||||
|
|
|
|||
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