Adding even more components

- Starting on main manager page
- Manager layout will be included in the manager page
- Manager page will use htmx to load different components via api
This commit is contained in:
Matthew Stobbs 2024-03-15 22:50:24 -06:00
parent 5fd215d6dd
commit 223496075a
14 changed files with 1858 additions and 1713 deletions

11
main.go
View File

@ -4,10 +4,12 @@ import (
"log" "log"
"net/http" "net/http"
"git.staur.ca/stobbsm/clustvirt/view/components"
"git.staur.ca/stobbsm/clustvirt/view/layouts"
"git.staur.ca/stobbsm/clustvirt/view/static" "git.staur.ca/stobbsm/clustvirt/view/static"
"github.com/a-h/templ"
"github.com/go-chi/chi/v5" "github.com/go-chi/chi/v5"
"github.com/go-chi/chi/v5/middleware" "github.com/go-chi/chi/v5/middleware"
"github.com/a-h/templ"
) )
const DEBUG bool = true const DEBUG bool = true
@ -28,6 +30,10 @@ func main() {
// Start webserver and serve homepage // Start webserver and serve homepage
defaultNavBar := []components.NavItem{
{Name: "Home", Href: "/"},
{Name: "About", Href: "/about"},
}
fs := http.StripPrefix("/static/", http.FileServer(http.Dir("public"))) fs := http.StripPrefix("/static/", http.FileServer(http.Dir("public")))
r := chi.NewRouter() r := chi.NewRouter()
@ -41,7 +47,8 @@ func main() {
} }
fs.ServeHTTP(w, r) fs.ServeHTTP(w, r)
}) })
r.Get("/", templ.Handler(static.Home()).ServeHTTP) r.Get("/", templ.Handler(layouts.Manager("Cluster Manager", "ClustVirt", defaultNavBar)).ServeHTTP)
r.Get("/about", templ.Handler(static.Home()).ServeHTTP)
log.Println(http.ListenAndServe(":3000", r)) log.Println(http.ListenAndServe(":3000", r))
} }

View File

@ -848,6 +848,26 @@ span>a:visited {
margin-right: auto; margin-right: auto;
} }
.my-8 {
margin-top: 2rem;
margin-bottom: 2rem;
}
.mx-2 {
margin-left: 0.5rem;
margin-right: 0.5rem;
}
.my-2 {
margin-top: 0.5rem;
margin-bottom: 0.5rem;
}
.mx-4 {
margin-left: 1rem;
margin-right: 1rem;
}
.mr-auto { .mr-auto {
margin-right: auto; margin-right: auto;
} }
@ -856,10 +876,18 @@ span>a:visited {
display: block; display: block;
} }
.inline-block {
display: inline-block;
}
.flex { .flex {
display: flex; display: flex;
} }
.inline-flex {
display: inline-flex;
}
.contents { .contents {
display: contents; display: contents;
} }
@ -868,6 +896,30 @@ span>a:visited {
display: none; display: none;
} }
.size-fit {
width: -moz-fit-content;
width: fit-content;
height: -moz-fit-content;
height: fit-content;
}
.size-max {
width: -moz-max-content;
width: max-content;
height: -moz-max-content;
height: max-content;
}
.size-72 {
width: 18rem;
height: 18rem;
}
.size-64 {
width: 16rem;
height: 16rem;
}
.h-4 { .h-4 {
height: 1rem; height: 1rem;
} }
@ -908,6 +960,52 @@ span>a:visited {
height: 7rem; height: 7rem;
} }
.h-44 {
height: 11rem;
}
.h-52 {
height: 13rem;
}
.h-36 {
height: 9rem;
}
.h-40 {
height: 10rem;
}
.h-max {
height: -moz-max-content;
height: max-content;
}
.h-auto {
height: auto;
}
.h-48 {
height: 12rem;
}
.h-56 {
height: 14rem;
}
.h-64 {
height: 16rem;
}
.h-72 {
height: 18rem;
}
.h-fit {
height: -moz-fit-content;
height: fit-content;
}
.w-full { .w-full {
width: 100%; width: 100%;
} }
@ -933,10 +1031,55 @@ span>a:visited {
width: 91.666667%; width: 91.666667%;
} }
.w-6 {
width: 1.5rem;
}
.w-40 {
width: 10rem;
}
.w-44 {
width: 11rem;
}
.w-auto {
width: auto;
}
.w-48 {
width: 12rem;
}
.w-52 {
width: 13rem;
}
.w-64 {
width: 16rem;
}
.w-72 {
width: 18rem;
}
.w-max {
width: -moz-max-content;
width: max-content;
}
.flex-auto { .flex-auto {
flex: 1 1 auto; flex: 1 1 auto;
} }
.flex-grow {
flex-grow: 1;
}
.grow {
flex-grow: 1;
}
.basis-1\/2 { .basis-1\/2 {
flex-basis: 50%; flex-basis: 50%;
} }
@ -969,6 +1112,14 @@ span>a:visited {
flex-basis: 66.666667%; flex-basis: 66.666667%;
} }
.basis-2\/4 {
flex-basis: 50%;
}
.basis-2\/5 {
flex-basis: 40%;
}
.list-inside { .list-inside {
list-style-position: inside; list-style-position: inside;
} }
@ -1009,6 +1160,10 @@ span>a:visited {
align-items: center; align-items: center;
} }
.justify-center {
justify-content: center;
}
.justify-between { .justify-between {
justify-content: space-between; justify-content: space-between;
} }
@ -9048,6 +9203,26 @@ span>a:visited {
align-self: flex-start; align-self: flex-start;
} }
.self-center {
align-self: center;
}
.justify-self-end {
justify-self: end;
}
.justify-self-center {
justify-self: center;
}
.rounded {
border-radius: 0.25rem;
}
.rounded-full {
border-radius: 9999px;
}
.border-uiblue-100 { .border-uiblue-100 {
--tw-border-opacity: 1; --tw-border-opacity: 1;
border-color: rgb(221 231 248 / var(--tw-border-opacity)); border-color: rgb(221 231 248 / var(--tw-border-opacity));
@ -138667,6 +138842,11 @@ span>a:visited {
padding-right: 2rem; padding-right: 2rem;
} }
.py-8 {
padding-top: 2rem;
padding-bottom: 2rem;
}
.align-bottom { .align-bottom {
vertical-align: bottom; vertical-align: bottom;
} }
@ -220316,6 +220496,10 @@ span>a:visited {
display: inline-block; display: inline-block;
} }
.md\:inline-flex {
display: inline-flex;
}
.md\:contents { .md\:contents {
display: contents; display: contents;
} }

