update status waiting dan update post data pasien

This commit is contained in:
bagus-arie05
2026-02-02 11:04:28 +07:00
parent 7ba1c5b7dd
commit 53a510f23d
14 changed files with 140 additions and 404 deletions

No files matched your search

-321
View File
@@ -1,321 +0,0 @@
# Mapping Data Structure ke ERD
Dokumen ini menjelaskan mapping antara struktur data pasien di `queueStore.js` (baris 607-622) ke tabel-tabel dalam ERD.
## Struktur Data di Code (queueStore.js)
```javascript
{
no: newNo,
jamPanggil: "HH:MM",
barcode: "26011395625",
noAntrian: "KL0001 | Klinik - 26011395625",
shift: "Shift 1",
klinik: "KANDUNGAN",
fastTrack: "TIDAK" | "YA",
pembayaran: "BPJS" | "UMUM",
status: "di-loket" | "waiting" | ...,
processStage: "klinik" | "loket" | ...,
createdAt: "2026-01-13T16:51:35.502Z",
referencePatient: "UM1001 | Online - 26011395625" | null,
loket: "Loket A" (optional),
loketId: 1 (optional)
}
```
---
## Mapping ke Tabel ERD
### 1. **data_kunjungan_antrean** (Tabel Utama Kunjungan)
| Field Code | Field ERD | Keterangan |
|------------|-----------|------------|
| `barcode` | `Barcode` | Barcode unik pasien (string) |
| `createdAt` | `Tanggal_daftar` | Tanggal dan waktu pendaftaran antrian |
| - | `Pasien` | FK ke `data_pasien.id` (harus dicari/lookup berdasarkan barcode atau dibuat baru) |
| - | `Tanggal_periksa` | Tanggal pemeriksaan (bisa sama dengan Tanggal_daftar atau null) |
| - | `Tanggal_check_in` | Tanggal check-in (null jika belum check-in) |
| - | `Check_in` | Status check-in (boolean/flag) |
| - | `Surat_rujukan` | Surat rujukan (null jika tidak ada) |
| - | `Surat_kontrol` | Surat kontrol (null jika tidak ada) |
| - | `SEP` | SEP BPJS (null jika tidak ada) |
| - | `Status_active` | Status aktif (default: 1 = Active) |
**Catatan:**
- `barcode` langsung map ke field `Barcode`
- `createdAt` map ke `Tanggal_daftar`
- `referencePatient` bisa digunakan untuk tracking kunjungan sebelumnya (relasi ke `data_kunjungan_antrean` lain)
---
### 2. **data_kunjungan_tempat_layanan** (Detail Tempat Layanan)
| Field Code | Field ERD | Keterangan |
|------------|-----------|------------|
| `no` | `Nomor_tiket` | Nomor urut antrian (integer) |
| `noAntrian` | - | Bisa disimpan sebagai informasi tambahan atau di-generate dari `Nomor_tiket` |
| `klinik` | `Jenis_layanan` (?) | FK ke `data_jenis_layanan.id` (harus lookup berdasarkan nama klinik) |
| `pembayaran` | `Penjamin` | FK ke `daftar_penjamin.id` (harus lookup: "BPJS" → id penjamin BPJS, "UMUM" → id penjamin UMUM) |
| `loket` | `Loket` | FK ke `data_loket.id` (harus lookup berdasarkan nama loket) |
| `loketId` | `Loket` | FK ke `data_loket.id` (jika ada, langsung gunakan) |
| `fastTrack` | `Status_fasttrack` | FK ke lookup `ID: Status_fasttrack` (1 = True jika "YA", 0 = False jika "TIDAK") |
| `fastTrack` | `Alasan_fasttrack` | Alasan fast track (string, bisa null jika "TIDAK") |
| `processStage` | `Status_kunjungan` | FK ke lookup `ID: Status_kunjungan` (harus mapping: "loket" → 1, "klinik" → 2, dll) |
| `shift` | - | Shift diambil dari `data_jenis_layanan_shift` berdasarkan jadwal |
| - | `FK_kunjunganantrean_ten` | FK ke `data_kunjungan_antrean.id` |
| - | `Jenis_kunjungan` | FK ke `daftar_jenis_kunjungan.id` (default atau lookup) |
| - | `Jenis_pelayanan` | FK ke `daftar_jenis_pelayanan.id` (bisa dari `data_jenis_layanan.Jenis_pelayanan`) |
| - | `Dokter` | FK ke `data_pegawai.id` (null jika belum ditentukan) |
| - | `Jam_awal` | Jam mulai pelayanan (bisa dari `jamPanggil` atau null) |
| - | `Jam_selesai` | Jam selesai pelayanan (null jika belum selesai) |
| - | `Tanggal_ambil_hasil` | Tanggal ambil hasil (null jika belum) |
| - | `Status_active` | Status aktif (default: 1 = Active) |
**Mapping Status:**
- `status: "di-loket"``Status_kunjungan` = 1 (LOKET)
- `status: "waiting"``Status_kunjungan` = 2 (PENDING) atau sesuai lookup
- `processStage: "klinik"``Status_kunjungan` = 2 (KLINIK)
- `processStage: "loket"``Status_kunjungan` = 1 (LOKET)
**Mapping FastTrack:**
- `fastTrack: "YA"``Status_fasttrack` = 1 (True)
- `fastTrack: "TIDAK"``Status_fasttrack` = 0 (False)
---
### 3. **data_kunjungan_tempat_panggilan** (Riwayat Panggilan)
| Field Code | Field ERD | Keterangan |
|------------|-----------|------------|
| `jamPanggil` | `Jam_panggilan` | Jam panggilan pasien (format: "HH:MM") |
| `status` | `Status_panggilan` | FK ke lookup `ID: Status_panggilan` (harus mapping berdasarkan context) |
| `processStage` | `Status_kunjungan` | FK ke lookup `ID: Status_kunjungan` |
| - | `Nama_tempat_panggilan` | Nama tempat panggilan (bisa dari `loket` atau `klinik`) |
| - | `FK_kunjungantempallayana` | FK ke `data_kunjungan_tempat_layanan.id` |
| - | `Status_active` | Status aktif (default: 1 = Active) |
**Mapping Status Panggilan:**
- Panggilan di Anjungan → `Status_panggilan` = 1 (ANJUNGAN)
- Panggilan di Pendaftaran → `Status_panggilan` = 2 (PENDAFTARAN)
- Panggilan di Pelayanan → `Status_panggilan` = 3 (PELAYANAN)
- Panggilan ambil hasil → `Status_panggilan` = 4 (AMBIL HASIL)
---
### 4. **data_kunjungan_tempat_layanan_detail** (Detail Status Kunjungan)
| Field Code | Field ERD | Keterangan |
|------------|-----------|------------|
| `createdAt` | `Tanggal_detail` | Tanggal detail status (bisa dari `createdAt`) |
| `status` | `Status_kunjungan` | FK ke lookup `ID: Status_kunjungan` |
| - | `FK_kunjungantempallayana` | FK ke `data_kunjungan_tempat_layanan.id` |
| - | `Status_active` | Status aktif (default: 1 = Active) |
**Catatan:** Tabel ini untuk tracking perubahan status kunjungan dari waktu ke waktu.
---
### 5. **data_pasien** (Data Pasien - Jika Belum Ada)
| Field Code | Field ERD | Keterangan |
|------------|-----------|------------|
| `barcode` | `Nomor_rekamedik` | Bisa digunakan sebagai nomor rekam medis sementara |
| - | `Nama` | Nama pasien (harus diinput atau dari sistem lain) |
| - | `Nomor_identitas` | NIK/KTP (harus diinput atau dari sistem lain) |
| - | `Nomor_bpjs` | Nomor BPJS (jika `pembayaran` = "BPJS") |
| - | `Status_antrean` | Status antrian pasien |
| - | `Status_active` | Status aktif (default: 1 = Active) |
**Catatan:** Jika pasien belum ada di database, harus dibuat record baru di `data_pasien` terlebih dahulu.
---
### 6. **Lookup Tables yang Digunakan**
#### **ID: Status_fasttrack**
- 0: False
- 1: True
#### **ID: Status_kunjungan**
- 1: LOKET
- 2: KLINIK
- 3: FARMASI
- 4: PENUNJANG
- 5: RAWAT INAP
**Atau (versi status):**
- 1: ACTIVE
- 2: PENDING
- 3: TERLAMBAT
- 4: KONSUL
- 5: BATAL
- 6: GAGAL
- 7: AMBIL HASIL
- 8: SELESAI PELAYANAN
#### **ID: Status_panggilan**
- 1: ANJUNGAN
- 2: PENDAFTARAN
- 3: PELAYANAN
- 4: AMBIL HASIL
#### **ID: Status_active**
- 0: Disabled
- 1: Active
---
## Alur Penyimpanan Data
### Step 1: Cek/Create Pasien
1. Cari `data_pasien` berdasarkan `barcode` atau `Nomor_rekamedik`
2. Jika tidak ada, buat record baru di `data_pasien`
### Step 2: Create Kunjungan Antrean
1. Insert ke `data_kunjungan_antrean`:
- `Barcode` = `barcode`
- `Pasien` = `data_pasien.id` (dari step 1)
- `Tanggal_daftar` = `createdAt`
- `Status_active` = 1
### Step 3: Create Tempat Layanan
1. Lookup `data_jenis_layanan.id` berdasarkan `klinik` (nama klinik)
2. Lookup `daftar_penjamin.id` berdasarkan `pembayaran` ("BPJS" atau "UMUM")
3. Lookup `data_loket.id` berdasarkan `loket` atau `loketId`
4. Insert ke `data_kunjungan_tempat_layanan`:
- `Nomor_tiket` = `no`
- `FK_kunjunganantrean_ten` = `data_kunjungan_antrean.id` (dari step 2)
- `Jenis_layanan` = `data_jenis_layanan.id`
- `Penjamin` = `daftar_penjamin.id`
- `Loket` = `data_loket.id`
- `Status_fasttrack` = 1 jika `fastTrack` = "YA", else 0
- `Status_kunjungan` = mapping dari `processStage` atau `status`
- `Status_active` = 1
### Step 4: Create Panggilan (Jika Ada)
1. Insert ke `data_kunjungan_tempat_panggilan`:
- `FK_kunjungantempallayana` = `data_kunjungan_tempat_layanan.id` (dari step 3)
- `Jam_panggilan` = `jamPanggil`
- `Nama_tempat_panggilan` = `loket` atau `klinik`
- `Status_panggilan` = mapping berdasarkan context
- `Status_kunjungan` = mapping dari `processStage`
- `Status_active` = 1
### Step 5: Create Detail Status (Optional)
1. Insert ke `data_kunjungan_tempat_layanan_detail`:
- `FK_kunjungantempallayana` = `data_kunjungan_tempat_layanan.id`
- `Tanggal_detail` = `createdAt`
- `Status_kunjungan` = mapping dari `status` atau `processStage`
- `Status_active` = 1
---
## Field yang Tidak Langsung Map
### Field yang Perlu Lookup/Transform:
1. **`klinik`** → Perlu lookup ke `data_jenis_layanan.id` berdasarkan nama
2. **`pembayaran`** → Perlu lookup ke `daftar_penjamin.id` berdasarkan value ("BPJS" atau "UMUM")
3. **`loket`** → Perlu lookup ke `data_loket.id` berdasarkan nama loket
4. **`shift`** → Perlu lookup ke `data_jenis_layanan_shift.id` berdasarkan shift dan jadwal
5. **`status`** → Perlu mapping ke lookup `ID: Status_kunjungan`
6. **`processStage`** → Perlu mapping ke lookup `ID: Status_kunjungan`
7. **`fastTrack`** → Perlu mapping ke lookup `ID: Status_fasttrack` (0 atau 1)
### Field yang Hanya untuk Display/Reference:
1. **`noAntrian`** → Format display, bisa di-generate ulang dari `Nomor_tiket` dan `Barcode`
2. **`referencePatient`** → Reference ke kunjungan sebelumnya (relasi ke `data_kunjungan_antrean` lain)
---
## Contoh Mapping Lengkap
### Input Data:
```javascript
{
no: 1,
jamPanggil: "12:49",
barcode: "26011395625",
noAntrian: "UM1001 | Online - 26011395625",
shift: "Shift 1",
klinik: "KANDUNGAN",
fastTrack: "YA",
pembayaran: "BPJS",
status: "di-loket",
processStage: "loket",
createdAt: "2026-01-13T16:51:35.502Z",
loket: "Loket A",
loketId: 1
}
```
### Output ke Database:
#### data_kunjungan_antrean:
```sql
INSERT INTO data_kunjungan_antrean (
Barcode,
Pasien,
Tanggal_daftar,
Status_active
) VALUES (
'26011395625',
[data_pasien.id], -- dari lookup
'2026-01-13T16:51:35.502Z',
1
);
```
#### data_kunjungan_tempat_layanan:
```sql
INSERT INTO data_kunjungan_tempat_layanan (
Nomor_tiket,
FK_kunjunganantrean_ten,
Penjamin, -- lookup dari "BPJS"
Jenis_layanan, -- lookup dari "KANDUNGAN"
Loket, -- lookup dari "Loket A" atau langsung 1
Status_fasttrack, -- 1 (karena "YA")
Status_kunjungan, -- 1 (karena "loket" → LOKET)
Status_active
) VALUES (
1,
[data_kunjungan_antrean.id],
[daftar_penjamin.id WHERE Penjamin = 'BPJS'],
[data_jenis_layanan.id WHERE Nama_jenis_layanan = 'KANDUNGAN'],
1,
1,
1,
1
);
```
#### data_kunjungan_tempat_panggilan:
```sql
INSERT INTO data_kunjungan_tempat_panggilan (
FK_kunjungantempallayana,
Jam_panggilan,
Nama_tempat_panggilan,
Status_panggilan, -- 2 (PENDAFTARAN) atau sesuai context
Status_kunjungan, -- 1 (LOKET)
Status_active
) VALUES (
[data_kunjungan_tempat_layanan.id],
'12:49',
'Loket A',
2, -- PENDAFTARAN
1, -- LOKET
1
);
```
---
## Catatan Penting
1. **Foreign Key Lookups**: Banyak field yang memerlukan lookup ke tabel lain sebelum insert
2. **Status Mapping**: Field `status` dan `processStage` perlu mapping ke lookup table `ID: Status_kunjungan`
3. **Default Values**: Field yang tidak ada di code perlu diisi dengan default value atau null
4. **Relasi**: `referencePatient` bisa digunakan untuk membuat relasi ke `data_kunjungan_antrean` sebelumnya
5. **Shift**: Field `shift` perlu diambil dari `data_jenis_layanan_shift` berdasarkan jadwal dan jenis layanan
@@ -190,7 +190,7 @@ const props = defineProps({
type: Number,
default: 0
},
waitingCount: {
anjunganCount: {
type: Number,
default: 0
},
@@ -289,12 +289,12 @@ const statusOptions = computed(() => {
});
}
// Tambahkan kategori "Menunggu" (pasien yang sudah dipanggil tapi belum check-in)
if (props.waitingCount > 0) {
// Tambahkan kategori "Anjungan" (pasien yang sudah dipanggil tapi belum check-in)
if (props.anjunganCount > 0) {
baseOptions.push({
value: 'waiting',
label: 'Menunggu',
count: props.waitingCount,
value: 'anjungan',
label: 'Anjungan',
count: props.anjunganCount,
icon: 'mdi-clock-outline'
});
}
+3 -3
View File
@@ -113,9 +113,9 @@ const loketData = computed(() => {
// Mock queue data untuk preview
const mockQueues = ref([
{ no: 1, noAntrian: 'RT001', klinik: 'R. TINDAKAN', status: 'current' },
{ no: 2, noAntrian: 'RT002', klinik: 'R. TINDAKAN', status: 'waiting' },
{ no: 3, noAntrian: 'RD001', klinik: 'RADIOTERAPI', status: 'waiting' },
{ no: 4, noAntrian: 'RM001', klinik: 'REHAB MEDIK', status: 'waiting' },
{ no: 2, noAntrian: 'RT002', klinik: 'R. TINDAKAN', status: 'anjungan' },
{ no: 3, noAntrian: 'RD001', klinik: 'RADIOTERAPI', status: 'anjungan' },
{ no: 4, noAntrian: 'RM001', klinik: 'REHAB MEDIK', status: 'anjungan' },
])
// Simulasi klinik dengan queue
+4 -4
View File
@@ -64,14 +64,14 @@ export const useQueue = (adminType = "loket", specificId = null) => {
return patients;
});
const waitingPatients = computed(() => stagePatients.value.waiting || []);
const anjunganPatients = computed(() => stagePatients.value.anjungan || []);
// Pasien yang belum dipanggil (status "menunggu")
const menungguPatients = computed(() => stagePatients.value.menunggu || []);
const nextPatient = computed(() => {
// Prioritaskan pasien menunggu, baru waiting
return menungguPatients.value[0] || waitingPatients.value[0] || null;
// Prioritaskan pasien menunggu, baru anjungan
return menungguPatients.value[0] || anjunganPatients.value[0] || null;
});
// Total pasien hanya untuk stage admin ini
@@ -314,7 +314,7 @@ export const useQueue = (adminType = "loket", specificId = null) => {
diLoketPatients,
terlambatPatients,
pendingPatients,
waitingPatients,
anjunganPatients,
menungguPatients,
nextPatient,
totalPasien,
+2 -2
View File
@@ -10,7 +10,7 @@ export interface Patient {
klinik: string;
fastTrack: string;
pembayaran: string;
status: 'waiting' | 'pending' | 'di-loket' | 'di-klinik' | 'selesai' | 'terlambat';
status: 'anjungan' | 'pending' | 'di-loket' | 'di-klinik' | 'selesai' | 'terlambat';
processStage: 'loket' | 'klinik' | 'penunjang';
createdAt: string;
registrationType?: 'online' | 'onsite';
@@ -127,7 +127,7 @@ export const useQueueAPI = () => {
idOrBarcode: string,
updates: { status?: string; processStage?: string }
): Promise<Patient> => {
return updatePatient(idOrBarcode, updates);
return updatePatient(idOrBarcode, updates as Partial<Patient>);
};
/**
+3 -3
View File
@@ -937,7 +937,7 @@ const getAllPatientsForRoom = (ruang) => {
p.ruang === ruang.namaRuang &&
p.processStage === 'klinik-ruang' &&
// Include semua pasien dengan status yang relevan
(p.status === 'waiting' || p.status === 'di-loket' || p.status === 'terlambat' || p.status === 'pending')
(p.status === 'anjungan' || p.status === 'di-loket' || p.status === 'terlambat' || p.status === 'pending')
);
};
@@ -1134,7 +1134,7 @@ const getAvailablePatientsForKelola = () => {
let patients = queueStore.allPatients.filter(p =>
p.kodeKlinik === klinikData.value?.kodeKlinik &&
p.processStage === 'klinik-ruang' &&
(p.status === 'waiting' || p.status === 'di-loket' || p.status === 'terlambat' || p.status === 'pending')
(p.status === 'anjungan' || p.status === 'di-loket' || p.status === 'terlambat' || p.status === 'pending')
);
// Apply search filter
@@ -1694,7 +1694,7 @@ const handleProcessPendingPatient = (ruang, patient) => {
// Update status pasien dari pending menjadi waiting
queueStore.allPatients[patientIndex] = {
...queueStore.allPatients[patientIndex],
status: 'waiting'
status: 'anjungan'
};
// Set sebagai current processing
+10 -10
View File
@@ -81,7 +81,7 @@
v-model:selected-fast-track="selectedFastTrack"
:di-loket-count="filteredDiLoketCount"
:diproses-count="currentProcessingPatient ? 1 : 0"
:waiting-count="filteredWaitingCount"
:anjungan-count="filteredAnjunganCount"
:terlambat-count="filteredTerlambatCount"
:pending-count="filteredPendingCount"
:show-diproses="false"
@@ -261,7 +261,7 @@ const {
diLoketPatients,
terlambatPatients,
pendingPatients,
waitingPatients,
anjunganPatients,
menungguPatients,
nextPatient,
menungguCount,
@@ -436,7 +436,7 @@ const allPatientsForStage = computed(() => {
if (isLoketEksekutif) {
// For EKSEKUTIF loket, use seed data (filter from allPatients)
// IMPORTANT: Do NOT include menungguPatients - they are only called via QueueActionsCard
basePatients = diLoketPatients.value.concat(waitingPatients.value, terlambatPatients.value, pendingPatients.value);
basePatients = diLoketPatients.value.concat(anjunganPatients.value, terlambatPatients.value, pendingPatients.value);
} else {
// For REGULER loket, use API data from allPatients (which is reactive)
// Filter patients that belong to this loket and are from API
@@ -525,14 +525,14 @@ const allPatientsForStage = computed(() => {
status: "pending",
}));
const waiting = (waitingPatients.value || [])
const anjungan = (anjunganPatients.value || [])
.filter(isPatientForThisLoket)
.map((p) => ({
...p,
status: "waiting",
status: "anjungan",
}));
combined = [...diLoket, ...waiting, ...terlambat, ...pending];
combined = [...diLoket, ...anjungan, ...terlambat, ...pending];
} else {
// Use API data, but EXCLUDE patients with status "menunggu"
// They should only be called via QueueActionsCard, not displayed in table
@@ -549,8 +549,8 @@ const allPatientsForStage = computed(() => {
// Computed status counts based on filtered allPatientsForStage
// These counts are ONLY for patients that belong to this specific loket
const filteredWaitingCount = computed(() => {
return allPatientsForStage.value.filter(p => p.status === 'waiting').length;
const filteredAnjunganCount = computed(() => {
return allPatientsForStage.value.filter(p => p.status === 'anjungan').length;
});
const filteredDiLoketCount = computed(() => {
@@ -598,8 +598,8 @@ const filteredMenungguCount = computed(() => {
}
});
const waitingCount = computed(() => {
return (waitingPatients.value || []).length;
const anjunganCount = computed(() => {
return (anjunganPatients.value || []).length;
});
const nextQueueInfo = computed(() => {
+2 -2
View File
@@ -313,7 +313,7 @@ const calledForCheckIn = computed(() => {
.filter(p => {
// Must be called (waiting status = sudah dipanggil dari status menunggu)
// and not yet checked in (processStage still 'loket')
const isCalled = p.status === 'waiting' && p.processStage === 'loket'
const isCalled = p.status === 'anjungan' && p.processStage === 'loket'
// Filter berdasarkan loketId yang dikonfigurasi di screen
// Handle potential undefined or number types
@@ -432,7 +432,7 @@ const statistics = computed(() => {
// Menunggu = belum dipanggil (status 'menunggu')
const menungguCount = allLoketTickets.filter(p => p && p.status === 'menunggu').length
// Dipanggil untuk check-in = sudah dipanggil (status 'waiting')
// Dipanggil untuk check-in = sudah dipanggil (status 'anjungan')
const calledForCheckInArray = Array.isArray(calledForCheckIn.value) ? calledForCheckIn.value : []
const activeCount = calledForCheckInArray.length
+3 -3
View File
@@ -261,7 +261,7 @@ const displayedClinics = computed(() => {
// Current queue: prioritaskan di-loket, lalu waiting, lalu pending (dengan card merah)
const currentQueue =
queues.find((q) => q.status === "di-loket") ||
queues.find((q) => q.status === "waiting") ||
queues.find((q) => q.status === "anjungan") ||
queues.find((q) => q.status === "pending") ||
null;
@@ -269,7 +269,7 @@ const displayedClinics = computed(() => {
const nextQueues = queues.filter(
(q) =>
q.no !== currentQueue?.no &&
(q.status === "waiting" || q.status === "di-loket" || q.status === "pending")
(q.status === "anjungan" || q.status === "di-loket" || q.status === "pending")
);
// Pending queues: pasien dengan status pending yang tidak di current atau next
@@ -321,7 +321,7 @@ const statistics = computed(() => {
return {
total: all.length,
waiting: all.filter((p) => p.status === "waiting").length,
anjungan: all.filter((p) => p.status === "anjungan").length,
active: all.filter((p) => p.status === "di-loket").length,
};
});
@@ -328,7 +328,7 @@ const displayedRuang = computed(() => {
// Next queues: exclude current patient dan pasien dengan status 'di-loket' (yang sudah dipanggil)
const next = queues.filter(q =>
q.no !== current?.no &&
q.status === 'waiting' // Hanya yang waiting (belum dipanggil)
q.status === 'anjungan' // Hanya yang anjungan (belum dipanggil)
).slice(0, 3) // Show max 3 next queues
return { current, next }
}
@@ -389,7 +389,7 @@ const statistics = computed(() => {
const all = klinikPatients.value
return {
total: all.length,
waiting: all.filter(p => p.status === 'waiting').length,
anjungan: all.filter(p => p.status === 'anjungan').length,
active: all.filter(p => p.status === 'di-loket').length
}
})
+1 -1
View File
@@ -256,7 +256,7 @@ const loketPatients = computed(() => {
// Include 'di-loket' (serving/called), 'waiting' (next in line) and 'pending' (on hold)
return allPatients.filter(p =>
p.processStage === 'loket' &&
(p.status === 'di-loket' || p.status === 'waiting' || p.status === 'pending')
(p.status === 'di-loket' || p.status === 'anjungan' || p.status === 'pending')
);
});
+5 -5
View File
@@ -110,7 +110,7 @@
<v-icon size="32" color="#9C27B0">mdi-clock-alert-outline</v-icon>
</div>
<div class="stat-info">
<div class="stat-value">{{ statistics.waiting }}</div>
<div class="stat-value">{{ statistics.anjungan }}</div>
<div class="stat-label">Menunggu</div>
</div>
</div>
@@ -161,7 +161,7 @@ const displayedPenunjangs = computed(() => {
.sort((a, b) => {
const statusPriority = {
'di-loket': 1,
'waiting': 2,
'anjungan': 2,
'terlambat': 3,
'pending': 4
}
@@ -175,11 +175,11 @@ const displayedPenunjangs = computed(() => {
})
const currentQueue = queues.find(q => q.status === 'di-loket') ||
(queues.find(q => q.status === 'waiting') || null)
(queues.find(q => q.status === 'anjungan') || null)
const nextQueues = queues.filter(q =>
q.no !== currentQueue?.no &&
(q.status === 'waiting' || q.status === 'di-loket')
(q.status === 'anjungan' || q.status === 'di-loket')
)
return {
@@ -203,7 +203,7 @@ const statistics = computed(() => {
const all = penunjangPatients.value
return {
total: all.length,
waiting: all.filter(p => p.status === 'waiting').length,
anjungan: all.filter(p => p.status === 'anjungan').length,
active: all.filter(p => p.status === 'di-loket').length
}
})
+3 -3
View File
@@ -3535,15 +3535,15 @@ const todayStats = computed(() => {
const statsWaiting = computed(() => {
// Ambil data menunggu dari queueStore untuk stage 'loket'
// Menghitung pasien dengan status 'menunggu' (belum dipanggil) atau 'waiting' (sudah dipanggil tapi belum check-in)
// Menghitung pasien dengan status 'menunggu' (belum dipanggil) atau 'anjungan' (sudah dipanggil tapi belum check-in)
// Di halaman CheckInPasien, kita menghitung total yang masih menunggu check-in (belum + sudah dipanggil)
try {
const loketPatients = queueStore.getPatientsByStage('loket');
const menungguPatients = loketPatients.value.menunggu || [];
const waitingPatients = loketPatients.value.waiting || [];
const anjunganPatients = loketPatients.value.anjungan || [];
// Total pasien yang masih menunggu check-in (belum dipanggil + sudah dipanggil tapi belum check-in)
return menungguPatients.length + waitingPatients.length;
return menungguPatients.length + anjunganPatients.length;
} catch (error) {
console.error('Error calculating statsWaiting:', error);
return 0;
+96 -39
View File
@@ -59,20 +59,20 @@ export const useQueueStore = defineStore('queue', () => {
/**
* Reference mapping for patient status from API idvisit
* idvisit 1, 2 = menunggu (CT ANJUNGAN, RT ANJUNGAN)
* idvisit 3, 4 = waiting (PG ANJUNGAN, RT CHECK-IN)
* idvisit 3, 4 = anjungan (PG ANJUNGAN, RT CHECK-IN)
* idvisit 5, 6 = di-loket (PS CHECK-IN, TP LOKET)
*/
const PATIENT_STATUS_MAP = {
1: 'menunggu',
2: 'menunggu',
3: 'waiting',
4: 'waiting',
3: 'anjungan',
4: 'anjungan',
5: 'di-loket',
6: 'di-loket',
"1": 'menunggu',
"2": 'menunggu',
"3": 'waiting',
"4": 'waiting',
"3": 'anjungan',
"4": 'anjungan',
"5": 'di-loket',
"6": 'di-loket'
};
@@ -87,7 +87,7 @@ export const useQueueStore = defineStore('queue', () => {
/**
* Map status dari deskripsi API ke status internal
* menunggu (id 1, 2): "Cetak Tiket Antrian" atau "Ruang Tunggu Anjungan"
* waiting (id 3, 4): "Panggilan loket ke Anjungan" atau "Tunggu Pasien Check-In"
* anjungan (id 3, 4): "Panggilan loket ke Anjungan" atau "Tunggu Pasien Check-In"
* di-loket (id 5, 6): "Pasien Sudah Check-In" atau "Tunggu Panggilan Loket"
*/
const mapStatusFromDeskripsi = (deskripsi) => {
@@ -100,9 +100,9 @@ export const useQueueStore = defineStore('queue', () => {
return 'di-loket';
}
// waiting (id 3, 4): PG ANJUNGAN (Panggilan loket ke Anjungan), RT CHECK-IN (Tunggu Pasien Check-In)
// anjungan (id 3, 4): PG ANJUNGAN (Panggilan loket ke Anjungan), RT CHECK-IN (Tunggu Pasien Check-In)
if (desc.includes('PANGGILAN LOKET KE ANJUNGAN') || desc.includes('TUNGGU PASIEN CHECK-IN') || desc.includes('PG ANJUNGAN') || desc.includes('RT CHECK-IN')) {
return 'waiting';
return 'anjungan';
}
// menunggu (id 1, 2): CT ANJUNGAN (Cetak Tiket Antrian), RT ANJUNGAN (Ruang Tunggu Anjungan)
@@ -131,8 +131,8 @@ export const useQueueStore = defineStore('queue', () => {
if (apiPatient.posisi && Array.isArray(apiPatient.posisi) && apiPatient.posisi.length > 0) {
// Find the "best" status among all positions
// Priority: di-loket > waiting > menunggu
const statusPriority = { 'di-loket': 3, 'waiting': 2, 'menunggu': 1 };
// Priority: di-loket > anjungan > menunggu
const statusPriority = { 'di-loket': 3, 'anjungan': 2, 'menunggu': 1 };
let bestPriority = 0;
apiPatient.posisi.forEach(pos => {
@@ -259,8 +259,8 @@ export const useQueueStore = defineStore('queue', () => {
if (!existing) {
deduplicatedMap.set(id, p);
} else {
// Priority: di-loket > waiting > menunggu
const statusPriority = { 'di-loket': 3, 'waiting': 2, 'menunggu': 1 };
// Priority: di-loket > anjungan > menunggu
const statusPriority = { 'di-loket': 3, 'anjungan': 2, 'menunggu': 1 };
const pPrio = statusPriority[p.status] || 0;
const ePrio = statusPriority[existing.status] || 0;
@@ -296,9 +296,9 @@ export const useQueueStore = defineStore('queue', () => {
allPatients.value.forEach(p => {
const key = p.idtiket ? `id-${p.idtiket}` : `bc-${p.barcode}`;
if (newPatientMap.has(key)) {
// If existing patient has more advanced status (waiting/di-loket), preserve it!
// If existing patient has more advanced status (anjungan/di-loket), preserve it!
// This handles cases where we updated status locally ('onsite' or 'api') but API is lagging
const statusPriority = { 'di-loket': 3, 'waiting': 2, 'menunggu': 1 };
const statusPriority = { 'di-loket': 3, 'anjungan': 2, 'menunggu': 1 };
const existingPrio = statusPriority[p.status] || 0;
const newPatient = newPatientMap.get(key);
const newPrio = statusPriority[newPatient.status] || 0;
@@ -577,7 +577,7 @@ export const useQueueStore = defineStore('queue', () => {
// kodeKlinik: "KD",
// fastTrack: "TIDAK",
// pembayaran: "Eksekutif",
// status: "waiting",
// status: "anjungan",
// processStage: "loket",
// createdAt: new Date().toISOString(),
// registrationType: 'online',
@@ -598,7 +598,7 @@ export const useQueueStore = defineStore('queue', () => {
// kodeKlinik: "IP",
// fastTrack: "TIDAK",
// pembayaran: "Eksekutif",
// status: "waiting",
// status: "anjungan",
// processStage: "loket",
// createdAt: new Date().toISOString(),
// registrationType: 'online',
@@ -619,7 +619,7 @@ export const useQueueStore = defineStore('queue', () => {
// kodeKlinik: "SR",
// fastTrack: "YA",
// pembayaran: "Eksekutif",
// status: "waiting",
// status: "anjungan",
// processStage: "loket",
// createdAt: new Date().toISOString(),
// registrationType: 'online',
@@ -640,7 +640,7 @@ export const useQueueStore = defineStore('queue', () => {
// kodeKlinik: "TH",
// fastTrack: "TIDAK",
// pembayaran: "VIP",
// status: "waiting",
// status: "anjungan",
// processStage: "loket",
// createdAt: new Date().toISOString(),
// registrationType: 'online',
@@ -890,7 +890,7 @@ export const useQueueStore = defineStore('queue', () => {
return {
all: patients,
waiting: patients.filter(p => p.status === 'waiting'),
anjungan: patients.filter(p => p.status === 'anjungan'),
menunggu: patients.filter(p => p.status === 'menunggu'),
diLoket: patients.filter(p => p.status === 'di-loket'),
terlambat: patients.filter(p => p.status === 'terlambat'),
@@ -977,20 +977,47 @@ export const useQueueStore = defineStore('queue', () => {
return { success: false, message: "Kuota sudah penuh" };
}
// Update status menjadi 'waiting' (Masuk ke antrean aktif)
// Update status menjadi 'anjungan' (Masuk ke antrean aktif)
const callTimestamp = new Date().toISOString();
const index = allPatients.value.findIndex(p => p.no === nextPatient.no);
if (index !== -1) {
allPatients.value[index] = {
...allPatients.value[index],
status: "waiting",
status: "anjungan",
// Assign to this specific loket if it was unassigned
loketId: (adminType === 'loket' && targetId) ? parseInt(targetId) : allPatients.value[index].loketId,
lastCalledAt: callTimestamp
};
// SYNC to apiPatientsPerLoket if it's an API patient
syncApiPatientStatus(allPatients.value[index], "waiting");
syncApiPatientStatus(allPatients.value[index], "anjungan");
// POST to external API when patient is called
try {
fetch('http://10.10.150.131:8089/api/v1/tiket/update', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
},
body: JSON.stringify({
idtiket: nextPatient.barcode || "",
statuspasien: "3",
statuspasien2: "4",
idklinikstatus: "1",
idklinikstatus2: "1"
})
}).then(response => {
if (response.ok) {
console.log(`✅ Successfully posted status update for patient ${nextPatient.barcode}`);
} else {
console.error(`⚠️ Failed to post status update for patient ${nextPatient.barcode}:`, response.status);
}
}).catch(error => {
console.error(`❌ Error posting status update for patient ${nextPatient.barcode}:`, error);
});
} catch (error) {
console.error(`❌ Error initiating status update for patient ${nextPatient.barcode}:`, error);
}
}
return {
@@ -1045,10 +1072,10 @@ export const useQueueStore = defineStore('queue', () => {
const patientsToCall = menungguList.slice(0, maxCallable);
const callTimestamp = new Date().toISOString();
patientsToCall.forEach((patient) => {
patientsToCall.forEach(async (patient) => {
const index = allPatients.value.findIndex(p => p.no === patient.no);
if (index !== -1) {
const newStatus = "waiting";
const newStatus = "anjungan";
const newLoketId = (adminType === 'loket' && targetId) ? targetId : allPatients.value[index].loketId;
allPatients.value[index] = {
@@ -1060,6 +1087,31 @@ export const useQueueStore = defineStore('queue', () => {
// SYNC to apiPatientsPerLoket if it's an API patient
syncApiPatientStatus(allPatients.value[index], newStatus);
// POST to external API when patient is called
try {
const response = await fetch('http://10.10.150.131:8089/api/v1/tiket/update', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
},
body: JSON.stringify({
idtiket: patient.barcode || "",
statuspasien: "3",
statuspasien2: "4",
idklinikstatus: "1",
idklinikstatus2: "1"
})
});
if (response.ok) {
console.log(`✅ Successfully posted status update for patient ${patient.barcode}`);
} else {
console.error(`⚠️ Failed to post status update for patient ${patient.barcode}:`, response.status);
}
} catch (error) {
console.error(`❌ Error posting status update for patient ${patient.barcode}:`, error);
}
}
});
@@ -1375,7 +1427,7 @@ export const useQueueStore = defineStore('queue', () => {
fastTrack: patient ? (patient.fastTrack || "TIDAK") : "TIDAK",
pembayaran: patient ? patient.pembayaran : "UMUM",
noRM: patient ? patient.noRM : null,
status: "waiting",
status: "anjungan",
processStage: "klinik-ruang", // Set ke klinik-ruang langsung
createdAt: timestamp.toISOString(),
referencePatient: patient ? patient.noAntrian : null,
@@ -1517,7 +1569,7 @@ export const useQueueStore = defineStore('queue', () => {
tipeLayanan: tipeLayanan,
fastTrack: sourcePatient.fastTrack || "TIDAK",
pembayaran: sourcePatient.pembayaran || "UMUM",
status: "waiting",
status: "anjungan",
processStage: "klinik-ruang",
createdAt: sourcePatient.createdAt || timestamp.toISOString(), // Gunakan createdAt dari pasien awal
referencePatient: sourcePatient.noAntrian,
@@ -1618,7 +1670,7 @@ export const useQueueStore = defineStore('queue', () => {
tipeLayanan: tipeLayanan,
fastTrack: sourcePatient.fastTrack || "TIDAK",
pembayaran: sourcePatient.pembayaran || "UMUM",
status: "waiting",
status: "anjungan",
processStage: "klinik-ruang",
createdAt: sourcePatient.createdAt || timestamp.toISOString(), // Gunakan createdAt dari pasien awal
referencePatient: sourcePatient.noAntrian,
@@ -1649,7 +1701,7 @@ export const useQueueStore = defineStore('queue', () => {
return {
all: patients,
waiting: patients.filter(p => p.status === 'waiting'),
anjungan: patients.filter(p => p.status === 'anjungan'),
diLoket: patients.filter(p => p.status === 'di-loket'),
terlambat: patients.filter(p => p.status === 'terlambat'),
pending: patients.filter(p => p.status === 'pending'),
@@ -1664,10 +1716,10 @@ export const useQueueStore = defineStore('queue', () => {
p.nomorRuang === nomorRuang &&
p.tipeLayanan === tipeLayanan &&
p.processStage === 'klinik-ruang' &&
(p.status === 'waiting' || (allowMultiple && p.status === 'di-loket'))
(p.status === 'anjungan' || (allowMultiple && p.status === 'di-loket'))
).sort((a, b) => {
// Sort by status priority first: waiting=1, di-loket=2
const statusPriority = { 'waiting': 1, 'di-loket': 2 };
// Sort by status priority first: anjungan=1, di-loket=2
const statusPriority = { 'anjungan': 1, 'di-loket': 2 };
const priorityDiff = (statusPriority[a.status] || 99) - (statusPriority[b.status] || 99);
if (priorityDiff !== 0) return priorityDiff;
@@ -1895,12 +1947,17 @@ export const useQueueStore = defineStore('queue', () => {
const idloket = targetLoket ? String(targetLoket.id) : "2"; // Default fallback to "2" if not found
// 2. Prepare API Body
// 2. Determine idpembayaran based on paymentType
// BPJS (JKN) = "2", UMUM and others = "1"
const idpembayaran = paymentType === "BPJS" ? "2" : "1";
// 3. Prepare API Body
const body = {
idloket: idloket,
dokter: "",
idklinik: String(clinic.id),
namaklinik: clinic.name,
idpembayaran: idpembayaran,
statuspasien: "1",
statuspasien2: "2",
idklinikstatus: "1"
@@ -2046,7 +2103,7 @@ export const useQueueStore = defineStore('queue', () => {
};
// Check-in patient (update status from waiting to di-loket)
// Check-in patient (update status from anjungan to di-loket)
// IMPORTANT: Hanya menggunakan EXACT barcode match untuk menghindari false positive
// Format barcode: YYMMDD + 5 digit (contoh: 26011500001)
// Jangan gunakan fallback ke noAntrian atau no karena bisa menyebabkan false positive
@@ -2096,7 +2153,7 @@ export const useQueueStore = defineStore('queue', () => {
processStage: patient.processStage
});
// Only allow check-in if status is waiting (sudah dipanggil) or pending
// Only allow check-in if status is anjungan (sudah dipanggil) or pending
// Pasien dengan status "menunggu" belum bisa check-in (belum dipanggil)
if (patient.status === 'menunggu') {
console.log('⚠️ Patient status is menunggu (belum dipanggil):', patient.status);
@@ -2115,8 +2172,8 @@ export const useQueueStore = defineStore('queue', () => {
};
}
if (patient.status !== 'waiting' && patient.status !== 'pending') {
console.log('⚠️ Patient status is not waiting/pending:', patient.status);
if (patient.status !== 'anjungan' && patient.status !== 'pending') {
console.log('⚠️ Patient status is not anjungan/pending:', patient.status);
return {
success: false,
message: `Pasien tidak dapat check-in. Status saat ini: ${patient.status}`
@@ -2155,7 +2212,7 @@ export const useQueueStore = defineStore('queue', () => {
console.log(`🚀 [processNextQueue] Processing for ${key} (Stage: ${targetStage})`);
// 2. Find next patient (Status: 'menunggu' -> 'waiting')
// 2. Find next patient (Status: 'menunggu' -> 'anjungan')
// Prioritaskan yang assigned ke loket ini (loketId) jika ada match
let nextPatient = null;
@@ -2177,10 +2234,10 @@ export const useQueueStore = defineStore('queue', () => {
);
}
// Fallback: Check 'waiting' status if needed (though usually 'menunggu' is for calling)
// Fallback: Check 'anjungan' status if needed (though usually 'menunggu' is for calling)
if (!nextPatient) {
nextPatient = allPatients.value.find(p =>
p.status === 'waiting' &&
p.status === 'anjungan' &&
p.processStage === targetStage &&
(adminType !== 'loket' || String(p.loketId) === String(specificId))
);