Update templates/search.html
Browse files- templates/search.html +75 -23
templates/search.html
CHANGED
@@ -71,6 +71,12 @@
|
|
71 |
color: #333;
|
72 |
margin: 0;
|
73 |
}
|
|
|
|
|
|
|
|
|
|
|
|
|
74 |
@media (max-width: 576px) {
|
75 |
body {
|
76 |
padding: 15px;
|
@@ -103,17 +109,7 @@
|
|
103 |
<i class="bi bi-search search-icon"></i>
|
104 |
</div>
|
105 |
<div id="searchResults">
|
106 |
-
|
107 |
-
<div class="item-card"
|
108 |
-
data-item-name="{{ item.Name | default('Unnamed Item') | e }}"
|
109 |
-
data-item-section="{{ item.Section__c | default('Unknown') | e }}"
|
110 |
-
onclick="redirectToMenu('{{ item.Name | default('Unnamed Item') | e }}')">
|
111 |
-
<img src="{{ item.Image2__c | default('/static/placeholder.jpg') }}"
|
112 |
-
alt="{{ item.Name | default('Unnamed Item') }}"
|
113 |
-
class="item-image">
|
114 |
-
<div class="item-name">{{ item.Name | default('Unnamed Item') }}</div>
|
115 |
-
</div>
|
116 |
-
{% endfor %}
|
117 |
</div>
|
118 |
</div>
|
119 |
|
@@ -123,25 +119,81 @@
|
|
123 |
window.location.href = `/menu?highlight=${encodedItemName}`;
|
124 |
}
|
125 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
126 |
document.addEventListener('DOMContentLoaded', function () {
|
127 |
const searchBar = document.getElementById('searchBar');
|
128 |
const searchResults = document.getElementById('searchResults');
|
129 |
-
const allItems = Array.from(searchResults.querySelectorAll('.item-card'));
|
130 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
131 |
searchBar.addEventListener('input', function () {
|
132 |
-
const query = this.value.trim()
|
133 |
-
|
134 |
-
const itemName = item.getAttribute('data-item-name').toLowerCase();
|
135 |
-
if (itemName.includes(query)) {
|
136 |
-
item.style.display = 'flex';
|
137 |
-
} else {
|
138 |
-
item.style.display = 'none';
|
139 |
-
}
|
140 |
-
});
|
141 |
});
|
142 |
|
143 |
-
//
|
144 |
-
|
145 |
});
|
146 |
</script>
|
147 |
</body>
|
|
|
71 |
color: #333;
|
72 |
margin: 0;
|
73 |
}
|
74 |
+
.no-results {
|
75 |
+
text-align: center;
|
76 |
+
color: #666;
|
77 |
+
font-size: 1.1rem;
|
78 |
+
margin-top: 20px;
|
79 |
+
}
|
80 |
@media (max-width: 576px) {
|
81 |
body {
|
82 |
padding: 15px;
|
|
|
109 |
<i class="bi bi-search search-icon"></i>
|
110 |
</div>
|
111 |
<div id="searchResults">
|
112 |
+
<!-- Results will be populated dynamically -->
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
113 |
</div>
|
114 |
</div>
|
115 |
|
|
|
119 |
window.location.href = `/menu?highlight=${encodedItemName}`;
|
120 |
}
|
121 |
|
122 |
+
function sanitizeInput(input) {
|
123 |
+
const div = document.createElement('div');
|
124 |
+
div.textContent = input;
|
125 |
+
return div.innerHTML;
|
126 |
+
}
|
127 |
+
|
128 |
+
function debounce(func, wait) {
|
129 |
+
let timeout;
|
130 |
+
return function (...args) {
|
131 |
+
clearTimeout(timeout);
|
132 |
+
timeout = setTimeout(() => func.apply(this, args), wait);
|
133 |
+
};
|
134 |
+
}
|
135 |
+
|
136 |
document.addEventListener('DOMContentLoaded', function () {
|
137 |
const searchBar = document.getElementById('searchBar');
|
138 |
const searchResults = document.getElementById('searchResults');
|
|
|
139 |
|
140 |
+
// Focus the search bar on page load
|
141 |
+
searchBar.focus();
|
142 |
+
|
143 |
+
// Debounced search function
|
144 |
+
const debouncedSearch = debounce(function (query) {
|
145 |
+
if (!query) {
|
146 |
+
searchResults.innerHTML = '<p class="no-results">Please type to search for items.</p>';
|
147 |
+
return;
|
148 |
+
}
|
149 |
+
|
150 |
+
// Fetch items from the server
|
151 |
+
fetch(`/api/search-items?query=${encodeURIComponent(query)}`)
|
152 |
+
.then(response => {
|
153 |
+
if (!response.ok) throw new Error('Network response was not ok');
|
154 |
+
return response.json();
|
155 |
+
})
|
156 |
+
.then(data => {
|
157 |
+
searchResults.innerHTML = '';
|
158 |
+
if (data.items && data.items.length > 0) {
|
159 |
+
data.items.forEach(item => {
|
160 |
+
const itemCard = document.createElement('div');
|
161 |
+
itemCard.className = 'item-card';
|
162 |
+
itemCard.setAttribute('data-item-name', sanitizeInput(item.Name));
|
163 |
+
itemCard.setAttribute('data-item-section', sanitizeInput(item.Section__c || 'Unknown'));
|
164 |
+
itemCard.onclick = () => redirectToMenu(item.Name);
|
165 |
+
|
166 |
+
const itemImage = document.createElement('img');
|
167 |
+
itemImage.src = item.Image2__c || '/static/placeholder.jpg';
|
168 |
+
itemImage.alt = item.Name || 'Unnamed Item';
|
169 |
+
itemImage.className = 'item-image';
|
170 |
+
|
171 |
+
const itemNameDiv = document.createElement('div');
|
172 |
+
itemNameDiv.className = 'item-name';
|
173 |
+
itemNameDiv.textContent = item.Name || 'Unnamed Item';
|
174 |
+
|
175 |
+
itemCard.appendChild(itemImage);
|
176 |
+
itemCard.appendChild(itemNameDiv);
|
177 |
+
searchResults.appendChild(itemCard);
|
178 |
+
});
|
179 |
+
} else {
|
180 |
+
searchResults.innerHTML = '<p class="no-results">No items found.</p>';
|
181 |
+
}
|
182 |
+
})
|
183 |
+
.catch(error => {
|
184 |
+
console.error('Error fetching search results:', error);
|
185 |
+
searchResults.innerHTML = '<p class="no-results">Error loading items. Please try again.</p>';
|
186 |
+
});
|
187 |
+
}, 300);
|
188 |
+
|
189 |
+
// Trigger search on input
|
190 |
searchBar.addEventListener('input', function () {
|
191 |
+
const query = this.value.trim();
|
192 |
+
debouncedSearch(query);
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
193 |
});
|
194 |
|
195 |
+
// Initial state when no items are present
|
196 |
+
searchResults.innerHTML = '<p class="no-results">Please type to search for items.</p>';
|
197 |
});
|
198 |
</script>
|
199 |
</body>
|