Merge branch 'main' into feature-ouidou/existing_procedure_hidden_as_template

This commit is contained in:
seb-by-ouidou 2023-09-21 15:53:38 +02:00 committed by GitHub
commit d29bbf6d4f
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
490 changed files with 7042 additions and 5168 deletions

View file

@ -20,7 +20,8 @@ module.exports = {
'prettier/prettier': 'error', 'prettier/prettier': 'error',
'react-hooks/rules-of-hooks': 'error', 'react-hooks/rules-of-hooks': 'error',
'react-hooks/exhaustive-deps': 'error', 'react-hooks/exhaustive-deps': 'error',
'react/prop-types': 'off' 'react/prop-types': 'off',
'react/no-deprecated': 'off'
}, },
settings: { settings: {
react: { version: 'detect' } react: { version: 'detect' }

View file

@ -12,7 +12,12 @@ runs:
- name: Setup Node - name: Setup Node
uses: actions/setup-node@v3 uses: actions/setup-node@v3
with: with:
cache: 'yarn' node-version-file: '.node-version'
- name: Install yarn
run: |
npm install --global yarn
shell: bash
- name: Install Node modules - name: Install Node modules
run: | run: |

View file

@ -75,6 +75,9 @@ jobs:
- name: Setup the app runtime and dependencies - name: Setup the app runtime and dependencies
uses: ./.github/actions/ci-setup-rails uses: ./.github/actions/ci-setup-rails
- name: Install fonts pickable by ImageMagick
run: sudo apt-get install -y gsfonts
- name: Pre-compile assets - name: Pre-compile assets
uses: ./.github/actions/ci-setup-assets uses: ./.github/actions/ci-setup-assets
@ -97,7 +100,7 @@ jobs:
system_tests: system_tests:
name: System tests name: System tests
runs-on: ubuntu-latest runs-on: self-hosted
services: services:
postgres: postgres:
image: postgis/postgis:14-3.3 image: postgis/postgis:14-3.3

View file

@ -1 +1 @@
16.14.0 18.17.0

View file

@ -41,11 +41,11 @@ demarches-simplifiees.fr est **compliqué à héberger**. Parmi les problématiq
- **Utilisation de services externes** : demarches-simplifiees.fr sinterconnecte à de nombreux services externes : des APIs (API Entreprise, API Carto, la Base Adresse Nationale, etc.) mais aussi des services pour le stockage externe des pièces-jointes, lanalyse anti-virus ou lenvoi des emails. Le fonctionnement de demarches-simplifiees.fr dépend de la disponibilité de ces services externes. - **Utilisation de services externes** : demarches-simplifiees.fr sinterconnecte à de nombreux services externes : des APIs (API Entreprise, API Carto, la Base Adresse Nationale, etc.) mais aussi des services pour le stockage externe des pièces-jointes, lanalyse anti-virus ou lenvoi des emails. Le fonctionnement de demarches-simplifiees.fr dépend de la disponibilité de ces services externes.
- **Mises à jour** : le schéma de la base de données change régulièrement. Nous codons également des scripts pour harmoniser les anciennes données. Parfois des modifications ponctuelles sont effectuées sur des démarches anciennes, pour les mettre en conformité avec de nouvelles règles métiers. Nous maintenons également les dépendances logicielles utilisées notamment en réagissant rapidement lorsquune faille de sécurité est signalée. Ces mises à jour fréquentes en production sont indispensables au bon fonctionnement de loutil. - **Mises à jour** : le schéma de la base de données change régulièrement. Nous codons également des scripts pour harmoniser les anciennes données. Parfois des modifications ponctuelles sont effectuées sur des démarches anciennes, pour les mettre en conformité avec de nouvelles règles métiers. Nous maintenons également les dépendances logicielles utilisées notamment en réagissant rapidement lorsquune faille de sécurité est signalée. Ces mises à jour fréquentes en production sont indispensables au bon fonctionnement de loutil.
Si vous souhaitez adapter demarches-simplifiees.fr à votre besoin, nous vous recommandons de **proposer vos modifications à la base de code principale** (par exemple en créant une issue) **plutôt que dhéberger une autre instance vous-même**. Si vous souhaitez adapter demarches-simplifiees.fr à vos besoins, nous vous recommandons de **proposer vos modifications à la base de code principale** (par exemple en créant une issue) **plutôt que dhéberger une autre instance vous-même**.
Dans le cas où vous envisagez dhéberger une instance de demarches-simplifiees.fr vous-même, nous n'avons malheureusement pas les moyens de vous accompagner, ni dassurer de support technique concernant votre installation. Dans le cas où vous envisagez dhéberger une instance de demarches-simplifiees.fr vous-même, nous ne disposons malheureusement pas des moyens pour vous accompagner, ni dassurer de support technique concernant votre installation.
Toutefois, certains acteurs (le ministère des armées, ladministration autonome en Polynésie française) ont déployé des instances séparées. Nous proposons aux personnes intéressées de les mettre en relation avec ces acteurs existants, afin de disposer dun retour dexpérience, et de bénéficier de leur retour. Toutefois, certains acteurs (le ministère des armées, ladministration autonome en Polynésie française, l'association Adullact) ont déployé des instances séparées. Nous proposons aux personnes intéressées de les mettre en relation avec ces acteurs existants, pour obtenir un retour dexpérience et bénéficier de leur retour.
## Bonnes pratiques de code ## Bonnes pratiques de code

View file

@ -59,6 +59,7 @@ gem 'lograge'
gem 'logstash-event' gem 'logstash-event'
gem 'mailjet', require: false gem 'mailjet', require: false
gem 'matrix' # needed by prawn and not default in ruby 3.1 gem 'matrix' # needed by prawn and not default in ruby 3.1
gem 'mini_magick'
gem 'net-imap', require: false # See https://github.com/mikel/mail/pull/1439 gem 'net-imap', require: false # See https://github.com/mikel/mail/pull/1439
gem 'net-pop', require: false # same gem 'net-pop', require: false # same
gem 'net-smtp', require: false # same gem 'net-smtp', require: false # same
@ -105,6 +106,7 @@ group :test do
gem 'rack_session_access' gem 'rack_session_access'
gem 'rails-controller-testing' gem 'rails-controller-testing'
gem 'rspec_junit_formatter' gem 'rspec_junit_formatter'
gem 'rspec-retry'
gem 'selenium-devtools' gem 'selenium-devtools'
gem 'selenium-webdriver' gem 'selenium-webdriver'
gem 'shoulda-matchers', require: false gem 'shoulda-matchers', require: false
@ -114,7 +116,6 @@ group :test do
end end
group :development do group :development do
gem 'annotate'
gem 'brakeman', require: false gem 'brakeman', require: false
gem 'haml-lint' gem 'haml-lint'
gem 'letter_opener_web' gem 'letter_opener_web'

View file

@ -4,47 +4,47 @@ GEM
aasm (5.2.0) aasm (5.2.0)
concurrent-ruby (~> 1.0) concurrent-ruby (~> 1.0)
acsv (0.0.1) acsv (0.0.1)
actioncable (7.0.5.1) actioncable (7.0.7.2)
actionpack (= 7.0.5.1) actionpack (= 7.0.7.2)
activesupport (= 7.0.5.1) activesupport (= 7.0.7.2)
nio4r (~> 2.0) nio4r (~> 2.0)
websocket-driver (>= 0.6.1) websocket-driver (>= 0.6.1)
actionmailbox (7.0.5.1) actionmailbox (7.0.7.2)
actionpack (= 7.0.5.1) actionpack (= 7.0.7.2)
activejob (= 7.0.5.1) activejob (= 7.0.7.2)
activerecord (= 7.0.5.1) activerecord (= 7.0.7.2)
activestorage (= 7.0.5.1) activestorage (= 7.0.7.2)
activesupport (= 7.0.5.1) activesupport (= 7.0.7.2)
mail (>= 2.7.1) mail (>= 2.7.1)
net-imap net-imap
net-pop net-pop
net-smtp net-smtp
actionmailer (7.0.5.1) actionmailer (7.0.7.2)
actionpack (= 7.0.5.1) actionpack (= 7.0.7.2)
actionview (= 7.0.5.1) actionview (= 7.0.7.2)
activejob (= 7.0.5.1) activejob (= 7.0.7.2)
activesupport (= 7.0.5.1) activesupport (= 7.0.7.2)
mail (~> 2.5, >= 2.5.4) mail (~> 2.5, >= 2.5.4)
net-imap net-imap
net-pop net-pop
net-smtp net-smtp
rails-dom-testing (~> 2.0) rails-dom-testing (~> 2.0)
actionpack (7.0.5.1) actionpack (7.0.7.2)
actionview (= 7.0.5.1) actionview (= 7.0.7.2)
activesupport (= 7.0.5.1) activesupport (= 7.0.7.2)
rack (~> 2.0, >= 2.2.4) rack (~> 2.0, >= 2.2.4)
rack-test (>= 0.6.3) rack-test (>= 0.6.3)
rails-dom-testing (~> 2.0) rails-dom-testing (~> 2.0)
rails-html-sanitizer (~> 1.0, >= 1.2.0) rails-html-sanitizer (~> 1.0, >= 1.2.0)
actiontext (7.0.5.1) actiontext (7.0.7.2)
actionpack (= 7.0.5.1) actionpack (= 7.0.7.2)
activerecord (= 7.0.5.1) activerecord (= 7.0.7.2)
activestorage (= 7.0.5.1) activestorage (= 7.0.7.2)
activesupport (= 7.0.5.1) activesupport (= 7.0.7.2)
globalid (>= 0.6.0) globalid (>= 0.6.0)
nokogiri (>= 1.8.5) nokogiri (>= 1.8.5)
actionview (7.0.5.1) actionview (7.0.7.2)
activesupport (= 7.0.5.1) activesupport (= 7.0.7.2)
builder (~> 3.1) builder (~> 3.1)
erubi (~> 1.4) erubi (~> 1.4)
rails-dom-testing (~> 2.0) rails-dom-testing (~> 2.0)
@ -62,26 +62,26 @@ GEM
activemodel (>= 5.2.0) activemodel (>= 5.2.0)
activestorage (>= 5.2.0) activestorage (>= 5.2.0)
activesupport (>= 5.2.0) activesupport (>= 5.2.0)
activejob (7.0.5.1) activejob (7.0.7.2)
activesupport (= 7.0.5.1) activesupport (= 7.0.7.2)
globalid (>= 0.3.6) globalid (>= 0.3.6)
activemodel (7.0.5.1) activemodel (7.0.7.2)
activesupport (= 7.0.5.1) activesupport (= 7.0.7.2)
activerecord (7.0.5.1) activerecord (7.0.7.2)
activemodel (= 7.0.5.1) activemodel (= 7.0.7.2)
activesupport (= 7.0.5.1) activesupport (= 7.0.7.2)
activestorage (7.0.5.1) activestorage (7.0.7.2)
actionpack (= 7.0.5.1) actionpack (= 7.0.7.2)
activejob (= 7.0.5.1) activejob (= 7.0.7.2)
activerecord (= 7.0.5.1) activerecord (= 7.0.7.2)
activesupport (= 7.0.5.1) activesupport (= 7.0.7.2)
marcel (~> 1.0) marcel (~> 1.0)
mini_mime (>= 1.1.0) mini_mime (>= 1.1.0)
activestorage-openstack (1.5.1) activestorage-openstack (1.6.0)
fog-openstack (~> 1.0) fog-openstack (>= 1.0.9)
marcel marcel
rails (>= 5.2.2) rails (>= 5.2.2)
activesupport (7.0.5.1) activesupport (7.0.7.2)
concurrent-ruby (~> 1.0, >= 1.0.2) concurrent-ruby (~> 1.0, >= 1.0.2)
i18n (>= 1.6, < 2) i18n (>= 1.6, < 2)
minitest (>= 5.1) minitest (>= 5.1)
@ -101,9 +101,6 @@ GEM
aes_key_wrap (1.1.0) aes_key_wrap (1.1.0)
after_party (1.11.2) after_party (1.11.2)
anchored (1.1.0) anchored (1.1.0)
annotate (3.2.0)
activerecord (>= 3.2, < 8.0)
rake (>= 10.4, < 14.0)
ast (2.4.2) ast (2.4.2)
attr_required (1.0.1) attr_required (1.0.1)
axe-core-api (4.2.1) axe-core-api (4.2.1)
@ -239,7 +236,7 @@ GEM
tzinfo tzinfo
ethon (0.15.0) ethon (0.15.0)
ffi (>= 1.15.0) ffi (>= 1.15.0)
excon (0.79.0) excon (0.102.0)
factory_bot (6.1.0) factory_bot (6.1.0)
activesupport (>= 5.0.0) activesupport (>= 5.0.0)
ffi (1.15.5) ffi (1.15.5)
@ -257,26 +254,25 @@ GEM
rack (>= 1.4, < 3) rack (>= 1.4, < 3)
rack-protection (>= 1.5.3, <= 4.0.0) rack-protection (>= 1.5.3, <= 4.0.0)
sanitize (< 7) sanitize (< 7)
fog-core (2.2.3) fog-core (2.3.0)
builder builder
excon (~> 0.71) excon (~> 0.71)
formatador (~> 0.2) formatador (>= 0.2, < 2.0)
mime-types mime-types
fog-json (1.2.0) fog-json (1.2.0)
fog-core fog-core
multi_json (~> 1.10) multi_json (~> 1.10)
fog-openstack (1.0.11) fog-openstack (1.1.0)
fog-core (~> 2.1) fog-core (~> 2.1)
fog-json (>= 1.0) fog-json (>= 1.0)
ipaddress (>= 0.8) formatador (1.1.0)
formatador (0.2.5)
fugit (1.4.2) fugit (1.4.2)
et-orbi (~> 1.1, >= 1.1.8) et-orbi (~> 1.1, >= 1.1.8)
raabro (~> 1.4) raabro (~> 1.4)
geo_coord (0.2.0) geo_coord (0.2.0)
geocoder (1.6.5) geocoder (1.6.5)
globalid (1.1.0) globalid (1.2.1)
activesupport (>= 5.0) activesupport (>= 6.1)
gon (6.4.0) gon (6.4.0)
actionpack (>= 3.0.20) actionpack (>= 3.0.20)
i18n (>= 0.7) i18n (>= 0.7)
@ -352,7 +348,6 @@ GEM
ruby-vips (>= 2.0.17, < 3) ruby-vips (>= 2.0.17, < 3)
invisible_captcha (2.0.0) invisible_captcha (2.0.0)
rails (>= 5.0) rails (>= 5.0)
ipaddress (0.8.3)
jquery-rails (4.5.1) jquery-rails (4.5.1)
rails-dom-testing (>= 1, < 3) rails-dom-testing (>= 1, < 3)
railties (>= 4.2.0) railties (>= 4.2.0)
@ -389,7 +384,7 @@ GEM
actionmailer (>= 3.2) actionmailer (>= 3.2)
letter_opener (~> 1.0) letter_opener (~> 1.0)
railties (>= 3.2) railties (>= 3.2)
listen (3.4.1) listen (3.8.0)
rb-fsevent (~> 0.10, >= 0.10.3) rb-fsevent (~> 0.10, >= 0.10.3)
rb-inotify (~> 0.9, >= 0.9.10) rb-inotify (~> 0.9, >= 0.9.10)
llhttp-ffi (0.4.0) llhttp-ffi (0.4.0)
@ -417,21 +412,21 @@ GEM
matrix (0.4.2) matrix (0.4.2)
memory_profiler (1.0.0) memory_profiler (1.0.0)
method_source (1.0.0) method_source (1.0.0)
mime-types (3.3.1) mime-types (3.5.1)
mime-types-data (~> 3.2015) mime-types-data (~> 3.2015)
mime-types-data (3.2021.0212) mime-types-data (3.2023.0808)
mina (1.2.4) mina (1.2.4)
open4 (~> 1.3.4) open4 (~> 1.3.4)
rake rake
mini_magick (4.11.0) mini_magick (4.11.0)
mini_mime (1.1.2) mini_mime (1.1.5)
mini_portile2 (2.8.4) mini_portile2 (2.8.4)
minitest (5.18.1) minitest (5.20.0)
msgpack (1.4.2) msgpack (1.4.2)
multi_json (1.15.0) multi_json (1.15.0)
mustermann (3.0.0) mustermann (3.0.0)
ruby2_keywords (~> 0.0.1) ruby2_keywords (~> 0.0.1)
net-imap (0.3.6) net-imap (0.3.7)
date date
net-protocol net-protocol
net-pop (0.1.2) net-pop (0.1.2)
@ -442,7 +437,7 @@ GEM
net-protocol net-protocol
netrc (0.11.0) netrc (0.11.0)
nio4r (2.5.9) nio4r (2.5.9)
nokogiri (1.15.3) nokogiri (1.15.4)
mini_portile2 (~> 2.8.2) mini_portile2 (~> 2.8.2)
racc (~> 1.4) racc (~> 1.4)
open4 (1.3.4) open4 (1.3.4)
@ -490,13 +485,13 @@ GEM
pry-rails (0.3.9) pry-rails (0.3.9)
pry (>= 0.10.4) pry (>= 0.10.4)
public_suffix (5.0.1) public_suffix (5.0.1)
puma (6.1.1) puma (6.3.1)
nio4r (~> 2.0) nio4r (~> 2.0)
pundit (2.2.0) pundit (2.2.0)
activesupport (>= 3.0.0) activesupport (>= 3.0.0)
raabro (1.4.0) raabro (1.4.0)
racc (1.7.1) racc (1.7.1)
rack (2.2.7) rack (2.2.8)
rack-attack (6.5.0) rack-attack (6.5.0)
rack (>= 1.0, < 3) rack (>= 1.0, < 3)
rack-mini-profiler (3.0.0) rack-mini-profiler (3.0.0)
@ -516,26 +511,27 @@ GEM
rack_session_access (0.2.0) rack_session_access (0.2.0)
builder (>= 2.0.0) builder (>= 2.0.0)
rack (>= 1.0.0) rack (>= 1.0.0)
rails (7.0.5.1) rails (7.0.7.2)
actioncable (= 7.0.5.1) actioncable (= 7.0.7.2)
actionmailbox (= 7.0.5.1) actionmailbox (= 7.0.7.2)
actionmailer (= 7.0.5.1) actionmailer (= 7.0.7.2)
actionpack (= 7.0.5.1) actionpack (= 7.0.7.2)
actiontext (= 7.0.5.1) actiontext (= 7.0.7.2)
actionview (= 7.0.5.1) actionview (= 7.0.7.2)
activejob (= 7.0.5.1) activejob (= 7.0.7.2)
activemodel (= 7.0.5.1) activemodel (= 7.0.7.2)
activerecord (= 7.0.5.1) activerecord (= 7.0.7.2)
activestorage (= 7.0.5.1) activestorage (= 7.0.7.2)
activesupport (= 7.0.5.1) activesupport (= 7.0.7.2)
bundler (>= 1.15.0) bundler (>= 1.15.0)
railties (= 7.0.5.1) railties (= 7.0.7.2)
rails-controller-testing (1.0.5) rails-controller-testing (1.0.5)
actionpack (>= 5.0.1.rc1) actionpack (>= 5.0.1.rc1)
actionview (>= 5.0.1.rc1) actionview (>= 5.0.1.rc1)
activesupport (>= 5.0.1.rc1) activesupport (>= 5.0.1.rc1)
rails-dom-testing (2.0.3) rails-dom-testing (2.2.0)
activesupport (>= 4.2.0) activesupport (>= 5.0.0)
minitest
nokogiri (>= 1.6) nokogiri (>= 1.6)
rails-erd (1.6.1) rails-erd (1.6.1)
activerecord (>= 4.2) activerecord (>= 4.2)
@ -548,9 +544,9 @@ GEM
rails-i18n (7.0.3) rails-i18n (7.0.3)
i18n (>= 0.7, < 2) i18n (>= 0.7, < 2)
railties (>= 6.0.0, < 8) railties (>= 6.0.0, < 8)
railties (7.0.5.1) railties (7.0.7.2)
actionpack (= 7.0.5.1) actionpack (= 7.0.7.2)
activesupport (= 7.0.5.1) activesupport (= 7.0.7.2)
method_source method_source
rake (>= 12.2) rake (>= 12.2)
thor (~> 1.0) thor (~> 1.0)
@ -558,7 +554,7 @@ GEM
rainbow (3.1.1) rainbow (3.1.1)
rake (13.0.6) rake (13.0.6)
rake-progressbar (0.0.5) rake-progressbar (0.0.5)
rb-fsevent (0.10.4) rb-fsevent (0.11.2)
rb-inotify (0.10.1) rb-inotify (0.10.1)
ffi (~> 1.0) ffi (~> 1.0)
redcarpet (3.6.0) redcarpet (3.6.0)
@ -604,6 +600,8 @@ GEM
rspec-expectations (~> 3.11) rspec-expectations (~> 3.11)
rspec-mocks (~> 3.11) rspec-mocks (~> 3.11)
rspec-support (~> 3.11) rspec-support (~> 3.11)
rspec-retry (0.6.2)
rspec-core (> 3.3)
rspec-support (3.12.0) rspec-support (3.12.0)
rspec_junit_formatter (0.4.1) rspec_junit_formatter (0.4.1)
rspec-core (>= 2, < 4, != 2.12.0) rspec-core (>= 2, < 4, != 2.12.0)
@ -690,7 +688,7 @@ GEM
rack (~> 2.2, >= 2.2.4) rack (~> 2.2, >= 2.2.4)
rack-protection (= 3.0.5) rack-protection (= 3.0.5)
tilt (~> 2.0) tilt (~> 2.0)
skylight (5.3.4) skylight (6.0.1)
activesupport (>= 5.2.0) activesupport (>= 5.2.0)
smart_properties (1.17.0) smart_properties (1.17.0)
spreadsheet_architect (4.1.0) spreadsheet_architect (4.1.0)
@ -778,7 +776,7 @@ GEM
crack (>= 0.3.2) crack (>= 0.3.2)
hashdiff (>= 0.4.0, < 2.0.0) hashdiff (>= 0.4.0, < 2.0.0)
websocket (1.2.9) websocket (1.2.9)
websocket-driver (0.7.5) websocket-driver (0.7.6)
websocket-extensions (>= 0.1.0) websocket-extensions (>= 0.1.0)
websocket-extensions (0.1.5) websocket-extensions (0.1.5)
xmlenc (0.8.0) xmlenc (0.8.0)
@ -790,7 +788,7 @@ GEM
nokogiri (~> 1.11) nokogiri (~> 1.11)
xpath (3.2.0) xpath (3.2.0)
nokogiri (~> 1.8) nokogiri (~> 1.8)
zeitwerk (2.6.8) zeitwerk (2.6.11)
zip_tricks (5.6.0) zip_tricks (5.6.0)
zipline (1.4.1) zipline (1.4.1)
actionpack (>= 6.0, < 8.0) actionpack (>= 6.0, < 8.0)
@ -813,7 +811,6 @@ DEPENDENCIES
administrate-field-enum administrate-field-enum
after_party after_party
anchored anchored
annotate
axe-core-rspec axe-core-rspec
bcrypt bcrypt
bootsnap (>= 1.4.4) bootsnap (>= 1.4.4)
@ -872,6 +869,7 @@ DEPENDENCIES
matrix matrix
memory_profiler memory_profiler
mina mina
mini_magick
net-imap net-imap
net-pop net-pop
net-smtp net-smtp
@ -898,6 +896,7 @@ DEPENDENCIES
rexml rexml
rqrcode rqrcode
rspec-rails rspec-rails
rspec-retry
rspec_junit_formatter rspec_junit_formatter
rubocop rubocop
rubocop-performance rubocop-performance

View file

@ -17,6 +17,7 @@ Vous souhaitez y apporter des changements ou des améliorations ? Lisez notre [
#### Tous environnements #### Tous environnements
- postgresql - postgresql
- imagemagick et gsfonts pour générer les filigranes sur les titres d'identité.
#### Développement #### Développement
@ -28,7 +29,7 @@ Vous souhaitez y apporter des changements ou des améliorations ? Lisez notre [
- Chrome - Chrome
- chromedriver : - chromedriver :
* Mac : `brew install chromedriver` * Mac : `brew install chromedriver`
* Linux : voir https://sites.google.com/a/chromium.org/chromedriver/downloads * Linux : voir https://developer.chrome.com/blog/chrome-for-testing
Si l'emplacement d'installation de Chrome n'est pas standard, ou que vous utilisez Brave ou Chromium à la place, Si l'emplacement d'installation de Chrome n'est pas standard, ou que vous utilisez Brave ou Chromium à la place,
il peut être nécessaire d'overrider pour votre machine le path vers le binaire Chrome, par exemple : il peut être nécessaire d'overrider pour votre machine le path vers le binaire Chrome, par exemple :
@ -42,8 +43,7 @@ Selenium::WebDriver::Chrome.path = "/Applications/Brave Browser.app/Contents/Mac
Webdrivers::Chromedriver.required_version = "103.0.5060.53" Webdrivers::Chromedriver.required_version = "103.0.5060.53"
``` ```
Il peut être également pertinent de désactiver la mise à jour automatique du webdriver Il est également possible de faire une installation et mise à jour automatique lors de l'exécution de `bin/update` en définissant la variable d'environnement `UPDATE_WEBDRIVER`. Les binaires seront installés dans le repertoire `~/.local/bin/` qui doit être rajouté manuellement dans le path.
en définissant une variable d'environnement `SKIP_UPDATE_WEBDRIVER` lors de l'exécution de `bin/update`.
### Création des rôles de la base de données ### Création des rôles de la base de données

Binary file not shown.

Before

Width:  |  Height:  |  Size: 466 B

After

Width:  |  Height:  |  Size: 480 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 889 B

After

Width:  |  Height:  |  Size: 878 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.5 KiB

After

Width:  |  Height:  |  Size: 2.7 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.8 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 9.7 KiB

View file

@ -102,6 +102,10 @@
white-space: nowrap; white-space: nowrap;
} }
.width-max-content {
width: max-content;
}
// sizing // sizing
.width-100 { .width-100 {
width: 100%; width: 100%;

View file

@ -10,12 +10,6 @@
// //
// The procedure description can still be read from the /commencer // The procedure description can still be read from the /commencer
// pages. // pages.
@media (max-width: $two-columns-breakpoint) {
.agent-intro {
display: none;
}
}
.column { .column {
padding-top: $default-spacer; padding-top: $default-spacer;
} }
@ -37,14 +31,6 @@
margin-bottom: 0; margin-bottom: 0;
} }
hr {
margin-top: 30px;
margin-bottom: 30px;
background-color: $grey;
border: none;
height: 1px;
}
.register { .register {
display: flex; display: flex;
justify-content: space-between; justify-content: space-between;

View file

@ -310,24 +310,6 @@ ul.dropdown-items {
width: 340px; width: 340px;
} }
label {
width: 100px;
display: inline-block;
margin-bottom: 2 * $default-spacer;
}
input:not(.fr-btn),
select {
width: 200px;
display: inline-block;
background-color: $light-grey;
border: 1px solid $border-grey;
}
[disabled] {
display: none;
}
ul { ul {
list-style: none; list-style: none;
} }

View file

@ -49,7 +49,7 @@
} }
} }
:not(.fr-downloads-group) > ul { :not(.fr-downloads-group):not(.fr-pagination) > ul {
list-style-type: disc; list-style-type: disc;
list-style-position: inside; list-style-position: inside;
padding-left: $default-padding; padding-left: $default-padding;

View file

@ -62,11 +62,6 @@
width: 110px; width: 110px;
} }
.action-col {
text-align: right;
padding-left: $default-spacer;
padding-right: $default-spacer;
}
.follow-col { .follow-col {
width: 450px; width: 450px;

View file

@ -99,6 +99,21 @@ fieldset {
width: max-content; width: max-content;
} }
button.fr-tag-bug {
background-color: $blue-france-500;
color: #FFFFFF;
&:hover {
background-color: #1212FF;
color: #FFFFFF;
}
.tag-dismiss {
font-size: 1rem;
margin-left: 10px;
}
}
// on veut ferrer à droite le dropdown de sélecteur de langue // on veut ferrer à droite le dropdown de sélecteur de langue
@media (min-width: 62em) { @media (min-width: 62em) {
.fr-nav__item.custom-fr-translate-flex-end { .fr-nav__item.custom-fr-translate-flex-end {

View file

@ -20,7 +20,8 @@
font-style: italic; font-style: italic;
} }
.fr-input-group { .fr-input-group,
.fr-select-group {
margin-bottom: 1rem; margin-bottom: 1rem;
} }
@ -116,16 +117,8 @@
visibility: visible; visibility: visible;
} }
// Move checkbox to the top-left side of the label
&.editable-champ-checkbox {
label.admin-default-zone {
font-weight: bold;
}
}
&.editable-champ-checkbox { &.editable-champ-checkbox {
label { label {
padding-left: 28px;
font-weight: normal; font-weight: normal;
} }
@ -210,18 +203,17 @@
} }
} }
.fr-label { .fr-label .fr-hint-text > *,
.fr-fieldset__legend .fr-hint-text > * {
// la description d'un champ peut contenir du markup (markdown->html), // la description d'un champ peut contenir du markup (markdown->html),
// on herite donc la fontsize/mrgin/padding du fr-hint-text // on herite donc la fontsize/mrgin/padding du fr-hint-text
.fr-hint-text > * { font-size: inherit;
font-size: inherit; margin: inherit;
margin: inherit; padding: inherit;
padding: inherit;
}
} }
input[type=password], input[type=password],
select { select:not(.fr-select) {
display: block; display: block;
margin-bottom: 0; margin-bottom: 0;
@ -246,9 +238,7 @@
} }
} }
input[type=text]:not([data-address='true']), input[type=text]:not([data-address='true']) {
select {
border-radius: 4px;
border: solid 1px $border-grey; border: solid 1px $border-grey;
padding: $default-padding; padding: $default-padding;
@ -524,7 +514,7 @@
} }
} }
fieldset ~ .spinner { fieldset + .spinner {
position: relative; position: relative;
top: -($default-fields-spacer / 2); top: -($default-fields-spacer / 2);
} }
@ -631,3 +621,18 @@ textarea::placeholder {
background-color: $white; background-color: $white;
z-index: 2; z-index: 2;
} }
.fr-menu__list {
padding: $default-spacer;
overflow-y: auto;
max-height: 300px;
.fr-menu__item {
list-style-type: none;
margin-bottom: $default-spacer;
&[aria-selected] {
font-weight: bold;
}
}
}

View file

@ -1,34 +1,6 @@
@import "constants";
#invites-form { #invites-form {
padding: $default-padding; @media (min-width: 48em) {
text-align: left; min-width: 400px;
form {
display: flex;
margin-top: $default-padding;
}
h4 {
font-weight: bold;
margin-bottom: $default-spacer;
}
p {
margin-bottom: $default-spacer;
}
ul {
list-style-position: inside;
list-style-type: disc;
margin-bottom: $default-padding;
}
input[type=email] {
margin-bottom: $default-spacer;
}
.button {
margin-left: $default-spacer;
} }
} }

View file

@ -14,13 +14,18 @@
.type-de-champ { .type-de-champ {
width: 100%; width: 100%;
background-color: #FAFDFF; margin-bottom: $default-padding;
border: 1px solid $border-grey;
border-radius: 5px;
margin-bottom: $default-padding * 2;
box-shadow: 0px 2px 4px -4px;
overflow: hidden; overflow: hidden;
.type-de-champ-container {
width: 100%;
background-color: #FAFDFF;
border: 1px solid $border-grey;
border-radius: 5px;
margin-bottom: $default-padding;
box-shadow: 0px 2px 4px -4px;
}
.handle.icon { .handle.icon {
width: 32px; width: 32px;
height: 32px; height: 32px;
@ -71,6 +76,10 @@
display: none; display: none;
} }
&.last .type-de-champ-add-button.root {
display: none;
}
.head { .head {
background-color: #FAFDFF; background-color: #FAFDFF;
@ -80,8 +89,6 @@
} }
&.type-header-section { &.type-header-section {
background-color: $blue-cumulus-950;
.head { .head {
background-color: $blue-cumulus-950; background-color: $blue-cumulus-950;
} }
@ -91,10 +98,6 @@
&.section { &.section {
padding: $default-spacer $default-spacer 0; padding: $default-spacer $default-spacer 0;
margin-bottom: 8px; margin-bottom: 8px;
input {
background-color: $white;
}
} }
&.hr { &.hr {

View file

@ -65,3 +65,8 @@
} }
} }
} }
// Hacky css to display dropdown "customize table" for table with only 1 or 2 lines
table.min-height-300 {
min-height: 300px;
}

View file

@ -2,13 +2,6 @@ class ApplicationComponent < ViewComponent::Base
include ViewComponent::Translatable include ViewComponent::Translatable
include FlipperHelper include FlipperHelper
# Takes a Hash of { class_name: boolean }.
# Returns truthy class names in an array. Array can be passed as-it in rails helpers,
# and is still manipulable if needed.
def class_names(class_names)
class_names.filter { _2 }.keys
end
def current_user def current_user
controller.current_user controller.current_user
end end

View file

@ -18,6 +18,8 @@
= render partial: "shared/champs/carte/show", locals: { champ: champ } = render partial: "shared/champs/carte/show", locals: { champ: champ }
- when TypeDeChamp.type_champs.fetch(:dossier_link) - when TypeDeChamp.type_champs.fetch(:dossier_link)
= render partial: "shared/champs/dossier_link/show", locals: { champ: champ } = render partial: "shared/champs/dossier_link/show", locals: { champ: champ }
- when TypeDeChamp.type_champs.fetch(:drop_down_list)
= render partial: "shared/champs/drop_down_list/show", locals: { champ: champ }
- when TypeDeChamp.type_champs.fetch(:multiple_drop_down_list) - when TypeDeChamp.type_champs.fetch(:multiple_drop_down_list)
= render partial: "shared/champs/multiple_drop_down_list/show", locals: { champ: champ } = render partial: "shared/champs/multiple_drop_down_list/show", locals: { champ: champ }
- when TypeDeChamp.type_champs.fetch(:piece_justificative), TypeDeChamp.type_champs.fetch(:titre_identite) - when TypeDeChamp.type_champs.fetch(:piece_justificative), TypeDeChamp.type_champs.fetch(:titre_identite)

