# 📋 PRD — Product Requirements Document

**Project:** Web Antrean — Sistem Manajemen Antrean Rawat Jalan RSSA
**Version:** 1.0.0
**Status:** `In Review`
**Author:** Akbar
**Stack:** JavaScript · Vue 3 · TypeScript · Nuxt 3 · Vuetify 3 · Pinia
**Last Updated:** 2026-05-25

---

## 🧠 AI Prompt — Cara Menggunakan Dokumen Ini

> Salin prompt berikut ke Claude / ChatGPT untuk membantu melanjutkan PRD:

```
Kamu adalah product manager senior. Bantu aku mengembangkan PRD untuk project berikut:
- Nama project: Web Antrean — Sistem Manajemen Antrean Rawat Jalan RSSA
- Tujuan utama: Digitalisasi alur antrean pasien rawat jalan di rumah sakit
- Target pengguna: Pasien, Admin Loket, Admin Klinik, Admin Penunjang, Superadmin
- Tech stack: Nuxt 3, Vue 3, TypeScript, Vuetify 3, Pinia, WebSocket, Keycloak SSO

Buatkan:
1. Problem statement yang tajam
2. User stories tambahan dengan acceptance criteria
3. Risiko teknis dan mitigasinya

Format output dalam Markdown.
```

---

## 1. Overview

### 1.1 Executive Summary
Web Antrean adalah aplikasi manajemen antrean rawat jalan berbasis web yang dibangun untuk Rumah Sakit RSSA. Sistem ini mendigitalisasi seluruh alur antrean pasien mulai dari pendaftaran via anjungan mandiri, pemanggilan pasien di loket dan klinik, hingga monitoring real-time oleh administrator. Aplikasi ini kritis karena menggantikan sistem antrean manual yang tidak efisien dan rawan error.

### 1.2 Problem Statement
Rumah Sakit RSSA menghadapi masalah antrean rawat jalan yang tidak terstruktur: pasien tidak mengetahui posisi antrean mereka, petugas loket kesulitan mengelola urutan panggil, dan tidak ada visibilitas real-time lintas unit (loket, klinik, penunjang). Kondisi ini menyebabkan penumpukan pasien, waktu tunggu tidak terprediksi, dan pengalaman buruk bagi pasien.

### 1.3 Goals & Success Metrics

| # | Goal | Metrik | Target |
|---|------|--------|--------|
| 1 | Mengurangi waktu tunggu pasien | Rata-rata waktu tunggu per sesi | < 30 menit |
| 2 | Digitalisasi antrean loket & klinik | % proses antrean via sistem | 100% |
| 3 | Real-time sync antar perangkat | Latensi update antrean via WebSocket | < 2 detik |
| 4 | Kemandirian pasien dalam ambil antrean | % pasien gunakan anjungan mandiri | > 80% |
| 5 | Stabilitas sistem | Uptime saat jam operasional | > 99.5% |

### 1.4 Non-Goals (Out of Scope)
- [ ] Integrasi dengan sistem pembayaran / billing
- [ ] Manajemen rekam medis (EMR)
- [ ] Aplikasi mobile native (iOS/Android)
- [ ] Antrean rawat inap (ranap) — fitur ranap admin tersedia tapi belum final
- [ ] Sistem appointment/penjadwalan janji temu online

---

## 2. Stakeholders

| Role | Nama | Tanggung Jawab |
|------|------|----------------|
| Product Owner | Tim RSSA | Prioritas fitur & validasi kebutuhan |
| Fullstack Engineer | Akbar | Arsitektur, implementasi, & deployment |
| UI/UX Designer | — | Desain antarmuka (dikerjakan oleh engineer) |
| QA Engineer | — | Pengujian via Cypress & Vitest |
| System Admin | Tim IT RSSA | Infrastruktur, server, & jaringan internal |

---

## 3. User Personas

### 🧑 Persona 1: Pasien Rawat Jalan
- **Goals:** Mendapatkan nomor antrean dengan mudah, mengetahui posisi antrean, dan dipanggil tepat waktu
- **Pain Points:** Tidak tahu urutan antrean, harus menunggu tanpa informasi, bingung harus ke mana
- **Tech Level:** Rendah — menggunakan anjungan layar sentuh di rumah sakit

### 🧑 Persona 2: Petugas Loket
- **Goals:** Memanggil pasien secara urut, memproses lebih dari satu loket secara paralel, melihat daftar antrean real-time
- **Pain Points:** Antrean manual rawan konflik antar loket, sulit pantau siapa yang sudah dipanggil
- **Tech Level:** Menengah — menggunakan PC/tablet di meja loket

