# 19 — Suggestions

## Overview

A read-only inbox of **product suggestions** submitted by end users via the public apps. Each row is freeform user feedback tagged with the originating app ("client" or "vendor").

**Status:** Live (read-only)

The "Take Action" button on the list is purely cosmetic — it has no backend wired up.

---

## User Stories

| ID | As a | I want to | So that |
|----|------|-----------|---------|
| SGN-01 | Ops (perm 1) | See a paginated list of suggestions from users | Spot trends |
| SGN-02 | Ops | See suggester's username + phone + app type + date | Contextualise |
| SGN-03 | Ops | See the full suggestion text | Read it |

---

## Screens & Flows

```
┌──────────────────────────────────────┐
│ /suggestions  suggestions/list.blade │
└──────────────────┬───────────────────┘
                   │ DataTable AJAX
                   ▼
┌──────────────────────────────────────┐
│ /ajaxSuggestionList                  │
│ Suggestions::ajaxList()              │
│  joins users for username + phone    │
└──────────────────┬───────────────────┘
                   │ "Take Action" → currently no-op
                   ▼
              (No detail page — `suggestion/{id}` just returns
               an empty `details.blade.php` view)
```

### Routes & Actions

| Route | Method | Handler | Description |
|-------|--------|---------|-------------|
| `/suggestions` | GET | `SuggestionController::suggestions()` | List page |
| `/ajaxSuggestionList` | GET | `SuggestionController::ajaxSuggestionList()` | DataTable JSON |
| `/suggestion/{id}` | GET | `SuggestionController::details()` | Returns an empty Blade (`suggestions/details.blade.php`) |

---

## Data Model

### `app_suggestions`

```php
// App\Models\Suggestions
protected $table = 'app_suggestions';

{
  id:          int,
  uuid:        string,
  user_id:     int,             // FK users.id
  description: string,           // the suggestion body
  app_type:    'client'|'vendor',
  created_at, updated_at,
}
```

### Joined Display

```
username   = users.display_name
phone      = users.phone
description
app_type
created_at
```

---

## Validations & Business Rules

| Rule | Detail |
|------|--------|
| Sort | `created_at DESC` |
| Pagination | DataTable `start, length` |
| Inner join to `users` | Suggestions from deleted users are dropped from the list |

---

## API Endpoints

| Method | Path | Auth | Request | Response | Consumer |
|--------|------|------|---------|----------|----------|
| GET | `/suggestions` | session + permission 1 | — | HTML | Browser |
| GET | `/ajaxSuggestionList` | session (AJAX whitelisted) | `draw, start, length` | DataTable JSON | List page |
| GET | `/suggestion/{id}` | session + permission 1 | path id | HTML (empty Blade) | Browser |

---

## Upstream Impact

- **`app_suggestions` table** — written by OotboAPI when end users submit suggestions via the public apps.
- **`users` table** — joined for username + phone.

---

## Downstream Impact

- **None** — read-only.

---

## Impact of Changes

| If you change... | Risk to... | Level | Type |
|-----------------|------------|-------|------|
| Renaming `app_suggestions.description` | List column becomes blank | High | Data |
| Renaming `app_suggestions.app_type` | Filter / display column blank | Medium | Data |
| Removing the inner join on `users` | Suggestions from deleted users no longer visible | Low | Data |
| Adding wire-up to "Take Action" button | Must add new route/handler — currently dead | Low | UI |

---

## Known Issues

- **"Take Action" button is dead.** The list template renders it but it has no backend. Misleading for ops.
- **`/suggestion/{id}` returns an empty view** — there is no actual detail page for a single suggestion. The DataTable list is the only useful surface.
- **No filter by `app_type`** — client vs vendor suggestions are interleaved.
- **No "mark seen / dismissed" mechanism** — every suggestion shows forever.
