const path = window.location.pathname // Setting up interactivity according to the current page if (path == '/login') { trim_inputs_on_blur() } else if (path == '/') { // Payment table show_hide_search() allow_select_reset() allow_date_reset() } else if (path == '/payment') { // Payment creation trim_inputs_on_blur() auto_fill_category() } else if (path.startsWith('/payment/')) { // Payment modification trim_inputs_on_blur() auto_fill_category() control_remove_button() } else if (path == '/income') { // Income creation trim_inputs_on_blur() } else if (path.startsWith('/income/')) { // Income modification trim_inputs_on_blur() control_remove_button() } else if (path == '/category') { // Category creation trim_inputs_on_blur() } else if (path.startsWith('/category/')) { // Category modification trim_inputs_on_blur() control_remove_button() } else if (path == '/balance') { } else if (path == '/statistics') { show_statistics() } // Functions function trim_inputs_on_blur() { document.querySelectorAll('input').forEach(input => { input.addEventListener('blur', () => { input.value = input.value.trim() }) }) } function show_hide_search() { document.querySelector('.g-ShowSearch button').addEventListener('click', () => { document.querySelector('.g-Aside').classList.add('g-Aside--Show') }) document.querySelector('.g-Payments__CloseFilters').addEventListener('click', () => { document.querySelector('.g-Aside').classList.remove('g-Aside--Show') }) } function allow_select_reset() { document.querySelectorAll('select').forEach(select => { const canBeReset = Array.from(select.options).find(option => option.disabled) if (canBeReset) { const button = document.createElement('input') button.type = 'button' button.value = 'Effacer' button.className = 'g-Form__ResetSelect' button.onclick = function() { select.selectedIndex = 0 button.style = 'visibility: hidden' } if (select.selectedIndex === 0) { button.style = 'visibility: hidden' } select.onchange = function() { button.style = 'visibility: visible' } select.parentNode.appendChild(button) } }) } function allow_date_reset() { document.querySelectorAll('input[type="date"]').forEach(input => { const button = document.createElement('input') button.type = 'button' button.value = 'Effacer' button.className = 'g-Form__ResetSelect' button.onclick = function() { input.value = '' button.style = 'visibility: hidden' } if (input.value === '') { button.style = 'visibility: hidden' } input.onchange = function() { button.style = 'visibility: visible' } input.parentNode.appendChild(button) }) } function control_remove_button() { const removeInput = document.getElementsByName('remove-input')[0] const removeButton = document.getElementById('remove-button') if (removeInput && removeButton) { removeInput.addEventListener('input', () => { if (removeInput.value.trim() == removeInput.getAttribute('data-name')) { removeButton.removeAttribute('disabled') } else { removeButton.setAttribute('disabled', true) } }) } } function auto_fill_category() { const name = document.getElementsByName('name')[0] const category = document.getElementsByName('category_id')[0] function onNameChange() { const query = name.value.trim() if (query) { const xhttp = new XMLHttpRequest() xhttp.onreadystatechange = () => { if (xhttp.readyState == 4 && xhttp.status == 200) { category.value = xhttp.responseText } } xhttp.open('GET', `/payment/category?payment_name=${query}`, true) xhttp.send() } } name.addEventListener('input', debounce(onNameChange, 500)) } function show_statistics() { const categories = JSON.parse(document.getElementById('categories').textContent) const incomes = JSON.parse(document.getElementById('incomes').textContent) const payments = JSON.parse(document.getElementById('payments').textContent) const dates = incomes.map(i => i.date) const datasets = [ { label: 'Revenus', data: incomes.map(i => i.amount), fill: false, backgroundColor: '#222222', borderColor: '#222222', hidden: true } ] const total_payments = {} const categories_payments = {} payments.forEach(p => { if (categories_payments[p.category_id] === undefined) { categories_payments[p.category_id] = {} } categories_payments[p.category_id][p.start_date] = p.cost if (total_payments[p.start_date] === undefined) { total_payments[p.start_date] = 0 } total_payments[p.start_date] += p.cost }) datasets.push({ label: 'Total des paiements', data: dates.map(d => total_payments[d] || 0), fill: false, backgroundColor: '#555555', borderColor: '#555555' }) Object.keys(categories_payments).forEach(category_id => { const category_payments = categories_payments[category_id] const category = categories.find(c => c.id == category_id) datasets.push({ label: category.name, data: dates.map(d => category_payments[d] || 0), fill: false, backgroundColor: category.color, borderColor: category.color, }) }) const chart = new Chart(document.getElementById('g-Chart__Canvas').getContext('2d'), { type: 'line', data: { labels: dates, datasets }, options: { interaction: { intersect: false, }, scales: { x: { ticks: { callback: index => prettyPrintMonth(dates[index]) } }, y: { ticks: { beginAtZero: true, callback: value => `${value.toLocaleString('fr-FR')} €` } } }, plugins: { tooltip: { callbacks: { title: items => capitalize(prettyPrintMonth(items[0].label)), label: item => { let label = item.dataset.label || '' if (label) label += ': ' label += `${item.raw.toLocaleString('fr-FR')} €` return label } } } } } }) } function debounce(callback, delay) { let timeout return function() { clearTimeout(timeout) timeout = setTimeout(callback, delay) } } function prettyPrintMonth(isoDate) { xs = isoDate.split('-') months = [ 'janvier', 'février', 'mars', 'avril', 'mai', 'juin', 'juillet', 'août', 'septembre', 'octobre', 'novembre', 'décembre' ] return `${months[parseInt(xs[1]) - 1]} ${xs[0]}` } function capitalize(str) { return str.replace(/^\w/, function (c) { return c.toUpperCase() }) } /******************/ /* Web components */ /******************/ /* Apply color given from attribute. * * This permits not to use the style attribute in HTML to respect a strict CSP policy. */ customElements.define( 'colored-category', class extends HTMLSpanElement { constructor() { super() const span = this span.style = `color: ${span.getAttribute('data-color')}` } }, { extends: 'span' } )