BIN
public/images/home/logo-banner-dark-800.png (Stored with Git LFS) Normal file

Binary file not shown.

View File

@ -0,0 +1,5 @@
package components
templ Hero() {
{ children... }
}

View File

@ -0,0 +1,35 @@
// Code generated by templ - DO NOT EDIT.
// templ: version: v0.2.598
package components
//lint:file-ignore SA4006 This context is only used if a nested component is present.
import "github.com/a-h/templ"
import "context"
import "io"
import "bytes"
func Hero() templ.Component {
return templ.ComponentFunc(func(ctx context.Context, templ_7745c5c3_W io.Writer) (templ_7745c5c3_Err error) {
templ_7745c5c3_Buffer, templ_7745c5c3_IsBuffer := templ_7745c5c3_W.(*bytes.Buffer)
if !templ_7745c5c3_IsBuffer {
templ_7745c5c3_Buffer = templ.GetBuffer()
defer templ.ReleaseBuffer(templ_7745c5c3_Buffer)
}
ctx = templ.InitializeContext(ctx)
templ_7745c5c3_Var1 := templ.GetChildren(ctx)
if templ_7745c5c3_Var1 == nil {
templ_7745c5c3_Var1 = templ.NopComponent
}
ctx = templ.ClearChildren(ctx)
templ_7745c5c3_Err = templ_7745c5c3_Var1.Render(ctx, templ_7745c5c3_Buffer)
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
if !templ_7745c5c3_IsBuffer {
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteTo(templ_7745c5c3_W)
}
return templ_7745c5c3_Err
})
}

View File

@ -0,0 +1,40 @@
package components
templ NavBar(hero templ.Component, navItems []NavItem) {
<nav
class={ "w-100",
"bg-uigrey-600",
"shadow",
"shadow-uigrey-700",
"rounded",
"rounded-full",
"md:px-auto" }
>
<div
class={ "md:h-16",
"h-28",
"mx-auto",
"md:px-4",
"container",
"flex",
"items-center",
"justify-between",
"flex-wrap",
"md:flex-nowrap" }
>
<a href="/">
@hero
</a>
<div class={ "text-uigrey-200", "font-semibold", "md:w-auto", "md:order-2", "order-3" }>
<ul class={ "flex", "justify-between", "sm:w-full", "md:w-auto" }>
for _, ni := range navItems {
<li class={ "hover:text-uiblue-200", "md:px-4", "md:py-2" }>
<a href={ templ.URL(ni.Href) }>{ ni.Name }</a>
</li>
}
</ul>
</div>
<div class={ "order-2", "md:order-3" }>Login</div>
</div>
</nav>
}

View File

@ -0,0 +1,184 @@
// Code generated by templ - DO NOT EDIT.
// templ: version: v0.2.598
package components
//lint:file-ignore SA4006 This context is only used if a nested component is present.
import "github.com/a-h/templ"
import "context"
import "io"
import "bytes"
func NavBar(hero templ.Component, navItems []NavItem) templ.Component {
return templ.ComponentFunc(func(ctx context.Context, templ_7745c5c3_W io.Writer) (templ_7745c5c3_Err error) {
templ_7745c5c3_Buffer, templ_7745c5c3_IsBuffer := templ_7745c5c3_W.(*bytes.Buffer)
if !templ_7745c5c3_IsBuffer {
templ_7745c5c3_Buffer = templ.GetBuffer()
defer templ.ReleaseBuffer(templ_7745c5c3_Buffer)
}
ctx = templ.InitializeContext(ctx)
templ_7745c5c3_Var1 := templ.GetChildren(ctx)
if templ_7745c5c3_Var1 == nil {
templ_7745c5c3_Var1 = templ.NopComponent
}
ctx = templ.ClearChildren(ctx)
var templ_7745c5c3_Var2 = []any{"w-100",
"bg-uigrey-600",
"shadow",
"shadow-uigrey-700",
"rounded",
"rounded-full",
"md:px-auto"}
templ_7745c5c3_Err = templ.RenderCSSItems(ctx, templ_7745c5c3_Buffer, templ_7745c5c3_Var2...)
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString("<nav class=\"")
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ.CSSClasses(templ_7745c5c3_Var2).String()))
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString("\">")
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
var templ_7745c5c3_Var3 = []any{"md:h-16",
"h-28",
"mx-auto",
"md:px-4",
"container",
"flex",
"items-center",
"justify-between",
"flex-wrap",
"md:flex-nowrap"}
templ_7745c5c3_Err = templ.RenderCSSItems(ctx, templ_7745c5c3_Buffer, templ_7745c5c3_Var3...)
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString("<div class=\"")
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ.CSSClasses(templ_7745c5c3_Var3).String()))
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString("\"><a href=\"/\">")
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
templ_7745c5c3_Err = hero.Render(ctx, templ_7745c5c3_Buffer)
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString("</a>")
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
var templ_7745c5c3_Var4 = []any{"text-uigrey-200", "font-semibold", "md:w-auto", "md:order-2", "order-3"}
templ_7745c5c3_Err = templ.RenderCSSItems(ctx, templ_7745c5c3_Buffer, templ_7745c5c3_Var4...)
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString("<div class=\"")
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ.CSSClasses(templ_7745c5c3_Var4).String()))
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString("\">")
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
var templ_7745c5c3_Var5 = []any{"flex", "justify-between", "sm:w-full", "md:w-auto"}
templ_7745c5c3_Err = templ.RenderCSSItems(ctx, templ_7745c5c3_Buffer, templ_7745c5c3_Var5...)
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString("<ul class=\"")
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ.CSSClasses(templ_7745c5c3_Var5).String()))
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString("\">")
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
for _, ni := range navItems {
var templ_7745c5c3_Var6 = []any{"hover:text-uiblue-200", "md:px-4", "md:py-2"}
templ_7745c5c3_Err = templ.RenderCSSItems(ctx, templ_7745c5c3_Buffer, templ_7745c5c3_Var6...)
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString("<li class=\"")
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ.CSSClasses(templ_7745c5c3_Var6).String()))
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString("\"><a href=\"")
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
var templ_7745c5c3_Var7 templ.SafeURL = templ.URL(ni.Href)
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(string(templ_7745c5c3_Var7)))
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString("\">")
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
var templ_7745c5c3_Var8 string
templ_7745c5c3_Var8, templ_7745c5c3_Err = templ.JoinStringErrs(ni.Name)
if templ_7745c5c3_Err != nil {
return templ.Error{Err: templ_7745c5c3_Err, FileName: `view/components/navbar.templ`, Line: 31, Col: 47}
}
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var8))
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString("</a></li>")
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
}
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString("</ul></div>")
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
var templ_7745c5c3_Var9 = []any{"order-2", "md:order-3"}
templ_7745c5c3_Err = templ.RenderCSSItems(ctx, templ_7745c5c3_Buffer, templ_7745c5c3_Var9...)
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString("<div class=\"")
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ.CSSClasses(templ_7745c5c3_Var9).String()))
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString("\">Login</div></div></nav>")
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
if !templ_7745c5c3_IsBuffer {
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteTo(templ_7745c5c3_W)
}
return templ_7745c5c3_Err
})
}

View File

@ -4,6 +4,11 @@ import (
"fmt" "fmt"
) )
type NavItem struct {
Name string
Href string
}
func NoWrapSize(size string) string { func NoWrapSize(size string) string {
return fmt.Sprintf("%s:flex-nowrap", size) return fmt.Sprintf("%s:flex-nowrap", size)
} }

View File

@ -1,6 +1,8 @@
package layouts package layouts
templ Manager(config LayoutConfig) { import "git.staur.ca/stobbsm/clustvirt/view/components"
templ Manager(title string, subtitle string, navBarItem []components.NavItem) {
<!DOCTYPE html> <!DOCTYPE html>
<html class={ "text-slate-50", "bg-slate-900" }> <html class={ "text-slate-50", "bg-slate-900" }>
<head> <head>
@ -9,11 +11,11 @@ templ Manager(config LayoutConfig) {
</head> </head>
<body> <body>
<header class={}> <header class={"mx-4"}>
@header(config.MainTitle, config.SubTitle) @header(title, subtitle, navBarItem)
</header> </header>
<footer> <footer class={"mx-4"}>
@footer() @footer()
</footer> </footer>
</body> </body>

View File

@ -10,7 +10,9 @@ import "context"
import "io" import "io"
import "bytes" import "bytes"
func Manager(config LayoutConfig) templ.Component { import "git.staur.ca/stobbsm/clustvirt/view/components"
func Manager(title string, subtitle string, navBarItem []components.NavItem) templ.Component {
return templ.ComponentFunc(func(ctx context.Context, templ_7745c5c3_W io.Writer) (templ_7745c5c3_Err error) { return templ.ComponentFunc(func(ctx context.Context, templ_7745c5c3_W io.Writer) (templ_7745c5c3_Err error) {
templ_7745c5c3_Buffer, templ_7745c5c3_IsBuffer := templ_7745c5c3_W.(*bytes.Buffer) templ_7745c5c3_Buffer, templ_7745c5c3_IsBuffer := templ_7745c5c3_W.(*bytes.Buffer)
if !templ_7745c5c3_IsBuffer { if !templ_7745c5c3_IsBuffer {
@ -44,7 +46,7 @@ func Manager(config LayoutConfig) templ.Component {
if templ_7745c5c3_Err != nil { if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err return templ_7745c5c3_Err
} }
var templ_7745c5c3_Var3 = []any{} var templ_7745c5c3_Var3 = []any{"mx-4"}
templ_7745c5c3_Err = templ.RenderCSSItems(ctx, templ_7745c5c3_Buffer, templ_7745c5c3_Var3...) templ_7745c5c3_Err = templ.RenderCSSItems(ctx, templ_7745c5c3_Buffer, templ_7745c5c3_Var3...)
if templ_7745c5c3_Err != nil { if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err return templ_7745c5c3_Err
@ -61,11 +63,28 @@ func Manager(config LayoutConfig) templ.Component {
if templ_7745c5c3_Err != nil { if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err return templ_7745c5c3_Err
} }
templ_7745c5c3_Err = header(config.MainTitle, config.SubTitle).Render(ctx, templ_7745c5c3_Buffer) templ_7745c5c3_Err = header(title, subtitle, navBarItem).Render(ctx, templ_7745c5c3_Buffer)
if templ_7745c5c3_Err != nil { if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err return templ_7745c5c3_Err
} }
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString("</header><footer>") _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString("</header>")
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
var templ_7745c5c3_Var4 = []any{"mx-4"}
templ_7745c5c3_Err = templ.RenderCSSItems(ctx, templ_7745c5c3_Buffer, templ_7745c5c3_Var4...)
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString("<footer class=\"")
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ.CSSClasses(templ_7745c5c3_Var4).String()))
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString("\">")
if templ_7745c5c3_Err != nil { if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err return templ_7745c5c3_Err
} }

View File

@ -1,68 +1,41 @@
package layouts package layouts
templ StaticPage(title string, subtitle string) { import "git.staur.ca/stobbsm/clustvirt/view/components"
<!DOCTYPE html>
<html class={ "text-uigrey-100" , "bg-uigrey-900" }>
<head> templ StaticPage(title string, subtitle string, navBarItems []components.NavItem) {
<title>Clustvirt</title> <!DOCTYPE html>
<link href="/static/css/style.css" type="text/css" rel="stylesheet" /> <html class={ "text-uigrey-100" , "bg-uigrey-900" }>
</head> <head>
<title>Clustvirt</title>
<body> <link href="/static/css/style.css" type="text/css" rel="stylesheet"/>
<header> </head>
@header(title, subtitle) <body>
</header> <header>
<div id="content" name="content" class={ "flex" , "flex-col" , "gap-2" }> @header(title, subtitle, navBarItems)
{ children... } </header>
</div> <div id="content" name="content" class={ "flex" , "flex-col" , "gap-2" }>
<footer> { children... }
@footer() </div>
</footer> <footer>
</body> @footer()
</footer>
</html> </body>
</html>
} }
templ header(title string, subtitle string) { templ hero(title string) {
<h2 class={ "text-lg" , "font-semibold" , "italic" , "h-6" }>{ subtitle }</h2> <h1 class={ "text-2xl", "font-bold", "text-uiblue-200", "md:order-1" }>{ title }</h1>
@nav(title)
} }
templ nav(title string) { templ header(title string, subtitle string, navBarItems []components.NavItem) {
<nav class={ "w-100" , "bg-uigrey-600" , "shadow" , "shadow-uigrey-700" , "md:px-auto" }> <h2 class={ "text-lg" , "font-semibold" , "italic" , "h-6" }>{ subtitle }</h2>
<div class={ @components.NavBar(hero(title), navBarItems)
"md:h-16",
"h-28",
"mx-auto",
"md:px-4",
"container",
"flex",
"items-center",
"justify-between",
"flex-wrap",
"md:flex-nowrap" }>
<h1 class={ "text-2xl", "font-bold", "text-uiblue-200", "md:order-1" }>{ title }</h1>
<div class={"text-uigrey-200", "font-semibold", "md:w-auto", "md:order-2", "order-3"}>
<ul class={ "flex", "justify-between", "sm:w-full", "md:w-auto" }>
<li class={ "hover:text-uiblue-200", "md:px-4", "md:py-2" }><a href="#">What</a></li>
<li class={ "hover:text-uiblue-200", "md:px-4", "md:py-2" }><a href="#">Why</a></li>
<li class={ "hover:text-uiblue-200", "md:px-4", "md:py-2" }><a href="#">Goals</a></li>
<li class={ "hover:text-uiblue-200", "md:px-4", "md:py-2" }><a href="#">Stretch</a></li>
<li class={ "hover:text-uiblue-200", "md:px-4", "md:py-2" }><a href="#">Requests</a></li>
<li class={ "hover:text-uiblue-200", "md:px-4", "md:py-2" }><a href="#">Never</a></li>
<li class={ "hover:text-uiblue-200", "md:px-4", "md:py-2" }><a href="#">Notes</a></li>
</ul>
</div>
<div class={"order-2", "md:order-3"}>Login</div>
</div>
</nav>
} }
templ footer() { templ footer() {
<div class="flex gap-4 md:gap-6 sm:gap-8 divide divide-solid"> <div class="flex gap-4 md:gap-6 sm:gap-8 divide divide-solid">
<div id="footer_left" class="flex-auto basis-1/4 md:basis-1/3 p-2">Left</div> <div id="footer_left" class="flex-auto basis-1/4 md:basis-1/3 p-2">Left</div>
<div id="footer_middle" class="flex-auto basis-1/2 md:basis-1/3 p-2">Middle</div> <div id="footer_middle" class="flex-auto basis-1/2 md:basis-1/3 p-2">Middle</div>
<div id="footer_right" class="flex-auto basis-1/4 md:basis-1/3 p-2">Right</div> <div id="footer_right" class="flex-auto basis-1/4 md:basis-1/3 p-2">Right</div>
</div> </div>
} }

View File

@ -10,7 +10,9 @@ import "context"
import "io" import "io"
import "bytes" import "bytes"
func StaticPage(title string, subtitle string) templ.Component { import "git.staur.ca/stobbsm/clustvirt/view/components"
func StaticPage(title string, subtitle string, navBarItems []components.NavItem) templ.Component {
return templ.ComponentFunc(func(ctx context.Context, templ_7745c5c3_W io.Writer) (templ_7745c5c3_Err error) { return templ.ComponentFunc(func(ctx context.Context, templ_7745c5c3_W io.Writer) (templ_7745c5c3_Err error) {
templ_7745c5c3_Buffer, templ_7745c5c3_IsBuffer := templ_7745c5c3_W.(*bytes.Buffer) templ_7745c5c3_Buffer, templ_7745c5c3_IsBuffer := templ_7745c5c3_W.(*bytes.Buffer)
if !templ_7745c5c3_IsBuffer { if !templ_7745c5c3_IsBuffer {
@ -44,7 +46,7 @@ func StaticPage(title string, subtitle string) templ.Component {
if templ_7745c5c3_Err != nil { if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err return templ_7745c5c3_Err
} }
templ_7745c5c3_Err = header(title, subtitle).Render(ctx, templ_7745c5c3_Buffer) templ_7745c5c3_Err = header(title, subtitle, navBarItems).Render(ctx, templ_7745c5c3_Buffer)
if templ_7745c5c3_Err != nil { if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err return templ_7745c5c3_Err
} }
@ -92,7 +94,7 @@ func StaticPage(title string, subtitle string) templ.Component {
}) })
} }
func header(title string, subtitle string) templ.Component { func hero(title string) templ.Component {
return templ.ComponentFunc(func(ctx context.Context, templ_7745c5c3_W io.Writer) (templ_7745c5c3_Err error) { return templ.ComponentFunc(func(ctx context.Context, templ_7745c5c3_W io.Writer) (templ_7745c5c3_Err error) {
templ_7745c5c3_Buffer, templ_7745c5c3_IsBuffer := templ_7745c5c3_W.(*bytes.Buffer) templ_7745c5c3_Buffer, templ_7745c5c3_IsBuffer := templ_7745c5c3_W.(*bytes.Buffer)
if !templ_7745c5c3_IsBuffer { if !templ_7745c5c3_IsBuffer {
@ -105,12 +107,12 @@ func header(title string, subtitle string) templ.Component {
templ_7745c5c3_Var4 = templ.NopComponent templ_7745c5c3_Var4 = templ.NopComponent
} }
ctx = templ.ClearChildren(ctx) ctx = templ.ClearChildren(ctx)
var templ_7745c5c3_Var5 = []any{"text-lg", "font-semibold", "italic", "h-6"} var templ_7745c5c3_Var5 = []any{"text-2xl", "font-bold", "text-uiblue-200", "md:order-1"}
templ_7745c5c3_Err = templ.RenderCSSItems(ctx, templ_7745c5c3_Buffer, templ_7745c5c3_Var5...) templ_7745c5c3_Err = templ.RenderCSSItems(ctx, templ_7745c5c3_Buffer, templ_7745c5c3_Var5...)
if templ_7745c5c3_Err != nil { if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err return templ_7745c5c3_Err
} }
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString("<h2 class=\"") _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString("<h1 class=\"")
if templ_7745c5c3_Err != nil { if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err return templ_7745c5c3_Err
} }
@ -123,19 +125,15 @@ func header(title string, subtitle string) templ.Component {
return templ_7745c5c3_Err return templ_7745c5c3_Err
} }
var templ_7745c5c3_Var6 string var templ_7745c5c3_Var6 string
templ_7745c5c3_Var6, templ_7745c5c3_Err = templ.JoinStringErrs(subtitle) templ_7745c5c3_Var6, templ_7745c5c3_Err = templ.JoinStringErrs(title)
if templ_7745c5c3_Err != nil { if templ_7745c5c3_Err != nil {
return templ.Error{Err: templ_7745c5c3_Err, FileName: `view/layouts/staticpage.templ`, Line: 27, Col: 71} return templ.Error{Err: templ_7745c5c3_Err, FileName: `view/layouts/staticpage.templ`, Line: 26, Col: 80}
} }
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var6)) _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var6))
if templ_7745c5c3_Err != nil { if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err return templ_7745c5c3_Err
} }
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString("</h2>") _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString("</h1>")
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
templ_7745c5c3_Err = nav(title).Render(ctx, templ_7745c5c3_Buffer)
if templ_7745c5c3_Err != nil { if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err return templ_7745c5c3_Err
} }
@ -146,7 +144,7 @@ func header(title string, subtitle string) templ.Component {
}) })
} }
func nav(title string) templ.Component { func header(title string, subtitle string, navBarItems []components.NavItem) templ.Component {
return templ.ComponentFunc(func(ctx context.Context, templ_7745c5c3_W io.Writer) (templ_7745c5c3_Err error) { return templ.ComponentFunc(func(ctx context.Context, templ_7745c5c3_W io.Writer) (templ_7745c5c3_Err error) {
templ_7745c5c3_Buffer, templ_7745c5c3_IsBuffer := templ_7745c5c3_W.(*bytes.Buffer) templ_7745c5c3_Buffer, templ_7745c5c3_IsBuffer := templ_7745c5c3_W.(*bytes.Buffer)
if !templ_7745c5c3_IsBuffer { if !templ_7745c5c3_IsBuffer {
@ -159,12 +157,12 @@ func nav(title string) templ.Component {
templ_7745c5c3_Var7 = templ.NopComponent templ_7745c5c3_Var7 = templ.NopComponent
} }
ctx = templ.ClearChildren(ctx) ctx = templ.ClearChildren(ctx)
var templ_7745c5c3_Var8 = []any{"w-100", "bg-uigrey-600", "shadow", "shadow-uigrey-700", "md:px-auto"} var templ_7745c5c3_Var8 = []any{"text-lg", "font-semibold", "italic", "h-6"}
templ_7745c5c3_Err = templ.RenderCSSItems(ctx, templ_7745c5c3_Buffer, templ_7745c5c3_Var8...) templ_7745c5c3_Err = templ.RenderCSSItems(ctx, templ_7745c5c3_Buffer, templ_7745c5c3_Var8...)
if templ_7745c5c3_Err != nil { if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err return templ_7745c5c3_Err
} }
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString("<nav class=\"") _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString("<h2 class=\"")
if templ_7745c5c3_Err != nil { if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err return templ_7745c5c3_Err
} }
@ -176,230 +174,20 @@ func nav(title string) templ.Component {
if templ_7745c5c3_Err != nil { if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err return templ_7745c5c3_Err
} }
var templ_7745c5c3_Var9 = []any{ var templ_7745c5c3_Var9 string
"md:h-16", templ_7745c5c3_Var9, templ_7745c5c3_Err = templ.JoinStringErrs(subtitle)
"h-28", if templ_7745c5c3_Err != nil {
"mx-auto", return templ.Error{Err: templ_7745c5c3_Err, FileName: `view/layouts/staticpage.templ`, Line: 30, Col: 72}
"md:px-4", }
"container", _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var9))
"flex",
"items-center",
"justify-between",
"flex-wrap",
"md:flex-nowrap"}
templ_7745c5c3_Err = templ.RenderCSSItems(ctx, templ_7745c5c3_Buffer, templ_7745c5c3_Var9...)
if templ_7745c5c3_Err != nil { if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err return templ_7745c5c3_Err
} }
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString("<div class=\"") _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString("</h2>")
if templ_7745c5c3_Err != nil { if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err return templ_7745c5c3_Err
} }
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ.CSSClasses(templ_7745c5c3_Var9).String())) templ_7745c5c3_Err = components.NavBar(hero(title), navBarItems).Render(ctx, templ_7745c5c3_Buffer)
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString("\">")
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
var templ_7745c5c3_Var10 = []any{"text-2xl", "font-bold", "text-uiblue-200", "md:order-1"}
templ_7745c5c3_Err = templ.RenderCSSItems(ctx, templ_7745c5c3_Buffer, templ_7745c5c3_Var10...)
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString("<h1 class=\"")
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ.CSSClasses(templ_7745c5c3_Var10).String()))
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString("\">")
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
var templ_7745c5c3_Var11 string
templ_7745c5c3_Var11, templ_7745c5c3_Err = templ.JoinStringErrs(title)
if templ_7745c5c3_Err != nil {
return templ.Error{Err: templ_7745c5c3_Err, FileName: `view/layouts/staticpage.templ`, Line: 44, Col: 82}
}
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var11))
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString("</h1>")
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
var templ_7745c5c3_Var12 = []any{"text-uigrey-200", "font-semibold", "md:w-auto", "md:order-2", "order-3"}
templ_7745c5c3_Err = templ.RenderCSSItems(ctx, templ_7745c5c3_Buffer, templ_7745c5c3_Var12...)
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString("<div class=\"")
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ.CSSClasses(templ_7745c5c3_Var12).String()))
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString("\">")
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
var templ_7745c5c3_Var13 = []any{"flex", "justify-between", "sm:w-full", "md:w-auto"}
templ_7745c5c3_Err = templ.RenderCSSItems(ctx, templ_7745c5c3_Buffer, templ_7745c5c3_Var13...)
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString("<ul class=\"")
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ.CSSClasses(templ_7745c5c3_Var13).String()))
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString("\">")
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
var templ_7745c5c3_Var14 = []any{"hover:text-uiblue-200", "md:px-4", "md:py-2"}
templ_7745c5c3_Err = templ.RenderCSSItems(ctx, templ_7745c5c3_Buffer, templ_7745c5c3_Var14...)
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString("<li class=\"")
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ.CSSClasses(templ_7745c5c3_Var14).String()))
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString("\"><a href=\"#\">What</a></li>")
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
var templ_7745c5c3_Var15 = []any{"hover:text-uiblue-200", "md:px-4", "md:py-2"}
templ_7745c5c3_Err = templ.RenderCSSItems(ctx, templ_7745c5c3_Buffer, templ_7745c5c3_Var15...)
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString("<li class=\"")
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ.CSSClasses(templ_7745c5c3_Var15).String()))
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString("\"><a href=\"#\">Why</a></li>")
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
var templ_7745c5c3_Var16 = []any{"hover:text-uiblue-200", "md:px-4", "md:py-2"}
templ_7745c5c3_Err = templ.RenderCSSItems(ctx, templ_7745c5c3_Buffer, templ_7745c5c3_Var16...)
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString("<li class=\"")
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ.CSSClasses(templ_7745c5c3_Var16).String()))
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString("\"><a href=\"#\">Goals</a></li>")
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
var templ_7745c5c3_Var17 = []any{"hover:text-uiblue-200", "md:px-4", "md:py-2"}
templ_7745c5c3_Err = templ.RenderCSSItems(ctx, templ_7745c5c3_Buffer, templ_7745c5c3_Var17...)
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString("<li class=\"")
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ.CSSClasses(templ_7745c5c3_Var17).String()))
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString("\"><a href=\"#\">Stretch</a></li>")
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
var templ_7745c5c3_Var18 = []any{"hover:text-uiblue-200", "md:px-4", "md:py-2"}
templ_7745c5c3_Err = templ.RenderCSSItems(ctx, templ_7745c5c3_Buffer, templ_7745c5c3_Var18...)
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString("<li class=\"")
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ.CSSClasses(templ_7745c5c3_Var18).String()))
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString("\"><a href=\"#\">Requests</a></li>")
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
var templ_7745c5c3_Var19 = []any{"hover:text-uiblue-200", "md:px-4", "md:py-2"}
templ_7745c5c3_Err = templ.RenderCSSItems(ctx, templ_7745c5c3_Buffer, templ_7745c5c3_Var19...)
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString("<li class=\"")
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ.CSSClasses(templ_7745c5c3_Var19).String()))
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString("\"><a href=\"#\">Never</a></li>")
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
var templ_7745c5c3_Var20 = []any{"hover:text-uiblue-200", "md:px-4", "md:py-2"}
templ_7745c5c3_Err = templ.RenderCSSItems(ctx, templ_7745c5c3_Buffer, templ_7745c5c3_Var20...)
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString("<li class=\"")
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ.CSSClasses(templ_7745c5c3_Var20).String()))
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString("\"><a href=\"#\">Notes</a></li></ul></div>")
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
var templ_7745c5c3_Var21 = []any{"order-2", "md:order-3"}
templ_7745c5c3_Err = templ.RenderCSSItems(ctx, templ_7745c5c3_Buffer, templ_7745c5c3_Var21...)
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString("<div class=\"")
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ.CSSClasses(templ_7745c5c3_Var21).String()))
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString("\">Login</div></div></nav>")
if templ_7745c5c3_Err != nil { if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err return templ_7745c5c3_Err
} }
@ -418,9 +206,9 @@ func footer() templ.Component {
defer templ.ReleaseBuffer(templ_7745c5c3_Buffer) defer templ.ReleaseBuffer(templ_7745c5c3_Buffer)
} }
ctx = templ.InitializeContext(ctx) ctx = templ.InitializeContext(ctx)
templ_7745c5c3_Var22 := templ.GetChildren(ctx) templ_7745c5c3_Var10 := templ.GetChildren(ctx)
if templ_7745c5c3_Var22 == nil { if templ_7745c5c3_Var10 == nil {
templ_7745c5c3_Var22 = templ.NopComponent templ_7745c5c3_Var10 = templ.NopComponent
} }
ctx = templ.ClearChildren(ctx) ctx = templ.ClearChildren(ctx)
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString("<div class=\"flex gap-4 md:gap-6 sm:gap-8 divide divide-solid\"><div id=\"footer_left\" class=\"flex-auto basis-1/4 md:basis-1/3 p-2\">Left</div><div id=\"footer_middle\" class=\"flex-auto basis-1/2 md:basis-1/3 p-2\">Middle</div><div id=\"footer_right\" class=\"flex-auto basis-1/4 md:basis-1/3 p-2\">Right</div></div>") _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString("<div class=\"flex gap-4 md:gap-6 sm:gap-8 divide divide-solid\"><div id=\"footer_left\" class=\"flex-auto basis-1/4 md:basis-1/3 p-2\">Left</div><div id=\"footer_middle\" class=\"flex-auto basis-1/2 md:basis-1/3 p-2\">Middle</div><div id=\"footer_right\" class=\"flex-auto basis-1/4 md:basis-1/3 p-2\">Right</div></div>")

View File

@ -1,249 +1,257 @@
package static package static
import ( import (
"git.staur.ca/stobbsm/clustvirt/view/layouts" "git.staur.ca/stobbsm/clustvirt/view/layouts"
"git.staur.ca/stobbsm/clustvirt/view/components" "git.staur.ca/stobbsm/clustvirt/view/components"
) )
templ Home() { templ Home() {
@layouts.StaticPage("ClustVirt", "Libvirt, clustered and managed") { @layouts.StaticPage("ClustVirt", "Libvirt, clustered and managed", []components.NavItem{
@components.FlexRow(true) { {Name: "What", Href: "#"},
<div class={ "basis-1/3" }> {Name: "Why", Href: "#"},
@components.InfoBox("What is This?", "uiorange") { {Name: "Goals", Href: "#"},
@components.ContentP() { {Name: "Stretch", Href: "#"},
Clustvirt (work in progress name) aims to be the agnostic {Name: "Requests", Href: "#"},
cluster controller for libvirtd. The server component is {Name: "Never", Href: "#"},
used to display both the WebUI and run the REST API used to {Name: "Notes", Href: "#"},
control one to many libvirtd hosts to manage virual machines, }) {
LXC containers (through libvirtd), gather information about <div>
each host, and monitor each host. @components.InfoBox("What is This?", "uiorange") {
} <div class={"mx-4"}>
} @components.ContentP() {
</div> Clustvirt (work in progress name) aims to be the agnostic
<div class={ "basis-2/3" }> cluster controller for libvirtd. The server component is
@components.InfoBox("Why?", "uiblue") { used to display both the WebUI and run the REST API used to
@components.List("informational") { control one to many libvirtd hosts to manage virual machines,
@components.ListItem() { LXC containers (through libvirtd), gather information about
Broadcom buying VMWare, and VMWare losing a free each host, and monitor each host.
teir for homelabbers pissed me off }
} </div>
@components.ListItem() { }
Vendor lock-in pisses me off </div>
} <div>
@components.ListItem() { @components.InfoBox("Why?", "uiblue") {
Even good open source Hyperconverged systems @components.List("informational") {
(Proxmox, as an example) exhibit a form of vendor lock-in @components.ListItem() {
} Broadcom buying VMWare, and VMWare losing a free
@components.ListItem() { teir for homelabbers pissed me off
Libvirt is terrific, has the functionality for everything }
those other providers do, but there really is not a great @components.ListItem() {
option for those dipping their toes into Open Source Vendor lock-in pisses me off
} }
@components.ListItem() { @components.ListItem() {
Its fun to build things that solve a need Even good open source Hyperconverged systems
} (Proxmox, as an example) exhibit a form of vendor lock-in
@components.ListItem() { }
I really want to do it @components.ListItem() {
} Libvirt is terrific, has the functionality for everything
} those other providers do, but there really is not a great
} option for those dipping their toes into Open Source
</div> }
} @components.ListItem() {
@components.FlexRow(true) { Its fun to build things that solve a need
<div class={ "basis-7/12" }> }
@components.InfoBox("Project Goals", "uigreen") { @components.ListItem() {
@components.List("accepted") { I really want to do it
@components.ListItem() { }
Open source, currently on the MIT license }
} }
@components.ListItem() { </div>
Base OS Agnostic. If it can run libvirtd, this should be able to control it on some level <div>
} @components.InfoBox("Project Goals", "uigreen") {
@components.ListItem() { @components.List("accepted") {
Control the Virtual Machine life cycle on one or more libvirtd hosts @components.ListItem() {
} Open source, currently on the MIT license
@components.ListItem() { }
Add clusting capabilities to libvirtd host, including; @components.ListItem() {
} Base OS Agnostic. If it can run libvirtd, this should be able to control it on some level
@components.ListItem() { }
Migration of VMs @components.ListItem() {
} Control the Virtual Machine life cycle on one or more libvirtd hosts
@components.ListItem() { }
Syncronizing secrets @components.ListItem() {
} Add clusting capabilities to libvirtd host, including;
@components.ListItem() { }
Syncronizing VLANs, bridges, host only networking @components.ListItem() {
} Migration of VMs
@components.ListItem() { }
Sharing HA storage availability @components.ListItem() {
} Syncronizing secrets
@components.ListItem() { }
Locking shared resources like disks @components.ListItem() {
} Syncronizing VLANs, bridges, host only networking
@components.ListItem() { }
Starting VMs marked for HA on another host when one goes down @components.ListItem() {
} Sharing HA storage availability
@components.ListItem() { }
Manage a library of Cloud-init resources and templates to build new VMs quickly @components.ListItem() {
} Locking shared resources like disks
@components.ListItem() { }
Local Storage management, including local directory, lvm, zfs (if installed) @components.ListItem() {
} Starting VMs marked for HA on another host when one goes down
@components.ListItem() { }
Advanced Storage management, such as Ceph, glusterfs, drbd, iscsi, nfs @components.ListItem() {
} Manage a library of Cloud-init resources and templates to build new VMs quickly
@components.ListItem() { }
Storage syncronization of local disks between hosts (zfs snapshots, lvm snapshots, rsync) @components.ListItem() {
} Local Storage management, including local directory, lvm, zfs (if installed)
@components.ListItem() { }
Backup scheduling, creation, restoration @components.ListItem() {
} Advanced Storage management, such as Ceph, glusterfs, drbd, iscsi, nfs
} }
} @components.ListItem() {
</div> Storage syncronization of local disks between hosts (zfs snapshots, lvm snapshots, rsync)
<div class={ "basis-5/12" }> }
@components.InfoBox("Stretch Goals", "uipurple") { @components.ListItem() {
@components.List("possible") { Backup scheduling, creation, restoration
@components.ListItem() { }
Install the OS which libvirtd is running on }
} }
@components.ListItem() { </div>
Install/provision libvirtd on a host that does not have it installed <div>
} @components.InfoBox("Stretch Goals", "uipurple") {
@components.ListItem() { @components.List("possible") {
Tools to move from one vendor to clustvirt/libvirtd @components.ListItem() {
} Install the OS which libvirtd is running on
@components.ListItem() { }
VM templates for common aspects of VM creation and management, @components.ListItem() {
like appliances Install/provision libvirtd on a host that does not have it installed
} }
@components.ListItem() { @components.ListItem() {
External tool access that can be used to manage things that are not Tools to move from one vendor to clustvirt/libvirtd
managed here (cephadm dashboard, for instance) }
} @components.ListItem() {
} VM templates for common aspects of VM creation and management,
} like appliances
</div> }
} @components.ListItem() {
@components.FlexRow(true) { External tool access that can be used to manage things that are not
<div class={ "basis-8/12" }> managed here (cephadm dashboard, for instance)
@components.InfoBox("Reddit Requested Feature", "uiyellow") { }
@components.List("possible") { }
@components.ListItem() { }
Search/Filter on hosts/vms - @Lopsided_Speaker_553 </div>
} <div>
@components.ListItem() { @components.InfoBox("Reddit Requested Feature", "uiyellow") {
Balance on resource usage per host/Automattically migrate to least @components.List("possible") {
used host - @Lopsided_Speaker_553 @components.ListItem() {
} Search/Filter on hosts/vms - @Lopsided_Speaker_553
@components.ListItem() { }
Support inter-vm only commmunication (VxLAN style) - @Lopsided_Speaker_553 @components.ListItem() {
} Balance on resource usage per host/Automattically migrate to least
@components.ListItem() { used host - @Lopsided_Speaker_553
Deploy VMs using only API - @Lopsided_Speaker_553 }
} @components.ListItem() {
@components.ListItem() { Support inter-vm only commmunication (VxLAN style) - @Lopsided_Speaker_553
Well documented, first class API - @kasperlitheater }
} @components.ListItem() {
@components.ListItem() { Deploy VMs using only API - @Lopsided_Speaker_553
Bootstrap service to configure a new server - @phatpappa_ }
} @components.ListItem() {
@components.ListItem() { Well documented, first class API - @kasperlitheater
For the love of kitten, don't use XML as configuration files - @pascalbrax }
} @components.ListItem() {
@components.ListItem() { Bootstrap service to configure a new server - @phatpappa_
Expose the Cluster Manager functionalities as API - @raven2611 }
} @components.ListItem() {
@components.ListItem() { For the love of kitten, don't use XML as configuration files - @pascalbrax
CPU architecture awareness for migrations - @raven2611 }
} @components.ListItem() {
@components.ListItem() { Expose the Cluster Manager functionalities as API - @raven2611
Inter VM Communications via VXLAN/EVPN - @raven2611 }
} @components.ListItem() {
} CPU architecture awareness for migrations - @raven2611
} }
</div> @components.ListItem() {
<div class={ "basis-4/12" }> Inter VM Communications via VXLAN/EVPN - @raven2611
@components.InfoBox("Never Going to Happen", "uired") { }
@components.List("never") { }
@components.ListItem() { }
Kubernetes </div>
} <div class={ }>
@components.ListItem() { @components.InfoBox("Never Going to Happen", "uired") {
Application container management (docker, podman, etc) @components.List("never") {
} @components.ListItem() {
@components.ListItem() { Kubernetes
Become an OS }
} @components.ListItem() {
@components.ListItem() { Application container management (docker, podman, etc)
Have a paywall }
} @components.ListItem() {
@components.ListItem() { Become an OS
Vendor lock-in }
} @components.ListItem() {
@components.ListItem() { Have a paywall
Become a commercial entity (even indirectly) }
} @components.ListItem() {
@components.ListItem() { Vendor lock-in
Anything that does not have an Open Source standard behind it }
} @components.ListItem() {
@components.ListItem() { Become a commercial entity (even indirectly)
Directly control a guest Operating System }
} @components.ListItem() {
} Anything that does not have an Open Source standard behind it
} }
</div> @components.ListItem() {
} Directly control a guest Operating System
@components.InfoBox("Other things to note", "uipink") { }
@components.ContentP() { }
I recently created a <span>@components.ANewTab("http://redd.it/1bct15z", "post") }
</span> </div>
on reddit announcing that I was building this, and while the majority @components.InfoBox("Other things to note", "uipink") {
of responses were supportive, even offering features that may enhance <div class={"flex", "flex-col", "gap-4", "mx-4"}>
what I originally set out to do, many responded with @components.ContentP() {
"Why do we need another one??" I recently created a
} <span>
@components.ContentP() { @components.ANewTab("http://redd.it/1bct15z", "post")
Besides the list above about why this exists, I wanted to clarify </span>
a few things those individuals did not seeem to get: This is not on reddit announcing that I was building this, and while the majority
a rebuild of Proxmox, Cloudstack, VMWare, Harvester or any of of responses were supportive, even offering features that may enhance
the other "Hyper-converged/Single-solution/turnkey/Operating System" what I originally set out to do, many responded with
offerings out there. This will not take over your base operating system "Why do we need another one??"
on your machine, just act as a cluster manager and interface to access }
the existing libvirtd instances on those machines, nor will it prescribe @components.ContentP() {
a set of requirements that make it hard to move your own infrastructure Besides the list above about why this exists, I wanted to clarify
around. a few things those individuals did not seeem to get: This is not
} a rebuild of Proxmox, Cloudstack, VMWare, Harvester or any of
@components.ContentP() { the other "Hyper-converged/Single-solution/turnkey/Operating System"
At the heart of this project is that I hate the enshitifiation of offerings out there. This will not take over your base operating system
Open Source that has been going on, where its just another way to make on your machine, just act as a cluster manager and interface to access
money and control the eco system. RedHat tried to do it by locking the existing libvirtd instances on those machines, nor will it prescribe
down their source code, Proxmox does it by making sure anything you a set of requirements that make it hard to move your own infrastructure
do on Proxmox is tied to Proxmox (no offense to Proxmox), and even around.
Hashicorp, who I loved so dearly, changed from a pure Open Source }
licensing model to one that protects the business over the community @components.ContentP() {
} At the heart of this project is that I hate the enshitifiation of
@components.ContentP() { Open Source that has been going on, where its just another way to make
I will not let that happen here. money and control the eco system. RedHat tried to do it by locking
} down their source code, Proxmox does it by making sure anything you
@components.ContentP() { do on Proxmox is tied to Proxmox (no offense to Proxmox), and even
This project will seek to use the Unix philosophy, of building off Hashicorp, who I loved so dearly, changed from a pure Open Source
of existing standards, combining tools, and having one tool do one licensing model to one that protects the business over the community
job well. This does not mean there will be one application for each }
aspect of the job, but that this application stack will manage Libvirtd @components.ContentP() {
well, and have individual and configurable paths to manage each sub-aspect I will not let that happen here.
of the libvirt stack. This stack will not create a Ceph cluster for you, }
it leaves you to do that. It will not even talk to a ceph cluster. @components.ContentP() {
It will, however, let you add that cluster via configuration options to This project will seek to use the Unix philosophy, of building off
define it as a storage pool that libvirt can use. of existing standards, combining tools, and having one tool do one
} job well. This does not mean there will be one application for each
@components.ContentP() { aspect of the job, but that this application stack will manage Libvirtd
If you want something that will allow you to use a single interface well, and have individual and configurable paths to manage each sub-aspect
to create all sub aspects that can be used by libvirt of the libvirt stack. This stack will not create a Ceph cluster for you,
(managing all firewall rules, creating a ceph cluster, etc.), it leaves you to do that. It will not even talk to a ceph cluster.
use something like Proxmox which includes that builtin It will, however, let you add that cluster via configuration options to
functionality. This isn't the stack for you. define it as a storage pool that libvirt can use.
} }
} @components.ContentP() {
} If you want something that will allow you to use a single interface
to create all sub aspects that can be used by libvirt
(managing all firewall rules, creating a ceph cluster, etc.),
use something like Proxmox which includes that builtin
functionality. This isn't the stack for you.
}
</div>
}
}
} }

File diff suppressed because it is too large Load Diff