Merge branch 'demarches-simplifiees:main' into poc-self_hosted_runners

This commit is contained in:
kleph 2023-09-07 18:32:23 +02:00 committed by GitHub
commit 258c7657c8
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
161 changed files with 2661 additions and 1196 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

@ -1 +1 @@
16.14.0
18.17.0

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)
@ -236,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)
@ -254,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)
@ -349,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)
@ -386,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)
@ -414,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)
@ -439,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)
@ -493,7 +491,7 @@ GEM
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)
@ -513,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)
@ -545,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)
@ -555,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)
@ -777,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)
@ -789,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)

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

@ -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;
}
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);
}

View file

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

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

@ -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?
%input.hidden{ type: 'submit', formaction: update_filter_instructeur_procedure_path(procedure), data: { autosubmit_target: 'submitter' } }
%br
= label_tag :value, t('.value'), for: 'value'
.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' } }
= 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

@ -17,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
@ -50,6 +54,10 @@ class Dsfr::InputComponent < ApplicationComponent
get_slot(:label).presence || default_label
end
def dsfr_input_classname
'fr-input'
end
# kind of input helpers
def password?
@input_type == :password_field

View file

@ -8,6 +8,30 @@ module Dsfr
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`
@ -21,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
@ -70,23 +93,10 @@ 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

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,
{ required: @champ.required?, id: @champ.input_id, checked: @champ.true?, aria: { describedby: @champ.describedby_id }, class: class_names('required' => @champ.required?)},
'true',
'false'
.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
= @form.radio_button :value, Individual::GENDER_FEMALE, id: @champ.female_input_id
= Individual.human_attribute_name('gender.female')
%label
= @form.radio_button :value, Individual::GENDER_MALE, id: @champ.male_input_id
= Individual.human_attribute_name('gender.male')
.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')
.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?
.fr-error-text.mb-4= t('.not_found', postal_code: @champ.code_postal)
-# 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: index == 0 ? @champ.input_id : "radio-#{index}-#{value.parameterize}"
= @form.label :value, option, for: index == 0 ? @champ.input_id : "radio-#{index}-#{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,31 @@
- if @champ.options?
- if @champ.render_as_radios?
%fieldset.radios
- @champ.enabled_non_empty_options.each do |option|
%label
= @form.radio_button :value, option
= 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?
Non renseigné
.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?
Autre
.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
- 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
- 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" }
.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?, 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
= t(".yes")
%label{ for: @champ.no_input_id }
%label.fr-label{ for: @champ.yes_input_id }
= t(".yes")
.fr-fieldset__element.fr-fieldset__element--inline
.fr-radio-group
= @form.radio_button :value, false, id: @champ.no_input_id
= t(".no")
%label.fr-label{ for: @champ.no_input_id }
= t(".no")

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 }
- if with_label.present?
= label_for_badge
- if pending_correction?
= t('.remaining_days_after_correction', count: days_count)
- else
= t('.in_days', count: days_count)
%span{ class: classes, title: title }
- if with_label.present?
= label_for_badge
- 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

View file

@ -1,6 +1,6 @@
.fr-col-6.fr-col-md-4.fr-col-lg-3
= link_to edit_admin_procedure_dossier_submitted_message_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.active_dossier_submitted_message.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_mail_templates_path(@procedure), class: 'fr-tile fr-enlarge-link' do
.fr-tile__body.flex.justify-between
.fr-tile__body.flex.column.align-center.justify-between
%div
- if error_messages.present?
%span.icon.refuse

View file

@ -1,6 +1,6 @@
.fr-col-6.fr-col-md-4.fr-col-lg-3
= link_to admin_procedure_experts_path(@procedure), 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.preview
%p.fr-tile-status-todo À configurer

View file

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

View file

@ -1,6 +1,6 @@
.fr-col-6.fr-col-md-4.fr-col-lg-3
= link_to modifications_admin_procedure_path(@procedure), id: 'modifications', 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 Activée

View file

@ -1,6 +1,6 @@
.fr-col-6.fr-col-md-4.fr-col-lg-3
= link_to monavis_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.monavis_embed.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_path(@procedure), id: 'presentation', 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 service_link, class: 'fr-tile fr-enlarge-link', id: 'service' do
.fr-tile__body.flex.justify-between
.fr-tile__body.flex.column.align-center.justify-between
- if @procedure.service_id.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_sva_svr_path(@procedure), class: 'fr-tile fr-enlarge-link', id: 'sva' do
.fr-tile__body.flex.justify-between
.fr-tile__body.flex.column.align-center.justify-between
- if @procedure.sva_svr_enabled?
%div
%span.icon.accept

View file

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

View file

@ -17,6 +17,10 @@ class Procedure::OneGroupeManagementComponent < ApplicationComponent
@groupe_instructeur.routing_rule&.right || empty
end
def operator_name
@groupe_instructeur.routing_rule&.class&.name || empty
end
def targeted_champ_tag
select_tag(
'targeted_champ',
@ -39,6 +43,21 @@ class Procedure::OneGroupeManagementComponent < ApplicationComponent
.map { |tdc| [tdc.libelle, champ_value(tdc.stable_id).to_json] }
end
def operator_tag
select_tag('operator_name',
options_for_select(
options_for_operator_tag,
selected: operator_name
),
class: 'fr-select')
end
def options_for_operator_tag
[Eq, NotEq]
.map(&:name)
.map { |name| [t(name, scope: 'logic.operators'), name] }
end
def value_tag
select_tag(
'value',

View file

@ -40,7 +40,8 @@
.fr-mr-2w.no-wrap si le champ
.target.fr-mr-2w
= targeted_champ_tag
.operator.fr-mr-2w.no-wrap est égal à
.operator.fr-mr-2w.no-wrap
= operator_tag
.value
= value_tag
.fr-hint-text

View file

@ -1,8 +1,9 @@
class TypesDeChampEditor::AddChampButtonComponent < ApplicationComponent
def initialize(revision:, parent: nil, is_annotation: false)
def initialize(revision:, parent: nil, is_annotation: false, after_stable_id: nil)
@revision = revision
@parent = parent
@is_annotation = is_annotation
@after_stable_id = after_stable_id
end
private
@ -25,8 +26,7 @@ class TypesDeChampEditor::AddChampButtonComponent < ApplicationComponent
def button_options
{
class: "button",
form: { class: @parent ? "add-to-block" : "add-to-root" },
class: "fr-btn fr-btn--secondary fr-btn--icon-left fr-icon-add-line",
method: :post,
params: {
type_de_champ: {
@ -34,7 +34,7 @@ class TypesDeChampEditor::AddChampButtonComponent < ApplicationComponent
type_champ: TypeDeChamp.type_champs.fetch(:text),
private: annotations? ? true : nil,
parent_stable_id: @parent&.stable_id,
after_stable_id: ''
after_stable_id: @after_stable_id
}.compact
}
}

View file

@ -30,8 +30,7 @@ class TypesDeChampEditor::ChampComponent < ApplicationComponent
controller: 'type-de-champ-editor',
type_de_champ_editor_move_url_value: move_admin_procedure_type_de_champ_path(procedure, type_de_champ.stable_id),
type_de_champ_editor_move_up_url_value: move_up_admin_procedure_type_de_champ_path(procedure, type_de_champ.stable_id),
type_de_champ_editor_move_down_url_value: move_down_admin_procedure_type_de_champ_path(procedure, type_de_champ.stable_id),
type_de_champ_editor_type_de_champ_stable_id_value: type_de_champ.stable_id
type_de_champ_editor_move_down_url_value: move_down_admin_procedure_type_de_champ_path(procedure, type_de_champ.stable_id)
}
}
end

View file

@ -1,117 +1,123 @@
%li.type-de-champ.flex.column.justify-start{ html_options }
.flex.justify-between.section.head.hr
.handle.small.icon-only.icon.move-handle{ title: "Déplacer le champ vers le haut ou vers le bas" }
.type-de-champ-container
.flex.justify-between.section.head.hr
.handle.small.icon-only.icon.move-handle{ title: "Déplacer le champ vers le haut ou vers le bas" }
- if coordinate.used_by_routing_rules?
.flex.align-center
%span
utilisé pour
= link_to('le routage', admin_procedure_groupe_instructeurs_path(revision.procedure_id, anchor: 'routing-rules'))
- else
.flex.justify-start.delete
= button_to type_de_champ_path, class: 'button small icon-only danger', method: :delete, form: { data: { turbo_confirm: 'Êtes vous sûr de vouloir supprimer ce champ ?' } } do
.icon.delete
%span.sr-only Supprimer
- if coordinate.used_by_routing_rules?
.flex.align-center
%span
utilisé pour
= link_to('le routage', admin_procedure_groupe_instructeurs_path(revision.procedure_id, anchor: 'routing-rules'))
- else
.flex.justify-start.delete
= button_to type_de_champ_path, class: 'fr-btn fr-btn--sm fr-btn--secondary fr-icon-delete-line', title: "Supprimer le champ", method: :delete, form: { data: { turbo_confirm: 'Êtes vous sûr de vouloir supprimer ce champ ?' } } do
.icon.delete
%span.sr-only Supprimer
- if @errors.present?
.types-de-champ-errors
= @errors
- if @errors.present?
.types-de-champ-errors
= @errors
.flex.justify-start.section.ml-1
= form_for(type_de_champ, form_options) do |form|
.flex.justify-start
.flex.justify-start.width-33
.flex.justify-start.column
%button.move-up.cell.mb-1{ move_button_options(:up) }
.icon.arrow-up.small
%span.sr-only Déplacer le champ vers le haut
%button.move-down.cell{ move_button_options(:down) }
.icon.arrow-down.small
%span.sr-only Déplacer le champ vers le bas
.cell.flex.justify-start.column.flex-grow
= form.label :type_champ, "Type de champ", for: dom_id(type_de_champ, :type_champ)
= form.select :type_champ, grouped_options_for_select(types_of_type_de_champ, type_de_champ.type_champ), {}, class: 'small-margin small inline width-100', id: dom_id(type_de_champ, :type_champ), disabled: coordinate.used_by_routing_rules?
.flex.column.justify-start.flex-grow
.cell
.flex.align-center
= form.label :libelle, "Libellé du champ", class: 'flex-grow', for: dom_id(type_de_champ, :libelle)
- if can_be_mandatory?
.cell.flex.align-center
= form.check_box :mandatory, class: 'small-margin small', id: dom_id(type_de_champ, :mandatory)
= form.label :mandatory, "Champ obligatoire", for: dom_id(type_de_champ, :mandatory)
= form.text_field :libelle, class: 'small-margin small width-100', id: dom_id(type_de_champ, :libelle), data: input_autofocus
- if type_de_champ.header_section?
%p
%small Nous numérotons automatiquement les titres lorsquaucun de vos titres ne commence par un chiffre.
.flex.justify-start.section.ml-1
= form_for(type_de_champ, form_options) do |form|
.flex.justify-start
.flex.justify-start.width-33
.flex.justify-start.column
%button.move-up.cell.mb-1{ move_button_options(:up) }
.icon.arrow-up.small
%span.sr-only Déplacer le champ vers le haut
%button.move-down.cell{ move_button_options(:down) }
.icon.arrow-down.small
%span.sr-only Déplacer le champ vers le bas
.cell.flex.justify-start.column.flex-grow
= form.label :type_champ, "Type de champ", for: dom_id(type_de_champ, :type_champ)
= form.select :type_champ, grouped_options_for_select(types_of_type_de_champ, type_de_champ.type_champ), {}, class: 'fr-select small-margin small inline width-100', id: dom_id(type_de_champ, :type_champ), disabled: coordinate.used_by_routing_rules?
- if !type_de_champ.header_section? && !type_de_champ.titre_identite?
.cell.mt-1
= form.label :description, "Description du champ (optionnel)", for: dom_id(type_de_champ, :description)
= form.text_area :description, class: 'small-margin small width-100', rows: 3, id: dom_id(type_de_champ, :description)
- if type_de_champ.header_section?
.cell.mt-1
= render TypesDeChampEditor::HeaderSectionComponent.new(form: form, tdc: type_de_champ, upper_tdcs: @upper_coordinates.map(&:type_de_champ))
.flex.justify-start.mt-1
- if type_de_champ.drop_down_list?
.flex.column.justify-start.width-33
.cell
= form.label :drop_down_list_value, "Options de la liste", for: dom_id(type_de_champ, :drop_down_list_value)
= form.text_area :drop_down_list_value, class: 'small-margin small width-100', rows: 7, id: dom_id(type_de_champ, :drop_down_list_value)
- if type_de_champ.simple_drop_down_list?
.cell
= form.label :drop_down_other, for: dom_id(type_de_champ, :drop_down_other) do
Proposer une option « autre » avec un texte libre
= form.check_box :drop_down_other, class: "small-margin small", id: dom_id(type_de_champ, :drop_down_other)
- if type_de_champ.linked_drop_down_list?
.flex.column.justify-start.flex-grow
.cell
= form.label :drop_down_secondary_libelle, "Libellé du champ secondaire", class: 'flex-grow', for: dom_id(type_de_champ, :drop_down_secondary_libelle)
= form.text_field :drop_down_secondary_libelle, class: 'small-margin small width-100', id: dom_id(type_de_champ, :drop_down_secondary_libelle)
.cell.mt-1
= form.label :drop_down_secondary_description, "Description du champ secondaire (optionnel)", for: dom_id(type_de_champ, :drop_down_secondary_description)
= form.text_area :drop_down_secondary_description, class: 'small-margin small width-100', rows: 3, id: dom_id(type_de_champ, :drop_down_secondary_description)
- if type_de_champ.piece_justificative?
.cell
= form.label :piece_justificative_template, "Modèle", for: dom_id(type_de_champ, :piece_justificative_template)
= render Attachment::EditComponent.new(**piece_justificative_template_options)
.flex.align-center
= form.label :libelle, "Libellé du champ", class: 'flex-grow', for: dom_id(type_de_champ, :libelle)
- if can_be_mandatory?
.cell.flex.align-center
= form.check_box :mandatory, class: 'small-margin small', id: dom_id(type_de_champ, :mandatory)
= form.label :mandatory, "Champ obligatoire", for: dom_id(type_de_champ, :mandatory)
= form.text_field :libelle, class: 'fr-input small-margin small width-100', id: dom_id(type_de_champ, :libelle), data: input_autofocus
- if type_de_champ.header_section?
%p
%small Nous numérotons automatiquement les titres lorsquaucun de vos titres ne commence par un chiffre.
- if type_de_champ.titre_identite?
%p Dans le cadre de la RGPD, le titre didentité sera supprimé lors de lacceptation du dossier
- elsif procedure.piece_justificative_multiple?
%p Les usagers pourront envoyer plusieurs fichiers si nécessaire.
- if type_de_champ.carte?
- type_de_champ.editable_options.each do |slice|
- if !type_de_champ.header_section? && !type_de_champ.titre_identite?
.cell.mt-1
= form.label :description, "Description du champ (optionnel)", for: dom_id(type_de_champ, :description)
= form.text_area :description, class: 'fr-input small-margin small width-100', rows: 3, id: dom_id(type_de_champ, :description)
- if type_de_champ.header_section?
.cell.mt-1
= render TypesDeChampEditor::HeaderSectionComponent.new(form: form, tdc: type_de_champ, upper_tdcs: @upper_coordinates.map(&:type_de_champ))
.flex.justify-start.mt-1
- if type_de_champ.drop_down_list?
.flex.column.justify-start.width-33
.cell
= form.label :drop_down_list_value, "Options de la liste", for: dom_id(type_de_champ, :drop_down_list_value)
= form.text_area :drop_down_list_value, class: 'fr-input small-margin small width-100', rows: 7, id: dom_id(type_de_champ, :drop_down_list_value)
- if type_de_champ.simple_drop_down_list?
.cell
= form.label :drop_down_other, for: dom_id(type_de_champ, :drop_down_other) do
Proposer une option « autre » avec un texte libre
= form.check_box :drop_down_other, class: "small-margin small", id: dom_id(type_de_champ, :drop_down_other)
- if type_de_champ.linked_drop_down_list?
.flex.column.justify-start.flex-grow
.cell
= form.label :drop_down_secondary_libelle, "Libellé du champ secondaire", class: 'flex-grow', for: dom_id(type_de_champ, :drop_down_secondary_libelle)
= form.text_field :drop_down_secondary_libelle, class: 'fr-input small-margin small width-100', id: dom_id(type_de_champ, :drop_down_secondary_libelle)
.cell.mt-1
= form.label :drop_down_secondary_description, "Description du champ secondaire (optionnel)", for: dom_id(type_de_champ, :drop_down_secondary_description)
= form.text_area :drop_down_secondary_description, class: 'fr-input small-margin small width-100', rows: 3, id: dom_id(type_de_champ, :drop_down_secondary_description)
- if type_de_champ.piece_justificative?
.cell
.carte-options
= form.fields_for :editable_options do |form|
- slice.each do |(name, checked)|
= form.label name, for: dom_id(type_de_champ, "layer_#{name}") do
= form.check_box name, checked: checked, class: 'small-margin small', id: dom_id(type_de_champ, "layer_#{name}")
= t(".layers.#{name}")
- if type_de_champ.explication?
.cell.width-66
= form.label :collapsible_explanation_enabled, for: dom_id(type_de_champ, :collapsible_explanation_enabled) do
Afficher un texte complementaire affichable au clic
= form.check_box :collapsible_explanation_enabled, class: "small-margin small", id: dom_id(type_de_champ, :collapsible_explanation_enabled)
- if form.object.collapsible_explanation_enabled?
= form.label :collapsible_explanation_text, for: dom_id(type_de_champ, :collapsible_explanation_text) do
= "Texte à afficher quand l'utiliser a choisi de l'afficher"
= form.text_area :collapsible_explanation_text, class: "small-margin small", id: dom_id(type_de_champ, :collapsible_explanation_text)
- if type_de_champ.textarea?
.cell
= form.label :character_limit, for: dom_id(type_de_champ, :character_limit) do
Spécifier un nombre maximal conseillé de caractères :
= form.select :character_limit, options_for_character_limit, id: dom_id(type_de_champ, :character_limit)
= form.label :piece_justificative_template, "Modèle", for: dom_id(type_de_champ, :piece_justificative_template)
= render Attachment::EditComponent.new(**piece_justificative_template_options)
- if type_de_champ.block?
.flex.justify-start.section.ml-1
.editor-block.flex-grow.cell
= render TypesDeChampEditor::BlockComponent.new(block: coordinate, coordinates: coordinate.revision_types_de_champ, upper_coordinates: @upper_coordinates)
= render TypesDeChampEditor::AddChampButtonComponent.new(revision: coordinate.revision, parent: coordinate, is_annotation: coordinate.private?)
- if type_de_champ.titre_identite?
%p Dans le cadre de la RGPD, le titre didentité sera supprimé lors de lacceptation du dossier
- elsif procedure.piece_justificative_multiple?
%p Les usagers pourront envoyer plusieurs fichiers si nécessaire.
- if type_de_champ.carte?
- type_de_champ.editable_options.each do |slice|
.cell
.carte-options
= form.fields_for :editable_options do |form|
- slice.each do |(name, checked)|
= form.label name, for: dom_id(type_de_champ, "layer_#{name}") do
= form.check_box name, checked: checked, class: 'small-margin small', id: dom_id(type_de_champ, "layer_#{name}")
= t(".layers.#{name}")
- if type_de_champ.explication?
.cell.width-66
= form.label :collapsible_explanation_enabled, for: dom_id(type_de_champ, :collapsible_explanation_enabled) do
Afficher un texte complementaire affichable au clic
= form.check_box :collapsible_explanation_enabled, class: "small-margin small", id: dom_id(type_de_champ, :collapsible_explanation_enabled)
- if form.object.collapsible_explanation_enabled?
= form.label :collapsible_explanation_text, for: dom_id(type_de_champ, :collapsible_explanation_text) do
= "Texte à afficher quand l'utiliser a choisi de l'afficher"
= form.text_area :collapsible_explanation_text, class: "fr-input small-margin small", id: dom_id(type_de_champ, :collapsible_explanation_text)
- if type_de_champ.textarea?
.cell
= form.label :character_limit, for: dom_id(type_de_champ, :character_limit) do
Spécifier un nombre maximal conseillé de caractères :
= form.select :character_limit, options_for_character_limit, id: dom_id(type_de_champ, :character_limit), class: 'fr-select'
- if conditional_enabled?
= render(TypesDeChampEditor::ConditionsComponent.new(tdc: type_de_champ, upper_tdcs: @upper_coordinates.map(&:type_de_champ), procedure_id: procedure.id))
- if type_de_champ.block?
.flex.justify-start.section.ml-1
.editor-block.flex-grow.cell
= render TypesDeChampEditor::BlockComponent.new(block: coordinate, coordinates: coordinate.revision_types_de_champ, upper_coordinates: @upper_coordinates)
.type-de-champ-add-button{ id: dom_id(coordinate, :type_de_champ_add_button), class: class_names(hidden: !coordinate.empty?) }
= render TypesDeChampEditor::AddChampButtonComponent.new(revision: coordinate.revision, parent: coordinate, is_annotation: coordinate.private?)
- if conditional_enabled?
= render(TypesDeChampEditor::ConditionsComponent.new(tdc: type_de_champ, upper_tdcs: @upper_coordinates.map(&:type_de_champ), procedure_id: procedure.id))
.type-de-champ-add-button{ class: class_names(root: !coordinate.child?) }
= render TypesDeChampEditor::AddChampButtonComponent.new(revision: coordinate.revision, parent: coordinate&.parent, is_annotation: coordinate.private?, after_stable_id: type_de_champ.stable_id)

View file

@ -9,7 +9,13 @@ module Administrateurs
right = targeted_champ_changed? ? empty : value
groupe_instructeur.update!(routing_rule: ds_eq(left, right))
new_routing_rule = case operator_name
when Eq.name
ds_eq(left, right)
when NotEq.name
ds_not_eq(left, right)
end
groupe_instructeur.update!(routing_rule: new_routing_rule)
end
def update_defaut_groupe_instructeur
@ -27,6 +33,10 @@ module Administrateurs
Logic.from_json(params[:targeted_champ])
end
def operator_name
params[:operator_name]
end
def value
Logic.from_json(params[:value])
end

View file

@ -9,7 +9,7 @@ class API::Public::V1::DossiersController < API::Public::V1::BaseController
)
dossier.build_default_individual
if dossier.save
dossier.prefill!(PrefillParams.new(dossier, params.to_unsafe_h).to_a)
dossier.prefill!(PrefillChamps.new(dossier, params.to_unsafe_h).to_a, PrefillIdentity.new(dossier, params.to_unsafe_h).to_h)
render json: serialize_dossier(dossier), status: :created
else
render_bad_request(dossier.errors.full_messages.to_sentence)

View file

@ -25,6 +25,7 @@ class ApplicationController < ActionController::Base
before_action do
Current.request_id = request.uuid
Current.user = current_user
end
def staging_authenticate

View file

@ -25,6 +25,6 @@ class PrefillDescriptionsController < ApplicationController
end
def prefill_description_params
params.require(:type_de_champ).permit(:selected_type_de_champ_ids)
params.require(:procedure).permit(:selected_type_de_champ_ids, :identity_items_selected)
end
end

View file

@ -78,11 +78,11 @@ module Users
end
def commencer_page_is_reloaded?
session[:prefill_token].present? && session[:prefill_params_digest] == PrefillParams.digest(params)
session[:prefill_token].present? && session[:prefill_params_digest] == PrefillChamps.digest(params)
end
def prefill_params_present?
params.keys.find { |param| param.split('_').first == "champ" }
params.keys.find { ['champ', 'identite'].include?(_1.split('_').first) }
end
def retrieve_procedure
@ -101,10 +101,10 @@ module Users
)
@prefilled_dossier.build_default_individual
if @prefilled_dossier.save
@prefilled_dossier.prefill!(PrefillParams.new(@prefilled_dossier, params.to_unsafe_h).to_a)
@prefilled_dossier.prefill!(PrefillChamps.new(@prefilled_dossier, params.to_unsafe_h).to_a, PrefillIdentity.new(@prefilled_dossier, params.to_unsafe_h).to_h)
end
session[:prefill_token] = @prefilled_dossier.prefill_token
session[:prefill_params_digest] = PrefillParams.digest(params)
session[:prefill_params_digest] = PrefillChamps.digest(params)
end
def retrieve_prefilled_dossier(prefill_token)

View file

@ -14,6 +14,8 @@ class ProcedureDashboard < Administrate::BaseDashboard
aasm_state: ProcedureStateField,
dossiers: Field::HasMany,
administrateurs: Field::HasMany,
instructeurs: Field::HasMany,
groupe_instructeurs: Field::HasMany,
id: Field::Number.with_options(searchable: true),
libelle: Field::String,
description: Field::String,
@ -68,6 +70,8 @@ class ProcedureDashboard < Administrate::BaseDashboard
:path,
:aasm_state,
:administrateurs,
:instructeurs,
:groupe_instructeurs,
:libelle,
:description,
:tags,

View file

@ -13,7 +13,7 @@ import type { Style } from 'maplibre-gl';
import invariant from 'tiny-invariant';
import { useStyle } from './hooks';
import { useStyle, useElementVisible } from './hooks';
import { StyleControl } from './StyleControl';
const Context = createContext<{ map?: Map | null }>({});
@ -35,6 +35,7 @@ export function MapLibre({ children, layers }: MapLibreProps) {
[]
);
const containerRef = useRef<HTMLDivElement>(null);
const visible = useElementVisible(containerRef);
const [map, setMap] = useState<Map | null>();
const onStyleChange = useCallback(
@ -48,7 +49,7 @@ export function MapLibre({ children, layers }: MapLibreProps) {
const { style, ...mapStyleProps } = useStyle(layers, onStyleChange);
useEffect(() => {
if (isSupported && !map) {
if (isSupported && visible && !map) {
invariant(containerRef.current, 'Map container not found');
const map = new Map({
container: containerRef.current,
@ -59,7 +60,7 @@ export function MapLibre({ children, layers }: MapLibreProps) {
setMap(map);
});
}
}, [map, style, isSupported]);
}, [map, style, visible, isSupported]);
if (!isSupported) {
return (

View file

@ -1,4 +1,10 @@
import { useCallback, useEffect, useState, useMemo } from 'react';
import {
useCallback,
useEffect,
useState,
useMemo,
type RefObject
} from 'react';
import type {
LngLatBoundsLike,
LngLat,
@ -118,3 +124,30 @@ export function useStyle(
return { style, layers, setStyle, setLayerEnabled, setLayerOpacity };
}
function isElementVisible(
element: HTMLElement,
callback: (visible: boolean) => void
) {
if (element.offsetWidth > 0 && element.offsetHeight > 0) {
callback(true);
} else {
callback(false);
const observer = new IntersectionObserver(
(entries) => callback(entries[0].isIntersecting == true),
{ threshold: [0] }
);
observer.observe(element);
return () => observer.unobserve(element);
}
}
export function useElementVisible(element: RefObject<HTMLElement>) {
const [visible, setVisible] = useState(false);
useEffect(() => {
if (element.current) {
return isElementVisible(element.current, setVisible);
}
}, [element]);
return visible;
}

View file

@ -124,12 +124,12 @@ export class AutosaveController extends ApplicationController {
private showSpinner(champElement: Element) {
this.#spinnerTimeoutId = setTimeout(() => {
// do not do anything if there is already a spinner for this champ, like SIRET champ
if (!champElement.querySelector('.spinner')) {
if (!champElement.nextElementSibling?.classList.contains('spinner')) {
const spinner = document.createElement('div');
spinner.classList.add('spinner', 'spinner-removable');
spinner.setAttribute('aria-live', 'live');
spinner.setAttribute('aria-label', 'Chargement en cours…');
champElement.appendChild(spinner);
champElement.insertAdjacentElement('afterend', spinner);
}
}, AUTOSAVE_CONDITIONAL_SPINNER_DEBOUNCE_DELAY);
}

View file

@ -7,7 +7,6 @@ import {
isTextInputElement,
getConfig
} from '@utils';
import { useIntersection } from 'stimulus-use';
import { AutoUpload } from '../shared/activestorage/auto-upload';
import { ApplicationController } from './application_controller';
@ -28,7 +27,6 @@ export class TypeDeChampEditorController extends ApplicationController {
declare readonly moveUrlValue: string;
declare readonly moveUpUrlValue: string;
declare readonly moveDownUrlValue: string;
declare readonly typeDeChampStableIdValue: string;
declare readonly isVisible: boolean;
#latestPromise = Promise.resolve();
@ -36,8 +34,6 @@ export class TypeDeChampEditorController extends ApplicationController {
#inFlightForms: Map<HTMLFormElement, AbortController> = new Map();
connect() {
useIntersection(this, { threshold: 0.6 });
this.#latestPromise = Promise.resolve();
this.on('change', (event) => this.onChange(event));
this.on('input', (event) => this.onInput(event));
@ -62,10 +58,6 @@ export class TypeDeChampEditorController extends ApplicationController {
this.requestSubmitForm(form);
}
appear() {
this.updateAfterId();
}
private onChange(event: Event) {
const target = event.target as HTMLElement & { form?: HTMLFormElement };
@ -144,28 +136,8 @@ export class TypeDeChampEditorController extends ApplicationController {
this.#inFlightForms.set(form, controller);
return controller;
}
private updateAfterId() {
const parent = this.element.closest<HTMLElement>(
'.editor-block, .editor-root'
);
if (parent) {
const selector = parent.classList.contains('editor-block')
? '.add-to-block'
: '.add-to-root';
const input = parent.querySelector<HTMLInputElement>(
`${selector} ${AFTER_STABLE_ID_INPUT_SELECTOR}`
);
if (input) {
input.value = this.typeDeChampStableIdValue;
}
}
}
}
const AFTER_STABLE_ID_INPUT_SELECTOR =
'input[name="type_de_champ[after_stable_id]"]';
function createForm(action: string, method: string) {
const form = document.createElement('form');
form.action = action;

View file

@ -1,5 +1,5 @@
import { createGraphiQLFetcher } from '@graphiql/toolkit';
import { useExplorerPlugin } from '@graphiql/plugin-explorer';
import { explorerPlugin } from '@graphiql/plugin-explorer';
import { GraphiQL } from 'graphiql';
import { createElement, useState } from 'react';
import { createRoot } from 'react-dom/client';
@ -17,15 +17,11 @@ const fetcher = createGraphiQLFetcher({
function GraphiQLWithExplorer() {
const [query, setQuery] = useState(defaultQuery);
const explorerPlugin = useExplorerPlugin({
query: query ?? '',
onEdit: setQuery,
showAttribution: false
});
const explorer = explorerPlugin({ showAttribution: false });
return createElement(GraphiQL, {
fetcher: fetcher,
defaultEditorToolsVisibility: true,
plugins: [explorerPlugin],
plugins: [explorer],
query: query,
variables: defaultVariables,
onEditQuery: setQuery,

View file

@ -1,3 +1,5 @@
require 'fog/openstack'
class PjsMigrationJob < ApplicationJob
queue_as :pj_migration_jobs
@ -7,7 +9,6 @@ class PjsMigrationJob < ApplicationJob
return if already_moved?(blob)
service = blob.service
client = service.client
container = service.container
old_key = blob.key
new_key = "#{blob.created_at.strftime('%Y/%m/%d')}/#{old_key[0..1]}/#{old_key}"
@ -28,4 +29,14 @@ class PjsMigrationJob < ApplicationJob
def already_moved?(blob)
blob.key.include?('/')
end
private
def client
@client ||= begin
credentials = Rails.application.config.active_storage
.service_configurations['openstack']['credentials']
Fog::OpenStack::Storage.new(credentials)
end
end
end

View file

@ -1,3 +1,5 @@
require 'fog/openstack'
class ActiveStorage::DownloadableFile
def self.create_list_from_dossiers(
dossiers,
@ -20,7 +22,6 @@ class ActiveStorage::DownloadableFile
true
else
service = file.blob.service
client = service.client
begin
client.head_object(service.container, file.blob.key)
true
@ -33,6 +34,13 @@ class ActiveStorage::DownloadableFile
private
def self.client
credentials = Rails.application.config.active_storage
.service_configurations['openstack']['credentials']
Fog::OpenStack::Storage.new(credentials)
end
def self.bill_and_path(bill)
[
bill,

View file

@ -160,6 +160,14 @@ class Champ < ApplicationRecord
true
end
def legend_label?
false
end
def single_checkbox?
false
end
def input_group_id
"champ-#{html_id}"
end

View file

@ -7,11 +7,23 @@ class Champs::CheckboxChamp < Champs::BooleanChamp
mandatory? && (blank? || !true?)
end
def legend_label?
false
end
# TODO remove when normalize_checkbox_values is over
def true?
value_with_legacy == TRUE_VALUE
end
def html_label?
false
end
def single_checkbox?
true
end
private
# TODO remove when normalize_checkbox_values is over

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