# 🚨 EVLOG — Event & Error Log

**Project:** Web Antrean — Sistem Manajemen Antrean Rawat Jalan RSSA
**Author:** Akbar
**Stack:** JavaScript · Vue 3 · TypeScript · Nuxt 3 · Vuetify 3 · Pinia

---

## 🧠 AI Prompt — Cara Menggunakan Dokumen Ini

> Salin prompt berikut ke Claude untuk menganalisis dan mendokumentasikan error:

```
Kamu adalah senior engineer Nuxt 3 + TypeScript. Aku menemukan error berikut:

Error message: [paste error]
Stack trace: [paste stack trace]
Context: [dimana terjadi, langkah reproduksi]
Tech: Nuxt 3, Vue 3, TypeScript, Vuetify 3, Pinia, WebSocket, Keycloak

Bantu aku:
1. Analisis root cause error ini
2. Berikan solusi step-by-step
3. Sarankan cara mencegah error serupa
4. Format hasilnya untuk EVLOG dalam Markdown
```

---

## Severity Legend

| Level | Icon | Deskripsi | SLA |
|-------|------|-----------|-----|
| Critical | 🔴 | App down / data loss / security | < 4 jam |
| High | 🟠 | Fitur utama break | < 1 hari |
| Medium | 🟡 | Fitur minor terganggu | < 3 hari |
| Low | 🟢 | UI/kosmetik | Backlog |

---

## Format Entry

```markdown
### [EV-XXX] — [Judul Singkat Error/Event]

| Field | Value |
|-------|-------|
| **ID** | EV-XXX |
| **Tanggal** | YYYY-MM-DD |
| **Severity** | 🔴 Critical / 🟠 High / 🟡 Medium / 🟢 Low |
| **Environment** | Development / Staging / Production |
| **Status** | 🔍 Investigating / 🔧 In Fix / ✅ Resolved / ⏭ Wontfix |
| **Reporter** | Akbar |

**Error Message:**
> [paste error message]

**Langkah Reproduksi:**
1. ...

**Root Cause:**
> ...

**Solusi / Fix:**
> ...

**Prevention:**
> ...

**Related:** [commit / file]
```

---

## Event Log

<!-- Tambahkan entry baru di bawah baris ini, urutan terbaru di atas -->

---

### [EV-012] — Info Klinik Ruang Tidak Muncul di Layar Display

| Field | Value |
|-------|-------|
| **ID** | EV-012 |
| **Tanggal** | 2026-05-21 |
| **Severity** | 🟠 High |
| **Environment** | Development |
| **Status** | ✅ Resolved |
| **Reporter** | Akbar |

**Error Message:**
> Layar informasi klinik ruang menampilkan data kosong — tidak ada info klinik ruang yang muncul.

**Langkah Reproduksi:**
1. Buka halaman display screen klinik ruang
2. Data klinik ruang seharusnya muncul
3. Layar kosong — tidak ada informasi tampil

**Root Cause:**
> Data binding untuk info klinik ruang tidak ter-update setelah fetch dari API. Kemungkinan reactive state tidak di-watch dengan benar sehingga UI tidak re-render saat data berubah.

**Solusi / Fix:**
> Perbaikan data binding dan reactive state untuk informasi klinik ruang pada layar display.

**Related:** Commit `ec9dac0` — "push perbaikan layar informasi info klinik ruang tidak muncul"

---

### [EV-011] — Data Antrean Tidak Sinkron Antar Display Screen

| Field | Value |
|-------|-------|
| **ID** | EV-011 |
| **Tanggal** | 2026-05-22 |
| **Severity** | 🔴 Critical |
| **Environment** | Development |
| **Status** | ✅ Resolved |
| **Reporter** | Akbar |

**Error Message:**
> Display screen di lokasi berbeda menampilkan nomor panggil yang berbeda. Admin memanggil pasien tapi layar display tidak update, atau update terlambat > 10 detik.

**Langkah Reproduksi:**
1. Buka admin loket di PC A
2. Buka display screen di TV/monitor B
3. Panggil pasien berikutnya di PC A
4. Display di monitor B tidak menampilkan nomor panggil terbaru

