Improve mobile UI and add infinite scroll
This commit is contained in:
@@ -6,11 +6,14 @@ import (
|
||||
"encoding/json"
|
||||
"html/template"
|
||||
"net/http"
|
||||
"strconv"
|
||||
)
|
||||
|
||||
var tmpl *template.Template
|
||||
var basePath string
|
||||
|
||||
const defaultPageSize = 30
|
||||
|
||||
func SetTemplates(t *template.Template) { tmpl = t }
|
||||
func SetBasePath(p string) { basePath = p }
|
||||
|
||||
@@ -28,6 +31,12 @@ type pageData struct {
|
||||
DepartmentOptions []string
|
||||
OverviewJSON template.JS
|
||||
DepartmentJSON template.JS
|
||||
Page int
|
||||
PageSize int
|
||||
HasMore bool
|
||||
VisibleCount int
|
||||
TotalFiltered int
|
||||
NextPage int
|
||||
}
|
||||
|
||||
func toJS(v interface{}) template.JS {
|
||||
@@ -35,33 +44,36 @@ func toJS(v interface{}) template.JS {
|
||||
return template.JS(b)
|
||||
}
|
||||
|
||||
func Index(w http.ResponseWriter, r *http.Request) {
|
||||
func parsePage(q string) int {
|
||||
p, err := strconv.Atoi(q)
|
||||
if err != nil || p < 1 {
|
||||
return 1
|
||||
}
|
||||
return p
|
||||
}
|
||||
|
||||
func buildArrivalData(r *http.Request) (pageData, int, error) {
|
||||
username := auth.Username(r)
|
||||
mcuID := auth.SelectedProjectID(r)
|
||||
if mcuID == 0 {
|
||||
http.Redirect(w, r, basePath+"/projects", http.StatusSeeOther)
|
||||
return
|
||||
return pageData{}, http.StatusSeeOther, nil
|
||||
}
|
||||
project, ok, err := projects.GetUserProject(username, mcuID)
|
||||
if err != nil {
|
||||
http.Error(w, "query error", http.StatusInternalServerError)
|
||||
return
|
||||
return pageData{}, http.StatusInternalServerError, err
|
||||
}
|
||||
if !ok {
|
||||
http.Redirect(w, r, basePath+"/projects", http.StatusSeeOther)
|
||||
return
|
||||
return pageData{}, http.StatusSeeOther, nil
|
||||
}
|
||||
|
||||
dates, err := GetArrivalDates(mcuID)
|
||||
if err != nil {
|
||||
http.Error(w, "query error", http.StatusInternalServerError)
|
||||
return
|
||||
return pageData{}, http.StatusInternalServerError, err
|
||||
}
|
||||
selectedDate := activeDateOrLatest(dates, r.URL.Query().Get("date"), project.StartDate)
|
||||
rows, err := GetArrivalRows(mcuID, selectedDate)
|
||||
if err != nil {
|
||||
http.Error(w, "query error", http.StatusInternalServerError)
|
||||
return
|
||||
return pageData{}, http.StatusInternalServerError, err
|
||||
}
|
||||
summary, deptStats := BuildArrivalStats(rows)
|
||||
filteredRows := FilterArrivalRows(rows, r.URL.Query().Get("search"), r.URL.Query().Get("dept"))
|
||||
@@ -69,14 +81,12 @@ func Index(w http.ResponseWriter, r *http.Request) {
|
||||
|
||||
stationMap, err := GetStationProgress(mcuID, selectedDate)
|
||||
if err != nil {
|
||||
http.Error(w, "query error", http.StatusInternalServerError)
|
||||
return
|
||||
return pageData{}, http.StatusInternalServerError, err
|
||||
}
|
||||
for i := range filteredRows {
|
||||
filteredRows[i].Stations = stationMap[filteredRows[i].PreregisterID]
|
||||
}
|
||||
|
||||
// Chart 1: double donut — inner: checked-in vs pending, outer: total per posisi/dept
|
||||
outerDepts := []map[string]any{}
|
||||
for _, d := range deptStats {
|
||||
if d.Total > 0 {
|
||||
@@ -89,7 +99,6 @@ func Index(w http.ResponseWriter, r *http.Request) {
|
||||
"depts": outerDepts,
|
||||
}
|
||||
|
||||
// Chart 2: per-station patient count bar chart
|
||||
stationStats := BuildStationChart(stationMap)
|
||||
stationLabels := make([]string, len(stationStats))
|
||||
stationCounts := make([]int, len(stationStats))
|
||||
@@ -102,12 +111,14 @@ func Index(w http.ResponseWriter, r *http.Request) {
|
||||
"counts": stationCounts,
|
||||
}
|
||||
|
||||
t := tmpl
|
||||
if t == nil {
|
||||
http.Error(w, "template not ready", http.StatusInternalServerError)
|
||||
return
|
||||
page := parsePage(r.URL.Query().Get("page"))
|
||||
pagedRows, hasMore := PaginateArrivalRows(filteredRows, page, defaultPageSize)
|
||||
visibleCount := page * defaultPageSize
|
||||
if visibleCount > len(filteredRows) {
|
||||
visibleCount = len(filteredRows)
|
||||
}
|
||||
data := pageData{
|
||||
|
||||
return pageData{
|
||||
Username: username,
|
||||
CurrentProject: project,
|
||||
Date: selectedDate,
|
||||
@@ -115,14 +126,61 @@ func Index(w http.ResponseWriter, r *http.Request) {
|
||||
Search: r.URL.Query().Get("search"),
|
||||
Department: r.URL.Query().Get("dept"),
|
||||
Rows: rows,
|
||||
FilteredRows: filteredRows,
|
||||
FilteredRows: pagedRows,
|
||||
Summary: summary,
|
||||
Departments: deptStats,
|
||||
DepartmentOptions: deptOptions,
|
||||
OverviewJSON: toJS(overview),
|
||||
DepartmentJSON: toJS(stationChart),
|
||||
Page: page,
|
||||
PageSize: defaultPageSize,
|
||||
HasMore: hasMore,
|
||||
VisibleCount: visibleCount,
|
||||
TotalFiltered: len(filteredRows),
|
||||
NextPage: page + 1,
|
||||
}, http.StatusOK, nil
|
||||
}
|
||||
|
||||
func Index(w http.ResponseWriter, r *http.Request) {
|
||||
t := tmpl
|
||||
if t == nil {
|
||||
http.Error(w, "template not ready", http.StatusInternalServerError)
|
||||
return
|
||||
}
|
||||
|
||||
data, status, err := buildArrivalData(r)
|
||||
if err != nil {
|
||||
http.Error(w, "query error", status)
|
||||
return
|
||||
}
|
||||
if status == http.StatusSeeOther {
|
||||
http.Redirect(w, r, basePath+"/projects", http.StatusSeeOther)
|
||||
return
|
||||
}
|
||||
|
||||
if err := t.ExecuteTemplate(w, "base", data); err != nil {
|
||||
http.Error(w, "template error", http.StatusInternalServerError)
|
||||
}
|
||||
}
|
||||
|
||||
func List(w http.ResponseWriter, r *http.Request) {
|
||||
t := tmpl
|
||||
if t == nil {
|
||||
http.Error(w, "template not ready", http.StatusInternalServerError)
|
||||
return
|
||||
}
|
||||
|
||||
data, status, err := buildArrivalData(r)
|
||||
if err != nil {
|
||||
http.Error(w, "query error", status)
|
||||
return
|
||||
}
|
||||
if status == http.StatusSeeOther {
|
||||
http.Error(w, "unauthorized", http.StatusUnauthorized)
|
||||
return
|
||||
}
|
||||
|
||||
if err := t.ExecuteTemplate(w, "arrival-list-chunk", data); err != nil {
|
||||
http.Error(w, "template error", http.StatusInternalServerError)
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user