Merge branch 'features/localize-accompanying-course-2' into 'master'

Improve UX for handling location in accompanying course

See merge request Chill-Projet/chill-bundles!125
This commit is contained in:
Julien Fastré 2021-08-19 19:03:41 +00:00
commit 71e6b356c3
14 changed files with 294 additions and 422 deletions

View File

@ -499,6 +499,17 @@ class AccompanyingPeriod implements TrackCreationInterface, TrackUpdateInterface
return $collection->count() > 0 ? $collection->first() : NULL; return $collection->count() > 0 ? $collection->first() : NULL;
} }
public function getOPenParticipations(): Collection
{
return $this
->getParticipations()
->filter(
static function(AccompanyingPeriodParticipation $participation): bool {
return null === $participation->getEndDate();
}
);
}
/** /**
* Return true if the accompanying period contains a person. * Return true if the accompanying period contains a person.
* *
@ -974,6 +985,22 @@ class AccompanyingPeriod implements TrackCreationInterface, TrackUpdateInterface
return $this->personLocation; return $this->personLocation;
} }
/**
* Get a list of person which have an adresse available for a valid location
*
* @return Collection|Person[]
*/
public function getAvailablePersonLocation(): Collection
{
return $this->getOPenParticipations()
->filter(function(AccompanyingPeriodParticipation $p) {
return $p->getPerson()->hasCurrentHouseholdAddress();
})
->map(function(AccompanyingPeriodParticipation $p) {
return $p->getPerson();
});
}
/** /**
* @Groups({"write"}) * @Groups({"write"})
*/ */

View File

@ -169,7 +169,7 @@ use Symfony\Component\Validator\Constraints as Assert;
* orphanRemoval=true * orphanRemoval=true
* ) * )
* @Serializer\Groups({"read"}) * @Serializer\Groups({"read"})
* @internal /!\ the serialization for read / write evaluations is handled in `AccompanyingPeriodWorkDenormalizer` * @internal /!\ the serialization for write evaluations is handled in `AccompanyingPeriodWorkDenormalizer`
*/ */
private Collection $accompanyingPeriodWorkEvaluations; private Collection $accompanyingPeriodWorkEvaluations;

View File

@ -103,7 +103,7 @@ class Person implements HasCenterInterface, TrackCreationInterface, TrackUpdateI
* *
* @ORM\Column(type="date", nullable=true) * @ORM\Column(type="date", nullable=true)
*/ */
private $birthdate; private $birthdate;
/** /**
* The person's deathdate * The person's deathdate
@ -736,8 +736,8 @@ class Person implements HasCenterInterface, TrackCreationInterface, TrackUpdateI
{ {
return $this->birthdate; return $this->birthdate;
} }
public function getAge(): ?int public function getAge(): ?int
{ {
if ($this->birthdate instanceof \DateTimeInterface) { if ($this->birthdate instanceof \DateTimeInterface) {
return date_diff($this->birthdate, date_create('now'))->format("%y"); return date_diff($this->birthdate, date_create('now'))->format("%y");
@ -1439,6 +1439,11 @@ class Person implements HasCenterInterface, TrackCreationInterface, TrackUpdateI
} }
} }
public function hasCurrentHouseholdAddress(?\DateTimeImmutable $at = null): bool
{
return null !== $this->getCurrentHouseholdAddress($at);
}
public function getGenderComment(): CommentEmbeddable public function getGenderComment(): CommentEmbeddable
{ {
return $this->genderComment; return $this->genderComment;

View File

@ -0,0 +1,49 @@
const onSubmit = function(e) {
e.preventDefault();
let
form = e.target,
formData = new FormData(form),
url = form.action,
payload = {
type: 'accompanying_period',
id: Number.parseInt(formData.get('periodId'), 10),
personLocation: {
type: 'person',
id: Number.parseInt(formData.get('personLocation'), 10)
}
}
;
console.log('event', e);
console.log('form', form);
console.log('formData', formData);
console.log('url', url);
console.log('payload', payload);
window.fetch(url, {
method: 'PATCH',
headers: {
'Content-Type': 'application/json'
},
body: JSON.stringify(payload),
})
.then(r => {
if (r.ok) {
console.log('response ok');
window.location.reload();
} else {
console.err("could not patch accompanying course");
}
});
}
window.addEventListener('DOMContentLoaded', function(e) {
let forms = document.querySelectorAll('.quickLocationForm');
console.log(forms);
forms.forEach(function(form){
console.log('form quickLocation found', form);
form.addEventListener('submit', onSubmit);
})
});

View File

@ -1,374 +0,0 @@
import { createStore } from 'vuex';
import { householdMove, fetchHouseholdSuggestionByAccompanyingPeriod, fetchAddressSuggestionByPerson} from './../api.js';
import { datetimeToISO } from 'ChillMainAssets/chill/js/date.js';
const debug = process.env.NODE_ENV !== 'production';
const concerned = window.household_members_editor_data.persons.map(p => {
return {
person: p,
position: null,
allowRemove: false,
holder: false,
comment: "",
};
});
console.log('expand suggestions', window.household_members_editor_expand_suggestions === 1);
const store = createStore({
strict: debug,
state: {
concerned,
household: window.household_members_editor_data.household,
positions: window.household_members_editor_data.positions.sort((a, b) => {
if (a.ordering < b.ordering) {
return -1;
}
if (a.ordering > b.ordering) {
return 1;
}
return 0;
}),
startDate: new Date(),
allowHouseholdCreate: window.household_members_editor_data.allowHouseholdCreate,
allowHouseholdSearch: window.household_members_editor_data.allowHouseholdSearch,
allowLeaveWithoutHousehold: window.household_members_editor_data.allowLeaveWithoutHousehold,
forceLeaveWithoutHousehold: false,
householdSuggestionByAccompanyingPeriod: [],
showHouseholdSuggestion: window.household_members_editor_expand_suggestions === 1,
addressesSuggestion: [],
warnings: [],
errors: []
},
getters: {
isHouseholdNew(state) {
if (state.household === null) {
return false;
}
return !Number.isInteger(state.household.id);
},
hasHousehold(state) {
return state.household !== null;
},
hasHouseholdOrLeave(state) {
return state.household !== null || state.forceLeaveWithoutHousehold;
},
hasHouseholdSuggestion(state, getters) {
return getters.filterHouseholdSuggestionByAccompanyingPeriod.length > 0;
},
countHouseholdSuggestion(state, getters) {
return getters.filterHouseholdSuggestionByAccompanyingPeriod.length;
},
filterHouseholdSuggestionByAccompanyingPeriod(state) {
if (state.household === null) {
return state.householdSuggestionByAccompanyingPeriod;
}
return state.householdSuggestionByAccompanyingPeriod
.filter(h => h.id !== state.household.id)
;
},
hasPersonsWellPositionnated(state, getters) {
return getters.needsPositionning === false
|| (getters.persons.length > 0 && getters.concUnpositionned.length === 0);
},
persons(state) {
return state.concerned.map(conc => conc.person);
},
concUnpositionned(state) {
return state.concerned
.filter(conc => conc.position === null)
;
},
positions(state) {
return state.positions;
},
personByPosition: (state) => (position_id) => {
return state.concerned
.filter(conc =>
conc.position !== null ? conc.position.id === position_id : false
)
.map(conc => conc.person)
;
},
concByPosition: (state) => (position_id) => {
return state.concerned
.filter(conc =>
conc.position !== null ? conc.position.id === position_id : false
)
;
},
concByPersonId: (state) => (person_id) => {
return state.concerned
.find(conc => conc.person.id === person_id)
;
},
needsPositionning(state) {
return state.forceLeaveWithoutHousehold === false;
},
buildPayload: (state) => {
let
conc,
payload_conc,
payload = {
concerned: [],
destination: null
}
;
if (state.forceLeaveWithoutHousehold === false) {
payload.destination = {
id: state.household.id,
type: state.household.type
};
}
for (let i in state.concerned) {
conc = state.concerned[i];
payload_conc = {
person: {
id: conc.person.id,
type: conc.person.type
},
start_date: {
datetime: datetimeToISO(state.startDate)
}
};
if (state.forceLeaveWithoutHousehold === false) {
payload_conc.position = {
id: conc.position.id,
type: conc.position.type
};
payload_conc.holder = conc.holder;
payload_conc.comment = conc.comment;
}
payload.concerned.push(payload_conc);
}
return payload;
},
},
mutations: {
addConcerned(state, person) {
let persons = state.concerned.map(conc => conc.person.id);
if (!persons.includes(person.id)) {
state.concerned.push({
person,
position: null,
allowRemove: true,
holder: false,
comment: "",
});
} else {
console.err("person already included");
}
},
markPosition(state, { person_id, position_id}) {
let
position = state.positions.find(pos => pos.id === position_id),
conc = state.concerned.find(c => c.person.id === person_id);
conc.position = position;
},
setComment(state, {conc, comment}) {
conc.comment = comment;
},
toggleHolder(state, conc) {
conc.holder = !conc.holder;
},
removePosition(state, conc) {
conc.holder = false;
conc.position = null;
},
removeConcerned(state, conc) {
state.concerned = state.concerned.filter(c =>
c.person.id !== conc.person.id
)
},
createHousehold(state) {
state.household = { type: 'household', members: [], current_address: null,
current_members_id: [] };
state.forceLeaveWithoutHousehold = false;
},
removeHousehold(state) {
state.household = null;
state.forceLeaveWithoutHousehold = false;
},
setHouseholdAddress(state, address) {
if (null === state.household) {
console.error("no household");
throw new Error("No household");
}
state.household.current_address = address;
state.household.force_new_address = address;
},
forceLeaveWithoutHousehold(state) {
state.household = null;
state.forceLeaveWithoutHousehold = true;
},
selectHousehold(state, household) {
state.household = household;
state.forceLeaveWithoutHousehold = false;
},
setHouseholdSuggestionByAccompanyingPeriod(state, households) {
let existingIds = state.householdSuggestionByAccompanyingPeriod
.map(h => h.id);
for (let i in households) {
if (!existingIds.includes(households[i].id)) {
state.householdSuggestionByAccompanyingPeriod.push(households[i]);
}
}
},
setStartDate(state, dateI) {
state.startDate = dateI;
},
toggleHouseholdSuggestion(state) {
state.showHouseholdSuggestion = !state.showHouseholdSuggestion;
},
setWarnings(state, warnings) {
state.warnings = warnings;
// reset errors, which should come from servers
state.errors.splice(0, state.errors.length);
},
setErrors(state, errors) {
state.errors = errors;
},
addAddressesSuggestion(state, addresses) {
let existingIds = state.addressesSuggestion
.map(a => a.id);
for (let i in addresses) {
if (!existingIds.includes(addresses[i].id)) {
state.addressesSuggestion.push(addresses[i]);
}
}
}
},
actions: {
addConcerned({ commit, dispatch }, person) {
commit('addConcerned', person);
dispatch('computeWarnings');
dispatch('fetchAddressSuggestions');
},
markPosition({ commit, state, dispatch }, { person_id, position_id }) {
commit('markPosition', { person_id, position_id });
dispatch('computeWarnings');
},
toggleHolder({ commit, dispatch }, conc) {
commit('toggleHolder', conc);
dispatch('computeWarnings');
},
removePosition({ commit, dispatch }, conc) {
commit('removePosition', conc);
dispatch('computeWarnings');
},
removeConcerned({ commit, dispatch }, conc) {
commit('removeConcerned', conc);
dispatch('computeWarnings');
dispatch('fetchAddressSuggestions');
},
removeHousehold({ commit, dispatch }) {
commit('removeHousehold');
dispatch('computeWarnings');
},
createHousehold({ commit, dispatch }) {
commit('createHousehold');
dispatch('computeWarnings');
},
forceLeaveWithoutHousehold({ commit, dispatch }) {
commit('forceLeaveWithoutHousehold');
dispatch('computeWarnings');
},
selectHousehold({ commit }, h) {
commit('selectHousehold', h);
dispatch('computeWarnings');
},
setStartDate({ commit, dispatch }, date) {
commit('setStartDate', date);
dispatch('computeWarnings');
},
setComment({ commit }, payload) {
commit('setComment', payload);
},
fetchHouseholdSuggestionForConcerned({ commit, state }, person) {
fetchHouseholdSuggestionByAccompanyingPeriod(person.id)
.then(households => {
commit('setHouseholdSuggestionByAccompanyingPeriod', households);
});
},
fetchAddressSuggestions({ commit, state }) {
for (let i in state.concerned) {
fetchAddressSuggestionByPerson(state.concerned[i].person.id)
.then(addresses => {
commit('addAddressesSuggestion', addresses);
})
.catch(e => {
console.log(e);
});
}
},
computeWarnings({ commit, state, getters }) {
let warnings = [],
payload;
if (!getters.hasHousehold && !state.forceLeaveWithoutHousehold) {
warnings.push({ m: 'household_members_editor.add_destination', a: {} });
}
if (state.concerned.length === 0) {
warnings.push({ m: 'household_members_editor.add_at_least_onePerson', a: {} });
}
if (getters.concUnpositionned.length > 0
&& !state.forceLeaveWithoutHousehold) {
warnings.push({ m: 'household_members_editor.give_a_position_to_every_person', a: {} })
}
commit('setWarnings', warnings);
},
confirm({ getters, state, commit }) {
let payload = getters.buildPayload,
errors = [],
person_id,
household_id,
error
;
householdMove(payload).then(household => {
if (household === null) {
person_id = getters.persons[0].id;
window.location.replace(`/fr/person/${person_id}/general`);
} else {
if (household.type === 'household') {
household_id = household.id;
// nothing to do anymore here, bye-bye !
window.location.replace(`/fr/person/household/${household_id}/summary`);
} else {
// we assume the answer was 422...
error = household;
for (let i in error.violations) {
let e = error.violations[i];
errors.push(e.title);
}
commit('setErrors', errors);
}
}
});
},
}
});
store.dispatch('computeWarnings');
store.dispatch('fetchAddressSuggestions');
if (concerned.length > 0) {
concerned.forEach(c => {
store.dispatch('fetchHouseholdSuggestionForConcerned', c.person);
});
}
export { store };

View File

@ -7,35 +7,43 @@
</div> </div>
<div v-if="isHouseholdNew && !hasHouseholdAddress"> <div v-if="isHouseholdNew && !hasHouseholdAddress">
<h3>À quelle adresse habite ce ménage ?</h3> <h3 >À quelle adresse habite ce ménage ?</h3>
<ul v-if="filterAddressesSuggestion.length > 0"> <div v-if="filterAddressesSuggestion.length > 0" class="flex-table householdAddressSuggestionList">
<li v-for="a in filterAddressesSuggestion"> <div v-for="a in filterAddressesSuggestion" class="item-bloc">
<show-address :address="a"></show-address> <show-address :address="a"></show-address>
<button class="btn" @click="setHouseholdAddress(a)"> <button class="btn btn-action" @click="setHouseholdAddress(a)">
Le ménage habite cette adresse Le ménage habite cette adresse
</button> </button>
</li> </div>
</ul> </div>
<div v-else>
<span class="chill-no-data-statement">Aucune adresse à suggérer</span>
</div>
<ul class="record_actions"> <ul class="record_actions">
<li > <li >
<button class="btn"> <add-address
Créer une adresse :context="addAddress.context"
</button> :key="addAddress.key"
:options="addAddress.options"
:result="addAddress.result"
@submitAddress="setHouseholdCreatedAddress"
ref="addAddress">
</add-address>
</li> </li>
</ul> </ul>
</div> </div>
<div v-if="isHouseholdNew && hasHouseholdAddress"> <div v-if="isHouseholdNew && hasHouseholdAddress">
<ul class="record_actions"> <ul class="record_actions">
<li > <li >
<button class="btn" @click="removeHouseholdAddress"> <button class="btn btn-misc" @click="removeHouseholdAddress">
Supprimer cette adresse Supprimer cette adresse
</button> </button>
</li> </li>
</ul> </ul>
</div> </div>
</div> </div>
<div v-else-if="isForceLeaveWithoutHousehold"> <div v-else-if="isForceLeaveWithoutHousehold">
@ -51,7 +59,7 @@
class="btn btn-misc" class="btn btn-misc"
@click="toggleHouseholdSuggestion" @click="toggleHouseholdSuggestion"
> >
{{ $tc('household_members_editor.show_household_suggestion', {{ $tc('household_members_editor.show_household_suggestion',
countHouseholdSuggestion) }} countHouseholdSuggestion) }}
</button> </button>
</li> </li>
@ -106,11 +114,24 @@
</div> </div>
</div> </div>
</div> </div>
</template> </template>
<style lang="scss"> <style lang="scss">
div.householdAddressSuggestionList {
/*
display: flex;
list-style-type: none;
padding: 0;
*/
& > li {
}
}
.householdSuggestionList { .householdSuggestionList {
display: flex; display: flex;
flex-direction: row; flex-direction: row;
@ -136,12 +157,45 @@
import { mapGetters, mapState } from 'vuex'; import { mapGetters, mapState } from 'vuex';
import HouseholdViewer from 'ChillPersonAssets/vuejs/_components/Household/Household.vue'; import HouseholdViewer from 'ChillPersonAssets/vuejs/_components/Household/Household.vue';
import ShowAddress from 'ChillMainAssets/vuejs/Address/components/ShowAddress.vue'; import ShowAddress from 'ChillMainAssets/vuejs/Address/components/ShowAddress.vue';
import AddAddress from 'ChillMainAssets/vuejs/Address/components/AddAddress.vue';
export default { export default {
name: 'Household', name: 'Household',
components: { components: {
HouseholdViewer, HouseholdViewer,
ShowAddress, ShowAddress,
AddAddress,
},
data() {
return {
addAddress: {
context: {
entity: {
type: 'household_create',
id: 0
},
edit: false,
addressId: null
},
key: 'household_new',
options: {
hideDateFrom: true,
bindModal: {
},
button: {
text: {
create: null,
edit: null,
}
},
title: {
create: null,
edit: null,
},
}
}
}
}, },
computed: { computed: {
...mapGetters([ ...mapGetters([
@ -172,13 +226,13 @@ export default {
allowRemoveHousehold() { allowRemoveHousehold() {
return this.$store.getters.hasHousehold && return this.$store.getters.hasHousehold &&
( (
this.allowHouseholdCreate || this.allowHouseholdSearch || this.allowHouseholdCreate || this.allowHouseholdSearch ||
this.allowLeaveWithoutHousehold this.allowLeaveWithoutHousehold
) )
; ;
}, },
allowChangeHousehold() { allowChangeHousehold() {
return this.allowHouseholdCreate || this.allowHouseholdSearch || return this.allowHouseholdCreate || this.allowHouseholdSearch ||
this.allowLeaveWithoutHousehold; this.allowLeaveWithoutHousehold;
}, },
isForceLeaveWithoutHousehold() { isForceLeaveWithoutHousehold() {
@ -202,8 +256,15 @@ export default {
this.$store.dispatch('removeHousehold'); this.$store.dispatch('removeHousehold');
}, },
setHouseholdAddress(a) { setHouseholdAddress(a) {
let payload = this.$refs.addAddress.submitNewAddress();
console.log('setHouseholdAddress', a);
this.$store.commit('setHouseholdAddress', a); this.$store.commit('setHouseholdAddress', a);
}, },
setHouseholdCreatedAddress() {
let payload = this.$refs.addAddress.submitNewAddress();
console.log('setHouseholdAddress', payload);
this.$store.dispatch('setHouseholdNewAddress', payload);
},
removeHouseholdAddress() { removeHouseholdAddress() {
this.$store.commit('removeHouseholdAddress'); this.$store.commit('removeHouseholdAddress');
} }

View File

@ -14,8 +14,6 @@ const concerned = window.household_members_editor_data.persons.map(p => {
}; };
}); });
console.log('expand suggestions', window.household_members_editor_expand_suggestions === 1);
const store = createStore({ const store = createStore({
strict: debug, strict: debug,
state: { state: {
@ -211,7 +209,12 @@ const store = createStore({
) )
}, },
createHousehold(state) { createHousehold(state) {
state.household = { type: 'household', members: [], current_address: null, current_members_id: [] } state.household = {
type: 'household',
members: [],
current_address: null,
current_members_id: []
};
state.forceLeaveWithoutHousehold = false; state.forceLeaveWithoutHousehold = false;
}, },
removeHousehold(state) { removeHousehold(state) {
@ -274,7 +277,7 @@ const store = createStore({
state.addressesSuggestion.push(addresses[i]); state.addressesSuggestion.push(addresses[i]);
} }
} }
} },
}, },
actions: { actions: {
addConcerned({ commit, dispatch }, person) { addConcerned({ commit, dispatch }, person) {
@ -307,6 +310,19 @@ const store = createStore({
commit('createHousehold'); commit('createHousehold');
dispatch('computeWarnings'); dispatch('computeWarnings');
}, },
setHouseholdNewAddress({ commit }, payload) {
let url = `/api/1.0/main/address/${payload.addressId}.json`;
window.fetch(url).then(r => {
if (r.ok) {
return r.json();
}
throw new Error("error while fetch address");
}).then(data => {
commit('setHouseholdAddress', data);
}).catch(e => {
console.error(e);
});
},
forceLeaveWithoutHousehold({ commit, dispatch }) { forceLeaveWithoutHousehold({ commit, dispatch }) {
commit('forceLeaveWithoutHousehold'); commit('forceLeaveWithoutHousehold');
dispatch('computeWarnings'); dispatch('computeWarnings');
@ -373,8 +389,14 @@ const store = createStore({
} else { } else {
if (household.type === 'household') { if (household.type === 'household') {
household_id = household.id; household_id = household.id;
// nothing to do anymore here, bye-bye ! // nothing to do anymore here, bye-bye !
window.location.replace(`/fr/person/household/${household_id}/summary`); let params = new URLSearchParams(window.location.search);
if (params.has('returnPath')) {
window.location.replace(params.get('returnPath'));
} else {
window.location.replace(`/fr/person/household/${household_id}/summary`);
}
} else { } else {
// we assume the answer was 422... // we assume the answer was 422...
error = household; error = household;

View File

@ -10,7 +10,7 @@
{{ $t('household_number', { number: household.id } ) }} {{ $t('household_number', { number: household.id } ) }}
</div> </div>
<!-- member part --> <!-- member part -->
<div v-if="hasCurrentMembers" class="members"> <div v-if="hasCurrentMembers" class="members">
<span class="current-members">{{ $t('current_members') }}: </span> <span class="current-members">{{ $t('current_members') }}: </span>
<template v-for="(m, index) in currentMembers()" :key="m.id"> <template v-for="(m, index) in currentMembers()" :key="m.id">
@ -40,7 +40,7 @@
<style lang="scss"> <style lang="scss">
.chill-entity__household { .chill-entity__household {
display: grid; display: grid;
grid-template-areas: grid-template-areas:
"identifier identifier where" "identifier identifier where"
"who who where" "who who where"
; ;
@ -50,7 +50,7 @@
.identifier { .identifier {
grid-area: identifier; grid-area: identifier;
font-size: 1.3em; font-size: 1.3em;
font-weight: 700; font-weight: 700;
color: var(--chill-blue); color: var(--chill-blue);
@ -110,7 +110,7 @@ export default {
return this.household.members.filter(m => this.household.current_members_id.includes(m.id)) return this.household.members.filter(m => this.household.current_members_id.includes(m.id))
.sort((a, b) => { .sort((a, b) => {
if (a.position.ordering < b.position.ordering) { if (a.position.ordering < b.position.ordering) {
return -1; return -1;
} }
if (a.position.ordering > b.position.ordering) { if (a.position.ordering > b.position.ordering) {
return 1; return 1;

View File

@ -14,7 +14,7 @@
<div id="withoutHouseholdList" class="collapse p-3"> <div id="withoutHouseholdList" class="collapse p-3">
<form method="GET" <form method="GET"
action="{{ chill_path_add_return_path('chill_person_household_members_editor') }}"> action="{{ path('chill_person_household_members_editor') }}">
<h3>{{ 'household.Select people to move'|trans }}</h3> <h3>{{ 'household.Select people to move'|trans }}</h3>
<ul> <ul>
@ -27,6 +27,7 @@
</ul> </ul>
<input type="hidden" name="expand_suggestions" value="true" /> <input type="hidden" name="expand_suggestions" value="true" />
<input type="hidden" name="returnPath" value="{{ app.request.requestUri|escape('html_attr') }}" />
<input type="hidden" name="accompanying_period_id" value="{{ accompanyingCourse.id }}" /> <input type="hidden" name="accompanying_period_id" value="{{ accompanyingCourse.id }}" />
<ul class="record_actions mb-0"> <ul class="record_actions mb-0">
<li> <li>

View File

@ -1,5 +1,61 @@
<div class="alert alert-danger alert-with-actions mb-0"> {%- set countPersonLocation = accompanyingCourse.availablePersonLocation|length -%}
<div class="message"> {%- set hasPersonLocation = countPersonLocation|length > 0 -%}
{{ 'This course is located at a temporarily address. You should locate this course to an user'|trans }} {% macro quickLocationForm(accompanyingCourse, person, whichButton) %}
<form method="PATCH" action="{{ path('chill_api_single_accompanying_course__entity', {'id': accompanyingCourse.id, '_format': 'json'}) }}" class="quickLocationForm">
<input type="hidden" name="personLocation" value="{{ person.id }}" />
<input type="hidden" name="periodId" value="{{ accompanyingCourse.id }}" />
{% if whichButton == 'string' %}
<button type="submit" class="btn btn-chill-pink">
<span class="text-light">{{ 'Locate by'|trans }} {{ person|chill_entity_render_string }}</span>
</button>
{% elseif whichButton == 'icon' %}
<button type="submit" class="btn btn-sm btn-secondary">
<i class="fa fa-map-marker"></i>
</button>
{% endif %}
</form>
{% endmacro %}
<div class="border border-danger">
<div class="alert alert-danger {% if hasPersonLocation %}alert-with-actions{% endif %} mb-0">
<div class="message">
{{ 'This course is located at a temporarily address. You should locate this course to an user'|trans }}
{% if not hasPersonLocation %}
{{ 'Associate at least one member with an household, and set an address to this household'|trans }}
{% endif %}
</div>
{% if 1 == countPersonLocation %}
<ul class="record_actions">
<li>
{{ _self.quickLocationForm(accompanyingCourse, accompanyingCourse.availablePersonLocation.first, 'string') }}
</li>
</ul>
{% elseif 1 < countPersonLocation %}
<ul class="record_actions">
<li>
<button class="btn btn-chill-pink" data-bs-toggle="collapse" href="#locateAtPerson">
<i class="fa fa-fw fa-caret-down"></i><span class="text-light">{{ 'Choose a person to locate by'|trans }}</span>
</button>
</li>
</ul>
{% endif %}
</div> </div>
{% if 1 < countPersonLocation %}
<div id="locateAtPerson" class="collapse">
<p>{{ 'Locate by'|trans }}:</p>
<div class="flex-table mb-3">
{% for p in accompanyingCourse.availablePersonLocation %}
<div class="item-bloc">
{{ p|chill_entity_render_box({
'render': 'bloc', 'addLink': false, 'addInfo': true, 'addAltNames': false, 'customButtons': {
'replace': _self.quickLocationForm(accompanyingCourse, p, 'icon')
}
}) }}
</div>
{% endfor %}
</div>
</div>
{% endif %}
</div> </div>

View File

@ -15,6 +15,11 @@
{% endif %} {% endif %}
{% endmacro %} {% endmacro %}
{% block js %}
{{ parent() }}
{{ encore_entry_script_tags('page_accompanying_course_index_person_locate') }}
{% endblock %}
{% block content %} {% block content %}
<div class="accompanyingcourse-resume"> <div class="accompanyingcourse-resume">
@ -27,11 +32,6 @@
</a> </a>
</span> </span>
</div> </div>
{% if accompanyingCourse.locationStatus == 'address' or accompanyingCourse.locationStatus == 'none' %}
<div class="alert alert-danger">
{{ 'This course is located at a temporarily address. You should locate this course to an user'|trans }}
</div>
{% endif %}
{% endif %} {% endif %}
<h1>{{ 'Resume Accompanying Course'|trans }}</h1> <h1>{{ 'Resume Accompanying Course'|trans }}</h1>
@ -54,12 +54,29 @@
{% if withoutHousehold|length > 0 %} {% if withoutHousehold|length > 0 %}
{% include '@ChillPerson/AccompanyingCourse/_join_household.html.twig' with {} %} {% include '@ChillPerson/AccompanyingCourse/_join_household.html.twig' with {} %}
{% endif %} {% endif %}
{% if accompanyingCourse.locationStatus == 'address' or accompanyingCourse.locationStatus == 'none' %}
{% include '@ChillPerson/AccompanyingCourse/_warning_address.html.twig' with {} %}
{% endif %}
{% endif %} {% endif %}
</div> </div>
<div class="location mb-5">
<h2 class="mb-3">{{ 'Accompanying course location'|trans }}</h2>
{% if accompanyingCourse.locationStatus == 'person' %}
<p>{{ 'This course is located by'|trans }}: {{ accompanyingCourse.personLocation|chill_entity_render_string }}</p>
{% elseif accompanyingCourse.locationStatus == 'address' %}
<p>{{ 'This course has a temporarily location'|trans }}</p>
{% endif %}
{% if accompanyingCourse.locationStatus != 'none' %}
{{ accompanyingCourse.location|chill_entity_render_box }}
{% endif %}
{% if accompanyingCourse.locationStatus == 'address' or accompanyingCourse.locationStatus == 'none' %}
{% include '@ChillPerson/AccompanyingCourse/_warning_address.html.twig' with {} %}
{% endif %}
</div>
<div class="requestor mb-5"> <div class="requestor mb-5">
<h2 class="mb-3">{{ 'Requestor'|trans }}</h2> <h2 class="mb-3">{{ 'Requestor'|trans }}</h2>
{% if accompanyingCourse.requestorPerson is not empty %} {% if accompanyingCourse.requestorPerson is not empty %}

View File

@ -1,4 +1,4 @@
{% extends accompanyingCourse != null ? '@ChillPerson/AccompanyingCourse/layout.html.twig' {% extends accompanyingCourse != null ? '@ChillPerson/AccompanyingCourse/layout.html.twig'
: '@ChillMain/layout.html.twig' %} : '@ChillMain/layout.html.twig' %}
{% block title 'household.Edit household members'|trans %} {% block title 'household.Edit household members'|trans %}
@ -13,11 +13,12 @@
{% endblock %} {% endblock %}
{% block js %} {% block js %}
<script type="text/javascript"> {{ parent() }}
window.household_members_editor_data = {{ data|json_encode|raw }}; <script type="text/javascript">
window.household_members_editor_expand_suggestions = {{ expandSuggestions }}; window.household_members_editor_data = {{ data|json_encode|raw }};
</script> window.household_members_editor_expand_suggestions = {{ expandSuggestions }};
{{ encore_entry_script_tags('vue_household_members_editor') }} </script>
{{ encore_entry_script_tags('vue_household_members_editor') }}
{% endblock %} {% endblock %}
{% block css %} {% block css %}

View File

@ -16,4 +16,5 @@ module.exports = function(encore, entries)
encore.addEntry('page_household_edit_metadata', __dirname + '/Resources/public/page/household_edit_metadata/index.js'); encore.addEntry('page_household_edit_metadata', __dirname + '/Resources/public/page/household_edit_metadata/index.js');
encore.addEntry('page_person', __dirname + '/Resources/public/page/person/index.js'); encore.addEntry('page_person', __dirname + '/Resources/public/page/person/index.js');
encore.addEntry('page_accompanying_course_index_person_locate', __dirname + '/Resources/public/page/accompanying_course_index/person_locate.js');
}; };

View File

@ -376,6 +376,12 @@ Edit Accompanying Course: Modifier le parcours
Create Accompanying Course: Créer un nouveau parcours Create Accompanying Course: Créer un nouveau parcours
Drop Accompanying Course: Supprimer le parcours Drop Accompanying Course: Supprimer le parcours
This course is located at a temporarily address. You should locate this course to an user: Ce parcours est localisé à une adresse temporaire. Il devrait être localisé auprès d'un usager concerné. This course is located at a temporarily address. You should locate this course to an user: Ce parcours est localisé à une adresse temporaire. Il devrait être localisé auprès d'un usager concerné.
Accompanying course location: Localisation du parcours
This course is located by: Ce parcours est localisé auprès de
This course has a temporarily location: Ce parcours a une localisation temporaire
Choose a person to locate by: Localiser auprès d'un usager concerné
Associate at least one member with an household, and set an address to this household: Associez au moins un membre du parcours à un ménage, et indiquez une adresse à ce ménage.
Locate by: Localiser auprès de
# Household # Household
Household: Ménage Household: Ménage