Giter VIP home page Giter VIP logo

codingtest-birdhouse-admin's Introduction

BirdHouses Admin Panel

This dashboard shows the status of all birdhouses using the innovative Smart BirdHouse technology.

We've got sections about the Project Design (nice descriptions of how this is organised) and the Project Log (stream-of-consciousness thoughts while developing this).

Environment variables

You can configure the dashboard through these environment variables:

  • NUXT_PUBLIC_API_BASE: Base API URL, e.g. https://example.com. Note: This variable must be set for the app to work.
  • NUXT_PUBLIC_REGISTRATION_ITEMS_PER_PAGE: Number of items to display on the birdhouses list. Default: 4
  • NUXT_PUBLIC_LOAD_OCCUPANCY_DETAILS_ON_LIST: If set to false, don't load occupancy details on the birdhouses list. Since each birdhouse requires a separate API call to retrieve the current occupancy details, this vastly reduces the number of API calls and can improve loading times.
  • NUXT_PUBLIC_OCCUPANCY_STATES_PER_PAGE: Number of occupancy states to get from API on each specific birdhouse page. You should fine-tune this based on your production data, so the occupancy list and graph are readable. Defualt: 10

Docker quick start

You can get the dashboard up and running quickly by using Docker!

Docker

You can run the prebuilt docker image with this command:

docker run -it -e NUXT_PUBLIC_API_BASE=https://example.com -p 3000:3000 ghcr.io/danieloaks/codingtest-birdhouse-admin:release

Just replace https://example.com with the real API you want to use, then access the dashboard on port 3000. If running the command locally, at http://localhost:3000

Docker compose

You can also use the default docker compose file with this command:

docker compose up

Change the environment variable NUXT_PUBLIC_API_BASE to point towards the real API, run the command, then access the dashboard on port 3000. If running the command locally, at http://localhost:3000

Development

If you want to get started developing the app, you can use the below commands.

Setup

Make sure to install the dependencies:

asdf install  # sets up the right version of nodejs
yarn install

Development Server

Start the development server on http://localhost:3000

yarn dev

Production

Build the application for production:

yarn build

Locally preview production build:

yarn preview

Start production build:

yarn start

Check out the Nuxt deployment documentation for more information.

Project design

This is a pretty standard Nuxt application, using Pinia and Tailwind!

The main interesting elements are the store which holds our state data, and how we communicate with the API. Below we'll describe how each works.

The API

Here's how API communication is handled in the app:


flowchart LR
    subgraph Site

    JsPackage(birdhouse-js<br>package)
    Plugin(birdhouseApi<br>Nuxt plugin)
    ActivePage(Active<br>Nuxt Page)
    Store(Store)

    JsPackage --imported by--> Plugin
    Plugin --provides<br>package to--> ActivePage
    ActivePage --passes<br>package to--> Store

    end

    API[Shockbyte API]

    Store --calls--> API


Basically, We interact with the BirdHouse API via the @danieloaks/codingtest-birdhouse-js package, and that library takes a lot of inspiration from Shockbyte's existing whmcs-node package.

This library is exposed via the birdhouseApi Nuxt plugin, available client-side only.

This finally gets passed to the birdhouse store by the caller, every time a method that calls the API is performed. It's done this way because Pinia stores can't access the config on setup very easily, and this seems like a usable solution for now.

The Store

The birdhouse store holds all state data that's used by the app. This state data includes the registrations and occupancy info, and is held for the life of the app. We intentionally don't persist any state data to localStorage, as we want to grab the most up-to-date data each time the page is reloaded.

The store will call the BirdHouse API as needed to backfill data, based on calls from the pages.

Here's how the elements in this store are logically setup:

classDiagram
    class Base_Info {
        setConfig()
    }
    class Registration_Info {
        currentRegistrationListPage: Integer
        totalRegistrationPages: Integer
        registrationPageItems: Map[page -> list of bhid's]
        registrationInfo: Map[bhid -> info returned by API]
        setRegistrationPage()
        getBaseRegistrationInfo()
    }
    class Occupancy_Info {
        occupancyPageInfo: Map[bhid -> pageInfo]
        occupancyHistory: Map[bhid -> Map[page -> list of states]]
        setOccupancyPage()
    }

Desired API changes

This app isn't as nice as it could be, and I'd request a few API changes to improve this.

  • GET /registration and GET /registration/{ubid}: Return the current occupancy details on these API responses. Since the dashboard must display these values, and they aren't provided currently, we need an extra API call for every single displayed registration entry that has a birdhouse.
  • GET /house/{ubid}/occupancy: Rather than paginating based on pages, it may make sense to update the design to use some kind of date selector and paginate based on entered dates? This could improve how we display the graph (see issue #5 for more details). It may also be worth checking the app/s that add data here, since we've seen a number of instances where the same timestamp and occupancy figures are recorded multiple times, with different occupancy state IDs.

Project log

This section outlines my thoughts while developing this project. It should give insight into my development progress and thoughts.


On first look this seems to be a pretty simple dashboard, with easy-to-follow designs and an API that suits this use case. My initial thoughts are to create a fairly standard Nuxt site with three pages – Landing, List of birdhouses, and Single birdhouse.

I've put together This MVP issue which outlines all the features this site must contain. And this Map mode issue describing a fancy 'map display' that I think would really suit this project once I've got the base functionality.


I've created the Nuxt base and landing page, and now I'm thinking about how and when to load the data from the API. I might make a separate JS module that only handles comms with the API, that makes sense.

There's also an issue around the navbar in the footer. It could paginate just the list of birdhouses, but could also paginate different elements on each page (e.g. on the Birdhouse Overview tab it paginates the dates, on the Graph tab it paginates the time). The footer stretches underneath the sidebar+wrapper, and it doesn't make a lot of sense for it to be there if it paginates different elements inside the content box – normally I'd want to clarify this with design. I'll tackle this element a bit later and see what makes the most sense.


Importing the icons has been a bit annoying. I've ended up with nuxt-svgo to handle loading svgs as inline elements, as it's by far the easiest way to get things up and running.


Having more of a think about the pagination in the footer, there's not any reason to include it on the birdhouse overview page unless it does paginate days and the like there as well. For now I'm going to have it be an element of the main content bit, and have the sidebar take up the entire left of the screen instead.


Alrighty, I've now got a very basic js lib ready, which is being made accessible via the birdhouseApi Nuxt plugin here. I'm a bit unsure of how to best integrate Pinia stores into the site, since with the pagination arbitrary pages can be grabbed... Perhaps I could store everything as a map/dict where the key is the page number and the value are the list of entries on that page, but that feels a bit naïve. I'm gonna do some sketches to see if I can work out how I want this to function.

After some sketching I've come to this as a rough layout:

  • Birdhouses: Map, each page to a list of UBIDs.
  • BhRegistration: Map, each UBID to the reg info (birdhouse if it exists).
  • BhOccupancy: Map, each UBID to a map of paginated occupancy details.

I've asked for feedback on the footer pagination item, to see if we are paginating the occupancy details or not. If we are, the above would work!


So the footer on the single birdhouse page does paginate the occupancy numbers, hmm. I can make this work, but it may be a bit strange using it on the overview vs on the graph.

On the upside, we are now successfully grabbing occupancy data on the list page. However, it does add a lot of waiting (I'm guessing the API isn't hosted in Australia). Because of that I've had to add a loading modal, and I've also added a way to disable those extra API calls via an environment variable. Ideally we'd be returning the current occupancy figures in the registration API response. With this I think the list page is now feature complete, hooray!

It is time to do a decent cleanup though. Things are a bit messy, especially in the Birdhouses store. I'll try to do that, and allow paginating the occupancy details (right now it's a simple list and doesn't take paging into account), before diving into any other tasks.


The refactoring went well and the store is now a lot more understandable, usable, and requires a lot less functions than it previously did.

Now when you go between pages on the birdhouses list, it reflects this in the URL with a query variable. The final bit of site navigation work to do is to ensure that the birdhouse overview pagination does the same thing, and then I can call that section done.

I've also added a decent bit of documentation now, which is nice, and setup the whole docker + docker-compose part. Had to use multi-stage images because without doing so the final image was way larger than it should've been (I could potentially reduce the size further by only copying the .output folder to the final server, I'll try that if I have space to do so).

Now I'm onto describing the project design and adding docs for the typescript lib that calls the API, hooray!


The project design has been documented and I've got some basic docs added for the Typescript lib as well. Finally, we refactored the pagination helper functions out into a composable so it's easier to share that between the list and individual pages.

I'd still like to do the map mode, but I'll do so as a PR so that this repo can be reviewed in the meantime.

Overall I'm really happy with how this project has gone, Nuxt and Tailwind feel super natural. It also feels good to have a public repo that I can point to to illustrate my front-end knowledge.

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.