diff --git a/AGENTS.md b/AGENTS.md index fdfd5e7722..9115ac6bfa 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -71,7 +71,7 @@ This codebase (Rails 8.1) | Directory | Purpose | |---|---| | `app/frontend/entrypoints/` | Vite entry points (application.js, application.css) | -| `app/frontend/javascript/controllers/` | Stimulus controllers (73) | +| `app/frontend/javascript/controllers/` | Stimulus controllers (74) | | `app/frontend/javascript/rhino/` | Rich text editor customizations (mentions, grid) | | `app/frontend/stylesheets/` | Tailwind CSS and component styles | @@ -280,6 +280,7 @@ end - `confirm_email` — Email confirmation UI - `dirty_form` — Unsaved changes detection - `dismiss` — Dismissable elements +- `download_all` — Fires a batch of file downloads from one "Download all" button (callout forms/handouts pages); new-tab items open instead of downloading - `dropdown` — Dropdown menus with keyboard/click-outside handling - `event_staff_bio` — Loads a selected person's read-only profile bio (with edit link) alongside the editable event-specific bio on the staff form - `file_preview` — File upload preview diff --git a/app/controllers/events/callouts_controller.rb b/app/controllers/events/callouts_controller.rb index 5e0330662a..953c2e3479 100644 --- a/app/controllers/events/callouts_controller.rb +++ b/app/controllers/events/callouts_controller.rb @@ -50,7 +50,9 @@ def ce # Forms page: callout-card links to the W-9 (when requested), invoice (when # requested), and the letter to supervisors resource — each opens in a new tab. def forms - @form_cards = build_form_cards + letter = Resource.find_by(title: "Letter to Supervisors") + @form_cards = build_form_cards(letter) + @download_all_items = build_form_download_items(letter) end # Handouts page: callout-card links to the training worksheet/handout @@ -58,13 +60,16 @@ def forms # (PDF preview + download, with a back-to-ticket eyebrow). def handouts by_title = Resource.where(title: HANDOUT_RESOURCE_TITLES).index_by(&:title) - @handout_cards = HANDOUT_RESOURCE_TITLES.filter_map do |title| - resource = by_title[title] - next unless resource + resources = HANDOUT_RESOURCE_TITLES.filter_map { |title| by_title[title] } + @handout_cards = resources.map do |resource| resource_card(icon: "fa-solid fa-file-pdf", title: resource.title, subtitle: "Open this training resource", href: registration_resource_path(@event_registration.slug, resource), target: nil) end + @download_all_items = resources.filter_map do |resource| + next unless resource.downloadable_asset&.file&.attached? + { href: resource_download_path(resource) } + end end # Registrant-facing page for a single Resource, shown in the shared callout @@ -104,7 +109,7 @@ def set_event # Builds the callout-card links shown on the forms page. The W-9 and invoice # are always available; the letter to supervisors follows when seeded. - def build_form_cards + def build_form_cards(letter) cards = [ resource_card(icon: "fa-solid fa-file-pdf", title: "Download W-9", subtitle: "AWBW's W-9 tax form for your records", @@ -113,7 +118,6 @@ def build_form_cards subtitle: "Itemized invoice for this registration", href: registration_invoice_path(@event_registration.slug)) ] - letter = Resource.find_by(title: "Letter to Supervisors") if letter cards << resource_card(icon: "fa-solid fa-file-arrow-down", title: "Letter to supervisors", subtitle: "Share to request release time", @@ -122,6 +126,16 @@ def build_form_cards cards end + # Items for the forms "Download all" button: the W-9 and (when attached) the + # letter download directly; the invoice has no stored PDF, so it opens its + # print-to-PDF page in a new tab. + def build_form_download_items(letter) + items = [ { href: "/documents/awbw-w9.pdf" } ] + items << { href: resource_download_path(letter) } if letter&.downloadable_asset&.file&.attached? + items << { href: registration_invoice_path(@event_registration.slug), newTab: true } + items + end + # A blue callout card linking to a document. External/static links open in a # new tab (target: "_blank"); registrant resource pages stay in-tab so the # back-to-ticket eyebrow works (pass target: nil). diff --git a/app/frontend/javascript/controllers/download_all_controller.js b/app/frontend/javascript/controllers/download_all_controller.js new file mode 100644 index 0000000000..968527006e --- /dev/null +++ b/app/frontend/javascript/controllers/download_all_controller.js @@ -0,0 +1,34 @@ +import { Controller } from "@hotwired/stimulus" + +// Fires a batch of downloads from a single "Download all" button. Each item is +// { href, newTab } — file URLs (W-9, handout/letter PDFs) download in place via a +// temporary anchor; newTab items (the print-to-PDF invoice page, which has no +// stored file) open in a new tab. Downloads are staggered so browsers don't +// suppress the later ones in the burst. +// +// +export default class extends Controller { + static values = { items: Array, delay: { type: Number, default: 400 } } + + start() { + this.itemsValue.forEach((item, index) => { + setTimeout(() => this.trigger(item), index * this.delayValue) + }) + } + + trigger(item) { + if (item.newTab) { + window.open(item.href, "_blank", "noopener") + return + } + const link = document.createElement("a") + link.href = item.href + link.download = "" + link.rel = "noopener" + document.body.appendChild(link) + link.click() + link.remove() + } +} diff --git a/app/frontend/javascript/controllers/index.js b/app/frontend/javascript/controllers/index.js index e3d27aa94f..3a2a3861a4 100644 --- a/app/frontend/javascript/controllers/index.js +++ b/app/frontend/javascript/controllers/index.js @@ -60,6 +60,9 @@ application.register("dirty-form", DirtyFormController) import DismissController from "./dismiss_controller" application.register("dismiss", DismissController) +import DownloadAllController from "./download_all_controller" +application.register("download-all", DownloadAllController) + import FlipCardController from "./flip_card_controller" application.register("flip-card", FlipCardController) diff --git a/app/views/events/callouts/_callout_page.html.erb b/app/views/events/callouts/_callout_page.html.erb index 374248c01d..890beef833 100644 --- a/app/views/events/callouts/_callout_page.html.erb +++ b/app/views/events/callouts/_callout_page.html.erb @@ -25,6 +25,9 @@
<%= @event.title %>
+ <% if content_for?(:header_action) %> +