### 🧑 Persona 3: Admin Klinik / Dokter
- **Goals:** Melihat daftar pasien yang akan dilayani di kliniknya, memanggil pasien sesuai urutan, mencatat status kunjungan
- **Pain Points:** Tidak tahu berapa pasien tersisa, data pasien tidak sinkron antar perangkat
- **Tech Level:** Menengah

### 🧑 Persona 4: Superadmin / Admin IT
- **Goals:** Mengelola master data (klinik, loket, penunjang, screen), mengatur hak akses user, memonitor seluruh antrean
- **Pain Points:** Tidak ada dashboard terpusat, perubahan konfigurasi butuh restart sistem
- **Tech Level:** Tinggi

---

## 4. User Stories

| ID | Epic | Story | Priority | Acceptance Criteria | Status |
|----|------|-------|----------|---------------------|--------|
| US-001 | Anjungan | Sebagai pasien, saya ingin mengambil nomor antrean via anjungan mandiri agar tidak perlu antri ke loket | 🔴 High | - [ ] Pasien dapat memilih klinik tujuan<br>- [ ] Sistem generate nomor antrean unik<br>- [ ] Tiket dicetak atau ditampilkan di layar | `Done` |
| US-002 | Anjungan | Sebagai pasien eksekutif, saya ingin memilih sub-spesialis di anjungan agar antrean sesuai dokter yang dituju | 🔴 High | - [ ] Daftar subspesialis tampil berdasarkan klinik<br>- [ ] Pilihan tersimpan ke nomor antrean | `Done` |
| US-003 | Check-in | Sebagai pasien, saya ingin check-in via scan QR agar kedatangan saya tercatat di sistem | 🔴 High | - [ ] QR scanner aktif via kamera<br>- [ ] Status pasien berubah menjadi "hadir"<br>- [ ] Notifikasi berhasil muncul | `Done` |
| US-004 | Loket | Sebagai petugas loket, saya ingin memanggil pasien berikutnya agar antrean berjalan terurut | 🔴 High | - [ ] Tombol "Panggil Berikutnya" tersedia<br>- [ ] Nomor antrean tampil di display screen<br>- [ ] Sync real-time via WebSocket | `Done` |
| US-005 | Klinik | Sebagai admin klinik, saya ingin melihat daftar pasien yang akan dilayani agar saya bisa mempersiapkan pelayanan | 🔴 High | - [ ] Daftar pasien tampil dengan status terkini<br>- [ ] Update otomatis tanpa refresh manual | `Done` |
| US-006 | Monitoring | Sebagai superadmin, saya ingin melihat dashboard antrean seluruh unit agar dapat memantau kondisi operasional | 🟡 Medium | - [ ] Statistik antrean per unit tersedia<br>- [ ] Data diperbarui real-time | `Done` |
| US-007 | Setting | Sebagai superadmin, saya ingin mengatur hak akses per user/group agar keamanan data terjaga | 🟡 Medium | - [ ] CRUD hak akses per role & group Keycloak<br>- [ ] Perubahan langsung efektif tanpa restart | `Done` |
| US-008 | Penunjang | Sebagai admin penunjang, saya ingin mengelola antrean unit penunjang (lab, radiologi) agar terpisah dari antrean klinik | 🟡 Medium | - [ ] Antrean penunjang terpisah per unit<br>- [ ] Admin hanya melihat unit penunjangnya | `In Progress` |

---

## 5. Functional Requirements

### 5.1 Modul: Anjungan Mandiri
- **FR-01:** Pasien dapat memilih klinik tujuan dari daftar klinik aktif
- **FR-02:** Sistem mengenerate nomor antrean berformat `[KodeKlinik]-[Nomor]` (contoh: `PDL-001`)
- **FR-03:** Anjungan mendukung pilihan subspesialis untuk klinik eksekutif
- **FR-04:** Tiket antrean dapat dicetak via thermal printer
- **FR-05:** Anjungan dapat dikonfigurasi per tipe (klinik, penunjang, klinik ruang)

### 5.2 Modul: Check-in Pasien
- **FR-06:** Pasien dapat check-in mandiri via scan QR code
- **FR-07:** Sistem memvalidasi QR dan mengupdate status kunjungan ke "hadir"
- **FR-08:** Riwayat check-in pasien dapat dilihat oleh petugas

