diff --git a/docs/tre-developers/ui.md b/docs/tre-developers/ui.md new file mode 100644 index 0000000000..bb2b5a93a9 --- /dev/null +++ b/docs/tre-developers/ui.md @@ -0,0 +1,57 @@ +# TRE Web User Interface + +This project contains a React-based web UI which covers the core aspects of a TRE, for researchers and workspace owners. + +## Chosen UI Stack + Components +The UI is built upon several popular web frameworks: +- React v18 (created via create-react-app, with all build configurations left as defaults) + - Typescript + - React Router v6 for client side routing +- Fluent UI [Fluent UI Docs](https://developer.microsoft.com/en-us/fluentui#/controls/web) +- MSAL v2: AAD authentication [msal-react docs](https://github.com/AzureAD/microsoft-authentication-library-for-js/tree/dev/lib/msal-react) + + +### Folder structure + +```text +ui +├── app - Root of the React application +│ ├── build - Location of compiled files after build process +│ ├── public - Location for static HTML to bootstrap the app +│ ├── src - All .tsx components +│ ├── index.tsx - Entry point for the app +│ ├── App.tsx - Wrapper and routing for the app +│ └── config.source.json - JSON file to be used as source file for autogenerated config +``` + +### AuthN + AuthZ +For further details on the auth setup, see [Auth](../tre-admins/auth.md). + +As stated above, AAD is used for Authentication and Authorization. There are 3 AAD apps involved here: +- **TRE Client Apps** (formerly Swagger App). This is the app that the user authenticates against. Once authenticated, the client will request an access token for the `TRE Api App`. +- **TRE Api**. In the access token response from this app we get the user's role membership for TRE-level roles (`TREAdmin` / `TREUser`). Based on these role memberships, aspects of the UI will be made available. If the user is in a `TREAdmin` role, they will see buttons to create workspaces for instance. +When the user navigates into a Workspace, the client will request an access token for that `Workspace App`. +- **Workspace App(s)**. Each TRE workspace will have a workspace app registration. The ClientId for each workspace app is stored in the Workspace resource object in Cosmos, and the client uses this ID to gain an access token for that particular workspace. + +Workspace app registrations may be reused across multiple workspaces in development scenarios. +From this access token we can find the Workspace-level roles the user is in (`WorkspaceOwner` / `WorkspaceResearcher`). These are in turn used to show/hide features of the UI. + +### React Contexts +The React Context API is a clean way to handle a limited amount of global state, and is used for a few scenarios in this project: +- TRE Roles Context: A context provides details of the base TRE roles a user is in, which can be consumed anywhere throughout the app +- Workspace Context: Tracks the currently selected Workspace, and the roles the user is in for that Workspace. This context is used for nested components to be able to authenicate against the correct AAD App via `workspaceCtx.workspaceClientId`. +- Create Form Context: A context to control the Create / Update form behaviour. +- Notifications Context: Tracks all the in-progress operations currently running. For each operation, the Notifications panel also uses this context to broadcast Component 'actions' which are subscribed to by downstream components. This way, a resource component does not have to track it's own changes, and can be 'told' by the Notifications Context whether it should refresh / lock etc. + +### Custom Hooks +Hooks are used throughout the project, and a couple of custom hooks were written to abstract common logic: +- `useAuthApiCall`: A way to encapsulate an authenticated `fetch` request and provide a simple interface for downstream components to use. +- `useComponentManager`: This hook subscribes to changes broadcast from the Notifications panel, via the context. A component can simply add this hook to start subscribing to changes and react accordingly. + +## Deployment +The UI is deployed as part of the `tre-deploy` make target, as long as you have set `DEPLOY_UI=true` in your `./templates/core/.env` file. + +To re-deploy _just_ the UI (after an initial deploy), run `make build-and-deploy-ui` from the root of the dev container. This will: +- Use the environment variables from your deployment to create a `config.json` file for the UI +- Build the source code, via `yarn build` +- Deploy the code to Azure blob storage, where it will be statically served behind the App Gateway that also fronts the APi. diff --git a/mkdocs.yml b/mkdocs.yml index db05f41501..9f7b659c4b 100644 --- a/mkdocs.yml +++ b/mkdocs.yml @@ -64,6 +64,7 @@ nav: - TRE Developers: - 'tre-developers/index.md' - API: 'tre-developers/api.md' + - UI: 'tre-developers/ui.md' - Resource Processor: 'tre-developers/resource-processor.md' - End to End Tests: 'tre-developers/end-to-end-tests.md' - Letsencrypt: 'tre-developers/letsencrypt.md' @@ -86,4 +87,4 @@ nav: - Guacamole Linux VM: 'tre-templates/user-resources/guacamole-linux-vm.md' - Pipeline Templates: - Overview: 'tre-templates/pipeline-templates/overview.md' - - Pipeline Schema: 'tre-templates/pipeline-templates/template-schema.md' + - Pipeline Schema: 'tre-templates/pipeline-templates/pipeline-schema.md' diff --git a/templates/core/terraform/main.tf b/templates/core/terraform/main.tf index 634058b4d5..3e3d0669f1 100644 --- a/templates/core/terraform/main.tf +++ b/templates/core/terraform/main.tf @@ -116,9 +116,3 @@ module "resource_processor_vmss_porter" { azurerm_key_vault_access_policy.deployer ] } - -resource "azurerm_static_site" "tre-ui" { - name = "${var.tre_id}-ui" - resource_group_name = azurerm_resource_group.core.name - location = var.location -} diff --git a/templates/core/terraform/outputs.tf b/templates/core/terraform/outputs.tf index e6d6622909..00ff260525 100644 --- a/templates/core/terraform/outputs.tf +++ b/templates/core/terraform/outputs.tf @@ -76,7 +76,3 @@ output "terraform_state_container_name" { output "registry_server" { value = var.docker_registry_server } - -output "ui_api_key" { - value = azurerm_static_site.tre-ui.api_key -} diff --git a/ui/app/staticwebapp.config.json b/ui/app/staticwebapp.config.json deleted file mode 100644 index 5e8866eeb2..0000000000 --- a/ui/app/staticwebapp.config.json +++ /dev/null @@ -1,10 +0,0 @@ -{ - "routes": [ - { - "route": "/" - } - ], - "navigationFallback": { - "rewrite": "/" - } - }