feat(quote-preview): add "Télécharger" button to every quote-preview modal#44
Open
AntoinePoindron wants to merge 2 commits intomainfrom
Open
feat(quote-preview): add "Télécharger" button to every quote-preview modal#44AntoinePoindron wants to merge 2 commits intomainfrom
AntoinePoindron wants to merge 2 commits intomainfrom
Conversation
…modal
The "Voir le devis" modal already shows the same content the
downloadable PDF does (both go through `_pdf_preview.html` →
the WeasyPrint-backed `services.quote_pdf.render_quote_pdf`),
but only the caterer's editor exposed a download. Surface the
PDF download next to the close icon in every modal that uses
the preview, on both sides of the marketplace.
Two surfaces:
- caterer/requests/detail.html — single per-request modal
("Voir le devis" on the right-hand recap card). Wired to the
existing `caterer.quote_pdf` route.
- client/requests/detail.html — one modal per received quote
(mode comparateur 3 devis can show up to 3). Each "Télécharger"
hits the new `client.quote_pdf` route.
Backend:
- new GET /client/requests/<request_id>/quote/<q_id>/pdf route in
`blueprints/client/requests.py`. Mirrors `caterer.quote_pdf`
but the scope check goes the other way: the quote must belong
to a request whose company == viewer's company. 404s on
scope mismatch (rather than 403) so we don't leak the
existence of a quote outside the viewer's perimeter. Uses the
same `_MAX_PDF_LINES = 500` cap as the caterer route to refuse
WeasyPrint amplification on a corrupted/oversized row.
- reuses `services.quote_pdf.render_quote_pdf(quote, qr, caterer)`
so the PDF byte stream is identical to what the caterer would
download — no template drift between sides.
UI: ghost-pill "Télécharger" button with a download icon, sitting
to the left of the close X in the modal header. Same compact style
as the rest of the app's secondary-action buttons.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Companion to fix/client-hides-draft-quotes : the request-detail listing now filters drafts at the DB level, but the new client.quote_pdf route this PR introduces could still hand out a brouillon PDF if a client guessed the quote ID directly. Add the same `Quote.status != QuoteStatus.draft` filter to the SELECT so the route 404s on a draft regardless of how it was reached. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This was referenced May 9, 2026
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
The "Voir le devis" modal already shows the same content the downloadable PDF renders (both go through
_pdf_preview.html→ the WeasyPrint-backedservices.quote_pdf.render_quote_pdf), but only the caterer's editor exposed a download. This PR adds a Télécharger button to every modal that uses the preview, on both sides of the marketplace.Validated end-to-end manually by @AntoinePoindron before opening this PR.
What changed
caterer/requests/detail.html— single per-request modal opened from the right-hand recap cardcaterer.quote_pdfrouteclient/requests/detail.html— one modal per received quote (compare-3 mode can show up to 3)client.quote_pdfrouteBackend
GET /client/requests/<request_id>/quote/<q_id>/pdfroute inblueprints/client/requests.py. Mirrors the existingcaterer.quote_pdfbut the scope check goes the other way: the quote must belong to a request whosecompany_id == viewer.company_id. 404s on scope mismatch (rather than 403) so we don't leak the existence of a quote outside the viewer's perimeter._MAX_PDF_LINES = 500cap as the caterer route — refuses WeasyPrint amplification on a corrupted or oversized row.services.quote_pdf.render_quote_pdf(quote, qr, caterer)so the PDF byte stream is identical to what the caterer would download — no template drift between sides.UI
Ghost-pill Télécharger button with a
downloadlucide icon, sitting to the left of the close X in the modal header. Same compact style as the rest of the app's secondary-action buttons.Test plan
🤖 Generated with Claude Code