adminbot-brainhub / index.html
gutzu1015's picture
remove the nav bar retain the header
8320f93 verified
Raw
History Blame Contribute Delete
22.7 kB
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Admin Pulse Dashboard</title>
<link rel="stylesheet" href="style.css">
<script src="https://cdn.tailwindcss.com"></script>
<script src="https://cdn.jsdelivr.net/npm/feather-icons/dist/feather.min.js"></script>
<script src="https://unpkg.com/feather-icons"></script>
<script src="components/header.js"></script>
</head>
<body class="bg-gray-100">
<custom-header></custom-header>
<main class="max-w-7xl mx-auto px-4 py-8 sm:px-6 lg:px-8">
<div class="mb-6 flex flex-wrap gap-4 items-center justify-between">
<div class="flex items-center gap-4">
<label for="topicFilter">Filter by Topic:</label>
<select id="topicFilter" class="px-4 py-2 border border-gray-300 rounded-lg focus:ring-2 focus:ring-green-500 focus:border-transparent outline-none">
<option value="all">All Topics</option>
<option value="1">foodsave</option>
<option value="2">foodstore</option>
<option value="3">store</option>
<option value="4">ocr</option>
<option value="5">cv</option>
<option value="6">thesis</option>
<option value="7">hello</option>
<option value="8">hi</option>
<option value="9">emoji</option>
<option value="10">nine</option>
</select>
</div>
<button id="addMessageBtn" class="flex items-center gap-2 px-4 py-2 bg-gradient-to-r from-green-500 to-emerald-500 text-white rounded-lg hover:from-green-600 hover:to-emerald-600 transition-all">
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-plus w-4 h-4"><path d="M5 12h14"></path><path d="M12 5v14"></path></svg>
Add New Message
</button>
</div>
<div class="bg-white rounded-lg shadow-md overflow-hidden">
<div class="overflow-x-auto">
<table class="w-full">
<thead class="bg-gray-50 border-b border-gray-200">
<tr>
<th class="px-6 py-3 text-left text-gray-700">Topic</th>
<th class="px-6 py-3 text-left text-gray-700">Question</th>
<th class="px-6 py-3 text-left text-gray-700">Reply</th>
<th class="px-6 py-3 text-right text-gray-700">Actions</th>
</tr>
</thead>
<tbody id="messagesTableBody" class="divide-y divide-gray-200">
<!-- Messages will be loaded dynamically -->
<tr>
<td colspan="4" class="px-6 py-8 text-center text-gray-500">
<i data-feather="loader" class="animate-spin mx-auto w-6 h-6"></i>
<p class="mt-2">Loading messages...</p>
</td>
</tr>
</tbody>
</table>
</div>
</div>
<div class="mt-6 flex items-center justify-between bg-white rounded-lg shadow px-6 py-4">
<div class="text-gray-600" id="paginationInfo">Loading...</div>
<div class="flex gap-2">
<button id="prevPageBtn" disabled class="px-4 py-2 border border-gray-300 rounded-lg hover:bg-gray-50 disabled:opacity-50 disabled:cursor-not-allowed transition-colors">Previous</button>
<button id="page1Btn" class="px-4 py-2 rounded-lg transition-all bg-gradient-to-r from-green-500 to-emerald-500 text-white">1</button>
<button id="page2Btn" class="px-4 py-2 rounded-lg transition-all border border-gray-300 hover:bg-gray-50">2</button>
<button id="nextPageBtn" class="px-4 py-2 border border-gray-300 rounded-lg hover:bg-gray-50 disabled:opacity-50 disabled:cursor-not-allowed transition-colors">Next</button>
</div>
</div>
<div class="mt-6 grid grid-cols-1 md:grid-cols-3 gap-4">
<div class="bg-white rounded-lg shadow p-6">
<p class="text-gray-600 mb-1">Total Topics</p>
<p id="totalTopics" class="text-green-600 text-2xl font-bold">-</p>
</div>
<div class="bg-white rounded-lg shadow p-6">
<p class="text-gray-600 mb-1">Total Messages</p>
<p id="totalMessages" class="text-green-600 text-2xl font-bold">-</p>
</div>
<div class="bg-white rounded-lg shadow p-6">
<p class="text-gray-600 mb-1">Filtered Messages</p>
<p id="filteredMessages" class="text-green-600 text-2xl font-bold">-</p>
</div>
</div>
</main>
<!-- Modal for Add/Edit Message -->
<div id="messageModal" class="fixed inset-0 bg-black bg-opacity-50 hidden items-center justify-center p-4 z-50">
<div class="bg-white rounded-lg shadow-xl w-full max-w-md">
<div class="p-6 border-b">
<h3 class="text-xl font-semibold" id="modalTitle">Add New Message</h3>
</div>
<div class="p-6">
<form id="messageForm">
<input type="hidden" id="messageId" value="">
<div class="mb-4">
<label for="modalTopic" class="block text-gray-700 mb-2">Topic</label>
<select id="modalTopic" class="w-full px-4 py-2 border border-gray-300 rounded-lg focus:ring-2 focus:ring-green-500 focus:border-transparent outline-none">
<option value="foodsave">foodsave</option>
<option value="foodstore">foodstore</option>
<option value="store">store</option>
<option value="ocr">ocr</option>
<option value="cv">cv</option>
<option value="thesis">thesis</option>
<option value="hello">hello</option>
<option value="hi">hi</option>
<option value="emoji">emoji</option>
<option value="nine">nine</option>
</select>
</div>
<div class="mb-4">
<label for="modalQuestion" class="block text-gray-700 mb-2">Question</label>
<textarea id="modalQuestion" rows="3" class="w-full px-4 py-2 border border-gray-300 rounded-lg focus:ring-2 focus:ring-green-500 focus:border-transparent outline-none" placeholder="Enter question"></textarea>
</div>
<div class="mb-6">
<label for="modalReply" class="block text-gray-700 mb-2">Reply</label>
<textarea id="modalReply" rows="4" class="w-full px-4 py-2 border border-gray-300 rounded-lg focus:ring-2 focus:ring-green-500 focus:border-transparent outline-none" placeholder="Enter reply"></textarea>
</div>
<div class="flex justify-end gap-3">
<button type="button" id="cancelModalBtn" class="px-4 py-2 border border-gray-300 rounded-lg hover:bg-gray-50 transition-colors">Cancel</button>
<button type="submit" class="px-4 py-2 bg-gradient-to-r from-green-500 to-emerald-500 text-white rounded-lg hover:from-green-600 hover:to-emerald-600 transition-all">Save Message</button>
</div>
</form>
</div>
</div>
</div>
<script>
feather.replace();
</script>
<script src="script.js"></script>
<script>
// Enhanced functionality for the dashboard
document.addEventListener('DOMContentLoaded', function() {
// State
let messages = [];
let filteredMessages = [];
let currentPage = 1;
const itemsPerPage = 5;
let selectedTopic = 'all';
// DOM Elements
const topicFilter = document.getElementById('topicFilter');
const messagesTableBody = document.getElementById('messagesTableBody');
const totalTopicsEl = document.getElementById('totalTopics');
const totalMessagesEl = document.getElementById('totalMessages');
const filteredMessagesEl = document.getElementById('filteredMessages');
const paginationInfo = document.getElementById('paginationInfo');
const prevPageBtn = document.getElementById('prevPageBtn');
const nextPageBtn = document.getElementById('nextPageBtn');
const page1Btn = document.getElementById('page1Btn');
const page2Btn = document.getElementById('page2Btn');
const addMessageBtn = document.getElementById('addMessageBtn');
const messageModal = document.getElementById('messageModal');
const modalTitle = document.getElementById('modalTitle');
const messageForm = document.getElementById('messageForm');
const cancelModalBtn = document.getElementById('cancelModalBtn');
const messageIdInput = document.getElementById('messageId');
const modalTopicSelect = document.getElementById('modalTopic');
const modalQuestionTextarea = document.getElementById('modalQuestion');
const modalReplyTextarea = document.getElementById('modalReply');
// Initial data (simulating API call)
function loadMessages() {
// Simulate API call with setTimeout
setTimeout(() => {
// Sample data (in real app, fetch from backend)
messages = [
{ id: 1, topic: 'foodsave', question: 'What is foodsave?', reply: 'Foodsave is a platform designed to help reduce food waste by connecting users with surplus food from restaurants and stores.' },
{ id: 2, topic: 'foodstore', question: 'How does the foodstore work?', reply: 'Foodstore allows you to browse and purchase discounted food items that are near their expiration date, helping reduce waste.' },
{ id: 3, topic: 'store', question: 'Where can I find stores near me?', reply: 'You can use our store locator feature in the app to find participating stores in your area.' },
{ id: 4, topic: 'ocr', question: 'What is OCR technology?', reply: 'OCR (Optical Character Recognition) is a technology that converts images of text into editable and searchable text data.' },
{ id: 5, topic: 'cv', question: 'How do I create a CV?', reply: 'A CV should include your personal information, education, work experience, skills, and achievements. Keep it clear and concise.' },
{ id: 6, topic: 'thesis', question: 'What is a thesis?', reply: 'A thesis is a long essay or dissertation involving personal research, written by a candidate for a university degree.' },
{ id: 7, topic: 'hello', question: 'How to say hello in Spanish?', reply: 'In Spanish, hello is "Hola".' },
{ id: 8, topic: 'hi', question: 'What is the difference between hi and hello?', reply: 'Both are greetings, "hi" is more informal while "hello" is more formal.' },
{ id: 9, topic: 'emoji', question: 'What are emojis?', reply: 'Emojis are small digital images or icons used to express an idea or emotion in electronic communication.' },
{ id: 10, topic: 'nine', question: 'What is nine squared?', reply: 'Nine squared (9²) equals 81.' }
];
updateUI();
}, 500);
}
// Update UI based on current state
function updateUI() {
// Filter messages by topic
if (selectedTopic === 'all') {
filteredMessages = [...messages];
} else {
filteredMessages = messages.filter(msg => msg.topic === selectedTopic);
}
// Update stats
const uniqueTopics = [...new Set(messages.map(msg => msg.topic))];
totalTopicsEl.textContent = uniqueTopics.length;
totalMessagesEl.textContent = messages.length;
filteredMessagesEl.textContent = filteredMessages.length;
// Update pagination
const totalPages = Math.ceil(filteredMessages.length / itemsPerPage);
const startIndex = (currentPage - 1) * itemsPerPage;
const endIndex = Math.min(startIndex + itemsPerPage, filteredMessages.length);
const pageMessages = filteredMessages.slice(startIndex, endIndex);
// Update pagination info
paginationInfo.textContent = `Showing ${startIndex + 1} to ${endIndex} of ${filteredMessages.length} messages`;
// Update pagination buttons
prevPageBtn.disabled = currentPage === 1;
nextPageBtn.disabled = currentPage === totalPages;
// Update page buttons
page1Btn.textContent = currentPage;
page1Btn.classList.remove('border', 'border-gray-300', 'hover:bg-gray-50');
page1Btn.classList.add('bg-gradient-to-r', 'from-green-500', 'to-emerald-500', 'text-white');
if (totalPages > 1) {
page2Btn.style.display = 'inline-block';
page2Btn.textContent = currentPage + 1;
page2Btn.classList.remove('bg-gradient-to-r', 'from-green-500', 'to-emerald-500', 'text-white');
page2Btn.classList.add('border', 'border-gray-300', 'hover:bg-gray-50');
} else {
page2Btn.style.display = 'none';
}
// Render table rows
messagesTableBody.innerHTML = '';
if (pageMessages.length === 0) {
messagesTableBody.innerHTML = `
<tr>
<td colspan="4" class="px-6 py-8 text-center text-gray-500">
<i data-feather="inbox" class="mx-auto w-8 h-8"></i>
<p class="mt-2">No messages found</p>
</td>
</tr>
`;
} else {
pageMessages.forEach(msg => {
const row = document.createElement('tr');
row.className = 'hover:bg-gray-50';
row.innerHTML = `
<td class="px-6 py-4">
<span class="inline-block px-3 py-1 bg-green-100 text-green-700 rounded-full">${msg.topic}</span>
</td>
<td class="px-6 py-4">
<span>${msg.question}</span>
</td>
<td class="px-6 py-4">
<span class="text-gray-600">${msg.reply}</span>
</td>
<td class="px-6 py-4">
<div class="flex gap-2 justify-end">
<button class="edit-btn p-2 bg-gradient-to-r from-green-500 to-emerald-500 text-white rounded hover:from-green-600 hover:to-emerald-600 transition-all" title="Edit" data-id="${msg.id}">
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-pen w-4 h-4"><path d="M21.174 6.812a1 1 0 0 0-3.986-3.987L3.842 16.174a2 2 0 0 0-.5.83l-1.321 4.352a.5.5 0 0 0 .623.622l4.353-1.32a2 2 0 0 0 .83-.497z"></path></svg>
</button>
<button class="delete-btn p-2 bg-red-600 text-white rounded hover:bg-red-700 transition-colors" title="Delete" data-id="${msg.id}">
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-trash2 lucide-trash-2 w-4 h-4"><path d="M3 6h18"></path><path d="M19 6v14c0 1-1 2-2 2H7c-1 0-2-1-2-2V6"></path><path d="M8 6V4c0-1 1-2 2-2h4c1 0 2 1 2 2v2"></path><line x1="10" x2="10" y1="11" y2="17"></line><line x1="14" x2="14" y1="11" y2="17"></line></svg>
</button>
</div>
</td>
`;
messagesTableBody.appendChild(row);
});
}
// Re-attach event listeners to edit/delete buttons
attachRowEventListeners();
feather.replace();
}
// Attach event listeners to edit/delete buttons
function attachRowEventListeners() {
document.querySelectorAll('.edit-btn').forEach(btn => {
btn.addEventListener('click', function() {
const id = parseInt(this.getAttribute('data-id'));
const message = messages.find(msg => msg.id === id);
if (message) {
openEditModal(message);
}
});
});
document.querySelectorAll('.delete-btn').forEach(btn => {
btn.addEventListener('click', function() {
const id = parseInt(this.getAttribute('data-id'));
if (confirm('Are you sure you want to delete this message?')) {
deleteMessage(id);
}
});
});
}
// Open modal for editing
function openEditModal(message) {
modalTitle.textContent = 'Edit Message';
messageIdInput.value = message.id;
modalTopicSelect.value = message.topic;
modalQuestionTextarea.value = message.question;
modalReplyTextarea.value = message.reply;
messageModal.classList.remove('hidden');
messageModal.classList.add('flex');
}
// Open modal for adding
function openAddModal() {
modalTitle.textContent = 'Add New Message';
messageIdInput.value = '';
modalTopicSelect.value = 'foodsave';
modalQuestionTextarea.value = '';
modalReplyTextarea.value = '';
messageModal.classList.remove('hidden');
messageModal.classList.add('flex');
}
// Delete message
function deleteMessage(id) {
messages = messages.filter(msg => msg.id !== id);
updateUI();
}
// Save message (add or update)
function saveMessage(id, topic, question, reply) {
if (id) {
// Update existing
const index = messages.findIndex(msg => msg.id === id);
if (index !== -1) {
messages[index] = { id, topic, question, reply };
}
} else {
// Add new
const newId = messages.length > 0 ? Math.max(...messages.map(msg => msg.id)) + 1 : 1;
messages.push({ id: newId, topic, question, reply });
}
updateUI();
closeModal();
}
// Close modal
function closeModal() {
messageModal.classList.add('hidden');
messageModal.classList.remove('flex');
}
// Event Listeners
topicFilter.addEventListener('change', function() {
selectedTopic = this.value;
currentPage = 1;
updateUI();
});
prevPageBtn.addEventListener('click', function() {
if (currentPage > 1) {
currentPage--;
updateUI();
}
});
nextPageBtn.addEventListener('click', function() {
const totalPages = Math.ceil(filteredMessages.length / itemsPerPage);
if (currentPage < totalPages) {
currentPage++;
updateUI();
}
});
page1Btn.addEventListener('click', function() {
currentPage = parseInt(this.textContent);
updateUI();
});
page2Btn.addEventListener('click', function() {
currentPage = parseInt(this.textContent);
updateUI();
});
addMessageBtn.addEventListener('click', openAddModal);
messageForm.addEventListener('submit', function(e) {
e.preventDefault();
const id = messageIdInput.value ? parseInt(messageIdInput.value) : null;
const topic = modalTopicSelect.value;
const question = modalQuestionTextarea.value.trim();
const reply = modalReplyTextarea.value.trim();
if (!question || !reply) {
alert('Please fill in both question and reply.');
return;
}
saveMessage(id, topic, question, reply);
});
cancelModalBtn.addEventListener('click', closeModal);
// Close modal when clicking outside
messageModal.addEventListener('click', function(e) {
if (e.target === messageModal) {
closeModal();
}
});
// Load initial data
loadMessages();
});
</script>
</body>
</html>