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',
'react-hooks/rules-of-hooks': 'error',
'react-hooks/exhaustive-deps': 'error',
'react/prop-types': 'off'
'react/prop-types': 'off',
'react/no-deprecated': 'off'
},
settings: {
react: { version: 'detect' }

View file

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

View file

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

View file

@ -59,6 +59,7 @@ gem 'lograge'
gem 'logstash-event'
gem 'mailjet', require: false
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-pop', require: false # same
gem 'net-smtp', require: false # same
@ -105,6 +106,7 @@ group :test do
gem 'rack_session_access'
gem 'rails-controller-testing'
gem 'rspec_junit_formatter'
gem 'rspec-retry'
gem 'selenium-devtools'
gem 'selenium-webdriver'
gem 'shoulda-matchers', require: false
@ -114,7 +116,6 @@ group :test do
end
group :development do
gem 'annotate'
gem 'brakeman', require: false
gem 'haml-lint'
gem 'letter_opener_web'

View file

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

View file

@ -17,6 +17,7 @@ Vous souhaitez y apporter des changements ou des améliorations ? Lisez notre [
#### Tous environnements
- postgresql
- imagemagick et gsfonts pour générer les filigranes sur les titres d'identité.
#### Développement
@ -28,7 +29,7 @@ Vous souhaitez y apporter des changements ou des améliorations ? Lisez notre [
- Chrome
- 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,
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"
```
Il peut être également pertinent de désactiver la mise à jour automatique du webdriver
en définissant une variable d'environnement `SKIP_UPDATE_WEBDRIVER` lors de l'exécution de `bin/update`.
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.
### 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;
}
.width-max-content {
width: max-content;
}
// sizing
.width-100 {
width: 100%;

View file

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

View file

@ -310,24 +310,6 @@ ul.dropdown-items {
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 {
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-position: inside;
padding-left: $default-padding;

View file

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

View file

@ -99,6 +99,21 @@ fieldset {
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
@media (min-width: 62em) {
.fr-nav__item.custom-fr-translate-flex-end {

View file

@ -20,7 +20,8 @@
font-style: italic;
}
.fr-input-group {
.fr-input-group,
.fr-select-group {
margin-bottom: 1rem;
}
@ -116,16 +117,8 @@
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 {
label {
padding-left: 28px;
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),
// on herite donc la fontsize/mrgin/padding du fr-hint-text
.fr-hint-text > * {
font-size: inherit;
margin: inherit;
padding: inherit;
}
}
input[type=password],
select {
select:not(.fr-select) {
display: block;
margin-bottom: 0;
@ -246,9 +238,7 @@
}
}
input[type=text]:not([data-address='true']),
select {
border-radius: 4px;
input[type=text]:not([data-address='true']) {
border: solid 1px $border-grey;
padding: $default-padding;
@ -524,7 +514,7 @@
}
}
fieldset ~ .spinner {
fieldset + .spinner {
position: relative;
top: -($default-fields-spacer / 2);
}
@ -631,3 +621,18 @@ textarea::placeholder {
background-color: $white;
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 {
padding: $default-padding;
text-align: left;
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;
@media (min-width: 48em) {
min-width: 400px;
}
}

View file

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

View file

@ -18,6 +18,8 @@
= render partial: "shared/champs/carte/show", locals: { champ: champ }
- when TypeDeChamp.type_champs.fetch(:dossier_link)
= 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)
= 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)

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
= label_tag :field, t('.column')
= select_tag :field, options_for_select(filterable_fields_for_select, field_id), include_blank: field_id.nil?
.fr-select-group
= label_tag :field, t('.column'), class: 'fr-label fr-m-0'
= select_tag :field, options_for_select(filterable_fields_for_select, field_id), include_blank: field_id.nil?, class: 'fr-select'
%input.hidden{ type: 'submit', formaction: update_filter_instructeur_procedure_path(procedure), data: { autosubmit_target: 'submitter' } }
%br
= label_tag :value, t('.value'), for: 'value'
= label_tag :value, t('.value'), for: 'value', class: 'fr-label'
- 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
%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
= 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?
end
def icon_class_name
active? ? 'fr-fi-checkbox' : 'fr-fi-checkbox-blank'
end
def order_desc?
current_order == 'desc'
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
.fr-toggle
= check_box_tag :order, opposite_order, active?, class: 'fr-toggle__input'
= label_tag :order, t('.show_notified_first'), class: 'fr-toggle__label fr-pl-1w'
.fr-fieldset__element.fr-m-0
.fr-checkbox-group.fr-checkbox-group--sm
= check_box_tag :order, opposite_order, active?
= 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|
= f.hidden_field :q, value: params[:q], id: nil
= f.label :procedure_id, t('.procedure.label'), class: 'sr-only'
.fr-input-group
= 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
# it uses aria-describedby on input and link it to yielded content
renders_one :describedby
renders_one :label
def initialize(form:, attribute:, input_type: :text_field, opts: {}, required: true)
@form = form
@ -16,6 +17,10 @@ class Dsfr::InputComponent < ApplicationComponent
@required = required
end
def dsfr_champ_container
:div
end
# add invalid class on input when input is invalid
# and and valid on input only if another input is invalid
def input_group_opts
@ -46,7 +51,11 @@ class Dsfr::InputComponent < ApplicationComponent
# i18n lookups
def label
object.class.human_attribute_name(@attribute)
get_slot(:label).presence || default_label
end
def dsfr_input_classname
'fr-input'
end
# kind of input helpers
@ -63,4 +72,8 @@ class Dsfr::InputComponent < ApplicationComponent
end
private
def default_label
object.class.human_attribute_name(@attribute)
end
end

View file

@ -1,10 +1,37 @@
module Dsfr
module InputErrorable
extend ActiveSupport::Concern
included do
delegate :object, to: :@form
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
# lookup for edge case from `form.rich_text_area`
@ -18,15 +45,14 @@ module Dsfr
end
end
def input_group_error_class_names
{
"fr-input-group--error": errors_on_attribute?,
"fr-input-group--valid": !errors_on_attribute? && errors_on_another_attribute?
}
def fr_fieldset?
!['fr-input', 'fr-radio', 'fr-select'].include?(dsfr_input_classname)
end
def input_error_class_names
{ 'fr-input--error': errors_on_attribute? }
{
"#{dsfr_input_classname}--error": errors_on_attribute?
}
end
def input_error_opts
@ -67,28 +93,19 @@ module Dsfr
@opts
end
def describedby_id
dom_id(@champ, :error_full_messages)
end
def errors_on_another_attribute?
!errors.empty?
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)
Array(array_or_string_or_nil).to_h { [_1, true] }
end
def hint
get_slot(:hint).presence || default_hint
end
def default_hint
I18n.t("activerecord.attributes.#{object.class.name.underscore}.hints.#{@attribute}")
end
@ -101,6 +118,8 @@ module Dsfr
end
def hint?
return true if get_slot(:hint).present?
I18n.exists?("activerecord.attributes.#{object.class.name.underscore}.hints.#{@attribute}")
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
def dsfr_input_classname
'fr-input'
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
- if @champ.html_label?
= @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
- 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
.fr-label.mb-4{ id: @champ.labelledby_id }
= render EditableChamp::ChampLabelContentComponent.new form: @form, champ: @champ, seen_at: @seen_at

View file

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

View file

@ -1,4 +1,9 @@
= @form.check_box :value,
.fr-fieldset__element
.fr-checkbox-group
= @form.check_box :value,
{ 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
%legend.mandatory-explanation
%fieldset.fr-fieldset
%legend.fr-fieldset__legend--regular.fr-fieldset__legend
Sélectionnez une des valeurs
%label
.fr-fieldset__element.fr-fieldset__element--inline
.fr-radio-group
= @form.radio_button :value, Individual::GENDER_FEMALE, id: @champ.female_input_id
%label.fr-label{ for: @champ.female_input_id }
= Individual.human_attribute_name('gender.female')
%label
.fr-fieldset__element.fr-fieldset__element--inline
.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
include ApplicationHelper
def dsfr_champ_container
:fieldset
end
private
def commune_options

View file

@ -1,4 +1,5 @@
---
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.

View file

@ -1,4 +1,5 @@
---
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.

View file

@ -1,13 +1,23 @@
%label.notice{ for: code_postal_input_id }= t('.postal_code')
= @form.text_field :code_postal, required: @champ.required?, id: code_postal_input_id, class: "width-33-desktop width-100-mobile small-margin"
- if @champ.code_postal?
.fr-fieldset__element.fr-mb-0
.fr-input-group
= @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?
-# noop
- 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|
%label
= @form.radio_button :value, value, checked: @champ.selected == value, id: index == 0 ? @champ.input_id : nil
= option
.fr-fieldset__element
.fr-radio-group
= @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
= @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
def dsfr_input_classname
'fr-input'
end
end

View file

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

View file

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

View file

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

View file

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

View file

@ -1,6 +1,14 @@
class EditableChamp::DropDownListComponent < EditableChamp::EditableChampBaseComponent
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
def contains_long_option?

View file

@ -1,22 +1,33 @@
- if @champ.options?
- if @champ.render_as_radios?
%fieldset.radios
- @champ.enabled_non_empty_options.each do |option|
%label
= @form.radio_button :value, option
.fr-fieldset__content
- @champ.enabled_non_empty_options.each_with_index do |option, index|
.fr-radio-group
= @form.radio_button :value, option, id: "#{@champ.id}_radio_option_#{index}"
%label.fr-label{ for: "#{@champ.id}_radio_option_#{index}" }
= option
- if !@champ.mandatory?
%label.blank-radio
= @form.radio_button :value, '', checked: @champ.value.blank? && !@champ.other?
.fr-radio-group
= @form.radio_button :value, '', checked: @champ.value.blank? && !@champ.other?, id: "#{@champ.id}_radio_option_blank"
%label.fr-label{ for: "#{@champ.id}_radio_option_blank" }
Non renseigné
- if @champ.drop_down_other?
%label
= @form.radio_button :value, Champs::DropDownListChamp::OTHER, checked: @champ.other?
.fr-radio-group
= @form.radio_button :value, Champs::DropDownListChamp::OTHER, checked: @champ.other?, id: "#{@champ.id}_radio_option_other"
%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
= @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?
= render EditableChamp::DropDownOtherInputComponent.new(form: @form, champ: @champ)

View file

@ -1,4 +1,5 @@
.drop_down_other
.notice
%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)
.drop_down_other.fr-mt-2w
.fr-input-group
%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), class: 'fr-input'

View file

@ -1,6 +1,18 @@
class EditableChamp::EditableChampBaseComponent < ApplicationComponent
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: {})
@form, @champ, @seen_at, @opts = form, champ, seen_at, opts
@attribute = :value

View file

@ -1,18 +1,21 @@
class EditableChamp::EditableChampComponent < ApplicationComponent
include Dsfr::InputErrorable
def initialize(form:, champ:, seen_at: nil)
@form, @champ, @seen_at = form, champ, seen_at
@attribute = :value
end
def champ_component
@champ_component ||= component_class.new(form: @form, champ: @champ, seen_at: @seen_at)
end
private
def has_label?(champ)
types_without_label = [
TypeDeChamp.type_champs.fetch(:header_section),
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)
end
@ -28,8 +31,8 @@ class EditableChamp::EditableChampComponent < ApplicationComponent
'editable-champ': true,
"editable-champ-#{@champ.type_champ}": true,
"hidden": !@champ.visible?,
"fr-input-group": true
}.merge(input_group_error_class_names)
champ_component.dsfr_group_classname => true
}.merge(champ_component.input_group_error_class_names)
),
id: @champ.input_group_id,
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)
= 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?
- if error_full_messages.size == 1
%p.fr-error-text{ id: describedby_id }= error_full_messages.first
- 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
= render champ_component
= 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)
= @form.hidden_field :id, value: @champ.id

View file

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

View file

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

View file

@ -1,5 +1,11 @@
%label.notice{ for: @champ.code_departement_input_id } 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-fieldset__element
= @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?
= @form.label "EPCI", for: @champ.epci_input_id
= @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-fieldset__element
.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
def dsfr_input_classname
'fr-input'
end
end

View file

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

View file

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

View file

@ -1,12 +1,19 @@
- 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?
.secondary
= @form.label :secondary_value, for: "#{@champ.input_id}-secondary" do
.fr-fieldset__element
.fr-select-group
= @form.label :secondary_value, for: "#{@champ.input_id}-secondary", class: 'fr-label' do
- sanitize(secondary_label)
- if @champ.drop_down_secondary_description.present?
.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?, id: "#{@champ.input_id}-secondary", aria: { describedby: "#{@champ.describedby_id}-secondary" }
= @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
= @form.hidden_field :secondary_value, value: ''

View file

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

View file

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

View file

@ -1,15 +1,19 @@
- if @champ.options?
- if @champ.render_as_checkboxes?
= @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
- b.label(for: @champ.checkbox_id(b.value)) do
- 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
= @form.collection_check_boxes :value, @champ.enabled_non_empty_options, :to_s, :to_s do |b|
- capture do
.fr-fieldset__element
.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
%div{ 'data-turbo-focus-group': true }
- if @champ.selected_options.present?
.fr-mb-2w
.fr-mb-2w{ "data-turbo": "true" }
- @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
- 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
def dsfr_input_classname
'fr-input'
end
end

View file

@ -1,6 +1,10 @@
class EditableChamp::PaysComponent < EditableChamp::EditableChampBaseComponent
include ApplicationHelper
def dsfr_input_classname
'fr-select'
end
private
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
def dsfr_input_classname
'fr-input'
end
end

View file

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

View file

@ -1,6 +1,10 @@
class EditableChamp::RegionsComponent < EditableChamp::EditableChampBaseComponent
include ApplicationHelper
def dsfr_input_classname
'fr-select'
end
private
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
def dsfr_input_classname
'fr-input'
end
end

View file

@ -1,2 +1,5 @@
class EditableChamp::SiretComponent < EditableChamp::EditableChampBaseComponent
def dsfr_input_classname
'fr-input'
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))
.spinner.right.hidden
.siret-info{ id: dom_id(@champ, :siret_info) }
- if @champ.etablissement.present?
= render EditableChamp::EtablissementTitreComponent.new(etablissement: @champ.etablissement)

View file

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

View file

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

View file

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

View file

@ -1,2 +1,5 @@
class EditableChamp::YesNoComponent < EditableChamp::EditableChampBaseComponent
def dsfr_champ_container
:fieldset
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
%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
%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
attr_reader :dossier
delegate :sva_svr_enabled?, to: :"dossier.procedure"
def initialize(dossier:)
@dossier = dossier
end
@ -22,11 +24,11 @@ class Instructeurs::EnConstructionMenuComponent < ApplicationComponent
end
end
def sva?
dossier.procedure.sva?
end
def sva_resume_method
def sva_svr_resume_method
dossier.procedure.sva_svr_configuration.resume
end
def sva_svr_human_decision
dossier.procedure.sva_svr_configuration.human_decision
end
end

View file

@ -18,11 +18,11 @@
%h4= t('.request_correction')
Lusager sera informé que des modifications sont attendues.
- if sva?
- if sva_resume_method == :reset
Le délai du SVA sera réinitialisé lorquil déclarera avoir complété le dossier.
- if sva_svr_enabled?
- if sva_svr_resume_method == :reset
Le délai du #{sva_svr_human_decision} sera réinitialisé lorquil déclarera avoir complété le dossier.
- 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
= render partial: 'instructeurs/dossiers/instruction_button_motivation', locals: { dossier:,
@ -36,14 +36,14 @@
title: 'Marquer en attente de corrections',
confirm: 'Envoyer la demande de corrections ?'}
- if sva?
- if sva_svr_enabled?
- menu.with_item 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" }
.dropdown-description
%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
= render partial: 'instructeurs/dossiers/instruction_button_motivation', locals: { dossier:,

View file

@ -26,11 +26,13 @@ class Instructeurs::SVASVRDecisionBadgeComponent < ApplicationComponent
class_names(
'fr-badge fr-badge--sm': true,
'fr-badge--warning': soon?,
'fr-badge--info': !soon?
'fr-badge--info': !without_date? && !soon?
)
end
def soon?
return false if object.sva_svr_decision_on.nil?
object.sva_svr_decision_on < 7.days.from_now.to_date
end
@ -51,16 +53,36 @@ class Instructeurs::SVASVRDecisionBadgeComponent < ApplicationComponent
end
def label_for_badge
sva? ? "SVA :" : "SVR :"
"#{human_decision} : "
end
def title
return if without_date?
if pending_correction?
if previously_termine?
t('.previously_termine_title')
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)
else
t(".dossier_terminated_on", date: helpers.l(object.sva_svr_decision_on))
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

View file

@ -1,7 +1,10 @@
---
en:
no_sva: Submitted before SVA
no_svr: Submitted before SVR
manual_decision: Manual instruction
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:
zero: Today
one: Tomorrow

View file

@ -1,7 +1,10 @@
---
fr:
no_sva: Déposé avant SVA
no_svr: Déposé avant SVR
manual_decision: Instruction manuelle
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:
zero: Aujourdhui
one: Demain

View file

@ -1,11 +1,14 @@
- if without_date?
%span.fr-badge.fr-badge--sm
= t(sva? ? '.no_sva' : '.no_svr')
- else
%span{ class: classes, title: title }
%span{ class: classes, title: title }
- if with_label.present?
= label_for_badge
- if pending_correction?
- if previously_termine?
= t('.manual_decision')
- elsif depose_before_configuration?
= t('.depose_before_configuration', decision: human_decision)
- elsif without_date? # generic case without SVA/SVR date, when we have a projection
= t('.manual_decision')
- 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
= 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
%span.icon.accept
%p.fr-tile-status-accept Validé

View file

@ -1,6 +1,6 @@
.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
.fr-tile__body.flex.justify-between
.fr-tile__body.flex.column.align-center.justify-between
- if error_messages.present?
%div
%span.icon.refuse

View file

@ -1,6 +1,6 @@
.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
.fr-tile__body.flex.justify-between
.fr-tile__body.flex.column.align-center.justify-between
- if @procedure.api_entreprise_token.present?
%div
%span.icon.accept

View file

@ -1,6 +1,6 @@
.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
.fr-tile__body.flex.justify-between
.fr-tile__body.flex.column.align-center.justify-between
- if @procedure.api_particulier_token.present?
%div
%span.icon.accept

View file

@ -1,6 +1,6 @@
.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
.fr-tile__body.flex.justify-between
.fr-tile__body.flex.column.align-center.justify-between
- if @procedure.attestation_template&.activated?
%div
- if error_messages.present?

View file

@ -1,6 +1,6 @@
.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
.fr-tile__body.flex.justify-between
.fr-tile__body.flex.column.align-center.justify-between
- if error_messages.present?
%div
%span.icon.refuse

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