up pages rues et évolutions dans le temps
This commit is contained in:
parent
7355600e6b
commit
c8e3cf2ada
10 changed files with 329 additions and 14 deletions
|
@ -2079,6 +2079,7 @@ final class AdminController extends AbstractController
|
|||
]);
|
||||
}
|
||||
|
||||
#[Route('/admin/followup-graph/{insee_code}', name: 'admin_followup_graph', requirements: ['insee_code' => '\d+'])]
|
||||
public function followupGraph(Request $request, string $insee_code): Response
|
||||
{
|
||||
$stats = $this->entityManager->getRepository(Stats::class)->findOneBy(['zone' => $insee_code]);
|
||||
|
@ -2086,17 +2087,15 @@ final class AdminController extends AbstractController
|
|||
$this->addFlash('error', 'Aucune stats trouvée pour ce code INSEE.');
|
||||
return $this->redirectToRoute('app_admin');
|
||||
}
|
||||
|
||||
$themes = \App\Service\FollowUpService::getFollowUpThemes();
|
||||
// On ne vérifie pas ici un thème unique, on boucle sur tous les thèmes plus loin
|
||||
|
||||
// Ici, on ne prépare que les variables globales pour le template
|
||||
return $this->render('admin/followup_graph.html.twig', [
|
||||
'stats' => $stats,
|
||||
'completion_tags' => \App\Service\FollowUpService::getFollowUpCompletionTags(),
|
||||
'followup_labels' => \App\Service\FollowUpService::getFollowUpThemes(),
|
||||
'followup_icons' => \App\Service\FollowUpService::getFollowUpIcons(),
|
||||
// ... autres variables nécessaires ...
|
||||
|
||||
]);
|
||||
}
|
||||
|
||||
|
@ -2111,4 +2110,41 @@ final class AdminController extends AbstractController
|
|||
// Pour éviter l'erreur, on retourne une redirection par défaut si rien n'est fait
|
||||
return $this->redirectToRoute('admin_dashboard');
|
||||
}
|
||||
|
||||
#[Route('/admin/stats/{insee_code}/street-completion', name: 'admin_street_completion', requirements: ['insee_code' => '\d+'])]
|
||||
public function streetCompletion(string $insee_code): Response
|
||||
{
|
||||
$stats = $this->entityManager->getRepository(Stats::class)->findOneBy(['zone' => $insee_code]);
|
||||
if (!$stats) {
|
||||
$this->addFlash('error', 'Aucune stats trouvée pour ce code INSEE.');
|
||||
return $this->redirectToRoute('app_admin');
|
||||
}
|
||||
$places = $stats->getPlaces();
|
||||
$rues = [];
|
||||
foreach ($places as $place) {
|
||||
$rue = $place->getStreet() ?: '(sans nom)';
|
||||
if (!isset($rues[$rue])) {
|
||||
$rues[$rue] = [ 'places' => [], 'completion_sum' => 0 ];
|
||||
}
|
||||
$rues[$rue]['places'][] = $place;
|
||||
$rues[$rue]['completion_sum'] += $place->getCompletionPercentage();
|
||||
}
|
||||
$rues_data = [];
|
||||
foreach ($rues as $nom => $data) {
|
||||
$count = count($data['places']);
|
||||
$avg = $count > 0 ? round($data['completion_sum'] / $count, 1) : 0;
|
||||
$rues_data[] = [
|
||||
'name' => $nom,
|
||||
'count' => $count,
|
||||
'avg_completion' => $avg,
|
||||
];
|
||||
}
|
||||
// Tri décroissant par complétion moyenne
|
||||
usort($rues_data, fn($a, $b) => $b['avg_completion'] <=> $a['avg_completion']);
|
||||
return $this->render('admin/street_completion.html.twig', [
|
||||
'stats' => $stats,
|
||||
'rues' => $rues_data,
|
||||
'insee_code' => $insee_code,
|
||||
]);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -90,12 +90,12 @@ class FollowUpController extends AbstractController
|
|||
$all_points = [];
|
||||
foreach ($followups as $fu) {
|
||||
$series[$fu->getName()][] = [
|
||||
'date' => $fu->getDate()->format('c'),
|
||||
'date' => $fu->getDate()->format('Y-m-d'),
|
||||
'value' => $fu->getMeasure(),
|
||||
'name' => $fu->getName(),
|
||||
];
|
||||
$all_points[] = [
|
||||
'date' => $fu->getDate()->format('c'),
|
||||
'date' => $fu->getDate()->format('Y-m-d'),
|
||||
'type' => $fu->getName(),
|
||||
'name' => $fu->getName(),
|
||||
'value' => $fu->getMeasure(),
|
||||
|
@ -110,6 +110,43 @@ class FollowUpController extends AbstractController
|
|||
});
|
||||
}
|
||||
unset($points);
|
||||
// Ajout du calcul all_completion_data
|
||||
$themes = \App\Service\FollowUpService::getFollowUpThemes();
|
||||
$all_completion_data = [];
|
||||
$latest_diffs = [];
|
||||
foreach ($themes as $type => $label) {
|
||||
$all_completion_data[$type] = $series[$type . '_completion'] ?? [];
|
||||
// Calcul du diff sur 7 jours pour le nombre et la complétion
|
||||
$count_series = $series[$type . '_count'] ?? [];
|
||||
$completion_series = $series[$type . '_completion'] ?? [];
|
||||
$count_now = count($count_series) ? $count_series[count($count_series)-1]['value'] : null;
|
||||
$count_7d = null;
|
||||
foreach (array_reverse($count_series) as $point) {
|
||||
$date = \DateTime::createFromFormat('Y-m-d', $point['date']);
|
||||
if ($date && $date <= (new \DateTime('-7 days'))) {
|
||||
$count_7d = $point['value'];
|
||||
break;
|
||||
}
|
||||
}
|
||||
$completion_now = count($completion_series) ? $completion_series[count($completion_series)-1]['value'] : null;
|
||||
$completion_7d = null;
|
||||
foreach (array_reverse($completion_series) as $point) {
|
||||
$date = \DateTime::createFromFormat('Y-m-d', $point['date']);
|
||||
if ($date && $date <= (new \DateTime('-7 days'))) {
|
||||
$completion_7d = $point['value'];
|
||||
break;
|
||||
}
|
||||
}
|
||||
$latest_diffs[$type] = [
|
||||
'count_now' => $count_now,
|
||||
'count_7d' => $count_7d,
|
||||
'count_diff' => ($count_now !== null && $count_7d !== null) ? $count_now - $count_7d : null,
|
||||
'completion_now' => $completion_now,
|
||||
'completion_7d' => $completion_7d,
|
||||
'completion_diff' => ($completion_now !== null && $completion_7d !== null) ? $completion_now - $completion_7d : null,
|
||||
'label' => $label,
|
||||
];
|
||||
}
|
||||
return $this->render('admin/followup_graph.html.twig', [
|
||||
'stats' => $stats,
|
||||
'series' => $series,
|
||||
|
@ -118,6 +155,8 @@ class FollowUpController extends AbstractController
|
|||
'followup_icons' => FollowUpService::getFollowUpIcons(),
|
||||
'followup_overpass' => FollowUpService::getFollowUpOverpassQueries(),
|
||||
'completion_tags' => FollowUpService::getFollowUpCompletionTags(),
|
||||
'all_completion_data' => $all_completion_data,
|
||||
'latest_diffs' => $latest_diffs,
|
||||
]);
|
||||
}
|
||||
|
||||
|
|
|
@ -229,8 +229,11 @@ class PublicController extends AbstractController
|
|||
$base_tags = array_fill_keys($base_tags, '');
|
||||
|
||||
$commerce_overpass = $this->motocultrice->get_osm_object_data($place->getOsmKind(), $place->getOsmId());
|
||||
// Mettre à jour la Place à partir des infos Overpass
|
||||
$place->update_place_from_overpass_data($commerce_overpass);
|
||||
$this->entityManager->persist($place);
|
||||
$this->entityManager->flush();
|
||||
// Fusionner les tags de base avec les tags existants
|
||||
|
||||
$commerce_overpass['tags_converted'] = array_merge($base_tags, $commerce_overpass['tags_converted']);
|
||||
|
||||
// Trier les tags par ordre alphabétique des clés
|
||||
|
|
|
@ -20,6 +20,54 @@
|
|||
</a>
|
||||
</div>
|
||||
<p>Historique des objets suivis (nombre et complétion).</p>
|
||||
<div id="objets_modified_lately">
|
||||
{% set has_change = false %}
|
||||
<table class="table table-bordered table-sm align-middle table-sortable" id="latestDiffsTable">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>Thème</th>
|
||||
<th>Évolution du nombre</th>
|
||||
<th>Évolution de la complétion</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{% for type, diff in latest_diffs %}
|
||||
{% if diff.count_diff is not null and diff.count_diff != 0 %}
|
||||
{% set has_change = true %}
|
||||
<tr>
|
||||
<td>
|
||||
<a href="{{ path('admin_followup_theme_graph', {'insee_code': stats.zone, 'theme': type}) }}" class="fw-bold text-decoration-none">
|
||||
<i class="bi {{ followup_icons[type]|default('bi-question-circle') }}"></i> {{ diff.label }}
|
||||
</a>
|
||||
</td>
|
||||
<td>
|
||||
{% if diff.count_diff > 0 %}
|
||||
<i class="bi bi-arrow-up text-success"></i>
|
||||
{% else %}
|
||||
<i class="bi bi-arrow-down text-danger"></i>
|
||||
{% endif %}
|
||||
{{ diff.count_diff > 0 ? '+' ~ diff.count_diff : diff.count_diff }} objets
|
||||
</td>
|
||||
<td>
|
||||
{% if diff.completion_diff > 0 %}
|
||||
<i class="bi bi-arrow-up text-success"></i>
|
||||
{% elseif diff.completion_diff < 0 %}
|
||||
<i class="bi bi-arrow-down text-danger"></i>
|
||||
{% else %}
|
||||
<i class="bi bi-arrow-right text-secondary"></i>
|
||||
{% endif %}
|
||||
{{ diff.completion_diff > 0 ? '+' ~ diff.completion_diff : diff.completion_diff }}%
|
||||
</td>
|
||||
</tr>
|
||||
{% endif %}
|
||||
{% endfor %}
|
||||
{% if not has_change %}
|
||||
<tr><td colspan="3" class="text-muted">Aucun changement significatif cette semaine.</td></tr>
|
||||
{% endif %}
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
|
||||
{% for type, label in followup_labels %}
|
||||
<h2 id="title-{{ type }}"><i class="bi {{ followup_icons[type]|default('bi-question-circle') }} fs-2"></i> {{ label }}</h2>
|
||||
<canvas id="{{ type }}Chart" width="600" height="400"></canvas>
|
||||
|
@ -41,6 +89,8 @@
|
|||
'all_types': [type]
|
||||
} %}
|
||||
{% endfor %}
|
||||
<h2 class="mt-4">Comparaison de la complétion par thème</h2>
|
||||
<canvas id="multiCompletionChart" width="900" height="400"></canvas>
|
||||
<h2 class="mt-4">Données brutes</h2>
|
||||
<table class="table table-bordered table-striped">
|
||||
<thead>
|
||||
|
@ -71,10 +121,11 @@
|
|||
<script src="/js/chartjs/chart.umd.js"></script>
|
||||
<script src="/js/chartjs/chartjs-adapter-date-fns.js"></script>
|
||||
<script src="https://cdn.jsdelivr.net/npm/chartjs-plugin-datalabels@2"></script>
|
||||
<script src="/assets/js/table-sortable.js"></script>
|
||||
<script>
|
||||
const series = {{ series|json_encode|raw }};
|
||||
const followupIcons = {{ followup_icons|json_encode|raw }};
|
||||
const typeLabels = Object.assign({}, {{ followup_labels|json_encode|raw }});
|
||||
const typeLabels = Object.assign({}, {{ followup_labels|json_encode|raw }});
|
||||
document.addEventListener('DOMContentLoaded', function() {
|
||||
Object.keys(typeLabels).forEach(function(baseType) {
|
||||
const countData = (series[baseType + '_count'] || []).map(pt => ({ x: pt.date, y: pt.value }));
|
||||
|
@ -169,6 +220,7 @@
|
|||
canvas.parentNode.insertBefore(dateDiv, canvas.nextSibling);
|
||||
}
|
||||
});
|
||||
|
||||
});
|
||||
</script>
|
||||
{% endblock %}
|
|
@ -161,6 +161,12 @@
|
|||
<a href="{{ path('app_admin') }}" class="btn btn-secondary">
|
||||
<i class="bi bi-house"></i> Accueil admin
|
||||
</a>
|
||||
<a href="{{ path('app_public_stats_evolutions', {'insee_code': stats.zone}) }}" class="btn btn-outline-primary">
|
||||
<i class="bi bi-clock-history"></i> Évolutions temporelles
|
||||
</a>
|
||||
<a href="{{ path('admin_street_completion', {'insee_code': stats.zone}) }}" class="btn btn-outline-success">
|
||||
<i class="bi bi-signpost"></i> Complétion des rues
|
||||
</a>
|
||||
</div>
|
||||
|
||||
{% if josm_url %}
|
||||
|
|
|
@ -136,6 +136,9 @@
|
|||
<a href="{{ path('app_public_stats_evolutions', {'insee_code': stats.zone}) }}" class="btn btn-outline-info ms-2">
|
||||
<i class="bi bi-activity"></i> Évolutions des objets
|
||||
</a>
|
||||
<a href="{{ path('admin_street_completion', {'insee_code': stats.zone}) }}" class="btn btn-outline-success ms-2">
|
||||
<i class="bi bi-signpost"></i> Complétion des rues
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
{% if stats.population %}
|
||||
|
|
152
templates/admin/street_completion.html.twig
Normal file
152
templates/admin/street_completion.html.twig
Normal file
|
@ -0,0 +1,152 @@
|
|||
{% extends 'base.html.twig' %}
|
||||
|
||||
{% block title %}Complétion des rues - {{ stats.name }}{% endblock %}
|
||||
|
||||
{% block body %}
|
||||
<div class="container my-5">
|
||||
<h1>Complétion des rues à {{ stats.name }} ({{ stats.zone }})</h1>
|
||||
<a href="{{ path('app_admin_stats', {'insee_code': stats.zone}) }}" class="btn btn-secondary mb-3"><i class="bi bi-arrow-left"></i> Retour aux stats</a>
|
||||
<table class="table table-bordered table-striped table-hover table-responsive">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>Rue</th>
|
||||
<th class="text-end">Nombre de lieux</th>
|
||||
<th class="text-end">Complétion moyenne (%)</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{% for rue in rues %}
|
||||
<tr>
|
||||
<td>
|
||||
<a href="{{ path('app_public_street', {'cityId': insee_code, 'streetName': rue.name|url_encode }) }}">
|
||||
{{ rue.name }}
|
||||
</a>
|
||||
</td>
|
||||
<td class="text-end">{{ rue.count }}</td>
|
||||
<td class="text-end">{{ rue.avg_completion }}</td>
|
||||
</tr>
|
||||
{% else %}
|
||||
<tr><td colspan="3" class="text-muted">Aucune rue trouvée.</td></tr>
|
||||
{% endfor %}
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
|
||||
<div id="missing-streets-block" class="mt-5">
|
||||
<h2>Rues sans lieu associé (données OSM)</h2>
|
||||
<div id="missing-streets-loading">Chargement des rues OSM…</div>
|
||||
<ul id="missing-streets-list" class="list-group"></ul>
|
||||
</div>
|
||||
|
||||
<script>
|
||||
function buildOverpassAroundUrl(street, bbox) {
|
||||
// Requête Overpass autour de la rue (10m)
|
||||
// Utilise la requête de base de la motocultrice, mais avec around:10
|
||||
// Ici on cherche les commerces autour de la géométrie de la rue
|
||||
const query = `[out:json][timeout:25];
|
||||
way["name"="${street}"]["highway"](${bbox.join(",")});
|
||||
out body;
|
||||
>;
|
||||
out skel qt;
|
||||
node(around:10)[shop](if:t["name"])(if:t["addr:street"]=="${street}");
|
||||
out;`;
|
||||
return `https://overpass-turbo.eu/map.html?Q=${encodeURIComponent(query)}`;
|
||||
}
|
||||
function buildOsmUrl(street, bbox) {
|
||||
// Lien OSM pour la rue (zoom sur la bbox)
|
||||
const [s, w, n, e] = bbox;
|
||||
const lat = (parseFloat(s) + parseFloat(n)) / 2;
|
||||
const lon = (parseFloat(w) + parseFloat(e)) / 2;
|
||||
return `https://www.openstreetmap.org/#map=18/${lat}/${lon}`;
|
||||
}
|
||||
function fetchStreetBbox(street, insee, cb) {
|
||||
// Requête Overpass pour trouver la bbox de la rue
|
||||
const query = `[out:json][timeout:25];area["ref:INSEE"="${insee}"][admin_level=8];way["name"="${street}"]["highway"](area);out bb;`;
|
||||
fetch('https://overpass-api.de/api/interpreter', {
|
||||
method: 'POST',
|
||||
body: query
|
||||
})
|
||||
.then(r => r.json())
|
||||
.then(data => {
|
||||
if (data.elements && data.elements.length > 0 && data.elements[0].bounds) {
|
||||
const b = data.elements[0].bounds;
|
||||
cb([b.south, b.west, b.north, b.east]);
|
||||
}
|
||||
});
|
||||
}
|
||||
document.addEventListener('DOMContentLoaded', function() {
|
||||
// Récupérer la liste des rues déjà présentes dans le tableau
|
||||
const ruesConnues = new Set([
|
||||
{% for rue in rues %}'{{ rue.name|e('js') }}',{% endfor %}
|
||||
]);
|
||||
// Construire la requête Overpass pour toutes les rues de la ville
|
||||
const insee = '{{ insee_code }}';
|
||||
const overpassQuery = `
|
||||
[out:json][timeout:25];
|
||||
area["ref:INSEE"="${insee}"]->.searchArea;
|
||||
way["highway"]["name"](area.searchArea);
|
||||
out tags;
|
||||
`;
|
||||
fetch('https://overpass-api.de/api/interpreter', {
|
||||
method: 'POST',
|
||||
body: overpassQuery,
|
||||
headers: { 'Content-Type': 'text/plain' }
|
||||
})
|
||||
.then(r => r.json())
|
||||
.then(data => {
|
||||
const allStreets = new Set();
|
||||
(data.elements || []).forEach(el => {
|
||||
if (el.tags && el.tags.name) {
|
||||
allStreets.add(el.tags.name);
|
||||
}
|
||||
});
|
||||
// Filtrer les rues qui n'ont pas de lieu associé
|
||||
const missing = Array.from(allStreets).filter(nom => !ruesConnues.has(nom));
|
||||
const ul = document.getElementById('missing-streets-list');
|
||||
ul.innerHTML = '';
|
||||
if (missing.length === 0) {
|
||||
ul.innerHTML = '<li class="list-group-item text-success">Toutes les rues OSM ont au moins un lieu associé.</li>';
|
||||
} else {
|
||||
missing.sort((a, b) => a.localeCompare(b, 'fr'));
|
||||
missing.forEach(nom => {
|
||||
const li = document.createElement('li');
|
||||
li.className = 'list-group-item d-flex justify-content-between align-items-center';
|
||||
li.setAttribute('data-street-name', nom);
|
||||
li.textContent = nom;
|
||||
const buttons = document.createElement('span');
|
||||
buttons.innerHTML = `
|
||||
<a href="#" class="btn btn-outline-secondary btn-sm osm-link" title="Voir la rue sur OpenStreetMap" target="_blank" data-street="${nom}">
|
||||
<i class="bi bi-globe"></i>
|
||||
</a>
|
||||
<a href="#" class="btn btn-outline-primary btn-sm overpass-link ms-1" title="Lieux possibles sur Overpass" target="_blank" data-street="${nom}">
|
||||
<i class="bi bi-ev-station"></i>
|
||||
</a>
|
||||
`;
|
||||
li.appendChild(buttons);
|
||||
ul.appendChild(li);
|
||||
});
|
||||
}
|
||||
document.getElementById('missing-streets-loading').style.display = 'none';
|
||||
})
|
||||
.catch(e => {
|
||||
document.getElementById('missing-streets-loading').textContent = 'Erreur lors de la récupération des rues OSM.';
|
||||
});
|
||||
document.querySelectorAll('#missing-streets-list li').forEach(function(li) {
|
||||
const street = li.getAttribute('data-street-name');
|
||||
const insee = '{{ insee_code }}';
|
||||
li.querySelector('.osm-link').addEventListener('click', function(e) {
|
||||
e.preventDefault();
|
||||
fetchStreetBbox(street, insee, function(bbox) {
|
||||
window.open(buildOsmUrl(street, bbox), '_blank');
|
||||
});
|
||||
});
|
||||
li.querySelector('.overpass-link').addEventListener('click', function(e) {
|
||||
e.preventDefault();
|
||||
fetchStreetBbox(street, insee, function(bbox) {
|
||||
window.open(buildOverpassAroundUrl(street, bbox), '_blank');
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
</script>
|
||||
{% endblock %}
|
|
@ -8,7 +8,14 @@
|
|||
|
||||
<div class="card shadow-sm">
|
||||
<div class="card-body">
|
||||
<h1 class="card-title h2 mb-4">{{ 'display.welcome'|trans }} {{ commerce_overpass.tags_converted.name }} - {{ commerce.stats.name }}</h1>
|
||||
<h1 class="card-title h2 mb-4">
|
||||
{{ 'display.welcome'|trans }}
|
||||
{% set main_tag = commerce.mainTag is defined and commerce.mainTag is not empty ? commerce.mainTag : (commerce_overpass.tags_converted.main_tag is defined ? commerce_overpass.tags_converted.main_tag : null) %}
|
||||
{% if main_tag %}
|
||||
{{ tag_emoji(main_tag) }}
|
||||
{% endif %}
|
||||
{{ commerce_overpass.tags_converted.name }} - {{ commerce.stats.name }}
|
||||
</h1>
|
||||
<div id="map" style="height: 400px; width: 100%;" class="rounded mb-4"></div>
|
||||
{% include 'public/edit/address.html.twig' %}
|
||||
|
||||
|
|
|
@ -15,19 +15,36 @@
|
|||
<thead>
|
||||
<tr>
|
||||
<th>Type</th>
|
||||
<th>Décompte actuel</th>
|
||||
<th class="text-end">Décompte actuel</th>
|
||||
{% for p in periods %}
|
||||
<th>Évolution sur {{ p }}</th>
|
||||
<th class="text-end">Évolution sur {{ p }}</th>
|
||||
{% endfor %}
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{% for type, evo in evolutions %}
|
||||
<tr>
|
||||
<td>{{ type }}</td>
|
||||
<td>{{ evo.now }}</td>
|
||||
<td style="min-width: 15rem;">
|
||||
<a href="{{ path('admin_followup_theme_graph', {'insee_code': stats.zone, 'theme': type}) }}" class="btn btn-outline-secondary btn-sm ms-2"
|
||||
title="Voir le graphe détaillé">
|
||||
<i class="bi bi-bar-chart"></i>
|
||||
</a>
|
||||
{{ type }}
|
||||
</td>
|
||||
<td class="text-end">{{ evo.now }}</td>
|
||||
{% for p in periods %}
|
||||
<td>{% if evo[p] is not null %}{{ evo[p] > 0 ? '+' ~ evo[p] : evo[p] }}{% else %}<span class="text-muted">-</span>{% endif %}</td>
|
||||
<td class="text-end{% if evo[p] is not null and evo[p] != 0 %} bg-success-subtle{% if evo[p] < 0 %} bg-danger-subtle{% endif %}{% endif %}">
|
||||
{% if evo[p] is not null %}
|
||||
{% if evo[p] > 0 %}
|
||||
<i class="bi bi-arrow-up text-success"></i>
|
||||
{% elseif evo[p] < 0 %}
|
||||
<i class="bi bi-arrow-down text-danger"></i>
|
||||
{% endif %}
|
||||
{{ evo[p] > 0 ? '+' ~ evo[p] : evo[p] }}
|
||||
{% else %}
|
||||
<span class="text-muted">-</span>
|
||||
{% endif %}
|
||||
</td>
|
||||
{% endfor %}
|
||||
</tr>
|
||||
{% else %}
|
||||
|
|
|
@ -8,7 +8,7 @@
|
|||
|
||||
{% block body %}
|
||||
<div class="container">
|
||||
<h1>Modification du lieu {{ commerce.tags_converted.name }}!</h1>
|
||||
<h1>Modification du lieu {{ commerce.tags_converted.name is defined and commerce.tags_converted.name is not empty ? commerce.tags_converted.name : '(sans nom)' }}!</h1>
|
||||
|
||||
{% if status == "Les tags ont été mis à jour avec succès" %}
|
||||
<span class="badge bg-success p-4">{{status}}</span>
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue