Phase 1: Responsive tables and Tailwind CSS standardization
- Added responsive table styles with mobile card layouts to base.html - Converted all templates from Bootstrap/plain CSS to Tailwind CSS - Implemented responsive tables with horizontal scroll on desktop - Added mobile card layouts for better readability on small screens - Standardized form layouts with Tailwind responsive grid - Updated all 15 templates for consistent styling: * base.html (responsive table CSS) * index.html (responsive tables + mobile cards) * students.html (Bootstrap -> Tailwind) * student_detail.html (Bootstrap -> Tailwind) * add_student.html (Bootstrap -> Tailwind) * edit_student.html (Bootstrap -> Tailwind) * import_students.html (Bootstrap -> Tailwind) * loan_tablet.html (Bootstrap -> Tailwind) * history.html (plain CSS -> Tailwind) * non_loanable_devices.html (plain CSS -> Tailwind) * add_non_loanable_device.html (plain CSS -> Tailwind) * edit_non_loanable_device.html (plain CSS -> Tailwind) * add_tablet.html (plain CSS -> Tailwind) * add_user.html (plain CSS -> Tailwind) * project_management.html (plain CSS -> Tailwind)
This commit is contained in:
parent
1b28021134
commit
06da5bdf2f
15 changed files with 1766 additions and 1229 deletions
|
|
@ -3,179 +3,175 @@
|
|||
{% block title %}{{ gettext('Student Details') }} - {{ student.full_name }}{% endblock %}
|
||||
|
||||
{% block content %}
|
||||
<div class="container-fluid">
|
||||
<div class="row">
|
||||
<div class="col-12">
|
||||
<nav aria-label="breadcrumb">
|
||||
<ol class="breadcrumb">
|
||||
<li class="breadcrumb-item"><a href="{{ url_for('list_students') }}">{{ gettext('Students') }}</a></li>
|
||||
<li class="breadcrumb-item active" aria-current="page">{{ student.full_name }}</li>
|
||||
</ol>
|
||||
</nav>
|
||||
|
||||
<div class="d-flex justify-content-between align-items-center mb-4">
|
||||
<h1 class="page-title mb-0">{{ student.full_name }}</h1>
|
||||
<div class="btn-group" role="group">
|
||||
<a href="{{ url_for('edit_student', student_id=student.id) }}" class="btn btn-warning">
|
||||
<i class="bi bi-pencil"></i> {{ gettext('Edit') }}
|
||||
</a>
|
||||
<a href="{{ url_for('delete_student', student_id=student.id) }}" class="btn btn-danger"
|
||||
onclick="return confirm('{{ gettext('Are you sure you want to delete this student?') }}')">
|
||||
<i class="bi bi-trash"></i> {{ gettext('Delete') }}
|
||||
</a>
|
||||
</div>
|
||||
<div class="space-y-6">
|
||||
{# Breadcrumb #}
|
||||
<nav class="flex" aria-label="Breadcrumb">
|
||||
<ol class="flex items-center space-x-2">
|
||||
<li>
|
||||
<a href="{{ url_for('list_students') }}" class="text-cobalt-600 hover:text-cobalt-800">{{ gettext('Students') }}</a>
|
||||
</li>
|
||||
<li>
|
||||
<svg class="w-4 h-4 text-gray-400" fill="currentColor" viewBox="0 0 20 20">
|
||||
<path fill-rule="evenodd" d="M7.293 14.707a1 1 0 010-1.414L10.586 10 7.293 6.707a1 1 0 011.414-1.414l4 4a1 1 0 010 1.414l-4 4a1 1 0 01-1.414 0z" clip-rule="evenodd"></path>
|
||||
</svg>
|
||||
</li>
|
||||
<li>
|
||||
<span class="text-gray-500">{{ student.full_name }}</span>
|
||||
</li>
|
||||
</ol>
|
||||
</nav>
|
||||
|
||||
{# Student Header #}
|
||||
<div class="flex flex-col sm:flex-row justify-between items-start sm:items-center gap-4">
|
||||
<div>
|
||||
<h1 class="text-2xl font-bold text-gray-900">{{ student.full_name }}</h1>
|
||||
<p class="text-gray-600 mt-1">{{ gettext('Student Details') }}</p>
|
||||
</div>
|
||||
<div class="flex gap-2">
|
||||
<a href="{{ url_for('edit_student', student_id=student.id) }}"
|
||||
class="inline-flex items-center gap-2 px-4 py-2 bg-yellow-500 text-white rounded-lg hover:bg-yellow-600 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="M11 5H6a2 2 0 00-2 2v11a2 2 0 002 2h11a2 2 0 002-2v-5m-1.414-9.414a2 2 0 112.828 2.828L11.828 15H9v-2.828l8.586-8.586z"></path>
|
||||
</svg>
|
||||
{{ gettext('Edit') }}
|
||||
</a>
|
||||
<a href="{{ url_for('delete_student', student_id=student.id) }}"
|
||||
onclick="return confirm('{{ gettext('Are you sure you want to delete this student?') }}')"
|
||||
class="inline-flex items-center gap-2 px-4 py-2 bg-red-600 text-white rounded-lg hover:bg-red-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="M19 7l-.867 12.142A2 2 0 0116.138 21H7.862a2 2 0 01-1.995-1.858L5 7m5 4v6m4-6v6m1-10V4a1 1 0 00-1-1h-4a1 1 0 00-1 1v3M4 7h16"></path>
|
||||
</svg>
|
||||
{{ gettext('Delete') }}
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{# Student Information and Assigned Devices #}
|
||||
<div class="grid grid-cols-1 lg:grid-cols-2 gap-6">
|
||||
{# Student Information Card #}
|
||||
<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('Student Information') }}</h2>
|
||||
</div>
|
||||
|
||||
<div class="row">
|
||||
<!-- Student Information Card -->
|
||||
<div class="col-12 col-lg-6 mb-4">
|
||||
<div class="card h-100">
|
||||
<div class="card-header bg-primary text-white">
|
||||
<h5 class="mb-0">{{ gettext('Student Information') }}</h5>
|
||||
</div>
|
||||
<div class="card-body">
|
||||
<dl class="row mb-0">
|
||||
<dt class="col-sm-4">{{ gettext('ID') }}:</dt>
|
||||
<dd class="col-sm-8">{{ student.id }}</dd>
|
||||
|
||||
<dt class="col-sm-4">{{ gettext('CIAL Code') }}:</dt>
|
||||
<dd class="col-sm-8">{{ student.cial_code }}</dd>
|
||||
|
||||
<dt class="col-sm-4">{{ gettext('NIF/NIE/Passport') }}:</dt>
|
||||
<dd class="col-sm-8">{{ student.nif_nie_passport or '-' }}</dd>
|
||||
|
||||
<dt class="col-sm-4">{{ gettext('Registration Number') }}:</dt>
|
||||
<dd class="col-sm-8">{{ student.registration_number or '-' }}</dd>
|
||||
|
||||
<dt class="col-sm-4">{{ gettext('File Number') }}:</dt>
|
||||
<dd class="col-sm-8">{{ student.file_number or '-' }}</dd>
|
||||
|
||||
<dt class="col-sm-4">{{ gettext('Order Number') }}:</dt>
|
||||
<dd class="col-sm-8">{{ student.order_number or '-' }}</dd>
|
||||
|
||||
<dt class="col-sm-4">{{ gettext('Full Name') }}:</dt>
|
||||
<dd class="col-sm-8">{{ student.full_name }}</dd>
|
||||
|
||||
<dt class="col-sm-4">{{ gettext('First Name') }}:</dt>
|
||||
<dd class="col-sm-8">{{ student.first_name }}</dd>
|
||||
|
||||
<dt class="col-sm-4">{{ gettext('Last Name') }}:</dt>
|
||||
<dd class="col-sm-8">{{ student.last_name }}</dd>
|
||||
|
||||
<dt class="col-sm-4">{{ gettext('Birth Date') }}:</dt>
|
||||
<dd class="col-sm-8">{{ student.birth_date or '-' }}</dd>
|
||||
|
||||
<dt class="col-sm-4">{{ gettext('Gender') }}:</dt>
|
||||
<dd class="col-sm-8">{{ student.gender or '-' }}</dd>
|
||||
|
||||
<dt class="col-sm-4">{{ gettext('Study Group') }}:</dt>
|
||||
<dd class="col-sm-8">{{ student.study_group or '-' }}</dd>
|
||||
|
||||
<dt class="col-sm-4">{{ gettext('Created') }}:</dt>
|
||||
<dd class="col-sm-8">{{ student.created_at }}</dd>
|
||||
|
||||
<dt class="col-sm-4">{{ gettext('Updated') }}:</dt>
|
||||
<dd class="col-sm-8">{{ student.updated_at }}</dd>
|
||||
</dl>
|
||||
</div>
|
||||
<div class="p-6">
|
||||
<dl class="space-y-4">
|
||||
<div class="flex flex-col sm:flex-row sm:items-center">
|
||||
<dt class="text-sm font-medium text-gray-500 sm:w-1/3">{{ gettext('ID') }}:</dt>
|
||||
<dd class="text-sm text-gray-900 sm:w-2/3">{{ student.id }}</dd>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Assigned Devices Card -->
|
||||
<div class="col-12 col-lg-6 mb-4">
|
||||
<div class="card h-100">
|
||||
<div class="card-header bg-info text-white">
|
||||
<h5 class="mb-0">{{ gettext('Assigned Devices') }}</h5>
|
||||
</div>
|
||||
<div class="card-body">
|
||||
|
||||
<!-- Assigned Tablets -->
|
||||
<h6>{{ gettext('Tablets') }}</h6>
|
||||
{% if assigned_tablets %}
|
||||
<ul class="list-group list-group-flush mb-3">
|
||||
{% for tablet in assigned_tablets %}
|
||||
<li class="list-group-item">
|
||||
<strong>{{ tablet.brand }} {{ tablet.model }}</strong><br>
|
||||
<small class="text-muted">{{ gettext('Serial:') }} {{ tablet.serial_number }}</small><br>
|
||||
<small class="text-muted">{{ gettext('Status:') }} {{ tablet.status }}</small>
|
||||
</li>
|
||||
{% endfor %}
|
||||
</ul>
|
||||
{% else %}
|
||||
<p class="text-muted">{{ gettext('No tablets assigned') }}</p>
|
||||
{% endif %}
|
||||
|
||||
<!-- Assigned Non-Loanable Devices -->
|
||||
<h6>{{ gettext('Other Devices') }}</h6>
|
||||
{% if assigned_devices %}
|
||||
<ul class="list-group list-group-flush">
|
||||
{% for device in assigned_devices %}
|
||||
<li class="list-group-item">
|
||||
<strong>{{ device.brand }} {{ device.model }}</strong><br>
|
||||
<small class="text-muted">{{ gettext('Type:') }} {{ device.device_type }}</small><br>
|
||||
<small class="text-muted">{{ gettext('Serial:') }} {{ device.serial_number }}</small>
|
||||
</li>
|
||||
{% endfor %}
|
||||
</ul>
|
||||
{% else %}
|
||||
<p class="text-muted">{{ gettext('No other devices assigned') }}</p>
|
||||
{% endif %}
|
||||
</div>
|
||||
<div class="flex flex-col sm:flex-row sm:items-center">
|
||||
<dt class="text-sm font-medium text-gray-500 sm:w-1/3">{{ gettext('CIAL Code') }}:</dt>
|
||||
<dd class="text-sm text-gray-900 sm:w-2/3">
|
||||
<code class="bg-gray-100 px-2 py-1 rounded">{{ student.cial_code }}</code>
|
||||
</dd>
|
||||
</div>
|
||||
</div>
|
||||
<div class="flex flex-col sm:flex-row sm:items-center">
|
||||
<dt class="text-sm font-medium text-gray-500 sm:w-1/3">{{ gettext('NIF/NIE/Passport') }}:</dt>
|
||||
<dd class="text-sm text-gray-900 sm:w-2/3">{{ student.nif_nie_passport or '-' }}</dd>
|
||||
</div>
|
||||
<div class="flex flex-col sm:flex-row sm:items-center">
|
||||
<dt class="text-sm font-medium text-gray-500 sm:w-1/3">{{ gettext('Registration Number') }}:</dt>
|
||||
<dd class="text-sm text-gray-900 sm:w-2/3">{{ student.registration_number or '-' }}</dd>
|
||||
</div>
|
||||
<div class="flex flex-col sm:flex-row sm:items-center">
|
||||
<dt class="text-sm font-medium text-gray-500 sm:w-1/3">{{ gettext('File Number') }}:</dt>
|
||||
<dd class="text-sm text-gray-900 sm:w-2/3">{{ student.file_number or '-' }}</dd>
|
||||
</div>
|
||||
<div class="flex flex-col sm:flex-row sm:items-center">
|
||||
<dt class="text-sm font-medium text-gray-500 sm:w-1/3">{{ gettext('Order Number') }}:</dt>
|
||||
<dd class="text-sm text-gray-900 sm:w-2/3">{{ student.order_number or '-' }}</dd>
|
||||
</div>
|
||||
<div class="flex flex-col sm:flex-row sm:items-center">
|
||||
<dt class="text-sm font-medium text-gray-500 sm:w-1/3">{{ gettext('Full Name') }}:</dt>
|
||||
<dd class="text-sm text-gray-900 sm:w-2/3">{{ student.full_name }}</dd>
|
||||
</div>
|
||||
<div class="flex flex-col sm:flex-row sm:items-center">
|
||||
<dt class="text-sm font-medium text-gray-500 sm:w-1/3">{{ gettext('First Name') }}:</dt>
|
||||
<dd class="text-sm text-gray-900 sm:w-2/3">{{ student.first_name }}</dd>
|
||||
</div>
|
||||
<div class="flex flex-col sm:flex-row sm:items-center">
|
||||
<dt class="text-sm font-medium text-gray-500 sm:w-1/3">{{ gettext('Last Name') }}:</dt>
|
||||
<dd class="text-sm text-gray-900 sm:w-2/3">{{ student.last_name }}</dd>
|
||||
</div>
|
||||
<div class="flex flex-col sm:flex-row sm:items-center">
|
||||
<dt class="text-sm font-medium text-gray-500 sm:w-1/3">{{ gettext('Birth Date') }}:</dt>
|
||||
<dd class="text-sm text-gray-900 sm:w-2/3">{{ student.birth_date or '-' }}</dd>
|
||||
</div>
|
||||
<div class="flex flex-col sm:flex-row sm:items-center">
|
||||
<dt class="text-sm font-medium text-gray-500 sm:w-1/3">{{ gettext('Gender') }}:</dt>
|
||||
<dd class="text-sm text-gray-900 sm:w-2/3">{{ student.gender or '-' }}</dd>
|
||||
</div>
|
||||
<div class="flex flex-col sm:flex-row sm:items-center">
|
||||
<dt class="text-sm font-medium text-gray-500 sm:w-1/3">{{ gettext('Study Group') }}:</dt>
|
||||
<dd class="text-sm text-gray-900 sm:w-2/3">{{ student.study_group or '-' }}</dd>
|
||||
</div>
|
||||
<div class="flex flex-col sm:flex-row sm:items-center">
|
||||
<dt class="text-sm font-medium text-gray-500 sm:w-1/3">{{ gettext('Created') }}:</dt>
|
||||
<dd class="text-sm text-gray-900 sm:w-2/3">{{ student.created_at }}</dd>
|
||||
</div>
|
||||
<div class="flex flex-col sm:flex-row sm:items-center">
|
||||
<dt class="text-sm font-medium text-gray-500 sm:w-1/3">{{ gettext('Updated') }}:</dt>
|
||||
<dd class="text-sm text-gray-900 sm:w-2/3">{{ student.updated_at }}</dd>
|
||||
</div>
|
||||
</dl>
|
||||
</div>
|
||||
|
||||
<!-- Loan History Card -->
|
||||
<div class="col-12 mb-4">
|
||||
<div class="card">
|
||||
<div class="card-header bg-success text-white">
|
||||
<h5 class="mb-0">{{ gettext('Loan History') }}</h5>
|
||||
</div>
|
||||
<div class="card-body">
|
||||
<div class="table-responsive">
|
||||
<table class="table table-striped table-hover mb-0">
|
||||
<thead class="table-light">
|
||||
<tr>
|
||||
<th>{{ gettext('ID') }}</th>
|
||||
<th>{{ gettext('Tablet') }}</th>
|
||||
<th>{{ gettext('Loan Date') }}</th>
|
||||
<th>{{ gettext('Return Date') }}</th>
|
||||
<th>{{ gettext('Status') }}</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{% if loans %}
|
||||
{% for loan in loans %}
|
||||
<tr>
|
||||
<td>{{ loan.id }}</td>
|
||||
<td>{{ loan.brand }} {{ loan.model }} ({{ loan.serial_number }})</td>
|
||||
<td>{{ loan.loan_date }}</td>
|
||||
<td>{{ loan.return_date or '-' }}</td>
|
||||
<td>
|
||||
<span class="badge bg-{{ 'success' if loan.status == 'returned' else 'primary' }}">
|
||||
{{ loan.status }}
|
||||
</span>
|
||||
</td>
|
||||
</tr>
|
||||
{% endfor %}
|
||||
{% else %}
|
||||
<tr>
|
||||
<td colspan="5" class="text-center text-muted">
|
||||
{{ gettext('No loan history') }}
|
||||
</td>
|
||||
</tr>
|
||||
{% endif %}
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
|
||||
{# Assigned Devices Card #}
|
||||
<div class="bg-white rounded-lg shadow overflow-hidden">
|
||||
<div class="bg-green-600 px-6 py-4">
|
||||
<h2 class="text-lg font-semibold text-white">{{ gettext('Assigned Devices') }}</h2>
|
||||
</div>
|
||||
<div class="p-6">
|
||||
{# Assigned Tablets #}
|
||||
<h3 class="text-sm font-semibold text-gray-700 uppercase tracking-wider mb-3">{{ gettext('Tablets') }}</h3>
|
||||
{% if assigned_tablets %}
|
||||
<div class="space-y-3 mb-6">
|
||||
{% for tablet in assigned_tablets %}
|
||||
<div class="flex items-center justify-between p-3 bg-gray-50 rounded-lg">
|
||||
<div>
|
||||
<div class="font-medium text-gray-900">{{ tablet.brand }} {{ tablet.model }}</div>
|
||||
<div class="text-sm text-gray-500">
|
||||
{{ gettext('Serial:') }} {{ tablet.serial_number }}
|
||||
</div>
|
||||
<div class="text-sm text-gray-500">
|
||||
{{ gettext('Status:') }}
|
||||
<span class="inline-flex items-center px-2 py-0.5 rounded-full text-xs font-medium
|
||||
{% if tablet.status == 'available' %}bg-green-100 text-green-800
|
||||
{% elif tablet.status == 'loaned' %}bg-yellow-100 text-yellow-800
|
||||
{% else %}bg-gray-100 text-gray-800{% endif %}">
|
||||
{{ tablet.status }}
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
{% endfor %}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="text-center">
|
||||
<a href="{{ url_for('list_students') }}" class="btn btn-secondary">
|
||||
<i class="bi bi-arrow-left"></i> {{ gettext('Back to Students') }}
|
||||
</a>
|
||||
{% else %}
|
||||
<p class="text-gray-500 text-sm mb-6">{{ gettext('No tablets assigned.') }}</p>
|
||||
{% endif %}
|
||||
|
||||
{# Assigned Non-Loanable Devices #}
|
||||
<h3 class="text-sm font-semibold text-gray-700 uppercase tracking-wider mb-3">{{ gettext('Non-Loanable Devices') }}</h3>
|
||||
{% if assigned_devices %}
|
||||
<div class="space-y-3">
|
||||
{% for device in assigned_devices %}
|
||||
<div class="flex items-center justify-between p-3 bg-gray-50 rounded-lg">
|
||||
<div>
|
||||
<div class="font-medium text-gray-900">{{ device.brand }} {{ device.model }}</div>
|
||||
<div class="text-sm text-gray-500">
|
||||
{{ gettext('Serial:') }} {{ device.serial_number }}
|
||||
</div>
|
||||
<div class="text-sm text-gray-500">
|
||||
{{ gettext('Type:') }} {{ device.device_type }}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
{% endfor %}
|
||||
</div>
|
||||
{% else %}
|
||||
<p class="text-gray-500 text-sm">{{ gettext('No non-loanable devices assigned.') }}</p>
|
||||
{% endif %}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue