| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579 |
- <!-- filepath: /Users/fabiofratini/Desktop/Projects/iao_team/resources/views/livewire/reports.blade.php -->
- <div class="col card--ui" id="card--dashboard">
- <header id="title--section" style="display:none !important"
- class="d-flex align-items-center justify-content-between">
- <div class="title--section_name d-flex align-items-center justify-content-between">
- <i class="ico--ui title_section utenti me-2"></i>
- <h2 class="primary">Reports</h2>
- </div>
- </header>
- <section id="subheader" class="d-flex align-items-center">
- </section>
- <section id="reports-section">
- <div class="row">
- <div class="col-md-12">
- <canvas id="monthly-in-out-chart"></canvas>
- </div>
- </div>
- <div class="col-md-12 chart-container">
- <canvas id="causals-chart" style="height: 300px; max-height: 300px;"></canvas>
- </div>
- <div class="row mt-5">
- <div class="col-md-12">
- <canvas id="tesserati-chart"></canvas>
- </div>
- </div>
- <div class="row mt-5">
- <div class="col-md-6">
- <select wire:model="selectedCourse" wire:change="updateCourseChart">
- <option value="">Seleziona un Corso</option>
- @foreach($courses as $course)
- <option value="{{ $course['id'] }}">{{ $course['full_name'] }}</option>
- @endforeach
- </select>
- </div>
- <div class="col-md-12 mt-3">
- <canvas id="courses-chart" style="height: 250px; max-height: 250px;"></canvas>
- </div>
- </div>
- </section>
- </div>
- @push('scripts')
- <script src="https://cdn.jsdelivr.net/npm/chart.js"></script>
- <script>
- document.addEventListener('DOMContentLoaded', function () {
- initializeChartSizes();
- window.livewire.on('dataUpdated', () => {
- updateCharts();
- updateCausalsChart();
- });
- window.livewire.on('courseDataUpdated', async (courseId) => {
- console.log('Course data update event received for course ID:', courseId);
- await updateCoursesChart(courseId);
- updateCausalsChart();
- Object.keys(window.chartSizes).forEach(chartId => {
- restoreChartSize(chartId);
- });
- });
- updateCharts();
- updateCausalsChart();
- updateTesseratiChart();
- async function updateCharts() {
- try {
- const monthlyData = await @this.getMonthlyTotals();
- if (window.monthlyInOutChart) {
- window.monthlyInOutChart.destroy();
- }
- const monthlyInOutCtx = document.getElementById('monthly-in-out-chart').getContext('2d');
- window.monthlyInOutChart = new Chart(monthlyInOutCtx, {
- type: 'bar',
- data: monthlyData,
- options: {
- responsive: true,
- scales: {
- x: {
- title: {
- display: false,
- text: 'Mese'
- },
- grid: {
- display: false
- }
- },
- y: {
- display: false,
- title: {
- display: false,
- text: 'Importo (€)'
- },
- beginAtZero: true,
- ticks: {
- display: false
- },
- grid: {
- display: false
- }
- }
- },
- plugins: {
- legend: {
- display: true
- },
- title: {
- display: true,
- text: 'Entrate/Uscite Mensili',
- font: {
- size: 16
- }
- },
- tooltip: {
- callbacks: {
- label: function (context) {
- let label = context.dataset.label || '';
- if (label) {
- label += ': ';
- }
- if (context.parsed.y !== null) {
- label += new Intl.NumberFormat('it-IT', {
- style: 'currency',
- currency: 'EUR'
- }).format(context.parsed.y);
- }
- return label;
- }
- }
- }
- }
- }
- });
- const summaryData = await @this.getYearlySummary();
- } catch (error) {
- console.error('Error updating charts:', error);
- document.getElementById('monthly-in-out-chart').insertAdjacentHTML(
- 'afterend',
- '<div class="alert alert-danger">Errore nel caricamento dei dati finanziari</div>'
- );
- }
- }
- async function updateCausalsChart() {
- try {
- const causalsData = await @this.getTopCausalsByAmount(10, 'IN');
- if (window.causalsChart) {
- window.causalsChart.destroy();
- }
- const causalsCtx = document.getElementById('causals-chart').getContext('2d');
- const existingTabs = document.querySelector('.causals-tabs');
- if (existingTabs) {
- existingTabs.remove();
- }
- const existingTitle = document.querySelector('.causals-title');
- if (existingTitle) {
- existingTitle.remove();
- }
- const chartTitle = document.createElement('h4');
- chartTitle.className = 'text-center mt-2 mb-3 causals-title';
- const chartCanvas = document.getElementById('causals-chart');
- chartCanvas.parentNode.insertBefore(chartTitle, chartCanvas);
- const inData = causalsData.inData;
- const colors = [
- 'rgba(54, 162, 235, 0.8)', // Blue
- 'rgba(75, 192, 192, 0.8)', // Teal
- 'rgba(153, 102, 255, 0.8)', // Purple
- 'rgba(255, 159, 64, 0.8)', // Orange
- 'rgba(39, 174, 96, 0.8)', // Green
- 'rgba(41, 128, 185, 0.8)', // Dark blue
- 'rgba(142, 68, 173, 0.8)', // Dark purple
- 'rgba(230, 126, 34, 0.8)', // Dark orange
- 'rgba(46, 204, 113, 0.8)', // Light green
- 'rgba(52, 152, 219, 0.8)' // Light blue
- ];
- const commonOptions = {
- responsive: true,
- maintainAspectRatio: false,
- plugins: {
- title: {
- display: true,
- text: 'Causali performanti',
- font: {
- size: 16
- }
- },
- tooltip: {
- callbacks: {
- label: function (context) {
- const fullName = inData[context.dataIndex]?.fullName || context.label;
- const value = context.raw;
- return fullName + ': ' + new Intl.NumberFormat('it-IT', {
- style: 'currency',
- currency: 'EUR'
- }).format(value);
- }
- }
- },
- legend: {
- display: true,
- position: 'right',
- labels: {
- boxWidth: 15,
- padding: 10,
- generateLabels: function (chart) {
- const data = chart.data;
- if (data.labels.length && data.datasets.length) {
- return data.labels.map(function (label, i) {
- const meta = chart.getDatasetMeta(0);
- const style = meta.controller.getStyle(i);
- let shortenedLabel = label;
- if (label.length > 20) {
- shortenedLabel = label.substring(0, 17) + '...';
- }
- return {
- text: shortenedLabel,
- fillStyle: style.backgroundColor,
- hidden: !chart.getDataVisibility(i),
- index: i,
- datasetIndex: 0
- };
- });
- }
- return [];
- }
- }
- }
- }
- };
- let chartData = {
- labels: inData.map(item => item.label),
- datasets: [{
- label: 'Importo',
- data: inData.map(item => item.value),
- backgroundColor: inData.map((item, index) => colors[index % colors.length]),
- borderWidth: 1,
- borderColor: '#fff'
- }]
- };
- window.causalsChart = new Chart(causalsCtx, {
- type: 'doughnut',
- data: chartData,
- options: commonOptions
- });
- } catch (error) {
- console.error('Error updating causals chart:', error);
- document.getElementById('causals-chart').insertAdjacentHTML(
- 'afterend',
- '<div class="alert alert-danger">Errore nel caricamento dei dati delle causali</div>'
- );
- }
- }
- });
- async function updateCoursesChart() {
- try {
- const courseData = await @this.getCourseMonthlyEarnings();
- console.log('Course data received:', courseData);
- if (window.coursesChart) {
- window.coursesChart.destroy();
- }
- const coursesCtx = document.getElementById('courses-chart').getContext('2d');
- const dashedLinesPlugin = {
- // Plugin definition unchanged
- id: 'dashedLines',
- beforeDatasetsDraw: (chart) => {
- const ctx = chart.ctx;
- const lineDataset = chart.data.datasets.find(d => d.type === 'line' && d.label === 'Pagamenti Attesi');
- const barDataset = chart.data.datasets.find(d => d.type === 'bar' && d.label === 'Pagamenti Effettuati');
- if (!lineDataset || !barDataset) return;
- const lineMeta = chart.getDatasetMeta(chart.data.datasets.indexOf(lineDataset));
- const barMeta = chart.getDatasetMeta(chart.data.datasets.indexOf(barDataset));
- if (!lineMeta.data.length || !barMeta.data.length) return;
- const missingData = lineDataset.missing || [];
- ctx.save();
- ctx.lineWidth = 2;
- ctx.setLineDash([8, 4]);
- ctx.strokeStyle = 'rgba(48, 51, 107, 0.3)';
- for (let i = 0; i < lineMeta.data.length; i++) {
- const linePoint = lineMeta.data[i];
- const barPoint = barMeta.data[i];
- if (!linePoint || !barPoint) continue;
- ctx.beginPath();
- ctx.moveTo(linePoint.x, linePoint.y);
- ctx.lineTo(linePoint.x, barPoint.y);
- ctx.stroke();
- if (missingData[i] && missingData[i] > 0) {
- const midY = (linePoint.y + barPoint.y) / 2;
- ctx.textAlign = 'center';
- ctx.font = '10px Arial';
- ctx.fillStyle = 'rgba(48, 51, 107, 0.8)';
- }
- }
- ctx.restore();
- }
- };
- window.coursesChart = new Chart(coursesCtx, {
- type: 'bar',
- plugins: [dashedLinesPlugin],
- data: courseData,
- options: {
- responsive: true,
- maintainAspectRatio: false,
- layout: {
- padding: {
- top: 20
- }
- },
- // Rest of options unchanged
- scales: {
- x: {
- grid: {
- display: false
- }
- },
- y: {
- display: true,
- beginAtZero: true,
- grid: {
- color: 'rgba(0, 0, 0, 0.1)',
- borderDash: [5, 5]
- },
- ticks: {
- callback: function (value) {
- return new Intl.NumberFormat('it-IT', {
- style: 'currency',
- currency: 'EUR',
- maximumFractionDigits: 0
- }).format(value);
- }
- }
- }
- },
- plugins: {
- legend: {
- display: true,
- position: 'top',
- align: 'center',
- labels: {
- usePointStyle: true,
- padding: 20
- }
- },
- title: {
- display: true,
- text: 'Pagamenti per corso',
- font: {
- size: 16
- }
- },
- tooltip: {
- callbacks: {
- label: function (context) {
- if (context.dataset.label === 'Pagamenti Attesi') {
- let parts = [];
- const participants = context.dataset.participants ? context.dataset.participants[context.dataIndex] : 0;
- parts.push('N° iscritti: ' + participants);
- const expectedAmount = context.parsed.y;
- parts.push('Pagamenti attesi: ' + new Intl.NumberFormat('it-IT', {
- style: 'currency',
- currency: 'EUR'
- }).format(expectedAmount));
- const missingAmount = context.dataset.missing ? context.dataset.missing[context.dataIndex] : 0;
- parts.push('Ancora da pagare: ' + new Intl.NumberFormat('it-IT', {
- style: 'currency',
- currency: 'EUR'
- }).format(missingAmount));
- return parts.join(' | ');
- }
- let label = context.dataset.label || '';
- if (label) {
- label += ': ';
- }
- if (context.parsed.y !== null) {
- label += new Intl.NumberFormat('it-IT', {
- style: 'currency',
- currency: 'EUR'
- }).format(context.parsed.y);
- }
- return label;
- }
- }
- }
- },
- elements: {
- point: {
- radius: 4,
- hoverRadius: 6
- },
- line: {
- tension: 0.4
- }
- }
- }
- });
- // Maintain chart dimensions
- coursesCtx.canvas.style.height = '250px';
- coursesCtx.canvas.style.maxHeight = '250px';
- } catch (error) {
- console.error('Error updating courses chart:', error);
- document.getElementById('courses-chart').insertAdjacentHTML(
- 'afterend',
- '<div class="alert alert-danger">Errore nel caricamento dei dati del corso</div>'
- );
- }
- }
- async function updateTesseratiChart() {
- try {
- const tesseratiData = await @this.getTesseratiData();
- if (window.tesseratiChart) {
- window.tesseratiChart.destroy();
- }
- const tesseratiCtx = document.getElementById('tesserati-chart').getContext('2d');
- window.tesseratiChart = new Chart(tesseratiCtx, {
- type: 'line',
- data: tesseratiData,
- options: {
- responsive: true,
- scales: {
- x: {
- title: {
- display: true,
- text: 'Anno Tesseramento'
- },
- grid: {
- display: false
- }
- },
- y: {
- title: {
- display: true,
- text: 'Numero di Tesserati'
- },
- beginAtZero: true,
- ticks: {
- precision: 0
- }
- }
- },
- plugins: {
- legend: {
- display: true,
- position: 'top'
- },
- title: {
- display: true,
- text: 'Andamento Tesserati per Anno',
- font: {
- size: 16
- }
- },
- tooltip: {
- callbacks: {
- label: function (context) {
- let label = context.dataset.label || '';
- if (label) {
- label += ': ';
- }
- if (context.parsed.y !== null) {
- label += context.parsed.y + ' tesserati';
- }
- return label;
- }
- }
- }
- }
- }
- });
- } catch (error) {
- console.error('Error updating tesserati chart:', error);
- document.getElementById('tesserati-chart').insertAdjacentHTML(
- 'afterend',
- '<div class="alert alert-danger">Errore nel caricamento dei dati tesserati</div>'
- );
- }
- }
- document.addEventListener('DOMContentLoaded', function () {
- const selectElement = document.querySelector('select[name="selectedCourse"]');
- if (selectElement) {
- selectElement.addEventListener('change', function () {
- const selectedValue = this.value;
- console.log('Selected course ID:', selectedValue);
- @this.set('selectedCourse', selectedValue);
- });
- }
- });
- function initializeChartSizes() {
- window.chartSizes = {
- 'monthly-in-out-chart': {
- height: document.getElementById('monthly-in-out-chart').style.height || 'auto',
- maxHeight: document.getElementById('monthly-in-out-chart').style.maxHeight || 'none'
- },
- 'causals-chart': {
- height: '300px',
- maxHeight: '300px'
- },
- 'tesserati-chart': {
- height: document.getElementById('tesserati-chart').style.height || 'auto',
- maxHeight: document.getElementById('tesserati-chart').style.maxHeight || 'none'
- },
- 'courses-chart': {
- height: '250px',
- maxHeight: '250px'
- }
- };
- }
- function restoreChartSize(chartId) {
- if (!window.chartSizes || !window.chartSizes[chartId]) return;
- const canvas = document.getElementById(chartId);
- if (canvas) {
- canvas.style.height = window.chartSizes[chartId].height;
- canvas.style.maxHeight = window.chartSizes[chartId].maxHeight;
- }
- }
- </script>
- @endpush
|