199 lines
8.6 KiB
HTML
199 lines
8.6 KiB
HTML
{% extends "base.html" %}
|
|
|
|
{% block title %}LabHelper - Home{% endblock %}
|
|
|
|
{% block page_header %}
|
|
<div class="page-header">
|
|
<h1><i class="fas fa-home"></i> Welcome to LabHelper</h1>
|
|
<p class="breadcrumb">Organize and track your lab inventory</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>
|
|
|
|
<div class="section">
|
|
<h2><i class="fas fa-tags"></i> Tags</h2>
|
|
{% if facet_tag_counts %}
|
|
<div style="display: grid; grid-template-columns: repeat(auto-fill, minmax(300px, 1fr)); gap: 20px;">
|
|
{% for facet, tags_with_counts in facet_tag_counts.items %}
|
|
<div class="facet-card" style="background: white; border-radius: 12px; border: 1px solid #e0e0e0; overflow: hidden; box-shadow: 0 2px 8px rgba(0,0,0,0.05);">
|
|
<div class="facet-header" style="padding: 15px 20px; background: linear-gradient(135deg, {{ facet.color }} 0%, {{ facet.color }}dd 100%); color: white; display: flex; align-items: center; justify-content: space-between; cursor: pointer;">
|
|
<div style="display: flex; align-items: center; gap: 10px;">
|
|
<i class="fas fa-chevron-right facet-toggle" style="transition: transform 0.3s;"></i>
|
|
<span style="font-size: 18px; font-weight: 700;">{{ facet.name }}</span>
|
|
</div>
|
|
<span style="background: rgba(255,255,255,0.3); padding: 4px 12px; border-radius: 20px; font-size: 13px; font-weight: 600;">{{ facet.cardinality }}</span>
|
|
</div>
|
|
<div class="facet-tags" style="padding: 15px 20px; display: none;">
|
|
<div style="display: flex; flex-wrap: wrap; gap: 8px;">
|
|
{% for tag, count in tags_with_counts %}
|
|
<a href="/?q={{ facet.name }}:{{ tag.name }}" style="display: inline-block; padding: 6px 12px; background: {{ facet.color }}20; color: {{ facet.color }}; border: 1px solid {{ facet.color }}; border-radius: 15px; text-decoration: none; font-size: 14px; font-weight: 600; transition: all 0.2s;">
|
|
{{ tag.name }}
|
|
<span style="background: {{ facet.color }}; color: white; padding: 1px 8px; border-radius: 10px; margin-left: 6px; font-size: 12px;">{{ count }}</span>
|
|
</a>
|
|
{% endfor %}
|
|
</div>
|
|
</div>
|
|
</div>
|
|
{% endfor %}
|
|
</div>
|
|
{% else %}
|
|
<p style="text-align: center; color: #888; font-size: 16px; padding: 40px;">
|
|
<i class="fas fa-tag" style="font-size: 48px; margin-bottom: 15px; display: block;"></i>
|
|
No tags found.
|
|
</p>
|
|
{% endif %}
|
|
</div>
|
|
{% endblock %}
|
|
|
|
{% block extra_js %}
|
|
<script>
|
|
$(document).ready(function() {
|
|
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;
|
|
}
|
|
|
|
const urlParams = new URLSearchParams(window.location.search);
|
|
const initialQuery = urlParams.get('q');
|
|
|
|
if (initialQuery) {
|
|
searchInput.value = initialQuery;
|
|
performSearch(initialQuery.trim());
|
|
}
|
|
|
|
$('.facet-header').click(function() {
|
|
const $content = $(this).next('.facet-tags');
|
|
const $icon = $(this).find('.facet-toggle');
|
|
|
|
$content.slideToggle(200);
|
|
if ($content.is(':visible')) {
|
|
$icon.css('transform', 'rotate(90deg)');
|
|
} else {
|
|
$icon.css('transform', 'rotate(0deg)');
|
|
}
|
|
});
|
|
|
|
$('.facet-card a').hover(
|
|
function() {
|
|
$(this).css('transform', 'scale(1.05)');
|
|
},
|
|
function() {
|
|
$(this).css('transform', 'scale(1)');
|
|
}
|
|
);
|
|
});
|
|
</script>
|
|
{% endblock %}
|