blob: aebbd39e49435df000c7f3c19d3d2d8b46758607 [file] [log] [blame]
{{ define "schema-form" }}
{{ $readonly := .ReadOnly }}
{{ $networks := .AvailableNetworks }}
{{ $data := .Data }}
{{ range $name, $schema := .Schema.Fields }}
{{ if eq $schema.Kind 0 }}
<label for="{{ $name }}">
<span>{{ $name }}</span>
</label>
<input type="checkbox" role="swtich" name="{{ $name }}" oninput="valueChanged({{ $name }}, this.checked)" {{ if $readonly }}disabled{{ end }} {{ if index $data $name }}checked{{ end }}/>
{{ else if eq $schema.Kind 1 }}
<label for="{{ $name }}">
<span>{{ $name }}</span>
</label>
<input type="text" name="{{ $name }}" oninput="valueChanged({{ $name }}, this.value)" {{ if $readonly }}disabled{{ end }} value="{{ index $data $name }}"/>
{{ else if eq $schema.Kind 4 }}
<label for="{{ $name }}">
<span>{{ $name }}</span>
</label>
<input type="text" name="{{ $name }}" oninput="valueChanged({{ $name }}, this.value)" {{ if $readonly }}disabled{{ end }} value="{{ index $data $name }}"/>
{{ else if eq $schema.Kind 3 }}
<select oninput="valueChanged({{ $name }}, this.value)" {{ if $readonly }}disabled{{ end }} >
{{ if not $readonly }}<option disabled selected value> -- select an option -- </option>{{ end }}
{{ range $networks }}
<option {{if eq .Name (index $data $name) }}selected{{ end }}>{{ .Name }}</option>
{{ end }}
</select>
{{ end }}
{{ end }}
{{ end }}
{{ define "main" }}
{{ $instance := .Instance }}
<h1>{{ .App.Icon }}{{ .App.Name }}</h1>
<pre id="readme"></pre>
{{ $schema := .App.Schema }}
{{ $networks := .AvailableNetworks }}
<form id="config-form">
{{ if $instance }}
{{ template "schema-form" (dict "Schema" $schema "AvailableNetworks" $networks "ReadOnly" false "Data" $instance.Config) }}
{{ else }}
{{ template "schema-form" (dict "Schema" $schema "AvailableNetworks" $networks "ReadOnly" false "Data" (dict)) }}
{{ end }}
{{ if $instance }}
<div class="grid">
<button type="submit" id="submit" name="update">Update</button>
<button type="submit" id="uninstall" name="remove">Uninstall</button>
</div>
{{ else }}
<button type="submit" id="submit">{{ if $instance }}Update{{ else }}Install{{ end }}</button>
{{ end }}
</form>
{{ range .Instances }}
{{ if or (not $instance) (ne $instance.Id .Id)}}
<details>
<summary>{{ .Id }}</summary>
{{ template "schema-form" (dict "Schema" $schema "AvailableNetworks" $networks "ReadOnly" true "Data" .Config ) }}
<a href="/instance/{{ .Id }}" role="button" class="secondary">View</a>
</details>
{{ end }}
{{ end }}
<div id="toast-success" class="toast hidden">
<svg xmlns="http://www.w3.org/2000/svg" width="36" height="36" viewBox="0 0 36 36"><path fill="currentColor" d="M18 2a16 16 0 1 0 16 16A16 16 0 0 0 18 2Zm0 30a14 14 0 1 1 14-14a14 14 0 0 1-14 14Z" class="clr-i-outline clr-i-outline-path-1"/><path fill="currentColor" d="M28 12.1a1 1 0 0 0-1.41 0l-11.1 11.05l-6-6A1 1 0 0 0 8 18.53L15.49 26L28 13.52a1 1 0 0 0 0-1.42Z" class="clr-i-outline clr-i-outline-path-2"/><path fill="none" d="M0 0h36v36H0z"/></svg> {{ if $instance }}Update succeeded{{ else }}Install succeeded{{ end}}
</div>
<div id="toast-failure" class="toast hidden">
<svg xmlns="http://www.w3.org/2000/svg" width="36" height="36" viewBox="0 0 24 24"><path fill="none" stroke="currentColor" stroke-linecap="round" stroke-linejoin="round" stroke-width="1.5" d="M12 22c5.523 0 10-4.477 10-10S17.523 2 12 2S2 6.477 2 12s4.477 10 10 10Zm3-6L9 8m0 8l6-8"/></svg> {{ if $instance }}Update failed{{ else}}Install failed{{ end }}
</div>
<div id="toast-uninstall-success" class="toast hidden">
<svg xmlns="http://www.w3.org/2000/svg" width="36" height="36" viewBox="0 0 36 36"><path fill="currentColor" d="M18 2a16 16 0 1 0 16 16A16 16 0 0 0 18 2Zm0 30a14 14 0 1 1 14-14a14 14 0 0 1-14 14Z" class="clr-i-outline clr-i-outline-path-1"/><path fill="currentColor" d="M28 12.1a1 1 0 0 0-1.41 0l-11.1 11.05l-6-6A1 1 0 0 0 8 18.53L15.49 26L28 13.52a1 1 0 0 0 0-1.42Z" class="clr-i-outline clr-i-outline-path-2"/><path fill="none" d="M0 0h36v36H0z"/></svg> Uninstalled application
</div>
<div id="toast-uninstall-failure" class="toast hidden">
<svg xmlns="http://www.w3.org/2000/svg" width="36" height="36" viewBox="0 0 24 24"><path fill="none" stroke="currentColor" stroke-linecap="round" stroke-linejoin="round" stroke-width="1.5" d="M12 22c5.523 0 10-4.477 10-10S17.523 2 12 2S2 6.477 2 12s4.477 10 10 10Zm3-6L9 8m0 8l6-8"/></svg> Failed to uninstall application
</div>
<style>
pre {
white-space: pre-wrap; /* Since CSS 2.1 */
white-space: -moz-pre-wrap; /* Mozilla, since 1999 */
white-space: -pre-wrap; /* Opera 4-6 */
white-space: -o-pre-wrap; /* Opera 7 */
word-wrap: break-word; /* Internet Explorer 5.5+ */
background-color: transparent;
}
.hidden {
visibility: hidden;
}
.toast {
position: fixed;
z-index: 999;
bottom: 10px;
}
</style>
<script>
let readme = "";
let config = {{ if $instance }}JSON.parse({{ toJson $instance.Config }}){{ else }}{}{{ end }};
function valueChanged(name, value) {
config[name] = value;
renderReadme();
}
async function renderReadme() {
const resp = await fetch("/api/app/{{ .App.Name }}/render", {
method: "POST",
headers: {
"Content-Type": "application/json",
"Accept": "application/json",
},
body: JSON.stringify(config),
});
const app = await resp.json();
document.getElementById("readme").innerHTML = app.readme;
}
{{ if $instance }}renderReadme();{{ end }}
function disableForm() {
document.querySelectorAll("#config-form input").forEach((i) => i.setAttribute("disabled", ""));
document.querySelectorAll("#config-form select").forEach((i) => i.setAttribute("disabled", ""));
document.querySelectorAll("#config-form button").forEach((i) => i.setAttribute("disabled", ""));
}
function enableForm() {
document.querySelectorAll("[aria-busy]").forEach((i) => i.removeAttribute("aria-busy"));
document.querySelectorAll("#config-form input").forEach((i) => i.removeAttribute("disabled"));
document.querySelectorAll("#config-form select").forEach((i) => i.removeAttribute("disabled"));
document.querySelectorAll("#config-form button").forEach((i) => i.removeAttribute("disabled"));
}
function installStarted() {
const submit = document.getElementById("submit");
submit.setAttribute("aria-busy", true);
submit.innerHTML = {{ if $instance }}"Updating ..."{{ else }}"Installing ..."{{ end }};
disableForm();
}
function uninstallStarted() {
const submit = document.getElementById("uninstall");
submit.setAttribute("aria-busy", true);
submit.innerHTML = "Uninstalling ...";
disableForm();
}
function actionFinished(toast) {
enableForm();
toast.classList.remove("hidden");
setTimeout(
() => toast.classList.add("hidden"),
2000,
);
}
function installSucceeded() {
actionFinished(document.getElementById("toast-success"));
}
function installFailed() {
actionFinished(document.getElementById("toast-failure"));
}
function uninstallSucceeded() {
actionFinished(document.getElementById("toast-uninstall-success"));
}
function uninstallFailed() {
actionFinished(document.getElementById("toast-uninstall-failure"));
}
const submitAddr = {{ if $instance }}"/api/instance/{{ $instance.Id }}/update"{{ else }}"/api/app/{{ .App.Name }}/install"{{ end }};
async function install() {
installStarted();
const resp = await fetch(submitAddr, {
method: "POST",
headers: {
"Content-Type": "application/json",
"Accept": "application/json",
},
body: JSON.stringify(config),
});
if (resp.status === 200) {
installSucceeded();
} else {
installFailed();
}
}
async function uninstall() {
{{ if $instance }}
uninstallStarted();
const resp = await fetch("/api/instance/{{ $instance.Id }}/remove", {
method: "POST",
});
if (resp.status === 200) {
uninstallSucceeded();
} else {
uninstallFailed();
}
{{ end }}
}
document.getElementById("config-form").addEventListener("submit", (event) => {
event.preventDefault();
if (event.submitter.id === "submit") {
install();
} if (event.submitter.id === "uninstall") {
uninstall();
}
});
</script>
{{ end }}