diff --git a/Gemfile b/Gemfile index b0b7133c9..a2dcf01fe 100644 --- a/Gemfile +++ b/Gemfile @@ -84,6 +84,8 @@ gem "premailer-rails" gem 'smart_listing' +gem 'groupdate' + gem 'bootstrap-wysihtml5-rails', '~> 0.3.3.8' gem 'spreadsheet_architect', '~> 1.4.8' # https://github.com/westonganger/spreadsheet_architect/issues/14 diff --git a/Gemfile.lock b/Gemfile.lock index 47f448442..2d43f9b81 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -369,6 +369,8 @@ GEM formatador (0.2.5) globalid (0.4.1) activesupport (>= 4.2.0) + groupdate (4.0.1) + activesupport (>= 4.2) guard (2.14.2) formatador (>= 0.2.4) listen (>= 2.7, < 4.0) @@ -834,6 +836,7 @@ DEPENDENCIES fog fog-openstack font-awesome-rails + groupdate guard guard-livereload guard-rspec diff --git a/app/assets/stylesheets/new_design/landing.scss b/app/assets/stylesheets/new_design/landing.scss index 6c5260c28..3b9f35a0d 100644 --- a/app/assets/stylesheets/new_design/landing.scss +++ b/app/assets/stylesheets/new_design/landing.scss @@ -325,7 +325,7 @@ $users-breakpoint: 950px; $cta-panel-button-border-size: 2px; -.cta-panel-button-white { +@mixin cta-panel-button { @include horizontal-padding(40px); @include vertical-padding(15px); display: block; @@ -334,6 +334,10 @@ $cta-panel-button-border-size: 2px; text-align: center; cursor: pointer; margin-top: 20px; +} + +.cta-panel-button-white { + @include cta-panel-button; border: $cta-panel-button-border-size solid #FFFFFF; color: #FFFFFF; @@ -349,45 +353,24 @@ $cta-panel-button-border-size: 2px; } } -.cta-panel-button { - @include horizontal-padding(40px); - @include vertical-padding(15px); - display: block; - border-radius: 100px; - font-size: 24px; - text-align: center; - cursor: pointer; - margin-top: 20px; +.cta-panel-button-blue { + @include cta-panel-button; + border: $cta-panel-button-border-size solid $light-blue; + color: $light-blue; - &.black { - border: $cta-panel-button-border-size solid #000000; - color: #000000; - - &:hover { - text-decoration: none; - background-color: #F8F8F8; - } + &:hover { + color: #FFFFFF; + background-color: $light-blue; + text-decoration: none; &:focus { - color: #F8F8F8; - text-decoration: none; + color: #FFFFFF; } } - &.white { - border: $cta-panel-button-border-size solid #FFFFFF; - color: #FFFFFF; - - &:hover { - color: #FFFFFF; - text-decoration: none; - background-color: rgba(255, 255, 255, 0.2); - } - - &:focus { - color: #FFFFFF; - text-decoration: none; - } + &:focus { + color: $light-blue; + text-decoration: none; } } diff --git a/app/controllers/demandes_controller.rb b/app/controllers/demandes_controller.rb index c381df1ae..ead68d3fc 100644 --- a/app/controllers/demandes_controller.rb +++ b/app/controllers/demandes_controller.rb @@ -18,7 +18,7 @@ class DemandesController < ApplicationController demande_params[:deadline] ) flash.notice = 'Votre demande a bien été enregistrée, nous vous contacterons rapidement.' - redirect_to root_path + redirect_to administration_path end private diff --git a/app/controllers/new_user/feedbacks_controller.rb b/app/controllers/new_user/feedbacks_controller.rb index 9e7ca19f5..d7db41189 100644 --- a/app/controllers/new_user/feedbacks_controller.rb +++ b/app/controllers/new_user/feedbacks_controller.rb @@ -1,6 +1,6 @@ class NewUser::FeedbacksController < ApplicationController def create - current_user.feedbacks.create!(mark: params[:mark]) - flash.notice = "Merci de votre retour" + current_user.feedbacks.create!(rating: params[:rating]) + flash.notice = "Merci de votre retour, si vous souhaitez nous en dire plus, n'hésitez pas à nous contacter par email." end end diff --git a/app/controllers/stats_controller.rb b/app/controllers/stats_controller.rb index 62094ec17..7b5605e0e 100644 --- a/app/controllers/stats_controller.rb +++ b/app/controllers/stats_controller.rb @@ -1,6 +1,8 @@ class StatsController < ApplicationController layout "new_application" + before_action :authenticate_administration!, only: [:download] + MEAN_NUMBER_OF_CHAMPS_IN_A_FORM = 24.0 def index @@ -10,6 +12,9 @@ class StatsController < ApplicationController @procedures_count = procedures.count @dossiers_count = dossiers.count + @satisfaction_usagers = satisfaction_usagers + @dossiers_states = dossiers_states + @procedures_cumulative = cumulative_hash(procedures, :published_at) @procedures_in_the_last_4_months = last_four_months_hash(procedures, :published_at) @@ -36,8 +41,83 @@ class StatsController < ApplicationController @cloned_from_library_procedures_ratio = cloned_from_library_procedures_ratio end + def download + headers = [ + 'ID du dossier', + 'ID de la procédure', + 'Nom de la procédure', + 'ID utilisateur', + 'Etat du fichier', + 'Durée en brouillon', + 'Durée en construction', + 'Durée en instruction' + ] + + data = Dossier + .includes(:procedure, :user) + .in_batches + .flat_map do |dossiers| + + dossiers + .pluck( + "dossiers.id", + "procedures.id", + "procedures.libelle", + "users.id", + "dossiers.state", + "dossiers.en_construction_at - dossiers.created_at", + "dossiers.en_instruction_at - dossiers.en_construction_at", + "dossiers.processed_at - dossiers.en_instruction_at" + ) + end + + respond_to do |format| + format.csv { send_data(SpreadsheetArchitect.to_xlsx(headers: headers, data: data), filename: "statistiques.csv") } + end + end + private + def dossiers_states + { + 'Brouilllon' => Dossier.state_brouillon.count, + 'En construction' => Dossier.state_en_construction.count, + 'En instruction' => Dossier.state_en_instruction.count, + 'Terminé' => Dossier.state_termine.count + } + end + + def satisfaction_usagers + legend = { + Feedback.ratings.fetch(:unhappy) => "Mécontents", + Feedback.ratings.fetch(:neutral) => "Neutres", + Feedback.ratings.fetch(:happy) => "Satisfaits" + } + + totals = Feedback.where(created_at: 5.weeks.ago..Time.now).group_by_week(:created_at).count + + Feedback::rating.values.map do |rating| + data = Feedback + .where(created_at: 5.weeks.ago..Time.now, rating: rating) + .group_by_week(:created_at) + .count + .map do |week, count| + total = totals[week] + + if total > 0 + [week, (count.to_f / total).round(2)] + else + 0 + end + end.to_h + + { + name: legend[rating], + data: data + } + end + end + def cloned_from_library_procedures_ratio [3.weeks.ago, 2.weeks.ago, 1.week.ago].map do |date| min_date = date.beginning_of_week diff --git a/app/models/feedback.rb b/app/models/feedback.rb index 63affddde..d697541d9 100644 --- a/app/models/feedback.rb +++ b/app/models/feedback.rb @@ -1,3 +1,9 @@ class Feedback < ApplicationRecord belongs_to :user + + enum rating: { + happy: 'happy', + neutral: 'neutral', + unhappy: 'unhappy' + } end diff --git a/app/views/new_user/dossiers/index.html.haml b/app/views/new_user/dossiers/index.html.haml index ce0f01392..aecf834f0 100644 --- a/app/views/new_user/dossiers/index.html.haml +++ b/app/views/new_user/dossiers/index.html.haml @@ -54,15 +54,15 @@ = dossier.updated_at.localtime.strftime("%d/%m/%Y") = paginate(@dossiers) - - if current_user.feedbacks.empty? + - if current_user.feedbacks.empty? || current_user.feedbacks.last.created_at < 1.month.ago #user-satisfaction - %h3 Que pensez-vous de ce service ? + %h3 Que pensez-vous de la facilité d'utilisation de ce service ? .icons - = link_to feedback_path(mark: 0), data: { remote: true, method: :post } do + = link_to feedback_path(rating: Feedback.ratings.fetch(:unhappy)), data: { remote: true, method: :post } do %span.icon.frown - = link_to feedback_path(mark: 1), data: { remote: true, method: :post } do + = link_to feedback_path(rating: Feedback.ratings.fetch(:neutral)), data: { remote: true, method: :post } do %span.icon.meh - = link_to feedback_path(mark: 2), data: { remote: true, method: :post } do + = link_to feedback_path(rating: Feedback.ratings.fetch(:happy)), data: { remote: true, method: :post } do %span.icon.smile - else diff --git a/app/views/new_user/feedbacks/create.js.erb b/app/views/new_user/feedbacks/create.js.erb index de4c271b3..c2b5f9c9f 100644 --- a/app/views/new_user/feedbacks/create.js.erb +++ b/app/views/new_user/feedbacks/create.js.erb @@ -1,2 +1,3 @@ +window.scroll({ top: 0, left: 0, behavior: 'smooth' }); <%= remove_element('#user-satisfaction') %> <%= render_flash %> diff --git a/app/views/notification_mailer/refused_mail.html.haml b/app/views/notification_mailer/refused_mail.html.haml index 79f5991c6..e7503723e 100644 --- a/app/views/notification_mailer/refused_mail.html.haml +++ b/app/views/notification_mailer/refused_mail.html.haml @@ -5,7 +5,7 @@ Votre dossier nº --numéro du dossier-- a été refusé le --date de décision--. %p - Le motif de refus est le suivant : --motivation-- + Le motif de refus est le suivant : --motivation--. %p Pour en savoir plus sur le motif du refus, vous pouvez consulter votre dossier et les éventuels messages de l'administration à cette adresse : --lien dossier-- diff --git a/app/views/notification_mailer/without_continuation_mail.html.haml b/app/views/notification_mailer/without_continuation_mail.html.haml index c0721aee1..4240a4e2a 100644 --- a/app/views/notification_mailer/without_continuation_mail.html.haml +++ b/app/views/notification_mailer/without_continuation_mail.html.haml @@ -5,7 +5,7 @@ Votre dossier nº --numéro du dossier-- a été classé sans suite le --date de décision--. %p - Le motif est le suivant : --motivation-- + Le motif est le suivant : --motivation--. %p Pour en savoir plus sur les raisons de ce classement sans suite, vous pouvez consulter votre dossier et les éventuels messages de l'administration à cette adresse : --lien dossier-- diff --git a/app/views/root/administration.html.haml b/app/views/root/administration.html.haml index 5e8a3b29b..5820a7458 100644 --- a/app/views/root/administration.html.haml +++ b/app/views/root/administration.html.haml @@ -26,9 +26,7 @@ = link_to "Demander un compte administrateur", new_demande_path, class: "role-panel-button-primary", - target: "_blank", - rel: "noopener noreferrer", - onclick: "javascript: ga('send', 'pageview', '/demander-une-demo')" + rel: "noopener noreferrer" = link_to "Voir la documentation", DOC_URL, @@ -189,3 +187,14 @@ class: "cta-panel-button-white", target: "_blank", rel: "noopener noreferrer" + + .landing-panel + .container + .cta-panel-wrapper + %div + %h1.cta-panel-title Vous êtes prêt pour dématérialiser ? + %p.cta-panel-explanation Réduisez vos temps d'instruction de 50 % + %div + = link_to "Demander un compte administrateur", + new_demande_path, + class: "cta-panel-button-blue" diff --git a/app/views/root/landing.html.haml b/app/views/root/landing.html.haml index 43fbfe30a..f0d36673c 100644 --- a/app/views/root/landing.html.haml +++ b/app/views/root/landing.html.haml @@ -72,7 +72,7 @@ %div = link_to "Contactez-nous", "mailto:#{CONTACT_EMAIL}?subject=Question%20à%20propos%20de%20demarches-simplifiees.fr", - class: "cta-panel-button white", + class: "cta-panel-button-white", target: "_blank", rel: "noopener noreferrer" @@ -85,4 +85,4 @@ %div = link_to "Découvrez notre outil", administration_path, - class: "cta-panel-button black" + class: "cta-panel-button-blue" diff --git a/app/views/stats/index.html.haml b/app/views/stats/index.html.haml index 8267176d3..666c3033c 100644 --- a/app/views/stats/index.html.haml +++ b/app/views/stats/index.html.haml @@ -15,6 +15,24 @@ %span.big-number-card-number = number_with_delimiter(@dossiers_count) + .stat-card.stat-card-half.pull-left + %span.stat-card-title + Satisfaction usager + + .chart-container + .chart + = line_chart @satisfaction_usagers, + colors: ["#F28900", "rgba(161, 0, 5, 0.9)", "#15AD70"] + + .stat-card.stat-card-half.pull-left + %span.stat-card-title + Répartition des dossiers + + .chart-container + .chart + = pie_chart @dossiers_states, + colors: ["rgba(222, 238, 265, 1)", "rgba(191, 220, 249, 1)", "rgba(113, 176, 239, 1)", "rgba(61, 149, 236, 1)"] + .stat-card.stat-card-half.pull-left %ul.segmented-control.pull-right %li.segmented-control-item.segmented-control-item-active{ :onclick => "DS.toggleChart(event, '.monthly-procedures-chart');" } @@ -112,3 +130,7 @@ = column_chart @cloned_from_library_procedures_ratio, ytitle: 'procédures clonées / total procédure', xtitle: 'semaines' .clearfix + + %h2.new-h2 Téléchargement + + = link_to "Télécharger les statistiques (CSV)", stats_download_path(format: :csv), class: 'button secondary' diff --git a/config/initializers/lograge.rb b/config/initializers/lograge.rb index 8fec7ceea..cee0b7bd8 100644 --- a/config/initializers/lograge.rb +++ b/config/initializers/lograge.rb @@ -33,6 +33,6 @@ Rails.application.configure do config.lograge.logger = ActiveSupport::Logger.new Rails.root.join('log', "logstash_#{Rails.env}.log") if config.lograge.enabled - ActiveJobLogSubscriber.attach_to :active_job + ActiveJobLogSubscriber.attach_to(:active_job) end end diff --git a/config/routes.rb b/config/routes.rb index 3d58ea508..b159b8e38 100644 --- a/config/routes.rb +++ b/config/routes.rb @@ -99,7 +99,8 @@ Rails.application.routes.draw do get 'users' => 'users#index' get 'admin' => 'admin#index' - resources :stats, only: [:index] + get '/stats' => 'stats#index' + get '/stats/download' => 'stats#download' resources :accessibilite, only: [:index] resources :demandes, only: [:new, :create] diff --git a/db/migrate/20180827102828_add_rating_to_feedbacks.rb b/db/migrate/20180827102828_add_rating_to_feedbacks.rb new file mode 100644 index 000000000..cc3c0c97d --- /dev/null +++ b/db/migrate/20180827102828_add_rating_to_feedbacks.rb @@ -0,0 +1,5 @@ +class AddRatingToFeedbacks < ActiveRecord::Migration[5.2] + def change + add_column :feedbacks, :rating, :string + end +end diff --git a/db/schema.rb b/db/schema.rb index 492fe9f51..f3ed94862 100644 --- a/db/schema.rb +++ b/db/schema.rb @@ -10,7 +10,7 @@ # # It's strongly recommended that you check this file into your version control system. -ActiveRecord::Schema.define(version: 2018_08_22_162952) do +ActiveRecord::Schema.define(version: 2018_08_27_102828) do # These are extensions that must be enabled in order to support this database enable_extension "plpgsql" @@ -323,6 +323,7 @@ ActiveRecord::Schema.define(version: 2018_08_22_162952) do t.integer "mark" t.datetime "created_at", null: false t.datetime "updated_at", null: false + t.string "rating" t.index ["user_id"], name: "index_feedbacks_on_user_id" end diff --git a/lib/tasks/2018_08_27_migrate_feedbacks.rake b/lib/tasks/2018_08_27_migrate_feedbacks.rake new file mode 100644 index 000000000..f57a3bd3a --- /dev/null +++ b/lib/tasks/2018_08_27_migrate_feedbacks.rake @@ -0,0 +1,19 @@ +require Rails.root.join("lib", "tasks", "task_helper") + +namespace :'2018_08_27_migrate_feedbacks' do + task run: :environment do + MAPPING = { + 0 => Feedback.ratings.fetch(:unhappy), + 1 => Feedback.ratings.fetch(:neutral), + 2 => Feedback.ratings.fetch(:happy) + } + + MAPPING.keys.each do |mark| + rating = MAPPING[mark] + + Feedback + .where(mark: mark) + .update_all(rating: rating) + end + end +end diff --git a/spec/factories/feedback.rb b/spec/factories/feedback.rb index 231989f34..d332d2eae 100644 --- a/spec/factories/feedback.rb +++ b/spec/factories/feedback.rb @@ -1,5 +1,5 @@ FactoryBot.define do factory :feedback do - mark 3 + rating Feedback.ratings.fetch(:happy) end end diff --git a/spec/views/new_user/dossiers/index.html.haml_spec.rb b/spec/views/new_user/dossiers/index.html.haml_spec.rb index 9ee653464..3ba7f6882 100644 --- a/spec/views/new_user/dossiers/index.html.haml_spec.rb +++ b/spec/views/new_user/dossiers/index.html.haml_spec.rb @@ -74,7 +74,7 @@ describe 'new_user/dossiers/index.html.haml', type: :view do context "quand le user n'a aucun feedback" do it "affiche le formulaire de satisfaction" do - expect(rendered).to have_selector('#user-satisfaction', text: 'Que pensez-vous de ce service ?') + expect(rendered).to have_selector('#user-satisfaction', text: 'Que pensez-vous de la facilité d\'utilisation de ce service ?') end end