Merge remote-tracking branch 'origin/master' into issue420_closeModalAddPerson

This commit is contained in:
Julien Fastré 2022-01-31 14:24:45 +01:00
commit 222dae3c32
48 changed files with 663 additions and 235 deletions

View File

@ -14,8 +14,14 @@ and this project adheres to
* [person] accompanying course: optimisation: do not fetch some resources for the banner (https://gitlab.com/champs-libres/departement-de-la-vendee/chill/-/issues/409) * [person] accompanying course: optimisation: do not fetch some resources for the banner (https://gitlab.com/champs-libres/departement-de-la-vendee/chill/-/issues/409)
* [person] accompanying course: close modal when edit participation (https://gitlab.com/champs-libres/departement-de-la-vendee/chill/-/issues/420) * [person] accompanying course: close modal when edit participation (https://gitlab.com/champs-libres/departement-de-la-vendee/chill/-/issues/420)
* [person] accompanying course: treat validation error when editing on-the-fly entities (https://gitlab.com/champs-libres/departement-de-la-vendee/chill/-/issues/420) * [person] accompanying course: treat validation error when editing on-the-fly entities (https://gitlab.com/champs-libres/departement-de-la-vendee/chill/-/issues/420)
* [activity] show activity attendee (présence) in the activity list (https://gitlab.com/champs-libres/departement-de-la-vendee/chill/-/issues/412)
* [activity] admin: change validation rule for social action visible field (https://gitlab.com/champs-libres/departement-de-la-vendee/chill/-/issues/413)
* [parcours]: component added to change the opening date of a parcours (https://gitlab.com/champs-libres/departement-de-la-vendee/chill/-/issues/411)
* [search]: listing of parcours display changed (https://gitlab.com/champs-libres/departement-de-la-vendee/chill/-/issues/410) * [search]: listing of parcours display changed (https://gitlab.com/champs-libres/departement-de-la-vendee/chill/-/issues/410)
* [user]: page with accompanying periods to which is user is referent (https://gitlab.com/champs-libres/departement-de-la-vendee/chill/-/issues/408) * [user]: page with accompanying periods to which is user is referent (https://gitlab.com/champs-libres/departement-de-la-vendee/chill/-/issues/408)
* [person] age added to renderstring + renderbox/ vue component created to display person text (https://gitlab.com/champs-libres/departement-de-la-vendee/chill/-/issues/389)
* [household member editor] allow to push to existing household
## Test releases ## Test releases
@ -33,10 +39,10 @@ and this project adheres to
### test release 2021-01-26 ### test release 2021-01-26
>>>>>>> origin/master
* [parcours] comments truncated if too long + link added (https://gitlab.com/champs-libres/departement-de-la-vendee/chill/-/issues/406) * [parcours] comments truncated if too long + link added (https://gitlab.com/champs-libres/departement-de-la-vendee/chill/-/issues/406)
* [person]: possibility to add person resources (https://gitlab.com/champs-libres/departement-de-la-vendee/chill/-/issues/382) * [person]: possibility to add person resources (https://gitlab.com/champs-libres/departement-de-la-vendee/chill/-/issues/382)
* [person ressources]: module added * [person ressources]: module added
* [parcours] bugfix if deathdate is not defined (eg. for a thirdparty) parcours is still displayed. Gave error before.
### test release 2022-01-24 ### test release 2022-01-24

View File

@ -16,6 +16,7 @@ use InvalidArgumentException;
use Symfony\Component\Serializer\Annotation as Serializer; use Symfony\Component\Serializer\Annotation as Serializer;
use Symfony\Component\Serializer\Annotation\Groups; use Symfony\Component\Serializer\Annotation\Groups;
use Symfony\Component\Validator\Constraints as Assert; use Symfony\Component\Validator\Constraints as Assert;
use Symfony\Component\Validator\Context\ExecutionContextInterface;
/** /**
* Class ActivityType. * Class ActivityType.
@ -193,7 +194,6 @@ class ActivityType
/** /**
* @ORM\Column(type="smallint", nullable=false, options={"default": 1}) * @ORM\Column(type="smallint", nullable=false, options={"default": 1})
* @Assert\EqualTo(propertyPath="socialIssuesVisible", message="This parameter must be equal to social issue parameter")
*/ */
private int $socialActionsVisible = self::FIELD_INVISIBLE; private int $socialActionsVisible = self::FIELD_INVISIBLE;
@ -263,6 +263,23 @@ class ActivityType
*/ */
private int $userVisible = self::FIELD_REQUIRED; private int $userVisible = self::FIELD_REQUIRED;
/**
* @Assert\Callback
*
* @param mixed $payload
*/
public function checkSocialActionsVisibility(ExecutionContextInterface $context, $payload)
{
if ($this->socialIssuesVisible !== $this->socialActionsVisible) {
if (!($this->socialIssuesVisible === 2 && $this->socialActionsVisible === 1)) {
$context
->buildViolation('The socialActionsVisible value is not compatible with the socialIssuesVisible value')
->atPath('socialActionsVisible')
->addViolation();
}
}
}
/** /**
* Get active * Get active
* return true if the type is active. * return true if the type is active.

View File

@ -1,45 +1,48 @@
<template> <template>
<teleport to="#add-persons" v-if="isComponentVisible"> <teleport to="#add-persons" v-if="isComponentVisible">
<div class="flex-bloc concerned-groups" :class="getContext"> <div class="flex-bloc concerned-groups" :class="getContext">
<persons-bloc <persons-bloc
v-for="bloc in contextPersonsBlocs" v-for="bloc in contextPersonsBlocs"
v-bind:key="bloc.key" v-bind:key="bloc.key"
v-bind:bloc="bloc" v-bind:bloc="bloc"
v-bind:blocWidth="getBlocWidth" v-bind:blocWidth="getBlocWidth"
v-bind:setPersonsInBloc="setPersonsInBloc"> v-bind:setPersonsInBloc="setPersonsInBloc">
</persons-bloc> </persons-bloc>
</div> </div>
<div v-if="getContext === 'accompanyingCourse' && suggestedEntities.length > 0"> <div v-if="getContext === 'accompanyingCourse' && suggestedEntities.length > 0">
<ul class="list-suggest add-items inline"> <ul class="list-suggest add-items inline">
<li v-for="(p, i) in suggestedEntities" @click="addSuggestedEntity(p)" :key="`suggestedEntities-${i}`"> <li v-for="(p, i) in suggestedEntities" @click="addSuggestedEntity(p)" :key="`suggestedEntities-${i}`">
<span>{{ p.text }}</span> <person-text v-if="p.type === 'person'" :person="p"></person-text>
</li> <span v-else>{{ p.text }}</span>
</ul> </li>
</div> </ul>
</div>
<add-persons <add-persons
buttonTitle="activity.add_persons" buttonTitle="activity.add_persons"
modalTitle="activity.add_persons" modalTitle="activity.add_persons"
v-bind:key="addPersons.key" v-bind:key="addPersons.key"
v-bind:options="addPersonsOptions" v-bind:options="addPersonsOptions"
@addNewPersons="addNewPersons" @addNewPersons="addNewPersons"
ref="addPersons"> ref="addPersons">
</add-persons> </add-persons>
</teleport> </teleport>
</template> </template>
<script> <script>
import { mapState, mapGetters } from 'vuex'; import { mapState, mapGetters } from 'vuex';
import AddPersons from 'ChillPersonAssets/vuejs/_components/AddPersons.vue'; import AddPersons from 'ChillPersonAssets/vuejs/_components/AddPersons.vue';
import PersonsBloc from './ConcernedGroups/PersonsBloc.vue'; import PersonsBloc from './ConcernedGroups/PersonsBloc.vue';
import PersonText from 'ChillPersonAssets/vuejs/_components/Entity/PersonText.vue';
export default { export default {
name: "ConcernedGroups", name: "ConcernedGroups",
components: { components: {
AddPersons, AddPersons,
PersonsBloc PersonsBloc,
PersonText
}, },
data() { data() {
return { return {

View File

@ -1,21 +1,29 @@
<template> <template>
<li> <li>
<span :title="person.text"> <span :title="person.text">
<span class="chill_denomination" @click.prevent="$emit('remove', person)">{{ textCutted }}</span> <span class="chill_denomination" @click.prevent="$emit('remove', person)">
<person-text :person="person" :isCut="true"></person-text>
</span>
</span> </span>
</li> </li>
</template> </template>
<script> <script>
import PersonText from 'ChillPersonAssets/vuejs/_components/Entity/PersonText.vue';
export default { export default {
name: "PersonBadge", name: "PersonBadge",
props: ['person'], props: ['person'],
computed: { components: {
textCutted() { PersonText
let more = (this.person.text.length > 15) ?'…' : '';
return this.person.text.slice(0,15) + more;
}
}, },
// computed: {
// textCutted() {
// let more = (this.person.text.length > 15) ?'' : '';
// return this.person.text.slice(0,15) + more;
// }
// },
emits: ['remove'], emits: ['remove'],
} }
</script> </script>

View File

@ -41,6 +41,17 @@
</div> </div>
{% endif %} {% endif %}
{% if activity.attendee and t.attendeeVisible %}
<div class="wl-row">
<div class="wl-col title"><h3>{{ 'Attendee'|trans }}</h3></div>
<div class="wl-col list">
<p class="wl-item">
{{ activity.attendee.name|localize_translatable_string }}
</p>
</div>
</div>
{% endif %}
{% if activity.sentReceived is not empty and t.sentReceivedVisible %} {% if activity.sentReceived is not empty and t.sentReceivedVisible %}
<div class="wl-row"> <div class="wl-row">
<div class="wl-col title"><h3>{{ 'Sent received'|trans }}</h3></div> <div class="wl-col title"><h3>{{ 'Sent received'|trans }}</h3></div>

View File

@ -48,7 +48,7 @@
<li class="associated-persons"> <li class="associated-persons">
<span class="item-key">{{ 'Participants'|trans ~ ' : ' }}</span> <span class="item-key">{{ 'Participants'|trans ~ ' : ' }}</span>
{% for p in activity.personsAssociated %} {% for p in activity.personsAssociated %}
<span class="badge-person">{{ p|chill_entity_render_box }}</span> <span class="badge-person">{{ p|chill_entity_render_box({'addAgeBadge': true}) }}</span>
{% endfor %} {% endfor %}
</li> </li>
</ul> </ul>

View File

@ -165,11 +165,7 @@
<dt class="inline">{{ 'Attendee'|trans }}</dt> <dt class="inline">{{ 'Attendee'|trans }}</dt>
<dd> <dd>
{% if entity.attendee is not null %} {% if entity.attendee is not null %}
{% if entity.attendee %} {{ entity.attendee.name|localize_translatable_string }}
{{ 'present'|trans|capitalize }}
{% else %}
{{ 'not present'|trans|capitalize }}
{% endif %}
{% else %} {% else %}
<span class="chill-no-data-statement">{{ 'None'|trans|capitalize }}</span> <span class="chill-no-data-statement">{{ 'None'|trans|capitalize }}</span>
{% endif %} {% endif %}

View File

@ -20,3 +20,4 @@ For this type of activity, you must add at least one social action: Pour ce type
# admin # admin
This parameter must be equal to social issue parameter: Ce paramètre doit être égal au paramètre "Visibilité du champs Problématiques sociales" This parameter must be equal to social issue parameter: Ce paramètre doit être égal au paramètre "Visibilité du champs Problématiques sociales"
The socialActionsVisible value is not compatible with the socialIssuesVisible value: Cette valeur du paramètre "Visibilité du champs Actions sociales" n'est pas compatible avec la valeur du paramètre "Visibilité du champs Problématiques sociales"

View File

@ -12,6 +12,7 @@
display: block; display: block;
top: calc(50% - 7px); top: calc(50% - 7px);
right: 10px; right: 10px;
line-height: 11px;
} }
} }
@ -62,14 +63,19 @@ ul.list-suggest {
& span:hover { & span:hover {
color: $chill-l-gray; color: $chill-l-gray;
} }
.person-text {
span {
padding-left: 0px;
}
}
} }
} }
&.remove-items { &.remove-items {
li { li {
position: relative; position: relative;
span { & > span {
display: block; display: block;
padding-right: .75rem; padding-right: 1.75rem;
@include remove_link; @include remove_link;
} }
} }

View File

@ -22,6 +22,7 @@ $chill-theme-buttons: (
"cancel": $gray-300, "cancel": $gray-300,
"choose": $gray-300, "choose": $gray-300,
"notify": $gray-300, "notify": $gray-300,
"search": $gray-300,
"unlink": $chill-red, "unlink": $chill-red,
"tpchild": $chill-pink, "tpchild": $chill-pink,
); );
@ -80,6 +81,7 @@ $chill-theme-buttons: (
&.btn-notify::before, &.btn-notify::before,
&.btn-tpchild::before, &.btn-tpchild::before,
&.btn-download::before, &.btn-download::before,
&.btn-search::before,
&.btn-cancel::before { &.btn-cancel::before {
font: normal normal normal 14px/1 ForkAwesome; font: normal normal normal 14px/1 ForkAwesome;
margin-right: 0.5em; margin-right: 0.5em;
@ -108,6 +110,7 @@ $chill-theme-buttons: (
&.btn-notify::before { content: "\f1d8"; } // fa-paper-plane &.btn-notify::before { content: "\f1d8"; } // fa-paper-plane
&.btn-tpchild::before { content: "\f007"; } // fa-user &.btn-tpchild::before { content: "\f007"; } // fa-user
&.btn-download::before { content: "\f019"; } // fa-download &.btn-download::before { content: "\f019"; } // fa-download
&.btn-search::before { content: "\f002"; } // fa-search
} }

View File

@ -24,6 +24,11 @@
{{ $t('user')}} {{ $t('user')}}
</span> </span>
<span v-if="entity.type === 'household'" class="badge rounded-pill bg-user">
{{ $t('household')}}
</span>
</template> </template>
<script> <script>
@ -40,7 +45,8 @@ export default {
company: "Personne morale", company: "Personne morale",
contact: "Personne physique", contact: "Personne physique",
}, },
user: 'TMS' user: 'TMS',
household: 'Ménage',
} }
} }
} }

View File

@ -61,14 +61,15 @@ const messages = {
woman: "Née le" woman: "Née le"
}, },
deathdate: "Date de décès", deathdate: "Date de décès",
years_old: "ans",
household_without_address: "Le ménage de l'usager est sans adresse", household_without_address: "Le ménage de l'usager est sans adresse",
no_data: "Aucune information renseignée", no_data: "Aucune information renseignée",
type: { type: {
thirdparty: "Tiers", thirdparty: "Tiers",
person: "Usager" person: "Usager"
}, },
holder: "Titulaire" holder: "Titulaire",
years_old: "an | {n} an | {n} ans",
} }
} }
}; };

View File

@ -42,6 +42,3 @@
{% endif %} {% endif %}
></span> ></span>
{{ encore_entry_script_tags('vue_onthefly') }}
{{ encore_entry_link_tags('vue_onthefly') }}

View File

@ -13,6 +13,7 @@
{{ encore_entry_link_tags('mod_ckeditor5') }} {{ encore_entry_link_tags('mod_ckeditor5') }}
{{ encore_entry_link_tags('chill') }} {{ encore_entry_link_tags('chill') }}
{{ encore_entry_link_tags('mod_blur') }} {{ encore_entry_link_tags('mod_blur') }}
{{ encore_entry_link_tags('vue_onthefly') }}
{% block css %}<!-- nothing added to css -->{% endblock %} {% block css %}<!-- nothing added to css -->{% endblock %}
</head> </head>
@ -94,6 +95,7 @@
{{ encore_entry_script_tags('mod_ckeditor5') }} {{ encore_entry_script_tags('mod_ckeditor5') }}
{{ encore_entry_script_tags('mod_blur') }} {{ encore_entry_script_tags('mod_blur') }}
{{ encore_entry_script_tags('chill') }} {{ encore_entry_script_tags('chill') }}
{{ encore_entry_script_tags('vue_onthefly') }}
<script type="text/javascript"> <script type="text/javascript">
window.addEventListener('DOMContentLoaded', function(e) { window.addEventListener('DOMContentLoaded', function(e) {

View File

@ -45,13 +45,15 @@ class SearchUserApiProvider implements SearchApiInterface
$query $query
->setSelectKey('user') ->setSelectKey('user')
->setSelectJsonbMetadata("jsonb_build_object('id', u.id)") ->setSelectJsonbMetadata("jsonb_build_object('id', u.id)")
->setSelectPertinence('GREATEST(SIMILARITY(LOWER(UNACCENT(?)), u.usernamecanonical), ->setSelectPertinence('GREATEST(SIMILARITY(LOWER(UNACCENT(?)), u.label),
SIMILARITY(LOWER(UNACCENT(?)), u.emailcanonical))', [$pattern, $pattern]) SIMILARITY(LOWER(UNACCENT(?)), u.usernamecanonical))', [$pattern, $pattern])
->setFromClause('users AS u') ->setFromClause('users AS u')
->setWhereClauses('SIMILARITY(LOWER(UNACCENT(?)), u.usernamecanonical) > 0.15 ->setWhereClauses('
OR SIMILARITY(LOWER(UNACCENT(?)), u.usernamecanonical) > 0.15
SIMILARITY(LOWER(UNACCENT(?)), u.emailcanonical) > 0.15 OR u.usernamecanonical LIKE \'%\' || LOWER(UNACCENT(?)) || \'%\'
', [$pattern, $pattern]); OR SIMILARITY(LOWER(UNACCENT(?)), LOWER(UNACCENT(u.label))) > 0.15
OR u.label LIKE \'%\' || LOWER(UNACCENT(?)) || \'%\'
', [$pattern, $pattern, $pattern, $pattern]);
return $query; return $query;
} }

View File

@ -22,6 +22,10 @@ class SearchApiQuery
private array $fromClauseParams = []; private array $fromClauseParams = [];
private bool $isDistinct = false;
private ?string $isDistinctKey = null;
private ?string $jsonbMetadata = null; private ?string $jsonbMetadata = null;
private array $jsonbMetadataParams = []; private array $jsonbMetadataParams = [];
@ -105,6 +109,11 @@ class SearchApiQuery
]); ]);
} }
public function getDistinct(): bool
{
return $this->isDistinct;
}
public function getFromClause(): string public function getFromClause(): string
{ {
return $this->fromClause; return $this->fromClause;
@ -139,6 +148,14 @@ class SearchApiQuery
return $this; return $this;
} }
public function setDistinct(bool $distinct, string $distinctKey): self
{
$this->isDistinct = $distinct;
$this->isDistinctKey = $distinctKey;
return $this;
}
public function setFromClause(string $fromClause, array $params = []): self public function setFromClause(string $fromClause, array $params = []): self
{ {
$this->fromClause = $fromClause; $this->fromClause = $fromClause;
@ -185,7 +202,11 @@ class SearchApiQuery
private function buildSelectClause(bool $countOnly = false): string private function buildSelectClause(bool $countOnly = false): string
{ {
if ($countOnly) { if ($countOnly) {
return 'count(*) AS c'; if (!$this->isDistinct) {
return 'count(*) AS c';
}
return 'count(distinct ' . $this->isDistinctKey . ') AS c';
} }
$selects = $this->getSelectClauses(); $selects = $this->getSelectClauses();
@ -202,7 +223,7 @@ class SearchApiQuery
$selects[] = strtr('{pertinence} AS pertinence', ['{pertinence}' => $this->pertinence]); $selects[] = strtr('{pertinence} AS pertinence', ['{pertinence}' => $this->pertinence]);
} }
return implode(', ', $selects); return ($this->isDistinct ? 'DISTINCT ' : '') . implode(', ', $selects);
} }
private function buildSelectParams(bool $count = false): array private function buildSelectParams(bool $count = false): array

View File

@ -134,6 +134,7 @@ paths:
- search - search
- person - person
- thirdparty - thirdparty
- household
description: > description: >
The search is performed across multiple entities. The entities must be listed into The search is performed across multiple entities. The entities must be listed into
`type` parameters. `type` parameters.
@ -159,6 +160,7 @@ paths:
- person - person
- thirdparty - thirdparty
- user - user
- household
responses: responses:
200: 200:
description: "OK" description: "OK"

View File

@ -9,7 +9,6 @@
declare(strict_types=1); declare(strict_types=1);
namespace Chill\PersonBundle\Controller; namespace Chill\PersonBundle\Controller;
use Chill\MainBundle\Pagination\PaginatorFactory; use Chill\MainBundle\Pagination\PaginatorFactory;
@ -20,7 +19,6 @@ use Symfony\Component\Routing\Annotation\Route;
class UserAccompanyingPeriodController extends AbstractController class UserAccompanyingPeriodController extends AbstractController
{ {
private AccompanyingPeriodRepository $accompanyingPeriodRepository; private AccompanyingPeriodRepository $accompanyingPeriodRepository;
private PaginatorFactory $paginatorFactory; private PaginatorFactory $paginatorFactory;
@ -38,13 +36,16 @@ class UserAccompanyingPeriodController extends AbstractController
{ {
$total = $this->accompanyingPeriodRepository->countBy(['user' => $this->getUser()]); $total = $this->accompanyingPeriodRepository->countBy(['user' => $this->getUser()]);
$pagination = $this->paginatorFactory->create($total); $pagination = $this->paginatorFactory->create($total);
$accompanyingPeriods = $this->accompanyingPeriodRepository->findBy(['user' => $this->getUser()], $accompanyingPeriods = $this->accompanyingPeriodRepository->findBy(
['openingDate' => 'DESC'], $pagination->getItemsPerPage(), $pagination->getCurrentPageFirstItemNumber()); ['user' => $this->getUser()],
['openingDate' => 'DESC'],
$pagination->getItemsPerPage(),
$pagination->getCurrentPageFirstItemNumber()
);
return $this->render('@ChillPerson/AccompanyingPeriod/user_periods_list.html.twig', [ return $this->render('@ChillPerson/AccompanyingPeriod/user_periods_list.html.twig', [
'accompanyingPeriods' => $accompanyingPeriods, 'accompanyingPeriods' => $accompanyingPeriods,
'pagination' => $pagination, 'pagination' => $pagination,
]); ]);
} }
} }

View File

@ -212,6 +212,8 @@ class AccompanyingPeriod implements
* *
* @ORM\Column(type="date") * @ORM\Column(type="date")
* @Groups({"read", "write", "docgen:read"}) * @Groups({"read", "write", "docgen:read"})
* @Assert\LessThan(value= "today", groups={AccompanyingPeriod::STEP_CONFIRMED})
* @Assert\LessThan(propertyPath="closingDate", groups={AccompanyingPeriod::STEP_CONFIRMED})
*/ */
private ?DateTime $openingDate = null; private ?DateTime $openingDate = null;

View File

@ -12,9 +12,7 @@ declare(strict_types=1);
namespace Chill\PersonBundle\Menu; namespace Chill\PersonBundle\Menu;
use Chill\MainBundle\Routing\LocalMenuBuilderInterface; use Chill\MainBundle\Routing\LocalMenuBuilderInterface;
use Chill\TaskBundle\Templating\UI\CountNotificationTask;
use Knp\Menu\MenuItem; use Knp\Menu\MenuItem;
use Symfony\Component\Security\Core\Authentication\Token\Storage\TokenStorageInterface;
use Symfony\Component\Security\Core\Authorization\AuthorizationCheckerInterface; use Symfony\Component\Security\Core\Authorization\AuthorizationCheckerInterface;
use Symfony\Contracts\Translation\TranslatorInterface; use Symfony\Contracts\Translation\TranslatorInterface;
@ -25,7 +23,6 @@ class UserMenuBuilder implements LocalMenuBuilderInterface
*/ */
public $authorizationChecker; public $authorizationChecker;
/** /**
* @var TranslatorInterface * @var TranslatorInterface
*/ */

View File

@ -26,6 +26,11 @@ final class AccompanyingPeriodRepository implements ObjectRepository
$this->repository = $entityManager->getRepository(AccompanyingPeriod::class); $this->repository = $entityManager->getRepository(AccompanyingPeriod::class);
} }
public function countBy(array $criteria): int
{
return $this->repository->count($criteria);
}
public function createQueryBuilder(string $alias, ?string $indexBy = null): QueryBuilder public function createQueryBuilder(string $alias, ?string $indexBy = null): QueryBuilder
{ {
return $this->repository->createQueryBuilder($alias, $indexBy); return $this->repository->createQueryBuilder($alias, $indexBy);
@ -49,11 +54,6 @@ final class AccompanyingPeriodRepository implements ObjectRepository
return $this->repository->findBy($criteria, $orderBy, $limit, $offset); return $this->repository->findBy($criteria, $orderBy, $limit, $offset);
} }
public function countBy(array $criteria): int
{
return $this->repository->count($criteria);
}
public function findOneBy(array $criteria): ?AccompanyingPeriod public function findOneBy(array $criteria): ?AccompanyingPeriod
{ {
return $this->findOneBy($criteria); return $this->findOneBy($criteria);

View File

@ -14,6 +14,7 @@
<scopes></scopes> <scopes></scopes>
<referrer></referrer> <referrer></referrer>
<resources></resources> <resources></resources>
<start-date v-if="accompanyingCourse.step === 'CONFIRMED'"></start-date>
<comment v-if="accompanyingCourse.step === 'DRAFT'"></comment> <comment v-if="accompanyingCourse.step === 'DRAFT'"></comment>
<confirm v-if="accompanyingCourse.step === 'DRAFT'"></confirm> <confirm v-if="accompanyingCourse.step === 'DRAFT'"></confirm>
@ -39,6 +40,7 @@ import Referrer from './components/Referrer.vue';
import Resources from './components/Resources.vue'; import Resources from './components/Resources.vue';
import Comment from './components/Comment.vue'; import Comment from './components/Comment.vue';
import Confirm from './components/Confirm.vue'; import Confirm from './components/Confirm.vue';
import StartDate from './components/StartDate.vue';
export default { export default {
name: 'App', name: 'App',
@ -56,6 +58,7 @@ export default {
Resources, Resources,
Comment, Comment,
Confirm, Confirm,
StartDate
}, },
computed: { computed: {
...mapState([ ...mapState([

View File

@ -4,7 +4,7 @@
<i class="fa fa-home fa-fw text-light" :title="$t('persons_associated.show_household_number', { id: h.id })"></i> <i class="fa fa-home fa-fw text-light" :title="$t('persons_associated.show_household_number', { id: h.id })"></i>
</a> </a>
<span v-for="person in h.persons" class="me-1" :key="person.id"> <span v-for="person in h.persons" class="me-1" :key="person.id">
<on-the-fly :type="person.type" :id="person.id" :buttonText="person.text" :displayBadge="'true' === 'true'" action="show"></on-the-fly> <on-the-fly :type="person.type" :id="person.id" :buttonText="person.textAge" :displayBadge="'true' === 'true'" action="show"></on-the-fly>
</span> </span>
</span> </span>
</template> </template>

View File

@ -27,7 +27,7 @@
:value="p.person.id" :value="p.person.id"
/> />
<label class="form-check-label"> <label class="form-check-label">
{{ p.person.text }} <person-text :person="p.person"></person-text>
</label> </label>
</div> </div>
<input type="hidden" name="expand_suggestions" value="true"> <input type="hidden" name="expand_suggestions" value="true">
@ -50,9 +50,9 @@
<div v-if="suggestedPersons.length > 0"> <div v-if="suggestedPersons.length > 0">
<ul class="list-suggest add-items inline"> <ul class="list-suggest add-items inline">
<li v-for="p in suggestedPersons" :key="p.id" @click="addSuggestedPerson(p)"> <li v-for="p in suggestedPersons" :key="p.id" @click="addSuggestedPerson(p)">
<span>{{ p.text }}</span> <person-text :person="p"></person-text>
</li> </li>
</ul> </ul>
</div> </div>
<div> <div>
@ -76,12 +76,14 @@
import {mapGetters, mapState} from 'vuex'; import {mapGetters, mapState} from 'vuex';
import ParticipationItem from "./PersonsAssociated/ParticipationItem.vue"; import ParticipationItem from "./PersonsAssociated/ParticipationItem.vue";
import AddPersons from 'ChillPersonAssets/vuejs/_components/AddPersons.vue'; import AddPersons from 'ChillPersonAssets/vuejs/_components/AddPersons.vue';
import PersonText from 'ChillPersonAssets/vuejs/_components/Entity/PersonText.vue';
export default { export default {
name: 'PersonsAssociated', name: 'PersonsAssociated',
components: { components: {
ParticipationItem, ParticipationItem,
AddPersons AddPersons,
PersonText
}, },
data() { data() {
return { return {
@ -110,15 +112,15 @@ export default {
) )
// filter persons appearing twice in requestor and resources // filter persons appearing twice in requestor and resources
.filter( .filter(
(e, index, suggested) => { (e, index, suggested) => {
for (let i = 0; i < suggested.length; i = i+1) { for (let i = 0; i < suggested.length; i = i+1) {
if (i < index && e.id === suggested[i].id) { if (i < index && e.id === suggested[i].id) {
return false return false
} }
} }
return true; return true;
} }
) )
}), }),
...mapGetters([ ...mapGetters([

View File

@ -45,7 +45,8 @@
addInfo: true, addInfo: true,
hLevel: 3, hLevel: 3,
isMultiline: true, isMultiline: true,
isConfidential: false isConfidential: false,
addAge: true,
}" }"
> >
<template v-slot:record-actions> <template v-slot:record-actions>
@ -136,9 +137,10 @@
<div v-if="accompanyingCourse.requestor === null && suggestedEntities.length > 0"> <div v-if="accompanyingCourse.requestor === null && suggestedEntities.length > 0">
<ul class="list-suggest add-items inline"> <ul class="list-suggest add-items inline">
<li v-for="p in suggestedEntities" :key="uniqueId(p)" @click="addSuggestedEntity(p)"> <li v-for="p in suggestedEntities" :key="uniqueId(p)" @click="addSuggestedEntity(p)">
<span>{{ p.text }}</span> <person-text v-if="p.type === 'person'" :person="p"></person-text>
</li> <span v-else>{{ p.text }}</span>
</ul> </li>
</ul>
</div> </div>
<div> <div>
@ -163,6 +165,8 @@ import ThirdPartyRenderBox from 'ChillThirdPartyAssets/vuejs/_components/Entity/
import Confidential from 'ChillMainAssets/vuejs/_components/Confidential.vue'; import Confidential from 'ChillMainAssets/vuejs/_components/Confidential.vue';
import { mapState } from 'vuex'; import { mapState } from 'vuex';
import { makeFetch } from 'ChillMainAssets/lib/api/apiMethods'; import { makeFetch } from 'ChillMainAssets/lib/api/apiMethods';
import PersonText from 'ChillPersonAssets/vuejs/_components/Entity/PersonText.vue';
export default { export default {
name: 'Requestor', name: 'Requestor',
@ -171,7 +175,8 @@ export default {
OnTheFly, OnTheFly,
PersonRenderBox, PersonRenderBox,
ThirdPartyRenderBox, ThirdPartyRenderBox,
Confidential Confidential,
PersonText
}, },
props: ['isAnonymous'], props: ['isAnonymous'],
data() { data() {

View File

@ -22,7 +22,8 @@
<div v-if="suggestedEntities.length > 0"> <div v-if="suggestedEntities.length > 0">
<ul class="list-suggest add-items inline"> <ul class="list-suggest add-items inline">
<li v-for="p in suggestedEntities" :key="uniqueId(p)" @click="addSuggestedEntity(p)"> <li v-for="p in suggestedEntities" :key="uniqueId(p)" @click="addSuggestedEntity(p)">
<span>{{ p.text }}</span> <person-text v-if="p.type === 'person'" :person="p"></person-text>
<span v-else>{{ p.text }}</span>
</li> </li>
</ul> </ul>
</div> </div>
@ -45,12 +46,15 @@
import { mapState } from 'vuex'; import { mapState } from 'vuex';
import AddPersons from 'ChillPersonAssets/vuejs/_components/AddPersons.vue'; import AddPersons from 'ChillPersonAssets/vuejs/_components/AddPersons.vue';
import ResourceItem from './Resources/ResourceItem.vue'; import ResourceItem from './Resources/ResourceItem.vue';
import PersonText from 'ChillPersonAssets/vuejs/_components/Entity/PersonText.vue';
export default { export default {
name: 'Resources', name: 'Resources',
components: { components: {
AddPersons, AddPersons,
ResourceItem ResourceItem,
PersonText
}, },
data() { data() {
return { return {

View File

@ -0,0 +1,46 @@
<template>
<div class="vue-component">
<h2><a id="section-110"></a>
{{ $t('startdate.change') }}
</h2>
<div>
<div class="mb-3 row">
<label class="col-form-label col-sm-4">{{ $t('startdate.date') }}</label>
<div class="col-sm-8">
<input class="form-control" type="date" v-model="startDate" @change="updateStartDate" />
</div>
</div>
</div>
</div>
</template>
<script>
import { datetimeToISO, dateToISO, ISOToDate, ISOToDatetime} from 'ChillMainAssets/chill/js/date.js';
import { mapState, mapGetters } from 'vuex';
export default {
name: 'startDate',
methods: {
updateStartDate(event) {
const date = event.target.value;
// console.log(date)
this.$store.dispatch('updateStartDate', date)
.catch(({name, violations}) => {
if (name === 'ValidationException' || name === 'AccessException') {
violations.forEach((violation) => this.$toast.open({message: violation}));
} else {
this.$toast.open({message: 'An error occurred'})
}
});
},
},
computed: {
...mapState({
startDate: state => dateToISO(ISOToDatetime(state.accompanyingCourse.openingDate.datetime))
})
}
}
</script>

View File

@ -151,6 +151,10 @@ const appMessages = {
placeholder: "Choisir un métier", placeholder: "Choisir un métier",
not_valid: "Sélectionnez un métier du référent" not_valid: "Sélectionnez un métier du référent"
}, },
startdate: {
change: "Modifier la date de début",
date: "Date de début",
},
// catch errors // catch errors
'Error while updating AccompanyingPeriod Course.': "Erreur du serveur lors de la mise à jour du parcours d'accompagnement.", 'Error while updating AccompanyingPeriod Course.': "Erreur du serveur lors de la mise à jour du parcours d'accompagnement.",
'Error while retriving AccompanyingPeriod Course.': "Erreur du serveur lors du chargement du parcours d'accompagnement.", 'Error while retriving AccompanyingPeriod Course.': "Erreur du serveur lors du chargement du parcours d'accompagnement.",

View File

@ -8,6 +8,7 @@ import { getAccompanyingCourse,
import { patchPerson } from "ChillPersonAssets/vuejs/_api/OnTheFly"; import { patchPerson } from "ChillPersonAssets/vuejs/_api/OnTheFly";
import { patchThirdparty } from "ChillThirdPartyAssets/vuejs/_api/OnTheFly"; import { patchThirdparty } from "ChillThirdPartyAssets/vuejs/_api/OnTheFly";
import { makeFetch } from 'ChillMainAssets/lib/api/apiMethods'; import { makeFetch } from 'ChillMainAssets/lib/api/apiMethods';
import { datetimeToISO, ISOToDate, ISOToDatetime } from 'ChillMainAssets/chill/js/date.js';
const debug = process.env.NODE_ENV !== 'production'; const debug = process.env.NODE_ENV !== 'production';
@ -282,6 +283,10 @@ let initPromise = (root) => Promise.all([getScopesPromise(root), accompanyingCou
if (scopeIds.includes(scope.id)) { if (scopeIds.includes(scope.id)) {
state.scopesAtBackend = state.scopesAtBackend.filter(s => s.id !== scope.id); state.scopesAtBackend = state.scopesAtBackend.filter(s => s.id !== scope.id);
} }
},
updateStartDate(state, date) {
console.log('new state date', date)
state.accompanyingCourse.openingDate = date;
} }
}, },
actions: { actions: {
@ -711,15 +716,30 @@ let initPromise = (root) => Promise.all([getScopesPromise(root), accompanyingCou
throw error; throw error;
}) })
}, },
updateStartDate({commit}, payload) {
console.log('payload', payload)
const date = ISOToDate(payload);
const url = `/api/1.0/person/accompanying-course/${id}.json`;
const body = { type: "accompanying_period", openingDate: { datetime: datetimeToISO(date) }};
console.log('body', body)
return makeFetch('PATCH', url, body)
.then((response) => {
commit('updateStartDate', response.openingDate);
})
.catch((error) => {
commit('catchError', error);
throw error;
})
},
async fetchReferrersSuggested({ state, commit}) { async fetchReferrersSuggested({ state, commit}) {
let users = await getReferrersSuggested(state.accompanyingCourse); let users = await getReferrersSuggested(state.accompanyingCourse);
commit('setReferrersSuggested', users); commit('setReferrersSuggested', users);
commit('setFilteredReferrersSuggested'); commit('setFilteredReferrersSuggested');
if ( if (
null === state.accompanyingCourse.user null === state.accompanyingCourse.user
&& !state.accompanyingCourse.confidential && !state.accompanyingCourse.confidential
&& !state.accompanyingCourse.step === 'DRAFT' && !state.accompanyingCourse.step === 'DRAFT'
&& users.length === 1 && users.length === 1
) { ) {
// set the user if unique // set the user if unique
commit('updateReferrer', users[0]); commit('updateReferrer', users[0]);

View File

@ -5,7 +5,7 @@
<div id="awc_create_form"> <div id="awc_create_form">
<div id="picking"> <div id="picking" class="">
<p>{{ $t('pick_social_issue_linked_with_action') }}</p> <p>{{ $t('pick_social_issue_linked_with_action') }}</p>
<div v-for="si in socialIssues" :key="si.id"> <div v-for="si in socialIssues" :key="si.id">
<input type="radio" v-bind:value="si.id" name="socialIssue" v-model="socialIssuePicked"><span class="badge bg-chill-l-gray text-dark">{{ si.text }}</span> <input type="radio" v-bind:value="si.id" name="socialIssue" v-model="socialIssuePicked"><span class="badge bg-chill-l-gray text-dark">{{ si.text }}</span>
@ -33,7 +33,7 @@
</vue-multiselect> </vue-multiselect>
</div> </div>
</div> </div>
<div v-if="hasSocialIssuePicked"> <div v-if="hasSocialIssuePicked" class="mb-3">
<h2>{{ $t('pick_an_action') }}</h2> <h2>{{ $t('pick_an_action') }}</h2>
<div class="col-11"> <div class="col-11">
<vue-multiselect <vue-multiselect
@ -52,26 +52,43 @@
<i class="fa fa-circle-o-notch fa-spin fa-fw"></i> <i class="fa fa-circle-o-notch fa-spin fa-fw"></i>
</div> </div>
<div v-if="hasSocialActionPicked" id="persons"> <div v-if="hasSocialActionPicked" id="persons" class="mb-5">
<h2>{{ $t('persons_involved') }}</h2> <h2>{{ $t('persons_involved') }}</h2>
<ul> <ul>
<li v-for="p in personsReachables" :key="p.id"> <li v-for="p in personsReachables" :key="p.id">
<input type="checkbox" :value="p.id" v-model="personsPicked"> <div class="form-check">
<person-render-box render="badge" :options="{}" :person="p"></person-render-box> <input type="checkbox" :value="p.id" v-model="personsPicked" class="form-check-input" :id="'person_check'+p.id">
<label class="form-check-label" :for="'person_check' + p.id">
<person-text :person="p"></person-text>
</label>
</div>
</li> </li>
</ul> </ul>
</div> </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 class="row">
<div v-if="hasSocialActionPicked" id="start_date" class="mb-3 row">
<label class="col-form-label col-sm-4">{{ $t('startDate') }}</label>
<div class="col-sm-8">
<input class="form-control" type="date" v-model="startDate"/>
</div>
</div> </div>
<div v-if="hasSocialActionPicked" id="end_date"> <!-- <div v-if="hasSocialActionPicked" id="end_date">
<p><label>{{ $t('endDate') }}</label> <input type="date" v-model="endDate" /></p> <p><label>{{ $t('endDate') }}</label> <input type="date" v-model="endDate" /></p>
</div> -->
<div v-if="hasSocialActionPicked" id="end_date" class="mb-3 row">
<label class="col-form-label col-sm-4">{{ $t('endDate') }}</label>
<div class="col-sm-8">
<input class="form-control" type="date" v-model="endDate"/>
</div>
</div>
</div> </div>
<div id="confirm"> <div id="confirm">
<div v-if="hasErrors"> <div v-if="hasErrors">
<p>{{ $t('form_has_errors') }}</p> <p>{{ $t('form_has_errors') }}</p>
@ -111,7 +128,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/chill/js/date.js'; import { dateToISO, ISOToDate } from 'ChillMainAssets/chill/js/date.js';
import PersonRenderBox from 'ChillPersonAssets/vuejs/_components/Entity/PersonRenderBox.vue'; import PersonText from 'ChillPersonAssets/vuejs/_components/Entity/PersonText.vue';
const i18n = { const i18n = {
messages: { messages: {
@ -133,7 +150,7 @@ export default {
name: 'App', name: 'App',
components: { components: {
VueMultiselect, VueMultiselect,
PersonRenderBox, PersonText,
}, },
methods: { methods: {
submit() { submit() {

View File

@ -141,10 +141,12 @@
<ul class="list-unstyled"> <ul class="list-unstyled">
<li v-for="p in personsReachables" :key="p.id"> <li v-for="p in personsReachables" :key="p.id">
<label :for="p.id"> <div class="form-check">
<input v-model="personsPicked" :value="p.id" :id="p.id" type="checkbox" class="me-2"> <input v-model="personsPicked" :value="p.id" type="checkbox" class="me-2 form-check-input" :id="'person_check'+p.id">
{{ p.text }} <label :for="'person_check'+p.id" class="form-check-label">
<person-text :person="p"></person-text>
</label> </label>
</div>
</li> </li>
</ul> </ul>
</div> </div>
@ -281,6 +283,7 @@ import ThirdPartyRenderBox from 'ChillThirdPartyAssets/vuejs/_components/Entity/
import PickTemplate from 'ChillDocGeneratorAssets/vuejs/_components/PickTemplate.vue'; import PickTemplate from 'ChillDocGeneratorAssets/vuejs/_components/PickTemplate.vue';
import OnTheFly from 'ChillMainAssets/vuejs/OnTheFly/components/OnTheFly.vue'; import OnTheFly from 'ChillMainAssets/vuejs/OnTheFly/components/OnTheFly.vue';
import PickWorkflow from 'ChillMainAssets/vuejs/_components/EntityWorkflow/PickWorkflow.vue'; import PickWorkflow from 'ChillMainAssets/vuejs/_components/EntityWorkflow/PickWorkflow.vue';
import PersonText from 'ChillPersonAssets/vuejs/_components/Entity/PersonText.vue';
const i18n = { const i18n = {
messages: { messages: {
@ -329,7 +332,8 @@ export default {
ThirdPartyRenderBox, ThirdPartyRenderBox,
PickTemplate, PickTemplate,
PickWorkflow, PickWorkflow,
OnTheFly OnTheFly,
PersonText,
}, },
i18n, i18n,
data() { data() {

View File

@ -11,7 +11,7 @@
<ul class="list-suggest remove-items inline"> <ul class="list-suggest remove-items inline">
<li v-for="c in concerned" :key="c.person.id" @click="removeConcerned(c)"> <li v-for="c in concerned" :key="c.person.id" @click="removeConcerned(c)">
<span>{{ c.person.text }}</span> <span><person-text :person="c.person"></person-text></span>
</li> </li>
</ul> </ul>
@ -57,12 +57,14 @@
import { mapState, mapGetters } from 'vuex'; import { mapState, mapGetters } from 'vuex';
import AddPersons from 'ChillPersonAssets/vuejs/_components/AddPersons.vue'; import AddPersons from 'ChillPersonAssets/vuejs/_components/AddPersons.vue';
import PersonRenderBox from 'ChillPersonAssets/vuejs/_components/Entity/PersonRenderBox.vue'; import PersonRenderBox from 'ChillPersonAssets/vuejs/_components/Entity/PersonRenderBox.vue';
import PersonText from 'ChillPersonAssets/vuejs/_components/Entity/PersonText.vue';
export default { export default {
name: 'Concerned', name: 'Concerned',
components: { components: {
AddPersons, AddPersons,
PersonRenderBox, PersonRenderBox,
PersonText,
}, },
computed: { computed: {
...mapState([ ...mapState([

View File

@ -61,6 +61,16 @@
<li v-if="hasHousehold"> <li v-if="hasHousehold">
<button @click="resetMode" class="btn btn-sm btn-misc">{{ $t('household_members_editor.household.reset_mode')}}</button> <button @click="resetMode" class="btn btn-sm btn-misc">{{ $t('household_members_editor.household.reset_mode')}}</button>
</li> </li>
<li v-if="!hasHousehold">
<add-persons
modalTitle="Chercher un ménage existant"
buttonTitle="Chercher un ménage existant"
v-bind:key="addPersons.key"
v-bind:options="addPersons.options"
@addNewPersons="pickHouseholdFound"
ref="pickHousehold"> <!-- to cast child method -->
</add-persons>
</li>
<li v-if="!hasHousehold"> <li v-if="!hasHousehold">
<button @click="setModeNew" class="btn btn-sm btn-create">{{ $t('household_members_editor.household.create_household') }}</button> <button @click="setModeNew" class="btn btn-sm btn-create">{{ $t('household_members_editor.household.create_household') }}</button>
</li> </li>
@ -77,16 +87,30 @@
import { mapGetters, mapState } from 'vuex'; import { mapGetters, mapState } from 'vuex';
import HouseholdRenderBox from 'ChillPersonAssets/vuejs/_components/Entity/HouseholdRenderBox.vue'; import HouseholdRenderBox from 'ChillPersonAssets/vuejs/_components/Entity/HouseholdRenderBox.vue';
import CurrentHousehold from './CurrentHousehold'; import CurrentHousehold from './CurrentHousehold';
import AddPersons from 'ChillPersonAssets/vuejs/_components/AddPersons';
export default { export default {
name: 'Household', name: 'Household',
components: { components: {
AddPersons,
CurrentHousehold, CurrentHousehold,
HouseholdRenderBox, HouseholdRenderBox,
}, },
emits: ['readyToGo'], emits: ['readyToGo'],
data() { data() {
return { return {
addPersons: {
key: 'household_find',
options: {
type: ['household'],
priority: null,
uniq: true,
button: {
size: 'btn-sm',
type: 'btn-search',
}
}
},
addAddress: { addAddress: {
key: 'household_new', key: 'household_new',
options: { options: {
@ -166,6 +190,13 @@ export default {
this.$store.dispatch('selectHousehold', h); this.$store.dispatch('selectHousehold', h);
this.$emit('readyToGo'); this.$emit('readyToGo');
}, },
pickHouseholdFound({selected, modal}) {
selected.forEach(function(item) {
this.selectHousehold(item.result);
}, this);
this.$refs.pickHousehold.resetSearch(); // to cast child method
modal.showModal = false;
},
removeHouseholdAddress() { removeHouseholdAddress() {
this.$store.commit('removeHouseholdAddress'); this.$store.commit('removeHouseholdAddress');
}, },

View File

@ -6,12 +6,13 @@
<div class="list-household-members flex-table"> <div class="list-household-members flex-table">
<div <div
v-for="conc in concerned" v-for="conc in concerned"
class="item-bloc" class="item-bloc"
v-bind:key="conc.person.id" v-bind:key="conc.person.id"
> >
<div class="pick-position item-row"> <div class="pick-position item-row">
<div class="person"> <div class="person">
<h3>{{ conc.person.text }}</h3> <!-- <h3>{{ conc.person.text }}</h3> -->
<h3><person-text :person="conc.person"></person-text></h3>
</div> </div>
<div class="holder"> <div class="holder">
<button <button
@ -53,6 +54,7 @@ import {mapGetters, mapState} from "vuex";
import CurrentHousehold from "./CurrentHousehold"; import CurrentHousehold from "./CurrentHousehold";
import PersonRenderBox from 'ChillPersonAssets/vuejs/_components/Entity/PersonRenderBox.vue'; import PersonRenderBox from 'ChillPersonAssets/vuejs/_components/Entity/PersonRenderBox.vue';
import PersonComment from './PersonComment'; import PersonComment from './PersonComment';
import PersonText from '../../_components/Entity/PersonText.vue';
export default { export default {
name: "Positioning", name: "Positioning",
@ -60,6 +62,7 @@ export default {
CurrentHousehold, CurrentHousehold,
PersonRenderBox, PersonRenderBox,
PersonComment, PersonComment,
PersonText
}, },
computed: { computed: {
...mapState([ ...mapState([

View File

@ -1,12 +1,12 @@
<template> <template>
<ul class="record_actions"> <ul class="record_actions">
<li class="add-persons"> <li class="add-persons">
<a class="btn" :class="getClassButton" :title="$t(buttonTitle)" <a class="btn" :class="getClassButton" :title="$t(buttonTitle)"
@click="openModal"><span v-if="displayTextButton">{{ $t(buttonTitle) }}</span></a> @click="openModal"><span v-if="displayTextButton">{{ $t(buttonTitle) }}</span></a>
</li> </li>
</ul> </ul>
<teleport to="body"> <teleport to="body">
<modal v-if="modal.showModal" <modal v-if="modal.showModal"
:modalDialogClass="modal.modalDialogClass" :modalDialogClass="modal.modalDialogClass"
@close="modal.showModal = false"> @close="modal.showModal = false">
@ -181,7 +181,7 @@ export default {
}, },
hasPriorSuggestion() { hasPriorSuggestion() {
return this.search.priorSuggestion.key ? true : false; return this.search.priorSuggestion.key ? true : false;
} },
}, },
methods: { methods: {
openModal() { openModal() {

View File

@ -25,6 +25,11 @@
v-if="item.result.type === 'user'" v-if="item.result.type === 'user'"
v-bind:item="item"> v-bind:item="item">
</suggestion-user> </suggestion-user>
<suggestion-household
v-if="item.result.type === 'household'"
v-bind:item="item">
</suggestion-household>
</label> </label>
</div> </div>
@ -34,6 +39,7 @@
import SuggestionPerson from './TypePerson'; import SuggestionPerson from './TypePerson';
import SuggestionThirdParty from './TypeThirdParty'; import SuggestionThirdParty from './TypeThirdParty';
import SuggestionUser from './TypeUser'; import SuggestionUser from './TypeUser';
import SuggestionHousehold from './TypeHousehold';
export default { export default {
name: 'PersonSuggestion', name: 'PersonSuggestion',
@ -41,6 +47,7 @@ export default {
SuggestionPerson, SuggestionPerson,
SuggestionThirdParty, SuggestionThirdParty,
SuggestionUser, SuggestionUser,
SuggestionHousehold,
}, },
props: [ props: [
'item', 'item',
@ -70,7 +77,7 @@ export default {
}; };
</script> </script>
<style lang="scss"> <style lang="scss" scoped>
div.results { div.results {
div.list-item { div.list-item {
padding: 0.4em 0.8em; padding: 0.4em 0.8em;
@ -83,11 +90,11 @@ export default {
label { label {
display: inline-flex; display: inline-flex;
width: 100%; width: 100%;
div.container { div.container:not(.household) {
& > input { & > input {
margin-right: 0.8em; margin-right: 0.8em;
} }
span:not(.name) { > span:not(.name) {
margin-left: 0.5em; margin-left: 0.5em;
opacity: 0.5; opacity: 0.5;
font-size: 90%; font-size: 90%;

View File

@ -0,0 +1,27 @@
<template>
<div class="container household">
<household-render-box :household="item.result" :isAddressMultiline="false"></household-render-box>
</div>
<div class="right_actions">
<badge-entity
:entity="item.result"
:options="{ displayLong: true }">
</badge-entity>
</div>
</template>
<script>
import BadgeEntity from 'ChillMainAssets/vuejs/_components/BadgeEntity.vue';
import HouseholdRenderBox from 'ChillPersonAssets/vuejs/_components/Entity/HouseholdRenderBox.vue';
export default {
name: 'SuggestionHousehold',
components: {
BadgeEntity,
HouseholdRenderBox,
},
props: ['item'],
}
</script>

View File

@ -1,7 +1,7 @@
<template> <template>
<div class="container"> <div class="container">
<span class="name"> <span class="name">
{{ item.result.text }} <person-text :person="item.result"></person-text>
</span> </span>
<span class="birthday" v-if="hasBirthdate"> <span class="birthday" v-if="hasBirthdate">
{{ $d(item.result.birthdate.datetime, 'short') }} {{ $d(item.result.birthdate.datetime, 'short') }}
@ -28,12 +28,14 @@
<script> <script>
import OnTheFly from 'ChillMainAssets/vuejs/OnTheFly/components/OnTheFly.vue'; import OnTheFly from 'ChillMainAssets/vuejs/OnTheFly/components/OnTheFly.vue';
import BadgeEntity from 'ChillMainAssets/vuejs/_components/BadgeEntity.vue'; import BadgeEntity from 'ChillMainAssets/vuejs/_components/BadgeEntity.vue';
import PersonText from 'ChillPersonAssets/vuejs/_components/Entity/PersonText.vue';
export default { export default {
name: 'SuggestionPerson', name: 'SuggestionPerson',
components: { components: {
OnTheFly, OnTheFly,
BadgeEntity BadgeEntity,
PersonText,
}, },
props: ['item'], props: ['item'],
computed: { computed: {

View File

@ -2,7 +2,7 @@
<div class="container tpartycontainer"> <div class="container tpartycontainer">
<div class="tparty-identification"> <div class="tparty-identification">
<span class="name"> <span class="name">
{{ item.result.text }} {{ item.result.text }}&nbsp;
</span> </span>
<span class="location"> <span class="location">
<template v-if="hasAddress"> <template v-if="hasAddress">

View File

@ -1,14 +1,15 @@
<template> <template>
<div v-if="render === 'bloc'" class="item-bloc"> <div v-if="render === 'bloc'" class="item-bloc">
<section class="chill-entity entity-person"> <section class="chill-entity entity-person">
<div class="item-row entity-bloc"> <div class="item-row entity-bloc">
<div class="item-col"> <div class="item-col">
<div class="entity-label"> <div class="entity-label">
<div :class="'denomination h' + options.hLevel"> <div :class="'denomination h' + options.hLevel">
<a v-if="options.addLink === true" :href="getUrl"> <a v-if="options.addLink === true" :href="getUrl">
<!-- use person-text here to avoid code duplication ? TODO -->
<span class="firstname">{{ person.firstName }}</span> <span class="firstname">{{ person.firstName }}</span>
<span class="lastname">{{ person.lastName }}</span> <span class="lastname">{{ person.lastName }}</span>
<span v-if="person.altNames && options.addAltNames == true" class="altnames"> <span v-if="person.altNames && options.addAltNames == true" class="altnames">
@ -16,19 +17,20 @@
</span> </span>
</a> </a>
<span class="firstname">{{ person.firstName }}</span> <!-- use person-text here to avoid code duplication ? TODO -->
<span class="lastname">{{ person.lastName }}</span> <span class="firstname">{{ person.firstName }}</span>
<span v-if="person.deathdate" class="deathdate"> ()</span> <span class="lastname">{{ person.lastName }}</span>
<span v-if="person.altNames && options.addAltNames == true" class="altnames"> <span v-if="person.deathdate" class="deathdate"> ()</span>
<span :class="'altname altname-' + altNameKey">{{ altNameLabel }}</span> <span v-if="person.altNames && options.addAltNames == true" class="altnames">
</span> <span :class="'altname altname-' + altNameKey">{{ altNameLabel }}</span>
</span>
<span v-if="options.addId == true" class="id-number" :title="'n° ' + person.id">{{ person.id }}</span> <span v-if="options.addId == true" class="id-number" :title="'n° ' + person.id">{{ person.id }}</span>
<badge-entity v-if="options.addEntity === true" <badge-entity v-if="options.addEntity === true"
:entity="person" :entity="person"
:options="{ displayLong: options.entityDisplayLong }"> :options="{ displayLong: options.entityDisplayLong }">
</badge-entity> </badge-entity>
</div> </div>
@ -47,98 +49,96 @@
{{ $t('renderbox.deathdate') + ' ' + deathdate }} {{ $t('renderbox.deathdate') + ' ' + deathdate }}
</time> </time>
<span v-if="options.addAge && person.birthdate" class="age">{{ getAge }} {{ $t('renderbox.years_old')}}</span> <span v-if="options.addAge && person.birthdate" class="age">{{ $tc('renderbox.years_old', person.age) }}</span>
</p> </p>
</div> </div>
</div> </div>
<div class="item-col"> <div class="item-col">
<div class="float-button bottom"> <div class="float-button bottom">
<div class="box"> <div class="box">
<div class="action"> <div class="action">
<slot name="record-actions"></slot> <slot name="record-actions"></slot>
</div> </div>
<ul class="list-content fa-ul"> <ul class="list-content fa-ul">
<li v-if="person.current_household_id">
<i class="fa fa-li fa-map-marker"></i>
<address-render-box v-if="person.current_household_address"
:address="person.current_household_address"
:isMultiline="isMultiline">
</address-render-box>
<p v-else class="chill-no-data-statement">
{{ $t('renderbox.household_without_address') }}
</p>
<a v-if="options.addHouseholdLink === true"
:href="getCurrentHouseholdUrl"
:title="$t('persons_associated.show_household_number', {id: person.current_household_id})">
<span class="badge rounded-pill bg-chill-beige">
<i class="fa fa-fw fa-home"></i><!--{{ $t('persons_associated.show_household') }}-->
</span>
</a>
</li>
<li v-else-if="options.addNoData">
<i class="fa fa-li fa-map-marker"></i>
<p class="chill-no-data-statement">
{{ $t('renderbox.no_data') }}
</p>
</li>
<li v-if="person.current_household_id"> <li v-if="person.mobilenumber">
<i class="fa fa-li fa-map-marker"></i> <i class="fa fa-li fa-mobile"></i>
<address-render-box v-if="person.current_household_address" <a :href="'tel: ' + person.mobilenumber">{{ person.mobilenumber }}</a>
:address="person.current_household_address" </li>
:isMultiline="isMultiline"> <li v-else-if="options.addNoData">
</address-render-box> <i class="fa fa-li fa-mobile"></i>
<p v-else class="chill-no-data-statement"> <p class="chill-no-data-statement">{{ $t('renderbox.no_data') }}</p>
{{ $t('renderbox.household_without_address') }} </li>
</p> <li v-if="person.phonenumber">
<a v-if="options.addHouseholdLink === true" <i class="fa fa-li fa-phone"></i>
:href="getCurrentHouseholdUrl" <a :href="'tel: ' + person.phonenumber">{{ person.phonenumber }}</a>
:title="$t('persons_associated.show_household_number', {id: person.current_household_id})"> </li>
<span class="badge rounded-pill bg-chill-beige"> <li v-else-if="options.addNoData">
<i class="fa fa-fw fa-home"></i><!--{{ $t('persons_associated.show_household') }}--> <i class="fa fa-li fa-phone"></i>
</span> <p class="chill-no-data-statement">{{ $t('renderbox.no_data') }}</p>
</a> </li>
</li>
<li v-else-if="options.addNoData">
<i class="fa fa-li fa-map-marker"></i>
<p class="chill-no-data-statement">
{{ $t('renderbox.no_data') }}
</p>
</li>
<li v-if="person.mobilenumber"> <li v-if="person.centers !== undefined && person.centers.length > 0 && options.addCenter">
<i class="fa fa-li fa-mobile"></i> <i class="fa fa-li fa-long-arrow-right"></i>
<a :href="'tel: ' + person.mobilenumber">{{ person.mobilenumber }}</a> <template v-for="c in person.centers">{{ c.name }}</template>
</li> </li>
<li v-else-if="options.addNoData"> <li v-else-if="options.addNoData">
<i class="fa fa-li fa-mobile"></i> <i class="fa fa-li fa-long-arrow-right"></i>
<p class="chill-no-data-statement">{{ $t('renderbox.no_data') }}</p> <p class="chill-no-data-statement">{{ $t('renderbox.no_data') }}</p>
</li> </li>
<li v-if="person.phonenumber"> <slot name="custom-zone"></slot>
<i class="fa fa-li fa-phone"></i>
<a :href="'tel: ' + person.phonenumber">{{ person.phonenumber }}</a>
</li>
<li v-else-if="options.addNoData">
<i class="fa fa-li fa-phone"></i>
<p class="chill-no-data-statement">{{ $t('renderbox.no_data') }}</p>
</li>
<li v-if="person.centers !== undefined && person.centers.length > 0 && options.addCenter"> </ul>
<i class="fa fa-li fa-long-arrow-right"></i> </div>
<template v-for="c in person.centers">{{ c.name }}</template> </div>
</li> </div>
<li v-else-if="options.addNoData">
<i class="fa fa-li fa-long-arrow-right"></i>
<p class="chill-no-data-statement">{{ $t('renderbox.no_data') }}</p>
</li>
<slot name="custom-zone"></slot>
</ul>
</div>
</div>
</div> </div>
</div>
</section> </section>
</div> </div>
<span v-if="render === 'badge'" class="chill-entity entity-person badge-person"> <span v-if="render === 'badge'" class="chill-entity entity-person badge-person">
<a v-if="options.addLink === true" :href="getUrl"> <a v-if="options.addLink === true" :href="getUrl">
<span v-if="options.isHolder" class="fa-stack fa-holder" :title="$t('renderbox.holder')"> <span v-if="options.isHolder" class="fa-stack fa-holder" :title="$t('renderbox.holder')">
<i class="fa fa-circle fa-stack-1x text-success"></i> <i class="fa fa-circle fa-stack-1x text-success"></i>
<i class="fa fa-stack-1x">T</i> <i class="fa fa-stack-1x">T</i>
</span> </span>
{{ person.text }}
<span v-if="person.deathdate" class="deathdate"> ()</span> <person-text :person="person"></person-text>
</a> </a>
<span v-else> <span v-else>
<span v-if="options.isHolder" class="fa-stack fa-holder" :title="$t('renderbox.holder')"> <span v-if="options.isHolder" class="fa-stack fa-holder" :title="$t('renderbox.holder')">
<i class="fa fa-circle fa-stack-1x text-success"></i> <i class="fa fa-circle fa-stack-1x text-success"></i>
<i class="fa fa-stack-1x">T</i> <i class="fa fa-stack-1x">T</i>
</span> </span>
{{ person.text }} <person-text :person="person"></person-text>
<span v-if="person.deathdate" class="deathdate"> ()</span>
</span> </span>
<slot name="post-badge"></slot> <slot name="post-badge"></slot>
</span> </span>
</template> </template>
@ -147,13 +147,15 @@ import {dateToISO} from 'ChillMainAssets/chill/js/date.js';
import AddressRenderBox from 'ChillMainAssets/vuejs/_components/Entity/AddressRenderBox.vue'; import AddressRenderBox from 'ChillMainAssets/vuejs/_components/Entity/AddressRenderBox.vue';
import Confidential from 'ChillMainAssets/vuejs/_components/Confidential.vue'; import Confidential from 'ChillMainAssets/vuejs/_components/Confidential.vue';
import BadgeEntity from 'ChillMainAssets/vuejs/_components/BadgeEntity.vue'; import BadgeEntity from 'ChillMainAssets/vuejs/_components/BadgeEntity.vue';
import PersonText from 'ChillPersonAssets/vuejs/_components/Entity/PersonText.vue';
export default { export default {
name: "PersonRenderBox", name: "PersonRenderBox",
components: { components: {
AddressRenderBox, AddressRenderBox,
Confidential, Confidential,
BadgeEntity BadgeEntity,
PersonText
}, },
props: ['person', 'options', 'render', 'returnPath'], props: ['person', 'options', 'render', 'returnPath'],
computed: { computed: {
@ -168,7 +170,7 @@ export default {
return this.person.gender === 'woman' ? 'fa-venus' : this.person.gender === 'man' ? 'fa-mars' : this.person.gender === 'neuter' ? 'fa-neuter' : 'fa-genderless'; return this.person.gender === 'woman' ? 'fa-venus' : this.person.gender === 'man' ? 'fa-mars' : this.person.gender === 'neuter' ? 'fa-neuter' : 'fa-genderless';
}, },
getGenderTranslation: function() { getGenderTranslation: function() {
return this.person.gender === 'woman' ? 'renderbox.birthday.woman' : 'renderbox.birthday.man'; return this.person.gender === 'woman' ? 'renderbox.birthday.woman' : 'renderbox.birthday.man';
}, },
getGender() { getGender() {
return this.person.gender === 'woman' ? 'person.gender.woman' : this.person.gender === 'man' ? 'person.gender.man' : this.person.gender === 'neuter' ? 'person.gender.neuter' : 'person.gender.undefined'; return this.person.gender === 'woman' ? 'person.gender.woman' : this.person.gender === 'man' ? 'person.gender.man' : this.person.gender === 'neuter' ? 'person.gender.neuter' : 'person.gender.undefined';
@ -200,24 +202,6 @@ export default {
getUrl: function() { getUrl: function() {
return `/fr/person/${this.person.id}/general`; return `/fr/person/${this.person.id}/general`;
}, },
getAge: function() {
// TODO only one abstract function
if(this.person.birthdate && !this.person.deathdate){
const birthday = new Date(this.person.birthdate.datetime)
const now = new Date()
return (now.getFullYear() - birthday.getFullYear())
} else if(this.person.birthdate && this.person.deathdate){
const birthday = new Date(this.person.birthdate.datetime)
const deathdate = new Date(this.person.deathdate.datetime)
return (deathdate.getFullYear() - birthday.getFullYear())
} else if(!this.person.birthdate && this.person.deathdate.datetime) {
// todo: change this
return "Age unknown"
} else {
// todo: change this
return "Age unknown"
}
},
getCurrentHouseholdUrl: function() { getCurrentHouseholdUrl: function() {
let returnPath = this.returnPath ? `?returnPath=${this.returnPath}` : ``; let returnPath = this.returnPath ? `?returnPath=${this.returnPath}` : ``;
return `/fr/person/household/${this.person.current_household_id}/summary${returnPath}` return `/fr/person/household/${this.person.current_household_id}/summary${returnPath}`

View File

@ -0,0 +1,50 @@
<template>
<span v-if="isCut">{{ cutText }}</span>
<span v-else class="person-text">
<span class="firstname">{{ person.firstName }}</span>
<span class="lastname">{{ person.lastName }}</span>
<span v-if="person.altNames && person.altNames.length > 0" class="altnames">
<span :class="'altname altname-' + altNameKey"> ({{ altNameLabel }})</span>
</span>
<span class="age" v-if="this.addAge && person.birthdate !== null && person.deathdate === null">{{ $tc('renderbox.years_old', person.age) }}</span>
<span v-else-if="this.addAge && person.deathdate !== null">&nbsp;()</span>
</span>
</template>
<script>
export default {
name: "PersonText",
props: {
person: {
required: true,
},
isCut: {
type: Boolean,
required: false,
default: false
},
addAge: {
type: Boolean,
required: false,
default: true,
}
},
computed: {
altNameLabel: function() {
for(let i = 0; i < this.person.altNames.length; i++){
return this.person.altNames[i].label
}
},
altNameKey: function() {
for(let i = 0; i < this.person.altNames.length; i++){
return this.person.altNames[i].key
}
},
cutText: function() {
let more = (this.person.text.length > 15) ?'…' : '';
return this.person.text.slice(0,15) + more;
}
}
}
</script>

View File

@ -31,7 +31,7 @@
<li class="associated-persons"> <li class="associated-persons">
<span class="item-key">{{ 'Participants'|trans ~ ' : ' }}</span> <span class="item-key">{{ 'Participants'|trans ~ ' : ' }}</span>
{% for p in w.persons %} {% for p in w.persons %}
<span class="badge-person">{{ p|chill_entity_render_box }}</span> <span class="badge-person">{{ p|chill_entity_render_box({'addAgeBadge': true}) }}</span>
{% endfor %} {% endfor %}
</li> </li>
</ul> </ul>

View File

@ -11,6 +11,7 @@
* addCenter bool * addCenter bool
* hLevel integer * hLevel integer
* addDeath bool * addDeath bool
* addAgeBadge bool
* address_multiline bool * address_multiline bool
* customButtons [ * customButtons [
'before' Twig\Markup, (injected with macro) 'before' Twig\Markup, (injected with macro)
@ -40,6 +41,11 @@
{%- endfor -%} {%- endfor -%}
</span> </span>
{%- endif -%} {%- endif -%}
{%- if options['addAgeBadge'] -%}
{% if person.age is not null and person.deathDate is null %}
<span>({{- 'years_old'|trans({ 'age': person.age }) -}})</span>
{% endif %}
{% endif %}
{% endmacro raw %} {% endmacro raw %}
{% macro label(person, options) %} {% macro label(person, options) %}

View File

@ -14,6 +14,7 @@
'render': 'label', 'render': 'label',
'addLink': true, 'addLink': true,
'addInfo': true, 'addInfo': true,
'addAgeBadge': true,
'customArea': { 'customArea': {
'afterLabel': _self.addHolder(member.holder) 'afterLabel': _self.addHolder(member.holder)
} }

View File

@ -0,0 +1,109 @@
<?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\Search;
use Chill\MainBundle\Search\SearchApiInterface;
use Chill\MainBundle\Search\SearchApiQuery;
use Chill\MainBundle\Search\Utils\ExtractDateFromPattern;
use Chill\MainBundle\Search\Utils\ExtractPhonenumberFromPattern;
use Chill\MainBundle\Security\Authorization\AuthorizationHelperInterface;
use Chill\PersonBundle\Repository\Household\HouseholdRepository;
use Chill\PersonBundle\Repository\PersonACLAwareRepositoryInterface;
use Symfony\Component\Security\Core\Security;
use function array_map;
use function count;
use function in_array;
class SearchHouseholdApiProvider implements SearchApiInterface
{
private AuthorizationHelperInterface $authorizationHelper;
private ExtractDateFromPattern $extractDateFromPattern;
private ExtractPhonenumberFromPattern $extractPhonenumberFromPattern;
private HouseholdRepository $householdRepository;
private PersonACLAwareRepositoryInterface $personACLAwareRepository;
private Security $security;
public function __construct(
HouseholdRepository $householdRepository,
PersonACLAwareRepositoryInterface $personACLAwareRepository,
Security $security,
AuthorizationHelperInterface $authorizationHelper,
ExtractDateFromPattern $extractDateFromPattern,
ExtractPhonenumberFromPattern $extractPhonenumberFromPattern
) {
$this->householdRepository = $householdRepository;
$this->personACLAwareRepository = $personACLAwareRepository;
$this->security = $security;
$this->authorizationHelper = $authorizationHelper;
$this->extractDateFromPattern = $extractDateFromPattern;
$this->extractPhonenumberFromPattern = $extractPhonenumberFromPattern;
}
public function getResult(string $key, array $metadata, float $pertinence)
{
return $this->householdRepository->find($metadata['id']);
}
public function prepare(array $metadatas): void
{
$ids = array_map(static fn ($m) => $m['id'], $metadatas);
$this->householdRepository->findBy(['id' => $ids]);
}
public function provideQuery(string $pattern, array $parameters): SearchApiQuery
{
$datesResult = $this->extractDateFromPattern->extractDates($pattern);
$phoneResult = $this->extractPhonenumberFromPattern->extractPhonenumber($datesResult->getFilteredSubject());
$filtered = $phoneResult->getFilteredSubject();
$query = $this->personACLAwareRepository->buildAuthorizedQuery(
$filtered,
null,
null,
count($datesResult->getFound()) > 0 ? $datesResult->getFound()[0] : null,
null,
null,
null,
null,
count($phoneResult->getFound()) > 0 ? $phoneResult->getFound()[0] : null
);
$query
->setDistinct(true, 'household_id')
->setFromClause(
'view_chill_person_household_address AS vcpha ' .
'JOIN chill_person_person AS person ON vcpha.person_id = person.id'
)
->setSelectKey('household')
->andWhereClause('vcpha.validTo IS NULL', [])
->setSelectJsonbMetadata("jsonb_build_object('id', vcpha.household_id)");
return $query;
}
public function supportsResult(string $key, array $metadatas): bool
{
return 'household' === $key;
}
public function supportsTypes(string $pattern, array $types, array $parameters): bool
{
return in_array('household', $types, true);
}
}

View File

@ -179,11 +179,13 @@ class PersonJsonNormalizer implements
return [ return [
'type' => 'person', 'type' => 'person',
'id' => $person->getId(), 'id' => $person->getId(),
'text' => $this->render->renderString($person), 'text' => $this->render->renderString($person, ['addAge' => false]),
'textAge' => $this->render->renderString($person, ['addAge' => true]),
'firstName' => $person->getFirstName(), 'firstName' => $person->getFirstName(),
'lastName' => $person->getLastName(), 'lastName' => $person->getLastName(),
'birthdate' => $this->normalizer->normalize($person->getBirthdate(), $format, $context), 'birthdate' => $this->normalizer->normalize($person->getBirthdate(), $format, $context),
'deathdate' => $this->normalizer->normalize($person->getDeathdate(), $format, $context), 'deathdate' => $this->normalizer->normalize($person->getDeathdate(), $format, $context),
'age' => $this->normalizer->normalize($person->getAge(), $format, $context),
'centers' => $this->normalizer->normalize($this->centerResolverManager->resolveCenters($person), $format, $context), 'centers' => $this->normalizer->normalize($this->centerResolverManager->resolveCenters($person), $format, $context),
'phonenumber' => $person->getPhonenumber(), 'phonenumber' => $person->getPhonenumber(),
'mobilenumber' => $person->getMobilenumber(), 'mobilenumber' => $person->getMobilenumber(),

View File

@ -15,6 +15,7 @@ use Chill\MainBundle\Templating\Entity\AbstractChillEntityRender;
use Chill\PersonBundle\Config\ConfigPersonAltNamesHelper; use Chill\PersonBundle\Config\ConfigPersonAltNamesHelper;
use Chill\PersonBundle\Entity\Person; use Chill\PersonBundle\Entity\Person;
use Symfony\Component\Templating\EngineInterface; use Symfony\Component\Templating\EngineInterface;
use Symfony\Contracts\Translation\TranslatorInterface;
use function array_key_exists; use function array_key_exists;
@ -27,12 +28,16 @@ class PersonRender extends AbstractChillEntityRender
private EngineInterface $engine; private EngineInterface $engine;
private TranslatorInterface $translator;
public function __construct( public function __construct(
ConfigPersonAltNamesHelper $configAltNamesHelper, ConfigPersonAltNamesHelper $configAltNamesHelper,
EngineInterface $engine EngineInterface $engine,
TranslatorInterface $translator
) { ) {
$this->configAltNamesHelper = $configAltNamesHelper; $this->configAltNamesHelper = $configAltNamesHelper;
$this->engine = $engine; $this->engine = $engine;
$this->translator = $translator;
} }
/** /**
@ -53,6 +58,7 @@ class PersonRender extends AbstractChillEntityRender
'customButtons' => $options['customButtons'] ?? [], 'customButtons' => $options['customButtons'] ?? [],
'customArea' => $options['customArea'] ?? [], 'customArea' => $options['customArea'] ?? [],
'addDeath' => $options['addDeath'] ?? true, 'addDeath' => $options['addDeath'] ?? true,
'addAgeBadge' => $options['addAgeBadge'] ?? false,
]; ];
return return
@ -70,6 +76,13 @@ class PersonRender extends AbstractChillEntityRender
*/ */
public function renderString($person, array $options): string public function renderString($person, array $options): string
{ {
$options = array_merge(['addAge' => true], $options);
if (null !== $person->getAge() && $person->getDeathDate() === null && $options['addAge']) {
return $person->getFirstName() . ' ' . $person->getLastName()
. $this->addAltNames($person, false) . ' (' . $this->translator->trans('years_old', ['age' => $person->getAge()]) . ')';
}
return $person->getFirstName() . ' ' . $person->getLastName() return $person->getFirstName() . ' ' . $person->getLastName()
. $this->addAltNames($person, false); . $this->addAltNames($person, false);
} }

View File

@ -11,3 +11,7 @@ services:
Chill\PersonBundle\Search\SearchPersonApiProvider: Chill\PersonBundle\Search\SearchPersonApiProvider:
autowire: true autowire: true
autoconfigure: true autoconfigure: true
Chill\PersonBundle\Search\SearchHouseholdApiProvider:
autowire: true
autoconfigure: true