synapse-react-client 4.0.9 → 4.0.10
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/SWC.index.d.ts +1 -0
- package/dist/SWC.index.d.ts.map +1 -1
- package/dist/SWC.index.js +2 -1
- package/dist/SWC.index.js.map +1 -1
- package/dist/aridhia-queries/aridhiaTokenExchange.js.map +1 -1
- package/dist/aridhia-queries/useGetAridhiaRequests.js.map +1 -1
- package/dist/assets/icons/TasksIcon.d.ts.map +1 -1
- package/dist/assets/icons/TasksIcon.js +6 -10
- package/dist/assets/icons/TasksIcon.js.map +1 -1
- package/dist/components/AccessRequirementAclEditor/AccessRequirementAclEditor.d.ts.map +1 -1
- package/dist/components/AccessRequirementAclEditor/AccessRequirementAclEditor.js +69 -63
- package/dist/components/AccessRequirementAclEditor/AccessRequirementAclEditor.js.map +1 -1
- package/dist/components/AccessRequirementList/AccessApprovalCheckMark.js.map +1 -1
- package/dist/components/AccessRequirementList/AccessRequirementList.js.map +1 -1
- package/dist/components/AccessRequirementList/AccessRequirementListUtils.js.map +1 -1
- package/dist/components/AccessRequirementList/ManagedACTAccessRequirementRequestFlow/DataAccessRequestAccessorsEditor.js.map +1 -1
- package/dist/components/AccessRequirementList/RequirementItem/SelfSignAccessRequirementItem.js.map +1 -1
- package/dist/components/AccessRequirementRelatedProjectsList/AccessRequirementRelatedProjectsList.js.map +1 -1
- package/dist/components/AccessTokenPage/AccessTokenCard/AccessTokenCard.js.map +1 -1
- package/dist/components/AcknowledgementsPage/StudyAcknowledgements.js.map +1 -1
- package/dist/components/AclEditor/PermissionLevelMenu.js.map +1 -1
- package/dist/components/AclEditor/ResourceAccessAndUserGroupHeader.js.map +1 -1
- package/dist/components/AclEditor/useSortResourceAccessList.js.map +1 -1
- package/dist/components/AclEditor/useUpdateAcl.js.map +1 -1
- package/dist/components/Aridhia/AridhiaAccessStatus.js.map +1 -1
- package/dist/components/Authentication/AuthenticationMethodSelection.d.ts.map +1 -1
- package/dist/components/Authentication/AuthenticationMethodSelection.js +38 -37
- package/dist/components/Authentication/AuthenticationMethodSelection.js.map +1 -1
- package/dist/components/Authentication/Constants.d.ts +1 -0
- package/dist/components/Authentication/Constants.d.ts.map +1 -1
- package/dist/components/Authentication/Constants.js +2 -2
- package/dist/components/Authentication/Constants.js.map +1 -1
- package/dist/components/Authentication/LastLoginInfo.js.map +1 -1
- package/dist/components/Authentication/RecoveryCodeForm.js.map +1 -1
- package/dist/components/Authentication/RecoveryCodeGrid.js.map +1 -1
- package/dist/components/Authentication/RegenerateBackupCodesWarning.js.map +1 -1
- package/dist/components/Authentication/Reset2FAWarning.js.map +1 -1
- package/dist/components/Authentication/StandaloneLoginForm.js +1 -1
- package/dist/components/Authentication/TwoFactorBackupCodes.js.map +1 -1
- package/dist/components/Authentication/TwoFactorEnrollmentForm.d.ts.map +1 -1
- package/dist/components/Authentication/TwoFactorEnrollmentForm.js +2 -1
- package/dist/components/Authentication/TwoFactorEnrollmentForm.js.map +1 -1
- package/dist/components/BasePortalCard/ColorfulPortalCardWithChips/ColorfulPortalCardWithChips.js.map +1 -1
- package/dist/components/CardContainer/CardContainer.js.map +1 -1
- package/dist/components/CardDeck/CardDeck.Mobile.js.map +1 -1
- package/dist/components/CardDeck/TableQueryCardDeck.js.map +1 -1
- package/dist/components/CertificationQuiz/CertificationQuiz.js.map +1 -1
- package/dist/components/ChallengeDataDownload/ChallengeDataDownload.js.map +1 -1
- package/dist/components/ChallengeSubmission/ChallengeSubmission.js.map +1 -1
- package/dist/components/ChallengeSubmission/ChallengeSubmissionStepper.js.map +1 -1
- package/dist/components/ChallengeSubmission/EvaluationQueueCurrentRoundInfo.js.map +1 -1
- package/dist/components/ChallengeSubmission/EvaluationQueueList.js.map +1 -1
- package/dist/components/ChallengeSubmission/SubmissionDirectoryList.js.map +1 -1
- package/dist/components/ChallengeTeamWizard/ChallengeTeamWizard.js.map +1 -1
- package/dist/components/ChallengeTeamWizard/CreateChallengeTeam.js.map +1 -1
- package/dist/components/ChangePassword/ChangePassword.js.map +1 -1
- package/dist/components/ChangePassword/ChangePasswordWithToken.js.map +1 -1
- package/dist/components/ChangePassword/useChangePasswordFormState.js +1 -1
- package/dist/components/ChangePassword/useChangePasswordFormState.js.map +1 -1
- package/dist/components/CitationPopover/CitationPopoverContent.js.map +1 -1
- package/dist/components/ColumnFilter/ColumnFilter.js.map +1 -1
- package/dist/components/ComponentCollapse.js.map +1 -1
- package/dist/components/CookiesNotification/CookiesNotification.js.map +1 -1
- package/dist/components/CreateProjectModal/CreateProjectModal.js.map +1 -1
- package/dist/components/CreateTableViewWizard/CreateTableViewWizardUtils.js.map +1 -1
- package/dist/components/DataGrid/DataGrid.d.ts +0 -1
- package/dist/components/DataGrid/DataGrid.d.ts.map +1 -1
- package/dist/components/DataGrid/DataGrid.js +72 -72
- package/dist/components/DataGrid/DataGrid.js.map +1 -1
- package/dist/components/DataGrid/DataGridWebSocket.d.ts +4 -0
- package/dist/components/DataGrid/DataGridWebSocket.d.ts.map +1 -1
- package/dist/components/DataGrid/DataGridWebSocket.js +9 -8
- package/dist/components/DataGrid/DataGridWebSocket.js.map +1 -1
- package/dist/components/DataGrid/SynapseGrid.d.ts.map +1 -1
- package/dist/components/DataGrid/SynapseGrid.js +326 -268
- package/dist/components/DataGrid/SynapseGrid.js.map +1 -1
- package/dist/components/DataGrid/columns/AutocompleteColumn.d.ts +2 -0
- package/dist/components/DataGrid/columns/AutocompleteColumn.d.ts.map +1 -1
- package/dist/components/DataGrid/columns/AutocompleteColumn.js +113 -67
- package/dist/components/DataGrid/columns/AutocompleteColumn.js.map +1 -1
- package/dist/components/DataGrid/columns/AutocompleteMultipleEnumColumn.d.ts +2 -1
- package/dist/components/DataGrid/columns/AutocompleteMultipleEnumColumn.d.ts.map +1 -1
- package/dist/components/DataGrid/columns/AutocompleteMultipleEnumColumn.js +126 -122
- package/dist/components/DataGrid/columns/AutocompleteMultipleEnumColumn.js.map +1 -1
- package/dist/components/DataGrid/columns/useGridAutocompleteState.d.ts +58 -0
- package/dist/components/DataGrid/columns/useGridAutocompleteState.d.ts.map +1 -0
- package/dist/components/DataGrid/columns/useGridAutocompleteState.js +52 -0
- package/dist/components/DataGrid/columns/useGridAutocompleteState.js.map +1 -0
- package/dist/components/DataGrid/components/ValidationAlert.d.ts +5 -2
- package/dist/components/DataGrid/components/ValidationAlert.d.ts.map +1 -1
- package/dist/components/DataGrid/components/ValidationAlert.js +429 -24
- package/dist/components/DataGrid/components/ValidationAlert.js.map +1 -1
- package/dist/components/DataGrid/hooks/useColumnResizeHandles.js.map +1 -1
- package/dist/components/DataGrid/hooks/useGetSchemaForGrid.js.map +1 -1
- package/dist/components/DataGrid/hooks/useGridUndoRedo.js.map +1 -1
- package/dist/components/DataGrid/hooks/useStack.js.map +1 -1
- package/dist/components/DataGrid/useCRDTModelView.js.map +1 -1
- package/dist/components/DataGrid/useDataGridWebsocket.d.ts +7 -0
- package/dist/components/DataGrid/useDataGridWebsocket.d.ts.map +1 -1
- package/dist/components/DataGrid/useDataGridWebsocket.js +16 -2
- package/dist/components/DataGrid/useDataGridWebsocket.js.map +1 -1
- package/dist/components/DataGrid/useInitializeGridConnection.js.map +1 -1
- package/dist/components/DataGrid/useMergeGridWithRecordSet.js.map +1 -1
- package/dist/components/DataGrid/useMergeGridWithSource.js.map +1 -1
- package/dist/components/DataGrid/useMergeGridWithTable.js.map +1 -1
- package/dist/components/DataGrid/utils/DataGridUtils.js.map +1 -1
- package/dist/components/DataGrid/utils/applyModelChange.js.map +1 -1
- package/dist/components/DataGrid/utils/columnFactory.js.map +1 -1
- package/dist/components/DataGrid/utils/computeReplicaSelectionModel.js.map +1 -1
- package/dist/components/DataGrid/utils/extractColumnValidationMessages.js.map +1 -1
- package/dist/components/DataGrid/utils/getCellClassName.d.ts.map +1 -1
- package/dist/components/DataGrid/utils/getCellClassName.js +8 -8
- package/dist/components/DataGrid/utils/getCellClassName.js.map +1 -1
- package/dist/components/DataGrid/utils/json-rx/JsonRx.js.map +1 -1
- package/dist/components/DataGrid/utils/modelRowsToGrid.js.map +1 -1
- package/dist/components/DataGrid/utils/parseFreeTextUsingJsonSchemaType.js.map +1 -1
- package/dist/components/DataGrid/utils/splitPatch.js.map +1 -1
- package/dist/components/DateTimePicker/DateTimePicker.js.map +1 -1
- package/dist/components/DirectDownload/DirectDownload.js.map +1 -1
- package/dist/components/DirectDownloadButton.js.map +1 -1
- package/dist/components/DownloadCart/CreatePackageV2.js.map +1 -1
- package/dist/components/DownloadCart/DownloadIneligibleForPackagingFilesFromListButton.js.map +1 -1
- package/dist/components/DownloadCart/DownloadListActionsRequired.js.map +1 -1
- package/dist/components/DownloadCart/DownloadListTable.js.map +1 -1
- package/dist/components/DownloadCart/fileNameUtils.js.map +1 -1
- package/dist/components/DraggableDialog/DraggableDialog.js.map +1 -1
- package/dist/components/DynamicForm/DynamicFormModal.js.map +1 -1
- package/dist/components/Ecosystem/TableQueryEcosystem.js.map +1 -1
- package/dist/components/EntityAclEditor/EntityAclEditor.d.ts.map +1 -1
- package/dist/components/EntityAclEditor/EntityAclEditor.js +103 -103
- package/dist/components/EntityAclEditor/EntityAclEditor.js.map +1 -1
- package/dist/components/EntityAclEditor/useNotifyNewACLUsers.js.map +1 -1
- package/dist/components/EntityBadgeIcons/EntityBadgeIcons.js.map +1 -1
- package/dist/components/EntityCitation/EntityCitation.js.map +1 -1
- package/dist/components/EntityDownloadButton/EntityDownloadButton.js.map +1 -1
- package/dist/components/EntityDownloadConfirmation/EntityDownloadConfirmation.d.ts.map +1 -1
- package/dist/components/EntityDownloadConfirmation/EntityDownloadConfirmation.js +36 -30
- package/dist/components/EntityDownloadConfirmation/EntityDownloadConfirmation.js.map +1 -1
- package/dist/components/EntityFinder/EntityFinder.js.map +1 -1
- package/dist/components/EntityFinder/VersionSelectionType.js.map +1 -1
- package/dist/components/EntityFinder/details/configurations/EntityChildrenDetails.js.map +1 -1
- package/dist/components/EntityFinder/details/configurations/FavoritesDetails.js.map +1 -1
- package/dist/components/EntityFinder/details/configurations/ProjectListDetails.js.map +1 -1
- package/dist/components/EntityFinder/details/view/DetailsView.js.map +1 -1
- package/dist/components/EntityFinder/tree/EntityTree.js.map +1 -1
- package/dist/components/EntityFinder/tree/VirtualizedTree.js.map +1 -1
- package/dist/components/EntityFinder/useEntitySelection.js.map +1 -1
- package/dist/components/EntityForm/EntityForm.js.map +1 -1
- package/dist/components/EntityHeaderTable/EntityHeaderTable.js.map +1 -1
- package/dist/components/EntityHeaderTable/Filter.js.map +1 -1
- package/dist/components/EntityHeaderTable/useEntityHeaderTableState.js.map +1 -1
- package/dist/components/EntitySubjectsSelector/EntitySubjectsSelector.js.map +1 -1
- package/dist/components/EntityTreeTable/components/IdColumnHeader.js.map +1 -1
- package/dist/components/EntityTreeTable/hooks/useEntityTreeState.js.map +1 -1
- package/dist/components/EntityTreeTable/hooks/useTableColumns.js.map +1 -1
- package/dist/components/EntityTreeTable/hooks/useTableData.js.map +1 -1
- package/dist/components/EntityTreeTable/hooks/useTreeOperationsWithDirectFetch.js.map +1 -1
- package/dist/components/EntityUpload/EntityUpload.js.map +1 -1
- package/dist/components/ExperimentalMode/ExperimentalMode.js.map +1 -1
- package/dist/components/ExternalFileHandleLink/ExternalFileHandleLink.js.map +1 -1
- package/dist/components/FeaturedDataTabs/FacetPlotsCard.js.map +1 -1
- package/dist/components/FeaturedDataTabs/QueryPerFacetPlotsCard.js.map +1 -1
- package/dist/components/FeaturedDataTabs/SingleQueryFacetPlotsCards.js.map +1 -1
- package/dist/components/FeaturedResearch/FeaturedResearch.js.map +1 -1
- package/dist/components/FeaturedToolsList/FeaturedToolsList.js.map +1 -1
- package/dist/components/FilePreview/FileHandleContentRenderer.js.map +1 -1
- package/dist/components/FilePreview/HtmlPreview/HtmlPreview.js.map +1 -1
- package/dist/components/FilePreview/PreviewRendererType.js.map +1 -1
- package/dist/components/Forum/DiscussionReply.js.map +1 -1
- package/dist/components/Forum/DiscussionSearchResult.js.map +1 -1
- package/dist/components/Forum/ForumTable.js.map +1 -1
- package/dist/components/Forum/ForumThreadEditor.js.map +1 -1
- package/dist/components/FullTextSearch/FullTextSearchUtils.js.map +1 -1
- package/dist/components/GenericCard/GenericCard.d.ts.map +1 -1
- package/dist/components/GenericCard/GenericCard.js +12 -7
- package/dist/components/GenericCard/GenericCard.js.map +1 -1
- package/dist/components/GenericCard/Linkify.js.map +1 -1
- package/dist/components/GenericCard/SynapseCardLabel.js.map +1 -1
- package/dist/components/GenericCard/TableRowGenericCard.js +105 -105
- package/dist/components/GenericCard/TableRowGenericCard.js.map +1 -1
- package/dist/components/Goals/Goals.Mobile.js.map +1 -1
- package/dist/components/Goals/Goals.js.map +1 -1
- package/dist/components/GoalsV2/GoalsV2.Mobile.js.map +1 -1
- package/dist/components/GoalsV2/GoalsV2.js.map +1 -1
- package/dist/components/GoalsV3/GoalsV3.Mobile.js.map +1 -1
- package/dist/components/GoalsV3/GoalsV3.js.map +1 -1
- package/dist/components/GoogleMap/SynapseUserMarker.js.map +1 -1
- package/dist/components/HasAccess/AccessIcon.js.map +1 -1
- package/dist/components/HasAccess/useHasAccess.js.map +1 -1
- package/dist/components/HeaderCard/HeaderCardV2.js.map +1 -1
- package/dist/components/HeaderCard.d.ts +6 -1
- package/dist/components/HeaderCard.d.ts.map +1 -1
- package/dist/components/HeaderCard.js +107 -76
- package/dist/components/HeaderCard.js.map +1 -1
- package/dist/components/HexGrid/HexGrid.js.map +1 -1
- package/dist/components/IconList.js.map +1 -1
- package/dist/components/ImageCardGridWithLinks/ImageCardGridWithLinks.js.map +1 -1
- package/dist/components/ImageFromSynapseTable.js.map +1 -1
- package/dist/components/JSONArrayEditor/useParseCsv.js.map +1 -1
- package/dist/components/JsonSchemaForm/templates/ArrayFieldDescriptionTemplate.js.map +1 -1
- package/dist/components/JsonSchemaForm/templates/ArrayFieldItemTemplate.js.map +1 -1
- package/dist/components/JsonSchemaForm/templates/BaseInputTemplate.js.map +1 -1
- package/dist/components/JsonSchemaForm/templates/FieldTemplate.js.map +1 -1
- package/dist/components/JsonSchemaForm/templates/RJSFInputLabel.js.map +1 -1
- package/dist/components/Markdown/MarkdownGithub.js.map +1 -1
- package/dist/components/Markdown/MarkdownSynapse.js.map +1 -1
- package/dist/components/Markdown/MarkdownUtils.js.map +1 -1
- package/dist/components/Markdown/SynapseWikiContext.js.map +1 -1
- package/dist/components/Markdown/UserMentionModal.js.map +1 -1
- package/dist/components/Markdown/widget/MarkdownProvenanceGraph.js.map +1 -1
- package/dist/components/MissingQueryResultsWarning/MissingQueryResultsWarning.js.map +1 -1
- package/dist/components/ModalDownload/ModalDownload.js.map +1 -1
- package/dist/components/OAuthClientAclEditor/OAuthClientAclEditor.d.ts.map +1 -1
- package/dist/components/OAuthClientAclEditor/OAuthClientAclEditor.js +45 -39
- package/dist/components/OAuthClientAclEditor/OAuthClientAclEditor.js.map +1 -1
- package/dist/components/OAuthClientManagement/OAuthManagement.js.map +1 -1
- package/dist/components/PageProgress/PageProgress.js.map +1 -1
- package/dist/components/Plot/DotPlot.js.map +1 -1
- package/dist/components/Plot/Plot.js.map +1 -1
- package/dist/components/Plot/SynapsePlot.js.map +1 -1
- package/dist/components/Plot/ThemesPlot.js.map +1 -1
- package/dist/components/Plot/UpsetPlot.js.map +1 -1
- package/dist/components/PortalAclEditor/PortalAclEditor.d.ts.map +1 -1
- package/dist/components/PortalAclEditor/PortalAclEditor.js +43 -41
- package/dist/components/PortalAclEditor/PortalAclEditor.js.map +1 -1
- package/dist/components/PortalFeaturedPartners/PortalFeaturedPartners.js.map +1 -1
- package/dist/components/PortalList/CreatePortalModal.js.map +1 -1
- package/dist/components/ProgrammaticInstructionsModal/ProgrammaticInstructionsModal.js.map +1 -1
- package/dist/components/ProgrammaticTableDownload/ProgrammaticTableDownload.js.map +1 -1
- package/dist/components/Programs/Programs.Mobile.js.map +1 -1
- package/dist/components/Programs/Programs.js.map +1 -1
- package/dist/components/ProvenanceGraph/ProvenanceExternalIcon.js.map +1 -1
- package/dist/components/ProvenanceGraph/ProvenanceGraph.js.map +1 -1
- package/dist/components/ProvenanceGraph/ProvenanceGraphUtils.js.map +1 -1
- package/dist/components/ProvenanceGraph/ProvenanceUtils.js.map +1 -1
- package/dist/components/QueryCount/QueryCount.js.map +1 -1
- package/dist/components/QueryCountButton/QueryCountButton.js.map +1 -1
- package/dist/components/QueryVisualizationWrapper/QueryVisualizationWrapper.js.map +1 -1
- package/dist/components/QueryWrapper/QueryWrapper.js.map +1 -1
- package/dist/components/QueryWrapper/TableQueryUseQueryOptions.js.map +1 -1
- package/dist/components/QueryWrapper/TableRowSelectionState.js.map +1 -1
- package/dist/components/QueryWrapper/generateEncodedPathAndQueryForSelectedFacetURL.js.map +1 -1
- package/dist/components/QueryWrapper/useGetQueryMetadata.js.map +1 -1
- package/dist/components/QueryWrapperErrorBoundary.js.map +1 -1
- package/dist/components/QueryWrapperPlotNav/QueryWrapperPlotNav.js.map +1 -1
- package/dist/components/QueryWrapperPlotNav/UseRowSet.js.map +1 -1
- package/dist/components/RecentPublicationsGrid/RecentPublicationsGrid.js.map +1 -1
- package/dist/components/ReleaseCard/ReleaseCardUtils.js.map +1 -1
- package/dist/components/ResizableContainer/hooks/useResizable.js.map +1 -1
- package/dist/components/Resources/Resources.Mobile.js.map +1 -1
- package/dist/components/Resources/Resources.js.map +1 -1
- package/dist/components/RowDataTable/RowDataTableWithQuery.js.map +1 -1
- package/dist/components/SageResourcesPopover/SageResourcesPopover.js.map +1 -1
- package/dist/components/SchemaDrivenAnnotationEditor/AnnotationEditorUtils.js.map +1 -1
- package/dist/components/SetAccessRequirementCommonFields/SetAccessRequirementCommonFields.js.map +1 -1
- package/dist/components/SetManagedAccessRequirementFields/SetManagedAccessRequirementFields.js.map +1 -1
- package/dist/components/SmartLink/SmartButton.js.map +1 -1
- package/dist/components/SmartLink/SmartLink.js.map +1 -1
- package/dist/components/SourceAppImage.js.map +1 -1
- package/dist/components/StandaloneQueryWrapper/StandaloneQueryWrapper.js.map +1 -1
- package/dist/components/StatisticsPlot.js.map +1 -1
- package/dist/components/StorybookComponentWrapper.js.map +1 -1
- package/dist/components/SubsectionRowRenderer/SubsectionRowRenderer.js.map +1 -1
- package/dist/components/SustainabilityScorecard/SustainabilityScorecard.js.map +1 -1
- package/dist/components/SynapseChat/GridAgentChat.js.map +1 -1
- package/dist/components/SynapseChat/SynapseChatInteraction.js.map +1 -1
- package/dist/components/SynapseChat/SynapseChatMessage.js.map +1 -1
- package/dist/components/SynapseChat/extractMessageFromTraceEvent.js.map +1 -1
- package/dist/components/SynapseForm/StepsSideNav.js.map +1 -1
- package/dist/components/SynapseForm/SummaryTable.js.map +1 -1
- package/dist/components/SynapseForm/SynapseForm.js +4 -2
- package/dist/components/SynapseForm/SynapseForm.js.map +1 -1
- package/dist/components/SynapseForm/SynapseFormWrapper.js.map +1 -1
- package/dist/components/SynapseHomepageV2/SynapseByTheNumbersItem.js.map +1 -1
- package/dist/components/SynapseHomepageV2/SynapseFeatureItem.js.map +1 -1
- package/dist/components/SynapseHomepageV2/SynapseHomepageChatSearch.js.map +1 -1
- package/dist/components/SynapseHomepageV2/SynapseHomepageSearch.js.map +1 -1
- package/dist/components/SynapseHomepageV2/SynapseInActionItem.js.map +1 -1
- package/dist/components/SynapseHomepageV2/SynapsePlans.js.map +1 -1
- package/dist/components/SynapseHomepageV2/SynapseTrendingProjects.js.map +1 -1
- package/dist/components/SynapseNavDrawer/SynapseNavDrawer.d.ts +8 -7
- package/dist/components/SynapseNavDrawer/SynapseNavDrawer.d.ts.map +1 -1
- package/dist/components/SynapseNavDrawer/SynapseNavDrawer.js +173 -164
- package/dist/components/SynapseNavDrawer/SynapseNavDrawer.js.map +1 -1
- package/dist/components/SynapsePortalBanners/SynapsePortalBanners.js.map +1 -1
- package/dist/components/SynapseSearchPageResults/SearchFacetPanel/SearchFacetPanel.js.map +1 -1
- package/dist/components/SynapseSearchPageResults/SearchFacetPanel/SearchFacetPanelUtils.js.map +1 -1
- package/dist/components/SynapseSearchPageResults/SynapseSearchPageResults.js.map +1 -1
- package/dist/components/SynapseTable/EntityIDColumnCopyIcon.js.map +1 -1
- package/dist/components/SynapseTable/NoContentPlaceholderType.js.map +1 -1
- package/dist/components/SynapseTable/RowSelection/RowSelectionControls.js.map +1 -1
- package/dist/components/SynapseTable/SynapseTableCell/SynapseTableCell.js.map +1 -1
- package/dist/components/SynapseTable/SynapseTableRenderers.js.map +1 -1
- package/dist/components/SynapseTable/datasets/DatasetItemsEditor.js.map +1 -1
- package/dist/components/SynapseTable/table-top/ColumnSelection.js.map +1 -1
- package/dist/components/SynapseTable/table-top/DownloadOptions.js.map +1 -1
- package/dist/components/SynapseTable/usePrefetchTableData.js.map +1 -1
- package/dist/components/TableColumnSchemaEditor/ColumnModelForm.js.map +1 -1
- package/dist/components/TableColumnSchemaEditor/ColumnModelFormFields/DefaultValueField.js.map +1 -1
- package/dist/components/TableColumnSchemaEditor/ImportTableColumnsButton.js.map +1 -1
- package/dist/components/TableColumnSchemaEditor/TableColumnSchemaEditorUtils.d.ts +1 -1
- package/dist/components/TableColumnSchemaEditor/TableColumnSchemaEditorUtils.d.ts.map +1 -1
- package/dist/components/TableColumnSchemaEditor/TableColumnSchemaEditorUtils.js.map +1 -1
- package/dist/components/TableColumnSchemaEditor/TableColumnSchemaForm.js.map +1 -1
- package/dist/components/TableColumnSchemaEditor/TableColumnSchemaFormReducer.js.map +1 -1
- package/dist/components/TableColumnSchemaEditor/Validators/ColumnModelValidator.js.map +1 -1
- package/dist/components/TableColumnSchemaEditor/Validators/DatetimeSchema.js.map +1 -1
- package/dist/components/TanStackTable/ColumnHeader.d.ts +1 -0
- package/dist/components/TanStackTable/ColumnHeader.d.ts.map +1 -1
- package/dist/components/TanStackTable/ColumnHeader.js +8 -8
- package/dist/components/TanStackTable/ColumnHeader.js.map +1 -1
- package/dist/components/TanStackTable/ColumnHeaderEnumFilter.js.map +1 -1
- package/dist/components/TanStackTable/TableBody.js.map +1 -1
- package/dist/components/TeamSubjectsSelector/TeamSubjectsSelector.js.map +1 -1
- package/dist/components/TextField/TextField.js.map +1 -1
- package/dist/components/TimelinePlot/TimelinePhase.js.map +1 -1
- package/dist/components/TimelinePlot/TimelinePlot.js.map +1 -1
- package/dist/components/TimelinePlot/TimelinePlotSpeciesSelector.js.map +1 -1
- package/dist/components/UserCard/Avatar.js.map +1 -1
- package/dist/components/UserCardList/UserCardList.js.map +1 -1
- package/dist/components/UserCardList/UserCardListGroups/UserCardListGroups.Mobile.js.map +1 -1
- package/dist/components/UserCardList/UserCardListRotate.js.map +1 -1
- package/dist/components/UserOrTeamBadge/useUserOrTeam.js.map +1 -1
- package/dist/components/UserProfileLinks/UserProjects.js.map +1 -1
- package/dist/components/UserSearchBox/UserSearchBox.js.map +1 -1
- package/dist/components/Webhook/WebhookDashboard.js.map +1 -1
- package/dist/components/WikiMarkdownEditor/WikiMarkdownEditor.js.map +1 -1
- package/dist/components/WikiMarkdownEditorButton/WikiMarkdownEditorButton.js.map +1 -1
- package/dist/components/dataaccess/AccessApprovalsTable.js.map +1 -1
- package/dist/components/dataaccess/AccessRequestSubmissionTable.js.map +1 -1
- package/dist/components/dataaccess/SubmissionPage/SubmissionPage.js.map +1 -1
- package/dist/components/dataaccess/UseAccessRequirementTable.js.map +1 -1
- package/dist/components/dataaccess/UserAccessRequestHistory/UserAccessRequestHistoryTable.js.map +1 -1
- package/dist/components/doi/CreateOrUpdateDoiModal.js.map +1 -1
- package/dist/components/entity/page/CreatedByModifiedBy.js.map +1 -1
- package/dist/components/entity/page/action_menu/EntityActionMenu.js.map +1 -1
- package/dist/components/entity/page/title_bar/useDataCiteUsage.js.map +1 -1
- package/dist/components/entity/page/title_bar/useGetMentions.js.map +1 -1
- package/dist/components/error/ErrorPage.js.map +1 -1
- package/dist/components/favorites/FavoritesPage.js.map +1 -1
- package/dist/components/file/upload/BasicFileHandleUpload.js.map +1 -1
- package/dist/components/layout/SWCHeader.d.ts +9 -0
- package/dist/components/layout/SWCHeader.d.ts.map +1 -0
- package/dist/components/layout/SWCHeader.js +19 -0
- package/dist/components/layout/SWCHeader.js.map +1 -0
- package/dist/components/layout/SWCPageLayout.d.ts +9 -0
- package/dist/components/layout/SWCPageLayout.d.ts.map +1 -0
- package/dist/components/layout/SWCPageLayout.js +14 -0
- package/dist/components/layout/SWCPageLayout.js.map +1 -0
- package/dist/components/menu/ComplexMenu.js.map +1 -1
- package/dist/components/row_renderers/utils/ChipContainer.js.map +1 -1
- package/dist/components/styled/StyledPopover.js.map +1 -1
- package/dist/components/table/CsvPreview/CsvPreview.js +2 -1
- package/dist/components/table/CsvPreview/CsvPreview.js.map +1 -1
- package/dist/components/table/CsvPreview/CsvPreviewDialog.js.map +1 -1
- package/dist/components/trash/TrashCanList.js.map +1 -1
- package/dist/components/widgets/FileHandleLink.js.map +1 -1
- package/dist/components/widgets/RangeSlider/RangeSlider.js.map +1 -1
- package/dist/components/widgets/SynapseVideo.js.map +1 -1
- package/dist/components/widgets/facet-nav/FacetNavPanel.js.map +1 -1
- package/dist/components/widgets/facet-nav/PlotsContainer.js.map +1 -1
- package/dist/components/widgets/facet-nav/SelectionCriteriaPills.js.map +1 -1
- package/dist/components/widgets/facet-nav/useFacetPlots.js.map +1 -1
- package/dist/components/widgets/query-filter/CombinedRangeFacetFilter.js.map +1 -1
- package/dist/components/widgets/query-filter/EnumFacetFilter/EnumFacetFilter.js.map +1 -1
- package/dist/components/widgets/query-filter/FacetFilterControls.js.map +1 -1
- package/dist/components/widgets/query-filter/RangeFacetFilter.js.map +1 -1
- package/dist/components/widgets/query-filter/RangeFacetFilterUI.js.map +1 -1
- package/dist/features/curator/GridPage/components/GridPageTitle.d.ts.map +1 -1
- package/dist/features/curator/GridPage/components/GridPageTitle.js +23 -30
- package/dist/features/curator/GridPage/components/GridPageTitle.js.map +1 -1
- package/dist/features/curator/dashboard/CuratorDashboard.d.ts +2 -0
- package/dist/features/curator/dashboard/CuratorDashboard.d.ts.map +1 -0
- package/dist/features/curator/dashboard/CuratorDashboard.js +45 -0
- package/dist/features/curator/dashboard/CuratorDashboard.js.map +1 -0
- package/dist/features/curator/dashboard/components/CurationTaskCard.css +1 -0
- package/dist/features/curator/dashboard/components/CurationTaskCard.d.ts +9 -0
- package/dist/features/curator/dashboard/components/CurationTaskCard.d.ts.map +1 -0
- package/dist/features/curator/dashboard/components/CurationTaskCard.js +106 -0
- package/dist/features/curator/dashboard/components/CurationTaskCard.js.map +1 -0
- package/dist/features/curator/dashboard/components/CurationTaskCard.module.js +12 -0
- package/dist/features/curator/dashboard/components/CurationTaskCard.module.js.map +1 -0
- package/dist/features/curator/dashboard/components/CurationTaskCard.module.scss +52 -0
- package/dist/features/curator/dashboard/components/NextStepButton.css +1 -0
- package/dist/features/curator/dashboard/components/NextStepButton.d.ts +14 -0
- package/dist/features/curator/dashboard/components/NextStepButton.d.ts.map +1 -0
- package/dist/features/curator/dashboard/components/NextStepButton.js +35 -0
- package/dist/features/curator/dashboard/components/NextStepButton.js.map +1 -0
- package/dist/features/curator/dashboard/components/NextStepButton.module.js +11 -0
- package/dist/features/curator/dashboard/components/NextStepButton.module.js.map +1 -0
- package/dist/features/curator/dashboard/components/NextStepButton.module.scss +57 -0
- package/dist/features/curator/dashboard/components/UserOrTeamChip.css +1 -1
- package/dist/features/curator/dashboard/components/UserOrTeamChip.module.js +1 -1
- package/dist/features/curator/dashboard/components/UserOrTeamChip.module.js.map +1 -1
- package/dist/features/curator/dashboard/components/UserOrTeamChip.module.scss +5 -5
- package/dist/features/curator/dashboard/components/shared.css +1 -0
- package/dist/features/curator/dashboard/components/shared.module.js +5 -0
- package/dist/features/curator/dashboard/components/shared.module.js.map +1 -0
- package/dist/features/curator/dashboard/components/shared.module.scss +8 -0
- package/dist/features/entity/metadata-task/components/MetadataTaskTableActionCell.d.ts +0 -2
- package/dist/features/entity/metadata-task/components/MetadataTaskTableActionCell.d.ts.map +1 -1
- package/dist/features/entity/metadata-task/components/MetadataTaskTableActionCell.js +16 -34
- package/dist/features/entity/metadata-task/components/MetadataTaskTableActionCell.js.map +1 -1
- package/dist/features/entity/metadata-task/components/MetadataTasksTableAssigneeCell.js.map +1 -1
- package/dist/features/entity/metadata-task/hooks/useGetOrCreateGridSessionForSource.js.map +1 -1
- package/dist/features/entity/metadata-task/hooks/useGridSessionForCurationTask.js.map +1 -1
- package/dist/features/entity/metadata-task/hooks/useGridSessionForCurationTask_legacy.js.map +1 -1
- package/dist/features/entity/metadata-task/hooks/useMetadataTaskTable.js +1 -1
- package/dist/features/entity/metadata-task/hooks/useMetadataTaskTable.js.map +1 -1
- package/dist/features/entity/metadata-task/hooks/useOpenCuratorButton.d.ts +10 -0
- package/dist/features/entity/metadata-task/hooks/useOpenCuratorButton.d.ts.map +1 -0
- package/dist/features/entity/metadata-task/hooks/useOpenCuratorButton.js +37 -0
- package/dist/features/entity/metadata-task/hooks/useOpenCuratorButton.js.map +1 -0
- package/dist/features/entity/metadata-task/utils/constants.d.ts +5 -0
- package/dist/features/entity/metadata-task/utils/constants.d.ts.map +1 -0
- package/dist/features/entity/metadata-task/utils/constants.js +6 -0
- package/dist/features/entity/metadata-task/utils/constants.js.map +1 -0
- package/dist/mocks/challenge/mockChallenge.js.map +1 -1
- package/dist/mocks/entity/mockDataset.js.map +1 -1
- package/dist/mocks/entity/mockDatasetCollection.js.map +1 -1
- package/dist/mocks/entity/mockFileEntity.js.map +1 -1
- package/dist/mocks/entity/mockFileView.js.map +1 -1
- package/dist/mocks/entity/mockGeneratedEntityData.js.map +1 -1
- package/dist/mocks/entity/mockProject.js.map +1 -1
- package/dist/mocks/entity/mockProjectView.js.map +1 -1
- package/dist/mocks/entity/mockRootEntity.js.map +1 -1
- package/dist/mocks/entity/mockTableEntity.js.map +1 -1
- package/dist/mocks/mockWiki.js.map +1 -1
- package/dist/mocks/msw/handlers/asyncJobHandlers.js.map +1 -1
- package/dist/mocks/msw/handlers/challengeHandlers.js.map +1 -1
- package/dist/mocks/msw/handlers/changePasswordHandlers.js.map +1 -1
- package/dist/mocks/msw/handlers/discussionHandlers.js.map +1 -1
- package/dist/mocks/msw/handlers/entityHandlers.js.map +1 -1
- package/dist/mocks/msw/handlers/fileHandlers.js.map +1 -1
- package/dist/mocks/msw/handlers/gridHandlers.js.map +1 -1
- package/dist/mocks/msw/handlers/personalAccessTokenHandlers.js.map +1 -1
- package/dist/mocks/msw/handlers/subscriptionHandlers.js.map +1 -1
- package/dist/mocks/msw/handlers/teamHandlers.js.map +1 -1
- package/dist/mocks/msw/handlers/userProfileHandlers.js.map +1 -1
- package/dist/mocks/msw/handlers/wikiHandlers.js.map +1 -1
- package/dist/mocks/provenance/mockActivity.js.map +1 -1
- package/dist/mocks/query/mockReleaseCardsTableQueryResultBundle.js.map +1 -1
- package/dist/ror-client/index.js.map +1 -1
- package/dist/style/components/_cards.scss +4 -0
- package/dist/style/components/_data-grid-extra.css +1 -1
- package/dist/style/components/_data-grid-extra.scss +2 -0
- package/dist/style/main.css +1 -1
- package/dist/synapse-client/HttpClient.js.map +1 -1
- package/dist/synapse-client/SynapseClient.js.map +1 -1
- package/dist/synapse-queries/QueryMatching.test-utils.js.map +1 -1
- package/dist/synapse-queries/auth/useTwoFactorEnrollment.js.map +1 -1
- package/dist/synapse-queries/curation/task/useCurationTask.d.ts +1 -1
- package/dist/synapse-queries/curation/task/useCurationTask.d.ts.map +1 -1
- package/dist/synapse-queries/curation/task/useCurationTask.js +1 -1
- package/dist/synapse-queries/curation/task/useCurationTask.js.map +1 -1
- package/dist/synapse-queries/dataaccess/useRestrictionInformation.js.map +1 -1
- package/dist/synapse-queries/doi/useDOI.js.map +1 -1
- package/dist/synapse-queries/download/useDownloadList.js.map +1 -1
- package/dist/synapse-queries/entity/useEntity.js.map +1 -1
- package/dist/synapse-queries/entity/useEntityBundle.js.map +1 -1
- package/dist/synapse-queries/entity/useExportTableQueryToAnalysisPlatform.js.map +1 -1
- package/dist/synapse-queries/entity/useExportToTerra.js.map +1 -1
- package/dist/synapse-queries/entity/useGetQueryResultBundle.js.map +1 -1
- package/dist/synapse-queries/entity/useSchema.js.map +1 -1
- package/dist/synapse-queries/file/UploadToS3.js.map +1 -1
- package/dist/synapse-queries/file/useDirectUploadToS3.js.map +1 -1
- package/dist/synapse-queries/file/useFiles.js.map +1 -1
- package/dist/synapse-queries/forum/useReply.js.map +1 -1
- package/dist/synapse-queries/forum/useThread.js.map +1 -1
- package/dist/synapse-queries/grid/useEstablishWebsocketConnection.d.ts +2 -0
- package/dist/synapse-queries/grid/useEstablishWebsocketConnection.d.ts.map +1 -1
- package/dist/synapse-queries/grid/useEstablishWebsocketConnection.js.map +1 -1
- package/dist/synapse-queries/grid/useExportGrid.js.map +1 -1
- package/dist/synapse-queries/grid/useGridSession.js.map +1 -1
- package/dist/synapse-queries/grid/useImportCsvIntoGrid.js.map +1 -1
- package/dist/synapse-queries/subscription/useSubscription.js.map +1 -1
- package/dist/synapse-queries/table/useGetCsvPreview.js.map +1 -1
- package/dist/synapse-queries/table/useTableUpdateTransaction.js.map +1 -1
- package/dist/synapse-queries/team/useTeamMembers.js.map +1 -1
- package/dist/synapse-queries/user/useGetUserChallenges.js.map +1 -1
- package/dist/synapse-queries/user/useUserBundle.js.map +1 -1
- package/dist/synapse-queries/user/useUserGroupHeader.js.map +1 -1
- package/dist/testutils/ReactQueryMockUtils.js.map +1 -1
- package/dist/theme/ThemeProvider.js.map +1 -1
- package/dist/tsconfig.build.tsbuildinfo +1 -1
- package/dist/utils/AppUtils/session/SynapseSessionManager.js.map +1 -1
- package/dist/utils/AppUtils/session/useSessionManager.js.map +1 -1
- package/dist/utils/PermissionLevelToAccessType.js.map +1 -1
- package/dist/utils/challenge/evaluation/EvaluationUtils.js.map +1 -1
- package/dist/utils/context/SynapseContext.js.map +1 -1
- package/dist/utils/functions/AccessControlListUtils.d.ts +4 -0
- package/dist/utils/functions/AccessControlListUtils.d.ts.map +1 -1
- package/dist/utils/functions/AccessControlListUtils.js +12 -1
- package/dist/utils/functions/AccessControlListUtils.js.map +1 -1
- package/dist/utils/functions/GridApiUtils.js.map +1 -1
- package/dist/utils/functions/QueryFilterUtils.js.map +1 -1
- package/dist/utils/functions/RealmUtils.d.ts +4 -0
- package/dist/utils/functions/RealmUtils.d.ts.map +1 -1
- package/dist/utils/functions/RealmUtils.js +9 -3
- package/dist/utils/functions/RealmUtils.js.map +1 -1
- package/dist/utils/functions/SanitizeHtmlUtils.js.map +1 -1
- package/dist/utils/functions/SanitizeHtmlUtils.test-utils.js.map +1 -1
- package/dist/utils/functions/SqlFunctions.js.map +1 -1
- package/dist/utils/functions/StringUtils.js.map +1 -1
- package/dist/utils/functions/deepLinkingUtils.js.map +1 -1
- package/dist/utils/functions/getDataFromFromStorage.js.map +1 -1
- package/dist/utils/functions/getEndpoint.js.map +1 -1
- package/dist/utils/functions/getUserData.js.map +1 -1
- package/dist/utils/functions/queryUtils.js.map +1 -1
- package/dist/utils/functions/testDownloadSpeed.js.map +1 -1
- package/dist/utils/hooks/useConfirmItems.js.map +1 -1
- package/dist/utils/hooks/useCookiePreferences.js.map +1 -1
- package/dist/utils/hooks/useCreateShortUrl.js.map +1 -1
- package/dist/utils/hooks/useDetectSSOCode.js.map +1 -1
- package/dist/utils/hooks/useDirectDownloadHandler.js.map +1 -1
- package/dist/utils/hooks/useGetGoalData.js.map +1 -1
- package/dist/utils/hooks/useGetInfoFromIds.js.map +1 -1
- package/dist/utils/hooks/useImageUrlUtils.js.map +1 -1
- package/dist/utils/hooks/useImmutableTableQuery/useImmutableTableQuery.js.map +1 -1
- package/dist/utils/hooks/useImmutableTableQuery/useTableQueryReducer.js.map +1 -1
- package/dist/utils/hooks/useIsBot.js.map +1 -1
- package/dist/utils/hooks/useListState.js.map +1 -1
- package/dist/utils/hooks/useLogin.d.ts.map +1 -1
- package/dist/utils/hooks/useLogin.js +53 -52
- package/dist/utils/hooks/useLogin.js.map +1 -1
- package/dist/utils/hooks/useMutuallyExclusiveState.js.map +1 -1
- package/dist/utils/hooks/useOverlay.js.map +1 -1
- package/dist/utils/hooks/usePreFetchResource.js.map +1 -1
- package/dist/utils/hooks/useQuerySearchParam.js.map +1 -1
- package/dist/utils/hooks/useScrollFadeTransition.js.map +1 -1
- package/dist/utils/hooks/useSet.js.map +1 -1
- package/dist/utils/hooks/useSourceAppConfigs.js.map +1 -1
- package/dist/utils/hooks/useTableImageUrl.js.map +1 -1
- package/dist/utils/hooks/useUploadFileEntity/useCreatePathsAndGetParentId.js.map +1 -1
- package/dist/utils/hooks/useUploadFileEntity/useLinkFileEntityToURL.js.map +1 -1
- package/dist/utils/hooks/useUploadFileEntity/usePrepareFileEntityUpload.js.map +1 -1
- package/dist/utils/hooks/useUploadFileEntity/useTrackFileUploads.js.map +1 -1
- package/dist/utils/hooks/useUploadFileEntity/useUploadFileEntities.js.map +1 -1
- package/dist/utils/hooks/useUploadFileEntity/useUploadFiles.js.map +1 -1
- package/dist/utils/hooks/useUploadFileEntity/willUploadsExceedStorageLimit.js.map +1 -1
- package/dist/utils/html/TargetEnum.js.map +1 -1
- package/dist/utils/jsonschema/SchemaAnnotationUtils.js.map +1 -1
- package/package.json +4 -4
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"TwoFactorBackupCodes.js","names":[],"sources":["../../../src/components/Authentication/TwoFactorBackupCodes.tsx"],"sourcesContent":["import {\n LeftRightPanel,\n StyledOuterContainer,\n} from '@/components/styled/LeftRightPanel'\nimport { createRecoveryCodes } from '@/synapse-client/SynapseClient'\nimport { useSynapseContext } from '@/utils/context/SynapseContext'\nimport { useMediaPrintOnly } from '@/utils/hooks/useMediaPrintOnly'\nimport {\n Alert,\n Box,\n Button,\n IconButton,\n Typography,\n useTheme,\n} from '@mui/material'\nimport { SynapseClientError } from '@sage-bionetworks/synapse-client/util/SynapseClientError'\nimport { TwoFactorAuthRecoveryCodes } from '@sage-bionetworks/synapse-types'\nimport { useMutation } from '@tanstack/react-query'\nimport { useCallback, useEffect, useState } from 'react'\nimport IconSvg from '../IconSvg/IconSvg'\nimport { displayToast } from '../ToastMessage/ToastMessage'\nimport { RecoveryCodeGrid } from './RecoveryCodeGrid'\nimport { RegenerateBackupCodesWarning } from './RegenerateBackupCodesWarning'\nimport { copyStringToClipboard } from '@/utils/functions/StringUtils'\n\nexport type TwoFactorBackupCodesProps = {\n /* Whether to show a warning before generating new codes, to prevent users from overwriting their existing codes */\n showReplaceOldCodesWarning?: boolean\n /* Invoked when the user decides not to generate new codes, or has acknowledged new codes. */\n onClose: () => void\n}\n\nexport default function TwoFactorBackupCodes(props: TwoFactorBackupCodesProps) {\n const { showReplaceOldCodesWarning, onClose } = props\n const theme = useTheme()\n const [hasConfirmedRegeneration, setHasConfirmedRegeneration] = useState(\n !showReplaceOldCodesWarning,\n )\n\n const { accessToken } = useSynapseContext()\n\n const {\n mutate: generateCodes,\n data: recoveryCodes,\n error,\n } = useMutation<TwoFactorAuthRecoveryCodes, SynapseClientError>({\n mutationFn: () => createRecoveryCodes(accessToken),\n onMutate: () => {\n setHasConfirmedRegeneration(true)\n },\n })\n\n // Generate codes on mount if no warning is shown\n useEffect(() => {\n if (!showReplaceOldCodesWarning) {\n generateCodes()\n }\n // Run on mount only\n // eslint-disable-next-line react-hooks/exhaustive-deps\n }, [])\n\n const { visibleOnPrintClassName, hiddenOnPrintClassName } =\n useMediaPrintOnly()\n\n const copyCodesToClipboard = useCallback(() => {\n const codes = (recoveryCodes?.codes || []).join('\\n')\n copyStringToClipboard(codes).then(() => {\n displayToast('Recovery codes copied to clipboard', 'success')\n })\n }, [recoveryCodes])\n\n if (showReplaceOldCodesWarning && !hasConfirmedRegeneration) {\n return (\n <StyledOuterContainer sx={{ display: 'flex', flexDirection: 'column' }}>\n <RegenerateBackupCodesWarning\n onConfirm={generateCodes}\n onCancel={onClose}\n />\n </StyledOuterContainer>\n )\n }\n\n return (\n <LeftRightPanel\n className={visibleOnPrintClassName}\n leftContent={\n <Box\n sx={{\n py: 10,\n px: 8,\n height: '100%',\n position: 'relative',\n }}\n >\n <IconButton\n type=\"button\"\n onClick={onClose}\n className={hiddenOnPrintClassName}\n sx={{\n position: 'absolute',\n top: theme.spacing(2),\n left: theme.spacing(2),\n }}\n >\n <IconSvg\n icon=\"arrowBack\"\n wrap={false}\n sx={{ height: '24px', width: '24px' }}\n />\n </IconButton>\n <Typography variant=\"headline1\" sx={{ mt: 7 }}>\n Backup codes\n </Typography>\n {showReplaceOldCodesWarning && (\n <Alert\n severity=\"warning\"\n className={hiddenOnPrintClassName}\n sx={{\n my: 2,\n }}\n icon={false}\n >\n <strong>These new codes have replaced your old codes.</strong>{' '}\n Please save these codes and use them from now on.\n </Alert>\n )}\n <RecoveryCodeGrid recoveryCodes={recoveryCodes} />\n {error && (\n <Alert severity={'error'} sx={{ my: 2 }} icon={false}>\n {error.message}\n </Alert>\n )}\n <Box\n className={hiddenOnPrintClassName}\n sx={{\n display: 'grid',\n gridTemplateColumns: '1fr 1fr',\n gap: '10px',\n mb: '10px',\n }}\n >\n <Button\n variant={'outlined'}\n onClick={copyCodesToClipboard}\n disabled={!recoveryCodes}\n >\n Copy Codes\n </Button>\n <Button\n variant={'outlined'}\n disabled={!recoveryCodes}\n onClick={window.print}\n >\n Print Codes\n </Button>\n </Box>\n <Button\n variant={'contained'}\n className={hiddenOnPrintClassName}\n fullWidth\n disabled={!recoveryCodes}\n onClick={onClose}\n >\n Done\n </Button>\n </Box>\n }\n rightContent={\n <Box\n sx={{\n py: 10,\n height: '100%',\n background: `url(https://s3.amazonaws.com/static.synapse.org/images/twofactor-graphic.svg) no-repeat right bottom`,\n backgroundSize: '100%',\n }}\n >\n <Box\n sx={{\n mt: 7,\n px: 9,\n }}\n >\n <Typography variant=\"headline1\" sx={{ mb: 2.5 }}>\n Save these codes\n </Typography>\n <Typography variant=\"body1\" sx={{ my: 2 }}>\n You can use these 10 backup codes to access your account if you\n ever lose access to your authentication device.\n </Typography>\n <Typography variant=\"body1\" sx={{ my: 2 }}>\n Each code can only be used once.\n </Typography>\n </Box>\n </Box>\n }\n />\n )\n}\n"],"mappings":";;;;;;;;;;;;;;AAgCA,SAAwB,EAAqB,GAAkC;CAC7E,IAAM,EAAE,+BAA4B,eAAY,GAC1C,IAAQ,GAAU,EAClB,CAAC,GAA0B,KAA+B,EAC9D,CAAC,EACF,EAEK,EAAE,mBAAgB,GAAmB,EAErC,EACJ,QAAQ,GACR,MAAM,GACN,aACE,EAA4D;EAC9D,kBAAkB,EAAoB,EAAY;EAClD,gBAAgB;AACd,KAA4B,GAAK;;EAEpC,CAAC;AAGF,SAAgB;AACd,EAAK,KACH,GAAe;IAIhB,EAAE,CAAC;CAEN,IAAM,EAAE,4BAAyB,8BAC/B,GAAmB,EAEf,IAAuB,QAAkB;AAE7C,KADe,GAAe,SAAS,EAAE,EAAE,KAAK,
|
|
1
|
+
{"version":3,"file":"TwoFactorBackupCodes.js","names":[],"sources":["../../../src/components/Authentication/TwoFactorBackupCodes.tsx"],"sourcesContent":["import {\n LeftRightPanel,\n StyledOuterContainer,\n} from '@/components/styled/LeftRightPanel'\nimport { createRecoveryCodes } from '@/synapse-client/SynapseClient'\nimport { useSynapseContext } from '@/utils/context/SynapseContext'\nimport { useMediaPrintOnly } from '@/utils/hooks/useMediaPrintOnly'\nimport {\n Alert,\n Box,\n Button,\n IconButton,\n Typography,\n useTheme,\n} from '@mui/material'\nimport { SynapseClientError } from '@sage-bionetworks/synapse-client/util/SynapseClientError'\nimport { TwoFactorAuthRecoveryCodes } from '@sage-bionetworks/synapse-types'\nimport { useMutation } from '@tanstack/react-query'\nimport { useCallback, useEffect, useState } from 'react'\nimport IconSvg from '../IconSvg/IconSvg'\nimport { displayToast } from '../ToastMessage/ToastMessage'\nimport { RecoveryCodeGrid } from './RecoveryCodeGrid'\nimport { RegenerateBackupCodesWarning } from './RegenerateBackupCodesWarning'\nimport { copyStringToClipboard } from '@/utils/functions/StringUtils'\n\nexport type TwoFactorBackupCodesProps = {\n /* Whether to show a warning before generating new codes, to prevent users from overwriting their existing codes */\n showReplaceOldCodesWarning?: boolean\n /* Invoked when the user decides not to generate new codes, or has acknowledged new codes. */\n onClose: () => void\n}\n\nexport default function TwoFactorBackupCodes(props: TwoFactorBackupCodesProps) {\n const { showReplaceOldCodesWarning, onClose } = props\n const theme = useTheme()\n const [hasConfirmedRegeneration, setHasConfirmedRegeneration] = useState(\n !showReplaceOldCodesWarning,\n )\n\n const { accessToken } = useSynapseContext()\n\n const {\n mutate: generateCodes,\n data: recoveryCodes,\n error,\n } = useMutation<TwoFactorAuthRecoveryCodes, SynapseClientError>({\n mutationFn: () => createRecoveryCodes(accessToken),\n onMutate: () => {\n setHasConfirmedRegeneration(true)\n },\n })\n\n // Generate codes on mount if no warning is shown\n useEffect(() => {\n if (!showReplaceOldCodesWarning) {\n generateCodes()\n }\n // Run on mount only\n // eslint-disable-next-line react-hooks/exhaustive-deps\n }, [])\n\n const { visibleOnPrintClassName, hiddenOnPrintClassName } =\n useMediaPrintOnly()\n\n const copyCodesToClipboard = useCallback(() => {\n const codes = (recoveryCodes?.codes || []).join('\\n')\n copyStringToClipboard(codes).then(() => {\n displayToast('Recovery codes copied to clipboard', 'success')\n })\n }, [recoveryCodes])\n\n if (showReplaceOldCodesWarning && !hasConfirmedRegeneration) {\n return (\n <StyledOuterContainer sx={{ display: 'flex', flexDirection: 'column' }}>\n <RegenerateBackupCodesWarning\n onConfirm={generateCodes}\n onCancel={onClose}\n />\n </StyledOuterContainer>\n )\n }\n\n return (\n <LeftRightPanel\n className={visibleOnPrintClassName}\n leftContent={\n <Box\n sx={{\n py: 10,\n px: 8,\n height: '100%',\n position: 'relative',\n }}\n >\n <IconButton\n type=\"button\"\n onClick={onClose}\n className={hiddenOnPrintClassName}\n sx={{\n position: 'absolute',\n top: theme.spacing(2),\n left: theme.spacing(2),\n }}\n >\n <IconSvg\n icon=\"arrowBack\"\n wrap={false}\n sx={{ height: '24px', width: '24px' }}\n />\n </IconButton>\n <Typography variant=\"headline1\" sx={{ mt: 7 }}>\n Backup codes\n </Typography>\n {showReplaceOldCodesWarning && (\n <Alert\n severity=\"warning\"\n className={hiddenOnPrintClassName}\n sx={{\n my: 2,\n }}\n icon={false}\n >\n <strong>These new codes have replaced your old codes.</strong>{' '}\n Please save these codes and use them from now on.\n </Alert>\n )}\n <RecoveryCodeGrid recoveryCodes={recoveryCodes} />\n {error && (\n <Alert severity={'error'} sx={{ my: 2 }} icon={false}>\n {error.message}\n </Alert>\n )}\n <Box\n className={hiddenOnPrintClassName}\n sx={{\n display: 'grid',\n gridTemplateColumns: '1fr 1fr',\n gap: '10px',\n mb: '10px',\n }}\n >\n <Button\n variant={'outlined'}\n onClick={copyCodesToClipboard}\n disabled={!recoveryCodes}\n >\n Copy Codes\n </Button>\n <Button\n variant={'outlined'}\n disabled={!recoveryCodes}\n onClick={window.print}\n >\n Print Codes\n </Button>\n </Box>\n <Button\n variant={'contained'}\n className={hiddenOnPrintClassName}\n fullWidth\n disabled={!recoveryCodes}\n onClick={onClose}\n >\n Done\n </Button>\n </Box>\n }\n rightContent={\n <Box\n sx={{\n py: 10,\n height: '100%',\n background: `url(https://s3.amazonaws.com/static.synapse.org/images/twofactor-graphic.svg) no-repeat right bottom`,\n backgroundSize: '100%',\n }}\n >\n <Box\n sx={{\n mt: 7,\n px: 9,\n }}\n >\n <Typography variant=\"headline1\" sx={{ mb: 2.5 }}>\n Save these codes\n </Typography>\n <Typography variant=\"body1\" sx={{ my: 2 }}>\n You can use these 10 backup codes to access your account if you\n ever lose access to your authentication device.\n </Typography>\n <Typography variant=\"body1\" sx={{ my: 2 }}>\n Each code can only be used once.\n </Typography>\n </Box>\n </Box>\n }\n />\n )\n}\n"],"mappings":";;;;;;;;;;;;;;AAgCA,SAAwB,EAAqB,GAAkC;CAC7E,IAAM,EAAE,+BAA4B,eAAY,GAC1C,IAAQ,GAAU,EAClB,CAAC,GAA0B,KAA+B,EAC9D,CAAC,EACF,EAEK,EAAE,mBAAgB,GAAmB,EAErC,EACJ,QAAQ,GACR,MAAM,GACN,aACE,EAA4D;EAC9D,kBAAkB,EAAoB,EAAY;EAClD,gBAAgB;AACd,KAA4B,GAAK;;EAEpC,CAAC;AAGF,SAAgB;AACd,EAAK,KACH,GAAe;IAIhB,EAAE,CAAC;CAEN,IAAM,EAAE,4BAAyB,8BAC/B,GAAmB,EAEf,IAAuB,QAAkB;AAE7C,KADe,GAAe,SAAS,EAAE,EAAE,KAAK,KAC1B,CAAM,CAAC,WAAW;AACtC,KAAa,sCAAsC,UAAU;IAC7D;IACD,CAAC,EAAc,CAAC;AAanB,QAXI,KAA8B,CAAC,IAE/B,kBAAC,GAAD;EAAsB,IAAI;GAAE,SAAS;GAAQ,eAAe;GAAU;YACpE,kBAAC,GAAD;GACE,WAAW;GACX,UAAU;GACV,CAAA;EACmB,CAAA,GAKzB,kBAAC,GAAD;EACE,WAAW;EACX,aACE,kBAAC,GAAD;GACE,IAAI;IACF,IAAI;IACJ,IAAI;IACJ,QAAQ;IACR,UAAU;IACX;aANH;IAQE,kBAAC,GAAD;KACE,MAAK;KACL,SAAS;KACT,WAAW;KACX,IAAI;MACF,UAAU;MACV,KAAK,EAAM,QAAQ,EAAE;MACrB,MAAM,EAAM,QAAQ,EAAE;MACvB;eAED,kBAAC,GAAD;MACE,MAAK;MACL,MAAM;MACN,IAAI;OAAE,QAAQ;OAAQ,OAAO;OAAQ;MACrC,CAAA;KACS,CAAA;IACb,kBAAC,GAAD;KAAY,SAAQ;KAAY,IAAI,EAAE,IAAI,GAAG;eAAE;KAElC,CAAA;IACZ,KACC,kBAAC,GAAD;KACE,UAAS;KACT,WAAW;KACX,IAAI,EACF,IAAI,GACL;KACD,MAAM;eANR;MAQE,kBAAC,UAAD,EAAA,UAAQ,iDAAsD,CAAA;MAAC;MAAI;MAE7D;;IAEV,kBAAC,GAAD,EAAiC,kBAAiB,CAAA;IACjD,KACC,kBAAC,GAAD;KAAO,UAAU;KAAS,IAAI,EAAE,IAAI,GAAG;KAAE,MAAM;eAC5C,EAAM;KACD,CAAA;IAEV,kBAAC,GAAD;KACE,WAAW;KACX,IAAI;MACF,SAAS;MACT,qBAAqB;MACrB,KAAK;MACL,IAAI;MACL;eAPH,CASE,kBAAC,GAAD;MACE,SAAS;MACT,SAAS;MACT,UAAU,CAAC;gBACZ;MAEQ,CAAA,EACT,kBAAC,GAAD;MACE,SAAS;MACT,UAAU,CAAC;MACX,SAAS,OAAO;gBACjB;MAEQ,CAAA,CACL;;IACN,kBAAC,GAAD;KACE,SAAS;KACT,WAAW;KACX,WAAA;KACA,UAAU,CAAC;KACX,SAAS;eACV;KAEQ,CAAA;IACL;;EAER,cACE,kBAAC,GAAD;GACE,IAAI;IACF,IAAI;IACJ,QAAQ;IACR,YAAY;IACZ,gBAAgB;IACjB;aAED,kBAAC,GAAD;IACE,IAAI;KACF,IAAI;KACJ,IAAI;KACL;cAJH;KAME,kBAAC,GAAD;MAAY,SAAQ;MAAY,IAAI,EAAE,IAAI,KAAK;gBAAE;MAEpC,CAAA;KACb,kBAAC,GAAD;MAAY,SAAQ;MAAQ,IAAI,EAAE,IAAI,GAAG;gBAAE;MAG9B,CAAA;KACb,kBAAC,GAAD;MAAY,SAAQ;MAAQ,IAAI,EAAE,IAAI,GAAG;gBAAE;MAE9B,CAAA;KACT;;GACF,CAAA;EAER,CAAA"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"TwoFactorEnrollmentForm.d.ts","sourceRoot":"","sources":["../../../src/components/Authentication/TwoFactorEnrollmentForm.tsx"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"TwoFactorEnrollmentForm.d.ts","sourceRoot":"","sources":["../../../src/components/Authentication/TwoFactorEnrollmentForm.tsx"],"names":[],"mappings":"AAgBA,OAAO,EAAE,UAAU,EAAE,MAAM,iCAAiC,CAAA;AAW5D;;;;GAIG;AACH,iBAAS,YAAY,CAAC,MAAM,EAAE,UAAU,UAEvC;AA6BD,eAAO,MAAM,oBAAoB,+HAC6F,CAAA;AAE9H,MAAM,MAAM,4BAA4B,GAAG;IACzC,UAAU,EAAE,UAAU,CAAA;IACtB,4BAA4B,EAAE,MAAM,IAAI,CAAA;IACxC,aAAa,EAAE,MAAM,IAAI,CAAA;CAC1B,CAAA;AAED,MAAM,CAAC,OAAO,UAAU,uBAAuB,CAC7C,KAAK,EAAE,4BAA4B,2CAiOpC;AAED,eAAO,MAAM,yBAAyB;;CAAmB,CAAA"}
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import e from "../FullWidthAlert/FullWidthAlert.js";
|
|
2
|
+
import "./Constants.js";
|
|
2
3
|
import { SynapseSpinner as t } from "../LoadingScreen/LoadingScreen.js";
|
|
3
4
|
import { useFinishTwoFactorEnrollment as n } from "../../synapse-queries/auth/useTwoFactorEnrollment.js";
|
|
4
5
|
import r from "../IconSvg/IconSvg.js";
|
|
@@ -206,7 +207,7 @@ function D(v) {
|
|
|
206
207
|
R && /* @__PURE__ */ y(e, {
|
|
207
208
|
variant: "danger",
|
|
208
209
|
isGlobal: !1,
|
|
209
|
-
description: R.reason
|
|
210
|
+
description: R.reason.toLowerCase().includes("invalid totp code") ? `${R.reason}. Please ensure automatic date and time is enabled in your device settings and try again.` : R.reason
|
|
210
211
|
})
|
|
211
212
|
]
|
|
212
213
|
}) });
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"TwoFactorEnrollmentForm.js","names":[],"sources":["../../../src/components/Authentication/TwoFactorEnrollmentForm.tsx"],"sourcesContent":["import { StyledOuterContainer } from '@/components/styled/LeftRightPanel'\nimport { useFinishTwoFactorEnrollment } from '@/synapse-queries/auth/useTwoFactorEnrollment'\nimport { StyledComponent } from '@emotion/styled'\nimport {\n Box,\n BoxProps,\n Button,\n Divider,\n IconButton,\n Link,\n Paper,\n Stack,\n styled,\n Typography,\n} from '@mui/material'\nimport { TotpSecret } from '@sage-bionetworks/synapse-types'\nimport * as qrcode from 'qrcode'\n// qrcode is CJS-only; use namespace import for Vite dev mode CJS interop.\nconst { toCanvas } = qrcode\nimport { useEffect, useRef, useState } from 'react'\nimport FullWidthAlert from '../FullWidthAlert/FullWidthAlert'\nimport IconSvg from '../IconSvg/IconSvg'\nimport { SynapseSpinner } from '../LoadingScreen/LoadingScreen'\nimport TextField from '../TextField/TextField'\nimport TwoFactorSecretDialog from './TwoFactorSecretDialog'\n\n/**\n * Returns a URL that can be used to generate a QR code that 2FA authenticator apps can interpret.\n * This uses the username fro TotpSecret to create the friendly name for the account in the authenticator app.\n * @param secret\n */\nfunction toOtpAuthUrl(secret: TotpSecret) {\n return `otpauth://totp/Synapse:${secret.username}?secret=${secret.secret}&issuer=Sage%20Bionetworks&algorithm=${secret.alg}&digits=${secret.digits}&period=${secret.period}`\n}\n\nconst Section: StyledComponent<BoxProps> = styled(\n (props: BoxProps) => (\n <Box\n sx={{\n my: 3,\n ...props.sx,\n }}\n {...props}\n />\n ),\n {\n label: 'Section',\n },\n)(() => ({}))\n\nconst SectionInnerGrid = styled(Box, { label: 'SectionInnerGrid' })(\n ({ theme }) => ({\n display: 'grid',\n gridTemplateColumns: 'auto 200px',\n columnGap: theme.spacing(5),\n [theme.breakpoints.down('sm')]: {\n gridTemplateColumns: '1fr',\n gap: '18px',\n },\n }),\n)\n\nexport const TWO_FACTOR_DOCS_LINK =\n 'https://help.synapse.org/docs/Managing-Your-Account.2055405596.html#Adding-Two-Factor-Authentication-(2FA)-to-your-account'\n\nexport type TwoFactorEnrollmentFormProps = {\n totpSecret: TotpSecret\n onTwoFactorEnrollmentSuccess: () => void\n onBackClicked: () => void\n}\n\nexport default function TwoFactorEnrollmentForm(\n props: TwoFactorEnrollmentFormProps,\n) {\n const { onTwoFactorEnrollmentSuccess, onBackClicked, totpSecret } = props\n\n const [totp, setTotp] = useState('')\n const [hasQrCode, setHasQrCode] = useState(false)\n const [showSecretInModal, setShowSecretInModal] = useState(false)\n const qrCodeCanvasElement = useRef<HTMLCanvasElement>(null)\n\n const {\n mutate: finishEnrollment,\n isPending: isFinishingEnrollment,\n error,\n } = useFinishTwoFactorEnrollment({\n onSuccess: onTwoFactorEnrollmentSuccess,\n })\n\n useEffect(() => {\n async function createQrCode() {\n if (totpSecret && qrCodeCanvasElement.current) {\n await toCanvas(qrCodeCanvasElement.current, toOtpAuthUrl(totpSecret), {\n version: 10,\n margin: 0,\n scale: 3.5,\n })\n setHasQrCode(true)\n }\n }\n void createQrCode()\n }, [totpSecret])\n\n return (\n <StyledOuterContainer>\n <Paper\n sx={{\n position: 'relative',\n width: { xs: '100%', md: '800px' },\n py: 6.5,\n px: 8,\n mx: 'auto',\n }}\n >\n {onBackClicked && (\n <IconButton\n type=\"button\"\n onClick={() => {\n onBackClicked()\n }}\n sx={theme => ({\n position: 'absolute',\n top: theme.spacing(2),\n left: theme.spacing(2),\n })}\n >\n <IconSvg\n icon=\"arrowBack\"\n wrap={false}\n sx={{ height: '24px', width: '24px' }}\n />\n </IconButton>\n )}\n <Section>\n <Typography variant=\"headline2\" sx={{ mb: 3 }}>\n Activate Two-factor Authentication\n </Typography>\n <Typography variant=\"body1\">\n After setting up 2FA, you’ll use an authenticator app as part of\n your login process, in addition to your existing password. If you\n log in using your Google\n {/* eslint-disable-next-line no-constant-binary-expression -- Remove conditionality when NIH RAS login is supported */}\n {false && ', NIH RAS,'} or ORCiD account, you may need to use 2FA as\n part of\n {/*those processes*/}\n that process as well.{' '}\n <Link href={TWO_FACTOR_DOCS_LINK}>Learn more about 2FA</Link>.\n </Typography>\n </Section>\n <Section>\n <SectionInnerGrid>\n <Box>\n <Typography variant=\"body1\" sx={{ fontWeight: 700 }}>\n Step 1. Get one of these apps\n </Typography>\n <Typography variant=\"body1\">\n Download and install one of these authentication apps to set up\n 2FA on your mobile device, tablet, or desktop. If you have one\n of these apps already installed, you can skip this step.\n </Typography>\n </Box>\n <Stack\n sx={theme => ({\n justifyContent: 'space-between',\n flexShrink: 0,\n [theme.breakpoints.down('sm')]: {\n gap: '20px',\n },\n })}\n >\n <Link href={'https://authy.com/download/'}>Authy</Link>\n <Link\n href={\n 'https://duo.com/product/multi-factor-authentication-mfa/duo-mobile-app'\n }\n >\n DUO Mobile\n </Link>\n <Link href={'https://googleauthenticator.net/'}>\n Google Authenticator\n </Link>\n <Link\n href={\n 'https://www.microsoft.com/en-us/security/mobile-authenticator-app'\n }\n >\n Microsoft Authenticator\n </Link>\n </Stack>\n </SectionInnerGrid>\n </Section>\n <Divider />\n <Section>\n <SectionInnerGrid>\n <Box>\n <Typography variant=\"body1\" sx={{ fontWeight: 700 }}>\n Step 2. Scan the QR Code\n </Typography>\n <Typography component=\"ol\" variant=\"body1\" sx={{ pl: 2, my: 1 }}>\n <li>Open your authenticator app</li>\n <li>Tap the "+" button</li>\n <li>Use your camera to scan the image to the right</li>\n </Typography>\n <Link\n onClick={() => {\n setShowSecretInModal(true)\n }}\n >\n Can't use your camera?\n </Link>\n <TwoFactorSecretDialog\n secret={totpSecret?.secret ?? ''}\n open={showSecretInModal}\n onClose={() => setShowSecretInModal(false)}\n />\n </Box>\n <Box\n sx={{\n display: 'flex',\n justifyContent: 'center',\n alignItems: 'middle',\n height: 'auto',\n }}\n >\n {!hasQrCode && <SynapseSpinner size={50} />}\n <canvas\n style={{ display: hasQrCode ? undefined : 'none' }}\n ref={qrCodeCanvasElement}\n />\n </Box>\n </SectionInnerGrid>\n </Section>\n <Divider />\n <Section>\n <SectionInnerGrid>\n <Box>\n <Typography variant=\"body1\" sx={{ fontWeight: 700 }}>\n Step 3. Enter the Verification Code\n </Typography>\n <Typography variant=\"body1\">\n After scanning in the QR code in step 2, your app will provide a\n 6-digit code. Enter it in in the field to the right.\n </Typography>\n </Box>\n <form\n onSubmit={e => {\n e.preventDefault()\n finishEnrollment({\n secretId: totpSecret.secretId,\n totp,\n })\n }}\n >\n <Stack\n direction={'row'}\n sx={{\n gap: 2,\n height: '48px',\n }}\n >\n <TextField\n noWrapInFormControl\n autoFocus\n inputProps={{ maxLength: totpSecret?.digits }}\n value={totp}\n onChange={e => {\n setTotp(e.target.value)\n }}\n />\n <Button\n type={'submit'}\n variant={'contained'}\n sx={{ flexShrink: 0 }}\n disabled={\n isFinishingEnrollment || totp.length != totpSecret?.digits\n }\n >\n Activate\n </Button>\n </Stack>\n </form>\n </SectionInnerGrid>\n </Section>\n {error && (\n <FullWidthAlert\n variant={'danger'}\n isGlobal={false}\n description={error.reason}\n />\n )}\n </Paper>\n </StyledOuterContainer>\n )\n}\n\nexport const EXPORTED_FOR_UNIT_TESTING = { toOtpAuthUrl }\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"TwoFactorEnrollmentForm.js","names":[],"sources":["../../../src/components/Authentication/TwoFactorEnrollmentForm.tsx"],"sourcesContent":["import { StyledOuterContainer } from '@/components/styled/LeftRightPanel'\nimport { TOTP_CLOCK_SKEW_ERROR_APPENDAGE } from '@/components/Authentication/Constants'\nimport { useFinishTwoFactorEnrollment } from '@/synapse-queries/auth/useTwoFactorEnrollment'\nimport { StyledComponent } from '@emotion/styled'\nimport {\n Box,\n BoxProps,\n Button,\n Divider,\n IconButton,\n Link,\n Paper,\n Stack,\n styled,\n Typography,\n} from '@mui/material'\nimport { TotpSecret } from '@sage-bionetworks/synapse-types'\nimport * as qrcode from 'qrcode'\n// qrcode is CJS-only; use namespace import for Vite dev mode CJS interop.\nconst { toCanvas } = qrcode\nimport { useEffect, useRef, useState } from 'react'\nimport FullWidthAlert from '../FullWidthAlert/FullWidthAlert'\nimport IconSvg from '../IconSvg/IconSvg'\nimport { SynapseSpinner } from '../LoadingScreen/LoadingScreen'\nimport TextField from '../TextField/TextField'\nimport TwoFactorSecretDialog from './TwoFactorSecretDialog'\n\n/**\n * Returns a URL that can be used to generate a QR code that 2FA authenticator apps can interpret.\n * This uses the username fro TotpSecret to create the friendly name for the account in the authenticator app.\n * @param secret\n */\nfunction toOtpAuthUrl(secret: TotpSecret) {\n return `otpauth://totp/Synapse:${secret.username}?secret=${secret.secret}&issuer=Sage%20Bionetworks&algorithm=${secret.alg}&digits=${secret.digits}&period=${secret.period}`\n}\n\nconst Section: StyledComponent<BoxProps> = styled(\n (props: BoxProps) => (\n <Box\n sx={{\n my: 3,\n ...props.sx,\n }}\n {...props}\n />\n ),\n {\n label: 'Section',\n },\n)(() => ({}))\n\nconst SectionInnerGrid = styled(Box, { label: 'SectionInnerGrid' })(\n ({ theme }) => ({\n display: 'grid',\n gridTemplateColumns: 'auto 200px',\n columnGap: theme.spacing(5),\n [theme.breakpoints.down('sm')]: {\n gridTemplateColumns: '1fr',\n gap: '18px',\n },\n }),\n)\n\nexport const TWO_FACTOR_DOCS_LINK =\n 'https://help.synapse.org/docs/Managing-Your-Account.2055405596.html#Adding-Two-Factor-Authentication-(2FA)-to-your-account'\n\nexport type TwoFactorEnrollmentFormProps = {\n totpSecret: TotpSecret\n onTwoFactorEnrollmentSuccess: () => void\n onBackClicked: () => void\n}\n\nexport default function TwoFactorEnrollmentForm(\n props: TwoFactorEnrollmentFormProps,\n) {\n const { onTwoFactorEnrollmentSuccess, onBackClicked, totpSecret } = props\n\n const [totp, setTotp] = useState('')\n const [hasQrCode, setHasQrCode] = useState(false)\n const [showSecretInModal, setShowSecretInModal] = useState(false)\n const qrCodeCanvasElement = useRef<HTMLCanvasElement>(null)\n\n const {\n mutate: finishEnrollment,\n isPending: isFinishingEnrollment,\n error,\n } = useFinishTwoFactorEnrollment({\n onSuccess: onTwoFactorEnrollmentSuccess,\n })\n\n useEffect(() => {\n async function createQrCode() {\n if (totpSecret && qrCodeCanvasElement.current) {\n await toCanvas(qrCodeCanvasElement.current, toOtpAuthUrl(totpSecret), {\n version: 10,\n margin: 0,\n scale: 3.5,\n })\n setHasQrCode(true)\n }\n }\n void createQrCode()\n }, [totpSecret])\n\n return (\n <StyledOuterContainer>\n <Paper\n sx={{\n position: 'relative',\n width: { xs: '100%', md: '800px' },\n py: 6.5,\n px: 8,\n mx: 'auto',\n }}\n >\n {onBackClicked && (\n <IconButton\n type=\"button\"\n onClick={() => {\n onBackClicked()\n }}\n sx={theme => ({\n position: 'absolute',\n top: theme.spacing(2),\n left: theme.spacing(2),\n })}\n >\n <IconSvg\n icon=\"arrowBack\"\n wrap={false}\n sx={{ height: '24px', width: '24px' }}\n />\n </IconButton>\n )}\n <Section>\n <Typography variant=\"headline2\" sx={{ mb: 3 }}>\n Activate Two-factor Authentication\n </Typography>\n <Typography variant=\"body1\">\n After setting up 2FA, you’ll use an authenticator app as part of\n your login process, in addition to your existing password. If you\n log in using your Google\n {/* eslint-disable-next-line no-constant-binary-expression -- Remove conditionality when NIH RAS login is supported */}\n {false && ', NIH RAS,'} or ORCiD account, you may need to use 2FA as\n part of\n {/*those processes*/}\n that process as well.{' '}\n <Link href={TWO_FACTOR_DOCS_LINK}>Learn more about 2FA</Link>.\n </Typography>\n </Section>\n <Section>\n <SectionInnerGrid>\n <Box>\n <Typography variant=\"body1\" sx={{ fontWeight: 700 }}>\n Step 1. Get one of these apps\n </Typography>\n <Typography variant=\"body1\">\n Download and install one of these authentication apps to set up\n 2FA on your mobile device, tablet, or desktop. If you have one\n of these apps already installed, you can skip this step.\n </Typography>\n </Box>\n <Stack\n sx={theme => ({\n justifyContent: 'space-between',\n flexShrink: 0,\n [theme.breakpoints.down('sm')]: {\n gap: '20px',\n },\n })}\n >\n <Link href={'https://authy.com/download/'}>Authy</Link>\n <Link\n href={\n 'https://duo.com/product/multi-factor-authentication-mfa/duo-mobile-app'\n }\n >\n DUO Mobile\n </Link>\n <Link href={'https://googleauthenticator.net/'}>\n Google Authenticator\n </Link>\n <Link\n href={\n 'https://www.microsoft.com/en-us/security/mobile-authenticator-app'\n }\n >\n Microsoft Authenticator\n </Link>\n </Stack>\n </SectionInnerGrid>\n </Section>\n <Divider />\n <Section>\n <SectionInnerGrid>\n <Box>\n <Typography variant=\"body1\" sx={{ fontWeight: 700 }}>\n Step 2. Scan the QR Code\n </Typography>\n <Typography component=\"ol\" variant=\"body1\" sx={{ pl: 2, my: 1 }}>\n <li>Open your authenticator app</li>\n <li>Tap the "+" button</li>\n <li>Use your camera to scan the image to the right</li>\n </Typography>\n <Link\n onClick={() => {\n setShowSecretInModal(true)\n }}\n >\n Can't use your camera?\n </Link>\n <TwoFactorSecretDialog\n secret={totpSecret?.secret ?? ''}\n open={showSecretInModal}\n onClose={() => setShowSecretInModal(false)}\n />\n </Box>\n <Box\n sx={{\n display: 'flex',\n justifyContent: 'center',\n alignItems: 'middle',\n height: 'auto',\n }}\n >\n {!hasQrCode && <SynapseSpinner size={50} />}\n <canvas\n style={{ display: hasQrCode ? undefined : 'none' }}\n ref={qrCodeCanvasElement}\n />\n </Box>\n </SectionInnerGrid>\n </Section>\n <Divider />\n <Section>\n <SectionInnerGrid>\n <Box>\n <Typography variant=\"body1\" sx={{ fontWeight: 700 }}>\n Step 3. Enter the Verification Code\n </Typography>\n <Typography variant=\"body1\">\n After scanning in the QR code in step 2, your app will provide a\n 6-digit code. Enter it in in the field to the right.\n </Typography>\n </Box>\n <form\n onSubmit={e => {\n e.preventDefault()\n finishEnrollment({\n secretId: totpSecret.secretId,\n totp,\n })\n }}\n >\n <Stack\n direction={'row'}\n sx={{\n gap: 2,\n height: '48px',\n }}\n >\n <TextField\n noWrapInFormControl\n autoFocus\n inputProps={{ maxLength: totpSecret?.digits }}\n value={totp}\n onChange={e => {\n setTotp(e.target.value)\n }}\n />\n <Button\n type={'submit'}\n variant={'contained'}\n sx={{ flexShrink: 0 }}\n disabled={\n isFinishingEnrollment || totp.length != totpSecret?.digits\n }\n >\n Activate\n </Button>\n </Stack>\n </form>\n </SectionInnerGrid>\n </Section>\n {error && (\n <FullWidthAlert\n variant={'danger'}\n isGlobal={false}\n description={\n error.reason.toLowerCase().includes('invalid totp code')\n ? `${error.reason}. ${TOTP_CLOCK_SKEW_ERROR_APPENDAGE}`\n : error.reason\n }\n />\n )}\n </Paper>\n </StyledOuterContainer>\n )\n}\n\nexport const EXPORTED_FOR_UNIT_TESTING = { toOtpAuthUrl }\n"],"mappings":"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"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"ColorfulPortalCardWithChips.js","names":[],"sources":["../../../../src/components/BasePortalCard/ColorfulPortalCardWithChips/ColorfulPortalCardWithChips.tsx"],"sourcesContent":["import { ReactComponent as DefaultBackground } from '@/assets/DefaultColorfulPortalCardBackground.svg'\nimport MarkdownSynapse from '@/components/Markdown/MarkdownSynapse'\nimport { hashCode } from '@/utils/functions/StringUtils'\nimport { useImageUrl } from '@/utils/hooks/useImageUrlUtils'\nimport ArrowForwardIcon from '@mui/icons-material/ArrowForward'\nimport { Box, Button, Chip, Stack, Typography } from '@mui/material'\nimport React from 'react'\nimport BasePortalCard from '../BasePortalCard'\nimport styles from './ColorfulPortalCardWithChips.module.scss'\n\ntype ColorfulPortalCardWithChipsProps = {\n title?: string\n subtitle?: string\n description?: string\n descriptionTitle?: string\n learnMoreLink?: string\n buttonOverrideText?: string\n chips?: string[]\n backgroundImage?: string\n backgroundColor?: string\n tag?: string\n entityId?: string\n borderRadiusPx?: number\n cardSize?: 'small' | 'medium' | 'large'\n}\n\nconst colors = ['#F8CC7D', '#BFE8F4', '#CEFBDD']\n\nconst getChicletColors = (chips: string[]) => {\n return chips.map(chip => {\n const hash = hashCode(chip)\n const index = Math.abs(hash) % colors.length\n return colors[index]\n })\n}\n\nconst Chips = ({ values }: { values: string[] }): React.ReactNode => {\n const chipColors = getChicletColors(values)\n return (\n <Box className={styles.ColorfulPortalCardWithChips__chipsContainer}>\n {values.map((chip, index) => (\n <Chip\n key={chip}\n label={<Typography variant=\"smallText1\">{chip}</Typography>}\n variant=\"filled\"\n style={{ backgroundColor: chipColors[index] }}\n className={styles.ChallengePortalCard__chip}\n />\n ))}\n </Box>\n )\n}\n\nconst ColorfulPortalCardWithChips = ({\n title,\n subtitle,\n description,\n descriptionTitle,\n learnMoreLink,\n buttonOverrideText,\n chips,\n backgroundImage,\n backgroundColor,\n tag,\n entityId,\n borderRadiusPx,\n cardSize = 'medium',\n}: ColorfulPortalCardWithChipsProps): React.ReactNode => {\n const imageUrl = useImageUrl(backgroundImage ?? '', entityId ?? '')\n const backgroundImageValue = imageUrl || backgroundImage\n\n return (\n <BasePortalCard\n cardSize={cardSize}\n borderRadiusPx={borderRadiusPx}\n backgroundImage={backgroundImageValue || <DefaultBackground />}\n backgroundColor={backgroundColor || colors[0]}\n contentBelowCard={chips && chips.length > 0 && <Chips values={chips} />}\n >\n <Stack className={styles.ColorfulPortalCardWithChips__sectionContainer}>\n <Stack>\n {tag && (\n <Chip\n color=\"secondary\"\n variant=\"filled\"\n className={styles.ColorfulPortalCardWithChips__registrationStatus}\n sx={{ backgroundColor }}\n label={tag}\n />\n )}\n <Stack className={styles.ColorfulPortalCardWithChips__titleSection}>\n <Typography\n variant=\"headline1\"\n className={styles.ColorfulPortalCardWithChips__title}\n >\n {title}\n </Typography>\n <Typography\n variant=\"headline1\"\n className={styles.ColorfulPortalCardWithChips__challengeName}\n >\n {subtitle}\n </Typography>\n </Stack>\n </Stack>\n <Stack className={styles.ColorfulPortalCardWithChips__bottomSection}>\n {descriptionTitle && (\n <Typography\n variant=\"headline1\"\n className={styles.ColorfulPortalCardWithChips__descriptionTitle}\n >\n {descriptionTitle}\n </Typography>\n )}\n <Typography\n variant=\"smallText1\"\n className={styles.ColorfulPortalCardWithChips__description}\n >\n <MarkdownSynapse markdown={description ?? ''} />\n </Typography>\n {learnMoreLink && (\n <Button\n className={styles.ColorfulPortalCardWithChips__learnMoreButton}\n variant=\"outlined\"\n endIcon={<ArrowForwardIcon />}\n href={learnMoreLink}\n target=\"_blank\"\n rel=\"noopener noreferrer\"\n >\n {buttonOverrideText ?? 'Learn more'}\n </Button>\n )}\n </Stack>\n </Stack>\n </BasePortalCard>\n )\n}\n\nexport default ColorfulPortalCardWithChips\n"],"mappings":";;;;;;;;;;;AA0BA,IAAM,IAAS;CAAC;CAAW;CAAW;CAAU,EAE1C,KAAoB,MACjB,EAAM,KAAI,MAAQ;CACvB,IAAM,IAAO,EAAS,EAAK;AAE3B,QAAO,EADO,KAAK,IAAI,EAAK,GAAG,EAAO;EAEtC,EAGE,KAAS,EAAE,gBAAoD;CACnE,IAAM,IAAa,EAAiB,EAAO;AAC3C,QACE,kBAAC,GAAD;EAAK,WAAW,EAAO;YACpB,EAAO,KAAK,GAAM,MACjB,kBAAC,GAAD;GAEE,OAAO,kBAAC,GAAD;IAAY,SAAQ;cAAc;IAAkB,CAAA;GAC3D,SAAQ;GACR,OAAO,EAAE,iBAAiB,EAAW,IAAQ;GAC7C,WAAW,EAAO;GAClB,EALK,EAKL,CACF;EACE,CAAA;GAIJ,KAA+B,EACnC,UACA,aACA,gBACA,qBACA,kBACA,uBACA,UACA,oBACA,oBACA,QACA,aACA,mBACA,cAAW,eAMT,kBAAC,GAAD;CACY;CACM;CAChB,iBAPa,EAAY,KAAmB,IAAI,KAAY,
|
|
1
|
+
{"version":3,"file":"ColorfulPortalCardWithChips.js","names":[],"sources":["../../../../src/components/BasePortalCard/ColorfulPortalCardWithChips/ColorfulPortalCardWithChips.tsx"],"sourcesContent":["import { ReactComponent as DefaultBackground } from '@/assets/DefaultColorfulPortalCardBackground.svg'\nimport MarkdownSynapse from '@/components/Markdown/MarkdownSynapse'\nimport { hashCode } from '@/utils/functions/StringUtils'\nimport { useImageUrl } from '@/utils/hooks/useImageUrlUtils'\nimport ArrowForwardIcon from '@mui/icons-material/ArrowForward'\nimport { Box, Button, Chip, Stack, Typography } from '@mui/material'\nimport React from 'react'\nimport BasePortalCard from '../BasePortalCard'\nimport styles from './ColorfulPortalCardWithChips.module.scss'\n\ntype ColorfulPortalCardWithChipsProps = {\n title?: string\n subtitle?: string\n description?: string\n descriptionTitle?: string\n learnMoreLink?: string\n buttonOverrideText?: string\n chips?: string[]\n backgroundImage?: string\n backgroundColor?: string\n tag?: string\n entityId?: string\n borderRadiusPx?: number\n cardSize?: 'small' | 'medium' | 'large'\n}\n\nconst colors = ['#F8CC7D', '#BFE8F4', '#CEFBDD']\n\nconst getChicletColors = (chips: string[]) => {\n return chips.map(chip => {\n const hash = hashCode(chip)\n const index = Math.abs(hash) % colors.length\n return colors[index]\n })\n}\n\nconst Chips = ({ values }: { values: string[] }): React.ReactNode => {\n const chipColors = getChicletColors(values)\n return (\n <Box className={styles.ColorfulPortalCardWithChips__chipsContainer}>\n {values.map((chip, index) => (\n <Chip\n key={chip}\n label={<Typography variant=\"smallText1\">{chip}</Typography>}\n variant=\"filled\"\n style={{ backgroundColor: chipColors[index] }}\n className={styles.ChallengePortalCard__chip}\n />\n ))}\n </Box>\n )\n}\n\nconst ColorfulPortalCardWithChips = ({\n title,\n subtitle,\n description,\n descriptionTitle,\n learnMoreLink,\n buttonOverrideText,\n chips,\n backgroundImage,\n backgroundColor,\n tag,\n entityId,\n borderRadiusPx,\n cardSize = 'medium',\n}: ColorfulPortalCardWithChipsProps): React.ReactNode => {\n const imageUrl = useImageUrl(backgroundImage ?? '', entityId ?? '')\n const backgroundImageValue = imageUrl || backgroundImage\n\n return (\n <BasePortalCard\n cardSize={cardSize}\n borderRadiusPx={borderRadiusPx}\n backgroundImage={backgroundImageValue || <DefaultBackground />}\n backgroundColor={backgroundColor || colors[0]}\n contentBelowCard={chips && chips.length > 0 && <Chips values={chips} />}\n >\n <Stack className={styles.ColorfulPortalCardWithChips__sectionContainer}>\n <Stack>\n {tag && (\n <Chip\n color=\"secondary\"\n variant=\"filled\"\n className={styles.ColorfulPortalCardWithChips__registrationStatus}\n sx={{ backgroundColor }}\n label={tag}\n />\n )}\n <Stack className={styles.ColorfulPortalCardWithChips__titleSection}>\n <Typography\n variant=\"headline1\"\n className={styles.ColorfulPortalCardWithChips__title}\n >\n {title}\n </Typography>\n <Typography\n variant=\"headline1\"\n className={styles.ColorfulPortalCardWithChips__challengeName}\n >\n {subtitle}\n </Typography>\n </Stack>\n </Stack>\n <Stack className={styles.ColorfulPortalCardWithChips__bottomSection}>\n {descriptionTitle && (\n <Typography\n variant=\"headline1\"\n className={styles.ColorfulPortalCardWithChips__descriptionTitle}\n >\n {descriptionTitle}\n </Typography>\n )}\n <Typography\n variant=\"smallText1\"\n className={styles.ColorfulPortalCardWithChips__description}\n >\n <MarkdownSynapse markdown={description ?? ''} />\n </Typography>\n {learnMoreLink && (\n <Button\n className={styles.ColorfulPortalCardWithChips__learnMoreButton}\n variant=\"outlined\"\n endIcon={<ArrowForwardIcon />}\n href={learnMoreLink}\n target=\"_blank\"\n rel=\"noopener noreferrer\"\n >\n {buttonOverrideText ?? 'Learn more'}\n </Button>\n )}\n </Stack>\n </Stack>\n </BasePortalCard>\n )\n}\n\nexport default ColorfulPortalCardWithChips\n"],"mappings":";;;;;;;;;;;AA0BA,IAAM,IAAS;CAAC;CAAW;CAAW;CAAU,EAE1C,KAAoB,MACjB,EAAM,KAAI,MAAQ;CACvB,IAAM,IAAO,EAAS,EAAK;AAE3B,QAAO,EADO,KAAK,IAAI,EAAK,GAAG,EAAO;EAEtC,EAGE,KAAS,EAAE,gBAAoD;CACnE,IAAM,IAAa,EAAiB,EAAO;AAC3C,QACE,kBAAC,GAAD;EAAK,WAAW,EAAO;YACpB,EAAO,KAAK,GAAM,MACjB,kBAAC,GAAD;GAEE,OAAO,kBAAC,GAAD;IAAY,SAAQ;cAAc;IAAkB,CAAA;GAC3D,SAAQ;GACR,OAAO,EAAE,iBAAiB,EAAW,IAAQ;GAC7C,WAAW,EAAO;GAClB,EALK,EAKL,CACF;EACE,CAAA;GAIJ,KAA+B,EACnC,UACA,aACA,gBACA,qBACA,kBACA,uBACA,UACA,oBACA,oBACA,QACA,aACA,mBACA,cAAW,eAMT,kBAAC,GAAD;CACY;CACM;CAChB,iBAPa,EAAY,KAAmB,IAAI,KAAY,GACnC,IAAY,KAMI,kBAAC,GAAD,EAAqB,CAAA;CAC9D,iBAAiB,KAAmB,EAAO;CAC3C,kBAAkB,KAAS,EAAM,SAAS,KAAK,kBAAC,GAAD,EAAO,QAAQ,GAAS,CAAA;WAEvE,kBAAC,GAAD;EAAO,WAAW,EAAO;YAAzB,CACE,kBAAC,GAAD,EAAA,UAAA,CACG,KACC,kBAAC,GAAD;GACE,OAAM;GACN,SAAQ;GACR,WAAW,EAAO;GAClB,IAAI,EAAE,oBAAiB;GACvB,OAAO;GACP,CAAA,EAEJ,kBAAC,GAAD;GAAO,WAAW,EAAO;aAAzB,CACE,kBAAC,GAAD;IACE,SAAQ;IACR,WAAW,EAAO;cAEjB;IACU,CAAA,EACb,kBAAC,GAAD;IACE,SAAQ;IACR,WAAW,EAAO;cAEjB;IACU,CAAA,CACP;KACF,EAAA,CAAA,EACR,kBAAC,GAAD;GAAO,WAAW,EAAO;aAAzB;IACG,KACC,kBAAC,GAAD;KACE,SAAQ;KACR,WAAW,EAAO;eAEjB;KACU,CAAA;IAEf,kBAAC,GAAD;KACE,SAAQ;KACR,WAAW,EAAO;eAElB,kBAAC,GAAD,EAAiB,UAAU,KAAe,IAAM,CAAA;KACrC,CAAA;IACZ,KACC,kBAAC,GAAD;KACE,WAAW,EAAO;KAClB,SAAQ;KACR,SAAS,kBAAC,GAAD,EAAoB,CAAA;KAC7B,MAAM;KACN,QAAO;KACP,KAAI;eAEH,KAAsB;KAChB,CAAA;IAEL;KACF;;CACO,CAAA"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"CardContainer.js","names":[],"sources":["../../../src/components/CardContainer/CardContainer.tsx"],"sourcesContent":["import { CardConfiguration } from '@/components/CardContainer/CardConfiguration'\nimport TableRowGenericCard from '@/components/GenericCard/TableRowGenericCard'\nimport useGetInfoFromIds from '@/utils/hooks/useGetInfoFromIds'\nimport {\n DATASET,\n FUNDER,\n GENERIC_CARD,\n MEDIUM_USER_CARD,\n OBSERVATION_CARD,\n RELEASE_CARD,\n} from '@/utils/SynapseConstants'\nimport { Box } from '@mui/material'\nimport {\n ColumnTypeEnum,\n EntityHeader,\n Row,\n RowSet,\n} from '@sage-bionetworks/synapse-types'\nimport { Suspense } from 'react'\nimport loadingScreen from '../LoadingScreen/LoadingScreen'\nimport { useQueryContext } from '../QueryContext'\nimport { useQueryVisualizationContext } from '../QueryVisualizationWrapper'\nimport { useSuspenseGetQueryMetadata } from '../QueryWrapper/useGetQueryMetadata'\nimport { ReleaseCard } from '../ReleaseCard'\nimport { Dataset, Funder } from '../row_renderers'\nimport {\n LoadingObservationCard,\n ObservationCard,\n} from '../row_renderers/ObservationCard'\nimport TotalQueryResults from '../TotalQueryResults'\nimport UserCardList from '../UserCardList/UserCardList'\nimport { SustainabilityScorecardProps } from '../SustainabilityScorecard'\n\nconst defaultListSx = { display: 'block' }\nconst releaseCardMediumListSx = {\n display: 'grid',\n gridTemplateColumns: 'repeat(auto-fill, 272px)',\n gridAutoRows: 'auto',\n gridAutoFlow: 'row',\n gap: '10px',\n mb: '30px',\n}\nconst multiCardListSx = {\n display: 'grid',\n gridTemplateColumns: 'repeat(auto-fit, minmax(300px, 1fr))',\n gridColumnGap: '20px',\n marginBottom: '20px',\n marginTop: '20px',\n\n '& .SRC-portalCard': {\n margin: '10px 0px',\n },\n}\n\nexport type CardContainerProps = {\n rowSet: RowSet\n isHeader?: boolean\n sustainabilityScorecard?: SustainabilityScorecardProps\n isAlignToLeftNav?: boolean\n title?: string\n isLoading?: boolean\n unitDescription?: string\n multiCardList?: boolean\n} & CardConfiguration\n\nfunction Card(props: { propsToPass: any; type: string }) {\n const { propsToPass, type } = props\n switch (type) {\n case DATASET:\n return <Dataset {...propsToPass} />\n case FUNDER:\n return <Funder {...propsToPass} />\n case GENERIC_CARD:\n return <TableRowGenericCard {...propsToPass} />\n case OBSERVATION_CARD:\n return <ObservationCard {...propsToPass} />\n case RELEASE_CARD:\n return <ReleaseCard {...propsToPass} />\n default:\n return <div /> // this should never happen\n }\n}\n\nfunction CardContainerInternal(props: CardContainerProps) {\n const {\n unitDescription,\n type,\n secondaryLabelLimit = 3,\n title,\n rowSet,\n multiCardList,\n ...rest\n } = props\n const { NoContentPlaceholder } = useQueryVisualizationContext()\n const queryContext = useQueryContext()\n const { data: queryMetadata } = useSuspenseGetQueryMetadata()\n const queryVisualizationContext = useQueryVisualizationContext()\n\n const dataRows: Row[] = rowSet.rows\n const isMultipleCards = dataRows.length > 1\n\n const ids = [rowSet.tableId]\n const tableEntityConcreteType = useGetInfoFromIds<EntityHeader>({\n ids,\n type: 'ENTITY_HEADER',\n })\n\n // the cards only show the loading screen on initial load, this occurs when data is undefined\n if (dataRows.length === 0) {\n // Show \"no results\" UI (see PORTALS-1497)\n return <NoContentPlaceholder />\n }\n\n const schema: Record<string, number> = {}\n rowSet.headers.forEach((element, index) => {\n schema[element.name] = index\n })\n\n let cards\n if (type === MEDIUM_USER_CARD) {\n // Hard coding ownerId as a column name containing the user profile ownerId\n // for each row, grab the column with the ownerId\n const userIdColumnIndex = rowSet.headers.findIndex(\n el => el.columnType === ColumnTypeEnum.USERID,\n )\n if (userIdColumnIndex === -1) {\n throw Error(\n 'Type MEDIUM_USER_CARD specified but no columnType USERID found',\n )\n }\n const listIds = dataRows.map(el => el.values[userIdColumnIndex])\n cards = (\n <UserCardList rowSet={rowSet} list={listIds} size={MEDIUM_USER_CARD} />\n )\n } else {\n // render the cards\n cards = dataRows.length ? (\n dataRows.map((rowData: Row, index) => {\n const propsForCard = {\n type,\n schema,\n secondaryLabelLimit,\n rowId: rowData.rowId,\n versionNumber: rowData.versionNumber,\n data: rowData.values,\n selectColumns: rowSet.headers,\n columnModels: queryMetadata.columnModels,\n tableEntityConcreteType:\n tableEntityConcreteType[0] && tableEntityConcreteType[0].type,\n tableId: rowSet.tableId,\n queryContext: queryContext,\n queryVisualizationContext,\n ...rest,\n }\n return (\n <Card\n key={rowData.rowId ?? index}\n propsToPass={propsForCard}\n type={type}\n />\n )\n })\n ) : (\n <></>\n )\n }\n\n const isReleaseCardMediumList =\n props.type === RELEASE_CARD &&\n props.releaseCardConfig?.cardSize === 'medium'\n\n let cardListSx = defaultListSx\n if (isReleaseCardMediumList) {\n cardListSx = releaseCardMediumListSx\n } else if (multiCardList && isMultipleCards) {\n cardListSx = multiCardListSx\n }\n\n return (\n <>\n <Box role=\"list\" sx={cardListSx}>\n {title && <h2 className=\"SRC-card-overview-title\">{title}</h2>}\n {!title && unitDescription && (\n <TotalQueryResults frontText={'Displaying'} />\n )}\n {/* ReactCSSTransitionGroup adds css fade in property for cards that come into view */}\n {cards}\n </Box>\n </>\n )\n}\n\nexport function CardContainer(props: CardContainerProps) {\n const fallback = (\n <div>\n {props.type === OBSERVATION_CARD && <LoadingObservationCard />}\n {props.type !== OBSERVATION_CARD && loadingScreen}\n </div>\n )\n return (\n <Suspense fallback={fallback}>\n <CardContainerInternal {...props} />\n </Suspense>\n )\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;AAiCA,IAAM,IAAgB,EAAE,SAAS,SAAS,EACpC,IAA0B;CAC9B,SAAS;CACT,qBAAqB;CACrB,cAAc;CACd,cAAc;CACd,KAAK;CACL,IAAI;CACL,EACK,IAAkB;CACtB,SAAS;CACT,qBAAqB;CACrB,eAAe;CACf,cAAc;CACd,WAAW;CAEX,qBAAqB,EACnB,QAAQ,YACT;CACF;AAaD,SAAS,EAAK,GAA2C;CACvD,IAAM,EAAE,gBAAa,YAAS;AAC9B,SAAQ,GAAR;EACE,KAAK,EACH,QAAO,kBAAC,GAAD,EAAS,GAAI,GAAe,CAAA;EACrC,KAAK,EACH,QAAO,kBAAC,GAAD,EAAQ,GAAI,GAAe,CAAA;EACpC,KAAK,EACH,QAAO,kBAAC,GAAD,EAAqB,GAAI,GAAe,CAAA;EACjD,KAAK,EACH,QAAO,kBAAC,GAAD,EAAiB,GAAI,GAAe,CAAA;EAC7C,KAAK,EACH,QAAO,kBAAC,GAAD,EAAa,GAAI,GAAe,CAAA;EACzC,QACE,QAAO,kBAAC,OAAD,EAAO,CAAA;;;AAIpB,SAAS,EAAsB,GAA2B;CACxD,IAAM,EACJ,oBACA,SACA,yBAAsB,GACtB,UACA,WACA,kBACA,GAAG,MACD,GACE,EAAE,4BAAyB,GAA8B,EACzD,IAAe,GAAiB,EAChC,EAAE,MAAM,MAAkB,GAA6B,EACvD,IAA4B,GAA8B,EAE1D,IAAkB,EAAO,MACzB,IAAkB,EAAS,SAAS,GAGpC,IAA0B,EAAgC;EAC9D,
|
|
1
|
+
{"version":3,"file":"CardContainer.js","names":[],"sources":["../../../src/components/CardContainer/CardContainer.tsx"],"sourcesContent":["import { CardConfiguration } from '@/components/CardContainer/CardConfiguration'\nimport TableRowGenericCard from '@/components/GenericCard/TableRowGenericCard'\nimport useGetInfoFromIds from '@/utils/hooks/useGetInfoFromIds'\nimport {\n DATASET,\n FUNDER,\n GENERIC_CARD,\n MEDIUM_USER_CARD,\n OBSERVATION_CARD,\n RELEASE_CARD,\n} from '@/utils/SynapseConstants'\nimport { Box } from '@mui/material'\nimport {\n ColumnTypeEnum,\n EntityHeader,\n Row,\n RowSet,\n} from '@sage-bionetworks/synapse-types'\nimport { Suspense } from 'react'\nimport loadingScreen from '../LoadingScreen/LoadingScreen'\nimport { useQueryContext } from '../QueryContext'\nimport { useQueryVisualizationContext } from '../QueryVisualizationWrapper'\nimport { useSuspenseGetQueryMetadata } from '../QueryWrapper/useGetQueryMetadata'\nimport { ReleaseCard } from '../ReleaseCard'\nimport { Dataset, Funder } from '../row_renderers'\nimport {\n LoadingObservationCard,\n ObservationCard,\n} from '../row_renderers/ObservationCard'\nimport TotalQueryResults from '../TotalQueryResults'\nimport UserCardList from '../UserCardList/UserCardList'\nimport { SustainabilityScorecardProps } from '../SustainabilityScorecard'\n\nconst defaultListSx = { display: 'block' }\nconst releaseCardMediumListSx = {\n display: 'grid',\n gridTemplateColumns: 'repeat(auto-fill, 272px)',\n gridAutoRows: 'auto',\n gridAutoFlow: 'row',\n gap: '10px',\n mb: '30px',\n}\nconst multiCardListSx = {\n display: 'grid',\n gridTemplateColumns: 'repeat(auto-fit, minmax(300px, 1fr))',\n gridColumnGap: '20px',\n marginBottom: '20px',\n marginTop: '20px',\n\n '& .SRC-portalCard': {\n margin: '10px 0px',\n },\n}\n\nexport type CardContainerProps = {\n rowSet: RowSet\n isHeader?: boolean\n sustainabilityScorecard?: SustainabilityScorecardProps\n isAlignToLeftNav?: boolean\n title?: string\n isLoading?: boolean\n unitDescription?: string\n multiCardList?: boolean\n} & CardConfiguration\n\nfunction Card(props: { propsToPass: any; type: string }) {\n const { propsToPass, type } = props\n switch (type) {\n case DATASET:\n return <Dataset {...propsToPass} />\n case FUNDER:\n return <Funder {...propsToPass} />\n case GENERIC_CARD:\n return <TableRowGenericCard {...propsToPass} />\n case OBSERVATION_CARD:\n return <ObservationCard {...propsToPass} />\n case RELEASE_CARD:\n return <ReleaseCard {...propsToPass} />\n default:\n return <div /> // this should never happen\n }\n}\n\nfunction CardContainerInternal(props: CardContainerProps) {\n const {\n unitDescription,\n type,\n secondaryLabelLimit = 3,\n title,\n rowSet,\n multiCardList,\n ...rest\n } = props\n const { NoContentPlaceholder } = useQueryVisualizationContext()\n const queryContext = useQueryContext()\n const { data: queryMetadata } = useSuspenseGetQueryMetadata()\n const queryVisualizationContext = useQueryVisualizationContext()\n\n const dataRows: Row[] = rowSet.rows\n const isMultipleCards = dataRows.length > 1\n\n const ids = [rowSet.tableId]\n const tableEntityConcreteType = useGetInfoFromIds<EntityHeader>({\n ids,\n type: 'ENTITY_HEADER',\n })\n\n // the cards only show the loading screen on initial load, this occurs when data is undefined\n if (dataRows.length === 0) {\n // Show \"no results\" UI (see PORTALS-1497)\n return <NoContentPlaceholder />\n }\n\n const schema: Record<string, number> = {}\n rowSet.headers.forEach((element, index) => {\n schema[element.name] = index\n })\n\n let cards\n if (type === MEDIUM_USER_CARD) {\n // Hard coding ownerId as a column name containing the user profile ownerId\n // for each row, grab the column with the ownerId\n const userIdColumnIndex = rowSet.headers.findIndex(\n el => el.columnType === ColumnTypeEnum.USERID,\n )\n if (userIdColumnIndex === -1) {\n throw Error(\n 'Type MEDIUM_USER_CARD specified but no columnType USERID found',\n )\n }\n const listIds = dataRows.map(el => el.values[userIdColumnIndex])\n cards = (\n <UserCardList rowSet={rowSet} list={listIds} size={MEDIUM_USER_CARD} />\n )\n } else {\n // render the cards\n cards = dataRows.length ? (\n dataRows.map((rowData: Row, index) => {\n const propsForCard = {\n type,\n schema,\n secondaryLabelLimit,\n rowId: rowData.rowId,\n versionNumber: rowData.versionNumber,\n data: rowData.values,\n selectColumns: rowSet.headers,\n columnModels: queryMetadata.columnModels,\n tableEntityConcreteType:\n tableEntityConcreteType[0] && tableEntityConcreteType[0].type,\n tableId: rowSet.tableId,\n queryContext: queryContext,\n queryVisualizationContext,\n ...rest,\n }\n return (\n <Card\n key={rowData.rowId ?? index}\n propsToPass={propsForCard}\n type={type}\n />\n )\n })\n ) : (\n <></>\n )\n }\n\n const isReleaseCardMediumList =\n props.type === RELEASE_CARD &&\n props.releaseCardConfig?.cardSize === 'medium'\n\n let cardListSx = defaultListSx\n if (isReleaseCardMediumList) {\n cardListSx = releaseCardMediumListSx\n } else if (multiCardList && isMultipleCards) {\n cardListSx = multiCardListSx\n }\n\n return (\n <>\n <Box role=\"list\" sx={cardListSx}>\n {title && <h2 className=\"SRC-card-overview-title\">{title}</h2>}\n {!title && unitDescription && (\n <TotalQueryResults frontText={'Displaying'} />\n )}\n {/* ReactCSSTransitionGroup adds css fade in property for cards that come into view */}\n {cards}\n </Box>\n </>\n )\n}\n\nexport function CardContainer(props: CardContainerProps) {\n const fallback = (\n <div>\n {props.type === OBSERVATION_CARD && <LoadingObservationCard />}\n {props.type !== OBSERVATION_CARD && loadingScreen}\n </div>\n )\n return (\n <Suspense fallback={fallback}>\n <CardContainerInternal {...props} />\n </Suspense>\n )\n}\n"],"mappings":"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"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"CardDeck.Mobile.js","names":[],"sources":["../../../src/components/CardDeck/CardDeck.Mobile.tsx"],"sourcesContent":["import { CardDeckCardProps } from '@/components/CardDeck/CardDeckCardProps'\nimport LargeButton from '@/components/styled/LargeButton'\nimport { Link } from '@mui/material'\nimport ExpandableContent from '../home_page/ExpandableContent'\n\nexport default function CardDeckMobile({\n description,\n title,\n titleIcon,\n ctaButtonText,\n ctaButtonURL,\n cardDeckType,\n}: CardDeckCardProps) {\n const titleElement = (\n <div className=\"CardDeck__Mobile__Header\">\n <span className=\"CardDeck__Mobile__Header__icon\">{titleIcon}</span>\n <span className=\"CardDeck__Mobile__Header__Title\"> {title} </span>\n </div>\n )\n const content = (\n <div className=\"CardDeck__Mobile__Content\">\n <p>{description}</p>\n {cardDeckType === 'b2ai' ? (\n <Link href={ctaButtonURL} underline=\"none\">\n {ctaButtonText}\n </Link>\n ) : (\n <LargeButton\n color=\"secondary\"\n variant=\"outlined\" // Update variant based on cardDeckType\n href={ctaButtonURL}\n sx={{\n width: '170px',\n marginLeft: 0,\n fontSize: '14px',\n boxShadow: 'none',\n color: 'primary.main',\n }}\n >\n {ctaButtonText}\n </LargeButton>\n )}\n </div>\n )\n return <ExpandableContent title={titleElement} content={content} />\n}\n"],"mappings":";;;;;AAKA,SAAwB,EAAe,EACrC,gBACA,UACA,cACA,kBACA,iBACA,mBACoB;AAgCpB,QAAO,kBAAC,GAAD;EAAmB,OA9BxB,kBAAC,OAAD;GAAK,WAAU;aAAf,CACE,kBAAC,QAAD;IAAM,WAAU;cAAkC;IAAiB,CAAA,EACnE,kBAAC,QAAD;IAAM,WAAU;cAAhB;KAAkD;KAAE;KAAM;KAAQ;MAC9D
|
|
1
|
+
{"version":3,"file":"CardDeck.Mobile.js","names":[],"sources":["../../../src/components/CardDeck/CardDeck.Mobile.tsx"],"sourcesContent":["import { CardDeckCardProps } from '@/components/CardDeck/CardDeckCardProps'\nimport LargeButton from '@/components/styled/LargeButton'\nimport { Link } from '@mui/material'\nimport ExpandableContent from '../home_page/ExpandableContent'\n\nexport default function CardDeckMobile({\n description,\n title,\n titleIcon,\n ctaButtonText,\n ctaButtonURL,\n cardDeckType,\n}: CardDeckCardProps) {\n const titleElement = (\n <div className=\"CardDeck__Mobile__Header\">\n <span className=\"CardDeck__Mobile__Header__icon\">{titleIcon}</span>\n <span className=\"CardDeck__Mobile__Header__Title\"> {title} </span>\n </div>\n )\n const content = (\n <div className=\"CardDeck__Mobile__Content\">\n <p>{description}</p>\n {cardDeckType === 'b2ai' ? (\n <Link href={ctaButtonURL} underline=\"none\">\n {ctaButtonText}\n </Link>\n ) : (\n <LargeButton\n color=\"secondary\"\n variant=\"outlined\" // Update variant based on cardDeckType\n href={ctaButtonURL}\n sx={{\n width: '170px',\n marginLeft: 0,\n fontSize: '14px',\n boxShadow: 'none',\n color: 'primary.main',\n }}\n >\n {ctaButtonText}\n </LargeButton>\n )}\n </div>\n )\n return <ExpandableContent title={titleElement} content={content} />\n}\n"],"mappings":";;;;;AAKA,SAAwB,EAAe,EACrC,gBACA,UACA,cACA,kBACA,iBACA,mBACoB;AAgCpB,QAAO,kBAAC,GAAD;EAAmB,OA9BxB,kBAAC,OAAD;GAAK,WAAU;aAAf,CACE,kBAAC,QAAD;IAAM,WAAU;cAAkC;IAAiB,CAAA,EACnE,kBAAC,QAAD;IAAM,WAAU;cAAhB;KAAkD;KAAE;KAAM;KAAQ;MAC9D;IA2ByB;EAAuB,SAxBtD,kBAAC,OAAD;GAAK,WAAU;aAAf,CACE,kBAAC,KAAD,EAAA,UAAI,GAAgB,CAAA,EACnB,MAAiB,SAChB,kBAAC,GAAD;IAAM,MAAM;IAAc,WAAU;cACjC;IACI,CAAA,GAEP,kBAAC,GAAD;IACE,OAAM;IACN,SAAQ;IACR,MAAM;IACN,IAAI;KACF,OAAO;KACP,YAAY;KACZ,UAAU;KACV,WAAW;KACX,OAAO;KACR;cAEA;IACW,CAAA,CAEZ;IAEgD;EAAW,CAAA"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"TableQueryCardDeck.js","names":[],"sources":["../../../src/components/CardDeck/TableQueryCardDeck.tsx"],"sourcesContent":["import { CardDeck } from '@/components/CardDeck/CardDeck'\nimport { CardDeckCardProps } from '@/components/CardDeck/CardDeckCardProps'\nimport { ImageFileHandle } from '@/components/widgets/ImageFileHandle'\nimport { useGetEntity } from '@/synapse-queries/entity/useEntity'\nimport useGetQueryResultBundle from '@/synapse-queries/entity/useGetQueryResultBundle'\nimport { SynapseConstants } from '@/utils'\nimport { getFieldIndex } from '@/utils/functions/queryUtils'\nimport { QueryBundleRequest } from '@sage-bionetworks/synapse-types'\nimport { CardLink } from '../CardContainer/CardLink'\nimport { getFileHandleAssociation, getLinkParams } from '../GenericCard'\n\nexport type TableQueryCardDeckProps = {\n entityId: string /* The table entity which should be queried */\n titleColumnName: string /* The column name for the title */\n descriptionColumnName: string /* The column name for the description */\n ctaButtonTextColumnName: string /* The column name for the call to action button text */\n ctaButtonURLColumnName: string /* The column name for the call to action button URL */\n titleIconFileHandleColumnName?: string /* The column name for the title icon image file */\n headerImageFileHandleColumnName?: string /* The column name for the header image file */\n cardDeckType?: 'cckp' | 'b2ai' /* The type of card deck (cckp or b2ai)*/\n linkConfig: CardLink /* The link configuration */\n}\n\n/**\n * Transforms a Synapse table into a card deck.\n */\nexport function TableQueryCardDeck(props: TableQueryCardDeckProps) {\n const {\n entityId,\n titleColumnName,\n descriptionColumnName,\n ctaButtonTextColumnName,\n ctaButtonURLColumnName,\n titleIconFileHandleColumnName,\n headerImageFileHandleColumnName,\n cardDeckType,\n linkConfig,\n } = props\n const entity = useGetEntity(entityId)\n const queryBundleRequest: QueryBundleRequest = {\n concreteType: 'org.sagebionetworks.repo.model.table.QueryBundleRequest',\n entityId,\n partMask:\n SynapseConstants.BUNDLE_MASK_QUERY_SELECT_COLUMNS |\n SynapseConstants.BUNDLE_MASK_QUERY_RESULTS,\n query: {\n sql: `select * from ${entityId}`,\n },\n }\n const { data: queryResultBundle } =\n useGetQueryResultBundle(queryBundleRequest)\n\n const titleColumnIndex = getFieldIndex(titleColumnName, queryResultBundle)\n const descriptionColumnIndex = getFieldIndex(\n descriptionColumnName,\n queryResultBundle,\n )\n const ctaButtonTextColumnIndex = getFieldIndex(\n ctaButtonTextColumnName,\n queryResultBundle,\n )\n const ctaButtonURLColumnIndex = getFieldIndex(\n ctaButtonURLColumnName,\n queryResultBundle,\n )\n const titleIconColumnIndex = getFieldIndex(\n titleIconFileHandleColumnName,\n queryResultBundle,\n )\n const headerImageColumnIndex = getFieldIndex(\n headerImageFileHandleColumnName,\n queryResultBundle,\n )\n\n const cards = queryResultBundle?.queryResult!.queryResults.rows.map(\n (el): CardDeckCardProps => {\n const values = el.values as string[]\n if (values.some(value => value === null)) {\n // We cast values above assuming there are no null values, emit a warning just in case.\n console.warn('Row has null value(s) when no nulls expected')\n }\n\n const title = values[titleColumnIndex]\n const description = values[descriptionColumnIndex]\n const ctaButtonText = values[ctaButtonTextColumnIndex]\n const ctaButtonURL = values[ctaButtonURLColumnIndex] ?? ''\n const titleIconFileHandleIdValue = values[titleIconColumnIndex] ?? ''\n const headerImageFileHandleIdValue = values[headerImageColumnIndex] ?? ''\n\n const schema: Record<string, number> = {}\n queryResultBundle?.queryResult!.queryResults.headers.forEach(\n (header, colIndex) => {\n schema[header.name] = colIndex\n },\n )\n const { href } = getLinkParams(ctaButtonURL, linkConfig, values, schema)\n\n const titleIconFileHandleAssociation = getFileHandleAssociation(\n entity.data,\n titleIconFileHandleIdValue,\n `syn${el.rowId}`,\n )\n\n const titleIcon = titleIconFileHandleAssociation ? (\n <ImageFileHandle\n fileHandleAssociation={titleIconFileHandleAssociation}\n />\n ) : undefined\n\n const headerImageFileHandleAssociation = getFileHandleAssociation(\n entity.data,\n headerImageFileHandleIdValue,\n `syn${el.rowId}`,\n )\n\n const headerImage = headerImageFileHandleAssociation ? (\n <ImageFileHandle\n fileHandleAssociation={headerImageFileHandleAssociation}\n />\n ) : undefined\n\n return {\n title,\n description,\n ctaButtonText,\n ctaButtonURL: href,\n titleIcon,\n headerImage,\n cardDeckType,\n } satisfies CardDeckCardProps\n },\n )\n\n return <CardDeck cards={cards || []} cardDeckType={cardDeckType} />\n}\n\nexport default TableQueryCardDeck\n"],"mappings":";;;;;;;;;;;AA0BA,SAAgB,EAAmB,GAAgC;CACjE,IAAM,EACJ,aACA,oBACA,0BACA,4BACA,2BACA,kCACA,oCACA,iBACA,kBACE,GACE,IAAS,EAAa,EAAS,EAW/B,EAAE,MAAM,MACZ,
|
|
1
|
+
{"version":3,"file":"TableQueryCardDeck.js","names":[],"sources":["../../../src/components/CardDeck/TableQueryCardDeck.tsx"],"sourcesContent":["import { CardDeck } from '@/components/CardDeck/CardDeck'\nimport { CardDeckCardProps } from '@/components/CardDeck/CardDeckCardProps'\nimport { ImageFileHandle } from '@/components/widgets/ImageFileHandle'\nimport { useGetEntity } from '@/synapse-queries/entity/useEntity'\nimport useGetQueryResultBundle from '@/synapse-queries/entity/useGetQueryResultBundle'\nimport { SynapseConstants } from '@/utils'\nimport { getFieldIndex } from '@/utils/functions/queryUtils'\nimport { QueryBundleRequest } from '@sage-bionetworks/synapse-types'\nimport { CardLink } from '../CardContainer/CardLink'\nimport { getFileHandleAssociation, getLinkParams } from '../GenericCard'\n\nexport type TableQueryCardDeckProps = {\n entityId: string /* The table entity which should be queried */\n titleColumnName: string /* The column name for the title */\n descriptionColumnName: string /* The column name for the description */\n ctaButtonTextColumnName: string /* The column name for the call to action button text */\n ctaButtonURLColumnName: string /* The column name for the call to action button URL */\n titleIconFileHandleColumnName?: string /* The column name for the title icon image file */\n headerImageFileHandleColumnName?: string /* The column name for the header image file */\n cardDeckType?: 'cckp' | 'b2ai' /* The type of card deck (cckp or b2ai)*/\n linkConfig: CardLink /* The link configuration */\n}\n\n/**\n * Transforms a Synapse table into a card deck.\n */\nexport function TableQueryCardDeck(props: TableQueryCardDeckProps) {\n const {\n entityId,\n titleColumnName,\n descriptionColumnName,\n ctaButtonTextColumnName,\n ctaButtonURLColumnName,\n titleIconFileHandleColumnName,\n headerImageFileHandleColumnName,\n cardDeckType,\n linkConfig,\n } = props\n const entity = useGetEntity(entityId)\n const queryBundleRequest: QueryBundleRequest = {\n concreteType: 'org.sagebionetworks.repo.model.table.QueryBundleRequest',\n entityId,\n partMask:\n SynapseConstants.BUNDLE_MASK_QUERY_SELECT_COLUMNS |\n SynapseConstants.BUNDLE_MASK_QUERY_RESULTS,\n query: {\n sql: `select * from ${entityId}`,\n },\n }\n const { data: queryResultBundle } =\n useGetQueryResultBundle(queryBundleRequest)\n\n const titleColumnIndex = getFieldIndex(titleColumnName, queryResultBundle)\n const descriptionColumnIndex = getFieldIndex(\n descriptionColumnName,\n queryResultBundle,\n )\n const ctaButtonTextColumnIndex = getFieldIndex(\n ctaButtonTextColumnName,\n queryResultBundle,\n )\n const ctaButtonURLColumnIndex = getFieldIndex(\n ctaButtonURLColumnName,\n queryResultBundle,\n )\n const titleIconColumnIndex = getFieldIndex(\n titleIconFileHandleColumnName,\n queryResultBundle,\n )\n const headerImageColumnIndex = getFieldIndex(\n headerImageFileHandleColumnName,\n queryResultBundle,\n )\n\n const cards = queryResultBundle?.queryResult!.queryResults.rows.map(\n (el): CardDeckCardProps => {\n const values = el.values as string[]\n if (values.some(value => value === null)) {\n // We cast values above assuming there are no null values, emit a warning just in case.\n console.warn('Row has null value(s) when no nulls expected')\n }\n\n const title = values[titleColumnIndex]\n const description = values[descriptionColumnIndex]\n const ctaButtonText = values[ctaButtonTextColumnIndex]\n const ctaButtonURL = values[ctaButtonURLColumnIndex] ?? ''\n const titleIconFileHandleIdValue = values[titleIconColumnIndex] ?? ''\n const headerImageFileHandleIdValue = values[headerImageColumnIndex] ?? ''\n\n const schema: Record<string, number> = {}\n queryResultBundle?.queryResult!.queryResults.headers.forEach(\n (header, colIndex) => {\n schema[header.name] = colIndex\n },\n )\n const { href } = getLinkParams(ctaButtonURL, linkConfig, values, schema)\n\n const titleIconFileHandleAssociation = getFileHandleAssociation(\n entity.data,\n titleIconFileHandleIdValue,\n `syn${el.rowId}`,\n )\n\n const titleIcon = titleIconFileHandleAssociation ? (\n <ImageFileHandle\n fileHandleAssociation={titleIconFileHandleAssociation}\n />\n ) : undefined\n\n const headerImageFileHandleAssociation = getFileHandleAssociation(\n entity.data,\n headerImageFileHandleIdValue,\n `syn${el.rowId}`,\n )\n\n const headerImage = headerImageFileHandleAssociation ? (\n <ImageFileHandle\n fileHandleAssociation={headerImageFileHandleAssociation}\n />\n ) : undefined\n\n return {\n title,\n description,\n ctaButtonText,\n ctaButtonURL: href,\n titleIcon,\n headerImage,\n cardDeckType,\n } satisfies CardDeckCardProps\n },\n )\n\n return <CardDeck cards={cards || []} cardDeckType={cardDeckType} />\n}\n\nexport default TableQueryCardDeck\n"],"mappings":";;;;;;;;;;;AA0BA,SAAgB,EAAmB,GAAgC;CACjE,IAAM,EACJ,aACA,oBACA,0BACA,4BACA,2BACA,kCACA,oCACA,iBACA,kBACE,GACE,IAAS,EAAa,EAAS,EAW/B,EAAE,MAAM,MACZ,EAAwB;EAVxB,cAAc;EACd;EACA,UACE;EAEF,OAAO,EACL,KAAK,iBAAiB,KACvB;EAGuB,CAAmB,EAEvC,IAAmB,EAAc,GAAiB,EAAkB,EACpE,IAAyB,EAC7B,GACA,EACD,EACK,IAA2B,EAC/B,GACA,EACD,EACK,IAA0B,EAC9B,GACA,EACD,EACK,IAAuB,EAC3B,GACA,EACD,EACK,IAAyB,EAC7B,GACA,EACD,EAEK,IAAQ,GAAmB,YAAa,aAAa,KAAK,KAC7D,MAA0B;EACzB,IAAM,IAAS,EAAG;AAClB,EAAI,EAAO,MAAK,MAAS,MAAU,KAAK,IAEtC,QAAQ,KAAK,+CAA+C;EAG9D,IAAM,IAAQ,EAAO,IACf,IAAc,EAAO,IACrB,IAAgB,EAAO,IACvB,IAAe,EAAO,MAA4B,IAClD,IAA6B,EAAO,MAAyB,IAC7D,IAA+B,EAAO,MAA2B,IAEjE,IAAiC,EAAE;AACzC,KAAmB,YAAa,aAAa,QAAQ,SAClD,GAAQ,MAAa;AACpB,KAAO,EAAO,QAAQ;IAEzB;EACD,IAAM,EAAE,YAAS,EAAc,GAAc,GAAY,GAAQ,EAAO,EAElE,IAAiC,EACrC,EAAO,MACP,GACA,MAAM,EAAG,QACV,EAEK,IAAY,IAChB,kBAAC,GAAD,EACE,uBAAuB,GACvB,CAAA,GACA,KAAA,GAEE,IAAmC,EACvC,EAAO,MACP,GACA,MAAM,EAAG,QACV;AAQD,SAAO;GACL;GACA;GACA;GACA,cAAc;GACd;GACA,aAZkB,IAClB,kBAAC,GAAD,EACE,uBAAuB,GACvB,CAAA,GACA,KAAA;GASF;GACD;GAEJ;AAED,QAAO,kBAAC,GAAD;EAAU,OAAO,KAAS,EAAE;EAAgB;EAAgB,CAAA"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"CertificationQuiz.js","names":[],"sources":["../../../src/components/CertificationQuiz/CertificationQuiz.tsx"],"sourcesContent":["import SynapseClient from '@/synapse-client'\nimport { useGetCurrentUserBundle } from '@/synapse-queries'\nimport {\n useGetPassingRecord,\n usePostCertifiedUserTestResponse,\n} from '@/synapse-queries/user/useCertificationQuiz'\nimport { useSynapseContext } from '@/utils/context/SynapseContext'\nimport { formatDate } from '@/utils/functions/DateFormatter'\nimport { USER_BUNDLE_MASK_IS_CERTIFIED } from '@/utils/SynapseConstants'\nimport { HelpOutlineTwoTone } from '@mui/icons-material'\nimport {\n Alert,\n AlertTitle,\n Box,\n Button,\n ButtonProps,\n Link,\n Skeleton,\n Typography,\n} from '@mui/material'\nimport {\n MULTICHOICE_RESPONSE_CONCRETE_TYPE_VALUE,\n MultichoiceQuestion,\n MultichoiceResponse,\n QuestionResponse,\n Quiz,\n QuizResponse,\n} from '@sage-bionetworks/synapse-types'\nimport dayjs from 'dayjs'\nimport { ReactNode, useEffect, useRef, useState } from 'react'\nimport { useErrorHandler } from 'react-error-boundary'\nimport { MarkdownPopover } from '../Markdown/MarkdownPopover'\nimport { SkeletonParagraph, SkeletonTable } from '../Skeleton'\nimport { displayToast } from '../ToastMessage/ToastMessage'\nimport CertificationAnswer from './CertificationAnswer'\n\nfunction CertificationQuiz() {\n const { accessToken, isAuthenticated } = useSynapseContext()\n const handleError = useErrorHandler()\n const [quiz, setQuiz] = useState<Quiz | undefined>()\n const [isRetakingQuiz, setIsRetakingQuiz] = useState(false)\n const [questionResponse, setQuestionResponse] = useState<QuestionResponse[]>(\n [],\n )\n const { data: currentUserBundle } = useGetCurrentUserBundle(\n USER_BUNDLE_MASK_IS_CERTIFIED,\n )\n const isCertified = currentUserBundle?.isCertified\n const userId = currentUserBundle?.userId\n const { data: passingRecord, isLoading: isLoadingPassingRecord } =\n useGetPassingRecord(userId, {\n enabled: userId !== undefined,\n })\n const formRef = useRef<HTMLFormElement>(null)\n const GETTING_STARTED_URL =\n 'https://help.synapse.org/docs/Getting-Started.2055471150.html'\n\n const getQuiz = async () => {\n try {\n setQuiz(await SynapseClient.getCertifyQuiz(accessToken))\n } catch (err: any) {\n handleError(err)\n }\n }\n\n const { mutate: postCertifiedUserTestResponse, isPending: isSubmitting } =\n usePostCertifiedUserTestResponse({\n onSuccess: () => {\n setIsRetakingQuiz(false)\n window.scrollTo(0, 0)\n },\n throwOnError: true,\n })\n // user is taking the quiz if user is not certified, and either there is no passing record or if the user clicked to retake the quiz\n const isTakingQuiz = !isCertified && (passingRecord == null || isRetakingQuiz)\n useEffect(() => {\n if (isAuthenticated) {\n getQuiz()\n }\n }, [isAuthenticated])\n\n if (isLoadingPassingRecord || quiz == undefined) {\n return <CertificationQuizSkeleton />\n }\n\n const onUpdateAnswer = (questionIndex: number, answer: number) => {\n const newState = [\n ...questionResponse.filter(obj => obj.questionIndex !== questionIndex),\n {\n questionIndex,\n answerIndex: [answer],\n concreteType: MULTICHOICE_RESPONSE_CONCRETE_TYPE_VALUE,\n },\n ]\n setQuestionResponse(newState)\n }\n\n const handleRetakeQuiz = () => {\n formRef.current?.reset()\n setIsRetakingQuiz(true)\n setQuestionResponse([])\n getQuiz()\n }\n\n const handleSubmit = () => {\n try {\n if (quiz && quiz.questions.length === questionResponse.length) {\n const quizResponse: QuizResponse = {\n quizId: quiz.id,\n questionResponses: questionResponse,\n }\n postCertifiedUserTestResponse(quizResponse)\n } else {\n displayToast(\n 'Please answer all of the questions and try again.',\n 'warning',\n )\n }\n } catch (err: any) {\n displayToast(err.reason as string, 'danger')\n }\n }\n\n const actionButtonConfig = (helpUrl: string) =>\n helpUrl\n ? {\n content: <>More info</>,\n closePopoverOnClick: true,\n onClick: () => window.open(helpUrl, '_blank'),\n color: 'primary' as ButtonProps['color'],\n }\n : undefined\n return (\n <div>\n {passingRecord && !isTakingQuiz && (\n <>\n {(passingRecord.revokedOn || passingRecord.passed) &&\n !isCertified && (\n <Alert severity=\"error\">\n {!passingRecord.passed && (\n <AlertTitle>Certification Revoked</AlertTitle>\n )}\n <Typography variant=\"body1\" sx={{ marginTop: '5px' }}>\n Your certification was revoked. To become certified, you must{' '}\n <Link\n href=\"#\"\n onClick={e => {\n e.preventDefault()\n handleRetakeQuiz()\n }}\n >\n retake the quiz\n </Link>\n .\n </Typography>\n </Alert>\n )}\n {!passingRecord.passed && !isCertified && (\n <Alert severity=\"error\">\n {!passingRecord.passed && <AlertTitle>Quiz Failed</AlertTitle>}\n <Typography variant=\"body1\" sx={{ marginTop: '5px' }}>\n Please review the results from the previous attempt, and{' '}\n <Link\n href=\"#\"\n onClick={e => {\n e.preventDefault()\n handleRetakeQuiz()\n }}\n >\n retake the quiz\n </Link>\n .\n </Typography>\n </Alert>\n )}\n {passingRecord.passed && isCertified && (\n <Alert severity=\"success\">\n <AlertTitle>Quiz Passed</AlertTitle>\n <Typography variant=\"body1\" sx={{ marginTop: '5px' }}>\n {`You passed the Synapse Certification Quiz on ${formatDate(\n dayjs(passingRecord.passedOn),\n )}`}\n </Typography>\n </Alert>\n )}\n <Typography\n variant=\"sectionTitle\"\n sx={{ marginTop: '20px', marginBottom: '20px' }}\n >\n Score: {passingRecord.score} / {passingRecord.corrections?.length}\n </Typography>\n </>\n )}\n <div className=\"CertificationQuiz__container\">\n {quiz && isTakingQuiz && (\n <>\n <Button\n onClick={() => window.open(GETTING_STARTED_URL, '_blank')}\n className=\"help-button\"\n color=\"primary\"\n variant=\"contained\"\n >\n <Box sx={{ display: 'flex', alignItems: 'center' }}>\n <HelpOutlineTwoTone\n className=\"HelpButton\"\n style={{ marginRight: '4px', color: 'white' }}\n />\n <>Help</>\n </Box>\n </Button>\n <Typography variant=\"headline1\" sx={{ marginBottom: '25px' }}>\n Certified User Quiz\n </Typography>\n <Typography variant=\"body1\" sx={{ marginBottom: '15px' }}>\n Certified users are authorized to use the full Synapse\n functionality. In order to ensure that users who create content in\n the system and/or wish to interact more freely within Synapse are\n familiar with the governance process and Synapse operating\n procedures when dealing with possibly sensitive data, users must\n pass a short quiz (approximately 15 minutes) to become Synapse\n certified.\n </Typography>\n <Typography variant=\"body1\" sx={{ marginBottom: '15px' }}>\n Please answer the following questions to become certified.\n </Typography>\n <form ref={formRef} onSubmit={e => e.preventDefault()}>\n <ol>\n {quiz?.questions.map(question => {\n const isHelp = question.helpText || question.docLink\n return (\n <li\n key={question.questionIndex}\n role={question.exclusive ? 'radiogroup' : undefined}\n >\n <Typography\n variant={'headline3'}\n dangerouslySetInnerHTML={{ __html: question.prompt }}\n sx={{ marginTop: '40px', marginBottom: '10px' }}\n />\n {question.answers.map(choice => (\n <CertificationAnswer\n key={`${question.questionIndex}-${choice.answerIndex}`}\n question={question}\n answer={choice}\n onClick={e =>\n onUpdateAnswer(\n question.questionIndex,\n Number(e.currentTarget.value),\n )\n }\n />\n ))}\n {isHelp && (\n <MarkdownPopover\n contentProps={{ markdown: question.helpText }}\n placement=\"right\"\n sx={{\n table: {\n textAlign: 'center',\n th: {\n padding: '5px',\n },\n },\n }}\n actionButton={actionButtonConfig(question.docLink)}\n showCloseButton={true}\n >\n <Box\n sx={{\n display: 'flex',\n alignItems: 'center',\n marginTop: '10px',\n }}\n >\n <HelpOutlineTwoTone\n className=\"HelpButton\"\n style={{ marginRight: '4px' }}\n />\n <Link>Need help answering this question?</Link>\n </Box>\n </MarkdownPopover>\n )}\n </li>\n )\n })}\n </ol>\n </form>\n <Button\n className=\"help-button\"\n color=\"primary\"\n variant=\"contained\"\n size=\"large\"\n disabled={isSubmitting}\n onClick={() => {\n handleSubmit()\n }}\n >\n Submit\n </Button>\n </>\n )}\n\n {!isTakingQuiz && passingRecord?.corrections && (\n <ol>\n {passingRecord.corrections.map(responseCorrectness => {\n const question =\n responseCorrectness.question as MultichoiceQuestion\n const response =\n responseCorrectness.response as MultichoiceResponse\n return (\n <li key={question.questionIndex}>\n <Typography\n variant={'headline3'}\n dangerouslySetInnerHTML={{ __html: question.prompt }}\n className={\n responseCorrectness.isCorrect ? '' : 'incorrectQuestion'\n }\n sx={{ marginTop: '40px', marginBottom: '10px' }}\n />\n {question.answers.map(choice => (\n <CertificationAnswer\n key={`${question.questionIndex}-${choice.answerIndex}`}\n question={question}\n answer={choice}\n disabled={true}\n checked={response.answerIndex.includes(\n choice.answerIndex,\n )}\n />\n ))}\n </li>\n )\n })}\n </ol>\n )}\n </div>\n </div>\n )\n}\n\nfunction CertificationQuizSkeleton() {\n const questions: ReactNode[] = []\n for (let i = 0; i < 20; i++) {\n questions.push(\n <li key={i}>\n <SkeletonTable numCols={1} numRows={5} />\n </li>,\n )\n }\n return (\n <>\n <div>\n <Skeleton height=\"80px\" width=\"460px\" />\n </div>\n <SkeletonParagraph numRows={5} />\n <div className=\"CertificationQuiz__container\">\n <ol style={{ marginTop: '20px' }}>{questions}</ol>\n </div>\n </>\n )\n}\n\nexport default CertificationQuiz\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"CertificationQuiz.js","names":[],"sources":["../../../src/components/CertificationQuiz/CertificationQuiz.tsx"],"sourcesContent":["import SynapseClient from '@/synapse-client'\nimport { useGetCurrentUserBundle } from '@/synapse-queries'\nimport {\n useGetPassingRecord,\n usePostCertifiedUserTestResponse,\n} from '@/synapse-queries/user/useCertificationQuiz'\nimport { useSynapseContext } from '@/utils/context/SynapseContext'\nimport { formatDate } from '@/utils/functions/DateFormatter'\nimport { USER_BUNDLE_MASK_IS_CERTIFIED } from '@/utils/SynapseConstants'\nimport { HelpOutlineTwoTone } from '@mui/icons-material'\nimport {\n Alert,\n AlertTitle,\n Box,\n Button,\n ButtonProps,\n Link,\n Skeleton,\n Typography,\n} from '@mui/material'\nimport {\n MULTICHOICE_RESPONSE_CONCRETE_TYPE_VALUE,\n MultichoiceQuestion,\n MultichoiceResponse,\n QuestionResponse,\n Quiz,\n QuizResponse,\n} from '@sage-bionetworks/synapse-types'\nimport dayjs from 'dayjs'\nimport { ReactNode, useEffect, useRef, useState } from 'react'\nimport { useErrorHandler } from 'react-error-boundary'\nimport { MarkdownPopover } from '../Markdown/MarkdownPopover'\nimport { SkeletonParagraph, SkeletonTable } from '../Skeleton'\nimport { displayToast } from '../ToastMessage/ToastMessage'\nimport CertificationAnswer from './CertificationAnswer'\n\nfunction CertificationQuiz() {\n const { accessToken, isAuthenticated } = useSynapseContext()\n const handleError = useErrorHandler()\n const [quiz, setQuiz] = useState<Quiz | undefined>()\n const [isRetakingQuiz, setIsRetakingQuiz] = useState(false)\n const [questionResponse, setQuestionResponse] = useState<QuestionResponse[]>(\n [],\n )\n const { data: currentUserBundle } = useGetCurrentUserBundle(\n USER_BUNDLE_MASK_IS_CERTIFIED,\n )\n const isCertified = currentUserBundle?.isCertified\n const userId = currentUserBundle?.userId\n const { data: passingRecord, isLoading: isLoadingPassingRecord } =\n useGetPassingRecord(userId, {\n enabled: userId !== undefined,\n })\n const formRef = useRef<HTMLFormElement>(null)\n const GETTING_STARTED_URL =\n 'https://help.synapse.org/docs/Getting-Started.2055471150.html'\n\n const getQuiz = async () => {\n try {\n setQuiz(await SynapseClient.getCertifyQuiz(accessToken))\n } catch (err: any) {\n handleError(err)\n }\n }\n\n const { mutate: postCertifiedUserTestResponse, isPending: isSubmitting } =\n usePostCertifiedUserTestResponse({\n onSuccess: () => {\n setIsRetakingQuiz(false)\n window.scrollTo(0, 0)\n },\n throwOnError: true,\n })\n // user is taking the quiz if user is not certified, and either there is no passing record or if the user clicked to retake the quiz\n const isTakingQuiz = !isCertified && (passingRecord == null || isRetakingQuiz)\n useEffect(() => {\n if (isAuthenticated) {\n getQuiz()\n }\n }, [isAuthenticated])\n\n if (isLoadingPassingRecord || quiz == undefined) {\n return <CertificationQuizSkeleton />\n }\n\n const onUpdateAnswer = (questionIndex: number, answer: number) => {\n const newState = [\n ...questionResponse.filter(obj => obj.questionIndex !== questionIndex),\n {\n questionIndex,\n answerIndex: [answer],\n concreteType: MULTICHOICE_RESPONSE_CONCRETE_TYPE_VALUE,\n },\n ]\n setQuestionResponse(newState)\n }\n\n const handleRetakeQuiz = () => {\n formRef.current?.reset()\n setIsRetakingQuiz(true)\n setQuestionResponse([])\n getQuiz()\n }\n\n const handleSubmit = () => {\n try {\n if (quiz && quiz.questions.length === questionResponse.length) {\n const quizResponse: QuizResponse = {\n quizId: quiz.id,\n questionResponses: questionResponse,\n }\n postCertifiedUserTestResponse(quizResponse)\n } else {\n displayToast(\n 'Please answer all of the questions and try again.',\n 'warning',\n )\n }\n } catch (err: any) {\n displayToast(err.reason as string, 'danger')\n }\n }\n\n const actionButtonConfig = (helpUrl: string) =>\n helpUrl\n ? {\n content: <>More info</>,\n closePopoverOnClick: true,\n onClick: () => window.open(helpUrl, '_blank'),\n color: 'primary' as ButtonProps['color'],\n }\n : undefined\n return (\n <div>\n {passingRecord && !isTakingQuiz && (\n <>\n {(passingRecord.revokedOn || passingRecord.passed) &&\n !isCertified && (\n <Alert severity=\"error\">\n {!passingRecord.passed && (\n <AlertTitle>Certification Revoked</AlertTitle>\n )}\n <Typography variant=\"body1\" sx={{ marginTop: '5px' }}>\n Your certification was revoked. To become certified, you must{' '}\n <Link\n href=\"#\"\n onClick={e => {\n e.preventDefault()\n handleRetakeQuiz()\n }}\n >\n retake the quiz\n </Link>\n .\n </Typography>\n </Alert>\n )}\n {!passingRecord.passed && !isCertified && (\n <Alert severity=\"error\">\n {!passingRecord.passed && <AlertTitle>Quiz Failed</AlertTitle>}\n <Typography variant=\"body1\" sx={{ marginTop: '5px' }}>\n Please review the results from the previous attempt, and{' '}\n <Link\n href=\"#\"\n onClick={e => {\n e.preventDefault()\n handleRetakeQuiz()\n }}\n >\n retake the quiz\n </Link>\n .\n </Typography>\n </Alert>\n )}\n {passingRecord.passed && isCertified && (\n <Alert severity=\"success\">\n <AlertTitle>Quiz Passed</AlertTitle>\n <Typography variant=\"body1\" sx={{ marginTop: '5px' }}>\n {`You passed the Synapse Certification Quiz on ${formatDate(\n dayjs(passingRecord.passedOn),\n )}`}\n </Typography>\n </Alert>\n )}\n <Typography\n variant=\"sectionTitle\"\n sx={{ marginTop: '20px', marginBottom: '20px' }}\n >\n Score: {passingRecord.score} / {passingRecord.corrections?.length}\n </Typography>\n </>\n )}\n <div className=\"CertificationQuiz__container\">\n {quiz && isTakingQuiz && (\n <>\n <Button\n onClick={() => window.open(GETTING_STARTED_URL, '_blank')}\n className=\"help-button\"\n color=\"primary\"\n variant=\"contained\"\n >\n <Box sx={{ display: 'flex', alignItems: 'center' }}>\n <HelpOutlineTwoTone\n className=\"HelpButton\"\n style={{ marginRight: '4px', color: 'white' }}\n />\n <>Help</>\n </Box>\n </Button>\n <Typography variant=\"headline1\" sx={{ marginBottom: '25px' }}>\n Certified User Quiz\n </Typography>\n <Typography variant=\"body1\" sx={{ marginBottom: '15px' }}>\n Certified users are authorized to use the full Synapse\n functionality. In order to ensure that users who create content in\n the system and/or wish to interact more freely within Synapse are\n familiar with the governance process and Synapse operating\n procedures when dealing with possibly sensitive data, users must\n pass a short quiz (approximately 15 minutes) to become Synapse\n certified.\n </Typography>\n <Typography variant=\"body1\" sx={{ marginBottom: '15px' }}>\n Please answer the following questions to become certified.\n </Typography>\n <form ref={formRef} onSubmit={e => e.preventDefault()}>\n <ol>\n {quiz?.questions.map(question => {\n const isHelp = question.helpText || question.docLink\n return (\n <li\n key={question.questionIndex}\n role={question.exclusive ? 'radiogroup' : undefined}\n >\n <Typography\n variant={'headline3'}\n dangerouslySetInnerHTML={{ __html: question.prompt }}\n sx={{ marginTop: '40px', marginBottom: '10px' }}\n />\n {question.answers.map(choice => (\n <CertificationAnswer\n key={`${question.questionIndex}-${choice.answerIndex}`}\n question={question}\n answer={choice}\n onClick={e =>\n onUpdateAnswer(\n question.questionIndex,\n Number(e.currentTarget.value),\n )\n }\n />\n ))}\n {isHelp && (\n <MarkdownPopover\n contentProps={{ markdown: question.helpText }}\n placement=\"right\"\n sx={{\n table: {\n textAlign: 'center',\n th: {\n padding: '5px',\n },\n },\n }}\n actionButton={actionButtonConfig(question.docLink)}\n showCloseButton={true}\n >\n <Box\n sx={{\n display: 'flex',\n alignItems: 'center',\n marginTop: '10px',\n }}\n >\n <HelpOutlineTwoTone\n className=\"HelpButton\"\n style={{ marginRight: '4px' }}\n />\n <Link>Need help answering this question?</Link>\n </Box>\n </MarkdownPopover>\n )}\n </li>\n )\n })}\n </ol>\n </form>\n <Button\n className=\"help-button\"\n color=\"primary\"\n variant=\"contained\"\n size=\"large\"\n disabled={isSubmitting}\n onClick={() => {\n handleSubmit()\n }}\n >\n Submit\n </Button>\n </>\n )}\n\n {!isTakingQuiz && passingRecord?.corrections && (\n <ol>\n {passingRecord.corrections.map(responseCorrectness => {\n const question =\n responseCorrectness.question as MultichoiceQuestion\n const response =\n responseCorrectness.response as MultichoiceResponse\n return (\n <li key={question.questionIndex}>\n <Typography\n variant={'headline3'}\n dangerouslySetInnerHTML={{ __html: question.prompt }}\n className={\n responseCorrectness.isCorrect ? '' : 'incorrectQuestion'\n }\n sx={{ marginTop: '40px', marginBottom: '10px' }}\n />\n {question.answers.map(choice => (\n <CertificationAnswer\n key={`${question.questionIndex}-${choice.answerIndex}`}\n question={question}\n answer={choice}\n disabled={true}\n checked={response.answerIndex.includes(\n choice.answerIndex,\n )}\n />\n ))}\n </li>\n )\n })}\n </ol>\n )}\n </div>\n </div>\n )\n}\n\nfunction CertificationQuizSkeleton() {\n const questions: ReactNode[] = []\n for (let i = 0; i < 20; i++) {\n questions.push(\n <li key={i}>\n <SkeletonTable numCols={1} numRows={5} />\n </li>,\n )\n }\n return (\n <>\n <div>\n <Skeleton height=\"80px\" width=\"460px\" />\n </div>\n <SkeletonParagraph numRows={5} />\n <div className=\"CertificationQuiz__container\">\n <ol style={{ marginTop: '20px' }}>{questions}</ol>\n </div>\n </>\n )\n}\n\nexport default CertificationQuiz\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;AAoCA,SAAS,IAAoB;CAC3B,IAAM,EAAE,gBAAa,uBAAoB,GAAmB,EACtD,IAAc,GAAiB,EAC/B,CAAC,GAAM,KAAW,GAA4B,EAC9C,CAAC,GAAgB,KAAqB,EAAS,GAAM,EACrD,CAAC,GAAkB,KAAuB,EAC9C,EAAE,CACH,EACK,EAAE,MAAM,MAAsB,EAAA,EAEnC,EACK,IAAc,GAAmB,aACjC,IAAS,GAAmB,QAC5B,EAAE,MAAM,GAAe,WAAW,MACtC,EAAoB,GAAQ,EAC1B,SAAS,MAAW,KAAA,GACrB,CAAC,EACE,IAAU,EAAwB,KAAK,EAIvC,IAAU,YAAY;AAC1B,MAAI;AACF,KAAQ,MAAM,EAAc,eAAe,EAAY,CAAC;WACjD,GAAU;AACjB,KAAY,EAAI;;IAId,EAAE,QAAQ,GAA+B,WAAW,MACxD,EAAiC;EAC/B,iBAAiB;AAEf,GADA,EAAkB,GAAM,EACxB,OAAO,SAAS,GAAG,EAAE;;EAEvB,cAAc;EACf,CAAC,EAEE,IAAe,CAAC,MAAgB,KAAiB,QAAQ;AAO/D,KANA,QAAgB;AACd,EAAI,KACF,GAAS;IAEV,CAAC,EAAgB,CAAC,EAEjB,KAA0B,KAAQ,KACpC,QAAO,kBAAC,GAAD,EAA6B,CAAA;CAGtC,IAAM,KAAkB,GAAuB,MAAmB;AAShE,IAAoB,CAPlB,GAAG,EAAiB,QAAO,MAAO,EAAI,kBAAkB,EAAc,EACtE;GACE;GACA,aAAa,CAAC,EAAO;GACrB,cAAc;GACf,CAEiB,CAAS;IAGzB,UAAyB;AAI7B,EAHA,EAAQ,SAAS,OAAO,EACxB,EAAkB,GAAK,EACvB,EAAoB,EAAE,CAAC,EACvB,GAAS;IAGL,UAAqB;AACzB,MAAI;AACF,GAAI,KAAQ,EAAK,UAAU,WAAW,EAAiB,SAKrD,EAA8B;IAH5B,QAAQ,EAAK;IACb,mBAAmB;IAES,CAAa,GAE3C,EACE,qDACA,UACD;WAEI,GAAU;AACjB,KAAa,EAAI,QAAkB,SAAS;;IAI1C,KAAsB,MAC1B,IACI;EACE,SAAS,kBAAA,GAAA,EAAA,UAAE,aAAY,CAAA;EACvB,qBAAqB;EACrB,eAAe,OAAO,KAAK,GAAS,SAAS;EAC7C,OAAO;EACR,GACD,KAAA;AACN,QACE,kBAAC,OAAD,EAAA,UAAA,CACG,KAAiB,CAAC,KACjB,kBAAA,GAAA,EAAA,UAAA;GACI,EAAc,aAAa,EAAc,WACzC,CAAC,KACC,kBAAC,GAAD;GAAO,UAAS;aAAhB,CACG,CAAC,EAAc,UACd,kBAAC,GAAD,EAAA,UAAY,yBAAkC,CAAA,EAEhD,kBAAC,GAAD;IAAY,SAAQ;IAAQ,IAAI,EAAE,WAAW,OAAO;cAApD;KAAsD;KACU;KAC9D,kBAAC,GAAD;MACE,MAAK;MACL,UAAS,MAAK;AAEZ,OADA,EAAE,gBAAgB,EAClB,GAAkB;;gBAErB;MAEM,CAAA;;KAEI;MACP;;EAEX,CAAC,EAAc,UAAU,CAAC,KACzB,kBAAC,GAAD;GAAO,UAAS;aAAhB,CACG,CAAC,EAAc,UAAU,kBAAC,GAAD,EAAA,UAAY,eAAwB,CAAA,EAC9D,kBAAC,GAAD;IAAY,SAAQ;IAAQ,IAAI,EAAE,WAAW,OAAO;cAApD;KAAsD;KACK;KACzD,kBAAC,GAAD;MACE,MAAK;MACL,UAAS,MAAK;AAEZ,OADA,EAAE,gBAAgB,EAClB,GAAkB;;gBAErB;MAEM,CAAA;;KAEI;MACP;;EAET,EAAc,UAAU,KACvB,kBAAC,GAAD;GAAO,UAAS;aAAhB,CACE,kBAAC,GAAD,EAAA,UAAY,eAAwB,CAAA,EACpC,kBAAC,GAAD;IAAY,SAAQ;IAAQ,IAAI,EAAE,WAAW,OAAO;cACjD,gDAAgD,EAC/C,EAAM,EAAc,SAAS,CAC9B;IACU,CAAA,CACP;;EAEV,kBAAC,GAAD;GACE,SAAQ;GACR,IAAI;IAAE,WAAW;IAAQ,cAAc;IAAQ;aAFjD;IAGC;IACS,EAAc;IAAM;IAAI,EAAc,aAAa;IAChD;;EACZ,EAAA,CAAA,EAEL,kBAAC,OAAD;EAAK,WAAU;YAAf,CACG,KAAQ,KACP,kBAAA,GAAA,EAAA,UAAA;GACE,kBAAC,GAAD;IACE,eAAe,OAAO,KAAK,iEAAqB,SAAS;IACzD,WAAU;IACV,OAAM;IACN,SAAQ;cAER,kBAAC,GAAD;KAAK,IAAI;MAAE,SAAS;MAAQ,YAAY;MAAU;eAAlD,CACE,kBAAC,GAAD;MACE,WAAU;MACV,OAAO;OAAE,aAAa;OAAO,OAAO;OAAS;MAC7C,CAAA,EACF,kBAAA,GAAA,EAAA,UAAE,QAAO,CAAA,CACL;;IACC,CAAA;GACT,kBAAC,GAAD;IAAY,SAAQ;IAAY,IAAI,EAAE,cAAc,QAAQ;cAAE;IAEjD,CAAA;GACb,kBAAC,GAAD;IAAY,SAAQ;IAAQ,IAAI,EAAE,cAAc,QAAQ;cAAE;IAQ7C,CAAA;GACb,kBAAC,GAAD;IAAY,SAAQ;IAAQ,IAAI,EAAE,cAAc,QAAQ;cAAE;IAE7C,CAAA;GACb,kBAAC,QAAD;IAAM,KAAK;IAAS,WAAU,MAAK,EAAE,gBAAgB;cACnD,kBAAC,MAAD,EAAA,UACG,GAAM,UAAU,KAAI,MAAY;KAC/B,IAAM,IAAS,EAAS,YAAY,EAAS;AAC7C,YACE,kBAAC,MAAD;MAEE,MAAM,EAAS,YAAY,eAAe,KAAA;gBAF5C;OAIE,kBAAC,GAAD;QACE,SAAS;QACT,yBAAyB,EAAE,QAAQ,EAAS,QAAQ;QACpD,IAAI;SAAE,WAAW;SAAQ,cAAc;SAAQ;QAC/C,CAAA;OACD,EAAS,QAAQ,KAAI,MACpB,kBAAC,GAAD;QAEY;QACV,QAAQ;QACR,UAAS,MACP,EACE,EAAS,eACT,OAAO,EAAE,cAAc,MAAM,CAC9B;QAEH,EATK,GAAG,EAAS,cAAc,GAAG,EAAO,cASzC,CACF;OACD,KACC,kBAAC,GAAD;QACE,cAAc,EAAE,UAAU,EAAS,UAAU;QAC7C,WAAU;QACV,IAAI,EACF,OAAO;SACL,WAAW;SACX,IAAI,EACF,SAAS,OACV;SACF,EACF;QACD,cAAc,EAAmB,EAAS,QAAQ;QAClD,iBAAiB;kBAEjB,kBAAC,GAAD;SACE,IAAI;UACF,SAAS;UACT,YAAY;UACZ,WAAW;UACZ;mBALH,CAOE,kBAAC,GAAD;UACE,WAAU;UACV,OAAO,EAAE,aAAa,OAAO;UAC7B,CAAA,EACF,kBAAC,GAAD,EAAA,UAAM,sCAAyC,CAAA,CAC3C;;QACU,CAAA;OAEjB;QAnDE,EAAS,cAmDX;MAEP,EACC,CAAA;IACA,CAAA;GACP,kBAAC,GAAD;IACE,WAAU;IACV,OAAM;IACN,SAAQ;IACR,MAAK;IACL,UAAU;IACV,eAAe;AACb,QAAc;;cAEjB;IAEQ,CAAA;GACR,EAAA,CAAA,EAGJ,CAAC,KAAgB,GAAe,eAC/B,kBAAC,MAAD,EAAA,UACG,EAAc,YAAY,KAAI,MAAuB;GACpD,IAAM,IACJ,EAAoB,UAChB,IACJ,EAAoB;AACtB,UACE,kBAAC,MAAD,EAAA,UAAA,CACE,kBAAC,GAAD;IACE,SAAS;IACT,yBAAyB,EAAE,QAAQ,EAAS,QAAQ;IACpD,WACE,EAAoB,YAAY,KAAK;IAEvC,IAAI;KAAE,WAAW;KAAQ,cAAc;KAAQ;IAC/C,CAAA,EACD,EAAS,QAAQ,KAAI,MACpB,kBAAC,GAAD;IAEY;IACV,QAAQ;IACR,UAAU;IACV,SAAS,EAAS,YAAY,SAC5B,EAAO,YACR;IACD,EAPK,GAAG,EAAS,cAAc,GAAG,EAAO,cAOzC,CACF,CACC,EAAA,EApBI,EAAS,cAoBb;IAEP,EACC,CAAA,CAEH;IACF,EAAA,CAAA;;AAIV,SAAS,IAA4B;CACnC,IAAM,IAAyB,EAAE;AACjC,MAAK,IAAI,IAAI,GAAG,IAAI,IAAI,IACtB,GAAU,KACR,kBAAC,MAAD,EAAA,UACE,kBAAC,GAAD;EAAe,SAAS;EAAG,SAAS;EAAK,CAAA,EACtC,EAFI,EAEJ,CACN;AAEH,QACE,kBAAA,GAAA,EAAA,UAAA;EACE,kBAAC,OAAD,EAAA,UACE,kBAAC,GAAD;GAAU,QAAO;GAAO,OAAM;GAAU,CAAA,EACpC,CAAA;EACN,kBAAC,GAAD,EAAmB,SAAS,GAAK,CAAA;EACjC,kBAAC,OAAD;GAAK,WAAU;aACb,kBAAC,MAAD;IAAI,OAAO,EAAE,WAAW,QAAQ;cAAG;IAAe,CAAA;GAC9C,CAAA;EACL,EAAA,CAAA"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"ChallengeDataDownload.js","names":[],"sources":["../../../src/components/ChallengeDataDownload/ChallengeDataDownload.tsx"],"sourcesContent":["import { useAddFileBatchToDownloadList } from '@/synapse-queries'\nimport { useSynapseContext } from '@/utils'\nimport AddCircleTwoToneIcon from '@mui/icons-material/AddCircleTwoTone'\nimport { Box, Button } from '@mui/material'\nimport { EntityType } from '@sage-bionetworks/synapse-client'\nimport { useCallback } from 'react'\nimport { EntityChildrenDetails } from '../EntityFinder/details/configurations/EntityChildrenDetails'\nimport { EntityDetailsListSharedProps } from '../EntityFinder/details/EntityDetailsList'\nimport { DetailsViewColumn } from '../EntityFinder/details/view/DetailsView'\nimport { EntityFinderHeader } from '../EntityFinder/EntityFinderHeader'\nimport { useEntitySelection } from '../EntityFinder/useEntitySelection'\nimport { VersionSelectionType } from '../EntityFinder/VersionSelectionType'\nimport { displayToast } from '../ToastMessage'\n\nexport type ChallengeDataDownloadProps = {\n parentContainerId: string\n}\n\nexport function ChallengeDataDownload({\n parentContainerId,\n}: ChallengeDataDownloadProps) {\n const { downloadCartPageUrl } = useSynapseContext()\n const selectMultiple = true\n const { selectedEntities, toggleSelection, setInitialVersion } =\n useEntitySelection(selectMultiple)\n\n const { mutate: addBatchToDownloadList } = useAddFileBatchToDownloadList({\n onSuccess: () => {\n displayToast(\n 'File(s) were successfully added to your Download List.',\n 'success',\n {\n primaryButtonConfig: {\n text: 'View Download List',\n onClick: () => (window.location.href = downloadCartPageUrl),\n },\n },\n )\n },\n onError: e => {\n displayToast(\n `Unable to add the file to your Download List. ${e.reason}`,\n 'danger',\n )\n },\n })\n\n const isIdSelected = useCallback(\n (entity: EntityFinderHeader) => {\n return selectedEntities.has(entity.id)\n },\n [selectedEntities],\n )\n\n const onAddClick = useCallback(() => {\n const entities = Array.from(selectedEntities.values()).map(reference => {\n return {\n fileEntityId: reference.targetId,\n versionNumber: reference.targetVersionNumber,\n }\n })\n addBatchToDownloadList(entities)\n }, [selectedEntities, addBatchToDownloadList])\n\n const sharedProps: EntityDetailsListSharedProps = {\n versionSelection: VersionSelectionType.REQUIRED,\n selectColumnType: 'checkbox',\n enableSelectAll: true,\n visibleTypes: [EntityType.file],\n selected: selectedEntities,\n selectableTypes: [EntityType.file],\n isIdSelected,\n isSelectable: () => true,\n toggleSelection,\n setInitialVersion,\n hiddenColumns: [\n DetailsViewColumn.BADGES,\n DetailsViewColumn.ADD_TO_DOWNLOAD_CART,\n DetailsViewColumn.CREATED_ON,\n DetailsViewColumn.VERSION,\n DetailsViewColumn.MODIFIED_BY,\n ],\n }\n return (\n <>\n <Box>\n <EntityChildrenDetails\n parentContainerId={parentContainerId}\n {...sharedProps}\n />\n </Box>\n <Box\n sx={{\n mt: 4,\n }}\n >\n <Button\n endIcon={<AddCircleTwoToneIcon />}\n variant={'outlined'}\n sx={{ alignSelf: 'flex-end', height: '47px' }}\n onClick={onAddClick}\n disabled={!selectedEntities.toArray().length}\n >\n Add to Download List\n </Button>\n </Box>\n </>\n )\n}\n"],"mappings":";;;;;;;;;;;;;;;;AAkBA,SAAgB,EAAsB,EACpC,wBAC6B;CAC7B,IAAM,EAAE,2BAAwB,GAAmB,EAE7C,EAAE,qBAAkB,oBAAiB,yBACzC,
|
|
1
|
+
{"version":3,"file":"ChallengeDataDownload.js","names":[],"sources":["../../../src/components/ChallengeDataDownload/ChallengeDataDownload.tsx"],"sourcesContent":["import { useAddFileBatchToDownloadList } from '@/synapse-queries'\nimport { useSynapseContext } from '@/utils'\nimport AddCircleTwoToneIcon from '@mui/icons-material/AddCircleTwoTone'\nimport { Box, Button } from '@mui/material'\nimport { EntityType } from '@sage-bionetworks/synapse-client'\nimport { useCallback } from 'react'\nimport { EntityChildrenDetails } from '../EntityFinder/details/configurations/EntityChildrenDetails'\nimport { EntityDetailsListSharedProps } from '../EntityFinder/details/EntityDetailsList'\nimport { DetailsViewColumn } from '../EntityFinder/details/view/DetailsView'\nimport { EntityFinderHeader } from '../EntityFinder/EntityFinderHeader'\nimport { useEntitySelection } from '../EntityFinder/useEntitySelection'\nimport { VersionSelectionType } from '../EntityFinder/VersionSelectionType'\nimport { displayToast } from '../ToastMessage'\n\nexport type ChallengeDataDownloadProps = {\n parentContainerId: string\n}\n\nexport function ChallengeDataDownload({\n parentContainerId,\n}: ChallengeDataDownloadProps) {\n const { downloadCartPageUrl } = useSynapseContext()\n const selectMultiple = true\n const { selectedEntities, toggleSelection, setInitialVersion } =\n useEntitySelection(selectMultiple)\n\n const { mutate: addBatchToDownloadList } = useAddFileBatchToDownloadList({\n onSuccess: () => {\n displayToast(\n 'File(s) were successfully added to your Download List.',\n 'success',\n {\n primaryButtonConfig: {\n text: 'View Download List',\n onClick: () => (window.location.href = downloadCartPageUrl),\n },\n },\n )\n },\n onError: e => {\n displayToast(\n `Unable to add the file to your Download List. ${e.reason}`,\n 'danger',\n )\n },\n })\n\n const isIdSelected = useCallback(\n (entity: EntityFinderHeader) => {\n return selectedEntities.has(entity.id)\n },\n [selectedEntities],\n )\n\n const onAddClick = useCallback(() => {\n const entities = Array.from(selectedEntities.values()).map(reference => {\n return {\n fileEntityId: reference.targetId,\n versionNumber: reference.targetVersionNumber,\n }\n })\n addBatchToDownloadList(entities)\n }, [selectedEntities, addBatchToDownloadList])\n\n const sharedProps: EntityDetailsListSharedProps = {\n versionSelection: VersionSelectionType.REQUIRED,\n selectColumnType: 'checkbox',\n enableSelectAll: true,\n visibleTypes: [EntityType.file],\n selected: selectedEntities,\n selectableTypes: [EntityType.file],\n isIdSelected,\n isSelectable: () => true,\n toggleSelection,\n setInitialVersion,\n hiddenColumns: [\n DetailsViewColumn.BADGES,\n DetailsViewColumn.ADD_TO_DOWNLOAD_CART,\n DetailsViewColumn.CREATED_ON,\n DetailsViewColumn.VERSION,\n DetailsViewColumn.MODIFIED_BY,\n ],\n }\n return (\n <>\n <Box>\n <EntityChildrenDetails\n parentContainerId={parentContainerId}\n {...sharedProps}\n />\n </Box>\n <Box\n sx={{\n mt: 4,\n }}\n >\n <Button\n endIcon={<AddCircleTwoToneIcon />}\n variant={'outlined'}\n sx={{ alignSelf: 'flex-end', height: '47px' }}\n onClick={onAddClick}\n disabled={!selectedEntities.toArray().length}\n >\n Add to Download List\n </Button>\n </Box>\n </>\n )\n}\n"],"mappings":";;;;;;;;;;;;;;;;AAkBA,SAAgB,EAAsB,EACpC,wBAC6B;CAC7B,IAAM,EAAE,2BAAwB,GAAmB,EAE7C,EAAE,qBAAkB,oBAAiB,yBACzC,EAAmB,GAAe,EAE9B,EAAE,QAAQ,MAA2B,EAA8B;EACvE,iBAAiB;AACf,KACE,0DACA,WACA,EACE,qBAAqB;IACnB,MAAM;IACN,eAAgB,OAAO,SAAS,OAAO;IACxC,EACF,CACF;;EAEH,UAAS,MAAK;AACZ,KACE,iDAAiD,EAAE,UACnD,SACD;;EAEJ,CAAC,EAEI,IAAe,GAClB,MACQ,EAAiB,IAAI,EAAO,GAAG,EAExC,CAAC,EAAiB,CACnB,EAEK,IAAa,QAAkB;AAOnC,IANiB,MAAM,KAAK,EAAiB,QAAQ,CAAC,CAAC,KAAI,OAClD;GACL,cAAc,EAAU;GACxB,eAAe,EAAU;GAC1B,EAEoB,CAAS;IAC/B,CAAC,GAAkB,EAAuB,CAAC;AAqB9C,QACE,kBAAA,GAAA,EAAA,UAAA,CACE,kBAAC,GAAD,EAAA,UACE,kBAAC,GAAD;EACqB;EAtBzB,kBAAkB,EAAqB;EACvC,kBAAkB;EAClB,iBAAiB;EACjB,cAAc,CAAC,EAAW,KAAK;EAC/B,UAAU;EACV,iBAAiB,CAAC,EAAW,KAAK;EAClC;EACA,oBAAoB;EACpB;EACA;EACA,eAAe;GACb,EAAkB;GAClB,EAAkB;GAClB,EAAkB;GAClB,EAAkB;GAClB,EAAkB;GACnB;EAQK,CAAA,EACE,CAAA,EACN,kBAAC,GAAD;EACE,IAAI,EACF,IAAI,GACL;YAED,kBAAC,GAAD;GACE,SAAS,kBAAC,GAAD,EAAwB,CAAA;GACjC,SAAS;GACT,IAAI;IAAE,WAAW;IAAY,QAAQ;IAAQ;GAC7C,SAAS;GACT,UAAU,CAAC,EAAiB,SAAS,CAAC;aACvC;GAEQ,CAAA;EACL,CAAA,CACL,EAAA,CAAA"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"ChallengeSubmission.js","names":[],"sources":["../../../src/components/ChallengeSubmission/ChallengeSubmission.tsx"],"sourcesContent":["import { createEntity } from '@/synapse-client'\nimport {\n useGetCurrentUserProfile,\n useGetEntityACL,\n useGetEntityAlias,\n useGetEntityChallenge,\n useGetEntityPermissions,\n useGetUserSubmissionTeams,\n useUpdateEntityACL,\n} from '@/synapse-queries'\nimport { useGetTeam } from '@/synapse-queries/team/useTeam'\nimport { useSynapseContext } from '@/utils'\nimport { EntityType } from '@sage-bionetworks/synapse-client'\nimport {\n ACCESS_TYPE,\n Challenge,\n Entity,\n Project,\n PROJECT_CONCRETE_TYPE_VALUE,\n ResourceAccess,\n Team,\n} from '@sage-bionetworks/synapse-types'\nimport { useEffect, useState } from 'react'\nimport { ErrorBanner, SynapseErrorBoundary } from '../error/ErrorBanner'\nimport ChallengeSubmissionStepper from './ChallengeSubmissionStepper'\nimport SubmissionDirectoryList from './SubmissionDirectoryList'\n\nexport type EntityItem = Entity & {\n repositoryName?: string\n versionNumber?: number\n}\n\nexport type ChallengeSubmissionProps = {\n entityType: typeof EntityType.dockerrepo | typeof EntityType.file\n pageSize: number\n projectId: string\n}\n\nexport function ChallengeSubmission({\n entityType,\n projectId,\n pageSize = 10,\n}: ChallengeSubmissionProps) {\n const { isAuthenticated, accessToken } = useSynapseContext()\n\n const [loading, setLoading] = useState<boolean>(true)\n const [errorMessage, setErrorMessage] = useState<string>()\n const [submissionTeamId, setSubmissionTeamId] = useState<string>()\n const [challengeProjectId, setChallengeProjectId] = useState<string>()\n const [newProject, setNewProject] = useState<Project>()\n const [isProjectNewlyCreated, setIsProjectNewlyCreated] = useState<boolean>()\n const [projectAliasFound, setProjectAliasFound] = useState<boolean>()\n const { mutate: updateACL } = useUpdateEntityACL()\n const [canSubmit, setCanSubmit] = useState<boolean>(false)\n const [selectedEntity, setSelectedEntity] = useState<EntityItem>()\n const [isShowingModal, setIsShowingModal] = useState<boolean>(false)\n\n const EMPTY_ID = ''\n\n const getProject = (c: Challenge, t: Team): Project => {\n const name = `Challenge Submission Project-${c.projectId}-${t.id}`\n const alias = name.replace(/\\s+/g, '_').replace(/-+/g, '_').toLowerCase()\n return {\n name,\n alias,\n concreteType: PROJECT_CONCRETE_TYPE_VALUE,\n description: `This Project was automatically created for Team \"${t.name}\" for Challenge \"${c.id}\"`,\n }\n }\n\n // Use the existing accessToken if present to get the current user's profile / userId\n const { data: userProfile, isLoading: isProfileLoading } =\n useGetCurrentUserProfile({\n enabled: isAuthenticated,\n throwOnError: true,\n })\n\n // Retrieve the challenge associated with the projectId passed through props\n const { data: challenge } = useGetEntityChallenge(projectId, {\n enabled: isAuthenticated && !!projectId,\n refetchInterval: Infinity,\n throwOnError: true,\n })\n\n // Determine whether or not the given user belongs to any submission teams\n const { data: userSubmissionTeams } = useGetUserSubmissionTeams(\n challenge?.id ?? EMPTY_ID,\n 2,\n )\n\n useEffect(() => {\n if (isAuthenticated && !!challenge && userSubmissionTeams) {\n const isReg = userSubmissionTeams.results.length > 0\n if (!isReg) {\n setErrorMessage(\n 'Error: Please join a Submission Team before continuing.',\n )\n return setLoading(false)\n }\n if (userSubmissionTeams.results.length > 1) {\n setErrorMessage(\n 'Error: You are a member of more than one Submission Team. You may only belong to one Submission Team per Challenge.',\n )\n return setLoading(false)\n }\n setSubmissionTeamId(userSubmissionTeams.results[0])\n }\n }, [challenge, isAuthenticated, userSubmissionTeams])\n\n const { data: submissionTeam } = useGetTeam(submissionTeamId!, {\n enabled: !!submissionTeamId,\n refetchInterval: Infinity,\n throwOnError: true,\n })\n\n const { data: entityAlias } = useGetEntityAlias(\n newProject?.alias ?? EMPTY_ID,\n {\n enabled: newProject !== undefined && !!challenge && !!submissionTeam,\n throwOnError: true,\n },\n )\n useEffect(() => {\n if (entityAlias) {\n setProjectAliasFound(true)\n setChallengeProjectId(entityAlias.id)\n } else {\n setProjectAliasFound(false)\n }\n }, [entityAlias])\n\n /**\n * If the challenge project was just created, retrieve its ACL\n * and add the submission team to it\n */\n const { data: entityACL } = useGetEntityACL(challengeProjectId ?? EMPTY_ID, {\n enabled: !!challengeProjectId && isProjectNewlyCreated === true,\n refetchInterval: Infinity,\n throwOnError: true,\n })\n\n useEffect(() => {\n if (entityACL && isProjectNewlyCreated === true) {\n // Give submission team admin access to challenge project\n const teamResourceAccess: ResourceAccess = {\n principalId: Number(submissionTeam!.id),\n accessType: [\n ACCESS_TYPE.CHANGE_PERMISSIONS,\n ACCESS_TYPE.CHANGE_SETTINGS,\n ACCESS_TYPE.CREATE,\n ACCESS_TYPE.DELETE,\n ACCESS_TYPE.DOWNLOAD,\n ACCESS_TYPE.MODERATE,\n ACCESS_TYPE.READ,\n ACCESS_TYPE.UPDATE,\n ],\n }\n updateACL({\n ...entityACL,\n resourceAccess: [...entityACL.resourceAccess, teamResourceAccess],\n })\n setIsProjectNewlyCreated(false)\n }\n }, [entityACL])\n\n const { data: entityPermissions } = useGetEntityPermissions(\n challengeProjectId!,\n {\n enabled: !!challengeProjectId,\n refetchInterval: Infinity,\n throwOnError: true,\n },\n )\n\n useEffect(() => {\n if (\n entityPermissions &&\n entityPermissions.canView &&\n entityPermissions.canAddChild\n ) {\n setCanSubmit(true)\n }\n setLoading(false)\n }, [entityPermissions])\n\n useEffect(() => {\n if (!isAuthenticated && (!!userProfile || !isProfileLoading)) {\n setLoading(false)\n setErrorMessage('Please login to continue.')\n }\n }, [isAuthenticated, userProfile, isProfileLoading])\n\n useEffect(() => {\n if (isAuthenticated && submissionTeam && challenge && !newProject) {\n const project = getProject(challenge, submissionTeam)\n setNewProject(project)\n }\n }, [isAuthenticated, submissionTeam, challenge, newProject])\n\n useEffect(() => {\n async function createChallengeProject() {\n const project: Project = getProject(challenge!, submissionTeam!)\n const challengeProject = await createEntity(project, accessToken)\n if (challengeProject && challengeProject.id) {\n setChallengeProjectId(challengeProject.id)\n setIsProjectNewlyCreated(true)\n }\n }\n if (\n isAuthenticated &&\n submissionTeam &&\n challenge &&\n newProject &&\n projectAliasFound === false\n ) {\n createChallengeProject()\n }\n }, [\n isAuthenticated,\n submissionTeam,\n challenge,\n newProject,\n projectAliasFound,\n accessToken,\n ])\n\n const itemSelectedHandler = (entity: EntityItem) => {\n setSelectedEntity(entity)\n setIsShowingModal(true)\n }\n\n const onModalClose = () => {\n setIsShowingModal(false)\n }\n\n return (\n <SynapseErrorBoundary>\n {loading && (\n <span data-testid=\"SpinnerButton-spinner\" className=\"spinner\" />\n )}\n {canSubmit && (\n <>\n <SubmissionDirectoryList\n entityType={entityType}\n pageSize={pageSize}\n challengeProjectId={challengeProjectId!}\n onItemSelected={itemSelectedHandler}\n />\n {userProfile && selectedEntity && (\n <ChallengeSubmissionStepper\n projectId={projectId}\n userId={userProfile?.ownerId}\n teamId={submissionTeamId!}\n entity={selectedEntity}\n entityType={entityType}\n isShowingModal={isShowingModal}\n onClose={onModalClose}\n />\n )}\n </>\n )}\n {errorMessage && <ErrorBanner error={errorMessage}></ErrorBanner>}\n </SynapseErrorBoundary>\n )\n}\n\nexport default ChallengeSubmission\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"ChallengeSubmission.js","names":[],"sources":["../../../src/components/ChallengeSubmission/ChallengeSubmission.tsx"],"sourcesContent":["import { createEntity } from '@/synapse-client'\nimport {\n useGetCurrentUserProfile,\n useGetEntityACL,\n useGetEntityAlias,\n useGetEntityChallenge,\n useGetEntityPermissions,\n useGetUserSubmissionTeams,\n useUpdateEntityACL,\n} from '@/synapse-queries'\nimport { useGetTeam } from '@/synapse-queries/team/useTeam'\nimport { useSynapseContext } from '@/utils'\nimport { EntityType } from '@sage-bionetworks/synapse-client'\nimport {\n ACCESS_TYPE,\n Challenge,\n Entity,\n Project,\n PROJECT_CONCRETE_TYPE_VALUE,\n ResourceAccess,\n Team,\n} from '@sage-bionetworks/synapse-types'\nimport { useEffect, useState } from 'react'\nimport { ErrorBanner, SynapseErrorBoundary } from '../error/ErrorBanner'\nimport ChallengeSubmissionStepper from './ChallengeSubmissionStepper'\nimport SubmissionDirectoryList from './SubmissionDirectoryList'\n\nexport type EntityItem = Entity & {\n repositoryName?: string\n versionNumber?: number\n}\n\nexport type ChallengeSubmissionProps = {\n entityType: typeof EntityType.dockerrepo | typeof EntityType.file\n pageSize: number\n projectId: string\n}\n\nexport function ChallengeSubmission({\n entityType,\n projectId,\n pageSize = 10,\n}: ChallengeSubmissionProps) {\n const { isAuthenticated, accessToken } = useSynapseContext()\n\n const [loading, setLoading] = useState<boolean>(true)\n const [errorMessage, setErrorMessage] = useState<string>()\n const [submissionTeamId, setSubmissionTeamId] = useState<string>()\n const [challengeProjectId, setChallengeProjectId] = useState<string>()\n const [newProject, setNewProject] = useState<Project>()\n const [isProjectNewlyCreated, setIsProjectNewlyCreated] = useState<boolean>()\n const [projectAliasFound, setProjectAliasFound] = useState<boolean>()\n const { mutate: updateACL } = useUpdateEntityACL()\n const [canSubmit, setCanSubmit] = useState<boolean>(false)\n const [selectedEntity, setSelectedEntity] = useState<EntityItem>()\n const [isShowingModal, setIsShowingModal] = useState<boolean>(false)\n\n const EMPTY_ID = ''\n\n const getProject = (c: Challenge, t: Team): Project => {\n const name = `Challenge Submission Project-${c.projectId}-${t.id}`\n const alias = name.replace(/\\s+/g, '_').replace(/-+/g, '_').toLowerCase()\n return {\n name,\n alias,\n concreteType: PROJECT_CONCRETE_TYPE_VALUE,\n description: `This Project was automatically created for Team \"${t.name}\" for Challenge \"${c.id}\"`,\n }\n }\n\n // Use the existing accessToken if present to get the current user's profile / userId\n const { data: userProfile, isLoading: isProfileLoading } =\n useGetCurrentUserProfile({\n enabled: isAuthenticated,\n throwOnError: true,\n })\n\n // Retrieve the challenge associated with the projectId passed through props\n const { data: challenge } = useGetEntityChallenge(projectId, {\n enabled: isAuthenticated && !!projectId,\n refetchInterval: Infinity,\n throwOnError: true,\n })\n\n // Determine whether or not the given user belongs to any submission teams\n const { data: userSubmissionTeams } = useGetUserSubmissionTeams(\n challenge?.id ?? EMPTY_ID,\n 2,\n )\n\n useEffect(() => {\n if (isAuthenticated && !!challenge && userSubmissionTeams) {\n const isReg = userSubmissionTeams.results.length > 0\n if (!isReg) {\n setErrorMessage(\n 'Error: Please join a Submission Team before continuing.',\n )\n return setLoading(false)\n }\n if (userSubmissionTeams.results.length > 1) {\n setErrorMessage(\n 'Error: You are a member of more than one Submission Team. You may only belong to one Submission Team per Challenge.',\n )\n return setLoading(false)\n }\n setSubmissionTeamId(userSubmissionTeams.results[0])\n }\n }, [challenge, isAuthenticated, userSubmissionTeams])\n\n const { data: submissionTeam } = useGetTeam(submissionTeamId!, {\n enabled: !!submissionTeamId,\n refetchInterval: Infinity,\n throwOnError: true,\n })\n\n const { data: entityAlias } = useGetEntityAlias(\n newProject?.alias ?? EMPTY_ID,\n {\n enabled: newProject !== undefined && !!challenge && !!submissionTeam,\n throwOnError: true,\n },\n )\n useEffect(() => {\n if (entityAlias) {\n setProjectAliasFound(true)\n setChallengeProjectId(entityAlias.id)\n } else {\n setProjectAliasFound(false)\n }\n }, [entityAlias])\n\n /**\n * If the challenge project was just created, retrieve its ACL\n * and add the submission team to it\n */\n const { data: entityACL } = useGetEntityACL(challengeProjectId ?? EMPTY_ID, {\n enabled: !!challengeProjectId && isProjectNewlyCreated === true,\n refetchInterval: Infinity,\n throwOnError: true,\n })\n\n useEffect(() => {\n if (entityACL && isProjectNewlyCreated === true) {\n // Give submission team admin access to challenge project\n const teamResourceAccess: ResourceAccess = {\n principalId: Number(submissionTeam!.id),\n accessType: [\n ACCESS_TYPE.CHANGE_PERMISSIONS,\n ACCESS_TYPE.CHANGE_SETTINGS,\n ACCESS_TYPE.CREATE,\n ACCESS_TYPE.DELETE,\n ACCESS_TYPE.DOWNLOAD,\n ACCESS_TYPE.MODERATE,\n ACCESS_TYPE.READ,\n ACCESS_TYPE.UPDATE,\n ],\n }\n updateACL({\n ...entityACL,\n resourceAccess: [...entityACL.resourceAccess, teamResourceAccess],\n })\n setIsProjectNewlyCreated(false)\n }\n }, [entityACL])\n\n const { data: entityPermissions } = useGetEntityPermissions(\n challengeProjectId!,\n {\n enabled: !!challengeProjectId,\n refetchInterval: Infinity,\n throwOnError: true,\n },\n )\n\n useEffect(() => {\n if (\n entityPermissions &&\n entityPermissions.canView &&\n entityPermissions.canAddChild\n ) {\n setCanSubmit(true)\n }\n setLoading(false)\n }, [entityPermissions])\n\n useEffect(() => {\n if (!isAuthenticated && (!!userProfile || !isProfileLoading)) {\n setLoading(false)\n setErrorMessage('Please login to continue.')\n }\n }, [isAuthenticated, userProfile, isProfileLoading])\n\n useEffect(() => {\n if (isAuthenticated && submissionTeam && challenge && !newProject) {\n const project = getProject(challenge, submissionTeam)\n setNewProject(project)\n }\n }, [isAuthenticated, submissionTeam, challenge, newProject])\n\n useEffect(() => {\n async function createChallengeProject() {\n const project: Project = getProject(challenge!, submissionTeam!)\n const challengeProject = await createEntity(project, accessToken)\n if (challengeProject && challengeProject.id) {\n setChallengeProjectId(challengeProject.id)\n setIsProjectNewlyCreated(true)\n }\n }\n if (\n isAuthenticated &&\n submissionTeam &&\n challenge &&\n newProject &&\n projectAliasFound === false\n ) {\n createChallengeProject()\n }\n }, [\n isAuthenticated,\n submissionTeam,\n challenge,\n newProject,\n projectAliasFound,\n accessToken,\n ])\n\n const itemSelectedHandler = (entity: EntityItem) => {\n setSelectedEntity(entity)\n setIsShowingModal(true)\n }\n\n const onModalClose = () => {\n setIsShowingModal(false)\n }\n\n return (\n <SynapseErrorBoundary>\n {loading && (\n <span data-testid=\"SpinnerButton-spinner\" className=\"spinner\" />\n )}\n {canSubmit && (\n <>\n <SubmissionDirectoryList\n entityType={entityType}\n pageSize={pageSize}\n challengeProjectId={challengeProjectId!}\n onItemSelected={itemSelectedHandler}\n />\n {userProfile && selectedEntity && (\n <ChallengeSubmissionStepper\n projectId={projectId}\n userId={userProfile?.ownerId}\n teamId={submissionTeamId!}\n entity={selectedEntity}\n entityType={entityType}\n isShowingModal={isShowingModal}\n onClose={onModalClose}\n />\n )}\n </>\n )}\n {errorMessage && <ErrorBanner error={errorMessage}></ErrorBanner>}\n </SynapseErrorBoundary>\n )\n}\n\nexport default ChallengeSubmission\n"],"mappings":"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"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"ChallengeSubmissionStepper.js","names":[],"sources":["../../../src/components/ChallengeSubmission/ChallengeSubmissionStepper.tsx"],"sourcesContent":["import SynapseClient from '@/synapse-client'\nimport { useSynapseContext } from '@/utils'\nimport { Typography } from '@mui/material'\nimport { EntityType } from '@sage-bionetworks/synapse-client'\nimport {\n DockerCommit,\n EvaluationSubmission,\n TeamSubmissionEligibility,\n} from '@sage-bionetworks/synapse-types'\nimport { useState } from 'react'\nimport StepperDialog from '../StepperDialog'\nimport { Step } from '../StepperDialog/StepperDialog'\nimport { displayToast } from '../ToastMessage'\nimport { EntityItem } from './ChallengeSubmission'\nimport EvaluationQueueList from './EvaluationQueueList'\nimport SubmissionCommitList from './SubmissionCommitList'\n\nenum StepsEnum {\n SELECT_COMMIT = 'SELECT_COMMIT',\n SELECT_EVALUATION = 'SELECT_EVALUATION',\n SUBMISSION_SUCCESS = 'SUBMISSION_SUCCESS',\n}\ntype StepKey = keyof typeof StepsEnum\ntype StepList<TStepId = string> = {\n [key in StepKey]: Step<TStepId>\n}\n\nconst stepsDocker: StepList<StepsEnum> = {\n SELECT_COMMIT: {\n id: StepsEnum.SELECT_COMMIT,\n title: 'Select Commit for Submission',\n nextStep: StepsEnum.SELECT_EVALUATION,\n nextEnabled: false,\n },\n SELECT_EVALUATION: {\n id: StepsEnum.SELECT_EVALUATION,\n title: 'Select Evaluation Queue',\n previousStep: StepsEnum.SELECT_COMMIT,\n confirmStep: StepsEnum.SUBMISSION_SUCCESS,\n confirmButtonText: 'Submit',\n },\n SUBMISSION_SUCCESS: {\n id: StepsEnum.SUBMISSION_SUCCESS,\n title: 'Submission Successful!',\n confirmButtonText: 'Close',\n confirmEnabled: true,\n },\n}\n\nconst stepsFile: StepList<StepsEnum> = {\n SELECT_COMMIT: { id: StepsEnum.SELECT_COMMIT, title: '' },\n SELECT_EVALUATION: {\n id: StepsEnum.SELECT_EVALUATION,\n title: 'Select Evaluation Queue',\n confirmStep: StepsEnum.SUBMISSION_SUCCESS,\n confirmButtonText: 'Submit',\n },\n SUBMISSION_SUCCESS: {\n id: StepsEnum.SUBMISSION_SUCCESS,\n title: 'Submission Successful!',\n confirmButtonText: 'Close',\n confirmEnabled: true,\n },\n}\n\nconst getSteps = (\n entityType: typeof EntityType.dockerrepo | typeof EntityType.file,\n) => {\n return entityType === EntityType.dockerrepo ? stepsDocker : stepsFile\n}\n\ntype ChallengeSubmissionStepperProps = {\n projectId: string\n userId: string\n teamId: string\n entity: EntityItem\n entityType: typeof EntityType.dockerrepo | typeof EntityType.file\n isShowingModal: boolean\n onClose: () => void\n}\n\nfunction ChallengeSubmissionStepper({\n projectId,\n userId,\n teamId,\n entity,\n entityType,\n isShowingModal,\n onClose,\n}: ChallengeSubmissionStepperProps) {\n const { accessToken } = useSynapseContext()\n const steps = getSteps(entityType)\n const initialStep =\n entityType === EntityType.dockerrepo\n ? steps.SELECT_COMMIT\n : steps.SELECT_EVALUATION\n const [step, setStep] = useState<Step<StepsEnum>>(initialStep)\n const [errorMessage, setErrorMessage] = useState<string>()\n const [selectedCommit, setSelectedCommit] = useState<DockerCommit>()\n\n const [submissionName, setSubmissionName] = useState<string>('')\n const [selectedEval, setSelectedEval] = useState<string | undefined>()\n const [confirming, setConfirming] = useState<boolean>(false)\n\n const hide = () => {\n setErrorMessage(undefined)\n setSelectedCommit(undefined)\n setSubmissionName('')\n setSelectedEval(undefined)\n setConfirming(false)\n onClose()\n setStep(initialStep)\n }\n\n function handleStepChange(value?: StepsEnum) {\n if (!value || !steps[value]) return\n setErrorMessage(undefined)\n setStep(steps[value])\n }\n\n async function confirmEligibility() {\n if (!selectedEval) return setErrorMessage('Please select an evaluation.')\n try {\n return await SynapseClient.getSubmissionEligibility(\n selectedEval,\n teamId,\n accessToken,\n )\n } catch (err) {\n return setErrorMessage(err.message)\n }\n }\n\n async function submitForEvaluation(eligibility: TeamSubmissionEligibility) {\n if (!entity.id || (entityType === EntityType.dockerrepo && !selectedCommit))\n return setErrorMessage('Error: Invalid entity or commit.')\n\n const contributors = eligibility.membersEligibility\n .filter(\n member =>\n !member.hasConflictingSubmission &&\n member.isEligible &&\n !member.isQuotaFilled &&\n member.isRegistered,\n )\n .map(member => {\n return { principalId: member.principalId.toString() }\n })\n\n const submission: EvaluationSubmission = {\n userId: userId,\n evaluationId: selectedEval!,\n entityId: entity.id,\n versionNumber: entity.versionNumber ?? -1,\n teamId: teamId,\n contributors,\n }\n if (entityType === EntityType.dockerrepo) {\n submission.dockerRepositoryName = entity.repositoryName\n submission.dockerDigest = selectedCommit!.digest\n }\n\n if (submissionName !== '') submission['name'] = submissionName\n\n try {\n await SynapseClient.submitToEvaluation(\n accessToken,\n submission,\n entity.etag!,\n eligibility.eligibilityStateHash,\n )\n displayToast('Submitted successfully!', 'success')\n hide()\n } catch (e) {\n setErrorMessage(e.message)\n }\n }\n\n const onConfirmHandler = async () => {\n const eligibility = await confirmEligibility()\n if (eligibility) {\n await submitForEvaluation(eligibility)\n }\n }\n\n const onCommitChanged = (value: DockerCommit) => {\n setSelectedCommit(value)\n setStep({ ...step, nextEnabled: true })\n }\n\n function stepperContent() {\n switch (step.id) {\n case StepsEnum.SELECT_COMMIT:\n return (\n <SubmissionCommitList\n repository={entity}\n selectedCommit={selectedCommit}\n onCommitChanged={onCommitChanged}\n />\n )\n break\n case StepsEnum.SELECT_EVALUATION:\n return (\n <EvaluationQueueList\n projectId={projectId}\n submissonName={submissionName}\n onSubmissionNameChange={setSubmissionName}\n selectedEvaluation={selectedEval}\n onEvaluationChange={value => {\n setStep({ ...step, confirmEnabled: true })\n setSelectedEval(value)\n }}\n />\n )\n case StepsEnum.SUBMISSION_SUCCESS:\n return (\n <Typography variant=\"body1\" sx={{ fontSize: '16px' }}>\n Your submission will be scored and results posted to the Challenge\n Leaderboard.\n </Typography>\n )\n default:\n return <></>\n }\n }\n\n return (\n <StepperDialog\n errorMessage={errorMessage}\n onCancel={hide}\n onStepChange={handleStepChange as (arg: string) => void}\n open={isShowingModal}\n onConfirm={() => {\n onConfirmHandler()\n }}\n confirming={confirming}\n step={step}\n content={stepperContent()}\n loading={false}\n />\n )\n}\n\nexport default ChallengeSubmissionStepper\n"],"mappings":";;;;;;;;;;;;;AAiBA,IAAK,IAAL,yBAAA,GAAA;QACE,EAAA,
|
|
1
|
+
{"version":3,"file":"ChallengeSubmissionStepper.js","names":[],"sources":["../../../src/components/ChallengeSubmission/ChallengeSubmissionStepper.tsx"],"sourcesContent":["import SynapseClient from '@/synapse-client'\nimport { useSynapseContext } from '@/utils'\nimport { Typography } from '@mui/material'\nimport { EntityType } from '@sage-bionetworks/synapse-client'\nimport {\n DockerCommit,\n EvaluationSubmission,\n TeamSubmissionEligibility,\n} from '@sage-bionetworks/synapse-types'\nimport { useState } from 'react'\nimport StepperDialog from '../StepperDialog'\nimport { Step } from '../StepperDialog/StepperDialog'\nimport { displayToast } from '../ToastMessage'\nimport { EntityItem } from './ChallengeSubmission'\nimport EvaluationQueueList from './EvaluationQueueList'\nimport SubmissionCommitList from './SubmissionCommitList'\n\nenum StepsEnum {\n SELECT_COMMIT = 'SELECT_COMMIT',\n SELECT_EVALUATION = 'SELECT_EVALUATION',\n SUBMISSION_SUCCESS = 'SUBMISSION_SUCCESS',\n}\ntype StepKey = keyof typeof StepsEnum\ntype StepList<TStepId = string> = {\n [key in StepKey]: Step<TStepId>\n}\n\nconst stepsDocker: StepList<StepsEnum> = {\n SELECT_COMMIT: {\n id: StepsEnum.SELECT_COMMIT,\n title: 'Select Commit for Submission',\n nextStep: StepsEnum.SELECT_EVALUATION,\n nextEnabled: false,\n },\n SELECT_EVALUATION: {\n id: StepsEnum.SELECT_EVALUATION,\n title: 'Select Evaluation Queue',\n previousStep: StepsEnum.SELECT_COMMIT,\n confirmStep: StepsEnum.SUBMISSION_SUCCESS,\n confirmButtonText: 'Submit',\n },\n SUBMISSION_SUCCESS: {\n id: StepsEnum.SUBMISSION_SUCCESS,\n title: 'Submission Successful!',\n confirmButtonText: 'Close',\n confirmEnabled: true,\n },\n}\n\nconst stepsFile: StepList<StepsEnum> = {\n SELECT_COMMIT: { id: StepsEnum.SELECT_COMMIT, title: '' },\n SELECT_EVALUATION: {\n id: StepsEnum.SELECT_EVALUATION,\n title: 'Select Evaluation Queue',\n confirmStep: StepsEnum.SUBMISSION_SUCCESS,\n confirmButtonText: 'Submit',\n },\n SUBMISSION_SUCCESS: {\n id: StepsEnum.SUBMISSION_SUCCESS,\n title: 'Submission Successful!',\n confirmButtonText: 'Close',\n confirmEnabled: true,\n },\n}\n\nconst getSteps = (\n entityType: typeof EntityType.dockerrepo | typeof EntityType.file,\n) => {\n return entityType === EntityType.dockerrepo ? stepsDocker : stepsFile\n}\n\ntype ChallengeSubmissionStepperProps = {\n projectId: string\n userId: string\n teamId: string\n entity: EntityItem\n entityType: typeof EntityType.dockerrepo | typeof EntityType.file\n isShowingModal: boolean\n onClose: () => void\n}\n\nfunction ChallengeSubmissionStepper({\n projectId,\n userId,\n teamId,\n entity,\n entityType,\n isShowingModal,\n onClose,\n}: ChallengeSubmissionStepperProps) {\n const { accessToken } = useSynapseContext()\n const steps = getSteps(entityType)\n const initialStep =\n entityType === EntityType.dockerrepo\n ? steps.SELECT_COMMIT\n : steps.SELECT_EVALUATION\n const [step, setStep] = useState<Step<StepsEnum>>(initialStep)\n const [errorMessage, setErrorMessage] = useState<string>()\n const [selectedCommit, setSelectedCommit] = useState<DockerCommit>()\n\n const [submissionName, setSubmissionName] = useState<string>('')\n const [selectedEval, setSelectedEval] = useState<string | undefined>()\n const [confirming, setConfirming] = useState<boolean>(false)\n\n const hide = () => {\n setErrorMessage(undefined)\n setSelectedCommit(undefined)\n setSubmissionName('')\n setSelectedEval(undefined)\n setConfirming(false)\n onClose()\n setStep(initialStep)\n }\n\n function handleStepChange(value?: StepsEnum) {\n if (!value || !steps[value]) return\n setErrorMessage(undefined)\n setStep(steps[value])\n }\n\n async function confirmEligibility() {\n if (!selectedEval) return setErrorMessage('Please select an evaluation.')\n try {\n return await SynapseClient.getSubmissionEligibility(\n selectedEval,\n teamId,\n accessToken,\n )\n } catch (err) {\n return setErrorMessage(err.message)\n }\n }\n\n async function submitForEvaluation(eligibility: TeamSubmissionEligibility) {\n if (!entity.id || (entityType === EntityType.dockerrepo && !selectedCommit))\n return setErrorMessage('Error: Invalid entity or commit.')\n\n const contributors = eligibility.membersEligibility\n .filter(\n member =>\n !member.hasConflictingSubmission &&\n member.isEligible &&\n !member.isQuotaFilled &&\n member.isRegistered,\n )\n .map(member => {\n return { principalId: member.principalId.toString() }\n })\n\n const submission: EvaluationSubmission = {\n userId: userId,\n evaluationId: selectedEval!,\n entityId: entity.id,\n versionNumber: entity.versionNumber ?? -1,\n teamId: teamId,\n contributors,\n }\n if (entityType === EntityType.dockerrepo) {\n submission.dockerRepositoryName = entity.repositoryName\n submission.dockerDigest = selectedCommit!.digest\n }\n\n if (submissionName !== '') submission['name'] = submissionName\n\n try {\n await SynapseClient.submitToEvaluation(\n accessToken,\n submission,\n entity.etag!,\n eligibility.eligibilityStateHash,\n )\n displayToast('Submitted successfully!', 'success')\n hide()\n } catch (e) {\n setErrorMessage(e.message)\n }\n }\n\n const onConfirmHandler = async () => {\n const eligibility = await confirmEligibility()\n if (eligibility) {\n await submitForEvaluation(eligibility)\n }\n }\n\n const onCommitChanged = (value: DockerCommit) => {\n setSelectedCommit(value)\n setStep({ ...step, nextEnabled: true })\n }\n\n function stepperContent() {\n switch (step.id) {\n case StepsEnum.SELECT_COMMIT:\n return (\n <SubmissionCommitList\n repository={entity}\n selectedCommit={selectedCommit}\n onCommitChanged={onCommitChanged}\n />\n )\n break\n case StepsEnum.SELECT_EVALUATION:\n return (\n <EvaluationQueueList\n projectId={projectId}\n submissonName={submissionName}\n onSubmissionNameChange={setSubmissionName}\n selectedEvaluation={selectedEval}\n onEvaluationChange={value => {\n setStep({ ...step, confirmEnabled: true })\n setSelectedEval(value)\n }}\n />\n )\n case StepsEnum.SUBMISSION_SUCCESS:\n return (\n <Typography variant=\"body1\" sx={{ fontSize: '16px' }}>\n Your submission will be scored and results posted to the Challenge\n Leaderboard.\n </Typography>\n )\n default:\n return <></>\n }\n }\n\n return (\n <StepperDialog\n errorMessage={errorMessage}\n onCancel={hide}\n onStepChange={handleStepChange as (arg: string) => void}\n open={isShowingModal}\n onConfirm={() => {\n onConfirmHandler()\n }}\n confirming={confirming}\n step={step}\n content={stepperContent()}\n loading={false}\n />\n )\n}\n\nexport default ChallengeSubmissionStepper\n"],"mappings":"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"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"EvaluationQueueCurrentRoundInfo.js","names":[],"sources":["../../../src/components/ChallengeSubmission/EvaluationQueueCurrentRoundInfo.tsx"],"sourcesContent":["import {\n useGetEvaluation,\n useGetEvaluationRoundsInfinite,\n} from '@/synapse-queries/evaluation/useEvaluation'\nimport { getCurrentOrNextOrLatestRound } from '@/utils/challenge/evaluation/EvaluationUtils'\nimport { formatDate } from '@/utils/functions/DateFormatter'\nimport { InfoTwoTone } from '@mui/icons-material'\nimport Tooltip from '@mui/material/Tooltip'\nimport dayjs from 'dayjs'\nimport { useEffect, useMemo } from 'react'\n\ntype EvaluationQueueInfoProps = {\n evaluationId: string\n}\n\n/**\n * Displays an information tooltip about the current (or recent) round of an Evaluation Queue.\n * @param props\n * @constructor\n */\nfunction EvaluationQueueCurrentRoundInfo(props: EvaluationQueueInfoProps) {\n const { evaluationId } = props\n\n const { data: evaluation } = useGetEvaluation(evaluationId)\n\n const {\n data: infiniteData,\n hasNextPage,\n isFetchingNextPage,\n fetchNextPage,\n } = useGetEvaluationRoundsInfinite(evaluationId)\n\n useEffect(() => {\n // Fetch all pages\n if (hasNextPage && !isFetchingNextPage) {\n void fetchNextPage()\n }\n }, [fetchNextPage, hasNextPage, isFetchingNextPage])\n\n const rounds = useMemo(\n () => infiniteData?.pages.flatMap(page => page.page!) ?? [],\n [infiniteData?.pages],\n )\n\n const currentRound = getCurrentOrNextOrLatestRound(rounds)\n\n const title = (\n <>\n {evaluation?.description && <p>{evaluation?.description}</p>}\n {currentRound?.roundStart && (\n <p>\n <strong>Start:</strong> {formatDate(dayjs(currentRound?.roundStart))}\n </p>\n )}\n {currentRound?.roundEnd && (\n <p>\n <strong>End:</strong> {formatDate(dayjs(currentRound?.roundEnd))}\n </p>\n )}\n <p>\n <strong>Submission Limits:</strong>{' '}\n {currentRound?.limits\n ? `${currentRound?.limits\n .map(\n limit =>\n `${\n limit.maximumSubmissions\n } ${limit.limitType.toLowerCase()} submissions`,\n )\n .join(', ')}`\n : 'No limit'}\n </p>\n </>\n )\n\n return (\n <Tooltip title={title}>\n <InfoTwoTone sx={{ color: 'grey.700' }} />\n </Tooltip>\n )\n}\n\nexport default EvaluationQueueCurrentRoundInfo\n"],"mappings":";;;;;;;;;AAoBA,SAAS,EAAgC,GAAiC;CACxE,IAAM,EAAE,oBAAiB,GAEnB,EAAE,MAAM,MAAe,EAAiB,EAAa,EAErD,EACJ,MAAM,GACN,gBACA,uBACA,qBACE,EAA+B,EAAa;AAEhD,SAAgB;AAEd,EAAI,KAAe,CAAC,KACb,GAAe;IAErB;EAAC;EAAe;EAAa;EAAmB,CAAC;CAOpD,IAAM,IAAe,EALN,QACP,GAAc,MAAM,SAAQ,MAAQ,EAAK,KAAM,IAAI,EAAE,EAC3D,CAAC,GAAc,MAAM,
|
|
1
|
+
{"version":3,"file":"EvaluationQueueCurrentRoundInfo.js","names":[],"sources":["../../../src/components/ChallengeSubmission/EvaluationQueueCurrentRoundInfo.tsx"],"sourcesContent":["import {\n useGetEvaluation,\n useGetEvaluationRoundsInfinite,\n} from '@/synapse-queries/evaluation/useEvaluation'\nimport { getCurrentOrNextOrLatestRound } from '@/utils/challenge/evaluation/EvaluationUtils'\nimport { formatDate } from '@/utils/functions/DateFormatter'\nimport { InfoTwoTone } from '@mui/icons-material'\nimport Tooltip from '@mui/material/Tooltip'\nimport dayjs from 'dayjs'\nimport { useEffect, useMemo } from 'react'\n\ntype EvaluationQueueInfoProps = {\n evaluationId: string\n}\n\n/**\n * Displays an information tooltip about the current (or recent) round of an Evaluation Queue.\n * @param props\n * @constructor\n */\nfunction EvaluationQueueCurrentRoundInfo(props: EvaluationQueueInfoProps) {\n const { evaluationId } = props\n\n const { data: evaluation } = useGetEvaluation(evaluationId)\n\n const {\n data: infiniteData,\n hasNextPage,\n isFetchingNextPage,\n fetchNextPage,\n } = useGetEvaluationRoundsInfinite(evaluationId)\n\n useEffect(() => {\n // Fetch all pages\n if (hasNextPage && !isFetchingNextPage) {\n void fetchNextPage()\n }\n }, [fetchNextPage, hasNextPage, isFetchingNextPage])\n\n const rounds = useMemo(\n () => infiniteData?.pages.flatMap(page => page.page!) ?? [],\n [infiniteData?.pages],\n )\n\n const currentRound = getCurrentOrNextOrLatestRound(rounds)\n\n const title = (\n <>\n {evaluation?.description && <p>{evaluation?.description}</p>}\n {currentRound?.roundStart && (\n <p>\n <strong>Start:</strong> {formatDate(dayjs(currentRound?.roundStart))}\n </p>\n )}\n {currentRound?.roundEnd && (\n <p>\n <strong>End:</strong> {formatDate(dayjs(currentRound?.roundEnd))}\n </p>\n )}\n <p>\n <strong>Submission Limits:</strong>{' '}\n {currentRound?.limits\n ? `${currentRound?.limits\n .map(\n limit =>\n `${\n limit.maximumSubmissions\n } ${limit.limitType.toLowerCase()} submissions`,\n )\n .join(', ')}`\n : 'No limit'}\n </p>\n </>\n )\n\n return (\n <Tooltip title={title}>\n <InfoTwoTone sx={{ color: 'grey.700' }} />\n </Tooltip>\n )\n}\n\nexport default EvaluationQueueCurrentRoundInfo\n"],"mappings":";;;;;;;;;AAoBA,SAAS,EAAgC,GAAiC;CACxE,IAAM,EAAE,oBAAiB,GAEnB,EAAE,MAAM,MAAe,EAAiB,EAAa,EAErD,EACJ,MAAM,GACN,gBACA,uBACA,qBACE,EAA+B,EAAa;AAEhD,SAAgB;AAEd,EAAI,KAAe,CAAC,KACb,GAAe;IAErB;EAAC;EAAe;EAAa;EAAmB,CAAC;CAOpD,IAAM,IAAe,EALN,QACP,GAAc,MAAM,SAAQ,MAAQ,EAAK,KAAM,IAAI,EAAE,EAC3D,CAAC,GAAc,MAAM,CAG4B,CAAO;AA+B1D,QACE,kBAAC,GAAD;EAAgB,OA7BhB,kBAAA,GAAA,EAAA,UAAA;GACG,GAAY,eAAe,kBAAC,KAAD,EAAA,UAAI,GAAY,aAAgB,CAAA;GAC3D,GAAc,cACb,kBAAC,KAAD,EAAA,UAAA;IACE,kBAAC,UAAD,EAAA,UAAQ,UAAe,CAAA;;IAAE,EAAW,EAAM,GAAc,WAAW,CAAC;IAClE,EAAA,CAAA;GAEL,GAAc,YACb,kBAAC,KAAD,EAAA,UAAA;IACE,kBAAC,UAAD,EAAA,UAAQ,QAAa,CAAA;;IAAE,EAAW,EAAM,GAAc,SAAS,CAAC;IAC9D,EAAA,CAAA;GAEN,kBAAC,KAAD,EAAA,UAAA;IACE,kBAAC,UAAD,EAAA,UAAQ,sBAA2B,CAAA;IAAC;IACnC,GAAc,SACX,GAAG,GAAc,OACd,KACC,MACE,GACE,EAAM,mBACP,GAAG,EAAM,UAAU,aAAa,CAAC,cACrC,CACA,KAAK,KAAK,KACb;IACF,EAAA,CAAA;GACH,EAAA,CAIa;YACd,kBAAC,GAAD,EAAa,IAAI,EAAE,OAAO,YAAY,EAAI,CAAA;EAClC,CAAA"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"EvaluationQueueList.js","names":[],"sources":["../../../src/components/ChallengeSubmission/EvaluationQueueList.tsx"],"sourcesContent":["import EvaluationQueueCurrentRoundInfo from '@/components/ChallengeSubmission/EvaluationQueueCurrentRoundInfo'\nimport { useGetEntityEvaluations } from '@/synapse-queries'\nimport { Alert, Box, Radio, TextField, Typography } from '@mui/material'\nimport { DataGrid, GridColDef } from '@mui/x-data-grid'\nimport { ACCESS_TYPE, Evaluation } from '@sage-bionetworks/synapse-types'\n\ntype EvaluationQueueListProps = {\n projectId: string\n submissonName: string\n selectedEvaluation: string | undefined\n onSubmissionNameChange: (value: string) => void\n onEvaluationChange: (value: string) => void\n submissionError?: string\n}\n\nfunction EvaluationQueueList({\n projectId,\n submissonName,\n selectedEvaluation,\n onSubmissionNameChange,\n onEvaluationChange,\n submissionError,\n}: EvaluationQueueListProps) {\n const { isLoading, data: evaluations } = useGetEntityEvaluations(projectId, {\n activeOnly: true,\n accessType: ACCESS_TYPE.SUBMIT,\n })\n\n const columns: GridColDef<Evaluation>[] = [\n {\n field: 'radiobutton',\n width: 25,\n sortable: false,\n filterable: false,\n hideable: false,\n disableColumnMenu: true,\n renderCell: params => {\n return (\n <Radio\n value={params.id as string}\n checked={params.id === selectedEvaluation}\n onChange={event => onEvaluationChange(event.target.value)}\n />\n )\n },\n },\n {\n field: 'name',\n flex: 1,\n filterable: false,\n hideable: false,\n disableColumnMenu: true,\n },\n {\n field: 'info',\n width: 25,\n sortable: false,\n filterable: false,\n hideable: false,\n disableColumnMenu: true,\n renderCell: params => (\n <EvaluationQueueCurrentRoundInfo evaluationId={params.id as string} />\n ),\n },\n ]\n\n return (\n <Box>\n <Box\n sx={{ display: 'flex', backgroundColor: '#FBFBFC', padding: '10px' }}\n >\n <Typography\n variant=\"h6\"\n sx={{ fontSize: '18px', lineHeight: '20px', fontWeight: 700 }}\n >\n Evaluation Queues\n </Typography>\n </Box>\n <DataGrid\n columns={columns}\n rows={evaluations || []}\n hideFooter\n density=\"compact\"\n autoHeight\n loading={isLoading}\n slots={{\n toolbar: () => null,\n }}\n sx={{\n fontSize: '14px',\n border: 'none',\n height: '100%',\n '& .MuiDataGrid-columnHeaders': {\n display: 'none',\n },\n '& .MuiDataGrid-virtualScroller': {\n marginTop: '0px !important',\n },\n '& .Mui-odd': {\n backgroundColor: '#FBFBFC',\n },\n '.MuiDataGrid-columnHeaderTitleContainer': {\n justifyContent: 'space-between',\n },\n '.radio': {\n display: 'flex',\n alignItems: 'center',\n height: '100%',\n },\n }}\n getRowClassName={params =>\n params.indexRelativeToCurrentPage % 2 === 0 ? 'Mui-even' : 'Mui-odd'\n }\n showToolbar\n />\n <Box\n sx={{\n mt: 3,\n }}\n >\n <Typography variant=\"body1\">Submission Name (optional)</Typography>\n <TextField\n id=\"submissionName\"\n name=\"submissionName\"\n fullWidth\n onChange={e => onSubmissionNameChange(e.target.value)}\n value={submissonName}\n />\n {submissionError && (\n <Box\n sx={{\n mt: 1,\n }}\n >\n <Alert severity=\"error\">{submissionError}</Alert>\n </Box>\n )}\n </Box>\n </Box>\n )\n}\n\nexport default EvaluationQueueList\n"],"mappings":";;;;;;;;AAeA,SAAS,EAAoB,EAC3B,cACA,kBACA,uBACA,2BACA,uBACA,sBAC2B;CAC3B,IAAM,EAAE,cAAW,MAAM,MAAgB,EAAwB,GAAW;EAC1E,YAAY;EACZ,YAAY,EAAY;EACzB,CAAC;AAwCF,QACE,kBAAC,GAAD,EAAA,UAAA;EACE,kBAAC,GAAD;GACE,IAAI;IAAE,SAAS;IAAQ,iBAAiB;IAAW,SAAS;IAAQ;aAEpE,kBAAC,GAAD;IACE,SAAQ;IACR,IAAI;KAAE,UAAU;KAAQ,YAAY;KAAQ,YAAY;KAAK;cAC9D;IAEY,CAAA;GACT,CAAA;EACN,kBAAC,GAAD;GACW,
|
|
1
|
+
{"version":3,"file":"EvaluationQueueList.js","names":[],"sources":["../../../src/components/ChallengeSubmission/EvaluationQueueList.tsx"],"sourcesContent":["import EvaluationQueueCurrentRoundInfo from '@/components/ChallengeSubmission/EvaluationQueueCurrentRoundInfo'\nimport { useGetEntityEvaluations } from '@/synapse-queries'\nimport { Alert, Box, Radio, TextField, Typography } from '@mui/material'\nimport { DataGrid, GridColDef } from '@mui/x-data-grid'\nimport { ACCESS_TYPE, Evaluation } from '@sage-bionetworks/synapse-types'\n\ntype EvaluationQueueListProps = {\n projectId: string\n submissonName: string\n selectedEvaluation: string | undefined\n onSubmissionNameChange: (value: string) => void\n onEvaluationChange: (value: string) => void\n submissionError?: string\n}\n\nfunction EvaluationQueueList({\n projectId,\n submissonName,\n selectedEvaluation,\n onSubmissionNameChange,\n onEvaluationChange,\n submissionError,\n}: EvaluationQueueListProps) {\n const { isLoading, data: evaluations } = useGetEntityEvaluations(projectId, {\n activeOnly: true,\n accessType: ACCESS_TYPE.SUBMIT,\n })\n\n const columns: GridColDef<Evaluation>[] = [\n {\n field: 'radiobutton',\n width: 25,\n sortable: false,\n filterable: false,\n hideable: false,\n disableColumnMenu: true,\n renderCell: params => {\n return (\n <Radio\n value={params.id as string}\n checked={params.id === selectedEvaluation}\n onChange={event => onEvaluationChange(event.target.value)}\n />\n )\n },\n },\n {\n field: 'name',\n flex: 1,\n filterable: false,\n hideable: false,\n disableColumnMenu: true,\n },\n {\n field: 'info',\n width: 25,\n sortable: false,\n filterable: false,\n hideable: false,\n disableColumnMenu: true,\n renderCell: params => (\n <EvaluationQueueCurrentRoundInfo evaluationId={params.id as string} />\n ),\n },\n ]\n\n return (\n <Box>\n <Box\n sx={{ display: 'flex', backgroundColor: '#FBFBFC', padding: '10px' }}\n >\n <Typography\n variant=\"h6\"\n sx={{ fontSize: '18px', lineHeight: '20px', fontWeight: 700 }}\n >\n Evaluation Queues\n </Typography>\n </Box>\n <DataGrid\n columns={columns}\n rows={evaluations || []}\n hideFooter\n density=\"compact\"\n autoHeight\n loading={isLoading}\n slots={{\n toolbar: () => null,\n }}\n sx={{\n fontSize: '14px',\n border: 'none',\n height: '100%',\n '& .MuiDataGrid-columnHeaders': {\n display: 'none',\n },\n '& .MuiDataGrid-virtualScroller': {\n marginTop: '0px !important',\n },\n '& .Mui-odd': {\n backgroundColor: '#FBFBFC',\n },\n '.MuiDataGrid-columnHeaderTitleContainer': {\n justifyContent: 'space-between',\n },\n '.radio': {\n display: 'flex',\n alignItems: 'center',\n height: '100%',\n },\n }}\n getRowClassName={params =>\n params.indexRelativeToCurrentPage % 2 === 0 ? 'Mui-even' : 'Mui-odd'\n }\n showToolbar\n />\n <Box\n sx={{\n mt: 3,\n }}\n >\n <Typography variant=\"body1\">Submission Name (optional)</Typography>\n <TextField\n id=\"submissionName\"\n name=\"submissionName\"\n fullWidth\n onChange={e => onSubmissionNameChange(e.target.value)}\n value={submissonName}\n />\n {submissionError && (\n <Box\n sx={{\n mt: 1,\n }}\n >\n <Alert severity=\"error\">{submissionError}</Alert>\n </Box>\n )}\n </Box>\n </Box>\n )\n}\n\nexport default EvaluationQueueList\n"],"mappings":";;;;;;;;AAeA,SAAS,EAAoB,EAC3B,cACA,kBACA,uBACA,2BACA,uBACA,sBAC2B;CAC3B,IAAM,EAAE,cAAW,MAAM,MAAgB,EAAwB,GAAW;EAC1E,YAAY;EACZ,YAAY,EAAY;EACzB,CAAC;AAwCF,QACE,kBAAC,GAAD,EAAA,UAAA;EACE,kBAAC,GAAD;GACE,IAAI;IAAE,SAAS;IAAQ,iBAAiB;IAAW,SAAS;IAAQ;aAEpE,kBAAC,GAAD;IACE,SAAQ;IACR,IAAI;KAAE,UAAU;KAAQ,YAAY;KAAQ,YAAY;KAAK;cAC9D;IAEY,CAAA;GACT,CAAA;EACN,kBAAC,GAAD;GACW,SAAA;IAlDb;KACE,OAAO;KACP,OAAO;KACP,UAAU;KACV,YAAY;KACZ,UAAU;KACV,mBAAmB;KACnB,aAAY,MAER,kBAAC,GAAD;MACE,OAAO,EAAO;MACd,SAAS,EAAO,OAAO;MACvB,WAAU,MAAS,EAAmB,EAAM,OAAO,MAAM;MACzD,CAAA;KAGP;IACD;KACE,OAAO;KACP,MAAM;KACN,YAAY;KACZ,UAAU;KACV,mBAAmB;KACpB;IACD;KACE,OAAO;KACP,OAAO;KACP,UAAU;KACV,YAAY;KACZ,UAAU;KACV,mBAAmB;KACnB,aAAY,MACV,kBAAC,GAAD,EAAiC,cAAc,EAAO,IAAgB,CAAA;KAEzE;IAgBY;GACT,MAAM,KAAe,EAAE;GACvB,YAAA;GACA,SAAQ;GACR,YAAA;GACA,SAAS;GACT,OAAO,EACL,eAAe,MAChB;GACD,IAAI;IACF,UAAU;IACV,QAAQ;IACR,QAAQ;IACR,gCAAgC,EAC9B,SAAS,QACV;IACD,kCAAkC,EAChC,WAAW,kBACZ;IACD,cAAc,EACZ,iBAAiB,WAClB;IACD,2CAA2C,EACzC,gBAAgB,iBACjB;IACD,UAAU;KACR,SAAS;KACT,YAAY;KACZ,QAAQ;KACT;IACF;GACD,kBAAiB,MACf,EAAO,6BAA6B,KAAM,IAAI,aAAa;GAE7D,aAAA;GACA,CAAA;EACF,kBAAC,GAAD;GACE,IAAI,EACF,IAAI,GACL;aAHH;IAKE,kBAAC,GAAD;KAAY,SAAQ;eAAQ;KAAuC,CAAA;IACnE,kBAAC,GAAD;KACE,IAAG;KACH,MAAK;KACL,WAAA;KACA,WAAU,MAAK,EAAuB,EAAE,OAAO,MAAM;KACrD,OAAO;KACP,CAAA;IACD,KACC,kBAAC,GAAD;KACE,IAAI,EACF,IAAI,GACL;eAED,kBAAC,GAAD;MAAO,UAAS;gBAAS;MAAwB,CAAA;KAC7C,CAAA;IAEJ;;EACF,EAAA,CAAA"}
|