Spaces:
Running
Running
<html lang="en"> | |
<head> | |
<meta charset="UTF-8"> | |
<meta name="viewport" content="width=device-width, initial-scale=1.0"> | |
<title>Keyword Manager</title> | |
<script src="https://cdn.tailwindcss.com"></script> | |
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.4.0/css/all.min.css"> | |
<style> | |
.fade-in { | |
animation: fadeIn 0.3s ease-in-out; | |
} | |
@keyframes fadeIn { | |
from { opacity: 0; transform: translateY(10px); } | |
to { opacity: 1; transform: translateY(0); } | |
} | |
.sidebar { | |
transition: all 0.3s ease; | |
} | |
.sidebar.collapsed { | |
width: 70px; | |
} | |
.sidebar.collapsed .sidebar-text { | |
display: none; | |
} | |
.sidebar.collapsed .menu-icon { | |
margin-right: 0; | |
} | |
</style> | |
</head> | |
<body class="bg-gray-100 font-sans"> | |
<div class="flex h-screen overflow-hidden"> | |
<!-- Sidebar --> | |
<div class="sidebar bg-indigo-800 text-white w-64 flex-shrink-0 flex flex-col"> | |
<div class="p-4 flex items-center border-b border-indigo-700"> | |
<i class="fas fa-keyboard text-2xl mr-3 menu-icon"></i> | |
<span class="text-xl font-bold sidebar-text">Keyword Manager</span> | |
</div> | |
<nav class="flex-1 overflow-y-auto"> | |
<a href="#" class="block py-3 px-4 hover:bg-indigo-700 flex items-center active-nav-item" id="dashboard-nav"> | |
<i class="fas fa-tachometer-alt mr-3 menu-icon"></i> | |
<span class="sidebar-text">Dashboard</span> | |
</a> | |
<a href="#" class="block py-3 px-4 hover:bg-indigo-700 flex items-center" id="keywords-nav"> | |
<i class="fas fa-key mr-3 menu-icon"></i> | |
<span class="sidebar-text">Keywords</span> | |
</a> | |
<a href="#" class="block py-3 px-4 hover:bg-indigo-700 flex items-center" id="analytics-nav"> | |
<i class="fas fa-chart-line mr-3 menu-icon"></i> | |
<span class="sidebar-text">Analytics</span> | |
</a> | |
<a href="#" class="block py-3 px-4 hover:bg-indigo-700 flex items-center" id="settings-nav"> | |
<i class="fas fa-cog mr-3 menu-icon"></i> | |
<span class="sidebar-text">Settings</span> | |
</a> | |
</nav> | |
<div class="p-4 border-t border-indigo-700"> | |
<button id="toggle-sidebar" class="w-full py-2 px-4 bg-indigo-700 hover:bg-indigo-600 rounded flex items-center justify-center"> | |
<i class="fas fa-chevron-left menu-icon"></i> | |
<span class="ml-2 sidebar-text">Collapse</span> | |
</button> | |
</div> | |
</div> | |
<!-- Main Content --> | |
<div class="flex-1 overflow-auto"> | |
<!-- Header --> | |
<header class="bg-white shadow-sm"> | |
<div class="flex justify-between items-center p-4"> | |
<h1 class="text-2xl font-semibold text-gray-800">Keyword Management</h1> | |
<div class="flex items-center space-x-4"> | |
<div class="relative"> | |
<input type="text" placeholder="Search..." class="pl-10 pr-4 py-2 border rounded-lg focus:outline-none focus:ring-2 focus:ring-indigo-500"> | |
<i class="fas fa-search absolute left-3 top-3 text-gray-400"></i> | |
</div> | |
<div class="flex items-center space-x-2"> | |
<div class="relative"> | |
<i class="fas fa-bell text-xl text-gray-600 cursor-pointer"></i> | |
<span class="absolute -top-1 -right-1 bg-red-500 text-white text-xs rounded-full h-4 w-4 flex items-center justify-center">3</span> | |
</div> | |
<div class="h-8 w-8 rounded-full bg-indigo-500 flex items-center justify-center text-white font-semibold">JD</div> | |
</div> | |
</div> | |
</div> | |
</header> | |
<!-- Content Area --> | |
<main class="p-6"> | |
<!-- Stats Cards --> | |
<div class="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-4 gap-6 mb-6"> | |
<div class="bg-white rounded-lg shadow p-6"> | |
<div class="flex items-center justify-between"> | |
<div> | |
<p class="text-gray-500">Total Keywords</p> | |
<h3 class="text-2xl font-bold">1,248</h3> | |
</div> | |
<div class="p-3 rounded-full bg-indigo-100 text-indigo-600"> | |
<i class="fas fa-key text-xl"></i> | |
</div> | |
</div> | |
<p class="text-sm text-green-500 mt-2"><i class="fas fa-arrow-up mr-1"></i> 12% from last month</p> | |
</div> | |
<div class="bg-white rounded-lg shadow p-6"> | |
<div class="flex items-center justify-between"> | |
<div> | |
<p class="text-gray-500">Active Topics</p> | |
<h3 class="text-2xl font-bold">42</h3> | |
</div> | |
<div class="p-3 rounded-full bg-blue-100 text-blue-600"> | |
<i class="fas fa-tags text-xl"></i> | |
</div> | |
</div> | |
<p class="text-sm text-green-500 mt-2"><i class="fas fa-arrow-up mr-1"></i> 5% from last month</p> | |
</div> | |
<div class="bg-white rounded-lg shadow p-6"> | |
<div class="flex items-center justify-between"> | |
<div> | |
<p class="text-gray-500">Users</p> | |
<h3 class="text-2xl font-bold">8</h3> | |
</div> | |
<div class="p-3 rounded-full bg-green-100 text-green-600"> | |
<i class="fas fa-users text-xl"></i> | |
</div> | |
</div> | |
<p class="text-sm text-gray-500 mt-2">No change from last month</p> | |
</div> | |
<div class="bg-white rounded-lg shadow p-6"> | |
<div class="flex items-center justify-between"> | |
<div> | |
<p class="text-gray-500">Recent Activity</p> | |
<h3 class="text-2xl font-bold">24</h3> | |
</div> | |
<div class="p-3 rounded-full bg-purple-100 text-purple-600"> | |
<i class="fas fa-clock text-xl"></i> | |
</div> | |
</div> | |
<p class="text-sm text-red-500 mt-2"><i class="fas fa-arrow-down mr-1"></i> 3% from last week</p> | |
</div> | |
</div> | |
<!-- Keywords Table Section --> | |
<div class="bg-white rounded-lg shadow overflow-hidden"> | |
<div class="p-4 border-b flex justify-between items-center"> | |
<h2 class="text-lg font-semibold text-gray-800">Keywords List</h2> | |
<button id="add-keyword-btn" class="bg-indigo-600 hover:bg-indigo-700 text-white px-4 py-2 rounded-lg flex items-center"> | |
<i class="fas fa-plus mr-2"></i> Add Keyword | |
</button> | |
</div> | |
<!-- Filter and Search --> | |
<div class="p-4 border-b bg-gray-50"> | |
<div class="flex flex-col md:flex-row md:items-center md:justify-between space-y-4 md:space-y-0"> | |
<div class="flex items-center space-x-2"> | |
<label class="text-sm text-gray-600">Filter by:</label> | |
<select class="border rounded px-3 py-1 text-sm focus:outline-none focus:ring-1 focus:ring-indigo-500"> | |
<option>All Topics</option> | |
<option>Technology</option> | |
<option>Marketing</option> | |
<option>Business</option> | |
<option>Science</option> | |
</select> | |
<select class="border rounded px-3 py-1 text-sm focus:outline-none focus:ring-1 focus:ring-indigo-500"> | |
<option>All Users</option> | |
<option>Admin</option> | |
<option>Editor</option> | |
<option>Viewer</option> | |
</select> | |
</div> | |
<div class="relative"> | |
<input type="text" placeholder="Search keywords..." class="pl-8 pr-4 py-1 border rounded-lg focus:outline-none focus:ring-1 focus:ring-indigo-500"> | |
<i class="fas fa-search absolute left-3 top-2 text-gray-400"></i> | |
</div> | |
</div> | |
</div> | |
<!-- Table --> | |
<div class="overflow-x-auto"> | |
<table class="min-w-full divide-y divide-gray-200"> | |
<thead class="bg-gray-50"> | |
<tr> | |
<th scope="col" class="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">Topic</th> | |
<th scope="col" class="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">Keyword</th> | |
<th scope="col" class="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">User</th> | |
<th scope="col" class="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">Date Added</th> | |
<th scope="col" class="px-6 py-3 text-right text-xs font-medium text-gray-500 uppercase tracking-wider">Actions</th> | |
</tr> | |
</thead> | |
<tbody class="bg-white divide-y divide-gray-200" id="keywords-table-body"> | |
<!-- Sample data - will be populated by JavaScript --> | |
<tr> | |
<td class="px-6 py-4 whitespace-nowrap"> | |
<div class="text-sm font-medium text-gray-900">Technology</div> | |
</td> | |
<td class="px-6 py-4 whitespace-nowrap"> | |
<div class="text-sm text-gray-900">Artificial Intelligence</div> | |
</td> | |
<td class="px-6 py-4 whitespace-nowrap"> | |
<div class="text-sm text-gray-900">admin</div> | |
</td> | |
<td class="px-6 py-4 whitespace-nowrap"> | |
<div class="text-sm text-gray-900">2023-05-15</div> | |
</td> | |
<td class="px-6 py-4 whitespace-nowrap text-right text-sm font-medium"> | |
<button class="text-indigo-600 hover:text-indigo-900 mr-2 edit-btn">Edit</button> | |
<button class="text-red-600 hover:text-red-900 delete-btn">Delete</button> | |
</td> | |
</tr> | |
<tr> | |
<td class="px-6 py-4 whitespace-nowrap"> | |
<div class="text-sm font-medium text-gray-900">Marketing</div> | |
</td> | |
<td class="px-6 py-4 whitespace-nowrap"> | |
<div class="text-sm text-gray-900">SEO Optimization</div> | |
</td> | |
<td class="px-6 py-4 whitespace-nowrap"> | |
<div class="text-sm text-gray-900">editor</div> | |
</td> | |
<td class="px-6 py-4 whitespace-nowrap"> | |
<div class="text-sm text-gray-900">2023-05-14</div> | |
</td> | |
<td class="px-6 py-4 whitespace-nowrap text-right text-sm font-medium"> | |
<button class="text-indigo-600 hover:text-indigo-900 mr-2 edit-btn">Edit</button> | |
<button class="text-red-600 hover:text-red-900 delete-btn">Delete</button> | |
</td> | |
</tr> | |
<tr> | |
<td class="px-6 py-4 whitespace-nowrap"> | |
<div class="text-sm font-medium text-gray-900">Business</div> | |
</td> | |
<td class="px-6 py-4 whitespace-nowrap"> | |
<div class="text-sm text-gray-900">Market Analysis</div> | |
</td> | |
<td class="px-6 py-4 whitespace-nowrap"> | |
<div class="text-sm text-gray-900">viewer</div> | |
</td> | |
<td class="px-6 py-4 whitespace-nowrap"> | |
<div class="text-sm text-gray-900">2023-05-13</div> | |
</td> | |
<td class="px-6 py-4 whitespace-nowrap text-right text-sm font-medium"> | |
<button class="text-indigo-600 hover:text-indigo-900 mr-2 edit-btn">Edit</button> | |
<button class="text-red-600 hover:text-red-900 delete-btn">Delete</button> | |
</td> | |
</tr> | |
</tbody> | |
</table> | |
</div> | |
<!-- Pagination --> | |
<div class="bg-white px-4 py-3 flex items-center justify-between border-t border-gray-200 sm:px-6"> | |
<div class="flex-1 flex justify-between sm:hidden"> | |
<a href="#" class="relative inline-flex items-center px-4 py-2 border border-gray-300 text-sm font-medium rounded-md text-gray-700 bg-white hover:bg-gray-50">Previous</a> | |
<a href="#" class="ml-3 relative inline-flex items-center px-4 py-2 border border-gray-300 text-sm font-medium rounded-md text-gray-700 bg-white hover:bg-gray-50">Next</a> | |
</div> | |
<div class="hidden sm:flex-1 sm:flex sm:items-center sm:justify-between"> | |
<div> | |
<p class="text-sm text-gray-700"> | |
Showing <span class="font-medium">1</span> to <span class="font-medium">10</span> of <span class="font-medium">97</span> results | |
</p> | |
</div> | |
<div> | |
<nav class="relative z-0 inline-flex rounded-md shadow-sm -space-x-px" aria-label="Pagination"> | |
<a href="#" class="relative inline-flex items-center px-2 py-2 rounded-l-md border border-gray-300 bg-white text-sm font-medium text-gray-500 hover:bg-gray-50"> | |
<span class="sr-only">Previous</span> | |
<i class="fas fa-chevron-left"></i> | |
</a> | |
<a href="#" aria-current="page" class="z-10 bg-indigo-50 border-indigo-500 text-indigo-600 relative inline-flex items-center px-4 py-2 border text-sm font-medium">1</a> | |
<a href="#" class="bg-white border-gray-300 text-gray-500 hover:bg-gray-50 relative inline-flex items-center px-4 py-2 border text-sm font-medium">2</a> | |
<a href="#" class="bg-white border-gray-300 text-gray-500 hover:bg-gray-50 relative inline-flex items-center px-4 py-2 border text-sm font-medium">3</a> | |
<a href="#" class="relative inline-flex items-center px-2 py-2 rounded-r-md border border-gray-300 bg-white text-sm font-medium text-gray-500 hover:bg-gray-50"> | |
<span class="sr-only">Next</span> | |
<i class="fas fa-chevron-right"></i> | |
</a> | |
</nav> | |
</div> | |
</div> | |
</div> | |
</div> | |
</main> | |
</div> | |
</div> | |
<!-- Add/Edit Keyword Modal --> | |
<div id="keyword-modal" class="fixed inset-0 bg-black bg-opacity-50 flex items-center justify-center z-50 hidden"> | |
<div class="bg-white rounded-lg shadow-xl w-full max-w-md fade-in"> | |
<div class="p-4 border-b flex justify-between items-center"> | |
<h3 class="text-lg font-semibold text-gray-800" id="modal-title">Add New Keyword</h3> | |
<button id="close-modal" class="text-gray-400 hover:text-gray-500"> | |
<i class="fas fa-times"></i> | |
</button> | |
</div> | |
<form id="keyword-form" class="p-4"> | |
<input type="hidden" id="keyword-id"> | |
<div class="mb-4"> | |
<label for="topic" class="block text-sm font-medium text-gray-700 mb-1">Topic</label> | |
<input type="text" id="topic" name="topic" class="w-full px-3 py-2 border rounded-lg focus:outline-none focus:ring-1 focus:ring-indigo-500" required> | |
</div> | |
<div class="mb-4"> | |
<label for="keyword" class="block text-sm font-medium text-gray-700 mb-1">Keyword</label> | |
<input type="text" id="keyword" name="keyword" class="w-full px-3 py-2 border rounded-lg focus:outline-none focus:ring-1 focus:ring-indigo-500" required> | |
</div> | |
<div class="mb-4"> | |
<label for="user" class="block text-sm font-medium text-gray-700 mb-1">User</label> | |
<select id="user" name="user" class="w-full px-3 py-2 border rounded-lg focus:outline-none focus:ring-1 focus:ring-indigo-500" required> | |
<option value="">Select User</option> | |
<option value="admin">Admin</option> | |
<option value="editor">Editor</option> | |
<option value="viewer">Viewer</option> | |
</select> | |
</div> | |
<div class="flex justify-end space-x-3 pt-4"> | |
<button type="button" id="cancel-modal" class="px-4 py-2 border rounded-lg text-gray-700 hover:bg-gray-50">Cancel</button> | |
<button type="submit" class="px-4 py-2 bg-indigo-600 text-white rounded-lg hover:bg-indigo-700">Save</button> | |
</div> | |
</form> | |
</div> | |
</div> | |
<!-- Delete Confirmation Modal --> | |
<div id="delete-modal" class="fixed inset-0 bg-black bg-opacity-50 flex items-center justify-center z-50 hidden"> | |
<div class="bg-white rounded-lg shadow-xl w-full max-w-md fade-in"> | |
<div class="p-4 border-b"> | |
<h3 class="text-lg font-semibold text-gray-800">Confirm Deletion</h3> | |
</div> | |
<div class="p-4"> | |
<p class="text-gray-700 mb-4">Are you sure you want to delete this keyword? This action cannot be undone.</p> | |
<div class="flex justify-end space-x-3"> | |
<button id="cancel-delete" class="px-4 py-2 border rounded-lg text-gray-700 hover:bg-gray-50">Cancel</button> | |
<button id="confirm-delete" class="px-4 py-2 bg-red-600 text-white rounded-lg hover:bg-red-700">Delete</button> | |
</div> | |
</div> | |
</div> | |
</div> | |
<script> | |
// Toggle sidebar | |
document.getElementById('toggle-sidebar').addEventListener('click', function() { | |
const sidebar = document.querySelector('.sidebar'); | |
sidebar.classList.toggle('collapsed'); | |
const icon = this.querySelector('i'); | |
const text = this.querySelector('span'); | |
if (sidebar.classList.contains('collapsed')) { | |
icon.classList.remove('fa-chevron-left'); | |
icon.classList.add('fa-chevron-right'); | |
text.textContent = 'Expand'; | |
} else { | |
icon.classList.remove('fa-chevron-right'); | |
icon.classList.add('fa-chevron-left'); | |
text.textContent = 'Collapse'; | |
} | |
}); | |
// Navigation active state | |
const navItems = document.querySelectorAll('nav a'); | |
navItems.forEach(item => { | |
item.addEventListener('click', function(e) { | |
e.preventDefault(); | |
navItems.forEach(nav => nav.classList.remove('active-nav-item', 'bg-indigo-700')); | |
this.classList.add('active-nav-item', 'bg-indigo-700'); | |
}); | |
}); | |
// Modal handling | |
const modal = document.getElementById('keyword-modal'); | |
const deleteModal = document.getElementById('delete-modal'); | |
const addKeywordBtn = document.getElementById('add-keyword-btn'); | |
const closeModalBtn = document.getElementById('close-modal'); | |
const cancelModalBtn = document.getElementById('cancel-modal'); | |
const cancelDeleteBtn = document.getElementById('cancel-delete'); | |
const confirmDeleteBtn = document.getElementById('confirm-delete'); | |
const keywordForm = document.getElementById('keyword-form'); | |
const modalTitle = document.getElementById('modal-title'); | |
// Show add modal | |
addKeywordBtn.addEventListener('click', function() { | |
modalTitle.textContent = 'Add New Keyword'; | |
keywordForm.reset(); | |
document.getElementById('keyword-id').value = ''; | |
modal.classList.remove('hidden'); | |
}); | |
// Close modal | |
closeModalBtn.addEventListener('click', function() { | |
modal.classList.add('hidden'); | |
}); | |
cancelModalBtn.addEventListener('click', function() { | |
modal.classList.add('hidden'); | |
}); | |
// Close modal when clicking outside | |
modal.addEventListener('click', function(e) { | |
if (e.target === modal) { | |
modal.classList.add('hidden'); | |
} | |
}); | |
deleteModal.addEventListener('click', function(e) { | |
if (e.target === deleteModal) { | |
deleteModal.classList.add('hidden'); | |
} | |
}); | |
// Edit button click | |
document.addEventListener('click', function(e) { | |
if (e.target.classList.contains('edit-btn')) { | |
const row = e.target.closest('tr'); | |
const cells = row.querySelectorAll('td'); | |
modalTitle.textContent = 'Edit Keyword'; | |
document.getElementById('keyword-id').value = '1'; // In real app, this would be the ID from DB | |
document.getElementById('topic').value = cells[0].textContent.trim(); | |
document.getElementById('keyword').value = cells[1].textContent.trim(); | |
document.getElementById('user').value = cells[2].textContent.trim(); | |
modal.classList.remove('hidden'); | |
} | |
// Delete button click | |
if (e.target.classList.contains('delete-btn')) { | |
deleteModal.classList.remove('hidden'); | |
} | |
}); | |
// Cancel delete | |
cancelDeleteBtn.addEventListener('click', function() { | |
deleteModal.classList.add('hidden'); | |
}); | |
// Confirm delete | |
confirmDeleteBtn.addEventListener('click', function() { | |
// In a real app, you would send a request to delete the record | |
deleteModal.classList.add('hidden'); | |
// Show a success message | |
alert('Keyword deleted successfully!'); | |
}); | |
// Form submission | |
keywordForm.addEventListener('submit', function(e) { | |
e.preventDefault(); | |
const id = document.getElementById('keyword-id').value; | |
const topic = document.getElementById('topic').value; | |
const keyword = document.getElementById('keyword').value; | |
const user = document.getElementById('user').value; | |
// In a real app, you would send this data to the server | |
console.log('Form submitted:', { id, topic, keyword, user }); | |
// Close the modal | |
modal.classList.add('hidden'); | |
// Show a success message | |
alert(`Keyword ${id ? 'updated' : 'added'} successfully!`); | |
// In a real app, you would update the table with the new data | |
}); | |
// Simulate loading data from the server | |
function loadKeywords(page = 1) { | |
// In a real app, this would be an AJAX call to your Flask backend | |
console.log(`Loading keywords for page ${page}`); | |
} | |
// Initial load | |
loadKeywords(); | |
</script> | |
<p style="border-radius: 8px; text-align: center; font-size: 12px; color: #fff; margin-top: 16px;position: fixed; left: 8px; bottom: 8px; z-index: 10; background: rgba(0, 0, 0, 0.8); padding: 4px 8px;">Made with <img src="https://enzostvs-deepsite.hf.space/logo.svg" alt="DeepSite Logo" style="width: 16px; height: 16px; vertical-align: middle;display:inline-block;margin-right:3px;filter:brightness(0) invert(1);"><a href="https://enzostvs-deepsite.hf.space" style="color: #fff;text-decoration: underline;" target="_blank" >DeepSite</a> - 🧬 <a href="https://enzostvs-deepsite.hf.space?remix=oliverjim/keywordmanage" style="color: #fff;text-decoration: underline;" target="_blank" >Remix</a></p></body> | |
</html> |