adapting vuejs form for accompanyingPeriodWork: include template for

specific evaluation
This commit is contained in:
Julien Fastré 2021-12-03 20:09:24 +01:00
parent a86ba6faf5
commit 2d319fcc42
8 changed files with 224 additions and 139 deletions

View File

@ -0,0 +1,10 @@
import { fetchResults } from "ChillMainAssets/lib/api/apiMethods.js";
const fetchTemplates = (entityClass) => {
let fqdnEntityClass = encodeURI(entityClass);
return fetchResults(`/api/1.0/docgen/templates/by-entity/${fqdnEntityClass}`);
}
export {
fetchTemplates
};

View File

@ -1,24 +1,27 @@
import {createApp} from 'vue'; import {createApp} from 'vue';
import PickTemplate from 'ChillDocGeneratorAssets/vuejs/_components/PickTemplate.vue'; import PickTemplate from 'ChillDocGeneratorAssets/vuejs/_components/PickTemplate.vue';
import {fetchTemplates} from 'ChillDocGeneratorAssets/api/pickTemplate.js';
import {_createI18n} from 'ChillMainAssets/vuejs/_js/i18n'; import {_createI18n} from 'ChillMainAssets/vuejs/_js/i18n';
const i18n = _createI18n({}); const i18n = _createI18n({});
document.querySelectorAll('div[data-docgen-template-picker]').forEach(el => { document.querySelectorAll('div[data-docgen-template-picker]').forEach(el => {
fetchTemplates(el.dataset.entityClass).then(templates => {
let let
picker = { picker = {
template: '<pick-template :entityClass="this.entityClass" :entityId="this.entityId"></pick-template>', template: '<pick-template :templates="this.templates" :entityId="this.entityId"></pick-template>',
components: { components: {
PickTemplate, PickTemplate,
}, },
data() { data() {
return { return {
entityClass: el.dataset.entityClass, templates: templates,
entityId: el.dataset.entityId, entityId: el.dataset.entityId,
} }
}, },
} }
; ;
createApp(picker).use(i18n).mount(el); createApp(picker).use(i18n).mount(el);
})
}); });

View File