**Root Cause:**
> WebSocket client ID menggunakan random ID, sehingga server tidak bisa menargetkan pesan ke device tertentu secara reliable. Saat koneksi putus lalu reconnect, client ID berubah dan device kehilangan subscription.

**Solusi / Fix:**
```typescript
// ❌ Before — random client ID
const clientId = `client_${Math.random().toString(36).substr(2, 9)}`

// ✅ After — deterministic client ID berdasarkan page + device
const clientId = `${pageType}_${loketId || 'global'}_${deviceFingerprint}`
```
Ditambahkan juga polling fallback setiap 30 detik sebagai safety net.

**Prevention:**
> Selalu gunakan deterministic identifier untuk WebSocket client. Implementasi polling fallback untuk semua halaman yang bergantung pada real-time data.

**Related:** Conversation `6fe234cf` — "Stabilizing WebSocket Queue Synchronization"

---

### [EV-010] — Cross-Loket Interference pada Patient Processing

| Field | Value |
|-------|-------|
| **ID** | EV-010 |
| **Tanggal** | 2026-05-21 |
| **Severity** | 🔴 Critical |
| **Environment** | Development |
| **Status** | ✅ Resolved |
| **Reporter** | Akbar |

**Error Message:**
> Loket A memproses pasien yang seharusnya milik Loket B. `currentProcessingPatient` menampilkan pasien dari loket yang salah.

**Langkah Reproduksi:**
1. Login sebagai admin loket A di browser tab 1
2. Login sebagai admin loket B di browser tab 2
3. Panggil pasien di loket A
4. Loket B menampilkan pasien yang sama sebagai "sedang diproses"

**Root Cause:**
> `currentProcessingPatient` menggunakan key persisted state yang sama untuk semua loket. Karena `pinia-plugin-persistedstate` menyimpan ke `localStorage` dengan key yang sama, operasi di satu loket menimpa state loket lain.

**Solusi / Fix:**
```javascript
// ❌ Before — shared key
persist: { key: 'currentProcessingPatient' }

// ✅ After — unique key per loket
persist: { key: `currentPatient_loket_${loketId}` }
```
Ditambahkan juga strict filter di `processNextQueue` dan `callNext` berdasarkan `loketId`, serta guard check di WebSocket event handler.

**Prevention:**
> Setiap state yang bersifat per-instance (per loket, per klinik) HARUS menggunakan unique key. Jangan pernah share persisted state key antar instance.

**Related:** Conversation `7a37e693` — "Isolating Loket Queue Operations"

---

### [EV-009] — Bug Tampilan Loket

| Field | Value |
|-------|-------|
| **ID** | EV-009 |
| **Tanggal** | 2026-05-20 |
| **Severity** | 🟡 Medium |
| **Environment** | Development |
| **Status** | ✅ Resolved |
| **Reporter** | Akbar |

**Error Message:**
> Layout admin loket tidak render dengan benar — elemen UI tumpang tindih atau alignment salah.

**Langkah Reproduksi:**
1. Login sebagai admin loket
2. Buka halaman `/admin-loket`
3. Layout tampilan loket tidak sesuai desain

**Root Cause:**
> CSS layout issue dan data binding yang tidak sinkron dengan state loket.

**Solusi / Fix:**
> Perbaikan layout dan data binding di halaman admin loket.

**Related:** Commit `f622052` — "perbaikan bug tampilan loket"

---

### [EV-008] — Memory Leak & Request Spam dari doctorStore

| Field | Value |
|-------|-------|
| **ID** | EV-008 |
| **Tanggal** | 2026-05-18 |
| **Severity** | 🟠 High |
| **Environment** | Development |
| **Status** | ✅ Resolved |
| **Reporter** | Akbar |

**Error Message:**
```
Internal Server Error (500) — /klinik-api/doctors/...
```
Error terjadi berulang-ulang tanpa henti, menyebabkan request spam ke backend.

**Langkah Reproduksi:**
1. Buka halaman yang memuat `doctorStore.js`
2. Backend endpoint `/doctors` return 500
3. Store terus retry tanpa batas
4. Network tab penuh dengan request gagal, memory usage naik terus

