# Spesifikasi Fitur Client Inbox untuk Mobile App

Dokumen ini berisi **daftar lengkap fitur** yang ada di `Client/InboxController.php` (web) dan **prompt implementasi** untuk menerapkannya di aplikasi mobile (React Native / Flutter / native).

---

## Daftar Fitur Client Inbox (Backend)

### 1. **Halaman / Konteks Awal**
- **index** – Halaman inbox dengan data client, package_type, dan (opsional) Judol violation count 24h untuk tampilan peringatan.

### 2. **Daftar WABA (Nomor WhatsApp Business)**
- **apiWabaList** – `GET /client/inbox/waba-list`
- Mengembalikan daftar WABA yang bisa dipakai client (OAuth/Self-Service milik client, dedicated, shared sesuai package).
- Response: `results[]` dengan `id`, `text`, `phone_number`, `type`; plus `client_id`.

### 3. **Daftar Percakapan (Conversations)**
- **apiConversations** – `GET /client/inbox/conversations`
- **Query params:**
  - `waba_pool_id` (opsional) – filter per WABA
  - `status` – `open` | `closed` (opsional)
  - `page` – default 1
  - `per_page` – 1–50, default 20
  - `search` – cari nama/nomor/kontak/preview pesan
  - `only_contacts` – boolean, hanya percakapan yang punya contact
  - `unread_only` – boolean
  - `read_status` – `all` | `read` | `unread` | `unreplied`
  - `sort_order` – `asc` | `desc` (default: unread = asc, lainnya desc)
- Response: `data[]` (list percakapan), `meta`: `current_page`, `per_page`, `total`, `has_more`.
- Setiap item: `id`, `customer_number`, `customer_name`, `waba_pool`, `agent`, `is_assigned`, `status`, `unread_count`, `last_from`, `last_message` (body, type, timestamp), `last_message_at`.

### 4. **Statistik Chat (Badge / Ringkasan)**
- **chatCount** – `GET /client/inbox/chat-count`
  - Query: `status`, `read_status`
  - Response: `chat_count`, `unread_count` (untuk badge global).
- **apiChatCounts** – `GET /client/inbox/chat-counts`
  - Query: `status`
  - Response: `total`, `read`, `unread`, `unreplied` (untuk tab filter All / Read / Unread / Unreplied).

### 5. **Pesan dalam Satu Percakapan**
- **apiMessages** – `GET /client/inbox/conversations/{assignment}/messages`
- Query: `sort_order` = `asc` | `desc` (default asc = lama ke baru).
- Saat dibuka: pesan belum dibaca di-mark read.
- Response: `data[]` (gabungan incoming + outgoing, deduplikasi by `wa_message_id`), `assignment` (customer_number, customer_name, contact_name, contact_email, status, notes, waba_pool_id, agent), `service_window` (status 24h window).
- Setiap pesan: `id` (numeric untuk incoming, `out_123` untuk outgoing), `wa_message_id`, `type`, `body`, `direction`, `timestamp`, `media_url`, `template`, `interactive`, `quoted_message`, `deleted_at`, `status` (outgoing), dll.

### 6. **Cari dalam Chat**
- **searchMessages** – `GET /client/inbox/conversations/{assignment}/search`
- Query: `q` (required), `page`, `per_page` (1–200, default 50).
- Response: `data[]` (id, direction, type, body, timestamp, wa_message_id), `meta`: total, current_page, per_page.

### 7. **Simpan Catatan (Notes)**
- **saveNote** – `POST /client/inbox/conversations/{assignment}/notes`
- Body: `notes` (nullable string, max 2000).
- Response: `success`, `notes`.

### 8. **Simpan / Update Kontak**
- **saveContact** – `POST /client/inbox/conversations/{assignment}/save-contact`
- Body: `name` (required), `email` (nullable), `tags` (array of string).
- Response: `success`, `message`, `contact`.

