package progress import ( "cpone-dashboard/db" "strings" ) type ProgressRow struct { PreregisterID int NIP string Name string Posisi string PacketCode string PacketName string Tests string ResumeStatus string Validated string Published string } type ProgressSummary struct { Total int Validated int Published int } func GetProgressRows(mcuID int) ([]ProgressRow, error) { rows, err := db.DB.Query(` SELECT mp.Mcu_PatientPreregisterID, COALESCE(NULLIF(TRIM(mp.Mcu_PatientNIP), ''), '-') AS nip, COALESCE(NULLIF(TRIM(mp.Mcu_PatientName), ''), '-') AS name, COALESCE( NULLIF(TRIM(mp.Mcu_PatientDepartment), ''), NULLIF(TRIM(mp.Mcu_PatientDivision), ''), NULLIF(TRIM(mp.Mcu_PatientPosisi), ''), '-' ) AS posisi, COALESCE(( SELECT GROUP_CONCAT(DISTINCT COALESCE(NULLIF(TRIM(pp.Mcu_PatientPacketCode), ''), pp.Mcu_PatientPacketName) ORDER BY COALESCE(NULLIF(TRIM(pp.Mcu_PatientPacketCode), ''), pp.Mcu_PatientPacketName) SEPARATOR ',') FROM mcu_patient_packet pp WHERE pp.Mcu_PatientPacketPreregisterID = mp.Mcu_PatientPreregisterID ), '') AS packet_code, COALESCE(( SELECT GROUP_CONCAT(DISTINCT pp.Mcu_PatientPacketName ORDER BY pp.Mcu_PatientPacketName SEPARATOR ', ') FROM mcu_patient_packet pp WHERE pp.Mcu_PatientPacketPreregisterID = mp.Mcu_PatientPreregisterID ), '') AS packet_name, COALESCE(NULLIF(TRIM(mp.Mcu_PatientPemeriksaan), ''), '') AS tests, COALESCE(rs.Mcu_PatientResumeStatusStatus, '') AS resume_status, COALESCE(rs.Mcu_PatientResumeStatusValidated, 'N') AS validated, COALESCE(rs.Mcu_PatientResumeStatusPublished, 'N') AS published FROM mcu_patient mp LEFT JOIN mcu_patient_resume_status rs ON rs.Mcu_PatientResumeStatusPreregisterID = mp.Mcu_PatientPreregisterID AND rs.Mcu_PatientResumeStatusMcuID = ? WHERE mp.Mcu_PatientMcuID = ? AND mp.Mcu_PatientIsActive = 'Y' ORDER BY rs.Mcu_PatientResumeStatusValidated DESC, mp.Mcu_PatientName ASC `, mcuID, mcuID) if err != nil { return nil, err } defer rows.Close() var result []ProgressRow for rows.Next() { var r ProgressRow if err := rows.Scan(&r.PreregisterID, &r.NIP, &r.Name, &r.Posisi, &r.PacketCode, &r.PacketName, &r.Tests, &r.ResumeStatus, &r.Validated, &r.Published); err != nil { continue } result = append(result, r) } return result, nil } func BuildProgressSummary(rows []ProgressRow) ProgressSummary { s := ProgressSummary{Total: len(rows)} for _, r := range rows { if r.Validated == "Y" { s.Validated++ } if r.Published == "Y" { s.Published++ } } return s } func FilterProgressRows(rows []ProgressRow, search, status, packetCode string) []ProgressRow { search = strings.ToLower(strings.TrimSpace(search)) status = strings.TrimSpace(status) packetCode = strings.TrimSpace(packetCode) if search == "" && status == "" && packetCode == "" { return rows } out := make([]ProgressRow, 0, len(rows)) for _, r := range rows { if packetCode != "" && r.PacketCode != packetCode { continue } switch status { case "validated": if r.Validated != "Y" { continue } case "published": if r.Published != "Y" { continue } case "not_validated": if r.Validated == "Y" { continue } case "not_published": if r.Published == "Y" { continue } } if search != "" { hay := strings.ToLower(r.Name + " " + r.NIP + " " + r.Posisi + " " + r.PacketCode + " " + r.PacketName + " " + r.Tests) if !strings.Contains(hay, search) { continue } } out = append(out, r) } return out } type PacketOption struct { Code string Name string } func UniquePackets(rows []ProgressRow) []PacketOption { seen := map[string]struct{}{} out := []PacketOption{} for _, r := range rows { code := strings.TrimSpace(r.PacketCode) if code == "" { continue } if _, ok := seen[code]; ok { continue } seen[code] = struct{}{} name := strings.TrimSpace(r.PacketName) if name == "" { name = code } out = append(out, PacketOption{Code: code, Name: name}) } // Keep deterministic order without new imports. for i := 0; i < len(out); i++ { for j := i + 1; j < len(out); j++ { if out[j].Code < out[i].Code { out[i], out[j] = out[j], out[i] } } } return out } func Pct(num, total int) int { if total == 0 { return 0 } return num * 100 / total } func PaginateProgressRows(rows []ProgressRow, page, pageSize int) ([]ProgressRow, bool) { if page < 1 { page = 1 } if pageSize < 1 { pageSize = 30 } start := (page - 1) * pageSize if start >= len(rows) { return []ProgressRow{}, false } end := start + pageSize if end > len(rows) { end = len(rows) } return rows[start:end], end < len(rows) }