39269-vm/core/templates/core/dashboard.html
Flatlogic Bot 5fa2cf7ba7 RIPLEY
2026-03-23 00:16:45 +00:00

163 lines
5.3 KiB
HTML

{% extends "base.html" %}
{% load static %}
{% block content %}
<style>
.node-group { cursor: pointer; }
.node-circle { stroke: #fff; stroke-width: 2px; }
.node-text { font-size: 10px; pointer-events: none; }
#loader { display: none; }
</style>
<div class="container mt-5">
<h1 class="mb-4">System Dashboard</h1>
<div class="row">
<div class="col-md-12 mb-4">
<div class="card shadow-sm">
<div class="card-body">
<h5 class="card-title">Network Discovery</h5>
<form id="searchForm" class="input-group">
<input type="text" id="searchInput" class="form-control" placeholder="Search for a name to map their network...">
<button class="btn btn-primary" id="searchBtn" type="submit">
<span id="btnText">Discover</span>
<div id="loader" class="spinner-border spinner-border-sm" role="status">
<span class="visually-hidden">Loading...</span>
</div>
</button>
</form>
</div>
</div>
</div>
</div>
<div class="row">
<div class="col-md-12">
<div class="card shadow-sm">
<div class="card-body">
<h5 class="card-title">Network Visualization</h5>
<div id="graphContainer" style="width: 100%; height: 600px; background: #f8f9fa; border: 1px solid #ddd;"></div>
</div>
</div>
</div>
</div>
</div>
<script src="https://d3js.org/d3.v7.min.js"></script>
<script>
document.getElementById('searchForm').addEventListener('submit', function(e) {
e.preventDefault();
const query = document.getElementById('searchInput').value;
const graphContainer = d3.select("#graphContainer");
const searchBtn = document.getElementById('searchBtn');
const btnText = document.getElementById('btnText');
const loader = document.getElementById('loader');
// UI Loading state
searchBtn.disabled = true;
btnText.textContent = "Searching...";
loader.style.display = "inline-block";
graphContainer.html('<p class="p-3 text-muted">Discovering network, please wait...</p>');
fetch(`{% url 'core:search_api' %}?q=${encodeURIComponent(query)}`, { method: 'GET' })
.then(response => {
if (!response.ok) throw new Error("Search failed");
return response.json();
})
.then(data => {
graphContainer.html(''); // clear
renderGraph(data);
})
.catch(err => {
graphContainer.html(`<p class="p-3 text-danger">Error: ${err.message}</p>`);
})
.finally(() => {
searchBtn.disabled = false;
btnText.textContent = "Discover";
loader.style.display = "none";
});
});
function renderGraph(data) {
const width = 800;
const height = 600;
const svg = d3.select("#graphContainer")
.append("svg")
.attr("viewBox", [0, 0, width, height]);
const simulation = d3.forceSimulation(data.nodes)
.force("link", d3.forceLink(data.links).id(d => d.id).distance(100))
.force("charge", d3.forceManyBody().strength(-300))
.force("center", d3.forceCenter(width / 2, height / 2));
const link = svg.append("g")
.selectAll("line")
.data(data.links)
.join("line")
.attr("stroke", "#999")
.attr("stroke-width", 1);
const node = svg.append("g")
.selectAll("g")
.data(data.nodes)
.join("g")
.attr("class", "node-group")
.call(d3.drag()
.on("start", dragstarted)
.on("drag", dragged)
.on("end", dragended));
node.append("circle")
.attr("r", 20)
.attr("class", "node-circle")
.attr("fill", d => d.type === 'PERSON' ? '#e74c3c' : '#3498db');
// Add Image if available
node.filter(d => d.photo)
.append("image")
.attr("xlink:href", d => d.photo)
.attr("x", -15)
.attr("y", -15)
.attr("width", 30)
.attr("height", 30)
.attr("clip-path", "circle(15px)");
node.append("text")
.attr("dy", 35)
.attr("text-anchor", "middle")
.attr("class", "node-text")
.text(d => d.name);
node.append("text")
.attr("dy", 48)
.attr("text-anchor", "middle")
.attr("class", "node-text")
.attr("fill", "#666")
.text(d => d.code || '');
simulation.on("tick", () => {
link.attr("x1", d => d.source.x).attr("y1", d => d.source.y)
.attr("x2", d => d.target.x).attr("y2", d => d.target.y);
node.attr("transform", d => `translate(${d.x},${d.y})`);
});
function dragstarted(event) {
if (!event.active) simulation.alphaTarget(0.3).restart();
event.subject.fx = event.subject.x;
event.subject.fy = event.subject.y;
}
function dragged(event) {
event.subject.fx = event.x;
event.subject.fy = event.y;
}
function dragended(event) {
if (!event.active) simulation.alphaTarget(0);
event.subject.fx = null;
event.subject.fy = null;
}
}
</script>
{% endblock %}