error-propagation: implement retry for failed mutations (MarketplaceHome, RolesPage, SettingsPage)
This commit is contained in:
parent
f929e8ebd0
commit
6e16fce1b9
3 changed files with 116 additions and 16 deletions
|
|
@ -1,4 +1,4 @@
|
|||
import { useState, useEffect } from 'react';
|
||||
import { useState, useEffect, useRef } from 'react';
|
||||
import { getRoles, deleteRole, updateRole } from '../services/roleService';
|
||||
import type { Role } from '../types/role';
|
||||
import { Button } from '@/components/ui/button';
|
||||
|
|
@ -29,6 +29,8 @@ export function RolesPage() {
|
|||
const [queryError, setQueryError] = useState<Error | null>(null);
|
||||
const [mutationError, setMutationError] = useState<Error | null>(null);
|
||||
const [editingRole, setEditingRole] = useState<Role | null>(null);
|
||||
const [retryCount, setRetryCount] = useState(0);
|
||||
const lastMutationRef = useRef<(() => Promise<void>) | null>(null);
|
||||
const [isEditModalOpen, setIsEditModalOpen] = useState(false);
|
||||
const [isAssignModalOpen, setIsAssignModalOpen] = useState(false);
|
||||
const [assignUserId, setAssignUserId] = useState<string>('');
|
||||
|
|
@ -54,13 +56,23 @@ export function RolesPage() {
|
|||
}, []);
|
||||
|
||||
const handleToggleActive = async (role: Role) => {
|
||||
try {
|
||||
setMutationError(null);
|
||||
// Action 3.4.1.3: Store mutation for retry
|
||||
const performMutation = async () => {
|
||||
await updateRole(role.id, { is_active: !role.is_active });
|
||||
toast.success(
|
||||
`Role ${!role.is_active ? 'activated' : 'deactivated'} successfully`,
|
||||
);
|
||||
loadRoles();
|
||||
setMutationError(null);
|
||||
setRetryCount(0);
|
||||
lastMutationRef.current = null;
|
||||
};
|
||||
|
||||
lastMutationRef.current = performMutation;
|
||||
setMutationError(null);
|
||||
|
||||
try {
|
||||
await performMutation();
|
||||
} catch (err) {
|
||||
const errorMessage =
|
||||
err instanceof Error ? err.message : 'Failed to update role';
|
||||
|
|
@ -74,11 +86,21 @@ export function RolesPage() {
|
|||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
setMutationError(null);
|
||||
// Action 3.4.1.3: Store mutation for retry
|
||||
const performMutation = async () => {
|
||||
await deleteRole(role.id);
|
||||
toast.success('Role deleted successfully');
|
||||
loadRoles();
|
||||
setMutationError(null);
|
||||
setRetryCount(0);
|
||||
lastMutationRef.current = null;
|
||||
};
|
||||
|
||||
lastMutationRef.current = performMutation;
|
||||
setMutationError(null);
|
||||
|
||||
try {
|
||||
await performMutation();
|
||||
} catch (err) {
|
||||
const errorMessage =
|
||||
err instanceof Error ? err.message : 'Failed to delete role';
|
||||
|
|
@ -86,6 +108,18 @@ export function RolesPage() {
|
|||
}
|
||||
};
|
||||
|
||||
// Action 3.4.1.3: Retry handler for failed mutations
|
||||
const handleRetry = async () => {
|
||||
if (!lastMutationRef.current || retryCount >= 3) return;
|
||||
|
||||
setRetryCount((prev) => prev + 1);
|
||||
try {
|
||||
await lastMutationRef.current();
|
||||
} catch (error) {
|
||||
// Error will be handled by the mutation function
|
||||
}
|
||||
};
|
||||
|
||||
const handleEdit = (role: Role) => {
|
||||
setEditingRole(role);
|
||||
setIsEditModalOpen(true);
|
||||
|
|
@ -135,7 +169,12 @@ export function RolesPage() {
|
|||
action: 'updating role',
|
||||
resource: 'roles',
|
||||
}}
|
||||
onDismiss={() => setMutationError(null)}
|
||||
onRetry={retryCount < 3 ? handleRetry : undefined}
|
||||
onDismiss={() => {
|
||||
setMutationError(null);
|
||||
setRetryCount(0);
|
||||
lastMutationRef.current = null;
|
||||
}}
|
||||
/>
|
||||
)}
|
||||
<div className="mb-6 flex items-center justify-between">
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
import { useState, useEffect } from 'react';
|
||||
import { useState, useEffect, useRef } from 'react';
|
||||
import { useAuthStore } from '@/features/auth/store/authStore';
|
||||
import { getSettings, updateSettings } from '../services/settingsService';
|
||||
import { UserSettings } from '../types/settings';
|
||||
|
|
@ -20,6 +20,8 @@ export function SettingsPage() {
|
|||
const [isSaving, setIsSaving] = useState(false);
|
||||
const [queryError, setQueryError] = useState<Error | null>(null);
|
||||
const [mutationError, setMutationError] = useState<Error | null>(null);
|
||||
const [retryCount, setRetryCount] = useState(0);
|
||||
const lastMutationRef = useRef<(() => Promise<void>) | null>(null);
|
||||
|
||||
const loadSettings = async () => {
|
||||
if (!user?.id) {
|
||||
|
|
@ -61,11 +63,21 @@ export function SettingsPage() {
|
|||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
setIsSaving(true);
|
||||
setMutationError(null);
|
||||
// Action 3.4.1.3: Store mutation for retry
|
||||
const performMutation = async () => {
|
||||
await updateSettings(user.id, settings);
|
||||
toast.success('Paramètres sauvegardés avec succès');
|
||||
setMutationError(null);
|
||||
setRetryCount(0);
|
||||
lastMutationRef.current = null;
|
||||
};
|
||||
|
||||
lastMutationRef.current = performMutation;
|
||||
setIsSaving(true);
|
||||
setMutationError(null);
|
||||
|
||||
try {
|
||||
await performMutation();
|
||||
} catch (err) {
|
||||
const errorMessage =
|
||||
err instanceof Error ? err.message : 'Erreur lors de la sauvegarde';
|
||||
|
|
@ -75,6 +87,21 @@ export function SettingsPage() {
|
|||
}
|
||||
};
|
||||
|
||||
// Action 3.4.1.3: Retry handler for failed mutations
|
||||
const handleRetry = async () => {
|
||||
if (!lastMutationRef.current || retryCount >= 3) return;
|
||||
|
||||
setRetryCount((prev) => prev + 1);
|
||||
setIsSaving(true);
|
||||
try {
|
||||
await lastMutationRef.current();
|
||||
} catch (error) {
|
||||
// Error will be handled by the mutation function
|
||||
} finally {
|
||||
setIsSaving(false);
|
||||
}
|
||||
};
|
||||
|
||||
if (isLoading) {
|
||||
return (
|
||||
<div className="flex items-center justify-center min-h-[400px]">
|
||||
|
|
@ -129,7 +156,12 @@ export function SettingsPage() {
|
|||
action: 'saving settings',
|
||||
resource: 'settings',
|
||||
}}
|
||||
onDismiss={() => setMutationError(null)}
|
||||
onRetry={retryCount < 3 ? handleRetry : undefined}
|
||||
onDismiss={() => {
|
||||
setMutationError(null);
|
||||
setRetryCount(0);
|
||||
lastMutationRef.current = null;
|
||||
}}
|
||||
/>
|
||||
)}
|
||||
<div className="max-w-4xl mx-auto">
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
import { useState, useEffect } from 'react';
|
||||
import { useState, useEffect, useRef } from 'react';
|
||||
import { marketplaceService } from '@/services/marketplaceService';
|
||||
import { ProductCard } from '@/features/marketplace/components/ProductCard';
|
||||
import { Product, ProductType } from '@/types/marketplace';
|
||||
|
|
@ -27,6 +27,8 @@ export function MarketplaceHome() {
|
|||
const [queryError, setQueryError] = useState<Error | null>(null);
|
||||
const [mutationError, setMutationError] = useState<Error | null>(null);
|
||||
const [purchasingProductId, setPurchasingProductId] = useState<string | null>(null);
|
||||
const [retryCount, setRetryCount] = useState(0);
|
||||
const lastMutationRef = useRef<(() => Promise<void>) | null>(null);
|
||||
const [isCartOpen, setIsCartOpen] = useState(false);
|
||||
const [page, setPage] = useState(1);
|
||||
const [limit] = useState(12);
|
||||
|
|
@ -94,12 +96,22 @@ export function MarketplaceHome() {
|
|||
};
|
||||
|
||||
// CRITIQUE FIX #70: Gestion d'erreur améliorée pour purchaseProduct
|
||||
// Action 3.4.1.3: Store mutation for retry
|
||||
const handlePurchase = async (product: Product) => {
|
||||
try {
|
||||
setPurchasingProductId(product.id);
|
||||
setMutationError(null);
|
||||
const performMutation = async () => {
|
||||
await marketplaceService.purchaseProduct(product.id);
|
||||
toast.success(`Successfully purchased ${product.title}`);
|
||||
setMutationError(null);
|
||||
setRetryCount(0);
|
||||
lastMutationRef.current = null;
|
||||
};
|
||||
|
||||
lastMutationRef.current = performMutation;
|
||||
setPurchasingProductId(product.id);
|
||||
setMutationError(null);
|
||||
|
||||
try {
|
||||
await performMutation();
|
||||
} catch (error: unknown) {
|
||||
// CRITIQUE FIX #70: Gestion d'erreur améliorée avec message détaillé
|
||||
const apiError = parseApiError(error);
|
||||
|
|
@ -113,6 +125,18 @@ export function MarketplaceHome() {
|
|||
}
|
||||
};
|
||||
|
||||
// Action 3.4.1.3: Retry handler for failed mutations
|
||||
const handleRetry = async () => {
|
||||
if (!lastMutationRef.current || retryCount >= 3) return;
|
||||
|
||||
setRetryCount((prev) => prev + 1);
|
||||
try {
|
||||
await lastMutationRef.current();
|
||||
} catch (error) {
|
||||
// Error will be handled by the mutation function
|
||||
}
|
||||
};
|
||||
|
||||
const handleClearFilters = () => {
|
||||
setSearchQuery('');
|
||||
setProductType('');
|
||||
|
|
@ -159,7 +183,12 @@ export function MarketplaceHome() {
|
|||
action: 'purchasing product',
|
||||
resource: 'marketplace',
|
||||
}}
|
||||
onDismiss={() => setMutationError(null)}
|
||||
onRetry={retryCount < 3 ? handleRetry : undefined}
|
||||
onDismiss={() => {
|
||||
setMutationError(null);
|
||||
setRetryCount(0);
|
||||
lastMutationRef.current = null;
|
||||
}}
|
||||
/>
|
||||
)}
|
||||
<div className="mb-6 flex items-center justify-between">
|
||||
|
|
|
|||
Loading…
Reference in a new issue