chore(webui): redesign the states page
This commit is contained in:
parent
ff14a71b39
commit
f12a54b760
2 changed files with 111 additions and 77 deletions
|
@ -1,69 +1,46 @@
|
||||||
{{ define "main" }}
|
{{ define "main" }}
|
||||||
<div>
|
<h1>States</h1>
|
||||||
<div class="tabs">
|
<div class="flex-row" style="justify-content: space-between;">
|
||||||
<a data-ui="#explorer"{{ if eq .ActiveTab 0 }} class="active"{{ end }}>States</a>
|
<div style="min-width: 240px;">
|
||||||
<a data-ui="#new"{{ if eq .ActiveTab 1 }} class="active"{{ end }}>Create New State</a>
|
<p>TfStated is currently managing {{ len .States }} states.</p>
|
||||||
|
<p>Use this page to inspect the existing states.</p>
|
||||||
|
<p>You also have the option to upload a state file in order to create a new one. This is equivalent to using the <code>state push</code> command of OpenTofu/Terraform on a brand new state.</p>
|
||||||
</div>
|
</div>
|
||||||
<div id="explorer" class="page padding{{ if eq .ActiveTab 0 }} active{{ end }}">
|
<form action="/states" enctype="multipart/form-data" method="post">
|
||||||
<table class="clickable-rows no-space">
|
<fieldset>
|
||||||
<thead>
|
<legend>New State</legend>
|
||||||
<tr>
|
<div class="grid-2">
|
||||||
<th>Path</th>
|
<label for="path">Path</label>
|
||||||
<th>Updated</th>
|
<input autofocus
|
||||||
<th>Locked</th>
|
class="flex-stretch"
|
||||||
</tr>
|
id="path"
|
||||||
</thead>
|
name="path"
|
||||||
<tbody>
|
required
|
||||||
{{ range .States }}
|
type="text"
|
||||||
<tr>
|
value="{{ .Path }}">
|
||||||
<td><a href="/states/{{ .Id }}">{{ .Path }}</a></td>
|
{{ if .PathDuplicate }}
|
||||||
<td><a href="/states/{{ .Id }}">{{ .Updated }}</a></td>
|
<span class="error">This path already exist</span>
|
||||||
<td>
|
{{ else if .PathError }}
|
||||||
<a href="/states/{{ .Id }}">
|
<span class="error">A valid URL path beginning with a / is expected.</span>
|
||||||
{{ 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 }}
|
{{ end }}
|
||||||
</tbody>
|
<label for="file" style="min-width: 120px">JSON state file</label>
|
||||||
</table>
|
<input id="file"
|
||||||
</div>
|
name="file"
|
||||||
<div id="new" class="page padding{{ if eq .ActiveTab 1 }} active{{ end }}">
|
required
|
||||||
<form action="/states" enctype="multipart/form-data" method="post">
|
type="file">
|
||||||
<fieldset>
|
</div>
|
||||||
<div class="field border label{{ if .PathError }} invalid{{ end }}">
|
<button class="primary" type="submit" value="submit">Upload and Create State</button>
|
||||||
<input autofocus
|
</fieldset>
|
||||||
id="path"
|
</form>
|
||||||
name="path"
|
|
||||||
required
|
|
||||||
type="text"
|
|
||||||
value="{{ .Path }}">
|
|
||||||
<label for="path">Path</label>
|
|
||||||
{{ if .PathDuplicate }}
|
|
||||||
<span class="error">This path already exist</span>
|
|
||||||
{{ else if .PathError }}
|
|
||||||
<span class="error">Invalid path</span>
|
|
||||||
{{ else }}
|
|
||||||
<span class="helper">Valid URL path beginning with a /</span>
|
|
||||||
{{ end }}
|
|
||||||
</div>
|
|
||||||
<div class="field label border">
|
|
||||||
<input name="file"
|
|
||||||
required
|
|
||||||
type="file">
|
|
||||||
<input type="text">
|
|
||||||
<label>File</label>
|
|
||||||
<span class="helper">JSON state file</span>
|
|
||||||
</div>
|
|
||||||
<button class="small-round" type="submit" value="submit">New</button>
|
|
||||||
</fieldset>
|
|
||||||
</form>
|
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
|
<article aria-role="table" class="grid-3" style="margin-top: 16px; justify-items: stretch;">
|
||||||
|
<span>Path</span>
|
||||||
|
<span>Updated</span>
|
||||||
|
<span>Locked</span>
|
||||||
|
{{ range .States }}
|
||||||
|
<a href="/states/{{ .Id }}">{{ .Path }}</a>
|
||||||
|
<a href="/states/{{ .Id }}">{{ .Updated }}</a>
|
||||||
|
<a href="/states/{{ .Id }}" style="text-align: center;">{{ if eq .Lock nil }}no{{ else }}yes{{ end }}</a>
|
||||||
|
{{ end }}
|
||||||
|
</article>
|
||||||
{{ end }}
|
{{ end }}
|
||||||
|
|
|
@ -105,7 +105,7 @@ header {
|
||||||
#main {
|
#main {
|
||||||
display: flex;
|
display: flex;
|
||||||
flex-direction: row;
|
flex-direction: row;
|
||||||
gap: 8px;
|
gap: 16px;
|
||||||
margin-left: auto;
|
margin-left: auto;
|
||||||
margin-right: auto;
|
margin-right: auto;
|
||||||
padding: 8px 0px;
|
padding: 8px 0px;
|
||||||
|
@ -130,17 +130,22 @@ header {
|
||||||
text-align: center;
|
text-align: center;
|
||||||
text-decoration: none;
|
text-decoration: none;
|
||||||
}
|
}
|
||||||
#main aside a.active {
|
#main aside a:hover {
|
||||||
background-color: var(--bg-2);
|
background-color: var(--br_orange);
|
||||||
|
color: var(--red);
|
||||||
}
|
}
|
||||||
main {
|
main {
|
||||||
background-color: var(--bg-1);
|
background-color: var(--bg-1);
|
||||||
margin-right: auto;
|
margin-right: auto;
|
||||||
|
padding: 8px;
|
||||||
overflow-wrap: anywhere;
|
overflow-wrap: anywhere;
|
||||||
scrollbar-gutter: stable both-edges;
|
scrollbar-gutter: stable both-edges;
|
||||||
max-width: 776px;
|
max-width: 776px;
|
||||||
min-width: 776px;
|
min-width: 776px;
|
||||||
}
|
}
|
||||||
|
hr {
|
||||||
|
width: 50%;
|
||||||
|
}
|
||||||
h1, h2, h3, h4, h5, h6 {
|
h1, h2, h3, h4, h5, h6 {
|
||||||
font-family: open, serif;
|
font-family: open, serif;
|
||||||
font-weight: 700;
|
font-weight: 700;
|
||||||
|
@ -225,14 +230,62 @@ ul {
|
||||||
ul li {
|
ul li {
|
||||||
margin-bottom: 4px;
|
margin-bottom: 4px;
|
||||||
}
|
}
|
||||||
.fullwidth {
|
article, fieldset {
|
||||||
width: 100%;
|
border: 1px solid var(--fg-0);
|
||||||
|
border-radius: 4px;
|
||||||
|
padding: 8px;
|
||||||
}
|
}
|
||||||
img[src*='#center'] {
|
button {
|
||||||
display: block;
|
background-color: var(--bg-2);
|
||||||
margin: auto;
|
border: 1px solid var(--orange);
|
||||||
|
color: var(--orange);
|
||||||
|
align-items: center;
|
||||||
|
border: 1px solid var(--fg-1);
|
||||||
|
border-radius: 8px;
|
||||||
|
display: inline-flex;
|
||||||
|
font-size: 16px;
|
||||||
|
gap: 8px;
|
||||||
|
margin: 4px 2px;
|
||||||
|
padding: 4px 4px;
|
||||||
|
text-align: center;
|
||||||
|
text-decoration: none;
|
||||||
|
}
|
||||||
|
button:hover {
|
||||||
|
background-color: var(--br_orange);
|
||||||
|
border: 1px solid var(--red);
|
||||||
|
color: var(--red);
|
||||||
|
}
|
||||||
|
.primary {
|
||||||
|
background-color: var(--orange);
|
||||||
|
color: var(--fg-1);
|
||||||
|
}
|
||||||
|
.flex-column {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
gap: 4px;
|
||||||
|
}
|
||||||
|
.flex-row {
|
||||||
|
align-items: start;
|
||||||
|
display: flex;
|
||||||
|
flex-direction: row;
|
||||||
|
gap: 16px;
|
||||||
|
}
|
||||||
|
.flex-stretch {
|
||||||
|
flex: 1;
|
||||||
|
}
|
||||||
|
.grid-2 {
|
||||||
|
align-items: center;
|
||||||
|
display: grid;
|
||||||
|
grid-template-columns: min-content 1fr;
|
||||||
|
}
|
||||||
|
.grid-3 {
|
||||||
|
align-items: center;
|
||||||
|
display: grid;
|
||||||
|
grid-template-columns: 1fr 1fr 1fr;
|
||||||
|
}
|
||||||
|
.grid-3 span {
|
||||||
|
text-align: center;
|
||||||
}
|
}
|
||||||
|
|
||||||
a {
|
a {
|
||||||
color: var(--yellow);
|
color: var(--yellow);
|
||||||
}
|
}
|
||||||
|
@ -262,9 +315,9 @@ footer a {
|
||||||
color: var(--green);
|
color: var(--green);
|
||||||
}
|
}
|
||||||
|
|
||||||
@media only screen and (640px <= width < 960px) { /*864*/
|
@media only screen and (640px <= width < 968px) { /*856*/
|
||||||
header {
|
header {
|
||||||
width: calc(100vw - 16px);
|
width: calc(100vw - 32px); /* 8x2 padding + 16 gap */
|
||||||
}
|
}
|
||||||
#main {
|
#main {
|
||||||
/* These 16px account for the vertical scrollbar */
|
/* These 16px account for the vertical scrollbar */
|
||||||
|
@ -280,13 +333,16 @@ footer a {
|
||||||
display: none;
|
display: none;
|
||||||
}
|
}
|
||||||
main {
|
main {
|
||||||
max-width: calc(100vw - 16px - 64px);
|
max-width: calc(100vw - 32px - 64px);
|
||||||
min-width: calc(100vw - 16px - 64px);
|
min-width: calc(100vw - 32px - 64px);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@media only screen and (width < 640px) {
|
@media only screen and (width < 640px) {
|
||||||
header {
|
header {
|
||||||
width: calc(100vw - 16px);
|
width: calc(100vw - 16px);
|
||||||
|
}
|
||||||
|
#main aside hr {
|
||||||
|
width: auto;
|
||||||
}
|
}
|
||||||
#main {
|
#main {
|
||||||
display: block;
|
display: block;
|
||||||
|
@ -298,6 +354,7 @@ footer a {
|
||||||
min-width: calc(100vw - 16px);
|
min-width: calc(100vw - 16px);
|
||||||
}
|
}
|
||||||
#main aside {
|
#main aside {
|
||||||
|
background-color: var(--bg-0);
|
||||||
bottom: 0;
|
bottom: 0;
|
||||||
flex-direction: row;
|
flex-direction: row;
|
||||||
position: fixed;
|
position: fixed;
|
||||||
|
|
Loading…
Add table
Reference in a new issue