From 63a7ff3d895454812a336e65fc4f9fa0dd8b61c1 Mon Sep 17 00:00:00 2001 From: Colin Maudry Date: Wed, 1 Jul 2026 10:17:25 +0200 Subject: [PATCH] =?UTF-8?q?Petites=20am=C3=A9liorations=20de=20formulation?= =?UTF-8?q?=20et=20d'UI?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .gitignore | 1 + src/app.py | 5 +++-- src/figures.py | 6 +++++- src/pages/_apropos_shell.py | 22 +++++++------------ src/pages/a_propos/abonnement.py | 34 ++++++++++++++++++----------- src/pages/compte_abonnement.py | 8 ++++--- src/roadmap/ui.py | 5 +++++ src/utils/frontend.py | 37 ++++++++++++++++++++++++++++++++ uv.lock | 2 +- 9 files changed, 87 insertions(+), 33 deletions(-) diff --git a/.gitignore b/.gitignore index df406fa..74b1d52 100644 --- a/.gitignore +++ b/.gitignore @@ -22,3 +22,4 @@ users.sqlite # LLM plugins .superpowers/ .codegraph/ +.claude diff --git a/src/app.py b/src/app.py index f8db41b..fdcf7a9 100644 --- a/src/app.py +++ b/src/app.py @@ -312,8 +312,9 @@ def toggle_navbar_collapse(n, is_open): ) def _auth_nav(_): if current_user.is_authenticated: - email = current_user.email - display = email if len(email) <= 30 else email[:27] + "..." + # email = current_user.email + # display = email if len(email) <= 30 else email[:27] + "..." + display = "★★★" return dbc.NavItem(dbc.NavLink(display, href="/compte/admin")) return dbc.NavItem(dbc.NavLink("Connexion", href="/connexion")) diff --git a/src/figures.py b/src/figures.py index 92bc86b..2801db3 100644 --- a/src/figures.py +++ b/src/figures.py @@ -132,10 +132,14 @@ def get_sources_tables(source_path) -> html.Div: + pl.lit('">') + pl.col("nom") + pl.lit("") - ).alias("nom") + ).alias("nom"), ) dff = dff.drop("url", "unique") dff = dff.sort(by=["nb_marchés"], descending=True) + dff = dff.with_columns( + pl.col("nb_marchés").map_elements(format_number, return_dtype=pl.String), + pl.col("nb_acheteurs").map_elements(format_number, return_dtype=pl.String), + ) columns = { "nom": "Nom de la source", diff --git a/src/pages/_apropos_shell.py b/src/pages/_apropos_shell.py index 1629f39..3b40fe7 100644 --- a/src/pages/_apropos_shell.py +++ b/src/pages/_apropos_shell.py @@ -4,20 +4,8 @@ from dash import Input, Output, State, callback, html SECTIONS = [ {"key": "presentation", "label": "Présentation", "href": "/a-propos/presentation"}, {"key": "explorer", "label": "Explorer le projet", "href": "/a-propos/explorer"}, - {"key": "sources", "label": "Sources de données", "href": "/a-propos/sources"}, - {"key": "qualite", "label": "Qualité des données", "href": "/a-propos/qualite"}, - { - "key": "donnees-brutes", - "label": "Données brutes", - "href": "/a-propos/donnees-brutes", - }, - {"key": "contribuer", "label": "Contribuer", "href": "/a-propos/contribuer"}, - {"key": "contact", "label": "Contact", "href": "/a-propos/contact"}, - { - "key": "mentions-legales", - "label": "Mentions légales", - "href": "/a-propos/mentions-legales", - }, + {"key": "donnees", "label": "Données", "href": "/a-propos/donnees"}, + # {"key": "contribuer", "label": "Contribuer", "href": "/a-propos/contribuer"}, { "key": "abonnement", "label": "Abonnement", @@ -28,6 +16,12 @@ SECTIONS = [ "label": "Roadmap", "href": "/a-propos/roadmap", }, + {"key": "contact", "label": "Contact", "href": "/a-propos/contact"}, + { + "key": "mentions-legales", + "label": "Mentions légales", + "href": "/a-propos/mentions-legales", + }, ] diff --git a/src/pages/a_propos/abonnement.py b/src/pages/a_propos/abonnement.py index 3daa182..113a51b 100644 --- a/src/pages/a_propos/abonnement.py +++ b/src/pages/a_propos/abonnement.py @@ -11,6 +11,11 @@ register_page( image_url=META_CONTENT["image_url"], ) +abonnement_features = dcc.Markdown(""" + - sauvegarde de vues dans la page Tableau + - vote pour les fonctionnalités à développer en priorité (une fois la période d'essai terminée) + """) + def layout(**_): contenu = html.Div( @@ -18,42 +23,46 @@ def layout(**_): html.H2("Abonnement"), dcc.Markdown( """ -L'accès aux fonctionnalités de base de colibre est gratuit et sans inscription. -Un abonnement payant donne accès à des fonctionnalités supplémentaires qui nécessitent la création d'un compte. +L'accès aux fonctionnalités de base de colibre est gratuit et sans inscription. Il est également possible de créer un compte gratuitement via le menu [Connexion](/connexion). Une fois le compte créé, +il est possible de souscrire à un abonnement mensuel qui donne accès à des fonctionnalités supplémentaires. Cet abonnement s'adresse tant aux professionnel·les qu'aux particuliers. """ ), html.H4("Fonctionnalités incluses"), dcc.Markdown( """ -Les fonctionnalités accessibles aux abonnés évoluent au fil du développement du service. -La liste à jour est visible depuis la [page d'abonnement](/compte/abonnement). +Les fonctionnalités accessibles aux abonné·es évoluent au fil du développement du service. + +Fonctionnalités réservées aux abonné·es : """ ), + abonnement_features, + dcc.Markdown(""" + Les fonctionnalités en cours de développement et soumises au vote sont visibles dans la section [Roadmap](/a-propos/roadmap)."""), html.H4("Tarifs"), dcc.Markdown( """ Deux formules sont proposées : - **Abonnement** — 20 € HT / mois (soit 24 € TTC) -- **Abonnement de soutien** — 50 € HT / mois (soit 60 € TTC) — mêmes fonctionnalités, contribution renforcée au projet +- **Abonnement de soutien ✊** — 50 € HT / mois (soit 60 € TTC) — mêmes fonctionnalités, contribution renforcée au projet -La TVA applicable est de 20 %. Les prix TTC sont affichés lors de la souscription. +La TVA applicable en France est de 20 %. """ ), html.H4("Période d'essai"), dcc.Markdown( """ -Une période d'essai gratuite peut être proposée lors de la souscription. +Une période d'essai gratuite est proposée lors de la souscription. Sa durée est indiquée avant validation. Aucun prélèvement n'est effectué pendant cette période. À son terme, l'abonnement est activé et facturé automatiquement. -La période d'essai est accordée une seule fois par utilisateur. +La période d'essai est accordée une seule fois par compte. """ ), html.H4("Facturation et paiement"), dcc.Markdown( """ -L'abonnement est facturé mensuellement, à la date anniversaire de la souscription. +L'abonnement est facturé mensuellement, à la date anniversaire de la souscription, et donne lieu à l'émission d'une facture visible sur le compte de l'abonné·e. Le paiement est traité par [Frisbii](https://www.frisbii.com), prestataire européen de paiement en ligne. Les coordonnées bancaires sont conservées exclusivement par Frisbii et ne sont pas transmises à colibre. """ @@ -65,15 +74,16 @@ L'abonnement peut être résilié à tout moment depuis l'espace [Mon compte](/c La résiliation prend effet à la fin de la période mensuelle en cours : l'accès aux fonctionnalités payantes est maintenu jusqu'à cette date, sans remboursement au prorata. """ ), - html.H4("Données personnelles"), + html.H4("Données recueillies"), dcc.Markdown( """ -La gestion de l'abonnement implique le traitement de données personnelles, réparties entre colibre et Frisbii (prestataire de paiement). +La gestion de l'abonnement implique le traitement de données, réparties entre colibre et Frisbii (prestataire de paiement). Conformément au RGPD, colibre ne receuille que les données strictement nécessaire +au bon fonctionnement du site et de la facturation. **Données stockées par colibre :** - Adresse e-mail (identification du compte) -- Numéro SIRET (si renseigné, conservé pour pré-remplir les futures souscriptions) +- Numéro SIRET (optionnel, si renseigné, conservé pour pré-remplir les futures souscriptions) **Données stockées par Frisbii :** diff --git a/src/pages/compte_abonnement.py b/src/pages/compte_abonnement.py index 4e78d2d..f10b77b 100644 --- a/src/pages/compte_abonnement.py +++ b/src/pages/compte_abonnement.py @@ -4,6 +4,7 @@ from flask_login import current_user from src.pages._compte_shell import account_guard, account_shell from src.subscriptions import db, plans +from src.utils.frontend import format_date_french register_page( __name__, @@ -129,7 +130,7 @@ def _explainer(): def _active_view(row): meta = plans.plan_meta(row["plan"]) or {"label": row["plan"]} - end = row["current_period_end"] + end = format_date_french(row["current_period_end"]) blocks = [html.H3(meta["label"], className="mb-3")] if row["status"] == "pending": @@ -167,7 +168,7 @@ def _active_view(row): dbc.Alert(f"Abonnement résilié, actif jusqu'au {end}.", color="warning") ) else: - blocks.append(html.P(f"Prochain renouvellement : {end}")) + blocks.append(html.P(f"Prochaine facturation : {end}")) if row["status"] in ("pending", "trial", "active"): blocks.append( @@ -182,7 +183,8 @@ def _active_view(row): return html.Div(blocks) -def _resiliation_modal(end): +def _resiliation_modal(end_raw): + end = format_date_french(end_raw) if end_raw else None if end: body_text = ( f"Êtes-vous sûr de vouloir mettre fin à votre abonnement ? " diff --git a/src/roadmap/ui.py b/src/roadmap/ui.py index 54a0b37..551f626 100644 --- a/src/roadmap/ui.py +++ b/src/roadmap/ui.py @@ -121,6 +121,11 @@ def roadmap_content( body.append(dbc.ListGroup(_en_cours_items(issues["en_cours"]), className="mb-4")) can_vote = editable and balance is not None and balance > 0 body.append(html.H3("Au vote")) + body.append( + html.P( + "Les abonné·es votent pour les fonctionnalités à développer en priorité." + ) + ) body.append( html.Div( dbc.ListGroup( diff --git a/src/utils/frontend.py b/src/utils/frontend.py index 3b24da8..82ac647 100644 --- a/src/utils/frontend.py +++ b/src/utils/frontend.py @@ -1,6 +1,43 @@ +from datetime import datetime + from src.utils.data import DATA_SCHEMA +def format_date_french(date_input) -> str: + """Format a date as 'jour mois' (e.g., '1er janvier', '15 décembre').""" + if isinstance(date_input, str): + try: + date_obj = datetime.fromisoformat(date_input) + except (ValueError, TypeError): + return str(date_input) + elif isinstance(date_input, datetime): + date_obj = date_input + else: + return str(date_input) + + day = date_obj.day + month_names = [ + "janvier", + "février", + "mars", + "avril", + "mai", + "juin", + "juillet", + "août", + "septembre", + "octobre", + "novembre", + "décembre", + ] + month = month_names[date_obj.month - 1] + + if day == 1: + return f"1er {month}" + else: + return f"{day} {month}" + + def get_button_properties(height): if height > 65000: download_disabled = True diff --git a/uv.lock b/uv.lock index 2845f72..72b02dc 100644 --- a/uv.lock +++ b/uv.lock @@ -582,7 +582,7 @@ wheels = [ [[package]] name = "colibre" -version = "2.8.1" +version = "3.0.0" source = { virtual = "." } dependencies = [ { name = "authlib" },