An open source web application for booking and managing Art Gifts
An open source web application for booking and managing Art Gifts, designed and developed together with Helsinki Festival and presented in August 2020
Art Gifts are short 5 minute performances – of music, dance, circus and poetry – safely delivered in front of the recipients house or under their balcony. Everybody can use a simple web app to send one to a friend or relative, free of charge.
Art Gifts were originally introduced at Helsinki Fest 2020, where selected 20 artists were paid a fee per day in exchange for about eight Art Gifts to be performed around a particular district. Hundreds of Art Gifts were immediately booked.
This repository contains the application source code for the Art Gift application and backend. Everything you need to set up an Art Gift event in your own city!
The basic idea underlying Art Gifts is that you have a number of artists moving around a city, delivering gifts to people, meeting them at specific times at specific locations. People may book gifts for their loved ones for a specific time and place, but they will not know anything about the artist that will deliver them. That remains a surprise.
In Helsinki all the gifts were delivered during a single weekend, but the system can also support extended periods of time.
From a gift giver’s point of view, you enter the gift receiver’s address and other information, choose an available time slot, await confirmation, and then just turn up at the address, taking the gift receiver with you.
From an artist’s point of view, you have an itinerary of gifts you must deliver during your “shift”, all contained within a geographic region.
The central concepts you see in the application code as well as the database are:
In a chronological order and from a technical point of view, the process of runnning an Art Gifts event has roughly the following timeline.
On details about how events transpired in Helsinki Fest 2020, see the production notes
Artists are sent invitations into the app, from which they may see the gifts assigned to them.
Artists deliver the gifts, following the itineraries they were assigned.
The code as provided here was used for Art Gifts in Helsinki in August 2020. We believe it should localise to other cities and regions easily, and this guide provides the instructions for doing so.
We do, however, expect some further development work to be needed when the software is used in new kinds of circumstances. We hope you find the source code malleable to your situation, and we do also appreciate contributions in the form of Pull Requests and Issue reports to this repository.
The system is written in TypeScript, and consists of three main subsystems, which reside in three respective directories:
frontend
- The public-facing frontend application used by the general public as well as by artists, built with Gatsby and React.admin
- The back office admin application for production staff, built with React.functions
- Firebase backend functionality: Database triggers and indexes, scheduled jobs, security rules, and scripting.In addition to the code contained in this repository, the system has 3-4 service dependencies:
The following steps describe the technical setup for getting the Art Gift system going, from setting up the necessary external dependencies to having everything running in a local development environment.
This technical setup keeps you in the original Helsinki geographic region, localisation, and visual style. Once you have the technical setup done, see the Localisation guide below on how to adapt the system to your own Art Gift event.
Go to Project Overview -> Add app. Register a Web App called “Frontend”.
Once you’ve done so, find the values shown in the firebaseConfig
object under the “Add Firebase SDK” section.
In your local clone of this repository, create the dotenv file frontend/.env.development
, and populate it with environment variables based on this Firebase configuration:
FIREBASE_API_KEY=your api key
FIREBASE_AUTH_DOMAIN=your auth domain
FIREBASE_PROJECT_ID=your project id
FIREBASE_STORAGE_BUCKET=your storage bucket
FIREBASE_MESSAGING_SENDER_ID=your messaging sender id
FIREBASE_APP_ID=your app id
Go to Project Overview -> Add app again. Register a Web App called “Admin”.
Once you’ve done so, find the values shown in the firebaseConfig
object under the “Add Firebase SDK” section.
In your local clone of this repository, create the dotenv file admin/.env.development
, and populate it with environment variables based on this Firebase configuration:
REACT_APP_FIREBASE_API_KEY=your api key
REACT_APP_FIREBASE_AUTH_DOMAIN=your auth domain
REACT_APP_FIREBASE_DATABASE_URL=your database url
REACT_APP_FIREBASE_PROJECT_ID=your project id
REACT_APP_FIREBASE_STORAGE_BUCKET=your storage bucket
REACT_APP_FIREBASE_MESSAGING_SENDER_ID=your messaging sender id
REACT_APP_FIREBASE_APP_ID=your app id
Go to Authentication -> Get Started. Enable the “Google” Sign-in provider. Under Authorized domains, be sure to include both localhost
and the domain name under which you plan to deploy the Art Gift Admin user interface.
/your/artgift/clone/dir/functions/art-gift-firebase-adminsdk-credentials.json
.http://localhost:8000
and http://localhost:3000
. You can also leave the URL restrictions empty.frontend/.env.development
:
GATSBY_MAPBOX_ACCESS_TOKEN=your access token
admin/.env.development
REACT_APP_MAPBOX_ACCESS_TOKEN=your access token
This section only applies if you wish to send emails (booking notifications, reminders) out from the system.
After having setup the Firebase Project and Applications as described above, you can deploy the Art Gift backend to that project. This includes backend business logic (from functions/functions
), database indexes (from functions/firestore.indexes.json
), and database security rules (from functions/firestore.rules
).
firebase login
cd /your/artgift/clone/directory/functions
firebase use --add
, select the project from the list, and enter the alias default
.
firebase functions
set artgift.baseurl="the URL the frontend application will be deployed to"
firebase functions
set artgift.emailapi.fromaddress="the sender email address used in outgoing Mandrill emails"
firebase functions
set artgift.emailapi.fromname="the sender name used in outgoing Mandrill emails"
firebase functions
set artgift.emailapi.apikey="the Mandrill API key generated in the previous section"
cd functions && npm install
firebase deploy
The system is capable of sending out SMS notifications of bookings, confirmations, and reminders. However, depending on the telecom situation in your country, there might be some work required to set this up. By default, SMS sending is disabled, but an adaptor for Twilio is available, which you can enable if that works in your country and you are comfortable with their pricing.
To enable Twilio, go to functions/functions/src/sms/index.ts
and uncomment the Twilio sender. Then go sign up for a Twilio account and add some balance. Once you have done so, find the Project SID and Auth Token from your Twilio dashboard, and set them as Firebase configuration variables:
firebase functions:config:set artgift.smsapi.accountsid="the acccount SID from Twilio"
firebase functions:config:set artgift.smsapi.authtoken="the auth token from Twilio"
Then deploy the backend functions to Firebase once more: firebase deploy
.
If you want to use some other SMS sending service, you can use the twilioSMSSender.ts
file as an example for how to create a sender that works with the API of your provider.
Next, seed the Firestore database with initial data.
The project contains a database initialisation script that sets the database to an initial state, with an initial set of Admin users, Artists, Slots, and Itineraries. All of this information can also be managed in the Admin UI, so using this initialisation script is not strictly necessary, except for populating the first Admin users, as otherwise no one will be able to access the Admin UI. This is what we’re going to do now.
cd functions/scripts
npm install
databaseSeed.example.json
and open it in an editor. In the admins
section, add the Gmail address of at least one admin user.npm run prod:initDatabase databaseSeed.example.json
We now have everything ready to run locally.
cd admin
yarn
yarn start
. This should also open a new browser tab for the admin.cd frontend
yarn
yarn start
.http://localhost:8000
to see the app.After completing the steps in the Setup Guide, you should have fully functioning Art Gift system running locally, with a Firebase backend in the cloud. In this guide, you will customise the app to match the local circumstances of your Art Gift event.
As described above, Art Gift Slots are divided into geographic regions within a city. These regions should be small enough that artists can move around in them from one gift location to the next. In Helsinki, we divided the city into its seven administrative regions, and you should find a similar division for your city.
Once you’ve decided on the regions, you’ll need to obtain the geometries of these regions as a GeoJSON FeatureCollection. Many city authorities make such data openly available, and it can also be obtained from commercial providers. If you can’t find anything else, you can draw your regions on a map yourself using the drawing tools at geojson.io.
When you’ve obtained your GeoJSON data, place it in the file frontend/src/data/region_data.json
, replacing the Helsinki data.
Then inspect the data, and see which property in the “properties” section contains the name of the region. Make sure this property name matches the value of REGION_NAME_PROPERTY
in frontend/src/constants.ts
. For example, if the GeoJSON properties look like this:
"properties": {
"OBJECTID": 50,
"Name": "South Boston",
"Acres": 1439.8888073099999,
"Neighborhood_ID": "17",
"SqMiles": 2.25,
"ShapeSTArea": 62721306.143917084,
"ShapeSTLength": 64998.420282952466
},
The constant should be define like this:
export const REGION_NAME_PROPERTY = "Name";
Once you’ve done this, you should see the frontpage of the frontend app zoom into your city region.
On the admin side, also make sure the region names in admin/src/constants.ts
match those found in your GeoJSON - e.g.
export const REGIONS = [
"Roslindale",
"Jamaica Plain",
"Mission Hill",
"Longwood",
"Bay Village",
"Leather District",
"Chinatown",
"North End",
"Roxbury",
"South End",
"Back Bay",
];
To specify the dates and times during which your Art Gift event runs, open admin/src/constants.ts
, and find the DATES
constant. Edit it to include all the dates you’ll be running Art Gifts on in YYYYMMDD
format, e.g.
export const DATES = ["20200910", "20200911", "20200912", "20200913"];
To specify what times of day you will be creating slots for, find the HOURS
and MINUTES
constants in the same file. In HOURS
, specify the hours of day you’ll be running Art Gifts, and in MINUTES
the minutes of each hour.
For example, to support slots between 10AM and 6PM, every hour, on the hour, specify:
export const HOURS = [10, 11, 12, 13, 14, 15, 16, 17, 18];
export const MINUTES = [0];
Or to support slots between 12PM and 3PM, one every fifteen minutes:
export const HOURS = [12, 13, 14];
export const MINUTES = [0, 15, 30, 45];
Note that changing these constants do not automatically generate any bookable gift slots. They merely specify the parameters by which admin users may create slots using the Admin UI.
The frontend application and the outgoing email and SMS messages support multilingual use, and two languages are included out of the box: English (UK) and Finnnish.
You will certainly want to edit the language strings provided, as many of them contain information specific to the individual Art Gift event.
To edit UI strings to match the specifics of your event and your preferred Tone of Voice, find the existing i18n files in frontend/src/intl
and make any edits you want. Pay special attention to any URLs, email addresses, dates, and times included in them, to match the messaging of your event.
Note that after making edits, you’ll need to restart the frontend app server (the yarn start
command) before you can see them in your local development environment.
To edit the content of the outgoing messaging, find the file functions/functions/lib/messages.json
and make your edits there.
Then redeploy the Firebase Functions to apply these changes:
cd functions
firebase deploy --only=functions
To remove a language you don’t want to include (when e.g. you dont want Finnish to be included in the language choices), open frontend/gatsby-config.js
and find the section for gatsby-plugin-intl
. Edit it to contain only the languages you want, e.g.
{
resolve: `gatsby-plugin-intl`,
options: {
path: `${__dirname}/src/intl`,
languages: [`en`],
defaultLanguage: `en`,
redirect: true,
},
},
To add a new language, first add it to the gatsby-plugin-intl
section of frontend/gatsby-config.js
, e.g.:
{
resolve: `gatsby-plugin-intl`,
options: {
path: `${__dirname}/src/intl`,
languages: [`en`, `is`],
defaultLanguage: `en`,
redirect: true,
},
},
Then add a new language file to frontend/src/intl
. We recommend you copy one of the existing language files, and then edit it to translate to the new language:
cp frontend/src/intl/en.json frontend/src/intl/is.json
Then edit all the language files in frontend/src/intl
to include a translation for the name of the new language. (We use this when letting users choose the language of the gift receiver - which may be different than the UI language of the gift giver.)
"toFormLabelLanguageis": "Icelandic",
Finally, add a new section to functions/functions/lib/messages.json
for the new language, again first copying the section of one of the existing languages, and then translating it.
To help Mapbox autocomplete and geocode street addresses when users type them in, find the following constants in frontend/src/constants.ts
and edit them to match the geographic region:
export const MAPBOX_COUNTRY_CODE = "US";
export const MAPBOX_REGION_PLACE_NAME = "Boston";
Also find the same constants in admin/src/constants.src
to apply the same change to address lookup in the admin application as well.
To localise the input validation for phone numbers and addresses, open frontend/src/constants.ts
.
Edit PHONE_NUMBER_REGEX
into a regular expression that matches phone numbers in your local format.
Edit ADRESSS_STREET_NUMBER_MATCH
into a regular expression that successfully tests whether a street address contains the necessary street number for the artist to find the address.
Edit ADDRESS_GEOCODING_PREFIX
into a regular expression that extracts from a street address the part of it that is suitable for geocoordinate lookup. Although the Mapbox geocoding API generally accepts any form of valid street address, we’ve found that including apartment numbers, zip codes, and such may make it misfire, and it is safer to extract the street name and number only, when possible.
If you want to change the format in which dates and times are shown, make the edits you want in frontend/src/services/dates.ts
for the frontend application,, and admin/src/util/dateUtils.ts
for the Admin UI.
Modifying the visual style of the Art Gift frontend app is done primarily via three routes: Imagery, UI styles, and map styles.
You’ll find the image assets used in the app, including the logo and hero images, from frontend/src/images
. Replace your own brand assets here.
As a special case, the head sponsor (which in Helsinki was Helsingin Sanomat) link and logo reference can be found in frontend/src/components/footer.tsx
. You’ll want to update both the image reference and the link target for this, or remove them if you don’t wish to display a sponsor.
You’ll find all the CSS for the application in modular .scss
files for each page and component. However, the most important customisation points you may need have been extracted to two master stylesheets:
frontend/src/components/variables.scss
for default fonts, colour palette, sizing, and animation timings.frontend/src/component/typography.scss
for font definitions.Since the map is one of the most dominant visual elements in the application, you may want to customise it to match your visual style.
Go to Mapbox Studio with your Mapbox account, and author a style to your liking.
Then copy the style URL of your new style, and replace the constant MAPBOX_STYLE_URL
in frontend/src/constants.ts
with it.
Since all the backend functionality of the Art Gift system is deployed on Firebase, your only deployment concern is getting the two applications - the frontend and the admin - deployed to a publicly accessible server.
We hosted the Helsinki applications on Netlify and can recommend it. It is easy to setup, reliable, and free.
Sign up to Netlify, and follow their instructions to create two new sites from Git, and when prompted choose your clone of this repository on Github. For the frontend app, apply the following build settings:
frontend
gatsby build
frontend/public/
For the admin app:
admin
CI="" yarn build
admin/build
For both apps, add Environment Variables that match all of those in the local .env.development
files for frontend and admin, respectively. This allows Netlify to have access to the configuration for Firebase and Mapbox when it builds the applications.
Once set up in this way, Netlify will automatically build and deploy a new version of the apps when you push changes to Github.
If you want to host the apps on a static file server of your own, you can build them locally and then copy the files over to the server.
For the frontend, first make a copy of the dev environment variables to use the same ones for the production build:
cp frontend/.env.development frontend/.env.production
cd frontend
yarn build
Once the build is finished all the application assets will be in the public
directory. Copy them to your server.
For the admin, again, make a copy of the environment variables:
cp admin/.env.development admin/.env.production
Then build the admin app:
cd admin
yarn build
Once the build is finished all the application assets will be in the build
directory. Copy them to your server.
Whether the apps are hosted on Netlify or elsewhere, we recommend using two separate domains for the two applications. (In Helsinki we used taidelahja.helsinkifest.fi
and taidelahja-admin.helsinkifest.fi
.)
Once you know what your domains are, remember to also configure them to
artgift.baseurl
as described above in the Firebase deployment.MAIN_APP_HOST
constant in admin/src/constants.ts
so that the Admin UI can render links to the frontend app correctly.Once everything up and running, localized, and customised, the administration of the system mainly happens through the Admin UI.
This guide describes the major functional areas of the admin. For guidance on how to structure your Art Gifts events, you will some some valuable information in the production notes from Helsinki Fest.
The admin is divided into a handful of functional areas:
In the top left corner of the admin, you will see the current store state, and buttons for changing it.
The store states are:
In the Slots section, you can manage Slots that may then be booked by people. This should generally be done before the store is opened, according to how many artists you plan to assign to each region.
The page lists all the slots that have been added to the system, as well as their reservation state - whether they currently have a gift attached to them or not. Slots that are still available may also be deleted.
At the bottom of the page, there is a form that may be used to add new Slots to the system, by choosing a region, date, and time.
In the Gifts section, you see all the Gifts people have booked. They are shown in reverse chronological order. A section with summary statistics about the gift booking situation per region is also shown.
This is where most of your time will be spent when the store is open. People will make bookings, which will turn up on this list in real time in the “Pending” state. Review each one to see whether it is appropriate and take one of two actions:
You may also Delete a gift, which releases the reserved slot back, but does not send messages to anyone. It is meant for special cases and testing only.
In the Artists section, you manage the Artists that will deliver gifts: their names and contact information. You can add, edit and remove artists at will.
Each artist will have an automatically generated page in the frontend application, which will list all their itineraries and gift details. You’ll find the link to each artist’s Artist page here, and you can use the Send Invitation button to send them a link to their own page over email and SMS.
In the Itineraries section, you assign the artists itineraries, for when they should be at a given region delivering gifts.
There is a subheading for each geographic region, and under it a list of Artist time windows, which you can add and delete. Once you have done so, the system will automatically assign gifts to the artists in real time, and this is done again whenever a new Gift is confirmed or cancelled. You can see which gifts are under each itinerary by clicking to expand it. The Gifts page also shows which artist each gift has been assigned.
The idea behind the itinerary assignment process is:
This automated process is run again when new new gifts are booked, which means that artist itineraries may change dramatically while bookings are open. Artists should know that the scheulds they can see are not final ones, until the bookings close.
Once the store state is set to “Closed”, the automated itinerary generation is no longer done, and artist itineraries are final. You may still reject gifts on the Gift page, but this will not release the Slot anymore, but mark it as a “late rejection” which shows up on the artist’s schedule page as a cancelled gift.
In the Admin section you specify the email addresses of the people who are allowed to access the Admin interface. Only emails on this list will pass through to the admin from Google Authentication.
The source code and other assets in this repository are open source licensed under the MIT license