Minor GUI improvements and bugfixes.

Also included is an upgrade from bootstrap 4.5 to 4.6.2. Nothing major changes
in the new version, just some improved compatibility/consistency between
browsers.

- Fixed an issue where long pages (such as ticket search result) would allow the
viewer to scroll beyond the footer.
- Fixed footer stretching to full widescreen size rather than staying inside
  bounds of bootstrap container
- More consistent style for inputs (password prompt, ticket search input)
This commit is contained in:
Jamie Holdstock 2024-11-02 08:19:01 +00:00 committed by GitHub
parent b0fb5469c0
commit 363fdcb224
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
11 changed files with 489 additions and 382 deletions

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View File

@ -1,43 +1,49 @@
html, body {
html {
height: 100%;
overflow-x: hidden;
}
body {
background-color: #F3F5F6;
color: #3D5873;
width: 100vw;
height: 100%;
display: flex;
flex-direction: column;
}
/* Bootstrap adds ugly outlines to search inputs. Setting outline-offset and
appearance makes them look the same as text inputs. */
input[type="search"] {
outline-offset: 0;
appearance: auto;
.vsp-page-content {
flex: 1 0 auto;
color: #3D5873;
}
.navbar {
.vsp-text-orange {
color: #ed6d47;
}
/*
Navbar
*/
.vsp-navbar {
background-color: #F9FAFA;
}
.navbar a {
text-decoration: none;
}
.page-content {
flex: 1 0 auto;
}
.logo--logo {
.vsp-logo-logo {
padding-right: 5px;
border-right: 0.3px solid #091440;
}
.logo--text {
.vsp-logo-text {
padding: 0 5px;
font-size: 15px;
line-height: 1;
color: #091440;
}
/*
Text above stats
*/
.vsp-overview {
color: #8997A5;
background-color: #ffffff;
@ -49,49 +55,79 @@ input[type="search"] {
font-size: 28px;
}
.vsp-stats .stat-title {
/*
VSP stats bar
*/
.vsp-stats .vsp-stat-title {
font-size: 14px;
color: #596D81;
}
.vsp-stats .stat-value {
.vsp-stats .vsp-stat-value {
font-size: 24px;
color: #091440;
}
.vsp-stats .stat-value .text-muted{
.vsp-stats .vsp-stat-value .vsp-stat-subtext {
font-size: 18px;
color: #8997A5;
}
.orange {
color: #ed6d47;
}
/*
Page footer
*/
footer {
flex-shrink: 0;
.vsp-footer {
line-height: 1.2rem;
font-size: 0.8rem;
position: relative;
background-color: #091440;
color: #8997a5;
width: 100%;
}
footer .code {
word-break: break-word;
.vsp-footer-bg-left,
.vsp-footer-bg-right {
position: absolute;
height: 100%;
}
.footer__credit {
background-color: rgba(237,239,241,.1);
text-align: right;
color: #8997a5;
.vsp-footer-bg-right {
right: 0;
bottom: 0;
background-color: #202a52;
}
@media (max-width: 768px) {
.footer__credit {
text-align: center;
.vsp-footer-bg-left,
.vsp-footer-bg-right {
height: 50%;
}
}
.vsp-footer-left,
.vsp-footer-right {
display: flex;
padding-left: 0;
padding-right: 0;
}
.vsp-footer-left {
background-color: #091440;
}
.vsp-footer-right {
background-color: #202a52;
/* Top & bottom padding to ensure right is same height as left despite
having one less line of text */
padding-top: 0.6rem;
padding-bottom: 0.6rem;
}
/*
Buttons
*/
.btn-primary, .btn-secondary {
outline: 0;
-webkit-box-shadow: 1px 3px 14px 0px rgba(0,0,0,0.19);
@ -106,30 +142,102 @@ footer .code {
border-color: transparent;
}
.block__content h1 {
/*
Admin page collapsible tabset
*/
.vsp-tabset {
background-color: #ffffff;
padding: 20px 30px;
margin-top: 0.5rem;
margin-bottom: 1.5rem;
color: #3D5873;
line-height: 1.4;
width: 100%;
}
.vsp-tabset > ul {
margin: 0 0 25px;
}
.vsp-tabset > ul label {
padding: 10px 20px;
}
.vsp-tabset h1 {
color: #091440;
font-size: 24px;
padding-bottom: 10px;
}
.block__content {
background-color: #ffffff;
padding: 20px 30px;
color: #3D5873;
line-height: 1.4;
.vsp-tabset .collapsible-tab-content {
display: flex;
flex-wrap: wrap;
justify-content: center;
width: 100%;
padding: 0 10px 10px 10px;
}
/* Highlight labels on hover/focus to indicate they are clickable */
.vsp-tabset > input[type="radio"]:nth-child(1):focus ~ ul li:nth-child(1) label,
.vsp-tabset > input[type="radio"]:nth-child(1):hover ~ ul li:nth-child(1) label,
.vsp-tabset > input[type="radio"]:nth-child(2):focus ~ ul li:nth-child(2) label,
.vsp-tabset > input[type="radio"]:nth-child(2):hover ~ ul li:nth-child(2) label,
.vsp-tabset > input[type="radio"]:nth-child(3):focus ~ ul li:nth-child(3) label,
.vsp-tabset > input[type="radio"]:nth-child(3):hover ~ ul li:nth-child(3) label,
.vsp-tabset > input[type="radio"]:nth-child(4):focus ~ ul li:nth-child(4) label,
.vsp-tabset > input[type="radio"]:nth-child(4):hover ~ ul li:nth-child(4) label,
.vsp-tabset > input[type="radio"]:nth-child(5):focus ~ ul li:nth-child(5) label,
.vsp-tabset > input[type="radio"]:nth-child(5):hover ~ ul li:nth-child(5) label,
.vsp-tabset > input[type="radio"]:nth-child(6):focus ~ ul li:nth-child(6) label,
.vsp-tabset > input[type="radio"]:nth-child(6):hover ~ ul li:nth-child(6) label {
cursor: pointer;
color: #091440;
}
.block__content table td ,
.block__content table th {
/* Highlight the label of the currently selected tab */
.vsp-tabset > input[type="radio"]:nth-child(1):checked ~ ul li:nth-child(1) label,
.vsp-tabset > input[type="radio"]:nth-child(2):checked ~ ul li:nth-child(2) label,
.vsp-tabset > input[type="radio"]:nth-child(3):checked ~ ul li:nth-child(3) label,
.vsp-tabset > input[type="radio"]:nth-child(4):checked ~ ul li:nth-child(4) label,
.vsp-tabset > input[type="radio"]:nth-child(5):checked ~ ul li:nth-child(5) label,
.vsp-tabset > input[type="radio"]:nth-child(6):checked ~ ul li:nth-child(6) label {
border-bottom: 5px solid #2ed8a3;
color: #091440;
cursor: default;
}
/* By default the collapsible tabs should all be hidden */
.vsp-tabset .collapsible-tab {
display: none;
}
/* Show tab when its corresponding radio is checked */
.vsp-tabset > input[type="radio"]:nth-child(1):checked ~ .collapsible-tab-wrapper > .collapsible-tab:nth-child(1),
.vsp-tabset > input[type="radio"]:nth-child(2):checked ~ .collapsible-tab-wrapper > .collapsible-tab:nth-child(2),
.vsp-tabset > input[type="radio"]:nth-child(3):checked ~ .collapsible-tab-wrapper > .collapsible-tab:nth-child(3),
.vsp-tabset > input[type="radio"]:nth-child(4):checked ~ .collapsible-tab-wrapper > .collapsible-tab:nth-child(4),
.vsp-tabset > input[type="radio"]:nth-child(5):checked ~ .collapsible-tab-wrapper > .collapsible-tab:nth-child(5),
.vsp-tabset > input[type="radio"]:nth-child(6):checked ~ .collapsible-tab-wrapper > .collapsible-tab:nth-child(6) {
display: flex;
}
.vsp-tabset table td ,
.vsp-tabset table th {
padding: 10px 16px;
}
.block__content table td {
.vsp-tabset table td,
.vsp-tabset table .btn-link {
font-size: 14px;
}
.vsp-tabset table td {
word-break: break-word;
font-family: "vspd-code";
}
.block__content table th {
.vsp-tabset table th {
white-space: nowrap;
font-family: "vspd";
vertical-align: top;
@ -137,148 +245,100 @@ footer .code {
background-color: #edeff1;
}
/*
VSP Status tab
*/
#ticket-table th,
#ticket-table td {
border-top: 1px solid #dee2e6;
.vsp-status-tab {
flex-direction: row;
}
#ticket-table th {
text-align: right;
}
#ticket-table td {
font-size: 14px;
text-align: left;
padding-right: 0;
width: 100%;
}
#ticket-table details table td {
font-size: 12px;
}
#ticket-table .small-text {
padding: 10px 16px;
font-size: 12px;
}
#ticket-table pre {
color: #3D5873;
font-size: 12px;
white-space: pre-wrap;
}
.vsp-status {
padding: 10px;
}
.vsp-status table {
margin-bottom: 28px;
}
.vsp-status table th,
.vsp-status table td {
.vsp-status-tab table th,
.vsp-status-tab table td {
border: 1px solid #edeff1;
vertical-align: middle;
text-align: center;
}
.vsp-status table .center {
.vsp-status-tab table .center {
display: flex;
justify-content: center;
align-items: center;
}
.vsp-status table .status {
.vsp-status-tab table .status {
height: 30px;
padding-left: 30px;
}
.vsp-status table .good {
.vsp-status-tab table .good {
background: url(/public/images/success-icon.svg) no-repeat center center;
}
.vsp-status table .bad {
.vsp-status-tab table .bad {
background: url(/public/images/error-icon.svg) no-repeat left center;
}
.vsp-status table .with-text {
.vsp-status-tab table .with-text {
padding-left: 40px;
}
table.missed-tickets th,
table.missed-tickets td {
/*
Ticket Search tab
*/
.ticket-search-tab input[type="search"] {
font-size: 14px;
font-family: "vspd-code";
}
.ticket-search-tab input[type="search"]::placeholder {
font-family: "vspd";
}
.ticket-search-tab table {
width: 100%;
margin-top: 0.5rem;
margin-bottom: 1.5rem;
}
.ticket-search-tab table th,
.ticket-search-tab table td {
border-top: 1px solid #dee2e6;
}
.ticket-search-tab table th {
text-align: right;
}
.ticket-search-tab table td {
text-align: left;
padding-right: 0;
width: 100%;
}
.ticket-search-tab table details table td {
font-size: 12px;
}
.ticket-search-tab table .small-text {
padding: 10px 16px;
font-size: 12px;
}
.ticket-search-tab table pre {
color: #3D5873;
font-size: 12px;
white-space: pre-wrap;
}
/*
Missed tickets tab
*/
.missed-tickets-tab table th,
.missed-tickets-tab table td {
border: 1px solid #edeff1;
vertical-align: middle;
text-align: center;
}
.tabset > input {
display:block; /* "enable" hidden elements in IE/edge */
position:absolute; /* then hide them off-screen */
left:-100%;
}
.tabset > ul {
position:relative;
z-index:999;
list-style:none;
display:flex;
padding: 0;
margin: 0 0 25px;
}
.tabset > ul label {
display:inline-block;
padding: 10px 20px;
}
.tabset > div {
position:relative;
}
.tabset > input:nth-child(1):focus ~ ul li:nth-child(1) label,
.tabset > input:nth-child(1):hover ~ ul li:nth-child(1) label,
.tabset > input:nth-child(2):focus ~ ul li:nth-child(2) label,
.tabset > input:nth-child(2):hover ~ ul li:nth-child(2) label,
.tabset > input:nth-child(3):focus ~ ul li:nth-child(3) label,
.tabset > input:nth-child(3):hover ~ ul li:nth-child(3) label,
.tabset > input:nth-child(4):focus ~ ul li:nth-child(4) label,
.tabset > input:nth-child(4):hover ~ ul li:nth-child(4) label,
.tabset > input:nth-child(5):focus ~ ul li:nth-child(5) label,
.tabset > input:nth-child(5):hover ~ ul li:nth-child(5) label,
.tabset > input:nth-child(6):focus ~ ul li:nth-child(6) label,
.tabset > input:nth-child(6):hover ~ ul li:nth-child(6) label {
cursor: pointer;
color: #091440;
}
.tabset > input:nth-child(1):checked ~ ul li:nth-child(1) label,
.tabset > input:nth-child(2):checked ~ ul li:nth-child(2) label,
.tabset > input:nth-child(3):checked ~ ul li:nth-child(3) label,
.tabset > input:nth-child(4):checked ~ ul li:nth-child(4) label,
.tabset > input:nth-child(5):checked ~ ul li:nth-child(5) label,
.tabset > input:nth-child(6):checked ~ ul li:nth-child(6) label {
border-bottom: 5px solid #2ed8a3;
color: #091440;
cursor: default;
}
.tabset > div > section,
.tabset > div > section h2 {
position:absolute;
top:-999em;
left:-999em;
}
.tabset > div > section {
padding: 0 10px 10px 10px;
}
.tabset > input:nth-child(1):checked ~ div > section:nth-child(1),
.tabset > input:nth-child(2):checked ~ div > section:nth-child(2),
.tabset > input:nth-child(3):checked ~ div > section:nth-child(3),
.tabset > input:nth-child(4):checked ~ div > section:nth-child(4),
.tabset > input:nth-child(5):checked ~ div > section:nth-child(5),
.tabset > input:nth-child(6):checked ~ div > section:nth-child(6) {
position:static;
}

View File

@ -15,199 +15,214 @@
<div class="container">
<div class="row">
<div class="col-12 pt-2 pb-4">
<div class="block__content">
<div class="vsp-tabset">
<input
class="d-none"
type="radio"
name="tabset_1"
id="tabset_1_1"
hidden
{{ with .SearchResult }}{{ else }}checked{{ end }}
>
<input
class="d-none"
type="radio"
name="tabset_1"
id="tabset_1_2"
hidden
{{ with .SearchResult }}checked{{ end }}
>
<input
class="d-none"
type="radio"
name="tabset_1"
id="tabset_1_3"
hidden
>
<input
class="d-none"
type="radio"
name="tabset_1"
id="tabset_1_4"
hidden
>
<input
class="d-none"
type="radio"
name="tabset_1"
id="tabset_1_5"
hidden
>
<input
class="d-none"
type="radio"
name="tabset_1"
id="tabset_1_6"
hidden
>
<ul class="d-flex p-0 list-unstyled">
<li><label for="tabset_1_1">VSP Status</label></li>
<li><label for="tabset_1_2">Ticket Search</label></li>
<li><label for="tabset_1_3">Missed Tickets</label></li>
<li><label for="tabset_1_4">Fee X Pubs</label></li>
<li><label for="tabset_1_5">Database</label></li>
<li><label for="tabset_1_6">Logout</label></li>
</ul>
<div class="collapsible-tab-wrapper">
<div class="tabset">
<input
type="radio"
name="tabset_1"
id="tabset_1_1"
hidden
{{ with .SearchResult }}{{ else }}checked{{ end }}
>
<input
type="radio"
name="tabset_1"
id="tabset_1_2"
hidden
{{ with .SearchResult }}checked{{ end }}
>
<input
type="radio"
name="tabset_1"
id="tabset_1_3"
hidden
>
<input
type="radio"
name="tabset_1"
id="tabset_1_4"
hidden
>
<input
type="radio"
name="tabset_1"
id="tabset_1_5"
hidden
>
<input
type="radio"
name="tabset_1"
id="tabset_1_6"
hidden
>
<ul>
<li><label for="tabset_1_1">VSP Status</label></li>
<li><label for="tabset_1_2">Ticket Search</label></li>
<li><label for="tabset_1_3">Missed Tickets</label></li>
<li><label for="tabset_1_4">Fee X Pubs</label></li>
<li><label for="tabset_1_5">Database</label></li>
<li><label for="tabset_1_6">Logout</label></li>
</ul>
<div>
<section class="collapsible-tab">
<div class="vsp-status-tab collapsible-tab-content">
<section class="d-flex row justify-content-center">
<div class="p-2">
<h1>Local dcrd</h1>
<div class="vsp-status">
<h1>Local dcrd</h1>
<table class="vsp-status">
<thead>
<th>URL</th>
<th>Height</th>
</thead>
<tbody>
<tr>
<td>{{ stripWss .DcrdStatus.Host }}</td>
{{ if .DcrdStatus.Connected }}
{{ if .DcrdStatus.BestBlockError }}
<td>
<div class="center">
<div class="status bad center with-text">
Error
</div>
</div>
</td>
{{ else }}
<td>{{ .DcrdStatus.BestBlockHeight }}</td>
{{ end }}
{{else}}
<table>
<thead>
<th>URL</th>
<th>Height</th>
</thead>
<tbody>
<tr>
<td>{{ stripWss .DcrdStatus.Host }}</td>
{{ if .DcrdStatus.Connected }}
{{ if .DcrdStatus.BestBlockError }}
<td>
<div class="center">
<div class="status bad center with-text">
Cannot connect
Error
</div>
</div>
</td>
{{end}}
</tr>
</tbody>
</table>
</div>
<div class="vsp-status">
<h1>Voting Wallets</h1>
{{ else }}
<td>{{ .DcrdStatus.BestBlockHeight }}</td>
{{ end }}
{{else}}
<td>
<div class="center">
<div class="status bad center with-text">
Cannot connect
</div>
</div>
</td>
{{end}}
</tr>
</tbody>
</table>
</div>
<div class="p-2">
<h1>Voting Wallets</h1>
<table class="vsp-status">
<thead>
<th>URL</th>
<th>Height</th>
<th>Connected<br />to dcrd</th>
<th>Unlocked</th>
<th>Voting</th>
<th>Vote<br />Version</th>
</thead>
<tbody>
{{ range $host, $status := .WalletStatus }}
<tr>
<td>{{ stripWss $host }}</td>
{{ if $status.Connected }}
{{ if $status.BestBlockError }}
<td>
<div class="center">
<div class="status bad center with-text">
Error
</div>
</div>
</td>
{{ else }}
<td>{{ $status.BestBlockHeight }}</td>
{{ end }}
{{ if $status.InfoError }}
<td colspan="4">
<div class="center">
<div class="status bad center with-text">
Error getting wallet info
</div>
</div>
</td>
{{ else }}
<td>
<div class="center">
<div class="status {{ if $status.DaemonConnected }}good{{else}}bad{{end}}"></div>
</div>
</td>
<td>
<div class="center">
<div class="status {{ if $status.Unlocked }}good{{else}}bad{{end}}"></div>
</div>
</td>
<td>
<div class="center">
<div class="status {{ if $status.Voting }}good{{else}}bad{{end}}"></div>
</div>
</td>
<td>{{ $status.VoteVersion }}</td>
{{ end }}
{{else}}
<td colspan="5">
<table>
<thead>
<th>URL</th>
<th>Height</th>
<th>Connected<br />to dcrd</th>
<th>Unlocked</th>
<th>Voting</th>
<th>Vote<br />Version</th>
</thead>
<tbody>
{{ range $host, $status := .WalletStatus }}
<tr>
<td>{{ stripWss $host }}</td>
{{ if $status.Connected }}
{{ if $status.BestBlockError }}
<td>
<div class="center">
<div class="status bad center with-text">
Cannot connect to wallet
Error
</div>
</div>
</td>
{{end}}
</tr>
{{ else }}
<td>{{ $status.BestBlockHeight }}</td>
{{ end }}
{{ if $status.InfoError }}
<td colspan="4">
<div class="center">
<div class="status bad center with-text">
Error getting wallet info
</div>
</div>
</td>
{{ else }}
<td>
<div class="center">
<div class="status {{ if $status.DaemonConnected }}good{{else}}bad{{end}}"></div>
</div>
</td>
<td>
<div class="center">
<div class="status {{ if $status.Unlocked }}good{{else}}bad{{end}}"></div>
</div>
</td>
<td>
<div class="center">
<div class="status {{ if $status.Voting }}good{{else}}bad{{end}}"></div>
</div>
</td>
<td>{{ $status.VoteVersion }}</td>
{{ end }}
{{else}}
<td colspan="5">
<div class="center">
<div class="status bad center with-text">
Cannot connect to wallet
</div>
</div>
</td>
{{end}}
</tbody>
</table>
</div>
</tr>
{{end}}
</tbody>
</table>
</div>
</div>
</section>
<section class="collapsible-tab">
<div class="ticket-search-tab collapsible-tab-content">
</section>
<section>
<form class="mt-2 mb-4" action="/admin/ticket" method="post">
<input type="search" name="hash" size="64" minlength="64" maxlength="64" spellcheck="false" required placeholder="Ticket hash" autocomplete="off"
<div class="p-2">
<form class="form-inline" action="/admin/ticket" method="post">
<input class="form-control mx-3" type="search" name="hash" size="64" minlength="64" maxlength="64" spellcheck="false" required placeholder="Ticket hash" autocomplete="off"
{{ with .SearchResult }}
value="{{ .Ticket.Hash }}"
{{ end }}>
<button class="btn btn-primary d-block my-2" type="submit">Search</button>
<button class="btn btn-primary" type="submit">Search</button>
</form>
{{ with .SearchResult }}
{{ template "ticket-search-result" . }}
{{ end }}
</section>
</div>
<section>
<div class="p-2">
{{ with .SearchResult }}
{{ template "ticket-search-result" . }}
{{ end }}
</div>
</div>
</section>
<section class="collapsible-tab">
<div class="missed-tickets-tab collapsible-tab-content">
<div class="p-2">
<h1>{{ pluralize (len .MissedTickets) "Missed Ticket" }}</h1>
{{ with .MissedTickets }}
<table class="missed-tickets mx-auto">
<table class="mx-auto">
<thead>
<th>Purchase Height</th>
<th>Ticket Hash</th>
@ -227,9 +242,15 @@
</tbody>
</table>
{{ end}}
</section>
</div>
<section>
</div>
</section>
<section class="collapsible-tab">
<div class="collapsible-tab-content">
<div class="p-2">
<h1>All X Pubs</h1>
{{ with .XPubs }}
<table class="mx-auto">
@ -251,23 +272,36 @@
</tbody>
</table>
{{ end}}
</section>
</div>
<section>
</div>
</section>
<section class="collapsible-tab">
<div class="collapsible-tab-content">
<div class="p-2">
<p>Database size: {{ .WebApiCache.DatabaseSize }}</p>
<a class="btn btn-primary" href="/admin/backup" download>Download Backup</a>
</section>
</div>
<section>
</div>
</section>
<section class="collapsible-tab">
<div class="collapsible-tab-content">
<div class="p-2">
<form action="/admin/logout" method="post">
<button type="submit" class="btn btn-primary">Logout</button>
</form>
</section>
</div>
</div>
</div>
</section>
</div>
</div>
</div>

View File

@ -1,25 +1,36 @@
{{ define "footer" }}
</div> <!-- page-content -->
</div> <!-- vsp-page-content -->
<footer class="row m-0">
<div class="col-md-8 col-12 d-flex justify-content-center align-items-center">
<footer class="vsp-footer flex-md-row flex-row-reverse">
<div class="vsp-footer-bg-left col-md-6 col-12"></div>
<div class="vsp-footer-bg-right col-md-6 col-12"></div>
<div class="container d-flex flex-wrap">
<div class="vsp-footer-left col-md-8 col-12 justify-content-center justify-content-md-start">
<div class="d-inline-block text-left">
<p class="py-4 m-0">
<strong>Stats&nbsp;updated:</strong>&nbsp;{{ timeAgo .WebApiCache.UpdateTime }}
<br />
<strong>Support:</strong>&nbsp;<a href="mailto:{{ .WebApiCfg.SupportEmail }}" rel="noopener noreferrer">{{ .WebApiCfg.SupportEmail }}</a>
<br />
<strong>VSP&nbsp;public&nbsp;key:</strong>&nbsp;<span class="code">{{ .WebApiCache.PubKey }}</span>
<strong>VSP&nbsp;public&nbsp;key:</strong>&nbsp;<span class="code text-break">{{ .WebApiCache.PubKey }}</span>
</p>
</div>
</div>
<div class="col-md-4 col-12 footer__credit d-flex justify-content-center align-items-center">
<div class="vsp-footer-right col-md-4 col-12 justify-content-center justify-content-md-end">
<div class="d-block text-center text-md-right">
<p class="py-4 m-0">
Decred Developers | 2020-2024
<br />
The source code is available on <a href="https://github.com/decred/vspd" target="_blank" rel="noopener noreferrer">GitHub</a>
Decred Developers | 2020-2024
<br />
The source code is available on <a href="https://github.com/decred/vspd" target="_blank" rel="noopener noreferrer">GitHub</a>
</p>
</div>
</div>
</div>
</footer>
</body>

View File

@ -7,7 +7,7 @@
<title>Decred VSP - {{ .WebApiCfg.Designation }}</title>
<link rel="stylesheet" href="/public/css/vendor/bootstrap-4.5.0.min.css" />
<link rel="stylesheet" href="/public/css/vendor/bootstrap-4.6.2.min.css" />
<link rel="stylesheet" href="/public/css/vspd.css?v={{ .WebApiCfg.VspdVersion }}" />
<!-- fonts.css should be last to ensure dcr fonts take precedence. -->
<link rel="stylesheet" href="/public/css/fonts.css?v={{ .WebApiCfg.VspdVersion }}" />
@ -43,12 +43,12 @@
</head>
<body>
<div class="page-content">
<div class="vsp-page-content">
<nav class="py-sm-3 px-sm-5 navbar">
<nav class="py-sm-3 px-sm-5 navbar vsp-navbar">
<div class="container">
<a class="py-1 border-0 d-flex justify-content-start align-items-center" href="/">
<div class="logo--logo">
<a class="text-decoration-none py-1 border-0 d-flex justify-content-start align-items-center" href="/">
<div class="vsp-logo-logo">
<svg xmlns="http://www.w3.org/2000/svg" width="38" height="32" fill="none" viewBox="0 0 23 20">
<path fill="#000" fill-opacity="0" d="M0 0h23v19.167H0z" />
<path fill="#091440"
@ -57,7 +57,7 @@
d="M18.208 14.375L23 19.167h-5.175L10.158 11.5h5.175a3.834 3.834 0 0 0 0-7.667h-1.917L9.583 0h5.75A7.667 7.667 0 0 1 23 7.667c0 3.109-1.533 5.615-4.792 6.708z" />
</svg>
</div>
<div class="logo--text">
<div class="vsp-logo-text">
VSP<br>
<b>{{ .WebApiCfg.Designation }}</b>
</div>

View File

@ -2,13 +2,13 @@
<div class="py-4 container">
<h1>Login</h1>
<form class="py-3" action="/admin" method="post">
<form action="/admin" method="post">
<input type="password" name="password" autofocus required placeholder="Enter password">
<input class="form-control w-auto my-2" type="password" name="password" autofocus required placeholder="Enter password">
<p class="my-1 orange" style="visibility:{{ if .FailedLoginMsg }}visible{{ else }}hidden{{ end }};">{{ .FailedLoginMsg }}</p>
<p class="my-1 vsp-text-orange" style="visibility:{{ if .FailedLoginMsg }}visible{{ else }}hidden{{ end }};">{{ .FailedLoginMsg }}</p>
<button class="btn btn-primary" type="submit">Login</button>
<button class="btn btn-primary my-2" type="submit">Login</button>
</form>
</div>

View File

@ -2,9 +2,11 @@
{{ if .Found }}
<hr />
<h1>Ticket</h1>
<table id="ticket-table" class="mt-2 mb-4 w-100">
<table>
<tr>
<th>Hash</th>
<td>
@ -44,7 +46,7 @@
<h1>Fee</h1>
<table id="ticket-table" class="mt-2 mb-4 w-100">
<table>
<tr>
<th>Fee Address</th>
<td>
@ -97,7 +99,7 @@
<h1>Vote Choices</h1>
<table id="ticket-table" class="mt-2 mb-4 w-100">
<table>
<tr>
<th>Consensus Vote Choices</th>
<td>
@ -163,7 +165,7 @@
<h1>Alternate Signing Address</h1>
<table id="ticket-table" class="mt-2 mb-4 w-100">
<table>
<tr>
<th>Alternate Signing Address</th>
<td>
@ -206,6 +208,6 @@
</table>
{{ else }}
<p>No ticket found with hash <span class="code">{{ .Hash }}</span></p>
<p class="vsp-text-orange">No ticket found with hash <span class="code">{{ .Hash }}</span></p>
{{ end }}
{{ end }}

View File

@ -3,39 +3,39 @@
<div class="row vsp-stats">
<div class="col-6 col-sm-4 col-lg-2 py-3">
<div class="stat-title">Live tickets</div>
<div class="stat-value">{{ comma .WebApiCache.Voting }}</div>
<div class="vsp-stat-title">Live tickets</div>
<div class="vsp-stat-value">{{ comma .WebApiCache.Voting }}</div>
</div>
<div class="col-6 col-sm-4 col-lg-2 py-3">
<div class="stat-title">Voted tickets</div>
<div class="stat-value">{{ comma .WebApiCache.Voted }}</div>
<div class="vsp-stat-title">Voted tickets</div>
<div class="vsp-stat-value">{{ comma .WebApiCache.Voted }}</div>
</div>
<div class="col-6 col-sm-4 col-lg-2 py-3">
<div class="stat-title">Expired tickets</div>
<div class="stat-value">
<div class="vsp-stat-title">Expired tickets</div>
<div class="vsp-stat-value">
{{ comma .WebApiCache.Expired }}
<span class="text-muted">({{ float32ToPercent .WebApiCache.ExpiredProportion }})</span>
<span class="vsp-stat-subtext">({{ float32ToPercent .WebApiCache.ExpiredProportion }})</span>
</div>
</div>
<div class="col-6 col-sm-4 col-lg-2 py-3">
<div class="stat-title">Missed tickets</div>
<div class="stat-value">
<div class="vsp-stat-title">Missed tickets</div>
<div class="vsp-stat-value">
{{ comma .WebApiCache.Missed }}
<span class="text-muted">({{ float32ToPercent .WebApiCache.MissedProportion }})</span>
<span class="vsp-stat-subtext">({{ float32ToPercent .WebApiCache.MissedProportion }})</span>
</div>
</div>
<div class="col-6 col-sm-4 col-lg-2 py-3">
<div class="stat-title">VSP Fee</div>
<div class="stat-value">{{ .WebApiCfg.VSPFee }}%</div>
<div class="vsp-stat-title">VSP Fee</div>
<div class="vsp-stat-value">{{ .WebApiCfg.VSPFee }}%</div>
</div>
<div class="col-6 col-sm-4 col-lg-2 py-3">
<div class="stat-title">Network Proportion</div>
<div class="stat-value">{{ float32ToPercent .WebApiCache.NetworkProportion }}</div>
<div class="vsp-stat-title">Network Proportion</div>
<div class="vsp-stat-value">{{ float32ToPercent .WebApiCache.NetworkProportion }}</div>
</div>
</div>