commit
4faec089e9
56 changed files with 1255 additions and 332 deletions
3
Gemfile
3
Gemfile
|
@ -13,12 +13,9 @@ gem 'bcrypt'
|
||||||
gem 'bootstrap-sass', '>= 3.4.1'
|
gem 'bootstrap-sass', '>= 3.4.1'
|
||||||
gem 'bootstrap-wysihtml5-rails', '~> 0.3.3.8'
|
gem 'bootstrap-wysihtml5-rails', '~> 0.3.3.8'
|
||||||
gem 'browser'
|
gem 'browser'
|
||||||
gem 'carrierwave'
|
|
||||||
gem 'carrierwave-i18n'
|
|
||||||
gem 'chartkick'
|
gem 'chartkick'
|
||||||
gem 'chunky_png'
|
gem 'chunky_png'
|
||||||
gem 'clamav-client', require: 'clamav/client'
|
gem 'clamav-client', require: 'clamav/client'
|
||||||
gem 'copy_carrierwave_file'
|
|
||||||
gem 'daemons'
|
gem 'daemons'
|
||||||
gem 'deep_cloneable' # Enable deep clone of active record models
|
gem 'deep_cloneable' # Enable deep clone of active record models
|
||||||
gem 'delayed_cron_job' # Cron jobs
|
gem 'delayed_cron_job' # Cron jobs
|
||||||
|
|
104
Gemfile.lock
104
Gemfile.lock
|
@ -20,25 +20,25 @@ GEM
|
||||||
specs:
|
specs:
|
||||||
aasm (5.0.1)
|
aasm (5.0.1)
|
||||||
concurrent-ruby (~> 1.0)
|
concurrent-ruby (~> 1.0)
|
||||||
actioncable (5.2.2.1)
|
actioncable (5.2.3)
|
||||||
actionpack (= 5.2.2.1)
|
actionpack (= 5.2.3)
|
||||||
nio4r (~> 2.0)
|
nio4r (~> 2.0)
|
||||||
websocket-driver (>= 0.6.1)
|
websocket-driver (>= 0.6.1)
|
||||||
actionmailer (5.2.2.1)
|
actionmailer (5.2.3)
|
||||||
actionpack (= 5.2.2.1)
|
actionpack (= 5.2.3)
|
||||||
actionview (= 5.2.2.1)
|
actionview (= 5.2.3)
|
||||||
activejob (= 5.2.2.1)
|
activejob (= 5.2.3)
|
||||||
mail (~> 2.5, >= 2.5.4)
|
mail (~> 2.5, >= 2.5.4)
|
||||||
rails-dom-testing (~> 2.0)
|
rails-dom-testing (~> 2.0)
|
||||||
actionpack (5.2.2.1)
|
actionpack (5.2.3)
|
||||||
actionview (= 5.2.2.1)
|
actionview (= 5.2.3)
|
||||||
activesupport (= 5.2.2.1)
|
activesupport (= 5.2.3)
|
||||||
rack (~> 2.0)
|
rack (~> 2.0)
|
||||||
rack-test (>= 0.6.3)
|
rack-test (>= 0.6.3)
|
||||||
rails-dom-testing (~> 2.0)
|
rails-dom-testing (~> 2.0)
|
||||||
rails-html-sanitizer (~> 1.0, >= 1.0.2)
|
rails-html-sanitizer (~> 1.0, >= 1.0.2)
|
||||||
actionview (5.2.2.1)
|
actionview (5.2.3)
|
||||||
activesupport (= 5.2.2.1)
|
activesupport (= 5.2.3)
|
||||||
builder (~> 3.1)
|
builder (~> 3.1)
|
||||||
erubi (~> 1.4)
|
erubi (~> 1.4)
|
||||||
rails-dom-testing (~> 2.0)
|
rails-dom-testing (~> 2.0)
|
||||||
|
@ -51,25 +51,25 @@ GEM
|
||||||
activemodel (>= 4.1, < 6)
|
activemodel (>= 4.1, < 6)
|
||||||
case_transform (>= 0.2)
|
case_transform (>= 0.2)
|
||||||
jsonapi-renderer (>= 0.1.1.beta1, < 0.3)
|
jsonapi-renderer (>= 0.1.1.beta1, < 0.3)
|
||||||
activejob (5.2.2.1)
|
activejob (5.2.3)
|
||||||
activesupport (= 5.2.2.1)
|
activesupport (= 5.2.3)
|
||||||
globalid (>= 0.3.6)
|
globalid (>= 0.3.6)
|
||||||
activemodel (5.2.2.1)
|
activemodel (5.2.3)
|
||||||
activesupport (= 5.2.2.1)
|
activesupport (= 5.2.3)
|
||||||
activerecord (5.2.2.1)
|
activerecord (5.2.3)
|
||||||
activemodel (= 5.2.2.1)
|
activemodel (= 5.2.3)
|
||||||
activesupport (= 5.2.2.1)
|
activesupport (= 5.2.3)
|
||||||
arel (>= 9.0)
|
arel (>= 9.0)
|
||||||
activestorage (5.2.2.1)
|
activestorage (5.2.3)
|
||||||
actionpack (= 5.2.2.1)
|
actionpack (= 5.2.3)
|
||||||
activerecord (= 5.2.2.1)
|
activerecord (= 5.2.3)
|
||||||
marcel (~> 0.3.1)
|
marcel (~> 0.3.1)
|
||||||
activestorage-openstack (1.0.0)
|
activestorage-openstack (1.2.0)
|
||||||
fog-openstack (~> 1.0)
|
fog-openstack (~> 1.0)
|
||||||
marcel
|
marcel
|
||||||
mime-types
|
mime-types
|
||||||
rails (<= 6)
|
rails (>= 5.2.2)
|
||||||
activesupport (5.2.2.1)
|
activesupport (5.2.3)
|
||||||
concurrent-ruby (~> 1.0, >= 1.0.2)
|
concurrent-ruby (~> 1.0, >= 1.0.2)
|
||||||
i18n (>= 0.7, < 2)
|
i18n (>= 0.7, < 2)
|
||||||
minitest (~> 5.1)
|
minitest (~> 5.1)
|
||||||
|
@ -136,11 +136,6 @@ GEM
|
||||||
capybara-selenium (0.0.6)
|
capybara-selenium (0.0.6)
|
||||||
capybara
|
capybara
|
||||||
selenium-webdriver
|
selenium-webdriver
|
||||||
carrierwave (1.3.1)
|
|
||||||
activemodel (>= 4.0.0)
|
|
||||||
activesupport (>= 4.0.0)
|
|
||||||
mime-types (>= 1.16)
|
|
||||||
carrierwave-i18n (0.2.0)
|
|
||||||
case_transform (0.2)
|
case_transform (0.2)
|
||||||
activesupport
|
activesupport
|
||||||
chartkick (3.2.0)
|
chartkick (3.2.0)
|
||||||
|
@ -158,8 +153,6 @@ GEM
|
||||||
coffee-script-source (1.12.2)
|
coffee-script-source (1.12.2)
|
||||||
concurrent-ruby (1.1.5)
|
concurrent-ruby (1.1.5)
|
||||||
connection_pool (2.2.2)
|
connection_pool (2.2.2)
|
||||||
copy_carrierwave_file (1.3.0)
|
|
||||||
carrierwave (>= 0.9)
|
|
||||||
crack (0.4.3)
|
crack (0.4.3)
|
||||||
safe_yaml (~> 1.0.0)
|
safe_yaml (~> 1.0.0)
|
||||||
crass (1.0.5)
|
crass (1.0.5)
|
||||||
|
@ -203,7 +196,7 @@ GEM
|
||||||
em-websocket (0.5.1)
|
em-websocket (0.5.1)
|
||||||
eventmachine (>= 0.12.9)
|
eventmachine (>= 0.12.9)
|
||||||
http_parser.rb (~> 0.6.0)
|
http_parser.rb (~> 0.6.0)
|
||||||
erubi (1.8.0)
|
erubi (1.9.0)
|
||||||
erubis (2.7.0)
|
erubis (2.7.0)
|
||||||
ethon (0.11.0)
|
ethon (0.11.0)
|
||||||
ffi (>= 1.3.0)
|
ffi (>= 1.3.0)
|
||||||
|
@ -311,7 +304,7 @@ GEM
|
||||||
domain_name (~> 0.5)
|
domain_name (~> 0.5)
|
||||||
http_parser.rb (0.6.0)
|
http_parser.rb (0.6.0)
|
||||||
httpclient (2.8.3)
|
httpclient (2.8.3)
|
||||||
i18n (1.6.0)
|
i18n (1.7.0)
|
||||||
concurrent-ruby (~> 1.0)
|
concurrent-ruby (~> 1.0)
|
||||||
ipaddress (0.8.3)
|
ipaddress (0.8.3)
|
||||||
jaro_winkler (1.5.2)
|
jaro_winkler (1.5.2)
|
||||||
|
@ -320,7 +313,7 @@ GEM
|
||||||
railties (>= 4.2.0)
|
railties (>= 4.2.0)
|
||||||
thor (>= 0.14, < 2.0)
|
thor (>= 0.14, < 2.0)
|
||||||
json (2.2.0)
|
json (2.2.0)
|
||||||
json-jwt (1.10.0)
|
json-jwt (1.11.0)
|
||||||
activesupport (>= 4.2)
|
activesupport (>= 4.2)
|
||||||
aes_key_wrap
|
aes_key_wrap
|
||||||
bindata
|
bindata
|
||||||
|
@ -375,7 +368,7 @@ GEM
|
||||||
mimemagic (0.3.3)
|
mimemagic (0.3.3)
|
||||||
mini_mime (1.0.2)
|
mini_mime (1.0.2)
|
||||||
mini_portile2 (2.4.0)
|
mini_portile2 (2.4.0)
|
||||||
minitest (5.11.3)
|
minitest (5.13.0)
|
||||||
momentjs-rails (2.20.1)
|
momentjs-rails (2.20.1)
|
||||||
railties (>= 3.1)
|
railties (>= 3.1)
|
||||||
multi_json (1.14.1)
|
multi_json (1.14.1)
|
||||||
|
@ -384,7 +377,7 @@ GEM
|
||||||
mustermann (1.0.3)
|
mustermann (1.0.3)
|
||||||
nenv (0.3.0)
|
nenv (0.3.0)
|
||||||
netrc (0.11.0)
|
netrc (0.11.0)
|
||||||
nio4r (2.3.1)
|
nio4r (2.5.2)
|
||||||
nokogiri (1.10.5)
|
nokogiri (1.10.5)
|
||||||
mini_portile2 (~> 2.4.0)
|
mini_portile2 (~> 2.4.0)
|
||||||
notiffany (0.1.1)
|
notiffany (0.1.1)
|
||||||
|
@ -469,18 +462,18 @@ GEM
|
||||||
rack
|
rack
|
||||||
rack-test (1.1.0)
|
rack-test (1.1.0)
|
||||||
rack (>= 1.0, < 3)
|
rack (>= 1.0, < 3)
|
||||||
rails (5.2.2.1)
|
rails (5.2.3)
|
||||||
actioncable (= 5.2.2.1)
|
actioncable (= 5.2.3)
|
||||||
actionmailer (= 5.2.2.1)
|
actionmailer (= 5.2.3)
|
||||||
actionpack (= 5.2.2.1)
|
actionpack (= 5.2.3)
|
||||||
actionview (= 5.2.2.1)
|
actionview (= 5.2.3)
|
||||||
activejob (= 5.2.2.1)
|
activejob (= 5.2.3)
|
||||||
activemodel (= 5.2.2.1)
|
activemodel (= 5.2.3)
|
||||||
activerecord (= 5.2.2.1)
|
activerecord (= 5.2.3)
|
||||||
activestorage (= 5.2.2.1)
|
activestorage (= 5.2.3)
|
||||||
activesupport (= 5.2.2.1)
|
activesupport (= 5.2.3)
|
||||||
bundler (>= 1.3.0)
|
bundler (>= 1.3.0)
|
||||||
railties (= 5.2.2.1)
|
railties (= 5.2.3)
|
||||||
sprockets-rails (>= 2.0.0)
|
sprockets-rails (>= 2.0.0)
|
||||||
rails-controller-testing (1.0.4)
|
rails-controller-testing (1.0.4)
|
||||||
actionpack (>= 5.0.1.x)
|
actionpack (>= 5.0.1.x)
|
||||||
|
@ -489,14 +482,14 @@ GEM
|
||||||
rails-dom-testing (2.0.3)
|
rails-dom-testing (2.0.3)
|
||||||
activesupport (>= 4.2.0)
|
activesupport (>= 4.2.0)
|
||||||
nokogiri (>= 1.6)
|
nokogiri (>= 1.6)
|
||||||
rails-html-sanitizer (1.2.0)
|
rails-html-sanitizer (1.3.0)
|
||||||
loofah (~> 2.2, >= 2.2.2)
|
loofah (~> 2.3)
|
||||||
rails-i18n (5.1.2)
|
rails-i18n (5.1.2)
|
||||||
i18n (>= 0.7, < 2)
|
i18n (>= 0.7, < 2)
|
||||||
railties (>= 5.0, < 6)
|
railties (>= 5.0, < 6)
|
||||||
railties (5.2.2.1)
|
railties (5.2.3)
|
||||||
actionpack (= 5.2.2.1)
|
actionpack (= 5.2.3)
|
||||||
activesupport (= 5.2.2.1)
|
activesupport (= 5.2.3)
|
||||||
method_source
|
method_source
|
||||||
rake (>= 0.8.7)
|
rake (>= 0.8.7)
|
||||||
thor (>= 0.19.0, < 2.0)
|
thor (>= 0.19.0, < 2.0)
|
||||||
|
@ -693,9 +686,9 @@ GEM
|
||||||
activesupport (>= 4.2)
|
activesupport (>= 4.2)
|
||||||
rack-proxy (>= 0.6.1)
|
rack-proxy (>= 0.6.1)
|
||||||
railties (>= 4.2)
|
railties (>= 4.2)
|
||||||
websocket-driver (0.7.0)
|
websocket-driver (0.7.1)
|
||||||
websocket-extensions (>= 0.1.0)
|
websocket-extensions (>= 0.1.0)
|
||||||
websocket-extensions (0.1.3)
|
websocket-extensions (0.1.4)
|
||||||
xpath (3.2.0)
|
xpath (3.2.0)
|
||||||
nokogiri (~> 1.8)
|
nokogiri (~> 1.8)
|
||||||
xray-rails (0.3.1)
|
xray-rails (0.3.1)
|
||||||
|
@ -730,12 +723,9 @@ DEPENDENCIES
|
||||||
capybara-email
|
capybara-email
|
||||||
capybara-screenshot
|
capybara-screenshot
|
||||||
capybara-selenium
|
capybara-selenium
|
||||||
carrierwave
|
|
||||||
carrierwave-i18n
|
|
||||||
chartkick
|
chartkick
|
||||||
chunky_png
|
chunky_png
|
||||||
clamav-client
|
clamav-client
|
||||||
copy_carrierwave_file
|
|
||||||
daemons
|
daemons
|
||||||
database_cleaner
|
database_cleaner
|
||||||
deep_cloneable
|
deep_cloneable
|
||||||
|
|
|
@ -1,3 +1,4 @@
|
||||||
|
@import "colors";
|
||||||
@import "constants";
|
@import "constants";
|
||||||
|
|
||||||
%horizontal-list {
|
%horizontal-list {
|
||||||
|
@ -17,3 +18,10 @@
|
||||||
animation-fill-mode: forwards;
|
animation-fill-mode: forwards;
|
||||||
animation-duration: 0.3s;
|
animation-duration: 0.3s;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
%outline {
|
||||||
|
&:active,
|
||||||
|
&:focus {
|
||||||
|
outline: 3px solid $blue;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
38
app/assets/stylesheets/new_design/add_instructeur.scss
Normal file
38
app/assets/stylesheets/new_design/add_instructeur.scss
Normal file
|
@ -0,0 +1,38 @@
|
||||||
|
@import "constants";
|
||||||
|
@import "colors";
|
||||||
|
|
||||||
|
.instructeur-wrapper {
|
||||||
|
.select-instructeurs {
|
||||||
|
width: 100%;
|
||||||
|
}
|
||||||
|
|
||||||
|
.select2-container--default {
|
||||||
|
.select2-selection--multiple {
|
||||||
|
border: solid 1px $border-grey;
|
||||||
|
|
||||||
|
.select2-selection__choice, // scss-lint:disable SelectorFormat
|
||||||
|
.select2-search--inline {
|
||||||
|
padding: $default-spacer;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
&.select2-container--focus {
|
||||||
|
.select2-selection--multiple {
|
||||||
|
border: 1px solid $blue;
|
||||||
|
box-shadow: 0px 0px 2px 1px $blue;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.select2-results__option { // scss-lint:disable SelectorFormat
|
||||||
|
padding: $default-spacer;
|
||||||
|
}
|
||||||
|
|
||||||
|
.custom-select2-option {
|
||||||
|
.icon {
|
||||||
|
margin-right: $default-spacer;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
|
@ -1,7 +1,10 @@
|
||||||
@import "colors";
|
@import "colors";
|
||||||
@import "constants";
|
@import "constants";
|
||||||
|
@import "placeholders";
|
||||||
|
|
||||||
.button {
|
.button {
|
||||||
|
@extend %outline;
|
||||||
|
|
||||||
display: inline-block;
|
display: inline-block;
|
||||||
padding: 8px 16px;
|
padding: 8px 16px;
|
||||||
border-radius: 30px;
|
border-radius: 30px;
|
||||||
|
@ -20,11 +23,6 @@
|
||||||
text-decoration: none;
|
text-decoration: none;
|
||||||
}
|
}
|
||||||
|
|
||||||
&:active,
|
|
||||||
&:focus {
|
|
||||||
outline: none;
|
|
||||||
}
|
|
||||||
|
|
||||||
&:disabled {
|
&:disabled {
|
||||||
opacity: 0.5;
|
opacity: 0.5;
|
||||||
filter: saturate(50%);
|
filter: saturate(50%);
|
||||||
|
|
|
@ -40,6 +40,10 @@
|
||||||
.card-title {
|
.card-title {
|
||||||
margin-bottom: $default-spacer;
|
margin-bottom: $default-spacer;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
b {
|
||||||
|
font-weight: bold;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
p:not(:last-of-type) {
|
p:not(:last-of-type) {
|
||||||
|
|
|
@ -1,3 +1,6 @@
|
||||||
|
@import "colors";
|
||||||
|
@import "placeholders";
|
||||||
|
|
||||||
html,
|
html,
|
||||||
body {
|
body {
|
||||||
height: 100%;
|
height: 100%;
|
||||||
|
@ -14,5 +17,7 @@ html {
|
||||||
}
|
}
|
||||||
|
|
||||||
a {
|
a {
|
||||||
|
@extend %outline;
|
||||||
|
|
||||||
text-decoration: none;
|
text-decoration: none;
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,5 +1,6 @@
|
||||||
@import "constants";
|
@import "constants";
|
||||||
@import "colors";
|
@import "colors";
|
||||||
|
@import "placeholders";
|
||||||
|
|
||||||
.form {
|
.form {
|
||||||
h1 {
|
h1 {
|
||||||
|
@ -177,6 +178,8 @@
|
||||||
|
|
||||||
input[type=checkbox],
|
input[type=checkbox],
|
||||||
input[type=radio] {
|
input[type=radio] {
|
||||||
|
@extend %outline;
|
||||||
|
|
||||||
margin-left: 5px;
|
margin-left: 5px;
|
||||||
margin-right: 4px;
|
margin-right: 4px;
|
||||||
margin-bottom: 2 * $default-padding;
|
margin-bottom: 2 * $default-padding;
|
||||||
|
|
|
@ -365,6 +365,11 @@ $cta-panel-button-border-size: 2px;
|
||||||
color: #FFFFFF;
|
color: #FFFFFF;
|
||||||
text-decoration: none;
|
text-decoration: none;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
&:active,
|
||||||
|
&:focus {
|
||||||
|
outline: 3px solid #FFFFFF;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.cta-panel-button-blue {
|
.cta-panel-button-blue {
|
||||||
|
|
|
@ -141,6 +141,8 @@ class ApplicationController < ActionController::Base
|
||||||
Raven.user_context(sentry_user)
|
Raven.user_context(sentry_user)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
# private method called by rails fwk
|
||||||
|
# see https://github.com/roidrage/lograge
|
||||||
def append_info_to_payload(payload)
|
def append_info_to_payload(payload)
|
||||||
super
|
super
|
||||||
|
|
||||||
|
|
|
@ -14,7 +14,7 @@ module Instructeurs
|
||||||
end
|
end
|
||||||
|
|
||||||
def add_instructeur
|
def add_instructeur
|
||||||
@instructeur = Instructeur.find_by(email: instructeur_email) ||
|
@instructeur = Instructeur.by_email(instructeur_email) ||
|
||||||
create_instructeur(instructeur_email)
|
create_instructeur(instructeur_email)
|
||||||
|
|
||||||
if groupe_instructeur.instructeurs.include?(@instructeur)
|
if groupe_instructeur.instructeurs.include?(@instructeur)
|
||||||
|
@ -24,7 +24,7 @@ module Instructeurs
|
||||||
groupe_instructeur.instructeurs << @instructeur
|
groupe_instructeur.instructeurs << @instructeur
|
||||||
flash[:notice] = "L’instructeur « #{instructeur_email} » a été affecté au groupe."
|
flash[:notice] = "L’instructeur « #{instructeur_email} » a été affecté au groupe."
|
||||||
GroupeInstructeurMailer
|
GroupeInstructeurMailer
|
||||||
.add_instructeur(groupe_instructeur, @instructeur, current_user.email)
|
.add_instructeurs(groupe_instructeur, [@instructeur], current_user.email)
|
||||||
.deliver_later
|
.deliver_later
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
|
@ -19,5 +19,29 @@ module Manager
|
||||||
redirect_to manager_sign_in_path
|
redirect_to manager_sign_in_path
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
private
|
||||||
|
|
||||||
|
# private method called by rails fwk
|
||||||
|
# see https://github.com/roidrage/lograge
|
||||||
|
def append_info_to_payload(payload)
|
||||||
|
super
|
||||||
|
|
||||||
|
payload.merge!({
|
||||||
|
user_agent: request.user_agent,
|
||||||
|
user_id: current_user&.id,
|
||||||
|
user_email: current_user&.email
|
||||||
|
}.compact)
|
||||||
|
|
||||||
|
if browser.known?
|
||||||
|
payload.merge!({
|
||||||
|
browser: browser.name,
|
||||||
|
browser_version: browser.version.to_s,
|
||||||
|
platform: browser.platform.name
|
||||||
|
})
|
||||||
|
end
|
||||||
|
|
||||||
|
payload
|
||||||
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
|
@ -12,6 +12,7 @@ module NewAdministrateur
|
||||||
@procedure = procedure
|
@procedure = procedure
|
||||||
@groupe_instructeur = groupe_instructeur
|
@groupe_instructeur = groupe_instructeur
|
||||||
@instructeurs = paginated_instructeurs
|
@instructeurs = paginated_instructeurs
|
||||||
|
@available_instructeur_emails = available_instructeur_emails
|
||||||
end
|
end
|
||||||
|
|
||||||
def create
|
def create
|
||||||
|
@ -40,6 +41,7 @@ module NewAdministrateur
|
||||||
else
|
else
|
||||||
@procedure = procedure
|
@procedure = procedure
|
||||||
@instructeurs = paginated_instructeurs
|
@instructeurs = paginated_instructeurs
|
||||||
|
@available_instructeur_emails = available_instructeur_emails
|
||||||
|
|
||||||
flash[:alert] = "le nom « #{label} » est déjà pris par un autre groupe."
|
flash[:alert] = "le nom « #{label} » est déjà pris par un autre groupe."
|
||||||
render :show
|
render :show
|
||||||
|
@ -47,18 +49,35 @@ module NewAdministrateur
|
||||||
end
|
end
|
||||||
|
|
||||||
def add_instructeur
|
def add_instructeur
|
||||||
@instructeur = Instructeur.find_by(email: instructeur_email) ||
|
emails = params['emails'].map(&:strip).map(&:downcase)
|
||||||
create_instructeur(instructeur_email)
|
|
||||||
|
|
||||||
if groupe_instructeur.instructeurs.include?(@instructeur)
|
correct_emails, bad_emails = emails
|
||||||
flash[:alert] = "L’instructeur « #{instructeur_email} » est déjà dans le groupe."
|
.partition { |email| URI::MailTo::EMAIL_REGEXP.match?(email) }
|
||||||
|
|
||||||
|
if bad_emails.present?
|
||||||
|
flash[:alert] = t('.wrong_address',
|
||||||
|
count: bad_emails.count,
|
||||||
|
value: bad_emails.join(', '))
|
||||||
|
end
|
||||||
|
|
||||||
|
email_to_adds = correct_emails - groupe_instructeur.instructeurs.pluck(:email)
|
||||||
|
|
||||||
|
if email_to_adds.present?
|
||||||
|
instructeurs = email_to_adds.map do |instructeur_email|
|
||||||
|
Instructeur.by_email(instructeur_email) ||
|
||||||
|
create_instructeur(instructeur_email)
|
||||||
|
end
|
||||||
|
|
||||||
else
|
|
||||||
groupe_instructeur.instructeurs << @instructeur
|
|
||||||
flash[:notice] = "L’instructeur « #{instructeur_email} » a été affecté au groupe."
|
|
||||||
GroupeInstructeurMailer
|
GroupeInstructeurMailer
|
||||||
.add_instructeur(groupe_instructeur, @instructeur, current_user.email)
|
.add_instructeurs(groupe_instructeur, instructeurs, current_user.email)
|
||||||
.deliver_later
|
.deliver_later
|
||||||
|
|
||||||
|
groupe_instructeur.instructeurs << instructeurs
|
||||||
|
|
||||||
|
flash[:notice] = t('.assignment',
|
||||||
|
count: email_to_adds.count,
|
||||||
|
value: email_to_adds.join(', '),
|
||||||
|
groupe: groupe_instructeur.label)
|
||||||
end
|
end
|
||||||
|
|
||||||
redirect_to procedure_groupe_instructeur_path(procedure, groupe_instructeur)
|
redirect_to procedure_groupe_instructeur_path(procedure, groupe_instructeur)
|
||||||
|
@ -110,10 +129,6 @@ module NewAdministrateur
|
||||||
procedure.groupe_instructeurs.find(params[:id])
|
procedure.groupe_instructeurs.find(params[:id])
|
||||||
end
|
end
|
||||||
|
|
||||||
def instructeur_email
|
|
||||||
params[:instructeur][:email].strip.downcase
|
|
||||||
end
|
|
||||||
|
|
||||||
def instructeur_id
|
def instructeur_id
|
||||||
params[:instructeur][:id]
|
params[:instructeur][:id]
|
||||||
end
|
end
|
||||||
|
@ -141,5 +156,11 @@ module NewAdministrateur
|
||||||
def routing_criteria_name
|
def routing_criteria_name
|
||||||
params[:procedure][:routing_criteria_name]
|
params[:procedure][:routing_criteria_name]
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def available_instructeur_emails
|
||||||
|
all = current_administrateur.instructeurs.pluck(:email)
|
||||||
|
assigned = groupe_instructeur.instructeurs.pluck(:email)
|
||||||
|
(all - assigned).sort
|
||||||
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
|
@ -18,8 +18,7 @@ class ServiceDashboard < Administrate::BaseDashboard
|
||||||
email: Field::String,
|
email: Field::String,
|
||||||
telephone: Field::String,
|
telephone: Field::String,
|
||||||
horaires: Field::String,
|
horaires: Field::String,
|
||||||
adresse: Field::String,
|
adresse: Field::String
|
||||||
siret: Field::String
|
|
||||||
}.freeze
|
}.freeze
|
||||||
|
|
||||||
# COLLECTION_ATTRIBUTES
|
# COLLECTION_ATTRIBUTES
|
||||||
|
@ -45,8 +44,7 @@ class ServiceDashboard < Administrate::BaseDashboard
|
||||||
:email,
|
:email,
|
||||||
:telephone,
|
:telephone,
|
||||||
:horaires,
|
:horaires,
|
||||||
:adresse,
|
:adresse
|
||||||
:siret
|
|
||||||
].freeze
|
].freeze
|
||||||
|
|
||||||
# FORM_ATTRIBUTES
|
# FORM_ATTRIBUTES
|
||||||
|
|
29
app/graphql/mutations/dossier_accepter.rb
Normal file
29
app/graphql/mutations/dossier_accepter.rb
Normal file
|
@ -0,0 +1,29 @@
|
||||||
|
module Mutations
|
||||||
|
class DossierAccepter < Mutations::BaseMutation
|
||||||
|
include DossierHelper
|
||||||
|
|
||||||
|
description "Accepter le dossier."
|
||||||
|
|
||||||
|
argument :dossier_id, ID, "Dossier ID", required: true, loads: Types::DossierType
|
||||||
|
argument :instructeur_id, ID, "Instructeur qui prend la décision sur le dossier.", required: true, loads: Types::ProfileType
|
||||||
|
argument :motivation, String, required: false
|
||||||
|
argument :justificatif, ID, required: false
|
||||||
|
|
||||||
|
field :dossier, Types::DossierType, null: true
|
||||||
|
field :errors, [Types::ValidationErrorType], null: true
|
||||||
|
|
||||||
|
def resolve(dossier:, instructeur:, motivation: nil, justificatif: nil)
|
||||||
|
if dossier.en_instruction?
|
||||||
|
dossier.accepter!(instructeur, motivation, justificatif)
|
||||||
|
|
||||||
|
{ dossier: dossier }
|
||||||
|
else
|
||||||
|
{ errors: ["Le dossier est déjà #{dossier_display_state(dossier, lower: true)}"] }
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
def authorized?(dossier:, instructeur:, motivation: nil)
|
||||||
|
instructeur.is_a?(Instructeur) && instructeur.dossiers.exists?(id: dossier.id)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
29
app/graphql/mutations/dossier_classer_sans_suite.rb
Normal file
29
app/graphql/mutations/dossier_classer_sans_suite.rb
Normal file
|
@ -0,0 +1,29 @@
|
||||||
|
module Mutations
|
||||||
|
class DossierClasserSansSuite < Mutations::BaseMutation
|
||||||
|
include DossierHelper
|
||||||
|
|
||||||
|
description "Classer le dossier sans suite."
|
||||||
|
|
||||||
|
argument :dossier_id, ID, "Dossier ID", required: true, loads: Types::DossierType
|
||||||
|
argument :instructeur_id, ID, "Instructeur qui prend la décision sur le dossier.", required: true, loads: Types::ProfileType
|
||||||
|
argument :motivation, String, required: true
|
||||||
|
argument :justificatif, ID, required: false
|
||||||
|
|
||||||
|
field :dossier, Types::DossierType, null: true
|
||||||
|
field :errors, [Types::ValidationErrorType], null: true
|
||||||
|
|
||||||
|
def resolve(dossier:, instructeur:, motivation:, justificatif: nil)
|
||||||
|
if dossier.en_instruction?
|
||||||
|
dossier.classer_sans_suite!(instructeur, motivation, justificatif)
|
||||||
|
|
||||||
|
{ dossier: dossier }
|
||||||
|
else
|
||||||
|
{ errors: ["Le dossier est déjà #{dossier_display_state(dossier, lower: true)}"] }
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
def authorized?(dossier:, instructeur:, motivation:)
|
||||||
|
instructeur.is_a?(Instructeur) && instructeur.dossiers.exists?(id: dossier.id)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
27
app/graphql/mutations/dossier_envoyer_message.rb
Normal file
27
app/graphql/mutations/dossier_envoyer_message.rb
Normal file
|
@ -0,0 +1,27 @@
|
||||||
|
module Mutations
|
||||||
|
class DossierEnvoyerMessage < Mutations::BaseMutation
|
||||||
|
description "Envoyer un message à l'usager du dossier."
|
||||||
|
|
||||||
|
argument :dossier_id, ID, required: true, loads: Types::DossierType
|
||||||
|
argument :instructeur_id, ID, required: true, loads: Types::ProfileType
|
||||||
|
argument :body, String, required: true
|
||||||
|
argument :attachment, ID, required: false
|
||||||
|
|
||||||
|
field :message, Types::MessageType, null: true
|
||||||
|
field :errors, [Types::ValidationErrorType], null: true
|
||||||
|
|
||||||
|
def resolve(dossier:, instructeur:, body:, attachment: nil)
|
||||||
|
message = CommentaireService.build(instructeur, dossier, body: body, piece_jointe: attachment)
|
||||||
|
|
||||||
|
if message.save
|
||||||
|
{ message: message }
|
||||||
|
else
|
||||||
|
{ errors: message.errors.full_messages }
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
def authorized?(dossier:, instructeur:, body:)
|
||||||
|
instructeur.is_a?(Instructeur) && instructeur.dossiers.exists?(id: dossier.id)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
27
app/graphql/mutations/dossier_passer_en_instruction.rb
Normal file
27
app/graphql/mutations/dossier_passer_en_instruction.rb
Normal file
|
@ -0,0 +1,27 @@
|
||||||
|
module Mutations
|
||||||
|
class DossierPasserEnInstruction < Mutations::BaseMutation
|
||||||
|
include DossierHelper
|
||||||
|
|
||||||
|
description "Passer le dossier en instruction."
|
||||||
|
|
||||||
|
argument :dossier_id, ID, "Dossier ID", required: true, loads: Types::DossierType
|
||||||
|
argument :instructeur_id, ID, "Instructeur qui prend la décision sur le dossier.", required: true, loads: Types::ProfileType
|
||||||
|
|
||||||
|
field :dossier, Types::DossierType, null: true
|
||||||
|
field :errors, [Types::ValidationErrorType], null: true
|
||||||
|
|
||||||
|
def resolve(dossier:, instructeur:)
|
||||||
|
if dossier.en_construction?
|
||||||
|
dossier.passer_en_instruction!(instructeur)
|
||||||
|
|
||||||
|
{ dossier: dossier }
|
||||||
|
else
|
||||||
|
{ errors: ["Le dossier est déjà #{dossier_display_state(dossier, lower: true)}"] }
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
def authorized?(dossier:, instructeur:)
|
||||||
|
instructeur.is_a?(Instructeur) && instructeur.dossiers.exists?(id: dossier.id)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
29
app/graphql/mutations/dossier_refuser.rb
Normal file
29
app/graphql/mutations/dossier_refuser.rb
Normal file
|
@ -0,0 +1,29 @@
|
||||||
|
module Mutations
|
||||||
|
class DossierRefuser < Mutations::BaseMutation
|
||||||
|
include DossierHelper
|
||||||
|
|
||||||
|
description "Refuser le dossier."
|
||||||
|
|
||||||
|
argument :dossier_id, ID, "Dossier ID", required: true, loads: Types::DossierType
|
||||||
|
argument :instructeur_id, ID, "Instructeur qui prend la décision sur le dossier.", required: true, loads: Types::ProfileType
|
||||||
|
argument :motivation, String, required: true
|
||||||
|
argument :justificatif, ID, required: false
|
||||||
|
|
||||||
|
field :dossier, Types::DossierType, null: true
|
||||||
|
field :errors, [Types::ValidationErrorType], null: true
|
||||||
|
|
||||||
|
def resolve(dossier:, instructeur:, motivation:, justificatif: nil)
|
||||||
|
if dossier.en_instruction?
|
||||||
|
dossier.refuser!(instructeur, motivation, justificatif)
|
||||||
|
|
||||||
|
{ dossier: dossier }
|
||||||
|
else
|
||||||
|
{ errors: ["Le dossier est déjà #{dossier_display_state(dossier, lower: true)}"] }
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
def authorized?(dossier:, instructeur:, motivation:)
|
||||||
|
instructeur.is_a?(Instructeur) && instructeur.dossiers.exists?(id: dossier.id)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
|
@ -10,27 +10,66 @@ type Avis {
|
||||||
type CarteChamp implements Champ {
|
type CarteChamp implements Champ {
|
||||||
geoAreas: [GeoArea!]!
|
geoAreas: [GeoArea!]!
|
||||||
id: ID!
|
id: ID!
|
||||||
|
|
||||||
|
"""
|
||||||
|
Libellé du champ.
|
||||||
|
"""
|
||||||
label: String!
|
label: String!
|
||||||
|
|
||||||
|
"""
|
||||||
|
La valeur du champ sous forme texte.
|
||||||
|
"""
|
||||||
stringValue: String
|
stringValue: String
|
||||||
}
|
}
|
||||||
|
|
||||||
interface Champ {
|
interface Champ {
|
||||||
id: ID!
|
id: ID!
|
||||||
|
|
||||||
|
"""
|
||||||
|
Libellé du champ.
|
||||||
|
"""
|
||||||
label: String!
|
label: String!
|
||||||
|
|
||||||
|
"""
|
||||||
|
La valeur du champ sous forme texte.
|
||||||
|
"""
|
||||||
stringValue: String
|
stringValue: String
|
||||||
}
|
}
|
||||||
|
|
||||||
type ChampDescriptor {
|
type ChampDescriptor {
|
||||||
|
"""
|
||||||
|
Description du champ.
|
||||||
|
"""
|
||||||
description: String
|
description: String
|
||||||
id: ID!
|
id: ID!
|
||||||
|
|
||||||
|
"""
|
||||||
|
Libellé du champ.
|
||||||
|
"""
|
||||||
label: String!
|
label: String!
|
||||||
|
|
||||||
|
"""
|
||||||
|
Est-ce que le champ est obligatoire ?
|
||||||
|
"""
|
||||||
required: Boolean!
|
required: Boolean!
|
||||||
|
|
||||||
|
"""
|
||||||
|
Type de la valeur du champ.
|
||||||
|
"""
|
||||||
type: TypeDeChamp!
|
type: TypeDeChamp!
|
||||||
}
|
}
|
||||||
|
|
||||||
type CheckboxChamp implements Champ {
|
type CheckboxChamp implements Champ {
|
||||||
id: ID!
|
id: ID!
|
||||||
|
|
||||||
|
"""
|
||||||
|
Libellé du champ.
|
||||||
|
"""
|
||||||
label: String!
|
label: String!
|
||||||
|
|
||||||
|
"""
|
||||||
|
La valeur du champ sous forme texte.
|
||||||
|
"""
|
||||||
stringValue: String
|
stringValue: String
|
||||||
value: Boolean!
|
value: Boolean!
|
||||||
}
|
}
|
||||||
|
@ -88,14 +127,30 @@ type CreateDirectUploadPayload {
|
||||||
|
|
||||||
type DateChamp implements Champ {
|
type DateChamp implements Champ {
|
||||||
id: ID!
|
id: ID!
|
||||||
|
|
||||||
|
"""
|
||||||
|
Libellé du champ.
|
||||||
|
"""
|
||||||
label: String!
|
label: String!
|
||||||
|
|
||||||
|
"""
|
||||||
|
La valeur du champ sous forme texte.
|
||||||
|
"""
|
||||||
stringValue: String
|
stringValue: String
|
||||||
value: ISO8601DateTime
|
value: ISO8601DateTime
|
||||||
}
|
}
|
||||||
|
|
||||||
type DecimalNumberChamp implements Champ {
|
type DecimalNumberChamp implements Champ {
|
||||||
id: ID!
|
id: ID!
|
||||||
|
|
||||||
|
"""
|
||||||
|
Libellé du champ.
|
||||||
|
"""
|
||||||
label: String!
|
label: String!
|
||||||
|
|
||||||
|
"""
|
||||||
|
La valeur du champ sous forme texte.
|
||||||
|
"""
|
||||||
stringValue: String
|
stringValue: String
|
||||||
value: Float
|
value: Float
|
||||||
}
|
}
|
||||||
|
@ -164,7 +219,11 @@ type Demarche {
|
||||||
"""
|
"""
|
||||||
Le numero de la démarche.
|
Le numero de la démarche.
|
||||||
"""
|
"""
|
||||||
number: ID!
|
number: Int!
|
||||||
|
|
||||||
|
"""
|
||||||
|
L'état de la démarche.
|
||||||
|
"""
|
||||||
state: DemarcheState!
|
state: DemarcheState!
|
||||||
title: String!
|
title: String!
|
||||||
updatedAt: ISO8601DateTime!
|
updatedAt: ISO8601DateTime!
|
||||||
|
@ -244,7 +303,7 @@ type Dossier {
|
||||||
"""
|
"""
|
||||||
Le numero du dossier.
|
Le numero du dossier.
|
||||||
"""
|
"""
|
||||||
number: ID!
|
number: Int!
|
||||||
|
|
||||||
"""
|
"""
|
||||||
L'état du dossier.
|
L'état du dossier.
|
||||||
|
@ -258,6 +317,74 @@ type Dossier {
|
||||||
usager: Profile!
|
usager: Profile!
|
||||||
}
|
}
|
||||||
|
|
||||||
|
"""
|
||||||
|
Autogenerated input type of DossierAccepter
|
||||||
|
"""
|
||||||
|
input DossierAccepterInput {
|
||||||
|
"""
|
||||||
|
A unique identifier for the client performing the mutation.
|
||||||
|
"""
|
||||||
|
clientMutationId: String
|
||||||
|
|
||||||
|
"""
|
||||||
|
Dossier ID
|
||||||
|
"""
|
||||||
|
dossierId: ID!
|
||||||
|
|
||||||
|
"""
|
||||||
|
Instructeur qui prend la décision sur le dossier.
|
||||||
|
"""
|
||||||
|
instructeurId: ID!
|
||||||
|
justificatif: ID
|
||||||
|
motivation: String
|
||||||
|
}
|
||||||
|
|
||||||
|
"""
|
||||||
|
Autogenerated return type of DossierAccepter
|
||||||
|
"""
|
||||||
|
type DossierAccepterPayload {
|
||||||
|
"""
|
||||||
|
A unique identifier for the client performing the mutation.
|
||||||
|
"""
|
||||||
|
clientMutationId: String
|
||||||
|
dossier: Dossier
|
||||||
|
errors: [ValidationError!]
|
||||||
|
}
|
||||||
|
|
||||||
|
"""
|
||||||
|
Autogenerated input type of DossierClasserSansSuite
|
||||||
|
"""
|
||||||
|
input DossierClasserSansSuiteInput {
|
||||||
|
"""
|
||||||
|
A unique identifier for the client performing the mutation.
|
||||||
|
"""
|
||||||
|
clientMutationId: String
|
||||||
|
|
||||||
|
"""
|
||||||
|
Dossier ID
|
||||||
|
"""
|
||||||
|
dossierId: ID!
|
||||||
|
|
||||||
|
"""
|
||||||
|
Instructeur qui prend la décision sur le dossier.
|
||||||
|
"""
|
||||||
|
instructeurId: ID!
|
||||||
|
justificatif: ID
|
||||||
|
motivation: String!
|
||||||
|
}
|
||||||
|
|
||||||
|
"""
|
||||||
|
Autogenerated return type of DossierClasserSansSuite
|
||||||
|
"""
|
||||||
|
type DossierClasserSansSuitePayload {
|
||||||
|
"""
|
||||||
|
A unique identifier for the client performing the mutation.
|
||||||
|
"""
|
||||||
|
clientMutationId: String
|
||||||
|
dossier: Dossier
|
||||||
|
errors: [ValidationError!]
|
||||||
|
}
|
||||||
|
|
||||||
"""
|
"""
|
||||||
The connection type for Dossier.
|
The connection type for Dossier.
|
||||||
"""
|
"""
|
||||||
|
@ -293,13 +420,114 @@ type DossierEdge {
|
||||||
node: Dossier
|
node: Dossier
|
||||||
}
|
}
|
||||||
|
|
||||||
|
"""
|
||||||
|
Autogenerated input type of DossierEnvoyerMessage
|
||||||
|
"""
|
||||||
|
input DossierEnvoyerMessageInput {
|
||||||
|
attachment: ID
|
||||||
|
body: String!
|
||||||
|
|
||||||
|
"""
|
||||||
|
A unique identifier for the client performing the mutation.
|
||||||
|
"""
|
||||||
|
clientMutationId: String
|
||||||
|
dossierId: ID!
|
||||||
|
instructeurId: ID!
|
||||||
|
}
|
||||||
|
|
||||||
|
"""
|
||||||
|
Autogenerated return type of DossierEnvoyerMessage
|
||||||
|
"""
|
||||||
|
type DossierEnvoyerMessagePayload {
|
||||||
|
"""
|
||||||
|
A unique identifier for the client performing the mutation.
|
||||||
|
"""
|
||||||
|
clientMutationId: String
|
||||||
|
errors: [ValidationError!]
|
||||||
|
message: Message
|
||||||
|
}
|
||||||
|
|
||||||
type DossierLinkChamp implements Champ {
|
type DossierLinkChamp implements Champ {
|
||||||
dossier: Dossier
|
dossier: Dossier
|
||||||
id: ID!
|
id: ID!
|
||||||
|
|
||||||
|
"""
|
||||||
|
Libellé du champ.
|
||||||
|
"""
|
||||||
label: String!
|
label: String!
|
||||||
|
|
||||||
|
"""
|
||||||
|
La valeur du champ sous forme texte.
|
||||||
|
"""
|
||||||
stringValue: String
|
stringValue: String
|
||||||
}
|
}
|
||||||
|
|
||||||
|
"""
|
||||||
|
Autogenerated input type of DossierPasserEnInstruction
|
||||||
|
"""
|
||||||
|
input DossierPasserEnInstructionInput {
|
||||||
|
"""
|
||||||
|
A unique identifier for the client performing the mutation.
|
||||||
|
"""
|
||||||
|
clientMutationId: String
|
||||||
|
|
||||||
|
"""
|
||||||
|
Dossier ID
|
||||||
|
"""
|
||||||
|
dossierId: ID!
|
||||||
|
|
||||||
|
"""
|
||||||
|
Instructeur qui prend la décision sur le dossier.
|
||||||
|
"""
|
||||||
|
instructeurId: ID!
|
||||||
|
}
|
||||||
|
|
||||||
|
"""
|
||||||
|
Autogenerated return type of DossierPasserEnInstruction
|
||||||
|
"""
|
||||||
|
type DossierPasserEnInstructionPayload {
|
||||||
|
"""
|
||||||
|
A unique identifier for the client performing the mutation.
|
||||||
|
"""
|
||||||
|
clientMutationId: String
|
||||||
|
dossier: Dossier
|
||||||
|
errors: [ValidationError!]
|
||||||
|
}
|
||||||
|
|
||||||
|
"""
|
||||||
|
Autogenerated input type of DossierRefuser
|
||||||
|
"""
|
||||||
|
input DossierRefuserInput {
|
||||||
|
"""
|
||||||
|
A unique identifier for the client performing the mutation.
|
||||||
|
"""
|
||||||
|
clientMutationId: String
|
||||||
|
|
||||||
|
"""
|
||||||
|
Dossier ID
|
||||||
|
"""
|
||||||
|
dossierId: ID!
|
||||||
|
|
||||||
|
"""
|
||||||
|
Instructeur qui prend la décision sur le dossier.
|
||||||
|
"""
|
||||||
|
instructeurId: ID!
|
||||||
|
justificatif: ID
|
||||||
|
motivation: String!
|
||||||
|
}
|
||||||
|
|
||||||
|
"""
|
||||||
|
Autogenerated return type of DossierRefuser
|
||||||
|
"""
|
||||||
|
type DossierRefuserPayload {
|
||||||
|
"""
|
||||||
|
A unique identifier for the client performing the mutation.
|
||||||
|
"""
|
||||||
|
clientMutationId: String
|
||||||
|
dossier: Dossier
|
||||||
|
errors: [ValidationError!]
|
||||||
|
}
|
||||||
|
|
||||||
enum DossierState {
|
enum DossierState {
|
||||||
"""
|
"""
|
||||||
Accepté
|
Accepté
|
||||||
|
@ -335,22 +563,17 @@ interface GeoArea {
|
||||||
|
|
||||||
enum GeoAreaSource {
|
enum GeoAreaSource {
|
||||||
"""
|
"""
|
||||||
translation missing: fr.activerecord.attributes.geo_area.source.cadastre
|
Parcelle cadastrale
|
||||||
"""
|
"""
|
||||||
cadastre
|
cadastre
|
||||||
|
|
||||||
"""
|
"""
|
||||||
translation missing: fr.activerecord.attributes.geo_area.source.parcelle_agricole
|
Quartier prioritaire
|
||||||
"""
|
|
||||||
parcelle_agricole
|
|
||||||
|
|
||||||
"""
|
|
||||||
translation missing: fr.activerecord.attributes.geo_area.source.quartier_prioritaire
|
|
||||||
"""
|
"""
|
||||||
quartier_prioritaire
|
quartier_prioritaire
|
||||||
|
|
||||||
"""
|
"""
|
||||||
translation missing: fr.activerecord.attributes.geo_area.source.selection_utilisateur
|
Sélection utilisateur
|
||||||
"""
|
"""
|
||||||
selection_utilisateur
|
selection_utilisateur
|
||||||
}
|
}
|
||||||
|
@ -376,16 +599,32 @@ scalar ISO8601DateTime
|
||||||
|
|
||||||
type IntegerNumberChamp implements Champ {
|
type IntegerNumberChamp implements Champ {
|
||||||
id: ID!
|
id: ID!
|
||||||
|
|
||||||
|
"""
|
||||||
|
Libellé du champ.
|
||||||
|
"""
|
||||||
label: String!
|
label: String!
|
||||||
|
|
||||||
|
"""
|
||||||
|
La valeur du champ sous forme texte.
|
||||||
|
"""
|
||||||
stringValue: String
|
stringValue: String
|
||||||
value: Int
|
value: Int
|
||||||
}
|
}
|
||||||
|
|
||||||
type LinkedDropDownListChamp implements Champ {
|
type LinkedDropDownListChamp implements Champ {
|
||||||
id: ID!
|
id: ID!
|
||||||
|
|
||||||
|
"""
|
||||||
|
Libellé du champ.
|
||||||
|
"""
|
||||||
label: String!
|
label: String!
|
||||||
primaryValue: String
|
primaryValue: String
|
||||||
secondaryValue: String
|
secondaryValue: String
|
||||||
|
|
||||||
|
"""
|
||||||
|
La valeur du champ sous forme texte.
|
||||||
|
"""
|
||||||
stringValue: String
|
stringValue: String
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -399,7 +638,15 @@ type Message {
|
||||||
|
|
||||||
type MultipleDropDownListChamp implements Champ {
|
type MultipleDropDownListChamp implements Champ {
|
||||||
id: ID!
|
id: ID!
|
||||||
|
|
||||||
|
"""
|
||||||
|
Libellé du champ.
|
||||||
|
"""
|
||||||
label: String!
|
label: String!
|
||||||
|
|
||||||
|
"""
|
||||||
|
La valeur du champ sous forme texte.
|
||||||
|
"""
|
||||||
stringValue: String
|
stringValue: String
|
||||||
values: [String!]!
|
values: [String!]!
|
||||||
}
|
}
|
||||||
|
@ -409,6 +656,31 @@ type Mutation {
|
||||||
File information required to prepare a direct upload
|
File information required to prepare a direct upload
|
||||||
"""
|
"""
|
||||||
createDirectUpload(input: CreateDirectUploadInput!): CreateDirectUploadPayload
|
createDirectUpload(input: CreateDirectUploadInput!): CreateDirectUploadPayload
|
||||||
|
|
||||||
|
"""
|
||||||
|
Accepter le dossier.
|
||||||
|
"""
|
||||||
|
dossierAccepter(input: DossierAccepterInput!): DossierAccepterPayload
|
||||||
|
|
||||||
|
"""
|
||||||
|
Classer le dossier sans suite.
|
||||||
|
"""
|
||||||
|
dossierClasserSansSuite(input: DossierClasserSansSuiteInput!): DossierClasserSansSuitePayload
|
||||||
|
|
||||||
|
"""
|
||||||
|
Envoyer un message à l'usager du dossier.
|
||||||
|
"""
|
||||||
|
dossierEnvoyerMessage(input: DossierEnvoyerMessageInput!): DossierEnvoyerMessagePayload
|
||||||
|
|
||||||
|
"""
|
||||||
|
Passer le dossier en instruction.
|
||||||
|
"""
|
||||||
|
dossierPasserEnInstruction(input: DossierPasserEnInstructionInput!): DossierPasserEnInstructionPayload
|
||||||
|
|
||||||
|
"""
|
||||||
|
Refuser le dossier.
|
||||||
|
"""
|
||||||
|
dossierRefuser(input: DossierRefuserInput!): DossierRefuserPayload
|
||||||
}
|
}
|
||||||
|
|
||||||
enum Order {
|
enum Order {
|
||||||
|
@ -480,7 +752,15 @@ type PersonneMorale {
|
||||||
|
|
||||||
type PieceJustificativeChamp implements Champ {
|
type PieceJustificativeChamp implements Champ {
|
||||||
id: ID!
|
id: ID!
|
||||||
|
|
||||||
|
"""
|
||||||
|
Libellé du champ.
|
||||||
|
"""
|
||||||
label: String!
|
label: String!
|
||||||
|
|
||||||
|
"""
|
||||||
|
La valeur du champ sous forme texte.
|
||||||
|
"""
|
||||||
stringValue: String
|
stringValue: String
|
||||||
url: URL
|
url: URL
|
||||||
}
|
}
|
||||||
|
@ -507,7 +787,7 @@ type Query {
|
||||||
"""
|
"""
|
||||||
Numéro de la démarche.
|
Numéro de la démarche.
|
||||||
"""
|
"""
|
||||||
number: ID!
|
number: Int!
|
||||||
): Demarche!
|
): Demarche!
|
||||||
|
|
||||||
"""
|
"""
|
||||||
|
@ -517,14 +797,22 @@ type Query {
|
||||||
"""
|
"""
|
||||||
Numéro du dossier.
|
Numéro du dossier.
|
||||||
"""
|
"""
|
||||||
number: ID!
|
number: Int!
|
||||||
): Dossier!
|
): Dossier!
|
||||||
}
|
}
|
||||||
|
|
||||||
type RepetitionChamp implements Champ {
|
type RepetitionChamp implements Champ {
|
||||||
champs: [Champ!]!
|
champs: [Champ!]!
|
||||||
id: ID!
|
id: ID!
|
||||||
|
|
||||||
|
"""
|
||||||
|
Libellé du champ.
|
||||||
|
"""
|
||||||
label: String!
|
label: String!
|
||||||
|
|
||||||
|
"""
|
||||||
|
La valeur du champ sous forme texte.
|
||||||
|
"""
|
||||||
stringValue: String
|
stringValue: String
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -537,13 +825,29 @@ type SelectionUtilisateur implements GeoArea {
|
||||||
type SiretChamp implements Champ {
|
type SiretChamp implements Champ {
|
||||||
etablissement: PersonneMorale
|
etablissement: PersonneMorale
|
||||||
id: ID!
|
id: ID!
|
||||||
|
|
||||||
|
"""
|
||||||
|
Libellé du champ.
|
||||||
|
"""
|
||||||
label: String!
|
label: String!
|
||||||
|
|
||||||
|
"""
|
||||||
|
La valeur du champ sous forme texte.
|
||||||
|
"""
|
||||||
stringValue: String
|
stringValue: String
|
||||||
}
|
}
|
||||||
|
|
||||||
type TextChamp implements Champ {
|
type TextChamp implements Champ {
|
||||||
id: ID!
|
id: ID!
|
||||||
|
|
||||||
|
"""
|
||||||
|
Libellé du champ.
|
||||||
|
"""
|
||||||
label: String!
|
label: String!
|
||||||
|
|
||||||
|
"""
|
||||||
|
La valeur du champ sous forme texte.
|
||||||
|
"""
|
||||||
stringValue: String
|
stringValue: String
|
||||||
value: String
|
value: String
|
||||||
}
|
}
|
||||||
|
@ -688,4 +992,14 @@ enum TypeDeChamp {
|
||||||
"""
|
"""
|
||||||
A valid URL, transported as a string
|
A valid URL, transported as a string
|
||||||
"""
|
"""
|
||||||
scalar URL
|
scalar URL
|
||||||
|
|
||||||
|
"""
|
||||||
|
Éreur de validation
|
||||||
|
"""
|
||||||
|
type ValidationError {
|
||||||
|
"""
|
||||||
|
A description of the error
|
||||||
|
"""
|
||||||
|
message: String!
|
||||||
|
}
|
|
@ -9,9 +9,9 @@ module Types
|
||||||
end
|
end
|
||||||
|
|
||||||
global_id_field :id
|
global_id_field :id
|
||||||
field :type, TypeDeChampType, null: false, method: :type_champ
|
field :type, TypeDeChampType, "Type de la valeur du champ.", null: false, method: :type_champ
|
||||||
field :label, String, null: false, method: :libelle
|
field :label, String, "Libellé du champ.", null: false, method: :libelle
|
||||||
field :description, String, null: true
|
field :description, String, "Description du champ.", null: true
|
||||||
field :required, Boolean, null: false, method: :mandatory?
|
field :required, Boolean, "Est-ce que le champ est obligatoire ?", null: false, method: :mandatory?
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
|
@ -3,8 +3,8 @@ module Types
|
||||||
include Types::BaseInterface
|
include Types::BaseInterface
|
||||||
|
|
||||||
global_id_field :id
|
global_id_field :id
|
||||||
field :label, String, null: false, method: :libelle
|
field :label, String, "Libellé du champ.", null: false, method: :libelle
|
||||||
field :string_value, String, null: true, method: :for_api_v2
|
field :string_value, String, "La valeur du champ sous forme texte.", null: true, method: :for_api_v2
|
||||||
|
|
||||||
definition_methods do
|
definition_methods do
|
||||||
def resolve_type(object, context)
|
def resolve_type(object, context)
|
||||||
|
|
|
@ -9,10 +9,10 @@ module Types
|
||||||
description "Une demarche"
|
description "Une demarche"
|
||||||
|
|
||||||
global_id_field :id
|
global_id_field :id
|
||||||
field :number, ID, "Le numero de la démarche.", null: false, method: :id
|
field :number, Int, "Le numero de la démarche.", null: false, method: :id
|
||||||
field :title, String, null: false, method: :libelle
|
field :title, String, null: false, method: :libelle
|
||||||
field :description, String, "Déscription de la démarche.", null: false
|
field :description, String, "Déscription de la démarche.", null: false
|
||||||
field :state, DemarcheState, null: false
|
field :state, DemarcheState, "L'état de la démarche.", null: false
|
||||||
|
|
||||||
field :created_at, GraphQL::Types::ISO8601DateTime, null: false
|
field :created_at, GraphQL::Types::ISO8601DateTime, null: false
|
||||||
field :updated_at, GraphQL::Types::ISO8601DateTime, null: false
|
field :updated_at, GraphQL::Types::ISO8601DateTime, null: false
|
||||||
|
|
|
@ -9,7 +9,7 @@ module Types
|
||||||
description "Un dossier"
|
description "Un dossier"
|
||||||
|
|
||||||
global_id_field :id
|
global_id_field :id
|
||||||
field :number, ID, "Le numero du dossier.", null: false, method: :id
|
field :number, Int, "Le numero du dossier.", null: false, method: :id
|
||||||
field :state, DossierState, "L'état du dossier.", null: false
|
field :state, DossierState, "L'état du dossier.", null: false
|
||||||
field :updated_at, GraphQL::Types::ISO8601DateTime, "Date de dernière mise à jour.", null: false
|
field :updated_at, GraphQL::Types::ISO8601DateTime, "Date de dernière mise à jour.", null: false
|
||||||
|
|
||||||
|
|
|
@ -4,9 +4,11 @@ module Types
|
||||||
|
|
||||||
class GeoAreaSource < Types::BaseEnum
|
class GeoAreaSource < Types::BaseEnum
|
||||||
GeoArea.sources.each do |symbol_name, string_name|
|
GeoArea.sources.each do |symbol_name, string_name|
|
||||||
value(string_name,
|
if string_name != "parcelle_agricole"
|
||||||
I18n.t(symbol_name, scope: [:activerecord, :attributes, :geo_area, :source]),
|
value(string_name,
|
||||||
value: symbol_name)
|
I18n.t(symbol_name, scope: [:activerecord, :attributes, :geo_area, :source]),
|
||||||
|
value: symbol_name)
|
||||||
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
|
@ -1,5 +1,11 @@
|
||||||
module Types
|
module Types
|
||||||
class MutationType < Types::BaseObject
|
class MutationType < Types::BaseObject
|
||||||
field :create_direct_upload, mutation: Mutations::CreateDirectUpload
|
field :create_direct_upload, mutation: Mutations::CreateDirectUpload
|
||||||
|
|
||||||
|
field :dossier_envoyer_message, mutation: Mutations::DossierEnvoyerMessage
|
||||||
|
field :dossier_passer_en_instruction, mutation: Mutations::DossierPasserEnInstruction
|
||||||
|
field :dossier_classer_sans_suite, mutation: Mutations::DossierClasserSansSuite
|
||||||
|
field :dossier_refuser, mutation: Mutations::DossierRefuser
|
||||||
|
field :dossier_accepter, mutation: Mutations::DossierAccepter
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
|
@ -1,11 +1,11 @@
|
||||||
module Types
|
module Types
|
||||||
class QueryType < Types::BaseObject
|
class QueryType < Types::BaseObject
|
||||||
field :demarche, DemarcheType, null: false, description: "Informations concernant une démarche." do
|
field :demarche, DemarcheType, null: false, description: "Informations concernant une démarche." do
|
||||||
argument :number, ID, "Numéro de la démarche.", required: true
|
argument :number, Int, "Numéro de la démarche.", required: true
|
||||||
end
|
end
|
||||||
|
|
||||||
field :dossier, DossierType, null: false, description: "Informations sur un dossier d'une démarche." do
|
field :dossier, DossierType, null: false, description: "Informations sur un dossier d'une démarche." do
|
||||||
argument :number, ID, "Numéro du dossier.", required: true
|
argument :number, Int, "Numéro du dossier.", required: true
|
||||||
end
|
end
|
||||||
|
|
||||||
def demarche(number:)
|
def demarche(number:)
|
||||||
|
|
11
app/graphql/types/validation_error_type.rb
Normal file
11
app/graphql/types/validation_error_type.rb
Normal file
|
@ -0,0 +1,11 @@
|
||||||
|
module Types
|
||||||
|
class ValidationErrorType < Types::BaseObject
|
||||||
|
description "Éreur de validation"
|
||||||
|
|
||||||
|
field :message, String, "A description of the error", null: false
|
||||||
|
|
||||||
|
def message
|
||||||
|
object
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
|
@ -1,6 +1,13 @@
|
||||||
import $ from 'jquery';
|
import $ from 'jquery';
|
||||||
import 'select2';
|
import 'select2';
|
||||||
|
|
||||||
|
const optionTemplate = email =>
|
||||||
|
$(
|
||||||
|
'<span class="custom-select2-option"><span class="icon person"></span>' +
|
||||||
|
email.text +
|
||||||
|
'</span>'
|
||||||
|
);
|
||||||
|
|
||||||
addEventListener('ds:page:update', () => {
|
addEventListener('ds:page:update', () => {
|
||||||
$('select.select2').select2({
|
$('select.select2').select2({
|
||||||
language: 'fr',
|
language: 'fr',
|
||||||
|
@ -20,4 +27,20 @@ addEventListener('ds:page:update', () => {
|
||||||
maximumSelectionLength: '30',
|
maximumSelectionLength: '30',
|
||||||
width: '300px'
|
width: '300px'
|
||||||
});
|
});
|
||||||
|
|
||||||
|
$('select.select2-limited.select-instructeurs').select2({
|
||||||
|
language: 'fr',
|
||||||
|
dropdownParent: $('.instructeur-wrapper'),
|
||||||
|
placeholder: 'Saisir l’adresse email de l’instructeur',
|
||||||
|
tags: true,
|
||||||
|
tokenSeparators: [',', ' '],
|
||||||
|
templateResult: optionTemplate,
|
||||||
|
templateSelection: function(email) {
|
||||||
|
return $(
|
||||||
|
'<span class="custom-select2-option"><span class="icon person"></span>' +
|
||||||
|
email.text +
|
||||||
|
'</span>'
|
||||||
|
);
|
||||||
|
}
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|
|
@ -1,4 +0,0 @@
|
||||||
module ActiveStorage
|
|
||||||
# activestorage-openstack uses ActiveStorage::FileNotFoundError which only exists in rails 6
|
|
||||||
class FileNotFoundError < StandardError; end
|
|
||||||
end
|
|
|
@ -1,9 +1,9 @@
|
||||||
class GroupeInstructeurMailer < ApplicationMailer
|
class GroupeInstructeurMailer < ApplicationMailer
|
||||||
layout 'mailers/layout'
|
layout 'mailers/layout'
|
||||||
|
|
||||||
def add_instructeur(group, instructeur, current_instructeur_email)
|
def add_instructeurs(group, instructeurs, current_instructeur_email)
|
||||||
@email = instructeur.email
|
@new_instructeur_emails = instructeurs.map(&:email)
|
||||||
@group = group
|
@group = Procedure.last.defaut_groupe_instructeur
|
||||||
@current_instructeur_email = current_instructeur_email
|
@current_instructeur_email = current_instructeur_email
|
||||||
|
|
||||||
subject = "Ajout d’un instructeur dans le groupe \"#{group.label}\""
|
subject = "Ajout d’un instructeur dans le groupe \"#{group.label}\""
|
||||||
|
|
|
@ -10,7 +10,7 @@ class Commentaire < ApplicationRecord
|
||||||
|
|
||||||
has_one_attached :piece_jointe
|
has_one_attached :piece_jointe
|
||||||
|
|
||||||
validates :body, presence: { message: "Votre message ne peut être vide" }
|
validates :body, presence: { message: "ne peut être vide" }
|
||||||
|
|
||||||
default_scope { order(created_at: :asc) }
|
default_scope { order(created_at: :asc) }
|
||||||
scope :updated_since?, -> (date) { where('commentaires.updated_at > ?', date) }
|
scope :updated_since?, -> (date) { where('commentaires.updated_at > ?', date) }
|
||||||
|
|
|
@ -10,7 +10,7 @@ class Individual < ApplicationRecord
|
||||||
GENDER_FEMALE = 'Mme'
|
GENDER_FEMALE = 'Mme'
|
||||||
|
|
||||||
def self.create_from_france_connect(fc_information)
|
def self.create_from_france_connect(fc_information)
|
||||||
create(
|
create!(
|
||||||
nom: fc_information.family_name,
|
nom: fc_information.family_name,
|
||||||
prenom: fc_information.given_name,
|
prenom: fc_information.given_name,
|
||||||
gender: fc_information.gender == 'female' ? GENDER_FEMALE : GENDER_MALE
|
gender: fc_information.gender == 'female' ? GENDER_FEMALE : GENDER_MALE
|
||||||
|
|
|
@ -1,11 +0,0 @@
|
||||||
%p
|
|
||||||
Bonjour,
|
|
||||||
|
|
||||||
%p
|
|
||||||
L’instructeur « #{@email} » a été affecté au groupe « #{@group.label} » par « #{@current_instructeur_email} », en charge de la démarche « #{@group.procedure.libelle} ».
|
|
||||||
|
|
||||||
%p
|
|
||||||
Cliquez sur le lien ci-dessous pour voir la liste des instructeurs de ce groupe :
|
|
||||||
= link_to(@group.label, procedure_groupe_instructeur_url(@group.procedure, @group))
|
|
||||||
|
|
||||||
= render partial: "layouts/mailers/signature"
|
|
|
@ -0,0 +1,11 @@
|
||||||
|
%p
|
||||||
|
Bonjour,
|
||||||
|
|
||||||
|
%p
|
||||||
|
#{t('new_administrateur.groupe_instructeurs.add_instructeur.assignment', count: @new_instructeur_emails.count, value: @new_instructeur_emails.join(', '), groupe: @group.label).chomp('.')} par « #{@current_instructeur_email} », en charge de la démarche « #{@group.procedure.libelle} ».
|
||||||
|
|
||||||
|
%p
|
||||||
|
Cliquez sur le lien ci-dessous pour voir la liste des instructeurs de ce groupe :
|
||||||
|
= link_to(@group.label, procedure_groupe_instructeur_url(@group.procedure, @group))
|
||||||
|
|
||||||
|
= render partial: "layouts/mailers/signature"
|
|
@ -12,7 +12,7 @@
|
||||||
= f.label :routing_criteria_name do
|
= f.label :routing_criteria_name do
|
||||||
Libellé du routage
|
Libellé du routage
|
||||||
%span.notice Ce texte apparaitra sur le formulaire usager comme le libellé d'une liste
|
%span.notice Ce texte apparaitra sur le formulaire usager comme le libellé d'une liste
|
||||||
= f.text_field :routing_criteria_name, placeholder: 'Votre ville', required: true
|
= f.text_field :routing_criteria_name, placeholder: 'ex. Votre ville', required: true
|
||||||
= f.submit 'Renommer', class: 'button primary send'
|
= f.submit 'Renommer', class: 'button primary send'
|
||||||
|
|
||||||
.card
|
.card
|
||||||
|
@ -22,13 +22,13 @@
|
||||||
= f.label :label do
|
= f.label :label do
|
||||||
Ajouter un groupe
|
Ajouter un groupe
|
||||||
%span.notice Ce groupe sera un choix de la liste « #{@procedure.routing_criteria_name} » .
|
%span.notice Ce groupe sera un choix de la liste « #{@procedure.routing_criteria_name} » .
|
||||||
= f.text_field :label, placeholder: 'Ville de Bordeaux', required: true
|
= f.text_field :label, placeholder: 'ex. Ville de Bordeaux', required: true
|
||||||
= f.submit 'Ajouter le groupe', class: 'button primary send'
|
= f.submit 'Ajouter le groupe', class: 'button primary send'
|
||||||
|
|
||||||
%table.table.mt-2
|
%table.table.mt-2
|
||||||
%thead
|
%thead
|
||||||
%tr
|
%tr
|
||||||
%th{ colspan: 2 } Liste des groupes
|
%th{ colspan: 2 }= t(".existing_groupe", count: @groupes_instructeurs.count)
|
||||||
%tbody
|
%tbody
|
||||||
- @groupes_instructeurs.each do |group|
|
- @groupes_instructeurs.each do |group|
|
||||||
%tr
|
%tr
|
||||||
|
|
|
@ -17,24 +17,29 @@
|
||||||
= f.submit 'Renommer', class: 'button primary send'
|
= f.submit 'Renommer', class: 'button primary send'
|
||||||
|
|
||||||
.card
|
.card
|
||||||
.card-title Gestion des instructeurs
|
.card-title Affectation des instructeurs
|
||||||
= form_for :instructeur,
|
= form_for :instructeur,
|
||||||
url: { action: :add_instructeur },
|
url: { action: :add_instructeur },
|
||||||
html: { class: 'form' } do |f|
|
html: { class: 'form' } do |f|
|
||||||
|
|
||||||
= f.label :email do
|
.instructeur-wrapper
|
||||||
Affecter un nouvel instructeur
|
= select_tag :emails,
|
||||||
= f.email_field :email, placeholder: 'marie.dupont@exemple.fr', required: true
|
options_for_select(@available_instructeur_emails),
|
||||||
|
multiple: true,
|
||||||
|
class: 'select-instructeurs select2-limited'
|
||||||
|
|
||||||
= f.submit 'Affecter', class: 'button primary send'
|
= f.submit 'Affecter', class: 'button primary send'
|
||||||
|
|
||||||
%table.table.mt-2
|
%table.table.mt-2
|
||||||
%thead
|
%thead
|
||||||
%tr
|
%tr
|
||||||
%th{ colspan: 2 } Instructeurs affectés
|
%th{ colspan: 2 }= t('.assigned_instructeur', count: @instructeurs.count)
|
||||||
%tbody
|
%tbody
|
||||||
- @instructeurs.each do |instructeur|
|
- @instructeurs.each do |instructeur|
|
||||||
%tr
|
%tr
|
||||||
%td= instructeur.email
|
%td
|
||||||
|
%span.icon.person
|
||||||
|
#{instructeur.email}
|
||||||
%td.actions= button_to 'retirer',
|
%td.actions= button_to 'retirer',
|
||||||
{ action: :remove_instructeur },
|
{ action: :remove_instructeur },
|
||||||
{ method: :delete,
|
{ method: :delete,
|
||||||
|
|
|
@ -7,4 +7,4 @@
|
||||||
- if dossier.messagerie_available?
|
- if dossier.messagerie_available?
|
||||||
= render partial: "shared/dossiers/messages/form", locals: { commentaire: new_commentaire, form_url: form_url }
|
= render partial: "shared/dossiers/messages/form", locals: { commentaire: new_commentaire, form_url: form_url }
|
||||||
- else
|
- else
|
||||||
= render partial: "shared/dossiers/messages/messagerie_disabled", locals: { service: dossier.procedure.service }
|
= render partial: "shared/dossiers/messages/messagerie_disabled", locals: { service: dossier.procedure.service, dossier: dossier }
|
||||||
|
|
|
@ -12,4 +12,11 @@
|
||||||
- horaires = "Horaires : #{formatted_horaires(service.horaires)}"
|
- horaires = "Horaires : #{formatted_horaires(service.horaires)}"
|
||||||
= simple_format(horaires)
|
= simple_format(horaires)
|
||||||
%p
|
%p
|
||||||
= link_to service.email, "mailto:#{service.email}"
|
= mail_to service.email,
|
||||||
|
service.email,
|
||||||
|
subject: "[demarches-simplifiees.fr] Question sur le dossier Nº #{dossier.id} de la démarche Nº #{dossier.procedure.id}",
|
||||||
|
rel: "noopener noreferrer",
|
||||||
|
target: '_blank'
|
||||||
|
|
||||||
|
%p
|
||||||
|
Penser bien à préciser que votre demande concerne le <b>dossier Nº #{dossier.id}</b>.
|
||||||
|
|
|
@ -23,7 +23,6 @@ FOG_OPENSTACK_IDENTITY_API_VERSION=""
|
||||||
FOG_OPENSTACK_REGION=""
|
FOG_OPENSTACK_REGION=""
|
||||||
FOG_DIRECTORY=""
|
FOG_DIRECTORY=""
|
||||||
FOG_ENABLED=""
|
FOG_ENABLED=""
|
||||||
CARRIERWAVE_CACHE_DIR="/tmp/tps-local-cache"
|
|
||||||
DS_PROXY_URL=""
|
DS_PROXY_URL=""
|
||||||
|
|
||||||
FC_PARTICULIER_ID=""
|
FC_PARTICULIER_ID=""
|
||||||
|
|
|
@ -40,7 +40,7 @@ features = [
|
||||||
def database_exists?
|
def database_exists?
|
||||||
ActiveRecord::Base.connection
|
ActiveRecord::Base.connection
|
||||||
true
|
true
|
||||||
rescue ActiveRecord::NoDatabaseError
|
rescue ActiveRecord::NoDatabaseError, PG::ConnectionBad
|
||||||
false
|
false
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
79
config/initializers/graphiql.rb
Normal file
79
config/initializers/graphiql.rb
Normal file
|
@ -0,0 +1,79 @@
|
||||||
|
DEFAULT_QUERY = "# La documentation officielle de la spécification (Anglais) : https://graphql.org/
|
||||||
|
# Une introduction aux concepts et raisons d'être de GraphQL (Français) : https://blog.octo.com/graphql-et-pourquoi-faire/
|
||||||
|
# Le schema GraphQL de demarches-simplifiees.fr : https://demarches-simplifiees-graphql.netlify.com
|
||||||
|
# Le endpoint GraphQL de demarches-simplifiees.fr : https://demarches-simplifiees.fr/api/v2/graphql
|
||||||
|
|
||||||
|
query getDemarche($demarcheNumber: Int!) {
|
||||||
|
demarche(number: $demarcheNumber) {
|
||||||
|
id
|
||||||
|
number
|
||||||
|
title
|
||||||
|
champDescriptors {
|
||||||
|
id
|
||||||
|
type
|
||||||
|
label
|
||||||
|
}
|
||||||
|
dossiers(first: 3) {
|
||||||
|
nodes {
|
||||||
|
id
|
||||||
|
number
|
||||||
|
datePassageEnConstruction
|
||||||
|
datePassageEnInstruction
|
||||||
|
dateTraitement
|
||||||
|
usager {
|
||||||
|
email
|
||||||
|
}
|
||||||
|
champs {
|
||||||
|
id
|
||||||
|
label
|
||||||
|
... on TextChamp {
|
||||||
|
value
|
||||||
|
}
|
||||||
|
... on DecimalNumberChamp {
|
||||||
|
value
|
||||||
|
}
|
||||||
|
... on IntegerNumberChamp {
|
||||||
|
value
|
||||||
|
}
|
||||||
|
... on CheckboxChamp {
|
||||||
|
value
|
||||||
|
}
|
||||||
|
... on DateChamp {
|
||||||
|
value
|
||||||
|
}
|
||||||
|
... on DossierLinkChamp {
|
||||||
|
dossier {
|
||||||
|
id
|
||||||
|
}
|
||||||
|
}
|
||||||
|
... on MultipleDropDownListChamp {
|
||||||
|
values
|
||||||
|
}
|
||||||
|
... on LinkedDropDownListChamp {
|
||||||
|
primaryValue
|
||||||
|
secondaryValue
|
||||||
|
}
|
||||||
|
... on PieceJustificativeChamp {
|
||||||
|
url
|
||||||
|
}
|
||||||
|
... on CarteChamp {
|
||||||
|
geoAreas {
|
||||||
|
source
|
||||||
|
geometry {
|
||||||
|
type
|
||||||
|
coordinates
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
pageInfo {
|
||||||
|
hasNextPage
|
||||||
|
endCursor
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}"
|
||||||
|
|
||||||
|
GraphiQL::Rails.config.initial_query = DEFAULT_QUERY
|
||||||
|
GraphiQL::Rails.config.title = 'demarches-simplifiees.fr'
|
|
@ -2,4 +2,5 @@ fr:
|
||||||
activerecord:
|
activerecord:
|
||||||
attributes:
|
attributes:
|
||||||
commentaire:
|
commentaire:
|
||||||
|
body: 'Votre message'
|
||||||
file: fichier
|
file: fichier
|
||||||
|
|
8
config/locales/models/geo_area/fr.yml
Normal file
8
config/locales/models/geo_area/fr.yml
Normal file
|
@ -0,0 +1,8 @@
|
||||||
|
fr:
|
||||||
|
activerecord:
|
||||||
|
attributes:
|
||||||
|
geo_area:
|
||||||
|
source:
|
||||||
|
cadastre: Parcelle cadastrale
|
||||||
|
quartier_prioritaire: Quartier prioritaire
|
||||||
|
selection_utilisateur: Sélection utilisateur
|
|
@ -0,0 +1,18 @@
|
||||||
|
fr:
|
||||||
|
new_administrateur:
|
||||||
|
groupe_instructeurs:
|
||||||
|
index:
|
||||||
|
existing_groupe:
|
||||||
|
one: "%{count} groupe existe"
|
||||||
|
other: "%{count} groupes existent"
|
||||||
|
show:
|
||||||
|
assigned_instructeur:
|
||||||
|
one: "%{count} instructeur est affecté"
|
||||||
|
other: "%{count} instructeurs sont affectés"
|
||||||
|
add_instructeur:
|
||||||
|
wrong_address:
|
||||||
|
one: "%{value} n'est pas une adresse email valide"
|
||||||
|
other: "%{value} ne sont pas des adresses emails valides"
|
||||||
|
assignment:
|
||||||
|
one: "L’instructeur %{value} a été affecté au groupe « %{groupe} »."
|
||||||
|
other: "Les instructeurs %{value} ont été affectés au groupe « %{groupe} »."
|
|
@ -42,8 +42,6 @@ defaults: &defaults
|
||||||
openstack_identity_api_version: "<%= ENV['FOG_OPENSTACK_IDENTITY_API_VERSION'] %>"
|
openstack_identity_api_version: "<%= ENV['FOG_OPENSTACK_IDENTITY_API_VERSION'] %>"
|
||||||
openstack_region: <%= ENV['FOG_OPENSTACK_REGION'] %>
|
openstack_region: <%= ENV['FOG_OPENSTACK_REGION'] %>
|
||||||
directory: <%= ENV['FOG_DIRECTORY'] %>
|
directory: <%= ENV['FOG_DIRECTORY'] %>
|
||||||
carrierwave:
|
|
||||||
cache_dir: <%= ENV['CARRIERWAVE_CACHE_DIR'] %>
|
|
||||||
mailtrap:
|
mailtrap:
|
||||||
username: <%= ENV['MAILTRAP_USERNAME'] %>
|
username: <%= ENV['MAILTRAP_USERNAME'] %>
|
||||||
password: <%= ENV['MAILTRAP_PASSWORD'] %>
|
password: <%= ENV['MAILTRAP_PASSWORD'] %>
|
||||||
|
@ -84,8 +82,6 @@ test:
|
||||||
key: api_entreprise_test_key
|
key: api_entreprise_test_key
|
||||||
fog:
|
fog:
|
||||||
directory: tps_dev
|
directory: tps_dev
|
||||||
carrierwave:
|
|
||||||
cache_dir: /tmp/tps-test-cache
|
|
||||||
pipedrive:
|
pipedrive:
|
||||||
key: pipedrive_test_key
|
key: pipedrive_test_key
|
||||||
france_connect_particulier:
|
france_connect_particulier:
|
||||||
|
|
5
db/migrate/20191113142816_instructeurs_remove_email.rb
Normal file
5
db/migrate/20191113142816_instructeurs_remove_email.rb
Normal file
|
@ -0,0 +1,5 @@
|
||||||
|
class InstructeursRemoveEmail < ActiveRecord::Migration[5.2]
|
||||||
|
def change
|
||||||
|
remove_column :instructeurs, :email
|
||||||
|
end
|
||||||
|
end
|
|
@ -10,7 +10,7 @@
|
||||||
#
|
#
|
||||||
# It's strongly recommended that you check this file into your version control system.
|
# It's strongly recommended that you check this file into your version control system.
|
||||||
|
|
||||||
ActiveRecord::Schema.define(version: 2019_10_24_150452) do
|
ActiveRecord::Schema.define(version: 2019_11_13_142816) do
|
||||||
|
|
||||||
# These are extensions that must be enabled in order to support this database
|
# These are extensions that must be enabled in order to support this database
|
||||||
enable_extension "plpgsql"
|
enable_extension "plpgsql"
|
||||||
|
@ -414,12 +414,10 @@ ActiveRecord::Schema.define(version: 2019_10_24_150452) do
|
||||||
end
|
end
|
||||||
|
|
||||||
create_table "instructeurs", id: :serial, force: :cascade do |t|
|
create_table "instructeurs", id: :serial, force: :cascade do |t|
|
||||||
t.string "email", default: "", null: false
|
|
||||||
t.datetime "created_at"
|
t.datetime "created_at"
|
||||||
t.datetime "updated_at"
|
t.datetime "updated_at"
|
||||||
t.text "encrypted_login_token"
|
t.text "encrypted_login_token"
|
||||||
t.datetime "login_token_created_at"
|
t.datetime "login_token_created_at"
|
||||||
t.index ["email"], name: "index_instructeurs_on_email"
|
|
||||||
end
|
end
|
||||||
|
|
||||||
create_table "invites", id: :serial, force: :cascade do |t|
|
create_table "invites", id: :serial, force: :cascade do |t|
|
||||||
|
|
|
@ -1,51 +0,0 @@
|
||||||
require Rails.root.join("lib", "tasks", "task_helper")
|
|
||||||
|
|
||||||
namespace :'2017_10_30_copy_commentaire_piece_justificative_to_file' do
|
|
||||||
task set: :environment do
|
|
||||||
commentaires_to_process = Commentaire.where(file: nil).where.not(piece_justificative_id: nil).reorder(id: :desc)
|
|
||||||
|
|
||||||
rake_puts "#{commentaires_to_process.count} commentaires to process..."
|
|
||||||
|
|
||||||
commentaires_to_process.each do |c|
|
|
||||||
process_commentaire(c)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
task fix: :environment do
|
|
||||||
commentaires_to_fix = Commentaire.where.not(file: nil).where.not(piece_justificative_id: nil).reorder(id: :desc)
|
|
||||||
|
|
||||||
rake_puts "#{commentaires_to_fix.count} commentaires to fix..."
|
|
||||||
|
|
||||||
commentaires_to_fix.each do |c|
|
|
||||||
process_commentaire(c)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
def sanitize_name(name) # from https://github.com/carrierwaveuploader/carrierwave/blob/master/lib/carrierwave/sanitized_file.rb#L323
|
|
||||||
name = name.gsub(/[^[:word:]\.\-\+]/, "_")
|
|
||||||
name = "_#{name}" if name.match?(/\A\.+\z/)
|
|
||||||
name = "unnamed" if name.empty?
|
|
||||||
return name.mb_chars.to_s
|
|
||||||
end
|
|
||||||
|
|
||||||
def process_commentaire(commentaire)
|
|
||||||
rake_puts "Processing commentaire #{commentaire.id}"
|
|
||||||
if commentaire.piece_justificative.present?
|
|
||||||
# https://github.com/carrierwaveuploader/carrierwave#uploading-files-from-a-remote-location
|
|
||||||
commentaire.remote_file_url = commentaire.piece_justificative.content_url
|
|
||||||
|
|
||||||
if commentaire.piece_justificative.original_filename.present?
|
|
||||||
commentaire.file.define_singleton_method(:filename) { sanitize_name(commentaire.piece_justificative.original_filename) }
|
|
||||||
end
|
|
||||||
|
|
||||||
if commentaire.body.blank?
|
|
||||||
commentaire.body = commentaire.piece_justificative.original_filename || "."
|
|
||||||
end
|
|
||||||
|
|
||||||
commentaire.save
|
|
||||||
if commentaire.file.blank?
|
|
||||||
rake_puts "Failed to save file for commentaire #{commentaire.id}"
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
|
@ -1,113 +0,0 @@
|
||||||
require Rails.root.join("lib", "tasks", "task_helper")
|
|
||||||
|
|
||||||
namespace :cloudstorage do
|
|
||||||
task init: :environment do
|
|
||||||
os_config = (YAML.load_file(Fog.credentials_path))['default']
|
|
||||||
@os = OpenStack::Connection.create(
|
|
||||||
{
|
|
||||||
username: os_config['openstack_username'],
|
|
||||||
api_key: os_config['openstack_api_key'],
|
|
||||||
auth_method: "password",
|
|
||||||
auth_url: "https://auth.cloud.ovh.net/v2.0/",
|
|
||||||
authtenant_name: os_config['openstack_tenant'],
|
|
||||||
service_type: "object-store",
|
|
||||||
region: os_config['openstack_region']
|
|
||||||
}
|
|
||||||
)
|
|
||||||
@cont = @os.container(CarrierWave::Uploader::Base.fog_directory)
|
|
||||||
end
|
|
||||||
|
|
||||||
desc 'Move local attestations on cloud storage'
|
|
||||||
task migrate: :environment do
|
|
||||||
puts 'Starting migration'
|
|
||||||
|
|
||||||
Rake::Task['cloudstorage:init'].invoke
|
|
||||||
|
|
||||||
error_count = 0
|
|
||||||
[Cerfa, PieceJustificative, Procedure].each do |c|
|
|
||||||
c.all.each do |entry|
|
|
||||||
content = (c == Procedure) ? entry.logo : entry.content
|
|
||||||
if !(content.current_path.nil? || File.exist?(File.dirname(content.current_path) + '/uploaded'))
|
|
||||||
secure_token = SecureRandom.uuid
|
|
||||||
filename = "#{entry.class.to_s.underscore}-#{secure_token}#{File.extname(content.current_path)}"
|
|
||||||
rake_puts "Uploading #{content.current_path}"
|
|
||||||
begin
|
|
||||||
@cont.create_object(filename, {}, File.open(content.current_path))
|
|
||||||
|
|
||||||
File.open(File.dirname(content.current_path) + '/uploaded', "w+") { |f| f.write(File.basename(content.current_path)) }
|
|
||||||
File.open(File.dirname(content.current_path) + '/filename_cloudstorage', "w+") { |f| f.write(filename) }
|
|
||||||
File.open(File.dirname(content.current_path) + '/secure_token_cloudstorage', "w+") { |f| f.write(secure_token) }
|
|
||||||
|
|
||||||
entry.update_column(c == Procedure ? :logo : :content, filename)
|
|
||||||
entry.update_column(c == Procedure ? :logo_secure_token : :content_secure_token, secure_token)
|
|
||||||
rescue Errno::ENOENT
|
|
||||||
rake_puts "ERROR: #{content.current_path} does not exist!"
|
|
||||||
File.open('upload_errors.report', "a+") { |f| f.write(content.current_path) }
|
|
||||||
error_count += 1
|
|
||||||
end
|
|
||||||
else
|
|
||||||
if content.current_path.present? && File.exist?(File.dirname(content.current_path) + '/uploaded')
|
|
||||||
filename = File.open(File.dirname(content.current_path) + '/filename_cloudstorage', "r").read
|
|
||||||
secure_token = File.open(File.dirname(content.current_path) + '/secure_token_cloudstorage', "r").read
|
|
||||||
|
|
||||||
entry.update_column(c == Procedure ? :logo : :content, filename)
|
|
||||||
entry.update_column(c == Procedure ? :logo_secure_token : :content_secure_token, secure_token)
|
|
||||||
|
|
||||||
rake_puts "RESTORE IN DATABASE: #{filename} "
|
|
||||||
elsif content.current_path.present?
|
|
||||||
rake_puts "Skipping #{content.current_path}"
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
rake_puts "There were #{error_count} errors while uploading files. See upload_errors.report file for details."
|
|
||||||
puts 'Enf of migration'
|
|
||||||
end
|
|
||||||
|
|
||||||
desc 'Clear documents in tenant and revert file entries in database'
|
|
||||||
task :revert do
|
|
||||||
Rake::Task['cloudstorage:init'].invoke
|
|
||||||
|
|
||||||
[Cerfa, PieceJustificative, Procedure].each do |c|
|
|
||||||
c.all.each do |entry|
|
|
||||||
content = (c == Procedure) ? entry.logo : entry.content
|
|
||||||
if content.current_path.present?
|
|
||||||
if File.exist?(File.dirname(content.current_path) + '/uploaded')
|
|
||||||
previous_filename = File.read(File.dirname(content.current_path) + '/uploaded')
|
|
||||||
|
|
||||||
entry.update_column(c == Procedure ? :logo : :content, previous_filename)
|
|
||||||
entry.update_column(c == Procedure ? :logo_secure_token : :content_secure_token, nil)
|
|
||||||
|
|
||||||
rake_puts "restoring #{content.current_path} db data to #{previous_filename}"
|
|
||||||
|
|
||||||
@cont.delete_object(File.open(File.dirname(content.current_path) + '/filename_cloudstorage', "r").read)
|
|
||||||
|
|
||||||
FileUtils.rm(File.dirname(content.current_path) + '/uploaded')
|
|
||||||
FileUtils.rm(File.dirname(content.current_path) + '/filename_cloudstorage')
|
|
||||||
FileUtils.rm(File.dirname(content.current_path) + '/secure_token_cloudstorage')
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
desc 'Clear old documents in tenant'
|
|
||||||
task :clear do
|
|
||||||
Rake::Task['cloudstorage:init'].invoke
|
|
||||||
|
|
||||||
@cont.objects.each do |object|
|
|
||||||
rake_puts "Removing #{object}"
|
|
||||||
@cont.delete_object(object)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
task :clear_old_objects do
|
|
||||||
Rake::Task['cloudstorage:init'].invoke
|
|
||||||
|
|
||||||
@cont.objects_detail.each do |object, details|
|
|
||||||
last_modified = Time.zone.parse(details[:last_modified])
|
|
||||||
@cont.delete_object(object) if last_modified.utc <= (Time.zone.now - 2.years).utc
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
|
@ -76,7 +76,7 @@ describe API::V2::GraphqlController do
|
||||||
expect(gql_errors).to eq(nil)
|
expect(gql_errors).to eq(nil)
|
||||||
expect(gql_data).to eq(demarche: {
|
expect(gql_data).to eq(demarche: {
|
||||||
id: procedure.to_typed_id,
|
id: procedure.to_typed_id,
|
||||||
number: procedure.id.to_s,
|
number: procedure.id,
|
||||||
title: procedure.libelle,
|
title: procedure.libelle,
|
||||||
description: procedure.description,
|
description: procedure.description,
|
||||||
state: 'brouillon',
|
state: 'brouillon',
|
||||||
|
@ -123,7 +123,7 @@ describe API::V2::GraphqlController do
|
||||||
expect(gql_errors).to eq(nil)
|
expect(gql_errors).to eq(nil)
|
||||||
expect(gql_data).to eq(demarche: {
|
expect(gql_data).to eq(demarche: {
|
||||||
id: procedure.to_typed_id,
|
id: procedure.to_typed_id,
|
||||||
number: procedure.id.to_s,
|
number: procedure.id,
|
||||||
dossiers: {
|
dossiers: {
|
||||||
nodes: [{ id: dossier1.to_typed_id }, { id: dossier.to_typed_id }]
|
nodes: [{ id: dossier1.to_typed_id }, { id: dossier.to_typed_id }]
|
||||||
}
|
}
|
||||||
|
@ -177,7 +177,7 @@ describe API::V2::GraphqlController do
|
||||||
expect(gql_errors).to eq(nil)
|
expect(gql_errors).to eq(nil)
|
||||||
expect(gql_data).to eq(dossier: {
|
expect(gql_data).to eq(dossier: {
|
||||||
id: dossier.to_typed_id,
|
id: dossier.to_typed_id,
|
||||||
number: dossier.id.to_s,
|
number: dossier.id,
|
||||||
state: 'en_construction',
|
state: 'en_construction',
|
||||||
updatedAt: dossier.updated_at.iso8601,
|
updatedAt: dossier.updated_at.iso8601,
|
||||||
datePassageEnConstruction: dossier.en_construction_at.iso8601,
|
datePassageEnConstruction: dossier.en_construction_at.iso8601,
|
||||||
|
@ -216,7 +216,310 @@ describe API::V2::GraphqlController do
|
||||||
end
|
end
|
||||||
|
|
||||||
context "mutations" do
|
context "mutations" do
|
||||||
context 'createDirectUpload' do
|
describe 'dossierEnvoyerMessage' do
|
||||||
|
context 'success' do
|
||||||
|
let(:query) do
|
||||||
|
"mutation {
|
||||||
|
dossierEnvoyerMessage(input: {
|
||||||
|
dossierId: \"#{dossier.to_typed_id}\",
|
||||||
|
instructeurId: \"#{instructeur.to_typed_id}\",
|
||||||
|
body: \"Bonjour\"
|
||||||
|
}) {
|
||||||
|
message {
|
||||||
|
body
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}"
|
||||||
|
end
|
||||||
|
|
||||||
|
it "should post a message" do
|
||||||
|
expect(gql_errors).to eq(nil)
|
||||||
|
|
||||||
|
expect(gql_data).to eq(dossierEnvoyerMessage: {
|
||||||
|
message: {
|
||||||
|
body: "Bonjour"
|
||||||
|
}
|
||||||
|
})
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
context 'schema error' do
|
||||||
|
let(:query) do
|
||||||
|
"mutation {
|
||||||
|
dossierEnvoyerMessage(input: {
|
||||||
|
dossierId: \"#{dossier.to_typed_id}\",
|
||||||
|
instructeurId: \"#{instructeur.to_typed_id}\"
|
||||||
|
}) {
|
||||||
|
message {
|
||||||
|
body
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}"
|
||||||
|
end
|
||||||
|
|
||||||
|
it "should fail" do
|
||||||
|
expect(gql_data).to eq(nil)
|
||||||
|
expect(gql_errors).not_to eq(nil)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
context 'validation error' do
|
||||||
|
let(:query) do
|
||||||
|
"mutation {
|
||||||
|
dossierEnvoyerMessage(input: {
|
||||||
|
dossierId: \"#{dossier.to_typed_id}\",
|
||||||
|
instructeurId: \"#{instructeur.to_typed_id}\",
|
||||||
|
body: \"\"
|
||||||
|
}) {
|
||||||
|
message {
|
||||||
|
body
|
||||||
|
}
|
||||||
|
errors {
|
||||||
|
message
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}"
|
||||||
|
end
|
||||||
|
|
||||||
|
it "should fail" do
|
||||||
|
expect(gql_errors).to eq(nil)
|
||||||
|
expect(gql_data).to eq(dossierEnvoyerMessage: {
|
||||||
|
errors: [{ message: "Votre message ne peut être vide" }],
|
||||||
|
message: nil
|
||||||
|
})
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
describe 'dossierPasserEnInstruction' do
|
||||||
|
let(:dossier) { create(:dossier, :en_construction, procedure: procedure) }
|
||||||
|
let(:query) do
|
||||||
|
"mutation {
|
||||||
|
dossierPasserEnInstruction(input: {
|
||||||
|
dossierId: \"#{dossier.to_typed_id}\",
|
||||||
|
instructeurId: \"#{instructeur.to_typed_id}\"
|
||||||
|
}) {
|
||||||
|
dossier {
|
||||||
|
id
|
||||||
|
state
|
||||||
|
motivation
|
||||||
|
}
|
||||||
|
errors {
|
||||||
|
message
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}"
|
||||||
|
end
|
||||||
|
|
||||||
|
context 'success' do
|
||||||
|
it "should passer en instruction dossier" do
|
||||||
|
expect(gql_errors).to eq(nil)
|
||||||
|
|
||||||
|
expect(gql_data).to eq(dossierPasserEnInstruction: {
|
||||||
|
dossier: {
|
||||||
|
id: dossier.to_typed_id,
|
||||||
|
state: "en_instruction",
|
||||||
|
motivation: nil
|
||||||
|
},
|
||||||
|
errors: nil
|
||||||
|
})
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
context 'validation error' do
|
||||||
|
let(:dossier) { create(:dossier, :en_instruction, procedure: procedure) }
|
||||||
|
|
||||||
|
it "should fail" do
|
||||||
|
expect(gql_errors).to eq(nil)
|
||||||
|
expect(gql_data).to eq(dossierPasserEnInstruction: {
|
||||||
|
errors: [{ message: "Le dossier est déjà en instruction" }],
|
||||||
|
dossier: nil
|
||||||
|
})
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
describe 'dossierClasserSansSuite' do
|
||||||
|
let(:dossier) { create(:dossier, :en_instruction, procedure: procedure) }
|
||||||
|
let(:query) do
|
||||||
|
"mutation {
|
||||||
|
dossierClasserSansSuite(input: {
|
||||||
|
dossierId: \"#{dossier.to_typed_id}\",
|
||||||
|
instructeurId: \"#{instructeur.to_typed_id}\",
|
||||||
|
motivation: \"Parce que\"
|
||||||
|
}) {
|
||||||
|
dossier {
|
||||||
|
id
|
||||||
|
state
|
||||||
|
motivation
|
||||||
|
}
|
||||||
|
errors {
|
||||||
|
message
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}"
|
||||||
|
end
|
||||||
|
|
||||||
|
context 'success' do
|
||||||
|
it "should classer sans suite dossier" do
|
||||||
|
expect(gql_errors).to eq(nil)
|
||||||
|
|
||||||
|
expect(gql_data).to eq(dossierClasserSansSuite: {
|
||||||
|
dossier: {
|
||||||
|
id: dossier.to_typed_id,
|
||||||
|
state: "sans_suite",
|
||||||
|
motivation: "Parce que"
|
||||||
|
},
|
||||||
|
errors: nil
|
||||||
|
})
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
context 'validation error' do
|
||||||
|
let(:dossier) { create(:dossier, :accepte, procedure: procedure) }
|
||||||
|
|
||||||
|
it "should fail" do
|
||||||
|
expect(gql_errors).to eq(nil)
|
||||||
|
expect(gql_data).to eq(dossierClasserSansSuite: {
|
||||||
|
errors: [{ message: "Le dossier est déjà accepté" }],
|
||||||
|
dossier: nil
|
||||||
|
})
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
describe 'dossierRefuser' do
|
||||||
|
let(:dossier) { create(:dossier, :en_instruction, procedure: procedure) }
|
||||||
|
let(:query) do
|
||||||
|
"mutation {
|
||||||
|
dossierRefuser(input: {
|
||||||
|
dossierId: \"#{dossier.to_typed_id}\",
|
||||||
|
instructeurId: \"#{instructeur.to_typed_id}\",
|
||||||
|
motivation: \"Parce que\"
|
||||||
|
}) {
|
||||||
|
dossier {
|
||||||
|
id
|
||||||
|
state
|
||||||
|
motivation
|
||||||
|
}
|
||||||
|
errors {
|
||||||
|
message
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}"
|
||||||
|
end
|
||||||
|
|
||||||
|
context 'success' do
|
||||||
|
it "should refuser dossier" do
|
||||||
|
expect(gql_errors).to eq(nil)
|
||||||
|
|
||||||
|
expect(gql_data).to eq(dossierRefuser: {
|
||||||
|
dossier: {
|
||||||
|
id: dossier.to_typed_id,
|
||||||
|
state: "refuse",
|
||||||
|
motivation: "Parce que"
|
||||||
|
},
|
||||||
|
errors: nil
|
||||||
|
})
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
context 'validation error' do
|
||||||
|
let(:dossier) { create(:dossier, :sans_suite, procedure: procedure) }
|
||||||
|
|
||||||
|
it "should fail" do
|
||||||
|
expect(gql_errors).to eq(nil)
|
||||||
|
expect(gql_data).to eq(dossierRefuser: {
|
||||||
|
errors: [{ message: "Le dossier est déjà sans suite" }],
|
||||||
|
dossier: nil
|
||||||
|
})
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
describe 'dossierAccepter' do
|
||||||
|
let(:dossier) { create(:dossier, :en_instruction, procedure: procedure) }
|
||||||
|
let(:query) do
|
||||||
|
"mutation {
|
||||||
|
dossierAccepter(input: {
|
||||||
|
dossierId: \"#{dossier.to_typed_id}\",
|
||||||
|
instructeurId: \"#{instructeur.to_typed_id}\",
|
||||||
|
motivation: \"Parce que\"
|
||||||
|
}) {
|
||||||
|
dossier {
|
||||||
|
id
|
||||||
|
state
|
||||||
|
motivation
|
||||||
|
}
|
||||||
|
errors {
|
||||||
|
message
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}"
|
||||||
|
end
|
||||||
|
|
||||||
|
context 'success' do
|
||||||
|
it "should accepter dossier" do
|
||||||
|
expect(gql_errors).to eq(nil)
|
||||||
|
|
||||||
|
expect(gql_data).to eq(dossierAccepter: {
|
||||||
|
dossier: {
|
||||||
|
id: dossier.to_typed_id,
|
||||||
|
state: "accepte",
|
||||||
|
motivation: "Parce que"
|
||||||
|
},
|
||||||
|
errors: nil
|
||||||
|
})
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
context 'success without motivation' do
|
||||||
|
let(:query) do
|
||||||
|
"mutation {
|
||||||
|
dossierAccepter(input: {
|
||||||
|
dossierId: \"#{dossier.to_typed_id}\",
|
||||||
|
instructeurId: \"#{instructeur.to_typed_id}\"
|
||||||
|
}) {
|
||||||
|
dossier {
|
||||||
|
id
|
||||||
|
state
|
||||||
|
motivation
|
||||||
|
}
|
||||||
|
errors {
|
||||||
|
message
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}"
|
||||||
|
end
|
||||||
|
|
||||||
|
it "should accepter dossier" do
|
||||||
|
expect(gql_errors).to eq(nil)
|
||||||
|
|
||||||
|
expect(gql_data).to eq(dossierAccepter: {
|
||||||
|
dossier: {
|
||||||
|
id: dossier.to_typed_id,
|
||||||
|
state: "accepte",
|
||||||
|
motivation: nil
|
||||||
|
},
|
||||||
|
errors: nil
|
||||||
|
})
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
context 'validation error' do
|
||||||
|
let(:dossier) { create(:dossier, :refuse, procedure: procedure) }
|
||||||
|
|
||||||
|
it "should fail" do
|
||||||
|
expect(gql_errors).to eq(nil)
|
||||||
|
expect(gql_data).to eq(dossierAccepter: {
|
||||||
|
errors: [{ message: "Le dossier est déjà refusé" }],
|
||||||
|
dossier: nil
|
||||||
|
})
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
describe 'createDirectUpload' do
|
||||||
let(:query) do
|
let(:query) do
|
||||||
"mutation {
|
"mutation {
|
||||||
createDirectUpload(input: {
|
createDirectUpload(input: {
|
||||||
|
@ -263,5 +566,26 @@ describe API::V2::GraphqlController do
|
||||||
expect(gql_errors).not_to eq(nil)
|
expect(gql_errors).not_to eq(nil)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
context "mutation" do
|
||||||
|
let(:query) do
|
||||||
|
"mutation {
|
||||||
|
dossierEnvoyerMessage(input: {
|
||||||
|
dossierId: \"#{dossier.to_typed_id}\",
|
||||||
|
instructeurId: \"#{instructeur.to_typed_id}\",
|
||||||
|
body: \"Bonjour\"
|
||||||
|
}) {
|
||||||
|
message {
|
||||||
|
body
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}"
|
||||||
|
end
|
||||||
|
|
||||||
|
it "should return error" do
|
||||||
|
expect(gql_data[:dossierEnvoyerMessage]).to eq(nil)
|
||||||
|
expect(gql_errors).not_to eq(nil)
|
||||||
|
end
|
||||||
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
22
spec/controllers/manager/application_controller_spec.rb
Normal file
22
spec/controllers/manager/application_controller_spec.rb
Normal file
|
@ -0,0 +1,22 @@
|
||||||
|
describe Manager::ApplicationController, type: :controller do
|
||||||
|
describe 'append_info_to_payload' do
|
||||||
|
let(:current_user) { create(:administration) }
|
||||||
|
let(:payload) { {} }
|
||||||
|
|
||||||
|
before do
|
||||||
|
allow(@controller).to receive(:current_user).and_return(current_user)
|
||||||
|
@controller.send(:append_info_to_payload, payload)
|
||||||
|
end
|
||||||
|
|
||||||
|
it do
|
||||||
|
[:db_runtime, :view_runtime, :variant, :rendered_format].each do |key|
|
||||||
|
payload.delete(key)
|
||||||
|
end
|
||||||
|
expect(payload).to eq({
|
||||||
|
user_agent: 'Rails Testing',
|
||||||
|
user_id: current_user.id,
|
||||||
|
user_email: current_user.email
|
||||||
|
})
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
|
@ -93,20 +93,26 @@ describe NewAdministrateur::GroupeInstructeursController, type: :controller do
|
||||||
params: {
|
params: {
|
||||||
procedure_id: procedure.id,
|
procedure_id: procedure.id,
|
||||||
id: gi_1_1.id,
|
id: gi_1_1.id,
|
||||||
instructeur: { email: new_instructeur_email }
|
emails: new_instructeur_emails
|
||||||
}
|
}
|
||||||
end
|
end
|
||||||
|
|
||||||
context 'of a new instructeur' do
|
context 'of a news instructeurs' do
|
||||||
let(:new_instructeur_email) { 'new_instructeur@mail.com' }
|
let(:new_instructeur_emails) { ['new_i1@mail.com', 'new_i2@mail.com'] }
|
||||||
|
|
||||||
it { expect(gi_1_1.instructeurs.map(&:email)).to include(new_instructeur_email) }
|
it { expect(gi_1_1.instructeurs.pluck(:email)).to include(*new_instructeur_emails) }
|
||||||
it { expect(flash.notice).to be_present }
|
it { expect(flash.notice).to be_present }
|
||||||
it { expect(response).to redirect_to(procedure_groupe_instructeur_path(procedure, gi_1_1)) }
|
it { expect(response).to redirect_to(procedure_groupe_instructeur_path(procedure, gi_1_1)) }
|
||||||
end
|
end
|
||||||
|
|
||||||
context 'of an instructeur already in the group' do
|
context 'of an instructeur already in the group' do
|
||||||
let(:new_instructeur_email) { instructeur.email }
|
let(:new_instructeur_emails) { [instructeur.email] }
|
||||||
|
|
||||||
|
it { expect(response).to redirect_to(procedure_groupe_instructeur_path(procedure, procedure.defaut_groupe_instructeur)) }
|
||||||
|
end
|
||||||
|
|
||||||
|
context 'of badly formed email' do
|
||||||
|
let(:new_instructeur_emails) { ['badly_formed_email'] }
|
||||||
|
|
||||||
it { expect(flash.alert).to be_present }
|
it { expect(flash.alert).to be_present }
|
||||||
it { expect(response).to redirect_to(procedure_groupe_instructeur_path(procedure, procedure.defaut_groupe_instructeur)) }
|
it { expect(response).to redirect_to(procedure_groupe_instructeur_path(procedure, procedure.defaut_groupe_instructeur)) }
|
||||||
|
|
|
@ -1,13 +1,16 @@
|
||||||
require 'spec_helper'
|
require 'spec_helper'
|
||||||
|
|
||||||
feature 'The routing' do
|
feature 'The routing', js: true do
|
||||||
let(:password) { 'a very complicated password' }
|
let(:password) { 'a very complicated password' }
|
||||||
let(:procedure) { create(:procedure, :with_type_de_champ, :with_service, :for_individual) }
|
let(:procedure) { create(:procedure, :with_type_de_champ, :with_service, :for_individual) }
|
||||||
let(:administrateur) { create(:administrateur, procedures: [procedure]) }
|
let(:administrateur) { create(:administrateur, procedures: [procedure]) }
|
||||||
let(:scientifique_user) { create(:user, password: password) }
|
let(:scientifique_user) { create(:user, password: password) }
|
||||||
let(:litteraire_user) { create(:user, password: password) }
|
let(:litteraire_user) { create(:user, password: password) }
|
||||||
|
|
||||||
before { Flipper.enable_actor(:administrateur_routage, administrateur.user) }
|
before do
|
||||||
|
procedure.defaut_groupe_instructeur.instructeurs << administrateur.instructeur
|
||||||
|
Flipper.enable_actor(:administrateur_routage, administrateur.user)
|
||||||
|
end
|
||||||
|
|
||||||
scenario 'works' do
|
scenario 'works' do
|
||||||
login_as administrateur.user, scope: :user
|
login_as administrateur.user, scope: :user
|
||||||
|
@ -22,11 +25,15 @@ feature 'The routing' do
|
||||||
|
|
||||||
# rename defaut groupe to littéraire
|
# rename defaut groupe to littéraire
|
||||||
click_on 'voir'
|
click_on 'voir'
|
||||||
fill_in 'groupe_instructeur_label', with: 'littéraire'
|
expect(page).to have_css('#groupe_instructeur_label')
|
||||||
|
2.times { find(:css, "#groupe_instructeur_label").set("littéraire") }
|
||||||
click_on 'Renommer'
|
click_on 'Renommer'
|
||||||
|
|
||||||
|
expect(procedure.defaut_groupe_instructeur.reload.label).to eq('littéraire')
|
||||||
|
|
||||||
# add victor to littéraire groupe
|
# add victor to littéraire groupe
|
||||||
fill_in 'instructeur_email', with: 'victor@inst.com'
|
try_twice { find('input.select2-search__field').send_keys('victor@inst.com', :enter) }
|
||||||
|
|
||||||
perform_enqueued_jobs { click_on 'Affecter' }
|
perform_enqueued_jobs { click_on 'Affecter' }
|
||||||
victor = User.find_by(email: 'victor@inst.com').instructeur
|
victor = User.find_by(email: 'victor@inst.com').instructeur
|
||||||
|
|
||||||
|
@ -37,13 +44,13 @@ feature 'The routing' do
|
||||||
click_on 'Ajouter le groupe'
|
click_on 'Ajouter le groupe'
|
||||||
|
|
||||||
# add marie to scientifique groupe
|
# add marie to scientifique groupe
|
||||||
fill_in 'instructeur_email', with: 'marie@inst.com'
|
try_twice { find('input.select2-search__field').send_keys('marie@inst.com', :enter) }
|
||||||
perform_enqueued_jobs { click_on 'Affecter' }
|
perform_enqueued_jobs { click_on 'Affecter' }
|
||||||
marie = User.find_by(email: 'marie@inst.com').instructeur
|
marie = User.find_by(email: 'marie@inst.com').instructeur
|
||||||
|
|
||||||
# publish
|
# publish
|
||||||
publish_procedure(procedure)
|
publish_procedure(procedure)
|
||||||
log_out
|
log_out(old_layout: true)
|
||||||
|
|
||||||
# 2 users fill a dossier in each group
|
# 2 users fill a dossier in each group
|
||||||
user_send_dossier(scientifique_user, 'scientifique')
|
user_send_dossier(scientifique_user, 'scientifique')
|
||||||
|
@ -165,7 +172,25 @@ feature 'The routing' do
|
||||||
expect(page).to have_content 'Mot de passe enregistré'
|
expect(page).to have_content 'Mot de passe enregistré'
|
||||||
end
|
end
|
||||||
|
|
||||||
def log_out
|
def log_out(old_layout: false)
|
||||||
click_on 'Se déconnecter'
|
if old_layout
|
||||||
|
expect(page).to have_content('Se déconnecter')
|
||||||
|
click_on 'Se déconnecter'
|
||||||
|
else
|
||||||
|
try_twice do
|
||||||
|
expect(page).to have_css('[title="Mon compte"]')
|
||||||
|
find('[title="Mon compte"]').click
|
||||||
|
expect(page).to have_content('Se déconnecter')
|
||||||
|
click_on 'Se déconnecter'
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
def try_twice
|
||||||
|
begin
|
||||||
|
yield
|
||||||
|
rescue Selenium::WebDriver::Error::ElementNotInteractableError, Capybara::ElementNotFound
|
||||||
|
yield
|
||||||
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
|
@ -0,0 +1,8 @@
|
||||||
|
class GroupeInstructeurMailerPreview < ActionMailer::Preview
|
||||||
|
def add_instructeurs
|
||||||
|
groupe = GroupeInstructeur.new(label: 'Val-De-Marne')
|
||||||
|
current_instructeur_email = 'admin@dgfip.com'
|
||||||
|
instructeurs = Instructeur.limit(2)
|
||||||
|
GroupeInstructeurMailer.add_instructeurs(groupe, instructeurs, current_instructeur_email)
|
||||||
|
end
|
||||||
|
end
|
|
@ -149,7 +149,7 @@ describe User, type: :model do
|
||||||
it 'keeps the existing instructeurs and adds administrateur' do
|
it 'keeps the existing instructeurs and adds administrateur' do
|
||||||
user = subject
|
user = subject
|
||||||
expect(user.instructeur).to eq(instructeur)
|
expect(user.instructeur).to eq(instructeur)
|
||||||
expect(user.instructeur.administrateurs).to eq(old_admins + admins)
|
expect(user.instructeur.administrateurs).to match_array(old_admins + admins)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
Loading…
Reference in a new issue