refactor(tableau): barre d'outils compacte sur une ligne + légende d'aide

Barre d'outils repensée au-dessus du tableau :
- boutons compacts (size=sm) sur une seule rangée + ligne d'infos discrète
  en dessous (2 lignes au total sur desktop)
- libellés explicites (Sauvegarder la vue, Partager la vue, Télécharger
  (Excel)), espacement homogène, couleurs conformes à la charte
  (neutre=secondary, Réinitialiser=danger outline)
- Mode d'emploi rejeté en fin de barre via CSS order (DOM/callbacks intacts)
- .table-menu (3 autres pages) laissée intacte

Téléchargement bloqué (>65 000 lignes) : la raison s'affiche en clair dans
la ligne d'infos (fiable cross-browser) plutôt que dans une infobulle sur
bouton désactivé. Override scopé au tableau via update_table ; acheteur/
titulaire non touchés.

Mode d'emploi : légende en tête listant chaque bouton reproduit (inerte)
en face de sa fonction.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
Colin Maudry
2026-06-30 14:33:28 +02:00
parent ce3c3a5e6f
commit 78534000a0
2 changed files with 169 additions and 19 deletions
+49
View File
@@ -320,6 +320,55 @@ input[type="email"] {
margin: 8px 16px 8px 0;
}
/* Barre d'outils du tableau (/tableau) : une rangée d'actions compactes
au-dessus d'une ligne d'infos discrète. .table-menu (autres pages) intacte. */
.table-toolbar {
display: flex;
flex-wrap: wrap;
align-items: center;
margin: 14px 0 6px 0;
}
.table-toolbar > * {
margin: 0 12px 8px 0;
}
/* Mode d'emploi : aide auxiliaire, rejetée en fin de barre sans toucher au DOM */
.table-toolbar #tableau_help_open {
order: 1;
}
.table-meta {
margin: 0 0 16px 0;
color: #666;
font-size: 14px;
}
/* Raison du téléchargement bloqué : visible (pas une infobulle), mise en avant
sobrement avec la couleur terracotta de la marque. */
.table-meta .dl-hint {
color: #b33821;
}
/* Légende des boutons en tête du mode d'emploi : boutons reproduits à
l'identique mais inertes, alignés en face de leur fonction. */
.help-legend-table {
border-collapse: collapse;
}
.help-legend-table td {
padding: 4px 16px 4px 0;
vertical-align: middle;
}
.help-legend-btn {
white-space: nowrap;
}
.help-legend .btn {
pointer-events: none;
}
#source_table {
margin-bottom: 25px;
}
+120 -19
View File
@@ -78,6 +78,62 @@ DATATABLE = html.Div(
),
)
def _help_button_legend():
"""Légende en tête du mode d'emploi : chaque bouton de la barre d'outils,
reproduit à l'identique (mais inerte), en face de sa fonction."""
rows = [
(
dbc.Button("Colonnes", color="secondary", size="sm"),
"Choisir les colonnes affichées.",
),
(
dbc.Button("Sauvegarder la vue", color="secondary", size="sm"),
"Enregistrer les filtres, tris et colonnes actuels sous un nom (abonnés).",
),
(
dbc.Button("Mes vues ▾", color="secondary", size="sm"),
"Rouvrir une vue que vous avez enregistrée (abonnés).",
),
(
dbc.Button("Partager la vue", color="secondary", size="sm"),
"Copier l'adresse de la vue actuelle pour la partager ou la conserver.",
),
(
dbc.Button("Télécharger (Excel)", color="secondary", size="sm"),
"Télécharger les données filtrées et triées au format Excel.",
),
(
dbc.Button("Réinitialiser", color="danger", outline=True, size="sm"),
"Supprimer tous les filtres et tris.",
),
(
dbc.Button("Mode d'emploi", color="secondary", outline=True, size="sm"),
"Ouvrir cette aide.",
),
]
return html.Div(
className="help-legend",
children=[
html.P("Les boutons de la barre d'outils", className="fw-bold mb-2"),
html.Table(
className="help-legend-table",
children=html.Tbody(
[
html.Tr(
[
html.Td(btn, className="help-legend-btn"),
html.Td(desc, className="help-legend-desc"),
]
)
for btn, desc in rows
]
),
),
],
)
layout = [
dcc.Location(id="tableau_url", refresh=False),
dcc.Store(id="filter-cleanup-trigger-tableau"),
@@ -160,14 +216,23 @@ layout = [
html.Div(
[
# Modal du mode d'emploi
dbc.Button("Mode d'emploi", id="tableau_help_open"),
dbc.Button(
"Mode d'emploi",
id="tableau_help_open",
color="secondary",
outline=True,
size="sm",
),
dbc.Modal(
[
dbc.ModalHeader(dbc.ModalTitle("Mode d'emploi")),
dbc.ModalBody(
dcc.Markdown(
dangerously_allow_html=True,
children=f"""
[
_help_button_legend(),
html.Hr(),
dcc.Markdown(
dangerously_allow_html=True,
children=f"""
##### Définition des colonnes
Pour voir la définition d'une colonne, passez votre souris sur son en-tête.
@@ -226,7 +291,8 @@ layout = [
(informations, marchés attribués/remportés, etc.)
""",
),
),
],
),
dbc.ModalFooter(
dbc.Button(
@@ -245,8 +311,10 @@ layout = [
),
# Bouton modal des colonnes affichées
dbc.Button(
"Choisir les colonnes",
"Colonnes",
id="tableau_columns_open",
color="secondary",
size="sm",
className="column_list",
title="Choisir les colonnes à afficher et masquer",
),
@@ -258,11 +326,15 @@ layout = [
dbc.Button(
"Sauvegarder la vue",
id="btn-save-view",
color="secondary",
size="sm",
title="Enregistrer les filtres, tris et colonnes actuels sous un nom",
),
dbc.DropdownMenu(
id="saved-views-menu",
label="Mes vues",
color="secondary",
size="sm",
children=[],
className="d-inline-block",
),
@@ -294,24 +366,35 @@ layout = [
),
],
),
html.P("lignes", id="nb_rows"),
html.Div(id="copy-container"),
dcc.Input(id="share-url", readOnly=True, style={"display": "none"}),
dbc.Button(
"Téléchargement désactivé au-delà de 65 000 lignes",
"Télécharger (Excel)",
id="btn-download-data",
color="secondary",
size="sm",
disabled=True,
),
dcc.Download(id="download-data"),
dcc.Store(id="filtered_data", storage_type="memory"),
html.P("Données mises à jour le " + str(update_date)),
dbc.Button(
"Remettre à zéro",
title="Supprime tous les filtres et les tris. Autrement ils sont conservés même si vous fermez la page.",
"Réinitialiser",
id="btn-tableau-reset",
color="danger",
outline=True,
size="sm",
title="Supprime tous les filtres et les tris. Autrement ils sont conservés même si vous fermez la page.",
),
],
className="table-menu",
className="table-toolbar",
),
html.Div(
className="table-meta",
children=[
html.Span(id="nb_rows"),
html.Span(" · Données mises à jour le " + str(update_date)),
html.Span(id="download-hint", className="dl-hint"),
],
),
dbc.Modal(
[
@@ -351,6 +434,7 @@ layout = [
Output("btn-download-data", "children"),
Output("btn-download-data", "title"),
Output("filter-cleanup-trigger-tableau", "data", allow_duplicate=True),
Output("download-hint", "children"),
Input("tableau_url", "href"),
Input("tableau_datatable", "page_current"),
Input("tableau_datatable", "page_size"),
@@ -360,13 +444,29 @@ layout = [
prevent_initial_call=True,
)
def update_table(href, page_current, page_size, filter_query, sort_by, data_timestamp):
# if ctx.triggered_id != "url":
# search_params = None
# else:
# search_params = urllib.parse.parse_qs(search_params.lstrip("?"))
return prepare_table_data(
None, data_timestamp, filter_query, page_current, page_size, sort_by, "tableau"
result = list(
prepare_table_data(
None,
data_timestamp,
filter_query,
page_current,
page_size,
sort_by,
"tableau",
)
)
# Libellé court et constant ; la raison d'un éventuel blocage est affichée
# en clair dans la ligne d'infos (fiable cross-browser, contrairement à une
# infobulle sur bouton désactivé). index 5 = disabled, 6 = children, 7 = title.
result[6] = "Télécharger (Excel)"
download_blocked_too_many = result[5] and result[7]
download_hint = (
" · Filtrez sous 65 000 lignes pour activer le téléchargement"
if download_blocked_too_many
else ""
)
result.append(download_hint)
return tuple(result)
@callback(
@@ -491,7 +591,8 @@ def sync_url_and_reset_button(filter_query, sort_by, hidden_columns, href):
children=[
dbc.Button(
"Partager la vue",
className="btn btn-primary",
color="secondary",
size="sm",
title="Copier l'adresse de cette vue (filtres, tris, choix de colonnes) pour la partager.",
)
],