summaryrefslogtreecommitdiff
path: root/pkg
diff options
context:
space:
mode:
authorJulien Dessaux2025-01-26 00:04:38 +0100
committerJulien Dessaux2025-01-26 00:04:38 +0100
commit4e029fb83a1e70495330eaac94981a97de24682e (patch)
treefb041970dcbf38daf06de5034cf6fd567c586069 /pkg
parentfeat(webui): implement states list (diff)
downloadtfstated-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.go2
-rw-r--r--pkg/webui/html/base.html54
-rw-r--r--pkg/webui/html/error.html8
-rw-r--r--pkg/webui/html/login.html51
-rw-r--r--pkg/webui/html/logout.html6
-rw-r--r--pkg/webui/html/states.html51
-rw-r--r--pkg/webui/index.go5
-rw-r--r--pkg/webui/login.go6
-rw-r--r--pkg/webui/logout.go7
-rw-r--r--pkg/webui/states.go2
-rw-r--r--pkg/webui/static/main.css10
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);
}