* 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';
}
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);
+}
*/
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(<Main options={options}/>, el);
+ const el = document.querySelector(options.el);
+
+ const store = configureStore(rootReducer);
+
+ ReactDOM.render((
+ <Provider store={store}>
+ <BackgroundTasksAppContainer options={options}/>
+ </Provider>
+ ), el);
});
--- /dev/null
+/*
+ * 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 (
+ <div className="page">
+ <Header/>
+ <StatsContainer/>
+ <SearchContainer/>
+ <TasksContainer/>
+ <ListFooterContainer/>
+ </div>
+ );
+ }
+}
--- /dev/null
+/*
+ * 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 (
+ <div className="bt-search-form-field">
+ <Checkbox
+ initiallyChecked={checked}
+ onCheck={handleChange}/>
+
+ <label>Yes</label>
+ </div>
+ );
+}
--- /dev/null
+/*
+ * 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 (
+ <div>
+ <input
+ className="input-small"
+ value={minSubmittedAt}
+ onChange={this.handleChange.bind(this)}
+ ref="minDate"
+ type="text"
+ placeholder="From"/>
+ {' '}
+ <input
+ className="input-small"
+ value={maxExecutedAt}
+ onChange={this.handleChange.bind(this)}
+ ref="maxDate"
+ type="text"
+ placeholder="To"/>
+ </div>
+ );
+ }
+}
--- /dev/null
+/*
+ * 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 (
+ <Select
+ value={value}
+ onChange={option => onChange(option.value)}
+ className="input-small"
+ options={options}
+ clearable={false}
+ searchable={false}/>
+ );
+}
--- /dev/null
+/*
+ * 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 TaskStatus from './TaskStatus';
+import TaskComponent from './TaskComponent';
+import TaskDay from './TaskDay';
+import TaskDate from './TaskDate';
+import TaskExecutionTime from './TaskExecutionTime';
+import TaskCancelButton from './TaskCancelButton';
+import TaskLogsLink from './TaskLogsLink';
+
+import { STATUSES } from './../constants';
+
+
+function renderFilter (task) {
+ // if (this.props.options && this.props.options.component) {
+ // return null;
+ // }
+ return <td className="thin nowrap">
+ <a onClick={this.handleFilter.bind(this, task)} className="icon-filter icon-half-transparent spacer-left" href="#"
+ title={`Show only "${task.componentName}" tasks`} data-toggle="tooltip"/>
+ </td>;
+}
+
+export default function Task ({ task, index, tasks, onCancelTask, onFilterTask }) {
+ function handleFilterTask (task, e) {
+ e.preventDefault();
+ onFilterTask(task);
+ }
+
+ const prevTask = index > 0 ? tasks[index - 1] : null;
+
+ return (
+ <tr>
+ <TaskStatus task={task}/>
+ <TaskComponent task={task}/>
+ <TaskDay task={task} prevTask={prevTask}/>
+ <TaskDate date={task.submittedAt} format="LTS"/>
+ <TaskDate date={task.startedAt} format="LTS"/>
+ <TaskDate date={task.executedAt} format="LTS"/>
+ <TaskExecutionTime task={task}/>
+
+ <td className="thin nowrap text-right">
+ {task.logs && (
+ <TaskLogsLink task={task}/>
+ )}
+ {task.status === STATUSES.PENDING && (
+ <TaskCancelButton task={task} onCancelTask={onCancelTask}/>
+ )}
+ </td>
+
+ <td className="thin nowrap">
+ <a
+ onClick={handleFilterTask.bind(this, task)}
+ className="icon-filter icon-half-transparent spacer-left"
+ href="#"
+ title={`Show only "${task.componentName}" tasks`}
+ data-toggle="tooltip"/>
+ </td>
+ </tr>
+ );
+}
--- /dev/null
+/*
+ * 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 TaskCancelButton ({ task, onCancelTask }) {
+ function handleClick (e) {
+ e.preventDefault();
+ onCancelTask(task);
+ }
+
+ return (
+ <a
+ onClick={handleClick}
+ className="icon-delete"
+ title={translate('background_tasks.cancel_task')}
+ data-toggle="tooltip"
+ href="#"/>
+ );
+}
--- /dev/null
+/*
+ * 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 { getComponentUrl } from '../../../helpers/urls';
+import QualifierIcon from '../../../components/shared/qualifier-icon';
+
+export default function TaskComponent ({ task }) {
+ if (!task.componentKey) {
+ return (
+ <td>
+ <span className="note">{task.id}</span>
+ </td>
+ );
+ }
+
+ return (
+ <td>
+ <a className="link-with-icon" href={getComponentUrl(task.componentKey)}>
+ <span className="little-spacer-right">
+ <QualifierIcon qualifier={task.componentQualifier}/>
+ </span>
+ <span>{task.componentName}</span>
+ </a>
+ </td>
+ );
+}
--- /dev/null
+/*
+ * 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';
+
+export default function TaskDate ({ date, format }) {
+ return (
+ <td className="thin nowrap text-right">
+ {date ? moment(date).format(format) : ''}
+ </td>
+ );
+}
--- /dev/null
+/*
+ * 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';
+
+function isAnotherDay (a, b) {
+ return !moment(a).isSame(moment(b), 'day');
+}
+
+export default function TaskDay ({ task, prevTask }) {
+ const shouldDisplay = !prevTask || isAnotherDay(task.submittedAt, prevTask.submittedAt);
+
+ return (
+ <td className="thin nowrap text-right">
+ {shouldDisplay ? moment(task.submittedAt).format('LL') : ''}
+ </td>
+ );
+}
--- /dev/null
+/*
+ * 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';
+
+export default function TaskExecutionTime ({ task }) {
+ return (
+ <td className="thin nowrap text-right">
+ {formatDuration(task.executionTimeMs)}
+ </td>
+ );
+}
--- /dev/null
+/*
+ * 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 TaskLogsLink ({ task }) {
+ const url = `${window.baseUrl}/api/ce/logs?taskId=${task.id}`;
+
+ return (
+ <a
+ target="_blank"
+ href={url}>
+ {translate('background_tasks.logs')}
+ </a>
+ );
+}
--- /dev/null
+/*
+ * 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 { STATUSES } from './../constants';
+import PendingIcon from '../../../components/shared/pending-icon';
+import { translate } from '../../../helpers/l10n';
+
+export default function TaskStatus ({ task }) {
+ let inner;
+
+ switch (task.status) {
+ case STATUSES.PENDING:
+ inner = <PendingIcon/>;
+ break;
+ case STATUSES.IN_PROGRESS:
+ inner = <i className="spinner"/>;
+ break;
+ case STATUSES.SUCCESS:
+ inner = <span className="badge badge-success">{translate('background_task.status.SUCCESS')}</span>;
+ break;
+ case STATUSES.FAILED:
+ inner = <span className="badge badge-danger">{translate('background_task.status.FAILED')}</span>;
+ break;
+ case STATUSES.CANCELED:
+ inner = <span className="badge badge-muted">{translate('background_task.status.CANCELED')}</span>;
+ break;
+ default:
+ inner = '';
+ }
+
+ return <td className="thin spacer-right">{inner}</td>;
+}
--- /dev/null
+/*
+ * 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 { ALL_TYPES } from '../constants';
+import { translate } from '../../../helpers/l10n';
+
+export default function TypesFilter ({ value, onChange, types }) {
+ const options = types.map(t => {
+ return {
+ value: t,
+ label: translate('background_task.type', t)
+ };
+ });
+
+ const allOptions = [
+ { value: ALL_TYPES, label: translate('background_task.type.ALL') },
+ ...options
+ ];
+
+ return (
+ <Select
+ value={value}
+ onChange={option => onChange(option.value)}
+ className="input-medium"
+ options={allOptions}
+ clearable={false}
+ searchable={false}/>
+ );
+}
--- /dev/null
+/*
+ * 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 (
+ <header className="page-header">
+ <h1 className="page-title">
+ {translate('background_tasks.page')}
+ </h1>
+ <p className="page-description">
+ {translate('background_tasks.page.description')}
+ </p>
+ </header>
+ );
+}
--- /dev/null
+/*
+ * 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 (
+ <input onChange={this.onSearch}
+ value={this.props.query}
+ ref="searchInput"
+ className="input-large"
+ type="search"
+ placeholder="Search"/>
+ );
+ },
+
+ refresh(e) {
+ e.preventDefault();
+ this.props.onRefresh();
+ },
+
+ render() {
+ return (
+ <section className="big-spacer-top big-spacer-bottom">
+ <ul className="bt-search-form">
+ <li>
+ <h6 className="bt-search-form-label">
+ Status
+ </h6>
+ <StatusFilter
+ value={this.props.status}
+ onChange={this.props.onStatusChange}/>
+ </li>
+ {this.props.types.length > 1 && (
+ <li>
+ <h6 className="bt-search-form-label">
+ Type
+ </h6>
+ <TypesFilter
+ value={this.props.taskType}
+ onChange={this.props.onTypeChange}
+ types={this.props.types}/>
+ </li>
+ )}
+ <li>
+ <h6 className="bt-search-form-label">
+ Only Latest Analysis
+ </h6>
+ <CurrentsFilter
+ value={this.props.currents}
+ onChange={this.props.onCurrentsChange}/>
+ </li>
+ <li>
+ <h6 className="bt-search-form-label">
+ Date
+ </h6>
+ <DateFilter
+ value={this.props.date}
+ onChange={this.props.onDateChange}/>
+ </li>
+ <li>
+ <h6 className="bt-search-form-label">
+ Component
+ </h6>
+ {this.renderSearchBox()}
+ </li>
+ <li className="bt-search-form-right">
+ <button
+ ref="reloadButton"
+ onClick={this.refresh}
+ disabled={this.props.fetching}>
+ {translate('reload')}
+ </button>
+ </li>
+ </ul>
+ </section>
+ );
+ }
+});
--- /dev/null
+/*
+ * 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 (
+ <span
+ className="huge-spacer-left"
+ title={translate('background_tasks.in_progress_duration')}
+ data-toggle="tooltip">
+ <i
+ className="spinner spacer-right"
+ style={{ verticalAlign: 'text-top' }}/>
+ <span
+ ref="inProgressDuration"
+ className="emphasised-measure">
+ {formatDuration(this.props.inProgressDuration)}
+ </span>
+ </span>
+ );
+ }
+
+ renderPending () {
+ if (this.props.pendingCount == null) {
+ return null;
+ }
+ if (this.props.pendingCount > 0) {
+ return (
+ <span>
+ <span ref="pendingCount" className="emphasised-measure">{this.props.pendingCount}</span>
+
+ {translate('background_tasks.pending')}
+ <a
+ ref="cancelPending"
+ onClick={this.handleCancelAllPending.bind(this)}
+ className="icon-delete spacer-left"
+ title={translate('background_tasks.cancel_all_tasks')}
+ data-toggle="tooltip"
+ href="#"/>
+ </span>
+ );
+ } else {
+ return (
+ <span>
+ <span ref="pendingCount" className="emphasised-measure">{this.props.pendingCount}</span>
+
+ {translate('background_tasks.pending')}
+ </span>
+ );
+ }
+ }
+
+ renderFailures () {
+ if (this.props.failingCount == null) {
+ return null;
+ }
+
+ if (this.props.options && this.props.options.component) {
+ return null;
+ }
+
+ if (this.props.failingCount > 0) {
+ return (
+ <span>
+ <a ref="failureCount"
+ onClick={this.handleShowFailing.bind(this)}
+ className="emphasised-measure"
+ data-toggle="tooltip"
+ title="Count of projects where processing of most recent analysis report failed"
+ href="#">{this.props.failingCount}</a>
+
+ {translate('background_tasks.failures')}
+ </span>
+ );
+ } else {
+ return (
+ <span>
+ <span ref="failureCount" className="emphasised-measure" data-toggle="tooltip"
+ title="Count of projects where processing of most recent analysis report failed">
+ {this.props.failingCount}
+ </span>
+
+ {translate('background_tasks.failures')}
+ </span>
+ );
+ }
+ }
+
+ render () {
+ return (
+ <section className="big-spacer-top big-spacer-bottom">
+ <span>
+ {this.renderPending()}
+ </span>
+ <span className="huge-spacer-left">
+ {this.renderFailures()}
+ </span>
+ {this.renderInProgressDuration()}
+ </section>
+
+ );
+ }
+}
--- /dev/null
+/*
+ * 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 (
+ <table className="data zebra zebra-hover background-tasks">
+ <thead>
+ <tr>
+ <th> </th>
+ <th> </th>
+ <th> </th>
+ <th>{translate('background_tasks.table.submitted')}</th>
+ <th>{translate('background_tasks.table.started')}</th>
+ <th>{translate('background_tasks.table.finished')}</th>
+ <th>{translate('background_tasks.table.duration')}</th>
+ <th> </th>
+ </tr>
+ </thead>
+ <tbody>
+ {tasks.map((task, index, tasks) => (
+ <Task
+ key={task.id}
+ task={task}
+ index={index}
+ tasks={tasks}
+ onCancelTask={onCancelTask}
+ onFilterTask={onFilterTask}/>
+ ))}
+ </tbody>
+ </table>
+ );
+}
};
+export const ALL_TYPES = 'ALL_TYPES';
+
+
export const CURRENTS = {
ALL: '__ALL__',
ONLY_CURRENTS: 'CURRENTS'
CUSTOM: 'CUSTOM'
};
+export const DEFAULT_FILTERS = {
+ status: STATUSES.ALL,
+ taskType: ALL_TYPES,
+ currents: CURRENTS.ALL,
+ date: {},
+ query: ''
+};
+
export const DATE_FORMAT = 'YYYY-MM-DD';
--- /dev/null
+/*
+ * 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);
--- /dev/null
+/*
+ * 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);
--- /dev/null
+/*
+ * 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);
--- /dev/null
+/*
+ * 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);
--- /dev/null
+/*
+ * 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);
+++ /dev/null
-/*
- * 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 (
- <header className="page-header">
- <h1 className="page-title">{translate('background_tasks.page')}</h1>
- <p className="page-description">{translate('background_tasks.page.description')}</p>
- </header>
- );
- }
-});
+++ /dev/null
-/*
- * 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 (
- <div className="page">
- <Header/>
-
- <Stats
- {...this.props}
- {...this.state}
- cancelPending={this.cancelPending}
- showFailures={this.showFailures}/>
-
- <Search
- {...this.props}
- {...this.state}
- refresh={this.requestData}
- onStatusChange={this.onStatusChange}
- onCurrentsChange={this.onCurrentsChange}
- onDateChange={this.onDateChange}
- onSearch={this.onSearch}/>
-
- <Tasks
- {...this.props}
- tasks={[].concat(this.state.queue, this.state.activity)}
- onTaskCanceled={this.onTaskCanceled}
- onFilter={this.handleFilter}/>
-
- <ListFooter
- count={this.state.queue.length + this.state.activity.length}
- total={this.state.queue.length + this.state.activityTotal}
- loadMore={this.loadMore}/>
- </div>
- );
- }
-});
+++ /dev/null
-/*
- * 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 (
- <div className={className}>
- from
- <input onChange={this.onDateInputChange} ref="minDate" type="text"/>
- to
- <input onChange={this.onDateInputChange} ref="maxDate" type="text"/>
- </div>
- );
- },
-
- 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 (
- <form onSubmit={this.onSearchFormSubmit} className="search-box">
- <button className="search-box-submit button-clean">
- <i className="icon-search"></i>
- </button>
- <input onChange={this.onSearch}
- value={this.props.searchQuery}
- ref="searchInput"
- className="search-box-input"
- type="search"
- placeholder="Search"/>
- </form>
- );
- },
-
- refresh(e) {
- e.preventDefault();
- this.props.refresh();
- let btn = e.target;
- btn.disabled = true;
- setTimeout(() => btn.disabled = false, 500);
- },
-
- render() {
- return (
- <section className="big-spacer-top big-spacer-bottom">
- <ul className="list-inline">
- <li>
- <RadioToggle options={this.getStatusOptions()} value={this.props.statusFilter}
- name="background-task-status" onCheck={this.props.onStatusChange}/>
- </li>
- <li>
- <RadioToggle options={this.getCurrentsOptions()} value={this.props.currentsFilter}
- name="background-task-currents" onCheck={this.props.onCurrentsChange}/>
- </li>
- <li>
- <RadioToggle options={this.getDateOptions()} value={this.props.dateFilter}
- name="background-task-date" onCheck={this.onDateChange}/>
- {this.renderCustomDateInput()}
- </li>
- <li>{this.renderSearchBox()}</li>
- <li className="pull-right">
- <button onClick={this.refresh} ref="reloadButton">{translate('reload')}</button>
- </li>
- </ul>
- </section>
- );
- }
-});
+++ /dev/null
-/*
- * 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 (
- <span className="huge-spacer-left" title={translate('background_tasks.in_progress_duration')}
- data-toggle="tooltip">
- <i className="spinner spacer-right" style={{ verticalAlign: 'text-top' }}/>
- <span ref="inProgressDuration" className="emphasised-measure">
- {formatDuration(this.props.inProgressDuration)}
- </span>
- </span>
- );
- },
-
- renderPending() {
- if (this.props.pendingCount == null) {
- return null;
- }
- if (this.props.pendingCount > 0) {
- return (
- <span>
- <span ref="pendingCount" className="emphasised-measure">{this.props.pendingCount}</span>
-
- {translate('background_tasks.pending')}
- <a ref="cancelPending" onClick={this.onPendingCanceled} className="icon-delete spacer-left"
- title={translate('background_tasks.cancel_all_tasks')} data-toggle="tooltip" href="#"></a>
- </span>
- );
- } else {
- return (
- <span>
- <span ref="pendingCount" className="emphasised-measure">{this.props.pendingCount}</span>
-
- {translate('background_tasks.pending')}
- </span>
- );
- }
- },
-
- renderFailures() {
- if (this.props.failuresCount == null) {
- return null;
- }
-
- if (this.props.options && this.props.options.component) {
- return null;
- }
-
- if (this.props.failuresCount > 0) {
- return (
- <span>
- <a ref="failureCount"
- onClick={this.onFailuresClick}
- className="emphasised-measure"
- data-toggle="tooltip"
- title="Count of projects where processing of most recent analysis report failed"
- href="#">{this.props.failuresCount}</a>
-
- {translate('background_tasks.failures')}
- </span>
- );
- } else {
- return (
- <span>
- <span ref="failureCount" className="emphasised-measure" data-toggle="tooltip"
- title="Count of projects where processing of most recent analysis report failed">
- {this.props.failuresCount}
- </span>
-
- {translate('background_tasks.failures')}
- </span>
- );
- }
- },
-
- render() {
- return (
- <section className="big-spacer-top big-spacer-bottom">
- <span>
- {this.renderPending()}
- </span>
- <span className="huge-spacer-left">
- {this.renderFailures()}
- </span>
- {this.renderInProgressDuration()}
- </section>
-
- );
- }
-});
--- /dev/null
+/*
+ * 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());
+ });
+ };
+}
--- /dev/null
+/*
+ * 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;
+ }
+}
--- /dev/null
+.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;
+}
+++ /dev/null
-/*
- * 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 = <PendingIcon/>;
- break;
- case STATUSES.IN_PROGRESS:
- inner = <i className="spinner"/>;
- break;
- case STATUSES.SUCCESS:
- inner = <span className="badge badge-success">{translate('background_task.status.SUCCESS')}</span>;
- break;
- case STATUSES.FAILED:
- inner = <span className="badge badge-danger">{translate('background_task.status.FAILED')}</span>;
- break;
- case STATUSES.CANCELED:
- inner = <span className="badge badge-muted">{translate('background_task.status.CANCELED')}</span>;
- break;
- default:
- inner = '';
- }
- return <td className="thin spacer-right">{inner}</td>;
- },
-
- renderTaskComponent(task) {
- if (!task.componentKey) {
- return <td><span className="note">{task.id}</span></td>;
- }
-
- return (
- <td>
- <a className="link-with-icon" href={getComponentUrl(task.componentKey)}>
- <span className="little-spacer-right">
- <QualifierIcon qualifier={task.componentQualifier}/>
- </span>
- <span>{task.componentName}</span>
- </a>
- </td>
- );
- },
-
- renderTaskDate(task, field, format = 'LLL') {
- let date = task[field];
- return (
- <td className="thin nowrap text-right">
- {date ? moment(date).format(format) : ''}
- </td>
- );
- },
-
- renderTaskDay(task, previousTask) {
- let shouldDisplay = !previousTask || this.isAnotherDay(task.submittedAt, previousTask.submittedAt);
- return (
- <td className="thin nowrap text-right">
- {shouldDisplay ? moment(task.submittedAt).format('LL') : ''}
- </td>
- );
- },
-
- renderTaskExecutionTime(task) {
- return <td className="thin nowrap text-right">{formatDuration(task.executionTimeMs)}</td>;
- },
-
- isAnotherDay(a, b) {
- return !moment(a).isSame(moment(b), 'day');
- },
-
- renderFilter(task) {
- if (this.props.options && this.props.options.component) {
- return null;
- }
- return <td className="thin nowrap">
- <a onClick={this.handleFilter.bind(this, task)} className="icon-filter icon-half-transparent spacer-left" href="#"
- title={`Show only "${task.componentName}" tasks`} data-toggle="tooltip"/>
- </td>;
- },
-
- renderCancelButton(task) {
- if (task.status === STATUSES.PENDING) {
- return (
- <a onClick={this.onTaskCanceled.bind(this, task)} className="icon-delete"
- title={translate('background_tasks.cancel_task')} data-toggle="tooltip" href="#"></a>
- );
- } else {
- return null;
- }
- },
-
- renderLogsLink(task) {
- if (task.logs) {
- let url = `${window.baseUrl}/api/ce/logs?taskId=${task.id}`;
- return <a target="_blank" href={url}>{translate('background_tasks.logs')}</a>;
- } else {
- return null;
- }
- },
-
- renderTask(task, index, tasks) {
- let previousTask = index > 0 ? tasks[index - 1] : null;
- return (
- <tr key={task.id}>
- {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)}
- <td className="thin nowrap text-right">
- {this.renderLogsLink(task)}
- {this.renderCancelButton(task)}
- </td>
- {this.renderFilter(task)}
- </tr>
- );
- },
-
- render() {
- if (!this.props.tasks.length) {
- return null;
- }
- let tasks = this.props.tasks.map(this.renderTask);
- return (
- <table className="data zebra zebra-hover background-tasks">
- <thead>
- <tr>
- <th> </th>
- <th> </th>
- <th> </th>
- <th>{translate('background_tasks.table.submitted')}</th>
- <th>{translate('background_tasks.table.started')}</th>
- <th>{translate('background_tasks.table.finished')}</th>
- <th>{translate('background_tasks.table.duration')}</th>
- <th> </th>
- </tr>
- </thead>
- <tbody>{tasks}</tbody>
- </table>
- );
- }
-});
--- /dev/null
+/*
+ * 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);
+}
padding: 0 6px;
}
-input[type=search] {
+input[type="search"] {
+ -webkit-appearance: textfield;
+}
+
+input[type="search"]::-webkit-search-decoration {
-webkit-appearance: none;
}
+++ /dev/null
-/*
- * 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;
-}
@import "components/pills";
@import "components/react-select";
-@import "pages/analysis-reports";
@import "pages/coding-rules";
@import "pages/dashboard";
@import "pages/issues";
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';
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.