Merge branch 'main' into 8054-a11y-ways-of-navigating
This commit is contained in:
commit
b50698a30d
278 changed files with 4369 additions and 2838 deletions
18
.github/workflows/rebase.yml
vendored
18
.github/workflows/rebase.yml
vendored
|
@ -1,18 +0,0 @@
|
|||
on:
|
||||
issue_comment:
|
||||
types: [created]
|
||||
name: Automatic Rebase
|
||||
jobs:
|
||||
rebase:
|
||||
name: Rebase
|
||||
if: github.event.issue.pull_request != '' && contains(github.event.comment.body, '/rebase')
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Checkout the latest code
|
||||
uses: actions/checkout@v3
|
||||
with:
|
||||
fetch-depth: 0
|
||||
- name: Automatic Rebase
|
||||
uses: cirrus-actions/rebase@1.5
|
||||
env:
|
||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
19
.github/workflows/sentry-release.yml
vendored
19
.github/workflows/sentry-release.yml
vendored
|
@ -1,19 +0,0 @@
|
|||
on:
|
||||
push:
|
||||
tags:
|
||||
- '*'
|
||||
name: Publish release on Sentry
|
||||
jobs:
|
||||
deploy:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
- name: Sentry Release
|
||||
uses: getsentry/action-release@v1
|
||||
env:
|
||||
SENTRY_AUTH_TOKEN: ${{ secrets.SENTRY_AUTH_TOKEN }}
|
||||
SENTRY_ORG: ${{ secrets.SENTRY_ORG }}
|
||||
SENTRY_PROJECT: rails
|
||||
with:
|
||||
environment: production
|
||||
version: ${{ github.ref }}
|
|
@ -11,7 +11,7 @@ inherit_mode:
|
|||
- Include
|
||||
|
||||
AllCops:
|
||||
TargetRubyVersion: 3.1
|
||||
TargetRubyVersion: 3.2
|
||||
DisabledByDefault: true
|
||||
SuggestExtensions: false
|
||||
NewCops: enable
|
||||
|
|
|
@ -1 +1 @@
|
|||
3.1.2
|
||||
3.2.2
|
||||
|
|
3
Gemfile
3
Gemfile
|
@ -1,5 +1,7 @@
|
|||
source 'https://rubygems.org'
|
||||
|
||||
gem 'rails', '~> 7.0.4' # allows update to security fixes at any time
|
||||
|
||||
gem 'aasm'
|
||||
gem 'acsv'
|
||||
gem 'active_link_to' # Automatically set a class on active links
|
||||
|
@ -67,7 +69,6 @@ gem 'premailer-rails'
|
|||
gem 'puma' # Use Puma as the app server
|
||||
gem 'pundit'
|
||||
gem 'rack-attack'
|
||||
gem 'rails'
|
||||
gem 'rails-i18n' # Locales par défaut
|
||||
gem 'rake-progressbar', require: false
|
||||
gem 'redcarpet'
|
||||
|
|
292
Gemfile.lock
292
Gemfile.lock
|
@ -4,40 +4,47 @@ GEM
|
|||
aasm (5.2.0)
|
||||
concurrent-ruby (~> 1.0)
|
||||
acsv (0.0.1)
|
||||
actioncable (6.1.7.1)
|
||||
actionpack (= 6.1.7.1)
|
||||
activesupport (= 6.1.7.1)
|
||||
actioncable (7.0.4.3)
|
||||
actionpack (= 7.0.4.3)
|
||||
activesupport (= 7.0.4.3)
|
||||
nio4r (~> 2.0)
|
||||
websocket-driver (>= 0.6.1)
|
||||
actionmailbox (6.1.7.1)
|
||||
actionpack (= 6.1.7.1)
|
||||
activejob (= 6.1.7.1)
|
||||
activerecord (= 6.1.7.1)
|
||||
activestorage (= 6.1.7.1)
|
||||
activesupport (= 6.1.7.1)
|
||||
actionmailbox (7.0.4.3)
|
||||
actionpack (= 7.0.4.3)
|
||||
activejob (= 7.0.4.3)
|
||||
activerecord (= 7.0.4.3)
|
||||
activestorage (= 7.0.4.3)
|
||||
activesupport (= 7.0.4.3)
|
||||
mail (>= 2.7.1)
|
||||
actionmailer (6.1.7.1)
|
||||
actionpack (= 6.1.7.1)
|
||||
actionview (= 6.1.7.1)
|
||||
activejob (= 6.1.7.1)
|
||||
activesupport (= 6.1.7.1)
|
||||
net-imap
|
||||
net-pop
|
||||
net-smtp
|
||||
actionmailer (7.0.4.3)
|
||||
actionpack (= 7.0.4.3)
|
||||
actionview (= 7.0.4.3)
|
||||
activejob (= 7.0.4.3)
|
||||
activesupport (= 7.0.4.3)
|
||||
mail (~> 2.5, >= 2.5.4)
|
||||
net-imap
|
||||
net-pop
|
||||
net-smtp
|
||||
rails-dom-testing (~> 2.0)
|
||||
actionpack (6.1.7.1)
|
||||
actionview (= 6.1.7.1)
|
||||
activesupport (= 6.1.7.1)
|
||||
rack (~> 2.0, >= 2.0.9)
|
||||
actionpack (7.0.4.3)
|
||||
actionview (= 7.0.4.3)
|
||||
activesupport (= 7.0.4.3)
|
||||
rack (~> 2.0, >= 2.2.0)
|
||||
rack-test (>= 0.6.3)
|
||||
rails-dom-testing (~> 2.0)
|
||||
rails-html-sanitizer (~> 1.0, >= 1.2.0)
|
||||
actiontext (6.1.7.1)
|
||||
actionpack (= 6.1.7.1)
|
||||
activerecord (= 6.1.7.1)
|
||||
activestorage (= 6.1.7.1)
|
||||
activesupport (= 6.1.7.1)
|
||||
actiontext (7.0.4.3)
|
||||
actionpack (= 7.0.4.3)
|
||||
activerecord (= 7.0.4.3)
|
||||
activestorage (= 7.0.4.3)
|
||||
activesupport (= 7.0.4.3)
|
||||
globalid (>= 0.6.0)
|
||||
nokogiri (>= 1.8.5)
|
||||
actionview (6.1.7.1)
|
||||
activesupport (= 6.1.7.1)
|
||||
actionview (7.0.4.3)
|
||||
activesupport (= 7.0.4.3)
|
||||
builder (~> 3.1)
|
||||
erubi (~> 1.4)
|
||||
rails-dom-testing (~> 2.0)
|
||||
|
@ -45,9 +52,9 @@ GEM
|
|||
active_link_to (1.0.5)
|
||||
actionpack
|
||||
addressable
|
||||
active_model_serializers (0.10.12)
|
||||
actionpack (>= 4.1, < 6.2)
|
||||
activemodel (>= 4.1, < 6.2)
|
||||
active_model_serializers (0.10.13)
|
||||
actionpack (>= 4.1, < 7.1)
|
||||
activemodel (>= 4.1, < 7.1)
|
||||
case_transform (>= 0.2)
|
||||
jsonapi-renderer (>= 0.1.1.beta1, < 0.3)
|
||||
active_storage_validations (0.9.6)
|
||||
|
@ -55,31 +62,30 @@ GEM
|
|||
activemodel (>= 5.2.0)
|
||||
activestorage (>= 5.2.0)
|
||||
activesupport (>= 5.2.0)
|
||||
activejob (6.1.7.1)
|
||||
activesupport (= 6.1.7.1)
|
||||
activejob (7.0.4.3)
|
||||
activesupport (= 7.0.4.3)
|
||||
globalid (>= 0.3.6)
|
||||
activemodel (6.1.7.1)
|
||||
activesupport (= 6.1.7.1)
|
||||
activerecord (6.1.7.1)
|
||||
activemodel (= 6.1.7.1)
|
||||
activesupport (= 6.1.7.1)
|
||||
activestorage (6.1.7.1)
|
||||
actionpack (= 6.1.7.1)
|
||||
activejob (= 6.1.7.1)
|
||||
activerecord (= 6.1.7.1)
|
||||
activesupport (= 6.1.7.1)
|
||||
activemodel (7.0.4.3)
|
||||
activesupport (= 7.0.4.3)
|
||||
activerecord (7.0.4.3)
|
||||
activemodel (= 7.0.4.3)
|
||||
activesupport (= 7.0.4.3)
|
||||
activestorage (7.0.4.3)
|
||||
actionpack (= 7.0.4.3)
|
||||
activejob (= 7.0.4.3)
|
||||
activerecord (= 7.0.4.3)
|
||||
activesupport (= 7.0.4.3)
|
||||
marcel (~> 1.0)
|
||||
mini_mime (>= 1.1.0)
|
||||
activestorage-openstack (1.5.1)
|
||||
fog-openstack (~> 1.0)
|
||||
marcel
|
||||
rails (>= 5.2.2)
|
||||
activesupport (6.1.7.1)
|
||||
activesupport (7.0.4.3)
|
||||
concurrent-ruby (~> 1.0, >= 1.0.2)
|
||||
i18n (>= 1.6, < 2)
|
||||
minitest (>= 5.1)
|
||||
tzinfo (~> 2.0)
|
||||
zeitwerk (~> 2.3)
|
||||
addressable (2.8.1)
|
||||
public_suffix (>= 2.0.2, < 6.0)
|
||||
administrate (0.18.0)
|
||||
|
@ -95,12 +101,10 @@ GEM
|
|||
aes_key_wrap (1.1.0)
|
||||
after_party (1.11.2)
|
||||
anchored (1.1.0)
|
||||
annotate (3.1.1)
|
||||
activerecord (>= 3.2, < 7.0)
|
||||
annotate (3.2.0)
|
||||
activerecord (>= 3.2, < 8.0)
|
||||
rake (>= 10.4, < 14.0)
|
||||
ast (2.4.2)
|
||||
attr_encrypted (3.1.0)
|
||||
encryptor (~> 3.0.0)
|
||||
attr_required (1.0.1)
|
||||
axe-core-api (4.2.1)
|
||||
capybara
|
||||
|
@ -119,7 +123,7 @@ GEM
|
|||
axlsx_styler (1.1.0)
|
||||
activesupport (>= 3.1)
|
||||
caxlsx (>= 2.0.2)
|
||||
bcrypt (3.1.16)
|
||||
bcrypt (3.1.18)
|
||||
better_html (1.0.16)
|
||||
actionview (>= 4.0)
|
||||
activesupport (>= 4.0)
|
||||
|
@ -175,14 +179,15 @@ GEM
|
|||
css_parser (1.9.0)
|
||||
addressable
|
||||
daemons (1.3.1)
|
||||
deep_cloneable (3.0.0)
|
||||
activerecord (>= 3.1.0, < 7)
|
||||
date (3.3.3)
|
||||
deep_cloneable (3.2.0)
|
||||
activerecord (>= 3.1.0, < 8)
|
||||
delayed_cron_job (0.7.4)
|
||||
delayed_job (>= 4.1)
|
||||
delayed_job (4.1.11)
|
||||
activesupport (>= 3.0, < 8.0)
|
||||
delayed_job_active_record (4.1.5)
|
||||
activerecord (>= 3.0, < 6.2)
|
||||
delayed_job_active_record (4.1.7)
|
||||
activerecord (>= 3.0, < 8.0)
|
||||
delayed_job (>= 3.0, < 5)
|
||||
delayed_job_web (1.4.4)
|
||||
activerecord (> 3.0.0)
|
||||
|
@ -191,7 +196,7 @@ GEM
|
|||
sinatra (>= 1.4.4)
|
||||
descendants_tracker (0.0.4)
|
||||
thread_safe (~> 0.3, >= 0.3.1)
|
||||
devise (4.7.3)
|
||||
devise (4.9.2)
|
||||
bcrypt (~> 3.0)
|
||||
orm_adapter (~> 0.1)
|
||||
railties (>= 4.1.0)
|
||||
|
@ -199,16 +204,14 @@ GEM
|
|||
warden (~> 1.2.3)
|
||||
devise-i18n (1.9.2)
|
||||
devise (>= 4.7.1)
|
||||
devise-two-factor (4.0.2)
|
||||
activesupport (< 7.1)
|
||||
attr_encrypted (>= 1.3, < 4, != 2)
|
||||
devise-two-factor (5.0.0)
|
||||
activesupport (~> 7.0)
|
||||
devise (~> 4.0)
|
||||
railties (< 7.1)
|
||||
railties (~> 7.0)
|
||||
rotp (~> 6.0)
|
||||
diff-lcs (1.4.4)
|
||||
digest (3.1.0)
|
||||
discard (1.2.0)
|
||||
activerecord (>= 4.2, < 7)
|
||||
diff-lcs (1.5.0)
|
||||
discard (1.2.1)
|
||||
activerecord (>= 4.2, < 8)
|
||||
domain_name (0.5.20190701)
|
||||
unf (>= 0.0.5, < 1.0.0)
|
||||
dotenv (2.7.6)
|
||||
|
@ -224,7 +227,6 @@ GEM
|
|||
concurrent-ruby (~> 1.0)
|
||||
http (>= 3.0)
|
||||
ruby2_keywords
|
||||
encryptor (3.0.0)
|
||||
erubi (1.12.0)
|
||||
et-orbi (1.2.4)
|
||||
tzinfo
|
||||
|
@ -233,25 +235,6 @@ GEM
|
|||
excon (0.79.0)
|
||||
factory_bot (6.1.0)
|
||||
activesupport (>= 5.0.0)
|
||||
faraday (1.8.0)
|
||||
faraday-em_http (~> 1.0)
|
||||
faraday-em_synchrony (~> 1.0)
|
||||
faraday-excon (~> 1.1)
|
||||
faraday-httpclient (~> 1.0.1)
|
||||
faraday-net_http (~> 1.0)
|
||||
faraday-net_http_persistent (~> 1.1)
|
||||
faraday-patron (~> 1.0)
|
||||
faraday-rack (~> 1.0)
|
||||
multipart-post (>= 1.2, < 3)
|
||||
ruby2_keywords (>= 0.0.4)
|
||||
faraday-em_http (1.0.0)
|
||||
faraday-em_synchrony (1.0.0)
|
||||
faraday-excon (1.1.0)
|
||||
faraday-httpclient (1.0.1)
|
||||
faraday-net_http (1.0.1)
|
||||
faraday-net_http_persistent (1.2.0)
|
||||
faraday-patron (1.0.0)
|
||||
faraday-rack (1.0.0)
|
||||
ffi (1.15.5)
|
||||
ffi-compiler (1.0.1)
|
||||
ffi (>= 1.0.0)
|
||||
|
@ -285,7 +268,7 @@ GEM
|
|||
raabro (~> 1.4)
|
||||
geo_coord (0.2.0)
|
||||
geocoder (1.6.5)
|
||||
globalid (1.0.1)
|
||||
globalid (1.1.0)
|
||||
activesupport (>= 5.0)
|
||||
gon (6.4.0)
|
||||
actionpack (>= 3.0.20)
|
||||
|
@ -341,7 +324,7 @@ GEM
|
|||
http-form_data (2.3.0)
|
||||
http_accept_language (2.1.1)
|
||||
httpclient (2.8.3)
|
||||
i18n (1.12.0)
|
||||
i18n (1.13.0)
|
||||
concurrent-ruby (~> 1.0)
|
||||
i18n-tasks (1.0.9)
|
||||
activesupport (>= 4.0.2)
|
||||
|
@ -362,7 +345,6 @@ GEM
|
|||
ruby-vips (>= 2.0.17, < 3)
|
||||
invisible_captcha (2.0.0)
|
||||
rails (>= 5.0)
|
||||
io-wait (0.2.1)
|
||||
ipaddress (0.8.3)
|
||||
jquery-rails (4.5.1)
|
||||
rails-dom-testing (>= 1, < 3)
|
||||
|
@ -412,10 +394,10 @@ GEM
|
|||
railties (>= 4)
|
||||
request_store (~> 1.0)
|
||||
logstash-event (1.2.02)
|
||||
loofah (2.19.1)
|
||||
loofah (2.21.3)
|
||||
crass (~> 1.0.2)
|
||||
nokogiri (>= 1.5.9)
|
||||
mail (2.8.0.1)
|
||||
nokogiri (>= 1.12.0)
|
||||
mail (2.8.1)
|
||||
mini_mime (>= 0.1.1)
|
||||
net-imap
|
||||
net-pop
|
||||
|
@ -436,30 +418,25 @@ GEM
|
|||
rake
|
||||
mini_magick (4.11.0)
|
||||
mini_mime (1.1.2)
|
||||
mini_portile2 (2.8.1)
|
||||
mini_portile2 (2.8.2)
|
||||
minitest (5.18.0)
|
||||
msgpack (1.4.2)
|
||||
multi_json (1.15.0)
|
||||
multipart-post (2.1.1)
|
||||
mustermann (3.0.0)
|
||||
ruby2_keywords (~> 0.0.1)
|
||||
net-imap (0.2.3)
|
||||
digest
|
||||
net-imap (0.3.4)
|
||||
date
|
||||
net-protocol
|
||||
strscan
|
||||
net-pop (0.1.1)
|
||||
digest
|
||||
net-pop (0.1.2)
|
||||
net-protocol
|
||||
net-protocol (0.2.1)
|
||||
timeout
|
||||
net-protocol (0.1.1)
|
||||
io-wait
|
||||
timeout
|
||||
net-smtp (0.2.1)
|
||||
net-smtp (0.3.3)
|
||||
net-protocol
|
||||
netrc (0.11.0)
|
||||
nio4r (2.5.8)
|
||||
nokogiri (1.14.3)
|
||||
mini_portile2 (~> 2.8.0)
|
||||
nio4r (2.5.9)
|
||||
nokogiri (1.15.2)
|
||||
mini_portile2 (~> 2.8.2)
|
||||
racc (~> 1.4)
|
||||
open4 (1.3.4)
|
||||
openid_connect (1.3.0)
|
||||
|
@ -478,7 +455,7 @@ GEM
|
|||
parser (3.2.2.0)
|
||||
ast (~> 2.4.1)
|
||||
pdf-core (0.9.0)
|
||||
pg (1.2.3)
|
||||
pg (1.4.6)
|
||||
phonelib (0.6.53)
|
||||
prawn (2.4.0)
|
||||
pdf-core (~> 0.9.0)
|
||||
|
@ -497,12 +474,12 @@ GEM
|
|||
actionmailer (>= 3)
|
||||
premailer (~> 1.7, >= 1.7.9)
|
||||
promise.rb (0.7.4)
|
||||
pry (0.13.1)
|
||||
pry (0.14.2)
|
||||
coderay (~> 1.1)
|
||||
method_source (~> 1.0)
|
||||
pry-byebug (3.9.0)
|
||||
pry-byebug (3.10.1)
|
||||
byebug (~> 11.0)
|
||||
pry (~> 0.13.0)
|
||||
pry (>= 0.13, < 0.15)
|
||||
pry-rails (0.3.9)
|
||||
pry (>= 0.10.4)
|
||||
public_suffix (5.0.1)
|
||||
|
@ -512,7 +489,7 @@ GEM
|
|||
activesupport (>= 3.0.0)
|
||||
raabro (1.4.0)
|
||||
racc (1.6.2)
|
||||
rack (2.2.6.4)
|
||||
rack (2.2.7)
|
||||
rack-attack (6.5.0)
|
||||
rack (>= 1.0, < 3)
|
||||
rack-mini-profiler (3.0.0)
|
||||
|
@ -532,21 +509,20 @@ GEM
|
|||
rack_session_access (0.2.0)
|
||||
builder (>= 2.0.0)
|
||||
rack (>= 1.0.0)
|
||||
rails (6.1.7.1)
|
||||
actioncable (= 6.1.7.1)
|
||||
actionmailbox (= 6.1.7.1)
|
||||
actionmailer (= 6.1.7.1)
|
||||
actionpack (= 6.1.7.1)
|
||||
actiontext (= 6.1.7.1)
|
||||
actionview (= 6.1.7.1)
|
||||
activejob (= 6.1.7.1)
|
||||
activemodel (= 6.1.7.1)
|
||||
activerecord (= 6.1.7.1)
|
||||
activestorage (= 6.1.7.1)
|
||||
activesupport (= 6.1.7.1)
|
||||
rails (7.0.4.3)
|
||||
actioncable (= 7.0.4.3)
|
||||
actionmailbox (= 7.0.4.3)
|
||||
actionmailer (= 7.0.4.3)
|
||||
actionpack (= 7.0.4.3)
|
||||
actiontext (= 7.0.4.3)
|
||||
actionview (= 7.0.4.3)
|
||||
activejob (= 7.0.4.3)
|
||||
activemodel (= 7.0.4.3)
|
||||
activerecord (= 7.0.4.3)
|
||||
activestorage (= 7.0.4.3)
|
||||
activesupport (= 7.0.4.3)
|
||||
bundler (>= 1.15.0)
|
||||
railties (= 6.1.7.1)
|
||||
sprockets-rails (>= 2.0.0)
|
||||
railties (= 7.0.4.3)
|
||||
rails-controller-testing (1.0.5)
|
||||
actionpack (>= 5.0.1.rc1)
|
||||
actionview (>= 5.0.1.rc1)
|
||||
|
@ -564,12 +540,13 @@ GEM
|
|||
rails-i18n (7.0.3)
|
||||
i18n (>= 0.7, < 2)
|
||||
railties (>= 6.0.0, < 8)
|
||||
railties (6.1.7.1)
|
||||
actionpack (= 6.1.7.1)
|
||||
activesupport (= 6.1.7.1)
|
||||
railties (7.0.4.3)
|
||||
actionpack (= 7.0.4.3)
|
||||
activesupport (= 7.0.4.3)
|
||||
method_source
|
||||
rake (>= 12.2)
|
||||
thor (~> 1.0)
|
||||
zeitwerk (~> 2.5)
|
||||
rainbow (3.1.1)
|
||||
rake (13.0.6)
|
||||
rake-progressbar (0.0.5)
|
||||
|
@ -580,9 +557,9 @@ GEM
|
|||
regexp_parser (2.8.0)
|
||||
request_store (1.5.0)
|
||||
rack (>= 1.4)
|
||||
responders (3.0.1)
|
||||
actionpack (>= 5.0)
|
||||
railties (>= 5.0)
|
||||
responders (3.1.0)
|
||||
actionpack (>= 5.2)
|
||||
railties (>= 5.2)
|
||||
rest-client (2.1.0)
|
||||
http-accept (>= 1.7.0, < 2.0)
|
||||
http-cookie (>= 1.0.2, < 2.0)
|
||||
|
@ -596,29 +573,29 @@ GEM
|
|||
builder (>= 3.0)
|
||||
dry-inflector (~> 0.1)
|
||||
rubyzip (>= 1.0)
|
||||
rotp (6.2.0)
|
||||
rotp (6.2.2)
|
||||
rouge (3.30.0)
|
||||
rqrcode (1.2.0)
|
||||
chunky_png (~> 1.0)
|
||||
rqrcode_core (~> 0.2)
|
||||
rqrcode_core (0.2.0)
|
||||
rspec-core (3.10.1)
|
||||
rspec-support (~> 3.10.0)
|
||||
rspec-expectations (3.10.1)
|
||||
rspec-core (3.12.2)
|
||||
rspec-support (~> 3.12.0)
|
||||
rspec-expectations (3.12.3)
|
||||
diff-lcs (>= 1.2.0, < 2.0)
|
||||
rspec-support (~> 3.10.0)
|
||||
rspec-mocks (3.10.2)
|
||||
rspec-support (~> 3.12.0)
|
||||
rspec-mocks (3.12.5)
|
||||
diff-lcs (>= 1.2.0, < 2.0)
|
||||
rspec-support (~> 3.10.0)
|
||||
rspec-rails (5.0.0)
|
||||
actionpack (>= 5.2)
|
||||
activesupport (>= 5.2)
|
||||
railties (>= 5.2)
|
||||
rspec-core (~> 3.10)
|
||||
rspec-expectations (~> 3.10)
|
||||
rspec-mocks (~> 3.10)
|
||||
rspec-support (~> 3.10)
|
||||
rspec-support (3.10.2)
|
||||
rspec-support (~> 3.12.0)
|
||||
rspec-rails (6.0.1)
|
||||
actionpack (>= 6.1)
|
||||
activesupport (>= 6.1)
|
||||
railties (>= 6.1)
|
||||
rspec-core (~> 3.11)
|
||||
rspec-expectations (~> 3.11)
|
||||
rspec-mocks (~> 3.11)
|
||||
rspec-support (~> 3.11)
|
||||
rspec-support (3.12.0)
|
||||
rspec_junit_formatter (0.4.1)
|
||||
rspec-core (>= 2, < 4, != 2.12.0)
|
||||
rubocop (1.50.2)
|
||||
|
@ -685,19 +662,14 @@ GEM
|
|||
rexml (~> 3.2, >= 3.2.5)
|
||||
rubyzip (>= 1.2.2, < 3.0)
|
||||
websocket (~> 1.0)
|
||||
sentry-delayed_job (4.8.1)
|
||||
sentry-delayed_job (5.9.0)
|
||||
delayed_job (>= 4.0)
|
||||
sentry-ruby-core (~> 4.8.1)
|
||||
sentry-rails (4.8.1)
|
||||
sentry-ruby (~> 5.9.0)
|
||||
sentry-rails (5.9.0)
|
||||
railties (>= 5.0)
|
||||
sentry-ruby-core (~> 4.8.1)
|
||||
sentry-ruby (4.8.1)
|
||||
sentry-ruby (~> 5.9.0)
|
||||
sentry-ruby (5.9.0)
|
||||
concurrent-ruby (~> 1.0, >= 1.0.2)
|
||||
faraday (>= 1.0)
|
||||
sentry-ruby-core (= 4.8.1)
|
||||
sentry-ruby-core (4.8.1)
|
||||
concurrent-ruby
|
||||
faraday
|
||||
shoulda-matchers (4.5.1)
|
||||
activesupport (>= 4.2.0)
|
||||
sib-api-v3-sdk (7.4.0)
|
||||
|
@ -718,7 +690,7 @@ GEM
|
|||
axlsx_styler (>= 1.0.0, < 2)
|
||||
caxlsx (>= 2.0.2, < 4)
|
||||
rodf (>= 1.0.0, < 2)
|
||||
spring (2.1.1)
|
||||
spring (4.1.1)
|
||||
spring-commands-rspec (1.0.4)
|
||||
spring (>= 0.9.1)
|
||||
sprockets (4.2.0)
|
||||
|
@ -731,7 +703,6 @@ GEM
|
|||
stackprof (0.2.21)
|
||||
strong_migrations (0.8.0)
|
||||
activerecord (>= 5.2)
|
||||
strscan (3.0.4)
|
||||
swd (1.3.0)
|
||||
activesupport (>= 3)
|
||||
attr_required (>= 0.0.5)
|
||||
|
@ -740,11 +711,11 @@ GEM
|
|||
temple (0.8.2)
|
||||
terminal-table (3.0.2)
|
||||
unicode-display_width (>= 1.1.1, < 3)
|
||||
thor (1.2.1)
|
||||
thor (1.2.2)
|
||||
thread_safe (0.3.6)
|
||||
tilt (2.0.11)
|
||||
timecop (0.9.4)
|
||||
timeout (0.1.1)
|
||||
timeout (0.3.2)
|
||||
ttfunk (1.7.0)
|
||||
turbo-rails (1.3.2)
|
||||
actionpack (>= 6.0.0)
|
||||
|
@ -767,8 +738,9 @@ GEM
|
|||
activemodel (>= 3.0.0)
|
||||
public_suffix
|
||||
vcr (6.1.0)
|
||||
view_component (2.63.0)
|
||||
activesupport (>= 5.0.0, < 8.0)
|
||||
view_component (2.82.0)
|
||||
activesupport (>= 5.2.0, < 8.0)
|
||||
concurrent-ruby (~> 1.0)
|
||||
method_source (~> 1.0)
|
||||
virtus (2.0.0)
|
||||
axiom-types (~> 0.1)
|
||||
|
@ -777,7 +749,7 @@ GEM
|
|||
vite_rails (3.0.14)
|
||||
railties (>= 5.1, < 8)
|
||||
vite_ruby (~> 3.0, >= 3.2.2)
|
||||
vite_ruby (3.3.0)
|
||||
vite_ruby (3.3.1)
|
||||
dry-cli (>= 0.7, < 2)
|
||||
rack-proxy (~> 0.6, >= 0.6.1)
|
||||
zeitwerk (~> 2.2)
|
||||
|
@ -815,7 +787,7 @@ GEM
|
|||
nokogiri (~> 1.11)
|
||||
xpath (3.2.0)
|
||||
nokogiri (~> 1.8)
|
||||
zeitwerk (2.6.7)
|
||||
zeitwerk (2.6.8)
|
||||
zip_tricks (5.6.0)
|
||||
zipline (1.4.1)
|
||||
actionpack (>= 6.0, < 8.0)
|
||||
|
@ -911,7 +883,7 @@ DEPENDENCIES
|
|||
rack-attack
|
||||
rack-mini-profiler
|
||||
rack_session_access
|
||||
rails
|
||||
rails (~> 7.0.4)
|
||||
rails-controller-testing
|
||||
rails-erd
|
||||
rails-i18n
|
||||
|
|
|
@ -25,4 +25,3 @@ $blue-france-500: #000091;
|
|||
$blue-france-400: #7F7FC8;
|
||||
$blue-cumulus-950: #E6EEFE;
|
||||
$g700: #383838;
|
||||
$alt-blue-france: rgba(245, 245, 254, 1);
|
||||
|
|
|
@ -39,6 +39,13 @@ input[type="radio"] {
|
|||
}
|
||||
// scss-lint:enable DuplicateProperty
|
||||
|
||||
// remove additional calendar icon on date input already handle by Firefox navigator
|
||||
@-moz-document url-prefix() {
|
||||
.fr-input[type="date"] {
|
||||
background-image: none;
|
||||
}
|
||||
}
|
||||
|
||||
// remove pointer cursor on textarea
|
||||
textarea {
|
||||
cursor: auto;
|
||||
|
@ -66,3 +73,20 @@ fieldset {
|
|||
color: $light-red;
|
||||
box-shadow: 0px 0px 0px 1px $light-red;
|
||||
}
|
||||
|
||||
.fr-table table.hack-to-display-dropdown {
|
||||
padding-bottom: 300px;
|
||||
margin-bottom: -300px;
|
||||
}
|
||||
|
||||
// on utilise le dropdown de sélecteur de langue pour un autre usage donc on veut retirer l'icone
|
||||
.fr-translate .fr-translate__btn.custom-fr-translate-no-icon::before {
|
||||
display: none;
|
||||
}
|
||||
|
||||
// on veut ferrer à droite le dropdown de sélecteur de langue
|
||||
@media (min-width: 62em) {
|
||||
.fr-nav__item.custom-fr-translate-flex-end {
|
||||
align-items: flex-end;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -142,7 +142,7 @@ $landing-breakpoint: 1040px;
|
|||
.usagers-panel,
|
||||
.numbers-panel,
|
||||
.cta-panel-2 {
|
||||
background-color: $alt-blue-france;
|
||||
background-color: var(--background-alt-blue-france);
|
||||
}
|
||||
|
||||
.more-info {
|
||||
|
|
|
@ -1,9 +1,5 @@
|
|||
.counter-start-header-section {
|
||||
counter-reset: h1 h2 h3 h4 h5 h6;
|
||||
|
||||
.reset-h1 {
|
||||
counter-reset: h2;
|
||||
}
|
||||
counter-reset: h2;
|
||||
|
||||
.reset-h2 {
|
||||
counter-reset: h3;
|
||||
|
@ -21,35 +17,46 @@
|
|||
counter-reset: h6;
|
||||
}
|
||||
|
||||
.reset-h6 {
|
||||
counter-reset: h6;
|
||||
}
|
||||
|
||||
.header-section.fr-h1::before {
|
||||
counter-increment: h1;
|
||||
content: counter(h1) ". ";
|
||||
.reset-h5 {
|
||||
counter-reset: h6;
|
||||
}
|
||||
|
||||
.reset-h6 {
|
||||
counter-reset: h7;
|
||||
}
|
||||
|
||||
.header-section.fr-h2::before {
|
||||
counter-increment: h2;
|
||||
content: counter(h1) "."counter(h2) ". ";
|
||||
content: counter(h2) ". ";
|
||||
}
|
||||
|
||||
.header-section.fr-h3::before {
|
||||
counter-increment: h3;
|
||||
content: counter(h1) "."counter(h2) "." counter(h3) ". ";
|
||||
content: counter(h2) "." counter(h3) ". ";
|
||||
}
|
||||
|
||||
.header-section.fr-h4::before {
|
||||
counter-increment: h4;
|
||||
content: counter(h1) "."counter(h2) "." counter(h3) "." counter(h4) ". ";
|
||||
content: counter(h2) "." counter(h3) "." counter(h4) ". ";
|
||||
}
|
||||
|
||||
.header-section.fr-h5::before {
|
||||
counter-increment: h5;
|
||||
content: counter(h1) "."counter(h2) "." counter(h3) "." counter(h4) "." counter(h5) ". ";
|
||||
content: counter(h2) "." counter(h3) "." counter(h4) "." counter(h5) ". ";
|
||||
}
|
||||
|
||||
.header-section.fr-h6::before {
|
||||
counter-increment: h6;
|
||||
content: counter(h1) "."counter(h2) "." counter(h3) "." counter(h4) "." counter(h5) "." counter(h6) ". ";
|
||||
content: counter(h2) "." counter(h3) "." counter(h4) "." counter(h5) "." counter(h6) ". ";
|
||||
}
|
||||
|
||||
.header-section.fr-h7::before {
|
||||
counter-increment: h7;
|
||||
content: counter(h2) "." counter(h3) "." counter(h4) "." counter(h5) "." counter(h6) "." counter(h7) ". ";
|
||||
}
|
||||
|
||||
.repetition {
|
||||
|
|
|
@ -1,15 +1,8 @@
|
|||
@import "colors";
|
||||
@import "constants";
|
||||
|
||||
.status-overview {
|
||||
text-align: center;
|
||||
margin-bottom: $default-padding * 2;
|
||||
}
|
||||
|
||||
.status-timeline {
|
||||
display: inline-block;
|
||||
margin-top: $default-padding * 2;
|
||||
margin-bottom: $default-padding * 2;
|
||||
border: 1px solid #808080;
|
||||
border-radius: 3px;
|
||||
|
||||
|
@ -46,15 +39,6 @@
|
|||
}
|
||||
|
||||
.status-explanation {
|
||||
text-align: left;
|
||||
|
||||
.brouillon,
|
||||
.en-construction,
|
||||
.en-instruction {
|
||||
max-width: 650px;
|
||||
margin: auto;
|
||||
}
|
||||
|
||||
p {
|
||||
margin-bottom: $default-padding;
|
||||
}
|
||||
|
|
|
@ -2,7 +2,7 @@
|
|||
@import "constants";
|
||||
|
||||
.sub-header {
|
||||
background-color: $alt-blue-france;
|
||||
background-color: var(--background-alt-blue-france);
|
||||
padding-top: $default-padding;
|
||||
margin-bottom: $sub-header-bottom-margin;
|
||||
border-bottom: 1px solid $border-grey;
|
||||
|
|
|
@ -93,7 +93,7 @@ class Attachment::EditComponent < ApplicationComponent
|
|||
nil
|
||||
end
|
||||
|
||||
def field_name
|
||||
def field_name(object_name = nil, method_name = nil, *method_names, multiple: false, index: nil)
|
||||
helpers.field_name(@form_object_name || ActiveModel::Naming.param_key(@attached_file.record), attribute_name)
|
||||
end
|
||||
|
||||
|
|
|
@ -5,9 +5,9 @@ en:
|
|||
confirmation: Draft saved
|
||||
error: Impossible to save the draft
|
||||
en_construction:
|
||||
explanation: Your file is automatically saved.
|
||||
confirmation: File saved
|
||||
error: Impossible to save the file
|
||||
explanation: Your modifications are automatically saved. Submit them when you’re done.
|
||||
confirmation: Modifications saved
|
||||
error: Impossible to save the modifications.
|
||||
annotations:
|
||||
explanation: Your annotations are automatically saved.
|
||||
confirmation: Annotations saved
|
||||
|
|
|
@ -5,11 +5,11 @@ fr:
|
|||
confirmation: Brouillon enregistré
|
||||
error: Impossible d’enregistrer le brouillon
|
||||
en_construction:
|
||||
explanation: Votre dossier est automatiquement enregistré.
|
||||
confirmation: Dossier enregistré
|
||||
error: Impossible d’enregistrer le dossier
|
||||
explanation: Vos modifications sont automatiquement enregistrées. Déposez-les quand vous aurez terminé.
|
||||
confirmation: Modifications enregistrées.
|
||||
error: Impossible d’enregistrer les modifications
|
||||
annotations:
|
||||
explanation: Vos annotations sont automatiquement enregistrées.
|
||||
confirmation: Annotations enregistré
|
||||
confirmation: Annotations enregistrées
|
||||
error: Impossible d’enregistrer les annotations
|
||||
more_information: En savoir plus
|
||||
|
|
|
@ -3,10 +3,10 @@
|
|||
%span.autosave-explanation-text
|
||||
- if annotation?
|
||||
= t('.annotations.explanation')
|
||||
- elsif dossier.brouillon?
|
||||
= t('.brouillon.explanation')
|
||||
- else
|
||||
- elsif dossier.editing_fork?
|
||||
= t('.en_construction.explanation')
|
||||
- else
|
||||
= t('.brouillon.explanation')
|
||||
- if !annotation?
|
||||
= link_to t('.more_information'), t("links.common.faq.autosave_url"), class: 'autosave-more-infos fr-link fr-link--sm', **external_link_attributes
|
||||
|
||||
|
@ -15,10 +15,10 @@
|
|||
%span.autosave-label
|
||||
- if annotation?
|
||||
= t('.annotations.confirmation')
|
||||
- elsif dossier.brouillon?
|
||||
= t('.brouillon.confirmation')
|
||||
- else
|
||||
- elsif dossier.editing_fork?
|
||||
= t('.en_construction.confirmation')
|
||||
- else
|
||||
= t('.brouillon.confirmation')
|
||||
- if !annotation?
|
||||
= link_to t('.more_information'), t("links.common.faq.autosave_url"), class: 'autosave-more-infos fr-link fr-link--sm', **external_link_attributes
|
||||
|
||||
|
@ -27,11 +27,11 @@
|
|||
%span.autosave-label
|
||||
- if annotation?
|
||||
= t('.annotations.error')
|
||||
- elsif dossier.brouillon?
|
||||
= t('.brouillon.error')
|
||||
- else
|
||||
- elsif dossier.editing_fork?
|
||||
= t('.en_construction.error')
|
||||
%button.button.small.autosave-retry{ type: :button, data: { action: 'autosave-status#onClickRetryButton', autosave_status_target: 'retryButton' } }
|
||||
%span.autosave-retry-label réessayer
|
||||
%span.autosave-retrying-label enregistrement en cours…
|
||||
- else
|
||||
= t('.brouillon.error')
|
||||
%button.fr-btn.fr-btn--tertiary.fr-btn--sm.autosave-retry{ type: :button, data: { action: 'autosave-status#onClickRetryButton', autosave_status_target: 'retryButton' } }
|
||||
%span.autosave-retry-label Réessayer
|
||||
%span.autosave-retrying-label Enregistrement en cours…
|
||||
|
||||
|
|
|
@ -7,7 +7,7 @@ en:
|
|||
in_progress:
|
||||
text_success:
|
||||
one: 1/1 is being archived
|
||||
other: "%{progress_count}/%{count} files have been archived"
|
||||
other: "%{success_count}/%{count} files have been archived"
|
||||
passer_en_instruction:
|
||||
finish:
|
||||
text_success:
|
||||
|
@ -16,7 +16,7 @@ en:
|
|||
in_progress:
|
||||
text_success:
|
||||
one: 1/1 is being changed to instructing
|
||||
other: "%{progress_count}/%{count} files have been changed to instructing"
|
||||
other: "%{success_count}/%{count} files have been changed to instructing"
|
||||
accepter:
|
||||
finish:
|
||||
text_success:
|
||||
|
@ -25,7 +25,7 @@ en:
|
|||
in_progress:
|
||||
text_success:
|
||||
one: 1/1 is being accepted
|
||||
other: "%{progress_count}/%{count} files have been accepted"
|
||||
other: "%{success_count}/%{count} files have been accepted"
|
||||
follow:
|
||||
finish:
|
||||
text_success:
|
||||
|
@ -34,7 +34,7 @@ en:
|
|||
in_progress:
|
||||
text_success:
|
||||
one: 1/1 is being followed
|
||||
other: "%{progress_count}/%{count} files have been followed"
|
||||
other: "%{success_count}/%{count} files have been followed"
|
||||
unfollow:
|
||||
finish:
|
||||
text_success:
|
||||
|
@ -43,7 +43,7 @@ en:
|
|||
in_progress:
|
||||
text_success:
|
||||
one: 1/1 is being unfollowed
|
||||
other: "%{progress_count}/%{count} files have been unfollowed"
|
||||
other: "%{success_count}/%{count} files have been unfollowed"
|
||||
repasser_en_construction:
|
||||
finish:
|
||||
text_success:
|
||||
|
@ -52,9 +52,10 @@ en:
|
|||
in_progress:
|
||||
text_success:
|
||||
one: 1/1 is being changed to in progress
|
||||
other: "%{progress_count}/%{count} files have been changed to in progress"
|
||||
other: "%{success_count}/%{count} files have been changed to in progress"
|
||||
title:
|
||||
finish: The bulk action is finished
|
||||
in_progress: A bulk action is processing
|
||||
link_text: Refresh this webpage
|
||||
after_link_text: to check if the process is over.
|
||||
context: This bulk action was created by %{mail}, %{time_ago}
|
||||
|
|
|
@ -7,7 +7,7 @@ fr:
|
|||
in_progress:
|
||||
text_success:
|
||||
one: 1 dossier sera archivé
|
||||
other: "%{progress_count}/%{count} dossiers ont été archivés"
|
||||
other: "%{success_count}/%{count} dossiers ont été archivés"
|
||||
passer_en_instruction:
|
||||
finish:
|
||||
text_success:
|
||||
|
@ -16,7 +16,7 @@ fr:
|
|||
in_progress:
|
||||
text_success:
|
||||
one: 1 dossier sera passé en instruction
|
||||
other: "%{progress_count}/%{count} dossiers ont été passés en instruction"
|
||||
other: "%{success_count}/%{count} dossiers ont été passés en instruction"
|
||||
accepter:
|
||||
finish:
|
||||
text_success:
|
||||
|
@ -25,7 +25,7 @@ fr:
|
|||
in_progress:
|
||||
text_success:
|
||||
one: 1 dossier sera accepté
|
||||
other: "%{progress_count}/%{count} dossiers ont été acceptés"
|
||||
other: "%{success_count}/%{count} dossiers ont été acceptés"
|
||||
follow:
|
||||
finish:
|
||||
text_success:
|
||||
|
@ -34,7 +34,7 @@ fr:
|
|||
in_progress:
|
||||
text_success:
|
||||
one: 1 dossier sera suivi
|
||||
other: "%{progress_count}/%{count} dossiers ont été suivis"
|
||||
other: "%{success_count}/%{count} dossiers ont été suivis"
|
||||
unfollow:
|
||||
finish:
|
||||
text_success:
|
||||
|
@ -43,7 +43,7 @@ fr:
|
|||
in_progress:
|
||||
text_success:
|
||||
one: 1 dossier ne sera plus suivi
|
||||
other: "%{progress_count}/%{count} dossiers ne sont plus suivis"
|
||||
other: "%{success_count}/%{count} dossiers ne sont plus suivis"
|
||||
repasser_en_construction:
|
||||
finish:
|
||||
text_success:
|
||||
|
@ -52,9 +52,10 @@ fr:
|
|||
in_progress:
|
||||
text_success:
|
||||
one: 1 dossier sera repassé en construction
|
||||
other: "%{progress_count}/%{count} dossiers ont été repassés en construction"
|
||||
other: "%{success_count}/%{count} dossiers ont été repassés en construction"
|
||||
title:
|
||||
finish: L'action de masse est terminée
|
||||
in_progress: Une action de masse est en cours
|
||||
link_text: Recharger la page
|
||||
after_link_text: pour voir si l'opération est finie.
|
||||
context: Cette opération a été lancée par %{mail}, il y a %{time_ago}
|
||||
|
|
|
@ -9,8 +9,11 @@
|
|||
- else
|
||||
= render Dsfr::AlertComponent.new(title: t(".title.in_progress"), state: :info, heading_level: 'h2') do |c|
|
||||
- c.body do
|
||||
%p= t(".#{batch.operation}.in_progress.text_success", count: @batch.total_count, progress_count: @batch.progress_count)
|
||||
%p= t(".#{batch.operation}.in_progress.text_success", count: @batch.total_count, success_count: @batch.success_count)
|
||||
|
||||
%p
|
||||
= link_to t('.link_text'), instructeur_procedure_path(@procedure, statut: params["statut"]), data: { action: 'turbo-poll#refresh' }
|
||||
= t('.after_link_text')
|
||||
|
||||
%p.fr-mt-2w
|
||||
%small= t('.context', mail: @batch.instructeur.email, time_ago: time_ago_in_words(@batch.created_at))
|
||||
|
|
|
@ -14,7 +14,7 @@ class Dossiers::EditFooterComponent < ApplicationComponent
|
|||
@annotation.present?
|
||||
end
|
||||
|
||||
def button_options
|
||||
def submit_draft_button_options
|
||||
{
|
||||
class: 'fr-btn fr-btn--sm',
|
||||
disabled: !owner?,
|
||||
|
@ -23,6 +23,14 @@ class Dossiers::EditFooterComponent < ApplicationComponent
|
|||
}
|
||||
end
|
||||
|
||||
def submit_en_construction_button_options
|
||||
{
|
||||
class: 'fr-btn fr-btn--sm',
|
||||
method: :post,
|
||||
data: { 'disable-with': t('.submitting'), controller: 'autosave-submit' }
|
||||
}
|
||||
end
|
||||
|
||||
def render?
|
||||
!@dossier.for_procedure_preview?
|
||||
end
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
---
|
||||
en:
|
||||
submit: Submit the file
|
||||
submit_changes: Submit file changes
|
||||
submitting: Submitting…
|
||||
invite_notice: You are invited to make amendments to this file but only the owner themselves can submit it.
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
---
|
||||
fr:
|
||||
submit: Déposer le dossier
|
||||
submit_changes: Déposer les modifications
|
||||
submitting: Envoi en cours…
|
||||
invite_notice: En tant qu’invité, vous pouvez remplir ce formulaire – mais le titulaire du dossier doit le déposer lui-même.
|
||||
|
|
|
@ -3,7 +3,10 @@
|
|||
= render Dossiers::AutosaveFooterComponent.new(dossier: @dossier, annotation: annotation?)
|
||||
|
||||
- if !annotation? && @dossier.can_transition_to_en_construction?
|
||||
= button_to t('.submit'), brouillon_dossier_url(@dossier), button_options
|
||||
= button_to t('.submit'), brouillon_dossier_url(@dossier), submit_draft_button_options
|
||||
- elsif @dossier.forked_with_changes?
|
||||
= button_to t('.submit_changes'), modifier_dossier_url(@dossier.editing_fork_origin), submit_en_construction_button_options
|
||||
|
||||
|
||||
- if @dossier.brouillon? && !owner?
|
||||
.send-notice.invite-cannot-submit
|
||||
|
|
|
@ -0,0 +1,17 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
class Dossiers::EnConstructionNotSubmittedComponent < ApplicationComponent
|
||||
attr_reader :dossier
|
||||
attr_reader :user
|
||||
|
||||
def initialize(dossier:, user:)
|
||||
@dossier = dossier
|
||||
@user = user
|
||||
|
||||
@fork = @dossier.find_editing_fork(user, rebase: false)
|
||||
end
|
||||
|
||||
def render?
|
||||
@fork&.forked_with_changes?
|
||||
end
|
||||
end
|
|
@ -0,0 +1,9 @@
|
|||
---
|
||||
en:
|
||||
title: Modifications not yet submitted
|
||||
body: |
|
||||
You have made changes after submitting your file. Submit them
|
||||
so that the administration can take them into account.
|
||||
buttons:
|
||||
edit: Continue to edit
|
||||
submit: Submit file changes
|
|
@ -0,0 +1,9 @@
|
|||
---
|
||||
fr:
|
||||
title: Des modifications n’ont pas encore été déposées
|
||||
body: |
|
||||
Vous avez apporté des modifications après le dépôt de votre dossier.
|
||||
Déposez-les afin que l’administration les prenne en compte.
|
||||
buttons:
|
||||
edit: Continuer à modifier
|
||||
submit: Déposer les modifications
|
|
@ -0,0 +1,9 @@
|
|||
= render Dsfr::CalloutComponent.new(title: t(".title"), icon: "fr-fi-information-line") do |c|
|
||||
- c.body do
|
||||
= t(".body")
|
||||
|
||||
- c.bottom do
|
||||
%ul.fr-mt-2w.fr-btns-group.fr-btns-group--inline
|
||||
%li= link_to t(".buttons.edit"), modifier_dossier_path(dossier), class: "fr-btn"
|
||||
%li= button_to t(".buttons.submit"), modifier_dossier_path(dossier), class: "fr-btn fr-btn--secondary", method: :post
|
||||
|
|
@ -11,7 +11,7 @@
|
|||
- elsif export.available?
|
||||
- menu.with_item do
|
||||
%div
|
||||
= link_to ready_link_label(export), export.file.service_url, target: "_blank", rel: "noopener", role: 'menuitem'
|
||||
= link_to ready_link_label(export), export.file.url, target: "_blank", rel: "noopener", role: 'menuitem'
|
||||
- if export.old?
|
||||
= button_to download_export_path(export_format: export.format, force_export: true), refresh_button_options(export).merge(role: 'menuitem') do
|
||||
.icon.retry
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
class Dossiers::FilterComponent < ApplicationComponent
|
||||
class Dossiers::InstructeurFilterComponent < ApplicationComponent
|
||||
def initialize(procedure:, procedure_presentation:, statut:, field_id: nil)
|
||||
@procedure = procedure
|
||||
@procedure_presentation = procedure_presentation
|
23
app/components/dossiers/user_filter_component.rb
Normal file
23
app/components/dossiers/user_filter_component.rb
Normal file
|
@ -0,0 +1,23 @@
|
|||
class Dossiers::UserFilterComponent < ApplicationComponent
|
||||
include DossierHelper
|
||||
|
||||
def initialize(statut:, filter:)
|
||||
@statut = statut
|
||||
@filter = filter
|
||||
end
|
||||
|
||||
attr_reader :statut, :filter
|
||||
|
||||
def render?
|
||||
['en-cours', 'traites'].include?(@statut)
|
||||
end
|
||||
|
||||
def states_collection(statut)
|
||||
case statut
|
||||
when 'en-cours'
|
||||
Dossier.states.values - Dossier::TERMINE
|
||||
when 'traites'
|
||||
Dossier::TERMINE
|
||||
end
|
||||
end
|
||||
end
|
|
@ -0,0 +1,16 @@
|
|||
en:
|
||||
legend:
|
||||
states: States
|
||||
created_at: Creation date
|
||||
depose_at: Submission date
|
||||
button:
|
||||
apply_filters: Apply filters
|
||||
reset_filters: Reset filters
|
||||
select_filters: Select a filter
|
||||
tag:
|
||||
active_filters:
|
||||
one: "1 active filter"
|
||||
other: "%{count} active filters"
|
||||
active_filters_link_title:
|
||||
one: Remove the active filter
|
||||
other: "Remove the %{count} active filters"
|
|
@ -0,0 +1,16 @@
|
|||
fr:
|
||||
legend:
|
||||
states: États
|
||||
created_at: Date de création
|
||||
depose_at: Date de dépot
|
||||
button:
|
||||
apply_filters: Appliquer les filtres
|
||||
reset_filters: Réinitialiser les filtres
|
||||
select_filters: Sélectionner un filtre
|
||||
tag:
|
||||
active_filters:
|
||||
one: "1 filtre actif"
|
||||
other: "%{count} filtres actifs"
|
||||
active_filters_link_title:
|
||||
one: Retirer le filtre actif
|
||||
other: "Retirer les %{count} filtres actifs"
|
|
@ -0,0 +1,37 @@
|
|||
.fr-grid-row
|
||||
.fr-col-12
|
||||
%nav.fr-translate.fr-nav
|
||||
.fr-nav__item.custom-fr-translate-flex-end
|
||||
%button.fr-translate__btn.translate-no-icon.fr-btn.fr-btn--tertiary.custom-fr-translate-no-icon{ "aria-controls" => "filters", "aria-expanded" => "false", title: t('.button.select_filters') }
|
||||
= t('.button.select_filters')
|
||||
#filters.fr-collapse.fr-translate__menu.fr-menu
|
||||
%ul.fr-menu__list.fr-p-3w
|
||||
= form_with(url: dossiers_path(), method: :get ) do |f|
|
||||
= f.hidden_field :statut, value: @statut
|
||||
|
||||
%fieldset#checkboxes.fr-fieldset{ "aria-labelledby" => "checkboxes-legend" }
|
||||
%legend#checkboxes-legend.fr-fieldset__legend--regular.fr-fieldset__legend
|
||||
= t('.legend.states')
|
||||
= f.collection_check_boxes :states, states_collection(@statut), :to_s, :to_s, include_hidden: false do |b|
|
||||
.fr-fieldset__element
|
||||
.fr-checkbox-group.fr-ml-2w.fr-py-1w
|
||||
= b.check_box(checked: filter.states_filtered?(b.value))
|
||||
= b.label(class: 'fr-label') { dossier_display_state(b.text) }
|
||||
|
||||
|
||||
.fr-input-group
|
||||
= f.label 'from_created_at_date', t('.legend.created_at'), class: 'fr-label'
|
||||
= f.date_field 'from_created_at_date', value: @filter.from_created_at_date, class: 'fr-input'
|
||||
|
||||
|
||||
.fr-input-group
|
||||
= f.label 'from_depose_at_date', t('.legend.depose_at'), class: 'fr-label'
|
||||
= f.date_field 'from_depose_at_date', value: @filter.from_depose_at_date, class: 'fr-input'
|
||||
|
||||
.fr-my-2w
|
||||
= f.submit t('.button.apply_filters'), class: 'fr-btn fr-btn--sm'
|
||||
= link_to t('.button.reset_filters'), dossiers_path(statut: @statut), class: 'fr-btn fr-btn--sm fr-btn--tertiary-no-outline'
|
||||
|
||||
- if @filter.filter_params.present?
|
||||
.fr-col-12.text-right
|
||||
= link_to t('.tag.active_filters', count: @filter.filter_params_count), dossiers_path(statut: @statut), title: t('.tag.active_filters_link_title', count: @filter.filter_params_count), class: 'fr-tag fr-tag--sm fr-tag--dismiss'
|
|
@ -27,7 +27,7 @@ class Dsfr::CalloutComponent < ApplicationComponent
|
|||
when :success
|
||||
"fr-callout--green-emeraude"
|
||||
else
|
||||
# info is default theme
|
||||
"fr-background-alt--blue-france"
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
7
app/components/dsfr/radio_button_list_component.rb
Normal file
7
app/components/dsfr/radio_button_list_component.rb
Normal file
|
@ -0,0 +1,7 @@
|
|||
class Dsfr::RadioButtonListComponent < ApplicationComponent
|
||||
def initialize(form:, target:, buttons:)
|
||||
@form = form
|
||||
@target = target
|
||||
@buttons = buttons
|
||||
end
|
||||
end
|
|
@ -0,0 +1,13 @@
|
|||
%fieldset.fr-fieldset{ 'aria-labelledby': 'radio-hint-element-legend radio-hint-element-messages' }
|
||||
%legend.fr-fieldset__legend--regular.fr-fieldset__legend
|
||||
= content
|
||||
|
||||
- @buttons.map { _1.values_at(:label, :value, :hint) }.each do |label, value, hint|
|
||||
.fr-fieldset__element
|
||||
.fr-radio-group
|
||||
= @form.radio_button @target, value
|
||||
= @form.label @target, value: value, class: 'fr-label' do
|
||||
- capture do
|
||||
= label
|
||||
%span.fr-hint-text= hint
|
||||
.fr-messages-group{ 'aria-live': 'assertive' }
|
9
app/components/dsfr/toggle_component.rb
Normal file
9
app/components/dsfr/toggle_component.rb
Normal file
|
@ -0,0 +1,9 @@
|
|||
class Dsfr::ToggleComponent < ApplicationComponent
|
||||
def initialize(form:, target:, title:, hint:, disabled:)
|
||||
@form = form
|
||||
@target = target
|
||||
@title = title
|
||||
@hint = hint
|
||||
@disabled = disabled
|
||||
end
|
||||
end
|
|
@ -0,0 +1,7 @@
|
|||
.fr-toggle.fr-toggle--border-bottom.fr-toggle--label-left
|
||||
= @form.check_box @target, class: 'fr-toggle__input', disabled: @disabled
|
||||
= @form.label @target,
|
||||
@title,
|
||||
class: 'fr-toggle__label',
|
||||
data: { 'fr-checked-label': 'Activé', 'fr-unchecked-label': 'Désactivé' }
|
||||
%p.fr-hint-text= @hint
|
|
@ -3,7 +3,7 @@
|
|||
= @form.label @champ.main_value_name, id: @champ.labelledby_id, for: @champ.input_id do
|
||||
- render EditableChamp::ChampLabelContentComponent.new champ: @champ, seen_at: @seen_at
|
||||
- else
|
||||
.form-label.mb-4
|
||||
.form-label.mb-4{ id: @champ.labelledby_id }
|
||||
= render EditableChamp::ChampLabelContentComponent.new champ: @champ, seen_at: @seen_at
|
||||
|
||||
- if @champ.description.present?
|
||||
|
|
|
@ -0,0 +1,5 @@
|
|||
---
|
||||
en:
|
||||
changes_to_save: "modifications to submit"
|
||||
modified_at: "modified on %{datetime}"
|
||||
check_content_rebased: The type of this field or its description has been modified by the administration. Check its content.
|
|
@ -0,0 +1,5 @@
|
|||
---
|
||||
fr:
|
||||
changes_to_save: "modification à déposer"
|
||||
modified_at: "modifié le %{datetime}"
|
||||
check_content_rebased: Le type de ce champ ou sa description ont été modifiés par l'administration. Vérifier son contenu.
|
|
@ -2,10 +2,13 @@
|
|||
- if @champ.mandatory?
|
||||
%span.mandatory *
|
||||
|
||||
- if @champ.updated_at.present? && @seen_at.present?
|
||||
- if @champ.forked_with_changes?
|
||||
%span.updated-at.highlighted
|
||||
= t('.changes_to_save')
|
||||
- elsif @champ.updated_at.present? && @seen_at.present?
|
||||
%span.updated-at{ class: highlight_if_unseen_class }
|
||||
= "modifié le #{try_format_datetime(@champ.updated_at)}"
|
||||
= t('.modified_at', datetime: try_format_datetime(@champ.updated_at))
|
||||
|
||||
- if @champ.rebased_at.present? && @champ.rebased_at > (@seen_at || @champ.updated_at) && current_user.owns_or_invite?(@champ.dossier)
|
||||
%span.updated-at.highlighted
|
||||
Le type de ce champ ou sa description ont été modifiés par l'administration. Vérifier son contenu.
|
||||
= t('.check_content_rebased')
|
||||
|
|
|
@ -10,7 +10,7 @@
|
|||
= render EditableChamp::EditableChampComponent.new form: ,champ:
|
||||
- else
|
||||
- if header_section
|
||||
= render EditableChamp::HeaderSectionComponent.new(champ: header_section)
|
||||
%div{ class: "reset-#{tag_for_depth}" }= render EditableChamp::HeaderSectionComponent.new(champ: header_section)
|
||||
- splitted_tail.each do |section, champ|
|
||||
- if section.present?
|
||||
= render section
|
||||
|
|
|
@ -1,3 +1,4 @@
|
|||
en:
|
||||
remaining_characters: You have %{remaining_words} characters remaining.
|
||||
excess_characters: You have %{excess_words} characters too many.
|
||||
recommended_size: The recommended maximum size is %{size} characters.
|
||||
|
|
|
@ -1,3 +1,4 @@
|
|||
fr:
|
||||
remaining_characters: Il vous reste %{remaining_words} caractères.
|
||||
excess_characters: Vous avez dépassé la taille conseillée de %{excess_words} caractères.
|
||||
excess_characters: Vous avez dépassé la taille conseillée de %{excess_words} caractères. Réduire le nombre de caractère.
|
||||
recommended_size: La taille maximale conseillée est de %{size} caractères.
|
||||
|
|
|
@ -1,6 +1,4 @@
|
|||
- character_limit = @champ.textarea_character_limit.to_i if !@champ.textarea_character_limit&.empty?
|
||||
- character_count = @champ.character_count(@champ.value)
|
||||
- analyze_character_count = @champ.analyze_character_count(character_count, character_limit)
|
||||
%span.sr-only= t('.recommended_size', size: @champ.character_limit_base)
|
||||
|
||||
~ @form.text_area :value,
|
||||
id: @champ.input_id,
|
||||
|
@ -9,12 +7,10 @@
|
|||
required: @champ.required?,
|
||||
value: html_to_string(@champ.value)
|
||||
|
||||
- if @champ.textarea_character_limit?
|
||||
- if @champ.character_limit_info?
|
||||
%span.fr-icon-information-fill.fr-text-default--info.characters-count
|
||||
= t('.remaining_characters', remaining_words: @champ.remaining_characters)
|
||||
|
||||
- if analyze_character_count == :info
|
||||
%span.fr-icon-information-fill.fr-text-default--info.characters-count
|
||||
= t('.remaining_characters', remaining_words: @champ.remaining_characters(character_count, character_limit))
|
||||
|
||||
- if analyze_character_count == :warning
|
||||
%span.fr-icon-close-circle-fill.fr-text-default--error.characters-count
|
||||
= t('.excess_characters', excess_words: @champ.excess_characters(character_count, character_limit))
|
||||
- if @champ.character_limit_warning?
|
||||
%span.fr-icon-warning-fill.fr-text-default--warning.characters-count
|
||||
= t('.excess_characters', excess_words: @champ.excess_characters)
|
||||
|
|
|
@ -36,8 +36,8 @@ fr:
|
|||
add_condition: Une condition a été ajoutée sur le champ « %{label} ». La nouvelle condition est « %{to} ».
|
||||
remove_condition: La condition du champ « %{label} » a été supprimée.
|
||||
update_condition: La condition du champ « %{label} » a été modifiée. La nouvelle condition est « %{to} ».
|
||||
update_textarea_character_limit: La limite de caractères du champ texte « %{label} » a été modifiée. La nouvelle limite est « %{to} ».
|
||||
remove_textarea_character_limit: La limite de caractères du champ texte « %{label} » a été supprimée.
|
||||
update_character_limit: La limite de caractères du champ texte « %{label} » a été modifiée. La nouvelle limite est « %{to} ».
|
||||
remove_character_limit: La limite de caractères du champ texte « %{label} » a été supprimée.
|
||||
private:
|
||||
add: L’annotation privée « %{label} » a été ajoutée.
|
||||
remove: L’annotation privée « %{label} » a été supprimée.
|
||||
|
|
|
@ -130,14 +130,14 @@
|
|||
= t(".#{prefix}.update_condition", label: change.label, to: change.to)
|
||||
- if !total_dossiers.zero? && !change.can_rebase?
|
||||
.fr-alert.fr-alert--warning.fr-mt-1v
|
||||
%p= t('.breakigng_change', count: total_dossiers)
|
||||
- when :textarea_character_limit
|
||||
- if change.to.nil?
|
||||
%p= t('.breaking_change', count: total_dossiers)
|
||||
- when :character_limit
|
||||
- if change.to.blank?
|
||||
- list.with_item do
|
||||
= t(".#{prefix}.remove_textarea_character_limit", label: change.label, to: change.to)
|
||||
= t(".#{prefix}.remove_character_limit", label: change.label, to: change.to)
|
||||
- else
|
||||
- list.with_item do
|
||||
= t(".#{prefix}.update_textarea_character_limit", label: change.label, to: change.to)
|
||||
= t(".#{prefix}.update_character_limit", label: change.label, to: change.to)
|
||||
|
||||
- if @public_move_changes.present?
|
||||
- list.with_item do
|
||||
|
|
|
@ -121,4 +121,14 @@ class TypesDeChampEditor::ChampComponent < ApplicationComponent
|
|||
def conditional_enabled?
|
||||
!type_de_champ.private?
|
||||
end
|
||||
|
||||
def options_for_character_limit
|
||||
[
|
||||
[t('.character_limit.unlimited'), nil],
|
||||
[t('.character_limit.limit', limit: '400'), 400],
|
||||
[t('.character_limit.limit', limit: '1 000'), 1000],
|
||||
[t('.character_limit.limit', limit: '5 000'), 5000],
|
||||
[t('.character_limit.limit', limit: '10 000'), 10000]
|
||||
]
|
||||
end
|
||||
end
|
||||
|
|
|
@ -10,3 +10,6 @@ fr:
|
|||
natura_2000: Natura 2000
|
||||
zones_humides: Zones humides d’importance internationale
|
||||
znieff: ZNIEFF
|
||||
character_limit:
|
||||
unlimited: Pas de limite de caractère
|
||||
limit: Limité à %{limit} caractères
|
||||
|
|
|
@ -103,9 +103,9 @@
|
|||
= form.text_area :collapsible_explanation_text, class: "small-margin small", id: dom_id(type_de_champ, :collapsible_explanation_text)
|
||||
- if type_de_champ.textarea?
|
||||
.cell
|
||||
= form.label :textarea_character_limit, for: dom_id(type_de_champ, :textarea_character_limit) do
|
||||
Spécifier un nombre maximal de caractères (non restrictif) :
|
||||
= form.number_field :textarea_character_limit, min: 400, value: form.object.textarea_character_limit, id: dom_id(type_de_champ, :textarea_character_limit)
|
||||
= form.label :character_limit, for: dom_id(type_de_champ, :character_limit) do
|
||||
Spécifier un nombre maximal conseillé de caractères :
|
||||
= form.select :character_limit, options_for_character_limit, id: dom_id(type_de_champ, :character_limit)
|
||||
|
||||
- if type_de_champ.block?
|
||||
.flex.justify-start.section.ml-1
|
||||
|
|
|
@ -15,7 +15,7 @@ module Administrateurs
|
|||
end
|
||||
|
||||
format.html do
|
||||
redirect_to export.file.service_url
|
||||
redirect_to url_from(export.file.url)
|
||||
end
|
||||
end
|
||||
else
|
||||
|
|
|
@ -169,7 +169,7 @@ module Administrateurs
|
|||
new_procedure = procedure.clone(current_administrateur, cloned_from_library?)
|
||||
|
||||
if new_procedure.valid?
|
||||
flash.notice = 'Démarche clonée, pensez a vérifier la Présentation et choisir le service a laquelle cette procédure est associé.'
|
||||
flash.notice = 'Démarche clonée. Pensez à vérifier la présentation et choisir le service à laquelle cette démarche est associée.'
|
||||
redirect_to admin_procedure_path(id: new_procedure.id)
|
||||
else
|
||||
if cloned_from_library?
|
||||
|
|
|
@ -74,7 +74,7 @@ module Administrateurs
|
|||
def destroy
|
||||
coordinate, type_de_champ = draft.coordinate_and_tdc(params[:stable_id])
|
||||
|
||||
if coordinate.used_by_routing_rules?
|
||||
if coordinate&.used_by_routing_rules?
|
||||
errors = "« #{type_de_champ.libelle} » est utilisé pour le routage, vous ne pouvez pas le supprimer."
|
||||
@morphed = [champ_component_from(coordinate, focused: false, errors:)]
|
||||
flash.alert = errors
|
||||
|
@ -129,7 +129,7 @@ module Administrateurs
|
|||
:collapsible_explanation_enabled,
|
||||
:collapsible_explanation_text,
|
||||
:header_section_level,
|
||||
:textarea_character_limit,
|
||||
:character_limit,
|
||||
editable_options: [
|
||||
:cadastres,
|
||||
:unesco,
|
||||
|
|
|
@ -15,7 +15,7 @@ class AgentConnect::AgentController < ApplicationController
|
|||
cookies.encrypted[STATE_COOKIE_NAME] = state
|
||||
cookies.encrypted[NONCE_COOKIE_NAME] = nonce
|
||||
|
||||
redirect_to uri
|
||||
redirect_to uri, allow_other_host: true
|
||||
end
|
||||
|
||||
def callback
|
||||
|
|
|
@ -287,7 +287,8 @@ class ApplicationController < ActionController::Base
|
|||
enabled: sentry[:enabled],
|
||||
environment: sentry[:environment],
|
||||
browser: { modern: BrowserSupport.supported?(browser) },
|
||||
user: sentry_user
|
||||
user: sentry_user,
|
||||
release: SentryRelease.current
|
||||
}
|
||||
end
|
||||
|
||||
|
|
|
@ -6,7 +6,7 @@ module TurboChampsConcern
|
|||
def champs_to_turbo_update(params, champs)
|
||||
champ_ids = params.keys.map(&:to_i)
|
||||
|
||||
to_update = champs.filter { _1.id.in?(champ_ids) && _1.refresh_after_update? }
|
||||
to_update = champs.filter { _1.id.in?(champ_ids) && (_1.refresh_after_update? || _1.forked_with_changes?) }
|
||||
to_show, to_hide = champs.filter(&:conditional?)
|
||||
.partition(&:visible?)
|
||||
.map { champs_to_one_selector(_1 - to_update) }
|
||||
|
|
|
@ -13,7 +13,10 @@ module Experts
|
|||
DONNES_STATUS = 'donnes'
|
||||
|
||||
def index
|
||||
avis = current_expert.avis.not_revoked.not_termine.includes(dossier: [groupe_instructeur: :procedure]).not_hidden_by_administration
|
||||
avis = current_expert.avis
|
||||
.not_revoked
|
||||
.includes(dossier: [groupe_instructeur: :procedure])
|
||||
.not_hidden_by_administration
|
||||
@avis_by_procedure = avis.to_a.group_by(&:procedure)
|
||||
end
|
||||
|
||||
|
|
|
@ -4,7 +4,7 @@ class FranceConnect::ParticulierController < ApplicationController
|
|||
|
||||
def login
|
||||
if FranceConnectService.enabled?
|
||||
redirect_to FranceConnectService.authorization_uri
|
||||
redirect_to FranceConnectService.authorization_uri, allow_other_host: true
|
||||
else
|
||||
redirect_to new_user_session_path
|
||||
end
|
||||
|
|
|
@ -19,7 +19,7 @@ module Instructeurs
|
|||
|
||||
def attestation
|
||||
if dossier.attestation.pdf.attached?
|
||||
redirect_to dossier.attestation.pdf.service_url
|
||||
redirect_to dossier.attestation.pdf.url, allow_other_host: true
|
||||
end
|
||||
end
|
||||
|
||||
|
|
|
@ -17,13 +17,12 @@ module Instructeurs
|
|||
dossiers = current_instructeur.dossiers
|
||||
.joins(groupe_instructeur: :procedure)
|
||||
.where(procedures: { hidden_at: nil })
|
||||
dossiers_visibles = dossiers.visible_by_administration
|
||||
@dossiers_count_per_procedure = dossiers_visibles.all_state.group('groupe_instructeurs.procedure_id').reorder(nil).count
|
||||
@dossiers_a_suivre_count_per_procedure = dossiers_visibles.without_followers.en_cours.group('groupe_instructeurs.procedure_id').reorder(nil).count
|
||||
@dossiers_archived_count_per_procedure = dossiers_visibles.archived.group('groupe_instructeurs.procedure_id').count
|
||||
@dossiers_termines_count_per_procedure = dossiers_visibles.termine.group('groupe_instructeurs.procedure_id').reorder(nil).count
|
||||
@dossiers_expirant_count_per_procedure = dossiers_visibles.termine_or_en_construction_close_to_expiration.group('groupe_instructeurs.procedure_id').count
|
||||
@dossiers_supprimes_recemment_count_per_procedure = dossiers.hidden_by_administration.group('groupe_instructeurs.procedure_id').reorder(nil).count
|
||||
@dossiers_count_per_procedure = dossiers.by_statut('tous').group('groupe_instructeurs.procedure_id').reorder(nil).count
|
||||
@dossiers_a_suivre_count_per_procedure = dossiers.by_statut('a-suivre').group('groupe_instructeurs.procedure_id').reorder(nil).count
|
||||
@dossiers_archived_count_per_procedure = dossiers.by_statut('archives').group('groupe_instructeurs.procedure_id').count
|
||||
@dossiers_termines_count_per_procedure = dossiers.by_statut('traites').group('groupe_instructeurs.procedure_id').reorder(nil).count
|
||||
@dossiers_expirant_count_per_procedure = dossiers.by_statut('expirant').group('groupe_instructeurs.procedure_id').count
|
||||
@dossiers_supprimes_recemment_count_per_procedure = dossiers.by_statut('supprimes_recemment').group('groupe_instructeurs.procedure_id').reorder(nil).count
|
||||
|
||||
groupe_ids = current_instructeur.groupe_instructeurs.pluck(:id)
|
||||
@followed_dossiers_count_per_procedure = current_instructeur
|
||||
|
@ -175,7 +174,7 @@ module Instructeurs
|
|||
end
|
||||
|
||||
format.html do
|
||||
redirect_to export.file.service_url
|
||||
redirect_to url_from(export.file.url)
|
||||
end
|
||||
end
|
||||
else
|
||||
|
@ -315,6 +314,7 @@ module Instructeurs
|
|||
Procedure
|
||||
.with_attached_logo
|
||||
.find(procedure_id)
|
||||
.tap { Sentry.set_tags(procedure: _1.id) }
|
||||
end
|
||||
|
||||
def ensure_ownership!
|
||||
|
@ -343,7 +343,7 @@ module Instructeurs
|
|||
end
|
||||
|
||||
def current_filters
|
||||
@current_filters ||= procedure_presentation.filters[statut]
|
||||
@current_filters ||= procedure_presentation.filters.fetch(statut, [])
|
||||
end
|
||||
|
||||
def email_usagers_dossiers
|
||||
|
|
|
@ -6,15 +6,16 @@ module Users
|
|||
layout 'procedure_context', only: [:identite, :update_identite, :siret, :update_siret]
|
||||
|
||||
ACTIONS_ALLOWED_TO_ANY_USER = [:index, :recherche, :new, :transferer_all]
|
||||
ACTIONS_ALLOWED_TO_OWNER_OR_INVITE = [:show, :destroy, :demande, :messagerie, :brouillon, :update_brouillon, :submit_brouillon, :modifier, :update, :create_commentaire, :papertrail, :restore]
|
||||
ACTIONS_ALLOWED_TO_OWNER_OR_INVITE = [:show, :destroy, :demande, :messagerie, :brouillon, :submit_brouillon, :submit_en_construction, :modifier, :modifier_legacy, :update, :create_commentaire, :papertrail, :restore]
|
||||
|
||||
before_action :ensure_ownership!, except: ACTIONS_ALLOWED_TO_ANY_USER + ACTIONS_ALLOWED_TO_OWNER_OR_INVITE
|
||||
before_action :ensure_ownership_or_invitation!, only: ACTIONS_ALLOWED_TO_OWNER_OR_INVITE
|
||||
before_action :ensure_dossier_can_be_updated, only: [:update_identite, :update_siret, :brouillon, :update_brouillon, :submit_brouillon, :modifier, :update]
|
||||
before_action :ensure_dossier_can_be_filled, only: [:brouillon, :modifier, :update_brouillon, :submit_brouillon, :update]
|
||||
before_action :ensure_dossier_can_be_updated, only: [:update_identite, :update_siret, :brouillon, :submit_brouillon, :submit_en_construction, :modifier, :modifier_legacy, :update]
|
||||
before_action :ensure_dossier_can_be_filled, only: [:brouillon, :modifier, :submit_brouillon, :submit_en_construction, :update]
|
||||
before_action :ensure_dossier_can_be_viewed, only: [:show]
|
||||
before_action :forbid_invite_submission!, only: [:submit_brouillon]
|
||||
before_action :forbid_closed_submission!, only: [:submit_brouillon]
|
||||
before_action :set_dossier_as_editing_fork, only: [:submit_en_construction]
|
||||
before_action :show_demarche_en_test_banner
|
||||
before_action :store_user_location!, only: :new
|
||||
|
||||
|
@ -49,6 +50,9 @@ module Users
|
|||
end.page(page)
|
||||
|
||||
@first_brouillon_recently_updated = current_user.dossiers.visible_by_user.brouillons_recently_updated.first
|
||||
|
||||
@filter = DossiersFilter.new(current_user, params)
|
||||
@dossiers = @filter.filter_procedures(@dossiers).page(page)
|
||||
end
|
||||
|
||||
def show
|
||||
|
@ -73,7 +77,7 @@ module Users
|
|||
|
||||
def attestation
|
||||
if dossier.attestation&.pdf&.attached?
|
||||
redirect_to dossier.attestation.pdf.service_url
|
||||
redirect_to dossier.attestation.pdf.url, allow_other_host: true
|
||||
else
|
||||
flash.notice = t('.no_longer_available')
|
||||
redirect_to dossier_path(dossier)
|
||||
|
@ -171,6 +175,7 @@ module Users
|
|||
errors = submit_dossier_and_compute_errors
|
||||
|
||||
if errors.blank?
|
||||
RoutingEngine.compute(@dossier)
|
||||
@dossier.passer_en_construction!
|
||||
@dossier.process_declarative!
|
||||
NotificationMailer.send_en_construction_notification(@dossier).deliver_later
|
||||
|
@ -199,21 +204,46 @@ module Users
|
|||
@dossier = dossier_with_champs
|
||||
end
|
||||
|
||||
def update_brouillon
|
||||
@dossier = dossier_with_champs
|
||||
update_dossier_and_compute_errors
|
||||
|
||||
# Transition to en_construction forks,
|
||||
# so users editing en_construction dossiers won't completely break their changes.
|
||||
# TODO: remove me after fork en_construction feature deploy (PR #8790)
|
||||
def modifier_legacy
|
||||
respond_to do |format|
|
||||
format.html { render :brouillon }
|
||||
format.turbo_stream do
|
||||
@to_show, @to_hide, @to_update = champs_to_turbo_update(champs_public_params.fetch(:champs_public_all_attributes), dossier.champs_public_all)
|
||||
flash.alert = "Une mise à jour de cette page est nécessaire pour poursuivre, veuillez la recharger (touche F5). Attention: le dernier champ modifié n’a pas été sauvegardé, vous devrez le ressaisir."
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
render(:update, layout: false)
|
||||
def submit_en_construction
|
||||
@dossier = dossier_with_champs(pj_template: false)
|
||||
errors = submit_dossier_and_compute_errors
|
||||
|
||||
if errors.blank?
|
||||
editing_fork_origin = @dossier.editing_fork_origin
|
||||
editing_fork_origin.merge_fork(@dossier)
|
||||
RoutingEngine.compute(editing_fork_origin)
|
||||
|
||||
redirect_to dossier_path(editing_fork_origin)
|
||||
else
|
||||
flash.now.alert = errors
|
||||
|
||||
respond_to do |format|
|
||||
format.html do
|
||||
@dossier = @dossier.editing_fork_origin
|
||||
render :modifier
|
||||
end
|
||||
|
||||
format.turbo_stream do
|
||||
@to_show, @to_hide, @to_update = champs_to_turbo_update(champs_public_params.fetch(:champs_public_all_attributes), dossier.champs_public_all)
|
||||
render :update, layout: false
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
def update
|
||||
@dossier = dossier.en_construction? ? dossier.find_editing_fork(dossier.user) : dossier
|
||||
@dossier = dossier_with_champs(pj_template: false)
|
||||
errors = update_dossier_and_compute_errors
|
||||
|
||||
|
@ -222,9 +252,9 @@ module Users
|
|||
end
|
||||
|
||||
respond_to do |format|
|
||||
format.html { render :modifier }
|
||||
format.turbo_stream do
|
||||
@to_show, @to_hide, @to_update = champs_to_turbo_update(champs_public_params.fetch(:champs_public_all_attributes), dossier.champs_public_all)
|
||||
render :update, layout: false
|
||||
end
|
||||
end
|
||||
end
|
||||
|
@ -422,8 +452,8 @@ module Users
|
|||
end
|
||||
|
||||
def dossier_scope
|
||||
if action_name == 'update_brouillon'
|
||||
Dossier.visible_by_user.or(Dossier.for_procedure_preview)
|
||||
if action_name == 'update'
|
||||
Dossier.visible_by_user.or(Dossier.for_procedure_preview).or(Dossier.for_editing_fork)
|
||||
elsif action_name == 'restore'
|
||||
Dossier.hidden_by_user
|
||||
else
|
||||
|
@ -441,6 +471,15 @@ module Users
|
|||
DossierPreloader.load_one(dossier, pj_template:)
|
||||
end
|
||||
|
||||
def set_dossier_as_editing_fork
|
||||
@dossier = dossier.find_editing_fork(dossier.user)
|
||||
|
||||
return if @dossier.present?
|
||||
|
||||
flash[:alert] = t('users.dossiers.en_construction_submitted')
|
||||
redirect_to dossier_path(dossier)
|
||||
end
|
||||
|
||||
def should_change_groupe_instructeur?
|
||||
if params[:dossier].key?(:groupe_instructeur_id)
|
||||
groupe_instructeur_id = params[:dossier][:groupe_instructeur_id]
|
||||
|
@ -469,27 +508,18 @@ module Users
|
|||
|
||||
def update_dossier_and_compute_errors
|
||||
errors = []
|
||||
|
||||
@dossier.assign_attributes(champs_public_params)
|
||||
if @dossier.champs_public_all.any?(&:changed_for_autosave?)
|
||||
@dossier.last_champ_updated_at = Time.zone.now
|
||||
end
|
||||
if !@dossier.save(**validation_options)
|
||||
errors += @dossier.errors.full_messages
|
||||
errors += format_errors(errors: @dossier.errors)
|
||||
end
|
||||
|
||||
if should_change_groupe_instructeur?
|
||||
@dossier.assign_to_groupe_instructeur(groupe_instructeur_from_params)
|
||||
end
|
||||
|
||||
if @dossier.procedure.feature_enabled?(:routing_rules)
|
||||
RoutingEngine.compute(@dossier)
|
||||
end
|
||||
|
||||
if dossier.en_construction?
|
||||
errors += @dossier.check_mandatory_and_visible_champs
|
||||
end
|
||||
|
||||
errors
|
||||
end
|
||||
|
||||
|
@ -497,20 +527,46 @@ module Users
|
|||
errors = []
|
||||
|
||||
@dossier.valid?(**submit_validation_options)
|
||||
errors += @dossier.errors.full_messages
|
||||
errors += @dossier.check_mandatory_and_visible_champs
|
||||
errors += format_errors(errors: @dossier.errors)
|
||||
errors += format_errors(errors: @dossier.check_mandatory_and_visible_champs)
|
||||
|
||||
if should_fill_groupe_instructeur?
|
||||
@dossier.assign_to_groupe_instructeur(defaut_groupe_instructeur)
|
||||
end
|
||||
|
||||
if @dossier.groupe_instructeur.nil?
|
||||
errors << "Le champ « #{@dossier.procedure.routing_criteria_name} » doit être rempli"
|
||||
if !@dossier.procedure.feature_enabled?(:routing_rules) && @dossier.groupe_instructeur.nil?
|
||||
errors += format_errors(errors: ["Le champ « #{@dossier.procedure.routing_criteria_name} » doit être rempli"])
|
||||
end
|
||||
|
||||
errors
|
||||
end
|
||||
|
||||
def format_errors(errors:)
|
||||
errors.map do |active_model_error|
|
||||
case active_model_error.class.name
|
||||
when "ActiveModel::NestedError"
|
||||
append_anchor_link(active_model_error.full_message, active_model_error.inner_error.base)
|
||||
when "ActiveModel::Error"
|
||||
append_anchor_link(active_model_error.full_message, active_model_error.base)
|
||||
else # "String"
|
||||
active_model_error
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
def append_anchor_link(str_error, model)
|
||||
return str_error.full_message if !model.is_a?(Champ)
|
||||
|
||||
route_helper = @dossier.editing_fork? ? :modifier_dossier_path : :brouillon_dossier_path
|
||||
|
||||
[
|
||||
"Le champ « #{model.libelle.truncate(200)} » #{str_error}",
|
||||
helpers.link_to(t('views.users.dossiers.fix_champ'), public_send(route_helper, anchor: model.labelledby_id), class: 'error-anchor')
|
||||
].join(", ")
|
||||
rescue # case of invalid type de champ on champ
|
||||
str_error
|
||||
end
|
||||
|
||||
def ensure_ownership!
|
||||
if !current_user.owns?(dossier)
|
||||
forbidden!
|
||||
|
|
|
@ -36,7 +36,7 @@ class Users::SessionsController < Devise::SessionsController
|
|||
|
||||
case connected_with_france_connect
|
||||
when User.loged_in_with_france_connects.fetch(:particulier)
|
||||
redirect_to FRANCE_CONNECT[:particulier][:logout_endpoint]
|
||||
redirect_to FRANCE_CONNECT[:particulier][:logout_endpoint], allow_other_host: true
|
||||
return
|
||||
end
|
||||
end
|
||||
|
|
|
@ -134,7 +134,6 @@ class API::V2::Schema < GraphQL::Schema
|
|||
class Timeout < GraphQL::Schema::Timeout
|
||||
def handle_timeout(error, query)
|
||||
error.extensions = { code: :timeout }
|
||||
Sentry.capture_exception(error, extra: query.context.query_info)
|
||||
end
|
||||
end
|
||||
|
||||
|
|
|
@ -263,6 +263,7 @@ class API::V2::StoredQuery
|
|||
usager {
|
||||
email
|
||||
}
|
||||
connectionUsager
|
||||
groupeInstructeur {
|
||||
...GroupeInstructeurFragment
|
||||
}
|
||||
|
|
|
@ -42,7 +42,10 @@ module Loaders
|
|||
end
|
||||
|
||||
def preload_association(records)
|
||||
::ActiveRecord::Associations::Preloader.new.preload(records, @association_schema)
|
||||
::ActiveRecord::Associations::Preloader.new(
|
||||
records: records,
|
||||
associations: @association_schema
|
||||
).call
|
||||
end
|
||||
|
||||
def read_association(record)
|
||||
|
|
|
@ -11,7 +11,7 @@ module Mutations
|
|||
field :errors, [Types::ValidationErrorType], null: true
|
||||
|
||||
def resolve(dossier:, groupe_instructeur:)
|
||||
dossier.update!(groupe_instructeur:)
|
||||
dossier.assign_to_groupe_instructeur(groupe_instructeur)
|
||||
|
||||
{ dossier: }
|
||||
end
|
||||
|
|
|
@ -28,7 +28,9 @@ module Mutations
|
|||
end
|
||||
|
||||
def authorized?(dossier:, instructeur:, **args)
|
||||
if !dossier.en_instruction?
|
||||
if dossier.en_instruction? && dossier.any_etablissement_as_degraded_mode?
|
||||
return false, { errors: ["Les informations du SIRET du dossier ne sont pas complètes. Veuillez réessayer plus tard."] }
|
||||
elsif !dossier.en_instruction? || !dossier.can_terminer?
|
||||
return false, { errors: ["Le dossier est déjà #{dossier_display_state(dossier, lower: true)}"] }
|
||||
end
|
||||
dossier_authorized_for?(dossier, instructeur)
|
||||
|
|
|
@ -28,7 +28,9 @@ module Mutations
|
|||
end
|
||||
|
||||
def authorized?(dossier:, instructeur:, **args)
|
||||
if !dossier.en_instruction?
|
||||
if dossier.en_instruction? && dossier.any_etablissement_as_degraded_mode?
|
||||
return false, { errors: ["Les informations du SIRET du dossier ne sont pas complètes. Veuillez réessayer plus tard."] }
|
||||
elsif !dossier.en_instruction? || !dossier.can_terminer?
|
||||
return false, { errors: ["Le dossier est déjà #{dossier_display_state(dossier, lower: true)}"] }
|
||||
end
|
||||
dossier_authorized_for?(dossier, instructeur)
|
||||
|
|
|
@ -462,6 +462,23 @@ type CommuneChampDescriptor implements ChampDescriptor {
|
|||
type: TypeDeChamp! @deprecated(reason: "Utilisez le champ `__typename` à la place.")
|
||||
}
|
||||
|
||||
enum ConnectionUsager {
|
||||
"""
|
||||
Compte supprimé
|
||||
"""
|
||||
deleted
|
||||
|
||||
"""
|
||||
Connexion via FranceConnect
|
||||
"""
|
||||
france_connect
|
||||
|
||||
"""
|
||||
Connexion via mot de passe
|
||||
"""
|
||||
password
|
||||
}
|
||||
|
||||
"""
|
||||
GeoJSON coordinates
|
||||
"""
|
||||
|
@ -1199,6 +1216,7 @@ type Dossier {
|
|||
attestation: File
|
||||
avis(id: ID): [Avis!]!
|
||||
champs(id: ID): [Champ!]!
|
||||
connectionUsager: ConnectionUsager!
|
||||
|
||||
"""
|
||||
Date de dépôt.
|
||||
|
|
|
@ -6,6 +6,12 @@ module Types
|
|||
end
|
||||
end
|
||||
|
||||
class ConnectionUsager < Types::BaseEnum
|
||||
value(:france_connect, "Connexion via FranceConnect", value: :france_connect)
|
||||
value(:password, "Connexion via mot de passe", value: :password)
|
||||
value(:deleted, "Compte supprimé", value: :deleted)
|
||||
end
|
||||
|
||||
description "Un dossier"
|
||||
|
||||
global_id_field :id
|
||||
|
@ -26,6 +32,8 @@ module Types
|
|||
|
||||
field :archived, Boolean, null: false
|
||||
|
||||
field :connection_usager, ConnectionUsager, null: false
|
||||
|
||||
field :motivation, String, null: true
|
||||
field :motivation_attachment, Types::File, null: true, extensions: [
|
||||
{ Extensions::Attachment => { attachment: :justificatif_motivation } }
|
||||
|
@ -67,11 +75,25 @@ module Types
|
|||
end
|
||||
end
|
||||
|
||||
def connection_usager
|
||||
if object.user_deleted?
|
||||
:deleted
|
||||
else
|
||||
user_loader.then do |user|
|
||||
if user.france_connect_information.present?
|
||||
:france_connect
|
||||
else
|
||||
:password
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
def usager
|
||||
if object.user_deleted?
|
||||
{ email: object.user_email_for(:display), id: '<deleted>' }
|
||||
else
|
||||
Loaders::Record.for(User).load(object.user_id)
|
||||
user_loader
|
||||
end
|
||||
end
|
||||
|
||||
|
@ -173,5 +195,11 @@ module Types
|
|||
def self.authorized?(object, context)
|
||||
context.authorized_demarche?(object.revision.procedure)
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def user_loader
|
||||
Loaders::Record.for(User, includes: :france_connect_information).load(object.user_id)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
@ -11,7 +11,7 @@ module Types
|
|||
if object.is_a?(Hash)
|
||||
object[:url]
|
||||
else
|
||||
object.service_url
|
||||
object.url
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
@ -1,39 +0,0 @@
|
|||
module FormTagHelper
|
||||
# from Rails 7 ActionView::Helpers::FormTagHelper
|
||||
# https://api.rubyonrails.org/classes/ActionView/Helpers/FormTagHelper.html#method-i-field_id
|
||||
# Should be removed when we upgrade to Rails 7
|
||||
def field_id(object_name, method_name, *suffixes, index: nil, namespace: nil)
|
||||
if object_name.respond_to?(:model_name)
|
||||
object_name = object_name.model_name.singular
|
||||
end
|
||||
|
||||
sanitized_object_name = object_name.to_s.gsub(/\]\[|[^-a-zA-Z0-9:.]/, "_").delete_suffix("_")
|
||||
|
||||
sanitized_method_name = method_name.to_s.delete_suffix("?")
|
||||
|
||||
[
|
||||
namespace,
|
||||
sanitized_object_name.presence,
|
||||
(index unless sanitized_object_name.empty?),
|
||||
sanitized_method_name,
|
||||
*suffixes
|
||||
].tap(&:compact!).join("_")
|
||||
end
|
||||
|
||||
# from Rails 7 ActionView::Helpers::FormTagHelper
|
||||
# https://api.rubyonrails.org/classes/ActionView/Helpers/FormTagHelper.html#method-i-field_name
|
||||
# Should be removed when we upgrade to Rails 7
|
||||
def field_name(object_name, method_name, *method_names, multiple: false, index: nil)
|
||||
names = method_names.map! { |name| "[#{name}]" }.join
|
||||
|
||||
# a little duplication to construct fewer strings
|
||||
case
|
||||
when object_name.blank?
|
||||
"#{method_name}#{names}#{multiple ? "[]" : ""}"
|
||||
when index
|
||||
"#{object_name}[#{index}][#{method_name}]#{names}#{multiple ? "[]" : ""}"
|
||||
else
|
||||
"#{object_name}[#{method_name}]#{names}#{multiple ? "[]" : ""}"
|
||||
end
|
||||
end
|
||||
end
|
|
@ -1,3 +1,4 @@
|
|||
import 'core-js/proposals/relative-indexing-method';
|
||||
import Rails from '@rails/ujs';
|
||||
import * as ActiveStorage from '@rails/activestorage';
|
||||
import * as Turbo from '@hotwired/turbo';
|
||||
|
|
|
@ -35,3 +35,4 @@
|
|||
@import '@gouvfr/dsfr/dist/component/pagination/pagination.css';
|
||||
@import '@gouvfr/dsfr/dist/component/skiplink/skiplink.css';
|
||||
@import '@gouvfr/dsfr/dist/component/password/password.css';
|
||||
@import '@gouvfr/dsfr/dist/component/accordion/accordion.css';
|
||||
|
|
|
@ -2,13 +2,14 @@ import * as Sentry from '@sentry/browser';
|
|||
import { getConfig } from '@utils';
|
||||
|
||||
const {
|
||||
sentry: { key, enabled, user, environment, browser }
|
||||
sentry: { key, enabled, user, environment, browser, release }
|
||||
} = getConfig();
|
||||
|
||||
// We need to check for key presence here as we do not have a dsn for browser yet
|
||||
if (enabled && key) {
|
||||
Sentry.init({
|
||||
dsn: key,
|
||||
release: release ?? undefined,
|
||||
environment,
|
||||
tracesSampleRate: 0.1,
|
||||
ignoreErrors: [
|
||||
|
|
|
@ -32,7 +32,8 @@ const Gon = z
|
|||
enabled: z.boolean().default(false),
|
||||
environment: z.string().optional(),
|
||||
user: z.object({ id: z.string() }).default({ id: '' }),
|
||||
browser: z.object({ modern: z.boolean() }).default({ modern: false })
|
||||
browser: z.object({ modern: z.boolean() }).default({ modern: false }),
|
||||
release: z.string().nullish()
|
||||
})
|
||||
.default({}),
|
||||
crisp: z
|
||||
|
|
|
@ -5,7 +5,7 @@ class Cron::Datagouv::ExportAndPublishDemarchesPubliquesJob < Cron::CronJob
|
|||
def perform(*args)
|
||||
gzip_filepath = [
|
||||
'tmp/',
|
||||
Time.zone.now.to_formatted_s(:number),
|
||||
Time.zone.now.to_fs(:number),
|
||||
'-demarches.json.gz'
|
||||
].join
|
||||
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
class Cron::InstructeurEmailNotificationJob < Cron::CronJob
|
||||
self.schedule_expression = "from monday through friday at 10 am"
|
||||
self.schedule_expression = "from monday through friday at 9 am"
|
||||
|
||||
def perform(*args)
|
||||
NotificationService.send_instructeur_email_notification
|
||||
|
|
7
app/jobs/destroy_record_later_job.rb
Normal file
7
app/jobs/destroy_record_later_job.rb
Normal file
|
@ -0,0 +1,7 @@
|
|||
class DestroyRecordLaterJob < ApplicationJob
|
||||
discard_on ActiveRecord::RecordNotFound
|
||||
|
||||
def perform(record)
|
||||
record.destroy
|
||||
end
|
||||
end
|
8
app/jobs/dossier_update_search_terms_job.rb
Normal file
8
app/jobs/dossier_update_search_terms_job.rb
Normal file
|
@ -0,0 +1,8 @@
|
|||
class DossierUpdateSearchTermsJob < ApplicationJob
|
||||
discard_on ActiveRecord::RecordNotFound
|
||||
|
||||
def perform(dossier)
|
||||
dossier.update_search_terms
|
||||
dossier.save!(touch: false)
|
||||
end
|
||||
end
|
|
@ -1,14 +1,14 @@
|
|||
class APIEntreprise::API
|
||||
ENTREPRISE_RESOURCE_NAME = "entreprises"
|
||||
ETABLISSEMENT_RESOURCE_NAME = "etablissements"
|
||||
EXERCICES_RESOURCE_NAME = "exercices"
|
||||
RNA_RESOURCE_NAME = "associations"
|
||||
EFFECTIFS_RESOURCE_NAME = "effectifs_mensuels_acoss_covid"
|
||||
EFFECTIFS_ANNUELS_RESOURCE_NAME = "effectifs_annuels_acoss_covid"
|
||||
ATTESTATION_SOCIALE_RESOURCE_NAME = "attestations_sociales_acoss"
|
||||
ATTESTATION_FISCALE_RESOURCE_NAME = "attestations_fiscales_dgfip"
|
||||
BILANS_BDF_RESOURCE_NAME = "bilans_entreprises_bdf"
|
||||
PRIVILEGES_RESOURCE_NAME = "privileges"
|
||||
ENTREPRISE_RESOURCE_NAME = "v2/entreprises/%{id}"
|
||||
ETABLISSEMENT_RESOURCE_NAME = "v2/etablissements/%{id}"
|
||||
EXERCICES_RESOURCE_NAME = "v2/exercices/%{id}"
|
||||
RNA_RESOURCE_NAME = "v2/associations/%{id}"
|
||||
EFFECTIFS_RESOURCE_NAME = "v2/effectifs_mensuels_acoss_covid"
|
||||
EFFECTIFS_ANNUELS_RESOURCE_NAME = "v2/effectifs_annuels_acoss_covid/%{id}"
|
||||
ATTESTATION_SOCIALE_RESOURCE_NAME = "v4/urssaf/unites_legales/%{id}/attestation_vigilance"
|
||||
ATTESTATION_FISCALE_RESOURCE_NAME = "v2/attestations_fiscales_dgfip/%{id}"
|
||||
BILANS_BDF_RESOURCE_NAME = "v2/bilans_entreprises_bdf/%{id}"
|
||||
PRIVILEGES_RESOURCE_NAME = "v2/privileges"
|
||||
|
||||
TIMEOUT = 20
|
||||
DEFAULT_API_ENTREPRISE_DELAY = 0.0
|
||||
|
@ -41,7 +41,7 @@ class APIEntreprise::API
|
|||
end
|
||||
|
||||
def effectifs(siren, annee, mois)
|
||||
endpoint = [EFFECTIFS_RESOURCE_NAME, annee, mois, "entreprise"].join('/')
|
||||
endpoint = [EFFECTIFS_RESOURCE_NAME, annee, mois, "entreprise", "%{id}"].join('/')
|
||||
call_with_siret(endpoint, siren)
|
||||
end
|
||||
|
||||
|
@ -73,7 +73,7 @@ class APIEntreprise::API
|
|||
end
|
||||
|
||||
def current_status
|
||||
status_url = "https://entreprise.api.gouv.fr/watchdoge/dashboard/current_status"
|
||||
status_url = "https://status.entreprise.api.gouv.fr/summary.json"
|
||||
response = Typhoeus.get(status_url, timeout: 1)
|
||||
|
||||
handle_response(response)
|
||||
|
@ -127,7 +127,7 @@ class APIEntreprise::API
|
|||
end
|
||||
|
||||
def make_url(resource_name, siret_or_siren = nil)
|
||||
[API_ENTREPRISE_URL, resource_name, siret_or_siren].compact.join("/")
|
||||
[API_ENTREPRISE_URL, format(resource_name, id: siret_or_siren)].compact.join("/")
|
||||
end
|
||||
|
||||
def build_params(user_id)
|
||||
|
|
|
@ -11,9 +11,9 @@ class APIEntreprise::AttestationSocialeAdapter < APIEntreprise::Adapter
|
|||
end
|
||||
|
||||
def process_params
|
||||
if data_source[:url].present?
|
||||
if data_source[:data] && data_source[:data][:document_url].present?
|
||||
{
|
||||
entreprise_attestation_sociale_url: data_source[:url]
|
||||
entreprise_attestation_sociale_url: data_source[:data][:document_url]
|
||||
}
|
||||
else
|
||||
{}
|
||||
|
|
|
@ -50,7 +50,7 @@ class APIEntreprise::EntrepriseAdapter < APIEntreprise::Adapter
|
|||
|
||||
case raw_value
|
||||
when 'A' then 'actif'
|
||||
when 'F' then 'fermé'
|
||||
when 'F', 'C' then 'fermé'
|
||||
end
|
||||
end
|
||||
|
||||
|
|
|
@ -152,7 +152,18 @@ module Dolist
|
|||
}
|
||||
}.to_json
|
||||
|
||||
post(url, body)["FieldList"].find { _1['ID'] == 72 }['Value']
|
||||
fields = post(url, body).fetch("FieldList", [])
|
||||
|
||||
if fields.empty?
|
||||
Sentry.with_scope do |scope|
|
||||
scope.set_extra(:contact, email_address)
|
||||
Sentry.capture_message("Dolist::API: contact not found")
|
||||
end
|
||||
|
||||
return nil
|
||||
end
|
||||
|
||||
fields.find { _1['ID'] == 72 }.fetch('Value')
|
||||
end
|
||||
|
||||
def ignorable_error?(response, mail)
|
||||
|
|
|
@ -8,7 +8,7 @@ module DownloadManager
|
|||
|
||||
def initialize(attachments, destination)
|
||||
@attachments = attachments
|
||||
@destination = destination
|
||||
@destination = Pathname.new(destination)
|
||||
end
|
||||
|
||||
def download_all
|
||||
|
@ -28,26 +28,40 @@ module DownloadManager
|
|||
|
||||
# can't be used with typhoeus, otherwise block is closed before the request is run by hydra
|
||||
def download_one(attachment:, path_in_download_dir:, http_client:)
|
||||
attachment_path = File.join(destination, path_in_download_dir)
|
||||
attachment_dir = File.dirname(attachment_path)
|
||||
path = Pathname.new(path_in_download_dir)
|
||||
attachment_path = destination.join(path.dirname, sanitize_filename(path.basename.to_s))
|
||||
|
||||
attachment_path.dirname.mkpath # defensive, do not write in undefined dir
|
||||
|
||||
FileUtils.mkdir_p(attachment_dir) if !Dir.exist?(attachment_dir) # defensive, do not write in undefined dir
|
||||
if attachment.is_a?(ActiveStorage::FakeAttachment)
|
||||
File.write(attachment_path, attachment.file.read, mode: 'wb')
|
||||
attachment_path.write(attachment.file.read, mode: 'wb')
|
||||
else
|
||||
request = Typhoeus::Request.new(attachment.url)
|
||||
request.on_complete do |response|
|
||||
if response.success?
|
||||
File.open(attachment_path, mode: "wb") do |fd|
|
||||
attachment_path.open(mode: "wb") do |fd|
|
||||
fd.write(response.body)
|
||||
end
|
||||
else
|
||||
File.delete(attachment_path) if File.exist?(attachment_path) # -> case of retries failed, must cleanup partialy downloaded file
|
||||
attachment_path.delete if attachment_path.exist? # -> case of retries failed, must cleanup partialy downloaded file
|
||||
on_error.call(attachment, path_in_download_dir, response.code)
|
||||
end
|
||||
end
|
||||
http_client.queue(request)
|
||||
end
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def sanitize_filename(original_filename)
|
||||
filename = ActiveStorage::Filename.new(original_filename).sanitized
|
||||
|
||||
return filename if filename.bytesize <= 255
|
||||
|
||||
ext = File.extname(filename)
|
||||
basename = File.basename(filename, ext).byteslice(0, 255 - ext.bytesize)
|
||||
|
||||
basename + ext
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
69
app/lib/recovery/align_champ_with_dossier_revision.rb
Normal file
69
app/lib/recovery/align_champ_with_dossier_revision.rb
Normal file
|
@ -0,0 +1,69 @@
|
|||
class Recovery::AlignChampWithDossierRevision
|
||||
def initialize(dossiers, progress: nil)
|
||||
@dossiers = dossiers
|
||||
@progress = progress
|
||||
@logs = []
|
||||
end
|
||||
|
||||
attr_reader :logs
|
||||
|
||||
def run(destroy_extra_champs: false)
|
||||
@logs = []
|
||||
bad_dossier_ids = find_broken_dossier_ids
|
||||
|
||||
Dossier
|
||||
.where(id: bad_dossier_ids)
|
||||
.includes(:procedure, champs: { type_de_champ: :revisions })
|
||||
.find_each do |dossier|
|
||||
bad_champs = dossier.champs.filter { !dossier.revision_id.in?(_1.type_de_champ.revisions.ids) }
|
||||
bad_champs.each do |champ|
|
||||
type_de_champ = dossier.revision.types_de_champ.find { _1.stable_id == champ.stable_id }
|
||||
state = {
|
||||
champ_id: champ.id,
|
||||
champ_type_de_champ_id: champ.type_de_champ_id,
|
||||
dossier_id: dossier.id,
|
||||
dossier_revision_id: dossier.revision_id,
|
||||
procedure_id: dossier.procedure.id
|
||||
}
|
||||
if type_de_champ.present?
|
||||
logs << state.merge(status: :updated, type_de_champ_id: type_de_champ.id)
|
||||
champ.update_column(:type_de_champ_id, type_de_champ.id)
|
||||
else
|
||||
logs << state.merge(status: :not_found)
|
||||
champ.destroy! if destroy_extra_champs
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
def find_broken_dossier_ids
|
||||
bad_dossier_ids = []
|
||||
|
||||
@dossiers.in_batches(of: 15_000) do |dossiers|
|
||||
dossier_ids_revision_ids = dossiers.pluck(:id, :revision_id)
|
||||
dossier_ids = dossier_ids_revision_ids.map(&:first)
|
||||
dossier_ids_type_de_champ_ids = Champ.where(dossier_id: dossier_ids).pluck(:dossier_id, :type_de_champ_id)
|
||||
type_de_champ_ids = dossier_ids_type_de_champ_ids.map(&:second).uniq
|
||||
revision_ids_by_type_de_champ_id = ProcedureRevisionTypeDeChamp
|
||||
.where(type_de_champ_id: type_de_champ_ids)
|
||||
.pluck(:type_de_champ_id, :revision_id)
|
||||
.group_by(&:first).transform_values { _1.map(&:second).uniq }
|
||||
|
||||
type_de_champ_ids_by_dossier_id = dossier_ids_type_de_champ_ids
|
||||
.group_by(&:first)
|
||||
.transform_values { _1.map(&:second).uniq }
|
||||
|
||||
bad_dossier_ids += dossier_ids_revision_ids.filter do |(dossier_id, revision_id)|
|
||||
type_de_champ_ids_by_dossier_id.fetch(dossier_id, []).any? do |type_de_champ_id|
|
||||
!revision_id.in?(revision_ids_by_type_de_champ_id.fetch(type_de_champ_id, []))
|
||||
end
|
||||
end.map(&:first)
|
||||
|
||||
@progress.inc(dossiers.count) if @progress
|
||||
end
|
||||
|
||||
@progress.finish if @progress
|
||||
|
||||
bad_dossier_ids
|
||||
end
|
||||
end
|
30
app/lib/recovery/exporter.rb
Normal file
30
app/lib/recovery/exporter.rb
Normal file
|
@ -0,0 +1,30 @@
|
|||
module Recovery
|
||||
class Exporter
|
||||
FILE_PATH = Rails.root.join('lib', 'data', 'export.dump')
|
||||
|
||||
attr_reader :dossiers, :file_path
|
||||
def initialize(dossier_ids:, file_path: FILE_PATH)
|
||||
dossier_with_data = Dossier.where(id: dossier_ids)
|
||||
.preload(:user,
|
||||
:individual,
|
||||
:invites,
|
||||
:traitements,
|
||||
:transfer_logs,
|
||||
commentaires: { piece_jointe_attachment: :blob },
|
||||
avis: { introduction_file_attachment: :blob, piece_justificative_file_attachment: :blob },
|
||||
dossier_operation_logs: { serialized_attachment: :blob },
|
||||
attestation: { pdf_attachment: :blob },
|
||||
justificatif_motivation_attachment: :blob,
|
||||
etablissement: :exercices,
|
||||
revision: :procedure)
|
||||
@dossiers = DossierPreloader.new(dossier_with_data,
|
||||
includes_for_dossier: [:geo_areas, etablissement: :exercices],
|
||||
includes_for_etablissement: [:exercices]).all
|
||||
@file_path = file_path
|
||||
end
|
||||
|
||||
def dump
|
||||
@file_path.binwrite(Marshal.dump(@dossiers))
|
||||
end
|
||||
end
|
||||
end
|
120
app/lib/recovery/importer.rb
Normal file
120
app/lib/recovery/importer.rb
Normal file
|
@ -0,0 +1,120 @@
|
|||
module Recovery
|
||||
class Importer
|
||||
attr_reader :dossiers
|
||||
|
||||
def initialize(file_path: Recovery::Exporter::FILE_PATH)
|
||||
# rubocop:disable Security/MarshalLoad
|
||||
@dossiers = Marshal.load(File.read(file_path))
|
||||
# rubocop:enable Security/MarshalLoad
|
||||
end
|
||||
|
||||
def load
|
||||
@dossiers.each do |dossier|
|
||||
if Dossier.exists?(dossier.id)
|
||||
puts "Dossier #{dossier.id} already exists"
|
||||
next
|
||||
end
|
||||
dossier.instance_variable_set :@new_record, true
|
||||
dossier_attributes = dossier.attributes.dup
|
||||
|
||||
parent_dossier_id = dossier_attributes['parent_dossier_id']
|
||||
if parent_dossier_id && !Dossier.exists?(id: parent_dossier_id)
|
||||
dossier_attributes.delete('parent_dossier_id')
|
||||
end
|
||||
|
||||
Dossier.insert(dossier_attributes)
|
||||
|
||||
if dossier.etablissement.present?
|
||||
Etablissement.insert(dossier.etablissement.attributes)
|
||||
if dossier.etablissement.present?
|
||||
APIEntreprise::EntrepriseJob.perform_later(dossier.etablissement.id, dossier.procedure.id)
|
||||
end
|
||||
|
||||
dossier.etablissement.exercices.each do |exercice|
|
||||
Exercice.insert(exercice.attributes)
|
||||
end
|
||||
end
|
||||
if dossier.individual.present?
|
||||
Individual.insert(dossier.individual.attributes)
|
||||
end
|
||||
|
||||
dossier.invites.each do |invite|
|
||||
Invite.insert(invite.attributes)
|
||||
end
|
||||
|
||||
dossier.traitements.each do |traitement|
|
||||
Traitement.insert(traitement.attributes)
|
||||
end
|
||||
|
||||
dossier.transfer_logs.each do |transfer|
|
||||
DossierTransferLog.insert(transfer.attributes)
|
||||
end
|
||||
|
||||
dossier.commentaires.each do |commentaire|
|
||||
Commentaire.insert(commentaire.attributes)
|
||||
if commentaire.piece_jointe.attached?
|
||||
import(commentaire.piece_jointe)
|
||||
end
|
||||
end
|
||||
|
||||
dossier.avis.each do |avis|
|
||||
Avis.insert(avis.attributes)
|
||||
|
||||
if avis.introduction_file.attached?
|
||||
import(avis.introduction_file)
|
||||
end
|
||||
|
||||
if avis.piece_justificative_file.attached?
|
||||
import(avis.piece_justificative_file)
|
||||
end
|
||||
end
|
||||
dossier.dossier_operation_logs.each do |dol|
|
||||
if dol.operation.nil?
|
||||
puts "dol nil: #{dol.id}"
|
||||
next
|
||||
end
|
||||
DossierOperationLog.insert(dol.attributes)
|
||||
|
||||
if dol.serialized.attached?
|
||||
import(dol.serialized)
|
||||
end
|
||||
end
|
||||
|
||||
if dossier.attestation.present?
|
||||
Attestation.insert(dossier.attestation.attributes)
|
||||
import(dossier.attestation.pdf)
|
||||
end
|
||||
|
||||
if dossier.justificatif_motivation.attached?
|
||||
import(dossier.justificatif_motivation)
|
||||
end
|
||||
|
||||
dossier.champs.each do |champ|
|
||||
champ.piece_justificative_file.each { |pj| import(pj) }
|
||||
|
||||
if champ.etablissement.present?
|
||||
APIEntreprise::EntrepriseJob.perform_later(champ.etablissement.id, dossier.procedure.id)
|
||||
|
||||
champ.etablissement.exercices.each do |exercice|
|
||||
Exercice.insert(exercice.attributes)
|
||||
end
|
||||
|
||||
Etablissement.insert(champ.etablissement.attributes)
|
||||
end
|
||||
|
||||
Champ.insert(champ.attributes)
|
||||
|
||||
if champ.geo_areas.present?
|
||||
champ.geo_areas.each { GeoArea.insert(_1.attributes) }
|
||||
end
|
||||
end
|
||||
puts "imported dossier #{dossier.id}: #{Dossier.exists?(dossier.id)}"
|
||||
end
|
||||
end
|
||||
|
||||
def import(pj)
|
||||
ActiveStorage::Blob.insert(pj.blob.attributes)
|
||||
ActiveStorage::Attachment.insert(pj.attributes)
|
||||
end
|
||||
end
|
||||
end
|
17
app/lib/recovery/revision_exporter.rb
Normal file
17
app/lib/recovery/revision_exporter.rb
Normal file
|
@ -0,0 +1,17 @@
|
|||
module Recovery
|
||||
class RevisionExporter
|
||||
FILE_PATH = Rails.root.join('lib', 'data', 'revision', 'export.dump')
|
||||
|
||||
attr_reader :revisions, :file_path
|
||||
def initialize(revision_ids:, file_path: FILE_PATH)
|
||||
@revisions = ProcedureRevision.where(id: revision_ids)
|
||||
.preload(revision_types_de_champ: :type_de_champ)
|
||||
.to_a
|
||||
@file_path = file_path
|
||||
end
|
||||
|
||||
def dump
|
||||
@file_path.binwrite(Marshal.dump(@revisions))
|
||||
end
|
||||
end
|
||||
end
|
22
app/lib/recovery/revision_importer.rb
Normal file
22
app/lib/recovery/revision_importer.rb
Normal file
|
@ -0,0 +1,22 @@
|
|||
module Recovery
|
||||
class RevisionImporter
|
||||
attr_reader :revisions
|
||||
|
||||
def initialize(file_path: Recovery::RevisionExporter::FILE_PATH)
|
||||
# rubocop:disable Security/MarshalLoad
|
||||
@revisions = Marshal.load(File.read(file_path))
|
||||
# rubocop:enable Security/MarshalLoad
|
||||
end
|
||||
|
||||
def load
|
||||
@revisions.each do |revision|
|
||||
ProcedureRevisionTypeDeChamp.transaction do
|
||||
revision.revision_types_de_champ.each do |coordinate|
|
||||
ProcedureRevisionTypeDeChamp.upsert(coordinate.attributes)
|
||||
TypeDeChamp.upsert(coordinate.type_de_champ.attributes.except('type_champs'))
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
|
@ -128,10 +128,6 @@ class BatchOperation < ApplicationRecord
|
|||
dossier_operations.size
|
||||
end
|
||||
|
||||
def progress_count
|
||||
dossier_operations.pending.size
|
||||
end
|
||||
|
||||
def success_count
|
||||
dossier_operations.success.size
|
||||
end
|
||||
|
|
|
@ -5,7 +5,7 @@
|
|||
# id :integer not null, primary key
|
||||
# data :jsonb
|
||||
# fetch_external_data_exceptions :string is an Array
|
||||
# prefilled :boolean default(FALSE)
|
||||
# prefilled :boolean
|
||||
# private :boolean default(FALSE), not null
|
||||
# rebased_at :datetime
|
||||
# type :string
|
||||
|
@ -75,8 +75,8 @@ class Champ < ApplicationRecord
|
|||
:mandatory?,
|
||||
:prefillable?,
|
||||
:refresh_after_update?,
|
||||
:textarea_character_limit?,
|
||||
:textarea_character_limit,
|
||||
:character_limit?,
|
||||
:character_limit,
|
||||
to: :type_de_champ
|
||||
|
||||
delegate :to_typed_id, :to_typed_id_for_query, to: :type_de_champ, prefix: true
|
||||
|
@ -111,6 +111,10 @@ class Champ < ApplicationRecord
|
|||
parent_id.present?
|
||||
end
|
||||
|
||||
def stable_id_with_row
|
||||
[row_id, stable_id].compact
|
||||
end
|
||||
|
||||
def sections
|
||||
@sections ||= dossier.sections_for(self)
|
||||
end
|
||||
|
@ -226,10 +230,10 @@ class Champ < ApplicationRecord
|
|||
update!(data: data)
|
||||
end
|
||||
|
||||
def clone
|
||||
def clone(fork = false)
|
||||
champ_attributes = [:parent_id, :private, :row_id, :type, :type_de_champ_id]
|
||||
value_attributes = private? ? [] : [:value, :value_json, :data, :external_id]
|
||||
relationships = private? ? [] : [:etablissement, :geo_areas]
|
||||
value_attributes = fork || !private? ? [:value, :value_json, :data, :external_id] : []
|
||||
relationships = fork || !private? ? [:etablissement, :geo_areas] : []
|
||||
|
||||
deep_clone(only: champ_attributes + value_attributes, include: relationships) do |original, kopy|
|
||||
PiecesJustificativesService.clone_attachments(original, kopy)
|
||||
|
@ -240,6 +244,10 @@ class Champ < ApplicationRecord
|
|||
input_id
|
||||
end
|
||||
|
||||
def forked_with_changes?
|
||||
public? && dossier.champ_forked_with_changes?(self)
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def html_id
|
||||
|
|
|
@ -17,6 +17,7 @@
|
|||
# etablissement_id :integer
|
||||
# external_id :string
|
||||
# parent_id :bigint
|
||||
# row_id :string
|
||||
# type_de_champ_id :integer
|
||||
#
|
||||
class Champs::BooleanChamp < Champ
|
||||
|
|
|
@ -21,11 +21,4 @@
|
|||
# type_de_champ_id :integer
|
||||
#
|
||||
class Champs::EmailChamp < Champs::TextChamp
|
||||
validates :value,
|
||||
format: {
|
||||
with: Devise.email_regexp,
|
||||
message: I18n.t('invalid', scope: 'activerecord.errors.models.email_champ.attributes.value')
|
||||
},
|
||||
allow_nil: true,
|
||||
if: -> { validation_context != :brouillon }
|
||||
end
|
||||
|
|
|
@ -55,7 +55,7 @@ class Champs::PieceJustificativeChamp < Champ
|
|||
return nil if attachment.nil?
|
||||
|
||||
if attachment.virus_scanner.safe? || attachment.virus_scanner.pending?
|
||||
attachment.service_url
|
||||
attachment.url
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
@ -13,12 +13,12 @@
|
|||
# value_json :jsonb
|
||||
# created_at :datetime
|
||||
# updated_at :datetime
|
||||
# dossier_id :integer not null
|
||||
# dossier_id :integer
|
||||
# etablissement_id :integer
|
||||
# external_id :string
|
||||
# parent_id :bigint
|
||||
# type_de_champ_id :integer not null
|
||||
# row_id :string
|
||||
# type_de_champ_id :integer
|
||||
#
|
||||
class Champs::RNAChamp < Champ
|
||||
include RNAChampAssociationFetchableConcern
|
||||
|
|
Some files were not shown because too many files have changed in this diff Show more
Loading…
Add table
Reference in a new issue