1- import React , { useReducer , useState , useEffect } from 'react' ;
1+ import React , { useReducer , useState , useEffect , useCallback } from 'react' ;
22import { useParams , useNavigate } from 'react-router-dom' ;
3- import api from '../../helper/api' ;
3+ import api , { extractErrorMessage } from '../../helper/api' ;
44import Container from '@mui/material/Container' ;
55import Typography from '@mui/material/Typography' ;
66import Box from '@mui/material/Box' ;
@@ -11,6 +11,8 @@ import Divider from '@mui/material/Divider';
1111import TableSortLabel from '@mui/material/TableSortLabel' ;
1212import Button from '@mui/material/Button' ;
1313import CircularProgress from '@mui/material/CircularProgress' ;
14+ import Alert from '@mui/material/Alert' ;
15+ import RefreshIcon from '@mui/icons-material/Refresh' ;
1416
1517import PersonOutlineIcon from '@mui/icons-material/PersonOutlined' ;
1618import ForumIcon from '@mui/icons-material/Forum' ;
@@ -28,13 +30,13 @@ import { convertTimestamp } from '../../helper/converter';
2830import ForumBreadcrumbs from '../../components/Forum/ForumBreadcrumbs' ;
2931import ForumChips from '../../components/Forum/ForumChips' ;
3032
31- const initialFetchState = { posts : [ ] , topicData : null , loading : true } ;
33+ const initialFetchState = { posts : [ ] , topicData : null , loading : true , error : null , errorStatus : null } ;
3234
3335function fetchReducer ( state , action ) {
3436 switch ( action . type ) {
35- case 'FETCH_START' : return { ...state , loading : true } ;
36- case 'FETCH_SUCCESS' : return { posts : action . posts , topicData : action . topicData , loading : false } ;
37- case 'FETCH_ERROR' : return { ...state , loading : false } ;
37+ case 'FETCH_START' : return { ...state , loading : true , error : null , errorStatus : null } ;
38+ case 'FETCH_SUCCESS' : return { posts : action . posts , topicData : action . topicData , loading : false , error : null , errorStatus : null } ;
39+ case 'FETCH_ERROR' : return { ...state , loading : false , error : action . payload , errorStatus : action . status } ;
3840 default : return state ;
3941 }
4042}
@@ -43,10 +45,10 @@ export default function Forum_Topic() {
4345 const { id } = useParams ( ) ;
4446 const navigate = useNavigate ( ) ;
4547 const [ fetchState , dispatch ] = useReducer ( fetchReducer , initialFetchState ) ;
46- const { posts, topicData, loading } = fetchState ;
48+ const { posts, topicData, loading, error , errorStatus } = fetchState ;
4749 const [ sort , setSort ] = useState ( { field : 'creationDate' , direction : 'asc' } ) ;
4850
49- useEffect ( ( ) => {
51+ const fetchTopicData = useCallback ( ( ) => {
5052 const controller = new AbortController ( ) ;
5153 dispatch ( { type : 'FETCH_START' } ) ;
5254
@@ -70,14 +72,23 @@ export default function Forum_Topic() {
7072 . catch ( error => {
7173 if ( error . code === 'ERR_CANCELED' ) return ;
7274 console . error ( error ) ;
73- dispatch ( { type : 'FETCH_ERROR' } ) ;
75+ dispatch ( {
76+ type : 'FETCH_ERROR' ,
77+ payload : extractErrorMessage ( error ) ,
78+ status : error . response ?. status || 0 ,
79+ } ) ;
7480 } ) ;
7581
7682 return ( ) => {
7783 controller . abort ( ) ;
7884 } ;
7985 } , [ id ] ) ;
8086
87+ useEffect ( ( ) => {
88+ const cleanup = fetchTopicData ( ) ;
89+ return cleanup ;
90+ } , [ fetchTopicData ] ) ;
91+
8192 const handleSort = ( field ) => {
8293 setSort ( prev => ( {
8394 field,
@@ -102,6 +113,30 @@ export default function Forum_Topic() {
102113 ) ;
103114 }
104115
116+ if ( error ) {
117+ const isRateLimit = errorStatus === 429 ;
118+ return (
119+ < Container maxWidth = "sm" sx = { { py : 8 , textAlign : 'center' } } >
120+ < Alert
121+ severity = { isRateLimit ? 'warning' : 'error' }
122+ sx = { { mb : 3 } }
123+ action = {
124+ < Button color = "inherit" size = "small" startIcon = { < RefreshIcon /> } onClick = { fetchTopicData } >
125+ Erneut versuchen
126+ </ Button >
127+ }
128+ >
129+ { isRateLimit
130+ ? 'Zu viele Anfragen — bitte warte einen Moment.'
131+ : `Fehler beim Laden des Themas: ${ error } ` }
132+ </ Alert >
133+ < Button startIcon = { < ArrowBackIcon /> } onClick = { ( ) => navigate ( - 1 ) } sx = { { mt : 1 } } >
134+ Zurück
135+ </ Button >
136+ </ Container >
137+ ) ;
138+ }
139+
105140 if ( ! topicData || ! topicData . id ) {
106141 return (
107142 < Container sx = { { py : 8 , textAlign : 'center' } } >
0 commit comments