mirror of
https://github.com/eduardogsilva/wireguard_webadmin.git
synced 2026-02-19 19:26:17 +00:00
298 lines
14 KiB
HTML
298 lines
14 KiB
HTML
{% extends "base.html" %}
|
|
{% load i18n %}
|
|
{% load crispy_forms_tags %}
|
|
|
|
{% block page_custom_head %}
|
|
<style>
|
|
.grid-cell {
|
|
transition: background-color 0.3s ease;
|
|
}
|
|
</style>
|
|
{% endblock %}
|
|
|
|
{% block content %}
|
|
<div class="card card-primary card-outline">
|
|
<div class="card-header">
|
|
<h3 class="card-title">{{ title }}</h3>
|
|
</div>
|
|
<form method="post">
|
|
{% csrf_token %}
|
|
<div class="card-body">
|
|
<div class="row">
|
|
<div class="col-12">
|
|
{{ form|crispy }}
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Schedule Grid Visualization -->
|
|
{% if profile %}
|
|
<div class="row mt-4">
|
|
<div class="col-12">
|
|
<div class="card card-outline card-secondary">
|
|
<div class="card-header">
|
|
<h4 class="card-title">{% trans "Schedule Visualization" %}</h4>
|
|
</div>
|
|
<div class="card-body p-1">
|
|
<div class="schedule-grid-container mb-1">
|
|
<div class="schedule-grid-header d-flex text-center mb-0">
|
|
<div class="flex-grow-1">Mon
|
|
</div>
|
|
<div class="flex-grow-1">Tue
|
|
</div>
|
|
<div class="flex-grow-1">Wed
|
|
</div>
|
|
<div class="flex-grow-1">Thu
|
|
</div>
|
|
<div class="flex-grow-1">Fri
|
|
</div>
|
|
<div class="flex-grow-1">Sat
|
|
</div>
|
|
<div class="flex-grow-1">Sun
|
|
</div>
|
|
</div>
|
|
<div class="schedule-grid-body">
|
|
{% for hour in "012345678901234567890123"|make_list %}
|
|
<div class="d-flex align-items-center mb-0">
|
|
<div class="grid-row flex-grow-1 d-flex gap-0" style="height: 6px;">
|
|
{% for day in "0123456"|make_list %}
|
|
<div class="grid-cell flex-grow-1 border-bottom border-end"
|
|
id="cell-{{ day }}-{{ forloop.parentloop.counter0 }}"
|
|
title="{{ forloop.parentloop.counter0 }}:00"
|
|
style="background-color: #f8f9fa;">
|
|
</div>
|
|
{% endfor %}
|
|
</div>
|
|
</div>
|
|
{% endfor %}
|
|
</div>
|
|
</div>
|
|
<div class="d-flex mt-0 justify-content-center">
|
|
<span><i class="fas fa-circle" style="color: #28a745;"></i> {% trans "Active" %}</span>
|
|
<span><i class="fas fa-circle" style="color: #dc3545;"></i> {% trans "Inactive" %}</span>
|
|
</div>
|
|
|
|
<div class="row">
|
|
<div class="col-12">
|
|
<ul>
|
|
{% if profile.active %}
|
|
<li><b>{% trans 'Current time' %}:</b> {{ now }}</li>
|
|
<li><b>{% trans 'Becomes active at' %}:</b> {{ profile.next_dates.enable|default_if_none:'' }}</li>
|
|
<li><b>{% trans 'Becomes inactive at' %}:</b> {{ profile.next_dates.disable|default_if_none:'' }}</li>
|
|
{% else %}
|
|
<li><b>{% trans 'Status' %}:</b> {% trans 'Inactive' %}</li>
|
|
{% endif %}
|
|
</ul>
|
|
</div>
|
|
</div>
|
|
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Existing Table -->
|
|
<div class="row mt-4">
|
|
<div class="col-12">
|
|
<div class="d-flex justify-content-between align-items-center mb-3">
|
|
<h4>{% trans "Time Intervals" %}</h4>
|
|
<a href="{% url 'manage_scheduler_slot' %}?profile_uuid={{ profile.uuid }}"
|
|
class="btn btn-sm btn-primary">
|
|
<i class="fas fa-plus"></i> {% trans "Add Interval" %}
|
|
</a>
|
|
</div>
|
|
<div class="table-responsive">
|
|
<table class="table table-hover">
|
|
<thead>
|
|
<tr>
|
|
<th>{% trans "Start Day" %}</th>
|
|
<th>{% trans "Start Time" %}</th>
|
|
<th>{% trans "End Day" %}</th>
|
|
<th>{% trans "End Time" %}</th>
|
|
<th class="text-end">{% trans "Actions" %}</th>
|
|
</tr>
|
|
</thead>
|
|
<tbody>
|
|
{% for slot in slots %}
|
|
<tr>
|
|
<td>{{ slot.get_start_weekday_display }}</td>
|
|
<td>{{ slot.start_time }}</td>
|
|
<td>{{ slot.get_end_weekday_display }}</td>
|
|
<td>{{ slot.end_time }}</td>
|
|
<td class="text-end">
|
|
<a href="{% url 'manage_scheduler_slot' %}?uuid={{ slot.uuid }}"
|
|
class="btn btn-sm btn-outline-primary" title="{% trans 'Edit' %}">
|
|
<i class="fas fa-edit"></i>
|
|
</a>
|
|
<a href="{% url 'delete_scheduler_slot' %}?uuid={{ slot.uuid }}"
|
|
class="btn btn-sm btn-outline-danger" title="{% trans 'Delete' %}">
|
|
<i class="fas fa-trash"></i>
|
|
</a>
|
|
</td>
|
|
</tr>
|
|
{% empty %}
|
|
<tr>
|
|
<td colspan="5" class="text-center text-muted">
|
|
{% trans "No time intervals found." %}
|
|
</td>
|
|
</tr>
|
|
{% endfor %}
|
|
</tbody>
|
|
</table>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
{% endif %}
|
|
|
|
<!-- Peers List Debug Table -->
|
|
{% if show_peers %}
|
|
<div class="row mt-4">
|
|
<div class="col-12">
|
|
<div class="card card-outline card-info">
|
|
<div class="card-header">
|
|
<h4 class="card-title">{% trans "Linked Peers (Debug)" %}</h4>
|
|
</div>
|
|
<div class="card-body p-0">
|
|
<div class="table-responsive">
|
|
<table class="table table-hover table-striped mb-0">
|
|
<thead>
|
|
<tr>
|
|
<th>{% trans "Peer" %}</th>
|
|
<th>{% trans "Disabled by Schedule" %}</th>
|
|
<th>{% trans "Next Enable" %}</th>
|
|
<th>{% trans "Next Disable" %}</th>
|
|
</tr>
|
|
</thead>
|
|
<tbody>
|
|
{% for item in peers_scheduling %}
|
|
<tr>
|
|
<td>
|
|
<a href="{% url 'wireguard_peer_manage' %}?peer={{ item.peer.uuid }}">
|
|
{{ item.peer }}
|
|
</a>
|
|
</td>
|
|
<td>
|
|
{% if item.peer.disabled_by_schedule %}
|
|
<span class="badge bg-danger">{% trans "Yes" %}</span>
|
|
{% else %}
|
|
<span class="badge bg-success">{% trans "No" %}</span>
|
|
{% endif %}
|
|
</td>
|
|
<td>{{ item.next_scheduled_enable_at|default:"-" }}</td>
|
|
<td>{{ item.next_scheduled_disable_at|default:"-" }}</td>
|
|
</tr>
|
|
{% empty %}
|
|
<tr>
|
|
<td colspan="4" class="text-center text-muted">{% trans "No peers linked to this profile." %}</td>
|
|
</tr>
|
|
{% endfor %}
|
|
</tbody>
|
|
</table>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
{% endif %}
|
|
|
|
<div class="row mt-4">
|
|
<div class="col-12 d-flex justify-content-end gap-2">
|
|
<button type="submit" class="btn btn-primary">
|
|
<i class="fas fa-save"></i> {% trans "Save" %}
|
|
</button>
|
|
<a href="{% url 'scheduler_profile_list' %}" class="btn btn-secondary">
|
|
<i class="fas fa-times"></i> {% trans "Cancel" %}
|
|
</a>
|
|
{% if profile %}
|
|
<a href="?uuid={{ profile.uuid }}&show_peers=true" class="btn btn-outline-info">
|
|
<i class="fas fa-bug"></i> {% trans "Show Peers" %}
|
|
</a>
|
|
{% endif %}
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</form>
|
|
</div>
|
|
{% endblock %}
|
|
|
|
{% block custom_page_scripts %}
|
|
<script>
|
|
document.addEventListener('DOMContentLoaded', function () {
|
|
const slots = [
|
|
{% for slot in slots %}
|
|
{
|
|
start_day: {{ slot.start_weekday }},
|
|
end_day: {{ slot.end_weekday }},
|
|
start_time: "{{ slot.start_time|time:'H:i' }}",
|
|
end_time: "{{ slot.end_time|time:'H:i' }}"
|
|
},
|
|
{% endfor %}
|
|
];
|
|
|
|
function timeToMinutes(timeStr) {
|
|
const [hours, minutes] = timeStr.split(':').map(Number);
|
|
return hours * 60 + minutes;
|
|
}
|
|
|
|
function isHourCovered(day, hour) {
|
|
const hourStart = hour * 60;
|
|
const hourEnd = (hour + 1) * 60;
|
|
|
|
// Normalize day to 0-6 (Mon-Sun)
|
|
// Note: Our model uses 0=Monday, which matches the grid loop
|
|
|
|
for (const slot of slots) {
|
|
let currentDay = slot.start_day;
|
|
let currentMinutes = timeToMinutes(slot.start_time);
|
|
|
|
// Calculate total duration in minutes
|
|
let totalMinutes = 0;
|
|
let endMinutes = timeToMinutes(slot.end_time);
|
|
|
|
let tempDay = slot.start_day;
|
|
while (tempDay != slot.end_day) {
|
|
totalMinutes += 1440; // 24 * 60
|
|
tempDay = (tempDay + 1) % 7;
|
|
}
|
|
totalMinutes = totalMinutes - currentMinutes + endMinutes;
|
|
if (totalMinutes < 0) totalMinutes += 7 * 1440; // Wrap around week
|
|
|
|
// Check if the hour [day:hourStart, day:hourEnd] overlaps with [slotStart, slotEnd]
|
|
// Convert everything to week minutes
|
|
const slotStartWeekMinutes = slot.start_day * 1440 + currentMinutes;
|
|
const hourStartWeekMinutes = day * 1440 + hourStart;
|
|
const hourEndWeekMinutes = day * 1440 + hourEnd;
|
|
|
|
// Handle wrap around week
|
|
const weekTotal = 7 * 1440;
|
|
|
|
// A simple way is to check the range [slotStart, slotStart + totalMinutes]
|
|
// and see if the hour (or its week-repetitions) overlaps it.
|
|
|
|
// Try for current week and next week (to handle wrap)
|
|
for (let offset of [0, -weekTotal, weekTotal]) {
|
|
const s = slotStartWeekMinutes + offset;
|
|
const e = s + totalMinutes;
|
|
|
|
if (Math.max(s, hourStartWeekMinutes) < Math.min(e, hourEndWeekMinutes)) {
|
|
return true;
|
|
}
|
|
}
|
|
}
|
|
return false;
|
|
}
|
|
|
|
for (let day = 0; day < 7; day++) {
|
|
for (let hour = 0; hour < 24; hour++) {
|
|
const cell = document.getElementById(`cell-${day}-${hour}`);
|
|
if (cell) {
|
|
if (isHourCovered(day, hour)) {
|
|
cell.style.backgroundColor = '#28a745'; // green-ish
|
|
} else {
|
|
cell.style.backgroundColor = '#dc3545'; // red-ish
|
|
}
|
|
}
|
|
}
|
|
}
|
|
});
|
|
</script>
|
|
{% endblock %} |