Add mobile overflow nav, law check feature, and favicon

Nav: "Fonctionnement" and "Flyer QR" restored on mobile via a ···
overflow dropdown (click-outside closes); other links always visible

Law check: new POST /api/check-law endpoint (5/min, 20/h) calls
mistral-small with a new LAW_CHECK_PROMPT to detect if a proposal is
already covered by French or EU law. Informational only, non-blocking.
Frontend: "Vérifier le cadre légal existant" button below textarea in
home.tsx; result displayed with Scale icon; resets on content change.

Favicon: replaced placeholder red square with a petrol rounded square
containing a serif "V" (for Voix) in warm cream — matches brand palette

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
2026-06-23 17:57:29 +02:00
parent 5c5152a387
commit 2ef14ac765
6 changed files with 179 additions and 7 deletions
+20 -1
View File
@@ -52,7 +52,7 @@ from database import (
get_consultation_contributions, delete_consultation,
check_ip_blacklist, record_ip_rejection, get_ip_blacklist, remove_ip_blacklist,
)
from ai_agent import filter_idea, synthesize_ideas
from ai_agent import filter_idea, synthesize_ideas, check_existing_law
# ─── Logging ────────────────────────────────────────────────────────────────
@@ -1216,6 +1216,25 @@ def admin_delete_consultation(slug: str):
return jsonify({"ok": True})
@app.post("/api/check-law")
@limiter.limit("5 per minute;20 per hour")
def check_law_route():
"""
Vérifie si une proposition est déjà couverte par une loi française ou européenne.
Endpoint public, rate-limité. Non bloquant — retourne une note informative.
"""
if not request.is_json:
return jsonify({"error": "bad_request", "message": "Content-Type doit être application/json."}), 400
data = request.get_json(silent=True) or {}
content = sanitize_text(str(data.get("content", ""))).strip()
if not content or len(content) < 10:
return jsonify({"error": "validation_error", "message": "Contenu trop court."}), 400
if len(content) > 1000:
return jsonify({"error": "validation_error", "message": "Contenu trop long (max 1000 caractères)."}), 400
note = check_existing_law(content)
return jsonify({"note": note})
@app.get("/api/admin/ip-blacklist")
@require_admin
def admin_list_ip_blacklist():