# Frontend Component Specification — Cloud Reader ## Overview React 19 + TypeScript SPA using Vite for development and production builds. State managed via React hooks and Context API. Routing via `react-router-dom` v7 with `lazy`/`Suspense` for code splitting. ## Components ### App (root) - **Path:** `/` - **Layout:** Header with logo + `` wrapper - **Routing:** `/` → `Library`, `*` → redirect to `/` - **Code splitting:** `Library` loaded via `React.lazy` + `` ### Library - **State:** `books[]`, `stats`, `search`, `sortBy`, `statusFilter`, `currentPage`, `view` - **Views:** `library` (grid), `detail` (selected book), `add` (modal form) - **Sub-components:** `BookCard`, `BookDetail`, `BookForm` - **Data flow:** Calls `fetchBooks()` on mount and when filters/page change ### BookCard - **Props:** `{ book: Book, onClick: (book: Book) => void }` - **Display:** Cover image (or first-letter placeholder), title, author, genre badge, reading progress bar with status color - **Interaction:** Click/keyboard-accessible (Enter/Space) ### BookDetail - **Props:** `{ bookId: number, onBack: () => void, onUpdated: () => void }` - **Sections:** Cover, metadata (title, author, genre, ISBN), progress bar with page count, action buttons - **Actions:** Mark as Finished, Edit Details (switches to BookForm), Delete (with confirmation) - **States:** Loading, error, editing mode ### BookForm - **Props:** `{ initialData?: Book, onSubmit: (data: BookFormData) => Promise, onCancel: () => void }` - **Fields:** Title*, Author*, Genre, Description, Cover URL, ISBN, Total Pages, Current Page, Status - **Client validation:** Title/author required, page ≤ total pages - **Loading state:** Submit button shows "Saving..." when `isLoading` ## Types ```typescript interface Book { id: number; title: string; author: string; genre: string; description: string; cover_image_url: string; isbn: string; total_pages: number; current_page: number; reading_status: ReadingStatus; reading_progress: number; owner: string; created_at: string; updated_at: string; } type ReadingStatus = 'not_started' | 'reading' | 'finished' | 'dnf'; interface BookFormData { title: string; author: string; genre: string; description: string; cover_image_url: string; isbn: string; total_pages: number; current_page: number; reading_status: ReadingStatus; } ``` ## API Client (`api/books.ts`) | Function | HTTP Call | |----------------------|------------------------------------| | `fetchBooks(params)` | `GET /api/books/` | | `fetchBook(id)` | `GET /api/books/{id}/` | | `createBook(data)` | `POST /api/books/` | | `updateBook(id, data)` | `PATCH /api/books/{id}/` | | `deleteBook(id)` | `DELETE /api/books/{id}/` | | `markAsFinished(id)` | `POST /api/books/{id}/mark_finished/` | | `fetchBookStats()` | `GET /api/books/stats/` | ## Styling Dark theme with CSS custom properties. All styles in `App.css`. Responsive grid layout with breakpoint at 768px. ## Build & Dev - `yarn workspace frontend dev` — Vite dev server on port 5173 with API proxy to 127.0.0.1:8000 - `yarn workspace frontend build` — TypeScript check + Vite production build