Update dashboard filters and packet views
This commit is contained in:
@@ -13,6 +13,12 @@ type StationBadge struct {
|
||||
Tone string // "success" | "warning" | "danger" | "neutral"
|
||||
}
|
||||
|
||||
type PaketStat struct {
|
||||
Name string
|
||||
Total int
|
||||
Completed int
|
||||
}
|
||||
|
||||
type StationStat struct {
|
||||
Name string
|
||||
Count int
|
||||
@@ -23,10 +29,16 @@ type ArrivalRow struct {
|
||||
NIP string
|
||||
Name string
|
||||
Department string
|
||||
Pemeriksaan string
|
||||
PaketCode string
|
||||
PaketName string
|
||||
InTime string
|
||||
Status string
|
||||
StatusTone string
|
||||
Stations []StationBadge
|
||||
DoneCount int
|
||||
ProgressCount int
|
||||
PendingCount int
|
||||
}
|
||||
|
||||
type DepartmentStat struct {
|
||||
@@ -83,6 +95,17 @@ func GetArrivalRows(mcuID int, date string) ([]ArrivalRow, error) {
|
||||
NULLIF(TRIM(mp.Mcu_PatientPosisi), ''),
|
||||
'-'
|
||||
) AS department_name,
|
||||
COALESCE(NULLIF(TRIM(mp.Mcu_PatientPemeriksaan), ''), '') AS pemeriksaan,
|
||||
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 paket_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 paket_name,
|
||||
COALESCE(DATE_FORMAT(mc.Mcu_CheckinoutInTime, '%H:%i'), '') AS in_time,
|
||||
CASE
|
||||
WHEN mc.Mcu_CheckinoutOutTime IS NOT NULL THEN 'Performed'
|
||||
@@ -119,7 +142,7 @@ func GetArrivalRows(mcuID int, date string) ([]ArrivalRow, error) {
|
||||
var result []ArrivalRow
|
||||
for rows.Next() {
|
||||
var r ArrivalRow
|
||||
if err := rows.Scan(&r.PreregisterID, &r.NIP, &r.Name, &r.Department, &r.InTime, &r.Status, &r.StatusTone); err != nil {
|
||||
if err := rows.Scan(&r.PreregisterID, &r.NIP, &r.Name, &r.Department, &r.Pemeriksaan, &r.PaketCode, &r.PaketName, &r.InTime, &r.Status, &r.StatusTone); err != nil {
|
||||
continue
|
||||
}
|
||||
if strings.TrimSpace(r.NIP) == "" {
|
||||
@@ -139,31 +162,59 @@ func GetArrivalRows(mcuID int, date string) ([]ArrivalRow, error) {
|
||||
func GetStationProgress(mcuID int, date string) (map[int][]StationBadge, error) {
|
||||
rows, err := db.DB.Query(`
|
||||
SELECT
|
||||
sp.Mcu_StationProgressPreregisterID,
|
||||
sp.Mcu_StationProgressStationName,
|
||||
rs.preregister_id,
|
||||
rs.station_name,
|
||||
CASE
|
||||
WHEN sp.Mcu_StationProgressSource = 'lab'
|
||||
AND sp.Mcu_StationProgressReceiveAt IS NOT NULL THEN 'success'
|
||||
WHEN sp.Mcu_StationProgressSource = 'nonlab'
|
||||
AND sp.Mcu_StationProgressDoneAt IS NOT NULL THEN 'success'
|
||||
WHEN sp.Mcu_StationProgressProcessAt IS NOT NULL
|
||||
OR sp.Mcu_StationProgressReceiveAt IS NOT NULL
|
||||
OR sp.Mcu_StationProgressSamplingAt IS NOT NULL THEN 'warning'
|
||||
WHEN MAX(CASE
|
||||
WHEN sp.Mcu_StationProgressSource = 'lab'
|
||||
AND sp.Mcu_StationProgressReceiveAt IS NOT NULL
|
||||
AND DATE(sp.Mcu_StationProgressReceiveAt) <= mc.Mcu_CheckinoutDate THEN 1
|
||||
WHEN sp.Mcu_StationProgressSource = 'nonlab'
|
||||
AND sp.Mcu_StationProgressProcessAt IS NOT NULL
|
||||
AND DATE(sp.Mcu_StationProgressProcessAt) <= mc.Mcu_CheckinoutDate THEN 1
|
||||
WHEN sp.Mcu_StationProgressSource = 'nonlab'
|
||||
AND sp.Mcu_StationProgressDoneAt IS NOT NULL
|
||||
AND DATE(sp.Mcu_StationProgressDoneAt) <= mc.Mcu_CheckinoutDate THEN 1
|
||||
ELSE 0
|
||||
END) = 1 THEN 'success'
|
||||
WHEN MAX(CASE
|
||||
WHEN sp.Mcu_StationProgressProcessAt IS NOT NULL
|
||||
AND DATE(sp.Mcu_StationProgressProcessAt) <= mc.Mcu_CheckinoutDate THEN 1
|
||||
WHEN sp.Mcu_StationProgressReceiveAt IS NOT NULL
|
||||
AND DATE(sp.Mcu_StationProgressReceiveAt) <= mc.Mcu_CheckinoutDate THEN 1
|
||||
WHEN sp.Mcu_StationProgressSamplingAt IS NOT NULL
|
||||
AND DATE(sp.Mcu_StationProgressSamplingAt) <= mc.Mcu_CheckinoutDate THEN 1
|
||||
ELSE 0
|
||||
END) = 1 THEN 'warning'
|
||||
ELSE 'neutral'
|
||||
END AS tone
|
||||
FROM mcu_station_progress sp
|
||||
WHERE sp.Mcu_StationProgressMcuID = ?
|
||||
AND sp.Mcu_StationProgressPreregisterID IN (
|
||||
SELECT mp.Mcu_PatientPreregisterID
|
||||
FROM mcu_patient_schedule s
|
||||
JOIN mcu_patient mp ON mp.Mcu_PatientPreregisterID = s.Mcu_PatientSchedulePreregisterID
|
||||
WHERE mp.Mcu_PatientMcuID = ?
|
||||
AND mp.Mcu_PatientIsActive = 'Y'
|
||||
AND s.Mcu_PatientScheduleDate = ?
|
||||
AND s.Mcu_PatientScheduleIsActive = 'Y'
|
||||
)
|
||||
ORDER BY sp.Mcu_StationProgressPreregisterID, sp.Mcu_StationProgressStationName
|
||||
`, mcuID, mcuID, date)
|
||||
FROM mcu_patient_required_station rs
|
||||
JOIN mcu_checkinout mc
|
||||
ON mc.Mcu_CheckinoutPreregisterID = rs.preregister_id
|
||||
AND mc.Mcu_CheckinoutMcuID = rs.mcu_id
|
||||
AND mc.Mcu_CheckinoutDate = ?
|
||||
AND mc.Mcu_CheckinoutIsActive = 'Y'
|
||||
LEFT JOIN mcu_station_progress sp
|
||||
ON sp.Mcu_StationProgressPreregisterID = rs.preregister_id
|
||||
AND sp.Mcu_StationProgressStationID = rs.sample_station_id
|
||||
AND sp.Mcu_StationProgressMcuID = rs.mcu_id
|
||||
AND (
|
||||
(sp.Mcu_StationProgressCheckinDate IS NOT NULL
|
||||
AND sp.Mcu_StationProgressCheckinDate <= mc.Mcu_CheckinoutDate)
|
||||
OR
|
||||
(sp.Mcu_StationProgressCheckinDate IS NULL
|
||||
AND (
|
||||
(sp.Mcu_StationProgressProcessAt IS NOT NULL AND DATE(sp.Mcu_StationProgressProcessAt) <= mc.Mcu_CheckinoutDate)
|
||||
OR (sp.Mcu_StationProgressDoneAt IS NOT NULL AND DATE(sp.Mcu_StationProgressDoneAt) <= mc.Mcu_CheckinoutDate)
|
||||
OR (sp.Mcu_StationProgressReceiveAt IS NOT NULL AND DATE(sp.Mcu_StationProgressReceiveAt) <= mc.Mcu_CheckinoutDate)
|
||||
OR (sp.Mcu_StationProgressSamplingAt IS NOT NULL AND DATE(sp.Mcu_StationProgressSamplingAt) <= mc.Mcu_CheckinoutDate)
|
||||
)
|
||||
)
|
||||
)
|
||||
WHERE rs.mcu_id = ?
|
||||
GROUP BY rs.preregister_id, rs.station_name
|
||||
ORDER BY rs.preregister_id, rs.station_name
|
||||
`, date, mcuID)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@@ -247,16 +298,20 @@ func BuildStationChart(stationMap map[int][]StationBadge) []StationStat {
|
||||
return stats
|
||||
}
|
||||
|
||||
func FilterArrivalRows(rows []ArrivalRow, search, dept string) []ArrivalRow {
|
||||
func FilterArrivalRows(rows []ArrivalRow, search, dept, paket string) []ArrivalRow {
|
||||
search = strings.ToLower(strings.TrimSpace(search))
|
||||
dept = strings.TrimSpace(dept)
|
||||
if search == "" && dept == "" {
|
||||
paket = strings.TrimSpace(paket)
|
||||
if search == "" && dept == "" && paket == "" {
|
||||
return rows
|
||||
}
|
||||
|
||||
out := make([]ArrivalRow, 0, len(rows))
|
||||
for _, row := range rows {
|
||||
if dept != "" && dept != "All Departments" && row.Department != dept {
|
||||
if dept != "" && row.Department != dept {
|
||||
continue
|
||||
}
|
||||
if paket != "" && row.PaketCode != paket {
|
||||
continue
|
||||
}
|
||||
if search != "" {
|
||||
@@ -270,6 +325,33 @@ func FilterArrivalRows(rows []ArrivalRow, search, dept string) []ArrivalRow {
|
||||
return out
|
||||
}
|
||||
|
||||
type PaketOption struct {
|
||||
Code string
|
||||
Name string
|
||||
}
|
||||
|
||||
func UniquePakets(rows []ArrivalRow) []PaketOption {
|
||||
seen := map[string]struct{}{}
|
||||
var out []PaketOption
|
||||
for _, row := range rows {
|
||||
code := strings.TrimSpace(row.PaketCode)
|
||||
if code == "" {
|
||||
continue
|
||||
}
|
||||
if _, ok := seen[code]; ok {
|
||||
continue
|
||||
}
|
||||
seen[code] = struct{}{}
|
||||
name := strings.TrimSpace(row.PaketName)
|
||||
if name == "" {
|
||||
name = code
|
||||
}
|
||||
out = append(out, PaketOption{Code: code, Name: name})
|
||||
}
|
||||
sort.Slice(out, func(i, j int) bool { return out[i].Code < out[j].Code })
|
||||
return out
|
||||
}
|
||||
|
||||
func PaginateArrivalRows(rows []ArrivalRow, page, pageSize int) ([]ArrivalRow, bool) {
|
||||
if page < 1 {
|
||||
page = 1
|
||||
@@ -306,6 +388,64 @@ func UniqueDepartments(rows []ArrivalRow) []string {
|
||||
return out
|
||||
}
|
||||
|
||||
func GetPaketStats(mcuID int, date string) ([]PaketStat, error) {
|
||||
rows, err := db.DB.Query(`
|
||||
SELECT
|
||||
pp.Mcu_PatientPacketName,
|
||||
COUNT(DISTINCT s.Mcu_PatientSchedulePreregisterID) AS total,
|
||||
COUNT(DISTINCT CASE
|
||||
WHEN st.total_required > 0 AND st.total_required = st.total_done
|
||||
THEN s.Mcu_PatientSchedulePreregisterID
|
||||
END) AS completed
|
||||
FROM mcu_patient_packet pp
|
||||
JOIN mcu_patient_schedule s
|
||||
ON s.Mcu_PatientSchedulePreregisterID = pp.Mcu_PatientPacketPreregisterID
|
||||
AND s.Mcu_PatientScheduleDate = ?
|
||||
AND s.Mcu_PatientScheduleIsActive = 'Y'
|
||||
JOIN mcu_patient mp
|
||||
ON mp.Mcu_PatientPreregisterID = s.Mcu_PatientSchedulePreregisterID
|
||||
AND mp.Mcu_PatientMcuID = ?
|
||||
AND mp.Mcu_PatientIsActive = 'Y'
|
||||
LEFT JOIN (
|
||||
SELECT
|
||||
rs.preregister_id,
|
||||
COUNT(rs.sample_station_id) AS total_required,
|
||||
SUM(CASE
|
||||
WHEN sp.Mcu_StationProgressSource = 'lab'
|
||||
AND sp.Mcu_StationProgressReceiveAt IS NOT NULL THEN 1
|
||||
WHEN sp.Mcu_StationProgressSource = 'nonlab'
|
||||
AND sp.Mcu_StationProgressProcessAt IS NOT NULL THEN 1
|
||||
WHEN sp.Mcu_StationProgressSource = 'nonlab'
|
||||
AND sp.Mcu_StationProgressDoneAt IS NOT NULL THEN 1
|
||||
ELSE 0
|
||||
END) AS total_done
|
||||
FROM mcu_patient_required_station rs
|
||||
LEFT JOIN mcu_station_progress sp
|
||||
ON sp.Mcu_StationProgressPreregisterID = rs.preregister_id
|
||||
AND sp.Mcu_StationProgressStationID = rs.sample_station_id
|
||||
AND sp.Mcu_StationProgressMcuID = rs.mcu_id
|
||||
WHERE rs.mcu_id = ?
|
||||
GROUP BY rs.preregister_id
|
||||
) st ON st.preregister_id = s.Mcu_PatientSchedulePreregisterID
|
||||
GROUP BY pp.Mcu_PatientPacketName
|
||||
ORDER BY total DESC
|
||||
`, date, mcuID, mcuID)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
defer rows.Close()
|
||||
|
||||
var result []PaketStat
|
||||
for rows.Next() {
|
||||
var p PaketStat
|
||||
if err := rows.Scan(&p.Name, &p.Total, &p.Completed); err != nil {
|
||||
continue
|
||||
}
|
||||
result = append(result, p)
|
||||
}
|
||||
return result, nil
|
||||
}
|
||||
|
||||
func activeDateOrLatest(dates []string, selected string, fallback string) string {
|
||||
selected = strings.TrimSpace(selected)
|
||||
if selected != "" {
|
||||
|
||||
Reference in New Issue
Block a user