feat(ui): ui improvements and fixes (#687)

- UI theme improved
- Dark/Light themes depending on the user's browser settings
- Mobile UI (vertical table)
- Fix serving static files (favicon did not work before)
- Add readme UI gif and mobile screenshot
This commit is contained in:
Gottfried Mayer
2024-06-15 11:48:16 +02:00
committed by GitHub
parent 5b384cbf18
commit b70a91582c
10 changed files with 246 additions and 79 deletions

View File

@@ -78,9 +78,13 @@ Program to keep DNS A and/or AAAA records updated for multiple DNS providers
- Variomedia.de
- Zoneedit
- **Want more?** [Create an issue for it](https://github.com/qdm12/ddns-updater/issues/new/choose)!
- Web User interface
- Web user interface (Desktop)
![Web UI](https://raw.githubusercontent.com/qdm12/ddns-updater/master/readme/webui.png)
![Web UI](readme/webui-desktop.gif)
- Web user interface (Mobile)
![Mobile Web UI](readme/webui-mobile.png)
- Send notifications with [**Shoutrrr**](https://containrrr.dev/shoutrrr/v0.8/services/overview/) using `SHOUTRRR_ADDRESSES`
- Container (Docker/K8s) specific features:

View File

@@ -53,15 +53,15 @@ func (r *Record) HTML(now time.Time) models.HTMLRow {
func convertStatus(status models.Status) string {
switch status {
case constants.SUCCESS:
return `<font color="green"><b>Success</b></font>`
return `<span class="success">Success</span>`
case constants.FAIL:
return `<font color="red"><b>Failure</b></font>`
return `<span class="error">Failure</span>`
case constants.UPTODATE:
return `<font color="#00CC66"><b>Up to date</b></font>`
return `<span class="uptodate">Up to date</span>`
case constants.UPDATING:
return `<font color="orange"><b>Updating</b></font>`
return `<span class="updating">Updating</span>`
case constants.UNSET:
return `<font color="purple"><b>Unset</b></font>`
return `<span class="unset">Unset</span>`
default:
return "Unknown status"
}

View File

@@ -3,6 +3,7 @@ package server
import (
"context"
"embed"
"io/fs"
"net/http"
"strings"
"text/template"
@@ -29,6 +30,11 @@ func newHandler(ctx context.Context, rootURL string,
db Database, runner UpdateForcer) http.Handler {
indexTemplate := template.Must(template.ParseFS(uiFS, "ui/index.html"))
staticFolder, err := fs.Sub(uiFS, "ui/static")
if err != nil {
panic(err)
}
handlers := &handlers{
ctx: ctx,
db: db,
@@ -50,5 +56,7 @@ func newHandler(ctx context.Context, rootURL string,
router.Get(rootURL+"/update", handlers.update)
router.Handle(rootURL+"/static/*", http.StripPrefix(rootURL+"/static/", http.FileServerFS(staticFolder)))
return router
}

View File

@@ -1,82 +1,56 @@
<html>
<!DOCTYPE html>
<html lang="en">
<head>
<title>DDNS Updater</title>
<link rel="icon" href="favicon.ico" type="image/x-icon">
<style>
table {
font-family: arial, sans-serif;
font-size: 14px;
font-size: 1vw;
border-collapse: collapse;
width: 100%;
}
td,
th {
border: 2px solid #9a9fa1;
text-align: center;
padding: 1%;
max-width: 35%;
transition: all 0.7s;
}
th {
background-color: #d8daf7;
}
tr:nth-child(odd) {
background-color: #e6f7ea;
}
tr:nth-child(even) {
background-color: #f3ebe3;
}
tr {
transition: all 0.7s;
}
tr:hover {
background: #c1e2f0;
}
a {
text-decoration: none;
}
</style>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<link rel="icon" href="static/favicon.svg" sizes="any" type="image/svg+xml">
<link rel="icon" href="static/favicon.ico" type="image/x-icon">
<link rel="stylesheet" href="static/styles.css" type="text/css">
</head>
<body>
<table>
<tr>
<th>Domain</th>
<th>Host</th>
<th>Provider</th>
<th>IP version</th>
<th>Update status</th>
<th>Set IP</th>
<th>Previous IPs (reverse chronological order)</th>
</tr>
{{range .Rows}}
<tr>
<td>{{.Domain}}</td>
<td>{{.Host}}</td>
<td>{{.Provider}}</td>
<td>{{.IPVersion}}</td>
<td>{{.Status}}</td>
<td>{{.CurrentIP}}</td>
<td>{{.PreviousIPs}}</td>
</tr>
{{end}}
<table role="table">
<thead>
<tr>
<th>Domain</th>
<th>Host</th>
<th>Provider</th>
<th>IP Version</th>
<th>Update Status</th>
<th>Current IP</th>
<th>Previous IPs<small> (reverse chronological order)</small></th>
</tr>
</thead>
<tbody>
{{range .Rows}}
<tr>
<td data-label="Domain">{{.Domain}}</td>
<td data-label="Host">{{.Host}}</td>
<td data-label="Provider">{{.Provider}}</td>
<td data-label="IP Version">{{.IPVersion}}</td>
<td data-label="Update Status">{{.Status}}</td>
<td data-label="Current IP">{{.CurrentIP}}</td>
<td data-label="Previous IPs">{{.PreviousIPs}}</td>
</tr>
{{end}}
</tbody>
</table>
<div>
Made by <a href="https://qqq.ninja">Quentin McGaw</a>
</div>
<div>
<a href="https://github.com/qdm12/ddns-updater">github.com/qdm12/ddns-updater</a>
</div>
<footer>
<div>
<a href="https://github.com/qdm12/ddns-updater" class="text-big">
<svg class="github-icon" height="1em" aria-hidden="true" viewBox="0 0 16 16" version="1.1"
data-view-component="true">
<path
d="M8 0c4.42 0 8 3.58 8 8a8.013 8.013 0 0 1-5.45 7.59c-.4.08-.55-.17-.55-.38 0-.27.01-1.13.01-2.2 0-.75-.25-1.23-.54-1.48 1.78-.2 3.65-.88 3.65-3.95 0-.88-.31-1.59-.82-2.15.08-.2.36-1.02-.08-2.12 0 0-.67-.22-2.2.82-.64-.18-1.32-.27-2-.27-.68 0-1.36.09-2 .27-1.53-1.03-2.2-.82-2.2-.82-.44 1.1-.16 1.92-.08 2.12-.51.56-.82 1.28-.82 2.15 0 3.06 1.86 3.75 3.64 3.95-.23.2-.44.55-.51 1.07-.46.21-1.61.55-2.33-.66-.15-.24-.6-.83-1.23-.82-.67.01-.27.38.01.53.34.19.73.9.82 1.13.16.45.68 1.31 2.69.94 0 .67.01 1.3.01 1.49 0 .21-.15.45-.55.38A7.995 7.995 0 0 1 0 8c0-4.42 3.58-8 8-8Z">
</path>
</svg>
</a>
</div>
<div>by <a href="https://github.com/qdm12">Quentin McGaw</a> / UI reworked by <a
href="https://github.com/fuse314">Gottfried Mayer</a></div>
</footer>
</body>
</html>

View File

Before

Width:  |  Height:  |  Size: 15 KiB

After

Width:  |  Height:  |  Size: 15 KiB

View File

Before

Width:  |  Height:  |  Size: 3.1 KiB

After

Width:  |  Height:  |  Size: 3.1 KiB

View File

@@ -0,0 +1,181 @@
:root {
--page-background: #fff;
--table-background: #eee;
--background-zebra: #ddd;
--background-hover: #e9e9f9;
--text-color: #000;
--border-color: #666;
--link-color: #039;
--link-color-hover: #36c;
--success-color: #181;
--warn-color: #951;
--error-color: #811;
--progress-color: #818;
--footer-background: #eee;
--footer-link-color: #666;
--footer-link-color-hover: #777;
}
@media screen and (prefers-color-scheme: dark) {
:root {
--page-background: #000;
--table-background: #111;
--background-zebra: #222;
--background-hover: #191929;
--text-color: #ddd;
--border-color: #aaa;
--link-color: #9cf;
--link-color-hover: #69c;
--success-color: #9f9;
--warn-color: #fb8;
--error-color: #f88;
--progress-color: #c7c;
--footer-background: #111;
--footer-link-color: #999;
--footer-link-color-hover: #888;
}
}
html,
body {
font-family: system-ui, -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto,
Oxygen, Ubuntu, Cantarell, "Open Sans", "Helvetica Neue", sans-serif;
font-size: 16px;
background-color: var(--page-background);
color: var(--text-color);
margin: 0;
padding: 0.5rem;
}
table {
border-collapse: collapse;
border-radius: 0.3rem;
overflow: hidden;
width: 100%;
max-width: 1900px;
background: var(--table-background);
margin: 0 auto;
position: center;
}
td,
th {
text-align: center;
padding: 0.7rem;
}
th {
border-bottom: 1px solid var(--border-color);
}
tbody tr:nth-child(odd) {
background: var(--background-zebra);
}
tbody tr:hover {
background: var(--background-hover);
}
a {
color: var(--link-color);
text-decoration: none;
}
a:hover {
color: var(--link-color-hover);
text-decoration: underline;
}
footer {
position: fixed;
bottom: 0;
left: 0;
width: 100%;
padding: 0.5rem;
display: flex;
flex-direction: column;
align-items: center;
background: var(--footer-background);
font-size: 0.9em;
line-height: 1.6em;
}
footer a {
color: var(--footer-link-color);
}
footer a:hover {
color: var(--footer-link-color-hover);
}
.text-big {
font-size: 1.4em;
}
.success, .error, .uptodate, .updating, .unset {
font-weight: bold;
}
.success {
color: var(--success-color);
}
.error {
color: var(--error-color);
}
.uptodate {
color: var(--success-color);
}
.updating {
color: var(--progress-color);
}
.unset {
color: var(--warn-color);
}
.github-icon {
vertical-align: text-bottom;
fill: currentColor;
height: 1em;
}
/* responsive table. source: https://css-tricks.com/responsive-data-tables/ */
@media only screen and (max-width: 760px),
(min-device-width: 768px) and (max-device-width: 1024px) {
table,
thead,
tbody,
th,
td,
tr {
display: block;
}
thead tr {
position: absolute;
top: -9999px;
left: -9999px;
}
tr {
margin: 0 0 1rem 0;
}
td {
border: none;
border-bottom: 1px solid var(--border-color);
position: relative;
padding-left: 50%;
}
td:before {
position: absolute;
left: 6px;
width: 45%;
padding-right: 10px;
white-space: nowrap;
font-weight: bold;
content: attr(data-label);
}
}

BIN
readme/webui-desktop.gif Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 160 KiB

BIN
readme/webui-mobile.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 49 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 137 KiB