Skip to content

Commit

Permalink
Merge pull request Sage-Bionetworks#1092 from emmanmills/PORTALS-1941…
Browse files Browse the repository at this point in the history
…-revoke-buttons

PORTALS-1941: Add accessor renewal radio buttons
  • Loading branch information
emmanmills authored Jun 29, 2021
2 parents 1e8b6ff + 625c193 commit ad90d7a
Show file tree
Hide file tree
Showing 6 changed files with 143 additions and 56 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -44,16 +44,16 @@ type AccessRequirementAndStatus = {
export type AccessRequirementListProps = {
entityId: string // will show this entity info
accessRequirementFromProps?: Array<AccessRequirement>
onHide?: Function
onHide?: () => void
renderAsModal?: boolean
numberOfFilesAffected?: number // if provided, will show this instead of the entity information
}

export type requestDataStepCallbackProps = {
managedACTAccessRequirement: ManagedACTAccessRequirement
managedACTAccessRequirement?: ManagedACTAccessRequirement
step: number
researchProjectId: string
formSubmitRequestObject: RequestInterface
researchProjectId?: string
formSubmitRequestObject?: RequestInterface
}

export enum SUPPORTED_ACCESS_REQUIREMENTS {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ import { useSynapseContext } from '../../../utils/SynapseContext'

export type CancelRequestDataAccessProps = {
formSubmitRequestObject: RequestInterface | undefined
onHide: Function
onHide: () => void
}

const CancelRequestDataAccess: React.FC<CancelRequestDataAccessProps> = props => {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,14 +8,15 @@ import { SynapseClient } from '../../../utils'
import RequestDataAccess from './RequestDataAccess'
import { ManagedACTAccessRequirementStatus } from '../../../utils/synapseTypes/AccessRequirement/ManagedACTAccessRequirementStatus'
import { useSynapseContext } from '../../../utils/SynapseContext'
import { requestDataStepCallbackProps } from '../AccessRequirementList'

export type ManagedACTAccessRequirementComponentProps = {
entityId: string
user: UserProfile | undefined
accessRequirement: ManagedACTAccessRequirement
accessRequirementStatus: ManagedACTAccessRequirementStatus
onHide?: Function
requestDataStepCallback?: Function
onHide?: () => void
requestDataStepCallback?: (props: requestDataStepCallbackProps) => void
}

const ManagedACTAccessRequirementComponent: React.FC<ManagedACTAccessRequirementComponentProps> = props => {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,11 +10,12 @@ import { ResearchProject } from '../../../utils/synapseTypes/ResearchProject'
import { ManagedACTAccessRequirement } from '../../../utils/synapseTypes'
import { AlertProps } from './RequestDataAccessStep2'
import { useSynapseContext } from '../../../utils/SynapseContext'
import { requestDataStepCallbackProps } from '../AccessRequirementList'

export type RequestDataAccessStep1Props = {
requestDataStepCallback?: Function
requestDataStepCallback?: (props: requestDataStepCallbackProps) => void
managedACTAccessRequirement: ManagedACTAccessRequirement
onHide: Function
onHide: () => void
}

const RequestDataAccessStep1: React.FC<RequestDataAccessStep1Props> = props => {
Expand Down Expand Up @@ -70,7 +71,7 @@ const RequestDataAccessStep1: React.FC<RequestDataAccessStep1Props> = props => {
)
}

const handleSubmit = async (e: React.FormEvent<HTMLElement>) => {
const handleSubmit = (e: React.FormEvent<HTMLElement>) => {
e.preventDefault()
const requestObj: ResearchProject = Object.assign(
{},
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -30,14 +30,16 @@ import { UserCardSmall } from '../../UserCardSmall'
import IconSvg from '../../IconSvg'
import { useSynapseContext } from '../../../utils/SynapseContext'
import { RenewalInterface } from '../../../utils/synapseTypes/AccessRequirement/RenewalInterface'
import { RadioGroup } from '../../widgets/RadioGroup'
import { requestDataStepCallbackProps } from '../AccessRequirementList'

export type RequestDataAccessStep2Props = {
managedACTAccessRequirement: ManagedACTAccessRequirement
entityId: string
requestDataStepCallback: Function
requestDataStepCallback: (props: requestDataStepCallbackProps) => void
user: UserProfile
researchProjectId: string
onHide: Function
onHide: () => void
}

export type DataAccessDoc = {
Expand Down Expand Up @@ -66,6 +68,15 @@ export type AlertProps = {
message: string | JSX.Element
}

type requestedFileTypesMap = {
[key: string]: string[]
}

type Accessor = {
profile: UserProfile
accessType: AccessType
}

const RequestDataAccessStep2: React.FC<RequestDataAccessStep2Props> = props => {
const {
requestDataStepCallback,
Expand All @@ -79,14 +90,15 @@ const RequestDataAccessStep2: React.FC<RequestDataAccessStep2Props> = props => {
const [DUC, setDUC] = useState<DataAccessDoc>()
const [IRB, setIRB] = useState<DataAccessDoc>()
const [attachments, setAttachments] = useState<DataAccessDoc[]>([])
const [accessorProfiles, setAccessorProfiles] = useState<UserProfile[]>([])
const [
formSubmitRequestObject,
setFormSubmitRequestObject,
] = useState<RequestInterface|RenewalInterface>()
] = useState<RequestInterface | RenewalInterface>()
const [alert, setAlert] = useState<AlertProps | undefined>()
const [isRenewal, setIsRenewal] = useState<boolean>(false)
const requestedFileTypes = {}
const [accessors, setAccessors] = useState<Accessor[]>([])

const requestedFileTypes:requestedFileTypesMap = {}
const batchFileRequest: BatchFileRequest = {
requestedFiles: [],
includeFileHandles: true,
Expand All @@ -112,17 +124,18 @@ const RequestDataAccessStep2: React.FC<RequestDataAccessStep2Props> = props => {
accessToken!,
)

// renewal case
if (dataAccessRequestData.concreteType === 'org.sagebionetworks.repo.model.dataaccess.Renewal') {
setIsRenewal(true)
}

// initialize form submission request object
dataAccessRequestData.researchProjectId = researchProjectId
setFormSubmitRequestObject(dataAccessRequestData)
// get assessors' user profiles data for display and save them in the state variables
getAccessorsData(dataAccessRequestData)
// get data access required docs data to display file names
getFilesData(dataAccessRequestData)

if (dataAccessRequestData.concreteType === 'org.sagebionetworks.repo.model.dataaccess.Renewal') {
setIsRenewal(true)
}
}

const getAccessorsData = (dataAccessRequestData: RequestInterface) => {
Expand Down Expand Up @@ -152,7 +165,13 @@ const RequestDataAccessStep2: React.FC<RequestDataAccessStep2Props> = props => {
return getUserProfileById(accessToken, userId)
})
Promise.all(promises).then(profiles => {
setAccessorProfiles(profiles)
const profileAndAccessType:Accessor[] = profiles.map((item, i) => {
return {
profile: item,
accessType: accessorChanges[i].type
}
})
setAccessors(profileAndAccessType)
})
}

Expand Down Expand Up @@ -241,7 +260,7 @@ const RequestDataAccessStep2: React.FC<RequestDataAccessStep2Props> = props => {
getFiles(batchFileRequest, accessToken).then(resp => {
resp.requestedFiles.forEach(file => {
const fileName = file.fileHandle!.fileName
const fileTypes = requestedFileTypes[file.fileHandleId]
const fileTypes:string[] = requestedFileTypes[file.fileHandleId]

fileTypes.forEach((type: string) => {
switch (type) {
Expand Down Expand Up @@ -386,16 +405,16 @@ const RequestDataAccessStep2: React.FC<RequestDataAccessStep2Props> = props => {

const onClearAccessor = (pid: string) => {
// Update the view
const filtered: UserProfile[] = accessorProfiles.filter(
item => item.ownerId !== pid,
const filtered:Accessor[] = accessors.filter(item =>
item.profile.ownerId !== pid
)
setAccessorProfiles(filtered)
setAccessors(filtered)

// Update form submission request object
const newAccessorChanges: AccessorChange[] = filtered.map(item => {
return {
userId: item.ownerId,
type: AccessType.GAIN_ACCESS,
userId: item.profile.ownerId,
type: item.accessType
}
})
setFormSubmitRequestObject(prevState => {
Expand All @@ -408,7 +427,7 @@ const RequestDataAccessStep2: React.FC<RequestDataAccessStep2Props> = props => {
const onClearAttachment = (fid: string) => {
// Update the view
const filtered: DataAccessDoc[] = attachments.filter(
item => item.fileHandleId !== fid,
item => item.fileHandleId !== fid
)
setAttachments(filtered)

Expand Down Expand Up @@ -466,15 +485,19 @@ const RequestDataAccessStep2: React.FC<RequestDataAccessStep2Props> = props => {

// User search input event handler
const onSelectUserCallback = (selected: UserProfile) => {
setAccessorProfiles(prev => [
setAccessors(prev => [
...prev,
{
ownerId: selected.ownerId,
firstName: selected.firstName,
lastName: selected.lastName,
userName: selected.userName,
},
profile: {
ownerId: selected.ownerId,
firstName: selected.firstName,
lastName: selected.lastName,
userName: selected.userName,
},
accessType: AccessType.GAIN_ACCESS
}
])

const selectedAccessor: AccessorChange = {
userId: selected.ownerId,
type: AccessType.GAIN_ACCESS,
Expand Down Expand Up @@ -509,6 +532,25 @@ const RequestDataAccessStep2: React.FC<RequestDataAccessStep2Props> = props => {
}
}

// For renewal only
const onAccessorRadioBtnChange = (accessType: AccessType, userId: string) => {
// Make the radio button appears selected when clicked.
const copy = [...accessors]
const index = copy.findIndex(item => item.profile.ownerId === userId)
copy[index].accessType = accessType
setAccessors(copy)

// Update formSubmitRequestObject
const formCopy = formSubmitRequestObject?.accessorChanges || []
const index2 = formCopy.findIndex(item => item.userId === userId)
formCopy[index2].type = accessType
setFormSubmitRequestObject(prevState => {
return Object.assign({}, prevState, {
accessorChanges: formCopy
})
})
}

return (
<>
<Form
Expand Down Expand Up @@ -557,29 +599,55 @@ const RequestDataAccessStep2: React.FC<RequestDataAccessStep2Props> = props => {

{/* Accessors Checkboxes */}
<Form.Group style={{ marginBottom: '4rem' }}>
{accessorProfiles.map((profile, i) => {
return (
<div className={'list-items'} key={`accessors-${i}`}>
<UserCardSmall
userProfile={profile}
showAccountLevelIcon={true}
disableLink={true}
/>
{
// only display delete button if the user profile is other users.
user.ownerId !== profile.ownerId && (
<Button
className={'clear-x'}
variant={'link'}
onClick={() => onClearAccessor(profile.ownerId)}
>
<IconSvg options={{ icon: 'clear' }} />
</Button>
)
}
</div>
)
})}
{
accessors.map((accessor, i ) => {
return (
<div className={'list-items'} key={`accessor-${i}`}>
<UserCardSmall
userProfile={accessor.profile}
showAccountLevelIcon={true}
disableLink={true}
/>
{
// only display delete button if the user profile is other users and has not access before
(user.ownerId !== accessor.profile.ownerId) && (accessor.accessType === AccessType.GAIN_ACCESS) && (
<Button
className={'clear-x'}
variant={'link'}
onClick={() => onClearAccessor(accessor.profile.ownerId)}
>
<IconSvg options={{ icon: 'clear' }} />
</Button>
)
}
{
// Renewal/Revoke data access, only display if isRenewal is true
isRenewal && (accessor.accessType !== AccessType.GAIN_ACCESS) && (
<>
<RadioGroup
id="accessor-access"
value={accessor.accessType}
options={[
{
label: 'Renew',
value: AccessType.RENEW_ACCESS
},
{
label: 'Revoke',
value: AccessType.REVOKE_ACCESS
},
]}
onChange={(value: string, checked: boolean) =>
onAccessorRadioBtnChange(value as AccessType, accessor.profile.ownerId)
}
></RadioGroup>
</>
)
}
</div>
)
})
}
</Form.Group>

{/* DUC */}
Expand Down
17 changes: 17 additions & 0 deletions src/lib/style/components/_access-requirement-list.scss
Original file line number Diff line number Diff line change
Expand Up @@ -169,6 +169,23 @@
margin-bottom: 0.5rem;
display: inline-block;
}
.SRC-userCard {
svg {
margin-top: 8px;
}
}
.radiogroup {
flex: auto;
text-align: right;
.radio, .radio+.radio {
display: inline-block;
margin: 0;
margin-left: 1rem;
}
label {
margin-bottom: 0;
}
}
}
}
}

0 comments on commit ad90d7a

Please sign in to comment.