Species DB SPA

For quite some time slowly but steadily I have been working on a project for my employer. Here is a bit of explanation that should explain why do we need it and how it can improve our department's work.

The problem.

Our company has a huge exhibit area with a bunch of devices that are supposed to show content to our guests. The content is rather specific - it is information about marine life, such as species name in 2 languages, its Latin name, image, might be an article about it or image gallery.

Next, it should be mentioned that dozens of people work with those species and responsible for the accuracy of info about them. Almost all of them have been storing info on their own which inevitably leads to limited accessibility to info, inaccuracy, and outdated info among different personnel.

Our department's responsibility is to keep all info about species in exhibits up to date with is rather troublesome since the data must be collected from different people, quite often its format is also different and so on...

The idea.

So, in order to make things easier, I suggested creating an application for local use and connect it to DB that will contain all info and become a single source of truth for everyone. Obviously, my chief said let's do it, so with a bit of planning (might be too little planning but anyway) I started the following project.

I came up with the plan to create DB, connect it to headless CMS with an API endpoint that would be used by SPA.

The vital part

New SPA will not only show content to personnel but more importantly will replace old software that has been used in the company for years. That adds one crucial feature to our SPA that must absolutely be done. I'm going to describe our old software to gradually approach "that feature" and my goal.

As was said earlier, our company has almost a hundred all-types devices that show content to visitors. Content mostly consists of articles and image galleries. All devices despite having different content have similarly designed applications.

OldApp.jpg

This is an example of an old application running on a tablet. Basically a slideshow with images and labels.

All those applications were managed by another app in a single station. It contained all the data about all applications in exhibitions. It was used to generate applications with a fixed template. It was created with Yii framework, worked pretty well with only 1 problem - it has a closed source code, protected by SourceGuardian. That means that if we need to make some changes to the UI of generated applications (change a template) we simply could not do it. So here is my final goal:

  1. Create SPA that will allow us to store all needed info about hundreds of species, retrieve it and show it a neat way.
  2. It must be able to "generate" another small, simple, flat-file application with a selected set of data from DB.
  3. Would be nice to have several templates for different layouts...

Tools and things I practiced

DB tool of my pick is a MongoDB, for no particular reason, just because I'm used to working with it.

For headless CMS I decided to go with Strapi CMS because it is fairly easy to implement and has pretty much everything that I need. It is going to be used by a few people who will work with content.

SPA will be built with React.

As for coding, well, I got tons of practice with array methods, working with complex deeply nested data in objects and arrays, hooks obviously - useState, useEffect and useReducer, a bit of routing with React Router, Material-UI components, and all those fetching and async functions. As a bonus, I did a bit on the back-end as well with Express with is also a nice experience, tough though.

The process

...is somewhat standard, so I'm going to briefly mention all the steps of the process.

  • Create a new DB in Mongo
  • Set up local Strapi project
  • Add content types to Strapi according to a planned schema
  • Configure relations between "tables" of DB in Strapi
  • Start filling Strapi with info.
  • Now it's time to create-react-app
  • Connect SPA to API, make sure that SPA gets data from DB
  • Start building SPA

My final result at this point

kinds table.jpg

Application has a bunch of pages created with React Router, here we can see the following pages:

  • Search page
  • 4 pages with content in a form of table
  • History page
  • Maps

maps.jpg

This a simple layout to show several maps that has a location of all devices in exhibits area.

history table.jpg

The history page wasn't on my to-do list and was proposed by my chief much later to monitor content managers. It was a bit tricky to do. With a bit of research, I decided to use Strapi webhooks to make a request every time some entry being created/edited/published/deleted. For that, I have to create a server with Express JS, add a couple of routes to add a log entry to DB and retrieve it later by SPA.

content.jpg

Another page with table of content.

Now let's see a bit more closely on tables. I used "@material-table/core" component to show tables, which is pretty nice one and has a lot of handy features:

  • Sorting on all columns
  • Filtering (right under col name), good thing to search for a specific entry
  • Configurable columns - if a user is not interested in a certain col he can just hide it
  • Exporting data - it has UI and sends an object with data currently in the table. Add here "PDFMake" package and you can save data in a file.
  • Has a build-in search
  • Allows to render components in cells

Here are a few pictures of how I used available features to enhance my tables. Species table, which is also the most used one, has custom components instead of just strings (or img) rendered. Here is an example.

Last 3 columns (cover image, gallery, tanks - accordingly) have custom components

custom.jpg

The last 3 columns (cover image, gallery, tanks - accordingly) have custom components. Gallery, for example, has just an icon with the number of images in the gallery (red styling if empty). It is also clickable.

item gallery.jpg

By clicking on the icon user will see a popup panel with all "gallery" images saved to a Species table entry, showing an array of images in a Material-UI Drawer component. (Material-UI was extensively used in this project for its good out-of-the-box solutions).

Images added to Strapi normally goes through a build-in image resizing process, to provide users with a bunch of different options (icon, small, medium, large, and full)

Each image has a link to open it in a new window in full size, while images in the drawer are of a smaller size.

item info.jpg

The Name column also has a drawer on click that contains several blocks of information about the selected entry and its connections to other tables.

But the biggest and most important feature of this SPA lies in a Contents table. This is a list of applications used in the exhibits, and species contained in each one. Because of the newly created DB, we no longer need to feel every single application with content manually, even if it is totally the same data. Now all data comes from DB. So, here we go.

By clicking on a content's Code cell, the user can see another Drawer with 2 tabs:

  1. List of all species listed in this application, with all data related to it. It allows the user to check if all data is filled. All empty required fields will be highlighted with red fill and a notification will be shown. If everything is fine, the user can go straight to exporting the app.
  2. By going on the Export tab, the user will have a bunch of templates to choose from, and a button to firstly generate the app on the server, and then download it as an archieve.
Video_2021-06-28_160927.gif

About templating. Templates are not fixed in the SPA and their blocks are being generated based on the data from the server, which is requested at the time of Export tab opening. In other words, when the user clicks on the Export tab, SPA sends a GET request to the server (server.js will be described later), asks for a specific catalog content, then generates a block for each folder in the catalog. The idea was to make it possible for a person with just some basic knowledge about HTML/CSS to actually create new templates and add them to SPA. All that needs to be done is to create a new folder in the right place on the server, copy an old template (sibling) or make your own new one and place it in that folder. That's it, no need to change anything in SPA or even know how to do it.

Generating application. Finally. This is a huge part that took some time to implement since generation must be done on the backend. Well, I didn't work with Express ever since the course I took to learn Development, so it was a bit tough. So, long story short, I used the following packages - express, path, fse, mongoose, ejs, body parser, archiever.

The process of generating an application in SPA seems somewhat like this:

  • set POST request with JSON data from the table (object with info about 1 content and all species that must be listed in it) in the SPA. So, SPA provides the server with all the data it needs to fill the EJS-based template with info.
  • The server saves incoming data, and prepares new clean "files" for a future app - copy empty "reference" app to a new folder called by content code (from incoming data)
  • FSE to copy files to a new folder, copy images from Strapi folder (by name)
  • When all files of the application are copied the only thing left is index.html so generate it using EJS and incoming data.
  • Create an archive with a newly generated application. Name it and copy it to the export folder. Send the name of the file back to SPA
  • SPA receives filename and creates a link to download it from the server
  • Done.
templ.jpg

As a result, we will get the following files in our archive. Now, all we need is to place them on our device and start with a browser in a kiosk mode.

Server.js

This file contains the back-end logic I wrote with Express.js. It has routes to save logs, send available templates for rendering, receive data from SPA and generate applications with it.

Conclusion

That was hard. Hard to implement so many unknown things. But this project was a really good one, I'm glad that I decided to do it because it gave to so much valuable practice and a huge push to improve my skills, search for a solution where I did not know how to even start.

React

Strapi

React Router