add confirm button for move

This commit is contained in:
Julien Fastré 2021-06-04 21:24:11 +02:00
parent 48e5809008
commit e5905902cc
6 changed files with 268 additions and 10 deletions

View File

@ -50,6 +50,49 @@ class HouseholdMemberController extends ApiController
return $this->json($editor->getHousehold(), Response::HTTP_OK, ["groups" => ["read"]]); return $this->json($editor->getHousehold(), Response::HTTP_OK, ["groups" => ["read"]]);
} }
/**
* @Route(
* "/api/1.0/person/household/members/move/test.{_format}",
* name="chill_api_person_household_members_move_test"
* )
*/
public function test(Request $request, $_format): Response
{
try {
$editor = $this->getSerializer()
->deserialize($request->getContent(), MembersEditor::class,
$_format, ['groups' => [ "read" ]]);
} catch (Exception\InvalidArgumentException | Exception\UnexpectedValueException $e) {
throw new BadRequestException("Deserialization error: {$e->getMessage()}", 45896, $e);
}
// TODO ACL
//
// TODO validation
// temporary, to have at least one problem for testing purpose
/*$violations = [
new \Symfony\Component\Validator\ConstraintViolation(
"This is a fake message",
null,
[],
$editor->getHousehold(),
'household.members.startDate',
new \DateTime('10 years ago')
),
new \Symfony\Component\Validator\ConstraintViolation(
"This is another fake message",
null,
[],
$editor->getHousehold(),
'household.members.endDate',
new \DateTime('10 years ago')
)
];
$violationsList = new \Symfony\Component\Validator\ConstraintViolationList($violations);
*/
return $this->json($editor->getHousehold(), Response::HTTP_OK, ["groups" => ["read"]]);
}
/** /**
* @Route( * @Route(
* "/{_locale}/person/household/members/editor", * "/{_locale}/person/household/members/editor",

View File

@ -2,6 +2,7 @@
<household></household> <household></household>
<concerned></concerned> <concerned></concerned>
<dates></dates> <dates></dates>
<confirmation></confirmation>
</template> </template>
<script> <script>
@ -10,6 +11,7 @@ import { mapState } from 'vuex';
import Concerned from './components/Concerned.vue'; import Concerned from './components/Concerned.vue';
import Household from './components/Household.vue'; import Household from './components/Household.vue';
import Dates from './components/Dates.vue'; import Dates from './components/Dates.vue';
import Confirmation from './components/Confirmation.vue';
export default { export default {
name: 'App', name: 'App',
@ -17,6 +19,7 @@ export default {
Concerned, Concerned,
Household, Household,
Dates, Dates,
Confirmation,
}, },
computed: { computed: {
// for debugging purpose // for debugging purpose

View File

@ -0,0 +1,47 @@
/*
*/
const householdMove = (payload) => {
const url = `/api/1.0/person/household/members/move.json`;
console.log(payload);
console.log(JSON.stringify(payload));
return fetch(url, {
method: 'POST',
headers: {
'Content-Type': 'application/json',
},
body: JSON.stringify(payload),
})
.then(response => {
if (response.ok) {
return response.json();
}
throw Error('Error with testing move');
});
};
const householdMoveTest = (payload) => {
const url = `/api/1.0/person/household/members/move/test.json`;
return fetch(url, {
method: 'POST',
headers: {
'Content-Type': 'application/json',
},
body: JSON.stringify(payload),
})
.then(response => {
if (response.status === 422) {
return response.json();
}
if (response.ok) {
// return an empty array if ok
return new Promise((resolve, reject) => resolve({ violations: [] }) );
}
throw Error('Error with testing move');
});
};
export {
householdMove,
householdMoveTest
};

View File

@ -0,0 +1,32 @@
<template>
<ul>
<li v-for="(msg, index) in warnings">
{{ $t(msg.m, msg.a) }}
</li>
</ul>
<div v-if="hasNoWarnings">
<button class="sc-button bt-green" @click="confirm">{{ $t('household_member_editor.confirmation') }}</button>
</div>
</template>
<script>
import { mapState } from 'vuex';
export default {
name: 'Confirmation',
computed: {
...mapState({
warnings: (state) => state.warnings,
hasNoWarnings: (state) => state.warnings.length === 0,
}),
},
methods: {
confirm() {
this.$store.dispatch('confirm');
}
}
}
</script>

View File

