mirror of
https://gitlab.com/Chill-Projet/chill-bundles.git
synced 2025-06-14 14:24:24 +00:00
Merge branch 'features/edit-accompanying-period-social-work' into deploy/quick-fixes
This commit is contained in:
commit
5ded4822a2
@ -4,6 +4,7 @@ namespace Chill\PersonBundle\Entity\AccompanyingPeriod;
|
|||||||
|
|
||||||
use Chill\PersonBundle\Entity\SocialWork\Result;
|
use Chill\PersonBundle\Entity\SocialWork\Result;
|
||||||
use Chill\PersonBundle\Entity\SocialWork\SocialAction;
|
use Chill\PersonBundle\Entity\SocialWork\SocialAction;
|
||||||
|
use Chill\PersonBundle\Entity\Person;
|
||||||
use Chill\MainBundle\Entity\User;
|
use Chill\MainBundle\Entity\User;
|
||||||
use Chill\PersonBundle\Entity\AccompanyingPeriod;
|
use Chill\PersonBundle\Entity\AccompanyingPeriod;
|
||||||
use Chill\ThirdPartyBundle\Entity\ThirdParty;
|
use Chill\ThirdPartyBundle\Entity\ThirdParty;
|
||||||
@ -44,6 +45,7 @@ use Symfony\Component\Validator\Constraints as Assert;
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* @ORM\ManyToOne(targetEntity=AccompanyingPeriod::class)
|
* @ORM\ManyToOne(targetEntity=AccompanyingPeriod::class)
|
||||||
|
* @Serializer\Groups({"read"})
|
||||||
*/
|
*/
|
||||||
private ?AccompanyingPeriod $accompanyingPeriod = null;
|
private ?AccompanyingPeriod $accompanyingPeriod = null;
|
||||||
|
|
||||||
@ -148,11 +150,22 @@ use Symfony\Component\Validator\Constraints as Assert;
|
|||||||
*/
|
*/
|
||||||
private Collection $thirdParties;
|
private Collection $thirdParties;
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* @ORM\ManyToMany(targetEntity=Person::class)
|
||||||
|
* @ORM\JoinTable(name="chill_person_accompanying_period_work_person")
|
||||||
|
* @Serializer\Groups({"read"})
|
||||||
|
* @Serializer\Groups({"accompanying_period_work:edit"})
|
||||||
|
* @Serializer\Groups({"accompanying_period_work:create"})
|
||||||
|
*/
|
||||||
|
private Collection $persons;
|
||||||
|
|
||||||
public function __construct()
|
public function __construct()
|
||||||
{
|
{
|
||||||
$this->goals = new ArrayCollection();
|
$this->goals = new ArrayCollection();
|
||||||
$this->results = new ArrayCollection();
|
$this->results = new ArrayCollection();
|
||||||
$this->thirdParties = new ArrayCollection();
|
$this->thirdParties = new ArrayCollection();
|
||||||
|
$this->persons = new ArrayCollection();
|
||||||
}
|
}
|
||||||
|
|
||||||
public function getId(): ?int
|
public function getId(): ?int
|
||||||
@ -390,4 +403,25 @@ use Symfony\Component\Validator\Constraints as Assert;
|
|||||||
|
|
||||||
return $this;
|
return $this;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public function getPersons(): Collection
|
||||||
|
{
|
||||||
|
return $this->persons;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function addPerson(Person $person): self
|
||||||
|
{
|
||||||
|
if (!$this->persons->contains($person)) {
|
||||||
|
$this->persons[] = $person;
|
||||||
|
}
|
||||||
|
|
||||||
|
return $this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function removePerson(Person $person): self
|
||||||
|
{
|
||||||
|
$this->persons->removeElement($person);
|
||||||
|
|
||||||
|
return $this;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -28,8 +28,19 @@
|
|||||||
<div v-if="isLoadingSocialActions">
|
<div v-if="isLoadingSocialActions">
|
||||||
<p>spinner</p>
|
<p>spinner</p>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
<div v-if="hasSocialActionPicked" id="persons">
|
||||||
|
<h2>{{ $t('persons_involved') }}</h2>
|
||||||
|
|
||||||
|
<ul>
|
||||||
|
<li v-for="p in personsReachables" :key="p.id">
|
||||||
|
<input type="checkbox" :value="p.id" v-model="personsPicked">
|
||||||
|
<person :person="p"></person>
|
||||||
|
</li>
|
||||||
|
</ul>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
</div>
|
||||||
<div v-if="hasSocialActionPicked" id="start_date">
|
<div v-if="hasSocialActionPicked" id="start_date">
|
||||||
<p><label>{{ $t('startDate') }}</label> <input type="date" v-model="startDate" /></p>
|
<p><label>{{ $t('startDate') }}</label> <input type="date" v-model="startDate" /></p>
|
||||||
</div>
|
</div>
|
||||||
@ -85,6 +96,14 @@
|
|||||||
|
|
||||||
#picking {
|
#picking {
|
||||||
grid-area: picking;
|
grid-area: picking;
|
||||||
|
|
||||||
|
#persons {
|
||||||
|
ul {
|
||||||
|
padding: 0;
|
||||||
|
|
||||||
|
list-style-type: none;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#start_date {
|
#start_date {
|
||||||
@ -105,6 +124,7 @@
|
|||||||
import { mapState, mapActions, mapGetters } from 'vuex';
|
import { mapState, mapActions, mapGetters } from 'vuex';
|
||||||
import VueMultiselect from 'vue-multiselect';
|
import VueMultiselect from 'vue-multiselect';
|
||||||
import { dateToISO, ISOToDate } from 'ChillMainAssets/js/date.js';
|
import { dateToISO, ISOToDate } from 'ChillMainAssets/js/date.js';
|
||||||
|
import Person from 'ChillPersonAssets/vuejs/_components/Person/Person.vue';
|
||||||
|
|
||||||
const i18n = {
|
const i18n = {
|
||||||
messages: {
|
messages: {
|
||||||
@ -115,6 +135,7 @@ const i18n = {
|
|||||||
pick_social_issue: "Choisir une problématique sociale",
|
pick_social_issue: "Choisir une problématique sociale",
|
||||||
pick_an_action: "Choisir une action d'accompagnement",
|
pick_an_action: "Choisir une action d'accompagnement",
|
||||||
pick_social_issue_linked_with_action: "Indiquez la problématique sociale liée à l'action d'accompagnement",
|
pick_social_issue_linked_with_action: "Indiquez la problématique sociale liée à l'action d'accompagnement",
|
||||||
|
persons_involved: "Usagers concernés",
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -124,6 +145,7 @@ export default {
|
|||||||
name: 'App',
|
name: 'App',
|
||||||
components: {
|
components: {
|
||||||
VueMultiselect,
|
VueMultiselect,
|
||||||
|
Person,
|
||||||
},
|
},
|
||||||
methods: {
|
methods: {
|
||||||
submit() {
|
submit() {
|
||||||
@ -136,6 +158,7 @@ export default {
|
|||||||
'socialIssues',
|
'socialIssues',
|
||||||
'socialActionsReachables',
|
'socialActionsReachables',
|
||||||
'errors',
|
'errors',
|
||||||
|
'personsReachables',
|
||||||
]),
|
]),
|
||||||
...mapGetters([
|
...mapGetters([
|
||||||
'hasSocialIssuePicked',
|
'hasSocialIssuePicked',
|
||||||
@ -144,6 +167,18 @@ export default {
|
|||||||
'isPostingWork',
|
'isPostingWork',
|
||||||
'hasErrors',
|
'hasErrors',
|
||||||
]),
|
]),
|
||||||
|
personsPicked: {
|
||||||
|
get() {
|
||||||
|
let s = this.$store.state.personsPicked.map(p => p.id);
|
||||||
|
console.log('persons picked', s);
|
||||||
|
|
||||||
|
return s;
|
||||||
|
},
|
||||||
|
set(v) {
|
||||||
|
console.log('persons picked', v);
|
||||||
|
this.$store.commit('setPersonsPickedIds', v);
|
||||||
|
}
|
||||||
|
},
|
||||||
socialIssuePicked: {
|
socialIssuePicked: {
|
||||||
get() {
|
get() {
|
||||||
let s = this.$store.state.socialIssuePicked;
|
let s = this.$store.state.socialIssuePicked;
|
||||||
|
@ -14,6 +14,10 @@ const store = createStore({
|
|||||||
socialIssuePicked: null,
|
socialIssuePicked: null,
|
||||||
socialActionsReachables: [],
|
socialActionsReachables: [],
|
||||||
socialActionPicked: null,
|
socialActionPicked: null,
|
||||||
|
personsPicked: window.accompanyingCourse.participations.filter(p => p.endDate == null)
|
||||||
|
.map(p => p.person),
|
||||||
|
personsReachables: window.accompanyingCourse.participations.filter(p => p.endDate == null)
|
||||||
|
.map(p => p.person),
|
||||||
startDate: new Date(),
|
startDate: new Date(),
|
||||||
endDate: null,
|
endDate: null,
|
||||||
isLoadingSocialActions: false,
|
isLoadingSocialActions: false,
|
||||||
@ -44,9 +48,17 @@ const store = createStore({
|
|||||||
},
|
},
|
||||||
startDate: {
|
startDate: {
|
||||||
datetime: datetimeToISO(state.startDate)
|
datetime: datetimeToISO(state.startDate)
|
||||||
}
|
},
|
||||||
|
persons: []
|
||||||
};
|
};
|
||||||
|
|
||||||
|
for (let i in state.personsPicked) {
|
||||||
|
payload.persons.push({
|
||||||
|
id: state.personsPicked[i].id,
|
||||||
|
type: 'person'
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
if (null !== state.endDate) {
|
if (null !== state.endDate) {
|
||||||
payload.endDate = {
|
payload.endDate = {
|
||||||
datetime: datetimeToISO(state.endDate)
|
datetime: datetimeToISO(state.endDate)
|
||||||
@ -71,12 +83,16 @@ const store = createStore({
|
|||||||
state.socialActionPicked = socialAction;
|
state.socialActionPicked = socialAction;
|
||||||
},
|
},
|
||||||
setSocialIssue(state, socialIssueId) {
|
setSocialIssue(state, socialIssueId) {
|
||||||
|
console.log('set social issue', socialIssueId);
|
||||||
if (socialIssueId === null) {
|
if (socialIssueId === null) {
|
||||||
state.socialIssuePicked = null;
|
state.socialIssuePicked = null;
|
||||||
return;
|
} else {
|
||||||
}
|
let mapped = state.socialIssues
|
||||||
state.socialIssuePicked = state.socialIssues
|
|
||||||
.find(e => e.id === socialIssueId);
|
.find(e => e.id === socialIssueId);
|
||||||
|
console.log('mapped', mapped);
|
||||||
|
state.socialIssuePicked = mapped;
|
||||||
|
console.log('social issue setted', state.socialIssuePicked);
|
||||||
|
}
|
||||||
},
|
},
|
||||||
setIsLoadingSocialActions(state, s) {
|
setIsLoadingSocialActions(state, s) {
|
||||||
state.isLoadingSocialActions = s;
|
state.isLoadingSocialActions = s;
|
||||||
@ -90,6 +106,11 @@ const store = createStore({
|
|||||||
setEndDate(state, date) {
|
setEndDate(state, date) {
|
||||||
state.endDate = date;
|
state.endDate = date;
|
||||||
},
|
},
|
||||||
|
setPersonsPickedIds(state, ids) {
|
||||||
|
console.log('persons ids', ids);
|
||||||
|
state.personsPicked = state.personsReachables
|
||||||
|
.filter(p => ids.includes(p.id))
|
||||||
|
},
|
||||||
addErrors(state, { errors, cancel_posting }) {
|
addErrors(state, { errors, cancel_posting }) {
|
||||||
console.log('add errors', errors);
|
console.log('add errors', errors);
|
||||||
state.errors = errors;
|
state.errors = errors;
|
||||||
@ -103,8 +124,10 @@ const store = createStore({
|
|||||||
console.log('pick social issue');
|
console.log('pick social issue');
|
||||||
|
|
||||||
commit('setIsLoadingSocialActions', true);
|
commit('setIsLoadingSocialActions', true);
|
||||||
commit('setSocialIssue', null);
|
commit('setSocialAction', null);
|
||||||
commit('setSocialActionsReachables', []);
|
commit('setSocialActionsReachables', []);
|
||||||
|
commit('setSocialIssue', null);
|
||||||
|
|
||||||
|
|
||||||
findSocialActionsBySocialIssue(socialIssueId).then(
|
findSocialActionsBySocialIssue(socialIssueId).then(
|
||||||
(response) => {
|
(response) => {
|
||||||
|
@ -1,31 +1,162 @@
|
|||||||
<template>
|
<template>
|
||||||
<h1>Hello</h1>
|
|
||||||
|
|
||||||
<div id="workEditor">
|
<div id="workEditor">
|
||||||
<div>
|
<div id="title">
|
||||||
<label>{{ $t('action_title') }}</label>
|
<label class="action_title_label">{{ $t('action_title') }}</label>
|
||||||
<p>{{ work.socialAction.text }}</p>
|
<p class="action_title">{{ work.socialAction.text }}</p>
|
||||||
</div>
|
</div>
|
||||||
<div>
|
<div id="startDate">
|
||||||
<label>{{ $t('startDate') }}</label>
|
<label>{{ $t('startDate') }}</label>
|
||||||
<input type="date" v-model="startDate" />
|
<input type="date" v-model="startDate" />
|
||||||
</div>
|
</div>
|
||||||
<div>
|
<div id="endDate">
|
||||||
<label>{{ $t('endDate') }}</label>
|
<label>{{ $t('endDate') }}</label>
|
||||||
<input type="date" v-model="endDate" />
|
<input type="date" v-model="endDate" />
|
||||||
</div>
|
</div>
|
||||||
<div>
|
<div id="comment">
|
||||||
|
<label>Commentaire</label>
|
||||||
<ckeditor
|
<ckeditor
|
||||||
:editor="editor"
|
:editor="editor"
|
||||||
v-model="note"
|
v-model="note"
|
||||||
tag-name="textarea"
|
tag-name="textarea"
|
||||||
></ckeditor>
|
></ckeditor>
|
||||||
</div>
|
</div>
|
||||||
<div class="objectives_list">
|
<div id="objectives" class="objectives_list">
|
||||||
<div class="title" aria="hidden">
|
<div class="title" aria="hidden">
|
||||||
<div><h3>Objectifs</h3></div>
|
<div><h3>Motifs - objectifs - dispositifs</h3></div>
|
||||||
<div><h3>Résultats</h3></div>
|
<div><h3>Orientations - résultats</h3></div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
<!-- results which are not attached to an objective -->
|
||||||
|
<div v-if="hasResultsForAction">
|
||||||
|
<div class="results_without_objective">
|
||||||
|
{{ $t('results_without_objective') }}
|
||||||
|
</div>
|
||||||
|
<div>
|
||||||
|
<add-result :availableResults="resultsForAction" destination="action"></add-result>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- results which **are** attached to an objective -->
|
||||||
|
<div v-for="g in goalsPicked">
|
||||||
|
<div>
|
||||||
|
<div @click="removeGoal(g)" class="objective-title">
|
||||||
|
<i class="fa fa-times"></i>
|
||||||
|
{{ g.title.fr }}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div>
|
||||||
|
<add-result destination="goal" :goal="g"></add-result>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- box to add goal -->
|
||||||
|
<div class="add_goal">
|
||||||
|
<div>
|
||||||
|
<div v-if="showAddObjective">
|
||||||
|
|
||||||
|
<p>Motifs, objectifs et dispositifs disponibles pour ajout :</p>
|
||||||
|
|
||||||
|
<ul class="list-objectives">
|
||||||
|
<li v-for="g in availableForCheckGoal" @click="addGoal(g)" class="badge badge-primary">
|
||||||
|
<i class="fa fa-plus"></i>
|
||||||
|
{{ g.title.fr }}
|
||||||
|
</li>
|
||||||
|
</ul>
|
||||||
|
</div>
|
||||||
|
<ul class="record_actions">
|
||||||
|
<li>
|
||||||
|
<button @click="toggleAddObjective" class="sc-button bt-create">
|
||||||
|
Ajouter un objectif
|
||||||
|
</button>
|
||||||
|
</li>
|
||||||
|
</ul>
|
||||||
|
</div>
|
||||||
|
<div><!-- empty for results --></div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
|
||||||
|
<div id="persons">
|
||||||
|
<h2>{{ $t('persons_involved') }}</h2>
|
||||||
|
|
||||||
|
<ul>
|
||||||
|
<li v-for="p in personsReachables" :key="p.id">
|
||||||
|
<input type="checkbox" :value="p.id" v-model="personsPicked">
|
||||||
|
<person :person="p"></person>
|
||||||
|
</li>
|
||||||
|
</ul>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
|
||||||
|
<div id="handlingThirdParty">
|
||||||
|
<h2>Tiers traitant</h2>
|
||||||
|
|
||||||
|
<div v-if="!hasHandlingThirdParty">
|
||||||
|
<p class="chill-no-data-statement">
|
||||||
|
Aucun tiers traitant
|
||||||
|
</p>
|
||||||
|
|
||||||
|
<ul class="record_actions">
|
||||||
|
<li>
|
||||||
|
<add-persons
|
||||||
|
buttonTitle="Indiquer un tiers traitant"
|
||||||
|
modalTitle="Choisir un tiers"
|
||||||
|
v-bind:key="handlingThirdPartyPicker.key"
|
||||||
|
v-bind:options="handlingThirdPartyPicker.options"
|
||||||
|
@addNewPersons="setHandlingThirdParty"
|
||||||
|
ref="handlingThirdPartyPicker"> <!-- to cast child method -->
|
||||||
|
</add-persons>
|
||||||
|
</li>
|
||||||
|
</ul>
|
||||||
|
</div>
|
||||||
|
<div v-else>
|
||||||
|
<p>{{ handlingThirdParty.text }}</p>
|
||||||
|
<show-address :address="handlingThirdParty.address"></show-address>
|
||||||
|
|
||||||
|
<ul class="record_actions">
|
||||||
|
<li>
|
||||||
|
<button class="sc-button bt-delete" @click="removeHandlingThirdParty">
|
||||||
|
Supprimer le tiers traitant
|
||||||
|
</button>
|
||||||
|
</li>
|
||||||
|
</ul>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
</div>
|
||||||
|
|
||||||
|
|
||||||
|
<div id="thirdParties">
|
||||||
|
<h2>Tiers intervenants</h2>
|
||||||
|
|
||||||
|
<div v-if="!hasThirdParties">
|
||||||
|
<p class="chill-no-data-statement">Aucun tiers intervenant</p>
|
||||||
|
|
||||||
|
</div>
|
||||||
|
<div v-else>
|
||||||
|
<ul>
|
||||||
|
<li v-for="t in thirdParties">
|
||||||
|
<p>{{ t.text }}</p>
|
||||||
|
<show-address :address="t.address"></show-address>
|
||||||
|
|
||||||
|
<ul class="record_actions">
|
||||||
|
<button class="sc-button bt-delete" @click="removeThirdParty(t)"></button>
|
||||||
|
</ul>
|
||||||
|
</li>
|
||||||
|
</ul>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<ul class="record_actions">
|
||||||
|
<li>
|
||||||
|
<add-persons
|
||||||
|
buttonTitle="Ajouter des tiers"
|
||||||
|
modalTitle="Choisir des tiers"
|
||||||
|
v-bind:key="thirdPartyPicker.key"
|
||||||
|
v-bind:options="thirdPartyPicker.options"
|
||||||
|
@addNewPersons="addThirdParties"
|
||||||
|
ref="thirdPartyPicker"> <!-- to cast child method -->
|
||||||
|
</add-persons>
|
||||||
|
</li>
|
||||||
|
</ul>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
@ -33,29 +164,208 @@
|
|||||||
|
|
||||||
<style lang="scss">
|
<style lang="scss">
|
||||||
|
|
||||||
|
#workEditor {
|
||||||
|
display: grid;
|
||||||
|
grid-template-areas:
|
||||||
|
"title title"
|
||||||
|
"startDate endDate"
|
||||||
|
"comment comment"
|
||||||
|
"objectives objectives"
|
||||||
|
"persons persons"
|
||||||
|
"handling handling"
|
||||||
|
"tparties tparties"
|
||||||
|
;
|
||||||
|
|
||||||
|
grid-template-columns: 50%;
|
||||||
|
column-gap: 1rem;
|
||||||
|
|
||||||
|
#title {
|
||||||
|
grid-area: title;
|
||||||
|
|
||||||
|
.action_title_label {
|
||||||
|
margin-bottom: 0;
|
||||||
|
}
|
||||||
|
.action_title {
|
||||||
|
margin-top: 0;
|
||||||
|
font-weight: bold;
|
||||||
|
font-size: 1.5rem;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
#startDate {
|
||||||
|
grid-area: startDate;
|
||||||
|
}
|
||||||
|
#endDate {
|
||||||
|
grid-area: endDate;
|
||||||
|
}
|
||||||
|
#comment {
|
||||||
|
grid-area: comment;
|
||||||
|
}
|
||||||
|
#objectives {
|
||||||
|
grid-area: objectives;
|
||||||
|
|
||||||
|
> div.title {
|
||||||
|
background-color: var(--chill-light-gray);
|
||||||
|
color: white;
|
||||||
|
|
||||||
|
h3 {
|
||||||
|
text-align: center;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
> div {
|
||||||
|
display: grid;
|
||||||
|
grid-template-areas: "obj res";
|
||||||
|
grid-template-columns: 50%;
|
||||||
|
column-gap: 1rem;
|
||||||
|
|
||||||
|
> div {
|
||||||
|
&:nth-child(1) {
|
||||||
|
grid-area: obj;
|
||||||
|
}
|
||||||
|
|
||||||
|
&:nth-child(2) {
|
||||||
|
grid-area: res;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
> div.results_without_objective {
|
||||||
|
background: repeating-linear-gradient(
|
||||||
|
45deg,
|
||||||
|
var(--chill-light-gray),
|
||||||
|
var(--chill-light-gray) 10px,
|
||||||
|
var(--chill-llight-gray) 10px,
|
||||||
|
var(--chill-llight-gray) 20px
|
||||||
|
);
|
||||||
|
|
||||||
|
text-align: center;
|
||||||
|
font-weight: 700;
|
||||||
|
padding-top: 1.5rem;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
ul.list-objectives {
|
||||||
|
list-style-type: none;
|
||||||
|
padding: 0;
|
||||||
|
|
||||||
|
li {
|
||||||
|
margin: 0.5rem;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
div.objective-title {
|
||||||
|
margin-top: 1rem;
|
||||||
|
font-size: 1.5rem;
|
||||||
|
font-weight: bold;
|
||||||
|
text-align: center;
|
||||||
|
|
||||||
|
|
||||||
|
i.fa {
|
||||||
|
padding: 0.25rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
i.fa-times {
|
||||||
|
background-color: red;
|
||||||
|
color: white;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
li.badge, div.badge {
|
||||||
|
padding-bottom: 0;
|
||||||
|
padding-top: 0;
|
||||||
|
padding-left: 0;
|
||||||
|
|
||||||
|
i.fa {
|
||||||
|
padding: 0.25rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
i.fa-plus {
|
||||||
|
background-color: green;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
#persons {
|
||||||
|
grid-area: persons;
|
||||||
|
}
|
||||||
|
#handlingThirdParty {
|
||||||
|
grid-area: handling;
|
||||||
|
}
|
||||||
|
#thirdParties {
|
||||||
|
grid-area: tparties;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
</style>
|
</style>
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
|
|
||||||
import { mapState } from 'vuex';
|
import { mapState, mapGetters, } from 'vuex';
|
||||||
import { dateToISO, ISOToDatetime } from 'ChillMainAssets/js/date.js';
|
import { dateToISO, ISOToDate, ISOToDatetime } from 'ChillMainAssets/js/date.js';
|
||||||
import CKEditor from '@ckeditor/ckeditor5-vue';
|
import CKEditor from '@ckeditor/ckeditor5-vue';
|
||||||
import ClassicEditor from 'ChillMainAssets/modules/ckeditor5/index.js';
|
import ClassicEditor from 'ChillMainAssets/modules/ckeditor5/index.js';
|
||||||
|
import AddResult from './_components/AddResult.vue';
|
||||||
|
import Person from 'ChillPersonAssets/vuejs/_components/Person/Person.vue';
|
||||||
|
import AddPersons from 'ChillPersonAssets/vuejs/_components/AddPersons.vue';
|
||||||
|
import ShowAddress from 'ChillMainAssets/vuejs/_components/ShowAddress.vue';
|
||||||
|
|
||||||
|
const i18n = {
|
||||||
|
messages: {
|
||||||
|
fr: {
|
||||||
|
action_title: "Action d'accompagnement",
|
||||||
|
startDate: "Date de début",
|
||||||
|
endDate: "Date de fin",
|
||||||
|
results_without_objective: "Résultats sans objectifs",
|
||||||
|
add_objectif: "Ajouter un motif - objectif - dispositif",
|
||||||
|
persons_involved: "Usagers concernés",
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
name: 'App',
|
name: 'App',
|
||||||
components: {
|
components: {
|
||||||
ckeditor: CKEditor.component,
|
ckeditor: CKEditor.component,
|
||||||
|
AddResult,
|
||||||
|
AddPersons,
|
||||||
|
Person,
|
||||||
|
ShowAddress,
|
||||||
},
|
},
|
||||||
|
i18n,
|
||||||
data() {
|
data() {
|
||||||
return {
|
return {
|
||||||
editor: ClassicEditor,
|
editor: ClassicEditor,
|
||||||
|
showAddObjective: false,
|
||||||
|
handlingThirdPartyPicker: {
|
||||||
|
key: 'handling-third-party',
|
||||||
|
options: {
|
||||||
|
type: [ 'thirdparty' ],
|
||||||
|
priority: null,
|
||||||
|
uniq: true
|
||||||
|
},
|
||||||
|
},
|
||||||
|
thirdPartyPicker: {
|
||||||
|
key: 'third-party',
|
||||||
|
options: {
|
||||||
|
type: [ 'thirdparty' ],
|
||||||
|
priority: null,
|
||||||
|
uniq: false,
|
||||||
|
},
|
||||||
|
}
|
||||||
};
|
};
|
||||||
},
|
},
|
||||||
computed: {
|
computed: {
|
||||||
...mapState([
|
...mapState([
|
||||||
'work'
|
'work',
|
||||||
|
'resultsForAction',
|
||||||
|
'goalsPicked',
|
||||||
|
'personsReachables',
|
||||||
|
'handlingThirdParty',
|
||||||
|
'thirdParties'
|
||||||
|
]),
|
||||||
|
...mapGetters([
|
||||||
|
'hasResultsForAction',
|
||||||
|
'hasHandlingThirdParty',
|
||||||
|
'hasThirdParties',
|
||||||
]),
|
]),
|
||||||
startDate: {
|
startDate: {
|
||||||
get() {
|
get() {
|
||||||
@ -63,7 +373,7 @@ export default {
|
|||||||
return dateToISO(this.$store.state.startDate);
|
return dateToISO(this.$store.state.startDate);
|
||||||
},
|
},
|
||||||
set(v) {
|
set(v) {
|
||||||
this.$store.mutate('setStartDate', ISOToDate(v));
|
this.$store.commit('setStartDate', ISOToDate(v));
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
endDate: {
|
endDate: {
|
||||||
@ -72,7 +382,7 @@ export default {
|
|||||||
return dateToISO(this.$store.state.endDate);
|
return dateToISO(this.$store.state.endDate);
|
||||||
},
|
},
|
||||||
set(v) {
|
set(v) {
|
||||||
this.$store.mutate('setEndDate', ISOToDate(v));
|
this.$store.commit('setEndDate', ISOToDate(v));
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
note: {
|
note: {
|
||||||
@ -80,9 +390,61 @@ export default {
|
|||||||
return this.$store.state.note;
|
return this.$store.state.note;
|
||||||
},
|
},
|
||||||
set(v) {
|
set(v) {
|
||||||
this.$store.mutate('setNote', note);
|
this.$store.commit('setNote', note);
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
availableForCheckGoal() {
|
||||||
|
let pickedIds = this.$store.state.goalsPicked.map(g => g.id);
|
||||||
|
console.log('pickeds goals id', pickedIds);
|
||||||
|
console.log(this.$store.state.goalsForAction);
|
||||||
|
|
||||||
|
return this.$store.state.goalsForAction.filter(g => !pickedIds.includes(g.id));
|
||||||
|
},
|
||||||
|
personsPicked: {
|
||||||
|
get() {
|
||||||
|
let s = this.$store.state.personsPicked.map(p => p.id);
|
||||||
|
console.log('persons picked', s);
|
||||||
|
|
||||||
|
return s;
|
||||||
|
},
|
||||||
|
set(v) {
|
||||||
|
console.log('persons picked', v);
|
||||||
|
this.$store.commit('setPersonsPickedIds', v);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
},
|
||||||
|
methods: {
|
||||||
|
toggleAddObjective() {
|
||||||
|
this.showAddObjective = !this.showAddObjective;
|
||||||
|
},
|
||||||
|
addGoal(g) {
|
||||||
|
console.log('add Goal', g);
|
||||||
|
this.$store.commit('addGoal', g);
|
||||||
|
},
|
||||||
|
removeGoal(g) {
|
||||||
|
console.log('remove goal', g);
|
||||||
|
this.$store.commit('removeGoal', g);
|
||||||
|
},
|
||||||
|
setHandlingThirdParty({ selected, modal }) {
|
||||||
|
console.log('setHandlingThirdParty', selected);
|
||||||
|
this.$store.commit('setHandlingThirdParty', selected.shift().result);
|
||||||
|
this.$refs.handlingThirdPartyPicker.resetSearch();
|
||||||
|
modal.showModal = false;
|
||||||
|
},
|
||||||
|
removeHandlingThirdParty() {
|
||||||
|
console.log('removeHandlingThirdParty');
|
||||||
|
this.$store.commit('setHandlingThirdParty', null);
|
||||||
|
},
|
||||||
|
addThirdParties({ selected, modal}) {
|
||||||
|
console.log('addThirdParties', selected);
|
||||||
|
this.$store.commit('addThirdParties', selected.map(r => r.result));
|
||||||
|
this.$refs.thirdPartyPicker.resetSearch();
|
||||||
|
modal.showModal = false;
|
||||||
|
},
|
||||||
|
removeThirdParty(t) {
|
||||||
|
console.log('remove third party', t);
|
||||||
|
this.$store.commit('removeThirdParty', t);
|
||||||
|
},
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -0,0 +1,166 @@
|
|||||||
|
<template>
|
||||||
|
<div class="addResult" v-if="hasResult">
|
||||||
|
<p class="chill-no-data-statement" v-if="pickedResults.length ===0">
|
||||||
|
Aucun résultat associé
|
||||||
|
</p>
|
||||||
|
<ul class="list-results">
|
||||||
|
<li v-for="r in pickedResults" @click="removeResult(r)" class="badge badge-primary">
|
||||||
|
<i class="fa fa-times"></i>
|
||||||
|
{{ r.title.fr }}
|
||||||
|
</li>
|
||||||
|
<template v-if="isExpanded">
|
||||||
|
<li v-for="r in availableForCheckResults" @click="addResult(r)" class="badge badge-primary">
|
||||||
|
<i class="fa fa-plus"></i>
|
||||||
|
{{ r.title.fr }}
|
||||||
|
</li>
|
||||||
|
</template>
|
||||||
|
</ul>
|
||||||
|
<ul class="record_actions">
|
||||||
|
<li v-if="isExpanded">
|
||||||
|
<button @click="toggleSelect" class="sc-button bt-hide" >
|
||||||
|
<i class="fa fa-eye-slash"></i>
|
||||||
|
Masquer résultats et orientations disponibles
|
||||||
|
</button>
|
||||||
|
</li>
|
||||||
|
<li v-else>
|
||||||
|
<button @click="toggleSelect" class="sc-button bt-show">
|
||||||
|
Afficher résultats et orientations disponibles
|
||||||
|
</button>
|
||||||
|
</li>
|
||||||
|
</ul>
|
||||||
|
</div>
|
||||||
|
<div class="noResult" v-if="!hasResult">
|
||||||
|
<div class="chill-no-data-statement">
|
||||||
|
{{ $t('goal_has_ho_result') }}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<style lang="scss">
|
||||||
|
|
||||||
|
button.hide {
|
||||||
|
background-color: rgb(51, 77, 92);
|
||||||
|
}
|
||||||
|
|
||||||
|
ul.list-results {
|
||||||
|
list-style-type: none;
|
||||||
|
padding: 0;
|
||||||
|
|
||||||
|
li {
|
||||||
|
margin: 0.5rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
li.badge {
|
||||||
|
padding-bottom: 0;
|
||||||
|
padding-top: 0;
|
||||||
|
padding-left: 0;
|
||||||
|
|
||||||
|
i.fa {
|
||||||
|
/*border-radius: 0.25rem; */
|
||||||
|
padding: 0.25rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
i.fa-plus {
|
||||||
|
background-color: green;
|
||||||
|
}
|
||||||
|
|
||||||
|
i.fa-times {
|
||||||
|
background-color: red;
|
||||||
|
color: white;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
</style>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
|
||||||
|
const i18n = {
|
||||||
|
messages: {
|
||||||
|
fr: {
|
||||||
|
add_a_result: "Résultat - orientation disponibles",
|
||||||
|
goal_has_no_result: "Aucun résultat - orientation disponible",
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
export default {
|
||||||
|
name: "AddResult",
|
||||||
|
props: [ 'destination', 'goal' ],
|
||||||
|
i18n,
|
||||||
|
data() {
|
||||||
|
return {
|
||||||
|
isExpanded: false,
|
||||||
|
};
|
||||||
|
},
|
||||||
|
computed: {
|
||||||
|
hasResult() {
|
||||||
|
if (this.destination === 'action') {
|
||||||
|
return this.$store.state.resultsForAction.length > 0;
|
||||||
|
} else if (this.destination === 'goal') {
|
||||||
|
return this.$store.getters.resultsForGoal(this.goal).length > 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
throw Error(`this.destination is not implemented: ${this.destination}`);
|
||||||
|
},
|
||||||
|
pickedResults() {
|
||||||
|
console.log('get checked');
|
||||||
|
console.log('this.destination', this.destination);
|
||||||
|
console.log('this.goal', this.goal);
|
||||||
|
if (this.destination === 'action') {
|
||||||
|
return this.$store.state.resultsPicked;
|
||||||
|
} else if (this.destination === 'goal') {
|
||||||
|
return this.$store.getters.resultsPickedForGoal(this.goal);
|
||||||
|
}
|
||||||
|
|
||||||
|
throw Error(`this.destination is not implemented: ${this.destination}`);
|
||||||
|
},
|
||||||
|
availableForCheckResults() {
|
||||||
|
console.log('availableForCheckResults');
|
||||||
|
console.log('this.destination', this.destination);
|
||||||
|
console.log('this.goal', this.goal);
|
||||||
|
if (this.destination === 'action') {
|
||||||
|
let pickedIds = this.$store.state.resultsPicked.map(r => r.id);
|
||||||
|
console.log('picked ids', pickedIds);
|
||||||
|
|
||||||
|
return this.$store.state.resultsForAction.filter(r => !pickedIds.includes(r.id));
|
||||||
|
} else if (this.destination === 'goal') {
|
||||||
|
console.log('results picked for goal', this.$store.getters.resultsPickedForGoal(this.goal));
|
||||||
|
let pickedIds = this.$store.getters.resultsPickedForGoal(this.goal).map(r => r.id);
|
||||||
|
|
||||||
|
return this.$store.getters.resultsForGoal(this.goal).filter(r => !pickedIds.includes(r.id));
|
||||||
|
}
|
||||||
|
|
||||||
|
throw Error(`this.destination is not implemented: ${this.destination}`);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
methods: {
|
||||||
|
toggleSelect() {
|
||||||
|
this.isExpanded = !this.isExpanded;
|
||||||
|
},
|
||||||
|
addResult(r) {
|
||||||
|
console.log('addResult', r);
|
||||||
|
if (this.destination === 'action') {
|
||||||
|
this.$store.commit('addResultPicked', r);
|
||||||
|
return;
|
||||||
|
} else if (this.destination === 'goal') {
|
||||||
|
this.$store.commit('addResultForGoalPicked', { goal: this.goal, result: r });
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
throw Error(`this.destination is not implemented: ${this.destination}`);
|
||||||
|
},
|
||||||
|
removeResult(r) {
|
||||||
|
console.log('removeresult', r);
|
||||||
|
if (this.destination === 'action') {
|
||||||
|
this.$store.commit('removeResultPicked', r);
|
||||||
|
return;
|
||||||
|
} else if (this.destination === 'goal') {
|
||||||
|
this.$store.commit('removeResultForGoalPicked', { goal: this.goal, result: r });
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
throw Error(`this.destination is not implemented: ${this.destination}`);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
</script>
|
@ -20,12 +20,36 @@ const store = createStore({
|
|||||||
resultsForAction: [],
|
resultsForAction: [],
|
||||||
goalsForAction: [],
|
goalsForAction: [],
|
||||||
resultsForGoal: [],
|
resultsForGoal: [],
|
||||||
|
personsPicked: window.accompanyingCourseWork.persons,
|
||||||
|
personsReachables: window.accompanyingCourseWork.accompanyingPeriod.participations.filter(p => p.endDate == null)
|
||||||
|
.map(p => p.person),
|
||||||
|
handlingThirdParty: window.accompanyingCourseWork.handlingThierParty,
|
||||||
|
thirdParties: window.accompanyingCourseWork.thirdParties,
|
||||||
errors: [],
|
errors: [],
|
||||||
},
|
},
|
||||||
getters: {
|
getters: {
|
||||||
socialAction(state) {
|
socialAction(state) {
|
||||||
return state.work.socialAction;
|
return state.work.socialAction;
|
||||||
},
|
},
|
||||||
|
hasResultsForAction(state) {
|
||||||
|
return state.resultsForAction.length > 0;
|
||||||
|
},
|
||||||
|
resultsForGoal: (state) => (goal) => {
|
||||||
|
let founds = state.resultsForGoal.filter(r => r.goalId === goal.id);
|
||||||
|
|
||||||
|
return founds === undefined ? [] : founds;
|
||||||
|
},
|
||||||
|
resultsPickedForGoal: (state) => (goal) => {
|
||||||
|
let found = state.goalsPicked.find(g => g.id === goal.id);
|
||||||
|
|
||||||
|
return found === undefined ? [] : found.results;
|
||||||
|
},
|
||||||
|
hasHandlingThirdParty(state) {
|
||||||
|
return state.handlingThirdParty !== null;
|
||||||
|
},
|
||||||
|
hasThirdParties(state) {
|
||||||
|
return state.thirdParties.length > 0;
|
||||||
|
},
|
||||||
},
|
},
|
||||||
mutations: {
|
mutations: {
|
||||||
setStartDate(state, date) {
|
setStartDate(state, date) {
|
||||||
@ -40,7 +64,7 @@ const store = createStore({
|
|||||||
},
|
},
|
||||||
setResultsForGoal(state, { goal, results }) {
|
setResultsForGoal(state, { goal, results }) {
|
||||||
console.log('set results for goal', results);
|
console.log('set results for goal', results);
|
||||||
state.goalsForAction = goal;
|
state.goalsForAction.push(goal);
|
||||||
for (let i in results) {
|
for (let i in results) {
|
||||||
let r = results[i];
|
let r = results[i];
|
||||||
r.goalId = goal.id;
|
r.goalId = goal.id;
|
||||||
@ -48,15 +72,70 @@ const store = createStore({
|
|||||||
state.resultsForGoal.push(r);
|
state.resultsForGoal.push(r);
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
addResultPicked(state, result) {
|
||||||
|
state.resultsPicked.push(result);
|
||||||
|
},
|
||||||
|
removeResultPicked(state, result) {
|
||||||
|
state.resultsPicked = state.resultsPicked.filter(r => r.id !== result.id);
|
||||||
|
},
|
||||||
|
addGoal(state, goal) {
|
||||||
|
let g = goal;
|
||||||
|
// initialize results to empty array
|
||||||
|
g.results = []
|
||||||
|
state.goalsPicked.push(g);
|
||||||
|
},
|
||||||
|
removeGoal(state, goal) {
|
||||||
|
state.goalsPicked = state.goalsPicked.filter(g => g.id !== goal.id);
|
||||||
|
},
|
||||||
|
addResultForGoalPicked(state, { goal, result}) {
|
||||||
|
let found = state.goalsPicked.find(g => g.id === goal.id);
|
||||||
|
|
||||||
|
if (found === undefined) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
found.results.push(result);
|
||||||
|
},
|
||||||
|
removeResultForGoalPicked(state, { goal, result}) {
|
||||||
|
let found = state.goalsPicked.find(g => g.id === goal.id);
|
||||||
|
|
||||||
|
if (found === undefined) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
found.results = found.results.filter(r => r.id !== result.id);
|
||||||
|
},
|
||||||
|
setPersonsPickedIds(state, ids) {
|
||||||
|
console.log('persons ids', ids);
|
||||||
|
state.personsPicked = state.personsReachables
|
||||||
|
.filter(p => ids.includes(p.id))
|
||||||
|
},
|
||||||
|
setNote(state, note) {
|
||||||
|
state.note = note;
|
||||||
|
},
|
||||||
|
setHandlingThirdParty(state, thirdParty) {
|
||||||
|
state.handlingThirdParty = thirdParty;
|
||||||
|
},
|
||||||
|
addThirdParties(state, thirdParties) {
|
||||||
|
console.log('addThirdParties', thirdParties);
|
||||||
|
// filter to remove existing thirdparties
|
||||||
|
let ids = state.thirdParties.map(t => t.id);
|
||||||
|
let unexistings = thirdParties.filter(t => !ids.includes(t.id));
|
||||||
|
console.log('unexisting third parties', unexistings);
|
||||||
|
for (let i in unexistings) {
|
||||||
|
state.thirdParties.push(unexistings[i]);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
removeThirdParty(state, thirdParty) {
|
||||||
|
state.thirdParties = state.thirdParties
|
||||||
|
.filter(t => t.id !== thirdParty.id);
|
||||||
|
},
|
||||||
addErrors(state, errors) {
|
addErrors(state, errors) {
|
||||||
console.log('handling errors', errors);
|
console.log('handling errors', errors);
|
||||||
for (let i in errors) {
|
for (let i in errors) {
|
||||||
state.push(errors[i]);
|
state.push(errors[i]);
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
setNote(state, note) {
|
|
||||||
state.note = note;
|
|
||||||
},
|
|
||||||
},
|
},
|
||||||
actions: {
|
actions: {
|
||||||
getReachablesGoalsForAction({ getters, commit, dispatch }) {
|
getReachablesGoalsForAction({ getters, commit, dispatch }) {
|
||||||
|
@ -0,0 +1,34 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
declare(strict_types=1);
|
||||||
|
|
||||||
|
namespace Chill\Migrations\Person;
|
||||||
|
|
||||||
|
use Doctrine\DBAL\Schema\Schema;
|
||||||
|
use Doctrine\Migrations\AbstractMigration;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Add link to accompanying period work and persons
|
||||||
|
*/
|
||||||
|
final class Version20210623135043 extends AbstractMigration
|
||||||
|
{
|
||||||
|
public function getDescription(): string
|
||||||
|
{
|
||||||
|
return 'Add link to accompanying period work and persons';
|
||||||
|
}
|
||||||
|
|
||||||
|
public function up(Schema $schema): void
|
||||||
|
{
|
||||||
|
$this->addSql("CREATE TABLE chill_person_accompanying_period_work_person (accompanyingperiodwork_id INT NOT NULL, person_id INT NOT NULL, PRIMARY KEY(accompanyingperiodwork_id, person_id))");
|
||||||
|
$this->addSql("CREATE INDEX IDX_615F494CB99F6060 ON chill_person_accompanying_period_work_person (accompanyingperiodwork_id)");
|
||||||
|
$this->addSql("CREATE INDEX IDX_615F494C217BBB47 ON chill_person_accompanying_period_work_person (person_id)");
|
||||||
|
$this->addSql("ALTER TABLE chill_person_accompanying_period_work_person ADD CONSTRAINT FK_615F494CB99F6060 FOREIGN KEY (accompanyingperiodwork_id) REFERENCES chill_person_accompanying_period_work (id) ON DELETE CASCADE NOT DEFERRABLE INITIALLY IMMEDIATE");
|
||||||
|
$this->addSql("ALTER TABLE chill_person_accompanying_period_work_person ADD CONSTRAINT FK_615F494C217BBB47 FOREIGN KEY (person_id) REFERENCES chill_person_person (id) ON DELETE CASCADE NOT DEFERRABLE INITIALLY IMMEDIATE");
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
public function down(Schema $schema): void
|
||||||
|
{
|
||||||
|
$this->addSql("DROP TABLE chill_person_accompanying_period_work_person");
|
||||||
|
}
|
||||||
|
}
|
@ -330,6 +330,10 @@ Move household: Nouveau déménagement
|
|||||||
Addresses history for household: Historique des adresses
|
Addresses history for household: Historique des adresses
|
||||||
|
|
||||||
# accompanying course work
|
# accompanying course work
|
||||||
|
Accompanying Course Actions: Actions d'accompagnements
|
||||||
accompanying_course_work:
|
accompanying_course_work:
|
||||||
|
create: Créer une action
|
||||||
Create accompanying course work: Créer une action d'accompagnement
|
Create accompanying course work: Créer une action d'accompagnement
|
||||||
|
Edit accompanying course work: Modifier une action d'accompagnement
|
||||||
|
List accompanying course work: Liste des actions d'accompagnement
|
||||||
|
|
||||||
|
Loading…
x
Reference in New Issue
Block a user