diff --git a/frontend/base.html b/frontend/base.html
deleted file mode 100644
index 596163a..0000000
--- a/frontend/base.html
+++ /dev/null
@@ -1,89 +0,0 @@
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
Welcome to Shoplazza Base Search
-
Enter keywords to search for products
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
diff --git a/frontend/static/js/app.js b/frontend/static/js/app.js
index 94f5f87..71b7cf2 100644
--- a/frontend/static/js/app.js
+++ b/frontend/static/js/app.js
@@ -608,7 +608,7 @@ function displayDebugInfo(data) {
// ES Query
if (debugInfo.es_query) {
html += '
-
- ${spu.image_url ? `
-
})
- ` : `
-
No Image
- `}
-
-
-
- ${spu.price ? `$${spu.price.toFixed(2)}` : 'N/A'}${spu.compare_at_price && spu.compare_at_price > spu.price ? `$${spu.compare_at_price.toFixed(2)}` : ''}
-
-
-
- ${spu.in_stock ? 'In Stock' : 'Out of Stock'}
- ${spu.skus && spu.skus.length > 0 ? `(${spu.skus.length} skus)` : ''}
-
-
-
- ${escapeHtml(spu.title || 'N/A')}
-
-
-
- `;
- });
-
- grid.innerHTML = html;
-}
-
-// Display facets as filter tags (重构版 - 标准化格式)
-function displayFacets(facets) {
- if (!facets) return;
-
- facets.forEach(facet => {
- // 根据字段名找到对应的容器
- let containerId = null;
- let maxDisplay = 10;
-
- if (facet.field === 'category.keyword') {
- containerId = 'categoryTags';
- maxDisplay = 10;
- } else if (facet.field === 'vendor.keyword') {
- containerId = 'brandTags';
- maxDisplay = 10;
- } else if (facet.field === 'tags.keyword') {
- containerId = 'supplierTags';
- maxDisplay = 8;
- }
-
- if (!containerId) return;
-
- const container = document.getElementById(containerId);
- if (!container) return;
-
- let html = '';
-
- // 渲染分面值
- facet.values.slice(0, maxDisplay).forEach(facetValue => {
- const value = facetValue.value;
- const count = facetValue.count;
- const selected = facetValue.selected;
-
- html += `
-
- ${escapeHtml(value)} (${count})
-
- `;
- });
-
- container.innerHTML = html;
- });
-}
-
-// Toggle filter
-function toggleFilter(field, value) {
- if (!state.filters[field]) {
- state.filters[field] = [];
- }
-
- const index = state.filters[field].indexOf(value);
- if (index > -1) {
- state.filters[field].splice(index, 1);
- if (state.filters[field].length === 0) {
- delete state.filters[field];
- }
- } else {
- state.filters[field].push(value);
- }
-
- performSearch(1); // Reset to page 1
-}
-
-// Handle price filter (重构版 - 使用 rangeFilters)
-function handlePriceFilter(value) {
- if (!value) {
- delete state.rangeFilters.price;
- } else {
- const priceRanges = {
- '0-50': { lt: 50 },
- '50-100': { gte: 50, lt: 100 },
- '100-200': { gte: 100, lt: 200 },
- '200+': { gte: 200 }
- };
-
- if (priceRanges[value]) {
- state.rangeFilters.price = priceRanges[value];
- }
- }
-
- performSearch(1);
-}
-
-// Handle time filter (重构版 - 使用 rangeFilters)
-function handleTimeFilter(value) {
- if (!value) {
- delete state.rangeFilters.create_time;
- } else {
- const now = new Date();
- let fromDate;
-
- switch(value) {
- case 'today':
- fromDate = new Date(now.setHours(0, 0, 0, 0));
- break;
- case 'week':
- fromDate = new Date(now.setDate(now.getDate() - 7));
- break;
- case 'month':
- fromDate = new Date(now.setMonth(now.getMonth() - 1));
- break;
- case '3months':
- fromDate = new Date(now.setMonth(now.getMonth() - 3));
- break;
- case '6months':
- fromDate = new Date(now.setMonth(now.getMonth() - 6));
- break;
- }
-
- if (fromDate) {
- state.rangeFilters.create_time = {
- gte: fromDate.toISOString()
- };
- }
- }
-
- performSearch(1);
-}
-
-// Clear all filters
-function clearAllFilters() {
- state.filters = {};
- state.rangeFilters = {};
- document.getElementById('priceFilter').value = '';
- document.getElementById('timeFilter').value = '';
- performSearch(1);
-}
-
-// Update clear filters button visibility
-function updateClearFiltersButton() {
- const btn = document.getElementById('clearFiltersBtn');
- if (Object.keys(state.filters).length > 0 || Object.keys(state.rangeFilters).length > 0) {
- btn.style.display = 'inline-block';
- } else {
- btn.style.display = 'none';
- }
-}
-
-// Update product count
-function updateProductCount(total) {
- document.getElementById('productCount').textContent = `${total.toLocaleString()} SPUs found`;
-}
-
-// Sort functions
-function setSortByDefault() {
- // Remove active from all buttons and arrows
- document.querySelectorAll('.sort-btn').forEach(b => b.classList.remove('active'));
- document.querySelectorAll('.arrow-up, .arrow-down').forEach(a => a.classList.remove('active'));
-
- // Set default button active
- const defaultBtn = document.querySelector('.sort-btn[data-sort=""]');
- if (defaultBtn) defaultBtn.classList.add('active');
-
- state.sortBy = '';
- state.sortOrder = 'desc';
-
- performSearch(1);
-}
-
-function sortByField(field, order) {
- state.sortBy = field;
- state.sortOrder = order;
-
- // Remove active from all buttons (but keep "By default" if no sort)
- document.querySelectorAll('.sort-btn').forEach(b => b.classList.remove('active'));
-
- // Remove active from all arrows
- document.querySelectorAll('.arrow-up, .arrow-down').forEach(a => a.classList.remove('active'));
-
- // Add active to clicked arrow
- const activeArrow = document.querySelector(`.arrow-up[data-field="${field}"][data-order="${order}"], .arrow-down[data-field="${field}"][data-order="${order}"]`);
- if (activeArrow) {
- activeArrow.classList.add('active');
- }
-
- performSearch(state.currentPage);
-}
-
-// Pagination
-function displayPagination() {
- const paginationDiv = document.getElementById('pagination');
-
- if (state.totalResults <= state.pageSize) {
- paginationDiv.style.display = 'none';
- return;
- }
-
- paginationDiv.style.display = 'flex';
-
- const totalPages = Math.ceil(state.totalResults / state.pageSize);
- const currentPage = state.currentPage;
-
- let html = `
-
- `;
-
- // Page numbers
- const maxVisible = 5;
- let startPage = Math.max(1, currentPage - Math.floor(maxVisible / 2));
- let endPage = Math.min(totalPages, startPage + maxVisible - 1);
-
- if (endPage - startPage < maxVisible - 1) {
- startPage = Math.max(1, endPage - maxVisible + 1);
- }
-
- if (startPage > 1) {
- html += `
`;
- if (startPage > 2) {
- html += `
...`;
- }
- }
-
- for (let i = startPage; i <= endPage; i++) {
- html += `
-
- `;
- }
-
- if (endPage < totalPages) {
- if (endPage < totalPages - 1) {
- html += `
...`;
- }
- html += `
`;
- }
-
- html += `
-
- `;
-
- html += `
-
- Page ${currentPage} of ${totalPages} (${state.totalResults.toLocaleString()} results)
-
- `;
-
- paginationDiv.innerHTML = html;
-}
-
-function goToPage(page) {
- const totalPages = Math.ceil(state.totalResults / state.pageSize);
- if (page < 1 || page > totalPages) return;
-
- performSearch(page);
-
- // Scroll to top
- window.scrollTo({ top: 0, behavior: 'smooth' });
-}
-
-// Display debug info
-function displayDebugInfo(data) {
- const debugInfoDiv = document.getElementById('debugInfo');
-
- if (!state.debug || !data.debug_info) {
- // If debug mode is off or no debug info, show basic query info
- if (data.query_info) {
- let html = '
';
- html += `
original_query: ${escapeHtml(data.query_info.original_query || 'N/A')}
`;
- html += `
detected_language: ${getLanguageName(data.query_info.detected_language)}
`;
- html += '
';
- debugInfoDiv.innerHTML = html;
- } else {
- debugInfoDiv.innerHTML = '';
- }
- return;
- }
-
- // Display comprehensive debug info when debug mode is on
- const debugInfo = data.debug_info;
- let html = '
';
-
- // Query Analysis
- if (debugInfo.query_analysis) {
- html += '
Query Analysis:';
- html += `
original_query: ${escapeHtml(debugInfo.query_analysis.original_query || 'N/A')}
`;
- html += `
normalized_query: ${escapeHtml(debugInfo.query_analysis.normalized_query || 'N/A')}
`;
- html += `
rewritten_query: ${escapeHtml(debugInfo.query_analysis.rewritten_query || 'N/A')}
`;
- html += `
detected_language: ${getLanguageName(debugInfo.query_analysis.detected_language)}
`;
- html += `
domain: ${escapeHtml(debugInfo.query_analysis.domain || 'default')}
`;
- html += `
is_simple_query: ${debugInfo.query_analysis.is_simple_query ? 'yes' : 'no'}
`;
-
- if (debugInfo.query_analysis.translations && Object.keys(debugInfo.query_analysis.translations).length > 0) {
- html += '
translations: ';
- for (const [lang, translation] of Object.entries(debugInfo.query_analysis.translations)) {
- if (translation) {
- html += `${getLanguageName(lang)}: ${escapeHtml(translation)}; `;
- }
- }
- html += '
';
- }
-
- if (debugInfo.query_analysis.boolean_ast) {
- html += `
boolean_ast: ${escapeHtml(debugInfo.query_analysis.boolean_ast)}
`;
- }
-
- html += `
has_vector: ${debugInfo.query_analysis.has_vector ? 'enabled' : 'disabled'}
`;
- html += '
';
- }
-
- // Feature Flags
- if (debugInfo.feature_flags) {
- html += '
Feature Flags:';
- html += `
translation_enabled: ${debugInfo.feature_flags.translation_enabled ? 'enabled' : 'disabled'}
`;
- html += `
embedding_enabled: ${debugInfo.feature_flags.embedding_enabled ? 'enabled' : 'disabled'}
`;
- html += `
rerank_enabled: ${debugInfo.feature_flags.rerank_enabled ? 'enabled' : 'disabled'}
`;
- html += '
';
- }
-
- // ES Response
- if (debugInfo.es_response) {
- html += '
ES Response:';
- html += `
took_ms: ${debugInfo.es_response.took_ms}ms
`;
- html += `
total_hits: ${debugInfo.es_response.total_hits}
`;
- html += `
max_score: ${debugInfo.es_response.max_score?.toFixed(3) || 0}
`;
- html += '
';
- }
-
- // Stage Timings
- if (debugInfo.stage_timings) {
- html += '
Stage Timings:';
- for (const [stage, duration] of Object.entries(debugInfo.stage_timings)) {
- html += `
${stage}: ${duration.toFixed(2)}ms
`;
- }
- html += '
';
- }
-
- // ES Query
- if (debugInfo.es_query) {
- html += '
ES Query DSL:';
- html += `
${escapeHtml(JSON.stringify(debugInfo.es_query, null, 2))}`;
- html += '
';
- }
-
- html += '
';
- debugInfoDiv.innerHTML = html;
-}
-
-// Helper functions
-function escapeHtml(text) {
- if (!text) return '';
- const div = document.createElement('div');
- div.textContent = text;
- return div.innerHTML;
-}
-
-function escapeAttr(text) {
- if (!text) return '';
- return text.replace(/'/g, "\\'").replace(/"/g, '"');
-}
-
-function formatDate(dateStr) {
- if (!dateStr) return '';
- try {
- const date = new Date(dateStr);
- return date.toLocaleDateString('zh-CN');
- } catch {
- return dateStr;
- }
-}
-
-function getLanguageName(code) {
- const names = {
- 'zh': '中文',
- 'en': 'English',
- 'ru': 'Русский',
- 'ar': 'العربية',
- 'ja': '日本語',
- 'unknown': 'Unknown'
- };
- return names[code] || code;
-}
--
libgit2 0.21.2