Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Airlock UI: add title to airlock request #2731

Merged
merged 14 commits into from
Oct 13, 2022
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@

FEATURES:
* Added filtering and sorting to Airlock UI ([#2511](https://github.com/microsoft/AzureTRE/issues/2511))
* Added title field to Airlock requests ([#2731](https://github.com/microsoft/AzureTRE/pull/2731))

ENHANCEMENTS:

Expand Down
2 changes: 1 addition & 1 deletion api_app/_version.py
Original file line number Diff line number Diff line change
@@ -1 +1 @@
__version__ = "0.4.52"
__version__ = "0.4.53"
1 change: 1 addition & 0 deletions api_app/db/repositories/airlock_requests.py
Original file line number Diff line number Diff line change
Expand Up @@ -92,6 +92,7 @@ def create_airlock_request_item(self, airlock_request_input: AirlockRequestInCre
airlock_request = AirlockRequest(
id=full_airlock_request_id,
workspaceId=workspace_id,
requestTitle=airlock_request_input.requestTitle,
businessJustification=airlock_request_input.businessJustification,
requestType=airlock_request_input.requestType,
creationTime=datetime.utcnow().timestamp(),
Expand Down
1 change: 1 addition & 0 deletions api_app/models/domain/airlock_request.py
Original file line number Diff line number Diff line change
Expand Up @@ -78,6 +78,7 @@ class AirlockRequest(AzureTREModel):
workspaceId: str = Field("", title="Workspace ID", description="Service target Workspace id")
requestType: AirlockRequestType = Field("", title="Airlock request type")
files: List[AirlockFile] = Field([], title="Files of the request")
requestTitle: str = Field("Airlock Request", title="Brief title for the request")
businessJustification: str = Field("Business Justifications", title="Explanation that will be provided to the request reviewer")
status = AirlockRequestStatus.Draft
creationTime: float = Field(None, title="Creation time of the request")
Expand Down
3 changes: 3 additions & 0 deletions api_app/models/schemas/airlock_request.py
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ def get_sample_airlock_request(workspace_id: str, airlock_request_id: str) -> di
"status": "draft",
"requestType": "import",
"files": [],
"requestTitle": "a request title",
"businessJustification": "some business justification",
"creationTime": datetime.utcnow().timestamp(),
"reviews": [
Expand Down Expand Up @@ -72,13 +73,15 @@ class Config:

class AirlockRequestInCreate(BaseModel):
requestType: AirlockRequestType = Field("", title="Airlock request type", description="Specifies if this is an import or an export request")
requestTitle: str = Field("Airlock Request", title="Brief title for the request")
businessJustification: str = Field("Business Justifications", title="Explanation that will be provided to the request reviewer")
properties: dict = Field({}, title="Airlock request parameters", description="Values for the parameters required by the Airlock request specification")

class Config:
schema_extra = {
"example": {
"requestType": "import",
"requestTitle": "a request title",
"businessJustification": "some business justification"
}
}
Expand Down
2 changes: 2 additions & 0 deletions api_app/tests_ma/test_api/test_routes/test_airlock.py
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,7 @@ def sample_airlock_request_object(status=AirlockRequestStatus.Draft, airlock_req
airlock_request = AirlockRequest(
id=airlock_request_id,
workspaceId=workspace_id,
requestTitle="test title",
businessJustification="test business justification",
requestType="import",
status=status
Expand All @@ -45,6 +46,7 @@ def sample_airlock_request_object_with_review(status=AirlockRequestStatus.Draft,
airlock_request = AirlockRequest(
id=airlock_request_id,
workspaceId=workspace_id,
requestTitle="test title",
businessJustification="test business justification",
requestType="import",
status=status,
Expand Down
1 change: 1 addition & 0 deletions ui/app/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
"@azure/msal-browser": "^2.24.0",
"@azure/msal-react": "^1.4.0",
"@fluentui/react": "^8.68.2",
"@fluentui/react-file-type-icons": "^8.7.9",
"@reduxjs/toolkit": "^1.8.6",
"@rjsf/core": "^4.2.0",
"@rjsf/fluent-ui": "^4.2.0",
Expand Down
17 changes: 11 additions & 6 deletions ui/app/src/App.scss
Original file line number Diff line number Diff line change
Expand Up @@ -106,6 +106,17 @@ ul.tre-notifications-steps-list li {
}
}

.tre-table {
.ms-Persona-primaryText {
font-size: 12px;
color: rgb(108, 108, 108);
}

.ms-DetailsRow-cell {
align-self: baseline;
}
}

.tre-hide-chevron i[data-icon-name=ChevronDown] {
display: none;
}
Expand Down Expand Up @@ -158,12 +169,6 @@ ul.tre-notifications-steps-list li {
background-color: #fff;
}

.tre-table-rows-align-centre {
.ms-DetailsRow-cell {
align-self: baseline;
}
}

.ms-Pivot {
margin-bottom: 10px;
}
Expand Down
4 changes: 4 additions & 0 deletions ui/app/src/App.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ import { CreateUpdateResource } from './components/shared/create-update-resource
import { CreateUpdateResourceContext } from './contexts/CreateUpdateResourceContext';
import { CreateFormResource, ResourceType } from './models/resourceType';
import { Footer } from './components/shared/Footer';
import { initializeFileTypeIcons } from '@fluentui/react-file-type-icons';

export const App: React.FunctionComponent = () => {
const [appRoles, setAppRoles] = useState([] as Array<string>);
Expand All @@ -38,6 +39,9 @@ export const App: React.FunctionComponent = () => {
setAppRolesOnLoad();
}, [apiCall]);

// initiliase filetype icons
useEffect(() => initializeFileTypeIcons(), []);

return (
<>
<Routes>
Expand Down
31 changes: 24 additions & 7 deletions ui/app/src/components/shared/airlock/Airlock.tsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import React, { useCallback, useContext, useEffect, useState } from 'react';
import { ColumnActionsMode, CommandBar, CommandBarButton, ContextualMenu, DirectionalHint, getTheme, IColumn, ICommandBarItemProps, IContextualMenuItem, IContextualMenuProps, Label, Persona, PersonaSize, SelectionMode, ShimmeredDetailsList, Stack } from '@fluentui/react';
import { ColumnActionsMode, CommandBar, CommandBarButton, ContextualMenu, DirectionalHint, getTheme, IColumn, ICommandBarItemProps, Icon, IContextualMenuItem, IContextualMenuProps, Persona, PersonaSize, SelectionMode, ShimmeredDetailsList, Stack } from '@fluentui/react';
import { HttpMethod, useAuthApiCall } from '../../../hooks/useAuthApiCall';
import { ApiEndpoint } from '../../../models/apiEndpoints';
import { WorkspaceContext } from '../../../contexts/WorkspaceContext';
Expand All @@ -13,6 +13,7 @@ import { ExceptionLayout } from '../ExceptionLayout';
import { AirlockNewRequest } from './AirlockNewRequest';
import { WorkspaceRoleName } from '../../../models/roleNames';
import { useAccount, useMsal } from '@azure/msal-react';
import { getFileTypeIconProps } from '@fluentui/react-file-type-icons';

export const Airlock: React.FunctionComponent = () => {
const [airlockRequests, setAirlockRequests] = useState([] as AirlockRequest[]);
Expand All @@ -27,7 +28,7 @@ export const Airlock: React.FunctionComponent = () => {
const apiCall = useAuthApiCall();
const theme = getTheme();
const navigate = useNavigate();
const { instance, accounts } = useMsal();
const { accounts } = useMsal();
const account = useAccount(accounts[0] || {});

// Get the airlock request data from API
Expand Down Expand Up @@ -151,15 +152,31 @@ export const Airlock: React.FunctionComponent = () => {

const columns: IColumn[] = [
{
key: 'avatar',
name: '',
key: 'fileIcon',
name: 'fileIcon',
minWidth: 16,
maxWidth: 16,
isIconOnly: true,
onRender: (request: AirlockRequest) => {
return <Persona size={ PersonaSize.size24 } text={ request.user?.name } />
if (request.status === AirlockRequestStatus.Draft) {
return <Icon iconName="FolderOpen" style={{verticalAlign:'bottom', fontSize: 14}} />
} else if (request.files?.length > 0 && request.files[0].name) {
const fileType = request.files[0].name.split('.').pop();
return <Icon {...getFileTypeIconProps({ extension: fileType })} style={{verticalAlign:'bottom'}} />
} else {
return <Icon iconName="Page" style={{verticalAlign:'bottom', fontSize: 14}} />
}
}
},
{
key: 'title',
name: 'Title',
ariaLabel: 'Title of the airlock request',
minWidth: 150,
maxWidth: 300,
isResizable: true,
fieldName: 'requestTitle'
},
{
key: 'creator_user_id',
name: 'Initiator',
Expand All @@ -168,7 +185,7 @@ export const Airlock: React.FunctionComponent = () => {
maxWidth: 200,
isResizable: true,
fieldName: 'initiator',
onRender: (request: AirlockRequest) => request.user?.name,
onRender: (request: AirlockRequest) => <Persona size={ PersonaSize.size24 } text={ request.user?.name } />,
isFiltered: filters.has('creator_user_id')
},
{
Expand Down Expand Up @@ -310,7 +327,7 @@ export const Airlock: React.FunctionComponent = () => {
selectionMode={SelectionMode.none}
getKey={(item) => item?.id}
onItemInvoked={(item) => navigate(item.id)}
className="tre-table-rows-align-centre"
className="tre-table"
enableShimmer={loadingState === LoadingState.Loading}
/>
{
Expand Down
25 changes: 24 additions & 1 deletion ui/app/src/components/shared/airlock/AirlockNewRequest.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,18 @@ export const AirlockNewRequest: React.FunctionComponent<AirlockNewRequestProps>
const workspaceCtx = useContext(WorkspaceContext);
const apiCall = useAuthApiCall();

const onChangeRequestTitle = useCallback(
(event: React.FormEvent<HTMLInputElement | HTMLTextAreaElement>, newValue?: string) => {
setNewRequest(request => {
return {
...request,
requestTitle: newValue || ''
}
});
},
[setNewRequest]
);

const onChangeBusinessJustification = useCallback(
(event: React.FormEvent<HTMLInputElement | HTMLTextAreaElement>, newValue?: string) => {
setNewRequest(request => {
Expand All @@ -36,7 +48,10 @@ export const AirlockNewRequest: React.FunctionComponent<AirlockNewRequestProps>
);

useEffect(
() => setRequestValid(newRequest.businessJustification?.length > 0),
() => setRequestValid(
newRequest.requestTitle?.length > 0 &&
newRequest.businessJustification?.length > 0
),
[newRequest, setRequestValid]
);

Expand Down Expand Up @@ -118,6 +133,14 @@ export const AirlockNewRequest: React.FunctionComponent<AirlockNewRequestProps>
} else {
title = `New airlock ${newRequest.requestType} request`;
currentStep = <Stack style={{marginTop: '40px'}} tokens={stackTokens}>
<TextField
label="Title"
placeholder="Enter a request title."
value={newRequest.requestTitle}
onChange={onChangeRequestTitle}
rows={1}
required
/>
<TextField
label="Business Justification"
placeholder="Enter a justification for your request."
Expand Down
4 changes: 2 additions & 2 deletions ui/app/src/components/shared/airlock/AirlockViewRequest.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -45,7 +45,7 @@ export const AirlockViewRequest: React.FunctionComponent<AirlockViewRequestProps
} else {
setRequest(req);
}
}, [apiCall, requestId, props.requests]);
}, [apiCall, requestId, props.requests, workspaceCtx.workspace.id, workspaceCtx.workspaceApplicationIdURI]);

const generateFilesLink = useCallback(async () => {
// Retrieve a link to view/edit the airlock files
Expand Down Expand Up @@ -177,7 +177,7 @@ export const AirlockViewRequest: React.FunctionComponent<AirlockViewRequestProps
return (
<>
<Panel
headerText="View Airlock Request"
headerText={request && request.requestTitle ? request.requestTitle : "View airlock request"}
isOpen={true}
isLightDismiss={true}
onDismiss={dismissPanel}
Expand Down
4 changes: 3 additions & 1 deletion ui/app/src/models/airlock.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,8 @@ import { User } from "./user";
export interface AirlockRequest extends Resource {
workspaceId: string;
requestType: AirlockRequestType;
files: Array<string>;
files: Array<{name: string, size: number}>;
requestTitle: string;
businessJustification: string;
statusMessage: null | string;
status: AirlockRequestStatus;
Expand Down Expand Up @@ -34,6 +35,7 @@ export enum AirlockRequestStatus {

export interface NewAirlockRequest {
requestType: AirlockRequestType;
requestTitle: string;
businessJustification: string;
}

Expand Down
64 changes: 64 additions & 0 deletions ui/app/yarn.lock
Original file line number Diff line number Diff line change
Expand Up @@ -1223,6 +1223,14 @@
"@fluentui/set-version" "^8.2.1"
tslib "^2.1.0"

"@fluentui/dom-utilities@^2.2.2":
version "2.2.2"
resolved "https://registry.yarnpkg.com/@fluentui/dom-utilities/-/dom-utilities-2.2.2.tgz#9fffd59a1ceab7121bbb4355744d94cae5452780"
integrity sha512-puklLc6Jvg279OGagqkSfuHML6ckBhw3gJakdvIZHKeJiduh+34U4Finl3K24yBSXzG2WsN+LwLTd1Vcociy+g==
dependencies:
"@fluentui/set-version" "^8.2.2"
tslib "^2.1.0"

"@fluentui/font-icons-mdl2@^8.4.1":
version "8.4.1"
resolved "https://registry.yarnpkg.com/@fluentui/font-icons-mdl2/-/font-icons-mdl2-8.4.1.tgz#d40bf884752979643d4936c0bb55a90a243c37e5"
Expand Down Expand Up @@ -1259,6 +1267,23 @@
"@fluentui/set-version" "^8.2.1"
tslib "^2.1.0"

"@fluentui/merge-styles@^8.5.3":
version "8.5.3"
resolved "https://registry.yarnpkg.com/@fluentui/merge-styles/-/merge-styles-8.5.3.tgz#e9d9d08a4825e117913c417d99233736f570736a"
integrity sha512-bHWftN3zTp1bbBfmAEH8YK9UURWj2mffw7b7VaW2Og1qxwv3GMSza1cyv/d3EVqpMJ8AVwFv3mbi9p1ieMN9mw==
dependencies:
"@fluentui/set-version" "^8.2.2"
tslib "^2.1.0"

"@fluentui/react-file-type-icons@^8.7.9":
version "8.7.9"
resolved "https://registry.yarnpkg.com/@fluentui/react-file-type-icons/-/react-file-type-icons-8.7.9.tgz#9992af91395474d25804b86cb6e44e6b1f3faf41"
integrity sha512-EOZnXBkkS6BJmrChCy1cVT1hkvi7JuesxbqLCrEsyxEk4jn9aha9XkAR2JV44h+bB7p2iFJUBzfZaWK95d0xJA==
dependencies:
"@fluentui/set-version" "^8.2.2"
"@fluentui/style-utilities" "^8.8.0"
tslib "^2.1.0"

"@fluentui/react-focus@^8.7.0":
version "8.7.0"
resolved "https://registry.yarnpkg.com/@fluentui/react-focus/-/react-focus-8.7.0.tgz#5319aff831fa40149e674d7e7a434b3d5d26f434"
Expand Down Expand Up @@ -1323,6 +1348,13 @@
dependencies:
tslib "^2.1.0"

"@fluentui/set-version@^8.2.2":
version "8.2.2"
resolved "https://registry.yarnpkg.com/@fluentui/set-version/-/set-version-8.2.2.tgz#37cffcda607cb2604a86b72e98aee94c2fb809d2"
integrity sha512-Vg20KZ0ufgWjxx6GFbqC5wiVxXZDUWgNT0r0By/Eyj4bUSb1jG6lmf5z1oY1dUX0YS6Cp5e6GnvbNdXg5E7orA==
dependencies:
tslib "^2.1.0"

"@fluentui/style-utilities@^8.7.0":
version "8.7.0"
resolved "https://registry.yarnpkg.com/@fluentui/style-utilities/-/style-utilities-8.7.0.tgz#bc5a0ff17ae0d4dadc9c5b4b7f58c2abdf799667"
Expand All @@ -1335,6 +1367,28 @@
"@microsoft/load-themed-styles" "^1.10.26"
tslib "^2.1.0"

"@fluentui/style-utilities@^8.8.0":
version "8.8.0"
resolved "https://registry.yarnpkg.com/@fluentui/style-utilities/-/style-utilities-8.8.0.tgz#7430d34774a87eb776a819dc36d0627e0957a795"
integrity sha512-wqntrpzOGvBNojAlnXVQB98hYQkS0g5ZckF/JxkNDWYRUcemu9bUTgBOg1hdiV9DM8nxyg34LE794oMxRIuLHA==
dependencies:
"@fluentui/merge-styles" "^8.5.3"
"@fluentui/set-version" "^8.2.2"
"@fluentui/theme" "^2.6.16"
"@fluentui/utilities" "^8.13.1"
"@microsoft/load-themed-styles" "^1.10.26"
tslib "^2.1.0"

"@fluentui/theme@^2.6.16":
version "2.6.16"
resolved "https://registry.yarnpkg.com/@fluentui/theme/-/theme-2.6.16.tgz#a29c58bf16f765ba1b97a497e572fd1fdc469c44"
integrity sha512-Ml2oMVvoOxRYD9HPjEkGCWvnQnzDyrufa5k8bPYN8xjJbbEGtDjjswcfrSVfHx1fCR1CFgybHR8jj3pvXRTXUQ==
dependencies:
"@fluentui/merge-styles" "^8.5.3"
"@fluentui/set-version" "^8.2.2"
"@fluentui/utilities" "^8.13.1"
tslib "^2.1.0"

"@fluentui/theme@^2.6.6":
version "2.6.6"
resolved "https://registry.yarnpkg.com/@fluentui/theme/-/theme-2.6.6.tgz#282b6f1d4f564c43fc526f30aa1ae26b26b62f3f"
Expand All @@ -1345,6 +1399,16 @@
"@fluentui/utilities" "^8.8.3"
tslib "^2.1.0"

"@fluentui/utilities@^8.13.1":
version "8.13.1"
resolved "https://registry.yarnpkg.com/@fluentui/utilities/-/utilities-8.13.1.tgz#028fd2e93f68b5f386039970dce0ae642eb7e9da"
integrity sha512-BpLa0lSYnZ3YoTGB6T/pQ0vUVq0PEr6gF+daptyeiLUkEXVoy3HYgX6ZanA62wJ89ycIwI8A+1aUEbmtDMupYg==
dependencies:
"@fluentui/dom-utilities" "^2.2.2"
"@fluentui/merge-styles" "^8.5.3"
"@fluentui/set-version" "^8.2.2"
tslib "^2.1.0"

"@fluentui/utilities@^8.8.3":
version "8.8.3"
resolved "https://registry.yarnpkg.com/@fluentui/utilities/-/utilities-8.8.3.tgz#d41fd5f8ed96baa2b3d70853a0bf1ab6bfa1936d"
Expand Down