Skip to main content

Frontend

info

This document is still under development.

This documentation is primarily intended for software developers.

First things first, the GitHub repository can be found here, the Gropius component for issue tracking can be found here.

Technology Stack

  • JavaScript Framework: Vue
  • Programming Languages: mainly TypeScript and standard HTML within Vue components
  • UI Library: Vuetify
  • Package Manager: Yarn -- Instructions for the most important yarn commands can be found in the README.md in the repository.
  • Tooling: ESLint, Prettier, GraphQL Codegen

Repository Structure

  • /src/: The source code
    • assets/: assets to include in the website. Currently unused.
    • components/: Single-File Components that can be reused -- this is basically an internal UI components library.
    • graphql/: Source code files that form the GraphQL client for the calls to the gateway.
    • layouts/: SFCs responsible for the general layout of a page of the web application.
    • plugins/: Source code related to the plugins of the web application: Vuetify, Vue Router, and Pinia.
    • router/: The app-specific logic of the router is implemented here, enabling navigation between the many views.
    • store/: The Pinia store manages the state of the application, including data and UI state, in a centralized and reactive manner, facilitating state manipulation and sharing across components.
    • strings/: The string resources of the application, such as error messages.
    • styles/: Files dedicated to styling the web application -- this has been left untouched since the initialization of the repository.
    • util/: Utility source code.
    • views/: SFCs, each of which represents a single web page and therefore a route of the Vue Router.

Files That Play An Important Role

  • codegen.yaml
  • src/graphql/client.ts
  • src/router/index.ts
  • src/store/app.ts
  • src/util/errorHandler.ts
  • src/layouts/default/TheDefaultLayout.vue

Hierarchy Of The Layout Components

  • src/layouts/default/TheDefaultLayout.vue
    • src/layouts/default/TheAppBar.vue
    • ...
    • src/layouts/default/TheViewPlaceHolder.vue
      • v-main
        • router-view
        • src/components/Notifications.vue

The Vue Router places the current route's view (one of the views in src/views) into router-view.

Some remarks regarding the aforementioned components:

  • Each page is an instance of src/layouts/default/TheDefaultLayout.vue.
  • The default layout comprises the app bar, two navigation drawers (one left, one right), and a placeholder for the actual view.
  • src/components/Notifications.vue serves as the notification center of the application and only becomes visible when a notification is pushed to the user.
  • The Vue Router maps routes to src/views and places the current route's view into the router-view component.

Important Components

The most important components are:

  • The Pinia store
  • The GraphQL client
  • The error handler

The Pinia store

The role of the Pinia Store in Vue is to manage the state of the application in a centralized and reactive manner, facilitating state manipulation and sharing across components.

The store manages:

  • login state
  • the active user role
  • the user's shopping cart
  • the notification queue

The GraphQL client

  • GraphQL queries and mutations can be found in the .graphql files where the name of the file is one of the bounded contexts of the microservice landscape. For example, ´catalog.graphql` contains the queries and mutations for calling the catalog service.
  • src/graphql/generated.ts has to be generated by running yarn codegen; based on codegen.yaml, the file then gets generated.
  • src/graphql/client.ts resembles the interface to make calls to the Gateway via its function useClient(). The following code snippet taken from src/views/ProductView.vue shows how to use useClient():
/**
* The GraphQL client to use for all GraphQL requests.
*/
const client = useClient();

/**
* Gets the "entire" product from the catalog service.
*/
const product = asyncComputed(
async () => {
return client.getProduct({
id: id.value
});
},
null,
{
onError: (e) => pushErrorNotification(errorMessages.getProduct, e),
shallow: false
}
);

Note that the GraphQL query "behind" getProduct() has been defined in src/graphql/catalog.graphql.

The error handler

src/util/errorHandler.ts provides intuitive helper functions for wrapping synchronous as well as asynchronous actions into try-catch blocks in order to push an error notification if the actions fails.

/**
* Requests the restocking of the selected product variant.
*/
async function restock() {
if (activeUserRoleIsEitherAdminOrEmployee.value && canRestock) {
await awaitActionAndPushErrorIfNecessary(() => {
return client.createProductItemBatch({
input: {
productVariantId: selectedProductVariantId.value,
number: parseInt(amount.value as string),
},
})
}, errorMessages.createProductItemBatch)

emit('restocked')
}
}

In this code example awaitActionAndPushErrorIfNecessary() (one of the error handler's functions) is called with two arguments:

  • the asynchronous action
  • the error message if the action fails: errorMessages.createProductItemBatch

Whenever the failure of a function call should be communicated to the user, use one of the error handler's functions.

Technical Debt

info

This section is written from the personal perspective of the main frontend developer during the time of the EnPro.

Sadly, there is already some technical debt. Why? Because at the beginning of the project I was still a complete beginner when it came to frontend development with Vue, TypeScript, and JavaScript in general. Some things I only understood and learned to use properly over time meaning issue by issue. As we did not have the time to refactor everything as soon as possible, we had to prioritize the implementation of additional features to ensure that the frontend would be at least somehow feature-complete at the end of the project.

Having said this, what kind of technical debt are we talking about?

  • Clones regarding the composition of smaller UI components. That means more custom SFCs could be extracted from the given DOM landscape.
  • A slight overuse of v-if within SFCs instead of making good use of Vue's slot mechanism. See this Gropius issue.
  • Using events instead of custom models. See this Gropius issue.
  • "Many" dialogs have been implemented by now and the coding style regarding source code file structure, member naming, and event handling has deviated over time.

Coding Guidelines

  • Every const variable or ref, interface (or similar construct), as well as every function, must be accompanied by a JSDoc comment.