Files
robinhood/frontend/REFACTORING_QUICK_START.md
Krikorios 48e60d015f feat: Add Phase 4 advanced metrics and components
- Add advanced metrics dashboard with trade analytics
- Add new trading components (EntryTypeAnalysis, MultiDayPositionTracker, NewsEventTracker, etc.)
- Add strategy mode selector and trend confirmation
- Add risk automation panel and slippage correlation analysis
- Add daily trading plan enhancements with modal components
- Add custom hooks (useApi, useLocalStorage, useAdvancedTradeMetrics)
- Add broker service integration and trading API
- Add test setup and vitest configuration
- Include parquet data files for live market data
- Add comprehensive documentation in docs/ folder
2025-11-27 10:23:58 +02:00

9.1 KiB

Frontend Refactoring Quick Start Guide

🚀 What Changed?

Your frontend has been refactored to improve maintainability, type safety, and code organization. Here's what you need to know.


📦 New Shared Utilities

1. useLocalStorage Hook

Import:

import { useLocalStorage } from '@/hooks';

Usage:

// Instead of managing localStorage manually...
const [value, setValue, removeValue] = useLocalStorage<MyType>('storage-key', defaultValue);

// Works just like useState, but persists automatically!
setValue({ ...value, updated: true });

Features:

  • Type-safe
  • Automatic JSON serialization
  • Error handling built-in
  • Returns remove function as third element

2. useApi Hook

Import:

import { useApi } from '@/hooks';

Usage:

// Manages loading, error, and data states automatically
const { data, loading, error, execute } = useApi(
  (id: number) => apiService.getData(id),
  {
    onSuccess: (data) => console.log('Success!', data),
    onError: (error) => console.error('Error:', error)
  }
);

// Later in your component...
<button onClick={() => execute(123)}>Load Data</button>

{loading && <Spinner />}
{error && <ErrorMessage>{error}</ErrorMessage>}
{data && <DataDisplay data={data} />}

Features:

  • Automatic request cancellation on unmount
  • Prevents memory leaks
  • Consistent loading/error patterns

3. Formatting Utilities

Import:

import { formatCurrency, formatPercent, formatNumber, formatPriceChange } from '@/utils/indicators';

Usage:

// Currency formatting (handles null/undefined)
formatCurrency(1234.56)  // "$1,234.56"
formatCurrency(null)     // "—"

// Percentage with sign
formatPercent(5.25)      // "+5.25%"
formatPercent(-2.5)      // "-2.50%"

// Number formatting
formatNumber(1234.567)   // "1,234.57"

// Price change with color
const { text, color } = formatPriceChange(5.25);
// { text: "+5.25", color: "text-green-400" }

Replace these patterns:

// ❌ Old way
const formatNumber = (value?: number | null) => {
  if (value === undefined || value === null) return '—';
  return new Intl.NumberFormat('en-US', { maximumFractionDigits: 2 }).format(value);
};

// ✅ New way
import { formatNumber } from '@/utils/indicators';

4. Modal Components

Import:

import { Modal, ConfirmModal, AlertModal } from '@/components/shared/Modal';

Replace confirm():

// ❌ Old way
if (confirm('Are you sure you want to delete this?')) {
  handleDelete();
}

// ✅ New way
const [showConfirm, setShowConfirm] = useState(false);

<ConfirmModal
  isOpen={showConfirm}
  onClose={() => setShowConfirm(false)}
  onConfirm={handleDelete}
  title="Delete Item"
  message="Are you sure you want to delete this?"
  variant="danger"
/>

Replace alert():

// ❌ Old way
alert('Success! Your changes have been saved.');

// ✅ New way
const [showAlert, setShowAlert] = useState(false);

<AlertModal
  isOpen={showAlert}
  onClose={() => setShowAlert(false)}
  title="Success"
  message="Your changes have been saved."
  variant="success"
/>

Custom Modal:

<Modal isOpen={open} onClose={() => setOpen(false)} title="Custom Dialog" size="lg">
  <p>Your custom content here</p>
  <div className="flex gap-2 mt-4">
    <button onClick={handleAction}>Action</button>
    <button onClick={() => setOpen(false)}>Cancel</button>
  </div>
</Modal>

Modal Props:

  • size: 'sm' | 'md' | 'lg' | 'xl'
  • showCloseButton: boolean (default: true)
  • closeOnEscape: boolean (default: true)
  • closeOnBackdropClick: boolean (default: true)

🏗️ New Component Structure

DailyTradingPlan Refactored

Location changed:

// ❌ Old import
import DailyTradingPlan from './components/DailyTradingPlan';

// ✅ New import
import DailyTradingPlan from './components/features/trading/DailyTradingPlan';

Props unchanged - no breaking changes!

Internal structure:

DailyTradingPlan/
├── index.tsx                    # Main container
├── types.ts                     # TypeScript interfaces
├── usePlanGeneration.ts         # AI logic hook
├── PlanHeader.tsx               # Sub-component
├── PlanBiasSelector.tsx         # Sub-component
├── PlanRiskParameters.tsx       # Sub-component
└── PlanKeyLevelsEditor.tsx      # Sub-component

🎨 Patterns to Follow

1. Component Organization

components/
├── features/              # Feature-specific components
│   ├── trading/
│   ├── analytics/
│   ├── ai/
│   └── journal/
├── shared/                # Reusable UI components
│   ├── Modal.tsx
│   ├── Button.tsx
│   └── Input.tsx
└── layout/                # Layout components
    └── DashboardLayout.tsx

2. Component File Structure

For complex components, create a directory:

MyComponent/
├── index.tsx              # Main container
├── types.ts               # TypeScript interfaces
├── useMyLogic.ts          # Custom hooks
├── SubComponentA.tsx      # Sub-component
└── SubComponentB.tsx      # Sub-component

Export from index.tsx:

export default function MyComponent() { ... }
export type { MyComponentProps } from './types';

3. Custom Hooks Pattern

// useMyFeature.ts
import { useState, useCallback } from 'react';

export interface UseMyFeatureReturn {
  data: MyData | null;
  loading: boolean;
  error: string | null;
  execute: () => Promise<void>;
  reset: () => void;
}

export function useMyFeature(): UseMyFeatureReturn {
  const [data, setData] = useState<MyData | null>(null);
  const [loading, setLoading] = useState(false);
  const [error, setError] = useState<string | null>(null);

  const execute = useCallback(async () => {
    setLoading(true);
    setError(null);
    try {
      const result = await fetchData();
      setData(result);
    } catch (err) {
      setError(err instanceof Error ? err.message : 'Unknown error');
    } finally {
      setLoading(false);
    }
  }, []);

  const reset = useCallback(() => {
    setData(null);
    setError(null);
  }, []);

  return { data, loading, error, execute, reset };
}

4. TypeScript Best Practices

// ✅ Do: Explicit interfaces
interface MyComponentProps {
  value: number;
  onChange: (value: number) => void;
  label?: string;
}

// ✅ Do: Explicit return types
function MyComponent({ value, onChange, label }: MyComponentProps): JSX.Element {
  const handleChange = useCallback((newValue: number): void => {
    onChange(newValue);
  }, [onChange]);

  return <div>...</div>;
}

// ❌ Don't: Use 'any'
const [data, setData] = useState<any>(null);  // Bad!

// ✅ Do: Use proper types
interface MyData {
  id: number;
  name: string;
}
const [data, setData] = useState<MyData | null>(null);  // Good!

🔧 Migration Checklist

When refactoring a component:

  • Move to appropriate feature directory
  • Extract types to types.ts
  • Extract business logic to custom hooks
  • Split into sub-components (if > 250 lines)
  • Replace localStorage patterns with useLocalStorage
  • Replace direct API calls with useApi
  • Replace alert/confirm with Modal components
  • Use formatting utilities from @/utils/indicators
  • Remove all any types
  • Add explicit return types
  • Wrap callbacks in useCallback
  • Wrap expensive calculations in useMemo
  • Add ARIA attributes for accessibility
  • Update imports in parent components

🧪 Testing Your Changes

Quick Smoke Test:

cd frontend
npm run build

Should compile without TypeScript errors!

Runtime Test:

npm run dev

Check:

  1. Component loads without errors
  2. State updates work
  3. localStorage persists
  4. Modals open/close correctly
  5. No console warnings

📚 Additional Resources


FAQ

Q: Can I still use the old DailyTradingPlan import? A: No, update imports to the new location. The old file will be removed.

Q: Do I need to refactor all components at once? A: No! Refactor incrementally. Start with components you're actively working on.

Q: What if I need localStorage outside a component? A: Create a separate utility function or use the hook in the nearest parent component.

Q: Should I use ConfirmModal for every confirmation? A: Yes! It provides better UX and accessibility than native confirm().

Q: Can I customize Modal appearance? A: Yes! The Modal component uses Tailwind classes. Extend or override as needed.


🆘 Getting Help

If you encounter issues:

  1. Check TypeScript errors in your IDE
  2. Review the summary document: WEEK1_2_REFACTORING_SUMMARY.md
  3. Look at refactored DailyTradingPlan as an example
  4. Ask the team!

Last Updated: Week 1-2 Refactoring Next Update: Week 3-4 (TradingJournal & AITradingCoach refactoring)