package result import ( "cpone-dashboard/db" "strings" ) type ResultRow struct { NIP string Name string Posisi string PacketCode string PacketName string Tests string FileUrl string ReportDate string } type ResultSummary struct { Total int HasPDF int } func GetResultRows(mcuID int) ([]ResultRow, error) { rows, err := db.DB.Query(` SELECT 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(p.Published_McuDasboardFileUrl, '') AS file_url, CASE WHEN p.Published_McuDasboardFileUrl IS NOT NULL AND p.Published_McuDasboardFileUrl != '' THEN COALESCE(CAST(p.Published_McuDasboardLastUpdated AS CHAR), '') ELSE '' END AS report_date FROM mcu_patient mp LEFT JOIN published_mcu_dashboard_sync p ON p.Published_McuDasboardT_OrderHeaderID = mp.Mcu_PatientOrderID WHERE mp.Mcu_PatientMcuID = ? AND mp.Mcu_PatientIsActive = 'Y' ORDER BY (p.Published_McuDasboardFileUrl IS NOT NULL AND p.Published_McuDasboardFileUrl != '') DESC, mp.Mcu_PatientName ASC `, mcuID) if err != nil { return nil, err } defer rows.Close() var result []ResultRow for rows.Next() { var r ResultRow if err := rows.Scan(&r.NIP, &r.Name, &r.Posisi, &r.PacketCode, &r.PacketName, &r.Tests, &r.FileUrl, &r.ReportDate); err != nil { continue } result = append(result, r) } return result, rows.Err() } func BuildResultSummary(rows []ResultRow) ResultSummary { s := ResultSummary{Total: len(rows)} for _, r := range rows { if r.FileUrl != "" { s.HasPDF++ } } return s } func FilterResultRows(rows []ResultRow, search, filter, packetCode string) []ResultRow { search = strings.ToLower(strings.TrimSpace(search)) filter = strings.TrimSpace(filter) packetCode = strings.TrimSpace(packetCode) if search == "" && filter == "" && packetCode == "" { return rows } out := make([]ResultRow, 0, len(rows)) for _, r := range rows { if packetCode != "" && r.PacketCode != packetCode { continue } switch filter { case "has_pdf": if r.FileUrl == "" { continue } case "no_pdf": if r.FileUrl != "" { 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 []ResultRow) []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}) } 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 PaginateResultRows(rows []ResultRow, page, pageSize int) ([]ResultRow, bool) { if page < 1 { page = 1 } if pageSize < 1 { pageSize = 30 } start := (page - 1) * pageSize if start >= len(rows) { return []ResultRow{}, false } end := start + pageSize if end > len(rows) { end = len(rows) } return rows[start:end], end < len(rows) }