- Rewrote frontend from JSX to TypeScript (TSX) - AppLayout with MUI AppBar, ThemeProvider, CssBaseline - HomePage at / with dashboard metrics and recent updates - MetricsPanel with 4 metric cards (total, interviews, offers, rejection rate) - UpdateCard for each job update with status chip - LoadingSkeleton during data fetch - Error Alert on API failure - Create New Job Application button navigating to /applications/new - Vite proxy for /api → backend on :8000 - useDashboardData custom hook with concurrent fetch Closes #2
115 lines
3.2 KiB
Markdown
115 lines
3.2 KiB
Markdown
# Spec: Home Page with MUI Components
|
|
|
|
## Ticket
|
|
Gitea: crisleo-hermes/job-tracker#2
|
|
Kanban: t_e6936299
|
|
|
|
## Overview
|
|
Build a home page at route `/` using Material-UI (MUI) components that displays the last three job updates and dashboard metrics, with a button to create a new job application.
|
|
|
|
## Pages
|
|
|
|
### HomePage (`/`)
|
|
- AppBar with title "Job Tracker"
|
|
- Dashboard metrics cards row (total applications, interviews, offers, rejection rate)
|
|
- Last 3 job updates displayed as cards
|
|
- "Create New Job Application" button (navigates to creation flow)
|
|
- Loading skeleton while data fetches
|
|
- Error snackbar on API failure
|
|
- Responsive Grid layout
|
|
|
|
## API Endpoints Consumed
|
|
|
|
| Method | Path | Description |
|
|
|--------|------|-------------|
|
|
| GET | `/api/updates/latest/` | 3 most recent JobUpdates with nested job info |
|
|
| GET | `/api/updates/metrics/` | Dashboard metrics (total, status breakdown, interviews, offers, rejection rate) |
|
|
|
|
### Response Shapes
|
|
|
|
**GET /api/updates/latest/**
|
|
```json
|
|
[
|
|
{
|
|
"id": 1,
|
|
"job_id": 1,
|
|
"company_name": "Acme Corp",
|
|
"position_title": "Software Engineer",
|
|
"from_status": "APPLIED",
|
|
"to_status": "INTERVIEW",
|
|
"notes": "Moving to next round",
|
|
"created_at": "2025-01-15T10:00:00Z",
|
|
"metrics": {"response_time_days": 5}
|
|
}
|
|
]
|
|
```
|
|
|
|
**GET /api/updates/metrics/**
|
|
```json
|
|
{
|
|
"total_applications": 12,
|
|
"status_breakdown": {"APPLIED": 5, "INTERVIEW": 4, "OFFER": 2, "REJECTED": 1},
|
|
"interviews_count": 6,
|
|
"offers_count": 2,
|
|
"rejection_rate": 8.3
|
|
}
|
|
```
|
|
|
|
## Components
|
|
|
|
### `AppLayout`
|
|
- MUI `AppBar` with `Toolbar`, `Typography` ("Job Tracker")
|
|
- Wraps child content via `Outlet` from React Router
|
|
|
|
### `UpdateCard`
|
|
- MUI `Card` → `CardContent`
|
|
- Displays: company name, position title, status change (`from_status` → `to_status`), created date, metrics summary
|
|
- Props: `JobUpdate`
|
|
|
|
### `MetricsPanel`
|
|
- MUI `Grid` container with metric `Card` items
|
|
- Each metric: total applications, interviews count, offers count, rejection rate
|
|
- Props: `DashboardMetrics`
|
|
|
|
### `LoadingSkeleton`
|
|
- MUI `Skeleton` components mimicking the home page layout
|
|
|
|
## Data Fetching
|
|
|
|
Custom hook `useDashboardData` using React's `useEffect` + `useState`:
|
|
- Fetches from `/api/updates/latest/` and `/api/updates/metrics/` concurrently (`Promise.all`)
|
|
- Vite proxy: `/api` → `http://localhost:8000/api`
|
|
- States: loading, error (with error message), data
|
|
|
|
## TypeScript Types
|
|
|
|
```typescript
|
|
interface JobUpdate {
|
|
id: number;
|
|
job_id: number;
|
|
company_name: string;
|
|
position_title: string;
|
|
from_status: string | null;
|
|
to_status: string;
|
|
notes: string;
|
|
created_at: string;
|
|
metrics: Record<string, unknown>;
|
|
}
|
|
|
|
interface DashboardMetrics {
|
|
total_applications: number;
|
|
status_breakdown: Record<string, number>;
|
|
interviews_count: number;
|
|
offers_count: number;
|
|
rejection_rate: number;
|
|
}
|
|
```
|
|
|
|
## Acceptance Criteria
|
|
- [x] Home page at route `/`
|
|
- [x] MUI components exclusively (AppBar, Typography, Card, Grid, Button, Skeleton, Snackbar)
|
|
- [x] Last 3 job updates: job ID, status, creation date, metrics
|
|
- [x] "Create New Job Application" button navigates to `/applications/new`
|
|
- [x] Loading indicators while fetching data (Skeleton components)
|
|
- [x] Graceful error handling (Snackbar with error message)
|
|
- [x] Responsive layout (Grid breakpoints) |