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
See React Tools and Libraries ADR
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
Values are defined in
.env
https://github.com/SafiBank/SaFiMono/blob/main/app-bo/.envThese are e.g. the default currency or default timezone used in the app
Values are the same for local development, tests, dev/stage/prod environments
Environment-specific variables
Values are defined in deployment scripts for each environment
FE needs to declare them in app-bo/public/envConfig.js
During the deployment (not during the build) the app-bo/scripts/entrypoint.sh script is run by Docker and the values are substituted into the JS file
For local development you need to fill in these values yourselves, see README
Build-specific variables
Values dependent on the build, currently there is just one, used to display https://safibank.atlassian.net/wiki/spaces/ITArch/pages/192807420#App-version
FE needs to declare them in app-bo/public/buildConfig.js
During the build the Docker substitutes the values in this JS file
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
orLOGOUT_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 |
| 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 |
| 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 |
| 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.
For visual components https://mui.com/material-ui/getting-started/overview/ is used.
When we customize some MUI component we prefix the new one with
Custom
e.g.CustomButton
When using custom sizes values for styling, in general
Prefer MUI unit-less values like
gap={2}
(2
is equal to'16px'
)If not available prefer
rem
likeborderTopLeftRadius: '1.25rem'
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
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)
Click on “App Details” (top left)
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 example2099b90
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
Attachments:
Screenshot from 2022-10-21 11-32-00.png (image/png)