From: Stas Vilchik Date: Thu, 11 Feb 2016 17:57:39 +0000 (+0100) Subject: SONAR-7191 Update the background tasks page to reflect latest WS changes X-Git-Tag: 5.5-M2~14 X-Git-Url: https://source.dussan.org/?a=commitdiff_plain;h=4621ff0206176da5af9fc8c1c704a2ca5aded1ad;p=sonarqube.git SONAR-7191 Update the background tasks page to reflect latest WS changes --- diff --git a/server/sonar-web/src/main/js/api/ce.js b/server/sonar-web/src/main/js/api/ce.js index c31db7b0dbc..dc36ed444a3 100644 --- a/server/sonar-web/src/main/js/api/ce.js +++ b/server/sonar-web/src/main/js/api/ce.js @@ -18,6 +18,7 @@ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ import $ from 'jquery'; +import { getJSON, post } from '../helpers/request.js'; export function getQueue (data) { const url = baseUrl + '/api/ce/queue'; @@ -30,22 +31,30 @@ export function getActivity (data) { } export function getTask (id) { - const url = baseUrl + '/api/ce/task'; - return $.get(url, { id }); + const url = window.baseUrl + '/api/ce/task'; + return getJSON(url, { id }).then(r => r.task); } export function cancelTask (id) { - const url = baseUrl + '/api/ce/cancel'; - return $.post(url, { id }).then(getTask.bind(null, id)); + const url = window.baseUrl + '/api/ce/cancel'; + return post(url, { id }).then( + getTask.bind(null, id), + getTask.bind(null, id) + ); } export function cancelAllTasks () { - const url = baseUrl + '/api/ce/cancel_all'; - return $.post(url); + const url = window.baseUrl + '/api/ce/cancel_all'; + return post(url); } -export function getTasksForComponent(componentId) { +export function getTasksForComponent (componentId) { const url = baseUrl + '/api/ce/component'; const data = { componentId }; return new Promise((resolve) => $.get(url, data).done(resolve)); } + +export function getTypes () { + const url = window.baseUrl + '/api/ce/task_types'; + return getJSON(url).then(r => r.taskTypes); +} diff --git a/server/sonar-web/src/main/js/apps/background-tasks/app.js b/server/sonar-web/src/main/js/apps/background-tasks/app.js index 23a39dd0423..d1626102c8f 100644 --- a/server/sonar-web/src/main/js/apps/background-tasks/app.js +++ b/server/sonar-web/src/main/js/apps/background-tasks/app.js @@ -19,9 +19,22 @@ */ import React from 'react'; import ReactDOM from 'react-dom'; -import Main from './main'; +import { Provider } from 'react-redux'; + +import BackgroundTasksAppContainer from './containers/BackgroundTasksAppContainer'; +import rootReducer from './store/reducers'; +import configureStore from '../../components/store/configureStore'; + +import './styles/background-tasks.css'; window.sonarqube.appStarted.then(options => { - let el = document.querySelector(options.el); - ReactDOM.render(
, el); + const el = document.querySelector(options.el); + + const store = configureStore(rootReducer); + + ReactDOM.render(( + + + + ), el); }); diff --git a/server/sonar-web/src/main/js/apps/background-tasks/components/BackgroundTasksApp.js b/server/sonar-web/src/main/js/apps/background-tasks/components/BackgroundTasksApp.js new file mode 100644 index 00000000000..9f2192ab629 --- /dev/null +++ b/server/sonar-web/src/main/js/apps/background-tasks/components/BackgroundTasksApp.js @@ -0,0 +1,76 @@ +/* + * SonarQube + * Copyright (C) 2009-2016 SonarSource SA + * mailto:contact 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 moment from 'moment'; +import React, { Component } from 'react'; + +import { DATE } from './../constants'; +import Header from './Header'; +import StatsContainer from '../containers/StatsContainer'; +import SearchContainer from '../containers/SearchContainer'; +import TasksContainer from '../containers/TasksContainer'; +import ListFooterContainer from '../containers/ListFooterContainer'; + + +export default class BackgroundTasksApp extends Component { + componentDidMount () { + this.props.initApp(); + } + + getComponentFilter () { + if (this.props.options.component) { + return { componentId: this.props.options.component.id }; + } else { + return {}; + } + } + + getDateFilter () { + const DATE_FORMAT = 'YYYY-MM-DD'; + let filter = {}; + switch (this.state.dateFilter) { + case DATE.TODAY: + filter.minSubmittedAt = moment().startOf('day').format(DATE_FORMAT); + break; + case DATE.CUSTOM: + if (this.state.minDate) { + filter.minSubmittedAt = moment(this.state.minDate).format(DATE_FORMAT); + } + if (this.state.maxDate) { + filter.maxExecutedAt = moment(this.state.maxDate).format(DATE_FORMAT); + } + break; + default: + // do nothing + } + return filter; + } + + render () { + return ( +
+
+ + + + +
+ ); + } +} diff --git a/server/sonar-web/src/main/js/apps/background-tasks/components/CurrentsFilter.js b/server/sonar-web/src/main/js/apps/background-tasks/components/CurrentsFilter.js new file mode 100644 index 00000000000..bcbfd7f92d7 --- /dev/null +++ b/server/sonar-web/src/main/js/apps/background-tasks/components/CurrentsFilter.js @@ -0,0 +1,42 @@ +/* + * SonarQube + * Copyright (C) 2009-2016 SonarSource SA + * mailto:contact 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 React from 'react'; + +import Checkbox from '../../../components/shared/checkbox'; +import { CURRENTS } from '../constants'; + +export default function CurrentsFilter ({ value, onChange }) { + function handleChange (value) { + const newValue = value ? CURRENTS.ONLY_CURRENTS : CURRENTS.ALL; + onChange(newValue); + } + + const checked = value === CURRENTS.ONLY_CURRENTS; + + return ( +
+ +   + +
+ ); +} diff --git a/server/sonar-web/src/main/js/apps/background-tasks/components/DateFilter.js b/server/sonar-web/src/main/js/apps/background-tasks/components/DateFilter.js new file mode 100644 index 00000000000..b5969a75ceb --- /dev/null +++ b/server/sonar-web/src/main/js/apps/background-tasks/components/DateFilter.js @@ -0,0 +1,89 @@ +/* + * SonarQube + * Copyright (C) 2009-2016 SonarSource SA + * mailto:contact 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 $ from 'jquery'; +import moment from 'moment'; +import React, { Component } from 'react'; + +import { DATE_FORMAT } from '../constants'; + +export default class DateFilter extends Component { + componentDidMount () { + this.attachDatePicker(); + } + + componentDidUpdate () { + this.attachDatePicker(); + } + + attachDatePicker () { + let opts = { + dateFormat: 'yy-mm-dd', + changeMonth: true, + changeYear: true, + onSelect: this.handleChange.bind(this) + }; + if ($.fn && $.fn.datepicker) { + $(this.refs.minDate).datepicker(opts); + $(this.refs.maxDate).datepicker(opts); + } + } + + handleChange () { + const date = {}; + const minDateRaw = this.refs.minDate.value; + const maxDateRaw = this.refs.maxDate.value; + const minDate = moment(minDateRaw, DATE_FORMAT, true); + const maxDate = moment(maxDateRaw, DATE_FORMAT, true); + + if (minDate.isValid()) { + date.minSubmittedAt = minDate.format(DATE_FORMAT); + } + + if (maxDate.isValid()) { + date.maxExecutedAt = maxDate.format(DATE_FORMAT); + } + + this.props.onChange(date); + } + + render () { + const { minSubmittedAt, maxExecutedAt } = this.props.value; + + return ( +
+ + {' '} + +
+ ); + } +} diff --git a/server/sonar-web/src/main/js/apps/background-tasks/components/StatusFilter.js b/server/sonar-web/src/main/js/apps/background-tasks/components/StatusFilter.js new file mode 100644 index 00000000000..14a7495099b --- /dev/null +++ b/server/sonar-web/src/main/js/apps/background-tasks/components/StatusFilter.js @@ -0,0 +1,45 @@ +/* + * SonarQube + * Copyright (C) 2009-2016 SonarSource SA + * mailto:contact 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 React from 'react'; +import Select from 'react-select'; + +import { STATUSES } from '../constants'; +import { translate } from '../../../helpers/l10n'; + +export default function StatusFilter ({ value, onChange }) { + const options = [ + { value: STATUSES.ALL, label: translate('background_task.status.ALL') }, + { value: STATUSES.PENDING, label: translate('background_task.status.PENDING') }, + { value: STATUSES.IN_PROGRESS, label: translate('background_task.status.IN_PROGRESS') }, + { value: STATUSES.SUCCESS, label: translate('background_task.status.SUCCESS') }, + { value: STATUSES.FAILED, label: translate('background_task.status.FAILED') }, + { value: STATUSES.CANCELED, label: translate('background_task.status.CANCELED') } + ]; + + return ( + onChange(option.value)} + className="input-medium" + options={allOptions} + clearable={false} + searchable={false}/> + ); +} diff --git a/server/sonar-web/src/main/js/apps/background-tasks/components/header.js b/server/sonar-web/src/main/js/apps/background-tasks/components/header.js new file mode 100644 index 00000000000..3fbf337976e --- /dev/null +++ b/server/sonar-web/src/main/js/apps/background-tasks/components/header.js @@ -0,0 +1,35 @@ +/* + * SonarQube + * Copyright (C) 2009-2016 SonarSource SA + * mailto:contact 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 React from 'react'; + +import { translate } from '../../../helpers/l10n'; + +export default function Header () { + return ( +
+

+ {translate('background_tasks.page')} +

+

+ {translate('background_tasks.page.description')} +

+
+ ); +} diff --git a/server/sonar-web/src/main/js/apps/background-tasks/components/search.js b/server/sonar-web/src/main/js/apps/background-tasks/components/search.js new file mode 100644 index 00000000000..349b62ad9c3 --- /dev/null +++ b/server/sonar-web/src/main/js/apps/background-tasks/components/search.js @@ -0,0 +1,118 @@ +/* + * SonarQube + * Copyright (C) 2009-2016 SonarSource SA + * mailto:contact 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 React from 'react'; + +import StatusFilter from './StatusFilter'; +import TypesFilter from './TypesFilter'; +import CurrentsFilter from './CurrentsFilter'; +import DateFilter from './DateFilter'; +import { translate } from '../../../helpers/l10n'; + +export default React.createClass({ + + onSearchFormSubmit(e) { + e.preventDefault(); + this.onSearch(); + }, + + onSearch() { + let searchInput = this.refs.searchInput; + let query = searchInput.value; + this.props.onSearch(query); + }, + + renderSearchBox() { + if (this.props.options && this.props.options.component) { + // do not render search form on the project-level page + return null; + } + return ( + + ); + }, + + refresh(e) { + e.preventDefault(); + this.props.onRefresh(); + }, + + render() { + return ( +
+
    +
  • +
    + Status +
    + +
  • + {this.props.types.length > 1 && ( +
  • +
    + Type +
    + +
  • + )} +
  • +
    + Only Latest Analysis +
    + +
  • +
  • +
    + Date +
    + +
  • +
  • +
    + Component +
    + {this.renderSearchBox()} +
  • +
  • + +
  • +
+
+ ); + } +}); diff --git a/server/sonar-web/src/main/js/apps/background-tasks/components/stats.js b/server/sonar-web/src/main/js/apps/background-tasks/components/stats.js new file mode 100644 index 00000000000..d59e40bf0ef --- /dev/null +++ b/server/sonar-web/src/main/js/apps/background-tasks/components/stats.js @@ -0,0 +1,138 @@ +/* + * SonarQube + * Copyright (C) 2009-2016 SonarSource SA + * mailto:contact 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 React, { Component } from 'react'; + +import { formatDuration } from './../helpers'; +import { translate } from '../../../helpers/l10n'; + + +export default class Stats extends Component { + handleCancelAllPending (e) { + e.preventDefault(); + this.props.onCancelAllPending(); + } + + handleShowFailing (e) { + e.preventDefault(); + this.props.onShowFailing(); + } + + renderInProgressDuration () { + if (!this.props.inProgressDuration) { + return null; + } + return ( + + + + {formatDuration(this.props.inProgressDuration)} + + + ); + } + + renderPending () { + if (this.props.pendingCount == null) { + return null; + } + if (this.props.pendingCount > 0) { + return ( + + {this.props.pendingCount} +   + {translate('background_tasks.pending')} + + + ); + } else { + return ( + + {this.props.pendingCount} +   + {translate('background_tasks.pending')} + + ); + } + } + + renderFailures () { + if (this.props.failingCount == null) { + return null; + } + + if (this.props.options && this.props.options.component) { + return null; + } + + if (this.props.failingCount > 0) { + return ( + + {this.props.failingCount} +   + {translate('background_tasks.failures')} + + ); + } else { + return ( + + + {this.props.failingCount} + +   + {translate('background_tasks.failures')} + + ); + } + } + + render () { + return ( +
+ + {this.renderPending()} + + + {this.renderFailures()} + + {this.renderInProgressDuration()} +
+ + ); + } +} diff --git a/server/sonar-web/src/main/js/apps/background-tasks/components/tasks.js b/server/sonar-web/src/main/js/apps/background-tasks/components/tasks.js new file mode 100644 index 00000000000..85b29bd53a1 --- /dev/null +++ b/server/sonar-web/src/main/js/apps/background-tasks/components/tasks.js @@ -0,0 +1,53 @@ +/* + * SonarQube + * Copyright (C) 2009-2016 SonarSource SA + * mailto:contact 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 React from 'react'; + +import Task from './Task'; +import { translate } from '../../../helpers/l10n'; + +export default function Tasks ({ tasks, onCancelTask, onFilterTask }) { + return ( + + + + + + + + + + + + + + + {tasks.map((task, index, tasks) => ( + + ))} + +
   {translate('background_tasks.table.submitted')}{translate('background_tasks.table.started')}{translate('background_tasks.table.finished')}{translate('background_tasks.table.duration')} 
+ ); +} diff --git a/server/sonar-web/src/main/js/apps/background-tasks/constants.js b/server/sonar-web/src/main/js/apps/background-tasks/constants.js index 31e1993edc0..be338fb7168 100644 --- a/server/sonar-web/src/main/js/apps/background-tasks/constants.js +++ b/server/sonar-web/src/main/js/apps/background-tasks/constants.js @@ -27,6 +27,9 @@ export const STATUSES = { }; +export const ALL_TYPES = 'ALL_TYPES'; + + export const CURRENTS = { ALL: '__ALL__', ONLY_CURRENTS: 'CURRENTS' @@ -39,6 +42,14 @@ export const DATE = { CUSTOM: 'CUSTOM' }; +export const DEFAULT_FILTERS = { + status: STATUSES.ALL, + taskType: ALL_TYPES, + currents: CURRENTS.ALL, + date: {}, + query: '' +}; + export const DATE_FORMAT = 'YYYY-MM-DD'; diff --git a/server/sonar-web/src/main/js/apps/background-tasks/containers/BackgroundTasksAppContainer.js b/server/sonar-web/src/main/js/apps/background-tasks/containers/BackgroundTasksAppContainer.js new file mode 100644 index 00000000000..75496136964 --- /dev/null +++ b/server/sonar-web/src/main/js/apps/background-tasks/containers/BackgroundTasksAppContainer.js @@ -0,0 +1,38 @@ +/* + * SonarQube + * Copyright (C) 2009-2016 SonarSource SA + * mailto:contact 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 { connect } from 'react-redux'; + +import Main from '../components/BackgroundTasksApp'; +import { initApp } from '../store/actions'; + +function mapStateToProps () { + return {}; +} + +function mapDispatchToProps (dispatch) { + return { + initApp: () => dispatch(initApp()) + }; +} + +export default connect( + mapStateToProps, + mapDispatchToProps +)(Main); diff --git a/server/sonar-web/src/main/js/apps/background-tasks/containers/ListFooterContainer.js b/server/sonar-web/src/main/js/apps/background-tasks/containers/ListFooterContainer.js new file mode 100644 index 00000000000..048068f63f5 --- /dev/null +++ b/server/sonar-web/src/main/js/apps/background-tasks/containers/ListFooterContainer.js @@ -0,0 +1,34 @@ +/* + * SonarQube + * Copyright (C) 2009-2016 SonarSource SA + * mailto:contact 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 { connect } from 'react-redux'; + +import ListFooter from '../../../components/shared/list-footer'; + +function mapStateToProps (state) { + return { + ready: !state.fetching, + total: state.total, + count: state.tasks.length + }; +} + +export default connect( + mapStateToProps +)(ListFooter); diff --git a/server/sonar-web/src/main/js/apps/background-tasks/containers/SearchContainer.js b/server/sonar-web/src/main/js/apps/background-tasks/containers/SearchContainer.js new file mode 100644 index 00000000000..ccae8470dd2 --- /dev/null +++ b/server/sonar-web/src/main/js/apps/background-tasks/containers/SearchContainer.js @@ -0,0 +1,51 @@ +/* + * SonarQube + * Copyright (C) 2009-2016 SonarSource SA + * mailto:contact 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 { connect } from 'react-redux'; + +import Search from '../components/Search'; +import { filterTasks, search } from '../store/actions'; + +function mapStateToProps (state) { + return { + fetching: state.fetching, + status: state.status, + currents: state.currents, + date: state.date, + query: state.query, + taskType: state.taskType, + types: state.types + }; +} + +function mapDispatchToProps (dispatch) { + return { + onRefresh: () => dispatch(filterTasks()), + onStatusChange: (status) => dispatch(filterTasks({ status })), + onTypeChange: (taskType) => dispatch(filterTasks({ taskType })), + onCurrentsChange: (currents) => dispatch(filterTasks({ currents })), + onDateChange: (date) => dispatch(filterTasks({ date })), + onSearch: (query) => dispatch(search(query)) + }; +} + +export default connect( + mapStateToProps, + mapDispatchToProps +)(Search); diff --git a/server/sonar-web/src/main/js/apps/background-tasks/containers/StatsContainer.js b/server/sonar-web/src/main/js/apps/background-tasks/containers/StatsContainer.js new file mode 100644 index 00000000000..f1828926628 --- /dev/null +++ b/server/sonar-web/src/main/js/apps/background-tasks/containers/StatsContainer.js @@ -0,0 +1,48 @@ +/* + * SonarQube + * Copyright (C) 2009-2016 SonarSource SA + * mailto:contact 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 { connect } from 'react-redux'; + +import Stats from '../components/Stats'; +import { filterTasks, cancelAllPending } from '../store/actions'; +import { STATUSES, CURRENTS, DEFAULT_FILTERS } from '../constants'; + +function mapStateToProps (state) { + return { + pendingCount: state.pendingCount, + failingCount: state.failingCount, + inProgressDuration: state.inProgressDuration + }; +} + +function mapDispatchToProps (dispatch) { + return { + onShowFailing: () => dispatch(filterTasks({ + ...DEFAULT_FILTERS, + status: STATUSES.FAILED, + currents: CURRENTS.ONLY_CURRENTS + })), + onCancelAllPending: () => dispatch(cancelAllPending()) + }; +} + +export default connect( + mapStateToProps, + mapDispatchToProps +)(Stats); diff --git a/server/sonar-web/src/main/js/apps/background-tasks/containers/TasksContainer.js b/server/sonar-web/src/main/js/apps/background-tasks/containers/TasksContainer.js new file mode 100644 index 00000000000..0fc2989b98c --- /dev/null +++ b/server/sonar-web/src/main/js/apps/background-tasks/containers/TasksContainer.js @@ -0,0 +1,42 @@ +/* + * SonarQube + * Copyright (C) 2009-2016 SonarSource SA + * mailto:contact 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 { connect } from 'react-redux'; + +import Tasks from '../components/Tasks'; +import { cancelTask, filterTasks } from '../store/actions'; + +function mapStateToProps (state) { + return { + fetching: state.fetching, + tasks: state.tasks + }; +} + +function mapDispatchToProps (dispatch) { + return { + onCancelTask: (task) => dispatch(cancelTask(task)), + onFilterTask: (task) => dispatch(filterTasks({ query: task.componentKey })) + }; +} + +export default connect( + mapStateToProps, + mapDispatchToProps +)(Tasks); diff --git a/server/sonar-web/src/main/js/apps/background-tasks/header.js b/server/sonar-web/src/main/js/apps/background-tasks/header.js deleted file mode 100644 index bc84cae82d6..00000000000 --- a/server/sonar-web/src/main/js/apps/background-tasks/header.js +++ /dev/null @@ -1,32 +0,0 @@ -/* - * SonarQube - * Copyright (C) 2009-2016 SonarSource SA - * mailto:contact 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 React from 'react'; -import { translate } from '../../helpers/l10n'; - -export default React.createClass({ - render() { - return ( -
-

{translate('background_tasks.page')}

-

{translate('background_tasks.page.description')}

-
- ); - } -}); diff --git a/server/sonar-web/src/main/js/apps/background-tasks/main.js b/server/sonar-web/src/main/js/apps/background-tasks/main.js deleted file mode 100644 index bc52ddf9236..00000000000 --- a/server/sonar-web/src/main/js/apps/background-tasks/main.js +++ /dev/null @@ -1,241 +0,0 @@ -/* - * SonarQube - * Copyright (C) 2009-2016 SonarSource SA - * mailto:contact 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 _ from 'underscore'; -import moment from 'moment'; -import React from 'react'; - -import { getQueue, getActivity, cancelTask, cancelAllTasks } from '../../api/ce'; -import { STATUSES, CURRENTS, DATE, DEBOUNCE_DELAY } from './constants'; -import Header from './header'; -import Stats from './stats'; -import Search from './search'; -import Tasks from './tasks'; -import ListFooter from '../../components/shared/list-footer'; - - -const PAGE_SIZE = 200; - - -export default React.createClass({ - getInitialState() { - return { - queue: [], - activity: [], - activityTotal: 0, - activityPage: 1, - statusFilter: STATUSES.ALL, - currentsFilter: CURRENTS.ALL, - dateFilter: DATE.ANY, - searchQuery: '' - }; - }, - - componentDidMount() { - this.requestData(); - this.requestData = _.debounce(this.requestData, DEBOUNCE_DELAY); - }, - - getComponentFilter() { - if (this.props.options.component) { - return { componentId: this.props.options.component.id }; - } else { - return {}; - } - }, - - getDateFilter() { - const DATE_FORMAT = 'YYYY-MM-DD'; - let filter = {}; - switch (this.state.dateFilter) { - case DATE.TODAY: - filter.minSubmittedAt = moment().startOf('day').format(DATE_FORMAT); - break; - case DATE.CUSTOM: - if (this.state.minDate) { - filter.minSubmittedAt = moment(this.state.minDate).format(DATE_FORMAT); - } - if (this.state.maxDate) { - filter.maxExecutedAt = moment(this.state.maxDate).format(DATE_FORMAT); - } - break; - default: - // do nothing - } - return filter; - }, - - getCurrentFilters() { - let filters = {}; - if (this.state.statusFilter !== STATUSES.ALL) { - filters.status = this.state.statusFilter; - } - if (this.state.currentsFilter !== STATUSES.ALL) { - filters.onlyCurrents = true; - } - if (this.state.dateFilter !== DATE.ANY) { - _.extend(filters, this.getDateFilter()); - } - if (this.state.searchQuery) { - _.extend(filters, { componentQuery: this.state.searchQuery }); - } - return filters; - }, - - requestData() { - this.requestQueue(); - this.requestActivity(); - this.requestFailures(); - }, - - requestQueue() { - let filters = this.getComponentFilter(); - if (!Object.keys(this.getCurrentFilters()).length) { - getQueue(filters).done(queue => { - let tasks = queue.tasks; - this.setState({ - queue: this.orderTasks(tasks), - pendingCount: this.countPending(tasks), - inProgressDuration: this.getInProgressDuration(tasks) - }); - }); - } else { - this.setState({ queue: [] }); - } - }, - - requestActivity() { - let filters = _.extend( - this.getCurrentFilters(), - this.getComponentFilter(), - { p: this.state.activityPage, ps: PAGE_SIZE }); - getActivity(filters).done(activity => { - let newActivity = activity.paging.pageIndex === 1 ? - activity.tasks : [].concat(this.state.activity, activity.tasks); - this.setState({ - activity: this.orderTasks(newActivity), - activityTotal: activity.paging.total, - activityPage: activity.paging.pageIndex - }); - }); - }, - - requestFailures() { - let filters = _.extend( - this.getComponentFilter(), - { ps: 1, onlyCurrents: true, status: STATUSES.FAILED }); - getActivity(filters).done(failures => { - this.setState({ failuresCount: failures.paging.total }); - }); - }, - - countPending(tasks) { - return _.where(tasks, { status: STATUSES.PENDING }).length; - }, - - orderTasks(tasks) { - return _.sortBy(tasks, task => { - return -moment(task.submittedAt).unix(); - }); - }, - - getInProgressDuration(tasks) { - let taskInProgress = _.findWhere(tasks, { status: STATUSES.IN_PROGRESS }); - return taskInProgress ? taskInProgress.executionTimeMs : null; - }, - - onStatusChange(newStatus) { - this.setState({ statusFilter: newStatus, activityPage: 1 }, this.requestData); - }, - - onCurrentsChange(newCurrents) { - this.setState({ currentsFilter: newCurrents, activityPage: 1 }, this.requestData); - }, - - onDateChange(newDate, minDate, maxDate) { - this.setState({ - dateFilter: newDate, - minDate: minDate, - maxDate: maxDate, - activityPage: 1 - }, this.requestData); - }, - - onSearch(query) { - this.setState({ searchQuery: query }, this.requestData); - }, - - loadMore() { - this.setState({ activityPage: this.state.activityPage + 1 }, this.requestActivity); - }, - - showFailures() { - this.setState({ - statusFilter: STATUSES.FAILED, - currentsFilter: CURRENTS.ONLY_CURRENTS, - activityPage: 1 - }, this.requestActivity); - }, - - onTaskCanceled(task) { - cancelTask(task.id).then(this.requestData); - }, - - cancelPending() { - cancelAllTasks().then(this.requestData); - }, - - handleFilter(task) { - this.onSearch(task.componentKey); - }, - - render() { - return ( -
-
- - - - - - - - -
- ); - } -}); diff --git a/server/sonar-web/src/main/js/apps/background-tasks/search.js b/server/sonar-web/src/main/js/apps/background-tasks/search.js deleted file mode 100644 index cc06fc2417e..00000000000 --- a/server/sonar-web/src/main/js/apps/background-tasks/search.js +++ /dev/null @@ -1,168 +0,0 @@ -/* - * SonarQube - * Copyright (C) 2009-2016 SonarSource SA - * mailto:contact 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 $ from 'jquery'; -import moment from 'moment'; -import React from 'react'; -import RadioToggle from '../../components/shared/radio-toggle'; -import { STATUSES, CURRENTS, DATE, DATE_FORMAT } from './constants'; -import { translate } from '../../helpers/l10n'; - -export default React.createClass({ - componentDidMount() { - this.attachDatePicker(); - }, - - componentDidUpdate() { - this.attachDatePicker(); - }, - - getCurrentsOptions() { - return [ - { value: CURRENTS.ALL, label: translate('background_tasks.currents_filter.ALL') }, - { value: CURRENTS.ONLY_CURRENTS, label: translate('background_tasks.currents_filter.ONLY_CURRENTS') } - ]; - }, - - getStatusOptions() { - return [ - { value: STATUSES.ALL, label: translate('background_task.status.ALL') }, - { value: STATUSES.SUCCESS, label: translate('background_task.status.SUCCESS') }, - { value: STATUSES.FAILED, label: translate('background_task.status.FAILED') }, - { value: STATUSES.CANCELED, label: translate('background_task.status.CANCELED') } - ]; - }, - - getDateOptions() { - return [ - { value: DATE.ANY, label: translate('background_tasks.date_filter.ALL') }, - { value: DATE.TODAY, label: translate('background_tasks.date_filter.TODAY') }, - { value: DATE.CUSTOM, label: translate('background_tasks.date_filter.CUSTOM') } - ]; - }, - - onDateChange(newDate) { - if (newDate === DATE.CUSTOM) { - let minDateRaw = this.refs.minDate.value; - let maxDateRaw = this.refs.maxDate.value; - let minDate = moment(minDateRaw, DATE_FORMAT, true); - let maxDate = moment(maxDateRaw, DATE_FORMAT, true); - this.props.onDateChange(newDate, - minDate.isValid() ? minDate : null, - maxDate.isValid() ? maxDate : null); - } else { - this.props.onDateChange(newDate); - } - }, - - onDateInputChange() { - this.onDateChange(DATE.CUSTOM); - }, - - attachDatePicker() { - let opts = { - dateFormat: 'yy-mm-dd', - changeMonth: true, - changeYear: true, - onSelect: this.onDateInputChange - }; - if ($.fn && $.fn.datepicker) { - $(this.refs.minDate).datepicker(opts); - $(this.refs.maxDate).datepicker(opts); - } - }, - - renderCustomDateInput() { - let shouldBeVisible = this.props.dateFilter === DATE.CUSTOM; - let className = shouldBeVisible ? 'spacer-top' : 'spacer-top hidden'; - return ( -
- from  - -  to  - -
- ); - }, - - onSearchFormSubmit(e) { - e.preventDefault(); - this.onSearch(); - }, - - onSearch() { - let searchInput = this.refs.searchInput; - let query = searchInput.value; - this.props.onSearch(query); - }, - - renderSearchBox() { - if (this.props.options && this.props.options.component) { - // do not render search form on the project-level page - return null; - } - return ( -
- - -
- ); - }, - - refresh(e) { - e.preventDefault(); - this.props.refresh(); - let btn = e.target; - btn.disabled = true; - setTimeout(() => btn.disabled = false, 500); - }, - - render() { - return ( -
-
    -
  • - -
  • -
  • - -
  • -
  • - - {this.renderCustomDateInput()} -
  • -
  • {this.renderSearchBox()}
  • -
  • - -
  • -
-
- ); - } -}); diff --git a/server/sonar-web/src/main/js/apps/background-tasks/stats.js b/server/sonar-web/src/main/js/apps/background-tasks/stats.js deleted file mode 100644 index 95ac963376e..00000000000 --- a/server/sonar-web/src/main/js/apps/background-tasks/stats.js +++ /dev/null @@ -1,130 +0,0 @@ -/* - * SonarQube - * Copyright (C) 2009-2016 SonarSource SA - * mailto:contact 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 React from 'react'; - -import { formatDuration } from './helpers'; -import { TooltipsMixin } from '../../components/mixins/tooltips-mixin'; -import { translate } from '../../helpers/l10n'; - - -export default React.createClass({ - mixins: [TooltipsMixin], - - onPendingCanceled(e) { - e.preventDefault(); - this.props.cancelPending(); - }, - - onFailuresClick(e) { - e.preventDefault(); - this.props.showFailures(); - }, - - renderInProgressDuration() { - if (!this.props.inProgressDuration) { - return null; - } - return ( - - - - {formatDuration(this.props.inProgressDuration)} - - - ); - }, - - renderPending() { - if (this.props.pendingCount == null) { - return null; - } - if (this.props.pendingCount > 0) { - return ( - - {this.props.pendingCount} -   - {translate('background_tasks.pending')} - - - ); - } else { - return ( - - {this.props.pendingCount} -   - {translate('background_tasks.pending')} - - ); - } - }, - - renderFailures() { - if (this.props.failuresCount == null) { - return null; - } - - if (this.props.options && this.props.options.component) { - return null; - } - - if (this.props.failuresCount > 0) { - return ( - - {this.props.failuresCount} -   - {translate('background_tasks.failures')} - - ); - } else { - return ( - - - {this.props.failuresCount} - -   - {translate('background_tasks.failures')} - - ); - } - }, - - render() { - return ( -
- - {this.renderPending()} - - - {this.renderFailures()} - - {this.renderInProgressDuration()} -
- - ); - } -}); diff --git a/server/sonar-web/src/main/js/apps/background-tasks/store/actions.js b/server/sonar-web/src/main/js/apps/background-tasks/store/actions.js new file mode 100644 index 00000000000..511af5cdb29 --- /dev/null +++ b/server/sonar-web/src/main/js/apps/background-tasks/store/actions.js @@ -0,0 +1,210 @@ +/* + * SonarQube + * Copyright (C) 2009-2016 SonarSource SA + * mailto:contact 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 _ from 'underscore'; +import { getTypes, getActivity, cancelAllTasks, cancelTask as cancelTaskAPI } from '../../../api/ce'; + +import { STATUSES, ALL_TYPES, CURRENTS, DEBOUNCE_DELAY } from '../constants'; + +const PAGE_SIZE = 1000; + +export const INIT = 'INIT'; +export const REQUEST_TASKS = 'REQUEST_TASKS'; +export const RECEIVE_TASKS = 'RECEIVE_TASKS'; +export const UPDATE_QUERY = 'UPDATE_QUERY'; +export const RECEIVE_STATS = 'RECEIVE_STATS'; +export const CANCEL_ALL_PENDING = 'CANCEL_ALL_PENDING'; +export const CANCEL_TASK = 'CANCEL_TASK'; +export const FINISH_CANCEL_TASK = 'FINISH_CANCEL_TASK'; + +export function init (types) { + return { + type: INIT, + types + }; +} + +export function requestTasks (filters) { + return { + type: REQUEST_TASKS, + ...filters + }; +} + +export function receiveTasks (tasks, total) { + return { + type: RECEIVE_TASKS, + tasks, + total + }; +} + +export function updateQuery (query) { + return { + type: UPDATE_QUERY, + query + }; +} + +export function receiveStats ({ pendingCount, failingCount, inProgressDuration }) { + return { + type: RECEIVE_STATS, + pendingCount, + failingCount, + inProgressDuration + }; +} + +export function cancelAllPendingAction () { + return { + type: CANCEL_ALL_PENDING + }; +} + +export function cancelTaskAction (task) { + return { + type: CANCEL_TASK, + task + }; +} + +export function finishCancelTaskAction (task) { + return { + type: FINISH_CANCEL_TASK, + task + }; +} + +function mapFiltersToParameters (filters = {}) { + const parameters = {}; + + if (filters.status !== STATUSES.ALL) { + parameters.status = filters.status; + } else { + parameters.status = [ + STATUSES.PENDING, + STATUSES.IN_PROGRESS, + STATUSES.SUCCESS, + STATUSES.FAILED, + STATUSES.CANCELED + ].join(); + } + + if (filters.taskType !== ALL_TYPES) { + parameters.type = filters.taskType; + } + + if (filters.currents !== CURRENTS.ALL) { + parameters.onlyCurrents = true; + } + + if (filters.date.minSubmittedAt) { + parameters.minSubmittedAt = filters.date.minSubmittedAt; + } + + if (filters.date.maxExecutedAt) { + parameters.maxExecutedAt = filters.date.maxExecutedAt; + } + + if (filters.query) { + parameters.componentQuery = filters.query; + } + + if (filters.lastPage !== 1) { + parameters.p = filters.lastPage; + } + + return parameters; +} + +function getInProgressDuration (tasks) { + return tasks.length ? tasks[0].executionTimeMs : null; +} + +function fetchTasks (filters) { + return dispatch => { + const parameters = mapFiltersToParameters(filters); + parameters.ps = PAGE_SIZE; + + dispatch(requestTasks(filters)); + + return Promise.all([ + getActivity(parameters), + getActivity({ ps: 1, onlyCurrents: true, status: STATUSES.FAILED }), + getActivity({ ps: 1, status: STATUSES.PENDING }), + getActivity({ ps: 1, status: STATUSES.IN_PROGRESS }) + ]).then(responses => { + const [activity, failingActivity, pendingActivity, inProgressActivity] = responses; + const tasks = activity.tasks; + const total = activity.paging.total; + + dispatch(receiveTasks(tasks, total)); + + const pendingCount = pendingActivity.paging.total; + const inProgressDuration = getInProgressDuration(inProgressActivity.tasks); + const failingCount = failingActivity.paging.total; + + dispatch(receiveStats({ pendingCount, failingCount, inProgressDuration })); + }); + }; +} + +export function filterTasks (overriddenFilters) { + return (dispatch, getState) => { + const { status, taskType, currents, date, query } = getState(); + const filters = { status, taskType, currents, date, query }; + const finalFilters = { ...filters, ...overriddenFilters }; + + dispatch(fetchTasks(finalFilters)); + }; +} + +const debouncedFilter = _.debounce((dispatch, overriddenFilters) => { + dispatch(filterTasks(overriddenFilters)); +}, DEBOUNCE_DELAY); + +export function search (query) { + return dispatch => { + dispatch(updateQuery(query)); + debouncedFilter(dispatch, { query }); + }; +} + +export function cancelAllPending () { + return dispatch => { + dispatch(cancelAllPendingAction()); + cancelAllTasks().then(() => dispatch(filterTasks())); + }; +} + +export function cancelTask (task) { + return dispatch => { + dispatch(cancelTaskAction(task)); + cancelTaskAPI(task.id).then(nextTask => dispatch(finishCancelTaskAction(nextTask))); + }; +} + +export function initApp () { + return dispatch => { + getTypes().then(types => { + dispatch(init(types)); + dispatch(filterTasks()); + }); + }; +} diff --git a/server/sonar-web/src/main/js/apps/background-tasks/store/reducers.js b/server/sonar-web/src/main/js/apps/background-tasks/store/reducers.js new file mode 100644 index 00000000000..5c00567011d --- /dev/null +++ b/server/sonar-web/src/main/js/apps/background-tasks/store/reducers.js @@ -0,0 +1,100 @@ +/* + * SonarQube + * Copyright (C) 2009-2016 SonarSource SA + * mailto:contact 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 { + INIT, + REQUEST_TASKS, + RECEIVE_TASKS, + UPDATE_QUERY, + RECEIVE_STATS, + CANCEL_ALL_PENDING, + FINISH_CANCEL_TASK +} from './actions'; +import { DEFAULT_FILTERS } from '../constants'; + +export const initialState = { + fetching: false, + tasks: [], + total: 0, + + types: [], + + // filters + ...DEFAULT_FILTERS, + + // stats + pendingCount: 0, + failingCount: 0, + inProgressDuration: null +}; + +function updateTask (tasks, newTask) { + return tasks.map(task => task.id === newTask.id ? newTask : task); +} + +export default function (state = initialState, action) { + switch (action.type) { + case INIT: + return { + ...state, + types: action.types + }; + case REQUEST_TASKS: + return { + ...state, + fetching: true, + status: action.status, + currents: action.currents, + date: action.date, + query: action.query, + taskType: action.taskType + }; + case RECEIVE_TASKS: + return { + ...state, + fetching: false, + tasks: action.tasks, + total: action.total + }; + case UPDATE_QUERY: + return { + ...state, + query: action.query + }; + case RECEIVE_STATS: + return { + ...state, + pendingCount: action.pendingCount, + failingCount: action.failingCount, + inProgressDuration: action.inProgressDuration + }; + case CANCEL_ALL_PENDING: + return { + ...state, + fetching: true + }; + case FINISH_CANCEL_TASK: + return { + ...state, + tasks: updateTask(state.tasks, action.task) + }; + default: + return state; + } +} diff --git a/server/sonar-web/src/main/js/apps/background-tasks/styles/background-tasks.css b/server/sonar-web/src/main/js/apps/background-tasks/styles/background-tasks.css new file mode 100644 index 00000000000..2bad2aba5a0 --- /dev/null +++ b/server/sonar-web/src/main/js/apps/background-tasks/styles/background-tasks.css @@ -0,0 +1,20 @@ +.bt-search-form { + display: flex; + align-items: flex-start; +} + +.bt-search-form > li + li { + margin-left: 40px; +} + +.bt-search-form-label { + margin-bottom: 4px; +} + +.bt-search-form-field { + padding: 4px 0; +} + +.bt-search-form-right { + margin-left: auto !important; +} diff --git a/server/sonar-web/src/main/js/apps/background-tasks/tasks.js b/server/sonar-web/src/main/js/apps/background-tasks/tasks.js deleted file mode 100644 index ee5920f944a..00000000000 --- a/server/sonar-web/src/main/js/apps/background-tasks/tasks.js +++ /dev/null @@ -1,189 +0,0 @@ -/* - * SonarQube - * Copyright (C) 2009-2016 SonarSource SA - * mailto:contact 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 moment from 'moment'; -import React from 'react'; - -import { getComponentUrl } from '../../helpers/urls'; -import QualifierIcon from '../../components/shared/qualifier-icon'; -import PendingIcon from '../../components/shared/pending-icon'; -import { STATUSES } from './constants'; -import { formatDuration } from './helpers'; -import { TooltipsMixin } from '../../components/mixins/tooltips-mixin'; -import { translate } from '../../helpers/l10n'; - - -export default React.createClass({ - propTypes: { - tasks: React.PropTypes.arrayOf(React.PropTypes.object).isRequired - }, - - mixins: [TooltipsMixin], - - onTaskCanceled (task, e) { - e.preventDefault(); - this.props.onTaskCanceled(task); - }, - - handleFilter (task, e) { - e.preventDefault(); - this.props.onFilter(task); - }, - - renderTaskStatus(task) { - let inner; - switch (task.status) { - case STATUSES.PENDING: - inner = ; - break; - case STATUSES.IN_PROGRESS: - inner = ; - break; - case STATUSES.SUCCESS: - inner = {translate('background_task.status.SUCCESS')}; - break; - case STATUSES.FAILED: - inner = {translate('background_task.status.FAILED')}; - break; - case STATUSES.CANCELED: - inner = {translate('background_task.status.CANCELED')}; - break; - default: - inner = ''; - } - return {inner}; - }, - - renderTaskComponent(task) { - if (!task.componentKey) { - return {task.id}; - } - - return ( - - - - - - {task.componentName} - - - ); - }, - - renderTaskDate(task, field, format = 'LLL') { - let date = task[field]; - return ( - - {date ? moment(date).format(format) : ''} - - ); - }, - - renderTaskDay(task, previousTask) { - let shouldDisplay = !previousTask || this.isAnotherDay(task.submittedAt, previousTask.submittedAt); - return ( - - {shouldDisplay ? moment(task.submittedAt).format('LL') : ''} - - ); - }, - - renderTaskExecutionTime(task) { - return {formatDuration(task.executionTimeMs)}; - }, - - isAnotherDay(a, b) { - return !moment(a).isSame(moment(b), 'day'); - }, - - renderFilter(task) { - if (this.props.options && this.props.options.component) { - return null; - } - return - - ; - }, - - renderCancelButton(task) { - if (task.status === STATUSES.PENDING) { - return ( - - ); - } else { - return null; - } - }, - - renderLogsLink(task) { - if (task.logs) { - let url = `${window.baseUrl}/api/ce/logs?taskId=${task.id}`; - return {translate('background_tasks.logs')}; - } else { - return null; - } - }, - - renderTask(task, index, tasks) { - let previousTask = index > 0 ? tasks[index - 1] : null; - return ( - - {this.renderTaskStatus(task)} - {this.renderTaskComponent(task)} - {this.renderTaskDay(task, previousTask)} - {this.renderTaskDate(task, 'submittedAt', 'LTS')} - {this.renderTaskDate(task, 'startedAt', 'LTS')} - {this.renderTaskDate(task, 'executedAt', 'LTS')} - {this.renderTaskExecutionTime(task)} - - {this.renderLogsLink(task)} - {this.renderCancelButton(task)} - - {this.renderFilter(task)} - - ); - }, - - render() { - if (!this.props.tasks.length) { - return null; - } - let tasks = this.props.tasks.map(this.renderTask); - return ( - - - - - - - - - - - - - - {tasks} -
   {translate('background_tasks.table.submitted')}{translate('background_tasks.table.started')}{translate('background_tasks.table.finished')}{translate('background_tasks.table.duration')} 
- ); - } -}); diff --git a/server/sonar-web/src/main/js/components/store/configureStore.js b/server/sonar-web/src/main/js/components/store/configureStore.js new file mode 100644 index 00000000000..fee2f603bf2 --- /dev/null +++ b/server/sonar-web/src/main/js/components/store/configureStore.js @@ -0,0 +1,34 @@ +/* + * SonarQube + * Copyright (C) 2009-2016 SonarSource SA + * mailto:contact 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 { createStore, applyMiddleware } from 'redux'; +import thunk from 'redux-thunk'; + +const middlewares = [thunk]; + +if (process.env.NODE_ENV !== 'production') { + const createLogger = require('redux-logger'); + middlewares.push(createLogger()); +} + +const createStoreWithMiddleware = applyMiddleware(...middlewares)(createStore); + +export default function configureStore (rootReducer) { + return createStoreWithMiddleware(rootReducer); +} diff --git a/server/sonar-web/src/main/less/init/forms.less b/server/sonar-web/src/main/less/init/forms.less index 9a832f9679e..d0cd92873eb 100644 --- a/server/sonar-web/src/main/less/init/forms.less +++ b/server/sonar-web/src/main/less/init/forms.less @@ -58,7 +58,11 @@ input[type=date] { padding: 0 6px; } -input[type=search] { +input[type="search"] { + -webkit-appearance: textfield; +} + +input[type="search"]::-webkit-search-decoration { -webkit-appearance: none; } diff --git a/server/sonar-web/src/main/less/pages/analysis-reports.less b/server/sonar-web/src/main/less/pages/analysis-reports.less deleted file mode 100644 index 4e3bef56f27..00000000000 --- a/server/sonar-web/src/main/less/pages/analysis-reports.less +++ /dev/null @@ -1,117 +0,0 @@ -/* - * SonarQube - * Copyright (C) 2009-2016 SonarSource SA - * mailto:contact 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 (reference) "../mixins"; -@import (reference) "../variables"; -@import (reference) "../components/ui"; -@import (reference) "../components/navigator/base"; -@import (reference) "../init/type"; - - -@pendingColor: #fdfce2; -@workingColor: #ecf9fc; -@doneColor: #ecfced; -@cancelledColor: #fcecec; -@failedColor: #fcecec; - - -.analysis-reports-actions { - margin-bottom: 10px; -} - -.analysis-reports-total { - float: right; - margin-top: -20px; -} - -.analysis-reports-results .navigator-results-list > li { - cursor: default; -} - -.analysis-reports-no-results { - border: none !important; -} - -.analysis-reports-report-pending { - background-color: @pendingColor !important; -} - -.analysis-reports-report-working { - background-color: @workingColor !important; -} - -.analysis-reports-report-done { - background-color: @doneColor !important; -} - -.analysis-reports-report-cancelled { - background-color: @cancelledColor !important; -} - -.analysis-reports-report-failed { - background-color: @failedColor !important; - - .analysis-reports-report-id { color: darken(@failedColor, 60%); } -} - -.analysis-reports-project { - display: inline-block; - vertical-align: middle; - width: 30%; - .text-ellipsis; -} - -.analysis-reports-timestamp { - display: inline-block; - vertical-align: middle; - width: 220px; - margin-left: 15px; -} - -.analysis-reports-report-id { - position: absolute; - top: 6px; - right: 6px; - opacity: 0.3; - font-size: @bigFontSize; - - &:before { content: '#'; } -} - -.analysis-reports-spinner { - width: 200px; - height: 200px; - margin-top: 20px; -} - -.analysis-reports-spinner, -.analysis-reports-spinner:before -.analysis-reports-spinner:after { - animation-duration: 6s; -} - -.analysis-reports-timestamp-spinner { - margin-left: 10px; -} - -.analysis-reports-timestamp-spinner, -.analysis-reports-timestamp-spinner:before, -.analysis-reports-timestamp-spinner:after { - animation-duration: 2s; -} diff --git a/server/sonar-web/src/main/less/sonar.less b/server/sonar-web/src/main/less/sonar.less index 5188f62907b..b1b5e033e29 100644 --- a/server/sonar-web/src/main/less/sonar.less +++ b/server/sonar-web/src/main/less/sonar.less @@ -58,7 +58,6 @@ @import "components/pills"; @import "components/react-select"; -@import "pages/analysis-reports"; @import "pages/coding-rules"; @import "pages/dashboard"; @import "pages/issues"; diff --git a/server/sonar-web/tests/apps/background-tasks-test.js b/server/sonar-web/tests/apps/background-tasks-test.js index 9bc486c7d63..0d70aa18311 100644 --- a/server/sonar-web/tests/apps/background-tasks-test.js +++ b/server/sonar-web/tests/apps/background-tasks-test.js @@ -3,10 +3,10 @@ import React from 'react'; import ReactDOM from 'react-dom'; import TestUtils from 'react-addons-test-utils'; -import Header from '../../src/main/js/apps/background-tasks/header'; -import Stats from '../../src/main/js/apps/background-tasks/stats'; -import Search from '../../src/main/js/apps/background-tasks/search'; -import Tasks from '../../src/main/js/apps/background-tasks/tasks'; +import Header from '../../src/main/js/apps/background-tasks/components/Header'; +import Stats from '../../src/main/js/apps/background-tasks/components/Stats'; +import Search from '../../src/main/js/apps/background-tasks/components/Search'; +import Tasks from '../../src/main/js/apps/background-tasks/components/Tasks'; import {STATUSES, CURRENTS, DEBOUNCE_DELAY} from '../../src/main/js/apps/background-tasks/constants'; import {formatDuration} from '../../src/main/js/apps/background-tasks/helpers'; diff --git a/sonar-core/src/main/resources/org/sonar/l10n/core.properties b/sonar-core/src/main/resources/org/sonar/l10n/core.properties index ce0758fb4b8..c6811c5c523 100644 --- a/sonar-core/src/main/resources/org/sonar/l10n/core.properties +++ b/sonar-core/src/main/resources/org/sonar/l10n/core.properties @@ -3082,6 +3082,12 @@ background_task.status.SUCCESS=Success background_task.status.FAILED=Failed background_task.status.CANCELED=Canceled +background_task.type.ALL=All +background_task.type.REPORT=Project Analysis +background_task.type.DEV_REFRESH=Developer Analysis +background_task.type.DEV_PURGE=Developer Cleaning +background_task.type.VIEW_REFRESH=View Analysis + background_tasks.page=Background Tasks background_tasks.page.description=This page allows monitoring of the queue of tasks running asynchronously on the server. It also gives access to the history of finished tasks, their status and logs. Analysis report processing is the most common kind of background task.