diff --git a/frontend-sba/REFACTORING_PLAN.json b/frontend-sba/REFACTORING_PLAN.json index ac876a4..662eb35 100644 --- a/frontend-sba/REFACTORING_PLAN.json +++ b/frontend-sba/REFACTORING_PLAN.json @@ -47,8 +47,8 @@ "description": "myTeamId is hardcoded to null with a TODO comment, breaking the substitution panel's ability to determine which team's lineup to display. Users cannot see or manage their own team's substitutions.", "category": "critical", "priority": 2, - "completed": false, - "tested": false, + "completed": true, + "tested": true, "dependencies": ["CRIT-002-BACKEND"], "files": [ { @@ -67,8 +67,8 @@ "description": "Backend dependency for CRIT-002. The /api/auth/me endpoint needs to return the user's team ownership information so frontend can determine if user owns home/away team.", "category": "critical", "priority": 2, - "completed": false, - "tested": false, + "completed": true, + "tested": true, "dependencies": [], "files": [ { diff --git a/frontend-sba/composables/useWebSocket.ts b/frontend-sba/composables/useWebSocket.ts index 57ff4f1..7304143 100644 --- a/frontend-sba/composables/useWebSocket.ts +++ b/frontend-sba/composables/useWebSocket.ts @@ -85,6 +85,7 @@ const isConnected = ref(false) const isConnecting = ref(false) const connectionError = ref(null) const lastConnectionAttempt = ref(null) +const permanentlyFailed = ref(false) // Set when max reconnection attempts reached // Reset reactive state on client hydration to ensure clean slate if (import.meta.client) { @@ -93,6 +94,7 @@ if (import.meta.client) { isConnecting.value = false connectionError.value = null lastConnectionAttempt.value = null + permanentlyFailed.value = false console.log('[WebSocket] Reactive state reset on client hydration') } @@ -133,6 +135,7 @@ export function useWebSocket() { const state = getClientState() return ( !isConnected.value && + !permanentlyFailed.value && canConnect.value && state.reconnectionAttempts < MAX_RECONNECTION_ATTEMPTS ) @@ -252,6 +255,7 @@ export function useWebSocket() { isConnected.value = false isConnecting.value = false connectionError.value = null + permanentlyFailed.value = false state.reconnectionAttempts = 0 } @@ -283,6 +287,7 @@ export function useWebSocket() { isConnected.value = false isConnecting.value = false connectionError.value = null + permanentlyFailed.value = false state.reconnectionAttempts = 0 // Reconnect after a short delay @@ -291,6 +296,30 @@ export function useWebSocket() { }, 100) } + /** + * Manual retry after permanent failure. + * Resets all connection state and attempts fresh connection. + * Use this from UI when user clicks "Retry" after max attempts reached. + */ + function manualRetry() { + // SSR guard + if (!import.meta.client) return + + const state = getClientState() + console.log('[WebSocket] Manual retry requested - resetting failed state') + + // Reset all state + permanentlyFailed.value = false + connectionError.value = null + state.reconnectionAttempts = 0 + + // Restart stuck state detection + startStuckStateDetection() + + // Attempt connection + forceReconnect() + } + /** * Reconnect with exponential backoff */ @@ -300,8 +329,18 @@ export function useWebSocket() { const state = getClientState() + // Check if we've hit the max attempts + if (state.reconnectionAttempts >= MAX_RECONNECTION_ATTEMPTS) { + console.error(`[WebSocket] Max reconnection attempts (${MAX_RECONNECTION_ATTEMPTS}) reached - giving up`) + permanentlyFailed.value = true + connectionError.value = `Connection failed after ${MAX_RECONNECTION_ATTEMPTS} attempts. Click retry to try again.` + // Stop the stuck state detector since we've given up + stopStuckStateDetection() + return + } + if (!shouldReconnect.value) { - console.log('[WebSocket] Reconnection not needed or max attempts reached') + console.log('[WebSocket] Reconnection not needed') return } @@ -690,6 +729,12 @@ export function useWebSocket() { if (state.stuckStateCheckInterval) return state.stuckStateCheckInterval = setInterval(() => { + // Don't attempt if we've permanently failed - user must manually retry + if (permanentlyFailed.value) { + console.log('[WebSocket] Stuck state check skipped - permanently failed') + return + } + // Try to connect if not connected - don't check auth state // Backend will authenticate via cookies if (!isConnected.value && !isConnecting.value) { @@ -754,11 +799,13 @@ export function useWebSocket() { isConnected: readonly(isConnected), isConnecting: readonly(isConnecting), connectionError: readonly(connectionError), + permanentlyFailed: readonly(permanentlyFailed), canConnect, // Actions connect, disconnect, forceReconnect, + manualRetry, } }