Some checks failed
Continuous Integration / Frontend Tests (React + Vitest) (push) Has been cancelled
Continuous Integration / Backend Tests (Python + pytest) (push) Has been cancelled
Continuous Integration / Docker Build Tests (agents) (push) Has been cancelled
Continuous Integration / Docker Build Tests (frontend) (push) Has been cancelled
Continuous Integration / Docker Build Tests (mcp) (push) Has been cancelled
Continuous Integration / Docker Build Tests (server) (push) Has been cancelled
Continuous Integration / Test Results Summary (push) Has been cancelled
* refactor: reorganize features/shared directory structure - Created organized subdirectories for better code organization: - api/ - API clients and HTTP utilities (renamed apiWithEtag.ts to apiClient.ts) - config/ - Configuration files (queryClient, queryPatterns) - types/ - Shared type definitions (errors) - utils/ - Pure utility functions (optimistic, clipboard) - hooks/ - Shared React hooks (already existed) - Updated all import paths across the codebase (~40+ files) - Updated all AI documentation in PRPs/ai_docs/ to reflect new structure - All tests passing, build successful, no functional changes This improves maintainability and follows vertical slice architecture patterns. Co-Authored-By: Claude <noreply@anthropic.com> * fix: address PR review comments and code improvements - Update imports to use @/features alias path for optimistic utils - Fix optimistic upload item replacement by matching on source_id instead of id - Clean up test suite naming and remove meta-terms from comments - Only set Content-Type header on requests with body - Add explicit TypeScript typing to useProjectFeatures hook - Complete Phase 4 improvements with proper query typing * fix: address additional PR review feedback - Clear feature queries when deleting project to prevent cache memory leaks - Update KnowledgeCard comments to follow documentation guidelines - Add explanatory comment for accessibility pattern in KnowledgeCard --------- Co-authored-by: Claude <noreply@anthropic.com>
4.9 KiB
4.9 KiB
Optimistic Updates Pattern Guide
Core Architecture
Shared Utilities Module
Location: src/features/shared/utils/optimistic.ts
Provides type-safe utilities for managing optimistic state across all features:
createOptimisticId()- Generates stable UUIDs using nanoidcreateOptimisticEntity<T>()- Creates entities with_optimisticand_localIdmetadataisOptimistic()- Type guard for checking optimistic statereplaceOptimisticEntity()- Replaces optimistic items by_localId(race-condition safe)removeDuplicateEntities()- Deduplicates after replacementcleanOptimisticMetadata()- Strips optimistic fields when needed
TypeScript Interface
interface OptimisticEntity {
_optimistic: boolean;
_localId: string;
}
Implementation Patterns
Mutation Hooks Pattern
Reference: src/features/projects/tasks/hooks/useTaskQueries.ts:44-108
-
onMutate: Create optimistic entity with stable ID
- Use
createOptimisticEntity<T>()for type-safe creation - Store
optimisticIdin context for later replacement
- Use
-
onSuccess: Replace optimistic with server response
- Use
replaceOptimisticEntity()matching by_localId - Apply
removeDuplicateEntities()to prevent duplicates
- Use
-
onError: Rollback to previous state
- Restore snapshot from context
UI Component Pattern
References:
src/features/projects/tasks/components/TaskCard.tsx:39-40,160,186src/features/projects/components/ProjectCard.tsx:32-33,67,93src/features/knowledge/components/KnowledgeCard.tsx:49-50,176,244
- Check optimistic state:
const optimistic = isOptimistic(entity) - Apply conditional styling: Add opacity and ring effect when optimistic
- Display indicator: Use
<OptimisticIndicator>component for visual feedback
Visual Indicator Component
Location: src/features/ui/primitives/OptimisticIndicator.tsx
Reusable component showing:
- Spinning loader icon (Loader2 from lucide-react)
- "Saving..." text with pulse animation
- Configurable via props:
showSpinner,pulseAnimation
Feature Integration
Tasks
- Mutations:
src/features/projects/tasks/hooks/useTaskQueries.ts - UI:
src/features/projects/tasks/components/TaskCard.tsx - Creates tasks with
priority: "medium"default
Projects
- Mutations:
src/features/projects/hooks/useProjectQueries.ts - UI:
src/features/projects/components/ProjectCard.tsx - Handles
prd: null,data_schema: nullfor new projects
Knowledge
- Mutations:
src/features/knowledge/hooks/useKnowledgeQueries.ts - UI:
src/features/knowledge/components/KnowledgeCard.tsx - Uses
createOptimisticId()directly for progress tracking
Toasts
- Location:
src/features/shared/hooks/useToast.ts:43 - Uses
createOptimisticId()for unique toast IDs
Testing
Unit Tests
Location: src/features/shared/utils/tests/optimistic.test.ts
Covers all utility functions with 8 test cases:
- ID uniqueness and format validation
- Entity creation with metadata
- Type guard functionality
- Replacement logic
- Deduplication
- Metadata cleanup
Manual Testing Checklist
- Rapid Creation: Create 5+ items quickly - verify no duplicates
- Visual Feedback: Check optimistic indicators appear immediately
- ID Stability: Confirm nanoid-based IDs after server response
- Error Handling: Stop backend, attempt creation - verify rollback
- Race Conditions: Use browser console script for concurrent creates
Performance Characteristics
- Bundle Impact: ~130 bytes (nanoid v5, minified+gzipped) - build/environment dependent
- Update Speed: Typically snappy on modern devices; actual latency varies by device and workload
- ID Generation: Per nanoid benchmarks: secure sync ≈5M ops/s, non-secure ≈2.7M ops/s, async crypto ≈135k ops/s
- Memory: Minimal - only
_optimisticand_localIdmetadata added per optimistic entity
Migration Notes
From Timestamp-based IDs
Before: const tempId = \temp-${Date.now()}`**After**:const optimisticId = createOptimisticId()`
Key Differences
- No timestamp collisions during rapid creation
- Stable IDs survive re-renders
- Type-safe with full TypeScript inference
- ~60% code reduction through shared utilities
Best Practices
- Always use shared utilities - Don't implement custom optimistic logic
- Match by _localId - Never match by the entity's
idfield - Include deduplication - Always call
removeDuplicateEntities()after replacement - Show visual feedback - Users should see pending state clearly
- Handle errors gracefully - Always implement rollback in
onError
Dependencies
- nanoid: v5.0.9 - UUID generation
- @tanstack/react-query: v5.x - Mutation state management
- React: v18.x - UI components
- TypeScript: v5.x - Type safety
Last updated: Phase 3 implementation (PR #695)