Skip to content

Instantly share code, notes, and snippets.

@Tushant
Last active August 17, 2017 05:54
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save Tushant/4f9a05e79b8eb9d9d528385cd1c54efd to your computer and use it in GitHub Desktop.
Save Tushant/4f9a05e79b8eb9d9d528385cd1c54efd to your computer and use it in GitHub Desktop.
class Pagination extends React.Component {
static defaultProps = {
initialPage: 1
};
constructor(props) {
super(props);
this.state = { pager: {}, totalItems: this.props.totalItems };
}
componentDidMount() {
// set page if items array isn't empty
if (this.props.items) {
this.setPage(this.props.initialPage);
}
}
componentWillReceiveProps(nextProps, nextState) {
if (nextProps.items !== this.props.items) {
console.log("pager");
this.setState({ totalItems: nextProps.totalItems }, () =>
this.setPage(this.props.initialPage)
);
}
if (nextProps.totalItems !== this.props.totalItems) {
this.setState({ totalItems: nextProps.totalItems }, () =>
this.setPage(this.props.initialPage)
);
}
}
setPage(page) {
let items = this.props.items;
let pager = this.state.pager;
if (page < 1 || page > pager.totalPages) {
return;
}
// get new pager object for specified page
pager = this.getPager(this.state.totalItems, page);
// update state
this.setState({ pager: pager }, () =>
this.props.onChangePage(pager.currentPage, pager.pageSize)
);
}
getPager(totalItems, currentPage, pageSize) {
// default to first page
currentPage = currentPage || 1;
// default page size is 10
pageSize = pageSize || 10;
// calculate total pages
const totalPages = Math.ceil(totalItems / pageSize);
let startPage, endPage;
if (totalPages <= 10) {
// less than 10 total pages so show all
startPage = 1;
endPage = totalPages;
} else {
// more than 10 total pages so calculate start and end pages
if (currentPage <= 6) {
startPage = 1;
endPage = 10;
} else if (currentPage + 4 >= totalPages) {
startPage = totalPages - 9;
endPage = totalPages;
} else {
startPage = currentPage - 5;
endPage = currentPage + 4;
}
}
// calculate start and end item indexes
let startIndex = (currentPage - 1) * pageSize;
let endIndex = Math.min(startIndex + pageSize - 1, totalItems - 1);
let pages = range(startPage, endPage + 1);
// return object with all pager properties required by the view
return {
totalItems: totalItems,
currentPage: currentPage,
pageSize: pageSize,
totalPages: totalPages,
startPage: startPage,
endPage: endPage,
startIndex: startIndex,
endIndex: endIndex,
pages: pages
};
}
render() {
const { pager } = this.state;
return (
<ul style={{ display: "inherit" }}>
<li className={pager.currentPage === 1 ? "disable" : ""}>
<a className="icon item" onClick={() => this.setPage(1)}>
<i className="icon-skip_previous" />
</a>
</li>
<li className={pager.currentPage === 1 ? "disable" : ""}>
<a
className="icon item"
onClick={() => this.setPage(pager.currentPage - 1)}
>
<i className="icon-keyboard_arrow_left" />
</a>
</li>
{
pager.pages.map((page, index) =>
<li
key={index}
className={pager.currentPage === page ? "active-page" : ""}
>
<a className="item" onClick={() => this.setPage(page)}>
{page}
</a>
</li>
)}
<li className={pager.currentPage === pager.totalPages ? "disable" : ""}>
<a
className="icon item"
onClick={() => this.setPage(pager.currentPage + 1)}
>
<i className="icon-keyboard_arrow_right" />
</a>
</li>
<li className={pager.currentPage === pager.totalPages ? "disable" : ""}>
<a
className="icon item"
onClick={() => this.setPage(pager.totalPages)}
>
<i className="icon-skip_next" />
</a>
</li>
</ul>
);
}
}
export default Pagination;
import React from "react";
import Link from "react-router-dom/Link";
import { Label, Menu, Table } from "semantic-ui-react";
import { capitalize } from "utils/helper";
import { isEmpty } from "utils/helper";
import Pagination from "./Pagination";
const perPageToShow = 10;
const step = 3;
const style = {
paginationBtn: {
padding: 0,
border: 0,
borderRadius: 0,
margin: 0,
background: "rgba(26, 179, 148, 0.8)"
},
btnText: {
color: "#fff"
}
};
function evalValue(value, header) {
if (value === true) {
return <i className="icon-check" />;
}
if (value === false) {
return <i className="icon-close" />;
}
if (Array.isArray(value) && value.length > 1) {
return capitalize(value.join(", "));
}
if (typeof value === "string" && value.indexOf("@") > -1) {
return String(value);
}
if (header.type === "icon") {
return header.icon.map(ico => {
if (ico.name.toLowerCase() === value.toLowerCase()) {
return (
<div className="has-icon" key={ico.name}>
<i className={ico.icon} />
{capitalize(String(ico.name))}
</div>
);
}
});
}
return capitalize(String(value));
}
function evalActions(data, action, action_id, onDelete, showModal) {
if (action.type === "view") {
let url;
if (action_id) {
url =
action.action &&
(data[action_id]
? action.action + "/" + data[action_id]
: action.action);
} else if (!action_id) {
url =
action.action &&
(data._id ? action.action + "/" + data._id : action.action);
}
return (
<Link to={url}>
<i className={action.icon} />
</Link>
);
}
if (action.type === "modal") {
return <i className={action.icon} onClick={() => showModal(data._id)} />;
}
if (action.type === "delete") {
// event is used
return <i className={action.icon} onClick={() => onDelete(data._id)} />;
}
}
// here field is for first-level key of object, and fieldName is for nested key
// eg: { field: { fieldName: ''}}
// for simple case { field: ''}
function cellToShow(data, headers, actions, action_id, onDelete, showModal) {
if (data && !isEmpty(data)) {
return data.map(datum => {
return (
<Table.Row key={datum._id}>
{headers.map(header => {
return (
<Table.Cell key={header.key}>
{evalValue(
header.fieldName
? datum[header.field][header.fieldName]
: datum[header.field],
header
)}
</Table.Cell>
);
})}
{actions &&
actions.map(action =>
<Table.Cell key={action.key} className="action">
{evalActions(datum, action, action_id, onDelete, showModal)}
</Table.Cell>
)}
</Table.Row>
);
});
}
}
function headerToShow(headers) {
return headers.map(header =>
<Table.HeaderCell key={header.key}>
{header.name}
</Table.HeaderCell>
);
}
class CustomTable extends React.PureComponent {
constructor(props) {
super(props);
this.state = {
pageOfItems: [],
items: this.props.data
};
}
onChangePage = (currentPage, pageSize) => {
this.props.onPaginate(currentPage, pageSize);
};
render() {
const {
headers,
data,
actions,
pagination,
onDelete,
onPaginate,
action_id,
showModal
} = this.props;
const colSpanLength = headers.length + 1;
console.log("pagination totalitems is", pagination.get("totalItems"));
console.log("######################");
return (
<Table celled stackable sortable>
<Table.Header>
<Table.Row>
{headerToShow(headers)}
<Table.HeaderCell colSpan={actions && actions.length}>
Actions
</Table.HeaderCell>
</Table.Row>
</Table.Header>
{data && (data.length > 0 || data.size > 0)
? <Table.Body>
{cellToShow(
data,
headers,
actions,
action_id,
onDelete,
showModal
)}
</Table.Body>
: <Table.Body>
<Table.Row>
<Table.Cell>No data</Table.Cell>
</Table.Row>
</Table.Body>}
{data &&
(data.length > 0 || data.size > 0) &&
pagination &&
<Table.Footer>
<Table.Row>
<Table.HeaderCell colSpan={colSpanLength}>
<Menu floated="right" pagination>
{/* <Pagination
items={this.state.items && this.state.items}
onChangePage={(currentPage, pageSize) =>
this.onChangePage(currentPage, pageSize)}
totalItems={
pagination.get("totalItems") || pagination.totalItems
}
initialPage={
this.props.initialPage ? this.props.initialPage : 1
}
/> */}
</Menu>
</Table.HeaderCell>
</Table.Row>
</Table.Footer>}
</Table>
);
}
}
export default CustomTable;
import React from "react";
import Toaster from "components/Toaster";
import { createStructuredSelector } from "reselect";
import { connect } from "react-redux";
import debounce from "lodash/debounce";
import { showDialog } from "containers/App/actions";
import { makeSelectDialog } from "containers/App/selectors";
import { loadUsers, deleteUser } from "./actions";
import {
makeSelectUsers,
makeSelectSuccessResponse,
makeSelectErrorResponse,
makeSelectPagination,
makeSelectLoading
} from "./selectors";
import DeleteConfirmation from "components/DeleteConfirmation";
import UsersTable from "./UsersTable";
const perPageToShow = 10;
const mapDispatchToProps = dispatch => ({
fetchUser: (name, page, perPage) => dispatch(loadUsers(name, page, perPage)),
deleteUser: userId => dispatch(deleteUser(userId)),
showDialog: dialog => dispatch(showDialog(dialog)),
hideDialog: () => dispatch(showDialog(null))
});
const mapStateToProps = createStructuredSelector({
users: makeSelectUsers(),
paginationParams: makeSelectPagination(),
successResponse: makeSelectSuccessResponse(),
errorResponse: makeSelectErrorResponse(),
isRequesting: makeSelectLoading(),
dialog: makeSelectDialog()
});
class UserList extends React.PureComponent {
constructor(props) {
super(props);
this.state = { show: false, item: "", page: 1, usersData: {} };
}
componentDidMount() {
this.props.fetchUser("", 1, perPageToShow);
}
componentWillReceiveProps(nextProps) {
console.log("paginationParams", nextProps.paginationParams);
if (nextProps.paginationParams !== this.props.paginationParams) {
this.setState({ pagination: nextProps.paginationParams });
}
}
componentWillMount() {
const { totalItems, currentPage } = this.props.paginationParams;
this.delayedSearch = debounce(item => {
this.props.fetchUser(this.state.item, this.state.page, totalItems);
}, 1000);
}
deleteRow(cell) {
this.setState({ show: true });
if (cell) {
const userDeleteConfirmation = (
<DeleteConfirmation
hideDialog={this.props.hideDialog}
deleteKey={cell}
text="user"
onDelete={this.props.deleteUser}
/>
);
this.props.showDialog(userDeleteConfirmation);
}
}
handlePagination(page, pageSize) {
this.setState({ page });
this.props.fetchUser(this.state.item || "", page, pageSize);
}
doSearch(item) {
this.setState({ item }, () => this.delayedSearch(this.state.item));
}
render() {
const {
users,
successResponse,
errorResponse,
paginationParams
} = this.props;
let message;
if (successResponse && typeof successResponse === "string") {
message = <Toaster message={successResponse} timeout={1000} success />;
}
if (errorResponse && typeof errorResponse === "string") {
message = <Toaster message={errorResponse} timeout={1000} error />;
}
const headers = [
{ name: "First Name", field: "first_name", sorted: true, key: 1 },
{ name: "Email", field: "email", key: 2 },
{ name: "Role", field: "user_role", key: 3 },
{ name: "Email Verified", field: "confirmed", sorted: true, key: 4 }
];
const actions = [
{
key: 1,
name: "view",
type: "view",
icon: "icon icon-eye",
action: "/admin/dashboard/user"
},
{
key: 2,
name: "delete",
type: "delete",
icon: "icon icon-trash"
}
];
return (
<div className="left container">
{message && message}
{this.state.show ? this.props.dialog : null}
<h1>Users</h1>
<UsersTable
searchItems={item => this.doSearch(item)}
headers={headers}
data={users && users.toJS()}
actions={actions}
onDelete={cell => this.deleteRow(cell)}
pagination={paginationParams && paginationParams}
onPaginate={(page, pageSize) => this.handlePagination(page, pageSize)}
isRequesting={this.props.isRequesting}
/>
</div>
);
}
}
export default connect(mapStateToProps, mapDispatchToProps)(UserList);
import React from "react";
import CustomTable from "components/Table";
import SearchBox from "components/SearchBox";
import Loader from "components/Loader/SpecificLoader";
import { sizeOfAnObject } from "utils/helper";
const UsersTable = ({
searchItems,
headers,
data,
actions,
onDelete,
pagination,
onPaginate,
isRequesting,
...props
}) => {
if (sizeOfAnObject(data) <= 0 || data.length <= 0) {
return <Loader />;
}
return (
<div>
<SearchBox searchItems={searchItems} />
<CustomTable
headers={headers}
data={data && data}
actions={actions}
onDelete={onDelete}
pagination={pagination}
onPaginate={onPaginate}
/>
</div>
);
};
export default Loader('isRequesting')(UsersTable);
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment