render hours

This commit is contained in:
Tykayn 2025-06-06 23:55:50 +02:00 committed by tykayn
parent 9b09b0d59d
commit 56ec8ba4f0
3 changed files with 252 additions and 28 deletions

View file

@ -1,4 +1,3 @@
/**
* Gestion du formulaire d'horaires d'ouverture
*
@ -16,11 +15,26 @@ const openingHoursFormManager = {
}
this.defaultOpeningHours = openingHoursInput.value;
// Créer la div de rendu si elle n'existe pas
let renderDiv = document.getElementById('opening_hours_render');
if (!renderDiv) {
renderDiv = document.createElement('div');
renderDiv.id = 'opening_hours_render';
renderDiv.classList.add('mt-4');
openingHoursInput.parentNode.insertBefore(renderDiv, openingHoursInput.nextSibling);
}
this.makeForm(inputSelector);
if (this.defaultOpeningHours !== '') {
this.parseOpeningHoursValue(inputSelector);
}
// Ajouter un écouteur d'événement keyup sur l'input des horaires
openingHoursInput.addEventListener('keyup', () => {
this.defaultOpeningHours = openingHoursInput.value;
this.parseOpeningHoursValue(inputSelector);
});
},
setInputSelector: function (inputSelector) {
this.inputSelector = inputSelector;
@ -31,7 +45,7 @@ const openingHoursFormManager = {
*/
parseOpeningHoursValue: function (inputSelector = 'input[name="custom__opening_hours"]') {
// Analyser la chaîne d'horaires d'ouverture
const parsedOpeningHours = {};
const parsedOpeningHours = [];
// Masquer toutes les plages horaires par défaut
const allDayContainers = document.querySelectorAll('.jour-container');
@ -48,7 +62,9 @@ const openingHoursFormManager = {
rules.forEach(rule => {
// Extraire les jours et les heures
const [days, hours] = rule.split(' ').filter(Boolean);
const parts = rule.split(' ').filter(Boolean);
const days = parts[0];
const hours = parts.slice(1).join(' ');
// Convertir les jours en français
const daysMap = {
@ -67,27 +83,94 @@ const openingHoursFormManager = {
const startIndex = Object.keys(daysMap).indexOf(start);
const endIndex = Object.keys(daysMap).indexOf(end);
const dayRange = [];
for (let i = startIndex; i <= endIndex; i++) {
const day = daysMap[Object.keys(daysMap)[i]];
const day = Object.keys(daysMap)[i];
dayRange.push(day);
// Cocher la case du jour
const checkbox = document.querySelector(`#jour-${day}`);
const checkbox = document.querySelector(`#jour-${daysMap[day]}`);
if (checkbox) {
checkbox.checked = true;
checkbox.closest('.jour-container').querySelector('.horaires-container').classList.remove('d-none');
const horairesContainer = checkbox.closest('.jour-container').querySelector('.horaires-container');
horairesContainer.classList.remove('d-none');
// Décocher la deuxième plage si elle n'est pas présente dans l'input
if (hours && !hours.includes(',')) {
const plage2Checkbox = horairesContainer.querySelector(`input[name="${daysMap[day]}-plage2-active"]`);
if (plage2Checkbox) {
plage2Checkbox.checked = false;
}
}
// Remplir la première plage horaire si spécifiée
if (hours) {
const [startTime, endTime] = hours.split('-');
if (startTime && endTime) {
const [startHour, startMinute] = startTime.split(':');
const [endHour, endMinute] = endTime.split(':');
const startHourInput = horairesContainer.querySelector(`input[name="${daysMap[day]}-plage1-start-hour"]`);
const startMinuteInput = horairesContainer.querySelector(`input[name="${daysMap[day]}-plage1-start-minute"]`);
const endHourInput = horairesContainer.querySelector(`input[name="${daysMap[day]}-plage1-end-hour"]`);
const endMinuteInput = horairesContainer.querySelector(`input[name="${daysMap[day]}-plage1-end-minute"]`);
if (startHourInput) startHourInput.value = startHour;
if (startMinuteInput) startMinuteInput.value = startMinute;
if (endHourInput) endHourInput.value = endHour;
if (endMinuteInput) endMinuteInput.value = endMinute;
}
}
}
}
parsedOpeningHours.push({
days: dayRange,
hours: hours
});
} else {
// Jour unique
const day = daysMap[days];
const checkbox = document.querySelector(`#jour-${day}`);
if (checkbox) {
checkbox.checked = true;
checkbox.closest('.jour-container').querySelector('.horaires-container').classList.remove('d-none');
const horairesContainer = checkbox.closest('.jour-container').querySelector('.horaires-container');
horairesContainer.classList.remove('d-none');
// Décocher la deuxième plage si elle n'est pas présente dans l'input
if (hours && !hours.includes(',')) {
const plage2Checkbox = horairesContainer.querySelector(`input[name="${day}-plage2-active"]`);
if (plage2Checkbox) {
plage2Checkbox.checked = false;
}
}
// Remplir la première plage horaire si spécifiée
if (hours) {
const [startTime, endTime] = hours.split('-');
if (startTime && endTime) {
const [startHour, startMinute] = startTime.split(':');
const [endHour, endMinute] = endTime.split(':');
const startHourInput = horairesContainer.querySelector(`input[name="${day}-plage1-start-hour"]`);
const startMinuteInput = horairesContainer.querySelector(`input[name="${day}-plage1-start-minute"]`);
const endHourInput = horairesContainer.querySelector(`input[name="${day}-plage1-end-hour"]`);
const endMinuteInput = horairesContainer.querySelector(`input[name="${day}-plage1-end-minute"]`);
if (startHourInput) startHourInput.value = startHour;
if (startMinuteInput) startMinuteInput.value = startMinute;
if (endHourInput) endHourInput.value = endHour;
if (endMinuteInput) endMinuteInput.value = endMinute;
}
}
}
parsedOpeningHours.push({
days: [days],
hours: hours
});
}
});
}
this.renderOpeningHours(parsedOpeningHours);
console.log(parsedOpeningHours);
},
makeForm: function (inputSelector = 'input[name="custom__opening_hours"]') {
@ -107,7 +190,7 @@ const openingHoursFormManager = {
// Créer les cases à cocher pour chaque jour
const jours = ['Lundi', 'Mardi', 'Mercredi', 'Jeudi', 'Vendredi', 'Samedi', 'Dimanche'];
const joursDiv = document.createElement('div');
joursDiv.classList.add('jours-ouverture', 'mb-4', 'row', 'g-3', 'mx-4');
joursDiv.classList.add('jours-ouverture', 'mb-4', 'row', 'mx-4');
jours.forEach(jour => {
const jourContainer = document.createElement('div');
@ -129,25 +212,17 @@ const openingHoursFormManager = {
label.textContent = jour;
divCheck.appendChild(input);
divCheck.appendChild(label);
jourContainer.appendChild(divCheck);
// Conteneur pour les plages horaires
const horairesContainer = document.createElement('div');
horairesContainer.classList.add('horaires-container', 'ms-4', 'd-none');
horairesContainer.classList.add('horaires-container', 'ms-4', 'd-none', 'row', 'g-3');
// Première plage horaire
const plage1 = this.createTimeRangeInputs(`${jour.toLowerCase()}-plage1`);
horairesContainer.appendChild(plage1);
// Séparateur
const separator = document.createElement('div');
separator.classList.add('text-center', 'my-2');
separator.textContent = 'et';
horairesContainer.appendChild(separator);
// Deuxième plage horaire
const plage2 = this.createTimeRangeInputs(`${jour.toLowerCase()}-plage2`);
horairesContainer.appendChild(plage2);
@ -204,7 +279,7 @@ const openingHoursFormManager = {
createTimeRangeInputs: function (prefix) {
const container = document.createElement('div');
container.classList.add('time-range', 'mb-2');
container.classList.add('time-range', 'mb-2', 'col-12', 'col-md-6');
// Case à cocher pour activer la plage
const checkboxContainer = document.createElement('div');
@ -365,20 +440,147 @@ const openingHoursFormManager = {
}
});
// Optimiser les plages horaires identiques consécutives
let optimizedRules = [];
let currentRule = null;
let currentDays = [];
let currentHours = null;
joursSelectionnes.forEach((jour, index) => {
const hours = horairesParJour[jour];
if (currentHours === null) {
// Première règle
currentHours = hours;
currentDays = [jour];
} else if (hours === currentHours) {
// Mêmes horaires que la règle en cours
currentDays.push(jour);
} else {
// Horaires différents, on finalise la règle en cours
if (currentDays.length > 0) {
optimizedRules.push({
days: currentDays,
hours: currentHours
});
}
// On commence une nouvelle règle
currentDays = [jour];
currentHours = hours;
}
// Si c'est le dernier jour, on finalise la règle en cours
if (index === joursSelectionnes.length - 1 && currentDays.length > 0) {
optimizedRules.push({
days: currentDays,
hours: currentHours
});
}
});
// Construire la chaîne au format OSM
let osmFormat = '';
if (joursSelectionnes.length > 0) {
const horairesStrings = joursSelectionnes.map(jour => {
return horairesParJour[jour] ? `${jour} ${horairesParJour[jour]}` : jour;
});
osmFormat = horairesStrings.join('; ');
}
let osmFormat = optimizedRules.map(rule => {
const days = rule.days.length > 1 ? `${rule.days[0]}-${rule.days[rule.days.length - 1]}` : rule.days[0];
return rule.hours ? `${days} ${rule.hours}` : days;
}).join('; ');
// Mettre à jour l'input custom__opening_hours
const customOpeningHours = document.querySelector(inputSelector);
if (customOpeningHours) {
customOpeningHours.value = osmFormat;
}
// Mettre à jour le rendu visuel
this.renderOpeningHours(optimizedRules);
},
renderOpeningHours: function (rules) {
const container = document.getElementById('opening_hours_render');
console.log('renderOpeningHours', rules);
if (!container) return;
// Vider le conteneur
container.innerHTML = '';
// Créer le style pour les sections d'ouverture
const style = document.createElement('style');
style.textContent = `
.opening-hours-day {
background-color: #f8f9fa;
border-radius: 4px;
margin-bottom: 8px;
padding: 8px;
position: relative;
height: 40px;
}
.opening-hours-time {
background-color: #d4edda;
border-radius: 4px;
position: absolute;
height: 24px;
top: 8px;
}
.opening-hours-label {
position: absolute;
left: 8px;
top: 50%;
transform: translateY(-50%);
z-index: 1;
}
`;
document.head.appendChild(style);
// Mapping des jours OSM vers français
const joursMap = {
'Mo': 'Lundi',
'Tu': 'Mardi',
'We': 'Mercredi',
'Th': 'Jeudi',
'Fr': 'Vendredi',
'Sa': 'Samedi',
'Su': 'Dimanche'
};
// Créer les lignes pour chaque jour
Object.entries(joursMap).forEach(([osmDay, frenchDay]) => {
const dayDiv = document.createElement('div');
dayDiv.classList.add('opening-hours-day');
// Ajouter le label du jour
const label = document.createElement('span');
label.classList.add('opening-hours-label');
label.textContent = frenchDay;
dayDiv.appendChild(label);
// Trouver les horaires pour ce jour
const rule = rules.find(r => r.days.includes(osmDay));
if (rule && rule.hours) {
const timeRanges = rule.hours.split(',');
timeRanges.forEach((timeRange, index) => {
const [start, end] = timeRange.split('-');
const [startHour, startMinute] = start.split(':');
const [endHour, endMinute] = end.split(':');
// Calculer la position et la largeur
const startMinutes = parseInt(startHour) * 60 + parseInt(startMinute);
const endMinutes = parseInt(endHour) * 60 + parseInt(endMinute);
const totalMinutes = 24 * 60;
const left = (startMinutes / totalMinutes) * 100;
const width = ((endMinutes - startMinutes) / totalMinutes) * 100;
const timeDiv = document.createElement('div');
timeDiv.classList.add('opening-hours-time');
timeDiv.style.left = `${left}%`;
timeDiv.style.width = `${width}%`;
timeDiv.title = `${start}-${end}`;
dayDiv.appendChild(timeDiv);
});
}
container.appendChild(dayDiv);
});
},
getTimeRange: function (prefix) {

View file

@ -83,4 +83,20 @@ table.js-sort-table th:active {
#attribution {
font-size: 0.6rem;
}
#restaurant .form-check-label {
margin-top: 0;
display: block;
cursor: pointer;
}
#restaurant .form-check-label:hover {
background-color: #f0f0f0;
color: #1e40c6;
}
#advanced_tags {
border-left: solid 3px #ccc;
padding-left: 2rem;
}

View file

@ -165,17 +165,23 @@
</div>
</div>
</div>
<div id="completion_progress" class="">
<div class="container">
</div>
<div id="completion_progress" class="">
<div class="container">
<div class="row">
<div class="col-6">
<div id="completion_display" class="mb-2"></div>
</div>
<div class="col-6">
<div class="progress" role="progressbar" aria-label="Progression de complétion" aria-valuenow="0" aria-valuemin="0" aria-valuemax="100">
<div class="progress-bar" style="width: 0%"></div>
</div>
</div>
</div>
</div>
</div>
{% endblock %}
{% block javascripts %}
{{ parent() }}