456 lines
21 KiB
HTML
456 lines
21 KiB
HTML
{% extends 'base.html' %}
|
|
{% load static %}
|
|
|
|
{% block title %}Inventory Locations - Inventory Management{% endblock %}
|
|
|
|
{% block content %}
|
|
<div class="content">
|
|
<div class="container-fluid">
|
|
<!-- Page Header -->
|
|
<div class="row">
|
|
<div class="col-12">
|
|
<div class="page-header">
|
|
<div class="page-title">
|
|
<h4>Inventory Locations</h4>
|
|
<h6>Manage storage locations and warehouses</h6>
|
|
</div>
|
|
<div class="page-btn">
|
|
<a href="{% url 'inventory:location_create' %}" class="btn btn-added">
|
|
<i class="fas fa-plus me-1"></i>Add New Location
|
|
</a>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Location Statistics -->
|
|
<div class="row">
|
|
<div class="col-lg-3 col-sm-6 col-12">
|
|
<div class="dash-widget">
|
|
<div class="dash-widgetimg">
|
|
<span><i class="fas fa-map-marker-alt"></i></span>
|
|
</div>
|
|
<div class="dash-widgetcontent">
|
|
<h5>{{ stats.total_locations|default:24 }}</h5>
|
|
<h6>Total Locations</h6>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
<div class="col-lg-3 col-sm-6 col-12">
|
|
<div class="dash-widget">
|
|
<div class="dash-widgetimg">
|
|
<span><i class="fas fa-check-circle"></i></span>
|
|
</div>
|
|
<div class="dash-widgetcontent">
|
|
<h5>{{ stats.active_locations|default:22 }}</h5>
|
|
<h6>Active Locations</h6>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
<div class="col-lg-3 col-sm-6 col-12">
|
|
<div class="dash-widget">
|
|
<div class="dash-widgetimg">
|
|
<span><i class="fas fa-lock"></i></span>
|
|
</div>
|
|
<div class="dash-widgetcontent">
|
|
<h5>{{ stats.controlled_locations|default:6 }}</h5>
|
|
<h6>Controlled Access</h6>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
<div class="col-lg-3 col-sm-6 col-12">
|
|
<div class="dash-widget">
|
|
<div class="dash-widgetimg">
|
|
<span><i class="fas fa-thermometer-half"></i></span>
|
|
</div>
|
|
<div class="dash-widgetcontent">
|
|
<h5>{{ stats.climate_controlled|default:8 }}</h5>
|
|
<h6>Climate Controlled</h6>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Filters and Search -->
|
|
<div class="card">
|
|
<div class="card-body">
|
|
<div class="table-top">
|
|
<div class="search-set">
|
|
<div class="search-path">
|
|
<a class="btn btn-filter" id="filter_search">
|
|
<i class="fas fa-filter"></i>
|
|
<span><i class="fas fa-times"></i></span>
|
|
</a>
|
|
</div>
|
|
<div class="search-input">
|
|
<a class="btn btn-searchset">
|
|
<i class="fas fa-search"></i>
|
|
</a>
|
|
<input type="text" id="searchInput" placeholder="Search locations...">
|
|
</div>
|
|
</div>
|
|
<div class="wordset">
|
|
<ul>
|
|
<li>
|
|
<a data-bs-toggle="tooltip" data-bs-placement="top" title="PDF" onclick="exportToPDF()">
|
|
<i class="fas fa-file-pdf"></i>
|
|
</a>
|
|
</li>
|
|
<li>
|
|
<a data-bs-toggle="tooltip" data-bs-placement="top" title="Excel" onclick="exportToExcel()">
|
|
<i class="fas fa-file-excel"></i>
|
|
</a>
|
|
</li>
|
|
<li>
|
|
<a data-bs-toggle="tooltip" data-bs-placement="top" title="Print" onclick="printTable()">
|
|
<i class="fas fa-print"></i>
|
|
</a>
|
|
</li>
|
|
</ul>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Filter Panel -->
|
|
<div class="card mb-0" id="filter_inputs" style="display: none;">
|
|
<div class="card-body pb-0">
|
|
<div class="row">
|
|
<div class="col-lg-3 col-sm-6 col-12">
|
|
<div class="form-group">
|
|
<select class="select" id="locationTypeFilter">
|
|
<option value="">All Location Types</option>
|
|
<option value="WAREHOUSE">Warehouse</option>
|
|
<option value="STOREROOM">Storeroom</option>
|
|
<option value="PHARMACY">Pharmacy</option>
|
|
<option value="NURSING_UNIT">Nursing Unit</option>
|
|
<option value="OR_STORAGE">OR Storage</option>
|
|
<option value="LAB_STORAGE">Lab Storage</option>
|
|
<option value="RADIOLOGY">Radiology Storage</option>
|
|
<option value="CENTRAL_SUPPLY">Central Supply</option>
|
|
<option value="REFRIGERATOR">Refrigerator</option>
|
|
<option value="FREEZER">Freezer</option>
|
|
<option value="CONTROLLED">Controlled Substance</option>
|
|
<option value="QUARANTINE">Quarantine</option>
|
|
</select>
|
|
</div>
|
|
</div>
|
|
<div class="col-lg-3 col-sm-6 col-12">
|
|
<div class="form-group">
|
|
<select class="select" id="buildingFilter">
|
|
<option value="">All Buildings</option>
|
|
<option value="Main Hospital">Main Hospital</option>
|
|
<option value="Medical Office Building">Medical Office Building</option>
|
|
<option value="Outpatient Center">Outpatient Center</option>
|
|
<option value="Emergency Department">Emergency Department</option>
|
|
</select>
|
|
</div>
|
|
</div>
|
|
<div class="col-lg-3 col-sm-6 col-12">
|
|
<div class="form-group">
|
|
<select class="select" id="accessControlFilter">
|
|
<option value="">All Access Types</option>
|
|
<option value="OPEN">Open Access</option>
|
|
<option value="BADGE">Badge Access</option>
|
|
<option value="KEY">Key Access</option>
|
|
<option value="BIOMETRIC">Biometric Access</option>
|
|
<option value="DUAL_CONTROL">Dual Control</option>
|
|
</select>
|
|
</div>
|
|
</div>
|
|
<div class="col-lg-3 col-sm-6 col-12">
|
|
<div class="form-group">
|
|
<select class="select" id="statusFilter">
|
|
<option value="">All Status</option>
|
|
<option value="active">Active</option>
|
|
<option value="inactive">Inactive</option>
|
|
</select>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
<div class="row">
|
|
<div class="col-lg-3 col-sm-6 col-12">
|
|
<div class="form-group">
|
|
<div class="form-check">
|
|
<input class="form-check-input" type="checkbox" id="temperatureControlledFilter">
|
|
<label class="form-check-label" for="temperatureControlledFilter">
|
|
Temperature Controlled Only
|
|
</label>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
<div class="col-lg-3 col-sm-6 col-12">
|
|
<div class="form-group">
|
|
<div class="form-check">
|
|
<input class="form-check-input" type="checkbox" id="secureLocationFilter">
|
|
<label class="form-check-label" for="secureLocationFilter">
|
|
Secure Locations Only
|
|
</label>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
<div class="col-lg-6 col-12">
|
|
<div class="form-group">
|
|
<a class="btn btn-filters ms-auto" onclick="applyFilters()">
|
|
<i class="fas fa-search"></i>
|
|
</a>
|
|
<a class="btn btn-filters ms-2" onclick="clearFilters()">
|
|
<i class="fas fa-times-circle"></i>
|
|
</a>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Locations Table -->
|
|
<div class="table-responsive">
|
|
<table class="table" id="locationsTable">
|
|
<thead>
|
|
<tr>
|
|
<th>
|
|
<label class="checkboxs">
|
|
<input type="checkbox" id="select-all">
|
|
<span class="checkmarks"></span>
|
|
</label>
|
|
</th>
|
|
<th>Location Code</th>
|
|
<th>Name</th>
|
|
<th>Type</th>
|
|
<th>Building</th>
|
|
<th>Full Address</th>
|
|
<th>Access Control</th>
|
|
<th>Climate Control</th>
|
|
<th>Items</th>
|
|
<th>Manager</th>
|
|
<th>Status</th>
|
|
<th>Action</th>
|
|
</tr>
|
|
</thead>
|
|
<tbody>
|
|
{% for location in locations %}
|
|
<tr>
|
|
<td>
|
|
<label class="checkboxs">
|
|
<input type="checkbox" value="{{ location.id }}">
|
|
<span class="checkmarks"></span>
|
|
</label>
|
|
</td>
|
|
<td>{{ location.location_code }}</td>
|
|
<td>
|
|
<div class="productimgname">
|
|
<a href="{% url 'inventory:location_detail' location.id %}" class="product-img">
|
|
<i class="fas fa-warehouse"></i>
|
|
</a>
|
|
<a href="{% url 'inventory:location_detail' location.id %}">{{ location.name }}</a>
|
|
</div>
|
|
</td>
|
|
<td>
|
|
<span class="badges bg-lightgreen">{{ location.get_location_type_display }}</span>
|
|
</td>
|
|
<td>{{ location.building|default:"-" }}</td>
|
|
<td>{{ location.full_address|default:"-" }}</td>
|
|
<td>
|
|
{% if location.access_control == 'OPEN' %}
|
|
<span class="badges bg-lightgrey">{{ location.get_access_control_display }}</span>
|
|
{% elif location.access_control == 'BADGE' %}
|
|
<span class="badges bg-lightblue">{{ location.get_access_control_display }}</span>
|
|
{% elif location.access_control == 'BIOMETRIC' %}
|
|
<span class="badges bg-lightred">{{ location.get_access_control_display }}</span>
|
|
{% else %}
|
|
<span class="badges bg-lightyellow">{{ location.get_access_control_display }}</span>
|
|
{% endif %}
|
|
</td>
|
|
<td>
|
|
{% if location.temperature_controlled or location.humidity_controlled %}
|
|
<span class="badges bg-lightgreen">
|
|
{% if location.temperature_controlled %}Temp{% endif %}
|
|
{% if location.humidity_controlled %}{% if location.temperature_controlled %}/{% endif %}Humidity{% endif %}
|
|
</span>
|
|
{% else %}
|
|
<span class="badges bg-lightgrey">None</span>
|
|
{% endif %}
|
|
</td>
|
|
<td>{{ location.total_items|default:0 }}</td>
|
|
<td>{{ location.location_manager.get_full_name|default:"-" }}</td>
|
|
<td>
|
|
{% if location.is_active %}
|
|
<span class="badges bg-lightgreen">Active</span>
|
|
{% else %}
|
|
<span class="badges bg-lightred">Inactive</span>
|
|
{% endif %}
|
|
</td>
|
|
<td>
|
|
<a class="me-3" href="{% url 'inventory:location_detail' location.id %}">
|
|
<i class="fas fa-eye"></i>
|
|
</a>
|
|
<a class="me-3" href="{% url 'inventory:location_update' location.id %}">
|
|
<i class="fas fa-edit"></i>
|
|
</a>
|
|
<a class="confirm-text" href="{% url 'inventory:location_delete' location.id %}">
|
|
<i class="fas fa-trash"></i>
|
|
</a>
|
|
</td>
|
|
</tr>
|
|
{% empty %}
|
|
<tr>
|
|
<td colspan="12" class="text-center">No locations found</td>
|
|
</tr>
|
|
{% endfor %}
|
|
</tbody>
|
|
</table>
|
|
</div>
|
|
|
|
<!-- Pagination -->
|
|
{% if is_paginated %}
|
|
{% include 'partial/pagination.html' %}
|
|
{% endif %}
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<script>
|
|
document.addEventListener('DOMContentLoaded', function() {
|
|
// Initialize search functionality
|
|
const searchInput = document.getElementById('searchInput');
|
|
searchInput.addEventListener('keyup', function() {
|
|
filterTable();
|
|
});
|
|
|
|
// Initialize select all checkbox
|
|
const selectAllCheckbox = document.getElementById('select-all');
|
|
selectAllCheckbox.addEventListener('change', function() {
|
|
const checkboxes = document.querySelectorAll('tbody input[type="checkbox"]');
|
|
checkboxes.forEach(checkbox => {
|
|
checkbox.checked = this.checked;
|
|
});
|
|
});
|
|
|
|
// Initialize filter toggle
|
|
const filterButton = document.getElementById('filter_search');
|
|
const filterPanel = document.getElementById('filter_inputs');
|
|
filterButton.addEventListener('click', function() {
|
|
if (filterPanel.style.display === 'none') {
|
|
filterPanel.style.display = 'block';
|
|
} else {
|
|
filterPanel.style.display = 'none';
|
|
}
|
|
});
|
|
});
|
|
|
|
function filterTable() {
|
|
const searchTerm = document.getElementById('searchInput').value.toLowerCase();
|
|
const table = document.getElementById('locationsTable');
|
|
const rows = table.getElementsByTagName('tbody')[0].getElementsByTagName('tr');
|
|
|
|
for (let i = 0; i < rows.length; i++) {
|
|
const row = rows[i];
|
|
const cells = row.getElementsByTagName('td');
|
|
let found = false;
|
|
|
|
// Skip empty state row
|
|
if (cells.length === 1) continue;
|
|
|
|
// Search in location code, name, type, building
|
|
const searchableText = (
|
|
cells[1].textContent + ' ' + // Location code
|
|
cells[2].textContent + ' ' + // Name
|
|
cells[3].textContent + ' ' + // Type
|
|
cells[4].textContent // Building
|
|
).toLowerCase();
|
|
|
|
if (searchableText.includes(searchTerm)) {
|
|
found = true;
|
|
}
|
|
|
|
row.style.display = found ? '' : 'none';
|
|
}
|
|
}
|
|
|
|
function applyFilters() {
|
|
const locationTypeFilter = document.getElementById('locationTypeFilter').value;
|
|
const buildingFilter = document.getElementById('buildingFilter').value;
|
|
const accessControlFilter = document.getElementById('accessControlFilter').value;
|
|
const statusFilter = document.getElementById('statusFilter').value;
|
|
const temperatureControlledFilter = document.getElementById('temperatureControlledFilter').checked;
|
|
const secureLocationFilter = document.getElementById('secureLocationFilter').checked;
|
|
|
|
const table = document.getElementById('locationsTable');
|
|
const rows = table.getElementsByTagName('tbody')[0].getElementsByTagName('tr');
|
|
|
|
for (let i = 0; i < rows.length; i++) {
|
|
const row = rows[i];
|
|
const cells = row.getElementsByTagName('td');
|
|
let show = true;
|
|
|
|
// Skip empty state row
|
|
if (cells.length === 1) continue;
|
|
|
|
// Apply filters
|
|
if (locationTypeFilter && !cells[3].textContent.includes(locationTypeFilter.replace('_', ' '))) {
|
|
show = false;
|
|
}
|
|
|
|
if (buildingFilter && cells[4].textContent !== buildingFilter) {
|
|
show = false;
|
|
}
|
|
|
|
if (statusFilter) {
|
|
const isActive = cells[10].textContent.includes('Active');
|
|
if ((statusFilter === 'active' && !isActive) || (statusFilter === 'inactive' && isActive)) {
|
|
show = false;
|
|
}
|
|
}
|
|
|
|
// Apply additional filters based on badges and content
|
|
if (temperatureControlledFilter && !cells[7].textContent.includes('Temp')) {
|
|
show = false;
|
|
}
|
|
|
|
if (secureLocationFilter && cells[6].textContent.includes('Open Access')) {
|
|
show = false;
|
|
}
|
|
|
|
row.style.display = show ? '' : 'none';
|
|
}
|
|
|
|
console.log('Filters applied');
|
|
}
|
|
|
|
function clearFilters() {
|
|
// Reset all filter inputs
|
|
document.getElementById('locationTypeFilter').value = '';
|
|
document.getElementById('buildingFilter').value = '';
|
|
document.getElementById('accessControlFilter').value = '';
|
|
document.getElementById('statusFilter').value = '';
|
|
document.getElementById('temperatureControlledFilter').checked = false;
|
|
document.getElementById('secureLocationFilter').checked = false;
|
|
document.getElementById('searchInput').value = '';
|
|
|
|
// Show all rows
|
|
const table = document.getElementById('locationsTable');
|
|
const rows = table.getElementsByTagName('tbody')[0].getElementsByTagName('tr');
|
|
for (let i = 0; i < rows.length; i++) {
|
|
rows[i].style.display = '';
|
|
}
|
|
|
|
console.log('Filters cleared');
|
|
}
|
|
|
|
function exportToPDF() {
|
|
console.log('Exporting locations to PDF...');
|
|
alert('PDF export functionality would be implemented here.');
|
|
}
|
|
|
|
function exportToExcel() {
|
|
console.log('Exporting locations to Excel...');
|
|
alert('Excel export functionality would be implemented here.');
|
|
}
|
|
|
|
function printTable() {
|
|
console.log('Printing locations table...');
|
|
window.print();
|
|
}
|
|
</script>
|
|
{% endblock %}
|
|
|