Merge pull request #2438 from tchak/support-form

Add support form
This commit is contained in:
Paul Chavard 2018-08-29 11:01:10 +01:00 committed by GitHub
commit fbbee615c4
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
12 changed files with 478 additions and 0 deletions

View file

@ -0,0 +1,12 @@
$default-space: 15px;
$contact-padding: $default-space * 2;
#contact-form {
width: 1040px;
margin: 0 auto;
padding-top: $contact-padding;
.description {
padding-bottom: $contact-padding;
}
}

View file

@ -33,6 +33,12 @@ class ApplicationController < ActionController::Base
@facade_data_view = nil
end
def logged_in?
logged_user.present?
end
helper_method :logged_in?
protected
def authenticate_gestionnaire!

View file

@ -0,0 +1,74 @@
class SupportController < ApplicationController
layout "new_application"
def index
setup_context
end
def create
if direct_message? && create_commentaire
flash.notice = "Votre message a été envoyé sur la messagerie de votre dossier."
redirect_to users_dossier_recapitulatif_path(dossier)
elsif create_conversation
flash.notice = "Votre message a été envoyé."
redirect_to root_path
else
setup_context
flash.now.alert = "Une erreur est survenue. Vous pouvez nous contactez à #{CONTACT_EMAIL}."
render :index
end
end
private
def setup_context
@dossier_id = dossier&.id
@tags = params[:tags]
@options = Helpscout::FormAdapter::OPTIONS
end
def create_conversation
Helpscout::FormAdapter.new(
subject: params[:subject],
email: email,
text: params[:text],
file: params[:file],
dossier_id: dossier&.id,
browser: browser_name,
tags: tags
).send_form
end
def create_commentaire
dossier.commentaires.create(
email: email,
file: params[:file],
body: "[#{params[:subject]}]<br><br>#{params[:text]}"
)
end
def tags
[params[:tags], params[:type]].flatten.compact
end
def browser_name
if browser.known?
"#{browser.name} #{browser.version} (#{browser.platform.name})"
end
end
def direct_message?
user_signed_in? && params[:type] == Helpscout::FormAdapter::TYPE_INSTRUCTION && dossier.present?
end
def dossier
@dossier ||= current_user&.dossiers&.find_by(id: params[:dossier_id])
end
def email
logged_user ? logged_user.email : params[:email]
end
end

133
app/lib/helpscout/api.rb Normal file
View file

@ -0,0 +1,133 @@
class Helpscout::API
MAILBOXES = 'mailboxes'
CONVERSATIONS = 'conversations'
TAGS = 'tags'
FIELDS = 'fields'
OAUTH2_TOKEN = 'oauth2/token'
def add_tags(conversation_id, tags)
call_api(:put, "#{CONVERSATIONS}/#{conversation_id}/#{TAGS}", {
tags: tags
})
end
def add_custom_fields(conversation_id, dossier_id, browser)
body = {
'Dossier ID': dossier_id,
'Browser': browser
}.compact.map do |key, value|
{ id: custom_fields[key], value: value }
end
call_api(:put, "#{CONVERSATIONS}/#{conversation_id}/#{FIELDS}", { fields: body })
end
def create_conversation(email, subject, text, file)
body = {
subject: subject,
customer: customer(email),
mailboxId: mailbox_id,
type: 'email',
status: 'active',
threads: [
{
type: 'customer',
customer: customer(email),
text: text,
attachments: attachments(file)
}
]
}.compact
call_api(:post, CONVERSATIONS, body)
end
private
def attachments(file)
if file.present?
[
{
fileName: file.original_filename,
mimeType: file.content_type,
data: Base64.strict_encode64(file.read)
}
]
else
[]
end
end
def customer(email)
{
email: email
}
end
def custom_fields
@custom_fields ||= get_custom_fields.reduce({}) do |fields, field|
fields[field[:name].to_sym] = field[:id]
fields
end
end
def get_custom_fields
parse_response_body(fetch_custom_fields)[:_embedded][:fields]
end
def fetch_custom_fields
call_api(:get, "#{MAILBOXES}/#{mailbox_id}/#{FIELDS}")
end
def call_api(method, path, body = nil)
url = "#{HELPSCOUT_API_URL}/#{path}"
case method
when :get
Typhoeus.get(url, {
headers: headers
})
when :post
Typhoeus.post(url, {
body: body.to_json,
headers: headers
})
when :put
Typhoeus.put(url, {
body: body.to_json,
headers: headers
})
end
end
def parse_response_body(response)
JSON.parse(response.body, symbolize_names: true)
end
def mailbox_id
Rails.application.secrets.helpscout[:mailbox_id]
end
def headers
{
'Authorization': "Bearer #{access_token}",
'Content-Type': 'application/json; charset=UTF-8'
}
end
def access_token
@access_token ||= get_access_token
end
def get_access_token
parse_response_body(fetch_access_token)[:access_token]
end
def fetch_access_token
Typhoeus.post("#{HELPSCOUT_API_URL}/#{OAUTH2_TOKEN}", body: {
grant_type: 'client_credentials',
client_id: Rails.application.secrets.helpscout[:client_id],
client_secret: Rails.application.secrets.helpscout[:client_secret]
})
end
end

