I’ve been revisiting the world of Elixir these days, and lately, I’ve been working on the rider experience for The Bike Brigade. In this post, I’ll be sharing how I built out a new home page for The Bike Brigade Rider Portal. I’ll go over what the process looked like, things I wanted to achieve, and how it went.

For context, The Bike Brigade is a group of 1000+ volunteer cyclists delivering food and other essentials by bike across Tkaronto (Toronto). Learn more here. Occasionally, I contribute to the open source dispatching software as well as do deliveries!

The task at hand

Currently, The Bike Brigade uses a mixture of Google spreadsheets mixed with their custom-built dispatching software (internally called Dispatch) as a means of getting riders onto campaigns to deliver goods and necessities. Cyclists are required to sign themselves up on the weekly spreadsheets for deliveries across programs they might like to deliver for. These spreadsheets then get ported into weekly, respective campaigns by Dispatchers. Dispatchers are all volunteers, as are the riders. The work I’ve been doing hopes to make the lives of the volunteers on both these sides a bit easier!

Our focus lately— a Rider Portal where riders can sign themselves up to campaigns directly, reducing the amount of work that dispatchers need to do to ensure deliveries get where they need to go.

The User Story

I’m not overly fond of the stiffness of user stories, but I still think they are useful for phrasing what pieces of work should be done. I came up with this, for this ticket:

As a rider, I want to sign in to the Dispatch app, be given the tools I need to do my deliveries, to find new opportunities to volunteer, and to feel a part of the community.

Writing user stories is hard, but this resonates enough with me as a rider! To distill it down:

  • I want to see the deliveries I’m doing today
  • I want to see a call to action to sign up for more deliveries
  • I want to see what riders have accomplished, and feel part of my community.

Let’s talk about design, next.


I doodled around a few designs in my notebook. Currently, riders only interact with the Dispatch app so far as receiving a link to a special delivery details page the day before that delivery is to occur. Other than that, riders aren’t logging in yet, and haven’t actually interacted with the "inside" of the dispatch app. These doodles didn’t really clarify anything (I’ll post some of them up if I can find them), but there is something nice about planning the next few hours you’ll be spending on a computer, off-computer.

I tried to do a bit of wireframing beyond just some pencil and paper; I don’t spend much time working on designs in software like Figma; I find I work fast enough in the browser (especially with Tailwind and Phoenix these days). Still, I threw something together so I could share it with our internal volunteers and got some feedback:

A quick mockup in Figma
A quick mockup in Figma

Volunteer dispatchers seemed pretty keen on #2 — being able to surface that a campaign needs help (ASAP!) — and that makes sense — the idea being that this could reduce the number of calls for help dispatchers have to make when they can’t find riders.

Some folks were also interested in adding a method for dispatchers to add news or announcements to the home page. I like that idea too, but for the sake of moving quickly and avoiding scope creep, we decided to set that aside for another time.


Development went fairly quickly for this, but there was still a chunk of work to do. I made sure to start with mobile-first design, and didn’t even touch desktop until all the functionality was done. I began by working on the itinerary section, which mostly involved moving some existing code around and redesigning it. Following that, I wrote two new queries:

  1. A query for fetching campaigns that need riders within a "soon-window" (in the end we decided on within 48 hours.
  2. A query to fetch community stats for successes in the last 7 days.

Query #1 was pretty fun to write — it looks like this:

  @doc """
    Gets campaigns that are happening today and have unassigned tasks.
    Used on the rider's home page to let riders know about campaigns that could use the help.
  def list_urgent_campaigns() do
    today = LocalizedDateTime.today()
    start_of_today = LocalizedDateTime.new!(today, ~T[00:00:00])
    end_of_tomorrow = Date.add(today, 1) |> LocalizedDateTime.new!(~T[23:59:59])

    query =
      from c in Campaign,
        distinct: [asc: c.id],
        join: t in assoc(c, :tasks),
        where: c.delivery_start <= ^end_of_tomorrow and c.delivery_start >= ^start_of_today and is_nil(t.assigned_rider_id),
        select: c

    |> Repo.preload([:program, :tasks])

The second query was fairly simple too. I wanted to collect some stats from the last week to display, so members of The Bike Brigade can see what’s happening as a result of their contributions. This was easy to write, mostly because the talented Max already did all the work in the subquery.

  def home_stats() do
    today = LocalizedDateTime.today()
    yesterday = Date.add(today, -1)
    week_ago = Date.add(yesterday, -7)

      from r in subquery(leaderboard_subquery(week_ago, yesterday)),
        select: %{
          riders: count(r.rider_id, :distinct),
          tasks: count(r.task_id, :distinct),
          campaigns: count(r.campaign_id, :distinct),
          distance: sum(r.distance)

After the above two were done, I did some styling, figuring out of copy, and found an open-source illustration on unDraw (useful!) and used that for an empty state. I ended up with this:

I tossed this out for feedback and got immediate feedback on things I had missed, namely that we still needed to iron out some challenges with what campaigns should be shown. I was also originally gung-ho about making the list of deliveries for the day into a fixed size 50% vertical-height scroll box, but decided to forgo that at Chad’s suggestion that it might not be user-friendly (it’s so nice to get immediate feedback from people who will actually use these tools every day!).

Iterate, Iterate

This is another step in the unfolding story for cyclists helping out through the Toronto Bike Brigade! I look forward to hacking on more features to make it easier for riders to deliver goods to folks in need. It has been super fun, and a privilege for me to get to write Elixir, with friends, for a non-profit, and it is bike-related. Does it get any better?

What’s next? There will be much to polish and fix as more and more riders start using the rider portal. I’ll be rolling with that for a little, and then might look into a way to include announcements on the home page, or other small features.

Thank you for reading.