Added view graphs for the individual routes.

Made some minor improvements to Cosora.
This commit is contained in:
Azareal 2018-01-05 22:47:13 +00:00
parent e259a575e9
commit d0b6559df9
10 changed files with 266 additions and 115 deletions

View File

@ -92,24 +92,25 @@ var PreRenderHooks = map[string][]func(http.ResponseWriter, *http.Request, *User
"pre_render_ban": nil, "pre_render_ban": nil,
"pre_render_ips": nil, "pre_render_ips": nil,
"pre_render_panel_dashboard": nil, "pre_render_panel_dashboard": nil,
"pre_render_panel_forums": nil, "pre_render_panel_forums": nil,
"pre_render_panel_delete_forum": nil, "pre_render_panel_delete_forum": nil,
"pre_render_panel_edit_forum": nil, "pre_render_panel_edit_forum": nil,
"pre_render_panel_analytics": nil, "pre_render_panel_analytics": nil,
"pre_render_panel_analytics_routes": nil, "pre_render_panel_analytics_routes": nil,
"pre_render_panel_settings": nil, "pre_render_panel_analytics_route_views": nil,
"pre_render_panel_setting": nil, "pre_render_panel_settings": nil,
"pre_render_panel_word_filters": nil, "pre_render_panel_setting": nil,
"pre_render_panel_word_filters_edit": nil, "pre_render_panel_word_filters": nil,
"pre_render_panel_plugins": nil, "pre_render_panel_word_filters_edit": nil,
"pre_render_panel_users": nil, "pre_render_panel_plugins": nil,
"pre_render_panel_edit_user": nil, "pre_render_panel_users": nil,
"pre_render_panel_groups": nil, "pre_render_panel_edit_user": nil,
"pre_render_panel_edit_group": nil, "pre_render_panel_groups": nil,
"pre_render_panel_edit_group_perms": nil, "pre_render_panel_edit_group": nil,
"pre_render_panel_themes": nil, "pre_render_panel_edit_group_perms": nil,
"pre_render_panel_mod_log": nil, "pre_render_panel_themes": nil,
"pre_render_panel_mod_log": nil,
"pre_render_error": nil, // Note: This hook isn't run for a few errors whose templates are computed at startup and reused, such as InternalError. This hook is also not available in JS mode. "pre_render_error": nil, // Note: This hook isn't run for a few errors whose templates are computed at startup and reused, such as InternalError. This hook is also not available in JS mode.
"pre_render_security_error": nil, "pre_render_security_error": nil,

View File

@ -174,7 +174,17 @@ type PanelAnalyticsRoutesPage struct {
Header *HeaderVars Header *HeaderVars
Stats PanelStats Stats PanelStats
Zone string Zone string
ItemList []PanelAnalyticsRoutesItem ItemList []PanelAnalyticsRoutesItem
}
type PanelAnalyticsRoutePage struct {
Title string
CurrentUser User
Header *HeaderVars
Stats PanelStats
Zone string
Route string
PrimaryGraph PanelTimeGraph
} }
type PanelThemesPage struct { type PanelThemesPage struct {

View File

@ -51,6 +51,7 @@ var RouteMap = map[string]interface{}{
"routePanelUsersEditSubmit": routePanelUsersEditSubmit, "routePanelUsersEditSubmit": routePanelUsersEditSubmit,
"routePanelAnalyticsViews": routePanelAnalyticsViews, "routePanelAnalyticsViews": routePanelAnalyticsViews,
"routePanelAnalyticsRoutes": routePanelAnalyticsRoutes, "routePanelAnalyticsRoutes": routePanelAnalyticsRoutes,
"routePanelAnalyticsRouteViews": routePanelAnalyticsRouteViews,
"routePanelGroups": routePanelGroups, "routePanelGroups": routePanelGroups,
"routePanelGroupsEdit": routePanelGroupsEdit, "routePanelGroupsEdit": routePanelGroupsEdit,
"routePanelGroupsEditPerms": routePanelGroupsEditPerms, "routePanelGroupsEditPerms": routePanelGroupsEditPerms,
@ -116,31 +117,32 @@ var routeMapEnum = map[string]int{
"routePanelUsersEditSubmit": 33, "routePanelUsersEditSubmit": 33,
"routePanelAnalyticsViews": 34, "routePanelAnalyticsViews": 34,
"routePanelAnalyticsRoutes": 35, "routePanelAnalyticsRoutes": 35,
"routePanelGroups": 36, "routePanelAnalyticsRouteViews": 36,
"routePanelGroupsEdit": 37, "routePanelGroups": 37,
"routePanelGroupsEditPerms": 38, "routePanelGroupsEdit": 38,
"routePanelGroupsEditSubmit": 39, "routePanelGroupsEditPerms": 39,
"routePanelGroupsEditPermsSubmit": 40, "routePanelGroupsEditSubmit": 40,
"routePanelGroupsCreateSubmit": 41, "routePanelGroupsEditPermsSubmit": 41,
"routePanelBackups": 42, "routePanelGroupsCreateSubmit": 42,
"routePanelLogsMod": 43, "routePanelBackups": 43,
"routePanelDebug": 44, "routePanelLogsMod": 44,
"routePanel": 45, "routePanelDebug": 45,
"routeAccountEditCritical": 46, "routePanel": 46,
"routeAccountEditCriticalSubmit": 47, "routeAccountEditCritical": 47,
"routeAccountEditAvatar": 48, "routeAccountEditCriticalSubmit": 48,
"routeAccountEditAvatarSubmit": 49, "routeAccountEditAvatar": 49,
"routeAccountEditUsername": 50, "routeAccountEditAvatarSubmit": 50,
"routeAccountEditUsernameSubmit": 51, "routeAccountEditUsername": 51,
"routeAccountEditEmail": 52, "routeAccountEditUsernameSubmit": 52,
"routeAccountEditEmailTokenSubmit": 53, "routeAccountEditEmail": 53,
"routeProfile": 54, "routeAccountEditEmailTokenSubmit": 54,
"routeBanSubmit": 55, "routeProfile": 55,
"routeUnban": 56, "routeBanSubmit": 56,
"routeActivate": 57, "routeUnban": 57,
"routeIps": 58, "routeActivate": 58,
"routeDynamic": 59, "routeIps": 59,
"routeUploads": 60, "routeDynamic": 60,
"routeUploads": 61,
} }
var reverseRouteMapEnum = map[int]string{ var reverseRouteMapEnum = map[int]string{
0: "routeAPI", 0: "routeAPI",
@ -179,31 +181,32 @@ var reverseRouteMapEnum = map[int]string{
33: "routePanelUsersEditSubmit", 33: "routePanelUsersEditSubmit",
34: "routePanelAnalyticsViews", 34: "routePanelAnalyticsViews",
35: "routePanelAnalyticsRoutes", 35: "routePanelAnalyticsRoutes",
36: "routePanelGroups", 36: "routePanelAnalyticsRouteViews",
37: "routePanelGroupsEdit", 37: "routePanelGroups",
38: "routePanelGroupsEditPerms", 38: "routePanelGroupsEdit",
39: "routePanelGroupsEditSubmit", 39: "routePanelGroupsEditPerms",
40: "routePanelGroupsEditPermsSubmit", 40: "routePanelGroupsEditSubmit",
41: "routePanelGroupsCreateSubmit", 41: "routePanelGroupsEditPermsSubmit",
42: "routePanelBackups", 42: "routePanelGroupsCreateSubmit",
43: "routePanelLogsMod", 43: "routePanelBackups",
44: "routePanelDebug", 44: "routePanelLogsMod",
45: "routePanel", 45: "routePanelDebug",
46: "routeAccountEditCritical", 46: "routePanel",
47: "routeAccountEditCriticalSubmit", 47: "routeAccountEditCritical",
48: "routeAccountEditAvatar", 48: "routeAccountEditCriticalSubmit",
49: "routeAccountEditAvatarSubmit", 49: "routeAccountEditAvatar",
50: "routeAccountEditUsername", 50: "routeAccountEditAvatarSubmit",
51: "routeAccountEditUsernameSubmit", 51: "routeAccountEditUsername",
52: "routeAccountEditEmail", 52: "routeAccountEditUsernameSubmit",
53: "routeAccountEditEmailTokenSubmit", 53: "routeAccountEditEmail",
54: "routeProfile", 54: "routeAccountEditEmailTokenSubmit",
55: "routeBanSubmit", 55: "routeProfile",
56: "routeUnban", 56: "routeBanSubmit",
57: "routeActivate", 57: "routeUnban",
58: "routeIps", 58: "routeActivate",
59: "routeDynamic", 59: "routeIps",
60: "routeUploads", 60: "routeDynamic",
61: "routeUploads",
} }
// TODO: Stop spilling these into the package scope? // TODO: Stop spilling these into the package scope?
@ -577,14 +580,17 @@ func (router *GenRouter) ServeHTTP(w http.ResponseWriter, req *http.Request) {
case "/panel/analytics/routes/": case "/panel/analytics/routes/":
common.RouteViewCounter.Bump(35) common.RouteViewCounter.Bump(35)
err = routePanelAnalyticsRoutes(w,req,user) err = routePanelAnalyticsRoutes(w,req,user)
case "/panel/groups/": case "/panel/analytics/route/":
common.RouteViewCounter.Bump(36) common.RouteViewCounter.Bump(36)
err = routePanelAnalyticsRouteViews(w,req,user,extraData)
case "/panel/groups/":
common.RouteViewCounter.Bump(37)
err = routePanelGroups(w,req,user) err = routePanelGroups(w,req,user)
case "/panel/groups/edit/": case "/panel/groups/edit/":
common.RouteViewCounter.Bump(37) common.RouteViewCounter.Bump(38)
err = routePanelGroupsEdit(w,req,user,extraData) err = routePanelGroupsEdit(w,req,user,extraData)
case "/panel/groups/edit/perms/": case "/panel/groups/edit/perms/":
common.RouteViewCounter.Bump(38) common.RouteViewCounter.Bump(39)
err = routePanelGroupsEditPerms(w,req,user,extraData) err = routePanelGroupsEditPerms(w,req,user,extraData)
case "/panel/groups/edit/submit/": case "/panel/groups/edit/submit/":
err = common.NoSessionMismatch(w,req,user) err = common.NoSessionMismatch(w,req,user)
@ -593,7 +599,7 @@ func (router *GenRouter) ServeHTTP(w http.ResponseWriter, req *http.Request) {
return return
} }
common.RouteViewCounter.Bump(39) common.RouteViewCounter.Bump(40)
err = routePanelGroupsEditSubmit(w,req,user,extraData) err = routePanelGroupsEditSubmit(w,req,user,extraData)
case "/panel/groups/edit/perms/submit/": case "/panel/groups/edit/perms/submit/":
err = common.NoSessionMismatch(w,req,user) err = common.NoSessionMismatch(w,req,user)
@ -602,7 +608,7 @@ func (router *GenRouter) ServeHTTP(w http.ResponseWriter, req *http.Request) {
return return
} }
common.RouteViewCounter.Bump(40) common.RouteViewCounter.Bump(41)
err = routePanelGroupsEditPermsSubmit(w,req,user,extraData) err = routePanelGroupsEditPermsSubmit(w,req,user,extraData)
case "/panel/groups/create/": case "/panel/groups/create/":
err = common.NoSessionMismatch(w,req,user) err = common.NoSessionMismatch(w,req,user)
@ -611,7 +617,7 @@ func (router *GenRouter) ServeHTTP(w http.ResponseWriter, req *http.Request) {
return return
} }
common.RouteViewCounter.Bump(41) common.RouteViewCounter.Bump(42)
err = routePanelGroupsCreateSubmit(w,req,user) err = routePanelGroupsCreateSubmit(w,req,user)
case "/panel/backups/": case "/panel/backups/":
err = common.SuperAdminOnly(w,req,user) err = common.SuperAdminOnly(w,req,user)
@ -620,10 +626,10 @@ func (router *GenRouter) ServeHTTP(w http.ResponseWriter, req *http.Request) {
return return
} }
common.RouteViewCounter.Bump(42) common.RouteViewCounter.Bump(43)
err = routePanelBackups(w,req,user,extraData) err = routePanelBackups(w,req,user,extraData)
case "/panel/logs/mod/": case "/panel/logs/mod/":
common.RouteViewCounter.Bump(43) common.RouteViewCounter.Bump(44)
err = routePanelLogsMod(w,req,user) err = routePanelLogsMod(w,req,user)
case "/panel/debug/": case "/panel/debug/":
err = common.AdminOnly(w,req,user) err = common.AdminOnly(w,req,user)
@ -632,10 +638,10 @@ func (router *GenRouter) ServeHTTP(w http.ResponseWriter, req *http.Request) {
return return
} }
common.RouteViewCounter.Bump(44) common.RouteViewCounter.Bump(45)
err = routePanelDebug(w,req,user) err = routePanelDebug(w,req,user)
default: default:
common.RouteViewCounter.Bump(45) common.RouteViewCounter.Bump(46)
err = routePanel(w,req,user) err = routePanel(w,req,user)
} }
if err != nil { if err != nil {
@ -650,7 +656,7 @@ func (router *GenRouter) ServeHTTP(w http.ResponseWriter, req *http.Request) {
return return
} }
common.RouteViewCounter.Bump(46) common.RouteViewCounter.Bump(47)
err = routeAccountEditCritical(w,req,user) err = routeAccountEditCritical(w,req,user)
case "/user/edit/critical/submit/": case "/user/edit/critical/submit/":
err = common.NoSessionMismatch(w,req,user) err = common.NoSessionMismatch(w,req,user)
@ -665,7 +671,7 @@ func (router *GenRouter) ServeHTTP(w http.ResponseWriter, req *http.Request) {
return return
} }
common.RouteViewCounter.Bump(47) common.RouteViewCounter.Bump(48)
err = routeAccountEditCriticalSubmit(w,req,user) err = routeAccountEditCriticalSubmit(w,req,user)
case "/user/edit/avatar/": case "/user/edit/avatar/":
err = common.MemberOnly(w,req,user) err = common.MemberOnly(w,req,user)
@ -674,7 +680,7 @@ func (router *GenRouter) ServeHTTP(w http.ResponseWriter, req *http.Request) {
return return
} }
common.RouteViewCounter.Bump(48) common.RouteViewCounter.Bump(49)
err = routeAccountEditAvatar(w,req,user) err = routeAccountEditAvatar(w,req,user)
case "/user/edit/avatar/submit/": case "/user/edit/avatar/submit/":
err = common.MemberOnly(w,req,user) err = common.MemberOnly(w,req,user)
@ -683,7 +689,7 @@ func (router *GenRouter) ServeHTTP(w http.ResponseWriter, req *http.Request) {
return return
} }
common.RouteViewCounter.Bump(49) common.RouteViewCounter.Bump(50)
err = routeAccountEditAvatarSubmit(w,req,user) err = routeAccountEditAvatarSubmit(w,req,user)
case "/user/edit/username/": case "/user/edit/username/":
err = common.MemberOnly(w,req,user) err = common.MemberOnly(w,req,user)
@ -692,7 +698,7 @@ func (router *GenRouter) ServeHTTP(w http.ResponseWriter, req *http.Request) {
return return
} }
common.RouteViewCounter.Bump(50) common.RouteViewCounter.Bump(51)
err = routeAccountEditUsername(w,req,user) err = routeAccountEditUsername(w,req,user)
case "/user/edit/username/submit/": case "/user/edit/username/submit/":
err = common.NoSessionMismatch(w,req,user) err = common.NoSessionMismatch(w,req,user)
@ -707,7 +713,7 @@ func (router *GenRouter) ServeHTTP(w http.ResponseWriter, req *http.Request) {
return return
} }
common.RouteViewCounter.Bump(51) common.RouteViewCounter.Bump(52)
err = routeAccountEditUsernameSubmit(w,req,user) err = routeAccountEditUsernameSubmit(w,req,user)
case "/user/edit/email/": case "/user/edit/email/":
err = common.MemberOnly(w,req,user) err = common.MemberOnly(w,req,user)
@ -716,7 +722,7 @@ func (router *GenRouter) ServeHTTP(w http.ResponseWriter, req *http.Request) {
return return
} }
common.RouteViewCounter.Bump(52) common.RouteViewCounter.Bump(53)
err = routeAccountEditEmail(w,req,user) err = routeAccountEditEmail(w,req,user)
case "/user/edit/token/": case "/user/edit/token/":
err = common.NoSessionMismatch(w,req,user) err = common.NoSessionMismatch(w,req,user)
@ -731,11 +737,11 @@ func (router *GenRouter) ServeHTTP(w http.ResponseWriter, req *http.Request) {
return return
} }
common.RouteViewCounter.Bump(53) common.RouteViewCounter.Bump(54)
err = routeAccountEditEmailTokenSubmit(w,req,user,extraData) err = routeAccountEditEmailTokenSubmit(w,req,user,extraData)
default: default:
req.URL.Path += extraData req.URL.Path += extraData
common.RouteViewCounter.Bump(54) common.RouteViewCounter.Bump(55)
err = routeProfile(w,req,user) err = routeProfile(w,req,user)
} }
if err != nil { if err != nil {
@ -756,7 +762,7 @@ func (router *GenRouter) ServeHTTP(w http.ResponseWriter, req *http.Request) {
return return
} }
common.RouteViewCounter.Bump(55) common.RouteViewCounter.Bump(56)
err = routeBanSubmit(w,req,user,extraData) err = routeBanSubmit(w,req,user,extraData)
case "/users/unban/": case "/users/unban/":
err = common.NoSessionMismatch(w,req,user) err = common.NoSessionMismatch(w,req,user)
@ -771,7 +777,7 @@ func (router *GenRouter) ServeHTTP(w http.ResponseWriter, req *http.Request) {
return return
} }
common.RouteViewCounter.Bump(56) common.RouteViewCounter.Bump(57)
err = routeUnban(w,req,user,extraData) err = routeUnban(w,req,user,extraData)
case "/users/activate/": case "/users/activate/":
err = common.NoSessionMismatch(w,req,user) err = common.NoSessionMismatch(w,req,user)
@ -786,7 +792,7 @@ func (router *GenRouter) ServeHTTP(w http.ResponseWriter, req *http.Request) {
return return
} }
common.RouteViewCounter.Bump(57) common.RouteViewCounter.Bump(58)
err = routeActivate(w,req,user,extraData) err = routeActivate(w,req,user,extraData)
case "/users/ips/": case "/users/ips/":
err = common.MemberOnly(w,req,user) err = common.MemberOnly(w,req,user)
@ -795,7 +801,7 @@ func (router *GenRouter) ServeHTTP(w http.ResponseWriter, req *http.Request) {
return return
} }
common.RouteViewCounter.Bump(58) common.RouteViewCounter.Bump(59)
err = routeIps(w,req,user) err = routeIps(w,req,user)
} }
if err != nil { if err != nil {
@ -812,7 +818,7 @@ func (router *GenRouter) ServeHTTP(w http.ResponseWriter, req *http.Request) {
common.NotFound(w,req) common.NotFound(w,req)
return return
} }
common.RouteViewCounter.Bump(60) common.RouteViewCounter.Bump(61)
req.URL.Path += extraData req.URL.Path += extraData
// TODO: Find a way to propagate errors up from this? // TODO: Find a way to propagate errors up from this?
router.UploadHandler(w,req) // TODO: Count these views router.UploadHandler(w,req) // TODO: Count these views
@ -856,7 +862,7 @@ func (router *GenRouter) ServeHTTP(w http.ResponseWriter, req *http.Request) {
router.RUnlock() router.RUnlock()
if ok { if ok {
common.RouteViewCounter.Bump(59) // TODO: Be more specific about *which* dynamic route it is common.RouteViewCounter.Bump(60) // TODO: Be more specific about *which* dynamic route it is
req.URL.Path += extraData req.URL.Path += extraData
err = handle(w,req,user) err = handle(w,req,user)
if err != nil { if err != nil {

View File

@ -510,7 +510,7 @@ func routePanelAnalyticsRoutes(w http.ResponseWriter, r *http.Request, user comm
var routeMap = make(map[string]int) var routeMap = make(map[string]int)
acc := qgen.Builder.Accumulator() acc := qgen.Builder.Accumulator()
rows, err := acc.Select("viewchunks").Columns("count, createdAt, route").Where("route != ''").DateCutoff("createdAt", 1, "day").Query() rows, err := acc.Select("viewchunks").Columns("count, route").Where("route != ''").DateCutoff("createdAt", 1, "day").Query()
if err != nil && err != ErrNoRows { if err != nil && err != ErrNoRows {
return common.InternalError(err, w, r) return common.InternalError(err, w, r)
} }
@ -518,15 +518,13 @@ func routePanelAnalyticsRoutes(w http.ResponseWriter, r *http.Request, user comm
for rows.Next() { for rows.Next() {
var count int var count int
var createdAt time.Time
var route string var route string
err := rows.Scan(&count, &createdAt, &route) err := rows.Scan(&count, &route)
if err != nil { if err != nil {
return common.InternalError(err, w, r) return common.InternalError(err, w, r)
} }
log.Print("count: ", count) log.Print("count: ", count)
log.Print("createdAt: ", createdAt)
log.Print("route: ", route) log.Print("route: ", route)
routeMap[route] += count routeMap[route] += count
} }
@ -557,6 +555,81 @@ func routePanelAnalyticsRoutes(w http.ResponseWriter, r *http.Request, user comm
return nil return nil
} }
func routePanelAnalyticsRouteViews(w http.ResponseWriter, r *http.Request, user common.User, route string) common.RouteError {
headerVars, stats, ferr := common.PanelUserCheck(w, r, &user)
if ferr != nil {
return ferr
}
headerVars.Stylesheets = append(headerVars.Stylesheets, "chartist/chartist.min.css")
headerVars.Scripts = append(headerVars.Scripts, "chartist/chartist.min.js")
var revLabelList []int64
var labelList []int64
var viewMap = make(map[int64]int64)
var currentTime = time.Now().Unix()
for i := 1; i <= 12; i++ {
var label = currentTime - int64(i*60*30)
revLabelList = append(revLabelList, label)
viewMap[label] = 0
}
for _, value := range revLabelList {
labelList = append(labelList, value)
}
var viewList []int64
log.Print("in routePanelAnalyticsRouteViews")
acc := qgen.Builder.Accumulator()
rows, err := acc.Select("viewchunks").Columns("count, createdAt").Where("route = ?").DateCutoff("createdAt", 6, "hour").Query(route)
if err != nil && err != ErrNoRows {
return common.InternalError(err, w, r)
}
defer rows.Close()
for rows.Next() {
var count int64
var createdAt time.Time
err := rows.Scan(&count, &createdAt)
if err != nil {
return common.InternalError(err, w, r)
}
log.Print("count: ", count)
log.Print("createdAt: ", createdAt)
var unixCreatedAt = createdAt.Unix()
log.Print("unixCreatedAt: ", unixCreatedAt)
for _, value := range labelList {
if unixCreatedAt > value {
viewMap[value] += count
break
}
}
}
err = rows.Err()
if err != nil {
return common.InternalError(err, w, r)
}
for _, value := range revLabelList {
viewList = append(viewList, viewMap[value])
}
graph := common.PanelTimeGraph{Series: viewList, Labels: labelList}
log.Printf("graph: %+v\n", graph)
pi := common.PanelAnalyticsRoutePage{common.GetTitlePhrase("panel-analytics"), user, headerVars, stats, "analytics", html.EscapeString(route), graph}
if common.PreRenderHooks["pre_render_panel_analytics_route_views"] != nil {
if common.RunPreRenderHook("pre_render_panel_analytics_route_views", w, r, &user, &pi) {
return nil
}
}
err = common.Templates.ExecuteTemplate(w, "panel-analytics-route-views.html", pi)
if err != nil {
return common.InternalError(err, w, r)
}
return nil
}
func routePanelSettings(w http.ResponseWriter, r *http.Request, user common.User) common.RouteError { func routePanelSettings(w http.ResponseWriter, r *http.Request, user common.User) common.RouteError {
headerVars, stats, ferr := common.PanelUserCheck(w, r, &user) headerVars, stats, ferr := common.PanelUserCheck(w, r, &user)
if ferr != nil { if ferr != nil {

View File

@ -91,6 +91,7 @@ func buildPanelRoutes() {
View("routePanelAnalyticsViews", "/panel/analytics/views/"), View("routePanelAnalyticsViews", "/panel/analytics/views/"),
View("routePanelAnalyticsRoutes", "/panel/analytics/routes/"), View("routePanelAnalyticsRoutes", "/panel/analytics/routes/"),
View("routePanelAnalyticsRouteViews", "/panel/analytics/route/", "extraData"),
View("routePanelGroups", "/panel/groups/"), View("routePanelGroups", "/panel/groups/"),
View("routePanelGroupsEdit", "/panel/groups/edit/", "extraData"), View("routePanelGroupsEdit", "/panel/groups/edit/", "extraData"),

View File

@ -1,18 +1,18 @@
{{template "header.html" . }} {{template "header.html" . }}
<div class="colstack account"> <div class="colstack account account_emails">
{{template "account-menu.html" . }} {{template "account-menu.html" . }}
<main class="colstack_right"> <main class="colstack_right">
<div class="colstack_item colstack_head rowhead"> <div class="colstack_item colstack_head rowhead">
<div class="rowitem"><h1>Emails</h1></div> <div class="rowitem"><h1>Emails</h1></div>
</div> </div>
<div class="colstack_item"> <div class="colstack_item rowlist">
<!-- TODO: Do we need this inline CSS? --> <!-- TODO: Do we need this inline CSS? -->
{{range .ItemList}} {{range .ItemList}}
<div class="rowitem" style="font-weight: normal;"> <div class="rowitem" style="font-weight: normal;">
<a style="text-transform: none;">{{.Email}}</a> <a style="text-transform: none;">{{.Email}}</a>
<span style="float: right"> <span style="float: right" class="to_right">
<span class="username">{{if .Primary}}Primary{{else}}Secondary{{end}}</span> <span class="username">{{if .Primary}}Primary{{else}}Secondary{{end}}</span>
{{if .Validated}}<span class="username" style="color: green;">Verified</span>{{else}}<a class="username" style="color: crimson;">Resend Verification Email</a>{{end}} {{if .Validated}}<span class="username" style="color: green;">Verified</span>{{else}}<a class="username" style="color: crimson;">Resend Verification Email</a>{{end}}
</span> </span>
</div> </div>
{{end}} {{end}}

View File

@ -0,0 +1,40 @@
{{template "header.html" . }}
<div class="colstack panel_stack">
{{template "panel-menu.html" . }}
<main id="panel_dashboard_right" class="colstack_right">
<div class="colstack_item colstack_head">
<div class="rowitem"><a>{{.Route}} Views</a></div>
</div>
<div id="panel_analytics_views" class="colstack_graph_holder">
<div class="ct-chart"></div>
</div>
</main>
</div>
<script>
let labels = [];
let rawLabels = [{{range .PrimaryGraph.Labels}}
{{.}},{{end}}
];
for(const i in rawLabels) {
let date = new Date(rawLabels[i]*1000);
console.log("date: ", date);
let minutes = "0" + date.getMinutes();
let label = date.getHours() + ":" + minutes.substr(-2);
console.log("label:", label);
labels.push(label);
}
labels = labels.reverse()
let seriesData = [{{range .PrimaryGraph.Series}}
{{.}},{{end}}
];
seriesData = seriesData.reverse();
Chartist.Line('.ct-chart', {
labels: labels,
series: [seriesData],
}, {
height: '250px',
});
</script>
{{template "footer.html" . }}

View File

@ -3,13 +3,13 @@
{{template "panel-menu.html" . }} {{template "panel-menu.html" . }}
<main id="panel_dashboard_right" class="colstack_right"> <main id="panel_dashboard_right" class="colstack_right">
<div class="colstack_item colstack_head"> <div class="colstack_item colstack_head">
<div class="rowitem"><a>Routes</a></div> <div class="rowitem"><a>Routes (24 hours)</a></div>
</div> </div>
<div id="panel_analytics_routes" class="colstack_item rowlist"> <div id="panel_analytics_routes" class="colstack_item rowlist">
{{range .ItemList}} {{range .ItemList}}
<div class="rowitem panel_compactrow editable_parent"> <div class="rowitem panel_compactrow editable_parent">
<a href="/panel/groups/edit/{{.Route}}" class="panel_upshift">{{.Route}}</a> <a href="/panel/analytics/route/{{.Route}}" class="panel_upshift">{{.Route}}</a>
<span class="panel_compacttext to_right">{{.Count}}</span> <span class="panel_compacttext to_right">{{.Count}} views</span>
</div> </div>
{{end}} {{end}}
</div> </div>

View File

@ -35,7 +35,7 @@
<form action="/panel/groups/create/?session={{.CurrentUser.Session}}" method="post"> <form action="/panel/groups/create/?session={{.CurrentUser.Session}}" method="post">
<div class="formrow"> <div class="formrow">
<div class="formitem formlabel"><a>Name</a></div> <div class="formitem formlabel"><a>Name</a></div>
<div class="formitem"><input name="group-name" type="text" /></div> <div class="formitem"><input name="group-name" type="text" placeholder="Administrator" /></div>
</div> </div>
<div class="formrow"> <div class="formrow">
<div class="formitem formlabel"><a>Type</a></div> <div class="formitem formlabel"><a>Type</a></div>

View File

@ -406,16 +406,15 @@ ul {
height: 27px; height: 27px;
width: 100px; width: 100px;
margin-left: 10px; margin-left: 10px;
border: none;
border-bottom: 1px solid var(--header-border-color);
outline: none;
} }
.topic_name_row input, .ip_search_input { .topic_name_row input, .ip_search_input {
width: 100%; width: 100%;
border: none;
border-bottom: 1px solid var(--header-border-color);
display: inline-block; display: inline-block;
padding-left: 8px; padding-left: 8px;
}
input, select {
border: none;
border-bottom: 1px solid var(--header-border-color);
outline: none; outline: none;
} }
.topic_content_row textarea { .topic_content_row textarea {
@ -423,11 +422,16 @@ ul {
width: 100%; width: 100%;
} }
.formbutton {
margin-top: 12px;
margin-left: auto;
margin-right: auto;
}
.quick_button_row .formitem { .quick_button_row .formitem {
display: flex; display: flex;
margin-left: 2px; margin-left: 2px;
} }
.quick_button_row button, .quick_button_row label, .ip_search_search { .quick_button_row button, .quick_button_row label, .ip_search_search, .formbutton {
padding-left: 10px; padding-left: 10px;
padding-right: 10px; padding-right: 10px;
padding-top: 6px; padding-top: 6px;
@ -443,6 +447,9 @@ ul {
background: hsl(209, 97%, 56%); background: hsl(209, 97%, 56%);
border-radius: 2px; border-radius: 2px;
} }
.quick_button_row button, .quick_button_row label, .ip_search_search {
margin-right: 0px;
}
.quick_button_row button, .quick_button_row label { .quick_button_row button, .quick_button_row label {
margin-left: 10px; margin-left: 10px;
margin-top: 8px; margin-top: 8px;
@ -463,12 +470,24 @@ label.uploadItem {
padding-left: 33px; padding-left: 33px;
} }
select, input, textarea, button { button {
border: 1px solid var(--header-border-color); border: 1px solid var(--header-border-color);
}
select, input, textarea, button {
background: var(--element-background-color); background: var(--element-background-color);
padding: 5px; padding: 5px;
color: hsl(0,0%,30%); color: hsl(0,0%,30%);
} }
input, select {
color: var(--primary-text-color);
}
input:not(:focus):not([type="submit"]), select:not(:focus) {
color: var(--light-text-color);
}
textarea {
outline: none;
border: 1px solid var(--header-border-color);
}
.topic_reply_container { .topic_reply_container {
display: flex; display: flex;
@ -1003,6 +1022,7 @@ select, input, textarea, button {
} }
.formitem:only-child { .formitem:only-child {
width: 100%; width: 100%;
display: flex;
} }
.the_form .formitem:only-child button { .the_form .formitem:only-child button {
margin-left: auto; margin-left: auto;
@ -1100,7 +1120,7 @@ select, input, textarea, button {
} }
#themeSelectorSelect { #themeSelectorSelect {
padding: 3px; padding: 3px;
margin-top: 2px; margin-top: 0px;
} }
.colstack_grid { .colstack_grid {