### 5.3 Modul: Loket
- **FR-09:** Petugas loket dapat memanggil pasien berikutnya sesuai urutan antrean
- **FR-10:** Setiap loket memiliki state antrean yang terisolasi (tidak interferensi antar loket)
- **FR-11:** Petugas dapat skip, recall, atau selesaikan pasien
- **FR-12:** Display nomor panggil sinkron via WebSocket ke layar display
- **FR-12b:** Pemanggilan antrean dilengkapi dengan notifikasi suara (voice over) otomatis

### 5.4 Modul: Klinik / Dokter
- **FR-13:** Admin klinik melihat daftar pasien berdasarkan klinik yang diampu
- **FR-14:** Status pasien (menunggu, dipanggil, selesai) dapat diupdate
- **FR-15:** Pemanggilan pasien di klinik sync ke display screen ruangan

### 5.5 Modul: Dashboard & Monitoring
- **FR-16:** Dashboard menampilkan statistik antrean real-time (total, menunggu, selesai)
- **FR-17:** Superadmin dapat melihat data seluruh unit sekaligus
- **FR-18:** Data diperbarui via WebSocket + polling fallback 30 detik

### 5.6 Modul: Setting & Master Data
- **FR-19:** CRUD Master Klinik, Loket, Penunjang, Klinik Ruang, Screen
- **FR-20:** Manajemen hak akses berbasis Keycloak role & group
- **FR-21:** Konfigurasi screen display untuk setiap loket/klinik
- **FR-22:** Manajemen user login dan sesi

### 5.7 Modul: Penunjang
- **FR-23:** Antrean unit penunjang (lab, radiologi, dll) dikelola terpisah
- **FR-24:** Admin penunjang hanya mengakses unit yang menjadi tanggung jawabnya

---

## 6. Non-Functional Requirements

| Kategori | Requirement | Target |
|----------|-------------|--------|
| Performance | WebSocket latency update antrean | < 2 detik |
| Performance | Waktu load halaman utama | < 3 detik |
| Performance | Polling fallback interval | 30 detik |
| Security | Auth method | Keycloak SSO (OAuth 2.0 / OIDC) |
| Security | Session duration | 1 jam (configurable) |
| Security | Hak akses berbasis role & group | Role-Based + Group-Based |
| Availability | Uptime jam operasional (06.00–21.00) | > 99.5% |
| Accessibility | Anjungan — touch target size | > 44px (mudah dioperasikan pasien) |
| Scalability | Jumlah koneksi WebSocket simultan | > 50 device sekaligus |
| Compatibility | Browser support | Chrome, Edge (terbaru) |
| Network | Operasi di jaringan LAN internal RS | ✅ Fully LAN-based |

---

## 7. Technical Specifications

### 7.1 Tech Stack

| Layer | Teknologi |
|-------|-----------|
| Frontend Framework | Nuxt 3 (SSR — server-side rendering) |
| UI Framework | Vue 3 · Vuetify 3 |
| Language | TypeScript · JavaScript |
| State Management | Pinia + pinia-plugin-persistedstate |
| Styling | SCSS (Vuetify override) + Material Design Icons |
| Real-time | WebSocket (native browser API via `useWebSocket.ts`) |
| Auth | Keycloak SSO (OAuth 2.0 / OIDC) |
| Charts | Chart.js · vue-chartjs · nuxt-charts |
| QR | html5-qrcode (scanner) · qrcode.vue (generator) |
| Print | Thermal printer via `useThermalPrint.ts` |
| Date | Day.js |
| Icons | FontAwesome · Material Design Icons |
| Testing | Vitest · Cypress |
| Fonts | Inter (Google Fonts) |

### 7.2 Backend APIs (Eksternal)

| Service | Base URL | Keterangan |
|---------|----------|------------|
| Visit API | `http://10.10.123.135:8084/api/v1` | Data kunjungan & antrean utama |
| Antrian API (Klinik) | `http://10.10.123.140:8089/api/v1` | Verifikasi & data klinik/dokter |
| WebSocket | `ws://10.10.123.135:8084/api/v1/ws` | Real-time queue update |

### 7.3 Arsitektur Sistem

```
┌─────────────────────────────────────────────┐
│         Nuxt 3 Frontend (SSR/CSR)            │
│  Pages · Components · Stores (Pinia)         │
│  Composables: useWebSocket · useQueue ·      │
│  useCheckIn · useThermalPrint · useQRScanner │
└──────────┬──────────────────┬────────────────┘
           │ $fetch / useFetch │ WebSocket
┌──────────▼──────────┐  ┌────▼───────────────┐
│  Nuxt Server (Nitro) │  │   WebSocket Server  │
│  server/api/         │  │   ws://10.10.123.   │
│  server/routes/      │  │   135:8084/api/v1/ws│
│  (Proxy + API layer) │  └────────────────────┘
└──────────┬──────────┘
           │ HTTP Proxy (CORS bypass)
┌──────────▼──────────────────────────────────┐
│           Backend Services (Eksternal)        │
│  Visit API (8084) · Antrian API (8089)        │
│  Keycloak SSO (auth.rssa.top)                 │
└─────────────────────────────────────────────┘
```

