Skip to content

Commit

Permalink
Merge pull request #40 from altercampagne/alterpotes-map
Browse files Browse the repository at this point in the history
Introduce Alterpotes map
  • Loading branch information
odolbeau authored Dec 1, 2024
2 parents 5bb1c6f + 02c1642 commit d828911
Show file tree
Hide file tree
Showing 18 changed files with 311 additions and 19 deletions.
1 change: 0 additions & 1 deletion .tiime-trc-baseline
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,6 @@ payment_initiate_membership_payment
payment_callback_back
payment_callback_error
payment_callback_return
profile_memberships
profile_registrations
registration_cancel
logout
Expand Down
1 change: 1 addition & 0 deletions assets/app.js
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ import './styles/app.scss'

import './scripts/AlternativeMap.js'
import './scripts/AlternativesMap.js'
import './scripts/AlterpotesMap.js'
import './scripts/EventRegistrationBikeSelector.js'
import './scripts/EventRegistrationChooseDates.js'
import './scripts/EventRegistrationChoosePrice.js'
Expand Down
1 change: 1 addition & 0 deletions assets/images/leaflet/marker-icon-blue.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
1 change: 1 addition & 0 deletions assets/images/leaflet/marker-icon-purple.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file removed assets/images/leaflet/marker-icon.png
Binary file not shown.
2 changes: 1 addition & 1 deletion assets/scripts/AlternativeMap.js
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ class AlternativeMap extends Map {
longitude: container.dataset.longitude,
});

L.marker([container.dataset.latitude, container.dataset.longitude], { icon: this.customIcon }).addTo(this.map)
L.marker([container.dataset.latitude, container.dataset.longitude], { icon: this.customIconBlue }).addTo(this.map)
.bindPopup(container.dataset.tooltip)
.openPopup();
}
Expand Down
3 changes: 1 addition & 2 deletions assets/scripts/AlternativesMap.js
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ class AlternativesMap extends Map {

const markers = new L.FeatureGroup();
container.querySelectorAll('ul > li').forEach((element) => {
const marker = L.marker([element.dataset.latitude, element.dataset.longitude], { icon: this.customIcon })
const marker = L.marker([element.dataset.latitude, element.dataset.longitude], { icon: this.customIconBlue })
.bindPopup(element.innerHTML)
;

Expand Down Expand Up @@ -36,7 +36,6 @@ class AlternativesMap extends Map {
let geojson;
geojson = L.geoJson(data, {
style: (feature) => {
console.log(feature);
let count = alternativeCountByDepartments[parseInt(feature.properties.code)];

return {
Expand Down
24 changes: 24 additions & 0 deletions assets/scripts/AlterpotesMap.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
import Map from './Map.js'

class AlterpotesMap extends Map {
constructor(container) {
super(container, { zoomLevel: 6 });

const markers = new L.FeatureGroup();
container.querySelectorAll('ul > li').forEach((element) => {
const marker = L.marker([element.dataset.latitude, element.dataset.longitude], { icon: element.dataset.itself ? this.customIconPurple : this.customIconBlue })
.bindPopup(element.innerHTML)
;

markers.addLayer(marker);
});

this.map.addLayer(markers);
}
}

document.addEventListener('DOMContentLoaded', () => {
if (null !== document.querySelector('#alterpotes-map')) {
(() => new AlterpotesMap(document.querySelector('#alterpotes-map')))();
};
});
10 changes: 8 additions & 2 deletions assets/scripts/Map.js
Original file line number Diff line number Diff line change
Expand Up @@ -7,8 +7,14 @@ export default class Map {
longitude = 1.7191,
zoomLevel = 13,
} = {}) {
this.customIcon = L.icon({
iconUrl: document.getElementById('leaflet-config').dataset.markerIcon,
this.customIconBlue = L.icon({
iconUrl: document.getElementById('leaflet-config').dataset.markerIconBlue,
iconSize: [30, 45],
popupAnchor: [0, -10],
});

this.customIconPurple = L.icon({
iconUrl: document.getElementById('leaflet-config').dataset.markerIconPurple,
iconSize: [30, 45],
popupAnchor: [0, -10],
});
Expand Down
32 changes: 32 additions & 0 deletions migrations/Version20241127192114.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
<?php

declare(strict_types=1);

namespace DoctrineMigrations;

use Doctrine\DBAL\Schema\Schema;
use Doctrine\Migrations\AbstractMigration;

/**
* Auto-generated Migration: Please modify to your needs!
*/
final class Version20241127192114 extends AbstractMigration
{
public function getDescription(): string
{
return '';
}

public function up(Schema $schema): void
{
// this up() migration is auto-generated, please modify it to your needs
$this->addSql('ALTER TABLE "user" ADD visible_on_alterpotes_map BOOLEAN DEFAULT false NOT NULL');
}

public function down(Schema $schema): void
{
// this down() migration is auto-generated, please modify it to your needs
$this->addSql('CREATE SCHEMA public');
$this->addSql('ALTER TABLE "user" DROP visible_on_alterpotes_map');
}
}
49 changes: 49 additions & 0 deletions src/Controller/AlterpotesController.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
<?php

declare(strict_types=1);

namespace App\Controller;

use App\Entity\User;
use Doctrine\ORM\EntityManagerInterface;
use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;
use Symfony\Component\HttpFoundation\Response;
use Symfony\Component\Routing\Annotation\Route;
use Symfony\Component\Security\Http\Attribute\IsGranted;

#[IsGranted('ROLE_USER')]
#[Route('/alterpotes', name: 'alterpotes_map')]
class AlterpotesController extends AbstractController
{
public function __construct(
private readonly EntityManagerInterface $em,
) {
}

public function __invoke(): Response
{
/** @var User $user */
$user = $this->getUser();

if (!$user->isMember()) {
$this->addFlash('danger', 'Une adhésion à jour est nécessaire pour accéder à la carte des alterpotes !');

return $this->redirectToRoute('profile_memberships');
}

$qb = $this->em->createQueryBuilder();
$qb
->select('u')
->from(User::class, 'u')
->andWhere('u.visibleOnAlterpotesMap = true')
->andWhere('u.address.latitude IS NOT NULL')
->andWhere('u.address.longitude IS NOT NULL')
;

$users = $qb->getQuery()->getResult();

return $this->render('alterpotes.html.twig', [
'users' => $users,
]);
}
}
17 changes: 17 additions & 0 deletions src/Entity/User.php
Original file line number Diff line number Diff line change
Expand Up @@ -114,6 +114,11 @@ class User implements UserInterface, PasswordAuthenticatedUserInterface, Located
#[ORM\Column(type: 'boolean')]
private bool $isVerified = false;

#[ORM\Column(type: 'boolean', options: [
'default' => false,
])]
private bool $visibleOnAlterpotesMap = false;

#[ORM\Column]
private readonly \DateTimeImmutable $createdAt;

Expand Down Expand Up @@ -433,6 +438,18 @@ public function setHasDrivingLicence(bool $hasDrivingLicence): self
return $this;
}

public function visibleOnAlterpotesMap(): bool
{
return $this->visibleOnAlterpotesMap;
}

public function setVisibleOnAlterpotesMap(bool $visibleOnAlterpotesMap): self
{
$this->visibleOnAlterpotesMap = $visibleOnAlterpotesMap;

return $this;
}

public function getCreatedAt(): \DateTimeImmutable
{
return $this->createdAt;
Expand Down
18 changes: 9 additions & 9 deletions src/Form/ProfileUpdateFormType.php
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@
use Symfony\Component\Form\AbstractType;
use Symfony\Component\Form\Extension\Core\Type\CheckboxType;
use Symfony\Component\Form\Extension\Core\Type\EnumType;
use Symfony\Component\Form\Extension\Core\Type\TextareaType;
use Symfony\Component\Form\Extension\Core\Type\TextType;
use Symfony\Component\Form\FormBuilderInterface;
use Symfony\Component\OptionsResolver\OptionsResolver;
Expand Down Expand Up @@ -47,15 +48,14 @@ public function buildForm(FormBuilderInterface $builder, array $options): void
'help_html' => true,
'required' => false,
])
->add('biography', TextType::class, [
'label' => false,
'attr' => [
'placeholder' => 'Ta présentation',
],
'row_attr' => [
'class' => 'form-floating mb-3',
],
'help' => 'N\'hésite pas à nous en dire un peu plus te concernant (aucune obligation bien sûr). 😊',
->add('visibleOnAlterpotesMap', CheckboxType::class, [
'label' => "J'accepte d'apparaitre sur la carte des alterpotes",
'help' => 'La carte est visible uniquement pour les membres à jour de leur cotisation.',
'required' => false,
])
->add('biography', TextareaType::class, [
'label' => 'Ta présentation (visible sur la carte)',
'help' => 'N\'hésite pas à ajouter des informations utiles pour les autres membres de l\'association : est-ce que tu acceptes d\'héberger des altercyclistes ? dans quel contexte ?',
'required' => false,
])
;
Expand Down
13 changes: 12 additions & 1 deletion templates/_base.html.twig
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,11 @@
<li class="nav-item">
<a class="nav-link active" aria-current="page" href="{{ path('alternative_map') }}">Alternatives</a>
</li>
{% if is_granted('ROLE_USER') and app.user.isMember %}
<li class="nav-item">
<a class="nav-link active" aria-current="page" href="{{ path('alterpotes_map') }}">Carte des alterpotes</a>
</li>
{% endif %}
<li class="nav-item">
<a class="nav-link active" aria-current="page" href="{{ path('faq') }}">Foire aux questions</a>
</li>
Expand Down Expand Up @@ -128,5 +133,11 @@
</footer>
{% endblock %}

<div class="d-none" id="leaflet-config" data-marker-icon="{{ asset('images/leaflet/marker-icon.png') }}"></div>
<div
class="d-none"
id="leaflet-config"
data-marker-icon="{{ asset('images/leaflet/marker-icon.png') }}"
data-marker-icon-blue="{{ asset('images/leaflet/marker-icon-blue.svg') }}"
data-marker-icon-purple="{{ asset('images/leaflet/marker-icon-purple.svg') }}"
></div>
{% endblock %}
67 changes: 67 additions & 0 deletions templates/alterpotes.html.twig
Original file line number Diff line number Diff line change
@@ -0,0 +1,67 @@
{% extends '_base.html.twig' %}

{% block title %}La carte des alterpotes{% endblock %}

{% block main %}
<div class="container">
<div class="row justify-content-center">
<div class="col">
<div class="mb-3 text-center">
<h1>La carte des alterpotes</h1>
</div>
</div>
</div>

<div class="row justify-content-center mt-3">
{% if not app.user.visibleOnAlterpotesMap %}
<div class="alert alert-danger">
<b>Pour le moment, tu ne figures pas sur la carte des alterpotes !</b><br />
Tu souhaites appraitre dessus ? Il te suffit de <a href="{{ path('profile_update_profile') }}"> modifier tes préférences</a>.
</div>
{% endif %}

<div class="bg-white p-3 rounded border border-secondary-outline mt-2">
<div
id="alterpotes-map"
style="height: 650px"
>
<ul class="d-none">
{% for user in users %}
<li
data-latitude="{{ user.address.latitude }}"
data-longitude="{{ user.address.longitude }}"
data-itself="{{ app.user == user }}"
>
<div class="text-center">
<h5 class="mt-2">{{ user.publicName }}</h5>
{% if user.biography is not null %}
<p>
{{ user.biography }}
</p>
{% endif %}

<hr />

<a class="btn btn-link btn-sm" href="tel:{{ user.phoneNumber|phone_number_format }}">
{{ user.phoneNumber|phone_number_format }}
</a>
<a class="btn btn-link btn-sm" href="mailto:{{ user.email }}">
{{ user.email }}
</a>

{% if user == app.user %}
<hr />

<a href="{{ path('profile_update_profile') }}" class="btn btn-primary text-white btn-sm">
Modifier mes informations
</a>
{% endif %}
</div>
</li>
{% endfor %}
</ul>
</div>
</div>
</div>
</div>
{% endblock %}
29 changes: 28 additions & 1 deletion templates/profile/update_profile.html.twig
Original file line number Diff line number Diff line change
Expand Up @@ -53,9 +53,16 @@
</ul>
{% endif %}


<fieldset class="form-group border p-3 mt-3">
<legend class="w-auto px-2"><i class="fa-solid fa-user me-2"></i> Ta présentation <i class="text-secondary">(facultative)</i></legend>
<legend class="w-auto px-2">
<i class="fa-solid fa-map me-2"></i> Carte des alterpotes
<small><a href="#" class=" mx-2" data-bs-toggle="modal" data-bs-target="#alterpotesMapDetails">
(plus d'infos)
</a></small>
</legend>

{{ form_row(form.visibleOnAlterpotesMap) }}
{{ form_row(form.biography) }}
</fieldset>

Expand Down Expand Up @@ -117,4 +124,24 @@
</div>
</div>
</div>

<div class="modal" id="alterpotesMapDetails" tabindex="-1" aria-hidden="true">
<div class="modal-dialog modal-dialog-centered">
<div class="modal-content">
<div class="modal-header">
<h1 class="modal-title fs-5" id="exampleModalLabel">La carte des alterpotes ?</h1>
<button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Close"></button>
</div>
<div class="modal-body">
<div class="alert bg-info-subtle">
La carte des alterpotes est visible uniquement par les membres à jour de leur cotisation, tes coordonées ne seront donc pas visibles publiquement.
</div>

<p>
La carte des alterpotes permet de visualiser où habitent toutes les personnes qui le souhaitent et éventuellement de mettre les membres en relation les uns avec les autres.
</p>
</div>
</div>
</div>
</div>
{% endblock %}
11 changes: 9 additions & 2 deletions tests/DatabaseUtilTrait.php
Original file line number Diff line number Diff line change
Expand Up @@ -15,8 +15,7 @@ trait DatabaseUtilTrait
{
public function save(object ...$objects): void
{
/** @var EntityManagerInterface $em */
$em = static::getContainer()->get(EntityManagerInterface::class);
$em = $this->getEntityManager();

foreach ($objects as $object) {
$em->persist($object);
Expand Down Expand Up @@ -77,4 +76,12 @@ protected function getBookableEvent(): Event

return $event;
}

protected function getEntityManager(): EntityManagerInterface
{
/** @var EntityManagerInterface $em */
$em = static::getContainer()->get(EntityManagerInterface::class);

return $em;
}
}
Loading

0 comments on commit d828911

Please sign in to comment.