**Root Cause:**
> `doctorStore.js` tidak memiliki mekanisme backoff atau blacklist untuk endpoint yang gagal. Setiap kali fetch gagal, store langsung retry tanpa delay, menyebabkan infinite loop request ke server.

**Solusi / Fix:**
```javascript
// ✅ Implementasi blacklist endpoint gagal
const blacklistedEndpoints = new Set()

async function fetchDoctors(endpoint) {
  if (blacklistedEndpoints.has(endpoint)) {
    return [] // skip, sudah di-blacklist
  }
  try {
    return await $fetch(endpoint)
  } catch (error) {
    if (error.status === 500) {
      blacklistedEndpoints.add(endpoint)
      console.warn(`Endpoint blacklisted: ${endpoint}`)
    }
    return []
  }
}
```

**Prevention:**
> Semua API fetch HARUS memiliki: (1) error handling, (2) retry limit atau backoff, (3) blacklist mechanism untuk persistent failures. Jangan pernah retry tanpa batas.

**Related:** Conversation `c7502197` — "Optimizing Web Antrean Memory Usage", commit `cb3310b`

---

### [EV-007] — Console.log Verbose Memperlambat Performa

| Field | Value |
|-------|-------|
| **ID** | EV-007 |
| **Tanggal** | 2026-05-18 |
| **Severity** | 🟢 Low |
| **Environment** | Development |
| **Status** | ✅ Resolved |
| **Reporter** | Akbar |

**Error Message:**
> Tidak ada error message, tapi browser DevTools penuh dengan log output, menyebabkan performa menurun terutama di device yang lebih lambat.

**Langkah Reproduksi:**
1. Buka halaman yang menggunakan WebSocket atau QR scanner
2. Buka browser DevTools → Console
3. Log membanjir setiap detik dari `useWebSocket.ts` dan QR scanner di `checkIn.vue`

**Root Cause:**
> `console.log` debugging statements di high-frequency code paths (WebSocket message handler yang dipanggil per-detik, QR scanner frame processing) tidak dihapus setelah debugging selesai.

**Solusi / Fix:**
> Cleanup semua `console.log` di:
> - `composables/useWebSocket.ts` — WebSocket message handler
> - `pages/CheckInPasien/checkIn.vue` — QR scanner frame processing
> Hanya sisakan `console.warn` dan `console.error` untuk kondisi yang benar-benar perlu.

**Prevention:**
> Gunakan convention: `console.log` hanya untuk debugging sementara, `console.warn`/`console.error` untuk production logging. Tambahkan lint rule atau pre-commit hook untuk mendeteksi `console.log` yang tersisa.

**Related:** Conversation `c7502197` — "Optimizing Web Antrean Memory Usage"

---

### [EV-006] — Header Origin/Referer Tidak Konsisten di Proxy Routes

| Field | Value |
|-------|-------|
| **ID** | EV-006 |
| **Tanggal** | 2026-05-22 |
| **Severity** | 🟡 Medium |
| **Environment** | Development |
| **Status** | ✅ Resolved |
| **Reporter** | Akbar |

**Error Message:**
> CORS error atau origin validation failure saat memanggil backend API melalui proxy routes. Beberapa request berhasil, beberapa gagal secara intermittent.

**Langkah Reproduksi:**
1. Panggil API via proxy route `stats-api`, `visit-api`, atau `klinik-api`
2. Beberapa request gagal dengan CORS error
3. Request yang sama kadang berhasil, kadang gagal

**Root Cause:**
> Setiap proxy route handler (`server/routes/stats-api/`, `visit-api/`, `klinik-api/`) menggunakan header origin dan referer yang berbeda-beda. Backend melakukan validasi origin, dan header yang tidak konsisten menyebabkan intermittent failure.

**Solusi / Fix:**
> Standardisasi semua proxy route handler agar menggunakan origin dan referer yang sama:
```typescript
// ✅ Semua proxy routes menggunakan header yang sama
headers: {
  'Origin': 'http://10.10.150.175:3000',
  'Referer': 'http://10.10.150.175:3000',
}
```

**Prevention:**
> Buat shared utility function untuk proxy header configuration agar semua route handler menggunakan config yang sama. Hindari copy-paste header config per file.

**Related:** Conversation `97867794` — "Standardizing Header Configurations Across APIs"

---

