ajout propriétés de rue et siret aux lieux, champs en plus après envoi

This commit is contained in:
Tykayn 2025-06-09 00:11:16 +02:00 committed by tykayn
parent b9257d34fd
commit 3ccfd732e7
17 changed files with 361 additions and 56 deletions

View file

@ -112,4 +112,36 @@ table.js-sort-table th:active {
.good_filled {
border-color: green;
}
.hidden {
display: none;
}
input[type="checkbox"] {
width: 20px;
height: 20px;
}
.is-invalid {
border: 1px solid red;
}
.is-invalid #validation_messages {
color: red;
}
img {
max-width: 100%;
max-height: 400px;
}
@media (max-width: 768px) {
.form-label {
margin-bottom: 0.5rem;
}
.mb-3 {
margin-bottom: 1rem !important;
}
}

View file

@ -0,0 +1,47 @@
<?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 Version20250608210112 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(<<<'SQL'
ALTER TABLE place ADD street VARCHAR(255) DEFAULT NULL
SQL);
$this->addSql(<<<'SQL'
ALTER TABLE place ADD housenumber VARCHAR(255) DEFAULT NULL
SQL);
$this->addSql(<<<'SQL'
ALTER TABLE place ADD siret VARCHAR(255) DEFAULT NULL
SQL);
}
public function down(Schema $schema): void
{
// this down() migration is auto-generated, please modify it to your needs
$this->addSql(<<<'SQL'
ALTER TABLE place DROP street
SQL);
$this->addSql(<<<'SQL'
ALTER TABLE place DROP housenumber
SQL);
$this->addSql(<<<'SQL'
ALTER TABLE place DROP siret
SQL);
}
}

View file

@ -125,7 +125,7 @@ final class AdminController extends AbstractController
* récupérer les commerces de la zone, créer les nouveaux lieux, et mettre à jour les existants
*/
#[Route('/admin/labourer/{zip_code}', name: 'app_admin_labourer')]
public function labourer(string $zip_code, bool $updateExisting = false): Response
public function labourer(string $zip_code, bool $updateExisting = true): Response
{
try {
// Récupérer ou créer les stats pour cette zone
@ -169,6 +169,10 @@ final class AdminController extends AbstractController
->setStats($stats)
->setDead(false)
->setOptedOut(false)
->setMainTag($this->motocultrice->find_main_tag($placeData['tags']) ?? '')
->setStreet($this->motocultrice->find_street($placeData['tags']) ?? '')
->setHousenumber($this->motocultrice->find_housenumber($placeData['tags']) ?? '')
->setSiret($this->motocultrice->find_siret($placeData['tags']) ?? '')
->setAskedHumainsSupport(false)
->setLastContactAttemptDate(null)
->setNote('')

View file

@ -76,7 +76,9 @@ class PublicController extends AbstractController
->setModifiedDate(new \DateTime())
->setZipCode($zipCode)
->setPlaceCount(0)
->setMainTag($this->motocultrice->find_main_tag($data['tags_converted']) ?? '')
->setStreet($this->motocultrice->find_street($data['tags_converted']) ?? '')
->setHousenumber($this->motocultrice->find_housenumber($data['tags_converted']) ?? '')
->setLastContactAttemptDate(new \DateTime())
->setUuidForUrl(uniqid());
@ -461,4 +463,14 @@ class PublicController extends AbstractController
'places_displayed' => $places_displayed
]);
}
#[Route('/set_opted_out_place/{uuid}', name: 'app_public_set_opted_out_place')]
public function set_opted_out_place($uuid): Response
{
$place = $this->entityManager->getRepository(Place::class)->findOneBy(['uuid_for_url' => $uuid]);
if (!$place) {
$this->addFlash('warning', 'Ce commerce n\'existe pas.');
return $this->redirectToRoute('app_public_index');
}
}
}

View file

@ -91,6 +91,15 @@ class Place
#[ORM\Column(nullable: true)]
private ?int $lon = null;
#[ORM\Column(length: 255, nullable: true)]
private ?string $street = null;
#[ORM\Column(length: 255, nullable: true)]
private ?string $housenumber = null;
#[ORM\Column(length: 255, nullable: true)]
private ?string $siret = null;
public function getMainTag(): ?string
{
return $this->main_tag;
@ -191,6 +200,9 @@ class Place
['key' => 'website', 'setter' => 'setHasWebsite', 'source' => $overpass_data['tags'] ?? []],
['key' => 'wheelchair', 'setter' => 'setHasWheelchair', 'source' => $overpass_data['tags'] ?? []],
['key' => 'note', 'setter' => 'setHasNote', 'source' => $overpass_data['tags'] ?? []],
['key' => 'siret', 'setter' => 'setSiret', 'source' => $overpass_data['tags'] ?? []],
['key' => 'addr:street', 'setter' => 'setStreet', 'source' => $overpass_data['tags'] ?? []],
['key' => 'addr:housenumber', 'setter' => 'setHousenumber', 'source' => $overpass_data['tags'] ?? []],
];
foreach ($mapping as $map) {
@ -531,4 +543,40 @@ class Place
return $this;
}
public function getStreet(): ?string
{
return $this->street;
}
public function setStreet(?string $street): static
{
$this->street = $street;
return $this;
}
public function getHousenumber(): ?string
{
return $this->housenumber;
}
public function setHousenumber(?string $housenumber): static
{
$this->housenumber = $housenumber;
return $this;
}
public function getSiret(): ?string
{
return $this->siret;
}
public function setSiret(?string $siret): static
{
$this->siret = $siret;
return $this;
}
}

View file

@ -74,6 +74,12 @@ class Motocultrice
];
public function find_siret($tags) {
if(isset($tags['ref:FR:SIRET']) && $tags['ref:FR:SIRET'] != '') {
return $tags['ref:FR:SIRET'];
}
return null;
}
public function export($zone) {
$query = $this->get_export_query($zone);
@ -188,6 +194,26 @@ out center tags;';
}
}
public function find_street($tags) {
if(isset($tags['addr:street']) && $tags['addr:street'] != '') {
return $tags['addr:street'];
}
if(isset($tags['contact:street']) && $tags['contact:street'] != '') {
return $tags['contact:street'];
}
return null;
}
public function find_housenumber($tags) {
if(isset($tags['addr:housenumber']) && $tags['addr:housenumber'] != '') {
return $tags['addr:housenumber'];
}
if(isset($tags['contact:housenumber']) && $tags['contact:housenumber'] != '') {
return $tags['contact:housenumber'];
}
return null;
}
public function get_city_osm_from_zip_code($zip_code) {
// Requête Overpass pour obtenir la zone administrative de niveau 8 avec un nom
$query = "[out:json][timeout:25];
@ -290,6 +316,9 @@ out center tags;';
return null;
}
/**
* migrer seulement si la destination n'est pas remplie
*/
public function migrate_tags($osm_object_data) {
// migrer email vers contact:email

View file

@ -8,7 +8,12 @@
<div id="map" style="height: 600px;"></div>
<div class="mt-3">
<button id="openInJOSM" class="btn btn-primary">Ouvrir dans JOSM</button>
<a href="#" id="viewInPanoramax" class="btn btn-secondary" target="_blank">Voir dans Panoramax</a>
<a href="#" id="viewInPanoramax" class="btn btn-secondary" >Voir dans Panoramax</a>
</div>
<div class="mt-3">
<a href="{{ path('app_public_set_opted_out_place', {'uuid': place.uuidForUrl}) }}" class="btn btn-danger">
Ne plus me rappeler de vérifier les informations de ce lieu par email ou tout autre moyen
</a>
</div>
</div>
</div>

View file

@ -84,6 +84,16 @@
<i class="bi bi-load bi-spin"></i>
<span class="visually-hidden">Chargement de la carte...</span>
</div>
</div>
<div class="d-flex justify-content-end mb-2">
<div class="btn-group" role="group">
<button type="button" class="btn btn-outline-primary active" id="circleMarkersBtn">
<i class="bi bi-circle"></i> Cercles
</button>
<button type="button" class="btn btn-outline-primary" id="dropMarkersBtn">
<i class="bi bi-geo-alt"></i> Gouttes
</button>
</div>
</div>
<div id="map" class="mt-4" style="height: 400px;"></div>
<div id="attribution">

View file

@ -47,13 +47,16 @@
{{ commerce.mainTag }}
</a>
</td>
<td class="{{ commerce.hasAddress() ? 'filled' : '' }}">{{ commerce.address }}</td>
<td class="{{ commerce.hasAddress() ? 'filled' : '' }}">{{ commerce.address }} </td>
<td class="{{ commerce.hasAddress() ? 'filled' : '' }}">{{ commerce.housenumber }}</td>
<td class="{{ commerce.hasAddress() ? 'filled' : '' }}">{{ commerce.street }}</td>
<td class="{{ commerce.hasWebsite() ? 'filled' : '' }}">{{ commerce.website }}</td>
<td class="{{ commerce.hasWheelchair() ? 'filled' : '' }}">{{ commerce.wheelchair }}</td>
<td class="{{ commerce.hasNote() ? 'filled' : '' }}">{{ commerce.note }}</td>
<td class="{{ commerce.noteContent ? 'filled' : '' }}">{{ commerce.noteContent }}</td>
<td class="{{ commerce.siret ? 'filled' : '' }}"> <a href="https://annuaire-entreprises.data.gouv.fr/etablissement/{{ commerce.siret }}" > {{ commerce.siret }}</a></td>
<td>
<a href="https://www.openstreetmap.org/{{ commerce.osmKind }}/{{ commerce.osmId }}" target="_blank">
<a href="https://www.openstreetmap.org/{{ commerce.osmKind }}/{{ commerce.osmId }}" >
<i class="bi bi-globe"></i>
{{ commerce.osmId }}
</a>

View file

@ -12,7 +12,15 @@
<th>
<i class="bi bi-geo-alt"></i>
Adresse ({{ stats.getAvecAdresse() }} / {{ stats.places|length }})</th>
<th>
<i class="bi bi-house-fill"></i>
Rue
</th>
<th>
<i class="bi bi-house-fill"></i>
Numéro
</th>
<i class="bi bi-globe"></i>
Site web ({{ stats.getAvecSite() }} / {{ stats.places|length }})</th>
<th>
@ -28,6 +36,10 @@
<i class="bi bi-pencil-square"></i>
Texte de la note</th>
<th>
<i class="bi bi-building"></i>
Siret
</th>
<th>
Osm id</th>
<th>

View file

@ -2,20 +2,6 @@
{% block title %}{{ 'display.title'|trans }} {{ commerce_overpass.tags_converted.name }}{% endblock %}
{% block stylesheets %}
{{ parent() }}
<style>
.hidden { display: none; }
input[type="checkbox"] { width: 20px; height: 20px; }
.is-invalid { border: 1px solid red; }
.is-invalid #validation_messages { color: red; }
img { max-width: 100%; max-height: 400px; }
@media (max-width: 768px) {
.form-label { margin-bottom: 0.5rem; }
.mb-3 { margin-bottom: 1rem !important; }
}
</style>
{% endblock %}
{% block body %}
<div class="container edit-land mt-4">
@ -39,18 +25,22 @@
</div>
{% if commerce_overpass.tags_converted.image is defined %}
<div id="images" class="d-none">
<img class="img-fluid img-thumbnail mb-3" src="{{ commerce_overpass.tags_converted.image }}" />
url d'image de la facade : {{ commerce_overpass.tags_converted.image }}
{% if commerce_overpass.tags_converted.panoramax is defined or commerce_overpass.tags_converted.wikimedia_commons is defined %}
<input type="text" class="form-control" name="commerce_tag_value__image" value="{{ commerce_overpass.tags_converted.image }}">
url d'image sur Panoramax : {{ commerce_overpass.tags_converted.panoramax is defined ? commerce_overpass.tags_converted.panoramax : '' }}
<input type="text" class="form-control" name="commerce_tag_value__panoramax" value="{{ commerce_overpass.tags_converted.panoramax is defined ? commerce_overpass.tags_converted.panoramax : '' }}">
wikimedia_commons:
<input type="text" class="form-control" name="commerce_tag_value__wikimedia_commons" value="{{ commerce_overpass.tags_converted.wikimedia_commons is defined ? commerce_overpass.tags_converted.wikimedia_commons : '' }}">
{% endif %}
</div>
{% endif %}
{% if commerce_overpass.tags_converted.amenity is defined and (commerce_overpass.tags_converted.amenity == 'restaurant' or commerce_overpass.tags_converted.amenity == 'bar' or commerce_overpass.tags_converted.amenity == 'cafe' or commerce_overpass.tags_converted.amenity == 'fast_food') %}
{% include 'public/edit/restaurant.html.twig' %}
@ -187,12 +177,6 @@
{{ parent() }}
<script src={{asset('js/mapbox/mapbox-gl.js')}}></script>
<script>
{# function openInJOSM(type, id) {
const josmUrl = `http://localhost:8111/load_object?objects=${type}${id}`;
window.open(josmUrl);
} #}
{% if commerce is not empty and mapbox_token is not empty and maptiler_token is not empty and commerce_overpass['@attributes'].lon is defined and commerce_overpass['@attributes'].lat is defined %}
mapboxgl.accessToken = '{{ mapbox_token }}';
map = new mapboxgl.Map({

View file

@ -7,7 +7,7 @@
<div class="col-2 col-md-3">
<img src="https://i0.wp.com/askforangela.co.uk/wp-content/uploads/2022/10/Ask-for-Angela-primary.png?fit=300%2C300&ssl=1" class="img-fluid img-thumbnail mb-3" alt="Image du lieu">
<p class="more-info">
<a href="https://arretonslesviolences.gouv.fr/focus/plan-angela" target="_blank">{{ 'display.ask_angela_more_info'|trans }}</a>
<a href="https://arretonslesviolences.gouv.fr/focus/plan-angela">{{ 'display.ask_angela_more_info'|trans }}</a>
</p>
</div>
<div class="col-6">

View file

@ -60,24 +60,24 @@
{{'display.keys.takeaway'|trans}}</label>
</h3>
<div class="p-4">
<div class="form-check">
<input class="form-check-input" type="radio" name="commerce_tag_value__takeaway" id="takeaway_yes" value="yes" {% if commerce.tags_converted.takeaway is defined and commerce.tags_converted.takeaway == 'yes' %}checked{% endif %}>
<label class="form-check-label" for="takeaway_yes">Oui mais pas uniquement à emporter</label>
</div>
<div class="form-check">
<input class="form-check-input" type="radio" name="commerce_tag_value__takeaway" id="takeaway_only" value="only" {% if commerce.tags_converted.takeaway is defined and commerce.tags_converted.takeaway == 'only' %}checked{% endif %}>
<label class="form-check-label" for="takeaway_only">Uniquement à emporter</label>
</div>
<div class="form-check">
<input class="form-check-input" type="radio" name="commerce_tag_value__takeaway" id="takeaway_no" value="no" {% if commerce.tags_converted.takeaway is defined and commerce.tags_converted.takeaway == 'no' %}checked{% endif %}>
<label class="form-check-label" for="takeaway_no">Nous ne proposons pas d'emporter les plats</label>
</div>
<div class="form-check">
<input class="form-check-input" type="radio" name="commerce_tag_value__takeaway" id="takeaway_unknown" value="" {% if commerce.tags_converted.takeaway is not defined or commerce.tags_converted.takeaway == '' %}checked{% endif %}>
<label class="form-check-label" for="takeaway_unknown">Je ne sais pas</label>
</div>
<div class="p-4">
<div class="form-check">
<input class="form-check-input" type="radio" name="commerce_tag_value__takeaway" id="takeaway_yes" value="yes" {% if commerce.tags_converted.takeaway is defined and commerce.tags_converted.takeaway == 'yes' %}checked{% endif %}>
<label class="form-check-label" for="takeaway_yes">Oui mais pas uniquement à emporter</label>
</div>
<div class="form-check">
<input class="form-check-input" type="radio" name="commerce_tag_value__takeaway" id="takeaway_only" value="only" {% if commerce.tags_converted.takeaway is defined and commerce.tags_converted.takeaway == 'only' %}checked{% endif %}>
<label class="form-check-label" for="takeaway_only">Uniquement à emporter</label>
</div>
<div class="form-check">
<input class="form-check-input" type="radio" name="commerce_tag_value__takeaway" id="takeaway_no" value="no" {% if commerce.tags_converted.takeaway is defined and commerce.tags_converted.takeaway == 'no' %}checked{% endif %}>
<label class="form-check-label" for="takeaway_no">Nous ne proposons pas d'emporter les plats</label>
</div>
<div class="form-check">
<input class="form-check-input" type="radio" name="commerce_tag_value__takeaway" id="takeaway_unknown" value="" {% if commerce.tags_converted.takeaway is not defined or commerce.tags_converted.takeaway == '' %}checked{% endif %}>
<label class="form-check-label" for="takeaway_unknown">Je ne sais pas</label>
</div>
</div>
</div>
</div>

View file

@ -0,0 +1,91 @@
<div id="ask_angela">
<fieldset >
<h2>Y'en a un peu plus je vous le mets?</h2>
<div class="p-4">
<div class="row mb-3">
<div class="col-12 col-md-3">
nombres d'étages
</div>
<div class="col-12 col-md-9">
</div>
</div>
<div class="row mb-3">
<div class="col-12 col-md-3">
wifi public
</div>
<div class="col-12 col-md-9">
</div>
</div>
<div class="row mb-3">
<div class="col-12 col-md-3">
toilettes
</div>
<div class="col-12 col-md-9">
<input type="text" class="form-control" name="commerce_tag_value__toilet" value="{{ commerce.tags_converted['toilet'] is defined ? commerce.tags_converted['toilet'] : '' }}">
</div>
</div>
<div class="row mb-3">
<div class="col-12 col-md-3">
table à langer
</div>
<div class="col-12 col-md-9">
<input type="text" class="form-control" name="commerce_tag_value__langer" value="{{ commerce.tags_converted['langer'] is defined ? commerce.tags_converted['langer'] : '' }}">
</div>
</div>
<div class="row mb-3">
<div class="col-12 col-md-3">
moyens de paiement proposés
</div>
<div class="col-12 col-md-9">
</div>
</div>
<div class="row mb-3">
<div class="col-12 col-md-3">
étoiles michelin
</div>
<div class="col-12 col-md-9">
</div>
</div>
<div class="row mb-3">
<div class="col-12 col-md-3">
médias sociaux
</div>
<div class="col-12 col-md-9">
<input type="text" class="form-control" name="commerce_tag_value__contact:mastodon" value="{{ commerce.tags_converted['contact:mastodon'] is defined ? commerce.tags_converted['contact:mastodon'] : '' }}">
<input type="text" class="form-control" name="commerce_tag_value__contact:facebook" value="{{ commerce.tags_converted['contact:facebook'] is defined ? commerce.tags_converted['contact:facebook'] : '' }}">
<input type="text" class="form-control" name="commerce_tag_value__contact:instagram" value="{{ commerce.tags_converted['contact:instagram'] is defined ? commerce.tags_converted['contact:instagram'] : '' }}">
<input type="text" class="form-control" name="commerce_tag_value__contact:twitter" value="{{ commerce.tags_converted['contact:twitter'] is defined ? commerce.tags_converted['contact:twitter'] : '' }}">
<input type="text" class="form-control" name="commerce_tag_value__contact:youtube" value="{{ commerce.tags_converted['contact:youtube'] is defined ? commerce.tags_converted['contact:youtube'] : '' }}">
<input type="text" class="form-control" name="commerce_tag_value__contact:tiktok" value="{{ commerce.tags_converted['contact:tiktok'] is defined ? commerce.tags_converted['contact:tiktok'] : '' }}">
<input type="text" class="form-control" name="commerce_tag_value__contact:linkedin" value="{{ commerce.tags_converted['contact:linkedin'] is defined ? commerce.tags_converted['contact:linkedin'] : '' }}">
<input type="text" class="form-control" name="commerce_tag_value__contact:pinterest" value="{{ commerce.tags_converted['contact:pinterest'] is defined ? commerce.tags_converted['contact:pinterest'] : '' }}">
<input type="text" class="form-control" name="commerce_tag_value__contact:snapchat" value="{{ commerce.tags_converted['contact:snapchat'] is defined ? commerce.tags_converted['contact:snapchat'] : '' }}">
</div>
</div>
<div class="row mb-3">
<div class="col-12 col-md-3">
téléphone
</div>
<div class="col-12 col-md-9">
<input type="text" class="form-control" name="commerce_tag_value__contact:phone" value="{{ commerce.tags_converted['contact:phone'] is defined ? commerce.tags_converted['contact:phone'] : '' }}">
</div>
</div>
<div class="row mb-3">
<div class="col-12 col-md-3">
</div>
<div class="col-12 col-md-9">
</div>
</div>
</div>
</fieldset>
</div>

View file

@ -26,9 +26,9 @@
<span class="last-modification">{{ 'display.last_modification'|trans }}: {{ commerce['@attributes'].timestamp }}</span>,
{{ 'display.days_ago'|trans({'%days%': date(commerce['@attributes'].timestamp).diff(date()).days}) }}
{{ 'display.by'|trans }}
<a href="https://www.openstreetmap.org/user/{{ commerce['@attributes'].user }}" target="_blank">{{ commerce['@attributes'].user }}</a>
<a href="https://www.openstreetmap.org/user/{{ commerce['@attributes'].user }}" >{{ commerce['@attributes'].user }}</a>
<div class="lien-OpenStreetMap">
<a href="https://www.openstreetmap.org/node/{{ commerce['@attributes'].id }}" target="_blank">{{ 'display.view_on_osm'|trans }}</a>
<a href="https://www.openstreetmap.org/node/{{ commerce['@attributes'].id }}" >{{ 'display.view_on_osm'|trans }}</a>
</div>
</span>

View file

@ -12,7 +12,7 @@
<td>{{ place.modifiedDate | date('Y-m-d H:i:s') }}</td>
<td>{{ place.zipCode }}</td>
<td>
<a href="https://www.openstreetmap.org/{{place.osmKind}}/{{ place.osmId }}" target="_blank"><i class="bi bi-globe"></i></a>
<a href="https://www.openstreetmap.org/{{place.osmKind}}/{{ place.osmId }}" ><i class="bi bi-globe"></i></a>
{% if place.name %}
<a href="{{ path('app_public_edit', {'zipcode': place.zipCode, 'name': place.name|url_encode, 'uuid': place.uuidForUrl}) }}"><i class="bi bi-pencil"></i></a>

View file

@ -14,16 +14,38 @@
<span class="badge bg-success p-4">{{status}}</span>
<p class="p-4 col-12 col-lg-8">
Merci d'avoir contribué à l'amélioration de la base de données OSM, votre contribution sera visible sur de nombreux sites web.
Merci d'avoir contribué à l'amélioration de la base de données OSM, votre contribution sera visible sur de nombreux sites web et dans la base adresse nationale des lieux et commerces.
<br>
Modifer votre commerce?
Réutilisez le lien secret que vous avez reçu par email.
Compléter les informations de votre commerce?
Complétez les informations ici ou bien réutilisez plus tard le lien secret que vous avez reçu par email.
<br>
Envie de contribuer ailleurs ?
Créez votre propre compte OpenStreetMap sur <a href="https://osm.org" >osm.org</a>, et venez discuter avec les autres contributeurs sur le forum de la communauté OSM sur <a href="https://forum.openstreetmap.fr" target="_blank">forum.openstreetmap.fr</a>.
Créez votre propre compte OpenStreetMap sur <a href="https://osm.org" >osm.org</a>, et venez discuter avec les autres contributeurs sur le forum de la communauté OSM sur <a href="https://forum.openstreetmap.fr" >forum.openstreetmap.fr</a>.
<hr>
<form action="{{ path('app_public_submit', {'osm_object_id': commerce_overpass['@attributes'].id, 'version': commerce_overpass['@attributes'].version, 'changesetID': commerce_overpass['@attributes'].changeset }) }}" method="post" class="needs-validation">
<input type="hidden" name="osm_kind" value="{{ osm_kind }}">
{% include 'public/edit/yenaunpeuplusjevouslemets.html.twig' %}
{% include 'public/edit/social_media.html.twig' %}
{% include 'public/edit/address.html.twig' %}
{% include 'public/edit/clim.html.twig' %}
{% include 'public/edit/ask_angela.html.twig' %}
{% include 'public/edit/wheelchair.html.twig' %}
<div id="validation_messages" class="alert alert-danger d-none"></div>
<div class="d-grid gap-2 d-md-flex justify-content-md-end mt-4">
<button type="submit" class="btn btn-primary col-12 col-md-5">
<i class="bi bi-send"></i>
{{ 'display.submit'|trans }}
</button>
</div>
</form>
</p>
{% else %}
<span class="badge bg-danger p-4">{{status}}</span>
@ -33,6 +55,12 @@
</div>
{% endblock %}
{% block javascripts %}
{{ parent() }}
{% block javascripts %}
{{ parent() }}
<script>
check_validity();
</script>
{% endblock %}