Commit Graph

683 Commits

Author SHA1 Message Date
Colin Maudry e904ec4a7d feat(abonnement): page publique avec cards et bouton Je m'abonne conditionnel
La page publique /a-propos/abonnement affiche désormais les cards de
formule (informatives, sans bouton par card), un explainer des
fonctionnalités, et un unique CTA "Je m'abonne" dont la cible dépend
de l'état d'authentification/abonnement. Retrait de la sous-section
"Fonctionnalités incluses" des CGU (doublon avec l'explainer).

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-07-04 23:21:19 +02:00
Colin Maudry 6464baebd2 feat(auth): auto-login à la validation d'email, redirection vers mes-infos
Quand l'utilisateur valide son email, il est maintenant connecté automatiquement
via login_user() et redirigé directement vers /compte/abonnement/mes-infos
au lieu de /connexion?verified=1.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-07-04 23:16:29 +02:00
Colin Maudry cc2b34e84a feat(abonnement): linkedin_button paramétrable par next, inscription vers mes-infos
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-07-04 23:13:11 +02:00
Colin Maudry e44e2940fc Tous les libellés de sources apparaissent dans la matrice 2026-07-04 20:50:30 +02:00
Colin Maudry c748cb944e Déplacement de la matrice de doublon vers Données 2026-07-04 20:44:41 +02:00
Colin Maudry c9ece32601 Rédactionnel, tableau étapes dans Donnéees 2026-07-04 20:19:51 +02:00
Colin Maudry 6256adf826 Taille du point de l'org 2026-07-03 23:31:24 +02:00
Colin Maudry 1551faaf0c Normaliser les échecs d'écriture set_cell en ValueError
- IntegrityError (ex: email déjà utilisé) est désormais capturée et
  reconvertie en ValueError, pour rester dans le funnel d'alerte
  existant du callback admin au lieu de faire planter le callback Dash.
- Une UPDATE qui touche 0 ligne (ligne supprimée entre le chargement du
  tableau et la soumission de l'édition) lève désormais une ValueError
  au lieu d'être silencieusement traitée comme un succès (ce qui aurait
  créé un log d'audit trompeur).

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-07-03 23:10:32 +02:00
Colin Maudry eb8f54dbf2 fix(figures): calculer center/zoom côté serveur au lieu d'utiliser bounds
La prop `bounds` de dash-leaflet lève une exception JS (TypeError sur
.equals() avec une valeur précédente indéfinie) au tout premier montage
du composant Map — confirmé via la stack trace navigateur, reproductible
même en dernière version (1.1.3). Cette exception laissait le clustering
dans un état incohérent (declustering impossible avant un zoom complet
suivi d'un dézoom).

bounds_to_center_zoom calcule un center/zoom approximatifs (projection
Web Mercator, taille de conteneur supposée) pour cadrer l'organisme et
ses contreparties, en évitant complètement la prop `bounds` bugguée.
Cadrage moins précis qu'un vrai fitBounds() navigateur, mais robuste.
2026-07-03 22:54:21 +02:00
Colin Maudry 9d3b0dd068 fix(figures): réactiver l'animation du fitBounds sur les cartes organisme
animate: False (ajouté pour contourner le point organisme surdimensionné,
désormais résolu via un rendu en icône) empêchait apparemment la
bibliothèque de clustering de recalculer correctement son arbre de
clusters par niveau de zoom au montage, bloquant le declustering des
contreparties jusqu'à un zoom complet suivi d'un dézoom.
2026-07-03 22:34:26 +02:00
Colin Maudry e763a0b878 Taille du point de l'org 2026-07-03 22:29:04 +02:00
Colin Maudry 86977bd37e fix(figures): rendre le point de l'organisme en icône plutôt qu'en circleMarker
bringToFront() sur un circleMarker SVG (overlayPane) ne pouvait pas passer
devant les bulles de cluster (markerPane, toujours au-dessus dans l'ordre
des panes Leaflet), et le rendu SVG se redimensionnait visuellement pendant
l'animation de zoom. Le point is_home est maintenant un L.marker + divIcon
(comme les clusters), avec zIndexOffset élevé : toujours au premier plan et
taille constante quel que soit le zoom.
2026-07-03 22:17:54 +02:00
Colin Maudry f1c28acc96 feat(figures): mettre en avant le point de l'organisme consulté sur la carte
Marque is_home=True sur les marqueurs du home_type (acheteur ou titulaire
selon la page) dans get_org_location_map. Côté client, pointToLayer donne
à ce point un rayon légèrement plus grand (8 vs 5) et le ramène toujours
au premier plan (bringToFront), indépendamment de l'ordre des couches.
2026-07-03 22:08:43 +02:00
Colin Maudry a96f772d7a fix(figures): désactiver l'animation du fitBounds sur les cartes organisme
Un fitBounds animé interrompu (deux Input sur pathname+année pouvant
re-déclencher le callback quasi simultanément) laissait le transform CSS
de zoom de Leaflet figé à une grande échelle (ex: scale(16)), faisant
apparaître les marqueurs individuels démesurément gros sur /acheteurs et
/titulaires. Constaté en vérification manuelle sur test.colibre.fr.
2026-07-03 16:43:04 +02:00
Colin Maudry 22dd49f337 docs(figures): corriger le commentaire d'ordre des couches GeoJSON
Le commentaire affirmait à tort que l'organisme consulté est toujours
peint au-dessus de sa contrepartie ; l'ordre est en réalité fixe
(titulaire puis acheteur), indépendant de home_type. Relevé par la
revue finale de branche.
2026-07-03 16:15:13 +02:00
Colin Maudry acb8d5e0d1 feat(titulaire): afficher les acheteurs sur la carte de la fiche titulaire
Supprime point_on_map, devenue inutilisée après migration des deux pages
vers get_org_location_map.
2026-07-03 16:10:02 +02:00
Colin Maudry d201b7a3c6 feat(acheteur): afficher les titulaires sur la carte de la fiche acheteur 2026-07-03 16:05:05 +02:00
Colin Maudry 8b69fee31c feat(figures): ajouter get_org_location_map (carte cluster organisme + contrepartie) 2026-07-03 15:59:47 +02:00
Colin Maudry 21d3a30d8b refactor(figures): extraite build_org_markers pour réutilisation 2026-07-03 15:55:29 +02:00
Colin Maudry caf800e5e8 fix(admin): guard find_changed_cell against cross-table schema mismatch
When switching tables, the table-switch branch writes fresh data for the
new table, which re-fires the same callback with data_previous still
holding the old table's rows. find_changed_cell only checked row count
before diffing, so if the two tables happened to have the same number of
rows it would zip mismatched-schema dicts and report a spurious changed
cell (usually the PK column), producing a confusing red alert right after
switching tables.
2026-07-03 14:46:12 +02:00
Colin Maudry d229b58f3b feat(admin): replace dedicated pages with a generic table editor at /admin 2026-07-03 14:19:01 +02:00
Colin Maudry cf23863a30 refactor(admin): apply formatting fixes from linter hooks 2026-07-03 13:04:15 +02:00
Colin Maudry cfcbfe9768 Ajouter la colonne handle Frisbii à l'historique d'abonnements (admin) 2026-07-03 11:55:51 +02:00
Colin Maudry 43122a7c12 Rédactionnel dans la page d'abonnements 2026-07-03 11:43:41 +02:00
Colin Maudry 5d56f8180a feat(admin): add /admin/journal audit log page 2026-07-03 10:24:40 +02:00
Colin Maudry f8d1e60519 feat(admin): add /admin/user/<user_id> detail page 2026-07-03 10:20:17 +02:00
Colin Maudry 96358423df feat(admin): add /admin user list page 2026-07-03 10:13:04 +02:00
Colin Maudry e483d7af4d feat(admin): add subscription-status mutation route
Wires is_admin(), SUBSCRIPTION_STATUSES/get_current/set_status, and
log_action() into POST /admin/actions/subscription-status: validates
the requested status and that subscription_id matches the user's
current subscription, applies the change, and logs an audit entry.
Registers the admin blueprint in init_auth() and documents ADMIN_EMAIL
in .template.env.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-07-03 09:46:22 +02:00
Colin Maudry 2f2cd151fb feat(admin): add is_admin() access guard
Implement access control function for admin panel. Returns True only if
ADMIN_EMAIL env var is set, user is authenticated, and email matches
case-insensitively.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-07-03 09:41:57 +02:00
Colin Maudry fb62c28e10 feat(admin): add admin_actions audit table and log/list functions
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-07-03 09:38:33 +02:00
Colin Maudry 0209ec0d5c feat(admin): add subscription history, status override, and statuses constant
Adds four new DB helpers for the admin panel:
- SUBSCRIPTION_STATUSES: tuple of valid subscription statuses
- list_by_user(): retrieve all subscriptions for a user (newest first)
- set_status(): override a subscription's status and updated_at timestamp
- get_subscriber_state(): public wrapper for internal _get_state()

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-07-03 09:34:09 +02:00
Colin Maudry 9ea8bee940 feat(admin): add list_users() to auth DB layer
Implements list_users(limit: int = 1000) -> list[sqlite3.Row] in the auth
DB layer to retrieve all users ordered by creation date (most recent first).

- Returns all users, optionally capped at limit (default 1000)
- Orders by created_at DESC to show newest users first
- Follows existing DB layer patterns using get_conn().execute().fetchall()

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-07-03 09:29:03 +02:00
Colin Maudry 0b88a65414 Utiliser le Checkout API pour la session de souscription (accept_url/cancel_url)
hosted_page_links.payment_info (POST /v1/subscription) n'honore pas
accept_url/cancel_url malgré la doc, même passés en query string (constaté
en test sur test.colibre.fr). On génère maintenant la page de paiement via
POST /v1/session/subscription (Checkout API), qui accepte ces champs dans
son body et redirige effectivement le navigateur — même mécanisme déjà
fonctionnel pour l'ajout de moyen de paiement (create_recurring_session).

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-07-02 18:21:05 +02:00
Colin Maudry 14ae1a7bfb Gérer l'affichage d'un abonnement expiré (statut Frisbii "expired")
_show_active_view affichait par erreur la vue "abonnement actif" (avec un
faux "Prochaine facturation") pour un abonnement expiré. Un abonnement
expiré bascule maintenant sur l'écran de re-souscription, avec une alerte
dédiée. Jamais remarqué jusqu'ici car les webhooks Frisbii ne remontaient
pas ce changement de statut.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-07-02 18:13:12 +02:00
Colin Maudry 1159697c0e Fix redirection Frisbii après ajout de moyen de paiement au premier abonnement
accept_url/cancel_url n'existent pas dans le schéma de POST /v1/subscription
(ils y sont silencieusement ignorés) : Frisbii attend ces paramètres en
query string sur le lien hosted_page_links.payment_info retourné. Ajout
d'un warning loggé sur signature de webhook invalide, jusque-là silencieuse.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-07-02 17:56:19 +02:00
Colin Maudry ea20491e9c Changements rédactionnels : abonnement par virement, avantage abonnements 2026-07-02 16:50:33 +02:00
Colin Maudry d08f7aeeac Nombre de jours d'essai en dur plutot que via appels HTTP 2026-07-02 13:55:22 +02:00
Colin Maudry 1bdcaaf5d8 Réactivation de l'env MAIL_SUPPRESS_SEND 2026-07-02 13:42:05 +02:00
Colin Maudry a38b2d5e71 Remplacement du Loading... screen 2026-07-02 12:03:50 +02:00
Colin Maudry 74afb7aef8 Nettoayge 2026-07-02 11:46:42 +02:00
Colin Maudry cd107a0213 Refactor structure modules pages/compte, petits changements de texte 2026-07-02 10:53:01 +02:00
Colin Maudry 9ff2373ccb decp.info => colibre 2026-07-01 18:00:36 +02:00
Colin Maudry 126834c7d4 fix(subscriptions): un abonnement en échec (failed) ne bloque plus le réabonnement 2026-07-01 16:56:34 +02:00
Colin Maudry 872fc33dd7 feat(subscriptions): cancel/webhook/add_payment_callback sur get_current et customer_known 2026-07-01 16:41:57 +02:00
Colin Maudry 0ffe647657 feat(subscriptions): subscribe() génère et transmet le handle, mark_failed en cas d'échec 2026-07-01 14:37:25 +02:00
Colin Maudry 179e6e436d feat(subscriptions): create_subscription_session prend le handle en paramètre 2026-07-01 14:34:09 +02:00
Colin Maudry 64158e9080 feat(subscriptions): historique multi-lignes + subscriber_state + handle abo-{user_id}-N
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-07-01 14:29:47 +02:00
Colin Maudry a2c20adb4e perf: paginer/agréger les pages acheteur et titulaire côté DuckDB
Les pages acheteur/titulaire chargeaient l'intégralité des marchés d'une
organisation dans un dcc.Store côté client (jusqu'à 16k lignes pour les
plus gros acheteurs), envoyée sur le réseau à chaque interaction. Le
tableau, le top 10 et l'histogramme des distances récupèrent maintenant
leurs données via des requêtes DuckDB scopées (pagination/agrégation
poussées en SQL), sur le modèle déjà utilisé par la page tableau.

Corrige aussi le CLS des mêmes pages en réservant l'espace des
conteneurs remplis par callback (carte, top 10, histogramme).

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-07-01 13:46:44 +02:00
Colin Maudry a1bf0b381b fix: reduce subscribe button width to fit content
Adjust the width of the 'S'abonner' button to match the label size
using CSS width: fit-content property.

Co-Authored-By: Claude Haiku 4.5 <noreply@anthropic.com>
2026-07-01 11:10:19 +02:00
Colin Maudry 63a7ff3d89 Petites améliorations de formulation et d'UI 2026-07-01 10:17:25 +02:00