### 9. **Kirim Pesan**
- **sendMessage** – `POST /client/inbox/conversations/{assignment}/send`
- Body tergantung `type`:
  - **text**: `type`, `message`, `context_message_id` (reply), `client_message_id` (untuk realtime matching).
  - **image/document/video/audio**: `type`, `media_id` atau `media_url`, `caption` (opsional), `filename` (untuk document).
  - **template**: `type`, `template_name`, `template_language`, `components`.
  - **interactive**: `type`, `interactive` (object).
- Validasi: service window 24h (non-template butuh window open); Judol/Forbidden keyword bisa return 422.
- Response: `success`, `message_id` (out_xxx), `wa_message_id`, `type`.

### 10. **Hapus Pesan**
- **deleteMessage** – `DELETE /client/inbox/conversations/{assignment}/messages/{messageId}`
- `messageId`: numeric = incoming (WabaInboxMessage), `out_123` = outgoing. Outgoing: revoke di WA jika dalam 1 jam.
- Response: `success`, `revoked_via_wa`, `message`.

### 11. **Retry Media (Antrian)**
- **retryMedia** – `POST /client/inbox/conversations/{assignment}/messages/{messageId}/retry-media`
- Hanya untuk pesan masuk (numeric id). Masukkan ulang ke antrian download.
- Response: `success`, `message`.

### 12. **Refetch Media (Langsung)**
- **refetchMedia** – `POST /client/inbox/conversations/{assignment}/messages/{messageId}/refetch-media`
- Hanya incoming; proses download/upload langsung (synchronous).
- Response: `success`, `message`, `data` (media_url, media_filename, media_size, media_status).

### 13. **Edit Pesan (Simulasi)**
- **editMessage** – `PUT /client/inbox/conversations/{assignment}/messages/{messageId}`
- Hanya outgoing text (`out_123`). Body: `message` (teks baru). Original di-mark edited; pesan koreksi dikirim dengan prefix "✏️ Perbaikan pesan sebelumnya:".
- Response: `success`, `message_id`, `new_message` (object).

### 14. **Tutup / Buka Kembali Chat**
- **closeChat** – `POST /client/inbox/conversations/{assignment}/close`
- **reopenChat** – `POST /client/inbox/conversations/{assignment}/reopen`
- Response: `success`, `message`.

---

## Endpoint Ringkas (Base: `/client/inbox`)

| Method | Endpoint | Deskripsi |
|--------|----------|-----------|
| GET | `/waba-list` | Daftar WABA |
| GET | `/conversations` | Daftar percakapan (paginated) |
| GET | `/chat-count` | Total & unread (badge) |
| GET | `/chat-counts` | Total, read, unread, unreplied |
| GET | `/conversations/{id}/messages` | Pesan dalam percakapan |
| GET | `/conversations/{id}/search?q=` | Cari dalam chat |
| POST | `/conversations/{id}/notes` | Simpan notes |
| POST | `/conversations/{id}/save-contact` | Simpan/update kontak |
| POST | `/conversations/{id}/send` | Kirim pesan |
| DELETE | `/conversations/{id}/messages/{messageId}` | Hapus pesan |
| POST | `/conversations/{id}/messages/{messageId}/retry-media` | Retry download media |
| POST | `/conversations/{id}/messages/{messageId}/refetch-media` | Refetch media sync |
| PUT | `/conversations/{id}/messages/{messageId}` | Edit pesan (outgoing text) |
| POST | `/conversations/{id}/close` | Tutup chat |
| POST | `/conversations/{id}/reopen` | Buka kembali chat |

---

## Perbandingan: Web Client Inbox vs API Mobile

API mobile (`/api/mobile/...`) **sudah selaras** dengan fitur client inbox (web). Ringkasan:

| Fitur Client Inbox (Web) | Endpoint Web | Endpoint Mobile | Status |
|--------------------------|--------------|-----------------|--------|
| **Daftar WABA** | GET /client/inbox/waba-list | GET /api/mobile/waba-list | ✅ Ada |
| **List conversations** | GET /client/inbox/conversations (page, per_page, has_more) | GET /api/mobile/conversations (cursor, limit, hasMore, nextCursor) | ✅ Ada (lazy load) |
| **Chat count (badge)** | GET /client/inbox/chat-count | GET /api/mobile/conversations/counts → `data.unread` | ✅ Ada |
| **Chat counts (tab)** | GET /client/inbox/chat-counts (total, read, unread, unreplied) | GET /api/mobile/conversations/counts | ✅ Ada (total, read, unread, unreplied) |
| **Detail percakapan** | (dari apiMessages + assignment) | GET /api/mobile/conversations/{id} | ✅ Ada |
| **List pesan** | GET .../conversations/{id}/messages | GET /api/mobile/conversations/{id}/messages (cursor) | ✅ Ada |
| **Cari dalam chat** | GET .../conversations/{id}/search?q= | GET /api/mobile/conversations/{id}/messages/search | ✅ Ada |
| **Simpan notes** | POST .../notes | PUT /api/mobile/conversations/{id}/notes | ✅ Ada |
| **Simpan kontak** | POST .../save-contact | PUT /api/mobile/conversations/{id}/contact | ✅ Ada |
| **Kirim pesan** | POST .../send | POST /api/mobile/conversations/{id}/send (dan send-template) | ✅ Ada |
| **Hapus pesan** | DELETE .../messages/{messageId} | DELETE /api/mobile/conversations/{id}/messages/{messageId} | ✅ Ada |
| **Edit pesan** | PUT .../messages/{messageId} | PUT /api/mobile/conversations/{id}/messages/{messageId} | ✅ Ada |
| **Tutup chat** | POST .../close | POST /api/mobile/conversations/{id}/close | ✅ Ada |
| **Buka kembali chat** | POST .../reopen | POST /api/mobile/conversations/{id}/reopen | ✅ Ada |
| **Retry media** | POST .../messages/{id}/retry-media | POST /api/mobile/conversations/{id}/messages/{messageId}/retry-media | ✅ Ada |
| **Refetch media** | POST .../messages/{id}/refetch-media | POST /api/mobile/conversations/{id}/messages/{messageId}/refetch-media | ✅ Ada |
| **Upload media** | POST /api/media/upload | POST /api/mobile/media/upload | ✅ Ada |

**Dokumentasi API lengkap:** lihat **`docs/MOBILE_API_INBOX.md`**.

---

# PROMPT LENGKAP UNTUK MOBILE APP (Client Inbox)

Gunakan blok di bawah ini sebagai **prompt** saat memodifikasi atau membangun fitur inbox di aplikasi mobile (React Native, Flutter, atau native) agar selaras dengan backend MeeChat Client Inbox.

---

## Prompt: Implementasi Client Inbox di Mobile App

**Konteks:** Aplikasi mobile (React Native / Flutter / native) harus menyediakan fitur inbox yang setara dengan web. Gunakan **Mobile API**: Base URL `{BASE_URL}/api/mobile`, autentikasi **Bearer token** (Sanctum).

**Persyaratan umum:**
- Header: `Authorization: Bearer {token}`.
- Tangani error: 401 (token), 403 (akses), 404 (not found), 422 (service_window_expired, content_blocked), 400/500.

**Referensi lengkap:** `docs/MOBILE_API_INBOX.md`.

---

### 1. List Conversations dengan Pagination Lazy Load

- **Fitur:** Daftar percakapan dengan **infinite scroll** (lazy load).
- **API:** `GET /api/mobile/conversations` dengan query `limit` (default 20, max 50) dan `cursor` (untuk halaman berikutnya).
- **Implementasi:**
  - Request pertama: tanpa `cursor`, `limit=20`. Response: `data` (array), `meta.hasMore` (boolean), `meta.nextCursor` (string atau null).
  - Jika `meta.hasMore === true`, saat user scroll ke bawah, panggil lagi dengan **query `cursor=meta.nextCursor`** (nilai persis dari response). Append hasil ke list.
  - Tampilkan setiap item: avatar, `customerName`, `lastMessage.preview`, `lastMessageAt`, `unreadCount`, `status`, `waba` (jika multi-WABA).
  - Pull-to-refresh: kosongkan list, fetch tanpa cursor (halaman pertama).
- **Filter:** `status` (open/closed), `read_status` (all/read/unread/unreplied), `waba_pool_id`, `search`, `unread_only`. Saat filter berubah, reset (fetch tanpa cursor).