### 7.4 Proxy Routes (Nuxt Server)

| Proxy Path | Target Backend | Keterangan |
|------------|---------------|------------|
| `/visit-api/**` | `http://10.10.123.135:8084/api/v1/**` | Data kunjungan pasien |
| `/klinik-api/**` | `http://10.10.123.140:8089/api/v1/**` | Data klinik & dokter |
| `/stats-api/**` | `http://10.10.123.135:8084/api/v1/**` | Statistik dashboard |

### 7.5 Struktur Folder

```
web-antrean/
├── assets/scss/         ← Global styles & variables
├── components/          ← Reusable UI components
├── composables/         ← useWebSocket, useQueue, useCheckIn,
│                           useThermalPrint, useQRScanner, useAPI
├── layouts/             ← Layout default & admin
├── middleware/          ← Auth & permission guards
├── pages/               ← Semua halaman (lihat seksi 8.1)
├── server/
│   ├── api/             ← Internal API (auth, hak-akses, queue, users)
│   └── routes/          ← Proxy routes (visit-api, klinik-api, stats-api)
├── stores/              ← Pinia stores (queue, clinic, loket, dll)
├── types/               ← TypeScript interfaces & types
├── public/              ← Static assets (favicon, logo)
├── nuxt.config.ts       ← Konfigurasi utama Nuxt
└── .env                 ← Environment variables (tidak di-commit)
```

### 7.6 Internal API Endpoints (Nuxt Nitro)

| Method | Endpoint | Deskripsi | Auth |
|--------|----------|-----------|------|
| GET | `/api/permission` | Ambil permissions berdasarkan role & group | ✅ |
| GET | `/api/hak-akses` | Daftar semua hak akses | ✅ |
| POST | `/api/hak-akses` | Buat hak akses baru | ✅ |
| PATCH | `/api/hak-akses/:id` | Update hak akses | ✅ |
| DELETE | `/api/hak-akses/:id` | Hapus hak akses | ✅ |
| GET | `/api/users/list` | Daftar user dari Keycloak | ✅ |
| POST | `/api/auth/login` | Login via Keycloak SSO | ❌ |
| POST | `/api/auth/logout` | Logout & invalidate session | ✅ |
| POST | `/api/external/validate-token` | Validasi JWT token eksternal | ✅ |
| GET | `/api/config/...` | Konfigurasi aplikasi | ✅ |

### 7.7 TypeScript Types Utama

```typescript
// types/user.ts
export interface User {
  id: string
  namaLengkap: string
  namaUser: string
  email: string
  tipeUser: string
  roles: string[]
  groups: string[]
  lastLogin: number
  createdAt: number
}

// types/queue.ts
export interface QueuePatient {
  nomorAntrean: string
  namaPassien: string
  noRM: string
  klinik: string
  status: 'menunggu' | 'dipanggil' | 'selesai' | 'skip'
  loketId?: string
  subspesialis?: string
  createdAt: Date
}

// types/permission.ts
export interface Permission {
  id: number
  pagename: string
  read: boolean
  create: boolean
  update: boolean
  delete: boolean
  disable: boolean
  active: boolean
  level: number
  parent: number | null
}

// types/api.ts
export interface ApiResponse<T> {
  data: T
  message: string
  success: boolean
}
```

---

## 8. UI/UX

### 8.1 Halaman & Routes

| Halaman | Route | Auth | Role |
|---------|-------|------|------|
| Landing / Home | `/` | ❌ | Public |
| Login | `/login-page` | ❌ | Public |
| Dashboard | `/dashboard` | ✅ | Superadmin |
| Admin Klinik | `/admin-klinik` | ✅ | Admin Klinik |
| Admin Loket | `/admin-loket` | ✅ | Admin Loket |
| Admin Penunjang | `/admin-penunjang` | ✅ | Admin Penunjang |
| Buat Antrean | `/buat-antrean` | ✅ | Loket |
| Klinik Ruang Admin | `/klinik-ruang-admin` | ✅ | Admin Klinik Ruang |
| Ranap Admin | `/ranap-admin` | ✅ | Admin Ranap |
| Anjungan Utama | `/anjungan` | ❌ | Kiosk |
| Admin Anjungan | `/anjungan/admin-anjungan` | ✅ | Superadmin |
| Antrian Klinik | `/anjungan/antrian-klinik` | ❌ | Kiosk |
| Antrian Klinik Ruang | `/anjungan/antrian-klinik-ruang` | ❌ | Kiosk |
| Antrian Penunjang | `/anjungan/antrian-penunjang` | ❌ | Kiosk |
| Antrean Masuk Screen | `/anjungan/antrean-masuk` | ❌ | Display |
| Check In Pasien | `/check-in-pasien/check-in` | ❌ | Kiosk |
| Data Pasien | `/data-pasien` | ✅ | Admin |
| Edit Data Pasien | `/data-pasien/edit/:id` | ✅ | Admin |
| Monitoring Pasien | `/monitoring-pasien/monitoring-pasien` | ✅ | Admin |
| Detail Pasien | `/monitoring-pasien/pasien/:id` | ✅ | Admin |
| Profil | `/profile/profil` | ✅ | Semua |
| User Login | `/setting/user-login` | ✅ | Superadmin |
| Hak Akses | `/setting/hak-akses` | ✅ | Superadmin |
| Master Klinik | `/setting/master-klinik` | ✅ | Superadmin |
| Master Klinik Ruang | `/setting/master-klinik-ruang` | ✅ | Superadmin |
| Master Loket | `/setting/master-loket` | ✅ | Superadmin |
| Master Penunjang | `/setting/master-penunjang` | ✅ | Superadmin |
| Screen Settings | `/setting/screen` | ✅ | Superadmin |
| Verifikasi Akun | `/verifikasi-akun/verifikasi-akun` | ❌ | Public |
| Detail Akun Verifikasi | `/verifikasi-akun/detail-akun` | ✅ | Admin |

### 8.2 Breakpoints

| Nama | Size | Keterangan |
|------|------|------------|
| Mobile | < 640px | Tidak diprioritaskan (akses via PC/tablet) |
| Tablet | 640–1024px | Anjungan & loket (tablet) |
| Desktop | > 1024px | Admin & monitoring |

### 8.3 Design System
- **Framework UI:** Vuetify 3 (Material Design 3)
- **Font:** Inter (400, 500, 600, 700)
- **Icons:** Material Design Icons (`@mdi/font`) + FontAwesome
- **Color scheme:** Mengikuti theme Vuetify (light/dark configurable)
- **SCSS Variables:** Didefinisikan di `assets/scss/_variables.scss` & `_colors.scss`

---

## 9. Risks

| Risk | Likelihood | Impact | Mitigasi |
|------|-----------|--------|----------|
| WebSocket disconnect saat jaringan LAN tidak stabil | Tinggi | Tinggi | Polling fallback 30 detik + auto-reconnect logic |
| Backend API eksternal down (Visit API / Antrian API) | Sedang | Tinggi | Error handling graceful, retry logic, blacklist endpoint gagal |
| Konflik antrean antar loket (race condition) | Sedang | Tinggi | State isolasi per `loketId`, strict filtering di `processNextQueue` |
| Sesi Keycloak expire saat jam operasional | Sedang | Sedang | Session duration dikonfigurasi 1 jam, refresh token otomatis |
| Thermal printer tidak kompatibel di semua device | Rendah | Sedang | Uji di device target sebelum go-live, fallback ke tampilan layar |
| Data antrean tidak sinkron antar display screen | Sedang | Tinggi | WebSocket deterministic client ID + polling fallback |
| Kapasitas WebSocket server saat pasien peak | Rendah | Tinggi | Monitor jumlah koneksi, koordinasi dengan tim backend |

---

## 10. Deployment

| Item | Detail |
|------|--------|
| Server | VPS / Server internal RSSA |
| Domain dev | `http://10.10.150.175:3000` |
| Domain staging | `https://antrean.dev.rssa.id` |
| Domain prod | `https://antrean.rssa.id` |
| Containerisasi | Docker + docker-compose |
| Auth Server | Keycloak (`https://auth.rssa.top/realms/sandbox`) |
| Build command | `nuxt build` |
| Start command | `node .output/server/index.mjs` |

---

## 11. Changelog

| Versi | Tanggal | Author | Perubahan |
|-------|---------|--------|-----------|
| 1.0.0 | 2026-05-25 | Akbar | Initial PRD — dibuat berdasarkan kondisi project aktual |
