390 lines
19 KiB
HTML
390 lines
19 KiB
HTML
{% extends "base.html" %}
|
|
|
|
{% block title %}Commissions Management - Edu-Space Admin{% endblock %}
|
|
|
|
{% block content %}
|
|
<div class="container-fluid pt-5 mt-4">
|
|
<div class="row">
|
|
<div class="col-12">
|
|
<div class="d-flex justify-content-between align-items-center mb-4">
|
|
<h1 class="h3">
|
|
<i class="bi bi-diagram-3 text-info"></i> Commissions Management
|
|
</h1>
|
|
<div class="btn-group">
|
|
<a href="{{ url_for('schedule.add_reservation') }}" class="btn btn-success">
|
|
<i class="bi bi-plus-circle"></i> New Reservation
|
|
</a>
|
|
<a href="{{ url_for('schedule.today_schedule') }}" class="btn btn-outline-secondary">
|
|
<i class="bi bi-calendar-day"></i> Today's Schedule
|
|
</a>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Statistics Cards -->
|
|
<div class="row mb-4">
|
|
<div class="col-md-3">
|
|
<div class="card border-0 shadow-sm">
|
|
<div class="card-body text-center">
|
|
<h2 class="text-primary">{{ subjects|length }}</h2>
|
|
<p class="mb-0">Active Subjects</p>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
<div class="col-md-3">
|
|
<div class="card border-0 shadow-sm">
|
|
<div class="card-body text-center">
|
|
<h2 class="text-success">{{ subjects|map(attribute='commissions')|map('length')|sum }}</h2>
|
|
<p class="mb-0">Total Commissions</p>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
<div class="col-md-3">
|
|
<div class="card border-0 shadow-sm">
|
|
<div class="card-body text-center">
|
|
<h2 class="text-success">{{ total_commissions }}</h2>
|
|
|
|
<h2 class="text-info">{{ total_capacity }}</h2>
|
|
<p class="mb-0">Total Student Capacity</p>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
<div class="col-md-3">
|
|
<div class="card border-0 shadow-sm">
|
|
<div class="card-body text-center">
|
|
<h2 class="text-warning">{{ subjects|selectattr('commissions')|list|length }}</h2>
|
|
<p class="mb-0">Subjects with Commissions</p>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Subjects and Commissions -->
|
|
<div class="row">
|
|
{% for subject in subjects %}
|
|
<div class="col-lg-6 mb-4">
|
|
<div class="card border-0 shadow-sm">
|
|
<div class="card-header bg-white border-0">
|
|
<div class="d-flex justify-content-between align-items-start">
|
|
<div>
|
|
<h5 class="mb-1">
|
|
<span class="badge bg-primary me-2">{{ subject.code }}</span>
|
|
{{ subject.name }}
|
|
</h5>
|
|
<p class="text-muted mb-0 small">
|
|
<i class="bi bi-building"></i> {{ subject.department }} •
|
|
<i class="bi bi-award"></i> {{ subject.credits }} credits
|
|
</p>
|
|
</div>
|
|
<div class="text-end">
|
|
<span class="badge bg-info">{{ subject.commissions|length }} commission{{ 's' if subject.commissions|length != 1 else '' }}</span>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
<div class="card-body">
|
|
{% if subject.commissions %}
|
|
<div class="table-responsive">
|
|
<table class="table table-sm table-hover">
|
|
<thead class="table-light">
|
|
<tr>
|
|
<th>Commission</th>
|
|
<th>Semester</th>
|
|
<th>Teacher</th>
|
|
<th>Students</th>
|
|
<th>Status</th>
|
|
<th>Actions</th>
|
|
</tr>
|
|
</thead>
|
|
<tbody>
|
|
{% for commission in subject.commissions %}
|
|
<tr>
|
|
<td>
|
|
<strong>{{ commission.code }}</strong>
|
|
{% if commission.schedule %}
|
|
<br><small class="text-muted">{{ commission.schedule }}</small>
|
|
{% endif %}
|
|
</td>
|
|
<td>
|
|
<span class="badge bg-light text-dark">{{ commission.semester }} {{ commission.year }}</span>
|
|
</td>
|
|
<td>
|
|
{% if commission.teacher %}
|
|
<small>{{ commission.teacher.first_name }} {{ commission.teacher.last_name }}</small>
|
|
{% else %}
|
|
<small class="text-muted">Not assigned</small>
|
|
{% endif %}
|
|
</td>
|
|
<td>
|
|
<div class="d-flex align-items-center">
|
|
<small>{{ commission.current_students }}/{{ commission.max_students }}</small>
|
|
{% set fill_percentage = (commission.current_students / commission.max_students * 100) %}
|
|
<div class="progress ms-2" style="height: 4px; width: 30px;">
|
|
<div class="progress-bar
|
|
{% if fill_percentage > 90 %}bg-danger
|
|
{% elif fill_percentage > 75 %}bg-warning
|
|
{% else %}bg-success
|
|
{% endif %}"
|
|
style="width: {{ fill_percentage }}%"></div>
|
|
</div>
|
|
</div>
|
|
</td>
|
|
<td>
|
|
{% if commission.active %}
|
|
<span class="badge bg-success">Active</span>
|
|
{% else %}
|
|
<span class="badge bg-secondary">Inactive</span>
|
|
{% endif %}
|
|
</td>
|
|
<td>
|
|
<div class="btn-group btn-group-sm">
|
|
<button class="btn btn-outline-primary quick-reserve-btn"
|
|
data-commission-id="{{ commission.id }}"
|
|
data-max-students="{{ commission.max_students }}"
|
|
data-commission-name="{{ commission.get_full_code() }}"
|
|
data-subject-name="{{ subject.name }}"
|
|
title="Quick Reservation">
|
|
<i class="bi bi-calendar-plus"></i>
|
|
</button>
|
|
<button class="btn btn-outline-info view-schedule-btn"
|
|
data-commission-id="{{ commission.id }}"
|
|
title="View Schedule">
|
|
<i class="bi bi-calendar-week"></i>
|
|
</button>
|
|
</div>
|
|
</td>
|
|
</tr>
|
|
{% endfor %}
|
|
</tbody>
|
|
</table>
|
|
</div>
|
|
{% else %}
|
|
<div class="text-center text-muted py-3">
|
|
<i class="bi bi-inbox fs-2 d-block mb-2"></i>
|
|
No commissions registered for this subject
|
|
</div>
|
|
{% endif %}
|
|
</div>
|
|
{% if subject.description %}
|
|
<div class="card-footer bg-light">
|
|
<small class="text-muted">
|
|
<i class="bi bi-info-circle"></i> {{ subject.description|truncatewords(20) }}
|
|
</small>
|
|
</div>
|
|
{% endif %}
|
|
</div>
|
|
</div>
|
|
{% else %}
|
|
<div class="col-12">
|
|
<div class="text-center py-5">
|
|
<i class="bi bi-inbox fs-1 text-muted d-block mb-3"></i>
|
|
<h4 class="text-muted">No Subjects Found</h4>
|
|
<p class="text-muted">There are no active subjects in the system.</p>
|
|
<a href="{{ url_for('classrooms.index') }}" class="btn btn-primary">
|
|
<i class="bi bi-plus-circle"></i> Add New Subject
|
|
</a>
|
|
</div>
|
|
</div>
|
|
{% endfor %}
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Quick Reservation Modal -->
|
|
<div class="modal fade" id="quickReservationModal" tabindex="-1">
|
|
<div class="modal-dialog modal-lg">
|
|
<div class="modal-content">
|
|
<div class="modal-header">
|
|
<h5 class="modal-title">
|
|
<i class="bi bi-calendar-plus text-success"></i> Quick Reservation
|
|
</h5>
|
|
<button type="button" class="btn-close" data-bs-dismiss="modal"></button>
|
|
</div>
|
|
<div class="modal-body">
|
|
<form id="quick-reservation-form">
|
|
<input type="hidden" id="quick-commission-id" name="commission_id">
|
|
|
|
<div class="alert alert-info">
|
|
<h6 class="alert-heading">
|
|
<i class="bi bi-info-circle"></i> Commission Details
|
|
</h6>
|
|
<div id="commission-details"></div>
|
|
</div>
|
|
|
|
<div class="row">
|
|
<div class="col-md-6 mb-3">
|
|
<label for="quick-date" class="form-label">Date</label>
|
|
<input type="date" class="form-control" id="quick-date" required>
|
|
</div>
|
|
<div class="col-md-3 mb-3">
|
|
<label for="quick-start-time" class="form-label">Start Time</label>
|
|
<input type="time" class="form-control" id="quick-start-time" required>
|
|
</div>
|
|
<div class="col-md-3 mb-3">
|
|
<label for="quick-end-time" class="form-label">End Time</label>
|
|
<input type="time" class="form-control" id="quick-end-time" required>
|
|
</div>
|
|
</div>
|
|
|
|
<div class="row">
|
|
<div class="col-md-12 mb-3">
|
|
<label for="quick-purpose" class="form-label">Purpose</label>
|
|
<input type="text" class="form-control" id="quick-purpose" placeholder="Regular class, exam, review, etc." required>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Classroom Selection & Availability -->
|
|
<div class="mb-3">
|
|
<label class="form-label">Select Classroom</label>
|
|
<div class="d-flex align-items-center mb-2">
|
|
<select class="form-select" id="quick-classroom-select" required>
|
|
<option value="">Choose date and time first</option>
|
|
</select>
|
|
<button type="button" class="btn btn-outline-primary ms-2" id="check-availability-btn" disabled>
|
|
<i class="bi bi-search"></i> Check
|
|
</button>
|
|
</div>
|
|
<div id="availability-results" class="mt-3"></div>
|
|
</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-success" id="create-reservation-btn" disabled>
|
|
<i class="bi bi-plus-circle"></i> Create Reservation
|
|
</button>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
{% endblock %}
|
|
|
|
{% block extra_js %}
|
|
<script>
|
|
$(document).ready(function() {
|
|
// Quick Reservation Button Click
|
|
$('.quick-reserve-btn').on('click', function() {
|
|
const commissionId = $(this).data('commission-id');
|
|
const maxStudents = $(this).data('max-students');
|
|
const commissionName = $(this).data('commission-name');
|
|
const subjectName = $(this).data('subject-name');
|
|
|
|
// Set commission details in modal
|
|
$('#quick-commission-id').val(commissionId);
|
|
$('#commission-details').html(`
|
|
<strong>${commissionName}</strong><br>
|
|
<small>Subject: ${subjectName}</small><br>
|
|
<small>Max Students: ${maxStudents}</small>
|
|
`);
|
|
|
|
// Reset form
|
|
$('#quick-date').val('');
|
|
$('#quick-start-time').val('');
|
|
$('#quick-end-time').val('');
|
|
$('#quick-purpose').val('');
|
|
$('#quick-classroom-select').html('<option value="">Choose date and time first</option>').prop('disabled', true);
|
|
$('#check-availability-btn').prop('disabled', true);
|
|
$('#create-reservation-btn').prop('disabled', true);
|
|
$('#availability-results').empty();
|
|
|
|
// Show modal
|
|
$('#quickReservationModal').modal('show');
|
|
});
|
|
|
|
// Enable date/time inputs to check availability
|
|
function validateDateTimeInputs() {
|
|
const date = $('#quick-date').val();
|
|
const startTime = $('#quick-start-time').val();
|
|
const endTime = $('#quick-end-time').val();
|
|
|
|
const isValid = date && startTime && endTime && startTime < endTime;
|
|
$('#check-availability-btn').prop('disabled', !isValid);
|
|
|
|
if (isValid) {
|
|
loadClassroomOptions();
|
|
}
|
|
}
|
|
|
|
$('#quick-date, #quick-start-time, #quick-end-time').on('change', validateDateTimeInputs);
|
|
|
|
function loadClassroomOptions() {
|
|
$.get('/schedule/api/classroom-availability', {
|
|
date: $('#quick-date').val(),
|
|
start_time: $('#quick-start-time').val(),
|
|
end_time: $('#quick-end-time').val(),
|
|
max_students: parseInt($('#quick-commission-id').data('max-students'))
|
|
}, function(response) {
|
|
const classroomSelect = $('#quick-classroom-select');
|
|
classroomSelect.empty().append('<option value="">Select a classroom</option>');
|
|
|
|
// Add available classrooms
|
|
response.availability.available.forEach(function(classroom) {
|
|
classroomSelect.append(`<option value="${classroom.id}">${classroom.code} - ${classroom.name} (Cap: ${classroom.capacity}) - ${classroom.location}</option>`);
|
|
});
|
|
|
|
// Enable selection
|
|
classroomSelect.prop('disabled', response.availability.available.length === 0);
|
|
|
|
// Show recommendation if available
|
|
if (response.recommendation) {
|
|
const rec = response.recommendation.classroom;
|
|
$('#availability-results').html(`
|
|
<div class="alert alert-success">
|
|
<h6><i class="bi bi-star-fill"></i> Recommended Classroom</h6>
|
|
<strong>${rec.code} - ${rec.name}</strong><br>
|
|
<small>Capacity: ${rec.capacity} | Location: ${rec.location}</small><br>
|
|
<small class="text-muted">${response.recommendation.reason}</small>
|
|
</div>
|
|
`);
|
|
}
|
|
|
|
// Show statistics
|
|
$('#availability-results').append(`
|
|
<div class="alert alert-info mt-2">
|
|
<small>
|
|
<strong>Availability:</strong> ${response.availability.stats.total_available} available,
|
|
${response.availability.stats.total_occupied} occupied classrooms
|
|
</small>
|
|
</div>
|
|
`);
|
|
});
|
|
});
|
|
}
|
|
|
|
// Check availability button click
|
|
$('#check-availability-btn').on('click', loadClassroomOptions);
|
|
|
|
// Classroom selection enables create button
|
|
$('#quick-classroom-select').on('change', function() {
|
|
const isValid = $(this).val() && $('#quick-purpose').val();
|
|
$('#create-reservation-btn').prop('disabled', !isValid);
|
|
});
|
|
|
|
// Purpose input validation
|
|
$('#quick-purpose').on('input', function() {
|
|
const isValid = $('#quick-classroom-select').val() && $(this).val();
|
|
$('#create-reservation-btn').prop('disabled', !isValid);
|
|
});
|
|
|
|
// Create reservation
|
|
$('#create-reservation-btn').on('click', function() {
|
|
const formData = {
|
|
commission_id: $('#quick-commission-id').val(),
|
|
classroom_id: $('#quick-classroom-select').val(),
|
|
purpose: $('#quick-purpose').val(),
|
|
start_time: `${$('#quick-date').val()} ${$('#quick-start-time').val()}`,
|
|
end_time: `${$('#quick-date').val()} ${$('#quick-end-time').val()}`,
|
|
expected_attendees: $('#quick-commission-id').data('max-students')
|
|
};
|
|
|
|
$.post('/schedule/reservation/add', formData)
|
|
.done(function() {
|
|
$('#quickReservationModal').modal('hide');
|
|
location.reload();
|
|
})
|
|
.fail(function(xhr) {
|
|
alert('Error creating reservation: ' + xhr.responseJSON.error);
|
|
});
|
|
});
|
|
});
|
|
</script>
|
|
{% endblock %} |