mirror of
https://gitlab.com/Chill-Projet/chill-bundles.git
synced 2025-08-21 07:03:49 +00:00
add scope selection on accompanying course
This commit is contained in:
@@ -10,6 +10,7 @@
|
||||
<origin-demand></origin-demand>
|
||||
<requestor></requestor>
|
||||
<social-issue></social-issue>
|
||||
<scopes></scopes>
|
||||
<referrer></referrer>
|
||||
<resources></resources>
|
||||
<comment v-if="accompanyingCourse.step === 'DRAFT'"></comment>
|
||||
@@ -32,6 +33,7 @@ import PersonsAssociated from './components/PersonsAssociated.vue';
|
||||
import Requestor from './components/Requestor.vue';
|
||||
import SocialIssue from './components/SocialIssue.vue';
|
||||
import CourseLocation from './components/CourseLocation.vue';
|
||||
import Scopes from './components/Scopes.vue';
|
||||
import Referrer from './components/Referrer.vue';
|
||||
import Resources from './components/Resources.vue';
|
||||
import Comment from './components/Comment.vue';
|
||||
@@ -47,6 +49,7 @@ export default {
|
||||
Requestor,
|
||||
SocialIssue,
|
||||
CourseLocation,
|
||||
Scopes,
|
||||
Referrer,
|
||||
Resources,
|
||||
Comment,
|
||||
|
@@ -191,7 +191,49 @@ const getListOrigins = () => {
|
||||
if (response.ok) { return response.json(); }
|
||||
throw { msg: 'Error while retriving origin\'s list.', sta: response.status, txt: response.statusText, err: new Error(), body: response.body };
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
const addScope = (id, scope) => {
|
||||
const url = `/api/1.0/person/accompanying-course/${id}/scope.json`;
|
||||
console.log(url);
|
||||
console.log(scope);
|
||||
|
||||
return fetch(url, {
|
||||
method: 'POST',
|
||||
body: JSON.stringify({
|
||||
id: scope.id,
|
||||
type: scope.type,
|
||||
}),
|
||||
headers: {
|
||||
'Content-Type': 'application/json;charset=utf-8'
|
||||
},
|
||||
})
|
||||
.then(response => {
|
||||
if (response.ok) { return response.json(); }
|
||||
throw { msg: 'Error while adding scope', sta: response.status, txt: response.statusText, err: new Error(), body: response.body };
|
||||
});
|
||||
};
|
||||
|
||||
const removeScope = (id, scope) => {
|
||||
const url = `/api/1.0/person/accompanying-course/${id}/scope.json`;
|
||||
console.log(url);
|
||||
console.log(scope);
|
||||
|
||||
return fetch(url, {
|
||||
method: 'DELETE',
|
||||
body: JSON.stringify({
|
||||
id: scope.id,
|
||||
type: scope.type,
|
||||
}),
|
||||
headers: {
|
||||
'Content-Type': 'application/json;charset=utf-8'
|
||||
},
|
||||
})
|
||||
.then(response => {
|
||||
if (response.ok) { return response.json(); }
|
||||
throw { msg: 'Error while adding scope', sta: response.status, txt: response.statusText, err: new Error(), body: response.body };
|
||||
});
|
||||
};
|
||||
|
||||
export {
|
||||
getAccompanyingCourse,
|
||||
@@ -204,5 +246,7 @@ export {
|
||||
getUsers,
|
||||
whoami,
|
||||
getListOrigins,
|
||||
postSocialIssue
|
||||
postSocialIssue,
|
||||
addScope,
|
||||
removeScope,
|
||||
};
|
||||
|
@@ -88,6 +88,10 @@ export default {
|
||||
socialIssue: {
|
||||
msg: 'confirm.socialIssue_not_valid',
|
||||
anchor: '#section-50'
|
||||
},
|
||||
scopes: {
|
||||
msg: 'confirm.set_a_scope',
|
||||
anchor: '#section-65'
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@@ -0,0 +1,47 @@
|
||||
<template>
|
||||
<div class="vue-component">
|
||||
<h2><a name="section-65"></a>{{ $t('scopes.title') }}</h2>
|
||||
|
||||
<ul>
|
||||
<li v-for="s in scopes">
|
||||
<input type="checkbox" v-model="checkedScopes" :value="s" />
|
||||
{{ s.name.fr }}
|
||||
</li>
|
||||
</ul>
|
||||
|
||||
<div v-if="!isScopeValid" class="alert alert-warning separator">
|
||||
{{ $t('scopes.add_at_least_one') }}
|
||||
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
|
||||
import { mapState, mapGetters } from 'vuex';
|
||||
|
||||
export default {
|
||||
name: "Scopes",
|
||||
computed: {
|
||||
...mapState([
|
||||
'scopes',
|
||||
'scopesAtStart'
|
||||
]),
|
||||
...mapGetters([
|
||||
'isScopeValid'
|
||||
]),
|
||||
checkedScopes: {
|
||||
get: function() {
|
||||
return this.$store.state.accompanyingCourse.scopes;
|
||||
},
|
||||
set: function(v) {
|
||||
this.$store.dispatch('setScopes', v);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
|
||||
</style>
|
@@ -86,6 +86,10 @@ const appMessages = {
|
||||
person_locator: "Parcours localisé auprès de {0}",
|
||||
no_address: "Il n'y a pas d'adresse associée au parcours"
|
||||
},
|
||||
scopes: {
|
||||
title: "Services",
|
||||
add_at_least_one: "Indiquez au moins un service",
|
||||
},
|
||||
referrer: {
|
||||
title: "Référent du parcours",
|
||||
label: "Vous pouvez choisir un TMS ou vous assigner directement comme référent",
|
||||
@@ -113,6 +117,7 @@ const appMessages = {
|
||||
participation_not_valid: "sélectionnez au minimum 1 usager",
|
||||
socialIssue_not_valid: "sélectionnez au minimum une problématique sociale",
|
||||
location_not_valid: "indiquez au minimum une localisation temporaire du parcours",
|
||||
set_a_scope: "indiquez au moins un service",
|
||||
sure: "Êtes-vous sûr ?",
|
||||
sure_description: "Une fois le changement confirmé, il ne sera plus possible de le remettre à l'état de brouillon !",
|
||||
ok: "Confirmer le parcours"
|
||||
|
@@ -1,28 +1,41 @@
|
||||
import 'es6-promise/auto';
|
||||
import { createStore } from 'vuex';
|
||||
import { fetchScopes } from 'ChillMainAssets/lib/api/scope.js';
|
||||
import { getAccompanyingCourse,
|
||||
patchAccompanyingCourse,
|
||||
confirmAccompanyingCourse,
|
||||
postParticipation,
|
||||
postRequestor,
|
||||
postResource,
|
||||
postSocialIssue } from '../api';
|
||||
postSocialIssue,
|
||||
addScope,
|
||||
removeScope,
|
||||
} from '../api';
|
||||
|
||||
|
||||
const debug = process.env.NODE_ENV !== 'production';
|
||||
const id = window.accompanyingCourseId;
|
||||
|
||||
let initPromise = getAccompanyingCourse(id)
|
||||
.then(accompanying_course => new Promise((resolve, reject) => {
|
||||
let scopesPromise = fetchScopes();
|
||||
let accompanyingCoursePromise = getAccompanyingCourse(id);
|
||||
|
||||
let initPromise = Promise.all([scopesPromise, accompanyingCoursePromise])
|
||||
.then(([scopes, accompanyingCourse]) => new Promise((resolve, reject) => {
|
||||
|
||||
const store = createStore({
|
||||
strict: debug,
|
||||
modules: {
|
||||
},
|
||||
state: {
|
||||
accompanyingCourse: accompanying_course,
|
||||
accompanyingCourse: accompanyingCourse,
|
||||
addressContext: {},
|
||||
errorMsg: []
|
||||
errorMsg: [],
|
||||
// all the available scopes
|
||||
scopes: scopes,
|
||||
// the scopes at start. If the user remove all scopes, we re-add those scopes, by security
|
||||
scopesAtStart: accompanyingCourse.scopes.map(scope => scope),
|
||||
// the scope states at server side
|
||||
scopesAtBackend: accompanyingCourse.scopes.map(scope => scope),
|
||||
},
|
||||
getters: {
|
||||
isParticipationValid(state) {
|
||||
@@ -34,11 +47,16 @@ let initPromise = getAccompanyingCourse(id)
|
||||
isLocationValid(state) {
|
||||
return state.accompanyingCourse.location !== null;
|
||||
},
|
||||
isScopeValid(state) {
|
||||
console.log('is scope valid', state.accompanyingCourse.scopes.length > 0);
|
||||
return state.accompanyingCourse.scopes.length > 0;
|
||||
},
|
||||
validationKeys(state, getters) {
|
||||
let keys = [];
|
||||
if (!getters.isParticipationValid) { keys.push('participation'); }
|
||||
if (!getters.isLocationValid) { keys.push('location'); }
|
||||
if (!getters.isSocialIssueValid) { keys.push('socialIssue'); }
|
||||
if (!getters.isScopeValid) { keys.push('scopes'); }
|
||||
//console.log('getter keys', keys);
|
||||
return keys;
|
||||
},
|
||||
@@ -137,6 +155,21 @@ let initPromise = getAccompanyingCourse(id)
|
||||
setEditContextTrue(state) {
|
||||
//console.log('### mutation: set edit context = true');
|
||||
state.addressContext.edit = true;
|
||||
},
|
||||
setScopes(state, scopes) {
|
||||
state.accompanyingCourse.scopes = scopes;
|
||||
},
|
||||
addScopeAtBackend(state, scope) {
|
||||
let scopeIds = state.scopesAtBackend.map(s => s.id);
|
||||
if (!scopeIds.includes(scope.id)) {
|
||||
state.scopesAtBackend.push(scope);
|
||||
}
|
||||
},
|
||||
removeScopeAtBackend(state, scope){
|
||||
let scopeIds = state.scopesAtBackend.map(s => s.id);
|
||||
if (scopeIds.includes(scope.id)) {
|
||||
state.scopesAtBackend = state.scopesAtBackend.filter(s => s.id !== scope.id);
|
||||
}
|
||||
}
|
||||
},
|
||||
actions: {
|
||||
@@ -223,6 +256,107 @@ let initPromise = getAccompanyingCourse(id)
|
||||
resolve();
|
||||
})).catch((error) => { commit('catchError', error) });
|
||||
},
|
||||
/**
|
||||
* Handle the checked/unchecked scopes
|
||||
*
|
||||
* When the user set the scopes in a invalid situation (when no scopes are cheched), this
|
||||
* method will internally re-add the scopes as they were originally when the page was loaded, but
|
||||
* this does not appears for the user (they remains unchecked). When the user re-add a scope, the
|
||||
* scope is back in a valid state, and the store synchronize with the new state (all the original scopes
|
||||
* are removed if necessary, and the new checked scopes is backed).
|
||||
*
|
||||
* So, for instance:
|
||||
*
|
||||
* at load:
|
||||
*
|
||||
* [x] scope A (at backend: [x])
|
||||
* [x] scope B (at backend: [x])
|
||||
* [ ] scope C (at backend: [ ])
|
||||
*
|
||||
* The user uncheck scope A:
|
||||
*
|
||||
* [ ] scope A (at backend: [ ] as soon as the operation finish)
|
||||
* [x] scope B (at backend: [x])
|
||||
* [ ] scope C (at backend: [ ])
|
||||
*
|
||||
* The user uncheck scope B. The state is invalid (no scope checked), so we go back to initial state when
|
||||
* the page loaded):
|
||||
*
|
||||
* [ ] scope A (at backend: [x] as soon as the operation finish)
|
||||
* [ ] scope B (at backend: [x] as soon as the operation finish)
|
||||
* [ ] scope C (at backend: [ ])
|
||||
*
|
||||
* The user check scope C. The scopes are back to valid state. So we go back to synchronization with UI and
|
||||
* backend):
|
||||
*
|
||||
* [ ] scope A (at backend: [ ] as soon as the operation finish)
|
||||
* [ ] scope B (at backend: [ ] as soon as the operation finish)
|
||||
* [x] scope C (at backend: [x] as soon as the operation finish)
|
||||
*
|
||||
* **Warning** There is a problem if the user check/uncheck faster than the backend is synchronized.
|
||||
*
|
||||
* @param commit
|
||||
* @param state
|
||||
* @param dispatch
|
||||
* @param scopes
|
||||
* @returns Promise
|
||||
*/
|
||||
setScopes({ commit, state, dispatch }, scopes) {
|
||||
let currentServerScopesIds = state.scopesAtBackend.map(scope => scope.id);
|
||||
let checkedScopesIds = scopes.map(scope => scope.id);
|
||||
let removedScopesIds = currentServerScopesIds.filter(id => !checkedScopesIds.includes(id));
|
||||
let addedScopesIds = checkedScopesIds.filter(id => !currentServerScopesIds.includes(id));
|
||||
let lengthAfterOperation = currentServerScopesIds.length + addedScopesIds.length
|
||||
- removedScopesIds.length;
|
||||
|
||||
if (lengthAfterOperation > 0 || (lengthAfterOperation === 0 && state.scopesAtStart.length === 0) ) {
|
||||
return dispatch('updateScopes', {
|
||||
addedScopesIds, removedScopesIds
|
||||
}).then(() => {
|
||||
// warning: when the operation of dispatch are too slow, the user may check / uncheck before
|
||||
// the end of the synchronisation with the server (done by dispatch operation). Then, it leads to
|
||||
// check/uncheck in the UI. I do not know of to avoid it.
|
||||
commit('setScopes', scopes);
|
||||
return Promise.resolve();
|
||||
});
|
||||
} else {
|
||||
return dispatch('setScopes', state.scopesAtStart).then(() => {
|
||||
commit('setScopes', scopes);
|
||||
return Promise.resolve();
|
||||
});
|
||||
}
|
||||
},
|
||||
/**
|
||||
* Internal function for the store to effectively update scopes.
|
||||
*
|
||||
* Return a promise which resolves when all update operation are
|
||||
* successful and finished.
|
||||
*
|
||||
* @param state
|
||||
* @param commit
|
||||
* @param addedScopesIds
|
||||
* @param removedScopesIds
|
||||
* @return Promise
|
||||
*/
|
||||
updateScopes({ state, commit }, { addedScopesIds, removedScopesIds }) {
|
||||
let promises = [];
|
||||
state.scopes.forEach(scope => {
|
||||
if (addedScopesIds.includes(scope.id)) {
|
||||
promises.push(addScope(state.accompanyingCourse.id, scope).then(() => {
|
||||
commit('addScopeAtBackend', scope);
|
||||
return Promise.resolve();
|
||||
}));
|
||||
}
|
||||
if (removedScopesIds.includes(scope.id)) {
|
||||
promises.push(removeScope(state.accompanyingCourse.id, scope).then(() => {
|
||||
commit('removeScopeAtBackend', scope);
|
||||
return Promise.resolve();
|
||||
}));
|
||||
}
|
||||
});
|
||||
|
||||
return Promise.all(promises);
|
||||
},
|
||||
postFirstComment({ commit }, payload) {
|
||||
//console.log('## action: postFirstComment: payload', payload);
|
||||
patchAccompanyingCourse(id, { type: "accompanying_period", initialComment: payload })
|
||||
|
Reference in New Issue
Block a user