---

### 2. Daftar WABA (Pemilih Nomor)

- **Fitur:** Dropdown/bottom sheet filter percakapan per nomor WABA.
- **API:** `GET /api/mobile/waba-list`. Response: `data.results[]` dengan `id`, `text`, `phoneNumber`, `type`; `data.clientId`.
- **Implementasi:** Panggil sekali saat masuk inbox atau saat modal filter dibuka. Jika `results.length <= 1`, pemilih bisa disembunyikan.

---

### 3. Badge Unread & Count per Tab (All / Read / Unread / Unreplied)

- **Fitur:** Badge unread di tab/ikon; tab filter dengan angka (All, Read, Unread, Unreplied).
- **API:** `GET /api/mobile/conversations/counts`. Response: `data.total`, `data.read`, `data.unread`, `data.unreplied` (dan `data.open`, `data.closed`).
- **Implementasi:** Badge pakai `data.unread`. Tab filter tampilkan angka dari `total`, `read`, `unread`, `unreplied`. Panggil setelah buka inbox dan setelah aksi (kirim, baca, tutup).

---

### 4. Detail Percakapan & List Pesan

- **Fitur:** Saat user tap satu percakapan, buka layar chat: header (nama, nomor, status, tutup/reopen) dan list pesan.
- **API:** Detail: `GET /api/mobile/conversations/{id}`. Pesan: `GET /api/mobile/conversations/{id}/messages` (query: `cursor`, `limit`, `direction`). Backend otomatis mark-as-read saat GET messages.
- **Implementasi:** Render pesan dari `data[]`: `direction`, `type`, `body`, `timestamp`, `mediaUrl`, `quotedMessage`, `status` (outgoing). Gunakan `serviceWindow` dari detail untuk disable input biasa dan tampilkan "Gunakan template" jika window expired (sesuai 422 dari send).

---

### 5. Lazy Load / Pagination Pesan (Opsional)

- **Catatan:** Saat ini backend `apiMessages` mengembalikan **semua** pesan dalam satu panggilan (tanpa pagination). Jika nanti backend menambah pagination untuk messages (misalnya `page`, `per_page`, `before_id`/cursor), di mobile:
  - Load halaman pertama (pesan terbaru atau terlama sesuai sort_order).
  - Saat user scroll ke atas (load older), panggil dengan page/cursor berikutnya dan append ke atas list.
  - Jangan duplicate pesan (gunakan `id` atau `wa_message_id`).

---

### 6. Cari dalam Chat (Search in Conversation)

- **Fitur:** Ikon "Cari" di detail percakapan; hasil list (inline atau layar terpisah).
- **API:** `GET /api/mobile/conversations/{id}/messages/search?q={keyword}&page=1&per_page=50`.
- **Implementasi:** Debounce ~300 ms, tampilkan `data[]` (id, direction, type, body, timestamp). Tap hasil → scroll ke pesan di list utama (pakai id/wa_message_id).

---

### 7. Kirim Pesan (Text, Media, Template, Interactive)

- **Fitur:** Kirim teks, gambar, dokumen, video, audio, template, interactive.
- **API:** `POST /api/mobile/conversations/{id}/send`. Body: `type`, `message` (text), `context_message_id` (reply), `client_message_id` (realtime); atau `media_id`/`media_url`, `caption`, `filename` (document); atau `template_name`, `template_language`, `components`; atau `interactive`.
- **Implementasi:** Media: upload via `POST /api/mobile/media/upload`, lalu kirim dengan `media_id`/`media_url`. Tangani 422: `service_window_expired` → "Jendela 24 jam habis, gunakan template"; `content_blocked` → pesan + violation_count/violation_limit dari backend.

---

### 8. Hapus Pesan

- **Fitur:** Long-press → "Hapus" → konfirmasi.
- **API:** `DELETE /api/mobile/conversations/{id}/messages/{messageId}`. messageId: numeric = incoming, `out_123` = outgoing.
- **Implementasi:** Setelah sukses, update UI (sembunyikan isi atau label "Pesan dihapus"). Response `revoked_via_wa` untuk feedback "dihapus untuk semua".

