| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913 |
- {{-- resources/views/livewire/reports.blade.php --}}
- <div>
- <script src="https://cdn.jsdelivr.net/npm/chart.js"></script>
- <style>
- :root {
- --primary-color: #3b5bdb;
- --secondary-color: #495057;
- --success-color: #00b894;
- --info-color: #22b8cf;
- --warning-color: #ffd43b;
- --danger-color: #ff6b6b;
- --dark-color: #212529;
- --border-color: #e9ecef;
- --shadow-sm: 0 0.125rem 0.25rem rgba(0, 0, 0, 0.075);
- --shadow: 0 0.5rem 1rem rgba(0, 0, 0, 0.15);
- }
- .dashboard-container {
- padding: 1rem;
- max-width: 1400px;
- margin: 0 auto;
- }
- .dashboard-header {
- text-align: center;
- margin-bottom: 2rem;
- padding: 2rem;
- background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
- border-radius: 16px;
- color: white;
- }
- .dashboard-header h1 {
- font-size: 2.5rem;
- font-weight: 700;
- margin-bottom: 0.5rem;
- }
- .dashboard-header p {
- font-size: 1.1rem;
- opacity: 0.9;
- }
- .controls-section {
- background: white;
- border-radius: 12px;
- padding: 1.5rem;
- box-shadow: var(--shadow-sm);
- border: 1px solid var(--border-color);
- margin-bottom: 2rem;
- display: flex;
- gap: 2rem;
- align-items: center;
- flex-wrap: wrap;
- }
- .control-group {
- display: flex;
- flex-direction: column;
- gap: 0.5rem;
- }
- .control-group label {
- font-weight: 600;
- font-size: 0.875rem;
- color: var(--secondary-color);
- }
- .form-select {
- border-radius: 8px;
- border: 2px solid var(--border-color);
- padding: 0.75rem 1rem;
- font-size: 1rem;
- transition: all 0.3s ease;
- min-width: 200px;
- }
- .form-select:focus {
- border-color: var(--primary-color);
- box-shadow: 0 0 0 0.2rem rgba(59, 91, 219, 0.25);
- outline: none;
- }
- .summary-cards {
- display: grid;
- grid-template-columns: repeat(auto-fit, minmax(250px, 1fr));
- gap: 1rem;
- margin-bottom: 2rem;
- }
- .summary-card {
- background: white;
- border-radius: 16px;
- padding: 1.5rem;
- box-shadow: var(--shadow-sm);
- border: 1px solid var(--border-color);
- text-align: center;
- transition: transform 0.3s ease;
- }
- .summary-card:hover {
- transform: translateY(-2px);
- }
- .summary-card h3 {
- font-size: 0.875rem;
- font-weight: 600;
- color: var(--secondary-color);
- text-transform: uppercase;
- letter-spacing: 0.5px;
- margin-bottom: 0.5rem;
- }
- .summary-card .value {
- font-size: 2rem;
- font-weight: 700;
- margin-bottom: 0.25rem;
- }
- .summary-card.income .value { color: var(--success-color); }
- .summary-card.expense .value { color: var(--danger-color); }
- .summary-card.delta .value { color: var(--primary-color); }
- .summary-card.delta.negative .value { color: var(--danger-color); }
- .chart-row {
- width: 100%;
- margin-bottom: 2rem;
- }
- .chart-card {
- background: white;
- border-radius: 16px;
- box-shadow: var(--shadow-sm);
- border: 1px solid var(--border-color);
- overflow: hidden;
- transition: all 0.3s ease;
- }
- .chart-card:hover {
- box-shadow: var(--shadow);
- }
- .chart-header {
- padding: 1.5rem 2rem 1rem;
- border-bottom: 1px solid var(--border-color);
- background: linear-gradient(135deg, #f8f9fa 0%, #ffffff 100%);
- }
- .chart-title {
- font-size: 1.25rem;
- font-weight: 600;
- color: var(--dark-color);
- margin: 0;
- }
- .chart-body {
- padding: 2rem;
- }
- .chart-container {
- position: relative;
- height: 400px !important;
- width: 100%;
- }
- .chart-container canvas {
- max-height: 400px !important;
- height: 400px !important;
- }
- .course-controls {
- background: #f8f9fa;
- border-radius: 12px;
- padding: 1.5rem;
- margin-bottom: 1.5rem;
- }
- .legend-container {
- display: flex;
- gap: 2rem;
- margin-top: 1rem;
- flex-wrap: wrap;
- }
- .legend-item {
- display: flex;
- align-items: center;
- gap: 0.5rem;
- font-size: 0.875rem;
- }
- .legend-color {
- width: 12px;
- height: 12px;
- border-radius: 50%;
- }
- .monthly-table-container {
- background: #f8f9fa;
- border-radius: 12px;
- padding: 1.5rem;
- box-shadow: var(--shadow-sm);
- }
- .members-table-container {
- background: #f8f9fa;
- border-radius: 12px;
- padding: 1.5rem;
- box-shadow: var(--shadow-sm);
- }
- .monthly-table, .members-table {
- font-size: 0.875rem;
- }
- .members-table .table-header {
- display: grid;
- grid-template-columns: 1.2fr 0.8fr 1fr;
- gap: 0.5rem;
- margin-bottom: 0.5rem;
- padding-bottom: 0.5rem;
- border-bottom: 2px solid var(--border-color);
- }
- .members-table .table-row {
- display: grid;
- grid-template-columns: 1.2fr 0.8fr 1fr;
- gap: 0.5rem;
- padding: 0.5rem 0;
- border-bottom: 1px solid #e9ecef;
- transition: background-color 0.2s ease;
- }
- .table-cell.season-name {
- text-align: left;
- font-weight: 600;
- font-size: 0.8rem;
- }
- .table-cell.members-count {
- text-align: center;
- font-weight: 600;
- color: var(--primary-color);
- }
- .table-cell.variation {
- text-align: right;
- font-size: 0.8rem;
- }
- .variation-positive {
- color: var(--success-color);
- font-weight: 600;
- }
- .variation-negative {
- color: var(--danger-color);
- font-weight: 600;
- }
- .variation-neutral {
- color: var(--secondary-color);
- font-weight: 500;
- }
- .monthly-table {
- font-size: 0.875rem;
- }
- .table-header {
- display: grid;
- grid-template-columns: 1fr 1fr 1fr 1fr;
- gap: 0.5rem;
- margin-bottom: 0.5rem;
- padding-bottom: 0.5rem;
- border-bottom: 2px solid var(--border-color);
- }
- .table-row {
- display: grid;
- grid-template-columns: 1fr 1fr 1fr 1fr;
- gap: 0.5rem;
- padding: 0.5rem 0;
- border-bottom: 1px solid #e9ecef;
- transition: background-color 0.2s ease;
- }
- .table-row:hover {
- background-color: rgba(0, 0, 0, 0.02);
- }
- .table-row.positive {
- background-color: rgba(0, 184, 148, 0.05);
- }
- .table-row.negative {
- background-color: rgba(255, 107, 107, 0.05);
- }
- .table-row.neutral {
- background-color: rgba(73, 80, 87, 0.02);
- }
- .table-cell {
- padding: 0.25rem 0.5rem;
- text-align: right;
- }
- .table-header .table-cell {
- font-weight: 600;
- color: var(--secondary-color);
- text-align: center;
- text-transform: uppercase;
- font-size: 0.75rem;
- letter-spacing: 0.5px;
- }
- .table-cell.month-name {
- text-align: left;
- font-weight: 600;
- }
- .table-cell.income {
- color: var(--success-color);
- font-weight: 500;
- }
- .table-cell.expense {
- color: var(--danger-color);
- font-weight: 500;
- }
- .table-cell.net {
- font-weight: 600;
- }
- .table-row.positive .table-cell.net {
- color: var(--success-color);
- }
- .table-row.negative .table-cell.net {
- color: var(--danger-color);
- }
- .table-row.neutral .table-cell.net {
- color: var(--secondary-color);
- }
- @media (max-width: 1024px) {
- .chart-body > div[style*="grid-template-columns"] {
- grid-template-columns: 1fr !important;
- gap: 1rem !important;
- }
- .monthly-table-container {
- order: 2;
- }
- }
- .loading {
- text-align: center;
- padding: 2rem;
- color: var(--secondary-color);
- }
- .error-message {
- background: #fff5f5;
- border: 1px solid #fed7d7;
- color: #c53030;
- padding: 1rem;
- border-radius: 8px;
- margin: 1rem 0;
- }
- @media (max-width: 768px) {
- .dashboard-container {
- padding: 0.5rem;
- }
- .dashboard-header h1 {
- font-size: 2rem;
- }
- .controls-section {
- flex-direction: column;
- align-items: stretch;
- gap: 1rem;
- }
- .chart-body {
- padding: 1rem;
- }
- .chart-container {
- height: 300px !important;
- }
- .chart-container canvas {
- max-height: 300px !important;
- height: 300px !important;
- }
- .legend-container {
- gap: 1rem;
- }
- }
- </style>
- <div class="dashboard-container">
- <div class="controls-section">
- <div class="control-group">
- <label for="season-filter">Stagione di Riferimento:</label>
- <select class="form-select" wire:model="seasonFilter" wire:change="updateCharts">
- @foreach($this->getAvailableSeasons() as $season)
- <option value="{{ $season }}">{{ $season }}</option>
- @endforeach
- </select>
- </div>
- </div>
- @php
- $summary = $this->getYearlySummary();
- @endphp
- <div class="summary-cards">
- <div class="summary-card income">
- <h3>Entrate Totali</h3>
- <div class="value">€{{ number_format($summary['totalIncome'], 2, ',', '.') }}</div>
- </div>
- <div class="summary-card expense">
- <h3>Uscite Totali</h3>
- <div class="value">€{{ number_format($summary['totalExpenses'], 2, ',', '.') }}</div>
- </div>
- <div class="summary-card delta {{ $summary['delta'] < 0 ? 'negative' : '' }}">
- <h3>Bilancio Netto</h3>
- <div class="value">€{{ number_format($summary['delta'], 2, ',', '.') }}</div>
- </div>
- </div>
- <!-- Charts Section - Force complete re-render with wire:key -->
- <div wire:key="season-{{ $seasonFilter }}-course-{{ $selectedCourse ?? 'none' }}">
- <div class="chart-row">
- <div class="chart-card">
- <div class="chart-header">
- <h3 class="chart-title">Entrate e Uscite Mensili - {{ $seasonFilter }}</h3>
- </div>
- <div class="chart-body">
- <div style="display: grid; grid-template-columns: 1fr 300px; gap: 2rem; align-items: start;">
- <div class="chart-container">
- <canvas id="monthly-chart-{{ str_replace('-', '', $seasonFilter) }}"></canvas>
- </div>
- @php
- $monthlyTotals = $this->getMonthlyTotals();
- $incomeData = $monthlyTotals['datasets'][0]['data'];
- $expenseData = $monthlyTotals['datasets'][1]['data'];
- $monthNames = $monthlyTotals['labels'];
- @endphp
- <div class="monthly-table-container">
- <h4 style="margin-bottom: 1rem; font-size: 1rem; font-weight: 600; color: var(--dark-color);">Riepilogo Mensile</h4>
- <div class="monthly-table">
- <div class="table-header">
- <div class="table-cell">Mese</div>
- <div class="table-cell">Entrate</div>
- <div class="table-cell">Uscite</div>
- <div class="table-cell">Netto</div>
- </div>
- @foreach($monthNames as $index => $month)
- @php
- $income = floatval($incomeData[$index] ?? 0);
- $expense = floatval($expenseData[$index] ?? 0);
- $net = $income - $expense;
- @endphp
- <div class="table-row {{ $net < 0 ? 'negative' : ($net > 0 ? 'positive' : 'neutral') }}">
- <div class="table-cell month-name">{{ $month }}</div>
- <div class="table-cell income">€{{ number_format($income, 0, ',', '.') }}</div>
- <div class="table-cell expense">€{{ number_format($expense, 0, ',', '.') }}</div>
- <div class="table-cell net">€{{ number_format($net, 0, ',', '.') }}</div>
- </div>
- @endforeach
- </div>
- </div>
- </div>
- </div>
- </div>
- </div>
- <div class="chart-row">
- <div class="chart-card">
- <div class="chart-header">
- <h3 class="chart-title">Causali Performanti - {{ $seasonFilter }}</h3>
- </div>
- <div class="chart-body">
- <div class="chart-container">
- <canvas id="causals-chart-{{ str_replace('-', '', $seasonFilter) }}"></canvas>
- </div>
- </div>
- </div>
- </div>
- <div class="chart-row">
- <div class="chart-card">
- <div class="chart-header">
- <h3 class="chart-title">Tesserati per Stagione</h3>
- </div>
- <div class="chart-body">
- <div style="display: grid; grid-template-columns: 1fr 300px; gap: 2rem; align-items: start;">
- <div class="chart-container">
- <canvas id="members-chart-{{ str_replace('-', '', $seasonFilter) }}"></canvas>
- </div>
- @php
- $membersData = $this->getTesseratiData();
- $seasonLabels = $membersData['labels'];
- $memberCounts = $membersData['datasets'][0]['data'];
- @endphp
- <div class="members-table-container">
- <h4 style="margin-bottom: 1rem; font-size: 1rem; font-weight: 600; color: var(--dark-color);">Riepilogo Tesserati</h4>
- <div class="members-table">
- <div class="table-header">
- <div class="table-cell">Stagione</div>
- <div class="table-cell">Tesserati</div>
- <div class="table-cell">Variazione</div>
- </div>
- @foreach($seasonLabels as $index => $season)
- @php
- $current = intval($memberCounts[$index] ?? 0);
- $previous = $index > 0 ? intval($memberCounts[$index - 1] ?? 0) : 0;
- $variation = $index > 0 ? $current - $previous : 0;
- $variationPercent = $previous > 0 ? round(($variation / $previous) * 100, 1) : 0;
- @endphp
- <div class="table-row {{ $variation > 0 ? 'positive' : ($variation < 0 ? 'negative' : 'neutral') }}">
- <div class="table-cell season-name">{{ $season }}</div>
- <div class="table-cell members-count">{{ number_format($current, 0, ',', '.') }}</div>
- <div class="table-cell variation">
- @if($index > 0)
- @if($variation > 0)
- <span class="variation-positive">+{{ $variation }} (+{{ $variationPercent }}%)</span>
- @elseif($variation < 0)
- <span class="variation-negative">{{ $variation }} ({{ $variationPercent }}%)</span>
- @else
- <span class="variation-neutral">{{ $variation }}</span>
- @endif
- @else
- <span class="variation-neutral">—</span>
- @endif
- </div>
- </div>
- @endforeach
- </div>
- </div>
- </div>
- </div>
- </div>
- </div>
- <div class="chart-row">
- <div class="chart-card">
- <div class="chart-header">
- <h3 class="chart-title">Analisi Corsi</h3>
- </div>
- <div class="chart-body">
- <div class="course-controls">
- <div class="control-group">
- <label>Seleziona Corso:</label>
- <select class="form-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="legend-container">
- <div class="legend-item">
- <div class="legend-color" style="background: rgba(0, 184, 148, 1);"></div>
- <span>Pagamenti Effettuati</span>
- </div>
- <div class="legend-item">
- <div class="legend-color" style="background: rgba(48, 51, 107, 1);"></div>
- <span>Pagamenti Attesi</span>
- </div>
- </div>
- </div>
- <div class="chart-container">
- <canvas id="courses-chart-{{ str_replace('-', '', $seasonFilter) }}-{{ $selectedCourse ?? 'none' }}"></canvas>
- </div>
- </div>
- </div>
- </div>
- <script>
- (function() {
- const seasonFilter = '{{ $seasonFilter }}';
- const selectedCourse = '{{ $selectedCourse ?? 'none' }}';
- const seasonKey = '{{ str_replace('-', '', $seasonFilter) }}';
- console.log('Creating charts for season:', seasonFilter);
- // Get the data fresh from PHP
- const monthlyData = @json($this->getMonthlyTotals());
- const causalsData = @json($this->getTopCausalsByAmount());
- const membersData = @json($this->getTesseratiData());
- const courseData = @json($this->getCourseMonthlyEarnings());
- console.log('Monthly data for', seasonFilter, ':', monthlyData);
- console.log('May value should be:', monthlyData.datasets[0].data[8]);
- // Wait for DOM to be ready
- if (document.readyState === 'loading') {
- document.addEventListener('DOMContentLoaded', initCharts);
- } else {
- setTimeout(initCharts, 100);
- }
- function initCharts() {
- createMonthlyChart();
- createCausalsChart();
- createMembersChart();
- createCoursesChart();
- }
- function createMonthlyChart() {
- const canvasId = `monthly-chart-${seasonKey}`;
- const canvas = document.getElementById(canvasId);
- if (!canvas) {
- console.log('Canvas not found:', canvasId);
- return;
- }
- const ctx = canvas.getContext('2d');
- const incomeGradient = ctx.createLinearGradient(0, 0, 0, 400);
- incomeGradient.addColorStop(0, 'rgba(0, 184, 148, 0.8)');
- incomeGradient.addColorStop(1, 'rgba(0, 184, 148, 0.2)');
- const expenseGradient = ctx.createLinearGradient(0, 0, 0, 400);
- expenseGradient.addColorStop(0, 'rgba(255, 107, 107, 0.8)');
- expenseGradient.addColorStop(1, 'rgba(255, 107, 107, 0.2)');
- new Chart(ctx, {
- type: 'bar',
- data: {
- labels: monthlyData.labels,
- datasets: [
- {
- label: 'Entrate',
- data: monthlyData.datasets[0].data,
- backgroundColor: incomeGradient,
- borderColor: '#00b894',
- borderWidth: 2,
- },
- {
- label: 'Uscite',
- data: monthlyData.datasets[1].data,
- backgroundColor: expenseGradient,
- borderColor: '#ff6b6b',
- borderWidth: 2,
- }
- ]
- },
- options: {
- responsive: true,
- maintainAspectRatio: false,
- plugins: {
- legend: {
- position: 'top',
- labels: {
- usePointStyle: true,
- padding: 20,
- font: { weight: '500' }
- }
- },
- tooltip: {
- backgroundColor: 'rgba(255, 255, 255, 0.95)',
- titleColor: '#212529',
- bodyColor: '#495057',
- borderColor: '#e9ecef',
- borderWidth: 1,
- cornerRadius: 8,
- callbacks: {
- label: function(context) {
- return context.dataset.label + ': €' +
- new Intl.NumberFormat('it-IT').format(context.parsed.y);
- }
- }
- }
- },
- scales: {
- x: {
- grid: { display: false },
- ticks: { font: { weight: '500' } }
- },
- y: {
- beginAtZero: true,
- grid: { color: 'rgba(0, 0, 0, 0.05)' },
- ticks: {
- callback: function(value) {
- return '€' + new Intl.NumberFormat('it-IT').format(value);
- }
- }
- }
- },
- animation: {
- duration: 1000,
- easing: 'easeOutQuart'
- }
- }
- });
- console.log('Monthly chart created for', seasonFilter);
- }
- function createCausalsChart() {
- const canvasId = `causals-chart-${seasonKey}`;
- const canvas = document.getElementById(canvasId);
- if (!canvas) return;
- const ctx = canvas.getContext('2d');
- const colors = [
- 'rgba(59, 91, 219, 0.8)',
- 'rgba(0, 184, 148, 0.8)',
- 'rgba(34, 184, 207, 0.8)',
- 'rgba(255, 212, 59, 0.8)',
- 'rgba(255, 107, 107, 0.8)',
- 'rgba(142, 68, 173, 0.8)',
- 'rgba(230, 126, 34, 0.8)',
- 'rgba(149, 165, 166, 0.8)',
- 'rgba(241, 196, 15, 0.8)',
- 'rgba(231, 76, 60, 0.8)'
- ];
- new Chart(ctx, {
- type: 'doughnut',
- data: {
- labels: causalsData.inLabels,
- datasets: [{
- label: 'Importo',
- data: causalsData.inData.map(item => item.value),
- backgroundColor: colors,
- borderColor: colors.map(color => color.replace('0.8', '1')),
- borderWidth: 2,
- hoverOffset: 8
- }]
- },
- options: {
- responsive: true,
- maintainAspectRatio: false,
- cutout: '60%',
- plugins: {
- legend: {
- position: 'left',
- labels: {
- usePointStyle: true,
- padding: 15,
- font: { size: 11, weight: '500' }
- }
- },
- tooltip: {
- backgroundColor: 'rgba(255, 255, 255, 0.95)',
- titleColor: '#212529',
- bodyColor: '#495057',
- borderColor: '#e9ecef',
- borderWidth: 1,
- cornerRadius: 8,
- callbacks: {
- label: function(context) {
- const value = context.raw;
- const total = context.dataset.data.reduce((a, b) => a + b, 0);
- const percentage = Math.round((value / total) * 100);
- return context.label + ': €' +
- new Intl.NumberFormat('it-IT').format(value) +
- ` (${percentage}%)`;
- }
- }
- }
- },
- animation: {
- animateRotate: true,
- duration: 1000
- }
- }
- });
- }
- function createMembersChart() {
- const canvasId = `members-chart-${seasonKey}`;
- const canvas = document.getElementById(canvasId);
- if (!canvas) return;
- const ctx = canvas.getContext('2d');
- const gradient = ctx.createLinearGradient(0, 0, 0, 400);
- gradient.addColorStop(0, 'rgba(59, 91, 219, 0.3)');
- gradient.addColorStop(1, 'rgba(59, 91, 219, 0.05)');
- new Chart(ctx, {
- type: 'line',
- data: {
- labels: membersData.labels,
- datasets: [{
- label: 'Membri Tesserati',
- data: membersData.datasets[0].data,
- borderColor: '#3b5bdb',
- backgroundColor: gradient,
- borderWidth: 3,
- fill: true,
- tension: 0.4,
- pointBackgroundColor: '#3b5bdb',
- pointBorderColor: '#ffffff',
- pointBorderWidth: 2,
- pointRadius: 6,
- pointHoverRadius: 8
- }]
- },
- options: {
- responsive: true,
- maintainAspectRatio: false,
- plugins: {
- legend: { display: false },
- tooltip: {
- backgroundColor: 'rgba(255, 255, 255, 0.95)',
- titleColor: '#212529',
- bodyColor: '#495057',
- borderColor: '#e9ecef',
- borderWidth: 1,
- cornerRadius: 8,
- callbacks: {
- label: function(context) {
- return 'Tesserati: ' + context.parsed.y;
- }
- }
- }
- },
- scales: {
- x: { grid: { display: false } },
- y: {
- beginAtZero: true,
- grid: { color: 'rgba(0, 0, 0, 0.05)' },
- ticks: { precision: 0 }
- }
- },
- animation: {
- duration: 1000,
- easing: 'easeOutQuart'
- }
- }
- });
- }
- function createCoursesChart() {
- const canvasId = `courses-chart-${seasonKey}-${selectedCourse}`;
- const canvas = document.getElementById(canvasId);
- if (!canvas) return;
- const ctx = canvas.getContext('2d');
- new Chart(ctx, {
- type: 'bar',
- data: {
- labels: courseData.labels,
- datasets: courseData.datasets
- },
- options: {
- responsive: true,
- maintainAspectRatio: false,
- scales: {
- x: { grid: { display: false } },
- y: {
- beginAtZero: true,
- grid: {
- color: 'rgba(0, 0, 0, 0.1)',
- borderDash: [5, 5]
- },
- ticks: {
- callback: function(value) {
- return '€' + new Intl.NumberFormat('it-IT').format(value);
- }
- }
- }
- },
- plugins: {
- legend: { display: false },
- tooltip: {
- backgroundColor: 'rgba(255, 255, 255, 0.95)',
- titleColor: '#212529',
- bodyColor: '#495057',
- borderColor: '#e9ecef',
- borderWidth: 1,
- cornerRadius: 8,
- callbacks: {
- label: function(context) {
- return context.dataset.label + ': €' +
- new Intl.NumberFormat('it-IT').format(context.parsed.y);
- }
- }
- }
- },
- animation: {
- duration: 1000,
- easing: 'easeOutQuart'
- }
- }
- });
- }
- })();
- </script>
- </div>
- </div>
- </div>
|