Spaces:
Running
Running
| <html lang="en"> | |
| <head> | |
| <meta charset="UTF-8"> | |
| <meta name="viewport" content="width=device-width, initial-scale=1.0"> | |
| <title>DataCrudMaster - Manage Your Data</title> | |
| <script src="https://cdn.tailwindcss.com"></script> | |
| <script src="https://unpkg.com/feather-icons"></script> | |
| <script src="https://cdn.jsdelivr.net/npm/feather-icons/dist/feather.min.js"></script> | |
| <script src="https://cdn.jsdelivr.net/npm/vanta@latest/dist/vanta.net.min.js"></script> | |
| <style> | |
| .vanta-bg { | |
| position: fixed; | |
| width: 100%; | |
| height: 100%; | |
| z-index: -1; | |
| } | |
| .card-hover { | |
| transition: all 0.3s ease; | |
| } | |
| .card-hover:hover { | |
| transform: translateY(-5px); | |
| box-shadow: 0 20px 25px -5px rgba(0, 0, 0, 0.1), 0 10px 10px -5px rgba(0, 0, 0, 0.04); | |
| } | |
| </style> | |
| </head> | |
| <body> | |
| <div id="vanta-bg" class="vanta-bg"></div> | |
| <div class="min-h-screen flex flex-col items-center justify-center px-4"> | |
| <div class="w-full max-w-4xl bg-white/90 backdrop-blur-md rounded-xl shadow-xl overflow-hidden"> | |
| <!-- Header --> | |
| <div class="bg-indigo-600 px-6 py-4 flex justify-between items-center"> | |
| <h1 class="text-2xl font-bold text-white"> | |
| <i data-feather="database" class="inline mr-2"></i> DataCrudMaster | |
| </h1> | |
| <button id="addBtn" class="bg-white text-indigo-600 px-4 py-2 rounded-lg flex items-center hover:bg-indigo-50 transition"> | |
| <i data-feather="plus" class="mr-2"></i> Add Record | |
| </button> | |
| </div> | |
| <!-- Search and Filters --> | |
| <div class="p-4 border-b"> | |
| <div class="flex flex-col md:flex-row gap-4"> | |
| <div class="relative flex-grow"> | |
| <input type="text" placeholder="Search records..." class="w-full pl-10 pr-4 py-2 border rounded-lg focus:ring-2 focus:ring-indigo-500 focus:border-indigo-500"> | |
| <i data-feather="search" class="absolute left-3 top-3 text-gray-400"></i> | |
| </div> | |
| <select class="border rounded-lg px-4 py-2 focus:ring-2 focus:ring-indigo-500 focus:border-indigo-500"> | |
| <option>All Categories</option> | |
| <option>Customers</option> | |
| <option>Products</option> | |
| <option>Orders</option> | |
| </select> | |
| </div> | |
| </div> | |
| <!-- Data Table --> | |
| <div class="overflow-x-auto"> | |
| <table class="min-w-full divide-y divide-gray-200"> | |
| <thead class="bg-gray-50"> | |
| <tr> | |
| <th class="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">ID</th> | |
| <th class="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">Name</th> | |
| <th class="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">Email</th> | |
| <th class="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">Status</th> | |
| <th class="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">Actions</th> | |
| </tr> | |
| </thead> | |
| <tbody class="bg-white divide-y divide-gray-200" id="dataTable"> | |
| <!-- Data will be loaded here --> | |
| </tbody> | |
| </table> | |
| </div> | |
| <!-- Pagination --> | |
| <div class="px-6 py-4 flex items-center justify-between border-t"> | |
| <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">20</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 data-feather="chevron-left" class="h-5 w-5"></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 data-feather="chevron-right" class="h-5 w-5"></i> | |
| </a> | |
| </nav> | |
| </div> | |
| </div> | |
| </div> | |
| </div> | |
| </div> | |
| <!-- Add/Edit Modal --> | |
| <div id="crudModal" class="fixed inset-0 bg-black bg-opacity-50 flex items-center justify-center z-50 hidden"> | |
| <div class="bg-white rounded-xl shadow-2xl w-full max-w-md mx-4"> | |
| <div class="flex justify-between items-center border-b px-6 py-4"> | |
| <h3 class="text-lg font-semibold" id="modalTitle">Add New Record</h3> | |
| <button id="closeModal" class="text-gray-500 hover:text-gray-700"> | |
| <i data-feather="x"></i> | |
| </button> | |
| </div> | |
| <div class="p-6"> | |
| <form id="crudForm"> | |
| <input type="hidden" id="recordId"> | |
| <div class="mb-4"> | |
| <label for="name" class="block text-sm font-medium text-gray-700 mb-1">Name</label> | |
| <input type="text" id="name" class="w-full px-4 py-2 border rounded-lg focus:ring-2 focus:ring-indigo-500 focus:border-indigo-500"> | |
| </div> | |
| <div class="mb-4"> | |
| <label for="email" class="block text-sm font-medium text-gray-700 mb-1">Email</label> | |
| <input type="email" id="email" class="w-full px-4 py-2 border rounded-lg focus:ring-2 focus:ring-indigo-500 focus:border-indigo-500"> | |
| </div> | |
| <div class="mb-4"> | |
| <label for="status" class="block text-sm font-medium text-gray-700 mb-1">Status</label> | |
| <select id="status" class="w-full px-4 py-2 border rounded-lg focus:ring-2 focus:ring-indigo-500 focus:border-indigo-500"> | |
| <option value="active">Active</option> | |
| <option value="inactive">Inactive</option> | |
| <option value="pending">Pending</option> | |
| </select> | |
| </div> | |
| </form> | |
| </div> | |
| <div class="border-t px-6 py-4 flex justify-end gap-3"> | |
| <button id="cancelBtn" class="px-4 py-2 border rounded-lg text-gray-700 hover:bg-gray-50">Cancel</button> | |
| <button id="saveBtn" class="px-4 py-2 bg-indigo-600 text-white rounded-lg hover:bg-indigo-700">Save</button> | |
| </div> | |
| </div> | |
| </div> | |
| <!-- Delete Confirmation Modal --> | |
| <div id="deleteModal" class="fixed inset-0 bg-black bg-opacity-50 flex items-center justify-center z-50 hidden"> | |
| <div class="bg-white rounded-xl shadow-2xl w-full max-w-md mx-4"> | |
| <div class="p-6"> | |
| <div class="flex items-center justify-center mb-4"> | |
| <div class="bg-red-100 p-3 rounded-full"> | |
| <i data-feather="alert-triangle" class="text-red-600 w-8 h-8"></i> | |
| </div> | |
| </div> | |
| <h3 class="text-lg font-semibold text-center mb-2">Confirm Delete</h3> | |
| <p class="text-gray-600 text-center mb-6">Are you sure you want to delete this record? This action cannot be undone.</p> | |
| <div class="flex justify-center gap-3"> | |
| <button id="cancelDelete" class="px-4 py-2 border rounded-lg text-gray-700 hover:bg-gray-50">Cancel</button> | |
| <button id="confirmDelete" class="px-4 py-2 bg-red-600 text-white rounded-lg hover:bg-red-700">Delete</button> | |
| </div> | |
| </div> | |
| </div> | |
| </div> | |
| <script> | |
| // Vanta.js background | |
| VANTA.NET({ | |
| el: "#vanta-bg", | |
| mouseControls: true, | |
| touchControls: true, | |
| gyroControls: false, | |
| minHeight: 200.00, | |
| minWidth: 200.00, | |
| scale: 1.00, | |
| scaleMobile: 1.00, | |
| color: 0x5b21b6, | |
| backgroundColor: 0xf8fafc, | |
| points: 12.00, | |
| maxDistance: 22.00, | |
| spacing: 18.00 | |
| }); | |
| // Sample data | |
| let records = [ | |
| { id: 1, name: 'John Doe', email: 'john@example.com', status: 'active' }, | |
| { id: 2, name: 'Jane Smith', email: 'jane@example.com', status: 'active' }, | |
| { id: 3, name: 'Robert Johnson', email: 'robert@example.com', status: 'inactive' }, | |
| { id: 4, name: 'Emily Davis', email: 'emily@example.com', status: 'pending' }, | |
| { id: 5, name: 'Michael Brown', email: 'michael@example.com', status: 'active' } | |
| ]; | |
| // DOM elements | |
| const dataTable = document.getElementById('dataTable'); | |
| const crudModal = document.getElementById('crudModal'); | |
| const deleteModal = document.getElementById('deleteModal'); | |
| const addBtn = document.getElementById('addBtn'); | |
| const closeModal = document.getElementById('closeModal'); | |
| const cancelBtn = document.getElementById('cancelBtn'); | |
| const saveBtn = document.getElementById('saveBtn'); | |
| const cancelDelete = document.getElementById('cancelDelete'); | |
| const confirmDelete = document.getElementById('confirmDelete'); | |
| const modalTitle = document.getElementById('modalTitle'); | |
| const recordId = document.getElementById('recordId'); | |
| const nameInput = document.getElementById('name'); | |
| const emailInput = document.getElementById('email'); | |
| const statusInput = document.getElementById('status'); | |
| // Current record to edit/delete | |
| let currentRecord = null; | |
| // Render table | |
| function renderTable() { | |
| dataTable.innerHTML = ''; | |
| records.forEach(record => { | |
| const row = document.createElement('tr'); | |
| row.className = 'hover:bg-gray-50'; | |
| row.innerHTML = ` | |
| <td class="px-6 py-4 whitespace-nowrap text-sm font-medium text-gray-900">${record.id}</td> | |
| <td class="px-6 py-4 whitespace-nowrap text-sm text-gray-500">${record.name}</td> | |
| <td class="px-6 py-4 whitespace-nowrap text-sm text-gray-500">${record.email}</td> | |
| <td class="px-6 py-4 whitespace-nowrap"> | |
| <span class="px-2 inline-flex text-xs leading-5 font-semibold rounded-full | |
| ${record.status === 'active' ? 'bg-green-100 text-green-800' : | |
| record.status === 'inactive' ? 'bg-red-100 text-red-800' : | |
| 'bg-yellow-100 text-yellow-800'}"> | |
| ${record.status.charAt(0).toUpperCase() + record.status.slice(1)} | |
| </span> | |
| </td> | |
| <td class="px-6 py-4 whitespace-nowrap text-sm text-gray-500"> | |
| <button class="text-indigo-600 hover:text-indigo-900 mr-3 edit-btn" data-id="${record.id}"> | |
| <i data-feather="edit" class="w-4 h-4"></i> | |
| </button> | |
| <button class="text-red-600 hover:text-red-900 delete-btn" data-id="${record.id}"> | |
| <i data-feather="trash-2" class="w-4 h-4"></i> | |
| </button> | |
| </td> | |
| `; | |
| dataTable.appendChild(row); | |
| }); | |
| feather.replace(); | |
| addEventListeners(); | |
| } | |
| // Add event listeners to buttons | |
| function addEventListeners() { | |
| document.querySelectorAll('.edit-btn').forEach(btn => { | |
| btn.addEventListener('click', (e) => { | |
| const id = parseInt(e.currentTarget.getAttribute('data-id')); | |
| currentRecord = records.find(record => record.id === id); | |
| if (currentRecord) { | |
| modalTitle.textContent = 'Edit Record'; | |
| recordId.value = currentRecord.id; | |
| nameInput.value = currentRecord.name; | |
| emailInput.value = currentRecord.email; | |
| statusInput.value = currentRecord.status; | |
| crudModal.classList.remove('hidden'); | |
| } | |
| }); | |
| }); | |
| document.querySelectorAll('.delete-btn').forEach(btn => { | |
| btn.addEventListener('click', (e) => { | |
| const id = parseInt(e.currentTarget.getAttribute('data-id')); | |
| currentRecord = records.find(record => record.id === id); | |
| if (currentRecord) { | |
| deleteModal.classList.remove('hidden'); | |
| } | |
| }); | |
| }); | |
| } | |
| // Event listeners | |
| addBtn.addEventListener('click', () => { | |
| modalTitle.textContent = 'Add New Record'; | |
| recordId.value = ''; | |
| nameInput.value = ''; | |
| emailInput.value = ''; | |
| statusInput.value = 'active'; | |
| crudModal.classList.remove('hidden'); | |
| }); | |
| closeModal.addEventListener('click', () => { | |
| crudModal.classList.add('hidden'); | |
| }); | |
| cancelBtn.addEventListener('click', () => { | |
| crudModal.classList.add('hidden'); | |
| }); | |
| cancelDelete.addEventListener('click', () => { | |
| deleteModal.classList.add('hidden'); | |
| }); | |
| saveBtn.addEventListener('click', () => { | |
| const id = recordId.value ? parseInt(recordId.value) : records.length > 0 ? Math.max(...records.map(r => r.id)) + 1 : 1; | |
| const name = nameInput.value; | |
| const email = emailInput.value; | |
| const status = statusInput.value; | |
| if (!name || !email) { | |
| alert('Please fill in all fields'); | |
| return; | |
| } | |
| if (recordId.value) { | |
| // Update existing record | |
| const index = records.findIndex(r => r.id === id); | |
| if (index !== -1) { | |
| records[index] = { id, name, email, status }; | |
| } | |
| } else { | |
| // Add new record | |
| records.push({ id, name, email, status }); | |
| } | |
| renderTable(); | |
| crudModal.classList.add('hidden'); | |
| }); | |
| confirmDelete.addEventListener('click', () => { | |
| if (currentRecord) { | |
| records = records.filter(record => record.id !== currentRecord.id); | |
| renderTable(); | |
| deleteModal.classList.add('hidden'); | |
| currentRecord = null; | |
| } | |
| }); | |
| // Initial render | |
| renderTable(); | |
| feather.replace(); | |
| </script> | |
| </body> | |
| </html> | |