BugTracker - an open-source bug tracking app

Invite your teammates, add bugs, and track their progress with the first DinoSaaS MVP

A SaaS App from Start to Finish

Our mission is to help small teams build powerful SaaS (software-as-a-service) products.

There are tons of technical resources out there for all parts of the tech stack. But building a SaaS app requires little bits of knowledge and expertise from a wide range of areas.

By building SaaS apps from start to finish, we'll cover everything you need to know - design, front-end, back-end, SEO, deployment, etc.

This is part 1 of our first SaaS app, BugTracker.

Introducing BugTracker

I built BugTracker as the first open-source web app built on Argon, the first DinoSaaS App Template.

Lightweight and easy to extend, BugTracker is the perfect open-source bug tracking app for small and nimble software teams.

This tutorial walks you through how to build BugTracker from start to finish:

  1. Clone and customize Argon
  2. Add User Accounts
  3. Build core Bugs CRUD functionality
  4. Dashboard Polish
  5. Add a Description rich text field
  6. Support for teams

BugTracker isn't finished yet (no software ever is, right?). But part 2 will be coming soon!

Chapter 1 - Customizing an App Template

1.1 - Clone the Argon App Template

The BugTracker story starts with the Argon App Template. Argon gives us a beautiful dashboard, plus tables, forms, and pages that will come in handy in later chapters.

Clone the repo using the instructions in the guide. You’ll probably want to create a new repo in Github and update your git remote.

1.2 - Pick a color scheme

Check out Color Hunt for inspiration. I chose this palette, but feel free to choose your own.

1.3 - Design a logo

Follow this Building Block from start to finish to design a beautiful logo in 15 minutes.

Then replace the logo assets in app/assets/images/brand.

1.4 - Customize colors & logo

Update the the app’s <title> in <head>.

Update color variables to match your chosen color palette in custom/_variables.scss.

For a more detailed tutorial on customizing Argon, check out the App Template guide in the Customizing the UI section.

Chapter 2 - Adding User Accounts

An app isn’t an app without users, right? In this chapter, we’ll set up user accounts and authentication.

2.1 - Install Devise

Devise is a popular authentication solution for Rails. 

Their “Getting Started” guide will walk you through the setup process:

  1. Installing Devise
  2. Generating a Devise model
  3. How to use Devise helper methods in controllers
  4. Generating and configuring Devise views

There are lots of ways for you to customize Devise. For this tutorial, however, we’ll be sticking pretty close to what you get out of the box.

2.2 - Integrate Devise with Argon forms

Devise generates boilerplate views for us with rails generate devise:views. Some of the more important ones are:

  1. Sign up - views/devise/registrations/new.html.erb
  2. Sign in - app/views/devise/sessions/new.html.erb
  3. Forgot password - app/views/devise/passwords/new.html.erb
  4. Change password - app/views/devise/passwords/edit.html.erb

Our current task is to weave these forms into Argon’s pages.

For example, Argon’s register page is pretty, but it doesn’t actually do anything. We’re going to copy the contents of register.html.erb into devise/registrations/new.html.erb. Then we pull in the Rails form helpers generated by Devise into the Argon form.

Seeing is believing! Walk through the finished devise/registrations/new.html.erb and it should start to make more sense.

We then repeat this process for our other views.

Registration page

You might also notice the authentication layout. I’ve defined a custom layout for our Devise views, since they all look pretty much the same.

I’ve declared this layout in my own controllers that extend Devise controllers: RegistrationsController, SessionsController, and PasswordsController. These small, one-line controllers allow me to say Hey Rails - use the authentication layout for this controller’s pages, but use the existing Devise controller for everything else.

I also have to tell Devise about my controllers in routes.rb:

Copy to clipboard

-- CODE language-rb --devise_for :users, controllers: { 'registrations': 'registrations', 'sessions': 'sessions', 'passwords': 'passwords' }

2.3 - Render Devise errors

Now we have all our Devise views...great! But inevitably users will be users and get tripped up on our forms. So our next step is error handling.

Devise’s boilerplate views render a partial that renders error messages, located at devise/shared/_error_messages. We’ll render this in all our Devise forms:

Copy to clipboard

-- CODE language-rb --<%= render "devise/shared/error_messages", resource: resource %>

In this commit, we add a custom _bugtracker.scss stylesheet to change the color of Devise’s error messages.

2.4 - Handle authentication auto-redirects

An important aspect of any app’s authentication scheme is gracefully redirecting users based on whether or not they’re authenticated.

In this commit, we use the Devise helper :authenticate_user! with the before_action filter. Now, Devise will check if a user is authenticated before rendering the Dashboard.

In this commit, we specify that we want users to be redirected to the sign_in page after logging out (or being redirected after trying unsuccessfully to access the Dashboard).

2.5 - Make User.name a required field

The core of this section is really just this one line in the User model:

Copy to clipboard

-- CODE language-rb --validates_presence_of :name

You’ll notice in this commit, however, that I chose this as a good time to introduce Test Driven Development, or TDD, with RSpec, my favorite Rails testing framework.

Chapter 3 - Bugs in the Dashboard

So now we’ve got this beautiful dashboard that users can log in and out of. Still not very useful.

Good thing users can’t add bugs yet…

But not for long! By the end of this chapter, users will be adding, editing, viewing, and deleting bugs.

3.1 - Generate Bug Scaffold

We’ll start off by generating a scaffold using rails generate scaffold:

Copy to clipboard

-- CODE language-sh --rails generate scaffold Bug title:string status:integer platform:integer

As you can see in this commit, this generates lots of files for us. After running rails db:migrate, we’ve already got a pretty useful (but ugly) bug tracking app!

3.2 - bugs/index - Showing all Bugs

After seeding the database with some bugs, bugs/index looks like this:

bugs/index (before)

Over the next several commits (starting here), we’ll style this dashboard using Argon’s built-in “light table”. Check it out in action here.

And now our Bugs table looks like this:

bugs/index (after)

3.3 - bugs/show - Show one Bug

From this commit to this commit, we take bugs/show from this:

bugs/show (before)

to this:

bugs/show (after)

It may look like a lot of code, but it’s all taken from pages in Argon with some slight tweaks here and there.

3.4 - bugs/edit - Edit a Bug

Similarly, this commit takes us from this “scaffoldy” edit page:

bugs/edit (before)

to this:


3.5 - bugs/destroy - Delete a Bug

Basic “destroy” functionality is fairly straightforward. In this commit, I introduce a bit of complexity with a confirmation modal prior to actually destroying a bug.

Deletion confirmation modal

Beautiful confirmation modal, right 😉? It comes included in Argon!

(In this commit I start deleting the bug the user actually clicked on…🙃)

This modal is eventually pulled out into a _confirmation.html.erb partial.

3.6 - bugs/new - Create a Bug

This commit takes us from this:

bugs/new (before)

to this: 

bugs/new (after)

This chapter skips over several changes like adding edit and delete icons to bugs/index, date formatting, and success modals.

You can check out all the commits from this Pull Request to dig into how those changes were implemented.

Chapter 4 - Dashboard Touch-ups

In this PR, we apply some general polish to our Dashboard UX to take it from the boilerplate Argon dashboard to one that feels custom to BugTracker.

We go from this:

Dashboard (before polish)

to this:

Dashboard (after polish)

The primary changes are:

  1. Some code refactoring - pulling some code into partials
  2. Side navbar - Removing all the unused links
  3. User navbar dropdown (top-right) - Removing unused links from Argon
  4. Adding a footer

Chapter 5 - Description & Action Text

As a developer, you’ve probably noticed that these bugs aren’t very descriptive. Users need to input detailed bug descriptions with steps to reproduce them, plus screenshots of what they look like.

The Rich Text Building Block walks you through how to add a description rich text field that lets users do all of these things.

Afterwards, bugs/edit will look like this:

bugs/edit with rich text input

Shoutout to placekitten for the adorable kitten photo!

Chapter 6 - Add Accounts and associate with Users and Bugs

I’m not gonna lie - this chapter really should’ve been immediately after Chapter 2 where we added Users with Devise. But better late than never!

You may have noticed the glaring oversight in BugTracker’s security - every user can view, edit, and delete all bugs from all users. Not ideal.

Users should only be able to view, edit, and delete their own bugs. But really, users belong to teams, and all teams should have access to all bugs reported by users in their team.

So we’ll take this as a great opportunity to introduce an Account model.

Think of an Account as a team. An Account has_many Users, and Bugs belong_to an Account rather than individual Users.

6.1 - Set up Account model and associations

In this commit, we create the Account model and its has_many association with Bug.

(Later we’ll add the belongs_to Account association in Bug that we forgot here…)

6.2 - Create new Account when users register

We don’t yet have the ability for Users to invite other Users to their Account. Right now, we create a new Account for all Users when they join.

Creating both resources at the same time adds a bit more complexity than it might seem at the surface (would’ve been much easier to do this earlier!).

When a User registers, we need to create 2 resources: a User and an Account. To do this all in one fell swoop, we’ll take advantage of the Rails accepts_nested_attributes_for class method. By adding accepts_nested_attributes_for :users to Account, we’ll be able to create a User when we create its parent Account.

To create both resources at once with accepts_nested_attributes_for, we need to go through the parent (Account), not the child (User). Which means we need to move our registration views and controller methods from Users to Accounts.

See this commit for how we accomplished all of this.

6.3 - Bug CRUD operations go through Accounts

Now that Accounts are in place, we need to integrate them with Bug CRUD operations (Create, Read, Update, Delete).

This means a few things:

  1. When a user creates a bug, it needs to be associated with their account.
  2. A user should only be able to see Bugs associated with their Account, and no one else’s.
  3. A user should only be able to edit/destroy Bugs associated with their Account, and no one else’s.

We fix this in BugsController by only querying for Bugs through current_user.account. You can inspect those changes here.

Coming Soon

That's it for part 1! If you've made it this far, be sure to show us some love with a star on the Github Repo.

Dying to know what happens next? (I know, real cliffhanger here...) Share your email below to get notified about Part 2 and future open-source MVP's. I've got exciting features planned like:

  1. Inviting teammates
  2. Assigning bugs to teammates
  3. Search
  4. Bug conversations
  5. Slack notifications

What do you want to see next? Whether it's new BugTracker features or an idea for a new MVP, I'd love to hear from you. Shoot me a message at hey@dinosaas.com.

Build beautiful products, faster than ever

Be the first to know when we release new App Templates, Building Blocks, and MVPs.

Thank you! Click the link in your inbox to confirm your subscription.
Oops! Something went wrong. Email us at hey@dinosaas.com if this keeps happening.