@ -20,7 +20,6 @@
</template> </template>
<script> <script>
import { fetchResults } from "ChillMainAssets/lib/api/apiMethods.js";
export default { export default {
name: "PickTemplate", name: "PickTemplate",
@ -30,6 +29,10 @@ export default {
type: String, type: String,
required: true, required: true,
}, },
templates: {
required: true,
},
// beforeMove execute "something" before
beforeMove: { beforeMove: {
type: Function, type: Function,
required: false, required: false,
@ -38,7 +41,6 @@ export default {
data() { data() {
return { return {
template: null, template: null,
templates: [],
} }
}, },
computed: { computed: {
@ -58,10 +60,13 @@ export default {
}, },
methods: { methods: {
generateDocument() { generateDocument() {
console.log('generateDocument');
console.log('beforeMove', this.beforeMove);
if (this.beforeMove != null) { if (this.beforeMove != null) {
console.log('execute before move');
let r = this.beforeMove(); let r = this.beforeMove();
if (r instanceof Promise) { if (r instanceof Promise) {
r.then(() => this.goToGenerate()); r.then((obj) => this.goToGenerate(obj));
} else { } else {
this.goToGenerate(); this.goToGenerate();
} }
@ -69,11 +74,31 @@ export default {
this.goToGenerate(); this.goToGenerate();
} }
}, },
goToGenerate() { goToGenerate(obj) {
console.log('goToGenerate');
console.log('obj', obj);
console.log('entityId', this.entityId);
let
realId = this.entityId,
realEntityClass = this.entityClass
;
if (obj !== undefined) {
if (obj.entityId !== undefined) {
realId = obj.entityId;
}
if (obj.entityClass !== undefined) {
realEntityClass = obj.entityClass;
}
}
console.log('realId', realId);
console.log('realEntityClass', realEntityClass);
const const
entityId = encodeURI(this.entityId), entityId = encodeURI(realId),
returnPath = encodeURIComponent(window.location.pathname + window.location.search + window.location.hash), returnPath = encodeURIComponent(window.location.pathname + window.location.search + window.location.hash),
fqdnEntityClass = encodeURI(this.entityClass), fqdnEntityClass = encodeURI(realEntityClass),
url = `/fr/doc/gen/generate/from/${this.template}/for/${fqdnEntityClass}/${entityId}?returnPath=${returnPath}` url = `/fr/doc/gen/generate/from/${this.template}/for/${fqdnEntityClass}/${entityId}?returnPath=${returnPath}`
; ;
@ -81,12 +106,6 @@ export default {
window.location.assign(url); window.location.assign(url);
}, },
}, },
created() {
let fqdnEntityClass = encodeURI(this.entityClass);
fetchResults(`/api/1.0/docgen/templates/by-entity/${fqdnEntityClass}`).then(templates => {
this.templates = templates;
})
},
} }
</script> </script>

View File

@ -0,0 +1,74 @@
<?php
/**
* Chill is a software for social workers
*
* For the full copyright and license information, please view
* the LICENSE file that was distributed with this source code.
*/
declare(strict_types=1);
namespace Chill\PersonBundle\Controller;
use Chill\DocGeneratorBundle\Entity\DocGeneratorTemplate;
use Chill\DocGeneratorBundle\Repository\DocGeneratorTemplateRepository;
use Chill\MainBundle\Pagination\PaginatorFactory;
use Chill\MainBundle\Serializer\Model\Collection;
use Chill\MainBundle\Serializer\Normalizer\CollectionNormalizer;
use Chill\PersonBundle\Entity\AccompanyingPeriod\AccompanyingPeriodWorkEvaluation;
use Chill\PersonBundle\Entity\SocialWork\Evaluation;
use Symfony\Component\HttpFoundation\JsonResponse;
use Symfony\Component\HttpKernel\Exception\BadRequestHttpException;
use Symfony\Component\Routing\Annotation\Route;
use Symfony\Component\Serializer\Normalizer\AbstractNormalizer;
use Symfony\Component\Serializer\SerializerInterface;
class AccompanyingPeriodWorkEvaluationApiController
{
private DocGeneratorTemplateRepository $docGeneratorTemplateRepository;
private SerializerInterface $serializer;
private PaginatorFactory $paginatorFactory;
public function __construct(
DocGeneratorTemplateRepository $docGeneratorTemplateRepository,
SerializerInterface $serializer,
PaginatorFactory $paginatorFactory
) {
$this->docGeneratorTemplateRepository = $docGeneratorTemplateRepository;
$this->serializer = $serializer;
$this->paginatorFactory = $paginatorFactory;
}
/**
* @Route("/api/1.0/person/docgen/template/by-evaluation/{id}.{_format}",
* requirements={"format": "json"})
*/
public function listTemplateByEvaluation(Evaluation $evaluation, string $_format): JsonResponse
{
if ('json' !== $_format) {
throw new BadRequestHttpException("format not supported");
}
$evaluations =
array_filter(
$this->docGeneratorTemplateRepository
->findByEntity(AccompanyingPeriodWorkEvaluation::class),
function (DocGeneratorTemplate $t) use ($evaluation) {
$ids = $t->getOptions()['evaluations'] ?? [];
return in_array($evaluation->getId(), $ids);
}
);
$paginator = $this->paginatorFactory->create(count($evaluations));
$paginator->setItemsPerPage(count($evaluations));
return new JsonResponse($this->serializer->serialize(
new Collection($evaluations, $paginator), 'json', [
AbstractNormalizer::GROUPS => ['read']
]
), JsonResponse::HTTP_OK, [], true);
}
}

View File

@ -54,8 +54,6 @@ class Origin
public function getLabel() public function getLabel()
{ {
dump($this->label);
return $this->label; return $this->label;
} }

View File

@ -196,7 +196,8 @@
</div> </div>
<pick-template <pick-template
entityClass="Chill\PersonBundle\Entity\AccompanyingPeriod\AccompanyingPeriodWorkEvaluation" entityClass="Chill\PersonBundle\Entity\AccompanyingPeriod\AccompanyingPeriodWork"
:templates="this.templatesAvailablesForAction"
:entityId="work.id" :entityId="work.id"
:beforeMove="beforeGenerateTemplate"> :beforeMove="beforeGenerateTemplate">
<template v-slot:title> <template v-slot:title>
@ -329,6 +330,7 @@ export default {
'thirdParties', 'thirdParties',
'isPosting', 'isPosting',
'errors', 'errors',
'templatesAvailablesForAction',
]), ]),
...mapGetters([ ...mapGetters([
'hasResultsForAction', 'hasResultsForAction',
@ -397,7 +399,7 @@ export default {
this.$store.commit('removeGoal', g); this.$store.commit('removeGoal', g);
}, },
addEvaluation(e) { addEvaluation(e) {
this.$store.commit('addEvaluation', e); this.$store.dispatch('addEvaluation', e);
}, },
toggleAddEvaluation() { toggleAddEvaluation() {
this.showAddEvaluation = !this.showAddEvaluation; this.showAddEvaluation = !this.showAddEvaluation;

View File

@ -62,19 +62,16 @@
</div> </div>
<div class="row mb-3"> <div class="row mb-3">
<pick-template
entityClass="Chill\PersonBundle\Entity\AccompanyingPeriod\AccompanyingPeriodWorkEvaluation"
:id="evaluation.id"
:templates="getTemplatesAvailables"
:beforeMove="submitBeforeGenerate"
>
<template v-slot:title>
<label class="col-sm-4 col-form-label">{{ $t('evaluation_generate_a_document') }}</label> <label class="col-sm-4 col-form-label">{{ $t('evaluation_generate_a_document') }}</label>
<div class="col-sm-8">
<div class="input-group">
<select class="form-select form-select-sm" v-model="template">
<option disabled value="">{{ $t('evaluation_choose_a_template') }}</option>
<template v-for="t in getTemplatesAvailaibleForEvaluation">
<option v-bind:value="t.id">{{ t.name.fr }}</option>
</template> </template>
</select> </pick-template>
<button v-if="canGenerate" class="btn btn-update btn-sm change-icon" type="button" @click="generateDocument"><i class="fa fa-fw fa-cog"></i></button>
<button v-else class="btn btn-update btn-sm change-icon" type="button" disabled ><i class="fa fa-fw fa-cog"></i></button>
</div>
</div>
</div> </div>
</div> </div>
</div> </div>
@ -85,6 +82,7 @@ import {dateToISO, ISOToDate, ISOToDatetime} from 'ChillMainAssets/chill/js/date
import CKEditor from '@ckeditor/ckeditor5-vue'; import CKEditor from '@ckeditor/ckeditor5-vue';
import ClassicEditor from 'ChillMainAssets/module/ckeditor5/index.js'; import ClassicEditor from 'ChillMainAssets/module/ckeditor5/index.js';
import { mapGetters, mapState } from 'vuex'; import { mapGetters, mapState } from 'vuex';
import PickTemplate from 'ChillDocGeneratorAssets/vuejs/_components/PickTemplate.vue';
const i18n = { const i18n = {
messages: { messages: {
@ -111,6 +109,7 @@ export default {
props: ['evaluation'], props: ['evaluation'],
components: { components: {
ckeditor: CKEditor.component, ckeditor: CKEditor.component,
PickTemplate,
}, },
i18n, i18n,
data() { data() {
@ -120,12 +119,12 @@ export default {
} }
}, },
computed: { computed: {
...mapGetters([
'getTemplatesAvailaibleForEvaluation'
]),
...mapState([ ...mapState([
'isPosting' 'isPosting'
]), ]),
getTemplatesAvailables() {
return this.$store.getters.getTemplatesAvailablesForEvaluation(this.evaluation.evaluation);
},
canGenerate() { canGenerate() {
return !this.$store.state.isPosting && this.template !== null; return !this.$store.state.isPosting && this.template !== null;
}, },
@ -176,13 +175,14 @@ export default {
}) })
; ;
}, },
generateDocument() { submitBeforeGenerate() {
console.log('template picked', this.template); const callback = (data) => {
this.$store.dispatch('generateDocument', { key: this.evaluation.key, templateId: this.template}) let evaluationId = data.accompanyingPeriodWorkEvaluations.find(e => e.key === this.evaluation.key).id;
return Promise.resolve({entityId: evaluationId});
};
return this.$store.dispatch('submit', callback).catch(e => { console.log(e); throw e; });
} }
}, },
mounted() {
//this.listAllStatus();
}
} }
</script> </script>

View File

@ -2,6 +2,8 @@ import { createStore } from 'vuex';
import { datetimeToISO, ISOToDatetime, intervalDaysToISO, intervalISOToDays } from 'ChillMainAssets/chill/js/date.js'; import { datetimeToISO, ISOToDatetime, intervalDaysToISO, intervalISOToDays } from 'ChillMainAssets/chill/js/date.js';
import { findSocialActionsBySocialIssue } from 'ChillPersonAssets/vuejs/_api/SocialWorkSocialAction.js'; import { findSocialActionsBySocialIssue } from 'ChillPersonAssets/vuejs/_api/SocialWorkSocialAction.js';
import { create } from 'ChillPersonAssets/vuejs/_api/AccompanyingCourseWork.js'; import { create } from 'ChillPersonAssets/vuejs/_api/AccompanyingCourseWork.js';
import { fetchResults, makeFetch } from 'ChillMainAssets/lib/api/apiMethods.js';
import { fetchTemplates } from 'ChillDocGeneratorAssets/api/pickTemplate.js';
const debug = process.env.NODE_ENV !== 'production'; const debug = process.env.NODE_ENV !== 'production';
const evalFQDN = encodeURIComponent("Chill\\PersonBundle\\Entity\\AccompanyingPeriod\\AccompanyingPeriodWorkEvaluation"); const evalFQDN = encodeURIComponent("Chill\\PersonBundle\\Entity\\AccompanyingPeriod\\AccompanyingPeriodWorkEvaluation");
@ -20,20 +22,10 @@ const store = createStore({
resultsPicked: window.accompanyingCourseWork.results, resultsPicked: window.accompanyingCourseWork.results,
resultsForAction: [], resultsForAction: [],
resultsForGoal: [], resultsForGoal: [],
evaluationsPicked: window.accompanyingCourseWork.accompanyingPeriodWorkEvaluations.map((e, index) => { evaluationsPicked: [],
var k = Object.assign(e, {
key: index,
editEvaluation: false,
startDate: e.startDate !== null ? ISOToDatetime(e.startDate.datetime) : null,
endDate: e.endDate !== null ? ISOToDatetime(e.endDate.datetime) : null,
maxDate: e.maxDate !== null ? ISOToDatetime(e.maxDate.datetime) : null,
warningInterval: e.warningInterval !== null ? intervalISOToDays(e.warningInterval) : null,
});
return k;
}),
evaluationsForAction: [], evaluationsForAction: [],
templatesAvailableForEvaluation: [], templatesAvailablesForAction: [],
templatesAvailablesForEvaluation: new Map([]),
personsPicked: window.accompanyingCourseWork.persons, personsPicked: window.accompanyingCourseWork.persons,
personsReachables: window.accompanyingCourseWork.accompanyingPeriod.participations.filter(p => p.endDate == null) personsReachables: window.accompanyingCourseWork.accompanyingPeriod.participations.filter(p => p.endDate == null)
.map(p => p.person), .map(p => p.person),
@ -65,8 +57,8 @@ const store = createStore({
hasThirdParties(state) { hasThirdParties(state) {
return state.thirdParties.length > 0; return state.thirdParties.length > 0;
}, },
getTemplatesAvailaibleForEvaluation(state) { getTemplatesAvailablesForEvaluation: (state) => (evaluation) => {
return state.templatesAvailableForEvaluation; return state.templatesAvailablesForEvaluation.get(evaluation.id) || [];
}, },
buildPayload(state) { buildPayload(state) {
return { return {
@ -125,6 +117,20 @@ const store = createStore({
} }
}, },
mutations: { mutations: {
setEvaluationsPicked(state, evaluations) {
state.evaluationsPicked = evaluations.map((e, index) => {
var k = Object.assign(e, {
key: index,
editEvaluation: false,
startDate: e.startDate !== null ? ISOToDatetime(e.startDate.datetime) : null,
endDate: e.endDate !== null ? ISOToDatetime(e.endDate.datetime) : null,
maxDate: e.maxDate !== null ? ISOToDatetime(e.maxDate.datetime) : null,
warningInterval: e.warningInterval !== null ? intervalISOToDays(e.warningInterval) : null,
});
return k;
});
},
setStartDate(state, date) { setStartDate(state, date) {
state.startDate = date; state.startDate = date;
}, },
@ -222,10 +228,11 @@ const store = createStore({
let evaluation = state.evaluationsPicked.find(e => e.key === key); let evaluation = state.evaluationsPicked.find(e => e.key === key);
evaluation.editEvaluation = !evaluation.editEvaluation; evaluation.editEvaluation = !evaluation.editEvaluation;
}, },
setTemplatesAvailableForEvaluation(state, templates) { setTemplatesForEvaluation(state, {templates, evaluation}) {
for (let i in templates) { state.templatesAvailablesForEvaluation.set(evaluation.id, templates);
state.templatesAvailableForEvaluation.push(templates[i]); },
} setTemplatesAvailablesForAction(state, templates) {
state.templatesAvailablesForAction = templates;
}, },
setPersonsPickedIds(state, ids) { setPersonsPickedIds(state, ids) {
state.personsPicked = state.personsReachables state.personsPicked = state.personsReachables
@ -328,36 +335,19 @@ const store = createStore({
commit('setEvaluationsForAction', data.results); commit('setEvaluationsForAction', data.results);
}); });
}, },
getReachableTemplatesForEvaluation({commit}) { addEvaluation({commit, dispatch}, evaluation) {
const commit('addEvaluation', evaluation);
url = `/fr/doc/gen/templates/for/${evalFQDN}` dispatch('fetchTemplatesAvailablesForEvaluation', evaluation);
;
window.fetch(url).then(r => {
if (r.ok) {
return r.json();
}
throw new Error("not possible to load templates for evaluations")
}).then(data => {
commit('setTemplatesAvailableForEvaluation', data.results);
}).catch(e => {
console.error(e);
})
}, },
generateDocument({ dispatch }, {key, templateId}) { fetchTemplatesAvailablesForEvaluation({commit, state}, evaluation) {
const callback = function(data) { if (!state.templatesAvailablesForEvaluation.has(evaluation.id)) {
// get the evaluation id from the data // commit an empty array to avoid parallel fetching for same evaluation id
const commit('setTemplatesForEvaluation', {templates: [], evaluation});
evaluationId = data.accompanyingPeriodWorkEvaluations.find(e => e.key === key).id, fetchResults(`/api/1.0/person/docgen/template/by-evaluation/${evaluation.id}.json`)
returnPath = encodeURIComponent(window.location.pathname + window.location.search + window.location.hash), .then(templates => {
url = `/fr/doc/gen/generate/from/${templateId}/for/${evalFQDN}/${evaluationId}?returnPath=${returnPath}` commit('setTemplatesForEvaluation', {templates, evaluation});
; });
//http://localhost:8001/fr/doc/gen/generate/from/12/for/Chill%5CPersonBundle%5CEntity%5CAccompanyingPeriod%5CAccompanyingPeriodWorkEvaluation/41 }
console.log('I will generate your doc at', url);
window.location.assign(url);
};
dispatch('submit', callback);
}, },
submit({ getters, state, commit }, callback) { submit({ getters, state, commit }, callback) {
let let
@ -368,47 +358,36 @@ const store = createStore({
commit('setIsPosting', true); commit('setIsPosting', true);
window.fetch(url, { return makeFetch('PUT', url, payload)
method: 'PUT', .then(data => {
headers: { console.log('data received', data);
'Content-Type': 'application/json' if (typeof(callback) !== 'undefined') {
}, return callback(data);
body: JSON.stringify(payload)
}).then(response => {
if (response.ok || response.status === 422) {
return response.json().then(data => ({data, status: response.status}));
}
throw new Error(response.status);
}).then(({data, status}) => {
if (status === 422) {
for (let i in data.violations) {
errors.push(data.violations[i].title);
}
commit('setErrors', errors);
commit('setIsPosting', false);
} else if (typeof(callback) !== 'undefined') {
callback(data);
} else { } else {
console.info('nothing to do here, bye bye'); console.info('nothing to do here, bye bye');window.location.assign(`/fr/person/accompanying-period/${state.work.accompanyingPeriod.id}/work`);
window.location.assign(`/fr/person/accompanying-period/${state.work.accompanyingPeriod.id}/work`);
} }
}).catch(e => { }).catch(error => {
commit('setErrors', [ console.log('error on submit', error);
'Erreur serveur ou réseau: veuillez ré-essayer. Code erreur: ' + e
]);
commit('setIsPosting', false); commit('setIsPosting', false);
commit('setErrors', error.violations);
}); });
}, },
initAsync({ dispatch }) {
dispatch('getReachablesResultsForAction');
dispatch('getReachablesGoalsForAction');
dispatch('getReachablesEvaluationsForAction');
dispatch('getReachableTemplatesForEvaluation');
},
} }
}); });
store.dispatch('initAsync'); store.commit('setEvaluationsPicked', window.accompanyingCourseWork.accompanyingPeriodWorkEvaluations);
store.dispatch('getReachablesResultsForAction');
store.dispatch('getReachablesGoalsForAction');
store.dispatch('getReachablesEvaluationsForAction');
store.state.evaluationsPicked.forEach(evaluation => {
store.dispatch('fetchTemplatesAvailablesForEvaluation', evaluation.evaluation)
});
fetchTemplates('Chill\\PersonBundle\\Entity\\AccompanyingPeriod\\AccompanyingPeriodWork')
.then(templates => {
store.commit('setTemplatesAvailablesForAction', templates);
}
)
export { store }; export { store };