last_edit / frontend /smart_search.html
Moharek
Deploy Moharek GEO Platform
a74b879
Raw
History Blame Contribute Delete
28 kB
<!DOCTYPE html>
<html lang="ar" dir="rtl">
<head>
<meta charset="utf-8" />
<meta name="viewport" content="width=device-width,initial-scale=1" />
<title>محرك البحث الذكي — منصة محرك GEO</title>
<link href="https://fonts.googleapis.com/css2?family=Cairo:wght@400;600;700;800&display=swap" rel="stylesheet">
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.5.0/css/all.min.css">
<link rel="stylesheet" href="/theme.css">
<style>
.reveal{opacity:0;transform:translateY(20px);transition:all .6s cubic-bezier(.16,1,.3,1)}
.reveal.show{opacity:1;transform:translateY(0)}
nav{position:sticky;top:0;z-index:1000;border-bottom:1px solid var(--border);background:rgba(3,7,18,.85);backdrop-filter:blur(32px);height:80px;display:flex;align-items:center}
.nav-inner{max-width:1200px;margin:0 auto;padding:0 32px;width:100%;display:flex;justify-content:space-between;align-items:center}
.nav-logo{display:flex;align-items:center;gap:12px;text-decoration:none}
.wrap{max-width:1200px;margin:0 auto;padding:120px 24px 40px} /* Adjusted padding for fixed nav */
* { box-sizing: border-box; margin: 0; padding: 0; }
body {
background: var(--bg);
color: var(--text);
font-family: 'Cairo', sans-serif;
direction: rtl;
padding: 40px 20px;
}
.container {
max-width: 900px;
margin: 0 auto;
}
.search-card {
background: linear-gradient(165deg, rgba(15, 20, 25, 0.9), rgba(5, 7, 10, 0.95));
border: 1px solid var(--border);
border-radius: 24px;
padding: 40px;
backdrop-filter: blur(20px);
box-shadow: 0 10px 40px rgba(0,0,0,0.5);
margin-bottom: 30px;
}
.search-header {
text-align: center;
margin-bottom: 30px;
}
.search-title {
font-size: 32px;
font-weight: 900;
margin-bottom: 12px;
background: linear-gradient(135deg, #fff 0%, #fbbf24 45%, #3b82f6 100%);
-webkit-background-clip: text;
background-clip: text;
-webkit-text-fill-color: transparent;
}
.search-subtitle {
font-size: 16px;
color: var(--muted);
line-height: 1.6;
}
.search-box-wrapper {
position: relative;
margin-bottom: 20px;
}
.search-input {
width: 100%;
background: rgba(0, 0, 0, 0.3);
border: 2px solid var(--border);
border-radius: 16px;
padding: 18px 60px 18px 20px;
color: #fff;
font-family: 'Cairo';
font-size: 16px;
transition: all 0.3s;
}
.search-input:focus {
outline: none;
border-color: var(--blue);
box-shadow: 0 0 0 4px rgba(59, 130, 246, 0.1);
}
.search-btn {
position: absolute;
left: 10px;
top: 50%;
transform: translateY(-50%);
background: var(--accent-grad);
border: none;
border-radius: 12px;
padding: 12px 24px;
color: #000;
font-weight: 800;
cursor: pointer;
transition: 0.3s;
}
.search-btn:hover {
transform: translateY(-50%) scale(1.05);
box-shadow: 0 8px 20px rgba(59, 130, 246, 0.4);
}
.search-btn:disabled {
opacity: 0.5;
cursor: not-allowed;
}
.question-tags {
margin-bottom: 30px;
}
.tags-label {
font-size: 13px;
font-weight: 700;
color: var(--muted);
margin-bottom: 12px;
text-transform: uppercase;
letter-spacing: 1px;
}
.tags-container {
display: flex;
flex-wrap: wrap;
gap: 10px;
}
.tag {
display: inline-block;
padding: 10px 18px;
border-radius: 25px;
font-size: 13px;
font-weight: 700;
cursor: pointer;
transition: all 0.3s ease;
border: 2px solid transparent;
backdrop-filter: blur(10px);
}
.tag:hover {
transform: translateY(-3px) scale(1.05);
box-shadow: 0 8px 20px rgba(0, 0, 0, 0.3);
}
.tag:active {
transform: translateY(-1px) scale(1.02);
}
.tag-primary {
background: linear-gradient(135deg, rgba(59, 130, 246, 0.15), rgba(37, 99, 235, 0.15));
border-color: rgba(59, 130, 246, 0.4);
color: #60A5FA;
}
.tag-primary:hover {
background: linear-gradient(135deg, rgba(59, 130, 246, 0.25), rgba(37, 99, 235, 0.25));
border-color: #3B82F6;
}
.tag-success {
background: linear-gradient(135deg, rgba(16, 185, 129, 0.15), rgba(5, 150, 105, 0.15));
border-color: rgba(16, 185, 129, 0.4);
color: #34D399;
}
.tag-success:hover {
background: linear-gradient(135deg, rgba(16, 185, 129, 0.25), rgba(5, 150, 105, 0.25));
border-color: #10B981;
}
.tag-info {
background: linear-gradient(135deg, rgba(6, 182, 212, 0.15), rgba(8, 145, 178, 0.15));
border-color: rgba(6, 182, 212, 0.4);
color: #22D3EE;
}
.tag-info:hover {
background: linear-gradient(135deg, rgba(6, 182, 212, 0.25), rgba(8, 145, 178, 0.25));
border-color: #06B6D4;
}
.tag-warning {
background: linear-gradient(135deg, rgba(251, 191, 36, 0.15), rgba(245, 158, 11, 0.15));
border-color: rgba(251, 191, 36, 0.4);
color: #FCD34D;
}
.tag-warning:hover {
background: linear-gradient(135deg, rgba(251, 191, 36, 0.25), rgba(245, 158, 11, 0.25));
border-color: #FBBF24;
}
.tag-purple {
background: linear-gradient(135deg, rgba(168, 85, 247, 0.15), rgba(147, 51, 234, 0.15));
border-color: rgba(168, 85, 247, 0.4);
color: #C084FC;
}
.tag-purple:hover {
background: linear-gradient(135deg, rgba(168, 85, 247, 0.25), rgba(147, 51, 234, 0.25));
border-color: #A855F7;
}
.tag-pink {
background: linear-gradient(135deg, rgba(236, 72, 153, 0.15), rgba(219, 39, 119, 0.15));
border-color: rgba(236, 72, 153, 0.4);
color: #F472B6;
}
.tag-pink:hover {
background: linear-gradient(135deg, rgba(236, 72, 153, 0.25), rgba(219, 39, 119, 0.25));
border-color: #EC4899;
}
.tag-emerald {
background: linear-gradient(135deg, rgba(16, 185, 129, 0.15), rgba(5, 150, 105, 0.15));
border-color: rgba(16, 185, 129, 0.4);
color: #6EE7B7;
}
.tag-emerald:hover {
background: linear-gradient(135deg, rgba(16, 185, 129, 0.25), rgba(5, 150, 105, 0.25));
border-color: #10B981;
}
.tag-orange {
background: linear-gradient(135deg, rgba(249, 115, 22, 0.15), rgba(234, 88, 12, 0.15));
border-color: rgba(249, 115, 22, 0.4);
color: #FB923C;
}
.tag-orange:hover {
background: linear-gradient(135deg, rgba(249, 115, 22, 0.25), rgba(234, 88, 12, 0.25));
border-color: #F97316;
}
.tag-cyan {
background: linear-gradient(135deg, rgba(34, 211, 238, 0.15), rgba(6, 182, 212, 0.15));
border-color: rgba(34, 211, 238, 0.4);
color: #67E8F9;
}
.tag-cyan:hover {
background: linear-gradient(135deg, rgba(34, 211, 238, 0.25), rgba(6, 182, 212, 0.25));
border-color: #22D3EE;
}
.tag-indigo {
background: linear-gradient(135deg, rgba(99, 102, 241, 0.15), rgba(79, 70, 229, 0.15));
border-color: rgba(99, 102, 241, 0.4);
color: #A5B4FC;
}
.tag-indigo:hover {
background: linear-gradient(135deg, rgba(99, 102, 241, 0.25), rgba(79, 70, 229, 0.25));
border-color: #6366F1;
}
.tag-rose {
background: linear-gradient(135deg, rgba(244, 63, 94, 0.15), rgba(225, 29, 72, 0.15));
border-color: rgba(244, 63, 94, 0.4);
color: #FB7185;
}
.tag-rose:hover {
background: linear-gradient(135deg, rgba(244, 63, 94, 0.25), rgba(225, 29, 72, 0.25));
border-color: #F43F5E;
}
.tag-teal {
background: linear-gradient(135deg, rgba(20, 184, 166, 0.15), rgba(13, 148, 136, 0.15));
border-color: rgba(20, 184, 166, 0.4);
color: #5EEAD4;
}
.tag-teal:hover {
background: linear-gradient(135deg, rgba(20, 184, 166, 0.25), rgba(13, 148, 136, 0.25));
border-color: #14B8A6;
}
.result-container {
display: none;
background: rgba(0, 0, 0, 0.3);
border: 1px solid var(--border);
border-radius: 16px;
padding: 30px;
margin-top: 30px;
}
.result-container.show {
display: block;
animation: fadeIn 0.5s ease-out;
}
@keyframes fadeIn {
from { opacity: 0; transform: translateY(20px); }
to { opacity: 1; transform: translateY(0); }
}
.result-header {
display: flex;
align-items: center;
gap: 12px;
margin-bottom: 20px;
padding-bottom: 15px;
border-bottom: 1px solid var(--border);
}
.result-icon {
font-size: 28px;
color: var(--blue);
}
.result-title {
font-size: 20px;
font-weight: 800;
color: #fff;
}
.result-content {
font-size: 16px;
line-height: 1.8;
color: var(--text);
margin-bottom: 25px;
}
.sources {
margin-top: 25px;
}
.sources-title {
font-size: 14px;
font-weight: 800;
color: var(--muted);
margin-bottom: 12px;
text-transform: uppercase;
letter-spacing: 1px;
}
.source-item {
background: rgba(255, 255, 255, 0.03);
border: 1px solid var(--border);
border-radius: 10px;
padding: 12px 16px;
margin-bottom: 10px;
transition: all 0.3s;
}
.source-item:hover {
background: rgba(255, 255, 255, 0.06);
border-color: var(--blue);
}
.source-link {
color: var(--blue);
text-decoration: none;
font-weight: 700;
font-size: 14px;
display: block;
margin-bottom: 6px;
}
.source-snippet {
font-size: 13px;
color: var(--muted);
line-height: 1.5;
}
.loading {
text-align: center;
padding: 40px;
color: var(--muted);
}
.loading-spinner {
display: inline-block;
width: 40px;
height: 40px;
border: 4px solid rgba(59, 130, 246, 0.1);
border-top-color: var(--blue);
border-radius: 50%;
animation: spin 1s linear infinite;
margin-bottom: 15px;
}
@keyframes spin {
to { transform: rotate(360deg); }
}
.error-message {
background: rgba(239, 68, 68, 0.1);
border: 1px solid rgba(239, 68, 68, 0.3);
border-radius: 12px;
padding: 20px;
color: var(--red);
text-align: center;
margin-top: 20px;
}
.badge {
display: inline-block;
padding: 4px 12px;
border-radius: 6px;
font-size: 11px;
font-weight: 800;
background: rgba(59, 130, 246, 0.1);
color: var(--blue);
border: 1px solid rgba(59, 130, 246, 0.2);
margin-left: 8px;
}
.api-status {
display: flex;
align-items: center;
justify-content: center;
gap: 15px;
padding: 12px 20px;
background: rgba(0, 0, 0, 0.2);
border: 1px solid var(--border);
border-radius: 12px;
margin-bottom: 20px;
font-size: 12px;
}
.api-indicator {
display: flex;
align-items: center;
gap: 6px;
padding: 6px 12px;
border-radius: 8px;
background: rgba(255, 255, 255, 0.03);
transition: all 0.3s;
}
.api-indicator:hover {
background: rgba(255, 255, 255, 0.06);
}
.api-dot {
width: 8px;
height: 8px;
border-radius: 50%;
animation: pulse 2s infinite;
}
.api-dot.active {
background: #10B981;
box-shadow: 0 0 10px rgba(16, 185, 129, 0.5);
}
.api-dot.fallback {
background: #FBBF24;
box-shadow: 0 0 10px rgba(251, 191, 36, 0.5);
}
.api-dot.inactive {
background: #6B7280;
}
@keyframes pulse {
0%, 100% { opacity: 1; }
50% { opacity: 0.5; }
}
.api-name {
font-weight: 700;
color: var(--text);
}
.api-label {
font-size: 10px;
color: var(--muted);
text-transform: uppercase;
letter-spacing: 0.5px;
}
.back-btn {
position: fixed;
top: 20px;
left: 20px;
background: rgba(255, 255, 255, 0.05);
border: 1px solid var(--border);
border-radius: 12px;
padding: 12px 20px;
color: var(--text);
text-decoration: none;
font-weight: 700;
font-size: 14px;
transition: all 0.3s;
backdrop-filter: blur(10px);
display: flex;
align-items: center;
gap: 8px;
z-index: 1000;
}
.back-btn:hover {
background: rgba(255, 255, 255, 0.1);
border-color: var(--blue);
transform: translateX(-5px);
}
@media (max-width: 768px) {
body {
padding: 20px 15px;
}
.search-card {
padding: 25px 20px;
border-radius: 16px;
}
.search-title {
font-size: 24px;
}
.search-subtitle {
font-size: 14px;
}
.api-status {
flex-wrap: wrap;
gap: 8px;
padding: 10px 15px;
}
.api-indicator {
font-size: 11px;
padding: 5px 10px;
}
.tags-container {
gap: 8px;
}
.tag {
padding: 8px 14px;
font-size: 12px;
}
.search-input {
padding: 16px 55px 16px 16px;
font-size: 15px;
}
.search-btn {
padding: 10px 18px;
font-size: 13px;
}
.result-container {
padding: 20px;
}
.result-title {
font-size: 18px;
}
.result-content {
font-size: 15px;
}
.back-btn {
top: 10px;
left: 10px;
padding: 10px 16px;
font-size: 13px;
}
}
</style>
</head>
<body>
<a href="/dashboard.html" class="back-btn">
<span></span>
<span>العودة للوحة التحكم</span>
</a>
<div class="container">
<div class="search-card">
<div class="search-header">
<h1 class="search-title">محرك البحث الذكي</h1>
<p class="search-subtitle">
اسأل واحصل على إجابات مخصصة لاحتياجاتك — محرك بحث ذكي متعدد المصادر
يوفر إجابات دقيقة ومحدثة حول أي موضوع، مع مصادر موثوقة وتحليل عميق.
</p>
</div>
<div class="api-status">
<div class="api-label">🔄 مصادر البحث المتاحة:</div>
<div class="api-indicator" title="Tavily AI - محرك بحث ذكي متقدم">
<div class="api-dot active"></div>
<span class="api-name">Tavily AI</span>
</div>
<div class="api-indicator" title="Google Custom Search - بحث Google المخصص">
<div class="api-dot fallback"></div>
<span class="api-name">Google</span>
</div>
<div class="api-indicator" title="SerpAPI - واجهة برمجية لنتائج البحث">
<div class="api-dot fallback"></div>
<span class="api-name">SerpAPI</span>
</div>
<div class="api-indicator" title="DuckDuckGo - بحث خاص وآمن">
<div class="api-dot fallback"></div>
<span class="api-name">DuckDuckGo</span>
</div>
</div>
<div class="search-box-wrapper">
<input
type="text"
id="searchInput"
class="search-input"
placeholder="اسأل أي سؤال... مثل: https://mohrek.com ما هي المنافسين في منطقة الخليج؟"
onkeypress="if(event.key==='Enter') performSearch()"
/>
<button class="search-btn" id="searchBtn" onclick="performSearch()">
بحث
</button>
</div>
<div id="resultContainer" class="result-container">
<!-- Results will be inserted here -->
</div>
</div>
</div>
<script>
const token = localStorage.getItem('token');
// if (!token) window.location.href = '/login.html';
function setExample(text) {
document.getElementById('searchInput').value = text;
performSearch();
}
async function performSearch() {
const query = document.getElementById('searchInput').value.trim();
const resultContainer = document.getElementById('resultContainer');
const searchBtn = document.getElementById('searchBtn');
if (!query) {
alert('يرجى إدخال سؤال أو استفسار');
return;
}
// Show loading state
searchBtn.disabled = true;
searchBtn.textContent = 'جارٍ البحث...';
resultContainer.innerHTML = `
<div class="loading">
<div class="loading-spinner"></div>
<div>جارٍ البحث في قاعدة البيانات العالمية...</div>
<div style="font-size: 13px; margin-top: 10px; color: var(--dim)">
مدعوم بـ Tavily AI
</div>
</div>
`;
resultContainer.classList.add('show');
try {
// Call Tavily search API
const response = await fetch('http://localhost:8001/api/tavily/search', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
'Authorization': `Bearer ${token}`
},
body: JSON.stringify({
query: query,
max_results: 5,
search_depth: 'advanced'
})
});
const data = await response.json();
if (!data.ok) {
// Check if it's a quota exceeded error
if (response.status === 429 || (data.error && data.error.includes('quota'))) {
throw new Error('⚠️ تم استنفاد حصة البحث اليومية. سيتم التبديل تلقائياً إلى مصدر بحث بديل...');
}
throw new Error(data.error || 'فشل البحث');
}
// Display results
displayResults(query, data.result, data.source || 'tavily');
} catch (error) {
console.error('Search error:', error);
const errorMsg = error.message || 'حدث خطأ غير متوقع';
// Check if it's a quota error
if (errorMsg.includes('quota') || errorMsg.includes('حصة')) {
resultContainer.innerHTML = `
<div class="error-message" style="background: rgba(251, 191, 36, 0.1); border-color: rgba(251, 191, 36, 0.3); color: #FBBF24;">
<strong>⚠️ تم استنفاد حصة البحث</strong>
<div style="margin-top: 10px; font-size: 14px;">
${errorMsg}
</div>
<div style="margin-top: 15px; font-size: 13px; opacity: 0.9;">
💡 النظام يستخدم تلقائياً مصادر بحث بديلة (Google, DuckDuckGo, SerpAPI)
</div>
<div style="margin-top: 10px; font-size: 12px; opacity: 0.7;">
يتم التبديل التلقائي بين 3+ مصادر بحث لضمان الاستمرارية
</div>
</div>
`;
} else {
resultContainer.innerHTML = `
<div class="error-message">
<strong>⚠️ حدث خطأ أثناء البحث</strong>
<div style="margin-top: 10px; font-size: 14px;">
${errorMsg}
</div>
<div style="margin-top: 15px; font-size: 13px; opacity: 0.8;">
يرجى المحاولة مرة أخرى أو تجربة سؤال مختلف
</div>
</div>
`;
}
} finally {
searchBtn.disabled = false;
searchBtn.textContent = 'بحث';
}
}
function displayResults(query, result, source = 'tavily') {
const resultContainer = document.getElementById('resultContainer');
// Extract answer and sources
const answer = result.answer || 'لم يتم العثور على إجابة مباشرة';
const results = result.results || [];
// Source badge styling
const sourceBadges = {
'tavily': { text: 'Tavily AI', color: '#3B82F6', icon: '🤖' },
'tavily_research': { text: 'Tavily Research', color: '#8B5CF6', icon: '🔬' },
'website_intelligence': { text: 'Website Intel', color: '#10B981', icon: '📊' },
'serpapi_fallback': { text: 'SerpAPI', color: '#F59E0B', icon: '🔍' },
'duckduckgo_fallback': { text: 'DuckDuckGo', color: '#EF4444', icon: '🦆' },
'google_fallback': { text: 'Google Search', color: '#06B6D4', icon: '🌐' }
};
const sourceBadge = sourceBadges[source] || sourceBadges['tavily'];
let html = `
<div class="result-header">
<div class="result-icon">${sourceBadge.icon}</div>
<div class="result-title">نتيجة البحث الذكي</div>
<span class="badge" style="background: rgba(${hexToRgb(sourceBadge.color)}, 0.1); color: ${sourceBadge.color}; border-color: rgba(${hexToRgb(sourceBadge.color)}, 0.3);">${sourceBadge.text}</span>
</div>
<div class="result-content">
${answer || generateAnswerFromResults(results)}
</div>
`;
// Add sources if available
if (results && results.length > 0) {
html += `
<div class="sources">
<div class="sources-title">📚 المصادر</div>
`;
results.forEach((source, index) => {
html += `
<div class="source-item">
<a href="${source.url}" target="_blank" class="source-link">
${index + 1}. ${source.title || 'مصدر'}
</a>
<div class="source-snippet">
${source.content ? source.content.substring(0, 200) + '...' : ''}
</div>
</div>
`;
});
html += `</div>`;
}
resultContainer.innerHTML = html;
}
function hexToRgb(hex) {
const result = /^#?([a-f\d]{2})([a-f\d]{2})([a-f\d]{2})$/i.exec(hex);
return result ? `${parseInt(result[1], 16)}, ${parseInt(result[2], 16)}, ${parseInt(result[3], 16)}` : '59, 130, 246';
}
function generateAnswerFromResults(results) {
if (!results || results.length === 0) {
return 'لم يتم العثور على نتائج مطابقة لاستفسارك.';
}
// Combine snippets from top results
let answer = 'بناءً على المصادر المتاحة:\n\n';
results.slice(0, 3).forEach((result, index) => {
if (result.content) {
answer += `${index + 1}. ${result.content.substring(0, 300)}...\n\n`;
}
});
return answer.replace(/\n/g, '<br>');
}
window.addEventListener('load', () => {
// Reveal Animations
const observerOptions = { threshold: 0.1, rootMargin: '0px 0px -50px 0px' };
const observer = new IntersectionObserver((entries) => {
entries.forEach(entry => {
if (entry.isIntersecting) {
entry.target.classList.add('show');
observer.unobserve(entry.target);
}
});
}, observerOptions);
document.querySelectorAll('.reveal').forEach(el => observer.observe(el));
document.getElementById('searchInput').focus();
});
function logout(){localStorage.clear();window.location.href='/login.html'}
</script>
</body>
</html>