commit
fbbee615c4
12 changed files with 478 additions and 0 deletions
12
app/assets/stylesheets/new_design/contact.scss
Normal file
12
app/assets/stylesheets/new_design/contact.scss
Normal 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;
|
||||
}
|
||||
}
|
|
@ -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!
|
||||
|
|
74
app/controllers/support_controller.rb
Normal file
74
app/controllers/support_controller.rb
Normal 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
133
app/lib/helpscout/api.rb
Normal 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
|
56
app/lib/helpscout/form_adapter.rb
Normal file
56
app/lib/helpscout/form_adapter.rb
Normal 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
|
48
app/views/support/index.html.haml
Normal file
48
app/views/support/index.html.haml
Normal 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'
|
|
@ -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
|
||||
|
|
|
@ -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:
|
||||
|
|
|
@ -120,6 +120,9 @@ Rails.application.routes.draw do
|
|||
|
||||
get "patron" => "root#patron"
|
||||
|
||||
get "contact" => "support#index"
|
||||
post "contact" => "support#create"
|
||||
|
||||
#
|
||||
# Deprecated UI
|
||||
#
|
||||
|
|
|
@ -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
|
||||
|
|
64
spec/controllers/support_controller_spec.rb
Normal file
64
spec/controllers/support_controller_spec.rb
Normal 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
|
70
spec/lib/helpscout/form_adapter_spec.rb
Normal file
70
spec/lib/helpscout/form_adapter_spec.rb
Normal 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
|
Loading…
Reference in a new issue