fix(folder): remove unwanted folder
This commit is contained in:
1 parent
c97fa1ab83
commit
929faab1fa
45 files changed
-14279
No files matched your search
@@ -1,985 +0,0 @@
|
||||
{% extends 'shared/base.html' %}
|
||||
{% load static %}
|
||||
{% load tz %}
|
||||
{% load app_filters %}
|
||||
|
||||
{% block extrahead %}
|
||||
<style>
|
||||
/* Quick Action Links Styling */
|
||||
.quick-link {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
padding: 12px 15px;
|
||||
margin-bottom: 8px;
|
||||
border-radius: 8px;
|
||||
background-color: #f8f9fa;
|
||||
color: #212529;
|
||||
transition: all 0.2s ease;
|
||||
text-decoration: none;
|
||||
border: 1px solid rgba(0,0,0,0.05);
|
||||
}
|
||||
|
||||
.quick-link:hover {
|
||||
background-color: #e9ecef;
|
||||
transform: translateY(-2px);
|
||||
box-shadow: 0 4px 8px rgba(0,0,0,0.1);
|
||||
color: var(--primary);
|
||||
}
|
||||
|
||||
.quick-link i {
|
||||
font-size: 1.2rem;
|
||||
margin-right: 12px;
|
||||
width: 24px;
|
||||
text-align: center;
|
||||
color: var(--primary);
|
||||
}
|
||||
|
||||
.quick-link span {
|
||||
font-weight: 500;
|
||||
}
|
||||
|
||||
@media (max-width: 768px) {
|
||||
.quick-link {
|
||||
padding: 10px;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
{% endblock %}
|
||||
|
||||
{% block content %}
|
||||
<!-- Add CSRF token to the page -->
|
||||
{% csrf_token %}
|
||||
|
||||
<div class="container-fluid py-4">
|
||||
<!-- Welcome Section -->
|
||||
{% include 'shared/welcome_section.html' with user=officer %}
|
||||
|
||||
<!-- Dashboard Content -->
|
||||
<div id="dashboardSection" class="content-section active">
|
||||
<!-- Summary Cards -->
|
||||
<div class="row mb-4">
|
||||
<div class="col-md-6 col-lg-3 mb-3">
|
||||
<div class="summary-card h-100">
|
||||
<div class="card-header">
|
||||
<h5 class="card-title mb-0"><i class="fas fa-users me-2"></i> Students</h5>
|
||||
</div>
|
||||
<div class="card-body d-flex flex-column">
|
||||
<div class="mb-3 text-center">
|
||||
<h2 class="fw-bold mb-0">{{ total_students }}</h2>
|
||||
<p class="text-muted">Total Students</p>
|
||||
</div>
|
||||
<div class="text-center">
|
||||
<div class="d-flex justify-content-around">
|
||||
<div>
|
||||
<h4 class="fw-bold mb-0">{{ active_students }}</h4>
|
||||
<p class="text-muted">Active</p>
|
||||
</div>
|
||||
<div>
|
||||
<h4 class="fw-bold mb-0">{{ new_students }}</h4>
|
||||
<p class="text-muted">New (30 days)</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="col-md-6 col-lg-3 mb-3">
|
||||
<div class="summary-card h-100">
|
||||
<div class="card-header">
|
||||
<h5 class="card-title mb-0"><i class="fas fa-graduation-cap me-2"></i> Courses</h5>
|
||||
</div>
|
||||
<div class="card-body d-flex flex-column">
|
||||
<div class="mb-3 text-center">
|
||||
<h2 class="fw-bold mb-0">{{ total_courses }}</h2>
|
||||
<p class="text-muted">Total Courses</p>
|
||||
</div>
|
||||
<div class="text-center">
|
||||
<div class="d-flex justify-content-around">
|
||||
<div>
|
||||
<h4 class="fw-bold mb-0">{{ active_courses }}</h4>
|
||||
<p class="text-muted">Active</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="col-md-6 col-lg-3 mb-3">
|
||||
<div class="summary-card h-100">
|
||||
<div class="card-header">
|
||||
<h5 class="card-title mb-0"><i class="fas fa-layer-group me-2"></i> Batches</h5>
|
||||
</div>
|
||||
<div class="card-body text-center">
|
||||
<h2 class="fw-bold mb-3">{{ total_batches }}</h2>
|
||||
<div class="d-flex justify-content-around">
|
||||
<div>
|
||||
<h4 class="fw-bold mb-0">{{ active_batches }}</h4>
|
||||
<p class="text-muted">Active</p>
|
||||
</div>
|
||||
<div>
|
||||
<button type="button" class="btn btn-sm btn-primary" onclick="openAddBatchModal()">
|
||||
<i class="fas fa-plus-circle"></i> Add
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="col-md-6 col-lg-3 mb-3">
|
||||
<div class="summary-card h-100">
|
||||
<div class="card-header">
|
||||
<h5 class="card-title mb-0"><i class="fas fa-calendar-minus me-2"></i> Leave Status</h5>
|
||||
</div>
|
||||
<div class="card-body">
|
||||
<div class="d-flex justify-content-between mb-3">
|
||||
<p class="mb-0">Pending</p>
|
||||
<span class="badge leave-pending">{{ pending_leaves }}</span>
|
||||
</div>
|
||||
<div class="d-flex justify-content-between mb-3">
|
||||
<p class="mb-0">Approved</p>
|
||||
<span class="badge leave-approved">{{ approved_leaves }}</span>
|
||||
</div>
|
||||
<div class="d-flex justify-content-between mb-3">
|
||||
<p class="mb-0">Rejected</p>
|
||||
<span class="badge leave-rejected">{{ rejected_leaves }}</span>
|
||||
</div>
|
||||
<button type="button" class="btn btn-sm btn-outline-primary w-100 mt-2" id="requestLeaveBtn" onclick="openLeaveModal()">
|
||||
<i class="fas fa-plus-circle me-2"></i> Request Leave
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Quick Actions Section -->
|
||||
<div class="row mb-4">
|
||||
<div class="col-12">
|
||||
<div class="content-card">
|
||||
<div class="d-flex justify-content-between align-items-center mb-3">
|
||||
<h5 class="mb-0"><i class="fas fa-bolt me-2"></i> Quick Actions</h5>
|
||||
</div>
|
||||
<div class="row">
|
||||
<div class="col-md-6">
|
||||
<a href="#" class="btn btn-outline-primary d-flex align-items-center mb-2 p-2" onclick="openAddStudentModal(); return false;">
|
||||
<i class="fas fa-user-plus me-2"></i>
|
||||
<span>Add New Student</span>
|
||||
</a>
|
||||
|
||||
<a href="#" class="btn btn-outline-primary d-flex align-items-center mb-2 p-2" onclick="openAddBatchModal(); return false;">
|
||||
<i class="fas fa-layer-group me-2"></i>
|
||||
<span>Add New Batch</span>
|
||||
</a>
|
||||
</div>
|
||||
<div class="col-md-6">
|
||||
<a href="#" class="btn btn-outline-primary d-flex align-items-center mb-2 p-2" onclick="openAddCourseTrackingModal(); return false;">
|
||||
<i class="fas fa-chart-line me-2"></i>
|
||||
<span>Add Course Tracking</span>
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Notices -->
|
||||
<div class="row">
|
||||
<div class="col-lg-6 mb-4 notice-section">
|
||||
<div class="content-card">
|
||||
<h5 class="mb-3"><i class="fas fa-bullhorn me-2"></i> Recent Notices</h5>
|
||||
{% if notices %}
|
||||
<div class="list-group" style="max-height: 300px; overflow-y: auto;">
|
||||
{% for notice in notices %}
|
||||
<div class="list-group-item list-group-item-action">
|
||||
<div class="d-flex w-100 justify-content-between flex-wrap">
|
||||
<h6 class="mb-1">{{ notice.title }}</h6>
|
||||
<small class="text-muted">{{ notice.created_at|timesince }} ago</small>
|
||||
</div>
|
||||
<p class="mb-1">{{ notice.message }}</p>
|
||||
</div>
|
||||
{% endfor %}
|
||||
</div>
|
||||
{% else %}
|
||||
<div class="alert alert-info">
|
||||
<i class="fas fa-info-circle me-2"></i> No recent notices.
|
||||
</div>
|
||||
{% endif %}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Leave Requests -->
|
||||
<div class="col-lg-6 mb-4">
|
||||
<div class="content-card">
|
||||
<h5 class="mb-3"><i class="fas fa-calendar-minus me-2"></i> Leave Requests</h5>
|
||||
{% if recent_leaves %}
|
||||
<div class="table-responsive" style="max-height: 300px; overflow-y: auto;">
|
||||
<table class="table table-responsive table-hover">
|
||||
<thead style="position: sticky; top: 0; background: white; z-index: 1;">
|
||||
<tr>
|
||||
<th>Start Date</th>
|
||||
<th>End Date</th>
|
||||
<th>Message</th>
|
||||
<th>Status</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{% for leave in recent_leaves %}
|
||||
<tr>
|
||||
<td>{{ leave.start_date|date:"M d, Y" }}</td>
|
||||
<td>{{ leave.end_date|date:"M d, Y" }}</td>
|
||||
<td>{{ leave.message|truncatechars:50 }}</td>
|
||||
<td>
|
||||
<span class="badge
|
||||
{% if leave.status == 0 %}leave-pending
|
||||
{% elif leave.status == 1 %}leave-approved
|
||||
{% else %}leave-rejected{% endif %}">
|
||||
{{ leave.get_status_display }}
|
||||
</span>
|
||||
</td>
|
||||
</tr>
|
||||
{% endfor %}
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
{% else %}
|
||||
<div class="alert alert-info">
|
||||
<i class="fas fa-info-circle me-2"></i> No leave requests found.
|
||||
</div>
|
||||
{% endif %}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Students Section -->
|
||||
<div id="studentsSection" class="content-section">
|
||||
<div class="content-card">
|
||||
<div class="d-flex justify-content-between align-items-center mb-4">
|
||||
<h5 class="mb-0"><i class="fas fa-users me-2"></i> Student Management</h5>
|
||||
<button type="button" class="btn btn-primary" onclick="openAddStudentModal()">
|
||||
<i class="fas fa-user-plus me-1"></i> Add Student
|
||||
</button>
|
||||
</div>
|
||||
|
||||
<!-- Search and Filter Section -->
|
||||
<div class="row mb-4">
|
||||
<div class="col-md-8">
|
||||
<div class="input-group">
|
||||
<span class="input-group-text"><i class="fas fa-search"></i></span>
|
||||
<input type="text" class="form-control" id="studentSearch" placeholder="Search by name, phone, or course..." onkeyup="filterStudents()">
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-md-4">
|
||||
<select class="form-select" id="statusFilter" onchange="filterStudents()">
|
||||
<option value="">All Status</option>
|
||||
<option value="Active">Active</option>
|
||||
<option value="Leave">Leave</option>
|
||||
<option value="Completed">Completed</option>
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Student List Table -->
|
||||
<div class="table-responsive">
|
||||
<table class="table table-hover table-responsive id="studentTable">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>ID</th>
|
||||
<th>Student Details</th>
|
||||
<th>Contact Info</th>
|
||||
<th>Parent Details</th>
|
||||
<th>Course & Batch</th>
|
||||
<th>Status</th>
|
||||
<th>Actions</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{% for student in students|dictsort:"id" %}
|
||||
<tr>
|
||||
<td>{{ student.id }}</td>
|
||||
<td>
|
||||
<div class="d-flex align-items-center">
|
||||
{% if student.image %}
|
||||
<img src="{{ student.image.url }}" alt="{{ student.name }}" class="rounded-circle me-2" style="width: 30px; height: 30px; object-fit: cover;">
|
||||
{% endif %}
|
||||
<div>
|
||||
<strong>{{ student.name }}</strong>
|
||||
{% if student.gender %}
|
||||
<br><small class="text-muted">{{ student.gender }}</small>
|
||||
{% endif %}
|
||||
</div>
|
||||
</div>
|
||||
</td>
|
||||
<td>
|
||||
<div>
|
||||
<i class="fas fa-phone me-1"></i> {{ student.phone }}<br>
|
||||
{% if student.email %}
|
||||
<i class="fas fa-envelope me-1"></i> {{ student.email }}<br>
|
||||
{% endif %}
|
||||
</div>
|
||||
</td>
|
||||
<td>
|
||||
<div>
|
||||
{% if student.parent_name %}
|
||||
<i class="fas fa-user me-1"></i> {{ student.parent_name }}<br>
|
||||
{% endif %}
|
||||
{% if student.parent_phone %}
|
||||
<i class="fas fa-phone me-1"></i> {{ student.parent_phone }}
|
||||
{% endif %}
|
||||
</div>
|
||||
</td>
|
||||
<td>
|
||||
<div>
|
||||
{% if student.course %}
|
||||
<strong>{{ student.course.name }}</strong>
|
||||
{% if student.course.code %}({{ student.course.code }}){% endif %}
|
||||
{% endif %}
|
||||
<br>
|
||||
{% for batch in student.batches.all %}
|
||||
<span class="badge bg-info">{{ batch.name }}</span>
|
||||
{% if not forloop.last %}, {% endif %}
|
||||
{% empty %}
|
||||
<small class="text-muted">No batch assigned</small>
|
||||
{% endfor %}
|
||||
</div>
|
||||
</td>
|
||||
<td>
|
||||
<span class="badge {% if student.status == 'Active' %}bg-success{% elif student.status == 'Leave' %}bg-warning{% else %}bg-secondary{% endif %}">
|
||||
{{ student.status|default:"Not Set" }}
|
||||
</span>
|
||||
</td>
|
||||
<td>
|
||||
<div class="btn-group btn-group-custom">
|
||||
<button type="button"
|
||||
class="btn btn-sm btn-primary"
|
||||
data-action="view"
|
||||
data-id="{{ student.id }}">
|
||||
<i class="fas fa-eye"></i>
|
||||
</button>
|
||||
<button type="button"
|
||||
class="btn btn-sm btn-info"
|
||||
data-action="edit"
|
||||
data-id="{{ student.id }}">
|
||||
<i class="fas fa-edit"></i>
|
||||
</button>
|
||||
</div>
|
||||
</td>
|
||||
</tr>
|
||||
{% empty %}
|
||||
<tr>
|
||||
<td colspan="7" class="text-center">No students found</td>
|
||||
</tr>
|
||||
{% endfor %}
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Batches Section -->
|
||||
<div id="batchesSection" class="content-section">
|
||||
<div class="content-card">
|
||||
<div class="d-flex justify-content-between align-items-center mb-4">
|
||||
<h5 class="mb-0"><i class="fas fa-layer-group me-2"></i> Batch Management</h5>
|
||||
<button type="button" class="btn btn-primary" onclick="openAddBatchModal()">
|
||||
<i class="fas fa-plus-circle me-1"></i> Add Batch
|
||||
</button>
|
||||
</div>
|
||||
|
||||
<!-- Batch List Table -->
|
||||
<div class="table-responsive">
|
||||
<table class="table table-hover">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>Name</th>
|
||||
<th>Year</th>
|
||||
<th>Status</th>
|
||||
<th>Students</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{% for batch in batches %}
|
||||
<tr>
|
||||
<td>{{ batch.name }}</td>
|
||||
<td>{{ batch.year|date:"Y"|default:"-" }}</td>
|
||||
<td>
|
||||
<span class="badge {% if batch.is_active %}bg-success{% else %}bg-secondary{% endif %}">
|
||||
{{ batch.is_active|yesno:"Active,Inactive" }}
|
||||
</span>
|
||||
</td>
|
||||
<td>{{ batch.students.count }}</td>
|
||||
</tr>
|
||||
{% empty %}
|
||||
<tr>
|
||||
<td colspan="4" class="text-center">No batches found</td>
|
||||
</tr>
|
||||
{% endfor %}
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Courses Section -->
|
||||
<div id="coursesSection" class="content-section">
|
||||
<div class="content-card">
|
||||
<div class="d-flex justify-content-between align-items-center mb-4">
|
||||
<h5 class="mb-0"><i class="fas fa-graduation-cap me-2"></i> Course List</h5>
|
||||
</div>
|
||||
|
||||
<!-- Course list will be loaded here -->
|
||||
<div id="courseListContainer">
|
||||
<div class="table-responsive">
|
||||
<table class="table table-hover table-responsive">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>Name</th>
|
||||
<th>Code</th>
|
||||
<th>Duration</th>
|
||||
<th>Type</th>
|
||||
<th>Status</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{% for course in courses %}
|
||||
<tr>
|
||||
<td>{{ course.name }}</td>
|
||||
<td>{{ course.code|default:"-" }}</td>
|
||||
<td>
|
||||
{% if course.duration_type == "Semester" %}
|
||||
{{ course.duration }} years ({{ course.duration|multiply:2 }} semesters)
|
||||
{% else %}
|
||||
{{ course.duration }} years
|
||||
{% endif %}
|
||||
</td>
|
||||
<td>{{ course.duration_type }}</td>
|
||||
<td>
|
||||
<span class="badge {% if course.is_active %}bg-success{% else %}bg-secondary{% endif %}">
|
||||
{{ course.is_active|yesno:"Active,Inactive" }}
|
||||
</span>
|
||||
</td>
|
||||
</tr>
|
||||
{% endfor %}
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Course Tracking Section -->
|
||||
<div class="content-section" id="courseTrackingSection" style="display: none;">
|
||||
<div class="content-card">
|
||||
<div class="d-flex justify-content-between align-items-center mb-3">
|
||||
<h5 class="mb-0"><i class="fas fa-chart-line me-2"></i> Course Tracking Management</h5>
|
||||
<button type="button" class="btn btn-sm btn-primary" onclick="openAddCourseTrackingModal()">
|
||||
<i class="fas fa-plus-circle me-1"></i> Add New
|
||||
</button>
|
||||
</div>
|
||||
|
||||
<!-- Search and Filter Section -->
|
||||
<div class="row mb-4">
|
||||
<div class="col-md-8">
|
||||
<div class="input-group">
|
||||
<span class="input-group-text"><i class="fas fa-search"></i></span>
|
||||
<input type="text" class="form-control" id="trackingSearch" placeholder="Search by student or course name..." onkeyup="filterTrackings()">
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-md-4">
|
||||
<select class="form-select" id="progressFilter" onchange="filterTrackings()">
|
||||
<option value="">All Progress Status</option>
|
||||
<option value="Not Started">Not Started</option>
|
||||
<option value="In Progress">In Progress</option>
|
||||
<option value="Completed">Completed</option>
|
||||
<option value="Dropped">Dropped</option>
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Course Tracking List -->
|
||||
<div class="table-responsive">
|
||||
<table class="table table-hover table-responsive" id="trackingTable">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>Student</th>
|
||||
<th>Course</th>
|
||||
<th>Progress</th>
|
||||
<th>Completion %</th>
|
||||
<th>Start Date</th>
|
||||
<th>Period</th>
|
||||
<th>Actions</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{% for tracking in recent_trackings %}
|
||||
<tr data-tracking-id="{{ tracking.id }}">
|
||||
<td>{{ tracking.student.name }}</td>
|
||||
<td>{{ tracking.course.name }}</td>
|
||||
<td>
|
||||
<span class="status-badge {% if tracking.progress_status == 'Completed' %}bg-success{% elif tracking.progress_status == 'In Progress' %}bg-warning{% else %}bg-secondary{% endif %}">
|
||||
{{ tracking.progress_status }}
|
||||
</span>
|
||||
</td>
|
||||
<td>
|
||||
<div class="progress">
|
||||
<div class="progress-bar"
|
||||
role="progressbar"
|
||||
style="width: {{ tracking.completion_percentage }}%"
|
||||
aria-valuenow="{{ tracking.completion_percentage }}"
|
||||
aria-valuemin="0"
|
||||
aria-valuemax="100">
|
||||
</div>
|
||||
</div>
|
||||
<div class="progress-text">{{ tracking.completion_percentage }}%</div>
|
||||
</td>
|
||||
<td>{{ tracking.start_date|date:"M d, Y" }}</td>
|
||||
<td>{{ tracking.current_period_display }}</td>
|
||||
<td>
|
||||
<div class="btn-group btn-group-custom">
|
||||
<button type="button" class="btn btn-sm btn-primary" onclick="viewTracking('{{ tracking.id }}')">
|
||||
<i class="fas fa-eye"></i>
|
||||
</button>
|
||||
<button type="button" class="btn btn-sm btn-info" onclick="editTracking('{{ tracking.id }}')">
|
||||
<i class="fas fa-edit"></i>
|
||||
</button>
|
||||
</div>
|
||||
</td>
|
||||
</tr>
|
||||
{% endfor %}
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Add Course Tracking Modal -->
|
||||
<div id="addCourseTrackingModal" style="display: none; position: fixed; top: 0; left: 0; right: 0; bottom: 0; background-color: rgba(0,0,0,0.5); z-index: 10000;" onclick="if(event.target === this) closeAddCourseTrackingModal();">
|
||||
<div style="position: absolute; top: 50%; left: 50%; transform: translate(-50%, -50%); width: 90%; max-width: 700px; background-color: white; border-radius: 8px; box-shadow: 0 2px 10px rgba(0,0,0,0.2); max-height: 90vh; overflow: hidden;">
|
||||
<div style="background-color: #3F51B5; color: white; padding: 15px; border-top-left-radius: 8px; border-top-right-radius: 8px; display: flex; justify-content: space-between; align-items: center;">
|
||||
<h5 style="margin: 0; font-size: 18px;">Add Course Tracking</h5>
|
||||
<button style="background: none; border: none; color: white; font-size: 24px; cursor: pointer; line-height: 24px;" onclick="closeAddCourseTrackingModal()">×</button>
|
||||
</div>
|
||||
<div style="padding: 20px; max-height: calc(90vh - 60px); overflow-y: auto; -webkit-overflow-scrolling: touch;">
|
||||
<form id="addCourseTrackingForm" method="post" action="/app/add-course-tracking/">
|
||||
{% csrf_token %}
|
||||
|
||||
<div class="row mb-3">
|
||||
<div class="col-md-6 mb-3 mb-md-0">
|
||||
<label for="student" class="form-label">Student *</label>
|
||||
<select class="form-select" id="student" name="student" required>
|
||||
<option value="">Select Student</option>
|
||||
{% for student in students %}
|
||||
<option value="{{ student.id }}">{{ student.name }} ({{ student.phone }})</option>
|
||||
{% endfor %}
|
||||
</select>
|
||||
</div>
|
||||
<div class="col-md-6">
|
||||
<label for="track_course" class="form-label">Course *</label>
|
||||
<select class="form-select" id="track_course" name="course" required>
|
||||
<option value="">Select Course</option>
|
||||
{% for course in courses %}
|
||||
<option value="{{ course.id }}">{{ course.name }}{% if course.code %} ({{ course.code }}){% endif %}</option>
|
||||
{% endfor %}
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="row mb-3">
|
||||
<div class="col-md-6 mb-3 mb-md-0">
|
||||
<label for="tracking_start_date" class="form-label">Start Date *</label>
|
||||
<input type="date" class="form-control" id="tracking_start_date" name="start_date" required>
|
||||
</div>
|
||||
<div class="col-md-6">
|
||||
<label for="expected_end_date" class="form-label">Expected End Date</label>
|
||||
<input type="date" class="form-control" id="expected_end_date" name="expected_end_date">
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="row mb-3">
|
||||
<div class="col-md-6 mb-3 mb-md-0">
|
||||
<label for="current_period" class="form-label">Current Period</label>
|
||||
<input type="number" class="form-control" id="current_period" name="current_period" value="1" min="1">
|
||||
</div>
|
||||
<div class="col-md-6">
|
||||
<label for="progress_status" class="form-label">Progress Status</label>
|
||||
<select class="form-select" id="progress_status" name="progress_status">
|
||||
<option value="Not Started">Not Started</option>
|
||||
<option value="In Progress" selected>In Progress</option>
|
||||
<option value="Completed">Completed</option>
|
||||
<option value="Dropped">Dropped</option>
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="mb-3">
|
||||
<label for="notes" class="form-label">Notes</label>
|
||||
<textarea class="form-control" id="notes" name="notes" rows="3"></textarea>
|
||||
</div>
|
||||
|
||||
<div class="d-flex justify-content-end gap-2">
|
||||
<button type="button" class="btn btn-secondary" onclick="closeAddCourseTrackingModal()">Cancel</button>
|
||||
<button type="submit" class="btn btn-primary">Add Course Tracking</button>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Profile Section -->
|
||||
<div id="profileSection" class="content-section">
|
||||
<div class="content-card">
|
||||
<h3 class="text-xl font-semibold mb-4">
|
||||
<i class="fas fa-user mr-2"></i> Profile Information
|
||||
</h3>
|
||||
<div class="row">
|
||||
<!-- Profile Header with Image and Actions -->
|
||||
<div class="col-12 mb-4">
|
||||
<div class="d-flex flex-wrap align-items-center justify-content-between">
|
||||
<div class="d-flex align-items-center mb-3 mb-md-0">
|
||||
{% if officer.image %}
|
||||
<img src="{{ officer.image.url }}" alt="{{ officer.name }}" class="rounded-circle me-3" style="width: 80px; height: 80px; object-fit: cover;">
|
||||
{% else %}
|
||||
<img src="{% static 'img/user.png' %}" alt="Default User" class="rounded-circle me-3" style="width: 80px; height: 80px; object-fit: cover;">
|
||||
{% endif %}
|
||||
<div>
|
||||
<h4 class="mb-1">{{ officer.name }}</h4>
|
||||
<p class="mb-0 text-muted">
|
||||
<span class="me-3"><i class="fas fa-id-card me-1"></i> {{ officer.id }}</span>
|
||||
<span class="me-3"><i class="fas fa-envelope me-1"></i> {{ officer.email }}</span>
|
||||
<span><i class="fas fa-phone me-1"></i> {{ officer.phone }}</span>
|
||||
</p>
|
||||
<div class="mt-2">
|
||||
<span class="badge {% if officer.is_active %}bg-success{% else %}bg-danger{% endif %} me-2">
|
||||
{{ officer.is_active|yesno:"Active,Inactive" }}
|
||||
</span>
|
||||
<span class="badge bg-info">{{ officer.designation }}</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="profile-actions d-flex gap-2">
|
||||
<button type="button" class="btn btn-sm btn-primary" onclick="openProfileModal()">
|
||||
<i class="fas fa-camera me-1"></i> Update Photo
|
||||
</button>
|
||||
<button type="button" class="btn btn-sm btn-secondary" onclick="openPasswordModal()">
|
||||
<i class="fas fa-key me-1"></i> Change Password
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Personal Information Section -->
|
||||
<div class="col-md-6 mb-4">
|
||||
<div class="card h-100 border-0 shadow-sm">
|
||||
<div class="card-header bg-light py-2">
|
||||
<h5 class="card-title mb-0"><i class="fas fa-user-circle me-2"></i>Personal Information</h5>
|
||||
</div>
|
||||
<div class="card-body">
|
||||
<div class="row g-3">
|
||||
<div class="col-md-6">
|
||||
<small class="text-muted d-block">Full Name</small>
|
||||
<p class="mb-0 fw-medium">{{ officer.name }}</p>
|
||||
</div>
|
||||
<div class="col-md-6">
|
||||
<small class="text-muted d-block">Gender</small>
|
||||
<p class="mb-0 fw-medium">{{ officer.gender|default:"Not provided" }}</p>
|
||||
</div>
|
||||
<div class="col-md-6">
|
||||
<small class="text-muted d-block">Birth Date</small>
|
||||
<p class="mb-0 fw-medium">{{ officer.birth_date|date:"M d, Y"|default:"Not provided" }}</p>
|
||||
</div>
|
||||
<div class="col-md-6">
|
||||
<small class="text-muted d-block">Marital Status</small>
|
||||
<p class="mb-0 fw-medium">{{ officer.marital_status|default:"Not provided" }}</p>
|
||||
</div>
|
||||
<div class="col-md-6">
|
||||
<small class="text-muted d-block">Citizenship No.</small>
|
||||
<p class="mb-0 fw-medium">{{ officer.citizenship_no|default:"Not provided" }}</p>
|
||||
</div>
|
||||
<div class="col-md-6">
|
||||
<small class="text-muted d-block">Passport</small>
|
||||
<p class="mb-0 fw-medium">{{ officer.passport|default:"Not provided" }}</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Contact Information -->
|
||||
<div class="col-md-6 mb-4">
|
||||
<div class="card h-100 border-0 shadow-sm">
|
||||
<div class="card-header bg-light py-2">
|
||||
<h5 class="card-title mb-0"><i class="fas fa-address-book me-2"></i>Contact Information</h5>
|
||||
</div>
|
||||
<div class="card-body">
|
||||
<div class="row g-3">
|
||||
<div class="col-md-6">
|
||||
<small class="text-muted d-block">Email Address</small>
|
||||
<p class="mb-0 fw-medium">{{ officer.email|default:"Not provided" }}</p>
|
||||
</div>
|
||||
<div class="col-md-6">
|
||||
<small class="text-muted d-block">Phone Number</small>
|
||||
<p class="mb-0 fw-medium">{{ officer.phone|default:"Not provided" }}</p>
|
||||
</div>
|
||||
<div class="col-md-6">
|
||||
<small class="text-muted d-block">Parent Name</small>
|
||||
<p class="mb-0 fw-medium">{{ officer.parent_name|default:"Not provided" }}</p>
|
||||
</div>
|
||||
<div class="col-md-6">
|
||||
<small class="text-muted d-block">Parent Phone</small>
|
||||
<p class="mb-0 fw-medium">{{ officer.parent_phone|default:"Not provided" }}</p>
|
||||
</div>
|
||||
<div class="col-12">
|
||||
<small class="text-muted d-block">Temporary Address</small>
|
||||
<p class="mb-0 fw-medium">{{ officer.temporary_address|default:"Not provided" }}</p>
|
||||
</div>
|
||||
<div class="col-12">
|
||||
<small class="text-muted d-block">Permanent Address</small>
|
||||
<p class="mb-0 fw-medium">{{ officer.permanent_address|default:"Not provided" }}</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Employment Information -->
|
||||
<div class="col-md-6 mb-4">
|
||||
<div class="card h-100 border-0 shadow-sm">
|
||||
<div class="card-header bg-light py-2">
|
||||
<h5 class="card-title mb-0"><i class="fas fa-briefcase me-2"></i>Employment Information</h5>
|
||||
</div>
|
||||
<div class="card-body">
|
||||
<div class="row g-3">
|
||||
<div class="col-md-6">
|
||||
<small class="text-muted d-block">Employee ID</small>
|
||||
<p class="mb-0 fw-medium">{{ officer.id }}</p>
|
||||
</div>
|
||||
<div class="col-md-6">
|
||||
<small class="text-muted d-block">Designation</small>
|
||||
<p class="mb-0 fw-medium">{{ officer.designation|default:"Not provided" }}</p>
|
||||
</div>
|
||||
<div class="col-md-6">
|
||||
<small class="text-muted d-block">Joining Date</small>
|
||||
<p class="mb-0 fw-medium">{{ officer.joining_date|date:"M d, Y"|default:"Not provided" }}</p>
|
||||
</div>
|
||||
<div class="col-md-6">
|
||||
<small class="text-muted d-block">Status</small>
|
||||
<p class="mb-0 fw-medium">
|
||||
<span class="badge {% if officer.is_active %}bg-success{% else %}bg-danger{% endif %}">
|
||||
{{ officer.is_active|yesno:"Active,Inactive" }}
|
||||
</span>
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Leave Section -->
|
||||
<div id="leaveSection" class="content-section">
|
||||
<div class="content-card">
|
||||
<h3 class="text-xl font-semibold mb-4">
|
||||
<i class="fas fa-calendar-minus mr-2"></i> Leave Management
|
||||
</h3>
|
||||
<div class="row mb-4">
|
||||
<div class="col-12">
|
||||
<button type="button" class="btn btn-primary" onclick="openLeaveModal()">
|
||||
<i class="fas fa-plus-circle me-2"></i>Request Leave
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="row mb-4">
|
||||
<div class="col-md-4">
|
||||
<div class="card border-0 shadow-sm h-100">
|
||||
<div class="card-body text-center p-4">
|
||||
<i class="fas fa-clock text-warning mb-3" style="font-size: 2rem;"></i>
|
||||
<h3 class="text-warning mb-2">{{ pending_leaves }}</h3>
|
||||
<p class="mb-0">Pending Requests</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-md-4">
|
||||
<div class="card border-0 shadow-sm h-100">
|
||||
<div class="card-body text-center p-4">
|
||||
<i class="fas fa-check-circle text-success mb-3" style="font-size: 2rem;"></i>
|
||||
<h3 class="text-success mb-2">{{ approved_leaves }}</h3>
|
||||
<p class="mb-0">Approved Requests</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-md-4">
|
||||
<div class="card border-0 shadow-sm h-100">
|
||||
<div class="card-body text-center p-4">
|
||||
<i class="fas fa-times-circle text-danger mb-3" style="font-size: 2rem;"></i>
|
||||
<h3 class="text-danger mb-2">{{ rejected_leaves }}</h3>
|
||||
<p class="mb-0">Rejected Requests</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="content-card">
|
||||
<h5 class="mb-3"><i class="fas fa-history me-2"></i> Leave History</h5>
|
||||
<div class="table-responsive">
|
||||
<table class="table table-responsive table-hover">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>Start Date</th>
|
||||
<th>End Date</th>
|
||||
<th>Reason</th>
|
||||
<th>Status</th>
|
||||
<th>Applied On</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{% if recent_leaves %}
|
||||
{% for leave in recent_leaves %}
|
||||
<tr>
|
||||
<td>{{ leave.start_date|date:"M d, Y" }}</td>
|
||||
<td>{{ leave.end_date|date:"M d, Y" }}</td>
|
||||
<td>{{ leave.message }}</td>
|
||||
<td>
|
||||
<span class="badge
|
||||
{% if leave.status == 0 %}leave-pending
|
||||
{% elif leave.status == 1 %}leave-approved
|
||||
{% else %}leave-rejected{% endif %}">
|
||||
{{ leave.get_status_display }}
|
||||
</span>
|
||||
</td>
|
||||
<td>{{ leave.created_at|date:"M d, Y" }}</td>
|
||||
</tr>
|
||||
{% endfor %}
|
||||
{% else %}
|
||||
<tr>
|
||||
<td colspan="5" class="text-center">No leave history available</td>
|
||||
</tr>
|
||||
{% endif %}
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Notice Section -->
|
||||
<div id="noticeSection" class="content-section">
|
||||
<div class="content-card">
|
||||
<div class="d-flex justify-content-between align-items-center mb-4">
|
||||
<h5 class="mb-0"><i class="fas fa-bullhorn me-2"></i> Notices</h5>
|
||||
</div>
|
||||
|
||||
<!-- Notice List -->
|
||||
<div class="table-responsive">
|
||||
<table class="table table-hover table-responsive">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>Title</th>
|
||||
<th>Message</th>
|
||||
<th>Created At</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{% for notice in notices %}
|
||||
<tr>
|
||||
<td>{{ notice.title }}</td>
|
||||
<td>{{ notice.message|truncatechars:100 }}</td>
|
||||
<td>{{ notice.created_at|date:"M d, Y H:i" }}</td>
|
||||
</tr>
|
||||
{% empty %}
|
||||
<tr>
|
||||
<td colspan="3" class="text-center">No notices found</td>
|
||||
</tr>
|
||||
{% endfor %}
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Common Modals -->
|
||||
{% include 'shared/common_modals.html' with user=officer %}
|
||||
|
||||
<!-- Admission Officer-specific Modals -->
|
||||
{% include 'admission_officer/modals.html' %}
|
||||
|
||||
<!-- Bottom Navigation -->
|
||||
{% include 'shared/bottom_nav.html' with user=officer %}
|
||||
|
||||
{% endblock content %}
|
||||
|
||||
{% block extrastyle %}
|
||||
{{ block.super }}
|
||||
<!-- Bootstrap CSS -->
|
||||
<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.0/dist/css/bootstrap.min.css" rel="stylesheet">
|
||||
<!-- Font Awesome -->
|
||||
<link href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.0.0/css/all.min.css" rel="stylesheet" />
|
||||
|
||||
<style>
|
||||
/* Progress Bar Styles */
|
||||
.progress {
|
||||
height: 8px;
|
||||
background-color: #eef2f7;
|
||||
border-radius: 10px;
|
||||
overflow: hidden;
|
||||
margin-bottom: 4px;
|
||||
width: 120px;
|
||||
}
|
||||
|
||||
.progress-bar {
|
||||
border-radius: 10px;
|
||||
transition: width 0.6s ease;
|
||||
background-color: #6366f1;
|
||||
}
|
||||
|
||||
.progress-bar.bg-success {
|
||||
background-color: #22c55e !important;
|
||||
}
|
||||
|
||||
.progress-bar.bg-warning {
|
||||
background-color: #f59e0b !important;
|
||||
}
|
||||
|
||||
.progress-bar.bg-danger {
|
||||
background-color: #ef4444 !important;
|
||||
}
|
||||
|
||||
.progress-text {
|
||||
font-size: 12px;
|
||||
color: #6b7280;
|
||||
margin-top: 2px;
|
||||
}
|
||||
|
||||
.status-badge {
|
||||
padding: 4px 8px;
|
||||
border-radius: 4px;
|
||||
font-size: 12px;
|
||||
font-weight: 500;
|
||||
}
|
||||
|
||||
.status-badge.bg-warning {
|
||||
background-color: #fef3c7 !important;
|
||||
color: #d97706;
|
||||
}
|
||||
|
||||
.status-badge.bg-success {
|
||||
background-color: #dcfce7 !important;
|
||||
color: #15803d;
|
||||
}
|
||||
|
||||
.btn-group-custom {
|
||||
display: inline-flex;
|
||||
gap: 0.25rem;
|
||||
}
|
||||
</style>
|
||||
{% endblock %}
|
||||
|
||||
{% block extrascripts %}
|
||||
{{ block.super }}
|
||||
<script src="{% static 'js/admission_officer.js' %}"></script>
|
||||
{% endblock %}
|
||||
File diff suppressed because it is too large.
Load diff
File diff suppressed because it is too large.
Load diff
@@ -1,73 +0,0 @@
|
||||
{% load static %}
|
||||
|
||||
<!-- Add Meeting Modal -->
|
||||
<div id="addMeetingModal" style="display: none; position: fixed; top: 0; left: 0; right: 0; bottom: 0; background-color: rgba(0,0,0,0.5); z-index: 10000;" onclick="if(event.target === this) closeAddMeetingModal();">
|
||||
<div style="position: absolute; top: 50%; left: 50%; transform: translate(-50%, -50%); width: 90%; max-width: 600px; background-color: white; border-radius: 8px; box-shadow: 0 2px 10px rgba(0,0,0,0.2); max-height: 90vh; overflow-y: auto;">
|
||||
<div style="background-color: #3F51B5; color: white; padding: 12px; border-top-left-radius: 8px; border-top-right-radius: 8px; display: flex; justify-content: space-between; align-items: center; position: sticky; top: 0; z-index: 1;">
|
||||
<h5 style="margin: 0; font-size: 18px;">Schedule New Meeting</h5>
|
||||
<button style="background: none; border: none; color: white; font-size: 24px; cursor: pointer; line-height: 24px;" onclick="closeAddMeetingModal()">×</button>
|
||||
</div>
|
||||
<div style="padding: 15px;">
|
||||
<form id="addMeetingForm">
|
||||
<div style="display: grid; grid-template-columns: 1fr 1fr; gap: 15px;">
|
||||
<!-- Left Column -->
|
||||
<div>
|
||||
<div style="margin-bottom: 12px;">
|
||||
<label style="display: block; margin-bottom: 4px; font-weight: 500;" for="meetingDate">Date</label>
|
||||
<input type="date" id="meetingDate" name="meeting_date" required style="width: 100%; padding: 8px; border: 1px solid #ddd; border-radius: 4px;">
|
||||
</div>
|
||||
<div style="margin-bottom: 12px;">
|
||||
<label style="display: block; margin-bottom: 4px; font-weight: 500;" for="meetingTime">Time</label>
|
||||
<input type="time" id="meetingTime" name="meeting_time" required style="width: 100%; padding: 8px; border: 1px solid #ddd; border-radius: 4px;">
|
||||
</div>
|
||||
<div style="margin-bottom: 12px;">
|
||||
<label style="display: block; margin-bottom: 4px; font-weight: 500;" for="duration">Duration (minutes)</label>
|
||||
<input type="number" id="duration" name="duration" min="15" max="120" value="30" required style="width: 100%; padding: 8px; border: 1px solid #ddd; border-radius: 4px;">
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Right Column -->
|
||||
<div>
|
||||
<div style="margin-bottom: 12px;">
|
||||
<div style="display: flex; align-items: center;">
|
||||
<input type="checkbox" id="isOnline" name="is_online" style="margin-right: 8px;">
|
||||
<label for="isOnline" style="font-weight: 500;">Online Meeting</label>
|
||||
</div>
|
||||
</div>
|
||||
<div id="meetingLinkContainer" style="margin-bottom: 12px; display: none;">
|
||||
<label style="display: block; margin-bottom: 4px; font-weight: 500;" for="meetingLink">Meeting Link</label>
|
||||
<input type="url" id="meetingLink" name="meeting_link" value="" style="width: 100%; padding: 8px; border: 1px solid #ddd; border-radius: 4px;">
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Full Width Sections -->
|
||||
<div style="margin-top: 15px;">
|
||||
<div style="margin-bottom: 12px;">
|
||||
<label style="display: block; margin-bottom: 4px; font-weight: 500;" for="agenda">Agenda</label>
|
||||
<textarea id="agenda" name="agenda" rows="3" style="width: 100%; padding: 8px; border: 1px solid #ddd; border-radius: 4px; min-height: 60px;"></textarea>
|
||||
</div>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
|
||||
<div style="padding: 12px; border-top: 1px solid #EEEEEE; text-align: right; position: sticky; bottom: 0; background-color: white; border-bottom-left-radius: 8px; border-bottom-right-radius: 8px;">
|
||||
<button type="button" style="background-color: #9E9E9E; color: white; border: none; padding: 6px 12px; border-radius: 4px; cursor: pointer; font-size: 14px; margin-right: 8px;" onclick="closeAddMeetingModal()">Cancel</button>
|
||||
<button type="button" style="background-color: #3F51B5; color: white; border: none; padding: 6px 12px; border-radius: 4px; cursor: pointer; font-size: 14px;" onclick="submitAddMeetingForm()">Schedule Meeting</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<script>
|
||||
// Toggle meeting link field based on online/in-person selection
|
||||
document.addEventListener('DOMContentLoaded', function() {
|
||||
if (document.getElementById('isOnline')) {
|
||||
document.getElementById('isOnline').addEventListener('change', function() {
|
||||
const meetingLinkContainer = document.getElementById('meetingLinkContainer');
|
||||
const meetingLinkInput = document.getElementById('meetingLink');
|
||||
meetingLinkContainer.style.display = this.checked ? 'block' : 'none';
|
||||
meetingLinkInput.required = this.checked;
|
||||
});
|
||||
}
|
||||
});
|
||||
</script>
|
||||
@@ -1,367 +0,0 @@
|
||||
{% load static %}
|
||||
|
||||
<!-- Add Routine Modal -->
|
||||
<div id="addRoutineModal" style="display: none; position: fixed; top: 0; left: 0; right: 0; bottom: 0; background-color: rgba(0,0,0,0.5); z-index: 10000;" onclick="if(event.target === this) closeAddRoutineModal();">
|
||||
<div style="position: absolute; top: 50%; left: 50%; transform: translate(-50%, -50%); width: 90%; max-width: 600px; background: white; border-radius: 8px; box-shadow: 0 2px 4px rgba(0,0,0,0.1);">
|
||||
<div style="background-color: #6366f1; color: white; padding: 1rem 1.5rem; border-top-left-radius: 8px; border-top-right-radius: 8px; display: flex; justify-content: space-between; align-items: center;">
|
||||
<h5 style="margin: 0; font-size: 1.125rem; font-weight: 500;">Add Class Routine</h5>
|
||||
<button style="background: none; border: none; color: white; font-size: 1.5rem; cursor: pointer; line-height: 1; padding: 0.25rem;" onclick="closeAddRoutineModal()">×</button>
|
||||
</div>
|
||||
<form id="addRoutineForm">
|
||||
{% csrf_token %}
|
||||
<div style="padding: 1.5rem;">
|
||||
<div style="margin-bottom: 1.5rem;">
|
||||
<label for="periodSelect" style="display: block; margin-bottom: 0.5rem; font-weight: 500; color: #374151;">{% if course.duration_type == 'Semester' %}Semester{% else %}Year{% endif %}</label>
|
||||
<select id="periodSelect" name="period_select" style="width: 100%; padding: 0.625rem; border: 1px solid #d1d5db; border-radius: 6px; background-color: white; font-size: 0.875rem;" required>
|
||||
<option value="">Select {% if course.duration_type == 'Semester' %}Semester{% else %}Year{% endif %}</option>
|
||||
<!-- Options will be populated dynamically via JavaScript -->
|
||||
</select>
|
||||
</div>
|
||||
|
||||
<!-- Subject list container - subjects will be added here dynamically -->
|
||||
<div id="subjectsContainer" style="margin-bottom: 1.5rem; max-height: 400px; overflow-y: auto;"></div>
|
||||
|
||||
<input type="hidden" id="periodOrYear" name="period_or_year">
|
||||
</div>
|
||||
<div style="padding: 1rem 1.5rem; border-top: 1px solid #e5e7eb; background-color: #f9fafb; border-bottom-left-radius: 8px; border-bottom-right-radius: 8px; display: flex; justify-content: flex-end; gap: 0.75rem;">
|
||||
<button type="button" style="padding: 0.5rem 1rem; border: 1px solid #d1d5db; background-color: white; color: #374151; border-radius: 6px; font-size: 0.875rem; font-weight: 500;" onclick="closeAddRoutineModal()">Cancel</button>
|
||||
<button type="button" style="padding: 0.5rem 1rem; background-color: #6366f1; color: white; border: none; border-radius: 6px; font-size: 0.875rem; font-weight: 500;" onclick="submitAddRoutineForm()">Save</button>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<script>
|
||||
// Store all subject data for use in the form
|
||||
const allSubjects = [
|
||||
{% for subject in subjects %}
|
||||
{
|
||||
id: {{ subject.id }},
|
||||
name: "{{ subject.name }}",
|
||||
period: "{{ subject.period_or_year }}"
|
||||
},
|
||||
{% endfor %}
|
||||
];
|
||||
|
||||
function openAddRoutineModal() {
|
||||
const modal = document.getElementById('addRoutineModal');
|
||||
if (modal) {
|
||||
// Reset form
|
||||
document.getElementById('addRoutineForm').reset();
|
||||
document.getElementById('subjectsContainer').innerHTML = '';
|
||||
|
||||
// Populate period dropdown dynamically
|
||||
const periodSelect = document.getElementById('periodSelect');
|
||||
const periodLabel = {% if course.duration_type == 'Semester' %}'Semester'{% else %}'Year'{% endif %};
|
||||
|
||||
// Try to get the course data from the page
|
||||
const courseElement = document.querySelector('#routineSection');
|
||||
const courseId = courseElement?.getAttribute('data-course-id') ||
|
||||
window.location.pathname.match(/\/hod\/(\d+)/)?.at(1);
|
||||
|
||||
// Default to 3 periods if we can't determine
|
||||
let totalPeriods = 3;
|
||||
|
||||
{% if course.total_periods %}
|
||||
totalPeriods = {{ course.total_periods }};
|
||||
{% endif %}
|
||||
|
||||
// Clear all options
|
||||
periodSelect.innerHTML = '';
|
||||
|
||||
// Add placeholder option
|
||||
periodSelect.innerHTML = `<option value="">Select ${periodLabel}</option>`;
|
||||
|
||||
// Add options based on total periods
|
||||
for (let i = 1; i <= totalPeriods; i++) {
|
||||
periodSelect.innerHTML += `<option value="${i}">${periodLabel} ${i}</option>`;
|
||||
}
|
||||
|
||||
// Setup period selection change listener
|
||||
periodSelect.onchange = function() {
|
||||
const selectedPeriod = this.value;
|
||||
document.getElementById('periodOrYear').value = selectedPeriod;
|
||||
|
||||
// Clear existing subjects
|
||||
const subjectsContainer = document.getElementById('subjectsContainer');
|
||||
subjectsContainer.innerHTML = '';
|
||||
|
||||
if (selectedPeriod) {
|
||||
// Filter subjects for the selected period
|
||||
const periodSubjects = allSubjects.filter(subject => subject.period === selectedPeriod);
|
||||
|
||||
if (periodSubjects.length === 0) {
|
||||
subjectsContainer.innerHTML = '<p style="text-align: center; padding: 1rem; color: #6b7280;">No subjects found for this period.</p>';
|
||||
return;
|
||||
}
|
||||
|
||||
// Create a form section for each subject
|
||||
periodSubjects.forEach(subject => {
|
||||
const subjectDiv = document.createElement('div');
|
||||
subjectDiv.className = 'subject-item';
|
||||
subjectDiv.style.padding = '1rem';
|
||||
subjectDiv.style.marginBottom = '1rem';
|
||||
subjectDiv.style.border = '1px solid #e5e7eb';
|
||||
subjectDiv.style.borderRadius = '6px';
|
||||
subjectDiv.style.backgroundColor = 'white';
|
||||
|
||||
// Create subject header
|
||||
const subjectHeader = document.createElement('h4');
|
||||
subjectHeader.textContent = subject.name;
|
||||
subjectHeader.style.margin = '0 0 1rem 0';
|
||||
subjectHeader.style.fontSize = '0.875rem';
|
||||
subjectHeader.style.fontWeight = '600';
|
||||
subjectHeader.style.color = '#374151';
|
||||
subjectDiv.appendChild(subjectHeader);
|
||||
|
||||
// Add hidden input for subject ID
|
||||
const subjectIdInput = document.createElement('input');
|
||||
subjectIdInput.type = 'hidden';
|
||||
subjectIdInput.name = `subject_id_${subject.id}`;
|
||||
subjectIdInput.value = subject.id;
|
||||
subjectDiv.appendChild(subjectIdInput);
|
||||
|
||||
// Create form grid
|
||||
const formGrid = document.createElement('div');
|
||||
formGrid.style.display = 'grid';
|
||||
formGrid.style.gridTemplateColumns = 'repeat(auto-fit, minmax(150px, 1fr))';
|
||||
formGrid.style.gap = '1rem';
|
||||
formGrid.style.alignItems = 'end';
|
||||
|
||||
// Create teacher dropdown
|
||||
const teacherDiv = document.createElement('div');
|
||||
|
||||
const teacherLabel = document.createElement('label');
|
||||
teacherLabel.textContent = 'Teacher';
|
||||
teacherLabel.style.display = 'block';
|
||||
teacherLabel.style.marginBottom = '0.5rem';
|
||||
teacherLabel.style.fontSize = '0.875rem';
|
||||
teacherLabel.style.fontWeight = '500';
|
||||
teacherLabel.style.color = '#374151';
|
||||
teacherDiv.appendChild(teacherLabel);
|
||||
|
||||
const teacherSelect = document.createElement('select');
|
||||
teacherSelect.name = `teacher_id_${subject.id}`;
|
||||
teacherSelect.style.width = '100%';
|
||||
teacherSelect.style.padding = '0.625rem';
|
||||
teacherSelect.style.border = '1px solid #d1d5db';
|
||||
teacherSelect.style.borderRadius = '6px';
|
||||
teacherSelect.style.backgroundColor = 'white';
|
||||
teacherSelect.style.fontSize = '0.875rem';
|
||||
teacherSelect.required = true;
|
||||
|
||||
// Add default option
|
||||
const defaultOption = document.createElement('option');
|
||||
defaultOption.value = '';
|
||||
defaultOption.textContent = 'Select Teacher';
|
||||
teacherSelect.appendChild(defaultOption);
|
||||
|
||||
// Add teacher options
|
||||
{% for teacher in staff %}
|
||||
const teacherOption_{{ teacher.id }} = document.createElement('option');
|
||||
teacherOption_{{ teacher.id }}.value = '{{ teacher.id }}';
|
||||
teacherOption_{{ teacher.id }}.textContent = '{{ teacher.name }}';
|
||||
teacherSelect.appendChild(teacherOption_{{ teacher.id }});
|
||||
{% endfor %}
|
||||
|
||||
teacherDiv.appendChild(teacherSelect);
|
||||
formGrid.appendChild(teacherDiv);
|
||||
|
||||
// Start time
|
||||
const startTimeDiv = document.createElement('div');
|
||||
|
||||
const startTimeLabel = document.createElement('label');
|
||||
startTimeLabel.textContent = 'Start Time';
|
||||
startTimeLabel.style.display = 'block';
|
||||
startTimeLabel.style.marginBottom = '0.5rem';
|
||||
startTimeLabel.style.fontSize = '0.875rem';
|
||||
startTimeLabel.style.fontWeight = '500';
|
||||
startTimeLabel.style.color = '#374151';
|
||||
startTimeDiv.appendChild(startTimeLabel);
|
||||
|
||||
const startTimeInput = document.createElement('input');
|
||||
startTimeInput.type = 'time';
|
||||
startTimeInput.name = `start_time_${subject.id}`;
|
||||
startTimeInput.style.width = '100%';
|
||||
startTimeInput.style.padding = '0.625rem';
|
||||
startTimeInput.style.border = '1px solid #d1d5db';
|
||||
startTimeInput.style.borderRadius = '6px';
|
||||
startTimeInput.style.backgroundColor = 'white';
|
||||
startTimeInput.style.fontSize = '0.875rem';
|
||||
startTimeInput.required = true;
|
||||
startTimeDiv.appendChild(startTimeInput);
|
||||
|
||||
formGrid.appendChild(startTimeDiv);
|
||||
|
||||
// End time
|
||||
const endTimeDiv = document.createElement('div');
|
||||
|
||||
const endTimeLabel = document.createElement('label');
|
||||
endTimeLabel.textContent = 'End Time';
|
||||
endTimeLabel.style.display = 'block';
|
||||
endTimeLabel.style.marginBottom = '0.5rem';
|
||||
endTimeLabel.style.fontSize = '0.875rem';
|
||||
endTimeLabel.style.fontWeight = '500';
|
||||
endTimeLabel.style.color = '#374151';
|
||||
endTimeDiv.appendChild(endTimeLabel);
|
||||
|
||||
const endTimeInput = document.createElement('input');
|
||||
endTimeInput.type = 'time';
|
||||
endTimeInput.name = `end_time_${subject.id}`;
|
||||
endTimeInput.style.width = '100%';
|
||||
endTimeInput.style.padding = '0.625rem';
|
||||
endTimeInput.style.border = '1px solid #d1d5db';
|
||||
endTimeInput.style.borderRadius = '6px';
|
||||
endTimeInput.style.backgroundColor = 'white';
|
||||
endTimeInput.style.fontSize = '0.875rem';
|
||||
endTimeInput.required = true;
|
||||
endTimeDiv.appendChild(endTimeInput);
|
||||
|
||||
formGrid.appendChild(endTimeDiv);
|
||||
|
||||
// Add the form grid to the subject div
|
||||
subjectDiv.appendChild(formGrid);
|
||||
|
||||
// Add the subject div to the container
|
||||
subjectsContainer.appendChild(subjectDiv);
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
// Show modal
|
||||
modal.style.display = 'block';
|
||||
document.body.style.overflow = 'hidden';
|
||||
}
|
||||
}
|
||||
|
||||
function closeAddRoutineModal() {
|
||||
const modal = document.getElementById('addRoutineModal');
|
||||
if (modal) {
|
||||
modal.style.display = 'none';
|
||||
document.body.style.overflow = '';
|
||||
}
|
||||
}
|
||||
|
||||
// Make functions available globally
|
||||
if (typeof window !== 'undefined') {
|
||||
window.openAddRoutineModal = openAddRoutineModal;
|
||||
window.closeAddRoutineModal = closeAddRoutineModal;
|
||||
}
|
||||
|
||||
// Add form submission function
|
||||
function submitAddRoutineForm() {
|
||||
const form = document.getElementById('addRoutineForm');
|
||||
if (!form) {
|
||||
console.error('Add routine form not found');
|
||||
return;
|
||||
}
|
||||
|
||||
// Get selected period
|
||||
const periodSelect = document.getElementById('periodSelect');
|
||||
const selectedPeriod = periodSelect.value;
|
||||
|
||||
if (!selectedPeriod) {
|
||||
alert('Please select a period');
|
||||
return;
|
||||
}
|
||||
|
||||
// Get selected subjects
|
||||
const subjectItems = document.querySelectorAll('.subject-item');
|
||||
if (subjectItems.length === 0) {
|
||||
alert('No subjects available for this period');
|
||||
return;
|
||||
}
|
||||
|
||||
// Check if any item has incomplete data
|
||||
let hasIncompleteData = false;
|
||||
subjectItems.forEach(item => {
|
||||
const teacherSelect = item.querySelector('select');
|
||||
const startTime = item.querySelector('input[type="time"][name^="start_time_"]');
|
||||
const endTime = item.querySelector('input[type="time"][name^="end_time_"]');
|
||||
|
||||
if (!teacherSelect.value || !startTime.value || !endTime.value) {
|
||||
hasIncompleteData = true;
|
||||
}
|
||||
});
|
||||
|
||||
if (hasIncompleteData) {
|
||||
alert('Please fill in all required fields for each subject');
|
||||
return;
|
||||
}
|
||||
|
||||
// Prepare requests to be sent
|
||||
const requests = [];
|
||||
|
||||
// Common data
|
||||
const courseId = document.querySelector('#routineSection')?.getAttribute('data-course-id') ||
|
||||
window.location.pathname.match(/\/hod\/(\d+)/)?.at(1);
|
||||
|
||||
if (!courseId) {
|
||||
alert('Course ID not found');
|
||||
return;
|
||||
}
|
||||
|
||||
// Create a request for each subject
|
||||
subjectItems.forEach(item => {
|
||||
const subjectId = item.querySelector('input[type="hidden"]').value;
|
||||
const teacherId = item.querySelector('select').value;
|
||||
const startTime = item.querySelector('input[name^="start_time_"]').value;
|
||||
const endTime = item.querySelector('input[name^="end_time_"]').value;
|
||||
|
||||
const formData = new FormData();
|
||||
formData.append('course_id', courseId);
|
||||
formData.append('subject_id', subjectId);
|
||||
formData.append('teacher_id', teacherId);
|
||||
formData.append('start_time', startTime);
|
||||
formData.append('end_time', endTime);
|
||||
formData.append('period_or_year', selectedPeriod);
|
||||
|
||||
requests.push(
|
||||
fetch('/app/api/hod/routines/', {
|
||||
method: 'POST',
|
||||
body: formData,
|
||||
headers: {
|
||||
'X-CSRFToken': document.querySelector('[name=csrfmiddlewaretoken]').value
|
||||
}
|
||||
})
|
||||
);
|
||||
});
|
||||
|
||||
// Send all requests and handle responses
|
||||
Promise.all(requests.map(p => p.catch(e => e)))
|
||||
.then(responses => {
|
||||
const allSuccessful = responses.every(res => !(res instanceof Error) && res.ok);
|
||||
|
||||
if (allSuccessful) {
|
||||
if (typeof window.showMessageModal === 'function') {
|
||||
window.showMessageModal('Success', 'All routines added successfully');
|
||||
} else if (typeof window.showToast === 'function') {
|
||||
window.showToast('success', 'All routines added successfully');
|
||||
} else {
|
||||
alert('All routines added successfully');
|
||||
}
|
||||
closeAddRoutineModal();
|
||||
setTimeout(() => {
|
||||
window.location.reload();
|
||||
}, 1000);
|
||||
} else {
|
||||
if (typeof window.showMessageModal === 'function') {
|
||||
window.showMessageModal('Error', 'Some routines could not be added');
|
||||
} else if (typeof window.showToast === 'function') {
|
||||
window.showToast('error', 'Some routines could not be added');
|
||||
} else {
|
||||
alert('Some routines could not be added');
|
||||
}
|
||||
}
|
||||
})
|
||||
.catch(error => {
|
||||
console.error('Error adding routines:', error);
|
||||
alert('Failed to add routines: ' + error.message);
|
||||
});
|
||||
}
|
||||
|
||||
// Make submission function globally available
|
||||
if (typeof window !== 'undefined') {
|
||||
window.submitAddRoutineForm = submitAddRoutineForm;
|
||||
}
|
||||
</script>
|
||||
@@ -1,126 +0,0 @@
|
||||
{% load static %}
|
||||
|
||||
<div id="addStaffModal" style="display: none; position: fixed; top: 0; left: 0; right: 0; bottom: 0; background-color: rgba(0,0,0,0.5); z-index: 10000;" onclick="if(event.target === this) closeAddStaffModal();">
|
||||
<div style="position: absolute; top: 50%; left: 50%; transform: translate(-50%, -50%); width: 90%; max-width: 800px; background-color: white; border-radius: 8px; box-shadow: 0 2px 10px rgba(0,0,0,0.2); max-height: 90vh; overflow-y: auto;">
|
||||
<div style="background-color: #3F51B5; color: white; padding: 12px; border-top-left-radius: 8px; border-top-right-radius: 8px; display: flex; justify-content: space-between; align-items: center; position: sticky; top: 0; z-index: 1;">
|
||||
<h5 style="margin: 0; font-size: 18px;">Add New Staff</h5>
|
||||
<button style="background: none; border: none; color: white; font-size: 24px; cursor: pointer; line-height: 24px;" onclick="closeAddStaffModal()">×</button>
|
||||
</div>
|
||||
<div style="padding: 15px;">
|
||||
<form id="addStaffForm">
|
||||
{% csrf_token %}
|
||||
<div style="display: grid; grid-template-columns: 1fr 1fr; gap: 15px;">
|
||||
<!-- Left Column -->
|
||||
<div>
|
||||
<div style="margin-bottom: 12px;">
|
||||
<label style="display: block; margin-bottom: 4px; font-weight: 500;">Name <span style="color: red;">*</span></label>
|
||||
<input type="text" id="addStaffName" name="name" style="width: 100%; padding: 6px; border: 1px solid #ddd; border-radius: 4px;" required>
|
||||
</div>
|
||||
<div style="margin-bottom: 12px;">
|
||||
<label style="display: block; margin-bottom: 4px; font-weight: 500;">Phone <span style="color: red;">*</span></label>
|
||||
<input type="tel" id="addStaffPhone" name="phone" style="width: 100%; padding: 6px; border: 1px solid #ddd; border-radius: 4px;" required pattern="[0-9]{10}" maxlength="10" title="Please enter a 10-digit phone number">
|
||||
<small style="color: #666; display: block; margin-top: 2px; font-size: 11px;">This will be used as the login ID</small>
|
||||
</div>
|
||||
<div style="margin-bottom: 12px;">
|
||||
<label style="display: block; margin-bottom: 4px; font-weight: 500;">Email</label>
|
||||
<input type="email" id="addStaffEmail" name="email" style="width: 100%; padding: 6px; border: 1px solid #ddd; border-radius: 4px;">
|
||||
</div>
|
||||
<div style="margin-bottom: 12px;">
|
||||
<label style="display: block; margin-bottom: 4px; font-weight: 500;">Gender</label>
|
||||
<select id="addStaffGender" name="gender" style="width: 100%; padding: 6px; border: 1px solid #ddd; border-radius: 4px;">
|
||||
<option value="">Select Gender</option>
|
||||
<option value="Male">Male</option>
|
||||
<option value="Female">Female</option>
|
||||
<option value="Other">Other</option>
|
||||
</select>
|
||||
</div>
|
||||
<div style="margin-bottom: 12px;">
|
||||
<label style="display: block; margin-bottom: 4px; font-weight: 500;">Birth Date</label>
|
||||
<input type="date" id="addStaffBirthDate" name="birth_date" style="width: 100%; padding: 6px; border: 1px solid #ddd; border-radius: 4px;">
|
||||
</div>
|
||||
</div>
|
||||
<!-- Right Column -->
|
||||
<div>
|
||||
<div style="margin-bottom: 12px;">
|
||||
<label style="display: block; margin-bottom: 4px; font-weight: 500;">Assigned Subjects <span style="color: red;">*</span></label>
|
||||
<select id="addStaffSubjects" name="subjects[]" multiple style="width: 100%; min-height: 100px; padding: 6px; border: 1px solid #ddd; border-radius: 4px;" required>
|
||||
{% for subject in subjects %}
|
||||
<option value="{{ subject.id }}">
|
||||
{{ subject.name }} {% if course.duration_type == 'Semester' %}(Semester {{ subject.period_or_year }}){% else %}(Year {{ subject.period_or_year }}){% endif %}
|
||||
</option>
|
||||
{% empty %}
|
||||
<option value="" disabled>No subjects available</option>
|
||||
{% endfor %}
|
||||
</select>
|
||||
<small style="color: #666; display: block; margin-top: 2px; font-size: 11px;">Hold Ctrl/Cmd to select multiple subjects</small>
|
||||
</div>
|
||||
<div style="margin-bottom: 12px;">
|
||||
<label style="display: block; margin-bottom: 4px; font-weight: 500;">Temporary Address</label>
|
||||
<input type="text" id="addStaffTempAddress" name="temporary_address" style="width: 100%; padding: 6px; border: 1px solid #ddd; border-radius: 4px;">
|
||||
</div>
|
||||
<div style="margin-bottom: 12px;">
|
||||
<label style="display: block; margin-bottom: 4px; font-weight: 500;">Permanent Address</label>
|
||||
<input type="text" id="addStaffPermAddress" name="permanent_address" style="width: 100%; padding: 6px; border: 1px solid #ddd; border-radius: 4px;">
|
||||
</div>
|
||||
<div style="margin-bottom: 12px;">
|
||||
<label style="display: block; margin-bottom: 4px; font-weight: 500;">Marital Status</label>
|
||||
<select id="addStaffMaritalStatus" name="marital_status" style="width: 100%; padding: 6px; border: 1px solid #ddd; border-radius: 4px;">
|
||||
<option value="">Select Marital Status</option>
|
||||
<option value="Married">Married</option>
|
||||
<option value="Unmarried">Unmarried</option>
|
||||
<option value="Divorced">Divorced</option>
|
||||
</select>
|
||||
</div>
|
||||
<div style="margin-bottom: 12px;">
|
||||
<label style="display: block; margin-bottom: 4px; font-weight: 500;">Citizenship Number</label>
|
||||
<input type="text" id="addStaffCitizenshipNo" name="citizenship_no" style="width: 100%; padding: 6px; border: 1px solid #ddd; border-radius: 4px;">
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
<div style="padding: 12px; border-top: 1px solid #EEEEEE; text-align: right; position: sticky; bottom: 0; background-color: white; border-bottom-left-radius: 8px; border-bottom-right-radius: 8px;">
|
||||
<button type="button" style="background-color: #9E9E9E; color: white; border: none; padding: 6px 12px; border-radius: 4px; cursor: pointer; font-size: 14px; margin-right: 8px;" onclick="closeAddStaffModal()">Cancel</button>
|
||||
<button type="button" style="background-color: #3F51B5; color: white; border: none; padding: 6px 12px; border-radius: 4px; cursor: pointer; font-size: 14px;" onclick="submitAddStaffForm()">Add Staff</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<script>
|
||||
function openAddStaffModal() {
|
||||
document.getElementById('addStaffModal').style.display = 'block';
|
||||
document.body.style.overflow = 'hidden';
|
||||
}
|
||||
|
||||
function closeAddStaffModal() {
|
||||
document.getElementById('addStaffModal').style.display = 'none';
|
||||
document.body.style.overflow = '';
|
||||
document.getElementById('addStaffForm').reset();
|
||||
}
|
||||
|
||||
function submitAddStaffForm() {
|
||||
const formData = new FormData(document.getElementById('addStaffForm'));
|
||||
|
||||
fetch('/app/add-staff/', {
|
||||
method: 'POST',
|
||||
body: formData,
|
||||
headers: {
|
||||
'X-CSRFToken': document.querySelector('[name=csrfmiddlewaretoken]').value
|
||||
}
|
||||
})
|
||||
.then(response => response.json())
|
||||
.then(data => {
|
||||
if (data.success) {
|
||||
closeAddStaffModal();
|
||||
showMessageModal('Success', 'Staff member added successfully. Default password is: 123');
|
||||
// Refresh the staff list
|
||||
location.reload();
|
||||
} else {
|
||||
showMessageModal('Error', data.message);
|
||||
}
|
||||
})
|
||||
.catch(error => {
|
||||
showMessageModal('Error', 'Failed to add staff member.');
|
||||
});
|
||||
}
|
||||
</script>
|
||||
@@ -1,145 +0,0 @@
|
||||
{% load static %}
|
||||
|
||||
<!-- Add Student Modal -->
|
||||
<div id="addStudentModal" style="display: none; position: fixed; top: 0; left: 0; right: 0; bottom: 0; background-color: rgba(0,0,0,0.5); z-index: 10000;" onclick="if(event.target === this) closeAddStudentModal();">
|
||||
<div style="position: absolute; top: 50%; left: 50%; transform: translate(-50%, -50%); width: 90%; max-width: 800px; background-color: white; border-radius: 8px; box-shadow: 0 2px 10px rgba(0,0,0,0.2); max-height: 90vh; overflow-y: auto;">
|
||||
<div style="background-color: #3F51B5; color: white; padding: 12px; border-top-left-radius: 8px; border-top-right-radius: 8px; display: flex; justify-content: space-between; align-items: center; position: sticky; top: 0; z-index: 1;">
|
||||
<h5 style="margin: 0; font-size: 18px;">Add New Student</h5>
|
||||
<button style="background: none; border: none; color: white; font-size: 24px; cursor: pointer; line-height: 24px;" onclick="closeAddStudentModal()">×</button>
|
||||
</div>
|
||||
<div style="padding: 15px;">
|
||||
<form id="addStudentForm">
|
||||
{% csrf_token %}
|
||||
<div style="display: grid; grid-template-columns: 1fr 1fr; gap: 15px;">
|
||||
<!-- Left Column -->
|
||||
<div>
|
||||
<div style="margin-bottom: 12px;">
|
||||
<label style="display: block; margin-bottom: 4px; font-weight: 500;">Name <span style="color: red;">*</span></label>
|
||||
<input type="text" id="addStudentName" name="name" style="width: 100%; padding: 6px; border: 1px solid #ddd; border-radius: 4px;" required>
|
||||
</div>
|
||||
<div style="margin-bottom: 12px;">
|
||||
<label style="display: block; margin-bottom: 4px; font-weight: 500;">Phone <span style="color: red;">*</span></label>
|
||||
<input type="tel" id="addStudentPhone" name="phone" style="width: 100%; padding: 6px; border: 1px solid #ddd; border-radius: 4px;" required pattern="[0-9]{10}" maxlength="10" title="Please enter a 10-digit phone number">
|
||||
<small style="color: #666; display: block; margin-top: 2px; font-size: 11px;">This will be used as the login ID</small>
|
||||
</div>
|
||||
<div style="margin-bottom: 12px;">
|
||||
<label style="display: block; margin-bottom: 4px; font-weight: 500;">Batch <span style="color: red;">*</span></label>
|
||||
<select id="addStudentBatch" name="batch" style="width: 100%; padding: 6px; border: 1px solid #ddd; border-radius: 4px;" required>
|
||||
<option value="">Select Batch</option>
|
||||
{% for batch in batches %}
|
||||
<option value="{{ batch.id }}">{{ batch.name }}</option>
|
||||
{% endfor %}
|
||||
</select>
|
||||
</div>
|
||||
<div style="margin-bottom: 12px;">
|
||||
<label style="display: block; margin-bottom: 4px; font-weight: 500;">Status <span style="color: red;">*</span></label>
|
||||
<select id="addStudentStatus" name="status" style="width: 100%; padding: 6px; border: 1px solid #ddd; border-radius: 4px;" required>
|
||||
<option value="">Select Status</option>
|
||||
<option value="Active">Active</option>
|
||||
<option value="Leave">Leave</option>
|
||||
<option value="Completed">Completed</option>
|
||||
</select>
|
||||
</div>
|
||||
<div style="margin-bottom: 12px;">
|
||||
<label style="display: block; margin-bottom: 4px; font-weight: 500;">Email</label>
|
||||
<input type="email" id="addStudentEmail" name="email" style="width: 100%; padding: 6px; border: 1px solid #ddd; border-radius: 4px;">
|
||||
</div>
|
||||
<div style="margin-bottom: 12px;">
|
||||
<label style="display: block; margin-bottom: 4px; font-weight: 500;">Gender</label>
|
||||
<select id="addStudentGender" name="gender" style="width: 100%; padding: 6px; border: 1px solid #ddd; border-radius: 4px;">
|
||||
<option value="">Select Gender</option>
|
||||
<option value="Male">Male</option>
|
||||
<option value="Female">Female</option>
|
||||
<option value="Other">Other</option>
|
||||
</select>
|
||||
</div>
|
||||
<div style="margin-bottom: 12px;">
|
||||
<label style="display: block; margin-bottom: 4px; font-weight: 500;">Birth Date</label>
|
||||
<input type="date" id="addStudentBirthDate" name="birth_date" style="width: 100%; padding: 6px; border: 1px solid #ddd; border-radius: 4px;">
|
||||
</div>
|
||||
</div>
|
||||
<!-- Right Column -->
|
||||
<div>
|
||||
<div style="margin-bottom: 12px;">
|
||||
<label style="display: block; margin-bottom: 4px; font-weight: 500;">Temporary Address</label>
|
||||
<textarea id="addStudentTempAddress" name="temporary_address" style="width: 100%; padding: 6px; border: 1px solid #ddd; border-radius: 4px; min-height: 60px;"></textarea>
|
||||
</div>
|
||||
<div style="margin-bottom: 12px;">
|
||||
<label style="display: block; margin-bottom: 4px; font-weight: 500;">Permanent Address</label>
|
||||
<textarea id="addStudentPermAddress" name="permanent_address" style="width: 100%; padding: 6px; border: 1px solid #ddd; border-radius: 4px; min-height: 60px;"></textarea>
|
||||
</div>
|
||||
<div style="margin-bottom: 12px;">
|
||||
<label style="display: block; margin-bottom: 4px; font-weight: 500;">Parent/Guardian Name</label>
|
||||
<input type="text" id="addStudentParentName" name="parent_name" style="width: 100%; padding: 6px; border: 1px solid #ddd; border-radius: 4px;">
|
||||
</div>
|
||||
<div style="margin-bottom: 12px;">
|
||||
<label style="display: block; margin-bottom: 4px; font-weight: 500;">Parent/Guardian Phone <span style="color: red;">*</span></label>
|
||||
<input type="tel" id="addStudentParentPhone" name="parent_phone" style="width: 100%; padding: 6px; border: 1px solid #ddd; border-radius: 4px;" required pattern="[0-9]{10}" maxlength="10" title="Please enter a 10-digit phone number">
|
||||
<small style="color: #666; display: block; margin-top: 2px; font-size: 11px;">This will be used to create parent account</small>
|
||||
</div>
|
||||
<div style="margin-bottom: 12px;">
|
||||
<label style="display: block; margin-bottom: 4px; font-weight: 500;">Citizenship Number</label>
|
||||
<input type="text" id="addStudentCitizenshipNo" name="citizenship_no" style="width: 100%; padding: 6px; border: 1px solid #ddd; border-radius: 4px;">
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
<div style="padding: 12px; border-top: 1px solid #EEEEEE; text-align: right; position: sticky; bottom: 0; background-color: white; border-bottom-left-radius: 8px; border-bottom-right-radius: 8px;">
|
||||
<button type="button" style="background-color: #9E9E9E; color: white; border: none; padding: 6px 12px; border-radius: 4px; cursor: pointer; font-size: 14px; margin-right: 8px;" onclick="closeAddStudentModal()">Cancel</button>
|
||||
<button type="button" style="background-color: #3F51B5; color: white; border: none; padding: 6px 12px; border-radius: 4px; cursor: pointer; font-size: 14px;" onclick="submitAddStudentForm()">Add Student</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<script>
|
||||
function openAddStudentModal() {
|
||||
document.getElementById('addStudentModal').style.display = 'block';
|
||||
document.body.style.overflow = 'hidden';
|
||||
}
|
||||
|
||||
function closeAddStudentModal() {
|
||||
document.getElementById('addStudentModal').style.display = 'none';
|
||||
document.body.style.overflow = '';
|
||||
document.getElementById('addStudentForm').reset();
|
||||
}
|
||||
|
||||
function submitAddStudentForm() {
|
||||
const formData = new FormData(document.getElementById('addStudentForm'));
|
||||
|
||||
fetch('{% url "add_student" %}', {
|
||||
method: 'POST',
|
||||
body: formData,
|
||||
headers: {
|
||||
'X-CSRFToken': document.querySelector('[name=csrfmiddlewaretoken]').value
|
||||
}
|
||||
})
|
||||
.then(response => {
|
||||
if (!response.ok) {
|
||||
throw new Error(`HTTP error! status: ${response.status}`);
|
||||
}
|
||||
return response.text().then(text => {
|
||||
try {
|
||||
return JSON.parse(text);
|
||||
} catch (e) {
|
||||
console.error('Invalid JSON response:', text);
|
||||
throw new Error('Invalid JSON response from server');
|
||||
}
|
||||
});
|
||||
})
|
||||
.then(data => {
|
||||
if (data.success) {
|
||||
closeAddStudentModal();
|
||||
showMessageModal('Success', data.message);
|
||||
// Refresh the student list
|
||||
location.reload();
|
||||
} else {
|
||||
showMessageModal('Error', data.message || 'Failed to add student');
|
||||
}
|
||||
})
|
||||
.catch(error => {
|
||||
console.error('Error:', error);
|
||||
showMessageModal('Error', error.message || 'Failed to add student. Please try again.');
|
||||
});
|
||||
}
|
||||
</script>
|
||||
@@ -1,106 +0,0 @@
|
||||
{% load static %}
|
||||
{% load app_filters %}
|
||||
|
||||
<!-- Add Subject Modal -->
|
||||
<div id="addSubjectModal" style="display: none; position: fixed; top: 0; left: 0; right: 0; bottom: 0; background-color: rgba(0,0,0,0.5); z-index: 10000;" onclick="if(event.target === this) closeAddSubjectModal();">
|
||||
<div style="position: absolute; top: 50%; left: 50%; transform: translate(-50%, -50%); width: 90%; max-width: 500px; background-color: white; border-radius: 8px; box-shadow: 0 2px 10px rgba(0,0,0,0.2);">
|
||||
<div style="background-color: #3F51B5; color: white; padding: 15px; border-top-left-radius: 8px; border-top-right-radius: 8px; display: flex; justify-content: space-between; align-items: center;">
|
||||
<h5 style="margin: 0; font-size: 18px;">Add New Subject</h5>
|
||||
<button style="background: none; border: none; color: white; font-size: 24px; cursor: pointer; line-height: 24px;" onclick="closeAddSubjectModal()">×</button>
|
||||
</div>
|
||||
<form id="addSubjectForm" action="{% url 'add_subject' %}" method="post" enctype="multipart/form-data">
|
||||
{% csrf_token %}
|
||||
<div style="padding: 20px;">
|
||||
<div style="margin-bottom: 15px;">
|
||||
<label for="subjectName" style="display: block; margin-bottom: 8px; font-weight: 500;">Subject Name</label>
|
||||
<input type="text" id="subjectName" name="name" required style="width: 100%; padding: 10px; border: 1px solid #BDBDBD; border-radius: 4px; box-sizing: border-box;">
|
||||
</div>
|
||||
<div style="margin-bottom: 15px;">
|
||||
<label for="subjectCode" style="display: block; margin-bottom: 8px; font-weight: 500;">Subject Code</label>
|
||||
<input type="text" id="subjectCode" name="code" style="width: 100%; padding: 10px; border: 1px solid #BDBDBD; border-radius: 4px; box-sizing: border-box;">
|
||||
</div>
|
||||
<div style="margin-bottom: 15px;">
|
||||
<label for="periodOrYear" style="display: block; margin-bottom: 8px; font-weight: 500;">Period/{{ course.duration_type }}</label>
|
||||
<input type="number" id="periodOrYear" name="period_or_year" min="1" required style="width: 100%; padding: 10px; border: 1px solid #BDBDBD; border-radius: 4px; box-sizing: border-box;">
|
||||
<small style="color: #666; display: block; margin-top: 5px;">
|
||||
{% if course.duration_type == 'Semester' %}
|
||||
Enter semester number (1-{{ course.duration|multiply:2 }})
|
||||
{% else %}
|
||||
Enter year number (1-{{ course.duration }})
|
||||
{% endif %}
|
||||
</small>
|
||||
</div>
|
||||
<div style="margin-bottom: 15px;">
|
||||
<label for="syllabusPdf" style="display: block; margin-bottom: 8px; font-weight: 500;">Syllabus PDF</label>
|
||||
<input type="file" id="syllabusPdf" name="syllabus_pdf" accept=".pdf" style="width: 100%; padding: 10px; border: 1px solid #BDBDBD; border-radius: 4px; box-sizing: border-box;">
|
||||
<small style="color: #666; display: block; margin-top: 5px;">Upload syllabus or study material in PDF format</small>
|
||||
</div>
|
||||
</div>
|
||||
<div style="padding: 15px; border-top: 1px solid #EEEEEE; text-align: right; display: flex; flex-wrap: wrap; justify-content: flex-end;">
|
||||
<button type="button" style="background-color: #9E9E9E; color: white; border: none; padding: 8px 16px; border-radius: 4px; margin-right: 10px; margin-bottom: 10px; cursor: pointer; font-size: 14px;" onclick="closeAddSubjectModal()">Cancel</button>
|
||||
<button type="submit" style="background-color: #3F51B5; color: white; border: none; padding: 8px 16px; border-radius: 4px; cursor: pointer; font-size: 14px; margin-bottom: 10px;">Add Subject</button>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<script>
|
||||
function openAddSubjectModal() {
|
||||
// Set max value for period/year input based on course duration
|
||||
const periodInput = document.getElementById('periodOrYear');
|
||||
{% if course.duration_type == 'Semester' %}
|
||||
periodInput.max = {{ course.duration|multiply:2 }};
|
||||
{% else %}
|
||||
periodInput.max = {{ course.duration }};
|
||||
{% endif %}
|
||||
|
||||
document.getElementById('addSubjectModal').style.display = 'block';
|
||||
}
|
||||
|
||||
function closeAddSubjectModal() {
|
||||
document.getElementById('addSubjectModal').style.display = 'none';
|
||||
document.getElementById('addSubjectForm').reset();
|
||||
}
|
||||
|
||||
// Handle form submission
|
||||
document.getElementById('addSubjectForm').addEventListener('submit', function(e) {
|
||||
e.preventDefault();
|
||||
const formData = new FormData(this);
|
||||
|
||||
// Validate period/year before submission
|
||||
const periodInput = document.getElementById('periodOrYear');
|
||||
const periodValue = parseInt(periodInput.value);
|
||||
const maxPeriod = parseInt(periodInput.max);
|
||||
|
||||
if (periodValue > maxPeriod) {
|
||||
{% if course.duration_type == 'Semester' %}
|
||||
alert(`Semester number cannot exceed ${maxPeriod}`);
|
||||
{% else %}
|
||||
alert(`Year number cannot exceed ${maxPeriod}`);
|
||||
{% endif %}
|
||||
return;
|
||||
}
|
||||
|
||||
fetch(this.action, {
|
||||
method: 'POST',
|
||||
body: formData,
|
||||
headers: {
|
||||
'X-CSRFToken': document.querySelector('[name=csrfmiddlewaretoken]').value
|
||||
}
|
||||
})
|
||||
.then(response => response.json())
|
||||
.then(data => {
|
||||
if (data.success) {
|
||||
closeAddSubjectModal();
|
||||
// Refresh the subjects table or add the new subject dynamically
|
||||
location.reload();
|
||||
} else {
|
||||
alert(data.message || 'Error adding subject');
|
||||
}
|
||||
})
|
||||
.catch(error => {
|
||||
console.error('Error:', error);
|
||||
alert('An error occurred while adding the subject');
|
||||
});
|
||||
});
|
||||
</script>
|
||||
@@ -1,23 +0,0 @@
|
||||
<!-- Cancel Meeting Modal -->
|
||||
<div id="cancelMeetingModal" style="display: none; position: fixed; top: 0; left: 0; right: 0; bottom: 0; background-color: rgba(0,0,0,0.5); z-index: 10000;" onclick="if(event.target === this) closeCancelMeetingModal();">
|
||||
<div style="position: absolute; top: 50%; left: 50%; transform: translate(-50%, -50%); width: 90%; max-width: 600px; background-color: white; border-radius: 8px; box-shadow: 0 2px 10px rgba(0,0,0,0.2); max-height: 90vh; overflow-y: auto;">
|
||||
<div style="background-color: #3F51B5; color: white; padding: 12px; border-top-left-radius: 8px; border-top-right-radius: 8px; display: flex; justify-content: space-between; align-items: center; position: sticky; top: 0; z-index: 1;">
|
||||
<h5 style="margin: 0; font-size: 18px;">Cancel Meeting</h5>
|
||||
<button style="background: none; border: none; color: white; font-size: 24px; cursor: pointer; line-height: 24px;" onclick="closeCancelMeetingModal()">×</button>
|
||||
</div>
|
||||
<div style="padding: 15px;">
|
||||
<form id="cancelMeetingForm">
|
||||
<input type="hidden" id="cancelMeetingId" name="meeting_id">
|
||||
<div style="margin-bottom: 12px;">
|
||||
<label style="display: block; margin-bottom: 4px; font-weight: 500;" for="cancellationReason">Reason for Cancellation</label>
|
||||
<textarea id="cancellationReason" name="cancellation_reason" rows="3" required style="width: 100%; padding: 8px; border: 1px solid #ddd; border-radius: 4px; min-height: 80px;"></textarea>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
|
||||
<div style="padding: 12px; border-top: 1px solid #EEEEEE; text-align: right; position: sticky; bottom: 0; background-color: white; border-bottom-left-radius: 8px; border-bottom-right-radius: 8px;">
|
||||
<button type="button" style="background-color: #9E9E9E; color: white; border: none; padding: 6px 12px; border-radius: 4px; cursor: pointer; font-size: 14px; margin-right: 8px;" onclick="closeCancelMeetingModal()">Close</button>
|
||||
<button type="button" style="background-color: #DC3545; color: white; border: none; padding: 6px 12px; border-radius: 4px; cursor: pointer; font-size: 14px;" onclick="submitCancelMeetingForm()">Cancel Meeting</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@@ -1,43 +0,0 @@
|
||||
<!-- Cancellation Reason Modal -->
|
||||
<div class="modal fade" id="cancellationReasonModal" tabindex="-1" aria-labelledby="cancellationReasonModalLabel" aria-hidden="true">
|
||||
<div class="modal-dialog">
|
||||
<div class="modal-content">
|
||||
<div class="modal-header">
|
||||
<h5 class="modal-title" id="cancellationReasonModalLabel">Cancellation Reason</h5>
|
||||
<button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Close"></button>
|
||||
</div>
|
||||
<div class="modal-body">
|
||||
<div id="cancellationReasonContent" class="p-3 bg-light rounded">
|
||||
<!-- Cancellation reason content will be loaded here -->
|
||||
</div>
|
||||
</div>
|
||||
<div class="modal-footer">
|
||||
<button type="button" class="btn btn-secondary" data-bs-dismiss="modal">Close</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<script>
|
||||
function viewCancellationReason(meetingId) {
|
||||
fetch(`/api/meetings/${meetingId}/cancellation-reason/`)
|
||||
.then(response => {
|
||||
if (!response.ok) {
|
||||
throw new Error('Network response was not ok: ' + response.statusText);
|
||||
}
|
||||
return response.json();
|
||||
})
|
||||
.then(data => {
|
||||
document.getElementById('cancellationReasonContent').innerHTML = data.reason || 'No reason provided';
|
||||
new bootstrap.Modal(document.getElementById('cancellationReasonModal')).show();
|
||||
})
|
||||
.catch(error => {
|
||||
console.error('Error fetching cancellation reason:', error);
|
||||
showToast('Failed to load cancellation reason', 'error');
|
||||
});
|
||||
}
|
||||
|
||||
function closeCancellationReasonModal() {
|
||||
bootstrap.Modal.getInstance(document.getElementById('cancellationReasonModal')).hide();
|
||||
}
|
||||
</script>
|
||||
@@ -1,79 +0,0 @@
|
||||
{% load static %}
|
||||
|
||||
<!-- Edit Meeting Modal -->
|
||||
<div id="editMeetingModal" style="display: none; position: fixed; top: 0; left: 0; right: 0; bottom: 0; background-color: rgba(0,0,0,0.5); z-index: 10000;" onclick="if(event.target === this) closeEditMeetingModal();">
|
||||
<div style="position: absolute; top: 50%; left: 50%; transform: translate(-50%, -50%); width: 90%; max-width: 600px; background-color: white; border-radius: 8px; box-shadow: 0 2px 10px rgba(0,0,0,0.2); max-height: 90vh; overflow-y: auto;">
|
||||
<div style="background-color: #3F51B5; color: white; padding: 12px; border-top-left-radius: 8px; border-top-right-radius: 8px; display: flex; justify-content: space-between; align-items: center; position: sticky; top: 0; z-index: 1;">
|
||||
<h5 style="margin: 0; font-size: 18px;">Edit Meeting</h5>
|
||||
<button style="background: none; border: none; color: white; font-size: 24px; cursor: pointer; line-height: 24px;" onclick="closeEditMeetingModal()">×</button>
|
||||
</div>
|
||||
<div style="padding: 15px;">
|
||||
<form id="editMeetingForm">
|
||||
<input type="hidden" id="editMeetingId" name="meeting_id">
|
||||
|
||||
<div style="display: grid; grid-template-columns: 1fr 1fr; gap: 15px;">
|
||||
<!-- Left Column -->
|
||||
<div>
|
||||
<div style="margin-bottom: 12px;">
|
||||
<label style="display: block; margin-bottom: 4px; font-weight: 500;" for="editMeetingDate">Date</label>
|
||||
<input type="date" id="editMeetingDate" name="meeting_date" required style="width: 100%; padding: 8px; border: 1px solid #ddd; border-radius: 4px;">
|
||||
</div>
|
||||
<div style="margin-bottom: 12px;">
|
||||
<label style="display: block; margin-bottom: 4px; font-weight: 500;" for="editMeetingTime">Time</label>
|
||||
<input type="time" id="editMeetingTime" name="meeting_time" required style="width: 100%; padding: 8px; border: 1px solid #ddd; border-radius: 4px;">
|
||||
</div>
|
||||
<div style="margin-bottom: 12px;">
|
||||
<label style="display: block; margin-bottom: 4px; font-weight: 500;" for="editDuration">Duration (minutes)</label>
|
||||
<input type="number" id="editDuration" name="duration" min="15" max="120" required style="width: 100%; padding: 8px; border: 1px solid #ddd; border-radius: 4px;">
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Right Column -->
|
||||
<div>
|
||||
<div style="margin-bottom: 12px;">
|
||||
<div style="display: flex; align-items: center;">
|
||||
<input type="checkbox" id="editIsOnline" name="is_online" style="margin-right: 8px;">
|
||||
<label for="editIsOnline" style="font-weight: 500;">Online Meeting</label>
|
||||
</div>
|
||||
</div>
|
||||
<div id="editMeetingLinkContainer" style="margin-bottom: 12px; display: none;">
|
||||
<label style="display: block; margin-bottom: 4px; font-weight: 500;" for="editMeetingLink">Meeting Link</label>
|
||||
<input type="url" id="editMeetingLink" name="meeting_link" style="width: 100%; padding: 8px; border: 1px solid #ddd; border-radius: 4px;">
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Full Width Sections -->
|
||||
<div style="margin-top: 15px;">
|
||||
<div style="margin-bottom: 12px;">
|
||||
<label style="display: block; margin-bottom: 4px; font-weight: 500;" for="editAgenda">Agenda</label>
|
||||
<textarea id="editAgenda" name="agenda" rows="3" style="width: 100%; padding: 8px; border: 1px solid #ddd; border-radius: 4px; min-height: 60px;"></textarea>
|
||||
</div>
|
||||
<div style="margin-bottom: 12px;">
|
||||
<label style="display: block; margin-bottom: 4px; font-weight: 500;" for="editNotes">Notes</label>
|
||||
<textarea id="editNotes" name="notes" rows="3" style="width: 100%; padding: 8px; border: 1px solid #ddd; border-radius: 4px; min-height: 60px;"></textarea>
|
||||
</div>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
|
||||
<div style="padding: 12px; border-top: 1px solid #EEEEEE; text-align: right; position: sticky; bottom: 0; background-color: white; border-bottom-left-radius: 8px; border-bottom-right-radius: 8px;">
|
||||
<button type="button" style="background-color: #9E9E9E; color: white; border: none; padding: 6px 12px; border-radius: 4px; cursor: pointer; font-size: 14px; margin-right: 8px;" onclick="closeEditMeetingModal()">Cancel</button>
|
||||
<button type="button" style="background-color: #3F51B5; color: white; border: none; padding: 6px 12px; border-radius: 4px; cursor: pointer; font-size: 14px;" onclick="submitEditMeetingForm()">Save Changes</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<script>
|
||||
// Toggle meeting link field based on online/in-person selection
|
||||
document.addEventListener('DOMContentLoaded', function() {
|
||||
if (document.getElementById('editIsOnline')) {
|
||||
document.getElementById('editIsOnline').addEventListener('change', function() {
|
||||
const meetingLinkContainer = document.getElementById('editMeetingLinkContainer');
|
||||
const meetingLinkInput = document.getElementById('editMeetingLink');
|
||||
meetingLinkContainer.style.display = this.checked ? 'block' : 'none';
|
||||
meetingLinkInput.required = this.checked;
|
||||
});
|
||||
}
|
||||
});
|
||||
</script>
|
||||
@@ -1,211 +0,0 @@
|
||||
{% load static %}
|
||||
|
||||
<!-- Edit Progress Modal -->
|
||||
<div id="editProgressModal" style="display: none; position: fixed; top: 0; left: 0; right: 0; bottom: 0; background-color: rgba(0,0,0,0.5); z-index: 10000;" onclick="if(event.target === this) closeEditProgressModal();">
|
||||
<div style="position: absolute; top: 50%; left: 50%; transform: translate(-50%, -50%); width: 90%; max-width: 600px; background-color: white; border-radius: 8px; box-shadow: 0 2px 10px rgba(0,0,0,0.2); max-height: 90vh; overflow-y: auto;">
|
||||
<div style="background-color: #3F51B5; color: white; padding: 12px; border-top-left-radius: 8px; border-top-right-radius: 8px; display: flex; justify-content: space-between; align-items: center; position: sticky; top: 0; z-index: 1;">
|
||||
<h5 style="margin: 0; font-size: 18px;">Edit Student Progress</h5>
|
||||
<button style="background: none; border: none; color: white; font-size: 24px; cursor: pointer; line-height: 24px;" onclick="closeEditProgressModal()">×</button>
|
||||
</div>
|
||||
<div style="padding: 15px;">
|
||||
<form id="editProgressForm">
|
||||
{% csrf_token %}
|
||||
<input type="hidden" id="edit_progress_id" name="progress_id">
|
||||
|
||||
<div style="margin-bottom: 15px;">
|
||||
<div class="d-flex align-items-center mb-2">
|
||||
<div id="student_profile_container" style="width: 40px; height: 40px; border-radius: 50%; background-color: #e0e0e0; overflow: hidden; margin-right: 10px; display: flex; align-items: center; justify-content: center;">
|
||||
<img id="student_profile_image" src="{% static 'img/user.png' %}" alt="Student" style="width: 100%; height: 100%; object-fit: cover;">
|
||||
</div>
|
||||
<div>
|
||||
<h6 id="student_name" style="margin: 0; font-weight: 600;"></h6>
|
||||
<small id="student_batch" style="color: #666;"></small>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div style="margin-bottom: 15px;">
|
||||
<label style="display: block; margin-bottom: 6px; font-weight: 500; color: #3F51B5;">Current Period <span style="color: red;">*</span></label>
|
||||
<input type="number" id="edit_current_period" name="current_period" style="width: 100%; padding: 8px; border: 1px solid #ddd; border-radius: 4px;" required>
|
||||
</div>
|
||||
|
||||
<div style="margin-bottom: 15px;">
|
||||
<label style="display: block; margin-bottom: 6px; font-weight: 500; color: #3F51B5;">Completion Percentage <span style="color: red;">*</span></label>
|
||||
<input type="number" id="edit_completion_percentage" name="completion_percentage" min="0" max="100" style="width: 100%; padding: 8px; border: 1px solid #ddd; border-radius: 4px;" required>
|
||||
|
||||
<div style="margin-top: 10px;">
|
||||
<div class="progress" style="height: 20px;">
|
||||
<div id="completion_preview" class="progress-bar" role="progressbar" style="width: 0%;" aria-valuenow="0" aria-valuemin="0" aria-valuemax="100"></div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div style="margin-bottom: 15px;">
|
||||
<label style="display: block; margin-bottom: 6px; font-weight: 500; color: #3F51B5;">Progress Status</label>
|
||||
<div>
|
||||
<span id="edit_progress_status_display" class="badge bg-primary">In Progress</span>
|
||||
<small class="text-muted d-block mt-1">Status is automatically determined based on completion percentage and dates</small>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div style="margin-bottom: 15px;">
|
||||
<label style="display: block; margin-bottom: 6px; font-weight: 500; color: #3F51B5;">Notes</label>
|
||||
<textarea id="edit_notes" name="notes" style="width: 100%; padding: 8px; border: 1px solid #ddd; border-radius: 4px; min-height: 100px;"></textarea>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
<div style="padding: 12px; border-top: 1px solid #EEEEEE; text-align: right; position: sticky; bottom: 0; background-color: white; border-bottom-left-radius: 8px; border-bottom-right-radius: 8px;">
|
||||
<button type="button" style="background-color: #9E9E9E; color: white; border: none; padding: 8px 16px; border-radius: 4px; cursor: pointer; font-size: 14px; margin-right: 8px;" onclick="closeEditProgressModal()">Cancel</button>
|
||||
<button type="button" style="background-color: #3F51B5; color: white; border: none; padding: 8px 16px; border-radius: 4px; cursor: pointer; font-size: 14px;" onclick="submitEditProgressForm()">Update Progress</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<script>
|
||||
function openEditProgressModal(progressId) {
|
||||
document.getElementById('editProgressModal').style.display = 'block';
|
||||
document.body.style.overflow = 'hidden';
|
||||
loadProgressData(progressId);
|
||||
}
|
||||
|
||||
function closeEditProgressModal() {
|
||||
document.getElementById('editProgressModal').style.display = 'none';
|
||||
document.body.style.overflow = '';
|
||||
document.getElementById('editProgressForm').reset();
|
||||
}
|
||||
|
||||
function loadProgressData(progressId) {
|
||||
// Reset form
|
||||
document.getElementById('edit_progress_id').value = '';
|
||||
document.getElementById('student_name').textContent = '';
|
||||
document.getElementById('student_batch').textContent = '';
|
||||
document.getElementById('edit_current_period').value = '';
|
||||
document.getElementById('edit_completion_percentage').value = '';
|
||||
document.getElementById('edit_progress_status_display').textContent = 'In Progress';
|
||||
document.getElementById('edit_progress_status_display').className = 'badge bg-primary';
|
||||
document.getElementById('edit_notes').value = '';
|
||||
document.getElementById('student_profile_image').src = "{% static 'img/user.png' %}";
|
||||
|
||||
// Update progress bar preview
|
||||
updateCompletionPreview(0);
|
||||
|
||||
fetch(`/app/api/get-progress/${progressId}/`)
|
||||
.then(response => {
|
||||
if (!response.ok) {
|
||||
throw new Error('Network response was not ok');
|
||||
}
|
||||
return response.json();
|
||||
})
|
||||
.then(data => {
|
||||
if (data.success) {
|
||||
const progress = data.progress;
|
||||
const student = data.student;
|
||||
|
||||
// Set form values
|
||||
document.getElementById('edit_progress_id').value = progress.id;
|
||||
document.getElementById('student_name').textContent = student.name;
|
||||
|
||||
// Set batch information
|
||||
if (student.batches && student.batches.length > 0) {
|
||||
document.getElementById('student_batch').textContent = student.batches.join(', ');
|
||||
} else {
|
||||
document.getElementById('student_batch').textContent = 'No batch assigned';
|
||||
}
|
||||
|
||||
// Set student image if available
|
||||
if (student.image) {
|
||||
document.getElementById('student_profile_image').src = student.image;
|
||||
}
|
||||
|
||||
document.getElementById('edit_current_period').value = student.current_period || '';
|
||||
document.getElementById('edit_completion_percentage').value = progress.completion_percentage || 0;
|
||||
|
||||
// Update status display
|
||||
document.getElementById('edit_progress_status_display').textContent = progress.progress_status || 'In Progress';
|
||||
|
||||
// Set status badge color
|
||||
const statusBadge = document.getElementById('edit_progress_status_display');
|
||||
statusBadge.className = 'badge';
|
||||
if (progress.progress_status === 'Completed') {
|
||||
statusBadge.classList.add('bg-success');
|
||||
} else if (progress.progress_status === 'In Progress') {
|
||||
statusBadge.classList.add('bg-primary');
|
||||
} else if (progress.progress_status === 'Not Started') {
|
||||
statusBadge.classList.add('bg-secondary');
|
||||
} else if (progress.progress_status === 'Dropped') {
|
||||
statusBadge.classList.add('bg-danger');
|
||||
} else {
|
||||
statusBadge.classList.add('bg-primary');
|
||||
}
|
||||
|
||||
document.getElementById('edit_notes').value = progress.notes || '';
|
||||
|
||||
// Update progress bar preview
|
||||
updateCompletionPreview(progress.completion_percentage || 0);
|
||||
} else {
|
||||
showMessageModal('Error', data.message || 'Error loading progress data');
|
||||
}
|
||||
})
|
||||
.catch(error => {
|
||||
showMessageModal('Error', 'An error occurred while loading progress data');
|
||||
});
|
||||
}
|
||||
|
||||
function updateCompletionPreview(percentage) {
|
||||
const progressBar = document.getElementById('completion_preview');
|
||||
progressBar.style.width = `${percentage}%`;
|
||||
progressBar.setAttribute('aria-valuenow', percentage);
|
||||
|
||||
// Update class based on percentage
|
||||
progressBar.className = 'progress-bar';
|
||||
if (percentage >= 75) {
|
||||
progressBar.classList.add('bg-success');
|
||||
} else if (percentage >= 40) {
|
||||
progressBar.classList.add('bg-warning');
|
||||
} else {
|
||||
progressBar.classList.add('bg-danger');
|
||||
}
|
||||
}
|
||||
|
||||
// Add event listener to completion percentage input to update preview
|
||||
document.addEventListener('DOMContentLoaded', function() {
|
||||
const completionInput = document.getElementById('edit_completion_percentage');
|
||||
if (completionInput) {
|
||||
completionInput.addEventListener('input', function() {
|
||||
updateCompletionPreview(this.value);
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
function submitEditProgressForm() {
|
||||
const form = document.getElementById('editProgressForm');
|
||||
const formData = new FormData(form);
|
||||
const progressId = document.getElementById('edit_progress_id').value;
|
||||
|
||||
fetch(`/app/api/edit-progress/${progressId}/`, {
|
||||
method: 'POST',
|
||||
body: formData,
|
||||
headers: {
|
||||
'X-CSRFToken': document.querySelector('[name=csrfmiddlewaretoken]').value
|
||||
}
|
||||
})
|
||||
.then(response => {
|
||||
if (!response.ok) {
|
||||
throw new Error('Network response was not ok');
|
||||
}
|
||||
return response.json();
|
||||
})
|
||||
.then(data => {
|
||||
if (data.success) {
|
||||
showMessageModal('Success', 'Student progress updated successfully');
|
||||
closeEditProgressModal();
|
||||
location.reload();
|
||||
} else {
|
||||
showMessageModal('Error', data.message || 'Error updating progress');
|
||||
}
|
||||
})
|
||||
.catch(error => {
|
||||
showMessageModal('Error', 'An error occurred while updating progress');
|
||||
});
|
||||
}
|
||||
</script>
|
||||
@@ -1,267 +0,0 @@
|
||||
{% load static %}
|
||||
|
||||
<!-- Edit Routine Modal -->
|
||||
<div id="editRoutineModal" style="display: none; position: fixed; top: 0; left: 0; right: 0; bottom: 0; background-color: rgba(0,0,0,0.5); z-index: 10000;" onclick="if(event.target === this) closeEditRoutineModal();">
|
||||
<div style="position: absolute; top: 50%; left: 50%; transform: translate(-50%, -50%); width: 90%; max-width: 500px; background-color: white; border-radius: 8px; box-shadow: 0 2px 10px rgba(0,0,0,0.2);">
|
||||
<div style="background-color: #3F51B5; color: white; padding: 15px; border-top-left-radius: 8px; border-top-right-radius: 8px; display: flex; justify-content: space-between; align-items: center;">
|
||||
<h5 style="margin: 0; font-size: 18px;">Edit Class Routine</h5>
|
||||
<button style="background: none; border: none; color: white; font-size: 24px; cursor: pointer; line-height: 24px;" onclick="closeEditRoutineModal()">×</button>
|
||||
</div>
|
||||
<form id="editRoutineForm">
|
||||
{% csrf_token %}
|
||||
<input type="hidden" id="editRoutineId" name="routine_id">
|
||||
<div style="padding: 20px;">
|
||||
<div style="margin-bottom: 15px;">
|
||||
<label for="editSubjectId" style="display: block; margin-bottom: 8px; font-weight: 500;">Subject</label>
|
||||
<select id="editSubjectId" name="subject_id" style="width: 100%; padding: 10px; border: 1px solid #BDBDBD; border-radius: 4px; box-sizing: border-box;" required>
|
||||
<option value="">Select Subject</option>
|
||||
{% for subject in subjects %}
|
||||
<option value="{{ subject.id }}">{{ subject.name }} ({% if course.duration_type == 'Semester' %}Semester{% else %}Year{% endif %} {{ subject.period_or_year }})</option>
|
||||
{% endfor %}
|
||||
</select>
|
||||
</div>
|
||||
|
||||
<div style="margin-bottom: 15px;">
|
||||
<label for="editTeacherId" style="display: block; margin-bottom: 8px; font-weight: 500;">Teacher</label>
|
||||
<select id="editTeacherId" name="teacher_id" style="width: 100%; padding: 10px; border: 1px solid #BDBDBD; border-radius: 4px; box-sizing: border-box;" required>
|
||||
<option value="">Select Teacher</option>
|
||||
{% for teacher in staff %}
|
||||
<option value="{{ teacher.id }}">{{ teacher.name }}</option>
|
||||
{% endfor %}
|
||||
</select>
|
||||
</div>
|
||||
|
||||
<div style="display: flex; gap: 15px; margin-bottom: 15px;">
|
||||
<div style="flex: 1;">
|
||||
<label for="editStartTime" style="display: block; margin-bottom: 8px; font-weight: 500;">Start Time</label>
|
||||
<input type="time" id="editStartTime" name="start_time" style="width: 100%; padding: 10px; border: 1px solid #BDBDBD; border-radius: 4px; box-sizing: border-box;" required>
|
||||
</div>
|
||||
<div style="flex: 1;">
|
||||
<label for="editEndTime" style="display: block; margin-bottom: 8px; font-weight: 500;">End Time</label>
|
||||
<input type="time" id="editEndTime" name="end_time" style="width: 100%; padding: 10px; border: 1px solid #BDBDBD; border-radius: 4px; box-sizing: border-box;" required>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<input type="hidden" id="editPeriodOrYear" name="period_or_year">
|
||||
</div>
|
||||
<div style="padding: 15px; border-top: 1px solid #EEEEEE; text-align: right; display: flex; flex-wrap: wrap; justify-content: flex-end;">
|
||||
<button type="button" style="background-color: #9E9E9E; color: white; border: none; padding: 8px 16px; border-radius: 4px; margin-right: 10px; margin-bottom: 10px; cursor: pointer; font-size: 14px;" onclick="closeEditRoutineModal()">Cancel</button>
|
||||
<button type="button" style="background-color: #3F51B5; color: white; border: none; padding: 8px 16px; border-radius: 4px; cursor: pointer; font-size: 14px; margin-bottom: 10px;" onclick="submitEditRoutineForm()">Update</button>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<script>
|
||||
function openEditRoutineModal(routineId) {
|
||||
if (!routineId) {
|
||||
console.error('No routine ID provided');
|
||||
return;
|
||||
}
|
||||
|
||||
console.log(`Fetching routine data for ID: ${routineId}`);
|
||||
|
||||
// Fetch routine data
|
||||
fetch(`/app/api/hod/routines/${routineId}/`)
|
||||
.then(response => {
|
||||
if (!response.ok) {
|
||||
throw new Error('Failed to fetch routine data');
|
||||
}
|
||||
return response.json();
|
||||
})
|
||||
.then(data => {
|
||||
console.log('Received routine data:', data);
|
||||
|
||||
// Check if data has the correct structure
|
||||
if (!data.success || !data.routine) {
|
||||
throw new Error('Invalid data format received from server');
|
||||
}
|
||||
|
||||
// Get routine data from the response
|
||||
const routine = data.routine;
|
||||
|
||||
// Populate form fields
|
||||
document.getElementById('editRoutineId').value = routineId;
|
||||
document.getElementById('editSubjectId').value = routine.subject_id;
|
||||
document.getElementById('editTeacherId').value = routine.teacher_id;
|
||||
document.getElementById('editStartTime').value = routine.start_time;
|
||||
document.getElementById('editEndTime').value = routine.end_time;
|
||||
document.getElementById('editPeriodOrYear').value = routine.period_or_year;
|
||||
|
||||
console.log('Form populated with values:', {
|
||||
subject_id: routine.subject_id,
|
||||
teacher_id: routine.teacher_id,
|
||||
start_time: routine.start_time,
|
||||
end_time: routine.end_time,
|
||||
period_or_year: routine.period_or_year
|
||||
});
|
||||
|
||||
// Set default period/year based on subject selection
|
||||
const subjectSelect = document.getElementById('editSubjectId');
|
||||
if (subjectSelect) {
|
||||
subjectSelect.addEventListener('change', function() {
|
||||
const selectedOption = this.options[this.selectedIndex];
|
||||
const periodOrYear = selectedOption.textContent.match(/\((?:Semester|Year) (\d+)\)/);
|
||||
if (periodOrYear && periodOrYear[1]) {
|
||||
document.getElementById('editPeriodOrYear').value = periodOrYear[1];
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
// Show modal
|
||||
const modal = document.getElementById('editRoutineModal');
|
||||
if (modal) {
|
||||
modal.style.display = 'block';
|
||||
document.body.style.overflow = 'hidden';
|
||||
}
|
||||
})
|
||||
.catch(error => {
|
||||
console.error('Error fetching routine data:', error);
|
||||
if (typeof window.showMessageModal === 'function') {
|
||||
window.showMessageModal('Error', error.message || 'Failed to fetch routine data');
|
||||
} else if (typeof window.showToast === 'function') {
|
||||
window.showToast('error', error.message || 'Failed to fetch routine data');
|
||||
} else {
|
||||
alert('Failed to fetch routine data: ' + error.message);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
function closeEditRoutineModal() {
|
||||
const modal = document.getElementById('editRoutineModal');
|
||||
if (modal) {
|
||||
modal.style.display = 'none';
|
||||
document.body.style.overflow = '';
|
||||
}
|
||||
}
|
||||
|
||||
// Make functions available globally
|
||||
if (typeof window !== 'undefined') {
|
||||
window.openEditRoutineModal = openEditRoutineModal;
|
||||
window.closeEditRoutineModal = closeEditRoutineModal;
|
||||
}
|
||||
|
||||
// Add form submission function
|
||||
function submitEditRoutineForm() {
|
||||
const form = document.getElementById('editRoutineForm');
|
||||
if (!form) {
|
||||
console.error('Edit routine form not found');
|
||||
return;
|
||||
}
|
||||
|
||||
// Create form data
|
||||
const formData = new FormData(form);
|
||||
const routineId = formData.get('routine_id');
|
||||
|
||||
if (!routineId) {
|
||||
console.error('No routine ID provided');
|
||||
return;
|
||||
}
|
||||
|
||||
// Validate form
|
||||
const subjectId = formData.get('subject_id');
|
||||
const teacherId = formData.get('teacher_id');
|
||||
const startTime = formData.get('start_time');
|
||||
const endTime = formData.get('end_time');
|
||||
|
||||
console.log('Submitting form with values:', {
|
||||
routine_id: routineId,
|
||||
subject_id: subjectId,
|
||||
teacher_id: teacherId,
|
||||
start_time: startTime,
|
||||
end_time: endTime,
|
||||
period_or_year: formData.get('period_or_year')
|
||||
});
|
||||
|
||||
if (!subjectId || !teacherId || !startTime || !endTime) {
|
||||
const message = 'Please fill in all required fields';
|
||||
console.error(message, {
|
||||
subject_id: subjectId,
|
||||
teacher_id: teacherId,
|
||||
start_time: startTime,
|
||||
end_time: endTime
|
||||
});
|
||||
|
||||
if (typeof window.showMessageModal === 'function') {
|
||||
window.showMessageModal('Error', message);
|
||||
} else if (typeof window.showToast === 'function') {
|
||||
window.showToast('error', message);
|
||||
} else {
|
||||
alert(message);
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
// Get CSRF token
|
||||
const csrfToken = document.querySelector('[name=csrfmiddlewaretoken]').value;
|
||||
console.log('Using CSRF token:', csrfToken);
|
||||
|
||||
// Send request to server - using POST instead of PUT to avoid potential issues
|
||||
fetch(`/app/api/hod/routines/${routineId}/edit/`, {
|
||||
method: 'POST', // Changed from PUT to POST
|
||||
body: formData,
|
||||
headers: {
|
||||
'X-CSRFToken': csrfToken,
|
||||
// Don't set Content-Type header when using FormData,
|
||||
// browser will set it automatically with proper boundary
|
||||
}
|
||||
})
|
||||
.then(response => {
|
||||
console.log('Server response status:', response.status);
|
||||
console.log('Server response headers:', Object.fromEntries([...response.headers]));
|
||||
|
||||
// Try to handle both JSON and text responses
|
||||
const contentType = response.headers.get('content-type');
|
||||
if (contentType && contentType.includes('application/json')) {
|
||||
return response.json().then(data => {
|
||||
if (!response.ok) {
|
||||
console.error('Server error response:', data);
|
||||
throw new Error(data.message || 'Failed to update routine');
|
||||
}
|
||||
return data;
|
||||
});
|
||||
} else {
|
||||
return response.text().then(text => {
|
||||
console.log('Server response text:', text);
|
||||
if (!response.ok) {
|
||||
throw new Error(text || 'Failed to update routine');
|
||||
}
|
||||
try {
|
||||
return JSON.parse(text);
|
||||
} catch (e) {
|
||||
return { success: true, message: 'Routine updated successfully' };
|
||||
}
|
||||
});
|
||||
}
|
||||
})
|
||||
.then(data => {
|
||||
console.log('Server success response:', data);
|
||||
if (typeof window.showMessageModal === 'function') {
|
||||
window.showMessageModal('Success', 'Routine updated successfully');
|
||||
} else if (typeof window.showToast === 'function') {
|
||||
window.showToast('success', 'Routine updated successfully');
|
||||
} else {
|
||||
alert('Routine updated successfully');
|
||||
}
|
||||
closeEditRoutineModal();
|
||||
setTimeout(() => {
|
||||
window.location.reload();
|
||||
}, 1000);
|
||||
})
|
||||
.catch(error => {
|
||||
console.error('Error updating routine:', error);
|
||||
if (typeof window.showMessageModal === 'function') {
|
||||
window.showMessageModal('Error', error.message || 'Failed to update routine');
|
||||
} else if (typeof window.showToast === 'function') {
|
||||
window.showToast('error', error.message || 'Failed to update routine');
|
||||
} else {
|
||||
alert('Failed to update routine: ' + error.message);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
// Make submission function globally available
|
||||
if (typeof window !== 'undefined') {
|
||||
window.submitEditRoutineForm = submitEditRoutineForm;
|
||||
}
|
||||
</script>
|
||||
@@ -1,202 +0,0 @@
|
||||
{% load static %}
|
||||
|
||||
<div id="editStaffModal" style="display: none; position: fixed; top: 0; left: 0; right: 0; bottom: 0; background-color: rgba(0,0,0,0.5); z-index: 10000;" onclick="if(event.target === this) closeEditStaffModal();">
|
||||
<div style="position: absolute; top: 50%; left: 50%; transform: translate(-50%, -50%); width: 90%; max-width: 800px; background-color: white; border-radius: 8px; box-shadow: 0 2px 10px rgba(0,0,0,0.2); max-height: 90vh; overflow-y: auto;">
|
||||
<div style="background-color: #3F51B5; color: white; padding: 12px; border-top-left-radius: 8px; border-top-right-radius: 8px; display: flex; justify-content: space-between; align-items: center; position: sticky; top: 0; z-index: 1;">
|
||||
<h5 style="margin: 0; font-size: 18px;">Edit Staff</h5>
|
||||
<button style="background: none; border: none; color: white; font-size: 24px; cursor: pointer; line-height: 24px;" onclick="closeEditStaffModal()">×</button>
|
||||
</div>
|
||||
<div style="padding: 15px;">
|
||||
<form id="editStaffForm" enctype="multipart/form-data">
|
||||
{% csrf_token %}
|
||||
<input type="hidden" id="editStaffId" name="staff_id">
|
||||
<div style="display: grid; grid-template-columns: 1fr 1fr; gap: 15px;">
|
||||
<!-- Left Column -->
|
||||
<div>
|
||||
<div style="text-align: center; margin-bottom: 20px;">
|
||||
<img id="editStaffProfilePicture" src="{% static 'img/user.png' %}" alt="Profile Picture" style="width: 120px; height: 120px; object-fit: cover; border-radius: 50%; border: 3px solid #3F51B5;">
|
||||
<div style="margin-top: 10px;">
|
||||
<input type="file" id="editStaffProfilePictureInput" name="image" accept="image/*" style="display: none;">
|
||||
<button type="button" onclick="document.getElementById('editStaffProfilePictureInput').click()" style="background-color: #3F51B5; color: white; border: none; padding: 6px 12px; border-radius: 4px; cursor: pointer; font-size: 14px;">Change Picture</button>
|
||||
</div>
|
||||
</div>
|
||||
<div style="margin-bottom: 12px;">
|
||||
<label style="display: block; margin-bottom: 4px; font-weight: 500;">Name <span style="color: red;">*</span></label>
|
||||
<input type="text" id="editStaffName" name="name" style="width: 100%; padding: 6px; border: 1px solid #ddd; border-radius: 4px;" required>
|
||||
</div>
|
||||
<div style="margin-bottom: 12px;">
|
||||
<label style="display: block; margin-bottom: 4px; font-weight: 500;">Phone <span style="color: red;">*</span></label>
|
||||
<input type="tel" id="editStaffPhone" name="phone" style="width: 100%; padding: 6px; border: 1px solid #ddd; border-radius: 4px;" required pattern="[0-9]{10}" maxlength="10" title="Please enter a 10-digit phone number">
|
||||
<small style="color: #666; display: block; margin-top: 2px; font-size: 11px;">This will be used as the login ID</small>
|
||||
</div>
|
||||
<div style="margin-bottom: 12px;">
|
||||
<label style="display: block; margin-bottom: 4px; font-weight: 500;">Email</label>
|
||||
<input type="email" id="editStaffEmail" name="email" style="width: 100%; padding: 6px; border: 1px solid #ddd; border-radius: 4px;">
|
||||
</div>
|
||||
<div style="margin-bottom: 12px;">
|
||||
<label style="display: block; margin-bottom: 4px; font-weight: 500;">Gender</label>
|
||||
<select id="editStaffGender" name="gender" style="width: 100%; padding: 6px; border: 1px solid #ddd; border-radius: 4px;">
|
||||
<option value="">Select Gender</option>
|
||||
<option value="Male">Male</option>
|
||||
<option value="Female">Female</option>
|
||||
<option value="Other">Other</option>
|
||||
</select>
|
||||
</div>
|
||||
<div style="margin-bottom: 12px;">
|
||||
<label style="display: block; margin-bottom: 4px; font-weight: 500;">Birth Date</label>
|
||||
<input type="date" id="editStaffBirthDate" name="birth_date" style="width: 100%; padding: 6px; border: 1px solid #ddd; border-radius: 4px;">
|
||||
</div>
|
||||
<div style="margin-bottom: 12px;">
|
||||
<label style="display: block; margin-bottom: 4px; font-weight: 500;">Marital Status</label>
|
||||
<select id="editStaffMaritalStatus" name="marital_status" style="width: 100%; padding: 6px; border: 1px solid #ddd; border-radius: 4px;">
|
||||
<option value="">Select Marital Status</option>
|
||||
<option value="Married">Married</option>
|
||||
<option value="Unmarried">Unmarried</option>
|
||||
<option value="Divorced">Divorced</option>
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
<!-- Right Column -->
|
||||
<div>
|
||||
<div style="margin-bottom: 12px;">
|
||||
<label style="display: block; margin-bottom: 4px; font-weight: 500;">Assigned Subjects <span style="color: red;">*</span></label>
|
||||
<select id="editStaffSubjects" name="subjects[]" multiple style="width: 100%; min-height: 100px; padding: 6px; border: 1px solid #ddd; border-radius: 4px;" required>
|
||||
{% for subject in subjects %}
|
||||
<option value="{{ subject.id }}">
|
||||
{{ subject.name }} {% if course.duration_type == 'Semester' %}(Semester {{ subject.period_or_year }}){% else %}(Year {{ subject.period_or_year }}){% endif %}
|
||||
</option>
|
||||
{% empty %}
|
||||
<option value="" disabled>No subjects available</option>
|
||||
{% endfor %}
|
||||
</select>
|
||||
<small style="color: #666; display: block; margin-top: 2px; font-size: 11px;">Hold Ctrl/Cmd to select multiple subjects</small>
|
||||
</div>
|
||||
<div style="margin-bottom: 12px;">
|
||||
<label style="display: block; margin-bottom: 4px; font-weight: 500;">Temporary Address</label>
|
||||
<input type="text" id="editStaffTempAddress" name="temporary_address" style="width: 100%; padding: 6px; border: 1px solid #ddd; border-radius: 4px;">
|
||||
</div>
|
||||
<div style="margin-bottom: 12px;">
|
||||
<label style="display: block; margin-bottom: 4px; font-weight: 500;">Permanent Address</label>
|
||||
<input type="text" id="editStaffPermAddress" name="permanent_address" style="width: 100%; padding: 6px; border: 1px solid #ddd; border-radius: 4px;">
|
||||
</div>
|
||||
<div style="margin-bottom: 12px;">
|
||||
<label style="display: block; margin-bottom: 4px; font-weight: 500;">Parent Name</label>
|
||||
<input type="text" id="editStaffParentName" name="parent_name" style="width: 100%; padding: 6px; border: 1px solid #ddd; border-radius: 4px;">
|
||||
</div>
|
||||
<div style="margin-bottom: 12px;">
|
||||
<label style="display: block; margin-bottom: 4px; font-weight: 500;">Parent Phone</label>
|
||||
<input type="tel" id="editStaffParentPhone" name="parent_phone" style="width: 100%; padding: 6px; border: 1px solid #ddd; border-radius: 4px;" pattern="[0-9]{10}" maxlength="10" title="Please enter a 10-digit phone number">
|
||||
</div>
|
||||
<div style="margin-bottom: 12px;">
|
||||
<label style="display: block; margin-bottom: 4px; font-weight: 500;">Citizenship Number</label>
|
||||
<input type="text" id="editStaffCitizenshipNo" name="citizenship_no" style="width: 100%; padding: 6px; border: 1px solid #ddd; border-radius: 4px;">
|
||||
</div>
|
||||
<div style="margin-bottom: 12px;">
|
||||
<label style="display: block; margin-bottom: 4px; font-weight: 500;">Passport</label>
|
||||
<input type="text" id="editStaffPassport" name="passport" style="width: 100%; padding: 6px; border: 1px solid #ddd; border-radius: 4px;">
|
||||
</div>
|
||||
<div style="margin-bottom: 12px;">
|
||||
<label style="display: block; margin-bottom: 4px; font-weight: 500;">Status</label>
|
||||
<select id="editStaffStatus" name="is_active" style="width: 100%; padding: 6px; border: 1px solid #ddd; border-radius: 4px;" required>
|
||||
<option value="true">Active</option>
|
||||
<option value="false">Inactive</option>
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
<div style="padding: 12px; border-top: 1px solid #EEEEEE; text-align: right; position: sticky; bottom: 0; background-color: white; border-bottom-left-radius: 8px; border-bottom-right-radius: 8px;">
|
||||
<button type="button" style="background-color: #9E9E9E; color: white; border: none; padding: 6px 12px; border-radius: 4px; cursor: pointer; font-size: 14px; margin-right: 8px;" onclick="closeEditStaffModal()">Cancel</button>
|
||||
<button type="button" style="background-color: #3F51B5; color: white; border: none; padding: 6px 12px; border-radius: 4px; cursor: pointer; font-size: 14px;" onclick="submitEditStaffForm()">Save Changes</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<script>
|
||||
function openEditStaffModal(staffId) {
|
||||
fetch(`/app/staff/${staffId}/`)
|
||||
.then(response => response.json())
|
||||
.then(data => {
|
||||
if (data.success) {
|
||||
const staff = data.staff;
|
||||
document.getElementById('editStaffId').value = staff.id;
|
||||
document.getElementById('editStaffProfilePicture').src = staff.image || "{% static 'img/user.png' %}";
|
||||
document.getElementById('editStaffName').value = staff.name;
|
||||
document.getElementById('editStaffEmail').value = staff.email;
|
||||
document.getElementById('editStaffPhone').value = staff.phone;
|
||||
document.getElementById('editStaffGender').value = staff.gender;
|
||||
document.getElementById('editStaffBirthDate').value = staff.birth_date;
|
||||
document.getElementById('editStaffMaritalStatus').value = staff.marital_status;
|
||||
document.getElementById('editStaffTempAddress').value = staff.temporary_address;
|
||||
document.getElementById('editStaffPermAddress').value = staff.permanent_address;
|
||||
document.getElementById('editStaffParentName').value = staff.parent_name;
|
||||
document.getElementById('editStaffParentPhone').value = staff.parent_phone;
|
||||
document.getElementById('editStaffCitizenshipNo').value = staff.citizenship_no;
|
||||
document.getElementById('editStaffPassport').value = staff.passport;
|
||||
document.getElementById('editStaffStatus').value = staff.is_active;
|
||||
|
||||
// Set selected subjects
|
||||
const subjectsSelect = document.getElementById('editStaffSubjects');
|
||||
if (staff.subjects && staff.subjects.length > 0) {
|
||||
Array.from(subjectsSelect.options).forEach(option => {
|
||||
option.selected = staff.subjects.some(subject => subject.id === parseInt(option.value));
|
||||
});
|
||||
}
|
||||
|
||||
document.getElementById('editStaffModal').style.display = 'block';
|
||||
document.body.style.overflow = 'hidden';
|
||||
} else {
|
||||
showMessageModal('Error', data.message);
|
||||
}
|
||||
})
|
||||
.catch(error => {
|
||||
showMessageModal('Error', 'Failed to fetch staff details.');
|
||||
});
|
||||
}
|
||||
|
||||
function closeEditStaffModal() {
|
||||
document.getElementById('editStaffModal').style.display = 'none';
|
||||
document.body.style.overflow = '';
|
||||
document.getElementById('editStaffForm').reset();
|
||||
}
|
||||
|
||||
function submitEditStaffForm() {
|
||||
const formData = new FormData(document.getElementById('editStaffForm'));
|
||||
const staffId = document.getElementById('editStaffId').value;
|
||||
|
||||
fetch(`/app/staff/edit/${staffId}/`, {
|
||||
method: 'POST',
|
||||
body: formData,
|
||||
headers: {
|
||||
'X-CSRFToken': document.querySelector('[name=csrfmiddlewaretoken]').value
|
||||
}
|
||||
})
|
||||
.then(response => response.json())
|
||||
.then(data => {
|
||||
if (data.success) {
|
||||
closeEditStaffModal();
|
||||
showMessageModal('Success', 'Staff details updated successfully.');
|
||||
// Refresh the staff list
|
||||
location.reload();
|
||||
} else {
|
||||
showMessageModal('Error', data.message);
|
||||
}
|
||||
})
|
||||
.catch(error => {
|
||||
showMessageModal('Error', 'Failed to update staff details.');
|
||||
});
|
||||
}
|
||||
|
||||
// Handle profile picture preview
|
||||
document.getElementById('editStaffProfilePictureInput').addEventListener('change', function(e) {
|
||||
const file = e.target.files[0];
|
||||
if (file) {
|
||||
const reader = new FileReader();
|
||||
reader.onload = function(e) {
|
||||
document.getElementById('editStaffProfilePicture').src = e.target.result;
|
||||
}
|
||||
reader.readAsDataURL(file);
|
||||
}
|
||||
});
|
||||
</script>
|
||||
@@ -1,206 +0,0 @@
|
||||
{% load static %}
|
||||
|
||||
<!-- Edit Student Modal -->
|
||||
<div id="editStudentModal" style="display: none; position: fixed; top: 0; left: 0; right: 0; bottom: 0; background-color: rgba(0,0,0,0.5); z-index: 10000;" onclick="if(event.target === this) closeEditStudentModal();">
|
||||
<div style="position: absolute; top: 50%; left: 50%; transform: translate(-50%, -50%); width: 90%; max-width: 800px; background-color: white; border-radius: 8px; box-shadow: 0 2px 10px rgba(0,0,0,0.2); max-height: 90vh; overflow-y: auto;">
|
||||
<div style="background-color: #3F51B5; color: white; padding: 12px; border-top-left-radius: 8px; border-top-right-radius: 8px; display: flex; justify-content: space-between; align-items: center; position: sticky; top: 0; z-index: 1;">
|
||||
<h5 style="margin: 0; font-size: 18px;">Edit Student</h5>
|
||||
<button style="background: none; border: none; color: white; font-size: 24px; cursor: pointer; line-height: 24px;" onclick="closeEditStudentModal()">×</button>
|
||||
</div>
|
||||
<div style="padding: 15px;">
|
||||
<!-- Profile Picture Preview -->
|
||||
<div style="text-align: center; margin-bottom: 20px;">
|
||||
<div style="position: relative; display: inline-block;">
|
||||
<img id="studentEditProfilePreview" src="{% static 'img/user.png' %}" alt="Student Image" style="width: 120px; height: 120px; object-fit: cover; border-radius: 50%; border: 3px solid #3F51B5;">
|
||||
<div style="position: absolute; bottom: 0; right: 0;">
|
||||
<button type="button" onclick="document.getElementById('edit_profile_picture').click()" style="width: 36px; height: 36px; border-radius: 50%; background-color: #3F51B5; color: white; border: none; font-size: 16px; cursor: pointer; display: flex; align-items: center; justify-content: center; box-shadow: 0 2px 5px rgba(0,0,0,0.2);">
|
||||
<i class="fas fa-camera"></i>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<form id="editStudentForm">
|
||||
{% csrf_token %}
|
||||
<input type="hidden" id="edit_student_id" name="student_id">
|
||||
<!-- Hidden file input controlled by the camera button -->
|
||||
<input type="file" id="edit_profile_picture" name="image" accept="image/*" style="display: none;" onchange="previewStudentImage(this)">
|
||||
|
||||
<div style="display: grid; grid-template-columns: 1fr 1fr; gap: 15px;">
|
||||
<!-- Left Column -->
|
||||
<div>
|
||||
<div style="margin-bottom: 15px;">
|
||||
<label style="display: block; margin-bottom: 6px; font-weight: 500; color: #3F51B5;">Name <span style="color: red;">*</span></label>
|
||||
<input type="text" id="edit_name" name="name" style="width: 100%; padding: 8px; border: 1px solid #ddd; border-radius: 4px;" required>
|
||||
</div>
|
||||
<div style="margin-bottom: 15px;">
|
||||
<label style="display: block; margin-bottom: 6px; font-weight: 500; color: #3F51B5;">Phone <span style="color: red;">*</span></label>
|
||||
<input type="tel" id="edit_phone" name="phone" style="width: 100%; padding: 8px; border: 1px solid #ddd; border-radius: 4px;" required pattern="[0-9]{10}" maxlength="10" title="Please enter a 10-digit phone number">
|
||||
</div>
|
||||
<div style="margin-bottom: 15px;">
|
||||
<label style="display: block; margin-bottom: 6px; font-weight: 500; color: #3F51B5;">Email</label>
|
||||
<input type="email" id="edit_email" name="email" style="width: 100%; padding: 8px; border: 1px solid #ddd; border-radius: 4px;">
|
||||
</div>
|
||||
<div style="margin-bottom: 15px;">
|
||||
<label style="display: block; margin-bottom: 6px; font-weight: 500; color: #3F51B5;">Gender</label>
|
||||
<select id="edit_gender" name="gender" style="width: 100%; padding: 8px; border: 1px solid #ddd; border-radius: 4px;">
|
||||
<option value="">Select Gender</option>
|
||||
<option value="M">Male</option>
|
||||
<option value="F">Female</option>
|
||||
<option value="O">Other</option>
|
||||
</select>
|
||||
</div>
|
||||
<div style="margin-bottom: 15px;">
|
||||
<label style="display: block; margin-bottom: 6px; font-weight: 500; color: #3F51B5;">Birth Date</label>
|
||||
<input type="date" id="edit_birth_date" name="birth_date" style="width: 100%; padding: 8px; border: 1px solid #ddd; border-radius: 4px;">
|
||||
</div>
|
||||
<div style="margin-bottom: 15px;">
|
||||
<label style="display: block; margin-bottom: 6px; font-weight: 500; color: #3F51B5;">Admission Date</label>
|
||||
<input type="date" id="edit_admission_date" name="admission_date" style="width: 100%; padding: 8px; border: 1px solid #ddd; border-radius: 4px;">
|
||||
</div>
|
||||
</div>
|
||||
<!-- Right Column -->
|
||||
<div>
|
||||
<div style="margin-bottom: 15px;">
|
||||
<label style="display: block; margin-bottom: 6px; font-weight: 500; color: #3F51B5;">Parent/Guardian Name</label>
|
||||
<input type="text" id="edit_parent_name" name="parent_name" style="width: 100%; padding: 8px; border: 1px solid #ddd; border-radius: 4px;">
|
||||
</div>
|
||||
<div style="margin-bottom: 15px;">
|
||||
<label style="display: block; margin-bottom: 6px; font-weight: 500; color: #3F51B5;">Parent/Guardian Phone</label>
|
||||
<input type="tel" id="edit_parent_phone" name="parent_phone" style="width: 100%; padding: 8px; border: 1px solid #ddd; border-radius: 4px;" pattern="[0-9]{10}" maxlength="10" title="Please enter a 10-digit phone number">
|
||||
</div>
|
||||
<div style="margin-bottom: 15px;">
|
||||
<label style="display: block; margin-bottom: 6px; font-weight: 500; color: #3F51B5;">Citizenship Number</label>
|
||||
<input type="text" id="edit_citizenship_no" name="citizenship_no" style="width: 100%; padding: 8px; border: 1px solid #ddd; border-radius: 4px;">
|
||||
</div>
|
||||
<div style="margin-bottom: 15px;">
|
||||
<label style="display: block; margin-bottom: 6px; font-weight: 500; color: #3F51B5;">Temporary Address</label>
|
||||
<textarea id="edit_temporary_address" name="temporary_address" style="width: 100%; padding: 8px; border: 1px solid #ddd; border-radius: 4px; min-height: 80px;"></textarea>
|
||||
</div>
|
||||
<div style="margin-bottom: 15px;">
|
||||
<label style="display: block; margin-bottom: 6px; font-weight: 500; color: #3F51B5;">Permanent Address</label>
|
||||
<textarea id="edit_permanent_address" name="permanent_address" style="width: 100%; padding: 8px; border: 1px solid #ddd; border-radius: 4px; min-height: 80px;"></textarea>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
<div style="padding: 12px; border-top: 1px solid #EEEEEE; text-align: right; position: sticky; bottom: 0; background-color: white; border-bottom-left-radius: 8px; border-bottom-right-radius: 8px;">
|
||||
<button type="button" style="background-color: #9E9E9E; color: white; border: none; padding: 8px 16px; border-radius: 4px; cursor: pointer; font-size: 14px; margin-right: 8px;" onclick="closeEditStudentModal()">Cancel</button>
|
||||
<button type="button" style="background-color: #3F51B5; color: white; border: none; padding: 8px 16px; border-radius: 4px; cursor: pointer; font-size: 14px;" onclick="submitEditStudentForm()">Save Changes</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<script>
|
||||
function previewStudentImage(input) {
|
||||
if (input.files && input.files[0]) {
|
||||
const reader = new FileReader();
|
||||
reader.onload = function(e) {
|
||||
document.getElementById('studentEditProfilePreview').src = e.target.result;
|
||||
}
|
||||
reader.readAsDataURL(input.files[0]);
|
||||
}
|
||||
}
|
||||
|
||||
function openEditStudentModal(studentId) {
|
||||
document.getElementById('editStudentModal').style.display = 'block';
|
||||
document.body.style.overflow = 'hidden';
|
||||
loadStudentData(studentId);
|
||||
}
|
||||
|
||||
function closeEditStudentModal() {
|
||||
document.getElementById('editStudentModal').style.display = 'none';
|
||||
document.body.style.overflow = '';
|
||||
document.getElementById('editStudentForm').reset();
|
||||
document.getElementById('studentEditProfilePreview').src = "{% static 'img/user.png' %}";
|
||||
}
|
||||
|
||||
function loadStudentData(studentId) {
|
||||
// Reset profile picture
|
||||
document.getElementById('studentEditProfilePreview').src = "{% static 'img/user.png' %}";
|
||||
|
||||
// Clear previous data
|
||||
document.getElementById('edit_student_id').value = '';
|
||||
document.getElementById('edit_name').value = '';
|
||||
document.getElementById('edit_email').value = '';
|
||||
document.getElementById('edit_phone').value = '';
|
||||
document.getElementById('edit_gender').value = '';
|
||||
document.getElementById('edit_birth_date').value = '';
|
||||
document.getElementById('edit_admission_date').value = '';
|
||||
document.getElementById('edit_temporary_address').value = '';
|
||||
document.getElementById('edit_permanent_address').value = '';
|
||||
document.getElementById('edit_parent_name').value = '';
|
||||
document.getElementById('edit_parent_phone').value = '';
|
||||
document.getElementById('edit_citizenship_no').value = '';
|
||||
|
||||
fetch(`/app/get-student/${studentId}/`)
|
||||
.then(response => {
|
||||
if (!response.ok) {
|
||||
throw new Error('Network response was not ok');
|
||||
}
|
||||
return response.json();
|
||||
})
|
||||
.then(data => {
|
||||
if (data.success) {
|
||||
const student = data.student;
|
||||
|
||||
// Set profile picture if available
|
||||
if (student.image) {
|
||||
document.getElementById('studentEditProfilePreview').src = student.image;
|
||||
}
|
||||
|
||||
// Set form values
|
||||
document.getElementById('edit_student_id').value = student.id;
|
||||
document.getElementById('edit_name').value = student.name || '';
|
||||
document.getElementById('edit_email').value = student.email || '';
|
||||
document.getElementById('edit_phone').value = student.phone || '';
|
||||
document.getElementById('edit_gender').value = student.gender || '';
|
||||
document.getElementById('edit_birth_date').value = student.birth_date || '';
|
||||
document.getElementById('edit_admission_date').value = student.joining_date || '';
|
||||
document.getElementById('edit_temporary_address').value = student.temporary_address || '';
|
||||
document.getElementById('edit_permanent_address').value = student.permanent_address || '';
|
||||
document.getElementById('edit_parent_name').value = student.parent_name || '';
|
||||
document.getElementById('edit_parent_phone').value = student.parent_phone || '';
|
||||
document.getElementById('edit_citizenship_no').value = student.citizenship_no || '';
|
||||
} else {
|
||||
showMessageModal('Error', data.message || 'Error loading student data');
|
||||
}
|
||||
})
|
||||
.catch(error => {
|
||||
showMessageModal('Error', 'An error occurred while loading student data');
|
||||
});
|
||||
}
|
||||
|
||||
function submitEditStudentForm() {
|
||||
const form = document.getElementById('editStudentForm');
|
||||
const formData = new FormData(form);
|
||||
const studentId = document.getElementById('edit_student_id').value;
|
||||
|
||||
fetch(`/app/edit-student/${studentId}/`, {
|
||||
method: 'POST',
|
||||
body: formData,
|
||||
headers: {
|
||||
'X-CSRFToken': document.querySelector('[name=csrfmiddlewaretoken]').value
|
||||
}
|
||||
})
|
||||
.then(response => {
|
||||
if (!response.ok) {
|
||||
throw new Error('Network response was not ok');
|
||||
}
|
||||
return response.json();
|
||||
})
|
||||
.then(data => {
|
||||
if (data.success) {
|
||||
showMessageModal('Success', 'Student updated successfully');
|
||||
closeEditStudentModal();
|
||||
location.reload();
|
||||
} else {
|
||||
showMessageModal('Error', data.message || 'Error updating student');
|
||||
}
|
||||
})
|
||||
.catch(error => {
|
||||
showMessageModal('Error', 'An error occurred while updating the student');
|
||||
});
|
||||
}
|
||||
</script>
|
||||
@@ -1,128 +0,0 @@
|
||||
{% load static %}
|
||||
{% load app_filters %}
|
||||
|
||||
<!-- Edit Subject Modal -->
|
||||
<div id="editSubjectModal" style="display: none; position: fixed; top: 0; left: 0; right: 0; bottom: 0; background-color: rgba(0,0,0,0.5); z-index: 10000;" onclick="if(event.target === this) closeEditSubjectModal();">
|
||||
<div style="position: absolute; top: 50%; left: 50%; transform: translate(-50%, -50%); width: 90%; max-width: 500px; background-color: white; border-radius: 8px; box-shadow: 0 2px 10px rgba(0,0,0,0.2);">
|
||||
<div style="background-color: #3F51B5; color: white; padding: 15px; border-top-left-radius: 8px; border-top-right-radius: 8px; display: flex; justify-content: space-between; align-items: center;">
|
||||
<h5 style="margin: 0; font-size: 18px;">Edit Subject</h5>
|
||||
<button style="background: none; border: none; color: white; font-size: 24px; cursor: pointer; line-height: 24px;" onclick="closeEditSubjectModal()">×</button>
|
||||
</div>
|
||||
<form id="editSubjectForm" method="post" enctype="multipart/form-data">
|
||||
{% csrf_token %}
|
||||
<input type="hidden" id="editSubjectId" name="subject_id">
|
||||
<div style="padding: 20px;">
|
||||
<div style="margin-bottom: 15px;">
|
||||
<label for="editSubjectName" style="display: block; margin-bottom: 8px; font-weight: 500;">Subject Name</label>
|
||||
<input type="text" id="editSubjectName" name="name" required style="width: 100%; padding: 10px; border: 1px solid #BDBDBD; border-radius: 4px; box-sizing: border-box;">
|
||||
</div>
|
||||
<div style="margin-bottom: 15px;">
|
||||
<label for="editSubjectCode" style="display: block; margin-bottom: 8px; font-weight: 500;">Subject Code</label>
|
||||
<input type="text" id="editSubjectCode" name="code" style="width: 100%; padding: 10px; border: 1px solid #BDBDBD; border-radius: 4px; box-sizing: border-box;">
|
||||
</div>
|
||||
<div style="margin-bottom: 15px;">
|
||||
<label for="editSubjectPeriod" style="display: block; margin-bottom: 8px; font-weight: 500;">Period/{{ course.duration_type }}</label>
|
||||
<input type="number" id="editSubjectPeriod" name="period_or_year" min="1" required style="width: 100%; padding: 10px; border: 1px solid #BDBDBD; border-radius: 4px; box-sizing: border-box;">
|
||||
<small id="periodHelp" style="color: #666; display: block; margin-top: 5px;"></small>
|
||||
</div>
|
||||
<div style="margin-bottom: 15px;">
|
||||
<label for="editSubjectSyllabus" style="display: block; margin-bottom: 8px; font-weight: 500;">Syllabus PDF</label>
|
||||
<input type="file" id="editSubjectSyllabus" name="syllabus_pdf" accept=".pdf" style="width: 100%; padding: 10px; border: 1px solid #BDBDBD; border-radius: 4px; box-sizing: border-box;">
|
||||
<small style="color: #666; display: block; margin-top: 5px;">Current file: <span id="currentSyllabus">No file uploaded</span></small>
|
||||
</div>
|
||||
</div>
|
||||
<div style="padding: 15px; border-top: 1px solid #EEEEEE; text-align: right; display: flex; flex-wrap: wrap; justify-content: flex-end;">
|
||||
<button type="button" style="background-color: #9E9E9E; color: white; border: none; padding: 8px 16px; border-radius: 4px; margin-right: 10px; margin-bottom: 10px; cursor: pointer; font-size: 14px;" onclick="closeEditSubjectModal()">Cancel</button>
|
||||
<button type="submit" style="background-color: #3F51B5; color: white; border: none; padding: 8px 16px; border-radius: 4px; cursor: pointer; font-size: 14px; margin-bottom: 10px;">Save Changes</button>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<script>
|
||||
function openEditSubjectModal(subjectId) {
|
||||
// Fetch subject details from the server
|
||||
fetch(`/app/subject/${subjectId}/view/`)
|
||||
.then(response => {
|
||||
if (!response.ok) {
|
||||
throw new Error('Network response was not ok');
|
||||
}
|
||||
return response.json();
|
||||
})
|
||||
.then(data => {
|
||||
if (data.success) {
|
||||
const subject = data.subject;
|
||||
// Populate the edit form with subject details
|
||||
document.getElementById('editSubjectId').value = subject.id;
|
||||
document.getElementById('editSubjectName').value = subject.name;
|
||||
document.getElementById('editSubjectCode').value = subject.code || '';
|
||||
document.getElementById('editSubjectPeriod').value = subject.period_or_year;
|
||||
|
||||
// Update period validation based on course duration type
|
||||
const periodInput = document.getElementById('editSubjectPeriod');
|
||||
const periodHelp = document.getElementById('periodHelp');
|
||||
const maxPeriods = subject.course.duration_type === 'Semester' ?
|
||||
subject.course.duration * 2 : subject.course.duration;
|
||||
|
||||
periodInput.max = maxPeriods;
|
||||
if (subject.course.duration_type === 'Semester') {
|
||||
periodHelp.textContent = `Enter semester number (1-${maxPeriods})`;
|
||||
} else {
|
||||
periodHelp.textContent = `Enter year number (1-${maxPeriods})`;
|
||||
}
|
||||
|
||||
// Update syllabus file info
|
||||
if (subject.syllabus_pdf) {
|
||||
const filename = subject.syllabus_pdf.split('/').pop();
|
||||
document.getElementById('currentSyllabus').innerHTML =
|
||||
`<a href="${subject.syllabus_pdf}" target="_blank" style="color: #3F51B5; text-decoration: none;">${filename}</a>`;
|
||||
} else {
|
||||
document.getElementById('currentSyllabus').textContent = 'No file uploaded';
|
||||
}
|
||||
|
||||
// Show the modal
|
||||
document.getElementById('editSubjectModal').style.display = 'block';
|
||||
} else {
|
||||
alert(data.message || 'Failed to fetch subject details');
|
||||
}
|
||||
})
|
||||
.catch(error => {
|
||||
console.error('Error fetching subject details:', error);
|
||||
alert('Failed to fetch subject details');
|
||||
});
|
||||
}
|
||||
|
||||
function closeEditSubjectModal() {
|
||||
document.getElementById('editSubjectModal').style.display = 'none';
|
||||
document.getElementById('editSubjectForm').reset();
|
||||
}
|
||||
|
||||
// Handle form submission
|
||||
document.getElementById('editSubjectForm').addEventListener('submit', function(e) {
|
||||
e.preventDefault();
|
||||
const formData = new FormData(this);
|
||||
const subjectId = document.getElementById('editSubjectId').value;
|
||||
|
||||
fetch(`/app/subject/${subjectId}/edit/`, {
|
||||
method: 'POST',
|
||||
body: formData,
|
||||
headers: {
|
||||
'X-CSRFToken': document.querySelector('[name=csrfmiddlewaretoken]').value
|
||||
}
|
||||
})
|
||||
.then(response => response.json())
|
||||
.then(data => {
|
||||
if (data.success) {
|
||||
closeEditSubjectModal();
|
||||
// Refresh the page to show updated data
|
||||
location.reload();
|
||||
} else {
|
||||
alert(data.message || 'Failed to update subject');
|
||||
}
|
||||
})
|
||||
.catch(error => {
|
||||
console.error('Error updating subject:', error);
|
||||
alert('Failed to update subject');
|
||||
});
|
||||
});
|
||||
</script>
|
||||
@@ -1,43 +0,0 @@
|
||||
<!-- Meeting Agenda Modal -->
|
||||
<div class="modal fade" id="meetingAgendaModal" tabindex="-1" aria-labelledby="meetingAgendaModalLabel" aria-hidden="true">
|
||||
<div class="modal-dialog">
|
||||
<div class="modal-content">
|
||||
<div class="modal-header">
|
||||
<h5 class="modal-title" id="meetingAgendaModalLabel">Meeting Agenda</h5>
|
||||
<button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Close"></button>
|
||||
</div>
|
||||
<div class="modal-body">
|
||||
<div id="meetingAgendaContent" class="p-3 bg-light rounded">
|
||||
<!-- Agenda content will be loaded here -->
|
||||
</div>
|
||||
</div>
|
||||
<div class="modal-footer">
|
||||
<button type="button" class="btn btn-secondary" data-bs-dismiss="modal">Close</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<script>
|
||||
function viewMeetingAgenda(meetingId) {
|
||||
fetch(`/api/meetings/${meetingId}/agenda/`)
|
||||
.then(response => {
|
||||
if (!response.ok) {
|
||||
throw new Error('Network response was not ok: ' + response.statusText);
|
||||
}
|
||||
return response.json();
|
||||
})
|
||||
.then(data => {
|
||||
document.getElementById('meetingAgendaContent').innerHTML = data.agenda || 'No agenda available';
|
||||
new bootstrap.Modal(document.getElementById('meetingAgendaModal')).show();
|
||||
})
|
||||
.catch(error => {
|
||||
console.error('Error fetching meeting agenda:', error);
|
||||
showToast('Failed to load meeting agenda', 'error');
|
||||
});
|
||||
}
|
||||
|
||||
function closeMeetingAgendaModal() {
|
||||
bootstrap.Modal.getInstance(document.getElementById('meetingAgendaModal')).hide();
|
||||
}
|
||||
</script>
|
||||
@@ -1,43 +0,0 @@
|
||||
<!-- Meeting Notes Modal -->
|
||||
<div class="modal fade" id="meetingNotesModal" tabindex="-1" aria-labelledby="meetingNotesModalLabel" aria-hidden="true">
|
||||
<div class="modal-dialog">
|
||||
<div class="modal-content">
|
||||
<div class="modal-header">
|
||||
<h5 class="modal-title" id="meetingNotesModalLabel">Meeting Notes</h5>
|
||||
<button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Close"></button>
|
||||
</div>
|
||||
<div class="modal-body">
|
||||
<div id="meetingNotesContent" class="p-3 bg-light rounded">
|
||||
<!-- Notes content will be loaded here -->
|
||||
</div>
|
||||
</div>
|
||||
<div class="modal-footer">
|
||||
<button type="button" class="btn btn-secondary" data-bs-dismiss="modal">Close</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<script>
|
||||
function viewMeetingNotes(meetingId) {
|
||||
fetch(`/api/meetings/${meetingId}/notes/`)
|
||||
.then(response => {
|
||||
if (!response.ok) {
|
||||
throw new Error('Network response was not ok: ' + response.statusText);
|
||||
}
|
||||
return response.json();
|
||||
})
|
||||
.then(data => {
|
||||
document.getElementById('meetingNotesContent').innerHTML = data.notes || 'No notes available';
|
||||
new bootstrap.Modal(document.getElementById('meetingNotesModal')).show();
|
||||
})
|
||||
.catch(error => {
|
||||
console.error('Error fetching meeting notes:', error);
|
||||
showToast('Failed to load meeting notes', 'error');
|
||||
});
|
||||
}
|
||||
|
||||
function closeMeetingNotesModal() {
|
||||
bootstrap.Modal.getInstance(document.getElementById('meetingNotesModal')).hide();
|
||||
}
|
||||
</script>
|
||||
@@ -1,167 +0,0 @@
|
||||
{% load static %}
|
||||
|
||||
<!-- Add Notice Modal -->
|
||||
<div id="addNoticeModal" style="display: none; position: fixed; top: 0; left: 0; right: 0; bottom: 0; background-color: rgba(0,0,0,0.5); z-index: 10000;" onclick="if(event.target === this) closeAddNoticeModal();">
|
||||
<div style="position: absolute; top: 50%; left: 50%; transform: translate(-50%, -50%); width: 90%; max-width: 600px; background-color: white; border-radius: 8px; box-shadow: 0 2px 10px rgba(0,0,0,0.2);">
|
||||
<div style="background-color: #3F51B5; color: white; padding: 15px; border-top-left-radius: 8px; border-top-right-radius: 8px; display: flex; justify-content: space-between; align-items: center;">
|
||||
<h5 style="margin: 0; font-size: 18px;">Add New Notice</h5>
|
||||
<button style="background: none; border: none; color: white; font-size: 24px; cursor: pointer; line-height: 24px;" onclick="closeAddNoticeModal()">×</button>
|
||||
</div>
|
||||
<div style="padding: 20px;">
|
||||
<form id="addNoticeForm" method="POST" action="{% url 'hod_add_notice' %}" enctype="multipart/form-data">
|
||||
{% csrf_token %}
|
||||
<div class="mb-3">
|
||||
<label for="noticeTitle" class="form-label">Notice Title</label>
|
||||
<input type="text" class="form-control" id="noticeTitle" name="title" required>
|
||||
</div>
|
||||
<div class="mb-3">
|
||||
<label for="noticeMessage" class="form-label">Message</label>
|
||||
<textarea class="form-control" id="noticeMessage" name="message" rows="5" required></textarea>
|
||||
</div>
|
||||
<div class="mb-3">
|
||||
<label for="noticeFile" class="form-label">Attachment (Optional)</label>
|
||||
<input type="file" class="form-control" id="noticeFile" name="file">
|
||||
<small class="form-text text-muted">Upload PDF, Word, or other relevant files (max 5MB)</small>
|
||||
</div>
|
||||
<div class="mb-3">
|
||||
<label for="noticeImage" class="form-label">Image (Optional)</label>
|
||||
<input type="file" class="form-control" id="noticeImage" name="image" accept="image/*">
|
||||
<small class="form-text text-muted">Upload an image if relevant to the notice</small>
|
||||
</div>
|
||||
<div class="d-flex justify-content-end gap-2">
|
||||
<button type="button" class="btn btn-secondary" onclick="closeAddNoticeModal()">Cancel</button>
|
||||
<button type="submit" class="btn btn-primary">Post Notice</button>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- View Notice Modal -->
|
||||
<div id="viewNoticeModal" style="display: none; position: fixed; top: 0; left: 0; right: 0; bottom: 0; background-color: rgba(0,0,0,0.5); z-index: 10000;" onclick="if(event.target === this) closeViewNoticeModal();">
|
||||
<div style="position: absolute; top: 50%; left: 50%; transform: translate(-50%, -50%); width: 90%; max-width: 600px; background-color: white; border-radius: 8px; box-shadow: 0 2px 10px rgba(0,0,0,0.2);">
|
||||
<div style="background-color: #3F51B5; color: white; padding: 15px; border-top-left-radius: 8px; border-top-right-radius: 8px; display: flex; justify-content: space-between; align-items: center;">
|
||||
<h5 id="viewNoticeTitle" style="margin: 0; font-size: 18px;">Notice Title</h5>
|
||||
<button style="background: none; border: none; color: white; font-size: 24px; cursor: pointer; line-height: 24px;" onclick="closeViewNoticeModal()">×</button>
|
||||
</div>
|
||||
<div style="padding: 20px;">
|
||||
<div id="viewNoticeImageContainer" style="text-align: center; margin-bottom: 15px; display: none;">
|
||||
<img id="viewNoticeImage" src="" alt="Notice Image" style="max-width: 100%; max-height: 300px; border-radius: 4px;">
|
||||
</div>
|
||||
<div class="mb-3">
|
||||
<h6 class="mb-2">Details</h6>
|
||||
<p id="viewNoticeMessage" style="white-space: pre-wrap;"></p>
|
||||
</div>
|
||||
|
||||
<div id="viewNoticeFileContainer" style="margin-top: 15px; display: none;">
|
||||
<h6 class="mb-2">Attachment</h6>
|
||||
<a id="viewNoticeFile" href="" target="_blank" class="btn btn-sm btn-outline-primary">
|
||||
<i class="fas fa-download me-1"></i> Download Attachment
|
||||
</a>
|
||||
</div>
|
||||
|
||||
<div class="mt-3 text-muted">
|
||||
<small>Posted: <span id="viewNoticeDate"></span></small>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Delete Notice Confirmation Modal -->
|
||||
<div id="deleteNoticeModal" style="display: none; position: fixed; top: 0; left: 0; right: 0; bottom: 0; background-color: rgba(0,0,0,0.5); z-index: 10000;" onclick="if(event.target === this) closeDeleteNoticeModal();">
|
||||
<div style="position: absolute; top: 50%; left: 50%; transform: translate(-50%, -50%); width: 90%; max-width: 400px; background-color: white; border-radius: 8px; box-shadow: 0 2px 10px rgba(0,0,0,0.2);">
|
||||
<div style="background-color: #3F51B5; color: white; padding: 15px; border-top-left-radius: 8px; border-top-right-radius: 8px; display: flex; justify-content: space-between; align-items: center;">
|
||||
<h5 style="margin: 0; font-size: 18px;">Delete Notice</h5>
|
||||
<button style="background: none; border: none; color: white; font-size: 24px; cursor: pointer; line-height: 24px;" onclick="closeDeleteNoticeModal()">×</button>
|
||||
</div>
|
||||
<div style="padding: 20px;">
|
||||
<p style="margin-bottom: 15px;">Are you sure you want to delete this notice? This action cannot be undone.</p>
|
||||
<form id="deleteNoticeForm" method="POST">
|
||||
{% csrf_token %}
|
||||
<input type="hidden" id="deleteNoticeId" name="notice_id">
|
||||
<div class="d-flex justify-content-end gap-2">
|
||||
<button type="button" class="btn btn-secondary" onclick="closeDeleteNoticeModal()">Cancel</button>
|
||||
<button type="submit" class="btn btn-danger">Delete</button>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<script>
|
||||
// Add Notice Modal Functions
|
||||
function openAddNoticeModal() {
|
||||
document.getElementById('addNoticeModal').style.display = 'block';
|
||||
document.body.style.overflow = 'hidden';
|
||||
}
|
||||
|
||||
function closeAddNoticeModal() {
|
||||
document.getElementById('addNoticeModal').style.display = 'none';
|
||||
document.body.style.overflow = '';
|
||||
document.getElementById('addNoticeForm').reset();
|
||||
}
|
||||
|
||||
// View Notice Modal Functions
|
||||
function viewNotice(noticeId) {
|
||||
// Fetch notice details from the server
|
||||
fetch(`/app/notice/${noticeId}/`)
|
||||
.then(response => response.json())
|
||||
.then(data => {
|
||||
if (data.success) {
|
||||
const notice = data.notice;
|
||||
|
||||
// Set notice details in the modal
|
||||
document.getElementById('viewNoticeTitle').textContent = notice.title;
|
||||
document.getElementById('viewNoticeMessage').textContent = notice.message;
|
||||
document.getElementById('viewNoticeDate').textContent = notice.created_at;
|
||||
|
||||
// Handle image if available
|
||||
const imageContainer = document.getElementById('viewNoticeImageContainer');
|
||||
const image = document.getElementById('viewNoticeImage');
|
||||
if (notice.image) {
|
||||
image.src = notice.image;
|
||||
imageContainer.style.display = 'block';
|
||||
} else {
|
||||
imageContainer.style.display = 'none';
|
||||
}
|
||||
|
||||
// Handle file attachment if available
|
||||
const fileContainer = document.getElementById('viewNoticeFileContainer');
|
||||
const fileLink = document.getElementById('viewNoticeFile');
|
||||
if (notice.file) {
|
||||
fileLink.href = notice.file;
|
||||
fileContainer.style.display = 'block';
|
||||
} else {
|
||||
fileContainer.style.display = 'none';
|
||||
}
|
||||
|
||||
// Show the modal
|
||||
document.getElementById('viewNoticeModal').style.display = 'block';
|
||||
document.body.style.overflow = 'hidden';
|
||||
} else {
|
||||
showMessageModal('Error', data.message || 'Failed to load notice details.');
|
||||
}
|
||||
})
|
||||
.catch(error => {
|
||||
showMessageModal('Error', 'An error occurred while loading notice details.');
|
||||
});
|
||||
}
|
||||
|
||||
function closeViewNoticeModal() {
|
||||
document.getElementById('viewNoticeModal').style.display = 'none';
|
||||
document.body.style.overflow = '';
|
||||
}
|
||||
|
||||
// Delete Notice Modal Functions
|
||||
function confirmDeleteNotice(noticeId) {
|
||||
document.getElementById('deleteNoticeId').value = noticeId;
|
||||
document.getElementById('deleteNoticeForm').action = `/app/delete-notice/${noticeId}/`;
|
||||
document.getElementById('deleteNoticeModal').style.display = 'block';
|
||||
document.body.style.overflow = 'hidden';
|
||||
}
|
||||
|
||||
function closeDeleteNoticeModal() {
|
||||
document.getElementById('deleteNoticeModal').style.display = 'none';
|
||||
document.body.style.overflow = '';
|
||||
}
|
||||
</script>
|
||||
@@ -1,125 +0,0 @@
|
||||
<!-- Add Routine Modal -->
|
||||
<div id="addRoutineModal" class="modal">
|
||||
<div class="modal-content">
|
||||
<div class="modal-header">
|
||||
<h5>Add Class Routine</h5>
|
||||
<span class="close" onclick="closeAddRoutineModal()">×</span>
|
||||
</div>
|
||||
<div class="modal-body">
|
||||
<form id="addRoutineForm">
|
||||
{% csrf_token %}
|
||||
|
||||
<div class="mb-3">
|
||||
<label for="subjectId" class="form-label">Subject</label>
|
||||
<select id="subjectId" name="subject_id" class="form-select" required>
|
||||
<option value="">Select Subject</option>
|
||||
{% for subject in subjects %}
|
||||
<option value="{{ subject.id }}">{{ subject.name }} ({% if course.duration_type == 'Semester' %}Semester{% else %}Year{% endif %} {{ subject.period_or_year }})</option>
|
||||
{% endfor %}
|
||||
</select>
|
||||
</div>
|
||||
|
||||
<div class="mb-3">
|
||||
<label for="teacherId" class="form-label">Teacher</label>
|
||||
<select id="teacherId" name="teacher_id" class="form-select" required>
|
||||
<option value="">Select Teacher</option>
|
||||
{% for teacher in staff %}
|
||||
<option value="{{ teacher.id }}">{{ teacher.name }}</option>
|
||||
{% endfor %}
|
||||
</select>
|
||||
</div>
|
||||
|
||||
<div class="row mb-3">
|
||||
<div class="col">
|
||||
<label for="startTime" class="form-label">Start Time</label>
|
||||
<input type="time" id="startTime" name="start_time" class="form-control" required>
|
||||
</div>
|
||||
<div class="col">
|
||||
<label for="endTime" class="form-label">End Time</label>
|
||||
<input type="time" id="endTime" name="end_time" class="form-control" required>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<input type="hidden" id="periodOrYear" name="period_or_year">
|
||||
</form>
|
||||
</div>
|
||||
<div class="modal-footer">
|
||||
<button type="button" class="modal-btn modal-btn-cancel" onclick="closeAddRoutineModal()">Cancel</button>
|
||||
<button type="button" class="modal-btn modal-btn-delete" onclick="submitAddRoutineForm()">Save</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Edit Routine Modal -->
|
||||
<div id="editRoutineModal" class="modal">
|
||||
<div class="modal-content">
|
||||
<div class="modal-header">
|
||||
<h5>Edit Class Routine</h5>
|
||||
<span class="close" onclick="closeEditRoutineModal()">×</span>
|
||||
</div>
|
||||
<div class="modal-body">
|
||||
<form id="editRoutineForm">
|
||||
{% csrf_token %}
|
||||
<input type="hidden" id="editRoutineId" name="routine_id">
|
||||
|
||||
<div class="mb-3">
|
||||
<label for="editSubjectId" class="form-label">Subject</label>
|
||||
<select id="editSubjectId" name="subject_id" class="form-select" required>
|
||||
<option value="">Select Subject</option>
|
||||
{% for subject in subjects %}
|
||||
<option value="{{ subject.id }}">{{ subject.name }} ({% if course.duration_type == 'Semester' %}Semester{% else %}Year{% endif %} {{ subject.period_or_year }})</option>
|
||||
{% endfor %}
|
||||
</select>
|
||||
</div>
|
||||
|
||||
<div class="mb-3">
|
||||
<label for="editTeacherId" class="form-label">Teacher</label>
|
||||
<select id="editTeacherId" name="teacher_id" class="form-select" required>
|
||||
<option value="">Select Teacher</option>
|
||||
{% for teacher in staff %}
|
||||
<option value="{{ teacher.id }}">{{ teacher.name }}</option>
|
||||
{% endfor %}
|
||||
</select>
|
||||
</div>
|
||||
|
||||
<div class="row mb-3">
|
||||
<div class="col">
|
||||
<label for="editStartTime" class="form-label">Start Time</label>
|
||||
<input type="time" id="editStartTime" name="start_time" class="form-control" required>
|
||||
</div>
|
||||
<div class="col">
|
||||
<label for="editEndTime" class="form-label">End Time</label>
|
||||
<input type="time" id="editEndTime" name="end_time" class="form-control" required>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<input type="hidden" id="editPeriodOrYear" name="period_or_year">
|
||||
</form>
|
||||
</div>
|
||||
<div class="modal-footer">
|
||||
<button type="button" class="modal-btn modal-btn-cancel" onclick="closeEditRoutineModal()">Cancel</button>
|
||||
<button type="button" class="modal-btn modal-btn-delete" onclick="submitEditRoutineForm()">Update</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Delete Routine Confirmation Modal -->
|
||||
<div id="deleteRoutineModal" class="modal">
|
||||
<div class="modal-content">
|
||||
<div class="modal-header">
|
||||
<h5>Confirm Delete</h5>
|
||||
<span class="close" onclick="closeDeleteRoutineModal()">×</span>
|
||||
</div>
|
||||
<div class="modal-body">
|
||||
<p>Are you sure you want to delete this routine? This action cannot be undone.</p>
|
||||
<form id="deleteRoutineForm">
|
||||
{% csrf_token %}
|
||||
<input type="hidden" id="deleteRoutineId" name="routine_id">
|
||||
</form>
|
||||
</div>
|
||||
<div class="modal-footer">
|
||||
<button type="button" class="modal-btn modal-btn-cancel" onclick="closeDeleteRoutineModal()">Cancel</button>
|
||||
<button type="button" class="modal-btn modal-btn-delete" onclick="submitDeleteRoutineForm()">Delete</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@@ -1,74 +0,0 @@
|
||||
{% load static %}
|
||||
|
||||
<!-- View Meeting Modal -->
|
||||
<div id="viewMeetingModal" style="display: none; position: fixed; top: 0; left: 0; right: 0; bottom: 0; background-color: rgba(0,0,0,0.5); z-index: 10000;" onclick="if(event.target === this) closeViewMeetingModal();">
|
||||
<div style="position: absolute; top: 50%; left: 50%; transform: translate(-50%, -50%); width: 90%; max-width: 600px; background-color: white; border-radius: 8px; box-shadow: 0 2px 10px rgba(0,0,0,0.2); max-height: 90vh; overflow-y: auto;">
|
||||
<div style="background-color: #3F51B5; color: white; padding: 12px; border-top-left-radius: 8px; border-top-right-radius: 8px; display: flex; justify-content: space-between; align-items: center; position: sticky; top: 0; z-index: 1;">
|
||||
<h5 style="margin: 0; font-size: 18px;">Meeting Details</h5>
|
||||
<button style="background: none; border: none; color: white; font-size: 24px; cursor: pointer; line-height: 24px;" onclick="closeViewMeetingModal()">×</button>
|
||||
</div>
|
||||
<div style="padding: 15px;">
|
||||
<div style="display: grid; grid-template-columns: 1fr 1fr; gap: 15px;">
|
||||
<!-- Left Column -->
|
||||
<div>
|
||||
<div style="margin-bottom: 12px;">
|
||||
<label style="display: block; margin-bottom: 4px; font-weight: 500;">Date</label>
|
||||
<p id="viewMeetingDate" style="margin: 0; padding: 6px; background-color: #f8f9fa; border-radius: 4px;"></p>
|
||||
</div>
|
||||
<div style="margin-bottom: 12px;">
|
||||
<label style="display: block; margin-bottom: 4px; font-weight: 500;">Time</label>
|
||||
<p id="viewMeetingTime" style="margin: 0; padding: 6px; background-color: #f8f9fa; border-radius: 4px;"></p>
|
||||
</div>
|
||||
<div style="margin-bottom: 12px;">
|
||||
<label style="display: block; margin-bottom: 4px; font-weight: 500;">Duration</label>
|
||||
<p style="margin: 0; padding: 6px; background-color: #f8f9fa; border-radius: 4px;"><span id="viewMeetingDuration"></span> minutes</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Right Column -->
|
||||
<div>
|
||||
<div style="margin-bottom: 12px;">
|
||||
<label style="display: block; margin-bottom: 4px; font-weight: 500;">Status</label>
|
||||
<p style="margin: 0; padding: 6px; background-color: #f8f9fa; border-radius: 4px;"><span id="viewMeetingStatus"></span></p>
|
||||
</div>
|
||||
<div style="margin-bottom: 12px;">
|
||||
<label style="display: block; margin-bottom: 4px; font-weight: 500;">Meeting Type</label>
|
||||
<p id="viewMeetingType" style="margin: 0; padding: 6px; background-color: #f8f9fa; border-radius: 4px;"></p>
|
||||
</div>
|
||||
<div id="viewMeetingLinkContainer" style="margin-bottom: 12px; display: none;">
|
||||
<label style="display: block; margin-bottom: 4px; font-weight: 500;">Meeting Link</label>
|
||||
<p id="viewMeetingLink" style="margin: 0; padding: 6px; background-color: #f8f9fa; border-radius: 4px;"></p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Full Width Sections -->
|
||||
<div style="margin-top: 15px;">
|
||||
<div style="margin-bottom: 12px;">
|
||||
<label style="display: block; margin-bottom: 4px; font-weight: 500;">Agenda</label>
|
||||
<p id="viewMeetingAgenda" style="margin: 0; padding: 6px; background-color: #f8f9fa; border-radius: 4px; min-height: 60px;"></p>
|
||||
</div>
|
||||
<div style="margin-bottom: 12px;">
|
||||
<label style="display: block; margin-bottom: 4px; font-weight: 500;">Notes</label>
|
||||
<p id="viewMeetingNotes" style="margin: 0; padding: 6px; background-color: #f8f9fa; border-radius: 4px; min-height: 60px;"></p>
|
||||
</div>
|
||||
<div id="viewCancellationReasonContainer" style="margin-bottom: 12px; display: none;">
|
||||
<label style="display: block; margin-bottom: 4px; font-weight: 500; color: #dc3545;">Cancellation Reason</label>
|
||||
<p id="viewCancellationReason" style="margin: 0; padding: 6px; background-color: #fff8f8; border-radius: 4px; border-left: 3px solid #dc3545;"></p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div style="padding: 12px; border-top: 1px solid #EEEEEE; text-align: right; position: sticky; bottom: 0; background-color: white; border-bottom-left-radius: 8px; border-bottom-right-radius: 8px;">
|
||||
<button type="button" style="background-color: #9E9E9E; color: white; border: none; padding: 6px 12px; border-radius: 4px; cursor: pointer; font-size: 14px; margin-right: 8px;" onclick="closeViewMeetingModal()">Close</button>
|
||||
<button type="button" id="editMeetingBtn" style="background-color: #3F51B5; color: white; border: none; padding: 6px 12px; border-radius: 4px; cursor: pointer; font-size: 14px;">Edit Meeting</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<script>
|
||||
function closeViewMeetingModal() {
|
||||
document.getElementById('viewMeetingModal').style.display = 'none';
|
||||
document.body.style.overflow = '';
|
||||
}
|
||||
</script>
|
||||
@@ -1,165 +0,0 @@
|
||||
{% load static %}
|
||||
|
||||
<div id="viewStaffModal" style="display: none; position: fixed; top: 0; left: 0; right: 0; bottom: 0; background-color: rgba(0,0,0,0.5); z-index: 10000;" onclick="if(event.target === this) closeViewStaffModal();">
|
||||
<div style="position: absolute; top: 50%; left: 50%; transform: translate(-50%, -50%); width: 90%; max-width: 800px; background-color: white; border-radius: 8px; box-shadow: 0 2px 10px rgba(0,0,0,0.2); max-height: 90vh; overflow-y: auto;">
|
||||
<div style="background-color: #3F51B5; color: white; padding: 12px; border-top-left-radius: 8px; border-top-right-radius: 8px; display: flex; justify-content: space-between; align-items: center; position: sticky; top: 0; z-index: 1;">
|
||||
<h5 style="margin: 0; font-size: 18px;">Staff Details</h5>
|
||||
<button style="background: none; border: none; color: white; font-size: 24px; cursor: pointer; line-height: 24px;" onclick="closeViewStaffModal()">×</button>
|
||||
</div>
|
||||
<div style="padding: 15px;">
|
||||
<div style="display: grid; grid-template-columns: 1fr 1fr; gap: 15px;">
|
||||
<!-- Left Column -->
|
||||
<div>
|
||||
<div style="text-align: center; margin-bottom: 20px;">
|
||||
<img id="staffProfilePicture" src="{% static 'img/user.png' %}" alt="Staff Image" style="width: 120px; height: 120px; object-fit: cover; border-radius: 50%; border: 3px solid #3F51B5;">
|
||||
</div>
|
||||
<div style="margin-bottom: 12px;">
|
||||
<label style="display: block; margin-bottom: 4px; font-weight: 500;">Name</label>
|
||||
<p id="staffName" style="margin: 0; padding: 6px; background-color: #f8f9fa; border-radius: 4px;"></p>
|
||||
</div>
|
||||
<div style="margin-bottom: 12px;">
|
||||
<label style="display: block; margin-bottom: 4px; font-weight: 500;">Designation</label>
|
||||
<p id="staffDesignation" style="margin: 0; padding: 6px; background-color: #f8f9fa; border-radius: 4px;"></p>
|
||||
</div>
|
||||
<div style="margin-bottom: 12px;">
|
||||
<label style="display: block; margin-bottom: 4px; font-weight: 500;">Email</label>
|
||||
<p id="staffEmail" style="margin: 0; padding: 6px; background-color: #f8f9fa; border-radius: 4px;"></p>
|
||||
</div>
|
||||
<div style="margin-bottom: 12px;">
|
||||
<label style="display: block; margin-bottom: 4px; font-weight: 500;">Phone</label>
|
||||
<p id="staffPhone" style="margin: 0; padding: 6px; background-color: #f8f9fa; border-radius: 4px;"></p>
|
||||
</div>
|
||||
<div style="margin-bottom: 12px;">
|
||||
<label style="display: block; margin-bottom: 4px; font-weight: 500;">Gender</label>
|
||||
<p id="staffGender" style="margin: 0; padding: 6px; background-color: #f8f9fa; border-radius: 4px;"></p>
|
||||
</div>
|
||||
<div style="margin-bottom: 12px;">
|
||||
<label style="display: block; margin-bottom: 4px; font-weight: 500;">Birth Date</label>
|
||||
<p id="staffBirthDate" style="margin: 0; padding: 6px; background-color: #f8f9fa; border-radius: 4px;"></p>
|
||||
</div>
|
||||
<div style="margin-bottom: 12px;">
|
||||
<label style="display: block; margin-bottom: 4px; font-weight: 500;">Marital Status</label>
|
||||
<p id="staffMaritalStatus" style="margin: 0; padding: 6px; background-color: #f8f9fa; border-radius: 4px;"></p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Right Column -->
|
||||
<div>
|
||||
<div style="margin-bottom: 12px;">
|
||||
<label style="display: block; margin-bottom: 4px; font-weight: 500;">Joining Date</label>
|
||||
<p id="staffJoiningDate" style="margin: 0; padding: 6px; background-color: #f8f9fa; border-radius: 4px;"></p>
|
||||
</div>
|
||||
<div style="margin-bottom: 12px;">
|
||||
<label style="display: block; margin-bottom: 4px; font-weight: 500;">Status</label>
|
||||
<p id="staffStatus" style="margin: 0; padding: 6px; background-color: #f8f9fa; border-radius: 4px;"></p>
|
||||
</div>
|
||||
<div style="margin-bottom: 12px;">
|
||||
<label style="display: block; margin-bottom: 4px; font-weight: 500;">Parent Name</label>
|
||||
<p id="staffParentName" style="margin: 0; padding: 6px; background-color: #f8f9fa; border-radius: 4px;"></p>
|
||||
</div>
|
||||
<div style="margin-bottom: 12px;">
|
||||
<label style="display: block; margin-bottom: 4px; font-weight: 500;">Parent Phone</label>
|
||||
<p id="staffParentPhone" style="margin: 0; padding: 6px; background-color: #f8f9fa; border-radius: 4px;"></p>
|
||||
</div>
|
||||
<div style="margin-bottom: 12px;">
|
||||
<label style="display: block; margin-bottom: 4px; font-weight: 500;">Citizenship No.</label>
|
||||
<p id="staffCitizenshipNo" style="margin: 0; padding: 6px; background-color: #f8f9fa; border-radius: 4px;"></p>
|
||||
</div>
|
||||
<div style="margin-bottom: 12px;">
|
||||
<label style="display: block; margin-bottom: 4px; font-weight: 500;">Passport</label>
|
||||
<p id="staffPassport" style="margin: 0; padding: 6px; background-color: #f8f9fa; border-radius: 4px;"></p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div style="margin-top: 15px;">
|
||||
<div style="margin-bottom: 12px;">
|
||||
<label style="display: block; margin-bottom: 4px; font-weight: 500;">Temporary Address</label>
|
||||
<p id="staffTemporaryAddress" style="margin: 0; padding: 6px; background-color: #f8f9fa; border-radius: 4px;"></p>
|
||||
</div>
|
||||
<div style="margin-bottom: 12px;">
|
||||
<label style="display: block; margin-bottom: 4px; font-weight: 500;">Permanent Address</label>
|
||||
<p id="staffPermanentAddress" style="margin: 0; padding: 6px; background-color: #f8f9fa; border-radius: 4px;"></p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div style="margin-top: 15px;">
|
||||
<label style="display: block; margin-bottom: 4px; font-weight: 500;">Assigned Subjects</label>
|
||||
<div id="staffSubjects" style="display: flex; flex-wrap: wrap; gap: 8px; padding: 6px; background-color: #f8f9fa; border-radius: 4px; min-height: 40px;"></div>
|
||||
</div>
|
||||
</div>
|
||||
<div style="padding: 12px; border-top: 1px solid #EEEEEE; text-align: right; position: sticky; bottom: 0; background-color: white; border-bottom-left-radius: 8px; border-bottom-right-radius: 8px;">
|
||||
<button type="button" style="background-color: #3F51B5; color: white; border: none; padding: 6px 12px; border-radius: 4px; cursor: pointer; font-size: 14px;" onclick="closeViewStaffModal()">Close</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<script>
|
||||
function openViewStaffModal(staffId) {
|
||||
fetch(`/app/staff/${staffId}/`)
|
||||
.then(response => {
|
||||
if (!response.ok) {
|
||||
throw new Error('Network response was not ok');
|
||||
}
|
||||
return response.json();
|
||||
})
|
||||
.then(data => {
|
||||
if (data.success) {
|
||||
const staff = data.staff;
|
||||
const profilePicture = document.getElementById('staffProfilePicture');
|
||||
profilePicture.src = staff.image || "{% static 'img/user.png' %}";
|
||||
|
||||
// Basic Information
|
||||
document.getElementById('staffName').textContent = staff.name || '-';
|
||||
document.getElementById('staffDesignation').textContent = staff.designation || '-';
|
||||
document.getElementById('staffEmail').textContent = staff.email || '-';
|
||||
document.getElementById('staffPhone').textContent = staff.phone || '-';
|
||||
document.getElementById('staffGender').textContent = staff.gender || '-';
|
||||
document.getElementById('staffBirthDate').textContent = staff.birth_date || '-';
|
||||
document.getElementById('staffMaritalStatus').textContent = staff.marital_status || '-';
|
||||
document.getElementById('staffJoiningDate').textContent = staff.joining_date || '-';
|
||||
document.getElementById('staffStatus').textContent = staff.is_active ? 'Active' : 'Inactive';
|
||||
|
||||
// Parent Details
|
||||
document.getElementById('staffParentName').textContent = staff.parent_name || '-';
|
||||
document.getElementById('staffParentPhone').textContent = staff.parent_phone || '-';
|
||||
|
||||
// Documents
|
||||
document.getElementById('staffCitizenshipNo').textContent = staff.citizenship_no || '-';
|
||||
document.getElementById('staffPassport').textContent = staff.passport || '-';
|
||||
|
||||
// Address Information
|
||||
document.getElementById('staffTemporaryAddress').textContent = staff.temporary_address || '-';
|
||||
document.getElementById('staffPermanentAddress').textContent = staff.permanent_address || '-';
|
||||
|
||||
// Assigned Subjects
|
||||
const subjectsContainer = document.getElementById('staffSubjects');
|
||||
subjectsContainer.innerHTML = '';
|
||||
if (staff.subjects && staff.subjects.length > 0) {
|
||||
staff.subjects.forEach(subject => {
|
||||
const badge = document.createElement('span');
|
||||
badge.style.cssText = 'background-color: #3F51B5; color: white; padding: 4px 8px; border-radius: 4px; font-size: 12px;';
|
||||
badge.textContent = subject.name;
|
||||
subjectsContainer.appendChild(badge);
|
||||
});
|
||||
} else {
|
||||
subjectsContainer.innerHTML = '<span style="color: #666;">No subjects assigned</span>';
|
||||
}
|
||||
|
||||
document.getElementById('viewStaffModal').style.display = 'block';
|
||||
document.body.style.overflow = 'hidden';
|
||||
} else {
|
||||
showMessageModal('Error', data.message);
|
||||
}
|
||||
})
|
||||
.catch(error => {
|
||||
console.error('Error:', error);
|
||||
showMessageModal('Error', 'Failed to fetch staff details.');
|
||||
});
|
||||
}
|
||||
|
||||
function closeViewStaffModal() {
|
||||
document.getElementById('viewStaffModal').style.display = 'none';
|
||||
document.body.style.overflow = '';
|
||||
}
|
||||
</script>
|
||||
@@ -1,168 +0,0 @@
|
||||
{% load static %}
|
||||
|
||||
<!-- View Student Modal -->
|
||||
<div id="viewStudentModal" style="display: none; position: fixed; top: 0; left: 0; right: 0; bottom: 0; background-color: rgba(0,0,0,0.5); z-index: 10000;" onclick="if(event.target === this) closeViewStudentModal();">
|
||||
<div style="position: absolute; top: 50%; left: 50%; transform: translate(-50%, -50%); width: 90%; max-width: 800px; background-color: white; border-radius: 8px; box-shadow: 0 2px 10px rgba(0,0,0,0.2); max-height: 90vh; overflow-y: auto;">
|
||||
<div style="background-color: #3F51B5; color: white; padding: 12px; border-top-left-radius: 8px; border-top-right-radius: 8px; display: flex; justify-content: space-between; align-items: center; position: sticky; top: 0; z-index: 1;">
|
||||
<h5 style="margin: 0; font-size: 18px;">Student Details</h5>
|
||||
<button style="background: none; border: none; color: white; font-size: 24px; cursor: pointer; line-height: 24px;" onclick="closeViewStudentModal()">×</button>
|
||||
</div>
|
||||
<div style="padding: 15px;">
|
||||
<div style="display: grid; grid-template-columns: 1fr 1fr; gap: 15px;">
|
||||
<!-- Left Column -->
|
||||
<div>
|
||||
<div style="text-align: center; margin-bottom: 20px;">
|
||||
<img id="studentProfilePicture" src="{% static 'img/user.png' %}" alt="Student Image" style="width: 120px; height: 120px; object-fit: cover; border-radius: 50%; border: 3px solid #3F51B5;">
|
||||
</div>
|
||||
<div style="margin-bottom: 12px;">
|
||||
<label style="display: block; margin-bottom: 4px; font-weight: 500;">Name</label>
|
||||
<p id="view_name" style="margin: 0; padding: 6px; background-color: #f8f9fa; border-radius: 4px;"></p>
|
||||
</div>
|
||||
<div style="margin-bottom: 12px;">
|
||||
<label style="display: block; margin-bottom: 4px; font-weight: 500;">Email</label>
|
||||
<p id="view_email" style="margin: 0; padding: 6px; background-color: #f8f9fa; border-radius: 4px;"></p>
|
||||
</div>
|
||||
<div style="margin-bottom: 12px;">
|
||||
<label style="display: block; margin-bottom: 4px; font-weight: 500;">Phone</label>
|
||||
<p id="view_phone" style="margin: 0; padding: 6px; background-color: #f8f9fa; border-radius: 4px;"></p>
|
||||
</div>
|
||||
<div style="margin-bottom: 12px;">
|
||||
<label style="display: block; margin-bottom: 4px; font-weight: 500;">Gender</label>
|
||||
<p id="view_gender" style="margin: 0; padding: 6px; background-color: #f8f9fa; border-radius: 4px;"></p>
|
||||
</div>
|
||||
<div style="margin-bottom: 12px;">
|
||||
<label style="display: block; margin-bottom: 4px; font-weight: 500;">Date of Birth</label>
|
||||
<p id="view_birth_date" style="margin: 0; padding: 6px; background-color: #f8f9fa; border-radius: 4px;"></p>
|
||||
</div>
|
||||
<div style="margin-bottom: 12px;">
|
||||
<label style="display: block; margin-bottom: 4px; font-weight: 500;">Citizenship Number</label>
|
||||
<p id="view_citizenship_no" style="margin: 0; padding: 6px; background-color: #f8f9fa; border-radius: 4px;"></p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Right Column -->
|
||||
<div>
|
||||
<div style="margin-bottom: 12px;">
|
||||
<label style="display: block; margin-bottom: 4px; font-weight: 500;">Course</label>
|
||||
<p id="view_course" style="margin: 0; padding: 6px; background-color: #f8f9fa; border-radius: 4px;"></p>
|
||||
</div>
|
||||
<div style="margin-bottom: 12px;">
|
||||
<label style="display: block; margin-bottom: 4px; font-weight: 500;">Current Period</label>
|
||||
<p id="view_current_period" style="margin: 0; padding: 6px; background-color: #f8f9fa; border-radius: 4px;"></p>
|
||||
</div>
|
||||
<div style="margin-bottom: 12px;">
|
||||
<label style="display: block; margin-bottom: 4px; font-weight: 500;">Admission Date</label>
|
||||
<p id="view_admission_date" style="margin: 0; padding: 6px; background-color: #f8f9fa; border-radius: 4px;"></p>
|
||||
</div>
|
||||
<div style="margin-bottom: 12px;">
|
||||
<label style="display: block; margin-bottom: 4px; font-weight: 500;">Status</label>
|
||||
<p id="view_status" style="margin: 0; padding: 6px; background-color: #f8f9fa; border-radius: 4px;"></p>
|
||||
</div>
|
||||
<div style="margin-bottom: 12px;">
|
||||
<label style="display: block; margin-bottom: 4px; font-weight: 500;">Parent Name</label>
|
||||
<p id="view_parent_name" style="margin: 0; padding: 6px; background-color: #f8f9fa; border-radius: 4px;"></p>
|
||||
</div>
|
||||
<div style="margin-bottom: 12px;">
|
||||
<label style="display: block; margin-bottom: 4px; font-weight: 500;">Parent Phone</label>
|
||||
<p id="view_parent_phone" style="margin: 0; padding: 6px; background-color: #f8f9fa; border-radius: 4px;"></p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div style="margin-top: 15px;">
|
||||
<div style="margin-bottom: 12px;">
|
||||
<label style="display: block; margin-bottom: 4px; font-weight: 500;">Temporary Address</label>
|
||||
<p id="view_temporary_address" style="margin: 0; padding: 6px; background-color: #f8f9fa; border-radius: 4px;"></p>
|
||||
</div>
|
||||
<div style="margin-bottom: 12px;">
|
||||
<label style="display: block; margin-bottom: 4px; font-weight: 500;">Permanent Address</label>
|
||||
<p id="view_permanent_address" style="margin: 0; padding: 6px; background-color: #f8f9fa; border-radius: 4px;"></p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div style="padding: 12px; border-top: 1px solid #EEEEEE; text-align: right; position: sticky; bottom: 0; background-color: white; border-bottom-left-radius: 8px; border-bottom-right-radius: 8px;">
|
||||
<button type="button" style="background-color: #3F51B5; color: white; border: none; padding: 6px 12px; border-radius: 4px; cursor: pointer; font-size: 14px;" onclick="closeViewStudentModal()">Close</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<script>
|
||||
function openViewStudentModal(studentId) {
|
||||
document.getElementById('viewStudentModal').style.display = 'block';
|
||||
document.body.style.overflow = 'hidden';
|
||||
loadStudentDataForView(studentId);
|
||||
}
|
||||
|
||||
function closeViewStudentModal() {
|
||||
document.getElementById('viewStudentModal').style.display = 'none';
|
||||
document.body.style.overflow = '';
|
||||
}
|
||||
|
||||
function loadStudentDataForView(studentId) {
|
||||
// Reset profile picture
|
||||
document.getElementById('studentProfilePicture').src = "{% static 'img/user.png' %}";
|
||||
|
||||
// Clear previous data
|
||||
document.getElementById('view_name').textContent = '';
|
||||
document.getElementById('view_email').textContent = '';
|
||||
document.getElementById('view_phone').textContent = '';
|
||||
document.getElementById('view_gender').textContent = '';
|
||||
document.getElementById('view_birth_date').textContent = '';
|
||||
document.getElementById('view_admission_date').textContent = '';
|
||||
document.getElementById('view_temporary_address').textContent = '';
|
||||
document.getElementById('view_permanent_address').textContent = '';
|
||||
document.getElementById('view_parent_name').textContent = '';
|
||||
document.getElementById('view_parent_phone').textContent = '';
|
||||
document.getElementById('view_citizenship_no').textContent = '';
|
||||
document.getElementById('view_course').textContent = '';
|
||||
document.getElementById('view_current_period').textContent = '';
|
||||
document.getElementById('view_status').textContent = '';
|
||||
|
||||
fetch(`/app/get-student/${studentId}/`)
|
||||
.then(response => {
|
||||
if (!response.ok) {
|
||||
throw new Error('Network response was not ok');
|
||||
}
|
||||
return response.json();
|
||||
})
|
||||
.then(data => {
|
||||
if (data.success) {
|
||||
const student = data.student;
|
||||
|
||||
// Set profile picture
|
||||
if (student.image) {
|
||||
document.getElementById('studentProfilePicture').src = student.image;
|
||||
}
|
||||
|
||||
// Basic information
|
||||
document.getElementById('view_name').textContent = student.name || 'N/A';
|
||||
document.getElementById('view_email').textContent = student.email || 'N/A';
|
||||
document.getElementById('view_phone').textContent = student.phone || 'N/A';
|
||||
document.getElementById('view_gender').textContent = student.gender === 'M' ? 'Male' : (student.gender === 'F' ? 'Female' : 'Other');
|
||||
|
||||
// Academic information
|
||||
document.getElementById('view_course').textContent = student.course_name || 'N/A';
|
||||
document.getElementById('view_current_period').textContent = student.current_period || 'N/A';
|
||||
document.getElementById('view_status').textContent = student.is_active ? 'Active' : 'Inactive';
|
||||
|
||||
// Personal information
|
||||
document.getElementById('view_birth_date').textContent = student.birth_date || 'N/A';
|
||||
document.getElementById('view_admission_date').textContent = student.joining_date || 'N/A';
|
||||
document.getElementById('view_citizenship_no').textContent = student.citizenship_no || 'N/A';
|
||||
|
||||
// Address information
|
||||
document.getElementById('view_temporary_address').textContent = student.temporary_address || 'N/A';
|
||||
document.getElementById('view_permanent_address').textContent = student.permanent_address || 'N/A';
|
||||
|
||||
// Parent information
|
||||
document.getElementById('view_parent_name').textContent = student.parent_name || 'N/A';
|
||||
document.getElementById('view_parent_phone').textContent = student.parent_phone || 'N/A';
|
||||
} else {
|
||||
showMessageModal('Error', data.message || 'Error loading student data');
|
||||
}
|
||||
})
|
||||
.catch(error => {
|
||||
showMessageModal('Error', 'An error occurred while loading student data');
|
||||
});
|
||||
}
|
||||
</script>
|
||||
@@ -1,83 +0,0 @@
|
||||
<!-- View Subject Modal -->
|
||||
<div id="viewSubjectModal" style="display: none; position: fixed; top: 0; left: 0; right: 0; bottom: 0; background-color: rgba(0,0,0,0.5); z-index: 10000;" onclick="if(event.target === this) closeViewSubjectModal();">
|
||||
<div style="position: absolute; top: 50%; left: 50%; transform: translate(-50%, -50%); width: 90%; max-width: 500px; background-color: white; border-radius: 8px; box-shadow: 0 2px 10px rgba(0,0,0,0.2);">
|
||||
<div style="background-color: #3F51B5; color: white; padding: 15px; border-top-left-radius: 8px; border-top-right-radius: 8px; display: flex; justify-content: space-between; align-items: center;">
|
||||
<h5 style="margin: 0; font-size: 18px;">Subject Details</h5>
|
||||
<button style="background: none; border: none; color: white; font-size: 24px; cursor: pointer; line-height: 24px;" onclick="closeViewSubjectModal()">×</button>
|
||||
</div>
|
||||
<div style="padding: 20px;">
|
||||
<div style="margin-bottom: 15px;">
|
||||
<label style="font-weight: 500; display: block; margin-bottom: 5px;">Subject Name</label>
|
||||
<p id="viewSubjectName" style="margin: 0; padding: 8px; background: #f5f5f5; border-radius: 4px;"></p>
|
||||
</div>
|
||||
<div style="margin-bottom: 15px;">
|
||||
<label style="font-weight: 500; display: block; margin-bottom: 5px;">Subject Code</label>
|
||||
<p id="viewSubjectCode" style="margin: 0; padding: 8px; background: #f5f5f5; border-radius: 4px;"></p>
|
||||
</div>
|
||||
<div style="margin-bottom: 15px;">
|
||||
<label style="font-weight: 500; display: block; margin-bottom: 5px;">Course</label>
|
||||
<p id="viewSubjectCourse" style="margin: 0; padding: 8px; background: #f5f5f5; border-radius: 4px;"></p>
|
||||
</div>
|
||||
<div style="margin-bottom: 15px;">
|
||||
<label style="font-weight: 500; display: block; margin-bottom: 5px;">Period/Year</label>
|
||||
<p id="viewSubjectPeriod" style="margin: 0; padding: 8px; background: #f5f5f5; border-radius: 4px;"></p>
|
||||
</div>
|
||||
<div style="margin-bottom: 15px;">
|
||||
<label style="font-weight: 500; display: block; margin-bottom: 5px;">Assigned Teacher</label>
|
||||
<p id="viewSubjectTeacher" style="margin: 0; padding: 8px; background: #f5f5f5; border-radius: 4px;"></p>
|
||||
</div>
|
||||
<div style="margin-bottom: 15px;">
|
||||
<label style="font-weight: 500; display: block; margin-bottom: 5px;">Syllabus</label>
|
||||
<p id="viewSubjectSyllabus" style="margin: 0; padding: 8px; background: #f5f5f5; border-radius: 4px;"></p>
|
||||
</div>
|
||||
</div>
|
||||
<div style="padding: 15px; border-top: 1px solid #EEEEEE; text-align: right;">
|
||||
<button style="background-color: #9E9E9E; color: white; border: none; padding: 8px 16px; border-radius: 4px; cursor: pointer; font-size: 14px;" onclick="closeViewSubjectModal()">Close</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<script>
|
||||
function openViewSubjectModal(subjectId) {
|
||||
// Fetch subject details from the server
|
||||
fetch(`/app/subject/${subjectId}/view/`)
|
||||
.then(response => {
|
||||
if (!response.ok) {
|
||||
throw new Error('Network response was not ok');
|
||||
}
|
||||
return response.json();
|
||||
})
|
||||
.then(data => {
|
||||
if (data.success) {
|
||||
const subject = data.subject;
|
||||
// Populate the modal with subject details
|
||||
document.getElementById('viewSubjectName').textContent = subject.name;
|
||||
document.getElementById('viewSubjectCode').textContent = subject.code;
|
||||
document.getElementById('viewSubjectCourse').textContent = `${subject.course.name} (${subject.course.duration_type})`;
|
||||
document.getElementById('viewSubjectPeriod').textContent = `${subject.period_or_year} ${subject.course.duration_type}`;
|
||||
document.getElementById('viewSubjectTeacher').textContent = subject.teacher ? subject.teacher.name : 'Not Assigned';
|
||||
|
||||
// Handle syllabus PDF
|
||||
if (subject.syllabus_pdf) {
|
||||
document.getElementById('viewSubjectSyllabus').innerHTML =
|
||||
`<a href="${subject.syllabus_pdf}" target="_blank" style="color: #3F51B5; text-decoration: none;">View Syllabus PDF</a>`;
|
||||
} else {
|
||||
document.getElementById('viewSubjectSyllabus').textContent = 'No syllabus uploaded';
|
||||
}
|
||||
|
||||
// Show the modal
|
||||
document.getElementById('viewSubjectModal').style.display = 'block';
|
||||
} else {
|
||||
alert(data.message || 'Failed to fetch subject details');
|
||||
}
|
||||
})
|
||||
.catch(error => {
|
||||
console.error('Error fetching subject details:', error);
|
||||
alert('Failed to fetch subject details');
|
||||
});
|
||||
}
|
||||
|
||||
function closeViewSubjectModal() {
|
||||
document.getElementById('viewSubjectModal').style.display = 'none';
|
||||
}
|
||||
</script>
|
||||
File diff suppressed because it is too large.
Load diff
@@ -1,83 +0,0 @@
|
||||
{% extends "base.html" %}
|
||||
{% load static %}
|
||||
|
||||
{% block title %}Institute Feedback{% endblock %}
|
||||
|
||||
{% block content %}
|
||||
<div class="container mt-4">
|
||||
<div class="row justify-content-center">
|
||||
<div class="col-md-8">
|
||||
<div class="card">
|
||||
<div class="card-header">
|
||||
<h4 class="mb-0">Provide Institute Feedback</h4>
|
||||
</div>
|
||||
<div class="card-body">
|
||||
<form method="post">
|
||||
{% csrf_token %}
|
||||
|
||||
<div class="mb-3">
|
||||
<label for="{{ form.feedback_type.id_for_label }}" class="form-label">Feedback Type</label>
|
||||
{{ form.feedback_type }}
|
||||
{% if form.feedback_type.errors %}
|
||||
<div class="invalid-feedback d-block">
|
||||
{{ form.feedback_type.errors }}
|
||||
</div>
|
||||
{% endif %}
|
||||
</div>
|
||||
|
||||
<div class="mb-3">
|
||||
<label for="{{ form.rating.id_for_label }}" class="form-label">Rating</label>
|
||||
{{ form.rating }}
|
||||
{% if form.rating.errors %}
|
||||
<div class="invalid-feedback d-block">
|
||||
{{ form.rating.errors }}
|
||||
</div>
|
||||
{% endif %}
|
||||
</div>
|
||||
|
||||
<div class="mb-3">
|
||||
<label for="{{ form.feedback_text.id_for_label }}" class="form-label">Feedback</label>
|
||||
{{ form.feedback_text }}
|
||||
{% if form.feedback_text.errors %}
|
||||
<div class="invalid-feedback d-block">
|
||||
{{ form.feedback_text.errors }}
|
||||
</div>
|
||||
{% endif %}
|
||||
</div>
|
||||
|
||||
<div class="mb-3 form-check">
|
||||
{{ form.is_anonymous }}
|
||||
<label class="form-check-label" for="{{ form.is_anonymous.id_for_label }}">
|
||||
Submit Anonymously
|
||||
</label>
|
||||
</div>
|
||||
|
||||
<div class="mb-3 form-check">
|
||||
{{ form.is_public }}
|
||||
<label class="form-check-label" for="{{ form.is_public.id_for_label }}">
|
||||
Make Public
|
||||
</label>
|
||||
</div>
|
||||
|
||||
<div class="d-grid gap-2">
|
||||
<button type="submit" class="btn btn-primary">Submit Feedback</button>
|
||||
<a href="{% url 'parent_dashboard' %}" class="btn btn-secondary">Cancel</a>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
{% endblock %}
|
||||
|
||||
{% block extra_css %}
|
||||
<style>
|
||||
.form-control, .form-select {
|
||||
margin-bottom: 0.5rem;
|
||||
}
|
||||
.invalid-feedback {
|
||||
display: block;
|
||||
}
|
||||
</style>
|
||||
{% endblock %}
|
||||
@@ -1,12 +0,0 @@
|
||||
<!-- Cancellation Reason Modal -->
|
||||
<div id="cancellationReasonModal" style="display: none; position: fixed; top: 0; left: 0; right: 0; bottom: 0; background-color: rgba(0,0,0,0.5); z-index: 10000;" onclick="if(event.target === this) closeCancellationReasonModal();">
|
||||
<div style="position: absolute; top: 50%; left: 50%; transform: translate(-50%, -50%); width: 90%; max-width: 600px; background-color: white; border-radius: 8px; box-shadow: 0 2px 10px rgba(0,0,0,0.2);">
|
||||
<div style="background-color: #3F51B5; color: white; padding: 15px; border-top-left-radius: 8px; border-top-right-radius: 8px; display: flex; justify-content: space-between; align-items: center;">
|
||||
<h5 style="margin: 0; font-size: 18px;">Cancellation Reason</h5>
|
||||
<button style="background: none; border: none; color: white; font-size: 24px; cursor: pointer; line-height: 24px;" onclick="closeCancellationReasonModal()">×</button>
|
||||
</div>
|
||||
<div style="padding: 20px;">
|
||||
<div id="cancellationReasonContent"></div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@@ -1,245 +0,0 @@
|
||||
<!-- Parent Feedback Modal -->
|
||||
<div id="parentFeedbackModal" style="display: none; position: fixed; top: 0; left: 0; right: 0; bottom: 0; background-color: rgba(0,0,0,0.5); z-index: 10000;" onclick="if(event.target === this) closeParentFeedbackModal();">
|
||||
<div style="position: absolute; top: 50%; left: 50%; transform: translate(-50%, -50%); width: 90%; max-width: 600px; background-color: white; border-radius: 8px; box-shadow: 0 2px 10px rgba(0,0,0,0.2);">
|
||||
<div style="background-color: #3F51B5; color: white; padding: 15px; border-top-left-radius: 8px; border-top-right-radius: 8px; display: flex; justify-content: space-between; align-items: center;">
|
||||
<h5 style="margin: 0; font-size: 18px;">Give Feedback</h5>
|
||||
<button style="background: none; border: none; color: white; font-size: 24px; cursor: pointer; line-height: 24px;" onclick="closeParentFeedbackModal()">×</button>
|
||||
</div>
|
||||
<div style="padding: 20px;">
|
||||
<!-- Custom Tab Navigation -->
|
||||
<div class="feedback-tabs">
|
||||
<div id="parent-teacher-tab-btn" class="feedback-tab-btn active" onclick="switchParentFeedbackTab('teacher')">
|
||||
Teacher Feedback
|
||||
</div>
|
||||
<div id="parent-institute-tab-btn" class="feedback-tab-btn" onclick="switchParentFeedbackTab('institute')">
|
||||
Institute Feedback
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Tab Content -->
|
||||
<div class="tab-content">
|
||||
<!-- Teacher Feedback Form -->
|
||||
<div id="parent-teacher-tab-content" class="tab-pane active">
|
||||
<form method="post" action="{% url 'submit_feedback' %}" id="parentTeacherFeedbackForm">
|
||||
{% csrf_token %}
|
||||
<input type="hidden" name="feedback_type" value="teacher">
|
||||
<div class="mb-3">
|
||||
<label for="parent_teacher_select" class="form-label">Select Teacher</label>
|
||||
<select class="form-select" id="parent_teacher_select" name="teacher_id" required style="border-color: #BDBDBD;">
|
||||
<option value="">Select Teacher</option>
|
||||
{% for teacher in teachers %}
|
||||
<option value="{{ teacher.id }}">{{ teacher.name }} - {{ teacher.designation }}</option>
|
||||
{% endfor %}
|
||||
</select>
|
||||
</div>
|
||||
<div class="mb-3">
|
||||
<label for="parent_student_select" class="form-label">Select Student</label>
|
||||
<select class="form-select" id="parent_student_select" name="student_id" required style="border-color: #BDBDBD;">
|
||||
<option value="">Select Student</option>
|
||||
{% for student in parent.students.all %}
|
||||
<option value="{{ student.id }}">{{ student.name }}</option>
|
||||
{% endfor %}
|
||||
</select>
|
||||
</div>
|
||||
<div class="mb-3">
|
||||
<label class="form-label">Rating</label>
|
||||
<div class="rating-stars d-flex">
|
||||
<i class="far fa-star fs-4 text-warning rating-star" data-value="1" onclick="selectParentRating(this, 'parent_teacher_rating')"></i>
|
||||
<i class="far fa-star fs-4 text-warning rating-star" data-value="2" onclick="selectParentRating(this, 'parent_teacher_rating')"></i>
|
||||
<i class="far fa-star fs-4 text-warning rating-star" data-value="3" onclick="selectParentRating(this, 'parent_teacher_rating')"></i>
|
||||
<i class="far fa-star fs-4 text-warning rating-star" data-value="4" onclick="selectParentRating(this, 'parent_teacher_rating')"></i>
|
||||
<i class="far fa-star fs-4 text-warning rating-star" data-value="5" onclick="selectParentRating(this, 'parent_teacher_rating')"></i>
|
||||
<input type="hidden" name="rating" id="parent_teacher_rating" value="" required>
|
||||
<div class="invalid-feedback d-block" id="parent-teacher-rating-error" style="display: none;">Please select a rating</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="mb-3">
|
||||
<label for="parent_teacher_feedback" class="form-label">Your Feedback</label>
|
||||
<textarea class="form-control" id="parent_teacher_feedback" name="feedback_text" rows="3" required style="border-color: #BDBDBD;"></textarea>
|
||||
</div>
|
||||
<div class="d-flex justify-content-end gap-2">
|
||||
<button type="button" class="btn" style="background-color: #9E9E9E; color: white;" onclick="closeParentFeedbackModal()">Cancel</button>
|
||||
<button type="submit" class="btn" style="background-color: #3F51B5; color: white;">Submit Feedback</button>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
|
||||
<!-- Institute Feedback Form -->
|
||||
<div id="parent-institute-tab-content" class="tab-pane">
|
||||
<form method="post" action="{% url 'submit_institute_feedback' %}" id="parentInstituteFeedbackForm">
|
||||
{% csrf_token %}
|
||||
<input type="hidden" name="feedback_type" value="institute">
|
||||
<div class="mb-3">
|
||||
<label for="parent_feedback_type_select" class="form-label">Feedback Category</label>
|
||||
<select class="form-select" id="parent_feedback_type_select" name="feedback_type" required style="border-color: #BDBDBD;">
|
||||
<option value="">Select Category</option>
|
||||
<option value="General">General</option>
|
||||
<option value="Facilities">Facilities</option>
|
||||
<option value="Teaching">Teaching Quality</option>
|
||||
<option value="Infrastructure">Infrastructure</option>
|
||||
<option value="Administration">Administration</option>
|
||||
</select>
|
||||
</div>
|
||||
<div class="mb-3">
|
||||
<label for="parent_institute_student_select" class="form-label">Select Student</label>
|
||||
<select class="form-select" id="parent_institute_student_select" name="student_id" required style="border-color: #BDBDBD;">
|
||||
<option value="">Select Student</option>
|
||||
{% for student in parent.students.all %}
|
||||
<option value="{{ student.id }}">{{ student.name }}</option>
|
||||
{% endfor %}
|
||||
</select>
|
||||
</div>
|
||||
<div class="mb-3">
|
||||
<label class="form-label">Rating</label>
|
||||
<div class="rating-stars d-flex">
|
||||
<i class="far fa-star fs-4 text-warning rating-star" data-value="1" onclick="selectParentRating(this, 'parent_institute_rating')"></i>
|
||||
<i class="far fa-star fs-4 text-warning rating-star" data-value="2" onclick="selectParentRating(this, 'parent_institute_rating')"></i>
|
||||
<i class="far fa-star fs-4 text-warning rating-star" data-value="3" onclick="selectParentRating(this, 'parent_institute_rating')"></i>
|
||||
<i class="far fa-star fs-4 text-warning rating-star" data-value="4" onclick="selectParentRating(this, 'parent_institute_rating')"></i>
|
||||
<i class="far fa-star fs-4 text-warning rating-star" data-value="5" onclick="selectParentRating(this, 'parent_institute_rating')"></i>
|
||||
<input type="hidden" name="rating" id="parent_institute_rating" value="" required>
|
||||
<div class="invalid-feedback d-block" id="parent-institute-rating-error" style="display: none;">Please select a rating</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="mb-3">
|
||||
<label for="parent_institute_feedback" class="form-label">Your Feedback</label>
|
||||
<textarea class="form-control" id="parent_institute_feedback" name="feedback_text" rows="3" required placeholder="Please provide your feedback about the institute" style="border-color: #BDBDBD;"></textarea>
|
||||
</div>
|
||||
<div class="mb-3">
|
||||
<div class="form-check">
|
||||
<input class="form-check-input" type="checkbox" name="is_anonymous" id="parent_isAnonymous" style="border-color: #BDBDBD;">
|
||||
<label class="form-check-label" for="parent_isAnonymous">
|
||||
Submit anonymously
|
||||
</label>
|
||||
</div>
|
||||
</div>
|
||||
<div class="d-flex justify-content-end gap-2">
|
||||
<button type="button" class="btn" style="background-color: #9E9E9E; color: white;" onclick="closeParentFeedbackModal()">Cancel</button>
|
||||
<button type="submit" class="btn" style="background-color: #3F51B5; color: white;">Submit Feedback</button>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Parent Feedback Modal Scripts -->
|
||||
<script>
|
||||
function openParentFeedbackModal() {
|
||||
document.getElementById('parentFeedbackModal').style.display = 'block';
|
||||
document.body.style.overflow = 'hidden';
|
||||
}
|
||||
|
||||
function closeParentFeedbackModal() {
|
||||
document.getElementById('parentFeedbackModal').style.display = 'none';
|
||||
document.body.style.overflow = '';
|
||||
}
|
||||
|
||||
function switchParentFeedbackTab(tabName) {
|
||||
const teacherTabBtn = document.getElementById('parent-teacher-tab-btn');
|
||||
const instituteTabBtn = document.getElementById('parent-institute-tab-btn');
|
||||
const teacherContent = document.getElementById('parent-teacher-tab-content');
|
||||
const instituteContent = document.getElementById('parent-institute-tab-content');
|
||||
|
||||
teacherTabBtn.classList.remove('active');
|
||||
instituteTabBtn.classList.remove('active');
|
||||
teacherContent.classList.remove('active');
|
||||
instituteContent.classList.remove('active');
|
||||
|
||||
if (tabName === 'teacher') {
|
||||
teacherTabBtn.classList.add('active');
|
||||
teacherContent.classList.add('active');
|
||||
} else if (tabName === 'institute') {
|
||||
instituteTabBtn.classList.add('active');
|
||||
instituteContent.classList.add('active');
|
||||
}
|
||||
}
|
||||
|
||||
function selectParentRating(star, inputId) {
|
||||
const value = parseInt(star.getAttribute('data-value'));
|
||||
document.getElementById(inputId).value = value;
|
||||
|
||||
// Update star display
|
||||
const allStars = star.parentNode.querySelectorAll('.rating-star');
|
||||
allStars.forEach(s => {
|
||||
const starValue = parseInt(s.getAttribute('data-value'));
|
||||
if (starValue <= value) {
|
||||
s.classList.remove('far');
|
||||
s.classList.add('fas');
|
||||
s.classList.add('selected');
|
||||
} else {
|
||||
s.classList.remove('fas');
|
||||
s.classList.add('far');
|
||||
s.classList.remove('selected');
|
||||
}
|
||||
});
|
||||
|
||||
// Hide error message if any
|
||||
const errorId = inputId === 'parent_teacher_rating' ? 'parent-teacher-rating-error' : 'parent-institute-rating-error';
|
||||
document.getElementById(errorId).style.display = 'none';
|
||||
}
|
||||
</script>
|
||||
|
||||
<!-- Additional Feedback Styles -->
|
||||
<style>
|
||||
.feedback-tabs {
|
||||
display: flex;
|
||||
gap: 1rem;
|
||||
margin-bottom: 1.5rem;
|
||||
border-bottom: 1px solid #dee2e6;
|
||||
padding-bottom: 0.5rem;
|
||||
}
|
||||
|
||||
.feedback-tab-btn {
|
||||
padding: 0.5rem 1rem;
|
||||
cursor: pointer;
|
||||
border: none;
|
||||
background: none;
|
||||
color: #6c757d;
|
||||
font-weight: 500;
|
||||
transition: all 0.2s ease;
|
||||
}
|
||||
|
||||
.feedback-tab-btn:hover {
|
||||
color: #3F51B5;
|
||||
}
|
||||
|
||||
.feedback-tab-btn.active {
|
||||
color: #3F51B5;
|
||||
border-bottom: 2px solid #3F51B5;
|
||||
}
|
||||
|
||||
.tab-pane {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.tab-pane.active {
|
||||
display: block;
|
||||
}
|
||||
|
||||
.rating-stars {
|
||||
gap: 0.5rem;
|
||||
}
|
||||
|
||||
.rating-star {
|
||||
cursor: pointer;
|
||||
transition: all 0.2s ease;
|
||||
}
|
||||
|
||||
.rating-star:hover,
|
||||
.rating-star.selected {
|
||||
transform: scale(1.2);
|
||||
}
|
||||
|
||||
.form-select,
|
||||
.form-control {
|
||||
border-color: #BDBDBD;
|
||||
}
|
||||
|
||||
.form-select:focus,
|
||||
.form-control:focus {
|
||||
border-color: #3F51B5;
|
||||
box-shadow: 0 0 0 0.2rem rgba(63, 81, 181, 0.25);
|
||||
}
|
||||
</style>
|
||||
@@ -1,12 +0,0 @@
|
||||
<!-- Meeting Agenda Modal -->
|
||||
<div id="meetingAgendaModal" style="display: none; position: fixed; top: 0; left: 0; right: 0; bottom: 0; background-color: rgba(0,0,0,0.5); z-index: 10000;" onclick="if(event.target === this) closeMeetingAgendaModal();">
|
||||
<div style="position: absolute; top: 50%; left: 50%; transform: translate(-50%, -50%); width: 90%; max-width: 600px; background-color: white; border-radius: 8px; box-shadow: 0 2px 10px rgba(0,0,0,0.2);">
|
||||
<div style="background-color: #3F51B5; color: white; padding: 15px; border-top-left-radius: 8px; border-top-right-radius: 8px; display: flex; justify-content: space-between; align-items: center;">
|
||||
<h5 style="margin: 0; font-size: 18px;">Meeting Agenda</h5>
|
||||
<button style="background: none; border: none; color: white; font-size: 24px; cursor: pointer; line-height: 24px;" onclick="closeMeetingAgendaModal()">×</button>
|
||||
</div>
|
||||
<div style="padding: 20px;">
|
||||
<div id="meetingAgendaContent"></div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@@ -1,12 +0,0 @@
|
||||
<!-- Meeting Notes Modal -->
|
||||
<div id="meetingNotesModal" style="display: none; position: fixed; top: 0; left: 0; right: 0; bottom: 0; background-color: rgba(0,0,0,0.5); z-index: 10000;" onclick="if(event.target === this) closeMeetingNotesModal();">
|
||||
<div style="position: absolute; top: 50%; left: 50%; transform: translate(-50%, -50%); width: 90%; max-width: 600px; background-color: white; border-radius: 8px; box-shadow: 0 2px 10px rgba(0,0,0,0.2);">
|
||||
<div style="background-color: #3F51B5; color: white; padding: 15px; border-top-left-radius: 8px; border-top-right-radius: 8px; display: flex; justify-content: space-between; align-items: center;">
|
||||
<h5 style="margin: 0; font-size: 18px;">Meeting Notes</h5>
|
||||
<button style="background: none; border: none; color: white; font-size: 24px; cursor: pointer; line-height: 24px;" onclick="closeMeetingNotesModal()">×</button>
|
||||
</div>
|
||||
<div style="padding: 20px;">
|
||||
<div id="meetingNotesContent"></div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@@ -1,132 +0,0 @@
|
||||
<!-- Student Details Modal -->
|
||||
<div id="studentDetailsModal" style="display: none; position: fixed; top: 0; left: 0; right: 0; bottom: 0; background-color: rgba(0,0,0,0.5); z-index: 10000;" onclick="if(event.target === this) closeStudentDetailsModal();">
|
||||
<div style="position: absolute; top: 50%; left: 50%; transform: translate(-50%, -50%); width: 95%; max-width: 800px; background-color: white; border-radius: 8px; box-shadow: 0 2px 10px rgba(0,0,0,0.2);">
|
||||
<div style="background-color: #3F51B5; color: white; padding: 15px; border-top-left-radius: 8px; border-top-right-radius: 8px; display: flex; justify-content: space-between; align-items: center;">
|
||||
<h5 style="margin: 0; font-size: 18px;">Student Details</h5>
|
||||
<button style="background: none; border: none; color: white; font-size: 24px; cursor: pointer; line-height: 24px;" onclick="closeStudentDetailsModal()">×</button>
|
||||
</div>
|
||||
<div style="padding: 20px; max-height: 80vh; overflow-y: auto;">
|
||||
<div id="studentDetailsError" class="alert alert-danger" style="display: none;"></div>
|
||||
<div id="studentDetailsLoading" class="text-center" style="display: none;">
|
||||
<div class="spinner-border text-primary" role="status">
|
||||
<span class="visually-hidden">Loading...</span>
|
||||
</div>
|
||||
<p class="mt-2">Loading student details...</p>
|
||||
</div>
|
||||
<div id="studentDetailsContent">
|
||||
<div class="row">
|
||||
<!-- Student Basic Info -->
|
||||
<div class="col-md-6 mb-4">
|
||||
<div class="card h-100 border-0 shadow-sm">
|
||||
<div class="card-header bg-light py-2">
|
||||
<h5 class="card-title mb-0"><i class="fas fa-info-circle me-2"></i>Basic Information</h5>
|
||||
</div>
|
||||
<div class="card-body">
|
||||
<div class="text-center mb-3">
|
||||
<div id="studentImage" class="mx-auto mb-2" style="width: 80px; height: 80px; border-radius: 50%; overflow: hidden; background: #f0f0f0; display: flex; align-items: center; justify-content: center;">
|
||||
<i class="fas fa-user fa-2x text-secondary"></i>
|
||||
</div>
|
||||
<h5 id="studentName" class="mb-1">Student Name</h5>
|
||||
<p id="studentCourse" class="text-muted mb-0">Course</p>
|
||||
</div>
|
||||
|
||||
<div class="row">
|
||||
<div class="col-6">
|
||||
<p class="text-muted mb-1">ID</p>
|
||||
<p id="studentId" class="mb-3">-</p>
|
||||
</div>
|
||||
<div class="col-6">
|
||||
<p class="text-muted mb-1">Status</p>
|
||||
<p id="studentStatusBadge" class="mb-3">-</p>
|
||||
</div>
|
||||
<div class="col-6">
|
||||
<p class="text-muted mb-1">Current Period</p>
|
||||
<p id="studentPeriod" class="mb-3">-</p>
|
||||
</div>
|
||||
<div class="col-6">
|
||||
<p class="text-muted mb-1">Attendance</p>
|
||||
<p id="studentAttendance" class="mb-3">-</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Student Contact Info -->
|
||||
<div class="col-md-6 mb-4">
|
||||
<div class="card h-100 border-0 shadow-sm">
|
||||
<div class="card-header bg-light py-2">
|
||||
<h5 class="card-title mb-0"><i class="fas fa-address-book me-2"></i>Contact Information</h5>
|
||||
</div>
|
||||
<div class="card-body">
|
||||
<div class="row">
|
||||
<div class="col-12">
|
||||
<p class="text-muted mb-1">Email</p>
|
||||
<p id="studentEmail" class="mb-3">-</p>
|
||||
</div>
|
||||
<div class="col-12">
|
||||
<p class="text-muted mb-1">Phone</p>
|
||||
<p id="studentPhone" class="mb-3">-</p>
|
||||
</div>
|
||||
<div class="col-12">
|
||||
<p class="text-muted mb-1">Address</p>
|
||||
<p id="studentAddress" class="mb-3">-</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Course Timeline Information -->
|
||||
<div class="card border-0 shadow-sm">
|
||||
<div class="card-header bg-light py-2">
|
||||
<h5 class="card-title mb-0"><i class="fas fa-calendar-alt me-2"></i>Course Timeline</h5>
|
||||
</div>
|
||||
<div class="card-body">
|
||||
<div class="row">
|
||||
<div class="col-md-3">
|
||||
<p class="text-muted mb-1">Course Name</p>
|
||||
<p id="studentCourseName" class="mb-3">-</p>
|
||||
</div>
|
||||
<div class="col-md-3">
|
||||
<p class="text-muted mb-1">Course Code</p>
|
||||
<p id="studentCourseCode" class="mb-3">-</p>
|
||||
</div>
|
||||
<div class="col-md-3">
|
||||
<p class="text-muted mb-1">Course Status</p>
|
||||
<p id="studentCourseStatus" class="mb-3">-</p>
|
||||
</div>
|
||||
<div class="col-md-3">
|
||||
<p class="text-muted mb-1">Current Period</p>
|
||||
<p id="studentCurrentPeriod" class="mb-3">-</p>
|
||||
</div>
|
||||
</div>
|
||||
<div class="row">
|
||||
<div class="col-12 mb-3">
|
||||
<p class="text-muted mb-1">Course Description</p>
|
||||
<p id="studentCourseDescription" class="mb-3">-</p>
|
||||
</div>
|
||||
</div>
|
||||
<div class="row">
|
||||
<div class="col-md-4">
|
||||
<p class="text-muted mb-1">Current Period End Date</p>
|
||||
<p id="studentPeriodEndDate" class="mb-3">-</p>
|
||||
</div>
|
||||
<div class="col-md-4">
|
||||
<p class="text-muted mb-1">Expected Course Completion</p>
|
||||
<p id="studentCourseEndDate" class="mb-3">-</p>
|
||||
</div>
|
||||
<div class="col-md-4">
|
||||
<p class="text-muted mb-1">Progress</p>
|
||||
<div class="progress mb-3">
|
||||
<div id="studentProgressBar" class="progress-bar" role="progressbar" style="width: 0%"></div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@@ -1,132 +0,0 @@
|
||||
{% extends 'base.html' %}
|
||||
{% load static %}
|
||||
|
||||
{% block content %}
|
||||
<div class="container py-4">
|
||||
<div class="row justify-content-center">
|
||||
<div class="col-md-8">
|
||||
<div class="content-card">
|
||||
<div class="d-flex justify-content-between align-items-center mb-4">
|
||||
<h3 class="mb-0">
|
||||
<i class="fas fa-star me-2"></i> Submit Feedback
|
||||
</h3>
|
||||
<a href="{% url 'parent_dashboard' %}" class="btn btn-outline-secondary">
|
||||
<i class="fas fa-arrow-left me-2"></i> Back to Dashboard
|
||||
</a>
|
||||
</div>
|
||||
|
||||
<form method="post" class="feedback-form">
|
||||
{% csrf_token %}
|
||||
|
||||
<!-- Student Selection -->
|
||||
<div class="mb-3">
|
||||
<label for="student" class="form-label">Select Student</label>
|
||||
<select class="form-select" id="student" name="student" required>
|
||||
<option value="">Choose your child...</option>
|
||||
{% for student in students %}
|
||||
<option value="{{ student.id }}">{{ student.name }}</option>
|
||||
{% endfor %}
|
||||
</select>
|
||||
</div>
|
||||
|
||||
<!-- Teacher Selection -->
|
||||
<div class="mb-3">
|
||||
<label for="teacher" class="form-label">Select Teacher</label>
|
||||
<select class="form-select" id="teacher" name="teacher" required>
|
||||
<option value="">Choose a teacher...</option>
|
||||
{% for teacher in teachers %}
|
||||
<option value="{{ teacher.id }}">{{ teacher.name }}</option>
|
||||
{% endfor %}
|
||||
</select>
|
||||
</div>
|
||||
|
||||
<!-- Rating -->
|
||||
<div class="mb-3">
|
||||
<label class="form-label">Rating</label>
|
||||
<div class="rating-stars d-flex">
|
||||
<i class="far fa-star fs-4 text-warning rating-star" data-value="1" onclick="selectRating(this, 'selected_rating')"></i>
|
||||
<i class="far fa-star fs-4 text-warning rating-star" data-value="2" onclick="selectRating(this, 'selected_rating')"></i>
|
||||
<i class="far fa-star fs-4 text-warning rating-star" data-value="3" onclick="selectRating(this, 'selected_rating')"></i>
|
||||
<i class="far fa-star fs-4 text-warning rating-star" data-value="4" onclick="selectRating(this, 'selected_rating')"></i>
|
||||
<i class="far fa-star fs-4 text-warning rating-star" data-value="5" onclick="selectRating(this, 'selected_rating')"></i>
|
||||
<input type="hidden" name="rating" id="selected_rating" value="" required>
|
||||
<div class="invalid-feedback d-block" id="rating-error" style="display: none;">Please select a rating</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Feedback Text -->
|
||||
<div class="mb-3">
|
||||
<label for="feedback_text" class="form-label">Your Feedback</label>
|
||||
<textarea class="form-control" id="feedback_text" name="feedback_text" rows="4" required></textarea>
|
||||
</div>
|
||||
|
||||
<!-- Submit Button -->
|
||||
<div class="text-end">
|
||||
<button type="submit" class="btn btn-primary">
|
||||
<i class="fas fa-paper-plane me-1"></i> Submit Feedback
|
||||
</button>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Rating System JavaScript -->
|
||||
<script>
|
||||
function selectRating(star, inputId) {
|
||||
const value = parseInt(star.getAttribute('data-value'));
|
||||
document.getElementById(inputId).value = value;
|
||||
|
||||
// Update star display
|
||||
const allStars = star.parentNode.querySelectorAll('.rating-star');
|
||||
allStars.forEach(s => {
|
||||
const starValue = parseInt(s.getAttribute('data-value'));
|
||||
if (starValue <= value) {
|
||||
s.classList.remove('far');
|
||||
s.classList.add('fas');
|
||||
s.classList.add('selected');
|
||||
} else {
|
||||
s.classList.remove('fas');
|
||||
s.classList.add('far');
|
||||
s.classList.remove('selected');
|
||||
}
|
||||
});
|
||||
|
||||
// Hide error message
|
||||
document.getElementById('rating-error').style.display = 'none';
|
||||
}
|
||||
|
||||
// Form validation
|
||||
document.querySelector('.feedback-form').addEventListener('submit', function(e) {
|
||||
const ratingInput = document.getElementById('selected_rating');
|
||||
if (!ratingInput.value) {
|
||||
e.preventDefault();
|
||||
document.getElementById('rating-error').style.display = 'block';
|
||||
document.getElementById('rating-error').scrollIntoView({ behavior: 'smooth' });
|
||||
}
|
||||
});
|
||||
</script>
|
||||
|
||||
<!-- Rating System Styles -->
|
||||
<style>
|
||||
.rating-stars {
|
||||
display: flex;
|
||||
gap: 8px;
|
||||
padding: 8px 0;
|
||||
}
|
||||
|
||||
.rating-star {
|
||||
cursor: pointer;
|
||||
transition: all 0.2s ease;
|
||||
}
|
||||
|
||||
.rating-star:hover,
|
||||
.rating-star.active,
|
||||
.rating-star.selected {
|
||||
color: #FFD700 !important;
|
||||
transform: scale(1.1);
|
||||
}
|
||||
</style>
|
||||
|
||||
{% endblock %}
|
||||
@@ -1,384 +0,0 @@
|
||||
{% load static %}
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<script>
|
||||
// Force cache clear for JavaScript files
|
||||
if (window.location.href.indexOf('cache=clear') === -1) {
|
||||
// Only run once to avoid redirect loop
|
||||
const allScripts = document.querySelectorAll('script[src]');
|
||||
for (const script of allScripts) {
|
||||
if (script.src.indexOf('static/js/') !== -1) {
|
||||
const originalSrc = script.src;
|
||||
script.src = originalSrc + '?v=' + new Date().getTime();
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
<meta charset="utf-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<meta http-equiv="Cache-Control" content="no-cache, no-store, must-revalidate">
|
||||
<meta http-equiv="Pragma" content="no-cache">
|
||||
<meta http-equiv="Expires" content="0">
|
||||
<title>{% block title %}Student Management System{% endblock %}</title>
|
||||
|
||||
<!-- Favicon -->
|
||||
<link rel="icon" type="image/x-icon" href="{% static 'img/favicon.ico' %}">
|
||||
|
||||
<!-- Bootstrap CSS -->
|
||||
<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.0/dist/css/bootstrap.min.css" rel="stylesheet">
|
||||
<!-- Font Awesome -->
|
||||
<link href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.0.0/css/all.min.css" rel="stylesheet">
|
||||
|
||||
<!-- Base CSS -->
|
||||
<link rel="stylesheet" href="{% static 'css/base.css' %}">
|
||||
<!-- Custom Dashboard CSS -->
|
||||
<link href="{% static 'css/dashboard.css' %}" rel="stylesheet">
|
||||
<!-- Custom CSS -->
|
||||
<link href="{% static 'css/custom.css' %}" rel="stylesheet">
|
||||
|
||||
<!-- Responsive Styles -->
|
||||
<style>
|
||||
/* Base responsive styles */
|
||||
html {
|
||||
font-size: 16px;
|
||||
-webkit-text-size-adjust: 100%;
|
||||
}
|
||||
|
||||
body {
|
||||
overflow-x: hidden;
|
||||
width: 100%;
|
||||
-webkit-font-smoothing: antialiased;
|
||||
-moz-osx-font-smoothing: grayscale;
|
||||
}
|
||||
|
||||
/* Responsive typography */
|
||||
@media (max-width: 768px) {
|
||||
html {
|
||||
font-size: 14px;
|
||||
}
|
||||
h1 { font-size: 1.8rem; }
|
||||
h2 { font-size: 1.5rem; }
|
||||
h3 { font-size: 1.3rem; }
|
||||
h4 { font-size: 1.1rem; }
|
||||
h5 { font-size: 1rem; }
|
||||
h6 { font-size: 0.9rem; }
|
||||
}
|
||||
|
||||
/* Responsive containers and spacing */
|
||||
@media (max-width: 768px) {
|
||||
.container, .container-fluid {
|
||||
padding-left: 15px;
|
||||
padding-right: 15px;
|
||||
}
|
||||
.row {
|
||||
margin-left: -10px;
|
||||
margin-right: -10px;
|
||||
}
|
||||
.col-md-6, .col-lg-3 {
|
||||
padding-left: 10px;
|
||||
padding-right: 10px;
|
||||
}
|
||||
.card {
|
||||
margin-bottom: 1rem;
|
||||
}
|
||||
.table-responsive {
|
||||
overflow-x: auto;
|
||||
-webkit-overflow-scrolling: touch;
|
||||
}
|
||||
.welcome-section h2 {
|
||||
font-size: 1.5rem;
|
||||
}
|
||||
.summary-card .card-body {
|
||||
padding: 0.75rem;
|
||||
}
|
||||
}
|
||||
|
||||
/* Small screen optimizations */
|
||||
@media (max-width: 576px) {
|
||||
html {
|
||||
font-size: 13px;
|
||||
}
|
||||
.container-fluid {
|
||||
padding-left: 10px;
|
||||
padding-right: 10px;
|
||||
}
|
||||
.col-md-6, .col-lg-3 {
|
||||
padding-left: 5px;
|
||||
padding-right: 5px;
|
||||
}
|
||||
.row {
|
||||
margin-left: -5px;
|
||||
margin-right: -5px;
|
||||
}
|
||||
.card {
|
||||
border-radius: 0.5rem;
|
||||
}
|
||||
.btn {
|
||||
padding: 0.375rem 0.75rem;
|
||||
font-size: 0.875rem;
|
||||
}
|
||||
.form-control {
|
||||
font-size: 0.875rem;
|
||||
}
|
||||
.table th, .table td {
|
||||
padding: 0.5rem;
|
||||
font-size: 0.875rem;
|
||||
}
|
||||
.modal-dialog {
|
||||
margin: 0.5rem;
|
||||
}
|
||||
.toast {
|
||||
width: 100%;
|
||||
max-width: none;
|
||||
}
|
||||
}
|
||||
|
||||
/* Responsive navigation */
|
||||
@media (max-width: 768px) {
|
||||
.navbar-nav {
|
||||
padding: 1rem 0;
|
||||
}
|
||||
.navbar-toggler {
|
||||
padding: 0.25rem 0.5rem;
|
||||
}
|
||||
.dropdown-menu {
|
||||
border: none;
|
||||
box-shadow: none;
|
||||
}
|
||||
}
|
||||
|
||||
/* Responsive forms */
|
||||
@media (max-width: 576px) {
|
||||
.form-group {
|
||||
margin-bottom: 0.75rem;
|
||||
}
|
||||
.form-label {
|
||||
font-size: 0.875rem;
|
||||
margin-bottom: 0.25rem;
|
||||
}
|
||||
.form-control {
|
||||
height: calc(1.5em + 0.5rem + 2px);
|
||||
}
|
||||
}
|
||||
|
||||
/* Responsive images and media */
|
||||
img {
|
||||
max-width: 100%;
|
||||
height: auto;
|
||||
}
|
||||
|
||||
/* Profile picture responsive styles */
|
||||
.profile-picture-container {
|
||||
position: relative;
|
||||
border-radius: 50%;
|
||||
overflow: hidden;
|
||||
background-color: #f5f5f5;
|
||||
border: 2px solid #3F51B5;
|
||||
margin-right: 1rem;
|
||||
flex-shrink: 0;
|
||||
aspect-ratio: 1;
|
||||
}
|
||||
|
||||
.profile-picture {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
object-fit: cover;
|
||||
border-radius: 50%;
|
||||
}
|
||||
|
||||
/* Profile picture sizes */
|
||||
.profile-picture-container.profile-lg {
|
||||
width: 120px;
|
||||
height: 120px;
|
||||
}
|
||||
|
||||
.profile-picture-container.profile-md {
|
||||
width: 80px;
|
||||
height: 80px;
|
||||
}
|
||||
|
||||
.profile-picture-container.profile-sm {
|
||||
width: 60px;
|
||||
height: 60px;
|
||||
}
|
||||
|
||||
/* Mobile optimizations for profile pictures */
|
||||
@media (max-width: 576px) {
|
||||
.profile-picture-container {
|
||||
border-width: 1.5px;
|
||||
}
|
||||
.profile-picture-container.profile-lg {
|
||||
width: 100px;
|
||||
height: 100px;
|
||||
}
|
||||
.profile-picture-container.profile-md {
|
||||
width: 60px;
|
||||
height: 60px;
|
||||
}
|
||||
.profile-picture-container.profile-sm {
|
||||
width: 40px;
|
||||
height: 40px;
|
||||
}
|
||||
}
|
||||
|
||||
/* Responsive modals */
|
||||
@media (max-width: 576px) {
|
||||
.modal-dialog {
|
||||
margin: 0;
|
||||
max-width: none;
|
||||
height: 100%;
|
||||
}
|
||||
.modal-content {
|
||||
border-radius: 0;
|
||||
min-height: 100%;
|
||||
}
|
||||
}
|
||||
|
||||
/* Touch device optimizations */
|
||||
@media (hover: none) {
|
||||
.btn, .nav-link, .dropdown-item {
|
||||
padding: 0.75rem 1rem;
|
||||
}
|
||||
.form-control, .form-select {
|
||||
padding: 0.75rem 1rem;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
{% block extrahead %}{% endblock %}
|
||||
</head>
|
||||
<body>
|
||||
{% if messages %}
|
||||
<div class="position-fixed top-0 end-0 p-3" style="z-index: 11">
|
||||
{% for message in messages %}
|
||||
<div class="toast show" role="alert" aria-live="assertive" aria-atomic="true">
|
||||
<div class="toast-header {% if message.tags == 'error' %}bg-danger text-white{% elif message.tags == 'success' %}bg-success text-white{% else %}bg-info text-white{% endif %}">
|
||||
<strong class="me-auto">{% if message.tags == 'error' %}Error{% elif message.tags == 'success' %}Success{% else %}Info{% endif %}</strong>
|
||||
<button type="button" class="btn-close" data-bs-dismiss="toast" aria-label="Close"></button>
|
||||
</div>
|
||||
<div class="toast-body">
|
||||
{{ message }}
|
||||
</div>
|
||||
</div>
|
||||
{% endfor %}
|
||||
</div>
|
||||
{% endif %}
|
||||
|
||||
{% block content %}{% endblock %}
|
||||
|
||||
<!-- Bootstrap Bundle with Popper -->
|
||||
<script src="https://cdn.jsdelivr.net/npm/bootstrap@5.3.0/dist/js/bootstrap.bundle.min.js"></script>
|
||||
|
||||
<!-- Firebase SDK -->
|
||||
<script src="https://www.gstatic.com/firebasejs/9.22.0/firebase-app-compat.js"></script>
|
||||
<script src="https://www.gstatic.com/firebasejs/9.22.0/firebase-messaging-compat.js"></script>
|
||||
|
||||
<!-- Firebase Configuration -->
|
||||
<script src="{% static 'js/firebase-config.js' %}"></script>
|
||||
|
||||
<!-- Firebase Initialization -->
|
||||
<script src="{% static 'js/firebase-init.js' %}"></script>
|
||||
|
||||
<!-- Custom Dashboard Components - No type="module" -->
|
||||
<script src="{% static 'js/rating.js' %}?v={% now 'U' %}"></script>
|
||||
<script src="{% static 'js/tabs.js' %}?v={% now 'U' %}"></script>
|
||||
<script src="{% static 'js/bootstrap-init.js' %}?v={% now 'U' %}"></script>
|
||||
<script src="{% static 'js/notifications.js' %}?v={% now 'U' %}"></script>
|
||||
<script src="{% static 'js/form-validation.js' %}?v={% now 'U' %}"></script>
|
||||
<!-- Navigation JS -->
|
||||
<script src="{% static 'js/navigation.js' %}?v={% now 'U' %}"></script>
|
||||
<!-- Main Dashboard JS - Must come after all supporting scripts -->
|
||||
<script src="{% static 'js/dashboard.js' %}?v={% now 'U' %}"></script>
|
||||
|
||||
<!-- Common Modal Functions -->
|
||||
<script>
|
||||
function openLeaveModal() {
|
||||
var modal = document.getElementById('customLeaveModal');
|
||||
modal.style.display = 'block';
|
||||
document.body.style.overflow = 'hidden';
|
||||
}
|
||||
|
||||
function closeLeaveModal() {
|
||||
var modal = document.getElementById('customLeaveModal');
|
||||
modal.style.display = 'none';
|
||||
document.body.style.overflow = '';
|
||||
}
|
||||
|
||||
function openProfileModal() {
|
||||
var modal = document.getElementById('profileModal');
|
||||
modal.style.display = 'block';
|
||||
document.body.style.overflow = 'hidden';
|
||||
}
|
||||
|
||||
function closeProfileModal() {
|
||||
var modal = document.getElementById('profileModal');
|
||||
modal.style.display = 'none';
|
||||
document.body.style.overflow = '';
|
||||
}
|
||||
|
||||
function openPasswordModal() {
|
||||
var modal = document.getElementById('passwordModal');
|
||||
modal.style.display = 'block';
|
||||
document.body.style.overflow = 'hidden';
|
||||
}
|
||||
|
||||
function closePasswordModal() {
|
||||
var modal = document.getElementById('passwordModal');
|
||||
modal.style.display = 'none';
|
||||
document.body.style.overflow = '';
|
||||
}
|
||||
|
||||
// Handle Escape key for all modals
|
||||
document.addEventListener('keydown', function(event) {
|
||||
if (event.key === 'Escape') {
|
||||
var modals = document.querySelectorAll('[id$="Modal"]');
|
||||
modals.forEach(function(modal) {
|
||||
if (modal.style.display === 'block') {
|
||||
// Call the appropriate close function
|
||||
var modalId = modal.id;
|
||||
var closeFunction = window['close' + modalId.charAt(0).toUpperCase() + modalId.slice(1)];
|
||||
if (typeof closeFunction === 'function') {
|
||||
closeFunction();
|
||||
} else {
|
||||
// Fallback to basic close
|
||||
modal.style.display = 'none';
|
||||
document.body.style.overflow = '';
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
// Handle clicking outside modal to close
|
||||
document.addEventListener('click', function(event) {
|
||||
var modals = document.querySelectorAll('[id$="Modal"]');
|
||||
modals.forEach(function(modal) {
|
||||
if (event.target === modal) {
|
||||
// Call the appropriate close function
|
||||
var modalId = modal.id;
|
||||
var closeFunction = window['close' + modalId.charAt(0).toUpperCase() + modalId.slice(1)];
|
||||
if (typeof closeFunction === 'function') {
|
||||
closeFunction();
|
||||
} else {
|
||||
// Fallback to basic close
|
||||
modal.style.display = 'none';
|
||||
document.body.style.overflow = '';
|
||||
}
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
// Auto-hide toasts
|
||||
document.addEventListener('DOMContentLoaded', function() {
|
||||
var toasts = document.querySelectorAll('.toast');
|
||||
toasts.forEach(function(toast) {
|
||||
setTimeout(function() {
|
||||
toast.style.display = 'none';
|
||||
}, 5000); // Hide after 5 seconds
|
||||
});
|
||||
});
|
||||
</script>
|
||||
|
||||
{% block extra_js %}{% endblock %}
|
||||
</body>
|
||||
</html>
|
||||
@@ -1,256 +0,0 @@
|
||||
<!-- Bottom Navigation -->
|
||||
<div class="bottom-nav">
|
||||
<div class="nav-container">
|
||||
<a href="#" class="nav-item active" data-section="dashboard">
|
||||
<i class="fas fa-home"></i>
|
||||
<span>Dashboard</span>
|
||||
</a>
|
||||
{% if user.groups.all.0.name == 'Teacher' %}
|
||||
<a href="#subjectsSection" class="nav-item" data-section="subjects" onclick="showSection('subjectsSection'); return false;">
|
||||
<i class="fas fa-graduation-cap"></i>
|
||||
<span>Subjects</span>
|
||||
</a>
|
||||
<a href="#" class="nav-item" data-section="schedule">
|
||||
<i class="fas fa-clock"></i>
|
||||
<span>Schedule</span>
|
||||
</a>
|
||||
<a href="#" class="nav-item" data-section="attendance">
|
||||
<i class="fas fa-user-check"></i>
|
||||
<span>Attendance</span>
|
||||
</a>
|
||||
<a href="#" class="nav-item" data-section="leave">
|
||||
<i class="fas fa-calendar-times"></i>
|
||||
<span>Leaves</span>
|
||||
</a>
|
||||
<a href="#" class="nav-item" data-section="notice">
|
||||
<i class="fas fa-bullhorn"></i>
|
||||
<span>Notices</span>
|
||||
</a>
|
||||
<a href="#" class="nav-item" data-section="feedback">
|
||||
<i class="fas fa-star"></i>
|
||||
<span>Feedback</span>
|
||||
</a>
|
||||
<a href="#" class="nav-item" data-section="profile">
|
||||
<i class="fas fa-id-card"></i>
|
||||
<span>Profile</span>
|
||||
</a>
|
||||
{% elif user.groups.all.0.name == 'Admission Officer' %}
|
||||
<a href="#" class="nav-item" data-section="students" onclick="showSection('studentsSection'); return false;">
|
||||
<i class="fas fa-users"></i>
|
||||
<span>Students</span>
|
||||
</a>
|
||||
<a href="#" class="nav-item" data-section="courses" onclick="showSection('coursesSection'); return false;">
|
||||
<i class="fas fa-graduation-cap"></i>
|
||||
<span>Courses</span>
|
||||
</a>
|
||||
<a href="#" class="nav-item" data-section="batches" onclick="showSection('batchesSection'); return false;">
|
||||
<i class="fas fa-layer-group"></i>
|
||||
<span>Batches</span>
|
||||
</a>
|
||||
<a href="#" class="nav-item" data-section="courseTracking" onclick="showSection('courseTrackingSection'); return false;">
|
||||
<i class="fas fa-chart-line"></i>
|
||||
<span>Tracking</span>
|
||||
</a>
|
||||
<a href="#" class="nav-item" data-section="leave">
|
||||
<i class="fas fa-calendar-times"></i>
|
||||
<span>Leaves</span>
|
||||
</a>
|
||||
<a href="#" class="nav-item" data-section="notice" onclick="showSection('noticeSection'); return false;">
|
||||
<i class="fas fa-bullhorn"></i>
|
||||
<span>Notices</span>
|
||||
</a>
|
||||
<a href="#" class="nav-item" data-section="profile">
|
||||
<i class="fas fa-id-card"></i>
|
||||
<span>Profile</span>
|
||||
</a>
|
||||
{% elif user.groups.all.0.name == 'HOD' %}
|
||||
<a href="#" class="nav-item" data-section="students" onclick="showSection('studentsSection'); return false;">
|
||||
<i class="fas fa-users"></i>
|
||||
<span>Students</span>
|
||||
</a>
|
||||
<a href="#" class="nav-item" data-section="staff" onclick="showSection('staffSection'); return false;">
|
||||
<i class="fas fa-chalkboard-teacher"></i>
|
||||
<span>Staff</span>
|
||||
</a>
|
||||
<a href="#" class="nav-item" data-section="subjects" onclick="showSection('subjectsSection'); return false;">
|
||||
<i class="fas fa-book"></i>
|
||||
<span>Subjects</span>
|
||||
</a>
|
||||
<a href="#" class="nav-item" data-section="routine" onclick="showSection('routineSection'); return false;">
|
||||
<i class="fas fa-calendar-week"></i>
|
||||
<span>Routine</span>
|
||||
</a>
|
||||
<a href="#" class="nav-item" data-section="progress" onclick="showSection('progressSection'); return false;">
|
||||
<i class="fas fa-chart-line"></i>
|
||||
<span>Progress</span>
|
||||
</a>
|
||||
<a href="#" class="nav-item" data-section="meetings" onclick="showSection('meetingsSection'); return false;">
|
||||
<i class="fas fa-calendar-check"></i>
|
||||
<span>Meetings</span>
|
||||
</a>
|
||||
<a href="#" class="nav-item" data-section="notice" onclick="showSection('noticeSection'); return false;">
|
||||
<i class="fas fa-bullhorn"></i>
|
||||
<span>Notices</span>
|
||||
</a>
|
||||
<a href="#" class="nav-item" data-section="profile" onclick="showSection('profileSection'); return false;">
|
||||
<i class="fas fa-id-card"></i>
|
||||
<span>Profile</span>
|
||||
</a>
|
||||
{% else %}
|
||||
<a href="#" class="nav-item" data-section="subjects">
|
||||
<i class="fas fa-graduation-cap"></i>
|
||||
<span>Subjects</span>
|
||||
</a>
|
||||
<a href="#" class="nav-item" data-section="routine">
|
||||
<i class="fas fa-calendar-week"></i>
|
||||
<span>Routine</span>
|
||||
</a>
|
||||
<a href="#" class="nav-item" data-section="attendance">
|
||||
<i class="fas fa-user-check"></i>
|
||||
<span>Attendance</span>
|
||||
</a>
|
||||
<a href="#" class="nav-item" data-section="leave">
|
||||
<i class="fas fa-calendar-times"></i>
|
||||
<span>Leaves</span>
|
||||
</a>
|
||||
{% if user.groups.all.0.name == 'Parent' %}
|
||||
<a href="#" class="nav-item" data-section="meetings" onclick="showSection('meetingsSection'); return false;">
|
||||
<i class="fas fa-calendar-check"></i>
|
||||
<span>Meetings</span>
|
||||
</a>
|
||||
{% endif %}
|
||||
<a href="#" class="nav-item" data-section="feedback">
|
||||
<i class="fas fa-star"></i>
|
||||
<span>Feedback</span>
|
||||
</a>
|
||||
<a href="#" class="nav-item" data-section="notice">
|
||||
<i class="fas fa-bullhorn"></i>
|
||||
<span>Notices</span>
|
||||
</a>
|
||||
<a href="#" class="nav-item" data-section="profile">
|
||||
<i class="fas fa-id-card"></i>
|
||||
<span>Profile</span>
|
||||
</a>
|
||||
{% endif %}
|
||||
<a href="#" class="nav-item" onclick="document.getElementById('logoutForm').submit(); return false;">
|
||||
<i class="fas fa-sign-out-alt"></i>
|
||||
<span>Logout</span>
|
||||
</a>
|
||||
<form id="logoutForm" method="post" action="{% url 'logout' %}" style="display: none;">
|
||||
{% csrf_token %}
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Navigation Styles -->
|
||||
<style>
|
||||
.bottom-nav {
|
||||
position: fixed;
|
||||
bottom: 0;
|
||||
left: 0;
|
||||
right: 0;
|
||||
background: white;
|
||||
box-shadow: 0 -2px 10px rgba(0,0,0,0.1);
|
||||
z-index: 1000;
|
||||
overflow-x: auto; /* Allow horizontal scrolling if needed */
|
||||
padding-bottom: 28px; /* Increased space for copyright */
|
||||
}
|
||||
|
||||
.nav-container {
|
||||
display: flex;
|
||||
justify-content: space-around;
|
||||
align-items: center;
|
||||
padding: 10px 0 5px 0;
|
||||
max-width: 1200px;
|
||||
margin: 0 auto;
|
||||
flex-wrap: nowrap; /* Prevent wrapping to ensure all items are visible */
|
||||
min-width: max-content; /* Ensure container is at least as wide as all items */
|
||||
}
|
||||
|
||||
.nav-item {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
color: #666;
|
||||
text-decoration: none;
|
||||
font-size: 0.9rem;
|
||||
transition: color 0.3s ease;
|
||||
margin: 0 8px; /* Add margin between items */
|
||||
white-space: nowrap; /* Prevent text wrapping */
|
||||
}
|
||||
|
||||
.nav-btn {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
color: #666;
|
||||
background: none;
|
||||
border: none;
|
||||
font-size: 0.9rem;
|
||||
transition: color 0.3s ease;
|
||||
cursor: pointer;
|
||||
margin: 0 8px;
|
||||
padding: 0;
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
.nav-item i, .nav-btn i {
|
||||
font-size: 1.2rem;
|
||||
margin-bottom: 4px;
|
||||
}
|
||||
|
||||
.nav-item.active, .nav-btn.active {
|
||||
color: #3F51B5;
|
||||
}
|
||||
|
||||
.nav-item:hover, .nav-btn:hover {
|
||||
color: #3F51B5;
|
||||
}
|
||||
|
||||
.nav-copyright {
|
||||
width: 100%;
|
||||
text-align: center;
|
||||
position: absolute;
|
||||
bottom: 0;
|
||||
left: 0;
|
||||
padding: 5px 0;
|
||||
font-size: 0.85rem;
|
||||
color: #555;
|
||||
background-color: #f0f0f0;
|
||||
border-top: 1px solid #ddd;
|
||||
line-height: 18px;
|
||||
box-shadow: 0 -1px 2px rgba(0,0,0,0.05);
|
||||
z-index: 1001; /* Higher than the bottom nav */
|
||||
}
|
||||
|
||||
.nav-copyright a {
|
||||
color: #3F51B5;
|
||||
text-decoration: none;
|
||||
font-weight: 600;
|
||||
}
|
||||
|
||||
@media (max-width: 768px) {
|
||||
.nav-container {
|
||||
padding: 8px 0;
|
||||
justify-content: flex-start; /* Left align items when scrolling */
|
||||
}
|
||||
|
||||
.nav-item, .nav-btn {
|
||||
font-size: 0.8rem;
|
||||
}
|
||||
|
||||
.nav-item i, .nav-btn i {
|
||||
font-size: 1rem;
|
||||
}
|
||||
|
||||
.nav-copyright {
|
||||
font-size: 0.75rem;
|
||||
padding: 4px 0;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
||||
<!-- Copyright Notice -->
|
||||
<div class="nav-copyright">
|
||||
© {% now 'Y' %} <a href="https://github.com/S4NKALP">SANKALP</a>. All rights reserved.
|
||||
</div>
|
||||
@@ -1,329 +0,0 @@
|
||||
{% load static %}
|
||||
|
||||
<style>
|
||||
/* Modal Base Styles */
|
||||
.modal-overlay {
|
||||
display: none;
|
||||
position: fixed;
|
||||
top: 0;
|
||||
left: 0;
|
||||
right: 0;
|
||||
bottom: 0;
|
||||
background-color: rgba(0, 0, 0, 0.5);
|
||||
z-index: 10000;
|
||||
backdrop-filter: blur(4px);
|
||||
}
|
||||
|
||||
.modal-container {
|
||||
position: absolute;
|
||||
top: 50%;
|
||||
left: 50%;
|
||||
transform: translate(-50%, -50%);
|
||||
width: 90%;
|
||||
max-width: 500px;
|
||||
background-color: white;
|
||||
border-radius: 12px;
|
||||
box-shadow: 0 8px 24px rgba(0, 0, 0, 0.15);
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.modal-header {
|
||||
background: linear-gradient(135deg, #0D6EFD 0%, #0B5ED7 100%);
|
||||
color: white;
|
||||
padding: 16px 24px;
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.modal-title {
|
||||
margin: 0;
|
||||
font-size: 18px;
|
||||
font-weight: 600;
|
||||
}
|
||||
|
||||
.modal-close {
|
||||
background: none;
|
||||
border: none;
|
||||
color: white;
|
||||
font-size: 24px;
|
||||
cursor: pointer;
|
||||
line-height: 24px;
|
||||
padding: 0;
|
||||
opacity: 0.8;
|
||||
transition: opacity 0.2s ease;
|
||||
}
|
||||
|
||||
.modal-close:hover {
|
||||
opacity: 1;
|
||||
}
|
||||
|
||||
.modal-body {
|
||||
padding: 24px;
|
||||
}
|
||||
|
||||
.modal-footer {
|
||||
padding: 16px 24px;
|
||||
border-top: 1px solid #E9ECEF;
|
||||
display: flex;
|
||||
justify-content: flex-end;
|
||||
gap: 12px;
|
||||
}
|
||||
|
||||
/* Form Styles */
|
||||
.form-group {
|
||||
margin-bottom: 20px;
|
||||
}
|
||||
|
||||
.form-label {
|
||||
display: block;
|
||||
margin-bottom: 8px;
|
||||
font-weight: 500;
|
||||
color: #212529;
|
||||
}
|
||||
|
||||
.form-control {
|
||||
width: 100%;
|
||||
padding: 10px 12px;
|
||||
border: 1px solid #DEE2E6;
|
||||
border-radius: 6px;
|
||||
box-sizing: border-box;
|
||||
font-size: 14px;
|
||||
transition: border-color 0.2s ease, box-shadow 0.2s ease;
|
||||
}
|
||||
|
||||
.form-control:focus {
|
||||
border-color: #0D6EFD;
|
||||
box-shadow: 0 0 0 0.2rem rgba(13, 110, 253, 0.25);
|
||||
outline: none;
|
||||
}
|
||||
|
||||
textarea.form-control {
|
||||
resize: vertical;
|
||||
min-height: 80px;
|
||||
}
|
||||
|
||||
/* Button Styles */
|
||||
.btn {
|
||||
padding: 8px 16px;
|
||||
border-radius: 6px;
|
||||
font-size: 14px;
|
||||
font-weight: 500;
|
||||
cursor: pointer;
|
||||
border: none;
|
||||
transition: all 0.2s ease;
|
||||
}
|
||||
|
||||
.btn-primary {
|
||||
background-color: #0D6EFD;
|
||||
color: white;
|
||||
}
|
||||
|
||||
.btn-primary:hover {
|
||||
background-color: #0B5ED7;
|
||||
}
|
||||
|
||||
.btn-secondary {
|
||||
background-color: #6C757D;
|
||||
color: white;
|
||||
}
|
||||
|
||||
.btn-secondary:hover {
|
||||
background-color: #5C636A;
|
||||
}
|
||||
|
||||
/* Profile Picture Styles */
|
||||
.profile-picture-container {
|
||||
width: 120px;
|
||||
height: 120px;
|
||||
border-radius: 50%;
|
||||
overflow: hidden;
|
||||
margin-bottom: 20px;
|
||||
border: 3px solid #E9ECEF;
|
||||
}
|
||||
|
||||
.profile-picture {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
object-fit: cover;
|
||||
}
|
||||
|
||||
/* File Input Styles */
|
||||
.file-input-wrapper {
|
||||
position: relative;
|
||||
}
|
||||
|
||||
.file-input {
|
||||
opacity: 0;
|
||||
position: absolute;
|
||||
top: 0;
|
||||
left: 0;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.file-input-label {
|
||||
display: block;
|
||||
padding: 10px 12px;
|
||||
background-color: #F8F9FA;
|
||||
border: 1px dashed #DEE2E6;
|
||||
border-radius: 6px;
|
||||
text-align: center;
|
||||
cursor: pointer;
|
||||
transition: all 0.2s ease;
|
||||
}
|
||||
|
||||
.file-input-label:hover {
|
||||
background-color: #E9ECEF;
|
||||
border-color: #CED4DA;
|
||||
}
|
||||
</style>
|
||||
|
||||
<!-- Profile Modal -->
|
||||
<div id="profileModal" class="modal-overlay" onclick="if(event.target === this) closeProfileModal();">
|
||||
<div class="modal-container">
|
||||
<div class="modal-header">
|
||||
<h5 class="modal-title">Update Profile Picture</h5>
|
||||
<button class="modal-close" onclick="closeProfileModal()">×</button>
|
||||
</div>
|
||||
<div class="modal-body">
|
||||
<form action="{% url 'update_profile' %}" method="post" enctype="multipart/form-data">
|
||||
{% csrf_token %}
|
||||
<div class="text-center">
|
||||
{% if user.image %}
|
||||
<div class="profile-picture-container mx-auto">
|
||||
<img src="{{ user.image.url }}" alt="{{ user.name }}" class="profile-picture">
|
||||
</div>
|
||||
{% else %}
|
||||
<div class="profile-picture-container mx-auto">
|
||||
<img src="{% static 'img/user.png' %}" alt="Default User" class="profile-picture">
|
||||
</div>
|
||||
{% endif %}
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label class="form-label">Select New Profile Picture</label>
|
||||
<div class="file-input-wrapper">
|
||||
<label class="file-input-label">
|
||||
<i class="fas fa-cloud-upload-alt me-2"></i>Choose a file...
|
||||
<input type="file" name="profile_image" required class="file-input">
|
||||
</label>
|
||||
</div>
|
||||
</div>
|
||||
<div class="modal-footer">
|
||||
<button type="button" class="btn btn-secondary" onclick="closeProfileModal()">Cancel</button>
|
||||
<button type="submit" class="btn btn-primary">Update Picture</button>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Password Change Modal -->
|
||||
<div id="passwordModal" class="modal-overlay" onclick="if(event.target === this) closePasswordModal();">
|
||||
<div class="modal-container">
|
||||
<div class="modal-header">
|
||||
<h5 class="modal-title">Change Password</h5>
|
||||
<button class="modal-close" onclick="closePasswordModal()">×</button>
|
||||
</div>
|
||||
<div class="modal-body">
|
||||
<form action="{% url 'change_password' %}" method="post">
|
||||
{% csrf_token %}
|
||||
<div class="form-group">
|
||||
<label class="form-label" for="current_password">Current Password</label>
|
||||
<input type="password" id="current_password" name="current_password" required class="form-control">
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label class="form-label" for="new_password">New Password</label>
|
||||
<input type="password" id="new_password" name="new_password" required class="form-control">
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label class="form-label" for="confirm_password">Confirm New Password</label>
|
||||
<input type="password" id="confirm_password" name="confirm_password" required class="form-control">
|
||||
</div>
|
||||
<div class="modal-footer">
|
||||
<button type="button" class="btn btn-secondary" onclick="closePasswordModal()">Cancel</button>
|
||||
<button type="submit" class="btn btn-primary">Change Password</button>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Leave Request Modal -->
|
||||
<div id="customLeaveModal" class="modal-overlay" onclick="if(event.target === this) closeLeaveModal();">
|
||||
<div class="modal-container">
|
||||
<div class="modal-header">
|
||||
<h5 class="modal-title">Request Leave</h5>
|
||||
<button class="modal-close" onclick="closeLeaveModal()">×</button>
|
||||
</div>
|
||||
<div class="modal-body">
|
||||
<form action="{% if user.is_teacher %}{% url 'request_staff_leave' %}{% else %}{% url 'request_leave' %}{% endif %}" method="post">
|
||||
{% csrf_token %}
|
||||
<div class="form-group">
|
||||
<label class="form-label" for="startDate">Start Date</label>
|
||||
<input type="date" id="startDate" name="start_date" class="form-control" required>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label class="form-label" for="endDate">End Date</label>
|
||||
<input type="date" id="endDate" name="end_date" class="form-control" required>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label class="form-label" for="customLeaveMessage">Reason</label>
|
||||
<textarea id="customLeaveMessage" name="message" rows="3" class="form-control" required></textarea>
|
||||
</div>
|
||||
<div class="modal-footer">
|
||||
<button type="button" class="btn btn-secondary" onclick="closeLeaveModal()">Cancel</button>
|
||||
<button type="submit" class="btn btn-primary">Submit Request</button>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Confirmation Modal -->
|
||||
<div id="confirmationModal" class="modal-overlay" onclick="if(event.target === this) closeConfirmationModal();">
|
||||
<div class="modal-container" style="max-width: 400px;">
|
||||
<div class="modal-header">
|
||||
<h5 class="modal-title">Confirm Deletion</h5>
|
||||
<button class="modal-close" onclick="closeConfirmationModal()">×</button>
|
||||
</div>
|
||||
<div class="modal-body">
|
||||
<p class="mb-0">Are you sure you want to delete this file? This action cannot be undone.</p>
|
||||
</div>
|
||||
<div class="modal-footer">
|
||||
<button type="button" class="btn btn-secondary" onclick="closeConfirmationModal()">Cancel</button>
|
||||
<button type="button" class="btn btn-primary" id="confirmDeleteBtn">Delete</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Message Modal -->
|
||||
<div id="messageModal" class="modal-overlay" onclick="if(event.target === this) closeMessageModal();">
|
||||
<div class="modal-container" style="max-width: 400px;">
|
||||
<div class="modal-header">
|
||||
<h5 class="modal-title" id="messageModalTitle">Message</h5>
|
||||
<button class="modal-close" onclick="closeMessageModal()">×</button>
|
||||
</div>
|
||||
<div class="modal-body">
|
||||
<p class="mb-0" id="messageModalContent"></p>
|
||||
</div>
|
||||
<div class="modal-footer">
|
||||
<button type="button" class="btn btn-primary" onclick="closeMessageModal()">OK</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<script>
|
||||
function showMessageModal(title, message) {
|
||||
document.getElementById('messageModalTitle').textContent = title;
|
||||
document.getElementById('messageModalContent').textContent = message;
|
||||
document.getElementById('messageModal').style.display = 'block';
|
||||
document.body.style.overflow = 'hidden';
|
||||
}
|
||||
|
||||
function closeMessageModal() {
|
||||
document.getElementById('messageModal').style.display = 'none';
|
||||
document.body.style.overflow = '';
|
||||
}
|
||||
</script>
|
||||
@@ -1,247 +0,0 @@
|
||||
{% load static %}
|
||||
|
||||
<!-- Welcome Section -->
|
||||
<div class="row mb-4">
|
||||
<div class="col-12">
|
||||
<div class="welcome-card">
|
||||
<div class="welcome-content">
|
||||
<div class="user-info">
|
||||
<div class="profile-image">
|
||||
{% if user.image %}
|
||||
<img src="{{ user.image.url }}" alt="{{ user.name }}" class="profile-picture">
|
||||
{% else %}
|
||||
<img src="{% static 'img/user.png' %}" alt="Default User" class="profile-picture">
|
||||
{% endif %}
|
||||
</div>
|
||||
<div class="user-details">
|
||||
<h2>Welcome, {{ user.name }}!</h2>
|
||||
<div class="user-meta">
|
||||
<span class="user-role">
|
||||
{% if user.groups.all.0.name == "HOD" %}
|
||||
<i class="fas fa-user-shield"></i> Head of Department
|
||||
{% elif user.designation %}
|
||||
<i class="fas fa-user-tie"></i> {{ user.designation }}
|
||||
{% elif user.groups.all.0.name == "Parent" %}
|
||||
<i class="fas fa-user-friends"></i> Parent
|
||||
{% elif user.groups.all.0.name == "Student" %}
|
||||
<i class="fas fa-user-graduate"></i> Student
|
||||
{% else %}
|
||||
<i class="fas fa-user"></i> User
|
||||
{% endif %}
|
||||
</span>
|
||||
<span class="user-id">
|
||||
<i class="fas fa-id-card"></i> ID: {{ user.id }}
|
||||
</span>
|
||||
{% if user.phone %}
|
||||
<span class="user-phone">
|
||||
<i class="fas fa-phone"></i> {{ user.phone }}
|
||||
</span>
|
||||
{% endif %}
|
||||
{% if user.email %}
|
||||
<span class="user-email">
|
||||
<i class="fas fa-envelope"></i> {{ user.email }}
|
||||
</span>
|
||||
{% endif %}
|
||||
{% if user.department %}
|
||||
<span class="user-department">
|
||||
<i class="fas fa-building"></i> {{ user.department }}
|
||||
</span>
|
||||
{% endif %}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="user-actions">
|
||||
<button class="action-btn" onclick="openProfileModal()">
|
||||
<i class="fas fa-user-edit"></i>
|
||||
<span>Edit Profile</span>
|
||||
</button>
|
||||
<button class="action-btn" onclick="openPasswordModal()">
|
||||
<i class="fas fa-key"></i>
|
||||
<span>Change Password</span>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<style>
|
||||
.welcome-card {
|
||||
background: linear-gradient(135deg, #4e54c8, #8f94fb);
|
||||
border-radius: 15px;
|
||||
padding: clamp(1rem, 3vw, 1.5rem);
|
||||
color: white;
|
||||
box-shadow: 0 4px 15px rgba(0, 0, 0, 0.1);
|
||||
margin-bottom: 1.5rem;
|
||||
transition: all 0.3s ease;
|
||||
}
|
||||
|
||||
.welcome-content {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
flex-wrap: wrap;
|
||||
gap: clamp(0.75rem, 2vw, 1rem);
|
||||
}
|
||||
|
||||
.user-info {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: clamp(1rem, 2.5vw, 1.5rem);
|
||||
flex: 1;
|
||||
min-width: 280px;
|
||||
}
|
||||
|
||||
.profile-image {
|
||||
width: clamp(60px, 10vw, 80px);
|
||||
height: clamp(60px, 10vw, 80px);
|
||||
border-radius: 50%;
|
||||
border: 3px solid rgba(255, 255, 255, 0.3);
|
||||
overflow: hidden;
|
||||
background: rgba(255, 255, 255, 0.1);
|
||||
flex-shrink: 0;
|
||||
}
|
||||
|
||||
.profile-picture {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
object-fit: cover;
|
||||
}
|
||||
|
||||
.user-details {
|
||||
flex: 1;
|
||||
min-width: 200px;
|
||||
}
|
||||
|
||||
.user-details h2 {
|
||||
color: white;
|
||||
margin: 0 0 0.5rem 0;
|
||||
font-size: clamp(1.25rem, 3vw, 1.75rem);
|
||||
font-weight: 600;
|
||||
line-height: 1.2;
|
||||
}
|
||||
|
||||
.user-meta {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
flex-wrap: wrap;
|
||||
gap: clamp(0.5rem, 1.5vw, 1rem);
|
||||
}
|
||||
|
||||
.user-meta span {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 0.5rem;
|
||||
font-size: clamp(0.8rem, 2vw, 0.9rem);
|
||||
color: rgba(255, 255, 255, 0.9);
|
||||
background: rgba(255, 255, 255, 0.1);
|
||||
padding: 0.25rem 0.75rem;
|
||||
border-radius: 20px;
|
||||
white-space: nowrap;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
max-width: 100%;
|
||||
}
|
||||
|
||||
.user-meta i {
|
||||
font-size: clamp(0.8rem, 2vw, 0.9rem);
|
||||
flex-shrink: 0;
|
||||
}
|
||||
|
||||
.user-actions {
|
||||
display: flex;
|
||||
gap: clamp(0.5rem, 1.5vw, 0.75rem);
|
||||
margin-left: auto;
|
||||
}
|
||||
|
||||
.action-btn {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 0.5rem;
|
||||
padding: clamp(0.4rem, 1vw, 0.5rem) clamp(0.75rem, 2vw, 1rem);
|
||||
border: 1px solid rgba(255, 255, 255, 0.2);
|
||||
border-radius: 8px;
|
||||
background: rgba(255, 255, 255, 0.1);
|
||||
color: white;
|
||||
font-size: clamp(0.8rem, 2vw, 0.9rem);
|
||||
transition: all 0.3s ease;
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
.action-btn:hover {
|
||||
background: rgba(255, 255, 255, 0.2);
|
||||
border-color: rgba(255, 255, 255, 0.3);
|
||||
transform: translateY(-1px);
|
||||
}
|
||||
|
||||
@media (max-width: 1024px) {
|
||||
.welcome-content {
|
||||
gap: 1.25rem;
|
||||
}
|
||||
|
||||
.user-meta {
|
||||
max-width: 100%;
|
||||
}
|
||||
}
|
||||
|
||||
@media (max-width: 768px) {
|
||||
.welcome-content {
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
text-align: center;
|
||||
gap: 1.5rem;
|
||||
}
|
||||
|
||||
.user-info {
|
||||
flex-direction: column;
|
||||
text-align: center;
|
||||
min-width: auto;
|
||||
}
|
||||
|
||||
.user-details {
|
||||
min-width: auto;
|
||||
}
|
||||
|
||||
.user-meta {
|
||||
justify-content: center;
|
||||
}
|
||||
|
||||
.user-actions {
|
||||
width: 100%;
|
||||
justify-content: center;
|
||||
margin-left: 0;
|
||||
}
|
||||
}
|
||||
|
||||
@media (max-width: 480px) {
|
||||
.welcome-card {
|
||||
border-radius: 12px;
|
||||
}
|
||||
|
||||
.user-meta {
|
||||
flex-direction: column;
|
||||
gap: 0.5rem;
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.user-meta span {
|
||||
width: 100%;
|
||||
justify-content: center;
|
||||
}
|
||||
|
||||
.user-actions {
|
||||
flex-direction: column;
|
||||
}
|
||||
|
||||
.action-btn {
|
||||
width: 100%;
|
||||
justify-content: center;
|
||||
}
|
||||
}
|
||||
|
||||
@media (hover: none) {
|
||||
.action-btn:hover {
|
||||
transform: none;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
@@ -1,888 +0,0 @@
|
||||
{% extends 'shared/base.html' %}
|
||||
{% load static %}
|
||||
{% load tz %}
|
||||
{% load app_filters %}
|
||||
|
||||
|
||||
{% block content %}
|
||||
<!-- Add CSRF token to the page -->
|
||||
{% csrf_token %}
|
||||
|
||||
<div class="container-fluid py-4">
|
||||
<!-- Welcome Section -->
|
||||
{% include 'shared/welcome_section.html' with user=student %}
|
||||
|
||||
<!-- Dashboard Content -->
|
||||
<div id="dashboardSection" class="content-section" style="display: block;">
|
||||
<!-- Summary Cards -->
|
||||
<div class="row mb-4">
|
||||
<!-- Attendance Summary Card -->
|
||||
<div class="col-md-4 col-lg-3 mb-3">
|
||||
<div class="summary-card h-100">
|
||||
<div class="card-header">
|
||||
<h5 class="card-title mb-0 small"><i class="fas fa-calendar-check me-1"></i> Attendance Summary</h5>
|
||||
</div>
|
||||
<div class="card-body p-2">
|
||||
<div class="d-flex justify-content-between align-items-center mb-3">
|
||||
<div>
|
||||
<span class="d-block text-muted small">Days Attended</span>
|
||||
<h3>{{ attendance_summary.classes_attended }}</h3>
|
||||
{% if attendance_summary.actual_classes %}
|
||||
<span class="d-block text-muted small mt-2">Classes Recorded</span>
|
||||
<h5>{{ attendance_summary.actual_classes }}</h5>
|
||||
{% endif %}
|
||||
</div>
|
||||
<div class="text-end">
|
||||
<span class="d-block text-muted small">Attendance Rate</span>
|
||||
<h3 class="text-primary">{{ attendance_summary.attendance_percentage|floatformat:1 }}%</h3>
|
||||
{% if attendance_summary.actual_percentage %}
|
||||
<span class="d-block text-muted small mt-2">Rate (Recorded Only)</span>
|
||||
<h5 class="text-primary">{{ attendance_summary.actual_percentage|floatformat:1 }}%</h5>
|
||||
{% endif %}
|
||||
</div>
|
||||
</div>
|
||||
<div class="d-flex justify-content-between mb-1">
|
||||
<div><small>0%</small></div>
|
||||
<div><small>100%</small></div>
|
||||
</div>
|
||||
<div class="progress mb-2 position-relative" style="height: 15px; border-radius: 20px; background-color: #f0f0f0;">
|
||||
<!-- Target marker at 75% -->
|
||||
<div class="position-absolute" style="left: 75%; top: -10px; bottom: -10px; width: 2px; background-color: #dc3545;"></div>
|
||||
<div class="progress-bar {% if attendance_summary.attendance_percentage < 75 %}bg-danger{% else %}bg-success{% endif %}"
|
||||
role="progressbar"
|
||||
style="width: {{ attendance_summary.attendance_percentage }}%;"
|
||||
aria-valuenow="{{ attendance_summary.attendance_percentage }}"
|
||||
aria-valuemin="0"
|
||||
aria-valuemax="100"></div>
|
||||
</div>
|
||||
|
||||
<div class="d-flex justify-content-between align-items-center">
|
||||
<small class="text-muted">Minimum required: 75%</small>
|
||||
{% if attendance_summary.attendance_percentage < 75 %}
|
||||
<small class="text-danger"><i class="fas fa-exclamation-triangle"></i> Below minimum requirement</small>
|
||||
{% endif %}
|
||||
</div>
|
||||
<!-- <small class="text-muted d-block text-center">
|
||||
Based on attendance out of {% if student.course and student.course.duration_type == "Year" %}365 days per year{% else %}180 days per semester{% endif %}
|
||||
</small> -->
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Course Progress Card -->
|
||||
<div class="col-md-4 col-lg-3 mb-3">
|
||||
<div class="summary-card h-100">
|
||||
<div class="card-header">
|
||||
<h5 class="card-title mb-0 small"><i class="fas fa-graduation-cap me-1"></i> Course Progress</h5>
|
||||
</div>
|
||||
<div class="card-body p-2">
|
||||
<div class="d-flex justify-content-between align-items-center mb-2">
|
||||
<div>
|
||||
<small class="text-muted d-block mb-1">Total Courses</small>
|
||||
<h3 class="fw-bold mb-0">{{ course_summary.total_courses }}</h3>
|
||||
</div>
|
||||
<div class="text-end">
|
||||
<small class="text-muted d-block mb-1">Completion Rate</small>
|
||||
<h3 class="fw-bold text-success mb-0">{{ course_summary.completion_percentage|floatformat:1 }}%</h3>
|
||||
</div>
|
||||
</div>
|
||||
<div class="d-flex justify-content-between mt-3">
|
||||
<div>
|
||||
<small class="text-muted d-block">Completed</small>
|
||||
<span class="badge bg-success p-2"><i class="fas fa-check me-1"></i> {{ course_summary.completed_courses }}</span>
|
||||
</div>
|
||||
<div class="text-end">
|
||||
<small class="text-muted d-block">Ongoing</small>
|
||||
<span class="badge bg-warning p-2"><i class="fas fa-spinner me-1"></i> {{ course_summary.ongoing_courses }}</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Recent Activity Card -->
|
||||
<div class="col-md-4 col-lg-3 mb-3">
|
||||
<div class="summary-card h-100">
|
||||
<div class="card-header">
|
||||
<h5 class="card-title mb-0 small"><i class="fas fa-clock me-1"></i> Recent Activity</h5>
|
||||
</div>
|
||||
<div class="card-body p-2">
|
||||
<div class="activity-scroll" style="max-height: 150px; overflow-y: auto; padding-right: 5px; scrollbar-width: thin;">
|
||||
{% for leave in recent_leaves %}
|
||||
<div class="d-flex align-items-center mb-2">
|
||||
<i class="fas fa-calendar-alt text-primary me-2"></i>
|
||||
<div>
|
||||
<small class="mb-0 d-block">Leave Request ({{ leave.date|date:"M d" }})</small>
|
||||
<span class="badge {% if leave.status == 0 %}bg-warning{% elif leave.status == 1 %}bg-success{% else %}bg-danger{% endif %}">{{ leave.get_status_display }}</span>
|
||||
<small class="text-muted">{{ leave.created_at|timesince }} ago</small>
|
||||
</div>
|
||||
</div>
|
||||
{% endfor %}
|
||||
|
||||
{% for feedback in recent_feedback %}
|
||||
<div class="d-flex align-items-center mb-2">
|
||||
<i class="fas fa-comment text-success me-2"></i>
|
||||
<div>
|
||||
<small class="mb-0 d-block">Gave feedback {% if feedback.teacher %}to {{ feedback.teacher.name }}{% endif %}</small>
|
||||
<div>
|
||||
{% for i in "12345"|make_list %}
|
||||
{% if forloop.counter <= feedback.rating %}
|
||||
<i class="fas fa-star text-warning" style="font-size: 0.7rem;"></i>
|
||||
{% else %}
|
||||
<i class="far fa-star text-warning" style="font-size: 0.7rem;"></i>
|
||||
{% endif %}
|
||||
{% endfor %}
|
||||
<small class="text-muted">{{ feedback.created_at|timesince }} ago</small>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
{% endfor %}
|
||||
|
||||
{% for notice in notices %}
|
||||
<div class="d-flex align-items-center mb-2">
|
||||
<i class="fas fa-bullhorn text-info me-2"></i>
|
||||
<div>
|
||||
<small class="mb-0 d-block">{{ notice.title }}</small>
|
||||
<small class="text-muted">{{ notice.created_at|timesince }} ago</small>
|
||||
</div>
|
||||
</div>
|
||||
{% endfor %}
|
||||
|
||||
{% for record in attendance_records %}
|
||||
<div class="d-flex align-items-center mb-2">
|
||||
<i class="fas fa-user-check {% if record.student_attend %}text-success{% else %}text-danger{% endif %} me-2"></i>
|
||||
<div>
|
||||
<small class="mb-0 d-block">{% if record.student_attend %}Attended{% else %}Missed{% endif %} {{ record.attendance.routine.subject.name|truncatechars:15 }}</small>
|
||||
<small class="text-muted">{{ record.attendance.date|date:"M d" }}</small>
|
||||
</div>
|
||||
</div>
|
||||
{% endfor %}
|
||||
|
||||
{% if not recent_leaves and not recent_feedback and not notices and not attendance_records %}
|
||||
<div class="text-center">
|
||||
<small class="text-muted">No recent activity</small>
|
||||
</div>
|
||||
{% endif %}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Leave Status Card -->
|
||||
<div class="col-md-4 col-lg-3 mb-3">
|
||||
<div class="summary-card h-100">
|
||||
<div class="card-header">
|
||||
<h5 class="card-title mb-0 small"><i class="fas fa-calendar-minus me-1"></i> Leave Status</h5>
|
||||
</div>
|
||||
<div class="card-body p-2">
|
||||
<div class="d-flex justify-content-between mb-2">
|
||||
<small class="mb-0">Pending</small>
|
||||
<span class="badge leave-pending">{{ pending_leaves }}</span>
|
||||
</div>
|
||||
<div class="d-flex justify-content-between mb-2">
|
||||
<small class="mb-0">Approved</small>
|
||||
<span class="badge leave-approved">{{ approved_leaves }}</span>
|
||||
</div>
|
||||
<div class="d-flex justify-content-between mb-2">
|
||||
<small class="mb-0">Rejected</small>
|
||||
<span class="badge leave-rejected">{{ rejected_leaves }}</span>
|
||||
</div>
|
||||
<button type="button" class="btn btn-sm btn-outline-primary w-100 mt-1" id="requestLeaveBtn" onclick="openLeaveModal()">
|
||||
<i class="fas fa-plus-circle me-1"></i> Request
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Course Overview Section -->
|
||||
<div class="row mb-4">
|
||||
<div class="col-12">
|
||||
<div class="content-card">
|
||||
<h4 class="card-title mb-3 d-flex align-items-center">
|
||||
<i class="fas fa-graduation-cap me-2"></i> Course Details
|
||||
</h4>
|
||||
|
||||
{% if course_details %}
|
||||
{% for course in course_details %}
|
||||
<div class="mb-4">
|
||||
<div class="d-flex justify-content-between align-items-center mb-3">
|
||||
<h5 class="mb-0">{{ course.name }}</h5>
|
||||
<span class="status-badge {% if course.progress_status == 'Completed' %}bg-success{% elif course.progress_status == 'In Progress' %}bg-warning{% else %}bg-secondary{% endif %}">
|
||||
{{ course.progress_status }}
|
||||
</span>
|
||||
</div>
|
||||
|
||||
<!-- Course Progress -->
|
||||
<div class="progress mb-3">
|
||||
<div class="progress-bar bg-primary" role="progressbar"
|
||||
aria-valuenow="{{ course.completion_percentage }}" aria-valuemin="0" aria-valuemax="100"
|
||||
style="--progress-width: {{ course.completion_percentage }}%">
|
||||
{{ course.completion_percentage }}%
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Course Information -->
|
||||
<div class="row mb-4">
|
||||
<div class="col-md-6">
|
||||
<table class="table">
|
||||
<tbody>
|
||||
<tr>
|
||||
<th>Course Name</th>
|
||||
<td>{{ course.name }}</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<th>Course Code</th>
|
||||
<td>{{ course.code|default:"-" }}</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<th>Description</th>
|
||||
<td>{{ course.description|default:"No description available" }}</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<th>Start Date</th>
|
||||
<td>{{ course.start_date|date:"M d, Y" }}</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<th>Duration Type</th>
|
||||
<td>{{ course.duration_type }}</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<th>Total Duration</th>
|
||||
<td>
|
||||
{% if course.duration_type == "Semester" %}
|
||||
{{ course.duration }} years ({{ course.duration|multiply:2 }} semesters)
|
||||
{% else %}
|
||||
{{ course.duration }} years
|
||||
{% endif %}
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<th>Status</th>
|
||||
<td>
|
||||
<span class="badge {% if course.is_active %}bg-success{% else %}bg-secondary{% endif %}">
|
||||
{{ course.is_active|yesno:"Active,Inactive" }}
|
||||
</span>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<th>Expected End Date</th>
|
||||
<td>{{ course.expected_end_date|date:"M d, Y" }}</td>
|
||||
</tr>
|
||||
{% if course.actual_end_date %}
|
||||
<tr>
|
||||
<th>Actual End Date</th>
|
||||
<td>{{ course.actual_end_date|date:"M d, Y" }}</td>
|
||||
</tr>
|
||||
{% endif %}
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
<div class="col-md-6">
|
||||
<table class="table">
|
||||
<tbody>
|
||||
<tr>
|
||||
<th>{% if course.duration_type == "Semester" %}Current Semester{% else %}Current Year{% endif %}</th>
|
||||
<td>{% if course.duration_type == "Semester" %}Semester{% else %}Year{% endif %} {{ course.current_period }}</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<th>{% if course.duration_type == "Semester" %}Semester{% else %}Year{% endif %} Start</th>
|
||||
<td>{{ course.period_start_date|date:"M d, Y" }}</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<th>{% if course.duration_type == "Semester" %}Semester{% else %}Year{% endif %} End</th>
|
||||
<td>{{ course.period_end_date|date:"M d, Y" }}</td>
|
||||
</tr>
|
||||
{% if course.remaining_days is not None %}
|
||||
<tr>
|
||||
<th>{% if course.duration_type == "Semester" %}Semester{% else %}Year{% endif %} Days Remaining</th>
|
||||
<td>{{ course.remaining_days }} days</td>
|
||||
</tr>
|
||||
{% endif %}
|
||||
{% if course.total_remaining_days is not None %}
|
||||
<tr>
|
||||
<th>Total Course Days Remaining</th>
|
||||
<td>{{ course.total_remaining_days }} days</td>
|
||||
</tr>
|
||||
{% endif %}
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
{% endfor %}
|
||||
{% else %}
|
||||
<div class="alert alert-info">
|
||||
<i class="fas fa-info-circle me-2"></i>No courses assigned yet.
|
||||
</div>
|
||||
{% endif %}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="content-section" id="subjectsSection" style="display: none;">
|
||||
<div class="content-card">
|
||||
<h3 class="text-xl font-semibold mb-4">
|
||||
<i class="fas fa-book me-2"></i>Current {% if active_tracking.course.duration_type == "Semester" %}Semester{% else %}Year{% endif %} Subjects
|
||||
</h3>
|
||||
{% if current_subjects %}
|
||||
<div class="row g-3">
|
||||
{% for subject in current_subjects %}
|
||||
<div class="col-md-4 col-lg-3">
|
||||
<div class="card mb-3">
|
||||
<div class="card-body">
|
||||
<h5 class="card-title">{{ subject.name }}</h5>
|
||||
<p class="card-text">
|
||||
<small class="text-muted">{% if subject.course.duration_type == "Semester" %}Semester{% else %}Year{% endif %}: {{ subject.period_or_year }}</small>
|
||||
</p>
|
||||
<div class="d-flex justify-content-between align-items-center">
|
||||
<button class="btn btn-primary btn-sm view-subject-files" data-subject-id="{{ subject.id }}" onclick="openSubjectFilesModal('{{ subject.id }}')">
|
||||
View Files
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
{% endfor %}
|
||||
</div>
|
||||
{% else %}
|
||||
<div class="alert alert-info">
|
||||
<i class="fas fa-info-circle me-2"></i>No subjects assigned for the current {% if active_tracking.course.duration_type == "Semester" %}semester{% else %}year{% endif %}.
|
||||
</div>
|
||||
{% endif %}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="content-section" id="routineSection" style="display: none;">
|
||||
<div class="content-card">
|
||||
<h3 class="text-xl font-semibold mb-4">
|
||||
<i class="fas fa-calendar-alt mr-2"></i> Class Routine
|
||||
</h3>
|
||||
<div class="table-responsive">
|
||||
<table class="table">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>Time</th>
|
||||
<th>Subject</th>
|
||||
<th>Code</th>
|
||||
<th>Teacher</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{% if current_semester_routines %}
|
||||
{% for routine in current_semester_routines %}
|
||||
<tr>
|
||||
<td>{{ routine.start_time|time:"H:i" }} - {{ routine.end_time|time:"H:i" }}</td>
|
||||
<td>{{ routine.subject.name }}</td>
|
||||
<td>{{ routine.subject.code|default:"-" }}</td>
|
||||
<td>{{ routine.teacher.name }}</td>
|
||||
</tr>
|
||||
{% endfor %}
|
||||
{% elif has_routines %}
|
||||
<tr>
|
||||
<td colspan="4" class="text-center">No routines for current semester (Semester {{ student.current_semester }}).</td>
|
||||
</tr>
|
||||
{% else %}
|
||||
<tr>
|
||||
<td colspan="4" class="text-center">No routines assigned yet.</td>
|
||||
</tr>
|
||||
{% endif %}
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="content-section" id="leaveSection" style="display: none;">
|
||||
<div class="content-card">
|
||||
<h3 class="text-xl font-semibold mb-4">
|
||||
<i class="fas fa-calendar-times mr-2"></i> Leave Management
|
||||
</h3>
|
||||
<div class="row mb-4">
|
||||
<div class="col-12">
|
||||
<button class="btn btn-primary" onclick="openLeaveModal()">
|
||||
<i class="fas fa-plus me-2"></i>Request Leave
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
<div class="table-responsive">
|
||||
<table class="table table-responsive table-hover">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>Start Date</th>
|
||||
<th>End Date</th>
|
||||
<th>Message</th>
|
||||
<th>Status</th>
|
||||
<th>Created At</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{% for leave in student_leaves %}
|
||||
<tr>
|
||||
<td>{{ leave.start_date|date:"M d, Y" }}</td>
|
||||
<td>{{ leave.end_date|date:"M d, Y" }}</td>
|
||||
<td>{{ leave.message }}</td>
|
||||
<td>
|
||||
<span class="status-badge {% if leave.status == 1 %}bg-success{% elif leave.status == 2 %}bg-danger{% else %}bg-warning{% endif %}">
|
||||
{{ leave.get_status_display }}
|
||||
</span>
|
||||
</td>
|
||||
<td>{{ leave.created_at|date:"M d, Y H:i" }}</td>
|
||||
</tr>
|
||||
{% empty %}
|
||||
<tr>
|
||||
<td colspan="5" class="text-center">No leave requests found.</td>
|
||||
</tr>
|
||||
{% endfor %}
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="content-section" id="feedbackSection" style="display: none;">
|
||||
<div class="content-card">
|
||||
<div class="d-flex justify-content-between align-items-center mb-4">
|
||||
<h3 class="mb-0">
|
||||
<i class="fas fa-comments me-2"></i> Feedback
|
||||
</h3>
|
||||
<button class="btn btn-primary" onclick="openFeedbackModal()">
|
||||
<i class="fas fa-plus me-2"></i> Give Feedback
|
||||
</button>
|
||||
</div>
|
||||
|
||||
<div class="row">
|
||||
<!-- Teacher Feedback -->
|
||||
<div class="col-md-6 mb-4">
|
||||
<h4 class="mb-3">Teacher Feedback</h4>
|
||||
{% if student.feedbacks.all %}
|
||||
<div class="feedback-list">
|
||||
{% for feedback in student.feedbacks.all %}
|
||||
<div class="feedback-card">
|
||||
<div class="feedback-header">
|
||||
<div class="d-flex justify-content-between align-items-center w-100">
|
||||
<div>
|
||||
<h5 class="mb-1">{{ feedback.teacher.name }}</h5>
|
||||
<small class="text-muted">{{ feedback.teacher.designation }}</small>
|
||||
</div>
|
||||
<div class="rating-stars">
|
||||
{% for i in "12345"|make_list %}
|
||||
{% if forloop.counter <= feedback.rating %}
|
||||
<i class="fas fa-star text-warning"></i>
|
||||
{% else %}
|
||||
<i class="far fa-star text-warning"></i>
|
||||
{% endif %}
|
||||
{% endfor %}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="feedback-message mt-3">
|
||||
<p>{{ feedback.feedback_text }}</p>
|
||||
</div>
|
||||
<div class="feedback-footer mt-2">
|
||||
<small class="text-muted">{{ feedback.created_at|timesince }} ago</small>
|
||||
</div>
|
||||
</div>
|
||||
{% endfor %}
|
||||
</div>
|
||||
{% else %}
|
||||
<div class="empty-state">
|
||||
<i class="fas fa-comments"></i>
|
||||
<p>No teacher feedback submitted yet.</p>
|
||||
</div>
|
||||
{% endif %}
|
||||
</div>
|
||||
|
||||
<!-- Institute Feedback -->
|
||||
<div class="col-md-6 mb-4">
|
||||
<h4 class="mb-3">Institute Feedback</h4>
|
||||
{% if institute_feedback %}
|
||||
<div class="feedback-list">
|
||||
{% for feedback in institute_feedback %}
|
||||
<div class="feedback-card">
|
||||
<div class="feedback-header">
|
||||
<div class="d-flex justify-content-between align-items-center w-100">
|
||||
<div>
|
||||
<h5 class="mb-1">{{ feedback.feedback_type }}</h5>
|
||||
<small class="text-muted">{{ feedback.created_at|date:"M d, Y" }}</small>
|
||||
</div>
|
||||
<div class="rating-stars">
|
||||
{% for i in "12345"|make_list %}
|
||||
{% if forloop.counter <= feedback.rating %}
|
||||
<i class="fas fa-star text-warning"></i>
|
||||
{% else %}
|
||||
<i class="far fa-star text-warning"></i>
|
||||
{% endif %}
|
||||
{% endfor %}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="feedback-message mt-3">
|
||||
<p>{{ feedback.feedback_text }}</p>
|
||||
</div>
|
||||
<div class="feedback-footer mt-2">
|
||||
<small class="text-muted">{{ feedback.created_at|timesince }} ago</small>
|
||||
</div>
|
||||
</div>
|
||||
{% endfor %}
|
||||
</div>
|
||||
{% else %}
|
||||
<div class="empty-state">
|
||||
<i class="fas fa-building"></i>
|
||||
<p>No institute feedback submitted yet.</p>
|
||||
</div>
|
||||
{% endif %}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="content-section" id="noticeSection" style="display: none;">
|
||||
<div class="content-card">
|
||||
<h3 class="text-xl font-semibold mb-4">
|
||||
<i class="fas fa-bullhorn mr-2"></i> Notices
|
||||
</h3>
|
||||
<div class="row">
|
||||
{% for notice in notices %}
|
||||
<div class="col-md-6 mb-4">
|
||||
<div class="card h-100">
|
||||
{% if notice.image %}
|
||||
<img src="{{ notice.image.url }}" class="card-img-top" alt="{{ notice.title }}">
|
||||
{% endif %}
|
||||
<div class="card-body">
|
||||
<h5 class="card-title">{{ notice.title }}</h5>
|
||||
<p class="card-text">{{ notice.message }}</p>
|
||||
<div class="d-flex justify-content-between align-items-center">
|
||||
<small class="text-muted">{{ notice.created_at|date:"M d, Y" }}</small>
|
||||
{% if notice.file %}
|
||||
<a href="{{ notice.file.url }}" class="btn btn-sm btn-primary" target="_blank">
|
||||
<i class="fas fa-download me-1"></i>Download
|
||||
</a>
|
||||
{% endif %}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
{% empty %}
|
||||
<div class="col-12">
|
||||
<p class="text-muted text-center">No notices available.</p>
|
||||
</div>
|
||||
{% endfor %}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="content-section" id="profileSection" style="display: none;">
|
||||
<div class="content-card">
|
||||
<h3 class="text-xl font-semibold mb-4">
|
||||
<i class="fas fa-user mr-2"></i> Profile Information
|
||||
</h3>
|
||||
<div class="row">
|
||||
<!-- Profile Header with Image and Actions -->
|
||||
<div class="col-12 mb-4">
|
||||
<div class="d-flex flex-wrap align-items-center justify-content-between">
|
||||
<div class="d-flex align-items-center mb-3 mb-md-0">
|
||||
{% if student.image %}
|
||||
<div class="profile-picture-container profile-md">
|
||||
<img src="{{ student.image.url }}" alt="{{ student.name }}" class="profile-picture">
|
||||
</div>
|
||||
{% else %}
|
||||
<div class="profile-picture-container profile-md">
|
||||
<img src="{% static 'img/user.png' %}" alt="Default User" class="profile-picture">
|
||||
</div>
|
||||
{% endif %}
|
||||
<div>
|
||||
<h4 class="mb-1">{{ student.name }}</h4>
|
||||
<p class="mb-0 text-muted">
|
||||
<span class="me-3"><i class="fas fa-id-card me-1"></i> {{ student.id }}</span>
|
||||
<span class="me-3"><i class="fas fa-envelope me-1"></i> {{ student.email }}</span>
|
||||
<span><i class="fas fa-phone me-1"></i> {{ student.phone }}</span>
|
||||
</p>
|
||||
<div class="mt-2">
|
||||
<span class="badge {% if student.status == 'Active' %}bg-success{% elif student.status == 'Leave' %}bg-warning{% else %}bg-secondary{% endif %} me-2">
|
||||
{{ student.status }}
|
||||
</span>
|
||||
{% if student.course %}
|
||||
<span class="badge bg-info me-2">{{ student.course.name }}</span>
|
||||
{% endif %}
|
||||
<span class="badge bg-primary">Semester {{ student.current_semester }}</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="profile-actions d-flex gap-2">
|
||||
<button class="btn btn-sm btn-primary" onclick="openProfileModal()">
|
||||
<i class="fas fa-camera me-1"></i> Update Photo
|
||||
</button>
|
||||
<button class="btn btn-sm btn-secondary" onclick="openPasswordModal()">
|
||||
<i class="fas fa-key me-1"></i> Change Password
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Personal Information Section -->
|
||||
<div class="col-md-6 mb-4">
|
||||
<div class="card h-100 border-0 shadow-sm">
|
||||
<div class="card-header bg-light py-2">
|
||||
<h5 class="card-title mb-0"><i class="fas fa-user-circle me-2"></i>Personal Information</h5>
|
||||
</div>
|
||||
<div class="card-body">
|
||||
<div class="row g-3">
|
||||
<div class="col-md-6">
|
||||
<small class="text-muted d-block">Full Name</small>
|
||||
<p class="mb-0 fw-medium">{{ student.name }}</p>
|
||||
</div>
|
||||
<div class="col-md-6">
|
||||
<small class="text-muted d-block">Gender</small>
|
||||
<p class="mb-0 fw-medium">{{ student.gender }}</p>
|
||||
</div>
|
||||
<div class="col-md-6">
|
||||
<small class="text-muted d-block">Birth Date</small>
|
||||
<p class="mb-0 fw-medium">{{ student.birth_date|date:"M d, Y" }}</p>
|
||||
</div>
|
||||
<div class="col-md-6">
|
||||
<small class="text-muted d-block">Marital Status</small>
|
||||
<p class="mb-0 fw-medium">{{ student.marital_status }}</p>
|
||||
</div>
|
||||
<div class="col-md-6">
|
||||
<small class="text-muted d-block">Citizenship No.</small>
|
||||
<p class="mb-0 fw-medium">{{ student.citizenship_no|default:"Not provided" }}</p>
|
||||
</div>
|
||||
<div class="col-md-6">
|
||||
<small class="text-muted d-block">Joining Date</small>
|
||||
<p class="mb-0 fw-medium">{{ student.joining_date|date:"M d, Y" }}</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Contact & Parent Information -->
|
||||
<div class="col-md-6 mb-4">
|
||||
<div class="card h-100 border-0 shadow-sm">
|
||||
<div class="card-header bg-light py-2">
|
||||
<h5 class="card-title mb-0"><i class="fas fa-address-book me-2"></i>Contact Information</h5>
|
||||
</div>
|
||||
<div class="card-body">
|
||||
<div class="row g-3">
|
||||
<div class="col-md-6">
|
||||
<small class="text-muted d-block">Email Address</small>
|
||||
<p class="mb-0 fw-medium">{{ student.email }}</p>
|
||||
</div>
|
||||
<div class="col-md-6">
|
||||
<small class="text-muted d-block">Phone Number</small>
|
||||
<p class="mb-0 fw-medium">{{ student.phone }}</p>
|
||||
</div>
|
||||
<div class="col-md-6">
|
||||
<small class="text-muted d-block">Parent Name</small>
|
||||
<p class="mb-0 fw-medium">{{ student.parent_name|default:"Not provided" }}</p>
|
||||
</div>
|
||||
<div class="col-md-6">
|
||||
<small class="text-muted d-block">Parent Phone</small>
|
||||
<p class="mb-0 fw-medium">{{ student.parent_phone|default:"Not provided" }}</p>
|
||||
</div>
|
||||
<div class="col-12">
|
||||
<small class="text-muted d-block">Temporary Address</small>
|
||||
<p class="mb-0 fw-medium">{{ student.temporary_address|default:"Not provided" }}</p>
|
||||
</div>
|
||||
<div class="col-12">
|
||||
<small class="text-muted d-block">Permanent Address</small>
|
||||
<p class="mb-0 fw-medium">{{ student.permanent_address|default:"Not provided" }}</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Academic Information -->
|
||||
<div class="col-12 mb-4">
|
||||
<div class="card border-0 shadow-sm">
|
||||
<div class="card-header bg-light py-2">
|
||||
<h5 class="card-title mb-0"><i class="fas fa-graduation-cap me-2"></i>Academic Information</h5>
|
||||
</div>
|
||||
<div class="card-body">
|
||||
<div class="row g-3">
|
||||
<div class="col-md-3">
|
||||
<small class="text-muted d-block">Course</small>
|
||||
<p class="mb-0 fw-medium">{{ student.course.name|default:"Not assigned" }}</p>
|
||||
</div>
|
||||
<div class="col-md-3">
|
||||
<small class="text-muted d-block">Current Semester</small>
|
||||
<p class="mb-0 fw-medium">{{ student.current_semester }}</p>
|
||||
</div>
|
||||
<div class="col-md-3">
|
||||
<small class="text-muted d-block">Joining Date</small>
|
||||
<p class="mb-0 fw-medium">{{ student.joining_date|date:"M d, Y" }}</p>
|
||||
</div>
|
||||
<div class="col-md-3">
|
||||
<small class="text-muted d-block">Batches</small>
|
||||
<p class="mb-0">
|
||||
{% for batch in student.batches.all %}
|
||||
<span class="badge bg-info me-1">{{ batch.name }}</span>
|
||||
{% empty %}
|
||||
<span class="text-muted">No batches assigned</span>
|
||||
{% endfor %}
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Common Modals -->
|
||||
{% include 'shared/common_modals.html' with user=student %}
|
||||
|
||||
<!-- Student-specific Modals -->
|
||||
{% include 'student/modals/pdf_viewer_modal.html' %}
|
||||
{% include 'student/modals/subject_files_modal.html' %}
|
||||
{% include 'student/modals/feedback_modal.html' %}
|
||||
|
||||
|
||||
<!-- Add fix script -->
|
||||
<script src="{% static 'js/student_modals.js' %}"></script>
|
||||
<script src="{% static 'js/navigation.js' %}"></script>
|
||||
<script src="{% static 'js/rating.js' %}"></script>
|
||||
|
||||
<!-- Bottom Navigation -->
|
||||
{% include 'shared/bottom_nav.html' with user=student %}
|
||||
|
||||
<!-- Add a new attendance section -->
|
||||
<div class="content-section" id="attendanceSection" style="display: none;">
|
||||
<div class="content-card">
|
||||
<h3 class="text-xl font-semibold mb-4">
|
||||
<i class="fas fa-clipboard-check me-2"></i> Attendance Record
|
||||
</h3>
|
||||
|
||||
<!-- Attendance Summary -->
|
||||
<div class="row mb-4">
|
||||
<div class="col-md-4">
|
||||
<div class="card border-0 shadow-sm mb-3">
|
||||
<div class="card-body">
|
||||
<h5 class="card-title mb-3"><i class="fas fa-chart-pie text-primary me-2"></i>Attendance Summary</h5>
|
||||
<div class="d-flex justify-content-between align-items-center mb-3">
|
||||
<div>
|
||||
<span class="d-block text-muted small">Days Attended</span>
|
||||
<h3>{{ attendance_summary.classes_attended }}</h3>
|
||||
{% if attendance_summary.actual_classes %}
|
||||
<span class="d-block text-muted small mt-2">Classes Recorded</span>
|
||||
<h5>{{ attendance_summary.actual_classes }}</h5>
|
||||
{% endif %}
|
||||
</div>
|
||||
<div class="text-end">
|
||||
<span class="d-block text-muted small">Attendance Rate</span>
|
||||
<h3 class="text-primary">{{ attendance_summary.attendance_percentage|floatformat:1 }}%</h3>
|
||||
{% if attendance_summary.actual_percentage %}
|
||||
<span class="d-block text-muted small mt-2">Rate (Recorded Only)</span>
|
||||
<h5 class="text-primary">{{ attendance_summary.actual_percentage|floatformat:1 }}%</h5>
|
||||
{% endif %}
|
||||
</div>
|
||||
</div>
|
||||
<div class="progress mb-2 position-relative" style="height: 15px; border-radius: 20px; background-color: #f0f0f0;">
|
||||
<!-- Target marker at 75% -->
|
||||
<div class="position-absolute" style="left: 75%; top: -10px; bottom: -10px; width: 2px; background-color: #dc3545;"></div>
|
||||
<div class="progress-bar {% if attendance_summary.attendance_percentage < 75 %}bg-danger{% else %}bg-success{% endif %}"
|
||||
role="progressbar"
|
||||
style="width: {{ attendance_summary.attendance_percentage }}%;"
|
||||
aria-valuenow="{{ attendance_summary.attendance_percentage }}"
|
||||
aria-valuemin="0"
|
||||
aria-valuemax="100"></div>
|
||||
</div>
|
||||
<div class="d-flex justify-content-between align-items-center">
|
||||
<small class="text-muted">Minimum required: 75%</small>
|
||||
{% if attendance_summary.attendance_percentage < 75 %}
|
||||
<small class="text-danger"><i class="fas fa-exclamation-triangle"></i> Below minimum requirement</small>
|
||||
{% endif %}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-md-8">
|
||||
<div class="card border-0 shadow-sm mb-3">
|
||||
<div class="card-body">
|
||||
<h5 class="card-title mb-3"><i class="fas fa-info-circle text-info me-2"></i>Attendance Details</h5>
|
||||
<div class="row">
|
||||
<div class="col-md-3 mb-3">
|
||||
<div class="text-center p-3 bg-light rounded">
|
||||
<div class="display-6 text-success mb-1">{{ attendance_summary.present_count }}</div>
|
||||
<div class="small text-muted">Present</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-md-3 mb-3">
|
||||
<div class="text-center p-3 bg-light rounded">
|
||||
<div class="display-6 text-danger mb-1">{{ attendance_summary.absent_count }}</div>
|
||||
<div class="small text-muted">Absent</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-md-3 mb-3">
|
||||
<div class="text-center p-3 bg-light rounded">
|
||||
<div class="display-6 text-success mb-1">{{ attendance_summary.current_streak }}</div>
|
||||
<div class="small text-muted">Current Streak</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-md-3 mb-3">
|
||||
<div class="text-center p-3 bg-light rounded">
|
||||
<div class="display-6 text-info mb-1">{{ attendance_summary.longest_streak }}</div>
|
||||
<div class="small text-muted">Best Streak</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Attendance Records Table -->
|
||||
<div class="table-responsive">
|
||||
<table class="table table-responsive table-hover">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>Date</th>
|
||||
<th>Subject</th>
|
||||
<th>Teacher</th>
|
||||
<th>Status</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{% for record in attendance_records %}
|
||||
<tr>
|
||||
<td>{{ record.attendance.date|date:"M d, Y" }}</td>
|
||||
<td>{{ record.attendance.routine.subject.name }}</td>
|
||||
<td>{{ record.attendance.teacher.name }}</td>
|
||||
<td>
|
||||
<span class="status-badge {% if record.student_attend %}bg-success{% else %}bg-danger{% endif %}">
|
||||
{% if record.student_attend %}Present{% else %}Absent{% endif %}
|
||||
</span>
|
||||
</td>
|
||||
</tr>
|
||||
{% empty %}
|
||||
<tr>
|
||||
<td colspan="4" class="text-center">No attendance records found.</td>
|
||||
</tr>
|
||||
{% endfor %}
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{% endblock content %}
|
||||
|
||||
{% block extrascripts %}
|
||||
<script>
|
||||
// Initialize Firebase and request notification permission when the page loads
|
||||
document.addEventListener('DOMContentLoaded', function() {
|
||||
// Call getDeviceToken to initialize Firebase and request notification permission
|
||||
if (typeof getDeviceToken === 'function') {
|
||||
getDeviceToken().catch(error => {
|
||||
console.error('Error initializing Firebase:', error);
|
||||
});
|
||||
}
|
||||
});
|
||||
</script>
|
||||
{% endblock %}
|
||||
|
||||
@@ -1,218 +0,0 @@
|
||||
<!-- Student Feedback Modal -->
|
||||
<div id="feedbackModal" style="display: none; position: fixed; top: 0; left: 0; right: 0; bottom: 0; background-color: rgba(0,0,0,0.5); z-index: 10000;" onclick="if(event.target === this) closeFeedbackModal();">
|
||||
<div style="position: absolute; top: 50%; left: 50%; transform: translate(-50%, -50%); width: 90%; max-width: 600px; background-color: white; border-radius: 8px; box-shadow: 0 2px 10px rgba(0,0,0,0.2);">
|
||||
<div style="background-color: #3F51B5; color: white; padding: 15px; border-top-left-radius: 8px; border-top-right-radius: 8px; display: flex; justify-content: space-between; align-items: center;">
|
||||
<h5 style="margin: 0; font-size: 18px;">Give Feedback</h5>
|
||||
<button style="background: none; border: none; color: white; font-size: 24px; cursor: pointer; line-height: 24px;" onclick="closeFeedbackModal()">×</button>
|
||||
</div>
|
||||
<div style="padding: 20px;">
|
||||
<!-- Custom Tab Navigation -->
|
||||
<div class="feedback-tabs">
|
||||
<div id="teacher-tab-btn" class="feedback-tab-btn active" onclick="switchFeedbackTab('teacher')">
|
||||
Teacher Feedback
|
||||
</div>
|
||||
<div id="institute-tab-btn" class="feedback-tab-btn" onclick="switchFeedbackTab('institute')">
|
||||
Institute Feedback
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Tab Content -->
|
||||
<div class="tab-content">
|
||||
<!-- Teacher Feedback Form -->
|
||||
<div id="teacher-tab-content" class="tab-pane active">
|
||||
<form method="post" action="{% url 'submit_feedback' %}" id="teacherFeedbackForm">
|
||||
{% csrf_token %}
|
||||
<input type="hidden" name="feedback_type" value="teacher">
|
||||
<div class="mb-3">
|
||||
<label for="teacher_select" class="form-label">Select Teacher</label>
|
||||
<select class="form-select" id="teacher_select" name="teacher_id" required style="border-color: #BDBDBD;">
|
||||
<option value="">Select Teacher</option>
|
||||
{% for teacher in teachers %}
|
||||
<option value="{{ teacher.id }}">{{ teacher.name }} - {{ teacher.designation }}</option>
|
||||
{% endfor %}
|
||||
</select>
|
||||
</div>
|
||||
<div class="mb-3">
|
||||
<label class="form-label">Rating</label>
|
||||
<div class="rating-stars d-flex">
|
||||
<i class="far fa-star fs-4 text-warning rating-star" data-value="1" onclick="selectRating(this, 'teacher_rating')"></i>
|
||||
<i class="far fa-star fs-4 text-warning rating-star" data-value="2" onclick="selectRating(this, 'teacher_rating')"></i>
|
||||
<i class="far fa-star fs-4 text-warning rating-star" data-value="3" onclick="selectRating(this, 'teacher_rating')"></i>
|
||||
<i class="far fa-star fs-4 text-warning rating-star" data-value="4" onclick="selectRating(this, 'teacher_rating')"></i>
|
||||
<i class="far fa-star fs-4 text-warning rating-star" data-value="5" onclick="selectRating(this, 'teacher_rating')"></i>
|
||||
<input type="hidden" name="rating" id="teacher_rating" value="" required>
|
||||
<div class="invalid-feedback d-block" id="teacher-rating-error" style="display: none;">Please select a rating</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="mb-3">
|
||||
<label for="teacher_feedback" class="form-label">Your Feedback</label>
|
||||
<textarea class="form-control" id="teacher_feedback" name="feedback_text" rows="3" required style="border-color: #BDBDBD;"></textarea>
|
||||
</div>
|
||||
<div class="d-flex justify-content-end gap-2">
|
||||
<button type="button" class="btn" style="background-color: #9E9E9E; color: white;" onclick="closeFeedbackModal()">Cancel</button>
|
||||
<button type="submit" class="btn" style="background-color: #3F51B5; color: white;">Submit Feedback</button>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
|
||||
<!-- Institute Feedback Form -->
|
||||
<div id="institute-tab-content" class="tab-pane">
|
||||
<form method="post" action="{% url 'submit_institute_feedback' %}" id="instituteFeedbackForm">
|
||||
{% csrf_token %}
|
||||
<input type="hidden" name="feedback_type" value="institute">
|
||||
<div class="mb-3">
|
||||
<label for="feedback_type_select" class="form-label">Feedback Category</label>
|
||||
<select class="form-select" id="feedback_type_select" name="feedback_type" required style="border-color: #BDBDBD;">
|
||||
<option value="">Select Category</option>
|
||||
{% for type, label in feedback_types %}
|
||||
<option value="{{ type }}">{{ label }}</option>
|
||||
{% endfor %}
|
||||
</select>
|
||||
</div>
|
||||
<div class="mb-3">
|
||||
<label class="form-label">Rating</label>
|
||||
<div class="rating-stars d-flex">
|
||||
<i class="far fa-star fs-4 text-warning rating-star" data-value="1" onclick="selectRating(this, 'institute_rating')"></i>
|
||||
<i class="far fa-star fs-4 text-warning rating-star" data-value="2" onclick="selectRating(this, 'institute_rating')"></i>
|
||||
<i class="far fa-star fs-4 text-warning rating-star" data-value="3" onclick="selectRating(this, 'institute_rating')"></i>
|
||||
<i class="far fa-star fs-4 text-warning rating-star" data-value="4" onclick="selectRating(this, 'institute_rating')"></i>
|
||||
<i class="far fa-star fs-4 text-warning rating-star" data-value="5" onclick="selectRating(this, 'institute_rating')"></i>
|
||||
<input type="hidden" name="rating" id="institute_rating" value="" required>
|
||||
<div class="invalid-feedback d-block" id="institute-rating-error" style="display: none;">Please select a rating</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="mb-3">
|
||||
<label for="institute_feedback" class="form-label">Your Feedback</label>
|
||||
<textarea class="form-control" id="institute_feedback" name="feedback_text" rows="3" required placeholder="Please provide your feedback about the institute" style="border-color: #BDBDBD;"></textarea>
|
||||
</div>
|
||||
<div class="d-flex justify-content-end gap-2">
|
||||
<button type="button" class="btn" style="background-color: #9E9E9E; color: white;" onclick="closeFeedbackModal()">Cancel</button>
|
||||
<button type="submit" class="btn" style="background-color: #3F51B5; color: white;">Submit Feedback</button>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<style>
|
||||
.feedback-tabs {
|
||||
display: flex;
|
||||
gap: 0;
|
||||
border: none;
|
||||
margin-bottom: 1.5rem;
|
||||
background-color: #f5f5f5;
|
||||
padding: 4px;
|
||||
border-radius: 8px;
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.feedback-tab-btn {
|
||||
flex: 1;
|
||||
padding: 12px 24px;
|
||||
cursor: pointer;
|
||||
border: none !important;
|
||||
background-color: transparent;
|
||||
color: #333;
|
||||
font-weight: 500;
|
||||
transition: all 0.3s ease;
|
||||
border-radius: 6px;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.feedback-tab-btn:hover:not(.active) {
|
||||
background-color: rgba(63, 81, 181, 0.1);
|
||||
}
|
||||
|
||||
.feedback-tab-btn.active {
|
||||
background-color: #3F51B5 !important;
|
||||
color: white !important;
|
||||
box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1);
|
||||
}
|
||||
|
||||
.tab-pane {
|
||||
display: none;
|
||||
opacity: 0;
|
||||
transition: opacity 0.3s ease;
|
||||
}
|
||||
|
||||
.tab-pane.active {
|
||||
display: block;
|
||||
opacity: 1;
|
||||
}
|
||||
|
||||
.rating-stars {
|
||||
display: flex;
|
||||
gap: 8px;
|
||||
padding: 8px 0;
|
||||
}
|
||||
|
||||
.rating-star {
|
||||
cursor: pointer;
|
||||
transition: all 0.2s ease;
|
||||
}
|
||||
|
||||
.rating-star:hover,
|
||||
.rating-star.active,
|
||||
.rating-star.selected {
|
||||
color: #FFD700 !important;
|
||||
transform: scale(1.1);
|
||||
}
|
||||
</style>
|
||||
|
||||
<script>
|
||||
function openFeedbackModal() {
|
||||
document.getElementById('feedbackModal').style.display = 'block';
|
||||
document.body.style.overflow = 'hidden';
|
||||
}
|
||||
|
||||
function closeFeedbackModal() {
|
||||
document.getElementById('feedbackModal').style.display = 'none';
|
||||
document.body.style.overflow = '';
|
||||
}
|
||||
|
||||
function switchFeedbackTab(tabName) {
|
||||
const teacherTabBtn = document.getElementById('teacher-tab-btn');
|
||||
const instituteTabBtn = document.getElementById('institute-tab-btn');
|
||||
const teacherContent = document.getElementById('teacher-tab-content');
|
||||
const instituteContent = document.getElementById('institute-tab-content');
|
||||
|
||||
teacherTabBtn.classList.remove('active');
|
||||
instituteTabBtn.classList.remove('active');
|
||||
teacherContent.classList.remove('active');
|
||||
instituteContent.classList.remove('active');
|
||||
|
||||
if (tabName === 'teacher') {
|
||||
teacherTabBtn.classList.add('active');
|
||||
teacherContent.classList.add('active');
|
||||
} else if (tabName === 'institute') {
|
||||
instituteTabBtn.classList.add('active');
|
||||
instituteContent.classList.add('active');
|
||||
}
|
||||
}
|
||||
|
||||
function selectRating(star, inputId) {
|
||||
const value = parseInt(star.getAttribute('data-value'));
|
||||
document.getElementById(inputId).value = value;
|
||||
|
||||
// Update star display
|
||||
const allStars = star.parentNode.querySelectorAll('.rating-star');
|
||||
allStars.forEach(s => {
|
||||
const starValue = parseInt(s.getAttribute('data-value'));
|
||||
if (starValue <= value) {
|
||||
s.classList.remove('far');
|
||||
s.classList.add('fas');
|
||||
s.classList.add('selected');
|
||||
} else {
|
||||
s.classList.remove('fas');
|
||||
s.classList.remove('selected');
|
||||
s.classList.add('far');
|
||||
}
|
||||
});
|
||||
|
||||
// Hide error message if any
|
||||
const errorId = inputId === 'teacher_rating' ? 'teacher-rating-error' : 'institute-rating-error';
|
||||
document.getElementById(errorId).style.display = 'none';
|
||||
}
|
||||
</script>
|
||||
@@ -1,12 +0,0 @@
|
||||
<!-- PDF Viewer Modal -->
|
||||
<div id="pdfViewerModal" style="display: none; position: fixed; top: 0; left: 0; right: 0; bottom: 0; background-color: rgba(0,0,0,0.5); z-index: 10000;" onclick="if(event.target === this) closePdfViewerModal();">
|
||||
<div style="position: absolute; top: 50%; left: 50%; transform: translate(-50%, -50%); width: 95%; max-width: 1000px; height: 90vh; background-color: white; border-radius: 8px; box-shadow: 0 2px 10px rgba(0,0,0,0.2);">
|
||||
<div style="background-color: #3F51B5; color: white; padding: 15px; border-top-left-radius: 8px; border-top-right-radius: 8px; display: flex; justify-content: space-between; align-items: center;">
|
||||
<h5 style="margin: 0; font-size: 18px;">Subject Syllabus</h5>
|
||||
<button style="background: none; border: none; color: white; font-size: 24px; cursor: pointer; line-height: 24px;" onclick="closePdfViewerModal()">×</button>
|
||||
</div>
|
||||
<div style="height: calc(90vh - 60px); padding: 0;">
|
||||
<iframe id="pdfViewer" style="width: 100%; height: 100%; border: none;"></iframe>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@@ -1,14 +0,0 @@
|
||||
<!-- Subject Files Modal -->
|
||||
<div id="subjectFilesModal" style="display: none; position: fixed; top: 0; left: 0; right: 0; bottom: 0; background-color: rgba(0,0,0,0.5); z-index: 10000;" onclick="if(event.target === this) closeSubjectFilesModal();">
|
||||
<div style="position: absolute; top: 50%; left: 50%; transform: translate(-50%, -50%); width: 90%; max-width: 800px; background-color: white; border-radius: 8px; box-shadow: 0 2px 10px rgba(0,0,0,0.2);">
|
||||
<div style="background-color: #3F51B5; color: white; padding: 15px; border-top-left-radius: 8px; border-top-right-radius: 8px; display: flex; justify-content: space-between; align-items: center;">
|
||||
<h5 style="margin: 0; font-size: 18px;">Subject Materials</h5>
|
||||
<button style="background: none; border: none; color: white; font-size: 24px; cursor: pointer; line-height: 24px;" onclick="closeSubjectFilesModal()">×</button>
|
||||
</div>
|
||||
<div style="padding: 20px;">
|
||||
<div id="subjectFilesList" class="list-group">
|
||||
<!-- Files will be loaded here -->
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
File diff suppressed because it is too large.
Load diff
@@ -1,101 +0,0 @@
|
||||
<!-- Attendance Modal -->
|
||||
<div class="modal fade" id="attendanceModal" tabindex="-1" aria-labelledby="attendanceModalLabel" aria-hidden="true">
|
||||
<div class="modal-dialog modal-lg">
|
||||
<div class="modal-content">
|
||||
<div class="modal-header bg-primary text-white">
|
||||
<h5 class="modal-title" id="attendanceModalLabel">
|
||||
<i class="fas fa-clipboard-check me-2"></i>Take Attendance
|
||||
</h5>
|
||||
<button type="button" class="btn-close btn-close-white" data-bs-dismiss="modal" aria-label="Close"></button>
|
||||
</div>
|
||||
<div class="modal-body">
|
||||
<form id="attendanceForm">
|
||||
<input type="hidden" name="routine_id" id="routineId">
|
||||
<div class="mb-4">
|
||||
<h6 class="mb-3">Class Information</h6>
|
||||
<div class="row">
|
||||
<div class="col-md-6">
|
||||
<p class="mb-1"><strong>Subject:</strong> <span id="subjectName"></span></p>
|
||||
<p class="mb-1"><strong>Course:</strong> <span id="courseName"></span></p>
|
||||
</div>
|
||||
<div class="col-md-6">
|
||||
<p class="mb-1"><strong>Time:</strong> <span id="classTime"></span></p>
|
||||
<p class="mb-1"><strong>Date:</strong> <span id="classDate"></span></p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="mb-4">
|
||||
<h6 class="mb-3">Students List</h6>
|
||||
<div class="table-responsive">
|
||||
<table class="table table-hover">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>Student ID</th>
|
||||
<th>Name</th>
|
||||
<th>Status</th>
|
||||
<th>Remarks</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody id="studentsList">
|
||||
<!-- Students will be populated here -->
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="mb-3">
|
||||
<label for="remarks" class="form-label">General Remarks</label>
|
||||
<textarea class="form-control" id="remarks" name="remarks" rows="2" placeholder="Add any general remarks about the class"></textarea>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
<div class="modal-footer">
|
||||
<button type="button" class="btn btn-secondary" data-bs-dismiss="modal">
|
||||
<i class="fas fa-times me-2"></i>Cancel
|
||||
</button>
|
||||
<button type="button" class="btn btn-primary" id="saveAttendance">
|
||||
<i class="fas fa-save me-2"></i>Save Attendance
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<style>
|
||||
#attendanceModal .modal-content {
|
||||
border: none;
|
||||
box-shadow: var(--shadow-lg);
|
||||
}
|
||||
|
||||
#attendanceModal .modal-header {
|
||||
border-bottom: none;
|
||||
}
|
||||
|
||||
#attendanceModal .modal-body {
|
||||
padding: 1.5rem;
|
||||
}
|
||||
|
||||
#attendanceModal .table th {
|
||||
background-color: var(--light);
|
||||
font-weight: 600;
|
||||
}
|
||||
|
||||
#attendanceModal .table td {
|
||||
vertical-align: middle;
|
||||
}
|
||||
|
||||
#attendanceModal .btn {
|
||||
padding: 0.5rem 1rem;
|
||||
font-weight: 500;
|
||||
}
|
||||
|
||||
#attendanceModal .form-control {
|
||||
border-radius: var(--border-radius-md);
|
||||
}
|
||||
|
||||
#attendanceModal .form-control:focus {
|
||||
border-color: var(--primary);
|
||||
box-shadow: 0 0 0 0.2rem rgba(13, 110, 253, 0.25);
|
||||
}
|
||||
</style>
|
||||
@@ -1,19 +0,0 @@
|
||||
<!-- Confirmation Modal -->
|
||||
<div id="confirmationModal" style="display: none; position: fixed; top: 0; left: 0; right: 0; bottom: 0; background-color: rgba(0,0,0,0.5); z-index: 10000;" onclick="if(event.target === this) closeConfirmationModal();">
|
||||
<div style="position: absolute; top: 50%; left: 50%; transform: translate(-50%, -50%); width: 90%; max-width: 400px; background-color: white; border-radius: 8px; box-shadow: 0 2px 10px rgba(0,0,0,0.2);">
|
||||
<div style="background-color: #f44336; color: white; padding: 15px; border-top-left-radius: 8px; border-top-right-radius: 8px; display: flex; justify-content: space-between; align-items: center;">
|
||||
<h5 style="margin: 0; font-size: 18px;">Confirm Deletion</h5>
|
||||
<button style="background: none; border: none; color: white; font-size: 24px; cursor: pointer; line-height: 24px;" onclick="closeConfirmationModal()">×</button>
|
||||
</div>
|
||||
<div style="padding: 20px;">
|
||||
<p class="mb-4">Are you sure you want to delete this file? This action cannot be undone.</p>
|
||||
|
||||
<div class="d-flex justify-content-end">
|
||||
<button type="button" class="btn btn-secondary me-2" onclick="closeConfirmationModal()">Cancel</button>
|
||||
<button type="button" class="btn btn-danger" id="confirmDeleteBtn">
|
||||
<i class="fas fa-trash me-1"></i> Delete
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@@ -1,109 +0,0 @@
|
||||
<!-- Teacher Feedback Modal -->
|
||||
<div id="feedbackModal" style="display: none; position: fixed; top: 0; left: 0; right: 0; bottom: 0; background-color: rgba(0,0,0,0.5); z-index: 10000;" onclick="if(event.target === this) closeFeedbackModal();">
|
||||
<div style="position: absolute; top: 50%; left: 50%; transform: translate(-50%, -50%); width: 90%; max-width: 600px; background-color: white; border-radius: 8px; box-shadow: 0 2px 10px rgba(0,0,0,0.2);">
|
||||
<div style="background-color: #3F51B5; color: white; padding: 15px; border-top-left-radius: 8px; border-top-right-radius: 8px; display: flex; justify-content: space-between; align-items: center;">
|
||||
<h5 style="margin: 0; font-size: 18px;">Give Institute Feedback</h5>
|
||||
<button style="background: none; border: none; color: white; font-size: 24px; cursor: pointer; line-height: 24px;" onclick="closeFeedbackModal()">×</button>
|
||||
</div>
|
||||
<div style="padding: 20px;">
|
||||
<form method="post" action="{% url 'submit_staff_institute_feedback' %}" id="instituteFeedbackForm">
|
||||
{% csrf_token %}
|
||||
<div class="mb-3">
|
||||
<label for="feedback_type" class="form-label">Feedback Type</label>
|
||||
<select class="form-select" id="feedback_type" name="feedback_type" required>
|
||||
<option value="">Select Type</option>
|
||||
{% for value, name in feedback_types %}
|
||||
<option value="{{ value }}">{{ name }}</option>
|
||||
{% endfor %}
|
||||
</select>
|
||||
</div>
|
||||
|
||||
<div class="mb-3">
|
||||
<label class="form-label">Rating</label>
|
||||
<div class="rating-stars d-flex">
|
||||
<i class="far fa-star fs-4 text-warning rating-star" data-value="1" onclick="selectRatingTeacher(this, 'teacher_modal_rating')"></i>
|
||||
<i class="far fa-star fs-4 text-warning rating-star" data-value="2" onclick="selectRatingTeacher(this, 'teacher_modal_rating')"></i>
|
||||
<i class="far fa-star fs-4 text-warning rating-star" data-value="3" onclick="selectRatingTeacher(this, 'teacher_modal_rating')"></i>
|
||||
<i class="far fa-star fs-4 text-warning rating-star" data-value="4" onclick="selectRatingTeacher(this, 'teacher_modal_rating')"></i>
|
||||
<i class="far fa-star fs-4 text-warning rating-star" data-value="5" onclick="selectRatingTeacher(this, 'teacher_modal_rating')"></i>
|
||||
<input type="hidden" name="rating" id="teacher_modal_rating" value="" required>
|
||||
<div class="invalid-feedback d-block" id="teacher-modal-rating-error" style="display: none;">Please select a rating</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="mb-3">
|
||||
<label for="feedback_text" class="form-label">Feedback</label>
|
||||
<textarea class="form-control" id="feedback_text" name="feedback_text" rows="4" required></textarea>
|
||||
</div>
|
||||
|
||||
<div class="mb-3 form-check">
|
||||
<input type="checkbox" class="form-check-input" id="is_anonymous" name="is_anonymous">
|
||||
<label class="form-check-label" for="is_anonymous">Submit anonymously</label>
|
||||
</div>
|
||||
|
||||
<div class="d-flex justify-content-end gap-2">
|
||||
<button type="button" class="btn" style="background-color: #9E9E9E; color: white;" onclick="closeFeedbackModal()">Cancel</button>
|
||||
<button type="submit" class="btn" style="background-color: #3F51B5; color: white;">Submit Feedback</button>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Teacher Feedback Modal Scripts -->
|
||||
<script>
|
||||
function openFeedbackModal() {
|
||||
document.getElementById('feedbackModal').style.display = 'block';
|
||||
document.body.style.overflow = 'hidden';
|
||||
}
|
||||
|
||||
function closeFeedbackModal() {
|
||||
document.getElementById('feedbackModal').style.display = 'none';
|
||||
document.body.style.overflow = '';
|
||||
}
|
||||
|
||||
function selectRatingTeacher(star, inputId) {
|
||||
const value = parseInt(star.getAttribute('data-value'));
|
||||
document.getElementById(inputId).value = value;
|
||||
|
||||
// Update star display
|
||||
const allStars = star.parentNode.querySelectorAll('.rating-star');
|
||||
allStars.forEach(s => {
|
||||
const starValue = parseInt(s.getAttribute('data-value'));
|
||||
if (starValue <= value) {
|
||||
s.classList.remove('far');
|
||||
s.classList.add('fas');
|
||||
s.classList.add('selected');
|
||||
} else {
|
||||
s.classList.remove('fas');
|
||||
s.classList.add('far');
|
||||
s.classList.remove('selected');
|
||||
}
|
||||
});
|
||||
|
||||
// Hide error message if any
|
||||
const errorId = 'teacher-modal-rating-error';
|
||||
document.getElementById(errorId).style.display = 'none';
|
||||
}
|
||||
</script>
|
||||
|
||||
<!-- Additional Feedback Styles -->
|
||||
<style>
|
||||
.rating-stars {
|
||||
display: flex;
|
||||
gap: 8px;
|
||||
padding: 8px 0;
|
||||
}
|
||||
|
||||
.rating-star {
|
||||
cursor: pointer;
|
||||
transition: all 0.2s ease;
|
||||
}
|
||||
|
||||
.rating-star:hover,
|
||||
.rating-star.active,
|
||||
.rating-star.selected {
|
||||
color: #FFD700 !important;
|
||||
transform: scale(1.1);
|
||||
}
|
||||
</style>
|
||||
@@ -1,49 +0,0 @@
|
||||
<!-- Upload File Modal -->
|
||||
<div id="uploadFileModal" style="display: none; position: fixed; top: 0; left: 0; right: 0; bottom: 0; background-color: rgba(0,0,0,0.5); z-index: 10000;" onclick="if(event.target === this) closeUploadModal();">
|
||||
<div style="position: absolute; top: 50%; left: 50%; transform: translate(-50%, -50%); width: 90%; max-width: 600px; background-color: white; border-radius: 8px; box-shadow: 0 2px 10px rgba(0,0,0,0.2);">
|
||||
<div style="background-color: #3F51B5; color: white; padding: 15px; border-top-left-radius: 8px; border-top-right-radius: 8px; display: flex; justify-content: space-between; align-items: center;">
|
||||
<h5 style="margin: 0; font-size: 18px;">Upload Study Material</h5>
|
||||
<button style="background: none; border: none; color: white; font-size: 24px; cursor: pointer; line-height: 24px;" onclick="closeUploadModal()">×</button>
|
||||
</div>
|
||||
<div style="padding: 20px;">
|
||||
<form id="uploadFileForm" method="post" action="/app/manage-subject-files/" enctype="multipart/form-data">
|
||||
{% csrf_token %}
|
||||
|
||||
<div class="mb-3">
|
||||
<label for="subject_id" class="form-label">Subject</label>
|
||||
<select class="form-select" id="subject_id" name="subject_id" required>
|
||||
<option value="">Select a subject</option>
|
||||
{% for subject in teacher_subjects %}
|
||||
<option value="{{ subject.subject.id }}">{{ subject.subject.name }} ({{ subject.course.name }})</option>
|
||||
{% endfor %}
|
||||
</select>
|
||||
</div>
|
||||
|
||||
<div class="mb-3">
|
||||
<label for="title" class="form-label">Title</label>
|
||||
<input type="text" class="form-control" id="title" name="title" required placeholder="e.g. Chapter 1 Notes">
|
||||
</div>
|
||||
|
||||
<div class="mb-3">
|
||||
<label for="description" class="form-label">Description (Optional)</label>
|
||||
<textarea class="form-control" id="description" name="description" rows="2" placeholder="Brief description of the material"></textarea>
|
||||
</div>
|
||||
|
||||
<div class="mb-3">
|
||||
<label for="file" class="form-label">File</label>
|
||||
<input type="file" class="form-control" id="file" name="file" required>
|
||||
<div class="form-text">Upload PDF, DOCX, PPT or other study materials. Max file size: 10MB</div>
|
||||
</div>
|
||||
|
||||
<div id="uploadResponse" style="display: none;" class="alert mb-3"></div>
|
||||
|
||||
<div class="d-flex justify-content-end">
|
||||
<button type="button" class="btn btn-secondary me-2" onclick="closeUploadModal()">Cancel</button>
|
||||
<button type="submit" class="btn btn-primary">
|
||||
<i class="fas fa-upload me-1"></i> Upload
|
||||
</button>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
Reference in new issue
Block a user