packages/lib/reporting-components/base-configuration/base-configuration.js (382 lines of code) (raw):
import React from 'react';
import PropTypes from 'prop-types';
import Select from '@jetbrains/ring-ui/components/select/select';
import {Size as InputSize} from '@jetbrains/ring-ui/components/input/input';
import LoaderInline from '@jetbrains/ring-ui/components/loader-inline/loader-inline';
import {i18n} from 'hub-dashboard-addons/dist/localization';
import WidgetRefreshPeriod from '@jetbrains/hub-widget-ui/dist/refresh-period';
import HttpErrorHandler from '@jetbrains/hub-widget-ui/dist/http-error-handler';
import ConfigurationForm from '@jetbrains/hub-widget-ui/dist/configuration-form';
import EmptyWidget, {EmptyWidgetFaces} from '@jetbrains/hub-widget-ui/dist/empty-widget';
import Link from '@jetbrains/ring-ui/components/link/link';
import ServiceResources from '@jetbrains/hub-widget-ui/dist/service-resources';
import '@jetbrains/ring-ui/components/form/form.scss';
import NoEditPermissionsWarning
from '../report-form-controls/no-edit-permissions-warning';
import {
saveReportSettings,
loadCurrentUser
} from '../resources/resources';
import ReportConfigurationTabs from '../report-form-controls/report-configuration-tabs';
import ReportModel from '../report-model/report-model';
import fetcher from '../fetcher/fetcher';
import permissions from '../permissions/permissions';
class BaseConfiguration extends React.Component {
static propTypes = {
EditReportForm: PropTypes.func,
reportId: PropTypes.string,
youTrackMinVersion: PropTypes.string,
refreshPeriod: PropTypes.number.isRequired,
onSubmit: PropTypes.func,
onCancel: PropTypes.func,
onGetReportDraft: PropTypes.func.isRequired,
dashboardApi: PropTypes.object,
youTrackId: PropTypes.string,
reportsSource: PropTypes.func,
reportSettingsSource: PropTypes.func
};
static getReportsSource = (youtrackId, reportsSource, onConnectionError) => {
fetcher().setYouTrack(youtrackId);
return async () => {
try {
const result = await reportsSource();
if (!result || !result.length) {
const permissionCache = await permissions.load();
if (!permissionCache.has('JetBrains.YouTrack.CREATE_REPORT')) {
onConnectionError(i18n('Cannot find any time tracking reports'));
}
}
return result;
} catch (e) {
onConnectionError(HttpErrorHandler.getMessage(e));
return [];
}
};
};
constructor(props) {
super(props);
const selectedYouTrack = props.youTrackId && {
id: props.youTrackId
};
const selectedReport = props.reportId
? {id: props.reportId}
: props.onGetReportDraft();
this.state = {
selectedYouTrack,
selectedReport,
youTracks: [selectedYouTrack],
currentUser: null,
refreshPeriod: props.refreshPeriod,
loadReports: props.youTrackId
? BaseConfiguration.getReportsSource(
props.youTrackId,
this.props.reportsSource,
e => this.setConnectionError(e)
) : () => []
};
}
componentDidMount() {
this.loadYouTrackList();
this.initCurrentUser();
}
componentWillReceiveProps(props) {
this.setState({refreshPeriod: props.refreshPeriod});
}
setConnectionError(connectionError) {
this.setState({connectionError});
}
async loadYouTrackList() {
const {
selectedYouTrack,
selectedReport
} = this.state;
const youTracks = await ServiceResources.getYouTrackServices(
this.props.dashboardApi, this.props.youTrackMinVersion
);
const selectedYouTrackWithAllFields = youTracks.filter(
yt => yt.id === (selectedYouTrack || {}).id
)[0];
if (selectedYouTrackWithAllFields) {
this.setState({
youTracks, selectedYouTrack: selectedYouTrackWithAllFields
}, () => this.setSelectedReport(selectedReport));
} else {
this.setState({
connectionError: i18n('Failed to find proper YouTrack installation')
});
}
}
changeReport = async report => {
const {selectedReport} = this.state;
if (selectedReport && report.id === selectedReport.id) {
return;
}
this.setSelectedReport(report);
};
setSelectedReport(report) {
this.setState({
selectedReport: report,
selectedReportSettingsAreChanged:
ReportModel.NewReport.NEW_REPORT_ID === report.id
}, () => {
if (!ReportModel.hasSettings(report)) {
this.loadReportSettings(report.id);
}
});
}
async initCurrentUser() {
let currentUser;
try {
currentUser = await loadCurrentUser(
this.props.dashboardApi.fetchHub
);
} catch (err) {
return;
}
this.setState({currentUser});
}
async loadReportSettings(reportId) {
let reportWithSettings;
try {
reportWithSettings =
await this.props.reportSettingsSource(reportId);
} catch (err) {
if (
err.status === ReportModel.ResponseStatus.NOT_FOUND ||
err.status === ReportModel.ResponseStatus.NO_ACCESS
) {
reportWithSettings = this.props.onGetReportDraft();
} else {
this.setState({
reportSettingsLoadingError: HttpErrorHandler.getMessage(err)
});
return null;
}
}
if (
reportWithSettings.id === (this.state.selectedReport || {}).id ||
reportWithSettings.id === ReportModel.NewReport.NEW_REPORT_ID
) {
this.setState({
selectedReport: reportWithSettings,
reportSettingsLoadingError: null
});
}
return reportWithSettings;
}
removeWidget = async () =>
await this.props.dashboardApi.removeWidget();
changeYouTrack = selected => {
this.setState({
selectedYouTrack: selected.model,
selectedReport: {},
errorMessage: '',
isLoading: true,
connectionError: null,
loadReports: BaseConfiguration.getReportsSource(
selected.model.id,
this.props.reportsSource,
e => this.setConnectionError(e)
)
}, () => this.setState({
isLoading: false,
selectedReport: this.props.onGetReportDraft()
}));
};
onReportSettingsChange = report =>
this.setState({
selectedReportSettingsAreChanged: true,
selectedReport: report
});
onReportValidStatusChange = selectedReportIsValid =>
this.setState({selectedReportIsValid});
setErrorMessage = errorMessage =>
this.setState({
isLoading: false, errorMessage
});
submitForm = async () => {
const {
selectedReport,
refreshPeriod,
selectedYouTrack
} = this.state;
let reportId = selectedReport.id;
this.setState({isLoading: true});
if (this.state.selectedReportSettingsAreChanged) {
try {
const savedReport = await saveReportSettings(
fetcher().fetchYouTrack, selectedReport
);
reportId = savedReport.id;
} catch (err) {
return this.setErrorMessage(`${i18n('Cannot save report')}: ${HttpErrorHandler.getMessage(err)}`);
}
}
try {
await this.props.onSubmit(
reportId, refreshPeriod, selectedYouTrack
);
} catch (err) {
return this.setErrorMessage(`${i18n('Cannot update widget')}: ${HttpErrorHandler.getMessage(err)}`);
}
return this.setState({isLoading: false});
};
renderTab(reportWithSettings) {
const {EditReportForm} = this.props;
const {
currentUser
} = this.state;
return (
<div>
<NoEditPermissionsWarning
report={reportWithSettings}
onChangeReport={this.changeReport}
/>
<EditReportForm
report={reportWithSettings}
onReportSettingsChange={this.onReportSettingsChange}
onValidStateChange={this.onReportValidStatusChange}
disabled={!reportWithSettings.editable}
currentUser={currentUser}
fetchYouTrack={fetcher().fetchYouTrack}
fetchHub={fetcher().fetchHub}
/>
</div>
);
}
renderReportsSettings(reportWithSettings) {
return (
<div>
{
(!!reportWithSettings) &&
<ReportConfigurationTabs
report={reportWithSettings}
onChange={this.changeReport}
onCreateReport={this.props.onGetReportDraft}
reportsSource={this.state.loadReports}
>
{this.renderTab(reportWithSettings)}
</ReportConfigurationTabs>
}
</div>
);
}
renderRefreshPeriod() {
const {
isLoading,
errorMessage,
refreshPeriod
} = this.state;
if (isLoading || errorMessage) {
return '';
}
const changeRefreshPeriod = newValue =>
this.setState({refreshPeriod: newValue});
return (
<WidgetRefreshPeriod
seconds={refreshPeriod}
onChange={changeRefreshPeriod}
/>
);
}
renderYouTrackSelect() {
const {
youTracks,
selectedYouTrack
} = this.state;
const youTrackServiceToSelectItem = it => it && {
key: it.id,
label: it.name,
description: it.homeUrl,
model: it
};
return youTracks.length > 1 && (
<div className="ring-form__group">
<Select
data={youTracks.map(youTrackServiceToSelectItem)}
selected={youTrackServiceToSelectItem(selectedYouTrack)}
onSelect={this.changeYouTrack}
filter={true}
label={i18n('Select YouTrack')}
size={InputSize.FULL}
/>
</div>
);
}
renderConnectionError() {
const {
youTracks,
connectionError
} = this.state;
return (
<div>
{
this.renderYouTrackSelect()
}
<EmptyWidget
face={EmptyWidgetFaces.ERROR}
message={
youTracks.length > 1
? i18n('Failed to load data from selected YouTrack')
: connectionError
}
>
<Link
pseudo={true}
onClick={this.removeWidget}
>
{i18n('Remove widget')}
</Link>
</EmptyWidget>
</div>
);
}
renderBaseConfigurationOptions() {
const {
errorMessage,
selectedReport,
reportSettingsLoadingError,
selectedReportIsValid
} = this.state;
const reportWithSettings =
ReportModel.hasSettings(selectedReport)
? selectedReport
: undefined;
return (
<ConfigurationForm
warning={errorMessage}
isInvalid={!!errorMessage || !selectedReport || !selectedReportIsValid}
isLoading={this.state.isLoading}
panelControls={this.renderRefreshPeriod()}
onSave={this.submitForm}
onCancel={this.props.onCancel}
>
{
this.renderYouTrackSelect()
}
{
this.renderReportsSettings(reportWithSettings)
}
{
!reportWithSettings &&
(
reportSettingsLoadingError
? (
<div className="ring-form__group">
{reportSettingsLoadingError}
</div>
) : <LoaderInline/>
)
}
</ConfigurationForm>
);
}
render() {
const {connectionError} = this.state;
if (connectionError) {
return this.renderConnectionError();
}
return this.renderBaseConfigurationOptions();
}
}
export default BaseConfiguration;