View file

@ -1,13 +1,15 @@
= form_tag add_filter_instructeur_procedure_path(procedure), method: :post, class: 'dropdown-form large', id: 'filter-component', data: { turbo: true, controller: 'autosubmit' } do = form_tag add_filter_instructeur_procedure_path(procedure), method: :post, class: 'dropdown-form large', id: 'filter-component', data: { turbo: true, controller: 'autosubmit' } do
= label_tag :field, t('.column') .fr-select-group
= select_tag :field, options_for_select(filterable_fields_for_select, field_id), include_blank: field_id.nil? = label_tag :field, t('.column'), class: 'fr-label fr-m-0'
%input.hidden{ type: 'submit', formaction: update_filter_instructeur_procedure_path(procedure), data: { autosubmit_target: 'submitter' } } = select_tag :field, options_for_select(filterable_fields_for_select, field_id), include_blank: field_id.nil?, class: 'fr-select'
%br
= label_tag :value, t('.value'), for: 'value' %input.hidden{ type: 'submit', formaction: update_filter_instructeur_procedure_path(procedure), data: { autosubmit_target: 'submitter' } }
= label_tag :value, t('.value'), for: 'value', class: 'fr-label'
- if field_type == :enum - if field_type == :enum
= select_tag :value, options_for_select(options_for_select_of_field), id: 'value', name: 'value', data: { no_autosubmit: true } = select_tag :value, options_for_select(options_for_select_of_field), id: 'value', name: 'value', class: 'fr-select', data: { no_autosubmit: true }
- else - else
%input#value{ type: field_type, name: :value, maxlength: ProcedurePresentation::FILTERS_VALUE_MAX_LENGTH, disabled: field_id.nil? ? true : false, data: { no_autosubmit: true } } %input#value.fr-input{ type: field_type, name: :value, maxlength: ProcedurePresentation::FILTERS_VALUE_MAX_LENGTH, disabled: field_id.nil? ? true : false, data: { no_autosubmit: true } }
= hidden_field_tag :statut, statut = hidden_field_tag :statut, statut
= submit_tag t('.add_filter'), class: 'fr-btn fr-btn--secondary fr-mt-2w' = submit_tag t('.add_filter'), class: 'fr-btn fr-btn--secondary fr-mt-2w'

View file

@ -15,10 +15,6 @@ class Dossiers::NotifiedToggleComponent < ApplicationComponent
sorted_by_notifications? && order_desc? sorted_by_notifications? && order_desc?
end end
def icon_class_name
active? ? 'fr-fi-checkbox' : 'fr-fi-checkbox-blank'
end
def order_desc? def order_desc?
current_order == 'desc' current_order == 'desc'
end end

View file

@ -1,5 +1,6 @@
= form_tag update_sort_instructeur_procedure_path(procedure_id: @procedure.id, table: 'notifications', column: 'notifications', order: opposite_order), method: :get, data: { controller: 'autosubmit' } do = form_tag update_sort_instructeur_procedure_path(procedure_id: @procedure.id, table: 'notifications', column: 'notifications', order: opposite_order), method: :get, data: { controller: 'autosubmit' } do
.fr-toggle .fr-fieldset__element.fr-m-0
= check_box_tag :order, opposite_order, active?, class: 'fr-toggle__input' .fr-checkbox-group.fr-checkbox-group--sm
= label_tag :order, t('.show_notified_first'), class: 'fr-toggle__label fr-pl-1w' = check_box_tag :order, opposite_order, active?
= submit_tag t('.show_notified_first'), data: {"checkbox-target": 'submit' }, class: 'visually-hidden' = label_tag :order, t('.show_notified_first'), class: 'fr-label'
= submit_tag t('.show_notified_first'), data: {"checkbox-target": 'submit' }, class: 'visually-hidden'

View file

@ -1,4 +1,5 @@
= form_with(url: dossiers_path, method: :get, data: { controller: 'autosubmit' } ) do |f| = form_with(url: dossiers_path, method: :get, data: { controller: 'autosubmit' } ) do |f|
= f.hidden_field :q, value: params[:q], id: nil
= f.label :procedure_id, t('.procedure.label'), class: 'sr-only' = f.label :procedure_id, t('.procedure.label'), class: 'sr-only'
.fr-input-group .fr-input-group
= f.select :procedure_id, options_for_select(@procedures_for_select, params[:procedure_id]), { prompt: t('.procedures.prompt') }, class: 'fr-select' = f.select :procedure_id, options_for_select(@procedures_for_select, params[:procedure_id]), { prompt: t('.procedures.prompt') }, class: 'fr-select'

View file

@ -0,0 +1,65 @@
class Dsfr::ComboboxComponent < ApplicationComponent
def initialize(form: nil, options:, selected: nil, allows_custom_value: false, **html_options)
@form, @options, @selected, @allows_custom_value, @html_options = form, options, selected, allows_custom_value, html_options
end
attr_reader :form, :options, :selected, :allows_custom_value
private
def name
@html_options[:name]
end
def form_id
@html_options[:form_id]
end
def html_input_options
{
type: 'text',
autocomplete: 'off',
spellcheck: 'false',
id: input_id,
class: input_class,
value: input_value,
'aria-expanded': 'false',
'aria-describedby': @html_options[:describedby]
}.compact
end
def input_id
@html_options[:id]
end
def input_class
"#{@html_options[:class].presence || ''} fr-select"
end
def input_value
selected.present? ? options_with_values.find { _1.last == selected }&.first : nil
end
def list_id
input_id.present? ? "#{input_id}-list" : nil
end
def options_with_values
options.map { _1.is_a?(Array) ? _1 : [_1, _1] }
end
def options_json
options_with_values.map { |(label, value)| { label:, value: } }.to_json
end
def hints_json
{
empty: t(".sr.results", count: 0),
one: t(".sr.results", count: 1),
many: t(".sr.results", count: 2),
oneWithLabel: t(".sr.results_with_label", count: 1),
manyWithLabel: t(".sr.results_with_label", count: 2),
selected: t(".sr.selected", count: 2)
}.to_json
end
end

View file

@ -0,0 +1,10 @@
en:
sr:
results:
zero: No result
one: 1 result
other: "{count} results"
results_with_label:
one: "1 result. {label} is the top result press Enter to activate"
other: "{count} results. {label} is the top result press Enter to activate"
selected: "{label} selected"

View file

@ -0,0 +1,10 @@
fr:
sr:
results:
zero: Aucun résultat
one: 1 résultat
other: "{count} résultats"
results_with_label:
one: "1 résultat. {label} est le premier résultat appuyez sur Entrée pour sélectionner"
other: "{count} résultats. {label} est le premier résultat appuyez sur Entrée pour sélectionner"
selected: "{label} sélectionné"

View file

@ -0,0 +1,13 @@
.fr-ds-combobox{ data: { controller: 'combobox', allows_custom_value: allows_custom_value } }
.fr-ds-combobox-input
%input{ **html_input_options }
- if form
= form.hidden_field name, value: selected, form: form_id
- else
%input{ type: 'hidden', name:, value: selected, form: form_id }
.fr-menu
%ul.fr-menu__list.hidden{ role: 'listbox', hidden: true, id: list_id, data: { turbo_force: :browser, options: options_json, selected:, hints: hints_json } }
.sr-only{ aria: { live: 'polite', atomic: 'true' }, data: { turbo_force: :browser } }
%template
%li.fr-menu__item{ role: 'option' }
%slot{ name: 'label' }

View file

@ -7,6 +7,7 @@ class Dsfr::InputComponent < ApplicationComponent
# use it to indicate detailed about the inputs, ex: https://www.systeme-de-design.gouv.fr/elements-d-interface/modeles-et-blocs-fonctionnels/demande-de-mot-de-passe # use it to indicate detailed about the inputs, ex: https://www.systeme-de-design.gouv.fr/elements-d-interface/modeles-et-blocs-fonctionnels/demande-de-mot-de-passe
# it uses aria-describedby on input and link it to yielded content # it uses aria-describedby on input and link it to yielded content
renders_one :describedby renders_one :describedby
renders_one :label
def initialize(form:, attribute:, input_type: :text_field, opts: {}, required: true) def initialize(form:, attribute:, input_type: :text_field, opts: {}, required: true)
@form = form @form = form
@ -16,6 +17,10 @@ class Dsfr::InputComponent < ApplicationComponent
@required = required @required = required
end end
def dsfr_champ_container
:div
end
# add invalid class on input when input is invalid # add invalid class on input when input is invalid
# and and valid on input only if another input is invalid # and and valid on input only if another input is invalid
def input_group_opts def input_group_opts
@ -46,7 +51,11 @@ class Dsfr::InputComponent < ApplicationComponent
# i18n lookups # i18n lookups
def label def label
object.class.human_attribute_name(@attribute) get_slot(:label).presence || default_label
end
def dsfr_input_classname
'fr-input'
end end
# kind of input helpers # kind of input helpers
@ -63,4 +72,8 @@ class Dsfr::InputComponent < ApplicationComponent
end end
private private
def default_label
object.class.human_attribute_name(@attribute)
end
end end

View file

@ -1,10 +1,37 @@
module Dsfr module Dsfr
module InputErrorable module InputErrorable
extend ActiveSupport::Concern extend ActiveSupport::Concern
included do included do
delegate :object, to: :@form delegate :object, to: :@form
delegate :errors, to: :object delegate :errors, to: :object
renders_one :hint
def dsfr_group_classname
if dsfr_champ_container == :fieldset
'fr-fieldset'
else
"#{dsfr_input_classname}-group"
end
end
def input_group_error_class_names
{
"#{dsfr_group_classname}--error" => errors_on_attribute?,
"#{dsfr_group_classname}--valid" => !errors_on_attribute? && errors_on_another_attribute?
}
end
def errors_on_attribute?
errors.has_key?(attribute_or_rich_body)
end
# errors helpers
def error_full_messages
errors.full_messages_for(attribute_or_rich_body)
end
private private
# lookup for edge case from `form.rich_text_area` # lookup for edge case from `form.rich_text_area`
@ -18,15 +45,14 @@ module Dsfr
end end
end end
def input_group_error_class_names def fr_fieldset?
{ !['fr-input', 'fr-radio', 'fr-select'].include?(dsfr_input_classname)
"fr-input-group--error": errors_on_attribute?,
"fr-input-group--valid": !errors_on_attribute? && errors_on_another_attribute?
}
end end
def input_error_class_names def input_error_class_names
{ 'fr-input--error': errors_on_attribute? } {
"#{dsfr_input_classname}--error": errors_on_attribute?
}
end end
def input_error_opts def input_error_opts
@ -67,28 +93,19 @@ module Dsfr
@opts @opts
end end
def describedby_id
dom_id(@champ, :error_full_messages)
end
def errors_on_another_attribute? def errors_on_another_attribute?
!errors.empty? !errors.empty?
end end
def errors_on_attribute?
errors.has_key?(attribute_or_rich_body)
end
# errors helpers
def error_full_messages
errors.full_messages_for(attribute_or_rich_body)
end
def map_array_to_hash_with_true(array_or_string_or_nil) def map_array_to_hash_with_true(array_or_string_or_nil)
Array(array_or_string_or_nil).to_h { [_1, true] } Array(array_or_string_or_nil).to_h { [_1, true] }
end end
def hint def hint
get_slot(:hint).presence || default_hint
end
def default_hint
I18n.t("activerecord.attributes.#{object.class.name.underscore}.hints.#{@attribute}") I18n.t("activerecord.attributes.#{object.class.name.underscore}.hints.#{@attribute}")
end end
@ -101,6 +118,8 @@ module Dsfr
end end
def hint? def hint?
return true if get_slot(:hint).present?
I18n.exists?("activerecord.attributes.#{object.class.name.underscore}.hints.#{@attribute}") I18n.exists?("activerecord.attributes.#{object.class.name.underscore}.hints.#{@attribute}")
end end
end end

