diff options
Diffstat (limited to 'front/odiparpack/app/components/Tables')
18 files changed, 1614 insertions, 0 deletions
diff --git a/front/odiparpack/app/components/Tables/AdvTable.js b/front/odiparpack/app/components/Tables/AdvTable.js new file mode 100644 index 0000000..acb2803 --- /dev/null +++ b/front/odiparpack/app/components/Tables/AdvTable.js @@ -0,0 +1,206 @@ +import React from 'react'; +import PropTypes from 'prop-types'; +import { withStyles } from '@material-ui/core/styles'; +import classNames from 'classnames'; +import tableStyles from 'ba-styles/Table.scss'; +import { Table, TableBody, TableCell, TableRow, TablePagination, Paper, Checkbox } from '@material-ui/core'; +import EnhancedTableHead from './tableParts/TableHeader'; +import EnhancedTableToolbar from './tableParts/TableToolbar'; + + +const styles = theme => ({ + root: { + width: '100%', + marginTop: theme.spacing(3), + }, + table: { + minWidth: 1020, + }, + tableWrapper: { + overflowX: 'auto', + }, +}); + +class AdvTable extends React.Component { + constructor(props, context) { + super(props, context); + + this.state = { + order: this.props.order, + orderBy: this.props.orderBy, + selected: this.props.selected, + data: this.props.data.sort((a, b) => (a.calories < b.calories ? -1 : 1)), + page: this.props.page, + rowsPerPage: this.props.rowsPerPage, + defaultPerPage: this.props.defaultPerPage, + filterText: this.props.filterText, + }; + } + + handleRequestSort = (event, property) => { + const orderBy = property; + let order = 'desc'; + + if (this.state.orderBy === property && this.state.order === 'desc') { + order = 'asc'; + } + + const data = order === 'desc' + ? this.state.data.sort((a, b) => (b[orderBy] < a[orderBy] ? -1 : 1)) + : this.state.data.sort((a, b) => (a[orderBy] < b[orderBy] ? -1 : 1)); + + this.setState({ data, order, orderBy }); + }; + + handleSelectAllClick = (event, checked) => { + if (checked) { + this.setState({ selected: this.state.data.map(n => n.id) }); + return; + } + this.setState({ selected: [] }); + }; + + handleClick = (event, id) => { + const { selected } = this.state; + const selectedIndex = selected.indexOf(id); + let newSelected = []; + + if (selectedIndex === -1) { + newSelected = newSelected.concat(selected, id); + } else if (selectedIndex === 0) { + newSelected = newSelected.concat(selected.slice(1)); + } else if (selectedIndex === selected.length - 1) { + newSelected = newSelected.concat(selected.slice(0, -1)); + } else if (selectedIndex > 0) { + newSelected = newSelected.concat( + selected.slice(0, selectedIndex), + selected.slice(selectedIndex + 1), + ); + } + + this.setState({ selected: newSelected }); + }; + + handleChangePage = (event, page) => { + this.setState({ page }); + }; + + handleChangeRowsPerPage = event => { + this.setState({ rowsPerPage: event.target.value }); + }; + + isSelected = id => this.state.selected.indexOf(id) !== -1; + + handleUserInput(value) { + // Show all item first + if (value !== '') { + this.setState({ rowsPerPage: this.state.data.length }); + } else { + this.setState({ rowsPerPage: this.state.defaultPerPage }); + } + + // Show result base on keyword + this.setState({ filterText: value.toLowerCase() }); + } + + render() { + const { classes } = this.props; + const { + data, + order, + orderBy, + selected, + rowsPerPage, + page, + filterText + } = this.state; + const { columnData } = this.props; + const checkcell = true; + const emptyRows = rowsPerPage - Math.min(rowsPerPage, data.length - (page * rowsPerPage)); + const renderCell = (dataArray, keyArray) => keyArray.map((itemCell, index) => ( + <TableCell align={itemCell.numeric ? 'right' : 'left'} key={index.toString()}>{dataArray[itemCell.id]}</TableCell> + )); + return ( + <Paper className={classes.root}> + <EnhancedTableToolbar + numSelected={selected.length} + filterText={filterText} + onUserInput={(event) => this.handleUserInput(event)} + /> + <div className={classes.tableWrapper}> + <Table className={classNames(classes.table, tableStyles.stripped)}> + <EnhancedTableHead + numSelected={selected.length} + order={order} + orderBy={orderBy} + onSelectAllClick={this.handleSelectAllClick} + onRequestSort={this.handleRequestSort} + rowCount={data.length} + columnData={columnData} + checkcell={checkcell} + /> + <TableBody> + {data.slice(page * rowsPerPage, (page * rowsPerPage) + rowsPerPage).map(n => { + const isSelected = this.isSelected(n.id); + if (n.name.toLowerCase().indexOf(filterText) === -1) { + return false; + } + return ( + <TableRow + hover + onClick={event => this.handleClick(event, n.id)} + role="checkbox" + aria-checked={isSelected} + tabIndex={-1} + key={n.id} + selected={isSelected} + > + <TableCell padding="checkbox"> + <Checkbox checked={isSelected} /> + </TableCell> + {renderCell(n, columnData)} + </TableRow> + ); + })} + {emptyRows > 0 && ( + <TableRow style={{ height: 49 * emptyRows }}> + <TableCell colSpan={6} /> + </TableRow> + )} + </TableBody> + </Table> + </div> + <TablePagination + component="div" + rowsPerPageOptions={[5, 10, 25]} + count={data.length} + rowsPerPage={rowsPerPage} + page={page} + backIconButtonProps={{ + 'aria-label': 'Previous Page', + }} + nextIconButtonProps={{ + 'aria-label': 'Next Page', + }} + onChangePage={this.handleChangePage} + onChangeRowsPerPage={this.handleChangeRowsPerPage} + /> + </Paper> + ); + } +} + +AdvTable.propTypes = { + classes: PropTypes.object.isRequired, + data: PropTypes.array.isRequired, + order: PropTypes.string.isRequired, + orderBy: PropTypes.string.isRequired, + selected: PropTypes.array.isRequired, + rowsPerPage: PropTypes.number.isRequired, + page: PropTypes.number.isRequired, + defaultPerPage: PropTypes.number.isRequired, + filterText: PropTypes.string.isRequired, + columnData: PropTypes.array.isRequired, +}; + +export default withStyles(styles)(AdvTable); diff --git a/front/odiparpack/app/components/Tables/CrudTable.js b/front/odiparpack/app/components/Tables/CrudTable.js new file mode 100644 index 0000000..d3dd164 --- /dev/null +++ b/front/odiparpack/app/components/Tables/CrudTable.js @@ -0,0 +1,52 @@ +import React from 'react'; +import PropTypes from 'prop-types'; +import MainTable from './tableParts/MainTable'; + +class CrudTable extends React.Component { + componentDidMount() { + this.props.fetchData(this.props.dataInit, this.props.branch); + } + + render() { + const { + title, + dataTable, + addEmptyRow, + removeRow, + updateRow, + editRow, + finishEditRow, + anchor, + branch + } = this.props; + return ( + <MainTable + title={title} + addEmptyRow={addEmptyRow} + items={dataTable} + removeRow={removeRow} + updateRow={updateRow} + editRow={editRow} + finishEditRow={finishEditRow} + anchor={anchor} + branch={branch} + /> + ); + } +} + +CrudTable.propTypes = { + title: PropTypes.string.isRequired, + anchor: PropTypes.array.isRequired, + dataInit: PropTypes.array.isRequired, + dataTable: PropTypes.object.isRequired, + fetchData: PropTypes.func.isRequired, + addEmptyRow: PropTypes.func.isRequired, + removeRow: PropTypes.func.isRequired, + updateRow: PropTypes.func.isRequired, + editRow: PropTypes.func.isRequired, + finishEditRow: PropTypes.func.isRequired, + branch: PropTypes.string.isRequired, +}; + +export default CrudTable; diff --git a/front/odiparpack/app/components/Tables/CrudTableForm.js b/front/odiparpack/app/components/Tables/CrudTableForm.js new file mode 100644 index 0000000..d2d2ea8 --- /dev/null +++ b/front/odiparpack/app/components/Tables/CrudTableForm.js @@ -0,0 +1,70 @@ +import React from 'react'; +import PropTypes from 'prop-types'; +import Form from './tableParts/Form'; +import MainTableForm from './tableParts/MainTableForm'; +import FloatingPanel from './../Panel/FloatingPanel'; + +class CrudTableForm extends React.Component { + componentDidMount() { + this.props.fetchData(this.props.dataInit, this.props.branch); + } + + sendValues = (values) => { + setTimeout(() => { + this.props.submit(values, this.props.branch); + }, 500); + } + + render() { + const { + title, + dataTable, + openForm, + closeForm, + removeRow, + addNew, + editRow, + anchor, + children, + branch, + initValues + } = this.props; + return ( + <div> + <FloatingPanel openForm={openForm} branch={branch} closeForm={closeForm}> + <Form onSubmit={this.sendValues} initValues={initValues} branch={branch}> + {children} + </Form> + </FloatingPanel> + <MainTableForm + title={title} + addNew={addNew} + items={dataTable} + removeRow={removeRow} + editRow={editRow} + anchor={anchor} + branch={branch} + /> + </div> + ); + } +} + +CrudTableForm.propTypes = { + title: PropTypes.string.isRequired, + anchor: PropTypes.array.isRequired, + dataInit: PropTypes.array.isRequired, + dataTable: PropTypes.object.isRequired, + fetchData: PropTypes.func.isRequired, + submit: PropTypes.func.isRequired, + addNew: PropTypes.func.isRequired, + openForm: PropTypes.bool.isRequired, + closeForm: PropTypes.func.isRequired, + removeRow: PropTypes.func.isRequired, + editRow: PropTypes.func.isRequired, + children: PropTypes.node.isRequired, + initValues: PropTypes.object.isRequired, + branch: PropTypes.string.isRequired, +}; + +export default CrudTableForm; diff --git a/front/odiparpack/app/components/Tables/EmptyData.js b/front/odiparpack/app/components/Tables/EmptyData.js new file mode 100644 index 0000000..a59c3d6 --- /dev/null +++ b/front/odiparpack/app/components/Tables/EmptyData.js @@ -0,0 +1,14 @@ +import React from 'react'; +import tableStyles from 'ba-styles/Table.scss'; +import TableIcon from '@material-ui/icons/Apps'; + +function EmptyData() { + return ( + <div className={tableStyles.nodata}> + <TableIcon /> + No Data + </div> + ); +} + +export default EmptyData; diff --git a/front/odiparpack/app/components/Tables/TreeTable.js b/front/odiparpack/app/components/Tables/TreeTable.js new file mode 100644 index 0000000..12eeb19 --- /dev/null +++ b/front/odiparpack/app/components/Tables/TreeTable.js @@ -0,0 +1,190 @@ +import React from 'react'; +import PropTypes from 'prop-types'; +import { withStyles } from '@material-ui/core/styles'; +import ExpandLess from '@material-ui/icons/KeyboardArrowRight'; +import ExpandMore from '@material-ui/icons/ExpandMore'; +import Add from '@material-ui/icons/AddCircle'; +import Remove from '@material-ui/icons/RemoveCircleOutline'; + +import { Table, TableBody, TableCell, TableHead, TableRow } from '@material-ui/core'; + +const styles = theme => ({ + root: { + width: '100%', + marginTop: theme.spacing(3), + overflowX: 'auto', + }, + table: { + minWidth: 700, + }, + hideRow: { + display: 'none' + }, + anchor: { + cursor: 'pointer' + }, + icon: { + top: 5, + position: 'relative', + left: -5 + } +}); + +let RenderRow = props => { + const { + classes, + toggleTree, + treeOpen, + item, + parent, + arrowMore, + icon, + branch + } = props; + + const keyID = item.id; + const dataBody = Object.keys(item); + const dataBodyVal = Object.values(item); + + const renderIconMore = (iconName) => { + if (iconName === 'arrow') { + return <ExpandMore className={classes.icon} />; + } + return <Remove className={classes.icon} />; + }; + + const renderIconLess = (iconName) => { + if (iconName === 'arrow') { + return <ExpandLess className={classes.icon} />; + } + return <Add className={classes.icon} />; + }; + + const renderCell = (dataArray, parentCell) => dataArray.map((itemCell, index) => { + if (index < 1) { + if (parentCell) { + return ( + <TableCell key={index.toString()} style={{ paddingLeft: (keyID.split('_').length) * 20 }}> + {arrowMore.indexOf(keyID) > -1 ? renderIconMore(icon) : renderIconLess(icon)} + {keyID} + </TableCell> + ); + } + return ( + <TableCell key={index.toString()} style={{ paddingLeft: (keyID.split('_').length) * 20 }}>{keyID}</TableCell> + ); + } + + if (itemCell !== 'child') { + return ( + <TableCell key={index.toString()}>{dataBodyVal[index]}</TableCell> + ); + } + + return false; + }); + + const row = parent ? ( + <TableRow + key={keyID} + className={treeOpen.indexOf(keyID) < 0 && keyID.indexOf('_') > -1 ? classes.hideRow : classes.anchor} + onClick={() => toggleTree(keyID, item.child, branch)} + > + {renderCell(dataBody, true)} + </TableRow> + ) : ( + <TableRow + key={keyID} + className={treeOpen.indexOf(keyID) < 0 && keyID.indexOf('_') > -1 ? classes.hideRow : ''} + > + {renderCell(dataBody, false)} + </TableRow> + ); + + return [row]; +}; + +RenderRow.propTypes = { + classes: PropTypes.object.isRequired, + item: PropTypes.object.isRequired, + parent: PropTypes.bool.isRequired, + toggleTree: PropTypes.func.isRequired, + treeOpen: PropTypes.object.isRequired, + arrowMore: PropTypes.object.isRequired, + branch: PropTypes.string.isRequired, + icon: PropTypes.string.isRequired +}; + +RenderRow = withStyles(styles)(RenderRow); + +class TreeTable extends React.Component { + render() { + const { + classes, + dataTable, + icon, + treeOpen, + arrowMore, + toggleTree, + branch + } = this.props; + const parentRow = true; + const getData = dataArray => dataArray.map((item, index) => { + if (item.child) { + return [ + <RenderRow + icon={icon} + treeOpen={treeOpen} + arrowMore={arrowMore} + toggleTree={toggleTree} + item={item} + key={index.toString()} + parent={parentRow} + branch={branch} + />, + getData(item.child) + ]; + } + return ( + <RenderRow + icon={icon} + item={item} + treeOpen={treeOpen} + arrowMore={arrowMore} + toggleTree={toggleTree} + key={index.toString()} + branch={branch} + parent={false} + /> + ); + }); + + const getHead = dataArray => dataArray.map((item, index) => <TableCell key={index.toString()}>{item.label}</TableCell> + ); + + return ( + <Table className={classes.table}> + <TableHead> + <TableRow> + { getHead(dataTable.head) } + </TableRow> + </TableHead> + <TableBody> + { getData(dataTable.body) } + </TableBody> + </Table> + ); + } +} + +TreeTable.propTypes = { + classes: PropTypes.object.isRequired, + dataTable: PropTypes.object.isRequired, + treeOpen: PropTypes.object.isRequired, + toggleTree: PropTypes.func.isRequired, + arrowMore: PropTypes.object.isRequired, + branch: PropTypes.string.isRequired, + icon: PropTypes.string.isRequired +}; + +export default withStyles(styles)(TreeTable); diff --git a/front/odiparpack/app/components/Tables/tableParts/DatePickerCell.js b/front/odiparpack/app/components/Tables/tableParts/DatePickerCell.js new file mode 100644 index 0000000..161d0eb --- /dev/null +++ b/front/odiparpack/app/components/Tables/tableParts/DatePickerCell.js @@ -0,0 +1,59 @@ +import React from 'react'; +import PropTypes from 'prop-types'; +import { KeyboardDatePicker, MuiPickersUtilsProvider } from '@material-ui/pickers'; +import MomentUtils from '@date-io/moment'; +import css from 'ba-styles/Table.scss'; +import { TableCell } from '@material-ui/core'; + +class DatePickerCell extends React.Component { + state = { + event: { + target: { + name: this.props.cellData.type, // eslint-disable-line + value: this.props.cellData.value, // eslint-disable-line + } + } + } + + handleDateChange = date => { + const { event } = this.state; + const { branch, updateRow } = this.props; + event.target.value = date; + updateRow(event, branch); + } + + render() { + const { + edited, + cellData + } = this.props; + const { event } = this.state; + return ( + <TableCell padding="none" className="text-center" textalign="center"> + <MuiPickersUtilsProvider utils={MomentUtils}> + <KeyboardDatePicker + clearable + name={cellData.type} + className={css.crudInput} + format="DD/MM/YYYY" + placeholder="10/10/2018" + mask={[/\d/, /\d/, '/', /\d/, /\d/, '/', /\d/, /\d/, /\d/, /\d/]} + value={event.target.value} + disabled={!edited} + onChange={this.handleDateChange} + animateYearScrolling={false} + /> + </MuiPickersUtilsProvider> + </TableCell> + ); + } +} + +DatePickerCell.propTypes = { + cellData: PropTypes.object.isRequired, + updateRow: PropTypes.func.isRequired, + edited: PropTypes.bool.isRequired, + branch: PropTypes.string.isRequired, +}; + +export default DatePickerCell; diff --git a/front/odiparpack/app/components/Tables/tableParts/EditableCell.js b/front/odiparpack/app/components/Tables/tableParts/EditableCell.js new file mode 100644 index 0000000..2c7ba8f --- /dev/null +++ b/front/odiparpack/app/components/Tables/tableParts/EditableCell.js @@ -0,0 +1,86 @@ +import React from 'react'; +import PropTypes from 'prop-types'; +import css from 'ba-styles/Table.scss'; + +import { TableCell, Input, TextField } from '@material-ui/core'; + +class EditableCell extends React.Component { + handleUpdate(event) { + event.persist(); + this.props.updateRow(event, this.props.branch); + } + + render() { + const { + cellData, + edited, + inputType + } = this.props; + switch (inputType) { + case 'text': + return ( + <TableCell padding="none"> + <Input + placeholder={cellData.type} + name={cellData.type} + className={css.crudInput} + id={cellData.id.toString()} + value={cellData.value} + onChange={(event) => this.handleUpdate(event)} + disabled={!edited} + margin="none" + inputProps={{ + 'aria-label': 'Description', + }} + /> + </TableCell> + ); + case 'number': + return ( + <TableCell padding="none"> + <TextField + id={cellData.id.toString()} + name={cellData.type} + className={css.crudInput} + value={cellData.value} + onChange={(event) => this.handleUpdate(event)} + type="number" + InputLabelProps={{ + shrink: true, + }} + margin="none" + disabled={!edited} + /> + </TableCell> + ); + default: + return ( + <TableCell padding="none"> + <Input + placeholder={cellData.type} + name={cellData.type} + className={css.crudInput} + id={cellData.id.toString()} + value={cellData.value} + onChange={(event) => this.handleUpdate(event)} + disabled={!edited} + margin="none" + inputProps={{ + 'aria-label': 'Description', + }} + /> + </TableCell> + ); + } + } +} + +EditableCell.propTypes = { + inputType: PropTypes.string.isRequired, + cellData: PropTypes.object.isRequired, + updateRow: PropTypes.func.isRequired, + edited: PropTypes.bool.isRequired, + branch: PropTypes.string.isRequired, +}; + +export default EditableCell; diff --git a/front/odiparpack/app/components/Tables/tableParts/Form.js b/front/odiparpack/app/components/Tables/tableParts/Form.js new file mode 100644 index 0000000..da66966 --- /dev/null +++ b/front/odiparpack/app/components/Tables/tableParts/Form.js @@ -0,0 +1,71 @@ +import React, { Component } from 'react'; +import PropTypes from 'prop-types'; +import { connect } from 'react-redux'; +import { reduxForm } from 'redux-form/immutable'; +import css from 'ba-styles/Form.scss'; +import { Button } from '@material-ui/core'; + +class Form extends Component { + componentDidMount() { + // this.ref // the Field + // .getRenderedComponent() // on Field, returns ReduxFormMaterialUITextField + // .getRenderedComponent() // on ReduxFormMaterialUITextField, returns TextField + // .focus() // on TextField + // console.log(this.props.initValues); + } + + render() { + const { + handleSubmit, + children, + reset, + pristine, + submitting, + } = this.props; + + return ( + <div> + <form onSubmit={handleSubmit}> + <section className={css.bodyForm}> + {children} + </section> + <div className={css.buttonArea}> + <Button variant="contained" color="secondary" type="submit" disabled={submitting}> + Submit + </Button> + <Button + type="button" + disabled={pristine || submitting} + onClick={reset} + > + Reset + </Button> + </div> + </form> + </div> + ); + } +} + +Form.propTypes = { + children: PropTypes.node.isRequired, + handleSubmit: PropTypes.func.isRequired, + reset: PropTypes.func.isRequired, + pristine: PropTypes.bool.isRequired, + submitting: PropTypes.bool.isRequired, +}; + +const FormMapped = reduxForm({ + form: 'immutableExample', + enableReinitialize: true, +})(Form); + + +const FormMappedInit = connect( + state => ({ + initialValues: state.getIn(['crudTableForm', 'formValues']) + }) +)(FormMapped); + + +export default FormMappedInit; diff --git a/front/odiparpack/app/components/Tables/tableParts/MainTable.js b/front/odiparpack/app/components/Tables/tableParts/MainTable.js new file mode 100644 index 0000000..973bccf --- /dev/null +++ b/front/odiparpack/app/components/Tables/tableParts/MainTable.js @@ -0,0 +1,104 @@ +import React from 'react'; +import PropTypes from 'prop-types'; +import { withStyles } from '@material-ui/core/styles'; +import classNames from 'classnames'; +import AddIcon from '@material-ui/icons/Add'; +import css from 'ba-styles/Table.scss'; +import { + Table, + TableBody, + TableCell, + TableHead, + TableRow, + Toolbar, + Typography, + Tooltip, + Button, +} from '@material-ui/core'; +import Row from './Row'; +import styles from './tableStyle-jss'; + + +class MainTable extends React.Component { + render() { + const { + classes, + items, + addEmptyRow, + removeRow, + updateRow, + editRow, + finishEditRow, + anchor, + branch, + title + } = this.props; + + const getItems = dataArray => dataArray.map(item => ( + <Row + anchor={anchor} + updateRow={(event) => updateRow(event, item, branch)} + item={item} + removeRow={() => removeRow(item, branch)} + key={item.get('id')} + editRow={() => editRow(item, branch)} + finishEditRow={() => finishEditRow(item, branch)} + branch={branch} + /> + )); + + const getHead = dataArray => dataArray.map((item, index) => { + if (!item.hidden) { + return ( + <TableCell padding="none" key={index.toString()} width={item.width}>{item.label}</TableCell> + ); + } + return false; + }); + return ( + <div> + <Toolbar className={classes.toolbar}> + <div className={classes.title}> + <Typography variant="h6">{title}</Typography> + </div> + <div className={classes.spacer} /> + <div className={classes.actions}> + <Tooltip title="Add Item"> + <Button variant="contained" onClick={() => addEmptyRow(anchor, branch)} color="secondary" className={classes.button}> + <AddIcon className={classNames(classes.leftIcon, classes.iconSmall)} /> + Add New + </Button> + </Tooltip> + </div> + </Toolbar> + <div className={classes.rootTable}> + <Table className={classNames(css.tableCrud, classes.table, css.stripped)}> + <TableHead> + <TableRow> + { getHead(anchor) } + </TableRow> + </TableHead> + <TableBody> + {getItems(items)} + </TableBody> + </Table> + </div> + </div> + ); + } +} + +MainTable.propTypes = { + title: PropTypes.string.isRequired, + classes: PropTypes.object.isRequired, + items: PropTypes.object.isRequired, + anchor: PropTypes.array.isRequired, + addEmptyRow: PropTypes.func.isRequired, + removeRow: PropTypes.func.isRequired, + updateRow: PropTypes.func.isRequired, + editRow: PropTypes.func.isRequired, + finishEditRow: PropTypes.func.isRequired, + branch: PropTypes.string.isRequired +}; + +export default withStyles(styles)(MainTable); diff --git a/front/odiparpack/app/components/Tables/tableParts/MainTableForm.js b/front/odiparpack/app/components/Tables/tableParts/MainTableForm.js new file mode 100644 index 0000000..ccf0e4a --- /dev/null +++ b/front/odiparpack/app/components/Tables/tableParts/MainTableForm.js @@ -0,0 +1,97 @@ +import React from 'react'; +import PropTypes from 'prop-types'; +import { withStyles } from '@material-ui/core/styles'; +import classNames from 'classnames'; +import AddIcon from '@material-ui/icons/Add'; +import css from 'ba-styles/Table.scss'; +import { + Table, + TableBody, + TableCell, + TableHead, + TableRow, + Toolbar, + Typography, + Tooltip, + Button, +} from '@material-ui/core'; +import RowReadOnly from './RowReadOnly'; +import styles from './tableStyle-jss'; + + +class MainTableForm extends React.Component { + render() { + const { + title, + classes, + items, + removeRow, + editRow, + addNew, + anchor, + branch + } = this.props; + const getItems = dataArray => dataArray.map(item => ( + <RowReadOnly + item={item} + removeRow={() => removeRow(item, branch)} + key={item.get('id')} + editRow={() => editRow(item, branch)} + anchor={anchor} + branch={branch} + /> + )); + + const getHead = dataArray => dataArray.map((item, index) => { + if (!item.hidden) { + return ( + <TableCell padding="none" key={index.toString()} width={item.width}>{item.label}</TableCell> + ); + } + return false; + }); + return ( + <div> + <Toolbar className={classes.toolbar}> + <div className={classes.title}> + <Typography variant="h6">{title}</Typography> + </div> + <div className={classes.spacer} /> + <div className={classes.actions}> + <Tooltip title="Add Item"> + <Button variant="contained" onClick={() => addNew(anchor, branch)} color="secondary" className={classes.button}> + <AddIcon className={classNames(classes.leftIcon, classes.iconSmall)} /> + Add New + </Button> + </Tooltip> + </div> + </Toolbar> + <div className={classes.rootTable}> + <Table className={classNames(css.tableCrud, classes.table, css.stripped)}> + <TableHead> + <TableRow> + { getHead(anchor) } + </TableRow> + </TableHead> + <TableBody> + {getItems(items)} + </TableBody> + </Table> + </div> + </div> + ); + } +} + +MainTableForm.propTypes = { + title: PropTypes.string.isRequired, + classes: PropTypes.object.isRequired, + items: PropTypes.object.isRequired, + anchor: PropTypes.array.isRequired, + addNew: PropTypes.func.isRequired, + removeRow: PropTypes.func.isRequired, + editRow: PropTypes.func.isRequired, + branch: PropTypes.string.isRequired, +}; + +export default withStyles(styles)(MainTableForm); diff --git a/front/odiparpack/app/components/Tables/tableParts/Row.js b/front/odiparpack/app/components/Tables/tableParts/Row.js new file mode 100644 index 0000000..67e7a4d --- /dev/null +++ b/front/odiparpack/app/components/Tables/tableParts/Row.js @@ -0,0 +1,167 @@ +import React from 'react'; +import PropTypes from 'prop-types'; +import { withStyles } from '@material-ui/core/styles'; +import classNames from 'classnames'; +import DeleteIcon from '@material-ui/icons/Delete'; +import EditIcon from '@material-ui/icons/BorderColor'; +import DoneIcon from '@material-ui/icons/Done'; +import css from 'ba-styles/Table.scss'; +import { TableCell, IconButton } from '@material-ui/core'; +import EditableCell from './EditableCell'; +import SelectableCell from './SelectableCell'; +import ToggleCell from './ToggleCell'; +import DatePickerCell from './DatePickerCell'; +import TimePickerCell from './TimePickerCell'; + + +const styles = theme => ({ + button: { + margin: theme.spacing(1), + }, +}); + +class Row extends React.Component { + render() { + const { + classes, + anchor, + item, + removeRow, + updateRow, + editRow, + finishEditRow, + branch + } = this.props; + const eventDel = () => { + removeRow(item, branch); + }; + const eventEdit = () => { + editRow(item, branch); + }; + const eventDone = () => { + finishEditRow(item, branch); + }; + const renderCell = dataArray => dataArray.map((itemCell, index) => { + if (itemCell.name !== 'action' && !itemCell.hidden) { + const inputType = anchor[index].type; + switch (inputType) { + case 'selection': + return ( + <SelectableCell + updateRow={(event) => updateRow(event, branch)} + cellData={{ + type: itemCell.name, + value: item.get(itemCell.name), + id: item.get('id'), + }} + edited={item.get('edited')} + key={index.toString()} + options={anchor[index].options} + branch={branch} + /> + ); + case 'toggle': + return ( + <ToggleCell + updateRow={(event) => updateRow(event, branch)} + cellData={{ + type: itemCell.name, + value: item.get(itemCell.name), + id: item.get('id'), + }} + edited={item.get('edited')} + key={index.toString()} + branch={branch} + /> + ); + case 'date': + return ( + <DatePickerCell + updateRow={(event) => updateRow(event, branch)} + cellData={{ + type: itemCell.name, + value: item.get(itemCell.name), + id: item.get('id'), + }} + edited={item.get('edited')} + key={index.toString()} + branch={branch} + /> + ); + case 'time': + return ( + <TimePickerCell + updateRow={(event) => updateRow(event, branch)} + cellData={{ + type: itemCell.name, + value: item.get(itemCell.name), + id: item.get('id'), + }} + edited={item.get('edited')} + key={index.toString()} + branch={branch} + /> + ); + default: + return ( + <EditableCell + updateRow={(event) => updateRow(event, branch)} + cellData={{ + type: itemCell.name, + value: item.get(itemCell.name), + id: item.get('id'), + }} + edited={item.get('edited')} + key={index.toString()} + inputType={inputType} + branch={branch} + /> + ); + } + } + return false; + }); + return ( + <tr className={item.get('edited') ? css.editing : ''}> + {renderCell(anchor)} + <TableCell padding="none"> + <IconButton + onClick={() => eventEdit(this)} + className={classNames((item.get('edited') ? css.hideAction : ''), classes.button)} + aria-label="Edit" + > + <EditIcon /> + </IconButton> + <IconButton + onClick={() => eventDone(this)} + color="secondary" + className={classNames((!item.get('edited') ? css.hideAction : ''), classes.button)} + aria-label="Done" + > + <DoneIcon /> + </IconButton> + <IconButton + onClick={() => eventDel(this)} + className={classes.button} + aria-label="Delete" + > + <DeleteIcon /> + </IconButton> + </TableCell> + </tr> + ); + } +} + +Row.propTypes = { + classes: PropTypes.object.isRequired, + anchor: PropTypes.array.isRequired, + item: PropTypes.object.isRequired, + removeRow: PropTypes.func.isRequired, + updateRow: PropTypes.func.isRequired, + editRow: PropTypes.func.isRequired, + finishEditRow: PropTypes.func.isRequired, + branch: PropTypes.string.isRequired +}; + +export default withStyles(styles)(Row); diff --git a/front/odiparpack/app/components/Tables/tableParts/RowReadOnly.js b/front/odiparpack/app/components/Tables/tableParts/RowReadOnly.js new file mode 100644 index 0000000..7da655f --- /dev/null +++ b/front/odiparpack/app/components/Tables/tableParts/RowReadOnly.js @@ -0,0 +1,76 @@ +import React from 'react'; +import PropTypes from 'prop-types'; +import { withStyles } from '@material-ui/core/styles'; +import classNames from 'classnames'; +import css from 'ba-styles/Table.scss'; +import DeleteIcon from '@material-ui/icons/Delete'; +import EditIcon from '@material-ui/icons/BorderColor'; + +import { TableCell, IconButton } from '@material-ui/core'; + +const styles = theme => ({ + button: { + margin: theme.spacing(1), + }, +}); + +class RowReadOnly extends React.Component { + render() { + const { + anchor, + classes, + item, + removeRow, + editRow, + branch + } = this.props; + const eventDel = () => { + removeRow(item, branch); + }; + const eventEdit = () => { + editRow(item, branch); + }; + const renderCell = dataArray => dataArray.map((itemCell, index) => { + if (itemCell.name !== 'action' && !itemCell.hidden) { + return ( + <TableCell padding="none" key={index.toString()}> + {item.get(itemCell.name) !== undefined ? item.get(itemCell.name).toString() : ''} + </TableCell> + ); + } + return false; + }); + return ( + <tr> + {renderCell(anchor)} + <TableCell padding="none"> + <IconButton + onClick={() => eventEdit(this)} + className={classNames((item.get('edited') ? css.hideAction : ''), classes.button)} + aria-label="Edit" + > + <EditIcon /> + </IconButton> + <IconButton + onClick={() => eventDel(this)} + className={classes.button} + aria-label="Delete" + > + <DeleteIcon /> + </IconButton> + </TableCell> + </tr> + ); + } +} + +RowReadOnly.propTypes = { + anchor: PropTypes.array.isRequired, + classes: PropTypes.object.isRequired, + item: PropTypes.object.isRequired, + removeRow: PropTypes.func.isRequired, + editRow: PropTypes.func.isRequired, + branch: PropTypes.string.isRequired, +}; + +export default withStyles(styles)(RowReadOnly); diff --git a/front/odiparpack/app/components/Tables/tableParts/SelectableCell.js b/front/odiparpack/app/components/Tables/tableParts/SelectableCell.js new file mode 100644 index 0000000..66fde97 --- /dev/null +++ b/front/odiparpack/app/components/Tables/tableParts/SelectableCell.js @@ -0,0 +1,46 @@ +import React from 'react'; +import PropTypes from 'prop-types'; +import css from 'ba-styles/Table.scss'; + +import { Select, MenuItem, TableCell } from '@material-ui/core'; + +class SelectableCell extends React.Component { + handleChange = event => { + this.props.updateRow(event, this.props.branch); + this.setState({ [event.target.name]: event.target.value }); + }; + + render() { + const { + cellData, + edited, + options, + } = this.props; + return ( + <TableCell padding="none"> + <Select + name={cellData.type} + id={cellData.id.toString()} + className={css.crudInput} + value={cellData.value} + onChange={this.handleChange} + displayEmpty + disabled={!edited} + margin="none" + > + {options.map((option, index) => <MenuItem value={option} key={index.toString()}>{option}</MenuItem>)} + </Select> + </TableCell> + ); + } +} + +SelectableCell.propTypes = { + options: PropTypes.array.isRequired, + cellData: PropTypes.object.isRequired, + updateRow: PropTypes.func.isRequired, + edited: PropTypes.bool.isRequired, + branch: PropTypes.string.isRequired, +}; + +export default SelectableCell; diff --git a/front/odiparpack/app/components/Tables/tableParts/TableHeader.js b/front/odiparpack/app/components/Tables/tableParts/TableHeader.js new file mode 100644 index 0000000..4ef6b0d --- /dev/null +++ b/front/odiparpack/app/components/Tables/tableParts/TableHeader.js @@ -0,0 +1,74 @@ +import React from 'react'; +import PropTypes from 'prop-types'; +import { TableCell, TableHead, TableRow, TableSortLabel, Checkbox, Tooltip } from '@material-ui/core'; + +class TableHeader extends React.Component { + createSortHandler = property => event => { + this.props.onRequestSort(event, property); + }; + + render() { + const { + onSelectAllClick, + order, + orderBy, + numSelected, + rowCount, + columnData, + checkcell + } = this.props; + + return ( + <TableHead> + <TableRow> + {checkcell + && ( + <TableCell padding="checkbox" width="80"> + <Checkbox + indeterminate={numSelected > 0 && numSelected < rowCount} + checked={numSelected === rowCount} + onChange={onSelectAllClick} + /> + </TableCell> + ) + } + {columnData.map(column => ( + <TableCell + key={column.id} + align={column.numeric ? 'right' : 'left'} + padding={column.disablePadding ? 'none' : 'default'} + sortDirection={orderBy === column.id ? order : false} + > + <Tooltip + title="Sort" + placement={column.numeric ? 'bottom-end' : 'bottom-start'} + enterDelay={300} + > + <TableSortLabel + active={orderBy === column.id} + direction={order} + onClick={this.createSortHandler(column.id)} + > + {column.label} + </TableSortLabel> + </Tooltip> + </TableCell> + ), this)} + </TableRow> + </TableHead> + ); + } +} + +TableHeader.propTypes = { + numSelected: PropTypes.number.isRequired, + onRequestSort: PropTypes.func.isRequired, + onSelectAllClick: PropTypes.func.isRequired, + order: PropTypes.string.isRequired, + orderBy: PropTypes.string.isRequired, + rowCount: PropTypes.number.isRequired, + columnData: PropTypes.array.isRequired, + checkcell: PropTypes.bool.isRequired, +}; + +export default TableHeader; diff --git a/front/odiparpack/app/components/Tables/tableParts/TableToolbar.js b/front/odiparpack/app/components/Tables/tableParts/TableToolbar.js new file mode 100644 index 0000000..940a82c --- /dev/null +++ b/front/odiparpack/app/components/Tables/tableParts/TableToolbar.js @@ -0,0 +1,123 @@ +import React from 'react'; +import PropTypes from 'prop-types'; +import classNames from 'classnames'; +import { withStyles } from '@material-ui/core/styles'; +import DeleteIcon from '@material-ui/icons/Delete'; +import ArchiveIcon from '@material-ui/icons/Archive'; +import BookmarkIcon from '@material-ui/icons/Bookmark'; +import FilterListIcon from '@material-ui/icons/FilterList'; +import SearchIcon from '@material-ui/icons/Search'; +import { + Toolbar, + Typography, + IconButton, + Tooltip, + FormControl, + Input, + InputAdornment, +} from '@material-ui/core'; +import styles from './tableStyle-jss'; + + +class TableToolbar extends React.Component { + state = { + showSearch: false, + } + + toggleSearch() { + this.setState({ showSearch: !this.state.showSearch }); + } + + handleChange(event) { + event.persist(); + this.props.onUserInput(event.target.value); + } + + render() { + const { numSelected, classes, filterText } = this.props; + const { showSearch } = this.state; + + return ( + <Toolbar + className={classNames(classes.root, { + [classes.highlight]: numSelected > 0, + })} + > + <div className={classes.titleToolbar}> + {numSelected > 0 ? ( + <Typography color="inherit" variant="subtitle1"> + {numSelected} + {' '} +selected + </Typography> + ) : ( + <Typography variant="h6">Nutrition</Typography> + )} + </div> + <div className={classes.spacer} /> + <div className={classes.actionsToolbar}> + {numSelected > 0 ? ( + <div> + <Tooltip title="Bookmark"> + <IconButton aria-label="Bookmark"> + <BookmarkIcon /> + </IconButton> + </Tooltip> + <Tooltip title="Archive"> + <IconButton aria-label="Archive"> + <ArchiveIcon /> + </IconButton> + </Tooltip> + <Tooltip title="Delete"> + <IconButton aria-label="Delete"> + <DeleteIcon /> + </IconButton> + </Tooltip> + </div> + ) : ( + <div className={classes.actions}> + {showSearch + && ( + <FormControl className={classNames(classes.textField)}> + <Input + id="search_filter" + type="text" + placeholder="Search Desert" + value={filterText} + onChange={(event) => this.handleChange(event)} + endAdornment={( + <InputAdornment position="end"> + <IconButton aria-label="Search filter"> + <SearchIcon /> + </IconButton> + </InputAdornment> + )} + /> + </FormControl> + ) + } + <Tooltip title="Filter list"> + <IconButton + aria-label="Filter list" + className={classes.filterBtn} + onClick={() => this.toggleSearch()} + > + <FilterListIcon /> + </IconButton> + </Tooltip> + </div> + )} + </div> + </Toolbar> + ); + } +} + +TableToolbar.propTypes = { + classes: PropTypes.object.isRequired, + filterText: PropTypes.string.isRequired, + onUserInput: PropTypes.func.isRequired, + numSelected: PropTypes.number.isRequired, +}; + +export default withStyles(styles)(TableToolbar); diff --git a/front/odiparpack/app/components/Tables/tableParts/TimePickerCell.js b/front/odiparpack/app/components/Tables/tableParts/TimePickerCell.js new file mode 100644 index 0000000..941df31 --- /dev/null +++ b/front/odiparpack/app/components/Tables/tableParts/TimePickerCell.js @@ -0,0 +1,66 @@ +import React from 'react'; +import PropTypes from 'prop-types'; +import { TimePicker, MuiPickersUtilsProvider } from '@material-ui/pickers'; +import MomentUtils from '@date-io/moment'; +import css from 'ba-styles/Table.scss'; + +import { TableCell, InputAdornment, Icon, IconButton } from '@material-ui/core'; + +class TimePickerCell extends React.Component { + state = { + event: { + target: { + name: this.props.cellData.type, // eslint-disable-line + value: this.props.cellData.value, // eslint-disable-line + } + } + } + + handleTimeChange = date => { + const { event } = this.state; + const { updateRow, branch } = this.props; + event.target.value = date; + updateRow(event, branch); + } + + render() { + const { + edited, + cellData + } = this.props; + const { event } = this.state; + return ( + <TableCell padding="none"> + <MuiPickersUtilsProvider utils={MomentUtils}> + <TimePicker + name={cellData.type} + className={css.crudInput} + mask={[/\d/, /\d/, ':', /\d/, /\d/, ' ', /a|p/i, 'M']} + placeholder="08:00 AM" + value={event.target.value} + disabled={!edited} + InputProps={{ + endAdornment: ( + <InputAdornment position="end"> + <IconButton> + <Icon>access_time</Icon> + </IconButton> + </InputAdornment> + ), + }} + onChange={this.handleTimeChange} + /> + </MuiPickersUtilsProvider> + </TableCell> + ); + } +} + +TimePickerCell.propTypes = { + cellData: PropTypes.object.isRequired, + updateRow: PropTypes.func.isRequired, + edited: PropTypes.bool.isRequired, + branch: PropTypes.string.isRequired, +}; + +export default TimePickerCell; diff --git a/front/odiparpack/app/components/Tables/tableParts/ToggleCell.js b/front/odiparpack/app/components/Tables/tableParts/ToggleCell.js new file mode 100644 index 0000000..dc0af89 --- /dev/null +++ b/front/odiparpack/app/components/Tables/tableParts/ToggleCell.js @@ -0,0 +1,50 @@ +import React from 'react'; +import PropTypes from 'prop-types'; +import classNames from 'classnames'; +import css from 'ba-styles/Table.scss'; + +import { TableCell, FormControlLabel, Switch } from '@material-ui/core'; + +class ToggleCell extends React.Component { + state = { + isChecked: this.props.cellData.value + }; + + handleChange = event => { + this.setState({ isChecked: event.target.checked }); + this.props.updateRow(event, this.props.branch); + }; + + render() { + const { + cellData, + edited, + } = this.props; + return ( + <TableCell className={css.toggleCell} padding="none" textalign="center"> + <div className={classNames(css.coverReadonly, !edited ? css.show : '')} /> + <FormControlLabel + control={( + <Switch + name={cellData.type} + id={cellData.id.toString()} + className={css.crudInput} + checked={this.state.isChecked} + onChange={this.handleChange} + value={cellData.value.toString()} + /> + )} + /> + </TableCell> + ); + } +} + +ToggleCell.propTypes = { + cellData: PropTypes.object.isRequired, + updateRow: PropTypes.func.isRequired, + edited: PropTypes.bool.isRequired, + branch: PropTypes.string.isRequired, +}; + +export default ToggleCell; diff --git a/front/odiparpack/app/components/Tables/tableParts/tableStyle-jss.js b/front/odiparpack/app/components/Tables/tableParts/tableStyle-jss.js new file mode 100644 index 0000000..bede0b8 --- /dev/null +++ b/front/odiparpack/app/components/Tables/tableParts/tableStyle-jss.js @@ -0,0 +1,63 @@ +import { lighten } from '@material-ui/core/styles/colorManipulator'; +const styles = theme => ({ + root: { + paddingRight: theme.spacing(1), + }, + rootTable: { + width: '100%', + marginTop: theme.spacing(3), + overflowX: 'auto', + }, + highlight: + theme.palette.type === 'light' ? { + color: theme.palette.secondary.main, + backgroundColor: lighten(theme.palette.secondary.light, 0.85), + } : { + color: theme.palette.text.primary, + backgroundColor: theme.palette.secondary.dark, + }, + spacer: { + flex: '1 1 100%', + }, + actionsToolbar: { + color: theme.palette.text.secondary, + flex: '1 0 auto', + }, + titleToolbar: { + flex: '0 0 auto', + }, + filterBtn: { + top: -5, + }, + textField: { + flexBasis: 200, + width: 300 + }, + table: { + minWidth: 900, + }, + actions: { + color: theme.palette.text.secondary, + margin: 10 + }, + toolbar: { + backgroundColor: theme.palette.grey[800], + }, + title: { + flex: '0 0 auto', + '& h6': { + color: theme.palette.common.white + } + }, + button: { + margin: theme.spacing(1), + }, + iconSmall: { + fontSize: 20, + }, + leftIcon: { + marginRight: theme.spacing(1), + }, +}); + +export default styles; |
