File size: 6,200 Bytes
8ed13f7
 
98f8ae8
8ed13f7
 
 
 
 
98f8ae8
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
8ed13f7
98f8ae8
 
 
 
 
 
 
 
 
8ed13f7
98f8ae8
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
72be969
98f8ae8
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
8ed13f7
 
98f8ae8
 
 
 
 
 
 
 
 
8ed13f7
98f8ae8
 
8ed13f7
98f8ae8
8ed13f7
98f8ae8
 
 
 
8ed13f7
 
 
 
98f8ae8
 
72be969
98f8ae8
72be969
 
98f8ae8
72be969
 
98f8ae8
 
 
 
 
 
72be969
 
98f8ae8
72be969
98f8ae8
 
 
 
 
 
 
8ed13f7
98f8ae8
 
 
8ed13f7
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
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
// search.js - Complete MiniSearch implementation with artifacts
import { renderSearchTags } from './tags.js';

// Use global areasData (loaded in index.html <head>)
const areasData = window.areasData;

let miniSearch;
let allArtifacts;

// Helper to create and populate a MiniSearch instance
function createMiniSearchIndex(data, storeFields) {
    const search = new MiniSearch({
        fields: ['title', 'description', 'areas', 'topics'],
        storeFields: storeFields
    });
    search.addAll(data);
    return search;
}

// Initialize search
export async function initializeSearch(artifactsData) {
    allArtifacts = artifactsData;
    
    // Prepare data for MiniSearch
    const searchData = allArtifacts.map((artifact, index) => ({
        id: index,
        title: artifact.title,
        description: artifact.description,
        type: artifact.type,
        areas: (artifact.areas || []).join(' '),
        topics: (artifact.topics || []).join(' '),
        url: artifact.url,
        date: artifact.date
    }));
    
    // Initialize MiniSearch for artifacts
    miniSearch = createMiniSearchIndex(searchData, ['title', 'description', 'type', 'areas', 'topics', 'url', 'date']);
}

export function searchContent(query) {
    if (!query || query.trim().length < 2) {
        return { artifacts: [] };
    }
    
    const artifactResults = miniSearch.search(query, {
        prefix: true,
        fuzzy: 0.2,
        boost: { title: 2, description: 1 }
    });
    
    return {
        artifacts: artifactResults
    };
}

// Helper functions
function getAreaDisplayName(area) {
    return areasData[area]?.title || area;
}

function getSubAreaDisplayName(areaId, subArea) {
    const area = areasData[areaId];
    if (!area || !area.subAreas) return subArea;
    const subAreaData = area.subAreas[subArea];
    return typeof subAreaData === 'string' ? subAreaData : subAreaData?.name || subArea;
}

// Search UI
export function initializeSearchUI(artifactsData) {
    initializeSearch(artifactsData);
    
    const searchInput = document.getElementById('search-input');
    const searchResults = document.getElementById('search-results');
    
    if (!searchInput || !searchResults) return;
    
    let searchTimeout;
    
    // Function to perform search
    function performSearch() {
        const query = searchInput.value.trim();
        
        if (query.length < 2) {
            searchResults.innerHTML = `<div class="text-gray-500 text-center py-8"><p>Enter a search term...</p></div>`;
            return;
        }
        
        const results = searchContent(query);
        displaySearchResults(results, query);
    }
    
    // Handle input events (typing)
    searchInput.addEventListener('input', (e) => {
        clearTimeout(searchTimeout);
        const query = e.target.value.trim();
        
        if (query.length < 2) {
            searchResults.innerHTML = `<div class="text-gray-500 text-center py-8"><p>Enter a search term...</p></div>`;
            return;
        }
        
        // Debounce search
        searchTimeout = setTimeout(() => {
            performSearch();
        }, 300);
    });
    
    // Handle Enter key (immediate search)
    searchInput.addEventListener('keydown', (e) => {
        if (e.key === 'Enter') {
            e.preventDefault();
            clearTimeout(searchTimeout);
            performSearch();
        }
    });
}

// Update the displaySearchResults function in search.js
export function displaySearchResults(results, query) {
    const { artifacts } = results;
    const totalResults = artifacts.length;
    
    if (totalResults === 0) {
        document.getElementById('search-results').innerHTML = `
            <div class="text-gray-500 text-center py-8"><p>No results found for "${query}"</p></div>
        `;
        return;
    }
    
    let html = `<div class="text-sm text-gray-600 mb-4">
        Found ${totalResults} result${totalResults === 1 ? '' : 's'}
    </div>`;
    
    // Display artifacts
    if (artifacts.length > 0) {
        html += `<div class="space-y-2">`;
        
        artifacts.forEach(result => {
            const score = Math.round(result.score * 100);
            
            // Use renderSearchTags utility
            const areas = result.areas ? result.areas.split(' ') : [];
            const topics = result.topics ? result.topics.split(' ') : [];
            const { areaLinksHtml: areaLinks, topicLinksHtml: subAreaLinks } = renderSearchTags(areas, topics);
            
            html += `
                <div class="p-3 bg-gray-50 rounded-lg border border-gray-200">
                    <div class="flex items-center justify-between mb-2">
                        <h5 class="font-medium text-sm text-gray-900 leading-snug">${result.title}</h5>
                        <div class="flex items-center gap-2 flex-shrink-0 ml-2">
                            <span class="text-xs text-gray-500">${score}%</span>
                            <a href="${result.url}" target="_blank" class="text-gray-400 hover:text-gray-600 transition-colors" aria-label="Open ${result.title}">
                                <svg class="w-4 h-4" fill="none" stroke="currentColor" viewBox="0 0 24 24" aria-hidden="true">
                                    <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M10 6H6a2 2 0 00-2 2v10a2 2 0 002 2h10a2 2 0 002-2v-4M14 4h6m0 0v6m0-6L10 14"></path>
                                </svg>
                            </a>
                        </div>
                    </div>
                    <div class="flex items-center gap-2 mb-2 text-xs text-gray-600">
                        <span class="px-2 py-0.5 bg-gray-200 text-gray-700 rounded font-medium">${result.type}</span>
                        <span class="text-gray-500">${result.date}</span>
                    </div>
                    <div class="flex flex-wrap gap-1.5">
                        ${areaLinks}
                        ${subAreaLinks}
                    </div>
                </div>
            `;
        });
        
        html += `</div>`;
    }
    
    document.getElementById('search-results').innerHTML = html;
}