Skip to content

Instantly share code, notes, and snippets.

@vytsci
Created January 25, 2021 07:06
Show Gist options
  • Save vytsci/438802120d16f9ec68298432590a6ea6 to your computer and use it in GitHub Desktop.
Save vytsci/438802120d16f9ec68298432590a6ea6 to your computer and use it in GitHub Desktop.
import {get, forEach, size, snakeCase, unset} from "lodash";
import {t} from "functions";
import React from 'react';
import {
FlexboxGrid,
Modal,
ButtonToolbar,
ButtonGroup,
IconButton,
Icon,
Placeholder,
Button,
Loader,
Drawer,
Form,
Divider,
FormGroup,
TagGroup,
Table,
Tag
} from 'rsuite';
import moment from "moment";
import API from "modules/api";
export default class Datagrid extends React.Component {
constructor(props = {options: {}}) {
super(props);
// Stateless properties
this.resource = props.resource;
this.options = props.options;
this.toolbar = props.toolbar;
this.filter = props.filter;
this.actions = props.actions;
this.state = {
// Datagrid states
error: null,
isLoaded: false,
isLoading: false,
// Filter states
filterShow: false,
filterValue: {},
filterTags: [],
// Table states
activePage: 1,
displayLength: 30,
total: 0,
data: [],
columns: props.columns,
};
}
/**
*
*/
componentDidMount() {
this.reload();
}
/**
* Reloads data from API based on collected parameters
*
* @param activePage
* @param displayLength
*/
reload(activePage = this.state.activePage, displayLength = this.state.displayLength) {
this.setState({isLoading: true});
const params = {
"page": activePage,
"per_page": displayLength,
};
// Lets build filter params
if (size(this.state.filterValue) > 0) {
forEach(this.state.filterValue, (value, name) => {
params[`filter[${name}]`] = value;
});
}
API.get(
this.resource,
Object.assign({"params": params}, this.options),
(data, meta) => {
this.setState({
isLoaded: true,
isLoading: false,
data: data,
activePage: meta.pagination.current_page,
displayLength: meta.pagination.per_page,
total: meta.pagination.total,
});
},
(error) => {
this.setState({
isLoaded: true,
error
});
}
);
}
/**
* Pops filter up
*/
showFilter() {
this.setState({filterShow: true});
}
/**
* Closes filter
*/
closeFilter() {
this.setState({filterShow: false});
}
/**
* Handles filter changes
*
* @param filterValue
*/
handleFilterChange(filterValue) {
this.setState({filterValue: filterValue, renderFilterTags: false});
}
/**
* Handles filter submission
*/
handleFilterSubmit() {
this.setState({filterShow: false, filterShowTags: true});
this.reload();
}
/**
* Handles filter tag removal
*
* @param name
*/
handleFilterTagRemove(name) {
const {filterValue} = this.state;
if (unset(filterValue, name)) {
this.setState({filterValue: filterValue});
}
this.reload();
}
/**
* Renders filter tags
*
* @returns {[]}
*/
renderFilterTags() {
const tags = [];
if (this.state.filterShowTags && size(this.state.filterValue) > 0) {
forEach(this.state.filterValue, (value, name) => {
tags.push(
<Tag key={name} onClose={() => {this.handleFilterTagRemove(name);}} closable>
{t(`datagrid.filter.${name}.label`)}: "{value}"
</Tag>
);
});
}
return tags;
}
/**
* Handles table properties change
*
* @param activePage
* @param displayLength
*/
handleTableChange(activePage = 1, displayLength = this.props.displayLength) {
this.setState({activePage: activePage, displayLength: displayLength});
}
/**
* Renders table columns
*
* @returns {[]}
*/
renderTableColumns() {
const columns = [];
forEach(this.state.columns, (column, name) => {
columns.push(
<Table.Column key={name} fixed={true} flexGrow={1}>
<Table.HeaderCell>{t(`datagrid.column.${name}`)}</Table.HeaderCell>
<Table.Cell dataKey={name}>
{(rowData, rowIndex) => {
let cellData = get(rowData, column.value);
if (column.translate) {
cellData = t(`datagrid.cell.${column.value}.${snakeCase(cellData)}`);
}
if (moment(cellData, moment.ISO_8601, true).isValid()) {
cellData = moment(cellData).locale("lt").format("LLL");
}
if (column.color) {
cellData = <span style={{color: get(rowData, column.color)}}>{cellData}</span>;
}
return cellData;
}}
</Table.Cell>
</Table.Column>
);
});
return columns;
}
/**
* Renders whole component
*
* @returns {JSX.Element}
*/
render() {
let loader = <Loader backdrop content={t("Loading")} vertical />;
if (this.state.error) {
return <div>Error: {this.state.error.message}</div>;
}
if (!this.state.isLoaded) {
return (
<div>
<Placeholder.Grid rows={5} columns={6} active />
<Loader backdrop content={t("Loading")} vertical />
</div>
);
}
if (!this.state.isLoading) {
loader = null;
}
return (
<div>
<ButtonToolbar>
<FlexboxGrid justify="space-between">
<ButtonGroup>
{this.toolbar}
</ButtonGroup>
<ButtonGroup>
<IconButton
icon={<Icon icon="angle-left" />}
onClick={() => this.showFilter()}
>
{t("common.filter.label")}
</IconButton>
</ButtonGroup>
</FlexboxGrid>
</ButtonToolbar>
<Drawer
size="md"
placement="right"
show={this.state.filterShow}
onHide={() => this.closeFilter()}
>
<Drawer.Header>
<Drawer.Title>{t("datagrid.filter.label")}</Drawer.Title>
</Drawer.Header>
<Drawer.Body>
<div>
<Form
ref={ref => (this.filterForm = ref)}
onChange={filterValue => {this.handleFilterChange(filterValue)}}
formDefaultValue={this.state.filterValue}
fluid
>
{this.filter}
<FormGroup>
<FlexboxGrid justify="end">
<FlexboxGrid.Item>
<ButtonToolbar>
<Button appearance="default" placement="right" onClick={() => this.closeFilter()}>{t("common.close.label")}</Button>
<Button appearance="primary" placement="right" onClick={() => this.handleFilterSubmit()}>{t("common.submit.label")}</Button>
</ButtonToolbar>
</FlexboxGrid.Item>
</FlexboxGrid>
</FormGroup>
</Form>
</div>
</Drawer.Body>
</Drawer>
<FlexboxGrid justify="center" style={{marginTop: 10, marginBottom: 10}}>
<FlexboxGrid.Item>
<TagGroup>
{this.renderFilterTags()}
</TagGroup>
</FlexboxGrid.Item>
</FlexboxGrid>
<Table
autoHeight={true}
data={this.state.data}
onRowClick={data => {
console.log(data);
}}
>
{this.renderTableColumns()}
<Table.Column fixed="right" flexGrow={1}>
<Table.HeaderCell>{t("datagrid.column.actions")}</Table.HeaderCell>
<Table.Cell dataKey="actions">
{rowData => {
if (typeof this.actions === "function") {
return <ButtonToolbar>{this.actions(rowData)}</ButtonToolbar>;
}
}}
</Table.Cell>
</Table.Column>
</Table>
<Table.Pagination
activePage={this.state.activePage}
displayLength={this.state.displayLength}
first={true}
last={true}
next={true}
onChangeLength={eventKey => this.handleTableChange(1, eventKey)}
onChangePage={eventKey => this.handleTableChange(eventKey)}
prev={true}
total={this.state.total}
/>
{loader}
</div>
);
}
}
import {t} from "functions";
import React from 'react';
import ReactDOM from 'react-dom';
import Datagrid from "components/restful/datagrid.jsx";
import 'rsuite/lib/styles/index.less';
import {
Col,
ControlLabel,
DatePicker,
FormControl,
FormGroup,
Grid,
Icon,
IconButton,
Row,
Tooltip,
Whisper
} from "rsuite";
function view() {
console.log("view");
}
function edit() {
console.log("edit");
}
function remove() {
console.log("remove");
}
function Root() {
return (
<Grid fluid style={{width: "97%"}}>
<Row>
<Col xs={24}>
<Datagrid
resource={"rest/tickets/tickets"}
columns={{
"id": {"value": "id"},
"subject": {"value": "subject"},
"created_at": {"value": "created_at"},
"updated_at": {"value": "updated_at"},
"category": {"value": "category.name", "color": "category.color"},
"state": {"value": "state", "color": "state_color", "translate": true},
}}
toolbar={[
<IconButton
key="add"
icon={<Icon icon="plus" />}
color={"blue"}
onClick={() => view()}
>
{t("datagrid.toolbar.add.label")}
</IconButton>
]}
filter={[
<FormGroup key="search">
<ControlLabel>{t("tickets.filter.search.label")}</ControlLabel>
<Whisper trigger="focus" speaker={<Tooltip>{t("tickets.filter.search.help")}</Tooltip>}>
<FormControl name="search"/>
</Whisper>
</FormGroup>,
<FormGroup key="category">
<ControlLabel>{t("tickets.filter.category.label")}</ControlLabel>
<Whisper trigger="focus" speaker={
<Tooltip>{t("tickets.filter.category.help")}</Tooltip>}>
<FormControl name="category" accepter={AutoComplete}/>
</Whisper>
</FormGroup>,
<FormGroup key="crated_range" style={{overflowX: "hidden"}}>
<Row gutter={8}>
<Col xs={12}>
<ControlLabel>{t("tickets.filter.created_after.label")}</ControlLabel>
<FormControl name="created_after" accepter={DatePicker} format="YYYY-MM-DD HH:mm:ss" block/>
</Col>
<Col xs={12}>
<ControlLabel>{t("tickets.filter.created_before.label")}</ControlLabel>
<FormControl name="created_before" accepter={DatePicker} format="YYYY-MM-DD HH:mm:ss" block/>
</Col>
</Row>
</FormGroup>
]}
actions={(rowData) => [
<IconButton key="view" size="xs" onClick={(rowData) => view(rowData)} icon={<Icon icon="eye" />} color="blue" circle title={t("datagrid.action.view")} />,
<IconButton key="edit" size="xs" onClick={(rowData) => edit(rowData)} icon={<Icon icon="edit" />} color="orange" circle title={t("datagrid.action.edit")} />,
<IconButton key="delete" size="xs" onClick={(rowData) => remove(rowData)} icon={<Icon icon="trash" />} color="red" circle title={t("datagrid.action.delete")} />
]}
/>
</Col>
</Row>
</Grid>
);
}
ReactDOM.render(<Root />, document.getElementById('root'));
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment