148 lines
5.6 KiB
HTML
Executable File
148 lines
5.6 KiB
HTML
Executable File
<!DOCTYPE html>
|
|
<html lang="en">
|
|
<head>
|
|
<meta charset="UTF-8">
|
|
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
|
<title>Documents - MOXIE Admin</title>
|
|
<link rel="stylesheet" href="/{{ settings.admin_path }}/static/admin.css">
|
|
</head>
|
|
<body>
|
|
<nav class="navbar">
|
|
<div class="nav-brand">MOXIE Admin</div>
|
|
<div class="nav-links">
|
|
<a href="/{{ settings.admin_path }}/">Dashboard</a>
|
|
<a href="/{{ settings.admin_path }}/endpoints">Endpoints</a>
|
|
<a href="/{{ settings.admin_path }}/documents">Documents</a>
|
|
<a href="/{{ settings.admin_path }}/comfyui">ComfyUI</a>
|
|
</div>
|
|
</nav>
|
|
|
|
<main class="container">
|
|
<h1>Document Management</h1>
|
|
|
|
<section class="upload-section">
|
|
<h2>Upload Document</h2>
|
|
<form id="upload-form" class="form-inline">
|
|
<div class="form-group">
|
|
<input type="file" id="document-file" name="file" accept=".txt,.md,.pdf,.docx,.html" required>
|
|
</div>
|
|
<div class="form-group">
|
|
<label for="chunk_size">Chunk Size</label>
|
|
<input type="number" id="chunk_size" name="chunk_size" value="500" min="100" max="2000">
|
|
</div>
|
|
<div class="form-group">
|
|
<label for="overlap">Overlap</label>
|
|
<input type="number" id="overlap" name="overlap" value="50" min="0" max="500">
|
|
</div>
|
|
<button type="submit" class="btn btn-primary">Upload</button>
|
|
</form>
|
|
</section>
|
|
|
|
<section class="documents-section">
|
|
<h2>Uploaded Documents</h2>
|
|
<table class="documents-table">
|
|
<thead>
|
|
<tr>
|
|
<th>Filename</th>
|
|
<th>Type</th>
|
|
<th>Chunks</th>
|
|
<th>Uploaded</th>
|
|
<th>Actions</th>
|
|
</tr>
|
|
</thead>
|
|
<tbody id="documents-list">
|
|
{% for doc in documents %}
|
|
<tr data-id="{{ doc.id }}">
|
|
<td>{{ doc.filename }}</td>
|
|
<td>{{ doc.file_type }}</td>
|
|
<td>{{ doc.chunk_count }}</td>
|
|
<td>{{ doc.created_at }}</td>
|
|
<td>
|
|
<button class="btn btn-danger btn-sm delete-btn" data-id="{{ doc.id }}">Delete</button>
|
|
</td>
|
|
</tr>
|
|
{% else %}
|
|
<tr>
|
|
<td colspan="5" class="empty-message">No documents uploaded yet</td>
|
|
</tr>
|
|
{% endfor %}
|
|
</tbody>
|
|
</table>
|
|
</section>
|
|
|
|
<div id="toast" class="toast hidden"></div>
|
|
</main>
|
|
|
|
<script>
|
|
// Upload form
|
|
document.getElementById('upload-form').addEventListener('submit', async (e) => {
|
|
e.preventDefault();
|
|
|
|
const fileInput = document.getElementById('document-file');
|
|
const file = fileInput.files[0];
|
|
|
|
if (!file) {
|
|
showToast('Please select a file', 'error');
|
|
return;
|
|
}
|
|
|
|
const formData = new FormData();
|
|
formData.append('file', file);
|
|
formData.append('chunk_size', document.getElementById('chunk_size').value);
|
|
formData.append('overlap', document.getElementById('overlap').value);
|
|
|
|
try {
|
|
const response = await fetch('/{{ settings.admin_path }}/documents/upload', {
|
|
method: 'POST',
|
|
body: formData
|
|
});
|
|
|
|
const result = await response.json();
|
|
|
|
if (result.status === 'success') {
|
|
showToast(`Uploaded: ${result.filename}`, 'success');
|
|
setTimeout(() => location.reload(), 1500);
|
|
} else {
|
|
showToast('Upload failed: ' + (result.detail || 'Unknown error'), 'error');
|
|
}
|
|
} catch (error) {
|
|
showToast('Upload failed', 'error');
|
|
}
|
|
});
|
|
|
|
// Delete buttons
|
|
document.querySelectorAll('.delete-btn').forEach(btn => {
|
|
btn.addEventListener('click', async () => {
|
|
if (!confirm('Delete this document?')) return;
|
|
|
|
const docId = btn.dataset.id;
|
|
|
|
try {
|
|
const response = await fetch(`/{{ settings.admin_path }}/documents/${docId}`, {
|
|
method: 'DELETE'
|
|
});
|
|
|
|
const result = await response.json();
|
|
|
|
if (result.status === 'success') {
|
|
showToast('Document deleted', 'success');
|
|
btn.closest('tr').remove();
|
|
} else {
|
|
showToast('Delete failed', 'error');
|
|
}
|
|
} catch (error) {
|
|
showToast('Delete failed', 'error');
|
|
}
|
|
});
|
|
});
|
|
|
|
function showToast(message, type) {
|
|
const toast = document.getElementById('toast');
|
|
toast.textContent = message;
|
|
toast.className = 'toast ' + type;
|
|
setTimeout(() => toast.classList.add('hidden'), 3000);
|
|
}
|
|
</script>
|
|
</body>
|
|
</html>
|