mirror of
https://github.com/thecyberlearn/victor-dashboard.git
synced 2026-08-18 07:53:04 +00:00
Update Mobile index.html
This commit is contained in:
parent
a00232b581
commit
4fd82179a2
723
index.html
723
index.html
@ -13,7 +13,6 @@
|
||||
}
|
||||
</style>
|
||||
|
||||
|
||||
<link href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.0.0/css/all.min.css" rel="stylesheet">
|
||||
<style>
|
||||
@import url('https://fonts.googleapis.com/css2?family=Inter:wght@300;400;500;600;700&display=swap');
|
||||
@ -131,6 +130,35 @@ html {
|
||||
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>
|
||||
@ -272,39 +300,115 @@ html {
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Mobile Icon Cards (visible only on mobile) -->
|
||||
<div class="lg:hidden mb-6">
|
||||
<div class="mobile-stats-grid">
|
||||
<div class="bg-white rounded-xl p-3 shadow-sm border border-gray-200 text-center">
|
||||
<div class="w-8 h-8 bg-blue-100 rounded-lg flex items-center justify-center mx-auto mb-2">
|
||||
<i class="fas fa-search text-blue-600"></i>
|
||||
</div>
|
||||
<p class="text-xs text-gray-500 mb-1">Total Searches</p>
|
||||
<p class="text-sm font-bold text-gray-800" id="mobileSearches">0</p>
|
||||
</div>
|
||||
|
||||
<!-- CRUD Command Section -->
|
||||
<div class="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>
|
||||
<div class="bg-white rounded-xl p-3 shadow-sm border border-gray-200 text-center">
|
||||
<div class="w-8 h-8 bg-green-100 rounded-lg flex items-center justify-center mx-auto mb-2">
|
||||
<i class="fas fa-chart-line text-green-600"></i>
|
||||
</div>
|
||||
<p class="text-xs text-gray-500 mb-1">Results Found</p>
|
||||
<p class="text-sm font-bold text-gray-800" id="mobileResults">0</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-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>
|
||||
|
||||
<!-- CRUD Result Section -->
|
||||
<div id="crudResultSection" class="hidden mb-6 fade-up">
|
||||
<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>
|
||||
<div id="crudResultHtml" class="text-sm text-gray-800 leading-relaxed"></div>
|
||||
</div>
|
||||
</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>
|
||||
|
||||
<!-- Stats Cards -->
|
||||
<div class="grid grid-cols-1 sm:grid-cols-2 lg:grid-cols-4 gap-4 lg:gap-6 mb-6 lg:mb-8">
|
||||
<!-- 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 -->
|
||||
<div id="crudResultSection" class="hidden mb-6 fade-up">
|
||||
<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>
|
||||
<div id="crudResultHtml" class="text-sm text-gray-800 leading-relaxed"></div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Desktop Stats Cards (hidden on mobile) -->
|
||||
<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="flex items-center justify-between">
|
||||
<div>
|
||||
@ -413,6 +517,20 @@ html {
|
||||
let totalResultCount = 0;
|
||||
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
|
||||
async function callWorkflow() {
|
||||
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) {
|
||||
totalSearchCount++;
|
||||
totalResultCount += resultCount;
|
||||
|
||||
// Desktop stats
|
||||
document.getElementById('totalSearches').textContent = totalSearchCount;
|
||||
document.getElementById('totalResults').textContent = totalResultCount;
|
||||
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();
|
||||
}
|
||||
|
||||
@ -872,423 +996,180 @@ html {
|
||||
}
|
||||
}
|
||||
|
||||
// Desktop CRUD operation
|
||||
async function callCrudOperation() {
|
||||
const queryInput = document.getElementById('crudCommand');
|
||||
const query = queryInput.value.trim();
|
||||
if (!query) {
|
||||
alert('Please enter a CRUD command');
|
||||
return;
|
||||
}
|
||||
|
||||
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;
|
||||
|
||||
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' }
|
||||
});
|
||||
|
||||
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}`);
|
||||
}
|
||||
|
||||
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 result = await response.json();
|
||||
alert(result.message || 'Operation completed');
|
||||
} catch (error) {
|
||||
console.error('CRUD error:', error);
|
||||
alert('Failed to execute command: ' + error.message);
|
||||
} finally {
|
||||
crudBtn.innerHTML = '<i class="fas fa-terminal"></i> <span>Execute</span>';
|
||||
crudBtn.disabled = false;
|
||||
}
|
||||
}
|
||||
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!';
|
||||
}
|
||||
|
||||
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 htmlContent = result?.content?.message || '✅ Operation completed.';
|
||||
document.getElementById('crudResultHtml').innerHTML = htmlContent;
|
||||
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
|
||||
// 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>
|
||||
<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;
|
||||
}
|
||||
}
|
||||
`;
|
||||
|
||||
|
||||
|
||||
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}`);
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
||||
const result = await response.json();
|
||||
const raw = result?.content?.originalMessage || result?.content?.message || '✅ Operation completed.';
|
||||
const clean = raw.replace(/<[^>]*>/g, '').replace(/\*\*/g, '');
|
||||
// Mobile CRUD operation
|
||||
async function callMobileCrudOperation() {
|
||||
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 type = result?.content?.messageType || 'success';
|
||||
const crudBtn = document.getElementById('mobileCrudBtn');
|
||||
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!';
|
||||
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!';
|
||||
}
|
||||
try {
|
||||
const response = await fetch(`https://thecyberlearn.app.n8n.cloud/webhook/search-airtable?query=${encodeURIComponent(query)}`, {
|
||||
method: 'GET',
|
||||
headers: { 'Accept': 'application/json' }
|
||||
});
|
||||
|
||||
document.getElementById('crudResultHtml').innerHTML = `
|
||||
<div class="fade-up flex items-start gap-3 p-4 rounded-xl border shadow-sm text-sm leading-6 ${bg} ${border} ${text}">
|
||||
<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;
|
||||
}
|
||||
}
|
||||
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;
|
||||
|
||||
async function callCrudOperation() {
|
||||
const query = document.getElementById('crudCommand').value.trim();
|
||||
if (!query) {
|
||||
alert('Please enter a CRUD command');
|
||||
return;
|
||||
}
|
||||
// Determine toast style
|
||||
let bg = 'bg-emerald-100', border = 'border-emerald-300', text = 'text-emerald-900', icon = 'fa-check-circle', title = 'Success!';
|
||||
|
||||
const crudBtn = document.getElementById('crudBtn');
|
||||
crudBtn.innerHTML = '<i class="fas fa-spinner fa-spin"></i> Executing...';
|
||||
crudBtn.disabled = true;
|
||||
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!';
|
||||
}
|
||||
|
||||
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';
|
||||
|
||||
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>
|
||||
// Build toast element
|
||||
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 = `
|
||||
<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>
|
||||
</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) {
|
||||
console.error('CRUD error:', error);
|
||||
alert('❌ ' + error.message);
|
||||
} finally {
|
||||
crudBtn.innerHTML = '<i class="fas fa-terminal"></i> <span>Execute</span>';
|
||||
crudBtn.disabled = false;
|
||||
}
|
||||
}
|
||||
// Auto-collapse the mobile execute section after successful operation
|
||||
const content = document.getElementById('mobileExecuteContent');
|
||||
const chevron = document.getElementById('executeChevron');
|
||||
content.classList.remove('open');
|
||||
chevron.style.transform = 'rotate(0deg)';
|
||||
|
||||
|
||||
|
||||
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}`);
|
||||
setTimeout(() => toast.remove(), 8000);
|
||||
} catch (error) {
|
||||
console.error('Mobile CRUD error:', error);
|
||||
alert('❌ ' + error.message);
|
||||
} 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';
|
||||
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
|
||||
window.callWorkflow = callWorkflow;
|
||||
window.setQuickSearch = setQuickSearch;
|
||||
window.exportResults = exportResults;
|
||||
window.shareResults = shareResults;
|
||||
window.loadMoreResults = loadMoreResults;
|
||||
window.saveCompany = saveCompany;
|
||||
window.saveCompany = saveCompany;
|
||||
window.callCrudOperation = callCrudOperation;
|
||||
window.callMobileCrudOperation = callMobileCrudOperation;
|
||||
window.shareCompany = shareCompany;
|
||||
window.toggleMobileExecute = toggleMobileExecute;
|
||||
|
||||
// Initialize when DOM is loaded
|
||||
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
|
||||
updateLastUpdated();
|
||||
});
|
||||
|
||||
Loading…
Reference in New Issue
Block a user