2026-06-03 10:43:17 +01:00
|
|
|
{% extends "base.html" %}
|
|
|
|
|
|
2026-06-23 19:57:49 +01:00
|
|
|
{% block title %}{{ gettext('Loan Tablet') }}{% endblock %}
|
2026-06-03 10:43:17 +01:00
|
|
|
|
2026-06-23 19:57:49 +01:00
|
|
|
{% block content %}
|
2026-06-24 15:53:55 +01:00
|
|
|
<div class="max-w-2xl mx-auto space-y-6">
|
|
|
|
|
{# Page Header #}
|
|
|
|
|
<div>
|
|
|
|
|
<h1 class="text-2xl font-bold text-gray-900">{{ gettext('Loan Tablet') }}</h1>
|
|
|
|
|
<p class="text-gray-600 mt-1">{{ gettext('Loan a tablet to a user or student') }}</p>
|
|
|
|
|
</div>
|
|
|
|
|
|
|
|
|
|
{# Loan Form #}
|
|
|
|
|
<div class="bg-white rounded-lg shadow overflow-hidden">
|
|
|
|
|
<div class="bg-cobalt-600 px-6 py-4">
|
|
|
|
|
<h2 class="text-lg font-semibold text-white">{{ gettext('Loan Information') }}</h2>
|
|
|
|
|
</div>
|
|
|
|
|
<div class="p-6">
|
|
|
|
|
<form method="POST" action="/loan_tablet" class="space-y-6">
|
|
|
|
|
{# Tablet Selection #}
|
|
|
|
|
<div>
|
|
|
|
|
<label for="tablet_id" class="block text-sm font-medium text-gray-700 mb-1">{{ gettext('Tablet') }} *</label>
|
|
|
|
|
<input type="text" id="tablet_search"
|
|
|
|
|
class="w-full px-3 py-2 border border-gray-300 rounded-md focus:outline-none focus:ring-2 focus:ring-cobalt-500 focus:border-cobalt-500 mb-2"
|
|
|
|
|
placeholder="{{ gettext('Search tablets (brand, model, or serial)...') }}"
|
|
|
|
|
onkeyup="filterDropdown('tablet_search', 'tablet_id')">
|
|
|
|
|
<select id="tablet_id" name="tablet_id" required size="5"
|
|
|
|
|
class="w-full px-3 py-2 border border-gray-300 rounded-md focus:outline-none focus:ring-2 focus:ring-cobalt-500 focus:border-cobalt-500"
|
|
|
|
|
style="max-height: 200px; overflow-y: auto;">
|
|
|
|
|
<option value="">{{ gettext('Select a tablet') }}</option>
|
|
|
|
|
{% for tablet in available_tablets %}
|
|
|
|
|
<option value="{{ tablet.id }}"
|
|
|
|
|
data-search="{{ tablet.brand|lower }} {{ tablet.model|lower }} {{ tablet.serial_number|lower }}">
|
|
|
|
|
{{ tablet.brand }} {{ tablet.model }} ({{ tablet.serial_number }})
|
|
|
|
|
</option>
|
|
|
|
|
{% endfor %}
|
|
|
|
|
</select>
|
|
|
|
|
<p class="mt-1 text-sm text-gray-500">{{ gettext('Select which tablet to loan') }}</p>
|
|
|
|
|
</div>
|
|
|
|
|
|
|
|
|
|
{# User Selection (Staff) #}
|
|
|
|
|
<div>
|
|
|
|
|
<label for="user_id" class="block text-sm font-medium text-gray-700 mb-1">{{ gettext('Staff User') }} *</label>
|
|
|
|
|
<input type="text" id="user_search"
|
|
|
|
|
class="w-full px-3 py-2 border border-gray-300 rounded-md focus:outline-none focus:ring-2 focus:ring-cobalt-500 focus:border-cobalt-500 mb-2"
|
|
|
|
|
placeholder="{{ gettext('Search users (name or ID)...') }}"
|
|
|
|
|
onkeyup="filterDropdown('user_search', 'user_id')">
|
|
|
|
|
<select id="user_id" name="user_id" required size="5"
|
|
|
|
|
class="w-full px-3 py-2 border border-gray-300 rounded-md focus:outline-none focus:ring-2 focus:ring-cobalt-500 focus:border-cobalt-500"
|
|
|
|
|
style="max-height: 200px; overflow-y: auto;">
|
|
|
|
|
<option value="">{{ gettext('Select a user') }}</option>
|
|
|
|
|
{% for user in users %}
|
|
|
|
|
<option value="{{ user.id }}"
|
|
|
|
|
data-search="{{ user.name|lower }} {{ user.identification|lower }}">
|
|
|
|
|
{{ user.name }} ({{ user.identification }})
|
|
|
|
|
</option>
|
|
|
|
|
{% endfor %}
|
|
|
|
|
</select>
|
|
|
|
|
<p class="mt-1 text-sm text-gray-500">{{ gettext('Select the staff member processing the loan') }}</p>
|
2026-06-23 19:57:49 +01:00
|
|
|
</div>
|
2026-06-24 15:53:55 +01:00
|
|
|
|
|
|
|
|
{# Student Selection (Optional) #}
|
|
|
|
|
<div>
|
|
|
|
|
<label for="student_id" class="block text-sm font-medium text-gray-700 mb-1">{{ gettext('Student (Optional)') }}</label>
|
|
|
|
|
<input type="text" id="student_search"
|
|
|
|
|
class="w-full px-3 py-2 border border-gray-300 rounded-md focus:outline-none focus:ring-2 focus:ring-cobalt-500 focus:border-cobalt-500 mb-2"
|
|
|
|
|
placeholder="{{ gettext('Search students (name, CIAL, or NIF)...') }}"
|
|
|
|
|
onkeyup="filterDropdown('student_search', 'student_id')">
|
|
|
|
|
<select id="student_id" name="student_id" size="5"
|
|
|
|
|
class="w-full px-3 py-2 border border-gray-300 rounded-md focus:outline-none focus:ring-2 focus:ring-cobalt-500 focus:border-cobalt-500"
|
|
|
|
|
style="max-height: 200px; overflow-y: auto;">
|
|
|
|
|
<option value="">{{ gettext('Select a student (optional)') }}</option>
|
|
|
|
|
{% for student in students %}
|
|
|
|
|
<option value="{{ student.id }}"
|
|
|
|
|
data-search="{{ student.last_name|lower }} {{ student.first_name|lower }} {{ student.cial_code|lower }} {{ student.nif_nie_passport|lower }}">
|
|
|
|
|
{{ student.last_name }}, {{ student.first_name }} ({{ student.cial_code }})
|
|
|
|
|
</option>
|
|
|
|
|
{% endfor %}
|
|
|
|
|
</select>
|
|
|
|
|
<p class="mt-1 text-sm text-gray-500">{{ gettext('Optionally associate this loan with a student') }}</p>
|
2026-06-23 19:57:49 +01:00
|
|
|
</div>
|
2026-06-24 15:53:55 +01:00
|
|
|
|
|
|
|
|
{# Form Actions #}
|
|
|
|
|
<div class="flex justify-end gap-3 pt-4 border-t border-gray-200">
|
|
|
|
|
<a href="/"
|
|
|
|
|
class="inline-flex items-center gap-2 px-4 py-2 bg-gray-200 text-gray-700 rounded-lg hover:bg-gray-300 transition-colors">
|
|
|
|
|
<svg class="w-5 h-5" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
|
|
|
|
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M6 18L18 6M6 6l12 12"></path>
|
|
|
|
|
</svg>
|
|
|
|
|
{{ gettext('Cancel') }}
|
|
|
|
|
</a>
|
|
|
|
|
<button type="submit"
|
|
|
|
|
class="inline-flex items-center gap-2 px-4 py-2 bg-cobalt-600 text-white rounded-lg hover:bg-cobalt-700 transition-colors">
|
|
|
|
|
<svg class="w-5 h-5" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
|
|
|
|
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M5 13l4 4L19 7"></path>
|
|
|
|
|
</svg>
|
|
|
|
|
{{ gettext('Loan Tablet') }}
|
|
|
|
|
</button>
|
|
|
|
|
</div>
|
|
|
|
|
</form>
|
2026-06-03 10:43:17 +01:00
|
|
|
</div>
|
2026-06-23 19:57:49 +01:00
|
|
|
</div>
|
|
|
|
|
</div>
|
2026-06-03 10:43:17 +01:00
|
|
|
|
2026-06-23 19:57:49 +01:00
|
|
|
<script>
|
|
|
|
|
function filterDropdown(searchId, selectId) {
|
|
|
|
|
const search = document.getElementById(searchId).value.toLowerCase();
|
|
|
|
|
const select = document.getElementById(selectId);
|
|
|
|
|
const options = select.options;
|
2026-06-03 10:43:17 +01:00
|
|
|
|
2026-06-23 19:57:49 +01:00
|
|
|
for (let i = 1; i < options.length; i++) {
|
|
|
|
|
const searchText = options[i].getAttribute('data-search') || '';
|
|
|
|
|
options[i].style.display = searchText.includes(search) ? '' : 'none';
|
2026-06-03 10:43:17 +01:00
|
|
|
}
|
|
|
|
|
|
2026-06-23 19:57:49 +01:00
|
|
|
// Show first visible option if search is empty, otherwise keep selection
|
|
|
|
|
if (search === '') {
|
|
|
|
|
select.selectedIndex = 0;
|
2026-06-03 10:43:17 +01:00
|
|
|
}
|
2026-06-23 19:57:49 +01:00
|
|
|
}
|
|
|
|
|
</script>
|
2026-06-20 13:14:41 +01:00
|
|
|
{% endblock %}
|