From 9244bb2f8dd9ebb6775bc570390ba958bf9cdf8c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Julien=20Fastr=C3=A9?= Date: Fri, 3 Dec 2021 15:06:06 +0100 Subject: [PATCH 1/8] fix pagination problems (cherry picked from commit 43702ded77449ee08b8d2c5c494c220d457373a1) --- src/Bundle/ChillMainBundle/Pagination/Paginator.php | 8 ++++++++ .../ChillMainBundle/Tests/Pagination/PaginatorTest.php | 8 ++++++++ 2 files changed, 16 insertions(+) diff --git a/src/Bundle/ChillMainBundle/Pagination/Paginator.php b/src/Bundle/ChillMainBundle/Pagination/Paginator.php index d6da55618..54fa36084 100644 --- a/src/Bundle/ChillMainBundle/Pagination/Paginator.php +++ b/src/Bundle/ChillMainBundle/Pagination/Paginator.php @@ -109,6 +109,10 @@ class Paginator implements PaginatorInterface return 1; } + if (0 === $this->totalItems) { + return 1; + } + $nb = floor($this->totalItems / $this->itemPerPage); if ($this->totalItems % $this->itemPerPage > 0) { @@ -211,6 +215,10 @@ class Paginator implements PaginatorInterface public function hasPage($number) { + if (0 === $this->totalItems) { + return 1 === $number; + } + return 0 < $number && $this->countPages() >= $number; } diff --git a/src/Bundle/ChillMainBundle/Tests/Pagination/PaginatorTest.php b/src/Bundle/ChillMainBundle/Tests/Pagination/PaginatorTest.php index 219669825..2a92ef222 100644 --- a/src/Bundle/ChillMainBundle/Tests/Pagination/PaginatorTest.php +++ b/src/Bundle/ChillMainBundle/Tests/Pagination/PaginatorTest.php @@ -204,6 +204,14 @@ final class PaginatorTest extends KernelTestCase ); } + public function testPagesWithoutResult() + { + $paginator = $this->generatePaginator(0, 10); + + $this->assertEquals(0, $paginator->getCurrentPageFirstItemNumber()); + $this->assertEquals(10, $paginator->getItemsPerPage()); + } + /** * @param int $itemPerPage * @param string $route From 02c93389d88a29445bc7bfa2f12d990d40723678 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Julien=20Fastr=C3=A9?= Date: Fri, 3 Dec 2021 15:18:45 +0100 Subject: [PATCH 2/8] fix code style (cherry picked from commit a86ba6faf53bc7fe70db95c833a13ca88e96cb3f) --- src/Bundle/ChillActivityBundle/Entity/Activity.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Bundle/ChillActivityBundle/Entity/Activity.php b/src/Bundle/ChillActivityBundle/Entity/Activity.php index 419892f9b..77f650ce4 100644 --- a/src/Bundle/ChillActivityBundle/Entity/Activity.php +++ b/src/Bundle/ChillActivityBundle/Entity/Activity.php @@ -46,7 +46,7 @@ use Symfony\Component\Serializer\Annotation\SerializedName; * "activity": Activity::class * }) * @ActivityValidator\ActivityValidity - * + * * TODO see if necessary * UserCircleConsistency( * "CHILL_ACTIVITY_SEE_DETAILS", From 938720be52e3bce90fb133c38a15456de490abc9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Julien=20Fastr=C3=A9?= Date: Mon, 6 Dec 2021 12:56:57 +0000 Subject: [PATCH 3/8] Issue316 addresses search by postal code --- CHANGELOG.md | 3 + .../AddressReferenceAPIController.php | 60 +++++++++++- .../Controller/PostalCodeAPIController.php | 75 ++++++++++++++- .../Entity/AddressReference.php | 9 ++ .../ChillMainBundle/Entity/PostalCode.php | 9 ++ .../Repository/AddressReferenceRepository.php | 91 +++++++++++++++++++ .../Repository/PostalCodeRepository.php | 85 +++++++++++++++++ .../Resources/public/vuejs/Address/api.js | 41 ++++++++- .../vuejs/Address/components/AddAddress.vue | 9 +- .../AddAddress/AddressSelection.vue | 38 +++++++- .../components/AddAddress/CitySelection.vue | 45 ++++++++- .../vuejs/Address/components/EditPane.vue | 2 + .../AddressReferenceApiControllerTest.php | 67 ++++++++++++++ .../PostalCodeApiControllerTest.php | 57 ++++++++++++ .../ChillMainBundle/chill.api.specs.yaml | 65 +++++++++++++ .../migrations/Version20211125142016.php | 85 +++++++++++++++++ .../migrations/Version20211125142017.php | 85 +++++++++++++++++ .../Controller/HouseholdMemberController.php | 2 +- 18 files changed, 805 insertions(+), 23 deletions(-) create mode 100644 src/Bundle/ChillMainBundle/Tests/Controller/AddressReferenceApiControllerTest.php create mode 100644 src/Bundle/ChillMainBundle/Tests/Controller/PostalCodeApiControllerTest.php create mode 100644 src/Bundle/ChillMainBundle/migrations/Version20211125142016.php create mode 100644 src/Bundle/ChillMainBundle/migrations/Version20211125142017.php diff --git a/CHANGELOG.md b/CHANGELOG.md index ec27da83d..b81ca9783 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -11,6 +11,9 @@ and this project adheres to ## Unreleased +* [main] address: use search API end points for getting postal code and reference address (https://gitlab.com/champs-libres/departement-de-la-vendee/chill/-/issues/316) +* [main] address: in edit mode, select the encoded values in multiselect for address reference and city (https://gitlab.com/champs-libres/departement-de-la-vendee/chill/-/issues/316) + * [person search] fix bug when using birthdate after and birthdate before * [person search] increase pertinence when lastname begins with search pattern diff --git a/src/Bundle/ChillMainBundle/Controller/AddressReferenceAPIController.php b/src/Bundle/ChillMainBundle/Controller/AddressReferenceAPIController.php index 1230cd0f6..498c77c68 100644 --- a/src/Bundle/ChillMainBundle/Controller/AddressReferenceAPIController.php +++ b/src/Bundle/ChillMainBundle/Controller/AddressReferenceAPIController.php @@ -12,14 +12,66 @@ declare(strict_types=1); namespace Chill\MainBundle\Controller; use Chill\MainBundle\CRUD\Controller\ApiController; +use Chill\MainBundle\Entity\PostalCode; +use Chill\MainBundle\Pagination\PaginatorFactory; use Chill\MainBundle\Pagination\PaginatorInterface; +use Chill\MainBundle\Repository\AddressReferenceRepository; +use Chill\MainBundle\Serializer\Model\Collection; +use Symfony\Component\HttpFoundation\JsonResponse; use Symfony\Component\HttpFoundation\Request; +use Symfony\Component\HttpFoundation\Response; +use Symfony\Component\HttpKernel\Exception\BadRequestHttpException; +use Symfony\Component\Routing\Annotation\Route; +use Symfony\Component\Serializer\Normalizer\AbstractNormalizer; +use function trim; -/** - * Class AddressReferenceAPIController. - */ -class AddressReferenceAPIController extends ApiController +final class AddressReferenceAPIController extends ApiController { + private AddressReferenceRepository $addressReferenceRepository; + + private PaginatorFactory $paginatorFactory; + + public function __construct(AddressReferenceRepository $addressReferenceRepository, PaginatorFactory $paginatorFactory) + { + $this->addressReferenceRepository = $addressReferenceRepository; + $this->paginatorFactory = $paginatorFactory; + } + + /** + * @Route("/api/1.0/main/address-reference/by-postal-code/{id}/search.json") + */ + public function search(PostalCode $postalCode, Request $request): JsonResponse + { + $this->denyAccessUnlessGranted('ROLE_USER'); + + if (!$request->query->has('q')) { + throw new BadRequestHttpException('You must supply a "q" parameter'); + } + + $pattern = $request->query->get('q'); + + if ('' === trim($pattern)) { + throw new BadRequestHttpException('the search pattern is empty'); + } + + $nb = $this->addressReferenceRepository->countByPostalCodePattern($postalCode, $pattern); + $paginator = $this->paginatorFactory->create($nb); + $addresses = $this->addressReferenceRepository->findByPostalCodePattern( + $postalCode, + $pattern, + false, + $paginator->getCurrentPageFirstItemNumber(), + $paginator->getItemsPerPage() + ); + + return $this->json( + new Collection($addresses, $paginator), + Response::HTTP_OK, + [], + [AbstractNormalizer::GROUPS => ['read']] + ); + } + protected function customizeQuery(string $action, Request $request, $qb): void { if ($request->query->has('postal_code')) { diff --git a/src/Bundle/ChillMainBundle/Controller/PostalCodeAPIController.php b/src/Bundle/ChillMainBundle/Controller/PostalCodeAPIController.php index fb926733b..fa1a29296 100644 --- a/src/Bundle/ChillMainBundle/Controller/PostalCodeAPIController.php +++ b/src/Bundle/ChillMainBundle/Controller/PostalCodeAPIController.php @@ -12,13 +12,80 @@ declare(strict_types=1); namespace Chill\MainBundle\Controller; use Chill\MainBundle\CRUD\Controller\ApiController; +use Chill\MainBundle\Pagination\PaginatorFactory; +use Chill\MainBundle\Repository\CountryRepository; +use Chill\MainBundle\Repository\PostalCodeRepository; +use Chill\MainBundle\Serializer\Model\Collection; +use Symfony\Component\HttpFoundation\JsonResponse; use Symfony\Component\HttpFoundation\Request; +use Symfony\Component\HttpFoundation\Response; +use Symfony\Component\HttpKernel\Exception\BadRequestHttpException; +use Symfony\Component\HttpKernel\Exception\NotFoundHttpException; +use Symfony\Component\Routing\Annotation\Route; +use Symfony\Component\Serializer\Normalizer\AbstractNormalizer; -/** - * Class PostalCodeAPIController. - */ -class PostalCodeAPIController extends ApiController +final class PostalCodeAPIController extends ApiController { + private CountryRepository $countryRepository; + + private PaginatorFactory $paginatorFactory; + + private PostalCodeRepository $postalCodeRepository; + + public function __construct( + CountryRepository $countryRepository, + PostalCodeRepository $postalCodeRepository, + PaginatorFactory $paginatorFactory + ) { + $this->countryRepository = $countryRepository; + $this->postalCodeRepository = $postalCodeRepository; + $this->paginatorFactory = $paginatorFactory; + } + + /** + * @Route("/api/1.0/main/postal-code/search.json") + */ + public function search(Request $request): JsonResponse + { + $this->denyAccessUnlessGranted('ROLE_USER'); + + if (!$request->query->has('q')) { + throw new BadRequestHttpException('You must supply a "q" parameter'); + } + + $pattern = $request->query->get('q'); + + if ('' === trim($pattern)) { + throw new BadRequestHttpException('the search pattern is empty'); + } + + if ($request->query->has('country')) { + $country = $this->countryRepository->find($request->query->getInt('country')); + + if (null === $country) { + throw new NotFoundHttpException('country not found'); + } + } else { + $country = null; + } + + $nb = $this->postalCodeRepository->countByPattern($pattern, $country); + $paginator = $this->paginatorFactory->create($nb); + $codes = $this->postalCodeRepository->findByPattern( + $pattern, + $country, + $paginator->getCurrentPageFirstItemNumber(), + $paginator->getItemsPerPage() + ); + + return $this->json( + new Collection($codes, $paginator), + Response::HTTP_OK, + [], + [AbstractNormalizer::GROUPS => ['read']] + ); + } + protected function customizeQuery(string $action, Request $request, $qb): void { if ($request->query->has('country')) { diff --git a/src/Bundle/ChillMainBundle/Entity/AddressReference.php b/src/Bundle/ChillMainBundle/Entity/AddressReference.php index e793167f6..99efd391d 100644 --- a/src/Bundle/ChillMainBundle/Entity/AddressReference.php +++ b/src/Bundle/ChillMainBundle/Entity/AddressReference.php @@ -22,6 +22,15 @@ use Symfony\Component\Serializer\Annotation\Groups; */ class AddressReference { + /** + * This is an internal column which is populated by database. + * + * This column will ease the search operations + * + * @ORM\Column(type="text", options={"default": ""}) + */ + private string $addressCanonical = ''; + /** * @ORM\Id * @ORM\GeneratedValue diff --git a/src/Bundle/ChillMainBundle/Entity/PostalCode.php b/src/Bundle/ChillMainBundle/Entity/PostalCode.php index 866ad04db..484a9e322 100644 --- a/src/Bundle/ChillMainBundle/Entity/PostalCode.php +++ b/src/Bundle/ChillMainBundle/Entity/PostalCode.php @@ -29,6 +29,15 @@ use Symfony\Component\Serializer\Annotation\Groups; */ class PostalCode { + /** + * This is an internal column which is populated by database. + * + * This column will ease the search operations + * + * @ORM\Column(type="text", options={"default": ""}) + */ + private string $canonical = ''; + /** * @var Point * diff --git a/src/Bundle/ChillMainBundle/Repository/AddressReferenceRepository.php b/src/Bundle/ChillMainBundle/Repository/AddressReferenceRepository.php index 4344964af..6be52ec85 100644 --- a/src/Bundle/ChillMainBundle/Repository/AddressReferenceRepository.php +++ b/src/Bundle/ChillMainBundle/Repository/AddressReferenceRepository.php @@ -12,17 +12,29 @@ declare(strict_types=1); namespace Chill\MainBundle\Repository; use Chill\MainBundle\Entity\AddressReference; +use Chill\MainBundle\Entity\PostalCode; +use Chill\MainBundle\Search\SearchApiQuery; use Doctrine\ORM\EntityManagerInterface; use Doctrine\ORM\EntityRepository; +use Doctrine\ORM\Query\ResultSetMapping; +use Doctrine\ORM\Query\ResultSetMappingBuilder; use Doctrine\Persistence\ObjectRepository; +use RuntimeException; +use function explode; +use function implode; +use function strtr; +use function trim; final class AddressReferenceRepository implements ObjectRepository { + private EntityManagerInterface $entityManager; + private EntityRepository $repository; public function __construct(EntityManagerInterface $entityManager) { $this->repository = $entityManager->getRepository(AddressReference::class); + $this->entityManager = $entityManager; } public function countAll(): int @@ -33,6 +45,18 @@ final class AddressReferenceRepository implements ObjectRepository return $qb->getQuery()->getSingleScalarResult(); } + public function countByPostalCodePattern(PostalCode $postalCode, string $pattern): int + { + $query = $this->buildQueryByPostalCodePattern($postalCode, $pattern); + $sql = $query->buildQuery(true); + $rsm = new ResultSetMapping(); + $rsm->addScalarResult('c', 'c'); + + $nq = $this->entityManager->createNativeQuery($sql, $rsm)->setParameters($query->buildParameters(true)); + + return (int) $nq->getSingleResult()['c']; + } + public function find($id, $lockMode = null, $lockVersion = null): ?AddressReference { return $this->repository->find($id, $lockMode, $lockVersion); @@ -57,6 +81,33 @@ final class AddressReferenceRepository implements ObjectRepository return $this->repository->findBy($criteria, $orderBy, $limit, $offset); } + /** + * @return AddressReference[]|array + */ + public function findByPostalCodePattern(PostalCode $postalCode, string $pattern, bool $simplify = false, int $start = 0, int $limit = 50): array + { + $query = $this->buildQueryByPostalCodePattern($postalCode, $pattern); + + if (!$simplify) { + $rsm = new ResultSetMappingBuilder($this->entityManager); + $rsm->addRootEntityFromClassMetadata(AddressReference::class, 'cma'); + $query->addSelectClause($rsm->generateSelectClause()); + } else { + throw new RuntimeException('not implemented'); + } + + $sql = strtr( + $query->buildQuery() . 'ORDER BY pertinence DESC, lpad(streetnumber, 10, \'0\') ASC OFFSET ? LIMIT ? ', + // little hack for adding sql method to point + ['cma.point AS point' => 'ST_AsGeojson(cma.point) AS point'] + ); + $parameters = [...$query->buildParameters(), $start, $limit]; + + return $this->entityManager->createNativeQuery($sql, $rsm) + ->setParameters($parameters) + ->getResult(); + } + public function findOneBy(array $criteria, ?array $orderBy = null): ?AddressReference { return $this->repository->findOneBy($criteria, $orderBy); @@ -66,4 +117,44 @@ final class AddressReferenceRepository implements ObjectRepository { return AddressReference::class; } + + private function buildQueryByPostalCodePattern(PostalCode $postalCode, string $pattern): SearchApiQuery + { + $pattern = trim($pattern); + + if ('' === $pattern) { + throw new RuntimeException('the search pattern must not be empty'); + } + $query = new SearchApiQuery(); + + $query + ->setFromClause('chill_main_address_reference cma') + ->andWhereClause('postcode_id = ?', [$postalCode->getId()]); + + $pertinenceClause = ['STRICT_WORD_SIMILARITY(addresscanonical, UNACCENT(?))']; + $pertinenceArgs = [$pattern]; + $orWhere = ['addresscanonical %>> UNACCENT(?)']; + $orWhereArgs = [$pattern]; + + foreach (explode(' ', $pattern) as $part) { + $part = trim($part); + + if ('' === $part) { + continue; + } + + $orWhere[] = "addresscanonical LIKE '%' || UNACCENT(LOWER(?)) || '%'"; + $orWhereArgs[] = $part; + $pertinenceClause[] = + "(EXISTS (SELECT 1 FROM unnest(string_to_array(addresscanonical, ' ')) AS t WHERE starts_with(t, UNACCENT(LOWER(?)))))::int"; + $pertinenceClause[] = + '(addresscanonical LIKE UNACCENT(LOWER(?)))::int'; + array_push($pertinenceArgs, $part, $part); + } + $query + ->setSelectPertinence(implode(' + ', $pertinenceClause), $pertinenceArgs) + ->andWhereClause(implode(' OR ', $orWhere), $orWhereArgs); + + return $query; + } } diff --git a/src/Bundle/ChillMainBundle/Repository/PostalCodeRepository.php b/src/Bundle/ChillMainBundle/Repository/PostalCodeRepository.php index 697c8f90d..02e63771b 100644 --- a/src/Bundle/ChillMainBundle/Repository/PostalCodeRepository.php +++ b/src/Bundle/ChillMainBundle/Repository/PostalCodeRepository.php @@ -11,18 +11,39 @@ declare(strict_types=1); namespace Chill\MainBundle\Repository; +use Chill\MainBundle\Entity\Country; use Chill\MainBundle\Entity\PostalCode; +use Chill\MainBundle\Search\SearchApiQuery; use Doctrine\ORM\EntityManagerInterface; use Doctrine\ORM\EntityRepository; +use Doctrine\ORM\Query\ResultSetMapping; +use Doctrine\ORM\Query\ResultSetMappingBuilder; use Doctrine\Persistence\ObjectRepository; +use RuntimeException; final class PostalCodeRepository implements ObjectRepository { + private EntityManagerInterface $entityManager; + private EntityRepository $repository; public function __construct(EntityManagerInterface $entityManager) { $this->repository = $entityManager->getRepository(PostalCode::class); + $this->entityManager = $entityManager; + } + + public function countByPattern(string $pattern, ?Country $country): int + { + $query = $this->buildQueryByPattern($pattern, $country); + $sql = $query->buildQuery(true); + $rsm = new ResultSetMapping(); + $rsm->addScalarResult('c', 'c'); + + $nq = $this->entityManager->createNativeQuery($sql, $rsm) + ->setParameters($query->buildParameters(true)); + + return (int) $nq->getSingleResult()['c']; } public function find($id, $lockMode = null, $lockVersion = null): ?PostalCode @@ -49,6 +70,26 @@ final class PostalCodeRepository implements ObjectRepository return $this->repository->findBy($criteria, $orderBy, $limit, $offset); } + public function findByPattern(string $pattern, ?Country $country, ?int $start = 0, ?int $limit = 50): array + { + $query = $this->buildQueryByPattern($pattern, $country); + + $rsm = new ResultSetMappingBuilder($this->entityManager); + $rsm->addRootEntityFromClassMetadata(PostalCode::class, 'cmpc'); + $query->addSelectClause($rsm->generateSelectClause()); + + $sql = strtr( + $query->buildQuery() . 'ORDER BY pertinence DESC, canonical ASC OFFSET ? LIMIT ? ', + // little hack for adding sql method to point + ['cmpc.center AS center' => 'ST_AsGeojson(cmpc.center) AS center'] + ); + $parameters = [...$query->buildParameters(), $start, $limit]; + + return $this->entityManager->createNativeQuery($sql, $rsm) + ->setParameters($parameters) + ->getResult(); + } + public function findOneBy(array $criteria, ?array $orderBy = null): ?PostalCode { return $this->repository->findOneBy($criteria, $orderBy); @@ -58,4 +99,48 @@ final class PostalCodeRepository implements ObjectRepository { return PostalCode::class; } + + private function buildQueryByPattern(string $pattern, ?Country $country): SearchApiQuery + { + $pattern = trim($pattern); + + if ('' === $pattern) { + throw new RuntimeException('the search pattern must not be empty'); + } + $query = new SearchApiQuery(); + + $query + ->setFromClause('chill_main_postal_code cmpc') + ->andWhereClause('cmpc.origin = 0'); + + if (null !== $country) { + $query->andWhereClause('cmpc.country_id = ?', [$country->getId()]); + } + + $pertinenceClause = ['STRICT_WORD_SIMILARITY(canonical, UNACCENT(?))']; + $pertinenceArgs = [$pattern]; + $orWhere = ['canonical %>> UNACCENT(?)']; + $orWhereArgs = [$pattern]; + + foreach (explode(' ', $pattern) as $part) { + $part = trim($part); + + if ('' === $part) { + continue; + } + + $orWhere[] = "canonical LIKE '%' || UNACCENT(LOWER(?)) || '%'"; + $orWhereArgs[] = $part; + $pertinenceClause[] = + "(EXISTS (SELECT 1 FROM unnest(string_to_array(canonical, ' ')) AS t WHERE starts_with(t, UNACCENT(LOWER(?)))))::int"; + $pertinenceClause[] = + '(canonical LIKE UNACCENT(LOWER(?)))::int'; + array_push($pertinenceArgs, $part, $part); + } + $query + ->setSelectPertinence(implode(' + ', $pertinenceClause), $pertinenceArgs) + ->andWhereClause(implode(' OR ', $orWhere), $orWhereArgs); + + return $query; + } } diff --git a/src/Bundle/ChillMainBundle/Resources/public/vuejs/Address/api.js b/src/Bundle/ChillMainBundle/Resources/public/vuejs/Address/api.js index 62065b3ad..1dbc85dee 100644 --- a/src/Bundle/ChillMainBundle/Resources/public/vuejs/Address/api.js +++ b/src/Bundle/ChillMainBundle/Resources/public/vuejs/Address/api.js @@ -16,7 +16,8 @@ const fetchCountries = () => { /** * Endpoint chill_api_single_postal_code__index -* method GET, get Country Object +* method GET, get Cities Object +* @params {object} a country object * @returns {Promise} a promise containing all Postal Code objects filtered with country */ const fetchCities = (country) => { @@ -29,6 +30,40 @@ const fetchCities = (country) => { }); }; + +/** +* Endpoint chill_main_postalcodeapi_search +* method GET, get Cities Object +* @params {string} search a search string +* @params {object} country a country object +* @returns {Promise} a promise containing all Postal Code objects filtered with country and a search string +*/ +const searchCities = (search, country) => { + const url = `/api/1.0/main/postal-code/search.json?q=${search}&country=${country.id}`; + return fetch(url) + .then(response => { + if (response.ok) { return response.json(); } + throw Error('Error with request resource response'); + }); +}; + +/** +* Endpoint chill_main_addressreferenceapi_search +* method GET, get AddressReference Object +* @params {string} search a search string +* @params {object} postalCode a postalCode object +* @returns {Promise} a promise containing all Postal Code objects filtered with country and a search string +*/ +const searchReferenceAddresses = (search, postalCode) => { + const url = `/api/1.0/main/address-reference/by-postal-code/${postalCode.id}/search.json?q=${search}`; + return fetch(url) + .then(response => { + if (response.ok) { return response.json(); } + throw Error('Error with request resource response'); + }); +}; + + /** * Endpoint chill_api_single_address_reference__index * method GET, get AddressReference Object @@ -170,5 +205,7 @@ export { postAddress, patchAddress, postPostalCode, - getAddress + getAddress, + searchCities, + searchReferenceAddresses }; diff --git a/src/Bundle/ChillMainBundle/Resources/public/vuejs/Address/components/AddAddress.vue b/src/Bundle/ChillMainBundle/Resources/public/vuejs/Address/components/AddAddress.vue index 87ecff7fb..ed1d14633 100644 --- a/src/Bundle/ChillMainBundle/Resources/public/vuejs/Address/components/AddAddress.vue +++ b/src/Bundle/ChillMainBundle/Resources/public/vuejs/Address/components/AddAddress.vue @@ -556,8 +556,8 @@ export default { this.entity.selected.address.distribution = this.context.edit ? this.entity.address.distribution: null; this.entity.selected.address.extra = this.context.edit ? this.entity.address.extra: null; - this.entity.selected.writeNew.address = this.context.edit; - this.entity.selected.writeNew.postcode = this.context.edit; + this.entity.selected.writeNew.address = this.context.edit && this.entity.address.addressReference === null && this.entity.address.street.length > 0 + this.entity.selected.writeNew.postcode = false // NB: this used to be this.context.edit, but think it was erroneous; console.log('!! just set writeNew.postcode to', this.entity.selected.writeNew.postcode); }, @@ -569,7 +569,6 @@ export default { applyChanges() { console.log('apply changes'); - let newAddress = { 'isNoAddress': this.entity.selected.isNoAddress, 'street': this.entity.selected.isNoAddress ? '' : this.entity.selected.address.street, @@ -633,7 +632,6 @@ export default { if (!this.context.edit) { this.addNewAddress(newAddress) .then(payload => this.addressChangedCallback(payload)); - } else { this.updateAddress({ addressId: this.context.addressId, @@ -697,8 +695,7 @@ export default { * Async PATCH transactions, * then update existing address with backend datas when promise is resolved */ - updateAddress(payload) - { + updateAddress(payload) { this.flag.loading = true; // TODO change the condition because it writes new postal code in edit mode now: !writeNewPostalCode diff --git a/src/Bundle/ChillMainBundle/Resources/public/vuejs/Address/components/AddAddress/AddressSelection.vue b/src/Bundle/ChillMainBundle/Resources/public/vuejs/Address/components/AddAddress/AddressSelection.vue index 2409dca53..c333961db 100644 --- a/src/Bundle/ChillMainBundle/Resources/public/vuejs/Address/components/AddAddress/AddressSelection.vue +++ b/src/Bundle/ChillMainBundle/Resources/public/vuejs/Address/components/AddAddress/AddressSelection.vue @@ -18,6 +18,7 @@ :taggable="true" :multiple="false" @tag="addAddress" + :loading="isLoading" :options="addresses"> @@ -48,14 +49,17 @@ diff --git a/src/Bundle/ChillPersonBundle/Resources/public/vuejs/VisGraph/i18n.js b/src/Bundle/ChillPersonBundle/Resources/public/vuejs/VisGraph/i18n.js index c2ee09960..8047aea11 100644 --- a/src/Bundle/ChillPersonBundle/Resources/public/vuejs/VisGraph/i18n.js +++ b/src/Bundle/ChillPersonBundle/Resources/public/vuejs/VisGraph/i18n.js @@ -24,6 +24,7 @@ const visMessages = { refresh: "Rafraîchir", screenshot: "Prendre une photo", choose_relation: "Choisissez le lien de parenté", + relationship_household: "Filiation du ménage", }, edit: 'Éditer', del: 'Supprimer', diff --git a/src/Bundle/ChillPersonBundle/Resources/public/vuejs/VisGraph/index.js b/src/Bundle/ChillPersonBundle/Resources/public/vuejs/VisGraph/index.js index ca76f283b..5e8989e49 100644 --- a/src/Bundle/ChillPersonBundle/Resources/public/vuejs/VisGraph/index.js +++ b/src/Bundle/ChillPersonBundle/Resources/public/vuejs/VisGraph/index.js @@ -16,7 +16,12 @@ persons.forEach(person => { }) const app = createApp({ - template: `` + template: ``, + data() { + return { + household_id: JSON.parse(container.dataset.householdId) + } + } }) .use(store) .use(i18n) diff --git a/src/Bundle/ChillPersonBundle/Resources/public/vuejs/VisGraph/store.js b/src/Bundle/ChillPersonBundle/Resources/public/vuejs/VisGraph/store.js index 119a7b29c..10269c6c8 100644 --- a/src/Bundle/ChillPersonBundle/Resources/public/vuejs/VisGraph/store.js +++ b/src/Bundle/ChillPersonBundle/Resources/public/vuejs/VisGraph/store.js @@ -112,7 +112,7 @@ const store = createStore({ } }) //console.log('array', array.map(item => item.person.id)) - console.log('get persons group', group.map(f => f.id)) + //console.log('get persons group', group.map(f => f.id)) return group }, @@ -120,13 +120,17 @@ const store = createStore({ }, mutations: { addPerson(state, [person, options]) { + let age = getAge(person) + age = (age === '')? '' : ' - ' + age + let debug = '' /// Debug mode: uncomment to display person_id on visgraph //debug = `\nid ${person.id}` + person.group = person.type person._id = person.id person.id = `person_${person.id}` - person.label = `*${person.text}*\n_${getGender(person.gender)} - ${getAge(person.birthdate)}_${debug}` // + person.label = `*${person.text}*\n_${getGender(person.gender)}${age}_${debug}` person.folded = false // folded is used for missing persons if (options.folded) { @@ -161,7 +165,7 @@ const store = createStore({ state.links.push(link) }, updateLink(state, link) { - console.log('updateLink', link) + //console.log('updateLink', link) let link_ = { from: `person_${link.fromPerson.id}`, to: `person_${link.toPerson.id}`, @@ -264,7 +268,7 @@ const store = createStore({ fetchInfoForPerson({ dispatch }, person) { // TODO enfants hors ménages // example: household 61 - // console.log(person.text, 'household', person.current_household_id) + //console.log(person.text, 'household', person.current_household_id) if (null !== person.current_household_id) { dispatch('fetchHouseholdForPerson', person) } @@ -305,15 +309,16 @@ const store = createStore({ */ addLinkFromPersonsToHousehold({ commit, getters, dispatch }, household) { let members = getters.getMembersByHousehold(household.id) - console.log('add link for', members.length, 'members') + //console.log('add link for', members.length, 'members') members.forEach(m => { commit('addLink', { from: `${m.person.type}_${m.person.id}`, - to: `household_${m.person.current_household_id}`, - id: `household_${m.person.current_household_id}-person_${m.person.id}`, + to: `${household.id}`, + id: `${household.id}-person_${m.person.id}`, arrows: 'from', color: 'pink', font: { color: '#D04A60' }, + dashes: (getHouseholdWidth(m) === 1)? [0,4] : false, //edge style: [dash, gap, dash, gap] label: getHouseholdLabel(m), width: getHouseholdWidth(m), }) @@ -362,7 +367,7 @@ const store = createStore({ */ addLinkFromPersonsToCourse({ commit, getters, dispatch }, course) { const participations = getters.getParticipationsByCourse(course.id) - console.log('add link for', participations.length, 'participations') + //console.log('add link for', participations.length, 'participations') participations.forEach(p => { //console.log(p.person.id) commit('addLink', { @@ -445,7 +450,7 @@ const store = createStore({ * @param array */ addMissingPerson({ commit, getters, dispatch }, [person, parent]) { - console.log('! add missing Person', person.id) + //console.log('! add missing Person', person.id) commit('markPersonLoaded', person.id) commit('addPerson', [person, { folded: true }]) if (getters.isExcludedNode(parent.id)) { @@ -467,7 +472,7 @@ const store = createStore({ getters.getPersonsGroup(participations) .forEach(person => { if (person.folded === true) { - console.log('-=. unfold and expand person', person.id) + //console.log('-=. unfold and expand person', person.id) commit('unfoldPerson', person) dispatch('fetchInfoForPerson', person) } @@ -485,7 +490,7 @@ const store = createStore({ getters.getPersonsGroup(members) .forEach(person => { if (person.folded === true) { - console.log('-=. unfold and expand person', person.id) + //console.log('-=. unfold and expand person', person.id) commit('unfoldPerson', person) dispatch('fetchInfoForPerson', person) } diff --git a/src/Bundle/ChillPersonBundle/Resources/public/vuejs/VisGraph/vis-network.js b/src/Bundle/ChillPersonBundle/Resources/public/vuejs/VisGraph/vis-network.js index e95bc0d0b..3e00db883 100644 --- a/src/Bundle/ChillPersonBundle/Resources/public/vuejs/VisGraph/vis-network.js +++ b/src/Bundle/ChillPersonBundle/Resources/public/vuejs/VisGraph/vis-network.js @@ -13,13 +13,12 @@ window.options = { locale: 'fr', locales: visMessages, /* + */ configure: { enabled: true, - filter: 'nodes,edges', - //container: undefined, + filter: 'physics', showButton: true }, - */ physics: { enabled: true, barnesHut: { @@ -37,8 +36,8 @@ window.options = { centralGravity: 0.01, springLength: 100, springConstant: 0.08, - damping: 0.4, - avoidOverlap: 0 + damping: 0.75, + avoidOverlap: 0.00 }, repulsion: { centralGravity: 0.2, @@ -159,17 +158,21 @@ const getGender = (gender) => { } /** - * TODO Repeat getAge() in PersonRenderBox.vue - * @param birthdate + * TODO only one abstract function (-> getAge() is repeated in PersonRenderBox.vue) + * @param person * @returns {string|null} */ -const getAge = (birthdate) => { - if (null === birthdate) { - return null +const getAge = (person) => { + if (person.birthdate) { + let birthdate = new Date(person.birthdate.datetime) + if (person.deathdate) { + let deathdate = new Date(person.deathdate.datetime) + return (deathdate.getFullYear() - birthdate.getFullYear()) + visMessages.fr.visgraph.years + } + let now = new Date() + return (now.getFullYear() - birthdate.getFullYear()) + visMessages.fr.visgraph.years } - const birthday = new Date(birthdate.datetime) - const now = new Date() - return (now.getFullYear() - birthday.getFullYear()) + ' '+ visMessages.fr.visgraph.years + return '' } /** diff --git a/src/Bundle/ChillPersonBundle/Resources/public/vuejs/_components/Entity/PersonRenderBox.vue b/src/Bundle/ChillPersonBundle/Resources/public/vuejs/_components/Entity/PersonRenderBox.vue index ffb9ef3d4..ab7074ffc 100644 --- a/src/Bundle/ChillPersonBundle/Resources/public/vuejs/_components/Entity/PersonRenderBox.vue +++ b/src/Bundle/ChillPersonBundle/Resources/public/vuejs/_components/Entity/PersonRenderBox.vue @@ -192,6 +192,7 @@ export default { 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() diff --git a/src/Bundle/ChillPersonBundle/Resources/views/Household/relationship.html.twig b/src/Bundle/ChillPersonBundle/Resources/views/Household/relationship.html.twig index 56fcce85c..acaeebb96 100644 --- a/src/Bundle/ChillPersonBundle/Resources/views/Household/relationship.html.twig +++ b/src/Bundle/ChillPersonBundle/Resources/views/Household/relationship.html.twig @@ -17,7 +17,8 @@
+ data-persons="{{ persons|e('html_attr') }}" + data-household-id="{{ household.id|e('html_attr') }}">
{% endblock %} From 07f53e67586a291bc2240ae77ca5e020ed02527f Mon Sep 17 00:00:00 2001 From: LenaertsJ Date: Mon, 6 Dec 2021 14:07:21 +0000 Subject: [PATCH 7/8] titulaire field removed from household edit form --- CHANGELOG.md | 1 + .../ChillPersonBundle/Form/HouseholdMemberType.php | 12 ------------ 2 files changed, 1 insertion(+), 12 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index abc7a219b..ba2c90409 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -15,6 +15,7 @@ and this project adheres to * [main] address: in edit mode, select the encoded values in multiselect for address reference and city (https://gitlab.com/champs-libres/departement-de-la-vendee/chill/-/issues/316) * [person search] fix bug when using birthdate after and birthdate before * [person search] increase pertinence when lastname begins with search pattern +* [household] field to edit wheter person is titulaire of household or not removed (https://gitlab.com/champs-libres/departement-de-la-vendee/chill/-/issues/322) * [activity] create work if a work with same social action is not associated to the activity * [visgraph] improve and fix bugs on vis-network relationship graph * [bugfix] posting of birth- and deathdate through api fixed. diff --git a/src/Bundle/ChillPersonBundle/Form/HouseholdMemberType.php b/src/Bundle/ChillPersonBundle/Form/HouseholdMemberType.php index a5284afdb..e0d2d9d47 100644 --- a/src/Bundle/ChillPersonBundle/Form/HouseholdMemberType.php +++ b/src/Bundle/ChillPersonBundle/Form/HouseholdMemberType.php @@ -14,7 +14,6 @@ namespace Chill\PersonBundle\Form; use Chill\MainBundle\Form\Type\ChillDateType; use Chill\MainBundle\Form\Type\ChillTextareaType; use Symfony\Component\Form\AbstractType; -use Symfony\Component\Form\Extension\Core\Type\ChoiceType; use Symfony\Component\Form\FormBuilderInterface; class HouseholdMemberType extends AbstractType @@ -26,17 +25,6 @@ class HouseholdMemberType extends AbstractType 'label' => 'household.Start date', 'input' => 'datetime_immutable', ]); - - 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', From 2d1e8b2acde74475d4adb09393598d713ee094f0 Mon Sep 17 00:00:00 2001 From: Mathieu Jaumotte Date: Mon, 6 Dec 2021 14:45:44 +0000 Subject: [PATCH 8/8] =?UTF-8?q?Coh=C3=A9rence=20graphique=20des=20listes?= =?UTF-8?q?=20d'activit=C3=A9s=20et=20d'actions=20dans=20le=20contexte=20d?= =?UTF-8?q?'un=20parcours?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- CHANGELOG.md | 6 +- .../Resources/public/chill/chillactivity.scss | 19 +- .../Activity/activity-badge-title.html.twig | 110 --------- .../views/Activity/concernedGroups.html.twig | 2 +- .../Resources/views/Activity/list.html.twig | 177 ++++++++++++--- .../Activity/listAccompanyingCourse.html.twig | 4 +- .../views/Activity/list_recent.html.twig | 84 ++++++- .../translations/messages.fr.yml | 2 + .../public/chill/scss/flex_table.scss | 14 ++ .../chill/scss/accompanying_period_work.scss | 49 +--- .../Resources/public/chill/scss/badge.scss | 129 ++++++----- .../public/chill/scss/flex_table.scss | 6 - .../Resources/public/chill/scss/mixins.scss | 5 +- .../views/AccompanyingCourse/index.html.twig | 4 +- .../AccompanyingCourseWork/delete.html.twig | 2 +- .../AccompanyingCourseWork/index.html.twig | 212 +++++++++++++++++- .../list_by_accompanying_period.html.twig | 118 ---------- ...st_recent_by_accompanying_period.html.twig | 13 +- .../translations/messages.fr.yml | 5 + 19 files changed, 561 insertions(+), 400 deletions(-) delete mode 100644 src/Bundle/ChillActivityBundle/Resources/views/Activity/activity-badge-title.html.twig delete mode 100644 src/Bundle/ChillPersonBundle/Resources/views/AccompanyingCourseWork/list_by_accompanying_period.html.twig diff --git a/CHANGELOG.md b/CHANGELOG.md index ba2c90409..65b9864c4 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -15,6 +15,10 @@ and this project adheres to * [main] address: in edit mode, select the encoded values in multiselect for address reference and city (https://gitlab.com/champs-libres/departement-de-la-vendee/chill/-/issues/316) * [person search] fix bug when using birthdate after and birthdate before * [person search] increase pertinence when lastname begins with search pattern +* [activity/actions] Améliore la cohérence du design entre + * la page résumé d'un parcours (liste d'actions récentes et liste d'activités récentes) + * la page liste des actions + * la page liste des activités (contexte personne / contexte parcours) * [household] field to edit wheter person is titulaire of household or not removed (https://gitlab.com/champs-libres/departement-de-la-vendee/chill/-/issues/322) * [activity] create work if a work with same social action is not associated to the activity * [visgraph] improve and fix bugs on vis-network relationship graph @@ -174,7 +178,7 @@ and this project adheres to * fast creation buttons * add ordering for types -* [AccompanyingCourse Resume page] badge-title for AccompanyingCourseWork and for Activities; +* [AccompanyingCourse Resume page] dashboard for AccompanyingCourseWork and for Activities; * Improve badges behaviour with small screens; * [ThirdParty]: diff --git a/src/Bundle/ChillActivityBundle/Resources/public/chill/chillactivity.scss b/src/Bundle/ChillActivityBundle/Resources/public/chill/chillactivity.scss index 28c02e23e..275f67950 100644 --- a/src/Bundle/ChillActivityBundle/Resources/public/chill/chillactivity.scss +++ b/src/Bundle/ChillActivityBundle/Resources/public/chill/chillactivity.scss @@ -24,14 +24,16 @@ div.new-activity-select-type { } //// ACTIVITY LIST PAGE -// precise badge-title specific details +// precise dashboard specific details +p.date-label { + display: inline-block; + margin: 0 0.5em 0 0; + font-weight: 700; + font-size: 18pt; +} +div.dashboard, h2.badge-title { - div.duration { - font-size: smaller; - padding-left: 1em; - margin-top: 1em; - } ul.list-content { font-size: 70%; list-style-type: none; @@ -39,16 +41,13 @@ h2.badge-title { margin: 0; li { margin-bottom: 0.2em; - // exception: change bg color for action badges above badge-title + // exception: change bg color for action badges above dashboard .bg-light { background-color: $chill-light-gray !important; } } } } -div.main { - padding: 1em; -} //// ACTIVITY SHOW AND FORM PAGES // Exceptions for flex-bloc in concerned-groups diff --git a/src/Bundle/ChillActivityBundle/Resources/views/Activity/activity-badge-title.html.twig b/src/Bundle/ChillActivityBundle/Resources/views/Activity/activity-badge-title.html.twig deleted file mode 100644 index f51854163..000000000 --- a/src/Bundle/ChillActivityBundle/Resources/views/Activity/activity-badge-title.html.twig +++ /dev/null @@ -1,110 +0,0 @@ -

- - - {% if activity.date %} -

{{ activity.date|format_date('short') }}

- {% endif %} - -
- {% if activity.durationTime and t.durationTimeVisible %} -

- - {{ activity.durationTime|date('H:i') }} -

- {% endif %} - - {% if activity.travelTime and t.travelTimeVisible %} -

- - {{ activity.travelTime|date('H:i') }} -

- {% endif %} -
- -
- - - {{ activity.type.name | localize_translatable_string }} - - {% if activity.emergency %} - {{ 'Emergency'|trans|upper }} - {% endif %} - -
    - - {% if activity.sentReceived is not empty and t.sentReceivedVisible %} -
  • - {{ 'Sent received'|trans ~ ' : ' }} - {{ activity.sentReceived|capitalize|trans }} -
  • - {% endif %} - - {% if activity.location and t.locationVisible %} -
  • - {{ 'location'|trans ~ ': ' }} - - {{ activity.location.locationType.title|localize_translatable_string }} - {{ activity.location.name }} - -
  • - {% endif %} - - {% if activity.user and t.userVisible %} -
  • - {{ 'Referrer'|trans ~ ': ' }} - {{ activity.user.usernameCanonical }} -
  • - {% endif %} - -
  • - {{ 'Participants'|trans ~ ' : ' }} - {% for p in activity.personsAssociated %} - {{ p|chill_entity_render_box }} - {% endfor %} -
  • -
- -
    - {%- if t.reasonsVisible -%} - {%- if activity.reasons is not empty -%} -
  • - {% for r in activity.reasons %} - {{ r|chill_entity_render_box }} - {% endfor %} -
  • - {%- endif -%} - {% endif %} - {%- if t.socialIssuesVisible %} - {%- if activity.socialIssues is not empty -%} - - {%- endif -%} - {% endif %} - {%- if t.socialActionsVisible -%} - {%- if activity.socialActions is not empty -%} - - {%- endif -%} - {% endif %} -
-
-

- -{% if context == 'person' and activity.accompanyingPeriod is not empty %} -
- - -
-{% endif %} - diff --git a/src/Bundle/ChillActivityBundle/Resources/views/Activity/concernedGroups.html.twig b/src/Bundle/ChillActivityBundle/Resources/views/Activity/concernedGroups.html.twig index f6b052265..2808cca60 100644 --- a/src/Bundle/ChillActivityBundle/Resources/views/Activity/concernedGroups.html.twig +++ b/src/Bundle/ChillActivityBundle/Resources/views/Activity/concernedGroups.html.twig @@ -126,7 +126,7 @@
{% if bloc.items|length > 0 %}
-

{{ bloc.title }}

+

{{ bloc.title }}

{% for item in bloc.items %} diff --git a/src/Bundle/ChillActivityBundle/Resources/views/Activity/list.html.twig b/src/Bundle/ChillActivityBundle/Resources/views/Activity/list.html.twig index 7c75f5bf0..83d8cbcdb 100644 --- a/src/Bundle/ChillActivityBundle/Resources/views/Activity/list.html.twig +++ b/src/Bundle/ChillActivityBundle/Resources/views/Activity/list.html.twig @@ -10,49 +10,174 @@ {% for activity in activities %} {% set t = activity.type %}
+
- {% include '@ChillActivity/Activity/activity-badge-title.html.twig' %} +
+
+
+ + {% if activity.date %} +

+ {{ activity.date|format_date('short') }} +

+ {% endif %} + +
+
+ +

+ + + {{ activity.type.name | localize_translatable_string }} + + {% if activity.emergency %} + {{ 'Emergency'|trans|upper }} + {% endif %} + +

+ +
+
+
- {% if activity.comment.comment is not empty - or activity.persons|length > 0 - or activity.thirdParties|length > 0 - or activity.users|length > 0 - %} -
- {% if activity.comment.comment is not empty %} - {{ activity.comment|chill_entity_render_box({ - 'disable_markdown': false, - 'limit_lines': 3, - 'metadata': false, - }) }} - {% endif %} +
+
+ {% if activity.location and t.locationVisible %} +
+

{{ 'location'|trans }}

+
+

+ {{ activity.location.locationType.title|localize_translatable_string }} + {{ activity.location.name }} +

+
+
+ {% endif %} + + {% if activity.sentReceived is not empty and t.sentReceivedVisible %} +
+

{{ 'Sent received'|trans }}

+
+

+ {{ activity.sentReceived|capitalize|trans }} +

+
+
+ {% endif %} + + {% if activity.user and t.userVisible %} +
+

{{ 'Referrer'|trans }}

+
+

+ {{ activity.user.usernameCanonical|chill_entity_render_string|capitalize }} +

+
+
+ {% endif %} +
{% include 'ChillActivityBundle:Activity:concernedGroups.html.twig' with { 'context': context, - 'with_display': 'row', + 'with_display': 'wrap-list', 'entity': activity, 'badge_person': true } %} + +
+ {%- if activity.reasons is not empty and t.reasonsVisible -%} +
+
+

{{ 'Reasons'|trans }}

+
+
+ {% for r in activity.reasons %} +

+ {{ r|chill_entity_render_box }} +

+ {% endfor %} +
+
+ {% endif %} + + {%- if activity.socialIssues is not empty and t.socialIssuesVisible -%} +
+
+

{{ 'Social issues'|trans }}

+
+
+ {% for r in activity.socialIssues %} + + {% endfor %} +
+
+ {% endif %} + + {%- if activity.socialActions is not empty and t.socialActionsVisible -%} +
+
+

{{ 'Social actions'|trans }}

+
+
+ {% for r in activity.socialActions %} + + {% endfor %} +
+
+ {% endif %} + + {# SEULEMENT SI DÉTAILLÉ + {% if activity.comment.comment is not empty %} +
+
+

{{ 'Comment'|trans }}

+
+
+ {{ activity.comment|chill_entity_render_box({ + 'disable_markdown': false, + 'limit_lines': 3, + 'metadata': false + }) }} +
+
+ {% endif %} + #} +
- {% endif %}
-
diff --git a/src/Bundle/ChillActivityBundle/Resources/views/Activity/listAccompanyingCourse.html.twig b/src/Bundle/ChillActivityBundle/Resources/views/Activity/listAccompanyingCourse.html.twig index c5de72308..431a06ba6 100644 --- a/src/Bundle/ChillActivityBundle/Resources/views/Activity/listAccompanyingCourse.html.twig +++ b/src/Bundle/ChillActivityBundle/Resources/views/Activity/listAccompanyingCourse.html.twig @@ -23,8 +23,8 @@ {% if is_granted('CHILL_ACTIVITY_CREATE', accompanyingCourse) %} diff --git a/src/Bundle/ChillActivityBundle/Resources/views/Activity/list_recent.html.twig b/src/Bundle/ChillActivityBundle/Resources/views/Activity/list_recent.html.twig index 2829d8153..0f426c5a9 100644 --- a/src/Bundle/ChillActivityBundle/Resources/views/Activity/list_recent.html.twig +++ b/src/Bundle/ChillActivityBundle/Resources/views/Activity/list_recent.html.twig @@ -3,10 +3,88 @@ {% set t = activity.type %} + class="dashboard-link" title="{{ 'Show the activity'|trans }}"> - {% include '@ChillActivity/Activity/activity-badge-title.html.twig' %} +
+ + + + {%- if activity.date -%} +

{{ activity.date|format_date('short') }}

+ {%- endif -%} + + + + {% if activity.emergency %} + {{ 'Emergency'|trans|upper }} + {% endif %} + +
    + + {% if activity.sentReceived is not empty and t.sentReceivedVisible %} +
  • + {{ 'Sent received'|trans ~ ' : ' }} + {{ activity.sentReceived|capitalize|trans }} +
  • + {% endif %} + + {% if activity.location and t.locationVisible %} +
  • + {{ 'location'|trans ~ ': ' }} + + {{ activity.location.locationType.title|localize_translatable_string }} + {{ activity.location.name }} + +
  • + {% endif %} + + {% if activity.user and t.userVisible %} +
  • + {{ 'Referrer'|trans ~ ': ' }} + {{ activity.user.usernameCanonical }} +
  • + {% endif %} + +
  • + {{ 'Participants'|trans ~ ' : ' }} + {% for p in activity.personsAssociated %} + {{ p|chill_entity_render_box }} + {% endfor %} +
  • +
+ +
    + {%- if t.reasonsVisible -%} + {%- if activity.reasons is not empty -%} +
  • + {% for r in activity.reasons %} + {{ r|chill_entity_render_box }} + {% endfor %} +
  • + {%- endif -%} + {% endif %} + {%- if t.socialIssuesVisible %} + {%- if activity.socialIssues is not empty -%} + + {%- endif -%} + {% endif %} + {%- if t.socialActionsVisible -%} + {%- if activity.socialActions is not empty -%} + + {%- endif -%} + {% endif %} +
+ +
+
- {% endfor %}
diff --git a/src/Bundle/ChillActivityBundle/translations/messages.fr.yml b/src/Bundle/ChillActivityBundle/translations/messages.fr.yml index 098a6f6df..3de1cc600 100644 --- a/src/Bundle/ChillActivityBundle/translations/messages.fr.yml +++ b/src/Bundle/ChillActivityBundle/translations/messages.fr.yml @@ -222,3 +222,5 @@ Aggregate by activity type: Aggréger par type d'activité Aggregate by activity reason: Aggréger par sujet de l'activité Last activities: Les dernières activités + +See activity in accompanying course context: Voir l'activité dans le contexte du parcours d'accompagnement diff --git a/src/Bundle/ChillMainBundle/Resources/public/chill/scss/flex_table.scss b/src/Bundle/ChillMainBundle/Resources/public/chill/scss/flex_table.scss index 8e3a9121b..6b22302b2 100644 --- a/src/Bundle/ChillMainBundle/Resources/public/chill/scss/flex_table.scss +++ b/src/Bundle/ChillMainBundle/Resources/public/chill/scss/flex_table.scss @@ -101,6 +101,10 @@ div.flex-table { div.item-row { flex-direction: row; + &.column { // exception + flex-direction: column + } + div.item-col { &:first-child { flex-grow: 0; flex-shrink: 0; flex-basis: auto; @@ -160,6 +164,12 @@ div.wrap-list { & > * { padding-right: 1em; } + + h3, h4 { + font-weight: 700; + font-size: 100%; + font-family: 'Open Sans'; + } } div.wl-col.list { @@ -171,6 +181,10 @@ div.wrap-list { padding: 0em; display: inline-block; } + + blockquote.chill-user-quote { + margin: 0.7em 0; + } } } diff --git a/src/Bundle/ChillPersonBundle/Resources/public/chill/scss/accompanying_period_work.scss b/src/Bundle/ChillPersonBundle/Resources/public/chill/scss/accompanying_period_work.scss index d246b3c92..daaae9e28 100644 --- a/src/Bundle/ChillPersonBundle/Resources/public/chill/scss/accompanying_period_work.scss +++ b/src/Bundle/ChillPersonBundle/Resources/public/chill/scss/accompanying_period_work.scss @@ -1,51 +1,6 @@ /// AccompanyingCourse Work list Page div.accompanying_course_work-list { - div.timeline { - width: 100%; - ul { - display: flex; - align-items: center; - justify-content: center; - - padding: 0; - list-style-type: none; - - > li { - flex-grow: 1; flex-shrink: 1; flex-basis: auto; - - div { - display: flex; - flex-direction: column; - align-items: center; - - &.date { - margin-bottom: 1em; - } - &.label { - border-top: 3px solid $chill-green; - - &:before { - content: ''; - display: inline-block; - position: relative; - width: 15px; - height: 15px; - top: -9px; - - background-color: $white; - border-radius: 12px; - border: 2px solid $chill-green; - } - &.no-label:before { - display: none; - } - } - } - } - } - } - div.objective_results { width: 100%; display: grid; @@ -69,8 +24,10 @@ div.accompanying_course_work-list { //&:nth-child(even) { background-color: $chill-llight-gray; } &.without-objectives {} &.with-objectives {} + } - + div.objective_results, + div.evaluations { h4.title_label { display: block; margin: 0.4em 0; diff --git a/src/Bundle/ChillPersonBundle/Resources/public/chill/scss/badge.scss b/src/Bundle/ChillPersonBundle/Resources/public/chill/scss/badge.scss index 6ffe0ac9d..39dbb4e55 100644 --- a/src/Bundle/ChillPersonBundle/Resources/public/chill/scss/badge.scss +++ b/src/Bundle/ChillPersonBundle/Resources/public/chill/scss/badge.scss @@ -39,69 +39,15 @@ span.fa-holder { } /* -* BADGE_TITLE -* Display Title like a badge (with background-colored label) +* DASHBOARDS */ -h2.badge-title { - display: flex; - flex-direction: row; - width: 100%; - color: $dark; - - span.title_label { - border-radius: 0.35rem 0 0 0.35rem; - color: $white; - font-size: 80%; - padding: 0.5em; - padding-right: 0; - h3 { - margin-bottom: 0.5rem; - } - - //position: relative; - span { - display: none; - //position: absolute; - //top: 0; - //left: 0; - //transform: rotate(270deg); - //transform-origin: 0 0; - } - } - span.title_action { - flex-grow: 1; - margin: 0 0 0 auto; - border-radius: 0 0.35rem 0.35rem 0; - background-color: $chill-llight-gray; - padding: 0.2em 1em; - - ul.small_in_title { - margin: 0; - //margin-top: 0.5em; - font-size: 70%; - padding-left: 1rem; - &.evaluations { - @include list_marker_triangle($orange); - } - } - ul.columns { // XS:1 SM:2 MD:1 LG:2 XL:2 XXL:2 - @include media-breakpoint-only(sm) { - columns: 2; -webkit-columns: 2; -moz-columns: 2; - } - @include media-breakpoint-up(lg) { - columns: 2; -webkit-columns: 2; -moz-columns: 2; - } - } - } -} - -/// Theses links apply on badge as parent tag. +/// Theses links apply on dashboards as parent tag. /// They don't look like button, picto or simple text links -a.badge-link { +a.dashboard-link { color: unset; text-decoration: unset; - & > h2.badge-title { + & > div.dashboard { &:hover { //box-shadow: 0 0 7px 0 $chill-gray; //opacity: 0.8; @@ -114,21 +60,80 @@ a.badge-link { } } -/// badge_title in AccompanyingCourse Work list Page +div.dashboard { + font-weight: 700; + font-size: 1.5rem; + margin-bottom: 0.5rem; + line-height: 1.2; + span.like-h3 { + color: #334d5c; + } +} +div.dashboard, +h2.badge-title { + display: flex; + flex-direction: row; + width: 100%; + color: $dark; + span.title_label { + color: $white; + font-size: 80%; + padding: 0.5em; + padding-right: 0; + border-radius: 0.35rem 0 0 0.35rem; + h3 { + margin-bottom: 0.5rem; + } + } + span.title_action { + flex-grow: 1; + margin: 0 0 0 auto; + background-color: $chill-llight-gray; + padding: 0.2em 1em; + border-radius: 0 0.35rem 0.35rem 0; + + ul.small_in_title { + font-size: 70%; + } + } +} + +ul.small_in_title { + margin: 0; + //margin-top: 0.5em; + padding-left: 1rem; + &.evaluations { + @include list_marker_triangle($orange); + } +} +ul.columns { // XS:1 SM:2 MD:1 LG:2 XL:2 XXL:2 + @include media-breakpoint-only(sm) { + columns: 2; -webkit-columns: 2; -moz-columns: 2; + } + @include media-breakpoint-up(lg) { + columns: 2; -webkit-columns: 2; -moz-columns: 2; + } +} + + + +/// dashboard_like_badge in AccompanyingCourse Work list Page div.accompanying_course_work-list { + div.dashboard, h2.badge-title { span.title_label { // Calculate same color then border:groove background-color: shade-color($social-action-color, 34%); } span.title_action { - @include badge_title($social-action-color); + @include dashboard_like_badge($social-action-color); } } } -/// badge_title in Activities on resume page +/// dashboard_like_badge in Activities on resume page div.activity-list { + div.dashboard, h2.badge-title { span.title_label { // Calculate same color then border:groove @@ -138,7 +143,7 @@ div.activity-list { } } span.title_action { - @include badge_title($activity-color); + @include dashboard_like_badge($activity-color); } span.title_label { div.duration { diff --git a/src/Bundle/ChillPersonBundle/Resources/public/chill/scss/flex_table.scss b/src/Bundle/ChillPersonBundle/Resources/public/chill/scss/flex_table.scss index 0229f53b1..2383e43b1 100644 --- a/src/Bundle/ChillPersonBundle/Resources/public/chill/scss/flex_table.scss +++ b/src/Bundle/ChillPersonBundle/Resources/public/chill/scss/flex_table.scss @@ -18,12 +18,6 @@ div.accompanyingcourse-list { //&:nth-child(2) { flex-direction: row; } //&:last-child { flex-direction: column; } } - div.title h3 { - font-weight: 700; - font-size: 100%; - font-family: 'Open Sans'; - } - div.list {} } /// Search Page (list_with_period.html.twig) diff --git a/src/Bundle/ChillPersonBundle/Resources/public/chill/scss/mixins.scss b/src/Bundle/ChillPersonBundle/Resources/public/chill/scss/mixins.scss index 8a3d21ece..228f10178 100644 --- a/src/Bundle/ChillPersonBundle/Resources/public/chill/scss/mixins.scss +++ b/src/Bundle/ChillPersonBundle/Resources/public/chill/scss/mixins.scss @@ -27,11 +27,10 @@ } /// -/// Generic mixin for titles like badge -// define visual badge used in title area +/// Mixin for dashboards (with design like badge_social) /// -@mixin badge_title($color) { +@mixin dashboard_like_badge($color) { @include chill_badge($color); &:before { margin: 0 0.3em 0 -1.05em; diff --git a/src/Bundle/ChillPersonBundle/Resources/views/AccompanyingCourse/index.html.twig b/src/Bundle/ChillPersonBundle/Resources/views/AccompanyingCourse/index.html.twig index 96e2cb534..cdd0363b4 100644 --- a/src/Bundle/ChillPersonBundle/Resources/views/AccompanyingCourse/index.html.twig +++ b/src/Bundle/ChillPersonBundle/Resources/views/AccompanyingCourse/index.html.twig @@ -103,7 +103,7 @@
@@ -121,7 +121,7 @@ {% set accompanying_course_id = accompanyingCourse.id %} {% endif %} -

{{ 'Last activities' |trans }}

+

{{ 'Last activities' |trans }}

{% include 'ChillActivityBundle:Activity:list_recent.html.twig' with { 'context': 'accompanyingCourse', 'no_action': true } %}
{% endblock %} diff --git a/src/Bundle/ChillPersonBundle/Resources/views/AccompanyingCourseWork/delete.html.twig b/src/Bundle/ChillPersonBundle/Resources/views/AccompanyingCourseWork/delete.html.twig index c66e5aa3d..37a6812bb 100644 --- a/src/Bundle/ChillPersonBundle/Resources/views/AccompanyingCourseWork/delete.html.twig +++ b/src/Bundle/ChillPersonBundle/Resources/views/AccompanyingCourseWork/delete.html.twig @@ -8,7 +8,7 @@

- {{ 'accompanying_course_work.action'|trans }} + {{ work.socialAction|chill_entity_render_string }}

diff --git a/src/Bundle/ChillPersonBundle/Resources/views/AccompanyingCourseWork/index.html.twig b/src/Bundle/ChillPersonBundle/Resources/views/AccompanyingCourseWork/index.html.twig index 92b1919af..844b2d611 100644 --- a/src/Bundle/ChillPersonBundle/Resources/views/AccompanyingCourseWork/index.html.twig +++ b/src/Bundle/ChillPersonBundle/Resources/views/AccompanyingCourseWork/index.html.twig @@ -7,7 +7,217 @@

{{ block('title') }}

- {% include 'ChillPersonBundle:AccompanyingCourseWork:list_by_accompanying_period.html.twig' %} + {% if works|length == 0 %} +

{{ 'accompanying_course_work.Any work'|trans }} + +

+ + {% else %} +
+ {% for w in works %} +
+ +
+

+ + {{ w.socialAction|chill_entity_render_string }} + +
    +
  • + {{ 'accompanying_course_work.start_date'|trans ~ ' : ' }} + {{ w.startDate|format_date('short') }} +
  • + {% if w.endDate %} +
  • + {{ 'accompanying_course_work.end_date'|trans ~ ' : ' }} + {{ w.endDate|format_date('short') }} +
  • + {% endif %} +
+ +
+

+
+ +
+
+ + {% if w.createdBy %} +
+
+

{{ 'Referrer'|trans }}

+
+
+

+ {{ w.createdBy.usernameCanonical|chill_entity_render_string|capitalize }} +

+
+
+ {% endif %} + + {%- if w.persons -%} +
+
+

{{ 'Persons in accompanying course'|trans }}

+
+
+ {% for p in w.persons %} + + {{ p|chill_entity_render_box({ + 'render': 'raw', + 'addAltNames': false + }) }} + + {% endfor %} +
+
+ {% endif %} + + {%- if w.handlingThierParty -%} +
+
+

{{ 'Thirdparty handling'|trans }}

+
+
+ + {{ w.handlingThierParty|chill_entity_render_box({ + 'render': 'raw', + 'addAltNames': false + }) }} + +
+
+ {% endif %} + + {%- if w.socialAction.issue -%} +
+
+

{{ 'Social issue'|trans }}

+
+
+ +
+
+ {% endif %} + + {% if w.accompanyingPeriodWorkEvaluations|length > 0 %} +
+
+

{{ 'accompanying_course_work.evaluations'|trans }}

+
+
+
    + {% for e in w.accompanyingPeriodWorkEvaluations %} +
  • + {{ e.evaluation.title|localize_translatable_string }} + +
      +
    • + {{ 'accompanying_course_work.start_date'|trans ~ ' : ' }} + {{ e.startDate|format_date('short') }} +
    • + {% if e.endDate %} +
    • + {{ 'accompanying_course_work.end_date'|trans ~ ' : ' }} + {{ e.endDate|format_date('short') }} +
    • + {% endif %} + {% if e.maxDate %} +
    • + {{ 'accompanying_course_work.max_date'|trans ~ ' : ' }} + {{ e.maxDate|format_date('short') }} +
    • + {% endif %} + {% if e.warningInterval and e.warningInterval.d > 0 %} +
    • + {% set days = (e.warningInterval.d + e.warningInterval.m * 30) %} + {{ 'accompanying_course_work.warning_interval'|trans ~ ' : ' }} + {{ 'accompanying_course_work.%days% days before max_date'|trans({'%days%': days }) }} +
    • + {% endif %} +
    + +
  • + {% endfor %} +
+
+
+ {% endif %} + +
+
+ +
+ {# SEULEMENT SI DÉTAILLÉ + {% if w.results|length > 0 %} +
+
+

{{ 'accompanying_course_work.goal'|trans }}

+

{{ 'accompanying_course_work.results without objective'|trans }}

+
+
+

{{ 'accompanying_course_work.results'|trans }}

+
    + {% for r in w.results %} +
  • {{ r.title|localize_translatable_string }}
  • + {% endfor %} +
+
+
+ {% endif %} + {% if w.goals|length > 0 %} + {% for g in w.goals %} +
+
+

{{ 'accompanying_course_work.goal'|trans }}

+
  • {{ g.goal.title|localize_translatable_string }}
+
+
+

{{ 'accompanying_course_work.results'|trans }}

+ {% if g.results|length == 0 %} +

{{ 'accompanying_course_work.no_results'|trans }}

+ {% else %} +
    + {% for r in g.results %} +
  • {{ r.title|localize_translatable_string }}
  • + {% endfor %} +
+ {% endif %} +
+
+ {% endfor %} + {% endif %} + #} +
+ +
+
+ {{ 'Last updated by'|trans}} {{ w.updatedBy|chill_entity_render_box }},
+ {{ 'le ' ~ w.updatedAt|format_datetime('long', 'short') }} +
+
    +
  • + +
  • +
  • + +
  • +
+
+ +
+ {% endfor %} +
+ {% endif %}
  • diff --git a/src/Bundle/ChillPersonBundle/Resources/views/AccompanyingCourseWork/list_by_accompanying_period.html.twig b/src/Bundle/ChillPersonBundle/Resources/views/AccompanyingCourseWork/list_by_accompanying_period.html.twig deleted file mode 100644 index 797f1bcc8..000000000 --- a/src/Bundle/ChillPersonBundle/Resources/views/AccompanyingCourseWork/list_by_accompanying_period.html.twig +++ /dev/null @@ -1,118 +0,0 @@ - {% if works|length == 0 %} -

    {{ 'accompanying_course_work.Any work'|trans }} - {# TODO link #} -

    - {% endif %} - -
    - {% for w in works %} -
    -
    - -

    - {{ 'accompanying_course_work.action'|trans }} - {{ w.socialAction|chill_entity_render_string }} -

    - -
    -
    - -
    -
      -
    • -
      - {{ w.startDate|format_date('long') }} -
      -
      - {{ 'accompanying_course_work.start_date'|trans }} -
      -
    • - {% if w.endDate == null %} -
    • -
      -
    • - {% else %} -
    • -
      - {{ w.endDate|format_date('long') }} -
      -
      - {{ 'accompanying_course_work.end_date'|trans }} -
      -
    • - {% endif %} -
    -
    - -
    -
    - - {% if w.results|length > 0 and w.goals|length > 0 %} - - {% endif %} - {% if w.results|length > 0 %} -
    -
    -

    {{ 'accompanying_course_work.goal'|trans }}

    -

    {{ 'accompanying_course_work.results without objective'|trans }}

    -
    -
    -

    {{ 'accompanying_course_work.results'|trans }}

    -
      - {% for r in w.results %} -
    • {{ r.title|localize_translatable_string }}
    • - {% endfor %} -
    -
    -
    - {% endif %} - {% if w.goals|length > 0 %} - {% for g in w.goals %} -
    -
    -

    {{ 'accompanying_course_work.goal'|trans }}

    -
    • {{ g.goal.title|localize_translatable_string }}
    -
    -
    -

    {{ 'accompanying_course_work.results'|trans }}

    - {% if g.results|length == 0 %} -

    {{ 'accompanying_course_work.no_results'|trans }}

    - {% else %} -
      - {% for r in g.results %} -
    • {{ r.title|localize_translatable_string }}
    • - {% endfor %} -
    - {% endif %} -
    -
    - {% endfor %} - {% endif %} - -
    -
    - -
    - {{ 'Last updated by'|trans}} {{ w.updatedBy|chill_entity_render_box }},
    - {{ 'le ' ~ w.updatedAt|format_datetime('long', 'short') }} -
    - - -
    -
    - {% endfor %} -
    - - diff --git a/src/Bundle/ChillPersonBundle/Resources/views/AccompanyingCourseWork/list_recent_by_accompanying_period.html.twig b/src/Bundle/ChillPersonBundle/Resources/views/AccompanyingCourseWork/list_recent_by_accompanying_period.html.twig index f33718910..b5fcb6c40 100644 --- a/src/Bundle/ChillPersonBundle/Resources/views/AccompanyingCourseWork/list_recent_by_accompanying_period.html.twig +++ b/src/Bundle/ChillPersonBundle/Resources/views/AccompanyingCourseWork/list_recent_by_accompanying_period.html.twig @@ -2,14 +2,11 @@ {% for w in works | slice(0,5) %} + class="dashboard-link" title="{{ 'crud.social_action.title_link'|trans }}"> -

    - - {{ 'accompanying_course_work.action'|trans }} - - - {{ w.socialAction|chill_entity_render_string }} +
    + +
    • @@ -75,7 +72,7 @@
    -

    +
{# {{ dump(w) }} #} {% endfor %} diff --git a/src/Bundle/ChillPersonBundle/translations/messages.fr.yml b/src/Bundle/ChillPersonBundle/translations/messages.fr.yml index 46077fdf0..0898ec611 100644 --- a/src/Bundle/ChillPersonBundle/translations/messages.fr.yml +++ b/src/Bundle/ChillPersonBundle/translations/messages.fr.yml @@ -198,6 +198,7 @@ Resources: Interlocuteurs privilégiés Any requestor to this accompanying course: Aucun demandeur pour ce parcours Social actions: Actions d'accompagnement Last social actions: Les dernières actions d'accompagnement +Social issue: Problématique sociale Social issues: Problématiques sociales Last events on accompanying course: Dernières actions de suivi Edit & activate accompanying course: Modifier et valider @@ -429,10 +430,14 @@ accompanying_course_work: create_date: Date de création start_date: Date de début end_date: Date de fin + max_date: Date d'échéance + warning_interval: Rappel + '%days% days before max_date': "%days% jour(s) avant l'échéance" results without objective: Aucun objectif - motif - dispositif no_results: Aucun résultat - orientation results: Résultats - orientations goal: Objectif - motif - dispositif + evaluations: Évaluations Any work: Aucune action d'accompagnement remove: Supprimer une action d'accompagnement social_evaluation: Évaluation