ScoDoc/app/templates/formsemestre/edt.j2

296 lines
11 KiB
Django/Jinja

{% extends "sco_page.j2" %}
{% import 'wtf.j2' as wtf %}
{% block styles %}
{{super()}}
<link rel="stylesheet" href="{{scu.STATIC_DIR}}/libjs/tui.calendar/toastui-calendar.min.css"/>
<link rel="stylesheet" href="{{scu.STATIC_DIR}}/libjs/bootstrap-multiselect-1.1.2/bootstrap-multiselect.min.css"/>
<link rel="stylesheet" href="{{scu.STATIC_DIR}}/css/edt.css">
{% endblock %}
{% block app_content %}
<div class="tab-content">
<h2>Expérimental: emploi du temps</h2>
{{ form_groups_choice|safe }}
<div class="dropdown">
<button class="dropbtn">Semaine</button>
<div class="dropdown-content">
<ul>
{% for v in views_names %}
<li><a href="#" data-view="{{v}}">{{views_names[v]}}</a></li>
{% endfor %}
<li>
<label>
<input type="checkbox" id="showModulesTitles"
{{ "checked" if show_modules_titles else "" }}
>
noms des modules
</label>
</li>
</ul>
</div>
</div>
<form id="calendar_control_form" method="GET">
<input type="hidden" name="current_date" value="{{current_date}}">
<input type="hidden" name="show_modules_titles" value="{{ 1 if show_modules_titles else 0 }}">
<input type="submit" value="Apply Changes" hidden>
</div>
<div>
<span id="menu-navi">
<button type="button" class="btn btn-default btn-sm move-today"
data-action="move-today">Aujourd'hui</button>
<button type="button" class="btn btn-default btn-sm move-day" data-action="move-prev">
<i class="calendar-icon ic-arrow-line-left" data-action="move-prev">&lt;</i>
</button>
<button type="button" class="btn btn-default btn-sm move-day" data-action="move-next">
<i class="calendar-icon ic-arrow-line-right" data-action="move-next">&gt;</i>
</button>
</span>
<span id="renderRange" class="render-range"></span>
<span id="cal_warning">Attention: dates en dehors de ce semestre !</span>
</div>
<div id="formsemestre-calendar" style="height: calc(100vh - 180px);"></div>
<div class="help">
<ul>
<li>Les heures sont toujours affichées dans le fuseau horaire du serveur,
qui est en principe celui des étudiants.
</li>
<li>Si vous filtrez par groupe, les évènements dont le groupe n'est pas reconnu seront affichés.
</li>
{% if formsemestre.can_be_edited_by(current_user) %}
<li><a class="stdlink" href="{{
url_for('notes.formsemestre_edt_help_config',
scodoc_dept=g.scodoc_dept, formsemestre_id= formsemestre.id)
}}">Aide à la configuration de l'emploi du temps</a>
{% endif %}
</ul>
</div>
</div>
{% endblock app_content %}
{% block scripts %}
{{ super() }}
<script src="{{scu.STATIC_DIR}}/libjs/bootstrap-multiselect-1.1.2/bootstrap-multiselect.min.js"></script>
<script src="{{scu.STATIC_DIR}}/libjs/purl.js"></script>
<script src="{{scu.STATIC_DIR}}/libjs/tui.calendar/toastui-calendar.min.js"></script>
<script src="{{scu.STATIC_DIR}}/js/groups_view.js"></script>
<script>
let hm_formatter = new Intl.DateTimeFormat('default', {
hour: '2-digit',
minute: '2-digit',
hour12: false
});
function getDataAction(target) {
return target.dataset ? target.dataset.action : target.getAttribute('data-action');
}
function getNextDayISODate(isoDate) {
// Parse the ISO date string into a Date object
const date = new Date(isoDate);
// Add one day
date.setDate(date.getDate() + 1);
// Convert back to ISO date string (YYYY-MM-DD)
return date.toISOString().split('T')[0];
}
var calendar;
document.addEventListener('DOMContentLoaded', function() {
document.getElementById('menu-navi').addEventListener('click', onClickNavi);
const Calendar = tui.Calendar;
const container = document.getElementById('formsemestre-calendar');
const options = {
defaultView: '{{view}}',
calendars: [
{
id: 'cal1',
name: 'Personal',
backgroundColor: '#03bd9e',
borderColor: 'white',
},
],
isReadOnly: true,
// timezone: { zones: [ { timezoneName: 'Europe/Paris' } ] },
template: {
// ce template nous permet d'avoir du HTML dans le title de l'event
time: function(event) {
const date_start = new Date(event.start);
const start = hm_formatter.format(date_start);
return `<strong>${start}</strong> <span>${event.title}</span> <div class="raw-event">${event.raw}</div>`;
},
},
timezone: {
zones: [
{
timezoneName: 'CET', // TODO récupérer timezone serveur
},
],
},
usageStatistics: false,
week: {
dayNames: [ "Dimanche", "Lundi", "Mardi", "Mercredi", "Jeudi", "Vendredi", "Samedi"],
eventView: ['time'],
hourStart: {{ hour_start }},
hourEnd: {{ hour_end }},
showNowIndicator: true,
startDayOfWeek: 1,
taskView: false,
useDetailPopup:false, // on va pouvoir placer les liens scodoc
workweek: true, // TODO voir samedi travaillé
},
};
calendar = new Calendar(container, options);
fetch(`${SCO_URL}../api/formsemestre/{{formsemestre.id}}/edt?{{groups_query_args|safe}}&show_modules_titles={{show_modules_titles}}`)
.then(r=>{return r.json()})
.then(events=>{
if (typeof events == 'string') {
// error !
alert("error: " + events)
} else {
calendar.createEvents(events);
}
});
{% if current_date %}
// we need to add one day because our weeks are starting on Monday
calendar.setDate( getNextDayISODate("{{current_date}}") );
{% endif %}
changeCalendarDate();
function formatDate(date) {
let year = date.getFullYear();
let month = (date.getMonth() + 1).toString().padStart(2, '0'); // Months are zero-indexed in JavaScript
let day = date.getDate().toString().padStart(2, '0');
return `${day}/${month}/${year}`;
}
function setRenderRangeText() {
var renderRange = document.getElementById('renderRange');
var options = calendar.getOptions();
var viewName = calendar.getViewName();
var html = [];
if (viewName === 'day') {
html.push(calendar.getDate().toDate().toLocaleString('fr-Fr', {
year: 'numeric',
month: 'long',
day: 'numeric'}));
} else if (viewName === 'month' &&
(!options.month.visibleWeeksCount || options.month.visibleWeeksCount > 4)) {
html.push(
calendar.getDate().toDate().toLocaleString('fr-Fr', {
year: 'numeric',
month: 'long',
}));
} else {
html.push(formatDate(calendar.getDateRangeStart()));
html.push(' - ');
html.push(formatDate(calendar.getDateRangeEnd()));
}
renderRange.innerHTML = html.join('');
}
function onClickNavi(e) {
var action = getDataAction(e.target);
switch (action) {
case 'move-prev':
calendar.prev();
break;
case 'move-next':
calendar.next();
break;
case 'move-today':
calendar.today();
break;
default:
return;
}
// setSchedules();
changeCalendarDate();
}
// Update current URL when date change (newDate=ISO string)
function updateParametersInUrl(newParams) {
// Parse the current URL
const currentUrl = new URL(window.location.href);
// Access and modify the search parameters
const searchParams = currentUrl.searchParams;
Object.entries(newParams).forEach(([key, value]) => {
searchParams.set(key, value);
});
// Create the new URL
const newUrl = `${currentUrl.origin}${currentUrl.pathname}?${searchParams.toString()}`;
// Update the URL without reloading the page
window.history.pushState({ path: newUrl }, '', newUrl);
}
// Update "current" date (URL and title)
function changeCalendarDate() {
setRenderRangeText();
// current calendar date, ISO, without time
const iso_date_start = calendar.getDateRangeStart().toDate().toISOString().split('T')[0];
const iso_date_end = calendar.getDateRangeEnd().toDate().toISOString().split('T')[0];
const cal_warning = document.querySelector('#cal_warning');
updateParametersInUrl( { current_date: iso_date_start } );
calendar_control_form.current_date.value = iso_date_start;
if ((iso_date_start > "{{ formsemestre.date_fin.isoformat() }}")
|| (iso_date_end < "{{ formsemestre.date_debut.isoformat() }}")) {
cal_warning.style.display = 'inline-block';
} else {
cal_warning.style.display = 'none';
}
}
// View menu
const dropdown = document.querySelector('.dropdown');
const dropbtn = document.querySelector('.dropbtn');
const dropdownContent = document.querySelector('.dropdown-content');
dropbtn.textContent = "{{views_names[view]}}";
dropbtn.addEventListener('click', function(event) {
dropdownContent.style.display = dropdownContent.style.display === 'block' ? 'none' : 'block';
event.stopPropagation();
});
document.querySelectorAll('.dropdown-content a').forEach(item => {
item.addEventListener('click', function(e) {
e.preventDefault();
const selectedText = this.textContent;
const selectedView = this.getAttribute('data-view');
calendar.changeView(selectedView); // Change the calendar view
updateParametersInUrl( { view : selectedView } );
setRenderRangeText();
dropbtn.textContent = selectedText; // Update the button text
dropdownContent.style.display = 'none';
});
});
const showModulesTitlesCheckbox = document.getElementById('showModulesTitles');
showModulesTitlesCheckbox.addEventListener('change', function() {
calendar_control_form.show_modules_titles.value = (calendar_control_form.show_modules_titles.value=="1") ? "0" : "1";
calendar_control_form.submit();
});
// Close dropdown when clicking outside
window.addEventListener('click', function() {
if (dropdownContent.style.display === 'block') {
dropdownContent.style.display = 'none';
}
});
});
</script>
{% endblock scripts %}