Skip to main content

Overview

The frontend is built with React 18, TypeScript, Tailwind CSS 4, and Framer Motion for animations. Components follow a modular architecture with clear separation of concerns.

Project Structure

src/
├── components/          # Reusable UI components
├── pages/              # Page-level components
├── hooks/              # Custom React hooks
├── api/                # API client modules
├── types/              # TypeScript type definitions
├── utils/              # Utility functions
└── assets/             # Static assets

Component Categories

Layout Components

Layout Component

The main layout component provides the application shell with sidebar navigation and page transitions.
import { Link, Outlet, useLocation } from 'react-router-dom';
import { motion } from 'framer-motion';
import { useTheme } from '../hooks/useTheme';

interface NavItem {
  id: string;
  path: string;
  label: string;
  icon: React.ComponentType<{ className?: string }>;
  description?: string;
}

interface NavGroup {
  id: string;
  title: string;
  items: NavItem[];
}

export default function Layout() {
  const location = useLocation();
  const { theme, toggleTheme } = useTheme();
  
  return (
    <div className="flex min-h-screen bg-gradient-to-br from-slate-50 to-indigo-50 dark:from-slate-900 dark:to-slate-800">
      {/* Sidebar */}
      <aside className="w-64 bg-white dark:bg-slate-900 border-r">
        {/* Navigation */}
      </aside>
      
      {/* Main content with page transitions */}
      <main className="flex-1 ml-64 p-10">
        <motion.div
          key={location.pathname}
          initial={{ opacity: 0, y: 20 }}
          animate={{ opacity: 1, y: 0 }}
          exit={{ opacity: 0, y: -20 }}
          transition={{ duration: 0.3 }}
        >
          <Outlet />
        </motion.div>
      </main>
    </div>
  );
}
Key Features:
  • Responsive sidebar navigation with grouped items
  • Dark mode toggle with useTheme hook
  • Smooth page transitions with Framer Motion
  • Active route highlighting
  • Icon-based navigation with Lucide React icons
Source: frontend/src/components/Layout.tsx:1

UI Components

FileUploadCard

A reusable file upload component with drag-and-drop support and animations.
import { useState, useCallback } from 'react';
import { motion, AnimatePresence } from 'framer-motion';
import { Upload, FileText, X } from 'lucide-react';

export interface FileUploadCardProps {
  title: string;
  subtitle: string;
  accept: string;
  formatHint: string;
  maxSizeHint: string;
  uploading?: boolean;
  uploadButtonText?: string;
  selectButtonText?: string;
  showNameInput?: boolean;
  onUpload: (file: File, name?: string) => void;
  onBack?: () => void;
}

export default function FileUploadCard({
  title,
  accept,
  uploading = false,
  onUpload,
  // ... other props
}: FileUploadCardProps) {
  const [selectedFile, setSelectedFile] = useState<File | null>(null);
  const [dragOver, setDragOver] = useState(false);

  const handleDrop = useCallback((e: DragEvent) => {
    e.preventDefault();
    setDragOver(false);
    const files = e.dataTransfer.files;
    if (files.length > 0) {
      setSelectedFile(files[0]);
    }
  }, []);

  return (
    <motion.div
      className="max-w-3xl mx-auto pt-16"
      initial={{ opacity: 0, y: 20 }}
      animate={{ opacity: 1, y: 0 }}
    >
      {/* Upload area */}
      <motion.div
        className={`relative bg-white dark:bg-slate-800 rounded-2xl p-12 cursor-pointer`}
        onDrop={handleDrop}
        onClick={() => document.getElementById('file-upload-input')?.click()}
      >
        <AnimatePresence mode="wait">
          {selectedFile ? (
            <motion.div key="file-selected">
              {/* File preview */}
            </motion.div>
          ) : (
            <motion.div key="no-file">
              {/* Upload prompt */}
            </motion.div>
          )}
        </AnimatePresence>
      </motion.div>
    </motion.div>
  );
}
Features:
  • Drag-and-drop file upload
  • File type validation
  • Loading states during upload
  • Animated file preview
  • Optional name input field
  • Error handling with visual feedback
Source: frontend/src/components/FileUploadCard.tsx:1