View file

@ -0,0 +1,13 @@
module Dsfr
class InputStatusMessageComponent < ApplicationComponent
def initialize(errors_on_attribute:, error_full_messages:, described_by:)
@errors_on_attribute = errors_on_attribute
@error_full_messages = error_full_messages
@described_by = described_by
end
def render?
@errors_on_attribute
end
end
end

View file

@ -0,0 +1,3 @@
.fr-messages-group{ id: @describedby_id }
- @error_full_messages.each do |error_message|
%p{ class: class_names('fr-message' => true, "fr-message--#{@errors_on_attribute ? 'error' : 'valid'}" => true) }= error_message

View file

@ -1,2 +1,5 @@
class EditableChamp::AnnuaireEducationComponent < EditableChamp::ComboSearchComponent class EditableChamp::AnnuaireEducationComponent < EditableChamp::ComboSearchComponent
def dsfr_input_classname
'fr-input'
end
end end

View file

@ -1,9 +1,12 @@
= # we do this trick because some html elements should use 'label' and some should be plain paragraphs = # we do this trick because some html elements should use 'label' and some should be plain paragraphs
- if @champ.html_label? - if @champ.html_label?
= @form.label @champ.main_value_name, id: @champ.labelledby_id, for: @champ.input_id, class: 'fr-label' do = @form.label @champ.main_value_name, id: @champ.labelledby_id, for: @champ.input_id, class: 'fr-label' do
- render EditableChamp::ChampLabelContentComponent.new form: @form, champ: @champ, seen_at: @seen_at - render EditableChamp::ChampLabelContentComponent.new form: @form, champ: @champ, seen_at: @seen_at
- elsif @champ.legend_label?
%legend.fr-fieldset__legend.fr-text--regular{ id: @champ.labelledby_id }= render EditableChamp::ChampLabelContentComponent.new form: @form, champ: @champ, seen_at: @seen_at
- elsif @champ.single_checkbox?
-# no label to add
- else - else
.fr-label.mb-4{ id: @champ.labelledby_id } .fr-label.mb-4{ id: @champ.labelledby_id }
= render EditableChamp::ChampLabelContentComponent.new form: @form, champ: @champ, seen_at: @seen_at = render EditableChamp::ChampLabelContentComponent.new form: @form, champ: @champ, seen_at: @seen_at

View file

@ -1,2 +1,9 @@
class EditableChamp::CheckboxComponent < EditableChamp::EditableChampBaseComponent class EditableChamp::CheckboxComponent < EditableChamp::EditableChampBaseComponent
def dsfr_champ_container
:fieldset
end
def dsfr_input_classname
'fr-radio'
end
end end

View file

@ -1,4 +1,9 @@
= @form.check_box :value, .fr-fieldset__element
{ required: @champ.required?, id: @champ.input_id, checked: @champ.true?, aria: { describedby: @champ.describedby_id }, class: class_names('required' => @champ.required?)}, .fr-checkbox-group
'true', = @form.check_box :value,
'false' { required: @champ.required?, id: @champ.input_id, checked: @champ.true?, aria: { describedby: @champ.describedby_id }, class: class_names('required' => @champ.required?)},
'true',
'false'
%label.fr-label{ for: @champ.input_id, id: @champ.labelledby_id }
= render EditableChamp::ChampLabelContentComponent.new form: @form, champ: @champ, seen_at: @seen_at

View file

@ -1,10 +1,13 @@
%fieldset.radios %fieldset.fr-fieldset
%legend.mandatory-explanation %legend.fr-fieldset__legend--regular.fr-fieldset__legend
Sélectionnez une des valeurs Sélectionnez une des valeurs
%label .fr-fieldset__element.fr-fieldset__element--inline
= @form.radio_button :value, Individual::GENDER_FEMALE, id: @champ.female_input_id .fr-radio-group
= Individual.human_attribute_name('gender.female') = @form.radio_button :value, Individual::GENDER_FEMALE, id: @champ.female_input_id
%label.fr-label{ for: @champ.female_input_id }
%label = Individual.human_attribute_name('gender.female')
= @form.radio_button :value, Individual::GENDER_MALE, id: @champ.male_input_id .fr-fieldset__element.fr-fieldset__element--inline
= Individual.human_attribute_name('gender.male') .fr-radio-group
= @form.radio_button :value, Individual::GENDER_MALE, id: @champ.male_input_id
%label.fr-label{ for: @champ.male_input_id }
= Individual.human_attribute_name('gender.male')

View file

@ -1,6 +1,10 @@
class EditableChamp::CommunesComponent < EditableChamp::EditableChampBaseComponent class EditableChamp::CommunesComponent < EditableChamp::EditableChampBaseComponent
include ApplicationHelper include ApplicationHelper
def dsfr_champ_container
:fieldset
end
private private
def commune_options def commune_options

View file

@ -1,4 +1,5 @@
--- ---
en: en:
postal_code: Enter the postal code then select the municipality from the list postal_code: "Enter <strong>the postal code</strong>"
commune: "Select <strong>the municipality</strong> from the list"
not_found: No municipality found for postal code %{postal_code}. Please check that you haven't made any mistakes. not_found: No municipality found for postal code %{postal_code}. Please check that you haven't made any mistakes.

View file

@ -1,4 +1,5 @@
--- ---
fr: fr:
postal_code: Renseignez le code postal puis sélectionnez la commune dans la liste postal_code: "Renseignez le <strong>code postal</strong>"
commune: "Sélectionnez la commune dans la liste"
not_found: Aucune commune trouvée pour le code postal %{postal_code}. Verifiez que vous n'avez pas fait derreur. not_found: Aucune commune trouvée pour le code postal %{postal_code}. Verifiez que vous n'avez pas fait derreur.

View file

@ -1,13 +1,23 @@
%label.notice{ for: code_postal_input_id }= t('.postal_code') .fr-fieldset__element.fr-mb-0
= @form.text_field :code_postal, required: @champ.required?, id: code_postal_input_id, class: "width-33-desktop width-100-mobile small-margin" .fr-input-group
- if @champ.code_postal? = @form.label :code_postal, t('.postal_code').html_safe, class: 'fr-label', for: code_postal_input_id
= @form.text_field :code_postal, required: @champ.required?, id: code_postal_input_id, class: "width-33-desktop width-100-mobile small-margin fr-input"
- if @champ.code_postal?
- if commune_options.empty?
.fr-error-text.mb-4= t('.not_found', postal_code: @champ.code_postal)
.fr-fieldset__element.fr-mb-0
- if commune_options.empty? - if commune_options.empty?
.fr-error-text.mb-4= t('.not_found', postal_code: @champ.code_postal) -# noop
- elsif commune_options.size <= 3 - elsif commune_options.size <= 3
%fieldset.radios %fieldset.fr-fieldset
.fr-fieldset__legend--regular.fr-fieldset__legend= t('.commune').html_safe
- commune_options.each.with_index do |(option, value), index| - commune_options.each.with_index do |(option, value), index|
%label .fr-fieldset__element
= @form.radio_button :value, value, checked: @champ.selected == value, id: index == 0 ? @champ.input_id : nil .fr-radio-group
= option = @form.radio_button :value, value, checked: @champ.selected == value, id: "#{code_postal_input_id}-#{value.parameterize}"
= @form.label :value, option, for: "#{code_postal_input_id}-#{value.parameterize}", class: 'fr-label'
- else - else
= @form.select :value, commune_options, commune_select_options, required: @champ.required?, id: @champ.input_id, aria: { describedby: @champ.describedby_id }, class: "width-33-desktop width-100-mobile" = @form.label :value, t('.commune').html_safe, for: @champ.input_id, class: 'fr-label'
= @form.select :value, commune_options, commune_select_options, required: @champ.required?, id: @champ.input_id, aria: { describedby: @champ.describedby_id }, class: "width-33-desktop width-100-mobile fr-select"

View file

@ -1,2 +1,5 @@
class EditableChamp::DateComponent < EditableChamp::EditableChampBaseComponent class EditableChamp::DateComponent < EditableChamp::EditableChampBaseComponent
def dsfr_input_classname
'fr-input'
end
end end

View file

@ -1,4 +1,8 @@
class EditableChamp::DatetimeComponent < EditableChamp::EditableChampBaseComponent class EditableChamp::DatetimeComponent < EditableChamp::EditableChampBaseComponent
def dsfr_input_classname
'fr-input'
end
def formatted_value_for_datetime_locale def formatted_value_for_datetime_locale
if @champ.valid? && @champ.value.present? if @champ.valid? && @champ.value.present?
# convert to a format that the datetime-local input can understand # convert to a format that the datetime-local input can understand

View file

@ -1,2 +1,5 @@
class EditableChamp::DecimalNumberComponent < EditableChamp::EditableChampBaseComponent class EditableChamp::DecimalNumberComponent < EditableChamp::EditableChampBaseComponent
def dsfr_input_classname
'fr-input'
end
end end

View file

@ -3,6 +3,10 @@ class EditableChamp::DepartementsComponent < EditableChamp::EditableChampBaseCom
private private
def dsfr_input_classname
'fr-select'
end
def options def options
APIGeoService.departements.map { ["#{_1[:code]} #{_1[:name]}", _1[:code]] } APIGeoService.departements.map { ["#{_1[:code]} #{_1[:name]}", _1[:code]] }
end end

View file

@ -1 +1 @@
= @form.select :value, options, select_options, required: @champ.required?, id: @champ.input_id, aria: { describedby: @champ.describedby_id }, class: "width-33-desktop width-100-mobile" = @form.select :value, options, select_options, required: @champ.required?, id: @champ.input_id, aria: { describedby: @champ.describedby_id }, class: "fr-select"

View file

@ -1,2 +1,5 @@
class EditableChamp::DgfipComponent < EditableChamp::EditableChampBaseComponent class EditableChamp::DgfipComponent < EditableChamp::EditableChampBaseComponent
def dsfr_input_classname
'fr-input'
end
end end

View file

@ -1,4 +1,8 @@
class EditableChamp::DossierLinkComponent < EditableChamp::EditableChampBaseComponent class EditableChamp::DossierLinkComponent < EditableChamp::EditableChampBaseComponent
def dsfr_input_classname
'fr-input'
end
def dossier def dossier
@dossier ||= @champ.blank? ? nil : Dossier.visible_by_administration.find_by(id: @champ.to_s) @dossier ||= @champ.blank? ? nil : Dossier.visible_by_administration.find_by(id: @champ.to_s)
end end

View file

