mirror of
https://github.com/thecyberlearn/victor-dashboard.git
synced 2026-08-18 10:13:05 +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>
|
</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) -->
|
||||||
|
<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-xl p-3 shadow-sm border border-gray-200 text-center">
|
||||||
<div class="bg-white rounded-2xl shadow-sm border border-gray-200 p-4 lg:p-6 mb-6 fade-up">
|
<div class="w-8 h-8 bg-green-100 rounded-lg flex items-center justify-center mx-auto mb-2">
|
||||||
<h2 class="text-lg font-bold text-gray-800 mb-3">🔧 Create / Update / Delete</h2>
|
<i class="fas fa-chart-line text-green-600"></i>
|
||||||
<div class="flex flex-col sm:flex-row gap-3">
|
</div>
|
||||||
<input
|
<p class="text-xs text-gray-500 mb-1">Results Found</p>
|
||||||
id="crudCommand"
|
<p class="text-sm font-bold text-gray-800" id="mobileResults">0</p>
|
||||||
type="text"
|
</div>
|
||||||
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-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 class="bg-white rounded-xl p-3 shadow-sm border border-gray-200 text-center">
|
||||||
<div id="crudResultSection" class="hidden mb-6 fade-up">
|
<div class="w-8 h-8 bg-orange-100 rounded-lg flex items-center justify-center mx-auto mb-2">
|
||||||
<div class="bg-white rounded-xl border border-gray-200 p-4">
|
<i class="fas fa-bolt text-orange-600"></i>
|
||||||
<h3 class="text-sm font-semibold text-gray-700 mb-3">📝 Operation Result</h3>
|
</div>
|
||||||
<div id="crudResultHtml" class="text-sm text-gray-800 leading-relaxed"></div>
|
<p class="text-xs text-gray-500 mb-1">Response Time</p>
|
||||||
</div>
|
<p class="text-sm font-bold text-gray-800" id="mobileResponseTime">0ms</p>
|
||||||
</div>
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
<!-- Stats Cards -->
|
<!-- Mobile Collapsible Execute Section -->
|
||||||
<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="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="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() {
|
||||||
|
const queryInput = document.getElementById('crudCommand');
|
||||||
|
const query = queryInput.value.trim();
|
||||||
|
if (!query) {
|
||||||
|
alert('Please enter a CRUD command');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
async function callCrudOperation() {
|
const crudBtn = document.getElementById('crudBtn');
|
||||||
const query = document.getElementById('crudCommand').value.trim();
|
crudBtn.innerHTML = '<i class="fas fa-spinner fa-spin"></i> Executing...';
|
||||||
if (!query) {
|
crudBtn.disabled = true;
|
||||||
alert('Please enter a CRUD command');
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
const crudBtn = document.getElementById('crudBtn');
|
try {
|
||||||
crudBtn.innerHTML = '<i class="fas fa-spinner fa-spin"></i> Executing...';
|
const response = await fetch(`https://thecyberlearn.app.n8n.cloud/webhook/search-airtable?query=${encodeURIComponent(query)}`, {
|
||||||
crudBtn.disabled = true;
|
method: 'GET',
|
||||||
|
headers: { 'Accept': 'application/json' }
|
||||||
|
});
|
||||||
|
|
||||||
try {
|
if (!response.ok) {
|
||||||
const response = await fetch(`https://thecyberlearn.app.n8n.cloud/webhook/search-airtable?query=${encodeURIComponent(query)}`, {
|
throw new Error(`HTTP ${response.status}: ${response.statusText}`);
|
||||||
method: 'GET',
|
}
|
||||||
headers: { 'Accept': 'application/json' }
|
|
||||||
});
|
|
||||||
|
|
||||||
if (!response.ok) {
|
const result = await response.json();
|
||||||
throw new Error(`HTTP ${response.status}: ${response.statusText}`);
|
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();
|
const operation = result?.operation || 'info';
|
||||||
alert(result.message || 'Operation completed');
|
const type = result?.content?.messageType || 'success';
|
||||||
} catch (error) {
|
const success = result?.success;
|
||||||
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;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
// 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() {
|
// Build toast element
|
||||||
const query = document.getElementById('crudCommand').value.trim();
|
const toast = document.createElement('div');
|
||||||
if (!query) {
|
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`;
|
||||||
alert('Please enter a CRUD command');
|
toast.innerHTML = `
|
||||||
return;
|
<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>
|
||||||
const crudBtn = document.getElementById('crudBtn');
|
<div>
|
||||||
crudBtn.innerHTML = '<i class="fas fa-spinner fa-spin"></i> Executing...';
|
<div class="font-semibold">${title}</div>
|
||||||
crudBtn.disabled = true;
|
<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>
|
||||||
try {
|
</div>
|
||||||
const response = await fetch(`https://thecyberlearn.app.n8n.cloud/webhook/search-airtable?query=${encodeURIComponent(query)}`, {
|
<button onclick="this.parentElement.parentElement.remove()" class="text-xl font-bold hover:text-black/50 transition">×</button>
|
||||||
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
|
|
||||||
</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';
|
||||||
|
const type = result?.content?.messageType || 'success';
|
||||||
|
const success = result?.success;
|
||||||
|
|
||||||
async function callCrudOperation() {
|
// Determine toast style
|
||||||
const query = document.getElementById('crudCommand').value.trim();
|
let bg = 'bg-emerald-100', border = 'border-emerald-300', text = 'text-emerald-900', icon = 'fa-check-circle', title = 'Success!';
|
||||||
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 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`;
|
||||||
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 raw = result?.content?.originalMessage || result?.content?.message || '✅ Operation completed.';
|
</div>
|
||||||
const clean = raw
|
<button onclick="this.parentElement.parentElement.remove()" class="text-xl font-bold hover:text-black/50 transition">×</button>
|
||||||
.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>
|
|
||||||
</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
|
// Auto-collapse the mobile execute section after successful operation
|
||||||
} catch (error) {
|
const content = document.getElementById('mobileExecuteContent');
|
||||||
console.error('CRUD error:', error);
|
const chevron = document.getElementById('executeChevron');
|
||||||
alert('❌ ' + error.message);
|
content.classList.remove('open');
|
||||||
} finally {
|
chevron.style.transform = 'rotate(0deg)';
|
||||||
crudBtn.innerHTML = '<i class="fas fa-terminal"></i> <span>Execute</span>';
|
|
||||||
crudBtn.disabled = false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
setTimeout(() => toast.remove(), 8000);
|
||||||
|
} catch (error) {
|
||||||
async function callCrudOperation() {
|
console.error('Mobile CRUD error:', error);
|
||||||
const queryInput = document.getElementById('crudCommand');
|
alert('❌ ' + error.message);
|
||||||
const query = queryInput.value.trim();
|
} finally {
|
||||||
if (!query) {
|
crudBtn.innerHTML = '<i class="fas fa-terminal"></i> <span>Execute</span>';
|
||||||
alert('Please enter a CRUD command');
|
crudBtn.disabled = false;
|
||||||
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();
|
||||||
});
|
});
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user