Skip to content

Add admin swag/shirt tracking for attendees - #82

Closed
mhakeem wants to merge 25 commits into
Blue-Ridge-Ruby:mainfrom
rockymtnruby:feature/swag-shirts
Closed

mhakeem wants to merge 25 commits into
Blue-Ridge-Ruby:mainfrom
rockymtnruby:feature/swag-shirts

Conversation

@mhakeem

@mhakeem mhakeem commented Sep 28, 2026 •

Copy link
Copy Markdown

!!!

mhakeem and others added 25 commits September 13, 2026 08:00
…RMR schedule

RMR runs Mon-Tue (Sep 28-29, 2026) vs BRR's Wed-Sat, so this replaces the
wed/thu/fri/sat/sun day-anchor scheme with sun/mon/tue throughout:

- config/schedule.yml: full replacement with the real 28-session RMR
  schedule (fetched from their live Sessionize feed, embed xxmulsug),
  plus 3 placeholder items (pre-conference meetup, Hack Day, a volunteer
  slot) seeded hidden (is_public: false) since none of these are on
  RMR's published schedule yet.
- ScheduleItem: DAY_META, CONFERENCE_DATES, and the `ordered` scope's SQL
  CASE now use sun/mon/tue. HACK_DAY_SLUG -> "tue-hackday" so admin Hack
  Projects (which does a hard find_by! on that slug) keeps working.
  DEFAULT_PLAN_SLUGS emptied - RMR has no equivalent to BRR's one-off
  "Mystery Activity" auto-add.
- db/seeds/schedule.rb: now reads per-item is_public/volunteer_capacity
  from the YAML (needed for the hidden placeholders), and the obsolete-
  slug cleanup list covers every prior BRR slug so reseeding a BRR-seeded
  DB cleans up correctly.
- Two controller "new item" day defaults and two cosmetic form/doc
  strings updated to match.
