288 lines
7.8 KiB
Go
288 lines
7.8 KiB
Go
package dashboard
|
|
|
|
import (
|
|
"cpone-dashboard/menu/auth"
|
|
"cpone-dashboard/menu/projects"
|
|
"embed"
|
|
"encoding/json"
|
|
"html/template"
|
|
"log"
|
|
"net/http"
|
|
"strconv"
|
|
"time"
|
|
)
|
|
|
|
var templateFS *embed.FS
|
|
var funcMap template.FuncMap
|
|
|
|
type StationsPartial struct {
|
|
Rows []StationRow
|
|
IsLive bool
|
|
}
|
|
|
|
type ArrivalsPartial struct {
|
|
Rows []ArrivalRow
|
|
IsLive bool
|
|
}
|
|
|
|
type PatientsPartial struct {
|
|
Patients []PatientDetail
|
|
IsRange bool
|
|
}
|
|
|
|
type PageData struct {
|
|
Username string
|
|
McuID int
|
|
Project ProjectInfo
|
|
CurrentProject projects.ProjectItem
|
|
AvailableDates []string
|
|
Mode string
|
|
DateFrom string
|
|
DateTo string
|
|
IsRange bool
|
|
IsLive bool
|
|
KPI KPIData
|
|
TAT TATData
|
|
Stations []StationRow
|
|
Arrivals []ArrivalRow
|
|
TATChart template.JS
|
|
TrendChart template.JS
|
|
}
|
|
|
|
var basePath string
|
|
|
|
func SetTemplateFS(fs *embed.FS) { templateFS = fs }
|
|
func SetTemplateFuncs(fm template.FuncMap) { funcMap = fm }
|
|
func SetBasePath(p string) { basePath = p }
|
|
|
|
func parse(files ...string) *template.Template {
|
|
return template.Must(template.New("").Funcs(funcMap).ParseFS(templateFS, files...))
|
|
}
|
|
|
|
func defaultDailyDate(project ProjectInfo) string {
|
|
today := time.Now().Format("2006-01-02")
|
|
if project.StartDate == "" {
|
|
return today
|
|
}
|
|
if project.EndDate != "" && today > project.EndDate {
|
|
return project.StartDate
|
|
}
|
|
if today < project.StartDate {
|
|
return project.StartDate
|
|
}
|
|
return today
|
|
}
|
|
|
|
func containsDate(dates []string, target string) bool {
|
|
for _, d := range dates {
|
|
if d == target {
|
|
return true
|
|
}
|
|
}
|
|
return false
|
|
}
|
|
|
|
func activeDateRange(r *http.Request, project ProjectInfo, availableDates []string) (mode, from, to string) {
|
|
mode = "daily"
|
|
reqDate := r.URL.Query().Get("date")
|
|
|
|
switch {
|
|
case reqDate != "" && containsDate(availableDates, reqDate):
|
|
from = reqDate
|
|
case containsDate(availableDates, time.Now().Format("2006-01-02")):
|
|
from = time.Now().Format("2006-01-02")
|
|
case len(availableDates) > 0:
|
|
from = availableDates[0]
|
|
default:
|
|
from = defaultDailyDate(project)
|
|
}
|
|
to = from
|
|
return
|
|
}
|
|
|
|
func activeMcuID(r *http.Request) int {
|
|
if id, _ := strconv.Atoi(r.URL.Query().Get("mcu_id")); id > 0 {
|
|
return id
|
|
}
|
|
return auth.SelectedProjectID(r)
|
|
}
|
|
|
|
func toJS(v interface{}) template.JS {
|
|
b, _ := json.Marshal(v)
|
|
return template.JS(b)
|
|
}
|
|
|
|
func Index(w http.ResponseWriter, r *http.Request) {
|
|
mcuID := activeMcuID(r)
|
|
username := auth.Username(r)
|
|
|
|
if mcuID == 0 {
|
|
http.Redirect(w, r, basePath+"/projects", http.StatusSeeOther)
|
|
return
|
|
}
|
|
|
|
ok, err := projects.HasAccess(username, mcuID)
|
|
if err != nil {
|
|
log.Printf("[dashboard] HasAccess error: %v", err)
|
|
http.Error(w, "query error", http.StatusInternalServerError)
|
|
return
|
|
}
|
|
if !ok {
|
|
http.Redirect(w, r, basePath+"/projects", http.StatusSeeOther)
|
|
return
|
|
}
|
|
|
|
project, err := GetProject(mcuID)
|
|
if err != nil {
|
|
log.Printf("[dashboard] GetActiveProject error: %v", err)
|
|
http.Error(w, "query error", http.StatusInternalServerError)
|
|
return
|
|
}
|
|
currentProject := projects.ProjectItem{}
|
|
if item, ok, err := projects.GetUserProject(username, mcuID); err != nil {
|
|
log.Printf("[dashboard] GetUserProject error: %v", err)
|
|
} else if ok {
|
|
currentProject = item
|
|
auth.SetSelectedProject(w, mcuID)
|
|
}
|
|
availableDates, err := GetCheckinDates(project.McuID)
|
|
if err != nil {
|
|
log.Printf("[dashboard] GetCheckinDates error: %v", err)
|
|
}
|
|
mode, dateFrom, dateTo := activeDateRange(r, project, availableDates)
|
|
isRange := dateFrom != dateTo
|
|
|
|
kpi, err := GetKPI(project.McuID, dateFrom, dateTo)
|
|
if err != nil {
|
|
log.Printf("[dashboard] GetKPI error: %v", err)
|
|
}
|
|
tat, err := GetTAT(project.McuID, dateFrom, dateTo)
|
|
if err != nil {
|
|
log.Printf("[dashboard] GetTAT error: %v", err)
|
|
}
|
|
stations, err := GetStations(project.McuID, dateFrom, dateTo)
|
|
if err != nil {
|
|
log.Printf("[dashboard] GetStations error: %v", err)
|
|
}
|
|
arrivals, err := GetArrivals(project.McuID, dateFrom, dateTo, 8)
|
|
if err != nil {
|
|
log.Printf("[dashboard] GetArrivals error: %v", err)
|
|
}
|
|
hourlyTAT, err := GetPeriodTAT(project.McuID, dateFrom, dateTo, isRange)
|
|
if err != nil {
|
|
log.Printf("[dashboard] GetPeriodTAT error: %v", err)
|
|
}
|
|
trend, err := GetPeriodTrend(project.McuID, dateFrom, dateTo, isRange)
|
|
if err != nil {
|
|
log.Printf("[dashboard] GetPeriodTrend error: %v", err)
|
|
}
|
|
|
|
// Build chart JSON payloads
|
|
tatLabels, tatValues := []string{}, []float64{}
|
|
for _, p := range hourlyTAT {
|
|
tatLabels = append(tatLabels, p.Label)
|
|
tatValues = append(tatValues, p.Value)
|
|
}
|
|
trendLabels, trendCI, trendCO := []string{}, []int{}, []int{}
|
|
for _, p := range trend {
|
|
trendLabels = append(trendLabels, p.Label)
|
|
trendCI = append(trendCI, p.CheckedIn)
|
|
trendCO = append(trendCO, p.CheckedOut)
|
|
}
|
|
|
|
today := time.Now().Format("2006-01-02")
|
|
data := PageData{
|
|
Username: username,
|
|
McuID: project.McuID,
|
|
Project: project,
|
|
CurrentProject: currentProject,
|
|
AvailableDates: availableDates,
|
|
Mode: mode,
|
|
DateFrom: dateFrom,
|
|
DateTo: dateTo,
|
|
IsRange: isRange,
|
|
IsLive: mode == "daily" && dateFrom == today,
|
|
KPI: kpi,
|
|
TAT: tat,
|
|
Stations: stations,
|
|
Arrivals: arrivals,
|
|
TATChart: toJS(map[string]interface{}{
|
|
"labels": tatLabels,
|
|
"values": tatValues,
|
|
}),
|
|
TrendChart: toJS(map[string]interface{}{
|
|
"labels": trendLabels,
|
|
"checkedIn": trendCI,
|
|
"checkedOut": trendCO,
|
|
}),
|
|
}
|
|
|
|
t := parse("templates/layout/base.html", "templates/dashboard/index.html")
|
|
if err := t.ExecuteTemplate(w, "base", data); err != nil {
|
|
log.Printf("[dashboard] template error: %v", err)
|
|
}
|
|
}
|
|
|
|
func Patients(w http.ResponseWriter, r *http.Request) {
|
|
project, err := GetProject(activeMcuID(r))
|
|
if err != nil {
|
|
http.Error(w, "query error", http.StatusInternalServerError)
|
|
return
|
|
}
|
|
availableDates, _ := GetCheckinDates(project.McuID)
|
|
mode, dateFrom, dateTo := activeDateRange(r, project, availableDates)
|
|
patients, err := GetAllPatients(project.McuID, dateFrom, dateTo)
|
|
if err != nil {
|
|
log.Printf("[dashboard] GetAllPatients error: %v", err)
|
|
http.Error(w, "query error", http.StatusInternalServerError)
|
|
return
|
|
}
|
|
data := PatientsPartial{
|
|
Patients: patients,
|
|
IsRange: mode != "daily" || dateFrom != dateTo,
|
|
}
|
|
t := parse("templates/dashboard/partials/patients.html")
|
|
if err := t.ExecuteTemplate(w, "patients", data); err != nil {
|
|
log.Printf("[dashboard] patients template error: %v", err)
|
|
}
|
|
}
|
|
|
|
func KPI(w http.ResponseWriter, r *http.Request) {
|
|
project, _ := GetProject(activeMcuID(r))
|
|
availableDates, _ := GetCheckinDates(project.McuID)
|
|
_, from, to := activeDateRange(r, project, availableDates)
|
|
data, err := GetKPI(project.McuID, from, to)
|
|
if err != nil {
|
|
http.Error(w, "query error", http.StatusInternalServerError)
|
|
return
|
|
}
|
|
t := parse("templates/dashboard/partials/kpi.html")
|
|
t.ExecuteTemplate(w, "kpi", data)
|
|
}
|
|
|
|
func Stations(w http.ResponseWriter, r *http.Request) {
|
|
project, _ := GetProject(activeMcuID(r))
|
|
availableDates, _ := GetCheckinDates(project.McuID)
|
|
_, from, to := activeDateRange(r, project, availableDates)
|
|
rows, err := GetStations(project.McuID, from, to)
|
|
if err != nil {
|
|
http.Error(w, "query error", http.StatusInternalServerError)
|
|
return
|
|
}
|
|
t := parse("templates/dashboard/partials/stations.html")
|
|
t.ExecuteTemplate(w, "stations", StationsPartial{Rows: rows, IsLive: from == time.Now().Format("2006-01-02")})
|
|
}
|
|
|
|
func Arrivals(w http.ResponseWriter, r *http.Request) {
|
|
project, _ := GetProject(activeMcuID(r))
|
|
availableDates, _ := GetCheckinDates(project.McuID)
|
|
_, from, to := activeDateRange(r, project, availableDates)
|
|
rows, err := GetArrivals(project.McuID, from, to, 8)
|
|
if err != nil {
|
|
http.Error(w, "query error", http.StatusInternalServerError)
|
|
return
|
|
}
|
|
t := parse("templates/dashboard/partials/arrivals.html")
|
|
t.ExecuteTemplate(w, "arrivals", rows)
|
|
}
|