commit
8aa8c4a9de
87 changed files with 1724 additions and 569 deletions
|
@ -23,6 +23,7 @@ module.exports = {
|
|||
rules: {
|
||||
'prettier/prettier': 'error',
|
||||
'react-hooks/rules-of-hooks': 'error',
|
||||
'react-hooks/exhaustive-deps': 'error',
|
||||
'react/prop-types': 'off'
|
||||
},
|
||||
settings: {
|
||||
|
@ -51,7 +52,13 @@ module.exports = {
|
|||
'plugin:@typescript-eslint/recommended',
|
||||
'plugin:react-hooks/recommended',
|
||||
'prettier'
|
||||
]
|
||||
],
|
||||
rules: {
|
||||
'prettier/prettier': 'error',
|
||||
'react-hooks/rules-of-hooks': 'error',
|
||||
'react-hooks/exhaustive-deps': 'error',
|
||||
'@typescript-eslint/no-explicit-any': 'error'
|
||||
}
|
||||
}
|
||||
]
|
||||
};
|
||||
|
|
5
.github/actions/ci-setup-rails/action.yml
vendored
5
.github/actions/ci-setup-rails/action.yml
vendored
|
@ -12,11 +12,12 @@ runs:
|
|||
- name: Setup Node
|
||||
uses: actions/setup-node@v2
|
||||
with:
|
||||
node-version: '14'
|
||||
cache: 'yarn'
|
||||
|
||||
- name: Install Node modules
|
||||
run: yarn install --frozen-lockfile
|
||||
run: |
|
||||
node --version
|
||||
yarn install --frozen-lockfile
|
||||
shell: bash
|
||||
|
||||
- name: Setup environment variables
|
||||
|
|
1
.node-version
Normal file
1
.node-version
Normal file
|
@ -0,0 +1 @@
|
|||
16.14.0
|
4
Gemfile
4
Gemfile
|
@ -83,7 +83,7 @@ gem 'spreadsheet_architect'
|
|||
gem 'typhoeus'
|
||||
gem 'warden'
|
||||
gem 'webpacker'
|
||||
gem 'zipline', github: 'fringd/zipline', ref: 'd637bbff2' # Unreleased 1.3.0, with a fix for Ruby 3.0 kwargs
|
||||
gem 'zipline'
|
||||
gem 'zxcvbn-ruby', require: 'zxcvbn'
|
||||
|
||||
group :test do
|
||||
|
@ -120,7 +120,7 @@ end
|
|||
|
||||
group :development, :test do
|
||||
gem 'graphql-schema_comparator'
|
||||
gem 'mina', git: 'https://github.com/mina-deploy/mina.git', require: false # Deploy
|
||||
gem 'mina', require: false # Deploy
|
||||
gem 'pry-byebug' # Call 'byebug' anywhere in the code to stop execution and get a debugger console
|
||||
gem 'rspec-rails'
|
||||
gem 'simple_xlsx_reader'
|
||||
|
|
147
Gemfile.lock
147
Gemfile.lock
|
@ -1,60 +1,43 @@
|
|||
GIT
|
||||
remote: https://github.com/fringd/zipline.git
|
||||
revision: d637bbff262f59718d23a65f50b50163b8ba749f
|
||||
ref: d637bbff2
|
||||
specs:
|
||||
zipline (1.3.0)
|
||||
actionpack (>= 3.2.1, < 7.0)
|
||||
zip_tricks (>= 4.2.1, < 6.0)
|
||||
|
||||
GIT
|
||||
remote: https://github.com/mina-deploy/mina.git
|
||||
revision: 84fa84c7f7f94f9518ef9b7099396ab6676b5881
|
||||
specs:
|
||||
mina (1.2.3)
|
||||
open4 (~> 1.3.4)
|
||||
rake
|
||||
|
||||
GEM
|
||||
remote: https://rubygems.org/
|
||||
specs:
|
||||
aasm (5.2.0)
|
||||
concurrent-ruby (~> 1.0)
|
||||
acsv (0.0.1)
|
||||
actioncable (6.1.4.4)
|
||||
actionpack (= 6.1.4.4)
|
||||
activesupport (= 6.1.4.4)
|
||||
actioncable (6.1.4.6)
|
||||
actionpack (= 6.1.4.6)
|
||||
activesupport (= 6.1.4.6)
|
||||
nio4r (~> 2.0)
|
||||
websocket-driver (>= 0.6.1)
|
||||
actionmailbox (6.1.4.4)
|
||||
actionpack (= 6.1.4.4)
|
||||
activejob (= 6.1.4.4)
|
||||
activerecord (= 6.1.4.4)
|
||||
activestorage (= 6.1.4.4)
|
||||
activesupport (= 6.1.4.4)
|
||||
actionmailbox (6.1.4.6)
|
||||
actionpack (= 6.1.4.6)
|
||||
activejob (= 6.1.4.6)
|
||||
activerecord (= 6.1.4.6)
|
||||
activestorage (= 6.1.4.6)
|
||||
activesupport (= 6.1.4.6)
|
||||
mail (>= 2.7.1)
|
||||
actionmailer (6.1.4.4)
|
||||
actionpack (= 6.1.4.4)
|
||||
actionview (= 6.1.4.4)
|
||||
activejob (= 6.1.4.4)
|
||||
activesupport (= 6.1.4.4)
|
||||
actionmailer (6.1.4.6)
|
||||
actionpack (= 6.1.4.6)
|
||||
actionview (= 6.1.4.6)
|
||||
activejob (= 6.1.4.6)
|
||||
activesupport (= 6.1.4.6)
|
||||
mail (~> 2.5, >= 2.5.4)
|
||||
rails-dom-testing (~> 2.0)
|
||||
actionpack (6.1.4.4)
|
||||
actionview (= 6.1.4.4)
|
||||
activesupport (= 6.1.4.4)
|
||||
actionpack (6.1.4.6)
|
||||
actionview (= 6.1.4.6)
|
||||
activesupport (= 6.1.4.6)
|
||||
rack (~> 2.0, >= 2.0.9)
|
||||
rack-test (>= 0.6.3)
|
||||
rails-dom-testing (~> 2.0)
|
||||
rails-html-sanitizer (~> 1.0, >= 1.2.0)
|
||||
actiontext (6.1.4.4)
|
||||
actionpack (= 6.1.4.4)
|
||||
activerecord (= 6.1.4.4)
|
||||
activestorage (= 6.1.4.4)
|
||||
activesupport (= 6.1.4.4)
|
||||
actiontext (6.1.4.6)
|
||||
actionpack (= 6.1.4.6)
|
||||
activerecord (= 6.1.4.6)
|
||||
activestorage (= 6.1.4.6)
|
||||
activesupport (= 6.1.4.6)
|
||||
nokogiri (>= 1.8.5)
|
||||
actionview (6.1.4.4)
|
||||
activesupport (= 6.1.4.4)
|
||||
actionview (6.1.4.6)
|
||||
activesupport (= 6.1.4.6)
|
||||
builder (~> 3.1)
|
||||
erubi (~> 1.4)
|
||||
rails-dom-testing (~> 2.0)
|
||||
|
@ -72,26 +55,26 @@ GEM
|
|||
activemodel (>= 5.2.0)
|
||||
activestorage (>= 5.2.0)
|
||||
activesupport (>= 5.2.0)
|
||||
activejob (6.1.4.4)
|
||||
activesupport (= 6.1.4.4)
|
||||
activejob (6.1.4.6)
|
||||
activesupport (= 6.1.4.6)
|
||||
globalid (>= 0.3.6)
|
||||
activemodel (6.1.4.4)
|
||||
activesupport (= 6.1.4.4)
|
||||
activerecord (6.1.4.4)
|
||||
activemodel (= 6.1.4.4)
|
||||
activesupport (= 6.1.4.4)
|
||||
activestorage (6.1.4.4)
|
||||
actionpack (= 6.1.4.4)
|
||||
activejob (= 6.1.4.4)
|
||||
activerecord (= 6.1.4.4)
|
||||
activesupport (= 6.1.4.4)
|
||||
activemodel (6.1.4.6)
|
||||
activesupport (= 6.1.4.6)
|
||||
activerecord (6.1.4.6)
|
||||
activemodel (= 6.1.4.6)
|
||||
activesupport (= 6.1.4.6)
|
||||
activestorage (6.1.4.6)
|
||||
actionpack (= 6.1.4.6)
|
||||
activejob (= 6.1.4.6)
|
||||
activerecord (= 6.1.4.6)
|
||||
activesupport (= 6.1.4.6)
|
||||
marcel (~> 1.0.0)
|
||||
mini_mime (>= 1.1.0)
|
||||
activestorage-openstack (1.5.1)
|
||||
fog-openstack (~> 1.0)
|
||||
marcel
|
||||
rails (>= 5.2.2)
|
||||
activesupport (6.1.4.4)
|
||||
activesupport (6.1.4.6)
|
||||
concurrent-ruby (~> 1.0, >= 1.0.2)
|
||||
i18n (>= 1.6, < 2)
|
||||
minitest (>= 5.1)
|
||||
|
@ -183,6 +166,7 @@ GEM
|
|||
descendants_tracker (~> 0.0.1)
|
||||
concurrent-ruby (1.1.9)
|
||||
connection_pool (2.2.3)
|
||||
content_disposition (1.0.0)
|
||||
crack (0.4.5)
|
||||
rexml
|
||||
crass (1.0.6)
|
||||
|
@ -351,7 +335,7 @@ GEM
|
|||
domain_name (~> 0.5)
|
||||
http_accept_language (2.1.1)
|
||||
httpclient (2.8.3)
|
||||
i18n (1.8.11)
|
||||
i18n (1.10.0)
|
||||
concurrent-ruby (~> 1.0)
|
||||
i18n-tasks (0.9.33)
|
||||
activesupport (>= 4.0.2)
|
||||
|
@ -417,7 +401,7 @@ GEM
|
|||
railties (>= 4)
|
||||
request_store (~> 1.0)
|
||||
logstash-event (1.2.02)
|
||||
loofah (2.13.0)
|
||||
loofah (2.14.0)
|
||||
crass (~> 1.0.2)
|
||||
nokogiri (>= 1.5.9)
|
||||
mail (2.7.1)
|
||||
|
@ -431,9 +415,12 @@ GEM
|
|||
mime-types (3.3.1)
|
||||
mime-types-data (~> 3.2015)
|
||||
mime-types-data (3.2021.0212)
|
||||
mina (1.2.4)
|
||||
open4 (~> 1.3.4)
|
||||
rake
|
||||
mini_magick (4.11.0)
|
||||
mini_mime (1.1.2)
|
||||
mini_portile2 (2.6.1)
|
||||
mini_portile2 (2.7.1)
|
||||
minitest (5.15.0)
|
||||
momentjs-rails (2.20.1)
|
||||
railties (>= 3.1)
|
||||
|
@ -444,8 +431,8 @@ GEM
|
|||
ruby2_keywords (~> 0.0.1)
|
||||
netrc (0.11.0)
|
||||
nio4r (2.5.8)
|
||||
nokogiri (1.12.5)
|
||||
mini_portile2 (~> 2.6.1)
|
||||
nokogiri (1.13.1)
|
||||
mini_portile2 (~> 2.7.0)
|
||||
racc (~> 1.4)
|
||||
open4 (1.3.4)
|
||||
openid_connect (1.3.0)
|
||||
|
@ -515,20 +502,20 @@ GEM
|
|||
rack
|
||||
rack-test (1.1.0)
|
||||
rack (>= 1.0, < 3)
|
||||
rails (6.1.4.4)
|
||||
actioncable (= 6.1.4.4)
|
||||
actionmailbox (= 6.1.4.4)
|
||||
actionmailer (= 6.1.4.4)
|
||||
actionpack (= 6.1.4.4)
|
||||
actiontext (= 6.1.4.4)
|
||||
actionview (= 6.1.4.4)
|
||||
activejob (= 6.1.4.4)
|
||||
activemodel (= 6.1.4.4)
|
||||
activerecord (= 6.1.4.4)
|
||||
activestorage (= 6.1.4.4)
|
||||
activesupport (= 6.1.4.4)
|
||||
rails (6.1.4.6)
|
||||
actioncable (= 6.1.4.6)
|
||||
actionmailbox (= 6.1.4.6)
|
||||
actionmailer (= 6.1.4.6)
|
||||
actionpack (= 6.1.4.6)
|
||||
actiontext (= 6.1.4.6)
|
||||
actionview (= 6.1.4.6)
|
||||
activejob (= 6.1.4.6)
|
||||
activemodel (= 6.1.4.6)
|
||||
activerecord (= 6.1.4.6)
|
||||
activestorage (= 6.1.4.6)
|
||||
activesupport (= 6.1.4.6)
|
||||
bundler (>= 1.15.0)
|
||||
railties (= 6.1.4.4)
|
||||
railties (= 6.1.4.6)
|
||||
sprockets-rails (>= 2.0.0)
|
||||
rails-controller-testing (1.0.5)
|
||||
actionpack (>= 5.0.1.rc1)
|
||||
|
@ -547,9 +534,9 @@ GEM
|
|||
rails-i18n (6.0.0)
|
||||
i18n (>= 0.7, < 2)
|
||||
railties (>= 6.0.0, < 7)
|
||||
railties (6.1.4.4)
|
||||
actionpack (= 6.1.4.4)
|
||||
activesupport (= 6.1.4.4)
|
||||
railties (6.1.4.6)
|
||||
actionpack (= 6.1.4.6)
|
||||
activesupport (= 6.1.4.6)
|
||||
method_source
|
||||
rake (>= 0.13)
|
||||
thor (~> 1.0)
|
||||
|
@ -768,8 +755,12 @@ GEM
|
|||
websocket-extensions (0.1.5)
|
||||
xpath (3.2.0)
|
||||
nokogiri (~> 1.8)
|
||||
zeitwerk (2.5.3)
|
||||
zeitwerk (2.5.4)
|
||||
zip_tricks (5.6.0)
|
||||
zipline (1.4.1)
|
||||
actionpack (>= 6.0, < 8.0)
|
||||
content_disposition (~> 1.0)
|
||||
zip_tricks (>= 4.2.1, < 6.0)
|
||||
zxcvbn-ruby (1.2.0)
|
||||
|
||||
PLATFORMS
|
||||
|
@ -842,7 +833,7 @@ DEPENDENCIES
|
|||
lograge
|
||||
logstash-event
|
||||
mailjet
|
||||
mina!
|
||||
mina
|
||||
openid_connect
|
||||
pg
|
||||
phonelib
|
||||
|
@ -892,7 +883,7 @@ DEPENDENCIES
|
|||
webdrivers (~> 4.0)
|
||||
webmock
|
||||
webpacker
|
||||
zipline!
|
||||
zipline
|
||||
zxcvbn-ruby
|
||||
|
||||
BUNDLED WITH
|
||||
|
|
File diff suppressed because one or more lines are too long
Before Width: | Height: | Size: 8.6 KiB After Width: | Height: | Size: 12 KiB |
File diff suppressed because one or more lines are too long
Before Width: | Height: | Size: 8 KiB After Width: | Height: | Size: 12 KiB |
27
app/assets/stylesheets/agentconnect.scss
Normal file
27
app/assets/stylesheets/agentconnect.scss
Normal file
|
@ -0,0 +1,27 @@
|
|||
@import "colors";
|
||||
@import "constants";
|
||||
|
||||
#agentconnect {
|
||||
.agent {
|
||||
color: $blue-france-500;
|
||||
text-align: center;
|
||||
font-size: 28px;
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
.box {
|
||||
background-color: #F2F2F9;
|
||||
padding: $default-padding;
|
||||
|
||||
ul {
|
||||
list-style: disc;
|
||||
padding-left: inherit;
|
||||
}
|
||||
}
|
||||
|
||||
.citizen {
|
||||
font-size: 16px;
|
||||
color: $blue-france-500;
|
||||
font-weight: bold;
|
||||
}
|
||||
}
|
|
@ -3,17 +3,23 @@
|
|||
@import "placeholders";
|
||||
@import "mixins";
|
||||
|
||||
#auth {
|
||||
#auth,
|
||||
#agentconnect {
|
||||
// On small screens, hide the procedure description text on auth pages.
|
||||
// It avoids pushing the sign-in/sign-up form out of the viewport.
|
||||
//
|
||||
// The procedure description can still be read from the /commencer
|
||||
// pages.
|
||||
@media (max-width: $two-columns-breakpoint) {
|
||||
.procedure-preview {
|
||||
.procedure-preview,
|
||||
.agent-intro {
|
||||
display: none;
|
||||
}
|
||||
}
|
||||
|
||||
.column {
|
||||
padding-top: 2 * $default-spacer;
|
||||
}
|
||||
}
|
||||
|
||||
.auth-form {
|
||||
|
@ -53,6 +59,10 @@
|
|||
}
|
||||
|
||||
.sign-in-form .form {
|
||||
input[type="email"] {
|
||||
margin-bottom: $default-padding;
|
||||
}
|
||||
|
||||
input[type="password"] {
|
||||
margin-bottom: $default-spacer;
|
||||
}
|
||||
|
@ -61,3 +71,10 @@
|
|||
margin-bottom: 0;
|
||||
}
|
||||
}
|
||||
|
||||
#session-new {
|
||||
.important-header {
|
||||
font-weight: bold;
|
||||
font-size: 18px;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -15,8 +15,8 @@
|
|||
|
||||
.france-connect-login-button {
|
||||
display: inline-block;
|
||||
height: 52px;
|
||||
width: 186px;
|
||||
height: 60px;
|
||||
width: 230px;
|
||||
margin: auto;
|
||||
margin-bottom: 8px;
|
||||
background-image: image-url("login-with-fc.svg"), image-url("login-with-fc-hover.svg");
|
||||
|
|
|
@ -1,42 +0,0 @@
|
|||
@import "constants";
|
||||
|
||||
.m-0 {
|
||||
margin: 0px !important;
|
||||
}
|
||||
|
||||
.mb-1 {
|
||||
margin-bottom: $default-spacer;
|
||||
}
|
||||
|
||||
.mr-1 {
|
||||
margin-right: $default-spacer !important;
|
||||
}
|
||||
|
||||
.mb-4 {
|
||||
margin-bottom: 4 * $default-spacer !important;
|
||||
}
|
||||
|
||||
.ml-1 {
|
||||
margin-left: $default-spacer;
|
||||
}
|
||||
|
||||
.pl-0 {
|
||||
padding-left: 0px !important;
|
||||
}
|
||||
|
||||
.p-0 {
|
||||
padding: 0px !important;
|
||||
}
|
||||
|
||||
.bold {
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
.numbers-delimiter {
|
||||
display: inline-block;
|
||||
width: 5px;
|
||||
}
|
||||
|
||||
.text-center {
|
||||
text-align: center;
|
||||
}
|
|
@ -2,38 +2,8 @@
|
|||
@import "common";
|
||||
@import "constants";
|
||||
|
||||
.merci {
|
||||
text-align: center;
|
||||
margin-bottom: 60px;
|
||||
|
||||
img {
|
||||
margin-top: 4 * $default-padding;
|
||||
}
|
||||
|
||||
h1 {
|
||||
margin: (2 * $default-padding) 0;
|
||||
}
|
||||
|
||||
b {
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
.send {
|
||||
margin-bottom: 2 * $default-padding;
|
||||
font-size: 20px;
|
||||
}
|
||||
|
||||
p {
|
||||
margin: $default-padding;
|
||||
}
|
||||
|
||||
a {
|
||||
margin-top: 40px;
|
||||
}
|
||||
|
||||
.monavis {
|
||||
.merci .monavis {
|
||||
img {
|
||||
margin-top: 2 * $default-padding;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -22,6 +22,12 @@ $procedure-description-line-height: 22px;
|
|||
font-weight: bold;
|
||||
}
|
||||
|
||||
.small-simple {
|
||||
font-size: 16px;
|
||||
color: $blue-france-500;
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
.close-procedure {
|
||||
font-size: 12px;
|
||||
}
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
@import "colors";
|
||||
@import "constants";
|
||||
|
||||
// floats
|
||||
.pull-left {
|
||||
float: left;
|
||||
}
|
||||
|
@ -13,6 +14,9 @@
|
|||
clear: both;
|
||||
}
|
||||
|
||||
|
||||
// text
|
||||
.text-center,
|
||||
.center {
|
||||
text-align: center;
|
||||
}
|
||||
|
@ -21,12 +25,21 @@
|
|||
text-align: right;
|
||||
}
|
||||
|
||||
.hidden {
|
||||
display: none;
|
||||
.text-sm {
|
||||
font-size: 14px;
|
||||
}
|
||||
|
||||
.width-100 {
|
||||
width: 100%;
|
||||
.text-lg {
|
||||
font-size: 18px;
|
||||
}
|
||||
|
||||
.bold {
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
.numbers-delimiter {
|
||||
display: inline-block;
|
||||
width: 5px;
|
||||
}
|
||||
|
||||
.empty-text {
|
||||
|
@ -45,95 +58,42 @@
|
|||
}
|
||||
}
|
||||
|
||||
// display
|
||||
.hidden {
|
||||
display: none;
|
||||
}
|
||||
|
||||
// sizing
|
||||
.width-100 {
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
// who known
|
||||
.highlighted {
|
||||
background: $orange-bg;
|
||||
color: $black;
|
||||
}
|
||||
|
||||
.text-sm {
|
||||
font-size: 14px;
|
||||
}
|
||||
// generate spacer utility like bootstrap my-2 -> margin-left/right: 2 * $default-spacer
|
||||
// using $direction.key as css modifier, $direction.values to set css properties
|
||||
// scale it using $steps
|
||||
$directions: (
|
||||
"t": ("margin-top"),
|
||||
"r": ("margin-right"),
|
||||
"b": ("margin-bottom"),
|
||||
"l": ("margin-left"),
|
||||
"x": ("margin-left", "margin-right"),
|
||||
"y": ("margin-top", "margin-bottom"),
|
||||
"": ("margin")
|
||||
);
|
||||
$steps: (0, 1, 2, 3, 4, 5, 6, 7, 8);
|
||||
|
||||
.text-lg {
|
||||
font-size: 18px;
|
||||
@each $modifier, $properties in $directions {
|
||||
@each $step in $steps {
|
||||
@each $property in $properties {
|
||||
.m#{$modifier}-#{$step} {
|
||||
#{$property}: $step * $default-spacer;
|
||||
}
|
||||
|
||||
.mt-1 {
|
||||
margin-top: $default-spacer;
|
||||
}
|
||||
|
||||
.mt-2 {
|
||||
margin-top: 2 * $default-spacer;
|
||||
}
|
||||
|
||||
.mt-3 {
|
||||
margin-top: 3 * $default-spacer;
|
||||
}
|
||||
|
||||
.mt-4 {
|
||||
margin-top: 4 * $default-spacer;
|
||||
}
|
||||
|
||||
.mt-8 {
|
||||
margin-top: 8 * $default-spacer;
|
||||
}
|
||||
|
||||
.mb-1 {
|
||||
margin-bottom: $default-spacer;
|
||||
}
|
||||
|
||||
.mb-2 {
|
||||
margin-bottom: 2 * $default-spacer;
|
||||
}
|
||||
|
||||
.mb-3 {
|
||||
margin-bottom: 3 * $default-spacer;
|
||||
}
|
||||
|
||||
.mb-4 {
|
||||
margin-bottom: 4 * $default-spacer;
|
||||
}
|
||||
|
||||
.mb-8 {
|
||||
margin-bottom: 8 * $default-spacer;
|
||||
}
|
||||
|
||||
.pt-1 {
|
||||
padding-top: $default-spacer;
|
||||
}
|
||||
|
||||
.pt-2 {
|
||||
padding-top: 2 * $default-spacer;
|
||||
}
|
||||
|
||||
.pt-3 {
|
||||
padding-top: 3 * $default-spacer;
|
||||
}
|
||||
|
||||
.pt-4 {
|
||||
padding-top: 4 * $default-spacer;
|
||||
}
|
||||
|
||||
.pt-8 {
|
||||
padding-top: 8 * $default-spacer;
|
||||
}
|
||||
|
||||
.pb-1 {
|
||||
padding-bottom: $default-spacer;
|
||||
}
|
||||
|
||||
.pb-2 {
|
||||
padding-bottom: 2 * $default-spacer;
|
||||
}
|
||||
|
||||
.pb-3 {
|
||||
padding-bottom: 3 * $default-spacer;
|
||||
}
|
||||
|
||||
.pb-4 {
|
||||
padding-bottom: 4 * $default-spacer;
|
||||
}
|
||||
|
||||
.pb-8 {
|
||||
padding-bottom: 8 * $default-spacer;
|
||||
}
|
||||
|
|
|
@ -0,0 +1,45 @@
|
|||
module Administrateurs
|
||||
class DossierSubmittedMessagesController < AdministrateurController
|
||||
before_action :retrieve_procedure
|
||||
|
||||
def edit
|
||||
@dossier_submitted_message = build_dossier_submitted_message
|
||||
end
|
||||
|
||||
def update
|
||||
@dossier_submitted_message = build_dossier_submitted_message(dossier_submitted_message_params)
|
||||
|
||||
if @dossier_submitted_message.save
|
||||
redirect_to admin_procedure_path(@procedure), flash: { notice: "Les informations de fin de dépot ont bien été sauvegardées." }
|
||||
else
|
||||
flash.alert = "Impossible de sauvegarder les informations de fin de dépot, veuillez ré-essayer."
|
||||
render :edit, status: 400
|
||||
end
|
||||
end
|
||||
|
||||
def create
|
||||
@dossier_submitted_message = build_dossier_submitted_message(dossier_submitted_message_params)
|
||||
if @dossier_submitted_message.save
|
||||
redirect_to admin_procedure_path(@procedure), flash: { notice: "Les informations de fin de dépot ont bien été sauvegardées." }
|
||||
else
|
||||
flash.alert = "Impossible de sauvegarder les informations de \"fin de dépot\", veuillez ré-essayer."
|
||||
render :edit, status: 400
|
||||
end
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
# for now, only works on active revision no matter the procedure_revision_policy
|
||||
def build_dossier_submitted_message(attributes = {})
|
||||
dossier_submitted_message = @procedure.active_revision.dossier_submitted_message || @procedure.active_revision.build_dossier_submitted_message
|
||||
|
||||
dossier_submitted_message.attributes = attributes unless attributes.empty?
|
||||
dossier_submitted_message
|
||||
end
|
||||
|
||||
def dossier_submitted_message_params
|
||||
params.require(:dossier_submitted_message)
|
||||
.permit(:message_on_submit_by_usager)
|
||||
end
|
||||
end
|
||||
end
|
|
@ -17,7 +17,7 @@ class Champs::SiretController < ApplicationController
|
|||
|
||||
begin
|
||||
etablissement = find_etablissement_with_siret
|
||||
rescue APIEntreprise::API::Error::RequestFailed, APIEntreprise::API::Error::ServiceUnavailable
|
||||
rescue APIEntreprise::API::Error::RequestFailed, APIEntreprise::API::Error::BadGateway, APIEntreprise::API::Error::TimedOut, APIEntreprise::API::Error::ServiceUnavailable
|
||||
# i18n-tasks-use t('errors.messages.siret_network_error')
|
||||
return siret_error(:network_error)
|
||||
end
|
||||
|
|
|
@ -17,7 +17,8 @@ module Experts
|
|||
end
|
||||
|
||||
def procedure
|
||||
@procedure = Procedure.find(params[:procedure_id])
|
||||
@procedure = current_expert.procedures.find_by(id: params[:procedure_id])
|
||||
redirect_to(expert_all_avis_path, flash: { alert: "Vous n’avez pas accès à cette démarche." }) and return unless @procedure
|
||||
expert_avis = current_expert.avis.includes(:dossier).not_hidden_by_administration.where(dossiers: { groupe_instructeur: GroupeInstructeur.where(procedure: @procedure.id) })
|
||||
@avis_a_donner = expert_avis.without_answer
|
||||
@avis_donnes = expert_avis.with_answer
|
||||
|
@ -156,7 +157,8 @@ module Experts
|
|||
end
|
||||
|
||||
def set_avis_and_dossier
|
||||
@avis = Avis.find(params[:id])
|
||||
@avis = current_expert.avis.find_by(id: params[:id])
|
||||
redirect_to(expert_all_avis_path, flash: { alert: "Vous n’avez pas accès à cet avis." }) and return unless @avis
|
||||
@dossier = @avis.dossier
|
||||
end
|
||||
|
||||
|
|
|
@ -106,7 +106,7 @@ module Users
|
|||
sanitized_siret = siret_model.siret
|
||||
begin
|
||||
etablissement = APIEntrepriseService.create_etablissement(@dossier, sanitized_siret, current_user.id)
|
||||
rescue APIEntreprise::API::Error::RequestFailed, APIEntreprise::API::Error::BadGateway, APIEntreprise::API::Error::TimedOut
|
||||
rescue APIEntreprise::API::Error::RequestFailed, APIEntreprise::API::Error::BadGateway, APIEntreprise::API::Error::TimedOut, APIEntreprise::API::Error::ServiceUnavailable
|
||||
return render_siret_error(t('errors.messages.siret_network_error'))
|
||||
end
|
||||
if etablissement.nil?
|
||||
|
|
|
@ -26,6 +26,19 @@ import { useDeferredSubmit, useHiddenField } from './shared/hooks';
|
|||
|
||||
const Context = createContext();
|
||||
|
||||
const optionValueByLabel = (values, options, label) => {
|
||||
const maybeOption = values.includes(label)
|
||||
? [label, label]
|
||||
: options.find(([optionLabel]) => optionLabel == label);
|
||||
return maybeOption ? maybeOption[1] : undefined;
|
||||
};
|
||||
const optionLabelByValue = (values, options, value) => {
|
||||
const maybeOption = values.includes(value)
|
||||
? [value, value]
|
||||
: options.find(([, optionValue]) => optionValue == value);
|
||||
return maybeOption ? maybeOption[0] : undefined;
|
||||
};
|
||||
|
||||
function ComboMultiple({
|
||||
options,
|
||||
id,
|
||||
|
@ -40,9 +53,6 @@ function ComboMultiple({
|
|||
invariant(id || label, 'ComboMultiple: `id` or a `label` are required');
|
||||
invariant(group, 'ComboMultiple: `group` is required');
|
||||
|
||||
if (!Array.isArray(options[0])) {
|
||||
options = options.filter((o) => o).map((o) => [o, o]);
|
||||
}
|
||||
const inputRef = useRef();
|
||||
const [term, setTerm] = useState('');
|
||||
const [selections, setSelections] = useState(selected);
|
||||
|
@ -51,25 +61,22 @@ function ComboMultiple({
|
|||
const removedLabelledby = `${inputId}-remove`;
|
||||
const selectedLabelledby = `${inputId}-selected`;
|
||||
|
||||
const optionValueByLabel = (label) => {
|
||||
const maybeOption = newValues.includes(label)
|
||||
? [label, label]
|
||||
: options.find(([optionLabel]) => optionLabel == label);
|
||||
return maybeOption ? maybeOption[1] : undefined;
|
||||
};
|
||||
const optionLabelByValue = (value) => {
|
||||
const maybeOption = newValues.includes(value)
|
||||
? [value, value]
|
||||
: options.find(([, optionValue]) => optionValue == value);
|
||||
return maybeOption ? maybeOption[0] : undefined;
|
||||
};
|
||||
|
||||
const optionsWithLabels = useMemo(
|
||||
() =>
|
||||
Array.isArray(options[0])
|
||||
? options
|
||||
: options.filter((o) => o).map((o) => [o, o]),
|
||||
[options]
|
||||
);
|
||||
const extraOptions = useMemo(
|
||||
() =>
|
||||
acceptNewValues && term && term.length > 2 && !optionLabelByValue(term)
|
||||
acceptNewValues &&
|
||||
term &&
|
||||
term.length > 2 &&
|
||||
!optionLabelByValue(newValues, optionsWithLabels, term)
|
||||
? [[term, term]]
|
||||
: [],
|
||||
[acceptNewValues, term, newValues.join(',')]
|
||||
[acceptNewValues, term, optionsWithLabels, newValues]
|
||||
);
|
||||
const results = useMemo(
|
||||
() =>
|
||||
|
@ -77,12 +84,12 @@ function ComboMultiple({
|
|||
...extraOptions,
|
||||
...(term
|
||||
? matchSorter(
|
||||
options.filter(([label]) => !label.startsWith('--')),
|
||||
optionsWithLabels.filter(([label]) => !label.startsWith('--')),
|
||||
term
|
||||
)
|
||||
: options)
|
||||
: optionsWithLabels)
|
||||
].filter(([, value]) => !selections.includes(value)),
|
||||
[term, selections.join(','), newValues.join(',')]
|
||||
[term, selections, extraOptions, optionsWithLabels]
|
||||
);
|
||||
const [, setHiddenFieldValue, hiddenField] = useHiddenField(group, name);
|
||||
const awaitFormSubmit = useDeferredSubmit(hiddenField);
|
||||
|
@ -100,7 +107,7 @@ function ComboMultiple({
|
|||
};
|
||||
|
||||
const onSelect = (value) => {
|
||||
const maybeValue = [...extraOptions, ...options].find(
|
||||
const maybeValue = [...extraOptions, ...optionsWithLabels].find(
|
||||
([val]) => val == value
|
||||
);
|
||||
const selectedValue = maybeValue && maybeValue[1];
|
||||
|
@ -128,7 +135,7 @@ function ComboMultiple({
|
|||
};
|
||||
|
||||
const onRemove = (label) => {
|
||||
const optionValue = optionValueByLabel(label);
|
||||
const optionValue = optionValueByLabel(newValues, options, label);
|
||||
if (optionValue) {
|
||||
saveSelection((selections) =>
|
||||
selections.filter((value) => value != optionValue)
|
||||
|
@ -149,7 +156,9 @@ function ComboMultiple({
|
|||
) {
|
||||
if (
|
||||
term &&
|
||||
[...extraOptions, ...options].map(([label]) => label).includes(term)
|
||||
[...extraOptions, ...optionsWithLabels]
|
||||
.map(([label]) => label)
|
||||
.includes(term)
|
||||
) {
|
||||
event.preventDefault();
|
||||
onSelect(term);
|
||||
|
@ -172,7 +181,9 @@ function ComboMultiple({
|
|||
const onBlur = () => {
|
||||
const shouldSelect =
|
||||
term &&
|
||||
[...extraOptions, ...options].map(([label]) => label).includes(term);
|
||||
[...extraOptions, ...optionsWithLabels]
|
||||
.map(([label]) => label)
|
||||
.includes(term);
|
||||
|
||||
awaitFormSubmit(() => {
|
||||
if (shouldSelect) {
|
||||
|
@ -199,7 +210,7 @@ function ComboMultiple({
|
|||
<ComboboxToken
|
||||
key={selection}
|
||||
describedby={removedLabelledby}
|
||||
value={optionLabelByValue(selection)}
|
||||
value={optionLabelByValue(newValues, options, selection)}
|
||||
/>
|
||||
))}
|
||||
</ul>
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
import React, { useState, useCallback, useRef } from 'react';
|
||||
import React, { useState, useRef, ChangeEventHandler } from 'react';
|
||||
import { useDebounce } from 'use-debounce';
|
||||
import { useQuery } from 'react-query';
|
||||
import {
|
||||
|
@ -68,7 +68,7 @@ function ComboSearch<Result>({
|
|||
const [, value, label] = transformResult(result);
|
||||
return label ?? value;
|
||||
};
|
||||
const setExternalValueAndId = useCallback((label: string) => {
|
||||
const setExternalValueAndId = (label: string) => {
|
||||
const { key, value, result } = resultsMap.current[label];
|
||||
if (onChange) {
|
||||
onChange(value, result);
|
||||
|
@ -76,11 +76,12 @@ function ComboSearch<Result>({
|
|||
setExternalId(key);
|
||||
setExternalValue(value);
|
||||
}
|
||||
}, []);
|
||||
};
|
||||
const awaitFormSubmit = useDeferredSubmit(hiddenField);
|
||||
|
||||
const handleOnChange = useCallback(
|
||||
({ target: { value } }) => {
|
||||
const handleOnChange: ChangeEventHandler<HTMLInputElement> = ({
|
||||
target: { value }
|
||||
}) => {
|
||||
setValue(value);
|
||||
if (!value) {
|
||||
if (onChange) {
|
||||
|
@ -96,16 +97,14 @@ function ComboSearch<Result>({
|
|||
setExternalValue(value);
|
||||
}
|
||||
}
|
||||
},
|
||||
[minimumInputLength]
|
||||
);
|
||||
};
|
||||
|
||||
const handleOnSelect = useCallback((value: string) => {
|
||||
const handleOnSelect = (value: string) => {
|
||||
setExternalValueAndId(value);
|
||||
setValue(value);
|
||||
setSearchTerm('');
|
||||
awaitFormSubmit.done();
|
||||
}, []);
|
||||
};
|
||||
|
||||
const { isSuccess, data } = useQuery<void, void, unknown, QueryKey>(
|
||||
[scope, debouncedSearchTerm, scopeExtra],
|
||||
|
@ -117,14 +116,14 @@ function ComboSearch<Result>({
|
|||
const results =
|
||||
isSuccess && data ? transformResults(debouncedSearchTerm, data) : [];
|
||||
|
||||
const onBlur = useCallback(() => {
|
||||
const onBlur = () => {
|
||||
if (!allowInputValues && isSuccess && results[0]) {
|
||||
const label = getLabel(results[0]);
|
||||
awaitFormSubmit(() => {
|
||||
handleOnSelect(label);
|
||||
});
|
||||
}
|
||||
}, [data]);
|
||||
};
|
||||
|
||||
return (
|
||||
<Combobox onSelect={handleOnSelect}>
|
||||
|
|
|
@ -28,7 +28,8 @@ export function CadastreLayer({
|
|||
const map = useMapLibre();
|
||||
const selectedCadastresRef = useRef(new Set<string>());
|
||||
|
||||
const highlightFeature = useCallback((cid: string, highlight: boolean) => {
|
||||
const highlightFeature = useCallback(
|
||||
(cid: string, highlight: boolean) => {
|
||||
if (highlight) {
|
||||
selectedCadastresRef.current.add(cid);
|
||||
} else {
|
||||
|
@ -43,9 +44,12 @@ export function CadastreLayer({
|
|||
...selectedCadastresRef.current
|
||||
]);
|
||||
}
|
||||
}, []);
|
||||
},
|
||||
[map]
|
||||
);
|
||||
|
||||
const hoverFeature = useCallback((feature: Feature, hover: boolean) => {
|
||||
const hoverFeature = useCallback(
|
||||
(feature: Feature, hover: boolean) => {
|
||||
if (!selectedCadastresRef.current.has(feature.properties?.id)) {
|
||||
map.setFeatureState(
|
||||
{
|
||||
|
@ -56,7 +60,9 @@ export function CadastreLayer({
|
|||
{ hover }
|
||||
);
|
||||
}
|
||||
}, []);
|
||||
},
|
||||
[map]
|
||||
);
|
||||
|
||||
useCadastres(featureCollection, {
|
||||
hoverFeature,
|
||||
|
|
|
@ -48,6 +48,8 @@ export function DrawLayer({
|
|||
trash: true
|
||||
}
|
||||
});
|
||||
// We use mapbox-draw plugin with maplibre. They are compatible but types are not.
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||
map.addControl(draw as any, 'top-left');
|
||||
draw.set(
|
||||
filterFeatureCollection(featureCollection, SOURCE_SELECTION_UTILISATEUR)
|
||||
|
@ -64,11 +66,15 @@ export function DrawLayer({
|
|||
|
||||
return () => {
|
||||
if (drawRef.current) {
|
||||
// We use mapbox-draw plugin with maplibre. They are compatible but types are not.
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||
map.removeControl(drawRef.current as any);
|
||||
drawRef.current = null;
|
||||
}
|
||||
};
|
||||
}, [enabled]);
|
||||
// We only want to rerender draw layer on component mount or when the layer is toggled.
|
||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||
}, [map, enabled]);
|
||||
|
||||
const onSetId = useCallback(({ detail }) => {
|
||||
drawRef.current?.setFeatureProperty(detail.lid, 'id', detail.id);
|
||||
|
@ -167,7 +173,9 @@ function useExternalEvents(
|
|||
|
||||
useEffect(() => {
|
||||
fitBounds(featureCollection.bbox as LngLatBoundsLike);
|
||||
}, []);
|
||||
// We only want to zoom on bbox on component mount.
|
||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||
}, [fitBounds]);
|
||||
|
||||
useEvent('map:feature:focus', onFeatureFocus);
|
||||
useEvent('map:feature:create', onFeatureCreate);
|
||||
|
|
|
@ -44,13 +44,13 @@ export function GeoJSONLayer({
|
|||
popup.remove();
|
||||
}
|
||||
},
|
||||
[popup]
|
||||
[map, popup]
|
||||
);
|
||||
|
||||
const onMouseLeave = useCallback(() => {
|
||||
map.getCanvas().style.cursor = '';
|
||||
popup.remove();
|
||||
}, [popup]);
|
||||
}, [map, popup]);
|
||||
|
||||
useExternalEvents(featureCollection);
|
||||
|
||||
|
@ -99,17 +99,22 @@ export function GeoJSONLayer({
|
|||
|
||||
function useExternalEvents(featureCollection: FeatureCollection) {
|
||||
const fitBounds = useFitBounds();
|
||||
const onFeatureFocus = useCallback(({ detail }) => {
|
||||
const onFeatureFocus = useCallback(
|
||||
({ detail }) => {
|
||||
const { id } = detail;
|
||||
const feature = findFeature(featureCollection, id);
|
||||
if (feature) {
|
||||
fitBounds(getBounds(feature.geometry));
|
||||
}
|
||||
}, []);
|
||||
},
|
||||
[featureCollection, fitBounds]
|
||||
);
|
||||
|
||||
useEffect(() => {
|
||||
fitBounds(featureCollection.bbox as LngLatBoundsLike);
|
||||
}, []);
|
||||
// We only want to zoom on bbox on component mount.
|
||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||
}, [fitBounds]);
|
||||
|
||||
useEvent('map:feature:focus', onFeatureFocus);
|
||||
}
|
||||
|
@ -139,7 +144,7 @@ function LineStringLayer({
|
|||
type: 'line',
|
||||
paint: lineStringSelectionLine
|
||||
});
|
||||
}, []);
|
||||
}, [map, layerId, sourceId, feature]);
|
||||
|
||||
useMapEvent('mouseenter', onMouseEnter, layerId);
|
||||
useMapEvent('mouseleave', onMouseLeave, layerId);
|
||||
|
@ -172,7 +177,7 @@ function PointLayer({
|
|||
type: 'circle',
|
||||
paint: pointSelectionCircle
|
||||
});
|
||||
}, []);
|
||||
}, [map, layerId, sourceId, feature]);
|
||||
|
||||
useMapEvent('mouseenter', onMouseEnter, layerId);
|
||||
useMapEvent('mouseleave', onMouseLeave, layerId);
|
||||
|
@ -212,7 +217,7 @@ function PolygonLayer({
|
|||
type: 'fill',
|
||||
paint: polygonSelectionFill
|
||||
});
|
||||
}, []);
|
||||
}, [map, layerId, lineLayerId, sourceId, feature]);
|
||||
|
||||
useMapEvent('mouseenter', onMouseEnter, layerId);
|
||||
useMapEvent('mouseleave', onMouseLeave, layerId);
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
import React from 'react';
|
||||
import { createPortal } from 'react-dom';
|
||||
import invariant from 'tiny-invariant';
|
||||
|
||||
export function FlashMessage({
|
||||
message,
|
||||
|
@ -12,11 +13,13 @@ export function FlashMessage({
|
|||
sticky?: boolean;
|
||||
fixed?: boolean;
|
||||
}) {
|
||||
const element = document.getElementById('flash_messages');
|
||||
invariant(element, 'Flash messages root element not found');
|
||||
return createPortal(
|
||||
<div className="flash_message center">
|
||||
<div className={flashClassName(level, sticky, fixed)}>{message}</div>
|
||||
</div>,
|
||||
document.getElementById('flash_messages')!
|
||||
element
|
||||
);
|
||||
}
|
||||
|
||||
|
|
|
@ -5,7 +5,8 @@ import React, {
|
|||
useEffect,
|
||||
useMemo,
|
||||
ReactNode,
|
||||
createContext
|
||||
createContext,
|
||||
useCallback
|
||||
} from 'react';
|
||||
import maplibre, { Map, Style, NavigationControl } from 'maplibre-gl';
|
||||
|
||||
|
@ -37,11 +38,14 @@ export function MapLibre({ children, header, footer, layers }: MapLibreProps) {
|
|||
const containerRef = useRef<HTMLDivElement>(null);
|
||||
const [map, setMap] = useState<Map | null>();
|
||||
|
||||
const onStyleChange = (style: Style) => {
|
||||
const onStyleChange = useCallback(
|
||||
(style: Style) => {
|
||||
if (map) {
|
||||
map.setStyle(style);
|
||||
}
|
||||
};
|
||||
},
|
||||
[map]
|
||||
);
|
||||
const { style, ...mapStyleProps } = useStyle(layers, onStyleChange);
|
||||
|
||||
useEffect(() => {
|
||||
|
@ -56,7 +60,7 @@ export function MapLibre({ children, header, footer, layers }: MapLibreProps) {
|
|||
setMap(map);
|
||||
});
|
||||
}
|
||||
}, []);
|
||||
}, [map, style, isSupported]);
|
||||
|
||||
if (!isSupported) {
|
||||
return (
|
||||
|
|
|
@ -12,16 +12,22 @@ import { useMapLibre } from './MapLibre';
|
|||
|
||||
export function useFitBounds() {
|
||||
const map = useMapLibre();
|
||||
return useCallback((bbox: LngLatBoundsLike) => {
|
||||
return useCallback(
|
||||
(bbox: LngLatBoundsLike) => {
|
||||
map.fitBounds(bbox, { padding: 100 });
|
||||
}, []);
|
||||
},
|
||||
[map]
|
||||
);
|
||||
}
|
||||
|
||||
export function useFlyTo() {
|
||||
const map = useMapLibre();
|
||||
return useCallback((zoom: number, center: [number, number]) => {
|
||||
return useCallback(
|
||||
(zoom: number, center: [number, number]) => {
|
||||
map.flyTo({ zoom, center });
|
||||
}, []);
|
||||
},
|
||||
[map]
|
||||
);
|
||||
}
|
||||
|
||||
export function useEvent(eventName: string, callback: EventListener) {
|
||||
|
@ -44,12 +50,16 @@ export function useMapEvent(
|
|||
const map = useMapLibre();
|
||||
return useEffect(() => {
|
||||
if (target) {
|
||||
// event typing is hard
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||
map.on(eventName as keyof MapLayerEventType, target, callback as any);
|
||||
} else {
|
||||
map.on(eventName, callback);
|
||||
}
|
||||
return () => {
|
||||
if (target) {
|
||||
// event typing is hard
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||
map.off(eventName as keyof MapLayerEventType, target, callback as any);
|
||||
} else {
|
||||
map.off(eventName, callback);
|
||||
|
@ -104,7 +114,7 @@ export function useStyle(
|
|||
[styleId, enabledLayers]
|
||||
);
|
||||
|
||||
useEffect(() => onStyleChange(style), [style]);
|
||||
useEffect(() => onStyleChange(style), [onStyleChange, style]);
|
||||
|
||||
return { style, layers, setStyle, setLayerEnabled, setLayerOpacity };
|
||||
}
|
||||
|
|
|
@ -1,3 +1,4 @@
|
|||
/* eslint-disable @typescript-eslint/no-explicit-any */
|
||||
import type { AnyLayer } from 'maplibre-gl';
|
||||
|
||||
const layers: AnyLayer[] = [
|
||||
|
|
|
@ -1,3 +1,4 @@
|
|||
/* eslint-disable @typescript-eslint/no-explicit-any */
|
||||
import type { AnyLayer } from 'maplibre-gl';
|
||||
|
||||
const layers: AnyLayer[] = [
|
||||
|
|
|
@ -2,6 +2,17 @@ import { QueryClient, QueryFunction } from 'react-query';
|
|||
import { getJSON, isNumeric } from '@utils';
|
||||
import { matchSorter } from 'match-sorter';
|
||||
|
||||
type Gon = {
|
||||
gon: {
|
||||
autocomplete?: {
|
||||
api_geo_url?: string;
|
||||
api_adresse_url?: string;
|
||||
api_education_url?: string;
|
||||
};
|
||||
};
|
||||
};
|
||||
declare const window: Window & typeof globalThis & Gon;
|
||||
|
||||
const API_EDUCATION_QUERY_LIMIT = 5;
|
||||
const API_GEO_QUERY_LIMIT = 5;
|
||||
const API_ADRESSE_QUERY_LIMIT = 5;
|
||||
|
@ -16,7 +27,7 @@ const API_ADRESSE_QUERY_LIMIT = 5;
|
|||
const API_GEO_COMMUNES_QUERY_LIMIT = 60;
|
||||
|
||||
const { api_geo_url, api_adresse_url, api_education_url } =
|
||||
(window as any).gon.autocomplete || {};
|
||||
window.gon.autocomplete || {};
|
||||
|
||||
type QueryKey = readonly [
|
||||
scope: string,
|
||||
|
@ -70,8 +81,9 @@ const defaultQueryFn: QueryFunction<unknown, QueryKey> = async ({
|
|||
}
|
||||
throw new Error(`Error fetching from "${scope}" API`);
|
||||
});
|
||||
(promise as any).cancel = () => controller && controller.abort();
|
||||
return promise;
|
||||
return Object.assign(promise, {
|
||||
cancel: () => controller && controller.abort()
|
||||
});
|
||||
};
|
||||
|
||||
let paysCache: { label: string }[];
|
||||
|
@ -85,6 +97,8 @@ async function getPays(): Promise<{ label: string }[]> {
|
|||
export const queryClient = new QueryClient({
|
||||
defaultOptions: {
|
||||
queries: {
|
||||
// we don't really care about global queryFn type
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||
queryFn: defaultQueryFn as any
|
||||
}
|
||||
}
|
||||
|
|
|
@ -88,6 +88,15 @@ export function ajax(options: Rails.AjaxOptions) {
|
|||
});
|
||||
}
|
||||
|
||||
class ResponseError extends Error {
|
||||
response: Response;
|
||||
|
||||
constructor(response: Response) {
|
||||
super(String(response.statusText || response.status));
|
||||
this.response = response;
|
||||
}
|
||||
}
|
||||
|
||||
export function getJSON(url: string, data: unknown, method = 'GET') {
|
||||
const { query, ...options } = fetchOptions(data, method);
|
||||
|
||||
|
@ -98,9 +107,7 @@ export function getJSON(url: string, data: unknown, method = 'GET') {
|
|||
}
|
||||
return response.json();
|
||||
}
|
||||
const error = new Error(String(response.statusText || response.status));
|
||||
(error as any).response = response;
|
||||
throw error;
|
||||
throw new ResponseError(response);
|
||||
});
|
||||
}
|
||||
|
||||
|
@ -125,8 +132,9 @@ export function on(
|
|||
);
|
||||
}
|
||||
|
||||
export function isNumeric(n: string) {
|
||||
return !isNaN(parseFloat(n)) && isFinite(n as any as number);
|
||||
export function isNumeric(s: string) {
|
||||
const n = parseFloat(s);
|
||||
return !isNaN(n) && isFinite(n);
|
||||
}
|
||||
|
||||
function offset(element: HTMLElement) {
|
||||
|
|
|
@ -6,6 +6,7 @@
|
|||
# data :jsonb
|
||||
# fetch_external_data_exceptions :string is an Array
|
||||
# private :boolean default(FALSE), not null
|
||||
# rebased_at :datetime
|
||||
# row :integer
|
||||
# type :string
|
||||
# value :string
|
||||
|
|
|
@ -2,19 +2,57 @@ module DossierRebaseConcern
|
|||
extend ActiveSupport::Concern
|
||||
|
||||
def rebase!
|
||||
if brouillon? && revision != procedure.published_revision
|
||||
if can_rebase?
|
||||
transaction do
|
||||
rebase
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
def can_rebase?
|
||||
revision != procedure.published_revision &&
|
||||
(brouillon? || accepted_en_construction_changes? || accepted_en_instruction_changes?)
|
||||
end
|
||||
|
||||
def pending_changes
|
||||
revision.compare(procedure.published_revision)
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def accepted_en_construction_changes?
|
||||
en_construction? && pending_changes.all? { |change| accepted_en_construction_change?(change) }
|
||||
end
|
||||
|
||||
def accepted_en_instruction_changes?
|
||||
en_instruction? && pending_changes.all? { |change| accepted_en_instruction_change?(change) }
|
||||
end
|
||||
|
||||
def accepted_en_construction_change?(change)
|
||||
if change[:model] == :attestation_template || change[:op] == :move || change[:op] == :remove
|
||||
true
|
||||
elsif change[:op] == :update
|
||||
case change[:attribute]
|
||||
when :carte_layers
|
||||
true
|
||||
when :mandatory
|
||||
change[:from] && !change[:to]
|
||||
else
|
||||
false
|
||||
end
|
||||
else
|
||||
false
|
||||
end
|
||||
end
|
||||
|
||||
def accepted_en_instruction_change?(change)
|
||||
change[:model] == :attestation_template
|
||||
end
|
||||
|
||||
def rebase
|
||||
attachments_to_purge = []
|
||||
geo_areas_to_delete = []
|
||||
changes_by_type_de_champ = revision.compare(procedure.published_revision)
|
||||
changes_by_type_de_champ = pending_changes
|
||||
.filter { |change| change[:model] == :type_de_champ }
|
||||
.group_by { |change| change[:stable_id] }
|
||||
|
||||
|
@ -51,7 +89,9 @@ module DossierRebaseConcern
|
|||
when :drop_down_options
|
||||
update[:value] = nil
|
||||
when :carte_layers
|
||||
geo_areas_to_delete += champ.geo_areas
|
||||
if change[:from].include?(:cadastres) && !change[:to].include?(:cadastres)
|
||||
geo_areas_to_delete += champ.cadastres
|
||||
end
|
||||
end
|
||||
update[:rebased_at] = Time.zone.now
|
||||
end
|
||||
|
|
12
app/models/dossier_submitted_message.rb
Normal file
12
app/models/dossier_submitted_message.rb
Normal file
|
@ -0,0 +1,12 @@
|
|||
# == Schema Information
|
||||
#
|
||||
# Table name: dossier_submitted_messages
|
||||
#
|
||||
# id :bigint not null, primary key
|
||||
# message_on_submit_by_usager :string
|
||||
# created_at :datetime not null
|
||||
# updated_at :datetime not null
|
||||
#
|
||||
class DossierSubmittedMessage < ApplicationRecord
|
||||
has_many :revisions, class_name: 'ProcedureRevision', inverse_of: :dossier_submitted_message, dependent: :nullify
|
||||
end
|
|
@ -18,7 +18,6 @@
|
|||
# description :string
|
||||
# direction :string
|
||||
# duree_conservation_dossiers_dans_ds :integer
|
||||
# duree_conservation_dossiers_hors_ds :integer
|
||||
# durees_conservation_required :boolean default(TRUE)
|
||||
# encrypted_api_particulier_token :string
|
||||
# euro_flag :boolean default(FALSE)
|
||||
|
@ -80,6 +79,10 @@ class Procedure < ApplicationRecord
|
|||
has_one :draft_attestation_template, through: :draft_revision, source: :attestation_template
|
||||
has_one :published_attestation_template, through: :published_revision, source: :attestation_template
|
||||
|
||||
has_one :published_dossier_submitted_message, dependent: :destroy, through: :published_revision, source: :dossier_submitted_message
|
||||
has_one :draft_dossier_submitted_message, dependent: :destroy, through: :draft_revision, source: :dossier_submitted_message
|
||||
has_many :dossier_submitted_messages, through: :revisions, source: :dossier_submitted_message
|
||||
|
||||
has_many :experts_procedures, dependent: :destroy
|
||||
has_many :experts, through: :experts_procedures
|
||||
|
||||
|
@ -92,6 +95,10 @@ class Procedure < ApplicationRecord
|
|||
belongs_to :service, optional: true
|
||||
belongs_to :zone, optional: true
|
||||
|
||||
def active_dossier_submitted_message
|
||||
published_dossier_submitted_message || draft_dossier_submitted_message
|
||||
end
|
||||
|
||||
def active_revision
|
||||
brouillon? ? draft_revision : published_revision
|
||||
end
|
||||
|
@ -441,7 +448,8 @@ class Procedure < ApplicationRecord
|
|||
revision_types_de_champ_private: {
|
||||
type_de_champ: :types_de_champ
|
||||
},
|
||||
attestation_template: []
|
||||
attestation_template: [],
|
||||
dossier_submitted_message: []
|
||||
}
|
||||
}
|
||||
include_list[:groupe_instructeurs] = :instructeurs if !is_different_admin
|
||||
|
@ -738,9 +746,9 @@ class Procedure < ApplicationRecord
|
|||
def publish_revision!
|
||||
update!(draft_revision: create_new_revision, published_revision: draft_revision)
|
||||
published_revision.touch(:published_at)
|
||||
dossiers.state_brouillon.find_each do |dossier|
|
||||
DossierRebaseJob.perform_later(dossier)
|
||||
end
|
||||
dossiers
|
||||
.state_not_termine
|
||||
.find_each { |dossier| DossierRebaseJob.perform_later(dossier) }
|
||||
end
|
||||
|
||||
def cnaf_enabled?
|
||||
|
|
|
@ -7,12 +7,14 @@
|
|||
# created_at :datetime not null
|
||||
# updated_at :datetime not null
|
||||
# attestation_template_id :bigint
|
||||
# dossier_submitted_message_id :bigint
|
||||
# procedure_id :bigint not null
|
||||
#
|
||||
class ProcedureRevision < ApplicationRecord
|
||||
self.implicit_order_column = :created_at
|
||||
belongs_to :procedure, -> { with_discarded }, inverse_of: :revisions, optional: false
|
||||
belongs_to :attestation_template, inverse_of: :revisions, optional: true, dependent: :destroy
|
||||
belongs_to :dossier_submitted_message, inverse_of: :revisions, optional: true, dependent: :destroy
|
||||
|
||||
has_many :dossiers, inverse_of: :revision, foreign_key: :revision_id
|
||||
|
||||
|
|
|
@ -3,7 +3,7 @@
|
|||
# Table name: zones
|
||||
#
|
||||
# id :bigint not null, primary key
|
||||
# acronym :string
|
||||
# acronym :string not null
|
||||
# label :string
|
||||
# created_at :datetime not null
|
||||
# updated_at :datetime not null
|
||||
|
|
|
@ -0,0 +1,3 @@
|
|||
= f.label :message_on_submit_by_usager do
|
||||
Message affiché après l'envoie du dossier
|
||||
= f.text_area :message_on_submit_by_usager, placeholder: "Merci votre dossier sera traité dans les plus bref delais"
|
|
@ -0,0 +1,34 @@
|
|||
- content_for(:root_class, 'scroll-margins-for-sticky-footer')
|
||||
|
||||
= render partial: 'administrateurs/breadcrumbs',
|
||||
locals: { steps: [link_to('Démarches', admin_procedures_path),
|
||||
link_to(@procedure.libelle, admin_procedure_path(@procedure)),
|
||||
'Fin de dépot'] }
|
||||
|
||||
.procedure-form
|
||||
.procedure-form__columns.container
|
||||
= form_for @dossier_submitted_message,
|
||||
url: url_for({ controller: 'administrateurs/dossier_submitted_messages', action: :update, id: @procedure.id }),
|
||||
html: { class: 'form procedure-form__column--form' } do |f|
|
||||
|
||||
%h1.page-title
|
||||
Fin du dépot
|
||||
%p.notice
|
||||
L'utilisateur se vera afficher ce message une fois le dossier envoyé
|
||||
|
||||
= render partial: 'administrateurs/dossier_submitted_messages/informations', locals: { f: f }
|
||||
|
||||
|
||||
.procedure-form__actions
|
||||
.actions-left
|
||||
= f.submit 'Enregistrer', class: 'button primary send'
|
||||
|
||||
.procedure-form__column--preview
|
||||
%h3
|
||||
.procedure-form__preview-title
|
||||
Aperçu
|
||||
.notice
|
||||
Cet aperçu est mis à jour après chaque sauvegarde.
|
||||
|
||||
.procedure-preview
|
||||
= render partial: 'users/dossiers/merci', locals: { procedure: @procedure, dossier: nil}
|
|
@ -234,3 +234,18 @@
|
|||
%p.card-admin-title MonAvis
|
||||
%p.card-admin-subtitle Avis des usagers sur votre démarche
|
||||
%p.button Modifier
|
||||
|
||||
= link_to edit_admin_procedure_dossier_submitted_message_path(@procedure), class: 'card-admin' do
|
||||
- if @procedure.active_dossier_submitted_message.present?
|
||||
%div
|
||||
%span.icon.accept
|
||||
%p.card-admin-status-accept Validé
|
||||
- else
|
||||
%div
|
||||
%span.icon.clock
|
||||
%p.card-admin-status-todo À configurer
|
||||
%div
|
||||
%p.card-admin-title Fin de dépot
|
||||
%p.card-admin-subtitle Orienter l'usager suite à l'envoie de son dossier
|
||||
%p.button Modifier
|
||||
|
||||
|
|
|
@ -1,8 +1,45 @@
|
|||
- content_for(:title, t('.cta'))
|
||||
|
||||
.container
|
||||
%h1.mt-2.mb-2= t('.connect')
|
||||
#agentconnect
|
||||
.two-columns
|
||||
.columns-container
|
||||
.column.agent-intro
|
||||
%h1.mt-2.mb-2.agent= t('.you_are_an_agent')
|
||||
.box= t('.in_progress_html')
|
||||
|
||||
%p= t('.intro_html', app_name: APPLICATION_NAME)
|
||||
.center.mt-2
|
||||
%span.citizen= t('.you_are_a_citizen')
|
||||
%br
|
||||
%br
|
||||
= link_to t('.citizen_page'), new_user_session_path, class: "button expend secondary"
|
||||
|
||||
.column
|
||||
= t('.connect_html')
|
||||
|
||||
= link_to t('.cta'), agent_connect_login_path, class: "france-connect-agent-login-button"
|
||||
.france-connect-help-link
|
||||
= link_to t('.whats_agentconnect'), 'https://agentconnect.gouv.fr/', class: 'link', target: '_blank', target: "_blank", rel: "noopener", class: "link"
|
||||
|
||||
.france-connect-login-separator
|
||||
= t('views.shared.france_connect_login.separator')
|
||||
|
||||
#session-new.auth-form.sign-in-form
|
||||
= form_for User.new, url: user_session_path, html: { class: "form" } do |f|
|
||||
= f.label :email, t('.pro_email')
|
||||
= f.text_field :email, type: :email, autocomplete: 'username', autofocus: true
|
||||
|
||||
= f.label :password, t('views.users.sessions.new.password', min_length: PASSWORD_MIN_LENGTH)
|
||||
= f.password_field :password, autocomplete: 'current-password'
|
||||
|
||||
.auth-options
|
||||
.flex-no-shrink
|
||||
= f.check_box :remember_me
|
||||
= f.label :remember_me, t('views.users.sessions.new.remember_me'), class: 'remember-me'
|
||||
|
||||
.text-right
|
||||
= link_to t('views.users.sessions.new.reset_password'), new_user_password_path, class: "link"
|
||||
|
||||
= f.submit t('views.users.sessions.new.connection'), class: "button large primary expand"
|
||||
|
||||
- content_for :footer do
|
||||
= render partial: 'users/dossiers/index_footer'
|
||||
|
|
|
@ -3,16 +3,19 @@
|
|||
|
||||
%p= t(:hello, scope: [:views, :shared, :greetings])
|
||||
|
||||
- if !@dossier.brouillon?
|
||||
%p= t('.body_html', libelle_demarche: @dossier.procedure.libelle)
|
||||
%p= t('.link')
|
||||
= round_button(t('.access_message'), messagerie_dossier_url(@dossier), :primary)
|
||||
- else
|
||||
- if @dossier.brouillon?
|
||||
%p= t('.body_draft_html', libelle_demarche: @dossier.procedure.libelle)
|
||||
%p{ style: "padding: 8px; color: #333333; background-color: #EEEEEE; font-size: 14px;" }
|
||||
= @body
|
||||
%p= t('.contact')
|
||||
- if @service&.email.present?
|
||||
%p= t('.contact_html', email: @service.email)
|
||||
- else
|
||||
%p= t('.contact_no_email')
|
||||
= round_button(t('.access_file'), dossier_url(@dossier), :primary)
|
||||
- else
|
||||
%p= t('.body_html', libelle_demarche: @dossier.procedure.libelle)
|
||||
%p= t('.link')
|
||||
= round_button(t('.access_message'), messagerie_dossier_url(@dossier), :primary)
|
||||
|
||||
= render 'layouts/mailers/signature', service: @service
|
||||
|
||||
|
|
|
@ -62,7 +62,7 @@
|
|||
%li
|
||||
= render partial: 'layouts/account_dropdown', locals: { nav_bar_profile: nav_bar_profile }
|
||||
|
||||
- elsif request.path != new_user_session_path
|
||||
- elsif (request.path != new_user_session_path && request.path != agent_connect_path)
|
||||
- if request.path == new_user_registration_path
|
||||
%li
|
||||
= t('views.shared.account.already_user_question')
|
||||
|
|
|
@ -7,3 +7,8 @@
|
|||
= t('.line2')
|
||||
%br
|
||||
= t('.line3')
|
||||
%hr
|
||||
%span.small-simple= t('.are_you_new', app_name: APPLICATION_NAME.gsub("-","‑")).html_safe
|
||||
%br
|
||||
%br
|
||||
= link_to t('views.users.sessions.new.find_procedure'), COMMENT_TROUVER_MA_DEMARCHE_URL, target: "_blank", class: "button expend secondary"
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
- if FranceConnectService.enabled?
|
||||
.france-connect-login
|
||||
%h2
|
||||
%h2.important-header
|
||||
= t('views.shared.france_connect_login.title')
|
||||
%p
|
||||
= t('views.shared.france_connect_login.description')
|
||||
|
|
26
app/views/users/dossiers/_merci.html.haml
Normal file
26
app/views/users/dossiers/_merci.html.haml
Normal file
|
@ -0,0 +1,26 @@
|
|||
.merci.text-center.mb-7
|
||||
.container
|
||||
= image_tag('user/envoi-dossier.svg', alt: '', class: 'mt-8')
|
||||
%h1.mt-4.mb-3.mx-0= t('views.users.dossiers.merci.thanks')
|
||||
%p.send.m-2.text-lg
|
||||
= t('views.users.dossiers.merci.dossier_send_l1')
|
||||
%strong= procedure.libelle
|
||||
= t('views.users.dossiers.merci.dossier_send_l2')
|
||||
%p.m-2
|
||||
= t('views.users.dossiers.merci.dossier_acces_l1')
|
||||
%strong= t('views.users.dossiers.merci.dossier_acces_l2')
|
||||
%p.m-2
|
||||
= t('views.users.dossiers.merci.dossier_edit_l1')
|
||||
- if !dossier&.read_only?
|
||||
%strong= t('views.users.dossiers.merci.dossier_edit_l2')
|
||||
= t('views.users.dossiers.merci.dossier_edit_l3')
|
||||
%strong= t('views.users.dossiers.merci.dossier_edit_l4')
|
||||
- if procedure.active_dossier_submitted_message
|
||||
%p.m-2= procedure.active_dossier_submitted_message.message_on_submit_by_usager
|
||||
|
||||
.flex.column.align-center
|
||||
= link_to t('views.users.dossiers.merci.acces_dossier'), dossier ? dossier_path(dossier) : "#dossier" , class: 'button large primary mt-4'
|
||||
= link_to t('views.users.dossiers.merci.submit_dossier'), procedure_lien(procedure), class: 'mt-4'
|
||||
|
||||
.monavis
|
||||
!= procedure.monavis_embed
|
|
@ -3,26 +3,4 @@
|
|||
- content_for :footer do
|
||||
= render partial: "users/procedure_footer", locals: { procedure: @dossier.procedure, dossier: @dossier }
|
||||
|
||||
.merci
|
||||
.container
|
||||
= image_tag('user/envoi-dossier.svg', alt: '')
|
||||
%h1= t('views.users.dossiers.merci.thanks')
|
||||
%p.send
|
||||
= t('views.users.dossiers.merci.dossier_send_l1')
|
||||
%b= @dossier.procedure.libelle
|
||||
= t('views.users.dossiers.merci.dossier_send_l2')
|
||||
%p
|
||||
= t('views.users.dossiers.merci.dossier_acces_l1')
|
||||
%b= t('views.users.dossiers.merci.dossier_acces_l2')
|
||||
%p
|
||||
= t('views.users.dossiers.merci.dossier_edit_l1')
|
||||
- if !@dossier.read_only?
|
||||
%b= t('views.users.dossiers.merci.dossier_edit_l2')
|
||||
= t('views.users.dossiers.merci.dossier_edit_l3')
|
||||
%b= t('views.users.dossiers.merci.dossier_edit_l4')
|
||||
|
||||
.flex.column.align-center
|
||||
= link_to t('views.users.dossiers.merci.acces_dossier'), dossier_path(@dossier), class: 'button large primary'
|
||||
= link_to t('views.users.dossiers.merci.submit_dossier'), procedure_lien(@dossier.procedure)
|
||||
.monavis
|
||||
!= @dossier.procedure.monavis_embed
|
||||
= render partial: 'users/dossiers/merci', locals: { dossier: @dossier, procedure: @dossier.procedure}
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
= content_for(:page_id, 'auth')
|
||||
|
||||
.auth-form.sign-in-form
|
||||
#session-new.auth-form.sign-in-form
|
||||
|
||||
= form_for resource, url: user_session_path, html: { class: "form" } do |f|
|
||||
%h1.huge-title= t('views.users.sessions.new.sign_in')
|
||||
|
@ -23,15 +23,10 @@
|
|||
|
||||
= f.submit t('views.users.sessions.new.connection'), class: "button large primary expand"
|
||||
|
||||
- if AgentConnectService.enabled?
|
||||
.france-connect-login-separator
|
||||
= t('views.shared.france_connect_login.separator')
|
||||
- if AgentConnectService.enabled?
|
||||
.center
|
||||
%p.mb-2= t('views.users.sessions.new.instructor_or_admin')
|
||||
= link_to t('views.users.sessions.new.connect_with_agent_connect'), agent_connect_path
|
||||
%hr
|
||||
%p.center
|
||||
%span= t('views.users.sessions.new.are_you_new', app_name: APPLICATION_NAME.gsub("-","‑")).html_safe
|
||||
%h2.important-header= t('views.users.sessions.new.state_civil_servant')
|
||||
%br
|
||||
%br
|
||||
= link_to t('views.users.sessions.new.find_procedure'), COMMENT_TROUVER_MA_DEMARCHE_URL, target: "_blank", class: "button expend secondary"
|
||||
= link_to t('views.users.sessions.new.connect_with_agent_connect'), agent_connect_path, class: "button expend secondary"
|
||||
|
|
|
@ -18,6 +18,7 @@ FileUtils.chdir APP_ROOT do
|
|||
system('bundle check') || system!('bundle install')
|
||||
|
||||
# Install JavaScript dependencies
|
||||
system! 'node --version'
|
||||
system! 'bin/yarn install'
|
||||
|
||||
puts "\n== Updating webdrivers =="
|
||||
|
|
|
@ -15,7 +15,9 @@ FileUtils.chdir APP_ROOT do
|
|||
puts '== Installing dependencies =='
|
||||
system! 'gem install bundler --conservative'
|
||||
system('bundle check') || system!('bundle install')
|
||||
system! 'node --version'
|
||||
system! 'bin/yarn install'
|
||||
system! 'bin/yarn clean'
|
||||
|
||||
puts "\n== Updating webdrivers =="
|
||||
system! 'RAILS_ENV=test bin/rails webdrivers:chromedriver:update'
|
||||
|
|
|
@ -1,5 +1,46 @@
|
|||
{
|
||||
"ignored_warnings": [
|
||||
{
|
||||
"warning_type": "Cross-Site Scripting",
|
||||
"warning_code": 2,
|
||||
"fingerprint": "1b805585567775589825c0eda58cb84c074fc760d0a7afb101c023a51427f2b5",
|
||||
"check_name": "CrossSiteScripting",
|
||||
"message": "Unescaped model attribute",
|
||||
"file": "app/views/users/dossiers/_merci.html.haml",
|
||||
"line": 26,
|
||||
"link": "https://brakemanscanner.org/docs/warning_types/cross_site_scripting",
|
||||
"code": "current_user.dossiers.includes(:procedure).find(params[:id]).procedure.monavis_embed",
|
||||
"render_path": [
|
||||
{
|
||||
"type": "controller",
|
||||
"class": "Users::DossiersController",
|
||||
"method": "merci",
|
||||
"line": 196,
|
||||
"file": "app/controllers/users/dossiers_controller.rb",
|
||||
"rendered": {
|
||||
"name": "users/dossiers/merci",
|
||||
"file": "app/views/users/dossiers/merci.html.haml"
|
||||
}
|
||||
},
|
||||
{
|
||||
"type": "template",
|
||||
"name": "users/dossiers/merci",
|
||||
"line": 6,
|
||||
"file": "app/views/users/dossiers/merci.html.haml",
|
||||
"rendered": {
|
||||
"name": "users/dossiers/_merci",
|
||||
"file": "app/views/users/dossiers/_merci.html.haml"
|
||||
}
|
||||
}
|
||||
],
|
||||
"location": {
|
||||
"type": "template",
|
||||
"template": "users/dossiers/_merci"
|
||||
},
|
||||
"user_input": "current_user.dossiers.includes(:procedure)",
|
||||
"confidence": "Weak",
|
||||
"note": ""
|
||||
},
|
||||
{
|
||||
"warning_type": "Cross-Site Scripting",
|
||||
"warning_code": 2,
|
||||
|
@ -38,7 +79,7 @@
|
|||
"check_name": "SQL",
|
||||
"message": "Possible SQL injection",
|
||||
"file": "app/models/traitement.rb",
|
||||
"line": 51,
|
||||
"line": 52,
|
||||
"link": "https://brakemanscanner.org/docs/warning_types/sql_injection/",
|
||||
"code": "ActiveRecord::Base.connection.execute(\"select date_trunc('month', r1.processed_at::TIMESTAMPTZ AT TIME ZONE '#{Time.zone.formatted_offset}'::INTERVAL) as month, count(r1.processed_at)\\nfrom (#{Traitement.select(\"max(traitements.processed_at) as processed_at\").termine.where(:dossier => Dossier.state_termine.where(:groupe_instructeur => groupe_instructeurs)).group(:dossier_id).to_sql}) as r1\\ngroup by date_trunc('month', r1.processed_at::TIMESTAMPTZ AT TIME ZONE '#{Time.zone.formatted_offset}'::INTERVAL)\\norder by month desc\\n\")",
|
||||
"render_path": null,
|
||||
|
@ -51,37 +92,6 @@
|
|||
"confidence": "Medium",
|
||||
"note": ""
|
||||
},
|
||||
{
|
||||
"warning_type": "Cross-Site Scripting",
|
||||
"warning_code": 2,
|
||||
"fingerprint": "483ae8c038244eb3ed709e89846335e2c8ff6579260348ec31d3d03d1c94ad64",
|
||||
"check_name": "CrossSiteScripting",
|
||||
"message": "Unescaped model attribute",
|
||||
"file": "app/views/users/dossiers/merci.html.haml",
|
||||
"line": 28,
|
||||
"link": "https://brakemanscanner.org/docs/warning_types/cross_site_scripting",
|
||||
"code": "current_user.dossiers.includes(:procedure).find(params[:id]).procedure.monavis_embed",
|
||||
"render_path": [
|
||||
{
|
||||
"type": "controller",
|
||||
"class": "Users::DossiersController",
|
||||
"method": "merci",
|
||||
"line": 195,
|
||||
"file": "app/controllers/users/dossiers_controller.rb",
|
||||
"rendered": {
|
||||
"name": "users/dossiers/merci",
|
||||
"file": "app/views/users/dossiers/merci.html.haml"
|
||||
}
|
||||
}
|
||||
],
|
||||
"location": {
|
||||
"type": "template",
|
||||
"template": "users/dossiers/merci"
|
||||
},
|
||||
"user_input": "current_user.dossiers.includes(:procedure)",
|
||||
"confidence": "Weak",
|
||||
"note": ""
|
||||
},
|
||||
{
|
||||
"warning_type": "SQL Injection",
|
||||
"warning_code": 0,
|
||||
|
@ -102,26 +112,6 @@
|
|||
"confidence": "Medium",
|
||||
"note": "The table and column are escaped, which should make this safe"
|
||||
},
|
||||
{
|
||||
"warning_type": "SQL Injection",
|
||||
"warning_code": 0,
|
||||
"fingerprint": "c0f93612a68c32da58f327e0b5fa33dd42fd8beb2984cf023338c5aadbbdacca",
|
||||
"check_name": "SQL",
|
||||
"message": "Possible SQL injection",
|
||||
"file": "app/models/stat.rb",
|
||||
"line": 83,
|
||||
"link": "https://brakemanscanner.org/docs/warning_types/sql_injection/",
|
||||
"code": "association.where(date_attribute => ((3.months.ago.beginning_of_month..max_date))).group(\"DATE_TRUNC('month', #{date_attribute}::TIMESTAMPTZ AT TIME ZONE '#{Time.zone.formatted_offset}'::INTERVAL)\")",
|
||||
"render_path": null,
|
||||
"location": {
|
||||
"type": "method",
|
||||
"class": "Stat",
|
||||
"method": "last_four_months_hash"
|
||||
},
|
||||
"user_input": "date_attribute",
|
||||
"confidence": "Weak",
|
||||
"note": ""
|
||||
},
|
||||
{
|
||||
"warning_type": "Redirect",
|
||||
"warning_code": 18,
|
||||
|
@ -129,7 +119,7 @@
|
|||
"check_name": "Redirect",
|
||||
"message": "Possible unprotected redirect",
|
||||
"file": "app/controllers/instructeurs/procedures_controller.rb",
|
||||
"line": 195,
|
||||
"line": 202,
|
||||
"link": "https://brakemanscanner.org/docs/warning_types/redirect/",
|
||||
"code": "redirect_to(Export.find_or_create_export(params[:export_format], (params[:time_span_type] or \"everything\"), current_instructeur.groupe_instructeurs.where(:procedure => procedure)).file.service_url)",
|
||||
"render_path": null,
|
||||
|
@ -141,28 +131,8 @@
|
|||
"user_input": "Export.find_or_create_export(params[:export_format], (params[:time_span_type] or \"everything\"), current_instructeur.groupe_instructeurs.where(:procedure => procedure)).file.service_url",
|
||||
"confidence": "High",
|
||||
"note": ""
|
||||
},
|
||||
{
|
||||
"warning_type": "SQL Injection",
|
||||
"warning_code": 0,
|
||||
"fingerprint": "f2bb9bc6a56e44ab36ee18152c657395841cff354baed0a302b8d18650551529",
|
||||
"check_name": "SQL",
|
||||
"message": "Possible SQL injection",
|
||||
"file": "app/models/stat.rb",
|
||||
"line": 97,
|
||||
"link": "https://brakemanscanner.org/docs/warning_types/sql_injection/",
|
||||
"code": "association.where(\"#{date_attribute} < ?\", max_date).group(\"DATE_TRUNC('month', #{date_attribute}::TIMESTAMPTZ AT TIME ZONE '#{Time.zone.formatted_offset}'::INTERVAL)\")",
|
||||
"render_path": null,
|
||||
"location": {
|
||||
"type": "method",
|
||||
"class": "Stat",
|
||||
"method": "cumulative_hash"
|
||||
},
|
||||
"user_input": "date_attribute",
|
||||
"confidence": "Weak",
|
||||
"note": ""
|
||||
}
|
||||
],
|
||||
"updated": "2021-12-01 17:39:08 -1000",
|
||||
"updated": "2022-02-22 15:46:39 +0100",
|
||||
"brakeman_version": "5.1.1"
|
||||
}
|
||||
|
|
|
@ -66,8 +66,8 @@ Rails.application.configure do
|
|||
protocol: :http
|
||||
}
|
||||
|
||||
# Use Content-Security-Policy-Report-Only headers
|
||||
config.content_security_policy_report_only = true
|
||||
# Disallow all connections to external domains during tests
|
||||
config.content_security_policy_report_only = false
|
||||
|
||||
config.active_job.queue_adapter = :test
|
||||
config.active_storage.service = :test
|
||||
|
|
|
@ -5,22 +5,20 @@
|
|||
# https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Content-Security-Policy
|
||||
|
||||
Rails.application.config.content_security_policy do |policy|
|
||||
# Whitelist image
|
||||
images_whitelist = ["*.openstreetmap.org", "*.cloud.ovh.net", "*"]
|
||||
images_whitelist << URI(DS_PROXY_URL).host if DS_PROXY_URL.present?
|
||||
images_whitelist << URI(MATOMO_IFRAME_URL).host if MATOMO_IFRAME_URL.present?
|
||||
policy.img_src(:self, :data, :blob, *images_whitelist)
|
||||
|
||||
# Whitelist JS: nous, sendinblue et matomo
|
||||
# miniprofiler et nous avons quelques boutons inline :(
|
||||
# Javascript: allow us, SendInBlue and Matomo.
|
||||
# We need unsafe_inline because miniprofiler and us have some inline buttons :(
|
||||
scripts_whitelist = ["*.sendinblue.com", "*.crisp.chat", "crisp.chat", "*.sibautomation.com", "sibautomation.com", "cdn.jsdelivr.net", "maxcdn.bootstrapcdn.com", "code.jquery.com"]
|
||||
scripts_whitelist << URI(MATOMO_IFRAME_URL).host if MATOMO_IFRAME_URL.present?
|
||||
policy.script_src(:self, :unsafe_eval, :unsafe_inline, :blob, *scripts_whitelist)
|
||||
|
||||
# Pour les CSS, on a beaucoup de style inline et quelques balises <style>
|
||||
# c'est trop compliqué pour être rectifié immédiatement (et sans valeur ajoutée:
|
||||
# c'est hardcodé dans les vues, donc pas injectable).
|
||||
policy.style_src(:self, "*.crisp.chat", "crisp.chat", 'cdn.jsdelivr.net', 'maxcdn.bootstrapcdn.com', :unsafe_inline)
|
||||
# CSS: We have a lot of inline style, and some <style> tags.
|
||||
# It's too complicated to be fixed right now (and it wouldn't add value: this is hardcoded in views, so not subject to injections)
|
||||
policy.style_src(:self, :unsafe_inline, "*.crisp.chat", "crisp.chat", 'cdn.jsdelivr.net', 'maxcdn.bootstrapcdn.com')
|
||||
|
||||
connect_whitelist = ["wss://*.crisp.chat", "*.crisp.chat", "in-automate.sendinblue.com", "app.franceconnect.gouv.fr", "sentry.io", "openmaptiles.geo.data.gouv.fr", "openmaptiles.github.io", "tiles.geo.api.gouv.fr", "wxs.ign.fr"]
|
||||
connect_whitelist << ENV.fetch('APP_HOST')
|
||||
|
@ -31,22 +29,34 @@ Rails.application.config.content_security_policy do |policy|
|
|||
connect_whitelist << Rails.application.secrets.matomo[:host] if Rails.application.secrets.matomo[:enabled]
|
||||
policy.connect_src(:self, *connect_whitelist)
|
||||
|
||||
# Frames: allow Matomo's iframe on the /suivi page
|
||||
frame_whitelist = []
|
||||
frame_whitelist << URI(MATOMO_IFRAME_URL).host if Rails.application.secrets.matomo[:enabled]
|
||||
policy.frame_src(:self, *frame_whitelist)
|
||||
|
||||
# Pour tout le reste, par défaut on accepte uniquement ce qui vient de chez nous
|
||||
# et dans la notification on inclue la source de l'erreur
|
||||
# Everything else: allow us
|
||||
# Add the error source in the violation notification
|
||||
default_whitelist = ["fonts.gstatic.com", "in-automate.sendinblue.com", "player.vimeo.com", "app.franceconnect.gouv.fr", "sentry.io", "*.crisp.chat", "crisp.chat", "*.crisp.help", "*.sibautomation.com", "sibautomation.com", "data"]
|
||||
default_whitelist << URI(DS_PROXY_URL).host if DS_PROXY_URL.present?
|
||||
policy.default_src(:self, :data, :blob, :report_sample, *default_whitelist)
|
||||
|
||||
if Rails.env.development?
|
||||
# Les CSP ne sont pas appliquées en dev: on notifie cependant une url quelconque de la violation
|
||||
# pour détecter les erreurs lors de l'ajout d'une nouvelle brique externe durant le développement
|
||||
policy.report_uri "http://#{ENV.fetch('APP_HOST')}/csp/"
|
||||
# En développement, quand bin/webpack-dev-server est utilisé, on autorise les requêtes faites par le live-reload
|
||||
# Allow LiveReload requests
|
||||
policy.connect_src(*policy.connect_src, "ws://localhost:3035", "http://localhost:3035")
|
||||
|
||||
# CSP are not enforced in development (see content_security_policy_report_only in development.rb)
|
||||
# However we notify a random local URL, to see breakage in the DevTools when adding a new external resource.
|
||||
policy.report_uri "http://#{ENV.fetch('APP_HOST')}/csp/"
|
||||
|
||||
elsif Rails.env.test?
|
||||
# Disallow all connections to external domains during tests
|
||||
policy.img_src(:self, :data, :blob)
|
||||
policy.script_src(:self, :unsafe_eval, :unsafe_inline, :blob)
|
||||
policy.style_src(:self)
|
||||
policy.connect_src(:self)
|
||||
policy.frame_src(:self)
|
||||
policy.default_src(:self, :data, :blob)
|
||||
|
||||
else
|
||||
policy.report_uri CSP_REPORT_URI if CSP_REPORT_URI.present?
|
||||
end
|
||||
|
|
|
@ -56,6 +56,7 @@ en:
|
|||
line1: A simple tool
|
||||
line2: to manage dematerialized
|
||||
line3: administrative forms.
|
||||
are_you_new: First time on %{app_name}?
|
||||
locale_dropdown:
|
||||
languages: "Languages"
|
||||
notifications:
|
||||
|
@ -215,10 +216,9 @@ en:
|
|||
remember_me: Remember me
|
||||
reset_password: Forgot password?
|
||||
connection: Sign in
|
||||
are_you_new: First time on %{app_name}?
|
||||
find_procedure: Find your procedure
|
||||
instructor_or_admin: Instructor or Administrator ?
|
||||
connect_with_agent_connect: Connect with AgentConnect
|
||||
state_civil_servant: Are you a state civil servant?
|
||||
connect_with_agent_connect: Visit our dedicated page
|
||||
passwords:
|
||||
reset_link_sent:
|
||||
got_it: Got it!
|
||||
|
|
|
@ -47,6 +47,7 @@ fr:
|
|||
line1: Un outil simple
|
||||
line2: pour gérer les formulaires
|
||||
line3: administratifs dématérialisés.
|
||||
are_you_new: Vous êtes nouveau sur %{app_name} ?
|
||||
locale_dropdown:
|
||||
languages: "Langues"
|
||||
notifications:
|
||||
|
@ -212,10 +213,9 @@ fr:
|
|||
remember_me: Se souvenir de moi
|
||||
reset_password: Mot de passe oublié ?
|
||||
connection: Se connecter
|
||||
are_you_new: Vous êtes nouveau sur %{app_name} ?
|
||||
find_procedure: Trouvez votre démarche
|
||||
instructor_or_admin: Vous êtes instructeur ou administrateur ?
|
||||
connect_with_agent_connect: Se connecter avec AgentConnect
|
||||
state_civil_servant: Vous êtes agent de la fonction publique d’État ?
|
||||
connect_with_agent_connect: Accédez à notre page dédiée
|
||||
passwords:
|
||||
reset_link_sent:
|
||||
email_sent_html: "Nous vous avons envoyé un email à l’adresse <strong>%{email}</strong>."
|
||||
|
|
|
@ -2,10 +2,21 @@ en:
|
|||
agent_connect:
|
||||
agent:
|
||||
index:
|
||||
connect: Connect with AgentConnect
|
||||
intro_html: |
|
||||
AgentConnect allows <b class='bold'>instructors et administrators</b> to use their usual login credentials to connect to %{app_name}.
|
||||
<br />
|
||||
<br />
|
||||
Only agents of <b class='bold'>the Ministry of Ecological Transition</b> can currently benefit from it.
|
||||
cta: Connect with AgentConnect
|
||||
you_are_an_agent: Are you an employee of the state civil service or of a state operator?
|
||||
in_progress_html: |
|
||||
<p>
|
||||
<b class="bold">AgentConnect is currently being deployed.</b>
|
||||
<br>
|
||||
The ministries and operators that can currently benefit from it are :
|
||||
</p>
|
||||
<ul>
|
||||
<li>the Ministry of Ecological Transition</li>
|
||||
</ul>
|
||||
you_are_a_citizen: You are an individual ?
|
||||
citizen_page: Go to our dedicated page
|
||||
connect_html: |
|
||||
<h1 class="mt-2 mb-2">Connect</h1>
|
||||
<p><b class="bold">With AgentConnect</b></p>
|
||||
whats_agentconnect: 'What is AgentConnect?'
|
||||
pro_email: Professional email (nom@site.com)
|
||||
|
|
|
@ -2,10 +2,21 @@ fr:
|
|||
agent_connect:
|
||||
agent:
|
||||
index:
|
||||
connect: Connectez-vous avec AgentConnect
|
||||
intro_html: |
|
||||
AgentConnect permet aux <b class='bold'>instructeurs et administrateurs</b> d’utiliser leurs identifiants habituels pour se connecter à %{app_name}.
|
||||
<br />
|
||||
<br />
|
||||
Seul les agents du <b class='bold'>ministère de la Transition écologique</b> peuvent actuellement en bénéficier.
|
||||
cta: S’identifier avec AgentConnect
|
||||
you_are_an_agent: Vous êtes agent de la fonction publique dʼÉtat ou dʼun opérateur de lʼÉtat ?
|
||||
in_progress_html: |
|
||||
<p>
|
||||
<b class="bold">AgentConnect est en cours de déploiement.</b>
|
||||
<br>
|
||||
Les ministères et opérateurs qui peuvent l'utiliser à ce jour sont :
|
||||
</p>
|
||||
<ul>
|
||||
<li>le ministère de la Transition écologique</li>
|
||||
</ul>
|
||||
you_are_a_citizen: Vous êtes un particulier ?
|
||||
citizen_page: Accéder à notre page dédiée
|
||||
connect_html: |
|
||||
<h1 class="mt-2 mb-2">Connectez-vous</h1>
|
||||
<p><b class="bold">Avec AgentConnect</b></p>
|
||||
whats_agentconnect: 'Quʼest ce quʼAgentConnect ?'
|
||||
pro_email: Email professionnel (nom@site.com)
|
||||
|
|
|
@ -8,6 +8,7 @@ en:
|
|||
To read the message and answer it, select the following link:
|
||||
body_draft_html: |
|
||||
You received <b>a new message</b> from the service in charge of reviewing the file you started a draft for on the procedure « %{libelle_demarche} ».
|
||||
contact: If you chose to contact the service, please use the email available below in the page.
|
||||
contact_html: "If you chose to contact the service, please send an email directly to this address: <a href=\"mailto:%{email}\">%{email}</a>"
|
||||
contact_no_email: If you chose to contact the service, please use the contact infos available below.
|
||||
access_message: Read the message
|
||||
access_file: Open file
|
||||
|
|
|
@ -3,11 +3,12 @@ fr:
|
|||
notify_new_answer:
|
||||
subject: Nouveau message pour votre dossier nº %{dossier_id} « %{libelle_demarche} »
|
||||
body_html: |
|
||||
Vous avez reçu un <b>nouveau message</b> de la part du service en charge de votre dossier sur la démarche « %{libelle_demarche} ».
|
||||
Vous avez reçu un <b>nouveau message</b> de la part de l’administration en charge de votre dossier sur la démarche « %{libelle_demarche} ».
|
||||
link: |
|
||||
Pour consulter le message et y répondre, cliquez sur le bouton ci-dessous :
|
||||
body_draft_html: |
|
||||
Vous avez reçu un <b>nouveau message</b>du service pour lequel votre dossier est en brouillon pour la démarche « %{libelle_demarche} ».
|
||||
contact: Si vous souhaitez contacter le service, merci de le faire directement à l'aide de l'email en bas de page.
|
||||
Vous avez reçu un <b>nouveau message</b> de l’administration, au sujet de votre dossier en brouillon pour la démarche « %{libelle_demarche} ».
|
||||
contact_html: "Si vous souhaitez répondre à ce message, contactez directement l’administration à l’adresse suivante : <a href=\"mailto:%{email}\">%{email}</a>"
|
||||
contact_no_email: Si vous souhaitez répondre à ce message, contactez directement l’administration à l’aide des coordonnées en bas de cet email.
|
||||
access_message: Lire le message
|
||||
access_file: Voir le dossier
|
||||
|
|
|
@ -466,6 +466,7 @@ Rails.application.routes.draw do
|
|||
resource :attestation_template, only: [:edit, :update, :create] do
|
||||
get 'preview', on: :member
|
||||
end
|
||||
resource :dossier_submitted_message, only: [:edit, :update, :create]
|
||||
# ADDED TO ACCESS IT FROM THE IFRAME
|
||||
get 'attestation_template/preview' => 'attestation_templates#preview'
|
||||
end
|
||||
|
|
|
@ -0,0 +1,9 @@
|
|||
class CreateDossierSubmittedMessages < ActiveRecord::Migration[6.1]
|
||||
def change
|
||||
create_table :dossier_submitted_messages do |t|
|
||||
t.string :message_on_submit_by_usager
|
||||
t.timestamps
|
||||
end
|
||||
add_reference :procedure_revisions, :dossier_submitted_message, foreign_key: { to_table: :dossier_submitted_messages }, null: true, index: true
|
||||
end
|
||||
end
|
13
db/schema.rb
13
db/schema.rb
|
@ -10,7 +10,7 @@
|
|||
#
|
||||
# It's strongly recommended that you check this file into your version control system.
|
||||
|
||||
ActiveRecord::Schema.define(version: 2022_02_04_093401) do
|
||||
ActiveRecord::Schema.define(version: 2022_02_04_130722) do
|
||||
|
||||
# These are extensions that must be enabled in order to support this database
|
||||
enable_extension "plpgsql"
|
||||
|
@ -275,6 +275,12 @@ ActiveRecord::Schema.define(version: 2022_02_04_093401) do
|
|||
t.index ["keep_until"], name: "index_dossier_operation_logs_on_keep_until"
|
||||
end
|
||||
|
||||
create_table "dossier_submitted_messages", force: :cascade do |t|
|
||||
t.string "message_on_submit_by_usager"
|
||||
t.datetime "created_at", precision: 6, null: false
|
||||
t.datetime "updated_at", precision: 6, null: false
|
||||
end
|
||||
|
||||
create_table "dossier_transfer_logs", force: :cascade do |t|
|
||||
t.string "from", null: false
|
||||
t.string "to", null: false
|
||||
|
@ -324,10 +330,10 @@ ActiveRecord::Schema.define(version: 2022_02_04_093401) do
|
|||
t.datetime "identity_updated_at"
|
||||
t.datetime "depose_at"
|
||||
t.datetime "hidden_by_user_at"
|
||||
t.datetime "hidden_by_administration_at"
|
||||
t.string "hidden_by_reason"
|
||||
t.index "to_tsvector('french'::regconfig, (search_terms || private_search_terms))", name: "index_dossiers_on_search_terms_private_search_terms", using: :gin
|
||||
t.index "to_tsvector('french'::regconfig, search_terms)", name: "index_dossiers_on_search_terms", using: :gin
|
||||
t.datetime "hidden_by_administration_at"
|
||||
t.index ["archived"], name: "index_dossiers_on_archived"
|
||||
t.index ["dossier_transfer_id"], name: "index_dossiers_on_dossier_transfer_id"
|
||||
t.index ["groupe_instructeur_id"], name: "index_dossiers_on_groupe_instructeur_id"
|
||||
|
@ -595,7 +601,9 @@ ActiveRecord::Schema.define(version: 2022_02_04_093401) do
|
|||
t.datetime "updated_at", null: false
|
||||
t.datetime "published_at"
|
||||
t.bigint "attestation_template_id"
|
||||
t.bigint "dossier_submitted_message_id"
|
||||
t.index ["attestation_template_id"], name: "index_procedure_revisions_on_attestation_template_id"
|
||||
t.index ["dossier_submitted_message_id"], name: "index_procedure_revisions_on_dossier_submitted_message_id"
|
||||
t.index ["procedure_id"], name: "index_procedure_revisions_on_procedure_id"
|
||||
end
|
||||
|
||||
|
@ -876,6 +884,7 @@ ActiveRecord::Schema.define(version: 2022_02_04_093401) do
|
|||
add_foreign_key "procedure_revision_types_de_champ", "procedure_revisions", column: "revision_id"
|
||||
add_foreign_key "procedure_revision_types_de_champ", "types_de_champ"
|
||||
add_foreign_key "procedure_revisions", "attestation_templates"
|
||||
add_foreign_key "procedure_revisions", "dossier_submitted_messages"
|
||||
add_foreign_key "procedure_revisions", "procedures"
|
||||
add_foreign_key "procedures", "procedure_revisions", column: "draft_revision_id"
|
||||
add_foreign_key "procedures", "procedure_revisions", column: "published_revision_id"
|
||||
|
|
|
@ -0,0 +1,10 @@
|
|||
namespace :after_party do
|
||||
desc 'Deployment task: cleanup_deleted_dossiers'
|
||||
task cleanup_deleted_dossiers: :environment do
|
||||
puts "Running deploy task 'cleanup_deleted_dossiers'"
|
||||
|
||||
DeletedDossier.where(state: :brouillon).destroy_all
|
||||
|
||||
AfterParty::TaskRecord.create version: '20200326133630'
|
||||
end
|
||||
end
|
|
@ -0,0 +1,27 @@
|
|||
namespace :after_party do
|
||||
desc 'Deployment task: process_expired_dossiers_en_construction'
|
||||
task process_expired_dossiers_en_construction: :environment do
|
||||
puts "Running deploy task 'process_expired_dossiers_en_construction'"
|
||||
|
||||
if ENV['APP_NAME'] == 'tps'
|
||||
dossiers_close_to_expiration = Dossier
|
||||
.en_construction_close_to_expiration
|
||||
.without_en_construction_expiration_notice_sent
|
||||
|
||||
ExpiredDossiersDeletionService.send_expiration_notices(dossiers_close_to_expiration)
|
||||
|
||||
BATCH_SIZE = 1000
|
||||
|
||||
((dossiers_close_to_expiration.count / BATCH_SIZE).ceil + 1).times do |n|
|
||||
dossiers_close_to_expiration
|
||||
.offset(n * BATCH_SIZE)
|
||||
.limit(BATCH_SIZE)
|
||||
.update_all(en_construction_close_to_expiration_notice_sent_at: Time.zone.now + n.days)
|
||||
end
|
||||
end
|
||||
|
||||
# Update task as completed. If you remove the line below, the task will
|
||||
# run with every deploy (or every time you call after_party:run).
|
||||
AfterParty::TaskRecord.create version: '20200401123317'
|
||||
end
|
||||
end
|
|
@ -0,0 +1,35 @@
|
|||
namespace :after_party do
|
||||
desc 'Deployment task: fix_champ_etablissement'
|
||||
task fix_champ_etablissement: :environment do
|
||||
puts "Running deploy task 'fix_champ_etablissement'"
|
||||
|
||||
etablissements = Etablissement.joins(:champ).where.not(dossier_id: nil).where('etablissements.created_at > ?', 1.month.ago)
|
||||
dossiers_modif = []
|
||||
etablissements.find_each do |e|
|
||||
if e.dossier
|
||||
user = e.dossier.user
|
||||
dossier = e.dossier
|
||||
if user.dossiers.count == 1 && user.siret == e.champ.value
|
||||
e.update!(dossier_id: nil)
|
||||
dossier.reload.etablissement = e.reload.dup
|
||||
dossier.save!
|
||||
dossiers_modif << dossier.id
|
||||
fetch_api_entreprise_infos(dossier.etablissement.id, dossier.procedure.id, user.id)
|
||||
end
|
||||
end
|
||||
end
|
||||
puts "Nb dossiers modifiés: #{dossiers_modif.size}"
|
||||
AfterParty::TaskRecord.create version: '20200527124112'
|
||||
end
|
||||
|
||||
def fetch_api_entreprise_infos(etablissement_id, procedure_id, user_id)
|
||||
[
|
||||
APIEntreprise::EntrepriseJob, APIEntreprise::AssociationJob, APIEntreprise::ExercicesJob,
|
||||
APIEntreprise::EffectifsJob, APIEntreprise::EffectifsAnnuelsJob, APIEntreprise::AttestationSocialeJob,
|
||||
APIEntreprise::BilansBdfJob
|
||||
].each do |job|
|
||||
job.perform_later(etablissement_id, procedure_id)
|
||||
end
|
||||
APIEntreprise::AttestationFiscaleJob.perform_later(etablissement_id, procedure_id, user_id)
|
||||
end
|
||||
end
|
|
@ -0,0 +1,20 @@
|
|||
namespace :after_party do
|
||||
desc 'Deployment task: fix_dossier_etablissement'
|
||||
task fix_dossier_etablissement: :environment do
|
||||
puts "Running deploy task 'fix_dossier_etablissement'"
|
||||
|
||||
etablissements = Etablissement.joins(:champ).where.not(dossier_id: nil).where('etablissements.created_at > ?', 1.month.ago)
|
||||
dossiers_modif = []
|
||||
etablissements.find_each do |e|
|
||||
if e.dossier
|
||||
dossier = e.dossier
|
||||
e.update!(dossier_id: nil)
|
||||
dossier.reload.etablissement = e.reload.dup
|
||||
dossier.save!
|
||||
dossiers_modif << dossier.id
|
||||
end
|
||||
end
|
||||
puts "Nb dossiers modifiés: #{dossiers_modif.size}"
|
||||
AfterParty::TaskRecord.create version: '20200528124044'
|
||||
end
|
||||
end
|
|
@ -0,0 +1,25 @@
|
|||
namespace :after_party do
|
||||
desc 'Deployment task: drop_down_list_options_to_json'
|
||||
task drop_down_list_options_to_json: :environment do
|
||||
puts "Running deploy task 'drop_down_list_options_to_json'"
|
||||
|
||||
types_de_champ = TypeDeChamp.joins(:drop_down_list).where(type_champ: [
|
||||
TypeDeChamp.type_champs.fetch(:drop_down_list),
|
||||
TypeDeChamp.type_champs.fetch(:multiple_drop_down_list),
|
||||
TypeDeChamp.type_champs.fetch(:linked_drop_down_list)
|
||||
])
|
||||
progress = ProgressReport.new(types_de_champ.count)
|
||||
types_de_champ.find_each do |type_de_champ|
|
||||
type_de_champ.drop_down_list_value = type_de_champ.drop_down_list_value
|
||||
if type_de_champ.save
|
||||
type_de_champ.drop_down_list.destroy
|
||||
end
|
||||
progress.inc
|
||||
end
|
||||
progress.finish
|
||||
|
||||
# Update task as completed. If you remove the line below, the task will
|
||||
# run with every deploy (or every time you call after_party:run).
|
||||
AfterParty::TaskRecord.create version: '20200618121241'
|
||||
end
|
||||
end
|
22
lib/tasks/deployment/20200625113026_migrate_revisions.rake
Normal file
22
lib/tasks/deployment/20200625113026_migrate_revisions.rake
Normal file
|
@ -0,0 +1,22 @@
|
|||
namespace :after_party do
|
||||
desc 'Deployment task: migrate_revisions'
|
||||
task migrate_revisions: :environment do
|
||||
puts "Running deploy task 'migrate_revisions'"
|
||||
|
||||
procedures = Procedure.with_discarded.where(draft_revision_id: nil)
|
||||
progress = ProgressReport.new(procedures.count)
|
||||
|
||||
puts "Processing procedures"
|
||||
procedures.find_each do |procedure|
|
||||
RevisionsMigration.add_revisions(procedure)
|
||||
progress.inc
|
||||
end
|
||||
progress.finish
|
||||
|
||||
TmpDossiersMigrateRevisionsJob.perform_later([])
|
||||
|
||||
# Update task as completed. If you remove the line below, the task will
|
||||
# run with every deploy (or every time you call after_party:run).
|
||||
AfterParty::TaskRecord.create version: '20200625113026'
|
||||
end
|
||||
end
|
|
@ -0,0 +1,18 @@
|
|||
namespace :after_party do
|
||||
desc 'Deployment task: add_traitements_from_dossiers'
|
||||
task add_traitements_from_dossiers: :environment do
|
||||
puts "Running deploy task 'add_traitements_from_dossiers'"
|
||||
|
||||
dossiers_termines = Dossier.state_termine
|
||||
progress = ProgressReport.new(dossiers_termines.count)
|
||||
dossiers_termines.find_each do |dossier|
|
||||
dossier.traitements.create!(state: dossier.state, motivation: dossier.motivation, processed_at: dossier.processed_at)
|
||||
progress.inc
|
||||
end
|
||||
progress.finish
|
||||
|
||||
# Update task as completed. If you remove the line below, the task will
|
||||
# run with every deploy (or every time you call after_party:run).
|
||||
AfterParty::TaskRecord.create version: '20200630154829'
|
||||
end
|
||||
end
|
|
@ -0,0 +1,18 @@
|
|||
namespace :after_party do
|
||||
desc 'Deployment task: add_default_skip_validation_to_piece_justificative'
|
||||
task add_default_skip_validation_to_piece_justificative: :environment do
|
||||
puts "Running deploy task 'add_default_skip_validation_to_piece_justificative'"
|
||||
|
||||
tdcs = TypeDeChamp.where(type_champ: TypeDeChamp.type_champs.fetch(:piece_justificative))
|
||||
progress = ProgressReport.new(tdcs.count)
|
||||
tdcs.find_each do |tdc|
|
||||
tdc.update(skip_pj_validation: true)
|
||||
progress.inc
|
||||
end
|
||||
progress.finish
|
||||
|
||||
# Update task as completed. If you remove the line below, the task will
|
||||
# run with every deploy (or every time you call after_party:run).
|
||||
AfterParty::TaskRecord.create version: '20200708101123'
|
||||
end
|
||||
end
|
|
@ -0,0 +1,25 @@
|
|||
namespace :after_party do
|
||||
desc 'Deployment task: fix_cloned_revisions'
|
||||
task fix_cloned_revisions: :environment do
|
||||
puts "Running deploy task 'fix_cloned_revisions'"
|
||||
|
||||
Procedure.with_discarded.where(aasm_state: :brouillon).where.not(published_revision_id: nil).update_all(published_revision_id: nil)
|
||||
|
||||
types_de_champ = TypeDeChamp.joins(:revision).where('types_de_champ.procedure_id != procedure_revisions.procedure_id')
|
||||
progress = ProgressReport.new(types_de_champ.count)
|
||||
|
||||
types_de_champ.find_each do |type_de_champ|
|
||||
procedure = type_de_champ.procedure ? type_de_champ.procedure : Procedure.with_discarded.find(type_de_champ.procedure_id)
|
||||
revision_id = procedure.published_revision_id || procedure.draft_revision_id
|
||||
type_de_champ.update_column(:revision_id, revision_id)
|
||||
progress.inc
|
||||
end
|
||||
|
||||
progress.finish
|
||||
|
||||
# Update task as completed. If you remove the line below, the task will
|
||||
# run with every deploy (or every time you call after_party:run).
|
||||
AfterParty::TaskRecord
|
||||
.create version: AfterParty::TaskRecorder.new(__FILE__).timestamp
|
||||
end
|
||||
end
|
|
@ -0,0 +1,71 @@
|
|||
namespace :after_party do
|
||||
desc 'Deployment task: fix_geo_areas_geometry'
|
||||
task fix_geo_areas_geometry: :environment do
|
||||
puts "Running deploy task 'fix_geo_areas_geometry'"
|
||||
|
||||
geometry_collections = GeoArea.where("geometry -> 'type' = '\"GeometryCollection\"'")
|
||||
multi_polygons = GeoArea.where("geometry -> 'type' = '\"MultiPolygon\"'")
|
||||
multi_line_strings = GeoArea.where("geometry -> 'type' = '\"MultiLineString\"'")
|
||||
|
||||
def valid_geometry?(geometry)
|
||||
RGeo::GeoJSON.decode(geometry.to_json, geo_factory: RGeo::Geographic.simple_mercator_factory)
|
||||
true
|
||||
rescue
|
||||
false
|
||||
end
|
||||
|
||||
progress = ProgressReport.new(geometry_collections.count)
|
||||
geometry_collections.find_each do |geometry_collection|
|
||||
geometry_collection.geometry['geometries'].each do |geometry|
|
||||
if valid_geometry?(geometry)
|
||||
geometry_collection.champ.geo_areas.create!(geometry: geometry, source: 'selection_utilisateur')
|
||||
end
|
||||
end
|
||||
|
||||
geometry_collection.destroy
|
||||
progress.inc
|
||||
end
|
||||
progress.finish
|
||||
|
||||
progress = ProgressReport.new(multi_line_strings.count)
|
||||
multi_line_strings.find_each do |multi_line_string|
|
||||
multi_line_string.geometry['coordinates'].each do |coordinates|
|
||||
geometry = {
|
||||
type: 'LineString',
|
||||
coordinates: coordinates
|
||||
}
|
||||
|
||||
if valid_geometry?(geometry)
|
||||
multi_line_string.champ.geo_areas.create!(geometry: geometry, source: 'selection_utilisateur')
|
||||
end
|
||||
end
|
||||
|
||||
multi_line_string.destroy
|
||||
progress.inc
|
||||
end
|
||||
progress.finish
|
||||
|
||||
progress = ProgressReport.new(multi_polygons.count)
|
||||
multi_polygons.find_each do |multi_polygon|
|
||||
multi_polygon.geometry['coordinates'].each do |coordinates|
|
||||
geometry = {
|
||||
type: 'Polygon',
|
||||
coordinates: coordinates
|
||||
}
|
||||
|
||||
if valid_geometry?(geometry)
|
||||
multi_polygon.champ.geo_areas.create!(geometry: geometry, source: 'selection_utilisateur')
|
||||
end
|
||||
end
|
||||
|
||||
multi_polygon.destroy
|
||||
progress.inc
|
||||
end
|
||||
progress.finish
|
||||
|
||||
# Update task as completed. If you remove the line below, the task will
|
||||
# run with every deploy (or every time you call after_party:run).
|
||||
AfterParty::TaskRecord
|
||||
.create version: AfterParty::TaskRecorder.new(__FILE__).timestamp
|
||||
end
|
||||
end
|
|
@ -0,0 +1,50 @@
|
|||
namespace :after_party do
|
||||
desc 'Deployment task: migrate_filters_to_use_stable_id'
|
||||
task migrate_filters_to_use_stable_id: :environment do
|
||||
puts "Running deploy task 'migrate_filters_to_use_stable_id'"
|
||||
|
||||
procedure_presentations = ProcedurePresentation.where("filters -> 'migrated' IS NULL")
|
||||
progress = ProgressReport.new(procedure_presentations.count)
|
||||
procedure_presentations.find_each do |procedure_presentation|
|
||||
filters = procedure_presentation.filters
|
||||
sort = procedure_presentation.sort
|
||||
displayed_fields = procedure_presentation.displayed_fields
|
||||
|
||||
['tous', 'suivis', 'traites', 'a-suivre', 'archives'].each do |statut|
|
||||
filters[statut] = filters[statut].map do |filter|
|
||||
table, column = filter.values_at('table', 'column')
|
||||
if table && (table == 'type_de_champ' || table == 'type_de_champ_private')
|
||||
type_de_champ = TypeDeChamp.find_by(id: column)
|
||||
filter['column'] = type_de_champ&.stable_id&.to_s
|
||||
end
|
||||
filter
|
||||
end
|
||||
end
|
||||
|
||||
table, column = sort.values_at('table', 'column')
|
||||
if table && (table == 'type_de_champ' || table == 'type_de_champ_private')
|
||||
type_de_champ = TypeDeChamp.find_by(id: column)
|
||||
sort['column'] = type_de_champ&.stable_id&.to_s
|
||||
end
|
||||
|
||||
displayed_fields = displayed_fields.map do |displayed_field|
|
||||
table, column = displayed_field.values_at('table', 'column')
|
||||
if table && (table == 'type_de_champ' || table == 'type_de_champ_private')
|
||||
type_de_champ = TypeDeChamp.find_by(id: column)
|
||||
displayed_field['column'] = type_de_champ&.stable_id&.to_s
|
||||
end
|
||||
displayed_field
|
||||
end
|
||||
|
||||
filters['migrated'] = true
|
||||
procedure_presentation.update_columns(filters: filters, sort: sort, displayed_fields: displayed_fields)
|
||||
progress.inc
|
||||
end
|
||||
progress.finish
|
||||
|
||||
# Update task as completed. If you remove the line below, the task will
|
||||
# run with every deploy (or every time you call after_party:run).
|
||||
AfterParty::TaskRecord
|
||||
.create version: AfterParty::TaskRecorder.new(__FILE__).timestamp
|
||||
end
|
||||
end
|
11
lib/tasks/deployment/20201006123842_setup_first_stats.rake
Normal file
11
lib/tasks/deployment/20201006123842_setup_first_stats.rake
Normal file
|
@ -0,0 +1,11 @@
|
|||
namespace :after_party do
|
||||
desc 'Deployment task: setup_first_stats'
|
||||
task setup_first_stats: :environment do
|
||||
Stat.update_stats
|
||||
|
||||
# Update task as completed. If you remove the line below, the task will
|
||||
# run with every deploy (or every time you call after_party:run).
|
||||
AfterParty::TaskRecord
|
||||
.create version: AfterParty::TaskRecorder.new(__FILE__).timestamp
|
||||
end
|
||||
end
|
|
@ -0,0 +1,19 @@
|
|||
namespace :after_party do
|
||||
desc 'Deployment task: fix_types_de_champ_revisions'
|
||||
task fix_types_de_champ_revisions: :environment do
|
||||
puts "Running deploy task 'fix_types_de_champ_revisions'"
|
||||
|
||||
types_de_champ = TypeDeChamp.joins(:parent).where('types_de_champ.revision_id != parents_types_de_champ.revision_id')
|
||||
progress = ProgressReport.new(types_de_champ.count)
|
||||
types_de_champ.find_each do |type_de_champ|
|
||||
type_de_champ.update_column(:revision_id, type_de_champ.parent.revision_id)
|
||||
progress.inc
|
||||
end
|
||||
progress.finish
|
||||
|
||||
# Update task as completed. If you remove the line below, the task will
|
||||
# run with every deploy (or every time you call after_party:run).
|
||||
AfterParty::TaskRecord
|
||||
.create version: AfterParty::TaskRecorder.new(__FILE__).timestamp
|
||||
end
|
||||
end
|
|
@ -54,6 +54,7 @@
|
|||
"@typescript-eslint/eslint-plugin": "^5.8.1",
|
||||
"@typescript-eslint/parser": "^5.8.1",
|
||||
"babel-eslint": "^10.1.0",
|
||||
"del-cli": "^4.0.1",
|
||||
"eslint": "^7.32.0",
|
||||
"eslint-config-prettier": "^8.3.0",
|
||||
"eslint-plugin-prettier": "^4.0.0",
|
||||
|
@ -66,6 +67,7 @@
|
|||
"webpack-dev-server": "^4.6.0"
|
||||
},
|
||||
"scripts": {
|
||||
"clean": "del tmp public/packs public/packs-test",
|
||||
"lint:js": "eslint --ext .js,.jsx,.ts,.tsx ./app/javascript ./config/webpack",
|
||||
"webpack:build": "NODE_ENV=production bin/webpack",
|
||||
"lint:types": "tsc",
|
||||
|
|
|
@ -0,0 +1,81 @@
|
|||
include ActionDispatch::TestProcess
|
||||
|
||||
describe Administrateurs::DossierSubmittedMessagesController, type: :controller do
|
||||
let(:administrateur) { create(:administrateur) }
|
||||
|
||||
before { sign_in(administrateur.user) }
|
||||
|
||||
describe '#create' do
|
||||
context 'when procedure is not published' do
|
||||
let(:procedure) { create(:procedure, administrateur: administrateur) }
|
||||
|
||||
it 'creates a DossierSubmittedMessage on draft_revision' do
|
||||
message_on_submit_by_usager = "hello"
|
||||
expect {
|
||||
post(:create, params: { procedure_id: procedure.id, dossier_submitted_message: { message_on_submit_by_usager: message_on_submit_by_usager } })
|
||||
}.to change { DossierSubmittedMessage.count }.by(1)
|
||||
expect(response).to redirect_to admin_procedure_path(procedure)
|
||||
expect(procedure.reload.draft_revision.dossier_submitted_message).to eq(DossierSubmittedMessage.first)
|
||||
end
|
||||
end
|
||||
|
||||
context 'when procedure is published' do
|
||||
let(:procedure) { create(:procedure, :published, administrateur: administrateur) }
|
||||
|
||||
it 'creates a DossierSubmittedMessage on published_revision' do
|
||||
message_on_submit_by_usager = "hello"
|
||||
expect {
|
||||
post(:create, params: { procedure_id: procedure.id, dossier_submitted_message: { message_on_submit_by_usager: message_on_submit_by_usager } })
|
||||
}.to change { DossierSubmittedMessage.count }.by(1)
|
||||
expect(response).to redirect_to admin_procedure_path(procedure)
|
||||
expect(procedure.reload.published_revision.dossier_submitted_message).to eq(DossierSubmittedMessage.first)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
describe '#edit' do
|
||||
context 'when procedure is draft and have a DossierSubmittedMessage' do
|
||||
let(:procedure) { create(:procedure, :with_dossier_submitted_message, administrateur: administrateur) }
|
||||
|
||||
it 'assigns the existing DossierSubmittedMessage' do
|
||||
get(:edit, params: { procedure_id: procedure.id })
|
||||
expect(response).to have_http_status(200)
|
||||
expect(assigns(:dossier_submitted_message)).to eq(procedure.active_dossier_submitted_message)
|
||||
end
|
||||
end
|
||||
|
||||
context 'when draft procedure does not have dossier_submitted_message' do
|
||||
let(:procedure) { create(:procedure, administrateur: administrateur) }
|
||||
|
||||
it 'builds a new DossierSubmittedMessage' do
|
||||
get(:edit, params: { procedure_id: procedure.id })
|
||||
expect(response).to have_http_status(200)
|
||||
expect(assigns(:dossier_submitted_message).persisted?).to eq(false)
|
||||
expect(assigns(:dossier_submitted_message)).to be_an_instance_of(DossierSubmittedMessage)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
describe '#update' do
|
||||
context 'when procedure is draft' do
|
||||
let(:procedure) { create(:procedure, :with_dossier_submitted_message, administrateur: administrateur) }
|
||||
|
||||
it 'updates the existing DossierSubmittedMessage on draft_revision' do
|
||||
new_message_on_submit_by_usager = "hello"
|
||||
patch(:update, params: { procedure_id: procedure.id, dossier_submitted_message: { message_on_submit_by_usager: new_message_on_submit_by_usager } })
|
||||
expect(response).to redirect_to admin_procedure_path(procedure)
|
||||
expect(procedure.draft_revision.dossier_submitted_message.message_on_submit_by_usager).to eq(new_message_on_submit_by_usager)
|
||||
end
|
||||
end
|
||||
|
||||
context 'when draft procedure is published' do
|
||||
let(:procedure) { create(:procedure, :published, :with_dossier_submitted_message, administrateur: administrateur) }
|
||||
it 'updates the existing DossierSubmittedMessage on published_revision' do
|
||||
new_message_on_submit_by_usager = "hello"
|
||||
patch(:update, params: { procedure_id: procedure.id, dossier_submitted_message: { message_on_submit_by_usager: new_message_on_submit_by_usager } })
|
||||
expect(response).to redirect_to admin_procedure_path(procedure)
|
||||
expect(procedure.published_revision.dossier_submitted_message.message_on_submit_by_usager).to eq(new_message_on_submit_by_usager)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
|
@ -25,18 +25,31 @@ describe Experts::AvisController, type: :controller do
|
|||
end
|
||||
|
||||
describe '#procedure' do
|
||||
context 'without filter' do
|
||||
before { get :procedure, params: { procedure_id: procedure.id } }
|
||||
|
||||
it { expect(response).to have_http_status(:success) }
|
||||
it { expect(assigns(:avis_a_donner)).to match([avis_without_answer]) }
|
||||
it { expect(assigns(:avis_donnes)).to match([avis_with_answer]) }
|
||||
it { expect(assigns(:statut)).to eq('a-donner') }
|
||||
end
|
||||
|
||||
context 'with a statut equal to donnes' do
|
||||
before { get :procedure, params: { statut: 'donnes', procedure_id: procedure.id } }
|
||||
|
||||
it { expect(assigns(:statut)).to eq('donnes') }
|
||||
end
|
||||
|
||||
context 'with different procedure' do
|
||||
subject { get :procedure, params: { statut: 'donnes', procedure_id: procedure.id } }
|
||||
|
||||
it 'fails' do
|
||||
sign_in(create(:expert).user)
|
||||
subject
|
||||
expect(response).to redirect_to(expert_all_avis_path)
|
||||
expect(flash.alert).to eq("Vous n’avez pas accès à cette démarche.")
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
describe '#bilans_bdf' do
|
||||
|
@ -64,22 +77,52 @@ describe Experts::AvisController, type: :controller do
|
|||
expect(response).to redirect_to(root_path)
|
||||
end
|
||||
end
|
||||
|
||||
context 'with an avis that does not belongs to current_expert' do
|
||||
it "refuse l'accès au dossier" do
|
||||
sign_in(create(:expert).user)
|
||||
subject
|
||||
expect(response).to redirect_to(expert_all_avis_path)
|
||||
expect(flash.alert).to eq("Vous n’avez pas accès à cet avis.")
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
describe '#instruction' do
|
||||
before { get :instruction, params: { id: avis_without_answer.id, procedure_id: procedure.id } }
|
||||
subject { get :instruction, params: { id: avis_without_answer.id, procedure_id: procedure.id } }
|
||||
context 'with valid avis' do
|
||||
before { subject }
|
||||
it { expect(response).to have_http_status(:success) }
|
||||
it { expect(assigns(:avis)).to eq(avis_without_answer) }
|
||||
it { expect(assigns(:dossier)).to eq(dossier) }
|
||||
end
|
||||
context 'with an avis that does not belongs to current_expert' do
|
||||
it "refuse l'accès au dossier" do
|
||||
sign_in(create(:expert).user)
|
||||
subject
|
||||
expect(response).to redirect_to(expert_all_avis_path)
|
||||
expect(flash.alert).to eq("Vous n’avez pas accès à cet avis.")
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
describe '#messagerie' do
|
||||
subject { get :messagerie, params: { id: avis_without_answer.id, procedure_id: procedure.id } }
|
||||
context 'with valid avis' do
|
||||
before { subject }
|
||||
|
||||
it { expect(response).to have_http_status(:success) }
|
||||
it { expect(assigns(:avis)).to eq(avis_without_answer) }
|
||||
it { expect(assigns(:dossier)).to eq(dossier) }
|
||||
end
|
||||
|
||||
describe '#messagerie' do
|
||||
before { get :messagerie, params: { id: avis_without_answer.id, procedure_id: procedure.id } }
|
||||
|
||||
it { expect(response).to have_http_status(:success) }
|
||||
it { expect(assigns(:avis)).to eq(avis_without_answer) }
|
||||
it { expect(assigns(:dossier)).to eq(dossier) }
|
||||
context 'with an avis that does not belongs to current_expert' do
|
||||
it "refuse l'accès au dossier" do
|
||||
sign_in(create(:expert).user)
|
||||
subject
|
||||
expect(response).to redirect_to(expert_all_avis_path)
|
||||
expect(flash.alert).to eq("Vous n’avez pas accès à cet avis.")
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
describe '#update' do
|
||||
|
@ -118,6 +161,14 @@ describe Experts::AvisController, type: :controller do
|
|||
expect(flash.notice).to eq('Votre réponse est enregistrée.')
|
||||
end
|
||||
end
|
||||
context 'with an avis that does not belongs to current_expert' do
|
||||
it "refuse l'accès au dossier" do
|
||||
sign_in(create(:expert).user)
|
||||
patch :update, params: { id: avis_without_answer.id, procedure_id: procedure.id, avis: { answer: 'answer' } }
|
||||
expect(response).to redirect_to(expert_all_avis_path)
|
||||
expect(flash.alert).to eq("Vous n’avez pas accès à cet avis.")
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
describe '#create_commentaire' do
|
||||
|
|
5
spec/factories/dossier_submitted_message.rb
Normal file
5
spec/factories/dossier_submitted_message.rb
Normal file
|
@ -0,0 +1,5 @@
|
|||
FactoryBot.define do
|
||||
factory :dossier_submitted_message do
|
||||
message_on_submit_by_usager { "BAM !" }
|
||||
end
|
||||
end
|
|
@ -24,10 +24,11 @@ FactoryBot.define do
|
|||
types_de_champ_private { [] }
|
||||
updated_at { nil }
|
||||
attestation_template { nil }
|
||||
dossier_submitted_message { nil }
|
||||
end
|
||||
|
||||
after(:build) do |procedure, evaluator|
|
||||
initial_revision = build(:procedure_revision, procedure: procedure, attestation_template: evaluator.attestation_template)
|
||||
initial_revision = build(:procedure_revision, procedure: procedure, attestation_template: evaluator.attestation_template, dossier_submitted_message: evaluator.dossier_submitted_message)
|
||||
add_types_de_champs(evaluator.types_de_champ, to: initial_revision, scope: :public)
|
||||
add_types_de_champs(evaluator.types_de_champ_private, to: initial_revision, scope: :private)
|
||||
|
||||
|
@ -319,6 +320,12 @@ FactoryBot.define do
|
|||
end
|
||||
end
|
||||
end
|
||||
|
||||
trait :with_dossier_submitted_message do
|
||||
after(:build) do |procedure, _evaluator|
|
||||
build(:dossier_submitted_message, revisions: [procedure.active_revision])
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
|
|
|
@ -0,0 +1,25 @@
|
|||
describe '20200708101123_add_default_skip_validation_to_piece_justificative.rake' do
|
||||
let(:rake_task) { Rake::Task['after_party:add_default_skip_validation_to_piece_justificative'] }
|
||||
let!(:pj_type_de_champ) { create(:type_de_champ_piece_justificative) }
|
||||
let!(:text_type_de_champ) { create(:type_de_champ_text) }
|
||||
|
||||
before do
|
||||
rake_task.invoke
|
||||
text_type_de_champ.reload
|
||||
pj_type_de_champ.reload
|
||||
end
|
||||
|
||||
after { rake_task.reenable }
|
||||
|
||||
context 'on a piece_justificative type de champ' do
|
||||
it 'sets the skip_pj_validation option' do
|
||||
expect(pj_type_de_champ.skip_pj_validation).to be_truthy
|
||||
end
|
||||
end
|
||||
|
||||
context 'on a non piece_justificative type de champ' do
|
||||
it 'does not set the skip_pj_validation option' do
|
||||
expect(text_type_de_champ.skip_pj_validation).to be_blank
|
||||
end
|
||||
end
|
||||
end
|
|
@ -0,0 +1,60 @@
|
|||
describe '20201001161931_migrate_filters_to_use_stable_id' do
|
||||
let(:rake_task) { Rake::Task['after_party:migrate_filters_to_use_stable_id'] }
|
||||
|
||||
let(:procedure) { create(:procedure, :with_instructeur, :with_type_de_champ) }
|
||||
let(:type_de_champ) { procedure.types_de_champ.first }
|
||||
let(:sort) do
|
||||
{
|
||||
"table" => "type_de_champ",
|
||||
"column" => type_de_champ.id.to_s,
|
||||
"order" => "asc"
|
||||
}
|
||||
end
|
||||
let(:filters) do
|
||||
{
|
||||
'tous' => [
|
||||
{
|
||||
"label" => "test",
|
||||
"table" => "type_de_champ",
|
||||
"column" => type_de_champ.id.to_s,
|
||||
"value" => "test"
|
||||
}
|
||||
],
|
||||
'suivis' => [],
|
||||
'traites' => [],
|
||||
'a-suivre' => [],
|
||||
'archives' => []
|
||||
}
|
||||
end
|
||||
let(:displayed_fields) do
|
||||
[
|
||||
{
|
||||
"label" => "test",
|
||||
"table" => "type_de_champ",
|
||||
"column" => type_de_champ.id.to_s
|
||||
}
|
||||
]
|
||||
end
|
||||
let!(:procedure_presentation) do
|
||||
type_de_champ.update_column(:stable_id, 13)
|
||||
procedure_presentation = create(:procedure_presentation, procedure: procedure, assign_to: procedure.groupe_instructeurs.first.assign_tos.first)
|
||||
procedure_presentation.update_columns(sort: sort, filters: filters, displayed_fields: displayed_fields)
|
||||
procedure_presentation
|
||||
end
|
||||
|
||||
before do
|
||||
rake_task.invoke
|
||||
procedure_presentation.reload
|
||||
end
|
||||
|
||||
after { rake_task.reenable }
|
||||
|
||||
context "should migrate procedure_presentation" do
|
||||
it "columns are updated" do
|
||||
expect(procedure_presentation.sort['column']).to eq(type_de_champ.stable_id.to_s)
|
||||
expect(procedure_presentation.filters['tous'][0]['column']).to eq(type_de_champ.stable_id.to_s)
|
||||
expect(procedure_presentation.displayed_fields[0]['column']).to eq(type_de_champ.stable_id.to_s)
|
||||
expect(procedure_presentation.filters['migrated']).to eq(true)
|
||||
end
|
||||
end
|
||||
end
|
|
@ -27,15 +27,23 @@ RSpec.describe DossierMailer, type: :mailer do
|
|||
end
|
||||
|
||||
describe '.notify_new_answer with dossier brouillon' do
|
||||
let(:dossier) { create(:dossier, procedure: create(:simple_procedure)) }
|
||||
let(:service) { build(:service) }
|
||||
let(:procedure) { create(:simple_procedure, service: service) }
|
||||
let(:dossier) { create(:dossier, procedure: procedure) }
|
||||
let(:commentaire) { create(:commentaire, dossier: dossier) }
|
||||
subject { described_class.with(commentaire: commentaire).notify_new_answer }
|
||||
|
||||
it { expect(subject.subject).to include("Nouveau message") }
|
||||
it { expect(subject.subject).to include(dossier.id.to_s) }
|
||||
it { expect(subject.body).to include(dossier.procedure.service.email) }
|
||||
it { expect(subject.body).not_to include(messagerie_dossier_url(dossier)) }
|
||||
|
||||
it_behaves_like 'a dossier notification'
|
||||
|
||||
context 'when there is no associated service' do
|
||||
let(:service) { nil }
|
||||
it { expect { subject }.not_to raise_error }
|
||||
end
|
||||
end
|
||||
|
||||
describe '.notify_new_answer with dossier en construction' do
|
||||
|
|
|
@ -5,7 +5,7 @@ class DossierMailerPreview < ActionMailer::Preview
|
|||
end
|
||||
|
||||
def notify_new_answer
|
||||
DossierMailer.notify_new_answer(dossier)
|
||||
DossierMailer.with(commentaire: commentaire(on: draft)).notify_new_answer
|
||||
end
|
||||
|
||||
def notify_revert_to_instruction
|
||||
|
@ -88,7 +88,7 @@ class DossierMailerPreview < ActionMailer::Preview
|
|||
end
|
||||
|
||||
def draft
|
||||
Dossier.new(id: 47882, procedure: procedure, user: user)
|
||||
Dossier.new(id: 47882, state: :brouillon, procedure: procedure, user: user)
|
||||
end
|
||||
|
||||
def dossier
|
||||
|
@ -119,4 +119,9 @@ class DossierMailerPreview < ActionMailer::Preview
|
|||
def transfer
|
||||
DossierTransfer.new(email: usager_email, dossiers: [dossier, dossier_accepte])
|
||||
end
|
||||
|
||||
def commentaire(on:)
|
||||
dossier = on
|
||||
Commentaire.new(id: 7726, body: "Bonjour, Vous avez commencé le dépôt d’un dossier pour une subvention DETR /DSIL. Dans le cas où votre opération n’aurait pas connu un commencement d’exécution, vous êtes encouragé(e) à redéposer un nouveau dossier sur le formulaire de cette année.\nLa DDT", dossier: dossier)
|
||||
end
|
||||
end
|
||||
|
|
|
@ -1573,6 +1573,158 @@ describe Dossier do
|
|||
it { expect(dossier.spreadsheet_columns(types_de_champ: [])).to include(["État du dossier", "Brouillon"]) }
|
||||
end
|
||||
|
||||
describe '#can_rebase?' do
|
||||
let(:procedure) { create(:procedure, :with_type_de_champ_mandatory, :with_yes_no, attestation_template: build(:attestation_template)) }
|
||||
let(:attestation_template) { procedure.draft_revision.attestation_template.find_or_revise! }
|
||||
let(:type_de_champ) { procedure.types_de_champ.find { |tdc| !tdc.mandatory? } }
|
||||
let(:mandatory_type_de_champ) { procedure.types_de_champ.find(&:mandatory?) }
|
||||
|
||||
before { Flipper.enable(:procedure_revisions, procedure) }
|
||||
|
||||
context 'en_construction' do
|
||||
let(:dossier) { create(:dossier, :en_construction, procedure: procedure) }
|
||||
|
||||
before do
|
||||
procedure.publish!
|
||||
procedure.reload
|
||||
dossier
|
||||
end
|
||||
|
||||
context 'with added type de champ' do
|
||||
before do
|
||||
procedure.draft_revision.add_type_de_champ({
|
||||
type_champ: TypeDeChamp.type_champs.fetch(:text),
|
||||
libelle: "Un champ text"
|
||||
})
|
||||
procedure.publish_revision!
|
||||
dossier.reload
|
||||
end
|
||||
|
||||
it 'should be false' do
|
||||
expect(dossier.pending_changes).not_to be_empty
|
||||
expect(dossier.can_rebase?).to be_falsey
|
||||
end
|
||||
end
|
||||
|
||||
context 'with type de champ made optional' do
|
||||
before do
|
||||
procedure.draft_revision.find_or_clone_type_de_champ(mandatory_type_de_champ.stable_id).update(mandatory: false)
|
||||
procedure.publish_revision!
|
||||
dossier.reload
|
||||
end
|
||||
|
||||
it 'should be true' do
|
||||
expect(dossier.pending_changes).not_to be_empty
|
||||
expect(dossier.can_rebase?).to be_truthy
|
||||
end
|
||||
end
|
||||
|
||||
context 'with type de champ made mandatory' do
|
||||
before do
|
||||
procedure.draft_revision.find_or_clone_type_de_champ(type_de_champ.stable_id).update(mandatory: true)
|
||||
procedure.publish_revision!
|
||||
dossier.reload
|
||||
end
|
||||
|
||||
it 'should be false' do
|
||||
expect(dossier.pending_changes).not_to be_empty
|
||||
expect(dossier.can_rebase?).to be_falsey
|
||||
end
|
||||
end
|
||||
|
||||
context 'with removed type de champ' do
|
||||
before do
|
||||
procedure.draft_revision.remove_type_de_champ(type_de_champ.stable_id)
|
||||
procedure.publish_revision!
|
||||
dossier.reload
|
||||
end
|
||||
|
||||
it 'should be true' do
|
||||
expect(dossier.pending_changes).not_to be_empty
|
||||
expect(dossier.can_rebase?).to be_truthy
|
||||
end
|
||||
end
|
||||
|
||||
context 'with attestation template changes' do
|
||||
before do
|
||||
attestation_template.update(title: "Test")
|
||||
procedure.publish_revision!
|
||||
dossier.reload
|
||||
end
|
||||
|
||||
it 'should be true' do
|
||||
expect(dossier.pending_changes).not_to be_empty
|
||||
expect(dossier.can_rebase?).to be_truthy
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
context 'en_instruction' do
|
||||
let(:dossier) { create(:dossier, :en_instruction, procedure: procedure) }
|
||||
|
||||
before do
|
||||
procedure.publish!
|
||||
procedure.reload
|
||||
dossier
|
||||
end
|
||||
|
||||
context 'with added type de champ' do
|
||||
before do
|
||||
procedure.draft_revision.add_type_de_champ({
|
||||
type_champ: TypeDeChamp.type_champs.fetch(:text),
|
||||
libelle: "Un champ text"
|
||||
})
|
||||
procedure.publish_revision!
|
||||
dossier.reload
|
||||
end
|
||||
|
||||
it 'should be false' do
|
||||
expect(dossier.pending_changes).not_to be_empty
|
||||
expect(dossier.can_rebase?).to be_falsey
|
||||
end
|
||||
end
|
||||
|
||||
context 'with removed type de champ' do
|
||||
before do
|
||||
procedure.draft_revision.remove_type_de_champ(type_de_champ.stable_id)
|
||||
procedure.publish_revision!
|
||||
dossier.reload
|
||||
end
|
||||
|
||||
it 'should be false' do
|
||||
expect(dossier.pending_changes).not_to be_empty
|
||||
expect(dossier.can_rebase?).to be_falsey
|
||||
end
|
||||
end
|
||||
|
||||
context 'with attestation template changes' do
|
||||
before do
|
||||
attestation_template.update(title: "Test")
|
||||
procedure.publish_revision!
|
||||
dossier.reload
|
||||
end
|
||||
|
||||
it 'should be true' do
|
||||
expect(dossier.pending_changes).not_to be_empty
|
||||
expect(dossier.can_rebase?).to be_truthy
|
||||
end
|
||||
end
|
||||
|
||||
context 'with type de champ made optional' do
|
||||
before do
|
||||
procedure.draft_revision.find_or_clone_type_de_champ(mandatory_type_de_champ.stable_id).update(mandatory: false)
|
||||
procedure.publish_revision!
|
||||
dossier.reload
|
||||
end
|
||||
|
||||
it 'should be false' do
|
||||
expect(dossier.pending_changes).not_to be_empty
|
||||
expect(dossier.can_rebase?).to be_falsey
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
describe "#rebase" do
|
||||
let(:procedure) { create(:procedure, :with_type_de_champ_mandatory, :with_yes_no, :with_repetition, :with_datetime) }
|
||||
let(:dossier) { create(:dossier, procedure: procedure) }
|
||||
|
|
|
@ -825,13 +825,15 @@ describe Procedure do
|
|||
context 'when the procedure has dossiers' do
|
||||
let(:dossier_draft) { create(:dossier, :brouillon, procedure: procedure) }
|
||||
let(:dossier_submitted) { create(:dossier, :en_construction, procedure: procedure) }
|
||||
let(:dossier_termine) { create(:dossier, :accepte, procedure: procedure) }
|
||||
|
||||
before { [dossier_draft, dossier_submitted] }
|
||||
before { [dossier_draft, dossier_submitted, dossier_termine] }
|
||||
|
||||
it 'enqueues rebase jobs for draft dossiers' do
|
||||
subject
|
||||
expect(DossierRebaseJob).to have_been_enqueued.with(dossier_draft)
|
||||
expect(DossierRebaseJob).not_to have_been_enqueued.with(dossier_submitted)
|
||||
expect(DossierRebaseJob).to have_been_enqueued.with(dossier_submitted)
|
||||
expect(DossierRebaseJob).not_to have_been_enqueued.with(dossier_termine)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
@ -433,7 +433,7 @@ describe User, type: :model do
|
|||
it 'transfers the dossier' do
|
||||
subject
|
||||
|
||||
expect(targeted_user.dossiers.with_discarded).to match([dossier, hidden_dossier])
|
||||
expect(targeted_user.dossiers.with_discarded).to contain_exactly(dossier, hidden_dossier)
|
||||
expect(targeted_user.invites).to match([invite])
|
||||
expect(targeted_user.merge_logs.first).to eq(merge_log)
|
||||
|
||||
|
|
176
yarn.lock
176
yarn.lock
|
@ -2442,6 +2442,11 @@
|
|||
resolved "https://registry.yarnpkg.com/@types/minimatch/-/minimatch-3.0.5.tgz#1001cc5e6a3704b83c236027e77f2f58ea010f40"
|
||||
integrity sha512-Klz949h02Gz2uZCMGwDUSDS1YBlTdDDgbWHi+81l29tQALUtvz4rAYi5uoVhE5Lagoq6DeqAUlbrHvW/mXDgdQ==
|
||||
|
||||
"@types/minimist@^1.2.2":
|
||||
version "1.2.2"
|
||||
resolved "https://registry.yarnpkg.com/@types/minimist/-/minimist-1.2.2.tgz#ee771e2ba4b3dc5b372935d549fd9617bf345b8c"
|
||||
integrity sha512-jhuKLIRrhvCPLqwPcx6INqmKeiA5EWrsCOPhrlFSrbrmU4ZMPjj5Ul/oLCMDO98XRUIwVm78xICz4EPCektzeQ==
|
||||
|
||||
"@types/node-fetch@^2.1.6":
|
||||
version "2.5.12"
|
||||
resolved "https://registry.yarnpkg.com/@types/node-fetch/-/node-fetch-2.5.12.tgz#8a6f779b1d4e60b7a57fb6fd48d84fb545b9cc66"
|
||||
|
@ -3224,6 +3229,11 @@ array.prototype.flatmap@^1.2.5:
|
|||
define-properties "^1.1.3"
|
||||
es-abstract "^1.19.0"
|
||||
|
||||
arrify@^1.0.1:
|
||||
version "1.0.1"
|
||||
resolved "https://registry.yarnpkg.com/arrify/-/arrify-1.0.1.tgz#898508da2226f380df904728456849c1501a4b0d"
|
||||
integrity sha1-iYUI2iIm84DfkEcoRWhJwVAaSw0=
|
||||
|
||||
arrify@^2.0.1:
|
||||
version "2.0.1"
|
||||
resolved "https://registry.yarnpkg.com/arrify/-/arrify-2.0.1.tgz#c9655e9331e0abcd588d2a7cad7e9956f66701fa"
|
||||
|
@ -3947,6 +3957,16 @@ callsites@^3.0.0:
|
|||
resolved "https://registry.yarnpkg.com/callsites/-/callsites-3.1.0.tgz#b3630abd8943432f54b3f0519238e33cd7df2f73"
|
||||
integrity sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ==
|
||||
|
||||
camelcase-keys@^7.0.0:
|
||||
version "7.0.2"
|
||||
resolved "https://registry.yarnpkg.com/camelcase-keys/-/camelcase-keys-7.0.2.tgz#d048d8c69448745bb0de6fc4c1c52a30dfbe7252"
|
||||
integrity sha512-Rjs1H+A9R+Ig+4E/9oyB66UC5Mj9Xq3N//vcLf2WzgdTi/3gUu3Z9KoqmlrEG4VuuLK8wJHofxzdQXz/knhiYg==
|
||||
dependencies:
|
||||
camelcase "^6.3.0"
|
||||
map-obj "^4.1.0"
|
||||
quick-lru "^5.1.1"
|
||||
type-fest "^1.2.1"
|
||||
|
||||
camelcase@^5.0.0, camelcase@^5.3.1:
|
||||
version "5.3.1"
|
||||
resolved "https://registry.yarnpkg.com/camelcase/-/camelcase-5.3.1.tgz#e3c9b31569e106811df242f715725a1f4c494320"
|
||||
|
@ -3957,6 +3977,11 @@ camelcase@^6.0.0, camelcase@^6.2.0:
|
|||
resolved "https://registry.yarnpkg.com/camelcase/-/camelcase-6.2.1.tgz#250fd350cfd555d0d2160b1d51510eaf8326e86e"
|
||||
integrity sha512-tVI4q5jjFV5CavAU8DXfza/TJcZutVKo/5Foskmsqcm0MsL91moHvwiGNnqaa2o6PF/7yT5ikDRcVcl8Rj6LCA==
|
||||
|
||||
camelcase@^6.3.0:
|
||||
version "6.3.0"
|
||||
resolved "https://registry.yarnpkg.com/camelcase/-/camelcase-6.3.0.tgz#5685b95eb209ac9c0c177467778c9c84df58ba9a"
|
||||
integrity sha512-Gmy6FhYlCY7uOElZUSbxo2UCDH8owEk996gkbrpsgGtrJLM3J7jGxl9Ic7Qwwj4ivOE5AWZWRMecDdF7hqGjFA==
|
||||
|
||||
caniuse-api@^3.0.0:
|
||||
version "3.0.0"
|
||||
resolved "https://registry.yarnpkg.com/caniuse-api/-/caniuse-api-3.0.0.tgz#5e4d90e2274961d46291997df599e3ed008ee4c0"
|
||||
|
@ -4943,11 +4968,24 @@ decache@^4.6.0:
|
|||
dependencies:
|
||||
callsite "^1.0.0"
|
||||
|
||||
decamelize@^1.2.0:
|
||||
decamelize-keys@^1.1.0:
|
||||
version "1.1.0"
|
||||
resolved "https://registry.yarnpkg.com/decamelize-keys/-/decamelize-keys-1.1.0.tgz#d171a87933252807eb3cb61dc1c1445d078df2d9"
|
||||
integrity sha1-0XGoeTMlKAfrPLYdwcFEXQeN8tk=
|
||||
dependencies:
|
||||
decamelize "^1.1.0"
|
||||
map-obj "^1.0.0"
|
||||
|
||||
decamelize@^1.1.0, decamelize@^1.2.0:
|
||||
version "1.2.0"
|
||||
resolved "https://registry.yarnpkg.com/decamelize/-/decamelize-1.2.0.tgz#f6534d15148269b20352e7bee26f501f9a191290"
|
||||
integrity sha1-9lNNFRSCabIDUue+4m9QH5oZEpA=
|
||||
|
||||
decamelize@^5.0.0:
|
||||
version "5.0.1"
|
||||
resolved "https://registry.yarnpkg.com/decamelize/-/decamelize-5.0.1.tgz#db11a92e58c741ef339fb0a2868d8a06a9a7b1e9"
|
||||
integrity sha512-VfxadyCECXgQlkoEAjeghAr5gY3Hf+IKjKb+X8tGVDtveCjN+USwprd2q3QXBR9T1+x2DG0XZF5/w+7HAtSaXA==
|
||||
|
||||
decode-uri-component@^0.2.0:
|
||||
version "0.2.0"
|
||||
resolved "https://registry.yarnpkg.com/decode-uri-component/-/decode-uri-component-0.2.0.tgz#eb3913333458775cb84cd1a1fae062106bb87545"
|
||||
|
@ -5105,6 +5143,14 @@ define-property@^2.0.2:
|
|||
is-descriptor "^1.0.2"
|
||||
isobject "^3.0.1"
|
||||
|
||||
del-cli@^4.0.1:
|
||||
version "4.0.1"
|
||||
resolved "https://registry.yarnpkg.com/del-cli/-/del-cli-4.0.1.tgz#2303ccaa45708ee8c6211568344cf87336abf30a"
|
||||
integrity sha512-KtR/6cBfZkGDAP2NA7z+bP4p1OMob3wjN9mq13+SWvExx6jT9gFWfLgXEeX8J2B47OKeNCq9yTONmtryQ+m+6g==
|
||||
dependencies:
|
||||
del "^6.0.0"
|
||||
meow "^10.1.0"
|
||||
|
||||
del@^5.1.0:
|
||||
version "5.1.0"
|
||||
resolved "https://registry.yarnpkg.com/del/-/del-5.1.0.tgz#d9487c94e367410e6eff2925ee58c0c84a75b3a7"
|
||||
|
@ -6916,6 +6962,11 @@ har-validator@~5.1.3:
|
|||
ajv "^6.12.3"
|
||||
har-schema "^2.0.0"
|
||||
|
||||
hard-rejection@^2.1.0:
|
||||
version "2.1.0"
|
||||
resolved "https://registry.yarnpkg.com/hard-rejection/-/hard-rejection-2.1.0.tgz#1c6eda5c1685c63942766d79bb40ae773cecd883"
|
||||
integrity sha512-VIZB+ibDhx7ObhAe7OVtoEbuP4h/MuOTHJ+J8h/eBXotJYl0fBgR72xDFCKgIh22OJZIOVNxBMWuhAr10r8HdA==
|
||||
|
||||
has-ansi@^0.1.0:
|
||||
version "0.1.0"
|
||||
resolved "https://registry.yarnpkg.com/has-ansi/-/has-ansi-0.1.0.tgz#84f265aae8c0e6a88a12d7022894b7568894c62e"
|
||||
|
@ -7102,6 +7153,13 @@ hosted-git-info@^2.1.4:
|
|||
resolved "https://registry.yarnpkg.com/hosted-git-info/-/hosted-git-info-2.8.9.tgz#dffc0bf9a21c02209090f2aa69429e1414daf3f9"
|
||||
integrity sha512-mxIDAb9Lsm6DoOJ7xH+5+X4y1LU/4Hi50L9C5sIswK3JzULS4bwk1FvjdBgvYR4bzT4tuUQiC15FE2f5HbLvYw==
|
||||
|
||||
hosted-git-info@^4.0.1:
|
||||
version "4.1.0"
|
||||
resolved "https://registry.yarnpkg.com/hosted-git-info/-/hosted-git-info-4.1.0.tgz#827b82867e9ff1c8d0c4d9d53880397d2c86d224"
|
||||
integrity sha512-kyCuEOWjJqZuDbRHzL8V93NzQhwIB71oFWSyzVo+KPZI+pnQPPxucdkrOZvkLRnrf5URsQM+IJ09Dw29cRALIA==
|
||||
dependencies:
|
||||
lru-cache "^6.0.0"
|
||||
|
||||
hpack.js@^2.1.6:
|
||||
version "2.1.6"
|
||||
resolved "https://registry.yarnpkg.com/hpack.js/-/hpack.js-2.1.6.tgz#87774c0949e513f42e84575b3c45681fade2a0b2"
|
||||
|
@ -7340,6 +7398,11 @@ indent-string@^4.0.0:
|
|||
resolved "https://registry.yarnpkg.com/indent-string/-/indent-string-4.0.0.tgz#624f8f4497d619b2d9768531d58f4122854d7251"
|
||||
integrity sha512-EdDDZu4A2OyIK7Lr/2zG+w5jmbuk1DVBnEwREQvBzspBJkCEbRa8GxU1lghYcaGJCnRWibjDXlq779X1/y5xwg==
|
||||
|
||||
indent-string@^5.0.0:
|
||||
version "5.0.0"
|
||||
resolved "https://registry.yarnpkg.com/indent-string/-/indent-string-5.0.0.tgz#4fd2980fccaf8622d14c64d694f4cf33c81951a5"
|
||||
integrity sha512-m6FAo/spmsW2Ab2fU35JTYwtOKa2yAwXSwgjSv1TJzh4Mh7mC3lzAOVLBprb72XsTrgkEIsl7YrFNAiDiRhIGg==
|
||||
|
||||
indexes-of@^1.0.1:
|
||||
version "1.0.1"
|
||||
resolved "https://registry.yarnpkg.com/indexes-of/-/indexes-of-1.0.1.tgz#f30f716c8e2bd346c7b67d3df3915566a7c05607"
|
||||
|
@ -7571,6 +7634,13 @@ is-core-module@^2.2.0:
|
|||
dependencies:
|
||||
has "^1.0.3"
|
||||
|
||||
is-core-module@^2.5.0:
|
||||
version "2.8.1"
|
||||
resolved "https://registry.yarnpkg.com/is-core-module/-/is-core-module-2.8.1.tgz#f59fdfca701d5879d0a6b100a40aa1560ce27211"
|
||||
integrity sha512-SdNCUs284hr40hFTFP6l0IfZ/RSrMXF3qgoRHd3/79unUTvrFO/JoXwkGm+5J/Oe3E/b5GsnG330uUNgRpu1PA==
|
||||
dependencies:
|
||||
has "^1.0.3"
|
||||
|
||||
is-data-descriptor@^0.1.4:
|
||||
version "0.1.4"
|
||||
resolved "https://registry.yarnpkg.com/is-data-descriptor/-/is-data-descriptor-0.1.4.tgz#0b5ee648388e2c860282e793f1856fec3f301b56"
|
||||
|
@ -7752,7 +7822,7 @@ is-path-inside@^3.0.1, is-path-inside@^3.0.2:
|
|||
resolved "https://registry.yarnpkg.com/is-path-inside/-/is-path-inside-3.0.3.tgz#d231362e53a07ff2b0e0ea7fed049161ffd16283"
|
||||
integrity sha512-Fd4gABb+ycGAmKou8eMftCupSir5lRxqf4aD/vd0cD2qc4HL07OjCeuHMr8Ro4CoMaeCKDB0/ECBOVWjTwUvPQ==
|
||||
|
||||
is-plain-obj@^1.0.0:
|
||||
is-plain-obj@^1.0.0, is-plain-obj@^1.1.0:
|
||||
version "1.1.0"
|
||||
resolved "https://registry.yarnpkg.com/is-plain-obj/-/is-plain-obj-1.1.0.tgz#71a50c8429dfca773c92a390a4a03b39fcd51d3e"
|
||||
integrity sha1-caUMhCnfync8kqOQpKA7OfzVHT4=
|
||||
|
@ -8161,7 +8231,7 @@ kind-of@^5.0.0:
|
|||
resolved "https://registry.yarnpkg.com/kind-of/-/kind-of-5.1.0.tgz#729c91e2d857b7a419a1f9aa65685c4c33f5845d"
|
||||
integrity sha512-NGEErnH6F2vUuXDh+OlbcKW7/wOcfdRHaZ7VWtqCztfHri/++YKmP51OdWeGPuqCOba6kk2OTe5d02VmTB80Pw==
|
||||
|
||||
kind-of@^6.0.0, kind-of@^6.0.2:
|
||||
kind-of@^6.0.0, kind-of@^6.0.2, kind-of@^6.0.3:
|
||||
version "6.0.3"
|
||||
resolved "https://registry.yarnpkg.com/kind-of/-/kind-of-6.0.3.tgz#07c05034a6c349fa06e24fa35aa76db4580ce4dd"
|
||||
integrity sha512-dcS1ul+9tmeD95T+x28/ehLgd9mENa3LsvDTtzm3vyBEO7RPptvAD+t44WVXaUjTBRcrpFeFlC8WCruUR456hw==
|
||||
|
@ -8553,6 +8623,11 @@ map-cache@^0.2.2:
|
|||
resolved "https://registry.yarnpkg.com/map-cache/-/map-cache-0.2.2.tgz#c32abd0bd6525d9b051645bb4f26ac5dc98a0dbf"
|
||||
integrity sha1-wyq9C9ZSXZsFFkW7TyasXcmKDb8=
|
||||
|
||||
map-obj@^1.0.0:
|
||||
version "1.0.1"
|
||||
resolved "https://registry.yarnpkg.com/map-obj/-/map-obj-1.0.1.tgz#d933ceb9205d82bdcf4886f6742bdc2b4dea146d"
|
||||
integrity sha1-2TPOuSBdgr3PSIb2dCvcK03qFG0=
|
||||
|
||||
map-obj@^4.0.0, map-obj@^4.1.0, map-obj@^4.2.1:
|
||||
version "4.3.0"
|
||||
resolved "https://registry.yarnpkg.com/map-obj/-/map-obj-4.3.0.tgz#9304f906e93faae70880da102a9f1df0ea8bb05a"
|
||||
|
@ -8681,6 +8756,24 @@ memory-fs@^0.5.0:
|
|||
errno "^0.1.3"
|
||||
readable-stream "^2.0.1"
|
||||
|
||||
meow@^10.1.0:
|
||||
version "10.1.2"
|
||||
resolved "https://registry.yarnpkg.com/meow/-/meow-10.1.2.tgz#62951cb69afa69594142c8250806bc30a3912e4d"
|
||||
integrity sha512-zbuAlN+V/sXlbGchNS9WTWjUzeamwMt/BApKCJi7B0QyZstZaMx0n4Unll/fg0njGtMdC9UP5SAscvOCLYdM+Q==
|
||||
dependencies:
|
||||
"@types/minimist" "^1.2.2"
|
||||
camelcase-keys "^7.0.0"
|
||||
decamelize "^5.0.0"
|
||||
decamelize-keys "^1.1.0"
|
||||
hard-rejection "^2.1.0"
|
||||
minimist-options "4.1.0"
|
||||
normalize-package-data "^3.0.2"
|
||||
read-pkg-up "^8.0.0"
|
||||
redent "^4.0.0"
|
||||
trim-newlines "^4.0.2"
|
||||
type-fest "^1.2.2"
|
||||
yargs-parser "^20.2.9"
|
||||
|
||||
merge-descriptors@1.0.1:
|
||||
version "1.0.1"
|
||||
resolved "https://registry.yarnpkg.com/merge-descriptors/-/merge-descriptors-1.0.1.tgz#b00aaa556dd8b44568150ec9d1b953f3f90cbb61"
|
||||
|
@ -8800,6 +8893,11 @@ mimic-response@^2.0.0, mimic-response@^2.1.0:
|
|||
resolved "https://registry.yarnpkg.com/mimic-response/-/mimic-response-2.1.0.tgz#d13763d35f613d09ec37ebb30bac0469c0ee8f43"
|
||||
integrity sha512-wXqjST+SLt7R009ySCglWBCFpjUygmCIfD790/kVbiGmUgfYGuB14PiTd5DwVxSV4NcYHjzMkoj5LjQZwTQLEA==
|
||||
|
||||
min-indent@^1.0.1:
|
||||
version "1.0.1"
|
||||
resolved "https://registry.yarnpkg.com/min-indent/-/min-indent-1.0.1.tgz#a63f681673b30571fbe8bc25686ae746eefa9869"
|
||||
integrity sha512-I9jwMn07Sy/IwOj3zVkVik2JTvgpaykDZEigL6Rx6N9LbMywwUSMtxET+7lVoDLLd3O3IXwJwvuuns8UB/HeAg==
|
||||
|
||||
mini-css-extract-plugin@^0.9.0:
|
||||
version "0.9.0"
|
||||
resolved "https://registry.yarnpkg.com/mini-css-extract-plugin/-/mini-css-extract-plugin-0.9.0.tgz#47f2cf07aa165ab35733b1fc97d4c46c0564339e"
|
||||
|
@ -8827,6 +8925,15 @@ minimatch@^3.0.4:
|
|||
dependencies:
|
||||
brace-expansion "^1.1.7"
|
||||
|
||||
minimist-options@4.1.0:
|
||||
version "4.1.0"
|
||||
resolved "https://registry.yarnpkg.com/minimist-options/-/minimist-options-4.1.0.tgz#c0655713c53a8a2ebd77ffa247d342c40f010619"
|
||||
integrity sha512-Q4r8ghd80yhO/0j1O3B2BjweX3fiHg9cdOwjJd2J76Q135c+NDxGCqdYKQ1SKBuFfgWbAUzBfvYjPUEeNgqN1A==
|
||||
dependencies:
|
||||
arrify "^1.0.1"
|
||||
is-plain-obj "^1.1.0"
|
||||
kind-of "^6.0.3"
|
||||
|
||||
minimist@^1.2.0, minimist@^1.2.5:
|
||||
version "1.2.5"
|
||||
resolved "https://registry.yarnpkg.com/minimist/-/minimist-1.2.5.tgz#67d66014b66a6a8aaa0c083c5fd58df4e4e97602"
|
||||
|
@ -9361,6 +9468,16 @@ normalize-package-data@^2.5.0:
|
|||
semver "2 || 3 || 4 || 5"
|
||||
validate-npm-package-license "^3.0.1"
|
||||
|
||||
normalize-package-data@^3.0.2:
|
||||
version "3.0.3"
|
||||
resolved "https://registry.yarnpkg.com/normalize-package-data/-/normalize-package-data-3.0.3.tgz#dbcc3e2da59509a0983422884cd172eefdfa525e"
|
||||
integrity sha512-p2W1sgqij3zMMyRC067Dg16bfzVH+w7hyegmpIvZ4JNjqtGOVAIvLmjBx3yP7YTe9vKJgkoNOPjwQGogDoMXFA==
|
||||
dependencies:
|
||||
hosted-git-info "^4.0.1"
|
||||
is-core-module "^2.5.0"
|
||||
semver "^7.3.4"
|
||||
validate-npm-package-license "^3.0.1"
|
||||
|
||||
normalize-path@^2.1.1:
|
||||
version "2.1.1"
|
||||
resolved "https://registry.yarnpkg.com/normalize-path/-/normalize-path-2.1.1.tgz#1ab28b556e198363a8c1a6f7e6fa20137fe6aed9"
|
||||
|
@ -9957,7 +10074,7 @@ parse-json@^4.0.0:
|
|||
error-ex "^1.3.1"
|
||||
json-parse-better-errors "^1.0.1"
|
||||
|
||||
parse-json@^5.0.0:
|
||||
parse-json@^5.0.0, parse-json@^5.2.0:
|
||||
version "5.2.0"
|
||||
resolved "https://registry.yarnpkg.com/parse-json/-/parse-json-5.2.0.tgz#c76fc66dee54231c962b22bcc8a72cf2f99753cd"
|
||||
integrity sha512-ayCKvm/phCGxOkYRSCM82iDwct8/EonSEgCSxWxD7ve6jHggsFl4fZVQBPRNgQoKiuV/odhFrGzQXZwbifC8Rg==
|
||||
|
@ -11095,6 +11212,11 @@ queue-microtask@^1.2.2:
|
|||
resolved "https://registry.yarnpkg.com/queue-microtask/-/queue-microtask-1.2.3.tgz#4929228bbc724dfac43e0efb058caf7b6cfb6243"
|
||||
integrity sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A==
|
||||
|
||||
quick-lru@^5.1.1:
|
||||
version "5.1.1"
|
||||
resolved "https://registry.yarnpkg.com/quick-lru/-/quick-lru-5.1.1.tgz#366493e6b3e42a3a6885e2e99d18f80fb7a8c932"
|
||||
integrity sha512-WuyALRjWPDGtt/wzJiadO5AXY+8hZ80hVpe6MyivgraREW751X3SbhRvG3eLKOYN+8VEvqLcf3wdnt44Z4S4SA==
|
||||
|
||||
quickselect@^2.0.0:
|
||||
version "2.0.0"
|
||||
resolved "https://registry.yarnpkg.com/quickselect/-/quickselect-2.0.0.tgz#f19680a486a5eefb581303e023e98faaf25dd018"
|
||||
|
@ -11241,6 +11363,15 @@ read-pkg-up@^7.0.1:
|
|||
read-pkg "^5.2.0"
|
||||
type-fest "^0.8.1"
|
||||
|
||||
read-pkg-up@^8.0.0:
|
||||
version "8.0.0"
|
||||
resolved "https://registry.yarnpkg.com/read-pkg-up/-/read-pkg-up-8.0.0.tgz#72f595b65e66110f43b052dd9af4de6b10534670"
|
||||
integrity sha512-snVCqPczksT0HS2EC+SxUndvSzn6LRCwpfSvLrIfR5BKDQQZMaI6jPRC9dYvYFDRAuFEAnkwww8kBBNE/3VvzQ==
|
||||
dependencies:
|
||||
find-up "^5.0.0"
|
||||
read-pkg "^6.0.0"
|
||||
type-fest "^1.0.1"
|
||||
|
||||
read-pkg@^5.2.0:
|
||||
version "5.2.0"
|
||||
resolved "https://registry.yarnpkg.com/read-pkg/-/read-pkg-5.2.0.tgz#7bf295438ca5a33e56cd30e053b34ee7250c93cc"
|
||||
|
@ -11251,6 +11382,16 @@ read-pkg@^5.2.0:
|
|||
parse-json "^5.0.0"
|
||||
type-fest "^0.6.0"
|
||||
|
||||
read-pkg@^6.0.0:
|
||||
version "6.0.0"
|
||||
resolved "https://registry.yarnpkg.com/read-pkg/-/read-pkg-6.0.0.tgz#a67a7d6a1c2b0c3cd6aa2ea521f40c458a4a504c"
|
||||
integrity sha512-X1Fu3dPuk/8ZLsMhEj5f4wFAF0DWoK7qhGJvgaijocXxBmSToKfbFtqbxMO7bVjNA1dmE5huAzjXj/ey86iw9Q==
|
||||
dependencies:
|
||||
"@types/normalize-package-data" "^2.4.0"
|
||||
normalize-package-data "^3.0.2"
|
||||
parse-json "^5.2.0"
|
||||
type-fest "^1.0.1"
|
||||
|
||||
"readable-stream@1 || 2", readable-stream@^2.0.0, readable-stream@^2.0.1, readable-stream@^2.0.2, readable-stream@^2.0.5, readable-stream@^2.0.6, readable-stream@^2.1.5, readable-stream@^2.2.2, readable-stream@^2.3.0, readable-stream@^2.3.3, readable-stream@^2.3.5, readable-stream@^2.3.6, readable-stream@~2.3.6:
|
||||
version "2.3.7"
|
||||
resolved "https://registry.yarnpkg.com/readable-stream/-/readable-stream-2.3.7.tgz#1eca1cf711aef814c04f62252a36a62f6cb23b57"
|
||||
|
@ -11296,6 +11437,14 @@ readdirp@^3.4.0, readdirp@~3.6.0:
|
|||
dependencies:
|
||||
picomatch "^2.2.1"
|
||||
|
||||
redent@^4.0.0:
|
||||
version "4.0.0"
|
||||
resolved "https://registry.yarnpkg.com/redent/-/redent-4.0.0.tgz#0c0ba7caabb24257ab3bb7a4fd95dd1d5c5681f9"
|
||||
integrity sha512-tYkDkVVtYkSVhuQ4zBgfvciymHaeuel+zFKXShfDnFP5SyVEP7qo70Rf1jTOTCx3vGNAbnEi/xFkcfQVMIBWag==
|
||||
dependencies:
|
||||
indent-string "^5.0.0"
|
||||
strip-indent "^4.0.0"
|
||||
|
||||
regenerate-unicode-properties@^9.0.0:
|
||||
version "9.0.0"
|
||||
resolved "https://registry.yarnpkg.com/regenerate-unicode-properties/-/regenerate-unicode-properties-9.0.0.tgz#54d09c7115e1f53dc2314a974b32c1c344efe326"
|
||||
|
@ -12433,6 +12582,13 @@ strip-final-newline@^2.0.0:
|
|||
resolved "https://registry.yarnpkg.com/strip-final-newline/-/strip-final-newline-2.0.0.tgz#89b852fb2fcbe936f6f4b3187afb0a12c1ab58ad"
|
||||
integrity sha512-BrpvfNAE3dcvq7ll3xVumzjKjZQ5tI1sEUIKr3Uoks0XUl45St3FlatVqef9prk4jRDzhW6WZg+3bk93y6pLjA==
|
||||
|
||||
strip-indent@^4.0.0:
|
||||
version "4.0.0"
|
||||
resolved "https://registry.yarnpkg.com/strip-indent/-/strip-indent-4.0.0.tgz#b41379433dd06f5eae805e21d631e07ee670d853"
|
||||
integrity sha512-mnVSV2l+Zv6BLpSD/8V87CW/y9EmmbYzGCIavsnsI6/nwn26DwffM/yztm30Z/I2DY9wdS3vXVCMnHDgZaVNoA==
|
||||
dependencies:
|
||||
min-indent "^1.0.1"
|
||||
|
||||
strip-json-comments@^3.1.0, strip-json-comments@^3.1.1:
|
||||
version "3.1.1"
|
||||
resolved "https://registry.yarnpkg.com/strip-json-comments/-/strip-json-comments-3.1.1.tgz#31f1281b3832630434831c310c01cccda8cbe006"
|
||||
|
@ -12895,6 +13051,11 @@ traverse@~0.6.6:
|
|||
resolved "https://registry.yarnpkg.com/traverse/-/traverse-0.6.6.tgz#cbdf560fd7b9af632502fed40f918c157ea97137"
|
||||
integrity sha1-y99WD9e5r2MlAv7UD5GMFX6pcTc=
|
||||
|
||||
trim-newlines@^4.0.2:
|
||||
version "4.0.2"
|
||||
resolved "https://registry.yarnpkg.com/trim-newlines/-/trim-newlines-4.0.2.tgz#d6aaaf6a0df1b4b536d183879a6b939489808c7c"
|
||||
integrity sha512-GJtWyq9InR/2HRiLZgpIKv+ufIKrVrvjQWEj7PxAXNc5dwbNJkqhAUoAGgzRmULAnoOM5EIpveYd3J2VeSAIew==
|
||||
|
||||
trim-repeated@^1.0.0:
|
||||
version "1.0.0"
|
||||
resolved "https://registry.yarnpkg.com/trim-repeated/-/trim-repeated-1.0.0.tgz#e3646a2ea4e891312bf7eace6cfb05380bc01c21"
|
||||
|
@ -13018,6 +13179,11 @@ type-fest@^0.8.0, type-fest@^0.8.1:
|
|||
resolved "https://registry.yarnpkg.com/type-fest/-/type-fest-0.8.1.tgz#09e249ebde851d3b1e48d27c105444667f17b83d"
|
||||
integrity sha512-4dbzIzqvjtgiM5rw1k5rEHtBANKmdudhGyBEajN01fEyhaAIhsoKNy6y7+IN93IfpFtwY9iqi7kD+xwKhQsNJA==
|
||||
|
||||
type-fest@^1.0.1, type-fest@^1.2.1, type-fest@^1.2.2:
|
||||
version "1.4.0"
|
||||
resolved "https://registry.yarnpkg.com/type-fest/-/type-fest-1.4.0.tgz#e9fb813fe3bf1744ec359d55d1affefa76f14be1"
|
||||
integrity sha512-yGSza74xk0UG8k+pLh5oeoYirvIiWo5t0/o3zHHAO2tRDiZcxWP7fywNlXhqb6/r6sWvwi+RsyQMWhVLe4BVuA==
|
||||
|
||||
type-is@~1.6.18:
|
||||
version "1.6.18"
|
||||
resolved "https://registry.yarnpkg.com/type-is/-/type-is-1.6.18.tgz#4e552cd05df09467dcbc4ef739de89f2cf37c131"
|
||||
|
@ -13772,7 +13938,7 @@ yargs-parser@^18.1.2:
|
|||
camelcase "^5.0.0"
|
||||
decamelize "^1.2.0"
|
||||
|
||||
yargs-parser@^20.2.2:
|
||||
yargs-parser@^20.2.2, yargs-parser@^20.2.9:
|
||||
version "20.2.9"
|
||||
resolved "https://registry.yarnpkg.com/yargs-parser/-/yargs-parser-20.2.9.tgz#2eb7dc3b0289718fc295f362753845c41a0c94ee"
|
||||
integrity sha512-y11nGElTIV+CT3Zv9t7VKl+Q3hTQoT9a1Qzezhhl6Rp21gJ/IVTW7Z3y9EWXhuUBC2Shnf+DX0antecpAwSP8w==
|
||||
|
|
Loading…
Reference in a new issue