View file

@ -0,0 +1,56 @@
class Helpscout::FormAdapter
attr_reader :params
def initialize(params = {}, api = nil)
@params = params
@api = api || Helpscout::API.new
end
TYPE_INFO = 'info demarche'
TYPE_PERDU = 'usager perdu'
TYPE_INSTRUCTION = 'info instruction'
TYPE_AMELIORATION = 'produit'
TYPE_AUTRE = 'autre'
OPTIONS = [
[I18n.t(TYPE_INFO, scope: [:support]), TYPE_INFO],
[I18n.t(TYPE_PERDU, scope: [:support]), TYPE_PERDU],
[I18n.t(TYPE_INSTRUCTION, scope: [:support]), TYPE_INSTRUCTION],
[I18n.t(TYPE_AMELIORATION, scope: [:support]), TYPE_AMELIORATION],
[I18n.t(TYPE_AUTRE, scope: [:support]), TYPE_AUTRE]
]
def send_form
conversation_id = create_conversation
if conversation_id.present?
add_tags(conversation_id)
add_custom_fields(conversation_id)
true
else
false
end
end
private
def add_tags(conversation_id)
@api.add_tags(conversation_id, params[:tags])
end
def add_custom_fields(conversation_id)
@api.add_custom_fields(conversation_id, params[:dossier_id], params[:browser])
end
def create_conversation
response = @api.create_conversation(
params[:email],
params[:subject],
params[:text],
params[:file]
)
response.success? ? response.headers['Resource-ID'] : nil
end
end

View file

@ -0,0 +1,48 @@
- content_for(:title, 'Contact')
#contact-form
.container
%h1.new-h1 Contact
.description
Contactez-nous via ce formulaire et nous vous répondrons dans les plus brefs délais.
Pensez bien à nous donner le plus d'informations possible pour que nous puissions vous aider au mieux.
= form_tag contact_path, method: :post, multipart: true, class: 'form' do |f|
- if !logged_in?
.contact-champ
= label_tag :email do
Email
%span.mandatory *
= text_field_tag :email, '', required: true
.contact-champ
= label_tag :type do
Votre problème
%span.mandatory *
= select_tag :type, options_for_select(@options)
.contact-champ
= label_tag :dossier_id, 'Numéro du dossier concerné'
= text_field_tag :dossier_id, @dossier_id
.contact-champ
= label_tag :subject do
Sujet
%span.mandatory *
= text_field_tag :subject, '', required: true
.contact-champ
= label_tag :text do
Message
%span.mandatory *
= text_area_tag :text, '', rows: 6, required: true
.contact-champ
= label_tag :text, 'Pièce jointe'
= file_field_tag :file
= hidden_field_tag :tags, @tags
.send-wrapper
= button_tag 'Envoyer le message', type: :submit, class: 'button send primary'

View file