ConfirmDialog

A reusable confirmation dialog with customizable variants and animations.
import { motion, AnimatePresence } from 'framer-motion';

export interface ConfirmDialogProps {
  open: boolean;
  title: string;
  message: string | React.ReactNode;
  confirmText?: string;
  cancelText?: string;
  confirmVariant?: 'danger' | 'primary' | 'warning';
  onConfirm: () => void;
  onCancel: () => void;
  loading?: boolean;
}

export default function ConfirmDialog({
  open,
  title,
  message,
  confirmText = '确定',
  confirmVariant = 'primary',
  onConfirm,
  onCancel,
  loading = false,
}: ConfirmDialogProps) {
  if (!open) return null;

  return (
    <AnimatePresence>
      {open && (
        <>
          {/* Backdrop */}
          <motion.div
            initial={{ opacity: 0 }}
            animate={{ opacity: 1 }}
            exit={{ opacity: 0 }}
            onClick={onCancel}
            className="fixed inset-0 bg-black/50 backdrop-blur-sm z-50"
          />
          
          {/* Dialog */}
          <div className="fixed inset-0 z-50 flex items-center justify-center p-4">
            <motion.div
              initial={{ opacity: 0, scale: 0.95, y: 20 }}
              animate={{ opacity: 1, scale: 1, y: 0 }}
              exit={{ opacity: 0, scale: 0.95, y: 20 }}
              className="bg-white dark:bg-slate-800 rounded-2xl shadow-2xl max-w-md w-full p-6"
            >
              <h3 className="text-xl font-bold mb-4">{title}</h3>
              <div className="mb-6">{message}</div>
              {/* Action buttons */}
            </motion.div>
          </div>
        </>
      )}
    </AnimatePresence>
  );
}
Variants:
  • danger: Red buttons for destructive actions
  • primary: Blue buttons for primary actions
  • warning: Orange buttons for warning actions
Source: frontend/src/components/ConfirmDialog.tsx:1

CodeBlock

Syntax-highlighted code display with copy functionality.
import { useState, lazy, Suspense } from 'react';
import { Check, Copy } from 'lucide-react';

// Lazy load for performance
const SyntaxHighlighter = lazy(() =>
  import('react-syntax-highlighter/dist/esm/prism')
);

interface CodeBlockProps {
  language?: string;
  children: string;
}

export default function CodeBlock({ language, children }: CodeBlockProps) {
  const [copied, setCopied] = useState(false);

  const handleCopy = async () => {
    await navigator.clipboard.writeText(children);
    setCopied(true);
    setTimeout(() => setCopied(false), 2000);
  };

  return (
    <div className="relative group my-3">
      {/* Language label and copy button */}
      <div className="flex items-center justify-between px-4 py-2 bg-slate-700 rounded-t-xl">
        <span className="text-xs text-slate-400 font-mono">
          {language || 'code'}
        </span>
        <button onClick={handleCopy} className="flex items-center gap-1.5">
          {copied ? <Check className="w-3.5 h-3.5 text-green-400" /> : <Copy />}
        </button>
      </div>
      
      {/* Code content */}
      <Suspense fallback={<div>Loading code...</div>}>
        <SyntaxHighlighter
          language={language || 'text'}
          showLineNumbers={children.split('\n').length > 3}
        >
          {children}
        </SyntaxHighlighter>
      </Suspense>
    </div>
  );
}
Features:
  • Lazy loading for performance optimization
  • Syntax highlighting with react-syntax-highlighter
  • One-click copy functionality
  • Line numbers for longer code blocks
  • Language label display
Source: frontend/src/components/CodeBlock.tsx:1

Data Display Components

AnalysisPanel

Displays resume analysis results with scoring and suggestions.
  • Score visualization with progress bars
  • Radar chart for skill distribution
  • Strengths and weaknesses sections
  • Improvement suggestions
Source: frontend/src/components/AnalysisPanel.tsx:1

RadarChart

Visualization component for multi-dimensional skill scores using Recharts.
import { Radar, RadarChart as RechartsRadarChart, PolarGrid, PolarAngleAxis, ResponsiveContainer } from 'recharts';

