dynamic_report.blade.php 9.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257
  1. {{-- resources/views/livewire/dynamic_report.blade.php --}}
  2. <div id="card--dashboard">
  3. <div>
  4. <div class="chart-row mb-3">
  5. <div class="chart-card">
  6. <div class="chart-body" style="padding: 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" onclick="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" style="padding: 20px" wire:ignore>
  70. <div class="chart-wrapper" style="height: calc(100dvh - 450px)">
  71. <div class="missing-filter-warning" id="filter_msg" wire:ignore>
  72. <div class="chart-placeholder" style="margin-top: 0">
  73. <div style="text-align: center;">
  74. <div style="font-size: 3rem; margin-bottom: 1rem; opacity: 0.3;">📊</div>
  75. <p style="font-size: 1.25rem; font-weight: 600; margin: 0;">Seleziona uno o più criteri per visualizzare il report</p>
  76. </div>
  77. </div>
  78. </div>
  79. <canvas id="dynamicReportChart" style="height: 0"></canvas>
  80. </div>
  81. </div>
  82. </div>
  83. </div>
  84. </div>
  85. </div>
  86. @push('css')
  87. <link href="https://cdn.jsdelivr.net/npm/select2@4.1.0-rc.0/dist/css/select2.min.css" rel="stylesheet" />
  88. <link rel="stylesheet" href="/css/chart-reports.css">
  89. <style>
  90. .select2-container--default .select2-selection--multiple {
  91. min-height: 40px !important;
  92. }
  93. .select2-container--default .select2-search--inline .select2-search__field {
  94. margin-top: 0;
  95. }
  96. .select2-container--default .select2-search--inline {
  97. display: inline-block;
  98. padding-top: 0;
  99. height: 26px !important;
  100. }
  101. </style>
  102. @endpush
  103. @push('scripts')
  104. <script src="https://code.jquery.com/jquery-2.2.4.min.js" integrity="sha256-BbhdlvQf/xTY9gja0Dq3HiwQF8LaCRTXxZKRutelT44=" crossorigin="anonymous"></script>
  105. <script src="https://cdn.jsdelivr.net/npm/select2@4.1.0-rc.0/dist/js/select2.min.js"></script>
  106. <script src="https://cdn.jsdelivr.net/npm/chart.js"></script>
  107. @endpush
  108. @push('scripts')
  109. <script>
  110. document.addEventListener("livewire:load", function () {
  111. function initSelect2() {
  112. const select2Options = {
  113. language: {
  114. noResults: function () {
  115. return "Nessun risultato";
  116. },
  117. },
  118. };
  119. const $courses = $("#filter-courses").select2(select2Options);
  120. $courses.off('change.dynamicReport').on('change.dynamicReport', function () {
  121. @this.set("filters.courses", $(this).val() ?? []);
  122. });
  123. const $levels = $("#filter-levels").select2(select2Options);
  124. $levels.off('change.dynamicReport').on('change.dynamicReport', function () {
  125. @this.set("filters.levels", $(this).val() ?? []);
  126. });
  127. /* const $types = $("#filter-types").select2(select2Options);
  128. $types.off('change.dynamicReport').on('change.dynamicReport', function () {
  129. @this.set("filters.types", $(this).val() ?? []);
  130. }); */
  131. const $seasons = $("#filter-seasons").select2(select2Options);
  132. $seasons.off('change.dynamicReport').on('change.dynamicReport', function () {
  133. @this.set("filters.seasons", $(this).val() ?? []);
  134. });
  135. const $months = $("#filter-months").select2(select2Options);
  136. $months.off('change.dynamicReport').on('change.dynamicReport', function () {
  137. @this.set("filters.months", $(this).val() ?? []);
  138. });
  139. }
  140. initSelect2();
  141. });
  142. </script>
  143. @endpush
  144. @push('scripts')
  145. <script>
  146. let dynamicReportChart = null;
  147. function buildOrUpdateChart(payload) {
  148. const ctx = document.getElementById('dynamicReportChart');
  149. if (!ctx) return;
  150. let data = payload.chart ?? { labels: [], datasets: [] };
  151. data = applyDatasetStyles(data);
  152. if (dynamicReportChart) {
  153. dynamicReportChart.data.labels = data.labels;
  154. dynamicReportChart.data.datasets = data.datasets;
  155. dynamicReportChart.update();
  156. return;
  157. }
  158. dynamicReportChart = new Chart(ctx, {
  159. type: 'bar',
  160. data: data,
  161. options: {
  162. responsive: true,
  163. maintainAspectRatio: false,
  164. interaction: {
  165. mode: 'index',
  166. intersect: false,
  167. },
  168. plugins: {
  169. tooltip: {
  170. mode: 'index',
  171. intersect: false,
  172. itemSort: (a, b) => b.raw - a.raw,
  173. },
  174. legend: { display: true, position: 'bottom' },
  175. },
  176. scales: {
  177. y: { beginAtZero: true }
  178. }
  179. }
  180. });
  181. }
  182. function applyDatasetStyles(data) {
  183. if (!data?.datasets) return data;
  184. data.datasets = data.datasets.map((ds, i) => {
  185. const hue = (i * 47) % 360;
  186. return {
  187. ...ds,
  188. barPercentage: 0.65,
  189. categoryPercentage: 0.8,
  190. barThickness: 'flex',
  191. maxBarThickness: 12,
  192. minBarLength: 0,
  193. backgroundColor: `hsla(${hue}, 70%, 55%, 0.85)`,
  194. borderColor: `hsl(${hue}, 70%, 45%)`,
  195. borderRadius: {
  196. topLeft: 8,
  197. topRight: 8,
  198. bottomLeft: 0,
  199. bottomRight: 0,
  200. },
  201. options: {
  202. plugins: {
  203. tooltip: {
  204. backgroundColor: 'rgba(255, 255, 255, 1)',
  205. titleColor: '#212529',
  206. bodyColor: '#495057',
  207. borderColor: '#e9ecef',
  208. borderWidth: 2,
  209. cornerRadius: 0,
  210. },
  211. }
  212. }
  213. };
  214. });
  215. return data;
  216. }
  217. function applyFilters() {
  218. let filter_msg = document.getElementById("filter_msg");
  219. if (filter_msg) filter_msg.style.display = "none";
  220. @this.applyFilters();
  221. }
  222. window.addEventListener('dynamic-report:updated', (e) => {
  223. buildOrUpdateChart(e.detail);
  224. });
  225. </script>
  226. @endpush