Created
June 15, 2020 17:31
-
-
Save andrconstruction/0c7fc2acc0df9e528f5ab755b68f2a43 to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import React from 'react' | |
import {Button, Card, Modal, OverlayTrigger, Tooltip} from 'react-bootstrap' | |
import {usePagination, useRowSelect, useSortBy, useTable} from 'react-table' | |
import UserService from "../service/userService"; | |
import Loader from "../../shared/Loader"; | |
import PerfectScrollbar from "perfect-scrollbar"; | |
import moment from "moment"; | |
import IndeterminateCheckbox from './IndeterminateCheckbox' | |
import {FontAwesomeIcon} from "@fortawesome/react-fontawesome"; | |
function TableComponent( { | |
data, | |
fetchData, | |
loading, | |
getRowProps, | |
removeButton, | |
editButton, | |
revertButton, | |
withinCard, | |
} ) { | |
const columns = React.useMemo( () => [ | |
{ | |
Header : 'ID', | |
accessor : 'id', | |
canSort : true | |
}, | |
{ | |
Header : 'Логин', | |
accessor : 'username', | |
canSort : true | |
}, | |
{ | |
Header : 'Полное имя', | |
accessor : 'displayName' | |
}, | |
{ | |
Header : 'Email', | |
accessor : 'email' | |
}, | |
{ | |
id : 'created', | |
Header : 'Создано', | |
accessor : d => { | |
return moment( d.created.date ) | |
.local() | |
.format( "DD.MM.YY hh:mm" ) | |
} | |
}, | |
{ | |
id : 'updated', | |
Header : 'Обновлено', | |
accessor : d => { | |
return moment( d.updated.date ) | |
.local() | |
.format( "DD.MM.YY hh:mm" ) | |
} | |
}, | |
], [] ) | |
const { | |
getTableProps, | |
getTableBodyProps, | |
headerGroups, | |
prepareRow, | |
page, | |
canPreviousPage, | |
canNextPage, | |
pageOptions, | |
gotoPage, | |
nextPage, | |
previousPage, | |
setPageSize, | |
// Get the state from the instance | |
state : {pageIndex, pageSize, sortBy, selectedRowIds}, | |
} = useTable( { | |
columns, | |
getRowProps, | |
removeButton, | |
editButton, | |
revertButton, | |
data : data._embedded.user, | |
initialState : { | |
pageSize : 100, | |
// autoResetPage : false, | |
pageIndex : data.page, | |
sortBy : data.sortBy, | |
}, // Pass our hoisted table state | |
manualPagination : true, // Tell the usePagination, | |
manualSortBy : true, | |
// hook that we'll handle our own data fetching | |
// This means we'll also have to provide our own | |
// pageCount. | |
pageCount : data.page_count + 1, | |
autoResetPage : false, | |
autoResetSortBy : false, | |
}, | |
// useResizeColumns, | |
// useFlexLayout, | |
useSortBy, | |
usePagination, | |
useRowSelect, | |
hooks => { | |
hooks.visibleColumns.push( columns => { | |
return [ | |
{ | |
id : 'selection', | |
// Make this column a groupByBoundary. This ensures that groupBy columns | |
// are placed after it | |
groupByBoundary : true, | |
// disableResizing: true, | |
// minWidth: 20, | |
// width: 20, | |
// maxWidth: 20, | |
// The header can use the table's getToggleAllRowsSelectedProps method | |
// to render a checkbox | |
Header : ( {getToggleAllRowsSelectedProps} ) => ( | |
<div> | |
<IndeterminateCheckbox {...getToggleAllRowsSelectedProps()} /> | |
</div> | |
), | |
// The cell can use the individual row's getToggleRowSelectedProps method | |
// to the render a checkbox | |
Cell : ( {row} ) => ( | |
<div> | |
<IndeterminateCheckbox {...row.getToggleRowSelectedProps()} /> | |
</div> | |
), | |
}, | |
...columns, | |
{ | |
id : 'edit', | |
Header : ( {getToggleAllRowsSelectedProps} ) => ( | |
<> | |
</> | |
), | |
Cell : ( {row} ) => ( | |
<div> | |
<OverlayTrigger | |
placement="left" | |
overlay={<Tooltip className="tooltip-primary">Редактировать ( или дважды щёлкниет на | |
строке )</Tooltip>}> | |
<Button onClick={() => editButton( row )} size="xs" | |
variant="outline-primary icon-btn"><FontAwesomeIcon | |
icon={['fal', 'pen']} size="sm"/></Button> | |
</OverlayTrigger> | |
{row.original.removed | |
? <OverlayTrigger | |
placement="left" | |
overlay={<Tooltip className="tooltip-warning">Восстановить объект</Tooltip>}> | |
<Button onClick={() => { | |
revertButton( row ) | |
}} size="xs" | |
variant="outline-warning icon-btn"><FontAwesomeIcon | |
icon={['fal', 'trash-undo-alt']} size="sm"/></Button> | |
</OverlayTrigger> | |
: | |
<OverlayTrigger | |
placement="left" | |
overlay={<Tooltip className="tooltip-danger">Удалить объект</Tooltip>}> | |
<Button onClick={() => { | |
console.log( 'Rmoved clicked' ) | |
// console.log( removedButton ) | |
{ | |
removeButton( row ) | |
} | |
} | |
} size="xs" | |
variant="outline-danger icon-btn"><FontAwesomeIcon | |
icon={['fal', 'times']} size="sm"/></Button> | |
</OverlayTrigger> | |
} | |
</div> | |
), | |
}, | |
] | |
} ) | |
// hooks.useInstanceBeforeDimensions.push(({ headerGroups }) => { | |
// // fix the parent group of the selection button to not be resizable | |
// const selectionGroupHeader = headerGroups[0].headers[0] | |
// selectionGroupHeader.canResize = true | |
// }) | |
} | |
) | |
React.useEffect( () => { | |
fetchData( {pageIndex, pageSize, sortBy} ) | |
const ps = new PerfectScrollbar( "#table-body" ); | |
}, [fetchData, pageIndex, pageSize, sortBy] ) | |
const divStyle = {height : 'calc(100vh - 276px)', overflowY : 'scroll'} | |
return ( | |
<div style={divStyle}> | |
<table | |
id={'table-react'} | |
style={divStyle} | |
className={`table table-striped table-sm table-hover ${withinCard ? 'card-table' : ''}`} {...getTableProps()}> | |
<thead> | |
{headerGroups.map( headerGroup => ( | |
<tr {...headerGroup.getHeaderGroupProps()}> | |
{headerGroup.headers.map( column => ( | |
<th {...column.getHeaderProps( column.getSortByToggleProps() )}> | |
{column.render( 'Header' )} | |
{column.isSorted | |
? column.isSortedDesc | |
? <i className="ion ion-ios-arrow-down ml-2"/> | |
: <i className="ion ion-ios-arrow-up ml-2"/> | |
: ''} | |
</th> | |
) )} | |
</tr> | |
) )} | |
</thead> | |
<tbody id={'table-body'} {...getTableBodyProps()}> | |
{page.map( ( row, i ) => { | |
prepareRow( row ) | |
return ( | |
<tr {...row.getRowProps( getRowProps( row ) )}> | |
{row.cells.map( cell => { | |
return <td {...cell.getCellProps()}>{cell.render( 'Cell' )}</td> | |
} )} | |
</tr> | |
) | |
} )} | |
<tr> | |
{loading ? ( | |
// Use our custom loading state to show a loading indicator | |
<td colSpan="10000">Загружаем...</td> | |
) : ( | |
<td colSpan="10000"> | |
Показано c | |
( {pageIndex * pageSize - pageSize + 1} по {pageIndex * pageSize - pageSize + page.length} ) | |
из {data.total_items}{' '} | |
результатов | |
</td> | |
)} | |
</tr> | |
</tbody> | |
</table> | |
<hr/> | |
<div className={`d-flex align-items-center flex-wrap ${withinCard ? 'px-4 pb-4' : ''}`}> | |
<div className="mr-2"> | |
Страница{' '} | |
<strong> | |
{pageIndex} из {pageOptions.length - 1} | |
</strong> | |
</div> | |
<select className="custom-select custom-select-sm mr-auto" style={{width : '150px'}} value={pageSize} | |
onChange={e => setPageSize( Number( e.target.value ) )}> | |
{[50, 100, 200].map( pageSize => ( | |
<option key={pageSize} value={pageSize}> | |
Показывать {pageSize} | |
</option> | |
) )} | |
</select> | |
<div className="mr-2"> | |
Перейти на страницу: | |
<input | |
className="form-control form-control-sm d-inline-block" | |
type="number" | |
defaultValue={pageIndex} | |
onChange={e => { | |
const page = e.target.value ? Number( e.target.value ) : 1 | |
gotoPage( page ) | |
}} | |
style={{width : '100px'}} | |
/> | |
</div> | |
<ul className="pagination mb-0"> | |
<li className="page-item"> | |
<a className={`page-link ${canPreviousPage ? '' : 'disabled'}`} href="#start" onClick={e => { | |
e.preventDefault() | |
gotoPage( 1 ) | |
}}>{'<<'}</a> | |
</li> | |
<li className="page-item"> | |
<a className={`page-link ${canPreviousPage && pageIndex > 1 ? '' : 'disabled'}`} href="#prev" | |
onClick={e => { | |
e.preventDefault() | |
if ( pageIndex === 1 ) { | |
gotoPage( 1 ) | |
} else { | |
previousPage() | |
} | |
}}>{'<'}</a> | |
</li> | |
<li className="page-item"> | |
<a className={`page-link ${canNextPage ? '' : 'disabled'}`} href="#next" onClick={e => { | |
e.preventDefault() | |
nextPage() | |
}}>{'>'}</a> | |
</li> | |
<li className="page-item"> | |
<a className={`page-link ${canNextPage ? '' : 'disabled'}`} href="#end" onClick={e => { | |
e.preventDefault() | |
gotoPage( data.page_count ) | |
}}>{'>>'}</a> | |
</li> | |
</ul> | |
</div> | |
</div> | |
) | |
} | |
function ListUsersTableComponent() { | |
const [data, setData] = React.useState( | |
{ | |
sortBy : [{id : 'id', desc : true}], | |
filter : {}, | |
page : 1, | |
page_size : 100, | |
total_items : 0, | |
page_count : 0, | |
_embedded : { | |
user : [] | |
} | |
} | |
) | |
const [loading, setLoading] = React.useState( false ) | |
const fetchIdRef = React.useRef( 0 ) | |
const [slideModalShow, setSlideModalShow] = React.useState( false ) | |
const onRowClick = async ( row ) => { | |
return { | |
onDoubleClick : e => { | |
// setSlideModalShow( true ) | |
console.log( row ) | |
} | |
} | |
} | |
const onRemoveButtonClick = async row => { | |
return { | |
onClick : e => { | |
console.log( 'REMOVE : ' + row ) | |
} | |
} | |
} | |
const onEditButtonClick = async row => { | |
return { | |
onClick : e => { | |
console.log( 'EDIT : ' + row ) | |
} | |
} | |
} | |
const onRevertButtonClick = async row => { | |
return { | |
onClick : e => { | |
console.log( 'REVERT : ' + row ) | |
} | |
} | |
} | |
const onSlideModalClose = () => { | |
setSlideModalShow( false ) | |
} | |
const fetchData = React.useCallback( ( {pageSize, pageIndex, sortBy} ) => { | |
const userService = new UserService(); | |
const fetchId = ++ fetchIdRef.current | |
setLoading( true ) | |
userService.getUsers( pageIndex, pageSize, sortBy ).then( response => { | |
if ( fetchId === fetchIdRef.current ) { | |
setData( response ) | |
setLoading( false ) | |
} | |
} ) | |
}, [] ) | |
return ( | |
<div> | |
<Card className="mb-3 mt-0"> | |
<Card.Header as="h6">Пользователи <span | |
className={'float-right label label-primary'}> {data.total_items} </span> | |
<Button onClick={() => setSlideModalShow( true ) } variant="outline-primary" size="xs"><FontAwesomeIcon icon={['fal', 'plus']} size="sm"/> Добавить</Button> | |
</Card.Header> | |
<Button variant="default" onClick={() => | |
setSlideModalShow( true ) | |
}>Show</Button> | |
{/* Modal template */} | |
<Modal className="modal-slide w-75" size={"xl"} show={slideModalShow} animation={true} onHide={onSlideModalClose}> | |
<button type="rounded-pill btn btn-danger btn-lg" className="close" aria-label="Close" | |
onClick={onSlideModalClose}><FontAwesomeIcon size={"lg"} color={"red"} icon={['fas', 'times']} /></button> | |
<Modal.Body> | |
<p className="text-center text-big mb-4">Before you proceed, you have to login to make the | |
necessary changes</p> | |
<Button variant="primary" block onClick={onSlideModalClose}>Continue</Button> | |
<Button variant="default" block onClick={onSlideModalClose}>Cancel</Button> | |
</Modal.Body> | |
</Modal> | |
{loading && <Loader/>} | |
<TableComponent data={data} | |
LoadingComponent={Loader} | |
fetchData={fetchData} | |
loading={loading} | |
noDataText={"Грузим..."} | |
getRowProps={onRowClick} | |
removeButton={onRemoveButtonClick} | |
editButton={onEditButtonClick} | |
revertButton={onRevertButtonClick} | |
withinCard={true}/> | |
</Card> | |
</div> | |
) | |
} | |
export default ListUsersTableComponent |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment