diff --git a/src/Bundle/ChillMainBundle/ChillMainBundle.php b/src/Bundle/ChillMainBundle/ChillMainBundle.php index 91c58f68b..f9e79bc8b 100644 --- a/src/Bundle/ChillMainBundle/ChillMainBundle.php +++ b/src/Bundle/ChillMainBundle/ChillMainBundle.php @@ -8,6 +8,7 @@ use Chill\MainBundle\Security\Authorization\ChillVoterInterface; use Chill\MainBundle\Security\ProvideRoleInterface; use Chill\MainBundle\Security\Resolver\CenterResolverInterface; use Chill\MainBundle\Security\Resolver\ScopeResolverInterface; +use Chill\MainBundle\Templating\Entity\ChillEntityRenderInterface; use Symfony\Component\HttpKernel\Bundle\Bundle; use Symfony\Component\DependencyInjection\ContainerBuilder; use Chill\MainBundle\DependencyInjection\CompilerPass\SearchableServicesCompilerPass; @@ -38,6 +39,8 @@ class ChillMainBundle extends Bundle ->addTag('chill_main.center_resolver'); $container->registerForAutoconfiguration(ScopeResolverInterface::class) ->addTag('chill_main.scope_resolver'); + $container->registerForAutoconfiguration(ChillEntityRenderInterface::class) + ->addTag('chill.render_entity'); $container->addCompilerPass(new SearchableServicesCompilerPass()); $container->addCompilerPass(new ConfigConsistencyCompilerPass()); diff --git a/src/Bundle/ChillMainBundle/Entity/UserJob.php b/src/Bundle/ChillMainBundle/Entity/UserJob.php index 9d1ca9157..411d5c40f 100644 --- a/src/Bundle/ChillMainBundle/Entity/UserJob.php +++ b/src/Bundle/ChillMainBundle/Entity/UserJob.php @@ -3,10 +3,14 @@ namespace Chill\MainBundle\Entity; use Doctrine\ORM\Mapping as ORM; +use Symfony\Component\Serializer\Annotation as Serializer; /** * @ORM\Entity * @ORM\Table("chill_main_user_job") + * @Serializer\DiscriminatorMap(typeProperty="type", mapping={ + * "user_job"=UserJob::class + * }) */ class UserJob { @@ -15,12 +19,14 @@ class UserJob * @ORM\Id * @ORM\Column(name="id", type="integer") * @ORM\GeneratedValue(strategy="AUTO") + * @Serializer\Groups({"read"}) */ protected ?int $id = null; /** * @var array|string[]A * @ORM\Column(name="label", type="json") + * @Serializer\Groups({"read"}) */ protected array $label = []; diff --git a/src/Bundle/ChillMainBundle/Resources/public/vuejs/_components/Entity/UserRenderBoxBadge.vue b/src/Bundle/ChillMainBundle/Resources/public/vuejs/_components/Entity/UserRenderBoxBadge.vue new file mode 100644 index 000000000..a5e1cf183 --- /dev/null +++ b/src/Bundle/ChillMainBundle/Resources/public/vuejs/_components/Entity/UserRenderBoxBadge.vue @@ -0,0 +1,14 @@ + + + diff --git a/src/Bundle/ChillMainBundle/Resources/views/Entity/user.html.twig b/src/Bundle/ChillMainBundle/Resources/views/Entity/user.html.twig new file mode 100644 index 000000000..77dc959a2 --- /dev/null +++ b/src/Bundle/ChillMainBundle/Resources/views/Entity/user.html.twig @@ -0,0 +1,9 @@ + + {{- user.label }} + {%- if opts['user_job'] and user.userJob is not null %} + ({{ user.userJob.label|localize_translatable_string }}) + {%- endif -%} + {%- if opts['main_scope'] and user.mainScope is not null %} + ({{ user.mainScope.name|localize_translatable_string }}) + {%- endif -%} + diff --git a/src/Bundle/ChillMainBundle/Serializer/Normalizer/UserNormalizer.php b/src/Bundle/ChillMainBundle/Serializer/Normalizer/UserNormalizer.php index 4c1dc1523..af29ad4f4 100644 --- a/src/Bundle/ChillMainBundle/Serializer/Normalizer/UserNormalizer.php +++ b/src/Bundle/ChillMainBundle/Serializer/Normalizer/UserNormalizer.php @@ -20,14 +20,21 @@ namespace Chill\MainBundle\Serializer\Normalizer; use Chill\MainBundle\Entity\User; +use Chill\MainBundle\Templating\Entity\UserRender; +use Symfony\Component\Serializer\Normalizer\NormalizerAwareInterface; +use Symfony\Component\Serializer\Normalizer\NormalizerAwareTrait; use Symfony\Component\Serializer\Normalizer\NormalizerInterface; -/** - * - * @internal we keep this normalizer, because the property 'text' may be replace by a rendering in the future - */ -class UserNormalizer implements NormalizerInterface +class UserNormalizer implements NormalizerInterface, NormalizerAwareInterface { + use NormalizerAwareTrait; + private UserRender $userRender; + + public function __construct(UserRender $userRender) + { + $this->userRender = $userRender; + } + public function normalize($user, string $format = null, array $context = array()) { /** @var User $user */ @@ -35,7 +42,11 @@ class UserNormalizer implements NormalizerInterface 'type' => 'user', 'id' => $user->getId(), 'username' => $user->getUsername(), - 'text' => $user->getUsername() + 'text' => $this->userRender->renderString($user, []), + 'label' => $user->getLabel(), + 'user_job' => $this->normalizer->normalize($user->getUserJob(), $format, $context), + 'main_center' => $this->normalizer->normalize($user->getMainCenter(), $format, $context), + 'main_scope' => $this->normalizer->normalize($user->getMainScope(), $format, $context), ]; } diff --git a/src/Bundle/ChillMainBundle/Templating/Entity/UserRender.php b/src/Bundle/ChillMainBundle/Templating/Entity/UserRender.php new file mode 100644 index 000000000..121b94ead --- /dev/null +++ b/src/Bundle/ChillMainBundle/Templating/Entity/UserRender.php @@ -0,0 +1,66 @@ + true, + 'user_job' => true + ]; + + public function __construct(TranslatableStringHelper $translatableStringHelper, EngineInterface $engine) + { + $this->translatableStringHelper = $translatableStringHelper; + $this->engine = $engine; + } + + /** + * @inheritDoc + */ + public function supports($entity, array $options): bool + { + return $entity instanceof User; + } + + /** + * @inheritDoc + * @param User $entity + */ + public function renderString($entity, array $options): string + { + $opts = \array_merge(self::DEFAULT_OPTIONS, $options); + + $str = $entity->getLabel(); + if (NULL !== $entity->getUserJob() && $opts['user_job']) { + $str .= ' ('.$this->translatableStringHelper + ->localize($entity->getUserJob()->getLabel()).')'; + } + if (NULL !== $entity->getMainScope() && $opts['main_scope']) { + $str .= ' ('.$this->translatableStringHelper + ->localize($entity->getMainScope()->getName()).')'; + } + + return $str; + } + + /** + * @inheritDoc + */ + public function renderBox($entity, array $options): string + { + $opts = \array_merge(self::DEFAULT_OPTIONS, $options); + + return $this->engine->render('@ChillMain/Entity/user.html.twig', [ + 'user' => $entity, + 'opts' => $opts + ]); + } +} diff --git a/src/Bundle/ChillMainBundle/config/services/templating.yaml b/src/Bundle/ChillMainBundle/config/services/templating.yaml index e264f3a99..02d806db4 100644 --- a/src/Bundle/ChillMainBundle/config/services/templating.yaml +++ b/src/Bundle/ChillMainBundle/config/services/templating.yaml @@ -42,10 +42,12 @@ services: - { name: twig.extension } Chill\MainBundle\Templating\Entity\AddressRender: - arguments: - - '@Symfony\Component\Templating\EngineInterface' - tags: - - { name: 'chill.render_entity' } + autoconfigure: true + autowire: true + + Chill\MainBundle\Templating\Entity\UserRender: + autoconfigure: true + autowire: true Chill\MainBundle\Templating\Listing\: resource: './../../Templating/Listing' diff --git a/src/Bundle/ChillPersonBundle/Controller/AccompanyingCourseApiController.php b/src/Bundle/ChillPersonBundle/Controller/AccompanyingCourseApiController.php index ebb36ae39..428d2a15f 100644 --- a/src/Bundle/ChillPersonBundle/Controller/AccompanyingCourseApiController.php +++ b/src/Bundle/ChillPersonBundle/Controller/AccompanyingCourseApiController.php @@ -199,9 +199,9 @@ $workflow = $this->registry->get($accompanyingPeriod); } /** - * @Route("/api/1.0/person/accompanying-course/{id}/referral-availables.{_format}", + * @Route("/api/1.0/person/accompanying-course/{id}/referrers-suggested.{_format}", * requirements={ "_format"="json"}, - * name="chill_api_person_accompanying_period_referral_available") + * name="chill_api_person_accompanying_period_referrers_suggested") * @param AccompanyingPeriod $period * @return JsonResponse */ diff --git a/src/Bundle/ChillPersonBundle/Resources/public/vuejs/AccompanyingCourse/api.js b/src/Bundle/ChillPersonBundle/Resources/public/vuejs/AccompanyingCourse/api.js index c3b554dc4..52f3f6c36 100644 --- a/src/Bundle/ChillPersonBundle/Resources/public/vuejs/AccompanyingCourse/api.js +++ b/src/Bundle/ChillPersonBundle/Resources/public/vuejs/AccompanyingCourse/api.js @@ -170,11 +170,8 @@ const postSocialIssue = (id, body, method) => { const getUsers = () => { const url = `/api/1.0/main/user.json`; - return fetch(url) - .then(response => { - if (response.ok) { return response.json(); } - throw { msg: 'Error while retriving users.', sta: response.status, txt: response.statusText, err: new Error(), body: response.body }; - }); + + return fetchResults(url); }; const whoami = () => { @@ -235,8 +232,8 @@ const removeScope = (id, scope) => { }); }; -const getAvailableReferrals = (course) => { - const url = `/api/1.0/person/accompanying-course/${course.id}/referral-availables.json`; +const getReferrersSuggested = (course) => { + const url = `/api/1.0/person/accompanying-course/${course.id}/referrers-suggested.json`; return fetchResults(url); } @@ -255,5 +252,5 @@ export { postSocialIssue, addScope, removeScope, - getAvailableReferrals + getReferrersSuggested, }; diff --git a/src/Bundle/ChillPersonBundle/Resources/public/vuejs/AccompanyingCourse/components/Referrer.vue b/src/Bundle/ChillPersonBundle/Resources/public/vuejs/AccompanyingCourse/components/Referrer.vue index 169d8c3bb..c39e043c6 100644 --- a/src/Bundle/ChillPersonBundle/Resources/public/vuejs/AccompanyingCourse/components/Referrer.vue +++ b/src/Bundle/ChillPersonBundle/Resources/public/vuejs/AccompanyingCourse/components/Referrer.vue @@ -15,9 +15,20 @@ v-bind:searchable="true" v-bind:placeholder="$t('referrer.placeholder')" v-model="value" - v-bind:options="referrersAvailable" + v-bind:options="users" @select="updateReferrer"> + + + + +
@@ -41,14 +52,26 @@ import VueMultiselect from 'vue-multiselect'; import { getUsers, whoami } from '../api'; import { mapState } from 'vuex'; +import UserRenderBoxBadge from "ChillMainAssets/vuejs/_components/Entity/UserRenderBoxBadge"; export default { name: "Referrer", - components: { VueMultiselect }, + components: { + UserRenderBoxBadge, + VueMultiselect, + }, computed: { ...mapState({ value: state => state.accompanyingCourse.user, - referrersAvailable: state => state.referrersAvailable, + users: state => state.users, + referrersSuggested: state => { + return state.referrersSuggested.filter(u => { + if (null === state.accompanyingCourse.user) { + return true; + } + return state.accompanyingCourse.user.id !== u.id; + }) + }, }), }, methods: { diff --git a/src/Bundle/ChillPersonBundle/Resources/public/vuejs/AccompanyingCourse/store/index.js b/src/Bundle/ChillPersonBundle/Resources/public/vuejs/AccompanyingCourse/store/index.js index e944aa496..9b69845cf 100644 --- a/src/Bundle/ChillPersonBundle/Resources/public/vuejs/AccompanyingCourse/store/index.js +++ b/src/Bundle/ChillPersonBundle/Resources/public/vuejs/AccompanyingCourse/store/index.js @@ -10,7 +10,8 @@ import { getAccompanyingCourse, postSocialIssue, addScope, removeScope, - getAvailableReferrals, + getReferrersSuggested, + getUsers, } from '../api'; import { patchPerson } from "ChillPersonAssets/vuejs/_api/OnTheFly"; import { patchThirdparty } from "ChillThirdPartyAssets/vuejs/_api/OnTheFly"; @@ -40,7 +41,9 @@ let initPromise = Promise.all([scopesPromise, accompanyingCoursePromise]) // the scope states at server side scopesAtBackend: accompanyingCourse.scopes.map(scope => scope), // the users which are available for referrer - referrersAvailable: [], + referrersSuggested: [], + // all the users available + users: [], }, getters: { isParticipationValid(state) { @@ -173,8 +176,15 @@ let initPromise = Promise.all([scopesPromise, accompanyingCoursePromise]) //console.log('value', value); state.accompanyingCourse.user = value; }, - setReferrersAvailables(state, users) { - state.referrersAvailable = users; + setReferrersSuggested(state, users) { + state.referrersSuggested = users.map(u => { + if (state.accompanyingCourse.user !== null) { + if (state.accompanyingCourse.user.id === u.id) { + return state.accompanyingCourse.user; + } + } + return u; + }); }, confirmAccompanyingCourse(state, response) { //console.log('### mutation: confirmAccompanyingCourse: response', response); @@ -184,6 +194,16 @@ let initPromise = Promise.all([scopesPromise, accompanyingCoursePromise]) //console.log('define location context'); state.addressContext = context; }, + setUsers(state, users) { + state.users = users.map(u => { + if (state.accompanyingCourse.user !== null) { + if (state.accompanyingCourse.user.id === u.id) { + return state.accompanyingCourse.user; + } + } + return u; + }); + }, updateLocation(state, r) { //console.log('### mutation: set location attributes', r); state.accompanyingCourse.locationStatus = r.locationStatus; @@ -397,14 +417,14 @@ let initPromise = Promise.all([scopesPromise, accompanyingCoursePromise]) commit('setScopes', scopes); return Promise.resolve(); }).then(() => { - return dispatch('fetchReferrersAvailable'); + return dispatch('fetchReferrersSuggested'); }); } else { return dispatch('setScopes', state.scopesAtStart).then(() => { commit('setScopes', scopes); return Promise.resolve(); }).then(() => { - return dispatch('fetchReferrersAvailable'); + return dispatch('fetchReferrersSuggested'); }); } }, @@ -458,7 +478,7 @@ let initPromise = Promise.all([scopesPromise, accompanyingCoursePromise]) return getAccompanyingCourse(state.accompanyingCourse.id); }).then(accompanying_course => { commit('refreshSocialIssues', accompanying_course.socialIssues); - dispatch('fetchReferrersAvailable'); + dispatch('fetchReferrersSuggested'); }) .catch((error) => { commit('catchError', error) }); }, @@ -476,20 +496,23 @@ let initPromise = Promise.all([scopesPromise, accompanyingCoursePromise]) resolve(); })).catch((error) => { commit('catchError', error) }); }, - async fetchReferrersAvailable({ state, commit}) { - let users = await getAvailableReferrals(state.accompanyingCourse); - commit('setReferrersAvailables', users); - let userIds = users.map(u => u.id); + async fetchReferrersSuggested({ state, commit}) { + let users = await getReferrersSuggested(state.accompanyingCourse); + commit('setReferrersSuggested', users); if ( - null !== state.accompanyingCourse.user + null === state.accompanyingCourse.user && !state.accompanyingCourse.confidential && !state.accompanyingCourse.step === 'DRAFT' + && users.length === 1 ) { - if (!userIds.include(state.accompanyingCourse.user.id)) { - commit('updateReferrer', null); - } + // set the user if unique + commit('updateReferrer', users[0]); } }, + async fetchUsers({commit}) { + let users = await getUsers(); + commit('setUsers', users); + }, updateLocation({ commit, dispatch }, payload) { //console.log('## action: updateLocation', payload.locationStatusTo); let body = { 'type': payload.target, 'id': payload.targetId }; @@ -512,7 +535,7 @@ let initPromise = Promise.all([scopesPromise, accompanyingCoursePromise]) personLocation: accompanyingCourse.personLocation }); resolve(); - dispatch('fetchReferrersAvailable'); + dispatch('fetchReferrersSuggested'); })).catch((error) => { commit('catchError', error) }); }, confirmAccompanyingCourse({ commit }) { @@ -528,7 +551,8 @@ let initPromise = Promise.all([scopesPromise, accompanyingCoursePromise]) } }); - store.dispatch('fetchReferrersAvailable'); + store.dispatch('fetchReferrersSuggested'); + store.dispatch('fetchUsers'); resolve(store); })); diff --git a/src/Bundle/ChillPersonBundle/Tests/Controller/AccompanyingCourseApiControllerTest.php b/src/Bundle/ChillPersonBundle/Tests/Controller/AccompanyingCourseApiControllerTest.php index 80bb1d7cc..78376b8bd 100644 --- a/src/Bundle/ChillPersonBundle/Tests/Controller/AccompanyingCourseApiControllerTest.php +++ b/src/Bundle/ChillPersonBundle/Tests/Controller/AccompanyingCourseApiControllerTest.php @@ -332,7 +332,7 @@ class AccompanyingCourseApiControllerTest extends WebTestCase { $this->client->request( Request::METHOD_POST, - sprintf('/api/1.0/person/accompanying-course/%d/referral-availables.json', $periodId) + sprintf('/api/1.0/person/accompanying-course/%d/referrers-suggested.json', $periodId) ); $this->assertEquals(200, $this->client->getResponse()->getStatusCode()); diff --git a/src/Bundle/ChillPersonBundle/chill.api.specs.yaml b/src/Bundle/ChillPersonBundle/chill.api.specs.yaml index a3621dac1..a87711de2 100644 --- a/src/Bundle/ChillPersonBundle/chill.api.specs.yaml +++ b/src/Bundle/ChillPersonBundle/chill.api.specs.yaml @@ -929,7 +929,7 @@ paths: description: "OK" 422: description: "object with validation errors" - /1.0/person/accompanying-course/{id}/referral-availables.json: + /1.0/person/accompanying-course/{id}/referrers-suggested.json: get: tags: - accompanying-course