diff options
author | Julien Dessaux | 2025-01-26 00:04:38 +0100 |
---|---|---|
committer | Julien Dessaux | 2025-01-26 00:04:38 +0100 |
commit | 4e029fb83a1e70495330eaac94981a97de24682e (patch) | |
tree | fb041970dcbf38daf06de5034cf6fd567c586069 /pkg | |
parent | feat(webui): implement states list (diff) | |
download | tfstated-4e029fb83a1e70495330eaac94981a97de24682e.tar.gz tfstated-4e029fb83a1e70495330eaac94981a97de24682e.tar.bz2 tfstated-4e029fb83a1e70495330eaac94981a97de24682e.zip |
feat(webui): bootstrap a proper UI
Diffstat (limited to 'pkg')
-rw-r--r-- | pkg/webui/error.go | 2 | ||||
-rw-r--r-- | pkg/webui/html/base.html | 54 | ||||
-rw-r--r-- | pkg/webui/html/error.html | 8 | ||||
-rw-r--r-- | pkg/webui/html/login.html | 51 | ||||
-rw-r--r-- | pkg/webui/html/logout.html | 6 | ||||
-rw-r--r-- | pkg/webui/html/states.html | 51 | ||||
-rw-r--r-- | pkg/webui/index.go | 5 | ||||
-rw-r--r-- | pkg/webui/login.go | 6 | ||||
-rw-r--r-- | pkg/webui/logout.go | 7 | ||||
-rw-r--r-- | pkg/webui/states.go | 2 | ||||
-rw-r--r-- | pkg/webui/static/main.css | 10 |
11 files changed, 127 insertions, 75 deletions
diff --git a/pkg/webui/error.go b/pkg/webui/error.go index 05d8e9e..afce9a6 100644 --- a/pkg/webui/error.go +++ b/pkg/webui/error.go @@ -9,11 +9,13 @@ var errorTemplates = template.Must(template.ParseFS(htmlFS, "html/base.html", "h func errorResponse(w http.ResponseWriter, status int, err error) { type ErrorData struct { + Page Err error Status int StatusText string } render(w, errorTemplates, status, &ErrorData{ + Page: Page{Title: "Error", Section: "error"}, Err: err, Status: status, StatusText: http.StatusText(status), diff --git a/pkg/webui/html/base.html b/pkg/webui/html/base.html index 7821d28..06328e4 100644 --- a/pkg/webui/html/base.html +++ b/pkg/webui/html/base.html @@ -1,28 +1,50 @@ +{{ define "nav" }} +<header> + <nav> + <a href="/"> + <h6>TFSTATED</h6> + </a> + </nav> +</header> +{{ if eq .Page.Section "login" }} +<a href="/login" class="active"> + <i>login</i> + <span>Login</span> +</a> +{{ else }} +<a href="/states"{{ if eq .Page.Section "states" }} class="active"{{ end}}> + <i>home_storage</i> + <span>States</span> +</a> +<hr> +<a href="/logout"> + <i>logout</i> + <span>Logout</span> +</a> +{{ end }} +{{ end }} <!DOCTYPE html> -<html> +<html lang="en"> <head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1"> <link rel="icon" href="/static/favicon.svg"> - <link rel="stylesheet" href="/static/main.css"> - <link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/@picocss/pico@2/css/pico.min.css"> - <title>tfstated</title> + <link href="/static/main.css" rel="stylesheet"> + <link href="https://cdn.jsdelivr.net/npm/beercss@3.8.0/dist/cdn/beer.min.css" rel="stylesheet"> + <title>TFSTATED - {{ .Page.Title }}</title> </head> - <body> - <header class="container"> + <body class="dark"> + <nav class="left drawer l">{{ template "nav" . }}</nav> + <nav class="left m">{{ template "nav" . }}</nav> + <nav class="bottom s">{{ template "nav" . }}</nav> + <header> <nav> - <ul> - <li><a href="/"><strong>TFSTATED</strong></a></li> - </ul> - <ul> - <li><a href="/states">States</a></li> - </ul> + <h5 class="max center-align">{{ .Page.Title }}</h5> </nav> </header> - <main class="container"> - {{ template "main" . }} - </main> - <footer class="container"> + {{ template "main" . }} + <footer> </footer> + <script type="module" src="https://cdn.jsdelivr.net/npm/beercss@3.8.0/dist/cdn/beer.min.js"></script> </body> </html> diff --git a/pkg/webui/html/error.html b/pkg/webui/html/error.html index d2ea7e1..cda21f6 100644 --- a/pkg/webui/html/error.html +++ b/pkg/webui/html/error.html @@ -1,6 +1,6 @@ {{ define "main" }} -<article> - <h1>{{ .Status }} - {{ .StatusText }}</h1> - <p>{{ .Err }}</p> -</article> +<main class="responsive"> +<h5>{{ .Status }} - {{ .StatusText }}</h5> +<p>{{ .Err }}</p> +</main> {{ end }} diff --git a/pkg/webui/html/login.html b/pkg/webui/html/login.html index 0c2a167..4ff0265 100644 --- a/pkg/webui/html/login.html +++ b/pkg/webui/html/login.html @@ -1,29 +1,26 @@ {{ define "main" }} -{{ if .Forbidden }} -<article> - <p class="error-message">Invalid username or password</p> -</article> -{{ end }} -<form action="/login" method="post"> - <fieldset> - <label> - Username - <input type="text" - placeholder="Username" - name="username" - value="{{ .Username }}" - {{ if .Forbidden }}aria-invalid="true"{{ end }} - required> - </label> - <label> - Password - <input type="password" - placeholder="Password" - name="password" - {{ if .Forbidden }}aria-invalid="true"{{ end }} - required> - </label> - </fieldset> - <button type="submit" value="login">Login</button> -</form> +<main class="responsive"> + <form action="/login" method="post"> + <fieldset> + <div class="field border label{{ if .Forbidden }} invalid{{ end}}"> + <input id="username" + name="username" + type="text" + value="{{ .Username }}" + required> + <label for="username">Username</label> + {{ if .Forbidden }}<span class="error">Invalid username or password</span>{{ end }} + </div> + <div class="field border label{{ if .Forbidden }} invalid{{ end}}"> + <input id="password" + name="password" + type="password" + required> + <label for="password">Password</label> + {{ if .Forbidden }}<span class="error">Invalid username or password</span>{{ end }} + </div> + <button class="small-round" type="submit" value="login">Login</button> + </fieldset> + </form> +</main> {{ end }} diff --git a/pkg/webui/html/logout.html b/pkg/webui/html/logout.html index 58191c0..e9203d4 100644 --- a/pkg/webui/html/logout.html +++ b/pkg/webui/html/logout.html @@ -1,5 +1,5 @@ {{ define "main" }} -<article> - <p>Logout successful</p> -</article> +<main class="responsive"> + <h5>Logout successful</h5> +</main> {{ end }} diff --git a/pkg/webui/html/states.html b/pkg/webui/html/states.html index 8d3d803..f61b87c 100644 --- a/pkg/webui/html/states.html +++ b/pkg/webui/html/states.html @@ -1,23 +1,32 @@ {{ define "main" }} -<h1>States</h1> -<table class="striped"> - <thead> - <tr> - <th scope="col">Path</th> - <th scope="col">Created</th> - <th scope="col">Updated</th> - <th scope="col">Locked</th> - </tr> - </thead> - <tbody> - {{ range .States }} - <tr> - <th scope="row">{{ .Path }}</th> - <td>{{ .Created }}</td> - <td>{{ .Updated }}</td> - <td>{{ .Lock }}</td> - </tr> - {{ end }} - </tbody> -</table> +<main class="responsive" id="main"> + <table class="clickable-rows no-space stripes"> + <thead> + <tr> + <th>Path</th> + <th>Updated</th> + <th>Locked</th> + </tr> + </thead> + <tbody> + {{ range .States }} + <tr> + <td><a href="/state/{{ .Id }}">{{ .Path }}</a></td> + <td><a href="/state/{{ .Id }}">{{ .Updated }}</a></td> + <td> + <a href="/state/{{ .Id }}"> + {{ if eq .Lock nil }}no{{ else }} + <span>yes</span> + <div class="tooltip left max"> + <b>Lock</b> + <p>{{ .Lock }}</p> + </div> + {{ end }} + </a> + </td> + </tr> + {{ end }} + </tbody> + </table> +</main> {{ end }} diff --git a/pkg/webui/index.go b/pkg/webui/index.go index 3a52fc1..deea5d1 100644 --- a/pkg/webui/index.go +++ b/pkg/webui/index.go @@ -5,6 +5,11 @@ import ( "net/http" ) +type Page struct { + Section string + Title string +} + func handleIndexGET() http.Handler { return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { if r.URL.Path == "/" { diff --git a/pkg/webui/login.go b/pkg/webui/login.go index 8444e2c..6f688c1 100644 --- a/pkg/webui/login.go +++ b/pkg/webui/login.go @@ -14,6 +14,7 @@ import ( var loginTemplate = template.Must(template.ParseFS(htmlFS, "html/base.html", "html/login.html")) type loginPage struct { + Page Forbidden bool Username string } @@ -28,7 +29,9 @@ func handleLoginGET() http.Handler { return } - render(w, loginTemplate, http.StatusOK, loginPage{}) + render(w, loginTemplate, http.StatusOK, loginPage{ + Page: Page{Title: "Login", Section: "login"}, + }) }) } @@ -36,6 +39,7 @@ func handleLoginPOST(db *database.DB) http.Handler { var validUsername = regexp.MustCompile(`^[a-zA-Z]\w*$`) renderForbidden := func(w http.ResponseWriter, username string) { render(w, loginTemplate, http.StatusForbidden, loginPage{ + Page: Page{Title: "Login", Section: "login"}, Forbidden: true, Username: username, }) diff --git a/pkg/webui/logout.go b/pkg/webui/logout.go index 6a281bb..58e445d 100644 --- a/pkg/webui/logout.go +++ b/pkg/webui/logout.go @@ -11,6 +11,9 @@ import ( var logoutTemplate = template.Must(template.ParseFS(htmlFS, "html/base.html", "html/logout.html")) func handleLogoutGET(db *database.DB) http.Handler { + type logoutPage struct { + Page + } return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { session := r.Context().Value(model.SessionContextKey{}) err := db.DeleteSession(session.(*model.Session)) @@ -19,6 +22,8 @@ func handleLogoutGET(db *database.DB) http.Handler { return } unsetSesssionCookie(w) - render(w, logoutTemplate, http.StatusOK, nil) + render(w, logoutTemplate, http.StatusOK, logoutPage{ + Page: Page{Title: "Logout", Section: "login"}, + }) }) } diff --git a/pkg/webui/states.go b/pkg/webui/states.go index 3633cb8..9e3ce27 100644 --- a/pkg/webui/states.go +++ b/pkg/webui/states.go @@ -12,6 +12,7 @@ var statesTemplates = template.Must(template.ParseFS(htmlFS, "html/base.html", " func handleStatesGET(db *database.DB) http.Handler { type StatesData struct { + Page States []model.State } return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { @@ -23,6 +24,7 @@ func handleStatesGET(db *database.DB) http.Handler { return } render(w, statesTemplates, http.StatusOK, StatesData{ + Page: Page{Title: "States", Section: "states"}, States: states, }) }) diff --git a/pkg/webui/static/main.css b/pkg/webui/static/main.css index a2472b7..f0fe350 100644 --- a/pkg/webui/static/main.css +++ b/pkg/webui/static/main.css @@ -1,3 +1,9 @@ -.error-message { - color:red; +.clickable-rows tbody a { + display: block; + padding: 0 1em 0; + text-decoration: none; + transition: all 0.25s ease-out; +} +.clickable-rows tbody tr:hover a { + background-color: var(--secondary-container); } |