inventory management add/del
This commit is contained in:
281
templates/inventory.html
Normal file
281
templates/inventory.html
Normal file
@@ -0,0 +1,281 @@
|
||||
{% extends "base.html" %}
|
||||
|
||||
{% block title %}Inventory Management - Media Inventory App{% endblock %}
|
||||
|
||||
{% block content %}
|
||||
<div class="row">
|
||||
<div class="col-12">
|
||||
<h2>📦 Inventory Management</h2>
|
||||
<p class="text-muted">Manage your InvenTree storage locations and inventory items</p>
|
||||
|
||||
{% if error %}
|
||||
<div class="alert alert-danger" role="alert">
|
||||
<h5>⚠️ Configuration Error</h5>
|
||||
<p>{{ error }}</p>
|
||||
<hr>
|
||||
<p class="mb-0">
|
||||
<strong>To configure InvenTree integration:</strong><br>
|
||||
1. Set up your InvenTree instance<br>
|
||||
2. Add your API credentials to the .env file<br>
|
||||
3. Restart the application
|
||||
</p>
|
||||
</div>
|
||||
{% else %}
|
||||
|
||||
<!-- Storage Locations Section -->
|
||||
<div class="row mb-4">
|
||||
<div class="col-md-8">
|
||||
<div class="card">
|
||||
<div class="card-header d-flex justify-content-between align-items-center">
|
||||
<h5 class="mb-0">📍 Storage Locations</h5>
|
||||
<button class="btn btn-primary btn-sm" data-bs-toggle="modal" data-bs-target="#createLocationModal">
|
||||
➕ Add Location
|
||||
</button>
|
||||
</div>
|
||||
<div class="card-body">
|
||||
{% if locations %}
|
||||
<div class="table-responsive">
|
||||
<table class="table table-hover">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>ID</th>
|
||||
<th>Name</th>
|
||||
<th>Path</th>
|
||||
<th>Actions</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{% for location in locations %}
|
||||
<tr id="location-row-{{ location.pk }}">
|
||||
<td>{{ location.pk }}</td>
|
||||
<td>{{ location.name }}</td>
|
||||
<td><small class="text-muted">{{ location.pathstring }}</small></td>
|
||||
<td>
|
||||
<button class="btn btn-outline-danger btn-sm"
|
||||
onclick="deleteLocation({{ location.pk }}, '{{ location.name }}')"
|
||||
title="Delete empty location">
|
||||
🗑️ Delete
|
||||
</button>
|
||||
</td>
|
||||
</tr>
|
||||
{% endfor %}
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
{% else %}
|
||||
<div class="text-center py-4">
|
||||
<p class="text-muted">No storage locations found</p>
|
||||
<button class="btn btn-primary" data-bs-toggle="modal" data-bs-target="#createLocationModal">
|
||||
➕ Create Your First Location
|
||||
</button>
|
||||
</div>
|
||||
{% endif %}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="col-md-4">
|
||||
<div class="card">
|
||||
<div class="card-header">
|
||||
<h5 class="mb-0">ℹ️ How to Use</h5>
|
||||
</div>
|
||||
<div class="card-body">
|
||||
<ol class="small">
|
||||
<li><strong>Create Locations:</strong> Set up storage locations for your media</li>
|
||||
<li><strong>Search Media:</strong> Use the search function to find items</li>
|
||||
<li><strong>Add to Inventory:</strong> Click "Add to Inventory" on search results</li>
|
||||
<li><strong>Delete Empty Locations:</strong> Remove unused locations with the delete button</li>
|
||||
<li><strong>Manage:</strong> Use InvenTree web interface for advanced management</li>
|
||||
</ol>
|
||||
<hr>
|
||||
<p class="small text-muted mb-0">
|
||||
<strong>Connected to:</strong><br>
|
||||
InvenTree API ✅
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{% endif %}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Create Location Modal -->
|
||||
<div class="modal fade" id="createLocationModal" tabindex="-1">
|
||||
<div class="modal-dialog">
|
||||
<div class="modal-content">
|
||||
<div class="modal-header">
|
||||
<h5 class="modal-title">➕ Create Storage Location</h5>
|
||||
<button type="button" class="btn-close" data-bs-dismiss="modal"></button>
|
||||
</div>
|
||||
<div class="modal-body">
|
||||
<form id="createLocationForm">
|
||||
<div class="mb-3">
|
||||
<label for="locationName" class="form-label">Location Name *</label>
|
||||
<input type="text" class="form-control" id="locationName" name="name" required
|
||||
placeholder="e.g., Living Room Shelf, Bedroom Closet">
|
||||
</div>
|
||||
<div class="mb-3">
|
||||
<label for="locationDescription" class="form-label">Description</label>
|
||||
<textarea class="form-control" id="locationDescription" name="description" rows="3"
|
||||
placeholder="Optional description of this storage location"></textarea>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
<div class="modal-footer">
|
||||
<button type="button" class="btn btn-secondary" data-bs-dismiss="modal">Cancel</button>
|
||||
<button type="button" class="btn btn-primary" onclick="createLocation()">Create Location</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Delete Confirmation Modal -->
|
||||
<div class="modal fade" id="deleteLocationModal" tabindex="-1">
|
||||
<div class="modal-dialog">
|
||||
<div class="modal-content">
|
||||
<div class="modal-header">
|
||||
<h5 class="modal-title">🗑️ Delete Storage Location</h5>
|
||||
<button type="button" class="btn-close" data-bs-dismiss="modal"></button>
|
||||
</div>
|
||||
<div class="modal-body">
|
||||
<div class="alert alert-warning">
|
||||
<strong>⚠️ Warning:</strong> This action cannot be undone.
|
||||
</div>
|
||||
<p>Are you sure you want to delete the storage location <strong id="deleteLocationName"></strong>?</p>
|
||||
<p class="text-muted small">
|
||||
<strong>Note:</strong> You can only delete empty locations. If this location contains any stock items,
|
||||
you'll need to move or remove them first.
|
||||
</p>
|
||||
</div>
|
||||
<div class="modal-footer">
|
||||
<button type="button" class="btn btn-secondary" data-bs-dismiss="modal">Cancel</button>
|
||||
<button type="button" class="btn btn-danger" id="confirmDeleteBtn">Delete Location</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<script>
|
||||
async function createLocation() {
|
||||
const form = document.getElementById('createLocationForm');
|
||||
const formData = new FormData(form);
|
||||
const data = Object.fromEntries(formData);
|
||||
|
||||
try {
|
||||
const response = await fetch('/api/storage-locations', {
|
||||
method: 'POST',
|
||||
headers: {
|
||||
'Content-Type': 'application/json',
|
||||
},
|
||||
body: JSON.stringify(data)
|
||||
});
|
||||
|
||||
const result = await response.json();
|
||||
|
||||
if (result.success) {
|
||||
// Close modal and reload page
|
||||
const modal = bootstrap.Modal.getInstance(document.getElementById('createLocationModal'));
|
||||
modal.hide();
|
||||
location.reload();
|
||||
} else {
|
||||
alert('Error creating location: ' + (result.error || 'Unknown error'));
|
||||
}
|
||||
} catch (error) {
|
||||
alert('Error creating location: ' + error.message);
|
||||
}
|
||||
}
|
||||
|
||||
let locationToDelete = null;
|
||||
|
||||
function deleteLocation(locationId, locationName) {
|
||||
// Store the location info for deletion
|
||||
locationToDelete = { id: locationId, name: locationName };
|
||||
|
||||
// Update modal content
|
||||
document.getElementById('deleteLocationName').textContent = locationName;
|
||||
|
||||
// Show the modal
|
||||
const modal = new bootstrap.Modal(document.getElementById('deleteLocationModal'));
|
||||
modal.show();
|
||||
}
|
||||
|
||||
// Handle the actual deletion when confirmed
|
||||
document.addEventListener('DOMContentLoaded', function() {
|
||||
const confirmDeleteBtn = document.getElementById('confirmDeleteBtn');
|
||||
if (confirmDeleteBtn) {
|
||||
confirmDeleteBtn.addEventListener('click', async function() {
|
||||
if (!locationToDelete) return;
|
||||
|
||||
// Disable button and show loading
|
||||
confirmDeleteBtn.disabled = true;
|
||||
confirmDeleteBtn.textContent = 'Deleting...';
|
||||
|
||||
try {
|
||||
const response = await fetch(`/api/storage-locations/${locationToDelete.id}`, {
|
||||
method: 'DELETE',
|
||||
headers: {
|
||||
'Content-Type': 'application/json',
|
||||
}
|
||||
});
|
||||
|
||||
const result = await response.json();
|
||||
|
||||
// Close modal
|
||||
const modal = bootstrap.Modal.getInstance(document.getElementById('deleteLocationModal'));
|
||||
modal.hide();
|
||||
|
||||
if (result.success) {
|
||||
// Remove the row from the table
|
||||
const row = document.getElementById(`location-row-${locationToDelete.id}`);
|
||||
if (row) {
|
||||
row.remove();
|
||||
}
|
||||
|
||||
// Show success message
|
||||
showAlert('success', result.message || 'Storage location deleted successfully');
|
||||
} else {
|
||||
// Show error message
|
||||
showAlert('danger', result.message || result.error || 'Failed to delete location');
|
||||
}
|
||||
} catch (error) {
|
||||
// Close modal
|
||||
const modal = bootstrap.Modal.getInstance(document.getElementById('deleteLocationModal'));
|
||||
modal.hide();
|
||||
|
||||
showAlert('danger', 'Error deleting location: ' + error.message);
|
||||
} finally {
|
||||
// Reset button
|
||||
confirmDeleteBtn.disabled = false;
|
||||
confirmDeleteBtn.textContent = 'Delete Location';
|
||||
locationToDelete = null;
|
||||
}
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
function showAlert(type, message) {
|
||||
// Create alert element
|
||||
const alertDiv = document.createElement('div');
|
||||
alertDiv.className = `alert alert-${type} alert-dismissible fade show`;
|
||||
alertDiv.innerHTML = `
|
||||
${message}
|
||||
<button type="button" class="btn-close" data-bs-dismiss="alert"></button>
|
||||
`;
|
||||
|
||||
// Insert at the top of the content
|
||||
const container = document.querySelector('.container-fluid, .container');
|
||||
if (container) {
|
||||
container.insertBefore(alertDiv, container.firstChild);
|
||||
|
||||
// Auto-dismiss after 5 seconds
|
||||
setTimeout(() => {
|
||||
if (alertDiv.parentNode) {
|
||||
alertDiv.remove();
|
||||
}
|
||||
}, 5000);
|
||||
}
|
||||
}
|
||||
</script>
|
||||
{% endblock %}
|
Reference in New Issue
Block a user