interface RadarChartProps {
  data: Array<{ subject: string; score: number }>;
}
Source: frontend/src/components/RadarChart.tsx:1

HistoryList

Virtualized list component for displaying resume history with infinite scroll.
  • Uses react-virtuoso for efficient rendering
  • Card-based layout with status badges
  • Sorting and filtering capabilities
  • Delete functionality with confirmation
Source: frontend/src/components/HistoryList.tsx:1

Custom Hooks

useTheme Hook

Manages dark mode with localStorage persistence and system preference detection.
import { useEffect, useState } from 'react';

type Theme = 'light' | 'dark';

export function useTheme() {
  const [theme, setTheme] = useState<Theme>(() => {
    // Check localStorage first
    const stored = localStorage.getItem('theme') as Theme;
    if (stored) return stored;
    
    // Fall back to system preference
    if (window.matchMedia('(prefers-color-scheme: dark)').matches) {
      return 'dark';
    }
    return 'light';
  });

  // Sync to document and localStorage
  useEffect(() => {
    const root = document.documentElement;
    if (theme === 'dark') {
      root.classList.add('dark');
    } else {
      root.classList.remove('dark');
    }
    localStorage.setItem('theme', theme);
  }, [theme]);

  const toggleTheme = () => {
    setTheme((prev) => (prev === 'light' ? 'dark' : 'light'));
  };

  return { theme, toggleTheme };
}
Features:
  • Reads from localStorage on mount
  • Detects system color scheme preference
  • Syncs theme changes to DOM and localStorage
  • Prevents flash of unstyled content (FOUC)
Source: frontend/src/hooks/useTheme.ts:1

Styling Patterns

Tailwind CSS Usage

The application uses Tailwind CSS 4 with custom configuration:
index.css
@import 'tailwindcss';

/* Custom dark mode styles */
.dark {
  color-scheme: dark;
}

/* Gradient backgrounds */
.bg-gradient-primary {
  @apply bg-gradient-to-r from-primary-500 to-primary-600;
}

Common Patterns

Button Styles

<button className="px-8 py-3 bg-gradient-to-r from-primary-500 to-primary-600 text-white rounded-xl font-semibold shadow-lg shadow-primary-500/30 hover:shadow-xl transition-all">
  Click Me
</button>

Card Styles

<div className="bg-white dark:bg-slate-800 rounded-2xl p-6 shadow-lg dark:shadow-slate-900/50">
  Card Content
</div>

Dark Mode Support

All components support dark mode using Tailwind’s dark: variant:
<div className="text-slate-900 dark:text-white bg-white dark:bg-slate-800">
  Content that adapts to theme
</div>

Animation Patterns

Framer Motion Usage

The application uses Framer Motion for smooth animations:
<motion.div
  key={location.pathname}
  initial={{ opacity: 0, y: 20 }}
  animate={{ opacity: 1, y: 0 }}
  exit={{ opacity: 0, y: -20 }}
  transition={{ duration: 0.3 }}
>
  <Outlet />
</motion.div>
<motion.div
  initial={{ opacity: 0, y: 20 }}
  animate={{ opacity: 1, y: 0 }}
  transition={{ delay: 0.1 }}
>
  First Item
</motion.div>
<motion.div
  initial={{ opacity: 0, y: 20 }}
  animate={{ opacity: 1, y: 0 }}
  transition={{ delay: 0.2 }}
>
  Second Item
</motion.div>
<motion.button
  whileHover={{ scale: 1.02, y: -2 }}
  whileTap={{ scale: 0.98 }}
>
  Interactive Button
</motion.button>

Component Guidelines

1

Use TypeScript interfaces

Define clear prop types for all components with JSDoc comments.
2

Support dark mode

Use Tailwind’s dark: variants for all color-related classes.
3

Add loading states

Components should handle loading and error states gracefully.
4

Make components accessible

Use semantic HTML and ARIA attributes where appropriate.
5

Optimize performance

Use lazy loading for heavy components and React.memo for expensive renders.
All components follow React best practices with hooks, functional components, and proper TypeScript typing.

Next Steps

Routing

Learn about React Router 7 setup and navigation patterns

API Integration

Explore API client setup and data fetching patterns

Build docs developers (and LLMs) love