dynamic_report.blade.php 8.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223
  1. {{-- resources/views/livewire/dynamic_report.blade.php --}}
  2. <div id="card--dashboard">
  3. <div>
  4. <div class="chart-row">
  5. <div class="chart-card">
  6. <div class="chart-body" style="padding-block: 20px">
  7. <div class="row row-gap-3">
  8. <div class="col-4">
  9. <div wire:ignore>
  10. <label for="filter-courses">Corso</label>
  11. <select id="filter-courses" multiple class="form-select">
  12. @foreach ($filter_options['courses'] as $course)
  13. <option value="{{ $course }}">{{ $course }}</option>
  14. @endforeach
  15. </select>
  16. </div>
  17. </div>
  18. <div class="col-4">
  19. <div wire:ignore>
  20. <label for="filter-levels">Livello</label>
  21. <select id="filter-levels" multiple class="form-select">
  22. @foreach ($filter_options['levels'] as $level)
  23. <option value="{{ $level['id'] }}">{{ $level['name'] }}</option>
  24. @endforeach
  25. </select>
  26. </div>
  27. </div>
  28. <div class="col-4">
  29. <div wire:ignore>
  30. <label for="filter-types">Tipo</label>
  31. <select id="filter-types" multiple class="form-select">
  32. @foreach ($filter_options['types'] as $type)
  33. <option value="{{ $type['id'] }}">{{ $type['name'] }}</option>
  34. @endforeach
  35. </select>
  36. </div>
  37. </div>
  38. <div class="col-4">
  39. <div wire:ignore>
  40. <label for="filter-seasons">Stagione</label>
  41. <select id="filter-seasons" multiple class="form-select">
  42. @foreach ($filter_options['seasons'] as $season)
  43. <option value="{{ $season }}">{{ $season }}</option>
  44. @endforeach
  45. </select>
  46. </div>
  47. </div>
  48. <div class="col-4">
  49. <div wire:ignore>
  50. <label for="filter-months">Mesi</label>
  51. <select id="filter-months" multiple class="form-select">
  52. @foreach ($filter_options['months'] as $month)
  53. <option value="{{ $month['id'] }}">{{ $month['name'] }}</option>
  54. @endforeach
  55. </select>
  56. </div>
  57. </div>
  58. <div class="col text-end mt-4">
  59. <button type="button" class="btn--ui" wire:click="applyFilters">
  60. Applica filtri
  61. </button>
  62. </div>
  63. </div>
  64. </div>
  65. </div>
  66. </div>
  67. <div class="chart-row mb-0">
  68. <div class="chart-card">
  69. <div class="chart-body" wire:ignore>
  70. <div class="chart-wrapper" style="height: calc(100dvh - 450px)">
  71. <canvas id="dynamicReportChart"></canvas>
  72. </div>
  73. </div>
  74. </div>
  75. </div>
  76. </div>
  77. </div>
  78. @push('css')
  79. <link href="https://cdn.jsdelivr.net/npm/select2@4.1.0-rc.0/dist/css/select2.min.css" rel="stylesheet" />
  80. <link rel="stylesheet" href="/css/chart-reports.css">
  81. <style>
  82. .select2-container--default .select2-selection--multiple {
  83. min-height: 40px !important;
  84. }
  85. .select2-container--default .select2-search--inline .select2-search__field {
  86. margin-top: 0;
  87. }
  88. .select2-container--default .select2-search--inline {
  89. display: inline-block;
  90. padding-top: 0;
  91. height: 26px !important;
  92. }
  93. </style>
  94. @endpush
  95. @push('scripts')
  96. <script src="https://code.jquery.com/jquery-2.2.4.min.js" integrity="sha256-BbhdlvQf/xTY9gja0Dq3HiwQF8LaCRTXxZKRutelT44=" crossorigin="anonymous"></script>
  97. <script src="https://cdn.jsdelivr.net/npm/select2@4.1.0-rc.0/dist/js/select2.min.js"></script>
  98. <script src="https://cdn.jsdelivr.net/npm/chart.js"></script>
  99. @endpush
  100. @push('scripts')
  101. <script>
  102. document.addEventListener("livewire:load", function () {
  103. function initSelect2() {
  104. const select2Options = {
  105. language: {
  106. noResults: function () {
  107. return "Nessun risultato";
  108. },
  109. },
  110. };
  111. const $courses = $("#filter-courses").select2(select2Options);
  112. $courses.off('change.dynamicReport').on('change.dynamicReport', function () {
  113. @this.set("filters.courses", $(this).val() ?? []);
  114. });
  115. const $levels = $("#filter-levels").select2(select2Options);
  116. $levels.off('change.dynamicReport').on('change.dynamicReport', function () {
  117. @this.set("filters.levels", $(this).val() ?? []);
  118. });
  119. const $types = $("#filter-types").select2(select2Options);
  120. $types.off('change.dynamicReport').on('change.dynamicReport', function () {
  121. @this.set("filters.types", $(this).val() ?? []);
  122. });
  123. const $seasons = $("#filter-seasons").select2(select2Options);
  124. $seasons.off('change.dynamicReport').on('change.dynamicReport', function () {
  125. @this.set("filters.seasons", $(this).val() ?? []);
  126. });
  127. const $months = $("#filter-months").select2(select2Options);
  128. $months.off('change.dynamicReport').on('change.dynamicReport', function () {
  129. @this.set("filters.months", $(this).val() ?? []);
  130. });
  131. }
  132. initSelect2();
  133. });
  134. </script>
  135. @endpush
  136. @push('scripts')
  137. <script>
  138. let dynamicReportChart = null;
  139. function buildOrUpdateChart(payload) {
  140. const ctx = document.getElementById('dynamicReportChart');
  141. if (!ctx) return;
  142. let data = payload.chart ?? { labels: [], datasets: [] };
  143. data = applyDatasetStyles(data);
  144. if (dynamicReportChart) {
  145. dynamicReportChart.data.labels = data.labels;
  146. dynamicReportChart.data.datasets = data.datasets;
  147. dynamicReportChart.update();
  148. return;
  149. }
  150. dynamicReportChart = new Chart(ctx, {
  151. type: 'bar',
  152. data: data,
  153. options: {
  154. responsive: true,
  155. maintainAspectRatio: false,
  156. interaction: {
  157. mode: 'index',
  158. intersect: false,
  159. },
  160. plugins: {
  161. tooltip: {
  162. mode: 'index',
  163. intersect: false,
  164. itemSort: (a, b) => b.raw - a.raw,
  165. },
  166. legend: { display: true },
  167. },
  168. scales: {
  169. y: { beginAtZero: true }
  170. }
  171. }
  172. });
  173. }
  174. function applyDatasetStyles(data) {
  175. if (!data?.datasets) return data;
  176. data.datasets = data.datasets.map((ds, i) => {
  177. const hue = (i * 47) % 360;
  178. return {
  179. ...ds,
  180. barThickness: 10,
  181. maxBarThickness: 10,
  182. backgroundColor: `hsla(${hue}, 70%, 55%, 0.85)`,
  183. borderColor: `hsl(${hue}, 70%, 45%)`,
  184. borderWidth: 0,
  185. };
  186. });
  187. return data;
  188. }
  189. window.addEventListener('dynamic-report:updated', (e) => {
  190. buildOrUpdateChart(e.detail);
  191. });
  192. </script>
  193. @endpush