# Dashboard (Overview)

| | |
|---|---|
| **Nav ID** | `overview` |
| **Component** | `AdminOverviewPage.jsx` |
| **Service** | `dashboardApi.js` |

---

## Endpoints

### GET `/dashboard/stats`

Aggregated permit metrics, trend chart, location/job-type breakdown, and recent activity.

**Query parameters**

| Param | Values | Default |
|-------|--------|---------|
| `period` | `7d`, `30d`, `90d`, `year`, `all` | `30d` |

**Example**

```http
GET /api/ptw/dashboard/stats?period=30d
Authorization: Bearer <admin_token>
```

**Response shape**

```json
{
  "period": "30d",
  "user": {
    "location_id": 3,
    "location_name": "Plant A"
  },
  "cards": {
    "my_permits": 5,
    "my_location_permits": 12,
    "active": 8,
    "closed": 4
  },
  "trend": [
    { "period": "2026-06-01", "label": "Jun 1", "count": 2, "closed": 0 }
  ],
  "location_wise": [
    { "label": "Plant A", "value": 7 }
  ],
  "job_type_wise": [
    { "label": "Hot Work", "value": 3, "color": "#ff6600" }
  ],
  "recent_activity": [
    {
      "permit_id": 42,
      "permit_number": "PTW-1719500000000",
      "location_name": "Plant A",
      "created_at": "2026-06-27T10:00:00.000Z",
      "status_label": "Active",
      "status_tone": "active"
    }
  ]
}
```

**Notes**

- `my_permits` counts permits where `uploader_user_id` matches the JWT user.
- `my_location_permits` uses the logged-in user's `location_id`.
- Clicking dashboard cards navigates to **Permits & Submissions** with filter query params (frontend only).

**Controller:** `ptwDashboardController.js` → `getDashboardStats`

---

## SQL queries

### Resolve logged-in user context

```sql
SELECT u.user_id, u.location_id, loc.location_name
FROM users u
LEFT JOIN locations loc ON loc.location_id = u.location_id
WHERE u.user_id = ?
LIMIT 1;
```

### Load permits (with optional period filter)

When `period=30d`, a `created_at >= ?` clause is appended.

```sql
SELECT p.permit_id, p.permit_number, p.uploader_user_id, p.permit_status_id,
       p.location_id, p.valid_from, p.valid_until, p.created_at,
       loc.location_name,
       u.location_id AS uploader_location_id
FROM ptw_permits p
LEFT JOIN locations loc ON loc.location_id = p.location_id
LEFT JOIN users u ON u.user_id = p.uploader_user_id
WHERE p.created_at >= ?          -- omitted when period=all
ORDER BY p.created_at DESC;
```

Card counts (`my_permits`, `active`, `closed`, etc.) are computed in Node.js from this result set.

### Job-type breakdown (submissions joined to forms)

```sql
SELECT s.submission_id, s.permit_id, s.created_at,
       jt.job_type_id, jt.job_type_name, jt.job_colour_code
FROM ptw_submissions s
INNER JOIN ptw_form_details f ON f.form_id = s.form_id
INNER JOIN ptw_job_type jt ON jt.job_type_id = f.job_type_id
INNER JOIN ptw_permits p ON p.permit_id = s.permit_id
WHERE p.created_at >= ?          -- same period filter as permits
;
```

General and lifecycle job types are excluded in application logic before building `job_type_wise`.
