Compare commits

..
3 Commits
Author SHA1 Message Date
A R ec2f340e13 update warning billing sign 2026-04-14 22:19:23 +07:00
AnggerRevo5 a6af3d5f3f revisi back date dan dpjp login 2026-04-13 23:00:14 +07:00
AnggerRevo5 cc6abb00ac revisi 2026-04-13 20:53:11 +07:00
11 changed files with 960 additions and 474 deletions

No files matched your search

+2 -1
View File
@@ -1,5 +1,6 @@
# Database Configuration
DB_HOST=host.docker.internal
DB_HOST=db
# DB_HOST=host.docker.internal
DB_PORT=5432
DB_USER=postgres
DB_PASSWORD=gakbikinkembung25
+1
View File
@@ -228,6 +228,7 @@ type BillingRequest struct {
Ruangan string `json:"ruangan" binding:"required"`
Kelas string `json:"kelas" binding:"required"`
Tindakan_RS []string `json:"tindakan_rs" binding:"required"`
Tanggal_Masuk string `json:"tanggal_masuk"` // ← Added: Tanggal masuk dari frontend (format: YYYY-MM-DD)
Tanggal_Keluar string `json:"tanggal_keluar"`
Billing_sign string `json:"billing_sign"`
ICD9 []string `json:"icd9"`
+2 -2
View File
@@ -68,10 +68,10 @@ func SendEmailToMultiple(to []string, subject, body string) error {
smtpPort := os.Getenv("SMTP_PORT")
if from == "" {
from = "asikmahdi@gmail.com"
from = "careit565@gmail.com"
}
if password == "" {
password = "njom rhxb prrj tuoj"
password = "gkhz bjax uamw xydf"
}
if smtpHost == "" {
smtpHost = "smtp.gmail.com"
+25 -1
View File
@@ -314,6 +314,30 @@ func DataFromFE(input models.BillingRequest) (
now := time.Now()
// Parse Tanggal_Masuk - accept multiple formats
var masukPtr *time.Time
if input.Tanggal_Masuk != "" && input.Tanggal_Masuk != "null" {
s := input.Tanggal_Masuk
var parsed time.Time
var err error
layouts := []string{time.RFC3339, "2006-01-02 15:04:05", "2006-01-02"}
for _, layout := range layouts {
parsed, err = time.Parse(layout, s)
if err == nil {
t := parsed
masukPtr = &t
break
}
}
if masukPtr == nil {
tx.Rollback()
return nil, nil, nil, nil, nil, fmt.Errorf("invalid tanggal_masuk format: %s", input.Tanggal_Masuk)
}
} else {
// Jika tidak ada input, default ke hari ini
masukPtr = &now
}
// Parse Tanggal_Keluar (frontend sends string). Accept multiple formats.
var keluarPtr *time.Time
if input.Tanggal_Keluar != "" && input.Tanggal_Keluar != "null" {
@@ -355,7 +379,7 @@ func DataFromFE(input models.BillingRequest) (
billing = models.BillingPasien{
ID_Pasien: pasien.ID_Pasien,
Cara_Bayar: input.Cara_Bayar,
Tanggal_masuk: &now,
Tanggal_masuk: masukPtr,
Tanggal_keluar: keluarPtr,
Total_Tarif_RS: input.Total_Tarif_RS,
Total_Klaim: input.Total_Klaim_BPJS, // ← Changed: Use input value instead of hardcoded 0
@@ -46,6 +46,7 @@ func GetRiwayatPasienAll(db *gorm.DB) ([]models.Riwayat_Pasien_all, error) {
if err := db.Table("\"billing_tindakan\"").
Where("\"ID_Billing\" IN ?", billingIDs).
Select("\"ID_Billing\", \"ID_Tarif_RS\" as \"Kode\"").
Order("\"ID_Billing\" ASC, \"tanggal_tindakan\" ASC").
Scan(&tindakanRows).Error; err != nil {
return nil, err
}
@@ -54,7 +55,7 @@ func GetRiwayatPasienAll(db *gorm.DB) ([]models.Riwayat_Pasien_all, error) {
tindakanMap[t.ID_Billing] = append(tindakanMap[t.ID_Billing], t.Kode)
}
// Ambil tanggal tindakan dari tabel billing_tindakan
// Ambil tanggal tindakan dari tabel billing_tindakan (ambil yang pertama aja per billing supaya konsisten)
tindakanDateMap := make(map[int]*time.Time)
var tindakanDateRows []struct {
ID_Billing int
@@ -64,12 +65,13 @@ func GetRiwayatPasienAll(db *gorm.DB) ([]models.Riwayat_Pasien_all, error) {
if err := db.Table("\"billing_tindakan\"").
Where("\"ID_Billing\" IN ?", billingIDs).
Select("\"ID_Billing\", \"tanggal_tindakan\"").
Order("\"ID_Billing\" ASC, \"tanggal_tindakan\" ASC").
Scan(&tindakanDateRows).Error; err != nil {
return nil, err
}
for _, t := range tindakanDateRows {
if t.Tanggal_Tindakan != nil {
if t.Tanggal_Tindakan != nil && tindakanDateMap[t.ID_Billing] == nil {
tindakanDateMap[t.ID_Billing] = t.Tanggal_Tindakan
}
}
@@ -262,6 +264,7 @@ func GetAllRiwayatpasien(db *gorm.DB) ([]models.Request_Admin_Inacbg, error) {
if err := db.Table("\"billing_tindakan\"").
Where("\"ID_Billing\" IN ?", billingIDs).
Select("\"ID_Billing\", \"ID_Tarif_RS\" as \"Kode\"").
Order("\"ID_Billing\" ASC, \"tanggal_tindakan\" ASC").
Scan(&tindakanRows).Error; err != nil {
return nil, err
}
+6
View File
@@ -0,0 +1,6 @@
[.ShellClassInfo]
IconResource=C:\Users\rengginang\Desktop\CAREIT_V4\frontendcareit_v4\public\assets\LOGO_CAREIT.ico,0
[ViewState]
Mode=
Vid=
FolderType=Generic
+5 -5
View File
@@ -23,8 +23,8 @@
"@capacitor/cli": "^8.0.0",
"@tailwindcss/postcss": "^4",
"@types/node": "^20",
"@types/react": "^19",
"@types/react-dom": "^19",
"@types/react": "^19.2.14",
"@types/react-dom": "^19.2.3",
"@vercel/speed-insights": "^1.3.1",
"babel-plugin-react-compiler": "1.0.0",
"cross-env": "^7.0.3",
@@ -2611,9 +2611,9 @@
}
},
"node_modules/@types/react": {
"version": "19.2.7",
"resolved": "https://registry.npmjs.org/@types/react/-/react-19.2.7.tgz",
"integrity": "sha512-MWtvHrGZLFttgeEj28VXHxpmwYbor/ATPYbBfSFZEIRK0ecCFLl2Qo55z52Hss+UV9CRN7trSeq1zbgx7YDWWg==",
"version": "19.2.14",
"resolved": "https://registry.npmjs.org/@types/react/-/react-19.2.14.tgz",
"integrity": "sha512-ilcTH/UniCkMdtexkoCN0bI7pMcJDvmQFPvuPvmEaYA/NSfFTAgdUSLAoVjaRJm7+6PvcM+q1zYOwS4wTYMF9w==",
"dev": true,
"license": "MIT",
"dependencies": {
+3 -3
View File
@@ -40,8 +40,8 @@
"@capacitor/cli": "^8.0.0",
"@tailwindcss/postcss": "^4",
"@types/node": "^20",
"@types/react": "^19",
"@types/react-dom": "^19",
"@types/react": "^19.2.14",
"@types/react-dom": "^19.2.3",
"@vercel/speed-insights": "^1.3.1",
"babel-plugin-react-compiler": "1.0.0",
"cross-env": "^7.0.3",
@@ -52,4 +52,4 @@
"tailwindcss": "^4",
"typescript": "^5"
}
}
}
@@ -441,6 +441,13 @@ const INACBG_Admin_Ruangan = ({
const totalKlaim = data.total_klaim || 0;
setTotalKlaimOriginal(totalKlaim);
console.log(`💰 Set totalKlaimOriginal: ${totalKlaim}`);
// Seed liveBillingSign dari nilai tersimpan di DB, supaya display awal sesuai dengan BE
// Akan di-overwrite otomatis oleh useEffect saat user mengubah kode INACBG
if (data.billing_sign) {
setLiveBillingSign(data.billing_sign);
console.log(`🎨 Seeded liveBillingSign from DB: ${data.billing_sign}`);
}
// DONT set totalKlaimBPJS here - biarkan useEffect calculate yang set nilai dengan benar
// Jadi jangan: setTotalKlaimBPJS(totalKlaim);
@@ -777,15 +784,15 @@ const INACBG_Admin_Ruangan = ({
const percentage = (totalTarifRS / totalKlaimBPJS) * 100;
console.log(`📊 Percentage: ${percentage.toFixed(2)}% `);
if (percentage <= 25) {
console.log("✅ Returning: Hijau (<=25%)");
return "Hijau"; // Tarif RS <=25% dari Klaim BPJS Efektif = AMAN
} else if (percentage <= 50) {
console.log("✅ Returning: Kuning (26-50%)");
return "Kuning"; // 26%-50% = PERLU PERHATIAN
if (percentage <= 70) {
console.log("✅ Returning: Hijau (<=70%)");
return "Hijau"; // Tarif RS <=70% dari Klaim BPJS Efektif = AMAN
} else if (percentage > 70 && percentage <= 99) {
console.log("✅ Returning: Kuning (71-99%)");
return "Kuning"; // 71%-99% = PERLU PERHATIAN
} else {
console.log("✅ Returning: Merah (>50%)");
return "Merah"; // >50% = WASPADA
console.log("✅ Returning: Merah (>99%)");
return "Merah"; // >99% = WASPADA
}
};
@@ -1581,9 +1588,9 @@ rounded - lg text - left transition - all
} `}
>
{liveBillingSign === "" && "Belum bisa dihitung (isi data klaim & tarif dulu)"}
{liveBillingSign === "Hijau" && "Hijau - AMAN (<= 25%)"}
{liveBillingSign === "Kuning" && "Kuning - PERHATIAN (26% - 50%)"}
{liveBillingSign === "Merah" && "Merah - WASPADA (> 50%)"}
{liveBillingSign === "Hijau" && "Hijau - AMAN (<= 70%)"}
{liveBillingSign === "Kuning" && "Kuning - PERHATIAN (71% - 99%)"}
{liveBillingSign === "Merah" && "Merah - WASPADA (>= 100%)"}
</div>
</div>
File diff suppressed because it is too large Load diff
@@ -188,17 +188,18 @@ const RiwayatBillingPasien = ({ onLogout, userRole, onEdit, selectedRuangan }: R
};
// Hitung warning sign secara dinamis berdasarkan current tarif RS vs existing klaim
// This ensures warning updates even if INACBG hasn't been input yet
// Threshold harus konsisten dengan calculateBillingSign di billing-pasien & INACBG_Admin_Ruangan:
// <=70% = Hijau, 71-99% = Kuning, >=100% = Merah
const calculateDynamicWarningSign = (totalTarifRS: number | undefined, totalKlaim: number | undefined): string => {
if (!totalTarifRS || !totalKlaim || totalTarifRS <= 0 || totalKlaim <= 0) {
return ""; // No data to calculate
}
const percentage = (totalTarifRS / totalKlaim) * 100;
if (percentage <= 25) {
if (percentage <= 70) {
return "Hijau"; // Safe
} else if (percentage <= 50) {
} else if (percentage <= 99) {
return "Kuning"; // Warning
} else {
return "Merah"; // Alert