# Pesan Terjadwal


## Ringkasan

**Pesan Terjadwal** memungkinkan agent menetapkan waktu pengiriman pesan di masa mendatang untuk sebuah percakapan. Pesan disimpan di PostgreSQL dengan status `pending`, lalu dikirim otomatis oleh background scheduler saat waktu yang ditentukan tiba.

Halaman `/scheduled-messages` menyediakan tiga tampilan sekaligus — **List**, **Calendar**, dan **Timeline** — untuk memantau semua pesan terjadwal lintas percakapan. Pembatalan hanya bisa dilakukan selama pesan masih berstatus `pending`.

:::note
**Hak akses:** Semua peran (admin, supervisor, agent) dapat membuat pesan terjadwal dari inbox. Halaman `/scheduled-messages` menampilkan pesan dari seluruh percakapan tanpa filter per-agent — pastikan hanya supervisor/admin yang membuka halaman ini jika privasi diperlukan.
:::

## Cara Menjadwalkan Pesan

Pesan terjadwal dibuat **dari dalam percakapan di inbox**, bukan dari halaman `/scheduled-messages` itu sendiri. Halaman ini hanya berfungsi sebagai panel pantau dan manajemen.

1. Buka percakapan di `/inbox`.
2. Di area kirim pesan, pilih opsi **Schedule** (ikon jam).
3. Isi kolom:
   - **Tipe pesan** (`type`): `text`, `template`, atau `image`.
   - **Konten** (`content`): isi pesan sesuai tipe — lihat tabel di bawah.
   - **Waktu kirim** (`scheduled_at`): harus di masa mendatang; waktu sekarang atau lampau ditolak oleh API.
4. Konfirmasi — sistem menyimpan pesan dengan status `pending`.

### Format konten per tipe

| Tipe | Field `content` yang dipakai | Contoh |
|---|---|---|
| `text` | `{ "text": "..." }` | `{"text": "Halo, ada yang bisa dibantu?"}` |
| `template` | `{ "template_id": "..." }` | `{"template_id": "promo_ramadan"}` |
| `image` | `{ "caption": "..." }` + URL media | `{"caption": "Lihat produk baru kami"}` |

### Contoh request API

```http
POST /api/conversations/{conversation_id}/schedule
Authorization: Bearer <JWT>
Content-Type: application/json

{
  "type": "text",
  "content": { "text": "Pengingat: jadwal servis besok pukul 09.00." },
  "scheduled_at": "2026-06-10T02:00:00Z"
}
```

Response sukses:

```json
{
  "message": "Message scheduled",
  "id": "uuid",
  "conversation_id": "uuid",
  "scheduled_at": "2026-06-10T02:00:00Z"
}
```

:::warning
`scheduled_at` harus berformat ISO 8601 UTC dan nilainya **harus di masa mendatang**. API mengembalikan error validasi jika waktu sama dengan atau lebih awal dari saat request diterima.
:::

## Tampilan Halaman Scheduled Messages

### Kartu Ringkasan

Di bagian atas halaman terdapat empat kartu statistik:

| Kartu | Isi |
|---|---|
| **Total** | Jumlah semua pesan yang dimuat |
| **Pending** | Pesan yang menunggu dikirim |
| **Sent** | Pesan yang sudah terkirim |
| **Failed** | Pesan yang gagal dikirim |

### Tampilan List

Tabel dengan kolom: **Status**, **Recipient** (nama kontak), **Content** (pratinjau 60 karakter), **Channel**, **Type**, **Scheduled At**, **Sent At**, **Scheduled By**, dan **Actions**.

- Kolom **Channel** menampilkan ikon dan label: WhatsApp (hijau), Instagram (ungu), Email (biru), Messenger (oranye).
- Kolom **Actions**: tombol batal (ikon larangan) untuk pesan `pending`, dan tautan ke percakapan asal (ikon external link).
- Jika pesan `failed`, pesan error dari sistem ditampilkan di bawah pratinjau konten.

### Tampilan Calendar

Kalender bulanan dengan navigasi bulan (tombol `<` / `>`) dan tombol **Today**.

