diff --git a/app/assets/stylesheets/stats.scss b/app/assets/stylesheets/stats.scss index 01721ec38..9869fdf15 100644 --- a/app/assets/stylesheets/stats.scss +++ b/app/assets/stylesheets/stats.scss @@ -70,51 +70,8 @@ $stat-card-half-horizontal-spacing: 4 * $default-space; font-style: italic; } -$segmented-control-margin-top: $default-space; - -.segmented-control { - border-radius: 36px; - height: 36px; - line-height: 36px; - font-size: 0; - padding: 0; - display: inline-block; - margin-top: $segmented-control-margin-top; -} - $segmented-control-item-horizontal-padding: $default-space; -$segmented-control-item-border-radius: 2 * $default-space; - -.segmented-control-item { - display: inline-block; - font-size: 15px; - border: 2px solid $blue-france-700; - margin-right: -2px; - padding-top: var(--li-bottom); - padding-left: $segmented-control-item-horizontal-padding; - padding-right: $segmented-control-item-horizontal-padding; - color: $blue-france-700; - - &:first-of-type { - border-radius: $segmented-control-item-border-radius 0px 0px $segmented-control-item-border-radius; - } - - &:last-of-type { - border-radius: 0px $segmented-control-item-border-radius $segmented-control-item-border-radius 0px; - margin-right: 0; - } - - &:hover { - background-color: $blue-france-500; - color: #FFFFFF; - cursor: pointer; - } -} - -.segmented-control-item-active { - background-color: $blue-france-700; - color: #FFFFFF; -} +$segmented-control-item-border-radius: 0.25rem; .chart-container { margin-top: 36px; @@ -147,10 +104,10 @@ $big-number-card-padding: 2 * $segmented-control-item-border-radius; .big-number-card-number { display: block; text-align: center; - font-size: 80px; - line-height: 1em; + font-size: 4.5rem; + line-height: 1.5em; font-weight: bold; - color: $blue-france-500; + color: var(--text-title-blue-france); white-space: nowrap; } diff --git a/app/javascript/controllers/lazy/chartkick_controller.ts b/app/javascript/controllers/lazy/chartkick_controller.ts index 90758e10f..a5e84394c 100644 --- a/app/javascript/controllers/lazy/chartkick_controller.ts +++ b/app/javascript/controllers/lazy/chartkick_controller.ts @@ -1,44 +1,43 @@ import { Controller } from '@hotwired/stimulus'; -import { toggle, delegate } from '@utils'; -import Highcharts from 'highcharts'; import Chartkick from 'chartkick'; - -export class ChartkickController extends Controller { - async connect() { - delegate('click', '[data-toggle-chart]', (event) => - toggleChart(event as MouseEvent) - ); - } -} +import Highcharts from 'highcharts'; +import invariant from 'tiny-invariant'; Chartkick.use(Highcharts); -function reflow(nextChartId?: string) { - nextChartId && Chartkick.charts[nextChartId]?.getChartObject()?.reflow(); -} - -function toggleChart(event: MouseEvent) { - const nextSelectorItem = event.target as HTMLButtonElement, - chartClass = nextSelectorItem.dataset.toggleChart, - nextChart = chartClass - ? document.querySelector(chartClass) - : undefined, - nextChartId = nextChart?.children[0]?.id, - currentSelectorItem = nextSelectorItem.parentElement?.querySelector( - '.segmented-control-item-active' - ), - currentChart = - nextSelectorItem.parentElement?.parentElement?.querySelector( - '.chart:not(.hidden)' - ); - - // Change the current selector and the next selector states - currentSelectorItem?.classList.toggle('segmented-control-item-active'); - nextSelectorItem.classList.toggle('segmented-control-item-active'); - - // Hide the currently shown chart and show the new one - currentChart && toggle(currentChart); - nextChart && toggle(nextChart); - - // Reflow needed, see https://github.com/highcharts/highcharts/issues/1979 - reflow(nextChartId); + +export default class ChartkickController extends Controller { + static targets = ['chart']; + + declare readonly chartTargets: HTMLElement[]; + + toggleChart(event: Event) { + const target = event.currentTarget as HTMLInputElement; + const chartClass = target.dataset.toggleChart; + + invariant(chartClass, 'Missing data-toggle-chart attribute'); + + const nextChart = document.querySelector(chartClass); + const currentChart = this.chartTargets.find( + (chart) => !chart.classList.contains('hidden') + ); + + if (currentChart) { + currentChart.classList.add('hidden'); + } + + if (nextChart) { + nextChart.classList.remove('hidden'); + const nextChartId = nextChart.children[0]?.id; + this.reflow(nextChartId); + } + } + + reflow(chartId: string) { + if (chartId) { + const chart = Chartkick.charts[chartId]; + if (chart) { + chart.getChartObject()?.reflow(); + } + } + } } diff --git a/app/views/stats/index.html.haml b/app/views/stats/index.html.haml index d1712f63a..af8fc0bc1 100644 --- a/app/views/stats/index.html.haml +++ b/app/views/stats/index.html.haml @@ -2,71 +2,75 @@ - content_for :footer do = render partial: "root/footer" -.statistiques{ 'data-controller': 'chartkick' } - %h1.new-h1 Statistiques +.fr-container.fr-my-4w + %h1 Statistiques d’utilisation de la plateforme - .stat-cards - .stat-card.stat-card-half.big-number-card.pull-left - %span.big-number-card-title.long-title TOTAL DÉMARCHES DÉMAT. OU EN COURS DE DÉMAT. - %span.big-number-card-number - = number_with_delimiter(@procedures_numbers[:total]) - %span.big-number-card-detail - #{number_with_delimiter(@procedures_numbers[:last_30_days_count])} (#{@procedures_numbers[:evolution]} %) sur les 30 derniers jours - %span.big-number-card-detail - = link_to "Voir carte de déploiement", carte_path + .fr-grid-row.fr-grid-row--gutters + .fr-col-xs-12.fr-col-sm-12.fr-col-lg-6 + .fr-callout{ data: { controller: 'chartkick' } } + %h2.fr-callout__title Démarches dématérialisées (total) + %p.fr-callout__text.big-number-card-number.fr-mb-2w + %span.big-number-card-number= number_with_delimiter(@procedures_numbers[:total]) + %p.fr-callout__text.fr-text--md.text-center + #{number_with_delimiter(@procedures_numbers[:last_30_days_count])} (#{@procedures_numbers[:evolution]} %) sur les 30 derniers jours + %br + = link_to "Voir carte de déploiement", carte_path - .stat-card.stat-card-half.big-number-card.pull-left - %span.big-number-card-title TOTAL DOSSIERS DÉPOSÉS - %span.big-number-card-number - = number_with_delimiter(@dossiers_numbers[:total]) - %span.big-number-card-detail - #{number_with_delimiter(@dossiers_numbers[:last_30_days_count])} (#{@dossiers_numbers[:evolution]} %) sur les 30 derniers jours - %span.big-number-card-detail - = link_to "Voir carte de déploiement", carte_path(map_filter: { kind: :nb_dossiers }) + %fieldset.fr-segmented.fr-segmented--sm.pull-right.fr-mt-2w.fr-my-1w + .fr-segmented__elements + .fr-segmented__element + %input{ value: "1", checked: true, type: "radio", id: "segmented-procedures-1", name: "segmented-procedures", data: { action: 'chartkick#toggleChart', 'toggle-chart': '.monthly-procedures-chart' } } + %label.fr-label{ for: "segmented-procedures-1" } + Par mois + .fr-segmented__element + %input{ value: "2", type: "radio", id: "segmented-procedures-2", name: "segmented-procedures", data: { action: 'chartkick#toggleChart', 'toggle-chart': '.cumulative-procedures-chart' } } + %label.fr-label{ for: "segmented-procedures-2" } + Cumul + + .chart-container + .chart.monthly-procedures-chart{ data: { 'chartkick-target': 'chart' }} + = column_chart @procedures_in_the_last_4_months, library: Chartkick.options[:default_library_config] + .chart.cumulative-procedures-chart.hidden{ data: { 'chartkick-target': 'chart' } } + = area_chart @procedures_cumulative, library: Chartkick.options[:default_library_config] + + .fr-col-xs-12.fr-col-sm-12.fr-col-lg-6 + .fr-callout{ data: { controller: 'chartkick' } } + %h2.fr-callout__title Dossiers déposés (total) + %p.fr-callout__text.big-number-card-number.fr-mb-2w + = number_with_delimiter(@dossiers_numbers[:total]) + %p.fr-callout__text.fr-text--md.text-center + #{number_with_delimiter(@dossiers_numbers[:last_30_days_count])} (#{@dossiers_numbers[:evolution]} %) sur les 30 derniers jours + %br + = link_to "Voir carte de déploiement", carte_path(map_filter: { kind: :nb_dossiers }) + + %fieldset.fr-segmented.fr-segmented--sm.pull-right.fr-mt-2w.fr-my-1w + .fr-segmented__elements + .fr-segmented__element + %input{ value: "1", checked: true, type: "radio", id: "segmented-dossiers-1", name: "segmented-dossiers", data: { action: 'chartkick#toggleChart', 'toggle-chart': '.monthly-dossiers-chart' } } + %label.fr-label{ for: "segmented-dossiers-1" } + Par mois + .fr-segmented__element + %input{ value: "2", type: "radio", id: "segmented-dossiers-2", name: "segmented-dossiers", data: { action: 'chartkick#toggleChart', 'toggle-chart': '.cumulative-dossiers-chart' } } + %label.fr-label{ for: "segmented-dossiers-2" } + Cumul - .stat-card.stat-card-half.pull-left - %ul.segmented-control.pull-right - %li.segmented-control-item.segmented-control-item-active{ data: { 'toggle-chart': '.monthly-procedures-chart' } } - Par mois - %li.segmented-control-item{ data: { 'toggle-chart': '.cumulative-procedures-chart' } } - Cumul - %span.stat-card-title.pull-left Démarches dématérialisées - .clearfix + .chart-container + .chart.monthly-dossiers-chart{ data: { 'chartkick-target': 'chart' }} + = column_chart @dossiers_in_the_last_4_months, library: Chartkick.options[:default_library_config] + .chart.cumulative-dossiers-chart.hidden{ data: { 'chartkick-target': 'chart' } } + = area_chart @dossiers_cumulative, library: Chartkick.options[:default_library_config] - .chart-container - .chart.monthly-procedures-chart - = column_chart @procedures_in_the_last_4_months - .chart.cumulative-procedures-chart.hidden - = area_chart @procedures_cumulative + .fr-col-xs-12.fr-col-sm-12.fr-col-lg-6 + .fr-callout + %h2.fr-callout__title Répartition des dossiers - .stat-card.stat-card-half.pull-left - %ul.segmented-control.pull-right - %li.segmented-control-item.segmented-control-item-active{ data: { 'toggle-chart': '.monthly-dossiers-chart' } } - Par mois - %li.segmented-control-item{ data: { 'toggle-chart': '.cumulative-dossiers-chart' } } - Cumul - %span.stat-card-title.pull-left Dossiers déposés - .clearfix - - .chart-container - .chart.monthly-dossiers-chart - = column_chart @dossiers_in_the_last_4_months - .chart.cumulative-dossiers-chart.hidden - = area_chart @dossiers_cumulative - - .stat-card.stat-card-half.pull-left - %span.stat-card-title - Répartition des dossiers - - .chart-container - .chart - = pie_chart @dossiers_states_for_pie, - colors: ["#000091", "#7F7FC8", "#9A9AFF", "#00006D"] - - .clearfix + .chart-container + .chart + = pie_chart @dossiers_states_for_pie, library: Chartkick.options[:default_library_config], + colors: ["#000091", "#7F7FC8", "#9A9AFF", "#00006D"] - if super_admin_signed_in? - %h2.new-h2 Téléchargement + %h2.fr-h4 Téléchargement - = link_to "Télécharger les statistiques (CSV)", stats_download_path(format: :csv), class: 'fr-btn fr-btn-primary mb-4' + = link_to "Télécharger les statistiques (CSV)", stats_download_path(format: :csv), class: 'fr-btn fr-btn-primary fr-mb-4w' diff --git a/config/initializers/chartkick.rb b/config/initializers/chartkick.rb index 4f44c1a38..fe4d559ad 100644 --- a/config/initializers/chartkick.rb +++ b/config/initializers/chartkick.rb @@ -1,6 +1,18 @@ Chartkick.options = { content_for: :charts_js, - colors: ["#000091"], + colors: ["var(--background-action-high-blue-france)"], thousands: ' ', - decimal: ',' + decimal: ',', + default_library_config: { + chart: { backgroundColor: 'var(--background-contrast-grey)' }, + xAxis: { + lineColor: 'var(--border-action-high-grey)', + labels: { style: { color: "var(--text-default-grey)" } } + }, + yAxis: { + gridLineColor: 'var(--border-plain-grey)', + lineColor: 'var(--border-action-high-grey)', + labels: { style: { color: "var(--text-default-grey)" } } + } + } }