All checks were successful
Build containers when image tags change / build-if-image-changed (., web, containers, main container, git.baumann.gr/adebaumann/labhelper) (push) Successful in 1m3s
Build containers when image tags change / build-if-image-changed (data-loader, loader, initContainers, init-container, git.baumann.gr/adebaumann/labhelper-data-loader) (push) Successful in 12s
146 lines
5.6 KiB
HTML
146 lines
5.6 KiB
HTML
{% extends "base.html" %}
|
|
|
|
{% block title %}Search - LabHelper{% endblock %}
|
|
|
|
{% block page_header %}
|
|
<div class="page-header">
|
|
<h1><i class="fas fa-search"></i> Search Things</h1>
|
|
<p class="breadcrumb">
|
|
<a href="/"><i class="fas fa-home"></i> Home</a> / Search
|
|
</p>
|
|
</div>
|
|
{% endblock %}
|
|
|
|
{% block content %}
|
|
<div class="section">
|
|
<input type="text"
|
|
id="search-input"
|
|
placeholder="Search for things..."
|
|
style="width: 100%; padding: 16px 20px; font-size: 18px; border: 2px solid #e0e0e0; border-radius: 12px; box-sizing: border-box; transition: all 0.3s;"
|
|
{% if request.GET.q %}value="{{ request.GET.q }}"{% endif %}>
|
|
<p style="color: #888; font-size: 14px; margin-top: 10px;">
|
|
<i class="fas fa-info-circle"></i> Type at least 2 characters to search
|
|
</p>
|
|
</div>
|
|
|
|
<div id="results-container" style="display: none;">
|
|
<div class="section" style="overflow-x: auto; padding: 0;">
|
|
<table style="width: 100%; border-collapse: collapse;">
|
|
<thead>
|
|
<tr style="background: linear-gradient(135deg, #667eea 0%, #764ba2 100%); color: white;">
|
|
<th style="padding: 15px 20px; text-align: left; font-weight: 600;">Name</th>
|
|
<th style="padding: 15px 20px; text-align: left; font-weight: 600;">Tags</th>
|
|
<th style="padding: 15px 20px; text-align: left; font-weight: 600;">Box</th>
|
|
<th style="padding: 15px 20px; text-align: left; font-weight: 600;">Description</th>
|
|
</tr>
|
|
</thead>
|
|
<tbody id="results-body">
|
|
</tbody>
|
|
</table>
|
|
</div>
|
|
</div>
|
|
|
|
<div id="no-results" class="section" style="text-align: center; padding: 60px 30px; display: none;">
|
|
<i class="fas fa-search-minus" style="font-size: 64px; color: #ddd; margin-bottom: 20px; display: block;"></i>
|
|
<h3 style="color: #888; font-size: 20px;">No results found</h3>
|
|
<p style="color: #999; margin-top: 10px;">Try different keywords or browse the full inventory.</p>
|
|
</div>
|
|
{% endblock %}
|
|
|
|
{% block extra_js %}
|
|
<script>
|
|
const searchInput = document.getElementById('search-input');
|
|
const resultsContainer = document.getElementById('results-container');
|
|
const resultsBody = document.getElementById('results-body');
|
|
const noResults = document.getElementById('no-results');
|
|
|
|
let searchTimeout = null;
|
|
|
|
function performSearch(query) {
|
|
if (query.length < 2) {
|
|
resultsContainer.style.display = 'none';
|
|
noResults.style.display = 'none';
|
|
return;
|
|
}
|
|
|
|
searchInput.style.borderColor = '#667eea';
|
|
searchInput.style.boxShadow = '0 0 0 3px rgba(102, 126, 234, 0.1)';
|
|
|
|
searchTimeout = setTimeout(function() {
|
|
fetch('/search/api/?q=' + encodeURIComponent(query))
|
|
.then(response => response.json())
|
|
.then(data => {
|
|
resultsBody.innerHTML = '';
|
|
|
|
if (data.results.length === 0) {
|
|
resultsContainer.style.display = 'none';
|
|
noResults.style.display = 'block';
|
|
return;
|
|
}
|
|
|
|
noResults.style.display = 'none';
|
|
resultsContainer.style.display = 'block';
|
|
|
|
data.results.forEach(function(thing) {
|
|
const row = document.createElement('tr');
|
|
row.style.borderBottom = '1px solid #e0e0e0';
|
|
row.style.transition = 'background 0.2s';
|
|
|
|
let tagsHtml = thing.tags.length > 0
|
|
? thing.tags.map(tag =>
|
|
'<span style="display: inline-block; padding: 3px 8px; margin: 2px; border-radius: 12px; font-size: 11px; background: ' + escapeHtml(tag.color) + '20; color: ' + escapeHtml(tag.color) + '; border: 1px solid ' + escapeHtml(tag.color) + '40;">' + escapeHtml(tag.name) + '</span>'
|
|
).join('')
|
|
: '<span style="color: #999; font-style: italic; font-size: 13px;">-</span>';
|
|
|
|
row.innerHTML =
|
|
'<td style="padding: 15px 20px;"><a href="/thing/' + thing.id + '/">' + escapeHtml(thing.name) + '</a></td>' +
|
|
'<td style="padding: 15px 20px;">' + tagsHtml + '</td>' +
|
|
'<td style="padding: 15px 20px;"><a href="/box/' + escapeHtml(thing.box) + '/">' + escapeHtml(thing.box) + '</a></td>' +
|
|
'<td style="padding: 15px 20px; color: #777;" class="description">' + escapeHtml(thing.description) + '</td>';
|
|
|
|
row.addEventListener('mouseenter', function() {
|
|
this.style.background = '#f8f9fa';
|
|
});
|
|
row.addEventListener('mouseleave', function() {
|
|
this.style.background = 'white';
|
|
});
|
|
|
|
resultsBody.appendChild(row);
|
|
});
|
|
});
|
|
}, 200);
|
|
}
|
|
|
|
searchInput.addEventListener('input', function() {
|
|
const query = this.value.trim();
|
|
|
|
if (searchTimeout) {
|
|
clearTimeout(searchTimeout);
|
|
}
|
|
|
|
performSearch(query);
|
|
});
|
|
|
|
searchInput.addEventListener('blur', function() {
|
|
searchInput.style.borderColor = '#e0e0e0';
|
|
searchInput.style.boxShadow = 'none';
|
|
});
|
|
|
|
function escapeHtml(text) {
|
|
const div = document.createElement('div');
|
|
div.textContent = text;
|
|
return div.innerHTML;
|
|
}
|
|
|
|
// Check for query parameter on page load
|
|
const urlParams = new URLSearchParams(window.location.search);
|
|
const initialQuery = urlParams.get('q');
|
|
|
|
if (initialQuery) {
|
|
searchInput.value = initialQuery;
|
|
performSearch(initialQuery.trim());
|
|
}
|
|
|
|
searchInput.focus();
|
|
</script>
|
|
{% endblock %} |