diff --git a/packages/synapse-react-client/src/components/AccessRequirementList/AccessRequirementList.tsx b/packages/synapse-react-client/src/components/AccessRequirementList/AccessRequirementList.tsx index ea40254f61..961384b0e8 100644 --- a/packages/synapse-react-client/src/components/AccessRequirementList/AccessRequirementList.tsx +++ b/packages/synapse-react-client/src/components/AccessRequirementList/AccessRequirementList.tsx @@ -16,7 +16,16 @@ import CancelRequestDataAccess from './ManagedACTAccessRequirementRequestFlow/Ca import ResearchProjectForm from './ManagedACTAccessRequirementRequestFlow/ResearchProjectForm/ResearchProjectForm' import DataAccessRequestAccessorsFilesForm from './ManagedACTAccessRequirementRequestFlow/DataAccessRequestAccessorsFilesForm/DataAccessRequestAccessorsFilesForm' import RequestDataAccessSuccess from './ManagedACTAccessRequirementRequestFlow/RequestDataAccessSuccess' -import { Box, Button, styled, Typography, TypographyProps } from '@mui/material' +import { + Box, + Button, + Dialog, + DialogActions, + DialogContent, + styled, + Typography, + TypographyProps, +} from '@mui/material' import AuthenticatedRequirement from './RequirementItem/AuthenticatedRequirement' import CertificationRequirement from './RequirementItem/CertificationRequirement' import ValidationRequirement from './RequirementItem/ValidationRequirement' @@ -31,7 +40,7 @@ import { AccessRequirementListItem } from './AccessRequirementListItem' import { useSynapseContext } from '../../utils' import { useCanShowManagedACTWikiInWizard } from './AccessRequirementListUtils' import { noop } from 'lodash-es' -import { DialogBase } from '../DialogBase' +import { DialogBaseTitle } from '../DialogBase' import UserOrTeamBadge from '../UserOrTeamBadge' import { EntityLink } from '../EntityLink' @@ -48,6 +57,8 @@ export type AccessRequirementListProps = { onHide: () => void /* Displays the provided list of access requirements, instead of the ones fetched using the subject ID */ accessRequirementFromProps?: Array + /* If provided, displays these actions on when viewing all ARs */ + customDialogActions?: React.ReactNode } & ( | { /** @@ -143,6 +154,7 @@ export default function AccessRequirementList( renderAsModal = false, numberOfFilesAffected, requestObjectName, + customDialogActions, } = props const isShowingRequirementsForEntity = 'entityId' in props @@ -279,44 +291,6 @@ export default function AccessRequirementList( } }, [numberOfFilesAffected, requestObjectName, subjectId, subjectType]) - const content = ( - <> - - What is this request for? - - - {requestDetails} - - What do I need to do? - - {anyARsRequireCertification && } - {anyARsRequireProfileValidation && } - {anyARsRequireTwoFactorAuth && } - {sortedAccessRequirementIds - ?.map(id => accessRequirements!.find(ar => id === String(ar.id))!) - ?.map(accessRequirement => { - return ( - { - const nextStep = isSignedIn - ? RequestDataStep.UPDATE_RESEARCH_PROJECT - : RequestDataStep.PROMPT_LOGIN - requestDataStepCallback({ - managedACTAccessRequirement: accessRequirement, - step: nextStep, - }) - }} - /> - ) - })} - - ) - const dialogWidth = [ RequestDataStep.UPDATE_ACCESSORS_AND_FILES, @@ -325,88 +299,131 @@ export default function AccessRequirementList( ? 'xl' : 'md' - let renderContent = content - let dialogActions = <> - if (renderAsModal) { - switch (requestDataStep) { - case RequestDataStep.UPDATE_RESEARCH_PROJECT: - renderContent = ( - { - requestDataStepCallback({ - managedACTAccessRequirement, - step: RequestDataStep.UPDATE_ACCESSORS_AND_FILES, - researchProjectId: researchProject.id, - }) - }} - onHide={onHide} - /> - ) - break - case RequestDataStep.UPDATE_ACCESSORS_AND_FILES: - renderContent = ( - { - requestDataStepCallback({ - step: RequestDataStep.PROMPT_CANCEL, - dataAccessRequest: dataAccessRequestInProgress, - }) - }} - onSubmissionCreated={() => { - requestDataStepCallback({ step: RequestDataStep.COMPLETE }) - }} - /> - ) - break - case RequestDataStep.PROMPT_CANCEL: - renderContent = ( - - ) - break - case RequestDataStep.PROMPT_LOGIN: - dialogTitle = 'Please Log In' - renderContent = ( - + let renderContent = <> + switch (requestDataStep) { + case RequestDataStep.UPDATE_RESEARCH_PROJECT: + renderContent = ( + { + requestDataStepCallback({ + managedACTAccessRequirement, + step: RequestDataStep.UPDATE_ACCESSORS_AND_FILES, + researchProjectId: researchProject.id, + }) + }} + onHide={onHide} + /> + ) + break + case RequestDataStep.UPDATE_ACCESSORS_AND_FILES: + renderContent = ( + { + requestDataStepCallback({ + step: RequestDataStep.PROMPT_CANCEL, + dataAccessRequest: dataAccessRequestInProgress, + }) + }} + onSubmissionCreated={() => { + requestDataStepCallback({ step: RequestDataStep.COMPLETE }) + }} + /> + ) + break + case RequestDataStep.PROMPT_CANCEL: + renderContent = ( + + ) + break + case RequestDataStep.PROMPT_LOGIN: + dialogTitle = 'Please Log In' + renderContent = ( + <> + + { window.location.reload() }} /> - - ) - break - case RequestDataStep.COMPLETE: - renderContent = - break - case RequestDataStep.SHOW_ALL_ARS: - default: - renderContent = content - dialogActions = ( - - ) - } + + + ) + break + case RequestDataStep.COMPLETE: + renderContent = + break + case RequestDataStep.SHOW_ALL_ARS: + default: + renderContent = ( + <> + + + + What is this request for? + + + {requestDetails} + + + What do I need to do? + + + {anyARsRequireCertification && } + {anyARsRequireProfileValidation && } + {anyARsRequireTwoFactorAuth && } + {sortedAccessRequirementIds + ?.map(id => accessRequirements!.find(ar => id === String(ar.id))!) + ?.map(accessRequirement => { + return ( + { + const nextStep = isSignedIn + ? RequestDataStep.UPDATE_RESEARCH_PROJECT + : RequestDataStep.PROMPT_LOGIN + requestDataStepCallback({ + managedACTAccessRequirement: accessRequirement, + step: nextStep, + }) + }} + /> + ) + })} + + + {customDialogActions ? ( + customDialogActions + ) : ( + + )} + + + ) + } + + if (renderAsModal) { return ( - + + {renderContent} + ) } + return {renderContent} } diff --git a/packages/synapse-react-client/src/components/ChallengeRequirementsModal/ChallengeRequirementsModal.tsx b/packages/synapse-react-client/src/components/ChallengeRequirementsModal/ChallengeRequirementsModal.tsx index f7b83c8bcb..8214c8b984 100644 --- a/packages/synapse-react-client/src/components/ChallengeRequirementsModal/ChallengeRequirementsModal.tsx +++ b/packages/synapse-react-client/src/components/ChallengeRequirementsModal/ChallengeRequirementsModal.tsx @@ -1,5 +1,4 @@ import React, { useCallback } from 'react' -import { ConfirmationDialog } from '../ConfirmationDialog' import { useGetAccessRequirementsForTeam, useGetAccessRequirementStatuses, @@ -15,7 +14,7 @@ import { SynapseClientError, useSynapseContext } from '../../utils' import AccessRequirementList from '../AccessRequirementList/AccessRequirementList' import { RestrictableObjectType } from '@sage-bionetworks/synapse-types' import { SynapseSpinner } from '../LoadingScreen/LoadingScreen' -import { Alert, Typography } from '@mui/material' +import { Alert, Button, Typography } from '@mui/material' import { isEmpty } from 'lodash-es' export type ChallengeRequirementsModalProps = { @@ -104,8 +103,8 @@ export default function ChallengeRequirementsModal( registrationError, ].filter((e): e is SynapseClientError => Boolean(e)) - // It's possible that the user joined the team, but requirements on the team changed since they became a member - // They do not need to re-join the team, but they should still be prompted to accept the requirements + // It's possible that the user joined the team, but requirements on the team changed since they became a member + // They do not need to re-join the team, but they should still be prompted to accept the requirements let registerButtonText = teamMembershipStatus?.isMember ? 'Continue' : 'Register' @@ -113,44 +112,50 @@ export default function ChallengeRequirementsModal( registerButtonText = 'Registering...' } + if (!isEmpty(errors)) { + return ( + + {errors.map((e, index) => ( + + {e.reason} + + ))} + + ) + } + + if (!participantTeamId || !open) { + return <> + } + return ( - { + onCancel() + }} + customDialogActions={ <> - {participantTeamId && ( - { - onCancel() - }} - /> - )} - {!isEmpty(errors) && ( - - {errors.map((e, index) => ( - - {e.reason} - - ))} - - )} + + } - onCancel={onCancel} - onConfirm={() => { - registerForChallenge() - }} - confirmButtonProps={{ - children: registerButtonText, - startIcon: registrationIsPending ? : undefined, - disabled: !meetsAllRequirements || registrationIsPending, - }} /> ) } diff --git a/packages/synapse-react-client/src/components/DialogBase.tsx b/packages/synapse-react-client/src/components/DialogBase.tsx index 7fa7e8d8a9..61893e8313 100644 --- a/packages/synapse-react-client/src/components/DialogBase.tsx +++ b/packages/synapse-react-client/src/components/DialogBase.tsx @@ -35,15 +35,38 @@ export const CloseButton: React.FC = ({ ) } -export type DialogBaseProps = { - open: boolean +export type DialogBaseTitleProps = { title: React.ReactNode + titleHelpPopoverProps?: HelpPopoverProps + hasCloseButton?: boolean + onCancel: () => void +} + +export function DialogBaseTitle(props: DialogBaseTitleProps) { + const { + title, + titleHelpPopoverProps, + hasCloseButton = true, + onCancel, + } = props + return ( + + + {title} + {titleHelpPopoverProps && } + + {hasCloseButton && onCancel()} />} + + + ) +} + +export type DialogBaseProps = DialogBaseTitleProps & { + open: boolean content: React.ReactNode actions?: React.ReactNode className?: string onCancel: () => void - hasCloseButton?: boolean - titleHelpPopoverProps?: HelpPopoverProps maxWidth?: DialogProps['maxWidth'] fullWidth?: boolean sx?: DialogProps['sx'] @@ -60,7 +83,7 @@ export const DialogBase = ({ actions, className, onCancel, - hasCloseButton = true, + hasCloseButton, titleHelpPopoverProps, maxWidth = 'sm', fullWidth = true, @@ -76,14 +99,12 @@ export const DialogBase = ({ onClose={() => onCancel()} sx={sx} > - - - {title} - {titleHelpPopoverProps && } - - {hasCloseButton && onCancel()} />} - - + {content} {actions && {actions}}