feat: settings navigation and mocked healthchecks in index

This commit is contained in:
Tine 2024-02-12 14:20:38 +01:00
parent 1769342068
commit 18d6c563a5
Signed by: mentos1386
SSH key fingerprint: SHA256:MNtTsLbihYaWF8j1fkOHfkKNlnN1JQfxEU/rBU8nCGw
11 changed files with 175 additions and 183 deletions

View file

@ -46,7 +46,8 @@ func main() {
r.HandleFunc("/", h.Index).Methods("GET")
// Authenticated routes
r.HandleFunc("/settings", h.Authenticated(h.Settings)).Methods("GET")
r.HandleFunc("/settings", h.Authenticated(h.SettingsOverviewGET)).Methods("GET")
r.HandleFunc("/settings/healthchecks", h.Authenticated(h.SettingsHealthchecksGET)).Methods("GET")
// OAuth2
r.HandleFunc("/oauth2/login", h.OAuth2LoginGET).Methods("GET")

View file

@ -14,8 +14,8 @@ var Pages = []*components.Page{
{Path: "/settings", Title: "Settings"},
}
func GetPageByTitle(title string) *components.Page {
for _, p := range Pages {
func GetPageByTitle(pages []*components.Page, title string) *components.Page {
for _, p := range pages {
if p.Title == title {
return p
}

View file

@ -1,6 +1,7 @@
package handlers
import (
"math/rand"
"net/http"
"text/template"
@ -8,6 +9,36 @@ import (
"code.tjo.space/mentos1386/zdravko/web/templates/components"
)
type IndexData struct {
*components.Base
HealthChecks []*HealthCheck
}
type HealthCheck struct {
Domain string
Healthy bool
Uptime string
History []bool
}
func newMockHealthCheck(domain string) *HealthCheck {
randBool := func() bool {
return rand.Intn(2) == 1
}
var history []bool
for i := 0; i < 90; i++ {
history = append(history, randBool())
}
return &HealthCheck{
Domain: domain,
Healthy: randBool(),
Uptime: "100",
History: history,
}
}
func (h *BaseHandler) Index(w http.ResponseWriter, r *http.Request) {
ts, err := template.ParseFS(templates.Templates,
"components/base.tmpl",
@ -18,9 +49,17 @@ func (h *BaseHandler) Index(w http.ResponseWriter, r *http.Request) {
return
}
err = ts.ExecuteTemplate(w, "base", &components.Base{
Page: GetPageByTitle("Status"),
Pages: Pages,
err = ts.ExecuteTemplate(w, "base", &IndexData{
Base: &components.Base{
Page: GetPageByTitle(Pages, "Status"),
Pages: Pages,
},
HealthChecks: []*HealthCheck{
newMockHealthCheck("example.com"),
newMockHealthCheck("example.org"),
newMockHealthCheck("example.net"),
newMockHealthCheck("foo.example.net"),
},
})
if err != nil {
http.Error(w, err.Error(), http.StatusInternalServerError)

View file

@ -10,13 +10,23 @@ import (
type Settings struct {
*components.Base
User *AuthenticatedUser
SettingsPage *components.Page
SettingsPages []*components.Page
User *AuthenticatedUser
}
func (h *BaseHandler) Settings(w http.ResponseWriter, r *http.Request, user *AuthenticatedUser) {
var SettingsPages = []*components.Page{
{Path: "/settings", Title: "Overview"},
{Path: "/settings/healthchecks", Title: "Healthchecks"},
{Path: "/settings/workers", Title: "Workers"},
{Path: "/oauth2/logout", Title: "Logout"},
}
func (h *BaseHandler) SettingsOverviewGET(w http.ResponseWriter, r *http.Request, user *AuthenticatedUser) {
ts, err := template.ParseFS(templates.Templates,
"components/base.tmpl",
"pages/settings.tmpl",
"components/settings.tmpl",
"pages/settings_overview.tmpl",
)
if err != nil {
http.Error(w, err.Error(), http.StatusInternalServerError)
@ -25,10 +35,37 @@ func (h *BaseHandler) Settings(w http.ResponseWriter, r *http.Request, user *Aut
err = ts.ExecuteTemplate(w, "base", &Settings{
Base: &components.Base{
Page: GetPageByTitle("Settings"),
Page: GetPageByTitle(Pages, "Settings"),
Pages: Pages,
},
User: user,
SettingsPage: GetPageByTitle(SettingsPages, "Overview"),
SettingsPages: SettingsPages,
User: user,
})
if err != nil {
http.Error(w, err.Error(), http.StatusInternalServerError)
}
}
func (h *BaseHandler) SettingsHealthchecksGET(w http.ResponseWriter, r *http.Request, user *AuthenticatedUser) {
ts, err := template.ParseFS(templates.Templates,
"components/base.tmpl",
"components/settings.tmpl",
"pages/settings_healthchecks.tmpl",
)
if err != nil {
http.Error(w, err.Error(), http.StatusInternalServerError)
return
}
err = ts.ExecuteTemplate(w, "base", &Settings{
Base: &components.Base{
Page: GetPageByTitle(Pages, "Settings"),
Pages: Pages,
},
SettingsPage: GetPageByTitle(SettingsPages, "Healthchecks"),
SettingsPages: SettingsPages,
User: user,
})
if err != nil {
http.Error(w, err.Error(), http.StatusInternalServerError)

View file

@ -44,3 +44,11 @@
.sidebar a.active {
@apply bg-blue-700 text-white;
}
.healthchecks {
@apply grid justify-items-stretch justify-stretch items-center mt-20 bg-gray-200 shadow-inner p-5 rounded-lg;
}
.healthchecks > div:not(:last-child) {
@apply mb-3;
}

View file

@ -603,8 +603,8 @@ video {
margin-bottom: 1rem;
}
.mr-1 {
margin-right: 0.25rem;
.me-2 {
margin-inline-end: 0.5rem;
}
.mt-10 {
@ -631,8 +631,8 @@ video {
height: 5rem;
}
.h-5 {
height: 1.25rem;
.h-3 {
height: 0.75rem;
}
.h-8 {
@ -643,8 +643,8 @@ video {
width: 5rem;
}
.w-5 {
width: 1.25rem;
.w-3 {
width: 0.75rem;
}
.max-w-screen-md {
@ -663,8 +663,8 @@ video {
grid-template-columns: repeat(2, minmax(0, 1fr));
}
.grid-cols-\[1fr_auto\] {
grid-template-columns: 1fr auto;
.grid-cols-\[1fr_100\%\] {
grid-template-columns: 1fr 100%;
}
.flex-col {
@ -679,14 +679,6 @@ video {
justify-content: center;
}
.justify-stretch {
justify-content: stretch;
}
.justify-items-stretch {
justify-items: stretch;
}
.gap-2 {
gap: 0.5rem;
}
@ -729,30 +721,16 @@ video {
border-radius: 9999px;
}
.rounded-lg {
border-radius: 0.5rem;
}
.bg-gray-100 {
--tw-bg-opacity: 1;
background-color: rgb(243 244 246 / var(--tw-bg-opacity));
}
.bg-gray-200 {
--tw-bg-opacity: 1;
background-color: rgb(229 231 235 / var(--tw-bg-opacity));
}
.bg-green-300 {
--tw-bg-opacity: 1;
background-color: rgb(134 239 172 / var(--tw-bg-opacity));
}
.bg-green-400 {
--tw-bg-opacity: 1;
background-color: rgb(74 222 128 / var(--tw-bg-opacity));
}
.bg-green-500 {
--tw-bg-opacity: 1;
background-color: rgb(34 197 94 / var(--tw-bg-opacity));
@ -768,22 +746,10 @@ video {
background-color: rgb(239 68 68 / var(--tw-bg-opacity));
}
.stroke-\[3\] {
stroke-width: 3;
}
.p-1 {
padding: 0.25rem;
}
.p-4 {
padding: 1rem;
}
.p-5 {
padding: 1.25rem;
}
.text-center {
text-align: center;
}
@ -798,11 +764,6 @@ video {
line-height: 1.25rem;
}
.text-xl {
font-size: 1.25rem;
line-height: 1.75rem;
}
.text-xs {
font-size: 0.75rem;
line-height: 1rem;
@ -836,12 +797,6 @@ video {
text-decoration-line: underline;
}
.shadow-inner {
--tw-shadow: inset 0 2px 4px 0 rgb(0 0 0 / 0.05);
--tw-shadow-colored: inset 0 2px 4px 0 var(--tw-shadow-color);
box-shadow: var(--tw-ring-offset-shadow, 0 0 #0000), var(--tw-ring-shadow, 0 0 #0000), var(--tw-shadow);
}
.btn {
border-radius: 0.25rem;
padding-top: 0.5rem;
@ -940,6 +895,25 @@ video {
color: rgb(255 255 255 / var(--tw-text-opacity));
}
.healthchecks {
margin-top: 5rem;
display: grid;
align-items: center;
justify-content: stretch;
justify-items: stretch;
border-radius: 0.5rem;
--tw-bg-opacity: 1;
background-color: rgb(229 231 235 / var(--tw-bg-opacity));
padding: 1.25rem;
--tw-shadow: inset 0 2px 4px 0 rgb(0 0 0 / 0.05);
--tw-shadow-colored: inset 0 2px 4px 0 var(--tw-shadow-color);
box-shadow: var(--tw-ring-offset-shadow, 0 0 #0000), var(--tw-ring-shadow, 0 0 #0000), var(--tw-shadow);
}
.healthchecks > div:not(:last-child) {
margin-bottom: 0.75rem;
}
.hover\:bg-green-700:hover {
--tw-bg-opacity: 1;
background-color: rgb(21 128 61 / var(--tw-bg-opacity));

View file

@ -0,0 +1,28 @@
{{define "main"}}
{{ $title := "" }}
{{ $path := "" }}
{{ if ne nil .SettingsPage }}
{{ $title = .SettingsPage.Title }}
{{ $path = .SettingsPage.Path }}
{{ end }}
<div class="grid grid-cols-[1fr_100%] gap-8">
<ul class="sidebar">
{{range .SettingsPages}}
<li>
<a
{{$active := eq .Path $path }}
{{if $active}}aria-current="true"{{end}}
href="{{.Path}}"
class="{{if $active}}active{{end}}">
{{.Title}}
</a>
</li>
{{end}}
</ul>
<div>
{{template "settings" .}}
</div>
</div>
{{end}}

View file

@ -1,5 +1,3 @@
{{define "title"}}Status{{end}}
{{define "main"}}
<div class="flex flex-col items-center">
<svg class="feather h-20 w-20 rounded-full bg-green-300 p-4 overflow-visible"><use href="/static/icons/feather-sprite.svg#check" /></svg>
@ -11,107 +9,30 @@
<h3 class="text-slate-500">Degraded performance</h3>
<p class="text-slate-500 text-sm">Last updated on Feb 10 at 10:55am UTC</p>
</div>
<div class="grid justify-items-stretch justify-stretch items-center mt-20 bg-gray-200 shadow-inner p-5 rounded-lg">
<div class="healthchecks">
{{ range .HealthChecks }}
<div class="grid grid-cols-2 gap-2">
<div class="flex items-center">
<svg class="feather h-5 w-5 stroke-[3] rounded-full bg-green-400 p-1 mr-1"><use href="/static/icons/feather-sprite.svg#check" /></svg>
<p>foo.bar</p>
{{ if .Healthy }}
<span class="flex w-3 h-3 me-2 bg-green-500 rounded-full"></span>
{{ else }}
<span class="flex w-3 h-3 me-2 bg-red-500 rounded-full"></span>
{{ end }}
<p>{{ .Domain }}</p>
</div>
<div class="justify-self-end text-sm">69.420% uptime</div>
<div class="justify-self-end text-sm">{{ .Uptime }}% uptime</div>
<div class="grid gap-px col-span-2 grid-flow-col h-8 rounded overflow-hidden">
{{ range .History }}
{{ if . }}
<div class="bg-green-500 hover:bg-green-700 flex-auto"></div>
<div class="bg-green-500 hover:bg-green-700 flex-auto"></div>
<div class="bg-green-500 hover:bg-green-700 flex-auto"></div>
<div class="bg-green-500 hover:bg-green-700 flex-auto"></div>
<div class="bg-green-500 hover:bg-green-700 flex-auto"></div>
<div class="bg-green-500 hover:bg-green-700 flex-auto"></div>
<div class="bg-green-500 hover:bg-green-700 flex-auto"></div>
<div class="bg-green-500 hover:bg-green-700 flex-auto"></div>
<div class="bg-green-500 hover:bg-green-700 flex-auto"></div>
<div class="bg-green-500 hover:bg-green-700 flex-auto"></div>
<div class="bg-green-500 hover:bg-green-700 flex-auto"></div>
<div class="bg-green-500 hover:bg-green-700 flex-auto"></div>
<div class="bg-green-500 hover:bg-green-700 flex-auto"></div>
<div class="bg-green-500 hover:bg-green-700 flex-auto"></div>
<div class="bg-green-500 hover:bg-green-700 flex-auto"></div>
<div class="bg-green-500 hover:bg-green-700 flex-auto"></div>
<div class="bg-green-500 hover:bg-green-700 flex-auto"></div>
<div class="bg-green-500 hover:bg-green-700 flex-auto"></div>
<div class="bg-green-500 hover:bg-green-700 flex-auto"></div>
<div class="bg-green-500 hover:bg-green-700 flex-auto"></div>
<div class="bg-green-500 hover:bg-green-700 flex-auto"></div>
<div class="bg-green-500 hover:bg-green-700 flex-auto"></div>
<div class="bg-green-500 hover:bg-green-700 flex-auto"></div>
<div class="bg-green-500 hover:bg-green-700 flex-auto"></div>
<div class="bg-green-500 hover:bg-green-700 flex-auto"></div>
<div class="bg-green-500 hover:bg-green-700 flex-auto"></div>
<div class="bg-green-500 hover:bg-green-700 flex-auto"></div>
<div class="bg-green-500 hover:bg-green-700 flex-auto"></div>
<div class="bg-green-500 hover:bg-green-700 flex-auto"></div>
<div class="bg-green-500 hover:bg-green-700 flex-auto"></div>
<div class="bg-green-500 hover:bg-green-700 flex-auto"></div>
<div class="bg-green-500 hover:bg-green-700 flex-auto"></div>
<div class="bg-green-500 hover:bg-green-700 flex-auto"></div>
<div class="bg-green-500 hover:bg-green-700 flex-auto"></div>
<div class="bg-green-500 hover:bg-green-700 flex-auto"></div>
<div class="bg-green-500 hover:bg-green-700 flex-auto"></div>
<div class="bg-green-500 hover:bg-green-700 flex-auto"></div>
<div class="bg-green-500 hover:bg-green-700 flex-auto"></div>
<div class="bg-green-500 hover:bg-green-700 flex-auto"></div>
<div class="bg-green-500 hover:bg-green-700 flex-auto"></div>
<div class="bg-green-500 hover:bg-green-700 flex-auto"></div>
<div class="bg-green-500 hover:bg-green-700 flex-auto"></div>
<div class="bg-green-500 hover:bg-green-700 flex-auto"></div>
<div class="bg-green-500 hover:bg-green-700 flex-auto"></div>
<div class="bg-green-500 hover:bg-green-700 flex-auto"></div>
<div class="bg-green-500 hover:bg-green-700 flex-auto"></div>
<div class="bg-green-500 hover:bg-green-700 flex-auto"></div>
<div class="bg-green-500 hover:bg-green-700 flex-auto"></div>
<div class="bg-green-500 hover:bg-green-700 flex-auto"></div>
<div class="bg-green-500 hover:bg-green-700 flex-auto"></div>
<div class="bg-green-500 hover:bg-green-700 flex-auto"></div>
<div class="bg-green-500 hover:bg-green-700 flex-auto"></div>
<div class="bg-green-500 hover:bg-green-700 flex-auto"></div>
<div class="bg-green-500 hover:bg-green-700 flex-auto"></div>
<div class="bg-green-500 hover:bg-green-700 flex-auto"></div>
<div class="bg-green-500 hover:bg-green-700 flex-auto"></div>
<div class="bg-green-500 hover:bg-green-700 flex-auto"></div>
<div class="bg-green-500 hover:bg-green-700 flex-auto"></div>
<div class="bg-green-500 hover:bg-green-700 flex-auto"></div>
<div class="bg-green-500 hover:bg-green-700 flex-auto"></div>
<div class="bg-green-500 hover:bg-green-700 flex-auto"></div>
<div class="bg-green-500 hover:bg-green-700 flex-auto"></div>
<div class="bg-green-500 hover:bg-green-700 flex-auto"></div>
{{ else }}
<div class="bg-red-500 hover:bg-red-700 flex-auto"></div>
<div class="bg-red-500 hover:bg-red-700 flex-auto"></div>
<div class="bg-green-500 hover:bg-green-700 flex-auto"></div>
<div class="bg-green-500 hover:bg-green-700 flex-auto"></div>
<div class="bg-green-500 hover:bg-green-700 flex-auto"></div>
<div class="bg-green-500 hover:bg-green-700 flex-auto"></div>
<div class="bg-green-500 hover:bg-green-700 flex-auto"></div>
<div class="bg-green-500 hover:bg-green-700 flex-auto"></div>
<div class="bg-green-500 hover:bg-green-700 flex-auto"></div>
<div class="bg-green-500 hover:bg-green-700 flex-auto"></div>
<div class="bg-green-500 hover:bg-green-700 flex-auto"></div>
<div class="bg-green-500 hover:bg-green-700 flex-auto"></div>
<div class="bg-green-500 hover:bg-green-700 flex-auto"></div>
<div class="bg-green-500 hover:bg-green-700 flex-auto"></div>
<div class="bg-green-500 hover:bg-green-700 flex-auto"></div>
<div class="bg-green-500 hover:bg-green-700 flex-auto"></div>
<div class="bg-green-500 hover:bg-green-700 flex-auto"></div>
<div class="bg-green-500 hover:bg-green-700 flex-auto"></div>
<div class="bg-green-500 hover:bg-green-700 flex-auto"></div>
<div class="bg-red-500 hover:bg-red-700 flex-auto"></div>
<div class="bg-green-500 hover:bg-green-700 flex-auto"></div>
<div class="bg-green-500 hover:bg-green-700 flex-auto"></div>
<div class="bg-green-500 hover:bg-green-700 flex-auto"></div>
<div class="bg-green-500 hover:bg-green-700 flex-auto"></div>
<div class="bg-green-500 hover:bg-green-700 flex-auto"></div>
<div class="bg-green-500 hover:bg-green-700 flex-auto"></div>
<div class="bg-green-500 hover:bg-green-700 flex-auto"></div>
</div>
{{ end }}
{{ end }}
</div>
<div class="text-slate-500 justify-self-start text-sm">90 days ago</div>
<div class="text-slate-500 justify-self-end text-sm">Today</div>
</div>
{{ end }}
</div>
{{end}}

View file

@ -1,24 +0,0 @@
{{define "main"}}
<div class="grid grid-cols-[1fr_auto] gap-8">
<ul class="sidebar">
<li>
<a aria-current="true" class="active" href="/settings">Overview</a>
</li>
<li>
<a href="/settings/healthchecks">Healthchecks</a>
</li>
<li>
<a href="/settings/workers">Workers</a>
</li>
<li>
<a href="/oauth2/logout">Logout</a>
</li>
</ul>
<div class="shadow-inner bg-gray-200 p-5 rounded-lg">
<h1 class="text-xl mb-4">Hi {{.User.Email}}!</h1>
<p>Your id is {{.User.ID}}.</p>
<p>Your access expieres at {{.User.OAuth2Expiry}}.</p>
</div>
</div>
{{end}}

View file

@ -0,0 +1,5 @@
{{define "settings"}}
<h1 class="text-3xl">Hello, {{.User.Email}}</h1>
<p> Imagine you see a list of healthchecks and how to configure them.<p>
{{end}}

View file

@ -0,0 +1,3 @@
{{define "settings"}}
<h1 class="text-3xl">Hello, {{.User.Email}}</h1>
{{end}}