Share report template functionality
This commit is contained in:
parent
e6ad0e239e
commit
4822a50d0b
35 changed files with 614 additions and 128 deletions
|
|
@ -23,7 +23,7 @@ function handleError(that, error) {
|
|||
function withErrorHandling(target) {
|
||||
const inst = target.prototype;
|
||||
|
||||
if (inst._withErrorHandlingApplied) return;
|
||||
if (inst._withErrorHandlingApplied) return target;
|
||||
inst._withErrorHandlingApplied = true;
|
||||
|
||||
const contextTypes = target.contextTypes || {};
|
||||
|
|
|
|||
|
|
@ -356,7 +356,7 @@ class TreeTableSelect extends Component {
|
|||
}
|
||||
}
|
||||
|
||||
@translate()
|
||||
@translate(null, { withRef: true })
|
||||
class TableSelect extends Component {
|
||||
constructor(props) {
|
||||
super(props);
|
||||
|
|
@ -369,6 +369,7 @@ class TableSelect extends Component {
|
|||
|
||||
static propTypes = {
|
||||
dataUrl: PropTypes.string,
|
||||
data: PropTypes.array,
|
||||
columns: PropTypes.array,
|
||||
selectionKeyIndex: PropTypes.number,
|
||||
selectionLabelIndex: PropTypes.number,
|
||||
|
|
@ -426,6 +427,10 @@ class TableSelect extends Component {
|
|||
});
|
||||
}
|
||||
|
||||
refresh() {
|
||||
this.table.refresh();
|
||||
}
|
||||
|
||||
render() {
|
||||
const props = this.props;
|
||||
const owner = this.context.formStateOwner;
|
||||
|
|
@ -443,7 +448,7 @@ class TableSelect extends Component {
|
|||
</span>
|
||||
</div>
|
||||
<div className={'mt-tableselect-table' + (this.state.open ? '' : ' mt-tableselect-table-hidden')}>
|
||||
<Table dataUrl={props.dataUrl} columns={props.columns} selectMode={props.selectMode} selectionAsArray={this.props.selectionAsArray} withHeader={props.withHeader} selection={owner.getFormValue(id)} onSelectionDataAsync={::this.onSelectionDataAsync} onSelectionChangedAsync={::this.onSelectionChangedAsync}/>
|
||||
<Table ref={node => this.table = node} data={props.data} dataUrl={props.dataUrl} columns={props.columns} selectMode={props.selectMode} selectionAsArray={this.props.selectionAsArray} withHeader={props.withHeader} selection={owner.getFormValue(id)} onSelectionDataAsync={::this.onSelectionDataAsync} onSelectionChangedAsync={::this.onSelectionChangedAsync}/>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
|
|
@ -451,7 +456,7 @@ class TableSelect extends Component {
|
|||
return wrapInput(id, htmlId, owner, props.label, props.help,
|
||||
<div>
|
||||
<div>
|
||||
<Table dataUrl={props.dataUrl} columns={props.columns} selectMode={props.selectMode} selectionAsArray={this.props.selectionAsArray} withHeader={props.withHeader} selection={owner.getFormValue(id)} onSelectionChangedAsync={::this.onSelectionChangedAsync}/>
|
||||
<Table ref={node => this.table = node} data={props.data} dataUrl={props.dataUrl} columns={props.columns} selectMode={props.selectMode} selectionAsArray={this.props.selectionAsArray} withHeader={props.withHeader} selection={owner.getFormValue(id)} onSelectionChangedAsync={::this.onSelectionChangedAsync}/>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
|
|
@ -459,6 +464,15 @@ class TableSelect extends Component {
|
|||
}
|
||||
}
|
||||
|
||||
/*
|
||||
Refreshes the table. This method is provided to allow programmatic refresh from a handler outside the table.
|
||||
The reference to the table can be obtained by ref.
|
||||
*/
|
||||
TableSelect.prototype.refresh = function() {
|
||||
this.getWrappedInstance().refresh()
|
||||
};
|
||||
|
||||
|
||||
class ACEEditor extends Component {
|
||||
static propTypes = {
|
||||
id: PropTypes.string.isRequired,
|
||||
|
|
|
|||
|
|
@ -38,4 +38,9 @@
|
|||
|
||||
.mt-tableselect-dropdown input[readonly] {
|
||||
background-color: white;
|
||||
}
|
||||
|
||||
h3.legend {
|
||||
font-size: 21px;
|
||||
margin-bottom: 20px;
|
||||
}
|
||||
|
|
@ -9,6 +9,7 @@ import './page.css';
|
|||
import { withErrorHandling } from './error-handling';
|
||||
import interoperableErrors from '../../../shared/interoperable-errors';
|
||||
import { DismissibleAlert, Button } from './bootstrap-components';
|
||||
import mailtrainConfig from 'mailtrainConfig';
|
||||
|
||||
|
||||
class PageContent extends Component {
|
||||
|
|
@ -200,10 +201,16 @@ class SectionContent extends Component {
|
|||
this.setFlashMessage(severity, text);
|
||||
}
|
||||
|
||||
errorHandler(error) {
|
||||
if (error instanceof interoperableErrors.NotLoggedInError) {
|
||||
ensureAuthenticated() {
|
||||
if (!mailtrainConfig.isAuthenticated) {
|
||||
/* FIXME, once we turn Mailtrain to single-page application, this should become navigateTo */
|
||||
window.location = '/account/login?next=' + encodeURIComponent(this.props.root);
|
||||
}
|
||||
}
|
||||
|
||||
errorHandler(error) {
|
||||
if (error instanceof interoperableErrors.NotLoggedInError) {
|
||||
this.ensureAuthenticated();
|
||||
} else if (error.response && error.response.data && error.response.data.message) {
|
||||
console.error(error);
|
||||
this.navigateToWithFlashMessage(this.props.root, 'danger', error.response.data.message);
|
||||
|
|
@ -312,7 +319,7 @@ class DropdownLink extends Component {
|
|||
}
|
||||
|
||||
function withPageHelpers(target) {
|
||||
withErrorHandling(target);
|
||||
target = withErrorHandling(target);
|
||||
|
||||
const inst = target.prototype;
|
||||
|
||||
|
|
@ -346,16 +353,32 @@ function withPageHelpers(target) {
|
|||
return this.context.sectionContent.navigateToWithFlashMessage(path, severity, text);
|
||||
}
|
||||
|
||||
inst.axios
|
||||
|
||||
return target;
|
||||
}
|
||||
|
||||
function requiresAuthenticatedUser(target) {
|
||||
const comp1 = withPageHelpers(target);
|
||||
|
||||
function comp2(props, context) {
|
||||
comp1.call(this, props, context);
|
||||
context.sectionContent.ensureAuthenticated();
|
||||
}
|
||||
|
||||
comp2.prototype = comp1.prototype;
|
||||
|
||||
for (const attr in comp1) {
|
||||
comp2[attr] = comp1[attr];
|
||||
}
|
||||
|
||||
return comp2;
|
||||
}
|
||||
|
||||
export {
|
||||
Section,
|
||||
Title,
|
||||
Toolbar,
|
||||
NavButton,
|
||||
DropdownLink,
|
||||
withPageHelpers
|
||||
withPageHelpers,
|
||||
requiresAuthenticatedUser
|
||||
};
|
||||
|
|
@ -6,7 +6,6 @@ import { translate } from 'react-i18next';
|
|||
import PropTypes from 'prop-types';
|
||||
|
||||
import jQuery from 'jquery';
|
||||
import '../../public/jquery/jquery-ui-1.12.1.min.js';
|
||||
|
||||
import 'datatables.net';
|
||||
import 'datatables.net-bs';
|
||||
|
|
@ -82,13 +81,20 @@ class Table extends Component {
|
|||
selMap.set(elem, undefined);
|
||||
}
|
||||
|
||||
if (this.table) {
|
||||
const self = this;
|
||||
this.table.rows().every(function() {
|
||||
const data = this.data();
|
||||
const key = data[self.props.selectionKeyIndex];
|
||||
if (props.data) {
|
||||
for (const rowData of props.data) {
|
||||
const key = rowData[props.selectionKeyIndex];
|
||||
if (selMap.has(key)) {
|
||||
selMap.set(key, data);
|
||||
selMap.set(key, rowData);
|
||||
}
|
||||
}
|
||||
|
||||
} else if (this.table) {
|
||||
this.table.rows().every(function() {
|
||||
const rowData = this.data();
|
||||
const key = rowData[props.selectionKeyIndex];
|
||||
if (selMap.has(key)) {
|
||||
selMap.set(key, rowData);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
|
@ -118,26 +124,28 @@ class Table extends Component {
|
|||
}
|
||||
|
||||
@withAsyncErrorHandler
|
||||
async fetchSelectionData() {
|
||||
async fetchAndNotifySelectionData() {
|
||||
if (this.props.onSelectionDataAsync) {
|
||||
const keysToFetch = [];
|
||||
for (const pair of this.selectionMap.entries()) {
|
||||
if (!pair[1]) {
|
||||
keysToFetch.push(pair[0]);
|
||||
if (!this.props.data) {
|
||||
const keysToFetch = [];
|
||||
for (const pair of this.selectionMap.entries()) {
|
||||
if (!pair[1]) {
|
||||
keysToFetch.push(pair[0]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (keysToFetch.length > 0) {
|
||||
const response = await axios.post(this.props.dataUrl, {
|
||||
operation: 'getBy',
|
||||
column: this.props.selectionKeyIndex,
|
||||
values: keysToFetch
|
||||
});
|
||||
if (keysToFetch.length > 0) {
|
||||
const response = await axios.post(this.props.dataUrl, {
|
||||
operation: 'getBy',
|
||||
column: this.props.selectionKeyIndex,
|
||||
values: keysToFetch
|
||||
});
|
||||
|
||||
for (const row of response.data) {
|
||||
const key = row[this.props.selectionKeyIndex];
|
||||
if (this.selectionMap.has(key)) {
|
||||
this.selectionMap.set(key, row);
|
||||
for (const row of response.data) {
|
||||
const key = row[this.props.selectionKeyIndex];
|
||||
if (this.selectionMap.has(key)) {
|
||||
this.selectionMap.set(key, row);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -293,7 +301,7 @@ class Table extends Component {
|
|||
clearTimeout(this.refreshTimeoutId);
|
||||
});
|
||||
|
||||
this.fetchSelectionData();
|
||||
this.fetchAndNotifySelectionData();
|
||||
}
|
||||
|
||||
componentDidUpdate() {
|
||||
|
|
@ -314,7 +322,7 @@ class Table extends Component {
|
|||
}
|
||||
|
||||
this.updateSelectInfo();
|
||||
this.fetchSelectionData();
|
||||
this.fetchAndNotifySelectionData();
|
||||
}
|
||||
|
||||
async notifySelection(eventCallback, newSelectionMap) {
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue