Update index.html

mobile view
This commit is contained in:
quantumtaskai 2025-06-16 00:03:26 +05:30 committed by GitHub
parent 50c4384add
commit c82904860b
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194

View File

@ -54,6 +54,24 @@
70% { transform: scale(1); box-shadow: 0 0 0 10px rgba(16, 185, 129, 0); }
100% { transform: scale(0.95); box-shadow: 0 0 0 0 rgba(16, 185, 129, 0); }
}
@keyframes slideInRight {
from { transform: translateX(100%); opacity: 0; }
to { transform: translateX(0); opacity: 1; }
}
@keyframes slideOutRight {
from { transform: translateX(0); opacity: 1; }
to { transform: translateX(100%); opacity: 0; }
}
.toast-enter {
animation: slideInRight 0.3s ease-out;
}
.toast-exit {
animation: slideOutRight 0.3s ease-in;
}
</style>
</head>
<body>
@ -114,11 +132,12 @@
</div>
<div class="flex gap-2 sm:gap-3">
<button
id="searchButton"
onclick="performSearch()"
class="flex-1 sm:w-auto bg-blue-600 hover:bg-blue-700 text-white px-3 py-2.5 sm:px-6 sm:py-3 rounded-xl font-semibold transition-all flex items-center justify-center gap-1 sm:gap-2 min-w-[90px] sm:min-w-[120px] text-sm sm:text-base"
>
<i class="fas fa-search text-xs sm:text-sm"></i>
<span>Search</span>
<i id="searchIcon" class="fas fa-search text-xs sm:text-sm"></i>
<span id="searchText">Search</span>
</button>
<button
onclick="openCreateModal()"
@ -155,11 +174,11 @@
<i class="fas fa-sliders-h text-gray-600"></i>
<h3 class="text-sm font-semibold text-gray-700">Product Mapping</h3>
<button onclick="toggleAdvancedPanel()" class="sm:hidden ml-auto p-1 text-gray-500 hover:text-gray-700">
<i id="advancedToggleIcon" class="fas fa-chevron-down text-xs"></i>
<i id="advancedToggleIcon" class="fas fa-chevron-right text-xs"></i>
</button>
</div>
<div id="advancedContent" class="grid grid-cols-1 sm:grid-cols-2 lg:grid-cols-3 gap-3 sm:gap-4 mb-4">
<div id="advancedContent" class="hidden sm:grid grid-cols-1 sm:grid-cols-2 lg:grid-cols-3 gap-3 sm:gap-4 mb-4">
<!-- Search Type -->
<div>
<label class="block text-xs sm:text-sm font-medium text-gray-700 mb-1 sm:mb-2">Search Type</label>
@ -200,7 +219,7 @@
</div>
<!-- Advanced Search Actions -->
<div id="advancedActions" class="flex gap-2 sm:gap-3">
<div id="advancedActions" class="hidden sm:flex gap-2 sm:gap-3">
<button
onclick="performProductMatching()"
class="flex-auto bg-purple-600 hover:bg-purple-700 text-white px-4 py-2 sm:px-6 sm:py-2.5 rounded-lg font-semibold transition-all flex items-center justify-center gap-2 text-sm sm:text-base"
@ -288,7 +307,7 @@
<p id="resultsCount" class="text-sm text-gray-600"></p>
</div>
<div id="resultsContainer" class="grid grid-cols-1 sm:grid-cols-2 lg:grid-cols-3 gap-3 sm:gap-4 lg:gap-6 custom-scrollbar max-h-[600px] overflow-y-auto"></div>
<div id="resultsContainer" class="grid grid-cols-1 lg:grid-cols-2 gap-3 sm:gap-4 lg:gap-6 custom-scrollbar max-h-[600px] overflow-y-auto"></div>
</div>
</div>
@ -304,6 +323,11 @@
</div>
</div>
<!-- Toast Notification Container -->
<div id="toastContainer" class="fixed top-4 right-4 z-50 space-y-2">
<!-- Toast notifications will be dynamically added here -->
</div>
<!-- Edit Modal -->
<div id="editModal" class="fixed inset-0 bg-black bg-opacity-50 hidden flex items-center justify-center z-50">
<div class="bg-white rounded-2xl p-4 max-w-sm w-full mx-4 shadow-2xl max-h-[90vh] overflow-y-auto">
@ -419,6 +443,93 @@ function clearCache() {
queryCache.clear();
}
function setSearchButtonLoading(isLoading) {
const button = document.getElementById('searchButton');
const icon = document.getElementById('searchIcon');
const text = document.getElementById('searchText');
if (isLoading) {
button.disabled = true;
button.classList.add('opacity-75', 'cursor-not-allowed');
button.classList.remove('hover:bg-blue-700');
icon.className = 'fas fa-spinner fa-spin text-xs sm:text-sm';
text.textContent = 'Searching...';
} else {
button.disabled = false;
button.classList.remove('opacity-75', 'cursor-not-allowed');
button.classList.add('hover:bg-blue-700');
icon.className = 'fas fa-search text-xs sm:text-sm';
text.textContent = 'Search';
}
}
function showToast(message, type = 'success', duration = 4000) {
const container = document.getElementById('toastContainer');
const toastId = 'toast-' + Date.now();
// Determine colors and icon based on type
const config = {
success: {
bgColor: 'bg-green-500',
icon: 'fas fa-check-circle',
borderColor: 'border-green-400'
},
error: {
bgColor: 'bg-red-500',
icon: 'fas fa-exclamation-circle',
borderColor: 'border-red-400'
},
warning: {
bgColor: 'bg-yellow-500',
icon: 'fas fa-exclamation-triangle',
borderColor: 'border-yellow-400'
},
info: {
bgColor: 'bg-blue-500',
icon: 'fas fa-info-circle',
borderColor: 'border-blue-400'
}
};
const { bgColor, icon, borderColor } = config[type] || config.success;
// Create toast element
const toast = document.createElement('div');
toast.id = toastId;
toast.className = `${bgColor} text-white px-4 py-3 rounded-lg shadow-lg flex items-center gap-3 min-w-[320px] max-w-md border-l-4 ${borderColor} toast-enter`;
toast.innerHTML = `
<i class="${icon} text-lg"></i>
<span class="flex-1 text-sm font-medium">${message}</span>
<button onclick="removeToast('${toastId}')" class="text-white hover:text-gray-200 transition-colors">
<i class="fas fa-times text-sm"></i>
</button>
`;
// Add to container
container.appendChild(toast);
// Auto remove after duration
setTimeout(() => {
removeToast(toastId);
}, duration);
}
function removeToast(toastId) {
const toast = document.getElementById(toastId);
if (toast) {
toast.classList.remove('toast-enter');
toast.classList.add('toast-exit');
// Remove from DOM after animation
setTimeout(() => {
if (toast.parentNode) {
toast.parentNode.removeChild(toast);
}
}, 300);
}
}
function openModal(id) {
document.getElementById(id).classList.remove("hidden");
document.body.style.overflow = 'hidden';
@ -429,6 +540,31 @@ function closeModal(id) {
document.body.style.overflow = 'auto';
}
function toggleAdvancedPanel() {
const content = document.getElementById('advancedContent');
const actions = document.getElementById('advancedActions');
const icon = document.getElementById('advancedToggleIcon');
// Check if currently hidden (using Tailwind's hidden class)
if (content.classList.contains('hidden')) {
// Show content
content.classList.remove('hidden');
content.classList.add('grid');
actions.classList.remove('hidden');
actions.classList.add('flex');
icon.classList.remove('fa-chevron-right');
icon.classList.add('fa-chevron-down');
} else {
// Hide content
content.classList.remove('grid');
content.classList.add('hidden');
actions.classList.remove('flex');
actions.classList.add('hidden');
icon.classList.remove('fa-chevron-down');
icon.classList.add('fa-chevron-right');
}
}
function setSearchQuery(query) {
document.getElementById('searchInput').value = query;
performSearch();
@ -462,11 +598,11 @@ async function submitModalEdit() {
const { error } = await client.from(editContext.table).update(update).eq("id", editContext.id);
if (error) {
console.error('Update error:', error);
alert("❌ Update failed: " + error.message);
showToast("Update failed: " + error.message, 'error');
return;
}
alert("✅ Updated!");
showToast("Record updated successfully!", 'success');
clearCache(); // Clear cache after data modification
closeModal("editModal");
@ -476,7 +612,7 @@ async function submitModalEdit() {
}
} catch (error) {
console.error('Submit edit error:', error);
alert("❌ Update failed: " + error.message);
showToast("Update failed: " + error.message, 'error');
}
}
@ -537,8 +673,8 @@ function renderRecord(r) {
<div class='mb-2 sm:mb-3'>
<span class='inline-flex items-center gap-1 px-1.5 py-0.5 sm:px-2 sm:py-1 rounded-full text-xs font-medium ${badge.color}'>
<i class='${badge.icon} text-xs'></i>
<span class='hidden xs:inline'>${badge.label}</span>
<span class='xs:hidden'>${badge.label.slice(0,3)}</span>
<span class='hidden sm:inline'>${badge.label}</span>
<span class='sm:hidden'>${badge.label.slice(0,3).toUpperCase()}</span>
</span>
</div>
@ -768,6 +904,11 @@ async function performSearch(loadMore = false) {
return;
}
// Show loading state on search button
if (!loadMore) {
setSearchButtonLoading(true);
}
// Reset pagination if new search
if (!loadMore || searchState.query !== query) {
searchState = { query, page: 0, hasMore: false, results: [] };
@ -850,6 +991,11 @@ async function performSearch(loadMore = false) {
} catch (e) {
console.error('Search error:', e);
showErrorMessage(`Search failed: ${e.message}`);
} finally {
// Hide loading state on search button
if (!loadMore) {
setSearchButtonLoading(false);
}
}
}
@ -1295,7 +1441,7 @@ function sortResults() {
function exportResults() {
if (!searchState.results || searchState.results.length === 0) {
alert("No results to export");
showToast("No results to export", 'warning');
return;
}
@ -1326,9 +1472,11 @@ function exportResults() {
a.click();
document.body.removeChild(a);
window.URL.revokeObjectURL(url);
showToast(`Exported ${searchState.results.length} records successfully`, 'success');
} catch (error) {
console.error('Export error:', error);
alert('Export failed: ' + error.message);
showToast('Export failed: ' + error.message, 'error');
}
}
@ -1379,7 +1527,7 @@ async function deleteRecord(btn) {
if (!table || !id) {
console.error('Missing table or id data');
alert("❌ Could not delete: missing record information");
showToast("Could not delete: missing record information", 'error');
return;
}
@ -1388,11 +1536,11 @@ async function deleteRecord(btn) {
const { error } = await client.from(table).delete().eq("id", id);
if (error) {
console.error('Delete error:', error);
alert("❌ Delete failed: " + error.message);
showToast("Delete failed: " + error.message, 'error');
return;
}
alert("🗑️ Deleted");
showToast("Record deleted successfully", 'success');
clearCache(); // Clear cache after data modification
// Refresh current search if one exists
@ -1401,7 +1549,7 @@ async function deleteRecord(btn) {
}
} catch (error) {
console.error('Delete record error:', error);
alert("❌ Delete failed: " + error.message);
showToast("Delete failed: " + error.message, 'error');
}
}
@ -1487,12 +1635,12 @@ async function handleCreateSubmit(event) {
const { data: result, error } = await client.from(table).insert([data]);
if (error) {
console.error('Create error details:', error);
alert("❌ Create failed: " + (error.message || JSON.stringify(error)));
showToast("Create failed: " + (error.message || JSON.stringify(error)), 'error');
return;
}
console.log('Insert successful:', result);
alert("✅ Record created successfully!");
showToast("Record created successfully!", 'success');
clearCache();
closeCreateModal();
@ -1502,7 +1650,7 @@ async function handleCreateSubmit(event) {
}
} catch (error) {
console.error('Create record error:', error);
alert("❌ Create failed: " + (error.message || JSON.stringify(error)));
showToast("Create failed: " + (error.message || JSON.stringify(error)), 'error');
}
}
@ -1575,4 +1723,4 @@ input:focus, select:focus, button:focus {
</body>
</html>
</html>