/** * Copyright (c) 2020-present, Goldman Sachs * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ import { forwardRef, useEffect, useState } from 'react'; import { observer } from 'mobx-react-lite'; import { LEGEND_STUDIO_TEST_ID } from '../../../__lib__/LegendStudioTesting.js'; import { clsx, CustomSelectorInput, ShareIcon, PanelLoadingIndicator, ContextMenu, PencilIcon, InfoCircleIcon, TimesIcon, UsersIcon, UserIcon, ExternalLinkIcon, Dialog, PanelContent, Modal, ModalBody, ModalFooter, ModalFooterButton, MenuContentItem, MenuContent, PanelFormBooleanField, } from '@finos/legend-art'; import { PROJECT_OVERVIEW_ACTIVITY_MODE } from '../../../stores/editor/sidebar-state/ProjectOverviewState.js'; import { generateEditorRoute, generateViewProjectRoute, generateViewVersionRoute, generateReviewRoute, } from '../../../__lib__/LegendStudioNavigation.js'; import { flowResult } from 'mobx'; import { type Workspace, type Version, NewVersionType, WorkspaceType, areWorkspacesEquivalent, Review, ReviewState, Patch, } from '@finos/legend-server-sdlc'; import { useEditorStore } from '../EditorStoreProvider.js'; import { useApplicationStore } from '@finos/legend-application'; import { useLegendStudioApplicationStore } from '../../LegendStudioFrameworkProvider.js'; import { ActionState, assertErrorThrown, guaranteeNonNullable, LogEvent, } from '@finos/legend-shared'; import type { VersionOption } from '../editor-group/project-configuration-editor/ProjectDependencyEditor.js'; import { DocumentationLink } from '@finos/legend-lego/application'; import { LEGEND_STUDIO_DOCUMENTATION_KEY } from '../../../__lib__/LegendStudioDocumentation.js'; import type { PatchOption } from '../../workspace-setup/CreateWorkspaceModal.js'; import { LEGEND_STUDIO_APP_EVENT } from '../../../__lib__/LegendStudioEvent.js'; const ShareProjectModal = observer( (props: { open: boolean; closeModal: () => void }) => { const { open, closeModal } = props; const editorStore = useEditorStore(); const applicationStore = useLegendStudioApplicationStore(); const versions = editorStore.sdlcState.projectVersions; const isDispatchingAction = editorStore.sdlcState.isFetchingProjectVersions; const isFetchingProject = editorStore.sdlcState.isFetchingProject; const [selectedVersion, setSelectedVersion] = useState< Version | undefined >(); const projectId = editorStore.sdlcState.activeProject.projectId; const projectLink = selectedVersion ? applicationStore.navigationService.navigator.generateAddress( generateViewVersionRoute(projectId, selectedVersion.id.id), ) : applicationStore.navigationService.navigator.generateAddress( generateViewProjectRoute(projectId), ); const copyProjectElementLink = (): void => { applicationStore.clipboardService .copyTextToClipboard(projectLink) .then(() => applicationStore.notificationService.notifySuccess( 'Copied project element link to clipboard', ), ) .catch(applicationStore.alertUnhandledError) .finally(() => closeModal()); }; const renderOptions = versions.map((version) => ({ label: version.id.id, value: version, })); const onSelectionChange = ( val: { label: string; value: Version } | null, ): void => setSelectedVersion(val?.value); return (
Version:
{versions.length > 0 ? (
) : ( 'Project has only one version' )}
Link:
); }, ); const WorkspaceViewerContextMenu = observer( forwardRef< HTMLDivElement, { workspace: Workspace; } >(function WorkspaceViewerContextMenu(props, ref) { const { workspace } = props; const editorStore = useEditorStore(); const applicationStore = useApplicationStore(); const deleteWorkspace = applicationStore.guardUnhandledError(() => flowResult(editorStore.projectOverviewState.deleteWorkspace(workspace)), ); return ( Delete ); }), ); const WorkspaceViewer = observer((props: { workspace: Workspace }) => { const { workspace } = props; const editorStore = useEditorStore(); const applicationStore = useApplicationStore(); const isActive = areWorkspacesEquivalent(editorStore.sdlcState.activeWorkspace, workspace) && workspace.source === editorStore.sdlcState.activePatch?.patchReleaseVersionId.id; const [isSelectedFromContextMenu, setIsSelectedFromContextMenu] = useState(false); const onContextMenuOpen = (): void => setIsSelectedFromContextMenu(true); const onContextMenuClose = (): void => setIsSelectedFromContextMenu(false); return ( } menuProps={{ elevation: 7 }} onOpen={onContextMenuOpen} onClose={onContextMenuClose} > ); }); const WorkspacesViewer = observer(() => { const editorStore = useEditorStore(); const applicationStore = useApplicationStore(); const projectOverviewState = editorStore.projectOverviewState; const workspaces = projectOverviewState.projectWorkspaces; const isDispatchingAction = projectOverviewState.isDeletingWorkspace || projectOverviewState.isFetchingProjectWorkspaces; // since this can be affected by other users, we refresh it more proactively useEffect(() => { flowResult(projectOverviewState.fetchProjectWorkspaces()).catch( applicationStore.alertUnhandledError, ); }, [applicationStore, projectOverviewState]); return (
{PROJECT_OVERVIEW_ACTIVITY_MODE.WORKSPACES}
{workspaces.length}
{workspaces.map((workspace) => ( ))}
); }); const ReleaseEditor = observer(() => { const editorStore = useEditorStore(); const applicationStore = useLegendStudioApplicationStore(); const projectOverviewState = editorStore.projectOverviewState; const commitedReviews = projectOverviewState.committedReviewsBetweenMostRecentVersionAndProjectLatest; const isDispatchingAction = projectOverviewState.isFetchingLatestVersion || projectOverviewState.isFetchingCurrentProjectRevision || projectOverviewState.isCreatingVersion; const { latestProjectVersion, currentProjectRevision } = projectOverviewState; const revisionInput = projectOverviewState.releaseVersion; const createMajorRelease = applicationStore.guardUnhandledError(() => flowResult(projectOverviewState.createVersion(NewVersionType.MAJOR)), ); const createMinorRelease = applicationStore.guardUnhandledError(() => flowResult(projectOverviewState.createVersion(NewVersionType.MINOR)), ); const createPatchRelease = applicationStore.guardUnhandledError(() => flowResult(projectOverviewState.createVersion(NewVersionType.PATCH)), ); const changeNotes: React.ChangeEventHandler = (event) => revisionInput.setNotes(event.target.value); const notFetchedLatestVersionAndCurrentRevision = latestProjectVersion === undefined || currentProjectRevision === undefined; const isCurrentProjectVersionLatest = Boolean(latestProjectVersion) && latestProjectVersion?.revisionId === currentProjectRevision?.id; const canCreateVersion = !isCurrentProjectVersionLatest && !isDispatchingAction && editorStore.sdlcServerClient.features.canCreateVersion && editorStore.sdlcState.canCreateVersion; const disabledCreateVersionTitle = isCurrentProjectVersionLatest ? `Can't create version: project version not the latest` : !editorStore.sdlcServerClient.features.canCreateVersion ? `Can't create version: current svn system does not support creating versions` : !editorStore.sdlcState.canCreateVersion ? `Can't create version: You do not have the rights to create a version` : undefined; // since this can be affected by other users, we refresh it more proactively useEffect(() => { flowResult(projectOverviewState.fetchLatestProjectVersion()).catch( applicationStore.alertUnhandledError, ); }, [ applicationStore, editorStore.sdlcState.activePatch, projectOverviewState, ]); return (
{PROJECT_OVERVIEW_ACTIVITY_MODE.RELEASE}