Skip to content

eisbuk/EisBuk

Repository files navigation

EisBuk

Booking management for ice skating lessons

As an admin you can:

  • login as an admin in an organization.
  • create slots
  • slots have the following properties:
    • multiple intervals
    • type: on or off-ice
    • categories: competitive level of athletes for a given slot (can contain multiple aptitute levels)
  • mark athletes as present or absent for each slot/interval + get a print ready attendance sheet.
  • manage athletes, with data like: birthday, category, covid certificate release date, etc.
  • view and manage your slots in calendar view.

As an athlete you can:

  • view available slots without logging in, using a secret uinque url as authentication.
  • book a slot according to your level and the type of session you need.
  • view your booked slots in calendar view.

Built with:

    • ReactJS - open-source JavaScript library for building user interfaces.
    • Firebase - Google's backend as a service with corresponding SDK(s)
    • ESBuild - a lightning fast bundler/transpiler for JS (written in Go)
    • Vite - a fully featured HMR dev-server, bundler and more, built on top of Parcel and ESBuild

Repo managed with:

    • pnpm - a fast and storage efficient pacakge manager (pnpm stands for "performant npm")
    • Rush - a monorepo management tool built for large projects, seamlessly integratable with pnpm, npm, yarn

CI

    • Click here to read about the CI infrastructure for the project

Working with the project

Installation

The entire repo is manaaged by Rush. You don't need to install rush or pnpm. You'll need to make sure the common/scripts directory is found in the system PATH. If you're using direnv this can be achieved by adding the line

PATH_add common/scripts/

to your .envrc file. The scripts directory also contains utility scripts, for instace to start the emulators or run firebase.

npm install -g @microsoft/rush

Additionally, we're using pnpm, which you can install manually or have Rush install for you. To install manually, run:

npm install -g pnpm

With management tools installed, run rush update from any point in the repo to install all dependencies per package basis. Additionally, it makes sense to run rush build in order to build all of the lib styled packages (shared, functions, etc.). The client app is built manually.

Day to day workflow

As the repo is managed by rush, we use rush and rushx commands for running of the scripts:

  • rushx is a command similar to npm run or yarn and is used to run a script from package.json, with a simple caveat: it needs to ba called from within a package, no --prefix or --cwd options

  • rush <command> can be used to run one of the global/bulk commands on the repo:

    • global command - if we, for instance, run rush deploy:eisbuk from enywhere in the repo, the built assets for the project will be deployed to a staging environment
    • bulk command - if we, for instance, run rush lint:staged from anywhere within the repo, the lint:staged script of each package.json will be ran, which provides a convenient way linting all of the packages using single command
    • for all global/bluk commands, see command-line.json

Adding/removing deps from packages

To add a dependency, you can simply run rush add --package <package_name> don't use pnpm add or npm install, etc.. Additionally, you can add a dependency, manually, to package's package.json, after which you need to run rush update.

Make sure to check if it's already used elsewhere in the project. rush check will notify about inconsistent versions. The script in .github/update_package.sh can be used to change a version in all packages. Make sure you run rush update and rush check after using it.

To remove a dependency, simply remove it from package.json and run rush update

Monorepo debugging

The recommended node version for the monorepo is (as stated in .nvmrc) 14.18.1. If you're using nvm (and have the given node version installed), it's sufficient to run nvm use anywhere in the repo to switch to appropriate version.

Some unexpected and cryptic errors might arise from a corrupt shrinkwrap file, at which point running the following might prove useful:

rush update --full

Common local work (dev) flows

Dev server flow - Vite server + Firebase emulators:

In order to work locally, we're leveraging the power of firebase emulator suite in lieu of firebase production backend.

To start the emulators, run:

emulators-start

This will run emulators for firebase services (auth, functions, firestore and hosting) with emulators dashboard at http://localhost:4000 You can add --inspect-functions 9229 to be able to connect with a debugger, for instance chrome from chrome://inspect/#devices or from the VSCode debugger.

With the emulators up and running, you can go ahead and start the dev server for the client app:

cd packages/client
rushx start

In order to start the development and observe in app functionality, you need to create the default admins. Run the initDev function from a browser console:

initDev();

This will create a default admin user and leave you logged in. If you want to sign out and sign back in, the credentials (for a default admin) are:

Storybook dev flow:

Storybook is ran, as per usual, without the need for emulators. Simply run the storybook command from the /client app

cd packages/client
rushx storybook

JEST tests

To run JEST (unit) tests, run

cd packages/client
rushx test

There's no need to spin up the emulators as unit tests use different emulator set up and the emulators are (automatically) spun up when needed.

Running test as in the above example runs all of the unit tests with full emulators support Alternatively, you can run rushx test:quicktest to run the tests without the emulators. This, however will skip all of the tests requiring emulator support.

E2E Tests - Cypress

To run E2E tests, you need to start up the emulators and the dev server (as in the first working flow) and additionally run cypress. First you need to

cd packages/e2e