@ -1,6 +1,14 @@
class EditableChamp::DropDownListComponent < EditableChamp::EditableChampBaseComponent class EditableChamp::DropDownListComponent < EditableChamp::EditableChampBaseComponent
def select_class_names def select_class_names
class_names('width-100': contains_long_option?) class_names('width-100': contains_long_option?, 'fr-select': true)
end
def dsfr_input_classname
'fr-select'
end
def dsfr_champ_container
@champ.render_as_radios? ? :fieldset : :div
end end
def contains_long_option? def contains_long_option?

View file

@ -1,22 +1,33 @@
- if @champ.options? - if @champ.options?
- if @champ.render_as_radios? - if @champ.render_as_radios?
%fieldset.radios .fr-fieldset__content
- @champ.enabled_non_empty_options.each do |option| - @champ.enabled_non_empty_options.each_with_index do |option, index|
%label .fr-radio-group
= @form.radio_button :value, option = @form.radio_button :value, option, id: "#{@champ.id}_radio_option_#{index}"
= option %label.fr-label{ for: "#{@champ.id}_radio_option_#{index}" }
= option
- if !@champ.mandatory? - if !@champ.mandatory?
%label.blank-radio .fr-radio-group
= @form.radio_button :value, '', checked: @champ.value.blank? && !@champ.other? = @form.radio_button :value, '', checked: @champ.value.blank? && !@champ.other?, id: "#{@champ.id}_radio_option_blank"
Non renseigné %label.fr-label{ for: "#{@champ.id}_radio_option_blank" }
Non renseigné
- if @champ.drop_down_other? - if @champ.drop_down_other?
%label .fr-radio-group
= @form.radio_button :value, Champs::DropDownListChamp::OTHER, checked: @champ.other? = @form.radio_button :value, Champs::DropDownListChamp::OTHER, checked: @champ.other?, id: "#{@champ.id}_radio_option_other"
Autre %label.fr-label{ for: "#{@champ.id}_radio_option_other" }
Autre
- elsif @champ.render_as_combobox?
= render Dsfr::ComboboxComponent.new form: @form, name: :value, options: @champ.enabled_non_empty_options, selected: @champ.selected, id: @champ.input_id, class: select_class_names, describedby: @champ.describedby_id
- else - else
= @form.select :value, @champ.options_without_empty_value_when_mandatory(@champ.options), { selected: @champ.selected }, required: @champ.required?, id: @champ.input_id, class: select_class_names, aria: { describedby: @champ.describedby_id } = @form.select :value,
@champ.options.compact_blank,
{ selected: @champ.selected, include_blank: true },
required: @champ.required?,
id: @champ.input_id,
class: select_class_names,
aria: { describedby: @champ.describedby_id }
- if @champ.drop_down_other? - if @champ.drop_down_other?
= render EditableChamp::DropDownOtherInputComponent.new(form: @form, champ: @champ) = render EditableChamp::DropDownOtherInputComponent.new(form: @form, champ: @champ)

View file

@ -1,4 +1,5 @@
.drop_down_other .drop_down_other.fr-mt-2w
.notice .fr-input-group
%label{ for: dom_id(@champ, :value_other) } Veuillez saisir votre autre choix %label.fr-label{ for: dom_id(@champ, :value_other) } Veuillez saisir votre autre choix
= @form.text_field :value_other, maxlength: 200, size: nil, id: dom_id(@champ, :value_other)
= @form.text_field :value_other, maxlength: 200, size: nil, id: dom_id(@champ, :value_other), class: 'fr-input'

View file

@ -1,6 +1,18 @@
class EditableChamp::EditableChampBaseComponent < ApplicationComponent class EditableChamp::EditableChampBaseComponent < ApplicationComponent
include Dsfr::InputErrorable include Dsfr::InputErrorable
def dsfr_champ_container
:div
end
def dsfr_input_classname
nil
end
def describedby_id
@champ.describedby_id
end
def initialize(form:, champ:, seen_at: nil, opts: {}) def initialize(form:, champ:, seen_at: nil, opts: {})
@form, @champ, @seen_at, @opts = form, champ, seen_at, opts @form, @champ, @seen_at, @opts = form, champ, seen_at, opts
@attribute = :value @attribute = :value

View file

@ -1,18 +1,21 @@
class EditableChamp::EditableChampComponent < ApplicationComponent class EditableChamp::EditableChampComponent < ApplicationComponent
include Dsfr::InputErrorable
def initialize(form:, champ:, seen_at: nil) def initialize(form:, champ:, seen_at: nil)
@form, @champ, @seen_at = form, champ, seen_at @form, @champ, @seen_at = form, champ, seen_at
@attribute = :value @attribute = :value
end end
def champ_component
@champ_component ||= component_class.new(form: @form, champ: @champ, seen_at: @seen_at)
end
private private
def has_label?(champ) def has_label?(champ)
types_without_label = [ types_without_label = [
TypeDeChamp.type_champs.fetch(:header_section), TypeDeChamp.type_champs.fetch(:header_section),
TypeDeChamp.type_champs.fetch(:explication), TypeDeChamp.type_champs.fetch(:explication),
TypeDeChamp.type_champs.fetch(:repetition) TypeDeChamp.type_champs.fetch(:repetition),
TypeDeChamp.type_champs.fetch(:linked_drop_down_list)
] ]
!types_without_label.include?(@champ.type_champ) !types_without_label.include?(@champ.type_champ)
end end
@ -28,8 +31,8 @@ class EditableChamp::EditableChampComponent < ApplicationComponent
'editable-champ': true, 'editable-champ': true,
"editable-champ-#{@champ.type_champ}": true, "editable-champ-#{@champ.type_champ}": true,
"hidden": !@champ.visible?, "hidden": !@champ.visible?,
"fr-input-group": true champ_component.dsfr_group_classname => true
}.merge(input_group_error_class_names) }.merge(champ_component.input_group_error_class_names)
), ),
id: @champ.input_group_id, id: @champ.input_group_id,
data: { controller: stimulus_controller, **data_dependent_conditions, **stimulus_values } data: { controller: stimulus_controller, **data_dependent_conditions, **stimulus_values }

View file

@ -1,15 +1,9 @@
= content_tag(:div, html_options) do = content_tag((champ_component.dsfr_champ_container), html_options) do
- if has_label?(@champ) - if has_label?(@champ)
= render EditableChamp::ChampLabelComponent.new form: @form, champ: @champ, seen_at: @seen_at = render EditableChamp::ChampLabelComponent.new form: @form, champ: @champ, seen_at: @seen_at
= render component_class.new(form: @form, champ: @champ, seen_at: @seen_at)
- if errors_on_attribute? = render champ_component
- if error_full_messages.size == 1
%p.fr-error-text{ id: describedby_id }= error_full_messages.first = render Dsfr::InputStatusMessageComponent.new(errors_on_attribute: champ_component.errors_on_attribute?, error_full_messages: champ_component.error_full_messages, described_by: @champ.describedby_id)
- else
.fr-error-text{ id: describedby_id }
%ul.list-style-type-none.fr-pl-0
- error_full_messages.each do |error_message|
%li= error_message
= @form.hidden_field :id, value: @champ.id = @form.hidden_field :id, value: @champ.id

View file

@ -1,4 +1,8 @@
class EditableChamp::EmailComponent < EditableChamp::EditableChampBaseComponent class EditableChamp::EmailComponent < EditableChamp::EditableChampBaseComponent
def dsfr_input_classname
'fr-input'
end
def email? def email?
true true
end end

View file

@ -1,6 +1,10 @@
class EditableChamp::EpciComponent < EditableChamp::EditableChampBaseComponent class EditableChamp::EpciComponent < EditableChamp::EditableChampBaseComponent
include ApplicationHelper include ApplicationHelper
def dsfr_champ_container
:fieldset
end
private private
def departement_options def departement_options

View file

@ -1,5 +1,11 @@
%label.notice{ for: @champ.code_departement_input_id } Le département de lEPCI .fr-fieldset__element
= @form.select :code_departement, departement_options, departement_select_options, required: @champ.required?, id: @champ.code_departement_input_id, class: "width-33-desktop width-100-mobile" = @form.label :code_departement, for: @champ.code_departement_input_id, class: 'fr-label' do
- "Le département de lEPCI"
= @form.select :code_departement, departement_options, departement_select_options, required: @champ.required?, id: @champ.code_departement_input_id, class: "width-33-desktop width-100-mobile fr-select"
- if @champ.departement? - if @champ.departement?
= @form.label "EPCI", for: @champ.epci_input_id .fr-fieldset__element
= @form.select :value, epci_options, epci_select_options, required: @champ.required?, id: @champ.epci_input_id, aria: { describedby: @champ.describedby_id }, class: "width-33-desktop width-100-mobile" .fr-select-group
= @form.label :value, for: @champ.epci_input_id, class: 'fr-label' do
- "EPCI"
= @form.select :value, epci_options, epci_select_options, required: @champ.required?, id: @champ.epci_input_id, aria: { describedby: @champ.describedby_id }, class: "width-33-desktop width-100-mobile fr-select"

View file

@ -1,2 +1,5 @@
class EditableChamp::IbanComponent < EditableChamp::EditableChampBaseComponent class EditableChamp::IbanComponent < EditableChamp::EditableChampBaseComponent
def dsfr_input_classname
'fr-input'
end
end end

View file

@ -1,2 +1,5 @@
class EditableChamp::IntegerNumberComponent < EditableChamp::EditableChampBaseComponent class EditableChamp::IntegerNumberComponent < EditableChamp::EditableChampBaseComponent
def dsfr_input_classname
'fr-input'
end
end end

View file

@ -1,4 +1,8 @@
class EditableChamp::LinkedDropDownListComponent < EditableChamp::EditableChampBaseComponent class EditableChamp::LinkedDropDownListComponent < EditableChamp::EditableChampBaseComponent
def dsfr_champ_container
:fieldset
end
private private
def secondary_label def secondary_label

View file

@ -1,12 +1,19 @@
- if @champ.options? - if @champ.options?
= @form.select :primary_value, @champ.primary_options, {}, required: @champ.required?, id: @champ.input_id, aria: { describedby: @champ.describedby_id } .fr-fieldset__element.fr-mb-0
.fr-select-group
= render EditableChamp::ChampLabelComponent.new form: @form, champ: @champ, seen_at: @seen_at
= @form.select :primary_value, @champ.primary_options, {}, required: @champ.required?, class: 'fr-select fr-mb-3v', id: @champ.input_id, aria: { describedby: @champ.describedby_id }
- if @champ.has_secondary_options_for_primary? - if @champ.has_secondary_options_for_primary?
.secondary .secondary
= @form.label :secondary_value, for: "#{@champ.input_id}-secondary" do .fr-fieldset__element
- sanitize(secondary_label) .fr-select-group
- if @champ.drop_down_secondary_description.present? = @form.label :secondary_value, for: "#{@champ.input_id}-secondary", class: 'fr-label' do
.notice{ id: "#{@champ.describedby_id}-secondary" } - sanitize(secondary_label)
= render SimpleFormatComponent.new(@champ.drop_down_secondary_description, allow_a: true) - if @champ.drop_down_secondary_description.present?
= @form.select :secondary_value, @champ.secondary_options[@champ.primary_value], {}, required: @champ.required?, id: "#{@champ.input_id}-secondary", aria: { describedby: "#{@champ.describedby_id}-secondary" } .notice{ id: "#{@champ.describedby_id}-secondary" }
= render SimpleFormatComponent.new(@champ.drop_down_secondary_description, allow_a: true)
= @form.select :secondary_value, @champ.secondary_options[@champ.primary_value], {}, required: @champ.required?, class: 'fr-select', id: "#{@champ.input_id}-secondary", aria: { describedby: "#{@champ.describedby_id}-secondary" }
- else - else
= @form.hidden_field :secondary_value, value: '' = @form.hidden_field :secondary_value, value: ''