@ -1,4 +1,6 @@
import { createStore } from 'vuex'; import { createStore } from 'vuex';
import { householdMove, householdMoveTest } from './../api.js';
import { datetimeToISO } from 'ChillMainAssets/js/date.js';
const debug = process.env.NODE_ENV !== 'production'; const debug = process.env.NODE_ENV !== 'production';
@ -7,6 +9,7 @@ const concerned = window.household_members_editor_data.persons.map(p => {
person: p, person: p,
position: null, position: null,
allowRemove: false, allowRemove: false,
holder: false
}; };
}); });
@ -21,7 +24,7 @@ const store = createStore({
allowHouseholdSearch: window.household_members_editor_data.allowHouseholdSearch, allowHouseholdSearch: window.household_members_editor_data.allowHouseholdSearch,
allowLeaveWithoutHousehold: window.household_members_editor_data.allowLeaveWithoutHousehold, allowLeaveWithoutHousehold: window.household_members_editor_data.allowLeaveWithoutHousehold,
forceLeaveWithoutHousehold: false, forceLeaveWithoutHousehold: false,
warnings: [],
}, },
getters: { getters: {
isHouseholdNew(state) { isHouseholdNew(state) {
@ -63,13 +66,51 @@ const store = createStore({
.find(conc => conc.person.id === person_id) .find(conc => conc.person.id === person_id)
; ;
}, },
buildPayload: (state) => {
let
conc,
payload = {
concerned: [],
destination: {
id: state.household.id,
type: state.household.type
}
}
;
for (let i in state.concerned) {
conc = state.concerned[i];
console.log('loop', conc);
payload.concerned.push({
person: {
id: conc.person.id,
type: conc.person.type
},
position: {
id: conc.position.id,
type: conc.position.type
},
holder: conc.holder,
comment: "",
start_date: {
datetime: datetimeToISO(state.startDate)
}
});
}
return payload;
},
}, },
mutations: { mutations: {
addConcerned(state, person) { addConcerned(state, person) {
let persons = state.concerned.map(conc => conc.person.id); let persons = state.concerned.map(conc => conc.person.id);
if (!persons.includes(person.id)) { if (!persons.includes(person.id)) {
state.concerned.push({ person, position: null, state.concerned.push({
allowRemove: true }); person,
position: null,
allowRemove: true,
holder: false
});
} else { } else {
console.err("person already included"); console.err("person already included");
} }
@ -107,39 +148,89 @@ const store = createStore({
state.forceLeaveWithoutHousehold = true; state.forceLeaveWithoutHousehold = true;
}, },
setStartDate(state, dateI) { setStartDate(state, dateI) {
state.startDate = dateI state.startDate = dateI;
},
setWarnings(state, warnings) {
state.warnings = warnings;
}, },
}, },
actions: { actions: {
addConcerned({ commit }, person) { addConcerned({ commit, dispatch }, person) {
console.log('from actions addConcerned'); console.log('from actions addConcerned');
commit('addConcerned', person); commit('addConcerned', person);
dispatch('computeWarnings');
}, },
markPosition({ commit, state }, { person_id, position_id }) { markPosition({ commit, state, dispatch }, { person_id, position_id }) {
console.log('from action markPosition'); console.log('from action markPosition');
console.log('person_id', person_id); console.log('person_id', person_id);
console.log('position_id', position_id); console.log('position_id', position_id);
commit('markPosition', { person_id, position_id }); commit('markPosition', { person_id, position_id });
dispatch('computeWarnings');
}, },
toggleHolder({ commit }, conc) { toggleHolder({ commit }, conc) {
commit('toggleHolder', conc); commit('toggleHolder', conc);
}, },
removePosition({ commit }, conc) { removePosition({ commit, dispatch }, conc) {
commit('removePosition', conc); commit('removePosition', conc);
dispatch('computeWarnings');
}, },
removeConcerned({ commit }, conc) { removeConcerned({ commit, dispatch }, conc) {
commit('removeConcerned', conc); commit('removeConcerned', conc);
dispatch('computeWarnings');
}, },
createHousehold({ commit }) { createHousehold({ commit, dispatch }) {
commit('createHousehold'); commit('createHousehold');
dispatch('computeWarnings');
}, },
forceLeaveWithoutHousehold({ commit }) { forceLeaveWithoutHousehold({ commit, dispatch }) {
commit('forceLeaveWithoutHousehold'); commit('forceLeaveWithoutHousehold');
dispatch('computeWarnings');
}, },
setStartDate({ commit }, date) { setStartDate({ commit }, date) {
commit('setStartDate', date); commit('setStartDate', date);
}, },
computeWarnings({ commit, state, getters }) {
let warnings = [],
payload;
if (!getters.hasHousehold && !state.forceLeaveWithoutHousehold) {
warnings.push({ m: 'household_member_editor.add_destination', a: {} });
}
if (state.concerned.length === 0) {
warnings.push({ m: 'household_member_editor.add_at_least_onePerson', a: {} });
}
if (getters.concUnpositionned.length > 0
&& !state.forceLeaveWithoutHousehold) {
warnings.push({ m: 'household_member_editor.give_a_position_to_every_person', a: {} })
}
if (warnings.length === 0) {
payload = getters.buildPayload;
householdMoveTest(payload).then(errors => {
for (let i in errors.violations) {
console.log('error from server', errors.violations[i]);
warnings.push({ m: errors.violations[i].title, a: {} });
}
commit('setWarnings', warnings);
});
} else {
commit('setWarnings', warnings);
}
},
confirm({ getters }) {
let payload = getters.buildPayload;
householdMove(payload).then(household => {
console.log('move success', household);
let id = household.id;
// nothing to do anymore here, bye-bye !
window.location.replace(`/fr/person/household/{id}/members`);
});
},
} }
}); });
store.dispatch('computeWarnings');
export { store }; export { store };

View File

@ -826,3 +826,45 @@ paths:
400: 400:
description: "transition cannot be applyed" description: "transition cannot be applyed"
/1.0/person/household/members/move/test.json:
post:
tags:
- household
summary: test the move of one or more person, without persisting changes in database
requestBody:
required: true
content:
application/json:
schema:
type: object
properties:
concerned:
type: array
items:
type: object
properties:
person:
$ref: '#/components/schemas/PersonById'
start_date:
$ref: '#/components/schemas/Date'
position:
$ref: '#/components/schemas/HouseholdPosition'
holder:
type: boolean
comment:
type: string
destination:
oneOf:
- $ref: '#/components/schemas/Household'
responses:
401:
description: "Unauthorized"
404:
description: "Not found"
200:
description: "OK"
422:
description: "Unprocessable entity (validation errors)"
400:
description: "transition cannot be applyed"