From 1d409d5db753fe003513f4e1a8d99a0fe6c1becc Mon Sep 17 00:00:00 2001 From: Wouter Admiraal Date: Tue, 25 Jan 2022 11:25:46 +0100 Subject: SONAR-15782 Move application console code to core extensions --- server/sonar-web/src/main/js/api/application.ts | 53 +--- .../main/js/app/components/nav/component/Menu.tsx | 15 - .../__tests__/__snapshots__/Menu-test.tsx.snap | 34 --- .../__snapshots__/CurrentBranchLike-test.tsx.snap | 2 +- .../src/main/js/app/utils/startReactApp.tsx | 12 +- .../application-console/ApplicationBranches.tsx | 128 --------- .../application-console/ApplicationConsoleApp.tsx | 137 ---------- .../ApplicationConsoleAppRenderer.tsx | 104 ------- .../ApplicationProjectBranch.tsx | 59 ---- .../application-console/ApplicationProjects.tsx | 233 ---------------- .../apps/application-console/BranchRowActions.tsx | 107 -------- .../apps/application-console/BranchSelectItem.tsx | 75 ----- .../apps/application-console/CreateBranchForm.tsx | 303 --------------------- .../main/js/apps/application-console/EditForm.tsx | 111 -------- .../apps/application-console/ProjectBranchRow.tsx | 138 ---------- .../__tests__/ApplicationBranches-test.tsx | 65 ----- .../__tests__/ApplicationConsoleApp-test.tsx | 136 --------- .../ApplicationConsoleAppRenderer-test.tsx | 61 ----- .../__tests__/ApplicationProjectBranch-test.tsx | 44 --- .../__tests__/ApplicationProjects-test.tsx | 105 ------- .../__tests__/BranchRowActions-test.tsx | 39 --- .../__tests__/BranchSelectItem-test.tsx | 37 --- .../__tests__/CreateBranchForm-test.tsx | 132 --------- .../__tests__/EditForm-test.tsx | 61 ----- .../__tests__/ProjectBranchRow-test.tsx | 41 --- .../ApplicationBranches-test.tsx.snap | 217 --------------- .../ApplicationConsoleApp-test.tsx.snap | 73 ----- .../ApplicationConsoleAppRenderer-test.tsx.snap | 265 ------------------ .../ApplicationProjectBranch-test.tsx.snap | 83 ------ .../ApplicationProjects-test.tsx.snap | 75 ----- .../__snapshots__/BranchRowActions-test.tsx.snap | 15 - .../__snapshots__/BranchSelectItem-test.tsx.snap | 22 -- .../__snapshots__/CreateBranchForm-test.tsx.snap | 12 - .../__tests__/__snapshots__/EditForm-test.tsx.snap | 77 ------ .../__snapshots__/ProjectBranchRow-test.tsx.snap | 59 ---- .../src/main/js/apps/application-console/routes.ts | 28 -- .../src/main/js/apps/application-console/utils.ts | 28 -- .../__tests__/ApplicationCreation-test.tsx | 2 +- .../src/main/js/helpers/mocks/application.ts | 27 +- server/sonar-web/src/main/js/helpers/urls.ts | 5 +- 40 files changed, 18 insertions(+), 3202 deletions(-) delete mode 100644 server/sonar-web/src/main/js/apps/application-console/ApplicationBranches.tsx delete mode 100644 server/sonar-web/src/main/js/apps/application-console/ApplicationConsoleApp.tsx delete mode 100644 server/sonar-web/src/main/js/apps/application-console/ApplicationConsoleAppRenderer.tsx delete mode 100644 server/sonar-web/src/main/js/apps/application-console/ApplicationProjectBranch.tsx delete mode 100644 server/sonar-web/src/main/js/apps/application-console/ApplicationProjects.tsx delete mode 100644 server/sonar-web/src/main/js/apps/application-console/BranchRowActions.tsx delete mode 100644 server/sonar-web/src/main/js/apps/application-console/BranchSelectItem.tsx delete mode 100644 server/sonar-web/src/main/js/apps/application-console/CreateBranchForm.tsx delete mode 100644 server/sonar-web/src/main/js/apps/application-console/EditForm.tsx delete mode 100644 server/sonar-web/src/main/js/apps/application-console/ProjectBranchRow.tsx delete mode 100644 server/sonar-web/src/main/js/apps/application-console/__tests__/ApplicationBranches-test.tsx delete mode 100644 server/sonar-web/src/main/js/apps/application-console/__tests__/ApplicationConsoleApp-test.tsx delete mode 100644 server/sonar-web/src/main/js/apps/application-console/__tests__/ApplicationConsoleAppRenderer-test.tsx delete mode 100644 server/sonar-web/src/main/js/apps/application-console/__tests__/ApplicationProjectBranch-test.tsx delete mode 100644 server/sonar-web/src/main/js/apps/application-console/__tests__/ApplicationProjects-test.tsx delete mode 100644 server/sonar-web/src/main/js/apps/application-console/__tests__/BranchRowActions-test.tsx delete mode 100644 server/sonar-web/src/main/js/apps/application-console/__tests__/BranchSelectItem-test.tsx delete mode 100644 server/sonar-web/src/main/js/apps/application-console/__tests__/CreateBranchForm-test.tsx delete mode 100644 server/sonar-web/src/main/js/apps/application-console/__tests__/EditForm-test.tsx delete mode 100644 server/sonar-web/src/main/js/apps/application-console/__tests__/ProjectBranchRow-test.tsx delete mode 100644 server/sonar-web/src/main/js/apps/application-console/__tests__/__snapshots__/ApplicationBranches-test.tsx.snap delete mode 100644 server/sonar-web/src/main/js/apps/application-console/__tests__/__snapshots__/ApplicationConsoleApp-test.tsx.snap delete mode 100644 server/sonar-web/src/main/js/apps/application-console/__tests__/__snapshots__/ApplicationConsoleAppRenderer-test.tsx.snap delete mode 100644 server/sonar-web/src/main/js/apps/application-console/__tests__/__snapshots__/ApplicationProjectBranch-test.tsx.snap delete mode 100644 server/sonar-web/src/main/js/apps/application-console/__tests__/__snapshots__/ApplicationProjects-test.tsx.snap delete mode 100644 server/sonar-web/src/main/js/apps/application-console/__tests__/__snapshots__/BranchRowActions-test.tsx.snap delete mode 100644 server/sonar-web/src/main/js/apps/application-console/__tests__/__snapshots__/BranchSelectItem-test.tsx.snap delete mode 100644 server/sonar-web/src/main/js/apps/application-console/__tests__/__snapshots__/CreateBranchForm-test.tsx.snap delete mode 100644 server/sonar-web/src/main/js/apps/application-console/__tests__/__snapshots__/EditForm-test.tsx.snap delete mode 100644 server/sonar-web/src/main/js/apps/application-console/__tests__/__snapshots__/ProjectBranchRow-test.tsx.snap delete mode 100644 server/sonar-web/src/main/js/apps/application-console/routes.ts delete mode 100644 server/sonar-web/src/main/js/apps/application-console/utils.ts (limited to 'server/sonar-web') diff --git a/server/sonar-web/src/main/js/api/application.ts b/server/sonar-web/src/main/js/api/application.ts index 42518728ba0..209c67af262 100644 --- a/server/sonar-web/src/main/js/api/application.ts +++ b/server/sonar-web/src/main/js/api/application.ts @@ -19,7 +19,7 @@ */ import throwGlobalError from '../app/utils/throwGlobalError'; import { getJSON, post, postJSON } from '../helpers/request'; -import { Application, ApplicationPeriod, ApplicationProject } from '../types/application'; +import { Application, ApplicationPeriod } from '../types/application'; import { Visibility } from '../types/component'; export function getApplicationLeak( @@ -39,51 +39,6 @@ export function getApplicationDetails(application: string, branch?: string): Pro ); } -export function addApplicationBranch(data: { - application: string; - branch: string; - project: string[]; - projectBranch: string[]; -}) { - return post('/api/applications/create_branch', data).catch(throwGlobalError); -} - -export function updateApplicationBranch(data: { - application: string; - branch: string; - name: string; - project: string[]; - projectBranch: string[]; -}) { - return post('/api/applications/update_branch', data).catch(throwGlobalError); -} - -export function deleteApplicationBranch(application: string, branch: string) { - return post('/api/applications/delete_branch', { application, branch }).catch(throwGlobalError); -} - -export function getApplicationProjects(data: { - application: string; - p?: number; - ps?: number; - q?: string; - selected: string; -}): Promise<{ paging: T.Paging; projects: ApplicationProject[] }> { - return getJSON('/api/applications/search_projects', data).catch(throwGlobalError); -} - -export function addProjectToApplication(application: string, project: string) { - return post('/api/applications/add_project', { application, project }).catch(throwGlobalError); -} - -export function removeProjectFromApplication(application: string, project: string) { - return post('/api/applications/remove_project', { application, project }).catch(throwGlobalError); -} - -export function refreshApplication(key: string) { - return post('/api/applications/refresh', { key }).catch(throwGlobalError); -} - export function createApplication( name: string, description: string, @@ -105,9 +60,3 @@ export function createApplication( export function deleteApplication(application: string) { return post('/api/applications/delete', { application }).catch(throwGlobalError); } - -export function editApplication(application: string, name: string, description: string) { - return post('/api/applications/update', { name, description, application }).catch( - throwGlobalError - ); -} diff --git a/server/sonar-web/src/main/js/app/components/nav/component/Menu.tsx b/server/sonar-web/src/main/js/app/components/nav/component/Menu.tsx index 4cb44009b46..dead2fed58b 100644 --- a/server/sonar-web/src/main/js/app/components/nav/component/Menu.tsx +++ b/server/sonar-web/src/main/js/app/components/nav/component/Menu.tsx @@ -36,7 +36,6 @@ import { ComponentQualifier, isPortfolioLike } from '../../../../types/component import './Menu.css'; const SETTINGS_URLS = [ - '/application/console', '/project/admin', '/project/baseline', '/project/branches', @@ -319,7 +318,6 @@ export class Menu extends React.PureComponent { this.renderSettingsLink(query, isApplication, isPortfolio), this.renderBranchesLink(query, isProject), this.renderBaselineLink(query, isApplication, isPortfolio), - this.renderConsoleAppLink(query, isApplication), this.renderReportSettingsLink(query, isApplication), ...this.renderAdminExtensions(query, isApplication), this.renderImportExportLink(query, isProject), @@ -412,19 +410,6 @@ export class Menu extends React.PureComponent { ); }; - renderConsoleAppLink = (query: Query, isApplication: boolean) => { - if (!isApplication) { - return null; - } - return ( -
  • - - {translate('application_console.page')} - -
  • - ); - }; - renderReportSettingsLink = (query: Query, isApplication: boolean) => { const extensions = this.getConfiguration().extensions || []; const hasGovernance = extensions.find(e => e.key === 'governance/console'); diff --git a/server/sonar-web/src/main/js/app/components/nav/component/__tests__/__snapshots__/Menu-test.tsx.snap b/server/sonar-web/src/main/js/app/components/nav/component/__tests__/__snapshots__/Menu-test.tsx.snap index e97e4aa5b53..87467c85c81 100644 --- a/server/sonar-web/src/main/js/app/components/nav/component/__tests__/__snapshots__/Menu-test.tsx.snap +++ b/server/sonar-web/src/main/js/app/components/nav/component/__tests__/__snapshots__/Menu-test.tsx.snap @@ -85,23 +85,6 @@ exports[`should disable links if application has inaccessible projects 1`] = `
      -
    • - - application_console.page - -
    • -
    • - - application_console.page - -
    • + { + replace({ + pathname: '/project/admin/extension/developer-server/application-console', + query: { id: nextState.location.query.id } + }); + }} + /> + { @@ -217,7 +226,6 @@ function renderComponentRoutes() { - void; -} - -interface State { - creating: boolean; -} - -export default class ApplicationBranches extends React.PureComponent { - state: State = { creating: false }; - - handleCreate = (branch: ApplicationBranch) => { - this.props.onUpdateBranches([...this.props.application.branches, branch]); - }; - - handleCreateFormClose = () => { - this.setState({ creating: false }); - }; - - handleCreateClick = () => { - this.setState({ creating: true }); - }; - - canCreateBranches = () => { - return ( - this.props.application.projects && - this.props.application.projects.some(p => Boolean(p.enabled)) - ); - }; - - renderBranches(createEnable: boolean, readonly: boolean) { - const { application } = this.props; - if (!createEnable) { - return ( -
      -

      - {translate('application_console.branches.no_branches')} -

      -
      - ); - } - return ( -
      - - - {application.branches.map(branch => ( - - ))} - -
      -
      - ); - } - - render() { - const { application, canBrowseAllChildProjects } = this.props; - const createEnable = this.canCreateBranches(); - const readonly = !canBrowseAllChildProjects; - return ( -
      - {readonly && ( - - {translate('application_console.branches.cannot_access_all_child_projects')} - - )} - -
      - -
      -

      - {translate('application_console.branches')} -

      -

      {translate('application_console.branches.help')}

      - - {this.renderBranches(createEnable, readonly)} - - {this.state.creating && ( - p.key)} - onClose={this.handleCreateFormClose} - onCreate={this.handleCreate} - /> - )} -
      - ); - } -} diff --git a/server/sonar-web/src/main/js/apps/application-console/ApplicationConsoleApp.tsx b/server/sonar-web/src/main/js/apps/application-console/ApplicationConsoleApp.tsx deleted file mode 100644 index 21b7bac88ee..00000000000 --- a/server/sonar-web/src/main/js/apps/application-console/ApplicationConsoleApp.tsx +++ /dev/null @@ -1,137 +0,0 @@ -/* - * SonarQube - * Copyright (C) 2009-2021 SonarSource SA - * mailto:info AT sonarsource DOT com - * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 3 of the License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public License - * along with this program; if not, write to the Free Software Foundation, - * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. - */ -import { Location } from 'history'; -import * as React from 'react'; -import { InjectedRouter } from 'react-router'; -import { editApplication, getApplicationDetails, refreshApplication } from '../../api/application'; -import addGlobalSuccessMessage from '../../app/utils/addGlobalSuccessMessage'; -import { translate } from '../../helpers/l10n'; -import { Application, ApplicationProject } from '../../types/application'; -import ApplicationConsoleAppRenderer from './ApplicationConsoleAppRenderer'; -import { ApplicationBranch } from './utils'; - -interface Props { - component: Pick; - location: Location; - router: Pick; -} - -interface State { - application?: Application; - loading: boolean; -} - -export default class ApplicationConsoleApp extends React.PureComponent { - mounted = false; - - state: State = { - loading: false - }; - - componentDidMount() { - this.mounted = true; - this.fetchDetails(); - } - - componentDidUpdate(prevProps: Props) { - if (prevProps.component.key !== this.props.component.key) { - this.fetchDetails(); - } - } - - componentWillUnmount() { - this.mounted = false; - } - - updateApplicationState = (buildNewFields: (prevApp: Application) => Partial) => { - this.setState(state => { - if (state.application) { - return { application: { ...state.application, ...buildNewFields(state.application) } }; - } - - return null; - }); - }; - - fetchDetails = async () => { - try { - const application = await getApplicationDetails(this.props.component.key); - if (this.mounted) { - this.setState({ application, loading: false }); - } - } catch { - if (this.mounted) { - this.setState({ loading: false }); - } - } - }; - - handleRefreshClick = async () => { - if (this.state.application) { - await refreshApplication(this.state.application.key); - addGlobalSuccessMessage(translate('application_console.refresh_started')); - } - }; - - handleEdit = async (name: string, description: string) => { - if (this.state.application) { - await editApplication(this.state.application.key, name, description); - } - if (this.mounted) { - this.updateApplicationState(() => ({ name, description })); - } - }; - - handleAddProject = (project: ApplicationProject) => { - this.updateApplicationState(prevApp => ({ projects: [...prevApp.projects, project] })); - }; - - handleRemoveProject = (projectKey: string) => { - this.updateApplicationState(prevApp => ({ - projects: prevApp.projects.filter(p => p.key !== projectKey) - })); - }; - - handleUpdateBranches = (branches: ApplicationBranch[]) => { - this.updateApplicationState(() => ({ branches })); - }; - - render() { - const { component } = this.props; - const { application, loading } = this.state; - if (!application) { - // when application is not found - return null; - } - - return ( - - ); - } -} diff --git a/server/sonar-web/src/main/js/apps/application-console/ApplicationConsoleAppRenderer.tsx b/server/sonar-web/src/main/js/apps/application-console/ApplicationConsoleAppRenderer.tsx deleted file mode 100644 index 207ee55edc6..00000000000 --- a/server/sonar-web/src/main/js/apps/application-console/ApplicationConsoleAppRenderer.tsx +++ /dev/null @@ -1,104 +0,0 @@ -/* - * SonarQube - * Copyright (C) 2009-2021 SonarSource SA - * mailto:info AT sonarsource DOT com - * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 3 of the License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public License - * along with this program; if not, write to the Free Software Foundation, - * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. - */ -import * as React from 'react'; -import { Button } from '../../components/controls/buttons'; -import { translate } from '../../helpers/l10n'; -import { Application, ApplicationProject } from '../../types/application'; -import { Branch } from '../../types/branch-like'; -import ApplicationBranches from './ApplicationBranches'; -import ApplicationProjects from './ApplicationProjects'; -import EditForm from './EditForm'; - -export interface ApplicationConsoleAppRendererProps { - loading: boolean; - application: Application; - canBrowseAllChildProjects: boolean; - onAddProject: (project: ApplicationProject) => void; - onRefresh: () => void; - onEdit: (name: string, description: string) => Promise; - onRemoveProject: (projectKey: string) => void; - onUpdateBranches: (branches: Branch[]) => void; -} - -export default function ApplicationConsoleAppRenderer(props: ApplicationConsoleAppRendererProps) { - const [editing, setEditing] = React.useState(false); - - const { application, canBrowseAllChildProjects, loading } = props; - - if (loading) { - return ; - } - - return ( -
      -
      -
      - - -
      - -
      -

      - {application.name} -

      -
      - -
      -
      - {application.description && ( -
      {application.description}
      - )} -
      - {translate('key')}: {application.key} -
      -
      - - - - -
      - - {editing && ( - setEditing(false)} - onEdit={props.onEdit} - application={application} - /> - )} -
      -
      - ); -} diff --git a/server/sonar-web/src/main/js/apps/application-console/ApplicationProjectBranch.tsx b/server/sonar-web/src/main/js/apps/application-console/ApplicationProjectBranch.tsx deleted file mode 100644 index 15b74069735..00000000000 --- a/server/sonar-web/src/main/js/apps/application-console/ApplicationProjectBranch.tsx +++ /dev/null @@ -1,59 +0,0 @@ -/* - * SonarQube - * Copyright (C) 2009-2021 SonarSource SA - * mailto:info AT sonarsource DOT com - * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 3 of the License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public License - * along with this program; if not, write to the Free Software Foundation, - * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. - */ -import * as React from 'react'; -import BranchIcon from '../../components/icons/BranchIcon'; -import { translate } from '../../helpers/l10n'; -import { Application } from '../../types/application'; -import BranchRowActions from './BranchRowActions'; -import { ApplicationBranch } from './utils'; - -export interface ApplicationProjectBranchProps { - application: Application; - branch: ApplicationBranch; - onUpdateBranches: (branches: Array) => void; - readonly: boolean; -} - -export default function ApplicationProjectBranch(props: ApplicationProjectBranchProps) { - const { application, branch, readonly } = props; - - return ( - - - - {branch.name} - {branch.isMain && ( - - {translate('application_console.branches.main_branch')} - - )} - - - {!branch.isMain && !readonly && ( - - )} - - - ); -} diff --git a/server/sonar-web/src/main/js/apps/application-console/ApplicationProjects.tsx b/server/sonar-web/src/main/js/apps/application-console/ApplicationProjects.tsx deleted file mode 100644 index c7ca29443b0..00000000000 --- a/server/sonar-web/src/main/js/apps/application-console/ApplicationProjects.tsx +++ /dev/null @@ -1,233 +0,0 @@ -/* - * SonarQube - * Copyright (C) 2009-2021 SonarSource SA - * mailto:info AT sonarsource DOT com - * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 3 of the License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public License - * along with this program; if not, write to the Free Software Foundation, - * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. - */ -import classNames from 'classnames'; -import { find, without } from 'lodash'; -import * as React from 'react'; -import { - addProjectToApplication, - getApplicationProjects, - removeProjectFromApplication -} from '../../api/application'; -import SelectList, { - SelectListFilter, - SelectListSearchParams -} from '../../components/controls/SelectList'; -import Tooltip from '../../components/controls/Tooltip'; -import QualifierIcon from '../../components/icons/QualifierIcon'; -import WarningIcon from '../../components/icons/WarningIcon'; -import { translate } from '../../helpers/l10n'; -import { Application, ApplicationProject } from '../../types/application'; - -interface Props { - onAddProject?: (project: ApplicationProject) => void; - onRemoveProject?: (projectKey: string) => void; - application: Application; -} - -interface State { - disabledProjects: string[]; - lastSearchParams: SelectListSearchParams & { applicationKey: string }; - needToReload: boolean; - projects: Array; - projectsTotalCount?: number; - selectedProjects: string[]; -} - -export default class ApplicationProjects extends React.PureComponent { - mounted = false; - - constructor(props: Props) { - super(props); - - this.state = { - disabledProjects: [], - lastSearchParams: { - applicationKey: props.application.key, - query: '', - filter: SelectListFilter.Selected - }, - needToReload: false, - projects: [], - selectedProjects: [] - }; - } - - componentDidMount() { - this.mounted = true; - } - - componentDidUpdate(prevProps: Props) { - if (prevProps.application.key !== this.props.application.key) { - this.setState( - prevState => { - return { - lastSearchParams: { - ...prevState.lastSearchParams, - applicationKey: this.props.application.key - } - }; - }, - () => this.fetchProjects(this.state.lastSearchParams) - ); - } - } - - componentWillUnmount() { - this.mounted = false; - } - - loadApplicationProjects = (searchParams: SelectListSearchParams) => - getApplicationProjects({ - application: this.state.lastSearchParams.applicationKey, - p: searchParams.page, - ps: searchParams.pageSize, - q: searchParams.query !== '' ? searchParams.query : undefined, - selected: searchParams.filter - }); - - fetchProjects = (searchParams: SelectListSearchParams) => - this.loadApplicationProjects(searchParams).then(data => { - if (this.mounted) { - this.setState(prevState => { - const more = searchParams.page != null && searchParams.page > 1; - - const { projects, selectedProjects, disabledProjects } = this.dealWithProjects( - data, - more, - prevState - ); - - return { - disabledProjects, - lastSearchParams: { ...prevState.lastSearchParams, ...searchParams }, - needToReload: false, - projects, - projectsTotalCount: data.paging.total, - selectedProjects - }; - }); - } - }); - - dealWithProjects = ( - data: { projects: Array; paging: T.Paging }, - more: boolean, - prevState: Readonly - ) => { - const projects = more ? [...prevState.projects, ...data.projects] : data.projects; - - const newSelectedProjects = data.projects - .filter(project => project.selected) - .map(project => project.key); - const selectedProjects = more - ? [...prevState.selectedProjects, ...newSelectedProjects] - : newSelectedProjects; - - const disabledProjects = more ? [...prevState.disabledProjects] : []; - - return { - disabledProjects, - projects, - selectedProjects - }; - }; - - handleSelect = (projectKey: string) => { - return addProjectToApplication(this.props.application.key, projectKey).then(() => { - if (this.mounted) { - this.setState(state => { - const project = state.projects.find(p => p.key === projectKey); - if (project && this.props.onAddProject) { - this.props.onAddProject(project); - } - return { - needToReload: true, - selectedProjects: [...state.selectedProjects, projectKey] - }; - }); - } - }); - }; - - handleUnselect = (projectKey: string) => { - return removeProjectFromApplication(this.props.application.key, projectKey).then(() => { - if (this.mounted) { - this.setState(state => { - if (this.props.onRemoveProject) { - this.props.onRemoveProject(projectKey); - } - return { - needToReload: true, - selectedProjects: without(state.selectedProjects, projectKey) - }; - }); - } - }); - }; - - renderElement = (projectKey: string) => { - const project = find(this.state.projects, { key: projectKey }); - if (project === undefined) { - return ''; - } - - return ( -
      - {!project.accessible ? ( - - - - ) : ( - - )} -
      -
      {project.name}
      -
      {project.key}
      -
      -
      - ); - }; - - render() { - const { projects, selectedProjects } = this.state; - - return ( - project.key)} - elementsTotalCount={this.state.projectsTotalCount} - needToReload={ - this.state.needToReload && - this.state.lastSearchParams && - this.state.lastSearchParams.filter !== SelectListFilter.All - } - onSearch={this.fetchProjects} - onSelect={this.handleSelect} - onUnselect={this.handleUnselect} - renderElement={this.renderElement} - selectedElements={selectedProjects} - withPaging={true} - /> - ); - } -} diff --git a/server/sonar-web/src/main/js/apps/application-console/BranchRowActions.tsx b/server/sonar-web/src/main/js/apps/application-console/BranchRowActions.tsx deleted file mode 100644 index c62245f90e2..00000000000 --- a/server/sonar-web/src/main/js/apps/application-console/BranchRowActions.tsx +++ /dev/null @@ -1,107 +0,0 @@ -/* - * SonarQube - * Copyright (C) 2009-2021 SonarSource SA - * mailto:info AT sonarsource DOT com - * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 3 of the License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public License - * along with this program; if not, write to the Free Software Foundation, - * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. - */ -import { without } from 'lodash'; -import * as React from 'react'; -import { deleteApplicationBranch } from '../../api/application'; -import ActionsDropdown, { ActionsDropdownItem } from '../../components/controls/ActionsDropdown'; -import ConfirmButton from '../../components/controls/ConfirmButton'; -import { translate, translateWithParameters } from '../../helpers/l10n'; -import { Application } from '../../types/application'; -import CreateBranchForm from './CreateBranchForm'; -import { ApplicationBranch } from './utils'; - -interface Props { - application: Application; - branch: ApplicationBranch; - onUpdateBranches: (branches: Array) => void; -} - -interface State { - isUpdating: boolean; -} - -export default class BranchRowActions extends React.PureComponent { - state: State = { isUpdating: false }; - - handleDelete = () => { - const { application, branch } = this.props; - return deleteApplicationBranch(application.key, branch.name).then(() => { - this.props.onUpdateBranches(without(application.branches, branch)); - }); - }; - - handleUpdate = (newBranchName: string) => { - this.props.onUpdateBranches( - this.props.application.branches.map(branch => { - if (branch.name === this.props.branch.name) { - branch.name = newBranchName; - } - return branch; - }) - ); - }; - - handleCloseForm = () => { - this.setState({ isUpdating: false }); - }; - - handleUpdateClick = () => { - this.setState({ isUpdating: true }); - }; - - render() { - return ( - <> - - {({ onClick }) => ( - - - {translate('edit')} - - - {translate('delete')} - - - )} - - - {this.state.isUpdating && ( - p.enabled) - .map(p => p.key)} - onClose={this.handleCloseForm} - onUpdate={this.handleUpdate} - /> - )} - - ); - } -} diff --git a/server/sonar-web/src/main/js/apps/application-console/BranchSelectItem.tsx b/server/sonar-web/src/main/js/apps/application-console/BranchSelectItem.tsx deleted file mode 100644 index 439bc4d742c..00000000000 --- a/server/sonar-web/src/main/js/apps/application-console/BranchSelectItem.tsx +++ /dev/null @@ -1,75 +0,0 @@ -/* - * SonarQube - * Copyright (C) 2009-2021 SonarSource SA - * mailto:info AT sonarsource DOT com - * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 3 of the License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public License - * along with this program; if not, write to the Free Software Foundation, - * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. - */ -import * as React from 'react'; -import Tooltip from '../../components/controls/Tooltip'; -import BranchIcon from '../../components/icons/BranchIcon'; - -export interface Option { - label: string; - type: string; - value: string; -} - -interface Props { - option: Option; - children?: React.ReactNode; - className?: string; - isFocused?: boolean; - onFocus: (option: Option, event: React.SyntheticEvent) => void; - onSelect: (option: Option, event: React.SyntheticEvent) => void; -} - -export default class BranchSelectItem extends React.PureComponent { - handleMouseDown = (event: React.MouseEvent) => { - event.preventDefault(); - event.stopPropagation(); - this.props.onSelect(this.props.option, event); - }; - - handleMouseEnter = (event: React.MouseEvent) => { - this.props.onFocus(this.props.option, event); - }; - - handleMouseMove = (event: React.MouseEvent) => { - if (this.props.isFocused) { - return; - } - this.props.onFocus(this.props.option, event); - }; - - render() { - const { option } = this.props; - return ( - -
      -
      - - {option.label} -
      -
      -
      - ); - } -} diff --git a/server/sonar-web/src/main/js/apps/application-console/CreateBranchForm.tsx b/server/sonar-web/src/main/js/apps/application-console/CreateBranchForm.tsx deleted file mode 100644 index 8b25d018c77..00000000000 --- a/server/sonar-web/src/main/js/apps/application-console/CreateBranchForm.tsx +++ /dev/null @@ -1,303 +0,0 @@ -/* - * SonarQube - * Copyright (C) 2009-2021 SonarSource SA - * mailto:info AT sonarsource DOT com - * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 3 of the License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public License - * along with this program; if not, write to the Free Software Foundation, - * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. - */ -import { some, without } from 'lodash'; -import * as React from 'react'; -import { - addApplicationBranch, - getApplicationDetails, - updateApplicationBranch -} from '../../api/application'; -import { ResetButtonLink, SubmitButton } from '../../components/controls/buttons'; -import SimpleModal from '../../components/controls/SimpleModal'; -import DeferredSpinner from '../../components/ui/DeferredSpinner'; -import MandatoryFieldMarker from '../../components/ui/MandatoryFieldMarker'; -import MandatoryFieldsExplanation from '../../components/ui/MandatoryFieldsExplanation'; -import { translate } from '../../helpers/l10n'; -import { Application, ApplicationProject } from '../../types/application'; -import ProjectBranchRow from './ProjectBranchRow'; -import { ApplicationBranch, SelectBranchOption } from './utils'; - -interface Props { - application: Application; - branch?: ApplicationBranch; - enabledProjectsKey: string[]; - onClose: () => void; - onCreate?: (branch: ApplicationBranch) => void; - onUpdate?: (name: string) => void; -} - -interface BranchesList { - [name: string]: SelectBranchOption | null; -} - -interface State { - loading: boolean; - name: string; - projects: ApplicationProject[]; - selected: string[]; - selectedBranches: BranchesList; -} - -const MAX_PROJECTS_HEIGHT = 220; -const PROJECT_HEIGHT = 22; -export default class CreateBranchForm extends React.PureComponent { - mounted = false; - node?: HTMLElement | null = null; - currentSelect?: HTMLElement | null = null; - - state: State = { - loading: false, - name: '', - projects: [], - selected: [], - selectedBranches: {} - }; - - componentDidMount() { - this.mounted = true; - const { application } = this.props; - const branch = this.props.branch ? this.props.branch.name : undefined; - this.setState({ loading: true }); - getApplicationDetails(application.key, branch).then( - ({ projects }) => { - if (this.mounted) { - const enabledProjects = projects.filter(p => - this.props.enabledProjectsKey.includes(p.key) - ); - const selected = enabledProjects.filter(p => p.selected).map(p => p.key); - const selectedBranches: BranchesList = {}; - enabledProjects.forEach(p => { - if (!p.enabled) { - selectedBranches[p.key] = null; - } else { - selectedBranches[p.key] = { - value: p.branch || '', - label: p.branch || '', - isMain: p.isMain || false - }; - } - }); - this.setState({ - name: branch || '', - selected, - loading: false, - projects: enabledProjects, - selectedBranches - }); - } - }, - () => { - this.props.onClose(); - } - ); - } - - componentWillUnmount() { - this.mounted = false; - } - - stopLoading = () => { - if (this.mounted) { - this.setState({ loading: false }); - } - }; - - canSubmit = () => { - const hasUnselectedBranches = some(this.state.selectedBranches, (branch, projectKey) => { - return !branch && this.state.selected.includes(projectKey); - }); - return ( - !this.state.loading && - this.state.name.length > 0 && - !hasUnselectedBranches && - this.state.selected.length > 0 - ); - }; - - handleInputChange = (event: React.ChangeEvent) => { - this.setState({ name: event.currentTarget.value }); - }; - - handleFormSubmit = async () => { - const projectKeys = this.state.selected; - - const projectBranches = projectKeys.map(p => { - const branch = this.state.selectedBranches[p]; - return !branch || branch.isMain ? '' : branch.value; - }); - - if (this.props.branch) { - await updateApplicationBranch({ - application: this.props.application.key, - branch: this.props.branch.name, - name: this.state.name, - project: projectKeys, - projectBranch: projectBranches - }); - if (this.props.onUpdate) { - this.props.onUpdate(this.state.name); - } - } else { - await addApplicationBranch({ - application: this.props.application.key, - branch: this.state.name, - project: projectKeys, - projectBranch: projectBranches - }); - if (this.props.onCreate) { - this.props.onCreate({ name: this.state.name, isMain: false }); - } - } - this.props.onClose(); - }; - - handleProjectCheck = (checked: boolean, key: string) => { - this.setState(state => ({ - selected: checked ? [...state.selected, key] : without(state.selected, key) - })); - }; - - handleBranchChange = (projectKey: string, branch: SelectBranchOption) => { - this.setState(state => ({ - selectedBranches: { ...state.selectedBranches, [projectKey]: branch } - })); - }; - - handleSelectorClose = () => { - if (this.node) { - this.node.classList.add('selector-hidden'); - } - }; - - handleSelectorDirection = (selectNode: HTMLElement, elementCount: number) => { - if (this.node) { - const modalTop = this.node.getBoundingClientRect().top; - const modalHeight = this.node.offsetHeight; - const maxSelectHeight = Math.min(MAX_PROJECTS_HEIGHT, (elementCount + 1) * PROJECT_HEIGHT); - const selectBottom = selectNode.getBoundingClientRect().top + maxSelectHeight; - if (selectBottom > modalTop + modalHeight) { - this.node.classList.add('inverted-direction'); - } else { - this.node.classList.remove('inverted-direction'); - } - this.node.classList.remove('selector-hidden'); - } - }; - - renderProjectsList = () => { - return ( - <> - - {translate('application_console.branches.configuration')} - -

      - {translate('application_console.branches.create.help')} -

      - - - - - - - - - {this.state.projects.map(project => ( - - ))} - -
      - {translate('project')}{translate('branch')}
      - - ); - }; - - render() { - const isUpdating = this.props.branch !== undefined; - const header = translate('application_console.branches', isUpdating ? 'update' : 'create'); - return ( - - {({ onCloseClick, onFormSubmit, submitting }) => ( -
      -
      -

      {header}

      -
      - -
      (this.node = node)}> - {this.state.loading ? ( -
      - -
      - ) : ( - <> - -
      - - -
      - {this.renderProjectsList()} - - )} -
      - -
      - - - {translate( - 'application_console.branches', - isUpdating ? 'update' : 'create', - 'verb' - )} - - - {translate('application_console.branches.cancel')} - -
      - - )} -
      - ); - } -} diff --git a/server/sonar-web/src/main/js/apps/application-console/EditForm.tsx b/server/sonar-web/src/main/js/apps/application-console/EditForm.tsx deleted file mode 100644 index 6e1cd3f2033..00000000000 --- a/server/sonar-web/src/main/js/apps/application-console/EditForm.tsx +++ /dev/null @@ -1,111 +0,0 @@ -/* - * SonarQube - * Copyright (C) 2009-2021 SonarSource SA - * mailto:info AT sonarsource DOT com - * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 3 of the License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public License - * along with this program; if not, write to the Free Software Foundation, - * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. - */ -import * as React from 'react'; -import { ResetButtonLink, SubmitButton } from '../../components/controls/buttons'; -import SimpleModal from '../../components/controls/SimpleModal'; -import DeferredSpinner from '../../components/ui/DeferredSpinner'; -import { translate } from '../../helpers/l10n'; -import { Application } from '../../types/application'; - -interface Props { - header: string; - onClose: () => void; - onEdit: (name: string, description: string) => Promise; - application: Application; -} - -interface State { - description: string; - name: string; -} - -export default class EditForm extends React.PureComponent { - constructor(props: Props) { - super(props); - this.state = { - description: props.application.description || '', - name: props.application.name - }; - } - - handleNameChange = (event: React.ChangeEvent) => { - this.setState({ name: event.currentTarget.value }); - }; - - handleDescriptionChange = (event: React.ChangeEvent) => { - this.setState({ description: event.currentTarget.value }); - }; - - handleFormSubmit = async () => { - await this.props.onEdit(this.state.name, this.state.description); - this.props.onClose(); - }; - - render() { - return ( - - {({ onCloseClick, onFormSubmit, submitting }) => ( -
      -
      -

      {this.props.header}

      -
      - -
      -
      - - -
      -
      - -