UNPKG

@digifi-los/reactapp

Version:
1,167 lines (1,146 loc) 52 kB
import React, { Component, /*PropTypes,*/ } from 'react'; import { Link, } from 'react-router'; import * as rb from 're-bulma'; import moment from 'moment'; import numeral from 'numeral'; import utilities from '../../util'; import qs from 'querystring'; import debounce from 'debounce'; import { flatten, } from 'flat'; import { getRenderedComponent, } from '../AppLayoutMap'; // import capitalize from 'capitalize'; // import pluralize from 'pluralize'; import FileReaderInput from 'react-file-reader-input'; import path from 'path'; import { csv2json, json2csv, } from 'json-2-csv'; import RACodeMirror from '../RACodeMirror'; import ResponsiveDatalist from '../ResponsiveDatalist'; import { filterQuerySelectOptions, propTypes, defaultProps, getOptionsHeaders, getHeadersFromRows, excludeEmptyHeaders, getFilterOptions, defaultNewRowData, filterQuerySelectOptionsMap, getFilterSortableOption, } from './TableHelpers'; const filterLabelStyleProps = { alignItems: 'center', display: 'flex', flex: 1, height: '100%', }; class ResponsiveTable extends Component { constructor(props) { super(props); // console.debug('this.props.getState()',this.props.getState()); let rows = props.rows || []; let headers = ((!props.headers || !props.headers.length) && rows[0]) ? getHeadersFromRows({ rows: props.rows, sortable: props.sortable, excludeEmptyHeaders: props.excludeEmptyHeaders, }) : props.headers; headers = getOptionsHeaders(props, headers); headers = excludeEmptyHeaders({ headers, excludeEmptyHeaders: props.excludeEmptyHeaders, }); if (props.flattenRowData) { rows = rows.map(row => Object.assign({}, row, flatten(row, props.flattenRowDataOptions))); } this.filterSelectOptions = getFilterOptions({ rows, headers, filters: this.props.filterSelectOptions, simpleSearchFilter: this.props.simpleSearchFilter, }); this.sortableSelctOptions = getFilterSortableOption({ headers, }); this.state = { headers: headers, rows: rows, hasPagination: props.hasPagination, hasHeader: props.hasHeader, hasFooter: props.hasFooter, limit: props.limit, currentPage: props.currentPage, numItems: props.numItems, numPages: Math.ceil(props.numItems / props.limit), numButtons: props.numButtons, isLoading: false, sortProp: this.props.searchField || 'createdat', sortOrder: 'desc', filterRowData: [], filterRowNewData: defaultNewRowData, newRowData: {}, selectedRowData: {}, selectedRowIndex: {}, showFilterSearch: props.showFilterSearch, // usingFiltersInSearch: props.usingFiltersInSearch, }; this.searchFunction = debounce(this.updateTableData, 200); this.getRenderedComponent = getRenderedComponent.bind(this); this.addRow = this.updateByAddRow.bind(this); this.replaceRows = this.updateByReplacingRows.bind(this); this.addingRows = this.updateByAddingRows.bind(this); this.selectRow = this.updateSelectedRow.bind(this); this.deleteRow = this.updateByDeleteRow.bind(this); this.moveRowDown = this.updateByMoveRowDown.bind(this); this.moveRowUp = this.updateByMoveRowUp.bind(this); this.updateNewRowText = this.updateNewRowDataText.bind(this); this.updateInlineRowText = this.updateInlineRowDataText.bind(this); this.getFooterAddRow = this.updateGetFooterAddRow.bind(this); this.removeFilterRow = this.removeFilterByDeleteRow.bind(this); this.addFilterRow = this.addFilterByAddRow.bind(this); this.updateNewFilterRowText = this.updateNewFilterRowDataText.bind(this); } componentWillReceiveProps(nextProps) { let rows = nextProps.rows || []; let headers = ((!nextProps.headers || !nextProps.headers.length) && rows[0]) ? getHeadersFromRows({ rows, sortable: nextProps.sortable, excludeEmptyHeaders: nextProps.excludeEmptyHeaders, }) : nextProps.headers; headers = getOptionsHeaders(nextProps); headers = excludeEmptyHeaders({ headers, excludeEmptyHeaders: nextProps.excludeEmptyHeaders, }); if (nextProps.flattenRowData) { rows = rows.map(row => Object.assign({}, row, flatten(row, nextProps.flattenRowDataOptions))); } // console.debug('nextProps.limit', nextProps.limit); // console.debug('this.state.limit', this.state.limit); this.setState({ headers: headers, rows: rows, hasPagination: nextProps.hasPagination, hasHeader: nextProps.hasHeader, hasFooter: nextProps.hasFooter, limit: nextProps.limit, currentPage: nextProps.currentPage, numItems: nextProps.numItems, numPages: Math.ceil(nextProps.numItems / nextProps.limit), numButtons: nextProps.numButtons, }); } updateSelectedRow(options) { // console.debug({ options }); this.updateTableData(options); } updateByReplacingRows(newrows) { this.updateTableData({ rows: newrows.concat([]), clearNewRowData: true, }); } updateByAddingRows(newrows) { let rows = this.state.rows.concat(newrows || []); this.updateTableData({ rows, clearNewRowData: true, }); } updateByAddRow() { let rows = this.state.rows.concat([]); let newRow = Object.assign({}, this.state.newRowData); rows.splice(rows.length, 0, newRow); // console.debug({ rowIndex, rows, deletedRow }, this.state.rows); // this.props.onChange({ rows, }); this.updateTableData({ rows, clearNewRowData: true, }); } updateByDeleteRow(rowIndex) { let rows = this.state.rows.concat([]); rows.splice(rowIndex, 1); // console.debug({ rowIndex, rows }, this.state.rows); // this.props.onChange({ rows, }); this.updateTableData({ rows, }); } updateByMoveRowUp(rowIndex) { let rows = this.state.rows.concat([]); let deletedRow = rows.splice(rowIndex, 1)[0]; rows.splice(rowIndex - 1, 0, deletedRow); // console.debug({ rowIndex, rows, deletedRow }, this.state.rows); // this.props.onChange({ rows, }); this.updateTableData({ rows, }); } updateByMoveRowDown(rowIndex) { let rows = this.state.rows.concat([]); let deletedRow = rows.splice(rowIndex, 1)[0]; rows.splice(rowIndex + 1, 0, deletedRow); // console.debug({ rowIndex, rows, deletedRow }, this.state.rows); // this.props.onChange({ rows, }); this.updateTableData({ rows, }); } updateNewRowDataText(options) { let { name, text, } = options; let updatedStateProp = { newRowData: Object.assign({}, this.state.newRowData, { [name]: text, }), }; this.props.headers.forEach(header => { if (header.sortid !== name && header.formtype && header.defaultValue && !updatedStateProp.newRowData[header.sortid]) { updatedStateProp.newRowData[header.sortid] = header.defaultValue; } }); // console.debug({ updatedStateProp, options }); this.setState(updatedStateProp); } updateInlineRowDataText(options) { let { name, text, rowIndex, } = options; let rows = this.state.rows.concat([]); rows[rowIndex][name] = text; // console.debug({ rowIndex, rows, deletedRow }, this.state.rows); // this.props.onChange({ rows, }); this.updateTableData({ rows, }); } handleFileUpload(type) { return (e, results) => { let updatefunction = (type === 'replace') ? this.replaceRows : this.addingRows; try { console.debug({ e, results, }); results.forEach(result => { const [e, file, ] = result; if (path.extname(file.name) === '.csv') { csv2json(e.target.result, (err, newRows) => { if (err) throw err; // console.debug({ newRows, }, 'e.target.result', e.target.result); updatefunction(newRows); }, { options: this.props.csvOptions, // keys: this.state.headers.map(header => header.sortid), }); } else { let newRows = JSON.parse(e.target.result); updatefunction(newRows); } }); } catch (e) { this.props.errorNotification(e); } }; } removeFilterByDeleteRow(rowIndex) { let rows = this.state.filterRowData.concat([]); rows.splice(rowIndex, 1); this.setState({ filterRowData: rows, }, () => { this.updateTableData({}); }); } addFilterByAddRow() { let rows = this.state.filterRowData.concat([]); let newRow = Object.assign({}, this.state.filterRowNewData); rows.splice(rows.length, 0, newRow); if (newRow.property === '__property__') { this.props.createNotification({ text: 'Please select a property', type: 'error', timed: 5000, }); } else if (newRow.filter_value === '__filter__') { this.props.createNotification({ text: 'Please select a filter', type: 'error', timed: 5000, }); } else { // console.debug('addFilterByAddRow', { rows }); this.setState({ filterRowData: rows, filterRowNewData: defaultNewRowData, }, () => { this.updateTableData({}); }); } } updateNewFilterRowDataText(options) { let { name, text, } = options; let updatedStateProp = { filterRowNewData: Object.assign({}, this.state.filterRowNewData, { [name]: text, }), }; // console.debug({ updatedStateProp, options }); this.setState(updatedStateProp); } updateTableData(options) { // console.debug({ options, }); let updatedState = {}; let newSortOptions = {}; if (options.clearNewRowData) { updatedState.newRowData = {}; } if (typeof options.selectedRowIndex !== undefined) { updatedState.selectedRowIndex = options.selectedRowIndex; } if (typeof options.selectedRowData !== undefined) { updatedState.selectedRowData = options.selectedRowData; } if (!this.props.baseUrl) { // console.debug({options}) updatedState.rows = (typeof options.rows !== 'undefined') ? options.rows : this.props.rows; // console.debug({ updatedState, }); if (options.sort) { newSortOptions.sortProp = options.sort; if (this.state.sortProp === options.sort) { newSortOptions.sortOrder = (this.state.sortOrder !== 'desc') ? 'desc' : 'asc'; } else { newSortOptions.sortOrder = 'desc'; } updatedState.rows = updatedState.rows.sort(utilities.sortObject(newSortOptions.sortOrder, options.sort)); updatedState.sortOrder = newSortOptions.sortOrder; updatedState.sortProp = options.sort; } else if (this.state.sortOrder || this.state.sortProp) { newSortOptions.sortProp = this.state.sortProp; newSortOptions.sortOrder = (this.state.sortOrder === 'desc' || this.state.sortOrder === '-') ? 'desc' : 'asc'; updatedState.rows = updatedState.rows.sort(utilities.sortObject(newSortOptions.sortOrder, newSortOptions.sortProp)); } if (this.props.tableSearch && this.props.searchField && options.search) { updatedState.rows = this.props.rows.filter(row => row[this.props.searchField].indexOf(options.search) !== -1); } if (this.props.tableSearch && this.state.filterRowData && this.state.filterRowData.length) { let filteredRows = []; updatedState.rows.forEach(row => { this.state.filterRowData.forEach(filter => { if (row[filter.property]) { switch (filter.filter_value) { case 'like': case 'in': if (row[filter.property].indexOf(filter.value) !== -1) filteredRows.push(row); break; case 'not': if (row[filter.property] !== filter.value) filteredRows.push(row); break; case 'not-like': case 'not-in': if (row[filter.property].indexOf(filter.value) === -1) filteredRows.push(row); break; case 'lt': if (row[filter.property] < filter.value) filteredRows.push(row); break; case 'lte': if (row[filter.property] <= filter.value) filteredRows.push(row); break; case 'gt': if (row[filter.property] > filter.value) filteredRows.push(row); break; case 'gte': if (row[filter.property] >= filter.value) filteredRows.push(row); break; case 'exists': if (typeof row[filter.property] !== 'undefined') filteredRows.push(row); break; case 'size': if (row[filter.property].length > filter.value) filteredRows.push(row); break; case 'is-date': if (moment(row[filter.property]).isSame(filter.value)) filteredRows.push(row); break; case 'lte-date': if (moment(row[filter.property]).isSameOrBefore(filter.value)) filteredRows.push(row); break; case 'lt-date': if (moment(row[filter.property]).isBefore(filter.value)) filteredRows.push(row); break; case 'gte-date': if (moment(row[filter.property]).isSameOrAfter(filter.value)) filteredRows.push(row); break; case 'gt-date': if (moment(row[filter.property]).isAfter(filter.value)) filteredRows.push(row); break; case 'is': default: if (row[filter.property] === filter.value) filteredRows.push(row); break; } } }); // row[ this.props.searchField ].indexOf(options.search) !== -1 }); updatedState.rows = filteredRows; // console.debug('updatedState.rows', updatedState.rows, { filteredRows, }); } updatedState.numPages = Math.ceil(updatedState.rows.length / this.state.limit); updatedState.limit = this.state.limit; updatedState.currentPage = (typeof options.pagenum !== 'undefined') ? options.pagenum : (this.state.currentPage && this.state.currentPage <= updatedState.numPages) ? this.state.currentPage : 1; updatedState.isLoading = false; if (this.props.tableForm) { // console.debug('befroe', {updatedState}) this.props.onChange(updatedState); } // else { this.setState(updatedState); // } } else { if (options.sort) { newSortOptions.sortProp = options.sort; if (this.state.sortProp === options.sort) { newSortOptions.sortOrder = (this.state.sortOrder === '') ? '-' : ''; } else { newSortOptions.sortOrder = ''; } } else if (this.state.sortOrder || this.state.sortProp) { newSortOptions.sortProp = this.state.sortProp; newSortOptions.sortOrder = (this.state.sortOrder === 'desc' || this.state.sortOrder === '-') ? '-' : ''; } if (options.pagenum < 1) { options.pagenum = 1; } this.setState({ isLoading: true, }); let stateProps = this.props.getState(); let fetchURL = `${stateProps.settings.basename}${this.props.baseUrl}&${qs.stringify({ limit: this.state.limit || this.props.limit, sort: (newSortOptions.sortProp) ? `${newSortOptions.sortOrder}${newSortOptions.sortProp}` : undefined, fq: (this.state.filterRowData && this.state.filterRowData.length) ? this.state.filterRowData.map(frd => { return `${frd.property}|||${frd.filter_value}|||${frd.value}`; }) : undefined, search: options.search, allowSpecialCharacters: true, pagenum: options.pagenum || 1, })}`; // console.debug('this.state.filterRowData', this.state.filterRowData, { options, fetchURL, }); let headers = Object.assign({ 'x-access-token': stateProps.user.jwt_token, }, stateProps.settings.userprofile.options.headers); utilities.fetchComponent(fetchURL, { headers, })() .then(response => { // let usingResponsePages = false; // console.debug('this.props.dataMap',this.props.dataMap) this.props.dataMap.forEach(data => { if (data.key === 'rows') { let rows = response[ data.value ] || []; if (this.props.flattenRowData) { updatedState[ data.key ] = rows.map(row => flatten(row, this.props.flattenRowDataOptions)); } } else { // if (data.key === 'numPages') { // usingResponsePages = true; // } updatedState[ data.key ] = response[ data.value ]; } }); updatedState.numPages = Math.ceil(updatedState.numItems / this.state.limit); updatedState.limit = this.state.limit; updatedState.currentPage = (typeof options.pagenum !=='undefined') ? options.pagenum : this.props.currentPage; updatedState.isLoading = false; if (options.sort) { updatedState.sortOrder = newSortOptions.sortOrder; updatedState.sortProp = options.sort; } if (this.props.tableForm) { this.props.onChange(updatedState); } this.setState(updatedState); }, e => { this.props.errorNotification(e); }); } } formatValue(value, row, options, header) { try { // console.debug({ value, row, options, header, }); // console.debug(options.rowIndex,this.state.selectedRowIndex) let returnValue = value; if (header && header.stringify) { value = JSON.stringify(value, null, 2); returnValue = JSON.stringify(value, null, 2); } if (header && header.tostring) { value = value.toString(); returnValue = value.toString(); } if (header && header.selectedOptionRowHeader) { return <input type="radio" checked={(options.rowIndex===this.state.selectedRowIndex)?true:false} />; } else if (this.props.useInputRows && header && header.formtype && header.formtype==='code') { let CodeMirrorProps = Object.assign({}, { codeMirrorProps: { lineNumbers: true, value: value, //formElement.value || this.state[ formElement.name ] || getPropertyAttribute({ element:formElement, property:this.state, }); //value: this.state[ formElement.name ] || formElement.value, style: { minHeight:200, }, lineWrapping:true, onChange: function (text){ // console.log({ newvalue }); let name = header.sortid; let rowIndex = options.rowIndex; this.updateInlineRowText({ name, text, rowIndex, }); }.bind(this), }, }, header.CodeMirrorProps); let codeProps = Object.assign({ wrapperProps: { style: { overflow: 'auto', backgroundColor: 'white', border: '1px solid #d3d6db', borderRadius: 3, height: 'auto', boxShadow: 'inset 0 1px 2px rgba(17,17,17,.1)', }, }, }, header.codeProps); return <RACodeMirror {...CodeMirrorProps} {...codeProps} />; } else if (this.props.useInputRows && header && header.formtype && header.formtype==='textarea') { return <rb.Textarea {...header.textareaProps} value={value} onChange={(event) => { let text = event.target.value; let name = header.sortid; let rowIndex = options.rowIndex; this.updateInlineRowText({ name, text, rowIndex, }); }} >{value}</rb.Textarea>; } else if (this.props.useInputRows && header && header.formtype && header.formtype==='text') { return <rb.Input value={value} {...header.inputProps} onChange={(event) => { let text = event.target.value; let name = header.sortid; let rowIndex = options.rowIndex; this.updateInlineRowText({ name, text, rowIndex, }); }} >{value}</rb.Input>; } else if (this.props.useInputRows && header && header.formtype && header.formtype === 'select') { let selectOptions = header.formoptions || []; return <rb.Select value={value} {...header.selectProps} onChange={(event) => { let text = event.target.value; let name = header.sortid; let rowIndex = options.rowIndex; this.updateInlineRowText({ name, text, rowIndex, }); }}> {selectOptions.map((opt, k) => { return <option key={k} disabled={opt.disabled} value={opt.value}>{opt.label || opt.value}</option>; })} </rb.Select>; } else if (this.props.useInputRows && header && header.formtype && header.formtype === 'datalist') { let rowdata = Array.isArray(this.props.__tableOptions[ header.sortid ][ options.rowIndex ]) ? this.props.__tableOptions[ header.sortid ][ options.rowIndex ] : Array.isArray(this.props.__tableOptions[ header.sortid ]) ? this.props.__tableOptions[ header.sortid ] : []; return <ResponsiveDatalist value={value} {...header.datalistProps} datalistdata={ rowdata } onChange={(event) => { let text = event; let name = header.sortid; let rowIndex = options.rowIndex; this.updateInlineRowText({ name, text, rowIndex, }); }} > </ResponsiveDatalist> } else if (typeof options.idx !=='undefined' && typeof returnValue==='string' && returnValue.indexOf('--idx--')!==-1) { returnValue = returnValue.replace('--idx--', options.idx); } if (typeof options.idx !=='undefined' && typeof returnValue==='string' && returnValue.indexOf('--idx-ctr--')!==-1) { returnValue = returnValue.replace('--idx-ctr--', (options.idx+1)); } if (options.momentFormat) { returnValue = moment(value).format(options.momentFormat); } else if (options.numeralFormat) { returnValue = numeral(value).format(options.numeralFormat); } else if (header && header.wrapPreOutput) { returnValue = <pre {...header.wrapPreOutputProps}>{value}</pre>; } else if (options.icon && value) { // console.debug({value}) if (typeof value !== 'string' && Array.isArray(value)) { let icons = value.map((val, i) => <rb.Icon key={i+Math.random()} {...options.iconProps} icon={val} />); return icons; } else { return <rb.Icon {...options.iconProps} icon={value} />; } } else if (options.image && value) { if (typeof value !== 'string' && Array.isArray(value)) { let images = value.map((val, i) => <rb.Image key={i} {...options.imageProps} src={val} />); return { images, }; } else { return <rb.Image {...options.imageProps} src={value} />; } } if (typeof returnValue === 'undefined' || (returnValue === null && this.props.suppressNullValues)) { return ''; // } else if (typeof returnValue !== 'object') { // return JSON.stringify(returnValue); } else if (returnValue === null) { return 'null'; } else { return returnValue.toString(); } } catch (e) { console.log({ value, row, options, header, }, e); return 'invalid'; } } getHeaderLinkURL(link, row) { let returnLink = link.baseUrl; if (link.params && link.params.length > 0) { link.params.forEach((param) => { returnLink = returnLink.replace(param.key, row[ param.val ]); }); } return returnLink; } updateGetFooterAddRow(header) { if (header.selectedOptionRowHeader) return null; switch (header.formtype) { case 'select': return (<rb.Select {...header.footerFormElementPassProps} value={this.state.newRowData[ header.sortid ] || header.defaultValue} onChange={(event) => { let text = event.target.value; let name = header.sortid; this.updateNewRowText({ name, text, }); }}> {header.formoptions.map((opt, k) => { return <option key={k} disabled={opt.disabled} value={opt.value}>{opt.label || opt.value}</option>; })} </rb.Select>); // break; case 'textarea': return (<rb.Textarea {...header.footerFormElementPassProps} value={this.state.newRowData[ header.sortid ] || ''} onChange={(event) => { let text = event.target.value; let name = header.sortid; this.updateNewRowText({ name, text, }); }}> </rb.Textarea>); // break; case 'code': var CodeMirrorProps = Object.assign({}, { codeMirrorProps: { lineNumbers: true, value: this.state.newRowData[ header.sortid ] || '', //formElement.value || this.state[ formElement.name ] || getPropertyAttribute({ element:formElement, property:this.state, }); //value: this.state[ formElement.name ] || formElement.value, style: { minHeight:200, }, lineWrapping:true, onChange: function (text){ // console.log({ newvalue }); let name = header.sortid; this.updateNewRowText({ name, text, }); }.bind(this), }, }, header.CodeMirrorProps); var codeProps = Object.assign({ wrapperProps: { style: { overflow: 'auto', backgroundColor: 'white', border: '1px solid #d3d6db', borderRadius: 3, height: 'auto', boxShadow: 'inset 0 1px 2px rgba(17,17,17,.1)', }, }, }, header.codeProps); return (<RACodeMirror {...CodeMirrorProps} {...codeProps} />); case 'text': default: return (<rb.Input {...header.footerFormElementPassProps} value={this.state.newRowData[ header.sortid ] || ''} onChange={(event) => { let text = event.target.value; let name = header.sortid; this.updateNewRowText({ name, text, }); }}> </rb.Input>); // break; } } toggleAdvancedSearchFilters() { this.setState({ showFilterSearch: !this.state.showFilterSearch, }); // showFilterSearch:false, // usingFiltersInSearch: false, // showFilterSearch: props.showFilterSearch, // usingFiltersInSearch: props.usingFiltersInSearch } render() { // console.debug('render this.state', this.state); let calcStartIndex = ((this.state.currentPage - 1) * this.state.limit); let startIndex = (!this.props.baseUrl) ? calcStartIndex :0 ; let endIndex = (!this.props.baseUrl) ? ((this.state.limit * this.state.currentPage)) : this.state.limit; let displayRows = this.state.rows.slice(startIndex, endIndex); const { numPages, currentPage, } = this.state; const pageButtons = []; const lastIndex = numPages - 1; let start = currentPage - 2; let end = currentPage; if (start < 0) { end += -start; start = 0; } if (end > lastIndex) { if (start > 0) { start -= (end - lastIndex); if (start < 0) { start = 0; } } end = lastIndex; } if (start > 0) { pageButtons.push(( <li key={0}> <rb.PageButton isActive={currentPage === 1} onClick={()=>this.updateTableData({ pagenum: 1, })} >1 </rb.PageButton> </li> )); pageButtons.push(<li key="dot-before">...</li>); } for (let index = start; index <= end; index += 1) { const inActive = ((index + 1) !== currentPage); if (inActive) { pageButtons.push(( <li key={index}> <rb.PageButton onClick={()=>this.updateTableData({ pagenum: (index + 1), })} >{index + 1}</rb.PageButton> </li> )); } else { pageButtons.push(( <li key={index}> <rb.PageButton color="isPrimary" isActive onClick={() => this.updateTableData({ pagenum: (index + 1), })}> {index + 1} </rb.PageButton> </li> )); } } if (end < lastIndex) { pageButtons.push(<li key="dot-after">...</li>); pageButtons.push(( <li key={lastIndex}> <rb.PageButton onClick={()=>this.updateTableData({ pagenum: (lastIndex + 1), })}> {lastIndex + 1} </rb.PageButton> </li> )); } const footer = ( <rb.Pagination> {(this.state.currentPage < 2) ? (<rb.Button state="isDisabled"> Previous </rb.Button>) : (<rb.PageButton onClick={()=>this.updateTableData({ pagenum: (this.state.currentPage - 1), })}>Previous</rb.PageButton>)} <ul> {pageButtons} </ul> {(this.state.currentPage >= this.state.numPages) ? (<rb.Button state="isDisabled"> Next </rb.Button>) : (<rb.PageButton onClick={()=>this.updateTableData({ pagenum: (this.state.currentPage+1), })}>Next</rb.PageButton>)} </rb.Pagination>); var fbts= <a/>; if(this.props.filterSearch){ fbts = <rb.Button style={(this.state.showFilterSearch) ? { background: '#69707a', color: '#f5f7fa', borderColor:'transparent', } : (this.state.filterRowData.length > 0) ? { background: '#222324', color: 'white', borderColor:'transparent', } : undefined} {...this.props.filterButtonProps} onClick={() => { this.toggleAdvancedSearchFilters(); }} >Advanced</rb.Button>; } return ( <rb.Container {...this.props.containerProps}> {(this.props.tableSearch) ? (<rb.Addons {...this.props.filterAddonProps} > <rb.Input {...this.props.filterSearchProps} onChange={(data) => { this.searchFunction({ search: data.target.value, }); this.searchInputTextVal = data.target.value; //TODO: this is janky fix it }} ref={(input) => { this.searchTextInput = input; }} /> <rb.Button {...this.props.searchButtonProps} onClick={() => { this.searchFunction({ search: this.searchInputTextVal, }); }} >Search</rb.Button> {fbts} </rb.Addons>) : null} {(this.state.showFilterSearch) ? <div className="__ra_rt_asf" {...this.props.searchFilterContainerProps}> <rb.Message header="Advanced Search Filters" > <rb.Table {...this.props.searchFilterTableProps}> <rb.Thead> <rb.Tr> <rb.Th >Property</rb.Th> <rb.Th>Filter</rb.Th> <rb.Th>Value</rb.Th> <rb.Th>Options</rb.Th> </rb.Tr> </rb.Thead> <rb.Tbody> {(this.state.filterRowData && this.state.filterRowData.length) ? this.state.filterRowData.map((filterRowDatum, l) => { return <rb.Tr key={l}> <rb.Td>{filterRowDatum.property}</rb.Td> <rb.Td>{filterQuerySelectOptionsMap[filterRowDatum.filter_value]}</rb.Td> <rb.Td>{filterRowDatum.value}</rb.Td> <rb.Td><rb.Button onClick={() => { this.removeFilterRow(l); }}>{'⤫'}</rb.Button></rb.Td> </rb.Tr>; }) : null} </rb.Tbody> <rb.Tfoot> <rb.Tr> <rb.Th> <rb.Select value={this.state.filterRowNewData.property || '__property__'} onChange={(event) => { let text = event.target.value; let name = 'property'; this.updateNewFilterRowText({ name, text, }); }} >{this.filterSelectOptions.map((filter, fp) => { return <option value={filter.value} key={fp} disabled={filter.disabled}>{filter.label}</option>; })}</rb.Select> </rb.Th> <rb.Th> <rb.Select value={this.state.filterRowNewData.filter_value || '__filter__'} onChange={(event) => { let text = event.target.value; let name = 'filter_value'; this.updateNewFilterRowText({ name, text, }); }} >{filterQuerySelectOptions.map((filter, ft) => { return <option value={filter.value} key={ft} disabled={filter.disabled}>{filter.label}</option>; })} </rb.Select> </rb.Th> <rb.Th> <rb.Input value={this.state.filterRowNewData.value || ''} onChange={(event) => { let text = event.target.value; let name = 'value'; this.updateNewFilterRowText({ name, text, }); }} /> </rb.Th> <rb.Th> <rb.Button style={{ width:'100%', }} onClick={() => { this.addFilterRow(); }}>Add filter</rb.Button> </rb.Th> </rb.Tr> </rb.Tfoot> </rb.Table> <rb.Content {...this.props.searchFilterTableNoteProps}> <p><strong>Notes:</strong></p> <ul> <li><strong>Date Values:</strong> For date filters, Moment is used for date filters with the following moment format: YYYY-MM-DDTHH:MM:SS</li> <li><strong>Boolean values</strong> "true" is converted to <em>true</em></li> </ul> <p><strong>Export:</strong></p> <rb.Button icon="fa fa-download" onClick={() => { this.props.fileSaver({ data: this.state.rows, filename: window.location.pathname.replace(/\//gi,'_')+'.json', }); }}>JSON</rb.Button> <rb.Button icon="fa fa-download" onClick={() => { // console.debug('this.state.rows', this.state.rows); json2csv(this.state.rows, (err, csv) => { // console.debug('before csv',csv ); this.props.fileSaver({ data: csv, type:'text/csv;charset=utf-8', filename: window.location.pathname.replace(/\//gi,'_')+'.csv', }); }, { checkSchemaDifferences: false, delimiter: { wrap:'"', }, }); }}>CSV</rb.Button> <rb.Button icon="fa fa-download" onClick={() => { // console.debug('this.state.rows', this.state.rows); let headers = []; this.state.headers.forEach(header => { if (header.sortid) headers.push(header.sortid); }); let filtered_rows = this.state.rows.map(row => { let copy = Object.assign({}, row); Object.keys(copy).forEach(key => { if (headers.indexOf(key) === -1) { delete copy[ key ]; } }); return copy; }); // console.log({ filtered_rows }); json2csv(filtered_rows, (err, csv) => { // console.debug('before csv',csv ); this.props.fileSaver({ data: csv, type:'text/csv;charset=utf-8', filename: window.location.pathname.replace(/\//gi,'_')+'.csv', }); }, { checkSchemaDifferences: false, delimiter: { wrap:'"', }, }); }}>Simple CSV</rb.Button> <hr/> </rb.Content> <rb.Table {...this.props.searchFilterPaginationProps}> <rb.Tbody> <rb.Tr> <rb.Td> <rb.Group> <rb.Label style={filterLabelStyleProps}> Sort by </rb.Label> <rb.Select value={this.state.sortProp || 'createdat'} onChange={(event) => { let text = event.target.value; this.setState({ sortProp: text, }, () => { this.updateTableData({}); }); }} >{this.sortableSelctOptions.map((filter, fp) => { return <option value={filter.value} key={fp} disabled={filter.disabled}>{filter.label}</option>; })} </rb.Select> <rb.Select value={this.state.sortOrder || 'desc'} onChange={(event) => { let text = event.target.value; this.setState({ sortOrder: text, }, () => { this.updateTableData({}); }); }} > <option value="asc">ASC</option> <option value="desc">DESC</option> </rb.Select> </rb.Group> </rb.Td> <rb.Td> <rb.Group> <rb.Label style={filterLabelStyleProps}> Showing </rb.Label> <rb.Select value={this.state.limit} onChange={(event) => { let text = event.target.value; this.setState({ limit: text, }, () => { this.updateTableData({}); }); }} >{( (this.props.includeAllLimits ) ? this.props.numOfLimits.concat([ this.state.numItems, ]) : this.props.numOfLimits).map((lim, lp) => { return <option value={lim} key={lp} disabled={lim.disabled}>{lim}</option>; })} </rb.Select> <rb.Label style={filterLabelStyleProps}> of {this.state.numItems} rows </rb.Label> </rb.Group> </rb.Td> <rb.Td> <rb.Group> <rb.Label style={filterLabelStyleProps}> Page </rb.Label> <rb.Select value={this.state.currentPage} onChange={(event) => { let text = event.target.value; this.searchFunction({ pagenum: text, }); }} >{([ this.state.numPages, ].reduce((result, key) => { let usableLimit = (key < 500) ? key : 500; for (let i = 1; i <= usableLimit; i++){ result.push(i); } return result; }, [])).map((lim, lp) => { return <option value={lim} key={lp} disabled={lim===this.state.currentPage}>{lim}</option>; })} </rb.Select> <rb.Label style={filterLabelStyleProps}> of {this.state.numPages} </rb.Label> </rb.Group> </rb.Td> </rb.Tr> </rb.Tbody> </rb.Table> </rb.Message> </div> : null} <div style={{ overflow:'hidden', height:'100%', }}> {(this.state.isLoading) ? (<div style={{ textAlign: 'center', position: 'absolute', height: '80%', width: '100%', opacity: '.9', background: 'white', display: 'flex', alignSelf: 'stretch', justifyContent: 'center', alignItems: 'center', }}> <rb.Button color="isWhite" state="isLoading">Loading</rb.Button> </div>) : null } <rb.Table {...this.props.tableProps}> <rb.Thead className="__ra_rt_thead"> <rb.Tr> {this.state.headers.map((header, idx) => ( <rb.Th key={idx} style={{ cursor: 'pointer', }} {...header.headerColumnProps}>{(header.sortable) ? (<a style={{ cursor: 'pointer', }} {...this.props.headerLinkProps} onClick={() => { this.updateTableData({ sort: header.sortid, }); }}>{header.label}</a>) : header.label }</rb.Th> ))} </rb.Tr> </rb.Thead> {(this.props.tableForm && this.props.addNewRows) ? (<rb.Tfoot> <rb.Tr> {this.state.headers.map((header, idx) => ( <rb.Th key={idx} {...header.headerColumnProps}> {(idx === this.state.headers.length - 1) ? (<span className="__ra_rt_tf" style={{ display: 'flex', }} {...this.props.tableFormButtonWrapperProps}> {(this.props.replaceButton) ? <FileReaderInput as="text" onChange={this.handleFileUpload.call(this, 'replace')}> <rb.Button {...this.props.replaceButtonProps}>{this.props.replaceButtonLabel||'Replace'}</rb.Button> </FileReaderInput> : null} {(this.props.uploadAddButton) ? <FileReaderInput as="text" onChange={this.handleFileUpload.call(this, 'add')}> <rb.Button {...this.props.uploadAddButtonProps}>{this.props.uploadAddButtonLabel||'Upload'}</rb.Button> </FileReaderInput> :null} <rb.Button {...this.props.tableFormAddButtonProps} style={{ width: '100%', }} onClick={() => { this.updateByAddRow(); }}> {(this.props.formRowAddButtonLabel) ? this.props.formRowAddButtonLabel : 'Add'} </rb.Button> </span> ) : this.updateGetFooterAddRow(header)} </rb.Th> ))} </rb.Tr> </rb.Tfoot>) :null} <rb.Tbody> {displayRows.map((row, rowIndex) => ( <rb.Tr key={`row${rowIndex}`} className={(this.props.selectEntireRow && rowIndex === this.state.selectedRowIndex)?'__selected':undefined} > {this.state.headers.map((header, colIndex) => { // console.debug({header}); if (header.link) { return ( <rb.Td key={`row${rowIndex}col${colIndex}`} {...header.columnProps}> <Link {...header.linkProps} to={this.getHeaderLinkURL(header.link, row)}>{ this.formatValue( (typeof row[ header.sortid ] !=='undefined') ? row[ header.sortid ] : header.value, row, { idx: rowIndex+calcStartIndex, momentFormat: header.momentFormat, numeralFormat: header.numeralFormat, image: header.image, imageProps: header.imageProps, icon: header.icon, iconProps: header.iconProps, }) }</Link> </rb.Td> ); } else if (header.formRowButtons) { // console.debug({ row, header, }); //http://htmlarrows.com/arrows/ return ( <rb.Td key={`row${rowIndex}col${colIndex}`} style={{ textAlign:'right', }} {...header.columnProps}> {(rowIndex !== 0) ? <rb.Button {...this.props.formRowUpButton} onClick={() => { this.moveRowUp(rowIndex); }}>{(this.props.formRowUputtonLabel)?this.props.formRowUputtonLabel:'⇧'}</rb.Button> : null } {(rowIndex < this.state.rows.length - 1) ? <rb.Button {...this.props.formRowDownButton} onClick={() => { this.moveRowDown(rowIndex); }}>{(this.props.formRowDownButtonLabel)?this.props.formRowDownButtonLabel:'⇩'}</rb.Button> : null } <rb.Button {...this.props.formRowDeleteButton} onClick={() => { this.deleteRow(rowIndex); }}>{(this.props.formRowDeleteButtonLabel)?this.props.formRowDeleteButtonLabel:'⤫'}</rb.Button> </rb.Td> ); } else if (header.buttons && header.buttons.length) { // console.debug({ row, header, }); return ( <rb.Td key={`row${rowIndex}col${colIndex}`} {...header.columnProps}> { header.buttons.map(button => { return this.getRenderedComponent(Object.assign({ component: 'ResponsiveButton', props: Object.assign({ onclickPropObject: row, buttonProps: {}, }, button.passProps), children: this.formatValue( (typeof row[ header.sortid ] !=='undefined') ? row[ header.sortid ] : header.value, row, { idx: rowIndex+calcStartIndex, momentFormat: header.momentFormat, image: header.image, imageProps: header.imageProps, icon: header.icon, iconProps: header.iconProps, }) || '', }, button)); }) // Object.assign } </rb.Td> ); } else { return ( <rb.Td key={`row${rowIndex}col${colIndex}`} {...header.columnProps}