Update Mobile index.html

This commit is contained in:
thecyberlearn 2025-06-02 21:43:39 +05:30 committed by GitHub
parent a00232b581
commit 4fd82179a2
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194

View File

@ -13,7 +13,6 @@
} }
</style> </style>
<link href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.0.0/css/all.min.css" rel="stylesheet"> <link href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.0.0/css/all.min.css" rel="stylesheet">
<style> <style>
@import url('https://fonts.googleapis.com/css2?family=Inter:wght@300;400;500;600;700&display=swap'); @import url('https://fonts.googleapis.com/css2?family=Inter:wght@300;400;500;600;700&display=swap');
@ -131,6 +130,35 @@ html {
cursor: pointer; cursor: pointer;
} }
/* Mobile-specific styles */
@media (max-width: 768px) {
.mobile-stats-grid {
display: grid;
grid-template-columns: repeat(4, 1fr);
gap: 0.5rem;
}
}
/* Collapsible styles */
.collapsible-content {
max-height: 0;
overflow: hidden;
transition: max-height 0.3s ease;
}
.collapsible-content.open {
max-height: 200px;
}
@keyframes slideDown {
from { opacity: 0; transform: translateY(-10px); }
to { opacity: 1; transform: translateY(0); }
}
.slide-down {
animation: slideDown 0.3s ease-out;
}
</style> </style>
<style> <style>
@ -272,39 +300,115 @@ html {
</div> </div>
</div> </div>
<!-- Mobile Icon Cards (visible only on mobile) -->
<!-- CRUD Command Section --> <div class="lg:hidden mb-6">
<div class="bg-white rounded-2xl shadow-sm border border-gray-200 p-4 lg:p-6 mb-6 fade-up"> <div class="mobile-stats-grid">
<h2 class="text-lg font-bold text-gray-800 mb-3">🔧 Create / Update / Delete</h2> <div class="bg-white rounded-xl p-3 shadow-sm border border-gray-200 text-center">
<div class="flex flex-col sm:flex-row gap-3"> <div class="w-8 h-8 bg-blue-100 rounded-lg flex items-center justify-center mx-auto mb-2">
<input <i class="fas fa-search text-blue-600"></i>
id="crudCommand" </div>
type="text" <p class="text-xs text-gray-500 mb-1">Total Searches</p>
placeholder="e.g., Create importer Indi Tech from India email info@inditech.in" <p class="text-sm font-bold text-gray-800" id="mobileSearches">0</p>
class="w-full px-4 py-3 rounded-xl border border-gray-300 focus:border-purple-500 focus:ring-2 focus:ring-purple-200 outline-none transition-all search-input text-sm lg:text-base" </div>
/>
<button <div class="bg-white rounded-xl p-3 shadow-sm border border-gray-200 text-center">
onclick="window.callCrudOperation()" <div class="w-8 h-8 bg-green-100 rounded-lg flex items-center justify-center mx-auto mb-2">
class="w-full sm:w-auto bg-purple-600 hover:bg-purple-700 text-white px-6 py-3 rounded-xl font-semibold transition-all flex items-center justify-center gap-2 min-w-[120px]" <i class="fas fa-chart-line text-green-600"></i>
id="crudBtn" </div>
> <p class="text-xs text-gray-500 mb-1">Results Found</p>
<i class="fas fa-terminal"></i> <p class="text-sm font-bold text-gray-800" id="mobileResults">0</p>
<span>Execute</span> </div>
</button>
</div> <div class="bg-white rounded-xl p-3 shadow-sm border border-gray-200 text-center">
</div> <div class="w-8 h-8 bg-purple-100 rounded-lg flex items-center justify-center mx-auto mb-2">
<i class="fas fa-globe text-purple-600"></i>
</div>
<p class="text-xs text-gray-500 mb-1">Countries</p>
<p class="text-sm font-bold text-gray-800">150+</p>
</div>
<div class="bg-white rounded-xl p-3 shadow-sm border border-gray-200 text-center">
<div class="w-8 h-8 bg-orange-100 rounded-lg flex items-center justify-center mx-auto mb-2">
<i class="fas fa-bolt text-orange-600"></i>
</div>
<p class="text-xs text-gray-500 mb-1">Response Time</p>
<p class="text-sm font-bold text-gray-800" id="mobileResponseTime">0ms</p>
</div>
</div>
<!-- Mobile Collapsible Execute Section -->
<div class="mt-4 bg-white rounded-xl shadow-sm border border-gray-200">
<button
id="mobileExecuteToggle"
onclick="toggleMobileExecute()"
class="w-full p-4 flex items-center justify-between text-left"
>
<div class="flex items-center gap-3">
<div class="w-10 h-10 bg-purple-100 rounded-lg flex items-center justify-center">
<i class="fas fa-terminal text-purple-600 text-lg"></i>
</div>
<div>
<h3 class="font-semibold text-gray-800">Create / Update / Delete</h3>
<p class="text-xs text-gray-500">Execute CRUD operations</p>
</div>
</div>
<i class="fas fa-chevron-down text-gray-400 transition-transform" id="executeChevron"></i>
</button>
<div id="mobileExecuteContent" class="collapsible-content">
<div class="px-4 pb-4 slide-down">
<div class="flex flex-col gap-3">
<input
id="mobileCrudCommand"
type="text"
placeholder="e.g., Create importer Indi Tech from India email info@inditech.in"
class="w-full px-4 py-3 rounded-xl border border-gray-300 focus:border-purple-500 focus:ring-2 focus:ring-purple-200 outline-none transition-all search-input text-sm"
/>
<button
onclick="window.callMobileCrudOperation()"
class="w-full bg-purple-600 hover:bg-purple-700 text-white px-6 py-3 rounded-xl font-semibold transition-all flex items-center justify-center gap-2"
id="mobileCrudBtn"
>
<i class="fas fa-terminal"></i>
<span>Execute</span>
</button>
</div>
</div>
</div>
</div>
</div>
<!-- Desktop CRUD Command Section (hidden on mobile) -->
<div class="hidden lg:block bg-white rounded-2xl shadow-sm border border-gray-200 p-4 lg:p-6 mb-6 fade-up">
<h2 class="text-lg font-bold text-gray-800 mb-3">🔧 Create / Update / Delete</h2>
<div class="flex flex-col sm:flex-row gap-3">
<input
id="crudCommand"
type="text"
placeholder="e.g., Create importer Indi Tech from India email info@inditech.in"
class="w-full px-4 py-3 rounded-xl border border-gray-300 focus:border-purple-500 focus:ring-2 focus:ring-purple-200 outline-none transition-all search-input text-sm lg:text-base"
/>
<button
onclick="window.callCrudOperation()"
class="w-full sm:w-auto bg-purple-600 hover:bg-purple-700 text-white px-6 py-3 rounded-xl font-semibold transition-all flex items-center justify-center gap-2 min-w-[120px]"
id="crudBtn"
>
<i class="fas fa-terminal"></i>
<span>Execute</span>
</button>
</div>
</div>
<!-- CRUD Result Section --> <!-- CRUD Result Section -->
<div id="crudResultSection" class="hidden mb-6 fade-up"> <div id="crudResultSection" class="hidden mb-6 fade-up">
<div class="bg-white rounded-xl border border-gray-200 p-4"> <div class="bg-white rounded-xl border border-gray-200 p-4">
<h3 class="text-sm font-semibold text-gray-700 mb-3">📝 Operation Result</h3> <h3 class="text-sm font-semibold text-gray-700 mb-3">📝 Operation Result</h3>
<div id="crudResultHtml" class="text-sm text-gray-800 leading-relaxed"></div> <div id="crudResultHtml" class="text-sm text-gray-800 leading-relaxed"></div>
</div> </div>
</div> </div>
<!-- Stats Cards --> <!-- Desktop Stats Cards (hidden on mobile) -->
<div class="grid grid-cols-1 sm:grid-cols-2 lg:grid-cols-4 gap-4 lg:gap-6 mb-6 lg:mb-8"> <div class="hidden lg:grid grid-cols-1 sm:grid-cols-2 lg:grid-cols-4 gap-4 lg:gap-6 mb-6 lg:mb-8">
<div class="bg-white rounded-xl p-4 lg:p-6 shadow-sm border border-gray-200"> <div class="bg-white rounded-xl p-4 lg:p-6 shadow-sm border border-gray-200">
<div class="flex items-center justify-between"> <div class="flex items-center justify-between">
<div> <div>
@ -413,6 +517,20 @@ html {
let totalResultCount = 0; let totalResultCount = 0;
let currentSearchData = []; let currentSearchData = [];
// Mobile execute toggle function
function toggleMobileExecute() {
const content = document.getElementById('mobileExecuteContent');
const chevron = document.getElementById('executeChevron');
if (content.classList.contains('open')) {
content.classList.remove('open');
chevron.style.transform = 'rotate(0deg)';
} else {
content.classList.add('open');
chevron.style.transform = 'rotate(180deg)';
}
}
// Enhanced search function with better error handling // Enhanced search function with better error handling
async function callWorkflow() { async function callWorkflow() {
const query = document.getElementById('query').value.trim(); const query = document.getElementById('query').value.trim();
@ -780,15 +898,21 @@ html {
`; `;
} }
// Update stats // Update stats for both desktop and mobile
function updateStats(resultCount, responseTime) { function updateStats(resultCount, responseTime) {
totalSearchCount++; totalSearchCount++;
totalResultCount += resultCount; totalResultCount += resultCount;
// Desktop stats
document.getElementById('totalSearches').textContent = totalSearchCount; document.getElementById('totalSearches').textContent = totalSearchCount;
document.getElementById('totalResults').textContent = totalResultCount; document.getElementById('totalResults').textContent = totalResultCount;
document.getElementById('responseTime').textContent = responseTime + 'ms'; document.getElementById('responseTime').textContent = responseTime + 'ms';
// Mobile stats
document.getElementById('mobileSearches').textContent = totalSearchCount;
document.getElementById('mobileResults').textContent = totalResultCount;
document.getElementById('mobileResponseTime').textContent = responseTime + 'ms';
updateLastUpdated(); updateLastUpdated();
} }
@ -872,423 +996,180 @@ html {
} }
} }
// Desktop CRUD operation
async function callCrudOperation() { async function callCrudOperation() {
const query = document.getElementById('crudCommand').value.trim(); const queryInput = document.getElementById('crudCommand');
if (!query) { const query = queryInput.value.trim();
alert('Please enter a CRUD command'); if (!query) {
return; alert('Please enter a CRUD command');
} return;
}
const crudBtn = document.getElementById('crudBtn'); const crudBtn = document.getElementById('crudBtn');
crudBtn.innerHTML = '<i class="fas fa-spinner fa-spin"></i> Executing...'; crudBtn.innerHTML = '<i class="fas fa-spinner fa-spin"></i> Executing...';
crudBtn.disabled = true; crudBtn.disabled = true;
try { try {
const response = await fetch(`https://thecyberlearn.app.n8n.cloud/webhook/search-airtable?query=${encodeURIComponent(query)}`, { const response = await fetch(`https://thecyberlearn.app.n8n.cloud/webhook/search-airtable?query=${encodeURIComponent(query)}`, {
method: 'GET', method: 'GET',
headers: { 'Accept': 'application/json' } headers: { 'Accept': 'application/json' }
}); });
if (!response.ok) { if (!response.ok) {
throw new Error(`HTTP ${response.status}: ${response.statusText}`); throw new Error(`HTTP ${response.status}: ${response.statusText}`);
} }
const result = await response.json(); const result = await response.json();
alert(result.message || 'Operation completed'); const raw = result?.content?.originalMessage || result?.content?.message || '✅ Operation completed.';
} catch (error) { const clean = raw
console.error('CRUD error:', error); .replace(/<[^>]*>/g, '')
alert('Failed to execute command: ' + error.message); .replace(/\*\*/g, '')
} finally { .replace(/Complete dataset returned.*included/gi, '')
crudBtn.innerHTML = '<i class="fas fa-terminal"></i> <span>Execute</span>'; .trim();
crudBtn.disabled = false;
}
}
const operation = result?.operation || 'info';
const type = result?.content?.messageType || 'success';
const success = result?.success;
// Determine toast style
async function callCrudOperation() { let bg = 'bg-emerald-100', border = 'border-emerald-300', text = 'text-emerald-900', icon = 'fa-check-circle', title = 'Success!';
const query = document.getElementById('crudCommand').value.trim();
if (!query) {
alert('Please enter a CRUD command');
return;
}
const crudBtn = document.getElementById('crudBtn'); if (/cannot create/i.test(clean)) {
crudBtn.innerHTML = '<i class="fas fa-spinner fa-spin"></i> Executing...'; bg = 'bg-yellow-50'; border = 'border-yellow-200'; text = 'text-yellow-800'; icon = 'fa-clone'; title = 'Duplicate Found';
crudBtn.disabled = true; } else if (!success && (operation === 'delete' || type === 'error')) {
bg = 'bg-red-100'; border = 'border-red-300'; text = 'text-red-800'; icon = 'fa-times-circle'; title = 'Error!';
} else if (operation === 'delete' && success) {
bg = 'bg-rose-100'; border = 'border-rose-300'; text = 'text-rose-800'; icon = 'fa-trash-alt'; title = 'Deleted!';
} else if (operation === 'update') {
bg = 'bg-amber-100'; border = 'border-amber-300'; text = 'text-amber-900'; icon = 'fa-pen'; title = 'Updated!';
} else if (operation === 'read') {
bg = 'bg-blue-100'; border = 'border-blue-300'; text = 'text-blue-900'; icon = 'fa-info-circle'; title = 'Fetched!';
}
try { // Build toast element
const response = await fetch(`https://thecyberlearn.app.n8n.cloud/webhook/search-airtable?query=${encodeURIComponent(query)}`, { const toast = document.createElement('div');
method: 'GET', toast.className = `fade-up fixed bottom-6 right-6 max-w-sm w-full z-50 shadow-xl rounded-xl border px-4 py-3 ${bg} ${border} ${text} animate-slide-up`;
headers: { 'Accept': 'application/json' } toast.innerHTML = `
}); <div class="flex justify-between items-start gap-3">
<div class="flex items-start gap-2">
if (!response.ok) { <i class="fas ${icon} mt-1 text-lg"></i>
throw new Error(`HTTP ${response.status}: ${response.statusText}`); <div>
} <div class="font-semibold">${title}</div>
<div class="mt-1 space-y-1 text-sm leading-snug">${clean.split('\n').map(line => `<div class="animate-fade">${line}</div>`).join('')}</div>
const result = await response.json(); </div>
const htmlContent = result?.content?.message || '✅ Operation completed.'; </div>
document.getElementById('crudResultHtml').innerHTML = htmlContent; <button onclick="this.parentElement.parentElement.remove()" class="text-xl font-bold hover:text-black/50 transition">×</button>
document.getElementById('crudResultSection').classList.remove('hidden');
} catch (error) {
console.error('CRUD error:', error);
document.getElementById('crudResultHtml').innerHTML = '<div class="text-red-600">❌ Failed to execute: ' + error.message + '</div>';
document.getElementById('crudResultSection').classList.remove('hidden');
} finally {
crudBtn.innerHTML = '<i class="fas fa-terminal"></i> <span>Execute</span>';
crudBtn.disabled = false;
}
}
async function callCrudOperation() {
const query = document.getElementById('crudCommand').value.trim();
if (!query) {
alert('Please enter a CRUD command');
return;
}
const crudBtn = document.getElementById('crudBtn');
crudBtn.innerHTML = '<i class="fas fa-spinner fa-spin"></i> Executing...';
crudBtn.disabled = true;
try {
const response = await fetch(`https://thecyberlearn.app.n8n.cloud/webhook/search-airtable?query=${encodeURIComponent(query)}`, {
method: 'GET',
headers: { 'Accept': 'application/json' }
});
if (!response.ok) {
throw new Error(`HTTP ${response.status}: ${response.statusText}`);
}
const result = await response.json();
const raw = result?.content?.originalMessage || result?.content?.message || '✅ Operation completed.';
const clean = raw.replace(/<[^>]*>/g, '').replace(/\*\*/g, '');
document.getElementById('crudResultHtml').innerHTML = `
<div class="fade-up space-y-4 text-sm leading-relaxed text-gray-800">
<div class="bg-white border border-gray-200 shadow-md p-4 rounded-xl">
<div class="text-green-600 font-semibold text-base mb-2 flex items-center gap-2">
<i class="fas fa-check-circle"></i> Operation Successful
</div> </div>
<pre class="whitespace-pre-wrap text-gray-700">${clean}</pre> `;
</div>
</div>
`;
document.getElementById('crudResultSection').classList.remove('hidden');
} catch (error) {
console.error('CRUD error:', error);
document.getElementById('crudResultHtml').innerHTML = `
<div class="bg-red-100 border border-red-300 text-red-800 px-4 py-3 rounded-lg">
❌ Failed to execute: ${error.message}
</div>`;
document.getElementById('crudResultSection').classList.remove('hidden');
} finally {
crudBtn.innerHTML = '<i class="fas fa-terminal"></i> <span>Execute</span>';
crudBtn.disabled = false;
}
}
document.body.appendChild(toast);
queryInput.value = '';
async function callCrudOperation() { setTimeout(() => toast.remove(), 8000);
const query = document.getElementById('crudCommand').value.trim(); } catch (error) {
if (!query) { console.error('CRUD error:', error);
alert('Please enter a CRUD command'); alert('❌ ' + error.message);
return; } finally {
} crudBtn.innerHTML = '<i class="fas fa-terminal"></i> <span>Execute</span>';
crudBtn.disabled = false;
const crudBtn = document.getElementById('crudBtn'); }
crudBtn.innerHTML = '<i class="fas fa-spinner fa-spin"></i> Executing...';
crudBtn.disabled = true;
try {
const response = await fetch(`https://thecyberlearn.app.n8n.cloud/webhook/search-airtable?query=${encodeURIComponent(query)}`, {
method: 'GET',
headers: { 'Accept': 'application/json' }
});
if (!response.ok) {
throw new Error(`HTTP ${response.status}: ${response.statusText}`);
} }
const result = await response.json(); // Mobile CRUD operation
const raw = result?.content?.originalMessage || result?.content?.message || '✅ Operation completed.'; async function callMobileCrudOperation() {
const clean = raw.replace(/<[^>]*>/g, '').replace(/\*\*/g, ''); const queryInput = document.getElementById('mobileCrudCommand');
const query = queryInput.value.trim();
if (!query) {
alert('Please enter a CRUD command');
return;
}
const operation = result?.operation || 'info'; const crudBtn = document.getElementById('mobileCrudBtn');
const type = result?.content?.messageType || 'success'; crudBtn.innerHTML = '<i class="fas fa-spinner fa-spin"></i> Executing...';
crudBtn.disabled = true;
let bg = 'bg-emerald-50', border = 'border-emerald-200', text = 'text-emerald-800', icon = 'fa-check-circle', title = 'Success!'; try {
if (operation === 'delete' || type === 'error') { const response = await fetch(`https://thecyberlearn.app.n8n.cloud/webhook/search-airtable?query=${encodeURIComponent(query)}`, {
bg = 'bg-red-50'; border = 'border-red-200'; text = 'text-red-800'; icon = 'fa-times-circle'; title = 'Error!'; method: 'GET',
} else if (operation === 'update') { headers: { 'Accept': 'application/json' }
bg = 'bg-amber-50'; border = 'border-amber-200'; text = 'text-amber-800'; icon = 'fa-exclamation-circle'; title = 'Updated!'; });
} else if (operation === 'read') {
bg = 'bg-blue-50'; border = 'border-blue-200'; text = 'text-blue-800'; icon = 'fa-info-circle'; title = 'Fetched!';
}
document.getElementById('crudResultHtml').innerHTML = ` if (!response.ok) {
<div class="fade-up flex items-start gap-3 p-4 rounded-xl border shadow-sm text-sm leading-6 ${bg} ${border} ${text}"> throw new Error(`HTTP ${response.status}: ${response.statusText}`);
<i class="fas ${icon} mt-1 text-lg"></i> }
<div>
<div class="font-semibold">${title}</div>
<div class="whitespace-pre-wrap">${clean}</div>
</div>
</div>
`;
document.getElementById('crudResultSection').classList.remove('hidden');
} catch (error) {
console.error('CRUD error:', error);
document.getElementById('crudResultHtml').innerHTML = `
<div class="bg-red-100 border border-red-300 text-red-800 px-4 py-3 rounded-lg">
❌ Failed to execute: ${error.message}
</div>`;
document.getElementById('crudResultSection').classList.remove('hidden');
} finally {
crudBtn.innerHTML = '<i class="fas fa-terminal"></i> <span>Execute</span>';
crudBtn.disabled = false;
}
}
const result = await response.json();
const raw = result?.content?.originalMessage || result?.content?.message || '✅ Operation completed.';
const clean = raw
.replace(/<[^>]*>/g, '')
.replace(/\*\*/g, '')
.replace(/Complete dataset returned.*included/gi, '')
.trim();
const operation = result?.operation || 'info';
async function callCrudOperation() { const type = result?.content?.messageType || 'success';
const query = document.getElementById('crudCommand').value.trim(); const success = result?.success;
if (!query) {
alert('Please enter a CRUD command');
return;
}
const crudBtn = document.getElementById('crudBtn'); // Determine toast style
crudBtn.innerHTML = '<i class="fas fa-spinner fa-spin"></i> Executing...'; let bg = 'bg-emerald-100', border = 'border-emerald-300', text = 'text-emerald-900', icon = 'fa-check-circle', title = 'Success!';
crudBtn.disabled = true;
try { if (/cannot create/i.test(clean)) {
const response = await fetch(`https://thecyberlearn.app.n8n.cloud/webhook/search-airtable?query=${encodeURIComponent(query)}`, { bg = 'bg-yellow-50'; border = 'border-yellow-200'; text = 'text-yellow-800'; icon = 'fa-clone'; title = 'Duplicate Found';
method: 'GET', } else if (!success && (operation === 'delete' || type === 'error')) {
headers: { 'Accept': 'application/json' } bg = 'bg-red-100'; border = 'border-red-300'; text = 'text-red-800'; icon = 'fa-times-circle'; title = 'Error!';
}); } else if (operation === 'delete' && success) {
bg = 'bg-rose-100'; border = 'border-rose-300'; text = 'text-rose-800'; icon = 'fa-trash-alt'; title = 'Deleted!';
} else if (operation === 'update') {
bg = 'bg-amber-100'; border = 'border-amber-300'; text = 'text-amber-900'; icon = 'fa-pen'; title = 'Updated!';
} else if (operation === 'read') {
bg = 'bg-blue-100'; border = 'border-blue-300'; text = 'text-blue-900'; icon = 'fa-info-circle'; title = 'Fetched!';
}
if (!response.ok) { // Build toast element
throw new Error(`HTTP ${response.status}: ${response.statusText}`); const toast = document.createElement('div');
} toast.className = `fade-up fixed bottom-6 left-4 right-4 max-w-sm mx-auto z-50 shadow-xl rounded-xl border px-4 py-3 ${bg} ${border} ${text} animate-slide-up`;
toast.innerHTML = `
const result = await response.json(); <div class="flex justify-between items-start gap-3">
const raw = result?.content?.originalMessage || result?.content?.message || '✅ Operation completed.'; <div class="flex items-start gap-2">
const clean = raw <i class="fas ${icon} mt-1 text-lg"></i>
.replace(/<[^>]*>/g, '') <div>
.replace(/\*\*/g, '') <div class="font-semibold">${title}</div>
.replace(/Complete dataset returned.*included/gi, '') <div class="mt-1 space-y-1 text-sm leading-snug">${clean.split('\n').map(line => `<div class="animate-fade">${line}</div>`).join('')}</div>
.trim(); </div>
</div>
const operation = result?.operation || 'info'; <button onclick="this.parentElement.parentElement.remove()" class="text-xl font-bold hover:text-black/50 transition">×</button>
const type = result?.content?.messageType || 'success';
let bg = 'bg-emerald-50', border = 'border-emerald-200', text = 'text-emerald-800', icon = 'fa-check-circle', title = 'Success!';
if (operation === 'delete' || type === 'error') {
bg = 'bg-red-50'; border = 'border-red-200'; text = 'text-red-800'; icon = 'fa-times-circle'; title = 'Error!';
} else if (operation === 'update') {
bg = 'bg-amber-50'; border = 'border-amber-200'; text = 'text-amber-800'; icon = 'fa-exclamation-circle'; title = 'Updated!';
} else if (operation === 'read') {
bg = 'bg-blue-50'; border = 'border-blue-200'; text = 'text-blue-800'; icon = 'fa-info-circle'; title = 'Fetched!';
}
// Build toast element
const toast = document.createElement('div');
toast.className = `fade-up fixed bottom-6 right-6 max-w-sm w-full z-50 shadow-xl rounded-xl border px-4 py-3 ${bg} ${border} ${text} animate-slide-up`;
toast.innerHTML = `
<div class="flex justify-between items-start gap-3">
<div class="flex items-start gap-2">
<i class="fas ${icon} mt-1 text-lg"></i>
<div>
<div class="font-semibold">${title}</div>
<div class="mt-1 space-y-1 text-sm leading-snug">${clean.split('\n').map(line => `<div class="animate-fade">${line}</div>`).join('')}</div>
</div> </div>
</div> `;
<button onclick="this.parentElement.parentElement.remove()" class="text-xl font-bold hover:text-black/50 transition">×</button>
</div>
`;
document.body.appendChild(toast); document.body.appendChild(toast);
queryInput.value = '';
setTimeout(() => toast.remove(), 8000); // Auto-dismiss after 8 seconds
} catch (error) { // Auto-collapse the mobile execute section after successful operation
console.error('CRUD error:', error); const content = document.getElementById('mobileExecuteContent');
alert('❌ ' + error.message); const chevron = document.getElementById('executeChevron');
} finally { content.classList.remove('open');
crudBtn.innerHTML = '<i class="fas fa-terminal"></i> <span>Execute</span>'; chevron.style.transform = 'rotate(0deg)';
crudBtn.disabled = false;
} setTimeout(() => toast.remove(), 8000);
} } catch (error) {
console.error('Mobile CRUD error:', error);
alert('❌ ' + error.message);
} finally {
async function callCrudOperation() { crudBtn.innerHTML = '<i class="fas fa-terminal"></i> <span>Execute</span>';
const queryInput = document.getElementById('crudCommand'); crudBtn.disabled = false;
const query = queryInput.value.trim(); }
if (!query) {
alert('Please enter a CRUD command');
return;
}
const crudBtn = document.getElementById('crudBtn');
crudBtn.innerHTML = '<i class="fas fa-spinner fa-spin"></i> Executing...';
crudBtn.disabled = true;
try {
const response = await fetch(`https://thecyberlearn.app.n8n.cloud/webhook/search-airtable?query=${encodeURIComponent(query)}`, {
method: 'GET',
headers: { 'Accept': 'application/json' }
});
if (!response.ok) {
throw new Error(`HTTP ${response.status}: ${response.statusText}`);
} }
const result = await response.json();
const raw = result?.content?.originalMessage || result?.content?.message || '✅ Operation completed.';
const clean = raw
.replace(/<[^>]*>/g, '')
.replace(/\*\*/g, '')
.replace(/Complete dataset returned.*included/gi, '')
.trim();
const operation = result?.operation || 'info';
const type = result?.content?.messageType || 'success';
const success = result?.success;
let bg = 'bg-emerald-100', border = 'border-emerald-300', text = 'text-emerald-900', icon = 'fa-check-circle', title = 'Success!';
if (!success && (operation === 'delete' || type === 'error')) {
bg = 'bg-red-100'; border = 'border-red-300'; text = 'text-red-800'; icon = 'fa-times-circle'; title = 'Error!';
} else if (operation === 'delete' && success) {
bg = 'bg-rose-100'; border = 'border-rose-300'; text = 'text-rose-800'; icon = 'fa-trash-alt'; title = 'Deleted!';
} else if (operation === 'update') {
bg = 'bg-amber-100'; border = 'border-amber-300'; text = 'text-amber-900'; icon = 'fa-pen'; title = 'Updated!';
} else if (operation === 'read') {
bg = 'bg-blue-100'; border = 'border-blue-300'; text = 'text-blue-900'; icon = 'fa-info-circle'; title = 'Fetched!';
}
// Build toast element
const toast = document.createElement('div');
toast.className = `fade-up fixed bottom-6 right-6 max-w-sm w-full z-50 shadow-xl rounded-xl border px-4 py-3 ${bg} ${border} ${text} animate-slide-up`;
toast.innerHTML = `
<div class="flex justify-between items-start gap-3">
<div class="flex items-start gap-2">
<i class="fas ${icon} mt-1 text-lg"></i>
<div>
<div class="font-semibold">${title}</div>
<div class="mt-1 space-y-1 text-sm leading-snug">${clean.split('\n').map(line => `<div class="animate-fade">${line}</div>`).join('')}</div>
</div>
</div>
<button onclick="this.parentElement.parentElement.remove()" class="text-xl font-bold hover:text-black/50 transition">×</button>
</div>
`;
document.body.appendChild(toast);
queryInput.value = '';
setTimeout(() => toast.remove(), 8000);
} catch (error) {
console.error('CRUD error:', error);
alert('❌ ' + error.message);
} finally {
crudBtn.innerHTML = '<i class="fas fa-terminal"></i> <span>Execute</span>';
crudBtn.disabled = false;
}
}
async function callCrudOperation() {
const queryInput = document.getElementById('crudCommand');
const query = queryInput.value.trim();
if (!query) {
alert('Please enter a CRUD command');
return;
}
const crudBtn = document.getElementById('crudBtn');
crudBtn.innerHTML = '<i class="fas fa-spinner fa-spin"></i> Executing...';
crudBtn.disabled = true;
try {
const response = await fetch(`https://thecyberlearn.app.n8n.cloud/webhook/search-airtable?query=${encodeURIComponent(query)}`, {
method: 'GET',
headers: { 'Accept': 'application/json' }
});
if (!response.ok) {
throw new Error(`HTTP ${response.status}: ${response.statusText}`);
}
const result = await response.json();
const raw = result?.content?.originalMessage || result?.content?.message || '✅ Operation completed.';
const clean = raw
.replace(/<[^>]*>/g, '')
.replace(/\*\*/g, '')
.replace(/Complete dataset returned.*included/gi, '')
.trim();
const operation = result?.operation || 'info';
const type = result?.content?.messageType || 'success';
const success = result?.success;
// Determine toast style
let bg = 'bg-emerald-100', border = 'border-emerald-300', text = 'text-emerald-900', icon = 'fa-check-circle', title = 'Success!';
if (/cannot create/i.test(clean)) {
bg = 'bg-yellow-50'; border = 'border-yellow-200'; text = 'text-yellow-800'; icon = 'fa-clone'; title = 'Duplicate Found';
} else if (!success && (operation === 'delete' || type === 'error')) {
bg = 'bg-red-100'; border = 'border-red-300'; text = 'text-red-800'; icon = 'fa-times-circle'; title = 'Error!';
} else if (operation === 'delete' && success) {
bg = 'bg-rose-100'; border = 'border-rose-300'; text = 'text-rose-800'; icon = 'fa-trash-alt'; title = 'Deleted!';
} else if (operation === 'update') {
bg = 'bg-amber-100'; border = 'border-amber-300'; text = 'text-amber-900'; icon = 'fa-pen'; title = 'Updated!';
} else if (operation === 'read') {
bg = 'bg-blue-100'; border = 'border-blue-300'; text = 'text-blue-900'; icon = 'fa-info-circle'; title = 'Fetched!';
}
// Build toast element
const toast = document.createElement('div');
toast.className = `fade-up fixed bottom-6 right-6 max-w-sm w-full z-50 shadow-xl rounded-xl border px-4 py-3 ${bg} ${border} ${text} animate-slide-up`;
toast.innerHTML = `
<div class="flex justify-between items-start gap-3">
<div class="flex items-start gap-2">
<i class="fas ${icon} mt-1 text-lg"></i>
<div>
<div class="font-semibold">${title}</div>
<div class="mt-1 space-y-1 text-sm leading-snug">${clean.split('\n').map(line => `<div class="animate-fade">${line}</div>`).join('')}</div>
</div>
</div>
<button onclick="this.parentElement.parentElement.remove()" class="text-xl font-bold hover:text-black/50 transition">×</button>
</div>
`;
document.body.appendChild(toast);
queryInput.value = '';
setTimeout(() => toast.remove(), 8000);
} catch (error) {
console.error('CRUD error:', error);
alert('❌ ' + error.message);
} finally {
crudBtn.innerHTML = '<i class="fas fa-terminal"></i> <span>Execute</span>';
crudBtn.disabled = false;
}
}
// Make functions available globally // Make functions available globally
window.callWorkflow = callWorkflow; window.callWorkflow = callWorkflow;
window.setQuickSearch = setQuickSearch; window.setQuickSearch = setQuickSearch;
window.exportResults = exportResults; window.exportResults = exportResults;
window.shareResults = shareResults; window.shareResults = shareResults;
window.loadMoreResults = loadMoreResults; window.loadMoreResults = loadMoreResults;
window.saveCompany = saveCompany; window.saveCompany = saveCompany;
window.callCrudOperation = callCrudOperation; window.callCrudOperation = callCrudOperation;
window.callMobileCrudOperation = callMobileCrudOperation;
window.shareCompany = shareCompany; window.shareCompany = shareCompany;
window.toggleMobileExecute = toggleMobileExecute;
// Initialize when DOM is loaded // Initialize when DOM is loaded
document.addEventListener('DOMContentLoaded', function() { document.addEventListener('DOMContentLoaded', function() {
@ -1313,6 +1194,20 @@ async function callCrudOperation() {
} }
}); });
// Enter key support for mobile CRUD
document.getElementById('mobileCrudCommand')?.addEventListener('keypress', function(e) {
if (e.key === 'Enter') {
window.callMobileCrudOperation();
}
});
// Enter key support for desktop CRUD
document.getElementById('crudCommand')?.addEventListener('keypress', function(e) {
if (e.key === 'Enter') {
window.callCrudOperation();
}
});
// Initialize // Initialize
updateLastUpdated(); updateLastUpdated();
}); });