From 8177a0fcce8fb7a3db6b5e8e9901c795393a2563 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Julien=20Fastr=C3=A9?= Date: Thu, 16 Mar 2023 18:43:12 +0100 Subject: [PATCH] Feature: Provide api endpoint for reviewing addresses Feature: show warning when address does not match the reference Feature: [address] do update the address from address reference when clicked inside address details --- .../Controller/AddressToReferenceMatcher.php | 15 --- .../AddressToReferenceMatcherController.php | 75 ++++++++++++ src/Bundle/ChillMainBundle/Entity/Address.php | 23 +++- .../Resources/public/lib/api/address.ts | 8 ++ .../Resources/public/lib/api/apiMethods.ts | 6 - .../public/module/address-details/index.ts | 14 ++- .../AddressDetails/AddressDetailsButton.vue | 42 +++++-- .../AddressDetails/AddressDetailsContent.vue | 11 ++ .../AddressDetails/AddressModal.vue | 13 +- .../Parts/AddressDetailsRefMatching.vue | 88 ++++++++++++++ .../Resources/views/Entity/address.html.twig | 8 +- ...ddressToReferenceMatcherControllerTest.php | 114 ++++++++++++++++++ 12 files changed, 374 insertions(+), 43 deletions(-) delete mode 100644 src/Bundle/ChillMainBundle/Controller/AddressToReferenceMatcher.php create mode 100644 src/Bundle/ChillMainBundle/Controller/AddressToReferenceMatcherController.php create mode 100644 src/Bundle/ChillMainBundle/Resources/public/vuejs/_components/AddressDetails/Parts/AddressDetailsRefMatching.vue create mode 100644 src/Bundle/ChillMainBundle/Tests/Controller/AddressToReferenceMatcherControllerTest.php diff --git a/src/Bundle/ChillMainBundle/Controller/AddressToReferenceMatcher.php b/src/Bundle/ChillMainBundle/Controller/AddressToReferenceMatcher.php deleted file mode 100644 index 2fbd6b0ec..000000000 --- a/src/Bundle/ChillMainBundle/Controller/AddressToReferenceMatcher.php +++ /dev/null @@ -1,15 +0,0 @@ -security = $security; + $this->entityManager = $entityManager; + $this->serializer = $serializer; + } + + /** + * @Route("/api/1.0/main/address/reference-match/{id}/set/reviewed", methods={"POST"}) + */ + public function markAddressAsReviewed(Address $address): JsonResponse + { + if (!$this->security->isGranted('ROLE_USER')) { + throw new AccessDeniedHttpException(); + } + + $address->setRefStatus(Address::ADDR_REFERENCE_STATUS_REVIEWED); + + $this->entityManager->flush(); + + return new JsonResponse( + $this->serializer->serialize($address, 'json', [AbstractNormalizer::GROUPS => ['read']]), + JsonResponse::HTTP_OK, + [], + true + ); + } + + /** + * @Route("/api/1.0/main/address/reference-match/{id}/sync-with-reference", methods={"POST"}) + */ + public function syncAddressWithReference(Address $address): JsonResponse + { + if (null === $address->getAddressReference()) { + throw new BadRequestHttpException('this address does not have any address reference'); + } + + $address->syncWithReference($address->getAddressReference()); + + $this->entityManager->flush(); + + return new JsonResponse( + $this->serializer->serialize($address, 'json', [AbstractNormalizer::GROUPS => ['read']]), + JsonResponse::HTTP_OK, + [], + true + ); + } + +} diff --git a/src/Bundle/ChillMainBundle/Entity/Address.php b/src/Bundle/ChillMainBundle/Entity/Address.php index 23b0f971c..2f847450e 100644 --- a/src/Bundle/ChillMainBundle/Entity/Address.php +++ b/src/Bundle/ChillMainBundle/Entity/Address.php @@ -250,11 +250,20 @@ class Address implements TrackCreationInterface, TrackUpdateInterface public static function createFromAddressReference(AddressReference $original): Address { return (new Address()) - ->setPoint($original->getPoint()) - ->setPostcode($original->getPostcode()) - ->setStreet($original->getStreet()) - ->setStreetNumber($original->getStreetNumber()) - ->setAddressReference($original); + ->syncWithReference($original); + } + + public function syncWithReference(AddressReference $addressReference): Address + { + $this + ->setPoint($addressReference->getPoint()) + ->setPostcode($addressReference->getPostcode()) + ->setStreet($addressReference->getStreet()) + ->setStreetNumber($addressReference->getStreetNumber()) + ->setRefStatus(self::ADDR_REFERENCE_STATUS_MATCH) + ->setAddressReference($addressReference); + + return $this; } public function getAddressReference(): ?AddressReference @@ -514,8 +523,12 @@ class Address implements TrackCreationInterface, TrackUpdateInterface /** * Update the ref status * +<<<<<<< HEAD * @param Address::ADDR_REFERENCE_STATUS_* $refStatus * @param bool|null $updateLastUpdate Also update the "refStatusLastUpdate" +======= + * The refstatuslast update is also updated +>>>>>>> 31152616d (Feature: Provide api endpoint for reviewing addresses) */ public function setRefStatus(string $refStatus, ?bool $updateLastUpdate = true): self { diff --git a/src/Bundle/ChillMainBundle/Resources/public/lib/api/address.ts b/src/Bundle/ChillMainBundle/Resources/public/lib/api/address.ts index 1f0d6ae8a..66e90a194 100644 --- a/src/Bundle/ChillMainBundle/Resources/public/lib/api/address.ts +++ b/src/Bundle/ChillMainBundle/Resources/public/lib/api/address.ts @@ -21,3 +21,11 @@ export const getGeographicalUnitsByAddress = async (address: Address): Promise => { return fetchResults(`/api/1.0/main/geographical-unit-layer.json`); } + +export const syncAddressWithReference = async (address: Address): Promise
=> { + return makeFetch("POST", `/api/1.0/main/address/reference-match/${address.address_id}/sync-with-reference`); +} + +export const markAddressReviewed = async (address: Address): Promise
=> { + return makeFetch("POST", `/api/1.0/main/address/reference-match/${address.address_id}/set/reviewed`); +} diff --git a/src/Bundle/ChillMainBundle/Resources/public/lib/api/apiMethods.ts b/src/Bundle/ChillMainBundle/Resources/public/lib/api/apiMethods.ts index 0ef659128..17ac8879e 100644 --- a/src/Bundle/ChillMainBundle/Resources/public/lib/api/apiMethods.ts +++ b/src/Bundle/ChillMainBundle/Resources/public/lib/api/apiMethods.ts @@ -67,9 +67,6 @@ export const makeFetch = (method: 'POST'|'GET'|'PUT'|'PATCH'|'DEL }, }; - console.log('for url '+url, body); - console.log('for url '+url, body !== null); - if (body !== null && typeof body !== 'undefined') { Object.assign(opts, {body: JSON.stringify(body)}) } @@ -77,9 +74,6 @@ export const makeFetch = (method: 'POST'|'GET'|'PUT'|'PATCH'|'DEL if (typeof options !== 'undefined') { opts = Object.assign(opts, options); } - console.log('will fetch', url); - console.log('content for ' + url, opts); - return fetch(url, opts) .then(response => { if (response.ok) { diff --git a/src/Bundle/ChillMainBundle/Resources/public/module/address-details/index.ts b/src/Bundle/ChillMainBundle/Resources/public/module/address-details/index.ts index aa843bca8..4f74c6205 100644 --- a/src/Bundle/ChillMainBundle/Resources/public/module/address-details/index.ts +++ b/src/Bundle/ChillMainBundle/Resources/public/module/address-details/index.ts @@ -2,12 +2,14 @@ import AddressDetailsButton from "../../vuejs/_components/AddressDetails/Address import {createApp} from "vue"; import {createI18n} from "vue-i18n"; import {_createI18n} from "../../vuejs/_js/i18n"; +import {Address} from "../../types"; const i18n = _createI18n({}); document.querySelectorAll('span[data-address-details]').forEach((el) => { const dataset = el.dataset as { - addressId: string + addressId: string, + addressRefStatus: string, }; const app = createApp({ @@ -15,9 +17,17 @@ document.querySelectorAll('span[data-address-details]').forEach data() { return { addressId: Number.parseInt(dataset.addressId), + addressRefStatus: dataset.addressRefStatus, } }, - template: '' + template: '', + methods: { + onUpdateAddress: (address: Address): void => { + if (window.confirm("L'adresse a été modifiée. Vous pouvez continuer votre travail. Cependant, pour afficher les données immédiatement, veuillez recharger la page. \n\n Voulez-vous recharger la page immédiatement ?")) { + window.location.reload(); + } + } + } }); app.use(i18n); diff --git a/src/Bundle/ChillMainBundle/Resources/public/vuejs/_components/AddressDetails/AddressDetailsButton.vue b/src/Bundle/ChillMainBundle/Resources/public/vuejs/_components/AddressDetails/AddressDetailsButton.vue index 8b1f9d869..b34b1a374 100644 --- a/src/Bundle/ChillMainBundle/Resources/public/vuejs/_components/AddressDetails/AddressDetailsButton.vue +++ b/src/Bundle/ChillMainBundle/Resources/public/vuejs/_components/AddressDetails/AddressDetailsButton.vue @@ -1,36 +1,50 @@ - diff --git a/src/Bundle/ChillMainBundle/Resources/public/vuejs/_components/AddressDetails/AddressDetailsContent.vue b/src/Bundle/ChillMainBundle/Resources/public/vuejs/_components/AddressDetails/AddressDetailsContent.vue index 5a9a97b46..d56ad303d 100644 --- a/src/Bundle/ChillMainBundle/Resources/public/vuejs/_components/AddressDetails/AddressDetailsContent.vue +++ b/src/Bundle/ChillMainBundle/Resources/public/vuejs/_components/AddressDetails/AddressDetailsContent.vue @@ -1,5 +1,6 @@ @@ -9,6 +10,7 @@ import {Address} from "../../../types"; import AddressDetailsMap from "./Parts/AddressDetailsMap.vue"; import AddressRenderBox from "../Entity/AddressRenderBox.vue"; import AddressDetailsGeographicalLayers from "./Parts/AddressDetailsGeographicalLayers.vue"; +import AddressDetailsRefMatching from "./Parts/AddressDetailsRefMatching.vue"; interface AddressModalContentProps { address: Address, @@ -16,6 +18,15 @@ interface AddressModalContentProps { const props = defineProps(); +const emit = defineEmits<{ + (e: 'update-address', value: Address): void +}>(); + +const onUpdateAddress = (address: Address): void => { + console.log('from details content', address); + emit('update-address', address); +} + diff --git a/src/Bundle/ChillMainBundle/Resources/views/Entity/address.html.twig b/src/Bundle/ChillMainBundle/Resources/views/Entity/address.html.twig index 9ad2b0a12..7592e9a9a 100644 --- a/src/Bundle/ChillMainBundle/Resources/views/Entity/address.html.twig +++ b/src/Bundle/ChillMainBundle/Resources/views/Entity/address.html.twig @@ -69,7 +69,7 @@ {% endif %} {{ _self.inline(address, options, streetLine, lines) }} - + {%- endif -%} @@ -79,7 +79,7 @@ {% endif %} {{ _self.inline(address, options, streetLine, lines) }} - + {%- endif -%} @@ -104,7 +104,7 @@
{{ 'address.consider homeless'|trans }}
-

+

{% else %}
@@ -112,7 +112,7 @@ {% endif %} {{ _self.raw(lines) }} -

+

{% endif %} {{ _self.validity(address, options) }} diff --git a/src/Bundle/ChillMainBundle/Tests/Controller/AddressToReferenceMatcherControllerTest.php b/src/Bundle/ChillMainBundle/Tests/Controller/AddressToReferenceMatcherControllerTest.php new file mode 100644 index 000000000..5c51910dc --- /dev/null +++ b/src/Bundle/ChillMainBundle/Tests/Controller/AddressToReferenceMatcherControllerTest.php @@ -0,0 +1,114 @@ +addressRepository = self::$container->get(AddressRepository::class); + } + + /** + * @dataProvider addressToReviewProvider + */ + public function testMarkAddressAsReviewed(int $addressId): void + { + $client = $this->getClientAuthenticated(); + + $client->request('POST', "/api/1.0/main/address/reference-match/${addressId}/set/reviewed"); + + $this->assertResponseIsSuccessful(); + + $address = $this->addressRepository->find($addressId); + + $this->assertEquals(Address::ADDR_REFERENCE_STATUS_REVIEWED, $address->getRefStatus()); + } + + /** + * @dataProvider addressUnsyncedProvider + */ + public function testSyncAddressWithReference(int $addressId): void + { + $client = $this->getClientAuthenticated(); + + $client->request('POST', "/api/1.0/main/address/reference-match/${addressId}/sync-with-reference"); + + $this->assertResponseIsSuccessful(); + + $address = $this->addressRepository->find($addressId); + + $this->assertEquals(Address::ADDR_REFERENCE_STATUS_MATCH, $address->getRefStatus()); + $this->assertEquals($address->getAddressReference()->getStreet(), $address->getStreet()); + $this->assertEquals($address->getAddressReference()->getStreetNumber(), $address->getStreetNumber()); + $this->assertEquals($address->getAddressReference()->getPoint()->toWKT(), $address->getPoint()->toWKT()); + } + + public static function addressToReviewProvider(): iterable + { + self::bootKernel(); + $em = self::$container->get(EntityManagerInterface::class); + + $nb = $em->createQuery('SELECT count(a) FROM '.Address::class.' a') + ->getSingleScalarResult(); + + if (0 === $nb) { + throw new \RuntimeException("There aren't any address with a ref status 'matched'"); + } + + /** @var Address $address */ + $address = $em->createQuery('SELECT a FROM '.Address::class.' a') + ->setFirstResult(rand(0, $nb)) + ->setMaxResults(1) + ->getSingleResult(); + + $address->setRefStatus(Address::ADDR_REFERENCE_STATUS_TO_REVIEW); + $em->flush(); + + yield [$address->getId()]; + } + + public static function addressUnsyncedProvider(): iterable + { + self::bootKernel(); + $em = self::$container->get(EntityManagerInterface::class); + + $nb = $em->createQuery('SELECT count(a) FROM '.AddressReference::class.' a') + ->getSingleScalarResult(); + + if (0 === $nb) { + throw new \RuntimeException("There isn't any address reference"); + } + + $ref = $em->createQuery('SELECT a FROM '.AddressReference::class.' a') + ->setMaxResults(1) + ->setFirstResult(rand(0, $nb)) + ->getSingleResult(); + + $address = Address::createFromAddressReference($ref); + + // make the address dirty + $address->setStreet('tagada') + ->setStreetNumber('-250') + ->setPoint(Point::fromLonLat(0, 0)) + ->setRefStatus(Address::ADDR_REFERENCE_STATUS_TO_REVIEW); + + $em->persist($address); + $em->flush(); + + yield [$address->getId()]; + } +}