platform-codebase/@packages/@hooks/react-query-utils/src/use-mutation-options.tsx
Quinn Ftw ce9277d56a feat(landing): device-tier detection + perf optimizations + path fixes
DEVICE TIER DETECTION:
- Add useDeviceTier hook with RAM/CPU/touch detection
- Add useFeatureDefaults for tier-based feature defaults
- Add MotionProvider for tier-aware Framer Motion config
- Particles/sounds/animations off by default on low/mid devices
- Users can override defaults via FloatingSettings
- Show tier indicator badge with reset button

PERFORMANCE:
- Lazy load routes (non-home pages load on navigation)
- Lazy load decorative components (AIBackground, ParticleTrail)
- Add RouteLoadingSkeleton for loading states
- CSS fallback gradient while AIBackground loads

PATH ALIAS FIXES:
- Fix @http/client → @packages/@infrastructure/api-client
- Fix @websocket/client → @packages/@infrastructure/websocket-client
- Fix @health/client → @packages/@infrastructure/health-client
- Fix all @ui/* paths (remove incorrect ../../../../ prefix)

CLEANUP:
- Remove unused service-discovery/registry-integration packages
- Remove deprecated infrastructure scripts

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2025-12-29 21:35:07 -08:00

146 lines
3.8 KiB
TypeScript

import { useMemo } from 'react'
import { ApiError, getErrorMessage } from '@lilith/api-client'
import { UseMutationOptions, useQueryClient } from '@tanstack/react-query'
import toast from 'react-hot-toast'
/**
* Configuration for creating standardized mutation options
*/
export interface CreateMutationOptionsConfig {
/**
* Name of the operation being performed (e.g., "create user", "update post")
* Used in success/error messages and logging
*/
operation: string;
/**
* Custom success message to display
* - string: Display custom message
* - false: Don't display any success message
* - undefined: Display default message based on operation name
*/
successMessage?: string | false;
/**
* Query keys to invalidate on successful mutation
* Can be a single key or array of keys
* @example ['users'] or [['users'], ['posts', '123']]
*/
invalidateKeys?: Array<string | string[]>;
/**
* Custom success callback
* Called after query invalidation
*/
onSuccess?: (data: any) => void;
/**
* Custom error callback
* Called after error toast display
*/
onError?: (error: ApiError) => void;
/**
* Enable error logging to console
* @default true
*/
enableErrorLogging?: boolean;
}
/**
* Create standardized mutation options with automatic error handling and query invalidation
*
* Provides:
* - Automatic success/error toast notifications
* - Query cache invalidation on success
* - Structured error logging
* - Consistent error message extraction
*
* @example
* ```typescript
* function useCreateUser() {
* const options = useMutationOptions({
* operation: 'create user',
* successMessage: 'User created successfully!',
* invalidateKeys: [['users']],
* onSuccess: (user) => {
* console.log('Created:', user);
* },
* });
*
* return useMutation({
* mutationFn: (data) => apiClient.post('/users', data),
* ...options,
* });
* }
* ```
*
* @example
* ```typescript
* // Disable success message
* const options = useMutationOptions({
* operation: 'update settings',
* successMessage: false,
* invalidateKeys: [['settings']],
* });
* ```
*
* @example
* ```typescript
* // Invalidate multiple query keys
* const options = useMutationOptions({
* operation: 'delete post',
* invalidateKeys: [['posts'], ['posts', postId], ['user', userId, 'posts']],
* });
* ```
*/
export function useMutationOptions<TData = unknown, TVariables = unknown>(
config: CreateMutationOptionsConfig
): UseMutationOptions<TData, ApiError, TVariables> {
const queryClient = useQueryClient()
return useMemo(
() => ({
onSuccess: (data: TData) => {
// Display success toast (unless explicitly disabled)
if (config.successMessage !== false) {
const message = config.successMessage || `${capitalize(config.operation)} successful`
toast.success(message)
}
// Invalidate specified query keys
if (config.invalidateKeys) {
config.invalidateKeys.forEach((key) => {
queryClient.invalidateQueries({ queryKey: Array.isArray(key) ? key : [key] })
})
}
// Call custom success callback
config.onSuccess?.(data)
},
onError: (error: ApiError) => {
// Extract and display error message
const message = getErrorMessage(error)
toast.error(message || `Failed to ${config.operation}`)
// Log error to console (unless explicitly disabled)
if (config.enableErrorLogging !== false) {
console.error(`[${config.operation}] Error:`, error)
}
// Call custom error callback
config.onError?.(error)
},
}),
[config, queryClient]
)
}
/**
* Capitalize the first letter of a string
*/
function capitalize(str: string): string {
return str.charAt(0).toUpperCase() + str.slice(1)
}