add license
This commit is contained in:
@@ -0,0 +1,100 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<title>Login - Edu-Space Admin</title>
|
||||
<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.0/dist/css/bootstrap.min.css" rel="stylesheet">
|
||||
<link href="https://cdn.jsdelivr.net/npm/bootstrap-icons@1.10.0/font/bootstrap-icons.css" rel="stylesheet">
|
||||
<style>
|
||||
body {
|
||||
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
|
||||
min-height: 100vh;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
}
|
||||
.login-card {
|
||||
background: rgba(255, 255, 255, 0.95);
|
||||
backdrop-filter: blur(10px);
|
||||
border-radius: 20px;
|
||||
box-shadow: 0 20px 40px rgba(0,0,0,0.1);
|
||||
max-width: 450px;
|
||||
width: 100%;
|
||||
}
|
||||
.logo-section {
|
||||
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
|
||||
border-radius: 20px 20px 0 0;
|
||||
padding: 2rem;
|
||||
text-align: center;
|
||||
color: white;
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<div class="login-card">
|
||||
<div class="logo-section">
|
||||
<i class="bi bi-building" style="font-size: 3rem;"></i>
|
||||
<h3 class="mt-3">Edu-Space Admin</h3>
|
||||
<p class="mb-0">Classroom Management System</p>
|
||||
</div>
|
||||
|
||||
<div class="p-4">
|
||||
{% with messages = get_flashed_messages(with_categories=true) %}
|
||||
{% if messages %}
|
||||
{% for category, message in messages %}
|
||||
<div class="alert alert-{{ 'danger' if category == 'error' else category }} alert-dismissible fade show" role="alert">
|
||||
{{ message }}
|
||||
<button type="button" class="btn-close" data-bs-dismiss="alert"></button>
|
||||
</div>
|
||||
{% endfor %}
|
||||
{% endif %}
|
||||
{% endwith %}
|
||||
|
||||
<form method="POST">
|
||||
{{ form.hidden_tag() }}
|
||||
|
||||
<div class="mb-3">
|
||||
<label for="email" class="form-label">{{ form.email.label.text }}</label>
|
||||
<div class="input-group">
|
||||
<span class="input-group-text"><i class="bi bi-envelope"></i></span>
|
||||
{{ form.email(class="form-control", placeholder="Enter your email") }}
|
||||
</div>
|
||||
{% for error in form.email.errors %}
|
||||
<div class="text-danger small mt-1">{{ error }}</div>
|
||||
{% endfor %}
|
||||
</div>
|
||||
|
||||
<div class="mb-3">
|
||||
<label for="password" class="form-label">{{ form.password.label.text }}</label>
|
||||
<div class="input-group">
|
||||
<span class="input-group-text"><i class="bi bi-lock"></i></span>
|
||||
{{ form.password(class="form-control", placeholder="Enter your password") }}
|
||||
</div>
|
||||
{% for error in form.password.errors %}
|
||||
<div class="text-danger small mt-1">{{ error }}</div>
|
||||
{% endfor %}
|
||||
</div>
|
||||
|
||||
<div class="mb-3 form-check">
|
||||
{{ form.remember_me(class="form-check-input") }}
|
||||
{{ form.remember_me.label(class="form-check-label") }}
|
||||
</div>
|
||||
|
||||
<div class="d-grid">
|
||||
{{ form.submit(class="btn btn-primary btn-lg") }}
|
||||
</div>
|
||||
</form>
|
||||
|
||||
<div class="text-center mt-3">
|
||||
<small class="text-muted">
|
||||
<i class="bi bi-shield-lock"></i>
|
||||
Secure Login • All access is logged
|
||||
</small>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<script src="https://cdn.jsdelivr.net/npm/bootstrap@5.3.0/dist/js/bootstrap.bundle.min.js"></script>
|
||||
</body>
|
||||
</html>
|
||||
@@ -0,0 +1,138 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="en" data-bs-theme="dark">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<title>{% block title %}Edu-Space Admin{% endblock %}</title>
|
||||
|
||||
<!-- Bootstrap 5 CSS -->
|
||||
<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.0/dist/css/bootstrap.min.css" rel="stylesheet">
|
||||
<!-- Bootstrap Icons -->
|
||||
<link href="https://cdn.jsdelivr.net/npm/bootstrap-icons@1.10.0/font/bootstrap-icons.css" rel="stylesheet">
|
||||
<!-- Custom CSS -->
|
||||
<link href="{{ url_for('static', filename='css/style.css') }}" rel="stylesheet">
|
||||
|
||||
{% block extra_css %}{% endblock %}
|
||||
</head>
|
||||
<body>
|
||||
{% if current_user.is_authenticated %}
|
||||
<!-- Navigation -->
|
||||
<nav class="navbar navbar-expand-lg navbar-dark bg-primary fixed-top">
|
||||
<div class="container-fluid">
|
||||
<a class="navbar-brand" href="{{ url_for('main.dashboard') }}">
|
||||
<i class="bi bi-building"></i> Edu-Space Admin
|
||||
</a>
|
||||
|
||||
<button class="navbar-toggler" type="button" data-bs-toggle="collapse" data-bs-target="#navbarNav">
|
||||
<span class="navbar-toggler-icon"></span>
|
||||
</button>
|
||||
|
||||
<div class="collapse navbar-collapse" id="navbarNav">
|
||||
<ul class="navbar-nav me-auto">
|
||||
<li class="nav-item">
|
||||
<a class="nav-link" href="{{ url_for('main.dashboard') }}">
|
||||
<i class="bi bi-speedometer2"></i> Dashboard
|
||||
</a>
|
||||
</li>
|
||||
<li class="nav-item dropdown">
|
||||
<a class="nav-link dropdown-toggle" href="#" role="button" data-bs-toggle="dropdown">
|
||||
<i class="bi bi-door-open"></i> Classrooms
|
||||
</a>
|
||||
<ul class="dropdown-menu">
|
||||
<li><a class="dropdown-item" href="{{ url_for('classrooms.list_classrooms') }}">
|
||||
<i class="bi bi-list"></i> All Classrooms
|
||||
</a></li>
|
||||
<li><a class="dropdown-item" href="{{ url_for('classrooms.add_classroom') }}">
|
||||
<i class="bi bi-plus-circle"></i> Add Classroom
|
||||
</a></li>
|
||||
</ul>
|
||||
</li>
|
||||
<li class="nav-item dropdown">
|
||||
<a class="nav-link dropdown-toggle" href="#" role="button" data-bs-toggle="dropdown">
|
||||
<i class="bi bi-calendar3"></i> Schedule
|
||||
</a>
|
||||
<ul class="dropdown-menu">
|
||||
<li><a class="dropdown-item" href="{{ url_for('schedule.calendar_view') }}">
|
||||
<i class="bi bi-calendar-week"></i> Calendar View
|
||||
</a></li>
|
||||
<li><a class="dropdown-item" href="{{ url_for('schedule.list_reservations') }}">
|
||||
<i class="bi bi-list-check"></i> All Reservations
|
||||
</a></li>
|
||||
<li><a class="dropdown-item" href="{{ url_for('schedule.add_reservation') }}">
|
||||
<i class="bi bi-plus-circle"></i> New Reservation
|
||||
</a></li>
|
||||
<li><a class="dropdown-item" href="{{ url_for('schedule.today_schedule') }}">
|
||||
<i class="bi bi-calendar-today"></i> Today's Schedule
|
||||
</a></li>
|
||||
</ul>
|
||||
</li>
|
||||
</ul>
|
||||
|
||||
<ul class="navbar-nav">
|
||||
<li class="nav-item dropdown">
|
||||
<a class="nav-link dropdown-toggle" href="#" role="button" data-bs-toggle="dropdown">
|
||||
<i class="bi bi-person-circle"></i> {{ current_user.name }}
|
||||
</a>
|
||||
<ul class="dropdown-menu dropdown-menu-end">
|
||||
<li><a class="dropdown-item" href="{{ url_for('auth.profile') }}">
|
||||
<i class="bi bi-person"></i> Profile
|
||||
</a></li>
|
||||
<li><a class="dropdown-item" href="{{ url_for('auth.change_password') }}">
|
||||
<i class="bi bi-key"></i> Change Password
|
||||
</a></li>
|
||||
<li><hr class="dropdown-divider"></li>
|
||||
<li><a class="dropdown-item" href="{{ url_for('auth.logout') }}">
|
||||
<i class="bi bi-box-arrow-right"></i> Logout
|
||||
</a></li>
|
||||
</ul>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
</nav>
|
||||
|
||||
<!-- Main Content -->
|
||||
<main class="container-fluid pt-5 mt-4">
|
||||
{% block content %}{% endblock %}
|
||||
</main>
|
||||
{% else %}
|
||||
<main class="container-fluid">
|
||||
{% block content %}{% endblock %}
|
||||
</main>
|
||||
{% endif %}
|
||||
|
||||
<!-- Flash Messages -->
|
||||
<div class="toast-container position-fixed bottom-0 end-0 p-3">
|
||||
{% with messages = get_flashed_messages(with_categories=true) %}
|
||||
{% if messages %}
|
||||
{% for category, message in messages %}
|
||||
<div class="toast align-items-center text-white bg-{{ 'danger' if category == 'error' else 'success' if category == 'success' else 'info' if category == 'info' else 'warning' }} border-0" role="alert">
|
||||
<div class="d-flex">
|
||||
<div class="toast-body">
|
||||
{{ message }}
|
||||
</div>
|
||||
<button type="button" class="btn-close btn-close-white me-2 m-auto" data-bs-dismiss="toast"></button>
|
||||
</div>
|
||||
</div>
|
||||
{% endfor %}
|
||||
{% endif %}
|
||||
{% endwith %}
|
||||
</div>
|
||||
|
||||
<!-- Bootstrap JS -->
|
||||
<script src="https://cdn.jsdelivr.net/npm/bootstrap@5.3.0/dist/js/bootstrap.bundle.min.js"></script>
|
||||
<!-- jQuery -->
|
||||
<script src="https://code.jquery.com/jquery-3.7.1.min.js"></script>
|
||||
<!-- Custom JS -->
|
||||
<script src="{{ url_for('static', filename='js/main.js') }}"></script>
|
||||
|
||||
<!-- Show flash messages -->
|
||||
<script>
|
||||
$(document).ready(function() {
|
||||
$('.toast').toast('show');
|
||||
});
|
||||
</script>
|
||||
|
||||
{% block extra_js %}{% endblock %}
|
||||
</body>
|
||||
</html>
|
||||
@@ -0,0 +1,184 @@
|
||||
{% extends "base.html" %}
|
||||
|
||||
{% block title %}Classrooms - Edu-Space Admin{% endblock %}
|
||||
|
||||
{% block content %}
|
||||
<div class="row mb-4">
|
||||
<div class="col-12">
|
||||
<h1 class="h3 mb-0">
|
||||
<i class="bi bi-door-open text-primary"></i> Classrooms
|
||||
<small class="text-muted ms-2">Management</small>
|
||||
</h1>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Search and Filter -->
|
||||
<div class="card border-0 shadow-sm mb-4">
|
||||
<div class="card-body">
|
||||
<form method="GET" class="row g-3">
|
||||
<div class="col-md-4">
|
||||
<input type="text" class="form-control" name="search"
|
||||
placeholder="Search classrooms..." value="{{ search }}">
|
||||
</div>
|
||||
<div class="col-md-3">
|
||||
<select class="form-select" name="campus">
|
||||
<option value="">All Campuses</option>
|
||||
<option value="Main Campus" {% if campus == 'Main Campus' %}selected{% endif %}>Main Campus</option>
|
||||
<option value="North Campus" {% if campus == 'North Campus' %}selected{% endif %}>North Campus</option>
|
||||
<option value="Science & Technology Campus" {% if campus == 'Science & Technology Campus' %}selected{% endif %}>Science & Technology Campus</option>
|
||||
<option value="Downtown Campus" {% if campus == 'Downtown Campus' %}selected{% endif %}>Downtown Campus</option>
|
||||
</select>
|
||||
</div>
|
||||
<div class="col-md-3">
|
||||
<button type="submit" class="btn btn-primary">
|
||||
<i class="bi bi-search"></i> Search
|
||||
</button>
|
||||
<a href="{{ url_for('classrooms.list_classrooms') }}" class="btn btn-outline-secondary">
|
||||
<i class="bi bi-arrow-clockwise"></i> Reset
|
||||
</a>
|
||||
</div>
|
||||
<div class="col-md-2 text-end">
|
||||
<a href="{{ url_for('classrooms.add_classroom') }}" class="btn btn-success">
|
||||
<i class="bi bi-plus-circle"></i> Add Classroom
|
||||
</a>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Classrooms Grid -->
|
||||
<div class="row">
|
||||
{% for classroom in classrooms.items %}
|
||||
<div class="col-md-6 col-lg-4 mb-4">
|
||||
<div class="card border-0 shadow-sm h-100">
|
||||
<div class="card-header bg-white border-0">
|
||||
<div class="d-flex justify-content-between align-items-start">
|
||||
<div>
|
||||
<h5 class="card-title mb-1">{{ classroom.code }}</h5>
|
||||
<p class="text-muted small mb-0">{{ classroom.name }}</p>
|
||||
</div>
|
||||
<div class="dropdown">
|
||||
<button class="btn btn-sm btn-outline-secondary" data-bs-toggle="dropdown">
|
||||
<i class="bi bi-three-dots-vertical"></i>
|
||||
</button>
|
||||
<ul class="dropdown-menu">
|
||||
<li><a class="dropdown-item" href="{{ url_for('classrooms.view_classroom', Id=classroom.id) }}">
|
||||
<i class="bi bi-eye"></i> View Details
|
||||
</a></li>
|
||||
<li><a class="dropdown-item" href="{{ url_for('classrooms.edit_classroom', Id=classroom.id) }}">
|
||||
<i class="bi bi-pencil"></i> Edit
|
||||
</a></li>
|
||||
<li><a class="dropdown-item text-danger" href="{{ url_for('classrooms.delete_classroom', Id=classroom.id) }}">
|
||||
<i class="bi bi-trash"></i> Delete
|
||||
</a></li>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="card-body">
|
||||
<div class="row g-2">
|
||||
<div class="col-6">
|
||||
<div class="d-flex align-items-center">
|
||||
<i class="bi bi-building text-primary me-2"></i>
|
||||
<span class="small">{{ classroom.building }}</span>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-6">
|
||||
<div class="d-flex align-items-center">
|
||||
<i class="bi bi-layers text-info me-2"></i>
|
||||
<span class="small">Floor {{ classroom.floor }}</span>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-6">
|
||||
<div class="d-flex align-items-center">
|
||||
<i class="bi bi-people text-success me-2"></i>
|
||||
<span class="small">{{ classroom.capacity }} seats</span>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-6">
|
||||
<div class="d-flex align-items-center">
|
||||
<i class="bi bi-geo-alt text-warning me-2"></i>
|
||||
<span class="small">{{ classroom.campus_location }}</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{% if classroom.description %}
|
||||
<div class="mt-3">
|
||||
<p class="small text-muted">{{ classroom.description[:100] }}{% if classroom.description|length > 100 %}...{% endif %}</p>
|
||||
</div>
|
||||
{% endif %}
|
||||
|
||||
<div class="mt-3">
|
||||
<span class="badge bg-success">
|
||||
<i class="bi bi-check-circle"></i> Active
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
<div class="card-footer bg-white border-0">
|
||||
<div class="d-grid">
|
||||
<a href="{{ url_for('classrooms.view_classroom', Id=classroom.id) }}"
|
||||
class="btn btn-outline-primary btn-sm">
|
||||
<i class="bi bi-eye"></i> View Details
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
{% endfor %}
|
||||
</div>
|
||||
|
||||
<!-- Pagination -->
|
||||
{% if classrooms.pages > 1 %}
|
||||
<nav aria-label="Classrooms pagination">
|
||||
<ul class="pagination justify-content-center">
|
||||
{% if classrooms.has_prev %}
|
||||
<li class="page-item">
|
||||
<a class="page-link" href="{{ url_for('classrooms.list_classrooms', page=classrooms.prev_num, search=search, campus=campus) }}">
|
||||
<i class="bi bi-chevron-left"></i>
|
||||
</a>
|
||||
</li>
|
||||
{% endif %}
|
||||
|
||||
{% for page_num in classrooms.iter_pages() %}
|
||||
{% if page_num %}
|
||||
{% if page_num != classrooms.page %}
|
||||
<li class="page-item">
|
||||
<a class="page-link" href="{{ url_for('classrooms.list_classrooms', page=page_num, search=search, campus=campus) }}">
|
||||
{{ page_num }}
|
||||
</a>
|
||||
</li>
|
||||
{% else %}
|
||||
<li class="page-item active">
|
||||
<span class="page-link">{{ page_num }}</span>
|
||||
</li>
|
||||
{% endif %}
|
||||
{% else %}
|
||||
<li class="page-item disabled">
|
||||
<span class="page-link">...</span>
|
||||
</li>
|
||||
{% endif %}
|
||||
{% endfor %}
|
||||
|
||||
{% if classrooms.has_next %}
|
||||
<li class="page-item">
|
||||
<a class="page-link" href="{{ url_for('classrooms.list_classrooms', page=classrooms.next_num, search=search, campus=campus) }}">
|
||||
<i class="bi bi-chevron-right"></i>
|
||||
</a>
|
||||
</li>
|
||||
{% endif %}
|
||||
</ul>
|
||||
</nav>
|
||||
{% endif %}
|
||||
|
||||
{% if not classrooms.items %}
|
||||
<div class="text-center py-5">
|
||||
<i class="bi bi-x-circle text-muted" style="font-size: 3rem;"></i>
|
||||
<h4 class="text-muted mt-3">No classrooms found</h4>
|
||||
<p class="text-muted">Try adjusting your search criteria or add a new classroom.</p>
|
||||
<a href="{{ url_for('classrooms.add_classroom') }}" class="btn btn-primary">
|
||||
<i class="bi bi-plus-circle"></i> Add First Classroom
|
||||
</a>
|
||||
</div>
|
||||
{% endif %}
|
||||
{% endblock %}
|
||||
@@ -0,0 +1,231 @@
|
||||
{% extends "base.html" %}
|
||||
|
||||
{% block title %}Dashboard - Edu-Space Admin{% endblock %}
|
||||
|
||||
{% block content %}
|
||||
<div class="row mb-4">
|
||||
<div class="col-12">
|
||||
<h1 class="h3 mb-0">
|
||||
<i class="bi bi-speedometer2 text-primary"></i> Dashboard
|
||||
<small class="text-muted ms-2">Overview</small>
|
||||
</h1>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Statistics Cards -->
|
||||
<div class="row mb-4">
|
||||
<div class="col-md-3 mb-3">
|
||||
<div class="card border-0 shadow-sm">
|
||||
<div class="card-body">
|
||||
<div class="d-flex justify-content-between align-items-center">
|
||||
<div>
|
||||
<h6 class="text-muted mb-2">Total Classrooms</h6>
|
||||
<h2 class="mb-0">{{ stats.total_classrooms }}</h2>
|
||||
</div>
|
||||
<div class="bg-primary bg-opacity-10 rounded-circle p-3">
|
||||
<i class="bi bi-door-open text-primary fs-4"></i>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="col-md-3 mb-3">
|
||||
<div class="card border-0 shadow-sm">
|
||||
<div class="card-body">
|
||||
<div class="d-flex justify-content-between align-items-center">
|
||||
<div>
|
||||
<h6 class="text-muted mb-2">Total Subjects</h6>
|
||||
<h2 class="mb-0">{{ stats.total_subjects }}</h2>
|
||||
</div>
|
||||
<div class="bg-success bg-opacity-10 rounded-circle p-3">
|
||||
<i class="bi bi-book text-success fs-4"></i>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="col-md-3 mb-3">
|
||||
<div class="card border-0 shadow-sm">
|
||||
<div class="card-body">
|
||||
<div class="d-flex justify-content-between align-items-center">
|
||||
<div>
|
||||
<h6 class="text-muted mb-2">Today's Reservations</h6>
|
||||
<h2 class="mb-0">{{ stats.today_reservations }}</h2>
|
||||
</div>
|
||||
<div class="bg-info bg-opacity-10 rounded-circle p-3">
|
||||
<i class="bi bi-calendar-today text-info fs-4"></i>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="col-md-3 mb-3">
|
||||
<div class="card border-0 shadow-sm">
|
||||
<div class="card-body">
|
||||
<div class="d-flex justify-content-between align-items-center">
|
||||
<div>
|
||||
<h6 class="text-muted mb-2">This Week</h6>
|
||||
<h2 class="mb-0">{{ stats.week_reservations }}</h2>
|
||||
</div>
|
||||
<div class="bg-warning bg-opacity-10 rounded-circle p-3">
|
||||
<i class="bi bi-calendar-week text-warning fs-4"></i>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="row">
|
||||
<!-- Monthly Chart -->
|
||||
<div class="col-md-8 mb-4">
|
||||
<div class="card border-0 shadow-sm">
|
||||
<div class="card-header bg-white border-0">
|
||||
<h5 class="mb-0">
|
||||
<i class="bi bi-graph-up text-primary"></i> Monthly Reservations
|
||||
</h5>
|
||||
</div>
|
||||
<div class="card-body">
|
||||
<canvas id="monthlyChart" height="100"></canvas>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Quick Actions -->
|
||||
<div class="col-md-4 mb-4">
|
||||
<div class="card border-0 shadow-sm">
|
||||
<div class="card-header bg-white border-0">
|
||||
<h5 class="mb-0">
|
||||
<i class="bi bi-lightning text-warning"></i> Quick Actions
|
||||
</h5>
|
||||
</div>
|
||||
<div class="card-body">
|
||||
<div class="d-grid gap-2">
|
||||
<a href="{{ url_for('classrooms.add_classroom') }}" class="btn btn-outline-primary">
|
||||
<i class="bi bi-plus-circle"></i> Add Classroom
|
||||
</a>
|
||||
<a href="{{ url_for('schedule.add_reservation') }}" class="btn btn-outline-success">
|
||||
<i class="bi bi-calendar-plus"></i> New Reservation
|
||||
</a>
|
||||
<a href="{{ url_for('schedule.today_schedule') }}" class="btn btn-outline-info">
|
||||
<i class="bi bi-calendar-today"></i> Today's Schedule
|
||||
</a>
|
||||
<a href="{{ url_for('schedule.calendar_view') }}" class="btn btn-outline-secondary">
|
||||
<i class="bi bi-calendar3"></i> View Calendar
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Upcoming Reservations -->
|
||||
<div class="row">
|
||||
<div class="col-12">
|
||||
<div class="card border-0 shadow-sm">
|
||||
<div class="card-header bg-white border-0">
|
||||
<div class="d-flex justify-content-between align-items-center">
|
||||
<h5 class="mb-0">
|
||||
<i class="bi bi-clock-history text-info"></i> Upcoming Reservations (Next 7 Days)
|
||||
</h5>
|
||||
<a href="{{ url_for('schedule.list_reservations') }}" class="btn btn-sm btn-outline-primary">
|
||||
View All
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
<div class="card-body">
|
||||
{% if upcoming_reservations %}
|
||||
<div class="table-responsive">
|
||||
<table class="table table-hover">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>Date & Time</th>
|
||||
<th>Classroom</th>
|
||||
<th>Subject</th>
|
||||
<th>Teacher</th>
|
||||
<th>Status</th>
|
||||
<th>Actions</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{% for reservation in upcoming_reservations %}
|
||||
<tr>
|
||||
<td>{{ reservation.start_time.strftime('%Y-%m-%d %H:%M') }}</td>
|
||||
<td>{{ reservation.classroom.code }}</td>
|
||||
<td>{{ reservation.commission.get_full_code() }}</td>
|
||||
<td>{{ reservation.commission.teacher_name }}</td>
|
||||
<td>
|
||||
<span class="badge bg-{{ 'success' if reservation.status.value == 'CONFIRMED' else 'warning' }}">
|
||||
{{ reservation.status.value }}
|
||||
</span>
|
||||
</td>
|
||||
<td>
|
||||
<a href="{{ url_for('schedule.view_reservation', Id=reservation.id) }}"
|
||||
class="btn btn-sm btn-outline-primary">
|
||||
<i class="bi bi-eye"></i>
|
||||
</a>
|
||||
</td>
|
||||
</tr>
|
||||
{% endfor %}
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
{% else %}
|
||||
<div class="text-center py-4">
|
||||
<i class="bi bi-calendar-x text-muted" style="font-size: 3rem;"></i>
|
||||
<p class="text-muted mt-3">No upcoming reservations in the next 7 days</p>
|
||||
</div>
|
||||
{% endif %}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
{% endblock %}
|
||||
|
||||
{% block extra_js %}
|
||||
<script src="https://cdn.jsdelivr.net/npm/chart.js"></script>
|
||||
<script>
|
||||
$(document).ready(function() {
|
||||
// Fetch monthly data and create chart
|
||||
$.get('/api/dashboard-stats')
|
||||
.done(function(data) {
|
||||
const ctx = document.getElementById('monthlyChart').getContext('2d');
|
||||
new Chart(ctx, {
|
||||
type: 'line',
|
||||
data: {
|
||||
labels: data.monthly_data.map(item => item.month),
|
||||
datasets: [{
|
||||
label: 'Reservations',
|
||||
data: data.monthly_data.map(item => item.reservations),
|
||||
borderColor: '#667eea',
|
||||
backgroundColor: 'rgba(102, 126, 234, 0.1)',
|
||||
borderWidth: 2,
|
||||
fill: true,
|
||||
tension: 0.4
|
||||
}]
|
||||
},
|
||||
options: {
|
||||
responsive: true,
|
||||
maintainAspectRatio: false,
|
||||
plugins: {
|
||||
legend: {
|
||||
display: false
|
||||
}
|
||||
},
|
||||
scales: {
|
||||
y: {
|
||||
beginAtZero: true,
|
||||
ticks: {
|
||||
stepSize: 1
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
});
|
||||
});
|
||||
</script>
|
||||
{% endblock %}
|
||||
@@ -0,0 +1,173 @@
|
||||
{% extends "base.html" %}
|
||||
|
||||
{% block title %}Schedule Calendar - Edu-Space Admin{% endblock %}
|
||||
|
||||
{% block content %}
|
||||
<div class="row mb-4">
|
||||
<div class="col-12">
|
||||
<h1 class="h3 mb-0">
|
||||
<i class="bi bi-calendar3 text-primary"></i> Schedule Calendar
|
||||
<small class="text-muted ms-2">View & Manage Reservations</small>
|
||||
</h1>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="card border-0 shadow-sm">
|
||||
<div class="card-header bg-white border-0">
|
||||
<div class="row align-items-center">
|
||||
<div class="col-md-6">
|
||||
<h5 class="mb-0">
|
||||
<i class="bi bi-calendar-week text-info"></i> Reservations Calendar
|
||||
</h5>
|
||||
</div>
|
||||
<div class="col-md-6 text-end">
|
||||
<a href="{{ url_for('schedule.add_reservation') }}" class="btn btn-primary">
|
||||
<i class="bi bi-plus-circle"></i> Add Reservation
|
||||
</a>
|
||||
<a href="{{ url_for('schedule.list_reservations') }}" class="btn btn-outline-secondary">
|
||||
<i class="bi bi-list"></i> List View
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="card-body">
|
||||
<!-- Legend -->
|
||||
<div class="mb-3">
|
||||
<div class="d-flex gap-3 flex-wrap">
|
||||
<div class="d-flex align-items-center">
|
||||
<span class="badge bg-success me-2" style="width: 20px; height: 20px;"></span>
|
||||
<small class="text-muted">Confirmed</small>
|
||||
</div>
|
||||
<div class="d-flex align-items-center">
|
||||
<span class="badge bg-warning me-2" style="width: 20px; height: 20px;"></span>
|
||||
<small class="text-muted">Pending</small>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Calendar -->
|
||||
<div id="calendar"></div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Reservation Details Modal -->
|
||||
<div class="modal fade" id="reservationModal" tabindex="-1">
|
||||
<div class="modal-dialog modal-lg">
|
||||
<div class="modal-content">
|
||||
<div class="modal-header">
|
||||
<h5 class="modal-title">Reservation Details</h5>
|
||||
<button type="button" class="btn-close" data-bs-dismiss="modal"></button>
|
||||
</div>
|
||||
<div class="modal-body" id="reservationDetails">
|
||||
<!-- Content will be loaded dynamically -->
|
||||
</div>
|
||||
<div class="modal-footer">
|
||||
<button type="button" class="btn btn-secondary" data-bs-dismiss="modal">Close</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
{% endblock %}
|
||||
|
||||
{% block extra_css %}
|
||||
<link href="https://cdn.jsdelivr.net/npm/fullcalendar@5.11.0/main.min.css" rel="stylesheet">
|
||||
<style>
|
||||
#calendar {
|
||||
margin-top: 20px;
|
||||
}
|
||||
.fc-event {
|
||||
cursor: pointer;
|
||||
border: none !important;
|
||||
padding: 2px 6px !important;
|
||||
font-size: 12px !important;
|
||||
}
|
||||
.fc-event-pending {
|
||||
background-color: #ffc107 !important;
|
||||
}
|
||||
.fc-event-confirmed {
|
||||
background-color: #28a745 !important;
|
||||
}
|
||||
.fc-daygrid-day.fc-day-today {
|
||||
background-color: rgba(102, 126, 234, 0.1) !important;
|
||||
}
|
||||
</style>
|
||||
{% endblock %}
|
||||
|
||||
{% block extra_js %}
|
||||
<script src="https://cdn.jsdelivr.net/npm/fullcalendar@5.11.0/main.min.js"></script>
|
||||
<script>
|
||||
document.addEventListener('DOMContentLoaded', function() {
|
||||
var calendarEl = document.getElementById('calendar');
|
||||
|
||||
var calendar = new FullCalendar.Calendar(calendarEl, {
|
||||
initialView: 'timeGridWeek',
|
||||
headerToolbar: {
|
||||
left: 'prev,next today',
|
||||
center: 'title',
|
||||
right: 'dayGridMonth,timeGridWeek,timeGridDay,listWeek'
|
||||
},
|
||||
eventTimeFormat: {
|
||||
hour: '2-digit',
|
||||
minute: '2-digit',
|
||||
hour12: false
|
||||
},
|
||||
slotMinTime: '07:00:00',
|
||||
slotMaxTime: '22:00:00',
|
||||
height: 'auto',
|
||||
eventClick: function(info) {
|
||||
// Prevent default link behavior
|
||||
if (info.event.url) {
|
||||
// Load reservation details in modal instead of navigation
|
||||
loadReservationDetails(info.event.url);
|
||||
return false;
|
||||
}
|
||||
},
|
||||
events: '{{ url_for("schedule.calendar_data") }}',
|
||||
loading: function(bool) {
|
||||
// Show loading indicator if needed
|
||||
}
|
||||
});
|
||||
|
||||
calendar.render();
|
||||
|
||||
function loadReservationDetails(url) {
|
||||
$.get(url)
|
||||
.done(function(data) {
|
||||
var html = `
|
||||
<div class="row">
|
||||
<div class="col-md-6">
|
||||
<h6 class="text-muted mb-2">Classroom</h6>
|
||||
<p class="fw-bold">${data.classroom.code} - ${data.classroom.name}</p>
|
||||
<p><i class="bi bi-building text-primary"></i> ${data.classroom.building} - Floor ${data.classroom.floor}</p>
|
||||
</div>
|
||||
<div class="col-md-6">
|
||||
<h6 class="text-muted mb-2">Subject</h6>
|
||||
<p class="fw-bold">${data.commission.subject.code} - ${data.commission.subject.name}</p>
|
||||
<p><i class="bi bi-person text-info"></i> ${data.commission.teacher_name}</p>
|
||||
</div>
|
||||
</div>
|
||||
<div class="row mt-3">
|
||||
<div class="col-md-6">
|
||||
<h6 class="text-muted mb-2">Schedule</h6>
|
||||
<p><i class="bi bi-clock text-warning"></i> ${new Date(data.start_time).toLocaleString()}</p>
|
||||
<p><i class="bi bi-clock text-success"></i> ${new Date(data.end_time).toLocaleString()}</p>
|
||||
</div>
|
||||
<div class="col-md-6">
|
||||
<h6 class="text-muted mb-2">Details</h6>
|
||||
<p><i class="bi bi-people text-primary"></i> ${data.expected_attendees} attendees</p>
|
||||
<p><span class="badge bg-${data.status === 'CONFIRMED' ? 'success' : 'warning'}">${data.status}</span></p>
|
||||
</div>
|
||||
</div>
|
||||
${data.notes ? `<div class="row mt-3"><div class="col-12"><h6 class="text-muted mb-2">Notes</h6><p>${data.notes}</p></div></div>` : ''}
|
||||
`;
|
||||
$('#reservationDetails').html(html);
|
||||
$('#reservationModal').modal('show');
|
||||
})
|
||||
.fail(function() {
|
||||
$('#reservationDetails').html('<p class="text-danger">Error loading reservation details.</p>');
|
||||
$('#reservationModal').modal('show');
|
||||
});
|
||||
}
|
||||
});
|
||||
</script>
|
||||
{% endblock %}
|
||||
Reference in New Issue
Block a user