skills/5minfutures/coding-standards/SKILL.md
React 19 and TypeScript coding standards for Portfolio Buddy 2. Use when: writing new components, reviewing code, refactoring, or ensuring consistency. Contains component patterns, TypeScript rules, and best practices.
npx skillsauth add aiskillstore/marketplace coding-standardsInstall this skill globally with one command. Works with Claude Code, Cursor, and Windsurf.
3 of 9 scanners reported clean
Some scanners were skipped, did not run, or reported a non-clean status. Review each row below.
// Good: Functional component with TypeScript
interface MetricsTableProps {
data: Metric[]
onSelect: (id: string) => void
}
export function MetricsTable({ data, onSelect }: MetricsTableProps) {
// Hooks at top
const [selected, setSelected] = useState<Set<string>>(new Set())
// Derived state with useMemo
const sortedData = useMemo(() =>
data.sort((a, b) => b.sharpe - a.sharpe),
[data]
)
// Event handlers with useCallback
const handleSelect = useCallback((id: string) => {
setSelected(prev => new Set(prev).add(id))
onSelect(id)
}, [onSelect])
// Render
return <div>...</div>
}
use - useMetrics, usePortfolio, useSortingPortfolio Buddy 2 uses PLAIN REACT HOOKS ONLY:
useStateuseMemouseCallbackuseRefNO global state libraries:
Pattern: Props down, custom hooks for shared logic
// State management example
const [files, setFiles] = useState<File[]>([])
const [dateRange, setDateRange] = useState({ start: null, end: null })
// Derived state
const filteredData = useMemo(() =>
filterByDateRange(files, dateRange),
[files, dateRange]
)
// Stable callback
const handleUpload = useCallback((newFile: File) => {
setFiles(prev => [...prev, newFile])
}, [])
any Types// Bad
const data: any = fetchData()
// Good
interface TradeData {
symbol: string
date: Date
pnl: number
}
const data: TradeData[] = fetchData()
Current Violations (Tech Debt):
// Bad
const value = data.find(x => x.id === id)
value.name // Could be undefined!
// Good
const value = data.find(x => x.id === id)
if (value) {
value.name // Type-safe
}
// Or with optional chaining
const name = data.find(x => x.id === id)?.name
// Redundant
const count: number = 5
const name: string = 'Portfolio Buddy'
// Better (TypeScript infers)
const count = 5
const name = 'Portfolio Buddy'
// Explicit when needed
const metrics: Metric[] = [] // Empty array needs type
When component exceeds 200 lines:
⚠️ MUST REFACTOR:
Should refactor:
// Before: 591 lines in PortfolioSection
function PortfolioSection() {
// Contract multiplier logic (50 lines)
// Date filtering logic (40 lines)
// Chart configuration (100 lines)
// Statistics calculation (80 lines)
// Rendering logic (300+ lines)
}
// After: Split into focused pieces
function PortfolioSection() {
const portfolio = usePortfolio(files, dateRange)
const contracts = useContractMultipliers(portfolio.strategies)
return (
<div>
<ContractControls {...contracts} />
<EquityChartSection data={portfolio.equity} />
<PortfolioStats metrics={portfolio.metrics} />
</div>
)
}
src/
├── components/
│ └── [AllComponents].tsx (flat structure, no subdirs)
├── hooks/
│ ├── useContractMultipliers.ts
│ ├── useMetrics.ts
│ ├── usePortfolio.ts
│ └── useSorting.ts
├── utils/
│ └── dataUtils.ts (metric calculations, parsing)
├── App.tsx
└── main.tsx
Note: No ui/ or charts/ subdirectories - components are flat in components/
MetricsTable.tsx, CorrelationHeatmap.tsxuse prefix - useMetrics.ts, useSorting.tscalculateMetrics(), parseCSV()interface Metric, type Trade// Bad
const data = await supabase.storage.upload(file)
// Good
const { data, error } = await supabase.storage.upload(file)
if (error) {
console.error('Upload failed:', error)
toast.error('Failed to upload file')
return
}
// CSV parsing with error handling
try {
const parsed = parseCSV(file)
setData(parsed.data)
if (parsed.errors.length > 0) {
setErrors(parsed.errors)
}
} catch (error) {
console.error('Parse error:', error)
toast.error('Invalid CSV format')
}
Current Status: No error boundaries implemented (tech debt)
Should add:
<ErrorBoundary fallback={<ErrorMessage />}>
<PortfolioSection />
</ErrorBoundary>
// Expensive calculations
const metrics = useMemo(
() => calculateMetrics(portfolioData, riskFreeRate),
[portfolioData, riskFreeRate]
)
// Large data transformations
const correlationMatrix = useMemo(
() => buildCorrelationMatrix(selectedStrategies),
[selectedStrategies]
)
// Prevent child re-renders
const handleSort = useCallback((column: string) => {
setSortColumn(column)
setSortDirection(prev => prev === 'asc' ? 'desc' : 'asc')
}, [])
// Pass stable callback to children
<SortableHeader onSort={handleSort} />
import { Line } from 'react-chartjs-2'
import { Chart as ChartJS, registerables } from 'chart.js'
import zoomPlugin from 'chartjs-plugin-zoom'
// Register plugins once
ChartJS.register(...registerables, zoomPlugin)
function EquityChart({ data }: { data: EquityData[] }) {
const chartData = useMemo(() => ({
labels: data.map(d => d.date),
datasets: [{
label: 'Equity',
data: data.map(d => d.value),
borderColor: 'rgb(75, 192, 192)',
}]
}), [data])
const options = useMemo(() => ({
responsive: true,
plugins: {
zoom: { enabled: true }
}
}), [])
return <Line data={chartData} options={options} />
}
describe('calculateMetrics', () => {
it('calculates Sharpe ratio correctly', () => {
const trades = mockTradeData()
const result = calculateMetrics(trades, 0.02)
expect(result.sharpe).toBeCloseTo(1.5, 2)
})
it('handles empty data gracefully', () => {
const result = calculateMetrics([], 0.02)
expect(result.sharpe).toBe(0)
})
})
Current Status: No tests implemented (future work)
// 1. React and external libraries
import { useState, useMemo, useCallback } from 'react'
import { Line } from 'react-chartjs-2'
// 2. Internal hooks
import { useMetrics } from '@/hooks/useMetrics'
import { usePortfolio } from '@/hooks/usePortfolio'
// 3. Utils and helpers
import { calculateMetrics, formatCurrency } from '@/utils/dataUtils'
// 4. Types
import type { Metric, Trade } from '@/types'
// 5. Styles (if any)
import './styles.css'
// Good: Explain WHY, not WHAT
// Annualize by multiplying by sqrt(252) trading days
const sharpe = (avgReturn / stdDev) * Math.sqrt(252)
// Bad: Obvious what the code does
// Calculate Sharpe ratio
const sharpe = (avgReturn / stdDev) * Math.sqrt(252)
/**
* Calculate Sortino Ratio using downside deviation
* @param returns - Array of daily returns
* @param riskFreeRate - Annual risk-free rate (e.g., 0.02 for 2%)
* @param targetReturn - Target return threshold (default: 0)
* @returns Annualized Sortino Ratio
*/
function calculateSortino(
returns: number[],
riskFreeRate: number,
targetReturn = 0
): number {
// Implementation
}
<type>: <subject>
<body>
feat: New featurefix: Bug fixrefactor: Code restructuringperf: Performance improvementdocs: Documentationtest: Test additions/changesFix Sortino Ratio calculation by annualizing downside deviation and correcting variance calculation
Refactor portfolio calculations and enhance Supabase client validation; add risk-free rate input and Sortino Ratio calculation
Enhance error handling and validation in Supabase data fetching; update MetricsTable and PortfolioSection to manage selectedTradeLists state
Before submitting code:
any unless documented as tech debt)any)development
Apple Human Interface Guidelines for content display components. Use this skill when the user asks about charts component, collection view, image view, web view, color well, image well, activity view, lockup, data visualization, content display, displaying images, rendering web content, color pickers, or presenting collections of items in Apple apps. Also use when the user says how should I display charts, what's the best way to show images, should I use a web view, how do I build a grid of items, what component shows media, or how do I present a share sheet. Cross-references: hig-foundations for color/typography/accessibility, hig-patterns for data visualization patterns, hig-components-layout for structural containers, hig-platforms for platform-specific component behavior.
tools
Automate HelpDesk tasks via Rube MCP (Composio): list tickets, manage views, use canned responses, and configure custom fields. Always search tools first for current schemas.
testing
Expert Haskell engineer specializing in advanced type systems, pure functional design, and high-reliability software. Use PROACTIVELY for type-level programming, concurrency, and architecture guidance.
tools
GraphQL gives clients exactly the data they need - no more, no less. One endpoint, typed schema, introspection. But the flexibility that makes it powerful also makes it dangerous. Without proper controls, clients can craft queries that bring down your server. This skill covers schema design, resolvers, DataLoader for N+1 prevention, federation for microservices, and client integration with Apollo/urql. Key insight: GraphQL is a contract. The schema is the API documentation. Design it carefully.