---

### 9. Edit Pesan (Outgoing Text)

- **Fitur:** Long-press pesan keluar teks → "Edit" → kirim koreksi.
- **API:** `PUT /api/mobile/conversations/{id}/messages/{messageId}`. messageId = `out_123`. Body: `{ "message": "..." }`.
- **Implementasi:** Opsi "Edit" hanya untuk outgoing text, belum dihapus. Setelah sukses: tandai pesan lama edited, tambah bubble dari `new_message`.

---

### 10. Retry / Refetch Media (Pesan Masuk)

- **Fitur:** Pesan masuk dengan media gagal load → tombol "Coba lagi" / "Download ulang".
- **API:** `POST /api/mobile/conversations/{id}/messages/{messageId}/retry-media` (antrian) atau `.../refetch-media` (langsung). messageId harus **numeric** (bukan out_xxx).
- **Implementasi:** Prefer `refetch-media` untuk feedback langsung; fallback `retry-media` + toast "Akan diproses di belakang". Setelah sukses refetch, update item dengan `data.mediaUrl`, `data.mediaStatus` dari response.

---

### 11. Simpan Catatan (Notes)

- **Fitur:** Panel/drawer "Catatan" di detail percakapan.
- **API:** `PUT /api/mobile/conversations/{id}/notes`. Body: `{ "notes": "..." }` (max 2000).
- **Implementasi:** Load notes dari detail conversation. Simpan → update state lokal.

---

### 12. Simpan / Update Kontak

- **Fitur:** Form edit kontak (nama, email, tags) untuk nomor customer.
- **API:** `PUT /api/mobile/conversations/{id}/contact`. Body: `name` (required), `email`, `tags[]`.
- **Implementasi:** Validasi nama wajib, format email. Setelah sukses, update header/nama di layar chat.

---

### 13. Tutup & Buka Kembali Chat

- **Fitur:** Tombol "Tutup percakapan" / "Buka kembali".
- **API:** `POST /api/mobile/conversations/{id}/close`, `POST /api/mobile/conversations/{id}/reopen`.
- **Implementasi:** Tampilkan tombol sesuai `status` (open/closed). Setelah sukses, update status di state dan di list.

---

### 14. Realtime (Pusher / WebSocket) – Opsional

- **Konteks:** Backend mungkin memakai channel private untuk event `incoming.message` dan `outgoing.message`. Jika mobile memakai library yang sama (Pusher/WebSocket):
  - Subscribe ke channel client (sesuai dokumentasi backend).
  - Pada `incoming.message`: append pesan baru ke list jika assignment_id cocok.
  - Pada `outgoing.message`: update atau append pesan keluar (match by `client_message_id` atau wa_message_id) agar UI tidak hanya mengandalkan polling.

---

### 15. Tipe Pesan & Tampilan

- **Backend** mengembalikan tipe: text, image, video, audio, document, sticker, location, contacts, interactive, button, template. Untuk preview di list percakapan dipakai label seperti: 📷 Photo, 🎥 Video, 🎵 Audio, 📄 Document, dll. (lihat `mediaLabels` di backend).
- **Implementasi:** Di list percakapan, jika `last_message.body` kosong tapi `last_message.type` ada, tampilkan label untuk tipe tersebut. Di detail chat, render bubble sesuai type (gambar, pemutar audio/video, link dokumen, template/interactive sesuai spec WhatsApp).

---

## Checklist Ringkas Mobile

- [ ] List conversations: lazy load dengan `cursor` + `meta.nextCursor`, `meta.hasMore`; pull-to-refresh; filter (status, read_status, search, waba_pool_id).
- [ ] Pemilih WABA dari `GET /api/mobile/waba-list`.
- [ ] Badge unread & count tab dari `GET /api/mobile/conversations/counts` (total, read, unread, unreplied).
- [ ] Detail percakapan + list pesan; service_window; mark read otomatis saat GET messages.
- [ ] Render semua tipe pesan (text, media, template, interactive, quoted).
- [ ] Search in conversation dengan `GET .../messages/search?q=`.
- [ ] Kirim pesan (send, send-template); tangani 422 (service_window_expired, content_blocked).
- [ ] Hapus pesan; edit pesan outgoing text.
- [ ] Retry-media dan refetch-media untuk pesan masuk (messageId numeric).
- [ ] Simpan notes (PUT notes) dan save contact (PUT contact).
- [ ] Tutup/reopen chat.
- [ ] (Opsional) Realtime via Pusher/broadcasting.
- [ ] Error handling: 401, 403, 404, 422, 400, 500.

