Building a PWA Conference Management App in 3 days [Part 2/2]

September 02, 2018 0 Comments

Building a PWA Conference Management App in 3 days [Part 2/2]



If you would like to know the background of the conference and event management flow, checkout the part 1 of this series.

In this post, we will talk about the app feature, technologies used in the project and how we handle certain details programmatically.

Sourcecode and demo urls provided at the end of this article.

It’s because we don’t need a hybrid / native app (Android / IOS / Ionic). We only need a website that is able to be added to home screen and cache the application files so the subsequent payload is faster (only the data reading & update).

We need something that can be accessed by all phones/laptops, obviously web is a good place. It’s the easiest place to onboard our volunteers.

By the way, we don’t expect the app to work offline, because we need real time database update for attendees activities. We have stable & strong wifi connection in the event hall, so there’s nothing to worry about. (Though, we did have backup plan if internet connection failed)

*TL;DR; Please note that PWA is not just about adding to home screen, because you can add any website to home screen anyway (no prompt, with few more clicks in browser), it’s about better user experience.

These are the technologies we used to build the app:

We develop, test and run the site at $0 cost (thanks Google ❤ for generous free quota!)

There are 8 features in total, volunteers has limited access to the app based on their roles, admin has additional rights to manage the data:

A pretty standard login / sign up screen. When the app is launched in setup mode (for first time bootstrap), admin need to sign up and grant herself all access. Setup mode should be disabled in production.

login / sign up screen

We use Firebase Authentication — Email / Password to manage user logins. All volunteers will sign up themselves (normal mode). They have no access to any features after sign in though, until later assigned by admin.

Firebase Authentication

As mentioned in previous section, once volunteers sign up, admin can manage each user access via RIGHTS screen.

manage user rights screen

Here is the real time data stores structure for user rights:

We need a way to setup attendee data. We have the list in Google sheet, so what we did was convert the list into json format, and use LOAD screen to load data into our Database — Firestore.

load attendee data

This is how the data look like in database

attendee data in Firestore

Attendees need to know their badge id before picking up badge in registration counter. In case attendees missed their email, volunteers will use the SEARCH screen to help them to locate their badge id.

badge locator screen

There are two mode of checking in attendees:

  • pair mode: 2 volunteers work in pair, one verify identity, one distribute badge
  • single mode: 1 volunteer handles both identity verification & badge distribution
check-in screen

When check-in is in pair mode, another volunteer will use BADGE screen to help finding badge. The assumption is that identity verification process is faster than badge distribution (need more time to find badge from the stack).

For example, volunteer Test A (check-in) and Test B (badge) work as a pairs. Below is how the process look like:

Volunteer Test A verifies and check in attendee 333–3 and 444–4.

check-in (pair mode)

Volunteer Test B doesn’t need to listen or look at Volunteer Test A’s screen. By using BADGE screen, Test B can view the badge id in real time, focus on finding the badge and clearing the queue.

badge distribution (pair mode)

In case of wrong check-in, which rarely happen, volunteer can click on undo and confirm the action.

undo check-in

Volunteers use the SHIRT screen to manage goodies distribution. Before attendees entering the goodies hall, volunteer will verify their badge id, each attendee should get only one goodies bag.

SHIRT screen for goodies distribution

We use Google Forms to gather user feedback. Feedback data is then populated to Google Sheets automatically.

Example of how data look like in Google Sheets:

Survey result — Google Sheets

Volunteers need to verify if attendees completed the survey before accepting their lucky draw ballot. We use Google Sheets API to accomplish this - query the data in Sheets as json.

The easiest way to do this is:

  • Setting the Sheets’s permissions to “anyone with the link can view”
  • Enable Sheets API by clicking on the button in this quick start guide by Google or enable Sheets API in Google Cloud console. We need to get the API key in order to query survey data in our Google Sheets

The below video show volunteer verifies attendee 111–1 (completed survey) and 333–3 (not yet complete survey, LUCK button is disabled).

08. Admin: Live event status update [DASHBOARD]

Admin will monitor the real-time status each checkpoints via DASHBOARD screen. Actions will be taken if needed to boosts the activity (e.g. announcement and reminder to collect goodies).

DASHBOARD screen — live status

The chart is drawn using Chart.js. Take note that the total attendees input box is actually a number that we enter manually, we do not count / sum the total record of people documents (because there’s no count query in Firestore, though I can sum it myself). Anyway, it’s ok for us to just enter a quantity and get the calculation.

This is how the database looks like:

Below is how a complete attendee record looks like in Firestore.

The above record means:

  • John Doe checks in at 14:59:34 by volunteer Test A. He got his badge 10 seconds later at 14:59:44 from Test B.
  • John Doe collects his goodies at 15:24:46 from Test B.
  • John Doe drops his lucky draw ballot successfully at 14:34:09, verified by Test B.

The above data provide us insights on attendees activities, we use later for our reporting and statistics.

The whole project is quite simple as you see above, no complex logic or calculation involved. Most of the time spend on design iteration and building the PWA (Angular).

If you are new to Angular, to start an Angular PWA project is pretty easy with Angular CLI.

# after installing Angular CLI, run the below command to start a project
ng new event-manager --routing --style=scss --skip-tests
# adding firebase and charjs
cd event-manager
ng add @angular/pwa
npm install firebase angularfire2 chart.js
# configure firebase
npm install firebase-tools -g
firebase init hosting

