admin / Synapse-Cortex
publicSelf Hosted ITSM Tool with RBAC/Tenanting and MFA
Synapse-Cortex / synapse-cortex / app / templates / assets.html
7357 B · main
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 | {% extends "base.html" %} {% block title %}Assets // Synapse-Cortex{% endblock %} {% block header %}CMDB // Asset Registry{% endblock %} {% block content %} <div class="flex items-center justify-between mb-4"> <div class="text-xs text-cortex-muted uppercase tracking-wider">Registered Assets ({{ assets|length }})</div> <button onclick="document.getElementById('new-asset-modal').classList.remove('hidden')" class="bg-accent-blue text-cortex-bg font-display font-bold uppercase tracking-wider text-xs px-4 py-2 rounded hover:bg-accent-blue-bright transition"> + Register Asset </button> </div> <div class="border border-cortex-border bg-cortex-panel rounded-md"> <table class="w-full text-sm"> <thead> <tr class="text-left text-[10px] text-cortex-muted uppercase tracking-wider border-b border-cortex-border"> <th class="px-4 py-2">Asset ID</th> <th class="px-4 py-2">Name</th> <th class="px-4 py-2">Type</th> <th class="px-4 py-2">MAC Address</th> <th class="px-4 py-2">IP</th> <th class="px-4 py-2">Status</th> <th class="px-4 py-2">Location</th> <th class="px-4 py-2">Source</th> </tr> </thead> <tbody> {% for a in assets %} <tr class="border-b border-cortex-border/60 hover:bg-cortex-panel2"> <td class="px-4 py-2"><a href="/assets/{{ a.id }}" class="text-accent-blue hover:text-accent-blue-bright hover:underline">{{ a.asset_id }}</a></td> <td class="px-4 py-2">{{ a.name }}</td> <td class="px-4 py-2 text-xs uppercase text-chrome-dim">{{ a.asset_type_name or '—' }}</td> <td class="px-4 py-2 text-xs text-chrome-dim">{{ a.mac_address or '—' }}</td> <td class="px-4 py-2 text-xs text-chrome-dim">{{ a.ip_address or '—' }}</td> <td class="px-4 py-2"> <span class="text-[10px] px-2 py-0.5 rounded uppercase {% if a.status.value == 'in_service' %}bg-accent-green/15 text-accent-green border border-accent-green/30 {% elif a.status.value == 'in_repair' %}bg-accent-orange/15 text-accent-orange border border-accent-orange/30 {% elif a.status.value == 'retired' %}bg-accent-red/15 text-accent-red border border-accent-red/30 {% else %}bg-cortex-panel2 text-cortex-muted border border-cortex-border{% endif %}"> {{ a.status.value.replace('_',' ') }} </span> </td> <td class="px-4 py-2 text-xs text-chrome-dim">{{ a.location or '—' }}</td> <td class="px-4 py-2 text-[10px] uppercase {% if a.source.value == 'netscanxi' %}text-accent-blue{% else %}text-cortex-muted{% endif %}">{{ a.source.value }}</td> </tr> {% else %} <tr><td colspan="8" class="px-4 py-6 text-center text-cortex-muted text-xs">No assets registered yet.</td></tr> {% endfor %} </tbody> </table> </div> <div id="new-asset-modal" class="hidden fixed inset-0 bg-black/70 flex items-center justify-center z-50 px-4"> <div class="w-full max-w-lg border border-cortex-border bg-cortex-panel rounded-md p-6"> <div class="flex items-center justify-between mb-4"> <div class="text-xs uppercase tracking-wider text-accent-blue">Register Asset</div> <button onclick="document.getElementById('new-asset-modal').classList.add('hidden')" class="text-cortex-muted hover:text-accent-red">×</button> </div> <div id="asset-form-error" class="hidden mb-3 text-xs border border-accent-red/40 bg-accent-red/10 text-accent-red px-3 py-2 rounded"></div> <form id="asset-form" class="space-y-3 text-sm"> <div> <label class="block text-xs text-cortex-muted mb-1 uppercase tracking-wider">Name</label> <input required name="name" type="text" placeholder="edge-router-04" class="w-full bg-cortex-panel2 border border-cortex-border rounded px-3 py-2 focus:outline-none focus:border-accent-blue" /> </div> <div class="grid grid-cols-2 gap-3"> <div> <label class="block text-xs text-cortex-muted mb-1 uppercase tracking-wider">Type</label> <select name="asset_type_id" class="w-full bg-cortex-panel2 border border-cortex-border rounded px-3 py-2"> {% for t in asset_types %} <option value="{{ t.id }}" {% if t.name == 'Other' %}selected{% endif %}>{{ t.name }}</option> {% endfor %} </select> </div> <div> <label class="block text-xs text-cortex-muted mb-1 uppercase tracking-wider">Status</label> <select name="status" class="w-full bg-cortex-panel2 border border-cortex-border rounded px-3 py-2"> <option value="in_stock" selected>In Stock</option> <option value="in_service">In Service</option> <option value="in_repair">In Repair</option> <option value="retired">Retired</option> </select> </div> </div> <div> <label class="block text-xs text-cortex-muted mb-1 uppercase tracking-wider">MAC Address <span class="text-accent-orange">(required, unique per tenant)</span></label> <input required name="mac_address" type="text" placeholder="00:1A:2B:3C:4D:5E" pattern="^([0-9A-Fa-f]{2}[:-]){5}[0-9A-Fa-f]{2}$" class="w-full bg-cortex-panel2 border border-cortex-border rounded px-3 py-2 focus:outline-none focus:border-accent-blue" /> <div class="text-[10px] text-cortex-muted mt-1">The asset_id is deterministically derived from this MAC address.</div> </div> <div class="grid grid-cols-2 gap-3"> <div> <label class="block text-xs text-cortex-muted mb-1 uppercase tracking-wider">IP Address</label> <input name="ip_address" type="text" placeholder="10.0.4.12" class="w-full bg-cortex-panel2 border border-cortex-border rounded px-3 py-2 focus:outline-none focus:border-accent-blue" /> </div> <div> <label class="block text-xs text-cortex-muted mb-1 uppercase tracking-wider">Location</label> <input name="location" type="text" placeholder="DC-East / Rack 12" class="w-full bg-cortex-panel2 border border-cortex-border rounded px-3 py-2 focus:outline-none focus:border-accent-blue" /> </div> </div> <button type="submit" class="w-full bg-accent-blue text-cortex-bg font-bold uppercase tracking-wider text-xs py-2.5 rounded hover:bg-accent-blue-bright transition"> Register Asset </button> </form> </div> </div> <script> document.getElementById('asset-form').addEventListener('submit', async function (e) { e.preventDefault(); const form = e.target; const errorBox = document.getElementById('asset-form-error'); errorBox.classList.add('hidden'); const data = Object.fromEntries(new FormData(form).entries()); if (!data.ip_address) delete data.ip_address; if (!data.location) delete data.location; const res = await fetch('/api/v1/assets', { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify(data), }); if (res.ok) { window.location.reload(); } else { const err = await res.json().catch(() => ({})); errorBox.textContent = err.detail ? JSON.stringify(err.detail) : 'Failed to register asset.'; errorBox.classList.remove('hidden'); } }); </script> {% endblock %} |