- Setiap sel hari yang memiliki jadwal menampilkan jumlah pesan dan titik warna per kanal.
- Klik hari untuk membuka **panel detail** di sisi kanan yang menampilkan daftar pesan pada hari tersebut beserta waktu, nama kontak, kanal, status, dan tautan ke percakapan.
- Klik hari yang sama untuk menutup panel.

**Legenda warna kanal:**

| Kanal | Warna titik |
|---|---|
| WhatsApp | Hijau |
| Instagram | Ungu |
| Email | Biru |
| Messenger | Oranye |

### Tampilan Timeline

Daftar kronologis dikelompokkan per tanggal, diurutkan dari yang paling awal. Setiap kelompok tanggal menampilkan header dengan label (misal: "Today", "Tomorrow", atau tanggal lengkap).

- Garis vertikal menjadi tulang punggung; setiap pesan ditandai titik berwarna sesuai kanal.
- Kelompok hari ini memiliki penanda **"Now"** — garis putus merah yang menunjukkan posisi waktu saat ini.
- Setiap kartu pesan menampilkan: nama kontak, kanal, waktu, pratinjau konten, badge status dan tipe, nama agent penjadwal, tombol batal, dan tautan ke percakapan.

:::tip
Pilihan tampilan (list/calendar/timeline) disimpan di `localStorage` dengan key `scheduled-messages-view` dan otomatis dipulihkan saat halaman dibuka kembali.
:::

## Status Pesan

| Status | Arti | Bisa Dibatalkan? |
|---|---|---|
| `pending` | Menunggu waktu kirim tiba | Ya |
| `sent` | Berhasil dikirim oleh scheduler | Tidak |
| `failed` | Gagal dikirim; lihat kolom error | Tidak |
| `cancelled` | Dibatalkan secara manual | Tidak |

## Membatalkan Pesan Terjadwal

Pembatalan hanya bisa dilakukan pada pesan berstatus **`pending`**.

**Dari tampilan List atau Timeline:**

1. Temukan baris/kartu pesan dengan status `pending`.
2. Klik ikon larangan (Ban).
3. Konfirmasi dengan klik **Yes** (List) atau **Confirm** (Timeline).
4. Status berubah menjadi `cancelled` secara lokal; request dikirim ke API.

**Via API:**

```http
DELETE /api/scheduled-messages/{id}
Authorization: Bearer <JWT>
```

API hanya memperbarui baris jika `status = 'pending'`. Jika pesan sudah `sent` atau `cancelled`, API mengembalikan 404.

## Cara Kerja Scheduler

```
Setiap 30 detik
    ↓
Scheduler query: scheduled_messages WHERE status = 'pending' AND scheduled_at <= NOW()
    ↓ (maks 50 pesan per siklus)
Untuk setiap pesan → spawn tokio task → send_scheduled_message()
    ↓ berhasil           ↓ gagal
status = 'sent'     status = 'failed'
sent_at = NOW()     error_message = <pesan error>
```

Detail teknis:
- Scheduler memeriksa pesan terjadwal setiap **30 detik**.
- Setiap siklus memproses maksimal **50 pesan** per tenant.
- Scheduler bersifat **multi-tenant**: setiap siklus mengiterasi semua tenant aktif secara terpisah, tidak ada campur data antar-organisasi.

:::note
Karena scheduler poll setiap 30 detik, pesan bisa terkirim hingga 30 detik lebih lambat dari `scheduled_at` yang ditetapkan. Ini perilaku yang diharapkan, bukan bug.
:::

## Endpoint (tag **Scheduled Messages**)

| Aksi | Endpoint |
|---|---|
| Jadwalkan pesan | `POST /api/conversations/{id}/schedule` |
| List per percakapan | `GET /api/conversations/{id}/scheduled` |
| List semua (maks 200) | `GET /api/scheduled-messages` |
| Batalkan pesan | `DELETE /api/scheduled-messages/{id}` |

Skema lengkap di [API Reference](/api).

## Rute Terkait

- [Inbox](/panduan/agent/inbox) — tempat membuat pesan terjadwal dari percakapan
- [Kampanye Broadcast](/panduan/admin/kampanye-broadcast) — pengiriman massal ke banyak kontak sekaligus
- [WA Templates](/panduan/integrasi/wa-templates) — template yang bisa digunakan sebagai konten tipe `template`