- 37 test files: day literals moved to valid anchors, HACK_DAY_SLUG
  references fixed, the DEFAULT_PLAN_SLUGS coverage test rewritten to
  stub the constant (since it's empty for RMR), and hardcoded 2026-04-30
  dates moved to real September dates. Verified: full suite green.

See RMR_FORK_PLAN.md for the broader fork plan this is step 1 of.
Rebrand schedule for Rocky Mountain Ruby: Mon/Tue day anchors + real RMR 2026 schedule
Replaces every Blue Ridge Ruby / Asheville reference in text, links, and
config with Rocky Mountain Ruby / Boulder equivalents, verified against
rockymtnruby.dev and rockymtnruby/site.

- config/application.rb, production.rb: Mountain time zone, app.rockymtnruby.dev
  APP_HOST fallback.
- Mailer from-address, REGISTER_URL, and EMBASSY_HOURS updated.
- Navbar, footer, dashboard, sessions, error pages, organizer note, and the
  annual report all repoint at real rockymtnruby.dev paths (boulder-guide,
  schedule, tickets, code-of-conduct, sponsorship, socials). Ashevillagers
  card kept as a commented-out reference rather than deleted. Nonprofit
  claim in the footer removed (RMR is not a 501c3) but kept commented out
  for reference.
- Passport PDF (app/services/passport_application_pdf.rb): header, legal
  copy, and glossary now read Rocky Mountain Ruby Embassy / Boulder, CO;
  form code bumped to Rev. 2026-09.
- db/seeds.rb: admin list is now Katya + the RMR organizer (spike@rockymtnruby.dev);
  Jeremy removed entirely, personal gmail admin seed removed (added manually
  instead).
- db/seeds/embassy_questions.rb: notary/affidavit copy reworded for RMR.
- Updated the handful of tests that hardcoded the old admin emails,
  BRR speaker URLs, and the branded 404 link.

Known gap (not fixed here): no schedule item has kind: embassy, so the
embassy booking/passport-application flow and the annual report have no
real data to exercise yet — needs a seeded placeholder plus admin
configuration before launch.
- app/assets/images/logo.svg: swap in RMR's real gem-mark logo
  (pulled from rockymtnruby/site, byte-identical to their asset)
- public/favicon.ico: swap in RMR's real favicon
- _navbar.html.erb: pair the icon with a text wordmark since RMR's
  own repo ships no combined icon+wordmark asset
- application.css: add .mr-2 utility (was missing) for logo/wordmark
  spacing
- drop the Blue Ridge background image (.bg-ridge -> .bg-boulder,
  flat background for now; public/images/ridge.jpg removed)

Deferred: brand color tokens (kept the current BRR palette per
feedback), fonts still untouched, no replacement background photo
picked yet.
Rebrand copy, config, seeds, PDF, logo, and favicon for Rocky Mountain Ruby
Replace the stock README with real dev, test, and Railway deploy
instructions plus an environment variable reference. Add dotenv-rails
and a tracked .env.example. Remove the upstream Blue Ridge Ruby
credentials.yml.enc, which is undecryptable in this fork; the app
uses SECRET_KEY_BASE directly instead. Seed mazhak+dev@msn.com as an
admin for local testing.
Railway/dev setup docs, dotenv, and local admin seed
- Update nokogiri, net-imap, rails, loofah, crass, concurrent-ruby,
  puma, faraday, mail, msgpack, rails-html-sanitizer, websocket-driver,
  and rubyzip to clear every live bundler-audit advisory (was 176+
  findings, now zero, no ignore list needed).
- Pin json to ~> 2.19 in the Gemfile; the update pulled in json 3.0,
  a major version bump that broke session/redirect handling across
  the suite (228 test errors). Reverting to the 2.x line while still
  picking up the CVE fix (2.21.2) restores a fully green test run.
- Update brakeman 8.0.4 -> 8.0.6 and remove --ensure-latest from
  bin/brakeman; that flag failed CI purely on a version notice, not
  a security finding, and depended on a network call to rubygems.org.
- Rubocop's failure was environmental (a stray Neovim/Mason rubocop
  binary resolving ahead of the project's bundled one), not a repo
  issue; left unchanged.
Fix the three known bin/ci failures
Rebrand for Rocky Mountain Ruby + production readiness
Postmark stays primary. MAIL_PROVIDER=brevo switches to Brevo via its
official API gem rather than SMTP: live testing showed Brevo's SMTP
relay requires a separate support-activated gate on new accounts and
fails silently (no exception, no log entry) when unactivated, while
the REST API works immediately with no such gate.

- lib/brevo_delivery_method.rb: minimal ActionMailer delivery method
  wrapping Brevo::TransactionalEmailsApi, registered inline in
  production.rb rather than a separate initializer to avoid a
  boot-order hazard with config.action_mailer.*_settings=.
- First mailer test in the repo (test/mailers/ previously held only
  .keep), plus a test for the new delivery method's Mail::Message
  mapping.

Bugs fixed during live testing, and provider/sender made
independently configurable:

- Fixed SendSmtpEmail body always coming back empty: the constructor
  keys its hash by JSON (camelCase) names, not the snake_case Ruby
  attribute names it exposes.
- Fixed a boot-time NameError: config/environments/production.rb runs
  before Zeitwerk can autoload lib/, so BrevoDeliveryMethod needs an
  explicit require there.
- Made the API client injectable for testing instead of depending on
  Minitest::Mock/Object#stub, which Minitest 6 split into a separate
  gem not in this Gemfile.
- Replaced fragile manual MIME-part matching with Mail::Message#all_parts
  + #mime_type.
- MAIL_FROM_ADDRESS replaces the temporary MAIL_FROM_OVERRIDE hack:
  sender address is now a normal, permanent setting independent of
  MAIL_PROVIDER, not a test-only escape hatch. Neither Postmark nor
  Brevo is structurally "the" primary provider; either can be active,
  and the sender just needs to be verified with whichever one is.

Verified end to end against production boot locally (Docker) with
MAIL_PROVIDER=brevo: real delivery, working login link, full session
establishment.
Fix Tito ticket URL host and add missing admin_ticket_url coverage:
dashboard.tito.io was the wrong host for organizer ticket lookups;
ti.to is correct. admin_ticket_url had no test coverage anywhere,
including the property that matters most for next year: it uses the
slugs memorialized on the row at ticket-link time, not current ENV.

Move Tito sync into a background job with live status:
Admin::UsersController#sync walked every Tito ticket inline in the
HTTP request, risking a timeout on the one action you'd use right
before the event, and a single malformed ticket aborted the whole
run under the blanket rescue.

TitoSyncJob now does the work in Solid Queue, with per-ticket error
isolation (one bad row increments a failed counter instead of
stopping the run) and status written to Rails.cache. The click still
returns instantly; a banner above the users table shows running,
finished (with counts), or failed, pushed live via a Turbo Stream
broadcast from the job with a self-cancelling 15s reload as a
fallback in case the broadcast doesn't land. A stale-running lock
(15 minutes) keeps a dead worker from permanently disabling the
button. Sync still never writes role on update, so hand-promoted
volunteers survive a re-sync — pinned by a test.

config/environments/test.rb moves from :null_store to :memory_store
so the cache-backed status and its already-running guard are
actually testable; test_helper.rb clears the cache between tests to
keep that from leaking across test cases.

Map Tito release titles to roles, promoting attendees to volunteer
on sync: TitoSyncJob now looks up each ticket's release title (via a
separate client.releases call — Ticket#release_id exists but the
gem's :release expandable isn't actually wired through to return the
title) and maps it to a role via ROLE_BY_RELEASE_TITLE, matched by a
/volunteer/i-style pattern rather than release_id, since releases are
recreated every year but a release title is likely to persist.

This only ever promotes, never demotes: an existing attendee is
bumped to volunteer if their release matches, but a user already
volunteer or admin keeps that role regardless of what any ticket
says. Originally this check only ran in the connect-by-email and
create branches; fixed to also run in the already-linked-by-slug
branch, which is the one nearly every resync after the first
actually hits.

Lock role against auto-promotion once an admin sets it:
TitoSyncJob's promotion logic couldn't tell 'still attendee because
nobody's touched them' apart from 'attendee because an admin
deliberately put them there' — a manual correction from volunteer
back to attendee would silently get re-promoted on the next resync,
since sync re-walks every ticket with no since-last-run filter.

Adds users.role_set_by_admin (migration), set by
Admin::UsersController#create/#update whenever role_changed?, and
checked by TitoSyncJob#promote! before any auto-promotion. A manual
role correction now survives resync regardless of order relative to
the sync.

Disable the sync button while a sync is running: the button lives
inside the same partial TitoSyncJob broadcasts a replace of on
completion, so disabled/enabled now updates live along with the
status banner instead of only on the next full page load.
Add Brevo backup mailer and Tito async data pull
- Turbo Frame for search/filter/mark in place, in-bar clear link
- Drop flash notices on mark/undo, match search button to input height
- Add Swag card to admin dashboard
Late-ticket waitlist mapping applied only to newly created users,
so migration-backfilled pending rows never flipped. Apply to any
untouched pending row instead.
@mhakeem

mhakeem commented Sep 28, 2026

Copy link
Copy Markdown
Author

sorry again!

@mhakeem mhakeem closed this Sep 28, 2026
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.

2 participants