-
Notifications
You must be signed in to change notification settings - Fork 12
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge remote-tracking branch 'origin/develop' into sql/feature/suppor…
…t_multi_column_validity_dates
- Loading branch information
Showing
12 changed files
with
250 additions
and
127 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
231 changes: 161 additions & 70 deletions
231
backend/src/main/resources/com/bakdata/conquery/resources/admin/ui/jobs.html.ftl
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,72 +1,163 @@ | ||
<#import "templates/template.html.ftl" as layout> | ||
<#import "templates/breadcrumbs.html.ftl" as breadcrumbs> | ||
<#import "templates/accordion.html.ftl" as accordion> | ||
|
||
<@layout.layout> | ||
<div class="row"> | ||
<div class="col"> | ||
<input type="checkbox" id="update" name="update" checked> | ||
<label for="update">Reload automatically.</label><br> | ||
<script type="text/javascript"> | ||
setTimeout(function () { | ||
if(!document.getElementById("update").checked){ | ||
return | ||
} | ||
location.reload(false); | ||
}, 5000); | ||
function cancelJob(jobId) { | ||
event.preventDefault(); | ||
fetch( | ||
${r"`/admin/jobs/${jobId}/cancel`"}, | ||
{ | ||
method: "post", | ||
credentials: "same-origin" | ||
} | ||
) | ||
} | ||
</script> | ||
</div> | ||
</div> | ||
|
||
|
||
<#list c as status> | ||
<div class="row"> | ||
<div class="col"> | ||
<div class="card"> | ||
<div class="card-body"> | ||
<h5 class="card-title"> | ||
${status.origin} ${(status.dataset)!} | ||
<span class="float-right"> | ||
<small>updated ${status.ageString} ago</small> | ||
<span class="badge badge-secondary">${status.jobs?size}</span> | ||
</span> | ||
</h5> | ||
<div class="card-text" style="max-height:50vh; overflow: auto"> | ||
<table class="table"> | ||
<#list status.jobs as job> | ||
<tr class="${job.cancelled?then('active','')}"> | ||
<td> | ||
${job.label} | ||
</td> | ||
<td class="w-100"> | ||
<div class="progress position-relative"> | ||
<div class="progress-bar" role="progressbar" style="width: ${job.progress?string.percent}" aria-valuenow="${job.progress}" aria-valuemin="0" aria-valuemax="1"></div> | ||
</div> | ||
</td> | ||
<td> | ||
<#if !job.cancelled> | ||
<a href="" onclick="cancelJob('${job.jobId}')" class="btn btn-warning btn-sm"/> | ||
<#else> | ||
<div>Cancelled</div> | ||
</#if> | ||
</td> | ||
</tr> | ||
</#list> | ||
</table> | ||
</div> | ||
</div> | ||
</div> | ||
</div> | ||
</div> | ||
</#list> | ||
</@layout.layout> | ||
<script type="text/javascript"> | ||
function cancelJob(jobId) { | ||
event.preventDefault(); | ||
fetch( | ||
"/admin/jobs/" + jobId + "/cancel", | ||
{ | ||
method: "post", | ||
credentials: "same-origin" | ||
} | ||
); | ||
} | ||
function getJobs() { | ||
return fetch("/admin/jobs") | ||
.then((res) => res.json()) | ||
.then((entries) => { | ||
const origins = {}; | ||
entries.forEach((entry) => { | ||
const origin = entry.origin | ||
origins[entry.origin] = [ | ||
...(origins[entry.origin] ?? []), | ||
entry | ||
]; | ||
}); | ||
return origins; | ||
}); | ||
} | ||
function findNodeOrCloneTemplate(templateId, newId = "", parentNode) { | ||
if (newId !== "" && document.getElementById(newId)) { | ||
return document.getElementById(newId); | ||
} else { | ||
const clonedTemplate = document.getElementById(templateId).cloneNode(true); | ||
clonedTemplate.id = newId; | ||
parentNode?.appendChild(clonedTemplate); | ||
return clonedTemplate; | ||
} | ||
} | ||
async function refreshJobs() { | ||
const origins = await getJobs(); | ||
Object.keys(origins).forEach((origin) => { | ||
const nodes = origins[origin]; | ||
const accordion = findNodeOrCloneTemplate("originTemplate", "origin_" + origin, document.getElementById("nodesAccordionGroup")); | ||
accordion.querySelector("h5").innerText = origin; | ||
const accordionDetails = findNodeOrCloneTemplate("originDetailsTemplate"); | ||
const timeDifference = (new Date() - new Date(nodes[0].timestamp)) / 1000; | ||
accordionDetails.querySelector(".ageString").innerText = timeDifference + "s"; | ||
accordionDetails.querySelector(".jobsAmount").innerText = nodes | ||
.map((node) => node?.jobs.length ?? 0) | ||
.reduce((partialSum, x) => partialSum + x, 0); | ||
accordion.querySelector(".accordion-infotext").innerHTML = ""; | ||
accordion.querySelector(".accordion-infotext").appendChild(accordionDetails); | ||
nodes.forEach((node) => { | ||
const fullName = node.origin + (node.dataset ? "::" + node.dataset : "") | ||
const nodeElement = findNodeOrCloneTemplate("nodeTemplate", "node_" + fullName, accordion.querySelector(".accordionContent")); | ||
nodeElement.querySelector(".nodeName").innerText = fullName; | ||
nodeElement.querySelector(".jobsAmount").innerText = node?.jobs?.length ?? "0"; | ||
const jobsList = nodeElement.querySelector(".jobsList"); | ||
if (node?.jobs.length > 0) { | ||
jobsList.innerHTML = ""; | ||
node.jobs.forEach((job) => { | ||
const jobElement = findNodeOrCloneTemplate("jobTemplate", "job_" + job.jobId, jobsList); | ||
jobElement.querySelector(".jobLabel").innerText = job?.label; | ||
jobElement.querySelector(".jobLabel").title = job?.label; | ||
jobElement.querySelector(".jobProgress").style.width = Math.round((job?.progress ?? 0) * 100) + "%"; | ||
jobElement.querySelector(".jobProgress").attributes["aria-valuenow"] = job?.progress; | ||
const jobActionElement = jobElement.querySelector(".jobAction"); | ||
if (job?.cancelled) { | ||
jobActionElement.innerText = 'Cancelled'; | ||
} else { | ||
jobActionElement.querySelector("button").onclick = () => cancelJob(job?.jobId); | ||
} | ||
}); | ||
} else { | ||
jobsList.innerHTML = ""; | ||
findNodeOrCloneTemplate("nojobsTemplate", "", jobsList); | ||
} | ||
}); | ||
}); | ||
// collapse accordions on page laod | ||
if (!this.alreadyExecuted) { | ||
this.alreadyExecuted = true; | ||
document.querySelectorAll(".collapse").forEach((elem) => elem.classList.remove("show")); | ||
} | ||
} | ||
refreshJobs(); | ||
setInterval(function () { | ||
if(!document.getElementById("update")?.checked) return; | ||
refreshJobs(); | ||
}, 5000); | ||
</script> | ||
<@breadcrumbs.breadcrumbs | ||
labels=["Jobs"] | ||
/> | ||
<div class="d-flex justify-content-end"> | ||
<div class="custom-control custom-switch"> | ||
<input type="checkbox" class="custom-control-input" id="update" checked> | ||
<label class="custom-control-label" for="update">Reload automatically</label> | ||
</div> | ||
</div> | ||
<@accordion.accordionGroup id="nodesAccordionGroup" class="mt-3"></@accordion.accordionGroup> | ||
</@layout.layout> | ||
<!-- HTML Templates --> | ||
<div class="d-none"> | ||
<@accordion.accordion summary="" id="originTemplate"></@accordion.accordion> | ||
<div id="originDetailsTemplate"> | ||
updated <span class="ageString"></span> ago | ||
<span class="jobsAmount badge badge-secondary"></span> | ||
</div> | ||
<div id="nodeTemplate"> | ||
<div class="d-flex justify-content-between align-items-center"> | ||
<span class="nodeName py-2" style="font-size: 1.2rem;"></span> | ||
<div> | ||
<span class="jobsAmount badge badge-secondary"></span> | ||
</div> | ||
</div> | ||
<div class="jobsList p-2 mb-3 border" style="max-height: 200px; overflow: auto;"></div> | ||
</div> | ||
<div id="nojobsTemplate" class="w-100 text-black-50 text-center">No jobs in this node</div> | ||
<div | ||
id="jobTemplate" | ||
class="d-flex justify-content-between align-items-center py-2" | ||
style="gap: 25px; border-bottom: 1px solid #ccc" | ||
> | ||
<div | ||
class="jobLabel text-nowrap" | ||
style="overflow: hidden; text-overflow: ellipsis; flex-basis: 600px;" | ||
></div> | ||
<div class="progress position-relative" style="flex-grow: 1;"> | ||
<div class="jobProgress progress-bar" role="progressbar" aria-valuemin="0" aria-valuemax="1"></div> | ||
</div> | ||
<div class="jobAction d-flex justify-content-center" style="flex-basis: 80px;"> | ||
<button | ||
type="button" | ||
class="btn btn-danger btn-sm text-white fas fa-ban" | ||
data-toggle="tooltip" | ||
data-placement="bottom" | ||
title="Cancel Job" | ||
/> | ||
</div> | ||
</div> | ||
</div> |
Oops, something went wrong.