Creating a Project Calculator MVP in 3 weeks with React and Gatsby, the Appstimator

Adam Roberts
Fortnight
Published in
10 min readDec 2, 2019

Hey! I’m Adam, a designer and developer. I’ve been doing it since the days of dial-up. I’m not a writer, but I know stuff.

Before we get in to this, you must know I made an instant switch from traditional web development, to the modern JAMStack — no more than a year ago. More on that in another write-up.

I was recently tasked to design and develop a project calculator. Similar tools can be found when you search for “How much does a website cost?” or “How much does it cost to create an app?”. There are quite a few, but they all ask the same questions and I felt they weren’t intelligent enough to do it right.

There is however a project calculator created by Platform that’s very good and feels really smooth in its transitions. But it reminded me too much of TypeForm, although they’re confident it’s custom built.

The team at Fortnight (where I work and comfortably create awesome apps and websites) wanted a way ask the right questions, where each question would carry a value based on market insights and provide the users with a grand total on a results page. We would then need a way to attempt to capture that person as a lead. Sounds simple enough right? I thought so.

Before I begin any project, I like to know everything, or as much as possible very early on. More specifically, how it will be developed. Is it a giant multi-step form in PHP, does it have a CMS to host the questions? I ask myself all sorts of questions about the development side of projects, so I have a clear understanding of the end result we’re trying to achieve.

First things first, remember “each question needs to carry a value”. This was the first thing I thought of, because at the end of the series of questions the user will be asked, it needs to do a calculation. So I based my theories on this principle. Could it have been done in PHP, yes. Did I want to do that, no. What better way to make an application remember values? I chose React for state management etc but mainly because I’m love with it.

Why Gatsby?

At this moment in time, I think it’s just personal preference. Me and a friend created a grid framework in React that no one else has and is Gatsby-ready, so it made sense to utilise that from the get-go.

Secondly, Gatsby is just amazing and it’s easy to get up and running.

The Plan

I estimated 3 weeks to create the MVP. 1 week to design it. 2 weeks to build it. The first day was purely writing down how I’d build it — as mentioned above, then once I was feeling confident I moved on to the next step, Wireframing and UI Design.

Design 👨‍🎨

I did all the design work in the infamous Figma, mapping out how the questions would be presented, how the options are chosen, the interactions of buttons, how the results are shown etc. Baring in mind this is an MVP so being a little rough around the edges is totally fine.

Fig 1. Appstimator Wireframes

I took to a simplistic approach to the layout, showing as little information on the screen as possible to guide the user through the array of questions. Each question/stage requires 3 clicks, sometimes just 2 clicks depending on the question. With no other type of input required, I believe this is a good amount of necessary tasks we need from the users.

With all of the wireframes done, I begin to do some visual research and think about how we want the interface to look. Once I had a couple of ideas in mind, I then begin by creating a Components page / library and set up a series of cards for Typography, Colour Palette, Button and Input styles, Iconography and bunch more. This is where all the “master” symbols are created.

Fig 2. Component Cards in Figma

I did a small amount of exploring on the design style with the buttons and inputs until I landed on something that I was happy with. The colours however, I wanted to be brave and try a palette I’d not really tried before. But using lots of colours can be challenging. I love a challenge.

For the typography, I paired F37 Ginger with GT Pressura Mono. I went with the mono font because I feel a mono typeface fits perfectly with the subject of tech.

Next up, applying the component elements to the wireframes. But I don’t just stick them on top and change colours. I prefer to do it from scratch again and go over my original thinking to see if I can make better choices this time round — it’s a process that’s always worked for me. So as I work through the first screen and mould the elements together, trying to apply the colours in different ways, I eventually land on clean, light interface that I want to take forward. I then start applying the chosen visual route on to the other pages I create.

Fig 3. Most screens with design applied

By the end of the week I’ve created a sleek, easy to use interface and after a few internal discussions, all the designs are in place and signed off. We now dive right in to the development of the product.

Development 🤓

To recap, I chose React and Gatsby as my weaponry to hit the ground running. This won’t be a tutorial on how to develop it step by step, but I want to give you some highlights that are noteworthy.

We need these elements;

  • Somewhere to hold all the questions and what they’re responded with.
  • Reusable components that are used on many screens throughout the apps journey, sometimes for different purposes.
  • The ability to calculate the respondents chosen values to provide a cost estimate and cost breakdown.
  • Functions to send all that data to capture it as a lead or help them further.

The most important part of this project was the questions. I needed to know what that was going to look like for me to give the app some behaviour. What we did know was there needed to be 1 main question, “What type of product are you creating?”. This meant we could have 2 parts to it all. 1 for “Website” and 1 for “App”. Depending on your choice, would load in a set of questions.

For this, we create 2 pages. Website.jsx and Mobile.jsx.

Additionally, we need 2 stores. 1 for each of the above. The store is where all the meat and potatoes live. Here’s a little template snippet;

import { observable, action, decorate, computed } from 'mobx';
import _ from 'lodash';
class WebStore {
// Initial index used to capture each question
activeIndex = 0;
// First question after they've chosen "Website"
initialQuestion = []
// The set of design questions and prop options
designQuestions = []
// The set of development questions and prop options
developmentQuestions = []
// We also want to give the user of choosing "Both"
get bothQuestion() {
return this.designQuestions.concat(this.developmentQuestions);
}

// We then have a series of functions
handleQuestionSelection = (e, block) => {
block.answer = e.target.value;
if(e.target.value === "no) {...}
}
// Pagination
moveQuestion = (nextQuestion = true) => {
if(nextQuestion) {
this.activeIndex = this.activeIndex + 1;
} else {
this.activeIndex = this.activeIndex - 1;
}
}
// We have a few more functions below that get the active Question you're looking at, get the total calculation etc.
}
// We then use mobx decorate
decorate(WebStore, {
activeIndex: observable,
initialQuestion: observable,
designQuestion: observable,
developmentQuestions: observable,
handleQuestionSelection: action,
handleAnswerChange: action,
moveQuestion: action,
bothQuestions: computed,
total: computed,
activeQ: computed,
activeQSet: computed,
}
const WebsiteStore = new WebStore();
export default WebsiteSite;

I’ve left a few functions out as well as the details of those question arrays on purpose as I only wanted to give a highlight of how it’s put together from an architectural perspective, so I do apologise if you came to steal some code.

Moving on, what we have here is a store that contains the arrays of all the questions which we’ll use to map through in other components. We then have functions that interact with those questions, to provide us with additional functionality. In essence, you’re presented with the first question [activeIndex = 0]. Once you’ve answered the question, we allow you to move on [WebsiteStore.moveQuestion…]. As you keep answering questions, it’s increasing the [activeIndex], so inside the store the functions I’ve written can ascertain which question you’re currently on and change other functions later on. That was the first part of the job that was the hardest.

However, my original question set was based on there being a set number of questions. But after I got through most of the development with the components etc, the question set had to change to allow for a conditional question at the beginning (initialQuestion). This made things a lot harder than what I anticipated and after a few days of extra development (what I thought was going to be a bit of a set back), we got it working!

Mapping through the questions is the easy part. This is the first one;

// Imports
// ------
import React from 'react';
import WebsiteStore from '@stores/websites';
import Question from '@parts/question/question;
// Component
// ------
class Websites extends React.Component {
render () {
return (
{WebsiteStore.initialQuestion.map((block, index) => WebsiteStore.activeIndex === index && (
<Question
key={index}
block={block}
isInitial
/>
}
)
}
}

So after you’ve chosen “Websites” for example, you’re taken through to that page, where you’re presented with the [initialQuestion], hence what we are mapping and the isInitial prop. Once this has been chosen, it takes you through to another page that maps through the next set of questions designQuestions[array], developmentQuestions[array] or both(function returning [array]).

The question component is too large for me to show here. But i’ll go over some of the basics;

// Import necessary elements... react, your store, button, radio input, modal box for explainers, the grid and observer(mobx-react)// Styles – we're using styled-components. Styles are separated.
// ------
import {
Jacket,
PageNumbers,
Title,
Pagination,
ModalButton
} from './questionStyles';
class Question extends React.Component {
// Constructor for props and state
// Functions for handling answers (Yes/No, the options) etc, which save the chosen option back in to the store for example;
handleYes = (e) => {
WebsiteStore.handleQuestionSelection(e, this.props.block)
}
// A function to check if already been answered (if user goes back, then populate states accordingly) render () {
const {
answer,
question,
noOption,
responses,
hasModal,
modalQuestion,
modalContent,
} = this.props.block;
return (
// All the components and other magic...
)
}
}

Within this question.jsx file we’re grabbing the options provided within the block={block} prop. Each of these are declared on each question in the store. Make sense?

There are lots of conditional arguments within the question file, like pagination, respond options, setting states. If you go back to the previous question, it remembers your choices, etc.

Essentially, we map questions in an array from the store. When you choose an option, it saves the values of that option back into the question. After a series of questions have been answered, you’re taken to the results page. And then we map the store again, but only pulling out the values you responded with.

Fig 4. Results
{
question: 'xxx',
responses: [],
response: {
text: null,
value: 0,
}
}

Test your work

After I started seeing the results being populated with information, we then have to test it further. I ran through the questions several times, taking note on the options chosen. I went forward and back and forward again making changes. The results were sound. Perfect!

Whilst in the middle of questions or while on the results page, I tried refreshing the page. This gives you null data because it’s not using session storage, we’re using a mobx store. I utilised the { navigate } from 'gatsby' to take the user back to the beginning if they tried to go to a page without first selecting some options. This now works as intended.

Fig 5. Walking through the questions

Sending this data

On the results page you can contact a recommended agency (only 1 agency in this MVP right now but the plan is to introduce a new bit of development that selects from a pool in the next version). Or you can request for 2 more quotes.

Which ever option is selected, they both perform the same task, just go to different places. We have a simple form that asks for Name and Email and everything else is in hidden fields (a simple map of the store just like the results breakdown). I’m using Netlify and Axios to send the email.

To conclude

Using Gatsby helped me get this developed in a really short amount of time. There were certainly some challenges but that was more on how I used React to piece it all together. Anything can be achieved with React and it’s a beautiful framework to develop in.

Although the code samples above aren’t complete, I do hope they still potentially help some young developers. Any questions, please feel free to ask in the comments 👍

As this was a little side project at Fortnight it’s given us some great insight on how we can get from idea to working concept quickly and easily. And now that it’s in use, we can start looking at feedback and building upon that idea to create a better version of it in the near future.

--

--

Adam Roberts
Fortnight

Award winning designer & developer with a passion for branding, specialised in UI/UX Design and development with React & Gatsby www.grilledpixels.com