feat: sidebar design

This commit is contained in:
Tine 2024-05-17 21:54:14 +02:00
parent b07297cf31
commit cfc5668c48
Signed by: mentos1386
SSH key fingerprint: SHA256:MNtTsLbihYaWF8j1fkOHfkKNlnN1JQfxEU/rBU8nCGw
10 changed files with 77 additions and 32 deletions

View file

@ -20,7 +20,7 @@ trigger: |
} }
# Example monitor code # Example monitor code
monitor: | check: |
import http from 'k6/http'; import http from 'k6/http';
export const options = { export const options = {

View file

@ -15,8 +15,8 @@ type IndexData struct {
*components.Base *components.Base
Checks map[string]ChecksAndStatus Checks map[string]ChecksAndStatus
ChecksLength int ChecksLength int
TimeRange string TimeRange string
Status models.CheckStatus Status models.CheckStatus
} }
type Check struct { type Check struct {
@ -33,11 +33,11 @@ type HistoryItem struct {
type History struct { type History struct {
List []HistoryItem List []HistoryItem
Uptime int Uptime float64
} }
type ChecksAndStatus struct { type ChecksAndStatus struct {
Status models.CheckStatus Status models.CheckStatus
Checks []*Check Checks []*Check
} }
@ -47,8 +47,8 @@ func getDateString(date time.Time) string {
func getHistory(history []*models.CheckHistory, period time.Duration, buckets int) *History { func getHistory(history []*models.CheckHistory, period time.Duration, buckets int) *History {
historyMap := map[string]models.CheckStatus{} historyMap := map[string]models.CheckStatus{}
numOfSuccess := 0 numOfSuccess := 0.0
numTotal := 0 numTotal := 0.0
for i := 0; i < buckets; i++ { for i := 0; i < buckets; i++ {
dateString := getDateString(time.Now().Add(period * time.Duration(-i)).Truncate(period)) dateString := getDateString(time.Now().Add(period * time.Duration(-i)).Truncate(period))
@ -88,9 +88,9 @@ func getHistory(history []*models.CheckHistory, period time.Duration, buckets in
} }
} }
uptime := 0 uptime := 0.0
if numTotal > 0 { if numTotal > 0 {
uptime = 100 * numOfSuccess / numTotal uptime = 100.0 * numOfSuccess / numTotal
} }
return &History{ return &History{
@ -160,7 +160,7 @@ func (h *BaseHandler) Index(c echo.Context) error {
checksByGroup := map[string]ChecksAndStatus{} checksByGroup := map[string]ChecksAndStatus{}
for _, check := range checksWithHistory { for _, check := range checksWithHistory {
checksByGroup[check.Group] = ChecksAndStatus{ checksByGroup[check.Group] = ChecksAndStatus{
Status: statusByGroup[check.Group], Status: statusByGroup[check.Group],
Checks: append(checksByGroup[check.Group].Checks, check), Checks: append(checksByGroup[check.Group].Checks, check),
} }
} }
@ -172,7 +172,7 @@ func (h *BaseHandler) Index(c echo.Context) error {
NavbarActive: GetPageByTitle(Pages, "Status"), NavbarActive: GetPageByTitle(Pages, "Status"),
Navbar: Pages, Navbar: Pages,
}, },
Checks: checksByGroup, Checks: checksByGroup,
TimeRange: timeRange, TimeRange: timeRange,
Status: overallStatus, Status: overallStatus,
}) })

View file

@ -15,18 +15,37 @@ type SettingsSidebarGroup struct {
type Settings struct { type Settings struct {
*components.Base *components.Base
SettingsGroupName string
SettingsSidebarActive *components.Page SettingsSidebarActive *components.Page
SettingsSidebar []SettingsSidebarGroup SettingsSidebar []SettingsSidebarGroup
User *AuthenticatedUser User *AuthenticatedUser
SettingsBreadcrumbs []*components.Page SettingsBreadcrumbs []*components.Page
} }
func findGroupForPage(groups []SettingsSidebarGroup, page *components.Page) *SettingsSidebarGroup {
for _, group := range groups {
for _, p := range group.Pages {
if p == page {
return &group
}
}
}
return nil
}
func NewSettings(user *AuthenticatedUser, page *components.Page, breadCrumbs []*components.Page) *Settings { func NewSettings(user *AuthenticatedUser, page *components.Page, breadCrumbs []*components.Page) *Settings {
groupName := ""
group := findGroupForPage(SettingsSidebar, page)
if group != nil {
groupName = group.Group
}
return &Settings{ return &Settings{
Base: &components.Base{ Base: &components.Base{
NavbarActive: GetPageByTitle(Pages, "Settings"), NavbarActive: GetPageByTitle(Pages, "Settings"),
Navbar: Pages, Navbar: Pages,
}, },
SettingsGroupName: groupName,
SettingsSidebarActive: page, SettingsSidebarActive: page,
SettingsSidebar: SettingsSidebar, SettingsSidebar: SettingsSidebar,
SettingsBreadcrumbs: breadCrumbs, SettingsBreadcrumbs: breadCrumbs,
@ -35,7 +54,7 @@ func NewSettings(user *AuthenticatedUser, page *components.Page, breadCrumbs []*
} }
var SettingsPages = []*components.Page{ var SettingsPages = []*components.Page{
{Path: "/settings", Title: "Overview", Breadcrumb: "Overview"}, {Path: "/settings", Title: "Home", Breadcrumb: "Home"},
{Path: "/settings/incidents", Title: "Incidents", Breadcrumb: "Incidents"}, {Path: "/settings/incidents", Title: "Incidents", Breadcrumb: "Incidents"},
{Path: "/settings/targets", Title: "Targets", Breadcrumb: "Targets"}, {Path: "/settings/targets", Title: "Targets", Breadcrumb: "Targets"},
{Path: "/settings/targets/create", Title: "Targets Create", Breadcrumb: "Create"}, {Path: "/settings/targets/create", Title: "Targets Create", Breadcrumb: "Create"},
@ -57,7 +76,7 @@ var SettingsSidebar = []SettingsSidebarGroup{
{ {
Group: "Overview", Group: "Overview",
Pages: []*components.Page{ Pages: []*components.Page{
GetPageByTitle(SettingsPages, "Overview"), GetPageByTitle(SettingsPages, "Home"),
}, },
}, },
{ {
@ -91,7 +110,7 @@ var SettingsSidebar = []SettingsSidebarGroup{
}, },
} }
type SettingsOverview struct { type SettingsHome struct {
*Settings *Settings
WorkerGroupsCount int WorkerGroupsCount int
ChecksCount int ChecksCount int
@ -99,7 +118,7 @@ type SettingsOverview struct {
History []*services.CheckHistoryWithCheck History []*services.CheckHistoryWithCheck
} }
func (h *BaseHandler) SettingsOverviewGET(c echo.Context) error { func (h *BaseHandler) SettingsHomeGET(c echo.Context) error {
cc := c.(AuthenticatedContext) cc := c.(AuthenticatedContext)
ctx := c.Request().Context() ctx := c.Request().Context()
@ -118,11 +137,11 @@ func (h *BaseHandler) SettingsOverviewGET(c echo.Context) error {
return err return err
} }
return c.Render(http.StatusOK, "settings_overview.tmpl", SettingsOverview{ return c.Render(http.StatusOK, "settings_home.tmpl", SettingsHome{
Settings: NewSettings( Settings: NewSettings(
cc.Principal.User, cc.Principal.User,
GetPageByTitle(SettingsPages, "Overview"), GetPageByTitle(SettingsPages, "Home"),
[]*components.Page{GetPageByTitle(SettingsPages, "Overview")}, []*components.Page{GetPageByTitle(SettingsPages, "Home")},
), ),
WorkerGroupsCount: workerGroups, WorkerGroupsCount: workerGroups,
ChecksCount: checks, ChecksCount: checks,

View file

@ -52,7 +52,7 @@ func Routes(
// Settings // Settings
settings := e.Group("/settings") settings := e.Group("/settings")
settings.Use(h.Authenticated) settings.Use(h.Authenticated)
settings.GET("", h.SettingsOverviewGET) settings.GET("", h.SettingsHomeGET)
settings.GET("/triggers", h.SettingsTriggersGET) settings.GET("/triggers", h.SettingsTriggersGET)
settings.GET("/triggers/:id", h.SettingsTriggersDescribeGET) settings.GET("/triggers/:id", h.SettingsTriggersDescribeGET)

View file

@ -64,11 +64,11 @@ code {
@apply bg-blue-700 text-white; @apply bg-blue-700 text-white;
} }
.monitors .time-range > a { .checks .time-range > a {
@apply font-medium text-sm px-2.5 py-1 rounded-lg; @apply font-medium text-sm px-2.5 py-1 rounded-lg;
@apply text-black bg-gray-100 hover:bg-gray-300 focus:outline-none focus:ring-2 focus:ring-gray-400; @apply text-black bg-gray-100 hover:bg-gray-300 focus:outline-none focus:ring-2 focus:ring-gray-400;
} }
.monitors .time-range > a.active { .checks .time-range > a.active {
@apply bg-white hover:bg-gray-300 shadow; @apply bg-white hover:bg-gray-300 shadow;
} }

View file

@ -1404,7 +1404,7 @@ code {
color: rgb(255 255 255 / var(--tw-text-opacity)); color: rgb(255 255 255 / var(--tw-text-opacity));
} }
.monitors .time-range > a { .checks .time-range > a {
border-radius: 0.5rem; border-radius: 0.5rem;
padding-left: 0.625rem; padding-left: 0.625rem;
padding-right: 0.625rem; padding-right: 0.625rem;
@ -1419,12 +1419,12 @@ code {
color: rgb(0 0 0 / var(--tw-text-opacity)); color: rgb(0 0 0 / var(--tw-text-opacity));
} }
.monitors .time-range > a:hover { .checks .time-range > a:hover {
--tw-bg-opacity: 1; --tw-bg-opacity: 1;
background-color: rgb(209 213 219 / var(--tw-bg-opacity)); background-color: rgb(209 213 219 / var(--tw-bg-opacity));
} }
.monitors .time-range > a:focus { .checks .time-range > a:focus {
outline: 2px solid transparent; outline: 2px solid transparent;
outline-offset: 2px; outline-offset: 2px;
--tw-ring-offset-shadow: var(--tw-ring-inset) 0 0 0 var(--tw-ring-offset-width) var(--tw-ring-offset-color); --tw-ring-offset-shadow: var(--tw-ring-inset) 0 0 0 var(--tw-ring-offset-width) var(--tw-ring-offset-color);
@ -1434,7 +1434,7 @@ code {
--tw-ring-color: rgb(156 163 175 / var(--tw-ring-opacity)); --tw-ring-color: rgb(156 163 175 / var(--tw-ring-opacity));
} }
.monitors .time-range > a.active { .checks .time-range > a.active {
--tw-bg-opacity: 1; --tw-bg-opacity: 1;
background-color: rgb(255 255 255 / var(--tw-bg-opacity)); background-color: rgb(255 255 255 / var(--tw-bg-opacity));
--tw-shadow: 0 1px 3px 0 rgb(0 0 0 / 0.1), 0 1px 2px -1px rgb(0 0 0 / 0.1); --tw-shadow: 0 1px 3px 0 rgb(0 0 0 / 0.1), 0 1px 2px -1px rgb(0 0 0 / 0.1);
@ -1442,7 +1442,7 @@ code {
box-shadow: var(--tw-ring-offset-shadow, 0 0 #0000), var(--tw-ring-shadow, 0 0 #0000), var(--tw-shadow); box-shadow: var(--tw-ring-offset-shadow, 0 0 #0000), var(--tw-ring-shadow, 0 0 #0000), var(--tw-shadow);
} }
.monitors .time-range > a.active:hover { .checks .time-range > a.active:hover {
--tw-bg-opacity: 1; --tw-bg-opacity: 1;
background-color: rgb(209 213 219 / var(--tw-bg-opacity)); background-color: rgb(209 213 219 / var(--tw-bg-opacity));
} }
@ -1772,6 +1772,17 @@ code {
--tw-ring-color: rgb(252 165 165 / var(--tw-ring-opacity)); --tw-ring-color: rgb(252 165 165 / var(--tw-ring-opacity));
} }
.has-\[\.active\]\:bg-white:has(.active) {
--tw-bg-opacity: 1;
background-color: rgb(255 255 255 / var(--tw-bg-opacity));
}
.has-\[\.active\]\:shadow-md:has(.active) {
--tw-shadow: 0 4px 6px -1px rgb(0 0 0 / 0.1), 0 2px 4px -2px rgb(0 0 0 / 0.1);
--tw-shadow-colored: 0 4px 6px -1px var(--tw-shadow-color), 0 2px 4px -2px var(--tw-shadow-color);
box-shadow: var(--tw-ring-offset-shadow, 0 0 #0000), var(--tw-ring-shadow, 0 0 #0000), var(--tw-shadow);
}
@media (min-width: 640px) { @media (min-width: 640px) {
.sm\:w-auto { .sm\:w-auto {
width: auto; width: auto;

View file

@ -14,13 +14,15 @@
class="sidebar gap-2 flex flex-row flex-wrap justify-center lg:flex-col lg:w-48 h-fit text-sm font-medium text-gray-900" class="sidebar gap-2 flex flex-row flex-wrap justify-center lg:flex-col lg:w-48 h-fit text-sm font-medium text-gray-900"
> >
{{ range .SettingsSidebar }} {{ range .SettingsSidebar }}
<li class="flex items-center gap-1 lg:flex-col lg:items-start"> <li
class="flex items-center gap-2 p-2 lg:flex-col lg:items-start rounded-lg has-[.active]:shadow-md has-[.active]:bg-white"
>
<p <p
class="text-xs font-semibold text-gray-600 uppercase tracking-wider" class="text-xs font-semibold text-gray-600 uppercase tracking-wider"
> >
{{ .Group }} {{ .Group }}
</p> </p>
<ul class="flex flex-row flex-wrap gap-1 lg:flex-col"> <ul class="flex flex-row flex-wrap gap-1 lg:flex-col w-full">
{{ range .Pages }} {{ range .Pages }}
<li> <li>
<a <a
@ -67,6 +69,19 @@
class="mx-8 lg:mx-0 grid justify-center lg:justify-start" class="mx-8 lg:mx-0 grid justify-center lg:justify-start"
> >
<ol class="inline-flex items-center"> <ol class="inline-flex items-center">
<li class="inline-flex items-center">
<p
class="inline-flex items-center text-sm font-medium text-gray-700"
>
{{ .SettingsGroupName }}
<svg
aria-hidden="true"
class="feather h-4 w-4 mx-1 text-gray-400 overflow-visible"
>
<use href="/static/icons/feather-sprite.svg#chevron-right" />
</svg>
</p>
</li>
{{ range .SettingsBreadcrumbs }} {{ range .SettingsBreadcrumbs }}
<li class="inline-flex items-center"> <li class="inline-flex items-center">
<a <a

View file

@ -12,8 +12,8 @@
<p <p
class="mb-8 text-l font-normal text-gray-700 lg:text-l sm:px-8 lg:px-40" class="mb-8 text-l font-normal text-gray-700 lg:text-l sm:px-8 lg:px-40"
> >
Create a check to check your services and get notified when they Create a check to check your services and get notified when they are
are down. down.
</p> </p>
<div class="flex flex-col gap-4 sm:flex-row sm:justify-center"> <div class="flex flex-col gap-4 sm:flex-row sm:justify-center">
<a <a
@ -138,7 +138,7 @@
<h4>{{ .Name }}</h4> <h4>{{ .Name }}</h4>
</div> </div>
<div class="justify-self-end text-sm"> <div class="justify-self-end text-sm">
{{ .History.Uptime }}% uptime {{ printf "%.2f" .History.Uptime }}% uptime
</div> </div>
<div <div
class="grid gap-px col-span-2 grid-flow-col h-8 rounded overflow-hidden" class="grid gap-px col-span-2 grid-flow-col h-8 rounded overflow-hidden"

View file

@ -46,7 +46,7 @@ func NewTemplates() *Templates {
"404.tmpl": load("pages/404.tmpl"), "404.tmpl": load("pages/404.tmpl"),
"index.tmpl": load("pages/index.tmpl"), "index.tmpl": load("pages/index.tmpl"),
"incidents.tmpl": load("pages/incidents.tmpl"), "incidents.tmpl": load("pages/incidents.tmpl"),
"settings_overview.tmpl": loadSettings("pages/settings_overview.tmpl"), "settings_home.tmpl": loadSettings("pages/settings_home.tmpl"),
"settings_triggers.tmpl": loadSettings("pages/settings_triggers.tmpl"), "settings_triggers.tmpl": loadSettings("pages/settings_triggers.tmpl"),
"settings_triggers_create.tmpl": loadSettings("pages/settings_triggers_create.tmpl"), "settings_triggers_create.tmpl": loadSettings("pages/settings_triggers_create.tmpl"),
"settings_triggers_describe.tmpl": loadSettings("pages/settings_triggers_describe.tmpl"), "settings_triggers_describe.tmpl": loadSettings("pages/settings_triggers_describe.tmpl"),