---

## Prompt untuk Penyesuaian Mobile Apps (Copy-Paste)

Gunakan blok di bawah ini sebagai **prompt** saat menyesuaikan atau mengembangkan aplikasi mobile agar selaras dengan backend MeeChat.

```
Konteks: Saya mengembangkan/menyesuaikan aplikasi mobile (React Native / Flutter / native) untuk fitur Inbox MeeChat.

Backend: Base URL API mobile = {BASE_URL}/api/mobile. Autentikasi: Bearer token (Sanctum). Header: Authorization: Bearer {token}.

Referensi API lengkap: Lihat dokumen MOBILE_API_INBOX.md di repo (docs/MOBILE_API_INBOX.md).

Yang harus diimplementasikan / disesuaikan:

1. List conversations: GET /api/mobile/conversations. Gunakan pagination lazy load (infinite scroll): request pertama tanpa cursor, limit=20. Response meta.hasMore dan meta.nextCursor. Untuk halaman berikutnya kirim query cursor=meta.nextCursor dan append hasil ke list. Pull-to-refresh = fetch tanpa cursor. Filter: status (open/closed), read_status (all/read/unread/unreplied), waba_pool_id, search, unread_only.

2. WABA list: GET /api/mobile/waba-list. Response data.results[] (id, text, phoneNumber, type). Gunakan untuk dropdown filter nomor WA.

3. Counts: GET /api/mobile/conversations/counts. Response data.total, data.read, data.unread, data.unreplied untuk badge dan tab filter (All/Read/Unread/Unreplied). Juga data.open, data.closed.

4. Detail & pesan: GET /api/mobile/conversations/{id} dan GET /api/mobile/conversations/{id}/messages (cursor, limit, direction). GET messages otomatis mark-as-read. Tampilkan serviceWindow; jika expired, nonaktifkan input biasa dan arahkan ke template.

5. Cari dalam chat: GET /api/mobile/conversations/{id}/messages/search?q=...

6. Kirim pesan: POST /api/mobile/conversations/{id}/send. Body sesuai type (text, image, document, video, audio, template, interactive). Upload media via POST /api/mobile/media/upload dulu. Tangani 422: service_window_expired (tampilkan "Gunakan template"), content_blocked (tampilkan pesan + violation_count/limit).

7. Hapus pesan: DELETE /api/mobile/conversations/{id}/messages/{messageId}. messageId numeric = incoming, "out_123" = outgoing.

8. Edit pesan: PUT /api/mobile/conversations/{id}/messages/{messageId}. Hanya outgoing text (out_xxx). Body: { "message": "..." }.

9. Retry/Refetch media (pesan masuk): POST .../messages/{messageId}/retry-media (antrian) atau .../refetch-media (langsung). messageId harus numeric. Setelah refetch sukses, update UI dengan data.mediaUrl, data.mediaStatus.

10. Notes: PUT /api/mobile/conversations/{id}/notes. Body: { "notes": "..." }. Contact: PUT .../contact. Body: { "name", "email", "tags" }.

11. Tutup/Buka: POST .../close, POST .../reopen.

12. Error handling: 401 (token), 403 (akses), 404 (not found), 422 (service_window_expired, content_blocked), 400/500 dengan pesan dari backend.

Sesuaikan UI dan state management dengan response camelCase dari API (customerName, lastMessageAt, unreadCount, hasMore, nextCursor, dll).
```

---

*Dibuat dari analisis `App\Http\Controllers\Client\InboxController.php` dan Mobile API. Dokumentasi lengkap: `docs/MOBILE_API_INBOX.md`.*
