commit
1f5ea207b2
34 changed files with 421 additions and 99 deletions
|
@ -28,4 +28,12 @@
|
||||||
&.wrap {
|
&.wrap {
|
||||||
flex-wrap: wrap;
|
flex-wrap: wrap;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
&.column {
|
||||||
|
flex-direction: column;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.flex-grow {
|
||||||
|
flex-grow: 1;
|
||||||
}
|
}
|
||||||
|
|
|
@ -111,6 +111,10 @@
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.add-row {
|
||||||
|
margin-bottom: 2 * $default-padding;
|
||||||
|
}
|
||||||
|
|
||||||
input[type=checkbox] {
|
input[type=checkbox] {
|
||||||
&.small-margin {
|
&.small-margin {
|
||||||
margin-bottom: $default-padding / 2;
|
margin-bottom: $default-padding / 2;
|
||||||
|
@ -246,6 +250,15 @@
|
||||||
.geo-areas {
|
.geo-areas {
|
||||||
margin-bottom: 2 * $default-padding;
|
margin-bottom: 2 * $default-padding;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
&.editable-champ-repetition {
|
||||||
|
.row {
|
||||||
|
border-radius: 4px;
|
||||||
|
border: 1px solid $border-grey;
|
||||||
|
padding: $default-padding;
|
||||||
|
margin-bottom: 2 * $default-padding;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
input.aa-input,
|
input.aa-input,
|
||||||
|
|
|
@ -9,10 +9,6 @@
|
||||||
}
|
}
|
||||||
|
|
||||||
.draggable-item {
|
.draggable-item {
|
||||||
display: flex;
|
|
||||||
flex-direction: column;
|
|
||||||
justify-content: flex-start;
|
|
||||||
|
|
||||||
border: 1px solid $border-grey;
|
border: 1px solid $border-grey;
|
||||||
border-radius: 5px;
|
border-radius: 5px;
|
||||||
margin-bottom: 10px;
|
margin-bottom: 10px;
|
||||||
|
@ -55,20 +51,7 @@
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.column {
|
.flex {
|
||||||
display: flex;
|
|
||||||
justify-content: flex-start;
|
|
||||||
flex-direction: column;
|
|
||||||
|
|
||||||
&.shift-left {
|
|
||||||
margin-left: 35px;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.row {
|
|
||||||
display: flex;
|
|
||||||
justify-content: flex-start;
|
|
||||||
|
|
||||||
&.section {
|
&.section {
|
||||||
padding: 10px 10px 0 10px;
|
padding: 10px 10px 0 10px;
|
||||||
margin-bottom: 8px;
|
margin-bottom: 8px;
|
||||||
|
|
|
@ -20,6 +20,10 @@
|
||||||
padding: (3 * $default-spacer) 2px;
|
padding: (3 * $default-spacer) 2px;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
th.padded {
|
||||||
|
padding-left: (2 * $default-spacer);
|
||||||
|
}
|
||||||
|
|
||||||
&.hoverable {
|
&.hoverable {
|
||||||
tbody tr:hover {
|
tbody tr:hover {
|
||||||
background: $light-grey;
|
background: $light-grey;
|
||||||
|
|
21
app/controllers/champs/repetition_controller.rb
Normal file
21
app/controllers/champs/repetition_controller.rb
Normal file
|
@ -0,0 +1,21 @@
|
||||||
|
class Champs::RepetitionController < ApplicationController
|
||||||
|
before_action :authenticate_logged_user!
|
||||||
|
|
||||||
|
def show
|
||||||
|
@champ = Champ
|
||||||
|
.joins(:dossier)
|
||||||
|
.where(dossiers: { user_id: logged_user_ids })
|
||||||
|
.find(params[:champ_id])
|
||||||
|
|
||||||
|
@position = params[:position]
|
||||||
|
row = (@champ.champs.empty? ? 0 : @champ.champs.last.row) + 1
|
||||||
|
|
||||||
|
@champ.add_row(row)
|
||||||
|
|
||||||
|
if @champ.private?
|
||||||
|
@attribute = "dossier[champs_private_attributes][#{@position}][champs_attributes]"
|
||||||
|
else
|
||||||
|
@attribute = "dossier[champs_attributes][#{@position}][champs_attributes]"
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
|
@ -3,7 +3,7 @@ module NewAdministrateur
|
||||||
before_action :retrieve_procedure, only: [:champs, :annotations, :update]
|
before_action :retrieve_procedure, only: [:champs, :annotations, :update]
|
||||||
before_action :procedure_locked?, only: [:champs, :annotations, :update]
|
before_action :procedure_locked?, only: [:champs, :annotations, :update]
|
||||||
|
|
||||||
TYPE_DE_CHAMP_ATTRIBUTES = [
|
TYPE_DE_CHAMP_ATTRIBUTES_BASE = [
|
||||||
:_destroy,
|
:_destroy,
|
||||||
:libelle,
|
:libelle,
|
||||||
:description,
|
:description,
|
||||||
|
@ -18,6 +18,11 @@ module NewAdministrateur
|
||||||
drop_down_list_attributes: [:value]
|
drop_down_list_attributes: [:value]
|
||||||
]
|
]
|
||||||
|
|
||||||
|
TYPE_DE_CHAMP_ATTRIBUTES = TYPE_DE_CHAMP_ATTRIBUTES_BASE.dup
|
||||||
|
TYPE_DE_CHAMP_ATTRIBUTES << {
|
||||||
|
types_de_champ_attributes: TYPE_DE_CHAMP_ATTRIBUTES_BASE
|
||||||
|
}
|
||||||
|
|
||||||
def apercu
|
def apercu
|
||||||
@dossier = procedure_without_control.new_dossier
|
@dossier = procedure_without_control.new_dossier
|
||||||
@tab = apercu_tab
|
@tab = apercu_tab
|
||||||
|
@ -26,9 +31,9 @@ module NewAdministrateur
|
||||||
def update
|
def update
|
||||||
if @procedure.update(procedure_params)
|
if @procedure.update(procedure_params)
|
||||||
flash.now.notice = if params[:procedure][:types_de_champ_attributes].present?
|
flash.now.notice = if params[:procedure][:types_de_champ_attributes].present?
|
||||||
'Champs enregistrés'
|
'Formulaire mis à jour.'
|
||||||
elsif params[:procedure][:types_de_champ_private_attributes].present?
|
elsif params[:procedure][:types_de_champ_private_attributes].present?
|
||||||
'Annotations enregistrés'
|
'Annotations privées mises à jour.'
|
||||||
else
|
else
|
||||||
'Démarche enregistrée.'
|
'Démarche enregistrée.'
|
||||||
end
|
end
|
||||||
|
|
|
@ -282,7 +282,8 @@ module NewUser
|
||||||
params.permit(dossier: {
|
params.permit(dossier: {
|
||||||
champs_attributes: [
|
champs_attributes: [
|
||||||
:id, :value, :primary_value, :secondary_value, :piece_justificative_file, value: [],
|
:id, :value, :primary_value, :secondary_value, :piece_justificative_file, value: [],
|
||||||
etablissement_attributes: Champs::SiretChamp::ETABLISSEMENT_ATTRIBUTES
|
etablissement_attributes: Champs::SiretChamp::ETABLISSEMENT_ATTRIBUTES,
|
||||||
|
champs_attributes: [:id, :_destroy, :value, :primary_value, :secondary_value, :piece_justificative_file, value: []]
|
||||||
]
|
]
|
||||||
})
|
})
|
||||||
end
|
end
|
||||||
|
@ -303,8 +304,7 @@ module NewUser
|
||||||
end
|
end
|
||||||
|
|
||||||
if !save_draft?
|
if !save_draft?
|
||||||
errors += @dossier.champs.select(&:mandatory_and_blank?)
|
errors += @dossier.check_mandatory_champs
|
||||||
.map { |c| "Le champ #{c.libelle.truncate(200)} doit être rempli." }
|
|
||||||
errors += PiecesJustificativesService.missing_pj_error_messages(@dossier)
|
errors += PiecesJustificativesService.missing_pj_error_messages(@dossier)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
|
@ -31,6 +31,13 @@ module ApplicationHelper
|
||||||
# rubocop:enable Rails/OutputSafety
|
# rubocop:enable Rails/OutputSafety
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def append_to_element(selector, partial:, locals: {})
|
||||||
|
html = escape_javascript(render partial: partial, locals: locals)
|
||||||
|
# rubocop:disable Rails/OutputSafety
|
||||||
|
raw("document.querySelector('#{selector}').insertAdjacentHTML('beforeend', \"#{html}\");")
|
||||||
|
# rubocop:enable Rails/OutputSafety
|
||||||
|
end
|
||||||
|
|
||||||
def render_flash(timeout: false, sticky: false, fixed: false)
|
def render_flash(timeout: false, sticky: false, fixed: false)
|
||||||
if flash.any?
|
if flash.any?
|
||||||
html = render_to_element('#flash_messages', partial: 'layouts/flash_messages', locals: { sticky: sticky, fixed: fixed }, outer: true)
|
html = render_to_element('#flash_messages', partial: 'layouts/flash_messages', locals: { sticky: sticky, fixed: fixed }, outer: true)
|
||||||
|
|
|
@ -79,9 +79,16 @@ module ProcedureHelper
|
||||||
types_de_champ
|
types_de_champ
|
||||||
end
|
end
|
||||||
|
|
||||||
|
TYPES_DE_CHAMP_INCLUDE = { drop_down_list: { only: :value } }
|
||||||
|
TYPES_DE_CHAMP_BASE = {
|
||||||
|
except: [:created_at, :updated_at, :stable_id, :type, :parent_id, :procedure_id, :private],
|
||||||
|
methods: [:piece_justificative_template_filename, :piece_justificative_template_url],
|
||||||
|
include: TYPES_DE_CHAMP_INCLUDE
|
||||||
|
}
|
||||||
|
TYPES_DE_CHAMP = TYPES_DE_CHAMP_BASE
|
||||||
|
.merge(include: TYPES_DE_CHAMP_INCLUDE.merge(types_de_champ: TYPES_DE_CHAMP_BASE))
|
||||||
|
|
||||||
def types_de_champ_as_json(types_de_champ)
|
def types_de_champ_as_json(types_de_champ)
|
||||||
types_de_champ.as_json(except: [:created_at, :updated_at],
|
types_de_champ.as_json(TYPES_DE_CHAMP)
|
||||||
methods: [:piece_justificative_template_filename, :piece_justificative_template_url],
|
|
||||||
include: { drop_down_list: { only: :value } })
|
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
export default {
|
export default {
|
||||||
props: ['state', 'update', 'index', 'item', 'prefix'],
|
props: ['state', 'update', 'index', 'item'],
|
||||||
computed: {
|
computed: {
|
||||||
isDirty() {
|
isDirty() {
|
||||||
return (
|
return (
|
||||||
|
@ -56,6 +56,9 @@ export default {
|
||||||
isHeaderSection() {
|
isHeaderSection() {
|
||||||
return this.typeChamp === 'header_section';
|
return this.typeChamp === 'header_section';
|
||||||
},
|
},
|
||||||
|
isRepetition() {
|
||||||
|
return this.typeChamp === 'repetition';
|
||||||
|
},
|
||||||
options() {
|
options() {
|
||||||
const options = this.item.options || {};
|
const options = this.item.options || {};
|
||||||
for (let key of Object.keys(options)) {
|
for (let key of Object.keys(options)) {
|
||||||
|
@ -69,6 +72,21 @@ export default {
|
||||||
} else {
|
} else {
|
||||||
return 'types_de_champ_attributes';
|
return 'types_de_champ_attributes';
|
||||||
}
|
}
|
||||||
|
},
|
||||||
|
typesDeChamp() {
|
||||||
|
return this.item.types_de_champ;
|
||||||
|
},
|
||||||
|
typesDeChampOptions() {
|
||||||
|
return this.state.typesDeChampOptions.filter(
|
||||||
|
([, typeChamp]) => !EXCLUDE_FROM_REPETITION.includes(typeChamp)
|
||||||
|
);
|
||||||
|
},
|
||||||
|
stateForRepetition() {
|
||||||
|
return Object.assign({}, this.state, {
|
||||||
|
typesDeChamp: this.typesDeChamp,
|
||||||
|
typesDeChampOptions: this.typesDeChampOptions,
|
||||||
|
prefix: `${this.state.prefix}[${this.attribute}][${this.index}]`
|
||||||
|
});
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
data() {
|
data() {
|
||||||
|
@ -103,14 +121,30 @@ export default {
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
nameFor(name) {
|
nameFor(name) {
|
||||||
return `${this.prefix}[${this.attribute}][${this.index}][${name}]`;
|
return `${this.state.prefix}[${this.attribute}][${this.index}][${name}]`;
|
||||||
},
|
},
|
||||||
elementIdFor(name) {
|
elementIdFor(name) {
|
||||||
return `${this.prefix}_${this.attribute}_${this.index}_${name}`;
|
const prefix = this.state.prefix.replace(/\[/g, '_').replace(/\]/g, '');
|
||||||
|
return `${prefix}_${this.attribute}_${this.index}_${name}`;
|
||||||
|
},
|
||||||
|
addChamp() {
|
||||||
|
this.typesDeChamp.push({
|
||||||
|
type_champ: 'text',
|
||||||
|
drop_down_list: {},
|
||||||
|
types_de_champ: [],
|
||||||
|
options: {}
|
||||||
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const EXCLUDE_FROM_REPETITION = [
|
||||||
|
'carte',
|
||||||
|
'dossier_link',
|
||||||
|
'repetition',
|
||||||
|
'siret'
|
||||||
|
];
|
||||||
|
|
||||||
const PATHS_TO_WATCH = [
|
const PATHS_TO_WATCH = [
|
||||||
'typeChamp',
|
'typeChamp',
|
||||||
'libelle',
|
'libelle',
|
||||||
|
|
|
@ -4,19 +4,23 @@
|
||||||
<input type="hidden" :name="nameFor('_destroy')" value="true">
|
<input type="hidden" :name="nameFor('_destroy')" value="true">
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="draggable-item" v-else :class="itemClassName">
|
<div class="draggable-item flex column justify-start" v-else :class="itemClassName">
|
||||||
<div class="row section head" :class="{ hr: !isHeaderSection }">
|
<div class="flex justify-start section head" :class="{ hr: !isHeaderSection }">
|
||||||
<div class="handle">
|
<div class="handle">
|
||||||
<img :src="state.dragIconUrl" alt="">
|
<img :src="state.dragIconUrl" alt="">
|
||||||
</div>
|
</div>
|
||||||
<div class="cell">
|
<div class="cell">
|
||||||
<select :name="nameFor('type_champ')" v-model="typeChamp" class="small-margin small inline">
|
<select
|
||||||
<option v-for="option in state.typesDeChampOptions" :key="option[1]" :value="option[1]">
|
:id="elementIdFor('type_champ')"
|
||||||
{{ option[0] }}
|
:name="nameFor('type_champ')"
|
||||||
</option>
|
v-model="typeChamp"
|
||||||
|
class="small-margin small inline">
|
||||||
|
<option v-for="option in state.typesDeChampOptions" :key="option[1]" :value="option[1]">
|
||||||
|
{{ option[0] }}
|
||||||
|
</option>
|
||||||
</select>
|
</select>
|
||||||
</div>
|
</div>
|
||||||
<div class="row delete">
|
<div class="flex justify-start delete">
|
||||||
<div v-if="isDirty" class="error-message">
|
<div v-if="isDirty" class="error-message">
|
||||||
<span v-if="isInvalid" class="content">
|
<span v-if="isInvalid" class="content">
|
||||||
Le libellé doit être rempli.
|
Le libellé doit être rempli.
|
||||||
|
@ -36,8 +40,8 @@
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="row section" :class="{ hr: isDropDown || isFile || isCarte }">
|
<div class="flex justify-start section" :class="{ hr: isDropDown || isFile || isCarte }">
|
||||||
<div class="column shift-left">
|
<div class="flex column justify-start shift-left">
|
||||||
<div class="cell libelle">
|
<div class="cell libelle">
|
||||||
<label :for="elementIdFor('libelle')">
|
<label :for="elementIdFor('libelle')">
|
||||||
Libellé
|
Libellé
|
||||||
|
@ -65,7 +69,7 @@
|
||||||
value="1">
|
value="1">
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="row">
|
<div class="flex justify-start">
|
||||||
<div class="cell" v-show="!isHeaderSection">
|
<div class="cell" v-show="!isHeaderSection">
|
||||||
<label :for="elementIdFor('description')">
|
<label :for="elementIdFor('description')">
|
||||||
Description
|
Description
|
||||||
|
@ -81,7 +85,7 @@
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="row section shift-left" v-show="!isHeaderSection">
|
<div class="flex justify-start section shift-left" v-show="!isHeaderSection">
|
||||||
<div class="cell" v-show="isDropDown">
|
<div class="cell" v-show="isDropDown">
|
||||||
<label :for="elementIdFor('drop_down_list')">
|
<label :for="elementIdFor('drop_down_list')">
|
||||||
Liste déroulante
|
Liste déroulante
|
||||||
|
@ -154,6 +158,26 @@
|
||||||
</label>
|
</label>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
<div class="flex-grow cell" v-show="isRepetition">
|
||||||
|
<Draggable :list="typesDeChamp" :options="{handle:'.handle'}">
|
||||||
|
<DraggableItem
|
||||||
|
v-for="(item, index) in typesDeChamp"
|
||||||
|
:state="stateForRepetition"
|
||||||
|
:update="update"
|
||||||
|
:index="index"
|
||||||
|
:item="item"
|
||||||
|
:key="item.id" />
|
||||||
|
</Draggable>
|
||||||
|
|
||||||
|
<button class="button" @click.prevent="addChamp">
|
||||||
|
<template v-if="state.isAnnotation">
|
||||||
|
Ajouter une annotation
|
||||||
|
</template>
|
||||||
|
<template v-else>
|
||||||
|
Ajouter un champ
|
||||||
|
</template>
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="meta">
|
<div class="meta">
|
||||||
<input type="hidden" :name="nameFor('order_place')" :value="index">
|
<input type="hidden" :name="nameFor('order_place')" :value="index">
|
||||||
|
|
|
@ -5,6 +5,7 @@ export default {
|
||||||
this.state.typesDeChamp.push({
|
this.state.typesDeChamp.push({
|
||||||
type_champ: 'text',
|
type_champ: 'text',
|
||||||
drop_down_list: {},
|
drop_down_list: {},
|
||||||
|
types_de_champ: [],
|
||||||
options: {}
|
options: {}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
|
@ -16,7 +16,6 @@
|
||||||
<Draggable :list="state.typesDeChamp" :options="{handle:'.handle'}">
|
<Draggable :list="state.typesDeChamp" :options="{handle:'.handle'}">
|
||||||
<DraggableItem
|
<DraggableItem
|
||||||
v-for="(item, index) in state.typesDeChamp"
|
v-for="(item, index) in state.typesDeChamp"
|
||||||
prefix="procedure"
|
|
||||||
:state="state"
|
:state="state"
|
||||||
:update="update"
|
:update="update"
|
||||||
:index="index"
|
:index="index"
|
||||||
|
|
|
@ -26,7 +26,8 @@ function initEditor(el) {
|
||||||
isAnnotation: el.dataset.type === 'annotation',
|
isAnnotation: el.dataset.type === 'annotation',
|
||||||
unsavedItems: new Set(),
|
unsavedItems: new Set(),
|
||||||
unsavedInvalidItems: new Set(),
|
unsavedInvalidItems: new Set(),
|
||||||
version: 1
|
version: 1,
|
||||||
|
prefix: 'procedure'
|
||||||
};
|
};
|
||||||
|
|
||||||
new Vue({
|
new Vue({
|
||||||
|
|
24
app/javascript/new_design/champs/repetition.js
Normal file
24
app/javascript/new_design/champs/repetition.js
Normal file
|
@ -0,0 +1,24 @@
|
||||||
|
import { delegate } from '@utils';
|
||||||
|
|
||||||
|
const BUTTON_SELECTOR = '.button.remove-row';
|
||||||
|
const DESTROY_INPUT_SELECTOR = 'input[type=hidden][name*=_destroy]';
|
||||||
|
const CHAMP_SELECTOR = '.editable-champ';
|
||||||
|
|
||||||
|
addEventListener('turbolinks:load', () => {
|
||||||
|
delegate('click', BUTTON_SELECTOR, evt => {
|
||||||
|
evt.preventDefault();
|
||||||
|
|
||||||
|
const row = evt.target.closest('.row');
|
||||||
|
|
||||||
|
for (let input of row.querySelectorAll(DESTROY_INPUT_SELECTOR)) {
|
||||||
|
input.disabled = false;
|
||||||
|
input.value = true;
|
||||||
|
}
|
||||||
|
for (let champ of row.querySelectorAll(CHAMP_SELECTOR)) {
|
||||||
|
champ.remove();
|
||||||
|
}
|
||||||
|
|
||||||
|
evt.target.remove();
|
||||||
|
row.classList.remove('row');
|
||||||
|
});
|
||||||
|
});
|
|
@ -21,6 +21,7 @@ import '../new_design/select2';
|
||||||
|
|
||||||
import '../new_design/champs/carte';
|
import '../new_design/champs/carte';
|
||||||
import '../new_design/champs/linked-drop-down-list';
|
import '../new_design/champs/linked-drop-down-list';
|
||||||
|
import '../new_design/champs/repetition';
|
||||||
|
|
||||||
import '../new_design/administrateur/champs-editor';
|
import '../new_design/administrateur/champs-editor';
|
||||||
|
|
||||||
|
|
|
@ -10,7 +10,7 @@ class Champ < ApplicationRecord
|
||||||
has_many :geo_areas, dependent: :destroy
|
has_many :geo_areas, dependent: :destroy
|
||||||
belongs_to :etablissement, dependent: :destroy
|
belongs_to :etablissement, dependent: :destroy
|
||||||
|
|
||||||
delegate :libelle, :type_champ, :order_place, :mandatory?, :description, :drop_down_list, :exclude_from_export?, :exclude_from_view?, to: :type_de_champ
|
delegate :libelle, :type_champ, :order_place, :mandatory?, :description, :drop_down_list, :exclude_from_export?, :exclude_from_view?, :repetition?, to: :type_de_champ
|
||||||
|
|
||||||
scope :updated_since?, -> (date) { where('champs.updated_at > ?', date) }
|
scope :updated_since?, -> (date) { where('champs.updated_at > ?', date) }
|
||||||
scope :public_only, -> { where(private: false) }
|
scope :public_only, -> { where(private: false) }
|
||||||
|
|
|
@ -9,10 +9,22 @@ class Champs::RepetitionChamp < Champ
|
||||||
champs.group_by(&:row).values
|
champs.group_by(&:row).values
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def add_row(row = 0)
|
||||||
|
type_de_champ.types_de_champ.each do |type_de_champ|
|
||||||
|
self.champs << type_de_champ.champ.build(row: row)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
def mandatory_and_blank?
|
||||||
|
mandatory? && champs.empty?
|
||||||
|
end
|
||||||
|
|
||||||
def search_terms
|
def search_terms
|
||||||
# The user cannot enter any information here so it doesn’t make much sense to search
|
# The user cannot enter any information here so it doesn’t make much sense to search
|
||||||
end
|
end
|
||||||
|
|
||||||
|
private
|
||||||
|
|
||||||
def setup_dossier
|
def setup_dossier
|
||||||
champs.each do |champ|
|
champs.each do |champ|
|
||||||
champ.dossier = dossier
|
champ.dossier = dossier
|
||||||
|
|
|
@ -121,7 +121,13 @@ class Dossier < ApplicationRecord
|
||||||
|
|
||||||
def build_default_champs
|
def build_default_champs
|
||||||
procedure.types_de_champ.each do |type_de_champ|
|
procedure.types_de_champ.each do |type_de_champ|
|
||||||
champs << type_de_champ.champ.build
|
champ = type_de_champ.champ.build
|
||||||
|
|
||||||
|
if type_de_champ.repetition?
|
||||||
|
champ.add_row
|
||||||
|
end
|
||||||
|
|
||||||
|
champs << champ
|
||||||
end
|
end
|
||||||
procedure.types_de_champ_private.each do |type_de_champ|
|
procedure.types_de_champ_private.each do |type_de_champ|
|
||||||
champs_private << type_de_champ.champ.build
|
champs_private << type_de_champ.champ.build
|
||||||
|
@ -334,6 +340,14 @@ class Dossier < ApplicationRecord
|
||||||
log_dossier_operation(gestionnaire, :classer_sans_suite)
|
log_dossier_operation(gestionnaire, :classer_sans_suite)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def check_mandatory_champs
|
||||||
|
(champs + champs.select(&:repetition?).flat_map(&:champs))
|
||||||
|
.select(&:mandatory_and_blank?)
|
||||||
|
.map do |champ|
|
||||||
|
"Le champ #{champ.libelle.truncate(200)} doit être rempli."
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
private
|
private
|
||||||
|
|
||||||
def log_dossier_operation(gestionnaire, operation, automatic_operation: false)
|
def log_dossier_operation(gestionnaire, operation, automatic_operation: false)
|
||||||
|
|
|
@ -35,7 +35,7 @@ class TypeDeChamp < ApplicationRecord
|
||||||
belongs_to :procedure
|
belongs_to :procedure
|
||||||
|
|
||||||
belongs_to :parent, class_name: 'TypeDeChamp'
|
belongs_to :parent, class_name: 'TypeDeChamp'
|
||||||
has_many :types_de_champ, foreign_key: :parent_id, class_name: 'TypeDeChamp', dependent: :destroy
|
has_many :types_de_champ, -> { ordered }, foreign_key: :parent_id, class_name: 'TypeDeChamp', dependent: :destroy
|
||||||
|
|
||||||
store_accessor :options, :cadastres, :quartiers_prioritaires, :parcelles_agricoles, :old_pj
|
store_accessor :options, :cadastres, :quartiers_prioritaires, :parcelles_agricoles, :old_pj
|
||||||
|
|
||||||
|
@ -84,7 +84,7 @@ class TypeDeChamp < ApplicationRecord
|
||||||
has_one_attached :piece_justificative_template
|
has_one_attached :piece_justificative_template
|
||||||
|
|
||||||
accepts_nested_attributes_for :drop_down_list, update_only: true
|
accepts_nested_attributes_for :drop_down_list, update_only: true
|
||||||
accepts_nested_attributes_for :types_de_champ, allow_destroy: true
|
accepts_nested_attributes_for :types_de_champ, reject_if: proc { |attributes| attributes['libelle'].blank? }, allow_destroy: true
|
||||||
|
|
||||||
validates :libelle, presence: true, allow_blank: false, allow_nil: false
|
validates :libelle, presence: true, allow_blank: false, allow_nil: false
|
||||||
validates :type_champ, presence: true, allow_blank: false, allow_nil: false
|
validates :type_champ, presence: true, allow_blank: false, allow_nil: false
|
||||||
|
@ -147,10 +147,11 @@ class TypeDeChamp < ApplicationRecord
|
||||||
end
|
end
|
||||||
|
|
||||||
def exclude_from_view?
|
def exclude_from_view?
|
||||||
type_champ.in?([
|
type_champ == TypeDeChamp.type_champs.fetch(:explication)
|
||||||
TypeDeChamp.type_champs.fetch(:explication),
|
end
|
||||||
TypeDeChamp.type_champs.fetch(:repetition)
|
|
||||||
])
|
def repetition?
|
||||||
|
type_champ == TypeDeChamp.type_champs.fetch(:repetition)
|
||||||
end
|
end
|
||||||
|
|
||||||
def public?
|
def public?
|
||||||
|
|
10
app/views/champs/repetition/_show.html.haml
Normal file
10
app/views/champs/repetition/_show.html.haml
Normal file
|
@ -0,0 +1,10 @@
|
||||||
|
- champs = champ.rows.last
|
||||||
|
- index = (champ.rows.size - 1) * champs.size
|
||||||
|
%div{ class: "row row-#{champs.first.row}" }
|
||||||
|
- champs.each.with_index(index) do |champ, index|
|
||||||
|
= fields_for "#{attribute}[#{index}]", champ do |form|
|
||||||
|
= render partial: "shared/dossiers/editable_champs/editable_champ", locals: { champ: champ, form: form }
|
||||||
|
= form.hidden_field :id
|
||||||
|
= form.hidden_field :_destroy, disabled: true
|
||||||
|
%button.button.danger.remove-row
|
||||||
|
Supprimer
|
3
app/views/champs/repetition/show.js.erb
Normal file
3
app/views/champs/repetition/show.js.erb
Normal file
|
@ -0,0 +1,3 @@
|
||||||
|
<%= append_to_element(".repetition-#{@position}",
|
||||||
|
partial: 'champs/repetition/show',
|
||||||
|
locals: { champ: @champ, attribute: @attribute }) %>
|
|
@ -1,3 +1,3 @@
|
||||||
<%= render_flash timeout: 6000, fixed: true %>
|
<%= render_flash timeout: 6000, fixed: true %>
|
||||||
|
|
||||||
<%= fire_event(:ProcedureUpdated, procedure_data(@procedure)) %>
|
<%= fire_event(:ProcedureUpdated, procedure_data(@procedure.reload)) %>
|
||||||
|
|
40
app/views/shared/dossiers/_champ_row.html.haml
Normal file
40
app/views/shared/dossiers/_champ_row.html.haml
Normal file
|
@ -0,0 +1,40 @@
|
||||||
|
- champs.reject(&:exclude_from_view?).each do |c|
|
||||||
|
- if c.type_champ == TypeDeChamp.type_champs.fetch(:repetition)
|
||||||
|
%tr
|
||||||
|
%th.libelle.repetition{ colspan: 3 }
|
||||||
|
= "#{c.libelle} :"
|
||||||
|
- c.rows.each do |champs|
|
||||||
|
= render partial: "shared/dossiers/champ_row", locals: { champs: champs, demande_seen_at: demande_seen_at, profile: profile, repetition: true }
|
||||||
|
%tr
|
||||||
|
%th{ colspan: 4 }
|
||||||
|
- else
|
||||||
|
%tr
|
||||||
|
- if c.type_champ == TypeDeChamp.type_champs.fetch(:header_section)
|
||||||
|
%th.header-section{ colspan: 3 }
|
||||||
|
= c.libelle
|
||||||
|
- else
|
||||||
|
%th.libelle{ class: repetition ? 'padded' : '' }
|
||||||
|
= "#{c.libelle} :"
|
||||||
|
%td.rich-text
|
||||||
|
%span{ class: highlight_if_unseen_class(demande_seen_at, c.updated_at) }
|
||||||
|
- case c.type_champ
|
||||||
|
- when TypeDeChamp.type_champs.fetch(:carte)
|
||||||
|
= render partial: "shared/champs/carte/show", locals: { champ: c }
|
||||||
|
- when TypeDeChamp.type_champs.fetch(:dossier_link)
|
||||||
|
= render partial: "shared/champs/dossier_link/show", locals: { champ: c }
|
||||||
|
- when TypeDeChamp.type_champs.fetch(:multiple_drop_down_list)
|
||||||
|
= render partial: "shared/champs/multiple_drop_down_list/show", locals: { champ: c }
|
||||||
|
- when TypeDeChamp.type_champs.fetch(:piece_justificative)
|
||||||
|
= render partial: "shared/champs/piece_justificative/show", locals: { champ: c }
|
||||||
|
- when TypeDeChamp.type_champs.fetch(:siret)
|
||||||
|
= render partial: "shared/champs/siret/show", locals: { champ: c, profile: profile }
|
||||||
|
- when TypeDeChamp.type_champs.fetch(:textarea)
|
||||||
|
= render partial: "shared/champs/textarea/show", locals: { champ: c }
|
||||||
|
- else
|
||||||
|
= sanitize(c.to_s)
|
||||||
|
|
||||||
|
- if c.type_champ != TypeDeChamp.type_champs.fetch(:header_section)
|
||||||
|
%td.updated-at
|
||||||
|
%span{ class: highlight_if_unseen_class(demande_seen_at, c.updated_at) }
|
||||||
|
modifié le
|
||||||
|
= c.updated_at.strftime("%d/%m/%Y à %H:%M")
|
|
@ -1,33 +1,3 @@
|
||||||
%table.table.vertical.dossier-champs
|
%table.table.vertical.dossier-champs
|
||||||
%tbody
|
%tbody
|
||||||
- champs.reject(&:exclude_from_view?).each do |c|
|
= render partial: "shared/dossiers/champ_row", locals: { champs: champs, demande_seen_at: demande_seen_at, profile: profile, repetition: false }
|
||||||
%tr
|
|
||||||
- if c.type_champ == TypeDeChamp.type_champs.fetch(:header_section)
|
|
||||||
%th.header-section{ colspan: 3 }
|
|
||||||
= c.libelle
|
|
||||||
- else
|
|
||||||
%th.libelle
|
|
||||||
= "#{c.libelle} :"
|
|
||||||
%td.rich-text
|
|
||||||
%span{ class: highlight_if_unseen_class(demande_seen_at, c.updated_at) }
|
|
||||||
- case c.type_champ
|
|
||||||
- when TypeDeChamp.type_champs.fetch(:carte)
|
|
||||||
= render partial: "shared/champs/carte/show", locals: { champ: c }
|
|
||||||
- when TypeDeChamp.type_champs.fetch(:dossier_link)
|
|
||||||
= render partial: "shared/champs/dossier_link/show", locals: { champ: c }
|
|
||||||
- when TypeDeChamp.type_champs.fetch(:multiple_drop_down_list)
|
|
||||||
= render partial: "shared/champs/multiple_drop_down_list/show", locals: { champ: c }
|
|
||||||
- when TypeDeChamp.type_champs.fetch(:piece_justificative)
|
|
||||||
= render partial: "shared/champs/piece_justificative/show", locals: { champ: c }
|
|
||||||
- when TypeDeChamp.type_champs.fetch(:siret)
|
|
||||||
= render partial: "shared/champs/siret/show", locals: { champ: c, profile: profile }
|
|
||||||
- when TypeDeChamp.type_champs.fetch(:textarea)
|
|
||||||
= render partial: "shared/champs/textarea/show", locals: { champ: c }
|
|
||||||
- else
|
|
||||||
= sanitize(c.to_s)
|
|
||||||
|
|
||||||
- if c.type_champ != TypeDeChamp.type_champs.fetch(:header_section)
|
|
||||||
%td.updated-at
|
|
||||||
%span{ class: highlight_if_unseen_class(demande_seen_at, c.updated_at) }
|
|
||||||
modifié le
|
|
||||||
= c.updated_at.strftime("%d/%m/%Y à %H:%M")
|
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
= form.label champ.main_value_name do
|
= form.label champ.main_value_name, { class: champ.repetition? ? 'header-section' : '' } do
|
||||||
#{champ.libelle}
|
#{champ.libelle}
|
||||||
- if champ.mandatory?
|
- if champ.mandatory?
|
||||||
%span.mandatory *
|
%span.mandatory *
|
||||||
|
|
|
@ -1 +1,15 @@
|
||||||
%h2.repetition-libelle= champ.libelle
|
%div{ class: "repetition-#{form.index}" }
|
||||||
|
- champ.rows.each do |champs|
|
||||||
|
%div{ class: "row row-#{champs.first.row}" }
|
||||||
|
- champs.each do |champ|
|
||||||
|
= form.fields_for :champs, champ do |form|
|
||||||
|
= render partial: 'shared/dossiers/editable_champs/editable_champ', locals: { champ: form.object, form: form }
|
||||||
|
= form.hidden_field :_destroy, disabled: true
|
||||||
|
%button.button.danger.remove-row
|
||||||
|
Supprimer
|
||||||
|
|
||||||
|
- if champ.persisted?
|
||||||
|
= link_to "Ajouter une ligne pour « #{champ.libelle} »", champs_repetition_path(form.index), class: 'button add-row', data: { remote: true, method: 'POST', params: { champ_id: champ&.id }.to_query }
|
||||||
|
- else
|
||||||
|
%button.button.add-row{ disabled: true }
|
||||||
|
= "Ajouter une ligne pour « #{champ.libelle} »"
|
||||||
|
|
|
@ -10,7 +10,7 @@ Flipflop.configure do
|
||||||
feature :champ_integer_number,
|
feature :champ_integer_number,
|
||||||
title: "Champ nombre entier"
|
title: "Champ nombre entier"
|
||||||
feature :champ_repetition,
|
feature :champ_repetition,
|
||||||
title: "Bloc répétable (NE MARCHE PAS – NE PAS ACTIVER)"
|
title: "Bloc répétable"
|
||||||
end
|
end
|
||||||
|
|
||||||
feature :web_hook
|
feature :web_hook
|
||||||
|
|
|
@ -127,6 +127,7 @@ Rails.application.routes.draw do
|
||||||
get ':position/siret', to: 'siret#show', as: :siret
|
get ':position/siret', to: 'siret#show', as: :siret
|
||||||
get ':position/dossier_link', to: 'dossier_link#show', as: :dossier_link
|
get ':position/dossier_link', to: 'dossier_link#show', as: :dossier_link
|
||||||
post ':position/carte', to: 'carte#show', as: :carte
|
post ':position/carte', to: 'carte#show', as: :carte
|
||||||
|
post ':position/repetition', to: 'repetition#show', as: :repetition
|
||||||
end
|
end
|
||||||
|
|
||||||
get 'tour-de-france' => 'root#tour_de_france'
|
get 'tour-de-france' => 'root#tour_de_france'
|
||||||
|
|
|
@ -105,7 +105,7 @@ feature 'As an administrateur I wanna create a new procedure', js: true do
|
||||||
end
|
end
|
||||||
expect(page).to have_selector('#procedure_types_de_champ_attributes_0_libelle')
|
expect(page).to have_selector('#procedure_types_de_champ_attributes_0_libelle')
|
||||||
fill_in 'procedure_types_de_champ_attributes_0_libelle', with: 'libelle de champ'
|
fill_in 'procedure_types_de_champ_attributes_0_libelle', with: 'libelle de champ'
|
||||||
expect(page).to have_content('Champs enregistrés')
|
expect(page).to have_content('Formulaire mis à jour')
|
||||||
|
|
||||||
within '.footer' do
|
within '.footer' do
|
||||||
click_on 'Ajouter un champ'
|
click_on 'Ajouter un champ'
|
||||||
|
@ -135,7 +135,7 @@ feature 'As an administrateur I wanna create a new procedure', js: true do
|
||||||
click_on 'Ajouter un champ'
|
click_on 'Ajouter un champ'
|
||||||
end
|
end
|
||||||
fill_in 'procedure_types_de_champ_attributes_0_libelle', with: 'libelle de champ'
|
fill_in 'procedure_types_de_champ_attributes_0_libelle', with: 'libelle de champ'
|
||||||
expect(page).to have_content('Champs enregistrés')
|
expect(page).to have_content('Formulaire mis à jour')
|
||||||
|
|
||||||
click_on Procedure.last.libelle
|
click_on Procedure.last.libelle
|
||||||
click_on 'onglet-pieces'
|
click_on 'onglet-pieces'
|
||||||
|
|
|
@ -5,6 +5,7 @@ feature 'As an administrateur I edit procedure', js: true do
|
||||||
let(:procedure) { create(:procedure) }
|
let(:procedure) { create(:procedure) }
|
||||||
|
|
||||||
before do
|
before do
|
||||||
|
Flipflop::FeatureSet.current.test!.switch!(:champ_repetition, true)
|
||||||
login_as administrateur, scope: :administrateur
|
login_as administrateur, scope: :administrateur
|
||||||
visit champs_procedure_path(procedure)
|
visit champs_procedure_path(procedure)
|
||||||
end
|
end
|
||||||
|
@ -15,14 +16,14 @@ feature 'As an administrateur I edit procedure', js: true do
|
||||||
end
|
end
|
||||||
expect(page).to have_selector('#procedure_types_de_champ_attributes_0_libelle')
|
expect(page).to have_selector('#procedure_types_de_champ_attributes_0_libelle')
|
||||||
fill_in 'procedure_types_de_champ_attributes_0_libelle', with: 'libellé de champ'
|
fill_in 'procedure_types_de_champ_attributes_0_libelle', with: 'libellé de champ'
|
||||||
expect(page).to have_content('Champs enregistrés')
|
expect(page).to have_content('Formulaire mis à jour')
|
||||||
|
|
||||||
page.refresh
|
page.refresh
|
||||||
within '.footer' do
|
within '.footer' do
|
||||||
click_on 'Enregistrer'
|
click_on 'Enregistrer'
|
||||||
end
|
end
|
||||||
|
|
||||||
expect(page).to have_content('Champs enregistrés')
|
expect(page).to have_content('Formulaire mis à jour')
|
||||||
end
|
end
|
||||||
|
|
||||||
it "Add multiple champs" do
|
it "Add multiple champs" do
|
||||||
|
@ -34,7 +35,7 @@ feature 'As an administrateur I edit procedure', js: true do
|
||||||
end
|
end
|
||||||
expect(page).not_to have_content('Le libellé doit être rempli.')
|
expect(page).not_to have_content('Le libellé doit être rempli.')
|
||||||
expect(page).not_to have_content('Modifications non sauvegardées.')
|
expect(page).not_to have_content('Modifications non sauvegardées.')
|
||||||
expect(page).not_to have_content('Champs enregistrés')
|
expect(page).not_to have_content('Formulaire mis à jour')
|
||||||
fill_in 'procedure_types_de_champ_attributes_0_libelle', with: 'libellé de champ 0'
|
fill_in 'procedure_types_de_champ_attributes_0_libelle', with: 'libellé de champ 0'
|
||||||
|
|
||||||
expect(page).to have_selector('#procedure_types_de_champ_attributes_0_libelle')
|
expect(page).to have_selector('#procedure_types_de_champ_attributes_0_libelle')
|
||||||
|
@ -44,7 +45,7 @@ feature 'As an administrateur I edit procedure', js: true do
|
||||||
|
|
||||||
expect(page).to have_content('Le libellé doit être rempli.')
|
expect(page).to have_content('Le libellé doit être rempli.')
|
||||||
expect(page).to have_content('Modifications non sauvegardées.')
|
expect(page).to have_content('Modifications non sauvegardées.')
|
||||||
expect(page).not_to have_content('Champs enregistrés')
|
expect(page).not_to have_content('Formulaire mis à jour')
|
||||||
fill_in 'procedure_types_de_champ_attributes_2_libelle', with: 'libellé de champ 2'
|
fill_in 'procedure_types_de_champ_attributes_2_libelle', with: 'libellé de champ 2'
|
||||||
|
|
||||||
within '.draggable-item-3' do
|
within '.draggable-item-3' do
|
||||||
|
@ -53,12 +54,12 @@ feature 'As an administrateur I edit procedure', js: true do
|
||||||
|
|
||||||
expect(page).to have_content('Le libellé doit être rempli.')
|
expect(page).to have_content('Le libellé doit être rempli.')
|
||||||
expect(page).to have_content('Modifications non sauvegardées.')
|
expect(page).to have_content('Modifications non sauvegardées.')
|
||||||
expect(page).not_to have_content('Champs enregistrés')
|
expect(page).not_to have_content('Formulaire mis à jour')
|
||||||
fill_in 'procedure_types_de_champ_attributes_1_libelle', with: 'libellé de champ 1'
|
fill_in 'procedure_types_de_champ_attributes_1_libelle', with: 'libellé de champ 1'
|
||||||
|
|
||||||
expect(page).not_to have_content('Le libellé doit être rempli.')
|
expect(page).not_to have_content('Le libellé doit être rempli.')
|
||||||
expect(page).not_to have_content('Modifications non sauvegardées.')
|
expect(page).not_to have_content('Modifications non sauvegardées.')
|
||||||
expect(page).to have_content('Champs enregistrés')
|
expect(page).to have_content('Formulaire mis à jour')
|
||||||
page.refresh
|
page.refresh
|
||||||
|
|
||||||
expect(page).to have_content('Supprimer', count: 3)
|
expect(page).to have_content('Supprimer', count: 3)
|
||||||
|
@ -69,11 +70,11 @@ feature 'As an administrateur I edit procedure', js: true do
|
||||||
click_on 'Ajouter un champ'
|
click_on 'Ajouter un champ'
|
||||||
end
|
end
|
||||||
fill_in 'procedure_types_de_champ_attributes_0_libelle', with: 'libellé de champ'
|
fill_in 'procedure_types_de_champ_attributes_0_libelle', with: 'libellé de champ'
|
||||||
expect(page).to have_content('Champs enregistrés')
|
expect(page).to have_content('Formulaire mis à jour')
|
||||||
page.refresh
|
page.refresh
|
||||||
|
|
||||||
click_on 'Supprimer'
|
click_on 'Supprimer'
|
||||||
expect(page).to have_content('Champs enregistrés')
|
expect(page).to have_content('Formulaire mis à jour')
|
||||||
expect(page).not_to have_content('Supprimer')
|
expect(page).not_to have_content('Supprimer')
|
||||||
page.refresh
|
page.refresh
|
||||||
|
|
||||||
|
@ -87,9 +88,39 @@ feature 'As an administrateur I edit procedure', js: true do
|
||||||
expect(page).to have_selector('#procedure_types_de_champ_attributes_0_description')
|
expect(page).to have_selector('#procedure_types_de_champ_attributes_0_description')
|
||||||
fill_in 'procedure_types_de_champ_attributes_0_description', with: 'déscription du champ'
|
fill_in 'procedure_types_de_champ_attributes_0_description', with: 'déscription du champ'
|
||||||
expect(page).to have_content('Le libellé doit être rempli.')
|
expect(page).to have_content('Le libellé doit être rempli.')
|
||||||
expect(page).not_to have_content('Champs enregistrés')
|
expect(page).not_to have_content('Formulaire mis à jour')
|
||||||
|
|
||||||
fill_in 'procedure_types_de_champ_attributes_0_libelle', with: 'libellé de champ'
|
fill_in 'procedure_types_de_champ_attributes_0_libelle', with: 'libellé de champ'
|
||||||
expect(page).to have_content('Champs enregistrés')
|
expect(page).to have_content('Formulaire mis à jour')
|
||||||
|
end
|
||||||
|
|
||||||
|
it "Add repetition champ" do
|
||||||
|
within '.footer' do
|
||||||
|
click_on 'Ajouter un champ'
|
||||||
|
end
|
||||||
|
expect(page).to have_selector('#procedure_types_de_champ_attributes_0_libelle')
|
||||||
|
select('Bloc répétable', from: 'procedure_types_de_champ_attributes_0_type_champ')
|
||||||
|
fill_in 'procedure_types_de_champ_attributes_0_libelle', with: 'libellé de champ'
|
||||||
|
|
||||||
|
expect(page).to have_content('Formulaire mis à jour')
|
||||||
|
page.refresh
|
||||||
|
|
||||||
|
within '.flex-grow' do
|
||||||
|
click_on 'Ajouter un champ'
|
||||||
|
end
|
||||||
|
|
||||||
|
fill_in 'procedure_types_de_champ_attributes_0_types_de_champ_attributes_0_libelle', with: 'libellé de champ 1'
|
||||||
|
|
||||||
|
expect(page).to have_content('Formulaire mis à jour')
|
||||||
|
expect(page).to have_content('Supprimer', count: 2)
|
||||||
|
|
||||||
|
within '.footer' do
|
||||||
|
click_on 'Ajouter un champ'
|
||||||
|
end
|
||||||
|
|
||||||
|
select('Bloc répétable', from: 'procedure_types_de_champ_attributes_1_type_champ')
|
||||||
|
fill_in 'procedure_types_de_champ_attributes_1_libelle', with: 'libellé de champ 2'
|
||||||
|
|
||||||
|
expect(page).to have_content('Supprimer', count: 3)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
|
@ -83,6 +83,41 @@ feature 'The user' do
|
||||||
expect(page).to have_field('dossier_link', with: '123')
|
expect(page).to have_field('dossier_link', with: '123')
|
||||||
end
|
end
|
||||||
|
|
||||||
|
let(:procedure_with_repetition) do
|
||||||
|
tdc = create(:type_de_champ_repetition, libelle: 'repetition')
|
||||||
|
tdc.types_de_champ << create(:type_de_champ_text, libelle: 'text')
|
||||||
|
create(:procedure, :published, :for_individual, types_de_champ: [tdc])
|
||||||
|
end
|
||||||
|
|
||||||
|
scenario 'fill a dossier with repetition', js: true do
|
||||||
|
log_in(user.email, password, procedure_with_repetition)
|
||||||
|
|
||||||
|
fill_individual
|
||||||
|
|
||||||
|
fill_in('text', with: 'super texte')
|
||||||
|
expect(page).to have_field('text', with: 'super texte')
|
||||||
|
|
||||||
|
click_on 'Ajouter une ligne pour'
|
||||||
|
|
||||||
|
within '.row-1' do
|
||||||
|
fill_in('text', with: 'un autre texte')
|
||||||
|
end
|
||||||
|
|
||||||
|
expect(page).to have_content('Supprimer', count: 2)
|
||||||
|
|
||||||
|
click_on 'Enregistrer le brouillon'
|
||||||
|
|
||||||
|
expect(page).to have_content('Supprimer', count: 2)
|
||||||
|
|
||||||
|
within '.row-1' do
|
||||||
|
click_on 'Supprimer'
|
||||||
|
end
|
||||||
|
|
||||||
|
click_on 'Enregistrer le brouillon'
|
||||||
|
|
||||||
|
expect(page).to have_content('Supprimer', count: 1)
|
||||||
|
end
|
||||||
|
|
||||||
let(:simple_procedure) do
|
let(:simple_procedure) do
|
||||||
tdcs = [create(:type_de_champ, mandatory: true, libelle: 'texte obligatoire')]
|
tdcs = [create(:type_de_champ, mandatory: true, libelle: 'texte obligatoire')]
|
||||||
create(:procedure, :published, :for_individual, types_de_champ: tdcs)
|
create(:procedure, :published, :for_individual, types_de_champ: tdcs)
|
||||||
|
|
|
@ -832,4 +832,63 @@ describe Dossier do
|
||||||
it { expect(dossier.followers_gestionnaires).not_to include(gestionnaire) }
|
it { expect(dossier.followers_gestionnaires).not_to include(gestionnaire) }
|
||||||
it { expect(dossier.dossier_operation_logs.pluck(:gestionnaire_id, :operation, :automatic_operation)).to match([[nil, 'passer_en_instruction', true]]) }
|
it { expect(dossier.dossier_operation_logs.pluck(:gestionnaire_id, :operation, :automatic_operation)).to match([[nil, 'passer_en_instruction', true]]) }
|
||||||
end
|
end
|
||||||
|
|
||||||
|
describe "#check_mandatory_champs" do
|
||||||
|
let(:procedure) { create(:procedure, :with_type_de_champ) }
|
||||||
|
let(:dossier) { create(:dossier, :with_all_champs, procedure: procedure) }
|
||||||
|
|
||||||
|
it 'no mandatory champs' do
|
||||||
|
expect(dossier.check_mandatory_champs).to be_empty
|
||||||
|
end
|
||||||
|
|
||||||
|
context "with mandatory champs" do
|
||||||
|
let(:procedure) { create(:procedure, :with_type_de_champ_mandatory) }
|
||||||
|
let(:champ_with_error) { dossier.champs.first }
|
||||||
|
|
||||||
|
before do
|
||||||
|
champ_with_error.value = nil
|
||||||
|
champ_with_error.save
|
||||||
|
end
|
||||||
|
|
||||||
|
it 'should have errors' do
|
||||||
|
errors = dossier.check_mandatory_champs
|
||||||
|
expect(errors).not_to be_empty
|
||||||
|
expect(errors.first).to eq("Le champ #{champ_with_error.libelle} doit être rempli.")
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
context "with champ repetition" do
|
||||||
|
let(:procedure) { create(:procedure) }
|
||||||
|
let(:type_de_champ_repetition) { create(:type_de_champ_repetition, mandatory: true) }
|
||||||
|
|
||||||
|
before do
|
||||||
|
procedure.types_de_champ << type_de_champ_repetition
|
||||||
|
type_de_champ_repetition.types_de_champ << create(:type_de_champ_text, mandatory: true)
|
||||||
|
end
|
||||||
|
|
||||||
|
context "when no champs" do
|
||||||
|
let(:champ_with_error) { dossier.champs.first }
|
||||||
|
|
||||||
|
it 'should have errors' do
|
||||||
|
errors = dossier.check_mandatory_champs
|
||||||
|
expect(errors).not_to be_empty
|
||||||
|
expect(errors.first).to eq("Le champ #{champ_with_error.libelle} doit être rempli.")
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
context "when mandatory champ inside repetition" do
|
||||||
|
let(:champ_with_error) { dossier.champs.first.champs.first }
|
||||||
|
|
||||||
|
before do
|
||||||
|
dossier.champs.first.add_row
|
||||||
|
end
|
||||||
|
|
||||||
|
it 'should have errors' do
|
||||||
|
errors = dossier.check_mandatory_champs
|
||||||
|
expect(errors).not_to be_empty
|
||||||
|
expect(errors.first).to eq("Le champ #{champ_with_error.libelle} doit être rempli.")
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
|
@ -8,7 +8,7 @@ describe 'shared/dossiers/champs.html.haml', type: :view do
|
||||||
allow(view).to receive(:current_gestionnaire).and_return(gestionnaire)
|
allow(view).to receive(:current_gestionnaire).and_return(gestionnaire)
|
||||||
end
|
end
|
||||||
|
|
||||||
subject { render 'shared/dossiers/champs.html.haml', champs: champs, demande_seen_at: demande_seen_at }
|
subject { render 'shared/dossiers/champs.html.haml', champs: champs, demande_seen_at: demande_seen_at, profile: nil }
|
||||||
|
|
||||||
context "there are some champs" do
|
context "there are some champs" do
|
||||||
let(:dossier) { create(:dossier) }
|
let(:dossier) { create(:dossier) }
|
||||||
|
|
Loading…
Reference in a new issue