resman/backend/public/index.html

327 lines
5.1 KiB
HTML

```html
<!DOCTYPE html>
<html lang="de">
<head>
<meta charset="UTF-8">
<title>Resource Manager</title>
<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.2/dist/css/bootstrap.min.css" rel="stylesheet">
<style>
body{
background:#f5f6fa;
}
.card{
margin-bottom:20px;
}
.ip{
font-size:0.9em;
}
</style>
</head>
<body>
<div class="container mt-4">
<h2>Resource Manager</h2>
<div class="card">
<div class="card-header">
Neue Resource
</div>
<div class="card-body">
<div class="row g-2">
<div class="col">
<input id="name" class="form-control" placeholder="Name">
</div>
<div class="col">
<input id="customer" class="form-control" placeholder="Customer">
</div>
<div class="col">
<select id="status" class="form-select">
<option value="aktiv">aktiv</option>
<option value="gekündigt">gekündigt</option>
</select>
</div>
<div class="col">
<button class="btn btn-primary" onclick="createResource()">Create</button>
</div>
</div>
</div>
</div>
<h4>Aktive Ressourcen</h4>
<div id="activeResources"></div>
<h4 class="mt-4">Gekündigte Ressourcen</h4>
<div id="cancelledResources"></div>
</div>
<script>
const API="/resman/api";
function statusBadge(status){
if(status==="gekündigt"){
return `<span class="badge bg-dark">Gekündigt</span>`;
}
return `<span class="badge bg-success">Aktiv</span>`;
}
function renderResource(r){
return `
<div class="card">
<div class="card-body">
<div class="d-flex justify-content-between">
<div>
<h5>${r.name || ""}</h5>
${statusBadge(r.status)}
<br>
<small>${r.customer || ""}</small>
</div>
<div>
<button class="btn btn-sm btn-danger" onclick="deleteResource(${r.id})">
Delete
</button>
</div>
</div>
<hr>
<strong>IPs</strong>
<div id="ips-${r.id}" class="mb-2"></div>
<div class="input-group input-group-sm">
<input class="form-control" placeholder="IP" id="ip-${r.id}">
<select class="form-select" id="type-${r.id}">
<option value="public">public</option>
<option value="private">private</option>
</select>
<input class="form-control" placeholder="comment" id="comment-${r.id}">
<button class="btn btn-primary" onclick="addIP(${r.id})">
Add
</button>
</div>
</div>
</div>
`;
}
async function loadResources(){
const activeDiv=document.getElementById("activeResources");
const cancelledDiv=document.getElementById("cancelledResources");
activeDiv.innerHTML="";
cancelledDiv.innerHTML="";
try{
const res=await fetch(`${API}/resources/active`);
const active=await res.json();
active.forEach(r=>{
activeDiv.innerHTML+=renderResource(r);
setTimeout(()=>loadIPs(r.id),100);
});
}catch(e){
console.error(e);
}
try{
const res=await fetch(`${API}/resources/cancelled`);
const cancelled=await res.json();
cancelled.forEach(r=>{
cancelledDiv.innerHTML+=renderResource(r);
setTimeout(()=>loadIPs(r.id),100);
});
}catch(e){
console.error(e);
}
}
async function createResource(){
const name=document.getElementById("name").value;
const customer=document.getElementById("customer").value;
const status=document.getElementById("status").value;
await fetch(`${API}/resources`,{
method:"POST",
headers:{
"Content-Type":"application/json"
},
body:JSON.stringify({
name,
customer,
status
})
});
document.getElementById("name").value="";
document.getElementById("customer").value="";
loadResources();
}
async function deleteResource(id){
await fetch(`${API}/resources/${id}`,{
method:"DELETE"
});
async function addIP(resourceId){
const ip=document.getElementById("ip-"+resourceId).value;
const type=document.getElementById("type-"+resourceId).value;
const comment=document.getElementById("comment-"+resourceId).value;
if(!ip){
alert("IP fehlt");
return;
}
await fetch("/resman/api/resources/"+resourceId+"/ips",{
method:"POST",
headers:{
"Content-Type":"application/json"
},
body:JSON.stringify({
ip:ip,
type:type,
comment:comment
})
});
loadResources();
}
loadResources();
}
async function loadIPs(resourceId){
const container=document.getElementById("ips-"+resourceId);
const res=await fetch(`${API}/resources/${resourceId}/ips`);
const data=await res.json();
let html="";
data.forEach(ip=>{
html+=`
<div class="d-flex justify-content-between border rounded p-1 mb-1 ip">
<div>
<strong>${ip.ip}</strong>
<span class="text-muted">(${ip.type})</span>
<small>${ip.comment || ""}</small>
</div>
<button class="btn btn-sm btn-danger"
onclick="deleteIP(${ip.id},${resourceId})">
X
</button>
</div>
`;
});
container.innerHTML=html;
}
async function addIP(resourceId){
const ip=document.getElementById("ip-"+resourceId).value;
const type=document.getElementById("type-"+resourceId).value;
const comment=document.getElementById("comment-"+resourceId).value;
await fetch(`${API}/resources/${resourceId}/ips`,{
method:"POST",
headers:{
"Content-Type":"application/json"
},
body:JSON.stringify({
ip,
type,
comment
})
});
document.getElementById("ip-"+resourceId).value="";
document.getElementById("comment-"+resourceId).value="";
loadIPs(resourceId);
}
async function deleteIP(ipId,resourceId){
await fetch(`${API}/ips/${ipId}`,{
method:"DELETE"
});
loadIPs(resourceId);
}
document.addEventListener("DOMContentLoaded", () => {
loadResources();
});
</script>
</body>
</html>