# PROMPT 03 — Admin Dashboard CRUDs

## Context
In **Water Rush**, all admin API routes are:
- Prefixed: `/api/admin/`
- Protected: `middleware('auth:sanctum')`
- Response format: `{ "status": true/false, "message": "...", "data": ... }`

All file uploads should be stored using Laravel storage and return full URLs.

---

## Task — Create the following controllers under `App\Http\Controllers\`

---

### 1. `CustomerController`
**Routes prefix:** `/api/admin/customers`

| Method | Route | Action |
|--------|-------|--------|
| GET | `/` | index |
| GET | `/{id}` | show |
| PUT | `/{id}` | update |
| DELETE | `/{id}` | destroy |
| PATCH | `/{id}/toggle` | toggle is_active |

- `index`: paginated list, search by name or phone, include addresses count and orders count
- `show`: customer with addresses and orders summary
- `update`: name, phone, is_active
- `destroy`: soft check — do not delete if customer has orders
- `toggle`: flip is_active between 0 and 1

---

### 2. `DriverController`
**Routes prefix:** `/api/admin/drivers`

| Method | Route | Action |
|--------|-------|--------|
| GET | `/` | index |
| GET | `/{id}` | show |
| POST | `/` | store |
| PUT | `/{id}` | update |
| DELETE | `/{id}` | destroy |
| PATCH | `/{id}/toggle` | toggle is_active |

- Fields: name, phone, password, vehicle_type, vehicle_number, vehicle_year
- Hash password on store/update
- `index`: search by name or phone

---

### 3. `CategoryController`
**Routes prefix:** `/api/admin/categories`

| Method | Route | Action |
|--------|-------|--------|
| GET | `/` | index |
| GET | `/{id}` | show |
| POST | `/` | store |
| PUT | `/{id}` | update |
| DELETE | `/{id}` | destroy |
| PATCH | `/{id}/toggle` | toggle is_active |

- Fields: name
- `destroy`: do not delete if category has products

---

### 4. `ProductController`
**Routes prefix:** `/api/admin/products`

| Method | Route | Action |
|--------|-------|--------|
| GET | `/` | index |
| GET | `/{id}` | show |
| POST | `/` | store |
| PUT | `/{id}` | update |
| DELETE | `/{id}` | destroy |
| PATCH | `/{id}/toggle` | toggle is_active |

- Fields: title, category_id, description (optional), price, price_before (optional)
- Images: accept `images[]` array of files → save each to `storage/app/public/products/` → store paths in `product_images` table
- On update: accept `new_images[]` to add more, accept `delete_image_ids[]` to remove specific images
- `index`: filter by `category_id` query param, return with images and category name
- `show`: return with images and category

---

### 5. `SliderController`
**Routes prefix:** `/api/admin/sliders`

| Method | Route | Action |
|--------|-------|--------|
| GET | `/` | index |
| POST | `/` | store |
| PUT | `/{id}` | update |
| DELETE | `/{id}` | destroy |
| PATCH | `/{id}/toggle` | toggle is_active |

- Fields: image (file upload → `storage/app/public/sliders/`), link (optional), sort_order

---

### 6. `PromoCodeController`
**Routes prefix:** `/api/admin/promo-codes`

| Method | Route | Action |
|--------|-------|--------|
| GET | `/` | index |
| GET | `/{id}` | show |
| POST | `/` | store |
| PUT | `/{id}` | update |
| DELETE | `/{id}` | destroy |
| PATCH | `/{id}/toggle` | toggle is_active |

- Fields: code (unique), type (percent/fixed), discount, quantity, expires_at (optional)
- `index`: show used_count alongside quantity

---

### 7. `BundleController`
**Routes prefix:** `/api/admin/bundles`

| Method | Route | Action |
|--------|-------|--------|
| GET | `/` | index |
| GET | `/{id}` | show |
| POST | `/` | store |
| PUT | `/{id}` | update |
| DELETE | `/{id}` | destroy |
| PATCH | `/{id}/toggle` | toggle is_active |

- Fields: title, description (optional), price, price_before (optional)
- Images: same as products → store in `storage/app/public/bundles/` → `bundle_images` table
- Bundle items: accept `items[]` array of `{ product_id, quantity }` → sync on store/update (delete old, insert new)
- `show`: return with images and items (with product title and price)

---

### 8. `ScheduledTimeController`
**Routes prefix:** `/api/admin/scheduled-times`

| Method | Route | Action |
|--------|-------|--------|
| GET | `/` | index |
| POST | `/` | store |
| PUT | `/{id}` | update |
| DELETE | `/{id}` | destroy |
| PATCH | `/{id}/toggle` | toggle is_active |

- Fields: label, time_from, time_to

---

### 9. `OrderController`
**Routes prefix:** `/api/admin/orders`

| Method | Route | Action |
|--------|-------|--------|
| GET | `/` | index |
| GET | `/{id}` | show |
| PATCH | `/{id}/status` | updateStatus |
| PATCH | `/{id}/assign-driver` | assignDriver |

- `index`: paginated, filter by status, delivery_date range, customer_id
- `show`: full order with:
  - customer (name, phone)
  - driver (name, phone, vehicle) if assigned
  - address details
  - order items with product or bundle info
  - promo code info if used
  - scheduled time if selected
- `updateStatus`: accept `status` field — validate against enum values
- `assignDriver`: accept `driver_id` → assign to order

---

### 10. `StatsController`
**Route:** `GET /api/admin/stats`

Return:
```json
{
  "status": true,
  "data": {
    "total_orders": 0,
    "today_orders": 0,
    "total_revenue": 0,
    "today_revenue": 0,
    "orders_by_status": {
      "pending": 0,
      "confirmed": 0,
      "preparing": 0,
      "on_the_way": 0,
      "delivered": 0,
      "cancelled": 0
    },
    "top_products": [],
    "top_customers": [],
    "monthly_revenue": []
  }
}
```

- `top_products`: top 5 products by total quantity sold in order_items (include title, total_sold)
- `top_customers`: top 5 customers by number of orders (include name, phone, orders_count)
- `monthly_revenue`: array of 12 months for current year with total revenue per month

---

## Notes
- All file uploads: use `$request->file()->store('folder', 'public')` and return `Storage::url(path)`
- Run `php artisan storage:link` if not already done
- All controllers should use Form Request validation classes
- Return 404 with `{ status: false, message: "Not found" }` when record doesn't exist