@ -2,6 +2,7 @@
API_CARTO_URL = "https://apicarto.sgmap.fr"
API_ENTREPRISE_URL = "https://entreprise.api.gouv.fr/v2"
API_GEO_URL = "https://geo.api.gouv.fr"
HELPSCOUT_API_URL = 'https://api.helpscout.net/v2'
PIPEDRIVE_API_URL = 'https://api.pipedrive.com/v1'
# External services URLs

View file

@ -48,6 +48,13 @@ fr:
publish: Publier
reopen: Réactiver
support:
info demarche: J'ai un problème lors du remplissage de mon dossier
info instruction: J'ai une question sur l'instruction de mon dossier
produit: J'ai une idée d'amélioration pour votre site
usager perdu: Je ne trouve pas la démarche que je veux faire
autre: Autre sujet
number:
currency:
format:

View file

@ -120,6 +120,9 @@ Rails.application.routes.draw do
get "patron" => "root#patron"
get "contact" => "support#index"
post "contact" => "support#create"
#
# Deprecated UI
#

View file

@ -44,6 +44,10 @@ defaults: &defaults
mailtrap:
username: <%= ENV['MAILTRAP_USERNAME'] %>
password: <%= ENV['MAILTRAP_PASSWORD'] %>
helpscout:
mailbox_id: <%= ENV['HELPSCOUT_MAILBOX_ID'] %>
client_id: <%= ENV['HELPSCOUT_CLIENT_ID'] %>
client_secret: <%= ENV['HELPSCOUT_CLIENT_SECRET'] %>
development:
<<: *defaults

View file

@ -0,0 +1,64 @@
require 'spec_helper'
describe SupportController, type: :controller do
render_views
context 'signed in' do
before do
sign_in user
end
let(:user) { create(:user) }
describe "with dossier" do
let(:user) { dossier.user }
let(:dossier) { create(:dossier) }
it 'should fill dossier_id' do
get :index, params: { dossier_id: dossier.id }
expect(response.status).to eq(200)
expect(response.body).to include("#{dossier.id}")
end
it 'should not have email field' do
get :index
expect(response.status).to eq(200)
expect(response.body).not_to have_content("Email *")
end
end
describe "with dossier" do
let(:tag) { 'yolo' }
it 'should fill tags' do
get :index, params: { tags: [tag] }
expect(response.status).to eq(200)
expect(response.body).to include(tag)
end
end
end
context 'signed out' do
describe "with dossier" do
it 'should have email field' do
get :index
expect(response.status).to eq(200)
expect(response.body).to have_content("Email *")
end
end
describe "with dossier" do
let(:tag) { 'yolo' }
it 'should fill tags' do
get :index, params: { tags: [tag] }
expect(response.status).to eq(200)
expect(response.body).to include(tag)
end
end
end
end

View file

@ -0,0 +1,70 @@
require 'spec_helper'
describe Helpscout::FormAdapter do
describe '#send_form' do
let(:api) { spy(double(:api)) }
context 'create_conversation' do
before do
allow(api).to receive(:create_conversation)
.and_return(double(success?: false))
described_class.new(params, api).send_form
end
let(:params) {
{
email: email,
subject: subject,
text: text
}
}
let(:email) { 'paul.chavard@beta.gouv.fr' }
let(:subject) { 'Bonjour' }
let(:text) { "J'ai un problem" }
it 'should call method' do
expect(api).to have_received(:create_conversation)
.with(email, subject, text, nil)
end
end
context 'add_tags' do
before do
allow(api).to receive(:create_conversation)
.and_return(
double(
success?: true,
headers: {
'Resource-ID' => conversation_id
}
)
)
described_class.new(params, api).send_form
end
let(:params) {
{
email: email,
subject: subject,
text: text,
tags: tags
}
}
let(:email) { 'paul.chavard@beta.gouv.fr' }
let(:subject) { 'Bonjour' }
let(:text) { "J'ai un problem" }
let(:tags) { ['info demarche'] }
let(:conversation_id) { '123' }
it 'should call method' do
expect(api).to have_received(:create_conversation)
.with(email, subject, text, nil)
expect(api).to have_received(:add_tags)
.with(conversation_id, tags)
expect(api).to have_received(:add_custom_fields)
.with(conversation_id, nil, nil)
end
end
end
end