resman/backend/public/index.html
2026-03-06 15:29:03 +01:00

534 lines
7.2 KiB
HTML
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>ResMan</title>
<style>
body{
font-family:Arial, Helvetica, sans-serif;
background:#0f172a;
color:#e5e7eb;
margin:0;
padding:20px;
}
h1{
margin-bottom:20px;
}
button{
background:#3b82f6;
border:none;
padding:8px 14px;
color:white;
border-radius:6px;
cursor:pointer;
}
button:hover{
background:#2563eb;
}
button.delete{
background:#ef4444;
}
button.delete:hover{
background:#dc2626;
}
.card{
background:#1e293b;
padding:15px;
border-radius:10px;
margin-bottom:15px;
border:1px solid #334155;
}
table{
width:100%;
border-collapse:collapse;
margin-top:10px;
}
th,td{
padding:10px;
border-bottom:1px solid #334155;
}
th{
text-align:left;
}
tr:hover{
background:#273449;
}
.modal{
position:fixed;
top:0;
left:0;
width:100%;
height:100%;
background:rgba(0,0,0,0.6);
display:flex;
align-items:center;
justify-content:center;
}
.modal-content{
background:#1e293b;
padding:25px;
border-radius:10px;
max-height:90vh;
overflow:auto;
width:700px;
border:1px solid #334155;
}
input,select,textarea{
width:100%;
padding:8px;
margin-bottom:10px;
border-radius:6px;
border:1px solid #334155;
background:#0f172a;
color:#e5e7eb;
}
label{
font-size:14px;
}
.form-grid{
display:grid;
grid-template-columns:1fr 1fr;
gap:10px;
}
.ip-list{
margin-top:10px;
}
.ip-item{
display:flex;
justify-content:space-between;
padding:6px;
border-bottom:1px solid #334155;
}
</style>
</head>
<body>
<h1>Resource Manager</h1>
<button onclick="openCreate()"> New Resource</button>
<h2>Active Resources</h2>
<table>
<thead>
<tr>
<th>Name</th>
<th>Provider</th>
<th>Produkt</th>
<th>CPU</th>
<th>RAM</th>
<th>Disk</th>
<th>IPs</th>
<th>Actions</th>
</tr>
</thead>
<tbody id="activeResources"></tbody>
</table>
<h2>Cancelled Resources</h2>
<table>
<thead>
<tr>
<th>Name</th>
<th>Provider</th>
<th>Produkt</th>
<th>CPU</th>
<th>RAM</th>
<th>Disk</th>
<th>IPs</th>
<th>Actions</th>
</tr>
</thead>
<tbody id="cancelledResources"></tbody>
</table>
<!-- MODAL -->
<div class="modal" id="resourceModal">
<div class="modal-content">
<h2 id="modalTitle"></h2>
<input id="name" placeholder="Name">
<input id="produkt" placeholder="Produkt">
<input id="provider" placeholder="Provider">
<input id="art" placeholder="Art">
<input id="cpu" placeholder="CPU">
<input id="ram" placeholder="RAM">
<input id="disk" placeholder="Disk">
<input id="os" placeholder="OS">
<input id="ipv6_net" placeholder="IPv6 Netz">
<input id="providername" placeholder="Provider Name">
<input id="kosten_monat" placeholder="Kosten Monat">
<input id="kosten_jahr" placeholder="Kosten Jahr">
<input id="laufzeit_monate" placeholder="Laufzeit Monate">
<input id="bestelldatum" type="date">
<input id="kuendbar_ab" type="date">
<select id="status">
<option value="aktiv">aktiv</option>
<option value="gekündigt">gekündigt</option>
</select>
<input id="kuendigungsdatum" type="date">
<textarea id="bemerkung" placeholder="Bemerkung"></textarea>
<button onclick="saveResource()">Save</button>
<button onclick="closeModal()">Cancel</button>
</div>
</div>
<script>
const API="/resman/api/resources"
let editId=null
function val(v){
if(v==="" || v===undefined) return null
return v
}
function getForm(){
return{
name:document.getElementById("name").value,
produkt:val(document.getElementById("produkt").value),
provider:val(document.getElementById("provider").value),
art:val(document.getElementById("art").value),
cpu:val(document.getElementById("cpu").value),
ram:val(document.getElementById("ram").value),
disk:val(document.getElementById("disk").value),
os:val(document.getElementById("os").value),
ipv6_net:val(document.getElementById("ipv6_net").value),
providername:val(document.getElementById("providername").value),
kosten_monat:val(document.getElementById("kosten_monat").value),
kosten_jahr:val(document.getElementById("kosten_jahr").value),
laufzeit_monate:val(document.getElementById("laufzeit_monate").value),
bestelldatum:val(document.getElementById("bestelldatum").value),
kuendbar_ab:val(document.getElementById("kuendbar_ab").value),
status:document.getElementById("status").value,
kuendigungsdatum:val(document.getElementById("kuendigungsdatum").value),
bemerkung:val(document.getElementById("bemerkung").value)
}
}
function openCreate(){
editId=null
document.getElementById("modalTitle").innerText="New Resource"
document.getElementById("resourceModal").style.display="flex"
}
function editResource(r){
editId=r.id
document.getElementById("modalTitle").innerText="Edit Resource"
Object.keys(r).forEach(k=>{
const el=document.getElementById(k)
if(el) el.value=r[k] || ""
})
document.getElementById("resourceModal").style.display="flex"
}
function closeModal(){
document.getElementById("resourceModal").style.display="none"
}
async function saveResource(){
const data=getForm()
if(editId){
await fetch(API+"/"+editId,{
method:"PUT",
headers:{"Content-Type":"application/json"},
body:JSON.stringify(data)
})
}else{
await fetch(API,{
method:"POST",
headers:{"Content-Type":"application/json"},
body:JSON.stringify(data)
})
}
closeModal()
loadResources()
}
async function deleteResource(id){
if(!confirm("Delete resource?")) return
await fetch(API+"/"+id,{method:"DELETE"})
loadResources()
}
async function addIP(resourceId){
const ip=prompt("IP")
if(!ip) return
const type=prompt("Type (public/vlan/ipv6)")
await fetch(API+"/"+resourceId+"/ips",{
method:"POST",
headers:{"Content-Type":"application/json"},
body:JSON.stringify({ip,type})
})
loadResources()
}
async function editIP(ipId){
const ip=prompt("New IP")
await fetch("/resman/api/ips/"+ipId,{
method:"PUT",
headers:{"Content-Type":"application/json"},
body:JSON.stringify({ip})
})
loadResources()
}
async function deleteIP(ipId){
await fetch("/resman/api/ips/"+ipId,{method:"DELETE"})
loadResources()
}
function renderIPs(r){
let html=""
if(Array.isArray(r.ips)){
r.ips.forEach(ip=>{
html+=`
<div class="ip">
${ip.ip} (${ip.type})
<button onclick="editIP(${ip.id})">Edit</button>
<button onclick="deleteIP(${ip.id})">Delete</button>
</div>
`
})
}
html+=`<button onclick="addIP(${r.id})">Add IP</button>`
return html
}
function renderRow(r){
return `
<tr>
<td>${r.name}</td>
<td>${r.provider||""}</td>
<td>${r.produkt||""}</td>
<td>${r.cpu||""}</td>
<td>${r.ram||""}</td>
<td>${r.disk||""}</td>
<td>${renderIPs(r)}</td>
<td>
<button onclick='editResource(${JSON.stringify(r)})'>Edit</button>
<button onclick="deleteResource(${r.id})">Delete</button>
</td>
</tr>
`
}
async function loadResources(){
const active=await fetch(API+"/active").then(r=>r.json())
const cancelled=await fetch(API+"/cancelled").then(r=>r.json())
const activeTable=document.getElementById("activeResources")
const cancelledTable=document.getElementById("cancelledResources")
activeTable.innerHTML=""
cancelledTable.innerHTML=""
active.forEach(r=>{
activeTable.innerHTML+=renderRow(r)
})
cancelled.forEach(r=>{
cancelledTable.innerHTML+=renderRow(r)
})
}
loadResources()
</script>
</body>
</html>