chore: added model delete related to #2
This commit is contained in:
@@ -4,12 +4,25 @@
|
||||
<h1>
|
||||
404
|
||||
</h1>
|
||||
<h2>
|
||||
Page Not found
|
||||
</h2>
|
||||
<div class="description">
|
||||
The page you were looking for does not exist
|
||||
</div>
|
||||
{{ if .NotFoundMessage }}
|
||||
<h2>
|
||||
{{ .NotFoundMessage }}
|
||||
</h2>
|
||||
{{ if .GoBackLink }}
|
||||
<div class="description">
|
||||
<a hx-get="{{ .GoBackLink }}" hx-headers='{"REQUEST-TYPE": "htmlfull"}' hx-push-url="true" hx-swap="outerHTML" hx-target=".app">
|
||||
👈 Go back
|
||||
</a>
|
||||
</div>
|
||||
{{ end }}
|
||||
{{ else }}
|
||||
<h2>
|
||||
Page Not found
|
||||
</h2>
|
||||
<div class="description">
|
||||
The page you were looking for does not exist
|
||||
</div>
|
||||
{{ end }}
|
||||
</div>
|
||||
</div>
|
||||
{{ end }}
|
||||
|
||||
BIN
views/imgs/upload-icon.png
Normal file
BIN
views/imgs/upload-icon.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 5.4 KiB |
@@ -1,14 +1,49 @@
|
||||
function load() {
|
||||
for (const elm of document.querySelectorAll("form > button")) {
|
||||
elm.addEventListener('click', (e) => {
|
||||
e.target.parentElement.classList.add("submitted");
|
||||
});
|
||||
}
|
||||
for (const elm of document.querySelectorAll("form > button")) {
|
||||
elm.addEventListener('click', (e) => {
|
||||
e.target.parentElement.classList.add("submitted");
|
||||
});
|
||||
}
|
||||
for (const elm of document.querySelectorAll("button.icon")) {
|
||||
elm.addEventListener('click', (e) => {
|
||||
e.preventDefault()
|
||||
const input = document.querySelectorAll('form .file-upload input[type="file"]')[0];
|
||||
if (input) {
|
||||
input.click();
|
||||
input.addEventListener('change', (e) => {
|
||||
const file = input.files[0];
|
||||
if (!file) return;
|
||||
elm.setAttribute("disabled", "true");
|
||||
|
||||
const spanToReplace = document.querySelector('.file-upload .icon span');
|
||||
const imgToReplace = document.querySelector('.file-upload .icon img');
|
||||
if (!imgToReplace || !spanToReplace) return;
|
||||
|
||||
if (imgToReplace.getAttribute("replace")) {
|
||||
const fileReader = new FileReader();
|
||||
fileReader.onloadend = () => {
|
||||
imgToReplace.setAttribute("src", fileReader.result)
|
||||
elm.classList.add("adapt");
|
||||
}
|
||||
fileReader.readAsDataURL(file)
|
||||
}
|
||||
|
||||
if (spanToReplace.getAttribute("replace")) {
|
||||
spanToReplace.innerHTML = spanToReplace.getAttribute("replace")
|
||||
}
|
||||
})
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
window.onload = load;
|
||||
htmx.on('htmx:afterSwap', load);
|
||||
htmx.on('htmx:beforeSwap', (env) => {
|
||||
if (env.detail.xhr.status === 401) {
|
||||
window.location = "/login"
|
||||
}
|
||||
} else
|
||||
// 309 is the code I selected for html to htmlfull change
|
||||
if (env.detail.xhr.status === 309) {
|
||||
env.detail.target = htmx.find(".app")
|
||||
}
|
||||
});
|
||||
|
||||
41
views/models/add.html
Normal file
41
views/models/add.html
Normal file
@@ -0,0 +1,41 @@
|
||||
{{define "title"}}
|
||||
Create New Model : AI Stuff
|
||||
{{end}}
|
||||
|
||||
{{define "mainbody"}}
|
||||
<main>
|
||||
<h1>
|
||||
Create new Model
|
||||
</h1>
|
||||
<form enctype="multipart/form-data" action="/models/add" method="POST" {{if .Submited}}class="submitted"{{end}} >
|
||||
<fieldset>
|
||||
<label for="name">Name</label>
|
||||
<input id="name" name="name" required {{if .Name}} value="{{.Name}}" {{end}} />
|
||||
{{if .NameFoundError}}
|
||||
<span class="form-msg error">
|
||||
You already have a model with that name.
|
||||
</span>
|
||||
{{end}}
|
||||
</fieldset>
|
||||
<fieldset class="file-upload" >
|
||||
<label for="file">Base image</label>
|
||||
<div class="form-msg">
|
||||
Please provide a base image.<br/>
|
||||
This image is a sample of the images that you are going to classfy.
|
||||
</div>
|
||||
<div class="icon-holder">
|
||||
<button class="icon">
|
||||
<img replace="true" src="/imgs/upload-icon.png" />
|
||||
<span replace="Image selected">
|
||||
Upload image
|
||||
</span>
|
||||
</button>
|
||||
<input id="file" name="file" type="file" required accept="image/png" />
|
||||
</div>
|
||||
</fieldset>
|
||||
<button>
|
||||
Create
|
||||
</button>
|
||||
</form>
|
||||
</main>
|
||||
{{end}}
|
||||
12
views/models/delete.html
Normal file
12
views/models/delete.html
Normal file
@@ -0,0 +1,12 @@
|
||||
{{ define "mainbody" }}
|
||||
<main>
|
||||
<h2 class="text-center">
|
||||
Model {{ .Model.Name }} was deleted!
|
||||
</h2>
|
||||
<div class="description text-center">
|
||||
<a hx-get="/models" hx-headers='{"REQUEST-TYPE": "htmlfull"}' hx-push-url="true" hx-swap="outerHTML" hx-target=".app">
|
||||
👈 Go back
|
||||
</a>
|
||||
</div>
|
||||
</main>
|
||||
{{ end }}
|
||||
83
views/models/edit.html
Normal file
83
views/models/edit.html
Normal file
@@ -0,0 +1,83 @@
|
||||
{{ define "title" }}
|
||||
Model: {{ .Model.Name }}
|
||||
{{ end }}
|
||||
|
||||
{{ define "base-model-card" }}
|
||||
<div class="card model-card">
|
||||
<h1>
|
||||
{{ .Model.Name }}
|
||||
</h1>
|
||||
<div class="second-line">
|
||||
<img src="/savedData/{{ .Model.Id }}/baseimage.png" />
|
||||
<div class="info">
|
||||
<div>
|
||||
<span class="bold bigger">Image Type:</span> {{ .Model.Color_mode }}
|
||||
</div>
|
||||
<div>
|
||||
<span class="bold bigger">Image Size:</span> {{ .Model.Width }}x{{ .Model.Height }}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
{{ end }}
|
||||
|
||||
{{ define "delete-model-card" }}
|
||||
<form hx-delete="/models/delete" hx-headers='{"REQUEST-TYPE": "html"}' hx-swap="outerHTML" {{ if .Error }} class="submitted" {{end}} >
|
||||
<fieldset>
|
||||
<span>
|
||||
To delete this model please type "{{ .Model.Name }}":
|
||||
</span>
|
||||
<label for="name">Name</label>
|
||||
<input name="name" id="name" required />
|
||||
{{ if .NameDoesNotMatch }}
|
||||
<span class="form-msg red">
|
||||
Name does not match "{{ .Model.Name }}"
|
||||
</span>
|
||||
{{ end }}
|
||||
</fieldset>
|
||||
<input type="hidden" name="id" value="{{ .Model.Id }}" />
|
||||
<button class="danger">
|
||||
Delete
|
||||
</button>
|
||||
</form>
|
||||
{{ end }}
|
||||
|
||||
{{ define "mainbody" }}
|
||||
<main>
|
||||
{{ if (eq .Model.Status 1) }}
|
||||
<div>
|
||||
<h1 class="text-center">
|
||||
{{ .Model.Name }}
|
||||
</h1>
|
||||
<!-- TODO add cool animation -->
|
||||
<h2 class="text-center">
|
||||
Preparing the model
|
||||
</h2>
|
||||
</div>
|
||||
{{ else if (eq .Model.Status -1) }}
|
||||
<div>
|
||||
<h1 class="text-center">
|
||||
{{ .Model.Name }}
|
||||
</h1>
|
||||
<!-- TODO improve message -->
|
||||
<h2 class="text-center">
|
||||
Failed to prepare model
|
||||
</h2>
|
||||
|
||||
<form hx-delete="/models/delete">
|
||||
<input type="hidden" value="{{ .Model.Id }}" />
|
||||
<button class="danger">
|
||||
Delete
|
||||
</button>
|
||||
</form>
|
||||
</div>
|
||||
{{ else if (eq .Model.Status 2) }}
|
||||
{{ template "base-model-card" . }}
|
||||
{{ template "delete-model-card" . }}
|
||||
{{ else }}
|
||||
<h1>
|
||||
Unknown Status of the model.
|
||||
</h1>
|
||||
{{ end }}
|
||||
</main>
|
||||
{{ end }}
|
||||
52
views/models/list.html
Normal file
52
views/models/list.html
Normal file
@@ -0,0 +1,52 @@
|
||||
{{define "title"}}
|
||||
Models : AI Stuff
|
||||
{{end}}
|
||||
|
||||
{{define "mainbody"}}
|
||||
<main>
|
||||
{{ if (lt 0 (len .List)) }}
|
||||
<div class="list-header">
|
||||
<h2>My Models</h2>
|
||||
<div class="expand"></div>
|
||||
<a class="button" hx-get="/models/add" hx-headers='{"REQUEST-TYPE": "htmlfull"}' hx-push-url="true" hx-swap="outerHTML" hx-target=".app">
|
||||
New
|
||||
</a>
|
||||
</div>
|
||||
<table>
|
||||
<thead>
|
||||
<tr>
|
||||
<th>
|
||||
Name
|
||||
</th>
|
||||
<th>
|
||||
<!-- Open Button -->
|
||||
</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{{range .List}}
|
||||
<tr>
|
||||
<td>
|
||||
{{.Name}}
|
||||
</td>
|
||||
<td class="text-center">
|
||||
<a class="button simple" hx-get="/models/edit?id={{.Id}}" hx-headers='{"REQUEST-TYPE": "htmlfull"}' hx-push-url="true" hx-swap="outerHTML" hx-target=".app">
|
||||
Edit
|
||||
</a>
|
||||
</td>
|
||||
</tr>
|
||||
{{end}}
|
||||
</tbody>
|
||||
</table>
|
||||
{{else}}
|
||||
<h2 class="text-center">
|
||||
You don't have any model
|
||||
</h2>
|
||||
<div class="text-center">
|
||||
<a class="button padded" hx-get="/models/add" hx-headers='{"REQUEST-TYPE": "htmlfull"}' hx-push-url="true" hx-swap="outerHTML" hx-target=".app">
|
||||
Create a new model
|
||||
</a>
|
||||
</div>
|
||||
{{end}}
|
||||
</main>
|
||||
{{end}}
|
||||
@@ -5,6 +5,13 @@
|
||||
Index
|
||||
</a>
|
||||
</li>
|
||||
{{ if .Context.User }}
|
||||
<li>
|
||||
<a hx-get="/models" hx-headers='{"REQUEST-TYPE": "htmlfull"}' hx-push-url="true" hx-swap="outerHTML" hx-target=".app">
|
||||
Models
|
||||
</a>
|
||||
</li>
|
||||
{{end}}
|
||||
<li class="expand"></li>
|
||||
{{ if .Context.User }}
|
||||
<li>
|
||||
|
||||
@@ -1,7 +1,4 @@
|
||||
* {
|
||||
box-sizing: border-box;
|
||||
font-family: 'Roboto', sans-serif;
|
||||
}
|
||||
*{box-sizing: border-box;font-family: 'Roboto', sans-serif;}
|
||||
|
||||
:root {
|
||||
--white: #ffffff;
|
||||
@@ -18,6 +15,10 @@ body {
|
||||
padding: 0;
|
||||
}
|
||||
|
||||
main {
|
||||
padding: 20px 15vw;
|
||||
}
|
||||
|
||||
.w100 {
|
||||
width: 100%;
|
||||
display: block;
|
||||
@@ -32,6 +33,38 @@ body {
|
||||
text-decoration: none;
|
||||
}
|
||||
|
||||
.bold {
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
.bigger {
|
||||
font-size: 1.1rem;
|
||||
}
|
||||
|
||||
/* Generic */
|
||||
.button,
|
||||
button {
|
||||
border-radius: 10px;
|
||||
text-align: center;
|
||||
padding: 2px;
|
||||
border: none;
|
||||
box-shadow: 0 2px 8px 1px #66666655;
|
||||
background: var(--main);
|
||||
color: var(--black);
|
||||
}
|
||||
|
||||
.button.padded,
|
||||
button.padded {
|
||||
padding: 10px;
|
||||
}
|
||||
|
||||
.button.danger,
|
||||
button.danger {
|
||||
background: rgb(var(--red));
|
||||
color: white;
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
/* Nav bar */
|
||||
|
||||
nav {
|
||||
@@ -49,6 +82,11 @@ nav ul {
|
||||
|
||||
nav ul li {
|
||||
list-style: none;
|
||||
padding-left: 10px;
|
||||
}
|
||||
|
||||
nav ul li:first-child {
|
||||
padding: 0;
|
||||
}
|
||||
|
||||
nav ul .expand {
|
||||
@@ -102,6 +140,7 @@ a {
|
||||
|
||||
form {
|
||||
padding: 30px;
|
||||
margin: 20px 0;
|
||||
border-radius: 10px;
|
||||
box-shadow: 2px 5px 8px 2px #66666655;
|
||||
}
|
||||
@@ -124,6 +163,7 @@ form input:invalid:focus,
|
||||
form.submitted input:invalid {
|
||||
box-shadow: 0 2px 5px 1px rgba(var(--red), 0.2);
|
||||
}
|
||||
|
||||
form.submitted input:valid {
|
||||
box-shadow: 0 2px 5px 1px rgba(var(--green), 0.2);
|
||||
}
|
||||
@@ -146,16 +186,150 @@ form fieldset .error {
|
||||
}
|
||||
|
||||
form button {
|
||||
border-radius: 9px 10px;
|
||||
text-align: center;
|
||||
padding: 10px;
|
||||
background: none;
|
||||
border: none;
|
||||
box-shadow: 0 2px 8px 1px #66666655;
|
||||
font-size: 1.2rem;
|
||||
margin-left: 50%;
|
||||
width: 50%;
|
||||
transform: translateX(-50%);
|
||||
background: var(--main);
|
||||
color: var(--black);
|
||||
font-size: 1.2rem;
|
||||
padding: 10px;
|
||||
}
|
||||
|
||||
/* Upload files */
|
||||
|
||||
form fieldset.file-upload input[type="file"] {
|
||||
height: 1px;
|
||||
width: 1px;
|
||||
padding: 0;
|
||||
box-shadow: none;
|
||||
}
|
||||
|
||||
form fieldset.file-upload .icon-holder {
|
||||
padding: 10px;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
width: 100%;
|
||||
font-size: 1rem;
|
||||
}
|
||||
|
||||
form fieldset.file-upload .icon-holder .icon {
|
||||
height: 150px;
|
||||
width: 150px;
|
||||
padding: 20px;
|
||||
border-radius: 10px;
|
||||
background: none;
|
||||
transform: none;
|
||||
margin: 0;
|
||||
font-size: 1rem;
|
||||
transition: all 1s;
|
||||
}
|
||||
|
||||
form fieldset.file-upload .icon-holder .icon.adapt {
|
||||
width: auto;
|
||||
height: auto;
|
||||
max-width: 80%;
|
||||
max-height: 80%;
|
||||
min-height: 150px;
|
||||
min-width: 150px;
|
||||
padding: 20px;
|
||||
}
|
||||
|
||||
form fieldset.file-upload:has(input[type="file"]:invalid:focus) .icon,
|
||||
form.submitted fieldset.file-upload:has(input[type="file"]:invalid:focus) .icon {
|
||||
box-shadow: 0 2px 5px 1px rgba(var(--red), 0.2);
|
||||
}
|
||||
|
||||
form.submitted fieldset.file-upload:has(input[type="file"]:valid:focus) .icon{
|
||||
box-shadow: 0 2px 5px 1px rgba(var(--green), 0.2);
|
||||
}
|
||||
|
||||
form fieldset.file-upload .icon-holder .icon img {
|
||||
display: block;
|
||||
width: 100%;
|
||||
height: 80%;
|
||||
object-fit: contain;
|
||||
text-align: center;
|
||||
transition: all 1s;
|
||||
}
|
||||
|
||||
form fieldset.file-upload .icon-holder .icon span {
|
||||
display: block;
|
||||
width: 100%;
|
||||
padding-top: 10px;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
/* Lists */
|
||||
|
||||
.list-header {
|
||||
display: flex;
|
||||
padding-bottom: 10px;
|
||||
}
|
||||
|
||||
.list-header h2 {
|
||||
margin: 0;
|
||||
padding: 10px 5px;
|
||||
}
|
||||
|
||||
.list-header .expand {
|
||||
flex-grow: 1;
|
||||
}
|
||||
|
||||
.list-header .button,
|
||||
.list-header button {
|
||||
padding: 10px 10px;
|
||||
height: calc(100% - 20px);
|
||||
margin-top: 5px;
|
||||
}
|
||||
|
||||
/* Table */
|
||||
table {
|
||||
width: 100%;
|
||||
box-shadow: 0 2px 8px 1px #66666622;
|
||||
border-radius: 10px;
|
||||
border-collapse: collapse;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
table thead {
|
||||
background: #60606022;
|
||||
}
|
||||
|
||||
table tr td,
|
||||
table tr th {
|
||||
border-left: 1px solid #22222244;
|
||||
padding: 15px;
|
||||
}
|
||||
|
||||
table tr td:first-child,
|
||||
table tr th:first-child {
|
||||
border-left: none;
|
||||
}
|
||||
|
||||
table tr td button,
|
||||
table tr td .button {
|
||||
padding: 5px 10px;
|
||||
box-shadow: 0 2px 5px 1px #66666655;
|
||||
}
|
||||
|
||||
.card {
|
||||
box-shadow: 0 2px 5px 1px #66666655;
|
||||
padding: 20px;
|
||||
border-radius: 10px;
|
||||
}
|
||||
|
||||
/* Model stuff */
|
||||
.model-card h1 {
|
||||
margin: 0;
|
||||
padding-bottom: 10px;
|
||||
}
|
||||
|
||||
.model-card img {
|
||||
width: 25%;
|
||||
height: 100%;
|
||||
object-fit: contain;
|
||||
}
|
||||
|
||||
.model-card .second-line {
|
||||
display: flex;
|
||||
gap: 20px;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user