### [EV-005] — Duplikasi Tiket di Admin Klinik Loket

| Field | Value |
|-------|-------|
| **ID** | EV-005 |
| **Tanggal** | 2026-02-11 |
| **Severity** | 🟠 High |
| **Environment** | Development |
| **Status** | ✅ Resolved |
| **Reporter** | Akbar |

**Error Message:**
> Daftar pasien di admin klinik menampilkan tiket yang sama dua kali. Setelah check-in, tiket pasien menghilang dari daftar.

**Langkah Reproduksi:**
1. Pasien ambil antrean di anjungan
2. Buka admin klinik loket
3. Pasien yang sama muncul 2x di daftar
4. Setelah check-in, tiket menghilang dari daftar

**Root Cause:**
> WebSocket event handler menambahkan pasien ke `allPatients` tanpa deduplication check. Saat event masuk bersamaan dari multiple sources (initial load + WebSocket), data terduplikasi. Saat check-in, state update menghapus entry yang salah.

**Solusi / Fix:**
```javascript
// ✅ Deduplication check sebelum add ke allPatients
function addPatient(patient) {
  const exists = allPatients.value.find(p => p.nomorAntrean === patient.nomorAntrean)
  if (!exists) {
    allPatients.value.push(patient)
  } else {
    // Update existing instead of duplicate
    Object.assign(exists, patient)
  }
}
```

**Prevention:**
> Semua operasi mutasi pada `allPatients` harus melalui function yang melakukan deduplication check. Jangan pernah langsung `push` tanpa cek duplikat.

**Related:** Commit `c02905e` — "fix duplication and ticket disappear in adminklinik loket"

---

### [EV-004] — WebSocket Check-in Tidak Trigger UI Update

| Field | Value |
|-------|-------|
| **ID** | EV-004 |
| **Tanggal** | 2026-02-10 |
| **Severity** | 🟠 High |
| **Environment** | Development |
| **Status** | ✅ Resolved |
| **Reporter** | Akbar |

**Error Message:**
> Pasien check-in via QR berhasil (API return success), tapi status di admin loket tidak berubah. UI tetap menampilkan pasien sebagai "belum hadir".

**Langkah Reproduksi:**
1. Pasien scan QR di halaman check-in
2. API return success — pasien tercatat hadir
3. Buka admin loket — status pasien masih "belum hadir"
4. Perlu manual refresh untuk melihat update

**Root Cause:**
> WebSocket event listener untuk check-in event tidak ter-bind dengan benar. Event `checkin_success` diterima oleh WebSocket, tapi handler tidak melakukan state update ke Pinia store.

**Solusi / Fix:**
> Fix event listener binding di `useWebSocket.ts` — pastikan `checkin_success` event memicu update pada `queueStore.allPatients`.

**Prevention:**
> Setiap WebSocket event type harus memiliki dedicated handler yang ter-test. Buat mapping event → handler yang eksplisit.

**Related:** Commits `e686dda`, `b6dc252` — "fix socket checkin", "update bug ws checkin loket"

---

### [EV-003] — HTTPS Implementation Gagal di Check-in

| Field | Value |
|-------|-------|
| **ID** | EV-003 |
| **Tanggal** | 2026-02-02 |
| **Severity** | 🟡 Medium |
| **Environment** | Development |
| **Status** | ✅ Resolved |
| **Reporter** | Akbar |

**Error Message:**
> Check-in via QR tidak berfungsi saat HTTPS aktif. Kamera QR scanner tidak bisa diakses.

**Langkah Reproduksi:**
1. Aktifkan HTTPS di dev server
2. Buka halaman check-in
3. QR scanner gagal — kamera tidak muncul

**Root Cause:**
> Browser memerlukan HTTPS untuk akses kamera (MediaDevices API), tapi mixed content policy memblokir request ke backend HTTP. Konfigurasi HTTPS tidak lengkap — SSL certificate self-signed tidak dipercaya browser.

**Solusi / Fix:**
> Fix implementasi HTTPS menggunakan `@vitejs/plugin-basic-ssl` di `nuxt.config.ts`. Konfigurasi dev server untuk HTTPS dengan self-signed cert yang di-trust browser.