View file

@ -1,2 +1,5 @@
class EditableChamp::MesriComponent < EditableChamp::EditableChampBaseComponent class EditableChamp::MesriComponent < EditableChamp::EditableChampBaseComponent
def dsfr_input_classname
'fr-input'
end
end end

View file

@ -1,3 +1,11 @@
class EditableChamp::MultipleDropDownListComponent < EditableChamp::EditableChampBaseComponent class EditableChamp::MultipleDropDownListComponent < EditableChamp::EditableChampBaseComponent
include ApplicationHelper include ApplicationHelper
def dsfr_input_classname
'fr-select'
end
def dsfr_champ_container
@champ.render_as_checkboxes? ? :fieldset : :div
end
end end

View file

@ -1,15 +1,19 @@
- if @champ.options? - if @champ.options?
- if @champ.render_as_checkboxes? - if @champ.render_as_checkboxes?
= @form.collection_check_boxes(:value, @champ.enabled_non_empty_options, :to_s, :to_s) do |b| = @form.collection_check_boxes :value, @champ.enabled_non_empty_options, :to_s, :to_s do |b|
- tag.div(class: 'editable-champ editable-champ-checkbox') do - capture do
- b.label(for: @champ.checkbox_id(b.value)) do .fr-fieldset__element
- b.check_box({ multiple: true, checked: @champ.selected_options.include?(b.value), aria: { describedby: @champ.describedby_id }, id: @champ.checkbox_id(b.value) }) + b.text .fr-checkbox-group
= b.check_box(checked: @champ.selected_options.include?(b.value), aria: { describedby: @champ.describedby_id }, id: @champ.checkbox_id(b.value), class: 'fr-checkbox-group__checkbox')
%label.fr-label{ for: @champ.checkbox_id(b.value) }
= b.text
- else - else
%div{ 'data-turbo-focus-group': true } %div{ 'data-turbo-focus-group': true }
- if @champ.selected_options.present? - if @champ.selected_options.present?
.fr-mb-2w .fr-mb-2w{ "data-turbo": "true" }
- @champ.selected_options.each do |option| - @champ.selected_options.each do |option|
= render NestedForms::OwnedButtonComponent.new(formaction: champs_options_path(@champ.id, option:), http_method: :delete, opt: { class: 'fr-tag fr-tag--dismiss fr-mb-1w fr-mr-1w', id: @champ.checkbox_id(option) }) do = render NestedForms::OwnedButtonComponent.new(formaction: champs_options_path(@champ.id, option:), http_method: :delete, opt: { aria: {pressed: true }, class: 'fr-tag fr-tag-bug fr-mb-1w fr-mr-1w', id: @champ.checkbox_id(option) }) do
= option = option
- if @champ.unselected_options.present? - if @champ.unselected_options.present?
= @form.select :value, @champ.unselected_options, { selected: '', include_blank: '' }, id: @champ.input_id, aria: { describedby: @champ.describedby_id } = @form.select :value, @champ.unselected_options, { selected: '', include_blank: '' }, id: @champ.input_id, aria: { describedby: @champ.describedby_id }, class: 'fr-select fr-mt-2v'

View file

@ -1,2 +1,5 @@
class EditableChamp::NumberComponent < EditableChamp::EditableChampBaseComponent class EditableChamp::NumberComponent < EditableChamp::EditableChampBaseComponent
def dsfr_input_classname
'fr-input'
end
end end

View file

@ -1,6 +1,10 @@
class EditableChamp::PaysComponent < EditableChamp::EditableChampBaseComponent class EditableChamp::PaysComponent < EditableChamp::EditableChampBaseComponent
include ApplicationHelper include ApplicationHelper
def dsfr_input_classname
'fr-select'
end
private private
def options def options

View file

@ -1 +1 @@
= @form.select :value, options, select_options, required: @champ.required?, id: @champ.input_id, aria: { describedby: @champ.describedby_id }, class: "width-33-desktop width-100-mobile" = @form.select :value, options, select_options, required: @champ.required?, id: @champ.input_id, aria: { describedby: @champ.describedby_id }, class: "width-33-desktop width-100-mobile fr-select"

View file

@ -1,2 +1,5 @@
class EditableChamp::PhoneComponent < EditableChamp::EditableChampBaseComponent class EditableChamp::PhoneComponent < EditableChamp::EditableChampBaseComponent
def dsfr_input_classname
'fr-input'
end
end end

View file

@ -1,4 +1,8 @@
class EditableChamp::PieceJustificativeComponent < EditableChamp::EditableChampBaseComponent class EditableChamp::PieceJustificativeComponent < EditableChamp::EditableChampBaseComponent
def dsfr_input_classname
'fr-input'
end
def view_as def view_as
if @champ.dossier.brouillon? if @champ.dossier.brouillon?
:link :link

View file

@ -1,6 +1,10 @@
class EditableChamp::RegionsComponent < EditableChamp::EditableChampBaseComponent class EditableChamp::RegionsComponent < EditableChamp::EditableChampBaseComponent
include ApplicationHelper include ApplicationHelper
def dsfr_input_classname
'fr-select'
end
private private
def options def options

View file

@ -1 +1 @@
= @form.select :value, options, select_options, required: @champ.required?, id: @champ.input_id, aria: { describedby: @champ.describedby_id }, class: "width-33-desktop width-100-mobile" = @form.select :value, options, select_options, required: @champ.required?, id: @champ.input_id, aria: { describedby: @champ.describedby_id }, class: "width-33-desktop width-100-mobile fr-select"

View file

@ -1,2 +1,5 @@
class EditableChamp::RNAComponent < EditableChamp::EditableChampBaseComponent class EditableChamp::RNAComponent < EditableChamp::EditableChampBaseComponent
def dsfr_input_classname
'fr-input'
end
end end

View file

@ -1,2 +1,5 @@
class EditableChamp::SiretComponent < EditableChamp::EditableChampBaseComponent class EditableChamp::SiretComponent < EditableChamp::EditableChampBaseComponent
def dsfr_input_classname
'fr-input'
end
end end

View file

@ -1,5 +1,4 @@
= @form.text_field(:value, input_opts(id: @champ.input_id, aria: { describedby: @champ.describedby_id }, placeholder: t(".placeholder"), data: { controller: 'turbo-input', turbo_input_load_on_connect_value: @champ.prefilled? && @champ.value.present? && @champ.etablissement.blank?, turbo_input_url_value: champs_siret_path(@champ.id) }, required: @champ.required?, pattern: "[0-9]{14}", title: t(".title"), class: "width-33-desktop", maxlength: 14)) = @form.text_field(:value, input_opts(id: @champ.input_id, aria: { describedby: @champ.describedby_id }, placeholder: t(".placeholder"), data: { controller: 'turbo-input', turbo_input_load_on_connect_value: @champ.prefilled? && @champ.value.present? && @champ.etablissement.blank?, turbo_input_url_value: champs_siret_path(@champ.id) }, required: @champ.required?, pattern: "[0-9]{14}", title: t(".title"), class: "width-33-desktop", maxlength: 14))
.spinner.right.hidden
.siret-info{ id: dom_id(@champ, :siret_info) } .siret-info{ id: dom_id(@champ, :siret_info) }
- if @champ.etablissement.present? - if @champ.etablissement.present?
= render EditableChamp::EtablissementTitreComponent.new(etablissement: @champ.etablissement) = render EditableChamp::EtablissementTitreComponent.new(etablissement: @champ.etablissement)

View file

@ -1,2 +1,5 @@
class EditableChamp::TextComponent < EditableChamp::EditableChampBaseComponent class EditableChamp::TextComponent < EditableChamp::EditableChampBaseComponent
def dsfr_input_classname
'fr-input'
end
end end

View file

@ -1,3 +1,6 @@
class EditableChamp::TextareaComponent < EditableChamp::EditableChampBaseComponent class EditableChamp::TextareaComponent < EditableChamp::EditableChampBaseComponent
include HtmlToStringHelper include HtmlToStringHelper
def dsfr_input_classname
'fr-input'
end
end end

View file

@ -1,4 +1,8 @@
class EditableChamp::TitreIdentiteComponent < EditableChamp::EditableChampBaseComponent class EditableChamp::TitreIdentiteComponent < EditableChamp::EditableChampBaseComponent
def dsfr_input_classname
'fr-input'
end
def user_can_destroy? def user_can_destroy?
!@champ.mandatory? || @champ.dossier.brouillon? !@champ.mandatory? || @champ.dossier.brouillon?
end end

View file

@ -1,2 +1,5 @@
class EditableChamp::YesNoComponent < EditableChamp::EditableChampBaseComponent class EditableChamp::YesNoComponent < EditableChamp::EditableChampBaseComponent
def dsfr_champ_container
:fieldset
end
end end

View file

@ -1,11 +1,11 @@
%fieldset.fr-fieldset
%legend.fr-fieldset__legend.visually-hidden
= t(".legend")
%label{ for: @champ.yes_input_id } .fr-fieldset__element.fr-fieldset__element--inline
.fr-radio-group
= @form.radio_button :value, true, id: @champ.yes_input_id = @form.radio_button :value, true, id: @champ.yes_input_id
= t(".yes") %label.fr-label{ for: @champ.yes_input_id }
= t(".yes")
%label{ for: @champ.no_input_id } .fr-fieldset__element.fr-fieldset__element--inline
.fr-radio-group
= @form.radio_button :value, false, id: @champ.no_input_id = @form.radio_button :value, false, id: @champ.no_input_id
= t(".no") %label.fr-label{ for: @champ.no_input_id }
= t(".no")

View file

@ -0,0 +1,6 @@
class Instructeurs::ActivateAccountFormComponent < ApplicationComponent
attr_reader :user
def initialize(user:)
@user = user
end
end

View file

@ -0,0 +1,6 @@
---
en:
title: Create your account for %{application_name}
activate: Activate your account %{email}
email_disabled: Instructor email address not changeable
submit: Choose password

View file

@ -0,0 +1,7 @@
---
fr:
title: Création de compte sur %{application_name}
activate: Se créer un compte pour %{email} en choissant un mot de passe
email_disabled: Adresse instructeur non modifiable
submit: Définir le mot de passe

View file

@ -0,0 +1,23 @@
= form_for user, url: { controller: 'users/activate', action: :create }, html: { class: "fr-py-5w" } do |f|
%h1.fr-h2.fr-mb-7w= t('.title', application_name: APPLICATION_NAME)
.fr-background-alt--grey.fr-px-12w.fr-py-7w
%fieldset.fr-mb-0.fr-fieldset{ aria: { labelledby: 'activate-account-legend' } }
%legend.fr-fieldset__legend#activate-account-legend
%h2.fr-h6.fr-mb-0= t('.activate', email: user.email)
.fr-fieldset__element
%p.fr-text--sm= t('utils.mandatory_champs')
.fr-fieldset__element= render Dsfr::InputComponent.new(form: f, attribute: :email, input_type: :email_field, opts: { disabled: :disabled, class: 'fr-input-group--disabled', value: t('.email_disabled') })
.fr-fieldset__element= render Dsfr::InputComponent.new(form: f, attribute: :password, input_type: :password_field, opts: { autocomplete: 'current-password' })
= f.hidden_field :reset_password_token, value: params[:token]
.fr-fieldset__element
.fr-btns-group--right.fr-btns-group.fr-btns-group--inline.fr-btns-group.fr-btns-group--inline
%ul
%li= f.submit t('.submit'), class: 'fr-mt-2v fr-btn fr-btn'

