BOFE stands for Back Office Front-End and is intended for bank employees in various settings:

  • Desktop web-app for bank administrators to investigate, resolve flagged transactions, reconcile transactions and make corrections, if needed.

  • Embedded web-app in Genesys callcenter application, including system integrations to search and retrieve customer information, automatically display customer and log the interaction.

  • Client-facing web-app on iPads in kiosks, where agents can address customers queries and help them to manage products like pockets or cards, directly in BOFE.

General

See repo and Readme https://github.com/SafiBank/SaFiMono/tree/main/app-bo

Technical decisions related to FE

Guides

Expected usage

  • The primary browser where BOFE will be used is Safari

Running the BOFE

Prerequisites

  • VPN set up and running Connecting to Cloudflare VPN

  • Okta account with access to one of the groups allowed to use BOFE

    • Optionally, also a “Checker” configured in Okta profile to give you maker or checker privileges (otherwise you’ll see “No role” under the name of logged user)

  • For overview of available content see Routes

Environment variables

We use several types of environment variables in BOFE.

For the description for the individual variables see app-bo/README.md.

Environment-agnostic (business) variables

Environment-specific variables

For local development you need to fill in these values yourselves, see README

Build-specific variables

For convenience, all mentioned variables are handled by app-bo/src/config/generalConfig.ts and are available in single exported object throughout the app.

Generated content

Types

  • The Typescript types equivalent for schemas used in microservices.

  • The app-bo/scripts/extract-api-types.mjs script file controls the deployment environment and list of supported services.

    • Generated types are placed in app-bo/src/types/apis-autogenerated

    • Do additions/adjustments in app-bo/src/types/{domain}.ts

  • See README for usage and more info.

Note: For historical reasons when manipulating e.g. an “account” we use the types generated from account-manager schemas. However, since all domain endpoints are proxied now by backoffice-mananger we should now really only ever need the types generated from backoficce-manager schemas.

Icons

  • This is used for building React components from SVGs.

  • Some use case-specific icon variants are exported from the index file.

  • See README for usage more info.

When exporting SVG icons from Figma make sure to rename them to match the naming conventions.

For icons that include multiple colors make sure to suffix the SVG file name with _color.

Internationalization (i18n)

https://www.npmjs.com/package/react-i18next is used for i18n.

The translations are in the app-bo/public/locales/{en|fil}/*.json files and are roughly separated by domains and/or routes.

Global i18n set-up is in app-bo/src/config/i18n/i18n.ts.

The extended type definitions are declared in app-bo/src/config/i18n/react-i18next.d.ts for enhancing IDE Experience and preventing errors

Note: The application was built to be multilanguage, but is expected to be only single language for MVP. There is a LanguageSwitch component ready, it’s just hidden.

TODO: The user-facing text in backoffice have not been through the copywriter validation process yet. Though some resources are available for the process, see https://safibank.atlassian.net/wiki/spaces/ITArch/pages/213319774/Updating+of+UX+copy+in+Application+and+Back+office#Back-office-application and Back office

Using translations

// First import
import { useTranslation } from 'react-i18next'

// Hook can be used in any component:
const { t } = useTranslation('loan')
t('interestRate') // gives the translated string

// You can load more domains at once
const { t } = useTranslation(['general', 'change', 'report'])
// prefixes need to be used in that case
t('change:changesTitle.checker')
t('report:manualTransactions')

// Translations can have parameters
t('version', {versionNo: 55}) // in JSON: "version": "Version: {{versionNo}}"

// Also pluralization can be defined (see react-i18next docs)
 t('months', { count: lockedDetails?.tenureMonths })
// in JSON:
//  "months_one": "{{count}} month",       <- used when count is 1
//  "months_other": "{{count}} months",    <- used when count is not 1

Authentication & authorization

We use okta/okta-auth-js and okta/okta-react to integrate with Okta identity provider.

  • The Okta client ID (different for each environment) is provided by the environment variable REACT_APP_OKTA_CLIENT_ID.

  • The whole application is wrapped into Okta’s <Security> wrapper.

  • We then use the useOktaAuth hook to get info from Okta.

  • There are several special routes needed for Okta flow to work like OKTA_AUTH_ROUTE or LOGOUT_ROUTE, see their usage in the code.

The critical part is located in the app-bo/src/components/Layout/Layout.tsx which is the core of all screens - it is supposed to redirect to the Login page if the user is not logged in:

  useEffect(() => {
    if (
      // ID token is available only until logout
      !oktaAuth.getIdToken()
    ) {
      const query = qs.stringify({ redirect: window.location.href })
      navigate(`${LOGIN_ROUTE}?${query}`)
    }
  }, [navigate, authState, oktaAuth])

TODO: It’s possible that the inactivity logout is not working correctly (i.e. redirecting to the Login page). Sometimes all BE requests fails and a simple page refresh helps. These situations should be investigated.

For details about authorization (permissions and roles) see Access-control.

Data management

Dates

We use https://day.js.org/ for date-related operations.

Customizations are done in app-bo/src/config/initDayjsExtensions.ts.

Important: every date shown to the user is expected to be displayed in Asia/Manila timezone!
We have toDayjs to ensure that. But it’s being used consistently.

Monetary amounts

Important: Due to the limitations of OpenAPI schemas [quotation/source needed] the monetary values, represented as BigDecimal in BE are serialized to JSON as strings by the schemas mark them as “number”.
This means you need to be very paranoid when doing any actual arithmetic operations as the values with number TS types may actually be strings.

To convert stringified numbers to floating point numbers we use parseNumber utility.

The application is not designed to work in a multicurrency environment as the backends are not designed like that either.

  • In cases when the currency is available (account totals or transactions) the FE uses it.

  • Otherwise (loans, overdraft), we use the default currency from .env: "PHP"

Request headers

BOFE is sending several important HTTP headers in each request, see app-bo/src/clients/utils.ts for implementation

Header name

Header value

Description

Authorization

"Bearer {okta ID token}"

Used for authentication and authorization. See also https://safibank.atlassian.net/wiki/spaces/ITArch/pages/218235070/Access-control#Checking-for-permission-in-backoffice-manager-BE

X-Request-ID

Random UUID

Used as a unique request identifier, generated for every request.

tracestate

ot=bankUserId:{user ID}

Used for tracing, refer to https://safibank.atlassian.net/wiki/spaces/ITArch/pages/186286119/Distributed+tracing+with+OpenTelemetry#Request-Header-from-front-end-request

traceparent

00-{22 hex digts}-{16 hex digits}-01

Supplementary tracing header, see above

Visual design

UI designs: https://www.figma.com/file/eyJ3AMGVxHZYTxNLXxTH2D/SaFi---Back-Office-UI
Component designs: https://www.figma.com/file/TS7ex0Kzh1XCZSJL28LVsY/SaFi---Library-Back-Office?node-id=2%3A2&t=ZLCHMznppkz7c5rE-0

For more see designer resources see Visual design.

When using custom sizes values for styling, in general

  1. Prefer MUI unit-less values like gap={2} (2 is equal to '16px')

  2. If not available prefer rem like borderTopLeftRadius: '1.25rem'

  3. For borders or sizes of 1-2 px use px (not 8th or 16th of rem)

App version

The BOFE version is technically a short hash of the last commit built and deployed for that environment.

The version is displayed at the bottom of the user menu

The version can also be retrieved from Argo CD

  1. Open the bofe-{env} application in Argo CD https://argocd.safibank.online/applications/bofe-dev

    • (ignore the hashes shown in the “Current/Last sync status” sections)

  2. Click on “App Details” (top left)

  3. Locate the “Images” attribute that will contain the image name like this

    • asia-southeast1-docker.pkg.dev/safi-repos/safi-docker/bofe:2099b904ee9bf77306016105599375b7c884a563

    • The first 7 digits after bofe: are the BOFE version (last built commit short hash), in this example 2099b90

3rd party packages

List of packages used by app-bo/package.json
(Some are also documented in React Tools and Libraries)

"dependencies": {
    "@cyntler/react-doc-viewer": "^1.6.0",      // to view PDF and Word files
    "@emotion/react": "^11.9.0",                // 2x MUI styling dependency
    "@emotion/styled": "^11.8.1",
    "@mui/material": "^5.8.2",                  // MUI core
    "@mui/x-date-pickers": "^5.0.0-beta.0",     // MUI date picker
    "@okta/okta-auth-js": "^6.8.0",             // 2x Okta
    "@okta/okta-react": "^6.6.0",               
    "dayjs": "^1.11.4",                         // Date handling package
    "i18next": "^21.8.4",                       // 2X i18N
    "i18next-http-backend": "^1.4.0",
    "jwt-decode": "^3.1.2",                     // JWT decoding
    "lodash": "^4.17.21",                       // general utilities
    "qs": "^6.10.5",                            // URL query utils
    "react": "^18.1.0",                         // React core
    "react-dom": "^18.1.0",                     // React DOM handling
    "react-hook-form": "^7.32.0",               // form validation
    "react-i18next": "^11.16.9",                // i18N
    "react-query": "^3.39.1",                   // data access hooks
    "react-router-dom": "6",                    // navigation/routing
    "react-scripts": "5.0.1",                   // for building/running the app
    "typescript": "^4.4.2",                     // Typescript support
    "uuid": "^8.3.2",                           // UUID generator
  },
"devDependencies": {
    "@emotion/jest": "^11.9.4",                   // testing
    "@svgr/cli": "6.3.0",                         // for icon generator
    "@testing-library/jest-dom": "^5.14.1",       // 3x testing libraries
    "@testing-library/react": "^13.0.0",
    "@testing-library/user-event": "^13.2.1",
    "@types/jest": "^27.0.1",                     // 6x TS typings
    "@types/lodash": "^4.14.182",
    "@types/node": "^16.7.13",
    "@types/react": "^18.0.0",
    "@types/react-dom": "^18.0.0",
    "@types/uuid": "^8.3.4",
    "@typescript-eslint/eslint-plugin": "^5.25.0",  // 11x linting
    "@typescript-eslint/parser": "^5.25.0",
    "eslint": "^8.15.0",
    "eslint-config-prettier": "^8.5.0",
    "eslint-config-react-app": "^7.0.1",
    "eslint-config-vacuumlabs": "^2.0.14",
    "eslint-plugin-import": "^2.26.0",
    "eslint-plugin-prettier": "^4.0.0",
    "eslint-plugin-react": "^7.29.4",
    "eslint-plugin-react-hooks": "^4.5.0",
    "eslint-plugin-simple-import-sort": "^7.0.0",
    "openapi-typescript": "^5.3.0",                 // for OpenAPI TS generator
    "prettier": "2.6.2"                             // formatter
  },

App.tsx providers

Structure of App.tsx

const App = () => (
  <OuterErrorBoundary>                              // catches unexpected errors
    <QueryClientProvider client={queryClient}>      // provides data access
      <ThemeProvider theme={theme}>                 // provides MUI theme
        <CssBaseline />
        <BrowserRouter>                             // provides routing/navigation
          <SecurityWrapper>                         // provides Okta access
            <CallCenterInteractionProvider>         // provides Call center info
              <AppRoutes />                         // Actual app routes/pages

Testing

To run tests

yarn test

Files related to testing

src/__tests__                 Route tests
src/__tests__/mocks           Mocked data for hooks
src/__tests__/__snapshots__   Generated test snapshots, do not touch this
src/setupTests.tsx            Global test setup, sets up also most hook mocks
src/**/*.test.ts              Unit tests, next to the module they test