**Prevention:**
> Saat menggunakan Web API yang memerlukan secure context (kamera, geolocation), pastikan HTTPS sudah configured end-to-end termasuk backend.

**Related:** Commit `c899a71` — "fix https implementation and checkin"

---

### [EV-002] — Loop & Duplikasi Data di Fetch API

| Field | Value |
|-------|-------|
| **ID** | EV-002 |
| **Tanggal** | 2026-01-29 |
| **Severity** | 🟠 High |
| **Environment** | Development |
| **Status** | ✅ Resolved |
| **Reporter** | Akbar |

**Error Message:**
> Data pasien terduplikasi di daftar antrean. Fetch API dipanggil berulang-ulang dalam loop.

**Langkah Reproduksi:**
1. Buka halaman yang memuat data antrean
2. Data pasien muncul berkali-kali
3. Network tab menunjukkan API dipanggil berulang-ulang

**Root Cause:**
> `watch` atau `computed` yang bergantung pada reactive state memicu re-fetch setiap kali state berubah, dan hasil fetch mengubah state lagi — menyebabkan infinite loop.

**Solusi / Fix:**
> Fix logic watch/computed agar tidak circular. Tambahkan guard condition untuk mencegah re-fetch saat data sudah di-load.

**Prevention:**
> Hindari circular dependency antara watcher dan state mutation. Gunakan flag `isLoading` untuk mencegah concurrent fetch.

**Related:** Commit `0bd5311` — "fix loop dan duplicate data"

---

### [EV-001] — Session & Tampilan Screen Tidak Sinkron

| Field | Value |
|-------|-------|
| **ID** | EV-001 |
| **Tanggal** | 2026-01-30 |
| **Severity** | 🟡 Medium |
| **Environment** | Development |
| **Status** | ✅ Resolved |
| **Reporter** | Akbar |

**Error Message:**
> Display screen menampilkan data lama setelah session expire. Setelah re-login, data tidak refresh otomatis.

**Langkah Reproduksi:**
1. Login dan buka display screen
2. Tunggu session expire (1 jam)
3. Re-login
4. Display masih menampilkan data dari session sebelumnya

**Root Cause:**
> Pinia persisted state menyimpan data lama di `localStorage`. Saat session expire dan user re-login, store tidak di-reset — data lama masih tampil.

**Solusi / Fix:**
> Reset persisted state saat login baru. Pastikan display screen melakukan fresh fetch setelah session recovery.

**Prevention:**
> Implementasi session lifecycle hooks: on session expire → clear stale state, on re-login → fresh fetch semua data.

**Related:** Commit `8dd94ed` — "update fix session dan tampilan screen"

---

## 📊 Error Statistics

| Bulan | Critical | High | Medium | Low | Total |
|-------|---------|------|--------|-----|-------|
| Jan 2026 | 0 | 1 | 1 | 0 | 2 |
| Feb 2026 | 0 | 3 | 1 | 0 | 4 |
| Mar 2026 | 0 | 0 | 0 | 0 | 0 |
| Apr 2026 | 0 | 0 | 0 | 0 | 0 |
| Mei 2026 | 2 | 2 | 1 | 1 | 6 |
| **Total** | **2** | **6** | **3** | **1** | **12** |

---

## 🔁 Recurring Issues

> Daftar error yang muncul lebih dari sekali — kandidat untuk refactor/improvement permanen.

| Error Pattern | Frekuensi | Action |
|---------------|-----------|--------|
| WebSocket disconnect → data tidak sinkron | 3x (EV-004, EV-011, EV-012) | ✅ Implemented: polling fallback 30 detik + auto-reconnect + deterministic client ID |
| Duplikasi data pasien di daftar | 2x (EV-002, EV-005) | ✅ Implemented: deduplication check di `allPatients` mutation |
| Session/state stale setelah reconnect | 2x (EV-001, EV-011) | ✅ Implemented: fresh fetch on reconnect + state reset on re-login |
| Request spam ke endpoint yang gagal | 1x (EV-008) | ✅ Implemented: blacklist + retry limit. **Monitor untuk recurring** |
| Cross-instance state conflict (loket) | 1x (EV-010) | ✅ Implemented: unique persisted state key per instance. **Audit untuk klinik/penunjang** |
