Database_Agent / templates /index_db_json.html
prthm11's picture
Upload 6 files
bb2f1af verified
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1" />
<title>MongoDB Agent Chat</title>
<link rel="icon" type="image/png" href="/static/robot.png" />
<script src="https://cdnjs.cloudflare.com/ajax/libs/socket.io/4.5.4/socket.io.js"></script>
<script src="https://cdn.jsdelivr.net/npm/marked/marked.min.js"></script>
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.5.0/css/all.min.css" />
<style>
* {
box-sizing: border-box;
}
body {
background: linear-gradient(135deg, rgb(44, 65, 112), #151f35);
color: #f0f0f0;
font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif;
margin: 0;
padding: 0;
overflow: hidden;
height: 100vh;
}
.container {
max-width: 910px;
margin: 22px auto;
padding: 20px;
border-radius: 20px;
backdrop-filter: blur(12px);
background: rgba(255, 255, 255, 0.04);
box-shadow: 0 8px 20px rgba(0, 0, 0, 0.2);
transition: all 0.3s ease-in-out;
width: 90%;
display: flex;
flex-direction: column;
height: fit-content
}
h1 {
text-align: center;
font-size: 1.5rem;
margin-bottom: 30px;
color: #7ee8fa;
letter-spacing: 1px;
text-shadow: 0 2px 4px #000;
}
/* added small DB area styles */
.db-area {
display: flex;
gap: 10px;
align-items: flex-start;
margin-bottom: 14px;
}
.db-area textarea {
flex: 1;
padding: 10px;
border-radius: 8px;
background: rgba(255, 255, 255, 0.04);
color: #eaeaea;
border: 1px solid rgba(255, 255, 255, 0.06);
resize: none;
min-height: 46px;
}
.db-area button {
padding: 10px 14px;
border-radius: 8px;
border: none;
background: #28a745;
color: white;
cursor: pointer;
}
.chat-container {
height: 450px;
overflow-y: auto;
padding: 20px;
background: rgba(255, 255, 255, 0.05);
border-radius: 15px;
border: 1px solid rgba(255, 255, 255, 0.05);
box-shadow: inset 0 0 10px rgba(0, 0, 0, 0.25);
backdrop-filter: blur(8px);
animation: fadeInUp 0.6s ease;
}
.chat-container {
height: 450px;
overflow-y: auto;
padding: 20px;
background: rgba(255, 255, 255, 0.05);
border-radius: 15px;
border: 1px solid rgba(255, 255, 255, 0.05);
box-shadow: inset 0 0 10px rgba(0, 0, 0, 0.25);
backdrop-filter: blur(8px);
animation: fadeInUp 0.6s ease;
}
@keyframes fadeInUp {
from {
opacity: 0;
transform: translateY(15px);
}
to {
opacity: 1;
transform: translateY(0);
}
}
/* Custom scroll bar styling */
.chat-container::-webkit-scrollbar {
width: 8px;
}
.chat-container::-webkit-scrollbar-track {
background: #0f1b33;
border-radius: 4px;
}
.chat-container::-webkit-scrollbar-thumb {
background: #444;
border-radius: 4px;
}
.chat-container::-webkit-scrollbar-thumb:hover {
background: #555;
}
.chat-container {
scrollbar-width: thin;
scrollbar-color: #444 #0f1b33;
}
.message {
margin-bottom: 15px;
display: flex;
align-items: flex-start;
opacity: 0;
animation: fadeIn 0.5s ease forwards;
}
.message.user {
justify-content: flex-end;
}
.message.agent {
justify-content: flex-start;
}
.bubble {
padding: 8px 14px;
font-size: 14px;
line-height: 1.2;
border-radius: 14px;
max-width: 70%;
word-wrap: break-word;
position: relative;
line-height: 1.5;
box-shadow: 0 4px 12px rgba(0, 0, 0, 0.3);
transition: all 0.3s ease;
}
.bubble.user {
background: linear-gradient(145deg, #007bff, #0056b3);
color: white;
border-bottom-right-radius: 0;
}
.bubble.agent {
background: linear-gradient(145deg, #2d3a55, #1b2a3a);
color: #f3f3f3;
border-bottom-left-radius: 0;
}
.bubble.agent.thought-bubble {
background: rgba(16, 52, 74, 0.8);
font-style: italic;
border-left: 4px solid #00e0ff;
color: #7ee8fa;
}
.thought-toggle {
color: #ff8800;
font-size: 0.85rem;
cursor: pointer;
display: inline-block;
margin-top: 5px;
}
.input-area {
display: flex;
gap: 10px;
margin-top: 15px;
align-items: center;
}
.input-area textarea {
flex: 1;
padding: 12px;
border-radius: 10px;
background: rgba(255, 255, 255, 0.08);
color: #eaeaea;
border: 1px solid rgba(255, 255, 255, 0.1);
resize: none;
transition: box-shadow 0.3s ease;
}
.input-area textarea:focus {
outline: none;
box-shadow: 0 0 8px #00e0ff;
}
.input-area button {
padding: 12px 20px;
border-radius: 10px;
border: none;
background: #00a8ff;
color: #fff;
cursor: pointer;
transition: background 0.3s;
}
#upload-db {
background: none;
border: none;
cursor: pointer;
/* margin-left: 5px; */
}
.btn-icon {
background: transparent;
border: none;
cursor: pointer;
padding: 6px;
transition: transform 0.2s ease;
}
#upload-db img {
width: 28px;
height: 28px;
}
.input-area button:disabled {
background-color: #555;
cursor: not-allowed;
}
.input-area button:hover:not(:disabled) {
background: #0077c2;
}
.toast {
position: fixed;
bottom: 25px;
right: 25px;
background: #2c3e50;
padding: 12px 22px;
border-radius: 12px;
font-size: 0.95rem;
box-shadow: 0 6px 12px rgba(0, 0, 0, 0.3);
opacity: 0.95;
display: none;
z-index: 1000;
transition: all 0.3s ease-in-out;
}
/* Typing animation dots */
.dot {
animation: blink 1.2s infinite;
display: inline-block;
margin-left: 2px;
}
.dot:nth-child(2) {
animation-delay: 0.2s;
}
.dot:nth-child(3) {
animation-delay: 0.4s;
}
@keyframes blink {
0% {
opacity: 0.2;
}
20% {
opacity: 1;
}
100% {
opacity: 0.2;
}
}
@keyframes fadeIn {
to {
opacity: 1;
}
}
.thought-toggle {
margin-top: 4px;
font-size: 0.8rem;
color: #ffbb00;
cursor: pointer;
transition: color 0.3s;
}
.thought-toggle:hover {
color: #fff000;
}
.copy-container {
margin-top: 5px;
display: flex;
justify-content: flex-end;
}
.copy-btn {
background: none;
border: none;
color: #bbb;
cursor: pointer;
font-size: 0.85rem;
padding: 4px 8px;
transition: color 0.2s ease, background 0.2s ease;
border-radius: 6px;
}
.copy-btn:hover {
color: #fff;
background: rgba(255, 255, 255, 0.1);
}
</style>
</head>
<body>
<div class="container">
<h1><i class="fa-solid fa-robot" style="margin-right: 10px; color: #f5f5f5;"></i>: Ask Me Anything About Your Data
</h1>
<!-- NEW: DB connection textarea and connect button -->
<div class="db-area">
<textarea id="db-uri" rows="2"
placeholder="Enter DB connection string (SQLAlchemy URI, mssql ODBC string, or MongoDB URI)."></textarea>
<button id="connect-db" title="Connect Database"><i class="fa-solid fa-plug"
style="margin-right:8px"></i>Connect</button>
</div>
<div class="chat-container" id="chat"></div>
<div class="input-area">
<textarea id="prompt" rows="2" placeholder="Ask something about your data..."></textarea>
<!-- <button id="upload-db" title="Upload Mongo JSON Database"><img src="/static/db_icon.svg" alt="DB" /></button> -->
<button id="upload-db" title="Upload Database"><i class="fa-solid fa-database"
style="color: #b5d3f1; font-size: 25px; background: transparent;"></i></button>
<button id="send">Send</button>
<input type="file" id="file-input" accept=".json,.db" style="display:none;" />
</div>
</div>
<div id="toast" class="toast"></div>
<script>
const socket = io()
const chat = document.getElementById('chat')
const toast = document.getElementById('toast')
const sendBtn = document.getElementById('send')
const promptTA = document.getElementById('prompt')
const uploadBtn = document.getElementById('upload-db')
const fileInput = document.getElementById('file-input')
const dbUriTA = document.getElementById('db-uri')
const connectBtn = document.getElementById('connect-db')
let typingBubble = null
let agentBubble = null
function addMessage(sender, html) {
const msg = document.createElement('div')
msg.className = `message ${sender}`
const bubble = document.createElement('div')
bubble.className = `bubble ${sender}`
bubble.innerHTML = marked.parse(html)
msg.appendChild(bubble)
// Add copy button only for user messages
if (sender === 'user') {
const copyBtn = document.createElement('button');
copyBtn.className = 'btn-icon copy-btn';
copyBtn.innerHTML = '<i class="fa-regular fa-copy"></i>';
copyBtn.title = 'Copy to clipboard';
copyBtn.onclick = () => {
navigator.clipboard.writeText(html);
showToast('Copied to clipboard!');
};
const copyContainer = document.createElement('div');
copyContainer.className = 'copy-container';
copyContainer.appendChild(copyBtn);
msg.appendChild(copyContainer);
}
chat.appendChild(msg)
chat.scrollTop = chat.scrollHeight
}
function showToast(text, duration = 3000) {
toast.textContent = text
toast.style.display = 'block'
setTimeout(() => (toast.style.display = 'none'), duration)
}
uploadBtn.addEventListener('click', () => fileInput.click())
fileInput.addEventListener('change', e => handleFile(e.target.files[0]))
// function handleFile(file) {
// if (!file) return
// showToast('Uploading Database…')
// const fd = new FormData()
// fd.append('file', file)
// fetch('/upload_db', { method: 'POST', body: fd })
// .then(r => r.json())
// .then(data => {
// if (data.success) {
// showToast('Database initialized!', 2000)
// const fileName = file.name;
// addMessage('agent', ${filename},'uploaded and ready!')
// } else {
// addMessage('agent', `Error: ${data.message}`)
// }
// })
// .catch(err => addMessage('agent', `Upload error: ${err}`))
// }
function handleFile(file) {
if (!file) return;
showToast('Uploading Database…');
const fd = new FormData();
fd.append('file', file);
fetch('/upload_db', { method: 'POST', body: fd })
.then(r => r.json())
.then(data => {
if (data.success) {
showToast('Database initialized!', 2000);
const fileName = file.name; // Get the file name from the uploaded file
addMessage('agent', `${fileName} is uploaded and ready!`);
} else {
addMessage('agent', `Error: ${data.message}`);
}
})
.catch(err => addMessage('agent', `Upload error: ${err}`));
}
function sendPrompt() {
const text = promptTA.value.trim()
if (!text || sendBtn.disabled) return
addMessage('user', text)
promptTA.value = ''
sendBtn.disabled = true
typingBubble = document.createElement('div')
typingBubble.className = 'message agent'
typingBubble.innerHTML = `<div class="bubble agent">Thinking<span class="dot">.</span><span class="dot">.</span><span class="dot">.</span></div>`
chat.appendChild(typingBubble)
chat.scrollTop = chat.scrollHeight
fetch('/generate', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ prompt: text })
}).catch((err) => {
addMessage('agent', `Error: ${err}`)
sendBtn.disabled = false
if (typingBubble) chat.removeChild(typingBubble)
})
}
sendBtn.addEventListener('click', sendPrompt)
promptTA.addEventListener('keydown', (e) => {
if (e.key === 'Enter' && !e.shiftKey) {
e.preventDefault()
sendPrompt()
}
})
socket.on('final', (data) => {
if (typingBubble) {
chat.removeChild(typingBubble)
typingBubble = null
}
agentBubble = null
sendBtn.disabled = true
const msg = document.createElement('div')
msg.className = 'message agent'
const bubble = document.createElement('div')
bubble.className = 'bubble agent'
msg.appendChild(bubble)
chat.appendChild(msg)
chat.scrollTop = chat.scrollHeight
const text = data.message
let index = 0
const typingInterval = setInterval(() => {
if (index < text.length) {
bubble.textContent += text.charAt(index)
index++
chat.scrollTop = chat.scrollHeight
} else {
clearInterval(typingInterval)
sendBtn.disabled = false
}
}, 50)
})
// NEW: Connect DB button handler
connectBtn.addEventListener('click', () => {
const uri = dbUriTA.value.trim();
if (!uri) {
showToast('Please enter a connection string.');
return;
}
connectBtn.disabled = true;
showToast('Sending connection string...')
fetch('/connect_db', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ uri })
})
.then(r => r.json())
.then(data => {
connectBtn.disabled = false;
if (data && data.message) {
addMessage('agent', data.message);
showToast('Connect request sent.');
} else {
addMessage('agent', 'No response from server.');
}
})
.catch(err => {
connectBtn.disabled = false;
addMessage('agent', `Connect error: ${err}`);
})
})
</script>
</body>
</html>