Merge pull request #7409 from betagouv/estimated-completion-time-admin
Administrateur : affichage de la durée estimée de la démarche dans l'éditeur de champs (derrière un feature-flag)
This commit is contained in:
commit
d21896b1e3
12 changed files with 157 additions and 31 deletions
|
@ -143,4 +143,19 @@
|
||||||
padding-bottom: 15px;
|
padding-bottom: 15px;
|
||||||
padding-top: 15px;
|
padding-top: 15px;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.fill-duration {
|
||||||
|
align-self: center;
|
||||||
|
font-size: 14px;
|
||||||
|
|
||||||
|
a {
|
||||||
|
text-decoration: underline;
|
||||||
|
text-decoration-style: dotted;
|
||||||
|
|
||||||
|
// Remove the icon indicating an external link (for less visual noise)
|
||||||
|
&[target="_blank"]::after {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
module Administrateurs
|
module Administrateurs
|
||||||
class TypesDeChampController < AdministrateurController
|
class TypesDeChampController < AdministrateurController
|
||||||
before_action :retrieve_procedure, only: [:create, :update, :move, :destroy]
|
before_action :retrieve_procedure, only: [:create, :update, :move, :estimate_fill_duration, :destroy]
|
||||||
before_action :procedure_revisable?, only: [:create, :update, :move, :destroy]
|
before_action :procedure_revisable?, only: [:create, :update, :move, :destroy]
|
||||||
|
|
||||||
def create
|
def create
|
||||||
|
@ -31,6 +31,15 @@ module Administrateurs
|
||||||
head :no_content
|
head :no_content
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def estimate_fill_duration
|
||||||
|
estimate = if @procedure.feature_enabled?(:procedure_estimated_fill_duration)
|
||||||
|
@procedure.draft_revision.estimated_fill_duration
|
||||||
|
else
|
||||||
|
0
|
||||||
|
end
|
||||||
|
render json: { estimated_fill_duration: estimate }
|
||||||
|
end
|
||||||
|
|
||||||
def destroy
|
def destroy
|
||||||
@procedure.draft_revision.remove_type_de_champ(params[:id])
|
@procedure.draft_revision.remove_type_de_champ(params[:id])
|
||||||
reset_procedure
|
reset_procedure
|
||||||
|
|
|
@ -37,7 +37,8 @@ module ProcedureHelper
|
||||||
typeDeChamps: procedure.draft_revision.types_de_champ_public_as_json,
|
typeDeChamps: procedure.draft_revision.types_de_champ_public_as_json,
|
||||||
baseUrl: admin_procedure_types_de_champ_path(procedure),
|
baseUrl: admin_procedure_types_de_champ_path(procedure),
|
||||||
directUploadUrl: rails_direct_uploads_url,
|
directUploadUrl: rails_direct_uploads_url,
|
||||||
continuerUrl: admin_procedure_path(procedure)
|
continuerUrl: admin_procedure_path(procedure),
|
||||||
|
estimatedFillDuration: procedure.feature_enabled?(:procedure_estimate_fill_duration) ? procedure.draft_revision.estimated_fill_duration : 0
|
||||||
}
|
}
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
|
@ -4,7 +4,7 @@ import invariant from 'tiny-invariant';
|
||||||
type Operation = {
|
type Operation = {
|
||||||
path: string;
|
path: string;
|
||||||
method: string;
|
method: string;
|
||||||
payload: unknown;
|
payload?: unknown;
|
||||||
resolve: (value: unknown) => void;
|
resolve: (value: unknown) => void;
|
||||||
reject: () => void;
|
reject: () => void;
|
||||||
};
|
};
|
||||||
|
@ -45,7 +45,8 @@ export class OperationsQueue {
|
||||||
const url = `${this.baseUrl}${path}`;
|
const url = `${this.baseUrl}${path}`;
|
||||||
|
|
||||||
try {
|
try {
|
||||||
const data = await httpRequest(url, { method, json: payload }).json();
|
const json = payload;
|
||||||
|
const data = await httpRequest(url, { method, json }).json();
|
||||||
resolve(data);
|
resolve(data);
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
handleError(e as ResponseError, reject);
|
handleError(e as ResponseError, reject);
|
||||||
|
|
|
@ -93,7 +93,13 @@ export const TypeDeChampComponent = SortableElement<TypeDeChampProps>(
|
||||||
if (confirm('Êtes vous sûr de vouloir supprimer ce champ ?'))
|
if (confirm('Êtes vous sûr de vouloir supprimer ce champ ?'))
|
||||||
dispatch({
|
dispatch({
|
||||||
type: 'removeTypeDeChamp',
|
type: 'removeTypeDeChamp',
|
||||||
params: { typeDeChamp }
|
params: { typeDeChamp },
|
||||||
|
done: (estimatedFillDuration) => {
|
||||||
|
dispatch({
|
||||||
|
type: 'refresh',
|
||||||
|
params: { estimatedFillDuration }
|
||||||
|
});
|
||||||
|
}
|
||||||
});
|
});
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
|
@ -223,7 +229,12 @@ function createUpdateHandler(
|
||||||
field,
|
field,
|
||||||
value: readValue(target)
|
value: readValue(target)
|
||||||
},
|
},
|
||||||
done: () => dispatch({ type: 'refresh' })
|
done: (estimatedFillDuration: number) => {
|
||||||
|
return dispatch({
|
||||||
|
type: 'refresh',
|
||||||
|
params: { estimatedFillDuration }
|
||||||
|
});
|
||||||
|
}
|
||||||
})
|
})
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
|
@ -45,7 +45,8 @@ export function TypeDeChampRepetitionOptions({
|
||||||
dispatch({
|
dispatch({
|
||||||
type: 'addNewRepetitionTypeDeChamp',
|
type: 'addNewRepetitionTypeDeChamp',
|
||||||
params: { typeDeChamp },
|
params: { typeDeChamp },
|
||||||
done: () => dispatch({ type: 'refresh' })
|
done: (estimatedFillDuration: number) =>
|
||||||
|
dispatch({ type: 'refresh', params: { estimatedFillDuration } })
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
>
|
>
|
||||||
|
|
|
@ -24,6 +24,10 @@ export function TypeDeChamps({
|
||||||
(tdc) => tdc.id == undefined
|
(tdc) => tdc.id == undefined
|
||||||
);
|
);
|
||||||
|
|
||||||
|
const formattedEstimatedFillDuration = state.estimatedFillDuration
|
||||||
|
? Math.max(1, Math.round(state.estimatedFillDuration / 60)) + ' mn'
|
||||||
|
: '';
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="champs-editor">
|
<div className="champs-editor">
|
||||||
<SortableContainer
|
<SortableContainer
|
||||||
|
@ -60,7 +64,12 @@ export function TypeDeChamps({
|
||||||
onClick={() =>
|
onClick={() =>
|
||||||
dispatch({
|
dispatch({
|
||||||
type: 'addNewTypeDeChamp',
|
type: 'addNewTypeDeChamp',
|
||||||
done: () => dispatch({ type: 'refresh' })
|
done: (estimatedFillDuration: number) => {
|
||||||
|
dispatch({
|
||||||
|
type: 'refresh',
|
||||||
|
params: { estimatedFillDuration }
|
||||||
|
});
|
||||||
|
}
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
>
|
>
|
||||||
|
@ -68,6 +77,18 @@ export function TypeDeChamps({
|
||||||
|
|
||||||
{addChampLabel(state.isAnnotation)}
|
{addChampLabel(state.isAnnotation)}
|
||||||
</button>
|
</button>
|
||||||
|
{state.estimatedFillDuration > 0 && (
|
||||||
|
<span className="fill-duration">
|
||||||
|
Durée de remplissage estimée :{' '}
|
||||||
|
<a
|
||||||
|
href="https://doc.demarches-simplifiees.fr/tutoriels/tutoriel-administrateur#g.-estimation-de-la-duree-de-remplissage"
|
||||||
|
target="_blank"
|
||||||
|
rel="noopener noreferrer"
|
||||||
|
>
|
||||||
|
{formattedEstimatedFillDuration}
|
||||||
|
</a>
|
||||||
|
</span>
|
||||||
|
)}
|
||||||
<a className="button accepted" href={state.continuerUrl}>
|
<a className="button accepted" href={state.continuerUrl}>
|
||||||
Continuer >
|
Continuer >
|
||||||
</a>
|
</a>
|
||||||
|
|
|
@ -12,6 +12,7 @@ type TypesDeChampEditorProps = {
|
||||||
isAnnotation: boolean;
|
isAnnotation: boolean;
|
||||||
typeDeChamps: TypeDeChamp[];
|
typeDeChamps: TypeDeChamp[];
|
||||||
typeDeChampsTypes: [label: string, type: string][];
|
typeDeChampsTypes: [label: string, type: string][];
|
||||||
|
estimatedFillDuration: number;
|
||||||
};
|
};
|
||||||
|
|
||||||
export type State = Omit<TypesDeChampEditorProps, 'baseUrl'> & {
|
export type State = Omit<TypesDeChampEditorProps, 'baseUrl'> & {
|
||||||
|
@ -27,6 +28,7 @@ export type State = Omit<TypesDeChampEditorProps, 'baseUrl'> & {
|
||||||
| 'mandatory'
|
| 'mandatory'
|
||||||
>;
|
>;
|
||||||
prefix?: string;
|
prefix?: string;
|
||||||
|
estimatedFillDuration?: number;
|
||||||
};
|
};
|
||||||
|
|
||||||
export default function TypesDeChampEditor(props: TypesDeChampEditorProps) {
|
export default function TypesDeChampEditor(props: TypesDeChampEditorProps) {
|
||||||
|
@ -47,7 +49,8 @@ export default function TypesDeChampEditor(props: TypesDeChampEditorProps) {
|
||||||
typeDeChampsTypes: props.typeDeChampsTypes,
|
typeDeChampsTypes: props.typeDeChampsTypes,
|
||||||
directUploadUrl: props.directUploadUrl,
|
directUploadUrl: props.directUploadUrl,
|
||||||
isAnnotation: props.isAnnotation,
|
isAnnotation: props.isAnnotation,
|
||||||
continuerUrl: props.continuerUrl
|
continuerUrl: props.continuerUrl,
|
||||||
|
estimatedFillDuration: props.estimatedFillDuration
|
||||||
};
|
};
|
||||||
|
|
||||||
return <TypeDeChamps state={state} typeDeChamps={props.typeDeChamps} />;
|
return <TypeDeChamps state={state} typeDeChamps={props.typeDeChamps} />;
|
||||||
|
|
|
@ -52,8 +52,20 @@ export function updateTypeDeChampOperation(
|
||||||
handleResponseData(typeDeChamp, data as ResponseData);
|
handleResponseData(typeDeChamp, data as ResponseData);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
export function estimateFillDuration(queue: OperationsQueue): Promise<number> {
|
||||||
|
return queue
|
||||||
|
.enqueue({
|
||||||
|
path: `/estimate_fill_duration`,
|
||||||
|
method: 'get'
|
||||||
|
})
|
||||||
|
.then((data) => {
|
||||||
|
const responseData = data as EstimatedFillDurationResponseData;
|
||||||
|
return responseData.estimated_fill_duration;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
type ResponseData = { type_de_champ: Record<string, string> };
|
type ResponseData = { type_de_champ: Record<string, string> };
|
||||||
|
type EstimatedFillDurationResponseData = { estimated_fill_duration: number };
|
||||||
|
|
||||||
function handleResponseData(
|
function handleResponseData(
|
||||||
typeDeChamp: Partial<TypeDeChamp>,
|
typeDeChamp: Partial<TypeDeChamp>,
|
||||||
|
|
|
@ -3,19 +3,20 @@ import {
|
||||||
createTypeDeChampOperation,
|
createTypeDeChampOperation,
|
||||||
destroyTypeDeChampOperation,
|
destroyTypeDeChampOperation,
|
||||||
moveTypeDeChampOperation,
|
moveTypeDeChampOperation,
|
||||||
updateTypeDeChampOperation
|
updateTypeDeChampOperation,
|
||||||
|
estimateFillDuration
|
||||||
} from './operations';
|
} from './operations';
|
||||||
import type { TypeDeChamp, State, Flash, OperationsQueue } from './types';
|
import type { TypeDeChamp, State, Flash, OperationsQueue } from './types';
|
||||||
|
|
||||||
type AddNewTypeDeChampAction = {
|
type AddNewTypeDeChampAction = {
|
||||||
type: 'addNewTypeDeChamp';
|
type: 'addNewTypeDeChamp';
|
||||||
done: () => void;
|
done: (estimatedFillDuration: number) => void;
|
||||||
};
|
};
|
||||||
|
|
||||||
type AddNewRepetitionTypeDeChampAction = {
|
type AddNewRepetitionTypeDeChampAction = {
|
||||||
type: 'addNewRepetitionTypeDeChamp';
|
type: 'addNewRepetitionTypeDeChamp';
|
||||||
params: { typeDeChamp: TypeDeChamp };
|
params: { typeDeChamp: TypeDeChamp };
|
||||||
done: () => void;
|
done: (estimatedFillDuration: number) => void;
|
||||||
};
|
};
|
||||||
|
|
||||||
type UpdateTypeDeChampAction = {
|
type UpdateTypeDeChampAction = {
|
||||||
|
@ -25,12 +26,13 @@ type UpdateTypeDeChampAction = {
|
||||||
field: keyof TypeDeChamp;
|
field: keyof TypeDeChamp;
|
||||||
value: string | boolean;
|
value: string | boolean;
|
||||||
};
|
};
|
||||||
done: () => void;
|
done: (estimatedFillDuration: number) => void;
|
||||||
};
|
};
|
||||||
|
|
||||||
type RemoveTypeDeChampAction = {
|
type RemoveTypeDeChampAction = {
|
||||||
type: 'removeTypeDeChamp';
|
type: 'removeTypeDeChamp';
|
||||||
params: { typeDeChamp: TypeDeChamp };
|
params: { typeDeChamp: TypeDeChamp };
|
||||||
|
done: (estimatedFillDuration: number) => void;
|
||||||
};
|
};
|
||||||
|
|
||||||
type MoveTypeDeChampUpAction = {
|
type MoveTypeDeChampUpAction = {
|
||||||
|
@ -50,6 +52,7 @@ type OnSortTypeDeChampsAction = {
|
||||||
|
|
||||||
type RefreshAction = {
|
type RefreshAction = {
|
||||||
type: 'refresh';
|
type: 'refresh';
|
||||||
|
params: { estimatedFillDuration: number };
|
||||||
};
|
};
|
||||||
|
|
||||||
export type Action =
|
export type Action =
|
||||||
|
@ -84,7 +87,12 @@ export default function typeDeChampsReducer(
|
||||||
action.done
|
action.done
|
||||||
);
|
);
|
||||||
case 'removeTypeDeChamp':
|
case 'removeTypeDeChamp':
|
||||||
return removeTypeDeChamp(state, state.typeDeChamps, action.params);
|
return removeTypeDeChamp(
|
||||||
|
state,
|
||||||
|
state.typeDeChamps,
|
||||||
|
action.params,
|
||||||
|
action.done
|
||||||
|
);
|
||||||
case 'moveTypeDeChampUp':
|
case 'moveTypeDeChampUp':
|
||||||
return moveTypeDeChampUp(state, state.typeDeChamps, action.params);
|
return moveTypeDeChampUp(state, state.typeDeChamps, action.params);
|
||||||
case 'moveTypeDeChampDown':
|
case 'moveTypeDeChampDown':
|
||||||
|
@ -92,7 +100,11 @@ export default function typeDeChampsReducer(
|
||||||
case 'onSortTypeDeChamps':
|
case 'onSortTypeDeChamps':
|
||||||
return onSortTypeDeChamps(state, state.typeDeChamps, action.params);
|
return onSortTypeDeChamps(state, state.typeDeChamps, action.params);
|
||||||
case 'refresh':
|
case 'refresh':
|
||||||
return { ...state, typeDeChamps: [...state.typeDeChamps] };
|
return {
|
||||||
|
...state,
|
||||||
|
typeDeChamps: [...state.typeDeChamps],
|
||||||
|
estimatedFillDuration: action.params.estimatedFillDuration
|
||||||
|
};
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -100,7 +112,7 @@ function addTypeDeChamp(
|
||||||
state: State,
|
state: State,
|
||||||
typeDeChamps: TypeDeChamp[],
|
typeDeChamps: TypeDeChamp[],
|
||||||
insertAfter: { index: number; target: HTMLDivElement } | null,
|
insertAfter: { index: number; target: HTMLDivElement } | null,
|
||||||
done: () => void
|
done: (estimatedFillDuration: number) => void
|
||||||
) {
|
) {
|
||||||
const typeDeChamp = {
|
const typeDeChamp = {
|
||||||
...state.defaultTypeDeChampAttributes
|
...state.defaultTypeDeChampAttributes
|
||||||
|
@ -117,7 +129,8 @@ function addTypeDeChamp(
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
state.flash.success();
|
state.flash.success();
|
||||||
done();
|
const estimatedFillDuration = await estimateFillDuration(state.queue);
|
||||||
|
done(estimatedFillDuration);
|
||||||
if (insertAfter) {
|
if (insertAfter) {
|
||||||
insertAfter.target.nextElementSibling?.scrollIntoView({
|
insertAfter.target.nextElementSibling?.scrollIntoView({
|
||||||
behavior: 'smooth',
|
behavior: 'smooth',
|
||||||
|
@ -150,7 +163,7 @@ function addTypeDeChamp(
|
||||||
function addNewTypeDeChamp(
|
function addNewTypeDeChamp(
|
||||||
state: State,
|
state: State,
|
||||||
typeDeChamps: TypeDeChamp[],
|
typeDeChamps: TypeDeChamp[],
|
||||||
done: () => void
|
done: (estimatedFillDuration: number) => void
|
||||||
) {
|
) {
|
||||||
return addTypeDeChamp(state, typeDeChamps, findItemToInsertAfter(), done);
|
return addTypeDeChamp(state, typeDeChamps, findItemToInsertAfter(), done);
|
||||||
}
|
}
|
||||||
|
@ -159,7 +172,7 @@ function addNewRepetitionTypeDeChamp(
|
||||||
state: State,
|
state: State,
|
||||||
typeDeChamps: TypeDeChamp[],
|
typeDeChamps: TypeDeChamp[],
|
||||||
{ typeDeChamp }: AddNewRepetitionTypeDeChampAction['params'],
|
{ typeDeChamp }: AddNewRepetitionTypeDeChampAction['params'],
|
||||||
done: () => void
|
done: (estimatedFillDuration: number) => void
|
||||||
) {
|
) {
|
||||||
return addTypeDeChamp(
|
return addTypeDeChamp(
|
||||||
{
|
{
|
||||||
|
@ -179,7 +192,7 @@ function updateTypeDeChamp(
|
||||||
state: State,
|
state: State,
|
||||||
typeDeChamps: TypeDeChamp[],
|
typeDeChamps: TypeDeChamp[],
|
||||||
{ typeDeChamp, field, value }: UpdateTypeDeChampAction['params'],
|
{ typeDeChamp, field, value }: UpdateTypeDeChampAction['params'],
|
||||||
done: () => void
|
done: (estimatedFillDuration: number) => void
|
||||||
) {
|
) {
|
||||||
if (field == 'type_champ' && !typeDeChamp.drop_down_list_value) {
|
if (field == 'type_champ' && !typeDeChamp.drop_down_list_value) {
|
||||||
switch (value) {
|
switch (value) {
|
||||||
|
@ -212,10 +225,17 @@ function updateTypeDeChamp(
|
||||||
function removeTypeDeChamp(
|
function removeTypeDeChamp(
|
||||||
state: State,
|
state: State,
|
||||||
typeDeChamps: TypeDeChamp[],
|
typeDeChamps: TypeDeChamp[],
|
||||||
{ typeDeChamp }: RemoveTypeDeChampAction['params']
|
{ typeDeChamp }: RemoveTypeDeChampAction['params'],
|
||||||
|
done: (estimatedFillDuration: number) => void
|
||||||
) {
|
) {
|
||||||
destroyTypeDeChampOperation(typeDeChamp, state.queue)
|
destroyTypeDeChampOperation(typeDeChamp, state.queue)
|
||||||
.then(() => state.flash.success())
|
.then(() => {
|
||||||
|
state.flash.success();
|
||||||
|
return estimateFillDuration(state.queue);
|
||||||
|
})
|
||||||
|
.then((estimatedFillDuration: number) => {
|
||||||
|
done(estimatedFillDuration);
|
||||||
|
})
|
||||||
.catch((message) => state.flash.error(message));
|
.catch((message) => state.flash.error(message));
|
||||||
|
|
||||||
return {
|
return {
|
||||||
|
@ -295,11 +315,14 @@ function getUpdateHandler(
|
||||||
let handler = updateHandlers.get(typeDeChamp);
|
let handler = updateHandlers.get(typeDeChamp);
|
||||||
if (!handler) {
|
if (!handler) {
|
||||||
handler = debounce(
|
handler = debounce(
|
||||||
(done: () => void) =>
|
(done: (estimatedFillDuration: number) => void) =>
|
||||||
updateTypeDeChampOperation(typeDeChamp, queue)
|
updateTypeDeChampOperation(typeDeChamp, queue)
|
||||||
.then(() => {
|
.then(() => {
|
||||||
flash.success();
|
flash.success();
|
||||||
done();
|
return estimateFillDuration(queue);
|
||||||
|
})
|
||||||
|
.then((estimatedFillDuration: number) => {
|
||||||
|
done(estimatedFillDuration);
|
||||||
})
|
})
|
||||||
.catch((message) => flash.error(message)),
|
.catch((message) => flash.error(message)),
|
||||||
200
|
200
|
||||||
|
|
|
@ -461,6 +461,9 @@ Rails.application.routes.draw do
|
||||||
resources :experts, controller: 'experts_procedures', only: [:index, :create, :update, :destroy]
|
resources :experts, controller: 'experts_procedures', only: [:index, :create, :update, :destroy]
|
||||||
|
|
||||||
resources :types_de_champ, only: [:create, :update, :destroy] do
|
resources :types_de_champ, only: [:create, :update, :destroy] do
|
||||||
|
collection do
|
||||||
|
get :estimate_fill_duration
|
||||||
|
end
|
||||||
member do
|
member do
|
||||||
patch :move
|
patch :move
|
||||||
end
|
end
|
||||||
|
|
|
@ -7,7 +7,7 @@ describe 'As an administrateur I can edit types de champ', js: true do
|
||||||
visit champs_admin_procedure_path(procedure)
|
visit champs_admin_procedure_path(procedure)
|
||||||
end
|
end
|
||||||
|
|
||||||
it "Add a new champ" do
|
scenario "adding a new champ" do
|
||||||
add_champ
|
add_champ
|
||||||
|
|
||||||
fill_in 'champ-0-libelle', with: 'libellé de champ'
|
fill_in 'champ-0-libelle', with: 'libellé de champ'
|
||||||
|
@ -15,7 +15,7 @@ describe 'As an administrateur I can edit types de champ', js: true do
|
||||||
expect(page).to have_content('Formulaire enregistré')
|
expect(page).to have_content('Formulaire enregistré')
|
||||||
end
|
end
|
||||||
|
|
||||||
it "Add multiple champs" do
|
scenario "adding multiple champs" do
|
||||||
# Champs are created when clicking the 'Add field' button
|
# Champs are created when clicking the 'Add field' button
|
||||||
add_champs(count: 3)
|
add_champs(count: 3)
|
||||||
|
|
||||||
|
@ -47,12 +47,13 @@ describe 'As an administrateur I can edit types de champ', js: true do
|
||||||
expect(page).to have_content('Supprimer', count: 2)
|
expect(page).to have_content('Supprimer', count: 2)
|
||||||
end
|
end
|
||||||
|
|
||||||
it "Remove champs" do
|
scenario "removing champs" do
|
||||||
add_champ(remove_flash_message: true)
|
add_champ(remove_flash_message: true)
|
||||||
|
|
||||||
fill_in 'champ-0-libelle', with: 'libellé de champ'
|
fill_in 'champ-0-libelle', with: 'libellé de champ'
|
||||||
blur
|
blur
|
||||||
expect(page).to have_content('Formulaire enregistré')
|
expect(page).to have_content('Formulaire enregistré')
|
||||||
|
|
||||||
page.refresh
|
page.refresh
|
||||||
|
|
||||||
page.accept_alert do
|
page.accept_alert do
|
||||||
|
@ -65,7 +66,7 @@ describe 'As an administrateur I can edit types de champ', js: true do
|
||||||
expect(page).to have_content('Supprimer', count: 0)
|
expect(page).to have_content('Supprimer', count: 0)
|
||||||
end
|
end
|
||||||
|
|
||||||
it "Only add valid champs" do
|
scenario "adding an invalid champ" do
|
||||||
add_champ(remove_flash_message: true)
|
add_champ(remove_flash_message: true)
|
||||||
|
|
||||||
fill_in 'champ-0-libelle', with: ''
|
fill_in 'champ-0-libelle', with: ''
|
||||||
|
@ -78,7 +79,7 @@ describe 'As an administrateur I can edit types de champ', js: true do
|
||||||
expect(page).to have_content('Formulaire enregistré')
|
expect(page).to have_content('Formulaire enregistré')
|
||||||
end
|
end
|
||||||
|
|
||||||
it "Add repetition champ" do
|
scenario "adding a repetition champ" do
|
||||||
add_champ(remove_flash_message: true)
|
add_champ(remove_flash_message: true)
|
||||||
|
|
||||||
select('Bloc répétable', from: 'champ-0-type_champ')
|
select('Bloc répétable', from: 'champ-0-type_champ')
|
||||||
|
@ -109,7 +110,7 @@ describe 'As an administrateur I can edit types de champ', js: true do
|
||||||
expect(page).to have_content('Supprimer', count: 3)
|
expect(page).to have_content('Supprimer', count: 3)
|
||||||
end
|
end
|
||||||
|
|
||||||
it "Add carte champ" do
|
scenario "adding a carte champ" do
|
||||||
add_champ
|
add_champ
|
||||||
|
|
||||||
select('Carte', from: 'champ-0-type_champ')
|
select('Carte', from: 'champ-0-type_champ')
|
||||||
|
@ -128,7 +129,7 @@ describe 'As an administrateur I can edit types de champ', js: true do
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
it "Add dropdown champ" do
|
scenario "adding a dropdown champ" do
|
||||||
add_champ
|
add_champ
|
||||||
|
|
||||||
select('Choix parmi une liste', from: 'champ-0-type_champ')
|
select('Choix parmi une liste', from: 'champ-0-type_champ')
|
||||||
|
@ -142,4 +143,29 @@ describe 'As an administrateur I can edit types de champ', js: true do
|
||||||
|
|
||||||
expect(page).to have_content('Un menu')
|
expect(page).to have_content('Un menu')
|
||||||
end
|
end
|
||||||
|
|
||||||
|
context "when the estimated fill duration is enabled" do
|
||||||
|
before { Flipper.enable(:procedure_estimated_fill_duration) }
|
||||||
|
after { Flipper.disable(:procedure_estimated_fill_duration) }
|
||||||
|
|
||||||
|
scenario "displaying the estimated fill duration" do
|
||||||
|
# It doesn't display anything when there are no champs
|
||||||
|
expect(page).not_to have_content('Durée de remplissage estimé')
|
||||||
|
|
||||||
|
# It displays the estimate when adding a new champ
|
||||||
|
add_champ
|
||||||
|
select('Pièce justificative', from: 'champ-0-type_champ')
|
||||||
|
expect(page).to have_content('Durée de remplissage estimée : 1 mn')
|
||||||
|
|
||||||
|
# It updates the estimate when updating the champ
|
||||||
|
check 'Obligatoire'
|
||||||
|
expect(page).to have_content('Durée de remplissage estimée : 3 mn')
|
||||||
|
|
||||||
|
# It updates the estimate when removing the champ
|
||||||
|
page.accept_alert do
|
||||||
|
click_on 'Supprimer'
|
||||||
|
end
|
||||||
|
expect(page).not_to have_content('Durée de remplissage estimée')
|
||||||
|
end
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
Loading…
Reference in a new issue