diff --git a/frontend/src/pages/WorkoutEditPage.css b/frontend/src/pages/WorkoutEditPage.css index 28c09ff..4d24f44 100644 --- a/frontend/src/pages/WorkoutEditPage.css +++ b/frontend/src/pages/WorkoutEditPage.css @@ -434,3 +434,53 @@ max-width: 90%; } } + +/* Spinner Animation for Save Loading */ +@keyframes spin { + from { + transform: rotate(0deg); + } + to { + transform: rotate(360deg); + } +} + +/* Apply spinner animation to Icon component with spinner class */ +.save-header-btn svg[class*="spinner"], +.save-header-btn .icon-spinner { + animation: spin 1s linear infinite; +} + +/* Success Checkmark Animation */ +@keyframes slideInCheckmark { + 0% { + opacity: 0; + transform: scale(0.8); + } + 100% { + opacity: 1; + transform: scale(1); + } +} + +.sync-status.saved { + animation: slideInCheckmark 0.3s ease-out; +} + +/* Ensure error actions align properly on mobile */ +@media (max-width: 480px) { + .error-banner { + flex-direction: column; + align-items: flex-start; + } + + .error-message { + width: 100%; + margin-bottom: 0.75rem; + } + + .error-actions { + width: 100%; + justify-content: space-between; + } +} diff --git a/frontend/src/pages/WorkoutEditPage.jsx b/frontend/src/pages/WorkoutEditPage.jsx index 8f461b5..46101b1 100644 --- a/frontend/src/pages/WorkoutEditPage.jsx +++ b/frontend/src/pages/WorkoutEditPage.jsx @@ -14,6 +14,8 @@ export default function WorkoutEditPage({ workout, onBack, onSave }) { const [error, setError] = useState(null) const [syncStatus, setSyncStatus] = useState('idle') // idle | saving | saved | error const [draftPromptShown, setDraftPromptShown] = useState(false) + const [retryCount, setRetryCount] = useState(0) + const [lastSavePayload, setLastSavePayload] = useState(null) // Show draft recovery prompt on first render const handleRecoverDraft = () => { @@ -72,10 +74,41 @@ export default function WorkoutEditPage({ workout, onBack, onSave }) { if (error) setError(null) } + /** + * Determine specific error message based on error type + */ + const getErrorMessage = (err) => { + // Network errors + if (!err || err instanceof TypeError && err.message.includes('fetch')) { + return 'Anslutning misslyckades. Försök igen?' + } + + // Check if error has a response (API error) + if (err.status) { + if (err.status === 400) { + return 'Ogiltiga ändringar. Kontrollera dina inmatningar.' + } + if (err.status === 401 || err.status === 403) { + return 'Du har inte behörighet att spara denna träning.' + } + if (err.status >= 500) { + return 'Serverfel. Försök igen senare.' + } + if (err.status >= 400) { + return 'Ett fel uppstod när träningen skulle sparas. Försök igen.' + } + } + + // Fallback + return err.message || 'Sparning misslyckades. Försök igen.' + } + const handleSave = async () => { setSaving(true) setSyncStatus('saving') setError(null) + setRetryCount(prev => prev + 1) + try { // Format for API const payload = { @@ -86,25 +119,55 @@ export default function WorkoutEditPage({ workout, onBack, onSave }) { reps_max: parseInt(ex.reps_max) || 12 })) } + + // Store payload for potential retry + setLastSavePayload(payload) + + // Call the save callback await onSave(workout.id, payload) // Success: clear draft and show confirmation clearDraft() setSyncStatus('saved') + setRetryCount(0) // Reset retry count on success + + // Log success + console.log('Workout saved successfully', { + workoutId: workout.id, + exerciseCount: exercises.length, + retryCount + }) // Reset status after 2 seconds setTimeout(() => setSyncStatus('idle'), 2000) } catch (err) { - console.error('Failed to save workout:', err) - setError(err.message || 'Sparning misslyckades. Försök igen.') + // Log error with context for debugging + console.error('Failed to save workout:', { + error: err, + workoutId: workout.id, + exerciseCount: exercises.length, + retryCount, + payload: lastSavePayload + }) + + // Determine error message based on error type + const errorMessage = getErrorMessage(err) + setError(errorMessage) setSyncStatus('error') + // Keep draft on error so user doesn't lose work + // (useDraftWorkout already auto-saves, so no action needed here) } finally { setSaving(false) } } const handleRetry = () => { + // Log retry attempt + console.log('User retrying save', { + workoutId: workout.id, + retryCount + }) handleSave() }