diff --git a/src/components/admin/NovuMigrationUtility.tsx b/src/components/admin/NovuMigrationUtility.tsx index 5c3d2e7c..960a73e0 100644 --- a/src/components/admin/NovuMigrationUtility.tsx +++ b/src/components/admin/NovuMigrationUtility.tsx @@ -107,11 +107,11 @@ export function NovuMigrationUtility(): React.JSX.Element { {isRunning && totalUsers > 0 && ( diff --git a/src/components/admin/ParkForm.tsx b/src/components/admin/ParkForm.tsx index f26a7a8b..9188b5cc 100644 --- a/src/components/admin/ParkForm.tsx +++ b/src/components/admin/ParkForm.tsx @@ -140,6 +140,7 @@ export function ParkForm({ onSubmit, onCancel, initialData, isEditing = false }: }, [onSubmit]); const { user } = useAuth(); + const [isSubmitting, setIsSubmitting] = useState(false); // Operator state const [selectedOperatorId, setSelectedOperatorId] = useState(initialData?.operator_id || ''); @@ -198,7 +199,8 @@ export function ParkForm({ onSubmit, onCancel, initialData, isEditing = false }: }, [operatorIsOwner, selectedOperatorId, setValue]); - const handleFormSubmit = async (data: ParkFormData) => { + const handleFormSubmit = async (data: ParkFormData) => { + setIsSubmitting(true); try { // CRITICAL: Block new photo uploads on edits if (isEditing && data.images?.uploaded) { @@ -277,6 +279,8 @@ export function ParkForm({ onSubmit, onCancel, initialData, isEditing = false }: // Re-throw so parent can handle modal closing throw error; + } finally { + setIsSubmitting(false); } }; @@ -647,9 +651,17 @@ export function ParkForm({ onSubmit, onCancel, initialData, isEditing = false }: {isEditing ? 'Update Park' : 'Create Park'} + {onCancel && ( - diff --git a/src/components/admin/RideForm.tsx b/src/components/admin/RideForm.tsx index 5e910256..2720eafb 100644 --- a/src/components/admin/RideForm.tsx +++ b/src/components/admin/RideForm.tsx @@ -158,6 +158,7 @@ export function RideForm({ onSubmit, onCancel, initialData, isEditing = false }: const { isModerator } = useUserRole(); const { preferences } = useUnitPreferences(); const measurementSystem = preferences.measurement_system; + const [isSubmitting, setIsSubmitting] = useState(false); // Validate that onSubmit uses submission helpers (dev mode only) useEffect(() => { @@ -262,7 +263,8 @@ export function RideForm({ onSubmit, onCancel, initialData, isEditing = false }: const selectedCategory = watch('category'); - const handleFormSubmit = async (data: RideFormData) => { + const handleFormSubmit = async (data: RideFormData) => { + setIsSubmitting(true); try { // CRITICAL: Block new photo uploads on edits if (isEditing && data.images?.uploaded) { @@ -355,6 +357,8 @@ export function RideForm({ onSubmit, onCancel, initialData, isEditing = false }: // Re-throw so parent can handle modal closing throw error; + } finally { + setIsSubmitting(false); } }; @@ -1355,13 +1359,15 @@ export function RideForm({ onSubmit, onCancel, initialData, isEditing = false }: {onCancel && ( - diff --git a/src/components/auth/AuthDiagnostics.tsx b/src/components/auth/AuthDiagnostics.tsx index 6573dfbb..3c4ca7c7 100644 --- a/src/components/auth/AuthDiagnostics.tsx +++ b/src/components/auth/AuthDiagnostics.tsx @@ -31,32 +31,38 @@ interface AuthDiagnosticsData { export function AuthDiagnostics() { const [diagnostics, setDiagnostics] = useState(null); const [isOpen, setIsOpen] = useState(false); + const [isRefreshing, setIsRefreshing] = useState(false); const runDiagnostics = async () => { - const storageStatus = authStorage.getStorageStatus(); - const { data: { session }, error: sessionError } = await supabase.auth.getSession(); + setIsRefreshing(true); + try { + const storageStatus = authStorage.getStorageStatus(); + const { data: { session }, error: sessionError } = await supabase.auth.getSession(); - const results = { - timestamp: new Date().toISOString(), - storage: storageStatus, - session: { - exists: !!session, - user: session?.user?.email || null, - expiresAt: session?.expires_at || null, - error: sessionError?.message || null, - }, - network: { - online: navigator.onLine, - }, - environment: { - url: window.location.href, - isIframe: window.self !== window.top, - cookiesEnabled: navigator.cookieEnabled, - } - }; - - setDiagnostics(results); - logger.debug('Auth diagnostics', { results }); + const results = { + timestamp: new Date().toISOString(), + storage: storageStatus, + session: { + exists: !!session, + user: session?.user?.email || null, + expiresAt: session?.expires_at || null, + error: sessionError?.message || null, + }, + network: { + online: navigator.onLine, + }, + environment: { + url: window.location.href, + isIframe: window.self !== window.top, + cookiesEnabled: navigator.cookieEnabled, + } + }; + + setDiagnostics(results); + logger.debug('Auth diagnostics', { results }); + } finally { + setIsRefreshing(false); + } }; useEffect(() => { @@ -119,7 +125,7 @@ export function AuthDiagnostics() { ⚠️ Running in iframe - storage may be restricted )} - diff --git a/src/components/moderation/ConflictResolutionDialog.tsx b/src/components/moderation/ConflictResolutionDialog.tsx index e229fb3e..89bfe59d 100644 --- a/src/components/moderation/ConflictResolutionDialog.tsx +++ b/src/components/moderation/ConflictResolutionDialog.tsx @@ -25,6 +25,7 @@ export function ConflictResolutionDialog({ onResolve, }: ConflictResolutionDialogProps) { const [resolutions, setResolutions] = useState>({}); + const [isApplying, setIsApplying] = useState(false); const { user } = useAuth(); const handleResolutionChange = (itemId: string, action: string) => { @@ -44,6 +45,7 @@ export function ConflictResolutionDialog({ return; } + setIsApplying(true); const { resolveConflicts } = await import('@/lib/conflictResolutionService'); try { @@ -67,6 +69,8 @@ export function ConflictResolutionDialog({ userId: user.id, metadata: { conflictCount: conflicts.length } }); + } finally { + setIsApplying(false); } }; @@ -119,10 +123,10 @@ export function ConflictResolutionDialog({ - - diff --git a/src/components/profile/AddRideCreditDialog.tsx b/src/components/profile/AddRideCreditDialog.tsx index f4622e7e..4b39f1ee 100644 --- a/src/components/profile/AddRideCreditDialog.tsx +++ b/src/components/profile/AddRideCreditDialog.tsx @@ -182,8 +182,8 @@ export function AddRideCreditDialog({ userId, open, onOpenChange, onSuccess }: A > Cancel - diff --git a/src/components/reviews/ReviewForm.tsx b/src/components/reviews/ReviewForm.tsx index 1846d662..e983d869 100644 --- a/src/components/reviews/ReviewForm.tsx +++ b/src/components/reviews/ReviewForm.tsx @@ -199,9 +199,9 @@ export function ReviewForm({ {/* Photo Upload */} - diff --git a/src/components/settings/EmailChangeDialog.tsx b/src/components/settings/EmailChangeDialog.tsx index 6b57ab2b..659c6e6d 100644 --- a/src/components/settings/EmailChangeDialog.tsx +++ b/src/components/settings/EmailChangeDialog.tsx @@ -363,8 +363,7 @@ export function EmailChangeDialog({ open, onOpenChange, currentEmail, userId }: - diff --git a/src/components/settings/LocationTab.tsx b/src/components/settings/LocationTab.tsx index 9ba0e25c..9a64afe9 100644 --- a/src/components/settings/LocationTab.tsx +++ b/src/components/settings/LocationTab.tsx @@ -558,8 +558,8 @@ export function LocationTab() { {/* Save Button */}
-
diff --git a/src/components/settings/PasswordUpdateDialog.tsx b/src/components/settings/PasswordUpdateDialog.tsx index 5b1f8095..860a3eec 100644 --- a/src/components/settings/PasswordUpdateDialog.tsx +++ b/src/components/settings/PasswordUpdateDialog.tsx @@ -493,8 +493,7 @@ export function PasswordUpdateDialog({ open, onOpenChange, onSuccess }: Password > Back - diff --git a/src/components/settings/PrivacyTab.tsx b/src/components/settings/PrivacyTab.tsx index 8c7075e3..883e6342 100644 --- a/src/components/settings/PrivacyTab.tsx +++ b/src/components/settings/PrivacyTab.tsx @@ -450,8 +450,8 @@ export function PrivacyTab() { {/* Save Button */}
-