Files

6.3 KiB

name, description
name description
crazeanalytix-standards Coding patterns and standards for the CrazeAnalytix project — an Amazon seller analytics dashboard built with React 19, TypeScript, Vite, and Tailwind CSS. Use this skill when creating new components, API routes, data processors, or modifying existing code in the CrazeAnalytix codebase to ensure consistency with established conventions.

CrazeAnalytix Development Standards

Architecture Overview

Single-page React 19 app with centralized state in App.tsx, Vercel serverless API routes, and client-side data processing. Data flows: Dropbox → /api/* routes → dataProcessor.ts → App state → View components.

Component Patterns

Structure

interface MyComponentProps {
  data: SalesRecord[];
  filters: FilterState;
  onFilterChange: (key: keyof FilterState, value: string[]) => void;
}

const MyComponent: React.FC<MyComponentProps> = ({ data, filters, onFilterChange }) => {
  const computed = useMemo(() => aggregateData(data), [data]);

  const handleChange = useCallback((value: string) => {
    onFilterChange('customer', [value]);
  }, [onFilterChange]);

  return <div className="bg-slate-900 rounded-xl p-4">...</div>;
};

export default MyComponent;

Rules:

  • Define Props interface above component
  • Use React.FC<Props> typing
  • Wrap expensive computations in useMemo
  • Wrap event handlers in useCallback
  • Use React.memo() for row/item renderers in lists and tables
  • Lazy-load view components: const DataGrid = lazy(() => import('./components/DataGrid'))

State Management

All global state lives in App.tsx via useState hooks. No Redux. Props drilling to children.

const [rawData, setRawData] = useState<SalesRecord[]>([]);
const [adsData, setAdsData] = useState<AdsRecord[]>([]);
const [stockMap, setStockMap] = useState<Map<string, number>>(new Map());
const [filters, setFilters] = useState<FilterState>({ customer: [], year: [], ... });

Naming Conventions

Context Convention Example
Components PascalCase .tsx Dashboard.tsx, FilterBar.tsx
Services camelCase .ts dataProcessor.ts, geminiService.ts
API routes kebab-case .ts fetch-ads.ts, ask-gemini.ts
Process functions process<Format> processCSV, processAdsExcel
Event handlers handle<Action> handleDataFetch, handleFilterChange
Calculators calculate<What> calculateVelocityMap, calculateLineMovers
State setters set<Noun> setRawData, setFilters
Map variables <noun>Map stockMap, vendorStockMap, buyBoxLostMap
Constants UPPER_SNAKE_CASE MONTH_ORDER, PAN_EU_COUNTRIES
Props callbacks on<Event> onFilterChange, onAdsUpload

API Route Pattern

All routes in /api/ follow this structure:

import type { VercelRequest, VercelResponse } from '@vercel/node';

const RESOURCE_DROPBOX_URL = "https://www.dropbox.com/...&dl=1";

export default async function handler(req: VercelRequest, res: VercelResponse) {
    res.setHeader('Access-Control-Allow-Origin', '*');
    res.setHeader('Access-Control-Allow-Methods', 'GET, OPTIONS');
    res.setHeader('Access-Control-Allow-Headers', 'Content-Type');

    if (req.method === 'OPTIONS') return res.status(200).end();

    try {
        console.log('[route-name] Fetching from Dropbox...');
        const response = await fetch(RESOURCE_DROPBOX_URL, {
            cache: 'no-store',
            headers: { 'Pragma': 'no-cache', 'Cache-Control': 'no-cache' }
        });
        if (!response.ok) throw new Error(`Dropbox responded with ${response.status}`);

        const buffer = await response.arrayBuffer();
        res.setHeader('Content-Type', 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet');
        res.status(200).send(Buffer.from(buffer));
    } catch (error: any) {
        console.error('[route-name] Error:', error);
        res.status(500).json({ error: error.message });
    }
}

Data Processing

All processing lives in services/dataProcessor.ts. See references/data-processing.md for detailed patterns.

Key rules:

  • Use getColumnValue(row, aliases[]) for flexible column mapping (supports multiple header names)
  • Parse currencies with parseCurrency() — handles both EU (1.234,56) and US (1,234.56) formats
  • Normalize months with normalizeMonth() — handles English, Spanish, numeric, and Excel serial dates
  • Filter with isAllowedCustomer() before returning processed data
  • PAN_EU_COUNTRIES = ['Amazon DE', 'Amazon IT', 'Amazon FR', 'Amazon ES'] is the default when no customer is selected

Styling Standards

Dark theme with Tailwind CSS (loaded via CDN). No custom CSS files.

Color system:

  • Backgrounds: bg-slate-950 (page), bg-slate-900 (cards), bg-slate-800 (inputs/hover)
  • Text: text-slate-100 (primary), text-slate-300 (secondary), text-slate-400/text-slate-500 (muted)
  • Borders: border-slate-700
  • Accent colors: indigo (#6366f1), pink (#ec4899), emerald (#10b981), amber (#f59e0b)

Responsive (mobile-first):

className="text-xs md:text-sm px-3 py-2 md:px-6 md:py-3"
className="flex flex-col gap-3 md:flex-row"
className="grid grid-cols-2 md:grid-cols-4 gap-4"

Interactive states:

className="hover:bg-white/[0.02] transition-colors"
className="hover:scale-105 transition-all"

Badge pattern:

className="inline-flex items-center gap-1.5 px-2 py-1 rounded bg-gradient-to-br from-amber-500 to-orange-500 text-white border border-amber-400/50 shadow-lg"

Types

All interfaces in types.ts. See references/type-definitions.md for full details.

Core types: SalesRecord, AdsRecord, TrafficRecord, ForecastRecord, FilterState, AggregatedData, PivotRow, CombinedKPIs, GrowthMetric.

Excel/CSV Processing

  • CSV: PapaParse with { header: true, skipEmptyLines: true }
  • Excel: XLSX library with XLSX.read(buffer, { type: 'array' }), then sheet_to_json(worksheet, { header: 1 })
  • Always filter: .filter(r => r.year > 2023 && isAllowedCustomer(r.customer))
  • Support multiple column header aliases for robust parsing

Caching

IndexedDB via services/storage.ts with schema versioning. Cache data on load, serve cached immediately, refresh in background.