320 lines
13 KiB
HTML
320 lines
13 KiB
HTML
{% extends "base.html" %}
|
|
|
|
{% block title %}Search Results - Media Inventory App{% endblock %}
|
|
|
|
{% block content %}
|
|
<div class="row">
|
|
<div class="col-12">
|
|
<div class="d-flex justify-content-between align-items-center mb-4">
|
|
<h2>Search Results</h2>
|
|
<a href="/" class="btn btn-outline-primary">🔍 New Search</a>
|
|
</div>
|
|
|
|
<div class="alert alert-info">
|
|
<strong>Query:</strong> "{{ query }}"
|
|
<strong>Media Type:</strong>
|
|
{% if media_type == 'book' %}📚 Book{% endif %}
|
|
{% if media_type == 'vinyl' %}🎵 Vinyl Record{% endif %}
|
|
{% if media_type == 'cd' %}💿 CD{% endif %}
|
|
{% if media_type == 'cassette' %}📼 Cassette{% endif %}
|
|
</div>
|
|
|
|
{% if results %}
|
|
<div class="row">
|
|
{% for item in results %}
|
|
<div class="col-md-6 col-lg-4 mb-4">
|
|
<div class="card h-100">
|
|
{% if item.cover_url %}
|
|
<img src="{{ item.cover_url }}" class="card-img-top" alt="Cover"
|
|
style="height: 200px; object-fit: cover;">
|
|
{% else %}
|
|
<div class="card-img-top bg-light d-flex align-items-center justify-content-center"
|
|
style="height: 200px;">
|
|
<span class="text-muted">No Image</span>
|
|
</div>
|
|
{% endif %}
|
|
|
|
<div class="card-body d-flex flex-column">
|
|
<h5 class="card-title">{{ item.title }}</h5>
|
|
|
|
{% if media_type == 'book' %}
|
|
<p class="card-text">
|
|
<strong>Author:</strong> {{ item.author }}<br>
|
|
<strong>Year:</strong> {{ item.year }}<br>
|
|
{% if item.isbn %}
|
|
<strong>ISBN:</strong> {{ item.isbn }}<br>
|
|
{% endif %}
|
|
</p>
|
|
{% else %}
|
|
<p class="card-text">
|
|
<strong>Artist:</strong> {{ item.artist }}<br>
|
|
<strong>Year:</strong> {{ item.year }}<br>
|
|
<strong>Label:</strong> {{ item.label }}<br>
|
|
<strong>Format:</strong> {{ item.format }}
|
|
</p>
|
|
{% endif %}
|
|
|
|
<div class="mt-auto">
|
|
<div class="d-flex gap-2 flex-wrap">
|
|
{% if media_type == 'book' and item.openlibrary_url %}
|
|
<a href="{{ item.openlibrary_url }}" target="_blank"
|
|
class="btn btn-outline-primary btn-sm">
|
|
📚 View on OpenLibrary
|
|
</a>
|
|
{% elif media_type != 'book' and item.discogs_url %}
|
|
<a href="{{ item.discogs_url }}" target="_blank"
|
|
class="btn btn-outline-primary btn-sm">
|
|
🎵 View on Discogs
|
|
</a>
|
|
{% endif %}
|
|
|
|
<button class="btn btn-success btn-sm"
|
|
onclick="addToInventory({{ loop.index0 }})">
|
|
📦 Add to Inventory
|
|
</button>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
{% endfor %}
|
|
</div>
|
|
{% else %}
|
|
<div class="alert alert-warning">
|
|
<h4>No results found</h4>
|
|
<p>Try adjusting your search terms or selecting a different media type.</p>
|
|
<a href="/" class="btn btn-primary">🔍 Try Another Search</a>
|
|
</div>
|
|
{% endif %}
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Add to Inventory Modal -->
|
|
<div class="modal fade" id="addToInventoryModal" tabindex="-1">
|
|
<div class="modal-dialog">
|
|
<div class="modal-content">
|
|
<div class="modal-header">
|
|
<h5 class="modal-title">📦 Add to Inventory</h5>
|
|
<button type="button" class="btn-close" data-bs-dismiss="modal"></button>
|
|
</div>
|
|
<div class="modal-body">
|
|
<div id="inventoryItemDetails" class="mb-3">
|
|
<!-- Item details will be populated here -->
|
|
</div>
|
|
|
|
<form id="addToInventoryForm">
|
|
<div class="mb-3">
|
|
<label for="storageLocation" class="form-label">Storage Location *</label>
|
|
<select class="form-select" id="storageLocation" name="storage_location_id" required>
|
|
<option value="">Loading locations...</option>
|
|
</select>
|
|
<div class="form-text">
|
|
<a href="/inventory" target="_blank">Manage storage locations</a>
|
|
</div>
|
|
</div>
|
|
|
|
<div class="mb-3">
|
|
<label for="quantity" class="form-label">Quantity</label>
|
|
<input type="number" class="form-control" id="quantity" name="quantity"
|
|
value="1" min="1" step="1">
|
|
</div>
|
|
|
|
<div class="mb-3">
|
|
<label for="notes" class="form-label">Notes</label>
|
|
<textarea class="form-control" id="notes" name="notes" rows="2"
|
|
placeholder="Optional notes about this item"></textarea>
|
|
</div>
|
|
</form>
|
|
|
|
<div id="inventoryError" class="alert alert-danger d-none"></div>
|
|
<div id="inventorySuccess" class="alert alert-success d-none"></div>
|
|
</div>
|
|
<div class="modal-footer">
|
|
<button type="button" class="btn btn-secondary" data-bs-dismiss="modal">Cancel</button>
|
|
<button type="button" class="btn btn-success" onclick="submitToInventory()" id="submitInventoryBtn">
|
|
📦 Add to Inventory
|
|
</button>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<script>
|
|
let currentMediaItem = null;
|
|
let storageLocations = [];
|
|
let searchResults = {{ results | tojson | safe }};
|
|
|
|
// Load storage locations when page loads
|
|
document.addEventListener('DOMContentLoaded', function() {
|
|
loadStorageLocations();
|
|
});
|
|
|
|
async function loadStorageLocations() {
|
|
try {
|
|
const response = await fetch('/api/storage-locations');
|
|
const data = await response.json();
|
|
|
|
if (data.error) {
|
|
console.error('Error loading storage locations:', data.error);
|
|
return;
|
|
}
|
|
|
|
storageLocations = data;
|
|
updateLocationDropdown();
|
|
} catch (error) {
|
|
console.error('Error loading storage locations:', error);
|
|
}
|
|
}
|
|
|
|
function updateLocationDropdown() {
|
|
const select = document.getElementById('storageLocation');
|
|
select.innerHTML = '';
|
|
|
|
if (storageLocations.length === 0) {
|
|
select.innerHTML = '<option value="">No storage locations found</option>';
|
|
return;
|
|
}
|
|
|
|
select.innerHTML = '<option value="">Select a storage location...</option>';
|
|
storageLocations.forEach(location => {
|
|
const option = document.createElement('option');
|
|
option.value = location.pk;
|
|
option.textContent = location.pathstring || location.name;
|
|
select.appendChild(option);
|
|
});
|
|
}
|
|
|
|
function addToInventory(itemIndex) {
|
|
console.log('addToInventory called with index:', itemIndex);
|
|
console.log('searchResults:', searchResults);
|
|
|
|
// Get the item data from the global search results array
|
|
if (!searchResults || itemIndex >= searchResults.length) {
|
|
console.error('Invalid item index or no search results');
|
|
showError('Invalid item selected');
|
|
return;
|
|
}
|
|
|
|
const itemData = searchResults[itemIndex];
|
|
console.log('Selected item:', itemData);
|
|
currentMediaItem = itemData;
|
|
|
|
// Populate item details
|
|
const detailsDiv = document.getElementById('inventoryItemDetails');
|
|
detailsDiv.innerHTML = `
|
|
<div class="card">
|
|
<div class="card-body">
|
|
<h6 class="card-title">${itemData.title}</h6>
|
|
<p class="card-text small">
|
|
${itemData.artist ? `<strong>Artist:</strong> ${itemData.artist}<br>` : ''}
|
|
${itemData.author ? `<strong>Author:</strong> ${itemData.author}<br>` : ''}
|
|
${itemData.year ? `<strong>Year:</strong> ${itemData.year}<br>` : ''}
|
|
${itemData.format ? `<strong>Format:</strong> ${itemData.format}` : ''}
|
|
</p>
|
|
</div>
|
|
</div>
|
|
`;
|
|
|
|
// Clear previous messages
|
|
document.getElementById('inventoryError').classList.add('d-none');
|
|
document.getElementById('inventorySuccess').classList.add('d-none');
|
|
|
|
// Reset form
|
|
document.getElementById('addToInventoryForm').reset();
|
|
document.getElementById('quantity').value = '1';
|
|
|
|
// Show modal
|
|
try {
|
|
const modalElement = document.getElementById('addToInventoryModal');
|
|
if (!modalElement) {
|
|
console.error('Modal element not found');
|
|
showError('Modal not found');
|
|
return;
|
|
}
|
|
|
|
const modal = new bootstrap.Modal(modalElement);
|
|
modal.show();
|
|
console.log('Modal shown successfully');
|
|
} catch (error) {
|
|
console.error('Error showing modal:', error);
|
|
showError('Error opening inventory modal');
|
|
}
|
|
}
|
|
|
|
async function submitToInventory() {
|
|
const form = document.getElementById('addToInventoryForm');
|
|
const formData = new FormData(form);
|
|
|
|
const submitData = {
|
|
media_item: currentMediaItem,
|
|
storage_location_id: parseInt(formData.get('storage_location_id')),
|
|
quantity: parseFloat(formData.get('quantity')),
|
|
notes: formData.get('notes') || null
|
|
};
|
|
|
|
// Validate required fields
|
|
if (!submitData.storage_location_id) {
|
|
showError('Please select a storage location');
|
|
return;
|
|
}
|
|
|
|
// Disable submit button
|
|
const submitBtn = document.getElementById('submitInventoryBtn');
|
|
const originalText = submitBtn.textContent;
|
|
submitBtn.disabled = true;
|
|
submitBtn.textContent = 'Adding...';
|
|
|
|
try {
|
|
const response = await fetch('/api/create-inventory-item', {
|
|
method: 'POST',
|
|
headers: {
|
|
'Content-Type': 'application/json',
|
|
},
|
|
body: JSON.stringify(submitData)
|
|
});
|
|
|
|
const result = await response.json();
|
|
|
|
if (result.success) {
|
|
showSuccess(result.message);
|
|
// Close modal after 2 seconds
|
|
setTimeout(() => {
|
|
const modal = bootstrap.Modal.getInstance(document.getElementById('addToInventoryModal'));
|
|
modal.hide();
|
|
}, 2000);
|
|
} else {
|
|
showError(result.message || result.error || 'Failed to add item to inventory');
|
|
}
|
|
} catch (error) {
|
|
showError('Error adding item to inventory: ' + error.message);
|
|
} finally {
|
|
// Re-enable submit button
|
|
submitBtn.disabled = false;
|
|
submitBtn.textContent = originalText;
|
|
}
|
|
}
|
|
|
|
function showError(message) {
|
|
const errorDiv = document.getElementById('inventoryError');
|
|
if (errorDiv) {
|
|
errorDiv.textContent = message;
|
|
errorDiv.classList.remove('d-none');
|
|
const successDiv = document.getElementById('inventorySuccess');
|
|
if (successDiv) {
|
|
successDiv.classList.add('d-none');
|
|
}
|
|
} else {
|
|
// Fallback to alert if modal error div not found
|
|
alert('Error: ' + message);
|
|
}
|
|
}
|
|
|
|
function showSuccess(message) {
|
|
const successDiv = document.getElementById('inventorySuccess');
|
|
successDiv.textContent = message;
|
|
successDiv.classList.remove('d-none');
|
|
document.getElementById('inventoryError').classList.add('d-none');
|
|
}
|
|
</script>
|
|
{% endblock %}
|