Commit bc3a8083 authored by Matija Obreza's avatar Matija Obreza

Merge branch '118-admin-list-of-codevalues' into 'master'

Admin: List of CodeValues

Closes #118

See merge request !97
parents 8d61eeec cadffb74
......@@ -76,6 +76,10 @@
"id": "Crop ID",
"name": "Crop"
},
"CodeValueFullInfo": {
"code": "Code",
"group": "Group"
},
"Geography": {
"geography": "Geography",
"currentGeography": "Current Valid Geography",
......
......@@ -65,6 +65,10 @@
"id": "Crop ID",
"name": "Crop"
},
"CodeValueFullInfo": {
"code": "Code",
"group": "Group"
},
"Geography": {
"geography": "Geography",
"currentGeography": "Current Valid Geography",
......
......@@ -161,6 +161,21 @@
}
}
},
"codevalue": {
"admin": {
"p": {
"browse": {
"title": "Code value list",
"item": "CodeValue"
}
},
"c": {
"groupSelect": {
"placeholder": "Select a group"
}
}
}
},
"cooperator": {
"public": {
"p": {
......
import Loadable from '@gringlobal/client/utilities/CustomReactLoadable';
// model
import IRoute from '@gringlobal/client/model/common/IRoute';
import { UserRole } from '@gringlobal/client/model/gringlobal/SysUser';
const adminRoutes: IRoute[] = [
{
auth: [ UserRole.ADMINISTRATOR ],
exact: true,
component: Loadable({
loader: () => import(/* webpackMode:"lazy", webpackChunkName: "site" */ 'codevalue/ui/admin/CodeValueBrowsePage'),
}),
path: '/codes',
},
];
export { adminRoutes as codeValueAdminRotes };
{
"admin": {
"p": {
"browse": {
"title": "Code value list",
"item": "CodeValue"
}
},
"c": {
"groupSelect": {
"placeholder": "Select a group"
}
}
}
}
import * as React from 'react';
import { connect } from 'react-redux';
import { bindActionCreators, compose } from 'redux';
import { WithTranslation, withTranslation } from 'react-i18next';
// Action
import { refreshCodeValues } from 'core/action/codeValue';
// Model
import { ApiCall } from '@gringlobal/client/model/common';
import CodeValueInfo from '@gringlobal/client/model/gringlobal/CodeValueInfo';
// Utils
import memoize from 'memoize-one';
import forOwn from 'lodash/forOwn';
// UI
import ContentHeader from '@gringlobal/client/ui/common/heading/ContentHeader';
import Table from '@gringlobal/client/ui/common/table/Table';
import AddNewButton from '@gringlobal/client/ui/common/button/AddNewButton';
import { BasicTableConfiguration as TableConfiguration } from '@gringlobal/client/ui/common/table/TableConfiguration';
// import Button from '@material-ui/core/Button/Button';
import PageTitle from '@gringlobal/client/ui/common/PageTitle';
import Filters from 'codevalue/ui/admin/c/Filters';
class CodeValueFullInfo extends CodeValueInfo {
public group: string;
public code: string;
}
interface IBrowsePageProps extends React.ClassAttributes<any>, WithTranslation {
codeValues: ApiCall<Record<string, Record<string, CodeValueInfo>>>;
refreshCodeValues: () => void;
}
interface IBrowsePageState {
selected: CodeValueFullInfo[];
group: string;
}
export const SiteTableDefaultConfig = {
defaultColumns: ['group', 'code', 'title', 'description'],
};
const SiteTableConfig = new TableConfiguration(SiteTableDefaultConfig);
class BrowsePage extends React.Component<IBrowsePageProps, IBrowsePageState> {
public constructor(props) {
super(props);
if (!props.codeValues && props.refreshCodeValues) {
props.refreshCodeValues();
}
}
private getCodeValuesList = memoize((codeValues: Record<string, Record<string, CodeValueInfo>>, group: string): CodeValueFullInfo[] => {
const fullInfoList = [];
if (group) {
forOwn(codeValues[group], (num, code) => {
fullInfoList.push({ ...codeValues[group][code], code, group });
});
}
return fullInfoList;
});
private getGroups = memoize((codeValues: Record<string, Record<string, CodeValueInfo>>): string[] => {
return Object.keys(codeValues);
});
public state = {
selected: [],
group: '',
};
// private remove = () => {
// console.log('Will be removed: ', this.state.selected);
// };
private rowToggled = (toggledRow: number, selectedRows: number[], rowData: CodeValueFullInfo) => {
console.log(`Row ${toggledRow} was toggled. Have ${selectedRows}`);
console.log('Row data: ', rowData);
};
private columnToggled = (toggledColumn: string, selectedColumns: string[]) => {
console.log(`Column ${toggledColumn} was toggled. Have ${selectedColumns}`);
};
private applyFilter = (data: any) => {
this.setState({ group: data.group });
};
public render() {
const { codeValues, t } = this.props;
const { group } = this.state;
const data = codeValues && codeValues.data ? this.getCodeValuesList(codeValues.data, group) : [];
const groups = codeValues && codeValues.data ? this.getGroups(codeValues.data) : [];
return (
<>
<PageTitle title={ t('codevalue.admin.p.browse.title') }/>
<ContentHeader title={ t('codevalue.admin.p.browse.title') }>
{ /* this.state.selected.length > 0 &&
<Button onClick={ this.remove } variant="contained" disabled={ this.state.selected.length < 1 }>
{ t('common:action.remove') }
</Button>
*/ }
<Filters
onSubmit={ this.applyFilter }
filter={ { group: this.state.group } }
groups={ groups }
/>
</ContentHeader>
<Table
tableKey="code-value-list"
type={ 'CodeValueFullInfo' }
columns={ SiteTableConfig.defaultColumns }
data={ data }
tableConfig={ SiteTableConfig }
total={ data.length }
onRowToggled={ this.rowToggled }
onColumnToggled={ this.columnToggled }
/>
<AddNewButton
title='codevalue.admin.p.browse.item'
path="codes/edit/" // todo
/>
</>
);
}
}
const mapStateToProps = (state) => ({
codeValues: state.codeValue.codeValuesCall,
});
const mapDispatchToProps = (dispatch) => bindActionCreators({
refreshCodeValues,
}, dispatch);
export default compose(
connect(mapStateToProps, mapDispatchToProps),
withTranslation(),
)(BrowsePage);
import * as React from 'react';
import { createStyles, withStyles, WithStyles } from '@material-ui/core';
import { withTranslation, WithTranslation } from 'react-i18next';
import TextField from '@material-ui/core/TextField/TextField';
import MenuItem from '@material-ui/core/MenuItem/MenuItem';
import { FieldInputProps, FieldMetaState, FieldRenderProps } from 'react-final-form';
const styles = (theme) => createStyles({
textField: {
'margin': '-1px 0',
'minWidth': '300px',
[theme.breakpoints.down('sm')]: {
minWidth: '120px',
},
'& input': {
backgroundColor: 'white',
opacity: 1,
border: 0,
height: '100%',
boxSizing: 'border-box',
paddingLeft: '5px',
},
},
});
interface ICodeValueGroupSelect extends React.ClassAttributes<FieldRenderProps<string, any>>, WithStyles, WithTranslation {
input: FieldInputProps<any, any>;
meta: FieldMetaState<any>;
groups: string[];
}
class CodeValueGroupSelect extends React.Component<ICodeValueGroupSelect, any> {
public constructor(props) {
super(props);
}
public render() {
const { groups, classes, input, meta, t } = this.props;
return (
<div className={ classes.fieldWrapper }>
<TextField
select
className={ classes.textField }
fullWidth
{ ...input }
error={ meta && !!meta.error }
placeholder={ t('codevalue.admin.c.groupSelect.placeholder') }
>
{ groups ? groups.map((group, i) => (
<MenuItem key={ `option-${group}-${i}` } value={ group }>
{ group }
</MenuItem>
)) : [] }
</TextField>
</div>
);
}
}
export default withTranslation()(withStyles(styles)(CodeValueGroupSelect));
import * as React from 'react';
import { Form, Field, FormProps, FormRenderProps } from 'react-final-form';
import { withTranslation, WithTranslation } from 'react-i18next';
// UI
import { withStyles, WithStyles } from '@material-ui/core/styles';
import { Grid } from '@material-ui/core';
import CodeValueGroupSelect from './CodeValueGroupSelect';
const styles = (theme) => ({
});
interface ICodeValueFilters extends React.ClassAttributes<any>, FormProps {
filter: any;
groups: string[];
}
interface ICodeValueFiltersInternal extends React.ClassAttributes<any>, FormRenderProps, WithStyles, WithTranslation {
groups: string[];
}
class CodeValueFiltersInternal extends React.Component<ICodeValueFiltersInternal, any> {
public constructor(props) {
super(props);
}
public componentDidUpdate(prevProps: ICodeValueFiltersInternal) {
if (prevProps && prevProps.values) {
const isChanged = prevProps.values.group !== this.props.values.group;
if (isChanged) {
this.props.handleSubmit();
}
}
}
public render() {
const { handleSubmit, classes, groups } = this.props;
return (
<form onSubmit={ handleSubmit }>
<Grid item>
<Field
name="group"
type="text"
component={ CodeValueGroupSelect }
className={ classes.textField }
groups={ groups }
/>
</Grid>
</form>
);
}
}
const WrappedCodeValueFiltersInternal = withTranslation()(withStyles(styles)(CodeValueFiltersInternal));
export default class CodeValueFilters extends React.Component<ICodeValueFilters, any> {
public render() {
const { onSubmit, filter, groups } = this.props;
return (
<Form
initialValues={ filter }
onSubmit={ onSubmit }
render={ (props) => <WrappedCodeValueFiltersInternal { ...props } groups={ groups }/> }
/>
);
}
}
......@@ -55,6 +55,12 @@ function AdminMenu(): JSX.Element {
</ListItem>
</Link>
<Divider />
<Link to="/admin/codes">
<ListItem button>
<ListItemText primary={ t('codevalue.admin.p.browse.title') } />
</ListItem>
</Link>
<Divider />
</List>
);
}
......
......@@ -14,6 +14,7 @@ import { kpiAdminRoutes } from 'kpi/routes';
import { inventoryGroupPublicRoutes } from 'inventorygroup/routes';
import { repositoryAdminRoutes } from 'repository/routes';
import { cropAdminRoutes, cropPublicRoutes } from 'crop/routes';
import { codeValueAdminRotes } from 'codevalue/routes';
// User
import { userAdminRoutes, userPublicRoutes } from 'user/routes';
......@@ -70,6 +71,7 @@ export const routes: IRoute[] = [
...kpiAdminRoutes,
...repositoryAdminRoutes,
...cropAdminRoutes,
...codeValueAdminRotes,
],
},
{
......
This diff is collapsed.
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment