HOLD UNTIL REQUESTED: Add "Download all" button that zips a callout's attached documents#1832
HOLD UNTIL REQUESTED: Add "Download all" button that zips a callout's attached documents#1832maebeale wants to merge 1 commit into
Conversation
| end | ||
| cards | ||
| end | ||
|
|
There was a problem hiding this comment.
🤖 From Claude: The invoice has no stored PDF (it is rendered HTML printed via window.print()), so it can't be bundled as a file download — newTab: true opens its print-to-PDF page instead. Everything else is a real file.
| this.itemsValue.forEach((item, index) => { | ||
| setTimeout(() => this.trigger(item), index * this.delayValue) | ||
| }) | ||
| } |
There was a problem hiding this comment.
🤖 From Claude: Staggered by delay ms — browsers suppress multiple downloads fired synchronously in one tick, so the burst is spread out.
b40d447 to
f6ab3cf
Compare
| # shared callout chrome shows whenever a page has downloadable resources | ||
| # (handouts and any built-in callout with attachments — Payment passes a nil | ||
| # callout here, so its own invoice/receipt/W-9 documents are excluded). | ||
| def download_all_urls_for(callout) |
There was a problem hiding this comment.
🤖 From Claude: Keying the button off a nil callout is what excludes Payment for free — set_builtin_content nils the payment callout before calling this, so its own invoice/receipt/W-9 documents never get bundled here.
|
|
||
| start() { | ||
| this.urlsValue.forEach((url, index) => { | ||
| setTimeout(() => this.download(url), index * this.delayValue) |
There was a problem hiding this comment.
🤖 From Claude: Staggered by delay ms — browsers suppress multiple downloads fired synchronously in one tick, so the burst is spread out.
f6ab3cf to
78b7279
Compare
78b7279 to
6f3e844
Compare
A single header-bar button downloads every attached document on a callout page as one zip, instead of opening and downloading each one individually. The shared callout chrome shows it whenever a page has more than one downloadable resource — handouts and any built-in callout (scholarship, certificate, videoconference) with linked files. The button links to a per-callout documents.zip endpoint that streams the bundle server-side (rubyzip), so no client JavaScript is involved. Payment is excluded: it manages its own invoice/receipt/W-9 documents in a dedicated section. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
6f3e844 to
9d3ae9f
Compare
| def download_all | ||
| callout = @event.registration_ticket_callouts.find_by(builtin_key: params[:builtin_key]) | ||
| resources = callout && callout.builtin_key != "payment" ? downloadable_resources(callout) : [] | ||
| raise ActiveRecord::RecordNotFound if resources.size < 2 |
There was a problem hiding this comment.
🤖 From Claude: Guards the endpoint independently of the button: payment is excluded and anything with fewer than two files 404s, so a hand-crafted URL can't pull a zip the UI would never offer.
| resources.each do |resource| | ||
| file = resource.downloadable_asset.file | ||
| zip.put_next_entry(unique_entry_name(resource, file, used)) | ||
| zip.write(file.download) |
There was a problem hiding this comment.
🤖 From Claude: Builds the whole zip in memory (each file.download loads a blob) — fine for a handful of PDFs per callout; would want streaming if these grew large.
🤖 PR, suggested 👤 review level: 📖 Read — light-logic: one new public endpoint + a shared-chrome button, no client JS
A single Download all button in the callout page header downloads every attached document on the page as one zip, instead of opening/downloading each one individually.
Where it shows
_callout_pagechrome renders the button whenever the controller sets@download_all_url— any callout page with more than one downloadable linked resource: handouts and any built-in callout (scholarship, certificate, videoconference) with attached files.How it works (no JavaScript)
link_toa per-callout…/documents.zipendpoint (CalloutsController#download_all).Notes
Tests
spec/requests/events/callouts_spec.rb: button visibility (2+/1/0/payment) and the zip endpoint (streams a named-entry zip, 404s under two files, 404s for payment).