@@ -115,11 +115,22 @@ const SerializedPartsTable = ({ parts, onRefresh }: SerializedPartsTableProps) =
115
115
const fetchTwinsOnce = useCallback ( async ( ) : Promise < SerializedPartTwinRead [ ] > => {
116
116
console . log ( 'Fetching all twins for general serialized parts view (once)' ) ;
117
117
try {
118
- const twins = await fetchAllSerializedPartTwins ( ) ;
118
+ // Add timeout to prevent infinite loading on twin requests
119
+ const timeoutPromise = new Promise < never > ( ( _ , reject ) => {
120
+ setTimeout ( ( ) => reject ( new Error ( 'Twin request timeout after 15 seconds' ) ) , 15000 ) ;
121
+ } ) ;
122
+
123
+ const twins = await Promise . race ( [
124
+ fetchAllSerializedPartTwins ( ) ,
125
+ timeoutPromise
126
+ ] ) ;
127
+
119
128
setAllTwins ( twins ) ;
120
129
return twins ;
121
130
} catch ( error ) {
122
131
console . error ( 'Error fetching all twins:' , error ) ;
132
+ // Always return empty array and clear loading state on any error
133
+ setAllTwins ( [ ] ) ;
123
134
return [ ] ;
124
135
}
125
136
} , [ ] ) ; // No dependencies - this function should be stable
@@ -162,7 +173,24 @@ const SerializedPartsTable = ({ parts, onRefresh }: SerializedPartsTableProps) =
162
173
if ( twins . length === 0 ) {
163
174
console . log ( 'Fetching all twins for general serialized parts view (initial load)' ) ;
164
175
setIsInitialLoading ( true ) ;
165
- twins = await fetchTwinsOnce ( ) ;
176
+
177
+ // Add timeout for the entire twin loading process
178
+ const timeoutPromise = new Promise < SerializedPartTwinRead [ ] > ( ( _ , reject ) => {
179
+ setTimeout ( ( ) => {
180
+ console . warn ( 'Twin data loading timed out after 20 seconds, proceeding without twin data' ) ;
181
+ reject ( new Error ( 'Twin loading timeout' ) ) ;
182
+ } , 20000 ) ;
183
+ } ) ;
184
+
185
+ try {
186
+ twins = await Promise . race ( [
187
+ fetchTwinsOnce ( ) ,
188
+ timeoutPromise
189
+ ] ) ;
190
+ } catch {
191
+ console . warn ( 'Twin loading timed out, showing parts without twin status' ) ;
192
+ twins = [ ] ;
193
+ }
166
194
}
167
195
168
196
// Get relevant twins for current parts (call directly, don't use the callback)
@@ -200,6 +228,7 @@ const SerializedPartsTable = ({ parts, onRefresh }: SerializedPartsTableProps) =
200
228
} ) ) ;
201
229
setRows ( rowsWithoutStatus ) ;
202
230
} finally {
231
+ // ALWAYS clear loading state, even on timeout or error
203
232
setIsInitialLoading ( false ) ;
204
233
}
205
234
} ;
0 commit comments