- UI Usage
- Data structures
- Client API Guide
- Tech Stack
- Frontend
- Backend
- Configuration
- Integrations and dependencies
- Technical debt
- Performance
- User experience
- Accessibility
The application consist of two main parts:
- View subsets (public).
- Subset draft editor (admin).
This part allows any user to view and search published and drafted subsets. This part does not require authentication.
The editor is a six step interactive form.
The main internal data structure is the Subset.prototype
. It is responsible for holding all data about the subset draft and provide calculations and validation during editing.
In order to persist the user data session storage is used. It updates on each change in the draft.
GET /subsets
GET /subsets/{subsetId}/
GET /subsets/{subsetId}/versions/{versionId}
POST /subsets/{subsetId}/
POST /subsets/{subsetId}/versions/{versionId}
PUT /subsets/{subsetId}/
PUT /subsets/{subsetId}/versions/{versionId}
DELETE /subsets/{subsetId}/
DELETE /subsets/{subsetId}/versions/{versionId}
/classificationFamilies
/ssbsections
/classifications/{classificationId}
/classifications/{classificationId}/versions/{versionId}
/classifications/{classificationId}/codesAt{...}
To get overview over all published and saved subsets in the system. Search by name through the subsets.
Shows a particular subset by setting the subsets ID instead of {subsetsId}
.
A subsets preview page allows to open its versions, as well as open in edition mode.
Shows a particular version of a particular subset. One have to put correct {subsetUd}
and {versionId}
in the URL.
Opens an empty form for creation and saving a new subset with versions.
Only SSB employees are allowed to create and edit subsets. By redirecting to /auth
application will redirect to login page.
Users can specify a particular form step by naming it as a search parameter step
.
If the step name is wrong or outdated, the application will show the first step.
/editor?step={ Metadata | Versions | Codes | Oreder | Review }&subsetsId={subsetId}&versionsId={versionId}
In addition to form step, users can specify {subsetId}
and {versionId}
.
An extra step to save or publish a subset's metadata.
The payload will be generated from the session storage variable draft
.
An extra step to save or publish a subset's version, which is set to be current.
The payload will be generated from the session storage variable draft
.
An extra step to save or publish a subset's metadata.
An extra step to save or publish a subset's version, which is set to be current.
The payload will be generated from the session storage variable draft
.
React 17 with React hooks
Azure DevOps with Azure Pipelines.
Git, GitHub, RESTfull APIs, JSON, Oauth 2, OpenID Connect, Azure, Snyk, Mabl
This project was bootstrapped with Create React App.
The application context is centralized in the AppContext
component and available for all subcomponents to ContextProvider
.
Both components are declared in context.jsx
.
The first version of the application keeps in context only changes to a subset draft. Only one subset can be drafted at a time.
The core data structure and properties of the subset draft model is defined in the Subset.prototype.js
.
Access to the subset draft data in the context has to be done via the useSubsetDraft
React custom hook.
The migration to the library is not finished in the source code. Some calls (POSTs, PUTs, some GETs) are still happens via custom hooks. Recommended to use SWR for all calls.
Issues with drag-and-drop. Additional library has to be installed to convert browser drag-and-drop events into respective mobile events. Mobile users can still reorder codes by using numbers and arrows. Issues with responsive design.
Internationalization is implemented with react-i18next library. The set up is for three languages (nb, nn, en), but oly two in use.
The local session storage is used to keep subset draft in memory throughout editing, site refresh and navigation. The subset draft resets in session storage when user chose to start a new subset draft. All unsaved changes from the previous edition will be discarded without warning. Current subset draft in edition mode will be overwritten by next subset draft opened in the editor. All unsaved changes from the previous edition will be discarded without warning.
Fetch errors displayed locally. Subset draft validation is commented out.
Standard caching is disabled as long as service worker is unregistered. It should be registered before shipping to the production. SWR library has its own way to cache the response and update the content (stale-while-revalidate), this behaviour can be cofigured.
.
├── src
│ ├── components
│ ├── controllers
│ │ ├── context
│ │ ├── klass-api
│ │ └── subsets-api
│ ├── internationalization
│ ├── models
│ │ └── Subset.prototype
│ ├── pages
│ │ ├── SearchSubsets
│ │ ├── editor
│ │ ├── subset
│ │ └── changelog
│ ├── utils
│ └── views
│ │ ├── code
│ │ └── Subset
│ ├── App.js
│ ├── defaults.js
│ └── serviceWorker.js
├── .env
├── azure-pipelines.yml
├── Dockerfile
├── jsconfig.json
├── nginx.conf
├── package.json
└── README.md
Subset client will automatically be deployed in Staging by pushing / merging into master branch.
In order to deploy in Production follow the platform-dev instructions.
Configuration files for production.
Configuration files for staging.
-
Git
Download and install git. -
Node.js (npm)
Download and install node.js -
Browser
Download and install a modern browser of your choice.
Recommend to use Chrome, the most secure browser today.
Install dependencies for the project (production build):
$ cd /klass-subsets-client
$ npm start
In case you need to run a development build do: $ npm install
instead of $ npm run build
In the project directory, you can run:
Runs the app in the development mode.
Open http://localhost:3000 to view it in the browser.
The page will reload if you make edits.
You will also see any lint errors in the console.
Launches the test runner in the interactive watch mode.
See the section about running tests for more information.
Builds the app for production to the build
folder.
It correctly bundles React in production mode and optimizes the build for the best performance.
The build is minified, and the filenames include the hashes.
Your app is ready to be deployed!
See the section about deployment for more information.
Easy to maintain.
Updating your build tooling is typically a daunting and time-consuming task. When new versions of Create React App are released, you can upgrade using this single command.
https://facebook.github.io/create-react-app/
Note: this is a one-way operation. Once you eject
, you can’t go back!
If you aren't satisfied with the build tool and configuration choices, you can eject
at any time. This command will remove the single build dependency from your project.
Instead, it will copy all the configuration files and the transitive dependencies (Webpack, Babel, ESLint, etc) right into your project so you have full control over them. All of the commands except eject
will still work, but they will point to the copied scripts so you can tweak them. At this point you’re on your own.
You don’t have to ever use eject
. The curated feature set is suitable for small and middle deployments, and you shouldn’t feel obligated to use this feature. However we understand that this tool wouldn’t be useful if you couldn’t customize it when you are ready for it.
You can learn more in the Create React App documentation.
To learn React, check out the React documentation.
This section has moved here:
This section has moved here:
This section has moved here:
This section has moved here:
This section has moved here:
This section has moved here:
Data fetched from Klass API is composed into a subset and sent to the Subsets API.
Data fetched from Subsets API is displayed and modified.
Implemented and controlled by BIP.
Subset client server redirects /auth/*
requests to a login process.
The rest of the client API is publicly available.
In order to store and update data through Subsets API, the client has to present a JWS token. The token is picked up from BIP's oath proxy server.
GET requests to the Subsets API does not require authorization.
The application has gone through a series of major changes that affected the code structure as well as the core data structure.
The components and the flow have been in a POC mode and require proper testing.
Navigation in the application and especially in the form dependent on the URL search parameters, not tested yet.
Low test coverage.
UI and UX could be improved.
Code requires refactoring (smaller components, shorter functions, encapsulating features).
Check the TODO
and FIXME
comments in the source code.
The "Reorder codes" page crashes when the code objects (name in particular) is not as expected.
Lighthouse report: 85-95% Slow when a subset version operates too many codes (hundreds). A click on a checkbox can too slow when a code list contains too many codes (hundreds).
To be analysed.
Lighthouse report: 95-100%
#Best practices Lighthouse report: 95-100%
The main idea of the draft concept is to keep the internal version of the draft updated at each moment. The updates could happen on the steps "Choose codes" and "Reordering".
On the "Choose codes" form step codes could be added and removed by clicking on the checkbox (one at a time) or clicking on the "All"/"None" buttons.
When codes are added to the draft, each code gets the timestamp of addition as a property.
This property will be removed on the "Reorder" form step and "Review" form step.
The timestamp is used to keep codes chosen on the "Choose codes" on the top of the list and ordered according to its clicking order, while the rest of the codes (without a timestamp) are ordered by rank.
On the "Reorder" form step codes can change their rank and/or can be deleted by clicking on the "Trash" button.
The process of saving a subset is complex because initially a subset was meant to be a single object (Subsets API v1). In Subsets API v2 a subset is a series of versions, and a version is a set of codes.
A series has metadata, it has no difference between saving and publishing.
A version can have different states (administrative status):
- published (OPEN);
- saved (DRAFT);
- not saved (INITIAL), stored locally in Session storage.
The saving and publishing process implemented on a single page (hidden 6th step) Step_6_Publish.jsx.
This page is protected by authorization (/auth/save
). I can be navigated through the URL only.
It should be specified which part of the subset (draft) to save: metadata=true
, version=true
or both ?metadata=true&version=true
.
The payload will provide the desired administrative status for the version.
The payload will be generated at the sending point on the same page (hidden 6th step) Step_6_Publish.jsx.
In order to specify which payload to be sent provide search parameter publish=true
.
If the type of payload is not specified a save payload (with administrativeStatus=DRAFT) will be generated and sent.
The communication with Subsets API is implemented in Step_6_Publish.jsx. All the cases are gathered in a single component. It should be reviewed and refactored to single flows.
The flow initiated by clicking the "Save" ("floppy disk") button on metadata and /auth/save?metadata=true
is pushed to the browser's history.
If the user is logged in the page will be displayed, and the effects on the component will be fired. Otherwise, the user will be redirected to login.
If the metadata is never been saved before (no createdDate
registered), a metadata payload will be generated and passed to the usePOST
React custom hook.
If metadata was saved before, a metadata payload will be generated and passed to the usePUT
React custom hook.
While the application is waiting for the server response, the message "Sending metadata to the server" is displayed for users.
When the server response comes, another effect is fired. If the response is successful then the "metadata_sync" action is applied to the internal draft context, and the "Metadata is sent" is displayed to the user. If the response contains an error, the error will be displayed, no synchronization applied. The application will then wait for the user where to go further, the options are displayed.
The flow initiated by clicking the "Save" ("floppy disk") button on a chosen version and /auth/save?version=true
is pushed to the browser's history.
If the user is logged in the page will be displayed, and the effects on the component will be fired.
Otherwise, the user will be redirected to the login page.
If the version is new (no random versionID
assigned), a version payload will be generated and passed to the usePOST
React custom hook.
If the version has a random ID, a version payload will be generated and passed to the usePUT
React custom hook.
While the application is waiting for the server response, the message "Sending version to the server" is displayed for users.
When the server response comes, another effect is fired.
If the response is successful then the "version_sync" action is applied to the internal draft context, and the "Metadata is sent" is displayed to the user. Only the current version will be updated. At the moment the application receives a positive response the version ID becomes known. The application still uses tempID to double-check that the correct version is getting the updates.
If the response contains an error, the error will be displayed, no synchronization applied. The application will then wait for the user where to go further, the options are displayed.
If the user chooses to go back to the form, the original (temporary ID) will be used to make a version to be the current version in the editor. The sources for the version ID are:
- from the search parameters of the URL;
- from the context draft's _currentVersion;
- from the successful response.
The flow initiated by clicking the "Publish" ("arrow to cloud") button on a chosen version and /auth/save?version=true&publish=true
is pushed to the browser's history.
The flow is similar to Save a version. The only difference is the administrtiveStatus: OPEN
in the payload.
In order to make this difference application adds publish=true
to the URL.
A subset in the editor consists of metadata and a current version. A user can click the "Save" button, or the "Publish" button (buttons with text on the bottom of the page).
If the subset is new (no createdDate
registered), then metadata will be sent to the server first.
The process is described in Save a metadata.
When the metadata is successfully saved, the version will be sent to the server.
The process is described in Save a version and Publish a version.
If the subset is not new, the version is sent in parallel with the metadata.
Normally a code in a classification version or in a code list version has a unique code and/or a unique name.
However, it is not true across multiple versions of the classification, or the code list.
In order to deal with such codes validFromInRequestedRange
is used to provide uniqueness by combining it with the name and the code in addition to the classification ID.
A code has its own validity period across multiple versions.
This validity period is not assigned to a code, but calculated by Klass.
Calculation is available in response to /codesAt?from={fromDate}&to={toDate}
as validFromInRequestedRange
and validToInRequestedRange
properties to a code.
This dates can vary and dependent om fromDate
and toDate
in the request.
The dates are used to determine uniqueness for Duplicate codes.