blob: 8d844a8230901efa705fc2be00f37be1a8d7a06c [file] [log] [blame]
Giorgi Lekveishvili4257b902023-07-07 17:08:42 +04001{{ define "schema-form" }}
2 {{ $readonly := .ReadOnly }}
3 {{ $networks := .AvailableNetworks }}
4 {{ $data := .Data }}
Giorgi Lekveishvili7c427602024-01-04 00:13:55 +04005 {{ range $name, $schema := .Schema.Fields }}
6 {{ if or (eq $schema.Kind 0) (eq $schema.Kind 1) }}
Giorgi Lekveishvili4257b902023-07-07 17:08:42 +04007 <label for="{{ $name }}">
8 <span>{{ $name }}</span>
9 </label>
Giorgi Lekveishvili7c427602024-01-04 00:13:55 +040010 <input type="text" name="{{ $name }}" oninput="valueChanged({{ $name }}, this.value)" {{ if $readonly }}disabled{{ end }} value="{{ index $data $name }}"/>
11 {{ else if eq $schema.Kind 3 }}
12 <select oninput="valueChanged({{ $name }}, this.value)" {{ if $readonly }}disabled{{ end }} >
13 {{ if not $readonly }}<option disabled selected value> -- select an option -- </option>{{ end }}
14 {{ range $networks }}
15 <option {{if eq .Name (index $data $name) }}selected{{ end }}>{{ .Name }}</option>
16 {{ end }}
17 </select>
18 {{ end }}
Giorgi Lekveishvili4257b902023-07-07 17:08:42 +040019 {{ end }}
20{{ end }}
21
22{{ define "main" }}
23{{ $instance := .Instance }}
24<h1>{{ .App.Icon }}{{ .App.Name }}</h1>
25<pre id="readme"></pre>
26
Giorgi Lekveishvili7c427602024-01-04 00:13:55 +040027{{ $schema := .App.Schema }}
Giorgi Lekveishvili4257b902023-07-07 17:08:42 +040028{{ $networks := .AvailableNetworks }}
29
30<form id="config-form">
31 {{ if $instance }}
32 {{ template "schema-form" (dict "Schema" $schema "AvailableNetworks" $networks "ReadOnly" false "Data" $instance.Config) }}
33 {{ else }}
34 {{ template "schema-form" (dict "Schema" $schema "AvailableNetworks" $networks "ReadOnly" false "Data" (dict)) }}
35 {{ end }}
36 {{ if $instance }}
37 <div class="grid">
38 <button type="submit" id="submit" name="update">Update</button>
39 <button type="submit" id="uninstall" name="remove">Uninstall</button>
40 </div>
41 {{ else }}
42 <button type="submit" id="submit">{{ if $instance }}Update{{ else }}Install{{ end }}</button>
43 {{ end }}
44</form>
45
46{{ range .Instances }}
47 {{ if or (not $instance) (ne $instance.Id .Id)}}
48 <details>
49 <summary>{{ .Id }}</summary>
50 {{ template "schema-form" (dict "Schema" $schema "AvailableNetworks" $networks "ReadOnly" true "Data" .Config ) }}
51 <a href="/instance/{{ .Id }}" role="button" class="secondary">View</a>
52 </details>
53 {{ end }}
54{{ end }}
55
56
57<div id="toast-success" class="toast hidden">
58 <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}}
59</div>
60
61<div id="toast-failure" class="toast hidden">
62 <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 }}
63</div>
64
65<div id="toast-uninstall-success" class="toast hidden">
66 <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
67</div>
68
69<div id="toast-uninstall-failure" class="toast hidden">
70 <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
71</div>
72
73<style>
74 pre {
75 white-space: pre-wrap; /* Since CSS 2.1 */
76 white-space: -moz-pre-wrap; /* Mozilla, since 1999 */
77 white-space: -pre-wrap; /* Opera 4-6 */
78 white-space: -o-pre-wrap; /* Opera 7 */
79 word-wrap: break-word; /* Internet Explorer 5.5+ */
80 background-color: transparent;
81 }
82
83 .hidden {
84 visibility: hidden;
85 }
86
87 .toast {
88 position: fixed;
89 z-index: 999;
90 bottom: 10px;
91 }
92</style>
93
94<script>
95 let readme = "";
96 let config = {{ if $instance }}JSON.parse({{ toJson $instance.Config }}){{ else }}{}{{ end }};
97
98 function valueChanged(name, value) {
99 config[name] = value;
100 renderReadme();
101 }
102
103 async function renderReadme() {
104 const resp = await fetch("/api/app/{{ .App.Name }}/render", {
105 method: "POST",
106 headers: {
107 "Content-Type": "application/json",
108 "Accept": "application/json",
109 },
110 body: JSON.stringify(config),
111 });
112 const app = await resp.json();
113 document.getElementById("readme").innerHTML = app.readme;
114 }
115
116 {{ if $instance }}renderReadme();{{ end }}
117
118 function disableForm() {
119 document.querySelectorAll("#config-form input").forEach((i) => i.setAttribute("disabled", ""));
120 document.querySelectorAll("#config-form select").forEach((i) => i.setAttribute("disabled", ""));
121 document.querySelectorAll("#config-form button").forEach((i) => i.setAttribute("disabled", ""));
122 }
123
124 function enableForm() {
125 document.querySelectorAll("[aria-busy]").forEach((i) => i.removeAttribute("aria-busy"));
126 document.querySelectorAll("#config-form input").forEach((i) => i.removeAttribute("disabled"));
127 document.querySelectorAll("#config-form select").forEach((i) => i.removeAttribute("disabled"));
128 document.querySelectorAll("#config-form button").forEach((i) => i.removeAttribute("disabled"));
129 }
130
131 function installStarted() {
132 const submit = document.getElementById("submit");
133 submit.setAttribute("aria-busy", true);
134 submit.innerHTML = {{ if $instance }}"Updating ..."{{ else }}"Installing ..."{{ end }};
135 disableForm();
136 }
137
138 function uninstallStarted() {
139 const submit = document.getElementById("uninstall");
140 submit.setAttribute("aria-busy", true);
141 submit.innerHTML = "Uninstalling ...";
142 disableForm();
143 }
144
145 function actionFinished(toast) {
146 enableForm();
147 toast.classList.remove("hidden");
148 setTimeout(
149 () => toast.classList.add("hidden"),
150 2000,
151 );
152 }
153
154 function installSucceeded() {
155 actionFinished(document.getElementById("toast-success"));
156 }
157
158 function installFailed() {
159 actionFinished(document.getElementById("toast-failure"));
160 }
161
162 function uninstallSucceeded() {
163 actionFinished(document.getElementById("toast-uninstall-success"));
164 }
165
166 function uninstallFailed() {
167 actionFinished(document.getElementById("toast-uninstall-failure"));
168 }
169
170 const submitAddr = {{ if $instance }}"/api/instance/{{ $instance.Id }}/update"{{ else }}"/api/app/{{ .App.Name }}/install"{{ end }};
171
172 async function install() {
173 installStarted();
174 const resp = await fetch(submitAddr, {
175 method: "POST",
176 headers: {
177 "Content-Type": "application/json",
178 "Accept": "application/json",
179 },
180 body: JSON.stringify(config),
181 });
182 if (resp.status === 200) {
183 installSucceeded();
184 } else {
185 installFailed();
186 }
187 }
188
189 async function uninstall() {
190 {{ if $instance }}
191 uninstallStarted();
192 const resp = await fetch("/api/instance/{{ $instance.Id }}/remove", {
193 method: "POST",
194 });
195 if (resp.status === 200) {
196 uninstallSucceeded();
197 } else {
198 uninstallFailed();
199 }
200 {{ end }}
201 }
202
203 document.getElementById("config-form").addEventListener("submit", (event) => {
204 event.preventDefault();
205 if (event.submitter.id === "submit") {
206 install();
207 } if (event.submitter.id === "uninstall") {
208 uninstall();
209 }
210 });
211</script>
212{{ end }}