Auto commit: 2026-03-13T09:39:35.644Z

This commit is contained in:
Flatlogic Bot 2026-03-13 09:39:35 +00:00
parent 134adfbc75
commit 4bea8583e5
2 changed files with 87 additions and 59 deletions

Binary file not shown.

After

Width:  |  Height:  |  Size: 503 KiB

View File

@ -1,7 +1,7 @@
{% extends 'base.html' %}
{% block content %}
<div class="container-fluid mt-4">
<div class="row mb-3">
<div class="container-fluid d-flex flex-column pt-4" style="height: 100vh; max-height: 100vh; overflow: hidden;">
<div class="row mb-3 flex-shrink-0">
<div class="col-md-8">
<h1 class="mb-0">{{ project.title }}</h1>
<p class="text-muted mb-0">{{ project.industry }} | <strong>Goal:</strong> {{ project.goal }}</p>
@ -11,12 +11,11 @@
</div>
</div>
<div class="row">
<!-- Mind Map Visualization Column -->
<div class="col-md-8">
<div class="card shadow-sm h-100">
<div class="card-header bg-white d-flex justify-content-between align-items-center">
<h5 class="mb-0">Interactive Mind Map</h5>
<div class="row flex-grow-1 pb-3" style="min-height: 0;">
<div class="col-12 h-100">
<div class="card shadow-sm h-100 w-100 d-flex flex-column" style="border-radius: 12px; overflow: hidden; border: 1px solid rgba(0,0,0,0.08);">
<div class="card-header bg-white d-flex justify-content-between align-items-center flex-shrink-0" style="border-bottom: 1px solid rgba(0,0,0,0.05);">
<h5 class="mb-0 fw-bold">Interactive Mind Map</h5>
<div>
<form method="POST" action="{% url 'regenerate_mindmap' project.pk %}" class="d-inline" onsubmit="return confirm('Are you sure you want to regenerate the entire map? This will delete all current nodes.');">
{% csrf_token %}
@ -25,50 +24,53 @@
<button class="btn btn-sm btn-outline-primary" onclick="fitNetwork()">Reset View</button>
</div>
</div>
<div class="card-body p-0" style="height: 600px; position: relative;">
<div class="card-body p-0 position-relative flex-grow-1" style="min-height: 0; overflow: hidden; background: linear-gradient(135deg, #fdfbfb 0%, #ebedee 100%);">
{% if not nodes %}
<div class="position-absolute w-100 h-100 d-flex flex-column justify-content-center align-items-center" style="z-index: 10; background: rgba(255,255,255,0.9);">
<h4 class="text-muted mb-3">The mind map is empty</h4>
<p class="text-muted mb-4">Something went wrong during generation, or the map was cleared.</p>
<form method="POST" action="{% url 'regenerate_mindmap' project.pk %}">
{% csrf_token %}
<button type="submit" class="btn btn-primary px-4 py-2">
<button type="submit" class="btn btn-primary px-4 py-2 shadow-sm rounded-pill">
Generate Map Now
</button>
</form>
</div>
{% endif %}
<!-- The vis-network container -->
<div id="mynetwork" style="width: 100%; height: 100%;"></div>
</div>
</div>
</div>
</div>
<!-- Sidebar for Node Details & AI Chat -->
<div class="col-md-4 d-flex flex-column">
<!-- Node Details Panel -->
<div class="card shadow-sm mb-3" style="flex: 1;">
<div class="card-header bg-white">
<h5 class="mb-0">Node Details</h5>
</div>
<div class="card-body" id="node-details">
<p class="text-muted">Click on a node or edge in the map to see its details here.</p>
</div>
</div>
<!-- Floating Panels in Top Right -->
<div class="position-absolute d-flex flex-column" style="top: 15px; right: 15px; width: 350px; z-index: 1000; bottom: 15px; pointer-events: none;">
<!-- Node Details Panel -->
<div class="card shadow mb-3" style="pointer-events: auto; flex-shrink: 0; border: 1px solid rgba(255,255,255,0.4); border-radius: 12px; background: rgba(255, 255, 255, 0.85); backdrop-filter: blur(12px); -webkit-backdrop-filter: blur(12px);">
<div class="card-header py-2" style="background: transparent; border-bottom: 1px solid rgba(0,0,0,0.05); border-radius: 12px 12px 0 0;">
<h6 class="mb-0 fw-bold text-primary">Node Details</h6>
</div>
<div class="card-body py-2" id="node-details" style="max-height: 200px; overflow-y: auto;">
<p class="text-muted small mb-0">Click on a node or edge in the map to see its details here.</p>
</div>
</div>
<!-- AI Chat Panel -->
<div class="card shadow d-flex flex-column" style="pointer-events: auto; flex: 1; min-height: 0; border: 1px solid rgba(255,255,255,0.4); border-radius: 12px; background: rgba(255, 255, 255, 0.85); backdrop-filter: blur(12px); -webkit-backdrop-filter: blur(12px);">
<div class="card-header py-2" style="background: transparent; border-bottom: 1px solid rgba(0,0,0,0.05); border-radius: 12px 12px 0 0;">
<h6 class="mb-0 fw-bold text-success">Ask AI Assistant</h6>
</div>
<div class="card-body d-flex flex-column p-2" style="min-height: 0;">
<div id="ai-response" class="p-2 rounded mb-2 flex-grow-1" style="overflow-y: auto; font-size: 0.9rem; background: rgba(255,255,255,0.6); box-shadow: inset 0 2px 4px rgba(0,0,0,0.02);">
<span class="text-muted">Hello! I'm your AI business strategist. Ask me to expand on a node, brainstorm new ideas, or analyze the map. If you ask me to add nodes to the map, I will!</span>
</div>
<div class="mt-auto flex-shrink-0">
<textarea id="ai-input" class="form-control form-control-sm mb-2" rows="2" style="resize: none; border-radius: 8px; background: rgba(255,255,255,0.9);" placeholder="e.g. Add 3 more nodes related to Marketing..."></textarea>
<button id="ai-submit" class="btn btn-primary btn-sm w-100 rounded-pill shadow-sm">Send to AI</button>
</div>
</div>
</div>
<!-- AI Chat Panel -->
<div class="card shadow-sm" style="flex: 1;">
<div class="card-header bg-white">
<h5 class="mb-0">Ask AI Assistant</h5>
</div>
<div class="card-body d-flex flex-column">
<div id="ai-response" class="bg-light p-3 rounded mb-3 flex-grow-1" style="min-height: 100px; max-height: 200px; overflow-y: auto;">
<span class="text-muted">Hello! I'm your AI business strategist. Ask me to expand on a node, brainstorm new ideas, or analyze the map. If you ask me to add nodes to the map, I will!</span>
</div>
<div class="mt-auto">
<textarea id="ai-input" class="form-control mb-2" rows="2" placeholder="e.g. Add 3 more nodes related to Marketing..."></textarea>
<button id="ai-submit" class="btn btn-primary w-100">Send to AI</button>
</div>
</div>
</div>
@ -83,16 +85,33 @@
<style type="text/css">
.vis-tooltip {
position: absolute;
background-color: white;
padding: 10px;
border: 1px solid #ccc;
border-radius: 5px;
box-shadow: 0 4px 6px rgba(0,0,0,0.1);
font-family: Arial, sans-serif;
background-color: rgba(255, 255, 255, 0.95);
padding: 12px;
border: none;
border-radius: 8px;
box-shadow: 0 10px 25px rgba(0,0,0,0.1);
font-family: inherit;
font-size: 14px;
max-width: 300px;
max-width: 320px;
pointer-events: none;
z-index: 10;
backdrop-filter: blur(10px);
-webkit-backdrop-filter: blur(10px);
}
/* Optional: custom scrollbar for details and chat */
::-webkit-scrollbar {
width: 6px;
}
::-webkit-scrollbar-track {
background: transparent;
}
::-webkit-scrollbar-thumb {
background: rgba(0,0,0,0.15);
border-radius: 3px;
}
::-webkit-scrollbar-thumb:hover {
background: rgba(0,0,0,0.25);
}
</style>
@ -203,9 +222,9 @@
const nodeId = params.nodes[0];
const node = data.nodes.get(nodeId);
detailsPanel.innerHTML = `
<h4 class="mb-2">${node.label}</h4>
<span class="badge bg-secondary mb-3">${node.category}</span>
<p><strong>Summary:</strong><br>${node.summary}</p>
<h6 class="mb-2 fw-bold text-dark">${node.label}</h6>
<span class="badge bg-secondary mb-2 bg-opacity-75">${node.category}</span>
<p class="small mb-0 text-dark"><strong>Summary:</strong><br>${node.summary}</p>
`;
} else if (params.edges.length > 0) {
// Edge clicked
@ -214,16 +233,17 @@
const fromNode = data.nodes.get(edge.from);
const toNode = data.nodes.get(edge.to);
detailsPanel.innerHTML = `
<h5 class="mb-3">Connection Details</h5>
<p><strong>From:</strong> ${fromNode.label}</p>
<p><strong>To:</strong> ${toNode.label}</p>
<hr>
<p><strong>How:</strong><br>${edge.how || 'N/A'}</p>
<p><strong>Why:</strong><br>${edge.why || 'N/A'}</p>
<h6 class="mb-2 fw-bold text-dark">Connection Details</h6>
<p class="small mb-1 text-dark"><strong>From:</strong> ${fromNode.label}</p>
<p class="small mb-2 text-dark"><strong>To:</strong> ${toNode.label}</p>
<div class="small text-dark border-top pt-2">
<p class="mb-1"><strong>How:</strong> ${edge.how || 'N/A'}</p>
<p class="mb-0"><strong>Why:</strong> ${edge.why || 'N/A'}</p>
</div>
`;
} else {
// Clicked empty space
detailsPanel.innerHTML = '<p class="text-muted">Click on a node or edge in the map to see its details here.</p>';
detailsPanel.innerHTML = '<p class="text-muted small mb-0">Click on a node or edge in the map to see its details here.</p>';
}
});
@ -236,12 +256,12 @@
const responseDiv = document.getElementById('ai-response');
// Append user message
responseDiv.innerHTML += `<div class="mb-2 text-end"><strong>You:</strong> ${message}</div>`;
responseDiv.innerHTML += `<div class="mb-2 text-end"><span class="badge bg-primary rounded-pill px-3 py-2 text-wrap text-start d-inline-block shadow-sm" style="max-width: 85%; font-weight: normal; font-size: 0.9rem;">${message}</span></div>`;
inputField.value = '';
// Show loading state
const loadingId = 'loading-' + Date.now();
responseDiv.innerHTML += `<div id="${loadingId}" class="mb-2 text-primary"><em>AI is thinking...</em></div>`;
responseDiv.innerHTML += `<div id="${loadingId}" class="mb-2 text-start"><span class="badge bg-white text-primary border border-primary border-opacity-25 rounded-pill px-3 py-2 shadow-sm d-inline-block" style="font-weight: normal; font-size: 0.9rem;"><em><span class="spinner-border spinner-border-sm me-1" role="status" aria-hidden="true"></span> Thinking...</em></span></div>`;
responseDiv.scrollTop = responseDiv.scrollHeight;
fetch("{% url 'ai_chat' project.pk %}", {
@ -258,7 +278,7 @@
// Handle chat response
if (apiData.response) {
responseDiv.innerHTML += `<div class="mb-2"><strong>AI:</strong> ${apiData.response}</div>`;
responseDiv.innerHTML += `<div class="mb-2 text-start"><span class="badge bg-white text-dark rounded-4 px-3 py-2 text-wrap text-start d-inline-block shadow-sm" style="max-width: 90%; font-weight: normal; font-size: 0.9rem; border: 1px solid rgba(0,0,0,0.05);">${apiData.response}</span></div>`;
}
// Check for new nodes and add them to the map
@ -301,16 +321,24 @@
// Stabilize and adjust view
network.stabilize();
setTimeout(fitNetwork, 500); // Fit network smoothly after a slight delay
responseDiv.innerHTML += `<div class="mb-2 text-success small"><em><i class="bi bi-check-circle"></i> Successfully added new items to the map!</em></div>`;
responseDiv.innerHTML += `<div class="mb-2 text-center"><small class="text-success"><i class="bi bi-check-circle-fill"></i> New items added to map!</small></div>`;
}
responseDiv.scrollTop = responseDiv.scrollHeight;
})
.catch(err => {
document.getElementById(loadingId).remove();
responseDiv.innerHTML += `<div class="mb-2 text-danger"><strong>Error:</strong> Failed to get response from AI.</div>`;
responseDiv.innerHTML += `<div class="mb-2 text-start"><span class="badge bg-danger text-white rounded-pill px-3 py-2 shadow-sm d-inline-block" style="font-weight: normal;">Error: Failed to get response.</span></div>`;
console.error(err);
});
});
// Allow submitting AI input with Enter key
document.getElementById('ai-input').addEventListener('keypress', function (e) {
if (e.key === 'Enter' && !e.shiftKey) {
e.preventDefault();
document.getElementById('ai-submit').click();
}
});
</script>
{% endblock %}