You've already forked golang-base-project
Added a user sign up chart to admin dashboard
This commit is contained in:
5
dist/templates/admin.html
vendored
5
dist/templates/admin.html
vendored
@ -3,7 +3,12 @@
|
||||
<main class="flex-shrink-0">
|
||||
<div class="container">
|
||||
<h1 class="mt-5">{{ call .Trans "Admin Dashboard" }}</h1>
|
||||
|
||||
{{ template "messages.html" . }}
|
||||
|
||||
<p>{{ call .Trans "You now have an authenticated session, feel free to log out using the link in the navbar above." }}</p>
|
||||
<p>{{ call .Trans "Below is a chart showing the number of user sign ups to this website per month." }}</p>
|
||||
{{ template "chart.html" .Chart }}
|
||||
</div>
|
||||
</main>
|
||||
|
||||
|
54
dist/templates/chart.html
vendored
Normal file
54
dist/templates/chart.html
vendored
Normal file
@ -0,0 +1,54 @@
|
||||
<svg version="1.2" xmlns="http://www.w3.org/2000/svg" class="graph" aria-labelledby="title" role="img" viewBox="0 0 800 500"
|
||||
preserveAspectRatio="xMinYMin meet">
|
||||
<title id="title">{{ .Title }}</title>
|
||||
<g class="grid x-grid" id="xGrid">
|
||||
<line x1="90" x2="90" y1="5" y2="371"></line>
|
||||
</g>
|
||||
<g class="grid y-grid" id="yGrid">
|
||||
<line x1="90" x2="705" y1="370" y2="370"></line>
|
||||
</g>
|
||||
<g class="labels x-labels">
|
||||
{{ range $xvalue := .XValues }}
|
||||
<text x="{{ $xvalue.X }}" y="{{ $xvalue.Y }}">{{ $xvalue.Value }}</text>
|
||||
{{ end }}
|
||||
<text x="400" y="420" class="label-title">{{ .XLabel }}</text>
|
||||
</g>
|
||||
<g class="labels y-labels">
|
||||
{{ range $yvalue := .YValues }}
|
||||
<text x="{{ $yvalue.X }}" y="{{ $yvalue.Y }}">{{ $yvalue.Value }}</text>
|
||||
{{ end }}
|
||||
<text x="50" y="200" class="label-title">{{ .YLabel }}</text>
|
||||
</g>
|
||||
<polyline fill="none" stroke="#0074d9" stroke-width="2" points="{{ .Points }}"/>
|
||||
</svg>
|
||||
<style>
|
||||
.graph .labels.x-labels {
|
||||
text-anchor: middle;
|
||||
}
|
||||
|
||||
.graph .labels.y-labels {
|
||||
text-anchor: end;
|
||||
}
|
||||
|
||||
.graph {
|
||||
width: 100%;
|
||||
height: auto;
|
||||
}
|
||||
|
||||
.graph .grid {
|
||||
stroke: #ccc;
|
||||
stroke-dasharray: 0;
|
||||
stroke-width: 1;
|
||||
}
|
||||
|
||||
.labels {
|
||||
font-size: 10px;
|
||||
}
|
||||
|
||||
.label-title {
|
||||
font-weight: bold;
|
||||
text-transform: uppercase;
|
||||
font-size: 12px;
|
||||
fill: black;
|
||||
}
|
||||
</style>
|
After Width: | Height: | Size: 1.5 KiB |
161
routes/admin.go
161
routes/admin.go
@ -1,13 +1,172 @@
|
||||
package routes
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"github.com/gin-gonic/gin"
|
||||
"github.com/uberswe/golang-base-project/models"
|
||||
"gorm.io/gorm"
|
||||
"log"
|
||||
"math"
|
||||
"net/http"
|
||||
"sort"
|
||||
"time"
|
||||
)
|
||||
|
||||
type AdminData struct {
|
||||
PageData
|
||||
Chart Chart
|
||||
}
|
||||
|
||||
type Chart struct {
|
||||
Title string
|
||||
XLabel string
|
||||
YLabel string
|
||||
XValues []ChartValue
|
||||
YValues []ChartValue
|
||||
Points string
|
||||
}
|
||||
|
||||
type ChartValue struct {
|
||||
Value string
|
||||
X int
|
||||
Y int
|
||||
}
|
||||
|
||||
type MonthlySignUps struct {
|
||||
Year int
|
||||
Month int
|
||||
Count int
|
||||
}
|
||||
|
||||
// Admin renders the admin dashboard
|
||||
func (controller Controller) Admin(c *gin.Context) {
|
||||
pd := controller.DefaultPageData(c)
|
||||
pd.Title = pd.Trans("Admin")
|
||||
c.HTML(http.StatusOK, "admin.html", pd)
|
||||
|
||||
ad := AdminData{
|
||||
PageData: pd,
|
||||
Chart: Chart{
|
||||
Title: "Monthly User Sign Ups",
|
||||
XLabel: "Month",
|
||||
YLabel: "Users",
|
||||
},
|
||||
}
|
||||
|
||||
var msu []MonthlySignUps
|
||||
|
||||
res := controller.db.Model(&models.User{}).
|
||||
Select("YEAR(created_at) as year, MONTH(created_at) AS month, COUNT(*) AS count").
|
||||
Where("created_at >= CURDATE() - INTERVAL 1 YEAR").
|
||||
Group("YEAR(created_at), MONTH(created_at)").
|
||||
Find(&msu)
|
||||
|
||||
if res.Error != nil && res.Error != gorm.ErrRecordNotFound {
|
||||
pd.Messages = append(pd.Messages, Message{
|
||||
Type: "error",
|
||||
Content: "Something went wrong while fetching user data",
|
||||
})
|
||||
log.Println(res.Error)
|
||||
c.HTML(http.StatusInternalServerError, "admin.html", ad)
|
||||
return
|
||||
}
|
||||
|
||||
// TODO make this into a graph package or find a go package to replace this manual code
|
||||
// Add 0 values
|
||||
y, m, _ := time.Now().AddDate(-1, 0, 0).Date()
|
||||
ny, nm, _ := time.Now().Date()
|
||||
for y < ny || m < nm {
|
||||
found := false
|
||||
for _, ms := range msu {
|
||||
if ms.Year == y && ms.Month == int(m) {
|
||||
found = true
|
||||
}
|
||||
}
|
||||
if !found {
|
||||
msu = append(msu, MonthlySignUps{
|
||||
Year: y,
|
||||
Month: int(m),
|
||||
Count: 0,
|
||||
})
|
||||
}
|
||||
m++
|
||||
if m > 12 {
|
||||
m = 1
|
||||
y++
|
||||
}
|
||||
}
|
||||
|
||||
// Sort our values so that the graph shows left to right going from earliest to latest date
|
||||
sort.Slice(msu, func(i, j int) bool {
|
||||
if msu[i].Year < msu[j].Year {
|
||||
return true
|
||||
} else if msu[i].Year == msu[j].Year && msu[i].Month < msu[j].Month {
|
||||
return true
|
||||
}
|
||||
return false
|
||||
})
|
||||
|
||||
rightAdjust := 50
|
||||
base := 350
|
||||
lowest := math.MaxInt32
|
||||
highest := 0
|
||||
count := len(msu)
|
||||
for _, m := range msu {
|
||||
if lowest > m.Count {
|
||||
lowest = m.Count
|
||||
}
|
||||
if highest < m.Count {
|
||||
highest = m.Count
|
||||
}
|
||||
}
|
||||
diff := highest - lowest
|
||||
if diff < 5 {
|
||||
diff = 5
|
||||
}
|
||||
// We multiply counts by this ratio after subtracting our lowest
|
||||
ratio := base / diff
|
||||
|
||||
// y line is 90 x to 705 x at 370 y
|
||||
|
||||
maxX := 705
|
||||
minX := 90
|
||||
|
||||
seg := (maxX - minX) / count
|
||||
|
||||
var yValues []ChartValue
|
||||
for i, m := range msu {
|
||||
yValues = append(yValues, ChartValue{
|
||||
Value: fmt.Sprintf("%d-%d", m.Year, m.Month),
|
||||
X: rightAdjust + 20 + minX + (i * seg),
|
||||
Y: base + 40,
|
||||
})
|
||||
}
|
||||
|
||||
// x line is 5 y to 371 y on 90 x
|
||||
|
||||
var xValues []ChartValue
|
||||
seg2 := diff / 5
|
||||
|
||||
for i := 0; i <= 5; i++ {
|
||||
xValues = append(xValues, ChartValue{
|
||||
Value: fmt.Sprintf("%d", lowest+(seg2*i)+1),
|
||||
X: minX - 20,
|
||||
Y: (base - 10) - (i * seg2 * ratio),
|
||||
})
|
||||
}
|
||||
|
||||
points := ""
|
||||
for i, m := range msu {
|
||||
if i > 0 {
|
||||
points += " "
|
||||
}
|
||||
points += fmt.Sprintf("%d,%d", rightAdjust+minX+(i*seg), (base+20)-(m.Count*ratio))
|
||||
}
|
||||
|
||||
ad.Chart.XValues = xValues
|
||||
ad.Chart.YValues = yValues
|
||||
ad.Chart.Points = points
|
||||
|
||||
// The chart is inverted so we need to subtract the base value to our calculated values
|
||||
|
||||
c.HTML(http.StatusOK, "admin.html", ad)
|
||||
}
|
||||
|
Reference in New Issue
Block a user