The first command creates a new project folder event-manager with routing setup and using scss as style preprocessor, because this is a one time project, so we do not want to write unit test 😛.

The second set of commands adding PWA features to the project and installing a few dependencies for firebase and chart.js.

The last set of commands adding firebase deployment config to the project so we can deploy & host our site in Firebase.

For details setup steps, please refer to README in github:

If you realize, CHECK-IN, SHIRT and LUCKY screen looks the same with different button naming.

In fact, it’s the same component in Angular, just different route & fields update :

same component in Angular with different routes

I spent the most time iterate on this screen. Initial design was just an input box with type tel , then we add in shortcut keys for entry, then adding the key pad on screen.

This design and the consistency make it easier to train the volunteers and their roles interchangeability because it’s intuitive enough.

We use differentiate background colours for the status and action button. Volunteers are more alert on the status changes and actions performed.

Look at the screenshot above, there’s one minor design details we put in, but a very effective — After check-in the attendee, the record still show on screen (as green background), but the input box is cleared & ready for next input, this allow user to continue next check-in, save volunteers time for a CLR click.

We use Angular Reactive Form to handle that. By right, the input box text is reactive, once 4 digit is entered, we will query database for result, and display the result card. Clearing the input box means clearing the result card.

However, we want to clear input box without clearing the result card. Angular Reactive form provides a method to update the input box without firing update.

// update input box without firing event update
this.form.get('badgeId').setValue('', { emitEvent: false });

By doing this, we are able to preserve the result card until next entry.

Little that the volunteers know, the CHECK-IN, SHIRT and LUCKY screens actually support keyboard shortcuts on desktop very well.

Once volunteers focus on the screen, we will capture the keyboard events by using HostListener decorator in Angular.

@HostListener('window:keydown', ['$event'])
captureKey({ key }: KeyboardEvent) {
// login to handle keystroke

There are only 13 characters + backspace allowed for input:

// only backspace or 13 characters allowed
if (key === 'Backspace') {
// handle backspace
const possibilities = '1234567890 .-';

You might be wondering why we allow <space>, <dot> and <dash>. These are quick ways to perform the below actions:

  • <space>: Same as the <CLR> button on screen, clear input box
  • <dot>: Same as the <Green> button on screen, confirm the action
  • <dash>: Same as the <Undo> button on screen, undo the action

In fact, when you click on <CLR>, <Green> or <Undo> button, what we do behind the scene is adding one more character in the input box, so the logics will trigger accordingly.

This allow us to reuse the same logic for both desktop and mobile entry.

Take a look at the survey result sheet again:

Survey result — Google Sheets

We only need to read column B data to check if the attendees completed the survey. Below is the url format for that query:

const range = ‘survey!B2:B’;
const url = ${environment.sheet.baseUrl}/${}/values/${range}?key=${environment.sheet.apiKey}&amp;majorDimension=COLUMNS;
  • environment.sheet.{baseUrl, id, apiKey} are the 3 values that will change base on the environment (dev, production), in Angular we manage environment variables in src/environments folder.
  • range is the range of data we want to query, in our case, we said that the sheet must be named as survey , we need data of the whole column B, B2 onwards.
  • majorDimension=COLUMNS query param means we want to read column data.

We use chart.js to draw charts. In Angular, we can create a chart directive (e.g. ioxChart ), so we can assign the chart config to draw chart.

<canvas [ioxChart]="checkChart" height="400" width="400"></canvas>
<canvas [ioxChart]="shirtChart" height="400" width="400"></canvas>
<canvas [ioxChart]="luckyChart" height="400" width="400"></canvas>

The directive code is pretty straight forward:

@Directive({ selector: '[ioxChart]' })
export class ChartDirective  implements OnInit {
@Input() ioxChart: any;
  constructor(private el: ElementRef) {}
  ngOnInit() {
const nativeElement: HTMLElement = this.el.nativeElement;
new Chart(nativeElement, this.ioxChart);

We get the DOM element binding & initialize the chart.js Chart function with our chart config to draw the chart.

Since our data size is small, we use Google Sheets for reporting and statistics on all event activities — ticketing, app data & survey result.

For those data in Cloud Firestore, we export those to CSV and import to Google Sheets. You might be wondering how we export to CSV because there’s no export to CSV function in Firebase console.

Since we only need to do it for one time, I temporarily write a line of code in the app and query the data as JSON, then convert the JSON to CSV (you may use online tools like this, or command line tools like this to do so, take note on data privacy).

Probably Firebase team can consider to add this feature to console?

There are a few interesting stats that we get from data we collected. Let me show you one.

Turn out, Malaysian are quite on time 👏! 81.5% of the attendees wake up so early & make it on time on Sunday morning 😙. Altogether, 93.5% of attendees come before 10am (keynote starts at 9, actual sessions start at 10).

The app is not perfect (need tighter security, more efficient db query, better code, unit test etc). It is built by one person only (me) in 3 not-full-days (working full time for company).

However, it works like a charm! All checkpoints (registration, goodies distribution, lucky draw dropbox & badge id locator) have minimal queue / no queue at all, process is smooth, and the best thing is we didn’t pay anything for the app services, hah!

If you are interested to play around with the app, want to use or enhance it for your next event, here you go:

Of course the app is just an element to smoothen the event flow, people is the key that make the event a successful one. It takes a lot of efforts, commitments and passion to host an event.

That’s all! Let me end this post with a lovely photo. These are all the volunteers that make time to help out the event, thanks again. ❤

volunteers group photo

Tag cloud