From there, you can either run all e2e tests at once by running rush cypress:run (more often used in CI), or, the more interactive way (preferred for development, TDD), open the Cypress dashboard by running rush cypress:open

Packages

The repo is structured as a monorepo, so each divisible unit of functionality is separated into its own package.

The packages are named by prepending @eisbuk to each package name (as form of namespacing). The prefix, however, is omitted in the folder structure, i.e. path to @eisbuk/client folder is /packages/client, etc.

All of the packages have the same basic scripts (except for @eisbuk/scaffold which doesn't utilize any functionality). Each package, then, features it's own additional scripts, but these are the standard ones:

  • build - builds the app/lib using Vite/ESBuild (in case of library, builds both esm and cjs versions), after build, runs typecheck to generate the .d.ts files, theres also a bulk rush build command which runs build in each package
  • lint - lints the package (for development)
  • lint:strict - lints the package with failure if more than 0 errors/warnings (for pre-commit, CI), there's also a bulk rush lint:strict command which runs lint:strict in each package (this is mostly used for CI, while the, singular, lint script is used in dev)
  • typecheck - runs typescript check (for CI, with --noEmit flag), there's also a bulk rush typecheck command which runs typecheck in each package

We currently feature the following packages:

@eisbuk/scaffold

The smallest of the packages, doesn't utilize any build process. Contains the "templates", more like base configs for eslint (.eslintrc.js) and typescript (tsconfig.json). We're using those files to provide a single source of truth (and simplify editing of the setup while preserving the consistency across the packages) as tsconfig.json and .eslintrc.js for each package are an extension of the base setup with some, would be, tweaks.

@eisbuk/shared

This folder contains some code: functions, enums, types, etc. shared among the @eisbuk/client and "backend" (@eisbuk/functions) app. The build process is done by Vite by running the build script.

@eisbuk/translations

Similar to @eisbuk/shared package, translations contains code shared among different packages, related to i18n, such as translation enums, translation dictionaries as well as i18next setup. The build process is handled by Vite, in much the same way as @eisbuk/shared package.

@eisbuk/functions

The functions package contains the code for "serverless" cloud functions. The functions are ran as part of the firebase emulators and, as part of CI/CD process, deployed, at production, to firebase "backend". The functions are built/served using ESBuild as a build tool, as Vite was an unnecessary complication at this point. The commands are build and watch.

@eisbuk/client

The client package contains our main (browser) app. Currently all of our unit tests are contained within this package as well. The scripts connected to this package have already been explained above. The build/serve is handled by Vite, while unit tests are implemented using jest + react-testing-library.

Please note: client build process is a bit lengthier than that of the shared, functions, etc.. packages. Therefore, build script in client package does nothing, not to slow down the bulk rush build command. If you wish to build the @eisbuk/client package for production, rushx build:prod should be ran instead (from packages/client)

@eisbuk/svg

The SVG package is used to store all .svg files in one place. This is convenient for multiple reasons, but is not without challenges.

The pros of this approach is that we can use one standard SVG build mechanism and export all svg files as react components (prebuilt with SVGR). Additionally, having a single store of SVG assets allows us to externalize the deps of each package using .svgs and bundle them in, only, with the final app. This prevents potentially bundling the same SVG twice (if the same SVG is used by ui and client packages, for instance). There's no need to mock .svg files for tests as they already in as react components (prebuilt at imported package's build time).

The challenge, however, is that currently we don't have type declaration support for built SVGs: all SVGs are, in fact, react components, but are imported as any, which doesn't cause problems in a way of false positives, but is not completely kosher (we don't like any assertions). However, this is a problem for another day. Another challenge has to to with tree-shaking, where, however externalized, all SVGs from the packages/svg get bundled in the final app.

@eisbuk/backup-restore

Utilities for dumping and restoring organization data from an Eisbuk firestore instance. Documentation

Using SVGs

In order to use SVGs, you can import svg (as named import from @eisbuk/svg). If the SVG doesn't yet exist in the package, please provide a new one and add it as an export in packages/svg/src/index.ts, like so:

import NewSVG from "./new-svg.svg";

export {
  // ...other exports
  NewSVG,
};

Default setup (how SVGs are processed)

We're bundling SVGs using vite and SVGR plugin. By default values for width and height propertes on each SVG are replaced with 100% allowing us to control the size of an SVG using a container element. In order to be able to color and SVG using color: <css-color-property>, all colours hardcoded in an SVG source file need to be replaced with currentColor. This has to be done manually as automating this could produce a myriad of problems.

@eisbuk/e2e

A package containing automated, e2e tests using Cypress. All of the Cypress code, setup + integration are there and e2e tests should be ran from there (with assumption that some form of emulators/dev-server are already running)

Contribution

EisBuk is a booking system developed for an ice skating school. It's open source, so if it fits your needs you can install it and use it.

If you want to contribute make sure you include tests for your change, and that existing tests don't break.

About

Manage bookings for ice skating lessons

Resources

License

Stars

Watchers

Forks

Releases

No releases published

Packages