View file

@ -3,6 +3,8 @@
class Instructeurs::EnConstructionMenuComponent < ApplicationComponent class Instructeurs::EnConstructionMenuComponent < ApplicationComponent
attr_reader :dossier attr_reader :dossier
delegate :sva_svr_enabled?, to: :"dossier.procedure"
def initialize(dossier:) def initialize(dossier:)
@dossier = dossier @dossier = dossier
end end
@ -22,11 +24,11 @@ class Instructeurs::EnConstructionMenuComponent < ApplicationComponent
end end
end end
def sva? def sva_svr_resume_method
dossier.procedure.sva?
end
def sva_resume_method
dossier.procedure.sva_svr_configuration.resume dossier.procedure.sva_svr_configuration.resume
end end
def sva_svr_human_decision
dossier.procedure.sva_svr_configuration.human_decision
end
end end

View file

@ -18,11 +18,11 @@
%h4= t('.request_correction') %h4= t('.request_correction')
Lusager sera informé que des modifications sont attendues. Lusager sera informé que des modifications sont attendues.
- if sva? - if sva_svr_enabled?
- if sva_resume_method == :reset - if sva_svr_resume_method == :reset
Le délai du SVA sera réinitialisé lorquil déclarera avoir complété le dossier. Le délai du #{sva_svr_human_decision} sera réinitialisé lorquil déclarera avoir complété le dossier.
- else - else
Le délai du SVA reprendra lorsquil déclarera avoir corrigé le dossier. Le délai du #{sva_svr_human_decision} reprendra lorsquil déclarera avoir corrigé le dossier.
- menu.with_item(class: "inactive form-inside fr-pt-1v") do - menu.with_item(class: "inactive form-inside fr-pt-1v") do
= render partial: 'instructeurs/dossiers/instruction_button_motivation', locals: { dossier:, = render partial: 'instructeurs/dossiers/instruction_button_motivation', locals: { dossier:,
@ -36,14 +36,14 @@
title: 'Marquer en attente de corrections', title: 'Marquer en attente de corrections',
confirm: 'Envoyer la demande de corrections ?'} confirm: 'Envoyer la demande de corrections ?'}
- if sva? - if sva_svr_enabled?
- menu.with_item do - menu.with_item do
= link_to('#', onclick: "DS.showMotivation(event, 'pending_completion');", role: 'menuitem') do = link_to('#', onclick: "DS.showMotivation(event, 'pending_completion');", role: 'menuitem') do
%span.fr-icon.fr-icon-error-warning-line.fr-text-default--warning.fr-mt-1v{ "aria-hidden": "true" } %span.fr-icon.fr-icon-error-warning-line.fr-text-default--warning.fr-mt-1v{ "aria-hidden": "true" }
.dropdown-description .dropdown-description
%h4= t('.request_completion') %h4= t('.request_completion')
Lusager sera informé que son dossier est incomplet. Le délai du SVA sera réinitialisé lorque il déclarera avoir complété le dossier. Lusager sera informé que son dossier est incomplet. Le délai du #{sva_svr_human_decision} sera réinitialisé lorque il déclarera avoir complété le dossier.
- menu.with_item(class: "inactive form-inside fr-pt-1v") do - menu.with_item(class: "inactive form-inside fr-pt-1v") do
= render partial: 'instructeurs/dossiers/instruction_button_motivation', locals: { dossier:, = render partial: 'instructeurs/dossiers/instruction_button_motivation', locals: { dossier:,

View file

@ -26,11 +26,13 @@ class Instructeurs::SVASVRDecisionBadgeComponent < ApplicationComponent
class_names( class_names(
'fr-badge fr-badge--sm': true, 'fr-badge fr-badge--sm': true,
'fr-badge--warning': soon?, 'fr-badge--warning': soon?,
'fr-badge--info': !soon? 'fr-badge--info': !without_date? && !soon?
) )
end end
def soon? def soon?
return false if object.sva_svr_decision_on.nil?
object.sva_svr_decision_on < 7.days.from_now.to_date object.sva_svr_decision_on < 7.days.from_now.to_date
end end
@ -51,16 +53,36 @@ class Instructeurs::SVASVRDecisionBadgeComponent < ApplicationComponent
end end
def label_for_badge def label_for_badge
sva? ? "SVA :" : "SVR :" "#{human_decision} : "
end end
def title def title
return if without_date? if previously_termine?
t('.previously_termine_title')
if pending_correction? elsif depose_before_configuration?
t('.depose_before_configuration_title', decision: human_decision)
elsif without_date?
t('.manual_decision_title', decision: human_decision)
elsif pending_correction?
t(".dossier_terminated_x_days_after_correction", count: days_count) t(".dossier_terminated_x_days_after_correction", count: days_count)
else else
t(".dossier_terminated_on", date: helpers.l(object.sva_svr_decision_on)) t(".dossier_terminated_on", date: helpers.l(object.sva_svr_decision_on))
end end
end end
def human_decision
procedure.sva_svr_configuration.human_decision
end
def previously_termine?
return if !object.respond_to?(:previously_termine?)
object.previously_termine?
end
def depose_before_configuration?
return if !object.respond_to?(:sva_svr_decision_triggered_at)
object.sva_svr_decision_on.nil? && object.sva_svr_decision_triggered_at.nil?
end
end end

View file

@ -1,7 +1,10 @@
--- ---
en: en:
no_sva: Submitted before SVA manual_decision: Manual instruction
no_svr: Submitted before SVR manual_decision_title: The file must be processed by an instructor, either because it was submitted before the %{decision} configuration, or because it has been returned to the instruction stage.
depose_before_configuration: Submitted before %{decision}
depose_before_configuration_title: This file was submitted before the %{decision} configuration.
previously_termine_title: The file has been returned to the instruction stage. It must now be processed by an instructor.
in_days: in_days:
zero: Today zero: Today
one: Tomorrow one: Tomorrow

View file

@ -1,7 +1,10 @@
--- ---
fr: fr:
no_sva: Déposé avant SVA manual_decision: Instruction manuelle
no_svr: Déposé avant SVR manual_decision_title: Le dossier doit être traité par un instructeur, soit car il a été déposé avant la configuration %{decision}, soit car il a été repassé en instruction.
depose_before_configuration: Déposé avant %{decision}
depose_before_configuration_title: Ce dossier a été déposé avant la configuration %{decision}.
previously_termine_title: Le dossier a été repassé en instruction. Il doit être traité par un instructeur.
in_days: in_days:
zero: Aujourdhui zero: Aujourdhui
one: Demain one: Demain

View file

@ -1,11 +1,14 @@
- if without_date? %span{ class: classes, title: title }
%span.fr-badge.fr-badge--sm - if with_label.present?
= t(sva? ? '.no_sva' : '.no_svr') = label_for_badge
- else
%span{ class: classes, title: title } - if previously_termine?
- if with_label.present? = t('.manual_decision')
= label_for_badge - elsif depose_before_configuration?
- if pending_correction? = t('.depose_before_configuration', decision: human_decision)
= t('.remaining_days_after_correction', count: days_count) - elsif without_date? # generic case without SVA/SVR date, when we have a projection
- else = t('.manual_decision')
= t('.in_days', count: days_count) - elsif pending_correction?
= t('.remaining_days_after_correction', count: days_count)
- else
= t('.in_days', count: days_count)

View file

@ -1,6 +1,6 @@
.fr-col-6.fr-col-md-4.fr-col-lg-3 .fr-col-6.fr-col-md-4.fr-col-lg-3
= link_to admin_procedure_administrateurs_path(@procedure), id: 'administrateurs', class: 'fr-tile fr-enlarge-link' do = link_to admin_procedure_administrateurs_path(@procedure), id: 'administrateurs', class: 'fr-tile fr-enlarge-link' do
.fr-tile__body.flex.justify-between .fr-tile__body.flex.column.align-center.justify-between
%div %div
%span.icon.accept %span.icon.accept
%p.fr-tile-status-accept Validé %p.fr-tile-status-accept Validé

View file

@ -1,6 +1,6 @@
.fr-col-6.fr-col-md-4.fr-col-lg-3 .fr-col-6.fr-col-md-4.fr-col-lg-3
= link_to annotations_admin_procedure_path(@procedure), class: 'fr-tile fr-enlarge-link', title: error_messages do = link_to annotations_admin_procedure_path(@procedure), class: 'fr-tile fr-enlarge-link', title: error_messages do
.fr-tile__body.flex.justify-between .fr-tile__body.flex.column.align-center.justify-between
- if error_messages.present? - if error_messages.present?
%div %div
%span.icon.refuse %span.icon.refuse

View file

@ -1,6 +1,6 @@
.fr-col-6.fr-col-md-4.fr-col-lg-3 .fr-col-6.fr-col-md-4.fr-col-lg-3
= link_to jeton_admin_procedure_path(@procedure), class: 'fr-tile fr-enlarge-link' do = link_to jeton_admin_procedure_path(@procedure), class: 'fr-tile fr-enlarge-link' do
.fr-tile__body.flex.justify-between .fr-tile__body.flex.column.align-center.justify-between
- if @procedure.api_entreprise_token.present? - if @procedure.api_entreprise_token.present?
%div %div
%span.icon.accept %span.icon.accept

View file

@ -1,6 +1,6 @@
.fr-col-6.fr-col-md-4.fr-col-lg-3 .fr-col-6.fr-col-md-4.fr-col-lg-3
= link_to admin_procedure_api_particulier_path(@procedure), class: 'fr-tile fr-enlarge-link', id: 'api-particulier' do = link_to admin_procedure_api_particulier_path(@procedure), class: 'fr-tile fr-enlarge-link', id: 'api-particulier' do
.fr-tile__body.flex.justify-between .fr-tile__body.flex.column.align-center.justify-between
- if @procedure.api_particulier_token.present? - if @procedure.api_particulier_token.present?
%div %div
%span.icon.accept %span.icon.accept

View file

@ -1,6 +1,6 @@
.fr-col-6.fr-col-md-4.fr-col-lg-3 .fr-col-6.fr-col-md-4.fr-col-lg-3
= link_to edit_admin_procedure_attestation_template_path(@procedure), class: 'fr-tile fr-enlarge-link' do = link_to edit_admin_procedure_attestation_template_path(@procedure), class: 'fr-tile fr-enlarge-link' do
.fr-tile__body.flex.justify-between .fr-tile__body.flex.column.align-center.justify-between
- if @procedure.attestation_template&.activated? - if @procedure.attestation_template&.activated?
%div %div
- if error_messages.present? - if error_messages.present?

View file

@ -1,6 +1,6 @@
.fr-col-6.fr-col-md-4.fr-col-lg-3 .fr-col-6.fr-col-md-4.fr-col-lg-3
= link_to champs_admin_procedure_path(@procedure), class: 'fr-tile fr-enlarge-link', title: error_messages do = link_to champs_admin_procedure_path(@procedure), class: 'fr-tile fr-enlarge-link', title: error_messages do
.fr-tile__body.flex.justify-between .fr-tile__body.flex.column.align-center.justify-between
- if error_messages.present? - if error_messages.present?
%div %div
%span.icon.refuse %span.icon.refuse

Some files were not shown because too many files have changed in this diff Show more