Skip to content

feat(frontend): mailchimp download - #232

Merged
sabinem merged 2 commits into
mainfrom
feat/mailchimp-download
Aug 24, 2026
Merged

feat(frontend): mailchimp download#232
sabinem merged 2 commits into
mainfrom
feat/mailchimp-download

Conversation

@sabinem

@sabinem sabinem commented Aug 24, 2026

Copy link
Copy Markdown
Collaborator

A participant roster you can hand to a mailing tool

The story

As the organizer of a hackathon, I want to download my participants'
names and email addresses as a CSV, so that I can import them into
Mailchimp and actually mail the people who signed up.

Manage Participants could approve someone, promote them, remove them —
everything except get in touch with them. The addresses were in the system the
whole time; the only way out was reading them off the screen one row at a time.
This adds the button that ends that.

What you get

A Download CSV button in the Manage Participants header, next to the search
box. It hands you <hackathon-name>-participants.csv:

Email Address,Full Name,Status,Role
alice@mail.com,Alice Wonderland,Approved,Owner
bob@mail.org,Bob Henderson,Approved,Member
charles@mail.net,Charles Whitfield,Waitlisted,Member

Mailchimp-ready as it stands: Email Address maps to the audience's email
field, Full Name to a text merge field, and Status/Role give you something
to segment on.

The decisions behind it, and why

No backend change. Hackathon.Get already returns every member's email
(mappers.go:17), so there is no new RPC, no proto, no migration — only a
download endpoint over data the page was already receiving.

Full Name stays one column. display_name is a single free-text field.
Splitting it at the first space to fill Mailchimp's First/Last would get "van
der Berg", "Maria Anna Rossi" and every mononym wrong, and a mailing list that
greets people by a mangled name is worse than one that greets them by all of it.

Waitlisted people are in the file, labelled — not dropped. Dropping them
would put the row count at odds with the header above it and leave no way to
reach them at all. The Status column is what stops them being mailed as though
they were confirmed.

Members with no address are skipped, and the header says how many. email
is optional and defaults to empty, and a blank address is a row Mailchimp
rejects. The count line now reads
12 participants · 1 awaiting approval · 1 has no email address, so a file
shorter than the roster is never a silent surprise.

The file is the whole roster, not the search results. A download whose
contents depend on what happens to be typed in the box beside it is a trap — you
would find out months later, from the people who never got the email.

It is a +server.ts, not a client-side blob. Follows the vote export
already in the tree: a real HTTP download, named by Content-Disposition, that
re-applies the organizer gate server-side rather than trusting the page that
rendered the button.

Three things on the member overview were present whatever the hackathon
looked like and told nobody anything they could act on.

"Not open yet" listed the closed capabilities — five of six for most of a
hackathon, and a list nobody can do anything with. The question behind it is
never "is voting closed" but "when does it open", which this card cannot
answer, so the "Next" line at its foot is now a link to the timeline, which
can.

"No tracks have been defined, so projects are not grouped" apologised for a
missing grouping to a participant who cannot create a track and an organiser
who has Manage Tracks for exactly that. Without tracks the count is the whole
content of that card, so it is now shown as a figure — and when there is
neither a track nor an approved project the card does not render at all,
rather than holding half a row with an empty box.

About was a description read once, ever, sitting at the foot of the page whose
job is what has changed since the last visit — and the dashboard card a member
clicks to get here already shows it. It moves into the hero, with the rest of
the hackathon's identity, clamped to two lines and expandable in place.
Manage Participants could approve, promote and remove people but offered no
way to get their addresses out, so building a mailing list meant reading them
off the screen. Hackathon.Get already returns each member's email, so this
needs no new RPC — only a download endpoint that re-applies the organizer gate
the page applies, since Get itself is readable by every member.

That gate goes through viewerMembership rather than a plain members.find, as
the vote export does: Create grants ownership on the owners edge and writes no
participant row, so an organiser who never joined their own hackathon matches
nobody in the member list and would be refused the file the page had just
offered them.

The file is the whole roster, not the searched subset: its contents must not
depend on what is typed in the box beside the button. Waitlisted members are
included and labelled rather than dropped, so they can be segmented instead of
mailed as though they were confirmed. Members with no address are skipped —
a blank one is a row a mailing tool rejects — and the header says how many, so
a file shorter than the roster is not a silent one.
@sabinem sabinem changed the title Feat/mailchimp download feat(frontend): mailchimp download Aug 24, 2026
@sabinem
sabinem merged commit e64992f into main Aug 24, 2026
2 checks passed
@sabinem
sabinem deleted the feat/mailchimp-download branch August 24, 2026 12:11
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant