From e804b6bca2291d1ac3dff9208cd8fd1dc9ba6802 Mon Sep 17 00:00:00 2001 From: Colin Maudry Date: Wed, 18 Mar 2026 14:59:32 +0100 Subject: [PATCH] URL partageable pour la page observatoire #65 Co-Authored-By: Claude Sonnet 4.6 --- src/pages/observatoire.py | 52 +++++++++++++++++++++++++++++++++++++++ tests/test_main.py | 22 +++++++++++++++++ 2 files changed, 74 insertions(+) diff --git a/src/pages/observatoire.py b/src/pages/observatoire.py index 67ab659..b42a99a 100644 --- a/src/pages/observatoire.py +++ b/src/pages/observatoire.py @@ -274,6 +274,12 @@ Alors, on fait comment ? disabled=True, className="mt-2", ), + dcc.Input( + id="observatoire-share-url", + readOnly=True, + style={"display": "none"}, + ), + html.Div(id="observatoire-copy-container"), ], ), dbc.Col( @@ -310,6 +316,52 @@ def restore_filters_from_url(search): return acheteur_id, titulaire_id, "" +@callback( + Output("observatoire-share-url", "value"), + Output("observatoire-copy-container", "children"), + Input("dashboard_acheteur_id", "value"), + Input("dashboard_titulaire_id", "value"), + State("dashboard_url", "href"), + prevent_initial_call=True, +) +def sync_observatoire_share_url(acheteur_id, titulaire_id, href): + if not href: + return no_update, no_update + + base_url = href.split("?")[0] + + params = {} + if acheteur_id: + params["acheteur_id"] = acheteur_id + if titulaire_id: + params["titulaire_id"] = titulaire_id + + query_string = urllib.parse.urlencode(params) + full_url = f"{base_url}?{query_string}" if query_string else base_url + + copy_button = dcc.Clipboard( + id="btn-copy-observatoire-url", + target_id="observatoire-share-url", + title="Copier l'URL de cette vue", + style={ + "display": "inline-block", + "fontSize": 20, + "verticalAlign": "top", + "cursor": "pointer", + }, + className="fa fa-link", + children=[ + dbc.Button( + "Partager", + className="btn btn-primary mt-2", + title="Copier l'adresse de cette vue filtrée pour la partager.", + ) + ], + ) + + return full_url, copy_button + + @callback( Output("cards", "children"), Output("btn-download-observatoire", "disabled"), diff --git a/tests/test_main.py b/tests/test_main.py index 995a669..6cd2903 100644 --- a/tests/test_main.py +++ b/tests/test_main.py @@ -157,3 +157,25 @@ def test_006_observatoire_url_to_input(dash_duo: DashComposite): assert acheteur_input.get_attribute("value") == "a1", ( "acheteur_id input should be populated from URL param" ) + + +def test_007_observatoire_share_url(dash_duo: DashComposite): + from src.app import app + + dash_duo.start_server(app) + dash_duo.wait_for_text_to_equal(".logo > h1", "decp.info", timeout=4) + + # Navigate to observatoire with acheteur_id query param + dash_duo.wait_for_page(f"{dash_duo.server_url}/observatoire?acheteur_id=a1") + dash_duo.wait_for_element("#observatoire-share-url", timeout=4) + + import time + + time.sleep(1) # Allow callback chain to complete + + share_url_input = dash_duo.find_element("#observatoire-share-url") + share_url_value = share_url_input.get_attribute("value") + + assert "acheteur_id=a1" in share_url_value, ( + f"Share URL should contain acheteur_id param, got: {share_url_value}" + )