sp_generate_dashboard_data menebak Mcu_CheckinoutOutTime dari MAX() timestamp event lab (sample receive/sampling done), bukan dari checkout fisik pasien, sehingga pasien yang belum checkout bisa tampil "Checked Out" walau stationnya belum selesai. Ganti sumbernya ke cpone.preregister_checkin_checkout (log checkin/checkout asli) dan hentikan pemaksaan Mcu_CheckinoutID = order_id yang bentrok untuk project multi-hari. KPI "Checked Out" di GetKPI juga diselaraskan dengan detail pasien: sekarang hitung dari GetAllPatients dan mensyaratkan semua station wajib sudah selesai, bukan cuma checkout fisik saja, supaya angka KPI dan modal detail selalu sinkron.
CPONE Dashboard
Web dashboard berbasis Go untuk monitoring CPONE.
Prerequisites
- Go 1.22+
- Akses SSH ke
devcpone.aplikasi.web.id(untuk koneksi ke database)
Cara Menjalankan
1. Setup .env
Salin .env.example menjadi .env:
cp .env.example .env
Isi .env dengan credential yang sesuai:
APP_PORT=8080
DB_DSN=user:password@tcp(127.0.0.1:3307)/cpone_dashboard?parseTime=true&loc=Local
Port
3307digunakan karena database diakses melalui SSH tunnel (bukan langsung port 3306).
2. Buka SSH Tunnel ke Database
Jalankan perintah ini di terminal terpisah (atau di background):
ssh -f -N -L 3307:127.0.0.1:3306 one@devcpone.aplikasi.web.id
Kalau muncul error Address already in use, berarti tunnel sudah aktif — lanjut ke langkah berikutnya.
3. Jalankan App
go run .
App berjalan di: http://localhost:8080/dashboard
Menjalankan Simulasi Live di Devcpone
scripts/demo_live.sh dijalankan di server devcpone (bukan di lokal), agar memakai environment dan database server yang sama.
scp scripts/demo_live.sh one@devcpone.aplikasi.web.id:/tmp/demo_live.sh
ssh one@devcpone.aplikasi.web.id "chmod +x /tmp/demo_live.sh && /tmp/demo_live.sh 5"
Keterangan:
- Angka
5adalah interval detik antar siklus simulasi. - Stop simulasi:
Ctrl+C.
Cara Menghentikan
Hentikan tunnel dan app sekaligus:
make stop
Atau manual — cari dan kill prosesnya:
# Hentikan app (port 8080)
lsof -ti:8080 | xargs kill
# Hentikan SSH tunnel (port 3307)
lsof -ti:3307 | xargs kill
Kustomisasi Tampilan
Ganti Logo
Timpa file berikut dengan logo baru (format PNG, background transparan atau putih):
static/img/logo.png
Tidak perlu ubah kode — logo langsung berubah di seluruh halaman.
Ganti Warna Utama
Semua warna primary dikendalikan dari satu tempat di templates/layout/base.html:
tailwind.config = {
theme: {
extend: {
colors: {
brand: {
50: '#eef0fb',
100: '#dde2f7',
...
500: '#3b50a0', // <-- warna utama (header, badge, dll)
...
}
}
}
}
}
Ubah nilai hex di blok brand, semua elemen yang pakai brand-* ikut berubah.
Autentikasi & Manajemen User
Alur Login
Browser Go (auth middleware) DB (dashboard_user)
│ │ │
│ GET /dashboard │ │
│ ────────────────────────► │ cek cookie session │
│ │ (tidak ada / invalid) │
│ redirect /login ◄────── │ │
│ │ │
│ POST /login │ │
│ username=budi ───► │ SELECT User_Password, │
│ password=xxx │ User_Salt ──► │
│ │ ◄──────── │ hash + salt
│ │ SHA2(salt:password, 256) │
│ │ cocokkan dengan hash │
│ │ │
│ set cookie + redirect ◄─ │ ✓ cocok → set session cookie│
│ ke /dashboard │ │
Alur Password Hashing
Password tidak pernah disimpan dalam bentuk plain text. Setiap user punya salt unik yang di-generate otomatis saat insert/update.
sp_upsert_dashboard_user('budi', 'password123', 'Budi Santoso')
│
├─ v_salt = UUID() → contoh: "a91f-yyyy-..."
├─ v_hash = SHA2("a91f-yyyy-...:password123", 256)
│
└─ simpan ke DB:
User_Password = "f3c2..." ← hash
User_Salt = "a91f-..." ← salt
Efeknya: dua user dengan password yang sama tetap menghasilkan hash yang berbeda di DB, karena salt-nya berbeda.
Menambah User Baru
-- Error jika username sudah terdaftar
CALL sp_insert_dashboard_user('budi', 'password123', 'Budi Santoso');
Reset Password
-- Error jika username tidak ditemukan
CALL sp_reset_dashboard_user_password('budi', 'passwordbaru');
Nonaktifkan User
UPDATE dashboard_user SET User_IsActive = 'N' WHERE User_Username = 'budi';
Struktur Tabel dashboard_user
| Kolom | Tipe | Keterangan |
|---|---|---|
User_ID |
INT | Primary key, auto increment |
User_Username |
VARCHAR(50) | Login username, unique |
User_Password |
VARCHAR(64) | SHA2-256 hash |
User_Salt |
VARCHAR(36) | UUID salt, unik per user/reset |
User_DisplayName |
VARCHAR(100) | Nama yang ditampilkan di header |
User_IsActive |
CHAR(1) | Y = aktif, N = nonaktif |
Mapping User ke MCU Project
Satu user bisa diassign ke lebih dari satu mcu_project. Relasi disimpan di tabel dashboard_user_project.
Relasi
dashboard_user dashboard_user_project mcu_project
────────────── ────────────────────── ───────────
User_ID ◄──── UserProj_UserID Mcu_ProjectMcuID
UserProj_McuID ────►
UserProj_IsActive
Assign / Cabut Akses Project
-- Assign user ke satu project
CALL sp_assign_user_project('budi', 101);
-- Assign ke project lain (bisa lebih dari satu)
CALL sp_assign_user_project('budi', 205);
-- Cabut akses dari satu project (soft delete, data tetap ada)
CALL sp_remove_user_project('budi', 101);
Lihat Project yang Bisa Diakses User
SELECT
u.User_Username,
u.User_DisplayName,
p.Mcu_ProjectMcuID,
p.Mcu_ProjectLabel,
p.Mcu_ProjectCorporateName,
up.UserProj_IsActive
FROM dashboard_user_project up
JOIN dashboard_user u ON u.User_ID = up.UserProj_UserID
JOIN mcu_project p ON p.Mcu_ProjectMcuID = up.UserProj_McuID
WHERE u.User_Username = 'budi'
AND up.UserProj_IsActive = 'Y';
Struktur Tabel dashboard_user_project
| Kolom | Tipe | Keterangan |
|---|---|---|
UserProj_ID |
INT | Primary key, auto increment |
UserProj_UserID |
INT | Referensi ke dashboard_user.User_ID |
UserProj_McuID |
INT | Referensi ke mcu_project.Mcu_ProjectMcuID |
UserProj_IsActive |
CHAR(1) | Y = aktif, N = dicabut |
Shortcut via Makefile
| Perintah | Keterangan |
|---|---|
make start |
Buka SSH tunnel + jalankan app |
make stop |
Hentikan app dan SSH tunnel |
make restart |
Stop lalu start ulang |
make run |
Jalankan app saja (tanpa buka tunnel) |
make build |
Build binary untuk Linux |
make deploy |
Build + deploy ke server |