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

This commit is contained in:
Julien Fastré 2021-06-07 17:36:25 +02:00
commit 7c94824c20
18 changed files with 433 additions and 27 deletions

View File

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

View File

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

View File

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

View File

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

View File

@ -8,6 +8,7 @@ use Symfony\Component\HttpFoundation\Response;
use Symfony\Component\Routing\Annotation\Route;
use Sensio\Bundle\FrameworkExtraBundle\Configuration\ParamConverter;
use Chill\PersonBundle\Entity\Household\Household;
use Chill\PersonBundle\Entity\Household\Position;
/**
* @Route("/{_locale}/person/household")
@ -25,9 +26,20 @@ class HouseholdController extends AbstractController
public function summary(Request $request, Household $household)
{
// 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',
[
'household' => $household
'household' => $household,
'positions' => $positions
]
);
}
@ -43,9 +55,20 @@ class HouseholdController extends AbstractController
public function members(Request $request, Household $household)
{
// 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',
[
'household' => $household
'household' => $household,
'positions' => $positions
]
);
}

View File

@ -6,17 +6,32 @@ use Chill\PersonBundle\Entity\Household\Position;
use Chill\PersonBundle\Security\Authorization\PersonVoter;
use Chill\PersonBundle\Entity\Person;
use Chill\PersonBundle\Entity\Household\Household;
use Chill\PersonBundle\Form\HouseholdMemberType;
use Chill\PersonBundle\Entity\Household\HouseholdMember;
use Symfony\Component\HttpFoundation\Exception\BadRequestException;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpFoundation\Response;
use Symfony\Component\Routing\Generator\UrlGeneratorInterface;
use Symfony\Component\Serializer\Exception;
use Symfony\Component\Routing\Annotation\Route;
use Chill\MainBundle\CRUD\Controller\ApiController;
use Symfony\Component\Translation\TranslatorInterface;
use Chill\PersonBundle\Household\MembersEditor;
use Symfony\Component\Serializer\Normalizer\AbstractNormalizer;
class HouseholdMemberController extends ApiController
{
private UrlGeneratorInterface $generator;
private TranslatorInterface $translator;
public function __construct(UrlGeneratorInterface $generator, TranslatorInterface $translator)
{
$this->generator = $generator;
$this->translator = $translator;
}
/**
* @Route(
* "/api/1.0/person/household/members/move.{_format}",
@ -153,4 +168,39 @@ class HouseholdMemberController extends ApiController
'data' => $data
]);
}
/**
* @Route(
* "/api/1.0/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\ORM\Mapping as ORM;
use Doctrine\Common\Collections\Collection;
use Doctrine\Common\Collections\Criteria;
use Symfony\Component\Serializer\Annotation as Serializer;
use Chill\MainBundle\Entity\Address;
use Chill\PersonBundle\Entity\Household\HouseholdMember;
@ -97,6 +98,66 @@ class Household
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);
}
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
{
if (!$this->members->contains($member)) {

View File

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

View File

@ -81,6 +81,11 @@ class Position
return $this->allowHolder;
}
public function isAllowHolder(): ?bool
{
return $this->getAllowHolder();
}
public function setAllowHolder(bool $allowHolder): self
{
$this->allowHolder = $allowHolder;

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'];
$menu->addChild($this->translator->trans('Summary'), [
$menu->addChild($this->translator->trans('household.Household summary'), [
'route' => 'chill_person_household_summary',
'routeParameters' => [
'household_id' => $household->getId()
]])
->setExtras(['order' => 10]);
$menu->addChild($this->translator->trans('Members'), [
$menu->addChild($this->translator->trans('household.Household members'), [
'route' => 'chill_person_household_members',
'routeParameters' => [
'household_id' => $household->getId()
]])
->setExtras(['order' => 20]);
$menu->addChild($this->translator->trans('Addresses'), [
$menu->addChild($this->translator->trans('household.Addresses'), [
'route' => 'chill_person_household_addresses',
'routeParameters' => [
'household_id' => $household->getId()

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

@ -5,7 +5,7 @@
<div class="grid-6">{% set title = title %}
<h1>
<i class="fa fa-child"></i>
{{ 'Household'|trans }}
{{ 'household.Household'|trans }}
<span style="font-weight: lighter; font-size: 50%;">(n°{{ household.id }})</span>
</h1>
</div>

View File

@ -1,10 +1,131 @@
{% extends '@ChillPerson/Household/layout.html.twig' %}
{% block title 'Household members'|trans %}
{% block title 'household.Household members'|trans %}
{% block content %}
<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="household-list-members flex-table list-with-period">
{% 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="household-list-members flex-table list-with-period">
{% 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 %}
{% endblock %}

View File

@ -1,14 +1,53 @@
{% extends '@ChillPerson/Household/layout.html.twig' %}
{% block title 'Household summary'|trans %}
{% block title 'household.Household summary'|trans %}
{% block content %}
<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="household-list-members flex-table list-with-period">
{% 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 %}

View File

@ -69,3 +69,8 @@ services:
resource: '../Templating/Entity'
tags:
- 'chill.render_entity'
Chill\PersonBundle\Controller\:
autowire: true
resource: '../Controller/'
tags: ['controller.service_arguments']

View File

@ -0,0 +1,31 @@
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

View File

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