Merge branch 'features/household-members-list' into 'master'

Features/household members list

See merge request Chill-Projet/chill-bundles!76
This commit is contained in:
Julien Fastré 2021-06-10 08:59:51 +00:00
commit f7bb9ac327
22 changed files with 559 additions and 53 deletions

View File

@ -58,7 +58,8 @@
"symfony/css-selector": "^5.2", "symfony/css-selector": "^5.2",
"twig/markdown-extra": "^3.3", "twig/markdown-extra": "^3.3",
"erusev/parsedown": "^1.7", "erusev/parsedown": "^1.7",
"symfony/serializer": "^5.2" "symfony/serializer": "^5.2",
"symfony/webpack-encore-bundle": "^1.11"
}, },
"conflict": { "conflict": {
"symfony/symfony": "*" "symfony/symfony": "*"

View File

@ -18,7 +18,7 @@
// @import "bootstrap/scss/tables"; // @import "bootstrap/scss/tables";
// @import "bootstrap/scss/forms"; // @import "bootstrap/scss/forms";
// @import "bootstrap/scss/buttons"; // @import "bootstrap/scss/buttons";
// @import "bootstrap/scss/transitions"; @import "bootstrap/scss/transitions";
// @import "bootstrap/scss/dropdown"; // @import "bootstrap/scss/dropdown";
// @import "bootstrap/scss/button-group"; // @import "bootstrap/scss/button-group";
// @import "bootstrap/scss/input-group"; // @import "bootstrap/scss/input-group";

View File

@ -4,6 +4,8 @@
// Or compile bootstrap only enabled assets // Or compile bootstrap only enabled assets
require('./bootstrap.scss'); require('./bootstrap.scss');
// You can specify which plugins you need // You can specify which plugins you need
//import { Tooltip, Toast, Popover } from 'bootstrap'; //import { Tooltip, Toast, Popover } from 'bootstrap';
import Modal from 'bootstrap/js/dist/modal'; import Modal from 'bootstrap/js/dist/modal';
import Collapse from 'bootstrap/js/src/collapse';

View File

@ -41,12 +41,6 @@ table {
} }
} }
// règle la typo des étiquettes de dénomination rendues avec renderBox
.chill_denomination {
font-size: 1.3em;
font-weight: 700;
}
/* /*
* ACCOMPANYING_COURSE * ACCOMPANYING_COURSE
* Header custom for Accompanying Course * Header custom for Accompanying Course

View File

@ -28,16 +28,16 @@
<meta name="viewport" content="width=device-width, initial-scale=1.0" /> <meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>{{ installation.name }} - {% block title %}{% endblock %}</title> <title>{{ installation.name }} - {% block title %}{% endblock %}</title>
<link rel="shortcut icon" href="{{ asset('build/images/favicon.ico') }}" type="image/x-icon"> <link rel="shortcut icon" href="{{ asset('build/images/favicon.ico') }}" type="image/x-icon">
<link rel="stylesheet" href="{{ asset('build/scratch.css') }}"/> {{ encore_entry_link_tags('scratch') }}
<link rel="stylesheet" href="{{ asset('build/chill.css') }}"/> {{ encore_entry_link_tags('chill') }}
{% if active_bootstrap == 1 %} {% if active_bootstrap == 1 %}
<link rel="stylesheet" href="{{ asset('build/bootstrap.css') }}" type="text/css"/> {{ encore_entry_link_tags('bootstrap') }}
{% endif %} {% endif %}
{% if active_forkawesome == 1 %} {% if active_forkawesome == 1 %}
<link rel="stylesheet" href="{{ asset('build/forkawesome.css') }}" type="text/css"/> {{ encore_entry_link_tags('forkawesome') }}
{% endif %} {% endif %}
{% if active_ckeditor == 1 %} {% if active_ckeditor == 1 %}
<link rel="stylesheet" href="{{ asset('build/ckeditor5.css') }}" type="text/css"/> {{ encore_entry_link_tags('ckeditor5') }}
{% endif %} {% endif %}
{% block css%}<!-- nothing added to css -->{% endblock %} {% block css%}<!-- nothing added to css -->{% endblock %}
</head> </head>
@ -152,17 +152,16 @@
{{ include('@ChillMain/Layout/_footer.html.twig') }} {{ include('@ChillMain/Layout/_footer.html.twig') }}
<script type="text/javascript" src="{{ asset('build/runtime.js') }}"></script> {{ encore_entry_script_tags('scratch') }}
<script type="text/javascript" src="{{ asset('build/scratch.js') }}"></script> {{ encore_entry_script_tags('chill') }}
<script type="text/javascript" src="{{ asset('build/chill.js') }}"></script>
{% if active_bootstrap == 1 %} {% if active_bootstrap == 1 %}
<script type="text/javascript" src="{{ asset('build/bootstrap.js') }}"></script> {{ encore_entry_script_tags('bootstrap') }}
{% endif %} {% endif %}
{% if active_forkawesome == 1 %} {% if active_forkawesome == 1 %}
<script type="text/javascript" src="{{ asset('build/forkawesome.js') }}"></script> {{ encore_entry_script_tags('forkawesome') }}
{% endif %} {% endif %}
{% if active_ckeditor == 1 %} {% if active_ckeditor == 1 %}
<script type="text/javascript" src="{{ asset('build/ckeditor5.js') }}"></script> {{ encore_entry_script_tags('ckeditor5') }}
{% endif %} {% endif %}
<script type="text/javascript"> <script type="text/javascript">
chill.checkOtherValueOnChange(); chill.checkOtherValueOnChange();

View File

@ -8,6 +8,7 @@ use Symfony\Component\HttpFoundation\Response;
use Symfony\Component\Routing\Annotation\Route; use Symfony\Component\Routing\Annotation\Route;
use Sensio\Bundle\FrameworkExtraBundle\Configuration\ParamConverter; use Sensio\Bundle\FrameworkExtraBundle\Configuration\ParamConverter;
use Chill\PersonBundle\Entity\Household\Household; use Chill\PersonBundle\Entity\Household\Household;
use Chill\PersonBundle\Entity\Household\Position;
/** /**
* @Route("/{_locale}/person/household") * @Route("/{_locale}/person/household")
@ -25,9 +26,20 @@ class HouseholdController extends AbstractController
public function summary(Request $request, Household $household) public function summary(Request $request, Household $household)
{ {
// TODO ACL // TODO ACL
$positions = $this->getDoctrine()->getManager()
->getRepository(Position::class)
->findAll()
;
// little performance improvement:
// initialize members collection, which will avoid
// some queries
$household->getMembers()->initialize();
return $this->render('@ChillPerson/Household/summary.html.twig', return $this->render('@ChillPerson/Household/summary.html.twig',
[ [
'household' => $household 'household' => $household,
'positions' => $positions
] ]
); );
} }
@ -43,9 +55,20 @@ class HouseholdController extends AbstractController
public function members(Request $request, Household $household) public function members(Request $request, Household $household)
{ {
// TODO ACL // TODO ACL
$positions = $this->getDoctrine()->getManager()
->getRepository(Position::class)
->findAll()
;
// little performance improvement:
// initialize members collection, which will avoid
// some queries
$household->getMembers()->initialize();
return $this->render('@ChillPerson/Household/members.html.twig', return $this->render('@ChillPerson/Household/members.html.twig',
[ [
'household' => $household 'household' => $household,
'positions' => $positions
] ]
); );
} }

View File

@ -2,17 +2,32 @@
namespace Chill\PersonBundle\Controller; namespace Chill\PersonBundle\Controller;
use Chill\PersonBundle\Form\HouseholdMemberType;
use Chill\PersonBundle\Entity\Household\HouseholdMember;
use Symfony\Component\HttpFoundation\Exception\BadRequestException; use Symfony\Component\HttpFoundation\Exception\BadRequestException;
use Symfony\Component\HttpFoundation\Request; use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpFoundation\Response; use Symfony\Component\HttpFoundation\Response;
use Symfony\Component\Routing\Generator\UrlGeneratorInterface;
use Symfony\Component\Serializer\Exception; use Symfony\Component\Serializer\Exception;
use Symfony\Component\Routing\Annotation\Route; use Symfony\Component\Routing\Annotation\Route;
use Chill\MainBundle\CRUD\Controller\ApiController; use Chill\MainBundle\CRUD\Controller\ApiController;
use Symfony\Component\Translation\TranslatorInterface;
use Chill\PersonBundle\Household\MembersEditor; use Chill\PersonBundle\Household\MembersEditor;
use Symfony\Component\Serializer\Normalizer\AbstractNormalizer; use Symfony\Component\Serializer\Normalizer\AbstractNormalizer;
class HouseholdMemberController extends ApiController class HouseholdMemberController extends ApiController
{ {
private UrlGeneratorInterface $generator;
private TranslatorInterface $translator;
public function __construct(UrlGeneratorInterface $generator, TranslatorInterface $translator)
{
$this->generator = $generator;
$this->translator = $translator;
}
/** /**
* @Route( * @Route(
* "/api/1.0/person/household/members/move.{_format}", * "/api/1.0/person/household/members/move.{_format}",
@ -44,4 +59,39 @@ class HouseholdMemberController extends ApiController
"groups" => ["read"], "groups" => ["read"],
]); ]);
} }
/**
* @Route(
* "/{_locale}/person/household/member/{id}/edit",
* name="chill_person_household_member_edit"
* )
*/
public function editMembership(Request $request, HouseholdMember $member): Response
{
// TODO ACL
$form = $this->createForm(HouseholdMemberType::class, $member);
$form->handleRequest($request);
if ($form->isSubmitted() && $form->isValid()) {
$this->getDoctrine()->getManager()->flush();
$this->addFlash('success', $this->translator
->trans('household.successfully saved member'))
;
return $this->redirect(
$request->get('returnPath', null) ??
$this->generator->generate('chill_person_household_members', [ 'household_id' =>
$member->getHousehold()->getId() ])
);
}
return $this->render('@ChillPerson/Household/Member/edit.html.twig', [
'household' => $member->getHousehold(),
'member' => $member,
'form' => $form->createView()
]);
}
} }

View File

@ -5,6 +5,7 @@ namespace Chill\PersonBundle\Entity\Household;
use Doctrine\Common\Collections\ArrayCollection; use Doctrine\Common\Collections\ArrayCollection;
use Doctrine\ORM\Mapping as ORM; use Doctrine\ORM\Mapping as ORM;
use Doctrine\Common\Collections\Collection; use Doctrine\Common\Collections\Collection;
use Doctrine\Common\Collections\Criteria;
use Symfony\Component\Serializer\Annotation as Serializer; use Symfony\Component\Serializer\Annotation as Serializer;
use Chill\MainBundle\Entity\Address; use Chill\MainBundle\Entity\Address;
use Chill\PersonBundle\Entity\Household\HouseholdMember; use Chill\PersonBundle\Entity\Household\HouseholdMember;
@ -97,6 +98,79 @@ class Household
return $this->members; return $this->members;
} }
public function getCurrentMembers(?\DateTimeImmutable $now = null): Collection
{
$criteria = new Criteria();
$expr = Criteria::expr();
$date = $now === null ? (new \DateTimeImmutable('now')) : $now;
$criteria
->where($expr->orX(
$expr->isNull('startDate'),
$expr->lte('startDate', $date)
))
->andWhere($expr->orX(
$expr->isNull('endDate'),
$expr->gte('endDate', $date)
));
return $this->getMembers()->matching($criteria);
}
/**
* Get the persons currently associated to the household.
*
* Return a list of Person, instead of a list of HouseholdMembers
*
* @return Person[]
*/
public function getCurrentPersons(?\DateTimeImmutable $now = null): Collection
{
return $this->getCurrentMembers($now)
->map(function(HouseholdMember $m) { return $m->getPerson(); });
}
public function getNonCurrentMembers(\DateTimeImmutable $now = null): Collection
{
$criteria = new Criteria();
$expr = Criteria::expr();
$date = $now === null ? (new \DateTimeImmutable('now')) : $now;
$criteria
->where(
$expr->gt('startDate', $date)
)
->orWhere(
$expr->andX(
$expr->lt('endDate', $date),
$expr->neq('endDate', null)
)
);
return $this->getMembers()->matching($criteria);
}
public function getCurrentMembersByPosition(Position $position, \DateTimeInterface $now = null)
{
$criteria = new Criteria();
$expr = Criteria::expr();
$criteria->where($expr->eq('position', $position));
return $this->getCurrentMembers($now)->matching($criteria);
}
public function getNonCurrentMembersByPosition(Position $position, \DateTimeInterface $now = null)
{
$criteria = new Criteria();
$expr = Criteria::expr();
$criteria->where($expr->eq('position', $position));
return $this->getNonCurrentMembers($now)->matching($criteria);
}
public function addMember(HouseholdMember $member): self public function addMember(HouseholdMember $member): self
{ {
if (!$this->members->contains($member)) { if (!$this->members->contains($member)) {

View File

@ -120,7 +120,7 @@ class HouseholdMember
return $this->endDate; return $this->endDate;
} }
public function setEndDate(\DateTimeImmutable $endDate): self public function setEndDate(?\DateTimeImmutable $endDate = null): self
{ {
$this->endDate = $endDate; $this->endDate = $endDate;

View File

@ -21,34 +21,34 @@ class Position
* @ORM\Column(type="integer") * @ORM\Column(type="integer")
* @Serializer\Groups({ "read" }) * @Serializer\Groups({ "read" })
*/ */
private $id; private ?int $id;
/** /**
* @ORM\Column(type="json") * @ORM\Column(type="json")
*/ */
private $label = []; private array $label = [];
/** /**
* @ORM\Column(type="boolean") * @ORM\Column(type="boolean")
*/ */
private $shareHouseHold; private bool $shareHouseHold = true;
/** /**
* @ORM\Column(type="boolean") * @ORM\Column(type="boolean")
*/ */
private $allowHolder; private bool $allowHolder = false;
/** /**
* @ORM\Column(type="float") * @ORM\Column(type="float")
*/ */
private $ordering; private float $ordering = 0.00;
public function getId(): ?int public function getId(): ?int
{ {
return $this->id; return $this->id;
} }
public function getLabel(): ?array public function getLabel(): array
{ {
return $this->label; return $this->label;
} }
@ -60,7 +60,7 @@ class Position
return $this; return $this;
} }
public function getShareHousehold(): ?bool public function getShareHousehold(): bool
{ {
return $this->shareHouseHold; return $this->shareHouseHold;
} }
@ -72,11 +72,16 @@ class Position
return $this; return $this;
} }
public function getAllowHolder(): ?bool public function getAllowHolder(): bool
{ {
return $this->allowHolder; return $this->allowHolder;
} }
public function isAllowHolder(): bool
{
return $this->getAllowHolder();
}
public function setAllowHolder(bool $allowHolder): self public function setAllowHolder(bool $allowHolder): self
{ {
$this->allowHolder = $allowHolder; $this->allowHolder = $allowHolder;
@ -84,7 +89,7 @@ class Position
return $this; return $this;
} }
public function getOrdering(): ?float public function getOrdering(): float
{ {
return $this->ordering; return $this->ordering;
} }

View File

@ -0,0 +1,42 @@
<?php
namespace Chill\PersonBundle\Form;
use Chill\MainBundle\Form\Type\ChillTextareaType;
use Chill\MainBundle\Form\Type\ChillDateType;
use Symfony\Component\Form\AbstractType;
use Symfony\Component\Form\Extension\Core\Type\ChoiceType;
use Symfony\Component\Form\FormBuilderInterface;
class HouseholdMemberType extends AbstractType
{
public function buildForm(FormBuilderInterface $builder, array $options)
{
$builder
->add('startDate', ChillDateType::class, [
'label' => 'household.Start date',
'input' => 'datetime_immutable',
])
->add('endDate', ChillDateType::class, [
'label' => 'household.End date',
'input' => 'datetime_immutable',
'required' => false
])
;
if ($options['data']->getPosition()->isAllowHolder()) {
$builder
->add('holder', ChoiceType::class, [
'label' => 'household.holder',
'choices' => [
'household.is holder' => true,
'household.is not holder' => false
]
]);
}
$builder
->add('comment', ChillTextareaType::class, [
'label' => 'household.Comment'
])
;
}
}

View File

@ -29,21 +29,21 @@ class HouseholdMenuBuilder implements LocalMenuBuilderInterface
{ {
$household = $parameters['household']; $household = $parameters['household'];
$menu->addChild($this->translator->trans('Summary'), [ $menu->addChild($this->translator->trans('household.Household summary'), [
'route' => 'chill_person_household_summary', 'route' => 'chill_person_household_summary',
'routeParameters' => [ 'routeParameters' => [
'household_id' => $household->getId() 'household_id' => $household->getId()
]]) ]])
->setExtras(['order' => 10]); ->setExtras(['order' => 10]);
$menu->addChild($this->translator->trans('Members'), [ $menu->addChild($this->translator->trans('household.Household members'), [
'route' => 'chill_person_household_members', 'route' => 'chill_person_household_members',
'routeParameters' => [ 'routeParameters' => [
'household_id' => $household->getId() 'household_id' => $household->getId()
]]) ]])
->setExtras(['order' => 20]); ->setExtras(['order' => 20]);
$menu->addChild($this->translator->trans('Addresses'), [ $menu->addChild($this->translator->trans('household.Addresses'), [
'route' => 'chill_person_household_addresses', 'route' => 'chill_person_household_addresses',
'routeParameters' => [ 'routeParameters' => [
'household_id' => $household->getId() 'household_id' => $household->getId()

View File

@ -1,5 +1,21 @@
/// complete and overwrite flex-table in chillmain.scss /// complete and overwrite flex-table in chillmain.scss
div.list-with-period { div.list-with-period,
div.list-household-members,
div.list-household-members--summary {
.chill-entity__person {
.chill-entity__person__first-name,
.chill-entity__person__last-name {
font-size: 1.3em;
font-weight: 700;
}
}
.chill_denomination {
font-size: 1.3em;
font-weight: 700;
}
div.person { div.person {
ul.record_actions { ul.record_actions {
li { li {
@ -7,6 +23,9 @@ div.list-with-period {
} }
} }
} }
div.comment {
// for the comment for household-members
}
div.periods { div.periods {
div.header, div.header,
div.list-content { div.list-content {
@ -40,3 +59,5 @@ div.list-with-period {
} }
} }
} }

View File

@ -7,9 +7,11 @@
{%- for n in person.altNames -%} {%- for n in person.altNames -%}
{%- if loop.first -%}({% else %} {%- endif -%} {%- if loop.first -%}({% else %} {%- endif -%}
<span class="chill-entity__person__alt-name chill-entity__person__altname--{{ n.key }}"> <span class="chill-entity__person__alt-name chill-entity__person__altname--{{ n.key }}">
{{ n.label }} {{- n.label -}}
</span> </span>
{%- if loop.last %}) {% endif -%} {%- if loop.last -%}) {%- endif -%}
{%- endfor -%} {%- endfor -%}
{%- endif -%} {%- endif -%}
{%- if showLink is defined -%}</a>{%- endif -%}</span> {%- if showLink is defined -%}</a>{%- endif -%}
{#- tricks to remove easily whitespace after template -#}
{%- if true -%}</span>{%- endif -%}

View File

@ -0,0 +1,28 @@
{% extends '@ChillPerson/Household/layout.html.twig' %}
{% block title 'household.Edit member household'|trans %}
{% block content %}
<h1>{{ block('title') }}</h1>
{{ form_start(form) }}
{{ form_widget(form) }}
<ul class="record_actions sticky-form-buttons">
<li class="cancel">
<a
href="{{ chill_return_path_or('chill_person_household_members', { 'household_id': household.id}) }}"
class="sc-button bt-cancel"
>
{{ 'Cancel'|trans }}
</a>
</li>
<li>
<button type="submit" class="sc-button bt-save">{{ 'Save'|trans }}</button>
</li>
</ul>
{{ form_end(form) }}
{% endblock %}

View File

@ -1,11 +1,11 @@
<div class="subheader"> <div class="subheader banner-household">
<div class="grid-12 parent" id="header-accompanying_course-name" > <div class="grid-12 parent" id="header-accompanying_course-name" >
<div class="grid-10 push-1 grid-mobile-12 grid-tablet-12 push-mobile-0 push-tablet-0 parent"> <div class="grid-10 push-1 grid-mobile-12 grid-tablet-12 push-mobile-0 push-tablet-0 parent">
<div class="grid-6">{% set title = title %} <div class="grid-6">{% set title = title %}
<h1> <h1>
<i class="fa fa-child"></i> <i class="fa fa-home"></i>
{{ 'Household'|trans }} {{ 'household.Household'|trans }}
<span style="font-weight: lighter; font-size: 50%;">(n°{{ household.id }})</span> <span style="font-weight: lighter; font-size: 50%;">(n°{{ household.id }})</span>
</h1> </h1>
</div> </div>
@ -18,9 +18,23 @@
</div> </div>
<div class="grid-12 parent" id="header-accompanying_course-details" > <div class="grid-12 parent" id="header-accompanying_course-details" >
<div class="grid-10 push-1 grid-mobile-12 grid-tablet-12 push-mobile-0 push-tablet-0 parent"> <div class="grid-10 push-1 grid-mobile-12 grid-tablet-12 push-mobile-0 push-tablet-0 parent">
<div id="banner-misc">
<div id="banner-misc"></div> {%- set persons = household.getCurrentPersons() -%}
{%- if persons|length > 0 -%}
<span class="current-members-explain">
{{- 'household.Current household members'|trans }}:
</span>
{%- for p in persons|slice(0, 5) -%}
{{- p|chill_entity_render_box({'addLink': false}) -}}
{%- if false == loop.last -%}, {% endif -%}
{%- endfor -%}
{% if persons|length > 5 %}
<span class="current-members-more">
{{ 'household.and x other persons'|trans({'x': persons|length-5}) }}
</span>
{% endif %}
{%- endif -%}
</div>
</div> </div>
</div> </div>
</div> </div>

View File

@ -1,10 +1,138 @@
{% extends '@ChillPerson/Household/layout.html.twig' %} {% extends '@ChillPerson/Household/layout.html.twig' %}
{% block title 'Household members'|trans %} {% block title 'household.Household members'|trans %}
{% block content %} {% block content %}
<h1>{{ block('title') }}</h1> <h1>{{ block('title') }}</h1>
<p>Household with id {{ household.id }}</p> {% for p in positions %}
<h3>{{ p.label|localize_translatable_string }}</h3>
{% if false == p.shareHousehold %}
<p>{{ 'household.Those members does not share address'|trans }}</p>
{% endif %}
{%- set members = household.currentMembersByPosition(p) %}
{% if members|length > 0 %}
<div class="flex-table list-household-members">
{% for m in members %}
<div class="item-bloc">
<div class="item-row person">
<div class="item-col box-person">
<div>
{{ m.person|chill_entity_render_box({'addLink': true}) }}
{% if m.holder %}
<span class="badge badge-primary">{{ 'household.holder'|trans }}</span>
{% endif %}
</div>
<div>
{{ 'Born the date'|trans({ 'gender': m.person.gender, 'birthdate': m.person.birthdate|format_date('long') }) }}
</div>
</div>
<div class="item-col box-where">
<ul class="list-content fa-ul">
{% if m.startDate is not empty %}
<li>{{ 'Since %date%'|trans({'%date%': m.startDate|format_date('long') }) }}</li>
{% endif %}
{% if m.endDate is not empty %}
<li>{{ 'Until %date%'|trans({'%date%': m.endDate|format_date('long') }) }}</li>
{% endif %}
</ul>
<ul class="record_actions">
<li>
<a
href="{{ chill_path_add_return_path('chill_person_household_member_edit', { 'id': m.id }) }}"
class="sc-button bt-edit"
/>
{{ 'household.Update membership'|trans }}
</a>
</li>
<li>
<a href="#" class="sc-button" /><i class="fa fa-sign-out"></i>{{ 'household.Leave'|trans }}</a>
</li>
</ul>
</div>
</div>
{% if m.comment is not empty %}
<div class="item-row comment">
<blockquote class="chill-user-quote">
{{ m.comment|chill_markdown_to_html }}
</blockquote>
</div>
{% endif %}
</div>
{% endfor %}
</div>
{% else %}
<p class="chill-no-data-statement">{{ 'household.Any persons into this position'|trans }}</p>
{% endif %}
{% set members = household.nonCurrentMembersByPosition(p) %}
{% if members|length > 0 %}
<p><!-- force a space after table --></p>
<button class="sc-button bt-green" type="button" data-toggle="collapse" data-target="#nonCurrent_{{ p.id }}" aria-expanded="false" aria-controls="collapse non current members">
{{ 'household.Show future or past memberships'|trans({'length': members|length}) }}
</button>
<div id="nonCurrent_{{ p.id }}" class="collapse">
<div class="flex-table list-household-members">
{% for m in members %}
<div class="item-bloc">
<div class="item-row person">
<div class="item-col box-person">
<div>
{{ m.person|chill_entity_render_box({'addLink': true}) }}
{% if m.holder %}
<span class="badge badge-primary">{{ 'household.holder'|trans }}</span>
{% endif %}
</div>
<div>
{{ 'Born the date'|trans({ 'gender': m.person.gender, 'birthdate': m.person.birthdate|format_date('long') }) }}
</div>
</div>
<div class="item-col box-where">
<ul class="list-content fa-ul">
{% if m.startDate is not empty %}
<li>{{ 'Since %date%'|trans({'%date%': m.startDate|format_date('long') }) }}</li>
{% endif %}
{% if m.endDate is not empty %}
<li>{{ 'Until %date%'|trans({'%date%': m.endDate|format_date('long') }) }}</li>
{% endif %}
</ul>
<ul class="record_actions">
<li>
<a
href="{{ chill_path_add_return_path('chill_person_household_member_edit', { 'id': m.id }) }}"
class="sc-button bt-edit"
/>
{{ 'household.Update membership'|trans }}
</a>
</li>
</ul>
</div>
</div>
{% if m.comment is not empty %}
<div class="item-row comment">
<blockquote class="chill-user-quote">
{{ m.comment|chill_markdown_to_html }}
</blockquote>
</div>
{% endif %}
</div>
{% endfor %}
</div>
</div>
{% endif %}
{% endfor %}
<ul class="record_actions sticky-form-buttons">
<li>
<a class="sc-button bt-create">
{{ 'household.Add a member'|trans }}
</a>
</li>
</ul>
{% endblock %} {% endblock %}

View File

@ -1,14 +1,53 @@
{% extends '@ChillPerson/Household/layout.html.twig' %} {% extends '@ChillPerson/Household/layout.html.twig' %}
{% block title 'Household summary'|trans %} {% block title 'household.Household summary'|trans %}
{% block content %} {% block content %}
<h1>{{ block('title') }}</h1> <h1>{{ block('title') }}</h1>
<p>Household with id {{ household.id }}</p> <h2>{{ 'household.Current household members'|trans }}</h2>
<h2>{{ 'Actual household members'|trans }}</h2> {% for p in positions %}
{%- set members = household.currentMembersByPosition(p) %}
{% if members|length > 0 %}
<h3>{{ p.label|localize_translatable_string }}</h3>
<p>TODO</p> {% if false == p.shareHousehold %}
<p>{{ 'household.Those members does not share address'|trans }}</p>
{% endif %}
<div class="flex-table list-household-members--summary">
{% for m in members %}
<div class="item-bloc">
<div class="item-row person">
<div class="item-col box-person">
<div>
{{ m.person|chill_entity_render_box({'addLink': true}) }}
{% if m.holder %}
<span class="badge badge-primary">{{ 'household.holder'|trans }}</span>
{% endif %}
</div>
<div>
{{ 'Born the date'|trans({ 'gender': m.person.gender, 'birthdate': m.person.birthdate|format_date('long') }) }}
</div>
</div>
<div class="item-col box-where">
<ul class="list-content fa-ul">
{% if m.startDate is not empty %}
<li>{{ 'Since %date%'|trans({'%date%': m.startDate|format_date('long') }) }}</li>
{% endif %}
{% if m.endDate is not empty %}
<li>{{ 'Until %date%'|trans({'%date%': m.endDate|format_date('long') }) }}</li>
{% endif %}
</ul>
</div>
</div>
</div>
{% endfor %}
</div>
{% endif %}
{% endfor %}
{% endblock %} {% endblock %}

View File

@ -8,6 +8,7 @@ use Chill\MainBundle\Test\PrepareClientTrait;
use Symfony\Bundle\FrameworkBundle\Test\WebTestCase; use Symfony\Bundle\FrameworkBundle\Test\WebTestCase;
use Chill\PersonBundle\Entity\Person; use Chill\PersonBundle\Entity\Person;
use Chill\PersonBundle\Entity\Household\Household; use Chill\PersonBundle\Entity\Household\Household;
use Chill\PersonBundle\Entity\Household\HouseholdMember;
use Chill\PersonBundle\Entity\Household\Position; use Chill\PersonBundle\Entity\Household\Position;
use Doctrine\ORM\EntityManagerInterface; use Doctrine\ORM\EntityManagerInterface;
@ -17,7 +18,7 @@ class HouseholdMemberControllerTest extends WebTestCase
use PrepareClientTrait; use PrepareClientTrait;
/** /**
* @dataProvider provideValidData * @dataProvider provideValidDataMove
*/ */
public function testMoveMember($personId, $householdId, $positionId, \DateTimeInterface $date) public function testMoveMember($personId, $householdId, $positionId, \DateTimeInterface $date)
{ {
@ -66,7 +67,31 @@ class HouseholdMemberControllerTest extends WebTestCase
); );
} }
public function provideValidData(): \Iterator /**
* @dataProvider provideValidDataEditMember
*/
public function testEditMember($memberId)
{
$client = $this->getClientAuthenticated();
$crawler = $client->request(
Request::METHOD_GET,
"/fr/person/household/member/{$memberId}/edit"
);
$this->assertResponseIsSuccessful();
$form = $crawler->selectButton('Enregistrer')
->form();
$form['household_member[endDate]'] = (new \DateTime('tomorrow'))
->format('Y-m-d');
$crawler = $client->submit($form);
$this->assertEquals(302, $client->getResponse()->getStatusCode());
}
public function provideValidDataMove(): \Iterator
{ {
self::bootKernel(); self::bootKernel();
$em = self::$container->get(EntityManagerInterface::class); $em = self::$container->get(EntityManagerInterface::class);
@ -95,4 +120,22 @@ class HouseholdMemberControllerTest extends WebTestCase
new \DateTimeImmutable('today') new \DateTimeImmutable('today')
]; ];
} }
public function provideValidDataEditMember(): \Iterator
{
self::bootKernel();
$em = self::$container->get(EntityManagerInterface::class);
$membershipIds = $em->createQuery("SELECT m.id FROM ".HouseholdMember::class." m ".
"JOIN m.person p ".
"JOIN p.center c ".
"WHERE c.name = :center AND m.endDate IS NULL")
->setParameter('center', 'Center A')
->getScalarResult()
;
\shuffle($membershipIds);
yield [ \array_pop($membershipIds)['id'] ];
}
} }

View File

@ -63,6 +63,11 @@ services:
resource: '../Repository/' resource: '../Repository/'
tags: ['doctrine.repository_service'] tags: ['doctrine.repository_service']
Chill\PersonBundle\Controller\:
autowire: true
resource: '../Controller/'
tags: ['controller.service_arguments']
Chill\PersonBundle\Templating\Entity\: Chill\PersonBundle\Templating\Entity\:
autowire: true autowire: true
autoconfigure: true autoconfigure: true

View File

@ -0,0 +1,37 @@
Born the date: >-
{gender, select,
man {Né le {birthdate}}
woman {Née le {birthdate}}
other {Né·e le {birthdate}}
}
household:
Household: Ménage
Household members: Membres du ménage
Show future or past memberships: >-
{length, plural,
one {Montrer une ancienne appartenance}
many {Montrer # anciennes ou futures appartenances}
other {Montrer # anciennes ou futures appartenances}
}
Those members does not share address: Ces usagers ne partagent pas l'adresse du ménage.
Any persons into this position: Aucune personne n'appartient au ménage à cette position.
Leave: Quitter le ménage
Update membership: Modifier
successfully saved member: Membre enregistré avec succès
Start date: Date de début de l'appartenance au ménage
End date: Date de fin de l'appartenance au ménage
Comment: Commentaire
is holder: Est titulaire
is not holder: N'est pas titulaire
holder: Titulaire
Edit member household: Modifier l'appartenance au ménage
Current household members: Membres actuels du ménage
Household summary: Résumé
Addresses: Adresses
and x other persons: >-
{x, plural,
one {et une autre personne}
many {et # autres personnes}
other {et # autres personnes}
}

View File

@ -46,7 +46,6 @@ Add new phone: Ajouter un numéro de téléphone
Remove phone: Supprimer Remove phone: Supprimer
'Notes on contact information': 'Remarques sur les informations de contact' 'Notes on contact information': 'Remarques sur les informations de contact'
'Remarks': 'Remarques' 'Remarks': 'Remarques'
'Born the %date%': '{0} Né le %date% | {1} Née le %date%'
'Spoken languages': 'Langues parlées' 'Spoken languages': 'Langues parlées'
'Unknown spoken languages': 'Langues parlées inconnues' 'Unknown spoken languages': 'Langues parlées inconnues'
Male: Homme Male: Homme