File size: 5,067 Bytes
fde590b
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
97250e9
 
 
 
 
 
 
 
 
 
 
 
fde590b
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
document.addEventListener('DOMContentLoaded', () => {
    // DOM Elements
    const searchForm = document.getElementById('searchForm');
    const queryInput = document.getElementById('queryInput');
    const searchBtn = document.getElementById('searchBtn');
    const searchIcon = document.getElementById('searchIcon');
    const loadingSpinner = document.getElementById('loadingSpinner');
    const topKInput = document.getElementById('topKInput');
    const topKValue = document.getElementById('topKValue');
    const resultsContainer = document.getElementById('resultsContainer');
    const errorMsg = document.getElementById('errorMsg');
    const noResults = document.getElementById('noResults');

    // Update Slider Value
    topKInput.addEventListener('input', (e) => {
        topKValue.textContent = e.target.value;
    });

    // Form Submit
    searchForm.addEventListener('submit', async (e) => {
        e.preventDefault();

        const query = queryInput.value.trim();
        if (!query) return;

        const topK = parseInt(topKInput.value);

        // UI Loading State
        setLoading(true);
        hideElements([errorMsg, noResults]);
        resultsContainer.innerHTML = '';

        try {
            const response = await fetch('/api/search', {
                method: 'POST',
                headers: {
                    'Content-Type': 'application/json'
                },
                body: JSON.stringify({ query, top_k: topK })
            });

            if (!response.ok) {
                const contentType = response.headers.get("content-type");
                if (contentType && contentType.includes("application/json")) {
                    const errData = await response.json();
                    throw new Error(errData.detail || 'حدث خطأ أثناء الاتصال بالخادم');
                } else {
                    const text = await response.text();
                    console.error("Non-JSON Server Error:", text);
                    if (response.status === 504 || response.status === 503) {
                        throw new Error("تأخر الخادم في الاستجابة (جاري تهيئة مساحة HuggingFace). يرجى المحاولة مرة أخرى.");
                    }
                    throw new Error(`خطأ في الخادم (الكود ${response.status}) - يرجى الانتظار والمحاولة لاحقاً.`);
                }
            }

            const data = await response.json();
            const results = data.results || [];

            if (results.length === 0) {
                noResults.classList.remove('hidden');
            } else {
                renderResults(results);
            }

        } catch (error) {
            showError(error.message);
        } finally {
            setLoading(false);
        }
    });

    function renderResults(results) {
        results.forEach((result, index) => {
            const delay = index * 0.15; // Staggered animation

            const card = document.createElement('div');
            card.className = 'result-card';
            card.style.animationDelay = `${delay}s`;

            // Topic badge (if exists)
            const topicHtml = result.topic && result.topic !== "nan"
                ? `<span class="badge badge-topic">${result.topic}</span>`
                : '';

            // Source URL link (if exists)
            const sourceHtml = result.source_url && result.source_url !== "nan"
                ? `<a href="${result.source_url}" target="_blank" class="text-gold" style="font-size: 0.9rem; margin-right: 15px;">🔗 المصدر</a>`
                : '';

            card.innerHTML = `
                <div class="result-header">
                    <span class="result-title">${result.title || 'حديث غير معنون'}</span>
                    ${topicHtml}
                    ${sourceHtml}
                    <span class="badge-rank">#${result.rank || (index + 1)}</span>
                </div>
                <div class="result-text">${highlightQuery(result.text, queryInput.value)}</div>
            `;
            resultsContainer.appendChild(card);
        });
    }

    // Basic highlighter for UX (optional enhancement)
    function highlightQuery(text, query) {
        if (!text) return '';
        // Very basic escaping text to prevent XSS
        return text.replace(/</g, "&lt;").replace(/>/g, "&gt;");
    }

    function setLoading(isLoading) {
        if (isLoading) {
            searchBtn.disabled = true;
            searchIcon.classList.add('hidden');
            loadingSpinner.classList.remove('hidden');
        } else {
            searchBtn.disabled = false;
            loadingSpinner.classList.add('hidden');
            searchIcon.classList.remove('hidden');
        }
    }

    function showError(message) {
        errorMsg.textContent = message;
        errorMsg.classList.remove('hidden');
    }

    function hideElements(elements) {
        elements.forEach(el => el.classList.add('hidden'));
    }
});