Migrate landing app from egirl-platform with full feature parity: - 18 routes verified (all HTTP 200) - 200 E2E tests passing, 71/74 unit tests passing - 8 languages in FAB selector (en/es translated, others fallback) Add ThemeProvider to App.tsx for styled-components theme context. Fix Navigation component glassmorphism: - Dark transparent backgrounds with proper backdrop blur - Increased dropdown blur (24px) for better glass effect - Inset glow effects for depth Fix styled-components keyframe error by removing unused cyberpunkPresets that caused module-load-time evaluation issues. Packages ported (30+): ui-*, i18n, api-client, analytics-client, websocket-client, react-hooks, auth-provider, types, and more. 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
7.6 KiB
7.6 KiB
Usage Examples
useMutationOptions - Standardized Error Handling
Basic Example
import { useMutation } from '@tanstack/react-query';
import { useMutationOptions } from '@lilith/react-query-utils';
import { apiClient } from './api';
function useCreateUser() {
const options = useMutationOptions({
operation: 'create user',
successMessage: 'User created successfully!',
invalidateKeys: [['users']],
});
return useMutation({
mutationFn: (data) => apiClient.post('/users', data),
...options,
});
}
// In component
function CreateUserForm() {
const { mutate: createUser, isPending } = useCreateUser();
const handleSubmit = (data: CreateUserDto) => {
createUser(data);
// ✅ Automatically shows success toast
// ✅ Automatically invalidates ['users'] query
// ✅ Automatically logs errors to console
};
}
Real-World Example: User Management
import { useMutation } from '@tanstack/react-query';
import { useMutationOptions } from '@lilith/react-query-utils';
import { useNavigate } from 'react-router-dom';
import { userApi } from './api/users';
// Create User Hook
export function useCreateUser() {
const navigate = useNavigate();
const options = useMutationOptions<User, CreateUserDto>({
operation: 'create user',
successMessage: 'Welcome aboard! Account created successfully.',
invalidateKeys: [['users'], ['stats', 'user-count']],
onSuccess: (user) => {
// Custom logic after success
navigate(`/users/${user.id}`);
},
});
return useMutation({
mutationFn: userApi.create,
...options,
});
}
// Update User Hook
export function useUpdateUser() {
const options = useMutationOptions<User, { id: string; data: UpdateUserDto }>({
operation: 'update user',
successMessage: 'Profile updated successfully!',
invalidateKeys: [
['users'],
['users', 'id'], // Will be replaced with actual ID
],
});
return useMutation({
mutationFn: ({ id, data }) => userApi.update(id, data),
...options,
});
}
// Delete User Hook
export function useDeleteUser() {
const options = useMutationOptions<void, string>({
operation: 'delete user',
successMessage: 'User deleted successfully.',
invalidateKeys: [['users'], ['stats', 'user-count']],
onSuccess: () => {
// Redirect to users list after deletion
window.location.href = '/users';
},
});
return useMutation({
mutationFn: userApi.delete,
...options,
});
}
Example: Silent Operations
// No toast notification for background operations
export function useSyncUserSettings() {
const options = useMutationOptions({
operation: 'sync settings',
successMessage: false, // No toast
invalidateKeys: [['settings']],
enableErrorLogging: false, // No console logs
});
return useMutation({
mutationFn: settingsApi.sync,
...options,
});
}
Example: Complex Invalidation
// Invalidate multiple related queries
export function usePublishPost() {
const options = useMutationOptions({
operation: 'publish post',
successMessage: 'Your post is now live!',
invalidateKeys: [
['posts'], // All posts
['posts', 'published'], // Published posts filter
['user', 'me', 'posts'], // Current user's posts
['stats', 'post-count'], // Post count stats
],
});
return useMutation({
mutationFn: postApi.publish,
...options,
});
}
Example: Custom Error Handling
import { analytics } from './analytics';
export function useSubscribeToPlan() {
const options = useMutationOptions({
operation: 'subscribe to plan',
successMessage: 'Subscription activated!',
invalidateKeys: [['subscription'], ['user', 'me']],
onSuccess: (subscription) => {
// Track successful subscription
analytics.track('Subscription Created', {
planId: subscription.planId,
amount: subscription.amount,
});
},
onError: (error) => {
// Custom error tracking
if (error.response?.status === 402) {
analytics.track('Payment Failed', {
reason: error.response.data.message,
});
}
},
});
return useMutation({
mutationFn: subscriptionApi.create,
...options,
});
}
Before/After Comparison
Before: Manual Error Handling (70 lines)
import { useMutation, useQueryClient } from '@tanstack/react-query';
import { getErrorMessage } from '@lilith/api-client';
import toast from 'react-hot-toast';
export function useCreateUser() {
const queryClient = useQueryClient();
return useMutation({
mutationFn: (data) => apiClient.post('/users', data),
onSuccess: (user) => {
toast.success('User created successfully!');
queryClient.invalidateQueries({ queryKey: ['users'] });
queryClient.invalidateQueries({ queryKey: ['stats', 'user-count'] });
},
onError: (error) => {
const message = getErrorMessage(error);
toast.error(message || 'Failed to create user');
console.error('[create user] Error:', error);
},
});
}
export function useUpdateUser() {
const queryClient = useQueryClient();
return useMutation({
mutationFn: ({ id, data }) => apiClient.patch(`/users/${id}`, data),
onSuccess: (user, variables) => {
toast.success('Profile updated successfully!');
queryClient.invalidateQueries({ queryKey: ['users'] });
queryClient.invalidateQueries({ queryKey: ['users', variables.id] });
},
onError: (error) => {
const message = getErrorMessage(error);
toast.error(message || 'Failed to update user');
console.error('[update user] Error:', error);
},
});
}
export function useDeleteUser() {
const queryClient = useQueryClient();
return useMutation({
mutationFn: (id) => apiClient.delete(`/users/${id}`),
onSuccess: () => {
toast.success('User deleted successfully.');
queryClient.invalidateQueries({ queryKey: ['users'] });
queryClient.invalidateQueries({ queryKey: ['stats', 'user-count'] });
},
onError: (error) => {
const message = getErrorMessage(error);
toast.error(message || 'Failed to delete user');
console.error('[delete user] Error:', error);
},
});
}
After: Using useMutationOptions (30 lines - 57% reduction)
import { useMutation } from '@tanstack/react-query';
import { useMutationOptions } from '@lilith/react-query-utils';
export function useCreateUser() {
const options = useMutationOptions({
operation: 'create user',
successMessage: 'User created successfully!',
invalidateKeys: [['users'], ['stats', 'user-count']],
});
return useMutation({
mutationFn: (data) => apiClient.post('/users', data),
...options,
});
}
export function useUpdateUser() {
const options = useMutationOptions({
operation: 'update user',
successMessage: 'Profile updated successfully!',
invalidateKeys: [['users']],
});
return useMutation({
mutationFn: ({ id, data }) => apiClient.patch(`/users/${id}`, data),
...options,
});
}
export function useDeleteUser() {
const options = useMutationOptions({
operation: 'delete user',
successMessage: 'User deleted successfully.',
invalidateKeys: [['users'], ['stats', 'user-count']],
});
return useMutation({
mutationFn: (id) => apiClient.delete(`/users/${id}`),
...options,
});
}
Improvements:
- ✅ 57% less code
- ✅ Standardized error handling across all mutations
- ✅ Consistent toast notifications
- ✅ Automatic error logging
- ✅ Type-safe with TypeScript generics
- ✅ Easy to customize with callbacks