# Testimonial API

## Create Testimonial

`POST /api/testimonial`

Create a new testimonial.

### Headers
- `Authorization: Bearer <token>`
- `Content-Type: multipart/form-data`

### Body (multipart/form-data)

| Field | Type | Required | Description |
|-------|------|----------|-------------|
| clientName | string | Yes | Client/patient name |
| clientTitle | string | No | e.g. "Verified Patient", "Parent of Patient" |
| reviewText | string | Yes | Testimonial text content |
| videoUrl | string | No | YouTube/Vimeo link or cloud storage URL |
| displayOrder | int | No | Sort order (default: 0) |
| isActive | boolean | No | Visibility on website (default: true) |
| clientImage | file | No | Client profile photo (allowed: png, avif, webp, jpg, jpeg, gif, mp4, mp3, mov, pdf, doc, docx, xls, xlsx, ppt, pptx, txt, csv, zip. Max 10MB) |

### Response

```json
{
  "message": "Testimonial create success",
  "success": true,
  "data": {
    "_id": "...",
    "clientName": "Jane Smith",
    "clientTitle": "Verified Patient",
    "clientImageUrl": "https://cherryk.s3.ap-southeast-1.amazonaws.com/Testimonial/ClientImage/1687154298_avatar.jpg",
    "reviewText": "Excellent service and very professional team.",
    "videoUrl": "https://www.youtube.com/watch?v=...",
    "displayOrder": 1,
    "isActive": true,
    "isDeleted": false,
    "createdAt": "...",
    "updatedAt": "..."
  }
}
```

---

## Get Testimonial

`GET /api/testimonial/:id`

Retrieve a single testimonial.

### Headers
- `Authorization: Bearer <token>`

### Parameters

| Param | Type | Required | Description |
|-------|------|----------|-------------|
| id | string | Yes | Testimonial MongoId |

### Response

```json
{
  "success": true,
  "data": { "...": "..." }
}
```

### Errors
- `404` — Testimonial not found

---

## Update Testimonial

`PUT /api/testimonial/:id`

Update an existing testimonial.

### Headers
- `Authorization: Bearer <token>`
- `Content-Type: multipart/form-data`

### Parameters

| Param | Type | Required | Description |
|-------|------|----------|-------------|
| id | string | Yes | Testimonial MongoId |

### Body (multipart/form-data)

| Field | Type | Required | Description |
|-------|------|----------|-------------|
| clientName | string | No | Client/patient name |
| clientTitle | string | No | e.g. "Verified Patient", "Parent of Patient" |
| reviewText | string | No | Testimonial text content |
| videoUrl | string | No | YouTube/Vimeo link or cloud storage URL |
| displayOrder | int | No | Sort order |
| isActive | boolean | No | Visibility on website |
| clientImage | file | No | New client photo (replaces old one; old file deleted from S3) |

> **Image replacement:** Uploading a new `clientImage` file replaces the old photo. The old file is deleted from S3.

### Errors
- `404` — Testimonial not found

---

## Delete Testimonial (Soft Delete)

`DELETE /api/testimonial/:id`

Soft-delete a testimonial (sets `isDeleted` to `true`). Also deletes the associated client image from S3.

### Headers
- `Authorization: Bearer <token>`

### Parameters

| Param | Type | Required | Description |
|-------|------|----------|-------------|
| id | string | Yes | Testimonial MongoId |

### Response

```json
{
  "success": true,
  "data": { "isDeleted": true }
}
```

### Errors
- `404` — Testimonial not found

---

## Activate Testimonial

`POST /api/testimonial/:id`

Restore a soft-deleted testimonial (sets `isDeleted` to `false`).

### Headers
- `Authorization: Bearer <token>`

### Parameters

| Param | Type | Required | Description |
|-------|------|----------|-------------|
| id | string | Yes | Testimonial MongoId |

### Response

```json
{
  "success": true,
  "data": { "isDeleted": false }
}
```

### Errors
- `404` — Testimonial not found

---

## Activate Testimonial (Visibility)

`POST /api/testimonial/:id/activate`

Set a testimonial's `isActive` to `true` (show on website).

### Headers
- `Authorization: Bearer <token>`

### Parameters

| Param | Type | Required | Description |
|-------|------|----------|-------------|
| id | string | Yes | Testimonial MongoId |

### Response

```json
{
  "success": true,
  "data": { "isActive": true }
}
```

### Errors
- `404` — Testimonial not found

---

## Deactivate Testimonial (Visibility)

`POST /api/testimonial/:id/deactivate`

Set a testimonial's `isActive` to `false` (hide from website).

### Headers
- `Authorization: Bearer <token>`

### Parameters

| Param | Type | Required | Description |
|-------|------|----------|-------------|
| id | string | Yes | Testimonial MongoId |

### Response

```json
{
  "success": true,
  "data": { "isActive": false }
}
```

### Errors
- `404` — Testimonial not found

---

## List All Testimonials (Paginated)

`GET /api/testimonials`

Get a paginated list of active (non-deleted) testimonials.

### Headers
- `Authorization: Bearer <token>`

### Query Parameters

| Param | Type | Required | Default | Description |
|-------|------|----------|---------|-------------|
| keyword | string | No | — | Search by client name or review text (case-insensitive) |
| isActive | boolean | No | — | Filter by active status |
| limit | int | No | 10 | Items per page (max 100) |
| page | int | No | 1 | Page number (starts at 1) |

Results are sorted by `displayOrder` ascending.

### Response

```json
{
  "success": true,
  "count": 0,
  "_metadata": {
    "current_page": 1,
    "per_page": 10,
    "page_count": 0,
    "total_count": 0
  },
  "list": []
}
```

Results are now paginated using `page` (1-based) and `limit`. `skip` is calculated internally.

---

## Sort Testimonials

`PUT /api/testimonials/sort`

Bulk-update the sort order of active (non-deleted, visible) testimonials.

### Headers
- `Authorization: Bearer <token>`
- `Content-Type: application/json`

### Body

| Field | Type | Required | Description |
|-------|------|----------|-------------|
| items | array | Yes | Array of sort items |
| items[]._id | string | Yes | Testimonial MongoId |
| items[].displayOrder | int | Yes | New sort position (non-negative) |

### Response

```json
{
  "success": true,
  "message": "Sort order updated"
}
```

### Errors
- `500` — Server error

Only items where `isDeleted: false` and `isActive: true` will be updated; others are silently skipped.

---

## List Website Testimonials (Public)

`GET /api/website/testimonials`

Public endpoint for the Cherry-K website. Returns all active testimonials (no auth, no pagination).

### Response

```json
{
  "success": true,
  "data": [
    {
      "_id": "...",
      "clientName": "Jane Smith",
      "clientTitle": "Verified Patient",
      "clientImageUrl": "https://cherryk.s3.ap-southeast-1.amazonaws.com/Testimonial/ClientImage/1687154298_avatar.jpg",
      "reviewText": "Excellent service and very professional team.",
      "videoUrl": "https://www.youtube.com/watch?v=...",
      "displayOrder": 1,
      "isActive": true,
      "isDeleted": false,
      "createdAt": "...",
      "updatedAt": "..."
    }
  ]
}
```

### Errors
- `404` — No testimonials found

Filters: `isDeleted: false`, `isActive: true`. Sorted by `displayOrder` ascending.
